/ src / Ryujinx.Graphics.Gpu / Shader / GpuAccessorBase.cs
GpuAccessorBase.cs
  1  using Ryujinx.Common.Logging;
  2  using Ryujinx.Graphics.GAL;
  3  using Ryujinx.Graphics.Gpu.Image;
  4  using Ryujinx.Graphics.Shader;
  5  using Ryujinx.Graphics.Shader.Translation;
  6  
  7  namespace Ryujinx.Graphics.Gpu.Shader
  8  {
  9      /// <summary>
 10      /// GPU accessor.
 11      /// </summary>
 12      class GpuAccessorBase
 13      {
 14          private readonly GpuContext _context;
 15          private readonly ResourceCounts _resourceCounts;
 16          private readonly int _stageIndex;
 17  
 18          private int _reservedConstantBuffers;
 19          private int _reservedStorageBuffers;
 20          private int _reservedTextures;
 21          private int _reservedImages;
 22  
 23          private int _staticTexturesCount;
 24          private int _staticImagesCount;
 25  
 26          /// <summary>
 27          /// Creates a new GPU accessor.
 28          /// </summary>
 29          /// <param name="context">GPU context</param>
 30          /// <param name="resourceCounts">Counter of GPU resources used by the shader</param>
 31          /// <param name="stageIndex">Index of the shader stage, 0 for compute</param>
 32          public GpuAccessorBase(GpuContext context, ResourceCounts resourceCounts, int stageIndex)
 33          {
 34              _context = context;
 35              _resourceCounts = resourceCounts;
 36              _stageIndex = stageIndex;
 37          }
 38  
 39          /// <summary>
 40          /// Initializes counts for bindings that will be reserved for emulator use.
 41          /// </summary>
 42          /// <param name="tfEnabled">Indicates if the current graphics shader is used with transform feedback enabled</param>
 43          /// <param name="vertexAsCompute">Indicates that the vertex shader will be emulated on a compute shader</param>
 44          public void InitializeReservedCounts(bool tfEnabled, bool vertexAsCompute)
 45          {
 46              ResourceReservationCounts rrc = new(!_context.Capabilities.SupportsTransformFeedback && tfEnabled, vertexAsCompute);
 47  
 48              _reservedConstantBuffers = rrc.ReservedConstantBuffers;
 49              _reservedStorageBuffers = rrc.ReservedStorageBuffers;
 50              _reservedTextures = rrc.ReservedTextures;
 51              _reservedImages = rrc.ReservedImages;
 52          }
 53  
 54          public SetBindingPair CreateConstantBufferBinding(int index)
 55          {
 56              int binding;
 57  
 58              if (_context.Capabilities.Api == TargetApi.Vulkan)
 59              {
 60                  binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer");
 61              }
 62              else
 63              {
 64                  binding = _resourceCounts.UniformBuffersCount++;
 65              }
 66  
 67              return new SetBindingPair(_context.Capabilities.UniformBufferSetIndex, binding + _reservedConstantBuffers);
 68          }
 69  
 70          public SetBindingPair CreateImageBinding(int count, bool isBuffer)
 71          {
 72              int binding;
 73  
 74              if (_context.Capabilities.Api == TargetApi.Vulkan)
 75              {
 76                  if (count == 1)
 77                  {
 78                      int index = _staticImagesCount++;
 79  
 80                      if (isBuffer)
 81                      {
 82                          index += (int)_context.Capabilities.MaximumImagesPerStage;
 83                      }
 84  
 85                      binding = GetBindingFromIndex(index, _context.Capabilities.MaximumImagesPerStage * 2, "Image");
 86                  }
 87                  else
 88                  {
 89                      binding = (int)GetDynamicBaseIndexDual(_context.Capabilities.MaximumImagesPerStage) + _resourceCounts.ImagesCount++;
 90                  }
 91              }
 92              else
 93              {
 94                  binding = _resourceCounts.ImagesCount;
 95  
 96                  _resourceCounts.ImagesCount += count;
 97              }
 98  
 99              return new SetBindingPair(_context.Capabilities.ImageSetIndex, binding + _reservedImages);
100          }
101  
102          public SetBindingPair CreateStorageBufferBinding(int index)
103          {
104              int binding;
105  
106              if (_context.Capabilities.Api == TargetApi.Vulkan)
107              {
108                  binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer");
109              }
110              else
111              {
112                  binding = _resourceCounts.StorageBuffersCount++;
113              }
114  
115              return new SetBindingPair(_context.Capabilities.StorageBufferSetIndex, binding + _reservedStorageBuffers);
116          }
117  
118          public SetBindingPair CreateTextureBinding(int count, bool isBuffer)
119          {
120              int binding;
121  
122              if (_context.Capabilities.Api == TargetApi.Vulkan)
123              {
124                  if (count == 1)
125                  {
126                      int index = _staticTexturesCount++;
127  
128                      if (isBuffer)
129                      {
130                          index += (int)_context.Capabilities.MaximumTexturesPerStage;
131                      }
132  
133                      binding = GetBindingFromIndex(index, _context.Capabilities.MaximumTexturesPerStage * 2, "Texture");
134                  }
135                  else
136                  {
137                      binding = (int)GetDynamicBaseIndexDual(_context.Capabilities.MaximumTexturesPerStage) + _resourceCounts.TexturesCount++;
138                  }
139              }
140              else
141              {
142                  binding = _resourceCounts.TexturesCount;
143  
144                  _resourceCounts.TexturesCount += count;
145              }
146  
147              return new SetBindingPair(_context.Capabilities.TextureSetIndex, binding + _reservedTextures);
148          }
149  
150          private int GetBindingFromIndex(int index, uint maxPerStage, string resourceName)
151          {
152              if ((uint)index >= maxPerStage)
153              {
154                  Logger.Error?.Print(LogClass.Gpu, $"{resourceName} index {index} exceeds per stage limit of {maxPerStage}.");
155              }
156  
157              return GetStageIndex(_stageIndex) * (int)maxPerStage + index;
158          }
159  
160          public static int GetStageIndex(int stageIndex)
161          {
162              // This is just a simple remapping to ensure that most frequently used shader stages
163              // have the lowest binding numbers.
164              // This is useful because if we need to run on a system with a low limit on the bindings,
165              // then we can still get most games working as the most common shaders will have low binding numbers.
166              return stageIndex switch
167              {
168                  4 => 1, // Fragment
169                  3 => 2, // Geometry
170                  1 => 3, // Tessellation control
171                  2 => 4, // Tessellation evaluation
172                  _ => 0, // Vertex/Compute
173              };
174          }
175  
176          private static uint GetDynamicBaseIndexDual(uint maxPerStage)
177          {
178              return GetDynamicBaseIndex(maxPerStage) * 2;
179          }
180  
181          private static uint GetDynamicBaseIndex(uint maxPerStage)
182          {
183              return maxPerStage * Constants.ShaderStages;
184          }
185  
186          public int CreateExtraSet()
187          {
188              if (_resourceCounts.SetsCount >= _context.Capabilities.MaximumExtraSets)
189              {
190                  return -1;
191              }
192  
193              return _context.Capabilities.ExtraSetBaseIndex + _resourceCounts.SetsCount++;
194          }
195  
196          public int QueryHostGatherBiasPrecision() => _context.Capabilities.GatherBiasPrecision;
197  
198          public bool QueryHostReducedPrecision() => _context.Capabilities.ReduceShaderPrecision;
199  
200          public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug;
201  
202          public bool QueryHostHasVectorIndexingBug() => _context.Capabilities.HasVectorIndexingBug;
203  
204          public int QueryHostStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment;
205  
206          public int QueryHostSubgroupSize() => _context.Capabilities.ShaderSubgroupSize;
207  
208          public bool QueryHostSupportsBgraFormat() => _context.Capabilities.SupportsBgraFormat;
209  
210          public bool QueryHostSupportsFragmentShaderInterlock() => _context.Capabilities.SupportsFragmentShaderInterlock;
211  
212          public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel;
213  
214          public bool QueryHostSupportsGeometryShader() => _context.Capabilities.SupportsGeometryShader;
215  
216          public bool QueryHostSupportsGeometryShaderPassthrough() => _context.Capabilities.SupportsGeometryShaderPassthrough;
217  
218          public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
219  
220          public bool QueryHostSupportsLayerVertexTessellation() => _context.Capabilities.SupportsLayerVertexTessellation;
221  
222          public bool QueryHostSupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset;
223  
224          public bool QueryHostSupportsScaledVertexFormats() => _context.Capabilities.SupportsScaledVertexFormats;
225  
226          public bool QueryHostSupportsSeparateSampler() => _context.Capabilities.SupportsSeparateSampler;
227  
228          public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot;
229  
230          public bool QueryHostSupportsShaderBarrierDivergence() => _context.Capabilities.SupportsShaderBarrierDivergence;
231  
232          public bool QueryHostSupportsShaderFloat64() => _context.Capabilities.SupportsShaderFloat64;
233  
234          public bool QueryHostSupportsSnormBufferTextureFormat() => _context.Capabilities.SupportsSnormBufferTextureFormat;
235  
236          public bool QueryHostSupportsTextureGatherOffsets() => _context.Capabilities.SupportsTextureGatherOffsets;
237  
238          public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
239  
240          public bool QueryHostSupportsTransformFeedback() => _context.Capabilities.SupportsTransformFeedback;
241  
242          public bool QueryHostSupportsViewportIndexVertexTessellation() => _context.Capabilities.SupportsViewportIndexVertexTessellation;
243  
244          public bool QueryHostSupportsViewportMask() => _context.Capabilities.SupportsViewportMask;
245  
246          public bool QueryHostSupportsDepthClipControl() => _context.Capabilities.SupportsDepthClipControl;
247  
248          /// <summary>
249          /// Converts a packed Maxwell texture format to the shader translator texture format.
250          /// </summary>
251          /// <param name="format">Packed maxwell format</param>
252          /// <param name="formatSrgb">Indicates if the format is sRGB</param>
253          /// <returns>Shader translator texture format</returns>
254          protected static TextureFormat ConvertToTextureFormat(uint format, bool formatSrgb)
255          {
256              if (!FormatTable.TryGetTextureFormat(format, formatSrgb, out FormatInfo formatInfo))
257              {
258                  return TextureFormat.Unknown;
259              }
260  
261              return formatInfo.Format switch
262              {
263  #pragma warning disable IDE0055 // Disable formatting
264                  Format.R8Unorm           => TextureFormat.R8Unorm,
265                  Format.R8Snorm           => TextureFormat.R8Snorm,
266                  Format.R8Uint            => TextureFormat.R8Uint,
267                  Format.R8Sint            => TextureFormat.R8Sint,
268                  Format.R16Float          => TextureFormat.R16Float,
269                  Format.R16Unorm          => TextureFormat.R16Unorm,
270                  Format.R16Snorm          => TextureFormat.R16Snorm,
271                  Format.R16Uint           => TextureFormat.R16Uint,
272                  Format.R16Sint           => TextureFormat.R16Sint,
273                  Format.R32Float          => TextureFormat.R32Float,
274                  Format.R32Uint           => TextureFormat.R32Uint,
275                  Format.R32Sint           => TextureFormat.R32Sint,
276                  Format.R8G8Unorm         => TextureFormat.R8G8Unorm,
277                  Format.R8G8Snorm         => TextureFormat.R8G8Snorm,
278                  Format.R8G8Uint          => TextureFormat.R8G8Uint,
279                  Format.R8G8Sint          => TextureFormat.R8G8Sint,
280                  Format.R16G16Float       => TextureFormat.R16G16Float,
281                  Format.R16G16Unorm       => TextureFormat.R16G16Unorm,
282                  Format.R16G16Snorm       => TextureFormat.R16G16Snorm,
283                  Format.R16G16Uint        => TextureFormat.R16G16Uint,
284                  Format.R16G16Sint        => TextureFormat.R16G16Sint,
285                  Format.R32G32Float       => TextureFormat.R32G32Float,
286                  Format.R32G32Uint        => TextureFormat.R32G32Uint,
287                  Format.R32G32Sint        => TextureFormat.R32G32Sint,
288                  Format.R8G8B8A8Unorm     => TextureFormat.R8G8B8A8Unorm,
289                  Format.R8G8B8A8Snorm     => TextureFormat.R8G8B8A8Snorm,
290                  Format.R8G8B8A8Uint      => TextureFormat.R8G8B8A8Uint,
291                  Format.R8G8B8A8Sint      => TextureFormat.R8G8B8A8Sint,
292                  Format.R8G8B8A8Srgb      => TextureFormat.R8G8B8A8Unorm,
293                  Format.R16G16B16A16Float => TextureFormat.R16G16B16A16Float,
294                  Format.R16G16B16A16Unorm => TextureFormat.R16G16B16A16Unorm,
295                  Format.R16G16B16A16Snorm => TextureFormat.R16G16B16A16Snorm,
296                  Format.R16G16B16A16Uint  => TextureFormat.R16G16B16A16Uint,
297                  Format.R16G16B16A16Sint  => TextureFormat.R16G16B16A16Sint,
298                  Format.R32G32B32A32Float => TextureFormat.R32G32B32A32Float,
299                  Format.R32G32B32A32Uint  => TextureFormat.R32G32B32A32Uint,
300                  Format.R32G32B32A32Sint  => TextureFormat.R32G32B32A32Sint,
301                  Format.R10G10B10A2Unorm  => TextureFormat.R10G10B10A2Unorm,
302                  Format.R10G10B10A2Uint   => TextureFormat.R10G10B10A2Uint,
303                  Format.R11G11B10Float    => TextureFormat.R11G11B10Float,
304                  _                        => TextureFormat.Unknown,
305  #pragma warning restore IDE0055
306              };
307          }
308      }
309  }