Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix a memory leak bug in ShadowDepthWrapper #14406

Conversation

nonlinearthink
Copy link
Contributor

this._subMeshToDepthWrapper.mm.delete(params.subMesh);

This code trigger a depth effect recreation but not release the previous depth effect in engine._compiledEffects, which cause a memory leak

8DD664A4-5D02-4b6c-9C3D-CC179A3564A5

@bjsplat
Copy link
Collaborator

bjsplat commented Oct 10, 2023

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

@bjsplat
Copy link
Collaborator

bjsplat commented Oct 10, 2023

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

@nonlinearthink
Copy link
Contributor Author

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s). To prevent this PR from going to the changelog marked it with the "skip changelog" label.

I'm not contributor, can't add a labeI.

@nonlinearthink nonlinearthink changed the title fix memory leak in ShadowDepthWrapper Fix a memory leak bug in ShadowDepthWrapper Oct 10, 2023
@bjsplat
Copy link
Collaborator

bjsplat commented Oct 10, 2023

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

@bjsplat
Copy link
Collaborator

bjsplat commented Oct 10, 2023

@Popov72
Copy link
Contributor

Popov72 commented Oct 10, 2023

This is by design, the _compiledEffects object is a cache and we don't clean it out during the course of a program, the idea being that we'll be able to reuse effects already compiled: there's normally a limited set of combinations for shader code, so the size of this object shouldn't increase indefinitely.

What's not normal is that you keep creating new effects in your program. I see some kind of GUID in your screenshot in the list of effect defines: where does it come from? I think that's the problem, it makes the system create a new effect for each GUID, even if all the other defines are the same.

@sebavan
Copy link
Member

sebavan commented Oct 10, 2023

agree that those guids might be the root cause of the cache miss here. I ll close the current PR for know to prevent accidental merge and lets try to find how you can end up with them.

@sebavan sebavan closed this Oct 10, 2023
@nonlinearthink
Copy link
Contributor Author

This is by design, the _compiledEffects object is a cache and we don't clean it out during the course of a program, the idea being that we'll be able to reuse effects already compiled: there's normally a limited set of combinations for shader code, so the size of this object shouldn't increase indefinitely.

What's not normal is that you keep creating new effects in your program. I see some kind of GUID in your screenshot in the list of effect defines: where does it come from? I think that's the problem, it makes the system create a new effect for each GUID, even if all the other defines are the same.

i think it happened if you change defines for a material, and this material has a ShadowDepthWrapper, it will trigger onEffectCreatedObservable in ShadowDepthWrapper, delete _subMeshToDepthWrapper, then a new depth effect will be created with a new GUID.

@Popov72
Copy link
Contributor

Popov72 commented Oct 12, 2023

Indeed, the shadow depth wrapper uses a guid to identify shaders, it's not a define as I originally thought. However, because of this, the shader can't really be reused later, so I agree that we can get rid of the effects when we need to recreate them for some reason.

I'm reopening the PR, but before we can approve it, you should make a separate function to factorize the code and avoid having the same block of code twice.

@Popov72 Popov72 reopened this Oct 12, 2023
depthWrapperEntries.forEach((depthWrapper) => {
const effect = depthWrapper.mainDrawWrapper.effect;
if (effect) {
engine._releaseEffect(effect);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should remove this call, as effect.dispose() already does it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should remove this call, as effect.dispose() already does it.

it does, but there is still a big shader code string in memory, i don't kown why, so i try to call dispose() explicitly, then it works

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure the problem is coming from this? I don't see why calling first engine._releaseEffect would change anything... Are you able to provide a repro of the problem?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure the problem is coming from this? I don't see why calling first engine._releaseEffect would change anything... Are you able to provide a repro of the problem?

ok, i will try to build a playground tomorrow

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://playground.babylonjs.com/#C2PIGL

i just change albedoTexure per 500ms to trigger material defines change, see memoryLeakMaterial and memoryLeakTexture, my company's project is more complex

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested your PR by commenting the engine._releaseEffect(effect); line, and Object.keys(engine._compiledEffects).length remains constant (=9) during the execution of the PG (?)

Also, you should investigate why we get 12 times this warning in the console log:

[.WebGL-0000054400A15B00] GL_INVALID_VALUE: Program object expected.

We don't have them without the PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed, no need engine._releaseEffect(effect);, but i have no idea about this warning

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix for the warnings is a little involved (that's because the old effects should not be removed right away because of parallel shader compilation). I'm going to close this PR and will reopen a new one with your code and the fix for the warnings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact the problem was simpler, see line 126 in the other PR (#14449).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants