如何在 OpenLayers 中正确释放已移除图层的 WebGL 资源?

How to properly release WebGL resources of removed layers in OpenLayers?

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

尽管很复杂,但应用程序和 OL 运行良好。然而,在使用该应用程序一段时间后,我的浏览器开始向我发出有关太多活动 WebGL 上下文的警告。我在 post.

的底部添加了一个带有堆栈跟踪的示例错误消息

我的代码或 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 上下文的示例错误消息。 Chrome Linux,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 @ 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

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

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

在 WebGL 帮助程序中,上述更改也适用于此,因此我们尝试使用 "WEBGL_lose_context" extension if available. The effect of calling extension.loseContext() is perhaps unknowable – it isn't clear to me if this only simulates losing the context if it isn't already lost or if this actually frees up any resources. See the WebGL Public mailing list 来获取有关这个神秘主题的更多“上下文”。

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

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

上面的pull request也将layer.dispose()方法标记为publicAPI的一部分。理想情况下,您不必调用这些 dispose 方法(如果我们对垃圾收集器的看法是正确的)。我们公开此方法的另一个地方 publicly 是针对光栅源的,我们需要知道何时可以安全地调用 worker.terminate() – 但那是另一个主题。