mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-25 20:28:48 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7fac1aa9cd | |||
| c30b4763c6 | |||
| aa152de934 | |||
| 39dd12a11b | |||
| 4c6fc3052c |
Binary file not shown.
|
Before Width: | Height: | Size: 142 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 89 KiB |
@@ -126,7 +126,7 @@ jobs:
|
||||
needs:
|
||||
- init
|
||||
- build
|
||||
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/main')
|
||||
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -81,7 +81,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
||||
_gate.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
InsertRegionSorted(new MemoryRegion
|
||||
_regions.Add(new MemoryRegion
|
||||
{
|
||||
VirtualAddress = actualAddress,
|
||||
Size = alignedSize,
|
||||
@@ -210,7 +210,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
||||
_gate.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
InsertRegionSorted(new MemoryRegion
|
||||
_regions.Add(new MemoryRegion
|
||||
{
|
||||
VirtualAddress = actualAddress,
|
||||
Size = alignedSize,
|
||||
@@ -473,34 +473,30 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
||||
_gate.EnterReadLock();
|
||||
try
|
||||
{
|
||||
var region = FindRegion(virtualAddress, (ulong)destination.Length);
|
||||
if (region is not null &&
|
||||
TryResolveRegionOffset(
|
||||
virtualAddress,
|
||||
(ulong)destination.Length,
|
||||
region,
|
||||
out var offset))
|
||||
foreach (var region in _regions)
|
||||
{
|
||||
var srcPtr = (void*)(region.VirtualAddress + offset);
|
||||
if (destination.IsEmpty)
|
||||
if (TryResolveRegionOffset(virtualAddress, (ulong)destination.Length, region, out var offset))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (region.IsReservedOnly)
|
||||
{
|
||||
if (!EnsureRangeCommitted((ulong)srcPtr, (ulong)destination.Length, region))
|
||||
var srcPtr = (void*)(region.VirtualAddress + offset);
|
||||
if (destination.IsEmpty)
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (region.IsReservedOnly)
|
||||
{
|
||||
if (!EnsureRangeCommitted((ulong)srcPtr, (ulong)destination.Length, region))
|
||||
{
|
||||
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)
|
||||
{
|
||||
Buffer.MemoryCopy(srcPtr, destPtr, (nuint)destination.Length, (nuint)destination.Length);
|
||||
@@ -537,34 +533,30 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
||||
_gate.EnterReadLock();
|
||||
try
|
||||
{
|
||||
var region = FindRegion(virtualAddress, (ulong)source.Length);
|
||||
if (region is not null &&
|
||||
TryResolveRegionOffset(
|
||||
virtualAddress,
|
||||
(ulong)source.Length,
|
||||
region,
|
||||
out var offset))
|
||||
foreach (var region in _regions)
|
||||
{
|
||||
var destPtr = (void*)(region.VirtualAddress + offset);
|
||||
if (source.IsEmpty)
|
||||
if (TryResolveRegionOffset(virtualAddress, (ulong)source.Length, region, out var offset))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (region.IsReservedOnly)
|
||||
{
|
||||
if (!EnsureRangeCommitted((ulong)destPtr, (ulong)source.Length, region))
|
||||
var destPtr = (void*)(region.VirtualAddress + offset);
|
||||
if (source.IsEmpty)
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (region.IsReservedOnly)
|
||||
{
|
||||
if (!EnsureRangeCommitted((ulong)destPtr, (ulong)source.Length, region))
|
||||
{
|
||||
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)
|
||||
{
|
||||
Buffer.MemoryCopy(srcPtr, destPtr, (nuint)source.Length, (nuint)source.Length);
|
||||
@@ -597,14 +589,13 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
||||
|
||||
private bool TryReadExclusive(ulong virtualAddress, Span<byte> destination)
|
||||
{
|
||||
var region = FindRegion(virtualAddress, (ulong)destination.Length);
|
||||
if (region is not null &&
|
||||
TryResolveRegionOffset(
|
||||
virtualAddress,
|
||||
(ulong)destination.Length,
|
||||
region,
|
||||
out var offset))
|
||||
foreach (var region in _regions)
|
||||
{
|
||||
if (!TryResolveRegionOffset(virtualAddress, (ulong)destination.Length, region, out var offset))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var srcPtr = (void*)(region.VirtualAddress + offset);
|
||||
if (!EnsureRangeCommitted((ulong)srcPtr, (ulong)destination.Length, region))
|
||||
{
|
||||
@@ -646,14 +637,13 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
||||
|
||||
private bool TryWriteExclusive(ulong virtualAddress, ReadOnlySpan<byte> source)
|
||||
{
|
||||
var region = FindRegion(virtualAddress, (ulong)source.Length);
|
||||
if (region is not null &&
|
||||
TryResolveRegionOffset(
|
||||
virtualAddress,
|
||||
(ulong)source.Length,
|
||||
region,
|
||||
out var offset))
|
||||
foreach (var region in _regions)
|
||||
{
|
||||
if (!TryResolveRegionOffset(virtualAddress, (ulong)source.Length, region, out var offset))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var destPtr = (void*)(region.VirtualAddress + offset);
|
||||
if (!EnsureRangeCommitted((ulong)destPtr, (ulong)source.Length, region))
|
||||
{
|
||||
@@ -709,9 +699,15 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
||||
_gate.EnterReadLock();
|
||||
try
|
||||
{
|
||||
return FindRegion(virtualAddress, 1) is not null
|
||||
? (void*)virtualAddress
|
||||
: null;
|
||||
foreach (var region in _regions)
|
||||
{
|
||||
if (virtualAddress >= region.VirtualAddress &&
|
||||
virtualAddress < region.VirtualAddress + region.Size)
|
||||
{
|
||||
return (void*)virtualAddress;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -724,7 +720,14 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
||||
_gate.EnterReadLock();
|
||||
try
|
||||
{
|
||||
return FindRegion(virtualAddress, size) is not null;
|
||||
foreach (var region in _regions)
|
||||
{
|
||||
if (TryResolveRegionOffset(virtualAddress, size, region, out _))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -734,48 +737,14 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
||||
|
||||
private MemoryRegion? FindRegion(ulong address, ulong size)
|
||||
{
|
||||
var low = 0;
|
||||
var high = _regions.Count - 1;
|
||||
MemoryRegion? candidate = null;
|
||||
while (low <= high)
|
||||
foreach (var region in _regions)
|
||||
{
|
||||
var middle = low + ((high - low) >> 1);
|
||||
var region = _regions[middle];
|
||||
if (region.VirtualAddress <= address)
|
||||
if (TryResolveRegionOffset(address, size, region, out _))
|
||||
{
|
||||
candidate = region;
|
||||
low = middle + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
high = middle - 1;
|
||||
return region;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool TryGetOverlappingRegionEnd(ulong address, ulong size, out ulong overlapEnd)
|
||||
|
||||
@@ -155,16 +155,6 @@ public static class AgcExports
|
||||
(ulong Cs, ulong State, uint LocalX, uint LocalY, uint LocalZ),
|
||||
byte[]> _computeSpirvCache = 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 _dcbWaitRegMemTraceCount;
|
||||
private static long _createShaderTraceCount;
|
||||
@@ -3503,29 +3493,6 @@ public static class AgcExports
|
||||
$"{drawViewport.Width:0.###}x{drawViewport.Height:0.###}:" +
|
||||
$"{drawViewport.MinDepth:0.###}-{drawViewport.MaxDepth:0.###}"
|
||||
: "full";
|
||||
var rasterRegisters = new (string Name, uint Offset)[]
|
||||
{
|
||||
("screen_tl", PaScScreenScissorTl),
|
||||
("screen_br", PaScScreenScissorBr),
|
||||
("window_off", PaScWindowOffset),
|
||||
("window_tl", PaScWindowScissorTl),
|
||||
("window_br", PaScWindowScissorBr),
|
||||
("generic_tl", PaScGenericScissorTl),
|
||||
("generic_br", PaScGenericScissorBr),
|
||||
("vport_tl", PaScVportScissor0Tl),
|
||||
("vport_br", PaScVportScissor0Br),
|
||||
("mode", PaScModeCntl0),
|
||||
("xscale", PaClVportXScale),
|
||||
("xoffset", PaClVportXOffset),
|
||||
("yscale", PaClVportYScale),
|
||||
("yoffset", PaClVportYOffset),
|
||||
};
|
||||
var raster = string.Join(
|
||||
',',
|
||||
rasterRegisters.Select(entry =>
|
||||
state.CxRegisters.TryGetValue(entry.Offset, out var value)
|
||||
? $"{entry.Name}=0x{value:X8}"
|
||||
: $"{entry.Name}=missing"));
|
||||
var blend = draw.RenderState.Blend;
|
||||
TraceAgcShader(
|
||||
$"agc.shader_draw es=0x{draw.ExportShaderAddress:X16} " +
|
||||
@@ -3533,7 +3500,6 @@ public static class AgcExports
|
||||
$"primitive=0x{draw.PrimitiveType:X} " +
|
||||
$"blend={(blend.Enable ? 1 : 0)}:{blend.ColorSrcFactor}/{blend.ColorDstFactor}/{blend.ColorFunc} " +
|
||||
$"write_mask=0x{blend.WriteMask:X} scissor={scissor} viewport={viewport} " +
|
||||
$"raster=[{raster}] " +
|
||||
$"ps_ena=0x{psInputEna:X8} ps_addr=0x{psInputAddr:X8} " +
|
||||
$"targets=[{targets}] textures=[{textures}] " +
|
||||
$"buffers=[{buffers}] vertex=[{vertexInputs}] indices=[{indices}]");
|
||||
@@ -3639,31 +3605,6 @@ public static class AgcExports
|
||||
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)
|
||||
{
|
||||
var initialPixels = Array.Empty<byte>();
|
||||
@@ -5440,7 +5381,7 @@ public static class AgcExports
|
||||
|
||||
private static void TraceAgc(string message)
|
||||
{
|
||||
if (!_traceAgc)
|
||||
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"), "1", StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -5450,7 +5391,8 @@ public static class AgcExports
|
||||
|
||||
private static void TraceAgcShader(string message)
|
||||
{
|
||||
if (!_traceAgcShader)
|
||||
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"), "1", StringComparison.Ordinal) &&
|
||||
!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC_SHADER"), "1", StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -812,11 +812,7 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
"SFF1I32B32" => left == 0 ? uint.MaxValue : (uint)BitOperations.TrailingZeroCount(left),
|
||||
_ => registers[destination.Value] | (1u << ((int)left & 31)),
|
||||
};
|
||||
if (instruction.Opcode != "SBitset1B32")
|
||||
{
|
||||
scalarConditionCode = registers[destination.Value] != 0;
|
||||
}
|
||||
|
||||
scalarConditionCode = registers[destination.Value] != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -842,15 +838,13 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
}
|
||||
case "SSubU32":
|
||||
result = left - right;
|
||||
scalarConditionCode = right > left;
|
||||
scalarConditionCode = left >= right;
|
||||
break;
|
||||
case "SAddI32":
|
||||
result = unchecked((uint)((int)left + (int)right));
|
||||
scalarConditionCode = SignedAddOverflow(left, right, result);
|
||||
break;
|
||||
case "SSubI32":
|
||||
result = unchecked((uint)((int)left - (int)right));
|
||||
scalarConditionCode = SignedSubOverflow(left, right, result);
|
||||
break;
|
||||
case "SAddcU32":
|
||||
{
|
||||
@@ -861,27 +855,23 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
}
|
||||
case "SSubbU32":
|
||||
{
|
||||
var borrow = scalarConditionCode ? 1UL : 0UL;
|
||||
var borrow = scalarConditionCode ? 0UL : 1UL;
|
||||
var subtrahend = (ulong)right + borrow;
|
||||
result = unchecked(left - (uint)subtrahend);
|
||||
scalarConditionCode = subtrahend > left;
|
||||
scalarConditionCode = left >= subtrahend;
|
||||
break;
|
||||
}
|
||||
case "SMinI32":
|
||||
result = unchecked((uint)Math.Min((int)left, (int)right));
|
||||
scalarConditionCode = (int)left < (int)right;
|
||||
break;
|
||||
case "SMinU32":
|
||||
result = Math.Min(left, right);
|
||||
scalarConditionCode = left < right;
|
||||
break;
|
||||
case "SMaxI32":
|
||||
result = unchecked((uint)Math.Max((int)left, (int)right));
|
||||
scalarConditionCode = (int)left > (int)right;
|
||||
break;
|
||||
case "SMaxU32":
|
||||
result = Math.Max(left, right);
|
||||
scalarConditionCode = left > right;
|
||||
break;
|
||||
case "SCselectB32":
|
||||
result = scalarConditionCode ? left : right;
|
||||
@@ -945,7 +935,6 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
var offset = (int)right & 31;
|
||||
var width = Math.Min(((int)right >> 16) & 0x7F, 32 - offset);
|
||||
result = width == 0 ? 0 : left >> offset & (uint.MaxValue >> (32 - width));
|
||||
scalarConditionCode = result != 0;
|
||||
break;
|
||||
}
|
||||
case "SBfeI32":
|
||||
@@ -955,41 +944,23 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
result = width == 0
|
||||
? 0
|
||||
: unchecked((uint)(((int)(left << (32 - width - offset))) >> (32 - width)));
|
||||
scalarConditionCode = result != 0;
|
||||
break;
|
||||
}
|
||||
case "SAbsdiffI32":
|
||||
result = unchecked((uint)Math.Abs((long)(int)left - (int)right));
|
||||
scalarConditionCode = result != 0;
|
||||
break;
|
||||
case "SLshl1AddU32":
|
||||
{
|
||||
var wide = ((ulong)left << 1) + right;
|
||||
result = (uint)wide;
|
||||
scalarConditionCode = wide > uint.MaxValue;
|
||||
break;
|
||||
}
|
||||
result = (left << 1) + right;
|
||||
break;
|
||||
case "SLshl2AddU32":
|
||||
{
|
||||
var wide = ((ulong)left << 2) + right;
|
||||
result = (uint)wide;
|
||||
scalarConditionCode = wide > uint.MaxValue;
|
||||
break;
|
||||
}
|
||||
result = (left << 2) + right;
|
||||
break;
|
||||
case "SLshl3AddU32":
|
||||
{
|
||||
var wide = ((ulong)left << 3) + right;
|
||||
result = (uint)wide;
|
||||
scalarConditionCode = wide > uint.MaxValue;
|
||||
break;
|
||||
}
|
||||
result = (left << 3) + right;
|
||||
break;
|
||||
case "SLshl4AddU32":
|
||||
{
|
||||
var wide = ((ulong)left << 4) + right;
|
||||
result = (uint)wide;
|
||||
scalarConditionCode = wide > uint.MaxValue;
|
||||
break;
|
||||
}
|
||||
result = (left << 4) + right;
|
||||
break;
|
||||
case "SPackLlB32B16":
|
||||
result = (left & 0xFFFFu) | (right << 16);
|
||||
break;
|
||||
@@ -1031,8 +1002,7 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
"SNandSaveexecB64" or
|
||||
"SNorSaveexecB64" or
|
||||
"SXnorSaveexecB64" or
|
||||
"SAndn1SaveexecB64" or
|
||||
"SOrn1SaveexecB64"))
|
||||
"SAndn1SaveexecB64"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1060,12 +1030,11 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
"SAndSaveexecB64" => oldExec & source,
|
||||
"SOrSaveexecB64" => oldExec | source,
|
||||
"SXorSaveexecB64" => oldExec ^ source,
|
||||
"SAndn1SaveexecB64" => ~source & oldExec,
|
||||
"SAndn2SaveexecB64" => source & ~oldExec,
|
||||
"SOrn1SaveexecB64" => ~source | oldExec,
|
||||
"SOrn2SaveexecB64" => source | ~oldExec,
|
||||
"SNandSaveexecB64" => ~(source & oldExec),
|
||||
"SNorSaveexecB64" => ~(source | oldExec),
|
||||
"SAndn1SaveexecB64" => ~oldExec & source,
|
||||
"SAndn2SaveexecB64" => oldExec & ~source,
|
||||
"SOrn2SaveexecB64" => oldExec | ~source,
|
||||
"SNandSaveexecB64" => ~(oldExec & source),
|
||||
"SNorSaveexecB64" => ~(oldExec | source),
|
||||
_ => ~(oldExec ^ source),
|
||||
};
|
||||
|
||||
@@ -1135,12 +1104,6 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
|
||||
private static ulong MaskWaveValue(ulong value) => value & RdnaWaveMask;
|
||||
|
||||
private static bool SignedAddOverflow(uint left, uint right, uint result) =>
|
||||
((left ^ result) & (right ^ result) & 0x80000000u) != 0;
|
||||
|
||||
private static bool SignedSubOverflow(uint left, uint right, uint result) =>
|
||||
((left ^ right) & (left ^ result) & 0x80000000u) != 0;
|
||||
|
||||
private static bool TryExecuteScalarCompare(
|
||||
Gen5ShaderInstruction instruction,
|
||||
uint[] registers,
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.Libs.VideoOut;
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpEmu.Libs.Agc;
|
||||
@@ -14,14 +13,6 @@ internal static class Gen5ShaderTranslator
|
||||
private const int MaxInstructions = 4096;
|
||||
private const int MinimumUserDataDwords = 16;
|
||||
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 =
|
||||
[
|
||||
@@ -126,51 +117,16 @@ internal static class Gen5ShaderTranslator
|
||||
uint userDataScalarRegisterBase = 0)
|
||||
{
|
||||
state = default!;
|
||||
error = string.Empty;
|
||||
var cache = _decodeCaches.GetValue(ctx.Memory, static _ => new ShaderDecodeCache());
|
||||
Gen5ShaderProgram? program;
|
||||
lock (cache.Gate)
|
||||
if (!TryDecodeProgram(ctx, shaderAddress, out var program, out error))
|
||||
{
|
||||
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);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Gen5ShaderMetadata? metadata = null;
|
||||
if (shaderHeaderAddress != 0)
|
||||
if (shaderHeaderAddress != 0 &&
|
||||
Gen5ShaderMetadataReader.TryRead(ctx, shaderHeaderAddress, out var 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);
|
||||
}
|
||||
}
|
||||
metadata = decodedMetadata;
|
||||
}
|
||||
|
||||
var userData = new uint[GetUserDataDwordCount(metadata)];
|
||||
@@ -496,7 +452,6 @@ internal static class Gen5ShaderTranslator
|
||||
0x2A => "SNorSaveexecB64",
|
||||
0x2B => "SXnorSaveexecB64",
|
||||
0x37 => "SAndn1SaveexecB64",
|
||||
0x38 => "SOrn1SaveexecB64",
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
|
||||
@@ -649,20 +649,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
width);
|
||||
break;
|
||||
}
|
||||
case "VBfiB32":
|
||||
{
|
||||
var mask = GetRawSource(instruction, 0);
|
||||
var insert = GetRawSource(instruction, 1);
|
||||
var source = GetRawSource(instruction, 2);
|
||||
result = _module.AddInstruction(
|
||||
SpirvOp.BitwiseOr,
|
||||
_uintType,
|
||||
BitwiseAnd(mask, insert),
|
||||
BitwiseAnd(
|
||||
_module.AddInstruction(SpirvOp.Not, _uintType, mask),
|
||||
source));
|
||||
break;
|
||||
}
|
||||
case "VCvtPkrtzF16F32":
|
||||
{
|
||||
var first = TruncateFloat32ForPack(GetFloatSource(instruction, 0));
|
||||
@@ -975,16 +961,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
StoreS(destination, result);
|
||||
Store(_scc, IsNotZero(result));
|
||||
return true;
|
||||
case "SBitset1B32":
|
||||
result = _module.AddInstruction(
|
||||
SpirvOp.BitFieldInsert,
|
||||
_uintType,
|
||||
LoadS(destination),
|
||||
UInt(1),
|
||||
BitwiseAnd(left, UInt(31)),
|
||||
UInt(1));
|
||||
StoreS(destination, result);
|
||||
return true;
|
||||
default:
|
||||
{
|
||||
if (instruction.Sources.Count < 2)
|
||||
@@ -1011,14 +987,13 @@ internal static partial class Gen5SpirvTranslator
|
||||
left,
|
||||
right);
|
||||
Store(_scc, _module.AddInstruction(
|
||||
SpirvOp.UGreaterThan,
|
||||
SpirvOp.UGreaterThanEqual,
|
||||
_boolType,
|
||||
right,
|
||||
left));
|
||||
left,
|
||||
right));
|
||||
break;
|
||||
case "SAddI32":
|
||||
result = IAdd(left, right);
|
||||
Store(_scc, SignedAddOverflow(left, right, result));
|
||||
break;
|
||||
case "SSubI32":
|
||||
result = _module.AddInstruction(
|
||||
@@ -1026,7 +1001,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
_uintType,
|
||||
left,
|
||||
right);
|
||||
Store(_scc, SignedSubOverflow(left, right, result));
|
||||
break;
|
||||
case "SAddcU32":
|
||||
{
|
||||
@@ -1063,8 +1037,8 @@ internal static partial class Gen5SpirvTranslator
|
||||
SpirvOp.Select,
|
||||
_uintType,
|
||||
Load(_boolType, _scc),
|
||||
UInt(1),
|
||||
UInt(0));
|
||||
UInt(0),
|
||||
UInt(1));
|
||||
var partial = _module.AddInstruction(
|
||||
SpirvOp.ISub,
|
||||
_uintType,
|
||||
@@ -1075,31 +1049,23 @@ internal static partial class Gen5SpirvTranslator
|
||||
_uintType,
|
||||
partial,
|
||||
borrow);
|
||||
var firstBorrow = _module.AddInstruction(
|
||||
SpirvOp.UGreaterThan,
|
||||
var firstNoBorrow = _module.AddInstruction(
|
||||
SpirvOp.UGreaterThanEqual,
|
||||
_boolType,
|
||||
right,
|
||||
left);
|
||||
var secondBorrow = _module.AddInstruction(
|
||||
SpirvOp.LogicalAnd,
|
||||
left,
|
||||
right);
|
||||
var secondNoBorrow = _module.AddInstruction(
|
||||
SpirvOp.UGreaterThanEqual,
|
||||
_boolType,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.IEqual,
|
||||
_boolType,
|
||||
borrow,
|
||||
UInt(1)),
|
||||
_module.AddInstruction(
|
||||
SpirvOp.IEqual,
|
||||
_boolType,
|
||||
right,
|
||||
left));
|
||||
partial,
|
||||
borrow);
|
||||
Store(
|
||||
_scc,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.LogicalOr,
|
||||
SpirvOp.LogicalAnd,
|
||||
_boolType,
|
||||
firstBorrow,
|
||||
secondBorrow));
|
||||
firstNoBorrow,
|
||||
secondNoBorrow));
|
||||
break;
|
||||
}
|
||||
case "SMulI32":
|
||||
@@ -1111,7 +1077,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
break;
|
||||
case "SAndB32":
|
||||
result = BitwiseAnd(left, right);
|
||||
Store(_scc, IsNotZero(result));
|
||||
break;
|
||||
case "SOrB32":
|
||||
result = _module.AddInstruction(
|
||||
@@ -1119,7 +1084,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
_uintType,
|
||||
left,
|
||||
right);
|
||||
Store(_scc, IsNotZero(result));
|
||||
break;
|
||||
case "SXorB32":
|
||||
result = _module.AddInstruction(
|
||||
@@ -1127,64 +1091,22 @@ internal static partial class Gen5SpirvTranslator
|
||||
_uintType,
|
||||
left,
|
||||
right);
|
||||
Store(_scc, IsNotZero(result));
|
||||
break;
|
||||
case "SAndn2B32":
|
||||
result = BitwiseAnd(
|
||||
left,
|
||||
_module.AddInstruction(SpirvOp.Not, _uintType, right));
|
||||
Store(_scc, IsNotZero(result));
|
||||
break;
|
||||
case "SOrn2B32":
|
||||
result = _module.AddInstruction(
|
||||
SpirvOp.BitwiseOr,
|
||||
_uintType,
|
||||
left,
|
||||
_module.AddInstruction(SpirvOp.Not, _uintType, right));
|
||||
Store(_scc, IsNotZero(result));
|
||||
break;
|
||||
case "SNandB32":
|
||||
result = _module.AddInstruction(
|
||||
SpirvOp.Not,
|
||||
_uintType,
|
||||
BitwiseAnd(left, right));
|
||||
Store(_scc, IsNotZero(result));
|
||||
break;
|
||||
case "SNorB32":
|
||||
result = _module.AddInstruction(
|
||||
SpirvOp.Not,
|
||||
_uintType,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.BitwiseOr,
|
||||
_uintType,
|
||||
left,
|
||||
right));
|
||||
Store(_scc, IsNotZero(result));
|
||||
break;
|
||||
case "SXnorB32":
|
||||
result = _module.AddInstruction(
|
||||
SpirvOp.Not,
|
||||
_uintType,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.BitwiseXor,
|
||||
_uintType,
|
||||
left,
|
||||
right));
|
||||
Store(_scc, IsNotZero(result));
|
||||
break;
|
||||
case "SLshlB32":
|
||||
result = ShiftLeftLogical(left, right);
|
||||
Store(_scc, IsNotZero(result));
|
||||
break;
|
||||
case "SLshrB32":
|
||||
result = ShiftRightLogical(
|
||||
left,
|
||||
BitwiseAnd(right, UInt(31)));
|
||||
Store(_scc, IsNotZero(result));
|
||||
break;
|
||||
case "SAshrI32":
|
||||
result = ShiftRightArithmetic(left, right);
|
||||
Store(_scc, IsNotZero(result));
|
||||
break;
|
||||
case "SBfmB32":
|
||||
result = _module.AddInstruction(
|
||||
@@ -1227,7 +1149,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
left,
|
||||
offset,
|
||||
width);
|
||||
Store(_scc, IsNotZero(result));
|
||||
break;
|
||||
}
|
||||
case "SCselectB32":
|
||||
@@ -1240,47 +1161,9 @@ internal static partial class Gen5SpirvTranslator
|
||||
break;
|
||||
case "SMinU32":
|
||||
result = Ext(38, _uintType, left, right);
|
||||
Store(
|
||||
_scc,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.ULessThan,
|
||||
_boolType,
|
||||
left,
|
||||
right));
|
||||
break;
|
||||
case "SMinI32":
|
||||
result = Bitcast(
|
||||
_uintType,
|
||||
Ext(39, _intType, Bitcast(_intType, left), Bitcast(_intType, right)));
|
||||
Store(
|
||||
_scc,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.SLessThan,
|
||||
_boolType,
|
||||
Bitcast(_intType, left),
|
||||
Bitcast(_intType, right)));
|
||||
break;
|
||||
case "SMaxU32":
|
||||
result = Ext(41, _uintType, left, right);
|
||||
Store(
|
||||
_scc,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.UGreaterThan,
|
||||
_boolType,
|
||||
left,
|
||||
right));
|
||||
break;
|
||||
case "SMaxI32":
|
||||
result = Bitcast(
|
||||
_uintType,
|
||||
Ext(42, _intType, Bitcast(_intType, left), Bitcast(_intType, right)));
|
||||
Store(
|
||||
_scc,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.SGreaterThan,
|
||||
_boolType,
|
||||
Bitcast(_intType, left),
|
||||
Bitcast(_intType, right)));
|
||||
break;
|
||||
case "SLshl1AddU32":
|
||||
case "SLshl2AddU32":
|
||||
@@ -1425,55 +1308,17 @@ internal static partial class Gen5SpirvTranslator
|
||||
"SXorSaveexecB64" => _module.AddInstruction(
|
||||
SpirvOp.BitwiseXor, _ulongType, oldExec, left),
|
||||
"SAndn2SaveexecB64" => _module.AddInstruction(
|
||||
SpirvOp.BitwiseAnd,
|
||||
_ulongType,
|
||||
left,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.Not,
|
||||
_ulongType,
|
||||
oldExec)),
|
||||
SpirvOp.BitwiseAnd, _ulongType, oldExec, notLeft),
|
||||
"SAndn1SaveexecB64" => _module.AddInstruction(
|
||||
SpirvOp.BitwiseAnd,
|
||||
_ulongType,
|
||||
notLeft,
|
||||
oldExec),
|
||||
"SOrn1SaveexecB64" => _module.AddInstruction(
|
||||
SpirvOp.BitwiseOr,
|
||||
_ulongType,
|
||||
notLeft,
|
||||
oldExec),
|
||||
"SOrn2SaveexecB64" => _module.AddInstruction(
|
||||
SpirvOp.BitwiseOr,
|
||||
_ulongType,
|
||||
left,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.Not,
|
||||
_ulongType,
|
||||
oldExec)),
|
||||
"SNandSaveexecB64" => _module.AddInstruction(
|
||||
SpirvOp.Not,
|
||||
_ulongType,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.BitwiseAnd,
|
||||
_ulongType,
|
||||
left,
|
||||
oldExec)),
|
||||
"SNorSaveexecB64" => _module.AddInstruction(
|
||||
SpirvOp.Not,
|
||||
_ulongType,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.BitwiseOr,
|
||||
_ulongType,
|
||||
left,
|
||||
oldExec)),
|
||||
"SXnorSaveexecB64" => _module.AddInstruction(
|
||||
SpirvOp.Not,
|
||||
_ulongType,
|
||||
_module.AddInstruction(
|
||||
SpirvOp.BitwiseXor,
|
||||
_ulongType,
|
||||
left,
|
||||
oldExec)),
|
||||
oldExec),
|
||||
left),
|
||||
"SOrn2SaveexecB64" => _module.AddInstruction(
|
||||
SpirvOp.BitwiseOr, _ulongType, oldExec, notLeft),
|
||||
_ => 0u,
|
||||
};
|
||||
if (newExec == 0)
|
||||
@@ -1679,22 +1524,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
}
|
||||
|
||||
StoreS64(destination, value);
|
||||
if (instruction.Opcode is
|
||||
"SNotB64" or
|
||||
"SAndB64" or
|
||||
"SOrB64" or
|
||||
"SXorB64" or
|
||||
"SAndn1B64" or
|
||||
"SAndn2B64" or
|
||||
"SOrn1B64" or
|
||||
"SOrn2B64" or
|
||||
"SNandB64" or
|
||||
"SNorB64" or
|
||||
"SXnorB64")
|
||||
{
|
||||
Store(_scc, IsNotZero64(value));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2281,53 +2110,6 @@ internal static partial class Gen5SpirvTranslator
|
||||
value,
|
||||
_module.Constant64(_ulongType, 0));
|
||||
|
||||
private uint SignBit(uint value) =>
|
||||
ShiftRightLogical(value, UInt(31));
|
||||
|
||||
private uint SignedAddOverflow(uint left, uint right, uint result)
|
||||
{
|
||||
var leftSign = SignBit(left);
|
||||
var rightSign = SignBit(right);
|
||||
var resultSign = SignBit(result);
|
||||
var sameSourceSign = _module.AddInstruction(
|
||||
SpirvOp.IEqual,
|
||||
_boolType,
|
||||
leftSign,
|
||||
rightSign);
|
||||
var resultSignChanged = _module.AddInstruction(
|
||||
SpirvOp.INotEqual,
|
||||
_boolType,
|
||||
leftSign,
|
||||
resultSign);
|
||||
return _module.AddInstruction(
|
||||
SpirvOp.LogicalAnd,
|
||||
_boolType,
|
||||
sameSourceSign,
|
||||
resultSignChanged);
|
||||
}
|
||||
|
||||
private uint SignedSubOverflow(uint left, uint right, uint result)
|
||||
{
|
||||
var leftSign = SignBit(left);
|
||||
var rightSign = SignBit(right);
|
||||
var resultSign = SignBit(result);
|
||||
var differentSourceSign = _module.AddInstruction(
|
||||
SpirvOp.INotEqual,
|
||||
_boolType,
|
||||
leftSign,
|
||||
rightSign);
|
||||
var resultSignChanged = _module.AddInstruction(
|
||||
SpirvOp.INotEqual,
|
||||
_boolType,
|
||||
leftSign,
|
||||
resultSign);
|
||||
return _module.AddInstruction(
|
||||
SpirvOp.LogicalAnd,
|
||||
_boolType,
|
||||
differentSourceSign,
|
||||
resultSignChanged);
|
||||
}
|
||||
|
||||
private static bool TryDecodeInlineConstant(uint encoded, out uint value)
|
||||
{
|
||||
if (encoded == 125)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using SharpEmu.HLE;
|
||||
using System.Buffers.Binary;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SharpEmu.Libs.Pad;
|
||||
|
||||
@@ -59,7 +58,6 @@ public static class PadExports
|
||||
return SetReturn(ctx, OrbisPadErrorDeviceNotConnected);
|
||||
}
|
||||
|
||||
Console.Error.WriteLine("[LOADER][INFO] Keyboard controls: Arrow keys = D-pad, WASD = left stick, IJKL = right stick, Z/Enter = Cross, X/Esc = Circle, C = Square, V = Triangle, Q = L1, E = R1, R = L2, F = R2, Tab/Backspace = Options");
|
||||
return SetReturn(ctx, PrimaryPadHandle);
|
||||
}
|
||||
|
||||
@@ -164,19 +162,10 @@ public static class PadExports
|
||||
{
|
||||
Span<byte> data = stackalloc byte[PadDataSize];
|
||||
data.Clear();
|
||||
var acceptsKeyboardInput = IsEmulatorWindowFocused();
|
||||
var buttons = acceptsKeyboardInput ? ReadKeyboardButtons() : 0;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(data[0x00..], buttons);
|
||||
var leftX = acceptsKeyboardInput ? ReadAnalogStick(IsKeyDown(0x41), IsKeyDown(0x44)) : (byte)128;
|
||||
var leftY = acceptsKeyboardInput ? ReadAnalogStick(IsKeyDown(0x57), IsKeyDown(0x53)) : (byte)128;
|
||||
var rightX = acceptsKeyboardInput ? ReadAnalogStick(IsKeyDown(0x4A), IsKeyDown(0x4C)) : (byte)128;
|
||||
var rightY = acceptsKeyboardInput ? ReadAnalogStick(IsKeyDown(0x49), IsKeyDown(0x4B)) : (byte)128;
|
||||
data[0x04] = leftX;
|
||||
data[0x05] = leftY;
|
||||
data[0x06] = rightX;
|
||||
data[0x07] = rightY;
|
||||
data[0x08] = acceptsKeyboardInput && IsKeyDown(0x52) ? (byte)255 : (byte)0;
|
||||
data[0x09] = acceptsKeyboardInput && IsKeyDown(0x46) ? (byte)255 : (byte)0;
|
||||
data[0x04] = 128;
|
||||
data[0x05] = 128;
|
||||
data[0x06] = 128;
|
||||
data[0x07] = 128;
|
||||
BinaryPrimitives.WriteSingleLittleEndian(data[0x18..], 1.0f);
|
||||
data[0x4C] = 1;
|
||||
var timestampTicks = Stopwatch.GetTimestamp();
|
||||
@@ -196,58 +185,4 @@ public static class PadExports
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern short GetAsyncKeyState(int vKey);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern nint GetForegroundWindow();
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern uint GetWindowThreadProcessId(nint hWnd, out uint processId);
|
||||
|
||||
private static bool IsKeyDown(int vk) =>
|
||||
(GetAsyncKeyState(vk) & 0x8000) != 0;
|
||||
|
||||
private static bool IsEmulatorWindowFocused()
|
||||
{
|
||||
var foregroundWindow = GetForegroundWindow();
|
||||
if (foregroundWindow == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
GetWindowThreadProcessId(foregroundWindow, out var processId);
|
||||
return processId == (uint)Environment.ProcessId;
|
||||
}
|
||||
|
||||
private static uint ReadKeyboardButtons()
|
||||
{
|
||||
uint buttons = 0;
|
||||
// D-pad
|
||||
if (IsKeyDown(0x25)) buttons |= 0x0080; // Left
|
||||
if (IsKeyDown(0x27)) buttons |= 0x0020; // Right
|
||||
if (IsKeyDown(0x26)) buttons |= 0x0010; // Up
|
||||
if (IsKeyDown(0x28)) buttons |= 0x0040; // Down
|
||||
// Face buttons
|
||||
if (IsKeyDown(0x5A) || IsKeyDown(0x0D)) buttons |= 0x4000; // Z / Enter = Cross
|
||||
if (IsKeyDown(0x58) || IsKeyDown(0x1B)) buttons |= 0x2000; // X / Escape = Circle
|
||||
if (IsKeyDown(0x43)) buttons |= 0x8000; // C = Square
|
||||
if (IsKeyDown(0x56)) buttons |= 0x1000; // V = Triangle
|
||||
// Shoulder buttons
|
||||
if (IsKeyDown(0x51)) buttons |= 0x0400; // Q = L1
|
||||
if (IsKeyDown(0x45)) buttons |= 0x0800; // E = R1
|
||||
if (IsKeyDown(0x52)) buttons |= 0x0100; // R = L2 (digital)
|
||||
if (IsKeyDown(0x46)) buttons |= 0x0200; // F = R2 (digital)
|
||||
// Options (Start)
|
||||
if (IsKeyDown(0x09) || IsKeyDown(0x08)) buttons |= 0x0008; // Tab / Backspace = Options
|
||||
return buttons;
|
||||
}
|
||||
|
||||
private static byte ReadAnalogStick(bool negative, bool positive)
|
||||
{
|
||||
if (negative && !positive) return 0;
|
||||
if (positive && !negative) return 255;
|
||||
return 128;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,7 +400,7 @@ public static class SaveDataExports
|
||||
{
|
||||
var configured = Environment.GetEnvironmentVariable("SHARPEMU_SAVEDATA_DIR");
|
||||
var root = string.IsNullOrWhiteSpace(configured)
|
||||
? Path.Combine(AppContext.BaseDirectory, "user", "savedata")
|
||||
? Path.Combine(Environment.CurrentDirectory, "user", "savedata")
|
||||
: configured;
|
||||
return Path.GetFullPath(root);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.Libs.Kernel;
|
||||
using System.Buffers.Binary;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
|
||||
@@ -49,13 +48,6 @@ public static class VideoOutExports
|
||||
private static int _frameDumpCount;
|
||||
private static long _nextFrameDumpIndex;
|
||||
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)
|
||||
{
|
||||
@@ -820,46 +812,9 @@ public static class VideoOutExports
|
||||
}
|
||||
|
||||
TraceVideoOut($"videoout.submit_flip handle={handle} index={bufferIndex} mode={flipMode} arg={flipArg} events={flipEvents.Count}");
|
||||
ReportFrameRate(presented: false);
|
||||
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)
|
||||
{
|
||||
lock (_stateGate)
|
||||
|
||||
@@ -8,9 +8,7 @@ using SharpEmu.Libs.Agc;
|
||||
using Silk.NET.Vulkan;
|
||||
using Silk.NET.Vulkan.Extensions.KHR;
|
||||
using Silk.NET.Windowing;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
||||
using VkSemaphore = Silk.NET.Vulkan.Semaphore;
|
||||
@@ -599,24 +597,6 @@ internal static unsafe class VulkanVideoPresenter
|
||||
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(
|
||||
ulong sourceAddress,
|
||||
uint sourceWidth,
|
||||
@@ -887,50 +867,14 @@ internal static unsafe class VulkanVideoPresenter
|
||||
private int _tracedVertexBufferCount;
|
||||
private readonly Dictionary<byte[], Pipeline> _computePipelines =
|
||||
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 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
|
||||
{
|
||||
public string DebugName = "SharpEmu translated";
|
||||
public PipelineLayout PipelineLayout;
|
||||
public Pipeline Pipeline;
|
||||
public bool PipelineCached;
|
||||
public bool DescriptorLayoutCached;
|
||||
public DescriptorSetLayout DescriptorSetLayout;
|
||||
public DescriptorPool DescriptorPool;
|
||||
public DescriptorSet DescriptorSet;
|
||||
@@ -2034,16 +1978,81 @@ internal static unsafe class VulkanVideoPresenter
|
||||
var storageImageCount = textureCount - sampledImageCount;
|
||||
var globalBufferCount = resources.GlobalMemoryBuffers.Length;
|
||||
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)
|
||||
{
|
||||
var layoutInfo = new PipelineLayoutCreateInfo
|
||||
{
|
||||
SType = StructureType.PipelineLayoutCreateInfo,
|
||||
};
|
||||
PipelineLayout pipelineLayout;
|
||||
Check(
|
||||
_vk.CreatePipelineLayout(_device, &layoutInfo, null, out pipelineLayout),
|
||||
"vkCreatePipelineLayout");
|
||||
resources.PipelineLayout = pipelineLayout;
|
||||
return;
|
||||
}
|
||||
|
||||
var setLayout = layout.DescriptorSetLayout;
|
||||
var bindings = new DescriptorSetLayoutBinding[bindingCount];
|
||||
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[
|
||||
(sampledImageCount == 0 ? 0 : 1) +
|
||||
@@ -2193,21 +2202,6 @@ internal static unsafe class VulkanVideoPresenter
|
||||
RenderPass renderPass,
|
||||
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 fragmentModule = CreateShaderModule(fragmentSpirv);
|
||||
var entryPoint = (byte*)SilkMarshal.StringToPtr("main");
|
||||
@@ -2359,8 +2353,6 @@ internal static unsafe class VulkanVideoPresenter
|
||||
out pipeline),
|
||||
"vkCreateGraphicsPipelines(translated)");
|
||||
resources.Pipeline = pipeline;
|
||||
resources.PipelineCached = true;
|
||||
_graphicsPipelines.Add(pipelineKey, pipeline);
|
||||
SetDebugName(
|
||||
ObjectType.Pipeline,
|
||||
pipeline.Handle,
|
||||
@@ -2375,129 +2367,6 @@ 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)]
|
||||
private void CreateComputePipeline(
|
||||
TranslatedDrawResources resources,
|
||||
@@ -2910,7 +2779,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
SetDebugName(ObjectType.Buffer, stagingBuffer.Handle, $"{debugName} staging");
|
||||
SetDebugName(ObjectType.Image, image.Handle, $"{debugName} image");
|
||||
SetDebugName(ObjectType.ImageView, view.Handle, $"{debugName} view");
|
||||
var resource = new TextureResource
|
||||
return new TextureResource
|
||||
{
|
||||
Address = texture.Address,
|
||||
StagingBuffer = stagingBuffer,
|
||||
@@ -2926,38 +2795,6 @@ internal static unsafe class VulkanVideoPresenter
|
||||
OwnsStorage = true,
|
||||
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(
|
||||
@@ -3058,11 +2895,6 @@ internal static unsafe class VulkanVideoPresenter
|
||||
|
||||
private Sampler CreateSampler(VulkanGuestSampler sampler)
|
||||
{
|
||||
if (_samplers.TryGetValue(sampler, out var cachedSampler))
|
||||
{
|
||||
return cachedSampler;
|
||||
}
|
||||
|
||||
var minLod = DecodeSamplerMipFilter(sampler) == 0
|
||||
? 0f
|
||||
: DecodeSamplerMinLod(sampler);
|
||||
@@ -3089,7 +2921,6 @@ internal static unsafe class VulkanVideoPresenter
|
||||
Check(
|
||||
_vk.CreateSampler(_device, &samplerInfo, null, out vkSampler),
|
||||
"vkCreateSampler(texture)");
|
||||
_samplers.Add(sampler, vkSampler);
|
||||
return vkSampler;
|
||||
}
|
||||
|
||||
@@ -3204,31 +3035,11 @@ internal static unsafe class VulkanVideoPresenter
|
||||
out DeviceMemory memory)
|
||||
{
|
||||
var size = (ulong)Math.Max(data.Length, sizeof(uint));
|
||||
var capacity = BitOperations.RoundUpToPowerOf2(size);
|
||||
var key = new HostBufferPoolKey(usage, capacity);
|
||||
if (!_hostBufferPool.TryGetValue(key, out var available))
|
||||
{
|
||||
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;
|
||||
var buffer = CreateBuffer(
|
||||
size,
|
||||
usage,
|
||||
MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit,
|
||||
out memory);
|
||||
void* mapped;
|
||||
Check(_vk.MapMemory(_device, memory, 0, size, 0, &mapped), "vkMapMemory(host)");
|
||||
try
|
||||
@@ -3247,28 +3058,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
_vk.UnmapMemory(_device, memory);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static PrimitiveTopology GetPrimitiveTopology(uint primitiveType) =>
|
||||
@@ -3818,7 +3608,6 @@ internal static unsafe class VulkanVideoPresenter
|
||||
resources,
|
||||
GetTraceImages(resources));
|
||||
submitted = true;
|
||||
MarkSampledImagesInitialized(resources);
|
||||
MarkStorageImagesInitialized(resources, traceContents: false);
|
||||
TraceVulkanShader(
|
||||
$"vk.compute_dispatch groups={work.GroupCountX}x" +
|
||||
@@ -4007,7 +3796,6 @@ internal static unsafe class VulkanVideoPresenter
|
||||
GetTraceImages(resources, target));
|
||||
submitted = true;
|
||||
target.Initialized = true;
|
||||
MarkSampledImagesInitialized(resources);
|
||||
MarkStorageImagesInitialized(resources, traceContents: false);
|
||||
|
||||
var guestTextureFormat = GetGuestTextureFormat(target.Format);
|
||||
@@ -4710,7 +4498,6 @@ internal static unsafe class VulkanVideoPresenter
|
||||
CheckSwapchainResult(presentResult, "vkQueuePresentKHR");
|
||||
recreateAfterPresent |= presentResult == Result.SuboptimalKhr;
|
||||
Check(_vk.QueueWaitIdle(_queue), "vkQueueWaitIdle");
|
||||
VideoOutExports.ReportPresentedFrame();
|
||||
if (_swapchainReadbackPending)
|
||||
{
|
||||
TraceSwapchainReadback();
|
||||
@@ -4718,7 +4505,6 @@ internal static unsafe class VulkanVideoPresenter
|
||||
CollectCompletedGuestSubmissions(waitForOldest: false);
|
||||
if (translatedResources is not null)
|
||||
{
|
||||
MarkSampledImagesInitialized(translatedResources);
|
||||
MarkStorageImagesInitialized(translatedResources);
|
||||
DestroyTranslatedDrawResources(translatedResources);
|
||||
}
|
||||
@@ -5224,27 +5010,6 @@ 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)
|
||||
{
|
||||
if (image.Address == 0)
|
||||
@@ -5483,6 +5248,11 @@ internal static unsafe class VulkanVideoPresenter
|
||||
_vk.FreeMemory(_device, texture.StagingMemory, null);
|
||||
}
|
||||
|
||||
if (texture.Sampler.Handle != 0)
|
||||
{
|
||||
_vk.DestroySampler(_device, texture.Sampler, null);
|
||||
}
|
||||
|
||||
if (texture.NeedsUpload &&
|
||||
texture.GuestImage is { Initialized: false } guestImage)
|
||||
{
|
||||
@@ -5497,7 +5267,15 @@ internal static unsafe class VulkanVideoPresenter
|
||||
continue;
|
||||
}
|
||||
|
||||
RecycleHostBuffer(globalBuffer.Buffer, globalBuffer.Memory);
|
||||
if (globalBuffer.Buffer.Handle != 0)
|
||||
{
|
||||
_vk.DestroyBuffer(_device, globalBuffer.Buffer, null);
|
||||
}
|
||||
|
||||
if (globalBuffer.Memory.Handle != 0)
|
||||
{
|
||||
_vk.FreeMemory(_device, globalBuffer.Memory, null);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var vertexBuffer in resources.VertexBuffers)
|
||||
@@ -5507,10 +5285,26 @@ internal static unsafe class VulkanVideoPresenter
|
||||
continue;
|
||||
}
|
||||
|
||||
RecycleHostBuffer(vertexBuffer.Buffer, vertexBuffer.Memory);
|
||||
if (vertexBuffer.Buffer.Handle != 0)
|
||||
{
|
||||
_vk.DestroyBuffer(_device, vertexBuffer.Buffer, null);
|
||||
}
|
||||
|
||||
if (vertexBuffer.Memory.Handle != 0)
|
||||
{
|
||||
_vk.FreeMemory(_device, vertexBuffer.Memory, null);
|
||||
}
|
||||
}
|
||||
|
||||
RecycleHostBuffer(resources.IndexBuffer, resources.IndexMemory);
|
||||
if (resources.IndexBuffer.Handle != 0)
|
||||
{
|
||||
_vk.DestroyBuffer(_device, resources.IndexBuffer, null);
|
||||
}
|
||||
|
||||
if (resources.IndexMemory.Handle != 0)
|
||||
{
|
||||
_vk.FreeMemory(_device, resources.IndexMemory, null);
|
||||
}
|
||||
|
||||
if (!resources.PipelineCached && resources.Pipeline.Handle != 0)
|
||||
{
|
||||
@@ -5522,14 +5316,12 @@ internal static unsafe class VulkanVideoPresenter
|
||||
_vk.DestroyDescriptorPool(_device, resources.DescriptorPool, null);
|
||||
}
|
||||
|
||||
if (!resources.DescriptorLayoutCached &&
|
||||
resources.PipelineLayout.Handle != 0)
|
||||
if (resources.PipelineLayout.Handle != 0)
|
||||
{
|
||||
_vk.DestroyPipelineLayout(_device, resources.PipelineLayout, null);
|
||||
}
|
||||
|
||||
if (!resources.DescriptorLayoutCached &&
|
||||
resources.DescriptorSetLayout.Handle != 0)
|
||||
if (resources.DescriptorSetLayout.Handle != 0)
|
||||
{
|
||||
_vk.DestroyDescriptorSetLayout(_device, resources.DescriptorSetLayout, null);
|
||||
}
|
||||
@@ -5964,36 +5756,6 @@ internal static unsafe class VulkanVideoPresenter
|
||||
_vk.DestroyPipeline(_device, pipeline, null);
|
||||
}
|
||||
_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)
|
||||
{
|
||||
DestroyGuestImage(guestImage);
|
||||
|
||||
Reference in New Issue
Block a user