From 851f56b08a0c3b420f91143b6c6c007b429174a8 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 14 Jun 2022 13:30:39 -0300 Subject: [PATCH] Support Array/3D depth-stencil render target, and single layer clears (#3400) * Support Array/3D depth-stencil render target, and single layer clears * Alignment --- Ryujinx.Graphics.GAL/IPipeline.cs | 3 +- .../Commands/ClearRenderTargetColorCommand.cs | 6 +- .../ClearRenderTargetDepthStencilCommand.cs | 6 +- .../Multithreading/ThreadedPipeline.cs | 8 +- .../Engine/Threed/DrawManager.cs | 6 +- .../Engine/Threed/StateUpdater.cs | 6 +- .../Engine/Threed/ThreedClass.cs | 5 +- Ryujinx.Graphics.Gpu/Image/TextureCache.cs | 23 +++- Ryujinx.Graphics.OpenGL/Framebuffer.cs | 116 +++++++++++++++--- Ryujinx.Graphics.OpenGL/Pipeline.cs | 12 +- 10 files changed, 157 insertions(+), 34 deletions(-) diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs index aec096e7..83afcaa3 100644 --- a/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/Ryujinx.Graphics.GAL/IPipeline.cs @@ -10,9 +10,10 @@ namespace Ryujinx.Graphics.GAL 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( + int layer, float depthValue, bool depthMask, int stencilValue, diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs index 57509f1c..cde69e7b 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs @@ -4,19 +4,21 @@ { public CommandType CommandType => CommandType.ClearRenderTargetColor; private int _index; + private int _layer; private uint _componentMask; 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; + _layer = layer; _componentMask = componentMask; _color = color; } 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); } } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs index 3692cd37..c5c76539 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs @@ -3,13 +3,15 @@ struct ClearRenderTargetDepthStencilCommand : IGALCommand { public CommandType CommandType => CommandType.ClearRenderTargetDepthStencil; + private int _layer; private float _depthValue; private bool _depthMask; private int _stencilValue; 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; _depthMask = depthMask; _stencilValue = stencilValue; @@ -18,7 +20,7 @@ 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); } } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index 010ee7e6..2a1f474a 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -40,15 +40,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void ClearRenderTargetColor(int index, uint componentMask, ColorF color) + public void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color) { - _renderer.New().Set(index, componentMask, color); + _renderer.New().Set(index, layer, componentMask, color); _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().Set(depthValue, depthMask, stencilValue, stencilMask); + _renderer.New().Set(layer, depthValue, depthMask, stencilValue, stencilMask); _renderer.QueueCommand(); } diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index ab371314..f90baf99 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -505,8 +505,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } 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 // 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); - _context.Renderer.Pipeline.ClearRenderTargetColor(index, componentMask, color); + _context.Renderer.Pipeline.ClearRenderTargetColor(index, layer, componentMask, color); } if (clearDepth || clearStencil) @@ -602,6 +603,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } _context.Renderer.Pipeline.ClearRenderTargetDepthStencil( + layer, depthValue, clearDepth, stencilValue, diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index d0c3bc5a..f648479b 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -362,8 +362,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Updates render targets (color and depth-stencil buffers) based on current render target state. /// /// Use draw buffers information from render target control register + /// Indicates if the texture is layered /// If this is not -1, it indicates that only the given indexed target will be used. - public void UpdateRenderTargetState(bool useControl, int singleUse = -1) + public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1) { var memoryManager = _channel.MemoryManager; var rtControl = _state.State.RtControl; @@ -399,7 +400,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture( memoryManager, colorState, - _vtgWritesRtLayer, + _vtgWritesRtLayer || layered, samplesInX, samplesInY, sizeHint); @@ -433,6 +434,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed memoryManager, dsState, dsSize, + _vtgWritesRtLayer || layered, samplesInX, samplesInY, sizeHint); diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index a2e8c64c..764ba239 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -131,10 +131,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Updates render targets (color and depth-stencil buffers) based on current render target state. /// /// Use draw buffers information from render target control register + /// Indicates if the texture is layered /// If this is not -1, it indicates that only the given indexed target will be used. - 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); } /// diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 04541057..ba863a1e 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -349,6 +349,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// GPU memory manager where the texture is mapped /// Depth-stencil buffer texture to find or create /// Size of the depth-stencil texture + /// Indicates if the texture might be accessed with a non-zero layer index /// Number of samples in the X direction, for MSAA /// Number of samples in the Y direction, for MSAA /// A hint indicating the minimum used size for the texture @@ -357,6 +358,7 @@ namespace Ryujinx.Graphics.Gpu.Image MemoryManager memoryManager, RtDepthStencilState dsState, Size3D size, + bool layered, int samplesInX, int samplesInY, Size sizeHint) @@ -364,9 +366,24 @@ namespace Ryujinx.Graphics.Gpu.Image int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY(); int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ(); - Target target = (samplesInX | samplesInY) != 1 - ? Target.Texture2DMultisample - : Target.Texture2D; + Target target; + + 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; + } FormatInfo formatInfo = dsState.Format.Convert(); diff --git a/Ryujinx.Graphics.OpenGL/Framebuffer.cs b/Ryujinx.Graphics.OpenGL/Framebuffer.cs index da928b4c..dafa7672 100644 --- a/Ryujinx.Graphics.OpenGL/Framebuffer.cs +++ b/Ryujinx.Graphics.OpenGL/Framebuffer.cs @@ -9,10 +9,13 @@ namespace Ryujinx.Graphics.OpenGL class Framebuffer : IDisposable { public int Handle { get; private set; } + private int _clearFbHandle; + private bool _clearFbInitialized; private FramebufferAttachment _lastDsAttachment; private readonly TextureView[] _colors; + private TextureView _depthStencil; private int _colorsCount; private bool _dualSourceBlend; @@ -20,6 +23,7 @@ namespace Ryujinx.Graphics.OpenGL public Framebuffer() { Handle = GL.GenFramebuffer(); + _clearFbHandle = GL.GenFramebuffer(); _colors = new TextureView[8]; } @@ -55,20 +59,7 @@ namespace Ryujinx.Graphics.OpenGL if (depthStencil != null) { - FramebufferAttachment attachment; - - if (IsPackedDepthStencilFormat(depthStencil.Format)) - { - attachment = FramebufferAttachment.DepthStencilAttachment; - } - else if (IsDepthOnlyFormat(depthStencil.Format)) - { - attachment = FramebufferAttachment.DepthAttachment; - } - else - { - attachment = FramebufferAttachment.StencilAttachment; - } + FramebufferAttachment attachment = GetAttachment(depthStencil.Format); GL.FramebufferTexture( FramebufferTarget.Framebuffer, @@ -82,6 +73,8 @@ namespace Ryujinx.Graphics.OpenGL { _lastDsAttachment = 0; } + + _depthStencil = depthStencil; } public void SetDualSourceBlend(bool enable) @@ -124,6 +117,22 @@ namespace Ryujinx.Graphics.OpenGL 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) { return format == Format.D24UnormS8Uint || @@ -136,6 +145,78 @@ namespace Ryujinx.Graphics.OpenGL 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() { if (Handle != 0) @@ -144,6 +225,13 @@ namespace Ryujinx.Graphics.OpenGL Handle = 0; } + + if (_clearFbHandle != 0) + { + GL.DeleteFramebuffer(_clearFbHandle); + + _clearFbHandle = 0; + } } } } diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 637e4606..62d4dee9 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -110,7 +110,7 @@ namespace Ryujinx.Graphics.OpenGL 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( index, @@ -119,14 +119,18 @@ namespace Ryujinx.Graphics.OpenGL (componentMask & 4) != 0, (componentMask & 8) != 0); + _framebuffer.AttachColorLayerForClear(index, layer); + float[] colors = new float[] { color.Red, color.Green, color.Blue, color.Alpha }; GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors); + _framebuffer.DetachColorLayerForClear(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 = stencilMask != 0 && @@ -144,6 +148,8 @@ namespace Ryujinx.Graphics.OpenGL GL.DepthMask(depthMask); } + _framebuffer.AttachDepthStencilLayerForClear(layer); + if (depthMask && stencilMask != 0) { 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); } + _framebuffer.DetachDepthStencilLayerForClear(); + if (stencilMaskChanged) { GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask);