ShaderInfoBuilder.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. using Ryujinx.Graphics.GAL;
  2. using Ryujinx.Graphics.Shader;
  3. using System.Collections.Generic;
  4. namespace Ryujinx.Graphics.Gpu.Shader
  5. {
  6. /// <summary>
  7. /// Shader info structure builder.
  8. /// </summary>
  9. class ShaderInfoBuilder
  10. {
  11. private const int TotalSets = 4;
  12. private const int UniformSetIndex = 0;
  13. private const int StorageSetIndex = 1;
  14. private const int TextureSetIndex = 2;
  15. private const int ImageSetIndex = 3;
  16. private const ResourceStages SupportBufferStages =
  17. ResourceStages.Compute |
  18. ResourceStages.Vertex |
  19. ResourceStages.Fragment;
  20. private const ResourceStages VtgStages =
  21. ResourceStages.Vertex |
  22. ResourceStages.TessellationControl |
  23. ResourceStages.TessellationEvaluation |
  24. ResourceStages.Geometry;
  25. private readonly GpuContext _context;
  26. private int _fragmentOutputMap;
  27. private readonly int _reservedConstantBuffers;
  28. private readonly int _reservedStorageBuffers;
  29. private readonly int _reservedTextures;
  30. private readonly int _reservedImages;
  31. private readonly List<ResourceDescriptor>[] _resourceDescriptors;
  32. private readonly List<ResourceUsage>[] _resourceUsages;
  33. /// <summary>
  34. /// Creates a new shader info builder.
  35. /// </summary>
  36. /// <param name="context">GPU context that owns the shaders that will be added to the builder</param>
  37. /// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
  38. /// <param name="vertexAsCompute">Indicates that the vertex shader will be emulated on a compute shader</param>
  39. public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false)
  40. {
  41. _context = context;
  42. _fragmentOutputMap = -1;
  43. _resourceDescriptors = new List<ResourceDescriptor>[TotalSets];
  44. _resourceUsages = new List<ResourceUsage>[TotalSets];
  45. for (int index = 0; index < TotalSets; index++)
  46. {
  47. _resourceDescriptors[index] = new();
  48. _resourceUsages[index] = new();
  49. }
  50. AddDescriptor(SupportBufferStages, ResourceType.UniformBuffer, UniformSetIndex, 0, 1);
  51. AddUsage(SupportBufferStages, ResourceType.UniformBuffer, UniformSetIndex, 0, 1);
  52. ResourceReservationCounts rrc = new(!context.Capabilities.SupportsTransformFeedback && tfEnabled, vertexAsCompute);
  53. _reservedConstantBuffers = rrc.ReservedConstantBuffers;
  54. _reservedStorageBuffers = rrc.ReservedStorageBuffers;
  55. _reservedTextures = rrc.ReservedTextures;
  56. _reservedImages = rrc.ReservedImages;
  57. // TODO: Handle that better? Maybe we should only set the binding that are really needed on each shader.
  58. ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages;
  59. PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, UniformSetIndex, 1, rrc.ReservedConstantBuffers - 1);
  60. PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, StorageSetIndex, 0, rrc.ReservedStorageBuffers);
  61. PopulateDescriptorAndUsages(stages, ResourceType.BufferTexture, TextureSetIndex, 0, rrc.ReservedTextures);
  62. PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, ImageSetIndex, 0, rrc.ReservedImages);
  63. }
  64. private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count)
  65. {
  66. AddDescriptor(stages, type, setIndex, start, count);
  67. AddUsage(stages, type, setIndex, start, count);
  68. }
  69. /// <summary>
  70. /// Adds information from a given shader stage.
  71. /// </summary>
  72. /// <param name="info">Shader stage information</param>
  73. /// <param name="vertexAsCompute">True if the shader stage has been converted into a compute shader</param>
  74. public void AddStageInfo(ShaderProgramInfo info, bool vertexAsCompute = false)
  75. {
  76. if (info.Stage == ShaderStage.Fragment)
  77. {
  78. _fragmentOutputMap = info.FragmentOutputMap;
  79. }
  80. int stageIndex = GpuAccessorBase.GetStageIndex(info.Stage switch
  81. {
  82. ShaderStage.TessellationControl => 1,
  83. ShaderStage.TessellationEvaluation => 2,
  84. ShaderStage.Geometry => 3,
  85. ShaderStage.Fragment => 4,
  86. _ => 0,
  87. });
  88. ResourceStages stages = vertexAsCompute ? ResourceStages.Compute : info.Stage switch
  89. {
  90. ShaderStage.Compute => ResourceStages.Compute,
  91. ShaderStage.Vertex => ResourceStages.Vertex,
  92. ShaderStage.TessellationControl => ResourceStages.TessellationControl,
  93. ShaderStage.TessellationEvaluation => ResourceStages.TessellationEvaluation,
  94. ShaderStage.Geometry => ResourceStages.Geometry,
  95. ShaderStage.Fragment => ResourceStages.Fragment,
  96. _ => ResourceStages.None,
  97. };
  98. int uniformsPerStage = (int)_context.Capabilities.MaximumUniformBuffersPerStage;
  99. int storagesPerStage = (int)_context.Capabilities.MaximumStorageBuffersPerStage;
  100. int texturesPerStage = (int)_context.Capabilities.MaximumTexturesPerStage;
  101. int imagesPerStage = (int)_context.Capabilities.MaximumImagesPerStage;
  102. int uniformBinding = _reservedConstantBuffers + stageIndex * uniformsPerStage;
  103. int storageBinding = _reservedStorageBuffers + stageIndex * storagesPerStage;
  104. int textureBinding = _reservedTextures + stageIndex * texturesPerStage * 2;
  105. int imageBinding = _reservedImages + stageIndex * imagesPerStage * 2;
  106. AddDescriptor(stages, ResourceType.UniformBuffer, UniformSetIndex, uniformBinding, uniformsPerStage);
  107. AddDescriptor(stages, ResourceType.StorageBuffer, StorageSetIndex, storageBinding, storagesPerStage);
  108. AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, TextureSetIndex, textureBinding, texturesPerStage);
  109. AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, ImageSetIndex, imageBinding, imagesPerStage);
  110. AddUsage(info.CBuffers, stages, UniformSetIndex, isStorage: false);
  111. AddUsage(info.SBuffers, stages, StorageSetIndex, isStorage: true);
  112. AddUsage(info.Textures, stages, TextureSetIndex, isImage: false);
  113. AddUsage(info.Images, stages, ImageSetIndex, isImage: true);
  114. }
  115. /// <summary>
  116. /// Adds a resource descriptor to the list of descriptors.
  117. /// </summary>
  118. /// <param name="stages">Shader stages where the resource is used</param>
  119. /// <param name="type">Type of the resource</param>
  120. /// <param name="setIndex">Descriptor set number where the resource will be bound</param>
  121. /// <param name="binding">Binding number where the resource will be bound</param>
  122. /// <param name="count">Number of resources bound at the binding location</param>
  123. private void AddDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
  124. {
  125. for (int index = 0; index < count; index++)
  126. {
  127. _resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding + index, 1, type, stages));
  128. }
  129. }
  130. /// <summary>
  131. /// Adds two interleaved groups of resources to the list of descriptors.
  132. /// </summary>
  133. /// <param name="stages">Shader stages where the resource is used</param>
  134. /// <param name="type">Type of the first interleaved resource</param>
  135. /// <param name="type2">Type of the second interleaved resource</param>
  136. /// <param name="setIndex">Descriptor set number where the resource will be bound</param>
  137. /// <param name="binding">Binding number where the resource will be bound</param>
  138. /// <param name="count">Number of resources bound at the binding location</param>
  139. private void AddDualDescriptor(ResourceStages stages, ResourceType type, ResourceType type2, int setIndex, int binding, int count)
  140. {
  141. AddDescriptor(stages, type, setIndex, binding, count);
  142. AddDescriptor(stages, type2, setIndex, binding + count, count);
  143. }
  144. /// <summary>
  145. /// Adds buffer usage information to the list of usages.
  146. /// </summary>
  147. /// <param name="stages">Shader stages where the resource is used</param>
  148. /// <param name="type">Type of the resource</param>
  149. /// <param name="setIndex">Descriptor set number where the resource will be bound</param>
  150. /// <param name="binding">Binding number where the resource will be bound</param>
  151. /// <param name="count">Number of resources bound at the binding location</param>
  152. private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
  153. {
  154. for (int index = 0; index < count; index++)
  155. {
  156. _resourceUsages[setIndex].Add(new ResourceUsage(binding + index, type, stages));
  157. }
  158. }
  159. /// <summary>
  160. /// Adds buffer usage information to the list of usages.
  161. /// </summary>
  162. /// <param name="buffers">Buffers to be added</param>
  163. /// <param name="stages">Stages where the buffers are used</param>
  164. /// <param name="setIndex">Descriptor set index where the buffers will be bound</param>
  165. /// <param name="isStorage">True for storage buffers, false for uniform buffers</param>
  166. private void AddUsage(IEnumerable<BufferDescriptor> buffers, ResourceStages stages, int setIndex, bool isStorage)
  167. {
  168. foreach (BufferDescriptor buffer in buffers)
  169. {
  170. _resourceUsages[setIndex].Add(new ResourceUsage(
  171. buffer.Binding,
  172. isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer,
  173. stages));
  174. }
  175. }
  176. /// <summary>
  177. /// Adds texture usage information to the list of usages.
  178. /// </summary>
  179. /// <param name="textures">Textures to be added</param>
  180. /// <param name="stages">Stages where the textures are used</param>
  181. /// <param name="setIndex">Descriptor set index where the textures will be bound</param>
  182. /// <param name="isImage">True for images, false for textures</param>
  183. private void AddUsage(IEnumerable<TextureDescriptor> textures, ResourceStages stages, int setIndex, bool isImage)
  184. {
  185. foreach (TextureDescriptor texture in textures)
  186. {
  187. bool isBuffer = (texture.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
  188. ResourceType type = isBuffer
  189. ? (isImage ? ResourceType.BufferImage : ResourceType.BufferTexture)
  190. : (isImage ? ResourceType.Image : ResourceType.TextureAndSampler);
  191. _resourceUsages[setIndex].Add(new ResourceUsage(
  192. texture.Binding,
  193. type,
  194. stages));
  195. }
  196. }
  197. /// <summary>
  198. /// Creates a new shader information structure from the added information.
  199. /// </summary>
  200. /// <param name="pipeline">Optional pipeline state for background shader compilation</param>
  201. /// <param name="fromCache">Indicates if the shader comes from a disk cache</param>
  202. /// <returns>Shader information</returns>
  203. public ShaderInfo Build(ProgramPipelineState? pipeline, bool fromCache = false)
  204. {
  205. var descriptors = new ResourceDescriptorCollection[TotalSets];
  206. var usages = new ResourceUsageCollection[TotalSets];
  207. for (int index = 0; index < TotalSets; index++)
  208. {
  209. descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly());
  210. usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly());
  211. }
  212. ResourceLayout resourceLayout = new(descriptors.AsReadOnly(), usages.AsReadOnly());
  213. if (pipeline.HasValue)
  214. {
  215. return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache);
  216. }
  217. else
  218. {
  219. return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache);
  220. }
  221. }
  222. /// <summary>
  223. /// Builds shader information for shaders from the disk cache.
  224. /// </summary>
  225. /// <param name="context">GPU context that owns the shaders</param>
  226. /// <param name="programs">Shaders from the disk cache</param>
  227. /// <param name="pipeline">Optional pipeline for background compilation</param>
  228. /// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
  229. /// <returns>Shader information</returns>
  230. public static ShaderInfo BuildForCache(
  231. GpuContext context,
  232. IEnumerable<CachedShaderStage> programs,
  233. ProgramPipelineState? pipeline,
  234. bool tfEnabled)
  235. {
  236. ShaderInfoBuilder builder = new(context, tfEnabled);
  237. foreach (CachedShaderStage program in programs)
  238. {
  239. if (program?.Info != null)
  240. {
  241. builder.AddStageInfo(program.Info);
  242. }
  243. }
  244. return builder.Build(pipeline, fromCache: true);
  245. }
  246. /// <summary>
  247. /// Builds shader information for a compute shader.
  248. /// </summary>
  249. /// <param name="context">GPU context that owns the shader</param>
  250. /// <param name="info">Compute shader information</param>
  251. /// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
  252. /// <returns>Shader information</returns>
  253. public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false)
  254. {
  255. ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false);
  256. builder.AddStageInfo(info);
  257. return builder.Build(null, fromCache);
  258. }
  259. /// <summary>
  260. /// Builds shader information for a vertex or geometry shader thas was converted to compute shader.
  261. /// </summary>
  262. /// <param name="context">GPU context that owns the shader</param>
  263. /// <param name="info">Compute shader information</param>
  264. /// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
  265. /// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
  266. /// <returns>Shader information</returns>
  267. public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, bool tfEnabled, bool fromCache = false)
  268. {
  269. ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true);
  270. builder.AddStageInfo(info, vertexAsCompute: true);
  271. return builder.Build(null, fromCache);
  272. }
  273. }
  274. }