Jelajahi Sumber

Implement HLE macros for render target clears (#3528)

* Implement HLE macros for render target clears

* Add constants for the offsets
gdkchan 3 tahun lalu
induk
melakukan
1080f64df9

+ 2 - 1
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,

+ 4 - 2
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);
         }
     }
 }

+ 4 - 2
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);
         }
     }
 }

+ 4 - 4
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<ClearRenderTargetColorCommand>().Set(index, layer, componentMask, color);
+            _renderer.New<ClearRenderTargetColorCommand>().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<ClearRenderTargetDepthStencilCommand>().Set(layer, depthValue, depthMask, stencilValue, stencilMask);
+            _renderer.New<ClearRenderTargetDepthStencilCommand>().Set(layer, layerCount, depthValue, depthMask, stencilValue, stencilMask);
             _renderer.QueueCommand();
         }
 

+ 0 - 1
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;

+ 35 - 0
Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs

@@ -12,6 +12,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
     /// </summary>
     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
             }
         }
 
+        /// <summary>
+        /// Clears one bound color target.
+        /// </summary>
+        /// <param name="state">GPU state at the time of the call</param>
+        /// <param name="arg0">First argument of the call</param>
+        private void ClearColor(IDeviceState state, int arg0)
+        {
+            int index = (arg0 >> 6) & 0xf;
+            int layerCount = state.Read(ColorLayerCountOffset + index * ColorStructSize);
+
+            _processor.ThreedClass.Clear(arg0, layerCount);
+        }
+
+        /// <summary>
+        /// Clears the current depth-stencil target.
+        /// </summary>
+        /// <param name="state">GPU state at the time of the call</param>
+        /// <param name="arg0">First argument of the call</param>
+        private void ClearDepthStencil(IDeviceState state, int arg0)
+        {
+            int layerCount = state.Read(ZetaLayerCountOffset);
+
+            _processor.ThreedClass.Clear(arg0, layerCount);
+        }
+
         /// <summary>
         /// Performs a indirect multi-draw, with parameters from a GPU buffer.
         /// </summary>

+ 2 - 0
Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs

@@ -6,6 +6,8 @@
     enum MacroHLEFunctionName
     {
         None,
+        ClearColor,
+        ClearDepthStencil,
         MultiDrawElementsIndirectCount
     }
 }

+ 8 - 1
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;
             }

+ 16 - 4
Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs

@@ -487,11 +487,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
 
         /// <summary>
         /// 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.
         /// </summary>
         /// <param name="engine">3D engine where this method is being called</param>
         /// <param name="argument">Method call argument</param>
         public void Clear(ThreedClass engine, int argument)
+        {
+            Clear(engine, argument, 1);
+        }
+
+        /// <summary>
+        /// Clears the current color and depth-stencil buffers.
+        /// Which buffers should be cleared can also specified with the arguments.
+        /// </summary>
+        /// <param name="engine">3D engine where this method is being called</param>
+        /// <param name="argument">Method call argument</param>
+        /// <param name="layerCount">For array and 3D textures, indicates how many layers should be cleared</param>
+        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,

+ 11 - 0
Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs

@@ -497,6 +497,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
             return 0;
         }
 
+        /// <summary>
+        /// Clears the current color and depth-stencil buffers.
+        /// Which buffers should be cleared can also specified with the arguments.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        /// <param name="layerCount">For array and 3D textures, indicates how many layers should be cleared</param>
+        public void Clear(int argument, int layerCount)
+        {
+            _drawManager.Clear(this, argument, layerCount);
+        }
+
         /// <summary>
         /// Performs a indirect multi-draw, with parameters from a GPU buffer.
         /// </summary>

+ 10 - 0
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];

+ 43 - 18
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);
+
+                    GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors);
+                }
 
-            _framebuffer.DetachColorLayerForClear(index);
+                _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,23 +158,22 @@ namespace Ryujinx.Graphics.OpenGL
                 GL.DepthMask(depthMask);
             }
 
-            _framebuffer.AttachDepthStencilLayerForClear(layer);
-
-            if (depthMask && stencilMask != 0)
-            {
-                GL.ClearBuffer(ClearBufferCombined.DepthStencil, 0, depthValue, stencilValue);
-            }
-            else if (depthMask)
+            if (layer != 0 || layerCount != _framebuffer.GetDepthStencilLayerCount())
             {
-                GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Depth, 0, ref depthValue);
+                for (int l = layer; l < layer + layerCount; l++)
+                {
+                    _framebuffer.AttachDepthStencilLayerForClear(l);
+
+                    ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask);
+                }
+
+                _framebuffer.DetachDepthStencilLayerForClear();
             }
-            else if (stencilMask != 0)
+            else
             {
-                GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Stencil, 0, ref stencilValue);
+                ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask);
             }
 
-            _framebuffer.DetachDepthStencilLayerForClear();
-
             if (stencilMaskChanged)
             {
                 GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask);
@@ -176,6 +185,22 @@ namespace Ryujinx.Graphics.OpenGL
             }
         }
 
+        private static void ClearDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask)
+        {
+            if (depthMask && stencilMask != 0)
+            {
+                GL.ClearBuffer(ClearBufferCombined.DepthStencil, 0, depthValue, stencilValue);
+            }
+            else if (depthMask)
+            {
+                GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Depth, 0, ref depthValue);
+            }
+            else if (stencilMask != 0)
+            {
+                GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Stencil, 0, ref stencilValue);
+            }
+        }
+
         public void CommandBufferBarrier()
         {
             GL.MemoryBarrier(MemoryBarrierFlags.CommandBarrierBit);

+ 2 - 2
Ryujinx.Graphics.Vulkan/FramebufferParams.cs

@@ -149,14 +149,14 @@ namespace Ryujinx.Graphics.Vulkan
             return texture is TextureView view && view.Valid;
         }
 
-        public ClearRect GetClearRect(Rectangle<int> scissor, int layer)
+        public ClearRect GetClearRect(Rectangle<int> 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<DisposableFramebuffer> Create(Vk api, CommandBufferScoped cbs, Auto<DisposableRenderPass> renderPass)

+ 1 - 1
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);

+ 4 - 4
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);
         }

+ 2 - 2
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);
             }
         }