1272 lines
59 KiB
C#

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.VFX;
using UnityEngine;
using UnityEngine.VFX;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
using Object = UnityEngine.Object;
using System.IO;
namespace UnityEditor.VFX
{
struct VFXContextCompiledData
{
public VFXExpressionMapper cpuMapper;
public VFXExpressionMapper gpuMapper;
public VFXUniformMapper uniformMapper;
public VFXMapping[] parameters;
public int indexInShaderSource;
}
enum VFXCompilationMode
{
Edition,
Runtime,
}
class VFXDependentBuffersData
{
public Dictionary<VFXData, int> attributeBuffers = new Dictionary<VFXData, int>();
public Dictionary<VFXData, int> stripBuffers = new Dictionary<VFXData, int>();
public Dictionary<VFXData, int> eventBuffers = new Dictionary<VFXData, int>();
}
class VFXGraphCompiledData
{
public const uint compiledVersion = 2;
public VFXGraphCompiledData(VFXGraph graph)
{
if (graph == null)
throw new ArgumentNullException("VFXGraph cannot be null");
m_Graph = graph;
}
private struct GeneratedCodeData
{
public VFXContext context;
public bool computeShader;
public System.Text.StringBuilder content;
public VFXCompilationMode compilMode;
}
private static VFXExpressionObjectValueContainerDesc<T> CreateObjectValueDesc<T>(VFXExpression exp, int expIndex)
{
var desc = new VFXExpressionObjectValueContainerDesc<T>();
desc.instanceID = exp.Get<int>();
return desc;
}
private static VFXExpressionValueContainerDesc<T> CreateValueDesc<T>(VFXExpression exp, int expIndex)
{
var desc = new VFXExpressionValueContainerDesc<T>();
desc.value = exp.Get<T>();
return desc;
}
private void SetValueDesc<T>(VFXExpressionValueContainerDesc desc, VFXExpression exp)
{
((VFXExpressionValueContainerDesc<T>)desc).value = exp.Get<T>();
}
private void SetObjectValueDesc<T>(VFXExpressionValueContainerDesc desc, VFXExpression exp)
{
((VFXExpressionObjectValueContainerDesc<T>)desc).instanceID = exp.Get<int>();
}
public uint FindReducedExpressionIndexFromSlotCPU(VFXSlot slot)
{
if (m_ExpressionGraph == null)
{
return uint.MaxValue;
}
var targetExpression = slot.GetExpression();
if (targetExpression == null)
{
return uint.MaxValue;
}
if (!m_ExpressionGraph.CPUExpressionsToReduced.ContainsKey(targetExpression))
{
return uint.MaxValue;
}
var ouputExpression = m_ExpressionGraph.CPUExpressionsToReduced[targetExpression];
return (uint)m_ExpressionGraph.GetFlattenedIndex(ouputExpression);
}
private static void FillExpressionDescs(VFXExpressionGraph graph, List<VFXExpressionDesc> outExpressionCommonDescs, List<VFXExpressionDesc> outExpressionPerSpawnEventDescs, List<VFXExpressionValueContainerDesc> outValueDescs)
{
var flatGraph = graph.FlattenedExpressions;
var numFlattenedExpressions = flatGraph.Count;
var maxCommonExpressionIndex = (uint)numFlattenedExpressions;
for (int i = 0; i < numFlattenedExpressions; ++i)
{
var exp = flatGraph[i];
if (exp.Is(VFXExpression.Flags.PerSpawn) && maxCommonExpressionIndex == numFlattenedExpressions)
maxCommonExpressionIndex = (uint)i;
if (!exp.Is(VFXExpression.Flags.PerSpawn) && maxCommonExpressionIndex != numFlattenedExpressions)
throw new InvalidOperationException("Not contiguous expression VFXExpression.Flags.PerSpawn detected");
// Must match data in C++ expression
if (exp.Is(VFXExpression.Flags.Value))
{
VFXExpressionValueContainerDesc value;
switch (exp.valueType)
{
case VFXValueType.Float: value = CreateValueDesc<float>(exp, i); break;
case VFXValueType.Float2: value = CreateValueDesc<Vector2>(exp, i); break;
case VFXValueType.Float3: value = CreateValueDesc<Vector3>(exp, i); break;
case VFXValueType.Float4: value = CreateValueDesc<Vector4>(exp, i); break;
case VFXValueType.Int32: value = CreateValueDesc<int>(exp, i); break;
case VFXValueType.Uint32: value = CreateValueDesc<uint>(exp, i); break;
case VFXValueType.Texture2D:
case VFXValueType.Texture2DArray:
case VFXValueType.Texture3D:
case VFXValueType.TextureCube:
case VFXValueType.TextureCubeArray:
value = CreateObjectValueDesc<Texture>(exp, i);
break;
case VFXValueType.Matrix4x4: value = CreateValueDesc<Matrix4x4>(exp, i); break;
case VFXValueType.Curve: value = CreateValueDesc<AnimationCurve>(exp, i); break;
case VFXValueType.ColorGradient: value = CreateValueDesc<Gradient>(exp, i); break;
case VFXValueType.Mesh: value = CreateObjectValueDesc<Mesh>(exp, i); break;
case VFXValueType.SkinnedMeshRenderer: value = CreateObjectValueDesc<SkinnedMeshRenderer>(exp, i); break;
case VFXValueType.Boolean: value = CreateValueDesc<bool>(exp, i); break;
default: throw new InvalidOperationException("Invalid type");
}
value.expressionIndex = (uint)i;
outValueDescs.Add(value);
}
var outExpressionsDesc = i >= maxCommonExpressionIndex ? outExpressionPerSpawnEventDescs : outExpressionCommonDescs;
outExpressionsDesc.Add(new VFXExpressionDesc
{
op = exp.operation,
data = exp.GetOperands(graph).ToArray(),
});
}
}
private static void CollectExposedDesc(List<VFXMapping> outExposedParameters, string name, VFXSlot slot, VFXExpressionGraph graph)
{
var expression = slot.valueType != VFXValueType.None ? slot.GetInExpression() : null;
if (expression != null)
{
var exprIndex = graph.GetFlattenedIndex(expression);
if (exprIndex == -1)
throw new InvalidOperationException("Unable to retrieve value from exposed for " + name);
outExposedParameters.Add(new VFXMapping()
{
name = name,
index = exprIndex
});
}
else
{
foreach (var child in slot.children)
{
CollectExposedDesc(outExposedParameters, name + "_" + child.name, child, graph);
}
}
}
private static void FillExposedDescs(List<VFXMapping> outExposedParameters, VFXExpressionGraph graph, IEnumerable<VFXParameter> parameters)
{
foreach (var parameter in parameters)
{
if (parameter.exposed && !parameter.isOutput)
{
CollectExposedDesc(outExposedParameters, parameter.exposedName, parameter.GetOutputSlot(0), graph);
}
}
}
private static void FillEventAttributeDescs(List<VFXLayoutElementDesc> eventAttributeDescs, VFXExpressionGraph graph, IEnumerable<VFXContext> contexts)
{
foreach (var context in contexts.Where(o => o.contextType == VFXContextType.Spawner))
{
foreach (var linked in context.outputContexts)
{
var data = linked.GetData();
if (data)
{
foreach (var attribute in data.GetAttributes())
{
if ((attribute.mode & VFXAttributeMode.ReadSource) != 0 && !eventAttributeDescs.Any(o => o.name == attribute.attrib.name))
{
eventAttributeDescs.Add(new VFXLayoutElementDesc()
{
name = attribute.attrib.name,
type = attribute.attrib.type
});
}
}
}
}
}
var structureLayoutTotalSize = (uint)eventAttributeDescs.Sum(e => (long)VFXExpression.TypeToSize(e.type));
var currentLayoutSize = 0u;
var listWithOffset = new List<VFXLayoutElementDesc>();
eventAttributeDescs.ForEach(e =>
{
e.offset.element = currentLayoutSize;
e.offset.structure = structureLayoutTotalSize;
currentLayoutSize += (uint)VFXExpression.TypeToSize(e.type);
listWithOffset.Add(e);
});
eventAttributeDescs.Clear();
eventAttributeDescs.AddRange(listWithOffset);
}
private static List<VFXContext> CollectContextParentRecursively(IEnumerable<VFXContext> inputList, ref SubgraphInfos subgraphContexts)
{
var contextEffectiveInputLinks = subgraphContexts.contextEffectiveInputLinks;
var contextList = inputList.SelectMany(o => contextEffectiveInputLinks[o].SelectMany(t => t)).Select(t => t.context).Distinct().ToList();
if (contextList.Any(o => contextEffectiveInputLinks[o].Any()))
{
var parentContextList = CollectContextParentRecursively(contextList.Except(inputList), ref subgraphContexts);
foreach (var context in parentContextList)
{
if (!contextList.Contains(context))
{
contextList.Add(context);
}
}
}
return contextList;
}
private static VFXContext[] CollectSpawnersHierarchy(IEnumerable<VFXContext> vfxContext, ref SubgraphInfos subgraphContexts)
{
var initContext = vfxContext.Where(o => o.contextType == VFXContextType.Init || o.contextType == VFXContextType.OutputEvent).ToList();
var spawnerList = CollectContextParentRecursively(initContext, ref subgraphContexts);
return spawnerList.Where(o => o.contextType == VFXContextType.Spawner).Reverse().ToArray();
}
struct SpawnInfo
{
public int bufferIndex;
public int systemIndex;
}
private static VFXCPUBufferData ComputeArrayOfStructureInitialData(IEnumerable<VFXLayoutElementDesc> layout)
{
var data = new VFXCPUBufferData();
foreach (var element in layout)
{
var attribute = VFXAttribute.AllAttribute.FirstOrDefault(o => o.name == element.name);
bool useAttribute = attribute.name == element.name;
if (element.type == VFXValueType.Boolean)
{
var v = useAttribute ? attribute.value.Get<bool>() : default(bool);
data.PushBool(v);
}
else if (element.type == VFXValueType.Float)
{
var v = useAttribute ? attribute.value.Get<float>() : default(float);
data.PushFloat(v);
}
else if (element.type == VFXValueType.Float2)
{
var v = useAttribute ? attribute.value.Get<Vector2>() : default(Vector2);
data.PushFloat(v.x);
data.PushFloat(v.y);
}
else if (element.type == VFXValueType.Float3)
{
var v = useAttribute ? attribute.value.Get<Vector3>() : default(Vector3);
data.PushFloat(v.x);
data.PushFloat(v.y);
data.PushFloat(v.z);
}
else if (element.type == VFXValueType.Float4)
{
var v = useAttribute ? attribute.value.Get<Vector4>() : default(Vector4);
data.PushFloat(v.x);
data.PushFloat(v.y);
data.PushFloat(v.z);
data.PushFloat(v.w);
}
else if (element.type == VFXValueType.Int32)
{
var v = useAttribute ? attribute.value.Get<int>() : default(int);
data.PushInt(v);
}
else if (element.type == VFXValueType.Uint32)
{
var v = useAttribute ? attribute.value.Get<uint>() : default(uint);
data.PushUInt(v);
}
else
{
throw new NotImplementedException();
}
}
return data;
}
void RecursePutSubgraphParent(Dictionary<VFXSubgraphContext, VFXSubgraphContext> parents, List<VFXSubgraphContext> subgraphs, VFXSubgraphContext subgraph)
{
foreach (var subSubgraph in subgraph.subChildren.OfType<VFXSubgraphContext>().Where(t => t.subgraph != null))
{
subgraphs.Add(subSubgraph);
parents[subSubgraph] = subgraph;
RecursePutSubgraphParent(parents, subgraphs, subSubgraph);
}
}
static List<VFXContextLink>[] ComputeContextEffectiveLinks(VFXContext context, ref SubgraphInfos subgraphInfos)
{
List<VFXContextLink>[] result = new List<VFXContextLink>[context.inputFlowSlot.Length];
Dictionary<string, int> eventNameIndice = new Dictionary<string, int>();
for (int i = 0; i < context.inputFlowSlot.Length; ++i)
{
result[i] = new List<VFXContextLink>();
VFXSubgraphContext parentSubgraph = null;
subgraphInfos.spawnerSubgraph.TryGetValue(context, out parentSubgraph);
List<VFXContext> subgraphAncestors = new List<VFXContext>();
subgraphAncestors.Add(context);
while (parentSubgraph != null)
{
subgraphAncestors.Add(parentSubgraph);
if (!subgraphInfos.subgraphParents.TryGetValue(parentSubgraph, out parentSubgraph))
{
parentSubgraph = null;
}
}
List<List<int>> defaultEventPaths = new List<List<int>>();
defaultEventPaths.Add(new List<int>(new int[] { i }));
List<List<int>> newEventPaths = new List<List<int>>();
var usedContexts = new List<VFXContext>();
for (int j = 0; j < subgraphAncestors.Count; ++j)
{
var sg = subgraphAncestors[j];
var nextSg = j < subgraphAncestors.Count - 1 ? subgraphAncestors[j + 1] as VFXSubgraphContext : null;
foreach (var path in defaultEventPaths)
{
int currentFlowIndex = path.Last();
var eventSlot = sg.inputFlowSlot[currentFlowIndex];
var eventSlotSpawners = eventSlot.link.Where(t => !(t.context is VFXBasicEvent));
if (eventSlotSpawners.Any())
{
foreach (var evt in eventSlotSpawners)
{
result[i].Add(evt);
}
}
var eventSlotEvents = eventSlot.link.Where(t => t.context is VFXBasicEvent);
if (eventSlotEvents.Any())
{
foreach (var evt in eventSlotEvents)
{
string eventName = (evt.context as VFXBasicEvent).eventName;
switch (eventName)
{
case VisualEffectAsset.PlayEventName:
if (nextSg != null)
newEventPaths.Add(path.Concat(new int[] { 0 }).ToList());
else
result[i].Add(evt);
break;
case VisualEffectAsset.StopEventName:
if (nextSg != null)
newEventPaths.Add(path.Concat(new int[] { 1 }).ToList());
else
result[i].Add(evt);
break;
default:
{
if (nextSg != null)
{
int eventIndex = nextSg.GetInputFlowIndex(eventName);
if (eventIndex != -1)
newEventPaths.Add(path.Concat(new int[] { eventIndex }).ToList());
}
else
{
result[i].Add(evt);
}
}
break;
}
}
}
else if (!eventSlot.link.Any())
{
if (!(sg is VFXSubgraphContext))
{
if (nextSg != null)
{
int fixedSlotIndex = currentFlowIndex > 1 ? currentFlowIndex : nextSg.GetInputFlowIndex(currentFlowIndex == 1 ? VisualEffectAsset.StopEventName : VisualEffectAsset.PlayEventName);
if (fixedSlotIndex >= 0)
newEventPaths.Add(path.Concat(new int[] { fixedSlotIndex }).ToList());
}
else
newEventPaths.Add(path.Concat(new int[] { currentFlowIndex }).ToList());
}
else
{
var sgsg = sg as VFXSubgraphContext;
var eventName = sgsg.GetInputFlowName(currentFlowIndex);
var eventCtx = sgsg.GetEventContext(eventName);
if (eventCtx != null)
result[i].Add(new VFXContextLink() { slotIndex = 0, context = eventCtx });
}
}
}
defaultEventPaths.Clear();
defaultEventPaths.AddRange(newEventPaths);
newEventPaths.Clear();
}
}
return result;
}
private static void CollectParentExpressionRecursively(VFXExpression entry, HashSet<VFXExpression> processed)
{
if (processed.Contains(entry))
return;
foreach (var parent in entry.parents)
CollectParentExpressionRecursively(parent, processed);
processed.Add(entry);
}
private class ProcessChunk
{
public int startIndex;
public int endIndex;
}
static VFXMapping[] ComputePreProcessExpressionForSpawn(IEnumerable<VFXExpression> expressionPerSpawnToProcess, VFXExpressionGraph graph)
{
var allExpressions = new HashSet<VFXExpression>();
foreach (var expression in expressionPerSpawnToProcess)
CollectParentExpressionRecursively(expression, allExpressions);
var expressionIndexes = allExpressions.
Where(o => o.Is(VFXExpression.Flags.PerSpawn)) //Filter only per spawn part of graph
.Select(o => graph.GetFlattenedIndex(o))
.OrderBy(i => i);
//Additional verification of appropriate expected expression index
//In flatten expression, all common expressions are sorted first, then, we have chunk of additional preprocess
//We aren't supposed to happen a chunk which is running common expression here.
if (expressionIndexes.Any(i => i < graph.CommonExpressionCount))
{
var expressionInCommon = allExpressions
.Where(o => graph.GetFlattenedIndex(o) < graph.CommonExpressionCount)
.OrderBy(o => graph.GetFlattenedIndex(o));
Debug.LogErrorFormat("Unexpected preprocess expression detected : {0} (count)", expressionInCommon.Count());
}
var processChunk = new List<ProcessChunk>();
int previousIndex = int.MinValue;
foreach (var indice in expressionIndexes)
{
if (indice != previousIndex + 1)
processChunk.Add(new ProcessChunk()
{
startIndex = indice,
endIndex = indice + 1
});
else
processChunk.Last().endIndex = indice + 1;
previousIndex = indice;
}
return processChunk.SelectMany((o, i) =>
{
var prefix = VFXCodeGeneratorHelper.GeneratePrefix((uint)i);
return new[]
{
new VFXMapping
{
name = "start_" + prefix,
index = o.startIndex
},
new VFXMapping
{
name = "end_" + prefix,
index = o.endIndex
}
};
}).ToArray();
}
private static VFXEditorTaskDesc[] BuildEditorTaksDescFromBlockSpawner(IEnumerable<VFXBlock> blocks, VFXContextCompiledData contextData, VFXExpressionGraph graph)
{
var taskDescList = new List<VFXEditorTaskDesc>();
int index = 0;
foreach (var b in blocks)
{
var spawnerBlock = b as VFXAbstractSpawner;
if (spawnerBlock == null)
{
throw new InvalidCastException("Unexpected block type in spawnerContext");
}
if (spawnerBlock.spawnerType == VFXTaskType.CustomCallbackSpawner && spawnerBlock.customBehavior == null)
{
throw new InvalidOperationException("VFXAbstractSpawner excepts a custom behavior for custom callback type");
}
if (spawnerBlock.spawnerType != VFXTaskType.CustomCallbackSpawner && spawnerBlock.customBehavior != null)
{
throw new InvalidOperationException("VFXAbstractSpawner only expects a custom behavior for custom callback type");
}
var mappingList = new List<VFXMapping>();
var expressionPerSpawnToProcess = new List<VFXExpression>();
foreach (var namedExpression in contextData.cpuMapper.CollectExpression(index, false))
{
mappingList.Add(new VFXMapping()
{
index = graph.GetFlattenedIndex(namedExpression.exp),
name = namedExpression.name
});
if (namedExpression.exp.Is(VFXExpression.Flags.PerSpawn))
expressionPerSpawnToProcess.Add(namedExpression.exp);
}
if (expressionPerSpawnToProcess.Any())
{
var mappingPreProcess = ComputePreProcessExpressionForSpawn(expressionPerSpawnToProcess, graph);
var preProcessTask = new VFXEditorTaskDesc
{
type = UnityEngine.VFX.VFXTaskType.EvaluateExpressionsSpawner,
buffers = new VFXMapping[0],
values = mappingPreProcess,
parameters = contextData.parameters,
externalProcessor = null
};
taskDescList.Add(preProcessTask);
}
Object processor = null;
if (spawnerBlock.customBehavior != null)
processor = spawnerBlock.customBehavior;
taskDescList.Add(new VFXEditorTaskDesc
{
type = (UnityEngine.VFX.VFXTaskType)spawnerBlock.spawnerType,
buffers = new VFXMapping[0],
values = mappingList.ToArray(),
parameters = contextData.parameters,
externalProcessor = processor
});
index++;
}
return taskDescList.ToArray();
}
private static void FillSpawner(Dictionary<VFXContext, SpawnInfo> outContextSpawnToSpawnInfo,
List<VFXCPUBufferDesc> outCpuBufferDescs,
List<VFXEditorSystemDesc> outSystemDescs,
IEnumerable<VFXContext> contexts,
VFXExpressionGraph graph,
Dictionary<VFXContext, VFXContextCompiledData> contextToCompiledData,
ref SubgraphInfos subgraphInfos,
VFXSystemNames systemNames = null)
{
var spawners = CollectSpawnersHierarchy(contexts, ref subgraphInfos);
foreach (var it in spawners.Select((spawner, index) => new { spawner, index }))
{
outContextSpawnToSpawnInfo.Add(it.spawner, new SpawnInfo() { bufferIndex = outCpuBufferDescs.Count, systemIndex = it.index });
outCpuBufferDescs.Add(new VFXCPUBufferDesc()
{
capacity = 1u,
stride = graph.GlobalEventAttributes.First().offset.structure,
layout = graph.GlobalEventAttributes.ToArray(),
initialData = ComputeArrayOfStructureInitialData(graph.GlobalEventAttributes)
});
}
foreach (var spawnContext in spawners)
{
var buffers = new List<VFXMapping>();
buffers.Add(new VFXMapping()
{
index = outContextSpawnToSpawnInfo[spawnContext].bufferIndex,
name = "spawner_output"
});
for (int indexSlot = 0; indexSlot < 2 && indexSlot < spawnContext.inputFlowSlot.Length; ++indexSlot)
{
foreach (var input in subgraphInfos.contextEffectiveInputLinks[spawnContext][indexSlot])
{
var inputContext = input.context;
if (outContextSpawnToSpawnInfo.ContainsKey(inputContext))
{
buffers.Add(new VFXMapping()
{
index = outContextSpawnToSpawnInfo[inputContext].bufferIndex,
name = "spawner_input_" + (indexSlot == 0 ? "OnPlay" : "OnStop")
});
}
}
}
var contextData = contextToCompiledData[spawnContext];
var contextExpressions = contextData.cpuMapper.CollectExpression(-1);
var systemValueMappings = new List<VFXMapping>();
var expressionPerSpawnToProcess = new List<VFXExpression>();
foreach (var contextExpression in contextExpressions)
{
var expressionIndex = graph.GetFlattenedIndex(contextExpression.exp);
systemValueMappings.Add(new VFXMapping(contextExpression.name, expressionIndex));
if (contextExpression.exp.Is(VFXExpression.Flags.PerSpawn))
{
expressionPerSpawnToProcess.Add(contextExpression.exp);
}
}
if (expressionPerSpawnToProcess.Any())
{
var addiionnalValues = ComputePreProcessExpressionForSpawn(expressionPerSpawnToProcess, graph);
systemValueMappings.AddRange(addiionnalValues);
}
string nativeName = string.Empty;
if (systemNames != null)
nativeName = systemNames.GetUniqueSystemName(spawnContext);
else
throw new InvalidOperationException("system names manager cannot be null");
outSystemDescs.Add(new VFXEditorSystemDesc()
{
values = systemValueMappings.ToArray(),
buffers = buffers.ToArray(),
capacity = 0u,
name = nativeName,
flags = VFXSystemFlag.SystemDefault,
layer = uint.MaxValue,
tasks = BuildEditorTaksDescFromBlockSpawner(spawnContext.activeFlattenedChildrenWithImplicit, contextData, graph)
});
}
}
struct SubgraphInfos
{
public Dictionary<VFXSubgraphContext, VFXSubgraphContext> subgraphParents;
public Dictionary<VFXContext, VFXSubgraphContext> spawnerSubgraph;
public List<VFXSubgraphContext> subgraphs;
public Dictionary<VFXContext, List<VFXContextLink>[]> contextEffectiveInputLinks;
public List<VFXContextLink> GetContextEffectiveOutputLinks(VFXContext context, int slot)
{
List<VFXContextLink> effectiveOuts = new List<VFXContextLink>();
foreach (var kv in contextEffectiveInputLinks)
{
for (int i = 0; i < kv.Value.Length; ++i)
{
foreach (var link in kv.Value[i])
{
if (link.context == context && link.slotIndex == slot)
effectiveOuts.Add(new VFXContextLink() { context = kv.Key, slotIndex = i });
}
}
}
return effectiveOuts;
}
}
private static void FillEvent(List<VFXEventDesc> outEventDesc, Dictionary<VFXContext, SpawnInfo> contextSpawnToSpawnInfo, IEnumerable<VFXContext> contexts, ref SubgraphInfos subgraphInfos)
{
var contextEffectiveInputLinks = subgraphInfos.contextEffectiveInputLinks;
var allPlayNotLinked = contextSpawnToSpawnInfo.Where(o => !contextEffectiveInputLinks[o.Key][0].Any()).Select(o => (uint)o.Value.systemIndex).ToList();
var allStopNotLinked = contextSpawnToSpawnInfo.Where(o => !contextEffectiveInputLinks[o.Key][1].Any()).Select(o => (uint)o.Value.systemIndex).ToList();
var eventDescTemp = new[]
{
new { eventName = VisualEffectAsset.PlayEventName, playSystems = allPlayNotLinked, stopSystems = new List<uint>() },
new { eventName = VisualEffectAsset.StopEventName, playSystems = new List<uint>(), stopSystems = allStopNotLinked },
}.ToList();
var specialNames = new HashSet<string>(new string[] { VisualEffectAsset.PlayEventName, VisualEffectAsset.StopEventName });
var events = contexts.Where(o => o.contextType == VFXContextType.Event);
foreach (var evt in events)
{
var eventName = (evt as VFXBasicEvent).eventName;
if (subgraphInfos.spawnerSubgraph.ContainsKey(evt) && specialNames.Contains(eventName))
continue;
List<VFXContextLink> effecitveOuts = subgraphInfos.GetContextEffectiveOutputLinks(evt, 0);
foreach (var link in effecitveOuts)
{
if (contextSpawnToSpawnInfo.ContainsKey(link.context))
{
var eventIndex = eventDescTemp.FindIndex(o => o.eventName == eventName);
if (eventIndex == -1)
{
eventIndex = eventDescTemp.Count;
eventDescTemp.Add(new
{
eventName = eventName,
playSystems = new List<uint>(),
stopSystems = new List<uint>(),
});
}
var startSystem = link.slotIndex == 0;
var spawnerIndex = (uint)contextSpawnToSpawnInfo[link.context].systemIndex;
if (startSystem)
{
eventDescTemp[eventIndex].playSystems.Add(spawnerIndex);
}
else
{
eventDescTemp[eventIndex].stopSystems.Add(spawnerIndex);
}
}
}
}
outEventDesc.Clear();
var relevantEvent = eventDescTemp.Where(o => o.playSystems.Any() || o.stopSystems.Any());
outEventDesc.AddRange(relevantEvent.Select(o => new VFXEventDesc() { name = o.eventName, startSystems = o.playSystems.ToArray(), stopSystems = o.stopSystems.ToArray() }));
}
private void GenerateShaders(List<GeneratedCodeData> outGeneratedCodeData, VFXExpressionGraph graph, IEnumerable<VFXContext> contexts, Dictionary<VFXContext, VFXContextCompiledData> contextToCompiledData, VFXCompilationMode compilationMode, HashSet<string> dependencies)
{
Profiler.BeginSample("VFXEditor.GenerateShaders");
try
{
foreach (var context in contexts)
{
var gpuMapper = graph.BuildGPUMapper(context);
var uniformMapper = new VFXUniformMapper(gpuMapper, context.doesGenerateShader);
// Add gpu and uniform mapper
var contextData = contextToCompiledData[context];
contextData.gpuMapper = gpuMapper;
contextData.uniformMapper = uniformMapper;
contextToCompiledData[context] = contextData;
if (context.doesGenerateShader)
{
var generatedContent = VFXCodeGenerator.Build(context, compilationMode, contextData, dependencies);
if (generatedContent != null)
{
outGeneratedCodeData.Add(new GeneratedCodeData()
{
context = context,
computeShader = context.codeGeneratorCompute,
compilMode = compilationMode,
content = generatedContent
});
}
}
}
var resource = m_Graph.GetResource();
}
finally
{
Profiler.EndSample();
}
}
private static VFXShaderSourceDesc[] SaveShaderFiles(VisualEffectResource resource,
List<GeneratedCodeData> generatedCodeData,
Dictionary<VFXContext, VFXContextCompiledData> contextToCompiledData,
VFXSystemNames systemNames)
{
Profiler.BeginSample("VFXEditor.SaveShaderFiles");
try
{
var descs = new VFXShaderSourceDesc[generatedCodeData.Count];
var assetName = string.Empty;
if (resource.asset != null)
{
assetName = resource.asset.name; //Most Common case, asset is already available
}
else
{
var assetPath = AssetDatabase.GetAssetPath(resource); //Can occur during Copy/Past or Rename
if (!string.IsNullOrEmpty(assetPath))
{
assetName = Path.GetFileNameWithoutExtension(assetPath);
}
else if (resource.name != null) //Unable to retrieve asset path, last fallback use serialized resource name
{
assetName = resource.name;
}
}
for (int i = 0; i < generatedCodeData.Count; ++i)
{
var generated = generatedCodeData[i];
var systemName = systemNames.GetUniqueSystemName(generated.context.GetData());
var contextLetter = generated.context.letter;
var contextName = string.IsNullOrEmpty(generated.context.label) ? generated.context.libraryName : generated.context.label;
var shaderName = string.Empty;
var fileName = string.Empty;
if (contextLetter == '\0')
{
fileName = string.Format("[{0}] [{1}] {2}", assetName, systemName, contextName);
shaderName = string.Format("Hidden/VFX/{0}/{1}/{2}", assetName, systemName, contextName);
}
else
{
fileName = string.Format("[{0}] [{1}]{2} {3}", assetName, systemName, contextLetter, contextName);
shaderName = string.Format("Hidden/VFX/{0}/{1}/{2}/{3}", assetName, systemName, contextLetter, contextName);
}
if (!generated.computeShader)
{
generated.content.Insert(0, "Shader \"" + shaderName + "\"\n");
}
descs[i].source = generated.content.ToString();
descs[i].name = fileName;
descs[i].compute = generated.computeShader;
}
for (int i = 0; i < generatedCodeData.Count; ++i)
{
var generated = generatedCodeData[i];
var contextData = contextToCompiledData[generated.context];
contextData.indexInShaderSource = i;
contextToCompiledData[generated.context] = contextData;
}
return descs;
}
finally
{
Profiler.EndSample();
}
}
public void FillDependentBuffer(
IEnumerable<VFXData> compilableData,
List<VFXGPUBufferDesc> bufferDescs,
VFXDependentBuffersData buffers)
{
// TODO This should be in VFXDataParticle
foreach (var data in compilableData.OfType<VFXDataParticle>())
{
int attributeBufferIndex = -1;
if (data.attributeBufferSize > 0)
{
attributeBufferIndex = bufferDescs.Count;
bufferDescs.Add(data.attributeBufferDesc);
}
buffers.attributeBuffers.Add(data, attributeBufferIndex);
int stripBufferIndex = -1;
if (data.hasStrip)
{
stripBufferIndex = bufferDescs.Count;
uint stripCapacity = (uint)data.GetSettingValue("stripCapacity");
bufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Default, size = stripCapacity * 5, stride = 4 });
}
buffers.stripBuffers.Add(data, stripBufferIndex);
}
//Prepare GPU event buffer
foreach (var data in compilableData.SelectMany(o => o.dependenciesOut).Distinct().OfType<VFXDataParticle>())
{
var eventBufferIndex = -1;
uint capacity = (uint)data.GetSettingValue("capacity");
if (capacity > 0)
{
eventBufferIndex = bufferDescs.Count;
bufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Append, size = capacity, stride = 4 });
}
buffers.eventBuffers.Add(data, eventBufferIndex);
}
}
VFXRendererSettings GetRendererSettings(VFXRendererSettings initialSettings, IEnumerable<IVFXSubRenderer> subRenderers)
{
var settings = initialSettings;
settings.shadowCastingMode = subRenderers.Any(r => r.hasShadowCasting) ? ShadowCastingMode.On : ShadowCastingMode.Off;
settings.motionVectorGenerationMode = subRenderers.Any(r => r.hasMotionVector) ? MotionVectorGenerationMode.Object : MotionVectorGenerationMode.Camera;
return settings;
}
private class VFXImplicitContextOfExposedExpression : VFXContext
{
private VFXExpressionMapper mapper;
public VFXImplicitContextOfExposedExpression() : base(VFXContextType.None, VFXDataType.None, VFXDataType.None) {}
private static void CollectExposedExpression(List<VFXExpression> expressions, VFXSlot slot)
{
var expression = slot.valueType != VFXValueType.None ? slot.GetInExpression() : null;
if (expression != null)
expressions.Add(expression);
else
{
foreach (var child in slot.children)
CollectExposedExpression(expressions, child);
}
}
public void FillExpression(VFXGraph graph)
{
var allExposedParameter = graph.children.OfType<VFXParameter>().Where(o => o.exposed);
var expressionsList = new List<VFXExpression>();
foreach (var parameter in allExposedParameter)
CollectExposedExpression(expressionsList, parameter.outputSlots[0]);
mapper = new VFXExpressionMapper();
for (int i = 0; i < expressionsList.Count; ++i)
mapper.AddExpression(expressionsList[i], "ImplicitExposedExpression", i);
}
public override VFXExpressionMapper GetExpressionMapper(VFXDeviceTarget target)
{
return target == VFXDeviceTarget.CPU ? mapper : null;
}
}
static public Action<VisualEffectResource, bool> k_FnVFXResource_SetCompileInitialVariants = Find_FnVFXResource_SetCompileInitialVariants();
static Action<VisualEffectResource, bool> Find_FnVFXResource_SetCompileInitialVariants()
{
var property = typeof(VisualEffectResource).GetProperty("compileInitialVariants");
if (property != null)
{
return delegate(VisualEffectResource rsc, bool value)
{
property.SetValue(rsc, value, null);
};
}
return null;
}
void ComputeEffectiveInputLinks(ref SubgraphInfos subgraphInfos, IEnumerable<VFXContext> compilableContexts)
{
var contextEffectiveInputLinks = subgraphInfos.contextEffectiveInputLinks;
foreach (var context in compilableContexts.Where(t => !(t is VFXSubgraphContext)))
{
contextEffectiveInputLinks[context] = ComputeContextEffectiveLinks(context, ref subgraphInfos);
ComputeEffectiveInputLinks(ref subgraphInfos, contextEffectiveInputLinks[context].SelectMany(t => t).Select(t => t.context).Where(t => !contextEffectiveInputLinks.ContainsKey(t)));
}
}
public void Compile(VFXCompilationMode compilationMode, bool forceShaderValidation)
{
// Prevent doing anything ( and especially showing progress) in an empty graph.
if (m_Graph.children.Count() < 1)
{
// Cleaning
if (m_Graph.visualEffectResource != null)
m_Graph.visualEffectResource.ClearRuntimeData();
m_ExpressionGraph = new VFXExpressionGraph();
m_ExpressionValues = new VFXExpressionValueContainerDesc[] {};
return;
}
Profiler.BeginSample("VFXEditor.CompileAsset");
float nbSteps = 12.0f;
string assetPath = AssetDatabase.GetAssetPath(visualEffectResource);
string progressBarTitle = "Compiling " + assetPath;
try
{
EditorUtility.DisplayProgressBar(progressBarTitle, "Collecting dependencies", 0 / nbSteps);
var models = new HashSet<ScriptableObject>();
m_Graph.CollectDependencies(models, false);
var resource = m_Graph.GetResource();
resource.ClearSourceDependencies();
HashSet<string> sourceDependencies = new HashSet<string>();
foreach (VFXModel model in models.Where(t => t is IVFXSlotContainer))
{
model.GetSourceDependentAssets(sourceDependencies);
}
var contexts = models.OfType<VFXContext>().ToArray();
foreach (var c in contexts) // Unflag all contexts
c.MarkAsCompiled(false);
IEnumerable<VFXContext> compilableContexts = contexts.Where(c => c.CanBeCompiled()).ToArray();
var compilableData = models.OfType<VFXData>().Where(d => d.CanBeCompiled());
IEnumerable<VFXContext> implicitContexts = Enumerable.Empty<VFXContext>();
foreach (var d in compilableData) // Flag compiled contexts
implicitContexts = implicitContexts.Concat(d.InitImplicitContexts());
compilableContexts = compilableContexts.Concat(implicitContexts.ToArray());
foreach (var c in compilableContexts) // Flag compiled contexts
c.MarkAsCompiled(true);
EditorUtility.DisplayProgressBar(progressBarTitle, "Collecting attributes", 1 / nbSteps);
foreach (var data in compilableData)
data.CollectAttributes();
EditorUtility.DisplayProgressBar(progressBarTitle, "Process dependencies", 2 / nbSteps);
foreach (var data in compilableData)
data.ProcessDependencies();
EditorUtility.DisplayProgressBar(progressBarTitle, "Compiling expression Graph", 3 / nbSteps);
m_ExpressionGraph = new VFXExpressionGraph();
var exposedExpressionContext = ScriptableObject.CreateInstance<VFXImplicitContextOfExposedExpression>();
exposedExpressionContext.FillExpression(m_Graph); //Force all exposed expression to be visible, only for registering in CompileExpressions
var expressionContextOptions = compilationMode == VFXCompilationMode.Runtime ? VFXExpressionContextOption.ConstantFolding : VFXExpressionContextOption.Reduction;
m_ExpressionGraph.CompileExpressions(compilableContexts.Concat(new VFXContext[] { exposedExpressionContext }), expressionContextOptions);
EditorUtility.DisplayProgressBar(progressBarTitle, "Generating bytecode", 4 / nbSteps);
var expressionDescs = new List<VFXExpressionDesc>();
var expressionPerSpawnEventAttributesDescs = new List<VFXExpressionDesc>();
var valueDescs = new List<VFXExpressionValueContainerDesc>();
FillExpressionDescs(m_ExpressionGraph, expressionDescs, expressionPerSpawnEventAttributesDescs, valueDescs);
Dictionary<VFXContext, VFXContextCompiledData> contextToCompiledData = new Dictionary<VFXContext, VFXContextCompiledData>();
foreach (var context in compilableContexts)
contextToCompiledData.Add(context, new VFXContextCompiledData());
EditorUtility.DisplayProgressBar(progressBarTitle, "Generating mappings", 5 / nbSteps);
foreach (var context in compilableContexts)
{
var cpuMapper = m_ExpressionGraph.BuildCPUMapper(context);
var contextData = contextToCompiledData[context];
contextData.cpuMapper = cpuMapper;
contextData.parameters = context.additionalMappings.ToArray();
contextToCompiledData[context] = contextData;
}
var exposedParameterDescs = new List<VFXMapping>();
FillExposedDescs(exposedParameterDescs, m_ExpressionGraph, m_Graph.children.OfType<VFXParameter>());
var globalEventAttributeDescs = new List<VFXLayoutElementDesc>() { new VFXLayoutElementDesc() { name = "spawnCount", type = VFXValueType.Float } };
FillEventAttributeDescs(globalEventAttributeDescs, m_ExpressionGraph, compilableContexts);
SubgraphInfos subgraphInfos;
subgraphInfos.subgraphParents = new Dictionary<VFXSubgraphContext, VFXSubgraphContext>();
subgraphInfos.subgraphs = new List<VFXSubgraphContext>();
foreach (var subgraph in m_Graph.children.OfType<VFXSubgraphContext>().Where(t => t.subgraph != null))
{
subgraphInfos.subgraphs.Add(subgraph);
RecursePutSubgraphParent(subgraphInfos.subgraphParents, subgraphInfos.subgraphs, subgraph);
}
subgraphInfos.spawnerSubgraph = new Dictionary<VFXContext, VFXSubgraphContext>();
foreach (var subgraph in subgraphInfos.subgraphs)
{
foreach (var spawner in subgraph.subChildren.OfType<VFXContext>())
subgraphInfos.spawnerSubgraph.Add(spawner, subgraph);
}
subgraphInfos.contextEffectiveInputLinks = new Dictionary<VFXContext, List<VFXContextLink>[]>();
ComputeEffectiveInputLinks(ref subgraphInfos, compilableContexts.Where(o => o.contextType == VFXContextType.Init || o.contextType == VFXContextType.OutputEvent));
EditorUtility.DisplayProgressBar(progressBarTitle, "Generating Attribute layouts", 6 / nbSteps);
foreach (var data in compilableData)
data.GenerateAttributeLayout(subgraphInfos.contextEffectiveInputLinks);
var generatedCodeData = new List<GeneratedCodeData>();
EditorUtility.DisplayProgressBar(progressBarTitle, "Generating shaders", 7 / nbSteps);
GenerateShaders(generatedCodeData, m_ExpressionGraph, compilableContexts, contextToCompiledData, compilationMode, sourceDependencies);
m_Graph.systemNames.Sync(m_Graph);
EditorUtility.DisplayProgressBar(progressBarTitle, "Saving shaders", 8 / nbSteps);
VFXShaderSourceDesc[] shaderSources = SaveShaderFiles(m_Graph.visualEffectResource, generatedCodeData, contextToCompiledData, m_Graph.systemNames);
var bufferDescs = new List<VFXGPUBufferDesc>();
var temporaryBufferDescs = new List<VFXTemporaryGPUBufferDesc>();
var cpuBufferDescs = new List<VFXCPUBufferDesc>();
var systemDescs = new List<VFXEditorSystemDesc>();
EditorUtility.DisplayProgressBar(progressBarTitle, "Generating systems", 9 / nbSteps);
cpuBufferDescs.Add(new VFXCPUBufferDesc()
{
capacity = 1u,
layout = m_ExpressionGraph.GlobalEventAttributes.ToArray(),
stride = m_ExpressionGraph.GlobalEventAttributes.First().offset.structure,
initialData = ComputeArrayOfStructureInitialData(m_ExpressionGraph.GlobalEventAttributes)
});
var contextSpawnToSpawnInfo = new Dictionary<VFXContext, SpawnInfo>();
FillSpawner(contextSpawnToSpawnInfo, cpuBufferDescs, systemDescs, compilableContexts, m_ExpressionGraph, contextToCompiledData, ref subgraphInfos, m_Graph.systemNames);
var eventDescs = new List<VFXEventDesc>();
FillEvent(eventDescs, contextSpawnToSpawnInfo, compilableContexts, ref subgraphInfos);
var dependentBuffersData = new VFXDependentBuffersData();
FillDependentBuffer(compilableData, bufferDescs, dependentBuffersData);
var contextSpawnToBufferIndex = contextSpawnToSpawnInfo.Select(o => new { o.Key, o.Value.bufferIndex }).ToDictionary(o => o.Key, o => o.bufferIndex);
foreach (var data in compilableData)
{
data.FillDescs(VFXGraph.compileReporter, bufferDescs,
temporaryBufferDescs,
systemDescs,
m_ExpressionGraph,
contextToCompiledData,
contextSpawnToBufferIndex,
dependentBuffersData,
subgraphInfos.contextEffectiveInputLinks,
m_Graph.systemNames);
}
// Early check : OutputEvent should not be duplicated with same name
var outputEventNames = systemDescs.Where(o => o.type == VFXSystemType.OutputEvent).Select(o => o.name);
if (outputEventNames.Count() != outputEventNames.Distinct().Count())
{
throw new InvalidOperationException("There are duplicated entries in OutputEvent");
}
// Update transient renderer settings
ShadowCastingMode shadowCastingMode = compilableContexts.OfType<IVFXSubRenderer>().Any(r => r.hasShadowCasting) ? ShadowCastingMode.On : ShadowCastingMode.Off;
MotionVectorGenerationMode motionVectorGenerationMode = compilableContexts.OfType<IVFXSubRenderer>().Any(r => r.hasMotionVector) ? MotionVectorGenerationMode.Object : MotionVectorGenerationMode.Camera;
EditorUtility.DisplayProgressBar(progressBarTitle, "Setting up systems", 10 / nbSteps);
var expressionSheet = new VFXExpressionSheet();
expressionSheet.expressions = expressionDescs.ToArray();
expressionSheet.expressionsPerSpawnEventAttribute = expressionPerSpawnEventAttributesDescs.ToArray();
expressionSheet.values = valueDescs.OrderBy(o => o.expressionIndex).ToArray();
expressionSheet.exposed = exposedParameterDescs.OrderBy(o => o.name).ToArray();
resource.SetRuntimeData(expressionSheet, systemDescs.ToArray(), eventDescs.ToArray(), bufferDescs.ToArray(), cpuBufferDescs.ToArray(), temporaryBufferDescs.ToArray(), shaderSources, shadowCastingMode, motionVectorGenerationMode, compiledVersion);
m_ExpressionValues = expressionSheet.values;
foreach (var dep in sourceDependencies)
resource.AddSourceDependency(dep);
if (k_FnVFXResource_SetCompileInitialVariants != null)
{
k_FnVFXResource_SetCompileInitialVariants(m_Graph.visualEffectResource, forceShaderValidation);
}
}
catch (Exception e)
{
VisualEffectAsset asset = null;
if (m_Graph.visualEffectResource != null)
asset = m_Graph.visualEffectResource.asset;
Debug.LogError(string.Format("{2} : Exception while compiling expression graph: {0}: {1}", e, e.StackTrace, (asset != null) ? asset.name : "(Null Asset)"), asset);
// Cleaning
if (m_Graph.visualEffectResource != null)
m_Graph.visualEffectResource.ClearRuntimeData();
m_ExpressionGraph = new VFXExpressionGraph();
m_ExpressionValues = new VFXExpressionValueContainerDesc[] {};
}
finally
{
Profiler.EndSample();
EditorUtility.ClearProgressBar();
}
m_Graph.onRuntimeDataChanged?.Invoke(m_Graph);
}
public void UpdateValues()
{
var flatGraph = m_ExpressionGraph.FlattenedExpressions;
var numFlattenedExpressions = flatGraph.Count;
int descIndex = 0;
for (int i = 0; i < numFlattenedExpressions; ++i)
{
var exp = flatGraph[i];
if (exp.Is(VFXExpression.Flags.Value))
{
var desc = m_ExpressionValues[descIndex++];
if (desc.expressionIndex != i)
throw new InvalidOperationException();
switch (exp.valueType)
{
case VFXValueType.Float: SetValueDesc<float>(desc, exp); break;
case VFXValueType.Float2: SetValueDesc<Vector2>(desc, exp); break;
case VFXValueType.Float3: SetValueDesc<Vector3>(desc, exp); break;
case VFXValueType.Float4: SetValueDesc<Vector4>(desc, exp); break;
case VFXValueType.Int32: SetValueDesc<int>(desc, exp); break;
case VFXValueType.Uint32: SetValueDesc<uint>(desc, exp); break;
case VFXValueType.Texture2D:
case VFXValueType.Texture2DArray:
case VFXValueType.Texture3D:
case VFXValueType.TextureCube:
case VFXValueType.TextureCubeArray:
SetObjectValueDesc<Texture>(desc, exp);
break;
case VFXValueType.Matrix4x4: SetValueDesc<Matrix4x4>(desc, exp); break;
case VFXValueType.Curve: SetValueDesc<AnimationCurve>(desc, exp); break;
case VFXValueType.ColorGradient: SetValueDesc<Gradient>(desc, exp); break;
case VFXValueType.Mesh: SetObjectValueDesc<Mesh>(desc, exp); break;
case VFXValueType.SkinnedMeshRenderer: SetObjectValueDesc<SkinnedMeshRenderer>(desc, exp); break;
case VFXValueType.Boolean: SetValueDesc<bool>(desc, exp); break;
default: throw new InvalidOperationException("Invalid type");
}
}
}
m_Graph.visualEffectResource.SetValueSheet(m_ExpressionValues);
}
public VisualEffectResource visualEffectResource
{
get
{
if (m_Graph != null)
{
return m_Graph.visualEffectResource;
}
return null;
}
}
private VFXGraph m_Graph;
[NonSerialized]
private VFXExpressionGraph m_ExpressionGraph;
[NonSerialized]
private VFXExpressionValueContainerDesc[] m_ExpressionValues;
}
}