UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

141 lines (138 loc) 6.04 kB
import { Debug } from '../core/debug.js'; /** * @import { RenderPass } from '../platform/graphics/render-pass.js' * @import { RenderTarget } from '../platform/graphics/render-target.js' * @import { Texture } from '../platform/graphics/texture.js' */ /** * A frame graph represents a single rendering frame as a sequence of render passes. */ class FrameGraph { /** * Add a render pass to the frame. * * @param {RenderPass} renderPass - The render pass to add. */ addRenderPass(renderPass) { Debug.assert(renderPass); renderPass.frameUpdate(); var beforePasses = renderPass.beforePasses; for(var i = 0; i < beforePasses.length; i++){ var pass = beforePasses[i]; if (pass.enabled) { this.addRenderPass(pass); } } if (renderPass.enabled) { this.renderPasses.push(renderPass); } var afterPasses = renderPass.afterPasses; for(var i1 = 0; i1 < afterPasses.length; i1++){ var pass1 = afterPasses[i1]; if (pass1.enabled) { this.addRenderPass(pass1); } } } reset() { this.renderPasses.length = 0; } compile() { var renderTargetMap = this.renderTargetMap; var renderPasses = this.renderPasses; for(var i = 0; i < renderPasses.length; i++){ var renderPass = renderPasses[i]; var renderTarget = renderPass.renderTarget; // if using a target, or null which represents the default back-buffer if (renderTarget !== undefined) { // previous pass using the same render target var prevPass = renderTargetMap.get(renderTarget); if (prevPass) { // if we use the RT without clearing, make sure the previous pass stores data var count = renderPass.colorArrayOps.length; for(var j = 0; j < count; j++){ var colorOps = renderPass.colorArrayOps[j]; if (!colorOps.clear) { prevPass.colorArrayOps[j].store = true; } } if (!renderPass.depthStencilOps.clearDepth) { prevPass.depthStencilOps.storeDepth = true; } if (!renderPass.depthStencilOps.clearStencil) { prevPass.depthStencilOps.storeStencil = true; } } // add the pass to the map renderTargetMap.set(renderTarget, renderPass); } } // merge passes if possible for(var i1 = 0; i1 < renderPasses.length - 1; i1++){ var firstPass = renderPasses[i1]; var firstRT = firstPass.renderTarget; var secondPass = renderPasses[i1 + 1]; var 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 // mipmaps being generated after each face. /** @type {Texture} */ var lastCubeTexture = null; /** @type {RenderPass} */ var lastCubeRenderPass = null; for(var i2 = 0; i2 < renderPasses.length; i2++){ var renderPass1 = renderPasses[i2]; var renderTarget1 = renderPass1.renderTarget; var thisTexture = renderTarget1 == null ? undefined : renderTarget1.colorBuffer; if (thisTexture == null ? undefined : thisTexture.cubemap) { // if previous pass used the same cubemap texture, it does not need mipmaps generated if (lastCubeTexture === thisTexture) { var count1 = lastCubeRenderPass.colorArrayOps.length; for(var j1 = 0; j1 < count1; j1++){ lastCubeRenderPass.colorArrayOps[j1].mipmaps = false; } } lastCubeTexture = renderTarget1.colorBuffer; lastCubeRenderPass = renderPass1; } else if (renderPass1.requiresCubemaps) { // if the cubemap is required, break the cubemap rendering chain lastCubeTexture = null; lastCubeRenderPass = null; } } renderTargetMap.clear(); } render(device) { this.compile(); var renderPasses = this.renderPasses; for(var i = 0; i < renderPasses.length; i++){ renderPasses[i].render(); } } constructor(){ /** @type {RenderPass[]} */ this.renderPasses = []; /** * Map used during frame graph compilation. It maps a render target to its previous occurrence. * * @type {Map<RenderTarget, RenderPass>} */ this.renderTargetMap = new Map(); } } export { FrameGraph };