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

303 lines
16 KiB

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.IO;
using UnityEngine;
using UnityEditor.Build;
using UnityEditor.Graphing;
using UnityEditor.ShaderGraph.Internal;
using UnityEditor.ShaderGraph.Drawing;
using UnityEngine.Rendering;
using UnityEngine.Assertions;
using Pool = UnityEngine.Pool;
using UnityEngine.Profiling;
namespace UnityEditor.ShaderGraph
{
internal class GeneratorDerivativeUtils
{
public static FieldDescriptor uv0Ddx = new FieldDescriptor(StructFields.SurfaceDescriptionInputs.name, "uv0Ddx", "", ShaderValueType.Float4,
subscriptOptions: StructFieldOptions.Optional);
public static FieldDescriptor uv0Ddy = new FieldDescriptor(StructFields.SurfaceDescriptionInputs.name, "uv0Ddy", "", ShaderValueType.Float4,
subscriptOptions: StructFieldOptions.Optional);
public static FieldDescriptor uv1Ddx = new FieldDescriptor(StructFields.SurfaceDescriptionInputs.name, "uv1Ddx", "", ShaderValueType.Float4,
subscriptOptions: StructFieldOptions.Optional);
public static FieldDescriptor uv1Ddy = new FieldDescriptor(StructFields.SurfaceDescriptionInputs.name, "uv1Ddy", "", ShaderValueType.Float4,
subscriptOptions: StructFieldOptions.Optional);
public static FieldDescriptor uv2Ddx = new FieldDescriptor(StructFields.SurfaceDescriptionInputs.name, "uv2Ddx", "", ShaderValueType.Float4,
subscriptOptions: StructFieldOptions.Optional);
public static FieldDescriptor uv2Ddy = new FieldDescriptor(StructFields.SurfaceDescriptionInputs.name, "uv2Ddy", "", ShaderValueType.Float4,
subscriptOptions: StructFieldOptions.Optional);
public static FieldDescriptor uv3Ddx = new FieldDescriptor(StructFields.SurfaceDescriptionInputs.name, "uv3Ddx", "", ShaderValueType.Float4,
subscriptOptions: StructFieldOptions.Optional);
public static FieldDescriptor uv3Ddy = new FieldDescriptor(StructFields.SurfaceDescriptionInputs.name, "uv3Ddy", "", ShaderValueType.Float4,
subscriptOptions: StructFieldOptions.Optional);
#region GeneratePassStructsAndInterpolators
// This funcion is an exact copy of the lines in Generator.cs, except the the member value m_HumanReadable is replaced with the
// variable isHumanReadable so that this function can be static.
internal static void GeneratePassStructsAndInterpolators(out ShaderStringBuilder interpolatorBuilder, out ShaderStringBuilder passStructBuilder, ActiveFields activeFields, List<StructDescriptor> passStructs, bool isHumanReadable)
{
// -----------------------------
// Generated structs and Packing code
Profiler.BeginSample("StructsAndPacking");
interpolatorBuilder = new ShaderStringBuilder(humanReadable: isHumanReadable);
if (passStructs != null)
{
var packedStructs = new List<StructDescriptor>();
foreach (var shaderStruct in passStructs)
{
if (shaderStruct.packFields == false)
continue; //skip structs that do not need interpolator packs
List<int> packedCounts = new List<int>();
var packStruct = new StructDescriptor();
//generate packed functions
if (activeFields.permutationCount > 0)
{
var generatedPackedTypes = new Dictionary<string, (ShaderStringBuilder, List<int>)>();
foreach (var instance in activeFields.allPermutations.instances)
{
var instanceGenerator = new ShaderStringBuilder();
GenerationUtils.GenerateInterpolatorFunctions(shaderStruct, instance, isHumanReadable, out instanceGenerator);
var key = instanceGenerator.ToCodeBlock();
if (generatedPackedTypes.TryGetValue(key, out var value))
value.Item2.Add(instance.permutationIndex);
else
generatedPackedTypes.Add(key, (instanceGenerator, new List<int> { instance.permutationIndex }));
}
var isFirst = true;
foreach (var generated in generatedPackedTypes)
{
if (isFirst)
{
isFirst = false;
interpolatorBuilder.AppendLine(KeywordUtil.GetKeywordPermutationSetConditional(generated.Value.Item2));
}
else
interpolatorBuilder.AppendLine(KeywordUtil.GetKeywordPermutationSetConditional(generated.Value.Item2).Replace("#if", "#elif"));
//interpolatorBuilder.Concat(generated.Value.Item1);
interpolatorBuilder.AppendLines(generated.Value.Item1.ToString());
}
if (generatedPackedTypes.Count > 0)
interpolatorBuilder.AppendLine("#endif");
}
else
{
ShaderStringBuilder localInterpolatorBuilder; // GenerateInterpolatorFunctions do the allocation
GenerationUtils.GenerateInterpolatorFunctions(shaderStruct, activeFields.baseInstance, isHumanReadable, out localInterpolatorBuilder);
interpolatorBuilder.Concat(localInterpolatorBuilder);
}
//using interp index from functions, generate packed struct descriptor
GenerationUtils.GeneratePackedStruct(shaderStruct, activeFields, out packStruct);
packedStructs.Add(packStruct);
}
passStructs.AddRange(packedStructs);
}
if (interpolatorBuilder.length != 0) //hard code interpolators to float, TODO: proper handle precision
interpolatorBuilder.ReplaceInCurrentMapping(PrecisionUtil.Token, ConcretePrecision.Single.ToShaderString());
else
interpolatorBuilder.AppendLine("//Interpolator Packs: <None>");
Profiler.EndSample();
// Generated String Builders for all struct types
Profiler.BeginSample("StructTypes");
{
passStructBuilder = new ShaderStringBuilder(humanReadable: isHumanReadable);
if (passStructs != null)
{
var structBuilder = new ShaderStringBuilder(humanReadable: isHumanReadable);
foreach (StructDescriptor shaderStruct in passStructs)
{
GenerationUtils.GenerateShaderStruct(shaderStruct, activeFields, isHumanReadable, out structBuilder);
structBuilder.ReplaceInCurrentMapping(PrecisionUtil.Token, ConcretePrecision.Single.ToShaderString()); //hard code structs to float, TODO: proper handle precision
passStructBuilder.Concat(structBuilder);
}
}
if (passStructBuilder.length == 0)
passStructBuilder.AppendLine("//Pass Structs: <None>");
}
Profiler.EndSample();
}
#endregion
// Fundamentally, this function takes as input the varios in-progress structs/code, parses the results, and splices in the partial derivatives.
// The "outputs" override the existing values where necessary.
// Outputs:
// 1. spliceCommands
// - Overwrites the "InterpolatorPack" and "PassStructs" splice points.
// 2. neededUvDerivatives
// - Specificies which uvs need derivatives which will be put into the requirements structure so that we can output
// the correct #defines which cause the ddx/ddy interpolators to be generated.
internal static void ParseAndModifyForAnalyticDerivatives(
Target target,
Dictionary<string, string> spliceCommands,
bool[] neededUvDerivs,
PassDescriptor pass,
ActiveFields activeFields,
PropertyCollector subShaderProperties,
PropertyCollector propertyCollector,
KeywordCollector keywordCollector,
List<AbstractMaterialNode> vertexNodes,
List<AbstractMaterialNode> pixelNodes,
List<int>[] vertexNodePermutations,
List<int>[] pixelNodePermutations,
List<StructDescriptor> originalPassStructs,
bool applyEmulatedDerivatives,
bool isHumanReadable,
string primaryShaderFullName,
ConcretePrecision graphDefaultConcretePrecision)
{
// we need a list of the user custom funcs in order to ignore them during parsing
List<string> customFuncs = new List<string>();
for (int i = 0; i < pixelNodes.Count; i++)
{
if (pixelNodes[i] is CustomFunctionNode customNode)
{
string[] precNames = { "float", "half" };
for (int precIter = 0; precIter < 2; precIter++)
{
string currFunc = customNode.hlslFunctionName;
currFunc = currFunc.Replace("$precision", precNames[precIter]);
customFuncs.Add(currFunc);
}
}
}
// preview mode so that we get the global structs directly instead of as dots macros
var propertyBuilder = new ShaderStringBuilder(humanReadable: isHumanReadable);
subShaderProperties.GetPropertiesDeclaration(propertyBuilder, GenerationMode.Preview, graphDefaultConcretePrecision);
string propStr = propertyBuilder.ToString();
string surfaceDescStr;
{
StructDescriptor structDesc = new StructDescriptor();
foreach (var item in pass.structs)
{
if (item.descriptor.name == "SurfaceDescriptionInputs")
{
structDesc = item.descriptor;
}
}
// follow the same rules as above
var structBuilder = new ShaderStringBuilder(humanReadable: isHumanReadable);
GenerationUtils.GenerateShaderStruct(structDesc, activeFields, isHumanReadable, out structBuilder);
structBuilder.ReplaceInCurrentMapping(PrecisionUtil.Token, ConcretePrecision.Single.ToShaderString()); //hard code structs to float, TODO: proper handle precision
surfaceDescStr = structBuilder.ToCodeBlock();
}
string graphFuncStr = spliceCommands["GraphFunctions"];
string graphPixelStr = spliceCommands["GraphPixel"];
string dstGraphFunctions;
string dstGraphPixel;
bool[] adjustedUvDerivs;
bool success = target.DerivativeModificationCallback(
out dstGraphFunctions,
out dstGraphPixel,
out adjustedUvDerivs,
primaryShaderFullName,
pass.displayName,
propStr,
surfaceDescStr,
graphFuncStr,
graphPixelStr,
customFuncs,
applyEmulatedDerivatives);
if (success)
{
// use the new uv derivative needs to produce text
ShaderGraphRequirementsPerKeyword graphRequirements = new ShaderGraphRequirementsPerKeyword();
GenerationUtils.GetActiveFieldsAndPermutationsForNodes(pass, keywordCollector, vertexNodes, pixelNodes, adjustedUvDerivs,
vertexNodePermutations, pixelNodePermutations, activeFields, out graphRequirements);
ShaderStringBuilder interpolatorBuilder = new ShaderStringBuilder();
ShaderStringBuilder passStructBuilder = new ShaderStringBuilder();
// start with the original structs, and add interpolators, which might include uv0Ddx, uv0Ddy, etc.
List<StructDescriptor> adjustedPassStructs = new List<StructDescriptor>(originalPassStructs);
GeneratePassStructsAndInterpolators(out interpolatorBuilder, out passStructBuilder, activeFields, adjustedPassStructs, isHumanReadable);
string dstInterpolatorPack = interpolatorBuilder.ToCodeBlock();
string dstPassStructs = passStructBuilder.ToCodeBlock();
System.Array.Copy(adjustedUvDerivs, neededUvDerivs, neededUvDerivs.Length);
spliceCommands["InterpolatorPack"] = dstInterpolatorPack;
spliceCommands["PassStructs"] = dstPassStructs;
spliceCommands["GraphFunctions"] = dstGraphFunctions;
spliceCommands["GraphPixel"] = dstGraphPixel;
}
}
internal static void ApplyAnalyticDerivatives(
Target target,
Dictionary<string, string> spliceCommands,
PassDescriptor pass,
ActiveFields activeFields,
PropertyCollector subShaderProperties,
PropertyCollector propertyCollector,
KeywordCollector keywordCollector,
List<AbstractMaterialNode> vertexNodes,
List<AbstractMaterialNode> pixelNodes,
List<int>[] vertexNodePermutations,
List<int>[] pixelNodePermutations,
List<StructDescriptor> originalPassStructs,
bool applyEmulatedDerivatives,
bool isHumanReadable,
string primaryShaderFullName,
ConcretePrecision graphDefaultConcretePrecision)
{
bool[] neededUvDerivs = new bool[4];
// if we have any keywords per-node, then the generated code has #ifdefs and is unparsable
bool isValidForDerivatives = (keywordCollector.keywords.Count == 0 && keywordCollector.permutations.Count == 0);
if (isValidForDerivatives && pass.analyticDerivativesEnabled)
{
GeneratorDerivativeUtils.ParseAndModifyForAnalyticDerivatives(
target,
spliceCommands,
neededUvDerivs,
pass,
activeFields,
subShaderProperties,
propertyCollector,
keywordCollector,
vertexNodes,
pixelNodes,
vertexNodePermutations,
pixelNodePermutations,
originalPassStructs,
pass.analyticDerivativesApplyEmulate,
isHumanReadable,
primaryShaderFullName,
graphDefaultConcretePrecision);
}
Profiler.BeginSample("InputDerivativeDefines");
using (var passTokenBuilder = new ShaderStringBuilder(humanReadable: isHumanReadable))
{
// check for needed derivatives
for (int i = 0; i < neededUvDerivs.Length; i++)
{
if (neededUvDerivs[i])
{
passTokenBuilder.AppendLine("#define FRAG_INPUTS_USE_TEXCOORD{0}_DERIV", i.ToString());
}
}
string command = GenerationUtils.GetSpliceCommand(passTokenBuilder.ToCodeBlock(), "InputDerivativeDefines");
spliceCommands.Add("InputDerivativeDefines", command);
}
Profiler.EndSample();
}
}
}