Texture
A Texture are GPU objects that contain one or more images that all have the same image format, that can be accessed from shaders.
While the idea behind textures is simple in principle (a grid of pixels stored on GPU memory), GPU Textures are surprisingly complex objects. It can be helpful to read the API Guide section on textures to make sure you have a full picture.
Usage
Creating a texture
const texture = device.createTexture({sampler: {addressModeU: 'clamp-to-edge'});
Setting texture data from an image
const imageBitmap = // load an image from a URL, perhaps with loaders.gl ImageLoader
texture.copyFromExternalImage({source: imageBitmap});
Note that setting texture data from 8 bit RGBA arrays can also be done via texture.copyFromExternalImage() via ImageData.
const data = new ClampedUint8Array([...]);
const imageData = new ImageData(data, width, height);
texture.copyFromExternalImage({source: imageData});
Setting texture data for non-8-bit-per-channel bit depths, texture arrays etc.
This is still WIP
const commandEncoder = device.createCommandEncoder();
const buffer = device.createBuffer({usage: , byteLength});
const texture = device.createTexture({ })
commandEncoder.end();
Reading from Textures In Shaders
const texture = device.createTexture, ...);
// For ease of use, the `Model` class can bind textures for a draw call
model.draw({
renderPass,
bindings({texture1: texture, texture2: texture})
});
const framebuffer = device.createFramebuffer({
colorAttachments: [texture]
});
const renderPass = device.createRenderPass({
framebuffer
});
// Alternatively, bind the textures using the `Texture` API directly
model.draw({
uniforms({uMVMatrix: matrix})
});
- For additional usage examples,
Textureinherits fromResource.
Types
TextureProps
TextureProps extends ResourceProps and accepts the following fields.
| Property | Type | Default | Description |
|---|---|---|---|
id? | string | autogenerated | Optional human readable identifier inherited from ResourceProps. |
handle? | unknown | undefined | Supply an existing backend handle instead of letting luma.gl create one. |
userData? | Record<string, unknown> | undefined | Application data that is stored on the texture instance. |
data? | ExternalImage | TypedArray | null | null | Initial pixel data. This path is deprecated in favor of DynamicTexture and only supports direct image uploads. |
dimension? | '1d' | '2d' | '2d-array' | 'cube' | 'cube-array' | '3d' | '2d' | Declares the logical texture type and controls how width, height and depth are interpreted. Cubemaps automatically force a depth of 6. |
format? | TextureFormat | 'rgba8unorm' | The GPU format that determines channel ordering, bit depth and compression. |
width | number | — | Width in texels. Required unless it can be inferred from data. Values are clamped to positive integers. |
height | number | — | Height in texels. Required unless it can be inferred from data. Values are clamped to positive integers. |
depth? | number | 1 | Depth or array layer count for 3D textures and texture arrays. |
usage? | number | Texture.SAMPLE | Texture.RENDER | Texture.COPY_DST | Bit mask that declares how the texture will be used. See Usage. |
mipLevels? | number | 1 | Number of mip levels allocated for the texture. |
samples? | number | backend default | Multisample count for renderable textures. Provide values >1 to request MSAA. |
sampler? | Sampler | SamplerProps | {} | Sampler instance or props used to create the texture's default sampler. |
view? | TextureViewProps | backend default | Props used when creating the default texture view. |
Usage
Usage expresses two things: the texture type and which operations can be performed.
Note that the allowed combinations are very limited, especially in WebGPU.
| Usage Flag | Value | Description |
|---|---|---|
Texture.COPY_SRC | 0x01 | Enables the texture to be used as a source in copy commands. |
Texture.COPY_DST | 0x02 | Enables the texture to be used as a destination in copy or write commands. |
Texture.SAMPLE | 0x04 | Allows sampling the texture in shaders. Alias: Texture.TEXTURE (deprecated). |
Texture.STORAGE | 0x08 | Enables read/write access when bound as a storage texture. |
Texture.RENDER | 0x10 | Allows the texture to be bound as a color or depth/stencil attachment. Alias: Texture.RENDER_ATTACHMENT (deprecated). |
TextureDimension
| Dimension | WebGPU | WebGL2 | Description |
|---|---|---|---|
1d | ✅ | ❌ | Contains a one dimensional texture (typically used for compute ) |
2d | ✅ | ✅ | Contains a "normal" image texture |
2d-array | ✅ | ✅ | Holds an "array" of 2D textures. |
3d | ✅ | ✅ | Holds a "stack" of textures which enables 3D interpolation. |
cube | ✅ | ✅ | Holds 6 textures representing sides of a cube. |
cube-array | ✅ | ❌ | Holds an array where every 6 textures represent the sides of a cube. |
ExternalImage
luma.gl allows texture data to be initialized from a number of different CPU object that hold an image. These are referred to as external (to the GPU) images.
| Type | Description |
|---|---|
Image (HTMLImageElement) | image will be used to fill the texture. width and height will be deduced. |
Canvas (HTMLCanvasElement) | canvas will be used to fill the texture. width and height will be deduced. |
Video (HTMLVideoElement) | video will be used to continuously update the texture. width and height will be deduced. |
ImageData | canvas.getImageData() - Used to fill the texture. width and height will be deduced. |
TextureData
luma.gl allows textures to be created from a number of different data sources.
| Type | Description |
|---|---|
null | A texture will be created with the appropriate format, size and width. Bytes will be "uninitialized". |
typed array | Bytes will be interpreted according to format/type parameters and pixel store parameters. |
Buffer | Bytes will be interpreted according to format/type parameters and pixel store parameters. |
CubeFace
Lets cube faces be specified with semantic strings instead of just depth indexes (0-5).
type TextureCubeFace = '+X' | '-X' | '+Y' | '-Y' | '+Z' | '-Z';
Members
A number of read only accessors are available:
-
device:Device- holds a reference to theDevicethat created thisTexture. -
handle:unknown- holds the underlying WebGL or WebGPU shader object -
props:TextureProps- holds a copy of theTexturePropsused to create thisTexture. -
width- width of one face of the cube map -
height- height of one face of the cube map -
format- internal format of the face textures -
border- Always 0. -
type- type used to create face textures -
dataFormat- data format used to create face textures. -
offset- offset used to create face textures. -
handle- The underlying WebGL or WebGPU object. -
id- An identifying string that is intended to help debugging.
Static Methods
Texture.isExternalImage()
Check whether a value is a valid external image object.
Texture.getExternalImageSize()
Deduces the size (width and height) of an "external image" object.
Texture.isTextureLevelData()
Check whether a value is valid as data for setting a texture level.
Texture.getTextureDataSize()
Calculates the size of texture data for a composite texture data object
Texture.getMipLevelCount()
Calculate the number of mip levels for a texture of width and height.
It performs the standard calculation Math.floor(Math.log2(Math.max(width, height))) + 1.
Texture.getMipLevelCount(width: number, height: number): number
Texture.getCubeFaceDepth()
Convert luma.gl cubemap face constants to texture depth index (0-based).
Texture.getCubeFaceDepth(face: TextureCubeFace): number
Methods
constructor(props: TextureProps)
Texture is an abstract class and cannot be instantiated directly. Create with device.createTexture(...).
destroy(): void
Free up any GPU resources associated with this texture immediately (instead of waiting for garbage collection).
generateMipmap() : Texture2D
Call to regenerate mipmaps after modifying texture(s)
WebGL References gl.generateMipmap
copyExternalImage()
Copy data from an image data into the texture.
This function offers a highly granular control but can be called with just an image parameter and the remaining arguments will be deduced or set to canonical defaults.
copyExternalImage(options: {
image: ExternalImage;
sourceX?: number;
sourceY?: number;
width?: number;
height?: number;
depth?: number;
mipLevel?: number;
x?: number;
y?: number;
z?: number;
aspect?: 'all' | 'stencil-only' | 'depth-only';
colorSpace?: 'srgb';
premultipliedAlpha?: boolean;
}: {width: number; height: number}
| Parameter | Type | |
|---|---|---|
image | ExternalImage | Image |
sourceX? | number | Copy from image x offset (default 0) |
sourceY? | number | Copy from image y offset (default 0) |
width? | number | Copy area width (default 1) |
height? | number | Copy area height (default 1) |
depth? | number | Copy depth (default 1) |
mipLevel? | number | Which mip-level to copy into (default 0) |
x? | number | Start copying into offset x (default 0) |
y? | number | Start copying into offset y (default 0) |
z? | number | Start copying from depth layer z (default 0) |
aspect? | 'all' | 'stencil-only' | 'depth-only'; | When copying into depth stencil textures (default 'all') |
colorSpace? | 'srgb' | Specific color space of image data |
premultipliedAlpha? | boolean | premultiplied |
writeData()
Write typed-array data into a texture. This method supports full-texture uploads as well as partial updates to mip levels, cubemap faces, array layers, and 3D slices.
On WebGPU this corresponds closely to GPUQueue.writeTexture(). On WebGL, luma.gl emulates the same destination and layout semantics, including padded CPU-side source rows.
Use writeData() when the source data already lives on the CPU as a typed array or ArrayBuffer. The upload is defined by two things:
- The destination texture subresource:
mipLevelx,y,zwidth,height,depthOrArrayLayers
- The source memory layout:
byteOffsetbytesPerRowrowsPerImage
z follows the texture dimension:
- for
2d, it is always0 - for
cube, it selects the cube face (0..5) - for
2d-array, it selects the first array layer - for
3d, it selects the first depth slice
If layout fields are omitted, luma.gl computes defaults for a tightly packed upload of the requested write region at the requested mip level. This matters for explicit mip uploads: row layout is based on the mip size being written, not the base texture size.
On WebGPU, this is intentionally different from buffer copy paths such as writeBuffer() and readBuffer(): writeData() follows queue.writeTexture() semantics and does not implicitly pad rows to 256 bytes.
Use explicit bytesPerRow and rowsPerImage when:
- rows are padded
- multiple layers or slices are packed into one source allocation
- the source data starts at a nonzero
byteOffset
On WebGL, those fields are also how you upload padded CPU data: luma.gl maps them onto WebGL unpack state so the same source layout can be used on both WebGL and WebGPU.
writeData(
data: ArrayBuffer | ArrayBufferView,
options?: {
byteOffset?: number;
bytesPerRow?: number;
rowsPerImage?: number;
x?: number;
y?: number;
z?: number;
width?: number;
height?: number;
depthOrArrayLayers?: number;
mipLevel?: number;
aspect?: 'all' | 'stencil-only' | 'depth-only';
}
): void
| Parameter | Type | Description |
|---|---|---|
data | ArrayBuffer | ArrayBufferView | Source texel data. |
byteOffset? | number | Offset, in bytes, from the beginning of data to the first texel written. |
bytesPerRow? | number | Stride, in bytes, between successive texel rows. |
rowsPerImage? | number | Number of rows that make up one image when writing multiple layers or slices. |
x? | number | Destination x offset within the target subresource. |
y? | number | Destination y offset within the target subresource. |
z? | number | Destination array layer, cubemap face, or 3D slice offset. |
width? | number | Width of the region to write. Defaults to the target mip width. |
height? | number | Height of the region to write. Defaults to the target mip height. |
depthOrArrayLayers? | number | Number of array layers or depth slices to write. Defaults to 1, or the full mip depth for 3D textures. |
mipLevel? | number | Destination mip level. |
aspect? | 'all' | 'stencil-only' | 'depth-only' | Aspect to write for depth/stencil textures. |
Example: write a padded 2x2 mip into mipLevel: 1
const data = new Uint8Array(16 * 2);
texture.writeData(data, {
mipLevel: 1,
width: 2,
height: 2,
bytesPerRow: 16,
rowsPerImage: 2
});
readBuffer()
Copy texture contents into a GPU Buffer.
On WebGPU this corresponds closely to copyTextureToBuffer(). On WebGL, luma.gl emulates the same subresource targeting semantics.
Use readBuffer() when you want a GPU buffer containing texture data, typically for staging, readback pipelines, or explicit post-processing workflows.
Unlike readDataAsync(), readBuffer() exposes the linear buffer layout directly. On WebGPU, that means the returned layout follows buffer-copy alignment rules rather than tightly packed CPU upload rules.
If you need a matching layout, call texture.computeMemoryLayout() for the same region. On WebGPU, the returned bytesPerRow is padded for buffer copies, typically to a multiple of 256.
writeBuffer()
Copy data from a GPU buffer into a texture. This is the buffer-based counterpart to writeData(), using the same destination fields (x, y, z, width, height, depthOrArrayLayers, mipLevel, aspect) and the same layout fields (byteOffset, bytesPerRow, rowsPerImage).
On WebGPU this corresponds closely to copyBufferToTexture(). On WebGL, luma.gl emulates the same mip, layer, depth-slice, and cubemap-face targeting behavior.
Use writeBuffer() when the source data already lives in a GPU Buffer, or when your application wants to stage texture uploads through reusable upload buffers.
On WebGPU, writeBuffer() uses buffer-copy layout rules. In practice, bytesPerRow must satisfy WebGPU's row-alignment requirements, typically a multiple of 256.
On WebGL, no such 256-byte rule applies, but the same bytesPerRow and rowsPerImage fields are still used to describe the source layout.
writeBuffer() uses the same destination semantics as writeData():
mipLevelselects the mipx,y,zselect the destination origin inside that mipwidth,height,depthOrArrayLayersdefine the copied extentbyteOffset,bytesPerRow,rowsPerImagedescribe how texels are laid out in the source buffer
In other words, writeBuffer() and writeData() differ by source type, not by destination behavior.
Choose between them based on where the source data is:
- CPU memory:
writeData() - GPU buffer:
writeBuffer() - Browser image/video/canvas source:
copyExternalImage()
Example: copy from a buffer into array layer 3
texture.writeBuffer(uploadBuffer, {
z: 3,
width: 64,
height: 64,
depthOrArrayLayers: 1,
bytesPerRow: 256,
rowsPerImage: 64
});
copyImageData()
Deprecated compatibility wrapper over writeData(). Existing code continues to work, but new code should call writeData() directly.
copyImageData() inherits the same layout defaults and alignment rules as writeData(): tightly packed by default, explicit bytesPerRow and rowsPerImage only when the CPU-side source data is padded or layered. This applies on both WebGL and WebGPU.