276 lines
13 KiB
C#
276 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq;
|
|
|
|
using UnityEngine.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
|
|
}
|
|
|
|
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> globalEventAttibutes = null)
|
|
{
|
|
m_ReductionOptions = reductionOption;
|
|
m_GlobalEventAttribute = globalEventAttibutes;
|
|
|
|
if (Has(VFXExpressionContextOption.CPUEvaluation) && Has(VFXExpressionContextOption.GPUDataTransformation))
|
|
throw new ArgumentException("Invalid reduction options");
|
|
}
|
|
|
|
public void RegisterExpression(VFXExpression expression)
|
|
{
|
|
m_EndExpressions.Add(expression);
|
|
}
|
|
|
|
public void UnregisterExpression(VFXExpression expression)
|
|
{
|
|
Invalidate(expression);
|
|
m_EndExpressions.Remove(expression);
|
|
}
|
|
|
|
public void Compile()
|
|
{
|
|
Profiler.BeginSample("VFXEditor.CompileExpressionContext");
|
|
|
|
try
|
|
{
|
|
foreach (var exp in m_EndExpressions)
|
|
Compile(exp);
|
|
|
|
if (HasAny(VFXExpressionContextOption.GPUDataTransformation | VFXExpressionContextOption.PatchReadToEventAttribute))
|
|
{
|
|
var gpuTransformation = Has(VFXExpressionContextOption.GPUDataTransformation);
|
|
var spawnEventPath = Has(VFXExpressionContextOption.PatchReadToEventAttribute);
|
|
foreach (var exp in m_EndExpressions)
|
|
m_ReducedCache[exp] = PatchVFXExpression(GetReduced(exp), null /* no source in end expression */, gpuTransformation, spawnEventPath, m_GlobalEventAttribute);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Profiler.EndSample();
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
return reducedParents.All(e => (e.m_Flags & (flag | Flags.InvalidOnCPU)) == flag);
|
|
}
|
|
|
|
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 VFXExpressionSampleSkinnedMeshRendererFloat
|
|
|| targetExpression is VFXExpressionSampleSkinnedMeshRendererFloat2
|
|
|| targetExpression is VFXExpressionSampleSkinnedMeshRendererFloat3
|
|
|| targetExpression is VFXExpressionSampleSkinnedMeshRendererFloat4
|
|
|| targetExpression is VFXExpressionSampleSkinnedMeshRendererColor)
|
|
{
|
|
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);
|
|
}
|
|
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 (patchReadAttributeForSpawn && input is VFXAttributeExpression)
|
|
{
|
|
var attribute = input as VFXAttributeExpression;
|
|
if (attribute.attributeLocation == VFXAttributeLocation.Current)
|
|
{
|
|
if (globalEventAttribute == null)
|
|
throw new InvalidOperationException("m_GlobalEventAttribute is null");
|
|
|
|
var layoutDesc = globalEventAttribute.FirstOrDefault(o => o.name == attribute.attributeName);
|
|
if (layoutDesc.name != attribute.attributeName)
|
|
throw new InvalidOperationException("Unable to find " + attribute.attributeName + " in globalEventAttribute");
|
|
|
|
input = new VFXReadEventAttributeExpression(attribute.attribute, layoutDesc.offset.element);
|
|
}
|
|
}
|
|
|
|
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 = expression.parents.Select(e =>
|
|
{
|
|
var parent = Compile(e);
|
|
bool currentGPUTransformation = gpuTransformation
|
|
&& expression.IsAny(VFXExpression.Flags.NotCompilableOnCPU)
|
|
&& !parent.IsAny(VFXExpression.Flags.NotCompilableOnCPU);
|
|
parent = PatchVFXExpression(parent, expression, currentGPUTransformation, patchReadAttributeForSpawn, m_GlobalEventAttribute);
|
|
return parent;
|
|
}).ToArray();
|
|
|
|
if (ShouldEvaluate(expression, parents))
|
|
{
|
|
reduced = expression.Evaluate(parents);
|
|
}
|
|
else if (HasAny(VFXExpressionContextOption.Reduction | VFXExpressionContextOption.CPUEvaluation | VFXExpressionContextOption.ConstantFolding) || !parents.SequenceEqual(expression.parents))
|
|
{
|
|
reduced = expression.Reduce(parents);
|
|
}
|
|
else
|
|
{
|
|
reduced = expression;
|
|
}
|
|
|
|
m_ReducedCache[expression] = reduced;
|
|
}
|
|
return reduced;
|
|
}
|
|
|
|
public void Invalidate()
|
|
{
|
|
m_ReducedCache.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))
|
|
AddReducedGraph(reduced, m_ReducedCache[exp]);
|
|
return reduced;
|
|
}
|
|
|
|
public ReadOnlyCollection<VFXExpression> RegisteredExpressions { get { return m_EndExpressions.ToList().AsReadOnly(); } }
|
|
|
|
private Dictionary<VFXExpression, VFXExpression> m_ReducedCache = new Dictionary<VFXExpression, VFXExpression>();
|
|
private HashSet<VFXExpression> m_EndExpressions = new HashSet<VFXExpression>();
|
|
|
|
private IEnumerable<VFXLayoutElementDesc> m_GlobalEventAttribute;
|
|
private VFXExpressionContextOption m_ReductionOptions;
|
|
}
|
|
}
|
|
}
|