123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- import { DataBuffer } from '../../Meshes/dataBuffer';
- import { WebGPUDataBuffer } from '../../Meshes/WebGPU/webgpuDataBuffer';
- import { Nullable } from '../../types';
- /** @hidden */
- export class WebGPUBufferManager {
- private _device: GPUDevice;
- private _deferredReleaseBuffers: Array<GPUBuffer> = [];
- private static _IsGPUBuffer(buffer: DataBuffer | GPUBuffer): buffer is GPUBuffer {
- return (buffer as DataBuffer).underlyingResource === undefined;
- }
- constructor(device: GPUDevice) {
- this._device = device;
- }
- public createRawBuffer(viewOrSize: ArrayBufferView | number, flags: GPUBufferUsageFlags, mappedAtCreation = false): GPUBuffer {
- 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)
- const verticesBufferDescriptor = {
- mappedAtCreation,
- size: alignedLength,
- usage: flags
- };
- return this._device.createBuffer(verticesBufferDescriptor);
- }
- public createBuffer(viewOrSize: ArrayBufferView | number, flags: GPUBufferUsageFlags): DataBuffer {
- const isView = (viewOrSize as ArrayBufferView).byteLength !== undefined;
- const buffer = this.createRawBuffer(viewOrSize, flags);
- const dataBuffer = new WebGPUDataBuffer(buffer);
- dataBuffer.references = 1;
- dataBuffer.capacity = isView ? (viewOrSize as ArrayBufferView).byteLength : viewOrSize as number;
- if (isView) {
- this.setSubData(dataBuffer, 0, viewOrSize as ArrayBufferView);
- }
- return dataBuffer;
- }
- public setSubData(dataBuffer: WebGPUDataBuffer, dstByteOffset: number, src: ArrayBufferView, srcByteOffset = 0, byteLength = 0): void {
- const buffer = dataBuffer.underlyingResource as GPUBuffer;
- 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 as Uint8Array).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.defaultQueue.writeBuffer(buffer, dstByteOffset + offset, src.buffer, chunkStart + offset, maxChunk);
- offset += maxChunk;
- }
- this._device.defaultQueue.writeBuffer(buffer, dstByteOffset + offset, src.buffer, chunkStart + offset, byteLength - offset);
- }
- private _FromHalfFloat(value: number): number {
- const s = (value & 0x8000) >> 15;
- const e = (value & 0x7C00) >> 10;
- const f = value & 0x03FF;
- if (e === 0) {
- return (s ? -1 : 1) * Math.pow(2, -14) * (f / Math.pow(2, 10));
- } else if (e == 0x1F) {
- return f ? NaN : ((s ? -1 : 1) * Infinity);
- }
- return (s ? -1 : 1) * Math.pow(2, e - 15) * (1 + (f / Math.pow(2, 10)));
- }
- private _GetHalfFloatAsFloatRGBAArrayBuffer(dataLength: number, arrayBuffer: ArrayBuffer, destArray?: Float32Array): Float32Array {
- if (!destArray) {
- destArray = new Float32Array(dataLength);
- }
- const srcData = new Uint16Array(arrayBuffer);
- while (dataLength--) {
- destArray[dataLength] = this._FromHalfFloat(srcData[dataLength]);
- }
- return destArray;
- }
- public readDataFromBuffer(gpuBuffer: GPUBuffer, size: number, width: number, height: number, bytesPerRow: number, bytesPerRowAligned: number, floatFormat = 0, offset = 0, buffer: Nullable<ArrayBufferView> = null, destroyBuffer = true): Promise<ArrayBufferView> {
- return new Promise((resolve, reject) => {
- gpuBuffer.mapAsync(GPUMapMode.READ, offset, size).then(() => {
- const copyArrayBuffer = gpuBuffer.getMappedRange(offset, size);
- let data: Nullable<ArrayBufferView> | Uint8Array | Float32Array = buffer;
- if (data === null) {
- switch (floatFormat) {
- case 0: // byte format
- data = new Uint8Array(size);
- (data as Uint8Array).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 as Float32Array).set(new Float32Array(copyArrayBuffer));
- break;
- }
- } else {
- switch (floatFormat) {
- case 0: // byte format
- data = new Uint8Array(data.buffer);
- (data as Uint8Array).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 as Float32Array);
- break;
- case 2: // float
- data = new Float32Array(data.buffer);
- (data as Float32Array).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) {
- // 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) {
- 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) => reject(reason));
- });
- }
- public releaseBuffer(buffer: DataBuffer | GPUBuffer): boolean {
- if (WebGPUBufferManager._IsGPUBuffer(buffer)) {
- this._deferredReleaseBuffers.push(buffer);
- return true;
- }
- buffer.references--;
- if (buffer.references === 0) {
- this._deferredReleaseBuffers.push(buffer.underlyingResource as GPUBuffer);
- return true;
- }
- return false;
- }
- public destroyDeferredBuffers(): void {
- for (let i = 0; i < this._deferredReleaseBuffers.length; ++i) {
- this._deferredReleaseBuffers[i].destroy();
- }
- this._deferredReleaseBuffers.length = 0;
- }
- }
|