forked from Mirror/Ryujinx
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
|
namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
public struct Extents2D
|
public struct Extents2D
|
||||||
|
@ -14,5 +16,16 @@ namespace Ryujinx.Graphics.GAL
|
||||||
X2 = x2;
|
X2 = x2;
|
||||||
Y2 = y2;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (srcTexture.ScaleFactor != dstTexture.ScaleFactor)
|
float scale = srcTexture.ScaleFactor;
|
||||||
{
|
float dstScale = dstTexture.ScaleFactor;
|
||||||
srcTexture.PropagateScale(dstTexture);
|
|
||||||
}
|
|
||||||
|
|
||||||
float scale = srcTexture.ScaleFactor; // src and dest scales are identical now.
|
|
||||||
|
|
||||||
Extents2D srcRegion = new Extents2D(
|
Extents2D srcRegion = new Extents2D(
|
||||||
(int)Math.Ceiling(scale * (srcX1 / srcTexture.Info.SamplesInX)),
|
(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)));
|
(int)Math.Ceiling(scale * (srcY2 / srcTexture.Info.SamplesInY)));
|
||||||
|
|
||||||
Extents2D dstRegion = new Extents2D(
|
Extents2D dstRegion = new Extents2D(
|
||||||
(int)Math.Ceiling(scale * (dstX1 / dstTexture.Info.SamplesInX)),
|
(int)Math.Ceiling(dstScale * (dstX1 / dstTexture.Info.SamplesInX)),
|
||||||
(int)Math.Ceiling(scale * (dstY1 / dstTexture.Info.SamplesInY)),
|
(int)Math.Ceiling(dstScale * (dstY1 / dstTexture.Info.SamplesInY)),
|
||||||
(int)Math.Ceiling(scale * (dstX2 / dstTexture.Info.SamplesInX)),
|
(int)Math.Ceiling(dstScale * (dstX2 / dstTexture.Info.SamplesInX)),
|
||||||
(int)Math.Ceiling(scale * (dstY2 / dstTexture.Info.SamplesInY)));
|
(int)Math.Ceiling(dstScale * (dstY2 / dstTexture.Info.SamplesInY)));
|
||||||
|
|
||||||
bool linearFilter = control.UnpackLinearFilter();
|
bool linearFilter = control.UnpackLinearFilter();
|
||||||
|
|
||||||
|
@ -107,10 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
srcCopyTexture.Height++;
|
srcCopyTexture.Height++;
|
||||||
|
|
||||||
srcTexture = TextureManager.FindOrCreateTexture(srcCopyTexture, srcCopyTextureFormat, srcTexture.ScaleMode == TextureScaleMode.Scaled, srcHint);
|
srcTexture = TextureManager.FindOrCreateTexture(srcCopyTexture, srcCopyTextureFormat, srcTexture.ScaleMode == TextureScaleMode.Scaled, srcHint);
|
||||||
if (srcTexture.ScaleFactor != dstTexture.ScaleFactor)
|
scale = srcTexture.ScaleFactor;
|
||||||
{
|
|
||||||
srcTexture.PropagateScale(dstTexture);
|
|
||||||
}
|
|
||||||
|
|
||||||
srcRegion = new Extents2D(
|
srcRegion = new Extents2D(
|
||||||
(int)Math.Ceiling(scale * ((srcX1 / srcTexture.Info.SamplesInX) - srcTexture.Info.Width)),
|
(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>
|
/// <summary>
|
||||||
/// Copy the host texture to a scaled one. If a texture is not provided, create it with the given scale.
|
/// Copy the host texture to a scaled one. If a texture is not provided, create it with the given scale.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -486,14 +444,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
storage = _context.Renderer.CreateTexture(createInfo, scale);
|
storage = _context.Renderer.CreateTexture(createInfo, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Info.Target == Target.Texture2DArray)
|
HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true);
|
||||||
{
|
|
||||||
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;
|
return storage;
|
||||||
}
|
}
|
||||||
|
|
|
@ -387,7 +387,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <returns>True if eligible</returns>
|
/// <returns>True if eligible</returns>
|
||||||
public bool IsUpscaleCompatible(TextureInfo info)
|
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>
|
/// <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
|
// 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).
|
// 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))
|
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)
|
// Discount square textures that aren't depth-stencil like. (excludes game textures, cubemap faces, most 3D texture LUT, texture atlas)
|
||||||
|
|
|
@ -34,38 +34,61 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy());
|
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy());
|
||||||
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy());
|
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy());
|
||||||
|
|
||||||
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle);
|
int levels = Math.Min(src.Info.Levels, dst.Info.Levels);
|
||||||
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle);
|
int layers = Math.Min(src.Info.GetLayers(), dst.Info.GetLayers());
|
||||||
|
|
||||||
ClearBufferMask mask = GetMask(src.Format);
|
for (int level = 0; level < levels; level++)
|
||||||
|
|
||||||
if ((mask & (ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit)) != 0 || src.Format.IsInteger())
|
|
||||||
{
|
{
|
||||||
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.ReadFramebuffer, src.Format, 0);
|
||||||
Attach(FramebufferTarget.DrawFramebuffer, dst.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)
|
if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint)
|
||||||
{
|
{
|
||||||
GL.FramebufferTexture(target, FramebufferAttachment.DepthStencilAttachment, handle, 0);
|
return FramebufferAttachment.DepthStencilAttachment;
|
||||||
}
|
}
|
||||||
else if (IsDepthOnly(format))
|
else if (IsDepthOnly(format))
|
||||||
{
|
{
|
||||||
GL.FramebufferTexture(target, FramebufferAttachment.DepthAttachment, handle, 0);
|
return FramebufferAttachment.DepthAttachment;
|
||||||
}
|
}
|
||||||
else if (format == Format.S8Uint)
|
else if (format == Format.S8Uint)
|
||||||
{
|
{
|
||||||
GL.FramebufferTexture(target, FramebufferAttachment.StencilAttachment, handle, 0);
|
return FramebufferAttachment.StencilAttachment;
|
||||||
}
|
}
|
||||||
else
|
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)
|
private static ClearBufferMask GetMask(Format format)
|
||||||
{
|
{
|
||||||
if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint)
|
if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint)
|
||||||
|
|
Loading…
Reference in a new issue