Skip to content

fix: destroy tile textures on eviction (#591)#594

Merged
kylebarron merged 12 commits into
mainfrom
fix/591-destroy-tile-textures
Jun 5, 2026
Merged

fix: destroy tile textures on eviction (#591)#594
kylebarron merged 12 commits into
mainfrom
fix/591-destroy-tile-textures

Conversation

@kylebarron
Copy link
Copy Markdown
Member

@kylebarron kylebarron commented Jun 5, 2026

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 Texture holds a WebGL handle that JS GC does not deterministically reclaim, and deck.gl's onTileUnload docs state the caller owns anything returned from getTileData — passing a texture to a sublayer does not transfer ownership.

The fix follows a single ownership principle: whoever creates a texture frees it.

  • Library default pipelines now clean up their own textures. A new protected _onTileUnloadCallback() hook on RasterTileLayer (mirroring _getTileDataCallback() / _renderTileCallback()):
    • COGLayer destroys tile.data.texture + mask, but only when its default pipeline ran (no user getTileData).
    • MultiCOGLayer always destroys every band texture.
    • ZarrLayer inherits the no-op base — it requires a user getTileData, so its textures are user-owned.
  • Examples with a custom getTileData own their textures, so each frees them in its own onTileUnload one-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-tci was audited: its tile data is CPU ImageData and the GPU texture is created and owned by deck.gl's type: "image" prop handling, so no cleanup is needed (documented with a comment).

An internal destroyIfTexture helper guards with instanceof Texture; library cleanup composes with (runs after) any user-supplied onTileUnload. No new public API.

Design spec: dev-docs/specs/2026-06-05-destroy-tile-textures-design.md.

Closes #591. Closes #135

Test Plan

  • destroyIfTexture unit tests (destroys Textures, ignores absent/non-Texture)
  • COGLayer composes cleanup only when no getTileData; passthrough otherwise
  • MultiCOGLayer always composes band cleanup
  • Base RasterTileLayer returns user onTileUnload unchanged
  • Full suites: deck.gl-raster (137) + deck.gl-geotiff (31) green
  • Workspace pnpm typecheck and pnpm biome check clean

Written by Claude on behalf of @kylebarron

🤖 Generated with Claude Code

kylebarron and others added 7 commits June 5, 2026 14:55
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>
@kylebarron kylebarron changed the title Destroy tile textures on eviction (#591) fix: destroy tile textures on eviction (#591) Jun 5, 2026
@github-actions github-actions Bot added the fix label Jun 5, 2026
kylebarron and others added 5 commits June 5, 2026 15:28
)

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>
@kylebarron kylebarron enabled auto-merge (squash) June 5, 2026 19:56
@kylebarron kylebarron merged commit f32e3b0 into main Jun 5, 2026
4 checks passed
@kylebarron kylebarron deleted the fix/591-destroy-tile-textures branch June 5, 2026 19:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update examples to destroy user-created textures Layer finalization

1 participant