Skip to content

Commit

Permalink
Double buffer TAA’s accumulation buffer in preparation for last frame…
Browse files Browse the repository at this point in the history
… reprojection (#6025)

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
  • Loading branch information
mvaligursky and Martin Valigursky committed Jan 30, 2024
1 parent 11957c3 commit e9544c8
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 46 deletions.
24 changes: 21 additions & 3 deletions extras/render-passes/render-pass-bloom.js
Expand Up @@ -24,7 +24,7 @@ class RenderPassBloom extends RenderPass {

constructor(device, sourceTexture, format) {
super(device);
this.sourceTexture = sourceTexture;
this._sourceTexture = sourceTexture;
this.textureFormat = format;

this.bloomRenderTarget = this.createRenderTarget(0);
Expand Down Expand Up @@ -87,7 +87,7 @@ class RenderPassBloom extends RenderPass {
const device = this.device;

// progressive downscale
let passSourceTexture = this.sourceTexture;
let passSourceTexture = this._sourceTexture;
for (let i = 0; i < numPasses; i++) {

const pass = new RenderPassDownsample(device, passSourceTexture);
Expand Down Expand Up @@ -124,11 +124,29 @@ class RenderPassBloom extends RenderPass {
this.destroyRenderTargets(1);
}

set sourceTexture(value) {
this._sourceTexture = value;

if (this.beforePasses.length > 0) {
const firstPass = this.beforePasses[0];

// change resize source
firstPass.options.resizeSource = value;

// change downsample source
firstPass.sourceTexture = value;
}
}

get sourceTexture() {
return this._sourceTexture;
}

frameUpdate() {
super.frameUpdate();

// create an appropriate amount of render passes
let numPasses = this.calcMipLevels(this.sourceTexture.width, this.sourceTexture.height, 2 ** this.lastMipLevel);
let numPasses = this.calcMipLevels(this._sourceTexture.width, this._sourceTexture.height, 2 ** this.lastMipLevel);
numPasses = Math.max(1, numPasses);

if (this.renderTargets.length !== numPasses) {
Expand Down
25 changes: 20 additions & 5 deletions extras/render-passes/render-pass-camera-frame.js
Expand Up @@ -23,6 +23,8 @@ class RenderPassCameraFrame extends RenderPass {

bloomPass;

taaPass;

_bloomEnabled = true;

_renderTargetScale = 1;
Expand Down Expand Up @@ -174,11 +176,10 @@ class RenderPassCameraFrame extends RenderPass {

// ------ TAA ------

let taaPass;
let sceneTextureWithTaa = sceneTexture;
if (options.taaEnabled) {
taaPass = new RenderPassTAA(device, sceneTexture);
sceneTextureWithTaa = taaPass.accumulationTexture;
this.taaPass = new RenderPassTAA(device, sceneTexture);
sceneTextureWithTaa = this.taaPass.accumulationTexture;
}

// ------ BLOOM GENERATION ------
Expand All @@ -190,7 +191,7 @@ class RenderPassCameraFrame extends RenderPass {

// create a compose pass, which combines the scene texture with the bloom texture
this.composePass = new RenderPassCompose(app.graphicsDevice);
this.composePass.sceneTexture = sceneTextureWithTaa;
// this.composePass.sceneTexture = sceneTextureWithTaa;
this.composePass.bloomTexture = this.bloomPass.bloomTexture;

// compose pass renders directly to target renderTarget
Expand All @@ -206,9 +207,23 @@ class RenderPassCameraFrame extends RenderPass {
afterPass.addLayers(composition, cameraComponent, lastAddedIndex, clearRenderTarget);

// use these prepared render passes in the order they should be executed
const allPasses = [this.scenePass, colorGrabPass, scenePassTransparent, taaPass, this.bloomPass, this.composePass, afterPass];
const allPasses = [this.scenePass, colorGrabPass, scenePassTransparent, this.taaPass, this.bloomPass, this.composePass, afterPass];
this.beforePasses = allPasses.filter(element => element !== undefined);
}

frameUpdate() {

super.frameUpdate();

// scene texture is either output of taa pass or the scene render target
const sceneTexture = this.taaPass?.update() ?? this._rt.colorBuffer;

// TAA accumulation buffer is double buffered, assign the current one to the follow up passes.
this.composePass.sceneTexture = sceneTexture;
if (this.bloomEnabled) {
this.bloomPass.sourceTexture = sceneTexture;
}
}
}

export { RenderPassCameraFrame };
90 changes: 52 additions & 38 deletions extras/render-passes/render-pass-taa.js
@@ -1,18 +1,29 @@
import {
FILTER_LINEAR, ADDRESS_CLAMP_TO_EDGE,
BLENDEQUATION_ADD, BLENDMODE_CONSTANT, BLENDMODE_ONE_MINUS_CONSTANT,
Color,
BlendState,
RenderPassShaderQuad,
Texture,
RenderTarget
} from "playcanvas";

class RenderPassTAA extends RenderPassShaderQuad {
/**
* @type {Texture}
* The index of the accumulation texture to render to.
*
* @type {number}
*/
accumulationTexture;
accumulationIndex = 0;

accumulationTexture = null;

/**
* @type {Texture[]}
*/
accumulationTextures = [];

/**
* @type {RenderTarget[]}
*/
accumulationRenderTargets = [];

constructor(device, sourceTexture) {
super(device);
Expand All @@ -21,18 +32,19 @@ class RenderPassTAA extends RenderPassShaderQuad {
this.shader = this.createQuadShader('TaaResolveShader', `
uniform sampler2D sourceTexture;
uniform sampler2D accumulationTexture;
varying vec2 uv0;
void main()
{
vec4 src = texture2D(sourceTexture, uv0);
gl_FragColor = src;
vec4 acc = texture2D(accumulationTexture, uv0);
gl_FragColor = mix(acc, src, 0.05);
}`
);

this.sourceTextureId = device.scope.resolve('sourceTexture');

this.blendState = new BlendState(true, BLENDEQUATION_ADD, BLENDMODE_CONSTANT, BLENDMODE_ONE_MINUS_CONSTANT);
this.accumulationTextureId = device.scope.resolve('accumulationTexture');

this.setup();
}
Expand All @@ -47,46 +59,48 @@ class RenderPassTAA extends RenderPassShaderQuad {

setup() {

const { device } = this;

// create the accumulation render target
const texture = new Texture(device, {
name: 'TAA Accumulation Texture',
width: 4,
height: 4,
format: this.sourceTexture.format,
mipmaps: false,
minFilter: FILTER_LINEAR,
magFilter: FILTER_LINEAR,
addressU: ADDRESS_CLAMP_TO_EDGE,
addressV: ADDRESS_CLAMP_TO_EDGE
});
this.accumulationTexture = texture;

const rt = new RenderTarget({
colorBuffer: texture,
depth: false
});
// double buffered accumulation render target
for (let i = 0; i < 2; ++i) {
this.accumulationTextures[i] = new Texture(this.device, {
name: `TAA-Accumulation-${i}`,
width: 4,
height: 4,
format: this.sourceTexture.format,
mipmaps: false,
minFilter: FILTER_LINEAR,
magFilter: FILTER_LINEAR,
addressU: ADDRESS_CLAMP_TO_EDGE,
addressV: ADDRESS_CLAMP_TO_EDGE
});

this.accumulationRenderTargets[i] = new RenderTarget({
colorBuffer: this.accumulationTextures[i],
depth: false
});
}

this.init(rt, {
this.accumulationTexture = this.accumulationTextures[0];
this.init(this.accumulationRenderTargets[0], {
resizeSource: this.sourceTexture
});

// clear it to black initially
this.setClearColor(Color.BLACK);
}

execute() {
this.sourceTextureId.setValue(this.sourceTexture);

// TODO: add this to the parent class
const blend = 0.05;
this.device.setBlendColor(blend, blend, blend, blend);
this.accumulationTextureId.setValue(this.accumulationTextures[1 - this.accumulationIndex]);

super.execute();
}

// called when the parent render pass gets added to the frame graph
update() {

// swap source and destination accumulation texture
this.accumulationIndex = 1 - this.accumulationIndex;
this.accumulationTexture = this.accumulationTextures[this.accumulationIndex];
this.renderTarget = this.accumulationRenderTargets[this.accumulationIndex];

// disable clearing
this.setClearColor();
return this.accumulationTexture;
}
}

Expand Down

0 comments on commit e9544c8

Please sign in to comment.