From 1080f64df9abd7af7ed762668e4fc9a300ae30f2 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 4 Aug 2022 18:30:08 -0300 Subject: [PATCH] Implement HLE macros for render target clears (#3528) * Implement HLE macros for render target clears * Add constants for the offsets --- Ryujinx.Graphics.GAL/IPipeline.cs | 3 +- .../Commands/ClearRenderTargetColorCommand.cs | 6 +- .../ClearRenderTargetDepthStencilCommand.cs | 6 +- .../Multithreading/ThreadedPipeline.cs | 8 +-- .../Multithreading/ThreadedRenderer.cs | 1 - Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs | 35 +++++++++++ .../Engine/MME/MacroHLEFunctionName.cs | 2 + .../Engine/MME/MacroHLETable.cs | 9 ++- .../Engine/Threed/DrawManager.cs | 20 ++++-- .../Engine/Threed/ThreedClass.cs | 11 ++++ Ryujinx.Graphics.OpenGL/Framebuffer.cs | 10 +++ Ryujinx.Graphics.OpenGL/Pipeline.cs | 63 +++++++++++++------ Ryujinx.Graphics.Vulkan/FramebufferParams.cs | 4 +- Ryujinx.Graphics.Vulkan/HelperShader.cs | 2 +- Ryujinx.Graphics.Vulkan/PipelineBase.cs | 8 +-- Ryujinx.Graphics.Vulkan/PipelineFull.cs | 4 +- 16 files changed, 149 insertions(+), 43 deletions(-) diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs index 9d0da269b..69e99fea5 100644 --- a/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/Ryujinx.Graphics.GAL/IPipeline.cs @@ -11,10 +11,11 @@ namespace Ryujinx.Graphics.GAL void ClearBuffer(BufferHandle destination, int offset, int size, uint value); - void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color); + void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color); void ClearRenderTargetDepthStencil( int layer, + int layerCount, 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 cde69e7bb..00a5128a2 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetColorCommand.cs @@ -5,20 +5,22 @@ public CommandType CommandType => CommandType.ClearRenderTargetColor; private int _index; private int _layer; + private int _layerCount; private uint _componentMask; private ColorF _color; - public void Set(int index, int layer, uint componentMask, ColorF color) + public void Set(int index, int layer, int layerCount, uint componentMask, ColorF color) { _index = index; _layer = layer; + _layerCount = layerCount; _componentMask = componentMask; _color = color; } public static void Run(ref ClearRenderTargetColorCommand command, ThreadedRenderer threaded, IRenderer renderer) { - renderer.Pipeline.ClearRenderTargetColor(command._index, command._layer, command._componentMask, command._color); + renderer.Pipeline.ClearRenderTargetColor(command._index, command._layer, command._layerCount, command._componentMask, command._color); } } } diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs index c5c76539e..c9ebad215 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/ClearRenderTargetDepthStencilCommand.cs @@ -4,14 +4,16 @@ { public CommandType CommandType => CommandType.ClearRenderTargetDepthStencil; private int _layer; + private int _layerCount; private float _depthValue; private bool _depthMask; private int _stencilValue; private int _stencilMask; - public void Set(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask) + public void Set(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { _layer = layer; + _layerCount = layerCount; _depthValue = depthValue; _depthMask = depthMask; _stencilValue = stencilValue; @@ -20,7 +22,7 @@ public static void Run(ref ClearRenderTargetDepthStencilCommand command, ThreadedRenderer threaded, IRenderer renderer) { - renderer.Pipeline.ClearRenderTargetDepthStencil(command._layer, command._depthValue, command._depthMask, command._stencilValue, command._stencilMask); + renderer.Pipeline.ClearRenderTargetDepthStencil(command._layer, command._layerCount, 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 c462c5591..723d29f1f 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -41,15 +41,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color) + public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { - _renderer.New().Set(index, layer, componentMask, color); + _renderer.New().Set(index, layer, layerCount, componentMask, color); _renderer.QueueCommand(); } - public void ClearRenderTargetDepthStencil(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask) + public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { - _renderer.New().Set(layer, depthValue, depthMask, stencilValue, stencilMask); + _renderer.New().Set(layer, layerCount, depthValue, depthMask, stencilValue, stencilMask); _renderer.QueueCommand(); } diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index f05f37c9f..e105a0926 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -6,7 +6,6 @@ using Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer; using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Resources; using Ryujinx.Graphics.GAL.Multithreading.Resources.Programs; -using Ryujinx.Graphics.Shader; using System; using System.Diagnostics; using System.Runtime.CompilerServices; diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs index 05f3df0ee..5f238a718 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs @@ -12,6 +12,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME /// class MacroHLE : IMacroEE { + private const int ColorLayerCountOffset = 0x818; + private const int ColorStructSize = 0x40; + private const int ZetaLayerCountOffset = 0x1230; + private readonly GPFifoProcessor _processor; private readonly MacroHLEFunctionName _functionName; @@ -45,6 +49,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME { switch (_functionName) { + case MacroHLEFunctionName.ClearColor: + ClearColor(state, arg0); + break; + case MacroHLEFunctionName.ClearDepthStencil: + ClearDepthStencil(state, arg0); + break; case MacroHLEFunctionName.MultiDrawElementsIndirectCount: MultiDrawElementsIndirectCount(state, arg0); break; @@ -53,6 +63,31 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME } } + /// + /// Clears one bound color target. + /// + /// GPU state at the time of the call + /// First argument of the call + private void ClearColor(IDeviceState state, int arg0) + { + int index = (arg0 >> 6) & 0xf; + int layerCount = state.Read(ColorLayerCountOffset + index * ColorStructSize); + + _processor.ThreedClass.Clear(arg0, layerCount); + } + + /// + /// Clears the current depth-stencil target. + /// + /// GPU state at the time of the call + /// First argument of the call + private void ClearDepthStencil(IDeviceState state, int arg0) + { + int layerCount = state.Read(ZetaLayerCountOffset); + + _processor.ThreedClass.Clear(arg0, layerCount); + } + /// /// Performs a indirect multi-draw, with parameters from a GPU buffer. /// diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs index 60354a9bc..4cce07fa0 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs @@ -6,6 +6,8 @@ enum MacroHLEFunctionName { None, + ClearColor, + ClearDepthStencil, MultiDrawElementsIndirectCount } } diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs index 77d041adf..c5d988488 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs @@ -46,12 +46,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME private static readonly TableEntry[] Table = new TableEntry[] { + new TableEntry(MacroHLEFunctionName.ClearColor, new Hash128(0xA9FB28D1DC43645A, 0xB177E5D2EAE67FB0), 0x28), + new TableEntry(MacroHLEFunctionName.ClearDepthStencil, new Hash128(0x1B96CB77D4879F4F, 0x8557032FE0C965FB), 0x24), new TableEntry(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C) }; private static bool IsMacroHLESupported(Capabilities caps, MacroHLEFunctionName name) { - if (name == MacroHLEFunctionName.MultiDrawElementsIndirectCount) + if (name == MacroHLEFunctionName.ClearColor || + name == MacroHLEFunctionName.ClearDepthStencil) + { + return true; + } + else if (name == MacroHLEFunctionName.MultiDrawElementsIndirectCount) { return caps.SupportsIndirectParameters; } diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index e6a64205a..0791feefc 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -487,11 +487,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// /// Clears the current color and depth-stencil buffers. - /// Which buffers should be cleared is also specified on the argument. + /// Which buffers should be cleared can also be specified with the argument. /// /// 3D engine where this method is being called /// Method call argument public void Clear(ThreedClass engine, int argument) + { + Clear(engine, argument, 1); + } + + /// + /// Clears the current color and depth-stencil buffers. + /// Which buffers should be cleared can also specified with the arguments. + /// + /// 3D engine where this method is being called + /// Method call argument + /// For array and 3D textures, indicates how many layers should be cleared + public void Clear(ThreedClass engine, int argument, int layerCount) { ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable( _context, @@ -507,7 +519,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed int index = (argument >> 6) & 0xf; int layer = (argument >> 10) & 0x3ff; - engine.UpdateRenderTargetState(useControl: false, layered: layer != 0, singleUse: index); + engine.UpdateRenderTargetState(useControl: false, layered: layer != 0 || layerCount > 1, 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 @@ -578,7 +590,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed bool clearDepth = (argument & 1) != 0; bool clearStencil = (argument & 2) != 0; - uint componentMask = (uint)((argument >> 2) & 0xf); if (componentMask != 0) @@ -587,7 +598,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed ColorF color = new ColorF(clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha); - _context.Renderer.Pipeline.ClearRenderTargetColor(index, layer, componentMask, color); + _context.Renderer.Pipeline.ClearRenderTargetColor(index, layer, layerCount, componentMask, color); } if (clearDepth || clearStencil) @@ -609,6 +620,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _context.Renderer.Pipeline.ClearRenderTargetDepthStencil( layer, + layerCount, depthValue, clearDepth, stencilValue, diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index 95763910e..8e222e719 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -497,6 +497,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed return 0; } + /// + /// Clears the current color and depth-stencil buffers. + /// Which buffers should be cleared can also specified with the arguments. + /// + /// Method call argument + /// For array and 3D textures, indicates how many layers should be cleared + public void Clear(int argument, int layerCount) + { + _drawManager.Clear(this, argument, layerCount); + } + /// /// Performs a indirect multi-draw, with parameters from a GPU buffer. /// diff --git a/Ryujinx.Graphics.OpenGL/Framebuffer.cs b/Ryujinx.Graphics.OpenGL/Framebuffer.cs index dafa76723..d132e9c49 100644 --- a/Ryujinx.Graphics.OpenGL/Framebuffer.cs +++ b/Ryujinx.Graphics.OpenGL/Framebuffer.cs @@ -145,6 +145,16 @@ namespace Ryujinx.Graphics.OpenGL return format == Format.D16Unorm || format == Format.D32Float; } + public int GetColorLayerCount(int index) + { + return _colors[index].Info.GetDepthOrLayers(); + } + + public int GetDepthStencilLayerCount() + { + return _depthStencil?.Info.GetDepthOrLayers() ?? 0; + } + public void AttachColorLayerForClear(int index, int layer) { TextureView color = _colors[index]; diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 457f6a4af..58d165d93 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, int layer, uint componentMask, ColorF color) + public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { GL.ColorMask( index, @@ -119,18 +119,28 @@ 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); + if (layer != 0 || layerCount != _framebuffer.GetColorLayerCount(index)) + { + for (int l = layer; l < layer + layerCount; l++) + { + _framebuffer.AttachColorLayerForClear(index, l); - _framebuffer.DetachColorLayerForClear(index); + GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors); + } + + _framebuffer.DetachColorLayerForClear(index); + } + else + { + GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors); + } RestoreComponentMask(index); } - public void ClearRenderTargetDepthStencil(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask) + public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { bool stencilMaskChanged = stencilMask != 0 && @@ -148,8 +158,35 @@ namespace Ryujinx.Graphics.OpenGL GL.DepthMask(depthMask); } - _framebuffer.AttachDepthStencilLayerForClear(layer); + if (layer != 0 || layerCount != _framebuffer.GetDepthStencilLayerCount()) + { + for (int l = layer; l < layer + layerCount; l++) + { + _framebuffer.AttachDepthStencilLayerForClear(l); + ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask); + } + + _framebuffer.DetachDepthStencilLayerForClear(); + } + else + { + ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask); + } + + if (stencilMaskChanged) + { + GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask); + } + + if (depthMaskChanged) + { + GL.DepthMask(_depthMask); + } + } + + private static void ClearDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask) + { if (depthMask && stencilMask != 0) { GL.ClearBuffer(ClearBufferCombined.DepthStencil, 0, depthValue, stencilValue); @@ -162,18 +199,6 @@ namespace Ryujinx.Graphics.OpenGL { GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Stencil, 0, ref stencilValue); } - - _framebuffer.DetachDepthStencilLayerForClear(); - - if (stencilMaskChanged) - { - GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask); - } - - if (depthMaskChanged) - { - GL.DepthMask(_depthMask); - } } public void CommandBufferBarrier() diff --git a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index e5318e93b..0dadbf9cf 100644 --- a/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -149,14 +149,14 @@ namespace Ryujinx.Graphics.Vulkan return texture is TextureView view && view.Valid; } - public ClearRect GetClearRect(Rectangle scissor, int layer) + public ClearRect GetClearRect(Rectangle scissor, int layer, int layerCount) { int x = scissor.X; int y = scissor.Y; int width = Math.Min((int)Width - scissor.X, scissor.Width); int height = Math.Min((int)Height - scissor.Y, scissor.Height); - return new ClearRect(new Rect2D(new Offset2D(x, y), new Extent2D((uint)width, (uint)height)), (uint)layer, 1); + return new ClearRect(new Rect2D(new Offset2D(x, y), new Extent2D((uint)width, (uint)height)), (uint)layer, (uint)layerCount); } public unsafe Auto Create(Vk api, CommandBufferScoped cbs, Auto renderPass) diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index 53a03cfbe..8465c7444 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -157,7 +157,7 @@ namespace Ryujinx.Graphics.Vulkan if (clearAlpha) { - _pipeline.ClearRenderTargetColor(0, 0, new ColorF(0f, 0f, 0f, 1f)); + _pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f)); } _pipeline.SetViewports(viewports, false); diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs index d73b2a667..1f0080c45 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Vulkan size); } - public unsafe void ClearRenderTargetColor(int index, int layer, ColorF color) + public unsafe void ClearRenderTargetColor(int index, int layer, int layerCount, ColorF color) { if (FramebufferParams == null || !FramebufferParams.IsValidColorAttachment(index)) { @@ -178,12 +178,12 @@ namespace Ryujinx.Graphics.Vulkan var clearValue = new ClearValue(new ClearColorValue(color.Red, color.Green, color.Blue, color.Alpha)); var attachment = new ClearAttachment(ImageAspectFlags.ImageAspectColorBit, (uint)index, clearValue); - var clearRect = FramebufferParams?.GetClearRect(ClearScissor, layer) ?? default; + var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount); Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); } - public unsafe void ClearRenderTargetDepthStencil(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask) + public unsafe void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { // TODO: Use stencilMask (fully) @@ -208,7 +208,7 @@ namespace Ryujinx.Graphics.Vulkan } var attachment = new ClearAttachment(flags, 0, clearValue); - var clearRect = FramebufferParams?.GetClearRect(ClearScissor, layer) ?? default; + var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount); Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); } diff --git a/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/Ryujinx.Graphics.Vulkan/PipelineFull.cs index 8bb043829..4c76caf2d 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -49,7 +49,7 @@ namespace Ryujinx.Graphics.Vulkan _pendingQueryCopies.Clear(); } - public void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color) + public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { if (FramebufferParams == null) { @@ -85,7 +85,7 @@ namespace Ryujinx.Graphics.Vulkan } else { - ClearRenderTargetColor(index, layer, color); + ClearRenderTargetColor(index, layer, layerCount, color); } }