diff --git a/docs/api-reference/core/attribute-manager.md b/docs/api-reference/core/attribute-manager.md index f2ee23e6a8a..b6e17d5667c 100644 --- a/docs/api-reference/core/attribute-manager.md +++ b/docs/api-reference/core/attribute-manager.md @@ -52,7 +52,7 @@ the attributes that should be auto-calculated. ```js attributeManager.add({ positions: {size: 2, accessor: 'getPosition', update: calculatePositions}, - colors: {size: 4, type: GL.UNSIGNED_BYTE, accessor: 'getColor', update: calculateColors} + colors: {size: 4, type: 'unorm8', accessor: 'getColor', update: calculateColors} }); ``` @@ -61,13 +61,10 @@ Takes a single parameter as a map of attribute descriptor objects: * keys are attribute names * values are objects with attribute definitions: - luma.gl [accessor parameters](https://luma.gl/docs/api-reference-legacy/classes/accessor): - * `type` (string, optional) - data type of the attribute, see "Remarks" section below. + * `type` (string, optional) - data type of the attribute, see "Remarks" section below. Default `'float32'`. * `size` (number) - number of elements per vertex - * `normalized` (boolean) - default `false` - * `integer` (boolean) - WebGL2 only, default `false` - * `divisor` (boolean, optional) - `1` if this is an instanced attribute - (a.k.a. divisor). Default to `0`. - deck.gl attribute configurations: + * `stepMode` (string, optional) - One of `'vertex'`, `'instance'` and `'dynamic'`. If set to `'dynamic'`, will be resolved to `'instance'` when this attribute is applied to an instanced model, and `'vertex'` otherwise. Default `'vertex'`. * `isIndexed` (boolean, optional) - if this is an index attribute (a.k.a. indices). Default to `false`. * `accessor` (string | string[] | Function) - accessor name(s) that will @@ -85,12 +82,10 @@ Takes a single parameter as a map of attribute descriptor objects: * `size` (number) - number of elements per vertex * `vertexOffset` (number) - offset of the attribute by vertex (stride). Default `0`. * `elementOffset` (number) - offset of the attribute by element. default `0`. - * `divisor` (boolean, optional) - `1` if this is an instanced attribute - (a.k.a. divisor). Default to `0`. #### `addInstanced` {#addinstanced} -Shorthand for `add()` in which all attributes `instanced` field are set to `true`. +Shorthand for `add()` in which all attributes `stepMode` field are set to `'instance'`. #### `remove` {#remove} @@ -152,6 +147,16 @@ Notes: * Any preallocated buffers in "buffers" matching registered attribute names will be used. No update will happen in this case. * Calls onUpdateStart and onUpdateEnd log callbacks before and after. +#### `getBufferLayouts` + +Returns WebGPU-style buffer layout descriptors. + +Parameters: + +* `modelInfo` (object) - a luma.gl `Model` or a similarly shaped object + + `isInstanced` (boolean) - used to resolve `stepMode: 'dynamic'` + + ## Remarks ### Attribute Type @@ -160,15 +165,20 @@ The following `type` values are supported for attribute definitions: | type | value array type | notes | | ---- | ---------------- | ----- | -| `GL.FLOAT` | `Float32Array` | | -| `GL.DOUBLE` | `Float64Array` | Because 64-bit floats are not supported by WebGL, the value is converted to an interleaved `Float32Array` before uploading to the GPU. It is exposed to the vertex shader as two attributes, `` and `64Low`, the sum of which is the 64-bit value. | -| `GL.BYTE` | `Int8Array` | | -| `GL.SHORT` | `Int16Array` | | -| `GL.INT` | `Int32Array` | | -| `GL.UNSIGNED_BYTE` | `Uint8ClampedArray` | | -| `GL.UNSIGNED_SHORT` | `Uint16Array` | | -| `GL.UNSIGNED_INT` | `Uint32Array` | | - +| `float32` | `Float32Array` | | +| `float64` | `Float64Array` | Because 64-bit floats are not supported by WebGL, the value is converted to an interleaved `Float32Array` before uploading to the GPU. It is exposed to the vertex shader as two attributes, `` and `64Low`, the sum of which is the 64-bit value. | +| `sint8` | `Int8Array` | | +| `snorm8` | `Int8Array` | Normalized | +| `uint8` | `Uint8ClampedArray` | | +| `unorm8` | `Uint8ClampedArray` | Normalized | +| `sint16` | `Int16Array` | | +| `snorm16` | `Int16Array` | Normalized | +| `uint16` | `Uint16Array` | | +| `unorm16` | `Uint16Array` | Normalized | +| `sint32` | `Int32Array` | | +| `uint32` | `Uint32Array` | | + + ## Source [modules/core/src/lib/attribute-manager.ts](https://github.com/visgl/deck.gl/blob/master/modules/core/src/lib/attribute/attribute-manager.ts) diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md index 5963fead561..1685ac1655c 100644 --- a/docs/upgrade-guide.md +++ b/docs/upgrade-guide.md @@ -52,6 +52,7 @@ class MyLayer { While the 9.0 release of deck.gl does not yet support WebGPU, our goal is to enable WebGPU soon in a 9.x release. A number of changes will be required to deck.gl curtom layers: - deck.gl now uses uniform buffers instead of global uniforms. It is not yet required to use uniform buffers but it will be necessary if you would like to run deck.gl on WebGPU in future releases. +- When defining an attribute, `type` is now a WebGPU-style [string format](https://luma.gl/docs/api-guide/gpu/gpu-attributes#vertexformat) instead of GL constant, and `divisor` is replaced by `stepMode`. See [AttributeManager.add](./api-reference/core/attribute-manager.md#add) - WebGL draw modes `GL.TRIANGLE_FAN` and `GL.LINE_LOOP` are not supported on WebGPU. Select a different topology when creating geometries. - The luma picking module now [uses uniform buffers](https://github.com/visgl/luma.gl/blob/master/modules/shadertools/src/modules/engine/picking/picking.ts#L34-L50). To access picking state in shaders use `picking.isActive` rather than `picking_isActive` diff --git a/modules/core/src/lib/attribute/attribute-manager.ts b/modules/core/src/lib/attribute/attribute-manager.ts index e1c68a66e5b..f9faa4741dc 100644 --- a/modules/core/src/lib/attribute/attribute-manager.ts +++ b/modules/core/src/lib/attribute/attribute-manager.ts @@ -138,7 +138,7 @@ export default class AttributeManager { // Adds attributes addInstanced(attributes: {[id: string]: AttributeOptions}) { - this._add(attributes, {instanced: 1}); + this._add(attributes, {stepMode: 'instance'}); } /** @@ -308,50 +308,44 @@ export default class AttributeManager { return changedAttributes; } + /** Generate WebGPU-style buffer layout descriptors from all attributes */ getBufferLayouts( - attributes?: {[id: string]: Attribute}, - excludeAttributes: Record = {} - ): BufferLayout[] { - if (!attributes) { - attributes = this.getAttributes(); - } - const bufferMaps: BufferLayout[] = []; - for (const attributeName in attributes) { - if (!excludeAttributes[attributeName]) { - bufferMaps.push(attributes[attributeName].getBufferLayout()); - } + /** A luma.gl Model-shaped object that supplies additional hint to attribute resolution */ + modelInfo?: { + /** Whether the model is instanced */ + isInstanced?: boolean; } - return bufferMaps; + ): BufferLayout[] { + return Object.values(this.getAttributes()).map(attribute => + attribute.getBufferLayout(modelInfo) + ); } // PRIVATE METHODS - // Used to register an attribute - private _add(attributes: {[id: string]: AttributeOptions}, extraProps: any = {}) { + /** Register new attributes */ + private _add( + /** A map from attribute name to attribute descriptors */ + attributes: {[id: string]: AttributeOptions}, + /** Additional attribute settings to pass to all attributes */ + overrideOptions?: Partial + ) { for (const attributeName in attributes) { const attribute = attributes[attributeName]; + const props: AttributeOptions = { + ...attribute, + id: attributeName, + size: (attribute.isIndexed && 1) || attribute.size || 1, + ...overrideOptions + }; + // Initialize the attribute descriptor, with WebGL and metadata fields - this.attributes[attributeName] = this._createAttribute(attributeName, attribute, extraProps); + this.attributes[attributeName] = new Attribute(this.device, props); } this._mapUpdateTriggersToAttributes(); } - /* eslint-enable max-statements */ - - private _createAttribute(name: string, attribute: AttributeOptions, extraProps: any) { - // For expected default values see: - // https://github.com/visgl/luma.gl/blob/1affe21352e289eeaccee2a876865138858a765c/modules/webgl/src/classes/accessor.js#L5-L13 - // and https://deck.gl/docs/api-reference/core/attribute-manager#add - const props: AttributeOptions = { - ...attribute, - id: name, - size: (attribute.isIndexed && 1) || attribute.size || 1, - divisor: extraProps.instanced ? 1 : attribute.divisor || 0 - }; - - return new Attribute(this.device, props); - } // build updateTrigger name to attribute name mapping private _mapUpdateTriggersToAttributes() { diff --git a/modules/core/src/lib/attribute/attribute.ts b/modules/core/src/lib/attribute/attribute.ts index 9199cd42a5e..2ce352d79e4 100644 --- a/modules/core/src/lib/attribute/attribute.ts +++ b/modules/core/src/lib/attribute/attribute.ts @@ -43,6 +43,7 @@ export type Updater = ( export type AttributeOptions = DataColumnOptions<{ transition?: boolean | Partial; + stepMode?: 'vertex' | 'instance' | 'dynamic'; noAlloc?: boolean; update?: Updater; accessor?: Accessor | string | string[]; @@ -363,19 +364,31 @@ export default class Attribute extends DataColumn { return result; } - getBufferLayout( + protected _getBufferLayout( attributeName: string = this.id, options: Partial | null = null ): BufferLayout { diff --git a/modules/core/src/lib/layer.ts b/modules/core/src/lib/layer.ts index 3273b2e719f..0f5658608c7 100644 --- a/modules/core/src/lib/layer.ts +++ b/modules/core/src/lib/layer.ts @@ -771,7 +771,7 @@ export default abstract class Layer extends Component< if (bufferLayoutChanged) { // AttributeManager is always defined when this method is called const attributeManager = this.getAttributeManager()!; - model.setBufferLayout(attributeManager.getBufferLayouts()); + model.setBufferLayout(attributeManager.getBufferLayouts(model)); // All attributes must be reset after buffer layout change changedAttributes = attributeManager.getAttributes(); } diff --git a/modules/extensions/src/brushing/brushing-extension.ts b/modules/extensions/src/brushing/brushing-extension.ts index 33a8b50189d..6ebca94c86b 100644 --- a/modules/extensions/src/brushing/brushing-extension.ts +++ b/modules/extensions/src/brushing/brushing-extension.ts @@ -69,15 +69,8 @@ export default class BrushingExtension extends LayerExtension { attributeManager.add({ brushingTargets: { size: 2, - accessor: 'getBrushingTarget', - shaderAttributes: { - brushingTargets: { - divisor: 0 - }, - instanceBrushingTargets: { - divisor: 1 - } - } + stepMode: 'dynamic', + accessor: 'getBrushingTarget' } }); } diff --git a/modules/extensions/src/brushing/shader-module.ts b/modules/extensions/src/brushing/shader-module.ts index 1519bf13970..2e7368c7510 100644 --- a/modules/extensions/src/brushing/shader-module.ts +++ b/modules/extensions/src/brushing/shader-module.ts @@ -37,11 +37,7 @@ const vs = glsl` uniform vec2 brushing_mousePos; uniform float brushing_radius; - #ifdef NON_INSTANCED_MODEL in vec2 brushingTargets; - #else - in vec2 instanceBrushingTargets; - #endif out float brushing_isVisible; @@ -89,11 +85,7 @@ const inject = { } else if (brushing_target == 1) { brushingTarget = geometry.worldPositionAlt.xy; } else { - #ifdef NON_INSTANCED_MODEL brushingTarget = brushingTargets; - #else - brushingTarget = instanceBrushingTargets; - #endif } bool visible; if (brushing_target == 3) { diff --git a/modules/extensions/src/collision-filter/collision-filter-extension.ts b/modules/extensions/src/collision-filter/collision-filter-extension.ts index 344136e4f22..9ab37d29435 100644 --- a/modules/extensions/src/collision-filter/collision-filter-extension.ts +++ b/modules/extensions/src/collision-filter/collision-filter-extension.ts @@ -68,11 +68,8 @@ export default class CollisionFilterExtension extends LayerExtension { attributeManager!.add({ collisionPriorities: { size: 1, - accessor: 'getCollisionPriority', - shaderAttributes: { - collisionPriorities: {divisor: 0}, - instanceCollisionPriorities: {divisor: 1} - } + stepMode: 'dynamic', + accessor: 'getCollisionPriority' } }); } diff --git a/modules/extensions/src/collision-filter/shader-module.ts b/modules/extensions/src/collision-filter/shader-module.ts index 337c73a01b6..eead389c0a3 100644 --- a/modules/extensions/src/collision-filter/shader-module.ts +++ b/modules/extensions/src/collision-filter/shader-module.ts @@ -4,11 +4,7 @@ import {project} from '@deck.gl/core'; import {glsl} from '../utils/syntax-tags'; const vs = glsl` -#ifdef NON_INSTANCED_MODEL in float collisionPriorities; -#else -in float instanceCollisionPriorities; -#endif uniform sampler2D collision_texture; uniform bool collision_sort; @@ -60,11 +56,7 @@ const inject = { `, 'vs:DECKGL_FILTER_GL_POSITION': glsl` if (collision_sort) { - #ifdef NON_INSTANCED_MODEL float collisionPriority = collisionPriorities; - #else - float collisionPriority = instanceCollisionPriorities; - #endif position.z = -0.001 * collisionPriority * position.w; // Support range -1000 -> 1000 } diff --git a/modules/extensions/src/data-filter/data-filter-extension.ts b/modules/extensions/src/data-filter/data-filter-extension.ts index ab6369a1b04..0591273724a 100644 --- a/modules/extensions/src/data-filter/data-filter-extension.ts +++ b/modules/extensions/src/data-filter/data-filter-extension.ts @@ -167,15 +167,8 @@ export default class DataFilterExtension extends LayerExtension< filterValues: { size: filterSize, type: fp64 ? 'float64' : 'float32', - accessor: 'getFilterValue', - shaderAttributes: { - filterValues: { - divisor: 0 - }, - instanceFilterValues: { - divisor: 1 - } - } + stepMode: 'dynamic', + accessor: 'getFilterValue' } }); } @@ -184,19 +177,12 @@ export default class DataFilterExtension extends LayerExtension< attributeManager.add({ filterCategoryValues: { size: categorySize, + stepMode: 'dynamic', accessor: 'getFilterCategory', transform: categorySize === 1 ? d => extension._getCategoryKey.call(this, d, 0) - : d => d.map((x, i) => extension._getCategoryKey.call(this, x, i)), - shaderAttributes: { - filterCategoryValues: { - divisor: 0 - }, - instanceFilterCategoryValues: { - divisor: 1 - } - } + : d => d.map((x, i) => extension._getCategoryKey.call(this, x, i)) } }); } diff --git a/modules/extensions/src/data-filter/shader-module.ts b/modules/extensions/src/data-filter/shader-module.ts index e9008dce44d..7cb5333bb10 100644 --- a/modules/extensions/src/data-filter/shader-module.ts +++ b/modules/extensions/src/data-filter/shader-module.ts @@ -25,20 +25,6 @@ export type Defines = { * Enable 64-bit precision in numeric filter. */ DATAFILTER_DOUBLE?: boolean; - - // Defines derived in shader - /** - * Numeric filter attribute - */ - DATAFILTER_ATTRIB?: 'filterValues' | 'instanceFilterValues'; - /** - * Numeric filter attribute (low bits). Only used when `DATAFILTER_DOUBLE = true` - */ - DATAFILTER_ATTRIB_64LOW?: 'filterValues64Low' | 'instanceFilterValues64Low'; - /** - * Category filter attribute - */ - DATACATEGORY_ATTRIB?: 'filterCategoryValues' | 'instanceFilterCategoryValues'; }; const vs = glsl` @@ -53,17 +39,9 @@ uniform ivec4 filter_categoryBitMask; uniform DATAFILTER_TYPE filter_softMax; uniform DATAFILTER_TYPE filter_max; - #ifdef NON_INSTANCED_MODEL - #define DATAFILTER_ATTRIB filterValues - #define DATAFILTER_ATTRIB_64LOW filterValues64Low - #else - #define DATAFILTER_ATTRIB instanceFilterValues - #define DATAFILTER_ATTRIB_64LOW instanceFilterValues64Low - #endif - - in DATAFILTER_TYPE DATAFILTER_ATTRIB; + in DATAFILTER_TYPE filterValues; #ifdef DATAFILTER_DOUBLE - in DATAFILTER_TYPE DATAFILTER_ATTRIB_64LOW; + in DATAFILTER_TYPE filterValues64Low; uniform DATAFILTER_TYPE filter_min64High; uniform DATAFILTER_TYPE filter_max64High; @@ -72,12 +50,7 @@ uniform ivec4 filter_categoryBitMask; #ifdef DATACATEGORY_TYPE - #ifdef NON_INSTANCED_MODEL - #define DATACATEGORY_ATTRIB filterCategoryValues - #else - #define DATACATEGORY_ATTRIB instanceFilterCategoryValues - #endif - in DATACATEGORY_TYPE DATACATEGORY_ATTRIB; + in DATACATEGORY_TYPE filterCategoryValues; #endif out float dataFilter_value; @@ -119,7 +92,7 @@ float dataFilter_reduceValue(vec4 value) { } #endif -#ifdef DATACATEGORY_ATTRIB +#ifdef DATACATEGORY_TYPE void dataFilter_setCategoryValue(DATACATEGORY_TYPE category) { #if DATACATEGORY_CHANNELS == 1 // One 128-bit mask int dataFilter_masks = filter_categoryBitMask[int(category / 32.0)]; @@ -223,19 +196,19 @@ const inject = { 'vs:#main-start': glsl` dataFilter_value = 1.0; if (filter_enabled) { - #ifdef DATAFILTER_ATTRIB + #ifdef DATAFILTER_TYPE #ifdef DATAFILTER_DOUBLE dataFilter_setValue( - DATAFILTER_ATTRIB - filter_min64High + DATAFILTER_ATTRIB_64LOW, - DATAFILTER_ATTRIB - filter_max64High + DATAFILTER_ATTRIB_64LOW + filterValues - filter_min64High + filterValues64Low, + filterValues - filter_max64High + filterValues64Low ); #else - dataFilter_setValue(DATAFILTER_ATTRIB, DATAFILTER_ATTRIB); + dataFilter_setValue(filterValues, filterValues); #endif #endif - #ifdef DATACATEGORY_ATTRIB - dataFilter_setCategoryValue(DATACATEGORY_ATTRIB); + #ifdef DATACATEGORY_TYPE + dataFilter_setCategoryValue(filterCategoryValues); #endif } `, diff --git a/modules/extensions/src/fill-style/fill-style-extension.ts b/modules/extensions/src/fill-style/fill-style-extension.ts index 2a1e4ae411b..179e23b1c87 100644 --- a/modules/extensions/src/fill-style/fill-style-extension.ts +++ b/modules/extensions/src/fill-style/fill-style-extension.ts @@ -110,41 +110,20 @@ export default class FillStyleExtension extends LayerExtension vertexPositions: { size: 3, type: 'float64', + stepMode: 'dynamic', fp64: this.use64bitPositions(), transition: ATTRIBUTE_TRANSITION, accessor: 'getPolygon', @@ -219,68 +220,47 @@ export default class SolidPolygonLayer update: this.calculatePositions, noAlloc, shaderAttributes: { - instancePositions: { - vertexOffset: 0, - divisor: 1 - }, - instanceNextPositions: { - vertexOffset: 1, - divisor: 1 + nextVertexPositions: { + vertexOffset: 1 } } }, instanceVertexValid: { size: 1, type: 'uint16', - divisor: 1, + stepMode: 'instance', // eslint-disable-next-line @typescript-eslint/unbound-method update: this.calculateVertexValid, noAlloc }, elevations: { size: 1, + stepMode: 'dynamic', transition: ATTRIBUTE_TRANSITION, - accessor: 'getElevation', - shaderAttributes: { - instanceElevations: { - divisor: 1 - } - } + accessor: 'getElevation' }, fillColors: { size: this.props.colorFormat.length, type: 'unorm8', + stepMode: 'dynamic', transition: ATTRIBUTE_TRANSITION, accessor: 'getFillColor', - defaultValue: DEFAULT_COLOR, - shaderAttributes: { - instanceFillColors: { - divisor: 1 - } - } + defaultValue: DEFAULT_COLOR }, lineColors: { size: this.props.colorFormat.length, type: 'unorm8', + stepMode: 'dynamic', transition: ATTRIBUTE_TRANSITION, accessor: 'getLineColor', - defaultValue: DEFAULT_COLOR, - shaderAttributes: { - instanceLineColors: { - divisor: 1 - } - } + defaultValue: DEFAULT_COLOR }, pickingColors: { size: 4, type: 'uint8', + stepMode: 'dynamic', accessor: (object, {index, target: value}) => - this.encodePickingColor(object && object.__source ? object.__source.index : index, value), - shaderAttributes: { - instancePickingColors: { - divisor: 1 - } - } + this.encodePickingColor(object && object.__source ? object.__source.index : index, value) } }); /* eslint-enable max-len */ @@ -412,11 +392,10 @@ export default class SolidPolygonLayer let sideModel; let wireframeModel; - const bufferLayout = this.getAttributeManager()!.getBufferLayouts(); - if (filled) { const shaders = this.getShaders('top'); shaders.defines.NON_INSTANCED_MODEL = 1; + const bufferLayout = this.getAttributeManager()!.getBufferLayouts({isInstanced: false}); topModel = new Model(this.context.device, { ...shaders, @@ -433,6 +412,8 @@ export default class SolidPolygonLayer }); } if (extruded) { + const bufferLayout = this.getAttributeManager()!.getBufferLayouts({isInstanced: true}); + sideModel = new Model(this.context.device, { ...this.getShaders('side'), id: `${id}-side`, diff --git a/test/modules/core/lib/attribute/attribute-manager.spec.ts b/test/modules/core/lib/attribute/attribute-manager.spec.ts index 4ddcb247ecb..92b151bf617 100644 --- a/test/modules/core/lib/attribute/attribute-manager.spec.ts +++ b/test/modules/core/lib/attribute/attribute-manager.spec.ts @@ -70,10 +70,11 @@ test('AttributeManager.add', t => { {positions: ['positions'], getPosition: ['positions']}, 'AttributeManager.add - build update triggers mapping' ); + attributeManager.addInstanced({instancePositions: {size: 2, accessor: 'getPosition', update}}); t.equals( - attributeManager.getAttributes()['positions'].settings.divisor, - 0, - 'AttributeManager.add creates attribute with default divisor of 0' + attributeManager.getAttributes()['instancePositions'].settings.stepMode, + 'instance', + 'AttributeManager.addInstanced creates attribute with stepMode:instance' ); t.end(); }); @@ -370,3 +371,90 @@ test('AttributeManager.invalidate', t => { t.end(); }); + +test('AttributeManager.getBufferLayouts', t => { + const attributeManager = new AttributeManager(device); + attributeManager.add({ + // indexed attribute + indices: {size: 1, isIndexed: true, update}, + // non-instanced attribute + colors: {size: 4, type: 'unorm8', stepMode: 'vertex', accessor: 'getColor'}, + // instanced attribute + instanceColors: {size: 4, type: 'unorm8', stepMode: 'instance', accessor: 'getColor'}, + // dynamically assigned stepMode + positions: {size: 3, type: 'float64', fp64: true, stepMode: 'dynamic', accessor: 'getPosition'} + }); + + t.deepEqual( + attributeManager.getBufferLayouts(), + [ + { + name: 'indices', + byteStride: 4, + attributes: [ + { + attribute: 'indices', + format: 'uint32', + byteOffset: 0 + } + ], + stepMode: 'vertex' + }, + { + name: 'colors', + byteStride: 4, + attributes: [ + { + attribute: 'colors', + format: 'unorm8x4', + byteOffset: 0 + } + ], + stepMode: 'vertex' + }, + { + name: 'instanceColors', + byteStride: 4, + attributes: [ + { + attribute: 'instanceColors', + format: 'unorm8x4', + byteOffset: 0 + } + ], + stepMode: 'instance' + }, + { + name: 'positions', + byteStride: 24, + attributes: [ + { + attribute: 'positions', + format: 'float32x3', + byteOffset: 0 + }, + { + attribute: 'positions64Low', + format: 'float32x3', + byteOffset: 12 + } + ], + stepMode: 'instance' + } + ], + 'getBufferLayouts()' + ); + + t.is( + attributeManager.getBufferLayouts({isInstanced: false})[3].stepMode, + 'vertex', + 'dynamic attribute.stepMode in nonInstancedModel' + ); + t.is( + attributeManager.getBufferLayouts({isInstanced: true})[3].stepMode, + 'instance', + 'dynamic attribute.stepMode in instancedModel' + ); + + t.end(); +}); diff --git a/test/modules/core/lib/attribute/attribute.spec.ts b/test/modules/core/lib/attribute/attribute.spec.ts index 9006a87ddf5..dc4a97043c8 100644 --- a/test/modules/core/lib/attribute/attribute.spec.ts +++ b/test/modules/core/lib/attribute/attribute.spec.ts @@ -253,13 +253,10 @@ test('Attribute#shaderAttributes', t => { id: 'positions', update, size: 3, + stepMode: 'instance', shaderAttributes: { - instancePositions: { - divisor: 1 - }, - instanceNextPositions: { - vertexOffset: 1, - divisor: 1 + nextPositions: { + vertexOffset: 1 } } }); @@ -271,15 +268,12 @@ test('Attribute#shaderAttributes', t => { t.is(attributeLayout.format, 'float32x3', 'Attribute position has correct format'); t.is(attributeLayout.byteOffset, 0, 'Attribute position has correct offset'); attributeLayout = bufferLayout.attributes[1]; - t.is(attributeLayout.format, 'float32x3', 'Attribute instancePositions has correct format'); - t.is(attributeLayout.byteOffset, 0, 'Attribute instancePositions has correct offset'); - attributeLayout = bufferLayout.attributes[2]; - t.is(attributeLayout.format, 'float32x3', 'Attribute instanceNextPositions has correct format'); - t.is(attributeLayout.byteOffset, 12, 'Attribute instanceNextPositions has correct offset'); + t.is(attributeLayout.format, 'float32x3', 'Attribute nextPositions has correct format'); + t.is(attributeLayout.byteOffset, 12, 'Attribute nextPositions has correct offset'); t.deepEquals( attribute.getValue(), - {positions: buffer1, instancePositions: buffer1, instanceNextPositions: buffer1}, + {positions: buffer1, nextPositions: buffer1}, 'Attribute has buffer' );