Skip to content

Commit c92d27e

Browse files
svc-reach-platform-supportEvergreen
authored andcommitted
[Port] [6000.4] Graphics/SRP/RPF - [UUM-115209] Re-enable the second step of the Render Graph culling algorithm to prevent pass culling regression in HDRP
1 parent 2066342 commit c92d27e

File tree

2 files changed

+85
-49
lines changed

2 files changed

+85
-49
lines changed

Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/NativePassCompiler.cs

Lines changed: 49 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -475,58 +475,59 @@ void CullUnusedRenderGraphPasses()
475475
return;
476476

477477
// Must come first
478-
// TODO make another subfunction CullRenderGraphPassesWithNoSideEffect() for this first step of the culling stage
479-
var ctx = contextData;
478+
CullRenderGraphPassesWithNoSideEffect();
480479

481-
// Cull all passes first
482-
ctx.CullAllPasses(true);
483-
484-
// Flood fill downstream algorithm using BFS,
485-
// starting from the passes with side effects (writting to imported texture, not allowed to be culled, globals modification...)
486-
// to all their dependencies
487-
while (m_HasSideEffectPassIdCullingStack.Count != 0)
488-
{
489-
int passId = m_HasSideEffectPassIdCullingStack.Pop();
480+
// Second step of the algorithm that comes later
481+
CullRenderGraphPassesWritingOnlyUnusedResources();
482+
}
483+
}
490484

491-
ref var passData = ref ctx.passData.ElementAt(passId);
485+
void CullRenderGraphPassesWithNoSideEffect()
486+
{
487+
var ctx = contextData;
492488

493-
// We already found this node through another dependency chain
494-
if (!passData.culled) continue;
489+
// Cull all passes first
490+
ctx.CullAllPasses(true);
495491

496-
// Flow upstream from this node
497-
foreach (ref readonly var input in passData.Inputs(ctx))
498-
{
499-
ref var inputVersionedDataRes = ref ctx.resources[input.resource];
492+
// Flood fill downstream algorithm using BFS,
493+
// starting from the passes with side effects (writting to imported texture, not allowed to be culled, globals modification...)
494+
// to all their dependencies
495+
while (m_HasSideEffectPassIdCullingStack.Count != 0)
496+
{
497+
int passId = m_HasSideEffectPassIdCullingStack.Pop();
500498

501-
if (inputVersionedDataRes.written)
502-
{
503-
m_HasSideEffectPassIdCullingStack.Push(inputVersionedDataRes.writePassId);
504-
}
505-
}
499+
ref var passData = ref ctx.passData.ElementAt(passId);
506500

507-
// We need this node, don't cull it
508-
passData.culled = false;
509-
}
501+
// We already found this node through another dependency chain
502+
if (!passData.culled) continue;
510503

511-
// Update graph based on freshly culled nodes, remove any connection to them
512-
// We start from the latest passes to the first ones as we might need to decrement the version number of unwritten resources
513-
var numPasses = ctx.passData.Length;
514-
for (int passIndex = numPasses - 1; passIndex >= 0; passIndex--)
504+
// Flow upstream from this node
505+
foreach (ref readonly var input in passData.Inputs(ctx))
515506
{
516-
ref readonly var pass = ref ctx.passData.ElementAt(passIndex);
507+
ref var inputVersionedDataRes = ref ctx.resources[input.resource];
517508

518-
// Remove the connections from the list so they won't be visited again
519-
if (pass.culled)
509+
if (inputVersionedDataRes.written)
520510
{
521-
pass.DisconnectFromResources(ctx);
511+
m_HasSideEffectPassIdCullingStack.Push(inputVersionedDataRes.writePassId);
522512
}
523513
}
524514

525-
// Second step of the algorithm, must come after
526-
// TODO: The resources culling step is currently disabled due to an issue: https://jira.unity3d.com/projects/SRP/issues/SRP-897
527-
// Renabled the resource culling step after addressing the depth attachment problem above.
528-
// Renabled the relevent tests
529-
// CullRenderGraphPassesWritingOnlyUnusedResources();
515+
// We need this node, don't cull it
516+
passData.culled = false;
517+
}
518+
519+
// Update graph based on freshly culled nodes, remove any connection to them
520+
// We start from the latest passes to the first ones as we might need to decrement the version number of unwritten resources
521+
var numPasses = ctx.passData.Length;
522+
for (int passIndex = numPasses - 1; passIndex >= 0; passIndex--)
523+
{
524+
ref readonly var pass = ref ctx.passData.ElementAt(passIndex);
525+
526+
// Remove the connections from the list so they won't be visited again
527+
if (pass.culled)
528+
{
529+
pass.DisconnectFromResources(ctx);
530+
}
530531
}
531532
}
532533

@@ -581,24 +582,25 @@ void CullRenderGraphPassesWritingOnlyUnusedResources()
581582
producerData.culled = true;
582583
producerData.DisconnectFromResources(ctx, unusedVersionedResourceIdCullingStack, type);
583584
}
584-
else // Producer is (still) necessary, but we might need to remove the read of the previous version coming implicitly with the write of the current version
585+
else
585586
{
587+
// Producer is still necessary for now, but if the previous version is only implicitly read by it to write the unused resource
588+
// then we can consider this version useless as well and add it to the stack.
589+
// We purposefully keep the connection between the producer and this resource nevertheless to ensure proper lifetime handling and attachment setup.
590+
// A more optimal approach memory-wise would be to cut the dependency, decrease the latestVersionNumber of the resource and release it earlier
591+
// but we then need to create a transient resource with the right attachment properties and attach it to the non-culled producer or the native render pass setup will be incorrect.
592+
586593
// We always add written resource to the stack so versionedIndex > 0
587594
var prevVersionedRes = new ResourceHandle(unusedResource, unusedResource.version - 1);
588595

589-
// If no explicit read is requested by the user (AccessFlag.Write only), we need to remove the implicit read
590-
// so that we cut cleanly the connection between previous version of the resource and current producer
591596
bool isImplicitRead = graph.m_RenderPasses[producerData.passId].implicitReadsList.Contains(prevVersionedRes);
592597

593598
if (isImplicitRead)
594599
{
595600
ref var prevVersionedDataRes = ref ctx.resources[prevVersionedRes];
596601

597-
// Notify the previous version of this resource that it is not read anymore by this pass
598-
prevVersionedDataRes.RemoveReadingPass(ctx, prevVersionedRes, producerData.passId);
599-
600-
// We also need to add the previous version of the resource to the stack IF no other pass than current producer needed it
601-
if (prevVersionedDataRes.written && prevVersionedDataRes.numReaders == 0)
602+
// We add the previous version of the resource to the stack IF no other pass than current producer needs it
603+
if (prevVersionedDataRes.written && prevVersionedDataRes.numReaders == 1)
602604
{
603605
unusedVersionedResourceIdCullingStack.Push(prevVersionedRes);
604606
}

Packages/com.unity.render-pipelines.core/Tests/Editor/NativePassCompilerRenderGraphTests.cs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1799,7 +1799,6 @@ public void BreakPasses_WhenNoOrDifferentShadingRateImage()
17991799
}
18001800
}
18011801

1802-
/* // DepthAttachment bug: https://jira.unity3d.com/projects/SRP/issues/SRP-897
18031802
[Test]
18041803
public void UnusedResourceCulling_CullProducer_WhenVersionsAreNotExplicitlyRead()
18051804
{
@@ -1905,7 +1904,6 @@ public void UnusedResourceCulling_CullProducer_WhenNoneOfItsWrittenResourcesAreE
19051904
// extraBuffer[2] latest version remains at 1
19061905
Assert.AreEqual(result.contextData.UnversionedResourceData(passes[0].attachments[1].handle).latestVersionNumber, 1);
19071906
}
1908-
*/
19091907

19101908
[Test]
19111909
public void UnusedResourceCulling_DoNotCullProducer_WhenOneOfItsWrittenResourcesIsExplicitlyRead()
@@ -2009,6 +2007,42 @@ public void UnusedResourceCulling_CullProducer_WhenNextVersionOfProducedResource
20092007
Assert.AreEqual(result.contextData.UnversionedResourceData(passes[0].attachments[0].handle).latestVersionNumber, 3);
20102008
}
20112009

2010+
[Test]
2011+
public void UnusedResourceCulling_KeepUnusedLatestVersion_WhenProducerIsNotCulled()
2012+
{
2013+
var g = AllocateRenderGraph();
2014+
var renderTargets = ImportAndCreateRenderTargets(g);
2015+
2016+
// This pass implicitly reads version 0 of extraTextures[0] and writes its version 1 that will be implicitly read in the next pass - dependency
2017+
// It also explicitly reads version 0 of extraTextures[1] and writes its version 1 but none reads it - no side effect
2018+
// It also implicitly reads version 0 of depthBuffer that is imported in RG but don't write it - no side effect
2019+
using (var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass0", out var passData))
2020+
{
2021+
builder.SetRenderAttachment(renderTargets.extraTextures[0], 0, AccessFlags.ReadWrite);
2022+
builder.SetRenderAttachment(renderTargets.backBuffer, 1, AccessFlags.Write);
2023+
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
2024+
}
2025+
2026+
// This pass writes version 2 of extraTextures[2] that no one uses
2027+
// technically we could get rid of this version but we preserve it for now to ensure correct resource lifetime and render pass setup
2028+
// We explicitly request this pass not to be culled, so it will be preserved
2029+
using (var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("TestPass1", out var passData))
2030+
{
2031+
builder.SetRenderAttachment(renderTargets.extraTextures[0], 0, AccessFlags.Write);
2032+
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
2033+
builder.AllowPassCulling(false);
2034+
}
2035+
2036+
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
2037+
var passes = result.contextData.GetNativePasses();
2038+
2039+
// - TestPass1 can NOT be culled
2040+
// - TestPass1 can NOT be culled as it writes to an imported texture (backbuffer)
2041+
Assert.IsTrue(passes != null && passes.Count == 1 && passes[0].numGraphPasses == 2);
2042+
// extraBuffer[0] latest version remains at 2 if no one uses it
2043+
Assert.AreEqual(result.contextData.UnversionedResourceData(passes[0].attachments[0].handle).latestVersionNumber, 2);
2044+
}
2045+
20122046
// Test using a texture as both a texture and render attachment, one will require topleft and one bottom left so this should throw.
20132047
[Test]
20142048
public void TextureUVOrigin_CheckInvalidMixedUVOriginUseTextureCompiler()

0 commit comments

Comments
 (0)