diff --git a/src/platform/graphics/render-pass.js b/src/platform/graphics/render-pass.js index e805d582075..49f7384e6eb 100644 --- a/src/platform/graphics/render-pass.js +++ b/src/platform/graphics/render-pass.js @@ -126,6 +126,24 @@ class RenderPass { */ _enabled = true; + /** + * True if the render pass start is skipped. This means the render pass is merged into the + * previous one. + * + * @type {boolean} + * @private + */ + _skipStart = false; + + /** + * True if the render pass end is skipped. This means the following render pass is merged into + * this one. + * + * @type {boolean} + * @private + */ + _skipEnd = false; + /** * True if the render pass is enabled and execute function will be called. Note that before and * after functions are called regardless of this flag. @@ -414,13 +432,13 @@ class RenderPass { if (this.executeEnabled) { - if (realPass) { + if (realPass && !this._skipStart) { device.startRenderPass(this); } this.execute(); - if (realPass) { + if (realPass && !this._skipEnd) { device.endRenderPass(this); } } @@ -447,8 +465,9 @@ class RenderPass { ` ${rt.width} x ${rt.height}` + `${(this.samples > 0 ? ` samples: ${this.samples}` : '')}`; + const indexString = this._skipStart ? '++' : index.toString().padEnd(2, ' '); Debug.trace(TRACEID_RENDER_PASS, - `${index.toString().padEnd(2, ' ')}: ${this.name.padEnd(20, ' ')}` + + `${indexString}: ${this.name.padEnd(20, ' ')}` + `${this.executeEnabled ? '' : ' DISABLED '}${ rtInfo.padEnd(30)}`); diff --git a/src/scene/frame-graph.js b/src/scene/frame-graph.js index 1d1ee1b811b..b40287626e9 100644 --- a/src/scene/frame-graph.js +++ b/src/scene/frame-graph.js @@ -90,6 +90,41 @@ class FrameGraph { } } + // merge passes if possible + for (let i = 0; i < renderPasses.length - 1; i++) { + const firstPass = renderPasses[i]; + const firstRT = firstPass.renderTarget; + const secondPass = renderPasses[i + 1]; + const secondRT = secondPass.renderTarget; + + // if the render targets are different, we can't merge the passes + // also only merge passes that have a render target + if (firstRT !== secondRT || firstRT === undefined) { + continue; + } + + // do not merge if the second pass clears any of the attachments + if (secondPass.depthStencilOps.clearDepth || + secondPass.depthStencilOps.clearStencil || + secondPass.colorArrayOps.some(colorOps => colorOps.clear)) { + continue; + } + + // first pass cannot contain after passes + if (firstPass.afterPasses.length > 0) { + continue; + } + + // second pass cannot contain before passes + if (secondPass.beforePasses.length > 0) { + continue; + } + + // merge the passes + firstPass._skipEnd = true; + secondPass._skipStart = true; + } + // Walk over render passes to find passes rendering to the same cubemap texture. // If those passes are separated only by passes not requiring cubemap (shadows ..), // we skip the mipmap generation till the last rendering to the cubemap, to avoid