Virtual Textures workflow: is using huge virtual textures to reduce Material Instances number a good idea?

Hi,

Our team is in the process to optimize our game, and material instances number is one offender. We are wondering if creating big atlases of virtual textures and specifying which part to use with Custom Primitive Data is a viable workflow. Any insight on this subject?

Thanks !

Hi,

thanks for reaching out.

>> We are wondering if creating big atlases of virtual textures and specifying which part to use with Custom Primitive Data is a viable workflow.

If you want to cut down the number of material instances, a similar approach is to use UDIM textures (groups of image files which UE imports as a single virtual texture asset) and combine them with custom primitive data so you can index a different texture from the UDIM texture on each mesh instance. This is also the approach used by Epic’s City sample for rendering car textures.

You may also want to look into using the new (bindless) texture collections, which can also be combined with CPD (as used in this procedural texture plugin, which is discussed in this forum thread).

I hope the above helps, but let me know if you have more questions.

Best,

Sam

Thank you for your answer!

We had a look at the link you provided. This seems promising, but we found the following issue with Texture Collections: it is not working with virtual textures, which we heavily use.

It seems like a bad idea to convert every textures we have into regular textures and to use Texture Collections, as we would increase the memory budget overall.

Supposing we create a very big Virtual Texture (containing every textures of a level) and we manipulate the UVs to select which one we want with a Custom Primitive data for each mesh, we would technically have only one Shading Bin used, but do you think it would increase performance or is there a caveat using this ?

From what we could imagine, there are certain caveats regarding production iteration / flow:

- We would need to update this texture every time we change something

- Artists would fight over the checkout of this texture (unless we automate the merging)

- It would break WYSIWYG of the Static mesh viewer

- We would need tooling to set the CPD of the Static mesh in our Levels

- etc…

Maybe this is something that has already been discussed by the Rendering Team ? What would be your opinion on this ?

Thank you

Hi,

>> seems promising, but we found the following issue with Texture Collections: it is not working with virtual textures, which we heavily use.

I was recently told about a new feature that was added to 5.7, called Virtual Texture Collection, which aims to solve some of the issues you listed. Updates can be done per texture in the collection, instead of needing to update one single large texture. It should also be more performant than having to use UV remapping in a very large virtual texture, as it is block aligned:

Added VirtualTextureCollection's, a separate texture collection object with virtual texture backing
- Internally hosts a tile-wise atlas of the texture set. Single physical texture, all respective texture entries must share the same format and srgb mode. Alternatively, runtime format conversion is supported.
- Texture entries are naturally (mip wise) block aligned. Due to block alignment, we can avoid expensive UV re-mapping and manual sampler addressing by modifying the page table uniforms.
- Format conversion supports both virtual and regular textures, and block (re)compression should it be needed.

The feature is currently experimental and may not work in all cases. Unfortunately there is not much documentation available, but if you want to use it, please make sure to apply the patch from [this [Content removed] which fixes an issue with indexing. It also shows how to index into the texture collection with a CPD node. In the config files, you need to enable bindless resources by setting

[PCD3D_SM5]

BindlessResources=Enabled

BindlessSamplers=Enabled

in WindowsEngine.ini

and

rhi.Bindless.Resources=Enabled

As for the question of performance, it depends on the scene how much performance can be gained from optimizing draw calls by reducing the number of material instances. Will it can make a significant difference for non-Nanite geometry, Nanite draw calls work differently as Nanite decouples the rasterization of the geometry from the rendering of materials (it first draws the geometry into a vis buffer to determines which geometry is visible on screen, then uses that vis buffer to run the materials only on the visible geometry).

It’s not easy to predict the impact of such a change as Unreal Engine 5 already performs some optimizations under the hood for Nanite scenes over which you have little control. The best way to find out is to make a before/after comparison and profile both scenarios. The general recommendation is to keep material complexity as low as possible, as in pure Nanite scenes this will be bottlenecking performance more than the number of material instances. It’s better to have a few less complex materials than a single complex one (as the amount of draw calls is already optimised by Nanite).

Hopefully that helps, but let me know if you have further questions.

Sam

Sam