1744 lines
80 KiB
C#
1744 lines
80 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using UnityEngine;
|
|
using UnityEngine.VFX;
|
|
using UnityEngine.Serialization;
|
|
using static UnityEditor.VFX.VFXSortingUtility;
|
|
|
|
namespace UnityEditor.VFX
|
|
{
|
|
interface ILayoutProvider
|
|
{
|
|
void GenerateAttributeLayout(uint capacity, Dictionary<VFXAttribute, int> storedAttribute);
|
|
string GetCodeOffset(VFXAttribute attrib, uint capacity, string index, string instanceIndex);
|
|
uint GetBufferSize(uint capacity);
|
|
|
|
VFXGPUBufferDesc GetBufferDesc(uint capacity, ComputeBufferMode mode = ComputeBufferMode.Immutable);
|
|
}
|
|
|
|
class StructureOfArrayProvider : ILayoutProvider
|
|
{
|
|
private struct AttributeLayout
|
|
{
|
|
public int bucket;
|
|
public int offset;
|
|
|
|
public AttributeLayout(int bucket, int offset)
|
|
{
|
|
this.bucket = bucket;
|
|
this.offset = offset;
|
|
}
|
|
}
|
|
|
|
// return size
|
|
private int GenerateBucketLayout(List<VFXAttribute> attributes, int bucketId)
|
|
{
|
|
var sortedAttrib = attributes.OrderByDescending(a => VFXValue.TypeToSize(a.type));
|
|
|
|
var attribBlocks = new List<List<VFXAttribute>>();
|
|
foreach (var value in sortedAttrib)
|
|
{
|
|
var block = attribBlocks.FirstOrDefault(b => b.Sum(a => VFXValue.TypeToSize(a.type)) + VFXValue.TypeToSize(value.type) <= 4);
|
|
if (block != null)
|
|
block.Add(value);
|
|
else
|
|
attribBlocks.Add(new List<VFXAttribute>() { value });
|
|
}
|
|
|
|
int currentOffset = 0;
|
|
int minAlignment = 0;
|
|
foreach (var block in attribBlocks)
|
|
{
|
|
foreach (var attrib in block)
|
|
{
|
|
int size = VFXValue.TypeToSize(attrib.type);
|
|
int alignment = size > 2 ? 4 : size;
|
|
minAlignment = Math.Max(alignment, minAlignment);
|
|
// align offset
|
|
currentOffset = (currentOffset + alignment - 1) & ~(alignment - 1);
|
|
m_AttributeLayout.Add(attrib, new AttributeLayout(bucketId, currentOffset));
|
|
currentOffset += size;
|
|
}
|
|
}
|
|
|
|
return (currentOffset + minAlignment - 1) & ~(minAlignment - 1);
|
|
}
|
|
|
|
public void GenerateAttributeLayout(uint capacity, Dictionary<VFXAttribute, int> storedAttribute)
|
|
{
|
|
m_BucketSizes.Clear();
|
|
m_AttributeLayout.Clear();
|
|
m_BucketOffsets.Clear();
|
|
|
|
var attributeBuckets = new Dictionary<int, List<VFXAttribute>>();
|
|
foreach (var kvp in storedAttribute)
|
|
{
|
|
List<VFXAttribute> attributes;
|
|
if (!attributeBuckets.ContainsKey(kvp.Value))
|
|
{
|
|
attributes = new List<VFXAttribute>();
|
|
attributeBuckets[kvp.Value] = attributes;
|
|
}
|
|
else
|
|
attributes = attributeBuckets[kvp.Value];
|
|
|
|
attributes.Add(kvp.Key);
|
|
}
|
|
|
|
int bucketId = 0;
|
|
foreach (var bucket in attributeBuckets)
|
|
{
|
|
int bucketOffset = bucketId == 0 ? 0 : m_BucketOffsets[bucketId - 1] + (int)capacity * m_BucketSizes[bucketId - 1];
|
|
m_BucketOffsets.Add((bucketOffset + 3) & ~3); // align on dword;
|
|
m_BucketSizes.Add(GenerateBucketLayout(bucket.Value, bucketId));
|
|
++bucketId;
|
|
}
|
|
|
|
// Debug log
|
|
if (VFXViewPreference.advancedLogs)
|
|
{
|
|
var builder = new StringBuilder();
|
|
builder.AppendLine("ATTRIBUTE LAYOUT");
|
|
builder.Append(string.Format("NbBuckets:{0} ( ", m_BucketSizes.Count));
|
|
foreach (int size in m_BucketSizes)
|
|
builder.Append(size + " ");
|
|
builder.AppendLine(")");
|
|
foreach (var kvp in m_AttributeLayout)
|
|
builder.AppendLine(string.Format("Attrib:{0} type:{1} bucket:{2} offset:{3}", kvp.Key.name, kvp.Key.type, kvp.Value.bucket, kvp.Value.offset));
|
|
Debug.Log(builder.ToString());
|
|
}
|
|
}
|
|
|
|
public string GetCodeOffset(VFXAttribute attrib, uint capacity, string index, string instanceIndex)
|
|
{
|
|
AttributeLayout layout;
|
|
if (!m_AttributeLayout.TryGetValue(attrib, out layout))
|
|
{
|
|
throw new InvalidOperationException(string.Format("Cannot find attribute {0}", attrib.name));
|
|
}
|
|
return string.Format("(({3} * 0x{4:X}) + ({2} * 0x{0:X} + 0x{1:X})) << 2", m_BucketSizes[layout.bucket], m_BucketOffsets[layout.bucket] + layout.offset, index, instanceIndex, GetBufferSize(capacity));
|
|
}
|
|
|
|
public string GetCodeOffset(VFXAttribute attrib, string index, string eventIndex)
|
|
{
|
|
AttributeLayout layout;
|
|
if (!m_AttributeLayout.TryGetValue(attrib, out layout))
|
|
{
|
|
throw new InvalidOperationException(string.Format("Cannot find attribute {0}", attrib.name));
|
|
}
|
|
return string.Format("(({3} * 0x{4:X}) + ({2} * 0x{0:X} + 0x{1:X})) << 2", m_BucketSizes[layout.bucket], m_BucketOffsets[layout.bucket] + layout.offset, index, eventIndex, (uint)m_BucketSizes.LastOrDefault());
|
|
}
|
|
|
|
public uint GetBufferSize(uint capacity)
|
|
{
|
|
return (uint)m_BucketOffsets.LastOrDefault() + capacity * (uint)m_BucketSizes.LastOrDefault();
|
|
}
|
|
|
|
public VFXGPUBufferDesc GetBufferDesc(uint capacity, ComputeBufferMode mode = ComputeBufferMode.Immutable)
|
|
{
|
|
var layout = m_AttributeLayout.Select(o => new VFXLayoutElementDesc()
|
|
{
|
|
name = o.Key.name,
|
|
type = o.Key.type,
|
|
offset = new VFXLayoutOffset()
|
|
{
|
|
structure = (uint)m_BucketSizes[o.Value.bucket],
|
|
bucket = (uint)m_BucketOffsets[o.Value.bucket],
|
|
element = (uint)o.Value.offset
|
|
}
|
|
});
|
|
return new VFXGPUBufferDesc()
|
|
{
|
|
target = GraphicsBuffer.Target.Raw,
|
|
size = GetBufferSize(capacity),
|
|
stride = 4,
|
|
capacity = capacity,
|
|
layout = layout.ToArray(),
|
|
mode = mode
|
|
};
|
|
}
|
|
|
|
public struct BucketInfo
|
|
{
|
|
public int size;
|
|
public int usedSize;
|
|
public VFXAttribute[] attributes;
|
|
public int[] channels;
|
|
}
|
|
|
|
public BucketInfo[] GetBucketLayoutInfo()
|
|
{
|
|
int count = m_BucketSizes.Count;
|
|
BucketInfo[] buckets = new BucketInfo[count];
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
int size = m_BucketSizes[i];
|
|
buckets[i].size = size;
|
|
buckets[i].usedSize = 0;
|
|
buckets[i].attributes = new VFXAttribute[size];
|
|
buckets[i].channels = new int[size];
|
|
}
|
|
|
|
foreach (var kvp in m_AttributeLayout)
|
|
{
|
|
var attrib = kvp.Key;
|
|
int size = VFXValue.TypeToSize(attrib.type);
|
|
int offset = kvp.Value.offset;
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
buckets[kvp.Value.bucket].attributes[i + offset] = attrib;
|
|
buckets[kvp.Value.bucket].channels[i + offset] = i;
|
|
buckets[kvp.Value.bucket].usedSize = Math.Max(buckets[kvp.Value.bucket].usedSize, i + offset + 1);
|
|
}
|
|
}
|
|
|
|
return buckets;
|
|
}
|
|
|
|
private Dictionary<VFXAttribute, AttributeLayout> m_AttributeLayout = new Dictionary<VFXAttribute, AttributeLayout>();
|
|
private List<int> m_BucketSizes = new List<int>();
|
|
private List<int> m_BucketOffsets = new List<int>();
|
|
}
|
|
|
|
internal enum BoundsSettingMode
|
|
{
|
|
Recorded,
|
|
Manual,
|
|
Automatic,
|
|
}
|
|
|
|
class VFXDataParticle : VFXData, ISpaceable
|
|
{
|
|
public static readonly string k_IndirectBufferName = "indirectBuffer";
|
|
public static readonly string k_SortedIndirectBufferName = "sortedIndirectBuffer";
|
|
|
|
public VFXDataParticle()
|
|
{
|
|
m_GraphValuesLayout.uniformBlocks = new List<List<VFXExpression>>();
|
|
}
|
|
|
|
public override VFXDataType type { get { return hasStrip ? VFXDataType.ParticleStrip : VFXDataType.Particle; } }
|
|
|
|
internal enum DataType
|
|
{
|
|
Particle,
|
|
ParticleStrip
|
|
}
|
|
|
|
[VFXSetting(VFXSettingAttribute.VisibleFlags.InInspector), SerializeField]
|
|
protected DataType dataType = DataType.Particle;
|
|
[VFXSetting, Delayed, SerializeField, FormerlySerializedAs("m_Capacity")]
|
|
[Tooltip("Sets the maximum particle capacity of this system. Particles spawned after the capacity has been reached are discarded.")]
|
|
protected uint capacity = 128;
|
|
[VFXSetting, Delayed, SerializeField]
|
|
protected uint stripCapacity = 1;
|
|
[VFXSetting, Delayed, SerializeField]
|
|
protected uint particlePerStripCount = 128;
|
|
[VFXSetting(VFXSettingAttribute.VisibleFlags.None), SerializeField]
|
|
protected bool needsComputeBounds = false;
|
|
|
|
public bool NeedsComputeBounds() => needsComputeBounds;
|
|
|
|
public bool NeedsComputeBounds(VFXContext context)
|
|
{
|
|
return needsComputeBounds && context == m_Owners.Where(ctx => ctx is VFXBasicUpdate).Last();
|
|
}
|
|
|
|
[FormerlySerializedAs("boundsSettingMode")]
|
|
[VFXSetting(VFXSettingAttribute.VisibleFlags.Default),
|
|
Tooltip("Specifies how the bounds are set. They can be set manually, recorded in the Target GameObject window, or computed automatically at a small performance cost."),
|
|
SerializeField]
|
|
public BoundsSettingMode boundsMode = BoundsSettingMode.Recorded;
|
|
|
|
public bool hasStrip { get { return dataType == DataType.ParticleStrip; } }
|
|
|
|
public override void OnSettingModified(VFXSetting setting)
|
|
{
|
|
base.OnSettingModified(setting);
|
|
|
|
if (setting.name == "capacity" && capacity == 0)
|
|
capacity = 1;
|
|
else if (setting.name == "stripCapacity" && stripCapacity == 0)
|
|
stripCapacity = 1;
|
|
else if (setting.name == "particlePerStripCount" && particlePerStripCount == 0)
|
|
particlePerStripCount = 1;
|
|
else if (setting.name == "boundsMode")
|
|
{
|
|
//Refresh errors on Output contexts
|
|
var allSystemOutputContexts = owners.Where(ctx => ctx is VFXAbstractParticleOutput);
|
|
foreach (var ctx in allSystemOutputContexts)
|
|
{
|
|
ctx.RefreshErrors();
|
|
}
|
|
|
|
if (boundsMode == BoundsSettingMode.Automatic)
|
|
{
|
|
needsComputeBounds = true;
|
|
var graph = GetGraph();
|
|
graph.visualEffectResource.cullingFlags = VFXCullingFlags.CullNone;
|
|
}
|
|
else
|
|
{
|
|
needsComputeBounds = false;
|
|
}
|
|
}
|
|
if (hasStrip)
|
|
{
|
|
if (setting.name == "dataType") // strip has just been set
|
|
{
|
|
stripCapacity = 1;
|
|
particlePerStripCount = capacity;
|
|
}
|
|
capacity = stripCapacity * particlePerStripCount;
|
|
}
|
|
}
|
|
|
|
protected override void OnInvalidate(VFXModel model, InvalidationCause cause)
|
|
{
|
|
base.OnInvalidate(model, cause);
|
|
if (cause == InvalidationCause.kSettingChanged)
|
|
UpdateValidOutputs();
|
|
}
|
|
|
|
protected override IEnumerable<string> filteredOutSettings
|
|
{
|
|
get
|
|
{
|
|
foreach (var s in base.filteredOutSettings)
|
|
yield return s;
|
|
|
|
if (!VFXViewPreference.displayExperimentalOperator) // TODO Name is bad!
|
|
yield return "dataType";
|
|
|
|
if (hasStrip)
|
|
{
|
|
yield return "capacity";
|
|
}
|
|
else
|
|
{
|
|
yield return "stripCapacity";
|
|
yield return "particlePerStripCount";
|
|
}
|
|
}
|
|
}
|
|
|
|
public override IEnumerable<string> additionalHeaders
|
|
{
|
|
get
|
|
{
|
|
if (hasStrip)
|
|
{
|
|
yield return "#define STRIP_COUNT " + stripCapacity + "u";
|
|
yield return "#define PARTICLE_PER_STRIP_COUNT " + particlePerStripCount + "u";
|
|
}
|
|
yield return "#define RAW_CAPACITY " + capacity + "u";
|
|
}
|
|
}
|
|
|
|
private void UpdateValidOutputs()
|
|
{
|
|
var toUnlink = new List<VFXContext>();
|
|
|
|
foreach (var context in owners)
|
|
if (context.contextType == VFXContextType.Output) // Consider only outputs
|
|
{
|
|
var input = context.inputContexts.FirstOrDefault(); // Consider only one input at the moment because this is ensure by the data type (even if it may change in the future)
|
|
if (input != null && (input.outputType & context.inputType) != context.inputType)
|
|
toUnlink.Add(context);
|
|
}
|
|
|
|
foreach (var context in toUnlink)
|
|
context.UnlinkFrom(context.inputContexts.FirstOrDefault());
|
|
}
|
|
|
|
public uint alignedCapacity
|
|
{
|
|
get
|
|
{
|
|
uint paddedCapacity = capacity;
|
|
const uint kThreadPerGroup = 64;
|
|
if (paddedCapacity > kThreadPerGroup)
|
|
paddedCapacity = (uint)((paddedCapacity + kThreadPerGroup - 1) & ~(kThreadPerGroup - 1)); // multiple of kThreadPerGroup
|
|
|
|
return (paddedCapacity + 3u) & ~3u; // Align on 4 boundary
|
|
}
|
|
}
|
|
|
|
public uint ComputeSourceCount(Dictionary<VFXContext, List<VFXContextLink>[]> effectiveFlowInputLinks)
|
|
{
|
|
var init = owners.FirstOrDefault(o => o.contextType == VFXContextType.Init);
|
|
|
|
if (init == null)
|
|
return 0u;
|
|
|
|
var cpuCount = effectiveFlowInputLinks[init].SelectMany(t => t.Select(u => u.context)).Where(o => o.contextType == VFXContextType.Spawner).Count();
|
|
var gpuCount = effectiveFlowInputLinks[init].SelectMany(t => t.Select(u => u.context)).Where(o => o.contextType == VFXContextType.SpawnerGPU).Count();
|
|
|
|
if (cpuCount != 0 && gpuCount != 0)
|
|
{
|
|
throw new InvalidOperationException("Cannot mix GPU & CPU spawners in init");
|
|
}
|
|
|
|
if (cpuCount > 0)
|
|
{
|
|
return (uint)cpuCount;
|
|
}
|
|
else if (gpuCount > 0)
|
|
{
|
|
if (gpuCount > 1)
|
|
{
|
|
throw new InvalidOperationException("Don't support multiple GPU event (for now)");
|
|
}
|
|
var parent = m_DependenciesIn.OfType<VFXDataParticle>().FirstOrDefault();
|
|
return parent != null ? parent.capacity : 0u;
|
|
}
|
|
return init != null ? (uint)effectiveFlowInputLinks[init].SelectMany(t => t.Select(u => u.context)).Where(o => o.contextType == VFXContextType.Spawner /* Explicitly ignore spawner gpu */).Count() : 0u;
|
|
}
|
|
|
|
public uint attributeBufferSize
|
|
{
|
|
get
|
|
{
|
|
return m_layoutAttributeCurrent.GetBufferSize(alignedCapacity);
|
|
}
|
|
}
|
|
|
|
public VFXGPUBufferDesc attributeBufferDesc
|
|
{
|
|
get
|
|
{
|
|
return m_layoutAttributeCurrent.GetBufferDesc(alignedCapacity);
|
|
}
|
|
}
|
|
|
|
public VFXSpace space
|
|
{
|
|
get { return m_Space; }
|
|
set { m_Space = value; Modified(false); }
|
|
}
|
|
|
|
public override bool CanBeCompiled()
|
|
{
|
|
// Has enough contexts and capacity
|
|
if (m_Owners.Count < 1 || capacity <= 0)
|
|
return false;
|
|
|
|
// Has a initialize
|
|
if (m_Owners[0].contextType != VFXContextType.Init)
|
|
return false;
|
|
|
|
// Has a spawner
|
|
if (m_Owners[0].inputContexts.FirstOrDefault() == null)
|
|
return false;
|
|
|
|
// Has an output
|
|
if (m_Owners.Last().contextType == VFXContextType.Output)
|
|
return true;
|
|
|
|
// Has a least one dependent compilable system
|
|
if (m_Owners.SelectMany(c => c.allLinkedOutputSlot)
|
|
.Select(s => ((VFXModel)s.owner).GetFirstOfType<VFXContext>())
|
|
.Any(c => c.CanBeCompiled()))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
public override VFXDeviceTarget GetCompilationTarget(VFXContext context)
|
|
{
|
|
return VFXDeviceTarget.GPU;
|
|
}
|
|
|
|
uint m_SourceCount = 0xFFFFFFFFu;
|
|
|
|
public override uint staticSourceCount
|
|
{
|
|
get
|
|
{
|
|
return m_SourceCount;
|
|
}
|
|
}
|
|
|
|
public bool hasDynamicSourceCount
|
|
{
|
|
get
|
|
{
|
|
return m_Contexts.Any(
|
|
o => o.contextType == VFXContextType.Init
|
|
&& o.inputFlowSlot.Any(flow => flow.link.Any(link => link.context.contextType == VFXContextType.Event)));
|
|
}
|
|
}
|
|
|
|
public override void GenerateAttributeLayout(Dictionary<VFXContext, List<VFXContextLink>[]> effectiveFlowInputLinks)
|
|
{
|
|
m_layoutAttributeCurrent.GenerateAttributeLayout(alignedCapacity, m_StoredCurrentAttributes);
|
|
m_SourceCount = ComputeSourceCount(effectiveFlowInputLinks);
|
|
|
|
var parent = m_DependenciesIn.OfType<VFXDataParticle>().FirstOrDefault();
|
|
if (parent != null)
|
|
{
|
|
m_layoutAttributeSource.GenerateAttributeLayout(parent.alignedCapacity, parent.m_StoredCurrentAttributes);
|
|
m_ownAttributeSourceBuffer = false;
|
|
}
|
|
else
|
|
{
|
|
var readSourceAttribute = m_ReadSourceAttributes.ToDictionary(o => o, _ => (int)VFXAttributeMode.ReadSource);
|
|
m_layoutAttributeSource.GenerateAttributeLayout(m_SourceCount, readSourceAttribute);
|
|
m_ownAttributeSourceBuffer = true;
|
|
}
|
|
}
|
|
|
|
public override string GetAttributeDataDeclaration(VFXAttributeMode mode)
|
|
{
|
|
if (m_StoredCurrentAttributes.Count == 0)
|
|
return string.Empty;
|
|
else if ((mode & VFXAttributeMode.Write) != 0)
|
|
return "RWByteAddressBuffer attributeData;";
|
|
else
|
|
return "ByteAddressBuffer attributeData;";
|
|
}
|
|
|
|
private string GetCastAttributePrefix(VFXAttribute attrib)
|
|
{
|
|
if (VFXExpression.IsFloatValueType(attrib.type))
|
|
return "asfloat";
|
|
return "";
|
|
}
|
|
|
|
private string GetByteAddressBufferMethodSuffix(VFXAttribute attrib)
|
|
{
|
|
int size = VFXExpression.TypeToSize(attrib.type);
|
|
if (size == 1)
|
|
return string.Empty;
|
|
else if (size <= 4)
|
|
return size.ToString();
|
|
else
|
|
throw new ArgumentException(string.Format("Attribute {0} of type {1} cannot be handled in ByteAddressBuffer due to its size of {2}", attrib.name, attrib.type, size));
|
|
}
|
|
|
|
public override string GetLoadAttributeCode(VFXAttribute attrib, VFXAttributeLocation location)
|
|
{
|
|
var attributeStore = location == VFXAttributeLocation.Current ? m_layoutAttributeCurrent : m_layoutAttributeSource;
|
|
var attributeBuffer = location == VFXAttributeLocation.Current ? "attributeBuffer" : "sourceAttributeBuffer";
|
|
var parent = m_DependenciesIn.OfType<VFXDataParticle>().FirstOrDefault();
|
|
|
|
uint attributeCapacity;
|
|
if (location == VFXAttributeLocation.Current)
|
|
attributeCapacity = alignedCapacity;
|
|
else
|
|
attributeCapacity = (parent != null) ? parent.capacity : staticSourceCount;
|
|
|
|
var index = location == VFXAttributeLocation.Current ? "index" : "sourceIndex";
|
|
|
|
if (location == VFXAttributeLocation.Current && !m_StoredCurrentAttributes.ContainsKey(attrib))
|
|
throw new ArgumentException(string.Format("Attribute {0} does not exist in data layout", attrib.name));
|
|
|
|
if (location == VFXAttributeLocation.Source && !m_ReadSourceAttributes.Any(a => a.name == attrib.name))
|
|
throw new ArgumentException(string.Format("Attribute {0} does not exist in data layout", attrib.name));
|
|
|
|
string codeOffset = location == VFXAttributeLocation.Current
|
|
? attributeStore.GetCodeOffset(attrib, attributeCapacity, index, "instanceIndex")
|
|
: attributeStore.GetCodeOffset(attrib, index, "startEventIndex");
|
|
return string.Format("{0}({3}.Load{1}({2}))", GetCastAttributePrefix(attrib), GetByteAddressBufferMethodSuffix(attrib), codeOffset, attributeBuffer);
|
|
}
|
|
|
|
public override string GetStoreAttributeCode(VFXAttribute attrib, string value)
|
|
{
|
|
if (!m_StoredCurrentAttributes.ContainsKey(attrib))
|
|
throw new ArgumentException(string.Format("Attribute {0} does not exist in data layout", attrib.name));
|
|
|
|
return string.Format("attributeBuffer.Store{0}({1},{3}({2}))",
|
|
GetByteAddressBufferMethodSuffix(attrib),
|
|
m_layoutAttributeCurrent.GetCodeOffset(attrib, alignedCapacity, "index", "instanceIndex"),
|
|
value, attrib.type == VFXValueType.Boolean ? "uint" : "asuint");
|
|
}
|
|
|
|
public override IEnumerable<VFXContext> InitImplicitContexts()
|
|
{
|
|
var contexts = compilableOwners.ToList();
|
|
|
|
m_Contexts = new List<VFXContext>(contexts.Count + 2); // Allocate max number
|
|
int index = 0;
|
|
|
|
int updateIndex = Int32.MaxValue;
|
|
// First add init and updates
|
|
for (index = 0; index < contexts.Count; ++index)
|
|
{
|
|
if (contexts[index].contextType == VFXContextType.Update)
|
|
updateIndex = index;
|
|
|
|
if (contexts[index].contextType == VFXContextType.Output)
|
|
break;
|
|
m_Contexts.Add(contexts[index]);
|
|
}
|
|
bool hasMainUpdate = updateIndex != Int32.MaxValue;
|
|
|
|
//Reset needsOwnSort and needsOwnAabbBuffer flags
|
|
for (int outputIndex = index; outputIndex < contexts.Count; ++outputIndex)
|
|
{
|
|
var currentOutputContext = contexts[outputIndex];
|
|
var abstractParticleOutput = currentOutputContext as VFXAbstractParticleOutput;
|
|
if (abstractParticleOutput == null)
|
|
continue;
|
|
abstractParticleOutput.needsOwnSort = false;
|
|
abstractParticleOutput.needsOwnAabbBuffer = false;
|
|
}
|
|
var implicitContext = new List<VFXContext>();
|
|
|
|
bool needsGlobalSort = NeedsGlobalSort(out var globalSortCriterion);
|
|
//Issues a global sort when at least two outputs have the same criterion.
|
|
//If others don't match the criterion, or have a compute cull pass, they need a per output sort.
|
|
if (needsGlobalSort)
|
|
{
|
|
// Then the camera sort
|
|
var globalSort = VFXContext.CreateImplicitContext<VFXGlobalSort>(this);
|
|
SetContextSortCriteria(ref globalSort, globalSortCriterion);
|
|
implicitContext.Add(globalSort);
|
|
m_Contexts.Add(globalSort);
|
|
}
|
|
|
|
//AABB Buffer rules :
|
|
var rayTracedOutputs = compilableOwners.OfType<VFXAbstractParticleOutput>().Where(o => o.isRayTraced).ToArray();
|
|
var outputsWithoutAabbModifs = new List<VFXAbstractParticleOutput>();
|
|
if (rayTracedOutputs.Length > 0)
|
|
{
|
|
foreach (var output in rayTracedOutputs)
|
|
{
|
|
if (output.ModifiesAabbAttributes())
|
|
{
|
|
output.needsOwnAabbBuffer = true;
|
|
}
|
|
else
|
|
outputsWithoutAabbModifs.Add(output);
|
|
}
|
|
}
|
|
|
|
if (outputsWithoutAabbModifs.Count > 0 && hasMainUpdate)
|
|
{
|
|
var updateContext = (VFXBasicUpdate)contexts[updateIndex];
|
|
var firstEligibleOutput = outputsWithoutAabbModifs[0];
|
|
uint sharedDecimationFactor = firstEligibleOutput.GetRaytracingDecimationFactor();
|
|
updateContext.rayTracingDefines = firstEligibleOutput.rayTracingDefines;
|
|
for (var i = 1; i < outputsWithoutAabbModifs.Count; i++)
|
|
{
|
|
if (outputsWithoutAabbModifs[i].GetRaytracingDecimationFactor() != sharedDecimationFactor
|
|
|| !firstEligibleOutput.HasSameRayTracingScalingMode(outputsWithoutAabbModifs[i]) )
|
|
{
|
|
outputsWithoutAabbModifs[i].needsOwnAabbBuffer = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//additional update
|
|
for (int outputIndex = index; outputIndex < contexts.Count; ++outputIndex)
|
|
{
|
|
var currentOutputContext = contexts[outputIndex];
|
|
var abstractParticleOutput = currentOutputContext as VFXAbstractParticleOutput;
|
|
if (abstractParticleOutput == null)
|
|
continue;
|
|
|
|
abstractParticleOutput.needsOwnSort = OutputNeedsOwnSort(abstractParticleOutput, globalSortCriterion, hasMainUpdate);
|
|
if (abstractParticleOutput.NeedsOutputUpdate())
|
|
{
|
|
var update = VFXContext.CreateImplicitContext<VFXOutputUpdate>(this);
|
|
update.SetOutput(abstractParticleOutput);
|
|
implicitContext.Add(update);
|
|
m_Contexts.Add(update);
|
|
}
|
|
}
|
|
|
|
// And finally output
|
|
for (; index < contexts.Count; ++index)
|
|
m_Contexts.Add(contexts[index]);
|
|
|
|
return implicitContext;
|
|
}
|
|
|
|
public bool NeedsIndirectBuffer()
|
|
{
|
|
return compilableOwners.OfType<VFXAbstractParticleOutput>().Any(o => o.HasIndirectDraw());
|
|
}
|
|
|
|
public bool NeedsGlobalIndirectBuffer()
|
|
{
|
|
return compilableOwners.OfType<VFXAbstractParticleOutput>().Any(o => o.HasIndirectDraw() && !VFXOutputUpdate.HasFeature(o.outputUpdateFeatures, VFXOutputUpdate.Features.IndirectDraw));
|
|
}
|
|
|
|
public bool NeedsSharedAabbBuffer()
|
|
{
|
|
return compilableOwners.OfType<VFXAbstractParticleOutput>().Any(o => !o.NeedsOwnAabbBuffer() && o.isRayTraced);
|
|
}
|
|
|
|
private void PrepareAABBBuffers(out List<VFXAbstractParticleOutput> outputsSharingAABB,
|
|
out Dictionary<VFXAbstractParticleOutput, int> outputsOwningAABB,
|
|
out Dictionary<VFXAbstractParticleOutput, uint> outputAabbSize,
|
|
out int sharedAabbBufferIndex,
|
|
out uint sharedAabbCount,
|
|
ref List<VFXMapping> systemBufferMappings,
|
|
ref List<VFXGPUBufferDesc> outBufferDescs)
|
|
{
|
|
outputsSharingAABB = new List<VFXAbstractParticleOutput>();
|
|
outputsOwningAABB = new Dictionary<VFXAbstractParticleOutput, int>();
|
|
outputAabbSize = new Dictionary<VFXAbstractParticleOutput, uint>();
|
|
List<VFXAbstractParticleOutput> listOutputsOwningAABB = new List<VFXAbstractParticleOutput>();
|
|
sharedAabbCount = 0u;
|
|
|
|
|
|
sharedAabbBufferIndex = -1;
|
|
var rayTracedOutputs = compilableOwners.OfType<VFXAbstractParticleOutput>().Where(o => o.isRayTraced).ToArray();
|
|
if (rayTracedOutputs.Length == 0)
|
|
return;
|
|
foreach (var output in rayTracedOutputs)
|
|
{
|
|
if (output.NeedsOwnAabbBuffer())
|
|
listOutputsOwningAABB.Add(output);
|
|
else
|
|
outputsSharingAABB.Add(output);
|
|
}
|
|
|
|
int sharedAABBCount = outputsSharingAABB.Count;
|
|
if (sharedAABBCount > 0)
|
|
{
|
|
uint sharedDecimationFactor = outputsSharingAABB[0].GetRaytracingDecimationFactor();
|
|
uint aabbBufferCount = (capacity + sharedDecimationFactor - 1) / sharedDecimationFactor;
|
|
sharedAabbBufferIndex = outBufferDescs.Count;
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { target = GraphicsBuffer.Target.Structured, size = 0u, stride = 24 });
|
|
systemBufferMappings.Add(new VFXMapping("aabbBuffer", sharedAabbBufferIndex));
|
|
sharedAabbCount = aabbBufferCount;
|
|
}
|
|
|
|
int outputId = 0;
|
|
foreach (var output in listOutputsOwningAABB)
|
|
{
|
|
uint aabbBufferCount = (capacity + output.GetRaytracingDecimationFactor() - 1) / output.GetRaytracingDecimationFactor();
|
|
int bufferIndex = outBufferDescs.Count;
|
|
outputsOwningAABB.Add(output, bufferIndex);
|
|
outputAabbSize.Add(output, aabbBufferCount);
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { target = GraphicsBuffer.Target.Structured, size = 0u, stride = 24 });
|
|
systemBufferMappings.Add(new VFXMapping("aabbBuffer" + outputId++, bufferIndex));
|
|
}
|
|
}
|
|
|
|
public bool NeedsGlobalSort()
|
|
{
|
|
bool hasMainUpdate = compilableOwners.OfType<VFXBasicUpdate>().Any();
|
|
int sharedCriterionCount = GetGlobalSortingCriterionAndVoteCount(out var globalSortCriterion);
|
|
return hasMainUpdate && sharedCriterionCount >= 2;
|
|
}
|
|
|
|
public bool NeedsGlobalSort(out SortingCriterion globalSortCriterion)
|
|
{
|
|
bool hasMainUpdate = compilableOwners.OfType<VFXBasicUpdate>().Any();
|
|
if (!hasMainUpdate)
|
|
{
|
|
globalSortCriterion = null;
|
|
return false;
|
|
}
|
|
int sharedCriterionCount = GetGlobalSortingCriterionAndVoteCount(out globalSortCriterion);
|
|
return sharedCriterionCount >= 2;
|
|
}
|
|
|
|
int GetGlobalSortingCriterionAndVoteCount(out SortingCriterion globalSortCriterion)
|
|
{
|
|
var globalSortedCandidates = compilableOwners.OfType<VFXAbstractParticleOutput>()
|
|
.Where(o => o.CanBeCompiled() && o.HasSorting() && !VFXOutputUpdate.HasFeature(o.outputUpdateFeatures, VFXOutputUpdate.Features.IndirectDraw)).ToArray();
|
|
if (!globalSortedCandidates.Any())
|
|
{
|
|
globalSortCriterion = null;
|
|
return 0;
|
|
}
|
|
|
|
Func<VFXAbstractParticleOutput, SortingCriterion> getVoteFunc = VFXSortingUtility.GetVoteFunc;
|
|
var voteResult = MajorityVote(globalSortedCandidates, getVoteFunc, new SortingCriteriaComparer());
|
|
globalSortCriterion = voteResult.Value >= 2 ? voteResult.Key : null;
|
|
return voteResult.Value;
|
|
}
|
|
|
|
public override void FillDescs(
|
|
IVFXErrorReporter reporter,
|
|
VFXCompilationMode compilationMode,
|
|
List<VFXGPUBufferDesc> outBufferDescs,
|
|
List<VFXTemporaryGPUBufferDesc> outTemporaryBufferDescs,
|
|
List<VFXEditorSystemDesc> outSystemDescs,
|
|
VFXExpressionGraph expressionGraph,
|
|
VFXCompiledData compiledData,
|
|
IEnumerable<VFXContext> compilableContexts,
|
|
Dictionary<VFXContext, int> contextSpawnToBufferIndex,
|
|
VFXDependentBuffersData dependentBuffers,
|
|
Dictionary<VFXContext, List<VFXContextLink>[]> effectiveFlowInputLinks,
|
|
Dictionary<VFXData, uint> dataToSystemIndex,
|
|
VFXSystemNames systemNames = null)
|
|
{
|
|
bool hasKill = IsAttributeStored(VFXAttribute.Alive);
|
|
|
|
var deadListBufferIndex = -1;
|
|
|
|
var systemBufferMappings = new List<VFXMapping>();
|
|
var systemValueMappings = new List<VFXMapping>();
|
|
|
|
var attributeBufferIndex = dependentBuffers.attributeBuffers[this];
|
|
|
|
int attributeSourceBufferIndex = -1;
|
|
int eventGPUFrom = -1;
|
|
|
|
var stripDataIndex = -1;
|
|
|
|
int instancingIndirectAndActiveIndirectBufferIndex = -1;
|
|
|
|
if (m_DependenciesIn.Any())
|
|
{
|
|
if (m_DependenciesIn.Count != 1)
|
|
{
|
|
throw new InvalidOperationException("Unexpected multiple input dependency for GPU event");
|
|
}
|
|
|
|
var dependency = m_DependenciesIn.First();
|
|
|
|
attributeSourceBufferIndex = dependentBuffers.attributeBuffers[dependency];
|
|
eventGPUFrom = dependentBuffers.eventBuffers[this];
|
|
|
|
systemValueMappings.Add(new VFXMapping("parentSystemIndex", (int)dataToSystemIndex[dependency]));
|
|
}
|
|
var systemFlag = VFXSystemFlag.SystemDefault;
|
|
if (attributeBufferIndex != -1)
|
|
{
|
|
systemFlag |= VFXSystemFlag.SystemHasAttributeBuffer;
|
|
systemBufferMappings.Add(new VFXMapping("attributeBuffer", attributeBufferIndex));
|
|
}
|
|
if (m_ownAttributeSourceBuffer)
|
|
{
|
|
if (attributeSourceBufferIndex != -1)
|
|
{
|
|
throw new InvalidOperationException("Unexpected source while filling description of data particle");
|
|
}
|
|
|
|
attributeSourceBufferIndex = outBufferDescs.Count;
|
|
outBufferDescs.Add(m_layoutAttributeSource.GetBufferDesc(staticSourceCount, ComputeBufferMode.Dynamic));
|
|
}
|
|
|
|
if (attributeSourceBufferIndex != -1)
|
|
{
|
|
systemBufferMappings.Add(new VFXMapping("sourceAttributeBuffer", attributeSourceBufferIndex));
|
|
}
|
|
|
|
if (eventGPUFrom != -1)
|
|
{
|
|
systemFlag |= VFXSystemFlag.SystemReceivedEventGPU;
|
|
systemBufferMappings.Add(new VFXMapping("eventList", eventGPUFrom));
|
|
}
|
|
|
|
if (hasKill)
|
|
{
|
|
systemFlag |= VFXSystemFlag.SystemHasKill;
|
|
|
|
if (!hasStrip) // No dead list for strips
|
|
{
|
|
deadListBufferIndex = outBufferDescs.Count;
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { target = GraphicsBuffer.Target.Structured, size = capacity + 2, stride = 4 }); //capacity + 2 for the two counters
|
|
systemBufferMappings.Add(new VFXMapping("deadList", deadListBufferIndex));
|
|
}
|
|
}
|
|
|
|
if (hasStrip)
|
|
{
|
|
systemFlag |= VFXSystemFlag.SystemHasStrips;
|
|
|
|
systemValueMappings.Add(new VFXMapping("stripCount", (int)stripCapacity));
|
|
systemValueMappings.Add(new VFXMapping("particlePerStripCount", (int)particlePerStripCount));
|
|
|
|
stripDataIndex = dependentBuffers.stripBuffers[this];
|
|
systemBufferMappings.Add(new VFXMapping("stripDataBuffer", stripDataIndex));
|
|
}
|
|
|
|
bool hasInstancing = true;
|
|
if (hasInstancing)
|
|
{
|
|
instancingIndirectAndActiveIndirectBufferIndex = outBufferDescs.Count;
|
|
outBufferDescs.Add(new VFXGPUBufferDesc()); // description will be filled at the end when knowning split descs size.
|
|
systemBufferMappings.Add(new VFXMapping("instancingIndirectAndActiveIndirect", instancingIndirectAndActiveIndirectBufferIndex));
|
|
}
|
|
|
|
if (hasDynamicSourceCount)
|
|
{
|
|
systemFlag |= VFXSystemFlag.SystemHasDirectLink;
|
|
}
|
|
|
|
if (needsComputeBounds || boundsMode == BoundsSettingMode.Automatic)
|
|
{
|
|
systemFlag |= VFXSystemFlag.SystemNeedsComputeBounds;
|
|
|
|
var boundsBufferIndex = dependentBuffers.boundsBuffers[this];
|
|
systemBufferMappings.Add(new VFXMapping("boundsBuffer", boundsBufferIndex));
|
|
}
|
|
|
|
if (boundsMode == BoundsSettingMode.Automatic)
|
|
{
|
|
systemFlag |= VFXSystemFlag.SystemAutomaticBounds;
|
|
}
|
|
|
|
if (space == VFXSpace.World)
|
|
{
|
|
systemFlag |= VFXSystemFlag.SystemInWorldSpace;
|
|
}
|
|
|
|
//Particle systems allow use of instanced rendering
|
|
systemFlag |= VFXSystemFlag.SystemUsesInstancedRendering;
|
|
|
|
var initContext = m_Contexts.FirstOrDefault(o => o.contextType == VFXContextType.Init);
|
|
if (initContext != null)
|
|
systemBufferMappings.AddRange(effectiveFlowInputLinks[initContext]
|
|
.SelectMany(t => t.Select(u => u.context))
|
|
.Where(o => o.contextType == VFXContextType.Spawner)
|
|
.Select(o => new VFXMapping("spawner_input", contextSpawnToBufferIndex[o])));
|
|
if (m_Contexts.Count() > 0 && m_Contexts.First().contextType == VFXContextType.Init) // TODO This test can be removed once we ensure priorly the system is valid
|
|
{
|
|
var firstTaskOfContext = compiledData.contextToCompiledData[m_Contexts.First()].tasks.First();
|
|
var mapper = compiledData.taskToCompiledData[firstTaskOfContext].cpuMapper;
|
|
|
|
var boundsCenterExp = mapper.FromNameAndId("bounds_center", -1);
|
|
var boundsSizeExp = mapper.FromNameAndId("bounds_size", -1);
|
|
var boundsPaddingExp = mapper.FromNameAndId("boundsPadding", -1);
|
|
|
|
int boundsCenterIndex = boundsCenterExp != null ? expressionGraph.GetFlattenedIndex(boundsCenterExp) : -1;
|
|
int boundsSizeIndex = boundsSizeExp != null ? expressionGraph.GetFlattenedIndex(boundsSizeExp) : -1;
|
|
int boundsPaddingIndex = boundsPaddingExp != null ? expressionGraph.GetFlattenedIndex(boundsPaddingExp) : -1;
|
|
|
|
if (boundsCenterIndex != -1 && boundsSizeIndex != -1)
|
|
{
|
|
systemValueMappings.Add(new VFXMapping("bounds_center", boundsCenterIndex));
|
|
systemValueMappings.Add(new VFXMapping("bounds_size", boundsSizeIndex));
|
|
}
|
|
if (boundsPaddingIndex != -1)
|
|
{
|
|
systemValueMappings.Add(new VFXMapping("boundsPadding", boundsPaddingIndex));
|
|
}
|
|
}
|
|
|
|
systemValueMappings.Add(new VFXMapping("graphValuesOffset", systemValueMappings.Count + 1));
|
|
foreach (var uniform in m_GraphValuesLayout.uniformBlocks.SelectMany(o => o))
|
|
systemValueMappings.Add(new VFXMapping(m_SystemUniformMapper.GetName(uniform), expressionGraph.GetFlattenedIndex(uniform)));
|
|
|
|
HashSet<VFXContext> indirectOutputHasTaskDependency = null;
|
|
|
|
bool needsIndirectBuffer = NeedsIndirectBuffer();
|
|
int globalIndirectBufferIndex = -1;
|
|
bool needsGlobalIndirectBuffer = false;
|
|
if (needsIndirectBuffer)
|
|
{
|
|
indirectOutputHasTaskDependency = new();
|
|
|
|
// First link the tasks that output an indirect buffer to the particle output
|
|
foreach (var output in m_Contexts.OfType<VFXAbstractParticleOutput>())
|
|
{
|
|
var outputTasks = compiledData.contextToCompiledData[output].tasks;
|
|
|
|
// Find the VFXTask providing the indirect buffer to the output.
|
|
// In case the Output has multiple tasks and one of them uses an indirect buffer, it has the priority over the OutputUpate
|
|
|
|
foreach (var task in Enumerable.Reverse(outputTasks))
|
|
{
|
|
if ((task.type & VFXTaskType.Update) != 0)
|
|
{
|
|
if (task.needsIndirectBuffer)
|
|
{
|
|
indirectOutputHasTaskDependency.Add(output);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var cullCompute in m_Contexts.OfType<VFXOutputUpdate>())
|
|
if (cullCompute.HasFeature(VFXOutputUpdate.Features.IndirectDraw))
|
|
{
|
|
if (cullCompute.output.contextType != VFXContextType.Output)
|
|
throw new Exception("Context types expect to be an output.");
|
|
|
|
// If the has only one output task, then we find the last task using the indirect buffer in the OutputUpdate
|
|
if (!indirectOutputHasTaskDependency.Contains(cullCompute.output))
|
|
{
|
|
var indirectOutputUpdateTask = Enumerable.Reverse(compiledData.contextToCompiledData[cullCompute].tasks).FirstOrDefault(t => t.needsIndirectBuffer);
|
|
if (indirectOutputUpdateTask == null)
|
|
throw new InvalidOperationException("The type " + cullCompute + " did not return a task using indirect buffer even though the output requires it.");
|
|
indirectOutputHasTaskDependency.Add(cullCompute.output);
|
|
}
|
|
}
|
|
|
|
needsGlobalIndirectBuffer = NeedsGlobalIndirectBuffer();
|
|
if (needsGlobalIndirectBuffer)
|
|
{
|
|
globalIndirectBufferIndex = outBufferDescs.Count;
|
|
systemBufferMappings.Add(new VFXMapping("indirectBuffer0", outBufferDescs.Count));
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { target = GraphicsBuffer.Target.Structured, size = capacity + 1, stride = 4 });
|
|
}
|
|
}
|
|
|
|
// Flatten all the buffer descriptors
|
|
List<VFXContextBufferDescriptor> bufferDescriptorList = new();
|
|
foreach (var context in m_Contexts)
|
|
{
|
|
foreach (var buffer in compiledData.contextToCompiledData[context].buffers)
|
|
bufferDescriptorList.Add(buffer);
|
|
}
|
|
|
|
Dictionary<string, int> bufferNameToIndex = new();
|
|
Dictionary<VFXTask, int> taskGroups = new(); // Identify groups of tasks that shares the same buffers
|
|
int groupIndex = 0;
|
|
foreach (var cullCompute in m_Contexts.OfType<VFXOutputUpdate>())
|
|
if (cullCompute.HasFeature(VFXOutputUpdate.Features.IndirectDraw))
|
|
{
|
|
// Gather all the tasks in the update + output and assign them to the same group so they don't share buffers with another pair.
|
|
foreach (var task in compiledData.contextToCompiledData[cullCompute.output].tasks)
|
|
taskGroups[task] = groupIndex;
|
|
foreach (var task in compiledData.contextToCompiledData[cullCompute].tasks)
|
|
taskGroups[task] = groupIndex;
|
|
|
|
groupIndex++;
|
|
}
|
|
|
|
if (needsIndirectBuffer)
|
|
{
|
|
// Assign a task group to tasks coming from an output without VFXOutputUpdate but that have an indirect buffer
|
|
foreach (var output in indirectOutputHasTaskDependency)
|
|
{
|
|
bool incrementGroupIndex = false;
|
|
foreach (var task in compiledData.contextToCompiledData[output].tasks)
|
|
{
|
|
if (taskGroups.ContainsKey(task)) //output already handled through its VFXOutputUpdate above.
|
|
break;
|
|
incrementGroupIndex = true;
|
|
taskGroups[task] = groupIndex;
|
|
}
|
|
|
|
if (incrementGroupIndex)
|
|
groupIndex++;
|
|
}
|
|
}
|
|
|
|
// Allocate the buffers based on their binding order in the tasks
|
|
uint prefixIndex = 0;
|
|
foreach (var context in m_Contexts)
|
|
{
|
|
foreach (var task in compiledData.contextToCompiledData[context].tasks)
|
|
{
|
|
foreach (var bufferMapping in task.bufferMappings)
|
|
{
|
|
var bufferDescriptors = compiledData.contextToCompiledData[context].buffers;
|
|
if (context is VFXAbstractParticleOutput particleOutput)
|
|
foreach (var outputUpdate in m_Contexts.OfType<VFXOutputUpdate>().Where(o => o.output == particleOutput))
|
|
bufferDescriptors.AddRange(compiledData.contextToCompiledData[outputUpdate].buffers);
|
|
|
|
// Add fallback descriptors at the end of the list in case a pass needs a buffer not declared in output update or output
|
|
bufferDescriptors.AddRange(bufferDescriptorList);
|
|
|
|
// Find the buffer descriptor from it's name:
|
|
var bufferDescriptor = bufferDescriptors.FirstOrDefault(b => b.baseName == bufferMapping.bufferName);
|
|
if (bufferDescriptor.baseName == null)
|
|
continue;
|
|
|
|
string name = bufferDescriptor.baseName;
|
|
if (taskGroups.TryGetValue(task, out var taskGroupIndex))
|
|
name += taskGroupIndex;
|
|
|
|
if (bufferNameToIndex.ContainsKey(name))
|
|
continue;
|
|
|
|
bufferNameToIndex[name] = outBufferDescs.Count;
|
|
for (int i = 0; i < bufferDescriptor.bufferCount; i++)
|
|
{
|
|
string bufferName = $"{bufferDescriptor.baseName + i}_{VFXCodeGeneratorHelper.GeneratePrefix(prefixIndex++)}";
|
|
if (bufferDescriptor.isPerCamera)
|
|
bufferName += "PerCamera";
|
|
if (bufferDescriptor.includeInSystemMappings)
|
|
systemBufferMappings.Add(new VFXMapping(bufferName, outBufferDescs.Count));
|
|
|
|
uint size;
|
|
switch (bufferDescriptor.bufferSizeMode)
|
|
{
|
|
default:
|
|
case VFXContextBufferSizeMode.ScaleWithCapacity:
|
|
size = (uint)Math.Ceiling(capacity * (double)bufferDescriptor.capacityScaleMultiplier);
|
|
break;
|
|
case VFXContextBufferSizeMode.FixedSize:
|
|
size = bufferDescriptor.size;
|
|
break;
|
|
case VFXContextBufferSizeMode.FixedSizePlusScaleWithCapacity:
|
|
size = (uint)Math.Ceiling(capacity * (double)bufferDescriptor.capacityScaleMultiplier) + bufferDescriptor.size;
|
|
break;
|
|
}
|
|
|
|
outBufferDescs.Add(new VFXGPUBufferDesc
|
|
{
|
|
target = bufferDescriptor.bufferTarget, size = size, stride = bufferDescriptor.stride
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int GetBufferIndex(VFXTask task, string baseName)
|
|
{
|
|
if (taskGroups.TryGetValue(task, out var taskGroupIndex))
|
|
baseName += taskGroupIndex;
|
|
|
|
bufferNameToIndex.TryGetValue(baseName, out int index);
|
|
return index;
|
|
}
|
|
|
|
// Duplicate indirect buffers in case there are mulitple outputs
|
|
|
|
|
|
int graphValuesBufferIndex = -1;
|
|
int instancesPrefixSumBufferIndex = -1;
|
|
int spawnBufferIndex = -1;
|
|
if (hasInstancing)
|
|
{
|
|
FillGraphValuesBuffers(outBufferDescs, systemBufferMappings, m_GraphValuesLayout, out graphValuesBufferIndex);
|
|
|
|
FillPrefixSumBuffers(outBufferDescs, systemBufferMappings, staticSourceCount,
|
|
out instancesPrefixSumBufferIndex,
|
|
out spawnBufferIndex);
|
|
}
|
|
|
|
// sort buffers
|
|
int sortBufferAIndex = -1;
|
|
int sortBufferBIndex = -1;
|
|
bool needsGlobalSort = NeedsGlobalSort();
|
|
if (needsGlobalSort)
|
|
{
|
|
sortBufferAIndex = outBufferDescs.Count;
|
|
sortBufferBIndex = sortBufferAIndex + 1;
|
|
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { target = GraphicsBuffer.Target.Structured, size = capacity + 1, stride = 8 });
|
|
systemBufferMappings.Add(new VFXMapping("sortBufferA", sortBufferAIndex));
|
|
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { target = GraphicsBuffer.Target.Structured, size = capacity + 1, stride = 8 });
|
|
systemBufferMappings.Add(new VFXMapping("sortBufferB", sortBufferBIndex));
|
|
}
|
|
|
|
var elementToVFXBufferMotionVector = new Dictionary<VFXContext, int>();
|
|
foreach (VFXOutputUpdate context in m_Contexts.OfType<VFXOutputUpdate>())
|
|
{
|
|
if (context.HasFeature(VFXOutputUpdate.Features.MotionVector))
|
|
{
|
|
uint sizePerElement = 12U * 4U;
|
|
if (context.output.SupportsMotionVectorPerVertex(out uint vertsCount))
|
|
{
|
|
// 2 floats per vertex
|
|
sizePerElement = vertsCount * 2U * 4U;
|
|
}
|
|
// add previous frame index
|
|
sizePerElement += 4U;
|
|
int currentElementToVFXBufferMotionVector = outTemporaryBufferDescs.Count;
|
|
outTemporaryBufferDescs.Add(new VFXTemporaryGPUBufferDesc() { frameCount = 2u, desc = new VFXGPUBufferDesc { target = GraphicsBuffer.Target.Raw, size = capacity * sizePerElement, stride = 4 } });
|
|
elementToVFXBufferMotionVector.Add(context.output, currentElementToVFXBufferMotionVector);
|
|
}
|
|
}
|
|
|
|
PrepareAABBBuffers(out List<VFXAbstractParticleOutput> outputsSharingAABB,
|
|
out Dictionary<VFXAbstractParticleOutput, int> outputsOwningAABB,
|
|
out Dictionary<VFXAbstractParticleOutput, uint> outputAabbSize,
|
|
out int sharedAabbBufferIndex,
|
|
out uint sharedAabbSize,
|
|
ref systemBufferMappings,
|
|
ref outBufferDescs);
|
|
|
|
bool hasAnyRaytraced = outputsSharingAABB.Any() || outputsOwningAABB.Any();
|
|
if (hasAnyRaytraced)
|
|
systemFlag |= VFXSystemFlag.SystemIsRayTraced;
|
|
|
|
|
|
var taskDescs = new List<VFXEditorTaskDesc>();
|
|
var bufferMappings = new List<VFXMapping>();
|
|
var uniformMappings = new List<VFXMapping>();
|
|
var additionalParameters = new List<VFXMapping>();
|
|
var instanceSplitDescs = new List<VFXInstanceSplitDesc>();
|
|
|
|
AddInstanceSplitDesc(instanceSplitDescs, new List<uint>());
|
|
|
|
List<(VFXContext context, VFXTask task, VFXContextCompiledData contextCompiledData, long sortKey)> sortedTaskList = new();
|
|
|
|
for (int i = 0; i < m_Contexts.Count; ++i)
|
|
{
|
|
var context = m_Contexts[i];
|
|
var contextCompiledData = compiledData.contextToCompiledData[context];
|
|
|
|
var tasks = contextCompiledData.tasks;
|
|
|
|
if (!tasks.Any(t => compiledData.taskToCompiledData.ContainsKey(t)))
|
|
throw new InvalidOperationException("Unexpected context which hasn't been compiled : " + context);
|
|
|
|
foreach (var task in tasks)
|
|
{
|
|
long genericType = (int)task.type & 0xF0000000;
|
|
long sortKey = (genericType << 32) | (uint)i;
|
|
sortedTaskList.Add((context, task, contextCompiledData, sortKey));
|
|
}
|
|
}
|
|
|
|
// Ensures that the outputs are always after all the per camera update tasks while keeping the original declaration order of the contexts
|
|
sortedTaskList = sortedTaskList.OrderBy(t => t.sortKey).ToList();
|
|
m_ContextsToTaskIndex.Clear();
|
|
|
|
foreach (var (context, task, contextCompiledData, contextIndex) in sortedTaskList)
|
|
{
|
|
var temporaryBufferMappings = new List<VFXMappingTemporary>();
|
|
|
|
bufferMappings.Clear();
|
|
additionalParameters.Clear();
|
|
|
|
if (context is VFXOutputUpdate update)
|
|
{
|
|
if (update.HasFeature(VFXOutputUpdate.Features.MotionVector))
|
|
{
|
|
var currentIndex = elementToVFXBufferMotionVector[update.output];
|
|
temporaryBufferMappings.Add(new VFXMappingTemporary() { pastFrameIndex = 0u, perCameraBuffer = true, mapping = new VFXMapping("elementToVFXBuffer", currentIndex) });
|
|
}
|
|
if (update.HasFeature(VFXOutputUpdate.Features.FillRaytracingAABB) && outputsOwningAABB.ContainsKey(update.output))
|
|
{
|
|
bufferMappings.Add(new VFXMapping("aabbBuffer", outputsOwningAABB[update.output]));
|
|
additionalParameters.Add(new VFXMapping("aabbBufferCount", (int)outputAabbSize[update.output]));
|
|
}
|
|
}
|
|
else if (context.contextType == VFXContextType.Output && (context is IVFXSubRenderer) && (context as IVFXSubRenderer).hasMotionVector)
|
|
{
|
|
var currentIndex = elementToVFXBufferMotionVector[context];
|
|
temporaryBufferMappings.Add(new VFXMappingTemporary() { pastFrameIndex = 1u, perCameraBuffer = true, mapping = new VFXMapping("elementToVFXBufferPrevious", currentIndex) });
|
|
}
|
|
|
|
if (attributeBufferIndex != -1)
|
|
bufferMappings.Add(new VFXMapping("attributeBuffer", attributeBufferIndex));
|
|
|
|
if (graphValuesBufferIndex != -1)
|
|
bufferMappings.Add(new VFXMapping("graphValuesBuffer", graphValuesBufferIndex));
|
|
|
|
if (eventGPUFrom != -1 && context.contextType == VFXContextType.Init)
|
|
bufferMappings.Add(new VFXMapping("eventList", eventGPUFrom));
|
|
|
|
if (deadListBufferIndex != -1 && (context.taskType == VFXTaskType.Initialize || context.taskType == VFXTaskType.Update))
|
|
bufferMappings.Add(new VFXMapping("deadList", deadListBufferIndex));
|
|
|
|
if (attributeSourceBufferIndex != -1 && context.contextType == VFXContextType.Init)
|
|
bufferMappings.Add(new VFXMapping("sourceAttributeBuffer", attributeSourceBufferIndex));
|
|
|
|
if (stripDataIndex != -1 && context.ownedType == VFXDataType.ParticleStrip)
|
|
bufferMappings.Add(new VFXMapping("stripDataBuffer", stripDataIndex));
|
|
|
|
if (sharedAabbBufferIndex != -1 && (context.contextType == VFXContextType.Update ||
|
|
outputsSharingAABB.Contains(context)))
|
|
{
|
|
bufferMappings.Add(new VFXMapping("aabbBuffer", sharedAabbBufferIndex));
|
|
additionalParameters.Add(new VFXMapping("aabbBufferCount", (int)sharedAabbSize));
|
|
}
|
|
|
|
if (context is VFXAbstractParticleOutput output && outputsOwningAABB.ContainsKey(output))
|
|
{
|
|
bufferMappings.Add(new VFXMapping("aabbBuffer", outputsOwningAABB[output]));
|
|
additionalParameters.Add(new VFXMapping("aabbBufferCount", (int)outputAabbSize[output]));
|
|
}
|
|
|
|
if (context.contextType == VFXContextType.Init)
|
|
{
|
|
if(spawnBufferIndex != -1)
|
|
bufferMappings.Add(new VFXMapping("spawnBuffer", spawnBufferIndex));
|
|
}
|
|
|
|
if (hasInstancing)
|
|
{
|
|
bool needsInstancePrefixSum = context.contextType == VFXContextType.Init;
|
|
needsInstancePrefixSum |= !hasKill && task.doesGenerateShader && task.shaderType == VFXTaskShaderType.ComputeShader;
|
|
if (instancesPrefixSumBufferIndex != -1 && needsInstancePrefixSum)
|
|
bufferMappings.Add(new VFXMapping("instancingPrefixSum", instancesPrefixSumBufferIndex));
|
|
|
|
bool mapIndirectBuffers = contextCompiledData.tasks.Any(t => (t.type & (VFXTaskType.Update | VFXTaskType.Initialize)) != 0);
|
|
mapIndirectBuffers |= (context.contextType & (VFXContextType.Init | VFXContextType.Update | VFXContextType.Filter)) != 0;
|
|
|
|
if (mapIndirectBuffers)
|
|
{
|
|
if (instancingIndirectAndActiveIndirectBufferIndex != -1)
|
|
bufferMappings.Add(new VFXMapping("instancingIndirectAndActiveIndirect", instancingIndirectAndActiveIndirectBufferIndex));
|
|
}
|
|
}
|
|
|
|
bool hasAttachedStrip = IsAttributeStored(VFXAttribute.StripAlive);
|
|
if (hasAttachedStrip)
|
|
{
|
|
var stripData = dependenciesOut.First(d => ((VFXDataParticle)d).hasStrip); // TODO Handle several strip attached
|
|
bufferMappings.Add(new VFXMapping("attachedStripDataBuffer", dependentBuffers.stripBuffers[stripData]));
|
|
}
|
|
|
|
if (needsIndirectBuffer && task.needsIndirectBuffer)
|
|
{
|
|
systemFlag |= VFXSystemFlag.SystemHasIndirectBuffer;
|
|
|
|
if ((task.type & VFXTaskType.Output) != 0 && context is VFXAbstractParticleOutput outputContext && outputContext.HasIndirectDraw())
|
|
{
|
|
bool hasUpdateTask = indirectOutputHasTaskDependency.Contains(context);
|
|
|
|
int sortBufferIndex = hasUpdateTask ? (outputContext.HasSorting() ? GetBufferIndex(task, k_SortedIndirectBufferName) : GetBufferIndex(task, k_IndirectBufferName)) : globalIndirectBufferIndex;
|
|
int indirectBufferIndex = hasUpdateTask ? GetBufferIndex(task, k_IndirectBufferName) : globalIndirectBufferIndex;
|
|
additionalParameters.Add(new VFXMapping("indirectIndex", indirectBufferIndex == -1 ? 0 : indirectBufferIndex));
|
|
bufferMappings.Add(new VFXMapping(k_IndirectBufferName, sortBufferIndex == -1 ? 0 : sortBufferIndex));
|
|
}
|
|
}
|
|
|
|
if (context.contextType == VFXContextType.Update)
|
|
{
|
|
if (context.taskType == VFXTaskType.Update && needsGlobalIndirectBuffer)
|
|
bufferMappings.Add(new VFXMapping(k_IndirectBufferName, globalIndirectBufferIndex));
|
|
}
|
|
|
|
if (context.contextType == VFXContextType.Filter)
|
|
{
|
|
if (context.taskType == VFXTaskType.GlobalSort && needsGlobalIndirectBuffer)
|
|
bufferMappings.Add(new VFXMapping("inputBuffer", globalIndirectBufferIndex));
|
|
}
|
|
|
|
// Generate task mappings from the required buffers
|
|
foreach (var map in task.bufferMappings)
|
|
{
|
|
int index = GetBufferIndex(task, map.bufferName);
|
|
if (index == -1)
|
|
continue;
|
|
|
|
if (map.useBufferCountIndexInName && context is VFXOutputUpdate outputUpdate)
|
|
{
|
|
for (int j = 0; j < outputUpdate.bufferCount; j++) // TODO: we can refactor this by generating the list of buffers directly in the PrepareCompiledData()
|
|
bufferMappings.Add(new VFXMapping(map.mappingName + j, index + j));
|
|
}
|
|
else
|
|
{
|
|
// Check for duplicated mapping
|
|
if (bufferMappings.All(m => m.name != map.mappingName))
|
|
bufferMappings.Add(new VFXMapping(map.mappingName, index));
|
|
}
|
|
}
|
|
if (deadListBufferIndex != -1 && context.contextType == VFXContextType.Output && (context as VFXAbstractParticleOutput).NeedsDeadListCount())
|
|
bufferMappings.Add(new VFXMapping("deadList", deadListBufferIndex));
|
|
|
|
if (context.taskType == VFXTaskType.GlobalSort)
|
|
{
|
|
bufferMappings.Add(new VFXMapping("outputBuffer", sortBufferAIndex));
|
|
}
|
|
|
|
var contextData = compiledData.taskToCompiledData[task];
|
|
for (uint indexTarget = 0; indexTarget < (uint)contextData.linkedEventOut.Length; ++indexTarget)
|
|
{
|
|
var gpuTarget = dependentBuffers.eventBuffers[contextData.linkedEventOut[indexTarget].data];
|
|
var prefix = VFXCodeGeneratorHelper.GeneratePrefix(indexTarget);
|
|
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))
|
|
{
|
|
instancingSplitDescValues.Add((uint)index);
|
|
}
|
|
var name = contextData.uniformMapper.GetName(buffer);
|
|
uniformMappings.Add(new VFXMapping(name, index));
|
|
}
|
|
foreach (var texture in contextData.uniformMapper.textures)
|
|
{
|
|
int index = expressionGraph.GetFlattenedIndex(texture);
|
|
if (!texture.IsAny(VFXExpression.Flags.Constant | VFXExpression.Flags.Foldable))
|
|
{
|
|
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))
|
|
uniformMappings.Add(new VFXMapping(name, index));
|
|
}
|
|
|
|
// Retrieve all cpu mappings at context level (-1)
|
|
var cpuMappings = contextData.cpuMapper.CollectExpression(-1).Select(exp => new VFXMapping(exp.name, expressionGraph.GetFlattenedIndex(exp.exp))).ToArray();
|
|
|
|
//Check potential issue with invalid operation on CPU
|
|
foreach (var mapping in cpuMappings)
|
|
{
|
|
if (mapping.index < 0)
|
|
{
|
|
reporter?.RegisterError("GPUNodeLinkedTOCPUSlot", VFXErrorType.Error, "Can not link a GPU operator to a system wide (CPU) input.", context.GetSlotByPath(true, mapping.name));
|
|
throw new InvalidOperationException("Can not link a GPU operator to a system wide (CPU) input: " + mapping.name);
|
|
}
|
|
}
|
|
|
|
var taskDesc = new VFXEditorTaskDesc();
|
|
taskDesc.type = (UnityEngine.VFX.VFXTaskType)task.type;
|
|
|
|
taskDesc.buffers = bufferMappings.ToArray();
|
|
taskDesc.temporaryBuffers = temporaryBufferMappings.ToArray();
|
|
taskDesc.values = uniformMappings.OrderBy(mapping => mapping.index).ToArray();
|
|
taskDesc.parameters = cpuMappings.Concat(contextData.parameters).Concat(additionalParameters).ToArray();
|
|
taskDesc.instanceSplitIndex = AddInstanceSplitDesc(instanceSplitDescs, instancingSplitDescValues);
|
|
taskDesc.shaderSourceIndex = compiledData.taskToCompiledData[task].indexInShaderSource;
|
|
taskDesc.model = context;
|
|
taskDesc.usesMaterialVariant = compilationMode == VFXCompilationMode.Edition && context.usesMaterialVariantInEditMode;
|
|
|
|
if (context is IVFXMultiMeshOutput multiMeshOutput && multiMeshOutput.meshCount > 0) // If the context is a multi mesh output, split and patch task desc into several tasks
|
|
{
|
|
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);
|
|
AddTaskDesc(taskDescs, singleMeshTaskDesc, context);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AddTaskDesc(taskDescs, taskDesc, context);
|
|
}
|
|
|
|
// if task is a per output update with sorting, add sort tasks
|
|
// TODO: Replace this hardcoded pass by a task in the OutputUpdate context.
|
|
if (context is VFXOutputUpdate outUpdate)
|
|
{
|
|
if (outUpdate.HasFeature(VFXOutputUpdate.Features.CameraSort) || outUpdate.HasFeature(VFXOutputUpdate.Features.Sort))
|
|
{
|
|
for (int j = 0; j < outUpdate.bufferCount; ++j)
|
|
{
|
|
VFXEditorTaskDesc sortTaskDesc = new VFXEditorTaskDesc();
|
|
sortTaskDesc.type = UnityEngine.VFX.VFXTaskType.PerOutputSort;
|
|
sortTaskDesc.externalProcessor = null;
|
|
sortTaskDesc.model = context;
|
|
|
|
sortTaskDesc.buffers = new VFXMapping[3];
|
|
sortTaskDesc.buffers[0] = new VFXMapping("srcBuffer", GetBufferIndex(task, k_IndirectBufferName) + j);
|
|
if (capacity > 4096) // Add scratch buffer
|
|
{
|
|
sortTaskDesc.buffers[1] = new VFXMapping("scratchBuffer", outBufferDescs.Count);
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { target = GraphicsBuffer.Target.Structured, size = capacity + 1, stride = 8 });
|
|
}
|
|
else
|
|
sortTaskDesc.buffers[1] = new VFXMapping("scratchBuffer", -1); // No scratchBuffer needed
|
|
sortTaskDesc.buffers[2] = new VFXMapping("dstBuffer", GetBufferIndex(task, k_SortedIndirectBufferName) + j);
|
|
|
|
sortTaskDesc.parameters = new VFXMapping[2];
|
|
sortTaskDesc.parameters[0] = new VFXMapping("globalSort", 0);
|
|
sortTaskDesc.parameters[1] = new VFXMapping("isPerCameraSort", outUpdate.isPerCamera ? 1 : 0);
|
|
|
|
AddTaskDesc(taskDescs, sortTaskDesc, outUpdate.output);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
outBufferDescs[instancingIndirectAndActiveIndirectBufferIndex] = new VFXGPUBufferDesc() { target = GraphicsBuffer.Target.Structured, size = 1u + (uint)instanceSplitDescs.Count() , stride = 4, mode = ComputeBufferMode.Dynamic };
|
|
outBufferDescs[instancesPrefixSumBufferIndex] = new VFXGPUBufferDesc() { target = GraphicsBuffer.Target.Structured, size = (uint)instanceSplitDescs.Count() + 1u, stride = 4, mode = ComputeBufferMode.Dynamic };
|
|
|
|
if (hasStrip && hasKill)
|
|
{
|
|
var lastUpdateContext = m_Contexts.OfType<VFXBasicUpdate>().LastOrDefault();
|
|
if (lastUpdateContext != null)
|
|
{
|
|
if (m_ContextsToTaskIndex.TryGetValue(lastUpdateContext, out List<TaskProfilingData> tasksIndices))
|
|
{
|
|
TaskProfilingData taskProfilingData = new TaskProfilingData()
|
|
{ taskIndex = taskDescs.Count, taskName = "Update Strips" };
|
|
tasksIndices.Add(taskProfilingData);
|
|
}
|
|
}
|
|
}
|
|
|
|
string nativeName = string.Empty;
|
|
if (systemNames != null)
|
|
nativeName = systemNames.GetUniqueSystemName(this);
|
|
else
|
|
throw new InvalidOperationException("system names manager cannot be null");
|
|
|
|
outSystemDescs.Add(new VFXEditorSystemDesc()
|
|
{
|
|
flags = systemFlag,
|
|
tasks = taskDescs.ToArray(),
|
|
capacity = capacity,
|
|
name = nativeName,
|
|
buffers = systemBufferMappings.ToArray(),
|
|
values = systemValueMappings.ToArray(),
|
|
instanceSplitDescs = instanceSplitDescs.ToArray(),
|
|
type = VFXSystemType.Particle,
|
|
layer = m_Layer
|
|
});
|
|
}
|
|
|
|
void AddTaskDesc(List<VFXEditorTaskDesc> taskDescs, VFXEditorTaskDesc taskDesc, VFXContext context)
|
|
{
|
|
TaskProfilingData taskProfilingData = new TaskProfilingData()
|
|
{ taskIndex = taskDescs.Count, taskName = taskDesc.type.ToString() };
|
|
|
|
VFXContext visualContext;
|
|
if (context is VFXOutputUpdate outputUpdate)
|
|
{
|
|
visualContext = outputUpdate.output;
|
|
}
|
|
else if (context is VFXGlobalSort)
|
|
{
|
|
visualContext = m_ContextsToTaskIndex.Keys.FirstOrDefault(o => o is VFXBasicUpdate);
|
|
}
|
|
else
|
|
{
|
|
visualContext = context;
|
|
}
|
|
|
|
if (m_ContextsToTaskIndex.TryGetValue(visualContext, out List<TaskProfilingData> tasksIndices))
|
|
{
|
|
tasksIndices.Add(taskProfilingData);
|
|
}
|
|
else
|
|
{
|
|
m_ContextsToTaskIndex.Add(visualContext, new List<TaskProfilingData>() {taskProfilingData});
|
|
}
|
|
taskDescs.Add(taskDesc);
|
|
}
|
|
|
|
private void FillGraphValuesBuffers(List<VFXGPUBufferDesc> outBufferDescs, List<VFXMapping> systemBufferMappings, GraphValuesLayout graphValuesLayout, out int graphValuesIndex)
|
|
{
|
|
var graphValuesSize = graphValuesLayout.paddedSizeInBytes / 4;
|
|
graphValuesIndex = outBufferDescs.Count;
|
|
outBufferDescs.Add(new VFXGPUBufferDesc()
|
|
{
|
|
target = GraphicsBuffer.Target.Raw, size = graphValuesSize, stride = 4u, mode = ComputeBufferMode.Dynamic
|
|
});
|
|
systemBufferMappings.Add(new VFXMapping("graphValuesBuffer", graphValuesIndex));
|
|
}
|
|
|
|
private static void FillPrefixSumBuffers(List<VFXGPUBufferDesc> outBufferDescs, List<VFXMapping> systemBufferMappings, uint staticSourceCount,
|
|
out int instancesPrefixSumBufferIndex,
|
|
out int spawnBufferIndex)
|
|
{
|
|
instancesPrefixSumBufferIndex = outBufferDescs.Count;
|
|
outBufferDescs.Add(new VFXGPUBufferDesc()); // description will be filled at the end when knowning split descs size.
|
|
systemBufferMappings.Add(new VFXMapping("instancingPrefixSum", instancesPrefixSumBufferIndex));
|
|
|
|
spawnBufferIndex = outBufferDescs.Count;
|
|
uint spawnCountSize = Math.Max(staticSourceCount, 1u);
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { target = GraphicsBuffer.Target.Structured, size = spawnCountSize + 1u, stride = 4, mode = ComputeBufferMode.Dynamic });
|
|
systemBufferMappings.Add(new VFXMapping("spawnBuffer", spawnBufferIndex));
|
|
}
|
|
|
|
public override void OnEnable()
|
|
{
|
|
base.OnEnable();
|
|
if ((int)m_Space == int.MaxValue)
|
|
{
|
|
m_Space = VFXSpace.None;
|
|
}
|
|
}
|
|
|
|
private static uint AddInstanceSplitDesc(List<VFXInstanceSplitDesc> instanceSplitDescs, List<uint> instanceSplitDescValues)
|
|
{
|
|
int index = -1;
|
|
|
|
instanceSplitDescValues.Sort();
|
|
|
|
for (int i = 0; i < instanceSplitDescs.Count; ++i)
|
|
{
|
|
if (instanceSplitDescValues.SequenceEqual(instanceSplitDescs[i].values))
|
|
{
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
if (index < 0)
|
|
{
|
|
index = instanceSplitDescs.Count;
|
|
var newEntry = new VFXInstanceSplitDesc();
|
|
newEntry.values = instanceSplitDescValues.ToArray();
|
|
instanceSplitDescs.Add(newEntry);
|
|
}
|
|
return (uint)index;
|
|
}
|
|
|
|
public override void Sanitize(int version)
|
|
{
|
|
if (version < 8)
|
|
{
|
|
SetSettingValue("boundsMode", BoundsSettingMode.Manual);
|
|
}
|
|
|
|
if(boundsMode != BoundsSettingMode.Automatic && needsComputeBounds)
|
|
SetSettingValue(nameof(needsComputeBounds), false);
|
|
|
|
if (version < 12 && (int)m_Space == int.MaxValue)
|
|
{
|
|
m_Space = VFXSpace.None;
|
|
Debug.LogError("Unexpected space none detected in VFXDataParticle");
|
|
}
|
|
|
|
base.Sanitize(version);
|
|
}
|
|
|
|
public override void CopySettings<T>(T dst)
|
|
{
|
|
var instance = dst as VFXDataParticle;
|
|
instance.m_Space = m_Space;
|
|
}
|
|
|
|
public StructureOfArrayProvider.BucketInfo[] GetCurrentAttributeLayout()
|
|
{
|
|
return m_layoutAttributeCurrent.GetBucketLayoutInfo();
|
|
}
|
|
|
|
public StructureOfArrayProvider.BucketInfo[] GetSourceAttributeLayout()
|
|
{
|
|
return m_layoutAttributeSource.GetBucketLayoutInfo();
|
|
}
|
|
|
|
[SerializeField]
|
|
private VFXSpace m_Space; // TODO Should be an actual setting
|
|
[NonSerialized]
|
|
private StructureOfArrayProvider m_layoutAttributeCurrent = new StructureOfArrayProvider();
|
|
[NonSerialized]
|
|
private StructureOfArrayProvider m_layoutAttributeSource = new StructureOfArrayProvider();
|
|
[NonSerialized]
|
|
private bool m_ownAttributeSourceBuffer;
|
|
|
|
[NonSerialized]
|
|
private VFXUniformMapper m_SystemUniformMapper;
|
|
[NonSerialized]
|
|
private GraphValuesLayout m_GraphValuesLayout;
|
|
|
|
public VFXUniformMapper systemUniformMapper => m_SystemUniformMapper;
|
|
|
|
public struct GraphValuesLayout
|
|
{
|
|
public List<List<VFXExpression>> uniformBlocks;
|
|
public Dictionary<string, int> nameToOffset;
|
|
public uint paddedSizeInBytes;
|
|
|
|
private static readonly int kAlignement = 4;
|
|
private static readonly int kContextDataOffset = 16;
|
|
public void SetUniformBlocks(List<VFXExpression> orderedUniforms)
|
|
{
|
|
if (uniformBlocks == null)
|
|
{
|
|
uniformBlocks = new List<List<VFXExpression>>();
|
|
}
|
|
else
|
|
{
|
|
uniformBlocks.Clear();
|
|
}
|
|
foreach (var value in orderedUniforms)
|
|
{
|
|
var block = uniformBlocks.FirstOrDefault(b =>
|
|
b.Sum(e => VFXValue.TypeToSize(e.valueType)) + VFXValue.TypeToSize(value.valueType) <= kAlignement);
|
|
if (block != null)
|
|
block.Add(value);
|
|
else
|
|
uniformBlocks.Add(new List<VFXExpression>() { value });
|
|
}
|
|
}
|
|
|
|
private static int ComputePadding(int offset)
|
|
{
|
|
return (kAlignement - (offset % kAlignement)) % kAlignement;
|
|
}
|
|
|
|
public void GenerateOffsetMap(VFXUniformMapper systemUniformMapper)
|
|
{
|
|
int mapSize = uniformBlocks.Sum(o => o.Count);
|
|
nameToOffset = new Dictionary<string, int>(mapSize);
|
|
int currentOffset = kContextDataOffset;
|
|
foreach (var block in uniformBlocks)
|
|
{
|
|
int currentBlockSize = 0;
|
|
foreach (var value in block)
|
|
{
|
|
string name = systemUniformMapper.GetName(value);
|
|
|
|
if (nameToOffset.ContainsKey(name))
|
|
{
|
|
throw new ArgumentException(
|
|
"Uniform name should not appear twice in the graph values offset map");
|
|
}
|
|
nameToOffset.Add(name, currentOffset);
|
|
int typeSize = VFXExpression.TypeToSize(value.valueType);
|
|
currentOffset += sizeof(uint) * typeSize;
|
|
currentBlockSize += typeSize;
|
|
}
|
|
currentOffset += sizeof(uint) * ComputePadding(currentBlockSize);
|
|
}
|
|
paddedSizeInBytes = (uint)currentOffset;
|
|
}
|
|
}
|
|
|
|
public GraphValuesLayout graphValuesLayout
|
|
{
|
|
get { return m_GraphValuesLayout; }
|
|
set { m_GraphValuesLayout = value; }
|
|
}
|
|
|
|
public void GenerateSystemUniformMapper(VFXExpressionGraph graph, VFXCompiledData compiledData, ref Dictionary<VFXContext, VFXExpressionMapper> gpuMappers)
|
|
{
|
|
VFXUniformMapper uniformMapper = null;
|
|
foreach (var context in m_Contexts)
|
|
{
|
|
var gpuMapper = graph.BuildGPUMapper(context);
|
|
gpuMappers[context] = gpuMapper;
|
|
var contextUniformMapper = new VFXUniformMapper(gpuMapper, context.doesGenerateShader, true);
|
|
|
|
// SG inputs if needed
|
|
var shaderGraph = VFXShaderGraphHelpers.GetShaderGraph(context);
|
|
VFXSGInputs contextSGInputs = null;
|
|
if (shaderGraph)
|
|
{
|
|
var firstTaskOfContext = compiledData.contextToCompiledData[context].tasks.First();
|
|
var cpuMapper = compiledData.taskToCompiledData[firstTaskOfContext].cpuMapper;
|
|
|
|
contextSGInputs = new VFXSGInputs(cpuMapper, gpuMapper, contextUniformMapper, shaderGraph);
|
|
if (contextSGInputs.IsEmpty())
|
|
contextSGInputs = null;
|
|
}
|
|
|
|
// Add gpu and uniform mapper
|
|
foreach (var task in compiledData.contextToCompiledData[context].tasks)
|
|
{
|
|
var taskData = compiledData.taskToCompiledData[task];
|
|
taskData.gpuMapper = gpuMapper;
|
|
taskData.uniformMapper = contextUniformMapper;
|
|
taskData.SGInputs = contextSGInputs;
|
|
|
|
compiledData.taskToCompiledData[task] = taskData;
|
|
|
|
if (uniformMapper == null)
|
|
uniformMapper = new VFXUniformMapper(gpuMapper, true, true);
|
|
else
|
|
{
|
|
uniformMapper.AppendMapper(gpuMapper);
|
|
}
|
|
}
|
|
}
|
|
|
|
m_SystemUniformMapper = uniformMapper;
|
|
m_GraphValuesLayout = new GraphValuesLayout();
|
|
var orderedUniforms = new List<VFXExpression>(m_SystemUniformMapper?.uniforms
|
|
.Where(e => !e.IsAny(VFXExpression.Flags.Constant |
|
|
VFXExpression.Flags.InvalidOnCPU)) // Filter out constant expressions
|
|
.OrderByDescending(e => VFXValue.TypeToSize(e.valueType)));
|
|
|
|
m_GraphValuesLayout.SetUniformBlocks(orderedUniforms);
|
|
m_GraphValuesLayout.GenerateOffsetMap(m_SystemUniformMapper);
|
|
}
|
|
|
|
internal override void GenerateErrors(VFXErrorReporter report)
|
|
{
|
|
base.GenerateErrors(report);
|
|
|
|
if (boundsMode == BoundsSettingMode.Automatic)
|
|
{
|
|
if (CanBeCompiled())
|
|
report.RegisterError("WarningAutomaticBoundsFlagChange", VFXErrorType.Warning,
|
|
$"Changing the bounds mode to Automatic modifies the Culling Flags on the Visual Effect Asset to Always recompute bounds and simulate.", this);
|
|
}
|
|
}
|
|
}
|
|
}
|