WebGPUAttributeUtils.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import { Float16BufferAttribute } from 'three';
  2. import { GPUInputStepMode } from './WebGPUConstants.js';
  3. const typedArraysToVertexFormatPrefix = new Map( [
  4. [ Int8Array, [ 'sint8', 'snorm8' ]],
  5. [ Uint8Array, [ 'uint8', 'unorm8' ]],
  6. [ Int16Array, [ 'sint16', 'snorm16' ]],
  7. [ Uint16Array, [ 'uint16', 'unorm16' ]],
  8. [ Int32Array, [ 'sint32', 'snorm32' ]],
  9. [ Uint32Array, [ 'uint32', 'unorm32' ]],
  10. [ Float32Array, [ 'float32', ]],
  11. ] );
  12. const typedAttributeToVertexFormatPrefix = new Map( [
  13. [ Float16BufferAttribute, [ 'float16', ]],
  14. ] );
  15. const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [
  16. [ Int32Array, 'sint32' ],
  17. [ Uint32Array, 'uint32' ],
  18. [ Float32Array, 'float32' ]
  19. ] );
  20. class WebGPUAttributeUtils {
  21. constructor( backend ) {
  22. this.backend = backend;
  23. }
  24. createAttribute( attribute, usage ) {
  25. const bufferAttribute = this._getBufferAttribute( attribute );
  26. const backend = this.backend;
  27. const bufferData = backend.get( bufferAttribute );
  28. let buffer = bufferData.buffer;
  29. if ( buffer === undefined ) {
  30. const device = backend.device;
  31. const array = bufferAttribute.array;
  32. const size = array.byteLength + ( ( 4 - ( array.byteLength % 4 ) ) % 4 ); // ensure 4 byte alignment, see #20441
  33. buffer = device.createBuffer( {
  34. label: bufferAttribute.name,
  35. size: size,
  36. usage: usage,
  37. mappedAtCreation: true
  38. } );
  39. new array.constructor( buffer.getMappedRange() ).set( array );
  40. buffer.unmap();
  41. bufferData.buffer = buffer;
  42. }
  43. }
  44. updateAttribute( attribute ) {
  45. const bufferAttribute = this._getBufferAttribute( attribute );
  46. const backend = this.backend;
  47. const device = backend.device;
  48. const buffer = backend.get( bufferAttribute ).buffer;
  49. const array = bufferAttribute.array;
  50. const updateRanges = bufferAttribute.updateRanges;
  51. if ( updateRanges.length === 0 ) {
  52. // Not using update ranges
  53. device.queue.writeBuffer(
  54. buffer,
  55. 0,
  56. array,
  57. 0
  58. );
  59. } else {
  60. for ( let i = 0, l = updateRanges.length; i < l; i ++ ) {
  61. const range = updateRanges[ i ];
  62. device.queue.writeBuffer(
  63. buffer,
  64. 0,
  65. array,
  66. range.start * array.BYTES_PER_ELEMENT,
  67. range.count * array.BYTES_PER_ELEMENT
  68. );
  69. }
  70. bufferAttribute.clearUpdateRanges();
  71. }
  72. }
  73. createShaderVertexBuffers( renderObject ) {
  74. const attributes = renderObject.getAttributes();
  75. const vertexBuffers = new Map();
  76. for ( let slot = 0; slot < attributes.length; slot ++ ) {
  77. const geometryAttribute = attributes[ slot ];
  78. const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT;
  79. const bufferAttribute = this._getBufferAttribute( geometryAttribute );
  80. let vertexBufferLayout = vertexBuffers.get( bufferAttribute );
  81. if ( vertexBufferLayout === undefined ) {
  82. let arrayStride, stepMode;
  83. if ( geometryAttribute.isInterleavedBufferAttribute === true ) {
  84. arrayStride = geometryAttribute.data.stride * bytesPerElement;
  85. stepMode = geometryAttribute.data.isInstancedInterleavedBuffer ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
  86. } else {
  87. arrayStride = geometryAttribute.itemSize * bytesPerElement;
  88. stepMode = geometryAttribute.isInstancedBufferAttribute ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
  89. }
  90. vertexBufferLayout = {
  91. arrayStride,
  92. attributes: [],
  93. stepMode
  94. };
  95. vertexBuffers.set( bufferAttribute, vertexBufferLayout );
  96. }
  97. const format = this._getVertexFormat( geometryAttribute );
  98. const offset = ( geometryAttribute.isInterleavedBufferAttribute === true ) ? geometryAttribute.offset * bytesPerElement : 0;
  99. vertexBufferLayout.attributes.push( {
  100. shaderLocation: slot,
  101. offset,
  102. format
  103. } );
  104. }
  105. return Array.from( vertexBuffers.values() );
  106. }
  107. destroyAttribute( attribute ) {
  108. const backend = this.backend;
  109. const data = backend.get( this._getBufferAttribute( attribute ) );
  110. data.buffer.destroy();
  111. backend.delete( attribute );
  112. }
  113. async getArrayBufferAsync( attribute ) {
  114. const backend = this.backend;
  115. const device = backend.device;
  116. const data = backend.get( this._getBufferAttribute( attribute ) );
  117. const bufferGPU = data.buffer;
  118. const size = bufferGPU.size;
  119. let readBufferGPU = data.readBuffer;
  120. let needsUnmap = true;
  121. if ( readBufferGPU === undefined ) {
  122. readBufferGPU = device.createBuffer( {
  123. label: attribute.name,
  124. size,
  125. usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
  126. } );
  127. needsUnmap = false;
  128. data.readBuffer = readBufferGPU;
  129. }
  130. const cmdEncoder = device.createCommandEncoder( {} );
  131. cmdEncoder.copyBufferToBuffer(
  132. bufferGPU,
  133. 0,
  134. readBufferGPU,
  135. 0,
  136. size
  137. );
  138. if ( needsUnmap ) readBufferGPU.unmap();
  139. const gpuCommands = cmdEncoder.finish();
  140. device.queue.submit( [ gpuCommands ] );
  141. await readBufferGPU.mapAsync( GPUMapMode.READ );
  142. const arrayBuffer = readBufferGPU.getMappedRange();
  143. return arrayBuffer;
  144. }
  145. _getVertexFormat( geometryAttribute ) {
  146. const { itemSize, normalized } = geometryAttribute;
  147. const ArrayType = geometryAttribute.array.constructor;
  148. const AttributeType = geometryAttribute.constructor;
  149. let format;
  150. if ( itemSize == 1 ) {
  151. format = typeArraysToVertexFormatPrefixForItemSize1.get( ArrayType );
  152. } else {
  153. const prefixOptions = typedAttributeToVertexFormatPrefix.get( AttributeType ) || typedArraysToVertexFormatPrefix.get( ArrayType );
  154. const prefix = prefixOptions[ normalized ? 1 : 0 ];
  155. if ( prefix ) {
  156. const bytesPerUnit = ArrayType.BYTES_PER_ELEMENT * itemSize;
  157. const paddedBytesPerUnit = Math.floor( ( bytesPerUnit + 3 ) / 4 ) * 4;
  158. const paddedItemSize = paddedBytesPerUnit / ArrayType.BYTES_PER_ELEMENT;
  159. if ( paddedItemSize % 1 ) {
  160. throw new Error( 'THREE.WebGPUAttributeUtils: Bad vertex format item size.' );
  161. }
  162. format = `${prefix}x${paddedItemSize}`;
  163. }
  164. }
  165. if ( ! format ) {
  166. console.error( 'THREE.WebGPUAttributeUtils: Vertex format not supported yet.' );
  167. }
  168. return format;
  169. }
  170. _getBufferAttribute( attribute ) {
  171. if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
  172. return attribute;
  173. }
  174. }
  175. export default WebGPUAttributeUtils;