mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-26 12:48:39 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 99004a3ccd | |||
| e1a3b92567 | |||
| 8f9456229a | |||
| 5b602c0232 |
@@ -1449,6 +1449,9 @@ public sealed partial class DirectExecutionBackend
|
|||||||
var expectedSemaphoreTrywaitAgain =
|
var expectedSemaphoreTrywaitAgain =
|
||||||
string.Equals(nid, "H2a+IN9TP0E", StringComparison.Ordinal) &&
|
string.Equals(nid, "H2a+IN9TP0E", StringComparison.Ordinal) &&
|
||||||
result == OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN;
|
result == OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN;
|
||||||
|
var expectedPollSemaBusy =
|
||||||
|
string.Equals(nid, "12wOHk8ywb0", StringComparison.Ordinal) &&
|
||||||
|
result == OrbisGen2Result.ORBIS_GEN2_ERROR_BUSY;
|
||||||
var expectedNetAcceptWouldBlock =
|
var expectedNetAcceptWouldBlock =
|
||||||
string.Equals(nid, "PIWqhn9oSxc", StringComparison.Ordinal) &&
|
string.Equals(nid, "PIWqhn9oSxc", StringComparison.Ordinal) &&
|
||||||
resultValue == unchecked((int)0x80410123);
|
resultValue == unchecked((int)0x80410123);
|
||||||
@@ -1463,6 +1466,7 @@ public sealed partial class DirectExecutionBackend
|
|||||||
!expectedEqueueTimeout &&
|
!expectedEqueueTimeout &&
|
||||||
!expectedMutexTrylockBusy &&
|
!expectedMutexTrylockBusy &&
|
||||||
!expectedSemaphoreTrywaitAgain &&
|
!expectedSemaphoreTrywaitAgain &&
|
||||||
|
!expectedPollSemaBusy &&
|
||||||
!expectedNetAcceptWouldBlock &&
|
!expectedNetAcceptWouldBlock &&
|
||||||
!expectedUserServiceNoEvent &&
|
!expectedUserServiceNoEvent &&
|
||||||
!expectedPrivacyInvalidParameter)
|
!expectedPrivacyInvalidParameter)
|
||||||
|
|||||||
@@ -238,7 +238,15 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
var alignedSize = (size + 0xFFF) & ~0xFFFUL;
|
var alignedSize = (size + 0xFFF) & ~0xFFFUL;
|
||||||
var protection = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
|
var protection = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
|
||||||
var hostProtection = executable ? HostPageProtection.ReadWriteExecute : HostPageProtection.ReadWrite;
|
var hostProtection = executable ? HostPageProtection.ReadWriteExecute : HostPageProtection.ReadWrite;
|
||||||
var result = _hostMemory.Allocate(desiredAddress, alignedSize, hostProtection);
|
|
||||||
|
// Reserve address space only for very large non-executable regions; commit is done lazily later.
|
||||||
|
var reservedOnly = !executable &&
|
||||||
|
alignedSize >= LargeDataReserveThreshold &&
|
||||||
|
alignedSize > FullCommitRegionLimit;
|
||||||
|
|
||||||
|
var result = reservedOnly
|
||||||
|
? _hostMemory.Reserve(desiredAddress, alignedSize, HostPageProtection.ReadWrite)
|
||||||
|
: _hostMemory.Allocate(desiredAddress, alignedSize, hostProtection);
|
||||||
if (result == 0)
|
if (result == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -252,6 +260,8 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var state = reservedOnly ? ReserveRegion(actualAddress, alignedSize) : "n/a";
|
||||||
|
|
||||||
_gate.EnterWriteLock();
|
_gate.EnterWriteLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -260,7 +270,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
VirtualAddress = actualAddress,
|
VirtualAddress = actualAddress,
|
||||||
Size = alignedSize,
|
Size = alignedSize,
|
||||||
IsExecutable = executable,
|
IsExecutable = executable,
|
||||||
IsReservedOnly = false,
|
IsReservedOnly = reservedOnly,
|
||||||
Protection = protection
|
Protection = protection
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -269,6 +279,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
_gate.ExitWriteLock();
|
_gate.ExitWriteLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var allocationKind = executable ? "executable memory" : "data memory";
|
var allocationKind = executable ? "executable memory" : "data memory";
|
||||||
TraceVmem($"Allocated exact {allocationKind}: 0x{actualAddress:X16} - 0x{actualAddress + alignedSize:X16} ({alignedSize} bytes)");
|
TraceVmem($"Allocated exact {allocationKind}: 0x{actualAddress:X16} - 0x{actualAddress + alignedSize:X16} ({alignedSize} bytes)");
|
||||||
return true;
|
return true;
|
||||||
@@ -361,44 +372,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
|
|
||||||
var actualAddress = result;
|
var actualAddress = result;
|
||||||
|
|
||||||
var lazyPrimeState = "n/a";
|
var lazyPrimeState = reservedOnly ? ReserveRegion(actualAddress, alignedSize) : "n/a";
|
||||||
if (reservedOnly)
|
|
||||||
{
|
|
||||||
var primeBytes = Math.Min(alignedSize, LazyReservePrimeBytes);
|
|
||||||
if (primeBytes != 0)
|
|
||||||
{
|
|
||||||
ulong committedBytes = 0;
|
|
||||||
while (committedBytes < primeBytes)
|
|
||||||
{
|
|
||||||
var remaining = primeBytes - committedBytes;
|
|
||||||
var chunkBytes = Math.Min(remaining, LazyReservePrimeChunkBytes);
|
|
||||||
var commitAddress = actualAddress + committedBytes;
|
|
||||||
if (!_hostMemory.Commit(commitAddress, chunkBytes, HostPageProtection.ReadWrite))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
committedBytes += chunkBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (committedBytes != 0)
|
|
||||||
{
|
|
||||||
lazyPrimeState = committedBytes == primeBytes
|
|
||||||
? $"ok:{committedBytes:X}"
|
|
||||||
: $"partial:{committedBytes:X}/{primeBytes:X}";
|
|
||||||
TraceVmem($"Primed lazy region: 0x{actualAddress:X16} - 0x{actualAddress + committedBytes:X16} ({committedBytes} bytes)");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lazyPrimeState = $"fail:{primeBytes:X}";
|
|
||||||
TraceVmem($"Failed to prime lazy region at 0x{actualAddress:X16} ({primeBytes} bytes), continuing with on-demand commit");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lazyPrimeState = "skip:0";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_gate.EnterWriteLock();
|
_gate.EnterWriteLock();
|
||||||
try
|
try
|
||||||
@@ -425,6 +399,41 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
return actualAddress;
|
return actualAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string ReserveRegion(ulong actualAddress, ulong alignedSize)
|
||||||
|
{
|
||||||
|
var primeBytes = Math.Min(alignedSize, LazyReservePrimeBytes);
|
||||||
|
if (primeBytes == 0)
|
||||||
|
{
|
||||||
|
return "skip:0";
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong committedBytes = 0;
|
||||||
|
while (committedBytes < primeBytes)
|
||||||
|
{
|
||||||
|
var remaining = primeBytes - committedBytes;
|
||||||
|
var chunkBytes = Math.Min(remaining, LazyReservePrimeChunkBytes);
|
||||||
|
var commitAddress = actualAddress + committedBytes;
|
||||||
|
if (!_hostMemory.Commit(commitAddress, chunkBytes, HostPageProtection.ReadWrite))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
committedBytes += chunkBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (committedBytes != 0)
|
||||||
|
{
|
||||||
|
var state = committedBytes == primeBytes
|
||||||
|
? $"ok:{committedBytes:X}"
|
||||||
|
: $"partial:{committedBytes:X}/{primeBytes:X}";
|
||||||
|
TraceVmem($"region: 0x{actualAddress:X16} - 0x{actualAddress + committedBytes:X16} ({committedBytes} bytes)");
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceVmem($"Failed to reserve region at 0x{actualAddress:X16} ({primeBytes} bytes)!");
|
||||||
|
return $"fail:{primeBytes:X}";
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryBackFixedRange(ulong address, ulong size, bool executable)
|
public bool TryBackFixedRange(ulong address, ulong size, bool executable)
|
||||||
{
|
{
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
|
|||||||
@@ -8263,6 +8263,9 @@ public static partial class AgcExports
|
|||||||
private static bool IsGpuDetileBytesPerElement(int bytesPerElement) =>
|
private static bool IsGpuDetileBytesPerElement(int bytesPerElement) =>
|
||||||
bytesPerElement is 4 or 8 or 16;
|
bytesPerElement is 4 or 8 or 16;
|
||||||
|
|
||||||
|
private static bool IsGpuDetileTextureType(uint type) =>
|
||||||
|
type != Gen5TextureType3D;
|
||||||
|
|
||||||
private static bool TryGetTextureElementLayout(
|
private static bool TryGetTextureElementLayout(
|
||||||
TextureDescriptor descriptor,
|
TextureDescriptor descriptor,
|
||||||
uint sourceWidth,
|
uint sourceWidth,
|
||||||
@@ -8746,6 +8749,7 @@ public static partial class AgcExports
|
|||||||
// take this path.
|
// take this path.
|
||||||
if (_gpuDetileEnabled && hasElementLayout && !baseMipInTail &&
|
if (_gpuDetileEnabled && hasElementLayout && !baseMipInTail &&
|
||||||
IsGpuDetileBytesPerElement(bytesPerElement) &&
|
IsGpuDetileBytesPerElement(bytesPerElement) &&
|
||||||
|
IsGpuDetileTextureType(descriptor.Type) &&
|
||||||
(long)physicalSourceByteCount * arrayLayers <= int.MaxValue)
|
(long)physicalSourceByteCount * arrayLayers <= int.MaxValue)
|
||||||
{
|
{
|
||||||
var gpuArrayParams = GnmTiling.GetDetileParams(
|
var gpuArrayParams = GnmTiling.GetDetileParams(
|
||||||
@@ -8789,6 +8793,12 @@ public static partial class AgcExports
|
|||||||
WriteGeneration: hasWriteGeneration ? writeGeneration : -1,
|
WriteGeneration: hasWriteGeneration ? writeGeneration : -1,
|
||||||
ArrayedView: true,
|
ArrayedView: true,
|
||||||
ArrayLayers: arrayLayers,
|
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,
|
TiledSource: tiledLayers,
|
||||||
Detile: gpuArrayParams);
|
Detile: gpuArrayParams);
|
||||||
return true;
|
return true;
|
||||||
@@ -8920,7 +8930,8 @@ public static partial class AgcExports
|
|||||||
// Arrayed textures are handled by the arrayed branch above (they package
|
// Arrayed textures are handled by the arrayed branch above (they package
|
||||||
// every layer's tiled slice); this branch is the single-layer case.
|
// every layer's tiled slice); this branch is the single-layer case.
|
||||||
if (_gpuDetileEnabled && hasElementLayout && !baseMipInTail &&
|
if (_gpuDetileEnabled && hasElementLayout && !baseMipInTail &&
|
||||||
IsGpuDetileBytesPerElement(bytesPerElement) && !isArrayed)
|
IsGpuDetileBytesPerElement(bytesPerElement) && !isArrayed &&
|
||||||
|
IsGpuDetileTextureType(descriptor.Type))
|
||||||
{
|
{
|
||||||
var gpuDetileParams = GnmTiling.GetDetileParams(
|
var gpuDetileParams = GnmTiling.GetDetileParams(
|
||||||
descriptor.TileMode, bytesPerElement, elementsWide, elementsHigh);
|
descriptor.TileMode, bytesPerElement, elementsWide, elementsHigh);
|
||||||
@@ -8946,6 +8957,8 @@ public static partial class AgcExports
|
|||||||
Sampler: ToGuestSampler(samplerDescriptor),
|
Sampler: ToGuestSampler(samplerDescriptor),
|
||||||
WriteGeneration: hasWriteGeneration ? writeGeneration : -1,
|
WriteGeneration: hasWriteGeneration ? writeGeneration : -1,
|
||||||
ArrayedView: isArrayed,
|
ArrayedView: isArrayed,
|
||||||
|
Type: descriptor.Type,
|
||||||
|
Depth: textureDepth,
|
||||||
TiledSource: source,
|
TiledSource: source,
|
||||||
Detile: gpuDetileParams);
|
Detile: gpuDetileParams);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using SharpEmu.Libs.Agc;
|
using SharpEmu.Libs.Agc;
|
||||||
using SharpEmu.ShaderCompiler.Vulkan;
|
using SharpEmu.ShaderCompiler.Vulkan;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
@@ -45,6 +46,21 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
|||||||
private bool _initialized;
|
private bool _initialized;
|
||||||
private bool _disposed;
|
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(
|
public VulkanDetilePass(
|
||||||
Vk vk,
|
Vk vk,
|
||||||
Device device,
|
Device device,
|
||||||
@@ -69,23 +85,32 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
|||||||
parameters.Equation == DetileEquation.BlockTable) &&
|
parameters.Equation == DetileEquation.BlockTable) &&
|
||||||
parameters.BytesPerElement is 4 or 8 or 16;
|
parameters.BytesPerElement is 4 or 8 or 16;
|
||||||
|
|
||||||
/// <summary>Transient per-detile resources the caller must retire once the
|
/// <summary>Opaque handle to the pooled resources one recorded detile is using.
|
||||||
/// command buffer they were recorded into has completed.</summary>
|
/// The caller hands it back to <see cref="Retire"/> once the command buffer they
|
||||||
public readonly record struct Transients(
|
/// were recorded into has completed; nothing is destroyed, the buffers and the
|
||||||
(VkBuffer Buffer, DeviceMemory Memory)[] Buffers,
|
/// descriptor set return to this pass's free lists for the next texture.</summary>
|
||||||
DescriptorPool DescriptorPool);
|
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
|
private struct DetileResources
|
||||||
{
|
{
|
||||||
public VkBuffer Tiled;
|
public Allocation Tiled;
|
||||||
public DeviceMemory TiledMemory;
|
public Allocation Output;
|
||||||
public VkBuffer XTerm;
|
|
||||||
public DeviceMemory XMemory;
|
|
||||||
public VkBuffer YTerm;
|
|
||||||
public DeviceMemory YMemory;
|
|
||||||
public VkBuffer Output;
|
|
||||||
public DeviceMemory OutputMemory;
|
|
||||||
public DescriptorPool Pool;
|
|
||||||
public DescriptorSet Set;
|
public DescriptorSet Set;
|
||||||
public ulong OutputBytes;
|
public ulong OutputBytes;
|
||||||
public uint SrcSliceElements;
|
public uint SrcSliceElements;
|
||||||
@@ -93,6 +118,15 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
|||||||
public uint UintsPerElement;
|
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>
|
/// <summary>
|
||||||
/// Records the deswizzle of <paramref name="tiled"/> into <paramref name="image"/>
|
/// Records the deswizzle of <paramref name="tiled"/> into <paramref name="image"/>
|
||||||
/// (<paramref name="texelWidth"/> x <paramref name="texelHeight"/> texels x
|
/// (<paramref name="texelWidth"/> x <paramref name="texelHeight"/> texels x
|
||||||
@@ -117,7 +151,7 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
|||||||
in DetileParams parameters,
|
in DetileParams parameters,
|
||||||
out Transients transients)
|
out Transients transients)
|
||||||
{
|
{
|
||||||
transients = new Transients([], default);
|
transients = Transients.Empty;
|
||||||
if (_disposed || !Supports(parameters) || texelWidth == 0 || texelHeight == 0 || layers == 0 ||
|
if (_disposed || !Supports(parameters) || texelWidth == 0 || texelHeight == 0 || layers == 0 ||
|
||||||
tiled.IsEmpty || tiled.Length % (int)(layers * (uint)parameters.BytesPerElement) != 0)
|
tiled.IsEmpty || tiled.Length % (int)(layers * (uint)parameters.BytesPerElement) != 0)
|
||||||
{
|
{
|
||||||
@@ -134,21 +168,33 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
DestroyResources(in resources);
|
ReleaseResources(in resources);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
transients = new Transients(
|
transients = new Transients
|
||||||
[
|
{
|
||||||
(resources.Tiled, resources.TiledMemory),
|
Tiled = resources.Tiled,
|
||||||
(resources.XTerm, resources.XMemory),
|
Output = resources.Output,
|
||||||
(resources.YTerm, resources.YMemory),
|
Set = resources.Set,
|
||||||
(resources.Output, resources.OutputMemory),
|
Rented = true,
|
||||||
],
|
};
|
||||||
resources.Pool);
|
|
||||||
return 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>
|
/// <summary>
|
||||||
/// One-shot variant used by the isolation self-test: records the detile onto a
|
/// One-shot variant used by the isolation self-test: records the detile onto a
|
||||||
/// private command buffer, submits, waits, and frees every transient. Never
|
/// 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);
|
_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.
|
// log2(bpp) bits of every term are 0, so the right shift is exact.
|
||||||
// BlockTable: GetDetileParams' block table is already element offsets; it
|
// BlockTable: GetDetileParams' block table is already element offsets; it
|
||||||
// goes in binding 1 and binding 2 is an unused placeholder.
|
// goes in binding 1 and binding 2 is an unused placeholder.
|
||||||
uint[] xTerm;
|
TermBuffer xTerm;
|
||||||
uint[] yTerm;
|
TermBuffer yTerm;
|
||||||
if (parameters.Equation == DetileEquation.BlockTable)
|
if (parameters.Equation == DetileEquation.BlockTable)
|
||||||
{
|
{
|
||||||
xTerm = new uint[parameters.BlockTable.Length];
|
xTerm = GetTermBuffer(_blockTermBuffers, parameters.BlockTable, shift: 0);
|
||||||
for (var index = 0; index < xTerm.Length; index++)
|
yTerm = GetPlaceholderTermBuffer();
|
||||||
{
|
|
||||||
xTerm[index] = (uint)parameters.BlockTable[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
yTerm = [0];
|
|
||||||
resources.EquationValue = 1;
|
resources.EquationValue = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var shift = BitOperations.TrailingZeroCount((uint)parameters.BytesPerElement);
|
var shift = BitOperations.TrailingZeroCount((uint)parameters.BytesPerElement);
|
||||||
xTerm = ToElementTerms(parameters.XByteTerm, shift);
|
xTerm = GetTermBuffer(_xorTermBuffers, parameters.XByteTerm, shift);
|
||||||
yTerm = ToElementTerms(parameters.YByteTerm, shift);
|
yTerm = GetTermBuffer(_xorTermBuffers, parameters.YByteTerm, shift);
|
||||||
resources.EquationValue = 0;
|
resources.EquationValue = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,25 +290,144 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
|||||||
resources.OutputBytes =
|
resources.OutputBytes =
|
||||||
(ulong)parameters.ElementsWide * (ulong)parameters.ElementsHigh * bytesPerElement * layers;
|
(ulong)parameters.ElementsWide * (ulong)parameters.ElementsHigh * bytesPerElement * layers;
|
||||||
|
|
||||||
resources.Tiled = CreateHostBuffer((ulong)tiled.Length, BufferUsageFlags.StorageBufferBit, out resources.TiledMemory);
|
resources.Tiled = RentBuffer((ulong)tiled.Length, hostVisible: true);
|
||||||
UploadBytes(resources.TiledMemory, tiled);
|
UploadBytes(resources.Tiled.Memory, tiled);
|
||||||
resources.XTerm = CreateHostBuffer((ulong)xTerm.Length * sizeof(uint), BufferUsageFlags.StorageBufferBit, out resources.XMemory);
|
resources.Output = RentBuffer(resources.OutputBytes, hostVisible: false);
|
||||||
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.Pool = CreateDescriptorPool();
|
resources.Set = RentDescriptorSet();
|
||||||
resources.Set = AllocateDescriptorSet(resources.Pool);
|
|
||||||
WriteDescriptors(
|
WriteDescriptors(
|
||||||
resources.Set,
|
resources.Set,
|
||||||
(resources.Tiled, (ulong)tiled.Length),
|
(resources.Tiled.Buffer, (ulong)tiled.Length),
|
||||||
(resources.XTerm, (ulong)xTerm.Length * sizeof(uint)),
|
(xTerm.Buffer, xTerm.ByteSize),
|
||||||
(resources.YTerm, (ulong)yTerm.Length * sizeof(uint)),
|
(yTerm.Buffer, yTerm.ByteSize),
|
||||||
(resources.Output, resources.OutputBytes));
|
(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(
|
private void RecordCommands(
|
||||||
@@ -326,7 +486,7 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
|||||||
DstAccessMask = AccessFlags.TransferReadBit,
|
DstAccessMask = AccessFlags.TransferReadBit,
|
||||||
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||||
DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||||
Buffer = resources.Output,
|
Buffer = resources.Output.Buffer,
|
||||||
Offset = 0,
|
Offset = 0,
|
||||||
Size = resources.OutputBytes,
|
Size = resources.OutputBytes,
|
||||||
};
|
};
|
||||||
@@ -367,7 +527,7 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
|||||||
ImageExtent = new Extent3D(texelWidth, texelHeight, 1),
|
ImageExtent = new Extent3D(texelWidth, texelHeight, 1),
|
||||||
};
|
};
|
||||||
_vk.CmdCopyBufferToImage(
|
_vk.CmdCopyBufferToImage(
|
||||||
commandBuffer, resources.Output, image, ImageLayout.TransferDstOptimal, 1, ©Region);
|
commandBuffer, resources.Output.Buffer, image, ImageLayout.TransferDstOptimal, 1, ©Region);
|
||||||
|
|
||||||
TransitionImage(
|
TransitionImage(
|
||||||
commandBuffer,
|
commandBuffer,
|
||||||
@@ -381,19 +541,6 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
|||||||
layers);
|
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()
|
private void EnsurePipeline()
|
||||||
{
|
{
|
||||||
if (_initialized)
|
if (_initialized)
|
||||||
@@ -500,45 +647,67 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
|||||||
return terms;
|
return terms;
|
||||||
}
|
}
|
||||||
|
|
||||||
private VkBuffer CreateHostBuffer(ulong size, BufferUsageFlags usage, out DeviceMemory memory)
|
private Allocation CreateBuffer(ulong size, bool hostVisible)
|
||||||
{
|
{
|
||||||
var bufferInfo = new BufferCreateInfo
|
var bufferInfo = new BufferCreateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.BufferCreateInfo,
|
SType = StructureType.BufferCreateInfo,
|
||||||
Size = size,
|
Size = size,
|
||||||
Usage = usage,
|
Usage = BufferUsageFlags.StorageBufferBit | BufferUsageFlags.TransferSrcBit,
|
||||||
SharingMode = SharingMode.Exclusive,
|
SharingMode = SharingMode.Exclusive,
|
||||||
};
|
};
|
||||||
Check(_vk.CreateBuffer(_device, &bufferInfo, null, out var buffer), "vkCreateBuffer(detile)");
|
Check(_vk.CreateBuffer(_device, &bufferInfo, null, out var buffer), "vkCreateBuffer(detile)");
|
||||||
|
|
||||||
_vk.GetBufferMemoryRequirements(_device, buffer, out var requirements);
|
_vk.GetBufferMemoryRequirements(_device, buffer, out var requirements);
|
||||||
|
var required = hostVisible
|
||||||
|
? MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit
|
||||||
|
: MemoryPropertyFlags.DeviceLocalBit;
|
||||||
var allocateInfo = new MemoryAllocateInfo
|
var allocateInfo = new MemoryAllocateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.MemoryAllocateInfo,
|
SType = StructureType.MemoryAllocateInfo,
|
||||||
AllocationSize = requirements.Size,
|
AllocationSize = requirements.Size,
|
||||||
MemoryTypeIndex = FindMemoryType(
|
MemoryTypeIndex = FindMemoryType(requirements.MemoryTypeBits, required, hostVisible),
|
||||||
requirements.MemoryTypeBits,
|
|
||||||
MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit),
|
|
||||||
};
|
};
|
||||||
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)");
|
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);
|
if (!_memoryPropertiesLoaded)
|
||||||
var memoryTypes = &properties.MemoryTypes.Element0;
|
|
||||||
for (uint index = 0; index < properties.MemoryTypeCount; index++)
|
|
||||||
{
|
{
|
||||||
if ((typeBits & (1u << (int)index)) != 0 &&
|
_vk.GetPhysicalDeviceMemoryProperties(_physicalDevice, out _memoryProperties);
|
||||||
(memoryTypes[index].PropertyFlags & requiredFlags) == requiredFlags)
|
_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)
|
private void UploadBytes(DeviceMemory memory, ReadOnlySpan<byte> data)
|
||||||
@@ -558,42 +727,6 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
|||||||
_vk.UnmapMemory(_device, memory);
|
_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(
|
private void WriteDescriptors(
|
||||||
DescriptorSet descriptorSet,
|
DescriptorSet descriptorSet,
|
||||||
(VkBuffer Buffer, ulong Size) binding0,
|
(VkBuffer Buffer, ulong Size) binding0,
|
||||||
@@ -714,6 +847,25 @@ internal sealed unsafe class VulkanDetilePass : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
_disposed = true;
|
_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)
|
if (_pipeline.Handle != 0)
|
||||||
{
|
{
|
||||||
_vk.DestroyPipeline(_device, _pipeline, null);
|
_vk.DestroyPipeline(_device, _pipeline, null);
|
||||||
|
|||||||
@@ -261,23 +261,7 @@ internal static unsafe class VulkanDetileSelfTest
|
|||||||
{
|
{
|
||||||
vk.DestroyFence(device, fence, null);
|
vk.DestroyFence(device, fence, null);
|
||||||
vk.FreeCommandBuffers(device, commandPool, 1, &commandBuffer);
|
vk.FreeCommandBuffers(device, commandPool, 1, &commandBuffer);
|
||||||
foreach (var (buffer, memory) in transients.Buffers)
|
pass.Retire(transients);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -2914,9 +2914,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
private readonly Stack<Fence> _recycledGuestFences = new();
|
private readonly Stack<Fence> _recycledGuestFences = new();
|
||||||
private readonly Stack<CommandBuffer> _recycledGuestCommandBuffers = new();
|
private readonly Stack<CommandBuffer> _recycledGuestCommandBuffers = new();
|
||||||
private readonly List<(VkBuffer Buffer, DeviceMemory Memory)> _batchRetireBuffers = new();
|
private readonly List<(VkBuffer Buffer, DeviceMemory Memory)> _batchRetireBuffers = new();
|
||||||
// Descriptor pools from GPU-detile passes recorded into the batch; retired
|
private readonly List<VulkanDetilePass.Transients> _batchRetireDetile = new();
|
||||||
// with the batch's fence, alongside _batchRetireBuffers.
|
|
||||||
private readonly List<DescriptorPool> _batchRetireDescriptorPools = new();
|
|
||||||
private const int MaxRecycledGuestFences = 32;
|
private const int MaxRecycledGuestFences = 32;
|
||||||
private const int MaxRecycledGuestCommandBuffers = 32;
|
private const int MaxRecycledGuestCommandBuffers = 32;
|
||||||
private VkBuffer _stagingBuffer;
|
private VkBuffer _stagingBuffer;
|
||||||
@@ -3288,7 +3286,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
IReadOnlyList<TranslatedDrawResources> Resources,
|
IReadOnlyList<TranslatedDrawResources> Resources,
|
||||||
IReadOnlyList<GuestImageResource> TraceImages,
|
IReadOnlyList<GuestImageResource> TraceImages,
|
||||||
IReadOnlyList<(VkBuffer Buffer, DeviceMemory Memory)> RetireBuffers,
|
IReadOnlyList<(VkBuffer Buffer, DeviceMemory Memory)> RetireBuffers,
|
||||||
IReadOnlyList<DescriptorPool> RetirePools,
|
IReadOnlyList<VulkanDetilePass.Transients> RetireDetile,
|
||||||
ulong Timeline,
|
ulong Timeline,
|
||||||
string DebugName,
|
string DebugName,
|
||||||
VulkanGuestQueueIdentity Queue,
|
VulkanGuestQueueIdentity Queue,
|
||||||
@@ -4963,8 +4961,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
_batchResources.ToArray(),
|
_batchResources.ToArray(),
|
||||||
_batchTraceImages.ToArray(),
|
_batchTraceImages.ToArray(),
|
||||||
_batchRetireBuffers.Count > 0 ? _batchRetireBuffers.ToArray() : [],
|
_batchRetireBuffers.Count > 0 ? _batchRetireBuffers.ToArray() : [],
|
||||||
retirePools: _batchRetireDescriptorPools.Count > 0
|
retireDetile: _batchRetireDetile.Count > 0
|
||||||
? _batchRetireDescriptorPools.ToArray()
|
? _batchRetireDetile.ToArray()
|
||||||
: []);
|
: []);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -4983,9 +4981,9 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
_vk.FreeMemory(_device, memory, null);
|
_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);
|
ReleaseGuestCommandBuffer(_batchCommandBuffer);
|
||||||
@@ -4996,7 +4994,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
_batchResources.Clear();
|
_batchResources.Clear();
|
||||||
_batchTraceImages.Clear();
|
_batchTraceImages.Clear();
|
||||||
_batchRetireBuffers.Clear();
|
_batchRetireBuffers.Clear();
|
||||||
_batchRetireDescriptorPools.Clear();
|
_batchRetireDetile.Clear();
|
||||||
_batchCommandBuffer = default;
|
_batchCommandBuffer = default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5007,7 +5005,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
IReadOnlyList<GuestImageResource> traceImages,
|
IReadOnlyList<GuestImageResource> traceImages,
|
||||||
IReadOnlyList<(VkBuffer Buffer, DeviceMemory Memory)>? retireBuffers = null,
|
IReadOnlyList<(VkBuffer Buffer, DeviceMemory Memory)>? retireBuffers = null,
|
||||||
IReadOnlyList<TranslatedDrawResources>? referencedResources = null,
|
IReadOnlyList<TranslatedDrawResources>? referencedResources = null,
|
||||||
IReadOnlyList<DescriptorPool>? retirePools = null)
|
IReadOnlyList<VulkanDetilePass.Transients>? retireDetile = null)
|
||||||
{
|
{
|
||||||
var fence = AcquireGuestFence();
|
var fence = AcquireGuestFence();
|
||||||
try
|
try
|
||||||
@@ -5065,7 +5063,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
resources,
|
resources,
|
||||||
traceImages,
|
traceImages,
|
||||||
retireBuffers ?? [],
|
retireBuffers ?? [],
|
||||||
retirePools ?? [],
|
retireDetile ?? [],
|
||||||
_submitTimeline,
|
_submitTimeline,
|
||||||
resources.Count > 0 ? resources[0].DebugName : "batch",
|
resources.Count > 0 ? resources[0].DebugName : "batch",
|
||||||
_activeGuestQueue,
|
_activeGuestQueue,
|
||||||
@@ -5237,9 +5235,11 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
_vk.FreeMemory(_device, memory, null);
|
_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);
|
ReleaseGuestCommandBuffer(submission.CommandBuffer);
|
||||||
@@ -7855,7 +7855,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
// copy because this identity was marked cached; a miss here is
|
// copy because this identity was marked cached; a miss here is
|
||||||
// an invalidation race (eviction, cache clear). Self-heal by
|
// an invalidation race (eviction, cache clear). Self-heal by
|
||||||
// reading the texels directly rather than rendering a fallback.
|
// 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);
|
var refreshed = TryReadGuestTexturePixels(texture);
|
||||||
if (refreshed is null)
|
if (refreshed is null)
|
||||||
@@ -8300,6 +8301,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
texture.Detile is { } detileCandidate &&
|
texture.Detile is { } detileCandidate &&
|
||||||
texture.TiledSource is { Length: > 0 } tiledCandidate &&
|
texture.TiledSource is { Length: > 0 } tiledCandidate &&
|
||||||
VulkanDetilePass.Supports(detileCandidate) &&
|
VulkanDetilePass.Supports(detileCandidate) &&
|
||||||
|
!IsGuestTexture3D(texture.Type) &&
|
||||||
detileCandidate.ElementsWide > 0 &&
|
detileCandidate.ElementsWide > 0 &&
|
||||||
detileCandidate.ElementsHigh > 0 &&
|
detileCandidate.ElementsHigh > 0 &&
|
||||||
(long)tiledCandidate.Length >=
|
(long)tiledCandidate.Length >=
|
||||||
@@ -8458,8 +8460,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
detileParameters,
|
detileParameters,
|
||||||
out var detileTransients))
|
out var detileTransients))
|
||||||
{
|
{
|
||||||
_batchRetireBuffers.AddRange(detileTransients.Buffers);
|
_batchRetireDetile.Add(detileTransients);
|
||||||
_batchRetireDescriptorPools.Add(detileTransients.DescriptorPool);
|
|
||||||
gpuDetiled = true;
|
gpuDetiled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9444,7 +9445,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
size,
|
size,
|
||||||
BufferUsageFlags.StorageBufferBit,
|
BufferUsageFlags.StorageBufferBit,
|
||||||
MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit,
|
MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit,
|
||||||
out var memory);
|
out var memory,
|
||||||
|
preferredMemoryFlags: MemoryPropertyFlags.HostCachedBit);
|
||||||
void* mapped;
|
void* mapped;
|
||||||
Check(_vk.MapMemory(_device, memory, 0, size, 0, &mapped), "vkMapMemory(guest buffer)");
|
Check(_vk.MapMemory(_device, memory, 0, size, 0, &mapped), "vkMapMemory(guest buffer)");
|
||||||
var shadow = new byte[checked((int)size)];
|
var shadow = new byte[checked((int)size)];
|
||||||
@@ -10253,7 +10255,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
ulong size,
|
ulong size,
|
||||||
BufferUsageFlags usage,
|
BufferUsageFlags usage,
|
||||||
MemoryPropertyFlags memoryFlags,
|
MemoryPropertyFlags memoryFlags,
|
||||||
out DeviceMemory memory)
|
out DeviceMemory memory,
|
||||||
|
MemoryPropertyFlags preferredMemoryFlags = 0)
|
||||||
{
|
{
|
||||||
var bufferInfo = new BufferCreateInfo
|
var bufferInfo = new BufferCreateInfo
|
||||||
{
|
{
|
||||||
@@ -10269,7 +10272,10 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
{
|
{
|
||||||
SType = StructureType.MemoryAllocateInfo,
|
SType = StructureType.MemoryAllocateInfo,
|
||||||
AllocationSize = requirements.Size,
|
AllocationSize = requirements.Size,
|
||||||
MemoryTypeIndex = FindMemoryType(requirements.MemoryTypeBits, memoryFlags),
|
MemoryTypeIndex = FindMemoryType(
|
||||||
|
requirements.MemoryTypeBits,
|
||||||
|
memoryFlags,
|
||||||
|
preferredMemoryFlags),
|
||||||
};
|
};
|
||||||
Check(_vk.AllocateMemory(_device, &memoryInfo, null, out memory), "vkAllocateMemory");
|
Check(_vk.AllocateMemory(_device, &memoryInfo, null, out memory), "vkAllocateMemory");
|
||||||
Check(_vk.BindBufferMemory(_device, buffer, memory, 0), "vkBindBufferMemory");
|
Check(_vk.BindBufferMemory(_device, buffer, memory, 0), "vkBindBufferMemory");
|
||||||
@@ -10306,16 +10312,24 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private uint FindMemoryType(uint typeBits, MemoryPropertyFlags requiredFlags)
|
private uint FindMemoryType(
|
||||||
|
uint typeBits,
|
||||||
|
MemoryPropertyFlags requiredFlags,
|
||||||
|
MemoryPropertyFlags preferredFlags = 0)
|
||||||
{
|
{
|
||||||
_vk.GetPhysicalDeviceMemoryProperties(_physicalDevice, out var properties);
|
_vk.GetPhysicalDeviceMemoryProperties(_physicalDevice, out var properties);
|
||||||
var memoryTypes = &properties.MemoryTypes.Element0;
|
var memoryTypes = &properties.MemoryTypes.Element0;
|
||||||
for (uint index = 0; index < properties.MemoryTypeCount; index++)
|
|
||||||
|
for (var pass = preferredFlags != 0 ? 0 : 1; pass < 2; pass++)
|
||||||
{
|
{
|
||||||
if ((typeBits & (1u << (int)index)) != 0 &&
|
var wanted = pass == 0 ? requiredFlags | preferredFlags : requiredFlags;
|
||||||
(memoryTypes[index].PropertyFlags & requiredFlags) == requiredFlags)
|
for (uint index = 0; index < properties.MemoryTypeCount; index++)
|
||||||
{
|
{
|
||||||
return index;
|
if ((typeBits & (1u << (int)index)) != 0 &&
|
||||||
|
(memoryTypes[index].PropertyFlags & wanted) == wanted)
|
||||||
|
{
|
||||||
|
return index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user