123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- import { WebGPUDataBuffer } from "../../Meshes/WebGPU/webgpuDataBuffer.js";
- import { FromHalfFloat } from "../../Misc/textureTools.js";
- import { allocateAndCopyTypedBuffer } from "../Extensions/engine.readTexture.js";
- // eslint-disable-next-line @typescript-eslint/naming-convention
- import * as WebGPUConstants from "./webgpuConstants.js";
- /** @internal */
- export class WebGPUBufferManager {
- static _IsGPUBuffer(buffer) {
- return buffer.underlyingResource === undefined;
- }
- static _FlagsToString(flags, suffix = "") {
- let result = suffix;
- for (let i = 0; i <= 9; ++i) {
- if (flags & (1 << i)) {
- if (result) {
- result += "_";
- }
- result += WebGPUConstants.BufferUsage[1 << i];
- }
- }
- return result;
- }
- constructor(engine, device) {
- this._deferredReleaseBuffers = [];
- this._engine = engine;
- this._device = device;
- }
- createRawBuffer(viewOrSize, flags, mappedAtCreation = false, label) {
- const alignedLength = viewOrSize.byteLength !== undefined ? (viewOrSize.byteLength + 3) & ~3 : (viewOrSize + 3) & ~3; // 4 bytes alignments (because of the upload which requires this)
- const verticesBufferDescriptor = {
- label: "BabylonWebGPUDevice" + this._engine.uniqueId + "_" + WebGPUBufferManager._FlagsToString(flags, label ?? "Buffer") + "_size" + alignedLength,
- mappedAtCreation,
- size: alignedLength,
- usage: flags,
- };
- return this._device.createBuffer(verticesBufferDescriptor);
- }
- createBuffer(viewOrSize, flags, label) {
- const isView = viewOrSize.byteLength !== undefined;
- const buffer = this.createRawBuffer(viewOrSize, flags, undefined, label);
- const dataBuffer = new WebGPUDataBuffer(buffer);
- dataBuffer.references = 1;
- dataBuffer.capacity = isView ? viewOrSize.byteLength : viewOrSize;
- dataBuffer.engineId = this._engine.uniqueId;
- if (isView) {
- this.setSubData(dataBuffer, 0, viewOrSize);
- }
- return dataBuffer;
- }
- setRawData(buffer, dstByteOffset, src, srcByteOffset, byteLength) {
- this._device.queue.writeBuffer(buffer, dstByteOffset, src.buffer, srcByteOffset, byteLength);
- }
- setSubData(dataBuffer, dstByteOffset, src, srcByteOffset = 0, byteLength = 0) {
- const buffer = dataBuffer.underlyingResource;
- byteLength = byteLength || src.byteLength;
- byteLength = Math.min(byteLength, dataBuffer.capacity - dstByteOffset);
- // After Migration to Canary
- let chunkStart = src.byteOffset + srcByteOffset;
- let chunkEnd = chunkStart + byteLength;
- // 4 bytes alignments for upload
- const alignedLength = (byteLength + 3) & ~3;
- if (alignedLength !== byteLength) {
- const tempView = new Uint8Array(src.buffer.slice(chunkStart, chunkEnd));
- src = new Uint8Array(alignedLength);
- src.set(tempView);
- srcByteOffset = 0;
- chunkStart = 0;
- chunkEnd = alignedLength;
- byteLength = alignedLength;
- }
- // Chunk
- const maxChunk = 1024 * 1024 * 15;
- let offset = 0;
- while (chunkEnd - (chunkStart + offset) > maxChunk) {
- this._device.queue.writeBuffer(buffer, dstByteOffset + offset, src.buffer, chunkStart + offset, maxChunk);
- offset += maxChunk;
- }
- this._device.queue.writeBuffer(buffer, dstByteOffset + offset, src.buffer, chunkStart + offset, byteLength - offset);
- }
- _getHalfFloatAsFloatRGBAArrayBuffer(dataLength, arrayBuffer, destArray) {
- if (!destArray) {
- destArray = new Float32Array(dataLength);
- }
- const srcData = new Uint16Array(arrayBuffer);
- while (dataLength--) {
- destArray[dataLength] = FromHalfFloat(srcData[dataLength]);
- }
- return destArray;
- }
- readDataFromBuffer(gpuBuffer, size, width, height, bytesPerRow, bytesPerRowAligned, type = 0, offset = 0, buffer = null, destroyBuffer = true, noDataConversion = false) {
- const floatFormat = type === 1 ? 2 : type === 2 ? 1 : 0;
- const engineId = this._engine.uniqueId;
- return new Promise((resolve, reject) => {
- gpuBuffer.mapAsync(WebGPUConstants.MapMode.Read, offset, size).then(() => {
- const copyArrayBuffer = gpuBuffer.getMappedRange(offset, size);
- let data = buffer;
- if (noDataConversion) {
- if (data === null) {
- data = allocateAndCopyTypedBuffer(type, size, true, copyArrayBuffer);
- }
- else {
- data = allocateAndCopyTypedBuffer(type, data.buffer, undefined, copyArrayBuffer);
- }
- }
- else {
- if (data === null) {
- switch (floatFormat) {
- case 0: // byte format
- data = new Uint8Array(size);
- data.set(new Uint8Array(copyArrayBuffer));
- break;
- case 1: // half float
- // TODO WEBGPU use computer shaders (or render pass) to make the conversion?
- data = this._getHalfFloatAsFloatRGBAArrayBuffer(size / 2, copyArrayBuffer);
- break;
- case 2: // float
- data = new Float32Array(size / 4);
- data.set(new Float32Array(copyArrayBuffer));
- break;
- }
- }
- else {
- switch (floatFormat) {
- case 0: // byte format
- data = new Uint8Array(data.buffer);
- data.set(new Uint8Array(copyArrayBuffer));
- break;
- case 1: // half float
- // TODO WEBGPU use computer shaders (or render pass) to make the conversion?
- data = this._getHalfFloatAsFloatRGBAArrayBuffer(size / 2, copyArrayBuffer, buffer);
- break;
- case 2: // float
- data = new Float32Array(data.buffer);
- data.set(new Float32Array(copyArrayBuffer));
- break;
- }
- }
- }
- if (bytesPerRow !== bytesPerRowAligned) {
- // TODO WEBGPU use computer shaders (or render pass) to build the final buffer data?
- if (floatFormat === 1 && !noDataConversion) {
- // half float have been converted to float above
- bytesPerRow *= 2;
- bytesPerRowAligned *= 2;
- }
- const data2 = new Uint8Array(data.buffer);
- let offset = bytesPerRow, offset2 = 0;
- for (let y = 1; y < height; ++y) {
- offset2 = y * bytesPerRowAligned;
- for (let x = 0; x < bytesPerRow; ++x) {
- data2[offset++] = data2[offset2++];
- }
- }
- if (floatFormat !== 0 && !noDataConversion) {
- data = new Float32Array(data2.buffer, 0, offset / 4);
- }
- else {
- data = new Uint8Array(data2.buffer, 0, offset);
- }
- }
- gpuBuffer.unmap();
- if (destroyBuffer) {
- this.releaseBuffer(gpuBuffer);
- }
- resolve(data);
- }, (reason) => {
- if (this._engine.isDisposed || this._engine.uniqueId !== engineId) {
- // The engine was disposed while waiting for the promise, or a context loss/restoration has occurred: don't reject
- resolve(new Uint8Array());
- }
- else {
- reject(reason);
- }
- });
- });
- }
- releaseBuffer(buffer) {
- if (WebGPUBufferManager._IsGPUBuffer(buffer)) {
- this._deferredReleaseBuffers.push(buffer);
- return true;
- }
- buffer.references--;
- if (buffer.references === 0) {
- this._deferredReleaseBuffers.push(buffer.underlyingResource);
- return true;
- }
- return false;
- }
- destroyDeferredBuffers() {
- for (let i = 0; i < this._deferredReleaseBuffers.length; ++i) {
- this._deferredReleaseBuffers[i].destroy();
- }
- this._deferredReleaseBuffers.length = 0;
- }
- }
- //# sourceMappingURL=webgpuBufferManager.js.map
|