diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/NativePassCompiler.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/NativePassCompiler.cs index e1e47dc91ac..fde33ff20da 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/NativePassCompiler.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/NativePassCompiler.cs @@ -365,6 +365,19 @@ void CullUnusedRenderPasses() // Remove the connections from the list so they won't be visited again if (pass.culled) { + // If the culled pass was supposed to generate the latest version of a given resource, + // we need to decrement the latestVersionNumber of this resource + // because its last version will never be created due to its producer being culled + foreach (ref readonly var output in pass.Outputs(ctx)) + { + var outputResource = output.resource; + bool isOutputLastVersion = (outputResource.version == ctx.UnversionedResourceData(outputResource).latestVersionNumber); + + if (isOutputLastVersion) + ctx.UnversionedResourceData(outputResource).latestVersionNumber--; + } + + // Notifying the versioned resources that this pass is no longer reading them foreach (ref readonly var input in pass.Inputs(ctx)) { var inputResource = input.resource; @@ -567,10 +580,14 @@ void FindResourceUsageRanges() if (pass.waitOnGraphicsFencePassId == -1) { ref var pointToVer = ref ctx.VersionedResourceData(inputResource); - ref var wPass = ref ctx.passData.ElementAt(pointToVer.writePassId); - if (wPass.asyncCompute != pass.asyncCompute) + // If no RG pass writes to the resource, no need to wait for anyone + if (pointToVer.written) { - pass.waitOnGraphicsFencePassId = wPass.passId; + ref var wPass = ref ctx.passData.ElementAt(pointToVer.writePassId); + if (wPass.asyncCompute != pass.asyncCompute) + { + pass.waitOnGraphicsFencePassId = wPass.passId; + } } } } @@ -1247,6 +1264,11 @@ private void ExecuteDestroyResource(InternalRenderGraphContext rgContext, Render { using (new ProfilingScope(ProfilingSampler.Get(NativeCompilerProfileId.NRPRGComp_ExecuteDestroyResources))) { + // Unsafe pass might use temporary render targets, + // users can also use temporary data in their render graph execute nodes using public RenderGraphObjectPool API + // In both cases, we need to release these resources after the node execution + rgContext.renderGraphPool.ReleaseAllTempAlloc(); + if (pass.type == RenderGraphPassType.Raster && pass.nativePassIndex >= 0) { // For raster passes we need to destroy resources after all the subpasses at the end of the native renderpass @@ -1280,7 +1302,7 @@ private void ExecuteDestroyResource(InternalRenderGraphContext rgContext, Render } } - internal unsafe void SetRandomWriteTarget(in CommandBuffer cmd, RenderGraphResourceRegistry resources, int index, ResourceHandle resource, bool preserveCounterValue = true) + internal unsafe void ExecuteSetRandomWriteTarget(in CommandBuffer cmd, RenderGraphResourceRegistry resources, int index, ResourceHandle resource, bool preserveCounterValue = true) { if (resource.type == RenderGraphResourceType.Texture) { @@ -1387,7 +1409,7 @@ public void ExecuteGraph(InternalRenderGraphContext rgContext, RenderGraphResour } var nrpBegan = false; - if (isRaster == true && pass.mergeState <= PassMergeState.Begin) + if (isRaster && pass.mergeState <= PassMergeState.Begin) { if (pass.nativePassIndex >= 0) { @@ -1417,7 +1439,7 @@ public void ExecuteGraph(InternalRenderGraphContext rgContext, RenderGraphResour { foreach (var randomWriteAttachment in pass.RandomWriteTextures(contextData)) { - SetRandomWriteTarget(rgContext.cmd, resources, randomWriteAttachment.index, randomWriteAttachment.resource); + ExecuteSetRandomWriteTarget(rgContext.cmd, resources, randomWriteAttachment.index, randomWriteAttachment.resource); } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/ResourcesData.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/ResourcesData.cs index 6b242addb0c..c1330b14715 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/ResourcesData.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/ResourcesData.cs @@ -30,7 +30,7 @@ internal struct ResourceUnversionedData public readonly int volumeDepth; public readonly int msaaSamples; - public readonly int latestVersionNumber; + public int latestVersionNumber; // mostly readonly, can be decremented only if all passes using the last version are culled public readonly bool clear; // graph.m_Resources.GetTextureResourceDesc(fragment.resource).clearBuffer; public readonly bool discard; // graph.m_Resources.GetTextureResourceDesc(fragment.resource).discardBuffer; diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphObjectPool.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphObjectPool.cs index 2f97dbe7db7..13146be82f2 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphObjectPool.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphObjectPool.cs @@ -110,7 +110,12 @@ internal void ReleaseAllTempAlloc() m_AllocatedMaterialPropertyBlocks.Clear(); } - + + internal bool IsEmpty() + { + return m_AllocatedArrays.Count == 0 && m_AllocatedMaterialPropertyBlocks.Count == 0; + } + // Regular pooling API. Only internal use for now internal T Get() where T : class, new() { diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourcePool.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourcePool.cs index 9e916c8ddbe..bae48e7d305 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourcePool.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourcePool.cs @@ -20,7 +20,7 @@ abstract class RenderGraphResourcePool : IRenderGraphResourcePool where Ty protected Dictionary> m_ResourcePool = new Dictionary>(); // This list allows us to determine if all resources were correctly released in the frame when validity checks are enabled. - // This is useful to warn in case of user error or avoid leaks when a render graph execution errors occurs for example. + // This is useful to warn in case of user error or avoid leaks when a render graph execution error occurs for example. List<(int, Type)> m_FrameAllocatedResources = new List<(int, Type)>(); const int kStaleResourceLifetime = 10; diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/NativePassCompilerRenderGraphTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Editor/NativePassCompilerRenderGraphTests.cs index c7d4938353d..e40a401b225 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/NativePassCompilerRenderGraphTests.cs +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/NativePassCompilerRenderGraphTests.cs @@ -1317,5 +1317,37 @@ public void UpdateSubpassAttachmentIndices_WhenDepthAttachmentIsAdded() Assert.IsTrue(subPassDesc3.inputs.Length == 1); Assert.IsTrue(subPassDesc3.inputs[0] == 3); } + + [Test] + public void DecreaseResourceVersionIfLastPassIsCulled() + { + var g = AllocateRenderGraph(); + var buffers = ImportAndCreateBuffers(g); + + // Bumping version of extraBuffer within RG to 1 as we write to it in first pass + using (var builder = g.AddRasterRenderPass("TestPass0", out var passData)) + { + builder.SetRenderAttachment(buffers.extraBuffers[0], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + // Bumping version of extraBuffer within RG to 2 as we write to it in second pass + using (var builder = g.AddRasterRenderPass("TestPass1", out var passData)) + { + builder.SetRenderAttachment(buffers.extraBuffers[0], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(true); + } + + // First pass is preserved as requested but second pass is culled + var result = g.CompileNativeRenderGraph(g.ComputeGraphHash()); + var passes = result.contextData.GetNativePasses(); + + // Second pass has been culled + Assert.IsTrue(passes != null && passes.Count == 1 && passes[0].numGraphPasses == 1); + // extraBuffer version has decreased to 1 as it is only used by the first pass + Assert.AreEqual(passes[0].attachments[0].handle.version, 1); + } } } diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/RenderGraphTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Editor/RenderGraphTests.cs index d57ba826a75..85c8bc322bc 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/RenderGraphTests.cs +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/RenderGraphTests.cs @@ -1469,5 +1469,48 @@ public void Cleanup_RenderAgain_AfterCallingCleanup() // Ensure that the Render Graph data structures can be reinitialized at runtime, even native ones Assert.DoesNotThrow(() => m_Camera.Render()); } + + class TempAllocTestData + { + public TextureHandle whiteTexture; + } + + [Test] + public void GetTempMaterialPropertyBlockAreReleasedAfterRenderGraphNodeExecution() + { + m_RenderGraphTestPipeline.recordRenderGraphBody = (context, camera, cmd) => + { + using (var builder = m_RenderGraph.AddUnsafePass("MPBPass", out var passData)) + { + builder.AllowPassCulling(false); + passData.whiteTexture = m_RenderGraph.defaultResources.whiteTexture; + + builder.SetRenderFunc((TempAllocTestData data, UnsafeGraphContext context) => + { + // no temp alloc yet + Assert.IsTrue(context.renderGraphPool.IsEmpty()); + + var mpb = context.renderGraphPool.GetTempMaterialPropertyBlock(); + mpb.SetTexture(k_DefaultWhiteTextureID, data.whiteTexture); + + // memory temporarily allocated + Assert.IsFalse(context.renderGraphPool.IsEmpty()); + }); + } + + using (var builder = m_RenderGraph.AddUnsafePass("PostPass", out var passData)) + { + builder.AllowPassCulling(false); + + builder.SetRenderFunc((TempAllocTestData data, UnsafeGraphContext context) => + { + // memory has been deallocated at the end of the previous RG node, no leak + Assert.IsTrue(context.renderGraphPool.IsEmpty()); + }); + } + }; + + m_Camera.Render(); + } } } \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/Threading/FunctionTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Runtime/Threading/FunctionTests.cs index d3a193ec809..302bd474a9b 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/Threading/FunctionTests.cs +++ b/Packages/com.unity.render-pipelines.core/Tests/Runtime/Threading/FunctionTests.cs @@ -171,7 +171,6 @@ private void ValidateDeviceComputeGroupSize(int requiredComputeGroupSizeX) } [Test] - [Ignore("Unstable: https://jira.unity3d.com/browse/UUM-111743")] public void WaveTest([Values]Kernel kernel, [Values]WaveSizeKeyword waveSizeKeyword) { int groupSize = (int)GroupSizeKeyword.GROUP_SIZE_128; @@ -210,7 +209,6 @@ public void WaveTest([Values]Kernel kernel, [Values]WaveSizeKeyword waveSizeKeyw } [Test] - [Ignore("Unstable: https://jira.unity3d.com/browse/UUM-111749")] public void GroupTest([Values]Kernel kernel, [Values]GroupSizeKeyword groupSizeKeyword) { int groupSize = (int)groupSizeKeyword; diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/Tools/ColorCheckerToolEditor.cs b/Packages/com.unity.render-pipelines.high-definition/Editor/Tools/ColorCheckerToolEditor.cs index 14e4a5a8efc..befc21fada5 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/Tools/ColorCheckerToolEditor.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/Tools/ColorCheckerToolEditor.cs @@ -72,7 +72,8 @@ public override VisualElement CreateInspectorGUI() //Prepare metallic toggles for material mode for(int i=0; i<12;i++) { - Toggle metallicToggle = new Toggle() { name ="metallic" + i, label = "is Metallic", tabIndex = i}; + Toggle metallicToggle = new Toggle() { name ="metallic" + i, label = "Metallic", tabIndex = i}; + metallicToggle.Q