MCP 3D Printer Server

by DMontgomery40
Verified
import { 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' ] ] ); /** * A WebGPU backend utility module for managing shader attributes. * * @private */ class WebGPUAttributeUtils { /** * Constructs a new utility object. * * @param {WebGPUBackend} backend - The WebGPU backend. */ constructor( backend ) { /** * A reference to the WebGPU backend. * * @type {WebGPUBackend} */ this.backend = backend; } /** * Creates the GPU buffer for the given buffer attribute. * * @param {BufferAttribute} attribute - The buffer attribute. * @param {GPUBufferUsage} usage - A flag that indicates how the buffer may be used after its creation. */ 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 ) { if ( array.constructor === Int16Array ) { array = new Int32Array( array ); } else if ( array.constructor === Uint16Array ) { array = new Uint32Array( array ); if ( usage & GPUBufferUsage.INDEX ) { for ( let i = 0; i < array.length; i ++ ) { if ( array[ i ] === 0xffff ) array[ i ] = 0xffffffff; // use correct primitive restart index } } } } 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; } } /** * Updates the GPU buffer of the given buffer attribute. * * @param {BufferAttribute} attribute - The buffer attribute. */ 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 isTypedArray = this._isTypedArray( array ); const updateRanges = bufferAttribute.updateRanges; if ( updateRanges.length === 0 ) { // Not using update ranges device.queue.writeBuffer( buffer, 0, array, 0 ); } else { const byteOffsetFactor = isTypedArray ? 1 : array.BYTES_PER_ELEMENT; for ( let i = 0, l = updateRanges.length; i < l; i ++ ) { const range = updateRanges[ i ]; const dataOffset = range.start * byteOffsetFactor; const size = range.count * byteOffsetFactor; device.queue.writeBuffer( buffer, 0, array, dataOffset, size ); } bufferAttribute.clearUpdateRanges(); } } /** * This method creates the vertex buffer layout data which are * require when creating a render pipeline for the given render object. * * @param {RenderObject} renderObject - The render object. * @return {Array<Object>} An array holding objects which describe the vertex buffer layout. */ 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() ); } /** * Destroys the GPU buffer of the given buffer attribute. * * @param {BufferAttribute} attribute - The buffer attribute. */ destroyAttribute( attribute ) { const backend = this.backend; const data = backend.get( this._getBufferAttribute( attribute ) ); data.buffer.destroy(); backend.delete( attribute ); } /** * This method performs a readback operation by moving buffer data from * a storage buffer attribute from the GPU to the CPU. * * @async * @param {StorageBufferAttribute} attribute - The storage buffer attribute. * @return {Promise<ArrayBuffer>} A promise that resolves with the buffer data when the data are ready. */ 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 }_readback`, size, usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ } ); const cmdEncoder = device.createCommandEncoder( { label: `readback_encoder_${ attribute.name }` } ); cmdEncoder.copyBufferToBuffer( bufferGPU, 0, readBufferGPU, 0, size ); const gpuCommands = cmdEncoder.finish(); device.queue.submit( [ gpuCommands ] ); await readBufferGPU.mapAsync( GPUMapMode.READ ); const arrayBuffer = readBufferGPU.getMappedRange(); const dstBuffer = new attribute.array.constructor( arrayBuffer.slice( 0 ) ); readBufferGPU.unmap(); return dstBuffer.buffer; } /** * Returns the vertex format of the given buffer attribute. * * @private * @param {BufferAttribute} geometryAttribute - The buffer attribute. * @return {String} The vertex format (e.g. 'float32x3'). */ _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; } /** * Returns `true` if the given array is a typed array. * * @private * @param {Any} array - The array. * @return {Boolean} Whether the given array is a typed array or not. */ _isTypedArray( array ) { return ArrayBuffer.isView( array ) && ! ( array instanceof DataView ); } /** * Utility method for handling interleaved buffer attributes correctly. * To process them, their `InterleavedBuffer` is returned. * * @private * @param {BufferAttribute} attribute - The attribute. * @return {BufferAttribute|InterleavedBuffer} */ _getBufferAttribute( attribute ) { if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; return attribute; } } export default WebGPUAttributeUtils;