I am seeing very large spikes of FScene_AddPrimitive in UWorld_SendAllEndOfFrameUpdates when changing the material on numerous ISMs. When making updates on around ~250 instances across a number of ISMs in a single frame, I am seeing 60ms in development editor build, 30ms of which is in UStaticMesh::GetUsedMaterials. For comparison, using our own code to tear down the existing ISMs and recreate the ISMs with the same mesh but the new material takes significantly less time, around 4ms also on a development editor build; which seems strange since this requires a lot more work. Because of this I wonder if there might be a faster way to change the material that is being used. I am currently just using SetMaterial / SetOverlayMaterial
I can’t share traces due to confidentiality, but let me know if there might more information I could provide that would be helpful.
Thanks
Hi,
thanks for reaching out.
Just a few questions: are all the ISM instances using the same material? In which way does the old material differ from the new material that it gets replaced with?
To change/update materials at runtime, a more efficient alternative to setting the entire material on the instance is to create a dynamic material instance (UMaterialInstanceDynamic) and only update select material parameters at runtime.
Another approach is to use per instance custom data (with SetCustomData and SetCustomDataValue). Unreal Engine allows to store custom data per instance, which can be used to drive material changes dynamically.
If either of these approaches would work for your project and you need help with the implementation, or in case you have any other questions, please let me know.
Best,
Sam
Hi Sam,
The material is pretty much completely different, it’s a transparent “VFX-ey” material while the regular material is a metallic hard surface material. We need to do a full material swap; custom data / material parameters are not going to cut it. Is this amount of cost really expected from just a material swap? Like I said completely recreating the ISMs from scratch (i.e. completely new components) with the exact same number of instances as the old ones is 10x faster. That seems like it has to be a bug or some issue. Changing the material seems like it should be doing much less work under the hood. Can you provide some insight into what is making this cost so much?
Hi,
thanks for clarifying your use case, that makes more sense now.
I think the reason why destroying and recreating the entire ISM group is faster is because the material needs to be set for only one instance in the ISM group, which is then referenced by the other instances in the group, while getting/setting the material on an existing group of ISMs needs to be done for each instance separately, which is inefficient becomes expensive as the number of instances increases (setting an entirely new material is also more expensive than updating a material parameter). I believe that is what is happening, but cannot be entirely sure without inspecting the relevant code parts.
While destroying and rebuilding the ISM set from scratch is quicker, a more efficient approach might be to initialise a second ISM group with the new material applied and make it visible when needed, while hiding the first ISM group. That way you avoid the cost of creating a new ISM group during gameplay. Keeping a second hidden ISM set also shouldn’t consume much memory, just one additional instance.
Please let me know if this approach would work.
Best,
Sam
Hi,
my assumption might have been wrong. I tried to recreate the issue on my side by setting the material on 640 instances simultaneously and profiled with Unreal Insights. I did see a ~50ms spike when changing the material, but most time (~20 ms) was spent on the render thread in FRenderTargetPool_CreateTextureInternal. UWorld_SendAllEndOfFrameUpdates (which calls UStaticMesh::GetUsedMaterials) only took 200 microseconds. I’m not sure why my results are different, the complexity of the material might be a factor, since I’ve used very simple materials in my test. Would it be possible to provide more details about the material or a minimal repro scene for further investigation?
By ISM group I meant ISM component indeed, apologies for the confusion. My suggestion was indeed to keep two copies of the ISM component. According to the official docs, “An ISM is a component that contains a group of identical static meshes. Each static mesh within the component represents an instance (copy) of the static mesh asset” and “Instanced Static Mesh ComponentRepresents multiple instances of the assigned static mesh, which all share the same material and collision properties. This component is commonly used to render the same static mesh more efficiently with different transforms.” Hence the memory cost of the ISM component is essentially equal to that of a single instance (static mesh + material which is shared across all the instances) plus the transform matrices of all the instances inside the ISM component (each instance has a unique transform matrix). Since a transform matrix is very light weight in memory, the total memory occupied by an ISM component should therefore not be much more than the cost of a single instance.
I hope that helps,
Sam
Hi, sorry for the late response.
I’m not sure I understand what you are saying. Why would there need to be a reference on each instance to the material when the material is set at the component level? The total number of instances is exactly the same, the number we are recreating is the same as we originally had, so we have to tear down all the existing and add an equal number. I was hoping someone would investigate the code parts involved, as this seems like a significant issue.
I think I am not understanding your suggestion, what is an “ISM Group”, do you mean an ISM Component? If that is what you are suggesting it is not feasible due to the memory cost. It will not be a single instance, these ISMs have 100s-1000s of instances each, so having two copies of each with the different materials is a very significant amount of memory. Or is there a way to share the instance data across different ISM components or “groups”?