mirror of
https://git.743378673.xyz/MeloNX/MeloNX.git
synced 2025-08-02 22:37:10 +02:00
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
This commit is contained in:
parent
4965681e06
commit
9ac66336a2
10 changed files with 417 additions and 114 deletions
|
@ -15,62 +15,62 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <summary>
|
||||
/// Early Z force enable.
|
||||
/// </summary>
|
||||
public readonly bool EarlyZForce;
|
||||
public bool EarlyZForce;
|
||||
|
||||
/// <summary>
|
||||
/// Primitive topology of current draw.
|
||||
/// </summary>
|
||||
public readonly PrimitiveTopology Topology;
|
||||
public PrimitiveTopology Topology;
|
||||
|
||||
/// <summary>
|
||||
/// Tessellation mode.
|
||||
/// </summary>
|
||||
public readonly TessMode TessellationMode;
|
||||
public TessMode TessellationMode;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether alpha-to-coverage is enabled.
|
||||
/// </summary>
|
||||
public readonly bool AlphaToCoverageEnable;
|
||||
public bool AlphaToCoverageEnable;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether alpha-to-coverage dithering is enabled.
|
||||
/// </summary>
|
||||
public readonly bool AlphaToCoverageDitherEnable;
|
||||
public bool AlphaToCoverageDitherEnable;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the viewport transform is disabled.
|
||||
/// </summary>
|
||||
public readonly bool ViewportTransformDisable;
|
||||
public bool ViewportTransformDisable;
|
||||
|
||||
/// <summary>
|
||||
/// Depth mode zero to one or minus one to one.
|
||||
/// </summary>
|
||||
public readonly bool DepthMode;
|
||||
public bool DepthMode;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the point size is set on the shader or is fixed.
|
||||
/// </summary>
|
||||
public readonly bool ProgramPointSizeEnable;
|
||||
public bool ProgramPointSizeEnable;
|
||||
|
||||
/// <summary>
|
||||
/// Point size used if <see cref="ProgramPointSizeEnable" /> is false.
|
||||
/// </summary>
|
||||
public readonly float PointSize;
|
||||
public float PointSize;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether alpha test is enabled.
|
||||
/// </summary>
|
||||
public readonly bool AlphaTestEnable;
|
||||
public bool AlphaTestEnable;
|
||||
|
||||
/// <summary>
|
||||
/// When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded.
|
||||
/// </summary>
|
||||
public readonly CompareOp AlphaTestCompare;
|
||||
public CompareOp AlphaTestCompare;
|
||||
|
||||
/// <summary>
|
||||
/// When alpha test is enabled, indicates the value to compare with the fragment output alpha.
|
||||
/// </summary>
|
||||
public readonly float AlphaTestReference;
|
||||
public float AlphaTestReference;
|
||||
|
||||
/// <summary>
|
||||
/// Type of the vertex attributes consumed by the shader.
|
||||
|
@ -80,12 +80,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <summary>
|
||||
/// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
|
||||
/// </summary>
|
||||
public readonly bool HasConstantBufferDrawParameters;
|
||||
public bool HasConstantBufferDrawParameters;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that any storage buffer use is unaligned.
|
||||
/// </summary>
|
||||
public readonly bool HasUnalignedStorageBuffer;
|
||||
public bool HasUnalignedStorageBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GPU graphics state.
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
/// <summary>
|
||||
/// State used by the <see cref="GpuAccessor"/>.
|
||||
/// </summary>
|
||||
struct GpuChannelPoolState
|
||||
struct GpuChannelPoolState : IEquatable<GpuChannelPoolState>
|
||||
{
|
||||
/// <summary>
|
||||
/// GPU virtual address of the texture pool.
|
||||
|
@ -32,5 +34,17 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
TexturePoolMaximumId = texturePoolMaximumId;
|
||||
TextureBufferIndex = textureBufferIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the pool states are equal.
|
||||
/// </summary>
|
||||
/// <param name="other">Pool state to compare with</param>
|
||||
/// <returns>True if they are equal, false otherwise</returns>
|
||||
public bool Equals(GpuChannelPoolState other)
|
||||
{
|
||||
return TexturePoolGpuVa == other.TexturePoolGpuVa &&
|
||||
TexturePoolMaximumId == other.TexturePoolMaximumId &&
|
||||
TextureBufferIndex == other.TextureBufferIndex;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|||
/// <returns>True if the code is different, false otherwise</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -215,8 +215,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <returns>True if a cached host program was found, false otherwise</returns>
|
||||
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;
|
||||
|
|
|
@ -29,15 +29,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <returns>True if a compatible program is found, false otherwise</returns>
|
||||
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;
|
||||
|
|
|
@ -392,6 +392,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
state.Value.QueriedFlags |= QueriedTextureStateFlags.CoordNormalized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if primitive topology was queried by the shader.
|
||||
/// </summary>
|
||||
/// <returns>True if queried, false otherwise</returns>
|
||||
public bool IsPrimitiveTopologyQueried()
|
||||
{
|
||||
return _queriedState.HasFlag(QueriedStateFlags.PrimitiveTopology);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given texture was registerd on this specialization state.
|
||||
/// </summary>
|
||||
|
@ -486,8 +495,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <returns>True if the state matches, false otherwise</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -547,14 +556,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <param name="computeState">Compute state</param>
|
||||
/// <param name="checkTextures">Indicates whether texture descriptors should be checked</param>
|
||||
/// <returns>True if the state matches, false otherwise</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -618,7 +627,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <param name="checkTextures">Indicates whether texture descriptors should be checked</param>
|
||||
/// <param name="isCompute">Indicates whenever the check is requested by the 3D or compute engine</param>
|
||||
/// <returns>True if the state matches, false otherwise</returns>
|
||||
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;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue