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.

188 lines
3.3 KiB

2 years ago
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;