RDNA3 Vulkan project
This commit is contained in:
383
src/Ryujinx.Graphics.Rdna3Vulkan/PipelineLayoutCacheEntry.cs
Normal file
383
src/Ryujinx.Graphics.Rdna3Vulkan/PipelineLayoutCacheEntry.cs
Normal file
@@ -0,0 +1,383 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Rdna3Vulkan
|
||||
{
|
||||
class PipelineLayoutCacheEntry
|
||||
{
|
||||
private const int MaxPoolSizesPerSet = 8;
|
||||
|
||||
private readonly VulkanRenderer _gd;
|
||||
private readonly Device _device;
|
||||
|
||||
public DescriptorSetLayout[] DescriptorSetLayouts { get; }
|
||||
public bool[] DescriptorSetLayoutsUpdateAfterBind { get; }
|
||||
public PipelineLayout PipelineLayout { get; }
|
||||
|
||||
private readonly int[] _consumedDescriptorsPerSet;
|
||||
private readonly DescriptorPoolSize[][] _poolSizes;
|
||||
|
||||
private readonly DescriptorSetManager _descriptorSetManager;
|
||||
|
||||
private readonly List<Auto<DescriptorSetCollection>>[][] _dsCache;
|
||||
private List<Auto<DescriptorSetCollection>>[] _currentDsCache;
|
||||
private readonly int[] _dsCacheCursor;
|
||||
private int _dsLastCbIndex;
|
||||
private int _dsLastSubmissionCount;
|
||||
|
||||
private struct ManualDescriptorSetEntry
|
||||
{
|
||||
public Auto<DescriptorSetCollection> DescriptorSet;
|
||||
public uint CbRefMask;
|
||||
public bool InUse;
|
||||
|
||||
public ManualDescriptorSetEntry(Auto<DescriptorSetCollection> descriptorSet, int cbIndex)
|
||||
{
|
||||
DescriptorSet = descriptorSet;
|
||||
CbRefMask = 1u << cbIndex;
|
||||
InUse = true;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct PendingManualDsConsumption
|
||||
{
|
||||
public FenceHolder Fence { get; }
|
||||
public int CommandBufferIndex { get; }
|
||||
public int SetIndex { get; }
|
||||
public int CacheIndex { get; }
|
||||
|
||||
public PendingManualDsConsumption(FenceHolder fence, int commandBufferIndex, int setIndex, int cacheIndex)
|
||||
{
|
||||
Fence = fence;
|
||||
CommandBufferIndex = commandBufferIndex;
|
||||
SetIndex = setIndex;
|
||||
CacheIndex = cacheIndex;
|
||||
fence.Get();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<ManualDescriptorSetEntry>[] _manualDsCache;
|
||||
private readonly Queue<PendingManualDsConsumption> _pendingManualDsConsumptions;
|
||||
private readonly Queue<int>[] _freeManualDsCacheEntries;
|
||||
|
||||
private readonly Dictionary<long, DescriptorSetTemplate> _pdTemplates;
|
||||
private readonly ResourceDescriptorCollection _pdDescriptors;
|
||||
private long _lastPdUsage;
|
||||
private DescriptorSetTemplate _lastPdTemplate;
|
||||
|
||||
private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount)
|
||||
{
|
||||
_gd = gd;
|
||||
_device = device;
|
||||
|
||||
_dsCache = new List<Auto<DescriptorSetCollection>>[CommandBufferPool.MaxCommandBuffers][];
|
||||
|
||||
for (int i = 0; i < CommandBufferPool.MaxCommandBuffers; i++)
|
||||
{
|
||||
_dsCache[i] = new List<Auto<DescriptorSetCollection>>[setsCount];
|
||||
|
||||
for (int j = 0; j < _dsCache[i].Length; j++)
|
||||
{
|
||||
_dsCache[i][j] = new List<Auto<DescriptorSetCollection>>();
|
||||
}
|
||||
}
|
||||
|
||||
_dsCacheCursor = new int[setsCount];
|
||||
_manualDsCache = new List<ManualDescriptorSetEntry>[setsCount];
|
||||
_pendingManualDsConsumptions = new Queue<PendingManualDsConsumption>();
|
||||
_freeManualDsCacheEntries = new Queue<int>[setsCount];
|
||||
}
|
||||
|
||||
public PipelineLayoutCacheEntry(
|
||||
VulkanRenderer gd,
|
||||
Device device,
|
||||
ReadOnlyCollection<ResourceDescriptorCollection> setDescriptors,
|
||||
bool usePushDescriptors) : this(gd, device, setDescriptors.Count)
|
||||
{
|
||||
ResourceLayouts layouts = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors);
|
||||
|
||||
DescriptorSetLayouts = layouts.DescriptorSetLayouts;
|
||||
DescriptorSetLayoutsUpdateAfterBind = layouts.DescriptorSetLayoutsUpdateAfterBind;
|
||||
PipelineLayout = layouts.PipelineLayout;
|
||||
|
||||
_consumedDescriptorsPerSet = new int[setDescriptors.Count];
|
||||
_poolSizes = new DescriptorPoolSize[setDescriptors.Count][];
|
||||
|
||||
Span<DescriptorPoolSize> poolSizes = stackalloc DescriptorPoolSize[MaxPoolSizesPerSet];
|
||||
|
||||
for (int setIndex = 0; setIndex < setDescriptors.Count; setIndex++)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
foreach (var descriptor in setDescriptors[setIndex].Descriptors)
|
||||
{
|
||||
count += descriptor.Count;
|
||||
}
|
||||
|
||||
_consumedDescriptorsPerSet[setIndex] = count;
|
||||
_poolSizes[setIndex] = GetDescriptorPoolSizes(poolSizes, setDescriptors[setIndex], DescriptorSetManager.MaxSets).ToArray();
|
||||
}
|
||||
|
||||
if (usePushDescriptors)
|
||||
{
|
||||
_pdDescriptors = setDescriptors[0];
|
||||
_pdTemplates = new();
|
||||
}
|
||||
|
||||
_descriptorSetManager = new DescriptorSetManager(_device, setDescriptors.Count);
|
||||
}
|
||||
|
||||
public void UpdateCommandBufferIndex(int commandBufferIndex)
|
||||
{
|
||||
int submissionCount = _gd.CommandBufferPool.GetSubmissionCount(commandBufferIndex);
|
||||
|
||||
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 = _descriptorSetManager.AllocateDescriptorSet(
|
||||
_gd.Api,
|
||||
DescriptorSetLayouts[setIndex],
|
||||
_poolSizes[setIndex],
|
||||
setIndex,
|
||||
_consumedDescriptorsPerSet[setIndex],
|
||||
DescriptorSetLayoutsUpdateAfterBind[setIndex]);
|
||||
|
||||
list.Add(dsc);
|
||||
isNew = true;
|
||||
return dsc;
|
||||
}
|
||||
|
||||
isNew = false;
|
||||
return list[index];
|
||||
}
|
||||
|
||||
public Auto<DescriptorSetCollection> GetNewManualDescriptorSetCollection(CommandBufferScoped cbs, int setIndex, out int cacheIndex)
|
||||
{
|
||||
FreeCompletedManualDescriptorSets();
|
||||
|
||||
var list = _manualDsCache[setIndex] ??= new();
|
||||
var span = CollectionsMarshal.AsSpan(list);
|
||||
|
||||
Queue<int> freeQueue = _freeManualDsCacheEntries[setIndex];
|
||||
|
||||
// Do we have at least one freed descriptor set? If so, just use that.
|
||||
if (freeQueue != null && freeQueue.TryDequeue(out int freeIndex))
|
||||
{
|
||||
ref ManualDescriptorSetEntry entry = ref span[freeIndex];
|
||||
|
||||
Debug.Assert(!entry.InUse && entry.CbRefMask == 0);
|
||||
|
||||
entry.InUse = true;
|
||||
entry.CbRefMask = 1u << cbs.CommandBufferIndex;
|
||||
cacheIndex = freeIndex;
|
||||
|
||||
_pendingManualDsConsumptions.Enqueue(new PendingManualDsConsumption(cbs.GetFence(), cbs.CommandBufferIndex, setIndex, freeIndex));
|
||||
|
||||
return entry.DescriptorSet;
|
||||
}
|
||||
|
||||
// Otherwise create a new descriptor set, and add to our pending queue for command buffer consumption tracking.
|
||||
var dsc = _descriptorSetManager.AllocateDescriptorSet(
|
||||
_gd.Api,
|
||||
DescriptorSetLayouts[setIndex],
|
||||
_poolSizes[setIndex],
|
||||
setIndex,
|
||||
_consumedDescriptorsPerSet[setIndex],
|
||||
DescriptorSetLayoutsUpdateAfterBind[setIndex]);
|
||||
|
||||
cacheIndex = list.Count;
|
||||
list.Add(new ManualDescriptorSetEntry(dsc, cbs.CommandBufferIndex));
|
||||
_pendingManualDsConsumptions.Enqueue(new PendingManualDsConsumption(cbs.GetFence(), cbs.CommandBufferIndex, setIndex, cacheIndex));
|
||||
|
||||
return dsc;
|
||||
}
|
||||
|
||||
public void UpdateManualDescriptorSetCollectionOwnership(CommandBufferScoped cbs, int setIndex, int cacheIndex)
|
||||
{
|
||||
FreeCompletedManualDescriptorSets();
|
||||
|
||||
var list = _manualDsCache[setIndex];
|
||||
var span = CollectionsMarshal.AsSpan(list);
|
||||
ref var entry = ref span[cacheIndex];
|
||||
|
||||
uint cbMask = 1u << cbs.CommandBufferIndex;
|
||||
|
||||
if ((entry.CbRefMask & cbMask) == 0)
|
||||
{
|
||||
entry.CbRefMask |= cbMask;
|
||||
|
||||
_pendingManualDsConsumptions.Enqueue(new PendingManualDsConsumption(cbs.GetFence(), cbs.CommandBufferIndex, setIndex, cacheIndex));
|
||||
}
|
||||
}
|
||||
|
||||
private void FreeCompletedManualDescriptorSets()
|
||||
{
|
||||
FenceHolder signalledFence = null;
|
||||
while (_pendingManualDsConsumptions.TryPeek(out var pds) && (pds.Fence == signalledFence || pds.Fence.IsSignaled()))
|
||||
{
|
||||
signalledFence = pds.Fence; // Already checked - don't need to do it again.
|
||||
var dequeued = _pendingManualDsConsumptions.Dequeue();
|
||||
Debug.Assert(dequeued.Fence == pds.Fence);
|
||||
pds.Fence.Put();
|
||||
|
||||
var span = CollectionsMarshal.AsSpan(_manualDsCache[dequeued.SetIndex]);
|
||||
ref var entry = ref span[dequeued.CacheIndex];
|
||||
entry.CbRefMask &= ~(1u << dequeued.CommandBufferIndex);
|
||||
|
||||
if (!entry.InUse && entry.CbRefMask == 0)
|
||||
{
|
||||
// If not in use by any array, and not bound to any command buffer, the descriptor set can be re-used immediately.
|
||||
(_freeManualDsCacheEntries[dequeued.SetIndex] ??= new()).Enqueue(dequeued.CacheIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseManualDescriptorSetCollection(int setIndex, int cacheIndex)
|
||||
{
|
||||
var list = _manualDsCache[setIndex];
|
||||
var span = CollectionsMarshal.AsSpan(list);
|
||||
|
||||
span[cacheIndex].InUse = false;
|
||||
|
||||
if (span[cacheIndex].CbRefMask == 0)
|
||||
{
|
||||
// This is no longer in use by any array, so if not bound to any command buffer, the descriptor set can be re-used immediately.
|
||||
(_freeManualDsCacheEntries[setIndex] ??= new()).Enqueue(cacheIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private static Span<DescriptorPoolSize> GetDescriptorPoolSizes(Span<DescriptorPoolSize> output, ResourceDescriptorCollection setDescriptor, uint multiplier)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int index = 0; index < setDescriptor.Descriptors.Count; index++)
|
||||
{
|
||||
ResourceDescriptor descriptor = setDescriptor.Descriptors[index];
|
||||
DescriptorType descriptorType = descriptor.Type.Convert();
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (int poolSizeIndex = 0; poolSizeIndex < count; poolSizeIndex++)
|
||||
{
|
||||
if (output[poolSizeIndex].Type == descriptorType)
|
||||
{
|
||||
output[poolSizeIndex].DescriptorCount += (uint)descriptor.Count * multiplier;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
output[count++] = new DescriptorPoolSize()
|
||||
{
|
||||
Type = descriptorType,
|
||||
DescriptorCount = (uint)descriptor.Count,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return output[..count];
|
||||
}
|
||||
|
||||
public DescriptorSetTemplate GetPushDescriptorTemplate(PipelineBindPoint pbp, long updateMask)
|
||||
{
|
||||
if (_lastPdUsage == updateMask && _lastPdTemplate != null)
|
||||
{
|
||||
// Most likely result is that it asks to update the same buffers.
|
||||
return _lastPdTemplate;
|
||||
}
|
||||
|
||||
if (!_pdTemplates.TryGetValue(updateMask, out DescriptorSetTemplate template))
|
||||
{
|
||||
template = new DescriptorSetTemplate(_gd, _device, _pdDescriptors, updateMask, this, pbp, 0);
|
||||
|
||||
_pdTemplates.Add(updateMask, template);
|
||||
}
|
||||
|
||||
_lastPdUsage = updateMask;
|
||||
_lastPdTemplate = template;
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
protected virtual unsafe void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_pdTemplates != null)
|
||||
{
|
||||
foreach (DescriptorSetTemplate template in _pdTemplates.Values)
|
||||
{
|
||||
template.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _dsCache.Length; i++)
|
||||
{
|
||||
for (int j = 0; j < _dsCache[i].Length; j++)
|
||||
{
|
||||
for (int k = 0; k < _dsCache[i][j].Count; k++)
|
||||
{
|
||||
_dsCache[i][j][k].Dispose();
|
||||
}
|
||||
|
||||
_dsCache[i][j].Clear();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _manualDsCache.Length; i++)
|
||||
{
|
||||
if (_manualDsCache[i] == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 0; j < _manualDsCache[i].Count; j++)
|
||||
{
|
||||
_manualDsCache[i][j].DescriptorSet.Dispose();
|
||||
}
|
||||
|
||||
_manualDsCache[i].Clear();
|
||||
}
|
||||
|
||||
_gd.Api.DestroyPipelineLayout(_device, PipelineLayout, null);
|
||||
|
||||
for (int i = 0; i < DescriptorSetLayouts.Length; i++)
|
||||
{
|
||||
_gd.Api.DestroyDescriptorSetLayout(_device, DescriptorSetLayouts[i], null);
|
||||
}
|
||||
|
||||
while (_pendingManualDsConsumptions.TryDequeue(out var pds))
|
||||
{
|
||||
pds.Fence.Put();
|
||||
}
|
||||
|
||||
_descriptorSetManager.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user