initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -0,0 +1,207 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.ShaderGraph;
using UnityEditor.ShaderGraph.Internal;
namespace UnityEditor.ShaderGraph.Internal
{
internal interface IActiveFields : KeywordDependentCollection.IInstance, KeywordDependentCollection.ISet<IActiveFields>
{
IEnumerable<FieldDescriptor> fields { get; }
bool Add(FieldDescriptor field);
bool Contains(FieldDescriptor field);
bool Contains(string value);
}
internal interface IActiveFieldsSet : KeywordDependentCollection.ISet<IActiveFields>
{
void AddAll(FieldDescriptor field);
}
internal class FieldNamePairStorage
{
private HashSet<FieldDescriptor> m_fieldDescriptors;
private HashSet<string> m_fieldNames;
public IEnumerable<FieldDescriptor> fields => m_fieldDescriptors;
public FieldNamePairStorage()
{
m_fieldDescriptors = new HashSet<FieldDescriptor>();
m_fieldNames = new HashSet<string>(StringComparer.Ordinal);
}
public IEnumerable<FieldDescriptor> Union(FieldNamePairStorage other)
{
var output = new HashSet<FieldDescriptor>(m_fieldDescriptors);
output.UnionWith(other.m_fieldDescriptors);
return output;
}
public bool Contains(FieldDescriptor fieldDescriptor)
{
return m_fieldDescriptors.Contains(fieldDescriptor);
}
public bool Contains(string fieldName)
{
return m_fieldNames.Contains(fieldName);
}
public bool Add(FieldDescriptor fieldDescriptor)
{
bool added = m_fieldDescriptors.Add(fieldDescriptor);
if (added)
{
m_fieldNames.Add(fieldDescriptor.ToFieldString());
}
return added;
}
}
internal sealed class ActiveFields : KeywordDependentCollection<
FieldNamePairStorage,
ActiveFields.All,
ActiveFields.AllPermutations,
ActiveFields.ForPermutationIndex,
ActiveFields.Base,
IActiveFields,
IActiveFieldsSet
>
{
public struct ForPermutationIndex : IActiveFields, IActiveFieldsSet
{
private ActiveFields m_Source;
private int m_PermutationIndex;
public KeywordDependentCollection.KeywordPermutationInstanceType type => KeywordDependentCollection.KeywordPermutationInstanceType.Permutation;
public IEnumerable<IActiveFields> instances => Enumerable.Repeat<IActiveFields>(this, 1);
public IEnumerable<FieldDescriptor> fields =>
m_Source.baseStorage.Union(m_Source.GetOrCreateForPermutationIndex(m_PermutationIndex));
public int instanceCount => 1;
public int permutationIndex => m_PermutationIndex;
internal ForPermutationIndex(ActiveFields source, int index)
{
m_Source = source;
m_PermutationIndex = index;
}
public bool Add(FieldDescriptor field)
=> m_Source.GetOrCreateForPermutationIndex(m_PermutationIndex).Add(field);
public bool Contains(FieldDescriptor field) =>
m_Source.baseStorage.Contains(field)
|| m_Source.GetOrCreateForPermutationIndex(m_PermutationIndex).Contains(field);
public bool Contains(string value) => m_Source.baseStorage.Contains(value)
|| m_Source.GetOrCreateForPermutationIndex(m_PermutationIndex).Contains(value);
public void AddAll(FieldDescriptor field) => Add(field);
}
public struct Base : IActiveFields, IActiveFieldsSet
{
private ActiveFields m_Source;
public IEnumerable<FieldDescriptor> fields => m_Source.baseStorage.fields;
public int instanceCount => 1;
public int permutationIndex => -1;
public KeywordDependentCollection.KeywordPermutationInstanceType type => KeywordDependentCollection.KeywordPermutationInstanceType.Base;
public IEnumerable<IActiveFields> instances => Enumerable.Repeat<IActiveFields>(this, 1);
internal Base(ActiveFields source)
{
m_Source = source;
}
public bool Add(FieldDescriptor field) => m_Source.baseStorage.Add(field);
public bool Contains(FieldDescriptor field) => m_Source.baseStorage.Contains(field);
public bool Contains(string value) => m_Source.baseStorage.Contains(value);
public void AddAll(FieldDescriptor field) => Add(field);
}
public struct All : IActiveFieldsSet
{
private ActiveFields m_Source;
public int instanceCount => m_Source.permutationCount + 1;
internal All(ActiveFields source)
{
m_Source = source;
}
public void AddAll(FieldDescriptor field)
{
m_Source.baseInstance.Add(field);
for (var i = 0; i < m_Source.permutationCount; ++i)
m_Source.GetOrCreateForPermutationIndex(i).Add(field);
}
public IEnumerable<IActiveFields> instances
{
get
{
var self = this;
return m_Source.permutationStorages
.Select((v, i) => new ForPermutationIndex(self.m_Source, i) as IActiveFields)
.Union(Enumerable.Repeat((IActiveFields)m_Source.baseInstance, 1));
}
}
}
public struct AllPermutations : IActiveFieldsSet
{
private ActiveFields m_Source;
public int instanceCount => m_Source.permutationCount;
internal AllPermutations(ActiveFields source)
{
m_Source = source;
}
public void AddAll(FieldDescriptor field)
{
for (var i = 0; i < m_Source.permutationCount; ++i)
m_Source.GetOrCreateForPermutationIndex(i).Add(field);
}
public IEnumerable<IActiveFields> instances
{
get
{
var self = this;
return m_Source.permutationStorages
.Select((v, i) => new ForPermutationIndex(self.m_Source, i) as IActiveFields);
}
}
}
public struct NoPermutation : IActiveFields, IActiveFieldsSet
{
private ActiveFields m_Source;
public IEnumerable<FieldDescriptor> fields => m_Source.baseStorage.fields;
public int instanceCount => 1;
public int permutationIndex => -1;
public KeywordDependentCollection.KeywordPermutationInstanceType type => KeywordDependentCollection.KeywordPermutationInstanceType.Base;
internal NoPermutation(ActiveFields source)
{
m_Source = source;
}
public bool Add(FieldDescriptor field) => m_Source.baseInstance.Add(field);
public bool Contains(FieldDescriptor field) => m_Source.baseStorage.Contains(field);
public bool Contains(string value) => m_Source.baseStorage.Contains(value);
public void AddAll(FieldDescriptor field) => Add(field);
public IEnumerable<IActiveFields> instances => Enumerable.Repeat<IActiveFields>(this, 1);
}
protected override All CreateAllSmartPointer() => new All(this);
protected override AllPermutations CreateAllPermutationsSmartPointer() => new AllPermutations(this);
protected override ForPermutationIndex CreateForPermutationSmartPointer(int index) => new ForPermutationIndex(this, index);
protected override Base CreateBaseSmartPointer() => new Base(this);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 03e494bdcf7e6474693797d5feaccb15
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,316 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor.Graphing;
using UnityEditor.ShaderGraph.Internal;
using UnityEngine;
using Pool = UnityEngine.Pool;
namespace UnityEditor.ShaderGraph
{
internal static class CustomInterpolatorUtils
{
// We need to be able to adapt CustomInterpolatorNode's output if the feature couldn't work per pass.
// there isn't really a good way to get global information about generation state during a node's generation.
// TODO: If we jobify our generator calls, switch these to TLS.
internal static bool generatorSkipFlag = false;
// For node only generation, there isn't a breadcumb of information we get in a few of the AbstractMaterialNode
// code generating functions. Instead of completely refactoring for this case, we've got a global flag so that
// CustomInterpolatorNode can redirect NODE preview graph evaluation from it's CIB's input port directly--
// This is necessary because node previews don't interpolate anything. So to get any previews at all we need to reroute.
internal static bool generatorNodeOnly = false;
// Used by preview manager to find what custom interpolator nodes need rerouting for node previews.
internal static IEnumerable<CustomInterpolatorNode> GetCustomBlockNodeDependents(BlockNode bnode)
{
return bnode?.owner?.GetNodes<CustomInterpolatorNode>().Where(cin => cin.e_targetBlockNode == bnode).ToList()
?? new List<CustomInterpolatorNode>();
}
}
internal class CustomInterpSubGen
{
#region descriptor
// Common splicing locations or concepts. These may or may not exist in client's template code.
[GenerationAPI]
internal static class Splice
{
internal static string k_splicePreInclude => "CustomInterpolatorPreInclude";
internal static string k_splicePrePacking => "CustomInterpolatorPrePacking";
internal static string k_splicePreSurface => "CustomInterpolatorPreSurface";
internal static string k_splicePreVertex => "CustomInterpolatorPreVertex";
internal static string k_spliceCopyToSDI => "CustomInterpolatorCopyToSDI";
}
// Describes where/what/how custom interpolator behavior can be achieved through splicing and defines.
// Generally speaking, this may require a mix of changes to client template and includes.
[GenerationAPI]
internal struct Descriptor
{
internal string src, dst; // for function or block. For macro block src is start of the macro and dst is end of the macro.
internal string name; // for struct or function.
internal string define; // defined for client code to indicate we're live.
internal string splice; // splice location, prefer use something from the list.
internal string preprocessor;
internal bool hasMacro;
internal bool isBlock => src != null && dst != null && name == null && splice != null && !hasMacro;
internal bool isMacroBlock => src != null && dst != null && name == null && splice != null && hasMacro;
internal bool isStruct => src == null && dst == null && name != null && splice != null;
internal bool isFunc => src != null && dst != null && name != null && splice != null;
internal bool isDefine => define != null && splice != null && src == null && dst == null & name == null;
internal bool isValid => isDefine || isBlock || isStruct || isFunc || isMacroBlock;
internal bool hasPreprocessor => !String.IsNullOrEmpty(preprocessor);
internal static Descriptor MakeFunc(string splice, string name, string dstType, string srcType, string define = "", string preprocessor = "") => new Descriptor { splice = splice, name = name, dst = dstType, src = srcType, define = define, preprocessor = preprocessor };
internal static Descriptor MakeStruct(string splice, string name, string define = "", string preprocessor = "") => new Descriptor { splice = splice, name = name, define = define, preprocessor = preprocessor };
internal static Descriptor MakeBlock(string splice, string dst, string src, string preprocessor = "") => new Descriptor { splice = splice, dst = dst, src = src, preprocessor = preprocessor };
internal static Descriptor MakeMacroBlock(string splice, string startMacro, string endMacro, string preprocessor = "") => new Descriptor { splice = splice, dst = endMacro, src = startMacro, preprocessor = preprocessor, hasMacro = true };
internal static Descriptor MakeDefine(string splice, string define, string preprocessor = "") => new Descriptor { splice = splice, define = define, preprocessor = preprocessor };
}
[GenerationAPI]
internal class Collection : IEnumerable<Collection.Item>
{
public class Item
{
public Descriptor descriptor { get; }
public Item(Descriptor descriptor) { this.descriptor = descriptor; }
}
readonly List<Collection.Item> m_Items;
public Collection() { m_Items = new List<Collection.Item>(); }
public Collection Add(Collection structs) { foreach (Collection.Item item in structs) m_Items.Add(item); return this; }
public Collection Add(Descriptor descriptor) { m_Items.Add(new Collection.Item(descriptor)); return this; }
public IEnumerator<Item> GetEnumerator() { return m_Items.GetEnumerator(); }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
#endregion
private List<BlockNode> customBlockNodes;
private bool isNodePreview;
private Dictionary<string, ShaderStringBuilder> spliceCommandBuffer;
internal CustomInterpSubGen(bool isNodePreview)
{
this.isNodePreview = isNodePreview;
customBlockNodes = new List<BlockNode>();
spliceCommandBuffer = new Dictionary<String, ShaderStringBuilder>();
}
#region GeneratorEntryPoints
// This entry point handles adding our upstream antecedents to the generator's list of active nodes.
// Custom Interpolator Nodes have no way of expressing that their Custom Interpolator Block is a dependent within existing generator code.
internal void ProcessExistingStackData(List<AbstractMaterialNode> vertexNodes, List<MaterialSlot> vertexSlots, List<AbstractMaterialNode> pixelNodes, IActiveFieldsSet activeFields)
{
if (CustomInterpolatorUtils.generatorSkipFlag)
return;
bool needsGraphFeature = false;
// departing from current generation code, we will select what to generate based on some graph analysis.
foreach (var cin in pixelNodes.OfType<CustomInterpolatorNode>().ToList())
{
// The CustomBlockNode's subtree.
var anties = GetAntecedents(cin.e_targetBlockNode);
// cin contains an inlined value, so there is nothing to do.
if (anties == null)
{
continue;
}
else if (isNodePreview)
{
foreach (var ant in anties)
{
// sorted insertion, based on dependencies already present in pixelNodes (an issue because we're faking for the preview).
if (!pixelNodes.Contains(ant))
InsertAntecedent(pixelNodes, ant);
}
}
else // it's a full compile and cin isn't inlined, so do all the things.
{
if (!customBlockNodes.Contains(cin.e_targetBlockNode))
{
activeFields.AddAll(cin.e_targetBlockNode.descriptor); // add the BlockFieldDescriptor for VertexDescription
customBlockNodes.Add(cin.e_targetBlockNode);
}
foreach (var ant in anties)
{
if (!vertexNodes.Contains(ant))
InsertAntecedent(vertexNodes, ant);
}
if (!vertexNodes.Contains(cin.e_targetBlockNode))
vertexNodes.Add(cin.e_targetBlockNode);
if (!vertexSlots.Contains(cin.e_targetBlockNode.FindSlot<MaterialSlot>(0)))
vertexSlots.Add(cin.e_targetBlockNode.FindSlot<MaterialSlot>(0));
needsGraphFeature = true;
}
}
// if a target has allowed custom interpolators, it should expect that the vertex feature can be forced on.
if (needsGraphFeature)
activeFields.AddAll(Fields.GraphVertex);
}
// This entry point is to inject custom interpolator fields into the appropriate structs for struct generation.
internal List<StructDescriptor> CopyModifyExistingPassStructs(IEnumerable<StructDescriptor> passStructs, IActiveFieldsSet activeFields)
{
if (CustomInterpolatorUtils.generatorSkipFlag)
return passStructs.ToList();
var newPassStructs = new List<StructDescriptor>();
// StructDescriptor is (kind-of) immutable, so we need to do some copy/modify shenanigans to make this work.
foreach (var ps in passStructs)
{
if (ps.populateWithCustomInterpolators)
{
var agg = new List<FieldDescriptor>();
foreach (var cib in customBlockNodes)
{
var fd = new FieldDescriptor(ps.name, cib.customName, "", ShaderValueTypeFrom((int)cib.customWidth), subscriptOptions: StructFieldOptions.Generated);
agg.Add(fd);
activeFields.AddAll(fd);
}
newPassStructs.Add(new StructDescriptor { name = ps.name, packFields = ps.packFields, fields = ps.fields.Union(agg).ToArray() });
}
else
{
newPassStructs.Add(ps);
}
}
foreach (var cid in customBlockNodes.Select(bn => bn.descriptor))
activeFields.AddAll(cid);
return newPassStructs;
}
// Custom Interpolator descriptors indicate how and where code should be generated.
// At this entry point, we can process the descriptors on a provided pass and generate
// the corresponding splices.
internal void ProcessDescriptors(IEnumerable<Descriptor> descriptors)
{
if (CustomInterpolatorUtils.generatorSkipFlag)
return;
ShaderStringBuilder builder = new ShaderStringBuilder();
foreach (var desc in descriptors)
{
builder.Clear();
if (!desc.isValid)
continue;
if (desc.hasPreprocessor)
builder.AppendLine($"#ifdef {desc.preprocessor}");
if (desc.isBlock) GenCopyBlock(desc.dst, desc.src, builder);
else if (desc.isMacroBlock) GenCopyMacroBlock(desc.src, desc.dst, builder);
else if (desc.isFunc) GenCopyFunc(desc.name, desc.dst, desc.src, builder, desc.define);
else if (desc.isStruct) GenStruct(desc.name, builder, desc.define);
else if (desc.isDefine) builder.AppendLine($"#define {desc.define}");
if (desc.hasPreprocessor)
builder.AppendLine("#endif");
if (!spliceCommandBuffer.ContainsKey(desc.splice))
spliceCommandBuffer.Add(desc.splice, new ShaderStringBuilder());
spliceCommandBuffer[desc.splice].Concat(builder);
}
}
// add our splices to the generator's dictionary.
internal void AppendToSpliceCommands(Dictionary<string, string> spliceCommands)
{
if (CustomInterpolatorUtils.generatorSkipFlag)
return;
foreach (var spliceKV in spliceCommandBuffer)
spliceCommands.Add(spliceKV.Key, spliceKV.Value.ToCodeBlock());
}
#endregion
#region helpers
private void GenStruct(string structName, ShaderStringBuilder builder, string makeDefine = "")
{
builder.AppendLine($"struct {structName}");
builder.AppendLine("{");
using (builder.IndentScope())
{
foreach (var bn in customBlockNodes)
{
builder.AppendLine($"float{(int)bn.customWidth} {bn.customName};");
}
}
builder.AppendLine("};");
if (makeDefine != null && makeDefine != "")
builder.AppendLine($"#define {makeDefine}");
builder.AppendNewLine();
}
private void GenCopyBlock(string dst, string src, ShaderStringBuilder builder)
{
foreach (var bnode in customBlockNodes)
builder.AppendLine($"{dst}.{bnode.customName} = {src}.{bnode.customName};");
}
private void GenCopyMacroBlock(string startMacro, string endMacro, ShaderStringBuilder builder)
{
foreach (var bnode in customBlockNodes)
builder.AppendLine($"{startMacro}{bnode.customName}{endMacro};");
}
private void GenCopyFunc(string funcName, string dstType, string srcType, ShaderStringBuilder builder, string makeDefine = "")
{
builder.AppendLine($"{dstType} {funcName}(inout {dstType} output, {srcType} input)");
using (builder.BlockScope())
{
GenCopyBlock("output", "input", builder);
builder.AppendLine("return output;");
}
if (makeDefine != null && makeDefine != "")
builder.AppendLine($"#define {makeDefine}");
}
private static List<AbstractMaterialNode> GetAntecedents(BlockNode blockNode)
{
if (blockNode != null && blockNode.isCustomBlock && blockNode.isActive && blockNode.GetInputNodeFromSlot(0) != null)
{
List<AbstractMaterialNode> results = new List<AbstractMaterialNode>();
NodeUtils.DepthFirstCollectNodesFromNode(results, blockNode, NodeUtils.IncludeSelf.Exclude);
return results != null && results.Count() == 0 ? null : results;
}
return null;
}
private static void InsertAntecedent(List<AbstractMaterialNode> nodes, AbstractMaterialNode node)
{
var upstream = node.GetInputSlots<MaterialSlot>().Where(slot => slot.isConnected).Select(slot => node.GetInputNodeFromSlot(slot.id));
int safeIdx = nodes.FindLastIndex(n => upstream.Contains(n)) + 1;
nodes.Insert(safeIdx, node);
}
private static ShaderValueType ShaderValueTypeFrom(int width)
{
switch (width)
{
case 1: return ShaderValueType.Float;
case 2: return ShaderValueType.Float2;
case 3: return ShaderValueType.Float3;
default: return ShaderValueType.Float4;
}
}
#endregion
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c9895b99811e6d549867e6cba9fcd44f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a809112e0e86a0945a16de7cf2d3916c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ecf3346505d12bb47845b60544d5913a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace UnityEditor.ShaderGraph
{
// Required for Unity to handle nested array serialization
[Serializable]
struct IntArray
{
public int[] array;
public int this[int i]
{
get => array[i];
set => array[i] = value;
}
public static implicit operator IntArray(int[] array)
{
return new IntArray { array = array };
}
public static implicit operator int[](IntArray array)
{
return array.array;
}
}
[Serializable]
class GraphCompilationResult
{
public string[] codeSnippets;
public int[] sharedCodeIndices;
public IntArray[] outputCodeIndices;
public string GenerateCode(int[] outputIndices)
{
var codeIndexSet = new HashSet<int>();
foreach (var codeIndex in sharedCodeIndices)
{
codeIndexSet.Add(codeIndex);
}
foreach (var outputIndex in outputIndices)
{
foreach (var codeIndex in outputCodeIndices[outputIndex].array)
{
codeIndexSet.Add(codeIndex);
}
}
var codeIndices = new int[codeIndexSet.Count];
codeIndexSet.CopyTo(codeIndices);
Array.Sort(codeIndices);
var charCount = 0;
foreach (var codeIndex in codeIndices)
{
charCount += codeSnippets[codeIndex].Length;
}
var sb = new StringBuilder();
sb.EnsureCapacity(charCount);
foreach (var codeIndex in codeIndices)
{
sb.Append(codeSnippets[codeIndex]);
}
return sb.ToString();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4200a9cc82e224ab89a574f04d74f1d1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,16 @@
using System;
namespace UnityEditor.ShaderGraph
{
struct MatrixNames
{
public const string Model = "UNITY_MATRIX_M";
public const string ModelInverse = "UNITY_MATRIX_I_M";
public const string View = "UNITY_MATRIX_V";
public const string ViewInverse = "UNITY_MATRIX_I_V";
public const string Projection = "UNITY_MATRIX_P";
public const string ProjectionInverse = "UNITY_MATRIX_I_P";
public const string ViewProjection = "UNITY_MATRIX_VP";
public const string ViewProjectionInverse = "UNITY_MATRIX_I_VP";
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1358e9b39cd7e8442bece5b15a2a7c63
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,396 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.ShaderGraph.Internal;
using UnityEngine;
using TextureDimension = UnityEngine.Rendering.TextureDimension;
namespace UnityEditor.ShaderGraph
{
class PropertyCollector
{
public struct TextureInfo
{
public string name;
public int textureId;
public TextureDimension dimension;
public bool modifiable;
}
bool m_ReadOnly;
List<HLSLProperty> m_HLSLProperties = null;
// reference name ==> property index in list
Dictionary<string, int> m_ReferenceNames = new Dictionary<string, int>();
// list of properties (kept in a list to maintain deterministic declaration order)
List<AbstractShaderProperty> m_Properties = new List<AbstractShaderProperty>();
public int propertyCount => m_Properties.Count;
public IEnumerable<AbstractShaderProperty> properties => m_Properties;
public AbstractShaderProperty GetProperty(int index) { return m_Properties[index]; }
public void Sort()
{
if (m_ReadOnly)
{
Debug.LogError("Cannot sort the properties when the PropertyCollector is already marked ReadOnly");
return;
}
m_Properties.Sort((a, b) => String.CompareOrdinal(a.referenceName, b.referenceName));
// reference name indices are now messed up, rebuild them
m_ReferenceNames.Clear();
for (int i = 0; i < m_Properties.Count; i++)
m_ReferenceNames.Add(m_Properties[i].referenceName, i);
}
public void SetReadOnly()
{
m_ReadOnly = true;
}
private static bool EquivalentHLSLProperties(AbstractShaderProperty a, AbstractShaderProperty b)
{
bool equivalent = true;
var bHLSLProps = new List<HLSLProperty>();
b.ForeachHLSLProperty(bh => bHLSLProps.Add(bh));
a.ForeachHLSLProperty(ah =>
{
var i = bHLSLProps.FindIndex(bh => bh.name == ah.name);
if (i < 0)
equivalent = false;
else
{
var bh = bHLSLProps[i];
if (!ah.ValueEquals(bh))
equivalent = false;
bHLSLProps.RemoveAt(i);
}
});
return equivalent && (bHLSLProps.Count == 0);
}
public void AddShaderProperty(AbstractShaderProperty prop)
{
if (m_ReadOnly)
{
Debug.LogError("ERROR attempting to add property to readonly collection");
return;
}
int propIndex = -1;
if (m_ReferenceNames.TryGetValue(prop.referenceName, out propIndex))
{
// existing referenceName
var existingProp = m_Properties[propIndex];
if (existingProp != prop)
{
// duplicate reference name, but different property instances
if (existingProp.GetType() != prop.GetType())
{
Debug.LogError("Two properties with the same reference name (" + prop.referenceName + ") using different types");
}
else
{
if (!EquivalentHLSLProperties(existingProp, prop))
Debug.LogError("Two properties with the same reference name (" + prop.referenceName + ") produce different HLSL properties");
}
}
}
else
{
// new referenceName, new property
propIndex = m_Properties.Count;
m_Properties.Add(prop);
m_ReferenceNames.Add(prop.referenceName, propIndex);
}
}
private List<HLSLProperty> BuildHLSLPropertyList()
{
SetReadOnly();
if (m_HLSLProperties == null)
{
m_HLSLProperties = new List<HLSLProperty>();
var dict = new Dictionary<string, int>();
foreach (var p in m_Properties)
{
p.ForeachHLSLProperty(
h =>
{
if (dict.TryGetValue(h.name, out int index))
{
// check if same property
if (!h.ValueEquals(m_HLSLProperties[index]))
Debug.LogError("Two different HLSL Properties declared with the same name: " + h.name + " and " + m_HLSLProperties[index].name);
return;
}
dict.Add(h.name, m_HLSLProperties.Count);
m_HLSLProperties.Add(h);
}
);
}
}
return m_HLSLProperties;
}
public void GetPropertiesDeclaration(ShaderStringBuilder builder, GenerationMode mode, ConcretePrecision defaultPrecision)
{
foreach (var prop in properties)
{
// set up switched properties to use the inherited precision
prop.SetupConcretePrecision(defaultPrecision);
}
// build a list of all HLSL properties
var hlslProps = BuildHLSLPropertyList();
if (mode == GenerationMode.Preview)
{
builder.AppendLine("CBUFFER_START(UnityPerMaterial)");
// all non-gpu instanced properties (even non-batchable ones!)
// this is because for preview we convert all properties to UnityPerMaterial properties
// as we will be submitting the default preview values via the Material.. :)
foreach (var h in hlslProps)
{
if ((h.declaration == HLSLDeclaration.UnityPerMaterial) ||
(h.declaration == HLSLDeclaration.Global))
{
h.AppendTo(builder);
}
}
// gpu-instanced properties
var gpuInstancedProps = hlslProps.Where(h => h.declaration == HLSLDeclaration.HybridPerInstance);
if (gpuInstancedProps.Any())
{
builder.AppendLine("#ifdef UNITY_HYBRID_V1_INSTANCING_ENABLED");
foreach (var h in gpuInstancedProps)
{
h.AppendTo(builder, name => name + "_dummy");
}
builder.AppendLine("#else // V2");
foreach (var h in gpuInstancedProps)
{
h.AppendTo(builder);
}
builder.AppendLine("#endif");
}
builder.AppendLine("CBUFFER_END");
builder.AppendLine("#define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) var");
return;
}
// Hybrid V1 generates a special version of UnityPerMaterial, which has dummy constants for
// instanced properties, and regular constants for other properties.
// Hybrid V2 generates a perfectly normal UnityPerMaterial, but needs to append
// a UNITY_DOTS_INSTANCING_START/END block after it that contains the instanced properties.
#if !ENABLE_HYBRID_RENDERER_V2
builder.AppendLine("CBUFFER_START(UnityPerMaterial)");
// non-GPU-instanced batchable properties go first in the UnityPerMaterial cbuffer
foreach (var h in hlslProps)
if (h.declaration == HLSLDeclaration.UnityPerMaterial)
h.AppendTo(builder);
// followed by GPU-instanced batchable properties
var gpuInstancedProperties = hlslProps.Where(h => h.declaration == HLSLDeclaration.HybridPerInstance);
if (gpuInstancedProperties.Any())
{
builder.AppendLine("#ifdef UNITY_HYBRID_V1_INSTANCING_ENABLED");
foreach (var hlslProp in gpuInstancedProperties)
hlslProp.AppendTo(builder, name => name + "_dummy");
builder.AppendLine("#else");
foreach (var hlslProp in gpuInstancedProperties)
hlslProp.AppendTo(builder);
builder.AppendLine("#endif");
}
builder.AppendLine("CBUFFER_END");
#else
// TODO: need to test this path with HYBRID_RENDERER_V2 ...
builder.AppendLine("CBUFFER_START(UnityPerMaterial)");
int instancedCount = 0;
foreach (var h in hlslProps)
{
if (h.declaration == HLSLDeclaration.UnityPerMaterial)
h.AppendTo(builder);
else if (h.declaration == HLSLDeclaration.HybridPerInstance)
instancedCount++;
}
if (instancedCount > 0)
{
builder.AppendLine("// Hybrid instanced properties");
foreach (var h in hlslProps.Where(h => h.declaration == HLSLDeclaration.HybridPerInstance))
h.AppendTo(builder);
}
builder.AppendLine("CBUFFER_END");
if (instancedCount > 0)
{
builder.AppendLine("#if defined(UNITY_DOTS_INSTANCING_ENABLED)");
builder.AppendLine("// DOTS instancing definitions");
builder.AppendLine("UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata)");
foreach (var h in hlslProps.Where(h => h.declaration == HLSLDeclaration.HybridPerInstance))
{
var n = h.name;
string type = h.GetValueTypeString();
builder.AppendLine($" UNITY_DOTS_INSTANCED_PROP({type}, {n})");
}
builder.AppendLine("UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata)");
builder.AppendLine("// DOTS instancing usage macros");
builder.AppendLine("#define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) UNITY_ACCESS_DOTS_INSTANCED_PROP_FROM_MACRO(type, Metadata##var)");
builder.AppendLine("#else");
builder.AppendLine("#define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) var");
builder.AppendLine("#endif");
}
#endif
builder.AppendNewLine();
builder.AppendLine("// Object and Global properties");
foreach (var h in hlslProps)
if (h.declaration == HLSLDeclaration.Global)
h.AppendTo(builder);
}
public bool HasDotsProperties()
{
var hlslProps = BuildHLSLPropertyList();
bool hasDotsProperties = false;
foreach (var h in hlslProps)
{
if (h.declaration == HLSLDeclaration.HybridPerInstance)
hasDotsProperties = true;
}
return hasDotsProperties;
}
public string GetDotsInstancingPropertiesDeclaration(GenerationMode mode)
{
// Hybrid V1 needs to declare a special macro to that is injected into
// builtin instancing variables.
// Hybrid V2 does not need it.
#if !ENABLE_HYBRID_RENDERER_V2
var builder = new ShaderStringBuilder();
var batchAll = (mode == GenerationMode.Preview);
// build a list of all HLSL properties
var hybridHLSLProps = BuildHLSLPropertyList().Where(h => h.declaration == HLSLDeclaration.HybridPerInstance);
if (hybridHLSLProps.Any())
{
builder.AppendLine("#if defined(UNITY_HYBRID_V1_INSTANCING_ENABLED)");
builder.AppendLine("#define HYBRID_V1_CUSTOM_ADDITIONAL_MATERIAL_VARS \\");
int count = 0;
foreach (var prop in hybridHLSLProps)
{
// Combine multiple UNITY_DEFINE_INSTANCED_PROP lines with \ so the generated
// macro expands into multiple definitions if there are more than one.
if (count > 0)
{
builder.Append("\\");
builder.AppendNewLine();
}
builder.Append("UNITY_DEFINE_INSTANCED_PROP(");
builder.Append(prop.GetValueTypeString());
builder.Append(", ");
builder.Append(prop.name);
builder.Append(")");
count++;
}
builder.AppendNewLine();
}
builder.AppendLine("#define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) UNITY_ACCESS_INSTANCED_PROP(unity_Builtins0, var)");
builder.AppendLine("#else");
builder.AppendLine("#define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) var");
builder.AppendLine("#endif");
return builder.ToString();
#else
return "";
#endif
}
public List<TextureInfo> GetConfiguredTextures()
{
var result = new List<TextureInfo>();
// TODO: this should be interface based instead of looking for hard coded types
foreach (var prop in properties.OfType<Texture2DShaderProperty>())
{
if (prop.referenceName != null)
{
var textureInfo = new TextureInfo
{
name = prop.referenceName,
textureId = prop.value.texture != null ? prop.value.texture.GetInstanceID() : 0,
dimension = TextureDimension.Tex2D,
modifiable = prop.modifiable
};
result.Add(textureInfo);
}
}
foreach (var prop in properties.OfType<Texture2DArrayShaderProperty>())
{
if (prop.referenceName != null)
{
var textureInfo = new TextureInfo
{
name = prop.referenceName,
textureId = prop.value.textureArray != null ? prop.value.textureArray.GetInstanceID() : 0,
dimension = TextureDimension.Tex2DArray,
modifiable = prop.modifiable
};
result.Add(textureInfo);
}
}
foreach (var prop in properties.OfType<Texture3DShaderProperty>())
{
if (prop.referenceName != null)
{
var textureInfo = new TextureInfo
{
name = prop.referenceName,
textureId = prop.value.texture != null ? prop.value.texture.GetInstanceID() : 0,
dimension = TextureDimension.Tex3D,
modifiable = prop.modifiable
};
result.Add(textureInfo);
}
}
foreach (var prop in properties.OfType<CubemapShaderProperty>())
{
if (prop.referenceName != null)
{
var textureInfo = new TextureInfo
{
name = prop.referenceName,
textureId = prop.value.cubemap != null ? prop.value.cubemap.GetInstanceID() : 0,
dimension = TextureDimension.Cube,
modifiable = prop.modifiable
};
result.Add(textureInfo);
}
}
foreach (var prop in properties.OfType<VirtualTextureShaderProperty>().Where(p => p.referenceName != null))
{
prop.AddTextureInfo(result);
}
return result;
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ca768a19c7caa844c83fceae4a411f04
timeCreated: 1469695793
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,24 @@
using System;
using UnityEditor.ShaderGraph.Internal;
namespace UnityEditor.ShaderGraph
{
static class ShaderGeneratorNames
{
private static string[] UV = { "uv0", "uv1", "uv2", "uv3" };
public static int UVCount = 4;
public const string ScreenPosition = "ScreenPosition";
public const string VertexColor = "VertexColor";
public const string FaceSign = "FaceSign";
public const string TimeParameters = "TimeParameters";
public const string BoneWeights = "BoneWeights";
public const string BoneIndices = "BoneIndices";
public const string VertexID = "VertexID";
public static string GetUVName(this UVChannel channel)
{
return UV[(int)channel];
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 33854ecfd53cd054a99d8cb17b260170
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,485 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEditor.ShaderGraph.Internal;
using UnityEngine.Profiling;
namespace UnityEditor.ShaderGraph
{
static class ShaderSpliceUtil
{
private static char[] channelNames =
{ 'x', 'y', 'z', 'w' };
private static char[] whitespace =
{ ' ', '\t', '\r', '\n', '\f'};
public static string GetChannelSwizzle(int firstChannel, int channelCount)
{
System.Text.StringBuilder result = new System.Text.StringBuilder();
int lastChannel = System.Math.Min(firstChannel + channelCount - 1, 4);
for (int index = firstChannel; index <= lastChannel; index++)
{
result.Append(channelNames[index]);
}
return result.ToString();
}
// returns the offset of the first non-whitespace character, in the range [start, end] inclusive ... will return end if none found
private static int SkipWhitespace(string str, int start, int end)
{
int index = start;
while (index < end)
{
char c = str[index];
if (!whitespace.Contains(c))
{
break;
}
index++;
}
return index;
}
public class TemplatePreprocessor
{
// inputs
ActiveFields activeFields;
Dictionary<string, string> namedFragments;
string[] templatePaths;
bool isDebug;
// intermediates
HashSet<string> includedFiles;
// outputs
ShaderStringBuilder result;
AssetCollection assetCollection;
public TemplatePreprocessor(ActiveFields activeFields, Dictionary<string, string> namedFragments, bool isDebug, string[] templatePaths, AssetCollection assetCollection, bool humanReadable, ShaderStringBuilder outShaderCodeResult = null)
{
this.activeFields = activeFields;
this.namedFragments = namedFragments;
this.isDebug = isDebug;
this.templatePaths = templatePaths;
this.assetCollection = assetCollection;
this.result = outShaderCodeResult ?? new ShaderStringBuilder(humanReadable: humanReadable);
includedFiles = new HashSet<string>();
}
public ShaderStringBuilder GetShaderCode()
{
return result;
}
public void ProcessTemplateFile(string filePath)
{
if (File.Exists(filePath) &&
!includedFiles.Contains(filePath))
{
includedFiles.Add(filePath);
if (assetCollection != null)
{
GUID guid = AssetDatabase.GUIDFromAssetPath(filePath);
if (!guid.Empty())
assetCollection.AddAssetDependency(guid, AssetCollection.Flags.SourceDependency);
}
string[] templateLines = File.ReadAllLines(filePath);
foreach (string line in templateLines)
{
ProcessTemplateLine(line, 0, line.Length);
}
}
}
private struct Token
{
public string s;
public int start;
public int end;
public Token(string s, int start, int end)
{
this.s = s;
this.start = start;
this.end = end;
}
public static Token Invalid()
{
return new Token(null, 0, 0);
}
public bool IsValid()
{
return (s != null);
}
public bool Is(string other)
{
int len = end - start;
return (other.Length == len) && (0 == string.CompareOrdinal(s, start, other, 0, len));
}
public string GetString()
{
int len = end - start;
if (len > 0)
{
return s.Substring(start, end - start);
}
return null;
}
}
public void ProcessTemplateLine(string line, int start, int end)
{
bool appendEndln = true;
int cur = start;
while (cur < end)
{
// find an escape code '$'
int dollar = line.IndexOf('$', cur, end - cur);
if (dollar < 0)
{
// no escape code found in the remaining code -- just append the rest verbatim
AppendSubstring(line, cur, true, end, false);
break;
}
else
{
// found $ escape sequence
Token command = ParseIdentifier(line, dollar + 1, end);
if (!command.IsValid())
{
Error("ERROR: $ must be followed by a command string (if, splice, or include)", line, dollar + 1);
break;
}
else
{
if (command.Is("include"))
{
ProcessIncludeCommand(command, end);
appendEndln = false;
break; // include command always ignores the rest of the line, error or not
}
else if (command.Is("splice"))
{
if (!ProcessSpliceCommand(command, end, ref cur))
{
// error, skip the rest of the line
break;
}
}
else
{
// let's see if it is a predicate
Token predicate = ParseUntil(line, dollar + 1, end, ':');
if (!predicate.IsValid())
{
Error("ERROR: unrecognized command: " + command.GetString(), line, command.start);
break;
}
else
{
if (!ProcessPredicate(predicate, end, ref cur, ref appendEndln))
{
break; // skip the rest of the line
}
}
}
}
}
}
if (appendEndln)
{
result.AppendNewLine();
}
}
private void ProcessIncludeCommand(Token includeCommand, int lineEnd)
{
if (Expect(includeCommand.s, includeCommand.end, '('))
{
Token param = ParseString(includeCommand.s, includeCommand.end + 1, lineEnd);
if (!param.IsValid())
{
Error("ERROR: $include expected a string file path parameter", includeCommand.s, includeCommand.end + 1);
}
else
{
bool found = false;
string includeLocation = null;
// Use reverse order in the array, higher number element have higher priority in case $include exist in several directories
for (int i = templatePaths.Length - 1; i >= 0; i--)
{
string templatePath = templatePaths[i];
includeLocation = Path.Combine(templatePath, param.GetString());
if (File.Exists(includeLocation))
{
found = true;
break;
}
}
if (!found)
{
string errorStr = "ERROR: $include cannot find file : " + param.GetString() + ". Looked into:\n";
foreach (string templatePath in templatePaths)
{
errorStr += "// " + templatePath + "\n";
}
Error(errorStr, includeCommand.s, param.start);
}
else
{
int endIndex = result.length;
using (var temp = new ShaderStringBuilder(humanReadable: true))
{
// Wrap in debug mode
if (isDebug)
{
result.AppendLine("//-------------------------------------------------------------------------------------");
result.AppendLine("// TEMPLATE INCLUDE : " + param.GetString());
result.AppendLine("//-------------------------------------------------------------------------------------");
result.AppendNewLine();
}
// Recursively process templates
ProcessTemplateFile(includeLocation);
// Wrap in debug mode
if (isDebug)
{
result.AppendNewLine();
result.AppendLine("//-------------------------------------------------------------------------------------");
result.AppendLine("// END TEMPLATE INCLUDE : " + param.GetString());
result.AppendLine("//-------------------------------------------------------------------------------------");
}
result.AppendNewLine();
// Required to enforce indentation rules
// Append lines from this include into temporary StringBuilder
// Reduce result length to remove this include
temp.AppendLines(result.ToString(endIndex, result.length - endIndex));
result.length = endIndex;
result.AppendLines(temp.ToCodeBlock());
}
}
}
}
}
private bool ProcessSpliceCommand(Token spliceCommand, int lineEnd, ref int cur)
{
if (!Expect(spliceCommand.s, spliceCommand.end, '('))
{
return false;
}
else
{
Token param = ParseUntil(spliceCommand.s, spliceCommand.end + 1, lineEnd, ')');
if (!param.IsValid())
{
Error("ERROR: splice command is missing a ')'", spliceCommand.s, spliceCommand.start);
return false;
}
else
{
// append everything before the beginning of the escape sequence
AppendSubstring(spliceCommand.s, cur, true, spliceCommand.start - 1, false);
// find the named fragment
string name = param.GetString(); // unfortunately this allocates a new string
string fragment;
if ((namedFragments != null) && namedFragments.TryGetValue(name, out fragment))
{
// splice the fragment
result.Append(fragment);
}
else
{
// no named fragment found
result.Append("/* WARNING: $splice Could not find named fragment '{0}' */", name);
}
// advance to just after the ')' and continue parsing
cur = param.end + 1;
}
}
return true;
}
private bool ProcessPredicate(Token predicate, int endLine, ref int cur, ref bool appendEndln)
{
// eval if(param)
var fieldName = predicate.GetString();
var nonwhitespace = SkipWhitespace(predicate.s, predicate.end + 1, endLine);
if (!fieldName.StartsWith("features", StringComparison.Ordinal) && activeFields.permutationCount > 0)
{
var passedPermutations = activeFields.allPermutations.instances.Where(i => i.Contains(fieldName)).ToList();
if (passedPermutations.Count > 0)
{
var ifdefs = KeywordUtil.GetKeywordPermutationSetConditional(
passedPermutations.Select(i => i.permutationIndex).ToList()
);
result.AppendLine(ifdefs);
//Append the rest of the line
AppendSubstring(predicate.s, nonwhitespace, true, endLine, false);
result.AppendNewLine();
result.AppendLine("#endif");
return false;
}
else
{
appendEndln = false; //if line isn't active, remove whitespace
}
return false;
}
else
{
// eval if(param)
bool contains = activeFields.baseInstance.Contains(fieldName);
if (contains)
{
// predicate is active
// append everything before the beginning of the escape sequence
AppendSubstring(predicate.s, cur, true, predicate.start - 1, false);
// continue parsing the rest of the line, starting with the first nonwhitespace character
cur = nonwhitespace;
return true;
}
else
{
// predicate is not active
if (isDebug)
{
// append everything before the beginning of the escape sequence
AppendSubstring(predicate.s, cur, true, predicate.start - 1, false);
// append the rest of the line, commented out
result.Append("// ");
AppendSubstring(predicate.s, nonwhitespace, true, endLine, false);
}
else
{
// don't append anything
appendEndln = false;
}
return false;
}
}
}
private static bool IsLetter(char c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
private static bool IsLetterOrDigit(char c)
{
return IsLetter(c) || Char.IsDigit(c);
}
private Token ParseIdentifier(string code, int start, int end)
{
if (start < end)
{
char c = code[start];
if (IsLetter(c) || (c == '_'))
{
int cur = start + 1;
while (cur < end)
{
c = code[cur];
if (!(IsLetterOrDigit(c) || (c == '_')))
break;
cur++;
}
return new Token(code, start, cur);
}
}
return Token.Invalid();
}
private Token ParseString(string line, int start, int end)
{
if (Expect(line, start, '"'))
{
return ParseUntil(line, start + 1, end, '"');
}
return Token.Invalid();
}
private Token ParseUntil(string line, int start, int end, char endChar)
{
int cur = start;
while (cur < end)
{
if (line[cur] == endChar)
{
return new Token(line, start, cur);
}
cur++;
}
return Token.Invalid();
}
private bool Expect(string line, int location, char expected)
{
if ((location < line.Length) && (line[location] == expected))
{
return true;
}
Error("Expected '" + expected + "'", line, location);
return false;
}
private void Error(string error, string line, int location)
{
// append the line for context
result.Append("\n");
result.Append("// ");
AppendSubstring(line, 0, true, line.Length, false);
result.Append("\n");
// append the location marker, and error description
result.Append("// ");
result.AppendSpaces(location);
result.Append("^ ");
result.Append(error);
result.Append("\n");
}
// an easier to use version of substring Append() -- explicit inclusion on each end, and checks for positive length
private void AppendSubstring(string str, int start, bool includeStart, int end, bool includeEnd)
{
if (!includeStart)
{
start++;
}
if (!includeEnd)
{
end--;
}
int count = end - start + 1;
if (count > 0)
{
result.Append(str, start, count);
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ab3839d7cd68bbd4baa33356cbed2b29
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,295 @@
using System;
using System.Collections.Generic;
using System.Text;
using JetBrains.Annotations;
using UnityEditor.Graphing;
using System.Globalization;
using UnityEngine.Profiling;
namespace UnityEditor.ShaderGraph
{
struct ShaderStringMapping
{
public AbstractMaterialNode node { get; set; }
// public List<AbstractMaterialNode> nodes { get; set; }
public int startIndex { get; set; }
public int count { get; set; }
}
class ShaderStringBuilder : IDisposable
{
enum ScopeType
{
Indent,
Block,
BlockSemicolon
}
StringBuilder m_StringBuilder;
Stack<ScopeType> m_ScopeStack;
int m_IndentationLevel;
ShaderStringMapping m_CurrentMapping;
List<ShaderStringMapping> m_Mappings;
bool m_HumanReadable;
const string k_IndentationString = " ";
const string k_NewLineString = "\n";
internal AbstractMaterialNode currentNode
{
get { return m_CurrentMapping.node; }
set
{
m_CurrentMapping.count = m_StringBuilder.Length - m_CurrentMapping.startIndex;
if (m_CurrentMapping.count > 0)
m_Mappings.Add(m_CurrentMapping);
m_CurrentMapping.node = value;
m_CurrentMapping.startIndex = m_StringBuilder.Length;
m_CurrentMapping.count = 0;
}
}
internal List<ShaderStringMapping> mappings
{
get { return m_Mappings; }
}
public ShaderStringBuilder(int indentationLevel = 0, int stringBuilderSize = 8192, bool humanReadable = false)
{
IncreaseIndent(indentationLevel);
m_StringBuilder = new StringBuilder(stringBuilderSize);
m_ScopeStack = new Stack<ScopeType>();
m_Mappings = new List<ShaderStringMapping>();
m_CurrentMapping = new ShaderStringMapping();
m_HumanReadable = humanReadable;
}
public void AppendNewLine()
{
m_StringBuilder.Append(k_NewLineString);
}
private void AppendLine(string value, int startIndex, int count)
{
if (value.Length > 0)
{
TryAppendIndentation();
m_StringBuilder.Append(value, startIndex, count);
}
AppendNewLine();
}
public void AppendLine(string value)
{
if (!string.IsNullOrEmpty(value))
{
TryAppendIndentation();
m_StringBuilder.Append(value);
}
AppendNewLine();
}
[StringFormatMethod("formatString")]
public void AppendLine(string formatString, params object[] args)
{
TryAppendIndentation();
m_StringBuilder.AppendFormat(CultureInfo.InvariantCulture, formatString, args);
AppendNewLine();
}
static readonly char[] LineSeparators = new[] { '\n', '\r'};
public void AppendLines(string lines)
{
if (string.IsNullOrEmpty(lines))
return;
int startSearchIndex = 0;
int indexOfNextBreak = lines.IndexOfAny(LineSeparators);
while (indexOfNextBreak >= 0)
{
AppendLine(lines, startSearchIndex, indexOfNextBreak - startSearchIndex);
startSearchIndex = indexOfNextBreak + 1;
indexOfNextBreak = lines.IndexOfAny(LineSeparators, startSearchIndex);
}
if (startSearchIndex < lines.Length)
{
AppendLine(lines, startSearchIndex, lines.Length - startSearchIndex);
}
}
public void Append(string value)
{
m_StringBuilder.Append(value);
}
public void Append(string value, int start, int count)
{
m_StringBuilder.Append(value, start, count);
}
[StringFormatMethod("formatString")]
public void Append(string formatString, params object[] args)
{
m_StringBuilder.AppendFormat(formatString, args);
}
public void AppendSpaces(int count)
{
m_StringBuilder.Append(' ', count);
}
public void TryAppendIndentation()
{
if (m_HumanReadable)
{
for (var i = 0; i < m_IndentationLevel; i++)
m_StringBuilder.Append(k_IndentationString);
}
}
public IDisposable IndentScope()
{
m_ScopeStack.Push(ScopeType.Indent);
IncreaseIndent();
return this;
}
public IDisposable BlockScope()
{
AppendLine("{");
IncreaseIndent();
m_ScopeStack.Push(ScopeType.Block);
return this;
}
public IDisposable BlockSemicolonScope()
{
AppendLine("{");
IncreaseIndent();
m_ScopeStack.Push(ScopeType.BlockSemicolon);
return this;
}
public void IncreaseIndent()
{
m_IndentationLevel++;
}
public void IncreaseIndent(int level)
{
for (var i = 0; i < level; i++)
IncreaseIndent();
}
public void DecreaseIndent()
{
m_IndentationLevel--;
}
public void DecreaseIndent(int level)
{
for (var i = 0; i < level; i++)
DecreaseIndent();
}
public void Dispose()
{
if (m_ScopeStack.Count == 0)
return;
switch (m_ScopeStack.Pop())
{
case ScopeType.Indent:
DecreaseIndent();
break;
case ScopeType.Block:
DecreaseIndent();
AppendLine("}");
break;
case ScopeType.BlockSemicolon:
DecreaseIndent();
AppendLine("};");
break;
}
}
public void Concat(ShaderStringBuilder other)
{
// First re-add all the mappings from `other`, such that their mappings are transformed.
foreach (var mapping in other.m_Mappings)
{
currentNode = mapping.node;
// Use `AppendLines` to indent according to the current indentation.
if (m_HumanReadable)
{
AppendLines(other.ToString(mapping.startIndex, mapping.count));
}
else
{
Append(other.ToString(mapping.startIndex, mapping.count));
}
}
currentNode = other.currentNode;
if (m_HumanReadable)
{
AppendLines(other.ToString(other.m_CurrentMapping.startIndex, other.length - other.m_CurrentMapping.startIndex));
}
else
{
Append(other.ToString(other.m_CurrentMapping.startIndex, other.length - other.m_CurrentMapping.startIndex));
}
}
public void ReplaceInCurrentMapping(string oldValue, string newValue)
{
Profiler.BeginSample("ReplaceInCurrentMapping");
int start = m_CurrentMapping.startIndex;
int end = m_StringBuilder.Length - start;
m_StringBuilder.Replace(oldValue, newValue, start, end);
Profiler.EndSample();
}
public void Replace(string oldValue, string newValue, int start, int end)
{
m_StringBuilder.Replace(oldValue, newValue, start, end);
}
public string ToCodeBlock()
{
// Remove new line
if (m_StringBuilder.Length > 0)
m_StringBuilder.Length = m_StringBuilder.Length - 1;
if (m_HumanReadable)
{
// Set indentations
m_StringBuilder.Replace(Environment.NewLine, Environment.NewLine + k_IndentationString);
}
return m_StringBuilder.ToString();
}
public override string ToString()
{
return m_StringBuilder.ToString();
}
public string ToString(int startIndex, int length)
{
return m_StringBuilder.ToString(startIndex, length);
}
internal void Clear()
{
m_StringBuilder.Length = 0;
}
internal int length
{
get { return m_StringBuilder.Length; }
set { m_StringBuilder.Length = value; }
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e64ba38b003f49bdb6ae4a2259be61c6
timeCreated: 1513172221