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 }