diff --git a/Ryujinx.Graphics.GAL/Extents2D.cs b/Ryujinx.Graphics.GAL/Extents2D.cs index e9e26af4..05b0ce63 100644 --- a/Ryujinx.Graphics.GAL/Extents2D.cs +++ b/Ryujinx.Graphics.GAL/Extents2D.cs @@ -1,3 +1,5 @@ +using Ryujinx.Common; + namespace Ryujinx.Graphics.GAL { public struct Extents2D @@ -14,5 +16,16 @@ namespace Ryujinx.Graphics.GAL X2 = x2; Y2 = y2; } + + public Extents2D Reduce(int level) + { + int div = 1 << level; + + return new Extents2D( + X1 >> level, + Y1 >> level, + BitUtils.DivRoundUp(X2, div), + BitUtils.DivRoundUp(Y2, div)); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs b/Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs index 430b733e..173f5fe4 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs @@ -71,12 +71,8 @@ namespace Ryujinx.Graphics.Gpu.Engine return; } - if (srcTexture.ScaleFactor != dstTexture.ScaleFactor) - { - srcTexture.PropagateScale(dstTexture); - } - - float scale = srcTexture.ScaleFactor; // src and dest scales are identical now. + float scale = srcTexture.ScaleFactor; + float dstScale = dstTexture.ScaleFactor; Extents2D srcRegion = new Extents2D( (int)Math.Ceiling(scale * (srcX1 / srcTexture.Info.SamplesInX)), @@ -85,10 +81,10 @@ namespace Ryujinx.Graphics.Gpu.Engine (int)Math.Ceiling(scale * (srcY2 / srcTexture.Info.SamplesInY))); Extents2D dstRegion = new Extents2D( - (int)Math.Ceiling(scale * (dstX1 / dstTexture.Info.SamplesInX)), - (int)Math.Ceiling(scale * (dstY1 / dstTexture.Info.SamplesInY)), - (int)Math.Ceiling(scale * (dstX2 / dstTexture.Info.SamplesInX)), - (int)Math.Ceiling(scale * (dstY2 / dstTexture.Info.SamplesInY))); + (int)Math.Ceiling(dstScale * (dstX1 / dstTexture.Info.SamplesInX)), + (int)Math.Ceiling(dstScale * (dstY1 / dstTexture.Info.SamplesInY)), + (int)Math.Ceiling(dstScale * (dstX2 / dstTexture.Info.SamplesInX)), + (int)Math.Ceiling(dstScale * (dstY2 / dstTexture.Info.SamplesInY))); bool linearFilter = control.UnpackLinearFilter(); @@ -107,10 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Engine srcCopyTexture.Height++; srcTexture = TextureManager.FindOrCreateTexture(srcCopyTexture, srcCopyTextureFormat, srcTexture.ScaleMode == TextureScaleMode.Scaled, srcHint); - if (srcTexture.ScaleFactor != dstTexture.ScaleFactor) - { - srcTexture.PropagateScale(dstTexture); - } + scale = srcTexture.ScaleFactor; srcRegion = new Extents2D( (int)Math.Ceiling(scale * ((srcX1 / srcTexture.Info.SamplesInX) - srcTexture.Info.Width)), diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 21418a4b..7fb41572 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -430,48 +430,6 @@ namespace Ryujinx.Graphics.Gpu.Image } } - /// - /// Helper method for copying our Texture2DArray texture to the given target, with scaling. - /// This creates temporary views for each array layer on both textures, copying each one at a time. - /// - /// The texture array to copy to - private void CopyArrayScaled(ITexture target) - { - TextureInfo viewInfo = new TextureInfo( - Info.Address, - Info.Width, - Info.Height, - 1, - Info.Levels, - Info.SamplesInX, - Info.SamplesInY, - Info.Stride, - Info.IsLinear, - Info.GobBlocksInY, - Info.GobBlocksInZ, - Info.GobBlocksInTileX, - Target.Texture2D, - Info.FormatInfo, - Info.DepthStencilMode, - Info.SwizzleR, - Info.SwizzleG, - Info.SwizzleB, - Info.SwizzleA); - - TextureCreateInfo createInfo = TextureManager.GetCreateInfo(viewInfo, _context.Capabilities, ScaleFactor); - - for (int i = 0; i < Info.DepthOrLayers; i++) - { - ITexture from = HostTexture.CreateView(createInfo, i, 0); - ITexture to = target.CreateView(createInfo, i, 0); - - from.CopyTo(to, new Extents2D(0, 0, from.Width, from.Height), new Extents2D(0, 0, to.Width, to.Height), true); - - from.Release(); - to.Release(); - } - } - /// /// Copy the host texture to a scaled one. If a texture is not provided, create it with the given scale. /// @@ -486,14 +444,7 @@ namespace Ryujinx.Graphics.Gpu.Image storage = _context.Renderer.CreateTexture(createInfo, scale); } - if (Info.Target == Target.Texture2DArray) - { - CopyArrayScaled(storage); - } - else - { - HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true); - } + HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true); return storage; } diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index f1d31f4f..8e64ca61 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -387,7 +387,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// True if eligible public bool IsUpscaleCompatible(TextureInfo info) { - return (info.Target == Target.Texture2D || info.Target == Target.Texture2DArray) && info.Levels == 1 && !info.FormatInfo.IsCompressed && UpscaleSafeMode(info); + return (info.Target == Target.Texture2D || info.Target == Target.Texture2DArray) && !info.FormatInfo.IsCompressed && UpscaleSafeMode(info); } /// @@ -401,6 +401,13 @@ namespace Ryujinx.Graphics.Gpu.Image // While upscaling works for all targets defined by IsUpscaleCompatible, we additionally blacklist targets here that // may have undesirable results (upscaling blur textures) or simply waste GPU resources (upscaling texture atlas). + if (info.Levels > 3) + { + // Textures with more than 3 levels are likely to be game textures, rather than render textures. + // Small textures with full mips are likely to be removed by the next check. + return false; + } + if (!(info.FormatInfo.Format.IsDepthOrStencil() || info.FormatInfo.Components == 1)) { // Discount square textures that aren't depth-stencil like. (excludes game textures, cubemap faces, most 3D texture LUT, texture atlas) diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs index 74832dd8..20a3b914 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs @@ -34,38 +34,61 @@ namespace Ryujinx.Graphics.OpenGL.Image GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy()); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy()); - Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle); - Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle); + int levels = Math.Min(src.Info.Levels, dst.Info.Levels); + int layers = Math.Min(src.Info.GetLayers(), dst.Info.GetLayers()); - ClearBufferMask mask = GetMask(src.Format); - - if ((mask & (ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit)) != 0 || src.Format.IsInteger()) + for (int level = 0; level < levels; level++) { - linearFilter = false; + for (int layer = 0; layer < layers; layer++) + { + if (layers > 1) + { + Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level, layer); + Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level, layer); + } + else + { + Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level); + Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level); + } + + ClearBufferMask mask = GetMask(src.Format); + + if ((mask & (ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit)) != 0 || src.Format.IsInteger()) + { + linearFilter = false; + } + + BlitFramebufferFilter filter = linearFilter + ? BlitFramebufferFilter.Linear + : BlitFramebufferFilter.Nearest; + + GL.ReadBuffer(ReadBufferMode.ColorAttachment0); + GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + + GL.Disable(EnableCap.RasterizerDiscard); + GL.Disable(IndexedEnableCap.ScissorTest, 0); + + GL.BlitFramebuffer( + srcRegion.X1, + srcRegion.Y1, + srcRegion.X2, + srcRegion.Y2, + dstRegion.X1, + dstRegion.Y1, + dstRegion.X2, + dstRegion.Y2, + mask, + filter); + } + + if (level < levels - 1) + { + srcRegion = srcRegion.Reduce(1); + dstRegion = dstRegion.Reduce(1); + } } - BlitFramebufferFilter filter = linearFilter - ? BlitFramebufferFilter.Linear - : BlitFramebufferFilter.Nearest; - - GL.ReadBuffer(ReadBufferMode.ColorAttachment0); - GL.DrawBuffer(DrawBufferMode.ColorAttachment0); - - GL.Disable(EnableCap.RasterizerDiscard); - GL.Disable(IndexedEnableCap.ScissorTest, 0); - - GL.BlitFramebuffer( - srcRegion.X1, - srcRegion.Y1, - srcRegion.X2, - srcRegion.Y2, - dstRegion.X1, - dstRegion.Y1, - dstRegion.X2, - dstRegion.Y2, - mask, - filter); - Attach(FramebufferTarget.ReadFramebuffer, src.Format, 0); Attach(FramebufferTarget.DrawFramebuffer, dst.Format, 0); @@ -191,26 +214,40 @@ namespace Ryujinx.Graphics.OpenGL.Image } } - private static void Attach(FramebufferTarget target, Format format, int handle) + private static FramebufferAttachment AttachmentForFormat(Format format) { if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint) { - GL.FramebufferTexture(target, FramebufferAttachment.DepthStencilAttachment, handle, 0); + return FramebufferAttachment.DepthStencilAttachment; } else if (IsDepthOnly(format)) { - GL.FramebufferTexture(target, FramebufferAttachment.DepthAttachment, handle, 0); + return FramebufferAttachment.DepthAttachment; } else if (format == Format.S8Uint) { - GL.FramebufferTexture(target, FramebufferAttachment.StencilAttachment, handle, 0); + return FramebufferAttachment.StencilAttachment; } else { - GL.FramebufferTexture(target, FramebufferAttachment.ColorAttachment0, handle, 0); + return FramebufferAttachment.ColorAttachment0; } } + private static void Attach(FramebufferTarget target, Format format, int handle, int level = 0) + { + FramebufferAttachment attachment = AttachmentForFormat(format); + + GL.FramebufferTexture(target, attachment, handle, level); + } + + private static void Attach(FramebufferTarget target, Format format, int handle, int level, int layer) + { + FramebufferAttachment attachment = AttachmentForFormat(format); + + GL.FramebufferTextureLayer(target, attachment, handle, level, layer); + } + private static ClearBufferMask GetMask(Format format) { if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint)