From e19dbeddd306df4b34742a6f0eb64e9bbedb99f0 Mon Sep 17 00:00:00 2001 From: Gabriel A Date: Fri, 1 Mar 2024 14:35:41 -0300 Subject: [PATCH] Do not set render targets as modified for discard-only draws --- .../Engine/Threed/DrawManager.cs | 3 +- .../Engine/Threed/RenderTargetUpdateFlags.cs | 5 + .../Engine/Threed/StateUpdater.cs | 41 ++++-- src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 14 +- .../Image/TextureBindingsManager.cs | 2 +- .../Image/TextureManager.cs | 130 +++++++++++++----- .../Shader/DiskCache/DiskCacheHostStorage.cs | 7 + .../IntermediateRepresentation/BasicBlock.cs | 1 + .../ShaderProgramInfo.cs | 3 + .../Translation/FeatureFlags.cs | 1 + .../Translation/FeatureIdentification.cs | 38 +++++ .../Translation/TranslatorContext.cs | 6 + 12 files changed, 197 insertions(+), 54 deletions(-) create mode 100644 src/Ryujinx.Graphics.Shader/Translation/FeatureIdentification.cs diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index d8de14de09..fe35875160 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -466,6 +466,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed engine.UpdateState(ulong.MaxValue & ~(1UL << StateUpdater.ShaderStateIndex)); + _channel.TextureManager.SignalRenderTargetsModifiable(); _channel.TextureManager.UpdateRenderTargets(); int textureId = _state.State.DrawTextureTextureId; @@ -803,7 +804,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed int index = (argument >> 6) & 0xf; int layer = (argument >> 10) & 0x3ff; - RenderTargetUpdateFlags updateFlags = RenderTargetUpdateFlags.SingleColor; + RenderTargetUpdateFlags updateFlags = RenderTargetUpdateFlags.SingleColor | RenderTargetUpdateFlags.ForClear; if (layer != 0 || layerCount > 1) { diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs index 58c7bdb442..ebfa83e790 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs @@ -38,6 +38,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// DiscardClip = 1 << 4, + /// + /// Indicates that the render target will be used for a clear operation. + /// + ForClear = 1 << 5, + /// /// Default update flags for draw. /// diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index 6b4ea89f39..9fc9cc1a77 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -44,6 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private ProgramPipelineState _pipeline; private bool _fsReadsFragCoord; + private bool _fsAlwaysDiscards; private bool _vsUsesDrawParameters; private bool _vtgWritesRtLayer; private byte _vsClipDistancesWritten; @@ -476,6 +477,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed bool changedScale = false; uint rtNoAlphaMask = 0; + bool rtModifiable = updateFlags.HasFlag(RenderTargetUpdateFlags.ForClear) || !_fsAlwaysDiscards; + for (int index = 0; index < Constants.TotalRenderTargets; index++) { int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index; @@ -484,7 +487,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (index >= count || !IsRtEnabled(colorState) || (singleColor && index != singleUse)) { - changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null); + changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null, rtModifiable); continue; } @@ -503,7 +506,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed samplesInY, sizeHint); - changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color); + changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color, rtModifiable); if (color != null) { @@ -557,7 +560,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } } - changedScale |= _channel.TextureManager.SetRenderTargetDepthStencil(depthStencil); + changedScale |= _channel.TextureManager.SetRenderTargetDepthStencil(depthStencil, rtModifiable); if (changedScale) { @@ -1466,20 +1469,38 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _currentProgramInfo[stageIndex] = info; } - if (gs.Shaders[5]?.Info.UsesFragCoord == true) - { - // Make sure we update the viewport size on the support buffer if it will be consumed on the new shader. + _fsReadsFragCoord = false; - if (!_fsReadsFragCoord && _state.State.YControl.HasFlag(YControl.NegateY)) + ShaderProgramInfo fragmentShaderInfo = gs.Shaders[5]?.Info; + + if (fragmentShaderInfo != null) + { + if (fragmentShaderInfo.UsesFragCoord) { - UpdateSupportBufferViewportSize(); + // Make sure we update the viewport size on the support buffer if it will be consumed on the new shader. + + if (!_fsReadsFragCoord && _state.State.YControl.HasFlag(YControl.NegateY)) + { + UpdateSupportBufferViewportSize(); + } + + _fsReadsFragCoord = true; } - _fsReadsFragCoord = true; + if (_fsAlwaysDiscards != fragmentShaderInfo.HasUnconditionalDiscard) + { + _fsAlwaysDiscards = fragmentShaderInfo.HasUnconditionalDiscard; + + if (!_fsAlwaysDiscards) + { + _channel.TextureManager.RefreshModifiedTextures(); + _channel.TextureManager.SignalRenderTargetsModifiable(); + } + } } else { - _fsReadsFragCoord = false; + _fsAlwaysDiscards = false; } if (gs.VertexAsCompute != null) diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index f1615b388b..2c78f560b0 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1430,11 +1430,6 @@ namespace Ryujinx.Graphics.Gpu.Image /// True if the texture has been bound, false if it has been unbound public void SignalModifying(bool bound) { - if (bound) - { - _scaledSetScore = Math.Max(0, _scaledSetScore - 1); - } - if (_modifiedStale || Group.HasCopyDependencies || Group.HasFlushBuffer) { _modifiedStale = false; @@ -1442,9 +1437,18 @@ namespace Ryujinx.Graphics.Gpu.Image } _physicalMemory.TextureCache.Lift(this); + } + /// + /// Signals that a render target texture has been either bound or unbound. + /// + /// True if the texture has been bound, false if it has been unbound + public void SignalBindingChange(bool bound) + { if (bound) { + _scaledSetScore = Math.Max(0, _scaledSetScore - 1); + IncrementReferenceCount(); } else diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index ef5d0deaad..55d948898b 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Gpu.Image { (TexturePool texturePool, SamplerPool samplerPool) = GetPools(); - return (texturePool.Get(textureId), samplerPool.Get(samplerId)); + return (texturePool?.Get(textureId), samplerPool?.Get(samplerId)); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index 3bf0beefdb..db04155217 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -18,12 +18,39 @@ namespace Ryujinx.Graphics.Gpu.Image private readonly TexturePoolCache _texturePoolCache; private readonly SamplerPoolCache _samplerPoolCache; + /// + /// Bound render target texture modification report state. + /// + [Flags] + private enum BindState : byte + { + /// + /// Render target texture has not been signalled for modification, and can't be modified on the next render operations. + /// + None = 0, + + /// + /// Render target texture has been signalled for modification. + /// + Bound = 1 << 0, + + /// + /// Render target texture might be modified on the next render operations. + /// + Modified = 1 << 1, + + /// + /// Render target texture has been signalled for modification and might be modified on the next render operations. + /// + BoundModified = Bound | Modified, + } + private readonly Texture[] _rtColors; private readonly ITexture[] _rtHostColors; - private readonly bool[] _rtColorsBound; + private readonly BindState[] _rtColorsBound; private Texture _rtDepthStencil; private ITexture _rtHostDs; - private bool _rtDsBound; + private BindState _rtDsBound; public int ClipRegionWidth { get; private set; } public int ClipRegionHeight { get; private set; } @@ -53,7 +80,7 @@ namespace Ryujinx.Graphics.Gpu.Image _rtColors = new Texture[Constants.TotalRenderTargets]; _rtHostColors = new ITexture[Constants.TotalRenderTargets]; - _rtColorsBound = new bool[Constants.TotalRenderTargets]; + _rtColorsBound = new BindState[Constants.TotalRenderTargets]; } /// @@ -149,27 +176,39 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// The index of the color buffer to set (up to 8) /// The color buffer texture + /// Indicates if the following render operations will modidify contents /// True if render target scale must be updated. - public bool SetRenderTargetColor(int index, Texture color) + public bool SetRenderTargetColor(int index, Texture color, bool modified) { bool hasValue = color != null; bool changesScale = (hasValue != (_rtColors[index] != null)) || (hasValue && RenderTargetScale != color.ScaleFactor); if (_rtColors[index] != color) { - if (_rtColorsBound[index]) + Texture oldColor = _rtColors[index]; + + if (oldColor != null) { - _rtColors[index]?.SignalModifying(false); - } - else - { - _rtColorsBound[index] = true; + if (_rtColorsBound[index].HasFlag(BindState.Bound)) + { + oldColor.SignalModifying(false); + } + + oldColor.SignalBindingChange(false); } + _rtColorsBound[index] = modified ? BindState.BoundModified : BindState.None; + if (color != null) { color.SynchronizeMemory(); - color.SignalModifying(true); + + if (modified) + { + color.SignalModifying(true); + } + + color.SignalBindingChange(true); } _rtColors[index] = color; @@ -182,27 +221,39 @@ namespace Ryujinx.Graphics.Gpu.Image /// Sets the render target depth-stencil buffer. /// /// The depth-stencil buffer texture + /// Indicates if the following render operations will modidify contents /// True if render target scale must be updated. - public bool SetRenderTargetDepthStencil(Texture depthStencil) + public bool SetRenderTargetDepthStencil(Texture depthStencil, bool modified) { bool hasValue = depthStencil != null; bool changesScale = (hasValue != (_rtDepthStencil != null)) || (hasValue && RenderTargetScale != depthStencil.ScaleFactor); if (_rtDepthStencil != depthStencil) { - if (_rtDsBound) + Texture oldDepthStencil = _rtDepthStencil; + + if (oldDepthStencil != null) { - _rtDepthStencil?.SignalModifying(false); - } - else - { - _rtDsBound = true; + if (_rtDsBound.HasFlag(BindState.Bound)) + { + oldDepthStencil.SignalModifying(false); + } + + oldDepthStencil.SignalBindingChange(false); } + _rtDsBound = modified ? BindState.BoundModified : BindState.None; + if (depthStencil != null) { depthStencil.SynchronizeMemory(); - depthStencil.SignalModifying(true); + + if (modified) + { + depthStencil.SignalModifying(true); + } + + depthStencil.SignalBindingChange(true); } _rtDepthStencil = depthStencil; @@ -437,10 +488,10 @@ namespace Ryujinx.Graphics.Gpu.Image { hostDsTexture = dsTexture.HostTexture; - if (!_rtDsBound) + if (_rtDsBound == BindState.Modified) { dsTexture.SignalModifying(true); - _rtDsBound = true; + _rtDsBound |= BindState.Bound; } } @@ -459,10 +510,10 @@ namespace Ryujinx.Graphics.Gpu.Image { hostTexture = texture.HostTexture; - if (!_rtColorsBound[index]) + if (_rtColorsBound[index] == BindState.Modified) { texture.SignalModifying(true); - _rtColorsBound[index] = true; + _rtColorsBound[index] |= BindState.Bound; } } @@ -500,24 +551,37 @@ namespace Ryujinx.Graphics.Gpu.Image { Texture dsTexture = _rtDepthStencil; - if (dsTexture != null && _rtDsBound) + if (dsTexture != null && _rtDsBound.HasFlag(BindState.Bound)) { dsTexture.SignalModifying(false); - _rtDsBound = false; + _rtDsBound &= ~BindState.Bound; } for (int index = 0; index < _rtColors.Length; index++) { Texture texture = _rtColors[index]; - if (texture != null && _rtColorsBound[index]) + if (texture != null && _rtColorsBound[index].HasFlag(BindState.Bound)) { texture.SignalModifying(false); - _rtColorsBound[index] = false; + _rtColorsBound[index] &= ~BindState.Bound; } } } + /// + /// Indicates that the currently bound render targets might be modified, if they are used on the next render operation. + /// + public void SignalRenderTargetsModifiable() + { + _rtDsBound |= BindState.Modified; + + for (int index = 0; index < _rtColorsBound.Length; index++) + { + _rtColorsBound[index] |= BindState.Modified; + } + } + /// /// Forces the texture and sampler pools to be re-loaded from the cache on next use. /// @@ -554,19 +618,11 @@ namespace Ryujinx.Graphics.Gpu.Image for (int i = 0; i < _rtColors.Length; i++) { - if (_rtColorsBound[i]) - { - _rtColors[i]?.DecrementReferenceCount(); - } - + _rtColors[i]?.DecrementReferenceCount(); _rtColors[i] = null; } - if (_rtDsBound) - { - _rtDepthStencil?.DecrementReferenceCount(); - } - + _rtDepthStencil?.DecrementReferenceCount(); _rtDepthStencil = null; } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index c5763b0258..ceb88a259a 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -170,6 +170,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// public bool UsesRtLayer; + /// + /// Indicates that the fragment shader always discards the fragment, not producing any output for the bound render targets. + /// + public bool HasUnconditionalDiscard; + /// /// Bit mask with the clip distances written on the vertex stage. /// @@ -806,6 +811,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache dataInfo.UsesInstanceId, dataInfo.UsesDrawParameters, dataInfo.UsesRtLayer, + dataInfo.HasUnconditionalDiscard, dataInfo.ClipDistancesWritten, dataInfo.FragmentOutputMap); } @@ -836,6 +842,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache UsesInstanceId = info.UsesInstanceId, UsesDrawParameters = info.UsesDrawParameters, UsesRtLayer = info.UsesRtLayer, + HasUnconditionalDiscard = info.HasUnconditionalDiscard, ClipDistancesWritten = info.ClipDistancesWritten, FragmentOutputMap = info.FragmentOutputMap, }; diff --git a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/BasicBlock.cs b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/BasicBlock.cs index 637e120e1c..4158face90 100644 --- a/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/BasicBlock.cs +++ b/src/Ryujinx.Graphics.Shader/IntermediateRepresentation/BasicBlock.cs @@ -23,6 +23,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation set => _branch = AddSuccessor(_branch, value); } + public bool HasSuccessor => _branch != null || _next != null; public bool HasBranch => _branch != null; public bool Reachable => Index == 0 || Predecessors.Count != 0; diff --git a/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs b/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs index 22823ac38e..1bb9ea06a4 100644 --- a/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs +++ b/src/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs @@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Shader public bool UsesInstanceId { get; } public bool UsesDrawParameters { get; } public bool UsesRtLayer { get; } + public bool HasUnconditionalDiscard { get; } public byte ClipDistancesWritten { get; } public int FragmentOutputMap { get; } @@ -34,6 +35,7 @@ namespace Ryujinx.Graphics.Shader bool usesInstanceId, bool usesDrawParameters, bool usesRtLayer, + bool hasUnconditionalDiscard, byte clipDistancesWritten, int fragmentOutputMap) { @@ -50,6 +52,7 @@ namespace Ryujinx.Graphics.Shader UsesInstanceId = usesInstanceId; UsesDrawParameters = usesDrawParameters; UsesRtLayer = usesRtLayer; + HasUnconditionalDiscard = hasUnconditionalDiscard; ClipDistancesWritten = clipDistancesWritten; FragmentOutputMap = fragmentOutputMap; } diff --git a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs index 82a54db835..ad9bdcb1e4 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs @@ -26,5 +26,6 @@ namespace Ryujinx.Graphics.Shader.Translation SharedMemory = 1 << 11, Store = 1 << 12, VtgAsCompute = 1 << 13, + UnconditionalDiscard = 1 << 14, } } diff --git a/src/Ryujinx.Graphics.Shader/Translation/FeatureIdentification.cs b/src/Ryujinx.Graphics.Shader/Translation/FeatureIdentification.cs new file mode 100644 index 0000000000..82d7e0b019 --- /dev/null +++ b/src/Ryujinx.Graphics.Shader/Translation/FeatureIdentification.cs @@ -0,0 +1,38 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; + +namespace Ryujinx.Graphics.Shader.Translation +{ + static class FeatureIdentification + { + public static void RunPass(BasicBlock[] blocks, ShaderStage stage, ref FeatureFlags usedFeatures) + { + if (stage == ShaderStage.Fragment) + { + bool endsWithDiscardOnly = true; + + for (int blockIndex = 0; blockIndex < blocks.Length; blockIndex++) + { + BasicBlock block = blocks[blockIndex]; + + if (block.HasSuccessor) + { + continue; + } + + if (block.Operations.Count == 0 || + block.Operations.Last.Value is not Operation operation || + operation.Inst != Instruction.Discard) + { + endsWithDiscardOnly = false; + break; + } + } + + if (endsWithDiscardOnly) + { + usedFeatures |= FeatureFlags.UnconditionalDiscard; + } + } + } + } +} diff --git a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs index a193ab3c4d..7a37365025 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs @@ -300,6 +300,11 @@ namespace Ryujinx.Graphics.Shader.Translation Optimizer.RunPass(context); TransformPasses.RunPass(context); + + if (i == 0) + { + FeatureIdentification.RunPass(cfg.Blocks, Definitions.Stage, ref usedFeatures); + } } funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount); @@ -352,6 +357,7 @@ namespace Ryujinx.Graphics.Shader.Translation usedFeatures.HasFlag(FeatureFlags.InstanceId), usedFeatures.HasFlag(FeatureFlags.DrawParameters), usedFeatures.HasFlag(FeatureFlags.RtLayer), + usedFeatures.HasFlag(FeatureFlags.UnconditionalDiscard), clipDistancesWritten, originalDefinitions.OmapTargets);