I am learning about Temporal Anitaliasing and want to store each frame, N, in a frame history buffer. I am implementing this in a Custom Pass script using Unity 6000.0.23f1, and DirectX12. And compiling my shader with #pragma use_dxc
.
My buffer is a RTHandle
with 8 slices, and I’m doing a color copy pass to a specific slice each frame. This color copy pass simply copies the blended result of the current frame, which is both displayed and stored.
The issue is that every other slice, index=[1, 3, 5, 7] is not correct, while the others index=[0, 2, 4, 6] are correct. I inspect my buffer in RenderDoc and this is what I see:
Screenshot in RenderDoc where slice 0 is correct:
https://i.imgur.com/zTUOgLB.png
Screenshot in RenderDoc where slice 1 is not correct:
https://i.imgur.com/SadMe30.png
Slices [1, 3, 5, 7] all look like slice 1.
What could possibly go wrong here?
Here is some of my code for the Custom Pass:
protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)
{
N = 0;
volumeShader = Shader.Find("FullScreen/TemporalNanoVolumePass");
mat = CoreUtils.CreateEngineMaterial(volumeShader);
nextFrame = RTHandles.Alloc(
Vector2.one, TextureXR.slices,
colorFormat: GraphicsFormat.R16G16B16A16_SFloat,
dimension: TextureXR.dimension,
name: "Next_Frame_Buffer"
);
frameHistory = RTHandles.Alloc(
Vector2.one,
slices: temporalFrames,
colorFormat: GraphicsFormat.R16G16B16A16_SFloat,
dimension: TextureXR.dimension,
name: "Frame_History_Buffer"
);
blendedFrame = RTHandles.Alloc(
Vector2.one, TextureXR.slices,
colorFormat: GraphicsFormat.R16G16B16A16_SFloat,
dimension: TextureXR.dimension,
name: "Blended_Frame_Buffer"
);
}
protected override void Execute(CustomPassContext ctx)
{
if (!nanoVolumeLoaderComponent.IsLoaded())
{
return;
}
Vector4 scale = RTHandles.rtHandleProperties.rtHandleScale;
SetUniforms();
// Draw newest frame to a buffer
CoreUtils.SetRenderTarget(ctx.cmd, nextFrame, ClearFlag.Color);
CoreUtils.DrawFullScreen(ctx.cmd, mat, ctx.propertyBlock, shaderPassId: NANO_VOLUME_PASS_ID);
// Combine new frame with old frames into another buffer
ctx.propertyBlock.SetTexture("_NextFrame", nextFrame);
ctx.propertyBlock.SetTexture("_FrameHistory", frameHistory);
ctx.propertyBlock.SetInt("_FrameIndex", N);
CoreUtils.SetRenderTarget(ctx.cmd, blendedFrame, ClearFlag.Color);
CoreUtils.DrawFullScreen(ctx.cmd, mat, ctx.propertyBlock, shaderPassId: TEMPORAL_BLEND_PASS_ID);
// Save blended frame to history at slice N
ctx.propertyBlock.SetTexture("_BlendedFrame", blendedFrame);
CoreUtils.SetRenderTarget(ctx.cmd, frameHistory, ClearFlag.Color, depthSlice: N);
CoreUtils.DrawFullScreen(ctx.cmd, mat, ctx.propertyBlock, shaderPassId: COPY_COLOR_PASS_ID);
// Display blended frame to camera
ctx.cmd.Blit(blendedFrame, ctx.cameraColorBuffer, new Vector2(scale.x, scale.y), Vector2.zero, 0, 0);
N = (N + 1) % temporalFrames;
}
And here is my Blend Pass which uses the frame history buffer (TAA not implemented yet obv):
TEXTURE2D_ARRAY(_FrameHistory);
TEXTURE2D_X(_NextFrame);
uniform int _TemporalFrames;
uniform int _FrameIndex;
float4 TemporalBlendPass(Varyings varyings) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(varyings);
float depth = LoadCameraDepth(varyings.positionCS.xy);
PositionInputs posInput = GetPositionInput(varyings.positionCS.xy, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
float2 scaling = _RTHandleScale.xy;
float2 uv = posInput.positionNDC.xy * scaling;
float4 nextFrame = SAMPLE_TEXTURE2D_X(_NextFrame, s_linear_clamp_sampler, uv);
float4 lastFrame = SAMPLE_TEXTURE2D_ARRAY(_FrameHistory, s_linear_clamp_sampler, uv, (_FrameIndex - 1) % _TemporalFrames);
// blend next and lastFrame
float4 blendedFrame = lerp(lastFrame, nextFrame, 0.99);
return blendedFrame;
}