forked from BilalY/Rasagar
678 lines
26 KiB
C#
678 lines
26 KiB
C#
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Linq;
|
||
|
using System.Text;
|
||
|
using System.Globalization;
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.VFX;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Collections.ObjectModel;
|
||
|
|
||
|
namespace UnityEditor.VFX
|
||
|
{
|
||
|
static class VFXCodeGeneratorHelper
|
||
|
{
|
||
|
private static readonly char[] kAlpha = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
|
||
|
public static string GeneratePrefix(uint index)
|
||
|
{
|
||
|
if (index == 0u) return "a";
|
||
|
|
||
|
var prefix = "";
|
||
|
while (index != 0u)
|
||
|
{
|
||
|
prefix = kAlpha[index % kAlpha.Length] + prefix;
|
||
|
index /= (uint)kAlpha.Length;
|
||
|
}
|
||
|
return prefix;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
interface IHLSLCodeHolder : IEquatable<IHLSLCodeHolder>
|
||
|
{
|
||
|
IEnumerable<string> includes { get; }
|
||
|
ShaderInclude shaderFile { get; }
|
||
|
string sourceCode { get; set; }
|
||
|
string customCode { get; }
|
||
|
bool HasShaderFile();
|
||
|
}
|
||
|
|
||
|
class VFXShaderWriter
|
||
|
{
|
||
|
public VFXShaderWriter()
|
||
|
{ }
|
||
|
|
||
|
public VFXShaderWriter(string initialValue)
|
||
|
{
|
||
|
builder.Append(initialValue);
|
||
|
}
|
||
|
|
||
|
public static string GetValueString(VFXValueType type, object value)
|
||
|
{
|
||
|
var format = "";
|
||
|
switch (type)
|
||
|
{
|
||
|
case VFXValueType.Boolean:
|
||
|
case VFXValueType.Int32:
|
||
|
case VFXValueType.Uint32:
|
||
|
case VFXValueType.Float:
|
||
|
format = "({0}){1}";
|
||
|
break;
|
||
|
case VFXValueType.Float2:
|
||
|
case VFXValueType.Float3:
|
||
|
case VFXValueType.Float4:
|
||
|
case VFXValueType.Matrix4x4:
|
||
|
format = "{0}{1}";
|
||
|
break;
|
||
|
default: throw new Exception("GetValueString missing type: " + type);
|
||
|
}
|
||
|
// special cases of ToString
|
||
|
switch (type)
|
||
|
{
|
||
|
case VFXValueType.Boolean:
|
||
|
value = value.ToString().ToLower();
|
||
|
break;
|
||
|
case VFXValueType.Float:
|
||
|
value = FormatFloat((float)value);
|
||
|
break;
|
||
|
case VFXValueType.Float2:
|
||
|
value = $"({FormatFloat(((Vector2)value).x)}, {FormatFloat(((Vector2)value).y)})";
|
||
|
break;
|
||
|
case VFXValueType.Float3:
|
||
|
value = $"({FormatFloat(((Vector3)value).x)}, {FormatFloat(((Vector3)value).y)}, {FormatFloat(((Vector3)value).z)})";
|
||
|
break;
|
||
|
case VFXValueType.Float4:
|
||
|
value = $"({FormatFloat(((Vector4)value).x)}, {FormatFloat(((Vector4)value).y)}, {FormatFloat(((Vector4)value).z)}, {FormatFloat(((Vector4)value).w)})";
|
||
|
break;
|
||
|
case VFXValueType.Matrix4x4:
|
||
|
{
|
||
|
var matrix = ((Matrix4x4)value).transpose;
|
||
|
value = "(";
|
||
|
for (int i = 0; i < 16; ++i)
|
||
|
value += string.Format(CultureInfo.InvariantCulture, i == 15 ? "{0}" : "{0},", FormatFloat(matrix[i]));
|
||
|
value += ")";
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
return string.Format(CultureInfo.InvariantCulture, format, VFXExpression.TypeToCode(type), value);
|
||
|
}
|
||
|
|
||
|
private static string FormatFloat(float f)
|
||
|
{
|
||
|
if (float.IsInfinity(f))
|
||
|
return f > 0.0f ? "VFX_INFINITY" : "-VFX_INFINITY";
|
||
|
else if (float.IsNaN(f))
|
||
|
return "VFX_NAN";
|
||
|
else
|
||
|
return f.ToString("G9", CultureInfo.InvariantCulture);
|
||
|
}
|
||
|
|
||
|
public static string GetMultilineWithPrefix(string str, string linePrefix)
|
||
|
{
|
||
|
if (linePrefix.Length == 0)
|
||
|
return str;
|
||
|
|
||
|
if (str.Length == 0)
|
||
|
return linePrefix;
|
||
|
|
||
|
string[] delim = { System.Environment.NewLine, "\n" };
|
||
|
var lines = str.Split(delim, System.StringSplitOptions.None);
|
||
|
var dst = new StringBuilder(linePrefix.Length * lines.Length + str.Length);
|
||
|
|
||
|
foreach (var line in lines)
|
||
|
{
|
||
|
dst.Append(linePrefix);
|
||
|
dst.Append(line);
|
||
|
dst.Append('\n');
|
||
|
}
|
||
|
|
||
|
return dst.ToString(0, dst.Length - 1); // Remove the last line terminator
|
||
|
}
|
||
|
|
||
|
public void WriteFormat(string str, object arg0) { m_Builder.AppendFormat(str, arg0); }
|
||
|
public void WriteFormat(string str, object arg0, object arg1) { m_Builder.AppendFormat(str, arg0, arg1); }
|
||
|
public void WriteFormat(string str, object arg0, object arg1, object arg2) { m_Builder.AppendFormat(str, arg0, arg1, arg2); }
|
||
|
|
||
|
public void WriteLineFormat(string str, object arg0) { WriteFormat(str, arg0); WriteLine(); }
|
||
|
public void WriteLineFormat(string str, object arg0, object arg1) { WriteFormat(str, arg0, arg1); WriteLine(); }
|
||
|
public void WriteLineFormat(string str, object arg0, object arg1, object arg2) { WriteFormat(str, arg0, arg1, arg2); WriteLine(); }
|
||
|
|
||
|
// Generic builder method
|
||
|
public void Write<T>(T t)
|
||
|
{
|
||
|
m_Builder.Append(t);
|
||
|
}
|
||
|
|
||
|
// Optimize version to append substring and avoid useless allocation
|
||
|
public void Write(String s, int start, int length)
|
||
|
{
|
||
|
m_Builder.Append(s, start, length);
|
||
|
}
|
||
|
|
||
|
public void WriteLine<T>(T t)
|
||
|
{
|
||
|
Write(t);
|
||
|
WriteLine();
|
||
|
}
|
||
|
|
||
|
public void WriteLine()
|
||
|
{
|
||
|
m_Builder.Append('\n');
|
||
|
WriteIndent();
|
||
|
}
|
||
|
|
||
|
public void EnterScope()
|
||
|
{
|
||
|
WriteLine('{');
|
||
|
Indent();
|
||
|
}
|
||
|
|
||
|
public void ExitScope()
|
||
|
{
|
||
|
Deindent();
|
||
|
WriteLine('}');
|
||
|
}
|
||
|
|
||
|
public void ExitScopeStruct()
|
||
|
{
|
||
|
Deindent();
|
||
|
WriteLine("};");
|
||
|
}
|
||
|
|
||
|
public void ReplaceMultilineWithIndent(string tag, string src)
|
||
|
{
|
||
|
var str = m_Builder.ToString();
|
||
|
int startIndex = 0;
|
||
|
while (true)
|
||
|
{
|
||
|
int index = str.IndexOf(tag, startIndex, StringComparison.Ordinal);
|
||
|
if (index == -1)
|
||
|
break;
|
||
|
|
||
|
var lastPrefixIndex = index;
|
||
|
while (index > 0 && (str[index] == ' ' || str[index] == '\t'))
|
||
|
--index;
|
||
|
|
||
|
var prefix = str.Substring(index, lastPrefixIndex - index);
|
||
|
var formattedStr = GetMultilineWithPrefix(src, prefix).Substring(prefix.Length);
|
||
|
m_Builder.Replace(tag, formattedStr, lastPrefixIndex, tag.Length);
|
||
|
|
||
|
startIndex = index;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void WriteMultilineWithIndent<T>(T str)
|
||
|
{
|
||
|
if (m_Indent == 0)
|
||
|
Write(str);
|
||
|
else
|
||
|
{
|
||
|
var indentStr = new StringBuilder(m_Indent * kIndentStr.Length);
|
||
|
for (int i = 0; i < m_Indent; ++i)
|
||
|
indentStr.Append(kIndentStr);
|
||
|
WriteMultilineWithPrefix(str, indentStr.ToString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void WriteMultilineWithPrefix<T>(T str, string linePrefix)
|
||
|
{
|
||
|
if (linePrefix.Length == 0)
|
||
|
Write(str);
|
||
|
else
|
||
|
{
|
||
|
var res = GetMultilineWithPrefix(str.ToString(), linePrefix);
|
||
|
WriteLine(res.Substring(linePrefix.Length)); // Remove first line length;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override string ToString()
|
||
|
{
|
||
|
return m_Builder.ToString();
|
||
|
}
|
||
|
|
||
|
private static bool IsBufferBuiltinType(Type type)
|
||
|
{
|
||
|
return VFXExpression.IsUniform(VFXExpression.GetVFXValueTypeFromType(type));
|
||
|
}
|
||
|
|
||
|
static string GetStructureName(Type type)
|
||
|
{
|
||
|
if (IsBufferBuiltinType(type))
|
||
|
return VFXExpression.TypeToCode(VFXExpression.GetVFXValueTypeFromType(type));
|
||
|
else
|
||
|
return type.Name;
|
||
|
}
|
||
|
|
||
|
static void GenerateStructureCode(Type type, VFXShaderWriter structureDeclaration, HashSet<Type> alreadyGeneratedStructure)
|
||
|
{
|
||
|
if (IsBufferBuiltinType(type))
|
||
|
return; // No structure to generate, it is a builtin type
|
||
|
|
||
|
if (alreadyGeneratedStructure.Contains(type))
|
||
|
return;
|
||
|
|
||
|
var structureName = GetStructureName(type);
|
||
|
|
||
|
var prerequisite = new VFXShaderWriter();
|
||
|
var currentStructure = new VFXShaderWriter();
|
||
|
currentStructure.WriteLineFormat("struct {0}", structureName);
|
||
|
currentStructure.WriteLine("{");
|
||
|
currentStructure.Indent();
|
||
|
foreach (var field in VFXLibrary.GetFieldFromType(type))
|
||
|
{
|
||
|
string typeName;
|
||
|
if (field.valueType == VFXValueType.None)
|
||
|
{
|
||
|
typeName = GetStructureName(field.type);
|
||
|
GenerateStructureCode(field.type, prerequisite, alreadyGeneratedStructure);
|
||
|
}
|
||
|
else
|
||
|
typeName = VFXExpression.TypeToCode(field.valueType);
|
||
|
|
||
|
currentStructure.WriteLineFormat("{0} {1};", typeName, field.name);
|
||
|
}
|
||
|
|
||
|
currentStructure.Deindent();
|
||
|
currentStructure.WriteLine("};");
|
||
|
|
||
|
prerequisite.WriteLine(currentStructure.ToString());
|
||
|
structureDeclaration.Write(prerequisite.ToString());
|
||
|
alreadyGeneratedStructure.Add(type);
|
||
|
}
|
||
|
|
||
|
private void WriteBufferTypeDeclaration(Type type, HashSet<Type> alreadyGeneratedStructure)
|
||
|
{
|
||
|
GenerateStructureCode(type, this, alreadyGeneratedStructure);
|
||
|
|
||
|
var structureName = GetStructureName(type);
|
||
|
var expectedStride = Marshal.SizeOf(type);
|
||
|
WriteLineFormat("{0} SampleStructuredBuffer(StructuredBuffer<{0}> buffer, uint index, uint actualStride, uint actualCount)", structureName);
|
||
|
{
|
||
|
WriteLine("{");
|
||
|
Indent();
|
||
|
WriteLineFormat("{0} read = ({0})0;", structureName);
|
||
|
WriteLine("[branch]");
|
||
|
WriteLineFormat("if (actualStride == (uint){0} && index < actualCount)", expectedStride);
|
||
|
{
|
||
|
Indent();
|
||
|
WriteLine("read = buffer[index];");
|
||
|
Deindent();
|
||
|
}
|
||
|
WriteLineFormat("return read;", structureName);
|
||
|
Deindent();
|
||
|
WriteLine("}");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void WriteBufferTypeDeclaration(IEnumerable<BufferUsage> usages)
|
||
|
{
|
||
|
var types = usages.Select(usage =>
|
||
|
{
|
||
|
var type = usage.actualType;
|
||
|
if (IsBufferBuiltinType(type))
|
||
|
{
|
||
|
//Resolve type which are conflicting behind the same VFXValueType (Vector4 & Color for instance)
|
||
|
var valueType = VFXExpression.GetVFXValueTypeFromType(type);
|
||
|
type = VFXExpression.TypeToType(valueType);
|
||
|
}
|
||
|
return type;
|
||
|
}).Distinct();
|
||
|
|
||
|
var alreadyGeneratedStructure = new HashSet<Type>();
|
||
|
foreach (var type in types)
|
||
|
{
|
||
|
if (type == typeof(void))
|
||
|
continue;
|
||
|
|
||
|
WriteBufferTypeDeclaration(type, alreadyGeneratedStructure);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void WriteBuffer(VFXUniformMapper mapper, ReadOnlyDictionary<VFXExpression, BufferUsage> usageBuffer)
|
||
|
{
|
||
|
foreach (var buffer in mapper.buffers)
|
||
|
{
|
||
|
var name = mapper.GetName(buffer);
|
||
|
if (buffer.valueType == VFXValueType.Buffer && usageBuffer.TryGetValue(buffer, out var type))
|
||
|
{
|
||
|
if (!type.valid)
|
||
|
throw new NullReferenceException("Unexpected null type in graphicsBuffer usage");
|
||
|
|
||
|
WriteLineFormat("{0} {1};", GetBufferDeclaration(type), name);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WriteLineFormat("{0} {1};", VFXExpression.TypeToCode(buffer.valueType), name);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void WriteTexture(VFXUniformMapper mapper, IEnumerable<string> skipNames = null)
|
||
|
{
|
||
|
foreach (var texture in mapper.textures)
|
||
|
{
|
||
|
var names = mapper.GetNames(texture);
|
||
|
// 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
|
||
|
Debug.Assert(names.Distinct().Count() == names.Count);
|
||
|
foreach (var name in names)
|
||
|
{
|
||
|
if (skipNames != null && skipNames.Contains(name))
|
||
|
continue;
|
||
|
|
||
|
WriteLineFormat("{0} {1};", VFXExpression.TypeToCode(texture.valueType), name);
|
||
|
if (VFXExpression.IsTexture(texture.valueType)) //Mesh doesn't require a sampler or texel helper
|
||
|
{
|
||
|
WriteLineFormat("SamplerState sampler{0};", name);
|
||
|
WriteLineFormat("float4 {0}_TexelSize;", name); // TODO This is not very good to add a uniform for each texture that is hardly ever used
|
||
|
}
|
||
|
WriteLine();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void WriteEventBuffers(string baseName, int count)
|
||
|
{
|
||
|
for (int i = 0; i < count; ++i)
|
||
|
{
|
||
|
var prefix = VFXCodeGeneratorHelper.GeneratePrefix((uint)i);
|
||
|
WriteLineFormat("RWStructuredBuffer<uint> {0}_{1};", baseName, prefix);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool WriteGraphValuesStruct(VFXUniformMapper contextUniformMapper)
|
||
|
{
|
||
|
bool needsGraphValueStruct = false;
|
||
|
var contextUniforms = contextUniformMapper.uniforms;
|
||
|
if (contextUniforms.Any())
|
||
|
{
|
||
|
needsGraphValueStruct = true;
|
||
|
|
||
|
WriteLine("struct GraphValues");
|
||
|
WriteLine("{");
|
||
|
Indent();
|
||
|
|
||
|
foreach (var value in contextUniforms)
|
||
|
{
|
||
|
string name = contextUniformMapper.GetName(value);
|
||
|
string type = VFXExpression.TypeToCode(value.valueType);
|
||
|
WriteLineFormat("{0} {1};", type, name);
|
||
|
}
|
||
|
Deindent();
|
||
|
WriteLine("};");
|
||
|
}
|
||
|
WriteLine("ByteAddressBuffer graphValuesBuffer;");
|
||
|
WriteLine();
|
||
|
return needsGraphValueStruct;
|
||
|
}
|
||
|
|
||
|
public void GenerateLoadContextData(VFXDataParticle.GraphValuesLayout graphValuesLayout)
|
||
|
{
|
||
|
uint structSize = graphValuesLayout.paddedSizeInBytes;
|
||
|
WriteLine("struct ContextData");
|
||
|
WriteLine("{");
|
||
|
WriteLine(" uint maxParticleCount;");
|
||
|
WriteLine(" uint systemSeed;");
|
||
|
WriteLine(" uint initSpawnIndex;");
|
||
|
WriteLine("};");
|
||
|
|
||
|
WriteLine("ContextData contextData;");
|
||
|
WriteLine($"uint4 rawContextData = graphValuesBuffer.Load4(instanceActiveIndex * {structSize});");
|
||
|
WriteLine($"contextData.maxParticleCount = rawContextData.x;");
|
||
|
WriteLine($"contextData.systemSeed = rawContextData.y;");
|
||
|
WriteLine($"contextData.initSpawnIndex = rawContextData.z;");
|
||
|
}
|
||
|
|
||
|
public void GenerateFillGraphValuesStruct(VFXUniformMapper contextUniformMapper, VFXDataParticle.GraphValuesLayout graphValuesLayout)
|
||
|
{
|
||
|
uint structSize = graphValuesLayout.paddedSizeInBytes;
|
||
|
var nameToOffset = graphValuesLayout.nameToOffset;
|
||
|
var contextUniforms = contextUniformMapper.uniforms;
|
||
|
if (contextUniforms.Any())
|
||
|
{
|
||
|
contextUniforms = contextUniforms.OrderBy(o => nameToOffset[contextUniformMapper.GetName(o)]);
|
||
|
WriteLine("GraphValues graphValues;");
|
||
|
WriteLine();
|
||
|
foreach (var value in contextUniforms)
|
||
|
{
|
||
|
string name = contextUniformMapper.GetName(value);
|
||
|
int currentOffset = nameToOffset[name];
|
||
|
|
||
|
int typeSize = VFXExpression.TypeToSize(value.valueType);
|
||
|
string loadType = typeSize == 1 ? "" : typeSize.ToString();
|
||
|
if (value.valueType != VFXValueType.Matrix4x4)
|
||
|
{
|
||
|
string loadInstruction =
|
||
|
$"graphValuesBuffer.Load{loadType}(instanceActiveIndex * {structSize} + {currentOffset})";
|
||
|
|
||
|
switch (value.valueType)
|
||
|
{
|
||
|
case VFXValueType.Float:
|
||
|
case VFXValueType.Float2:
|
||
|
case VFXValueType.Float3:
|
||
|
case VFXValueType.Float4:
|
||
|
loadInstruction = $"asfloat({loadInstruction})";
|
||
|
break;
|
||
|
case VFXValueType.Int32:
|
||
|
loadInstruction = $"asint({loadInstruction})";
|
||
|
break;
|
||
|
case VFXValueType.Boolean:
|
||
|
loadInstruction = $"(bool){loadInstruction}";
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
WriteLineFormat("graphValues.{0} = {1};", name, loadInstruction);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (int i = 0; i < 4; i++)
|
||
|
{
|
||
|
string loadInstruction =
|
||
|
$"asfloat(graphValuesBuffer.Load4(instanceActiveIndex * {structSize} + {currentOffset + 16 * i}))";
|
||
|
string columnGetter = String.Format("._m0{0}_m1{0}_m2{0}_m3{0}",i);
|
||
|
WriteLineFormat("graphValues.{0}{1} = {2};", name,columnGetter, loadInstruction);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void WriteAttributeStruct(IEnumerable<VFXAttribute> attributes, string name)
|
||
|
{
|
||
|
WriteLineFormat("struct {0}", name);
|
||
|
WriteLine("{");
|
||
|
Indent();
|
||
|
|
||
|
foreach (var attribute in attributes)
|
||
|
WriteLineFormat("{0} {1};", VFXExpression.TypeToCode(attribute.type), attribute.name);
|
||
|
|
||
|
Deindent();
|
||
|
WriteLine("};");
|
||
|
}
|
||
|
|
||
|
private string AggregateParameters(List<string> parameters)
|
||
|
{
|
||
|
return parameters.Count == 0 ? "" : parameters.Aggregate((a, b) => a + ", " + b);
|
||
|
}
|
||
|
|
||
|
private static string GetBufferDeclaration(BufferUsage bufferUsage)
|
||
|
{
|
||
|
if (string.IsNullOrEmpty(bufferUsage.verbatimType))
|
||
|
return bufferUsage.container.ToString();
|
||
|
|
||
|
var verbatimType = IsBufferBuiltinType(bufferUsage.actualType)
|
||
|
? VFXExpression.TypeToCode(VFXExpression.GetVFXValueTypeFromType(bufferUsage.actualType))
|
||
|
: bufferUsage.verbatimType;
|
||
|
|
||
|
return $"{bufferUsage.container}<{verbatimType}>";
|
||
|
}
|
||
|
|
||
|
private static string GetFunctionParameterType(VFXExpression exp, ReadOnlyDictionary<VFXExpression, BufferUsage> usages)
|
||
|
{
|
||
|
switch (exp.valueType)
|
||
|
{
|
||
|
case VFXValueType.Texture2D: return "VFXSampler2D";
|
||
|
case VFXValueType.Texture2DArray: return "VFXSampler2DArray";
|
||
|
case VFXValueType.Texture3D: return "VFXSampler3D";
|
||
|
case VFXValueType.TextureCube: return "VFXSamplerCube";
|
||
|
case VFXValueType.TextureCubeArray: return "VFXSamplerCubeArray";
|
||
|
case VFXValueType.CameraBuffer: return "VFXSamplerCameraBuffer";
|
||
|
case VFXValueType.Buffer:
|
||
|
if (!usages.TryGetValue(exp, out var usage))
|
||
|
throw new KeyNotFoundException("Cannot find appropriate usage for " + exp);
|
||
|
return GetBufferDeclaration(usage);
|
||
|
default:
|
||
|
return VFXExpression.TypeToCode(exp.valueType);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static string GetFunctionParameterName(VFXExpression expression, Dictionary<VFXExpression, string> names)
|
||
|
{
|
||
|
var expressionName = names[expression];
|
||
|
switch (expression.valueType)
|
||
|
{
|
||
|
case VFXValueType.Texture2D:
|
||
|
case VFXValueType.Texture2DArray:
|
||
|
case VFXValueType.Texture3D:
|
||
|
case VFXValueType.TextureCube:
|
||
|
case VFXValueType.TextureCubeArray: return string.Format("GetVFXSampler({0}, {1})", expressionName, ("sampler" + expressionName));
|
||
|
case VFXValueType.CameraBuffer: return string.Format("GetVFXSampler({0}, {1})", expressionName, ("sampler" + expressionName));
|
||
|
|
||
|
default:
|
||
|
return expressionName;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static string GetInputModifier(VFXAttributeMode mode)
|
||
|
{
|
||
|
if ((mode & VFXAttributeMode.Write) != 0)
|
||
|
return "inout ";
|
||
|
|
||
|
return string.Empty;
|
||
|
}
|
||
|
|
||
|
public struct FunctionParameter
|
||
|
{
|
||
|
public string name;
|
||
|
public VFXExpression expression;
|
||
|
public VFXAttributeMode mode;
|
||
|
}
|
||
|
|
||
|
public void WriteBlockFunction(VFXTaskCompiledData taskData, string functionName, string source, IEnumerable<FunctionParameter> parameters, string commentMethod)
|
||
|
{
|
||
|
var parametersCode = new List<string>();
|
||
|
foreach (var parameter in parameters)
|
||
|
{
|
||
|
var inputModifier = GetInputModifier(parameter.mode);
|
||
|
var parameterType = GetFunctionParameterType(parameter.expression, taskData.bufferUsage);
|
||
|
parametersCode.Add(string.Format("{0}{1} {2}", inputModifier, parameterType, parameter.name));
|
||
|
}
|
||
|
|
||
|
WriteFormat("void {0}({1})", functionName, AggregateParameters(parametersCode));
|
||
|
if (!string.IsNullOrEmpty(commentMethod))
|
||
|
{
|
||
|
WriteFormat(" /*{0}*/", commentMethod);
|
||
|
}
|
||
|
WriteLine();
|
||
|
EnterScope();
|
||
|
if (source != null)
|
||
|
WriteMultilineWithIndent(source);
|
||
|
ExitScope();
|
||
|
}
|
||
|
|
||
|
public void WriteCallFunction(string functionName, IEnumerable<FunctionParameter> parameters, VFXExpressionMapper mapper, Dictionary<VFXExpression, string> variableNames)
|
||
|
{
|
||
|
var parametersCode = new List<string>();
|
||
|
foreach (var parameter in parameters)
|
||
|
{
|
||
|
var inputModifier = GetInputModifier(parameter.mode);
|
||
|
parametersCode.Add(string.Format("{1}{0}", GetFunctionParameterName(parameter.expression, variableNames), string.IsNullOrEmpty(inputModifier) ? string.Empty : string.Format(" /*{0}*/", inputModifier)));
|
||
|
}
|
||
|
|
||
|
WriteLineFormat("{0}({1});", functionName, AggregateParameters(parametersCode));
|
||
|
}
|
||
|
|
||
|
public void WriteAssignement(VFXValueType type, string variableName, string value)
|
||
|
{
|
||
|
var format = value == "0" ? "{1} = ({0}){2};" : "{1} = {2};";
|
||
|
WriteFormat(format, VFXExpression.TypeToCode(type), variableName, value);
|
||
|
}
|
||
|
|
||
|
public void WriteVariable(VFXValueType type, string variableName, string value)
|
||
|
{
|
||
|
if (!VFXExpression.IsTypeValidOnGPU(type))
|
||
|
throw new ArgumentException(string.Format("Invalid GPU Type: {0}", type));
|
||
|
|
||
|
WriteFormat("{0} ", VFXExpression.TypeToCode(type));
|
||
|
WriteAssignement(type, variableName, value);
|
||
|
}
|
||
|
|
||
|
public void WriteDeclaration(VFXValueType type, string variableName)
|
||
|
{
|
||
|
if (!VFXExpression.IsTypeValidOnGPU(type))
|
||
|
throw new ArgumentException(string.Format("Invalid GPU Type: {0}", type));
|
||
|
|
||
|
WriteFormat("{0} {1};\n", VFXExpression.TypeToCode(type), variableName);
|
||
|
}
|
||
|
|
||
|
public void WriteDeclaration(VFXValueType type, string variableName, string semantic)
|
||
|
{
|
||
|
if (!VFXExpression.IsTypeValidOnGPU(type))
|
||
|
throw new ArgumentException(string.Format("Invalid GPU Type: {0}", type));
|
||
|
|
||
|
WriteFormat("VFX_OPTIONAL_INTERPOLATION {0} {1} : {2};\n", VFXExpression.TypeToCode(type), variableName, semantic);
|
||
|
}
|
||
|
|
||
|
public void WriteVariable(VFXExpression exp, Dictionary<VFXExpression, string> variableNames)
|
||
|
{
|
||
|
if (!variableNames.ContainsKey(exp))
|
||
|
{
|
||
|
string entry;
|
||
|
if (exp.Is(VFXExpression.Flags.Constant))
|
||
|
entry = exp.GetCodeString(null); // Patch constant directly
|
||
|
else
|
||
|
{
|
||
|
foreach (var parent in exp.parents)
|
||
|
WriteVariable(parent, variableNames);
|
||
|
|
||
|
// Generate a new variable name
|
||
|
entry = "tmp_" + VFXCodeGeneratorHelper.GeneratePrefix((uint)variableNames.Count());
|
||
|
string value = exp.GetCodeString(exp.parents.Select(p => variableNames[p]).ToArray());
|
||
|
|
||
|
WriteVariable(exp.valueType, entry, value);
|
||
|
WriteLine();
|
||
|
}
|
||
|
|
||
|
variableNames[exp] = entry;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public StringBuilder builder { get { return m_Builder; } }
|
||
|
|
||
|
// Private stuff
|
||
|
private void Indent()
|
||
|
{
|
||
|
++m_Indent;
|
||
|
Write(kIndentStr);
|
||
|
}
|
||
|
|
||
|
private void Deindent()
|
||
|
{
|
||
|
if (m_Indent == 0)
|
||
|
throw new InvalidOperationException("Cannot de-indent as current indentation is 0");
|
||
|
|
||
|
--m_Indent;
|
||
|
m_Builder.Remove(m_Builder.Length - kIndentStr.Length, kIndentStr.Length); // remove last indent
|
||
|
}
|
||
|
|
||
|
private void WriteIndent()
|
||
|
{
|
||
|
for (int i = 0; i < m_Indent; ++i)
|
||
|
m_Builder.Append(kIndentStr);
|
||
|
}
|
||
|
|
||
|
private StringBuilder m_Builder = new StringBuilder();
|
||
|
private int m_Indent = 0;
|
||
|
private const string kIndentStr = " ";
|
||
|
}
|
||
|
}
|