Allow copy destination to have a different scale from source (#1711)
* Allow copy destination to have a different scale from source Will result in more scaled copy destinations, but allows scaling in some games that copy textures to the output framebuffer. * Support copying multiple levels/layers Uses glFramebufferTextureLayer to copy multiple layers, copies levels individually (and scales the regions). Remove CopyArrayScaled, since the backend copy handles it now.
This commit is contained in:
parent
cf7044e37b
commit
9493cdfe55
5 changed files with 99 additions and 98 deletions
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)),
|
||||
|
|
|
@ -430,48 +430,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="target">The texture array to copy to</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the host texture to a scaled one. If a texture is not provided, create it with the given scale.
|
||||
/// </summary>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
return storage;
|
||||
}
|
||||
|
|
|
@ -387,7 +387,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// <returns>True if eligible</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -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)
|
||||
|
|
|
@ -34,8 +34,23 @@ 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());
|
||||
|
||||
for (int level = 0; level < levels; level++)
|
||||
{
|
||||
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);
|
||||
|
||||
|
@ -65,6 +80,14 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||
dstRegion.Y2,
|
||||
mask,
|
||||
filter);
|
||||
}
|
||||
|
||||
if (level < levels - 1)
|
||||
{
|
||||
srcRegion = srcRegion.Reduce(1);
|
||||
dstRegion = dstRegion.Reduce(1);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
Reference in a new issue