/ src / Ryujinx.Graphics.Gpu / Engine / Threed / SpecializationStateUpdater.cs
SpecializationStateUpdater.cs
  1  using Ryujinx.Common.Memory;
  2  using Ryujinx.Graphics.GAL;
  3  using Ryujinx.Graphics.Gpu.Engine.Types;
  4  using Ryujinx.Graphics.Gpu.Shader;
  5  using Ryujinx.Graphics.Shader;
  6  
  7  namespace Ryujinx.Graphics.Gpu.Engine.Threed
  8  {
  9      /// <summary>
 10      /// Maintains a "current" specialiation state, and provides a flag to check if it has changed meaningfully.
 11      /// </summary>
 12      internal class SpecializationStateUpdater
 13      {
 14          private readonly GpuContext _context;
 15          private GpuChannelGraphicsState _graphics;
 16          private GpuChannelPoolState _pool;
 17  
 18          private bool _usesDrawParameters;
 19          private bool _usesTopology;
 20  
 21          private bool _changed;
 22  
 23          /// <summary>
 24          /// Creates a new instance of the specialization state updater class.
 25          /// </summary>
 26          /// <param name="context">GPU context</param>
 27          public SpecializationStateUpdater(GpuContext context)
 28          {
 29              _context = context;
 30          }
 31  
 32          /// <summary>
 33          /// Signal that the specialization state has changed.
 34          /// </summary>
 35          private void Signal()
 36          {
 37              _changed = true;
 38          }
 39  
 40          /// <summary>
 41          /// Checks if the specialization state has changed since the last check.
 42          /// </summary>
 43          /// <returns>True if it has changed, false otherwise</returns>
 44          public bool HasChanged()
 45          {
 46              if (_changed)
 47              {
 48                  _changed = false;
 49                  return true;
 50              }
 51              else
 52              {
 53                  return false;
 54              }
 55          }
 56  
 57          /// <summary>
 58          /// Sets the active shader, clearing the dirty state and recording if certain specializations are noteworthy.
 59          /// </summary>
 60          /// <param name="gs">The active shader</param>
 61          public void SetShader(CachedShaderProgram gs)
 62          {
 63              _usesDrawParameters = gs.Shaders[1]?.Info.UsesDrawParameters ?? false;
 64              _usesTopology = gs.SpecializationState.IsPrimitiveTopologyQueried();
 65  
 66              _changed = false;
 67          }
 68  
 69          /// <summary>
 70          /// Get the current graphics state.
 71          /// </summary>
 72          /// <returns>GPU graphics state</returns>
 73          public ref GpuChannelGraphicsState GetGraphicsState()
 74          {
 75              return ref _graphics;
 76          }
 77  
 78          /// <summary>
 79          /// Get the current pool state.
 80          /// </summary>
 81          /// <returns>GPU pool state</returns>
 82          public ref GpuChannelPoolState GetPoolState()
 83          {
 84              return ref _pool;
 85          }
 86  
 87          /// <summary>
 88          /// Early Z force enable.
 89          /// </summary>
 90          /// <param name="value">The new value</param>
 91          public void SetEarlyZForce(bool value)
 92          {
 93              _graphics.EarlyZForce = value;
 94  
 95              Signal();
 96          }
 97  
 98          /// <summary>
 99          /// Primitive topology of current draw.
100          /// </summary>
101          /// <param name="value">The new value</param>
102          public void SetTopology(PrimitiveTopology value)
103          {
104              if (value != _graphics.Topology)
105              {
106                  _graphics.Topology = value;
107  
108                  if (_usesTopology)
109                  {
110                      Signal();
111                  }
112              }
113          }
114  
115          /// <summary>
116          /// Tessellation mode.
117          /// </summary>
118          /// <param name="value">The new value</param>
119          public void SetTessellationMode(TessMode value)
120          {
121              if (value.Packed != _graphics.TessellationMode.Packed)
122              {
123                  _graphics.TessellationMode = value;
124  
125                  Signal();
126              }
127          }
128  
129          /// <summary>
130          /// Updates alpha-to-coverage state, and sets it as changed.
131          /// </summary>
132          /// <param name="enable">Whether alpha-to-coverage is enabled</param>
133          /// <param name="ditherEnable">Whether alpha-to-coverage dithering is enabled</param>
134          public void SetAlphaToCoverageEnable(bool enable, bool ditherEnable)
135          {
136              _graphics.AlphaToCoverageEnable = enable;
137              _graphics.AlphaToCoverageDitherEnable = ditherEnable;
138  
139              Signal();
140          }
141  
142          /// <summary>
143          /// Indicates whether the viewport transform is disabled.
144          /// </summary>
145          /// <param name="value">The new value</param>
146          public void SetViewportTransformDisable(bool value)
147          {
148              if (value != _graphics.ViewportTransformDisable)
149              {
150                  _graphics.ViewportTransformDisable = value;
151  
152                  Signal();
153              }
154          }
155  
156          /// <summary>
157          /// Depth mode zero to one or minus one to one.
158          /// </summary>
159          /// <param name="value">The new value</param>
160          public void SetDepthMode(bool value)
161          {
162              if (value != _graphics.DepthMode)
163              {
164                  _graphics.DepthMode = value;
165  
166                  Signal();
167              }
168          }
169  
170          /// <summary>
171          /// Indicates if the point size is set on the shader or is fixed.
172          /// </summary>
173          /// <param name="value">The new value</param>
174          public void SetProgramPointSizeEnable(bool value)
175          {
176              if (value != _graphics.ProgramPointSizeEnable)
177              {
178                  _graphics.ProgramPointSizeEnable = value;
179  
180                  Signal();
181              }
182          }
183  
184          /// <summary>
185          /// Point size used if <see cref="SetProgramPointSizeEnable" /> is provided false.
186          /// </summary>
187          /// <param name="value">The new value</param>
188          public void SetPointSize(float value)
189          {
190              if (value != _graphics.PointSize)
191              {
192                  _graphics.PointSize = value;
193  
194                  Signal();
195              }
196          }
197  
198          /// <summary>
199          /// Updates alpha test specialization state, and sets it as changed.
200          /// </summary>
201          /// <param name="enable">Whether alpha test is enabled</param>
202          /// <param name="reference">The value to compare with the fragment output alpha</param>
203          /// <param name="op">The comparison that decides if the fragment should be discarded</param>
204          public void SetAlphaTest(bool enable, float reference, CompareOp op)
205          {
206              _graphics.AlphaTestEnable = enable;
207              _graphics.AlphaTestReference = reference;
208              _graphics.AlphaTestCompare = op;
209  
210              Signal();
211          }
212  
213          /// <summary>
214          /// Updates the type of the vertex attributes consumed by the shader.
215          /// </summary>
216          /// <param name="state">The new state</param>
217          public void SetAttributeTypes(ref Array32<VertexAttribState> state)
218          {
219              bool changed = false;
220              ref Array32<AttributeType> attributeTypes = ref _graphics.AttributeTypes;
221              bool mayConvertVtgToCompute = ShaderCache.MayConvertVtgToCompute(ref _context.Capabilities);
222              bool supportsScaledFormats = _context.Capabilities.SupportsScaledVertexFormats && !mayConvertVtgToCompute;
223  
224              for (int location = 0; location < state.Length; location++)
225              {
226                  VertexAttribType type = state[location].UnpackType();
227                  VertexAttribSize size = state[location].UnpackSize();
228  
229                  AttributeType value;
230  
231                  if (supportsScaledFormats)
232                  {
233                      value = type switch
234                      {
235                          VertexAttribType.Sint => AttributeType.Sint,
236                          VertexAttribType.Uint => AttributeType.Uint,
237                          _ => AttributeType.Float,
238                      };
239                  }
240                  else
241                  {
242                      value = type switch
243                      {
244                          VertexAttribType.Sint => AttributeType.Sint,
245                          VertexAttribType.Uint => AttributeType.Uint,
246                          VertexAttribType.Uscaled => AttributeType.Uscaled,
247                          VertexAttribType.Sscaled => AttributeType.Sscaled,
248                          _ => AttributeType.Float,
249                      };
250                  }
251  
252                  if (mayConvertVtgToCompute && (size == VertexAttribSize.Rgb10A2 || size == VertexAttribSize.Rg11B10))
253                  {
254                      value |= AttributeType.Packed;
255  
256                      if (type == VertexAttribType.Snorm ||
257                          type == VertexAttribType.Sint ||
258                          type == VertexAttribType.Sscaled)
259                      {
260                          value |= AttributeType.PackedRgb10A2Signed;
261                      }
262                  }
263  
264                  if (attributeTypes[location] != value)
265                  {
266                      attributeTypes[location] = value;
267                      changed = true;
268                  }
269              }
270  
271              if (changed)
272              {
273                  Signal();
274              }
275          }
276  
277          /// <summary>
278          /// Updates the type of the outputs produced by the fragment shader based on the current render target state.
279          /// </summary>
280          /// <param name="rtControl">The render target control register</param>
281          /// <param name="state">The color attachment state</param>
282          public void SetFragmentOutputTypes(RtControl rtControl, ref Array8<RtColorState> state)
283          {
284              bool changed = false;
285              int count = rtControl.UnpackCount();
286  
287              for (int index = 0; index < Constants.TotalRenderTargets; index++)
288              {
289                  int rtIndex = rtControl.UnpackPermutationIndex(index);
290  
291                  var colorState = state[rtIndex];
292  
293                  if (index < count && StateUpdater.IsRtEnabled(colorState))
294                  {
295                      Format format = colorState.Format.Convert().Format;
296  
297                      AttributeType type = format.IsInteger() ? (format.IsSint() ? AttributeType.Sint : AttributeType.Uint) : AttributeType.Float;
298  
299                      if (type != _graphics.FragmentOutputTypes[index])
300                      {
301                          _graphics.FragmentOutputTypes[index] = type;
302                          changed = true;
303                      }
304                  }
305              }
306  
307              if (changed && _context.Capabilities.NeedsFragmentOutputSpecialization)
308              {
309                  Signal();
310              }
311          }
312  
313          /// <summary>
314          /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
315          /// </summary>
316          /// <param name="value">The new value</param>
317          public void SetHasConstantBufferDrawParameters(bool value)
318          {
319              if (value != _graphics.HasConstantBufferDrawParameters)
320              {
321                  _graphics.HasConstantBufferDrawParameters = value;
322  
323                  if (_usesDrawParameters)
324                  {
325                      Signal();
326                  }
327              }
328          }
329  
330          /// <summary>
331          /// Indicates that any storage buffer use is unaligned.
332          /// </summary>
333          /// <param name="value">The new value</param>
334          /// <returns>True if the unaligned state changed, false otherwise</returns>
335          public bool SetHasUnalignedStorageBuffer(bool value)
336          {
337              if (value != _graphics.HasUnalignedStorageBuffer)
338              {
339                  _graphics.HasUnalignedStorageBuffer = value;
340  
341                  Signal();
342  
343                  return true;
344              }
345  
346              return false;
347          }
348  
349          /// <summary>
350          /// Sets the GPU pool state.
351          /// </summary>
352          /// <param name="state">The new state</param>
353          public void SetPoolState(GpuChannelPoolState state)
354          {
355              if (!state.Equals(_pool))
356              {
357                  _pool = state;
358  
359                  Signal();
360              }
361          }
362  
363          /// <summary>
364          /// Sets the dual-source blend enabled state.
365          /// </summary>
366          /// <param name="enabled">True if blending is enabled and using dual-source blend</param>
367          public void SetDualSourceBlendEnabled(bool enabled)
368          {
369              if (enabled != _graphics.DualSourceBlendEnable)
370              {
371                  _graphics.DualSourceBlendEnable = enabled;
372  
373                  Signal();
374              }
375          }
376  
377          /// <summary>
378          /// Sets the Y negate enabled state.
379          /// </summary>
380          /// <param name="enabled">True if Y negate of the fragment coordinates is enabled</param>
381          public void SetYNegateEnabled(bool enabled)
382          {
383              if (enabled != _graphics.YNegateEnabled)
384              {
385                  _graphics.YNegateEnabled = enabled;
386  
387                  Signal();
388              }
389          }
390      }
391  }