You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

373 lines
17 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace UnityEditor.VFX
{
internal class VFXShaderSnippets
{
internal static StringBuilder GenerateShaderCode(string templatePath, ShaderGenerationData shaderGenerationData)
{
VFXSnippetNodeInclude rootNode = new VFXSnippetNodeInclude(templatePath, shaderGenerationData, "");
StringBuilder shaderStringSb = new StringBuilder();
rootNode.CollectChildren();
rootNode.AppendContent(shaderStringSb);
return shaderStringSb;
}
//This function insure to keep padding while replacing a specific string
private static string GetIndent(string src, int index)
{
int indentLength = 0;
index--;
while (index > 0 && (src[index] == ' ' || src[index] == '\t'))
{
index--;
indentLength++;
}
if (indentLength > 0)
return src.Substring(index + 1, indentLength);
return string.Empty;
}
private static string FormatPath(string path)
{
return Path.GetFullPath(path)
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
#if !UNITY_EDITOR_LINUX
.ToLowerInvariant()
#endif
;
}
internal class ShaderGenerationData
{
internal Dictionary<VFXExpression, string> m_ExpressionToName;
internal VFXNamedExpression[] m_MainParameters;
internal VFXContext m_Context;
internal VFXTaskCompiledData m_TaskData;
internal HashSet<string> m_Defines;
internal HashSet<string> m_Dependencies;
internal VFXCodeGenerator.Cache m_CodeGeneratorCache;
internal bool m_HumanReadable;
}
internal class VFXSnippetNode
{
private static readonly char[] kDefineSeparator = { ',', ' ', '\t' };
private const string kVFXBeginStr = "${VFXBegin";
private const string kVFXEndStr = "${VFXEnd";
private const string kVFXIncludeStr = "${VFXInclude";
private const string kVFXLoadParameterStr = "${VFXLoadParameter";
private const string kVFXLoadAttributesStr = "${VFXLoadAttributes";
private const string kVFXStoreAttributesStr = "${VFXStoreAttributes";
protected const string kEndLineStr = "\n";
private List<VFXSnippetNode> m_Children;
protected string m_CurrentIndent;
protected ShaderGenerationData m_ShaderGenerationData;
public VFXSnippetNode(ShaderGenerationData shaderGenerationData, string indent)
{
this.m_ShaderGenerationData = shaderGenerationData;
this.m_CurrentIndent = indent;
}
public virtual void AppendContent(StringBuilder sb)
{
if (m_Children != null)
{
foreach (var snippet in m_Children)
{
snippet.AppendContent(sb);
}
}
}
//Returns endIndex
protected int CollectChildrenFromString(string templateContent, int startIndex)
{
int index;
while (true)
{
index = -1;
if (startIndex <= templateContent.Length)
index = templateContent.IndexOf("${", startIndex, StringComparison.Ordinal);
if (index == -1)
{
AddRawContent(templateContent, startIndex, templateContent.Length);
break;
}
AddRawContent(templateContent, startIndex, index);
ExtractSnippet(templateContent, index, out string snippetWithDecoration, out string snippetIndent);
if (m_ShaderGenerationData.m_CodeGeneratorCache.TryGetSnippet(snippetWithDecoration, out StringBuilder snippetContent))
{
VFXSnippetNodeLeaf snippetLeaf = new VFXSnippetNodeLeaf(snippetContent, m_ShaderGenerationData, m_CurrentIndent + snippetIndent);
AddChildNode(snippetLeaf);
startIndex = index + snippetWithDecoration.Length;
}
else if (snippetWithDecoration.StartsWith(kVFXLoadParameterStr, StringComparison.Ordinal))
startIndex = HandleLoadParameter(templateContent, index, snippetIndent);
else if (snippetWithDecoration.StartsWith(kVFXLoadAttributesStr, StringComparison.Ordinal))
startIndex = HandleLoadAttribute(templateContent, index, snippetIndent);
else if (snippetWithDecoration.StartsWith(kVFXStoreAttributesStr, StringComparison.Ordinal))
startIndex = HandleStoreAttribute(templateContent, index, snippetIndent);
else if (snippetWithDecoration.StartsWith(kVFXBeginStr, StringComparison.Ordinal))
startIndex = HandleBeginTag(templateContent, snippetWithDecoration, index);
else if (snippetWithDecoration.StartsWith(kVFXEndStr, StringComparison.Ordinal))
return index + snippetWithDecoration.Length + 1;
else if (snippetWithDecoration.StartsWith(kVFXIncludeStr, StringComparison.Ordinal))
startIndex =
HandleTemplateInclude(templateContent, index, snippetIndent, snippetWithDecoration);
else
startIndex = index + snippetWithDecoration.Length + 1;
}
return index;
}
private void AddChildNode(VFXSnippetNode node)
{
if (m_Children == null)
m_Children = new List<VFXSnippetNode>();
m_Children.Add(node);
}
private void ExtractSnippet(string templateContent, int startIndex, out string snippetWithDecoration, out string snippetIndent)
{
int endIndex = templateContent.IndexOf("}", startIndex, StringComparison.Ordinal);
snippetWithDecoration = templateContent.Substring(startIndex, endIndex - startIndex + 1);
snippetIndent = GetIndent(templateContent, startIndex);
}
private void AddRawContent(string templateContent, int startIndex, int index)
{
string leafContent = templateContent.Substring(startIndex, index - startIndex);
if (!string.IsNullOrEmpty(leafContent))
{
VFXSnippetNodeLeaf leaf = new VFXSnippetNodeLeaf(leafContent, m_ShaderGenerationData, m_CurrentIndent);
AddChildNode(leaf);
}
}
private void MatchTemplateIncludePattern(string templateContent, int index, out string includePath, out string includeDefinesRaw, out bool renderPipelineInclude)
{
int includeNameBeginIndex = templateContent.IndexOf("\"", index, StringComparison.Ordinal) + 1;
int includeNameEndIndex = templateContent.IndexOf("\"", includeNameBeginIndex + 1, StringComparison.Ordinal);
includePath =
templateContent.Substring(includeNameBeginIndex, includeNameEndIndex - includeNameBeginIndex);
renderPipelineInclude = templateContent.IndexOf("RP", index, includeNameBeginIndex - index, StringComparison.Ordinal) != -1;
int definesBeginIndex = includeNameEndIndex + 2;
int definesEndIndex = templateContent.IndexOf("}", definesBeginIndex, StringComparison.Ordinal);
includeDefinesRaw = templateContent.Substring(definesBeginIndex, definesEndIndex - definesBeginIndex);
}
private int HandleTemplateInclude(string templateContent, int index, string snippetIndent, string snippetWithDecoration)
{
bool AcceptInclude(string includeDefinesFused)
{
var includeDefines = includeDefinesFused.Split(kDefineSeparator, StringSplitOptions.RemoveEmptyEntries);
foreach(var define in includeDefines)
{
if (define[0] != '!')
{
if (!m_ShaderGenerationData.m_Defines.Contains(define))
return false;
}
else if (m_ShaderGenerationData.m_Defines.Contains(define.Substring(1)))
return false;
}
return true;
}
MatchTemplateIncludePattern(templateContent, index, out string includePath,
out string includeDefinesRaw, out bool renderPipelineInclude);
if(string.IsNullOrEmpty(includeDefinesRaw) || AcceptInclude(includeDefinesRaw))
{
string absolutePath = $"{(renderPipelineInclude ? VFXLibrary.currentSRPBinder.templatePath : VisualEffectGraphPackageInfo.assetPackagePath)}/{includePath}";
VFXSnippetNodeInclude includeSnippetNode = new VFXSnippetNodeInclude(absolutePath, m_ShaderGenerationData, m_CurrentIndent + snippetIndent);
includeSnippetNode.CollectChildren();
AddChildNode(includeSnippetNode);
}
var startIndex = index + snippetWithDecoration.Length;
return startIndex;
}
private int HandleBeginTag(string templateContent, string snippetWithDecoration, int index)
{
string macroName = snippetWithDecoration.Substring(kVFXBeginStr.Length + 1,
snippetWithDecoration.Length - kVFXBeginStr.Length - 2);
int startIndex = index + snippetWithDecoration.Length;
if (templateContent[startIndex] == '\n')
startIndex += 1;
VFXSnippetNodeMacroDefinition macroDefinitionNode =
new VFXSnippetNodeMacroDefinition(macroName, m_ShaderGenerationData, "");
startIndex = macroDefinitionNode.CollectChildrenFromString(templateContent, startIndex);
macroDefinitionNode.ExpandMacroAndStore();
AddChildNode(macroDefinitionNode);
return startIndex;
}
private void MatchSnippetPattern(string templateContent, int index, out string key, out string pattern)
{
int endIndex = templateContent.IndexOf("}}", index, StringComparison.Ordinal) + 2;
int patternIndex = templateContent.IndexOf(":{", index, StringComparison.Ordinal) + 2;
key = templateContent.Substring(index, endIndex - index);
pattern = templateContent.Substring(patternIndex, endIndex - patternIndex - 2);
}
private void AddLeafNodeFromShaderWriter(string snippetIndent, VFXShaderWriter shaderWriter, string str)
{
VFXSnippetNodeLeaf snippetNodeLeaf = new VFXSnippetNodeLeaf(shaderWriter.builder,
m_ShaderGenerationData, m_CurrentIndent + snippetIndent);
AddChildNode(snippetNodeLeaf);
m_ShaderGenerationData.m_CodeGeneratorCache.TryAddSnippet(str, shaderWriter.builder);
}
private int HandleStoreAttribute(string templateContent, int index, string snippetIndent)
{
MatchSnippetPattern(templateContent, index, out string key, out string pattern);
var storeAttribute = VFXCodeGenerator.GenerateStoreAttribute(pattern, m_ShaderGenerationData.m_Context,
(uint)m_ShaderGenerationData.m_TaskData.linkedEventOut.Length);
AddLeafNodeFromShaderWriter(snippetIndent, storeAttribute, key);
int startIndex = index + key.Length + 1;
return startIndex;
}
private int HandleLoadAttribute(string templateContent, int index, string snippetIndent)
{
MatchSnippetPattern(templateContent, index, out string key, out string pattern);
var loadAttribute = VFXCodeGenerator.GenerateLoadAttribute(pattern, m_ShaderGenerationData.m_Context,
m_ShaderGenerationData.m_TaskData);
AddLeafNodeFromShaderWriter(snippetIndent, loadAttribute, key);
int startIndex = index + key.Length + 1;
return startIndex;
}
private int HandleLoadParameter(string templateContent, int index, string snippetIndent)
{
MatchSnippetPattern(templateContent, index, out string key, out string pattern);
var loadParameters = VFXCodeGenerator.GenerateLoadParameter(pattern,
m_ShaderGenerationData.m_MainParameters, m_ShaderGenerationData.m_ExpressionToName);
if (string.IsNullOrEmpty(loadParameters.ToString()))
loadParameters.builder.AppendLine();
AddLeafNodeFromShaderWriter(snippetIndent, loadParameters, key);
int startIndex = index + key.Length;
return startIndex;
}
}
class VFXSnippetNodeLeaf : VFXSnippetNode
{
private StringBuilder m_LeafContentSb;
private string m_LeafContentStr;
internal VFXSnippetNodeLeaf(StringBuilder leafContentSb, ShaderGenerationData shaderGenerationData, string indent) : base(shaderGenerationData, indent)
{
//Copy to not accumulate indent
this.m_LeafContentSb = new StringBuilder();
this.m_LeafContentSb.Append(leafContentSb);
}
internal VFXSnippetNodeLeaf(string leafContentStr, ShaderGenerationData shaderGenerationData, string indent) : base(shaderGenerationData, indent)
{
this.m_LeafContentStr = leafContentStr;
}
public override void AppendContent(StringBuilder sb)
{
if (m_ShaderGenerationData.m_HumanReadable && m_CurrentIndent.Length > 0)
{
if (m_LeafContentSb != null)
{
m_LeafContentSb.Replace(kEndLineStr, kEndLineStr + m_CurrentIndent);
sb.Append(m_LeafContentSb);
}
else
{
var lines = m_LeafContentStr.Split(kEndLineStr, StringSplitOptions.None);
int i = 0;
foreach (var line in lines)
{
if (i > 0)
sb.Append(kEndLineStr + m_CurrentIndent);
sb.Append(line);
i++;
}
}
}
else
{
if (m_LeafContentSb != null)
sb.Append(m_LeafContentSb);
else
sb.Append(m_LeafContentStr);
}
}
}
internal class VFXSnippetNodeInclude : VFXSnippetNode
{
private string m_IncludePath;
internal VFXSnippetNodeInclude(string includePath, ShaderGenerationData shaderGenerationData, string indent) : base(shaderGenerationData, indent)
{
this.m_IncludePath = includePath;
}
public void CollectChildren()
{
if (!m_ShaderGenerationData.m_CodeGeneratorCache.TryGetTemplateCache(m_IncludePath, out var templateContent))
{
m_ShaderGenerationData.m_Dependencies.Add(AssetDatabase.AssetPathToGUID(m_IncludePath));
var formattedPath = FormatPath(m_IncludePath);
templateContent = File.ReadAllText(formattedPath);
m_ShaderGenerationData.m_CodeGeneratorCache.AddTemplateCache(m_IncludePath, templateContent);
}
CollectChildrenFromString(templateContent, 0);
}
}
class VFXSnippetNodeMacroDefinition : VFXSnippetNode
{
private string m_MacroName;
private StringBuilder m_ExpandedMacro;
private bool m_Expanded;
internal VFXSnippetNodeMacroDefinition(string macroName, ShaderGenerationData shaderGenerationData,
string indent) : base(shaderGenerationData, indent)
{
this.m_MacroName = macroName;
}
public void ExpandMacroAndStore()
{
StringBuilder sb = new StringBuilder();
AppendContent(sb);
m_ExpandedMacro = sb;
string snippetizedMacroName = $"${{{m_MacroName}}}";
if (!m_ShaderGenerationData.m_CodeGeneratorCache.TryAddSnippet(snippetizedMacroName, m_ExpandedMacro))
{
m_ShaderGenerationData.m_CodeGeneratorCache.SetSnippet(snippetizedMacroName,m_ExpandedMacro);
}
m_Expanded = true;
}
public override void AppendContent(StringBuilder sb)
{
if (!m_Expanded)
base.AppendContent(sb);
}
}
}
}