Support Array/3D depth-stencil render target, and single layer clears (#3400)

* Support Array/3D depth-stencil render target, and single layer clears

* Alignment
This commit is contained in:
gdkchan 2022-06-14 13:30:39 -03:00 committed by GitHub
parent b1bd6a50b5
commit 851f56b08a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 157 additions and 34 deletions

View file

@ -10,9 +10,10 @@ namespace Ryujinx.Graphics.GAL
void ClearBuffer(BufferHandle destination, int offset, int size, uint value); void ClearBuffer(BufferHandle destination, int offset, int size, uint value);
void ClearRenderTargetColor(int index, uint componentMask, ColorF color); void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color);
void ClearRenderTargetDepthStencil( void ClearRenderTargetDepthStencil(
int layer,
float depthValue, float depthValue,
bool depthMask, bool depthMask,
int stencilValue, int stencilValue,

View file

@ -4,19 +4,21 @@
{ {
public CommandType CommandType => CommandType.ClearRenderTargetColor; public CommandType CommandType => CommandType.ClearRenderTargetColor;
private int _index; private int _index;
private int _layer;
private uint _componentMask; private uint _componentMask;
private ColorF _color; private ColorF _color;
public void Set(int index, uint componentMask, ColorF color) public void Set(int index, int layer, uint componentMask, ColorF color)
{ {
_index = index; _index = index;
_layer = layer;
_componentMask = componentMask; _componentMask = componentMask;
_color = color; _color = color;
} }
public static void Run(ref ClearRenderTargetColorCommand command, ThreadedRenderer threaded, IRenderer renderer) public static void Run(ref ClearRenderTargetColorCommand command, ThreadedRenderer threaded, IRenderer renderer)
{ {
renderer.Pipeline.ClearRenderTargetColor(command._index, command._componentMask, command._color); renderer.Pipeline.ClearRenderTargetColor(command._index, command._layer, command._componentMask, command._color);
} }
} }
} }

View file

@ -3,13 +3,15 @@
struct ClearRenderTargetDepthStencilCommand : IGALCommand struct ClearRenderTargetDepthStencilCommand : IGALCommand
{ {
public CommandType CommandType => CommandType.ClearRenderTargetDepthStencil; public CommandType CommandType => CommandType.ClearRenderTargetDepthStencil;
private int _layer;
private float _depthValue; private float _depthValue;
private bool _depthMask; private bool _depthMask;
private int _stencilValue; private int _stencilValue;
private int _stencilMask; private int _stencilMask;
public void Set(float depthValue, bool depthMask, int stencilValue, int stencilMask) public void Set(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask)
{ {
_layer = layer;
_depthValue = depthValue; _depthValue = depthValue;
_depthMask = depthMask; _depthMask = depthMask;
_stencilValue = stencilValue; _stencilValue = stencilValue;
@ -18,7 +20,7 @@
public static void Run(ref ClearRenderTargetDepthStencilCommand command, ThreadedRenderer threaded, IRenderer renderer) public static void Run(ref ClearRenderTargetDepthStencilCommand command, ThreadedRenderer threaded, IRenderer renderer)
{ {
renderer.Pipeline.ClearRenderTargetDepthStencil(command._depthValue, command._depthMask, command._stencilValue, command._stencilMask); renderer.Pipeline.ClearRenderTargetDepthStencil(command._layer, command._depthValue, command._depthMask, command._stencilValue, command._stencilMask);
} }
} }
} }

View file

@ -40,15 +40,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void ClearRenderTargetColor(int index, uint componentMask, ColorF color) public void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color)
{ {
_renderer.New<ClearRenderTargetColorCommand>().Set(index, componentMask, color); _renderer.New<ClearRenderTargetColorCommand>().Set(index, layer, componentMask, color);
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void ClearRenderTargetDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask) public void ClearRenderTargetDepthStencil(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask)
{ {
_renderer.New<ClearRenderTargetDepthStencilCommand>().Set(depthValue, depthMask, stencilValue, stencilMask); _renderer.New<ClearRenderTargetDepthStencilCommand>().Set(layer, depthValue, depthMask, stencilValue, stencilMask);
_renderer.QueueCommand(); _renderer.QueueCommand();
} }

View file

@ -505,8 +505,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
} }
int index = (argument >> 6) & 0xf; int index = (argument >> 6) & 0xf;
int layer = (argument >> 10) & 0x3ff;
engine.UpdateRenderTargetState(useControl: false, singleUse: index); engine.UpdateRenderTargetState(useControl: false, layered: layer != 0, singleUse: index);
// If there is a mismatch on the host clip region and the one explicitly defined by the guest // 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 // on the screen scissor state, then we need to force only one texture to be bound to avoid
@ -581,7 +582,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
ColorF color = new ColorF(clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha); ColorF color = new ColorF(clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha);
_context.Renderer.Pipeline.ClearRenderTargetColor(index, componentMask, color); _context.Renderer.Pipeline.ClearRenderTargetColor(index, layer, componentMask, color);
} }
if (clearDepth || clearStencil) if (clearDepth || clearStencil)
@ -602,6 +603,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
} }
_context.Renderer.Pipeline.ClearRenderTargetDepthStencil( _context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
layer,
depthValue, depthValue,
clearDepth, clearDepth,
stencilValue, stencilValue,

View file

@ -362,8 +362,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Updates render targets (color and depth-stencil buffers) based on current render target state. /// Updates render targets (color and depth-stencil buffers) based on current render target state.
/// </summary> /// </summary>
/// <param name="useControl">Use draw buffers information from render target control register</param> /// <param name="useControl">Use draw buffers information from render target control register</param>
/// <param name="layered">Indicates if the texture is layered</param>
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param> /// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
public void UpdateRenderTargetState(bool useControl, int singleUse = -1) public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1)
{ {
var memoryManager = _channel.MemoryManager; var memoryManager = _channel.MemoryManager;
var rtControl = _state.State.RtControl; var rtControl = _state.State.RtControl;
@ -399,7 +400,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture( Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
memoryManager, memoryManager,
colorState, colorState,
_vtgWritesRtLayer, _vtgWritesRtLayer || layered,
samplesInX, samplesInX,
samplesInY, samplesInY,
sizeHint); sizeHint);
@ -433,6 +434,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
memoryManager, memoryManager,
dsState, dsState,
dsSize, dsSize,
_vtgWritesRtLayer || layered,
samplesInX, samplesInX,
samplesInY, samplesInY,
sizeHint); sizeHint);

View file

@ -131,10 +131,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Updates render targets (color and depth-stencil buffers) based on current render target state. /// Updates render targets (color and depth-stencil buffers) based on current render target state.
/// </summary> /// </summary>
/// <param name="useControl">Use draw buffers information from render target control register</param> /// <param name="useControl">Use draw buffers information from render target control register</param>
/// <param name="layered">Indicates if the texture is layered</param>
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param> /// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
public void UpdateRenderTargetState(bool useControl, int singleUse = -1) public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1)
{ {
_stateUpdater.UpdateRenderTargetState(useControl, singleUse); _stateUpdater.UpdateRenderTargetState(useControl, layered, singleUse);
} }
/// <summary> /// <summary>

View file

@ -349,6 +349,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param> /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
/// <param name="dsState">Depth-stencil buffer texture to find or create</param> /// <param name="dsState">Depth-stencil buffer texture to find or create</param>
/// <param name="size">Size of the depth-stencil texture</param> /// <param name="size">Size of the depth-stencil texture</param>
/// <param name="layered">Indicates if the texture might be accessed with a non-zero layer index</param>
/// <param name="samplesInX">Number of samples in the X direction, for MSAA</param> /// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
/// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param> /// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param> /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
@ -357,6 +358,7 @@ namespace Ryujinx.Graphics.Gpu.Image
MemoryManager memoryManager, MemoryManager memoryManager,
RtDepthStencilState dsState, RtDepthStencilState dsState,
Size3D size, Size3D size,
bool layered,
int samplesInX, int samplesInX,
int samplesInY, int samplesInY,
Size sizeHint) Size sizeHint)
@ -364,9 +366,24 @@ namespace Ryujinx.Graphics.Gpu.Image
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY(); int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ(); int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
Target target = (samplesInX | samplesInY) != 1 Target target;
? Target.Texture2DMultisample
if (dsState.MemoryLayout.UnpackIsTarget3D())
{
target = Target.Texture3D;
}
else if ((samplesInX | samplesInY) != 1)
{
target = size.Depth > 1 && layered
? Target.Texture2DMultisampleArray
: Target.Texture2DMultisample;
}
else
{
target = size.Depth > 1 && layered
? Target.Texture2DArray
: Target.Texture2D; : Target.Texture2D;
}
FormatInfo formatInfo = dsState.Format.Convert(); FormatInfo formatInfo = dsState.Format.Convert();

View file

@ -9,10 +9,13 @@ namespace Ryujinx.Graphics.OpenGL
class Framebuffer : IDisposable class Framebuffer : IDisposable
{ {
public int Handle { get; private set; } public int Handle { get; private set; }
private int _clearFbHandle;
private bool _clearFbInitialized;
private FramebufferAttachment _lastDsAttachment; private FramebufferAttachment _lastDsAttachment;
private readonly TextureView[] _colors; private readonly TextureView[] _colors;
private TextureView _depthStencil;
private int _colorsCount; private int _colorsCount;
private bool _dualSourceBlend; private bool _dualSourceBlend;
@ -20,6 +23,7 @@ namespace Ryujinx.Graphics.OpenGL
public Framebuffer() public Framebuffer()
{ {
Handle = GL.GenFramebuffer(); Handle = GL.GenFramebuffer();
_clearFbHandle = GL.GenFramebuffer();
_colors = new TextureView[8]; _colors = new TextureView[8];
} }
@ -55,20 +59,7 @@ namespace Ryujinx.Graphics.OpenGL
if (depthStencil != null) if (depthStencil != null)
{ {
FramebufferAttachment attachment; FramebufferAttachment attachment = GetAttachment(depthStencil.Format);
if (IsPackedDepthStencilFormat(depthStencil.Format))
{
attachment = FramebufferAttachment.DepthStencilAttachment;
}
else if (IsDepthOnlyFormat(depthStencil.Format))
{
attachment = FramebufferAttachment.DepthAttachment;
}
else
{
attachment = FramebufferAttachment.StencilAttachment;
}
GL.FramebufferTexture( GL.FramebufferTexture(
FramebufferTarget.Framebuffer, FramebufferTarget.Framebuffer,
@ -82,6 +73,8 @@ namespace Ryujinx.Graphics.OpenGL
{ {
_lastDsAttachment = 0; _lastDsAttachment = 0;
} }
_depthStencil = depthStencil;
} }
public void SetDualSourceBlend(bool enable) public void SetDualSourceBlend(bool enable)
@ -124,6 +117,22 @@ namespace Ryujinx.Graphics.OpenGL
GL.DrawBuffers(colorsCount, drawBuffers); GL.DrawBuffers(colorsCount, drawBuffers);
} }
private static FramebufferAttachment GetAttachment(Format format)
{
if (IsPackedDepthStencilFormat(format))
{
return FramebufferAttachment.DepthStencilAttachment;
}
else if (IsDepthOnlyFormat(format))
{
return FramebufferAttachment.DepthAttachment;
}
else
{
return FramebufferAttachment.StencilAttachment;
}
}
private static bool IsPackedDepthStencilFormat(Format format) private static bool IsPackedDepthStencilFormat(Format format)
{ {
return format == Format.D24UnormS8Uint || return format == Format.D24UnormS8Uint ||
@ -136,6 +145,78 @@ namespace Ryujinx.Graphics.OpenGL
return format == Format.D16Unorm || format == Format.D32Float; return format == Format.D16Unorm || format == Format.D32Float;
} }
public void AttachColorLayerForClear(int index, int layer)
{
TextureView color = _colors[index];
if (!IsLayered(color))
{
return;
}
BindClearFb();
GL.FramebufferTextureLayer(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0 + index, color.Handle, 0, layer);
}
public void DetachColorLayerForClear(int index)
{
TextureView color = _colors[index];
if (!IsLayered(color))
{
return;
}
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0 + index, 0, 0);
Bind();
}
public void AttachDepthStencilLayerForClear(int layer)
{
TextureView depthStencil = _depthStencil;
if (!IsLayered(depthStencil))
{
return;
}
BindClearFb();
GL.FramebufferTextureLayer(FramebufferTarget.Framebuffer, GetAttachment(depthStencil.Format), depthStencil.Handle, 0, layer);
}
public void DetachDepthStencilLayerForClear()
{
TextureView depthStencil = _depthStencil;
if (!IsLayered(depthStencil))
{
return;
}
GL.FramebufferTexture(FramebufferTarget.Framebuffer, GetAttachment(depthStencil.Format), 0, 0);
Bind();
}
private void BindClearFb()
{
GL.BindFramebuffer(FramebufferTarget.Framebuffer, _clearFbHandle);
if (!_clearFbInitialized)
{
SetDrawBuffersImpl(Constants.MaxRenderTargets);
_clearFbInitialized = true;
}
}
private static bool IsLayered(TextureView view)
{
return view != null &&
view.Target != Target.Texture1D &&
view.Target != Target.Texture2D &&
view.Target != Target.Texture2DMultisample &&
view.Target != Target.TextureBuffer;
}
public void Dispose() public void Dispose()
{ {
if (Handle != 0) if (Handle != 0)
@ -144,6 +225,13 @@ namespace Ryujinx.Graphics.OpenGL
Handle = 0; Handle = 0;
} }
if (_clearFbHandle != 0)
{
GL.DeleteFramebuffer(_clearFbHandle);
_clearFbHandle = 0;
}
} }
} }
} }

View file

@ -110,7 +110,7 @@ namespace Ryujinx.Graphics.OpenGL
Buffer.Clear(destination, offset, size, value); Buffer.Clear(destination, offset, size, value);
} }
public void ClearRenderTargetColor(int index, uint componentMask, ColorF color) public void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color)
{ {
GL.ColorMask( GL.ColorMask(
index, index,
@ -119,14 +119,18 @@ namespace Ryujinx.Graphics.OpenGL
(componentMask & 4) != 0, (componentMask & 4) != 0,
(componentMask & 8) != 0); (componentMask & 8) != 0);
_framebuffer.AttachColorLayerForClear(index, layer);
float[] colors = new float[] { color.Red, color.Green, color.Blue, color.Alpha }; float[] colors = new float[] { color.Red, color.Green, color.Blue, color.Alpha };
GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors); GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors);
_framebuffer.DetachColorLayerForClear(index);
RestoreComponentMask(index); RestoreComponentMask(index);
} }
public void ClearRenderTargetDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask) public void ClearRenderTargetDepthStencil(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask)
{ {
bool stencilMaskChanged = bool stencilMaskChanged =
stencilMask != 0 && stencilMask != 0 &&
@ -144,6 +148,8 @@ namespace Ryujinx.Graphics.OpenGL
GL.DepthMask(depthMask); GL.DepthMask(depthMask);
} }
_framebuffer.AttachDepthStencilLayerForClear(layer);
if (depthMask && stencilMask != 0) if (depthMask && stencilMask != 0)
{ {
GL.ClearBuffer(ClearBufferCombined.DepthStencil, 0, depthValue, stencilValue); GL.ClearBuffer(ClearBufferCombined.DepthStencil, 0, depthValue, stencilValue);
@ -157,6 +163,8 @@ namespace Ryujinx.Graphics.OpenGL
GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Stencil, 0, ref stencilValue); GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Stencil, 0, ref stencilValue);
} }
_framebuffer.DetachDepthStencilLayerForClear();
if (stencilMaskChanged) if (stencilMaskChanged)
{ {
GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask); GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask);