initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 03e494bdcf7e6474693797d5feaccb15
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c9895b99811e6d549867e6cba9fcd44f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a809112e0e86a0945a16de7cf2d3916c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ecf3346505d12bb47845b60544d5913a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4200a9cc82e224ab89a574f04d74f1d1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1358e9b39cd7e8442bece5b15a2a7c63
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ca768a19c7caa844c83fceae4a411f04
|
||||
timeCreated: 1469695793
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 33854ecfd53cd054a99d8cb17b260170
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ab3839d7cd68bbd4baa33356cbed2b29
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e64ba38b003f49bdb6ae4a2259be61c6
|
||||
timeCreated: 1513172221
|
Loading…
Add table
Add a link
Reference in a new issue