From eefd1d2ae855505466a6e20789ff6f0aa30b2524 Mon Sep 17 00:00:00 2001 From: Gabriel de la Cruz Date: Thu, 27 Jun 2024 16:29:08 +0000 Subject: [PATCH] [2022.3][VFX] Split rendering batches for exposed meshes Non trivial backport of [this PR](https://github.cds.internal.unity3d.com/unity/unity/pull/43927) When we allowed using exposed objects with instancing, we missed one case, using a exposed mesh with mesh particles. The fix contains 2 parts: - Identifying which CPU expressions can split instancing batches. Currently just mesh and submeshMask, but easy to add more - Supporting indirect draw rendering with different meshes To test, just expose a mesh and plug it directly into the mesh slot of a particle mesh output. Then, create many instances and use different meshes for some instances. --- .../Editor/Compiler/VFXGraphCompiledData.cs | 2 + .../Editor/Data/VFXDataParticle.cs | 14 ++++--- .../Contexts/Implementations/VFXMeshOutput.cs | 22 +++++++++++ .../Editor/Models/Contexts/VFXContext.cs | 16 ++++++++ .../Models/Contexts/VFXMultiMeshHelper.cs | 38 ++++++++++++++++--- .../ProjectSettings/EditorBuildSettings.asset | 2 +- .../ProjectSettings/EditorBuildSettings.asset | 2 +- 7 files changed, 82 insertions(+), 14 deletions(-) diff --git a/Packages/com.unity.visualeffectgraph/Editor/Compiler/VFXGraphCompiledData.cs b/Packages/com.unity.visualeffectgraph/Editor/Compiler/VFXGraphCompiledData.cs index 7bec3c9748e..f94ec03a486 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Compiler/VFXGraphCompiledData.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/Compiler/VFXGraphCompiledData.cs @@ -22,6 +22,7 @@ struct VFXContextCompiledData public VFXExpressionMapper gpuMapper; public VFXUniformMapper uniformMapper; public VFXSGInputs SGInputs; + public List instancingSplitValues; public ReadOnlyDictionary graphicsBufferUsage; public VFXMapping[] parameters; public (VFXSlot slot, VFXData data)[] linkedEventOut; @@ -1102,6 +1103,7 @@ public void Compile(VFXCompilationMode compilationMode, bool forceShaderValidati contextData.cpuMapper = cpuMapper; contextData.parameters = context.additionalMappings.ToArray(); contextData.linkedEventOut = ComputeEventListFromSlot(context.allLinkedOutputSlot).ToArray(); + contextData.instancingSplitValues = context.CreateInstancingSplitValues(m_ExpressionGraph); contextToCompiledData[context] = contextData; } diff --git a/Packages/com.unity.visualeffectgraph/Editor/Data/VFXDataParticle.cs b/Packages/com.unity.visualeffectgraph/Editor/Data/VFXDataParticle.cs index 9d0ebfa3080..b7512d6cc94 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Data/VFXDataParticle.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/Data/VFXDataParticle.cs @@ -962,7 +962,6 @@ public override void FillDescs( for (int i = 0; i < m_Contexts.Count; ++i) { var temporaryBufferMappings = new List(); - var instanceSplitDescValues = new List(); var context = m_Contexts[i]; if (!contextToCompiledData.TryGetValue(context, out var contextData)) @@ -1113,6 +1112,7 @@ public override void FillDescs( bufferMappings.Add(new VFXMapping($"eventListOut_{prefix}", gpuTarget)); } + var instancingSplitDescValues = contextData.instancingSplitValues; uniformMappings.Clear(); foreach (var buffer in contextData.uniformMapper.buffers) @@ -1120,7 +1120,7 @@ public override void FillDescs( int index = expressionGraph.GetFlattenedIndex(buffer); if (!buffer.IsAny(VFXExpression.Flags.Constant | VFXExpression.Flags.Foldable)) { - instanceSplitDescValues.Add((uint)index); + instancingSplitDescValues.Add((uint)index); } var name = contextData.uniformMapper.GetName(buffer); uniformMappings.Add(new VFXMapping(name, index)); @@ -1130,7 +1130,7 @@ public override void FillDescs( int index = expressionGraph.GetFlattenedIndex(texture); if (!texture.IsAny(VFXExpression.Flags.Constant | VFXExpression.Flags.Foldable)) { - instanceSplitDescValues.Add((uint)index); + instancingSplitDescValues.Add((uint)index); } // TODO At the moment issue all names sharing the same texture as different texture slots. This is not optimized as it required more texture binding than necessary foreach (var name in contextData.uniformMapper.GetNames(texture)) @@ -1155,17 +1155,19 @@ public override void FillDescs( taskDesc.values = uniformMappings.OrderBy(mapping => mapping.index).ToArray(); taskDesc.parameters = cpuMappings.Concat(contextData.parameters).Concat(additionalParameters).ToArray(); taskDesc.shaderSourceIndex = contextToCompiledData[context].indexInShaderSource; - taskDesc.instanceSplitIndex = AddInstanceSplitDesc(instanceSplitDescs, instanceSplitDescValues); + taskDesc.instanceSplitIndex = AddInstanceSplitDesc(instanceSplitDescs, instancingSplitDescValues); taskDesc.model = context; - if (context is IVFXMultiMeshOutput) // If the context is a multi mesh output, split and patch task desc into several tasks + if (context is IVFXMultiMeshOutput multiMeshOutput && multiMeshOutput.meshCount > 0) // If the context is a multi mesh output, split and patch task desc into several tasks { - var multiMeshOutput = (IVFXMultiMeshOutput)context; for (int j = (int)multiMeshOutput.meshCount - 1; j >= 0; --j) // Back to front to be consistent with LOD and alpha { VFXEditorTaskDesc singleMeshTaskDesc = taskDesc; singleMeshTaskDesc.parameters = VFXMultiMeshHelper.PatchCPUMapping(taskDesc.parameters, multiMeshOutput.meshCount, j).ToArray(); singleMeshTaskDesc.buffers = VFXMultiMeshHelper.PatchBufferMapping(taskDesc.buffers, j).ToArray(); + var instancingSplitDescValuesMesh = new List(instancingSplitDescValues); + VFXMultiMeshHelper.PatchInstancingSplitValues(instancingSplitDescValuesMesh, expressionGraph, context.inputSlots, multiMeshOutput.meshCount, j); + singleMeshTaskDesc.instanceSplitIndex = AddInstanceSplitDesc(instanceSplitDescs, instancingSplitDescValuesMesh); taskDescs.Add(singleMeshTaskDesc); } } diff --git a/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/Implementations/VFXMeshOutput.cs b/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/Implementations/VFXMeshOutput.cs index 9654a8c9adb..4d0ab9f9c97 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/Implementations/VFXMeshOutput.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/Implementations/VFXMeshOutput.cs @@ -158,5 +158,27 @@ internal override void GenerateErrors(VFXInvalidateErrorReporter manager) $" so the resulted computed bounds can be too small or big" + $" Please use padding to mitigate this discrepancy."); } + + public override IEnumerable instancingSplitCPUExpressions + { + get + { + foreach (var exp in base.instancingSplitCPUExpressions) + yield return exp; + + // Only single mesh, multi-mesh will be patched later + if (meshCount == 1) + { + foreach (var name in VFXMultiMeshHelper.GetCPUExpressionNames(1)) + { + var exp = inputSlots.First(s => s.name == name).GetExpression(); + if (exp != null && !exp.IsAny(VFXExpression.Flags.Constant)) + yield return exp; + } + } + } + } + + } } diff --git a/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/VFXContext.cs b/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/VFXContext.cs index b132119e237..1562d80d7cc 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/VFXContext.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/VFXContext.cs @@ -685,5 +685,21 @@ public bool CanTransferSetting(VFXSetting setting) { return CanTransferSetting(setting.field.Name); } + + public List CreateInstancingSplitValues(VFXExpressionGraph expressionGraph) + { + List instancingSplitValues = new List(); + foreach (var exp in instancingSplitCPUExpressions) + { + int index = expressionGraph.GetFlattenedIndex(exp); + if (index >= 0) + { + instancingSplitValues.Add((uint)index); + } + } + return instancingSplitValues; + } + + public virtual IEnumerable instancingSplitCPUExpressions { get { return Enumerable.Empty(); } } } } diff --git a/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/VFXMultiMeshHelper.cs b/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/VFXMultiMeshHelper.cs index ebc30c06114..545879adb3c 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/VFXMultiMeshHelper.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/VFXMultiMeshHelper.cs @@ -1,9 +1,8 @@ using System.Linq; using System.Collections.Generic; +using System.Collections.ObjectModel; using UnityEngine; -using System; - namespace UnityEditor.VFX { interface IVFXMultiMeshOutput @@ -49,13 +48,21 @@ public static IEnumerable GetCPUExpressionNames(uint meshCount) { for (int i = 0; i < meshCount; ++i) { - string id = GetId(meshCount, i); - - yield return meshName + id; - yield return maskName + id; + foreach (var name in GetCPUExpressionNames(meshCount, i)) + { + yield return name; + } } } + public static IEnumerable GetCPUExpressionNames(uint meshCount, int meshIndex) + { + string id = GetId(meshCount, meshIndex); + + yield return meshName + id; + yield return maskName + id; + } + public static IEnumerable PatchCPUMapping(IEnumerable mappings, uint meshCount, int index) { string id = GetId(meshCount, index); @@ -88,5 +95,24 @@ public static IEnumerable PatchBufferMapping(IEnumerable yield return m; } } + + public static void PatchInstancingSplitValues(List instancingSplitDescValues, VFXExpressionGraph expressionGraph, ReadOnlyCollection inputSlots, uint meshCount, int meshIndex) + { + foreach (var name in GetCPUExpressionNames(meshCount, meshIndex)) + { + var exp = inputSlots.First(s => s.name == name).GetExpression(); + if (exp != null && !exp.IsAny(VFXExpression.Flags.Constant)) + { + // Obtain reduced version of the expression + exp = expressionGraph.CPUExpressionsToReduced.First(kvp => kvp.Key == exp).Value; + + int index = expressionGraph.GetFlattenedIndex(exp); + if (index >= 0) + { + instancingSplitDescValues.Add((uint)index); + } + } + } + } } } diff --git a/Tests/SRPTests/Projects/VisualEffectGraph_HDRP/ProjectSettings/EditorBuildSettings.asset b/Tests/SRPTests/Projects/VisualEffectGraph_HDRP/ProjectSettings/EditorBuildSettings.asset index d9535e6ee2a..8c2465e1884 100644 --- a/Tests/SRPTests/Projects/VisualEffectGraph_HDRP/ProjectSettings/EditorBuildSettings.asset +++ b/Tests/SRPTests/Projects/VisualEffectGraph_HDRP/ProjectSettings/EditorBuildSettings.asset @@ -26,7 +26,7 @@ EditorBuildSettings: - enabled: 1 path: Packages/com.unity.testing.visualeffectgraph/Scenes/006_StripAttributes.unity guid: 6cf5bcccabd797d4db3c44a0d4ac7389 - - enabled: 1 + - enabled: 0 path: Packages/com.unity.testing.visualeffectgraph/Scenes/007_MeshSampling.unity guid: b20a840a0adf929419e19f13139e8fa3 - enabled: 1 diff --git a/Tests/SRPTests/Projects/VisualEffectGraph_URP/ProjectSettings/EditorBuildSettings.asset b/Tests/SRPTests/Projects/VisualEffectGraph_URP/ProjectSettings/EditorBuildSettings.asset index 849dde26adf..0a360f3f215 100644 --- a/Tests/SRPTests/Projects/VisualEffectGraph_URP/ProjectSettings/EditorBuildSettings.asset +++ b/Tests/SRPTests/Projects/VisualEffectGraph_URP/ProjectSettings/EditorBuildSettings.asset @@ -26,7 +26,7 @@ EditorBuildSettings: - enabled: 1 path: Packages/com.unity.testing.visualeffectgraph/Scenes/006_StripAttributes.unity guid: 6cf5bcccabd797d4db3c44a0d4ac7389 - - enabled: 1 + - enabled: 0 path: Packages/com.unity.testing.visualeffectgraph/Scenes/007_MeshSampling.unity guid: b20a840a0adf929419e19f13139e8fa3 - enabled: 1