You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
BitmapLayer's docs list a plain object {data: <Uint8Array>, width, height} as a supported image value. In deck.gl 9.x this form produces a texture that samples as opaque black (not transparent — it actively paints black over everything beneath the layer's bounds).
Root cause in modules/core/src/utils/texture.ts (createTexture, still present on master as of 2026-06-11):
const{width, height}=image.data;// <- for the plain-object form,consttexture=device.createTexture({// image.data is the raw typed
...image,// array: width/height aresampler: {…},// undefinedmipLevels: device.getMipLevelCount(width,height)// <- garbage mip count});
Browser image objects (ImageData, ImageBitmap, canvas, …) are unaffected because they get wrapped as image = {data: browserImage} first, so image.data.width/height exist. Only the plain-object path reads dimensions from the wrong level: the object itself carries width/height, its data does not. Combined with the default mipmap-filtering sampler (mipmapFilter: 'linear'), the resulting texture is incomplete and samples as (0,0,0,1).
Reproduction (deck.gl 9.3.3, self-contained)
Two BitmapLayers, byte-identical RGBA checkerboards — left uses the documented plain-object form, right wraps the same array in an ImageData:
(or validate/short-circuit mipLevels when dimensions are unavailable).
Additional context
Environment: deck.gl 9.3.3, Chromium 141, macOS 15 (Apple M3 Pro); bug code path unchanged on current master.
In a larger app (MapboxOverlay interleaved with MapLibre), the same bug manifested only on real GPUs (ANGLE Metal) while headless SwiftShader rendered the texture correctly through that code path — so headless CI and screenshot harnesses showed a working layer while every real device showed black. Worth keeping in mind for regression coverage: a pixel test of this minimal repro does catch it headless.
Description
BitmapLayer's docs list a plain object{data: <Uint8Array>, width, height}as a supportedimagevalue. In deck.gl 9.x this form produces a texture that samples as opaque black (not transparent — it actively paints black over everything beneath the layer's bounds).Root cause in
modules/core/src/utils/texture.ts(createTexture, still present onmasteras of 2026-06-11):Browser image objects (ImageData, ImageBitmap, canvas, …) are unaffected because they get wrapped as
image = {data: browserImage}first, soimage.data.width/heightexist. Only the plain-object path reads dimensions from the wrong level: the object itself carrieswidth/height, itsdatadoes not. Combined with the default mipmap-filtering sampler (mipmapFilter: 'linear'), the resulting texture is incomplete and samples as(0,0,0,1).Reproduction (deck.gl 9.3.3, self-contained)
Two BitmapLayers, byte-identical RGBA checkerboards — left uses the documented plain-object form, right wraps the same array in an
ImageData:Result: left tile solid black, right tile renders the checkerboard.
Measured via Playwright pixel readback (Chromium 141.0.7390.37, macOS):
Expected behavior
The documented plain-object form renders identically to the equivalent
ImageData.Suggested fix
Read dimensions from the level that has them:
(or validate/short-circuit
mipLevelswhen dimensions are unavailable).Additional context
master.MapboxOverlayinterleaved with MapLibre), the same bug manifested only on real GPUs (ANGLE Metal) while headless SwiftShader rendered the texture correctly through that code path — so headless CI and screenshot harnesses showed a working layer while every real device showed black. Worth keeping in mind for regression coverage: a pixel test of this minimal repro does catch it headless.createTexture, different defect).ImageDatabefore handing it toBitmapLayer.