ShaderInfoBuilder.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. using Ryujinx.Graphics.GAL;
  2. using Ryujinx.Graphics.Shader;
  3. using System;
  4. using System.Collections.Generic;
  5. namespace Ryujinx.Graphics.Gpu.Shader
  6. {
  7. /// <summary>
  8. /// Shader info structure builder.
  9. /// </summary>
  10. class ShaderInfoBuilder
  11. {
  12. private const ResourceStages SupportBufferStages =
  13. ResourceStages.Compute |
  14. ResourceStages.Vertex |
  15. ResourceStages.Fragment;
  16. private const ResourceStages VtgStages =
  17. ResourceStages.Vertex |
  18. ResourceStages.TessellationControl |
  19. ResourceStages.TessellationEvaluation |
  20. ResourceStages.Geometry;
  21. private readonly GpuContext _context;
  22. private int _fragmentOutputMap;
  23. private readonly int _reservedConstantBuffers;
  24. private readonly int _reservedStorageBuffers;
  25. private readonly int _reservedTextures;
  26. private readonly int _reservedImages;
  27. private List<ResourceDescriptor>[] _resourceDescriptors;
  28. private List<ResourceUsage>[] _resourceUsages;
  29. /// <summary>
  30. /// Creates a new shader info builder.
  31. /// </summary>
  32. /// <param name="context">GPU context that owns the shaders that will be added to the builder</param>
  33. /// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
  34. /// <param name="vertexAsCompute">Indicates that the vertex shader will be emulated on a compute shader</param>
  35. public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false)
  36. {
  37. _context = context;
  38. _fragmentOutputMap = -1;
  39. int uniformSetIndex = context.Capabilities.UniformBufferSetIndex;
  40. int storageSetIndex = context.Capabilities.StorageBufferSetIndex;
  41. int textureSetIndex = context.Capabilities.TextureSetIndex;
  42. int imageSetIndex = context.Capabilities.ImageSetIndex;
  43. int totalSets = Math.Max(uniformSetIndex, storageSetIndex);
  44. totalSets = Math.Max(totalSets, textureSetIndex);
  45. totalSets = Math.Max(totalSets, imageSetIndex);
  46. totalSets++;
  47. _resourceDescriptors = new List<ResourceDescriptor>[totalSets];
  48. _resourceUsages = new List<ResourceUsage>[totalSets];
  49. for (int index = 0; index < totalSets; index++)
  50. {
  51. _resourceDescriptors[index] = new();
  52. _resourceUsages[index] = new();
  53. }
  54. AddDescriptor(SupportBufferStages, ResourceType.UniformBuffer, uniformSetIndex, 0, 1);
  55. AddUsage(SupportBufferStages, ResourceType.UniformBuffer, uniformSetIndex, 0, 1);
  56. ResourceReservationCounts rrc = new(!context.Capabilities.SupportsTransformFeedback && tfEnabled, vertexAsCompute);
  57. _reservedConstantBuffers = rrc.ReservedConstantBuffers;
  58. _reservedStorageBuffers = rrc.ReservedStorageBuffers;
  59. _reservedTextures = rrc.ReservedTextures;
  60. _reservedImages = rrc.ReservedImages;
  61. // TODO: Handle that better? Maybe we should only set the binding that are really needed on each shader.
  62. ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages;
  63. PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, uniformSetIndex, 1, rrc.ReservedConstantBuffers - 1);
  64. PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers);
  65. PopulateDescriptorAndUsages(stages, ResourceType.BufferTexture, textureSetIndex, 0, rrc.ReservedTextures);
  66. PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages);
  67. }
  68. /// <summary>
  69. /// Populates descriptors and usages for vertex as compute and transform feedback emulation reserved resources.
  70. /// </summary>
  71. /// <param name="stages">Shader stages where the resources are used</param>
  72. /// <param name="type">Resource type</param>
  73. /// <param name="setIndex">Resource set index where the resources are used</param>
  74. /// <param name="start">First binding number</param>
  75. /// <param name="count">Amount of bindings</param>
  76. private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count)
  77. {
  78. AddDescriptor(stages, type, setIndex, start, count);
  79. AddUsage(stages, type, setIndex, start, count);
  80. }
  81. /// <summary>
  82. /// Adds information from a given shader stage.
  83. /// </summary>
  84. /// <param name="info">Shader stage information</param>
  85. /// <param name="vertexAsCompute">True if the shader stage has been converted into a compute shader</param>
  86. public void AddStageInfo(ShaderProgramInfo info, bool vertexAsCompute = false)
  87. {
  88. if (info.Stage == ShaderStage.Fragment)
  89. {
  90. _fragmentOutputMap = info.FragmentOutputMap;
  91. }
  92. int stageIndex = GpuAccessorBase.GetStageIndex(info.Stage switch
  93. {
  94. ShaderStage.TessellationControl => 1,
  95. ShaderStage.TessellationEvaluation => 2,
  96. ShaderStage.Geometry => 3,
  97. ShaderStage.Fragment => 4,
  98. _ => 0,
  99. });
  100. ResourceStages stages = vertexAsCompute ? ResourceStages.Compute : info.Stage switch
  101. {
  102. ShaderStage.Compute => ResourceStages.Compute,
  103. ShaderStage.Vertex => ResourceStages.Vertex,
  104. ShaderStage.TessellationControl => ResourceStages.TessellationControl,
  105. ShaderStage.TessellationEvaluation => ResourceStages.TessellationEvaluation,
  106. ShaderStage.Geometry => ResourceStages.Geometry,
  107. ShaderStage.Fragment => ResourceStages.Fragment,
  108. _ => ResourceStages.None,
  109. };
  110. int uniformsPerStage = (int)_context.Capabilities.MaximumUniformBuffersPerStage;
  111. int storagesPerStage = (int)_context.Capabilities.MaximumStorageBuffersPerStage;
  112. int texturesPerStage = (int)_context.Capabilities.MaximumTexturesPerStage;
  113. int imagesPerStage = (int)_context.Capabilities.MaximumImagesPerStage;
  114. int uniformBinding = _reservedConstantBuffers + stageIndex * uniformsPerStage;
  115. int storageBinding = _reservedStorageBuffers + stageIndex * storagesPerStage;
  116. int textureBinding = _reservedTextures + stageIndex * texturesPerStage * 2;
  117. int imageBinding = _reservedImages + stageIndex * imagesPerStage * 2;
  118. int uniformSetIndex = _context.Capabilities.UniformBufferSetIndex;
  119. int storageSetIndex = _context.Capabilities.StorageBufferSetIndex;
  120. int textureSetIndex = _context.Capabilities.TextureSetIndex;
  121. int imageSetIndex = _context.Capabilities.ImageSetIndex;
  122. AddDescriptor(stages, ResourceType.UniformBuffer, uniformSetIndex, uniformBinding, uniformsPerStage);
  123. AddDescriptor(stages, ResourceType.StorageBuffer, storageSetIndex, storageBinding, storagesPerStage);
  124. AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, textureSetIndex, textureBinding, texturesPerStage);
  125. AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, imageSetIndex, imageBinding, imagesPerStage);
  126. AddArrayDescriptors(info.Textures, stages, isImage: false);
  127. AddArrayDescriptors(info.Images, stages, isImage: true);
  128. AddUsage(info.CBuffers, stages, isStorage: false);
  129. AddUsage(info.SBuffers, stages, isStorage: true);
  130. AddUsage(info.Textures, stages, isImage: false);
  131. AddUsage(info.Images, stages, isImage: true);
  132. }
  133. /// <summary>
  134. /// Adds a resource descriptor to the list of descriptors.
  135. /// </summary>
  136. /// <param name="stages">Shader stages where the resource is used</param>
  137. /// <param name="type">Type of the resource</param>
  138. /// <param name="setIndex">Descriptor set number where the resource will be bound</param>
  139. /// <param name="binding">Binding number where the resource will be bound</param>
  140. /// <param name="count">Number of resources bound at the binding location</param>
  141. private void AddDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
  142. {
  143. for (int index = 0; index < count; index++)
  144. {
  145. _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding + index, 1, type, stages));
  146. }
  147. }
  148. /// <summary>
  149. /// Adds two interleaved groups of resources to the list of descriptors.
  150. /// </summary>
  151. /// <param name="stages">Shader stages where the resource is used</param>
  152. /// <param name="type">Type of the first interleaved resource</param>
  153. /// <param name="type2">Type of the second interleaved resource</param>
  154. /// <param name="setIndex">Descriptor set number where the resource will be bound</param>
  155. /// <param name="binding">Binding number where the resource will be bound</param>
  156. /// <param name="count">Number of resources bound at the binding location</param>
  157. private void AddDualDescriptor(ResourceStages stages, ResourceType type, ResourceType type2, int setIndex, int binding, int count)
  158. {
  159. AddDescriptor(stages, type, setIndex, binding, count);
  160. AddDescriptor(stages, type2, setIndex, binding + count, count);
  161. }
  162. /// <summary>
  163. /// Adds all array descriptors (those with an array length greater than one).
  164. /// </summary>
  165. /// <param name="textures">Textures to be added</param>
  166. /// <param name="stages">Stages where the textures are used</param>
  167. /// <param name="isImage">True for images, false for textures</param>
  168. private void AddArrayDescriptors(IEnumerable<TextureDescriptor> textures, ResourceStages stages, bool isImage)
  169. {
  170. foreach (TextureDescriptor texture in textures)
  171. {
  172. if (texture.ArrayLength > 1)
  173. {
  174. ResourceType type = GetTextureResourceType(texture, isImage);
  175. GetDescriptors(texture.Set).Add(new ResourceDescriptor(texture.Binding, texture.ArrayLength, type, stages));
  176. }
  177. }
  178. }
  179. /// <summary>
  180. /// Adds buffer usage information to the list of usages.
  181. /// </summary>
  182. /// <param name="stages">Shader stages where the resource is used</param>
  183. /// <param name="type">Type of the resource</param>
  184. /// <param name="setIndex">Descriptor set number where the resource will be bound</param>
  185. /// <param name="binding">Binding number where the resource will be bound</param>
  186. /// <param name="count">Number of resources bound at the binding location</param>
  187. private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
  188. {
  189. for (int index = 0; index < count; index++)
  190. {
  191. _resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages));
  192. }
  193. }
  194. /// <summary>
  195. /// Adds buffer usage information to the list of usages.
  196. /// </summary>
  197. /// <param name="buffers">Buffers to be added</param>
  198. /// <param name="stages">Stages where the buffers are used</param>
  199. /// <param name="isStorage">True for storage buffers, false for uniform buffers</param>
  200. private void AddUsage(IEnumerable<BufferDescriptor> buffers, ResourceStages stages, bool isStorage)
  201. {
  202. foreach (BufferDescriptor buffer in buffers)
  203. {
  204. GetUsages(buffer.Set).Add(new ResourceUsage(
  205. buffer.Binding,
  206. 1,
  207. isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer,
  208. stages));
  209. }
  210. }
  211. /// <summary>
  212. /// Adds texture usage information to the list of usages.
  213. /// </summary>
  214. /// <param name="textures">Textures to be added</param>
  215. /// <param name="stages">Stages where the textures are used</param>
  216. /// <param name="isImage">True for images, false for textures</param>
  217. private void AddUsage(IEnumerable<TextureDescriptor> textures, ResourceStages stages, bool isImage)
  218. {
  219. foreach (TextureDescriptor texture in textures)
  220. {
  221. ResourceType type = GetTextureResourceType(texture, isImage);
  222. GetUsages(texture.Set).Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages));
  223. }
  224. }
  225. /// <summary>
  226. /// Gets the list of resource descriptors for a given set index. A new list will be created if needed.
  227. /// </summary>
  228. /// <param name="setIndex">Resource set index</param>
  229. /// <returns>List of resource descriptors</returns>
  230. private List<ResourceDescriptor> GetDescriptors(int setIndex)
  231. {
  232. if (_resourceDescriptors.Length <= setIndex)
  233. {
  234. int oldLength = _resourceDescriptors.Length;
  235. Array.Resize(ref _resourceDescriptors, setIndex + 1);
  236. for (int index = oldLength; index <= setIndex; index++)
  237. {
  238. _resourceDescriptors[index] = new();
  239. }
  240. }
  241. return _resourceDescriptors[setIndex];
  242. }
  243. /// <summary>
  244. /// Gets the list of resource usages for a given set index. A new list will be created if needed.
  245. /// </summary>
  246. /// <param name="setIndex">Resource set index</param>
  247. /// <returns>List of resource usages</returns>
  248. private List<ResourceUsage> GetUsages(int setIndex)
  249. {
  250. if (_resourceUsages.Length <= setIndex)
  251. {
  252. int oldLength = _resourceUsages.Length;
  253. Array.Resize(ref _resourceUsages, setIndex + 1);
  254. for (int index = oldLength; index <= setIndex; index++)
  255. {
  256. _resourceUsages[index] = new();
  257. }
  258. }
  259. return _resourceUsages[setIndex];
  260. }
  261. /// <summary>
  262. /// Gets a resource type from a texture descriptor.
  263. /// </summary>
  264. /// <param name="texture">Texture descriptor</param>
  265. /// <param name="isImage">Whether the texture is a image texture (writable) or not (sampled)</param>
  266. /// <returns>Resource type</returns>
  267. private static ResourceType GetTextureResourceType(TextureDescriptor texture, bool isImage)
  268. {
  269. bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
  270. if (isBuffer)
  271. {
  272. return isImage ? ResourceType.BufferImage : ResourceType.BufferTexture;
  273. }
  274. else if (isImage)
  275. {
  276. return ResourceType.Image;
  277. }
  278. else if (texture.Type == SamplerType.None)
  279. {
  280. return ResourceType.Sampler;
  281. }
  282. else if (texture.Separate)
  283. {
  284. return ResourceType.Texture;
  285. }
  286. else
  287. {
  288. return ResourceType.TextureAndSampler;
  289. }
  290. }
  291. /// <summary>
  292. /// Creates a new shader information structure from the added information.
  293. /// </summary>
  294. /// <param name="pipeline">Optional pipeline state for background shader compilation</param>
  295. /// <param name="fromCache">Indicates if the shader comes from a disk cache</param>
  296. /// <returns>Shader information</returns>
  297. public ShaderInfo Build(ProgramPipelineState? pipeline, bool fromCache = false)
  298. {
  299. int totalSets = _resourceDescriptors.Length;
  300. var descriptors = new ResourceDescriptorCollection[totalSets];
  301. var usages = new ResourceUsageCollection[totalSets];
  302. for (int index = 0; index < totalSets; index++)
  303. {
  304. descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly());
  305. usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly());
  306. }
  307. ResourceLayout resourceLayout = new(descriptors.AsReadOnly(), usages.AsReadOnly());
  308. if (pipeline.HasValue)
  309. {
  310. return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache);
  311. }
  312. else
  313. {
  314. return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache);
  315. }
  316. }
  317. /// <summary>
  318. /// Builds shader information for shaders from the disk cache.
  319. /// </summary>
  320. /// <param name="context">GPU context that owns the shaders</param>
  321. /// <param name="programs">Shaders from the disk cache</param>
  322. /// <param name="pipeline">Optional pipeline for background compilation</param>
  323. /// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
  324. /// <returns>Shader information</returns>
  325. public static ShaderInfo BuildForCache(
  326. GpuContext context,
  327. IEnumerable<CachedShaderStage> programs,
  328. ProgramPipelineState? pipeline,
  329. bool tfEnabled)
  330. {
  331. ShaderInfoBuilder builder = new(context, tfEnabled);
  332. foreach (CachedShaderStage program in programs)
  333. {
  334. if (program?.Info != null)
  335. {
  336. builder.AddStageInfo(program.Info);
  337. }
  338. }
  339. return builder.Build(pipeline, fromCache: true);
  340. }
  341. /// <summary>
  342. /// Builds shader information for a compute shader.
  343. /// </summary>
  344. /// <param name="context">GPU context that owns the shader</param>
  345. /// <param name="info">Compute shader information</param>
  346. /// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
  347. /// <returns>Shader information</returns>
  348. public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false)
  349. {
  350. ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false);
  351. builder.AddStageInfo(info);
  352. return builder.Build(null, fromCache);
  353. }
  354. /// <summary>
  355. /// Builds shader information for a vertex or geometry shader thas was converted to compute shader.
  356. /// </summary>
  357. /// <param name="context">GPU context that owns the shader</param>
  358. /// <param name="info">Compute shader information</param>
  359. /// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
  360. /// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
  361. /// <returns>Shader information</returns>
  362. public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, bool tfEnabled, bool fromCache = false)
  363. {
  364. ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true);
  365. builder.AddStageInfo(info, vertexAsCompute: true);
  366. return builder.Build(null, fromCache);
  367. }
  368. }
  369. }