Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(engine) Support all vertex format types in GPUGeometry #1884

Merged
merged 5 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// luma.gl, MIT licese
import {TypedArray, TypedArrayConstructor} from '../..';
import {VertexFormat} from '../types/vertex-formats';

// import {DataType} from '../types/vertex-formats';
// type Omit<DataType, 'float16'> unfortunately breaks Typescript inferance
type DataType = 'uint8' | 'sint8' | 'uint16' | 'sint16' | 'uint32' | 'sint32' | 'float32';

export function getDataTypeFromTypedArray(arrayOrType: TypedArray | TypedArrayConstructor): DataType {
const type = ArrayBuffer.isView(arrayOrType) ? arrayOrType.constructor : arrayOrType;
switch (type) {
case Float32Array:
return 'float32';
case Uint16Array:
return 'uint16';
case Uint32Array:
return 'uint32';
case Uint8Array:
case Uint8ClampedArray:
return 'uint8';
case Int8Array:
return 'sint8';
case Int16Array:
return 'sint16';
case Int32Array:
return 'sint32';
default:
// Failed to deduce data type from typed array
throw new Error(type.constructor.name);
}
}

export function getTypedArrayFromDataType(dataType: DataType): TypedArrayConstructor {
switch (dataType) {
case 'float32':
return Float32Array;
case 'uint16':
return Uint16Array;
case 'uint32':
return Uint32Array;
case 'uint8':
return Uint8Array;
case 'sint8':
return Int8Array;
case 'sint16':
return Int16Array;
case 'sint32':
return Int32Array;
default:
// Failed to deduce typed array from data type
throw new Error(dataType);
}
}

/** Get the vertex format for an attribute with TypedArray and size */
export function getVertexFormatFromAttribute(typedArray: TypedArray, size?: number): VertexFormat {
if(!size || size > 4) {
felixpalmer marked this conversation as resolved.
Show resolved Hide resolved
throw new Error(`size ${size}`);
}

const components = size as 1 | 2 | 3 | 4;
const dataType: DataType = getDataTypeFromTypedArray(typedArray);

if (dataType === 'uint8' || dataType === 'sint8') {
if (components === 1 || components === 3) {
// WebGPU 8 bit formats must be aligned to 16 bit boundaries');
throw new Error(`size: ${size}`);
}
return `${dataType}x${components}`;
}
if (dataType === 'uint16' || dataType === 'sint16') {
if (components === 1 || components === 3) {
// WebGPU 16 bit formats must be aligned to 32 bit boundaries
throw new Error(`size: ${size}`);
}
return `${dataType}x${components}`;
}

if (components === 1) {
return dataType;
}

return `${dataType}x${components}`;
felixpalmer marked this conversation as resolved.
Show resolved Hide resolved
}

1 change: 1 addition & 0 deletions modules/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export {UniformStore} from './lib/uniforms/uniform-store';
// TYPE UTILS
export {decodeVertexFormat} from './adapter/type-utils/decode-vertex-format';
export {decodeTextureFormat} from './adapter/type-utils/decode-texture-format';
export {getDataTypeFromTypedArray, getTypedArrayFromDataType, getVertexFormatFromAttribute} from './adapter/type-utils/vertex-format-from-attribute';

// SHADER TYPE UTILS
export {decodeShaderUniformType} from './adapter/type-utils/decode-shader-types';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// luma.gl, MIT license
import test from 'tape-promise/tape';
import {VertexFormat, getDataTypeFromTypedArray, getTypedArrayFromDataType, getVertexFormatFromAttribute} from '@luma.gl/core';
import type {TypedArray, TypedArrayConstructor} from '@luma.gl/core';

const TEST_CASES: {typedArray: TypedArray, size?: number, result?: VertexFormat, error?: string}[] = [
{typedArray: new Uint8Array(), size: 4, result: 'uint8x4'},
{typedArray: new Uint8ClampedArray(), size: 2, result: 'uint8x2'},
{typedArray: new Int8Array(), size: 4, result: 'sint8x4'},
{typedArray: new Uint16Array(), size: 4, result: 'uint16x4'},
{typedArray: new Int16Array(), size: 2, result: 'sint16x2'},
{typedArray: new Uint32Array(), size: 3, result: 'uint32x3'},
{typedArray: new Int32Array(), size: 1, result: 'sint32'},
{typedArray: new Float32Array(), size: 2, result: 'float32x2'},
{typedArray: new Float32Array(), size: 3, result: 'float32x3'},
{typedArray: new Float32Array(), size: 4, result: 'float32x4'},

{typedArray: new Float32Array(), size: 5, error: 'Invalid attribute size 5'},
{typedArray: new Int32Array(), error: 'Missing attribute size'},
{typedArray: new Uint8Array(), size: 1, error: 'Bad 16 bit alignment'},
{typedArray: new Int16Array(), size: 3, error: 'Bad 32 bit alignment'},
{typedArray: new Float64Array(), size: 2, error: 'Unknown array format'},
];

test('api#getVertexFormatFromAttribute', t => {
for (const {typedArray, size, result, error} of TEST_CASES) {
if (result) {
const vertexFormat = getVertexFormatFromAttribute(typedArray, size);
t.deepEqual(
vertexFormat,
result,
`Typed array: '${typedArray.constructor.name}, size: ${size}' => ${result}`
);
} else {
t.throws(() => {
getVertexFormatFromAttribute(typedArray, size);
}, error);
}
}
t.end();
});

const ARRAY_TEST_CASES: {typedArray: TypedArrayConstructor}[] = [
{typedArray: Uint8Array},
{typedArray: Int8Array},
{typedArray: Uint16Array},
{typedArray: Int16Array},
{typedArray: Uint32Array},
{typedArray: Int32Array},
{typedArray: Float32Array}
]

test('api#getDataTypeFromTypedArray', t => {
for (const {typedArray} of ARRAY_TEST_CASES) {
const dataType = getDataTypeFromTypedArray(typedArray);
const result = getTypedArrayFromDataType(dataType);
t.deepEqual(
typedArray,
result,
`TypedArray '${typedArray.name}, => ${dataType} => ${result.name}`
);
}
t.end();
});
1 change: 1 addition & 0 deletions modules/core/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import './adapter/type-utils/decode-attribute-type.spec';

import './adapter/type-utils/decode-vertex-format.spec';
import './adapter/type-utils/decode-texture-format.spec';
import './adapter/type-utils/vertex-format-from-attribute.spec';

// adapter
import './adapter/canvas-context.spec';
Expand Down
5 changes: 3 additions & 2 deletions modules/engine/src/geometry/gpu-geometry.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {PrimitiveTopology, BufferLayout} from '@luma.gl/core';
import {Device, Buffer, uid, assert} from '@luma.gl/core';
import {Device, Buffer, uid, assert, getVertexFormatFromAttribute} from '@luma.gl/core';
import type {Geometry} from '../geometry/geometry';

export type GPUGeometryProps = {
Expand Down Expand Up @@ -114,7 +114,8 @@ export function getAttributeBuffersFromGeometry(
case 'TEXCOORD_0': name = 'texCoords'; break;
}
attributes[name] = device.createBuffer({data: attribute.value, id: `${attributeName}-buffer`});
bufferLayout.push({name, format: `float32x${attribute.size as 2 | 3 | 4}`});
const {value, size} = attribute;
bufferLayout.push({name, format: getVertexFormatFromAttribute(value, size)});
}

const vertexCount = geometry._calculateVertexCount(geometry.attributes, geometry.indices)
Expand Down
2 changes: 2 additions & 0 deletions modules/webgl/src/classic/typed-array-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const ERR_TYPE_DEDUCTION = 'Failed to deduce GL constant from typed array';
/**
* Converts TYPED ARRAYS to corresponding GL constant
* Used to auto deduce gl parameter types
* @deprecated Use getDataTypeFromTypedArray
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess one would need two functions: getDataTypeFromTypedArray and getGLFromVertexType

Note to self: "Vertex Type" being "Normalized Data Type" which is indeed confusing - might want to consider renaming VertexType to NormalizedDataType

* @param arrayOrType
* @returns
*/
Expand Down Expand Up @@ -40,6 +41,7 @@ export function getGLTypeFromTypedArray(arrayOrType: TypedArray): GLDataType {
/**
* Converts GL constant to corresponding TYPED ARRAY
* Used to auto deduce gl parameter types
* @deprecated Use getTypedArrayFromDataType
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, one would need the getVertexTypeFromGL

* @param glType
* @param param1
* @returns
Expand Down
Loading