Skip to main content

Model

Model is the main engine-level rendering class in luma.gl. It assembles shaders, manages geometry and bindings, reuses immutable cached pipelines, and applies its dynamic draw state to a RenderPass.

GPUVectorModel baseโ€‹

Model extends GPUVectorModel, owned and exported by @luma.gl/engine. Import GPUVectorModel, GPUVectorGeometry, and their batching helpers from engine; gpgpu/gpu-data provides the GPUData and GPUVector inputs. The base owns the shared rendering state, contiguous vector setGPUVectors() API, and aligned vector drawBatches() API. Vector byte offsets are preserved. GPUVectorModel supports shader assembly, uniforms, and resource bindings. Model also adapts Geometry and GPUGeometry inputs and manages material bindings and uniforms. Vector inputs supply formats, strides, and offsets for layout inference. Explicit layouts can describe packed groups and override inferred bindings. BufferTransform and TextureTransform use GPUVectorModel directly; TableTransform inherits that implementation through BufferTransform.

GPU vector geometry and attributesโ€‹

GPUVectorModel accepts GPU vectors and constants. GPUVectorGeometry groups borrowed per-vertex columns, an optional contiguous uint16 or uint32 index vector (uint8 on WebGL), and topology. CPU Geometry and buffer-based GPUGeometry inputs are accepted by Model.

import {GPUVectorGeometry, GPUVectorModel} from '@luma.gl/engine';

const model = new GPUVectorModel(device, {
...shaders,
isInstanced: true,
geometry: new GPUVectorGeometry({
attributes: {positions, normals},
indices,
topology: 'triangle-list'
}),
attributeVectors: {instancePositions, colors}
});

model.setGeometryVectors({positions, normals});
model.setAttributeVectors({instancePositions, colors});

setGeometry() applies topology, geometry vectors, and indices together. The granular setGeometryVectors(), setIndexVector(), and setTopology() methods update them independently. setAttributeVectors() replaces supplemental attributes while preserving geometry. Supplemental vectors default to instance step mode on instanced models and vertex step mode otherwise. Constants broadcast without determining draw counts.

Both vector setters accept an optional second bufferLayouts argument. The setGPUAttributes(columns, bufferLayouts) method applies caller-resolved supplemental layouts, including packed buffers and mixed WebGPU vector/constant bindings. WebGL constants are converted to shader values; WebGPU constants use model-owned vertex buffers. All caller-supplied vectors and buffers remain borrowed.

Omit bufferLayouts to infer formats, strides, and offsets from the inputs:

model.setAttributeVectors({positions, colors, radius});

Within each vector input group, inferred constants share one model-owned, zero-stride vertex buffer on WebGPU. WebGL continues to use constant attribute values. Inferred vector views share one layout when every corresponding chunk uses the same buffer, stride, and relative attribute offsets within a row. This recognizes existing interleaved storage without copying it. Separate column allocations remain separate bindings; inference does not interleave or concatenate their data to fit device limits.

Explicit layouts and layouts carried by packed vectors remain authoritative and are not consolidated. Geometry and supplemental attribute groups are consolidated independently.

Geometry vectors must share logical length and chunk boundaries, as must supplemental attribute vectors. Non-instanced models require both groups to share their batch structure. draw() renders matching batches together for non-instanced models. Instanced models draw each geometry batch with each instance-stepped attribute batch. Supplemental vertex inputs share the geometry batch structure. Buffer layouts and model draw counts are restored after batched rendering. Indices remain a single contiguous stream shared by geometry batches.

validateGPUVectorBatchStructure(vectors), exported from @luma.gl/engine and @luma.gl/graph/gpu-data, validates row counts, chunk counts, and per-chunk row counts. It accepts a named map or array of vectors, ignores constants and undefined entries, and neither modifies nor repacks input storage.

Per-batch uniformsโ€‹

drawBatches() invokes onBatch before each aligned vector chunk. Set managed shader-input uniforms in the callback to supply batch-local state such as a picking row offset:

model.drawBatches(renderPass, {
vectors: {positions, colors},
onBatch: ({rowIndexOffset, rowCount}) => {
model.shaderInputs.setProps({picking: {indexOffset: rowIndexOffset}});
}
});

rowIndexOffset is cumulative within the supplied vectors; rowCount is the current chunk's length. Corresponding vectors must have equal lengths, chunk counts, and per-chunk row counts. This API does not pad, split, concatenate, or realign chunks. As with other drawBatches() inputs, configure matching model buffer layouts and initial attribute bindings first.

On WebGPU, the model reserves one buffer for all managed uniform blocks and batches in the call. Each batch binds a distinct {buffer, offset, size} range, aligned to the device's minUniformBufferOffsetAlignment. The model grows the allocation before drawing when needed, keeps it for reuse, and destroys it with the model. WebGL updates uniforms immediately.

Slots are reused on every drawBatches() call. Submit previous draws before another call changes their uniform values or requires a larger allocation:

const pass = encoder.beginRenderPass({framebuffer});
model.drawBatches(pass, {vectors, onBatch});
pass.end();
device.submit(encoder.finish());
// The model's batch slots can now be reused with different values.

This matches the proposed GPU layers' per-batch style-buffer pattern. It is not an immutable snapshot across multiple calls before submission. Keep the model alive while pending commands or reusable render bundles reference its buffers. Re-record or stop replaying bundles before changing the slots they reference. Explicit external buffer bindings remain caller-managed. The callback's shader-input values remain set after the call; temporary buffer bindings, attributes, and draw counts are restored, including when a callback throws.

Usageโ€‹

import {CubeGeometry, Model} from '@luma.gl/engine';
import {DynamicTexture} from '@luma.gl/core/dynamic';

const dynamicTexture = new DynamicTexture(device, {data: loadImageBitmap(url)});

const model = await Model.createAsync(device, {
vs: GLSL_VERTEX_SHADER,
fs: GLSL_FRAGMENT_SHADER,
geometry: new CubeGeometry(),
bindings: {
uSampler: dynamicTexture
}
});

const renderPass = device.beginRenderPass({framebuffer});
model.draw(renderPass);
renderPass.end();

Typesโ€‹

ModelPropsโ€‹

PropertyTypeDescription
source?stringUnified WGSL source that contains both stages.
vs?string | nullGLSL vertex shader source.
fs?string | nullGLSL fragment shader source.
modules?ShaderModule[]Shader modules to assemble into the shader source.
defines?Record<string, boolean>Shader module defines.
plugins?ShaderPlugin[]Reusable shader assembly plugins resolved for the active GLSL or WGSL backend. Plugin vertexInputs add shader-facing attributes; callers still own buffer layout and attribute data.
shaderInputs?ShaderInputsPre-created shader input manager.
bindings?Record<string, Binding | DynamicBuffer | DynamicBufferRange | TextureBindingSource>Textures, samplers, uniform buffers, dynamic buffers, and texture binding sources such as DynamicTexture and VideoTexture.
parameters?RenderPipelineParametersPipeline parameters baked into the model's pipeline.
geometry?GPUVectorGeometry | Geometry | GPUGeometry | nullGeometry source for attributes and indices.
isInstanced?booleanOptional override for instancing.
instanceCount?numberNumber of instances to draw.
vertexCount?numberNumber of vertices to draw. For indexed models, this is used as the index count when indexCount is not provided, including an explicit value of 0.
indexBuffer?Buffer | DynamicBuffer | nullOptional index buffer.
indexCount?numberNumber of indices to draw. Takes precedence over vertexCount; if neither is provided, the full index buffer is drawn.
attributes?Record<string, Buffer | DynamicBuffer>Buffer-valued attributes.
constantAttributes?Record<string, TypedArray>Constant attributes, primarily for WebGL.
disableWarnings?booleanSuppress warnings for unused attributes and bindings.
varyings?string[]WebGL transform-feedback varyings.
transformFeedback?TransformFeedbackOptional transform feedback object.
debugShaders?'never' | 'errors' | 'warnings' | 'always'Debug shader output policy.
pipelineFactory?PipelineFactoryFactory from @luma.gl/core used to create cached pipelines.
shaderFactory?ShaderFactoryFactory from @luma.gl/core used to create cached shaders.
shaderAssembler?ShaderAssemblerShader assembler override.

ModelProps also includes the standard RenderPipelineProps, except that bindings, vs, and fs are specialized for engine usage.

Propertiesโ€‹

id, deviceโ€‹

Application-provided identifier and owning device.

source, vs, fsโ€‹

The assembled WGSL source or the GLSL stage sources used to create the current pipeline.

pipelineFactory, shaderFactoryโ€‹

Factories from @luma.gl/core used to reuse cached pipelines and shaders.

parameters, topology, bufferLayoutโ€‹

Current pipeline parameters and geometry layout.

isInstanced, instanceCount, vertexCountโ€‹

Draw-count state for the model.

indexBuffer, bufferAttributes, constantAttributesโ€‹

Attribute and index data currently bound to the model.

bindingsโ€‹

Current binding map, including DynamicBuffer instances that may replace their backing buffer and TextureBindingSource instances such as DynamicTexture and VideoTexture that resolve to concrete texture bindings during draw preparation.

vertexArrayโ€‹

Underlying vertex array object used to track attribute bindings.

transformFeedbackโ€‹

Optional WebGL transform-feedback object.

pipelineโ€‹

Current render pipeline.

shaderInputsโ€‹

Active ShaderInputs manager.

userDataโ€‹

Application-owned metadata attached to the model.

Methodsโ€‹

constructor(device: Device, props: ModelProps)โ€‹

Creates a render model for one device.

Model.createAsync(device: Device, props: ModelProps): Promise<Model>โ€‹

Creates the model through the device's asynchronous render-pipeline path and resolves after its pipeline and vertex array are ready. Prefer it during WebGPU loading when multiple independent models can be created with Promise.all(). The new Model(...) constructor remains the synchronous compatibility path.

destroy(): voidโ€‹

Releases cached pipeline and shader references and destroys the internal uniform store.

needsRedraw(): false | stringโ€‹

Returns the current redraw reason and clears the internal redraw flag.

setNeedsRedraw(reason: string): voidโ€‹

Marks the model as needing redraw.

predraw(commandEncoder: CommandEncoder): voidโ€‹

Updates shader inputs and rebuilds the pipeline if necessary, encoding any managed uniform uploads onto the supplied command encoder before the render pass begins.

draw(renderPass: RenderPass, options?: ModelDrawOptions): booleanโ€‹

Draws once into the supplied render pass. The model selects its pipeline, bindings, and vertex array on that pass before issuing the draw. Returns false when required resources, such as unresolved texture binding sources, are not ready yet.

options.parameters accepts viewport, scissorRect, blendConstant, and stencilReference. Supplied values override pass state only for this draw, including all internal vector batches. Omitted fields inherit the current pass state. Prior values are restored even if drawing throws or resources are not ready. Model defaults and pipeline parameters are unchanged. Model inherits this API from GPUVectorModel.

model.draw(renderPass, {
parameters: {blendConstant: [0.5, 0.5, 0.5, 1], stencilReference: 3}
});

Dynamic overrides cannot be recorded into a render bundle. Set them on the executing render pass instead. Native-handle parameter mutations bypass luma's state tracking.

setGeometry(geometry: GPUVectorGeometry | Geometry | GPUGeometry | null): voidโ€‹

Replaces the geometry source.

setTopology(topology: PrimitiveTopology): voidโ€‹

Updates the primitive topology.

setBufferLayout(bufferLayout: BufferLayout[]): voidโ€‹

Replaces the buffer layout and marks the pipeline dirty.

setParameters(parameters: RenderPipelineParameters): voidโ€‹

Updates pipeline parameters. On WebGPU, where parameters are part of the render pipeline, this marks the pipeline dirty when the parameters change. On WebGL the model keeps its pipeline and passes its parameters to each draw call, so model.pipeline.props.parameters keeps the parameters the retained pipeline was created with.

setInstanceCount(instanceCount: number): voidโ€‹

Updates the instance count.

setVertexCount(vertexCount: number): voidโ€‹

Updates the vertex count. For indexed models without an explicit index count, this also limits the number of indices drawn.

setIndexCount(indexCount: number | undefined): voidโ€‹

Updates the indexed draw count. An explicit index count takes precedence over the vertex count.

setShaderInputs(shaderInputs: ShaderInputs): voidโ€‹

Replaces the current ShaderInputs instance.

updateShaderInputs(commandEncoder?: CommandEncoder): voidโ€‹

Flushes current ShaderInputs values into the model's internal uniform store and bindings. On WebGPU, pass the same CommandEncoder that will later open the render pass when uploads must be ordered with subsequent draws.

setBindings(bindings: Record<string, Binding | DynamicBuffer | DynamicBufferRange | TextureBindingSource>): voidโ€‹

Sets textures, samplers, uniform buffers, dynamic buffers, and texture binding sources.

setTransformFeedback(transformFeedback: TransformFeedback | null): voidโ€‹

Attaches or removes a transform-feedback object.

setIndexBuffer(indexBuffer: Buffer | DynamicBuffer | null): voidโ€‹

Replaces the index buffer.

setAttributes(buffers: Record<string, Buffer | DynamicBuffer>, options?): voidโ€‹

Sets buffer-valued attributes.

  • options.disableWarnings suppresses warnings for buffers without a matching layout or attribute.
  • options.byteOffsets maps buffer names to the byte offset where their vertex data starts. Buffers without an entry are bound at offset 0. Use this for views into a shared buffer; the buffer layout and render pipeline do not change.

setConstantAttributes(attributes: Record<string, TypedArray>, options?): voidโ€‹

Sets constant-valued attributes.

Remarksโ€‹

  • Model integrates with ShaderInputs, PipelineFactory, and ShaderFactory by default.
  • DynamicBuffer attributes, index buffers, and bindings are resolved before drawing so resized buffers are rebound automatically.
  • DynamicTexture and VideoTexture bindings are supported directly. Model.draw() defers rendering until texture binding sources are ready.

Draw count inference and independent lengthsโ€‹

inferDrawCount accepts 'instance', 'vertex', or 'no' and defaults to 'no'. It selects the count updated from vector lengths (or each batch's row count), preserving the other count. 'no' preserves both caller-supplied counts.

enforceMatchingVectorLengths defaults to true. Set it to false together with inferDrawCount: 'no' to bind attributes with independent lengths and single physical chunks. Layout and buffer validation still apply. drawBatches() always requires matching lengths and chunk boundaries.