From 149438420898e4a3512d127647750fc93659f964 Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Sat, 25 Jul 2026 14:06:38 +0300 Subject: [PATCH] [GPU] Fix detiled cache key for VulkanDetilePass --- src/SharpEmu.Libs/Agc/AgcExports.cs | 15 +- .../VideoOut/VulkanDetilePass.cs | 388 ++++++++++++------ .../VideoOut/VulkanDetileSelfTest.cs | 18 +- .../VideoOut/VulkanVideoPresenter.cs | 33 +- 4 files changed, 302 insertions(+), 152 deletions(-) diff --git a/src/SharpEmu.Libs/Agc/AgcExports.cs b/src/SharpEmu.Libs/Agc/AgcExports.cs index dfbb8cf6..dc22ab0c 100644 --- a/src/SharpEmu.Libs/Agc/AgcExports.cs +++ b/src/SharpEmu.Libs/Agc/AgcExports.cs @@ -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; diff --git a/src/SharpEmu.Libs/VideoOut/VulkanDetilePass.cs b/src/SharpEmu.Libs/VideoOut/VulkanDetilePass.cs index d3773dec..a92ce7b0 100644 --- a/src/SharpEmu.Libs/VideoOut/VulkanDetilePass.cs +++ b/src/SharpEmu.Libs/VideoOut/VulkanDetilePass.cs @@ -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 _xorTermBuffers = new(ReferenceComparer.Instance); + private readonly Dictionary _blockTermBuffers = new(ReferenceComparer.Instance); + private TermBuffer _placeholderTermBuffer; + + private readonly Dictionary<(ulong Bucket, bool HostVisible), Stack> _bufferPool = new(); + private readonly List _allAllocations = new(); + + private readonly Stack _freeDescriptorSets = new(); + private readonly List _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; - /// Transient per-detile resources the caller must retire once the - /// command buffer they were recorded into has completed. - public readonly record struct Transients( - (VkBuffer Buffer, DeviceMemory Memory)[] Buffers, - DescriptorPool DescriptorPool); + /// Opaque handle to the pooled resources one recorded detile is using. + /// The caller hands it back to 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. + 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 + { + public static readonly ReferenceComparer Instance = new(); + + public bool Equals(int[]? x, int[]? y) => ReferenceEquals(x, y); + + public int GetHashCode(int[] obj) => RuntimeHelpers.GetHashCode(obj); + } + /// /// Records the deswizzle of into /// ( x 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); + } + /// /// 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 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(); + _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 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); diff --git a/src/SharpEmu.Libs/VideoOut/VulkanDetileSelfTest.cs b/src/SharpEmu.Libs/VideoOut/VulkanDetileSelfTest.cs index cd699577..71eda7a7 100644 --- a/src/SharpEmu.Libs/VideoOut/VulkanDetileSelfTest.cs +++ b/src/SharpEmu.Libs/VideoOut/VulkanDetileSelfTest.cs @@ -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; diff --git a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs index 1ecbb450..24373e55 100644 --- a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs +++ b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs @@ -2914,9 +2914,7 @@ internal static unsafe class VulkanVideoPresenter private readonly Stack _recycledGuestFences = new(); private readonly Stack _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 _batchRetireDescriptorPools = new(); + private readonly List _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 Resources, IReadOnlyList TraceImages, IReadOnlyList<(VkBuffer Buffer, DeviceMemory Memory)> RetireBuffers, - IReadOnlyList RetirePools, + IReadOnlyList 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 traceImages, IReadOnlyList<(VkBuffer Buffer, DeviceMemory Memory)>? retireBuffers = null, IReadOnlyList? referencedResources = null, - IReadOnlyList? retirePools = null) + IReadOnlyList? 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; } }