Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 69 additions & 31 deletions rtxdi-sdk/include/rtxdi/ResamplingFunctions.slangh
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ bool RTXDI_StreamSample(
reservoir.portalIdx = uint8_t(portalIndex);
reservoir.uv = uv;
reservoir.targetPdf = targetPdf;
reservoir.lightIdentity = RAB_UseLightIdentityResolution() ? RAB_GetLightIdentity(uint16_t(lightIndex)) : 0u;
}

return selectSample;
Expand Down Expand Up @@ -87,6 +88,7 @@ bool RTXDI_CombineReservoirs(
reservoir.portalIdx = newReservoir.portalIdx;
reservoir.uv = newReservoir.uv;
reservoir.targetPdf = targetPdf;
reservoir.lightIdentity = newReservoir.lightIdentity;
}

return selectSample;
Expand Down Expand Up @@ -330,6 +332,7 @@ RTXDI_Reservoir RTXDI_SampleSingleLight(
state.uv = lightUV;
state.targetPdf = targetPdf;
state.M = 1;
state.lightIdentity = (targetPdf > 0 && RAB_UseLightIdentityResolution()) ? RAB_GetLightIdentity(lightIdx) : 0u;

if (testVisibility)
{
Expand Down Expand Up @@ -485,6 +488,8 @@ void RTXDI_TemporalResampling(
// calculate targetPdf for the old sample
bool selectedPreviousSample = false;
uint previousM = 0;
bool useStoredTemporalTargetPdf = false;
float storedTemporalTargetPdf = 0.0f;
if (prevPixelID.x >= 0)
{
RTXDI_Reservoir prevReservoir = RAB_LoadReservoir(prevPixelID, tparams.sourceBufferIndex);
Expand All @@ -493,17 +498,25 @@ void RTXDI_TemporalResampling(

// Save the light index in the previous frame context
const uint16_t originalPrevLightIdx = prevReservoir.lightIdx;

uint16_t mappedLightID = RAB_TranslateLightIndex(prevReservoir.lightIdx, /* currentToPrevious = */ false);
if (mappedLightID == RTXDI_INVALID_LIGHT_INDEX)

// Map the previous reservoir's stored light index to the current frame.
bool prevReservoirValid;
if (RAB_UseLightIdentityResolution())
{
// Kill the reservoir
prevReservoir.weightSum = 0;
prevReservoir.lightIdx = RTXDI_INVALID_LIGHT_INDEX;
prevReservoirValid = RAB_ResolveReservoirLightIndex(prevReservoir);
}
else

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the light identity match and light index translation cannot work at the same time? I see if else structure here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. There is actually a light index translation inside RAB_ResolveReservoirLightIndex as a backup case for dynamic lights (because the hash is position-dependent). The main reason that a if-else is used here is that we don't know which reservoirs were active last frame. So we cannot guarantee that translate light index gives correct result.

{
const uint16_t mappedLightID = RAB_TranslateLightIndex(prevReservoir.lightIdx, /* currentToPrevious = */ false);
prevReservoir.lightIdx = mappedLightID;
prevReservoirValid = (mappedLightID != RTXDI_INVALID_LIGHT_INDEX);
}

if (!prevReservoirValid)
{
// The light this reservoir referenced no longer exists: kill the reservoir.
prevReservoir.weightSum = 0;
prevReservoir.lightIdx = RTXDI_INVALID_LIGHT_INDEX;
}

float weightAtCurrent = 0;
Expand All @@ -527,7 +540,17 @@ void RTXDI_TemporalResampling(
selectedPreviousSample = RTXDI_CombineReservoirs(state, prevReservoir, RAB_GetNextRandom(rng), weightAtCurrent);
if (selectedPreviousSample)
{
selectedLightPrevIdx = originalPrevLightIdx;
if (!RAB_UseLightIdentityResolution())
{
selectedLightPrevIdx = originalPrevLightIdx;
}
else
{
// Reuse the reservoir's stored targetPdf as the temporal MIS target
// This is more stable when reservoirs are not updated every frame
useStoredTemporalTargetPdf = true;
storedTemporalTargetPdf = prevReservoir.targetPdf;
}
}
}

Expand All @@ -538,44 +561,54 @@ void RTXDI_TemporalResampling(
float piSum = state.targetPdf * initialM;

if(RTXDI_IsValidReservoir(state) &&
selectedLightPrevIdx != RTXDI_INVALID_LIGHT_INDEX &&
(selectedLightPrevIdx != RTXDI_INVALID_LIGHT_INDEX || useStoredTemporalTargetPdf) &&
previousM > 0)
{
float temporalP = 0;
RAB_Surface temporalSurface = RAB_GetSurfaceForTemporalBiasCorrection(surface, prevPixelID, prevNormal, tparams.virtualMotionVector);
float temporalP;

RAB_Surface virtualTemporalSurface = temporalSurface;
#if !RTXDI_NO_PORTALS
if (state.portalIdx != RTXDI_INVALID_PORTAL_INDEX)
if (useStoredTemporalTargetPdf)
{
RAB_TransformSurface(virtualTemporalSurface, RAB_GetPortalTransform(state.portalIdx));
// the previous-frame light buffer can't be indexed with its stored index,
// so use the reservoir's stored target as the selected sample's target in the temporal domain.
temporalP = storedTemporalTargetPdf;
}
else
{
RAB_Surface temporalSurface = RAB_GetSurfaceForTemporalBiasCorrection(surface, prevPixelID, prevNormal, tparams.virtualMotionVector);

RAB_Surface virtualTemporalSurface = temporalSurface;
#if !RTXDI_NO_PORTALS
if (state.portalIdx != RTXDI_INVALID_PORTAL_INDEX)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The portal handling code should be independent from the multiple frame light ID match code. What it's doing here is to calculate the virtual surface with respect to portal space. It's still needed when you are matching light ID across multiple frames.

{
RAB_TransformSurface(virtualTemporalSurface, RAB_GetPortalTransform(state.portalIdx));
}
#endif

RAB_LightSample selectedSampleAtTemporalSurface = RAB_GetLightSample(selectedLightPrevIdx,
state.uv, virtualTemporalSurface, /* usePreviousLights = */ true);
RAB_LightSample selectedSampleAtTemporalSurface = RAB_GetLightSample(selectedLightPrevIdx,
state.uv, virtualTemporalSurface, /* usePreviousLights = */ true);

temporalP = RAB_GetLightSampleTargetPdfForSurface(selectedSampleAtTemporalSurface, virtualTemporalSurface);
temporalP = RAB_GetLightSampleTargetPdfForSurface(selectedSampleAtTemporalSurface, virtualTemporalSurface);

// If we have selected the temporal sample and discard is enabled, we know that the sample was visible
// on the previous frame because otherwise it would've been discarded and we couldn't select it.
// In practice, temporal samples are selected way more often than new samples, which means this
// condition culls the vast majority of visibility rays here.
const bool selectedSampleKnownVisible = selectedPreviousSample && tparams.discardInvisibleSamples;
// If we have selected the temporal sample and discard is enabled, we know that the sample was visible
// on the previous frame because otherwise it would've been discarded and we couldn't select it.
// In practice, temporal samples are selected way more often than new samples, which means this
// condition culls the vast majority of visibility rays here.
const bool selectedSampleKnownVisible = selectedPreviousSample && tparams.discardInvisibleSamples;

if (temporalP > 0 && tparams.biasCorrectionMode >= RTXDI_BIAS_CORRECTION_RAY_TRACED && !selectedSampleKnownVisible)
{
float3 attenuation;
if (temporalP > 0 && tparams.biasCorrectionMode >= RTXDI_BIAS_CORRECTION_RAY_TRACED && !selectedSampleKnownVisible)
{
float3 attenuation;
#if RTXDI_NO_PORTALS
bool sampleVisible = RAB_TraceLightSampleVisibility(temporalSurface, selectedSampleAtTemporalSurface,
RTXDI_INVALID_PORTAL_INDEX, false, false, true, attenuation);
bool sampleVisible = RAB_TraceLightSampleVisibility(temporalSurface, selectedSampleAtTemporalSurface,
RTXDI_INVALID_PORTAL_INDEX, false, false, true, attenuation);
#else
bool sampleVisible = RAB_TraceLightSampleVisibility(temporalSurface, selectedSampleAtTemporalSurface,
state.portalIdx, false, false, true, attenuation);
bool sampleVisible = RAB_TraceLightSampleVisibility(temporalSurface, selectedSampleAtTemporalSurface,
state.portalIdx, false, false, true, attenuation);
#endif

if (!sampleVisible)
temporalP = 0;
if (!sampleVisible)
temporalP = 0;
}
}

pi = selectedPreviousSample ? temporalP : pi;
Expand Down Expand Up @@ -684,6 +717,11 @@ RTXDI_Reservoir RTXDI_SpatialResampling(

RTXDI_Reservoir neighborReservoir = RAB_LoadReservoir(neighborID, sparams.sourceBufferIndex);

if (RAB_UseLightIdentityResolution())
{
RAB_ResolveReservoirLightIndex(neighborReservoir);
}

float targetPdf = 0;
if (RTXDI_IsValidReservoir(neighborReservoir))
{
Expand Down
6 changes: 5 additions & 1 deletion rtxdi-sdk/include/rtxdi/Reservoir.slangh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct RTXDI_Reservoir
float weightSum;
float targetPdf;
uint16_t M;
uint lightIdentity;
};

RTXDI_PackedReservoir RTXDI_PackReservoir(RTXDI_Reservoir reservoir)
Expand All @@ -38,7 +39,8 @@ RTXDI_PackedReservoir RTXDI_PackReservoir(RTXDI_Reservoir reservoir)
packedReservoir.data0 = asuint(float4(reservoir.uv, reservoir.weightSum, reservoir.targetPdf));
packedReservoir.data1.x = ((uint(reservoir.lightIdx) << 16) | (reservoir.M & 0xffff));
packedReservoir.data1.y = uint(reservoir.portalIdx);
packedReservoir.data1.zw = 0;
packedReservoir.data1.z = reservoir.lightIdentity;
packedReservoir.data1.w = 0;
return packedReservoir;
}

Expand All @@ -51,6 +53,7 @@ RTXDI_Reservoir RTXDI_EmptyReservoir()
s.weightSum = 0.0f;
s.M = uint16_t(0);
s.portalIdx = RTXDI_INVALID_PORTAL_INDEX;
s.lightIdentity = 0;
return s;
}

Expand All @@ -64,6 +67,7 @@ RTXDI_Reservoir RTXDI_UnpackReservoir(RTXDI_PackedReservoir packedReservoir)
reservoir.M = uint16_t(packedReservoir.data1.x & 0xffff);

reservoir.portalIdx = uint8_t(packedReservoir.data1.y);
reservoir.lightIdentity = packedReservoir.data1.z;
return reservoir;
}

Expand Down
20 changes: 20 additions & 0 deletions rtxdi-sdk/shaders/ResamplingCompileTest.comp.slang
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,26 @@ int RAB_TranslateLightIndex(uint lightIndex, bool currentToPrevious)
return -1;
}

bool RAB_UseLightIdentityResolution()
{
return false;
}

uint RAB_GetLightIdentity(uint currentLightIdx)
{
return 0;
}

uint16_t RAB_ResolveLightIndexFromIdentity(uint identity)
{
return RTXDI_INVALID_LIGHT_INDEX;
}

bool RAB_ResolveReservoirLightIndex(inout RTXDI_Reservoir reservoir)
{
return RTXDI_IsValidReservoir(reservoir);
}

bool RAB_ShouldDiscardEnlargedPixel(inout RAB_RandomSamplerState rng, RAB_Surface surface, float3 virtualMotionVector)
{
return false;
Expand Down