Files
UnrealEngine/Engine/Shaders/Private/TemporalSuperResolution/TSRDecimateHistory.usf
2025-05-18 13:04:45 +08:00

533 lines
19 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "TSRDepthVelocityAnalysis.ush"
#include "TSRClosestOccluder.ush"
#include "TSRColorSpace.ush"
#include "TSRReprojectionField.ush"
//------------------------------------------------------- CONFIG
#define TILE_SIZE 8
#define CONFIG_KERNEL 0
#define CONFIG_MANUAL_LDS_SPILL 1
/** Whether to compile the moire reprojection. */
#define CONFIG_MOIRE_REPROJECTION (DIM_MOIRE_REPROJECTION)
/** Whether to compile the resurrection reprojection. */
#define CONFIG_RESURRECTION_REPROJECTION (DIM_RESURRECTION_REPROJECTION)
/** Whether to compile the thin geometry coverage reprojection.*/
#define CONFIG_THIN_GEOMETRY_COVERAGE_REPROJECTION (DIM_THIN_GEOMETRY_COVERAGE_REPROJECTION)
//------------------------------------------------------- PARAMETERS
float4x4 RotationalClipToPrevClip;
Texture2D<uint> DilatedReprojectionVectorTexture;
#if CONFIG_RESURRECTION_REPROJECTION
Texture2D<float2> ClosestDepthTexture;
#else
Texture2D<float> ClosestDepthTexture;
#endif
Texture2D<uint> DilateMaskTexture;
Texture2D<uint> DepthErrorTexture;
Texture2DArray<uint> PrevAtomicTextureArray;
Texture2DArray<tsr_half4> PrevHistoryGuide;
Texture2DArray<tsr_half4> PrevHistoryMoire;
#if CONFIG_THIN_GEOMETRY_COVERAGE_REPROJECTION
Texture2DArray<tsr_half> PrevHistoryCoverage;
#endif
float2 PrevGuideInfo_Extent;
float2 PrevGuideInfo_ExtentInverse;
uint2 PrevGuideInfo_ViewportMin;
uint2 PrevGuideInfo_ViewportMax;
float2 PrevGuideInfo_ViewportSize;
float2 PrevGuideInfo_ViewportSizeInverse;
float2 PrevGuideInfo_UVViewportBilinearMin;
float2 PrevGuideInfo_UVViewportBilinearMax;
FScreenTransform InputPixelPosToReprojectScreenPos;
FScreenTransform ScreenPosToPrevHistoryGuideBufferUV;
FScreenTransform ScreenPosToResurrectionGuideBufferUV;
float2 ResurrectionGuideUVViewportBilinearMin;
float2 ResurrectionGuideUVViewportBilinearMax;
float3 HistoryGuideQuantizationError;
float ResurrectionFrameIndex;
float PrevFrameIndex;
float4x4 ClipToResurrectionClip;
RWTexture2DArray<tsr_half4> ReprojectedHistoryGuideOutput;
RWTexture2DArray<tsr_half4> ReprojectedHistoryMoireOutput;
#if CONFIG_THIN_GEOMETRY_COVERAGE_REPROJECTION
RWTexture2DArray<tsr_half> ReprojectedHistoryCoverageOutput;
#endif
RWTexture2DArray<uint> ReprojectionFieldOutput;
RWTexture2D<tsr_half2> DecimateMaskOutput;
//------------------------------------------------------- DEBUG
#define DEBUG_ARRAY_SIZE 8
static float4 Debug[DEBUG_ARRAY_SIZE];
//------------------------------------------------------- LDS
#if CONFIG_MANUAL_LDS_SPILL
groupshared uint SharedArray0[TILE_SIZE * TILE_SIZE];
groupshared float SharedArray1[TILE_SIZE * TILE_SIZE];
#endif
//------------------------------------------------------- FUNCTIONS
// Correct pre exposure of the guide color.
tsr_halfC CorrectGuideColorExposure(tsr_halfC ReprojectedHistoryGuideColor, const float PreExposureCorrection)
{
ReprojectedHistoryGuideColor = PreExposureCorrectGCS(ReprojectedHistoryGuideColor, PreExposureCorrection);
// Removes NaN
ReprojectedHistoryGuideColor = -min(-ReprojectedHistoryGuideColor, tsr_half(0.0));
return ReprojectedHistoryGuideColor;
}
#if CONFIG_MOIRE_REPROJECTION
// Sample previous moire history
tsr_half4 ReprojectPrevHistoryMoire(tsr_ushort2 InputPixelPos, float2 TextureUV)
{
uint2 Random = Rand3DPCG16(int3(InputPixelPos - InputInfo_ViewportMin, View.StateFrameIndexMod8)).xy;
float2 E = Hammersley16(0, 1, Random).xy;
return PrevHistoryMoire.SampleLevel(GlobalPointClampedSampler, float3(TextureUV + (E - 0.5) * PrevGuideInfo_ExtentInverse, 0), 0);
}
// Correct pre exposure of the moire history.
tsr_half4 CorrectMoireExposure(tsr_half4 ReprojectedHistoryMoireColor, const float PreExposureCorrection)
{
tsr_half PrevFlickeringHistory = ReprojectedHistoryMoireColor[0];
PrevFlickeringHistory = PreExposureCorrectGCS(PrevFlickeringHistory.xxx, PreExposureCorrection).r;
// Removes NaN
PrevFlickeringHistory = -min(-PrevFlickeringHistory, tsr_half(0.0));
return tsr_half4(PrevFlickeringHistory, ReprojectedHistoryMoireColor.yzw);
}
#endif
#if CONFIG_THIN_GEOMETRY_COVERAGE_REPROJECTION
tsr_half ReprojectPrevHistoryCoverage(tsr_ushort2 InputPixelPos, float2 TextureUV)
{
return PrevHistoryCoverage.SampleLevel(GlobalPointClampedSampler, float3(TextureUV, 0), 0);
}
#endif
//ISOLATE
void ReprojectAllPrevTextures(
uint2 GroupId,
uint GroupThreadIndex,
uint EncodedReprojectionVector,
float DeviceZ,
out uint HistoryClosestDeviceZSamples0[4],
out uint HistoryClosestDeviceZSamples1[4],
out FCatmullRomFetches<tsr_half4> ReprojectedHistoryGuideSamples,
#if CONFIG_SCENE_COLOR_ALPHA
out FCatmullRomFetches<tsr_half4> ReprojectedHistoryGuideUncertaintySamples,
#endif
#if CONFIG_MOIRE_REPROJECTION
out tsr_half4 ReprojectedHistoryMoireColor,
#endif
#if CONFIG_THIN_GEOMETRY_COVERAGE_REPROJECTION
out tsr_half ReprojectedHistoryCoverage,
#endif
#if CONFIG_RESURRECTION_REPROJECTION
out tsr_halfC ResurrectedHistoryGuideColor,
out tsr_half ResurrectedHistoryGuideUncertainty,
out bool bIsResurrectionOffScreen,
#endif
out bool bIsOffScreen)
{
tsr_ushort2 InputPixelPos = (
Map8x8Tile2x2Lane(GroupThreadIndex) +
(tsr_ushort2(InputInfo_ViewportMin) + tsr_ushort2(GroupId) * tsr_ushort2(TILE_SIZE, TILE_SIZE)));
{
float2 ScreenPos = ApplyScreenTransform(float2(InputPixelPos), InputPixelPosToScreenPos);
float2 PrevScreenPos = ScreenPos - DecodeReprojectionVector(EncodedReprojectionVector);
LoadPrevAtomicTexturesSamples(
PrevAtomicTextureArray,
PrevScreenPos,
/* out */ HistoryClosestDeviceZSamples0,
/* out */ HistoryClosestDeviceZSamples1);
}
{
float2 OutputScreenPos = ApplyScreenTransform(float2(InputPixelPos), InputPixelPosToReprojectScreenPos);
float2 PrevOutputScreenPos = OutputScreenPos - DecodeReprojectionVector(EncodedReprojectionVector);
bIsOffScreen = (
(bCameraCut != 0) ||
(PrevOutputScreenPos.x < -1.0) ||
(PrevOutputScreenPos.y < -1.0) ||
(PrevOutputScreenPos.x > 1.0) ||
(PrevOutputScreenPos.y > 1.0));
float2 KernelHistoryBufferUV = ApplyScreenTransform(PrevOutputScreenPos, ScreenPosToPrevHistoryGuideBufferUV);
#if CONFIG_SCENE_COLOR_ALPHA
ReprojectedHistoryGuideSamples.FetchSamples(
PrevHistoryGuide,
KernelHistoryBufferUV,
PrevFrameIndex * 2.0 + 0.0,
PrevGuideInfo_Extent,
PrevGuideInfo_ExtentInverse,
PrevGuideInfo_UVViewportBilinearMin,
PrevGuideInfo_UVViewportBilinearMax);
ReprojectedHistoryGuideUncertaintySamples.FetchSamples(
PrevHistoryGuide,
KernelHistoryBufferUV,
PrevFrameIndex * 2.0 + 1.0,
PrevGuideInfo_Extent,
PrevGuideInfo_ExtentInverse,
PrevGuideInfo_UVViewportBilinearMin,
PrevGuideInfo_UVViewportBilinearMax);
#else
ReprojectedHistoryGuideSamples.FetchSamples(
PrevHistoryGuide,
KernelHistoryBufferUV,
PrevFrameIndex,
PrevGuideInfo_Extent,
PrevGuideInfo_ExtentInverse,
PrevGuideInfo_UVViewportBilinearMin,
PrevGuideInfo_UVViewportBilinearMax);
#endif
#if CONFIG_MOIRE_REPROJECTION
{
ReprojectedHistoryMoireColor = ReprojectPrevHistoryMoire(InputPixelPos, KernelHistoryBufferUV);
}
#endif
#if CONFIG_THIN_GEOMETRY_COVERAGE_REPROJECTION
{
ReprojectedHistoryCoverage = ReprojectPrevHistoryCoverage(InputPixelPos, KernelHistoryBufferUV);
}
#endif
}
#if CONFIG_RESURRECTION_REPROJECTION
{
float2 OutputScreenPos = ApplyScreenTransform(float2(InputPixelPos), InputPixelPosToReprojectScreenPos);
float4 ThisClip = float4(OutputScreenPos, DeviceZ, 1);
float4 ResurrectionClip = mul(ThisClip, ClipToResurrectionClip);
float2 ResurrectionScreen = ResurrectionClip.xy / ResurrectionClip.w;
bIsResurrectionOffScreen = (
(ResurrectionScreen.x < -1.0) ||
(ResurrectionScreen.y < -1.0) ||
(ResurrectionScreen.x > 1.0) ||
(ResurrectionScreen.y > 1.0));
float2 KernelHistoryBufferUV = ApplyScreenTransform(ResurrectionScreen, ScreenPosToResurrectionGuideBufferUV);
KernelHistoryBufferUV = fastClamp(KernelHistoryBufferUV, ResurrectionGuideUVViewportBilinearMin, ResurrectionGuideUVViewportBilinearMax);
#if CONFIG_SCENE_COLOR_ALPHA
ResurrectedHistoryGuideColor = PrevHistoryGuide.SampleLevel(GlobalBilinearClampedSampler, float3(KernelHistoryBufferUV, ResurrectionFrameIndex * 2.0 + 0.0), 0);
ResurrectedHistoryGuideUncertainty = PrevHistoryGuide.SampleLevel(GlobalBilinearClampedSampler, float3(KernelHistoryBufferUV, ResurrectionFrameIndex * 2.0 + 1.0), 0).r;
#else
tsr_half4 RawGuide = PrevHistoryGuide.SampleLevel(GlobalBilinearClampedSampler, float3(KernelHistoryBufferUV, ResurrectionFrameIndex), 0);
ResurrectedHistoryGuideColor = RawGuide.rgb;
ResurrectedHistoryGuideUncertainty = RawGuide.a;
#endif
}
#endif
} // ReprojectAllPrevTextures()
//------------------------------------------------------- ENTRY POINT
[numthreads(TILE_SIZE * TILE_SIZE, 1, 1)]
void MainCS(
uint2 GroupId : SV_GroupID,
uint GroupThreadIndex : SV_GroupIndex)
{
for (uint DebugId = 0; DebugId < DEBUG_ARRAY_SIZE; DebugId++)
{
Debug[DebugId] = 0.0;
}
// Sample current frame informations.
uint DilatedEncodedReprojectionVector;
float DeviceZ;
float PrevDeviceZ;
float DeviceZError;
bool bHasPixelAnimation;
bool bHasReprojectionOffset;
tsr_half ReprojectionEdge;
ISOLATE
{
uint EncodedReprojectionVectorNeighborhood[CONFIG_BUTTERFLY_SAMPLES];
tsr_half PremilinaryReprojectionEdge;
// Issue texture fetechs
ISOLATE
{
tsr_ushort2 InputPixelPos = (
Map8x8Tile2x2Lane(GroupThreadIndex) +
(tsr_ushort2(InputInfo_ViewportMin) + tsr_ushort2(GroupId) * tsr_ushort2(TILE_SIZE, TILE_SIZE)));
uint EncodedDilateMask = DilateMaskTexture[InputPixelPos];
uint EncodedDeviceZError = DepthErrorTexture[InputPixelPos];
#if CONFIG_RESURRECTION_REPROJECTION
float2 DeviceZAndPrevDeviceZ = ClosestDepthTexture[InputPixelPos];
DeviceZ = DeviceZAndPrevDeviceZ.g;
#else
float DeviceZAndPrevDeviceZ = ClosestDepthTexture[InputPixelPos];
DeviceZ = 0.0;
#endif
bHasReprojectionOffset = (EncodedDilateMask & 0x80u) != 0u;
PremilinaryReprojectionEdge = tsr_half(tsr_ushort(EncodedDilateMask & 0x7Fu)) * rcp(tsr_half(127.0));
PrevDeviceZ = abs(DeviceZAndPrevDeviceZ.r);
bHasPixelAnimation = DeviceZAndPrevDeviceZ.r < 0.0;
DeviceZError = DecodeDeviceZError(PrevDeviceZ, EncodedDeviceZError);
FetchReprojectionNeighborhood(DilatedReprojectionVectorTexture, InputPixelPos, /* out */ EncodedReprojectionVectorNeighborhood);
DilatedEncodedReprojectionVector = AccessNeighborhoodCenter(EncodedReprojectionVectorNeighborhood);
}
// LDS spill to save VGPR on the history texture fetches.
#if CONFIG_MANUAL_LDS_SPILL
ISOLATE
{
SharedArray0[GroupThreadIndex] = DilatedEncodedReprojectionVector;
SharedArray1[GroupThreadIndex] = PrevDeviceZ;
}
#endif
// Detect whether is on velocity edge to clamp the high frequencies
ISOLATE
{
float2 PixelVelocityNeighborhood[CONFIG_BUTTERFLY_SAMPLES];
ComputePixelVelocityNeighborhood(EncodedReprojectionVectorNeighborhood, /* out */ PixelVelocityNeighborhood);
ReprojectionEdge = ComputeReprojectionEdge(PixelVelocityNeighborhood);
ReprojectionEdge = select(PremilinaryReprojectionEdge > tsr_half(0.9), PremilinaryReprojectionEdge, ReprojectionEdge);
ReprojectionEdge = select(bHasReprojectionOffset, ReprojectionEdge, tsr_half(1.0));
}
}
// Sample the previous frame information
uint HistoryClosestDeviceZSamples0[4];
uint HistoryClosestDeviceZSamples1[4];
FCatmullRomFetches<tsr_half4> ReprojectedHistoryGuideSamples;
#if CONFIG_SCENE_COLOR_ALPHA
FCatmullRomFetches<tsr_half4> ReprojectedHistoryGuideUncertaintySamples;
#endif
#if CONFIG_MOIRE_REPROJECTION
tsr_half4 ReprojectedHistoryMoireColor;
#endif
#if CONFIG_THIN_GEOMETRY_COVERAGE_REPROJECTION
tsr_half ReprojectedHistoryCoverage;
#endif
#if CONFIG_RESURRECTION_REPROJECTION
tsr_halfC ResurrectedHistoryGuideColor;
tsr_half ResurrectedHistoryGuideUncertainty;
bool bIsResurrectionOffScreen;
#else
const bool bIsResurrectionOffScreen = false;
#endif
bool bIsOffScreen;
// Issues all the overlapping texture fetches for previous frame texture using motion vectors
{
ReprojectAllPrevTextures(
GroupId, GroupThreadIndex,
DilatedEncodedReprojectionVector, DeviceZ,
/* out */ HistoryClosestDeviceZSamples0,
/* out */ HistoryClosestDeviceZSamples1,
/* out */ ReprojectedHistoryGuideSamples,
#if CONFIG_SCENE_COLOR_ALPHA
/* out */ ReprojectedHistoryGuideUncertaintySamples,
#endif
#if CONFIG_MOIRE_REPROJECTION
/* out */ ReprojectedHistoryMoireColor,
#endif
#if CONFIG_THIN_GEOMETRY_COVERAGE_REPROJECTION
/* out */ ReprojectedHistoryCoverage,
#endif
#if CONFIG_RESURRECTION_REPROJECTION
/* out */ ResurrectedHistoryGuideColor,
/* out */ ResurrectedHistoryGuideUncertainty,
/* out */ bIsResurrectionOffScreen,
#endif
/* out */ bIsOffScreen);
}
// Process the history samples
tsr_halfC ReprojectedHistoryGuideColor;
tsr_half ReprojectedHistoryGuideUncertainty;
bool bIsParallaxDisocclusion;
bool bReprojectionHollFill;
//ISOLATE
{
#if CONFIG_SCENE_COLOR_ALPHA
{
ReprojectedHistoryGuideColor = saturate(ReprojectedHistoryGuideSamples.AccumulateSamples());
ReprojectedHistoryGuideUncertainty = ReprojectedHistoryGuideUncertaintySamples.AccumulateSamples().r;
}
#else
{
tsr_half4 RawGuide = ReprojectedHistoryGuideSamples.AccumulateSamples();
ReprojectedHistoryGuideColor = RawGuide.rgb;
ReprojectedHistoryGuideUncertainty = RawGuide.a;
}
#endif
ReprojectedHistoryGuideColor = CorrectGuideColorExposure(ReprojectedHistoryGuideColor, HistoryPreExposureCorrection);
#if CONFIG_MOIRE_REPROJECTION
{
ReprojectedHistoryMoireColor = CorrectMoireExposure(ReprojectedHistoryMoireColor, HistoryPreExposureCorrection);
}
#endif
#if CONFIG_RESURRECTION_REPROJECTION
{
ResurrectedHistoryGuideColor = CorrectGuideColorExposure(ResurrectedHistoryGuideColor, ResurrectionPreExposureCorrection);
}
#endif
// LDS despill
#if CONFIG_MANUAL_LDS_SPILL
{
DilatedEncodedReprojectionVector = SharedArray0[GroupThreadIndex];
PrevDeviceZ = SharedArray1[GroupThreadIndex];
}
#endif
tsr_ushort2 InputPixelPos = (
Map8x8Tile2x2Lane(GroupThreadIndex) +
(tsr_ushort2(InputInfo_ViewportMin) + tsr_ushort2(GroupId) * tsr_ushort2(TILE_SIZE, TILE_SIZE)));
float2 ScreenPos = ApplyScreenTransform(float2(InputPixelPos), InputPixelPosToScreenPos);
float2 ScreenVelocity = DecodeReprojectionVector(DilatedEncodedReprojectionVector);
// Compute the parralax rejection mask
float2 HoleFillingPixelVelocity;
bool bCanHoleFill;
ProcessPrevAtomicTexturesSamples(
HistoryClosestDeviceZSamples0,
HistoryClosestDeviceZSamples1,
ScreenPos,
ScreenVelocity,
PrevDeviceZ,
DeviceZError,
bIsOffScreen,
/* out */ bIsParallaxDisocclusion,
/* out */ HoleFillingPixelVelocity,
/* out */ bCanHoleFill);
// LDS despill
#if CONFIG_MANUAL_LDS_SPILL
{
DilatedEncodedReprojectionVector = SharedArray0[GroupThreadIndex];
}
#endif
bReprojectionHollFill = bCanHoleFill && bIsParallaxDisocclusion;
// If the hole filling velocity has better result that the neighborhood, use that.
BRANCH
if (bReprojectionHollFill)
{
float2 HoleFillingReprojectionVector = HoleFillingPixelVelocity * InputPixelVelocityToScreenVelocity;
DilatedEncodedReprojectionVector = EncodeReprojectionVector(HoleFillingReprojectionVector);
}
}
// Builds the decimate bitmask
tsr_ushort DecimateBitMask =
select(bIsOffScreen, tsr_ushort(0x01), tsr_ushort(0x0)) |
select(bIsParallaxDisocclusion, tsr_ushort(0x02), tsr_ushort(0x0)) |
select(bHasPixelAnimation, tsr_ushort(0x04), tsr_ushort(0x0)) |
select(bReprojectionHollFill, tsr_ushort(0x08), tsr_ushort(0x0)) |
select(bIsResurrectionOffScreen, tsr_ushort(0x10), tsr_ushort(0x0));
// Output full res
ISOLATE
{
tsr_ushort2 InputPixelPos = (
Map8x8Tile2x2Lane(GroupThreadIndex) +
(tsr_ushort2(InputInfo_ViewportMin) + tsr_ushort2(GroupId) * tsr_ushort2(TILE_SIZE, TILE_SIZE)));
#if CONFIG_ENABLE_STOCASTIC_QUANTIZATION && 1
{
uint2 Random = Rand3DPCG16(int3(InputPixelPos - InputInfo_ViewportMin, View.StateFrameIndexMod8)).xy;
tsr_half E = tsr_half(Hammersley16(0, 1, Random).x);
ReprojectedHistoryGuideColor = QuantizeForUNormRenderTarget(ReprojectedHistoryGuideColor, E, HistoryGuideQuantizationError);
}
#endif
tsr_short2 OutputPixelPos = InvalidateOutputPixelPos(InputPixelPos, InputInfo_ViewportMax);
{
#if CONFIG_SCENE_COLOR_ALPHA
ReprojectedHistoryGuideOutput[tsr_short3(OutputPixelPos, 0)] = ReprojectedHistoryGuideColor;
ReprojectedHistoryGuideOutput[tsr_short3(OutputPixelPos, 1)] = tsr_half4(ReprojectedHistoryGuideUncertainty, 0, 0, 0);
#else
ReprojectedHistoryGuideOutput[tsr_short3(OutputPixelPos, 0)] = tsr_half4(ReprojectedHistoryGuideColor.xyz, ReprojectedHistoryGuideUncertainty);
#endif
#if CONFIG_MOIRE_REPROJECTION
ReprojectedHistoryMoireOutput[tsr_short3(OutputPixelPos, 0)] = ReprojectedHistoryMoireColor;
#endif
#if CONFIG_THIN_GEOMETRY_COVERAGE_REPROJECTION
ReprojectedHistoryCoverageOutput[tsr_short3(OutputPixelPos, 0)] = ReprojectedHistoryCoverage;
#endif
}
#if CONFIG_RESURRECTION_REPROJECTION
{
#if CONFIG_SCENE_COLOR_ALPHA
ReprojectedHistoryGuideOutput[tsr_short3(OutputPixelPos, 2)] = ResurrectedHistoryGuideColor;
ReprojectedHistoryGuideOutput[tsr_short3(OutputPixelPos, 3)] = tsr_half4(ResurrectedHistoryGuideUncertainty, 0, 0, 0);
#else
ReprojectedHistoryGuideOutput[tsr_short3(OutputPixelPos, 1)] = tsr_half4(ResurrectedHistoryGuideColor.xyz, ResurrectedHistoryGuideUncertainty);
#endif
}
#endif
DecimateMaskOutput[OutputPixelPos] = tsr_half2(tsr_half(DecimateBitMask) * rcp(tsr_half(255.0)), ReprojectionEdge);
// Selectively overwrite the reprojection field
{
bool bWriteReprojectionVector = bReprojectionHollFill || true;
bool bWriteReprojectionJacobian = bReprojectionHollFill;
// Disable the reprojection's jacobian since this is to extrude disoccluded area we don't know how they look anyway.
const uint EncodedReprojectionJacobian = EncodeReprojectionJacobian(tsr_half2x2(tsr_half(0.0).xxxx));
ReprojectionFieldOutput[tsr_short3(select(bWriteReprojectionVector, OutputPixelPos.x, -tsr_short(1)), OutputPixelPos.y, kReprojectionVectorOutputIndex)] = DilatedEncodedReprojectionVector;
ReprojectionFieldOutput[tsr_short3(select(bWriteReprojectionJacobian, OutputPixelPos.x, -tsr_short(1)), OutputPixelPos.y, kReprojectionJacobianOutputIndex)] = EncodedReprojectionJacobian;
}
#if DEBUG_OUTPUT
for (uint DebugId = 0; DebugId < DEBUG_ARRAY_SIZE; DebugId++)
{
DebugOutput[tsr_short3(OutputPixelPos, DebugId)] = Debug[DebugId];
}
#endif
}
}