aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2023-09-25 21:00:02 -0300
committerGitHub <noreply@github.com>2023-09-26 02:00:02 +0200
commit4744bde0e59578baeb5f94126087953f6d8ddba0 (patch)
tree6f85ea9241d2b1dbdc67b73c072f145395153eab
parent4a835bb2b9130b91d35968b2f7800084cf286de4 (diff)
Reduce the amount of descriptor pool allocations on Vulkan (#5673)1.1.1030
* Reduce the amount of descriptor pool allocations on Vulkan * Formatting * Slice can be simplified * Make GetDescriptorPoolSizes static * Adjust CanFit calculation so that TryAllocateDescriptorSets never fails * Remove unused field
-rw-r--r--src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs7
-rw-r--r--src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs103
-rw-r--r--src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs12
-rw-r--r--src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs93
-rw-r--r--src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs13
-rw-r--r--src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs2
6 files changed, 173 insertions, 57 deletions
diff --git a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
index 17eeef68..4d3b8640 100644
--- a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
+++ b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
@@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan
{
public bool InUse;
public bool InConsumption;
+ public int SubmissionCount;
public CommandBuffer CommandBuffer;
public FenceHolder Fence;
public SemaphoreHolder Semaphore;
@@ -193,6 +194,11 @@ namespace Ryujinx.Graphics.Vulkan
return _commandBuffers[cbIndex].Fence;
}
+ public int GetSubmissionCount(int cbIndex)
+ {
+ return _commandBuffers[cbIndex].SubmissionCount;
+ }
+
private int FreeConsumed(bool wait)
{
int freeEntry = 0;
@@ -282,6 +288,7 @@ namespace Ryujinx.Graphics.Vulkan
Debug.Assert(entry.CommandBuffer.Handle == cbs.CommandBuffer.Handle);
entry.InUse = false;
entry.InConsumption = true;
+ entry.SubmissionCount++;
_inUseCount--;
var commandBuffer = entry.CommandBuffer;
diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs
index 2f7b604c..7594384d 100644
--- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs
+++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs
@@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Vulkan
{
class DescriptorSetManager : IDisposable
{
- private const uint DescriptorPoolMultiplier = 16;
+ public const uint MaxSets = 16;
public class DescriptorPoolHolder : IDisposable
{
@@ -14,36 +14,28 @@ namespace Ryujinx.Graphics.Vulkan
public Device Device { get; }
private readonly DescriptorPool _pool;
- private readonly uint _capacity;
+ private int _freeDescriptors;
private int _totalSets;
private int _setsInUse;
private bool _done;
- public unsafe DescriptorPoolHolder(Vk api, Device device)
+ public unsafe DescriptorPoolHolder(Vk api, Device device, ReadOnlySpan<DescriptorPoolSize> poolSizes, bool updateAfterBind)
{
Api = api;
Device = device;
- var poolSizes = new[]
+ foreach (var poolSize in poolSizes)
{
- new DescriptorPoolSize(DescriptorType.UniformBuffer, (1 + Constants.MaxUniformBufferBindings) * DescriptorPoolMultiplier),
- new DescriptorPoolSize(DescriptorType.StorageBuffer, Constants.MaxStorageBufferBindings * DescriptorPoolMultiplier),
- new DescriptorPoolSize(DescriptorType.CombinedImageSampler, Constants.MaxTextureBindings * DescriptorPoolMultiplier),
- new DescriptorPoolSize(DescriptorType.StorageImage, Constants.MaxImageBindings * DescriptorPoolMultiplier),
- new DescriptorPoolSize(DescriptorType.UniformTexelBuffer, Constants.MaxTextureBindings * DescriptorPoolMultiplier),
- new DescriptorPoolSize(DescriptorType.StorageTexelBuffer, Constants.MaxImageBindings * DescriptorPoolMultiplier),
- };
-
- uint maxSets = (uint)poolSizes.Length * DescriptorPoolMultiplier;
-
- _capacity = maxSets;
+ _freeDescriptors += (int)poolSize.DescriptorCount;
+ }
fixed (DescriptorPoolSize* pPoolsSize = poolSizes)
{
var descriptorPoolCreateInfo = new DescriptorPoolCreateInfo
{
SType = StructureType.DescriptorPoolCreateInfo,
- MaxSets = maxSets,
+ Flags = updateAfterBind ? DescriptorPoolCreateFlags.UpdateAfterBindBit : DescriptorPoolCreateFlags.None,
+ MaxSets = MaxSets,
PoolSizeCount = (uint)poolSizes.Length,
PPoolSizes = pPoolsSize,
};
@@ -52,18 +44,22 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- public DescriptorSetCollection AllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts)
+ public unsafe DescriptorSetCollection AllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, int consumedDescriptors)
{
- TryAllocateDescriptorSets(layouts, isTry: false, out var dsc);
+ TryAllocateDescriptorSets(layouts, consumedDescriptors, isTry: false, out var dsc);
return dsc;
}
- public bool TryAllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, out DescriptorSetCollection dsc)
+ public bool TryAllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, int consumedDescriptors, out DescriptorSetCollection dsc)
{
- return TryAllocateDescriptorSets(layouts, isTry: true, out dsc);
+ return TryAllocateDescriptorSets(layouts, consumedDescriptors, isTry: true, out dsc);
}
- private unsafe bool TryAllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, bool isTry, out DescriptorSetCollection dsc)
+ private unsafe bool TryAllocateDescriptorSets(
+ ReadOnlySpan<DescriptorSetLayout> layouts,
+ int consumedDescriptors,
+ bool isTry,
+ out DescriptorSetCollection dsc)
{
Debug.Assert(!_done);
@@ -84,7 +80,7 @@ namespace Ryujinx.Graphics.Vulkan
var result = Api.AllocateDescriptorSets(Device, &descriptorSetAllocateInfo, pDescriptorSets);
if (isTry && result == Result.ErrorOutOfPoolMemory)
{
- _totalSets = (int)_capacity;
+ _totalSets = (int)MaxSets;
_done = true;
DestroyIfDone();
dsc = default;
@@ -95,6 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ _freeDescriptors -= consumedDescriptors;
_totalSets += layouts.Length;
_setsInUse += layouts.Length;
@@ -109,9 +106,15 @@ namespace Ryujinx.Graphics.Vulkan
DestroyIfDone();
}
- public bool CanFit(int count)
+ public bool CanFit(int setsCount, int descriptorsCount)
{
- if (_totalSets + count <= _capacity)
+ // Try to determine if an allocation with the given parameters will succeed.
+ // An allocation may fail if the sets count or descriptors count exceeds the available counts
+ // of the pool.
+ // Not getting that right is not fatal, it will just create a new pool and try again,
+ // but it is less efficient.
+
+ if (_totalSets + setsCount <= MaxSets && _freeDescriptors >= descriptorsCount)
{
return true;
}
@@ -148,46 +151,74 @@ namespace Ryujinx.Graphics.Vulkan
}
private readonly Device _device;
- private DescriptorPoolHolder _currentPool;
+ private readonly DescriptorPoolHolder[] _currentPools;
- public DescriptorSetManager(Device device)
+ public DescriptorSetManager(Device device, int poolCount)
{
_device = device;
+ _currentPools = new DescriptorPoolHolder[poolCount];
}
- public Auto<DescriptorSetCollection> AllocateDescriptorSet(Vk api, DescriptorSetLayout layout)
+ public Auto<DescriptorSetCollection> AllocateDescriptorSet(
+ Vk api,
+ DescriptorSetLayout layout,
+ ReadOnlySpan<DescriptorPoolSize> poolSizes,
+ int poolIndex,
+ int consumedDescriptors,
+ bool updateAfterBind)
{
Span<DescriptorSetLayout> layouts = stackalloc DescriptorSetLayout[1];
layouts[0] = layout;
- return AllocateDescriptorSets(api, layouts);
+ return AllocateDescriptorSets(api, layouts, poolSizes, poolIndex, consumedDescriptors, updateAfterBind);
}
- public Auto<DescriptorSetCollection> AllocateDescriptorSets(Vk api, ReadOnlySpan<DescriptorSetLayout> layouts)
+ public Auto<DescriptorSetCollection> AllocateDescriptorSets(
+ Vk api,
+ ReadOnlySpan<DescriptorSetLayout> layouts,
+ ReadOnlySpan<DescriptorPoolSize> poolSizes,
+ int poolIndex,
+ int consumedDescriptors,
+ bool updateAfterBind)
{
// If we fail the first time, just create a new pool and try again.
- if (!GetPool(api, layouts.Length).TryAllocateDescriptorSets(layouts, out var dsc))
+
+ var pool = GetPool(api, poolSizes, poolIndex, layouts.Length, consumedDescriptors, updateAfterBind);
+ if (!pool.TryAllocateDescriptorSets(layouts, consumedDescriptors, out var dsc))
{
- dsc = GetPool(api, layouts.Length).AllocateDescriptorSets(layouts);
+ pool = GetPool(api, poolSizes, poolIndex, layouts.Length, consumedDescriptors, updateAfterBind);
+ dsc = pool.AllocateDescriptorSets(layouts, consumedDescriptors);
}
return new Auto<DescriptorSetCollection>(dsc);
}
- private DescriptorPoolHolder GetPool(Vk api, int requiredCount)
+ private DescriptorPoolHolder GetPool(
+ Vk api,
+ ReadOnlySpan<DescriptorPoolSize> poolSizes,
+ int poolIndex,
+ int setsCount,
+ int descriptorsCount,
+ bool updateAfterBind)
{
- if (_currentPool == null || !_currentPool.CanFit(requiredCount))
+ ref DescriptorPoolHolder currentPool = ref _currentPools[poolIndex];
+
+ if (currentPool == null || !currentPool.CanFit(setsCount, descriptorsCount))
{
- _currentPool = new DescriptorPoolHolder(api, _device);
+ currentPool = new DescriptorPoolHolder(api, _device, poolSizes, updateAfterBind);
}
- return _currentPool;
+ return currentPool;
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
- _currentPool?.Dispose();
+ for (int index = 0; index < _currentPools.Length; index++)
+ {
+ _currentPools[index]?.Dispose();
+ _currentPools[index] = null;
+ }
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
index 14e4c02f..a9a92df1 100644
--- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
+++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
@@ -59,6 +59,8 @@ namespace Ryujinx.Graphics.Vulkan
private BitMapStruct<Array2<long>> _uniformMirrored;
private BitMapStruct<Array2<long>> _storageMirrored;
+ private bool _updateDescriptorCacheCbIndex;
+
[Flags]
private enum DirtyFlags
{
@@ -218,6 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
public void SetProgram(ShaderCollection program)
{
_program = program;
+ _updateDescriptorCacheCbIndex = true;
_dirty = DirtyFlags.All;
}
@@ -490,7 +493,13 @@ namespace Ryujinx.Graphics.Vulkan
var dummyBuffer = _dummyBuffer?.GetBuffer();
- var dsc = program.GetNewDescriptorSetCollection(_gd, cbs.CommandBufferIndex, setIndex, out var isNew).Get(cbs);
+ if (_updateDescriptorCacheCbIndex)
+ {
+ _updateDescriptorCacheCbIndex = false;
+ program.UpdateDescriptorCacheCommandBufferIndex(cbs.CommandBufferIndex);
+ }
+
+ var dsc = program.GetNewDescriptorSetCollection(setIndex, out var isNew).Get(cbs);
if (!program.HasMinimalLayout)
{
@@ -697,6 +706,7 @@ namespace Ryujinx.Graphics.Vulkan
public void SignalCommandBufferChange()
{
+ _updateDescriptorCacheCbIndex = true;
_dirty = DirtyFlags.All;
_uniformSet.Clear();
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs
index eeb25dc0..2840dda0 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs
@@ -1,5 +1,6 @@
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
+using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -7,15 +8,28 @@ namespace Ryujinx.Graphics.Vulkan
{
class PipelineLayoutCacheEntry
{
+ // Those were adjusted based on current descriptor usage and the descriptor counts usually used on pipeline layouts.
+ // It might be a good idea to tweak them again if those change, or maybe find a way to calculate an optimal value dynamically.
+ private const uint DefaultUniformBufferPoolCapacity = 19 * DescriptorSetManager.MaxSets;
+ private const uint DefaultStorageBufferPoolCapacity = 16 * DescriptorSetManager.MaxSets;
+ private const uint DefaultTexturePoolCapacity = 128 * DescriptorSetManager.MaxSets;
+ private const uint DefaultImagePoolCapacity = 8 * DescriptorSetManager.MaxSets;
+
+ private const int MaxPoolSizesPerSet = 2;
+
private readonly VulkanRenderer _gd;
private readonly Device _device;
public DescriptorSetLayout[] DescriptorSetLayouts { get; }
public PipelineLayout PipelineLayout { get; }
+ private readonly int[] _consumedDescriptorsPerSet;
+
private readonly List<Auto<DescriptorSetCollection>>[][] _dsCache;
+ private List<Auto<DescriptorSetCollection>>[] _currentDsCache;
private readonly int[] _dsCacheCursor;
private int _dsLastCbIndex;
+ private int _dsLastSubmissionCount;
private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount)
{
@@ -44,29 +58,55 @@ namespace Ryujinx.Graphics.Vulkan
bool usePushDescriptors) : this(gd, device, setDescriptors.Count)
{
(DescriptorSetLayouts, PipelineLayout) = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors);
- }
- public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(
- VulkanRenderer gd,
- int commandBufferIndex,
- int setIndex,
- out bool isNew)
- {
- if (_dsLastCbIndex != commandBufferIndex)
+ _consumedDescriptorsPerSet = new int[setDescriptors.Count];
+
+ for (int setIndex = 0; setIndex < setDescriptors.Count; setIndex++)
{
- _dsLastCbIndex = commandBufferIndex;
+ int count = 0;
- for (int i = 0; i < _dsCacheCursor.Length; i++)
+ foreach (var descriptor in setDescriptors[setIndex].Descriptors)
{
- _dsCacheCursor[i] = 0;
+ count += descriptor.Count;
}
+
+ _consumedDescriptorsPerSet[setIndex] = count;
}
+ }
+
+ public void UpdateCommandBufferIndex(int commandBufferIndex)
+ {
+ int submissionCount = _gd.CommandBufferPool.GetSubmissionCount(commandBufferIndex);
- var list = _dsCache[commandBufferIndex][setIndex];
+ if (_dsLastCbIndex != commandBufferIndex || _dsLastSubmissionCount != submissionCount)
+ {
+ _dsLastCbIndex = commandBufferIndex;
+ _dsLastSubmissionCount = submissionCount;
+ Array.Clear(_dsCacheCursor);
+ }
+
+ _currentDsCache = _dsCache[commandBufferIndex];
+ }
+
+ public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(int setIndex, out bool isNew)
+ {
+ var list = _currentDsCache[setIndex];
int index = _dsCacheCursor[setIndex]++;
if (index == list.Count)
{
- var dsc = gd.DescriptorSetManager.AllocateDescriptorSet(gd.Api, DescriptorSetLayouts[setIndex]);
+ Span<DescriptorPoolSize> poolSizes = stackalloc DescriptorPoolSize[MaxPoolSizesPerSet];
+ poolSizes = GetDescriptorPoolSizes(poolSizes, setIndex);
+
+ int consumedDescriptors = _consumedDescriptorsPerSet[setIndex];
+
+ var dsc = _gd.DescriptorSetManager.AllocateDescriptorSet(
+ _gd.Api,
+ DescriptorSetLayouts[setIndex],
+ poolSizes,
+ setIndex,
+ consumedDescriptors,
+ false);
+
list.Add(dsc);
isNew = true;
return dsc;
@@ -76,6 +116,33 @@ namespace Ryujinx.Graphics.Vulkan
return list[index];
}
+ private static Span<DescriptorPoolSize> GetDescriptorPoolSizes(Span<DescriptorPoolSize> output, int setIndex)
+ {
+ int count = 1;
+
+ switch (setIndex)
+ {
+ case PipelineBase.UniformSetIndex:
+ output[0] = new(DescriptorType.UniformBuffer, DefaultUniformBufferPoolCapacity);
+ break;
+ case PipelineBase.StorageSetIndex:
+ output[0] = new(DescriptorType.StorageBuffer, DefaultStorageBufferPoolCapacity);
+ break;
+ case PipelineBase.TextureSetIndex:
+ output[0] = new(DescriptorType.CombinedImageSampler, DefaultTexturePoolCapacity);
+ output[1] = new(DescriptorType.UniformTexelBuffer, DefaultTexturePoolCapacity);
+ count = 2;
+ break;
+ case PipelineBase.ImageSetIndex:
+ output[0] = new(DescriptorType.StorageImage, DefaultImagePoolCapacity);
+ output[1] = new(DescriptorType.StorageTexelBuffer, DefaultImagePoolCapacity);
+ count = 2;
+ break;
+ }
+
+ return output[..count];
+ }
+
protected virtual unsafe void Dispose(bool disposing)
{
if (disposing)
diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
index 346fd916..0cb80ac7 100644
--- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
+++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs
@@ -464,13 +464,14 @@ namespace Ryujinx.Graphics.Vulkan
return true;
}
- public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(
- VulkanRenderer gd,
- int commandBufferIndex,
- int setIndex,
- out bool isNew)
+ public void UpdateDescriptorCacheCommandBufferIndex(int commandBufferIndex)
+ {
+ _plce.UpdateCommandBufferIndex(commandBufferIndex);
+ }
+
+ public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(int setIndex, out bool isNew)
{
- return _plce.GetNewDescriptorSetCollection(gd, commandBufferIndex, setIndex, out isNew);
+ return _plce.GetNewDescriptorSetCollection(setIndex, out isNew);
}
protected virtual void Dispose(bool disposing)
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index ac598c58..a483dc59 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -347,7 +347,7 @@ namespace Ryujinx.Graphics.Vulkan
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
- DescriptorSetManager = new DescriptorSetManager(_device);
+ DescriptorSetManager = new DescriptorSetManager(_device, PipelineBase.DescriptorSetLayouts);
PipelineLayoutCache = new PipelineLayoutCache();