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.
566 lines
31 KiB
566 lines
31 KiB
using System;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using UnityEditor.ShaderGraph;
|
|
using UnityEditor.ShaderGraph.Internal;
|
|
using UnityEngine;
|
|
using UnityEngine.VFX;
|
|
|
|
namespace UnityEditor.VFX
|
|
{
|
|
static class VFXSubTarget
|
|
{
|
|
// See: VFXShaderWriter.TypeToUniformCode
|
|
// TODO: Collapse these two maps into one
|
|
public static readonly Dictionary<Type, Type> kVFXShaderPropertyMap = new Dictionary<Type, Type>
|
|
{
|
|
{ typeof(float), typeof(Vector1ShaderProperty) },
|
|
{ typeof(Vector2), typeof(Vector2ShaderProperty) },
|
|
{ typeof(Vector3), typeof(Vector3ShaderProperty) },
|
|
{ typeof(Vector4), typeof(Vector4ShaderProperty) },
|
|
{ typeof(int), typeof(Vector1ShaderProperty) },
|
|
{ typeof(uint), typeof(Vector1ShaderProperty) },
|
|
{ typeof(Matrix4x4), typeof(Matrix4ShaderProperty) },
|
|
{ typeof(bool), typeof(BooleanShaderProperty) },
|
|
};
|
|
|
|
public static readonly Dictionary<Type, ShaderValueType> kVFXShaderValueTypeMap = new Dictionary<Type, ShaderValueType>
|
|
{
|
|
{ typeof(float), ShaderValueType.Float },
|
|
{ typeof(Vector2), ShaderValueType.Float2 },
|
|
{ typeof(Vector3), ShaderValueType.Float3 },
|
|
{ typeof(Vector4), ShaderValueType.Float4 },
|
|
{ typeof(int), ShaderValueType.Integer },
|
|
{ typeof(uint), ShaderValueType.Uint },
|
|
{ typeof(Matrix4x4), ShaderValueType.Matrix4 },
|
|
{ typeof(bool), ShaderValueType.Float }, // NOTE: Map boolean to float for VFX interpolator due to how ShaderGraph handles semantics for boolean interpolator.
|
|
};
|
|
|
|
public static ShaderValueType VFXTypeToSGType(VFXValueType t)
|
|
{
|
|
return kVFXShaderValueTypeMap[VFXExpression.TypeToType(t)];
|
|
}
|
|
|
|
public static FieldDescriptor VFXAttributeToFieldDescriptor(VFXAttribute attribute)
|
|
{
|
|
var type = VFXExpression.TypeToType(attribute.type);
|
|
|
|
if (!kVFXShaderValueTypeMap.TryGetValue(type, out var shaderValueType))
|
|
return null;
|
|
|
|
return new FieldDescriptor("VFXAttributes", attribute.name, "", shaderValueType);
|
|
}
|
|
|
|
static class VFXFields
|
|
{
|
|
public const string kTag = "OutputType";
|
|
public static FieldDescriptor ParticleMesh = new FieldDescriptor(kTag, "Mesh", "VFX_PARTICLE_MESH 1");
|
|
public static FieldDescriptor ParticlePlanarPrimitive = new FieldDescriptor(kTag, "PlanarPrimitive", "VFX_PARTICLE_PLANAR_PRIMITIVE 1");
|
|
public static FieldDescriptor RaytracingVFX = new FieldDescriptor(string.Empty, "RaytracingVFX", string.Empty);
|
|
}
|
|
|
|
internal static void GetFields(ref TargetFieldContext fieldsContext, VFXContext context)
|
|
{
|
|
fieldsContext.AddField(Fields.GraphVFX);
|
|
|
|
// Select the primitive implementation.
|
|
switch (context.taskType)
|
|
{
|
|
case VFXTaskType.ParticleMeshOutput:
|
|
fieldsContext.AddField(VFXFields.ParticleMesh);
|
|
break;
|
|
case VFXTaskType.ParticleTriangleOutput:
|
|
case VFXTaskType.ParticleOctagonOutput:
|
|
case VFXTaskType.ParticleQuadOutput:
|
|
fieldsContext.AddField(VFXFields.ParticlePlanarPrimitive);
|
|
break;
|
|
}
|
|
if (((VFXAbstractParticleOutput)context).isRayTraced && fieldsContext.pass.lightMode.Contains("DXR"))
|
|
{
|
|
fieldsContext.AddField(VFXFields.RaytracingVFX);
|
|
}
|
|
}
|
|
|
|
enum VFXAttributeType
|
|
{
|
|
Current,
|
|
Source
|
|
}
|
|
|
|
static readonly string[] kVFXAttributeStructNames =
|
|
{
|
|
"InternalAttributesElement",
|
|
"InternalSourceAttributesElement"
|
|
};
|
|
|
|
public static IEnumerable<(string name, ShaderValueType type, string interpolation)> GetVFXInterpolators(VFXContext context, VFXTaskCompiledData taskData)
|
|
{
|
|
if (taskData.SGInputs != null)
|
|
{
|
|
bool requiresInterpolation = context is VFXAbstractParticleOutput output && output.HasStrips();
|
|
string interpolationStr = requiresInterpolation ? string.Empty : "nointerpolation";
|
|
|
|
foreach (var interp in taskData.SGInputs.interpolators)
|
|
{
|
|
var (exp, name) = (interp.Key, interp.Value);
|
|
|
|
if (!VFXSubTarget.kVFXShaderValueTypeMap.TryGetValue(VFXExpression.TypeToType(exp.valueType), out var shaderValueType))
|
|
throw new Exception($"Unsupported interpolator type for {name}: {exp.valueType}");
|
|
|
|
yield return (name, shaderValueType, interpolationStr);
|
|
}
|
|
}
|
|
}
|
|
|
|
static StructDescriptor GenerateVFXAttributesStruct(VFXContext context, VFXAttributeType attributeType)
|
|
{
|
|
IEnumerable<VFXAttributeInfo> attributeInfos;
|
|
|
|
if (attributeType == VFXAttributeType.Current)
|
|
{
|
|
attributeInfos = context.GetData().GetAttributes().Where(a =>
|
|
(context.GetData().IsCurrentAttributeUsed(a.attrib, context)) ||
|
|
(context.contextType == VFXContextType.Init && context.GetData().IsAttributeStored(a.attrib))); // In init, needs to declare all stored attributes for intialization
|
|
}
|
|
else
|
|
{
|
|
attributeInfos = context.GetData().GetAttributes().Where(a => (context.GetData().IsSourceAttributeUsed(a.attrib, context)));
|
|
}
|
|
|
|
var attributes = attributeInfos.Select(a => a.attrib);
|
|
|
|
var attributeFieldDescriptors = new List<FieldDescriptor>();
|
|
foreach (var attribute in attributes)
|
|
{
|
|
var afd = VFXSubTarget.VFXAttributeToFieldDescriptor(attribute);
|
|
attributeFieldDescriptors.Add(afd);
|
|
}
|
|
|
|
return new StructDescriptor
|
|
{
|
|
name = kVFXAttributeStructNames[(int)attributeType],
|
|
fields = attributeFieldDescriptors.ToArray()
|
|
};
|
|
}
|
|
|
|
static void GenerateVFXAdditionalCommands(VFXContext context, VFXSRPBinder srp, VFXSRPBinder.ShaderGraphBinder shaderGraphBinder, VFXTaskCompiledData taskData,
|
|
out AdditionalCommandDescriptor srpCommonInclude,
|
|
out AdditionalCommandDescriptor perBlockDefines,
|
|
out AdditionalCommandDescriptor perBlockIncludes,
|
|
out AdditionalCommandDescriptor loadAttributeDescriptor,
|
|
out AdditionalCommandDescriptor blockFunctionDescriptor,
|
|
out AdditionalCommandDescriptor blockCallFunctionDescriptor,
|
|
out AdditionalCommandDescriptor interpolantsGenerationDescriptor,
|
|
out AdditionalCommandDescriptor buildFragInputsGenerationRTDescriptor,
|
|
out AdditionalCommandDescriptor buildVFXFragInputsDescriptor,
|
|
out AdditionalCommandDescriptor pixelPropertiesAssignDescriptor,
|
|
out AdditionalCommandDescriptor defineSpaceDescriptor,
|
|
out AdditionalCommandDescriptor parameterBufferDescriptor,
|
|
out AdditionalCommandDescriptor additionalDefinesDescriptor,
|
|
out AdditionalCommandDescriptor loadPositionAttributeDescriptor,
|
|
out AdditionalCommandDescriptor loadCropFactorAttributesDescriptor,
|
|
out AdditionalCommandDescriptor loadTexcoordAttributesDescriptor,
|
|
out AdditionalCommandDescriptor loadRayTracedScalingAttributesDescriptor,
|
|
out AdditionalCommandDescriptor loadCurrentFrameIndexParameterDescriptor,
|
|
out AdditionalCommandDescriptor vertexPropertiesGenerationDescriptor,
|
|
out AdditionalCommandDescriptor setInstancingIndicesDescriptor,
|
|
out AdditionalCommandDescriptor fillGraphValuesDescriptor,
|
|
out AdditionalCommandDescriptor loadContextDataDescriptor,
|
|
out AdditionalCommandDescriptor additionalFragInputs)
|
|
{
|
|
// TODO: Clean all of this up. Currently just an adapter between VFX Code Gen + SG Code Gen and *everything* has been stuffed here.
|
|
|
|
// Overwrite uniform names from the system uniform mapper
|
|
var particleData = context.GetData() as VFXDataParticle;
|
|
var systemUniformMapper = particleData.systemUniformMapper;
|
|
var graphValuesLayout = particleData.graphValuesLayout;
|
|
taskData.uniformMapper.OverrideUniformsNamesWithOther(systemUniformMapper);
|
|
|
|
// SRP Common Include
|
|
srpCommonInclude = new AdditionalCommandDescriptor("VFXSRPCommonInclude", string.Format("#include \"{0}\"", srp.runtimePath + "/VFXCommon.hlsl"));
|
|
|
|
// Load Attributes
|
|
loadAttributeDescriptor = new AdditionalCommandDescriptor("VFXLoadAttribute", VFXCodeGenerator.GenerateLoadAttribute(".", context, taskData).ToString());
|
|
|
|
// Graph Blocks
|
|
var expressionToName = VFXCodeGenerator.BuildExpressionToName(context, taskData);
|
|
VFXCodeGenerator.BuildContextBlocks(context, taskData, expressionToName,
|
|
out var blockFunction,
|
|
out var blockCallFunction,
|
|
out var blockIncludes,
|
|
out var blockDefines);
|
|
|
|
perBlockDefines = new AdditionalCommandDescriptor("VFXPerBlockDefines", blockDefines.builder.ToString());
|
|
perBlockIncludes = new AdditionalCommandDescriptor("VFXPerBlockIncludes", blockIncludes.builder.ToString());
|
|
|
|
blockFunctionDescriptor = new AdditionalCommandDescriptor("VFXGeneratedBlockFunction", blockFunction.builder.ToString());
|
|
blockCallFunctionDescriptor = new AdditionalCommandDescriptor("VFXProcessBlocks", blockCallFunction.builder.ToString());
|
|
|
|
// Vertex Input
|
|
VFXCodeGenerator.BuildVertexProperties(taskData, out var vertexPropertiesGeneration);
|
|
vertexPropertiesGenerationDescriptor = new AdditionalCommandDescriptor("VFXVertexPropertiesGeneration", vertexPropertiesGeneration);
|
|
|
|
// Interpolator
|
|
VFXCodeGenerator.BuildInterpolatorBlocks(taskData, out var interpolatorsGeneration);
|
|
interpolantsGenerationDescriptor = new AdditionalCommandDescriptor("VFXInterpolantsGeneration", interpolatorsGeneration);
|
|
|
|
// Frag Inputs - Only VFX will know if frag inputs come from interpolator or the CBuffer.
|
|
VFXCodeGenerator.BuildFragInputsGeneration(taskData, shaderGraphBinder.useFragInputs, out var buildFragInputsGeneration);
|
|
buildVFXFragInputsDescriptor = new AdditionalCommandDescriptor("VFXSetFragInputs", buildFragInputsGeneration);
|
|
|
|
VFXCodeGenerator.BuildFragInputsGenerationRayTracing(taskData, shaderGraphBinder.useFragInputs, out var buildFragInputsGenerationRT);
|
|
buildFragInputsGenerationRTDescriptor = new AdditionalCommandDescriptor("VFXSetFragInputsRT", buildFragInputsGenerationRT);
|
|
|
|
VFXCodeGenerator.BuildPixelPropertiesAssign(taskData, shaderGraphBinder.useFragInputs, out var pixelPropertiesAssign);
|
|
pixelPropertiesAssignDescriptor = new AdditionalCommandDescriptor("VFXPixelPropertiesAssign", pixelPropertiesAssign);
|
|
|
|
VFXCodeGenerator.BuildFillGraphValues(taskData, graphValuesLayout, systemUniformMapper, out var fillGraphValues);
|
|
fillGraphValuesDescriptor = new AdditionalCommandDescriptor("VFXLoadGraphValues", fillGraphValues);
|
|
|
|
VFXCodeGenerator.BuildLoadContextData(graphValuesLayout, out var loadContextData);
|
|
loadContextDataDescriptor = new AdditionalCommandDescriptor("VFXLoadContextData", loadContextData);
|
|
|
|
// Define coordinate space
|
|
var defineSpaceDescriptorContent = string.Empty;
|
|
if (context.GetData() is ISpaceable)
|
|
{
|
|
var spaceable = context.GetData() as ISpaceable;
|
|
defineSpaceDescriptorContent =
|
|
$"#define {(spaceable.space == VFXSpace.World ? "VFX_WORLD_SPACE" : "VFX_LOCAL_SPACE")} 1";
|
|
}
|
|
defineSpaceDescriptor = new AdditionalCommandDescriptor("VFXDefineSpace", defineSpaceDescriptorContent);
|
|
|
|
//Texture used as input of the shaderGraph will be declared by the shaderGraph generation
|
|
//However, if we are sampling a texture (or a mesh), we have to declare them before the VFX code generation.
|
|
//Thus, remove texture used in SG from VFX declaration and let the remainder.
|
|
var shaderGraph = VFXShaderGraphHelpers.GetShaderGraph(context);
|
|
var texureUsedInternallyInSG = shaderGraph.textureInfos.Select(o => o.name);
|
|
|
|
var textureExposedFromSG = context.inputSlots.Where(o =>
|
|
{
|
|
return VFXExpression.IsTexture(o.property.type);
|
|
}).Select(o => o.property.name);
|
|
|
|
var filteredTextureInSG = texureUsedInternallyInSG.Concat(textureExposedFromSG).ToArray();
|
|
|
|
// GraphValues + Buffers + Textures
|
|
VFXCodeGenerator.BuildParameterBuffer(taskData, filteredTextureInSG, out var parameterBuffer, out var needsGraphValueStruct);
|
|
parameterBufferDescriptor = new AdditionalCommandDescriptor("VFXParameterBuffer", parameterBuffer);
|
|
|
|
// Defines & Headers - Not all are necessary, however some important ones are mixed in like indirect draw, strips, flipbook, particle strip info...
|
|
ShaderStringBuilder additionalDefines = new ShaderStringBuilder();
|
|
// TODO: Need to add defines for current/source usage (i.e. scale).
|
|
|
|
var allCurrentAttributes = context.GetData().GetAttributes().Where(a =>
|
|
(context.GetData().IsCurrentAttributeUsed(a.attrib, context)) ||
|
|
(context.contextType == VFXContextType.Init && context.GetData().IsAttributeStored(a.attrib))); // In init, needs to declare all stored attributes for intialization
|
|
|
|
var allSourceAttributes = context.GetData().GetAttributes().Where(a => (context.GetData().IsSourceAttributeUsed(a.attrib, context)));
|
|
|
|
foreach (var attribute in allCurrentAttributes)
|
|
additionalDefines.AppendLine("#define VFX_USE_{0}_{1} 1", attribute.attrib.name.ToUpper(System.Globalization.CultureInfo.InvariantCulture), "CURRENT");
|
|
foreach (var attribute in allSourceAttributes)
|
|
additionalDefines.AppendLine("#define VFX_USE_{0}_{1} 1", attribute.attrib.name.ToUpper(System.Globalization.CultureInfo.InvariantCulture), "SOURCE");
|
|
foreach (var header in context.additionalDataHeaders)
|
|
additionalDefines.AppendLine(header);
|
|
foreach (var define in context.additionalDefines)
|
|
additionalDefines.AppendLine(define.Contains(' ') ? $"#define {define}" : $"#define {define} 1");
|
|
if(needsGraphValueStruct)
|
|
additionalDefines.AppendLine($"#define VFX_USE_GRAPH_VALUES 1");
|
|
foreach (string s in VFXCodeGenerator.GetInstancingAdditionalDefines(context, VFXTaskType.Output, particleData))
|
|
additionalDefines.AppendLine(s);
|
|
|
|
additionalDefinesDescriptor = new AdditionalCommandDescriptor("VFXDefines", additionalDefines.ToString());
|
|
|
|
// Load Position Attribute
|
|
loadPositionAttributeDescriptor = new AdditionalCommandDescriptor("VFXLoadPositionAttribute", VFXCodeGenerator.GenerateLoadAttribute("position", context, taskData).ToString());
|
|
|
|
// Load Crop Factor Attribute
|
|
var mainParameters = taskData.gpuMapper.CollectExpression(-1).ToArray();
|
|
loadCropFactorAttributesDescriptor = new AdditionalCommandDescriptor("VFXLoadCropFactorParameter", VFXCodeGenerator.GenerateLoadParameter("cropFactor", mainParameters, expressionToName).ToString());
|
|
loadTexcoordAttributesDescriptor = new AdditionalCommandDescriptor("VFXLoadTexcoordParameter", VFXCodeGenerator.GenerateLoadParameter("texCoord", mainParameters, expressionToName).ToString());
|
|
loadCurrentFrameIndexParameterDescriptor = new AdditionalCommandDescriptor("VFXLoadCurrentFrameIndexParameter", VFXCodeGenerator.GenerateLoadParameter("currentFrameIndex", mainParameters, expressionToName).ToString());
|
|
loadRayTracedScalingAttributesDescriptor = new AdditionalCommandDescriptor("VFXLoadRayTracedScaling", VFXCodeGenerator.GenerateLoadParameter("rayTracedScaling", mainParameters, expressionToName).ToString());
|
|
|
|
//Set VFX Instancing indices
|
|
setInstancingIndicesDescriptor = new AdditionalCommandDescriptor("VFXInitInstancing", VFXCodeGenerator.GenerateSetInstancingIndices().ToString());
|
|
|
|
additionalFragInputs = GenerateFragInputs(context, taskData);
|
|
}
|
|
|
|
static AdditionalCommandDescriptor GenerateFragInputs(VFXContext context, VFXTaskCompiledData taskData)
|
|
{
|
|
var builder = new ShaderStringBuilder();
|
|
|
|
if (taskData.SGInputs != null)
|
|
foreach (var input in taskData.SGInputs.fragInputs)
|
|
{
|
|
var (name, exp) = (input.Key, input.Value);
|
|
builder.AppendLine($"{VFXTypeToSGType(exp.valueType).ToShaderString("float")} {name};");
|
|
}
|
|
|
|
return new AdditionalCommandDescriptor("FragInputsVFX", builder.ToString());
|
|
}
|
|
|
|
static readonly (PragmaDescriptor oldDesc, PragmaDescriptor newDesc)[] k_CommonPragmaReplacement =
|
|
{
|
|
//Irrelevant general multicompile instancing (VFX will append them when needed)
|
|
( Pragma.MultiCompileInstancing, VFXSRPBinder.ShaderGraphBinder.kPragmaDescriptorNone),
|
|
( Pragma.DOTSInstancing, VFXSRPBinder.ShaderGraphBinder.kPragmaDescriptorNone),
|
|
( Pragma.InstancingOptions(InstancingOptions.RenderingLayer), VFXSRPBinder.ShaderGraphBinder.kPragmaDescriptorNone ),
|
|
( Pragma.InstancingOptions(InstancingOptions.NoLightProbe), VFXSRPBinder.ShaderGraphBinder.kPragmaDescriptorNone ),
|
|
( Pragma.InstancingOptions(InstancingOptions.NoLodFade), VFXSRPBinder.ShaderGraphBinder.kPragmaDescriptorNone ),
|
|
};
|
|
|
|
static PragmaCollection ApplyPragmaModifier(PragmaCollection pragmas, VFXSRPBinder.ShaderGraphBinder shaderGraphSRPInfo, bool addPragmaRequireCubeArray)
|
|
{
|
|
var pragmasReplacement = k_CommonPragmaReplacement;
|
|
if (shaderGraphSRPInfo.pragmasReplacement != null)
|
|
pragmasReplacement = shaderGraphSRPInfo.pragmasReplacement.Concat(pragmasReplacement).ToArray();
|
|
|
|
var overridenPragmas = new PragmaCollection();
|
|
foreach (var pragma in pragmas)
|
|
{
|
|
var currentPragma = pragma;
|
|
|
|
if (pragmasReplacement != null)
|
|
{
|
|
var replacement = pragmasReplacement.FirstOrDefault(o => o.oldDesc.value == pragma.descriptor.value);
|
|
if (replacement.newDesc.value == VFXSRPBinder.ShaderGraphBinder.kPragmaDescriptorNone.value)
|
|
continue; //Skip this irrelevant pragmas, kPragmaDescriptorNone shouldn't be null/empty
|
|
|
|
if (!string.IsNullOrEmpty(replacement.newDesc.value))
|
|
currentPragma = new PragmaCollection.Item(replacement.newDesc, pragma.fieldConditions);
|
|
}
|
|
overridenPragmas.Add(currentPragma.descriptor, currentPragma.fieldConditions);
|
|
}
|
|
|
|
if (addPragmaRequireCubeArray)
|
|
{
|
|
overridenPragmas.Add(new PragmaDescriptor() { value = "require cubearray" });
|
|
}
|
|
|
|
return overridenPragmas;
|
|
}
|
|
|
|
|
|
static StructCollection ApplyVaryingsStructModifier(StructCollection inStructs, VFXSRPBinder.ShaderGraphBinder shaderGraphSRPInfo, List<(string name, ShaderValueType type, string interpolation)> cachedVFXInterpolators)
|
|
{
|
|
// A key difference between Material Shader and VFX Shader generation is how surface properties are provided. Material Shaders
|
|
// simply provide properties via UnityPerMaterial cbuffer. VFX expects these same properties to be computed in the vertex
|
|
// stage (because we must evaluate them with the VFX blocks), and packed with the interpolators for the fragment stage.
|
|
|
|
var outStructs = new StructCollection();
|
|
outStructs.Add(shaderGraphSRPInfo.baseStructs);
|
|
foreach (var inStruct in inStructs)
|
|
{
|
|
//Currently all the varyings structs contain the string "Varyings" in it, in URP and HDRP
|
|
if (inStruct.descriptor.name.Contains("Varyings"))
|
|
{
|
|
var modifiedVaryingsDescriptor = inStruct.descriptor;
|
|
var fieldList = inStruct.descriptor.fields.ToList();
|
|
var vfxInterpolators = cachedVFXInterpolators.Select(o =>
|
|
new FieldDescriptor(inStruct.descriptor.name, o.name, string.Empty, o.type, subscriptOptions: StructFieldOptions.Static, interpolation: o.interpolation));
|
|
fieldList.AddRange(vfxInterpolators);
|
|
fieldList.AddRange(shaderGraphSRPInfo.varyingsAdditionalFields);
|
|
modifiedVaryingsDescriptor.fields = fieldList.ToArray();
|
|
outStructs.Add(modifiedVaryingsDescriptor, inStruct.fieldConditions);
|
|
}
|
|
}
|
|
return outStructs;
|
|
}
|
|
|
|
static readonly (KeywordDescriptor oldDesc, KeywordDescriptor newDesc)[] k_CommonKeywordReplacement =
|
|
{
|
|
(new KeywordDescriptor() {referenceName = Rendering.BuiltIn.ShaderGraph.BuiltInFields.VelocityPrecomputed.define}, VFXSRPBinder.ShaderGraphBinder.kKeywordDescriptorNone)
|
|
};
|
|
|
|
static readonly (KeywordDescriptor oldDesc, KeywordDescriptor newDesc)[] k_CommonDefineReplacement =
|
|
{
|
|
(new KeywordDescriptor() {referenceName = Rendering.BuiltIn.ShaderGraph.BuiltInFields.VelocityPrecomputed.define}, VFXSRPBinder.ShaderGraphBinder.kKeywordDescriptorNone)
|
|
};
|
|
|
|
static KeywordCollection ApplyKeywordModifier(KeywordCollection keywords, VFXSRPBinder.ShaderGraphBinder shaderGraphSRPInfo)
|
|
{
|
|
if (keywords != null)
|
|
{
|
|
var keywordsReplacement = k_CommonKeywordReplacement;
|
|
if (shaderGraphSRPInfo.keywordsReplacement != null)
|
|
keywordsReplacement = keywordsReplacement.Concat(shaderGraphSRPInfo.keywordsReplacement).ToArray();
|
|
|
|
var overridenKeywords = new KeywordCollection();
|
|
foreach (var keyword in keywords)
|
|
{
|
|
var currentKeyword = keyword;
|
|
|
|
var replacement = keywordsReplacement.FirstOrDefault(o => o.oldDesc.referenceName == keyword.descriptor.referenceName);
|
|
if (replacement.newDesc.referenceName == VFXSRPBinder.ShaderGraphBinder.kPragmaDescriptorNone.value)
|
|
continue; //Skip this irrelevant pragmas, kPragmaDescriptorNone shouldn't be null/empty
|
|
|
|
if (!string.IsNullOrEmpty(replacement.newDesc.referenceName))
|
|
currentKeyword = new KeywordCollection.Item(replacement.newDesc, currentKeyword.fieldConditions);
|
|
|
|
overridenKeywords.Add(currentKeyword.descriptor, currentKeyword.fieldConditions);
|
|
}
|
|
return overridenKeywords;
|
|
}
|
|
return keywords;
|
|
}
|
|
|
|
static DefineCollection ApplyDefineModifier(DefineCollection inputDefines, VFXSRPBinder.ShaderGraphBinder shaderGraphSRPInfo, VFXTaskCompiledData data)
|
|
{
|
|
var defineReplacement = k_CommonDefineReplacement;
|
|
//So far, no SRP custom define replacement
|
|
|
|
var overridenDefines = new DefineCollection();
|
|
bool empty = true;
|
|
if (inputDefines != null)
|
|
{
|
|
foreach (var define in inputDefines)
|
|
{
|
|
var currentDefine = define;
|
|
|
|
var replacement = defineReplacement.FirstOrDefault(o => o.oldDesc.referenceName == define.descriptor.referenceName);
|
|
if (replacement.newDesc.referenceName == VFXSRPBinder.ShaderGraphBinder.kPragmaDescriptorNone.value)
|
|
continue; //Skip this irrelevant pragmas, kPragmaDescriptorNone shouldn't be null/empty
|
|
|
|
if (!string.IsNullOrEmpty(replacement.newDesc.referenceName))
|
|
currentDefine = new DefineCollection.Item(replacement.newDesc, currentDefine.index, currentDefine.fieldConditions);
|
|
|
|
overridenDefines.Add(currentDefine.descriptor, currentDefine.index, currentDefine.fieldConditions);
|
|
empty = false;
|
|
}
|
|
}
|
|
|
|
if (data.SGInputs != null)
|
|
{
|
|
foreach (var keyword in data.SGInputs.keywordsToDefine)
|
|
{
|
|
if (!string.IsNullOrEmpty(keyword.Value))
|
|
{
|
|
//Even if the keyword behind the scene is an enum, we are short cutting the definition to boolean
|
|
//It will generate `#define _MY_ACTIVE_ENUM_OPTION 1`
|
|
overridenDefines.Add(new KeywordDescriptor() {referenceName = keyword.Value}, 1);
|
|
empty = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return !empty ? overridenDefines : null;
|
|
}
|
|
|
|
internal static SubShaderDescriptor PostProcessSubShader(SubShaderDescriptor subShaderDescriptor, VFXContext context, VFXTaskCompiledData data)
|
|
{
|
|
var srp = VFXLibrary.currentSRPBinder;
|
|
if (srp == null)
|
|
return subShaderDescriptor;
|
|
|
|
var shaderGraphSRPInfo = srp.GetShaderGraphDescriptor(context, data);
|
|
|
|
var attributesStruct = GenerateVFXAttributesStruct(context, VFXAttributeType.Current);
|
|
var sourceAttributesStruct = GenerateVFXAttributesStruct(context, VFXAttributeType.Source);
|
|
|
|
// Defer to VFX to generate various misc. code-gen that ShaderGraph currently can't handle.
|
|
// We use the AdditionalCommand descriptors for ShaderGraph generation to splice these in.
|
|
// ( i.e. VFX Graph Block Function declaration + calling, Property Mapping, etc. )
|
|
GenerateVFXAdditionalCommands(
|
|
context, srp, shaderGraphSRPInfo, data,
|
|
out var srpCommonInclude,
|
|
out var perBlockDefines,
|
|
out var perBlockIncludes,
|
|
out var loadAttributeDescriptor,
|
|
out var blockFunctionDescriptor,
|
|
out var blockCallFunctionDescriptor,
|
|
out var interpolantsGenerationDescriptor,
|
|
out var buildFragInputsGenerationRTDescriptor,
|
|
out var buildVFXFragInputs,
|
|
out var pixelPropertiesAssignDescriptor,
|
|
out var defineSpaceDescriptor,
|
|
out var parameterBufferDescriptor,
|
|
out var additionalDefinesDescriptor,
|
|
out var loadPositionAttributeDescriptor,
|
|
out var loadCropFactorAttributesDescriptor,
|
|
out var loadTexcoordAttributesDescriptor,
|
|
out var loadCurrentFrameIndexParameterDescriptor,
|
|
out var loadRayTracedScalingAttributesDescriptor,
|
|
out var vertexPropertiesGenerationDescriptor,
|
|
out var setInstancingIndicesDescriptor,
|
|
out var fillGraphValuesDescriptor,
|
|
out var loadContextDataDescriptor,
|
|
out var fragInputsDescriptor
|
|
);
|
|
|
|
// Omit META and MV or Shadow Pass if disabled on the context.
|
|
var filteredPasses = subShaderDescriptor.passes.AsEnumerable();
|
|
|
|
filteredPasses = filteredPasses.Where(o => o.descriptor.lightMode != "META");
|
|
var outputContext = (VFXAbstractParticleOutput)context;
|
|
if (!outputContext.hasMotionVector)
|
|
filteredPasses = filteredPasses.Where(o => o.descriptor.lightMode != "MotionVectors");
|
|
|
|
if (!outputContext.hasShadowCasting)
|
|
filteredPasses = filteredPasses.Where(o => o.descriptor.lightMode != "ShadowCaster");
|
|
|
|
// SPACEWARP DOES NOT SUPPORT VFX GRAPH FOR NOW, SO WE DISABLE IT HERE
|
|
filteredPasses = filteredPasses.Where(o => o.descriptor.lightMode != "XRMotionVectors");
|
|
|
|
var passes = filteredPasses.ToArray();
|
|
|
|
var addPragmaRequireCubeArray = data.uniformMapper.textures.Any(o => o.valueType == VFXValueType.TextureCubeArray);
|
|
|
|
PassCollection vfxPasses = new PassCollection();
|
|
var cachedVFXInterpolators = GetVFXInterpolators(context, data).ToList();
|
|
for (int i = 0; i < passes.Length; i++)
|
|
{
|
|
var passDescriptor = passes[i].descriptor;
|
|
|
|
passDescriptor.pragmas = ApplyPragmaModifier(passDescriptor.pragmas, shaderGraphSRPInfo, addPragmaRequireCubeArray);
|
|
passDescriptor.keywords = ApplyKeywordModifier(passDescriptor.keywords, shaderGraphSRPInfo);
|
|
passDescriptor.defines = ApplyDefineModifier(passDescriptor.defines, shaderGraphSRPInfo, data);
|
|
|
|
// Warning: We are replacing the struct provided in the regular pass. It is ok as for now the VFX editor don't support
|
|
// tessellation or raytracing
|
|
passDescriptor.structs = ApplyVaryingsStructModifier(passDescriptor.structs, shaderGraphSRPInfo, cachedVFXInterpolators);
|
|
passDescriptor.structs.Add(attributesStruct);
|
|
passDescriptor.structs.Add(sourceAttributesStruct);
|
|
|
|
// Add additional VFX dependencies
|
|
passDescriptor.fieldDependencies = passDescriptor.fieldDependencies == null ? new DependencyCollection() : new DependencyCollection { passDescriptor.fieldDependencies }; // Duplicate fieldDependencies to avoid side effects (static list modification)
|
|
passDescriptor.fieldDependencies.Add(shaderGraphSRPInfo.fieldDependencies);
|
|
|
|
passDescriptor.additionalCommands = new AdditionalCommandCollection
|
|
{
|
|
srpCommonInclude,
|
|
perBlockDefines,
|
|
perBlockIncludes,
|
|
loadAttributeDescriptor,
|
|
blockFunctionDescriptor,
|
|
blockCallFunctionDescriptor,
|
|
interpolantsGenerationDescriptor,
|
|
buildFragInputsGenerationRTDescriptor,
|
|
buildVFXFragInputs,
|
|
pixelPropertiesAssignDescriptor,
|
|
defineSpaceDescriptor,
|
|
parameterBufferDescriptor,
|
|
additionalDefinesDescriptor,
|
|
loadPositionAttributeDescriptor,
|
|
loadCropFactorAttributesDescriptor,
|
|
loadTexcoordAttributesDescriptor,
|
|
loadCurrentFrameIndexParameterDescriptor,
|
|
loadRayTracedScalingAttributesDescriptor,
|
|
vertexPropertiesGenerationDescriptor,
|
|
setInstancingIndicesDescriptor,
|
|
fillGraphValuesDescriptor,
|
|
loadContextDataDescriptor,
|
|
fragInputsDescriptor
|
|
};
|
|
|
|
vfxPasses.Add(passDescriptor, passes[i].fieldConditions);
|
|
}
|
|
|
|
subShaderDescriptor.passes = vfxPasses;
|
|
|
|
return subShaderDescriptor;
|
|
}
|
|
}
|
|
}
|