首页 > 解决方案 > 如何在 OpenLayers 中正确释放已移除层的 WebGL 资源?

问题描述

我正在使用 openlayers 6.7.0 开发一个应用程序,我在其中动态添加和删除地图上的图层。这些图层是带有 GeoTIFF 源的 WebGL 切片图层。该应用程序具有 1..N 个地图,每个地图有 X 个图层,每个图层有 Y 个 GeoTIFF 源。这些层经常被删除并被新层替换。地图组件很少被删除或添加,但这也会发生。

尽管很复杂,但应用程序和 OL 运行良好。但是,在使用该应用程序一段时间后,我的浏览器开始向我发出有关过多活动 WebGL 上下文的警告。我在这篇文章的底部添加了一个带有堆栈跟踪的示例错误消息。

似乎我的代码或 OpenLayers 没有正确删除 WebGL 上下文,而且我还没有找到解释如何清理已删除层的文档。我发现最好的是Layer.dispose(),我尝试使用以下代码删除已删除的图层,但这并没有解决问题:

    const previousLayers = [] as Layer<any>[]
    map.getLayers().forEach((l : Layer<any>) => previousLayers.push(l))
    map.getLayers().clear();
    previousLayers.forEach(l => l.dispose())

所以我的问题是:如何清理旧层以便这些层释放所有底层资源?

这是有关 WebGL 上下文的示例错误消息。Linux 上的 Chrome,OpenLayers 6.7.0。

webgl.js:95 WARNING: Too many active WebGL contexts. Oldest context will be lost.
getContext @ webgl.js:95
WebGLHelper @ Helper.js:266
WebGLLayerRenderer @ Layer.js:65
WebGLTileLayerRenderer @ TileLayer.js:121
WebGLTileLayer.createRenderer @ WebGLTile.js:303
Layer.getRenderer @ Layer.js:326
Layer.render @ Layer.js:254
CompositeMapRenderer.renderFrame @ Composite.js:122
PluggableMap.renderFrame_ @ PluggableMap.js:1418
(anonymous) @ PluggableMap.js:213
requestAnimationFrame (async)
PluggableMap.render @ PluggableMap.js:1327
Target.dispatchEvent @ Target.js:110
Observable.changed @ Observable.js:72
LayerGroup.handleLayerChange_ @ Group.js:121
Target.dispatchEvent @ Target.js:110
Observable.changed @ Observable.js:72
Layer.handleSourceChange_ @ Layer.js:210
Target.dispatchEvent @ Target.js:110
Observable.changed @ Observable.js:72
Source.setState @ Source.js:178
GeoTIFFSource.configure_ @ GeoTIFF.js:546
(anonymous) @ GeoTIFF.js:348
Promise.then (async)
GeoTIFFSource @ GeoTIFF.js:346
(anonymous) @ OpenLayersMap.tsx:165
(anonymous) @ OpenLayersMap.tsx:136
invokePassiveEffectCreate @ react-dom.development.js:23487
callCallback @ react-dom.development.js:3945
invokeGuardedCallbackDev @ react-dom.development.js:3994
invokeGuardedCallback @ react-dom.development.js:4056
flushPassiveEffectsImpl @ react-dom.development.js:23574
unstable_runWithPriority @ scheduler.development.js:468
runWithPriority$1 @ react-dom.development.js:11276
flushPassiveEffects @ react-dom.development.js:23447
(anonymous) @ react-dom.development.js:23324
workLoop @ scheduler.development.js:417
flushWork @ scheduler.development.js:390
performWorkUntilDeadline @ scheduler.development.js:157

标签: openlayers

解决方案


一个问题是该layer.dispose()方法没有在图层渲染器上调用 dispose。这适用于所有层类型,并且在这个提议的拉取请求中得到了解决:https ://github.com/openlayers/openlayers/pull/12798

除了将图层的 dispose 方法连接到渲染器之外,该拉取请求还使 WebGL 平铺图层渲染器在其 dispose 方法中做更多的工作。最重要的是,它现在调用gl.deleteTexture()其平铺纹理缓存中的所有纹理。

在 WebGL 帮助器中,上述更改也使得我们尝试使用“WEBGL_lose_context”扩展(如果可用)。调用的效果extension.loseContext()可能是不可知的——我不清楚这是否只是模拟丢失尚未丢失的上下文,或者这是否真的释放了任何资源。有关这个神秘主题的更多“背景”,请参阅WebGL 公共邮件列表

根据我对可用文档的阅读,JS 垃圾收集器应该最终会清理所有这些东西。假设您的应用程序不再有对资源的任何引用,它们应该被垃圾回收。我们还假设 OpenLayers 本身不会不必要地挂在任何引用上。

可能我们只需要忍受浏览器的警告“太多活动的 WebGL 上下文。最旧的上下文将会丢失。” 这实际上是我们想要的——清理与未使用的上下文相关的资源。理想情况下,我们调用额外的gl.delete*方法将允许在它已经发生之前进行清理,但我不确定是否真的有任何类型的“泄漏”我们可以在这里做任何事情。

上面的拉取请求也将该方法标记layer.dispose()为公共 API 的一部分。理想情况下,您不必调用这些 dispose 方法(如果我们相信垃圾收集器是真的)。我们公开公开此方法的另一个地方是光栅源,我们需要知道何时可以安全调用worker.terminate()- 但这是另一个主题。


推荐阅读