You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
3.3 KiB
187 lines
3.3 KiB
class WebGPUAttributes {
|
|
|
|
constructor( device ) {
|
|
|
|
this.buffers = new WeakMap();
|
|
this.device = device;
|
|
|
|
}
|
|
|
|
get( attribute ) {
|
|
|
|
if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
|
|
|
|
return this.buffers.get( attribute );
|
|
|
|
}
|
|
|
|
remove( attribute ) {
|
|
|
|
if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
|
|
|
|
const data = this.buffers.get( attribute );
|
|
|
|
if ( data ) {
|
|
|
|
this._destroyBuffers( data );
|
|
|
|
this.buffers.delete( attribute );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
update( attribute, isIndex = false, usage = null ) {
|
|
|
|
if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
|
|
|
|
let data = this.buffers.get( attribute );
|
|
|
|
if ( data === undefined ) {
|
|
|
|
if ( usage === null ) {
|
|
|
|
usage = ( isIndex === true ) ? GPUBufferUsage.INDEX : GPUBufferUsage.VERTEX;
|
|
|
|
}
|
|
|
|
data = this._createBuffer( attribute, usage );
|
|
|
|
this.buffers.set( attribute, data );
|
|
|
|
} else if ( usage && usage !== data.usage ) {
|
|
|
|
this._destroyBuffers( data );
|
|
|
|
data = this._createBuffer( attribute, usage );
|
|
|
|
this.buffers.set( attribute, data );
|
|
|
|
} else if ( data.version < attribute.version ) {
|
|
|
|
this._writeBuffer( data.buffer, attribute );
|
|
|
|
data.version = attribute.version;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
async getArrayBuffer( attribute ) {
|
|
|
|
const data = this.get( attribute );
|
|
const device = this.device;
|
|
|
|
const gpuBuffer = data.buffer;
|
|
const size = gpuBuffer.size;
|
|
|
|
let gpuReadBuffer = data.readBuffer;
|
|
let needsUnmap = true;
|
|
|
|
if ( gpuReadBuffer === null ) {
|
|
|
|
gpuReadBuffer = device.createBuffer( {
|
|
size,
|
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
|
|
} );
|
|
|
|
needsUnmap = false;
|
|
|
|
data.readBuffer = gpuReadBuffer;
|
|
|
|
}
|
|
|
|
const cmdEncoder = device.createCommandEncoder( {} );
|
|
|
|
cmdEncoder.copyBufferToBuffer(
|
|
gpuBuffer,
|
|
0,
|
|
gpuReadBuffer,
|
|
0,
|
|
size
|
|
);
|
|
|
|
if ( needsUnmap ) gpuReadBuffer.unmap();
|
|
|
|
const gpuCommands = cmdEncoder.finish();
|
|
device.queue.submit( [ gpuCommands ] );
|
|
|
|
await gpuReadBuffer.mapAsync( GPUMapMode.READ );
|
|
|
|
const arrayBuffer = gpuReadBuffer.getMappedRange();
|
|
|
|
return new Float32Array( arrayBuffer );
|
|
|
|
}
|
|
|
|
_createBuffer( attribute, usage ) {
|
|
|
|
const array = attribute.array;
|
|
const size = array.byteLength + ( ( 4 - ( array.byteLength % 4 ) ) % 4 ); // ensure 4 byte alignment, see #20441
|
|
|
|
const buffer = this.device.createBuffer( {
|
|
size,
|
|
usage: usage | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
|
|
mappedAtCreation: true
|
|
} );
|
|
|
|
new array.constructor( buffer.getMappedRange() ).set( array );
|
|
|
|
buffer.unmap();
|
|
|
|
attribute.onUploadCallback();
|
|
|
|
return {
|
|
version: attribute.version,
|
|
buffer,
|
|
readBuffer: null,
|
|
usage
|
|
};
|
|
|
|
}
|
|
|
|
_writeBuffer( buffer, attribute ) {
|
|
|
|
const device = this.device;
|
|
|
|
const array = attribute.array;
|
|
const updateRange = attribute.updateRange;
|
|
|
|
if ( updateRange.count === - 1 ) {
|
|
|
|
// Not using update ranges
|
|
|
|
device.queue.writeBuffer(
|
|
buffer,
|
|
0,
|
|
array,
|
|
0
|
|
);
|
|
|
|
} else {
|
|
|
|
device.queue.writeBuffer(
|
|
buffer,
|
|
0,
|
|
array,
|
|
updateRange.offset * array.BYTES_PER_ELEMENT,
|
|
updateRange.count * array.BYTES_PER_ELEMENT
|
|
);
|
|
|
|
updateRange.count = - 1; // reset range
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_destroyBuffers( { buffer, readBuffer } ) {
|
|
|
|
buffer.destroy();
|
|
|
|
if ( readBuffer !== null ) readBuffer.destroy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export default WebGPUAttributes;
|
|
|