webgpuBufferManager.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import { DataBuffer } from '../../Meshes/dataBuffer';
  2. import { WebGPUDataBuffer } from '../../Meshes/WebGPU/webgpuDataBuffer';
  3. import { Nullable } from '../../types';
  4. /** @hidden */
  5. export class WebGPUBufferManager {
  6. private _device: GPUDevice;
  7. private _deferredReleaseBuffers: Array<GPUBuffer> = [];
  8. private static _IsGPUBuffer(buffer: DataBuffer | GPUBuffer): buffer is GPUBuffer {
  9. return (buffer as DataBuffer).underlyingResource === undefined;
  10. }
  11. constructor(device: GPUDevice) {
  12. this._device = device;
  13. }
  14. public createRawBuffer(viewOrSize: ArrayBufferView | number, flags: GPUBufferUsageFlags, mappedAtCreation = false): GPUBuffer {
  15. const alignedLength = (viewOrSize as ArrayBufferView).byteLength !== undefined ? ((viewOrSize as ArrayBufferView).byteLength + 3) & ~3 : ((viewOrSize as number) + 3) & ~3; // 4 bytes alignments (because of the upload which requires this)
  16. const verticesBufferDescriptor = {
  17. mappedAtCreation,
  18. size: alignedLength,
  19. usage: flags
  20. };
  21. return this._device.createBuffer(verticesBufferDescriptor);
  22. }
  23. public createBuffer(viewOrSize: ArrayBufferView | number, flags: GPUBufferUsageFlags): DataBuffer {
  24. const isView = (viewOrSize as ArrayBufferView).byteLength !== undefined;
  25. const buffer = this.createRawBuffer(viewOrSize, flags);
  26. const dataBuffer = new WebGPUDataBuffer(buffer);
  27. dataBuffer.references = 1;
  28. dataBuffer.capacity = isView ? (viewOrSize as ArrayBufferView).byteLength : viewOrSize as number;
  29. if (isView) {
  30. this.setSubData(dataBuffer, 0, viewOrSize as ArrayBufferView);
  31. }
  32. return dataBuffer;
  33. }
  34. public setSubData(dataBuffer: WebGPUDataBuffer, dstByteOffset: number, src: ArrayBufferView, srcByteOffset = 0, byteLength = 0): void {
  35. const buffer = dataBuffer.underlyingResource as GPUBuffer;
  36. byteLength = byteLength || src.byteLength;
  37. byteLength = Math.min(byteLength, dataBuffer.capacity - dstByteOffset);
  38. // After Migration to Canary
  39. let chunkStart = src.byteOffset + srcByteOffset;
  40. let chunkEnd = chunkStart + byteLength;
  41. // 4 bytes alignments for upload
  42. const alignedLength = (byteLength + 3) & ~3;
  43. if (alignedLength !== byteLength) {
  44. const tempView = new Uint8Array(src.buffer.slice(chunkStart, chunkEnd));
  45. src = new Uint8Array(alignedLength);
  46. tempView.forEach((element, index) => {
  47. (src as Uint8Array)[index] = element;
  48. });
  49. srcByteOffset = 0;
  50. chunkStart = 0;
  51. chunkEnd = alignedLength;
  52. byteLength = alignedLength;
  53. }
  54. // Chunk
  55. const maxChunk = 1024 * 1024 * 15;
  56. let offset = 0;
  57. while ((chunkEnd - (chunkStart + offset)) > maxChunk) {
  58. this._device.defaultQueue.writeBuffer(buffer, dstByteOffset + offset, src.buffer, chunkStart + offset, maxChunk);
  59. offset += maxChunk;
  60. }
  61. this._device.defaultQueue.writeBuffer(buffer, dstByteOffset + offset, src.buffer, chunkStart + offset, byteLength - offset);
  62. }
  63. public readDataFromBuffer(buffer: GPUBuffer, size: number, offset = 0, destroyBuffer = true): Promise<Uint8Array> {
  64. return new Promise((resolve, reject) => {
  65. buffer.mapAsync(GPUMapMode.READ, offset, size).then(() => {
  66. const copyArrayBuffer = buffer.getMappedRange(offset, size);
  67. const data = new Uint8Array(size);
  68. data.set(new Uint8Array(copyArrayBuffer));
  69. buffer.unmap();
  70. if (destroyBuffer) {
  71. buffer.destroy();
  72. }
  73. resolve(data);
  74. }, (reason) => reject(reason));
  75. });
  76. }
  77. public releaseBuffer(buffer: DataBuffer | GPUBuffer): boolean {
  78. if (WebGPUBufferManager._IsGPUBuffer(buffer)) {
  79. this._deferredReleaseBuffers.push(buffer);
  80. return true;
  81. }
  82. buffer.references--;
  83. if (buffer.references === 0) {
  84. this._deferredReleaseBuffers.push(buffer.underlyingResource as GPUBuffer);
  85. return true;
  86. }
  87. return false;
  88. }
  89. public destroyDeferredBuffers(): void {
  90. for (let i = 0; i < this._deferredReleaseBuffers.length; ++i) {
  91. this._deferredReleaseBuffers[i].destroy();
  92. }
  93. this._deferredReleaseBuffers.length = 0;
  94. }
  95. }