mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-25 20:28:48 +08:00
[GPU] Fix detiled cache key for VulkanDetilePass (#620)
This commit is contained in:
@@ -8263,6 +8263,9 @@ public static partial class AgcExports
|
||||
private static bool IsGpuDetileBytesPerElement(int bytesPerElement) =>
|
||||
bytesPerElement is 4 or 8 or 16;
|
||||
|
||||
private static bool IsGpuDetileTextureType(uint type) =>
|
||||
type != Gen5TextureType3D;
|
||||
|
||||
private static bool TryGetTextureElementLayout(
|
||||
TextureDescriptor descriptor,
|
||||
uint sourceWidth,
|
||||
@@ -8746,6 +8749,7 @@ public static partial class AgcExports
|
||||
// take this path.
|
||||
if (_gpuDetileEnabled && hasElementLayout && !baseMipInTail &&
|
||||
IsGpuDetileBytesPerElement(bytesPerElement) &&
|
||||
IsGpuDetileTextureType(descriptor.Type) &&
|
||||
(long)physicalSourceByteCount * arrayLayers <= int.MaxValue)
|
||||
{
|
||||
var gpuArrayParams = GnmTiling.GetDetileParams(
|
||||
@@ -8789,6 +8793,12 @@ public static partial class AgcExports
|
||||
WriteGeneration: hasWriteGeneration ? writeGeneration : -1,
|
||||
ArrayedView: true,
|
||||
ArrayLayers: arrayLayers,
|
||||
// Must match the identity the CPU path below ships, or
|
||||
// the presenter caches this texture under a different
|
||||
// key than IsTextureContentCached queries above and the
|
||||
// texel-copy skip never hits for non-2D descriptors.
|
||||
Type: descriptor.Type,
|
||||
Depth: textureDepth,
|
||||
TiledSource: tiledLayers,
|
||||
Detile: gpuArrayParams);
|
||||
return true;
|
||||
@@ -8920,7 +8930,8 @@ public static partial class AgcExports
|
||||
// Arrayed textures are handled by the arrayed branch above (they package
|
||||
// every layer's tiled slice); this branch is the single-layer case.
|
||||
if (_gpuDetileEnabled && hasElementLayout && !baseMipInTail &&
|
||||
IsGpuDetileBytesPerElement(bytesPerElement) && !isArrayed)
|
||||
IsGpuDetileBytesPerElement(bytesPerElement) && !isArrayed &&
|
||||
IsGpuDetileTextureType(descriptor.Type))
|
||||
{
|
||||
var gpuDetileParams = GnmTiling.GetDetileParams(
|
||||
descriptor.TileMode, bytesPerElement, elementsWide, elementsHigh);
|
||||
@@ -8946,6 +8957,8 @@ public static partial class AgcExports
|
||||
Sampler: ToGuestSampler(samplerDescriptor),
|
||||
WriteGeneration: hasWriteGeneration ? writeGeneration : -1,
|
||||
ArrayedView: isArrayed,
|
||||
Type: descriptor.Type,
|
||||
Depth: textureDepth,
|
||||
TiledSource: source,
|
||||
Detile: gpuDetileParams);
|
||||
return true;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using SharpEmu.Libs.Agc;
|
||||
using SharpEmu.ShaderCompiler.Vulkan;
|
||||
using Silk.NET.Vulkan;
|
||||
@@ -45,6 +46,21 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
||||
private bool _initialized;
|
||||
private bool _disposed;
|
||||
|
||||
private PhysicalDeviceMemoryProperties _memoryProperties;
|
||||
private bool _memoryPropertiesLoaded;
|
||||
|
||||
private readonly Dictionary<int[], TermBuffer> _xorTermBuffers = new(ReferenceComparer.Instance);
|
||||
private readonly Dictionary<int[], TermBuffer> _blockTermBuffers = new(ReferenceComparer.Instance);
|
||||
private TermBuffer _placeholderTermBuffer;
|
||||
|
||||
private readonly Dictionary<(ulong Bucket, bool HostVisible), Stack<Allocation>> _bufferPool = new();
|
||||
private readonly List<Allocation> _allAllocations = new();
|
||||
|
||||
private readonly Stack<DescriptorSet> _freeDescriptorSets = new();
|
||||
private readonly List<DescriptorPool> _descriptorPools = new();
|
||||
private const uint DescriptorSetsPerPool = 64;
|
||||
private const ulong MinimumBufferBucket = 4096;
|
||||
|
||||
public VulkanDetilePass(
|
||||
Vk vk,
|
||||
Device device,
|
||||
@@ -69,23 +85,32 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
||||
parameters.Equation == DetileEquation.BlockTable) &&
|
||||
parameters.BytesPerElement is 4 or 8 or 16;
|
||||
|
||||
/// <summary>Transient per-detile resources the caller must retire once the
|
||||
/// command buffer they were recorded into has completed.</summary>
|
||||
public readonly record struct Transients(
|
||||
(VkBuffer Buffer, DeviceMemory Memory)[] Buffers,
|
||||
DescriptorPool DescriptorPool);
|
||||
/// <summary>Opaque handle to the pooled resources one recorded detile is using.
|
||||
/// The caller hands it back to <see cref="Retire"/> once the command buffer they
|
||||
/// were recorded into has completed; nothing is destroyed, the buffers and the
|
||||
/// descriptor set return to this pass's free lists for the next texture.</summary>
|
||||
public sealed class Transients
|
||||
{
|
||||
internal static readonly Transients Empty = new();
|
||||
|
||||
internal Allocation Tiled;
|
||||
internal Allocation Output;
|
||||
internal DescriptorSet Set;
|
||||
internal bool Rented;
|
||||
}
|
||||
|
||||
internal readonly record struct Allocation(
|
||||
VkBuffer Buffer,
|
||||
DeviceMemory Memory,
|
||||
ulong Capacity,
|
||||
bool HostVisible);
|
||||
|
||||
private readonly record struct TermBuffer(VkBuffer Buffer, ulong ByteSize);
|
||||
|
||||
private struct DetileResources
|
||||
{
|
||||
public VkBuffer Tiled;
|
||||
public DeviceMemory TiledMemory;
|
||||
public VkBuffer XTerm;
|
||||
public DeviceMemory XMemory;
|
||||
public VkBuffer YTerm;
|
||||
public DeviceMemory YMemory;
|
||||
public VkBuffer Output;
|
||||
public DeviceMemory OutputMemory;
|
||||
public DescriptorPool Pool;
|
||||
public Allocation Tiled;
|
||||
public Allocation Output;
|
||||
public DescriptorSet Set;
|
||||
public ulong OutputBytes;
|
||||
public uint SrcSliceElements;
|
||||
@@ -93,6 +118,15 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
||||
public uint UintsPerElement;
|
||||
}
|
||||
|
||||
private sealed class ReferenceComparer : IEqualityComparer<int[]>
|
||||
{
|
||||
public static readonly ReferenceComparer Instance = new();
|
||||
|
||||
public bool Equals(int[]? x, int[]? y) => ReferenceEquals(x, y);
|
||||
|
||||
public int GetHashCode(int[] obj) => RuntimeHelpers.GetHashCode(obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Records the deswizzle of <paramref name="tiled"/> into <paramref name="image"/>
|
||||
/// (<paramref name="texelWidth"/> x <paramref name="texelHeight"/> texels x
|
||||
@@ -117,7 +151,7 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
||||
in DetileParams parameters,
|
||||
out Transients transients)
|
||||
{
|
||||
transients = new Transients([], default);
|
||||
transients = Transients.Empty;
|
||||
if (_disposed || !Supports(parameters) || texelWidth == 0 || texelHeight == 0 || layers == 0 ||
|
||||
tiled.IsEmpty || tiled.Length % (int)(layers * (uint)parameters.BytesPerElement) != 0)
|
||||
{
|
||||
@@ -134,21 +168,33 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
||||
}
|
||||
catch
|
||||
{
|
||||
DestroyResources(in resources);
|
||||
ReleaseResources(in resources);
|
||||
throw;
|
||||
}
|
||||
|
||||
transients = new Transients(
|
||||
[
|
||||
(resources.Tiled, resources.TiledMemory),
|
||||
(resources.XTerm, resources.XMemory),
|
||||
(resources.YTerm, resources.YMemory),
|
||||
(resources.Output, resources.OutputMemory),
|
||||
],
|
||||
resources.Pool);
|
||||
transients = new Transients
|
||||
{
|
||||
Tiled = resources.Tiled,
|
||||
Output = resources.Output,
|
||||
Set = resources.Set,
|
||||
Rented = true,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Retire(Transients transients)
|
||||
{
|
||||
if (_disposed || transients is null || !transients.Rented)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
transients.Rented = false;
|
||||
ReturnBuffer(transients.Tiled);
|
||||
ReturnBuffer(transients.Output);
|
||||
_freeDescriptorSets.Push(transients.Set);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// One-shot variant used by the isolation self-test: records the detile onto a
|
||||
/// private command buffer, submits, waits, and frees every transient. Never
|
||||
@@ -207,7 +253,7 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
||||
_vk.FreeCommandBuffers(_device, _commandPool, 1, &commandBuffer);
|
||||
}
|
||||
|
||||
DestroyResources(in resources);
|
||||
ReleaseResources(in resources);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,24 +265,19 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
||||
// log2(bpp) bits of every term are 0, so the right shift is exact.
|
||||
// BlockTable: GetDetileParams' block table is already element offsets; it
|
||||
// goes in binding 1 and binding 2 is an unused placeholder.
|
||||
uint[] xTerm;
|
||||
uint[] yTerm;
|
||||
TermBuffer xTerm;
|
||||
TermBuffer yTerm;
|
||||
if (parameters.Equation == DetileEquation.BlockTable)
|
||||
{
|
||||
xTerm = new uint[parameters.BlockTable.Length];
|
||||
for (var index = 0; index < xTerm.Length; index++)
|
||||
{
|
||||
xTerm[index] = (uint)parameters.BlockTable[index];
|
||||
}
|
||||
|
||||
yTerm = [0];
|
||||
xTerm = GetTermBuffer(_blockTermBuffers, parameters.BlockTable, shift: 0);
|
||||
yTerm = GetPlaceholderTermBuffer();
|
||||
resources.EquationValue = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var shift = BitOperations.TrailingZeroCount((uint)parameters.BytesPerElement);
|
||||
xTerm = ToElementTerms(parameters.XByteTerm, shift);
|
||||
yTerm = ToElementTerms(parameters.YByteTerm, shift);
|
||||
xTerm = GetTermBuffer(_xorTermBuffers, parameters.XByteTerm, shift);
|
||||
yTerm = GetTermBuffer(_xorTermBuffers, parameters.YByteTerm, shift);
|
||||
resources.EquationValue = 0;
|
||||
}
|
||||
|
||||
@@ -249,25 +290,144 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
||||
resources.OutputBytes =
|
||||
(ulong)parameters.ElementsWide * (ulong)parameters.ElementsHigh * bytesPerElement * layers;
|
||||
|
||||
resources.Tiled = CreateHostBuffer((ulong)tiled.Length, BufferUsageFlags.StorageBufferBit, out resources.TiledMemory);
|
||||
UploadBytes(resources.TiledMemory, tiled);
|
||||
resources.XTerm = CreateHostBuffer((ulong)xTerm.Length * sizeof(uint), BufferUsageFlags.StorageBufferBit, out resources.XMemory);
|
||||
UploadUInts(resources.XMemory, xTerm);
|
||||
resources.YTerm = CreateHostBuffer((ulong)yTerm.Length * sizeof(uint), BufferUsageFlags.StorageBufferBit, out resources.YMemory);
|
||||
UploadUInts(resources.YMemory, yTerm);
|
||||
resources.Output = CreateHostBuffer(
|
||||
resources.OutputBytes,
|
||||
BufferUsageFlags.StorageBufferBit | BufferUsageFlags.TransferSrcBit,
|
||||
out resources.OutputMemory);
|
||||
resources.Tiled = RentBuffer((ulong)tiled.Length, hostVisible: true);
|
||||
UploadBytes(resources.Tiled.Memory, tiled);
|
||||
resources.Output = RentBuffer(resources.OutputBytes, hostVisible: false);
|
||||
|
||||
resources.Pool = CreateDescriptorPool();
|
||||
resources.Set = AllocateDescriptorSet(resources.Pool);
|
||||
resources.Set = RentDescriptorSet();
|
||||
WriteDescriptors(
|
||||
resources.Set,
|
||||
(resources.Tiled, (ulong)tiled.Length),
|
||||
(resources.XTerm, (ulong)xTerm.Length * sizeof(uint)),
|
||||
(resources.YTerm, (ulong)yTerm.Length * sizeof(uint)),
|
||||
(resources.Output, resources.OutputBytes));
|
||||
(resources.Tiled.Buffer, (ulong)tiled.Length),
|
||||
(xTerm.Buffer, xTerm.ByteSize),
|
||||
(yTerm.Buffer, yTerm.ByteSize),
|
||||
(resources.Output.Buffer, resources.OutputBytes));
|
||||
}
|
||||
|
||||
private TermBuffer GetTermBuffer(Dictionary<int[], TermBuffer> cache, int[] table, int shift)
|
||||
{
|
||||
if (cache.TryGetValue(table, out var cached))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
|
||||
var terms = ToElementTerms(table, shift);
|
||||
var byteSize = (ulong)terms.Length * sizeof(uint);
|
||||
var allocation = CreateBuffer(byteSize, hostVisible: true);
|
||||
UploadUInts(allocation.Memory, terms);
|
||||
var termBuffer = new TermBuffer(allocation.Buffer, byteSize);
|
||||
cache[table] = termBuffer;
|
||||
return termBuffer;
|
||||
}
|
||||
|
||||
private TermBuffer GetPlaceholderTermBuffer()
|
||||
{
|
||||
if (_placeholderTermBuffer.Buffer.Handle != 0)
|
||||
{
|
||||
return _placeholderTermBuffer;
|
||||
}
|
||||
|
||||
var allocation = CreateBuffer(sizeof(uint), hostVisible: true);
|
||||
UploadUInts(allocation.Memory, [0u]);
|
||||
_placeholderTermBuffer = new TermBuffer(allocation.Buffer, sizeof(uint));
|
||||
return _placeholderTermBuffer;
|
||||
}
|
||||
|
||||
private static ulong BucketFor(ulong size)
|
||||
{
|
||||
var bucket = MinimumBufferBucket;
|
||||
while (bucket < size)
|
||||
{
|
||||
bucket <<= 1;
|
||||
}
|
||||
|
||||
return bucket;
|
||||
}
|
||||
|
||||
private Allocation RentBuffer(ulong size, bool hostVisible)
|
||||
{
|
||||
var bucket = BucketFor(size);
|
||||
if (_bufferPool.TryGetValue((bucket, hostVisible), out var free) && free.Count > 0)
|
||||
{
|
||||
return free.Pop();
|
||||
}
|
||||
|
||||
return CreateBuffer(bucket, hostVisible);
|
||||
}
|
||||
|
||||
private void ReturnBuffer(Allocation allocation)
|
||||
{
|
||||
if (allocation.Buffer.Handle == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var key = (allocation.Capacity, allocation.HostVisible);
|
||||
if (!_bufferPool.TryGetValue(key, out var free))
|
||||
{
|
||||
free = new Stack<Allocation>();
|
||||
_bufferPool[key] = free;
|
||||
}
|
||||
|
||||
free.Push(allocation);
|
||||
}
|
||||
|
||||
private DescriptorSet RentDescriptorSet()
|
||||
{
|
||||
if (_freeDescriptorSets.Count > 0)
|
||||
{
|
||||
return _freeDescriptorSets.Pop();
|
||||
}
|
||||
|
||||
var poolSize = new DescriptorPoolSize
|
||||
{
|
||||
Type = DescriptorType.StorageBuffer,
|
||||
DescriptorCount = 4 * DescriptorSetsPerPool,
|
||||
};
|
||||
var poolInfo = new DescriptorPoolCreateInfo
|
||||
{
|
||||
SType = StructureType.DescriptorPoolCreateInfo,
|
||||
MaxSets = DescriptorSetsPerPool,
|
||||
PoolSizeCount = 1,
|
||||
PPoolSizes = &poolSize,
|
||||
};
|
||||
Check(
|
||||
_vk.CreateDescriptorPool(_device, &poolInfo, null, out var pool),
|
||||
"vkCreateDescriptorPool(detile)");
|
||||
_descriptorPools.Add(pool);
|
||||
|
||||
var layouts = stackalloc DescriptorSetLayout[(int)DescriptorSetsPerPool];
|
||||
for (var index = 0; index < DescriptorSetsPerPool; index++)
|
||||
{
|
||||
layouts[index] = _descriptorSetLayout;
|
||||
}
|
||||
|
||||
var sets = stackalloc DescriptorSet[(int)DescriptorSetsPerPool];
|
||||
var allocateInfo = new DescriptorSetAllocateInfo
|
||||
{
|
||||
SType = StructureType.DescriptorSetAllocateInfo,
|
||||
DescriptorPool = pool,
|
||||
DescriptorSetCount = DescriptorSetsPerPool,
|
||||
PSetLayouts = layouts,
|
||||
};
|
||||
Check(
|
||||
_vk.AllocateDescriptorSets(_device, &allocateInfo, sets),
|
||||
"vkAllocateDescriptorSets(detile)");
|
||||
for (var index = 0; index < DescriptorSetsPerPool; index++)
|
||||
{
|
||||
_freeDescriptorSets.Push(sets[index]);
|
||||
}
|
||||
|
||||
return _freeDescriptorSets.Pop();
|
||||
}
|
||||
|
||||
private void ReleaseResources(in DetileResources resources)
|
||||
{
|
||||
ReturnBuffer(resources.Tiled);
|
||||
ReturnBuffer(resources.Output);
|
||||
if (resources.Set.Handle != 0)
|
||||
{
|
||||
_freeDescriptorSets.Push(resources.Set);
|
||||
}
|
||||
}
|
||||
|
||||
private void RecordCommands(
|
||||
@@ -326,7 +486,7 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
||||
DstAccessMask = AccessFlags.TransferReadBit,
|
||||
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
Buffer = resources.Output,
|
||||
Buffer = resources.Output.Buffer,
|
||||
Offset = 0,
|
||||
Size = resources.OutputBytes,
|
||||
};
|
||||
@@ -367,7 +527,7 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
||||
ImageExtent = new Extent3D(texelWidth, texelHeight, 1),
|
||||
};
|
||||
_vk.CmdCopyBufferToImage(
|
||||
commandBuffer, resources.Output, image, ImageLayout.TransferDstOptimal, 1, ©Region);
|
||||
commandBuffer, resources.Output.Buffer, image, ImageLayout.TransferDstOptimal, 1, ©Region);
|
||||
|
||||
TransitionImage(
|
||||
commandBuffer,
|
||||
@@ -381,19 +541,6 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
||||
layers);
|
||||
}
|
||||
|
||||
private void DestroyResources(in DetileResources resources)
|
||||
{
|
||||
if (resources.Pool.Handle != 0)
|
||||
{
|
||||
_vk.DestroyDescriptorPool(_device, resources.Pool, null);
|
||||
}
|
||||
|
||||
DestroyBuffer(resources.Output, resources.OutputMemory);
|
||||
DestroyBuffer(resources.YTerm, resources.YMemory);
|
||||
DestroyBuffer(resources.XTerm, resources.XMemory);
|
||||
DestroyBuffer(resources.Tiled, resources.TiledMemory);
|
||||
}
|
||||
|
||||
private void EnsurePipeline()
|
||||
{
|
||||
if (_initialized)
|
||||
@@ -500,45 +647,67 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
||||
return terms;
|
||||
}
|
||||
|
||||
private VkBuffer CreateHostBuffer(ulong size, BufferUsageFlags usage, out DeviceMemory memory)
|
||||
private Allocation CreateBuffer(ulong size, bool hostVisible)
|
||||
{
|
||||
var bufferInfo = new BufferCreateInfo
|
||||
{
|
||||
SType = StructureType.BufferCreateInfo,
|
||||
Size = size,
|
||||
Usage = usage,
|
||||
Usage = BufferUsageFlags.StorageBufferBit | BufferUsageFlags.TransferSrcBit,
|
||||
SharingMode = SharingMode.Exclusive,
|
||||
};
|
||||
Check(_vk.CreateBuffer(_device, &bufferInfo, null, out var buffer), "vkCreateBuffer(detile)");
|
||||
|
||||
_vk.GetBufferMemoryRequirements(_device, buffer, out var requirements);
|
||||
var required = hostVisible
|
||||
? MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit
|
||||
: MemoryPropertyFlags.DeviceLocalBit;
|
||||
var allocateInfo = new MemoryAllocateInfo
|
||||
{
|
||||
SType = StructureType.MemoryAllocateInfo,
|
||||
AllocationSize = requirements.Size,
|
||||
MemoryTypeIndex = FindMemoryType(
|
||||
requirements.MemoryTypeBits,
|
||||
MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit),
|
||||
MemoryTypeIndex = FindMemoryType(requirements.MemoryTypeBits, required, hostVisible),
|
||||
};
|
||||
Check(_vk.AllocateMemory(_device, &allocateInfo, null, out memory), "vkAllocateMemory(detile)");
|
||||
Check(_vk.AllocateMemory(_device, &allocateInfo, null, out var memory), "vkAllocateMemory(detile)");
|
||||
Check(_vk.BindBufferMemory(_device, buffer, memory, 0), "vkBindBufferMemory(detile)");
|
||||
return buffer;
|
||||
var allocation = new Allocation(buffer, memory, size, hostVisible);
|
||||
_allAllocations.Add(allocation);
|
||||
return allocation;
|
||||
}
|
||||
|
||||
private uint FindMemoryType(uint typeBits, MemoryPropertyFlags requiredFlags)
|
||||
private uint FindMemoryType(uint typeBits, MemoryPropertyFlags requiredFlags, bool hostVisible)
|
||||
{
|
||||
_vk.GetPhysicalDeviceMemoryProperties(_physicalDevice, out var properties);
|
||||
var memoryTypes = &properties.MemoryTypes.Element0;
|
||||
for (uint index = 0; index < properties.MemoryTypeCount; index++)
|
||||
if (!_memoryPropertiesLoaded)
|
||||
{
|
||||
if ((typeBits & (1u << (int)index)) != 0 &&
|
||||
(memoryTypes[index].PropertyFlags & requiredFlags) == requiredFlags)
|
||||
_vk.GetPhysicalDeviceMemoryProperties(_physicalDevice, out _memoryProperties);
|
||||
_memoryPropertiesLoaded = true;
|
||||
}
|
||||
|
||||
fixed (PhysicalDeviceMemoryProperties* properties = &_memoryProperties)
|
||||
{
|
||||
var memoryTypes = &properties->MemoryTypes.Element0;
|
||||
for (uint index = 0; index < properties->MemoryTypeCount; index++)
|
||||
{
|
||||
return index;
|
||||
if ((typeBits & (1u << (int)index)) != 0 &&
|
||||
(memoryTypes[index].PropertyFlags & requiredFlags) == requiredFlags)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hostVisible)
|
||||
{
|
||||
for (uint index = 0; index < properties->MemoryTypeCount; index++)
|
||||
{
|
||||
if ((typeBits & (1u << (int)index)) != 0)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("No compatible Vulkan host-visible memory type for detile.");
|
||||
throw new InvalidOperationException("No compatible Vulkan memory type for detile.");
|
||||
}
|
||||
|
||||
private void UploadBytes(DeviceMemory memory, ReadOnlySpan<byte> data)
|
||||
@@ -558,42 +727,6 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
||||
_vk.UnmapMemory(_device, memory);
|
||||
}
|
||||
|
||||
private DescriptorPool CreateDescriptorPool()
|
||||
{
|
||||
var poolSize = new DescriptorPoolSize
|
||||
{
|
||||
Type = DescriptorType.StorageBuffer,
|
||||
DescriptorCount = 4,
|
||||
};
|
||||
var poolInfo = new DescriptorPoolCreateInfo
|
||||
{
|
||||
SType = StructureType.DescriptorPoolCreateInfo,
|
||||
MaxSets = 1,
|
||||
PoolSizeCount = 1,
|
||||
PPoolSizes = &poolSize,
|
||||
};
|
||||
Check(
|
||||
_vk.CreateDescriptorPool(_device, &poolInfo, null, out var pool),
|
||||
"vkCreateDescriptorPool(detile)");
|
||||
return pool;
|
||||
}
|
||||
|
||||
private DescriptorSet AllocateDescriptorSet(DescriptorPool pool)
|
||||
{
|
||||
var setLayout = _descriptorSetLayout;
|
||||
var allocateInfo = new DescriptorSetAllocateInfo
|
||||
{
|
||||
SType = StructureType.DescriptorSetAllocateInfo,
|
||||
DescriptorPool = pool,
|
||||
DescriptorSetCount = 1,
|
||||
PSetLayouts = &setLayout,
|
||||
};
|
||||
Check(
|
||||
_vk.AllocateDescriptorSets(_device, &allocateInfo, out var descriptorSet),
|
||||
"vkAllocateDescriptorSets(detile)");
|
||||
return descriptorSet;
|
||||
}
|
||||
|
||||
private void WriteDescriptors(
|
||||
DescriptorSet descriptorSet,
|
||||
(VkBuffer Buffer, ulong Size) binding0,
|
||||
@@ -714,6 +847,25 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
|
||||
foreach (var allocation in _allAllocations)
|
||||
{
|
||||
DestroyBuffer(allocation.Buffer, allocation.Memory);
|
||||
}
|
||||
|
||||
_allAllocations.Clear();
|
||||
_bufferPool.Clear();
|
||||
_xorTermBuffers.Clear();
|
||||
_blockTermBuffers.Clear();
|
||||
_placeholderTermBuffer = default;
|
||||
_freeDescriptorSets.Clear();
|
||||
foreach (var pool in _descriptorPools)
|
||||
{
|
||||
_vk.DestroyDescriptorPool(_device, pool, null);
|
||||
}
|
||||
|
||||
_descriptorPools.Clear();
|
||||
|
||||
if (_pipeline.Handle != 0)
|
||||
{
|
||||
_vk.DestroyPipeline(_device, _pipeline, null);
|
||||
|
||||
@@ -261,23 +261,7 @@ internal static unsafe class VulkanDetileSelfTest
|
||||
{
|
||||
vk.DestroyFence(device, fence, null);
|
||||
vk.FreeCommandBuffers(device, commandPool, 1, &commandBuffer);
|
||||
foreach (var (buffer, memory) in transients.Buffers)
|
||||
{
|
||||
if (buffer.Handle != 0)
|
||||
{
|
||||
vk.DestroyBuffer(device, buffer, null);
|
||||
}
|
||||
|
||||
if (memory.Handle != 0)
|
||||
{
|
||||
vk.FreeMemory(device, memory, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (transients.DescriptorPool.Handle != 0)
|
||||
{
|
||||
vk.DestroyDescriptorPool(device, transients.DescriptorPool, null);
|
||||
}
|
||||
pass.Retire(transients);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -2914,9 +2914,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
private readonly Stack<Fence> _recycledGuestFences = new();
|
||||
private readonly Stack<CommandBuffer> _recycledGuestCommandBuffers = new();
|
||||
private readonly List<(VkBuffer Buffer, DeviceMemory Memory)> _batchRetireBuffers = new();
|
||||
// Descriptor pools from GPU-detile passes recorded into the batch; retired
|
||||
// with the batch's fence, alongside _batchRetireBuffers.
|
||||
private readonly List<DescriptorPool> _batchRetireDescriptorPools = new();
|
||||
private readonly List<VulkanDetilePass.Transients> _batchRetireDetile = new();
|
||||
private const int MaxRecycledGuestFences = 32;
|
||||
private const int MaxRecycledGuestCommandBuffers = 32;
|
||||
private VkBuffer _stagingBuffer;
|
||||
@@ -3288,7 +3286,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
IReadOnlyList<TranslatedDrawResources> Resources,
|
||||
IReadOnlyList<GuestImageResource> TraceImages,
|
||||
IReadOnlyList<(VkBuffer Buffer, DeviceMemory Memory)> RetireBuffers,
|
||||
IReadOnlyList<DescriptorPool> RetirePools,
|
||||
IReadOnlyList<VulkanDetilePass.Transients> RetireDetile,
|
||||
ulong Timeline,
|
||||
string DebugName,
|
||||
VulkanGuestQueueIdentity Queue,
|
||||
@@ -4963,8 +4961,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
_batchResources.ToArray(),
|
||||
_batchTraceImages.ToArray(),
|
||||
_batchRetireBuffers.Count > 0 ? _batchRetireBuffers.ToArray() : [],
|
||||
retirePools: _batchRetireDescriptorPools.Count > 0
|
||||
? _batchRetireDescriptorPools.ToArray()
|
||||
retireDetile: _batchRetireDetile.Count > 0
|
||||
? _batchRetireDetile.ToArray()
|
||||
: []);
|
||||
}
|
||||
catch
|
||||
@@ -4983,9 +4981,9 @@ internal static unsafe class VulkanVideoPresenter
|
||||
_vk.FreeMemory(_device, memory, null);
|
||||
}
|
||||
|
||||
foreach (var pool in _batchRetireDescriptorPools)
|
||||
foreach (var transients in _batchRetireDetile)
|
||||
{
|
||||
_vk.DestroyDescriptorPool(_device, pool, null);
|
||||
_detilePass?.Retire(transients);
|
||||
}
|
||||
|
||||
ReleaseGuestCommandBuffer(_batchCommandBuffer);
|
||||
@@ -4996,7 +4994,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
_batchResources.Clear();
|
||||
_batchTraceImages.Clear();
|
||||
_batchRetireBuffers.Clear();
|
||||
_batchRetireDescriptorPools.Clear();
|
||||
_batchRetireDetile.Clear();
|
||||
_batchCommandBuffer = default;
|
||||
}
|
||||
}
|
||||
@@ -5007,7 +5005,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
IReadOnlyList<GuestImageResource> traceImages,
|
||||
IReadOnlyList<(VkBuffer Buffer, DeviceMemory Memory)>? retireBuffers = null,
|
||||
IReadOnlyList<TranslatedDrawResources>? referencedResources = null,
|
||||
IReadOnlyList<DescriptorPool>? retirePools = null)
|
||||
IReadOnlyList<VulkanDetilePass.Transients>? retireDetile = null)
|
||||
{
|
||||
var fence = AcquireGuestFence();
|
||||
try
|
||||
@@ -5065,7 +5063,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
resources,
|
||||
traceImages,
|
||||
retireBuffers ?? [],
|
||||
retirePools ?? [],
|
||||
retireDetile ?? [],
|
||||
_submitTimeline,
|
||||
resources.Count > 0 ? resources[0].DebugName : "batch",
|
||||
_activeGuestQueue,
|
||||
@@ -5237,9 +5235,11 @@ internal static unsafe class VulkanVideoPresenter
|
||||
_vk.FreeMemory(_device, memory, null);
|
||||
}
|
||||
|
||||
foreach (var pool in submission.RetirePools)
|
||||
// The fence has signalled, so the detile dispatch that used these
|
||||
// is done reading them; hand them back for the next texture.
|
||||
foreach (var transients in submission.RetireDetile)
|
||||
{
|
||||
_vk.DestroyDescriptorPool(_device, pool, null);
|
||||
_detilePass?.Retire(transients);
|
||||
}
|
||||
|
||||
ReleaseGuestCommandBuffer(submission.CommandBuffer);
|
||||
@@ -7855,7 +7855,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
// copy because this identity was marked cached; a miss here is
|
||||
// an invalidation race (eviction, cache clear). Self-heal by
|
||||
// reading the texels directly rather than rendering a fallback.
|
||||
if (texture.RgbaPixels.Length == 0)
|
||||
if (texture.RgbaPixels.Length == 0 &&
|
||||
texture.TiledSource is not { Length: > 0 })
|
||||
{
|
||||
var refreshed = TryReadGuestTexturePixels(texture);
|
||||
if (refreshed is null)
|
||||
@@ -8300,6 +8301,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
texture.Detile is { } detileCandidate &&
|
||||
texture.TiledSource is { Length: > 0 } tiledCandidate &&
|
||||
VulkanDetilePass.Supports(detileCandidate) &&
|
||||
!IsGuestTexture3D(texture.Type) &&
|
||||
detileCandidate.ElementsWide > 0 &&
|
||||
detileCandidate.ElementsHigh > 0 &&
|
||||
(long)tiledCandidate.Length >=
|
||||
@@ -8458,8 +8460,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
detileParameters,
|
||||
out var detileTransients))
|
||||
{
|
||||
_batchRetireBuffers.AddRange(detileTransients.Buffers);
|
||||
_batchRetireDescriptorPools.Add(detileTransients.DescriptorPool);
|
||||
_batchRetireDetile.Add(detileTransients);
|
||||
gpuDetiled = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user