aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Vulkan/TextureCopy.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Vulkan/TextureCopy.cs')
-rw-r--r--Ryujinx.Graphics.Vulkan/TextureCopy.cs359
1 files changed, 359 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.Vulkan/TextureCopy.cs b/Ryujinx.Graphics.Vulkan/TextureCopy.cs
new file mode 100644
index 00000000..05e11093
--- /dev/null
+++ b/Ryujinx.Graphics.Vulkan/TextureCopy.cs
@@ -0,0 +1,359 @@
+using Ryujinx.Common;
+using Ryujinx.Graphics.GAL;
+using Silk.NET.Vulkan;
+using System;
+using System.Numerics;
+
+namespace Ryujinx.Graphics.Vulkan
+{
+ static class TextureCopy
+ {
+ public static void Blit(
+ Vk api,
+ CommandBuffer commandBuffer,
+ Image srcImage,
+ Image dstImage,
+ TextureCreateInfo srcInfo,
+ TextureCreateInfo dstInfo,
+ Extents2D srcRegion,
+ Extents2D dstRegion,
+ int srcLayer,
+ int dstLayer,
+ int srcLevel,
+ int dstLevel,
+ int layers,
+ int levels,
+ bool linearFilter,
+ ImageAspectFlags srcAspectFlags = 0,
+ ImageAspectFlags dstAspectFlags = 0)
+ {
+ static (Offset3D, Offset3D) ExtentsToOffset3D(Extents2D extents, int width, int height, int level)
+ {
+ static int Clamp(int value, int max)
+ {
+ return Math.Clamp(value, 0, max);
+ }
+
+ var xy1 = new Offset3D(Clamp(extents.X1, width) >> level, Clamp(extents.Y1, height) >> level, 0);
+ var xy2 = new Offset3D(Clamp(extents.X2, width) >> level, Clamp(extents.Y2, height) >> level, 1);
+
+ return (xy1, xy2);
+ }
+
+ if (srcAspectFlags == 0)
+ {
+ srcAspectFlags = srcInfo.Format.ConvertAspectFlags();
+ }
+
+ if (dstAspectFlags == 0)
+ {
+ dstAspectFlags = dstInfo.Format.ConvertAspectFlags();
+ }
+
+ var srcOffsets = new ImageBlit.SrcOffsetsBuffer();
+ var dstOffsets = new ImageBlit.DstOffsetsBuffer();
+
+ var filter = linearFilter && !dstInfo.Format.IsDepthOrStencil() ? Filter.Linear : Filter.Nearest;
+
+ TextureView.InsertImageBarrier(
+ api,
+ commandBuffer,
+ srcImage,
+ TextureStorage.DefaultAccessMask,
+ AccessFlags.AccessTransferReadBit,
+ PipelineStageFlags.PipelineStageAllCommandsBit,
+ PipelineStageFlags.PipelineStageTransferBit,
+ srcAspectFlags,
+ srcLayer,
+ srcLevel,
+ layers,
+ levels);
+
+ uint copySrcLevel = (uint)srcLevel;
+ uint copyDstLevel = (uint)dstLevel;
+
+ for (int level = 0; level < levels; level++)
+ {
+ var srcSl = new ImageSubresourceLayers(srcAspectFlags, copySrcLevel, (uint)srcLayer, (uint)layers);
+ var dstSl = new ImageSubresourceLayers(dstAspectFlags, copyDstLevel, (uint)dstLayer, (uint)layers);
+
+ (srcOffsets.Element0, srcOffsets.Element1) = ExtentsToOffset3D(srcRegion, srcInfo.Width, srcInfo.Height, level);
+ (dstOffsets.Element0, dstOffsets.Element1) = ExtentsToOffset3D(dstRegion, dstInfo.Width, dstInfo.Height, level);
+
+ var region = new ImageBlit()
+ {
+ SrcSubresource = srcSl,
+ SrcOffsets = srcOffsets,
+ DstSubresource = dstSl,
+ DstOffsets = dstOffsets
+ };
+
+ api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region, filter);
+
+ copySrcLevel++;
+ copyDstLevel++;
+
+ if (srcInfo.Target == Target.Texture3D || dstInfo.Target == Target.Texture3D)
+ {
+ layers = Math.Max(1, layers >> 1);
+ }
+ }
+
+ TextureView.InsertImageBarrier(
+ api,
+ commandBuffer,
+ dstImage,
+ AccessFlags.AccessTransferWriteBit,
+ TextureStorage.DefaultAccessMask,
+ PipelineStageFlags.PipelineStageTransferBit,
+ PipelineStageFlags.PipelineStageAllCommandsBit,
+ dstAspectFlags,
+ dstLayer,
+ dstLevel,
+ layers,
+ levels);
+ }
+
+ public static void Copy(
+ Vk api,
+ CommandBuffer commandBuffer,
+ Image srcImage,
+ Image dstImage,
+ TextureCreateInfo srcInfo,
+ TextureCreateInfo dstInfo,
+ int srcViewLayer,
+ int dstViewLayer,
+ int srcViewLevel,
+ int dstViewLevel,
+ 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(
+ api,
+ commandBuffer,
+ srcImage,
+ dstImage,
+ srcInfo,
+ dstInfo,
+ srcViewLayer,
+ dstViewLayer,
+ srcViewLevel,
+ dstViewLevel,
+ srcLayer,
+ dstLayer,
+ srcLevel,
+ dstLevel,
+ depth,
+ levels);
+ }
+
+ private static int ClampLevels(TextureCreateInfo info, int levels)
+ {
+ int width = info.Width;
+ int height = info.Height;
+ int depth = info.Target == Target.Texture3D ? info.Depth : 1;
+
+ int maxLevels = 1 + BitOperations.Log2((uint)Math.Max(Math.Max(width, height), depth));
+
+ if (levels > maxLevels)
+ {
+ levels = maxLevels;
+ }
+
+ return levels;
+ }
+
+ public static void Copy(
+ Vk api,
+ CommandBuffer commandBuffer,
+ Image srcImage,
+ Image dstImage,
+ TextureCreateInfo srcInfo,
+ TextureCreateInfo dstInfo,
+ int srcViewLayer,
+ int dstViewLayer,
+ int srcViewLevel,
+ int dstViewLevel,
+ int srcDepthOrLayer,
+ int dstDepthOrLayer,
+ int srcLevel,
+ int dstLevel,
+ int depthOrLayers,
+ int levels)
+ {
+ 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 dstDepth;
+ int dstLayers;
+
+ if (dstInfo.Target == Target.Texture3D)
+ {
+ dstZ = dstDepthOrLayer;
+ dstLayer = 0;
+ dstDepth = depthOrLayers;
+ dstLayers = 1;
+ }
+ else
+ {
+ dstZ = 0;
+ dstLayer = dstDepthOrLayer;
+ dstDepth = 1;
+ 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;
+
+ // 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);
+
+ ImageAspectFlags srcAspect = srcInfo.Format.ConvertAspectFlags();
+ ImageAspectFlags dstAspect = dstInfo.Format.ConvertAspectFlags();
+
+ TextureView.InsertImageBarrier(
+ api,
+ commandBuffer,
+ srcImage,
+ TextureStorage.DefaultAccessMask,
+ AccessFlags.AccessTransferReadBit,
+ PipelineStageFlags.PipelineStageAllCommandsBit,
+ PipelineStageFlags.PipelineStageTransferBit,
+ srcAspect,
+ srcViewLayer + srcLayer,
+ srcViewLevel + srcLevel,
+ srcLayers,
+ levels);
+
+ 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;
+ }
+
+ var srcSl = new ImageSubresourceLayers(
+ srcAspect,
+ (uint)(srcViewLevel + srcLevel + level),
+ (uint)(srcViewLayer + srcLayer),
+ (uint)srcLayers);
+
+ var dstSl = new ImageSubresourceLayers(
+ dstAspect,
+ (uint)(dstViewLevel + dstLevel + level),
+ (uint)(dstViewLayer + dstLayer),
+ (uint)dstLayers);
+
+ int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width;
+ int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height;
+
+ var extent = new Extent3D((uint)copyWidth, (uint)copyHeight, (uint)srcDepth);
+
+ if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples)
+ {
+ var region = new ImageResolve(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent);
+
+ api.CmdResolveImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region);
+ }
+ else
+ {
+ var region = new ImageCopy(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent);
+
+ api.CmdCopyImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region);
+ }
+
+ width = Math.Max(1, width >> 1);
+ height = Math.Max(1, height >> 1);
+
+ if (srcInfo.Target == Target.Texture3D)
+ {
+ srcDepth = Math.Max(1, srcDepth >> 1);
+ }
+ }
+
+ TextureView.InsertImageBarrier(
+ api,
+ commandBuffer,
+ dstImage,
+ AccessFlags.AccessTransferWriteBit,
+ TextureStorage.DefaultAccessMask,
+ PipelineStageFlags.PipelineStageTransferBit,
+ PipelineStageFlags.PipelineStageAllCommandsBit,
+ dstAspect,
+ dstViewLayer + dstLayer,
+ dstViewLevel + dstLevel,
+ dstLayers,
+ levels);
+ }
+ }
+}