Declarations.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  2. using Ryujinx.Graphics.Shader.StructuredIr;
  3. using Ryujinx.Graphics.Shader.Translation;
  4. using Spv.Generator;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Diagnostics;
  8. using static Spv.Specification;
  9. using SpvInstruction = Spv.Generator.Instruction;
  10. namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
  11. {
  12. static class Declarations
  13. {
  14. public static void DeclareParameters(CodeGenContext context, StructuredFunction function)
  15. {
  16. DeclareParameters(context, function.InArguments, 0);
  17. DeclareParameters(context, function.OutArguments, function.InArguments.Length);
  18. }
  19. private static void DeclareParameters(CodeGenContext context, IEnumerable<AggregateType> argTypes, int argIndex)
  20. {
  21. foreach (AggregateType argType in argTypes)
  22. {
  23. SpvInstruction argPointerType = context.TypePointer(StorageClass.Function, context.GetType(argType));
  24. SpvInstruction spvArg = context.FunctionParameter(argPointerType);
  25. context.DeclareArgument(argIndex++, spvArg);
  26. }
  27. }
  28. public static void DeclareLocals(CodeGenContext context, StructuredFunction function)
  29. {
  30. foreach (AstOperand local in function.Locals)
  31. {
  32. SpvInstruction localPointerType = context.TypePointer(StorageClass.Function, context.GetType(local.VarType));
  33. SpvInstruction spvLocal = context.Variable(localPointerType, StorageClass.Function);
  34. context.AddLocalVariable(spvLocal);
  35. context.DeclareLocal(local, spvLocal);
  36. }
  37. }
  38. public static void DeclareAll(CodeGenContext context, StructuredProgramInfo info)
  39. {
  40. DeclareConstantBuffers(context, context.Properties.ConstantBuffers.Values);
  41. DeclareStorageBuffers(context, context.Properties.StorageBuffers.Values);
  42. DeclareMemories(context, context.Properties.LocalMemories, context.LocalMemories, StorageClass.Private);
  43. DeclareMemories(context, context.Properties.SharedMemories, context.SharedMemories, StorageClass.Workgroup);
  44. DeclareSamplers(context, context.Properties.Textures.Values);
  45. DeclareImages(context, context.Properties.Images.Values);
  46. DeclareInputsAndOutputs(context, info);
  47. }
  48. private static void DeclareMemories(
  49. CodeGenContext context,
  50. IReadOnlyDictionary<int, MemoryDefinition> memories,
  51. Dictionary<int, SpvInstruction> dict,
  52. StorageClass storage)
  53. {
  54. foreach ((int id, MemoryDefinition memory) in memories)
  55. {
  56. SpvInstruction pointerType = context.TypePointer(storage, context.GetType(memory.Type, memory.ArrayLength));
  57. SpvInstruction variable = context.Variable(pointerType, storage);
  58. context.AddGlobalVariable(variable);
  59. dict.Add(id, variable);
  60. }
  61. }
  62. private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
  63. {
  64. DeclareBuffers(context, buffers, isBuffer: false);
  65. }
  66. private static void DeclareStorageBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
  67. {
  68. DeclareBuffers(context, buffers, isBuffer: true);
  69. }
  70. private static void DeclareBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers, bool isBuffer)
  71. {
  72. HashSet<SpvInstruction> decoratedTypes = new();
  73. foreach (BufferDefinition buffer in buffers)
  74. {
  75. int setIndex = context.TargetApi == TargetApi.Vulkan ? buffer.Set : 0;
  76. int alignment = buffer.Layout == BufferLayout.Std140 ? 16 : 4;
  77. int alignmentMask = alignment - 1;
  78. int offset = 0;
  79. SpvInstruction[] structFieldTypes = new SpvInstruction[buffer.Type.Fields.Length];
  80. int[] structFieldOffsets = new int[buffer.Type.Fields.Length];
  81. for (int fieldIndex = 0; fieldIndex < buffer.Type.Fields.Length; fieldIndex++)
  82. {
  83. StructureField field = buffer.Type.Fields[fieldIndex];
  84. int fieldSize = (field.Type.GetSizeInBytes() + alignmentMask) & ~alignmentMask;
  85. structFieldTypes[fieldIndex] = context.GetType(field.Type, field.ArrayLength);
  86. structFieldOffsets[fieldIndex] = offset;
  87. if (field.Type.HasFlag(AggregateType.Array))
  88. {
  89. // We can't decorate the type more than once.
  90. if (decoratedTypes.Add(structFieldTypes[fieldIndex]))
  91. {
  92. context.Decorate(structFieldTypes[fieldIndex], Decoration.ArrayStride, (LiteralInteger)fieldSize);
  93. }
  94. // Zero lengths are assumed to be a "runtime array" (which does not have a explicit length
  95. // specified on the shader, and instead assumes the bound buffer length).
  96. // It is only valid as the last struct element.
  97. Debug.Assert(field.ArrayLength > 0 || fieldIndex == buffer.Type.Fields.Length - 1);
  98. offset += fieldSize * field.ArrayLength;
  99. }
  100. else
  101. {
  102. offset += fieldSize;
  103. }
  104. }
  105. SpvInstruction structType = context.TypeStruct(false, structFieldTypes);
  106. if (decoratedTypes.Add(structType))
  107. {
  108. context.Decorate(structType, isBuffer ? Decoration.BufferBlock : Decoration.Block);
  109. for (int fieldIndex = 0; fieldIndex < structFieldOffsets.Length; fieldIndex++)
  110. {
  111. context.MemberDecorate(structType, fieldIndex, Decoration.Offset, (LiteralInteger)structFieldOffsets[fieldIndex]);
  112. }
  113. }
  114. SpvInstruction pointerType = context.TypePointer(StorageClass.Uniform, structType);
  115. SpvInstruction variable = context.Variable(pointerType, StorageClass.Uniform);
  116. context.Name(variable, buffer.Name);
  117. context.Decorate(variable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
  118. context.Decorate(variable, Decoration.Binding, (LiteralInteger)buffer.Binding);
  119. context.AddGlobalVariable(variable);
  120. if (isBuffer)
  121. {
  122. context.StorageBuffers.Add(buffer.Binding, variable);
  123. }
  124. else
  125. {
  126. context.ConstantBuffers.Add(buffer.Binding, variable);
  127. }
  128. }
  129. }
  130. private static void DeclareSamplers(CodeGenContext context, IEnumerable<TextureDefinition> samplers)
  131. {
  132. foreach (TextureDefinition sampler in samplers)
  133. {
  134. int setIndex = context.TargetApi == TargetApi.Vulkan ? sampler.Set : 0;
  135. SpvInstruction imageType;
  136. SpvInstruction sampledImageType;
  137. if (sampler.Type != SamplerType.None)
  138. {
  139. Dim dim = (sampler.Type & SamplerType.Mask) switch
  140. {
  141. SamplerType.Texture1D => Dim.Dim1D,
  142. SamplerType.Texture2D => Dim.Dim2D,
  143. SamplerType.Texture3D => Dim.Dim3D,
  144. SamplerType.TextureCube => Dim.Cube,
  145. SamplerType.TextureBuffer => Dim.Buffer,
  146. _ => throw new InvalidOperationException($"Invalid sampler type \"{sampler.Type & SamplerType.Mask}\"."),
  147. };
  148. imageType = context.TypeImage(
  149. context.TypeFP32(),
  150. dim,
  151. sampler.Type.HasFlag(SamplerType.Shadow),
  152. sampler.Type.HasFlag(SamplerType.Array),
  153. sampler.Type.HasFlag(SamplerType.Multisample),
  154. 1,
  155. ImageFormat.Unknown);
  156. sampledImageType = context.TypeSampledImage(imageType);
  157. }
  158. else
  159. {
  160. imageType = sampledImageType = context.TypeSampler();
  161. }
  162. SpvInstruction sampledOrSeparateImageType = sampler.Separate ? imageType : sampledImageType;
  163. SpvInstruction sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledOrSeparateImageType);
  164. SpvInstruction sampledImageArrayPointerType = sampledImagePointerType;
  165. if (sampler.ArrayLength == 0)
  166. {
  167. SpvInstruction sampledImageArrayType = context.TypeRuntimeArray(sampledOrSeparateImageType);
  168. sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType);
  169. }
  170. else if (sampler.ArrayLength != 1)
  171. {
  172. SpvInstruction sampledImageArrayType = context.TypeArray(sampledOrSeparateImageType, context.Constant(context.TypeU32(), sampler.ArrayLength));
  173. sampledImageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageArrayType);
  174. }
  175. SpvInstruction sampledImageVariable = context.Variable(sampledImageArrayPointerType, StorageClass.UniformConstant);
  176. context.Samplers.Add(new(sampler.Set, sampler.Binding), new SamplerDeclaration(
  177. imageType,
  178. sampledImageType,
  179. sampledImagePointerType,
  180. sampledImageVariable,
  181. sampler.ArrayLength != 1));
  182. context.SamplersTypes.Add(new(sampler.Set, sampler.Binding), sampler.Type);
  183. context.Name(sampledImageVariable, sampler.Name);
  184. context.Decorate(sampledImageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
  185. context.Decorate(sampledImageVariable, Decoration.Binding, (LiteralInteger)sampler.Binding);
  186. context.AddGlobalVariable(sampledImageVariable);
  187. }
  188. }
  189. private static void DeclareImages(CodeGenContext context, IEnumerable<TextureDefinition> images)
  190. {
  191. foreach (TextureDefinition image in images)
  192. {
  193. int setIndex = context.TargetApi == TargetApi.Vulkan ? image.Set : 0;
  194. Dim dim = GetDim(image.Type);
  195. SpvInstruction imageType = context.TypeImage(
  196. context.GetType(image.Format.GetComponentType()),
  197. dim,
  198. image.Type.HasFlag(SamplerType.Shadow),
  199. image.Type.HasFlag(SamplerType.Array),
  200. image.Type.HasFlag(SamplerType.Multisample),
  201. AccessQualifier.ReadWrite,
  202. GetImageFormat(image.Format));
  203. SpvInstruction imagePointerType = context.TypePointer(StorageClass.UniformConstant, imageType);
  204. SpvInstruction imageArrayPointerType = imagePointerType;
  205. if (image.ArrayLength == 0)
  206. {
  207. SpvInstruction imageArrayType = context.TypeRuntimeArray(imageType);
  208. imageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, imageArrayType);
  209. }
  210. else if (image.ArrayLength != 1)
  211. {
  212. SpvInstruction imageArrayType = context.TypeArray(imageType, context.Constant(context.TypeU32(), image.ArrayLength));
  213. imageArrayPointerType = context.TypePointer(StorageClass.UniformConstant, imageArrayType);
  214. }
  215. SpvInstruction imageVariable = context.Variable(imageArrayPointerType, StorageClass.UniformConstant);
  216. context.Images.Add(new(image.Set, image.Binding), new ImageDeclaration(imageType, imagePointerType, imageVariable, image.ArrayLength != 1));
  217. context.Name(imageVariable, image.Name);
  218. context.Decorate(imageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
  219. context.Decorate(imageVariable, Decoration.Binding, (LiteralInteger)image.Binding);
  220. if (image.Flags.HasFlag(TextureUsageFlags.ImageCoherent))
  221. {
  222. context.Decorate(imageVariable, Decoration.Coherent);
  223. }
  224. context.AddGlobalVariable(imageVariable);
  225. }
  226. }
  227. private static Dim GetDim(SamplerType type)
  228. {
  229. return (type & SamplerType.Mask) switch
  230. {
  231. SamplerType.Texture1D => Dim.Dim1D,
  232. SamplerType.Texture2D => Dim.Dim2D,
  233. SamplerType.Texture3D => Dim.Dim3D,
  234. SamplerType.TextureCube => Dim.Cube,
  235. SamplerType.TextureBuffer => Dim.Buffer,
  236. _ => throw new ArgumentException($"Invalid sampler type \"{type & SamplerType.Mask}\"."),
  237. };
  238. }
  239. private static ImageFormat GetImageFormat(TextureFormat format)
  240. {
  241. return format switch
  242. {
  243. TextureFormat.Unknown => ImageFormat.Unknown,
  244. TextureFormat.R8Unorm => ImageFormat.R8,
  245. TextureFormat.R8Snorm => ImageFormat.R8Snorm,
  246. TextureFormat.R8Uint => ImageFormat.R8ui,
  247. TextureFormat.R8Sint => ImageFormat.R8i,
  248. TextureFormat.R16Float => ImageFormat.R16f,
  249. TextureFormat.R16Unorm => ImageFormat.R16,
  250. TextureFormat.R16Snorm => ImageFormat.R16Snorm,
  251. TextureFormat.R16Uint => ImageFormat.R16ui,
  252. TextureFormat.R16Sint => ImageFormat.R16i,
  253. TextureFormat.R32Float => ImageFormat.R32f,
  254. TextureFormat.R32Uint => ImageFormat.R32ui,
  255. TextureFormat.R32Sint => ImageFormat.R32i,
  256. TextureFormat.R8G8Unorm => ImageFormat.Rg8,
  257. TextureFormat.R8G8Snorm => ImageFormat.Rg8Snorm,
  258. TextureFormat.R8G8Uint => ImageFormat.Rg8ui,
  259. TextureFormat.R8G8Sint => ImageFormat.Rg8i,
  260. TextureFormat.R16G16Float => ImageFormat.Rg16f,
  261. TextureFormat.R16G16Unorm => ImageFormat.Rg16,
  262. TextureFormat.R16G16Snorm => ImageFormat.Rg16Snorm,
  263. TextureFormat.R16G16Uint => ImageFormat.Rg16ui,
  264. TextureFormat.R16G16Sint => ImageFormat.Rg16i,
  265. TextureFormat.R32G32Float => ImageFormat.Rg32f,
  266. TextureFormat.R32G32Uint => ImageFormat.Rg32ui,
  267. TextureFormat.R32G32Sint => ImageFormat.Rg32i,
  268. TextureFormat.R8G8B8A8Unorm => ImageFormat.Rgba8,
  269. TextureFormat.R8G8B8A8Snorm => ImageFormat.Rgba8Snorm,
  270. TextureFormat.R8G8B8A8Uint => ImageFormat.Rgba8ui,
  271. TextureFormat.R8G8B8A8Sint => ImageFormat.Rgba8i,
  272. TextureFormat.R16G16B16A16Float => ImageFormat.Rgba16f,
  273. TextureFormat.R16G16B16A16Unorm => ImageFormat.Rgba16,
  274. TextureFormat.R16G16B16A16Snorm => ImageFormat.Rgba16Snorm,
  275. TextureFormat.R16G16B16A16Uint => ImageFormat.Rgba16ui,
  276. TextureFormat.R16G16B16A16Sint => ImageFormat.Rgba16i,
  277. TextureFormat.R32G32B32A32Float => ImageFormat.Rgba32f,
  278. TextureFormat.R32G32B32A32Uint => ImageFormat.Rgba32ui,
  279. TextureFormat.R32G32B32A32Sint => ImageFormat.Rgba32i,
  280. TextureFormat.R10G10B10A2Unorm => ImageFormat.Rgb10A2,
  281. TextureFormat.R10G10B10A2Uint => ImageFormat.Rgb10a2ui,
  282. TextureFormat.R11G11B10Float => ImageFormat.R11fG11fB10f,
  283. _ => throw new ArgumentException($"Invalid texture format \"{format}\"."),
  284. };
  285. }
  286. private static void DeclareInputsAndOutputs(CodeGenContext context, StructuredProgramInfo info)
  287. {
  288. int firstLocation = int.MaxValue;
  289. if (context.Definitions.Stage == ShaderStage.Fragment && context.Definitions.DualSourceBlend)
  290. {
  291. foreach (IoDefinition ioDefinition in info.IoDefinitions)
  292. {
  293. if (ioDefinition.IoVariable == IoVariable.FragmentOutputColor && ioDefinition.Location < firstLocation)
  294. {
  295. firstLocation = ioDefinition.Location;
  296. }
  297. }
  298. }
  299. foreach (IoDefinition ioDefinition in info.IoDefinitions)
  300. {
  301. PixelImap iq = PixelImap.Unused;
  302. if (context.Definitions.Stage == ShaderStage.Fragment)
  303. {
  304. IoVariable ioVariable = ioDefinition.IoVariable;
  305. if (ioVariable == IoVariable.UserDefined)
  306. {
  307. iq = context.Definitions.ImapTypes[ioDefinition.Location].GetFirstUsedType();
  308. }
  309. else
  310. {
  311. (_, AggregateType varType) = IoMap.GetSpirvBuiltIn(ioVariable);
  312. AggregateType elemType = varType & AggregateType.ElementTypeMask;
  313. if (elemType is AggregateType.S32 or AggregateType.U32)
  314. {
  315. iq = PixelImap.Constant;
  316. }
  317. }
  318. }
  319. else if (IoMap.IsPerVertexBuiltIn(ioDefinition.IoVariable))
  320. {
  321. continue;
  322. }
  323. bool isOutput = ioDefinition.StorageKind.IsOutput();
  324. bool isPerPatch = ioDefinition.StorageKind.IsPerPatch();
  325. DeclareInputOrOutput(context, ioDefinition, isOutput, isPerPatch, iq, firstLocation);
  326. }
  327. DeclarePerVertexBlock(context);
  328. }
  329. private static void DeclarePerVertexBlock(CodeGenContext context)
  330. {
  331. if (context.Definitions.Stage.IsVtg())
  332. {
  333. if (context.Definitions.Stage != ShaderStage.Vertex)
  334. {
  335. SpvInstruction perVertexInputStructType = CreatePerVertexStructType(context);
  336. int arraySize = context.Definitions.Stage == ShaderStage.Geometry ? context.Definitions.InputTopology.ToInputVertices() : 32;
  337. SpvInstruction perVertexInputArrayType = context.TypeArray(perVertexInputStructType, context.Constant(context.TypeU32(), arraySize));
  338. SpvInstruction perVertexInputPointerType = context.TypePointer(StorageClass.Input, perVertexInputArrayType);
  339. SpvInstruction perVertexInputVariable = context.Variable(perVertexInputPointerType, StorageClass.Input);
  340. context.Name(perVertexInputVariable, "gl_in");
  341. context.AddGlobalVariable(perVertexInputVariable);
  342. context.Inputs.Add(new IoDefinition(StorageKind.Input, IoVariable.Position), perVertexInputVariable);
  343. if (context.Definitions.Stage == ShaderStage.Geometry &&
  344. context.Definitions.GpPassthrough &&
  345. context.HostCapabilities.SupportsGeometryShaderPassthrough)
  346. {
  347. context.MemberDecorate(perVertexInputStructType, 0, Decoration.PassthroughNV);
  348. context.MemberDecorate(perVertexInputStructType, 1, Decoration.PassthroughNV);
  349. context.MemberDecorate(perVertexInputStructType, 2, Decoration.PassthroughNV);
  350. context.MemberDecorate(perVertexInputStructType, 3, Decoration.PassthroughNV);
  351. }
  352. }
  353. SpvInstruction perVertexOutputStructType = CreatePerVertexStructType(context);
  354. void DecorateTfo(IoVariable ioVariable, int fieldIndex)
  355. {
  356. if (context.Definitions.TryGetTransformFeedbackOutput(ioVariable, 0, 0, out TransformFeedbackOutput transformFeedbackOutput))
  357. {
  358. context.MemberDecorate(perVertexOutputStructType, fieldIndex, Decoration.XfbBuffer, (LiteralInteger)transformFeedbackOutput.Buffer);
  359. context.MemberDecorate(perVertexOutputStructType, fieldIndex, Decoration.XfbStride, (LiteralInteger)transformFeedbackOutput.Stride);
  360. context.MemberDecorate(perVertexOutputStructType, fieldIndex, Decoration.Offset, (LiteralInteger)transformFeedbackOutput.Offset);
  361. }
  362. }
  363. DecorateTfo(IoVariable.Position, 0);
  364. DecorateTfo(IoVariable.PointSize, 1);
  365. DecorateTfo(IoVariable.ClipDistance, 2);
  366. SpvInstruction perVertexOutputArrayType;
  367. if (context.Definitions.Stage == ShaderStage.TessellationControl)
  368. {
  369. int arraySize = context.Definitions.ThreadsPerInputPrimitive;
  370. perVertexOutputArrayType = context.TypeArray(perVertexOutputStructType, context.Constant(context.TypeU32(), arraySize));
  371. }
  372. else
  373. {
  374. perVertexOutputArrayType = perVertexOutputStructType;
  375. }
  376. SpvInstruction perVertexOutputPointerType = context.TypePointer(StorageClass.Output, perVertexOutputArrayType);
  377. SpvInstruction perVertexOutputVariable = context.Variable(perVertexOutputPointerType, StorageClass.Output);
  378. context.AddGlobalVariable(perVertexOutputVariable);
  379. context.Outputs.Add(new IoDefinition(StorageKind.Output, IoVariable.Position), perVertexOutputVariable);
  380. }
  381. }
  382. private static SpvInstruction CreatePerVertexStructType(CodeGenContext context)
  383. {
  384. SpvInstruction vec4FloatType = context.TypeVector(context.TypeFP32(), 4);
  385. SpvInstruction floatType = context.TypeFP32();
  386. SpvInstruction array8FloatType = context.TypeArray(context.TypeFP32(), context.Constant(context.TypeU32(), 8));
  387. SpvInstruction array1FloatType = context.TypeArray(context.TypeFP32(), context.Constant(context.TypeU32(), 1));
  388. SpvInstruction perVertexStructType = context.TypeStruct(true, vec4FloatType, floatType, array8FloatType, array1FloatType);
  389. context.Name(perVertexStructType, "gl_PerVertex");
  390. context.MemberName(perVertexStructType, 0, "gl_Position");
  391. context.MemberName(perVertexStructType, 1, "gl_PointSize");
  392. context.MemberName(perVertexStructType, 2, "gl_ClipDistance");
  393. context.MemberName(perVertexStructType, 3, "gl_CullDistance");
  394. context.Decorate(perVertexStructType, Decoration.Block);
  395. if (context.HostCapabilities.ReducedPrecision)
  396. {
  397. context.MemberDecorate(perVertexStructType, 0, Decoration.Invariant);
  398. }
  399. context.MemberDecorate(perVertexStructType, 0, Decoration.BuiltIn, (LiteralInteger)BuiltIn.Position);
  400. context.MemberDecorate(perVertexStructType, 1, Decoration.BuiltIn, (LiteralInteger)BuiltIn.PointSize);
  401. context.MemberDecorate(perVertexStructType, 2, Decoration.BuiltIn, (LiteralInteger)BuiltIn.ClipDistance);
  402. context.MemberDecorate(perVertexStructType, 3, Decoration.BuiltIn, (LiteralInteger)BuiltIn.CullDistance);
  403. return perVertexStructType;
  404. }
  405. private static void DeclareInputOrOutput(
  406. CodeGenContext context,
  407. IoDefinition ioDefinition,
  408. bool isOutput,
  409. bool isPerPatch,
  410. PixelImap iq = PixelImap.Unused,
  411. int firstLocation = 0)
  412. {
  413. IoVariable ioVariable = ioDefinition.IoVariable;
  414. StorageClass storageClass = isOutput ? StorageClass.Output : StorageClass.Input;
  415. bool isBuiltIn;
  416. BuiltIn builtIn = default;
  417. AggregateType varType;
  418. if (ioVariable == IoVariable.UserDefined)
  419. {
  420. varType = context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput);
  421. isBuiltIn = false;
  422. }
  423. else if (ioVariable == IoVariable.FragmentOutputColor)
  424. {
  425. varType = context.Definitions.GetFragmentOutputColorType(ioDefinition.Location);
  426. isBuiltIn = false;
  427. }
  428. else
  429. {
  430. (builtIn, varType) = IoMap.GetSpirvBuiltIn(ioVariable);
  431. isBuiltIn = true;
  432. if (varType == AggregateType.Invalid)
  433. {
  434. throw new InvalidOperationException($"Unknown variable {ioVariable}.");
  435. }
  436. }
  437. bool hasComponent = context.Definitions.HasPerLocationInputOrOutputComponent(ioVariable, ioDefinition.Location, ioDefinition.Component, isOutput);
  438. if (hasComponent)
  439. {
  440. varType &= AggregateType.ElementTypeMask;
  441. }
  442. else if (ioVariable == IoVariable.UserDefined && context.Definitions.HasTransformFeedbackOutputs(isOutput))
  443. {
  444. varType &= AggregateType.ElementTypeMask;
  445. varType |= context.Definitions.GetTransformFeedbackOutputComponents(ioDefinition.Location, ioDefinition.Component) switch
  446. {
  447. 2 => AggregateType.Vector2,
  448. 3 => AggregateType.Vector3,
  449. 4 => AggregateType.Vector4,
  450. _ => AggregateType.Invalid,
  451. };
  452. }
  453. SpvInstruction spvType = context.GetType(varType, IoMap.GetSpirvBuiltInArrayLength(ioVariable));
  454. bool builtInPassthrough = false;
  455. if (!isPerPatch && IoMap.IsPerVertex(ioVariable, context.Definitions.Stage, isOutput))
  456. {
  457. int arraySize = context.Definitions.Stage == ShaderStage.Geometry ? context.Definitions.InputTopology.ToInputVertices() : 32;
  458. spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), arraySize));
  459. if (context.Definitions.GpPassthrough && context.HostCapabilities.SupportsGeometryShaderPassthrough)
  460. {
  461. builtInPassthrough = true;
  462. }
  463. }
  464. if (context.Definitions.Stage == ShaderStage.TessellationControl && isOutput && !isPerPatch)
  465. {
  466. spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), context.Definitions.ThreadsPerInputPrimitive));
  467. }
  468. SpvInstruction spvPointerType = context.TypePointer(storageClass, spvType);
  469. SpvInstruction spvVar = context.Variable(spvPointerType, storageClass);
  470. if (builtInPassthrough)
  471. {
  472. context.Decorate(spvVar, Decoration.PassthroughNV);
  473. }
  474. if (isBuiltIn)
  475. {
  476. if (isPerPatch)
  477. {
  478. context.Decorate(spvVar, Decoration.Patch);
  479. }
  480. if (context.HostCapabilities.ReducedPrecision && ioVariable == IoVariable.Position)
  481. {
  482. context.Decorate(spvVar, Decoration.Invariant);
  483. }
  484. context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)builtIn);
  485. }
  486. else if (isPerPatch)
  487. {
  488. context.Decorate(spvVar, Decoration.Patch);
  489. if (ioVariable == IoVariable.UserDefined)
  490. {
  491. int location = context.AttributeUsage.GetPerPatchAttributeLocation(ioDefinition.Location);
  492. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
  493. }
  494. }
  495. else if (ioVariable == IoVariable.UserDefined)
  496. {
  497. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)ioDefinition.Location);
  498. if (hasComponent)
  499. {
  500. context.Decorate(spvVar, Decoration.Component, (LiteralInteger)ioDefinition.Component);
  501. }
  502. if (!isOutput &&
  503. !isPerPatch &&
  504. (context.AttributeUsage.PassthroughAttributes & (1 << ioDefinition.Location)) != 0 &&
  505. context.HostCapabilities.SupportsGeometryShaderPassthrough)
  506. {
  507. context.Decorate(spvVar, Decoration.PassthroughNV);
  508. }
  509. }
  510. else if (ioVariable == IoVariable.FragmentOutputColor)
  511. {
  512. int location = ioDefinition.Location;
  513. if (context.Definitions.Stage == ShaderStage.Fragment && context.Definitions.DualSourceBlend)
  514. {
  515. int index = location - firstLocation;
  516. if ((uint)index < 2)
  517. {
  518. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)firstLocation);
  519. context.Decorate(spvVar, Decoration.Index, (LiteralInteger)index);
  520. }
  521. else
  522. {
  523. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
  524. }
  525. }
  526. else
  527. {
  528. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
  529. }
  530. }
  531. if (!isOutput)
  532. {
  533. switch (iq)
  534. {
  535. case PixelImap.Constant:
  536. context.Decorate(spvVar, Decoration.Flat);
  537. break;
  538. case PixelImap.ScreenLinear:
  539. context.Decorate(spvVar, Decoration.NoPerspective);
  540. break;
  541. }
  542. }
  543. else if (context.Definitions.TryGetTransformFeedbackOutput(
  544. ioVariable,
  545. ioDefinition.Location,
  546. ioDefinition.Component,
  547. out TransformFeedbackOutput transformFeedbackOutput))
  548. {
  549. context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)transformFeedbackOutput.Buffer);
  550. context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)transformFeedbackOutput.Stride);
  551. context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)transformFeedbackOutput.Offset);
  552. }
  553. context.AddGlobalVariable(spvVar);
  554. Dictionary<IoDefinition, SpvInstruction> dict = isPerPatch
  555. ? (isOutput ? context.OutputsPerPatch : context.InputsPerPatch)
  556. : (isOutput ? context.Outputs : context.Inputs);
  557. dict.Add(ioDefinition, spvVar);
  558. }
  559. }
  560. }