Declarations.cs 28 KB

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