diff --git a/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs b/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs index 456eb4ebe..be2ead0bf 100644 --- a/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs @@ -56,7 +56,8 @@ namespace Ryujinx.Graphics.Vulkan requiredFeatures |= FormatFeatureFlags.FormatFeatureStorageImageBit; } - if (!FormatSupports(srcFormat, requiredFeatures)) + if (!FormatSupports(srcFormat, requiredFeatures) || + (srcFormat == GAL.Format.D24UnormS8Uint && VulkanConfiguration.ForceD24S8Unsupported)) { // The format is not supported. Can we convert it to a higher precision format? if (srcFormat == GAL.Format.D24UnormS8Uint) diff --git a/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/Ryujinx.Graphics.Vulkan/TextureStorage.cs index c9d2d34f1..4e351bcd6 100644 --- a/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -3,6 +3,7 @@ using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.Numerics; +using VkBuffer = Silk.NET.Vulkan.Buffer; using VkFormat = Silk.NET.Vulkan.Format; namespace Ryujinx.Graphics.Vulkan @@ -28,7 +29,9 @@ namespace Ryujinx.Graphics.Vulkan private readonly Device _device; - private readonly TextureCreateInfo _info; + private TextureCreateInfo _info; + + public TextureCreateInfo Info => _info; private readonly Image _image; private readonly Auto _imageAuto; @@ -175,7 +178,7 @@ namespace Ryujinx.Graphics.Vulkan { _aliasedStorages ??= new Dictionary(); - var info = NewCreateInfoWith(_info, format, _info.BytesPerPixel); + var info = NewCreateInfoWith(ref _info, format, _info.BytesPerPixel); storage = new TextureStorage(_gd, default, _device, info, ScaleFactor, _allocationAuto); @@ -185,11 +188,21 @@ namespace Ryujinx.Graphics.Vulkan return storage; } - public static TextureCreateInfo NewCreateInfoWith(TextureCreateInfo info, GAL.Format format, int bytesPerPixel) + public static TextureCreateInfo NewCreateInfoWith(ref TextureCreateInfo info, GAL.Format format, int bytesPerPixel) + { + return NewCreateInfoWith(ref info, format, bytesPerPixel, info.Width, info.Height); + } + + public static TextureCreateInfo NewCreateInfoWith( + ref TextureCreateInfo info, + GAL.Format format, + int bytesPerPixel, + int width, + int height) { return new TextureCreateInfo( - info.Width, - info.Height, + width, + height, info.Depth, info.Levels, info.Samples, @@ -266,6 +279,92 @@ namespace Ryujinx.Graphics.Vulkan return new TextureView(_gd, _device, info, this, firstLayer, firstLevel); } + public void CopyFromOrToBuffer( + CommandBuffer commandBuffer, + VkBuffer buffer, + Image image, + int size, + bool to, + int x, + int y, + int dstLayer, + int dstLevel, + int dstLayers, + int dstLevels, + bool singleSlice, + ImageAspectFlags aspectFlags) + { + bool is3D = Info.Target == Target.Texture3D; + int width = Info.Width; + int height = Info.Height; + int depth = is3D && !singleSlice ? Info.Depth : 1; + int layer = is3D ? 0 : dstLayer; + int layers = dstLayers; + int levels = dstLevels; + + int offset = 0; + + for (int level = 0; level < levels; level++) + { + int mipSize = GetBufferDataLength(Info.GetMipSize(level)); + + int endOffset = offset + mipSize; + + if ((uint)endOffset > (uint)size) + { + break; + } + + int rowLength = (Info.GetMipStride(level) / Info.BytesPerPixel) * Info.BlockWidth; + + var sl = new ImageSubresourceLayers( + aspectFlags, + (uint)(dstLevel + level), + (uint)layer, + (uint)layers); + + var extent = new Extent3D((uint)width, (uint)height, (uint)depth); + + int z = is3D ? dstLayer : 0; + + var region = new BufferImageCopy((ulong)offset, (uint)rowLength, (uint)height, sl, new Offset3D(x, y, z), extent); + + if (to) + { + _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region); + } + else + { + _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region); + } + + offset += mipSize; + + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); + + if (Info.Target == Target.Texture3D) + { + depth = Math.Max(1, depth >> 1); + } + } + } + + private int GetBufferDataLength(int length) + { + if (NeedsD24S8Conversion()) + { + return length * 2; + } + + return length; + } + + private bool NeedsD24S8Conversion() + { + return Info.Format == GAL.Format.D24UnormS8Uint && VkFormat == VkFormat.D32SfloatS8Uint; + } + public void Dispose() { if (_aliasedStorages != null) diff --git a/Ryujinx.Graphics.Vulkan/TextureView.cs b/Ryujinx.Graphics.Vulkan/TextureView.cs index b6c47f213..69efcf61b 100644 --- a/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -18,7 +18,9 @@ namespace Ryujinx.Graphics.Vulkan private readonly Auto _imageView2dArray; private BufferHolder _flushStorage; - public TextureCreateInfo Info { get; } + private TextureCreateInfo _info; + + public TextureCreateInfo Info => _info; public TextureStorage Storage { get; } @@ -41,7 +43,7 @@ namespace Ryujinx.Graphics.Vulkan { _gd = gd; _device = device; - Info = info; + _info = info; Storage = storage; FirstLayer = firstLayer; FirstLevel = firstLevel; @@ -320,54 +322,7 @@ namespace Ryujinx.Graphics.Vulkan } else if (srcFormat == GAL.Format.D32FloatS8Uint && srcFormat == dstFormat && SupportsBlitFromD32FS8ToD32FAndS8()) { - var d32StorageInfo = TextureStorage.NewCreateInfoWith(src.Info, GAL.Format.D32Float, 4); - var s8StorageInfo = TextureStorage.NewCreateInfoWith(dst.Info, GAL.Format.S8Uint, 1); - - using var d32Storage = _gd.CreateTextureStorage(d32StorageInfo, dst.Storage.ScaleFactor); - using var s8Storage = _gd.CreateTextureStorage(s8StorageInfo, dst.Storage.ScaleFactor); - - void BlitAndCopy(ref TextureCreateInfo info, TextureStorage storage, ImageAspectFlags aspectFlags) - { - TextureCopy.Blit( - _gd.Api, - cbs.CommandBuffer, - src.GetImage().Get(cbs).Value, - storage.GetImage().Get(cbs).Value, - src.Info, - info, - srcRegion, - dstRegion, - src.FirstLayer, - 0, - src.FirstLevel, - 0, - layers, - levels, - false, - aspectFlags, - aspectFlags); - - TextureCopy.Copy( - _gd.Api, - cbs.CommandBuffer, - storage.GetImage().Get(cbs).Value, - dst.GetImage().Get(cbs).Value, - info, - dst.Info, - 0, - dst.FirstLayer, - 0, - dst.FirstLevel, - 0, - 0, - 0, - 0, - layers, - levels); - } - - BlitAndCopy(ref d32StorageInfo, d32Storage, ImageAspectFlags.ImageAspectDepthBit); - BlitAndCopy(ref s8StorageInfo, s8Storage, ImageAspectFlags.ImageAspectStencilBit); + BlitDepthStencilWithBuffer(_gd, cbs, src, dst, srcRegion, dstRegion); return; } @@ -427,6 +382,225 @@ namespace Ryujinx.Graphics.Vulkan ImageAspectFlags.ImageAspectColorBit); } + private static void BlitDepthStencilWithBuffer( + VulkanGraphicsDevice gd, + CommandBufferScoped cbs, + TextureView src, + TextureView dst, + Extents2D srcRegion, + Extents2D dstRegion) + { + int drBaseX = Math.Min(dstRegion.X1, dstRegion.X2); + int drBaseY = Math.Min(dstRegion.Y1, dstRegion.Y2); + int drWidth = Math.Abs(dstRegion.X2 - dstRegion.X1); + int drHeight = Math.Abs(dstRegion.Y2 - dstRegion.Y1); + + var drOriginZero = new Extents2D( + dstRegion.X1 - drBaseX, + dstRegion.Y1 - drBaseY, + dstRegion.X2 - drBaseX, + dstRegion.Y2 - drBaseY); + + var d32SrcStorageInfo = TextureStorage.NewCreateInfoWith(ref src._info, GAL.Format.D32Float, 4); + var d32DstStorageInfo = TextureStorage.NewCreateInfoWith(ref dst._info, GAL.Format.D32Float, 4, drWidth, drHeight); + var s8SrcStorageInfo = TextureStorage.NewCreateInfoWith(ref src._info, GAL.Format.S8Uint, 1); + var s8DstStorageInfo = TextureStorage.NewCreateInfoWith(ref dst._info, GAL.Format.S8Uint, 1, drWidth, drHeight); + + using var d32SrcStorage = gd.CreateTextureStorage(d32SrcStorageInfo, src.Storage.ScaleFactor); + using var d32DstStorage = gd.CreateTextureStorage(d32DstStorageInfo, dst.Storage.ScaleFactor); + using var s8SrcStorage = gd.CreateTextureStorage(s8SrcStorageInfo, src.Storage.ScaleFactor); + using var s8DstStorage = gd.CreateTextureStorage(s8DstStorageInfo, dst.Storage.ScaleFactor); + + void SlowBlit(TextureStorage srcTemp, TextureStorage dstTemp, ImageAspectFlags aspectFlags) + { + int levels = Math.Min(src.Info.Levels, dst.Info.Levels); + + int srcSize = 0; + int dstSize = 0; + + for (int l = 0; l < levels; l++) + { + srcSize += srcTemp.Info.GetMipSize2D(l); + dstSize += dstTemp.Info.GetMipSize2D(l); + } + + using var srcTempBuffer = gd.BufferManager.Create(gd, srcSize); + using var dstTempBuffer = gd.BufferManager.Create(gd, dstSize); + + src.Storage.CopyFromOrToBuffer( + cbs.CommandBuffer, + srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value, + src.GetImage().Get(cbs).Value, + srcSize, + to: true, + 0, + 0, + src.FirstLayer, + src.FirstLevel, + 1, + levels, + true, + aspectFlags); + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value, + AccessFlags.AccessTransferWriteBit, + AccessFlags.AccessTransferReadBit, + PipelineStageFlags.PipelineStageTransferBit, + PipelineStageFlags.PipelineStageTransferBit, + 0, + srcSize); + + srcTemp.CopyFromOrToBuffer( + cbs.CommandBuffer, + srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value, + srcTemp.GetImage().Get(cbs).Value, + srcSize, + to: false, + 0, + 0, + 0, + 0, + 1, + levels, + true, + aspectFlags); + + InsertImageBarrier( + gd, + cbs.CommandBuffer, + srcTemp.GetImage().Get(cbs).Value, + AccessFlags.AccessTransferWriteBit, + AccessFlags.AccessTransferReadBit, + PipelineStageFlags.PipelineStageTransferBit, + PipelineStageFlags.PipelineStageTransferBit, + aspectFlags, + 0, + 0, + 1, + levels); + + TextureCopy.Blit( + gd.Api, + cbs.CommandBuffer, + srcTemp.GetImage().Get(cbs).Value, + dstTemp.GetImage().Get(cbs).Value, + srcTemp.Info, + dstTemp.Info, + srcRegion, + drOriginZero, + 0, + 0, + 0, + 0, + 1, + levels, + false, + aspectFlags, + aspectFlags); + + InsertImageBarrier( + gd, + cbs.CommandBuffer, + dstTemp.GetImage().Get(cbs).Value, + AccessFlags.AccessTransferWriteBit, + AccessFlags.AccessTransferReadBit, + PipelineStageFlags.PipelineStageTransferBit, + PipelineStageFlags.PipelineStageTransferBit, + aspectFlags, + 0, + 0, + 1, + levels); + + dstTemp.CopyFromOrToBuffer( + cbs.CommandBuffer, + dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value, + dstTemp.GetImage().Get(cbs).Value, + dstSize, + to: true, + 0, + 0, + 0, + 0, + 1, + levels, + true, + aspectFlags); + + BufferHolder.InsertBufferBarrier( + gd, + cbs.CommandBuffer, + dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value, + AccessFlags.AccessTransferWriteBit, + AccessFlags.AccessTransferReadBit, + PipelineStageFlags.PipelineStageTransferBit, + PipelineStageFlags.PipelineStageTransferBit, + 0, + dstSize); + + dst.Storage.CopyFromOrToBuffer( + cbs.CommandBuffer, + dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value, + dst.GetImage().Get(cbs).Value, + dstSize, + to: false, + drBaseX, + drBaseY, + dst.FirstLayer, + dst.FirstLevel, + 1, + levels, + true, + aspectFlags); + } + + SlowBlit(d32SrcStorage, d32DstStorage, ImageAspectFlags.ImageAspectDepthBit); + SlowBlit(s8SrcStorage, s8DstStorage, ImageAspectFlags.ImageAspectStencilBit); + } + + public static unsafe void InsertImageBarrier( + VulkanGraphicsDevice gd, + CommandBuffer commandBuffer, + Image image, + AccessFlags srcAccessMask, + AccessFlags dstAccessMask, + PipelineStageFlags srcStageMask, + PipelineStageFlags dstStageMask, + ImageAspectFlags aspectFlags, + int firstLayer, + int firstLevel, + int layers, + int levels) + { + ImageMemoryBarrier memoryBarrier = new ImageMemoryBarrier() + { + SType = StructureType.ImageMemoryBarrier, + SrcAccessMask = srcAccessMask, + DstAccessMask = dstAccessMask, + SrcQueueFamilyIndex = Vk.QueueFamilyIgnored, + DstQueueFamilyIndex = Vk.QueueFamilyIgnored, + Image = image, + OldLayout = ImageLayout.General, + NewLayout = ImageLayout.General, + SubresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, (uint)levels, (uint)firstLayer, (uint)layers) + }; + + gd.Api.CmdPipelineBarrier( + commandBuffer, + srcStageMask, + dstStageMask, + 0, + 0, + null, + 0, + null, + 1, + memoryBarrier); + } + private bool SupportsBlitFromD32FS8ToD32FAndS8() { var formatFeatureFlags = FormatFeatureFlags.FormatFeatureBlitSrcBit | FormatFeatureFlags.FormatFeatureBlitDstBit; diff --git a/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs b/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs index 6ca671b71..b4d25edee 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs @@ -7,5 +7,7 @@ public const bool UseFastBufferUpdates = true; public const bool UseGranularBufferTracking = false; public const bool UseSlowSafeBlitOnAmd = true; + + public const bool ForceD24S8Unsupported = false; } }