Explorar o código

Optimize render pipeline cache further

Popov72 %!s(int64=4) %!d(string=hai) anos
pai
achega
6804b99259

+ 20 - 2
src/Engines/WebGPU/webgpuCacheRenderPipeline.ts

@@ -128,6 +128,8 @@ export abstract class WebGPUCacheRenderPipeline {
     private static _NumPipelineCreationCurrentFrame = 0;
 
     protected _states: number[];
+    protected _stateDirtyLowestIndex: number;
+    public lastStateDirtyLowestIndex: number; // for stats only
 
     private _device: GPUDevice;
     private _isDirty: boolean;
@@ -174,6 +176,7 @@ export abstract class WebGPUCacheRenderPipeline {
         this._device = device;
         this._states = [];
         this._states.length = StatePosition.NumStates;
+        this._stateDirtyLowestIndex = 0;
         this._emptyVertexBuffer = emptyVertexBuffer;
         this._mrtFormats = [];
         this._parameter = { token: undefined, pipeline: null };
@@ -220,15 +223,19 @@ export abstract class WebGPUCacheRenderPipeline {
         this._setDepthStencilState();
         this._setVertexState(effect);
 
+        this.lastStateDirtyLowestIndex = this._stateDirtyLowestIndex;
+
         if (!this._isDirty && this._parameter.pipeline) {
+            this._stateDirtyLowestIndex = this._states.length;
             WebGPUCacheRenderPipeline.NumCacheHitWithoutHash++;
             return this._parameter.pipeline;
         }
 
-        this._isDirty = false;
-
         this._getRenderPipeline(this._parameter);
 
+        this._isDirty = false;
+        this._stateDirtyLowestIndex = this._states.length;
+
         if (this._parameter.pipeline) {
             WebGPUCacheRenderPipeline.NumCacheHitWithHash++;
             return this._parameter.pipeline;
@@ -305,6 +312,7 @@ export abstract class WebGPUCacheRenderPipeline {
             this._depthBiasSlopeScale = depthBiasSlopeScale;
             this._states[StatePosition.DepthBiasSlopeScale] = depthBiasSlopeScale;
             this._isDirty = true;
+            this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.DepthBiasSlopeScale);
         }
     }
 
@@ -344,6 +352,7 @@ export abstract class WebGPUCacheRenderPipeline {
             this._states[StatePosition.MRTAttachments1] = bits[0];
             this._states[StatePosition.MRTAttachments2] = bits[1];
             this._isDirty = true;
+            this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.MRTAttachments1);
         }
     }
 
@@ -402,6 +411,7 @@ export abstract class WebGPUCacheRenderPipeline {
             this._stencilReadMask = mask;
             this._states[StatePosition.StencilReadMask] = mask;
             this._isDirty = true;
+            this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.StencilReadMask);
         }
     }
 
@@ -410,6 +420,7 @@ export abstract class WebGPUCacheRenderPipeline {
             this._stencilWriteMask = mask;
             this._states[StatePosition.StencilWriteMask] = mask;
             this._isDirty = true;
+            this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.StencilWriteMask);
         }
     }
 
@@ -677,6 +688,7 @@ export abstract class WebGPUCacheRenderPipeline {
             this._shaderId = id;
             this._states[StatePosition.ShaderStage] = id;
             this._isDirty = true;
+            this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.ShaderStage);
         }
     }
 
@@ -697,6 +709,7 @@ export abstract class WebGPUCacheRenderPipeline {
             this._rasterizationState = rasterizationState;
             this._states[StatePosition.RasterizationState] = this._rasterizationState;
             this._isDirty = true;
+            this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.RasterizationState);
         }
     }
 
@@ -719,6 +732,7 @@ export abstract class WebGPUCacheRenderPipeline {
             this._colorStates = colorStates;
             this._states[StatePosition.ColorStates] = this._colorStates;
             this._isDirty = true;
+            this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.ColorStates);
         }
     }
 
@@ -736,6 +750,7 @@ export abstract class WebGPUCacheRenderPipeline {
             this._depthStencilState = depthStencilState;
             this._states[StatePosition.DepthStencilState] = this._depthStencilState;
             this._isDirty = true;
+            this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.DepthStencilState);
         }
     }
 
@@ -763,6 +778,9 @@ export abstract class WebGPUCacheRenderPipeline {
 
         this._states.length = newNumStates;
         this._isDirty = this._isDirty || newNumStates !== currStateLen;
+        if (this._isDirty) {
+            this._stateDirtyLowestIndex = Math.min(this._stateDirtyLowestIndex, StatePosition.VertexState);
+        }
     }
 
     private _createPipelineLayout(webgpuPipelineContext: WebGPUPipelineContext): GPUPipelineLayout {

+ 13 - 54
src/Engines/WebGPU/webgpuCacheRenderPipelineTree.ts

@@ -26,7 +26,9 @@ class NodeState {
 /** @hidden */
 export class WebGPUCacheRenderPipelineTree extends WebGPUCacheRenderPipeline {
 
-    private static _Cache: NodeState;
+    private static _Cache: NodeState = new NodeState();
+
+    private _nodeStack: NodeState[];
 
     public static GetNodeCounts(): { nodeCount: number, pipelineCount: number } {
         const counts = WebGPUCacheRenderPipelineTree._Cache.count();
@@ -36,67 +38,24 @@ export class WebGPUCacheRenderPipelineTree extends WebGPUCacheRenderPipeline {
 
     constructor(device: GPUDevice, emptyVertexBuffer: VertexBuffer) {
         super(device, emptyVertexBuffer);
-        WebGPUCacheRenderPipelineTree._Cache = new NodeState();
+        this._nodeStack = [];
+        this._nodeStack[0] = WebGPUCacheRenderPipelineTree._Cache;
     }
 
     protected _getRenderPipeline(param: { token: any, pipeline: Nullable<GPURenderPipeline> }): void {
-        const n0 = WebGPUCacheRenderPipelineTree._Cache;
-        let n1 = n0.values[this._states[0]];
-        if (!n1) {
-            n1 = new NodeState();
-            n0.values[this._states[0]] = n1;
-        }
-        let n2 = n1.values[this._states[1]];
-        if (!n2) {
-            n2 = new NodeState();
-            n1.values[this._states[1]] =  n2;
-        }
-        let n3 = n2.values[this._states[2]];
-        if (!n3) {
-            n3 = new NodeState();
-            n2.values[this._states[2]] = n3;
-        }
-        let n4 = n3.values[this._states[3]];
-        if (!n4) {
-            n4 = new NodeState();
-            n3.values[this._states[3]] =  n4;
-        }
-        let n5 = n4.values[this._states[4]];
-        if (!n5) {
-            n5 = new NodeState();
-            n4.values[this._states[4]] = n5;
-        }
-        let n6 = n5.values[this._states[5]];
-        if (!n6) {
-            n6 = new NodeState();
-            n5.values[this._states[5]] = n6;
-        }
-        let n7 = n6.values[this._states[6]];
-        if (!n7) {
-            n7 = new NodeState();
-            n6.values[this._states[6]] = n7;
-        }
-        let n8 = n7.values[this._states[7]];
-        if (!n8) {
-            n8 = new NodeState();
-            n7.values[this._states[7]] = n8;
-        }
-        let n9 = n8.values[this._states[8]];
-        if (!n9) {
-            n9 = new NodeState();
-            n8.values[this._states[8]] = n9;
-        }
-        for (let i = 9; i < this._states.length; ++i) {
-            let nn: NodeState | undefined = n9!.values[this._states[i]];
+        let node = this._nodeStack[this._stateDirtyLowestIndex];
+        for (let i = this._stateDirtyLowestIndex; i < this._states.length; ++i) {
+            let nn: NodeState | undefined = node!.values[this._states[i]];
             if (!nn) {
                 nn = new NodeState();
-                n9!.values[this._states[i]] = nn;
+                node!.values[this._states[i]] = nn;
             }
-            n9 = nn;
+            node = nn;
+            this._nodeStack[i + 1] = node;
         }
 
-        param.token = n9;
-        param.pipeline = n9.pipeline;
+        param.token = node;
+        param.pipeline = node.pipeline;
     }
 
     protected _setRenderPipeline(param: { token: NodeState, pipeline: Nullable<GPURenderPipeline> }): void {

+ 3 - 4
src/Engines/webgpuEngine.ts

@@ -3359,15 +3359,14 @@ export class WebGPUEngine extends Engine {
             webgpuPipelineContext.uniformBuffer.update();
         }
 
-        // TODO WEBGPU try to optimize this loop / lookup in bindGroupsCache
         let node: WebGPUBindGroupCacheNode = webgpuPipelineContext.bindGroupsCache;
         for (let i = 0; i < webgpuPipelineContext.shaderProcessingContext.uniformBufferNames.length; ++i) {
             const bufferName = webgpuPipelineContext.shaderProcessingContext.uniformBufferNames[i];
-            const uboIndex = this._uniformsBuffers[bufferName].uniqueId;
-            let nextNode = node.values[uboIndex];
+            const uboId = this._uniformsBuffers[bufferName].uniqueId;
+            let nextNode = node.values[uboId];
             if (!nextNode) {
                 nextNode = new WebGPUBindGroupCacheNode();
-                node.values[uboIndex] = nextNode;
+                node.values[uboId] = nextNode;
             }
             node = nextNode;
         }