通常来说,对于大多数使用场景,页面上只需要创建一个 cesium 的 viewer 就够用了。
但是架不住,某些情况下,会需要多个 viewer 的存在。
了解 cesium 的童鞋应该知道,一般情况下,一个 viewer 对应着一个 webgl context,如果有多个就会存在多个 WebGLRenderingContext。
如果还不了解 WebGLRenderingContext 是什么的童鞋,建议去了解学习下 webgl 相关的知识,下面送上学习链接:https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext
对于大部分浏览器而言,都会对单个页面同时激活的 WebGL contexts 有限制,可以参考下面的讨论页:
https://bugzilla.mozilla.org/show_bug.cgi?id=1421481
例如,在火狐浏览器的代码里,就做了如下限制,对于 pc 而言,单个页面同时激活的 WebGL contexts 不能超过 16 个。
所以,为了使我们的程序能够保持正常的运行状态,我们必须要保证我们的页面应用严格的遵循这一原则。
我们可以写一个小应用, 进行测试,看一下,对于我们常用的浏览器,是否都会存在这个情况。
我们的应用,分为左右两侧,左侧是一个一直存在的 viewer,右侧是一个不断销毁和创建的 viewer 区域。
可以看到,当我们右侧第17次创建 viewer 的时候,左侧的 viewer 抛出异常错误。
打开控制台,可以看到,浏览器提示激活了太多的 webgl contexts,最老的 context 将会丢失掉。
由此我们推测,虽然我们每次都调用了 viewer.destroy()
方法,来销毁掉 viewer,并不等同于 webgl context 也被销毁掉了。
那么,webgl context 有没有提供 api 让我们能够手动销毁掉创建出来的 context 呢?
经过一番查找和研究,发现还是有的:https://developer.mozilla.org/en-US/docs/Web/API/WEBGL_lose_context
可以调用 WebGLRenderingContext getExtension 接口,拿到对应的 extension,然后调用 loseContext 方法,能让该 webgl context 失效掉。
这么说,其实我们 destory 掉 viewer 以后,cesium 源码里并没有调用上面这个方法,让对应的 webgl context 失效掉。
我们打开 vscode,对 cesium 的源码进行全局检索,检索关键字 WEBGL_lose_context
,可以发现在我们的 cesium 1.85 的源码里,并没有找到相关的代码。
所以,结论就很明显了,正如我们前面所猜想的那样,我们调用 destory 销毁 viewer 的时候,当前 viewer 所用的 webgl context 并没有被释放掉,还是会占用资源。
所以,我们稍微对销毁逻辑做一些优化,改成下面这样
我们在 destory 掉 viewer 以后,手动调用 gl.getExtension("WEBGL_lose_context").loseContext();
方法。
可以看到,代码像上面那样改动了以后,在不断销毁、创建 viewer 的过程中,我们最开始被创建出来的 viewer 已经不会丢失 webgl context 了,这样就完美的解决了我们的问题。