Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/flo-bit/shapecraft. flo-bit.dev/shapecraft
Something went wrong. Try again.
7.2 kB · 312 lines
JavaScript
at commit da69385e
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313import { GPUInputStepMode } from './WebGPUConstants.js';
import { Float16BufferAttribute } from '../../../core/BufferAttribute.js';
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 typedAttributeToVertexFormatPrefix = new Map( [ [ Float16BufferAttribute, [ 'float16', ]],] );
const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [ [ Int32Array, 'sint32' ], [ Int16Array, 'sint32' ], // patch for INT16 [ Uint32Array, 'uint32' ], [ Uint16Array, 'uint32' ], // patch for UINT16 [ Float32Array, 'float32' ]] );
class WebGPUAttributeUtils {
constructor( backend ) {
this.backend = backend;
}
createAttribute( attribute, usage ) {
const bufferAttribute = this._getBufferAttribute( attribute );
const backend = this.backend; const bufferData = backend.get( bufferAttribute );
let buffer = bufferData.buffer;
if ( buffer === undefined ) {
const device = backend.device;
let array = bufferAttribute.array;
// patch for INT16 and UINT16 if ( attribute.normalized === false && ( array.constructor === Int16Array || array.constructor === Uint16Array ) ) {
const tempArray = new Uint32Array( array.length ); for ( let i = 0; i < array.length; i ++ ) {
tempArray[ i ] = array[ i ];
}
array = tempArray;
}
bufferAttribute.array = array;
if ( ( bufferAttribute.isStorageBufferAttribute || bufferAttribute.isStorageInstancedBufferAttribute ) && bufferAttribute.itemSize === 3 ) {
array = new array.constructor( bufferAttribute.count * 4 );
for ( let i = 0; i < bufferAttribute.count; i ++ ) {
array.set( bufferAttribute.array.subarray( i * 3, i * 3 + 3 ), i * 4 );
}
// Update BufferAttribute bufferAttribute.itemSize = 4; bufferAttribute.array = array;
}
const size = array.byteLength + ( ( 4 - ( array.byteLength % 4 ) ) % 4 ); // ensure 4 byte alignment, see #20441
buffer = device.createBuffer( { label: bufferAttribute.name, size: size, usage: usage, mappedAtCreation: true } );
new array.constructor( buffer.getMappedRange() ).set( array );
buffer.unmap();
bufferData.buffer = buffer;
}
}
updateAttribute( attribute ) {
const bufferAttribute = this._getBufferAttribute( attribute );
const backend = this.backend; const device = backend.device;
const buffer = backend.get( bufferAttribute ).buffer;
const array = bufferAttribute.array; const updateRanges = bufferAttribute.updateRanges;
if ( updateRanges.length === 0 ) {
// Not using update ranges
device.queue.writeBuffer( buffer, 0, array, 0 );
} else {
for ( let i = 0, l = updateRanges.length; i < l; i ++ ) {
const range = updateRanges[ i ]; device.queue.writeBuffer( buffer, 0, array, range.start * array.BYTES_PER_ELEMENT, range.count * array.BYTES_PER_ELEMENT );
}
bufferAttribute.clearUpdateRanges();
}
}
createShaderVertexBuffers( renderObject ) {
const attributes = renderObject.getAttributes(); const vertexBuffers = new Map();
for ( let slot = 0; slot < attributes.length; slot ++ ) {
const geometryAttribute = attributes[ slot ]; const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT; const bufferAttribute = this._getBufferAttribute( geometryAttribute );
let vertexBufferLayout = vertexBuffers.get( bufferAttribute );
if ( vertexBufferLayout === undefined ) {
let arrayStride, stepMode;
if ( geometryAttribute.isInterleavedBufferAttribute === true ) {
arrayStride = geometryAttribute.data.stride * bytesPerElement; stepMode = geometryAttribute.data.isInstancedInterleavedBuffer ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
} else {
arrayStride = geometryAttribute.itemSize * bytesPerElement; stepMode = geometryAttribute.isInstancedBufferAttribute ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
}
// patch for INT16 and UINT16 if ( geometryAttribute.normalized === false && ( geometryAttribute.array.constructor === Int16Array || geometryAttribute.array.constructor === Uint16Array ) ) {
arrayStride = 4;
}
vertexBufferLayout = { arrayStride, attributes: [], stepMode };
vertexBuffers.set( bufferAttribute, vertexBufferLayout );
}
const format = this._getVertexFormat( geometryAttribute ); const offset = ( geometryAttribute.isInterleavedBufferAttribute === true ) ? geometryAttribute.offset * bytesPerElement : 0;
vertexBufferLayout.attributes.push( { shaderLocation: slot, offset, format } );
}
return Array.from( vertexBuffers.values() );
}
destroyAttribute( attribute ) {
const backend = this.backend; const data = backend.get( this._getBufferAttribute( attribute ) );
data.buffer.destroy();
backend.delete( attribute );
}
async getArrayBufferAsync( attribute ) {
const backend = this.backend; const device = backend.device;
const data = backend.get( this._getBufferAttribute( attribute ) );
const bufferGPU = data.buffer; const size = bufferGPU.size;
const readBufferGPU = device.createBuffer( { label: attribute.name, size, usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ } );
const cmdEncoder = device.createCommandEncoder( {} );
cmdEncoder.copyBufferToBuffer( bufferGPU, 0, readBufferGPU, 0, size );
readBufferGPU.unmap();
const gpuCommands = cmdEncoder.finish(); device.queue.submit( [ gpuCommands ] );
await readBufferGPU.mapAsync( GPUMapMode.READ );
const arrayBuffer = readBufferGPU.getMappedRange();
return arrayBuffer;
}
_getVertexFormat( geometryAttribute ) {
const { itemSize, normalized } = geometryAttribute; const ArrayType = geometryAttribute.array.constructor; const AttributeType = geometryAttribute.constructor;
let format;
if ( itemSize == 1 ) {
format = typeArraysToVertexFormatPrefixForItemSize1.get( ArrayType );
} else {
const prefixOptions = typedAttributeToVertexFormatPrefix.get( AttributeType ) || typedArraysToVertexFormatPrefix.get( ArrayType ); const prefix = prefixOptions[ normalized ? 1 : 0 ];
if ( prefix ) {
const bytesPerUnit = ArrayType.BYTES_PER_ELEMENT * itemSize; const paddedBytesPerUnit = Math.floor( ( bytesPerUnit + 3 ) / 4 ) * 4; const paddedItemSize = paddedBytesPerUnit / ArrayType.BYTES_PER_ELEMENT;
if ( paddedItemSize % 1 ) {
throw new Error( 'THREE.WebGPUAttributeUtils: Bad vertex format item size.' );
}
format = `${prefix}x${paddedItemSize}`;
}
}
if ( ! format ) {
console.error( 'THREE.WebGPUAttributeUtils: Vertex format not supported yet.' );
}
return format;
}
_getBufferAttribute( attribute ) {
if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
return attribute;
}
}
export default WebGPUAttributeUtils;