Efficient data-to-GPU attribute management system with automatic buffer updates, memory optimization, and seamless integration with WebGL vertex attributes for high-performance data visualization.
Central manager for vertex attributes handling data transformation and GPU buffer management.
/**
* Manages vertex attributes for layers
* Handles data-to-attribute transformations and WebGL buffer management
*/
class AttributeManager {
/** Initialize attribute manager */
constructor(device: Device, opts?: AttributeManagerOptions);
/** Add vertex attributes */
add(attributes: Record<string, AttributeOptions>): void;
/** Add instanced vertex attributes */
addInstanced(attributes: Record<string, AttributeOptions>): void;
/** Remove attributes by name */
remove(attributeNames: string[]): void;
/** Update attributes from data */
update(opts: UpdateOptions): void;
/** Mark attributes for update */
invalidate(triggerName: string, range?: [number, number]): void;
/** Mark all attributes invalid */
invalidateAll(): void;
/** Get all managed attributes */
getAttributes(): Record<string, Attribute>;
/** Get attributes that changed since last check */
getChangedAttributes(opts?: {clearChangedFlags?: boolean}): Record<string, Attribute>;
/** Get shared attribute accessor */
getSharedAccessor(attributeName: string): any;
/** Set shared attribute accessor */
setSharedAccessor(attributeName: string, accessor: any): void;
/** Check if any attribute needs update */
needsUpdate(): boolean;
/** Check if any attribute needs redraw */
needsRedraw(opts?: {clearRedrawFlags?: boolean}): boolean;
/** Get statistics */
getStats(): AttributeManagerStats;
/** Unique manager identifier */
readonly id: string;
/** Managed attributes */
readonly attributes: Record<string, Attribute>;
/** Whether attributes need redraw */
readonly needsRedraw: boolean;
}
interface AttributeManagerOptions {
/** Manager identifier */
id?: string;
/** Default accessor statistics */
stats?: any;
/** Data change detection */
dataComparator?: (newData: any, oldData: any) => boolean;
}
interface AttributeOptions {
/** Elements per vertex (1, 2, 3, or 4) */
size: number;
/** Data type */
type?: 'float32' | 'uint8' | 'int8' | 'uint16' | 'int16' | 'uint32' | 'int32';
/** Accessor function or property name */
accessor?: string | AccessorFunction<any, any>;
/** Default value when accessor returns undefined */
defaultValue?: number | number[];
/** Transform function for accessor results */
transform?: (value: any, objectInfo: any) => any;
/** Whether values should be normalized to [0,1] range */
normalized?: boolean;
/** Whether attribute is instanced */
instanced?: boolean | number;
/** Whether attribute should be updated */
noAlloc?: boolean;
/** Custom update function */
update?: (attribute: Attribute, opts: any) => void;
/** Buffer update settings */
bufferLayout?: any;
/** Shader attribute name */
shaderAttribute?: string;
}
interface UpdateOptions {
/** Number of data instances */
numInstances?: number;
/** Data array or source */
data?: any;
/** Layer properties */
props?: any;
/** Context information */
context?: any;
/** Buffer layout information */
bufferLayout?: any;
/** Partial range to update [start, end] */
range?: [number, number];
/** Additional parameters */
[key: string]: any;
}
interface AttributeManagerStats {
/** Total attributes managed */
attributeCount: number;
/** Total buffer memory used (bytes) */
totalMemory: number;
/** Number of attributes updated this frame */
attributesUpdated: number;
/** Time spent updating attributes (ms) */
updateTime: number;
}Usage Examples:
import { AttributeManager } from "@deck.gl/core";
// Create attribute manager for a custom layer
class MyLayer extends Layer {
initializeState(context) {
const {device} = context;
const attributeManager = new AttributeManager(device, {
id: 'my-layer-attributes'
});
// Add vertex attributes
attributeManager.add({
positions: {
size: 3,
type: 'float32',
accessor: 'getPosition',
update: this.calculatePositions
},
colors: {
size: 4,
type: 'uint8',
normalized: true,
accessor: 'getColor',
defaultValue: [255, 0, 0, 255]
},
sizes: {
size: 1,
type: 'float32',
accessor: 'getSize',
defaultValue: 1,
transform: (size, {index, data}) => size * data.scaleFactor
}
});
// Add instanced attributes
attributeManager.addInstanced({
instancePositions: {
size: 3,
accessor: 'getPosition'
},
instanceColors: {
size: 4,
type: 'uint8',
normalized: true,
accessor: 'getColor'
}
});
this.setState({attributeManager});
}
updateState({props, oldProps, changeFlags}) {
const {attributeManager} = this.state;
if (changeFlags.dataChanged) {
attributeManager.invalidateAll();
}
// Update specific attributes based on prop changes
if (props.getPosition !== oldProps.getPosition) {
attributeManager.invalidate('getPosition');
}
if (props.getColor !== oldProps.getColor) {
attributeManager.invalidate('getColor');
}
// Update attributes with current data
attributeManager.update({
data: props.data,
numInstances: props.data.length,
props: props,
context: this.context
});
}
calculatePositions(attribute, {data, numInstances}) {
const {value} = attribute;
for (let i = 0; i < numInstances; i++) {
const position = this.props.getPosition(data[i], {index: i, data, target: []});
value[i * 3] = position[0];
value[i * 3 + 1] = position[1];
value[i * 3 + 2] = position[2] || 0;
}
}
}Represents a single vertex attribute managing data transformation and GPU buffer operations.
/**
* Represents a single vertex attribute
* Manages data transformation and GPU buffer operations
*/
class Attribute {
/** Initialize attribute */
constructor(device: Device, opts: AttributeCreationOptions);
/** Check if attribute needs update */
needsUpdate(): boolean;
/** Check if attribute needs redraw */
needsRedraw(opts?: {clearRedrawFlags?: boolean}): boolean;
/** Allocate GPU buffer for specified number of instances */
allocate(numInstances: number): void;
/** Update buffer data */
updateBuffer(opts: UpdateBufferOptions): void;
/** Set constant value for all instances */
setConstantValue(value: number | number[]): void;
/** Update data using external buffer */
setExternalBuffer(buffer: Buffer): void;
/** Update partial data range */
updateSubData(opts: UpdateSubDataOptions): void;
/** Get buffer statistics */
getBufferStats(): BufferStats;
/** Delete and cleanup attribute */
delete(): void;
/** Attribute identifier */
readonly id: string;
/** Elements per vertex */
readonly size: number;
/** Data type */
readonly type: string;
/** Whether values are normalized */
readonly normalized: boolean;
/** Whether attribute is instanced */
readonly instanced: boolean | number;
/** Current attribute data */
readonly value: TypedArray;
/** GPU buffer */
readonly buffer: Buffer;
/** Whether attribute has constant value */
readonly constant: boolean;
/** Settings for buffer updates */
readonly settings: AttributeSettings;
}
interface AttributeCreationOptions {
/** Attribute identifier */
id: string;
/** Elements per vertex */
size: number;
/** Data type */
type?: string;
/** Whether values are normalized */
normalized?: boolean;
/** Whether attribute is instanced */
instanced?: boolean | number;
/** Initial buffer size */
bufferSize?: number;
/** Default value */
defaultValue?: number | number[];
/** Update function */
update?: (attribute: Attribute, opts: any) => void;
/** Additional settings */
settings?: Partial<AttributeSettings>;
}
interface AttributeSettings {
/** Whether to allocate buffer automatically */
noAlloc: boolean;
/** Buffer update frequency */
updateFrequency: 'once' | 'dynamic' | 'stream';
/** Whether attribute is per-instance */
isIndexed: boolean;
/** Shader attribute location */
shaderLocation: number;
}
interface UpdateBufferOptions {
/** Data to upload */
data?: TypedArray;
/** Number of instances */
numInstances?: number;
/** Byte offset in buffer */
offset?: number;
/** Number of bytes to update */
size?: number;
/** Partial range [start, end] */
range?: [number, number];
}
interface UpdateSubDataOptions {
/** Start index */
startOffset: number;
/** End index */
endOffset: number;
/** Data to upload */
data: TypedArray;
}
interface BufferStats {
/** Buffer size in bytes */
byteSize: number;
/** Number of elements */
elementCount: number;
/** Memory usage per element */
bytesPerElement: number;
/** Whether buffer is allocated */
allocated: boolean;
}Usage Examples:
import { Attribute } from "@deck.gl/core";
// Create custom attribute with manual management
class CustomLayer extends Layer {
initializeState({device}) {
// Manual attribute creation
const positionAttribute = new Attribute(device, {
id: 'positions',
size: 3,
type: 'float32',
update: this.updatePositions.bind(this)
});
const colorAttribute = new Attribute(device, {
id: 'colors',
size: 4,
type: 'uint8',
normalized: true,
defaultValue: [255, 255, 255, 255]
});
this.setState({
positionAttribute,
colorAttribute
});
}
updateState({props, changeFlags}) {
const {positionAttribute, colorAttribute} = this.state;
if (changeFlags.dataChanged) {
const numInstances = props.data.length;
// Allocate buffers
positionAttribute.allocate(numInstances);
colorAttribute.allocate(numInstances);
// Update data
positionAttribute.updateBuffer({
numInstances,
data: this.extractPositions(props.data)
});
colorAttribute.updateBuffer({
numInstances,
data: this.extractColors(props.data)
});
}
}
updatePositions(attribute, {data, numInstances}) {
const positions = new Float32Array(numInstances * 3);
for (let i = 0; i < numInstances; i++) {
const pos = this.props.getPosition(data[i]);
positions[i * 3] = pos[0];
positions[i * 3 + 1] = pos[1];
positions[i * 3 + 2] = pos[2] || 0;
}
return positions;
}
draw({uniforms}) {
const {positionAttribute, colorAttribute} = this.state;
// Use attributes in WebGL draw call
this.state.model.draw(this.context.renderPass, {
uniforms,
attributes: {
positions: positionAttribute,
colors: colorAttribute
}
});
}
}Support for binary attribute data for high-performance applications.
/**
* Binary attribute interface for direct GPU buffer management
* Enables zero-copy data transfer for optimal performance
*/
interface BinaryAttribute {
/** Buffer containing attribute data */
buffer: ArrayBuffer;
/** Byte offset within buffer */
offset?: number;
/** Byte stride between elements */
stride?: number;
/** Number of instances */
size: number;
/** Attribute value accessor */
value?: TypedArray;
}Built-in support for attribute transitions and animations.
interface AttributeTransitionSettings {
/** Transition type */
type: 'interpolation' | 'spring' | 'gpu';
/** Transition duration in milliseconds */
duration?: number;
/** Easing function */
easing?: (t: number) => number;
/** Called when transition starts */
onStart?: () => void;
/** Called during transition */
onUpdate?: (t: number) => void;
/** Called when transition ends */
onEnd?: () => void;
/** Whether to use GPU-based transitions */
useGPU?: boolean;
}
interface TransitionManager {
/** Start attribute transition */
startTransition(
attributeName: string,
fromValue: any,
toValue: any,
settings: AttributeTransitionSettings
): void;
/** Update all active transitions */
update(deltaTime: number): void;
/** Cancel transition */
cancelTransition(attributeName: string): void;
/** Get transition progress */
getTransitionProgress(attributeName: string): number;
}Usage Examples:
// Attribute transitions
const layer = new ScatterplotLayer({
id: 'animated-points',
data: data,
getPosition: d => d.coordinates,
getRadius: d => d.radius,
getColor: d => d.color,
// Animate radius changes
transitions: {
getRadius: {
type: 'interpolation',
duration: 1000,
easing: t => t * t, // quadratic easing
onStart: () => console.log('Radius animation started'),
onEnd: () => console.log('Radius animation completed')
},
// Spring animation for color changes
getColor: {
type: 'spring',
stiffness: 0.1,
damping: 0.8
}
}
});
// Update data with smooth transitions
layer.setProps({
data: newData,
updateTriggers: {
getRadius: Math.random(), // Force radius recalculation
getColor: Math.random() // Force color recalculation
}
});Advanced features for optimizing attribute performance.
interface AttributePerformanceSettings {
/** Whether to use typed array managers for memory pooling */
useTypedArrayManager?: boolean;
/** Buffer reuse strategy */
bufferReuseStrategy?: 'never' | 'same-size' | 'larger';
/** Whether to pack attributes in interleaved arrays */
interleaved?: boolean;
/** Target GPU memory usage limit */
memoryLimit?: number;
/** Batch size for partial updates */
batchSize?: number;
}Usage Examples:
// Optimized attribute manager for large datasets
const optimizedAttributeManager = new AttributeManager(device, {
id: 'large-dataset-attributes',
stats: new Stats({id: 'attributes'}),
performanceSettings: {
useTypedArrayManager: true,
bufferReuseStrategy: 'larger',
interleaved: true,
memoryLimit: 1024 * 1024 * 100, // 100MB limit
batchSize: 10000
}
});
// Add optimized attributes
optimizedAttributeManager.add({
positions: {
size: 3,
accessor: 'getPosition',
settings: {
updateFrequency: 'dynamic',
noAlloc: false
}
},
colors: {
size: 4,
type: 'uint8',
normalized: true,
accessor: 'getColor',
settings: {
updateFrequency: 'once'
}
}
});
// Batch updates for performance
const updateAttributes = (dataChunks) => {
dataChunks.forEach((chunk, index) => {
const startIndex = index * optimizedAttributeManager.batchSize;
const endIndex = Math.min(startIndex + chunk.length, totalDataSize);
optimizedAttributeManager.update({
data: chunk,
range: [startIndex, endIndex],
numInstances: chunk.length
});
});
};