Skip to content

Commit 463b561

Browse files
authored
feat(engine) Support normalized attributes in GPUGeometry (#1894)
1 parent 88a73b4 commit 463b561

File tree

3 files changed

+21
-7
lines changed

3 files changed

+21
-7
lines changed

modules/core/src/adapter/type-utils/vertex-format-from-attribute.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {VertexFormat} from '../types/vertex-formats';
66
// type Omit<DataType, 'float16'> unfortunately breaks Typescript inferance
77
type DataType = 'uint8' | 'sint8' | 'uint16' | 'sint16' | 'uint32' | 'sint32' | 'float32';
88

9+
type DataTypeNorm = 'unorm8' | 'snorm8'| 'unorm16' | 'snorm16';
10+
911
export function getDataTypeFromTypedArray(arrayOrType: TypedArray | TypedArrayConstructor): DataType {
1012
const type = ArrayBuffer.isView(arrayOrType) ? arrayOrType.constructor : arrayOrType;
1113
switch (type) {
@@ -53,26 +55,32 @@ export function getTypedArrayFromDataType(dataType: DataType): TypedArrayConstru
5355
}
5456

5557
/** Get the vertex format for an attribute with TypedArray and size */
56-
export function getVertexFormatFromAttribute(typedArray: TypedArray, size?: number): VertexFormat {
58+
export function getVertexFormatFromAttribute(typedArray: TypedArray, size: number, normalized?: boolean): VertexFormat {
5759
if(!size || size > 4) {
5860
throw new Error(`size ${size}`);
5961
}
6062

6163
const components = size as 1 | 2 | 3 | 4;
62-
const dataType: DataType = getDataTypeFromTypedArray(typedArray);
64+
let dataType: DataType | DataTypeNorm = getDataTypeFromTypedArray(typedArray);
6365

6466
if (dataType === 'uint8' || dataType === 'sint8') {
6567
if (components === 1 || components === 3) {
6668
// WebGPU 8 bit formats must be aligned to 16 bit boundaries');
6769
throw new Error(`size: ${size}`);
6870
}
71+
if (normalized) {
72+
dataType = dataType.replace('int', 'norm') as 'unorm8' | 'snorm8';
73+
}
6974
return `${dataType}x${components}`;
7075
}
7176
if (dataType === 'uint16' || dataType === 'sint16') {
7277
if (components === 1 || components === 3) {
7378
// WebGPU 16 bit formats must be aligned to 32 bit boundaries
7479
throw new Error(`size: ${size}`);
7580
}
81+
if (normalized) {
82+
dataType = dataType.replace('int', 'norm') as 'unorm16' | 'snorm16';
83+
}
7684
return `${dataType}x${components}`;
7785
}
7886

modules/core/test/adapter/type-utils/vertex-format-from-attribute.spec.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import test from 'tape-promise/tape';
33
import {VertexFormat, getDataTypeFromTypedArray, getTypedArrayFromDataType, getVertexFormatFromAttribute} from '@luma.gl/core';
44
import type {TypedArray, TypedArrayConstructor} from '@luma.gl/core';
55

6-
const TEST_CASES: {typedArray: TypedArray, size?: number, result?: VertexFormat, error?: string}[] = [
6+
const TEST_CASES: {typedArray: TypedArray, size?: number, normalized?: boolean, result?: VertexFormat, error?: string}[] = [
77
{typedArray: new Uint8Array(), size: 4, result: 'uint8x4'},
88
{typedArray: new Uint8ClampedArray(), size: 2, result: 'uint8x2'},
99
{typedArray: new Int8Array(), size: 4, result: 'sint8x4'},
@@ -15,6 +15,12 @@ const TEST_CASES: {typedArray: TypedArray, size?: number, result?: VertexFormat,
1515
{typedArray: new Float32Array(), size: 3, result: 'float32x3'},
1616
{typedArray: new Float32Array(), size: 4, result: 'float32x4'},
1717

18+
{typedArray: new Uint8Array(), size: 2, normalized: true, result: 'unorm8x2'},
19+
{typedArray: new Uint8ClampedArray(), size: 4, normalized: true, result: 'unorm8x4'},
20+
{typedArray: new Int8Array(), size: 2, normalized: true, result: 'snorm8x2'},
21+
{typedArray: new Uint16Array(), size: 2, normalized: true, result: 'unorm16x2'},
22+
{typedArray: new Int16Array(), size: 4, normalized: true, result: 'snorm16x4'},
23+
1824
{typedArray: new Float32Array(), size: 5, error: 'Invalid attribute size 5'},
1925
{typedArray: new Int32Array(), error: 'Missing attribute size'},
2026
{typedArray: new Uint8Array(), size: 1, error: 'Bad 16 bit alignment'},
@@ -23,9 +29,9 @@ const TEST_CASES: {typedArray: TypedArray, size?: number, result?: VertexFormat,
2329
];
2430

2531
test('api#getVertexFormatFromAttribute', t => {
26-
for (const {typedArray, size, result, error} of TEST_CASES) {
32+
for (const {typedArray, size, normalized, result, error} of TEST_CASES) {
2733
if (result) {
28-
const vertexFormat = getVertexFormatFromAttribute(typedArray, size);
34+
const vertexFormat = getVertexFormatFromAttribute(typedArray, size, normalized);
2935
t.deepEqual(
3036
vertexFormat,
3137
result,

modules/engine/src/geometry/gpu-geometry.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ export function getAttributeBuffersFromGeometry(
114114
case 'TEXCOORD_0': name = 'texCoords'; break;
115115
}
116116
attributes[name] = device.createBuffer({data: attribute.value, id: `${attributeName}-buffer`});
117-
const {value, size} = attribute;
118-
bufferLayout.push({name, format: getVertexFormatFromAttribute(value, size)});
117+
const {value, size, normalized} = attribute;
118+
bufferLayout.push({name, format: getVertexFormatFromAttribute(value, size, normalized)});
119119
}
120120

121121
const vertexCount = geometry._calculateVertexCount(geometry.attributes, geometry.indices)

0 commit comments

Comments
 (0)