WebGPUAttributeUtils.js 5.2 KB

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