266 lines
9.1 KiB
C#
Raw Normal View History

using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using SharpMetal.Metal;
using System;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
[SupportedOSPlatform("macos")]
static class TextureCopy
{
public static ulong CopyFromOrToBuffer(
CommandBufferScoped cbs,
MTLBuffer buffer,
MTLTexture image,
TextureCreateInfo info,
bool to,
int dstLayer,
int dstLevel,
int x,
int y,
int width,
int height,
ulong offset = 0)
{
MTLBlitCommandEncoder blitCommandEncoder = cbs.Encoders.EnsureBlitEncoder();
bool is3D = info.Target == Target.Texture3D;
int blockWidth = BitUtils.DivRoundUp(width, info.BlockWidth);
int blockHeight = BitUtils.DivRoundUp(height, info.BlockHeight);
ulong bytesPerRow = (ulong)BitUtils.AlignUp(blockWidth * info.BytesPerPixel, 4);
ulong bytesPerImage = bytesPerRow * (ulong)blockHeight;
MTLOrigin origin = new MTLOrigin { x = (ulong)x, y = (ulong)y, z = is3D ? (ulong)dstLayer : 0 };
MTLSize region = new MTLSize { width = (ulong)width, height = (ulong)height, depth = 1 };
uint layer = is3D ? 0 : (uint)dstLayer;
if (to)
{
blitCommandEncoder.CopyFromTexture(
image,
layer,
(ulong)dstLevel,
origin,
region,
buffer,
offset,
bytesPerRow,
bytesPerImage);
}
else
{
blitCommandEncoder.CopyFromBuffer(buffer, offset, bytesPerRow, bytesPerImage, region, image, layer, (ulong)dstLevel, origin);
}
return offset + bytesPerImage;
}
public static void Copy(
CommandBufferScoped cbs,
MTLTexture srcImage,
MTLTexture dstImage,
TextureCreateInfo srcInfo,
TextureCreateInfo dstInfo,
int srcLayer,
int dstLayer,
int srcLevel,
int dstLevel)
{
int srcDepth = srcInfo.GetDepthOrLayers();
int srcLevels = srcInfo.Levels;
int dstDepth = dstInfo.GetDepthOrLayers();
int dstLevels = dstInfo.Levels;
if (dstInfo.Target == Target.Texture3D)
{
dstDepth = Math.Max(1, dstDepth >> dstLevel);
}
int depth = Math.Min(srcDepth, dstDepth);
int levels = Math.Min(srcLevels, dstLevels);
Copy(
cbs,
srcImage,
dstImage,
srcInfo,
dstInfo,
srcLayer,
dstLayer,
srcLevel,
dstLevel,
depth,
levels);
}
public static void Copy(
CommandBufferScoped cbs,
MTLTexture srcImage,
MTLTexture dstImage,
TextureCreateInfo srcInfo,
TextureCreateInfo dstInfo,
int srcDepthOrLayer,
int dstDepthOrLayer,
int srcLevel,
int dstLevel,
int depthOrLayers,
int levels)
{
MTLBlitCommandEncoder blitCommandEncoder = cbs.Encoders.EnsureBlitEncoder();
int srcZ;
int srcLayer;
int srcDepth;
int srcLayers;
if (srcInfo.Target == Target.Texture3D)
{
srcZ = srcDepthOrLayer;
srcLayer = 0;
srcDepth = depthOrLayers;
srcLayers = 1;
}
else
{
srcZ = 0;
srcLayer = srcDepthOrLayer;
srcDepth = 1;
srcLayers = depthOrLayers;
}
int dstZ;
int dstLayer;
int dstLayers;
if (dstInfo.Target == Target.Texture3D)
{
dstZ = dstDepthOrLayer;
dstLayer = 0;
dstLayers = 1;
}
else
{
dstZ = 0;
dstLayer = dstDepthOrLayer;
dstLayers = depthOrLayers;
}
int srcWidth = srcInfo.Width;
int srcHeight = srcInfo.Height;
int dstWidth = dstInfo.Width;
int dstHeight = dstInfo.Height;
srcWidth = Math.Max(1, srcWidth >> srcLevel);
srcHeight = Math.Max(1, srcHeight >> srcLevel);
dstWidth = Math.Max(1, dstWidth >> dstLevel);
dstHeight = Math.Max(1, dstHeight >> dstLevel);
int blockWidth = 1;
int blockHeight = 1;
bool sizeInBlocks = false;
MTLBuffer tempBuffer = default;
if (srcInfo.Format != dstInfo.Format && (srcInfo.IsCompressed || dstInfo.IsCompressed))
{
// Compressed alias copies need to happen through a temporary buffer.
// The data is copied from the source to the buffer, then the buffer to the destination.
// The length of the buffer should be the maximum slice size for the destination.
tempBuffer = blitCommandEncoder.Device.NewBuffer((ulong)dstInfo.GetMipSize2D(0), MTLResourceOptions.ResourceStorageModePrivate);
}
// When copying from a compressed to a non-compressed format,
// the non-compressed texture will have the size of the texture
// in blocks (not in texels), so we must adjust that size to
// match the size in texels of the compressed texture.
if (!srcInfo.IsCompressed && dstInfo.IsCompressed)
{
srcWidth *= dstInfo.BlockWidth;
srcHeight *= dstInfo.BlockHeight;
blockWidth = dstInfo.BlockWidth;
blockHeight = dstInfo.BlockHeight;
sizeInBlocks = true;
}
else if (srcInfo.IsCompressed && !dstInfo.IsCompressed)
{
dstWidth *= srcInfo.BlockWidth;
dstHeight *= srcInfo.BlockHeight;
blockWidth = srcInfo.BlockWidth;
blockHeight = srcInfo.BlockHeight;
}
int width = Math.Min(srcWidth, dstWidth);
int height = Math.Min(srcHeight, dstHeight);
for (int level = 0; level < levels; level++)
{
// Stop copy if we are already out of the levels range.
if (level >= srcInfo.Levels || dstLevel + level >= dstInfo.Levels)
{
break;
}
int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width;
int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height;
int layers = Math.Max(dstLayers - dstLayer, srcLayers);
for (int layer = 0; layer < layers; layer++)
{
if (tempBuffer.NativePtr != 0)
{
// Copy through the temp buffer
CopyFromOrToBuffer(cbs, tempBuffer, srcImage, srcInfo, true, srcLayer + layer, srcLevel + level, 0, 0, copyWidth, copyHeight);
int dstBufferWidth = sizeInBlocks ? copyWidth * blockWidth : BitUtils.DivRoundUp(copyWidth, blockWidth);
int dstBufferHeight = sizeInBlocks ? copyHeight * blockHeight : BitUtils.DivRoundUp(copyHeight, blockHeight);
CopyFromOrToBuffer(cbs, tempBuffer, dstImage, dstInfo, false, dstLayer + layer, dstLevel + level, 0, 0, dstBufferWidth, dstBufferHeight);
}
else if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples)
{
// TODO
Logger.Warning?.PrintMsg(LogClass.Gpu, "Unsupported mismatching sample count copy");
}
else
{
blitCommandEncoder.CopyFromTexture(
srcImage,
(ulong)(srcLayer + layer),
(ulong)(srcLevel + level),
new MTLOrigin { z = (ulong)srcZ },
new MTLSize { width = (ulong)copyWidth, height = (ulong)copyHeight, depth = (ulong)srcDepth },
dstImage,
(ulong)(dstLayer + layer),
(ulong)(dstLevel + level),
new MTLOrigin { z = (ulong)dstZ });
}
}
width = Math.Max(1, width >> 1);
height = Math.Max(1, height >> 1);
if (srcInfo.Target == Target.Texture3D)
{
srcDepth = Math.Max(1, srcDepth >> 1);
}
}
if (tempBuffer.NativePtr != 0)
{
tempBuffer.Dispose();
}
}
}
}