New depth-stencil blit method for AMD

This commit is contained in:
gdkchan 2021-09-04 02:03:56 -03:00 committed by riperiperi
parent ceeb70a996
commit c109410ccd
4 changed files with 332 additions and 56 deletions

View file

@ -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)

View file

@ -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<DisposableImage> _imageAuto;
@ -175,7 +178,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_aliasedStorages ??= new Dictionary<GAL.Format, TextureStorage>();
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)

View file

@ -18,7 +18,9 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Auto<DisposableImageView> _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;

View file

@ -7,5 +7,7 @@
public const bool UseFastBufferUpdates = true;
public const bool UseGranularBufferTracking = false;
public const bool UseSlowSafeBlitOnAmd = true;
public const bool ForceD24S8Unsupported = false;
}
}