The system is structured as an Unreal plugin with two additional Unreal modules: one for the editor and one for the runtime. These modules use external libraries for the Mutable runtime, which is a standalone library with no dependencies.
The next diagram shows the basic folder structure of an Unreal Engine game that uses Mutable. The hierarchy only shows the relevant folders for Mutable source code.
The folder “source” is optional (and by default not included with all licenses) and it contains the source code of the standalone runtime and tools libraries. These are already included in the “lib” folder as precompiled binaries for each supported platform, with the required headers in the “include” folder.
Mutable will be automatically linked in your game executable. It doesn't require any additional files or dynamic-link libraries. In order to package the data for your Customizable Objects, an additional folders needs to be added in your project settings. The plugin will try to do this for you, but if you need to do it manually you can add "MutableStreamedData" to the list of "Additional non-asset directories to package" in the project settings. You can do this in the DefaultGame.ini file of your project:
or from the project settings in the Unreal Editor:
In most versions of Mutable, some changes in the Unreal Engine codebase are required in order to improve the performance. These are modifications that cannot be implemented through plugins. In some platforms like Windows, Mutable will still work without the engine changes, but it may have lower performance and some editor features may be disabled, like automatic compilation of models when packaging.
The engine modifications are distributed through a patch file that you can find with every release of the plugin. They are also available in a GitHub branch of the official engine repository, as per Epic Game's rules. In order to apply the patch you can use standard tools like TODO.
The patch with engine changes includes some helpers to try to detect if any modification is missing. If these warnings are visible it is a sign that some of the changes have not been applied, or may have been overwritten by other changes or merges.
These are for the runtime:
#define ANTICTO_MUTABLE_ENGINE_PATCH_TEXTURESTREAMING #define ANTICTO_MUTABLE_ENGINE_PATCH_VERTEXBUFFERS #define ANTICTO_MUTABLE_ENGINE_PATCH_MORPHTARGETVERTEX
These are for the editor:
You don't have to manually use these defines but if they are not present, the plugin will show warnings at compile time.
Please note that these are just simple preprocessor defines, so they do not guarantee that the actual changes are present, but capture most error cases.
Mutable uses memory mainly in 3 different ways:
- Streaming of source mutable data : The data required to build instaces of customizable objects needs to be loaded from disk. This data is then processed into a set of skeletal meshes and textures that compose the final object. The source data may be very big if you have many customization options and it is kept in disk. A cache of this data is maintained in memory to speed up object construction. The size of this cache can be controlled by per-platform scalability settings as explained in the Mutable settings section below.
- Working memory: This is the amount of data used while an object is being constructed, and it is freed while the system is idle. Mutable tries to minimise this amount of data, and its size depends entirely on the kind of objects to be built.
- Object memory: This is the data used by the actual Unreal resources that compose an instance of a customizable object. Mutable will try to reuse textures that are identical, but other than this, these reources can be treated and measured exactly like any standard Unreal Skeletal Mesh or Texture.
These settings can be modified by the standard Scalability ini file hierarchy in Unreal, or with Console Variables or game code. These are the variables for each section:
|b.MutableStreamingMemory||If different than 0, limit the amount of memory (in KB) to use to cache streaming data when building characters. 0 means no limit, -1 means use default (40Mb in PC and 20Mb in consoles).|
|b.NumGeneratedInstancesLimit||If different than 0, limit the number of mutable instances with full LODs to have at a given time.|
|b.NumGeneratedInstancesLimitLOD1||If different than 0, limit the number of mutable instances with LOD 1 to have at a given time.|
|b.NumGeneratedInstancesLimitLOD2||If different than 0, limit the number of mutable instances with LOD 2 to have at a given time.|
|b.DistanceForFixedLOD2||If NumGeneratedInstancesLimit is different than 0, sets the distance at which the system will fix the LOD of an instance to the lowest res one (LOD2) to prevent unnecessary LOD changes and memory|
These are settings that affect mutable in general ways. They can be defined in the DefaultMutable.ini file of your project.
|bExtraBoneInfluencesEnabled||If this is enabled, CustomizableObjects will be allowed to use 8 bone influences per vertex. Otherwise only the default 4 will be used. This is used at object compile time.|
A good starting point to see the performance of Mutable in your game, is to use the provided statistics UMG widget. You can find it in the plugin Content folder with the name UI/MutableStats.
If your project is not using UMG you can access these statistics and log them or display them from the central singleton object UCustomizableObjectSystem. These are some of the relevant methods:
// Get the singleton object. It will be created if it doesn't exist yet. UFUNCTION(BlueprintCallable, BlueprintPure, Category = Status) static UCustomizableObjectSystem* GetInstance(); // Find out if the performance engine patches have been applied UFUNCTION(BlueprintCallable, Category = Status) bool AreEnginePatchesPresent() const; // Find out the version of the plugin UFUNCTION(BlueprintCallable, Category = Status) FString GetPluginVersion() const; // Get the number of instances built and alive. UFUNCTION(BlueprintCallable, Category = Status) int32 GetNumInstances() const; // Get the number of instances waiting to be updated. UFUNCTION(BlueprintCallable, Category = Status) int32 GetNumPendingInstances() const; // Get the total number of instances includingbuilt and not built. UFUNCTION(BlueprintCallable, Category = Status) int32 GetTotalInstances() const; // Get the amount of memory in use for textures generated by mutable. UFUNCTION(BlueprintCallable, Category = Status) int64 GetTextureMemoryUsed() const; // Return the average build/update time of an instance in ms. UFUNCTION(BlueprintCallable, Category = Status) int32 GetAverageBuildTime() const;