Do not set render targets as modified for discard-only draws

This commit is contained in:
Gabriel A 2024-03-01 14:35:41 -03:00
commit e19dbeddd3
12 changed files with 197 additions and 54 deletions

View file

@ -466,6 +466,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
engine.UpdateState(ulong.MaxValue & ~(1UL << StateUpdater.ShaderStateIndex)); engine.UpdateState(ulong.MaxValue & ~(1UL << StateUpdater.ShaderStateIndex));
_channel.TextureManager.SignalRenderTargetsModifiable();
_channel.TextureManager.UpdateRenderTargets(); _channel.TextureManager.UpdateRenderTargets();
int textureId = _state.State.DrawTextureTextureId; int textureId = _state.State.DrawTextureTextureId;
@ -803,7 +804,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int index = (argument >> 6) & 0xf; int index = (argument >> 6) & 0xf;
int layer = (argument >> 10) & 0x3ff; int layer = (argument >> 10) & 0x3ff;
RenderTargetUpdateFlags updateFlags = RenderTargetUpdateFlags.SingleColor; RenderTargetUpdateFlags updateFlags = RenderTargetUpdateFlags.SingleColor | RenderTargetUpdateFlags.ForClear;
if (layer != 0 || layerCount > 1) if (layer != 0 || layerCount > 1)
{ {

View file

@ -38,6 +38,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
DiscardClip = 1 << 4, DiscardClip = 1 << 4,
/// <summary>
/// Indicates that the render target will be used for a clear operation.
/// </summary>
ForClear = 1 << 5,
/// <summary> /// <summary>
/// Default update flags for draw. /// Default update flags for draw.
/// </summary> /// </summary>

View file

@ -44,6 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private ProgramPipelineState _pipeline; private ProgramPipelineState _pipeline;
private bool _fsReadsFragCoord; private bool _fsReadsFragCoord;
private bool _fsAlwaysDiscards;
private bool _vsUsesDrawParameters; private bool _vsUsesDrawParameters;
private bool _vtgWritesRtLayer; private bool _vtgWritesRtLayer;
private byte _vsClipDistancesWritten; private byte _vsClipDistancesWritten;
@ -476,6 +477,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool changedScale = false; bool changedScale = false;
uint rtNoAlphaMask = 0; uint rtNoAlphaMask = 0;
bool rtModifiable = updateFlags.HasFlag(RenderTargetUpdateFlags.ForClear) || !_fsAlwaysDiscards;
for (int index = 0; index < Constants.TotalRenderTargets; index++) for (int index = 0; index < Constants.TotalRenderTargets; index++)
{ {
int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : 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)) if (index >= count || !IsRtEnabled(colorState) || (singleColor && index != singleUse))
{ {
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null); changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null, rtModifiable);
continue; continue;
} }
@ -503,7 +506,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
samplesInY, samplesInY,
sizeHint); sizeHint);
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color); changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color, rtModifiable);
if (color != null) 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) if (changedScale)
{ {
@ -1466,7 +1469,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_currentProgramInfo[stageIndex] = info; _currentProgramInfo[stageIndex] = info;
} }
if (gs.Shaders[5]?.Info.UsesFragCoord == true) _fsReadsFragCoord = false;
ShaderProgramInfo fragmentShaderInfo = gs.Shaders[5]?.Info;
if (fragmentShaderInfo != null)
{
if (fragmentShaderInfo.UsesFragCoord)
{ {
// Make sure we update the viewport size on the support buffer if it will be consumed on the new shader. // Make sure we update the viewport size on the support buffer if it will be consumed on the new shader.
@ -1477,9 +1486,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_fsReadsFragCoord = true; _fsReadsFragCoord = true;
} }
if (_fsAlwaysDiscards != fragmentShaderInfo.HasUnconditionalDiscard)
{
_fsAlwaysDiscards = fragmentShaderInfo.HasUnconditionalDiscard;
if (!_fsAlwaysDiscards)
{
_channel.TextureManager.RefreshModifiedTextures();
_channel.TextureManager.SignalRenderTargetsModifiable();
}
}
}
else else
{ {
_fsReadsFragCoord = false; _fsAlwaysDiscards = false;
} }
if (gs.VertexAsCompute != null) if (gs.VertexAsCompute != null)

View file

@ -1430,11 +1430,6 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="bound">True if the texture has been bound, false if it has been unbound</param> /// <param name="bound">True if the texture has been bound, false if it has been unbound</param>
public void SignalModifying(bool bound) public void SignalModifying(bool bound)
{ {
if (bound)
{
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
}
if (_modifiedStale || Group.HasCopyDependencies || Group.HasFlushBuffer) if (_modifiedStale || Group.HasCopyDependencies || Group.HasFlushBuffer)
{ {
_modifiedStale = false; _modifiedStale = false;
@ -1442,9 +1437,18 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
_physicalMemory.TextureCache.Lift(this); _physicalMemory.TextureCache.Lift(this);
}
/// <summary>
/// Signals that a render target texture has been either bound or unbound.
/// </summary>
/// <param name="bound">True if the texture has been bound, false if it has been unbound</param>
public void SignalBindingChange(bool bound)
{
if (bound) if (bound)
{ {
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
IncrementReferenceCount(); IncrementReferenceCount();
} }
else else

View file

@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
(TexturePool texturePool, SamplerPool samplerPool) = GetPools(); (TexturePool texturePool, SamplerPool samplerPool) = GetPools();
return (texturePool.Get(textureId), samplerPool.Get(samplerId)); return (texturePool?.Get(textureId), samplerPool?.Get(samplerId));
} }
/// <summary> /// <summary>

View file

@ -18,12 +18,39 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly TexturePoolCache _texturePoolCache; private readonly TexturePoolCache _texturePoolCache;
private readonly SamplerPoolCache _samplerPoolCache; private readonly SamplerPoolCache _samplerPoolCache;
/// <summary>
/// Bound render target texture modification report state.
/// </summary>
[Flags]
private enum BindState : byte
{
/// <summary>
/// Render target texture has not been signalled for modification, and can't be modified on the next render operations.
/// </summary>
None = 0,
/// <summary>
/// Render target texture has been signalled for modification.
/// </summary>
Bound = 1 << 0,
/// <summary>
/// Render target texture might be modified on the next render operations.
/// </summary>
Modified = 1 << 1,
/// <summary>
/// Render target texture has been signalled for modification and might be modified on the next render operations.
/// </summary>
BoundModified = Bound | Modified,
}
private readonly Texture[] _rtColors; private readonly Texture[] _rtColors;
private readonly ITexture[] _rtHostColors; private readonly ITexture[] _rtHostColors;
private readonly bool[] _rtColorsBound; private readonly BindState[] _rtColorsBound;
private Texture _rtDepthStencil; private Texture _rtDepthStencil;
private ITexture _rtHostDs; private ITexture _rtHostDs;
private bool _rtDsBound; private BindState _rtDsBound;
public int ClipRegionWidth { get; private set; } public int ClipRegionWidth { get; private set; }
public int ClipRegionHeight { get; private set; } public int ClipRegionHeight { get; private set; }
@ -53,7 +80,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_rtColors = new Texture[Constants.TotalRenderTargets]; _rtColors = new Texture[Constants.TotalRenderTargets];
_rtHostColors = new ITexture[Constants.TotalRenderTargets]; _rtHostColors = new ITexture[Constants.TotalRenderTargets];
_rtColorsBound = new bool[Constants.TotalRenderTargets]; _rtColorsBound = new BindState[Constants.TotalRenderTargets];
} }
/// <summary> /// <summary>
@ -149,29 +176,41 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
/// <param name="index">The index of the color buffer to set (up to 8)</param> /// <param name="index">The index of the color buffer to set (up to 8)</param>
/// <param name="color">The color buffer texture</param> /// <param name="color">The color buffer texture</param>
/// <param name="modified">Indicates if the following render operations will modidify <paramref name="color"/> contents</param>
/// <returns>True if render target scale must be updated.</returns> /// <returns>True if render target scale must be updated.</returns>
public bool SetRenderTargetColor(int index, Texture color) public bool SetRenderTargetColor(int index, Texture color, bool modified)
{ {
bool hasValue = color != null; bool hasValue = color != null;
bool changesScale = (hasValue != (_rtColors[index] != null)) || (hasValue && RenderTargetScale != color.ScaleFactor); bool changesScale = (hasValue != (_rtColors[index] != null)) || (hasValue && RenderTargetScale != color.ScaleFactor);
if (_rtColors[index] != color) if (_rtColors[index] != color)
{ {
if (_rtColorsBound[index]) Texture oldColor = _rtColors[index];
if (oldColor != null)
{ {
_rtColors[index]?.SignalModifying(false); if (_rtColorsBound[index].HasFlag(BindState.Bound))
}
else
{ {
_rtColorsBound[index] = true; oldColor.SignalModifying(false);
} }
oldColor.SignalBindingChange(false);
}
_rtColorsBound[index] = modified ? BindState.BoundModified : BindState.None;
if (color != null) if (color != null)
{ {
color.SynchronizeMemory(); color.SynchronizeMemory();
if (modified)
{
color.SignalModifying(true); color.SignalModifying(true);
} }
color.SignalBindingChange(true);
}
_rtColors[index] = color; _rtColors[index] = color;
} }
@ -182,29 +221,41 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Sets the render target depth-stencil buffer. /// Sets the render target depth-stencil buffer.
/// </summary> /// </summary>
/// <param name="depthStencil">The depth-stencil buffer texture</param> /// <param name="depthStencil">The depth-stencil buffer texture</param>
/// <param name="modified">Indicates if the following render operations will modidify <paramref name="depthStencil"/> contents</param>
/// <returns>True if render target scale must be updated.</returns> /// <returns>True if render target scale must be updated.</returns>
public bool SetRenderTargetDepthStencil(Texture depthStencil) public bool SetRenderTargetDepthStencil(Texture depthStencil, bool modified)
{ {
bool hasValue = depthStencil != null; bool hasValue = depthStencil != null;
bool changesScale = (hasValue != (_rtDepthStencil != null)) || (hasValue && RenderTargetScale != depthStencil.ScaleFactor); bool changesScale = (hasValue != (_rtDepthStencil != null)) || (hasValue && RenderTargetScale != depthStencil.ScaleFactor);
if (_rtDepthStencil != depthStencil) if (_rtDepthStencil != depthStencil)
{ {
if (_rtDsBound) Texture oldDepthStencil = _rtDepthStencil;
if (oldDepthStencil != null)
{ {
_rtDepthStencil?.SignalModifying(false); if (_rtDsBound.HasFlag(BindState.Bound))
}
else
{ {
_rtDsBound = true; oldDepthStencil.SignalModifying(false);
} }
oldDepthStencil.SignalBindingChange(false);
}
_rtDsBound = modified ? BindState.BoundModified : BindState.None;
if (depthStencil != null) if (depthStencil != null)
{ {
depthStencil.SynchronizeMemory(); depthStencil.SynchronizeMemory();
if (modified)
{
depthStencil.SignalModifying(true); depthStencil.SignalModifying(true);
} }
depthStencil.SignalBindingChange(true);
}
_rtDepthStencil = depthStencil; _rtDepthStencil = depthStencil;
} }
@ -437,10 +488,10 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
hostDsTexture = dsTexture.HostTexture; hostDsTexture = dsTexture.HostTexture;
if (!_rtDsBound) if (_rtDsBound == BindState.Modified)
{ {
dsTexture.SignalModifying(true); dsTexture.SignalModifying(true);
_rtDsBound = true; _rtDsBound |= BindState.Bound;
} }
} }
@ -459,10 +510,10 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
hostTexture = texture.HostTexture; hostTexture = texture.HostTexture;
if (!_rtColorsBound[index]) if (_rtColorsBound[index] == BindState.Modified)
{ {
texture.SignalModifying(true); texture.SignalModifying(true);
_rtColorsBound[index] = true; _rtColorsBound[index] |= BindState.Bound;
} }
} }
@ -500,24 +551,37 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
Texture dsTexture = _rtDepthStencil; Texture dsTexture = _rtDepthStencil;
if (dsTexture != null && _rtDsBound) if (dsTexture != null && _rtDsBound.HasFlag(BindState.Bound))
{ {
dsTexture.SignalModifying(false); dsTexture.SignalModifying(false);
_rtDsBound = false; _rtDsBound &= ~BindState.Bound;
} }
for (int index = 0; index < _rtColors.Length; index++) for (int index = 0; index < _rtColors.Length; index++)
{ {
Texture texture = _rtColors[index]; Texture texture = _rtColors[index];
if (texture != null && _rtColorsBound[index]) if (texture != null && _rtColorsBound[index].HasFlag(BindState.Bound))
{ {
texture.SignalModifying(false); texture.SignalModifying(false);
_rtColorsBound[index] = false; _rtColorsBound[index] &= ~BindState.Bound;
} }
} }
} }
/// <summary>
/// Indicates that the currently bound render targets might be modified, if they are used on the next render operation.
/// </summary>
public void SignalRenderTargetsModifiable()
{
_rtDsBound |= BindState.Modified;
for (int index = 0; index < _rtColorsBound.Length; index++)
{
_rtColorsBound[index] |= BindState.Modified;
}
}
/// <summary> /// <summary>
/// Forces the texture and sampler pools to be re-loaded from the cache on next use. /// Forces the texture and sampler pools to be re-loaded from the cache on next use.
/// </summary> /// </summary>
@ -553,20 +617,12 @@ namespace Ryujinx.Graphics.Gpu.Image
_samplerPoolCache.Dispose(); _samplerPoolCache.Dispose();
for (int i = 0; i < _rtColors.Length; i++) for (int i = 0; i < _rtColors.Length; i++)
{
if (_rtColorsBound[i])
{ {
_rtColors[i]?.DecrementReferenceCount(); _rtColors[i]?.DecrementReferenceCount();
}
_rtColors[i] = null; _rtColors[i] = null;
} }
if (_rtDsBound)
{
_rtDepthStencil?.DecrementReferenceCount(); _rtDepthStencil?.DecrementReferenceCount();
}
_rtDepthStencil = null; _rtDepthStencil = null;
} }
} }

View file

@ -170,6 +170,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// </summary> /// </summary>
public bool UsesRtLayer; public bool UsesRtLayer;
/// <summary>
/// Indicates that the fragment shader always discards the fragment, not producing any output for the bound render targets.
/// </summary>
public bool HasUnconditionalDiscard;
/// <summary> /// <summary>
/// Bit mask with the clip distances written on the vertex stage. /// Bit mask with the clip distances written on the vertex stage.
/// </summary> /// </summary>
@ -806,6 +811,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
dataInfo.UsesInstanceId, dataInfo.UsesInstanceId,
dataInfo.UsesDrawParameters, dataInfo.UsesDrawParameters,
dataInfo.UsesRtLayer, dataInfo.UsesRtLayer,
dataInfo.HasUnconditionalDiscard,
dataInfo.ClipDistancesWritten, dataInfo.ClipDistancesWritten,
dataInfo.FragmentOutputMap); dataInfo.FragmentOutputMap);
} }
@ -836,6 +842,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
UsesInstanceId = info.UsesInstanceId, UsesInstanceId = info.UsesInstanceId,
UsesDrawParameters = info.UsesDrawParameters, UsesDrawParameters = info.UsesDrawParameters,
UsesRtLayer = info.UsesRtLayer, UsesRtLayer = info.UsesRtLayer,
HasUnconditionalDiscard = info.HasUnconditionalDiscard,
ClipDistancesWritten = info.ClipDistancesWritten, ClipDistancesWritten = info.ClipDistancesWritten,
FragmentOutputMap = info.FragmentOutputMap, FragmentOutputMap = info.FragmentOutputMap,
}; };

View file

@ -23,6 +23,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
set => _branch = AddSuccessor(_branch, value); set => _branch = AddSuccessor(_branch, value);
} }
public bool HasSuccessor => _branch != null || _next != null;
public bool HasBranch => _branch != null; public bool HasBranch => _branch != null;
public bool Reachable => Index == 0 || Predecessors.Count != 0; public bool Reachable => Index == 0 || Predecessors.Count != 0;

View file

@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Shader
public bool UsesInstanceId { get; } public bool UsesInstanceId { get; }
public bool UsesDrawParameters { get; } public bool UsesDrawParameters { get; }
public bool UsesRtLayer { get; } public bool UsesRtLayer { get; }
public bool HasUnconditionalDiscard { get; }
public byte ClipDistancesWritten { get; } public byte ClipDistancesWritten { get; }
public int FragmentOutputMap { get; } public int FragmentOutputMap { get; }
@ -34,6 +35,7 @@ namespace Ryujinx.Graphics.Shader
bool usesInstanceId, bool usesInstanceId,
bool usesDrawParameters, bool usesDrawParameters,
bool usesRtLayer, bool usesRtLayer,
bool hasUnconditionalDiscard,
byte clipDistancesWritten, byte clipDistancesWritten,
int fragmentOutputMap) int fragmentOutputMap)
{ {
@ -50,6 +52,7 @@ namespace Ryujinx.Graphics.Shader
UsesInstanceId = usesInstanceId; UsesInstanceId = usesInstanceId;
UsesDrawParameters = usesDrawParameters; UsesDrawParameters = usesDrawParameters;
UsesRtLayer = usesRtLayer; UsesRtLayer = usesRtLayer;
HasUnconditionalDiscard = hasUnconditionalDiscard;
ClipDistancesWritten = clipDistancesWritten; ClipDistancesWritten = clipDistancesWritten;
FragmentOutputMap = fragmentOutputMap; FragmentOutputMap = fragmentOutputMap;
} }

View file

@ -26,5 +26,6 @@ namespace Ryujinx.Graphics.Shader.Translation
SharedMemory = 1 << 11, SharedMemory = 1 << 11,
Store = 1 << 12, Store = 1 << 12,
VtgAsCompute = 1 << 13, VtgAsCompute = 1 << 13,
UnconditionalDiscard = 1 << 14,
} }
} }

View file

@ -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;
}
}
}
}
}

View file

@ -300,6 +300,11 @@ namespace Ryujinx.Graphics.Shader.Translation
Optimizer.RunPass(context); Optimizer.RunPass(context);
TransformPasses.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); 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.InstanceId),
usedFeatures.HasFlag(FeatureFlags.DrawParameters), usedFeatures.HasFlag(FeatureFlags.DrawParameters),
usedFeatures.HasFlag(FeatureFlags.RtLayer), usedFeatures.HasFlag(FeatureFlags.RtLayer),
usedFeatures.HasFlag(FeatureFlags.UnconditionalDiscard),
clipDistancesWritten, clipDistancesWritten,
originalDefinitions.OmapTargets); originalDefinitions.OmapTargets);