mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-17 08:26:13 +08:00
[gpu-hotfix1] Cache improvements have been made (6x performance gain) (#18)
This commit is contained in:
@@ -81,7 +81,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
_gate.EnterWriteLock();
|
_gate.EnterWriteLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_regions.Add(new MemoryRegion
|
InsertRegionSorted(new MemoryRegion
|
||||||
{
|
{
|
||||||
VirtualAddress = actualAddress,
|
VirtualAddress = actualAddress,
|
||||||
Size = alignedSize,
|
Size = alignedSize,
|
||||||
@@ -210,7 +210,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
_gate.EnterWriteLock();
|
_gate.EnterWriteLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_regions.Add(new MemoryRegion
|
InsertRegionSorted(new MemoryRegion
|
||||||
{
|
{
|
||||||
VirtualAddress = actualAddress,
|
VirtualAddress = actualAddress,
|
||||||
Size = alignedSize,
|
Size = alignedSize,
|
||||||
@@ -473,30 +473,34 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
_gate.EnterReadLock();
|
_gate.EnterReadLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var region in _regions)
|
var region = FindRegion(virtualAddress, (ulong)destination.Length);
|
||||||
|
if (region is not null &&
|
||||||
|
TryResolveRegionOffset(
|
||||||
|
virtualAddress,
|
||||||
|
(ulong)destination.Length,
|
||||||
|
region,
|
||||||
|
out var offset))
|
||||||
{
|
{
|
||||||
if (TryResolveRegionOffset(virtualAddress, (ulong)destination.Length, region, out var offset))
|
var srcPtr = (void*)(region.VirtualAddress + offset);
|
||||||
|
if (destination.IsEmpty)
|
||||||
{
|
{
|
||||||
var srcPtr = (void*)(region.VirtualAddress + offset);
|
return true;
|
||||||
if (destination.IsEmpty)
|
}
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (region.IsReservedOnly)
|
if (region.IsReservedOnly)
|
||||||
|
{
|
||||||
|
if (!EnsureRangeCommitted((ulong)srcPtr, (ulong)destination.Length, region))
|
||||||
{
|
{
|
||||||
if (!EnsureRangeCommitted((ulong)srcPtr, (ulong)destination.Length, region))
|
return false;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CanReadWithoutProtectionChange((ulong)srcPtr, (ulong)destination.Length, region))
|
|
||||||
{
|
|
||||||
requiresExclusiveAccess = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CanReadWithoutProtectionChange((ulong)srcPtr, (ulong)destination.Length, region))
|
||||||
|
{
|
||||||
|
requiresExclusiveAccess = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
fixed (byte* destPtr = destination)
|
fixed (byte* destPtr = destination)
|
||||||
{
|
{
|
||||||
Buffer.MemoryCopy(srcPtr, destPtr, (nuint)destination.Length, (nuint)destination.Length);
|
Buffer.MemoryCopy(srcPtr, destPtr, (nuint)destination.Length, (nuint)destination.Length);
|
||||||
@@ -533,30 +537,34 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
_gate.EnterReadLock();
|
_gate.EnterReadLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var region in _regions)
|
var region = FindRegion(virtualAddress, (ulong)source.Length);
|
||||||
|
if (region is not null &&
|
||||||
|
TryResolveRegionOffset(
|
||||||
|
virtualAddress,
|
||||||
|
(ulong)source.Length,
|
||||||
|
region,
|
||||||
|
out var offset))
|
||||||
{
|
{
|
||||||
if (TryResolveRegionOffset(virtualAddress, (ulong)source.Length, region, out var offset))
|
var destPtr = (void*)(region.VirtualAddress + offset);
|
||||||
|
if (source.IsEmpty)
|
||||||
{
|
{
|
||||||
var destPtr = (void*)(region.VirtualAddress + offset);
|
return true;
|
||||||
if (source.IsEmpty)
|
}
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (region.IsReservedOnly)
|
if (region.IsReservedOnly)
|
||||||
|
{
|
||||||
|
if (!EnsureRangeCommitted((ulong)destPtr, (ulong)source.Length, region))
|
||||||
{
|
{
|
||||||
if (!EnsureRangeCommitted((ulong)destPtr, (ulong)source.Length, region))
|
return false;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CanWriteWithoutProtectionChange((ulong)destPtr, (ulong)source.Length, region))
|
|
||||||
{
|
|
||||||
requiresExclusiveAccess = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CanWriteWithoutProtectionChange((ulong)destPtr, (ulong)source.Length, region))
|
||||||
|
{
|
||||||
|
requiresExclusiveAccess = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
fixed (byte* srcPtr = source)
|
fixed (byte* srcPtr = source)
|
||||||
{
|
{
|
||||||
Buffer.MemoryCopy(srcPtr, destPtr, (nuint)source.Length, (nuint)source.Length);
|
Buffer.MemoryCopy(srcPtr, destPtr, (nuint)source.Length, (nuint)source.Length);
|
||||||
@@ -589,13 +597,14 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
|
|
||||||
private bool TryReadExclusive(ulong virtualAddress, Span<byte> destination)
|
private bool TryReadExclusive(ulong virtualAddress, Span<byte> destination)
|
||||||
{
|
{
|
||||||
foreach (var region in _regions)
|
var region = FindRegion(virtualAddress, (ulong)destination.Length);
|
||||||
|
if (region is not null &&
|
||||||
|
TryResolveRegionOffset(
|
||||||
|
virtualAddress,
|
||||||
|
(ulong)destination.Length,
|
||||||
|
region,
|
||||||
|
out var offset))
|
||||||
{
|
{
|
||||||
if (!TryResolveRegionOffset(virtualAddress, (ulong)destination.Length, region, out var offset))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var srcPtr = (void*)(region.VirtualAddress + offset);
|
var srcPtr = (void*)(region.VirtualAddress + offset);
|
||||||
if (!EnsureRangeCommitted((ulong)srcPtr, (ulong)destination.Length, region))
|
if (!EnsureRangeCommitted((ulong)srcPtr, (ulong)destination.Length, region))
|
||||||
{
|
{
|
||||||
@@ -637,13 +646,14 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
|
|
||||||
private bool TryWriteExclusive(ulong virtualAddress, ReadOnlySpan<byte> source)
|
private bool TryWriteExclusive(ulong virtualAddress, ReadOnlySpan<byte> source)
|
||||||
{
|
{
|
||||||
foreach (var region in _regions)
|
var region = FindRegion(virtualAddress, (ulong)source.Length);
|
||||||
|
if (region is not null &&
|
||||||
|
TryResolveRegionOffset(
|
||||||
|
virtualAddress,
|
||||||
|
(ulong)source.Length,
|
||||||
|
region,
|
||||||
|
out var offset))
|
||||||
{
|
{
|
||||||
if (!TryResolveRegionOffset(virtualAddress, (ulong)source.Length, region, out var offset))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var destPtr = (void*)(region.VirtualAddress + offset);
|
var destPtr = (void*)(region.VirtualAddress + offset);
|
||||||
if (!EnsureRangeCommitted((ulong)destPtr, (ulong)source.Length, region))
|
if (!EnsureRangeCommitted((ulong)destPtr, (ulong)source.Length, region))
|
||||||
{
|
{
|
||||||
@@ -699,15 +709,9 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
_gate.EnterReadLock();
|
_gate.EnterReadLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var region in _regions)
|
return FindRegion(virtualAddress, 1) is not null
|
||||||
{
|
? (void*)virtualAddress
|
||||||
if (virtualAddress >= region.VirtualAddress &&
|
: null;
|
||||||
virtualAddress < region.VirtualAddress + region.Size)
|
|
||||||
{
|
|
||||||
return (void*)virtualAddress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -720,14 +724,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
_gate.EnterReadLock();
|
_gate.EnterReadLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var region in _regions)
|
return FindRegion(virtualAddress, size) is not null;
|
||||||
{
|
|
||||||
if (TryResolveRegionOffset(virtualAddress, size, region, out _))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -737,14 +734,48 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
|
|
||||||
private MemoryRegion? FindRegion(ulong address, ulong size)
|
private MemoryRegion? FindRegion(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
foreach (var region in _regions)
|
var low = 0;
|
||||||
|
var high = _regions.Count - 1;
|
||||||
|
MemoryRegion? candidate = null;
|
||||||
|
while (low <= high)
|
||||||
{
|
{
|
||||||
if (TryResolveRegionOffset(address, size, region, out _))
|
var middle = low + ((high - low) >> 1);
|
||||||
|
var region = _regions[middle];
|
||||||
|
if (region.VirtualAddress <= address)
|
||||||
{
|
{
|
||||||
return region;
|
candidate = region;
|
||||||
|
low = middle + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
high = middle - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
return candidate is not null &&
|
||||||
|
TryResolveRegionOffset(address, size, candidate, out _)
|
||||||
|
? candidate
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InsertRegionSorted(MemoryRegion region)
|
||||||
|
{
|
||||||
|
var low = 0;
|
||||||
|
var high = _regions.Count;
|
||||||
|
while (low < high)
|
||||||
|
{
|
||||||
|
var middle = low + ((high - low) >> 1);
|
||||||
|
if (_regions[middle].VirtualAddress < region.VirtualAddress)
|
||||||
|
{
|
||||||
|
low = middle + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
high = middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_regions.Insert(low, region);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetOverlappingRegionEnd(ulong address, ulong size, out ulong overlapEnd)
|
private bool TryGetOverlappingRegionEnd(ulong address, ulong size, out ulong overlapEnd)
|
||||||
|
|||||||
@@ -155,6 +155,16 @@ public static class AgcExports
|
|||||||
(ulong Cs, ulong State, uint LocalX, uint LocalY, uint LocalZ),
|
(ulong Cs, ulong State, uint LocalX, uint LocalY, uint LocalZ),
|
||||||
byte[]> _computeSpirvCache = new();
|
byte[]> _computeSpirvCache = new();
|
||||||
private static readonly Dictionary<ulong, ulong> _shaderHeadersByCode = new();
|
private static readonly Dictionary<ulong, ulong> _shaderHeadersByCode = new();
|
||||||
|
private static readonly bool _traceAgc = string.Equals(
|
||||||
|
Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"),
|
||||||
|
"1",
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
private static readonly bool _traceAgcShader =
|
||||||
|
_traceAgc ||
|
||||||
|
string.Equals(
|
||||||
|
Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC_SHADER"),
|
||||||
|
"1",
|
||||||
|
StringComparison.Ordinal);
|
||||||
private static long _dcbWriteDataTraceCount;
|
private static long _dcbWriteDataTraceCount;
|
||||||
private static long _dcbWaitRegMemTraceCount;
|
private static long _dcbWaitRegMemTraceCount;
|
||||||
private static long _createShaderTraceCount;
|
private static long _createShaderTraceCount;
|
||||||
@@ -3629,6 +3639,31 @@ public static class AgcExports
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isStorage &&
|
||||||
|
descriptor.Address != 0 &&
|
||||||
|
VulkanVideoPresenter.IsGuestImageAvailable(
|
||||||
|
descriptor.Address,
|
||||||
|
descriptor.Format,
|
||||||
|
descriptor.NumberType))
|
||||||
|
{
|
||||||
|
texture = new VulkanGuestDrawTexture(
|
||||||
|
descriptor.Address,
|
||||||
|
descriptor.Width,
|
||||||
|
descriptor.Height,
|
||||||
|
descriptor.Format,
|
||||||
|
descriptor.NumberType,
|
||||||
|
[],
|
||||||
|
IsFallback: false,
|
||||||
|
IsStorage: false,
|
||||||
|
MipLevels: descriptor.MipLevels,
|
||||||
|
MipLevel: mipLevel,
|
||||||
|
Pitch: sourceWidth,
|
||||||
|
TileMode: descriptor.TileMode,
|
||||||
|
DstSelect: descriptor.DstSelect,
|
||||||
|
Sampler: ToVulkanSampler(samplerDescriptor));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (isStorage)
|
if (isStorage)
|
||||||
{
|
{
|
||||||
var initialPixels = Array.Empty<byte>();
|
var initialPixels = Array.Empty<byte>();
|
||||||
@@ -5405,7 +5440,7 @@ public static class AgcExports
|
|||||||
|
|
||||||
private static void TraceAgc(string message)
|
private static void TraceAgc(string message)
|
||||||
{
|
{
|
||||||
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"), "1", StringComparison.Ordinal))
|
if (!_traceAgc)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -5415,8 +5450,7 @@ public static class AgcExports
|
|||||||
|
|
||||||
private static void TraceAgcShader(string message)
|
private static void TraceAgcShader(string message)
|
||||||
{
|
{
|
||||||
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"), "1", StringComparison.Ordinal) &&
|
if (!_traceAgcShader)
|
||||||
!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC_SHADER"), "1", StringComparison.Ordinal))
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
using SharpEmu.HLE;
|
using SharpEmu.HLE;
|
||||||
using SharpEmu.Libs.VideoOut;
|
using SharpEmu.Libs.VideoOut;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace SharpEmu.Libs.Agc;
|
namespace SharpEmu.Libs.Agc;
|
||||||
@@ -13,6 +14,14 @@ internal static class Gen5ShaderTranslator
|
|||||||
private const int MaxInstructions = 4096;
|
private const int MaxInstructions = 4096;
|
||||||
private const int MinimumUserDataDwords = 16;
|
private const int MinimumUserDataDwords = 16;
|
||||||
private const int MaximumUserDataDwords = 256;
|
private const int MaximumUserDataDwords = 256;
|
||||||
|
private static readonly ConditionalWeakTable<object, ShaderDecodeCache> _decodeCaches = new();
|
||||||
|
|
||||||
|
private sealed class ShaderDecodeCache
|
||||||
|
{
|
||||||
|
public object Gate { get; } = new();
|
||||||
|
public Dictionary<ulong, Gen5ShaderProgram> Programs { get; } = new();
|
||||||
|
public Dictionary<ulong, Gen5ShaderMetadata?> Metadata { get; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly uint[] FullscreenBarycentricEs =
|
private static readonly uint[] FullscreenBarycentricEs =
|
||||||
[
|
[
|
||||||
@@ -117,16 +126,51 @@ internal static class Gen5ShaderTranslator
|
|||||||
uint userDataScalarRegisterBase = 0)
|
uint userDataScalarRegisterBase = 0)
|
||||||
{
|
{
|
||||||
state = default!;
|
state = default!;
|
||||||
if (!TryDecodeProgram(ctx, shaderAddress, out var program, out error))
|
error = string.Empty;
|
||||||
|
var cache = _decodeCaches.GetValue(ctx.Memory, static _ => new ShaderDecodeCache());
|
||||||
|
Gen5ShaderProgram? program;
|
||||||
|
lock (cache.Gate)
|
||||||
{
|
{
|
||||||
return false;
|
cache.Programs.TryGetValue(shaderAddress, out program);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (program is null)
|
||||||
|
{
|
||||||
|
if (!TryDecodeProgram(ctx, shaderAddress, out program, out error))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (cache.Gate)
|
||||||
|
{
|
||||||
|
cache.Programs.TryAdd(shaderAddress, program);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Gen5ShaderMetadata? metadata = null;
|
Gen5ShaderMetadata? metadata = null;
|
||||||
if (shaderHeaderAddress != 0 &&
|
if (shaderHeaderAddress != 0)
|
||||||
Gen5ShaderMetadataReader.TryRead(ctx, shaderHeaderAddress, out var decodedMetadata))
|
|
||||||
{
|
{
|
||||||
metadata = decodedMetadata;
|
var metadataCached = false;
|
||||||
|
lock (cache.Gate)
|
||||||
|
{
|
||||||
|
metadataCached = cache.Metadata.TryGetValue(shaderHeaderAddress, out metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!metadataCached)
|
||||||
|
{
|
||||||
|
if (Gen5ShaderMetadataReader.TryRead(
|
||||||
|
ctx,
|
||||||
|
shaderHeaderAddress,
|
||||||
|
out var decodedMetadata))
|
||||||
|
{
|
||||||
|
metadata = decodedMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (cache.Gate)
|
||||||
|
{
|
||||||
|
cache.Metadata.TryAdd(shaderHeaderAddress, metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var userData = new uint[GetUserDataDwordCount(metadata)];
|
var userData = new uint[GetUserDataDwordCount(metadata)];
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
using SharpEmu.HLE;
|
using SharpEmu.HLE;
|
||||||
using SharpEmu.Libs.Kernel;
|
using SharpEmu.Libs.Kernel;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
@@ -48,6 +49,13 @@ public static class VideoOutExports
|
|||||||
private static int _frameDumpCount;
|
private static int _frameDumpCount;
|
||||||
private static long _nextFrameDumpIndex;
|
private static long _nextFrameDumpIndex;
|
||||||
private static string _windowTitle = "SharpEmu VideoOut";
|
private static string _windowTitle = "SharpEmu VideoOut";
|
||||||
|
private static readonly bool _logFrameRate = string.Equals(
|
||||||
|
Environment.GetEnvironmentVariable("SHARPEMU_LOG_VIDEOOUT_FPS"),
|
||||||
|
"1",
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
private static long _frameRateWindowStart = Stopwatch.GetTimestamp();
|
||||||
|
private static long _submittedFrameCount;
|
||||||
|
private static long _presentedFrameCount;
|
||||||
|
|
||||||
public static void ConfigureApplicationInfo(string? title, string? titleId, string? version)
|
public static void ConfigureApplicationInfo(string? title, string? titleId, string? version)
|
||||||
{
|
{
|
||||||
@@ -812,9 +820,46 @@ public static class VideoOutExports
|
|||||||
}
|
}
|
||||||
|
|
||||||
TraceVideoOut($"videoout.submit_flip handle={handle} index={bufferIndex} mode={flipMode} arg={flipArg} events={flipEvents.Count}");
|
TraceVideoOut($"videoout.submit_flip handle={handle} index={bufferIndex} mode={flipMode} arg={flipArg} events={flipEvents.Count}");
|
||||||
|
ReportFrameRate(presented: false);
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void ReportPresentedFrame() =>
|
||||||
|
ReportFrameRate(presented: true);
|
||||||
|
|
||||||
|
private static void ReportFrameRate(bool presented)
|
||||||
|
{
|
||||||
|
if (!_logFrameRate)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (presented)
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _presentedFrameCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _submittedFrameCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
var started = Volatile.Read(ref _frameRateWindowStart);
|
||||||
|
var now = Stopwatch.GetTimestamp();
|
||||||
|
var elapsedTicks = now - started;
|
||||||
|
if (elapsedTicks < Stopwatch.Frequency ||
|
||||||
|
Interlocked.CompareExchange(ref _frameRateWindowStart, now, started) != started)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var elapsedSeconds = (double)elapsedTicks / Stopwatch.Frequency;
|
||||||
|
var submitted = Interlocked.Exchange(ref _submittedFrameCount, 0);
|
||||||
|
var presentedCount = Interlocked.Exchange(ref _presentedFrameCount, 0);
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[LOADER][PERF] videoout submitted_fps={submitted / elapsedSeconds:F1} " +
|
||||||
|
$"presented_fps={presentedCount / elapsedSeconds:F1}");
|
||||||
|
}
|
||||||
|
|
||||||
private static int RegisterBufferRange(VideoOutPortState port, int startIndex, ReadOnlySpan<ulong> addresses, BufferAttribute attribute, int requestedGroupIndex = -1)
|
private static int RegisterBufferRange(VideoOutPortState port, int startIndex, ReadOnlySpan<ulong> addresses, BufferAttribute attribute, int requestedGroupIndex = -1)
|
||||||
{
|
{
|
||||||
lock (_stateGate)
|
lock (_stateGate)
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ using SharpEmu.Libs.Agc;
|
|||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using Silk.NET.Vulkan.Extensions.KHR;
|
using Silk.NET.Vulkan.Extensions.KHR;
|
||||||
using Silk.NET.Windowing;
|
using Silk.NET.Windowing;
|
||||||
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
||||||
using VkSemaphore = Silk.NET.Vulkan.Semaphore;
|
using VkSemaphore = Silk.NET.Vulkan.Semaphore;
|
||||||
@@ -597,6 +599,24 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static bool IsGuestImageAvailable(
|
||||||
|
ulong address,
|
||||||
|
uint format,
|
||||||
|
uint numberType)
|
||||||
|
{
|
||||||
|
var guestFormat = GetGuestTextureFormat(format, numberType);
|
||||||
|
if (address == 0 || guestFormat == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_gate)
|
||||||
|
{
|
||||||
|
return _availableGuestImages.TryGetValue(address, out var availableFormat) &&
|
||||||
|
availableFormat == guestFormat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static bool TrySubmitGuestImageBlit(
|
public static bool TrySubmitGuestImageBlit(
|
||||||
ulong sourceAddress,
|
ulong sourceAddress,
|
||||||
uint sourceWidth,
|
uint sourceWidth,
|
||||||
@@ -867,14 +887,50 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
private int _tracedVertexBufferCount;
|
private int _tracedVertexBufferCount;
|
||||||
private readonly Dictionary<byte[], Pipeline> _computePipelines =
|
private readonly Dictionary<byte[], Pipeline> _computePipelines =
|
||||||
new(ReferenceEqualityComparer.Instance);
|
new(ReferenceEqualityComparer.Instance);
|
||||||
|
private readonly Dictionary<GraphicsPipelineKey, Pipeline> _graphicsPipelines = new();
|
||||||
|
private readonly Dictionary<VulkanGuestSampler, Sampler> _samplers = new();
|
||||||
|
private readonly Dictionary<byte[], string> _shaderDigests =
|
||||||
|
new(ReferenceEqualityComparer.Instance);
|
||||||
|
private readonly Dictionary<DescriptorLayoutKey, DescriptorLayoutBundle>
|
||||||
|
_descriptorLayouts = new();
|
||||||
|
private readonly Dictionary<HostBufferPoolKey, Stack<HostBufferAllocation>>
|
||||||
|
_hostBufferPool = new();
|
||||||
|
private readonly Dictionary<ulong, HostBufferAllocation> _hostBufferAllocations = new();
|
||||||
private readonly Queue<PendingGuestSubmission> _pendingGuestSubmissions = new();
|
private readonly Queue<PendingGuestSubmission> _pendingGuestSubmissions = new();
|
||||||
|
|
||||||
|
private readonly record struct GraphicsPipelineKey(
|
||||||
|
string VertexShader,
|
||||||
|
string FragmentShader,
|
||||||
|
ulong RenderPass,
|
||||||
|
PrimitiveTopology Topology,
|
||||||
|
VulkanGuestBlendState Blend,
|
||||||
|
string ResourceLayout,
|
||||||
|
string VertexLayout);
|
||||||
|
|
||||||
|
private readonly record struct HostBufferPoolKey(
|
||||||
|
BufferUsageFlags Usage,
|
||||||
|
ulong Capacity);
|
||||||
|
|
||||||
|
private readonly record struct DescriptorLayoutKey(
|
||||||
|
ShaderStageFlags Stages,
|
||||||
|
string Resources);
|
||||||
|
|
||||||
|
private sealed record DescriptorLayoutBundle(
|
||||||
|
DescriptorSetLayout DescriptorSetLayout,
|
||||||
|
PipelineLayout PipelineLayout);
|
||||||
|
|
||||||
|
private sealed record HostBufferAllocation(
|
||||||
|
VkBuffer Buffer,
|
||||||
|
DeviceMemory Memory,
|
||||||
|
HostBufferPoolKey Key);
|
||||||
|
|
||||||
private sealed class TranslatedDrawResources
|
private sealed class TranslatedDrawResources
|
||||||
{
|
{
|
||||||
public string DebugName = "SharpEmu translated";
|
public string DebugName = "SharpEmu translated";
|
||||||
public PipelineLayout PipelineLayout;
|
public PipelineLayout PipelineLayout;
|
||||||
public Pipeline Pipeline;
|
public Pipeline Pipeline;
|
||||||
public bool PipelineCached;
|
public bool PipelineCached;
|
||||||
|
public bool DescriptorLayoutCached;
|
||||||
public DescriptorSetLayout DescriptorSetLayout;
|
public DescriptorSetLayout DescriptorSetLayout;
|
||||||
public DescriptorPool DescriptorPool;
|
public DescriptorPool DescriptorPool;
|
||||||
public DescriptorSet DescriptorSet;
|
public DescriptorSet DescriptorSet;
|
||||||
@@ -1978,81 +2034,16 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
var storageImageCount = textureCount - sampledImageCount;
|
var storageImageCount = textureCount - sampledImageCount;
|
||||||
var globalBufferCount = resources.GlobalMemoryBuffers.Length;
|
var globalBufferCount = resources.GlobalMemoryBuffers.Length;
|
||||||
var bindingCount = textureCount + (globalBufferCount == 0 ? 0 : 1);
|
var bindingCount = textureCount + (globalBufferCount == 0 ? 0 : 1);
|
||||||
|
var layout = GetOrCreateDescriptorLayout(resources, stageFlags, bindingCount);
|
||||||
|
resources.DescriptorSetLayout = layout.DescriptorSetLayout;
|
||||||
|
resources.PipelineLayout = layout.PipelineLayout;
|
||||||
|
resources.DescriptorLayoutCached = true;
|
||||||
if (bindingCount == 0)
|
if (bindingCount == 0)
|
||||||
{
|
{
|
||||||
var layoutInfo = new PipelineLayoutCreateInfo
|
|
||||||
{
|
|
||||||
SType = StructureType.PipelineLayoutCreateInfo,
|
|
||||||
};
|
|
||||||
PipelineLayout pipelineLayout;
|
|
||||||
Check(
|
|
||||||
_vk.CreatePipelineLayout(_device, &layoutInfo, null, out pipelineLayout),
|
|
||||||
"vkCreatePipelineLayout");
|
|
||||||
resources.PipelineLayout = pipelineLayout;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var bindings = new DescriptorSetLayoutBinding[bindingCount];
|
var setLayout = layout.DescriptorSetLayout;
|
||||||
var bindingOffset = 0;
|
|
||||||
if (globalBufferCount != 0)
|
|
||||||
{
|
|
||||||
bindings[bindingOffset++] = new DescriptorSetLayoutBinding
|
|
||||||
{
|
|
||||||
Binding = 0,
|
|
||||||
DescriptorType = DescriptorType.StorageBuffer,
|
|
||||||
DescriptorCount = (uint)globalBufferCount,
|
|
||||||
StageFlags = stageFlags,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var index = 0; index < textureCount; index++)
|
|
||||||
{
|
|
||||||
bindings[bindingOffset + index] = new DescriptorSetLayoutBinding
|
|
||||||
{
|
|
||||||
Binding = (uint)(index + 1),
|
|
||||||
DescriptorType = resources.Textures[index].IsStorage
|
|
||||||
? DescriptorType.StorageImage
|
|
||||||
: DescriptorType.CombinedImageSampler,
|
|
||||||
DescriptorCount = 1,
|
|
||||||
StageFlags = stageFlags,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed (DescriptorSetLayoutBinding* bindingPointer = bindings)
|
|
||||||
{
|
|
||||||
var layoutInfo = new DescriptorSetLayoutCreateInfo
|
|
||||||
{
|
|
||||||
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
|
||||||
BindingCount = (uint)bindings.Length,
|
|
||||||
PBindings = bindingPointer,
|
|
||||||
};
|
|
||||||
DescriptorSetLayout descriptorSetLayout;
|
|
||||||
Check(
|
|
||||||
_vk.CreateDescriptorSetLayout(
|
|
||||||
_device,
|
|
||||||
&layoutInfo,
|
|
||||||
null,
|
|
||||||
out descriptorSetLayout),
|
|
||||||
"vkCreateDescriptorSetLayout");
|
|
||||||
resources.DescriptorSetLayout = descriptorSetLayout;
|
|
||||||
}
|
|
||||||
|
|
||||||
var setLayout = resources.DescriptorSetLayout;
|
|
||||||
var pipelineLayoutInfo = new PipelineLayoutCreateInfo
|
|
||||||
{
|
|
||||||
SType = StructureType.PipelineLayoutCreateInfo,
|
|
||||||
SetLayoutCount = 1,
|
|
||||||
PSetLayouts = &setLayout,
|
|
||||||
};
|
|
||||||
PipelineLayout translatedPipelineLayout;
|
|
||||||
Check(
|
|
||||||
_vk.CreatePipelineLayout(
|
|
||||||
_device,
|
|
||||||
&pipelineLayoutInfo,
|
|
||||||
null,
|
|
||||||
out translatedPipelineLayout),
|
|
||||||
"vkCreatePipelineLayout");
|
|
||||||
resources.PipelineLayout = translatedPipelineLayout;
|
|
||||||
|
|
||||||
var poolSizes = new DescriptorPoolSize[
|
var poolSizes = new DescriptorPoolSize[
|
||||||
(sampledImageCount == 0 ? 0 : 1) +
|
(sampledImageCount == 0 ? 0 : 1) +
|
||||||
@@ -2202,6 +2193,21 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
RenderPass renderPass,
|
RenderPass renderPass,
|
||||||
Extent2D extent)
|
Extent2D extent)
|
||||||
{
|
{
|
||||||
|
var pipelineKey = new GraphicsPipelineKey(
|
||||||
|
GetShaderDigest(vertexSpirv),
|
||||||
|
GetShaderDigest(fragmentSpirv),
|
||||||
|
renderPass.Handle,
|
||||||
|
resources.Topology,
|
||||||
|
resources.Blend,
|
||||||
|
GetResourceLayoutKey(resources),
|
||||||
|
GetVertexLayoutKey(resources));
|
||||||
|
if (_graphicsPipelines.TryGetValue(pipelineKey, out var cachedPipeline))
|
||||||
|
{
|
||||||
|
resources.Pipeline = cachedPipeline;
|
||||||
|
resources.PipelineCached = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var vertexModule = CreateShaderModule(vertexSpirv);
|
var vertexModule = CreateShaderModule(vertexSpirv);
|
||||||
var fragmentModule = CreateShaderModule(fragmentSpirv);
|
var fragmentModule = CreateShaderModule(fragmentSpirv);
|
||||||
var entryPoint = (byte*)SilkMarshal.StringToPtr("main");
|
var entryPoint = (byte*)SilkMarshal.StringToPtr("main");
|
||||||
@@ -2353,6 +2359,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
out pipeline),
|
out pipeline),
|
||||||
"vkCreateGraphicsPipelines(translated)");
|
"vkCreateGraphicsPipelines(translated)");
|
||||||
resources.Pipeline = pipeline;
|
resources.Pipeline = pipeline;
|
||||||
|
resources.PipelineCached = true;
|
||||||
|
_graphicsPipelines.Add(pipelineKey, pipeline);
|
||||||
SetDebugName(
|
SetDebugName(
|
||||||
ObjectType.Pipeline,
|
ObjectType.Pipeline,
|
||||||
pipeline.Handle,
|
pipeline.Handle,
|
||||||
@@ -2367,6 +2375,129 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DescriptorLayoutBundle GetOrCreateDescriptorLayout(
|
||||||
|
TranslatedDrawResources resources,
|
||||||
|
ShaderStageFlags stageFlags,
|
||||||
|
int bindingCount)
|
||||||
|
{
|
||||||
|
var key = new DescriptorLayoutKey(stageFlags, GetResourceLayoutKey(resources));
|
||||||
|
if (_descriptorLayouts.TryGetValue(key, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
DescriptorSetLayout descriptorSetLayout = default;
|
||||||
|
if (bindingCount != 0)
|
||||||
|
{
|
||||||
|
var bindings = new DescriptorSetLayoutBinding[bindingCount];
|
||||||
|
var bindingOffset = 0;
|
||||||
|
if (resources.GlobalMemoryBuffers.Length != 0)
|
||||||
|
{
|
||||||
|
bindings[bindingOffset++] = new DescriptorSetLayoutBinding
|
||||||
|
{
|
||||||
|
Binding = 0,
|
||||||
|
DescriptorType = DescriptorType.StorageBuffer,
|
||||||
|
DescriptorCount = (uint)resources.GlobalMemoryBuffers.Length,
|
||||||
|
StageFlags = stageFlags,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var index = 0; index < resources.Textures.Length; index++)
|
||||||
|
{
|
||||||
|
bindings[bindingOffset + index] = new DescriptorSetLayoutBinding
|
||||||
|
{
|
||||||
|
Binding = (uint)(index + 1),
|
||||||
|
DescriptorType = resources.Textures[index].IsStorage
|
||||||
|
? DescriptorType.StorageImage
|
||||||
|
: DescriptorType.CombinedImageSampler,
|
||||||
|
DescriptorCount = 1,
|
||||||
|
StageFlags = stageFlags,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed (DescriptorSetLayoutBinding* bindingPointer = bindings)
|
||||||
|
{
|
||||||
|
var descriptorInfo = new DescriptorSetLayoutCreateInfo
|
||||||
|
{
|
||||||
|
SType = StructureType.DescriptorSetLayoutCreateInfo,
|
||||||
|
BindingCount = (uint)bindings.Length,
|
||||||
|
PBindings = bindingPointer,
|
||||||
|
};
|
||||||
|
Check(
|
||||||
|
_vk.CreateDescriptorSetLayout(
|
||||||
|
_device,
|
||||||
|
&descriptorInfo,
|
||||||
|
null,
|
||||||
|
out descriptorSetLayout),
|
||||||
|
"vkCreateDescriptorSetLayout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pipelineInfo = new PipelineLayoutCreateInfo
|
||||||
|
{
|
||||||
|
SType = StructureType.PipelineLayoutCreateInfo,
|
||||||
|
};
|
||||||
|
if (descriptorSetLayout.Handle != 0)
|
||||||
|
{
|
||||||
|
pipelineInfo.SetLayoutCount = 1;
|
||||||
|
pipelineInfo.PSetLayouts = &descriptorSetLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
PipelineLayout pipelineLayout;
|
||||||
|
Check(
|
||||||
|
_vk.CreatePipelineLayout(
|
||||||
|
_device,
|
||||||
|
&pipelineInfo,
|
||||||
|
null,
|
||||||
|
out pipelineLayout),
|
||||||
|
"vkCreatePipelineLayout");
|
||||||
|
var created = new DescriptorLayoutBundle(descriptorSetLayout, pipelineLayout);
|
||||||
|
_descriptorLayouts.Add(key, created);
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetShaderDigest(byte[] spirv)
|
||||||
|
{
|
||||||
|
if (_shaderDigests.TryGetValue(spirv, out var digest))
|
||||||
|
{
|
||||||
|
return digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
digest = Convert.ToHexString(SHA256.HashData(spirv));
|
||||||
|
_shaderDigests.Add(spirv, digest);
|
||||||
|
return digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetResourceLayoutKey(TranslatedDrawResources resources)
|
||||||
|
{
|
||||||
|
var key = new StringBuilder();
|
||||||
|
key.Append(resources.GlobalMemoryBuffers.Length).Append(':');
|
||||||
|
foreach (var texture in resources.Textures)
|
||||||
|
{
|
||||||
|
key.Append(texture.IsStorage ? 'S' : 'T');
|
||||||
|
}
|
||||||
|
|
||||||
|
return key.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetVertexLayoutKey(TranslatedDrawResources resources)
|
||||||
|
{
|
||||||
|
var key = new StringBuilder();
|
||||||
|
foreach (var buffer in resources.VertexBuffers)
|
||||||
|
{
|
||||||
|
key.Append(buffer.Location).Append(',')
|
||||||
|
.Append(buffer.ComponentCount).Append(',')
|
||||||
|
.Append(buffer.DataFormat).Append(',')
|
||||||
|
.Append(buffer.NumberFormat).Append(',')
|
||||||
|
.Append(buffer.Stride == 0
|
||||||
|
? Math.Max(buffer.ComponentCount, 1) * sizeof(float)
|
||||||
|
: buffer.Stride)
|
||||||
|
.Append(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
return key.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
private void CreateComputePipeline(
|
private void CreateComputePipeline(
|
||||||
TranslatedDrawResources resources,
|
TranslatedDrawResources resources,
|
||||||
@@ -2779,7 +2910,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
SetDebugName(ObjectType.Buffer, stagingBuffer.Handle, $"{debugName} staging");
|
SetDebugName(ObjectType.Buffer, stagingBuffer.Handle, $"{debugName} staging");
|
||||||
SetDebugName(ObjectType.Image, image.Handle, $"{debugName} image");
|
SetDebugName(ObjectType.Image, image.Handle, $"{debugName} image");
|
||||||
SetDebugName(ObjectType.ImageView, view.Handle, $"{debugName} view");
|
SetDebugName(ObjectType.ImageView, view.Handle, $"{debugName} view");
|
||||||
return new TextureResource
|
var resource = new TextureResource
|
||||||
{
|
{
|
||||||
Address = texture.Address,
|
Address = texture.Address,
|
||||||
StagingBuffer = stagingBuffer,
|
StagingBuffer = stagingBuffer,
|
||||||
@@ -2795,6 +2926,38 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
OwnsStorage = true,
|
OwnsStorage = true,
|
||||||
SamplerState = texture.Sampler,
|
SamplerState = texture.Sampler,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (texture.Address != 0 &&
|
||||||
|
!_guestImages.ContainsKey(texture.Address))
|
||||||
|
{
|
||||||
|
var guestImage = new GuestImageResource
|
||||||
|
{
|
||||||
|
Address = texture.Address,
|
||||||
|
Width = width,
|
||||||
|
Height = height,
|
||||||
|
MipLevels = 1,
|
||||||
|
Format = vkFormat,
|
||||||
|
Image = image,
|
||||||
|
Memory = imageMemory,
|
||||||
|
View = view,
|
||||||
|
InitialUploadPending = true,
|
||||||
|
};
|
||||||
|
_guestImages.Add(texture.Address, guestImage);
|
||||||
|
resource.OwnsStorage = false;
|
||||||
|
resource.GuestImage = guestImage;
|
||||||
|
lock (_gate)
|
||||||
|
{
|
||||||
|
var guestFormat = VulkanVideoPresenter.GetGuestTextureFormat(
|
||||||
|
texture.Format,
|
||||||
|
texture.NumberType);
|
||||||
|
if (guestFormat != 0)
|
||||||
|
{
|
||||||
|
_availableGuestImages[texture.Address] = guestFormat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DumpTextureUpload(
|
private void DumpTextureUpload(
|
||||||
@@ -2895,6 +3058,11 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
|
|
||||||
private Sampler CreateSampler(VulkanGuestSampler sampler)
|
private Sampler CreateSampler(VulkanGuestSampler sampler)
|
||||||
{
|
{
|
||||||
|
if (_samplers.TryGetValue(sampler, out var cachedSampler))
|
||||||
|
{
|
||||||
|
return cachedSampler;
|
||||||
|
}
|
||||||
|
|
||||||
var minLod = DecodeSamplerMipFilter(sampler) == 0
|
var minLod = DecodeSamplerMipFilter(sampler) == 0
|
||||||
? 0f
|
? 0f
|
||||||
: DecodeSamplerMinLod(sampler);
|
: DecodeSamplerMinLod(sampler);
|
||||||
@@ -2921,6 +3089,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
Check(
|
Check(
|
||||||
_vk.CreateSampler(_device, &samplerInfo, null, out vkSampler),
|
_vk.CreateSampler(_device, &samplerInfo, null, out vkSampler),
|
||||||
"vkCreateSampler(texture)");
|
"vkCreateSampler(texture)");
|
||||||
|
_samplers.Add(sampler, vkSampler);
|
||||||
return vkSampler;
|
return vkSampler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3035,11 +3204,31 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
out DeviceMemory memory)
|
out DeviceMemory memory)
|
||||||
{
|
{
|
||||||
var size = (ulong)Math.Max(data.Length, sizeof(uint));
|
var size = (ulong)Math.Max(data.Length, sizeof(uint));
|
||||||
var buffer = CreateBuffer(
|
var capacity = BitOperations.RoundUpToPowerOf2(size);
|
||||||
size,
|
var key = new HostBufferPoolKey(usage, capacity);
|
||||||
usage,
|
if (!_hostBufferPool.TryGetValue(key, out var available))
|
||||||
MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit,
|
{
|
||||||
out memory);
|
available = new Stack<HostBufferAllocation>();
|
||||||
|
_hostBufferPool.Add(key, available);
|
||||||
|
}
|
||||||
|
|
||||||
|
HostBufferAllocation allocation;
|
||||||
|
if (available.TryPop(out var pooled))
|
||||||
|
{
|
||||||
|
allocation = pooled;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var buffer = CreateBuffer(
|
||||||
|
capacity,
|
||||||
|
usage,
|
||||||
|
MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit,
|
||||||
|
out var allocatedMemory);
|
||||||
|
allocation = new HostBufferAllocation(buffer, allocatedMemory, key);
|
||||||
|
_hostBufferAllocations.Add(buffer.Handle, allocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
memory = allocation.Memory;
|
||||||
void* mapped;
|
void* mapped;
|
||||||
Check(_vk.MapMemory(_device, memory, 0, size, 0, &mapped), "vkMapMemory(host)");
|
Check(_vk.MapMemory(_device, memory, 0, size, 0, &mapped), "vkMapMemory(host)");
|
||||||
try
|
try
|
||||||
@@ -3058,7 +3247,28 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
_vk.UnmapMemory(_device, memory);
|
_vk.UnmapMemory(_device, memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer;
|
return allocation.Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecycleHostBuffer(VkBuffer buffer, DeviceMemory memory)
|
||||||
|
{
|
||||||
|
if (buffer.Handle == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_hostBufferAllocations.TryGetValue(buffer.Handle, out var allocation) &&
|
||||||
|
allocation.Memory.Handle == memory.Handle)
|
||||||
|
{
|
||||||
|
_hostBufferPool[allocation.Key].Push(allocation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_vk.DestroyBuffer(_device, buffer, null);
|
||||||
|
if (memory.Handle != 0)
|
||||||
|
{
|
||||||
|
_vk.FreeMemory(_device, memory, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PrimitiveTopology GetPrimitiveTopology(uint primitiveType) =>
|
private static PrimitiveTopology GetPrimitiveTopology(uint primitiveType) =>
|
||||||
@@ -3608,6 +3818,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
resources,
|
resources,
|
||||||
GetTraceImages(resources));
|
GetTraceImages(resources));
|
||||||
submitted = true;
|
submitted = true;
|
||||||
|
MarkSampledImagesInitialized(resources);
|
||||||
MarkStorageImagesInitialized(resources, traceContents: false);
|
MarkStorageImagesInitialized(resources, traceContents: false);
|
||||||
TraceVulkanShader(
|
TraceVulkanShader(
|
||||||
$"vk.compute_dispatch groups={work.GroupCountX}x" +
|
$"vk.compute_dispatch groups={work.GroupCountX}x" +
|
||||||
@@ -3796,6 +4007,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
GetTraceImages(resources, target));
|
GetTraceImages(resources, target));
|
||||||
submitted = true;
|
submitted = true;
|
||||||
target.Initialized = true;
|
target.Initialized = true;
|
||||||
|
MarkSampledImagesInitialized(resources);
|
||||||
MarkStorageImagesInitialized(resources, traceContents: false);
|
MarkStorageImagesInitialized(resources, traceContents: false);
|
||||||
|
|
||||||
var guestTextureFormat = GetGuestTextureFormat(target.Format);
|
var guestTextureFormat = GetGuestTextureFormat(target.Format);
|
||||||
@@ -4498,6 +4710,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
CheckSwapchainResult(presentResult, "vkQueuePresentKHR");
|
CheckSwapchainResult(presentResult, "vkQueuePresentKHR");
|
||||||
recreateAfterPresent |= presentResult == Result.SuboptimalKhr;
|
recreateAfterPresent |= presentResult == Result.SuboptimalKhr;
|
||||||
Check(_vk.QueueWaitIdle(_queue), "vkQueueWaitIdle");
|
Check(_vk.QueueWaitIdle(_queue), "vkQueueWaitIdle");
|
||||||
|
VideoOutExports.ReportPresentedFrame();
|
||||||
if (_swapchainReadbackPending)
|
if (_swapchainReadbackPending)
|
||||||
{
|
{
|
||||||
TraceSwapchainReadback();
|
TraceSwapchainReadback();
|
||||||
@@ -4505,6 +4718,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
CollectCompletedGuestSubmissions(waitForOldest: false);
|
CollectCompletedGuestSubmissions(waitForOldest: false);
|
||||||
if (translatedResources is not null)
|
if (translatedResources is not null)
|
||||||
{
|
{
|
||||||
|
MarkSampledImagesInitialized(translatedResources);
|
||||||
MarkStorageImagesInitialized(translatedResources);
|
MarkStorageImagesInitialized(translatedResources);
|
||||||
DestroyTranslatedDrawResources(translatedResources);
|
DestroyTranslatedDrawResources(translatedResources);
|
||||||
}
|
}
|
||||||
@@ -5010,6 +5224,27 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void MarkSampledImagesInitialized(
|
||||||
|
TranslatedDrawResources resources)
|
||||||
|
{
|
||||||
|
lock (_gate)
|
||||||
|
{
|
||||||
|
foreach (var texture in resources.Textures)
|
||||||
|
{
|
||||||
|
if (!texture.NeedsUpload ||
|
||||||
|
texture.IsStorage ||
|
||||||
|
texture.Address == 0 ||
|
||||||
|
texture.GuestImage is not { } guestImage)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
guestImage.Initialized = true;
|
||||||
|
guestImage.InitialUploadPending = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool ShouldTraceGuestImageContents(GuestImageResource image)
|
private bool ShouldTraceGuestImageContents(GuestImageResource image)
|
||||||
{
|
{
|
||||||
if (image.Address == 0)
|
if (image.Address == 0)
|
||||||
@@ -5248,11 +5483,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
_vk.FreeMemory(_device, texture.StagingMemory, null);
|
_vk.FreeMemory(_device, texture.StagingMemory, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texture.Sampler.Handle != 0)
|
|
||||||
{
|
|
||||||
_vk.DestroySampler(_device, texture.Sampler, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texture.NeedsUpload &&
|
if (texture.NeedsUpload &&
|
||||||
texture.GuestImage is { Initialized: false } guestImage)
|
texture.GuestImage is { Initialized: false } guestImage)
|
||||||
{
|
{
|
||||||
@@ -5267,15 +5497,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (globalBuffer.Buffer.Handle != 0)
|
RecycleHostBuffer(globalBuffer.Buffer, globalBuffer.Memory);
|
||||||
{
|
|
||||||
_vk.DestroyBuffer(_device, globalBuffer.Buffer, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (globalBuffer.Memory.Handle != 0)
|
|
||||||
{
|
|
||||||
_vk.FreeMemory(_device, globalBuffer.Memory, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var vertexBuffer in resources.VertexBuffers)
|
foreach (var vertexBuffer in resources.VertexBuffers)
|
||||||
@@ -5285,26 +5507,10 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vertexBuffer.Buffer.Handle != 0)
|
RecycleHostBuffer(vertexBuffer.Buffer, vertexBuffer.Memory);
|
||||||
{
|
|
||||||
_vk.DestroyBuffer(_device, vertexBuffer.Buffer, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vertexBuffer.Memory.Handle != 0)
|
|
||||||
{
|
|
||||||
_vk.FreeMemory(_device, vertexBuffer.Memory, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resources.IndexBuffer.Handle != 0)
|
RecycleHostBuffer(resources.IndexBuffer, resources.IndexMemory);
|
||||||
{
|
|
||||||
_vk.DestroyBuffer(_device, resources.IndexBuffer, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resources.IndexMemory.Handle != 0)
|
|
||||||
{
|
|
||||||
_vk.FreeMemory(_device, resources.IndexMemory, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!resources.PipelineCached && resources.Pipeline.Handle != 0)
|
if (!resources.PipelineCached && resources.Pipeline.Handle != 0)
|
||||||
{
|
{
|
||||||
@@ -5316,12 +5522,14 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
_vk.DestroyDescriptorPool(_device, resources.DescriptorPool, null);
|
_vk.DestroyDescriptorPool(_device, resources.DescriptorPool, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resources.PipelineLayout.Handle != 0)
|
if (!resources.DescriptorLayoutCached &&
|
||||||
|
resources.PipelineLayout.Handle != 0)
|
||||||
{
|
{
|
||||||
_vk.DestroyPipelineLayout(_device, resources.PipelineLayout, null);
|
_vk.DestroyPipelineLayout(_device, resources.PipelineLayout, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resources.DescriptorSetLayout.Handle != 0)
|
if (!resources.DescriptorLayoutCached &&
|
||||||
|
resources.DescriptorSetLayout.Handle != 0)
|
||||||
{
|
{
|
||||||
_vk.DestroyDescriptorSetLayout(_device, resources.DescriptorSetLayout, null);
|
_vk.DestroyDescriptorSetLayout(_device, resources.DescriptorSetLayout, null);
|
||||||
}
|
}
|
||||||
@@ -5756,6 +5964,36 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
_vk.DestroyPipeline(_device, pipeline, null);
|
_vk.DestroyPipeline(_device, pipeline, null);
|
||||||
}
|
}
|
||||||
_computePipelines.Clear();
|
_computePipelines.Clear();
|
||||||
|
foreach (var pipeline in _graphicsPipelines.Values)
|
||||||
|
{
|
||||||
|
_vk.DestroyPipeline(_device, pipeline, null);
|
||||||
|
}
|
||||||
|
_graphicsPipelines.Clear();
|
||||||
|
foreach (var layout in _descriptorLayouts.Values)
|
||||||
|
{
|
||||||
|
_vk.DestroyPipelineLayout(_device, layout.PipelineLayout, null);
|
||||||
|
if (layout.DescriptorSetLayout.Handle != 0)
|
||||||
|
{
|
||||||
|
_vk.DestroyDescriptorSetLayout(
|
||||||
|
_device,
|
||||||
|
layout.DescriptorSetLayout,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_descriptorLayouts.Clear();
|
||||||
|
foreach (var sampler in _samplers.Values)
|
||||||
|
{
|
||||||
|
_vk.DestroySampler(_device, sampler, null);
|
||||||
|
}
|
||||||
|
_samplers.Clear();
|
||||||
|
_shaderDigests.Clear();
|
||||||
|
foreach (var allocation in _hostBufferAllocations.Values)
|
||||||
|
{
|
||||||
|
_vk.DestroyBuffer(_device, allocation.Buffer, null);
|
||||||
|
_vk.FreeMemory(_device, allocation.Memory, null);
|
||||||
|
}
|
||||||
|
_hostBufferAllocations.Clear();
|
||||||
|
_hostBufferPool.Clear();
|
||||||
foreach (var guestImage in _guestImages.Values)
|
foreach (var guestImage in _guestImages.Values)
|
||||||
{
|
{
|
||||||
DestroyGuestImage(guestImage);
|
DestroyGuestImage(guestImage);
|
||||||
|
|||||||
Reference in New Issue
Block a user