WebGPUAttributes.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import { DynamicDrawUsage } from "three";
  2. class WebGPUAttributes {
  3. constructor( device ) {
  4. this.buffers = new WeakMap();
  5. this.device = device;
  6. }
  7. get( attribute ) {
  8. const bufferAttribute = this._getBufferAttribute( attribute );
  9. return this.buffers.get( bufferAttribute );
  10. }
  11. remove( attribute ) {
  12. const bufferAttribute = this._getBufferAttribute( attribute );
  13. const data = this.buffers.get( bufferAttribute );
  14. if ( data ) {
  15. this._destroyBuffers( data );
  16. this.buffers.delete( bufferAttribute );
  17. }
  18. }
  19. update( attribute, isIndex = false, gpuUsage = null ) {
  20. const bufferAttribute = this._getBufferAttribute( attribute );
  21. let data = this.buffers.get( bufferAttribute );
  22. if ( data === undefined ) {
  23. if ( gpuUsage === null ) {
  24. gpuUsage = ( isIndex === true ) ? GPUBufferUsage.INDEX : GPUBufferUsage.VERTEX;
  25. }
  26. data = this._createBuffer( bufferAttribute, gpuUsage );
  27. this.buffers.set( bufferAttribute, data );
  28. } else if ( gpuUsage && gpuUsage !== data.usage ) {
  29. this._destroyBuffers( data );
  30. data = this._createBuffer( bufferAttribute, gpuUsage );
  31. this.buffers.set( bufferAttribute, data );
  32. } else if ( data.version < bufferAttribute.version || bufferAttribute.usage === DynamicDrawUsage ) {
  33. this._writeBuffer( data.buffer, bufferAttribute );
  34. data.version = bufferAttribute.version;
  35. }
  36. }
  37. async getArrayBuffer( attribute ) {
  38. const data = this.get( attribute );
  39. const device = this.device;
  40. const gpuBuffer = data.buffer;
  41. const size = gpuBuffer.size;
  42. let gpuReadBuffer = data.readBuffer;
  43. let needsUnmap = true;
  44. if ( gpuReadBuffer === null ) {
  45. gpuReadBuffer = device.createBuffer( {
  46. label: attribute.name,
  47. size,
  48. usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
  49. } );
  50. needsUnmap = false;
  51. data.readBuffer = gpuReadBuffer;
  52. }
  53. const cmdEncoder = device.createCommandEncoder( {} );
  54. cmdEncoder.copyBufferToBuffer(
  55. gpuBuffer,
  56. 0,
  57. gpuReadBuffer,
  58. 0,
  59. size
  60. );
  61. if ( needsUnmap ) gpuReadBuffer.unmap();
  62. const gpuCommands = cmdEncoder.finish();
  63. device.queue.submit( [ gpuCommands ] );
  64. await gpuReadBuffer.mapAsync( GPUMapMode.READ );
  65. const arrayBuffer = gpuReadBuffer.getMappedRange();
  66. return arrayBuffer;
  67. }
  68. _getBufferAttribute( attribute ) {
  69. if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
  70. return attribute;
  71. }
  72. _createBuffer( bufferAttribute, usage ) {
  73. const array = bufferAttribute.array;
  74. const size = array.byteLength + ( ( 4 - ( array.byteLength % 4 ) ) % 4 ); // ensure 4 byte alignment, see #20441
  75. const buffer = this.device.createBuffer( {
  76. label: bufferAttribute.name,
  77. size,
  78. usage: usage | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
  79. mappedAtCreation: true
  80. } );
  81. new array.constructor( buffer.getMappedRange() ).set( array );
  82. buffer.unmap();
  83. bufferAttribute.onUploadCallback();
  84. return {
  85. version: bufferAttribute.version,
  86. buffer,
  87. readBuffer: null,
  88. usage
  89. };
  90. }
  91. _writeBuffer( buffer, attribute ) {
  92. const device = this.device;
  93. const array = attribute.array;
  94. const updateRange = attribute.updateRange;
  95. if ( updateRange.count === - 1 ) {
  96. // Not using update ranges
  97. device.queue.writeBuffer(
  98. buffer,
  99. 0,
  100. array,
  101. 0
  102. );
  103. } else {
  104. device.queue.writeBuffer(
  105. buffer,
  106. 0,
  107. array,
  108. updateRange.offset * array.BYTES_PER_ELEMENT,
  109. updateRange.count * array.BYTES_PER_ELEMENT
  110. );
  111. updateRange.count = - 1; // reset range
  112. }
  113. }
  114. _destroyBuffers( { buffer, readBuffer } ) {
  115. buffer.destroy();
  116. if ( readBuffer !== null ) readBuffer.destroy();
  117. }
  118. }
  119. export default WebGPUAttributes;