Skip to content

Commit 9e9c9bf

Browse files
axolotoEvergreen
authored andcommitted
Graphics/SRP/RPF - [UUM-102113] Fixed Render Graph pass merging with VRS
1 parent a9ab649 commit 9e9c9bf

File tree

6 files changed

+117
-61
lines changed

6 files changed

+117
-61
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ public bool TryAddToFragmentList(TextureAccess access, int listFirstIndex, int n
180180
}
181181
}
182182

183-
// Validate that we're correctly building up the fragment lists we can only append to the last list
184-
// not int the middle of lists
183+
// Validate that we're correctly building up the fragment lists, we can only append to the last list
184+
// not in the middle of the other lists
185185
Debug.Assert(listFirstIndex + numItems == fragmentData.Length);
186186

187187
fragmentData.Add(new PassFragmentData(

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

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -265,16 +265,18 @@ bool TrySetupRasterFragmentList(ref PassData ctxPass, ref RenderGraphPass inputP
265265
}
266266
}
267267

268-
// shading rate
269-
if (inputPass.hasShadingRateImage &&
270-
inputPass.shadingRateAccess.textureHandle.handle.IsValid())
268+
// shading rate image - this is a specific type of attachment (more of an image resource that can't be sampled, only used by the rasterizer)
269+
if (inputPass.hasShadingRateImage && inputPass.shadingRateAccess.textureHandle.handle.IsValid())
271270
{
272-
ctxPass.shadingRateImageIndex = ctx.fragmentData.Length;
273-
ctx.TryAddToFragmentList(inputPass.shadingRateAccess, ctxPass.shadingRateImageIndex, 0, out errorMessage);
271+
if (ctx.TryAddToFragmentList(inputPass.shadingRateAccess, ctxPass.firstFragment, ctxPass.numFragments, out errorMessage))
272+
{
273+
ctxPass.shadingRateImageIndex = ctx.fragmentData.Length - 1;
274+
}
274275

275276
if (errorMessage != null)
276277
{
277-
errorMessage = $"when trying to add VRS attachment of type {inputPass.shadingRateAccess.textureHandle.handle.type} at index {inputPass.shadingRateAccess.textureHandle.handle.index} - {errorMessage}";
278+
errorMessage =
279+
$"when trying to add VRS attachment of type {inputPass.shadingRateAccess.textureHandle.handle.type} at index {inputPass.shadingRateAccess.textureHandle.handle.index} - {errorMessage}";
278280
return false;
279281
}
280282
}
@@ -287,9 +289,7 @@ bool TrySetupRasterFragmentList(ref PassData ctxPass, ref RenderGraphPass inputP
287289
// Skip unused fragment input slots
288290
if (!inputPass.fragmentInputAccess[ci].textureHandle.IsValid()) continue;
289291

290-
var resource = inputPass.fragmentInputAccess[ci].textureHandle;
291-
if (ctx.TryAddToFragmentList(inputPass.fragmentInputAccess[ci], ctxPass.firstFragmentInput,
292-
ctxPass.numFragmentInputs, out errorMessage))
292+
if (ctx.TryAddToFragmentList(inputPass.fragmentInputAccess[ci], ctxPass.firstFragmentInput, ctxPass.numFragmentInputs, out errorMessage))
293293
{
294294
ctxPass.TryAddFragmentInput(inputPass.fragmentInputAccess[ci].textureHandle.handle, ctx, out errorMessage);
295295
}
@@ -312,8 +312,7 @@ bool TrySetupRasterFragmentList(ref PassData ctxPass, ref RenderGraphPass inputP
312312
// Skip unused random write slots
313313
if (!uav.h.IsValid()) continue;
314314

315-
if (ctx.TryAddToRandomAccessResourceList(uav.h, ci, uav.preserveCounterValue,
316-
ctxPass.firstRandomAccessResource, ctxPass.numRandomAccessResources, out errorMessage))
315+
if (ctx.TryAddToRandomAccessResourceList(uav.h, ci, uav.preserveCounterValue, ctxPass.firstRandomAccessResource, ctxPass.numRandomAccessResources, out errorMessage))
317316
{
318317
ctxPass.AddRandomAccessResource();
319318
}
@@ -481,8 +480,12 @@ void CullUnusedRenderGraphPasses()
481480
// Flow upstream from this node
482481
foreach (ref readonly var input in passData.Inputs(ctx))
483482
{
484-
int inputPassIndex = ctx.resources[input.resource].writePassId;
485-
m_HasSideEffectPassIdCullingStack.Push(inputPassIndex);
483+
ref var inputVersionedDataRes = ref ctx.resources[input.resource];
484+
485+
if (inputVersionedDataRes.written)
486+
{
487+
m_HasSideEffectPassIdCullingStack.Push(inputVersionedDataRes.writePassId);
488+
}
486489
}
487490

488491
// We need this node, don't cull it

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -265,14 +265,17 @@ public readonly ReadOnlySpan<PassOutputData> Outputs(CompilerContextData ctx)
265265
public readonly ReadOnlySpan<PassInputData> Inputs(CompilerContextData ctx)
266266
=> ctx.inputData.MakeReadOnlySpan(firstInput, numInputs);
267267

268+
// RenderAttachments - MRT colors and depth
268269
[MethodImpl(MethodImplOptions.AggressiveInlining)]
269270
public readonly ReadOnlySpan<PassFragmentData> Fragments(CompilerContextData ctx)
270271
=> ctx.fragmentData.MakeReadOnlySpan(firstFragment, numFragments);
271272

273+
// ShadingRateImageAttachment
272274
[MethodImpl(MethodImplOptions.AggressiveInlining)]
273275
public readonly PassFragmentData ShadingRateImage(CompilerContextData ctx)
274276
=> ctx.fragmentData[shadingRateImageIndex];
275277

278+
// RenderInputAttachments
276279
[MethodImpl(MethodImplOptions.AggressiveInlining)]
277280
public readonly ReadOnlySpan<PassFragmentData> FragmentInputs(CompilerContextData ctx)
278281
=> ctx.fragmentData.MakeReadOnlySpan(firstFragmentInput, numFragmentInputs);
@@ -913,19 +916,22 @@ public static PassBreakAudit CanMerge(CompilerContextData contextData, int activ
913916
foreach (ref readonly var input in passToMerge.Inputs(contextData))
914917
{
915918
var inputResource = input.resource;
916-
var writingPassId = contextData.resources[inputResource].writePassId;
917-
// Is the writing pass enclosed in the current native renderpass
918-
if (writingPassId >= nativePass.firstGraphPass && writingPassId < nativePass.lastGraphPass + 1)
919+
920+
ref readonly var inputDataVersioned = ref contextData.VersionedResourceData(inputResource);
921+
922+
bool isWrittenInCurrNativePass = inputDataVersioned.written && (inputDataVersioned.writePassId >= nativePass.firstGraphPass && inputDataVersioned.writePassId < nativePass.lastGraphPass + 1);
923+
924+
if (isWrittenInCurrNativePass)
919925
{
920-
// If it's not used as a fragment, it's used as some sort of texture read of load so we need so sync it out
926+
// If it's not used as a fragment, it's used as some sort of texture read or load so we need to break the current native render pass
927+
// as we can't sample and write to it in the same native render pass
921928
if (!passToMerge.IsUsedAsFragment(inputResource, contextData))
922929
{
923930
return new PassBreakAudit(PassBreakReason.NextPassReadsTexture, passIdToMerge);
924931
}
925932
}
926933
}
927934

928-
929935
// Gather which attachments to add to the current renderpass
930936
var attachmentsToTryAdding = new FixedAttachmentArray<PassFragmentData>();
931937

@@ -961,7 +967,7 @@ public static PassBreakAudit CanMerge(CompilerContextData contextData, int activ
961967
}
962968
}
963969

964-
// Check if this fragment is already sampled in the native renderpass not as a fragment but as an input
970+
// Check if this fragment is already sampled in the native renderpass as a standard texture
965971
for (int i = nativePass.firstGraphPass; i <= nativePass.lastGraphPass; ++i)
966972
{
967973
ref var earlierPassData = ref contextData.passData.ElementAt(i);
@@ -980,7 +986,6 @@ public static PassBreakAudit CanMerge(CompilerContextData contextData, int activ
980986
}
981987
}
982988

983-
984989
foreach (ref readonly var fragmentInput in passToMerge.FragmentInputs(contextData))
985990
{
986991
bool alreadyAttached = false;
@@ -1315,7 +1320,8 @@ void AddDepthAttachmentFirstDuringMerge(CompilerContextData contextData, in Pass
13151320
}
13161321
}
13171322

1318-
// We also need to update the shading rate image index (VRS)
1323+
// We also need to update the shading rate image SRI index (used for VRS)
1324+
// This is unlikely to happen because the SRI is always added last, after color and input attachments
13191325
if (hasShadingRateImage && shadingRateImageIndex == 0)
13201326
{
13211327
shadingRateImageIndex = prevDepthIdx;

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

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,13 @@ public TextureHandle SetRandomAccessAttachment(TextureHandle input, int index, A
493493
return input;
494494
}
495495

496+
public void SetShadingRateImageAttachment(in TextureHandle tex)
497+
{
498+
CheckNotUseFragment(tex);
499+
var versionedTextureHandle = new TextureHandle(UseResource(tex.handle, AccessFlags.Read));
500+
m_RenderPass.SetShadingRateImageRaw(versionedTextureHandle);
501+
}
502+
496503
public BufferHandle UseBufferRandomAccess(BufferHandle input, int index, AccessFlags flags = AccessFlags.Read)
497504
{
498505
var h = UseBuffer(input, flags);
@@ -588,17 +595,6 @@ void CheckFrameBufferFetchEmulationIsSupported(in TextureHandle tex)
588595
}
589596
}
590597

591-
public void SetShadingRateImageAttachment(in TextureHandle sriTextureHandle)
592-
{
593-
CheckNotUseFragment(sriTextureHandle);
594-
595-
// shading rate image access flag is always read, only 1 mip and 1 slice
596-
var newSriTextureHandle = new TextureHandle();
597-
newSriTextureHandle.handle = UseResource(sriTextureHandle.handle, AccessFlags.Read);
598-
599-
m_RenderPass.SetShadingRateImage(newSriTextureHandle, AccessFlags.Read, 0, 0);
600-
}
601-
602598
public void SetShadingRateFragmentSize(ShadingRateFragmentSize shadingRateFragmentSize)
603599
{
604600
m_RenderPass.SetShadingRateFragmentSize(shadingRateFragmentSize);

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,16 @@ public void ComputeHash(ref HashFNV1A32 generator, RenderGraphResourceRegistry r
553553
generator.Append(GetRenderFuncHash());
554554
}
555555

556+
public void SetShadingRateImageRaw(in TextureHandle shadingRateImage)
557+
{
558+
if (ShadingRateInfo.supportsPerImageTile)
559+
{
560+
hasShadingRateImage = true;
561+
// shading rate image access flag is always read, only 1 mip and 1 slice
562+
shadingRateAccess = new TextureAccess(shadingRateImage, AccessFlags.Read, 0, 0);
563+
}
564+
}
565+
556566
public void SetShadingRateImage(in TextureHandle shadingRateImage, AccessFlags accessFlags, int mipLevel, int depthSlice)
557567
{
558568
if (ShadingRateInfo.supportsPerImageTile)

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

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ TestRenderTargets ImportAndCreateRenderTargets(RenderGraph g)
4848
var extraDepthBufferHandle = RTHandles.Alloc(depthBuffer, "Extra Depth Buffer");
4949
var extraDepthBufferBottomLeftHandle = RTHandles.Alloc(depthBuffer, "Extra Depth Buffer Bottom Left");
5050
var extraTextureTopLeftHandle = RTHandles.Alloc(backBuffer, "ExtraTextureTopLeft");
51-
var extraTextureBottomLeftHandle = RTHandles.Alloc(backBuffer,"ExtraTextureBottomLeft");
51+
var extraTextureBottomLeftHandle = RTHandles.Alloc(backBuffer, "ExtraTextureBottomLeft");
5252

5353
ImportResourceParams importParams = new ImportResourceParams();
5454
importParams.textureUVOrigin = TextureUVOrigin.TopLeft;
@@ -1629,57 +1629,98 @@ public void UpdateSubpassAttachmentIndices_WhenDepthAttachmentIsAdded()
16291629
Assert.IsTrue(subPassDesc3.inputs[0] == 3);
16301630
}
16311631

1632-
/* //VRS bug. It seems that there is a bug with VRS forcing pass breaking between passes using the same shading rate image where it shouldn't: UUM-102113.
16331632
[Test]
1634-
public void UpdateShadingRateImageIndex_WhenDepthAttachmentIsAdded()
1633+
public void MergePasses_WhenSameShadingRateImage()
16351634
{
16361635
var g = AllocateRenderGraph();
16371636
var renderTargets = ImportAndCreateRenderTargets(g);
16381637

1639-
using (var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("NoDepth_Subpass0", out var passData))
1638+
using (var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("Pass0", out var passData))
16401639
{
1641-
builder.SetShadingRateImageAttachment(renderTargets.extraTextures[0]);
16421640
builder.SetRenderAttachment(renderTargets.extraTextures[1], 0);
1641+
builder.SetShadingRateImageAttachment(renderTargets.extraTextures[0]);
16431642
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
16441643
builder.AllowPassCulling(false);
16451644
}
16461645

1647-
// Render Pass
1648-
// attachments: [extraTextures[0], extraTextures[1]]
1649-
// shading rate image : [0]
1650-
// subpass 0: color outputs : [1]
1651-
1652-
using (var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("Depth_Subpass1", out var passData))
1646+
// Same attachments, we should merge in the same subpass as Pass0's one
1647+
using (var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("Pass1", out var passData))
16531648
{
1649+
builder.SetRenderAttachment(renderTargets.extraTextures[1], 0);
16541650
builder.SetShadingRateImageAttachment(renderTargets.extraTextures[0]);
1655-
builder.SetRenderAttachmentDepth(renderTargets.depthBuffer, AccessFlags.Write);
1656-
builder.SetRenderAttachment(renderTargets.extraTextures[2], 0);
16571651
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
16581652
builder.AllowPassCulling(false);
16591653
}
16601654

1661-
// Render Pass
1662-
// attachments: [depthBuffer, extraTextures[1], extraTextures[0], extraTextures[2]]
1663-
// shading rate image : [0 -> 2]
1664-
// subpass 0: color outputs : [1]
1665-
// subpass 1: color outputs : [3]
1655+
// Same shading rate image but different render attachments, we should stay in the same native render pass but in a different subpass
1656+
using (var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("Pass2", out var passData))
1657+
{
1658+
builder.SetRenderAttachment(renderTargets.extraTextures[2], 0);
1659+
builder.SetShadingRateImageAttachment(renderTargets.extraTextures[0]);
1660+
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
1661+
builder.AllowPassCulling(false);
1662+
}
16661663

16671664
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
16681665
var passes = result.contextData.GetNativePasses();
16691666

16701667
// All graph passes are merged in the same render pass
1671-
Assert.IsTrue(passes != null && passes.Count == 1 && passes[0].numGraphPasses == 2 && passes[0].numNativeSubPasses == 2);
1668+
Assert.IsTrue(passes != null && passes.Count == 1 && passes[0].numGraphPasses == 3 && passes[0].numNativeSubPasses == 2);
16721669

1673-
// Depth is the first attachment
1674-
Assert.IsTrue(passes[0].attachments[0].handle.index == renderTargets.depthBuffer.handle.index);
1675-
Assert.IsTrue(passes[0].attachments[1].handle.index == renderTargets.extraTextures[1].handle.index);
1676-
Assert.IsTrue(passes[0].attachments[2].handle.index == renderTargets.extraTextures[0].handle.index);
1677-
Assert.IsTrue(passes[0].attachments[3].handle.index == renderTargets.extraTextures[2].handle.index);
1670+
// If no SRI support, we just discard the API call
1671+
if (ShadingRateInfo.supportsPerImageTile)
1672+
{
1673+
var shadingRateImageAttachmentIndex = passes[0].shadingRateImageIndex;
1674+
Assert.IsTrue(shadingRateImageAttachmentIndex == 1); // always after color and depth attachments
1675+
Assert.IsTrue(passes[0].attachments[shadingRateImageAttachmentIndex].handle.index == renderTargets.extraTextures[0].handle.index);
1676+
}
1677+
}
16781678

1679-
// Check Shading Rate Image index is correctly updated
1680-
Assert.IsTrue(passes[0].shadingRateImageIndex == renderTargets.extraTextures[0].handle.index);
1679+
[Test]
1680+
public void BreakPasses_WhenNoOrDifferentShadingRateImage()
1681+
{
1682+
var g = AllocateRenderGraph();
1683+
var renderTargets = ImportAndCreateRenderTargets(g);
1684+
1685+
using (var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("Pass0", out var passData))
1686+
{
1687+
builder.SetRenderAttachment(renderTargets.extraTextures[1], 0);
1688+
builder.SetShadingRateImageAttachment(renderTargets.extraTextures[0]);
1689+
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
1690+
builder.AllowPassCulling(false);
1691+
}
1692+
1693+
// Different SRI, we should break into a new native render pass
1694+
using (var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("Pass1", out var passData))
1695+
{
1696+
builder.SetRenderAttachment(renderTargets.extraTextures[1], 0);
1697+
builder.SetShadingRateImageAttachment(renderTargets.extraTextures[2]);
1698+
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
1699+
builder.AllowPassCulling(false);
1700+
}
1701+
1702+
// No SRI, we should break into a new native render pass
1703+
using (var builder = g.AddRasterRenderPass<RenderGraphTestPassData>("Pass2", out var passData))
1704+
{
1705+
builder.SetRenderAttachment(renderTargets.extraTextures[1], 0);
1706+
builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { });
1707+
builder.AllowPassCulling(false);
1708+
}
1709+
1710+
var result = g.CompileNativeRenderGraph(g.ComputeGraphHash());
1711+
var passes = result.contextData.GetNativePasses();
1712+
1713+
if (ShadingRateInfo.supportsPerImageTile)
1714+
{
1715+
// All graph passes are in different native render passes
1716+
Assert.IsTrue(passes != null && passes.Count == 3);
1717+
}
1718+
else
1719+
{
1720+
// If no SRI support, we just discard the API call, all graph passes are merged together
1721+
Assert.IsTrue(passes != null && passes.Count == 1);
1722+
}
16811723
}
1682-
*/
16831724

16841725
/* // DepthAttachment bug: https://jira.unity3d.com/projects/SRP/issues/SRP-897
16851726
[Test]

0 commit comments

Comments
 (0)