Sfoglia il codice sorgente

Merge pull request #8099 from ottoville/WebGPU

check zero buffer update, reformat pipeline layout, parallel shader loading
sebavan 5 anni fa
parent
commit
fbd1e68437

+ 1 - 1
src/Engines/WebGPU/webgpuPipelineContext.ts

@@ -63,7 +63,7 @@ export class WebGPUPipelineContext implements IPipelineContext {
 
     public vertexInputs: IWebGPUPipelineContextVertexInputsCache;
 
-    public bindGroupLayouts: (GPUBindGroupLayout | undefined)[];
+    public bindGroupLayouts: GPUBindGroupLayout[];
     public bindGroups: GPUBindGroup[];
 
     public renderPipeline: GPURenderPipeline;

+ 55 - 31
src/Engines/webgpuEngine.ts

@@ -517,25 +517,32 @@ export class WebGPUEngine extends Engine {
     //------------------------------------------------------------------------------
     //                              WebGPU Buffers
     //------------------------------------------------------------------------------
-
     private _createBuffer(view: ArrayBufferView, flags: GPUBufferUsageFlags): DataBuffer {
         if (view.byteLength == 0) {
-            throw new Error("Unable to create WebGPU buffer"); // Zero size buffer would kill the tab in chrome
+            throw new Error("Unable to create WebGPU buffer: cannot create zero-sized buffer"); // Zero size buffer would kill the tab in chrome
         }
         const padding = view.byteLength % 4;
+
         const verticesBufferDescriptor = {
             size: view.byteLength + padding,
             usage: flags
         };
-        const [buffer, arrbuffer] = this._device.createBufferMapped(verticesBufferDescriptor)
+        let buffer: GPUBuffer;
+        let arrBuffer: Nullable<ArrayBuffer> = null;
+        if (padding == 0 && view.byteLength < this._maxBufferChunk) {
+            [buffer, arrBuffer] = this._device.createBufferMapped(verticesBufferDescriptor);
+        }
+        else {
+            buffer = this._device.createBuffer(verticesBufferDescriptor);
+        }
+
         const dataBuffer = new WebGPUDataBuffer(buffer);
         dataBuffer.references = 1;
         dataBuffer.capacity = view.byteLength;
-        if (padding == 0 && view.byteLength < this._maxBufferChunk) {
-            new Uint8Array(arrbuffer).set(new Uint8Array(view.buffer));
+        if (arrBuffer) {
+            new Uint8Array(arrBuffer).set(new Uint8Array(view.buffer));
             buffer.unmap();
         } else {
-            buffer.unmap();
             this._setSubData(dataBuffer, 0, view);
         }
         return dataBuffer;
@@ -569,23 +576,30 @@ export class WebGPUEngine extends Engine {
         // Chunk
         const commandEncoder = this._device.createCommandEncoder();
         const tempBuffers: GPUBuffer[] = [];
-        for (let offset = 0; offset < src.byteLength; offset += this._maxBufferChunk) {
-            const uploadCount = Math.min(src.byteLength - offset, this._maxBufferChunk);
-
-            const [uploadBuffer, uploadMapping] = this._device.createBufferMapped({
-                usage: WebGPUConstants.GPUBufferUsage_TRANSFER_SRC,
-                size: uploadCount,
-            });
-            tempBuffers.push(uploadBuffer);
-            new Uint8Array(uploadMapping).set(new Uint8Array(src.buffer, srcByteOffset + offset, uploadCount));
-            uploadBuffer.unmap();
-            commandEncoder.copyBufferToBuffer(
-                uploadBuffer, 0,
-                buffer, dstByteOffset + offset,
-                uploadCount);
+        try {
+            for (let offset = 0; offset < src.byteLength; offset += this._maxBufferChunk) {
+                const uploadCount = Math.min(src.byteLength - offset, this._maxBufferChunk);
+                if (uploadCount == 0) {
+                    throw new Error("Cannot create zero-sized buffer"); // Zero size buffer would kill the tab in chrome
+                }
+                const [uploadBuffer, uploadMapping] = this._device.createBufferMapped({
+                    usage: WebGPUConstants.GPUBufferUsage_TRANSFER_SRC,
+                    size: uploadCount,
+                });
+                tempBuffers.push(uploadBuffer);
+                new Uint8Array(uploadMapping).set(new Uint8Array(src.buffer, srcByteOffset + offset, uploadCount));
+                uploadBuffer.unmap();
+                commandEncoder.copyBufferToBuffer(
+                    uploadBuffer, 0,
+                    buffer, dstByteOffset + offset,
+                    uploadCount);
+            }
+            this._device.defaultQueue.submit([commandEncoder.finish()]);
+        } catch (e) {
+            Logger.Error('Unable to update WebGPU buffer: ' + e);
+        } finally {
+            tempBuffers.forEach((buff) => buff.destroy());
         }
-        this._device.defaultQueue.submit([commandEncoder.finish()]);
-        tempBuffers.forEach((buff) => buff.destroy());
     }
 
     //------------------------------------------------------------------------------
@@ -2132,13 +2146,17 @@ export class WebGPUEngine extends Engine {
         for (let i = 0; i < webgpuPipelineContext.orderedUBOsAndSamplers.length; i++) {
             const setDefinition = webgpuPipelineContext.orderedUBOsAndSamplers[i];
             if (setDefinition === undefined) {
-                const groupLayout = bindGroupLayouts[i];
-                if (groupLayout) {
-                    bindGroups[i] = this._device.createBindGroup({
-                        layout: groupLayout,
-                        bindings: [],
-                    });
+                let groupLayout: GPUBindGroupLayout;
+                if (bindGroupLayouts && bindGroupLayouts[i]) {
+                    groupLayout = bindGroupLayouts[i];
                 }
+                else {
+                    groupLayout = webgpuPipelineContext.renderPipeline.getBindGroupLayout(i);
+                }
+                bindGroups[i] = this._device.createBindGroup({
+                    layout: groupLayout,
+                    entries: [],
+                });
                 continue;
             }
 
@@ -2190,11 +2208,17 @@ export class WebGPUEngine extends Engine {
                 }
             }
 
-            const groupLayout = bindGroupLayouts[i];
-            if (groupLayout) {
+            if (bindings.length > 0) {
+                let groupLayout: GPUBindGroupLayout;
+                if (bindGroupLayouts && bindGroupLayouts[i]) {
+                    groupLayout = bindGroupLayouts[i];
+                }
+                else {
+                    groupLayout = webgpuPipelineContext.renderPipeline.getBindGroupLayout(i);
+                }
                 bindGroups[i] = this._device.createBindGroup({
                     layout: groupLayout,
-                    bindings,
+                    entries: bindings,
                 });
             }
         }

+ 3 - 2
src/LibDeclarations/webgpu.d.ts

@@ -223,7 +223,7 @@ interface GPUBindGroupBinding {
 
 interface GPUBindGroupDescriptor extends GPUObjectDescriptorBase {
   layout: GPUBindGroupLayout;
-  bindings: GPUBindGroupBinding[];
+  entries: GPUBindGroupBinding[];
 }
 
 interface GPUBindGroupLayoutBinding {
@@ -802,8 +802,9 @@ interface GPURenderBundleEncoderDescriptor
   sampleCount?: number;
 }
 
-class GPURenderPipeline implements GPUObjectBase {
+declare class GPURenderPipeline implements GPUObjectBase {
   label: string | undefined;
+  getBindGroupLayout: (number) => GPUBindGroupLayout;
 }
 
 class GPUSampler implements GPUObjectBase {

+ 19 - 8
src/Materials/effect.ts

@@ -286,17 +286,28 @@ export class Effect implements IDisposable {
             processingContext: this._processingContext
         };
 
-        this._loadShader(vertexSource, "Vertex", "", (vertexCode) => {
-            this._loadShader(fragmentSource, "Fragment", "Pixel", (fragmentCode) => {
-                ShaderProcessor.Process(vertexCode, processorOptions, (migratedVertexCode) => {
-                    processorOptions.isFragment = true;
-                    ShaderProcessor.Process(fragmentCode, processorOptions, (migratedFragmentCode) => {
-                        const finalShaders = ShaderProcessor.Finalize(migratedVertexCode, migratedFragmentCode, processorOptions);
-                        this._useFinalCode(finalShaders.vertexCode, finalShaders.fragmentCode, baseName);
-                    });
+        let shaderCodes : [string | undefined, string | undefined] = [undefined, undefined];
+        let shadersLoaded = () => {
+            if (shaderCodes[0] && shaderCodes[1]) {
+                processorOptions.isFragment = true;
+                let [migratedVertexCode, fragmentCode] = shaderCodes;
+                ShaderProcessor.Process(fragmentCode, processorOptions, (migratedFragmentCode) => {
+                    const finalShaders = ShaderProcessor.Finalize(migratedVertexCode, migratedFragmentCode, processorOptions);
+                    this._useFinalCode(finalShaders.vertexCode, finalShaders.fragmentCode, baseName);
                 });
+            }
+
+        };
+        this._loadShader(vertexSource, "Vertex", "", (vertexCode) => {
+            ShaderProcessor.Process(vertexCode, processorOptions, (migratedVertexCode) => {
+                shaderCodes[0] = migratedVertexCode;
+                shadersLoaded();
             });
         });
+        this._loadShader(fragmentSource, "Fragment", "Pixel", (fragmentCode) => {
+            shaderCodes[1] = fragmentCode;
+            shadersLoaded();
+        });
     }
 
     private _useFinalCode(migratedVertexCode: string, migratedFragmentCode: string, baseName: any) {