playcanvas
Version:
PlayCanvas WebGL game engine
141 lines (138 loc) • 6.04 kB
JavaScript
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 };