VFXSubTarget.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. using System;
  2. using System.Linq;
  3. using System.Collections.Generic;
  4. using UnityEditor.ShaderGraph;
  5. using UnityEditor.ShaderGraph.Internal;
  6. using UnityEngine;
  7. namespace UnityEditor.VFX
  8. {
  9. static class VFXSubTarget
  10. {
  11. // See: VFXShaderWriter.TypeToUniformCode
  12. // TODO: Collapse these two maps into one
  13. public static readonly Dictionary<Type, Type> kVFXShaderPropertyMap = new Dictionary<Type, Type>
  14. {
  15. { typeof(float), typeof(Vector1ShaderProperty) },
  16. { typeof(Vector2), typeof(Vector2ShaderProperty) },
  17. { typeof(Vector3), typeof(Vector3ShaderProperty) },
  18. { typeof(Vector4), typeof(Vector4ShaderProperty) },
  19. { typeof(int), typeof(Vector1ShaderProperty) },
  20. { typeof(uint), typeof(Vector1ShaderProperty) },
  21. { typeof(Matrix4x4), typeof(Matrix4ShaderProperty) },
  22. { typeof(bool), typeof(BooleanShaderProperty) },
  23. };
  24. public static readonly Dictionary<Type, ShaderValueType> kVFXShaderValueTypeMap = new Dictionary<Type, ShaderValueType>
  25. {
  26. { typeof(float), ShaderValueType.Float },
  27. { typeof(Vector2), ShaderValueType.Float2 },
  28. { typeof(Vector3), ShaderValueType.Float3 },
  29. { typeof(Vector4), ShaderValueType.Float4 },
  30. { typeof(int), ShaderValueType.Integer },
  31. { typeof(uint), ShaderValueType.Uint },
  32. { typeof(Matrix4x4), ShaderValueType.Matrix4 },
  33. { typeof(bool), ShaderValueType.Float }, // NOTE: Map boolean to float for VFX interpolator due to how ShaderGraph handles semantics for boolean interpolator.
  34. };
  35. public static FieldDescriptor VFXAttributeToFieldDescriptor(VFXAttribute attribute)
  36. {
  37. var type = VFXExpression.TypeToType(attribute.type);
  38. if (!kVFXShaderValueTypeMap.TryGetValue(type, out var shaderValueType))
  39. return null;
  40. return new FieldDescriptor("VFXAttributes", attribute.name, "", shaderValueType);
  41. }
  42. static class VFXFields
  43. {
  44. public const string kTag = "OutputType";
  45. public static FieldDescriptor ParticleMesh = new FieldDescriptor(kTag, "Mesh", "VFX_PARTICLE_MESH 1");
  46. public static FieldDescriptor ParticlePlanarPrimitive = new FieldDescriptor(kTag, "PlanarPrimitive", "VFX_PARTICLE_PLANAR_PRIMITIVE 1");
  47. }
  48. internal static void GetFields(ref TargetFieldContext fieldsContext, VFXContext context)
  49. {
  50. fieldsContext.AddField(Fields.GraphVFX);
  51. // Select the primitive implementation.
  52. switch (context.taskType)
  53. {
  54. case VFXTaskType.ParticleMeshOutput:
  55. fieldsContext.AddField(VFXFields.ParticleMesh);
  56. break;
  57. case VFXTaskType.ParticleTriangleOutput:
  58. case VFXTaskType.ParticleOctagonOutput:
  59. case VFXTaskType.ParticleQuadOutput:
  60. fieldsContext.AddField(VFXFields.ParticlePlanarPrimitive);
  61. break;
  62. }
  63. }
  64. enum VFXAttributeType
  65. {
  66. Current,
  67. Source
  68. }
  69. static readonly string[] kVFXAttributeStructNames =
  70. {
  71. "InternalAttributesElement",
  72. "InternalSourceAttributesElement"
  73. };
  74. static StructDescriptor GenerateVFXAttributesStruct(VFXContext context, VFXAttributeType attributeType)
  75. {
  76. IEnumerable<VFXAttributeInfo> attributeInfos;
  77. if (attributeType == VFXAttributeType.Current)
  78. {
  79. attributeInfos = context.GetData().GetAttributes().Where(a =>
  80. (context.GetData().IsCurrentAttributeUsed(a.attrib, context)) ||
  81. (context.contextType == VFXContextType.Init && context.GetData().IsAttributeStored(a.attrib))); // In init, needs to declare all stored attributes for intialization
  82. }
  83. else
  84. {
  85. attributeInfos = context.GetData().GetAttributes().Where(a => (context.GetData().IsSourceAttributeUsed(a.attrib, context)));
  86. }
  87. var attributes = attributeInfos.Select(a => a.attrib);
  88. var attributeFieldDescriptors = new List<FieldDescriptor>();
  89. foreach (var attribute in attributes)
  90. {
  91. var afd = VFXSubTarget.VFXAttributeToFieldDescriptor(attribute);
  92. attributeFieldDescriptors.Add(afd);
  93. }
  94. return new StructDescriptor
  95. {
  96. name = kVFXAttributeStructNames[(int)attributeType],
  97. fields = attributeFieldDescriptors.ToArray()
  98. };
  99. }
  100. static void GenerateVFXAdditionalCommands(VFXContext context, VFXSRPBinder srp, VFXSRPBinder.ShaderGraphBinder shaderGraphBinder, VFXContextCompiledData contextData,
  101. out AdditionalCommandDescriptor srpCommonInclude,
  102. out AdditionalCommandDescriptor loadAttributeDescriptor,
  103. out AdditionalCommandDescriptor blockFunctionDescriptor,
  104. out AdditionalCommandDescriptor blockCallFunctionDescriptor,
  105. out AdditionalCommandDescriptor interpolantsGenerationDescriptor,
  106. out AdditionalCommandDescriptor buildVFXFragInputsDescriptor,
  107. out AdditionalCommandDescriptor pixelPropertiesAssignDescriptor,
  108. out AdditionalCommandDescriptor defineSpaceDescriptor,
  109. out AdditionalCommandDescriptor parameterBufferDescriptor,
  110. out AdditionalCommandDescriptor additionalDefinesDescriptor,
  111. out AdditionalCommandDescriptor loadPositionAttributeDescriptor,
  112. out AdditionalCommandDescriptor loadCropFactorAttributesDescriptor,
  113. out AdditionalCommandDescriptor loadTexcoordAttributesDescriptor,
  114. out AdditionalCommandDescriptor vertexPropertiesGenerationDescriptor,
  115. out AdditionalCommandDescriptor vertexPropertiesAssignDescriptor)
  116. {
  117. // TODO: Clean all of this up. Currently just an adapter between VFX Code Gen + SG Code Gen and *everything* has been stuffed here.
  118. // SRP Common Include
  119. srpCommonInclude = new AdditionalCommandDescriptor("VFXSRPCommonInclude", string.Format("#include \"{0}\"", srp.runtimePath + "/VFXCommon.hlsl"));
  120. // Load Attributes
  121. loadAttributeDescriptor = new AdditionalCommandDescriptor("VFXLoadAttribute", VFXCodeGenerator.GenerateLoadAttribute(".", context).ToString());
  122. // Graph Blocks
  123. VFXCodeGenerator.BuildContextBlocks(context, contextData, out var blockFunction, out var blockCallFunction);
  124. blockFunctionDescriptor = new AdditionalCommandDescriptor("VFXGeneratedBlockFunction", blockFunction);
  125. blockCallFunctionDescriptor = new AdditionalCommandDescriptor("VFXProcessBlocks", blockCallFunction);
  126. // Vertex Input
  127. VFXCodeGenerator.BuildVertexProperties(context, contextData, out var vertexPropertiesGeneration);
  128. vertexPropertiesGenerationDescriptor = new AdditionalCommandDescriptor("VFXVertexPropertiesGeneration", vertexPropertiesGeneration);
  129. VFXCodeGenerator.BuildVertexPropertiesAssign(context, contextData, out var vertexPropertiesAssign);
  130. vertexPropertiesAssignDescriptor = new AdditionalCommandDescriptor("VFXVertexPropertiesAssign", vertexPropertiesAssign);
  131. // Interpolator
  132. VFXCodeGenerator.BuildInterpolatorBlocks(context, contextData, out var interpolatorsGeneration);
  133. interpolantsGenerationDescriptor = new AdditionalCommandDescriptor("VFXInterpolantsGeneration", interpolatorsGeneration);
  134. // Frag Inputs - Only VFX will know if frag inputs come from interpolator or the CBuffer.
  135. VFXCodeGenerator.BuildFragInputsGeneration(context, contextData, shaderGraphBinder.useFragInputs, out var buildFragInputsGeneration);
  136. buildVFXFragInputsDescriptor = new AdditionalCommandDescriptor("VFXSetFragInputs", buildFragInputsGeneration);
  137. VFXCodeGenerator.BuildPixelPropertiesAssign(context, contextData, shaderGraphBinder.useFragInputs, out var pixelPropertiesAssign);
  138. pixelPropertiesAssignDescriptor = new AdditionalCommandDescriptor("VFXPixelPropertiesAssign", pixelPropertiesAssign);
  139. // Define coordinate space
  140. var defineSpaceDescriptorContent = string.Empty;
  141. if (context.GetData() is ISpaceable)
  142. {
  143. var spaceable = context.GetData() as ISpaceable;
  144. defineSpaceDescriptorContent =
  145. $"#define {(spaceable.space == VFXCoordinateSpace.World ? "VFX_WORLD_SPACE" : "VFX_LOCAL_SPACE")} 1";
  146. }
  147. defineSpaceDescriptor = new AdditionalCommandDescriptor("VFXDefineSpace", defineSpaceDescriptorContent);
  148. //Texture used as input of the shaderGraph will be declared by the shaderGraph generation
  149. //However, if we are sampling a texture (or a mesh), we have to declare them before the VFX code generation.
  150. //Thus, remove texture used in SG from VFX declaration and let the remainder.
  151. var shaderGraphOutput = context as VFXShaderGraphParticleOutput;
  152. if (shaderGraphOutput == null)
  153. throw new InvalidOperationException("Unexpected null VFXShaderGraphParticleOutput");
  154. var shaderGraphObject = shaderGraphOutput.GetOrRefreshShaderGraphObject();
  155. if (shaderGraphObject == null)
  156. throw new InvalidOperationException("Unexpected null GetOrRefreshShaderGraphObject");
  157. var texureUsedInternallyInSG = shaderGraphObject.textureInfos.Select(o =>
  158. {
  159. return o.name;
  160. });
  161. var textureExposedFromSG = context.inputSlots.Where(o =>
  162. {
  163. return VFXExpression.IsTexture(o.property.type);
  164. }).Select(o => o.property.name);
  165. var filteredTextureInSG = texureUsedInternallyInSG.Concat(textureExposedFromSG).ToArray();
  166. // Parameter Cbuffer
  167. VFXCodeGenerator.BuildParameterBuffer(contextData, filteredTextureInSG, out var parameterBuffer);
  168. parameterBufferDescriptor = new AdditionalCommandDescriptor("VFXParameterBuffer", parameterBuffer);
  169. // Defines & Headers - Not all are necessary, however some important ones are mixed in like indirect draw, strips, flipbook, particle strip info...
  170. ShaderStringBuilder additionalDefines = new ShaderStringBuilder();
  171. // TODO: Need to add defines for current/source usage (i.e. scale).
  172. var allCurrentAttributes = context.GetData().GetAttributes().Where(a =>
  173. (context.GetData().IsCurrentAttributeUsed(a.attrib, context)) ||
  174. (context.contextType == VFXContextType.Init && context.GetData().IsAttributeStored(a.attrib))); // In init, needs to declare all stored attributes for intialization
  175. var allSourceAttributes = context.GetData().GetAttributes().Where(a => (context.GetData().IsSourceAttributeUsed(a.attrib, context)));
  176. foreach (var attribute in allCurrentAttributes)
  177. additionalDefines.AppendLine("#define VFX_USE_{0}_{1} 1", attribute.attrib.name.ToUpper(System.Globalization.CultureInfo.InvariantCulture), "CURRENT");
  178. foreach (var attribute in allSourceAttributes)
  179. additionalDefines.AppendLine("#define VFX_USE_{0}_{1} 1", attribute.attrib.name.ToUpper(System.Globalization.CultureInfo.InvariantCulture), "SOURCE");
  180. foreach (var header in context.additionalDataHeaders)
  181. additionalDefines.AppendLine(header);
  182. foreach (var define in context.additionalDefines)
  183. additionalDefines.AppendLine(define.Contains(' ') ? $"#define {define}" : $"#define {define} 1");
  184. additionalDefinesDescriptor = new AdditionalCommandDescriptor("VFXDefines", additionalDefines.ToString());
  185. // Load Position Attribute
  186. loadPositionAttributeDescriptor = new AdditionalCommandDescriptor("VFXLoadPositionAttribute", VFXCodeGenerator.GenerateLoadAttribute("position", context).ToString().ToString());
  187. // Load Crop Factor Attribute
  188. var mainParameters = contextData.gpuMapper.CollectExpression(-1).ToArray();
  189. var expressionToName = context.GetData().GetAttributes().ToDictionary(o => new VFXAttributeExpression(o.attrib) as VFXExpression, o => (new VFXAttributeExpression(o.attrib)).GetCodeString(null));
  190. expressionToName = expressionToName.Union(contextData.uniformMapper.expressionToCode).ToDictionary(s => s.Key, s => s.Value);
  191. loadCropFactorAttributesDescriptor = new AdditionalCommandDescriptor("VFXLoadCropFactorParameter", VFXCodeGenerator.GenerateLoadParameter("cropFactor", mainParameters, expressionToName).ToString().ToString());
  192. loadTexcoordAttributesDescriptor = new AdditionalCommandDescriptor("VFXLoadTexcoordParameter", VFXCodeGenerator.GenerateLoadParameter("texCoord", mainParameters, expressionToName).ToString().ToString());
  193. }
  194. static AdditionalCommandDescriptor GenerateFragInputs(VFXContext context, VFXContextCompiledData contextData)
  195. {
  196. var builder = new ShaderStringBuilder();
  197. // VFX Material Properties
  198. var expressionToName = context.GetData().GetAttributes().ToDictionary(o => new VFXAttributeExpression(o.attrib) as VFXExpression, o => (new VFXAttributeExpression(o.attrib)).GetCodeString(null));
  199. expressionToName = expressionToName.Union(contextData.uniformMapper.expressionToCode).ToDictionary(s => s.Key, s => s.Value);
  200. var mainParameters = contextData.gpuMapper.CollectExpression(-1).ToArray();
  201. foreach (string fragmentParameter in context.fragmentParameters)
  202. {
  203. var filteredNamedExpression = mainParameters.FirstOrDefault(o => fragmentParameter == o.name);
  204. if (filteredNamedExpression.exp != null)
  205. {
  206. var type = VFXExpression.TypeToType(filteredNamedExpression.exp.valueType);
  207. if (!VFXSubTarget.kVFXShaderValueTypeMap.TryGetValue(type, out var shaderValueType))
  208. continue;
  209. builder.AppendLine($"{shaderValueType.ToShaderString("float")} {filteredNamedExpression.name};");
  210. }
  211. }
  212. return new AdditionalCommandDescriptor("FragInputsVFX", builder.ToString());
  213. }
  214. static PragmaCollection ApplyPragmaReplacement(PragmaCollection pragmas, VFXSRPBinder.ShaderGraphBinder shaderGraphSRPInfo)
  215. {
  216. if (shaderGraphSRPInfo.pragmasReplacement != null)
  217. {
  218. var overridenPragmas = new PragmaCollection();
  219. foreach (var pragma in pragmas)
  220. {
  221. var currentPragma = pragma;
  222. var replacement = shaderGraphSRPInfo.pragmasReplacement.FirstOrDefault(o => o.oldDesc.value == pragma.descriptor.value);
  223. if (!string.IsNullOrEmpty(replacement.newDesc.value))
  224. currentPragma = new PragmaCollection.Item(replacement.newDesc, pragma.fieldConditions);
  225. overridenPragmas.Add(currentPragma.descriptor, currentPragma.fieldConditions);
  226. }
  227. return overridenPragmas;
  228. }
  229. return pragmas;
  230. }
  231. internal static SubShaderDescriptor PostProcessSubShader(SubShaderDescriptor subShaderDescriptor, VFXContext context, VFXContextCompiledData data)
  232. {
  233. var srp = VFXLibrary.currentSRPBinder;
  234. if (srp == null)
  235. return subShaderDescriptor;
  236. var shaderGraphSRPInfo = srp.GetShaderGraphDescriptor(context, data);
  237. var attributesStruct = GenerateVFXAttributesStruct(context, VFXAttributeType.Current);
  238. var sourceAttributesStruct = GenerateVFXAttributesStruct(context, VFXAttributeType.Source);
  239. // Defer to VFX to generate various misc. code-gen that ShaderGraph currently can't handle.
  240. // We use the AdditionalCommand descriptors for ShaderGraph generation to splice these in.
  241. // ( i.e. VFX Graph Block Function declaration + calling, Property Mapping, etc. )
  242. GenerateVFXAdditionalCommands(
  243. context, srp, shaderGraphSRPInfo, data,
  244. out var srpCommonInclude,
  245. out var loadAttributeDescriptor,
  246. out var blockFunctionDescriptor,
  247. out var blockCallFunctionDescriptor,
  248. out var interpolantsGenerationDescriptor,
  249. out var buildVFXFragInputs,
  250. out var pixelPropertiesAssignDescriptor,
  251. out var defineSpaceDescriptor,
  252. out var parameterBufferDescriptor,
  253. out var additionalDefinesDescriptor,
  254. out var loadPositionAttributeDescriptor,
  255. out var loadCropFactorAttributesDescriptor,
  256. out var loadTexcoordAttributesDescriptor,
  257. out var vertexPropertiesGenerationDescriptor,
  258. out var vertexPropertiesAssignDescriptor
  259. );
  260. // Omit MV or Shadow Pass if disabled on the context.
  261. var filteredPasses = subShaderDescriptor.passes.AsEnumerable();
  262. var outputContext = (VFXAbstractParticleOutput)context;
  263. if (!outputContext.hasMotionVector)
  264. filteredPasses = filteredPasses.Where(o => o.descriptor.lightMode != "MotionVectors");
  265. if (!outputContext.hasShadowCasting)
  266. filteredPasses = filteredPasses.Where(o => o.descriptor.lightMode != "ShadowCaster");
  267. var passes = filteredPasses.ToArray();
  268. PassCollection vfxPasses = new PassCollection();
  269. for (int i = 0; i < passes.Length; i++)
  270. {
  271. var passDescriptor = passes[i].descriptor;
  272. passDescriptor.pragmas = ApplyPragmaReplacement(passDescriptor.pragmas, shaderGraphSRPInfo);
  273. // Warning: We are replacing the struct provided in the regular pass. It is ok as for now the VFX editor don't support
  274. // tessellation or raytracing
  275. passDescriptor.structs = new StructCollection();
  276. passDescriptor.structs.Add(shaderGraphSRPInfo.structs);
  277. passDescriptor.structs.Add(attributesStruct);
  278. passDescriptor.structs.Add(sourceAttributesStruct);
  279. // Add additional VFX dependencies
  280. passDescriptor.fieldDependencies = passDescriptor.fieldDependencies == null ? new DependencyCollection() : new DependencyCollection { passDescriptor.fieldDependencies }; // Duplicate fieldDependencies to avoid side effects (static list modification)
  281. passDescriptor.fieldDependencies.Add(shaderGraphSRPInfo.fieldDependencies);
  282. passDescriptor.additionalCommands = new AdditionalCommandCollection
  283. {
  284. srpCommonInclude,
  285. loadAttributeDescriptor,
  286. blockFunctionDescriptor,
  287. blockCallFunctionDescriptor,
  288. interpolantsGenerationDescriptor,
  289. buildVFXFragInputs,
  290. pixelPropertiesAssignDescriptor,
  291. defineSpaceDescriptor,
  292. parameterBufferDescriptor,
  293. additionalDefinesDescriptor,
  294. loadPositionAttributeDescriptor,
  295. loadCropFactorAttributesDescriptor,
  296. loadTexcoordAttributesDescriptor,
  297. vertexPropertiesGenerationDescriptor,
  298. vertexPropertiesAssignDescriptor,
  299. GenerateFragInputs(context, data)
  300. };
  301. vfxPasses.Add(passDescriptor, passes[i].fieldConditions);
  302. }
  303. subShaderDescriptor.passes = vfxPasses;
  304. return subShaderDescriptor;
  305. }
  306. }
  307. }