969 lines
43 KiB
C#
969 lines
43 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using UnityEngine;
|
|
using UnityEditor.VFX;
|
|
using UnityEngine.VFX;
|
|
using UnityEngine.Serialization;
|
|
|
|
namespace UnityEditor.VFX
|
|
{
|
|
interface ILayoutProvider
|
|
{
|
|
void GenerateAttributeLayout(uint capacity, Dictionary<VFXAttribute, int> storedAttribute);
|
|
string GetCodeOffset(VFXAttribute attrib, string index);
|
|
uint GetBufferSize(uint capacity);
|
|
|
|
VFXGPUBufferDesc GetBufferDesc(uint capacity);
|
|
}
|
|
|
|
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, string index)
|
|
{
|
|
AttributeLayout layout;
|
|
if (!m_AttributeLayout.TryGetValue(attrib, out layout))
|
|
{
|
|
throw new InvalidOperationException(string.Format("Cannot find attribute {0}", attrib.name));
|
|
}
|
|
return string.Format("({2} * 0x{0:X} + 0x{1:X}) << 2", m_BucketSizes[layout.bucket], m_BucketOffsets[layout.bucket] + layout.offset, index);
|
|
}
|
|
|
|
public uint GetBufferSize(uint capacity)
|
|
{
|
|
return (uint)m_BucketOffsets.LastOrDefault() + capacity * (uint)m_BucketSizes.LastOrDefault();
|
|
}
|
|
|
|
public VFXGPUBufferDesc GetBufferDesc(uint capacity)
|
|
{
|
|
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()
|
|
{
|
|
type = ComputeBufferType.Raw,
|
|
size = GetBufferSize(capacity),
|
|
stride = 4,
|
|
capacity = capacity,
|
|
layout = layout.ToArray()
|
|
};
|
|
}
|
|
|
|
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>();
|
|
}
|
|
|
|
class VFXDataParticle : VFXData, ISpaceable
|
|
{
|
|
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;
|
|
|
|
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;
|
|
|
|
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";
|
|
}
|
|
}
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
private 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 VFXCoordinateSpace 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 sourceCount
|
|
{
|
|
get
|
|
{
|
|
return m_SourceCount;
|
|
}
|
|
}
|
|
|
|
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 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));
|
|
|
|
return string.Format("{0}({3}.Load{1}({2}))", GetCastAttributePrefix(attrib), GetByteAddressBufferMethodSuffix(attrib), attributeStore.GetCodeOffset(attrib, index), 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, "index"), value, attrib.type == VFXValueType.Boolean ? "uint" : "asuint");
|
|
}
|
|
|
|
public override IEnumerable<VFXContext> InitImplicitContexts()
|
|
{
|
|
if (!NeedsGlobalSort() &&
|
|
!m_Owners.OfType<VFXAbstractParticleOutput>().Any(o => o.NeedsOutputUpdate()))
|
|
{
|
|
//Early out with the most common case
|
|
m_Contexts = m_Owners;
|
|
return Enumerable.Empty<VFXContext>();
|
|
}
|
|
|
|
m_Contexts = new List<VFXContext>(m_Owners.Count + 2); // Allocate max number
|
|
int index = 0;
|
|
|
|
// First add init and updates
|
|
for (index = 0; index < m_Owners.Count; ++index)
|
|
{
|
|
if ((m_Owners[index].contextType == VFXContextType.Output))
|
|
break;
|
|
m_Contexts.Add(m_Owners[index]);
|
|
}
|
|
|
|
var implicitContext = new List<VFXContext>();
|
|
if (NeedsGlobalSort())
|
|
{
|
|
// Then the camera sort
|
|
var cameraSort = VFXContext.CreateImplicitContext<VFXCameraSort>(this);
|
|
implicitContext.Add(cameraSort);
|
|
m_Contexts.Add(cameraSort);
|
|
}
|
|
|
|
//additional update
|
|
for (int outputIndex = index; outputIndex < m_Owners.Count; ++outputIndex)
|
|
{
|
|
var currentOutputContext = m_Owners[outputIndex];
|
|
var abstractParticleOutput = currentOutputContext as VFXAbstractParticleOutput;
|
|
if (abstractParticleOutput == null)
|
|
continue;
|
|
|
|
if (abstractParticleOutput.NeedsOutputUpdate())
|
|
{
|
|
var update = VFXContext.CreateImplicitContext<VFXOutputUpdate>(this);
|
|
update.SetOutput(abstractParticleOutput);
|
|
implicitContext.Add(update);
|
|
m_Contexts.Add(update);
|
|
}
|
|
}
|
|
|
|
// And finally output
|
|
for (; index < m_Owners.Count; ++index)
|
|
m_Contexts.Add(m_Owners[index]);
|
|
|
|
return implicitContext;
|
|
}
|
|
|
|
public bool NeedsIndirectBuffer()
|
|
{
|
|
return owners.OfType<VFXAbstractParticleOutput>().Any(o => o.HasIndirectDraw());
|
|
}
|
|
|
|
public bool NeedsGlobalIndirectBuffer()
|
|
{
|
|
return owners.OfType<VFXAbstractParticleOutput>().Any(o => o.HasIndirectDraw() && !VFXOutputUpdate.HasFeature(o.outputUpdateFeatures, VFXOutputUpdate.Features.IndirectDraw));
|
|
}
|
|
|
|
public bool NeedsGlobalSort()
|
|
{
|
|
return owners.OfType<VFXAbstractParticleOutput>().Any(o => o.HasSorting() && !VFXOutputUpdate.HasFeature(o.outputUpdateFeatures, VFXOutputUpdate.Features.IndirectDraw));
|
|
}
|
|
|
|
public override void FillDescs(
|
|
VFXCompileErrorReporter reporter,
|
|
List<VFXGPUBufferDesc> outBufferDescs,
|
|
List<VFXTemporaryGPUBufferDesc> outTemporaryBufferDescs,
|
|
List<VFXEditorSystemDesc> outSystemDescs,
|
|
VFXExpressionGraph expressionGraph,
|
|
Dictionary<VFXContext, VFXContextCompiledData> contextToCompiledData,
|
|
Dictionary<VFXContext, int> contextSpawnToBufferIndex,
|
|
VFXDependentBuffersData dependentBuffers,
|
|
Dictionary<VFXContext, List<VFXContextLink>[]> effectiveFlowInputLinks,
|
|
VFXSystemNames systemNames = null)
|
|
{
|
|
bool hasKill = IsAttributeStored(VFXAttribute.Alive);
|
|
|
|
var deadListBufferIndex = -1;
|
|
var deadListCountIndex = -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;
|
|
|
|
if (m_DependenciesIn.Any())
|
|
{
|
|
if (m_DependenciesIn.Count != 1)
|
|
{
|
|
throw new InvalidOperationException("Unexpected multiple input dependency for GPU event");
|
|
}
|
|
attributeSourceBufferIndex = dependentBuffers.attributeBuffers[m_DependenciesIn.FirstOrDefault()];
|
|
eventGPUFrom = dependentBuffers.eventBuffers[this];
|
|
}
|
|
|
|
if (attributeBufferIndex != -1)
|
|
{
|
|
systemBufferMappings.Add(new VFXMapping("attributeBuffer", attributeBufferIndex));
|
|
}
|
|
|
|
if (m_ownAttributeSourceBuffer && m_layoutAttributeSource.GetBufferSize(sourceCount) > 0u)
|
|
{
|
|
if (attributeSourceBufferIndex != -1)
|
|
{
|
|
throw new InvalidOperationException("Unexpected source while filling description of data particle");
|
|
}
|
|
|
|
attributeSourceBufferIndex = outBufferDescs.Count;
|
|
outBufferDescs.Add(m_layoutAttributeSource.GetBufferDesc(sourceCount));
|
|
}
|
|
|
|
if (attributeSourceBufferIndex != -1)
|
|
{
|
|
systemBufferMappings.Add(new VFXMapping("sourceAttributeBuffer", attributeSourceBufferIndex));
|
|
}
|
|
|
|
var systemFlag = VFXSystemFlag.SystemDefault;
|
|
if (eventGPUFrom != -1)
|
|
{
|
|
systemFlag |= VFXSystemFlag.SystemReceivedEventGPU;
|
|
systemBufferMappings.Add(new VFXMapping("eventList", eventGPUFrom));
|
|
}
|
|
|
|
if (hasKill)
|
|
{
|
|
systemFlag |= VFXSystemFlag.SystemHasKill;
|
|
|
|
deadListBufferIndex = outBufferDescs.Count;
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Counter, size = capacity, stride = 4 });
|
|
systemBufferMappings.Add(new VFXMapping("deadList", deadListBufferIndex));
|
|
|
|
deadListCountIndex = outBufferDescs.Count;
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Raw, size = 1, stride = 4 });
|
|
systemBufferMappings.Add(new VFXMapping("deadListCount", deadListCountIndex));
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
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 mapper = contextToCompiledData[m_Contexts.First()].cpuMapper;
|
|
|
|
var boundsCenterExp = mapper.FromNameAndId("bounds_center", -1);
|
|
var boundsSizeExp = mapper.FromNameAndId("bounds_size", -1);
|
|
|
|
int boundsCenterIndex = boundsCenterExp != null ? expressionGraph.GetFlattenedIndex(boundsCenterExp) : -1;
|
|
int boundsSizeIndex = boundsSizeExp != null ? expressionGraph.GetFlattenedIndex(boundsSizeExp) : -1;
|
|
|
|
if (boundsCenterIndex != -1 && boundsSizeIndex != -1)
|
|
{
|
|
systemValueMappings.Add(new VFXMapping("bounds_center", boundsCenterIndex));
|
|
systemValueMappings.Add(new VFXMapping("bounds_size", boundsSizeIndex));
|
|
}
|
|
}
|
|
|
|
Dictionary<VFXContext, VFXOutputUpdate> indirectOutputToCuller = null;
|
|
bool needsIndirectBuffer = NeedsIndirectBuffer();
|
|
int globalIndirectBufferIndex = -1;
|
|
bool needsGlobalIndirectBuffer = false;
|
|
if (needsIndirectBuffer)
|
|
{
|
|
indirectOutputToCuller = new Dictionary<VFXContext, VFXOutputUpdate>();
|
|
foreach (var cullCompute in m_Contexts.OfType<VFXOutputUpdate>())
|
|
if (cullCompute.HasFeature(VFXOutputUpdate.Features.IndirectDraw))
|
|
indirectOutputToCuller.Add(cullCompute.output, cullCompute);
|
|
|
|
var allIndirectOutputs = owners.OfType<VFXAbstractParticleOutput>().Where(o => o.HasIndirectDraw());
|
|
|
|
needsGlobalIndirectBuffer = NeedsGlobalIndirectBuffer();
|
|
if (needsGlobalIndirectBuffer)
|
|
{
|
|
globalIndirectBufferIndex = outBufferDescs.Count;
|
|
systemBufferMappings.Add(new VFXMapping("indirectBuffer0", outBufferDescs.Count));
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Counter, size = capacity, stride = 4 });
|
|
}
|
|
|
|
int currentIndirectBufferIndex = globalIndirectBufferIndex == -1 ? 0 : 1;
|
|
foreach (var indirectOutput in allIndirectOutputs)
|
|
{
|
|
if (indirectOutputToCuller.ContainsKey(indirectOutput))
|
|
{
|
|
VFXOutputUpdate culler = indirectOutputToCuller[indirectOutput];
|
|
uint bufferCount = culler.bufferCount;
|
|
culler.bufferIndex = outBufferDescs.Count;
|
|
bool perCamera = culler.isPerCamera;
|
|
uint bufferStride = culler.HasFeature(VFXOutputUpdate.Features.Sort) ? 8u : 4u;
|
|
for (uint i = 0; i < bufferCount; ++i)
|
|
{
|
|
string bufferName = "indirectBuffer" + currentIndirectBufferIndex++;
|
|
if (perCamera)
|
|
bufferName += "PerCamera";
|
|
systemBufferMappings.Add(new VFXMapping(bufferName, outBufferDescs.Count));
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Counter, size = capacity, stride = bufferStride });
|
|
}
|
|
|
|
if (culler.HasFeature(VFXOutputUpdate.Features.Sort))
|
|
{
|
|
culler.sortedBufferIndex = outBufferDescs.Count;
|
|
for (uint i = 0; i < bufferCount; ++i)
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Default, size = capacity, stride = 4 });
|
|
}
|
|
else
|
|
culler.sortedBufferIndex = culler.bufferIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
// sort buffers
|
|
int sortBufferAIndex = -1;
|
|
int sortBufferBIndex = -1;
|
|
bool needsSort = NeedsGlobalSort();
|
|
if (needsSort)
|
|
{
|
|
sortBufferAIndex = outBufferDescs.Count;
|
|
sortBufferBIndex = sortBufferAIndex + 1;
|
|
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Default, size = capacity, stride = 8 });
|
|
systemBufferMappings.Add(new VFXMapping("sortBufferA", sortBufferAIndex));
|
|
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Default, size = capacity, 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 { type = ComputeBufferType.Raw, size = capacity * sizePerElement, stride = 4 } });
|
|
elementToVFXBufferMotionVector.Add(context.output, currentElementToVFXBufferMotionVector);
|
|
}
|
|
}
|
|
|
|
var taskDescs = new List<VFXEditorTaskDesc>();
|
|
var bufferMappings = new List<VFXMapping>();
|
|
var uniformMappings = new List<VFXMapping>();
|
|
var additionalParameters = new List<VFXMapping>();
|
|
|
|
for (int i = 0; i < m_Contexts.Count; ++i)
|
|
{
|
|
var temporaryBufferMappings = new List<VFXMappingTemporary>();
|
|
|
|
var context = m_Contexts[i];
|
|
var contextData = contextToCompiledData[context];
|
|
|
|
var taskDesc = new VFXEditorTaskDesc();
|
|
taskDesc.type = (UnityEngine.VFX.VFXTaskType)context.taskType;
|
|
|
|
bufferMappings.Clear();
|
|
additionalParameters.Clear();
|
|
|
|
if (context is VFXOutputUpdate)
|
|
{
|
|
var update = (VFXOutputUpdate)context;
|
|
if (update.HasFeature(VFXOutputUpdate.Features.MotionVector))
|
|
{
|
|
var currentIndex = elementToVFXBufferMotionVector[update.output];
|
|
temporaryBufferMappings.Add(new VFXMappingTemporary() { pastFrameIndex = 0u, perCameraBuffer = true, mapping = new VFXMapping("elementToVFXBuffer", currentIndex) });
|
|
}
|
|
}
|
|
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 (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(context.contextType == VFXContextType.Update ? "deadListOut" : "deadListIn", deadListBufferIndex));
|
|
|
|
if (deadListCountIndex != -1 && context.contextType == VFXContextType.Init)
|
|
bufferMappings.Add(new VFXMapping("deadListCount", deadListCountIndex));
|
|
|
|
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));
|
|
|
|
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)
|
|
{
|
|
systemFlag |= VFXSystemFlag.SystemHasIndirectBuffer;
|
|
|
|
if (context.contextType == VFXContextType.Output && (context as VFXAbstractParticleOutput).HasIndirectDraw())
|
|
{
|
|
bool hasCuller = indirectOutputToCuller.ContainsKey(context);
|
|
additionalParameters.Add(new VFXMapping("indirectIndex", hasCuller ? indirectOutputToCuller[context].bufferIndex : globalIndirectBufferIndex));
|
|
bufferMappings.Add(new VFXMapping("indirectBuffer", hasCuller ? indirectOutputToCuller[context].sortedBufferIndex : globalIndirectBufferIndex));
|
|
}
|
|
|
|
if (context.contextType == VFXContextType.Update)
|
|
{
|
|
if (context.taskType == VFXTaskType.Update && needsGlobalIndirectBuffer)
|
|
bufferMappings.Add(new VFXMapping("indirectBuffer", globalIndirectBufferIndex));
|
|
}
|
|
|
|
if (context.contextType == VFXContextType.Filter)
|
|
{
|
|
if (context.taskType == VFXTaskType.CameraSort && needsGlobalIndirectBuffer)
|
|
bufferMappings.Add(new VFXMapping("inputBuffer", globalIndirectBufferIndex));
|
|
else if (context is VFXOutputUpdate)
|
|
{
|
|
var outputUpdate = (VFXOutputUpdate)context;
|
|
int startIndex = outputUpdate.bufferIndex;
|
|
uint bufferCount = outputUpdate.bufferCount;
|
|
for (int j = 0; j < bufferCount; ++j)
|
|
bufferMappings.Add(new VFXMapping("outputBuffer" + j, startIndex + j));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (deadListBufferIndex != -1 && context.contextType == VFXContextType.Output && (context as VFXAbstractParticleOutput).NeedsDeadListCount())
|
|
bufferMappings.Add(new VFXMapping("deadListCount", deadListCountIndex));
|
|
|
|
if (context.taskType == VFXTaskType.CameraSort)
|
|
{
|
|
bufferMappings.Add(new VFXMapping("outputBuffer", sortBufferAIndex));
|
|
if (deadListCountIndex != -1)
|
|
bufferMappings.Add(new VFXMapping("deadListCount", deadListCountIndex));
|
|
}
|
|
|
|
var gpuTarget = context.allLinkedOutputSlot.SelectMany(o => (o.owner as VFXContext).outputContexts)
|
|
.Where(c => c.CanBeCompiled())
|
|
.Select(o => dependentBuffers.eventBuffers[o.GetData()])
|
|
.ToArray();
|
|
for (uint indexTarget = 0; indexTarget < (uint)gpuTarget.Length; ++indexTarget)
|
|
{
|
|
var prefix = VFXCodeGeneratorHelper.GeneratePrefix(indexTarget);
|
|
bufferMappings.Add(new VFXMapping(string.Format("eventListOut_{0}", prefix), gpuTarget[indexTarget]));
|
|
}
|
|
|
|
uniformMappings.Clear();
|
|
|
|
foreach (var uniform in contextData.uniformMapper.uniforms)
|
|
uniformMappings.Add(new VFXMapping(contextData.uniformMapper.GetName(uniform), expressionGraph.GetFlattenedIndex(uniform)));
|
|
foreach (var buffer in contextData.uniformMapper.buffers)
|
|
uniformMappings.Add(new VFXMapping(contextData.uniformMapper.GetName(buffer), expressionGraph.GetFlattenedIndex(buffer)));
|
|
foreach (var texture in contextData.uniformMapper.textures)
|
|
{
|
|
// 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, expressionGraph.GetFlattenedIndex(texture)));
|
|
}
|
|
|
|
// 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(context.GetSlotByPath(true, mapping.name), "GPUNodeLinkedTOCPUSlot", VFXErrorType.Error, "Can not link a GPU operator to a system wide (CPU) input.");;
|
|
throw new InvalidOperationException("Unable to compute CPU expression for mapping : " + mapping.name);
|
|
}
|
|
}
|
|
|
|
taskDesc.buffers = bufferMappings.ToArray();
|
|
taskDesc.temporaryBuffers = temporaryBufferMappings.ToArray();
|
|
taskDesc.values = uniformMappings.ToArray();
|
|
taskDesc.parameters = cpuMappings.Concat(contextData.parameters).Concat(additionalParameters).ToArray();
|
|
taskDesc.shaderSourceIndex = contextToCompiledData[context].indexInShaderSource;
|
|
|
|
if (context is IVFXMultiMeshOutput) // If the context is a multi mesh output, split and patch task desc into several tasks
|
|
{
|
|
var multiMeshOutput = (IVFXMultiMeshOutput)context;
|
|
for (int j = (int)multiMeshOutput.meshCount - 1; j >= 0; --j) // Back to front to be consistent with LOD and alpha
|
|
{
|
|
VFXEditorTaskDesc singleMeshTaskDesc = taskDesc;
|
|
singleMeshTaskDesc.parameters = VFXMultiMeshHelper.PatchCPUMapping(taskDesc.parameters, multiMeshOutput.meshCount, j).ToArray();
|
|
singleMeshTaskDesc.buffers = VFXMultiMeshHelper.PatchBufferMapping(taskDesc.buffers, j).ToArray();
|
|
taskDescs.Add(singleMeshTaskDesc);
|
|
}
|
|
}
|
|
else
|
|
taskDescs.Add(taskDesc);
|
|
|
|
// if task is a per camera update with sorting, add sort tasks
|
|
if (context is VFXOutputUpdate)
|
|
{
|
|
var update = (VFXOutputUpdate)context;
|
|
|
|
if (update.HasFeature(VFXOutputUpdate.Features.Sort))
|
|
{
|
|
for (int j = 0; j < update.bufferCount; ++j)
|
|
{
|
|
VFXEditorTaskDesc sortTaskDesc = new VFXEditorTaskDesc();
|
|
sortTaskDesc.type = UnityEngine.VFX.VFXTaskType.PerCameraSort;
|
|
sortTaskDesc.externalProcessor = null;
|
|
|
|
sortTaskDesc.buffers = new VFXMapping[3];
|
|
sortTaskDesc.buffers[0] = new VFXMapping("srcBuffer", update.bufferIndex + j);
|
|
if (capacity > 4096) // Add scratch buffer
|
|
{
|
|
sortTaskDesc.buffers[1] = new VFXMapping("scratchBuffer", outBufferDescs.Count);
|
|
outBufferDescs.Add(new VFXGPUBufferDesc() { type = ComputeBufferType.Default, size = capacity, stride = 8 });
|
|
}
|
|
else
|
|
sortTaskDesc.buffers[1] = new VFXMapping("scratchBuffer", -1); // No scratchBuffer needed
|
|
sortTaskDesc.buffers[2] = new VFXMapping("dstBuffer", update.sortedBufferIndex + j);
|
|
|
|
sortTaskDesc.parameters = new VFXMapping[1];
|
|
sortTaskDesc.parameters[0] = new VFXMapping("globalSort", 0);
|
|
|
|
taskDescs.Add(sortTaskDesc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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(),
|
|
type = VFXSystemType.Particle,
|
|
layer = m_Layer
|
|
});
|
|
}
|
|
|
|
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 VFXCoordinateSpace 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;
|
|
}
|
|
}
|