Declarations.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. using Ryujinx.Common;
  2. using Ryujinx.Graphics.Shader.IntermediateRepresentation;
  3. using Ryujinx.Graphics.Shader.StructuredIr;
  4. using Ryujinx.Graphics.Shader.Translation;
  5. using Spv.Generator;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Numerics;
  10. using static Spv.Specification;
  11. using SpvInstruction = Spv.Generator.Instruction;
  12. namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
  13. {
  14. static class Declarations
  15. {
  16. private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
  17. public static void DeclareParameters(CodeGenContext context, StructuredFunction function)
  18. {
  19. DeclareParameters(context, function.InArguments, 0);
  20. DeclareParameters(context, function.OutArguments, function.InArguments.Length);
  21. }
  22. private static void DeclareParameters(CodeGenContext context, IEnumerable<AggregateType> argTypes, int argIndex)
  23. {
  24. foreach (var argType in argTypes)
  25. {
  26. var argPointerType = context.TypePointer(StorageClass.Function, context.GetType(argType));
  27. var spvArg = context.FunctionParameter(argPointerType);
  28. context.DeclareArgument(argIndex++, spvArg);
  29. }
  30. }
  31. public static void DeclareLocals(CodeGenContext context, StructuredFunction function)
  32. {
  33. foreach (AstOperand local in function.Locals)
  34. {
  35. var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(local.VarType));
  36. var spvLocal = context.Variable(localPointerType, StorageClass.Function);
  37. context.AddLocalVariable(spvLocal);
  38. context.DeclareLocal(local, spvLocal);
  39. }
  40. var ivector2Type = context.TypeVector(context.TypeS32(), 2);
  41. var coordTempPointerType = context.TypePointer(StorageClass.Function, ivector2Type);
  42. var coordTemp = context.Variable(coordTempPointerType, StorageClass.Function);
  43. context.AddLocalVariable(coordTemp);
  44. context.CoordTemp = coordTemp;
  45. }
  46. public static void DeclareLocalForArgs(CodeGenContext context, List<StructuredFunction> functions)
  47. {
  48. for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++)
  49. {
  50. StructuredFunction function = functions[funcIndex];
  51. SpvInstruction[] locals = new SpvInstruction[function.InArguments.Length];
  52. for (int i = 0; i < function.InArguments.Length; i++)
  53. {
  54. var type = function.GetArgumentType(i);
  55. var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(type));
  56. var spvLocal = context.Variable(localPointerType, StorageClass.Function);
  57. context.AddLocalVariable(spvLocal);
  58. locals[i] = spvLocal;
  59. }
  60. context.DeclareLocalForArgs(funcIndex, locals);
  61. }
  62. }
  63. public static void DeclareAll(CodeGenContext context, StructuredProgramInfo info)
  64. {
  65. if (context.Config.Stage == ShaderStage.Compute)
  66. {
  67. int localMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeLocalMemorySize(), 4);
  68. if (localMemorySize != 0)
  69. {
  70. DeclareLocalMemory(context, localMemorySize);
  71. }
  72. int sharedMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeSharedMemorySize(), 4);
  73. if (sharedMemorySize != 0)
  74. {
  75. DeclareSharedMemory(context, sharedMemorySize);
  76. }
  77. }
  78. else if (context.Config.LocalMemorySize != 0)
  79. {
  80. int localMemorySize = BitUtils.DivRoundUp(context.Config.LocalMemorySize, 4);
  81. DeclareLocalMemory(context, localMemorySize);
  82. }
  83. DeclareSupportBuffer(context);
  84. DeclareUniformBuffers(context, context.Config.GetConstantBufferDescriptors());
  85. DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
  86. DeclareSamplers(context, context.Config.GetTextureDescriptors());
  87. DeclareImages(context, context.Config.GetImageDescriptors());
  88. DeclareInputsAndOutputs(context, info);
  89. }
  90. private static void DeclareLocalMemory(CodeGenContext context, int size)
  91. {
  92. context.LocalMemory = DeclareMemory(context, StorageClass.Private, size);
  93. }
  94. private static void DeclareSharedMemory(CodeGenContext context, int size)
  95. {
  96. context.SharedMemory = DeclareMemory(context, StorageClass.Workgroup, size);
  97. }
  98. private static SpvInstruction DeclareMemory(CodeGenContext context, StorageClass storage, int size)
  99. {
  100. var arrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), size));
  101. var pointerType = context.TypePointer(storage, arrayType);
  102. var variable = context.Variable(pointerType, storage);
  103. context.AddGlobalVariable(variable);
  104. return variable;
  105. }
  106. private static void DeclareSupportBuffer(CodeGenContext context)
  107. {
  108. if (!context.Config.Stage.SupportsRenderScale() && !(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable()))
  109. {
  110. return;
  111. }
  112. var isBgraArrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), SupportBuffer.FragmentIsBgraCount));
  113. var viewportInverseVectorType = context.TypeVector(context.TypeFP32(), 4);
  114. var renderScaleArrayType = context.TypeArray(context.TypeFP32(), context.Constant(context.TypeU32(), SupportBuffer.RenderScaleMaxCount));
  115. context.Decorate(isBgraArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
  116. context.Decorate(renderScaleArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
  117. var supportBufferStructType = context.TypeStruct(false, context.TypeU32(), isBgraArrayType, viewportInverseVectorType, context.TypeS32(), renderScaleArrayType);
  118. context.MemberDecorate(supportBufferStructType, 0, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentAlphaTestOffset);
  119. context.MemberDecorate(supportBufferStructType, 1, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentIsBgraOffset);
  120. context.MemberDecorate(supportBufferStructType, 2, Decoration.Offset, (LiteralInteger)SupportBuffer.ViewportInverseOffset);
  121. context.MemberDecorate(supportBufferStructType, 3, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentRenderScaleCountOffset);
  122. context.MemberDecorate(supportBufferStructType, 4, Decoration.Offset, (LiteralInteger)SupportBuffer.GraphicsRenderScaleOffset);
  123. context.Decorate(supportBufferStructType, Decoration.Block);
  124. var supportBufferPointerType = context.TypePointer(StorageClass.Uniform, supportBufferStructType);
  125. var supportBufferVariable = context.Variable(supportBufferPointerType, StorageClass.Uniform);
  126. context.Decorate(supportBufferVariable, Decoration.DescriptorSet, (LiteralInteger)0);
  127. context.Decorate(supportBufferVariable, Decoration.Binding, (LiteralInteger)0);
  128. context.AddGlobalVariable(supportBufferVariable);
  129. context.SupportBuffer = supportBufferVariable;
  130. }
  131. private static void DeclareUniformBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
  132. {
  133. if (descriptors.Length == 0)
  134. {
  135. return;
  136. }
  137. uint ubSize = Constants.ConstantBufferSize / 16;
  138. var ubArrayType = context.TypeArray(context.TypeVector(context.TypeFP32(), 4), context.Constant(context.TypeU32(), ubSize), true);
  139. context.Decorate(ubArrayType, Decoration.ArrayStride, (LiteralInteger)16);
  140. var ubStructType = context.TypeStruct(true, ubArrayType);
  141. context.Decorate(ubStructType, Decoration.Block);
  142. context.MemberDecorate(ubStructType, 0, Decoration.Offset, (LiteralInteger)0);
  143. if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
  144. {
  145. int count = descriptors.Max(x => x.Slot) + 1;
  146. var ubStructArrayType = context.TypeArray(ubStructType, context.Constant(context.TypeU32(), count));
  147. var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructArrayType);
  148. var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
  149. context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_u");
  150. context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
  151. context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstConstantBufferBinding);
  152. context.AddGlobalVariable(ubVariable);
  153. context.UniformBuffersArray = ubVariable;
  154. }
  155. else
  156. {
  157. var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType);
  158. foreach (var descriptor in descriptors)
  159. {
  160. var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
  161. context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_c{descriptor.Slot}");
  162. context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
  163. context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
  164. context.AddGlobalVariable(ubVariable);
  165. context.UniformBuffers.Add(descriptor.Slot, ubVariable);
  166. }
  167. }
  168. }
  169. private static void DeclareStorageBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
  170. {
  171. if (descriptors.Length == 0)
  172. {
  173. return;
  174. }
  175. int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 1 : 0;
  176. int count = descriptors.Max(x => x.Slot) + 1;
  177. var sbArrayType = context.TypeRuntimeArray(context.TypeU32());
  178. context.Decorate(sbArrayType, Decoration.ArrayStride, (LiteralInteger)4);
  179. var sbStructType = context.TypeStruct(true, sbArrayType);
  180. context.Decorate(sbStructType, Decoration.BufferBlock);
  181. context.MemberDecorate(sbStructType, 0, Decoration.Offset, (LiteralInteger)0);
  182. var sbStructArrayType = context.TypeArray(sbStructType, context.Constant(context.TypeU32(), count));
  183. var sbPointerType = context.TypePointer(StorageClass.Uniform, sbStructArrayType);
  184. var sbVariable = context.Variable(sbPointerType, StorageClass.Uniform);
  185. context.Name(sbVariable, $"{GetStagePrefix(context.Config.Stage)}_s");
  186. context.Decorate(sbVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
  187. context.Decorate(sbVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstStorageBufferBinding);
  188. context.AddGlobalVariable(sbVariable);
  189. context.StorageBuffersArray = sbVariable;
  190. }
  191. private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
  192. {
  193. foreach (var descriptor in descriptors)
  194. {
  195. var meta = new TextureMeta(descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Format);
  196. if (context.Samplers.ContainsKey(meta))
  197. {
  198. continue;
  199. }
  200. int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 2 : 0;
  201. var dim = (descriptor.Type & SamplerType.Mask) switch
  202. {
  203. SamplerType.Texture1D => Dim.Dim1D,
  204. SamplerType.Texture2D => Dim.Dim2D,
  205. SamplerType.Texture3D => Dim.Dim3D,
  206. SamplerType.TextureCube => Dim.Cube,
  207. SamplerType.TextureBuffer => Dim.Buffer,
  208. _ => throw new InvalidOperationException($"Invalid sampler type \"{descriptor.Type & SamplerType.Mask}\".")
  209. };
  210. var imageType = context.TypeImage(
  211. context.TypeFP32(),
  212. dim,
  213. descriptor.Type.HasFlag(SamplerType.Shadow),
  214. descriptor.Type.HasFlag(SamplerType.Array),
  215. descriptor.Type.HasFlag(SamplerType.Multisample),
  216. 1,
  217. ImageFormat.Unknown);
  218. var nameSuffix = meta.CbufSlot < 0 ? $"_tcb_{meta.Handle:X}" : $"_cb{meta.CbufSlot}_{meta.Handle:X}";
  219. var sampledImageType = context.TypeSampledImage(imageType);
  220. var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageType);
  221. var sampledImageVariable = context.Variable(sampledImagePointerType, StorageClass.UniformConstant);
  222. context.Samplers.Add(meta, (imageType, sampledImageType, sampledImageVariable));
  223. context.SamplersTypes.Add(meta, descriptor.Type);
  224. context.Name(sampledImageVariable, $"{GetStagePrefix(context.Config.Stage)}_tex{nameSuffix}");
  225. context.Decorate(sampledImageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
  226. context.Decorate(sampledImageVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
  227. context.AddGlobalVariable(sampledImageVariable);
  228. }
  229. }
  230. private static void DeclareImages(CodeGenContext context, TextureDescriptor[] descriptors)
  231. {
  232. foreach (var descriptor in descriptors)
  233. {
  234. var meta = new TextureMeta(descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Format);
  235. if (context.Images.ContainsKey(meta))
  236. {
  237. continue;
  238. }
  239. int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 3 : 0;
  240. var dim = GetDim(descriptor.Type);
  241. var imageType = context.TypeImage(
  242. context.GetType(meta.Format.GetComponentType()),
  243. dim,
  244. descriptor.Type.HasFlag(SamplerType.Shadow),
  245. descriptor.Type.HasFlag(SamplerType.Array),
  246. descriptor.Type.HasFlag(SamplerType.Multisample),
  247. AccessQualifier.ReadWrite,
  248. GetImageFormat(meta.Format));
  249. var nameSuffix = meta.CbufSlot < 0 ?
  250. $"_tcb_{meta.Handle:X}_{meta.Format.ToGlslFormat()}" :
  251. $"_cb{meta.CbufSlot}_{meta.Handle:X}_{meta.Format.ToGlslFormat()}";
  252. var imagePointerType = context.TypePointer(StorageClass.UniformConstant, imageType);
  253. var imageVariable = context.Variable(imagePointerType, StorageClass.UniformConstant);
  254. context.Images.Add(meta, (imageType, imageVariable));
  255. context.Name(imageVariable, $"{GetStagePrefix(context.Config.Stage)}_img{nameSuffix}");
  256. context.Decorate(imageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
  257. context.Decorate(imageVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
  258. if (descriptor.Flags.HasFlag(TextureUsageFlags.ImageCoherent))
  259. {
  260. context.Decorate(imageVariable, Decoration.Coherent);
  261. }
  262. context.AddGlobalVariable(imageVariable);
  263. }
  264. }
  265. private static Dim GetDim(SamplerType type)
  266. {
  267. return (type & SamplerType.Mask) switch
  268. {
  269. SamplerType.Texture1D => Dim.Dim1D,
  270. SamplerType.Texture2D => Dim.Dim2D,
  271. SamplerType.Texture3D => Dim.Dim3D,
  272. SamplerType.TextureCube => Dim.Cube,
  273. SamplerType.TextureBuffer => Dim.Buffer,
  274. _ => throw new ArgumentException($"Invalid sampler type \"{type & SamplerType.Mask}\".")
  275. };
  276. }
  277. private static ImageFormat GetImageFormat(TextureFormat format)
  278. {
  279. return format switch
  280. {
  281. TextureFormat.Unknown => ImageFormat.Unknown,
  282. TextureFormat.R8Unorm => ImageFormat.R8,
  283. TextureFormat.R8Snorm => ImageFormat.R8Snorm,
  284. TextureFormat.R8Uint => ImageFormat.R8ui,
  285. TextureFormat.R8Sint => ImageFormat.R8i,
  286. TextureFormat.R16Float => ImageFormat.R16f,
  287. TextureFormat.R16Unorm => ImageFormat.R16,
  288. TextureFormat.R16Snorm => ImageFormat.R16Snorm,
  289. TextureFormat.R16Uint => ImageFormat.R16ui,
  290. TextureFormat.R16Sint => ImageFormat.R16i,
  291. TextureFormat.R32Float => ImageFormat.R32f,
  292. TextureFormat.R32Uint => ImageFormat.R32ui,
  293. TextureFormat.R32Sint => ImageFormat.R32i,
  294. TextureFormat.R8G8Unorm => ImageFormat.Rg8,
  295. TextureFormat.R8G8Snorm => ImageFormat.Rg8Snorm,
  296. TextureFormat.R8G8Uint => ImageFormat.Rg8ui,
  297. TextureFormat.R8G8Sint => ImageFormat.Rg8i,
  298. TextureFormat.R16G16Float => ImageFormat.Rg16f,
  299. TextureFormat.R16G16Unorm => ImageFormat.Rg16,
  300. TextureFormat.R16G16Snorm => ImageFormat.Rg16Snorm,
  301. TextureFormat.R16G16Uint => ImageFormat.Rg16ui,
  302. TextureFormat.R16G16Sint => ImageFormat.Rg16i,
  303. TextureFormat.R32G32Float => ImageFormat.Rg32f,
  304. TextureFormat.R32G32Uint => ImageFormat.Rg32ui,
  305. TextureFormat.R32G32Sint => ImageFormat.Rg32i,
  306. TextureFormat.R8G8B8A8Unorm => ImageFormat.Rgba8,
  307. TextureFormat.R8G8B8A8Snorm => ImageFormat.Rgba8Snorm,
  308. TextureFormat.R8G8B8A8Uint => ImageFormat.Rgba8ui,
  309. TextureFormat.R8G8B8A8Sint => ImageFormat.Rgba8i,
  310. TextureFormat.R16G16B16A16Float => ImageFormat.Rgba16f,
  311. TextureFormat.R16G16B16A16Unorm => ImageFormat.Rgba16,
  312. TextureFormat.R16G16B16A16Snorm => ImageFormat.Rgba16Snorm,
  313. TextureFormat.R16G16B16A16Uint => ImageFormat.Rgba16ui,
  314. TextureFormat.R16G16B16A16Sint => ImageFormat.Rgba16i,
  315. TextureFormat.R32G32B32A32Float => ImageFormat.Rgba32f,
  316. TextureFormat.R32G32B32A32Uint => ImageFormat.Rgba32ui,
  317. TextureFormat.R32G32B32A32Sint => ImageFormat.Rgba32i,
  318. TextureFormat.R10G10B10A2Unorm => ImageFormat.Rgb10A2,
  319. TextureFormat.R10G10B10A2Uint => ImageFormat.Rgb10a2ui,
  320. TextureFormat.R11G11B10Float => ImageFormat.R11fG11fB10f,
  321. _ => throw new ArgumentException($"Invalid texture format \"{format}\".")
  322. };
  323. }
  324. private static void DeclareInputsAndOutputs(CodeGenContext context, StructuredProgramInfo info)
  325. {
  326. foreach (var ioDefinition in info.IoDefinitions)
  327. {
  328. var ioVariable = ioDefinition.IoVariable;
  329. // Those are actually from constant buffer, rather than being actual inputs or outputs,
  330. // so we must ignore them here as they are declared as part of the support buffer.
  331. // TODO: Delete this after we represent this properly on the IR (as a constant buffer rather than "input").
  332. if (ioVariable == IoVariable.FragmentOutputIsBgra ||
  333. ioVariable == IoVariable.SupportBlockRenderScale ||
  334. ioVariable == IoVariable.SupportBlockViewInverse)
  335. {
  336. continue;
  337. }
  338. bool isOutput = ioDefinition.StorageKind.IsOutput();
  339. bool isPerPatch = ioDefinition.StorageKind.IsPerPatch();
  340. PixelImap iq = PixelImap.Unused;
  341. if (context.Config.Stage == ShaderStage.Fragment)
  342. {
  343. if (ioVariable == IoVariable.UserDefined)
  344. {
  345. iq = context.Config.ImapTypes[ioDefinition.Location].GetFirstUsedType();
  346. }
  347. else
  348. {
  349. (_, AggregateType varType) = IoMap.GetSpirvBuiltIn(ioVariable);
  350. AggregateType elemType = varType & AggregateType.ElementTypeMask;
  351. if (elemType == AggregateType.S32 || elemType == AggregateType.U32)
  352. {
  353. iq = PixelImap.Constant;
  354. }
  355. }
  356. }
  357. DeclareInputOrOutput(context, ioDefinition, isOutput, isPerPatch, iq);
  358. }
  359. }
  360. private static void DeclareInputOrOutput(CodeGenContext context, IoDefinition ioDefinition, bool isOutput, bool isPerPatch, PixelImap iq = PixelImap.Unused)
  361. {
  362. IoVariable ioVariable = ioDefinition.IoVariable;
  363. var storageClass = isOutput ? StorageClass.Output : StorageClass.Input;
  364. bool isBuiltIn;
  365. BuiltIn builtIn = default;
  366. AggregateType varType;
  367. if (ioVariable == IoVariable.UserDefined)
  368. {
  369. varType = context.Config.GetUserDefinedType(ioDefinition.Location, isOutput);
  370. isBuiltIn = false;
  371. }
  372. else if (ioVariable == IoVariable.FragmentOutputColor)
  373. {
  374. varType = context.Config.GetFragmentOutputColorType(ioDefinition.Location);
  375. isBuiltIn = false;
  376. }
  377. else
  378. {
  379. (builtIn, varType) = IoMap.GetSpirvBuiltIn(ioVariable);
  380. isBuiltIn = true;
  381. if (varType == AggregateType.Invalid)
  382. {
  383. throw new InvalidOperationException($"Unknown variable {ioVariable}.");
  384. }
  385. }
  386. bool hasComponent = context.Config.HasPerLocationInputOrOutputComponent(ioVariable, ioDefinition.Location, ioDefinition.Component, isOutput);
  387. if (hasComponent)
  388. {
  389. varType &= AggregateType.ElementTypeMask;
  390. }
  391. else if (ioVariable == IoVariable.UserDefined && context.Config.HasTransformFeedbackOutputs(isOutput))
  392. {
  393. varType &= AggregateType.ElementTypeMask;
  394. varType |= context.Config.GetTransformFeedbackOutputComponents(ioDefinition.Location, ioDefinition.Component) switch
  395. {
  396. 2 => AggregateType.Vector2,
  397. 3 => AggregateType.Vector3,
  398. 4 => AggregateType.Vector4,
  399. _ => AggregateType.Invalid
  400. };
  401. }
  402. var spvType = context.GetType(varType, IoMap.GetSpirvBuiltInArrayLength(ioVariable));
  403. bool builtInPassthrough = false;
  404. if (!isPerPatch && IoMap.IsPerVertex(ioVariable, context.Config.Stage, isOutput))
  405. {
  406. int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32;
  407. spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
  408. if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
  409. {
  410. builtInPassthrough = true;
  411. }
  412. }
  413. if (context.Config.Stage == ShaderStage.TessellationControl && isOutput && !isPerPatch)
  414. {
  415. spvType = context.TypeArray(spvType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
  416. }
  417. var spvPointerType = context.TypePointer(storageClass, spvType);
  418. var spvVar = context.Variable(spvPointerType, storageClass);
  419. if (builtInPassthrough)
  420. {
  421. context.Decorate(spvVar, Decoration.PassthroughNV);
  422. }
  423. if (isBuiltIn)
  424. {
  425. if (isPerPatch)
  426. {
  427. context.Decorate(spvVar, Decoration.Patch);
  428. }
  429. if (context.Config.GpuAccessor.QueryHostReducedPrecision() && ioVariable == IoVariable.Position)
  430. {
  431. context.Decorate(spvVar, Decoration.Invariant);
  432. }
  433. context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)builtIn);
  434. }
  435. else if (isPerPatch)
  436. {
  437. context.Decorate(spvVar, Decoration.Patch);
  438. if (ioVariable == IoVariable.UserDefined)
  439. {
  440. int location = context.Config.GetPerPatchAttributeLocation(ioDefinition.Location);
  441. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
  442. }
  443. }
  444. else if (ioVariable == IoVariable.UserDefined)
  445. {
  446. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)ioDefinition.Location);
  447. if (hasComponent)
  448. {
  449. context.Decorate(spvVar, Decoration.Component, (LiteralInteger)ioDefinition.Component);
  450. }
  451. if (!isOutput &&
  452. !isPerPatch &&
  453. (context.Config.PassthroughAttributes & (1 << ioDefinition.Location)) != 0 &&
  454. context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
  455. {
  456. context.Decorate(spvVar, Decoration.PassthroughNV);
  457. }
  458. }
  459. else if (ioVariable == IoVariable.FragmentOutputColor)
  460. {
  461. int location = ioDefinition.Location;
  462. if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
  463. {
  464. int firstLocation = BitOperations.TrailingZeroCount(context.Config.UsedOutputAttributes);
  465. int index = location - firstLocation;
  466. int mask = 3 << firstLocation;
  467. if ((uint)index < 2 && (context.Config.UsedOutputAttributes & mask) == mask)
  468. {
  469. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)firstLocation);
  470. context.Decorate(spvVar, Decoration.Index, (LiteralInteger)index);
  471. }
  472. else
  473. {
  474. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
  475. }
  476. }
  477. else
  478. {
  479. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
  480. }
  481. }
  482. if (!isOutput)
  483. {
  484. switch (iq)
  485. {
  486. case PixelImap.Constant:
  487. context.Decorate(spvVar, Decoration.Flat);
  488. break;
  489. case PixelImap.ScreenLinear:
  490. context.Decorate(spvVar, Decoration.NoPerspective);
  491. break;
  492. }
  493. }
  494. else if (context.Config.TryGetTransformFeedbackOutput(
  495. ioVariable,
  496. ioDefinition.Location,
  497. ioDefinition.Component,
  498. out var transformFeedbackOutput))
  499. {
  500. context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)transformFeedbackOutput.Buffer);
  501. context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)transformFeedbackOutput.Stride);
  502. context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)transformFeedbackOutput.Offset);
  503. }
  504. context.AddGlobalVariable(spvVar);
  505. var dict = isPerPatch
  506. ? (isOutput ? context.OutputsPerPatch : context.InputsPerPatch)
  507. : (isOutput ? context.Outputs : context.Inputs);
  508. dict.Add(ioDefinition, spvVar);
  509. }
  510. private static string GetStagePrefix(ShaderStage stage)
  511. {
  512. return StagePrefixes[(int)stage];
  513. }
  514. }
  515. }