Terrain draws tiles at sea level after a `raster` layer on the same raster-dem tileset is evicted — the tile-texture pool is keyed by size only (R32F DEM textures receive pooled RGBA8 textures)
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 66/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- typescript
- Domain
- frontend, performance
Research direction
Start by reading Painter.saveTileTexture and getTileTexture, then trace the DEM path through prepareDEMTexture and the raster path through Tile.setTexture and RasterTileSource.unloadTile. Reproduce with terrain and a raster layer on the same tileset, then verify the pool does not give a DEM an incompatible texture and that terrain retains its heights after raster tiles are evicted.
Written by the indexing model from the issue text.
Description
mapbox-gl-js version
v3.18.1 (bundle read; no related entry in the changelog through 3.32)
Browser and version
Chrome 154 / Windows 11 / AMD Radeon 860M (D3D11); Chromium 145 / Linux (ANGLE D3D12). Likely all.
Expected behavior
Terrain tiles keep their heights.
Actual behavior
Console: WebGL: INVALID_OPERATION: texSubImage2D: type UNSIGNED_BYTE but ArrayBufferView not Uint8Array or Uint8ClampedArray (one per affected tile), and the affected terrain tiles are drawn flat at ~0 m: a slab far below its neighbours, the sky visible through the gap, the neighbours' skirts standing as walls. It persists until the terrain is rebuilt or the page reloaded.
Cause (from the bundle)
Painter.saveTileTexture/getTileTexturepool freed tile textures bytexture.size[0]only.- The DEM tiles of this tileset are 512 px with a 1-px border:
DEMData.stride = 514.prepareDEMTexturetakespainter.getTileTexture(dem.stride)and otherwise creates a 514×514 R32F texture. - The same images decoded by a
rastersource become 514×514 RGBA8 textures (Tile.setTexture→getTileTexture(img.width)), whichRasterTileSource.unloadTilereturns to the pool. - The next DEM tile pops one of those;
Texture.updateissuestexSubImage2D(…, RGBA, UNSIGNED_BYTE, Float32Array); the upload is rejected and the texture keeps the raster image, which the terrain shader reads as heights.
Same family as #13491 (raster-array textures reaching _prepareDEMTextures, Safari's wording of the same error).
Suggested fix
Key the pool by format as well as size (or never hand a pooled texture to prepareDEMTexture unless its format === gl.R32F).
Workaround we ship
Never pool textures of the DEM stride:
const orig = map.painter.saveTileTexture.bind(map.painter);
map.painter.saveTileTexture = (t) => { if (t?.size?.[0] === 514) return; orig(t); };
Link to the demonstration
No response
Steps to trigger the unexpected behavior
- A map with 3D terrain on
mapbox://mapbox.mapbox-terrain-dem-v1, plus arasterlayer on the same tileset (e.g. to colour the terrain by height withraster-color). - Move around until raster tiles are evicted from the tile cache (set
maxTileCacheSize: 0to see it at once). - Load new terrain tiles (pan to a new area).
<script>
mapboxgl.accessToken = '…';
const map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/outdoors-v12', center: [137.70, 36.67], zoom: 10.9, pitch: 49, maxTileCacheSize: 0 });
map.on('style.load', () => {
map.addSource('mapbox-dem', { type: 'raster-dem', url: 'mapbox://mapbox.mapbox-terrain-dem-v1', tileSize: 512, maxzoom: 14 });
map.setTerrain({ source: 'mapbox-dem', exaggeration: 1 });
map.addSource('dem-as-image', { type: 'raster', url: 'mapbox://mapbox.mapbox-terrain-dem-v1', tileSize: 2048, maxzoom: 14 });
map.addLayer({ id: 'dem-as-image', type: 'raster', source: 'dem-as-image', paint: { 'raster-opacity': 0.5 } });
});
// then: map.jumpTo({ center: [140.70, 42.85] }) and back a few times
</script>
Relevant log output
WebGL: INVALID_OPERATION: texSubImage2D: type UNSIGNED_BYTE but ArrayBufferView not Uint8Array or Uint8ClampedArray
(one line per affected terrain tile — 16 in the session that showed it)
- Dominant language
- TypeScript
- Stars
- 12.5k
- Forks
- 2.4k
- PR merge metrics
- No merged PRs in 30d
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from mapbox/mapbox-gl-js
-
TypeError: Cannot read properties of undefined (reading 'get') when a map is removed while a color-theme LUT is decodingPossibly taken @Piyush495 claimed this 19 days ago. Openauto-triaged bug :lady_beetle:
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
mapbox/mapbox-gl-js#13737 · 1 comment ·
-
auto-triaged bug :lady_beetle:
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
mapbox/mapbox-gl-js#13723 · 2 comments ·
-
auto-triaged bug :lady_beetle:
Difficulty 1/5 Under an hour Newbie friendliness 86/100
mapbox/mapbox-gl-js#13717 ·
-
>= v3.21 Symbol layer without feature-state expression crash the mapPossibly taken @liueric-dev claimed this 60 days ago. Openauto-triaged bug :lady_beetle:
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
mapbox/mapbox-gl-js#13714 · 1 reaction ·
-
auto-triaged feature :green_apple:
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
mapbox/mapbox-gl-js#13676 · 2 comments ·
All issues in mapbox/mapbox-gl-js
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
grafana/profiles-drilldown#1212 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
grafana/traces-drilldown#980 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
openremote/openremote#3487 · 1 comment ·
-
triage:needs-human
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
garrytan/gbrain#7635 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
coollabsio/jean#867 ·
Maintainers usually reply within 1 day