647 lines
32 KiB
C#
647 lines
32 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.VFX;
|
|
|
|
using Object = UnityEngine.Object;
|
|
using System.Text.RegularExpressions;
|
|
using System.Globalization;
|
|
|
|
namespace UnityEditor.VFX
|
|
{
|
|
static class VFXCodeGenerator
|
|
{
|
|
private static string GetIndent(string src, int index)
|
|
{
|
|
var indent = "";
|
|
index--;
|
|
while (index > 0 && (src[index] == ' ' || src[index] == '\t'))
|
|
{
|
|
indent = src[index] + indent;
|
|
index--;
|
|
}
|
|
return indent;
|
|
}
|
|
|
|
//This function insure to keep padding while replacing a specific string
|
|
private static void ReplaceMultiline(StringBuilder target, string targetQuery, StringBuilder value)
|
|
{
|
|
string[] delim = { System.Environment.NewLine, "\n" };
|
|
var valueLines = value.ToString().Split(delim, System.StringSplitOptions.None);
|
|
if (valueLines.Length <= 1)
|
|
{
|
|
target.Replace(targetQuery, value.ToString());
|
|
}
|
|
else
|
|
{
|
|
while (true)
|
|
{
|
|
var targetCopy = target.ToString();
|
|
var index = targetCopy.IndexOf(targetQuery);
|
|
if (index == -1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var indent = GetIndent(targetCopy, index);
|
|
var currentValue = new StringBuilder();
|
|
foreach (var line in valueLines)
|
|
{
|
|
currentValue.Append(indent + line + '\n');
|
|
}
|
|
target.Replace(indent + targetQuery, currentValue.ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
static private VFXShaderWriter GenerateLoadAttribute(string matching, VFXContext context)
|
|
{
|
|
var r = new VFXShaderWriter();
|
|
|
|
var regex = new Regex(matching);
|
|
var attributesFromContext = context.GetData().GetAttributes().Where(o => regex.IsMatch(o.attrib.name)).ToArray();
|
|
var attributesSource = attributesFromContext.Where(a => context.GetData().IsSourceAttributeUsed(a.attrib, context)).ToArray();
|
|
var attributesCurrent = attributesFromContext.Where(a => context.GetData().IsCurrentAttributeUsed(a.attrib, context) || (context.contextType == VFXContextType.Init && context.GetData().IsAttributeStored(a.attrib))).ToArray();
|
|
|
|
//< Current Attribute
|
|
foreach (var attribute in attributesCurrent.Select(o => o.attrib))
|
|
{
|
|
var name = attribute.GetNameInCode(VFXAttributeLocation.Current);
|
|
if (attribute.name != VFXAttribute.EventCount.name)
|
|
{
|
|
if (context.contextType != VFXContextType.Init && context.GetData().IsAttributeStored(attribute))
|
|
{
|
|
r.WriteAssignement(attribute.type, name, context.GetData().GetLoadAttributeCode(attribute, VFXAttributeLocation.Current));
|
|
}
|
|
else
|
|
{
|
|
r.WriteAssignement(attribute.type, name, attribute.value.GetCodeString(null));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var linkedOutCount = context.allLinkedOutputSlot.Count();
|
|
r.WriteAssignement(attribute.type, name, attribute.value.GetCodeString(null));
|
|
for (uint i = 0; i < linkedOutCount; ++i)
|
|
{
|
|
r.WriteLine();
|
|
r.WriteFormat("uint {0}_{1} = 0u;", VFXAttribute.EventCount.name, VFXCodeGeneratorHelper.GeneratePrefix(i));
|
|
}
|
|
}
|
|
r.WriteLine();
|
|
}
|
|
|
|
//< Source Attribute (default temporary behavior, source is always the initial current value except for init context)
|
|
foreach (var attribute in attributesSource.Select(o => o.attrib))
|
|
{
|
|
var name = attribute.GetNameInCode(VFXAttributeLocation.Source);
|
|
if (context.contextType == VFXContextType.Init)
|
|
{
|
|
r.WriteAssignement(attribute.type, name, context.GetData().GetLoadAttributeCode(attribute, VFXAttributeLocation.Source));
|
|
}
|
|
else
|
|
{
|
|
if (attributesCurrent.Any(o => o.attrib.name == attribute.name))
|
|
{
|
|
var reference = new VFXAttributeExpression(new VFXAttribute(attribute.name, attribute.value), VFXAttributeLocation.Current);
|
|
r.WriteAssignement(reference.valueType, name, reference.GetCodeString(null));
|
|
}
|
|
else
|
|
{
|
|
r.WriteAssignement(attribute.type, name, attribute.value.GetCodeString(null));
|
|
}
|
|
}
|
|
r.WriteLine();
|
|
}
|
|
return r;
|
|
}
|
|
|
|
private const string eventListOutName = "eventListOut";
|
|
|
|
static private VFXShaderWriter GenerateStoreAttribute(string matching, VFXContext context, uint linkedOutCount)
|
|
{
|
|
var r = new VFXShaderWriter();
|
|
var regex = new Regex(matching);
|
|
|
|
var attributesFromContext = context.GetData().GetAttributes().Where(o => regex.IsMatch(o.attrib.name) &&
|
|
context.GetData().IsAttributeStored(o.attrib) &&
|
|
(context.contextType == VFXContextType.Init || context.GetData().IsCurrentAttributeWritten(o.attrib, context))).ToArray();
|
|
|
|
foreach (var attribute in attributesFromContext.Select(o => o.attrib))
|
|
{
|
|
r.Write(context.GetData().GetStoreAttributeCode(attribute, new VFXAttributeExpression(attribute).GetCodeString(null)));
|
|
r.WriteLine(';');
|
|
}
|
|
|
|
if (regex.IsMatch(VFXAttribute.EventCount.name))
|
|
{
|
|
for (uint i = 0; i < linkedOutCount; ++i)
|
|
{
|
|
var prefix = VFXCodeGeneratorHelper.GeneratePrefix(i);
|
|
r.WriteLineFormat("for (uint i = 0; i < {1}_{0}; ++i) {2}_{0}.Append(index);", prefix, VFXAttribute.EventCount.name, eventListOutName);
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static private VFXShaderWriter GenerateLoadParameter(string matching, VFXNamedExpression[] namedExpressions, Dictionary<VFXExpression, string> expressionToName)
|
|
{
|
|
var r = new VFXShaderWriter();
|
|
var regex = new Regex(matching);
|
|
|
|
var filteredNamedExpressions = namedExpressions.Where(o => regex.IsMatch(o.name) &&
|
|
!(expressionToName.ContainsKey(o.exp) && expressionToName[o.exp] == o.name)); // if parameter already in the global scope, there's nothing to do
|
|
|
|
bool needScope = false;
|
|
foreach (var namedExpression in filteredNamedExpressions)
|
|
{
|
|
r.WriteVariable(namedExpression.exp.valueType, namedExpression.name, "0");
|
|
r.WriteLine();
|
|
needScope = true;
|
|
}
|
|
|
|
if (needScope)
|
|
{
|
|
var expressionToNameLocal = new Dictionary<VFXExpression, string>(expressionToName);
|
|
r.EnterScope();
|
|
foreach (var namedExpression in filteredNamedExpressions)
|
|
{
|
|
if (!expressionToNameLocal.ContainsKey(namedExpression.exp))
|
|
{
|
|
r.WriteVariable(namedExpression.exp, expressionToNameLocal);
|
|
r.WriteLine();
|
|
}
|
|
r.WriteAssignement(namedExpression.exp.valueType, namedExpression.name, expressionToNameLocal[namedExpression.exp]);
|
|
r.WriteLine();
|
|
}
|
|
r.ExitScope();
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static public StringBuilder Build(VFXContext context, VFXCompilationMode compilationMode, VFXContextCompiledData contextData, HashSet<string> dependencies)
|
|
{
|
|
var templatePath = string.Format("{0}.template", context.codeGeneratorTemplate);
|
|
|
|
dependencies.Add(AssetDatabase.AssetPathToGUID(templatePath));
|
|
return Build(context, templatePath, compilationMode, contextData, dependencies);
|
|
}
|
|
|
|
static private void GetFunctionName(VFXBlock block, out string functionName, out string comment)
|
|
{
|
|
var settings = block.GetSettings(true).ToArray();
|
|
if (settings.Length > 0)
|
|
{
|
|
comment = "";
|
|
int hash = 0;
|
|
foreach (var setting in settings)
|
|
{
|
|
var value = setting.value;
|
|
hash = (hash * 397) ^ value.GetHashCode();
|
|
comment += string.Format("{0}:{1} ", setting.field.Name, value.ToString());
|
|
}
|
|
functionName = string.Format("{0}_{1}", block.GetType().Name, hash.ToString("X"));
|
|
}
|
|
else
|
|
{
|
|
comment = null;
|
|
functionName = block.GetType().Name;
|
|
}
|
|
}
|
|
|
|
static private string FormatPath(string path)
|
|
{
|
|
return Path.GetFullPath(path)
|
|
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
|
|
#if !UNITY_EDITOR_LINUX
|
|
.ToLowerInvariant()
|
|
#endif
|
|
;
|
|
}
|
|
|
|
static IEnumerable<Match> GetUniqueMatches(string regexStr, string src)
|
|
{
|
|
var regex = new Regex(regexStr);
|
|
var matches = regex.Matches(src);
|
|
return matches.Cast<Match>().GroupBy(m => m.Groups[0].Value).Select(g => g.First());
|
|
}
|
|
|
|
static private VFXShaderWriter GenerateComputeSourceIndex(VFXContext context)
|
|
{
|
|
var r = new VFXShaderWriter();
|
|
var spawnCountAttribute = new VFXAttribute("spawnCount", VFXValueType.Float);
|
|
if (!context.GetData().dependenciesIn.Any())
|
|
{
|
|
var spawnLinkCount = context.GetData().sourceCount;
|
|
r.WriteLine("int sourceIndex = 0;");
|
|
|
|
if (spawnLinkCount <= 1)
|
|
r.WriteLine("/*//Loop with 1 iteration generate a wrong IL Assembly (and actually, useless code)");
|
|
|
|
r.WriteLine("uint currentSumSpawnCount = 0u;");
|
|
r.WriteLineFormat("for (sourceIndex=0; sourceIndex<{0}; sourceIndex++)", spawnLinkCount);
|
|
r.EnterScope();
|
|
r.WriteLineFormat("currentSumSpawnCount += uint({0});", context.GetData().GetLoadAttributeCode(spawnCountAttribute, VFXAttributeLocation.Source));
|
|
r.WriteLine("if (id < currentSumSpawnCount)");
|
|
r.EnterScope();
|
|
r.WriteLine("break;");
|
|
r.ExitScope();
|
|
r.ExitScope();
|
|
|
|
if (spawnLinkCount <= 1)
|
|
r.WriteLine("*/");
|
|
}
|
|
else
|
|
{
|
|
/* context invalid or GPU event */
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static private StringBuilder GetFlattenedTemplateContent(string path, List<string> includes, IEnumerable<string> defines, HashSet<string> dependencies)
|
|
{
|
|
var formattedPath = FormatPath(path);
|
|
|
|
if (includes.Contains(formattedPath))
|
|
{
|
|
var includeHierarchy = new StringBuilder(string.Format("Cyclic VFXInclude dependency detected: {0}\n", formattedPath));
|
|
foreach (var str in Enumerable.Reverse<string>(includes))
|
|
includeHierarchy.Append(str + '\n');
|
|
throw new InvalidOperationException(includeHierarchy.ToString());
|
|
}
|
|
|
|
includes.Add(formattedPath);
|
|
var templateContent = new StringBuilder(System.IO.File.ReadAllText(formattedPath));
|
|
|
|
foreach (var match in GetUniqueMatches("\\${VFXInclude(RP|)\\(\\\"(.*?)\\\"\\)(,.*)?}", templateContent.ToString()))
|
|
{
|
|
var groups = match.Groups;
|
|
var renderPipelineInclude = groups[1].Value == "RP";
|
|
var includePath = groups[2].Value;
|
|
|
|
|
|
if (groups.Count > 3 && !String.IsNullOrEmpty(groups[2].Value))
|
|
{
|
|
var allDefines = groups[3].Value.Split(new char[] { ',', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
|
var neededDefines = allDefines.Where(d => d[0] != '!');
|
|
var forbiddenDefines = allDefines.Except(neededDefines).Select(d => d.Substring(1));
|
|
if (!neededDefines.All(d => defines.Contains(d)) || forbiddenDefines.Any(d => defines.Contains(d)))
|
|
{
|
|
ReplaceMultiline(templateContent, groups[0].Value, new StringBuilder());
|
|
continue;
|
|
}
|
|
}
|
|
|
|
string absolutePath;
|
|
if (renderPipelineInclude)
|
|
absolutePath = VFXLibrary.currentSRPBinder.templatePath + "/" + includePath;
|
|
else
|
|
absolutePath = VisualEffectGraphPackageInfo.assetPackagePath + "/" + includePath;
|
|
dependencies.Add(AssetDatabase.AssetPathToGUID(absolutePath));
|
|
|
|
var includeBuilder = GetFlattenedTemplateContent(absolutePath, includes, defines, dependencies);
|
|
ReplaceMultiline(templateContent, groups[0].Value, includeBuilder);
|
|
}
|
|
|
|
includes.Remove(formattedPath);
|
|
return templateContent;
|
|
}
|
|
|
|
static private void SubstituteMacros(StringBuilder builder)
|
|
{
|
|
var definesToCode = new Dictionary<string, string>();
|
|
var source = builder.ToString();
|
|
Regex beginRegex = new Regex("\\${VFXBegin:(.*)}");
|
|
|
|
int currentPos = -1;
|
|
int builderOffset = 0;
|
|
while ((currentPos = source.IndexOf("${")) != -1)
|
|
{
|
|
int endPos = source.IndexOf('}', currentPos);
|
|
if (endPos == -1)
|
|
throw new FormatException("Ill-formed VFX tag (Missing closing brace");
|
|
|
|
var tag = source.Substring(currentPos, endPos - currentPos + 1);
|
|
// Replace any tag found
|
|
string macro;
|
|
if (definesToCode.TryGetValue(tag, out macro))
|
|
{
|
|
builder.Remove(currentPos + builderOffset, tag.Length);
|
|
var indentedMacro = macro.Replace("\n", "\n" + GetIndent(source, currentPos));
|
|
builder.Insert(currentPos + builderOffset, indentedMacro);
|
|
}
|
|
else
|
|
{
|
|
const string endStr = "${VFXEnd}";
|
|
var match = beginRegex.Match(source, currentPos, tag.Length);
|
|
if (match.Success)
|
|
{
|
|
var macroStartPos = match.Index + match.Length;
|
|
var macroEndCodePos = source.IndexOf(endStr, macroStartPos);
|
|
if (macroEndCodePos == -1)
|
|
throw new FormatException("${VFXBegin} found without ${VFXEnd}");
|
|
|
|
var defineStr = "${" + match.Groups[1].Value + "}";
|
|
definesToCode[defineStr] = source.Substring(macroStartPos, macroEndCodePos - macroStartPos);
|
|
|
|
// Remove the define in builder
|
|
builder.Remove(match.Index + builderOffset, macroEndCodePos - match.Index + endStr.Length);
|
|
}
|
|
else if (tag == endStr)
|
|
throw new FormatException("${VFXEnd} found without ${VFXBegin}");
|
|
else // Remove undefined tag
|
|
builder.Remove(currentPos + builderOffset, tag.Length);
|
|
}
|
|
|
|
builderOffset += currentPos;
|
|
source = builder.ToString(builderOffset, builder.Length - builderOffset);
|
|
}
|
|
}
|
|
|
|
static private StringBuilder Build(VFXContext context, string templatePath, VFXCompilationMode compilationMode, VFXContextCompiledData contextData, HashSet<string> dependencies)
|
|
{
|
|
if (!context.SetupCompilation())
|
|
return null;
|
|
var stringBuilder = GetFlattenedTemplateContent(templatePath, new List<string>(), context.additionalDefines, dependencies);
|
|
|
|
var allCurrentAttributes = context.GetData().GetAttributes().Where(a =>
|
|
(context.GetData().IsCurrentAttributeUsed(a.attrib, context)) ||
|
|
(context.contextType == VFXContextType.Init && context.GetData().IsAttributeStored(a.attrib))); // In init, needs to declare all stored attributes for intialization
|
|
|
|
var allSourceAttributes = context.GetData().GetAttributes().Where(a => (context.GetData().IsSourceAttributeUsed(a.attrib, context)));
|
|
|
|
var globalDeclaration = new VFXShaderWriter();
|
|
globalDeclaration.WriteCBuffer(contextData.uniformMapper, "parameters");
|
|
globalDeclaration.WriteLine();
|
|
globalDeclaration.WriteBuffer(contextData.uniformMapper);
|
|
globalDeclaration.WriteLine();
|
|
globalDeclaration.WriteTexture(contextData.uniformMapper);
|
|
globalDeclaration.WriteAttributeStruct(allCurrentAttributes.Select(a => a.attrib), "Attributes");
|
|
globalDeclaration.WriteLine();
|
|
globalDeclaration.WriteAttributeStruct(allSourceAttributes.Select(a => a.attrib), "SourceAttributes");
|
|
globalDeclaration.WriteLine();
|
|
|
|
var linkedEventOut = context.allLinkedOutputSlot.Where(s => ((VFXModel)s.owner).GetFirstOfType<VFXContext>().CanBeCompiled()).ToList();
|
|
globalDeclaration.WriteEventBuffers(eventListOutName, linkedEventOut.Count);
|
|
|
|
//< Block processor
|
|
var blockFunction = new VFXShaderWriter();
|
|
var blockCallFunction = new VFXShaderWriter();
|
|
var blockDeclared = new HashSet<string>();
|
|
var expressionToName = context.GetData().GetAttributes().ToDictionary(o => new VFXAttributeExpression(o.attrib) as VFXExpression, o => (new VFXAttributeExpression(o.attrib)).GetCodeString(null));
|
|
expressionToName = expressionToName.Union(contextData.uniformMapper.expressionToCode).ToDictionary(s => s.Key, s => s.Value);
|
|
|
|
int cpt = 0;
|
|
foreach (var current in context.activeFlattenedChildrenWithImplicit)
|
|
{
|
|
BuildBlock(contextData, linkedEventOut, blockFunction, blockCallFunction, blockDeclared, expressionToName, current, ref cpt);
|
|
}
|
|
|
|
//< Final composition
|
|
var renderTemplatePipePath = VFXLibrary.currentSRPBinder.templatePath;
|
|
var renderRuntimePipePath = VFXLibrary.currentSRPBinder.runtimePath;
|
|
string renderPipeCommon = context.doesIncludeCommonCompute ? "Packages/com.unity.visualeffectgraph/Shaders/Common/VFXCommonCompute.hlsl" : VFXLibrary.currentSRPBinder.runtimePath + "/VFXCommon.hlsl";
|
|
string renderPipePasses = null;
|
|
if (!context.codeGeneratorCompute && !string.IsNullOrEmpty(renderTemplatePipePath))
|
|
{
|
|
renderPipePasses = renderTemplatePipePath + "/VFXPasses.template";
|
|
}
|
|
|
|
var globalIncludeContent = new VFXShaderWriter();
|
|
globalIncludeContent.WriteLine("#define NB_THREADS_PER_GROUP 64");
|
|
globalIncludeContent.WriteLine("#define HAS_ATTRIBUTES 1");
|
|
globalIncludeContent.WriteLine("#define VFX_PASSDEPTH_ACTUAL (0)");
|
|
globalIncludeContent.WriteLine("#define VFX_PASSDEPTH_MOTION_VECTOR (1)");
|
|
globalIncludeContent.WriteLine("#define VFX_PASSDEPTH_SELECTION (2)");
|
|
globalIncludeContent.WriteLine("#define VFX_PASSDEPTH_SHADOW (3)");
|
|
|
|
foreach (var attribute in allCurrentAttributes)
|
|
globalIncludeContent.WriteLineFormat("#define VFX_USE_{0}_{1} 1", attribute.attrib.name.ToUpper(CultureInfo.InvariantCulture), "CURRENT");
|
|
foreach (var attribute in allSourceAttributes)
|
|
globalIncludeContent.WriteLineFormat("#define VFX_USE_{0}_{1} 1", attribute.attrib.name.ToUpper(CultureInfo.InvariantCulture), "SOURCE");
|
|
|
|
foreach (var additionnalHeader in context.additionalDataHeaders)
|
|
globalIncludeContent.WriteLine(additionnalHeader);
|
|
|
|
foreach (var additionnalDefine in context.additionalDefines)
|
|
globalIncludeContent.WriteLineFormat("#define {0}{1}", additionnalDefine, additionnalDefine.Contains(' ') ? "" : " 1");
|
|
|
|
if (renderPipePasses != null)
|
|
globalIncludeContent.Write(GetFlattenedTemplateContent(renderPipePasses, new List<string>(), context.additionalDefines, dependencies));
|
|
|
|
if (context.GetData() is ISpaceable)
|
|
{
|
|
var spaceable = context.GetData() as ISpaceable;
|
|
globalIncludeContent.WriteLineFormat("#define {0} 1", spaceable.space == VFXCoordinateSpace.World ? "VFX_WORLD_SPACE" : "VFX_LOCAL_SPACE");
|
|
}
|
|
globalIncludeContent.WriteLineFormat("#include \"{0}/VFXDefines.hlsl\"", renderRuntimePipePath);
|
|
|
|
var perPassIncludeContent = new VFXShaderWriter();
|
|
perPassIncludeContent.WriteLine("#include \"" + renderPipeCommon + "\"");
|
|
perPassIncludeContent.WriteLine("#include \"Packages/com.unity.visualeffectgraph/Shaders/VFXCommon.hlsl\"");
|
|
|
|
// Per-block includes
|
|
var includes = Enumerable.Empty<string>();
|
|
foreach (var block in context.activeFlattenedChildrenWithImplicit)
|
|
includes = includes.Concat(block.includes);
|
|
var uniqueIncludes = new HashSet<string>(includes);
|
|
foreach (var includePath in uniqueIncludes)
|
|
perPassIncludeContent.WriteLine(string.Format("#include \"{0}\"", includePath));
|
|
|
|
|
|
ReplaceMultiline(stringBuilder, "${VFXGlobalInclude}", globalIncludeContent.builder);
|
|
ReplaceMultiline(stringBuilder, "${VFXGlobalDeclaration}", globalDeclaration.builder);
|
|
ReplaceMultiline(stringBuilder, "${VFXPerPassInclude}", perPassIncludeContent.builder);
|
|
ReplaceMultiline(stringBuilder, "${VFXGeneratedBlockFunction}", blockFunction.builder);
|
|
ReplaceMultiline(stringBuilder, "${VFXProcessBlocks}", blockCallFunction.builder);
|
|
|
|
var mainParameters = contextData.gpuMapper.CollectExpression(-1).ToArray();
|
|
foreach (var match in GetUniqueMatches("\\${VFXLoadParameter:{(.*?)}}", stringBuilder.ToString()))
|
|
{
|
|
var str = match.Groups[0].Value;
|
|
var pattern = match.Groups[1].Value;
|
|
var loadParameters = GenerateLoadParameter(pattern, mainParameters, expressionToName);
|
|
ReplaceMultiline(stringBuilder, str, loadParameters.builder);
|
|
}
|
|
var additionalInterpolantsGeneration = new VFXShaderWriter();
|
|
var additionalInterpolantsDeclaration = new VFXShaderWriter();
|
|
var additionalInterpolantsPreparation = new VFXShaderWriter();
|
|
|
|
|
|
int normSemantic = 0;
|
|
|
|
foreach (string fragmentParameter in context.fragmentParameters)
|
|
{
|
|
var filteredNamedExpression = mainParameters.FirstOrDefault(o => fragmentParameter == o.name &&
|
|
!(expressionToName.ContainsKey(o.exp) && expressionToName[o.exp] == o.name)); // if parameter already in the global scope, there's nothing to do
|
|
|
|
if (filteredNamedExpression.exp != null)
|
|
{
|
|
if (!filteredNamedExpression.exp.Is(VFXExpression.Flags.Constant))
|
|
{
|
|
additionalInterpolantsDeclaration.WriteDeclaration(filteredNamedExpression.exp.valueType, filteredNamedExpression.name, $"NORMAL{normSemantic++}");
|
|
additionalInterpolantsGeneration.WriteVariable(filteredNamedExpression.exp.valueType, filteredNamedExpression.name + "__", "0");
|
|
var expressionToNameLocal = new Dictionary<VFXExpression, string>(expressionToName);
|
|
additionalInterpolantsGeneration.EnterScope();
|
|
{
|
|
if (!expressionToNameLocal.ContainsKey(filteredNamedExpression.exp))
|
|
{
|
|
additionalInterpolantsGeneration.WriteVariable(filteredNamedExpression.exp, expressionToNameLocal);
|
|
additionalInterpolantsGeneration.WriteLine();
|
|
}
|
|
additionalInterpolantsGeneration.WriteAssignement(filteredNamedExpression.exp.valueType, filteredNamedExpression.name + "__", expressionToNameLocal[filteredNamedExpression.exp]);
|
|
additionalInterpolantsGeneration.WriteLine();
|
|
}
|
|
additionalInterpolantsGeneration.ExitScope();
|
|
additionalInterpolantsGeneration.WriteAssignement(filteredNamedExpression.exp.valueType, "o." + filteredNamedExpression.name, filteredNamedExpression.name + "__");
|
|
additionalInterpolantsPreparation.WriteVariable(filteredNamedExpression.exp.valueType, filteredNamedExpression.name, "i." + filteredNamedExpression.name);
|
|
}
|
|
else
|
|
additionalInterpolantsPreparation.WriteVariable(filteredNamedExpression.exp.valueType, filteredNamedExpression.name, filteredNamedExpression.exp.GetCodeString(null));
|
|
}
|
|
}
|
|
ReplaceMultiline(stringBuilder, "${VFXAdditionalInterpolantsGeneration}", additionalInterpolantsGeneration.builder);
|
|
ReplaceMultiline(stringBuilder, "${VFXAdditionalInterpolantsDeclaration}", additionalInterpolantsDeclaration.builder);
|
|
ReplaceMultiline(stringBuilder, "${VFXAdditionalInterpolantsPreparation}", additionalInterpolantsPreparation.builder);
|
|
|
|
//< Compute sourceIndex
|
|
if (stringBuilder.ToString().Contains("${VFXComputeSourceIndex}"))
|
|
{
|
|
var r = GenerateComputeSourceIndex(context);
|
|
ReplaceMultiline(stringBuilder, "${VFXComputeSourceIndex}", r.builder);
|
|
}
|
|
|
|
//< Load Attribute
|
|
if (stringBuilder.ToString().Contains("${VFXLoadAttributes}"))
|
|
{
|
|
var loadAttributes = GenerateLoadAttribute(".*", context);
|
|
ReplaceMultiline(stringBuilder, "${VFXLoadAttributes}", loadAttributes.builder);
|
|
}
|
|
|
|
foreach (var match in GetUniqueMatches("\\${VFXLoadAttributes:{(.*?)}}", stringBuilder.ToString()))
|
|
{
|
|
var str = match.Groups[0].Value;
|
|
var pattern = match.Groups[1].Value;
|
|
var loadAttributes = GenerateLoadAttribute(pattern, context);
|
|
ReplaceMultiline(stringBuilder, str, loadAttributes.builder);
|
|
}
|
|
|
|
//< Store Attribute
|
|
if (stringBuilder.ToString().Contains("${VFXStoreAttributes}"))
|
|
{
|
|
var storeAttribute = GenerateStoreAttribute(".*", context, (uint)linkedEventOut.Count);
|
|
ReplaceMultiline(stringBuilder, "${VFXStoreAttributes}", storeAttribute.builder);
|
|
}
|
|
|
|
foreach (var match in GetUniqueMatches("\\${VFXStoreAttributes:{(.*?)}}", stringBuilder.ToString()))
|
|
{
|
|
var str = match.Groups[0].Value;
|
|
var pattern = match.Groups[1].Value;
|
|
var storeAttributes = GenerateStoreAttribute(pattern, context, (uint)linkedEventOut.Count);
|
|
ReplaceMultiline(stringBuilder, str, storeAttributes.builder);
|
|
}
|
|
|
|
foreach (var addionalReplacement in context.additionalReplacements)
|
|
{
|
|
ReplaceMultiline(stringBuilder, addionalReplacement.Key, addionalReplacement.Value.builder);
|
|
}
|
|
|
|
// Replace defines
|
|
SubstituteMacros(stringBuilder);
|
|
|
|
if (VFXViewPreference.advancedLogs)
|
|
Debug.LogFormat("GENERATED_OUTPUT_FILE_FOR : {0}\n{1}", context.ToString(), stringBuilder.ToString());
|
|
|
|
context.EndCompilation();
|
|
return stringBuilder;
|
|
}
|
|
|
|
private static void BuildBlock(VFXContextCompiledData contextData, List<VFXSlot> linkedEventOut, VFXShaderWriter blockFunction, VFXShaderWriter blockCallFunction, HashSet<string> blockDeclared, Dictionary<VFXExpression, string> expressionToName, VFXBlock block, ref int blockIndex)
|
|
{
|
|
var parameters = block.mergedAttributes.Select(o =>
|
|
{
|
|
return new VFXShaderWriter.FunctionParameter
|
|
{
|
|
name = o.attrib.name,
|
|
expression = new VFXAttributeExpression(o.attrib) as VFXExpression,
|
|
mode = o.mode
|
|
};
|
|
}).ToList();
|
|
|
|
foreach (var parameter in block.parameters)
|
|
{
|
|
var expReduced = contextData.gpuMapper.FromNameAndId(parameter.name, blockIndex);
|
|
if (VFXExpression.IsTypeValidOnGPU(expReduced.valueType))
|
|
{
|
|
parameters.Add(new VFXShaderWriter.FunctionParameter
|
|
{
|
|
name = parameter.name,
|
|
expression = expReduced,
|
|
mode = VFXAttributeMode.None
|
|
});
|
|
}
|
|
}
|
|
|
|
string methodName, commentMethod;
|
|
GetFunctionName(block, out methodName, out commentMethod);
|
|
if (!blockDeclared.Contains(methodName))
|
|
{
|
|
blockDeclared.Add(methodName);
|
|
blockFunction.WriteBlockFunction(contextData.gpuMapper,
|
|
methodName,
|
|
block.source,
|
|
parameters,
|
|
commentMethod);
|
|
}
|
|
|
|
//< Parameters (computed and/or extracted from uniform)
|
|
var expressionToNameLocal = expressionToName;
|
|
bool needScope = parameters.Any(o => !expressionToNameLocal.ContainsKey(o.expression));
|
|
if (needScope)
|
|
{
|
|
expressionToNameLocal = new Dictionary<VFXExpression, string>(expressionToNameLocal);
|
|
blockCallFunction.EnterScope();
|
|
foreach (var exp in parameters.Select(o => o.expression))
|
|
{
|
|
if (expressionToNameLocal.ContainsKey(exp))
|
|
{
|
|
continue;
|
|
}
|
|
blockCallFunction.WriteVariable(exp, expressionToNameLocal);
|
|
}
|
|
}
|
|
|
|
var indexEventCount = parameters.FindIndex(o => o.name == VFXAttribute.EventCount.name);
|
|
if (indexEventCount != -1)
|
|
{
|
|
if ((parameters[indexEventCount].mode & VFXAttributeMode.Read) != 0)
|
|
throw new InvalidOperationException(string.Format("{0} isn't expected as read (special case)", VFXAttribute.EventCount.name));
|
|
blockCallFunction.WriteLine(string.Format("{0} = 0u;", VFXAttribute.EventCount.GetNameInCode(VFXAttributeLocation.Current)));
|
|
}
|
|
|
|
blockCallFunction.WriteCallFunction(methodName,
|
|
parameters,
|
|
contextData.gpuMapper,
|
|
expressionToNameLocal);
|
|
|
|
if (indexEventCount != -1)
|
|
{
|
|
foreach (var outputSlot in block.outputSlots.SelectMany(o => o.LinkedSlots))
|
|
{
|
|
var eventIndex = linkedEventOut.IndexOf(outputSlot);
|
|
if (eventIndex != -1)
|
|
blockCallFunction.WriteLineFormat("{0}_{1} += {2};", VFXAttribute.EventCount.name, VFXCodeGeneratorHelper.GeneratePrefix((uint)eventIndex), VFXAttribute.EventCount.GetNameInCode(VFXAttributeLocation.Current));
|
|
}
|
|
}
|
|
if (needScope)
|
|
blockCallFunction.ExitScope();
|
|
|
|
blockIndex++;
|
|
}
|
|
}
|
|
}
|