From a3e7bb8eb40b66e61a5a3bfef0b780d0c76a31c1 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 5 Jun 2022 14:06:47 -0300 Subject: [PATCH] Copy dependency for multisample and non-multisample textures (#3382) * Use copy dependency for textures that differs in multisample but are otherwise compatible * Remove allowMs flag as it's no longer required for correctness, it's just an optimization now * Dispose intermmediate pool --- Ryujinx.Graphics.GAL/Target.cs | 8 ++ Ryujinx.Graphics.Gpu/Image/Texture.cs | 20 +--- Ryujinx.Graphics.Gpu/Image/TextureCache.cs | 2 - .../Image/TextureCompatibility.cs | 6 +- .../Image/IntermmediatePool.cs | 98 +++++++++++++++++++ Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs | 51 ++++++++-- Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 69 ++++++++++++- 7 files changed, 225 insertions(+), 29 deletions(-) create mode 100644 Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs diff --git a/Ryujinx.Graphics.GAL/Target.cs b/Ryujinx.Graphics.GAL/Target.cs index a62d628a8..e20bd3c84 100644 --- a/Ryujinx.Graphics.GAL/Target.cs +++ b/Ryujinx.Graphics.GAL/Target.cs @@ -13,4 +13,12 @@ namespace Ryujinx.Graphics.GAL CubemapArray, TextureBuffer } + + public static class TargetExtensions + { + public static bool IsMultisample(this Target target) + { + return target == Target.Texture2DMultisample || target == Target.Texture2DMultisampleArray; + } + } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index cfb7a3b76..aadb4260b 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1136,32 +1136,22 @@ namespace Ryujinx.Graphics.Gpu.Image /// Texture view physical memory ranges /// Layer size on the given texture /// Host GPU capabilities - /// Indicates that multisample textures are allowed to match non-multisample requested textures /// Texture view initial layer on this texture /// Texture view first mipmap level on this texture /// The level of compatiblilty a view with the given parameters created from this texture has - public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, bool allowMs, out int firstLayer, out int firstLevel) + public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, out int firstLayer, out int firstLevel) { TextureViewCompatibility result = TextureViewCompatibility.Full; result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps)); if (result != TextureViewCompatibility.Incompatible) { - bool msTargetCompatible = false; + result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info)); - if (allowMs) + bool bothMs = Info.Target.IsMultisample() && info.Target.IsMultisample(); + if (bothMs && (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY)) { - msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D; - } - - if (!msTargetCompatible) - { - result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info)); - - if (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY) - { - result = TextureViewCompatibility.Incompatible; - } + result = TextureViewCompatibility.Incompatible; } if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 4fa80c95d..045410571 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -547,7 +547,6 @@ namespace Ryujinx.Graphics.Gpu.Image range.Value, sizeInfo.LayerSize, _context.Capabilities, - flags.HasFlag(TextureSearchFlags.ForCopy), out int firstLayer, out int firstLevel); @@ -662,7 +661,6 @@ namespace Ryujinx.Graphics.Gpu.Image overlap.Range, overlap.LayerSize, _context.Capabilities, - false, out int firstLayer, out int firstLevel); diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index b798441f1..61b48dc4d 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -2,7 +2,6 @@ using Ryujinx.Common; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Texture; using System; -using System.Numerics; namespace Ryujinx.Graphics.Gpu.Image { @@ -657,6 +656,11 @@ namespace Ryujinx.Graphics.Gpu.Image case Target.Texture2DMultisample: case Target.Texture2DMultisampleArray: + if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray) + { + return TextureViewCompatibility.CopyOnly; + } + result = rhs.Target == Target.Texture2DMultisample || rhs.Target == Target.Texture2DMultisampleArray; break; diff --git a/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs b/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs new file mode 100644 index 000000000..bd6efc763 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs @@ -0,0 +1,98 @@ +using Ryujinx.Graphics.GAL; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.OpenGL.Image +{ + class IntermmediatePool : IDisposable + { + private readonly Renderer _renderer; + private readonly List _entries; + + public IntermmediatePool(Renderer renderer) + { + _renderer = renderer; + _entries = new List(); + } + + public TextureView GetOrCreateWithAtLeast( + Target target, + int blockWidth, + int blockHeight, + int bytesPerPixel, + Format format, + int width, + int height, + int depth, + int levels) + { + TextureView entry; + + for (int i = 0; i < _entries.Count; i++) + { + entry = _entries[i]; + + if (entry.Target == target && entry.Format == format) + { + if (entry.Width < width || entry.Height < height || entry.Info.Depth < depth || entry.Info.Levels < levels) + { + width = Math.Max(width, entry.Width); + height = Math.Max(height, entry.Height); + depth = Math.Max(depth, entry.Info.Depth); + levels = Math.Max(levels, entry.Info.Levels); + + entry.Dispose(); + entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels); + _entries[i] = entry; + } + + return entry; + } + } + + entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels); + _entries.Add(entry); + + return entry; + } + + private TextureView CreateNew( + Target target, + int blockWidth, + int blockHeight, + int bytesPerPixel, + Format format, + int width, + int height, + int depth, + int levels) + { + return (TextureView)_renderer.CreateTexture(new TextureCreateInfo( + width, + height, + depth, + levels, + 1, + blockWidth, + blockHeight, + bytesPerPixel, + format, + DepthStencilMode.Depth, + target, + SwizzleComponent.Red, + SwizzleComponent.Green, + SwizzleComponent.Blue, + SwizzleComponent.Alpha), 1f); + } + + public void Dispose() + { + foreach (TextureView entry in _entries) + { + entry.Dispose(); + } + + _entries.Clear(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs index 9be865613..44804d436 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs @@ -9,6 +9,8 @@ namespace Ryujinx.Graphics.OpenGL.Image { private readonly Renderer _renderer; + public IntermmediatePool IntermmediatePool { get; } + private int _srcFramebuffer; private int _dstFramebuffer; @@ -18,6 +20,7 @@ namespace Ryujinx.Graphics.OpenGL.Image public TextureCopy(Renderer renderer) { _renderer = renderer; + IntermmediatePool = new IntermmediatePool(renderer); } public void Copy( @@ -25,7 +28,30 @@ namespace Ryujinx.Graphics.OpenGL.Image TextureView dst, Extents2D srcRegion, Extents2D dstRegion, - bool linearFilter) + bool linearFilter, + int srcLayer = 0, + int dstLayer = 0, + int srcLevel = 0, + int dstLevel = 0) + { + int levels = Math.Min(src.Info.Levels - srcLevel, dst.Info.Levels - dstLevel); + int layers = Math.Min(src.Info.GetLayers() - srcLayer, dst.Info.GetLayers() - dstLayer); + + Copy(src, dst, srcRegion, dstRegion, linearFilter, srcLayer, dstLayer, srcLevel, dstLevel, layers, levels); + } + + public void Copy( + TextureView src, + TextureView dst, + Extents2D srcRegion, + Extents2D dstRegion, + bool linearFilter, + int srcLayer, + int dstLayer, + int srcLevel, + int dstLevel, + int layers, + int levels) { TextureView srcConverted = src.Format.IsBgr() != dst.Format.IsBgr() ? BgraSwap(src) : src; @@ -34,22 +60,29 @@ namespace Ryujinx.Graphics.OpenGL.Image GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy()); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy()); - int levels = Math.Min(src.Info.Levels, dst.Info.Levels); - int layers = Math.Min(src.Info.GetLayers(), dst.Info.GetLayers()); + if (srcLevel != 0) + { + srcRegion = srcRegion.Reduce(srcLevel); + } + + if (dstLevel != 0) + { + dstRegion = dstRegion.Reduce(dstLevel); + } for (int level = 0; level < levels; level++) { for (int layer = 0; layer < layers; layer++) { - if (layers > 1) + if ((srcLayer | dstLayer) != 0 || layers > 1) { - Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level, layer); - Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level, layer); + Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level, srcLayer + layer); + Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level, dstLayer + layer); } else { - Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level); - Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level); + Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level); + Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level); } ClearBufferMask mask = GetMask(src.Format); @@ -484,6 +517,8 @@ namespace Ryujinx.Graphics.OpenGL.Image _copyPboHandle = 0; } + + IntermmediatePool.Dispose(); } } } diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index 909a06200..afb9a278c 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -115,14 +115,77 @@ namespace Ryujinx.Graphics.OpenGL.Image { TextureView destinationView = (TextureView)destination; - _renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel); + if (destinationView.Target.IsMultisample() || Target.IsMultisample()) + { + Extents2D srcRegion = new Extents2D(0, 0, Width, Height); + Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height); + + TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast( + GetIntermmediateTarget(Target), + Info.BlockWidth, + Info.BlockHeight, + Info.BytesPerPixel, + Format, + Width, + Height, + Info.Depth, + Info.Levels); + + GL.Disable(EnableCap.FramebufferSrgb); + + _renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true); + _renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, firstLayer, 0, firstLevel); + + GL.Enable(EnableCap.FramebufferSrgb); + } + else + { + _renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel); + } } public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) { - TextureView destinationView = (TextureView)destination; + TextureView destinationView = (TextureView)destination; - _renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); + if (destinationView.Target.IsMultisample() || Target.IsMultisample()) + { + Extents2D srcRegion = new Extents2D(0, 0, Width, Height); + Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height); + + TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast( + GetIntermmediateTarget(Target), + Info.BlockWidth, + Info.BlockHeight, + Info.BytesPerPixel, + Format, + Math.Max(1, Width >> srcLevel), + Math.Max(1, Height >> srcLevel), + 1, + 1); + + GL.Disable(EnableCap.FramebufferSrgb); + + _renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true, srcLayer, 0, srcLevel, 0, 1, 1); + _renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, dstLayer, 0, dstLevel, 1, 1); + + GL.Enable(EnableCap.FramebufferSrgb); + } + else + { + _renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); + } + } + + private static Target GetIntermmediateTarget(Target srcTarget) + { + return srcTarget switch + { + Target.Texture2D => Target.Texture2DMultisample, + Target.Texture2DArray => Target.Texture2DMultisampleArray, + Target.Texture2DMultisampleArray => Target.Texture2DArray, + _ => Target.Texture2D + }; } public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)