fix: destroy tile textures on eviction (#591)#594
Merged
Conversation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…GLayer (#591) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Examples that supply a custom getTileData own their tile textures; the library only frees textures its default pipeline created. Free each tile's texture in onTileUnload (cache eviction). Shared colormap/filter textures are module-level and intentionally left alone. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tile data is CPU ImageData; the GPU texture is created and owned by deck.gl's RasterLayer image-prop handling, so no onTileUnload cleanup is needed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
) RasterTileLayerProps picked the tile-lifecycle callbacks from the non-generic TileLayerProps, collapsing tile data to `unknown` and forcing `tile.data as XTileData` casts in every onTileUnload. Pick from TileLayerProps<DataT> instead so `tile.content` is typed `DataT | null`, and use the cast-free `tile.content?.texture.destroy()` in examples and the library's own COGLayer/MultiCOGLayer cleanup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
We need to manually destroy any GPU textures that we create ourselves. This applies both to library code and to app examples
Summary
Frees per-tile GPU textures on cache eviction, closing a GPU-memory leak that accumulated while panning/zooming. A luma.gl
Textureholds a WebGL handle that JS GC does not deterministically reclaim, and deck.gl'sonTileUnloaddocs state the caller owns anything returned fromgetTileData— passing a texture to a sublayer does not transfer ownership.The fix follows a single ownership principle: whoever creates a texture frees it.
_onTileUnloadCallback()hook onRasterTileLayer(mirroring_getTileDataCallback()/_renderTileCallback()):COGLayerdestroystile.data.texture+mask, but only when its default pipeline ran (no usergetTileData).MultiCOGLayeralways destroys every band texture.ZarrLayerinherits the no-op base — it requires a usergetTileData, so its textures are user-owned.getTileDataown their textures, so each frees them in its ownonTileUnloadone-liner:usgs-topo-cutline,vermont-cog-comparison,land-cover,naip-mosaic,dynamical-zarr-ecmwf,nldas-icechunk,aef-mosaic. Shared colormap/filter textures are module-level and intentionally left alone.zarr-sentinel2-tciwas audited: its tile data is CPUImageDataand the GPU texture is created and owned by deck.gl'stype: "image"prop handling, so no cleanup is needed (documented with a comment).An internal
destroyIfTexturehelper guards withinstanceof Texture; library cleanup composes with (runs after) any user-suppliedonTileUnload. No new public API.Design spec:
dev-docs/specs/2026-06-05-destroy-tile-textures-design.md.Closes #591. Closes #135
Test Plan
destroyIfTextureunit tests (destroys Textures, ignores absent/non-Texture)COGLayercomposes cleanup only when nogetTileData; passthrough otherwiseMultiCOGLayeralways composes band cleanupRasterTileLayerreturns useronTileUnloadunchangedpnpm typecheckandpnpm biome checkcleanWritten by Claude on behalf of @kylebarron
🤖 Generated with Claude Code