aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Vulkan
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.Vulkan')
-rw-r--r--src/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs1
-rw-r--r--src/Ryujinx.Graphics.Vulkan/BufferHolder.cs40
-rw-r--r--src/Ryujinx.Graphics.Vulkan/BufferManager.cs119
-rw-r--r--src/Ryujinx.Graphics.Vulkan/Constants.cs2
-rw-r--r--src/Ryujinx.Graphics.Vulkan/EnumConversion.cs10
-rw-r--r--src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs15
6 files changed, 173 insertions, 14 deletions
diff --git a/src/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs b/src/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs
index 7987017e..345191f1 100644
--- a/src/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BufferAllocationType.cs
@@ -8,5 +8,6 @@ namespace Ryujinx.Graphics.Vulkan
HostMapped,
DeviceLocal,
DeviceLocalMapped,
+ Sparse,
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs
index b1887eaa..b54ff3ab 100644
--- a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs
@@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Vulkan
private bool _lastAccessIsWrite;
- private readonly BufferAllocationType _baseType;
+ private BufferAllocationType _baseType;
private BufferAllocationType _currentType;
private bool _swapQueued;
@@ -109,6 +109,22 @@ namespace Ryujinx.Graphics.Vulkan
_flushLock = new ReaderWriterLockSlim();
}
+ public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, int size, Auto<MemoryAllocation>[] storageAllocations)
+ {
+ _gd = gd;
+ _device = device;
+ _waitable = new MultiFenceHolder(size);
+ _buffer = new Auto<DisposableBuffer>(new DisposableBuffer(gd.Api, device, buffer), _waitable, storageAllocations);
+ _bufferHandle = buffer.Handle;
+ Size = size;
+
+ _baseType = BufferAllocationType.Sparse;
+ _currentType = BufferAllocationType.Sparse;
+ DesiredType = BufferAllocationType.Sparse;
+
+ _flushLock = new ReaderWriterLockSlim();
+ }
+
public bool TryBackingSwap(ref CommandBufferScoped? cbs)
{
if (_swapQueued && DesiredType != _currentType)
@@ -122,7 +138,7 @@ namespace Ryujinx.Graphics.Vulkan
var currentBuffer = _buffer;
IntPtr currentMap = _map;
- (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) = _gd.BufferManager.CreateBacking(_gd, Size, DesiredType, false, _currentType);
+ (VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) = _gd.BufferManager.CreateBacking(_gd, Size, DesiredType, false, false, _currentType);
if (buffer.Handle != 0)
{
@@ -253,6 +269,14 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ public void Pin()
+ {
+ if (_baseType == BufferAllocationType.Auto)
+ {
+ _baseType = _currentType;
+ }
+ }
+
public unsafe Auto<DisposableBufferView> CreateView(VkFormat format, int offset, int size, Action invalidateView)
{
var bufferViewCreateInfo = new BufferViewCreateInfo
@@ -506,6 +530,16 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ public Auto<MemoryAllocation> GetAllocation()
+ {
+ return _allocationAuto;
+ }
+
+ public (DeviceMemory, ulong) GetDeviceMemoryAndOffset()
+ {
+ return (_allocation.Memory, _allocation.Offset);
+ }
+
public void SignalWrite(int offset, int size)
{
ConsiderBackingSwap();
@@ -1072,7 +1106,7 @@ namespace Ryujinx.Graphics.Vulkan
}
else
{
- _allocationAuto.Dispose();
+ _allocationAuto?.Dispose();
}
_flushLock.EnterWriteLock();
diff --git a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs
index 20e00338..e9ac9884 100644
--- a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs
@@ -96,25 +96,131 @@ namespace Ryujinx.Graphics.Vulkan
return Unsafe.As<ulong, BufferHandle>(ref handle64);
}
+ public unsafe BufferHandle CreateSparse(VulkanRenderer gd, ReadOnlySpan<BufferRange> storageBuffers)
+ {
+ var usage = DefaultBufferUsageFlags;
+
+ if (gd.Capabilities.SupportsIndirectParameters)
+ {
+ usage |= BufferUsageFlags.IndirectBufferBit;
+ }
+
+ ulong size = 0;
+
+ foreach (BufferRange range in storageBuffers)
+ {
+ size += (ulong)range.Size;
+ }
+
+ var bufferCreateInfo = new BufferCreateInfo()
+ {
+ SType = StructureType.BufferCreateInfo,
+ Size = size,
+ Usage = usage,
+ SharingMode = SharingMode.Exclusive,
+ Flags = BufferCreateFlags.SparseBindingBit | BufferCreateFlags.SparseAliasedBit
+ };
+
+ gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
+
+ var memoryBinds = new SparseMemoryBind[storageBuffers.Length];
+ var storageAllocations = new Auto<MemoryAllocation>[storageBuffers.Length];
+ int storageAllocationsCount = 0;
+
+ ulong dstOffset = 0;
+
+ for (int index = 0; index < storageBuffers.Length; index++)
+ {
+ BufferRange range = storageBuffers[index];
+
+ if (TryGetBuffer(range.Handle, out var existingHolder))
+ {
+ // Since this buffer now also owns the memory from the referenced buffer,
+ // we pin it to ensure the memory location will not change.
+ existingHolder.Pin();
+
+ (var memory, var offset) = existingHolder.GetDeviceMemoryAndOffset();
+
+ memoryBinds[index] = new SparseMemoryBind()
+ {
+ ResourceOffset = dstOffset,
+ Size = (ulong)range.Size,
+ Memory = memory,
+ MemoryOffset = offset + (ulong)range.Offset,
+ Flags = SparseMemoryBindFlags.None
+ };
+
+ storageAllocations[storageAllocationsCount++] = existingHolder.GetAllocation();
+ }
+ else
+ {
+ memoryBinds[index] = new SparseMemoryBind()
+ {
+ ResourceOffset = dstOffset,
+ Size = (ulong)range.Size,
+ Memory = default,
+ MemoryOffset = 0UL,
+ Flags = SparseMemoryBindFlags.None
+ };
+ }
+
+ dstOffset += (ulong)range.Size;
+ }
+
+ if (storageAllocations.Length != storageAllocationsCount)
+ {
+ Array.Resize(ref storageAllocations, storageAllocationsCount);
+ }
+
+ fixed (SparseMemoryBind* pMemoryBinds = memoryBinds)
+ {
+ SparseBufferMemoryBindInfo bufferBind = new SparseBufferMemoryBindInfo()
+ {
+ Buffer = buffer,
+ BindCount = (uint)memoryBinds.Length,
+ PBinds = pMemoryBinds
+ };
+
+ BindSparseInfo bindSparseInfo = new BindSparseInfo()
+ {
+ SType = StructureType.BindSparseInfo,
+ BufferBindCount = 1,
+ PBufferBinds = &bufferBind
+ };
+
+ gd.Api.QueueBindSparse(gd.Queue, 1, bindSparseInfo, default).ThrowOnError();
+ }
+
+ var holder = new BufferHolder(gd, _device, buffer, (int)size, storageAllocations);
+
+ BufferCount++;
+
+ ulong handle64 = (uint)_buffers.Add(holder);
+
+ return Unsafe.As<ulong, BufferHandle>(ref handle64);
+ }
+
public BufferHandle CreateWithHandle(
VulkanRenderer gd,
int size,
+ bool sparseCompatible = false,
BufferAllocationType baseType = BufferAllocationType.HostMapped,
BufferHandle storageHint = default,
bool forceMirrors = false)
{
- return CreateWithHandle(gd, size, out _, baseType, storageHint, forceMirrors);
+ return CreateWithHandle(gd, size, out _, sparseCompatible, baseType, storageHint, forceMirrors);
}
public BufferHandle CreateWithHandle(
VulkanRenderer gd,
int size,
out BufferHolder holder,
+ bool sparseCompatible = false,
BufferAllocationType baseType = BufferAllocationType.HostMapped,
BufferHandle storageHint = default,
bool forceMirrors = false)
{
- holder = Create(gd, size, baseType: baseType, storageHint: storageHint);
+ holder = Create(gd, size, forConditionalRendering: false, sparseCompatible, baseType, storageHint);
if (holder == null)
{
return BufferHandle.Null;
@@ -163,6 +269,7 @@ namespace Ryujinx.Graphics.Vulkan
int size,
BufferAllocationType type,
bool forConditionalRendering = false,
+ bool sparseCompatible = false,
BufferAllocationType fallbackType = BufferAllocationType.Auto)
{
var usage = DefaultBufferUsageFlags;
@@ -187,6 +294,11 @@ namespace Ryujinx.Graphics.Vulkan
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements);
+ if (sparseCompatible)
+ {
+ requirements.Alignment = Math.Max(requirements.Alignment, Constants.SparseBufferAlignment);
+ }
+
MemoryAllocation allocation;
do
@@ -227,6 +339,7 @@ namespace Ryujinx.Graphics.Vulkan
VulkanRenderer gd,
int size,
bool forConditionalRendering = false,
+ bool sparseCompatible = false,
BufferAllocationType baseType = BufferAllocationType.HostMapped,
BufferHandle storageHint = default)
{
@@ -255,7 +368,7 @@ namespace Ryujinx.Graphics.Vulkan
}
(VkBuffer buffer, MemoryAllocation allocation, BufferAllocationType resultType) =
- CreateBacking(gd, size, type, forConditionalRendering);
+ CreateBacking(gd, size, type, forConditionalRendering, sparseCompatible);
if (buffer.Handle != 0)
{
diff --git a/src/Ryujinx.Graphics.Vulkan/Constants.cs b/src/Ryujinx.Graphics.Vulkan/Constants.cs
index 1bf8c580..cd612211 100644
--- a/src/Ryujinx.Graphics.Vulkan/Constants.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Constants.cs
@@ -16,5 +16,7 @@ namespace Ryujinx.Graphics.Vulkan
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages;
public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages;
+
+ public const ulong SparseBufferAlignment = 0x10000;
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs
index b7787601..e1002705 100644
--- a/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs
+++ b/src/Ryujinx.Graphics.Vulkan/EnumConversion.cs
@@ -424,12 +424,12 @@ namespace Ryujinx.Graphics.Vulkan
public static BufferAllocationType Convert(this BufferAccess access)
{
- return access switch
+ if (access.HasFlag(BufferAccess.FlushPersistent) || access.HasFlag(BufferAccess.Stream))
{
- BufferAccess.FlushPersistent => BufferAllocationType.HostMapped,
- BufferAccess.Stream => BufferAllocationType.HostMapped,
- _ => BufferAllocationType.Auto,
- };
+ return BufferAllocationType.HostMapped;
+ }
+
+ return BufferAllocationType.Auto;
}
private static T2 LogInvalidAndReturn<T1, T2>(T1 value, string name, T2 defaultValue = default)
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index 7240bcad..893ecf1a 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -392,6 +392,8 @@ namespace Ryujinx.Graphics.Vulkan
LoadFeatures(maxQueueCount, queueFamilyIndex);
+ QueueFamilyIndex = queueFamilyIndex;
+
_window = new Window(this, _surface, _physicalDevice.PhysicalDevice, _device);
_initialized = true;
@@ -399,12 +401,12 @@ namespace Ryujinx.Graphics.Vulkan
public BufferHandle CreateBuffer(int size, BufferAccess access)
{
- return BufferManager.CreateWithHandle(this, size, access.Convert(), default, access == BufferAccess.Stream);
+ return BufferManager.CreateWithHandle(this, size, access.HasFlag(BufferAccess.SparseCompatible), access.Convert(), default, access == BufferAccess.Stream);
}
- public BufferHandle CreateBuffer(int size, BufferHandle storageHint)
+ public BufferHandle CreateBuffer(int size, BufferAccess access, BufferHandle storageHint)
{
- return BufferManager.CreateWithHandle(this, size, BufferAllocationType.Auto, storageHint);
+ return BufferManager.CreateWithHandle(this, size, access.HasFlag(BufferAccess.SparseCompatible), access.Convert(), storageHint);
}
public BufferHandle CreateBuffer(nint pointer, int size)
@@ -412,6 +414,11 @@ namespace Ryujinx.Graphics.Vulkan
return BufferManager.CreateHostImported(this, pointer, size);
}
+ public BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers)
+ {
+ return BufferManager.CreateSparse(this, storageBuffers);
+ }
+
public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info)
{
bool isCompute = sources.Length == 1 && sources[0].Stage == ShaderStage.Compute;
@@ -571,6 +578,7 @@ namespace Ryujinx.Graphics.Vulkan
Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2);
var limits = _physicalDevice.PhysicalDeviceProperties.Limits;
+ var mainQueueProperties = _physicalDevice.QueueFamilyProperties[QueueFamilyIndex];
return new Capabilities(
api: TargetApi.Vulkan,
@@ -590,6 +598,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsR4G4B4A4Format: supportsR4G4B4A4Format,
supportsSnormBufferTextureFormat: true,
supports5BitComponentFormat: supports5BitComponentFormat,
+ supportsSparseBuffer: features2.Features.SparseBinding && mainQueueProperties.QueueFlags.HasFlag(QueueFlags.SparseBindingBit),
supportsBlendEquationAdvanced: Capabilities.SupportsBlendEquationAdvanced,
supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: false,