From 9ac66336a216644a1b671e7949702d2e749838d2 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 4 Dec 2022 17:41:17 +0000 Subject: [PATCH] GPU: Use lazy checks for specialization state (#4004) * GPU: Use lazy checks for specialization state This PR adds a new class, the SpecializationStateUpdater, that allows elements of specialization state to be updated individually, and signal the state is checked when it changes between draws, instead of building and checking it on every draw. This also avoids building spec state when Most state updates have been moved behind the shader state update, so that their specialization state updates make it in before shaders are fetched. Downside: Fields in GpuChannelGraphicsState are no longer readonly. To counteract copies that might be caused this I pass it as `ref` when possible, though maybe `in` would be better? Not really sure about the quirks of `in` and the difference probably won't show on a benchmark. The result is around 2 extra FPS on SMO in the usual spot. Not much right now, but it will remove costs when we're doing more expensive specialization checks, such as fragment output type specialization for macos. It may also help more on other games with more draws. * Address Feedback * Oops --- .../Engine/Threed/DrawManager.cs | 13 +- .../Threed/SpecializationStateUpdater.cs | 280 ++++++++++++++++++ .../Engine/Threed/StateUpdater.cs | 138 +++++---- .../Engine/Threed/ThreedClass.cs | 5 +- .../Shader/GpuChannelGraphicsState.cs | 28 +- .../Shader/GpuChannelPoolState.cs | 16 +- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 16 +- .../Shader/ShaderCacheHashTable.cs | 6 +- .../Shader/ShaderSpecializationList.cs | 8 +- .../Shader/ShaderSpecializationState.cs | 21 +- 10 files changed, 417 insertions(+), 114 deletions(-) create mode 100644 Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index ed8ed2064..5a659f555 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private readonly GpuChannel _channel; private readonly DeviceStateWithShadow _state; private readonly DrawState _drawState; + private readonly SpecializationStateUpdater _currentSpecState; private bool _topologySet; private bool _instancedDrawPending; @@ -44,12 +45,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// GPU channel /// Channel state /// Draw state - public DrawManager(GpuContext context, GpuChannel channel, DeviceStateWithShadow state, DrawState drawState) + /// Specialization state updater + public DrawManager(GpuContext context, GpuChannel channel, DeviceStateWithShadow state, DrawState drawState, SpecializationStateUpdater spec) { _context = context; _channel = channel; _state = state; _drawState = drawState; + _currentSpecState = spec; } /// @@ -132,6 +135,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _drawState.FirstIndex = firstIndex; _drawState.IndexCount = indexCount; + _currentSpecState.SetHasConstantBufferDrawParameters(false); engine.UpdateState(); @@ -256,6 +260,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (_drawState.Topology != topology || !_topologySet) { _context.Renderer.Pipeline.SetPrimitiveTopology(topology); + _currentSpecState.SetTopology(topology); _drawState.Topology = topology; _topologySet = true; } @@ -452,7 +457,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _state.State.FirstInstance = (uint)firstInstance; _drawState.DrawIndexed = indexed; - _drawState.HasConstantBufferDrawParameters = true; + _currentSpecState.SetHasConstantBufferDrawParameters(true); engine.UpdateState(); @@ -469,7 +474,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _state.State.FirstInstance = 0; _drawState.DrawIndexed = false; - _drawState.HasConstantBufferDrawParameters = false; if (renderEnable == ConditionalRenderEnabled.Host) { @@ -527,7 +531,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _drawState.DrawIndexed = indexed; _drawState.DrawIndirect = true; - _drawState.HasConstantBufferDrawParameters = true; + _currentSpecState.SetHasConstantBufferDrawParameters(true); engine.UpdateState(); @@ -561,7 +565,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _drawState.DrawIndexed = false; _drawState.DrawIndirect = false; - _drawState.HasConstantBufferDrawParameters = false; if (renderEnable == ConditionalRenderEnabled.Host) { diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs new file mode 100644 index 000000000..9e888f506 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs @@ -0,0 +1,280 @@ +using Ryujinx.Common.Memory; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Shader; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.Gpu.Engine.Threed +{ + /// + /// Maintains a "current" specialiation state, and provides a flag to check if it has changed meaningfully. + /// + internal class SpecializationStateUpdater + { + private GpuChannelGraphicsState _graphics; + private GpuChannelPoolState _pool; + + private bool _usesDrawParameters; + private bool _usesTopology; + + private bool _changed; + + /// + /// Signal that the specialization state has changed. + /// + private void Signal() + { + _changed = true; + } + + /// + /// Checks if the specialization state has changed since the last check. + /// + /// True if it has changed, false otherwise + public bool HasChanged() + { + if (_changed) + { + _changed = false; + return true; + } + else + { + return false; + } + } + + /// + /// Sets the active shader, clearing the dirty state and recording if certain specializations are noteworthy. + /// + /// The active shader + public void SetShader(CachedShaderProgram gs) + { + _usesDrawParameters = gs.Shaders[1]?.Info.UsesDrawParameters ?? false; + _usesTopology = gs.SpecializationState.IsPrimitiveTopologyQueried(); + + _changed = false; + } + + /// + /// Get the current graphics state. + /// + /// GPU graphics state + public ref GpuChannelGraphicsState GetGraphicsState() + { + return ref _graphics; + } + + /// + /// Get the current pool state. + /// + /// GPU pool state + public ref GpuChannelPoolState GetPoolState() + { + return ref _pool; + } + + /// + /// Early Z force enable. + /// + /// The new value + public void SetEarlyZForce(bool value) + { + _graphics.EarlyZForce = value; + + Signal(); + } + + /// + /// Primitive topology of current draw. + /// + /// The new value + public void SetTopology(PrimitiveTopology value) + { + if (value != _graphics.Topology) + { + _graphics.Topology = value; + + if (_usesTopology) + { + Signal(); + } + } + } + + /// + /// Tessellation mode. + /// + /// The new value + public void SetTessellationMode(TessMode value) + { + if (value.Packed != _graphics.TessellationMode.Packed) + { + _graphics.TessellationMode = value; + + Signal(); + } + } + + /// + /// Updates alpha-to-coverage state, and sets it as changed. + /// + /// Whether alpha-to-coverage is enabled + /// Whether alpha-to-coverage dithering is enabled + public void SetAlphaToCoverageEnable(bool enable, bool ditherEnable) + { + _graphics.AlphaToCoverageEnable = enable; + _graphics.AlphaToCoverageDitherEnable = ditherEnable; + + Signal(); + } + + /// + /// Indicates whether the viewport transform is disabled. + /// + /// The new value + public void SetViewportTransformDisable(bool value) + { + if (value != _graphics.ViewportTransformDisable) + { + _graphics.ViewportTransformDisable = value; + + Signal(); + } + } + + /// + /// Depth mode zero to one or minus one to one. + /// + /// The new value + public void SetDepthMode(bool value) + { + if (value != _graphics.DepthMode) + { + _graphics.DepthMode = value; + + Signal(); + } + } + + /// + /// Indicates if the point size is set on the shader or is fixed. + /// + /// The new value + public void SetProgramPointSizeEnable(bool value) + { + if (value != _graphics.ProgramPointSizeEnable) + { + _graphics.ProgramPointSizeEnable = value; + + Signal(); + } + } + + /// + /// Point size used if is provided false. + /// + /// The new value + public void SetPointSize(float value) + { + if (value != _graphics.PointSize) + { + _graphics.PointSize = value; + + Signal(); + } + } + + /// + /// Updates alpha test specialization state, and sets it as changed. + /// + /// Whether alpha test is enabled + /// The value to compare with the fragment output alpha + /// The comparison that decides if the fragment should be discarded + public void SetAlphaTest(bool enable, float reference, CompareOp op) + { + _graphics.AlphaTestEnable = enable; + _graphics.AlphaTestReference = reference; + _graphics.AlphaTestCompare = op; + + Signal(); + } + + /// + /// Updates the type of the vertex attributes consumed by the shader. + /// + /// The new state + public void SetAttributeTypes(ref Array32 state) + { + bool changed = false; + ref Array32 attributeTypes = ref _graphics.AttributeTypes; + + for (int location = 0; location < state.Length; location++) + { + VertexAttribType type = state[location].UnpackType(); + + AttributeType value = type switch + { + VertexAttribType.Sint => AttributeType.Sint, + VertexAttribType.Uint => AttributeType.Uint, + _ => AttributeType.Float + }; + + if (attributeTypes[location] != value) + { + attributeTypes[location] = value; + changed = true; + } + } + + if (changed) + { + Signal(); + } + } + + /// + /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0. + /// + /// The new value + public void SetHasConstantBufferDrawParameters(bool value) + { + if (value != _graphics.HasConstantBufferDrawParameters) + { + _graphics.HasConstantBufferDrawParameters = value; + + if (_usesDrawParameters) + { + Signal(); + } + } + } + + /// + /// Indicates that any storage buffer use is unaligned. + /// + /// The new value + public void SetHasUnalignedStorageBuffer(bool value) + { + if (value != _graphics.HasUnalignedStorageBuffer) + { + _graphics.HasUnalignedStorageBuffer = value; + + Signal(); + } + } + + /// + /// Sets the GPU pool state. + /// + /// The new state + public void SetPoolState(GpuChannelPoolState state) + { + if (!state.Equals(_pool)) + { + _pool = state; + + Signal(); + } + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index fe7e0d09f..b611f4e70 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Engine.GPFifo; using Ryujinx.Graphics.Gpu.Engine.Types; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Shader; @@ -16,9 +17,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// class StateUpdater { - public const int ShaderStateIndex = 16; + public const int ShaderStateIndex = 26; public const int RasterizerStateIndex = 15; - public const int ScissorStateIndex = 18; + public const int ScissorStateIndex = 16; public const int VertexBufferStateIndex = 0; public const int PrimitiveRestartStateIndex = 12; @@ -31,6 +32,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private readonly ShaderProgramInfo[] _currentProgramInfo; private ShaderSpecializationState _shaderSpecState; + private SpecializationStateUpdater _currentSpecState; private ProgramPipelineState _pipeline; @@ -54,15 +56,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// GPU channel /// 3D engine state /// Draw state - public StateUpdater(GpuContext context, GpuChannel channel, DeviceStateWithShadow state, DrawState drawState) + /// Specialization state updater + public StateUpdater(GpuContext context, GpuChannel channel, DeviceStateWithShadow state, DrawState drawState, SpecializationStateUpdater spec) { _context = context; _channel = channel; _state = state; _drawState = drawState; _currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages]; + _currentSpecState = spec; - // ShaderState must be updated after other state updates, as pipeline state is sent to the backend when compiling new shaders. + // ShaderState must be updated after other state updates, as specialization/pipeline state is used when fetching shaders. // Render target state must appear after shader state as it depends on information from the currently bound shader. // Rasterizer and scissor states are checked by render target clear, their indexes // must be updated on the constants "RasterizerStateIndex" and "ScissorStateIndex" if modified. @@ -101,6 +105,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed nameof(ThreedClassState.DepthTestFunc)), new StateUpdateCallbackEntry(UpdateTessellationState, + nameof(ThreedClassState.TessMode), nameof(ThreedClassState.TessOuterLevel), nameof(ThreedClassState.TessInnerLevel), nameof(ThreedClassState.PatchVertices)), @@ -138,17 +143,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed new StateUpdateCallbackEntry(UpdateRasterizerState, nameof(ThreedClassState.RasterizeEnable)), - new StateUpdateCallbackEntry(UpdateShaderState, - nameof(ThreedClassState.ShaderBaseAddress), - nameof(ThreedClassState.ShaderState)), - - new StateUpdateCallbackEntry(UpdateRenderTargetState, - nameof(ThreedClassState.RtColorState), - nameof(ThreedClassState.RtDepthStencilState), - nameof(ThreedClassState.RtControl), - nameof(ThreedClassState.RtDepthStencilSize), - nameof(ThreedClassState.RtDepthStencilEnable)), - new StateUpdateCallbackEntry(UpdateScissorState, nameof(ThreedClassState.ScissorState), nameof(ThreedClassState.ScreenScissorState)), @@ -179,7 +173,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed new StateUpdateCallbackEntry(UpdateMultisampleState, nameof(ThreedClassState.AlphaToCoverageDitherEnable), - nameof(ThreedClassState.MultisampleControl)) + nameof(ThreedClassState.MultisampleControl)), + + new StateUpdateCallbackEntry(UpdateEarlyZState, + nameof(ThreedClassState.EarlyZForce)), + + new StateUpdateCallbackEntry(UpdateShaderState, + nameof(ThreedClassState.ShaderBaseAddress), + nameof(ThreedClassState.ShaderState)), + + new StateUpdateCallbackEntry(UpdateRenderTargetState, + nameof(ThreedClassState.RtColorState), + nameof(ThreedClassState.RtDepthStencilState), + nameof(ThreedClassState.RtControl), + nameof(ThreedClassState.RtDepthStencilSize), + nameof(ThreedClassState.RtDepthStencilEnable)), }); } @@ -209,17 +217,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Update() { - // If any state that the shader depends on changed, - // then we may need to compile/bind a different version - // of the shader for the new state. - if (_shaderSpecState != null) - { - if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState(), _vsUsesDrawParameters, false)) - { - ForceShaderUpdate(); - } - } - // The vertex buffer size is calculated using a different // method when doing indexed draws, so we need to make sure // to update the vertex buffers if we are doing a regular @@ -271,6 +268,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _updateTracker.Update(ulong.MaxValue); + // If any state that the shader depends on changed, + // then we may need to compile/bind a different version + // of the shader for the new state. + if (_shaderSpecState != null && _currentSpecState.HasChanged()) + { + if (!_shaderSpecState.MatchesGraphics(_channel, ref _currentSpecState.GetPoolState(), ref _currentSpecState.GetGraphicsState(), _vsUsesDrawParameters, false)) + { + // Shader must be reloaded. _vtgWritesRtLayer should not change. + UpdateShaderState(); + } + } + CommitBindings(); if (tfEnable && !_prevTfEnable) @@ -302,7 +311,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (!_channel.TextureManager.CommitGraphicsBindings(_shaderSpecState) || (buffers.HasUnalignedStorageBuffers != hasUnaligned)) { - // Shader must be reloaded. + _currentSpecState.SetHasUnalignedStorageBuffer(buffers.HasUnalignedStorageBuffers); + // Shader must be reloaded. _vtgWritesRtLayer should not change. UpdateShaderState(); } @@ -351,6 +361,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _state.State.PatchVertices, _state.State.TessOuterLevel.AsSpan(), _state.State.TessInnerLevel.AsSpan()); + + _currentSpecState.SetTessellationMode(_state.State.TessMode); } /// @@ -611,6 +623,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _state.State.AlphaTestEnable, _state.State.AlphaTestRef, _state.State.AlphaTestFunc); + + _currentSpecState.SetAlphaTest( + _state.State.AlphaTestEnable, + _state.State.AlphaTestRef, + _state.State.AlphaTestFunc); } /// @@ -710,6 +727,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _context.Renderer.Pipeline.SetDepthMode(GetDepthMode()); _context.Renderer.Pipeline.SetViewports(viewports, disableTransform); + + _currentSpecState.SetViewportTransformDisable(_state.State.ViewportTransformEnable == 0); + _currentSpecState.SetDepthMode(GetDepthMode() == DepthMode.MinusOneToOne); } /// @@ -847,6 +867,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _channel.TextureManager.SetGraphicsTexturePool(texturePool.Address.Pack(), texturePool.MaximumId); _channel.TextureManager.SetGraphicsTextureBufferIndex((int)_state.State.TextureBufferIndex); + + _currentSpecState.SetPoolState(GetPoolState()); } /// @@ -887,6 +909,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _pipeline.SetVertexAttribs(vertexAttribs); _context.Renderer.Pipeline.SetVertexAttribs(vertexAttribs); + _currentSpecState.SetAttributeTypes(ref _state.State.VertexAttribState); } /// @@ -914,6 +937,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Origin origin = (_state.State.PointCoordReplace & 4) == 0 ? Origin.LowerLeft : Origin.UpperLeft; _context.Renderer.Pipeline.SetPointParameters(size, isProgramPointSize, enablePointSprite, origin); + + _currentSpecState.SetProgramPointSizeEnable(isProgramPointSize); + _currentSpecState.SetPointSize(size); } /// @@ -1212,6 +1238,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed alphaToCoverageEnable, _state.State.AlphaToCoverageDitherEnable, alphaToOneEnable)); + + _currentSpecState.SetAlphaToCoverageEnable(alphaToCoverageEnable, _state.State.AlphaToCoverageDitherEnable); + } + + /// + /// Updates the early z flag, based on guest state. + /// + private void UpdateEarlyZState() + { + _currentSpecState.SetEarlyZForce(_state.State.EarlyZForce); } /// @@ -1239,10 +1275,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed addressesSpan[index] = baseAddress + shader.Offset; } - GpuChannelPoolState poolState = GetPoolState(); - GpuChannelGraphicsState graphicsState = GetGraphicsState(); + CachedShaderProgram gs = shaderCache.GetGraphicsShader(ref _state.State, ref _pipeline, _channel, ref _currentSpecState.GetPoolState(), ref _currentSpecState.GetGraphicsState(), addresses); - CachedShaderProgram gs = shaderCache.GetGraphicsShader(ref _state.State, ref _pipeline, _channel, poolState, graphicsState, addresses); + // Consume the modified flag for spec state so that it isn't checked again. + _currentSpecState.SetShader(gs); _shaderSpecState = gs.SpecializationState; @@ -1289,46 +1325,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed (int)_state.State.TextureBufferIndex); } - /// - /// Gets the current GPU channel state for shader creation or compatibility verification. - /// - /// Current GPU channel state - private GpuChannelGraphicsState GetGraphicsState() - { - ref var vertexAttribState = ref _state.State.VertexAttribState; - - Array32 attributeTypes = new Array32(); - - for (int location = 0; location < attributeTypes.Length; location++) - { - VertexAttribType type = vertexAttribState[location].UnpackType(); - - attributeTypes[location] = type switch - { - VertexAttribType.Sint => AttributeType.Sint, - VertexAttribType.Uint => AttributeType.Uint, - _ => AttributeType.Float - }; - } - - return new GpuChannelGraphicsState( - _state.State.EarlyZForce, - _drawState.Topology, - _state.State.TessMode, - (_state.State.MultisampleControl & 1) != 0, - _state.State.AlphaToCoverageDitherEnable, - _state.State.ViewportTransformEnable == 0, - GetDepthMode() == DepthMode.MinusOneToOne, - _state.State.VertexProgramPointSize, - _state.State.PointSize, - _state.State.AlphaTestEnable, - _state.State.AlphaTestFunc, - _state.State.AlphaTestRef, - ref attributeTypes, - _drawState.HasConstantBufferDrawParameters, - _channel.BufferManager.HasUnalignedStorageBuffers); - } - /// /// Gets the depth mode that is currently being used (zero to one or minus one to one). /// diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index 106a6f3f4..b254e95e0 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -67,12 +67,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _i2mClass = new InlineToMemoryClass(context, channel, initializeState: false); + var spec = new SpecializationStateUpdater(); var drawState = new DrawState(); - _drawManager = new DrawManager(context, channel, _state, drawState); + _drawManager = new DrawManager(context, channel, _state, drawState, spec); _semaphoreUpdater = new SemaphoreUpdater(context, channel, _state); _cbUpdater = new ConstantBufferUpdater(channel, _state); - _stateUpdater = new StateUpdater(context, channel, _state, drawState); + _stateUpdater = new StateUpdater(context, channel, _state, drawState, spec); // This defaults to "always", even without any register write. // Reads just return 0, regardless of what was set there. diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs index 511f4c235..e5e486260 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs @@ -15,62 +15,62 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Early Z force enable. /// - public readonly bool EarlyZForce; + public bool EarlyZForce; /// /// Primitive topology of current draw. /// - public readonly PrimitiveTopology Topology; + public PrimitiveTopology Topology; /// /// Tessellation mode. /// - public readonly TessMode TessellationMode; + public TessMode TessellationMode; /// /// Indicates whether alpha-to-coverage is enabled. /// - public readonly bool AlphaToCoverageEnable; + public bool AlphaToCoverageEnable; /// /// Indicates whether alpha-to-coverage dithering is enabled. /// - public readonly bool AlphaToCoverageDitherEnable; + public bool AlphaToCoverageDitherEnable; /// /// Indicates whether the viewport transform is disabled. /// - public readonly bool ViewportTransformDisable; + public bool ViewportTransformDisable; /// /// Depth mode zero to one or minus one to one. /// - public readonly bool DepthMode; + public bool DepthMode; /// /// Indicates if the point size is set on the shader or is fixed. /// - public readonly bool ProgramPointSizeEnable; + public bool ProgramPointSizeEnable; /// /// Point size used if is false. /// - public readonly float PointSize; + public float PointSize; /// /// Indicates whether alpha test is enabled. /// - public readonly bool AlphaTestEnable; + public bool AlphaTestEnable; /// /// When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded. /// - public readonly CompareOp AlphaTestCompare; + public CompareOp AlphaTestCompare; /// /// When alpha test is enabled, indicates the value to compare with the fragment output alpha. /// - public readonly float AlphaTestReference; + public float AlphaTestReference; /// /// Type of the vertex attributes consumed by the shader. @@ -80,12 +80,12 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0. /// - public readonly bool HasConstantBufferDrawParameters; + public bool HasConstantBufferDrawParameters; /// /// Indicates that any storage buffer use is unaligned. /// - public readonly bool HasUnalignedStorageBuffer; + public bool HasUnalignedStorageBuffer; /// /// Creates a new GPU graphics state. diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs index 0b36227ac..b894c57e7 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs @@ -1,9 +1,11 @@ +using System; + namespace Ryujinx.Graphics.Gpu.Shader { /// /// State used by the . /// - struct GpuChannelPoolState + struct GpuChannelPoolState : IEquatable { /// /// GPU virtual address of the texture pool. @@ -32,5 +34,17 @@ namespace Ryujinx.Graphics.Gpu.Shader TexturePoolMaximumId = texturePoolMaximumId; TextureBufferIndex = textureBufferIndex; } + + /// + /// Check if the pool states are equal. + /// + /// Pool state to compare with + /// True if they are equal, false otherwise + public bool Equals(GpuChannelPoolState other) + { + return TexturePoolGpuVa == other.TexturePoolGpuVa && + TexturePoolMaximumId == other.TexturePoolMaximumId && + TextureBufferIndex == other.TextureBufferIndex; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 3eaab79f0..23b213b4b 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -300,16 +300,16 @@ namespace Ryujinx.Graphics.Gpu.Shader ref ThreedClassState state, ref ProgramPipelineState pipeline, GpuChannel channel, - GpuChannelPoolState poolState, - GpuChannelGraphicsState graphicsState, + ref GpuChannelPoolState poolState, + ref GpuChannelGraphicsState graphicsState, ShaderAddresses addresses) { - if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, poolState, graphicsState, gpShaders, addresses)) + if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, ref poolState, ref graphicsState, gpShaders, addresses)) { return gpShaders; } - if (_graphicsShaderCache.TryFind(channel, poolState, graphicsState, addresses, out gpShaders, out var cachedGuestCode)) + if (_graphicsShaderCache.TryFind(channel, ref poolState, ref graphicsState, addresses, out gpShaders, out var cachedGuestCode)) { _gpPrograms[addresses] = gpShaders; return gpShaders; @@ -498,7 +498,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { if (IsShaderEqual(channel.MemoryManager, cpShader.Shaders[0], gpuVa)) { - return cpShader.SpecializationState.MatchesCompute(channel, poolState, computeState, true); + return cpShader.SpecializationState.MatchesCompute(channel, ref poolState, computeState, true); } return false; @@ -515,8 +515,8 @@ namespace Ryujinx.Graphics.Gpu.Shader /// True if the code is different, false otherwise private static bool IsShaderEqual( GpuChannel channel, - GpuChannelPoolState poolState, - GpuChannelGraphicsState graphicsState, + ref GpuChannelPoolState poolState, + ref GpuChannelGraphicsState graphicsState, CachedShaderProgram gpShaders, ShaderAddresses addresses) { @@ -536,7 +536,7 @@ namespace Ryujinx.Graphics.Gpu.Shader bool usesDrawParameters = gpShaders.Shaders[1]?.Info.UsesDrawParameters ?? false; - return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, usesDrawParameters, true); + return gpShaders.SpecializationState.MatchesGraphics(channel, ref poolState, ref graphicsState, usesDrawParameters, true); } /// diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs index 3d74e53a1..e35c06b13 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCacheHashTable.cs @@ -215,8 +215,8 @@ namespace Ryujinx.Graphics.Gpu.Shader /// True if a cached host program was found, false otherwise public bool TryFind( GpuChannel channel, - GpuChannelPoolState poolState, - GpuChannelGraphicsState graphicsState, + ref GpuChannelPoolState poolState, + ref GpuChannelGraphicsState graphicsState, ShaderAddresses addresses, out CachedShaderProgram program, out CachedGraphicsGuestCode guestCode) @@ -236,7 +236,7 @@ namespace Ryujinx.Graphics.Gpu.Shader if (found && _shaderPrograms.TryGetValue(idTable, out ShaderSpecializationList specList)) { - return specList.TryFindForGraphics(channel, poolState, graphicsState, out program); + return specList.TryFindForGraphics(channel, ref poolState, ref graphicsState, out program); } return false; diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs index cb6ab49a8..7d61332e5 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs @@ -29,15 +29,15 @@ namespace Ryujinx.Graphics.Gpu.Shader /// True if a compatible program is found, false otherwise public bool TryFindForGraphics( GpuChannel channel, - GpuChannelPoolState poolState, - GpuChannelGraphicsState graphicsState, + ref GpuChannelPoolState poolState, + ref GpuChannelGraphicsState graphicsState, out CachedShaderProgram program) { foreach (var entry in _entries) { bool usesDrawParameters = entry.Shaders[1]?.Info.UsesDrawParameters ?? false; - if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, usesDrawParameters, true)) + if (entry.SpecializationState.MatchesGraphics(channel, ref poolState, ref graphicsState, usesDrawParameters, true)) { program = entry; return true; @@ -60,7 +60,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { foreach (var entry in _entries) { - if (entry.SpecializationState.MatchesCompute(channel, poolState, computeState, true)) + if (entry.SpecializationState.MatchesCompute(channel, ref poolState, computeState, true)) { program = entry; return true; diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index 8f931507a..14f64bbf4 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -392,6 +392,15 @@ namespace Ryujinx.Graphics.Gpu.Shader state.Value.QueriedFlags |= QueriedTextureStateFlags.CoordNormalized; } + /// + /// Checks if primitive topology was queried by the shader. + /// + /// True if queried, false otherwise + public bool IsPrimitiveTopologyQueried() + { + return _queriedState.HasFlag(QueriedStateFlags.PrimitiveTopology); + } + /// /// Checks if a given texture was registerd on this specialization state. /// @@ -486,8 +495,8 @@ namespace Ryujinx.Graphics.Gpu.Shader /// True if the state matches, false otherwise public bool MatchesGraphics( GpuChannel channel, - GpuChannelPoolState poolState, - GpuChannelGraphicsState graphicsState, + ref GpuChannelPoolState poolState, + ref GpuChannelGraphicsState graphicsState, bool usesDrawParameters, bool checkTextures) { @@ -536,7 +545,7 @@ namespace Ryujinx.Graphics.Gpu.Shader return false; } - return Matches(channel, poolState, checkTextures, isCompute: false); + return Matches(channel, ref poolState, checkTextures, isCompute: false); } /// @@ -547,14 +556,14 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Compute state /// Indicates whether texture descriptors should be checked /// True if the state matches, false otherwise - public bool MatchesCompute(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelComputeState computeState, bool checkTextures) + public bool MatchesCompute(GpuChannel channel, ref GpuChannelPoolState poolState, GpuChannelComputeState computeState, bool checkTextures) { if (computeState.HasUnalignedStorageBuffer != ComputeState.HasUnalignedStorageBuffer) { return false; } - return Matches(channel, poolState, checkTextures, isCompute: true); + return Matches(channel, ref poolState, checkTextures, isCompute: true); } /// @@ -618,7 +627,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Indicates whether texture descriptors should be checked /// Indicates whenever the check is requested by the 3D or compute engine /// True if the state matches, false otherwise - private bool Matches(GpuChannel channel, GpuChannelPoolState poolState, bool checkTextures, bool isCompute) + private bool Matches(GpuChannel channel, ref GpuChannelPoolState poolState, bool checkTextures, bool isCompute) { int constantBufferUsePerStageMask = _constantBufferUsePerStage;