Skip to content

Commit

Permalink
[2022.3][VFX] Split rendering batches for exposed meshes
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
gabrieldelacruz authored and Evergreen committed Jun 27, 2024
1 parent 63913f6 commit eefd1d2
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct VFXContextCompiledData
public VFXExpressionMapper gpuMapper;
public VFXUniformMapper uniformMapper;
public VFXSGInputs SGInputs;
public List<uint> instancingSplitValues;
public ReadOnlyDictionary<VFXExpression, Type> graphicsBufferUsage;
public VFXMapping[] parameters;
public (VFXSlot slot, VFXData data)[] linkedEventOut;
Expand Down Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,6 @@ public override void FillDescs(
for (int i = 0; i < m_Contexts.Count; ++i)
{
var temporaryBufferMappings = new List<VFXMappingTemporary>();
var instanceSplitDescValues = new List<uint>();

var context = m_Contexts[i];
if (!contextToCompiledData.TryGetValue(context, out var contextData))
Expand Down Expand Up @@ -1113,14 +1112,15 @@ public override void FillDescs(
bufferMappings.Add(new VFXMapping($"eventListOut_{prefix}", gpuTarget));
}

var instancingSplitDescValues = contextData.instancingSplitValues;
uniformMappings.Clear();

foreach (var buffer in contextData.uniformMapper.buffers)
{
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));
Expand All @@ -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))
Expand All @@ -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<uint>(instancingSplitDescValues);
VFXMultiMeshHelper.PatchInstancingSplitValues(instancingSplitDescValuesMesh, expressionGraph, context.inputSlots, multiMeshOutput.meshCount, j);
singleMeshTaskDesc.instanceSplitIndex = AddInstanceSplitDesc(instanceSplitDescs, instancingSplitDescValuesMesh);
taskDescs.Add(singleMeshTaskDesc);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<VFXExpression> 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;
}
}
}
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -685,5 +685,21 @@ public bool CanTransferSetting(VFXSetting setting)
{
return CanTransferSetting(setting.field.Name);
}

public List<uint> CreateInstancingSplitValues(VFXExpressionGraph expressionGraph)
{
List<uint> instancingSplitValues = new List<uint>();
foreach (var exp in instancingSplitCPUExpressions)
{
int index = expressionGraph.GetFlattenedIndex(exp);
if (index >= 0)
{
instancingSplitValues.Add((uint)index);
}
}
return instancingSplitValues;
}

public virtual IEnumerable<VFXExpression> instancingSplitCPUExpressions { get { return Enumerable.Empty<VFXExpression>(); } }
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using UnityEngine;

using System;

namespace UnityEditor.VFX
{
interface IVFXMultiMeshOutput
Expand Down Expand Up @@ -49,13 +48,21 @@ public static IEnumerable<string> 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<string> GetCPUExpressionNames(uint meshCount, int meshIndex)
{
string id = GetId(meshCount, meshIndex);

yield return meshName + id;
yield return maskName + id;
}

public static IEnumerable<VFXMapping> PatchCPUMapping(IEnumerable<VFXMapping> mappings, uint meshCount, int index)
{
string id = GetId(meshCount, index);
Expand Down Expand Up @@ -88,5 +95,24 @@ public static IEnumerable<VFXMapping> PatchBufferMapping(IEnumerable<VFXMapping>
yield return m;
}
}

public static void PatchInstancingSplitValues(List<uint> instancingSplitDescValues, VFXExpressionGraph expressionGraph, ReadOnlyCollection<VFXSlot> 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);
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit eefd1d2

Please sign in to comment.