In our application, we load a scene via asset bundle. This scene has a few thousand game objects, and we apply material changes those game objects based on user input. We then unload the scene, load up a different scene, and do similar things to it. We’ve noticed that with the larger scenes, we see a huge performance hit, typically some number of frames after unloading the large scene. The delay between frames is sometimes 80 seconds. I’ve traced it down to the code where we essentially do gameObject.GetCompenent().material. If we don’t access that material, there’s no performance issue. I know Unity creates an instance of that material the first time it’s accessed, and there is functionality like MaterialPropertyBlock, but that won’t work here because we often need to enable/disable shader keywords. I’ve also traced this to Vulkan. It works just fine in DirectX. However, we need to use Vulkan as this is for HDRP on Linux. When I profile, I just see Semaphore.WaitForSignal() in the CPU thread, and of course GPU profiling isn’t supported for Vulkan… I should also note that we are calling Resources.UnloadUnusedAssets() after unloading the scene. Is there some bug out there related to large numbers of material instances and Vulkan? Is there some workaround I can do?
I’ve reproduced this in a isolated project that has the main scene, and two other scenes - SceneA, SceneB. Each of these scenes has 5000 of the basic “Cube” objects. Here’s my code:
private IEnumerator LoadScenes()
{
var timer = Stopwatch.StartNew();
SceneManager.LoadScene("SceneA", LoadSceneMode.Additive);
LogTiming(timer, "load scene A");
//Burn some frames
for(int i = 0; i < 20; i++)
{
yield return null;
LogTiming(timer, "frame" + i);
}
foreach(GameObject gameObject in Resources.FindObjectsOfTypeAll<GameObject>())
{
Renderer render = gameObject.GetComponent<MeshRenderer>();
if(render != null)
{
_ = render.material;
}
}
//Burn some frames
for (int i = 0; i < 20; i++)
{
yield return null;
LogTiming(timer, "frame" + i);
}
yield return SceneManager.UnloadSceneAsync("SceneA");
LogTiming(timer, "unload scene A");
yield return Resources.UnloadUnusedAssets();
LogTiming(timer, "UnloadUnusedAssets");
SceneManager.LoadScene("SceneB", LoadSceneMode.Additive);
LogTiming(timer, "load scene B");
//Burn some frames
for (int i = 0; i < 20; i++)
{
yield return null;
LogTiming(timer, "frame" + i);
}
foreach (GameObject gameObject in Resources.FindObjectsOfTypeAll<GameObject>())
{
Renderer render = gameObject.GetComponent<MeshRenderer>();
if (render != null)
{
_ = render.material;
}
}
//Burn some frames
for (int i = 0; i < 200; i++)
{
yield return null;
LogTiming(timer, "frame" + i);
}
}
My log file shows a 14.5 second delay in that last “burn” loop between frames 16 and 17. If I build this for DirectX, there is no lag.
Hi!
Please send us a bug report ![]()
Thank you!
Done! In the meantime, if anybody has any workaround ideas that would be much appreciated because this is an issue for us in production.
1 Like