Rasagar/Library/PackageCache/com.unity.visualeffectgraph/Editor/Expressions/VFXExpressionContext.cs
2024-08-26 23:07:20 +03:00

444 lines
21 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Profiling;
using UnityEngine.VFX;
namespace UnityEditor.VFX
{
[Flags]
enum VFXExpressionContextOption
{
None = 0,
Reduction = 1 << 0,
CPUEvaluation = 1 << 1,
ConstantFolding = 1 << 2,
GPUDataTransformation = 1 << 3,
PatchReadToEventAttribute = 1 << 4,
CollectPerContextData = 1 << 5
}
abstract partial class VFXExpression
{
public class Context
{
private bool Has(VFXExpressionContextOption options)
{
return (m_ReductionOptions & options) == options;
}
private bool HasAny(VFXExpressionContextOption options)
{
return (m_ReductionOptions & options) != 0;
}
public Context(VFXExpressionContextOption reductionOption, List<VFXLayoutElementDesc> globalEventAttributes = null)
{
m_ReductionOptions = reductionOption;
m_GlobalEventAttribute = globalEventAttributes;
if (Has(VFXExpressionContextOption.CPUEvaluation) && Has(VFXExpressionContextOption.GPUDataTransformation))
throw new ArgumentException("Invalid reduction options");
}
public void RegisterExpression(VFXExpression expression, VFXContext sourceContext = null)
{
if (!m_EndExpressions.TryGetValue(expression, out var contexts))
{
contexts = new();
m_EndExpressions.Add(expression, contexts);
}
if (sourceContext != null)
{
if (!contexts.Add(sourceContext))
throw new InvalidOperationException("Trying to add twice the same context for the same expression.");
}
}
public void UnregisterExpression(VFXExpression expression)
{
Invalidate(expression);
m_EndExpressions.Remove(expression);
}
class CollectedData
{
public readonly HashSet<VFXExpression> processedExpressions = new();
public readonly HashSet<VFXExpression> markedExpressions = new();
public readonly Dictionary<IHLSLCodeHolder, HashSet<VFXExpression>> childrenExpressionHLSLCodeHolder = new();
public readonly Dictionary<VFXExpressionBufferWithType, HashSet<VFXExpression>> childrenExpressionBufferWithType = new();
}
private void CollectPerContextDataRecursive(VFXExpression node, Stack<VFXExpression> currentChildren, CollectedData data)
{
if (data.processedExpressions.Contains(node))
{
if (data.markedExpressions.Contains(node))
{
foreach (var hlslCodeHolderCollection in data.childrenExpressionHLSLCodeHolder)
{
if (hlslCodeHolderCollection.Value.Contains(node))
{
foreach (var child in currentChildren)
{
data.markedExpressions.Add(child);
hlslCodeHolderCollection.Value.Add(child);
}
}
}
foreach (var expressionBufferWithTypeCollection in data.childrenExpressionBufferWithType)
{
if (expressionBufferWithTypeCollection.Value.Contains(node))
{
foreach (var child in currentChildren)
{
data.markedExpressions.Add(child);
expressionBufferWithTypeCollection.Value.Add(child);
}
}
}
}
return;
}
currentChildren.Push(node);
if (node is IHLSLCodeHolder hlslCodeHolder)
{
if (!data.childrenExpressionHLSLCodeHolder.TryGetValue(hlslCodeHolder, out var childCollection))
{
childCollection = new();
data.childrenExpressionHLSLCodeHolder.Add(hlslCodeHolder, childCollection);
}
foreach (var child in currentChildren)
{
data.markedExpressions.Add(child);
childCollection.Add(child);
}
}
if (node is VFXExpressionBufferWithType expressionWithType)
{
if (!data.childrenExpressionBufferWithType.TryGetValue(expressionWithType, out var childCollection))
{
childCollection = new();
data.childrenExpressionBufferWithType.Add(expressionWithType, childCollection);
}
foreach (var child in currentChildren)
{
data.markedExpressions.Add(child);
childCollection.Add(child);
}
}
foreach (var parent in node.parents)
CollectPerContextDataRecursive(parent, currentChildren, data);
data.processedExpressions.Add(node);
currentChildren.Pop();
}
private void CollectPerContextData()
{
var collectedDataCache = new CollectedData();
var childrenStackCache = new Stack<VFXExpression>();
foreach (var exp in m_EndExpressions)
{
if (childrenStackCache.Count > 0)
throw new InvalidOperationException("Unexpected Children Stack after dependency collection.");
CollectPerContextDataRecursive(exp.Key, childrenStackCache, collectedDataCache);
if (collectedDataCache.markedExpressions.Contains(exp.Key))
{
foreach (var hlslCodeHolderCollection in collectedDataCache.childrenExpressionHLSLCodeHolder)
{
if (hlslCodeHolderCollection.Value.Contains(exp.Key))
{
foreach (var context in exp.Value)
{
if (!m_HLSLCollectionPerContext.TryGetValue(context, out var codeHolders))
{
codeHolders = new();
m_HLSLCollectionPerContext.Add(context, codeHolders);
}
codeHolders.Add(hlslCodeHolderCollection.Key);
}
}
}
foreach (var expressionBufferWithTypeCollection in collectedDataCache.childrenExpressionBufferWithType)
{
if (expressionBufferWithTypeCollection.Value.Contains(exp.Key))
{
foreach (var context in exp.Value)
{
if (!m_GraphicsBufferTypeUsagePerContext.TryGetValue(context, out var usages))
{
usages = new();
m_GraphicsBufferTypeUsagePerContext.Add(context, usages);
}
var usage = expressionBufferWithTypeCollection.Key.usage;
var buffer = expressionBufferWithTypeCollection.Key.parents[0];
if (!usages.TryAdd(buffer, usage) && usages[buffer] != usage)
{
throw new InvalidOperationException($"Diverging type usage for GraphicsBuffer : {buffer}, {usage}");
}
}
}
}
}
}
}
static readonly ProfilerMarker s_CollectPerContextData = new ProfilerMarker("VFXEditor.CollectPerContextData");
static readonly ProfilerMarker s_CompileExpressionContext = new ProfilerMarker("VFXEditor.CompileExpressionContext");
public void Compile()
{
if (Has(VFXExpressionContextOption.CollectPerContextData))
{
using (s_CollectPerContextData.Auto())
{
CollectPerContextData();
}
}
using (s_CompileExpressionContext.Auto())
{
bool needToPatch = HasAny(VFXExpressionContextOption.GPUDataTransformation | VFXExpressionContextOption.PatchReadToEventAttribute);
var gpuTransformation = needToPatch && Has(VFXExpressionContextOption.GPUDataTransformation);
var spawnEventPath = needToPatch && Has(VFXExpressionContextOption.PatchReadToEventAttribute);
foreach (var exp in m_EndExpressions)
{
Compile(exp.Key);
if (needToPatch)
m_ReducedCache[exp.Key] = PatchVFXExpression(GetReduced(exp.Key), null /* no source in end expression */, gpuTransformation, spawnEventPath, m_GlobalEventAttribute);
}
}
}
public void Recompile()
{
Invalidate();
Compile();
}
private bool ShouldEvaluate(VFXExpression exp, VFXExpression[] reducedParents)
{
if (!HasAny(VFXExpressionContextOption.Reduction | VFXExpressionContextOption.CPUEvaluation | VFXExpressionContextOption.ConstantFolding))
return false;
if (exp.IsAny(Flags.NotCompilableOnCPU))
return false;
if (!Has(VFXExpressionContextOption.CPUEvaluation) && exp.IsAny(Flags.InvalidConstant))
return false;
if (!exp.Is(Flags.Value) && reducedParents.Length == 0) // not a value
return false;
Flags flag = Flags.Value;
if (!Has(VFXExpressionContextOption.CPUEvaluation))
flag |= Has(VFXExpressionContextOption.ConstantFolding) ? Flags.Foldable : Flags.Constant;
if (exp.Is(Flags.Value) && ((exp.m_Flags & (flag | Flags.InvalidOnCPU)) != flag))
return false;
foreach (var parent in reducedParents)
{
if ((parent.m_Flags & (flag | Flags.InvalidOnCPU)) != flag)
return false;
}
return true;
}
private VFXExpression PatchVFXExpression(VFXExpression input, VFXExpression targetExpression, bool insertGPUTransformation, bool patchReadAttributeForSpawn, IEnumerable<VFXLayoutElementDesc> globalEventAttribute)
{
if (insertGPUTransformation)
{
switch (input.valueType)
{
case VFXValueType.ColorGradient:
input = new VFXExpressionBakeGradient(input);
break;
case VFXValueType.Curve:
input = new VFXExpressionBakeCurve(input);
break;
case VFXValueType.Mesh:
case VFXValueType.SkinnedMeshRenderer:
if (targetExpression != null)
{
if (input.valueType == VFXValueType.Mesh)
{
switch (targetExpression.operation)
{
case VFXExpressionOperation.SampleMeshVertexFloat:
case VFXExpressionOperation.SampleMeshVertexFloat2:
case VFXExpressionOperation.SampleMeshVertexFloat3:
case VFXExpressionOperation.SampleMeshVertexFloat4:
case VFXExpressionOperation.SampleMeshVertexColor:
var channelFormatAndDimensionAndStream = targetExpression.parents[2];
channelFormatAndDimensionAndStream = Compile(channelFormatAndDimensionAndStream);
if (!(channelFormatAndDimensionAndStream is VFXExpressionMeshChannelInfos))
throw new InvalidOperationException("Unexpected type of expression in mesh sampling : " + channelFormatAndDimensionAndStream);
input = new VFXExpressionVertexBufferFromMesh(input, channelFormatAndDimensionAndStream);
break;
case VFXExpressionOperation.SampleMeshIndex:
input = new VFXExpressionIndexBufferFromMesh(input);
break;
default:
throw new InvalidOperationException("Unexpected source operation for InsertGPUTransformation : " + targetExpression.operation);
}
}
else //VFXValueType.SkinnedMeshRenderer
{
if (targetExpression is IVFXExpressionSampleSkinnedMesh skinnedMeshExpression)
{
var channelFormatAndDimensionAndStream = targetExpression.parents[2];
channelFormatAndDimensionAndStream = Compile(channelFormatAndDimensionAndStream);
if (!(channelFormatAndDimensionAndStream is VFXExpressionMeshChannelInfos))
throw new InvalidOperationException("Unexpected type of expression in skinned mesh sampling : " + channelFormatAndDimensionAndStream);
input = new VFXExpressionVertexBufferFromSkinnedMeshRenderer(input, channelFormatAndDimensionAndStream, skinnedMeshExpression.frame);
}
else
{
throw new InvalidOperationException("Unexpected source operation for InsertGPUTransformation : " + targetExpression);
}
}
} //else sourceExpression is null, we can't determine usage but it's possible if value is declared but not used.
break;
default:
//Nothing to patch on this type
break;
}
}
if (input.valueType == VFXValueType.Buffer && input is VFXExpressionBufferWithType bufferWithType)
{
input = input.parents[0]; //Explicitly skip NoOp expression
}
if (patchReadAttributeForSpawn && input is VFXAttributeExpression attribute)
{
if (attribute.attributeLocation == VFXAttributeLocation.Current)
{
if (globalEventAttribute == null)
throw new InvalidOperationException("m_GlobalEventAttribute is null");
foreach (var layoutDesc in globalEventAttribute)
{
if (layoutDesc.name == attribute.attributeName)
{
input = new VFXReadEventAttributeExpression(attribute.attribute, layoutDesc.offset.element);
break;
}
}
if (input is not VFXReadEventAttributeExpression)
throw new InvalidOperationException("Unable to find " + attribute.attributeName + " in globalEventAttribute");
}
}
return input;
}
public VFXExpression Compile(VFXExpression expression)
{
var gpuTransformation = Has(VFXExpressionContextOption.GPUDataTransformation);
var patchReadAttributeForSpawn = Has(VFXExpressionContextOption.PatchReadToEventAttribute);
VFXExpression reduced;
if (!m_ReducedCache.TryGetValue(expression, out reduced))
{
var parents = new VFXExpression[expression.parents.Length];
for (var i = 0; i < expression.parents.Length; i++)
{
var parent = Compile(expression.parents[i]);
bool currentGPUTransformation = gpuTransformation
&& expression.IsAny(VFXExpression.Flags.NotCompilableOnCPU)
&& !parent.IsAny(VFXExpression.Flags.NotCompilableOnCPU);
parent = PatchVFXExpression(parent, expression, currentGPUTransformation, patchReadAttributeForSpawn, m_GlobalEventAttribute);
parents[i] = parent;
}
if (ShouldEvaluate(expression, parents))
{
reduced = expression.Evaluate(parents);
}
else if (HasAny(VFXExpressionContextOption.Reduction | VFXExpressionContextOption.CPUEvaluation | VFXExpressionContextOption.ConstantFolding) || !StructuralComparisons.StructuralEqualityComparer.Equals(parents, expression.parents))
{
reduced = expression.Reduce(parents);
}
else
{
reduced = expression;
}
m_ReducedCache[expression] = reduced;
}
return reduced;
}
public void Invalidate()
{
m_HLSLCollectionPerContext.Clear();
m_ReducedCache.Clear();
m_GraphicsBufferTypeUsagePerContext.Clear();
}
public void Invalidate(VFXExpression expression)
{
m_ReducedCache.Remove(expression);
}
public VFXExpression GetReduced(VFXExpression expression)
{
VFXExpression reduced;
m_ReducedCache.TryGetValue(expression, out reduced);
return reduced != null ? reduced : expression;
}
private void AddReducedGraph(HashSet<VFXExpression> dst, VFXExpression exp)
{
if (!dst.Contains(exp))
{
dst.Add(exp);
foreach (var parent in exp.parents)
AddReducedGraph(dst, parent);
}
}
public HashSet<VFXExpression> BuildAllReduced()
{
var reduced = new HashSet<VFXExpression>();
foreach (var exp in m_EndExpressions)
if (m_ReducedCache.ContainsKey(exp.Key))
AddReducedGraph(reduced, m_ReducedCache[exp.Key]);
return reduced;
}
public IEnumerable<VFXExpression> RegisteredExpressions => m_EndExpressions.Keys;
public Dictionary<VFXContext, Dictionary<VFXExpression, BufferUsage>> GraphicsBufferTypeUsagePerContext => m_GraphicsBufferTypeUsagePerContext;
public Dictionary<VFXContext, List<IHLSLCodeHolder>> hlslCodeHoldersPerContext => m_HLSLCollectionPerContext;
private Dictionary<VFXExpression, VFXExpression> m_ReducedCache = new ();
private Dictionary<VFXExpression, HashSet<VFXContext>> m_EndExpressions = new ();
private Dictionary<VFXContext, Dictionary<VFXExpression, BufferUsage>> m_GraphicsBufferTypeUsagePerContext = new ();
private IEnumerable<VFXLayoutElementDesc> m_GlobalEventAttribute;
private VFXExpressionContextOption m_ReductionOptions;
private readonly Dictionary<VFXContext, List<IHLSLCodeHolder>> m_HLSLCollectionPerContext = new ();
}
}
}