diff --git a/Ryujinx.Graphics.GAL/Target.cs b/Ryujinx.Graphics.GAL/Target.cs
index a62d628a85..e20bd3c84d 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 cfb7a3b76a..aadb4260bc 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 4fa80c95d8..0454105717 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 b798441f13..61b48dc4d5 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 0000000000..bd6efc763f
--- /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 9be8656139..44804d436b 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 909a06200d..afb9a278cb 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)