webgl context is not released when destroy viewer!
Sandcastle example:
create two viewer instances in one page. and then destroy on of them and rebuild. After 16 times will throw developError ' DeveloperError: Expected width to be greater than 0, actual value was 0 '. Chrome allow create 16 webgl context.
fix: Modify the Context class destroy method, append this._gl.getExtension('WEBGL_lose_context').loseContext(). This error will disappear. I'm not sure this is the best way to fix this problem.
Browser: Chrome
Operating System: windows 11
Hi there, I believe this is a duplicate of https://github.com/CesiumGS/cesium/issues/5991. I'm going to close this issue and add your report there to keep conversation in one place. Thanks!
Gabby, I'm not sure it's actually a dupe. #5991 is about a helpful error message when the context is lost, this is about failing to clean up a resource, if I read it correctly.
yes, this is about failing to clean up a resource.
Got it, thanks @wangzwswip and @thw0rted!
Also reported in https://github.com/CesiumGS/cesium/issues/12517.
There is a thread in the community forum where multiple instances of this problem have been descibed: https://community.cesium.com/t/expected-width-to-be-greater-than-0-actual-value-was-0-error/7984
As I mentioned in my first response there: The error is very common, and can have many reasons - which leads to people websearching the error message, ending up in this thread, and chiming in. But in the latest reports, "the plot thickens" that the problem is really and only caused by the context not being destroyed (maybe really just the viewer not calling context.destroy() at the right place...)
Also reported in https://github.com/CesiumGS/cesium/issues/12652 with usage with react routing.
context.getExtension('WEBGL_lose_context').loseContext() seems to be the context.destroy() we're looking for.
Based on an example (link) shared in the community forum which reproduces the issue, I made some changes (link) which manually do this before destroying the viewer. (edited (twice) to fix the second sandcastle link)
function dialogClose() {
+ let context = viewer2.canvas.getContext('webgl2') ??
+ viewer2.canvas.getContext('webgl'); // There may be a better way to retrieve the context
+ context?.getExtension('WEBGL_lose_context')?.loseContext()
viewer2.destroy()
viewer2 = null
dialog.classList.remove('show')
}
This seemed to prevent the crash.
I didn't dive into the details here (yet), but loseContext was already mentioned in the first post. Looking at the documentation I doubted that this is the right approach: It says
The WEBGL_lose_context.loseContext() method is part of the WebGL API and allows you to simulate losing the context of a WebGLRenderingContext context.
(Emphasis by me)
Now, why should "simulating" help here? (And what is the context of a context context after all? 🤪 )
But when looking at the specification, it says
This is the recommended mechanism for applications to programmatically halt their use of the WebGL API.
Even though it may also not be clear what that means (without reading the whole WebGL spec), this ambiguity can be handled in the happy engineer's way: Try it out an see what happens.
And what happens is: It works. 🤷♂
But... it's only a workaround. The fact that is is necessary probably indicates a (possibly deeply hidden) resource management issue in CesiumJS. I think that the clean way of destroying a context is to meticulously clean up each and every object that was allocated and unbind everything that was bound, and then ... ... ... well, cross fingers that the garbage collector deems the context as "garbage". Maybe some programmatic "reset-it-all" function (similar to the one in the WebGLDeveloperTools) could be an alternative, but certainly involves much more effort...)
I tried this change out locally, and it seemed to work.
The fact that is is necessary probably indicates a (possibly deeply hidden) resource management issue in CesiumJS.
Yeah, this really is just a bandaid solution. I tried searching through the codebase for where else we might be missing some call to destroy, but I couldn't find anything unfortunately.
Adding my two cents:
- I can only reproduce the issue in Chrome, not Firefox.
- As @javagl said, the Khronos spec explicitly calls out the
.lostContext()function as being for testing, so I don't think we should be using it in production (even as a bandaid?) - The number 16 feels a little too conspicuous - looking at https://webglreport.com/, the max number of texture slots available is 16. My guess is that these are being exhausted / not released on destroying the viewer. If I'm right, that narrows the search space down a lot.
context.getExtension('WEBGL_lose_context').loseContext()似乎就是context.destroy()我们正在寻找的。根据社区论坛中分享的重现该问题的示例(链接) ,我做了一些更改(链接),在销毁查看器之前手动执行此操作。(编辑(两次)以修复第二个沙堡链接)
function dialogClose() {
- let context = viewer2.canvas.getContext('webgl2') ??
- viewer2.canvas.getContext('webgl'); // There may be a better way to retrieve the context
- context?.getExtension('WEBGL_lose_context')?.loseContext() viewer2.destroy() viewer2 = null dialog.classList.remove('show') } 这似乎可以防止事故发生。 这个测试了并没有解决