Skip to content

Commit

Permalink
Wrong fix for WebGPU gLTF support
Browse files Browse the repository at this point in the history
This works but is probably the wrong fix. I don't know three.js
that well so I thought I'd post this as an example of a fix
and it might lead to the correct fix.

The issue is WebGPU does not support itemSize = 1 or itemSize = 3
for 8bit or 16bit values.

When using KHR_mesh_quantization some attributes are like Int16x3.
But, according to the KHR_mesh_quantization spec they are actually
padded to Int16x4

In WebGL it's fine to set the attribute to

```
size = 3, type = gl.SHORT, normalize = true, stride = 8.
```

But in WebGPU there is no such thing as

`size = 3, type = gl.SHORT`

If it existed it would be called `sint16x3` or `uint16x3` or
`snorm16x3` or `unorm16x3` but none of those exist. Instead
you have to set the attribute to `???x4` for these situations.

You then need to be careful the shader doesn't actually use Z.
The shader can still declare its attribute has vec3f/vec3<f32>
but if it declares it as vec4/vec4<f32> the z value would be
undefined (whatever happens to be in the file in the padding)

My guess is the correct fix, rather than this hack which expands
the type, it should look at the stride. The stride to figure
out when to expand the type from x3 to x4 or from x1 to x2
or x1 to x4
  • Loading branch information
greggman committed Mar 13, 2023
1 parent 2236516 commit 6dd385a
Showing 1 changed file with 41 additions and 102 deletions.
143 changes: 41 additions & 102 deletions examples/jsm/renderers/webgpu/WebGPURenderPipeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ import {
ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstAlphaFactor, OneMinusDstAlphaFactor, DstColorFactor, OneMinusDstColorFactor, SrcAlphaSaturateFactor
} from 'three';

const typedArraysToVertexFormatPrefix = new Map( [

[ Int8Array, [ 'sint8', 'snorm8' ] ],
[ Uint8Array, [ 'uint8', 'unorm8' ] ],
[ Int16Array, [ 'sint16', 'snorm16' ] ],
[ Uint16Array, [ 'uint16', 'unorm16' ] ],
[ Int32Array, [ 'sint32', 'snorm32' ] ],
[ Uint32Array, [ 'uint32', 'unorm32' ] ],
[ Float32Array, [ 'float32', ] ],

] );

const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [

[ Int32Array, 'sint32' ],
[ Uint32Array, 'uint32' ],
[ Float32Array, 'float32' ],

] );

class WebGPURenderPipeline {

constructor( device, utils ) {
Expand Down Expand Up @@ -563,127 +583,46 @@ class WebGPURenderPipeline {

}

_getVertexFormat( type, bytesPerElement ) {

// float

if ( type === 'float' ) return GPUVertexFormat.Float32;

if ( type === 'vec2' ) {

if ( bytesPerElement === 2 ) {

return GPUVertexFormat.Float16x2;

} else {

return GPUVertexFormat.Float32x2;

}

}

if ( type === 'vec3' ) return GPUVertexFormat.Float32x3;

if ( type === 'vec4' ) {

if ( bytesPerElement === 2 ) {

return GPUVertexFormat.Float16x4;

} else {

return GPUVertexFormat.Float32x4;

}

}

// int

if ( type === 'int' ) return GPUVertexFormat.Sint32;

if ( type === 'ivec2' ) {

if ( bytesPerElement === 1 ) {
_getVertexFormat( geometryAttribute ) {

return GPUVertexFormat.Sint8x2;
const { itemSize, normalized } = geometryAttribute;
const ArrayType = geometryAttribute.array.__proto__.constructor;

} else if ( bytesPerElement === 2 ) {
let format;

return GPUVertexFormat.Sint16x2;
if ( itemSize == 1 ) {

} else {
format = typeArraysToVertexFormatPrefixForItemSize1.get(ArrayType)

return GPUVertexFormat.Sint32x2;

}

}

if ( type === 'ivec3' ) return GPUVertexFormat.Sint32x3;

if ( type === 'ivec4' ) {

if ( bytesPerElement === 1 ) {

return GPUVertexFormat.Sint8x4;

} else if ( bytesPerElement === 2 ) {

return GPUVertexFormat.Sint16x4;

} else {

return GPUVertexFormat.Sint32x4;

}

}

// uint

if ( type === 'uint' ) return GPUVertexFormat.Uint32;
} else {

if ( type === 'uvec2' ) {
const prefix = typedArraysToVertexFormatPrefix.get(ArrayType)[ normalized ? 1 : 0 ];

if ( bytesPerElement === 1 ) {
if ( prefix ) {

return GPUVertexFormat.Uint8x2;
const bytesPerUnit = ArrayType.BYTES_PER_ELEMENT * itemSize;
const paddedBytesPerUnit = Math.floor( ( bytesPerUnit + 3 ) / 4 ) * 4;
const paddedItemSize = paddedBytesPerUnit / ArrayType.BYTES_PER_ELEMENT;

} else if ( bytesPerElement === 2 ) {
if ( paddedItemSize % 1) {

return GPUVertexFormat.Uint16x2;
throw new Error( `bad item size `);

} else {
}

return GPUVertexFormat.Uint32x2;
format = `${prefix}x${paddedItemSize}`;

}

}

if ( type === 'uvec3' ) return GPUVertexFormat.Uint32x3;

if ( type === 'uvec4' ) {

if ( bytesPerElement === 1 ) {

return GPUVertexFormat.Uint8x4;
if ( !format ) {

} else if ( bytesPerElement === 2 ) {

return GPUVertexFormat.Uint16x4;

} else {

return GPUVertexFormat.Uint32x4;

}
console.error( 'THREE.WebGPURenderer: Shader variable type not supported yet.' );

}

console.error( 'THREE.WebGPURenderer: Shader variable type not supported yet.', type );
return format;

}

Expand All @@ -700,10 +639,10 @@ class WebGPURenderPipeline {
const type = nodeAttribute.type;

const geometryAttribute = geometry.getAttribute( name );
const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT;

const format = this._getVertexFormat( type, bytesPerElement );
const format = this._getVertexFormat( geometryAttribute );

const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT;
let arrayStride = geometryAttribute.itemSize * bytesPerElement;
let offset = 0;

Expand Down

0 comments on commit 6dd385a

Please sign in to comment.