Declarations.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. using Ryujinx.Common;
  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 System.Linq;
  9. using System.Numerics;
  10. using static Spv.Specification;
  11. namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
  12. {
  13. static class Declarations
  14. {
  15. // At least 16 attributes are guaranteed by the spec.
  16. public const int MaxAttributes = 16;
  17. private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
  18. public static void DeclareParameters(CodeGenContext context, StructuredFunction function)
  19. {
  20. DeclareParameters(context, function.InArguments, 0);
  21. DeclareParameters(context, function.OutArguments, function.InArguments.Length);
  22. }
  23. private static void DeclareParameters(CodeGenContext context, IEnumerable<AggregateType> argTypes, int argIndex)
  24. {
  25. foreach (var argType in argTypes)
  26. {
  27. var argPointerType = context.TypePointer(StorageClass.Function, context.GetType(argType));
  28. var spvArg = context.FunctionParameter(argPointerType);
  29. context.DeclareArgument(argIndex++, spvArg);
  30. }
  31. }
  32. public static void DeclareLocals(CodeGenContext context, StructuredFunction function)
  33. {
  34. foreach (AstOperand local in function.Locals)
  35. {
  36. var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(local.VarType));
  37. var spvLocal = context.Variable(localPointerType, StorageClass.Function);
  38. context.AddLocalVariable(spvLocal);
  39. context.DeclareLocal(local, spvLocal);
  40. }
  41. var ivector2Type = context.TypeVector(context.TypeS32(), 2);
  42. var coordTempPointerType = context.TypePointer(StorageClass.Function, ivector2Type);
  43. var coordTemp = context.Variable(coordTempPointerType, StorageClass.Function);
  44. context.AddLocalVariable(coordTemp);
  45. context.CoordTemp = coordTemp;
  46. }
  47. public static void DeclareLocalForArgs(CodeGenContext context, List<StructuredFunction> functions)
  48. {
  49. for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++)
  50. {
  51. StructuredFunction function = functions[funcIndex];
  52. Instruction[] locals = new Instruction[function.InArguments.Length];
  53. for (int i = 0; i < function.InArguments.Length; i++)
  54. {
  55. var type = function.GetArgumentType(i);
  56. var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(type));
  57. var spvLocal = context.Variable(localPointerType, StorageClass.Function);
  58. context.AddLocalVariable(spvLocal);
  59. locals[i] = spvLocal;
  60. }
  61. context.DeclareLocalForArgs(funcIndex, locals);
  62. }
  63. }
  64. public static void DeclareAll(CodeGenContext context, StructuredProgramInfo info)
  65. {
  66. if (context.Config.Stage == ShaderStage.Compute)
  67. {
  68. int localMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeLocalMemorySize(), 4);
  69. if (localMemorySize != 0)
  70. {
  71. DeclareLocalMemory(context, localMemorySize);
  72. }
  73. int sharedMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeSharedMemorySize(), 4);
  74. if (sharedMemorySize != 0)
  75. {
  76. DeclareSharedMemory(context, sharedMemorySize);
  77. }
  78. }
  79. else if (context.Config.LocalMemorySize != 0)
  80. {
  81. int localMemorySize = BitUtils.DivRoundUp(context.Config.LocalMemorySize, 4);
  82. DeclareLocalMemory(context, localMemorySize);
  83. }
  84. DeclareSupportBuffer(context);
  85. DeclareUniformBuffers(context, context.Config.GetConstantBufferDescriptors());
  86. DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
  87. DeclareSamplers(context, context.Config.GetTextureDescriptors());
  88. DeclareImages(context, context.Config.GetImageDescriptors());
  89. DeclareInputAttributes(context, info, perPatch: false);
  90. DeclareOutputAttributes(context, info, perPatch: false);
  91. DeclareInputAttributes(context, info, perPatch: true);
  92. DeclareOutputAttributes(context, info, perPatch: true);
  93. }
  94. private static void DeclareLocalMemory(CodeGenContext context, int size)
  95. {
  96. context.LocalMemory = DeclareMemory(context, StorageClass.Private, size);
  97. }
  98. private static void DeclareSharedMemory(CodeGenContext context, int size)
  99. {
  100. context.SharedMemory = DeclareMemory(context, StorageClass.Workgroup, size);
  101. }
  102. private static Instruction DeclareMemory(CodeGenContext context, StorageClass storage, int size)
  103. {
  104. var arrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), size));
  105. var pointerType = context.TypePointer(storage, arrayType);
  106. var variable = context.Variable(pointerType, storage);
  107. context.AddGlobalVariable(variable);
  108. return variable;
  109. }
  110. private static void DeclareSupportBuffer(CodeGenContext context)
  111. {
  112. if (!context.Config.Stage.SupportsRenderScale() && !(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable()))
  113. {
  114. return;
  115. }
  116. var isBgraArrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), SupportBuffer.FragmentIsBgraCount));
  117. var viewportInverseVectorType = context.TypeVector(context.TypeFP32(), 4);
  118. var renderScaleArrayType = context.TypeArray(context.TypeFP32(), context.Constant(context.TypeU32(), SupportBuffer.RenderScaleMaxCount));
  119. context.Decorate(isBgraArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
  120. context.Decorate(renderScaleArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
  121. var supportBufferStructType = context.TypeStruct(false, context.TypeU32(), isBgraArrayType, viewportInverseVectorType, context.TypeS32(), renderScaleArrayType);
  122. context.MemberDecorate(supportBufferStructType, 0, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentAlphaTestOffset);
  123. context.MemberDecorate(supportBufferStructType, 1, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentIsBgraOffset);
  124. context.MemberDecorate(supportBufferStructType, 2, Decoration.Offset, (LiteralInteger)SupportBuffer.ViewportInverseOffset);
  125. context.MemberDecorate(supportBufferStructType, 3, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentRenderScaleCountOffset);
  126. context.MemberDecorate(supportBufferStructType, 4, Decoration.Offset, (LiteralInteger)SupportBuffer.GraphicsRenderScaleOffset);
  127. context.Decorate(supportBufferStructType, Decoration.Block);
  128. var supportBufferPointerType = context.TypePointer(StorageClass.Uniform, supportBufferStructType);
  129. var supportBufferVariable = context.Variable(supportBufferPointerType, StorageClass.Uniform);
  130. context.Decorate(supportBufferVariable, Decoration.DescriptorSet, (LiteralInteger)0);
  131. context.Decorate(supportBufferVariable, Decoration.Binding, (LiteralInteger)0);
  132. context.AddGlobalVariable(supportBufferVariable);
  133. context.SupportBuffer = supportBufferVariable;
  134. }
  135. private static void DeclareUniformBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
  136. {
  137. if (descriptors.Length == 0)
  138. {
  139. return;
  140. }
  141. uint ubSize = Constants.ConstantBufferSize / 16;
  142. var ubArrayType = context.TypeArray(context.TypeVector(context.TypeFP32(), 4), context.Constant(context.TypeU32(), ubSize), true);
  143. context.Decorate(ubArrayType, Decoration.ArrayStride, (LiteralInteger)16);
  144. var ubStructType = context.TypeStruct(true, ubArrayType);
  145. context.Decorate(ubStructType, Decoration.Block);
  146. context.MemberDecorate(ubStructType, 0, Decoration.Offset, (LiteralInteger)0);
  147. if (context.Config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing))
  148. {
  149. int count = descriptors.Max(x => x.Slot) + 1;
  150. var ubStructArrayType = context.TypeArray(ubStructType, context.Constant(context.TypeU32(), count));
  151. var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructArrayType);
  152. var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
  153. context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_u");
  154. context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
  155. context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstConstantBufferBinding);
  156. context.AddGlobalVariable(ubVariable);
  157. context.UniformBuffersArray = ubVariable;
  158. }
  159. else
  160. {
  161. var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType);
  162. foreach (var descriptor in descriptors)
  163. {
  164. var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
  165. context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_c{descriptor.Slot}");
  166. context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
  167. context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
  168. context.AddGlobalVariable(ubVariable);
  169. context.UniformBuffers.Add(descriptor.Slot, ubVariable);
  170. }
  171. }
  172. }
  173. private static void DeclareStorageBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
  174. {
  175. if (descriptors.Length == 0)
  176. {
  177. return;
  178. }
  179. int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 1 : 0;
  180. int count = descriptors.Max(x => x.Slot) + 1;
  181. var sbArrayType = context.TypeRuntimeArray(context.TypeU32());
  182. context.Decorate(sbArrayType, Decoration.ArrayStride, (LiteralInteger)4);
  183. var sbStructType = context.TypeStruct(true, sbArrayType);
  184. context.Decorate(sbStructType, Decoration.BufferBlock);
  185. context.MemberDecorate(sbStructType, 0, Decoration.Offset, (LiteralInteger)0);
  186. var sbStructArrayType = context.TypeArray(sbStructType, context.Constant(context.TypeU32(), count));
  187. var sbPointerType = context.TypePointer(StorageClass.Uniform, sbStructArrayType);
  188. var sbVariable = context.Variable(sbPointerType, StorageClass.Uniform);
  189. context.Name(sbVariable, $"{GetStagePrefix(context.Config.Stage)}_s");
  190. context.Decorate(sbVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
  191. context.Decorate(sbVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstStorageBufferBinding);
  192. context.AddGlobalVariable(sbVariable);
  193. context.StorageBuffersArray = sbVariable;
  194. }
  195. private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
  196. {
  197. foreach (var descriptor in descriptors)
  198. {
  199. var meta = new TextureMeta(descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Format);
  200. if (context.Samplers.ContainsKey(meta))
  201. {
  202. continue;
  203. }
  204. int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 2 : 0;
  205. var dim = (descriptor.Type & SamplerType.Mask) switch
  206. {
  207. SamplerType.Texture1D => Dim.Dim1D,
  208. SamplerType.Texture2D => Dim.Dim2D,
  209. SamplerType.Texture3D => Dim.Dim3D,
  210. SamplerType.TextureCube => Dim.Cube,
  211. SamplerType.TextureBuffer => Dim.Buffer,
  212. _ => throw new InvalidOperationException($"Invalid sampler type \"{descriptor.Type & SamplerType.Mask}\".")
  213. };
  214. var imageType = context.TypeImage(
  215. context.TypeFP32(),
  216. dim,
  217. descriptor.Type.HasFlag(SamplerType.Shadow),
  218. descriptor.Type.HasFlag(SamplerType.Array),
  219. descriptor.Type.HasFlag(SamplerType.Multisample),
  220. 1,
  221. ImageFormat.Unknown);
  222. var nameSuffix = meta.CbufSlot < 0 ? $"_tcb_{meta.Handle:X}" : $"_cb{meta.CbufSlot}_{meta.Handle:X}";
  223. var sampledImageType = context.TypeSampledImage(imageType);
  224. var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageType);
  225. var sampledImageVariable = context.Variable(sampledImagePointerType, StorageClass.UniformConstant);
  226. context.Samplers.Add(meta, (imageType, sampledImageType, sampledImageVariable));
  227. context.SamplersTypes.Add(meta, descriptor.Type);
  228. context.Name(sampledImageVariable, $"{GetStagePrefix(context.Config.Stage)}_tex{nameSuffix}");
  229. context.Decorate(sampledImageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
  230. context.Decorate(sampledImageVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
  231. context.AddGlobalVariable(sampledImageVariable);
  232. }
  233. }
  234. private static void DeclareImages(CodeGenContext context, TextureDescriptor[] descriptors)
  235. {
  236. foreach (var descriptor in descriptors)
  237. {
  238. var meta = new TextureMeta(descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Format);
  239. if (context.Images.ContainsKey(meta))
  240. {
  241. continue;
  242. }
  243. int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 3 : 0;
  244. var dim = GetDim(descriptor.Type);
  245. var imageType = context.TypeImage(
  246. context.GetType(meta.Format.GetComponentType()),
  247. dim,
  248. descriptor.Type.HasFlag(SamplerType.Shadow),
  249. descriptor.Type.HasFlag(SamplerType.Array),
  250. descriptor.Type.HasFlag(SamplerType.Multisample),
  251. AccessQualifier.ReadWrite,
  252. GetImageFormat(meta.Format));
  253. var nameSuffix = meta.CbufSlot < 0 ?
  254. $"_tcb_{meta.Handle:X}_{meta.Format.ToGlslFormat()}" :
  255. $"_cb{meta.CbufSlot}_{meta.Handle:X}_{meta.Format.ToGlslFormat()}";
  256. var imagePointerType = context.TypePointer(StorageClass.UniformConstant, imageType);
  257. var imageVariable = context.Variable(imagePointerType, StorageClass.UniformConstant);
  258. context.Images.Add(meta, (imageType, imageVariable));
  259. context.Name(imageVariable, $"{GetStagePrefix(context.Config.Stage)}_img{nameSuffix}");
  260. context.Decorate(imageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
  261. context.Decorate(imageVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
  262. if (descriptor.Flags.HasFlag(TextureUsageFlags.ImageCoherent))
  263. {
  264. context.Decorate(imageVariable, Decoration.Coherent);
  265. }
  266. context.AddGlobalVariable(imageVariable);
  267. }
  268. }
  269. private static Dim GetDim(SamplerType type)
  270. {
  271. return (type & SamplerType.Mask) switch
  272. {
  273. SamplerType.Texture1D => Dim.Dim1D,
  274. SamplerType.Texture2D => Dim.Dim2D,
  275. SamplerType.Texture3D => Dim.Dim3D,
  276. SamplerType.TextureCube => Dim.Cube,
  277. SamplerType.TextureBuffer => Dim.Buffer,
  278. _ => throw new ArgumentException($"Invalid sampler type \"{type & SamplerType.Mask}\".")
  279. };
  280. }
  281. private static ImageFormat GetImageFormat(TextureFormat format)
  282. {
  283. return format switch
  284. {
  285. TextureFormat.Unknown => ImageFormat.Unknown,
  286. TextureFormat.R8Unorm => ImageFormat.R8,
  287. TextureFormat.R8Snorm => ImageFormat.R8Snorm,
  288. TextureFormat.R8Uint => ImageFormat.R8ui,
  289. TextureFormat.R8Sint => ImageFormat.R8i,
  290. TextureFormat.R16Float => ImageFormat.R16f,
  291. TextureFormat.R16Unorm => ImageFormat.R16,
  292. TextureFormat.R16Snorm => ImageFormat.R16Snorm,
  293. TextureFormat.R16Uint => ImageFormat.R16ui,
  294. TextureFormat.R16Sint => ImageFormat.R16i,
  295. TextureFormat.R32Float => ImageFormat.R32f,
  296. TextureFormat.R32Uint => ImageFormat.R32ui,
  297. TextureFormat.R32Sint => ImageFormat.R32i,
  298. TextureFormat.R8G8Unorm => ImageFormat.Rg8,
  299. TextureFormat.R8G8Snorm => ImageFormat.Rg8Snorm,
  300. TextureFormat.R8G8Uint => ImageFormat.Rg8ui,
  301. TextureFormat.R8G8Sint => ImageFormat.Rg8i,
  302. TextureFormat.R16G16Float => ImageFormat.Rg16f,
  303. TextureFormat.R16G16Unorm => ImageFormat.Rg16,
  304. TextureFormat.R16G16Snorm => ImageFormat.Rg16Snorm,
  305. TextureFormat.R16G16Uint => ImageFormat.Rg16ui,
  306. TextureFormat.R16G16Sint => ImageFormat.Rg16i,
  307. TextureFormat.R32G32Float => ImageFormat.Rg32f,
  308. TextureFormat.R32G32Uint => ImageFormat.Rg32ui,
  309. TextureFormat.R32G32Sint => ImageFormat.Rg32i,
  310. TextureFormat.R8G8B8A8Unorm => ImageFormat.Rgba8,
  311. TextureFormat.R8G8B8A8Snorm => ImageFormat.Rgba8Snorm,
  312. TextureFormat.R8G8B8A8Uint => ImageFormat.Rgba8ui,
  313. TextureFormat.R8G8B8A8Sint => ImageFormat.Rgba8i,
  314. TextureFormat.R16G16B16A16Float => ImageFormat.Rgba16f,
  315. TextureFormat.R16G16B16A16Unorm => ImageFormat.Rgba16,
  316. TextureFormat.R16G16B16A16Snorm => ImageFormat.Rgba16Snorm,
  317. TextureFormat.R16G16B16A16Uint => ImageFormat.Rgba16ui,
  318. TextureFormat.R16G16B16A16Sint => ImageFormat.Rgba16i,
  319. TextureFormat.R32G32B32A32Float => ImageFormat.Rgba32f,
  320. TextureFormat.R32G32B32A32Uint => ImageFormat.Rgba32ui,
  321. TextureFormat.R32G32B32A32Sint => ImageFormat.Rgba32i,
  322. TextureFormat.R10G10B10A2Unorm => ImageFormat.Rgb10A2,
  323. TextureFormat.R10G10B10A2Uint => ImageFormat.Rgb10a2ui,
  324. TextureFormat.R11G11B10Float => ImageFormat.R11fG11fB10f,
  325. _ => throw new ArgumentException($"Invalid texture format \"{format}\".")
  326. };
  327. }
  328. private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
  329. {
  330. bool iaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing);
  331. if (iaIndexing && !perPatch)
  332. {
  333. var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
  334. attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
  335. if (context.Config.Stage == ShaderStage.Geometry)
  336. {
  337. attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)context.InputVertices));
  338. }
  339. var spvType = context.TypePointer(StorageClass.Input, attrType);
  340. var spvVar = context.Variable(spvType, StorageClass.Input);
  341. if (context.Config.PassthroughAttributes != 0 && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
  342. {
  343. context.Decorate(spvVar, Decoration.PassthroughNV);
  344. }
  345. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0);
  346. context.AddGlobalVariable(spvVar);
  347. context.InputsArray = spvVar;
  348. }
  349. var inputs = perPatch ? info.InputsPerPatch : info.Inputs;
  350. foreach (int attr in inputs)
  351. {
  352. if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false, perPatch))
  353. {
  354. continue;
  355. }
  356. bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
  357. if (iaIndexing && isUserAttr && !perPatch)
  358. {
  359. continue;
  360. }
  361. PixelImap iq = PixelImap.Unused;
  362. if (context.Config.Stage == ShaderStage.Fragment)
  363. {
  364. if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
  365. {
  366. iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType();
  367. }
  368. else
  369. {
  370. AttributeInfo attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr: false);
  371. AggregateType elemType = attrInfo.Type & AggregateType.ElementTypeMask;
  372. if (attrInfo.IsBuiltin && (elemType == AggregateType.S32 || elemType == AggregateType.U32))
  373. {
  374. iq = PixelImap.Constant;
  375. }
  376. }
  377. }
  378. DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq);
  379. }
  380. }
  381. private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
  382. {
  383. bool oaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing);
  384. if (oaIndexing && !perPatch)
  385. {
  386. var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
  387. attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
  388. if (context.Config.Stage == ShaderStage.TessellationControl)
  389. {
  390. attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
  391. }
  392. var spvType = context.TypePointer(StorageClass.Output, attrType);
  393. var spvVar = context.Variable(spvType, StorageClass.Output);
  394. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0);
  395. context.AddGlobalVariable(spvVar);
  396. context.OutputsArray = spvVar;
  397. }
  398. var outputs = perPatch ? info.OutputsPerPatch : info.Outputs;
  399. foreach (int attr in outputs)
  400. {
  401. if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch))
  402. {
  403. continue;
  404. }
  405. bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
  406. if (oaIndexing && isUserAttr && !perPatch)
  407. {
  408. continue;
  409. }
  410. DeclareOutputAttribute(context, attr, perPatch);
  411. }
  412. if (context.Config.Stage == ShaderStage.Vertex)
  413. {
  414. DeclareOutputAttribute(context, AttributeConsts.PositionX, perPatch: false);
  415. }
  416. }
  417. private static void DeclareOutputAttribute(CodeGenContext context, int attr, bool perPatch)
  418. {
  419. DeclareInputOrOutput(context, attr, perPatch, isOutAttr: true);
  420. }
  421. public static void DeclareInvocationId(CodeGenContext context)
  422. {
  423. DeclareInputOrOutput(context, AttributeConsts.LaneId, perPatch: false, isOutAttr: false);
  424. }
  425. private static void DeclareInputOrOutput(CodeGenContext context, int attr, bool perPatch, bool isOutAttr, PixelImap iq = PixelImap.Unused)
  426. {
  427. bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
  428. if (isUserAttr && context.Config.TransformFeedbackEnabled && !perPatch &&
  429. ((isOutAttr && context.Config.LastInVertexPipeline) ||
  430. (!isOutAttr && context.Config.Stage == ShaderStage.Fragment)))
  431. {
  432. DeclareTransformFeedbackInputOrOutput(context, attr, isOutAttr, iq);
  433. return;
  434. }
  435. var dict = perPatch
  436. ? (isOutAttr ? context.OutputsPerPatch : context.InputsPerPatch)
  437. : (isOutAttr ? context.Outputs : context.Inputs);
  438. var attrInfo = perPatch
  439. ? AttributeInfo.FromPatch(context.Config, attr, isOutAttr)
  440. : AttributeInfo.From(context.Config, attr, isOutAttr);
  441. if (dict.ContainsKey(attrInfo.BaseValue))
  442. {
  443. return;
  444. }
  445. var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
  446. var attrType = context.GetType(attrInfo.Type, attrInfo.Length);
  447. bool builtInPassthrough = false;
  448. if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && !perPatch && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)))
  449. {
  450. int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32;
  451. attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
  452. if (context.Config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
  453. {
  454. builtInPassthrough = true;
  455. }
  456. }
  457. if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr && !perPatch)
  458. {
  459. attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
  460. }
  461. var spvType = context.TypePointer(storageClass, attrType);
  462. var spvVar = context.Variable(spvType, storageClass);
  463. if (builtInPassthrough)
  464. {
  465. context.Decorate(spvVar, Decoration.PassthroughNV);
  466. }
  467. if (attrInfo.IsBuiltin)
  468. {
  469. if (perPatch)
  470. {
  471. context.Decorate(spvVar, Decoration.Patch);
  472. }
  473. if (context.Config.GpuAccessor.QueryHostReducedPrecision() && attr == AttributeConsts.PositionX && context.Config.Stage != ShaderStage.Fragment)
  474. {
  475. context.Decorate(spvVar, Decoration.Invariant);
  476. }
  477. context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue));
  478. if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr)
  479. {
  480. var tfOutput = context.Info.GetTransformFeedbackOutput(attrInfo.BaseValue);
  481. if (tfOutput.Valid)
  482. {
  483. context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer);
  484. context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)tfOutput.Stride);
  485. context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)tfOutput.Offset);
  486. }
  487. }
  488. }
  489. else if (perPatch)
  490. {
  491. context.Decorate(spvVar, Decoration.Patch);
  492. int location = context.Config.GetPerPatchAttributeLocation((attr - AttributeConsts.UserAttributePerPatchBase) / 16);
  493. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
  494. }
  495. else if (isUserAttr)
  496. {
  497. int location = (attr - AttributeConsts.UserAttributeBase) / 16;
  498. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
  499. if (!isOutAttr &&
  500. !perPatch &&
  501. (context.Config.PassthroughAttributes & (1 << location)) != 0 &&
  502. context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
  503. {
  504. context.Decorate(spvVar, Decoration.PassthroughNV);
  505. }
  506. }
  507. else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
  508. {
  509. int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16;
  510. if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
  511. {
  512. int firstLocation = BitOperations.TrailingZeroCount(context.Config.UsedOutputAttributes);
  513. int index = location - firstLocation;
  514. int mask = 3 << firstLocation;
  515. if ((uint)index < 2 && (context.Config.UsedOutputAttributes & mask) == mask)
  516. {
  517. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)firstLocation);
  518. context.Decorate(spvVar, Decoration.Index, (LiteralInteger)index);
  519. }
  520. else
  521. {
  522. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
  523. }
  524. }
  525. else
  526. {
  527. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
  528. }
  529. }
  530. if (!isOutAttr)
  531. {
  532. switch (iq)
  533. {
  534. case PixelImap.Constant:
  535. context.Decorate(spvVar, Decoration.Flat);
  536. break;
  537. case PixelImap.ScreenLinear:
  538. context.Decorate(spvVar, Decoration.NoPerspective);
  539. break;
  540. }
  541. }
  542. context.AddGlobalVariable(spvVar);
  543. dict.Add(attrInfo.BaseValue, spvVar);
  544. }
  545. private static void DeclareTransformFeedbackInputOrOutput(CodeGenContext context, int attr, bool isOutAttr, PixelImap iq = PixelImap.Unused)
  546. {
  547. var dict = isOutAttr ? context.Outputs : context.Inputs;
  548. var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr);
  549. bool hasComponent = true;
  550. int component = (attr >> 2) & 3;
  551. int components = 1;
  552. var type = attrInfo.Type & AggregateType.ElementTypeMask;
  553. if (context.Config.LastInPipeline && isOutAttr)
  554. {
  555. components = context.Info.GetTransformFeedbackOutputComponents(attr);
  556. if (components > 1)
  557. {
  558. attr &= ~0xf;
  559. type = components switch
  560. {
  561. 2 => AggregateType.Vector2 | AggregateType.FP32,
  562. 3 => AggregateType.Vector3 | AggregateType.FP32,
  563. 4 => AggregateType.Vector4 | AggregateType.FP32,
  564. _ => AggregateType.FP32
  565. };
  566. hasComponent = false;
  567. }
  568. }
  569. if (dict.ContainsKey(attr))
  570. {
  571. return;
  572. }
  573. var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
  574. var attrType = context.GetType(type, components);
  575. if (AttributeInfo.IsArrayAttributeSpirv(context.Config.Stage, isOutAttr) && (!attrInfo.IsBuiltin || AttributeInfo.IsArrayBuiltIn(attr)))
  576. {
  577. int arraySize = context.Config.Stage == ShaderStage.Geometry ? context.InputVertices : 32;
  578. attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
  579. }
  580. if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr)
  581. {
  582. attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
  583. }
  584. var spvType = context.TypePointer(storageClass, attrType);
  585. var spvVar = context.Variable(spvType, storageClass);
  586. Debug.Assert(attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd);
  587. int location = (attr - AttributeConsts.UserAttributeBase) / 16;
  588. context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
  589. if (hasComponent)
  590. {
  591. context.Decorate(spvVar, Decoration.Component, (LiteralInteger)component);
  592. }
  593. if (isOutAttr)
  594. {
  595. var tfOutput = context.Info.GetTransformFeedbackOutput(attr);
  596. if (tfOutput.Valid)
  597. {
  598. context.Decorate(spvVar, Decoration.XfbBuffer, (LiteralInteger)tfOutput.Buffer);
  599. context.Decorate(spvVar, Decoration.XfbStride, (LiteralInteger)tfOutput.Stride);
  600. context.Decorate(spvVar, Decoration.Offset, (LiteralInteger)tfOutput.Offset);
  601. }
  602. }
  603. else
  604. {
  605. if ((context.Config.PassthroughAttributes & (1 << location)) != 0 &&
  606. context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
  607. {
  608. context.Decorate(spvVar, Decoration.PassthroughNV);
  609. }
  610. switch (iq)
  611. {
  612. case PixelImap.Constant:
  613. context.Decorate(spvVar, Decoration.Flat);
  614. break;
  615. case PixelImap.ScreenLinear:
  616. context.Decorate(spvVar, Decoration.NoPerspective);
  617. break;
  618. }
  619. }
  620. context.AddGlobalVariable(spvVar);
  621. dict.Add(attr, spvVar);
  622. }
  623. private static BuiltIn GetBuiltIn(CodeGenContext context, int attr)
  624. {
  625. return attr switch
  626. {
  627. AttributeConsts.TessLevelOuter0 => BuiltIn.TessLevelOuter,
  628. AttributeConsts.TessLevelInner0 => BuiltIn.TessLevelInner,
  629. AttributeConsts.Layer => BuiltIn.Layer,
  630. AttributeConsts.ViewportIndex => BuiltIn.ViewportIndex,
  631. AttributeConsts.PointSize => BuiltIn.PointSize,
  632. AttributeConsts.PositionX => context.Config.Stage == ShaderStage.Fragment ? BuiltIn.FragCoord : BuiltIn.Position,
  633. AttributeConsts.ClipDistance0 => BuiltIn.ClipDistance,
  634. AttributeConsts.PointCoordX => BuiltIn.PointCoord,
  635. AttributeConsts.TessCoordX => BuiltIn.TessCoord,
  636. AttributeConsts.InstanceId => BuiltIn.InstanceId,
  637. AttributeConsts.VertexId => BuiltIn.VertexId,
  638. AttributeConsts.BaseInstance => BuiltIn.BaseInstance,
  639. AttributeConsts.BaseVertex => BuiltIn.BaseVertex,
  640. AttributeConsts.InstanceIndex => BuiltIn.InstanceIndex,
  641. AttributeConsts.VertexIndex => BuiltIn.VertexIndex,
  642. AttributeConsts.DrawIndex => BuiltIn.DrawIndex,
  643. AttributeConsts.FrontFacing => BuiltIn.FrontFacing,
  644. AttributeConsts.FragmentOutputDepth => BuiltIn.FragDepth,
  645. AttributeConsts.ThreadKill => BuiltIn.HelperInvocation,
  646. AttributeConsts.ThreadIdX => BuiltIn.LocalInvocationId,
  647. AttributeConsts.CtaIdX => BuiltIn.WorkgroupId,
  648. AttributeConsts.LaneId => BuiltIn.SubgroupLocalInvocationId,
  649. AttributeConsts.InvocationId => BuiltIn.InvocationId,
  650. AttributeConsts.PrimitiveId => BuiltIn.PrimitiveId,
  651. AttributeConsts.PatchVerticesIn => BuiltIn.PatchVertices,
  652. AttributeConsts.EqMask => BuiltIn.SubgroupEqMask,
  653. AttributeConsts.GeMask => BuiltIn.SubgroupGeMask,
  654. AttributeConsts.GtMask => BuiltIn.SubgroupGtMask,
  655. AttributeConsts.LeMask => BuiltIn.SubgroupLeMask,
  656. AttributeConsts.LtMask => BuiltIn.SubgroupLtMask,
  657. AttributeConsts.SupportBlockViewInverseX => BuiltIn.Position,
  658. AttributeConsts.SupportBlockViewInverseY => BuiltIn.Position,
  659. _ => throw new ArgumentException($"Invalid attribute number 0x{attr:X}.")
  660. };
  661. }
  662. private static string GetStagePrefix(ShaderStage stage)
  663. {
  664. return StagePrefixes[(int)stage];
  665. }
  666. }
  667. }