Fix render target clear when sizes mismatch (#2994)
This commit is contained in:
parent
ef24c8983d
commit
6e0799580f
9 changed files with 181 additions and 25 deletions
|
@ -1,4 +1,3 @@
|
||||||
using Ryujinx.Graphics.Shader;
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
@ -15,9 +14,9 @@ namespace Ryujinx.Graphics.GAL
|
||||||
|
|
||||||
void ClearRenderTargetDepthStencil(
|
void ClearRenderTargetDepthStencil(
|
||||||
float depthValue,
|
float depthValue,
|
||||||
bool depthMask,
|
bool depthMask,
|
||||||
int stencilValue,
|
int stencilValue,
|
||||||
int stencilMask);
|
int stencilMask);
|
||||||
|
|
||||||
void CommandBufferBarrier();
|
void CommandBufferBarrier();
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,16 @@ namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
public struct RectangleF
|
public struct RectangleF
|
||||||
{
|
{
|
||||||
public float X { get; }
|
public float X { get; }
|
||||||
public float Y { get; }
|
public float Y { get; }
|
||||||
public float Width { get; }
|
public float Width { get; }
|
||||||
public float Height { get; }
|
public float Height { get; }
|
||||||
|
|
||||||
public RectangleF(float x, float y, float width, float height)
|
public RectangleF(float x, float y, float width, float height)
|
||||||
{
|
{
|
||||||
X = x;
|
X = x;
|
||||||
Y = y;
|
Y = y;
|
||||||
Width = width;
|
Width = width;
|
||||||
Height = height;
|
Height = height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
|
@ -489,14 +490,62 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scissor and rasterizer discard also affect clears.
|
|
||||||
engine.UpdateState((1UL << StateUpdater.RasterizerStateIndex) | (1UL << StateUpdater.ScissorStateIndex));
|
|
||||||
|
|
||||||
int index = (argument >> 6) & 0xf;
|
int index = (argument >> 6) & 0xf;
|
||||||
|
|
||||||
engine.UpdateRenderTargetState(useControl: false, singleUse: index);
|
engine.UpdateRenderTargetState(useControl: false, singleUse: index);
|
||||||
|
|
||||||
_channel.TextureManager.UpdateRenderTargets();
|
// If there is a mismatch on the host clip region and the one explicitly defined by the guest
|
||||||
|
// on the screen scissor state, then we need to force only one texture to be bound to avoid
|
||||||
|
// host clipping.
|
||||||
|
var screenScissorState = _state.State.ScreenScissorState;
|
||||||
|
|
||||||
|
// Must happen after UpdateRenderTargetState to have up-to-date clip region values.
|
||||||
|
bool clipMismatch = (screenScissorState.X | screenScissorState.Y) != 0 ||
|
||||||
|
screenScissorState.Width != _channel.TextureManager.ClipRegionWidth ||
|
||||||
|
screenScissorState.Height != _channel.TextureManager.ClipRegionHeight;
|
||||||
|
|
||||||
|
bool clearAffectedByStencilMask = (_state.State.ClearFlags & 1) != 0;
|
||||||
|
bool clearAffectedByScissor = (_state.State.ClearFlags & 0x100) != 0;
|
||||||
|
bool needsCustomScissor = !clearAffectedByScissor || clipMismatch;
|
||||||
|
|
||||||
|
// Scissor and rasterizer discard also affect clears.
|
||||||
|
ulong updateMask = 1UL << StateUpdater.RasterizerStateIndex;
|
||||||
|
|
||||||
|
if (!needsCustomScissor)
|
||||||
|
{
|
||||||
|
updateMask |= 1UL << StateUpdater.ScissorStateIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
engine.UpdateState(updateMask);
|
||||||
|
|
||||||
|
if (needsCustomScissor)
|
||||||
|
{
|
||||||
|
int scissorX = screenScissorState.X;
|
||||||
|
int scissorY = screenScissorState.Y;
|
||||||
|
int scissorW = screenScissorState.Width;
|
||||||
|
int scissorH = screenScissorState.Height;
|
||||||
|
|
||||||
|
if (clearAffectedByScissor)
|
||||||
|
{
|
||||||
|
ref var scissorState = ref _state.State.ScissorState[0];
|
||||||
|
|
||||||
|
scissorX = Math.Max(scissorX, scissorState.X1);
|
||||||
|
scissorY = Math.Max(scissorY, scissorState.Y1);
|
||||||
|
scissorW = Math.Min(scissorW, scissorState.X2 - scissorState.X1);
|
||||||
|
scissorH = Math.Min(scissorH, scissorState.Y2 - scissorState.Y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Renderer.Pipeline.SetScissor(0, true, scissorX, scissorY, scissorW, scissorH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clipMismatch)
|
||||||
|
{
|
||||||
|
_channel.TextureManager.UpdateRenderTarget(index);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_channel.TextureManager.UpdateRenderTargets();
|
||||||
|
}
|
||||||
|
|
||||||
bool clearDepth = (argument & 1) != 0;
|
bool clearDepth = (argument & 1) != 0;
|
||||||
bool clearStencil = (argument & 2) != 0;
|
bool clearStencil = (argument & 2) != 0;
|
||||||
|
@ -521,7 +570,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
|
|
||||||
if (clearStencil)
|
if (clearStencil)
|
||||||
{
|
{
|
||||||
stencilMask = _state.State.StencilTestState.FrontMask;
|
stencilMask = clearAffectedByStencilMask ? _state.State.StencilTestState.FrontMask : 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clipMismatch)
|
||||||
|
{
|
||||||
|
_channel.TextureManager.UpdateRenderTargetDepthStencil();
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
|
_context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
|
||||||
|
@ -531,6 +585,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
stencilMask);
|
stencilMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (needsCustomScissor)
|
||||||
|
{
|
||||||
|
engine.UpdateScissorState();
|
||||||
|
}
|
||||||
|
|
||||||
engine.UpdateRenderTargetState(useControl: true);
|
engine.UpdateRenderTargetState(useControl: true);
|
||||||
|
|
||||||
if (renderEnable == ConditionalRenderEnabled.Host)
|
if (renderEnable == ConditionalRenderEnabled.Host)
|
||||||
|
|
|
@ -339,6 +339,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
var scissor = _state.State.ScreenScissorState;
|
var scissor = _state.State.ScreenScissorState;
|
||||||
Size sizeHint = new Size(scissor.X + scissor.Width, scissor.Y + scissor.Height, 1);
|
Size sizeHint = new Size(scissor.X + scissor.Width, scissor.Y + scissor.Height, 1);
|
||||||
|
|
||||||
|
int clipRegionWidth = int.MaxValue;
|
||||||
|
int clipRegionHeight = int.MaxValue;
|
||||||
|
|
||||||
bool changedScale = false;
|
bool changedScale = false;
|
||||||
|
|
||||||
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
||||||
|
@ -363,6 +366,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
sizeHint);
|
sizeHint);
|
||||||
|
|
||||||
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color);
|
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color);
|
||||||
|
|
||||||
|
if (color != null)
|
||||||
|
{
|
||||||
|
if (clipRegionWidth > color.Width)
|
||||||
|
{
|
||||||
|
clipRegionWidth = color.Width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clipRegionHeight > color.Height)
|
||||||
|
{
|
||||||
|
clipRegionHeight = color.Height;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dsEnable = _state.State.RtDepthStencilEnable;
|
bool dsEnable = _state.State.RtDepthStencilEnable;
|
||||||
|
@ -381,6 +397,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
samplesInX,
|
samplesInX,
|
||||||
samplesInY,
|
samplesInY,
|
||||||
sizeHint);
|
sizeHint);
|
||||||
|
|
||||||
|
if (depthStencil != null)
|
||||||
|
{
|
||||||
|
if (clipRegionWidth > depthStencil.Width)
|
||||||
|
{
|
||||||
|
clipRegionWidth = depthStencil.Width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clipRegionHeight > depthStencil.Height)
|
||||||
|
{
|
||||||
|
clipRegionHeight = depthStencil.Height;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changedScale |= _channel.TextureManager.SetRenderTargetDepthStencil(depthStencil);
|
changedScale |= _channel.TextureManager.SetRenderTargetDepthStencil(depthStencil);
|
||||||
|
@ -398,6 +427,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
UpdateScissorState();
|
UpdateScissorState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_channel.TextureManager.SetClipRegion(clipRegionWidth, clipRegionHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -414,7 +445,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates host scissor test state based on current GPU state.
|
/// Updates host scissor test state based on current GPU state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateScissorState()
|
public void UpdateScissorState()
|
||||||
{
|
{
|
||||||
for (int index = 0; index < Constants.TotalViewports; index++)
|
for (int index = 0; index < Constants.TotalViewports; index++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -137,6 +137,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
_stateUpdater.UpdateRenderTargetState(useControl, singleUse);
|
_stateUpdater.UpdateRenderTargetState(useControl, singleUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates scissor based on current render target state.
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateScissorState()
|
||||||
|
{
|
||||||
|
_stateUpdater.UpdateScissorState();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marks the entire state as dirty, forcing a full host state update before the next draw.
|
/// Marks the entire state as dirty, forcing a full host state update before the next draw.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -754,7 +754,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
public int DrawTextureTextureId;
|
public int DrawTextureTextureId;
|
||||||
public int DrawTextureSrcX;
|
public int DrawTextureSrcX;
|
||||||
public int DrawTextureSrcY;
|
public int DrawTextureSrcY;
|
||||||
public fixed uint Reserved10B0[44];
|
public fixed uint Reserved10B0[18];
|
||||||
|
public uint ClearFlags;
|
||||||
|
public fixed uint Reserved10FC[25];
|
||||||
public Array16<VertexAttribState> VertexAttribState;
|
public Array16<VertexAttribState> VertexAttribState;
|
||||||
public fixed uint Reserved11A0[31];
|
public fixed uint Reserved11A0[31];
|
||||||
public RtControl RtControl;
|
public RtControl RtControl;
|
||||||
|
|
|
@ -47,6 +47,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Target Target { get; private set; }
|
public Target Target { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Texture width.
|
||||||
|
/// </summary>
|
||||||
|
public int Width { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Texture height.
|
||||||
|
/// </summary>
|
||||||
|
public int Height { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Texture information.
|
/// Texture information.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -926,7 +936,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
FlushTextureDataToGuest(tracked);
|
FlushTextureDataToGuest(tracked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a host texture to use for flushing the texture, at 1x resolution.
|
/// Gets a host texture to use for flushing the texture, at 1x resolution.
|
||||||
/// If the HostTexture is already at 1x resolution, it is returned directly.
|
/// If the HostTexture is already at 1x resolution, it is returned directly.
|
||||||
|
@ -1322,6 +1332,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
Info = info;
|
Info = info;
|
||||||
Target = info.Target;
|
Target = info.Target;
|
||||||
|
Width = info.Width;
|
||||||
|
Height = info.Height;
|
||||||
CanForceAnisotropy = CanTextureForceAnisotropy();
|
CanForceAnisotropy = CanTextureForceAnisotropy();
|
||||||
|
|
||||||
_depth = info.GetDepth();
|
_depth = info.GetDepth();
|
||||||
|
|
|
@ -19,6 +19,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
private Texture _rtDepthStencil;
|
private Texture _rtDepthStencil;
|
||||||
private ITexture _rtHostDs;
|
private ITexture _rtHostDs;
|
||||||
|
|
||||||
|
public int ClipRegionWidth { get; private set; }
|
||||||
|
public int ClipRegionHeight { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The scaling factor applied to all currently bound render targets.
|
/// The scaling factor applied to all currently bound render targets.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -210,6 +213,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
return changesScale || ScaleNeedsUpdated(depthStencil);
|
return changesScale || ScaleNeedsUpdated(depthStencil);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the host clip region, which should be the intersection of all render target texture sizes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="width">Width of the clip region, defined as the minimum width across all bound textures</param>
|
||||||
|
/// <param name="height">Height of the clip region, defined as the minimum height across all bound textures</param>
|
||||||
|
public void SetClipRegion(int width, int height)
|
||||||
|
{
|
||||||
|
ClipRegionWidth = width;
|
||||||
|
ClipRegionHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the first available bound colour target, or the depth stencil target if not present.
|
/// Gets the first available bound colour target, or the depth stencil target if not present.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -409,6 +423,35 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update host framebuffer attachments based on currently bound render target buffers.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// All attachments other than <paramref name="index"/> will be unbound.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="index">Index of the render target color to be updated</param>
|
||||||
|
public void UpdateRenderTarget(int index)
|
||||||
|
{
|
||||||
|
new Span<ITexture>(_rtHostColors).Fill(null);
|
||||||
|
_rtHostColors[index] = _rtColors[index]?.HostTexture;
|
||||||
|
|
||||||
|
_context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update host framebuffer attachments based on currently bound render target buffers.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// All color attachments will be unbound.
|
||||||
|
/// </remarks>
|
||||||
|
public void UpdateRenderTargetDepthStencil()
|
||||||
|
{
|
||||||
|
new Span<ITexture>(_rtHostColors).Fill(null);
|
||||||
|
_rtHostDs = _rtDepthStencil?.HostTexture;
|
||||||
|
|
||||||
|
_context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Forces all textures, samplers, images and render targets to be rebound the next time
|
/// Forces all textures, samplers, images and render targets to be rebound the next time
|
||||||
/// CommitGraphicsBindings is called.
|
/// CommitGraphicsBindings is called.
|
||||||
|
|
|
@ -6,7 +6,6 @@ using Ryujinx.Graphics.OpenGL.Queries;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.OpenGL
|
namespace Ryujinx.Graphics.OpenGL
|
||||||
{
|
{
|
||||||
|
@ -1058,14 +1057,17 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
|
|
||||||
_framebuffer.AttachColor(index, color);
|
_framebuffer.AttachColor(index, color);
|
||||||
|
|
||||||
int isBgra = color != null && color.Format.IsBgr() ? 1 : 0;
|
if (color != null)
|
||||||
|
|
||||||
if (_fpIsBgra[index].X != isBgra)
|
|
||||||
{
|
{
|
||||||
_fpIsBgra[index].X = isBgra;
|
int isBgra = color.Format.IsBgr() ? 1 : 0;
|
||||||
isBgraChanged = true;
|
|
||||||
|
|
||||||
RestoreComponentMask(index);
|
if (_fpIsBgra[index].X != isBgra)
|
||||||
|
{
|
||||||
|
_fpIsBgra[index].X = isBgra;
|
||||||
|
isBgraChanged = true;
|
||||||
|
|
||||||
|
RestoreComponentMask(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue