Compare commits

..

5 Commits

22 changed files with 240 additions and 1095 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ indent_size = 4
indent_style = space indent_style = space
tab_width = 4 tab_width = 4
[*.{md,json,yml,xml,props,csproj}] [*.{md,json,yml,props,csproj}]
indent_size = 2 indent_size = 2
tab_width = 2 tab_width = 2
Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

+1 -1
View File
@@ -126,7 +126,7 @@ jobs:
needs: needs:
- init - init
- build - 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 runs-on: ubuntu-latest
permissions: permissions:
contents: write contents: write
-1
View File
@@ -26,7 +26,6 @@ arm64/
*.userosscache *.userosscache
*.sln.docstates *.sln.docstates
.packages
packages/ packages/
*.nupkg *.nupkg
.nuget/ .nuget/
BIN
View File
Binary file not shown.
+1 -3
View File
@@ -3,14 +3,12 @@ version = 1
[[annotations]] [[annotations]]
path = [ path = [
"REUSE.toml", "REUSE.toml",
"nuget.config",
"global.json", "global.json",
"**/packages.lock.json", "**/packages.lock.json",
"scripts/ps5_names.txt", "scripts/ps5_names.txt",
"src/SharpEmu.HLE/Aerolib/aerolib.bin", "src/SharpEmu.HLE/Aerolib/aerolib.bin",
"_logs/**", "_logs/**",
".github/images/**", ".github/images/**"
"assets/images/**"
] ]
precedence = "aggregate" precedence = "aggregate"
SPDX-FileCopyrightText = "SharpEmu Emulator Project" SPDX-FileCopyrightText = "SharpEmu Emulator Project"
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

-18
View File
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="globalPackagesFolder" value=".packages" />
</config>
<packageSources>
<clear />
<add key="Nuget" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="Nuget">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
+1 -6
View File
@@ -16,7 +16,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
<RuntimeIdentifiers>win-x64;linux-x64;osx-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;linux-x64;osx-arm64</RuntimeIdentifiers>
<SelfContained>true</SelfContained> <SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>true</PublishSingleFile>
<!-- <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract> --> <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile> <EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -30,11 +30,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile> <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'win-x64' Or '$(RuntimeIdentifier)' == ''">
<ApplicationIcon>..\..\assets\images\SharpEmu.ico</ApplicationIcon>
<Win32Icon>..\..\assets\images\SharpEmu.ico</Win32Icon>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="..\..\LICENSE.txt"> <Content Include="..\..\LICENSE.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+73 -104
View File
@@ -81,7 +81,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
_gate.EnterWriteLock(); _gate.EnterWriteLock();
try try
{ {
InsertRegionSorted(new MemoryRegion _regions.Add(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
{ {
InsertRegionSorted(new MemoryRegion _regions.Add(new MemoryRegion
{ {
VirtualAddress = actualAddress, VirtualAddress = actualAddress,
Size = alignedSize, Size = alignedSize,
@@ -473,34 +473,30 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
_gate.EnterReadLock(); _gate.EnterReadLock();
try try
{ {
var region = FindRegion(virtualAddress, (ulong)destination.Length); foreach (var region in _regions)
if (region is not null &&
TryResolveRegionOffset(
virtualAddress,
(ulong)destination.Length,
region,
out var offset))
{ {
var srcPtr = (void*)(region.VirtualAddress + offset); if (TryResolveRegionOffset(virtualAddress, (ulong)destination.Length, region, out var offset))
if (destination.IsEmpty)
{ {
return true; var srcPtr = (void*)(region.VirtualAddress + offset);
} if (destination.IsEmpty)
if (region.IsReservedOnly)
{
if (!EnsureRangeCommitted((ulong)srcPtr, (ulong)destination.Length, region))
{ {
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) fixed (byte* destPtr = destination)
{ {
Buffer.MemoryCopy(srcPtr, destPtr, (nuint)destination.Length, (nuint)destination.Length); Buffer.MemoryCopy(srcPtr, destPtr, (nuint)destination.Length, (nuint)destination.Length);
@@ -537,34 +533,30 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
_gate.EnterReadLock(); _gate.EnterReadLock();
try try
{ {
var region = FindRegion(virtualAddress, (ulong)source.Length); foreach (var region in _regions)
if (region is not null &&
TryResolveRegionOffset(
virtualAddress,
(ulong)source.Length,
region,
out var offset))
{ {
var destPtr = (void*)(region.VirtualAddress + offset); if (TryResolveRegionOffset(virtualAddress, (ulong)source.Length, region, out var offset))
if (source.IsEmpty)
{ {
return true; var destPtr = (void*)(region.VirtualAddress + offset);
} if (source.IsEmpty)
if (region.IsReservedOnly)
{
if (!EnsureRangeCommitted((ulong)destPtr, (ulong)source.Length, region))
{ {
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) fixed (byte* srcPtr = source)
{ {
Buffer.MemoryCopy(srcPtr, destPtr, (nuint)source.Length, (nuint)source.Length); 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) private bool TryReadExclusive(ulong virtualAddress, Span<byte> destination)
{ {
var region = FindRegion(virtualAddress, (ulong)destination.Length); foreach (var region in _regions)
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))
{ {
@@ -646,14 +637,13 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
private bool TryWriteExclusive(ulong virtualAddress, ReadOnlySpan<byte> source) private bool TryWriteExclusive(ulong virtualAddress, ReadOnlySpan<byte> source)
{ {
var region = FindRegion(virtualAddress, (ulong)source.Length); foreach (var region in _regions)
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))
{ {
@@ -709,9 +699,15 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
_gate.EnterReadLock(); _gate.EnterReadLock();
try try
{ {
return FindRegion(virtualAddress, 1) is not null foreach (var region in _regions)
? (void*)virtualAddress {
: null; if (virtualAddress >= region.VirtualAddress &&
virtualAddress < region.VirtualAddress + region.Size)
{
return (void*)virtualAddress;
}
}
return null;
} }
finally finally
{ {
@@ -724,7 +720,14 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
_gate.EnterReadLock(); _gate.EnterReadLock();
try try
{ {
return FindRegion(virtualAddress, size) is not null; foreach (var region in _regions)
{
if (TryResolveRegionOffset(virtualAddress, size, region, out _))
{
return true;
}
}
return false;
} }
finally finally
{ {
@@ -734,48 +737,14 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
private MemoryRegion? FindRegion(ulong address, ulong size) private MemoryRegion? FindRegion(ulong address, ulong size)
{ {
var low = 0; foreach (var region in _regions)
var high = _regions.Count - 1;
MemoryRegion? candidate = null;
while (low <= high)
{ {
var middle = low + ((high - low) >> 1); if (TryResolveRegionOffset(address, size, region, out _))
var region = _regions[middle];
if (region.VirtualAddress <= address)
{ {
candidate = region; return 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)
+3 -61
View File
@@ -155,16 +155,6 @@ 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;
@@ -3503,29 +3493,6 @@ public static class AgcExports
$"{drawViewport.Width:0.###}x{drawViewport.Height:0.###}:" + $"{drawViewport.Width:0.###}x{drawViewport.Height:0.###}:" +
$"{drawViewport.MinDepth:0.###}-{drawViewport.MaxDepth:0.###}" $"{drawViewport.MinDepth:0.###}-{drawViewport.MaxDepth:0.###}"
: "full"; : "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; var blend = draw.RenderState.Blend;
TraceAgcShader( TraceAgcShader(
$"agc.shader_draw es=0x{draw.ExportShaderAddress:X16} " + $"agc.shader_draw es=0x{draw.ExportShaderAddress:X16} " +
@@ -3533,7 +3500,6 @@ public static class AgcExports
$"primitive=0x{draw.PrimitiveType:X} " + $"primitive=0x{draw.PrimitiveType:X} " +
$"blend={(blend.Enable ? 1 : 0)}:{blend.ColorSrcFactor}/{blend.ColorDstFactor}/{blend.ColorFunc} " + $"blend={(blend.Enable ? 1 : 0)}:{blend.ColorSrcFactor}/{blend.ColorDstFactor}/{blend.ColorFunc} " +
$"write_mask=0x{blend.WriteMask:X} scissor={scissor} viewport={viewport} " + $"write_mask=0x{blend.WriteMask:X} scissor={scissor} viewport={viewport} " +
$"raster=[{raster}] " +
$"ps_ena=0x{psInputEna:X8} ps_addr=0x{psInputAddr:X8} " + $"ps_ena=0x{psInputEna:X8} ps_addr=0x{psInputAddr:X8} " +
$"targets=[{targets}] textures=[{textures}] " + $"targets=[{targets}] textures=[{textures}] " +
$"buffers=[{buffers}] vertex=[{vertexInputs}] indices=[{indices}]"); $"buffers=[{buffers}] vertex=[{vertexInputs}] indices=[{indices}]");
@@ -3639,31 +3605,6 @@ 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>();
@@ -5440,7 +5381,7 @@ public static class AgcExports
private static void TraceAgc(string message) private static void TraceAgc(string message)
{ {
if (!_traceAgc) if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"), "1", StringComparison.Ordinal))
{ {
return; return;
} }
@@ -5450,7 +5391,8 @@ public static class AgcExports
private static void TraceAgcShader(string message) 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; return;
} }
@@ -812,11 +812,7 @@ internal static class Gen5ShaderScalarEvaluator
"SFF1I32B32" => left == 0 ? uint.MaxValue : (uint)BitOperations.TrailingZeroCount(left), "SFF1I32B32" => left == 0 ? uint.MaxValue : (uint)BitOperations.TrailingZeroCount(left),
_ => registers[destination.Value] | (1u << ((int)left & 31)), _ => registers[destination.Value] | (1u << ((int)left & 31)),
}; };
if (instruction.Opcode != "SBitset1B32") scalarConditionCode = registers[destination.Value] != 0;
{
scalarConditionCode = registers[destination.Value] != 0;
}
return true; return true;
} }
@@ -842,15 +838,13 @@ internal static class Gen5ShaderScalarEvaluator
} }
case "SSubU32": case "SSubU32":
result = left - right; result = left - right;
scalarConditionCode = right > left; scalarConditionCode = left >= right;
break; break;
case "SAddI32": case "SAddI32":
result = unchecked((uint)((int)left + (int)right)); result = unchecked((uint)((int)left + (int)right));
scalarConditionCode = SignedAddOverflow(left, right, result);
break; break;
case "SSubI32": case "SSubI32":
result = unchecked((uint)((int)left - (int)right)); result = unchecked((uint)((int)left - (int)right));
scalarConditionCode = SignedSubOverflow(left, right, result);
break; break;
case "SAddcU32": case "SAddcU32":
{ {
@@ -861,27 +855,23 @@ internal static class Gen5ShaderScalarEvaluator
} }
case "SSubbU32": case "SSubbU32":
{ {
var borrow = scalarConditionCode ? 1UL : 0UL; var borrow = scalarConditionCode ? 0UL : 1UL;
var subtrahend = (ulong)right + borrow; var subtrahend = (ulong)right + borrow;
result = unchecked(left - (uint)subtrahend); result = unchecked(left - (uint)subtrahend);
scalarConditionCode = subtrahend > left; scalarConditionCode = left >= subtrahend;
break; break;
} }
case "SMinI32": case "SMinI32":
result = unchecked((uint)Math.Min((int)left, (int)right)); result = unchecked((uint)Math.Min((int)left, (int)right));
scalarConditionCode = (int)left < (int)right;
break; break;
case "SMinU32": case "SMinU32":
result = Math.Min(left, right); result = Math.Min(left, right);
scalarConditionCode = left < right;
break; break;
case "SMaxI32": case "SMaxI32":
result = unchecked((uint)Math.Max((int)left, (int)right)); result = unchecked((uint)Math.Max((int)left, (int)right));
scalarConditionCode = (int)left > (int)right;
break; break;
case "SMaxU32": case "SMaxU32":
result = Math.Max(left, right); result = Math.Max(left, right);
scalarConditionCode = left > right;
break; break;
case "SCselectB32": case "SCselectB32":
result = scalarConditionCode ? left : right; result = scalarConditionCode ? left : right;
@@ -945,7 +935,6 @@ internal static class Gen5ShaderScalarEvaluator
var offset = (int)right & 31; var offset = (int)right & 31;
var width = Math.Min(((int)right >> 16) & 0x7F, 32 - offset); var width = Math.Min(((int)right >> 16) & 0x7F, 32 - offset);
result = width == 0 ? 0 : left >> offset & (uint.MaxValue >> (32 - width)); result = width == 0 ? 0 : left >> offset & (uint.MaxValue >> (32 - width));
scalarConditionCode = result != 0;
break; break;
} }
case "SBfeI32": case "SBfeI32":
@@ -955,41 +944,23 @@ internal static class Gen5ShaderScalarEvaluator
result = width == 0 result = width == 0
? 0 ? 0
: unchecked((uint)(((int)(left << (32 - width - offset))) >> (32 - width))); : unchecked((uint)(((int)(left << (32 - width - offset))) >> (32 - width)));
scalarConditionCode = result != 0;
break; break;
} }
case "SAbsdiffI32": case "SAbsdiffI32":
result = unchecked((uint)Math.Abs((long)(int)left - (int)right)); result = unchecked((uint)Math.Abs((long)(int)left - (int)right));
scalarConditionCode = result != 0;
break; break;
case "SLshl1AddU32": case "SLshl1AddU32":
{ result = (left << 1) + right;
var wide = ((ulong)left << 1) + right; break;
result = (uint)wide;
scalarConditionCode = wide > uint.MaxValue;
break;
}
case "SLshl2AddU32": case "SLshl2AddU32":
{ result = (left << 2) + right;
var wide = ((ulong)left << 2) + right; break;
result = (uint)wide;
scalarConditionCode = wide > uint.MaxValue;
break;
}
case "SLshl3AddU32": case "SLshl3AddU32":
{ result = (left << 3) + right;
var wide = ((ulong)left << 3) + right; break;
result = (uint)wide;
scalarConditionCode = wide > uint.MaxValue;
break;
}
case "SLshl4AddU32": case "SLshl4AddU32":
{ result = (left << 4) + right;
var wide = ((ulong)left << 4) + right; break;
result = (uint)wide;
scalarConditionCode = wide > uint.MaxValue;
break;
}
case "SPackLlB32B16": case "SPackLlB32B16":
result = (left & 0xFFFFu) | (right << 16); result = (left & 0xFFFFu) | (right << 16);
break; break;
@@ -1031,8 +1002,7 @@ internal static class Gen5ShaderScalarEvaluator
"SNandSaveexecB64" or "SNandSaveexecB64" or
"SNorSaveexecB64" or "SNorSaveexecB64" or
"SXnorSaveexecB64" or "SXnorSaveexecB64" or
"SAndn1SaveexecB64" or "SAndn1SaveexecB64"))
"SOrn1SaveexecB64"))
{ {
return false; return false;
} }
@@ -1060,12 +1030,11 @@ internal static class Gen5ShaderScalarEvaluator
"SAndSaveexecB64" => oldExec & source, "SAndSaveexecB64" => oldExec & source,
"SOrSaveexecB64" => oldExec | source, "SOrSaveexecB64" => oldExec | source,
"SXorSaveexecB64" => oldExec ^ source, "SXorSaveexecB64" => oldExec ^ source,
"SAndn1SaveexecB64" => ~source & oldExec, "SAndn1SaveexecB64" => ~oldExec & source,
"SAndn2SaveexecB64" => source & ~oldExec, "SAndn2SaveexecB64" => oldExec & ~source,
"SOrn1SaveexecB64" => ~source | oldExec, "SOrn2SaveexecB64" => oldExec | ~source,
"SOrn2SaveexecB64" => source | ~oldExec, "SNandSaveexecB64" => ~(oldExec & source),
"SNandSaveexecB64" => ~(source & oldExec), "SNorSaveexecB64" => ~(oldExec | source),
"SNorSaveexecB64" => ~(source | oldExec),
_ => ~(oldExec ^ source), _ => ~(oldExec ^ source),
}; };
@@ -1135,12 +1104,6 @@ internal static class Gen5ShaderScalarEvaluator
private static ulong MaskWaveValue(ulong value) => value & RdnaWaveMask; 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( private static bool TryExecuteScalarCompare(
Gen5ShaderInstruction instruction, Gen5ShaderInstruction instruction,
uint[] registers, uint[] registers,
+5 -50
View File
@@ -4,7 +4,6 @@
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;
@@ -14,14 +13,6 @@ 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 =
[ [
@@ -126,51 +117,16 @@ internal static class Gen5ShaderTranslator
uint userDataScalarRegisterBase = 0) uint userDataScalarRegisterBase = 0)
{ {
state = default!; state = default!;
error = string.Empty; if (!TryDecodeProgram(ctx, shaderAddress, out var program, out error))
var cache = _decodeCaches.GetValue(ctx.Memory, static _ => new ShaderDecodeCache());
Gen5ShaderProgram? program;
lock (cache.Gate)
{ {
cache.Programs.TryGetValue(shaderAddress, out program); return false;
}
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))
{ {
var metadataCached = false; metadata = decodedMetadata;
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)];
@@ -496,7 +452,6 @@ internal static class Gen5ShaderTranslator
0x2A => "SNorSaveexecB64", 0x2A => "SNorSaveexecB64",
0x2B => "SXnorSaveexecB64", 0x2B => "SXnorSaveexecB64",
0x37 => "SAndn1SaveexecB64", 0x37 => "SAndn1SaveexecB64",
0x38 => "SOrn1SaveexecB64",
_ => string.Empty, _ => string.Empty,
}; };
+21 -239
View File
@@ -649,20 +649,6 @@ internal static partial class Gen5SpirvTranslator
width); width);
break; 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": case "VCvtPkrtzF16F32":
{ {
var first = TruncateFloat32ForPack(GetFloatSource(instruction, 0)); var first = TruncateFloat32ForPack(GetFloatSource(instruction, 0));
@@ -975,16 +961,6 @@ internal static partial class Gen5SpirvTranslator
StoreS(destination, result); StoreS(destination, result);
Store(_scc, IsNotZero(result)); Store(_scc, IsNotZero(result));
return true; 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: default:
{ {
if (instruction.Sources.Count < 2) if (instruction.Sources.Count < 2)
@@ -1011,14 +987,13 @@ internal static partial class Gen5SpirvTranslator
left, left,
right); right);
Store(_scc, _module.AddInstruction( Store(_scc, _module.AddInstruction(
SpirvOp.UGreaterThan, SpirvOp.UGreaterThanEqual,
_boolType, _boolType,
right, left,
left)); right));
break; break;
case "SAddI32": case "SAddI32":
result = IAdd(left, right); result = IAdd(left, right);
Store(_scc, SignedAddOverflow(left, right, result));
break; break;
case "SSubI32": case "SSubI32":
result = _module.AddInstruction( result = _module.AddInstruction(
@@ -1026,7 +1001,6 @@ internal static partial class Gen5SpirvTranslator
_uintType, _uintType,
left, left,
right); right);
Store(_scc, SignedSubOverflow(left, right, result));
break; break;
case "SAddcU32": case "SAddcU32":
{ {
@@ -1063,8 +1037,8 @@ internal static partial class Gen5SpirvTranslator
SpirvOp.Select, SpirvOp.Select,
_uintType, _uintType,
Load(_boolType, _scc), Load(_boolType, _scc),
UInt(1), UInt(0),
UInt(0)); UInt(1));
var partial = _module.AddInstruction( var partial = _module.AddInstruction(
SpirvOp.ISub, SpirvOp.ISub,
_uintType, _uintType,
@@ -1075,31 +1049,23 @@ internal static partial class Gen5SpirvTranslator
_uintType, _uintType,
partial, partial,
borrow); borrow);
var firstBorrow = _module.AddInstruction( var firstNoBorrow = _module.AddInstruction(
SpirvOp.UGreaterThan, SpirvOp.UGreaterThanEqual,
_boolType, _boolType,
right, left,
left); right);
var secondBorrow = _module.AddInstruction( var secondNoBorrow = _module.AddInstruction(
SpirvOp.LogicalAnd, SpirvOp.UGreaterThanEqual,
_boolType, _boolType,
_module.AddInstruction( partial,
SpirvOp.IEqual, borrow);
_boolType,
borrow,
UInt(1)),
_module.AddInstruction(
SpirvOp.IEqual,
_boolType,
right,
left));
Store( Store(
_scc, _scc,
_module.AddInstruction( _module.AddInstruction(
SpirvOp.LogicalOr, SpirvOp.LogicalAnd,
_boolType, _boolType,
firstBorrow, firstNoBorrow,
secondBorrow)); secondNoBorrow));
break; break;
} }
case "SMulI32": case "SMulI32":
@@ -1111,7 +1077,6 @@ internal static partial class Gen5SpirvTranslator
break; break;
case "SAndB32": case "SAndB32":
result = BitwiseAnd(left, right); result = BitwiseAnd(left, right);
Store(_scc, IsNotZero(result));
break; break;
case "SOrB32": case "SOrB32":
result = _module.AddInstruction( result = _module.AddInstruction(
@@ -1119,7 +1084,6 @@ internal static partial class Gen5SpirvTranslator
_uintType, _uintType,
left, left,
right); right);
Store(_scc, IsNotZero(result));
break; break;
case "SXorB32": case "SXorB32":
result = _module.AddInstruction( result = _module.AddInstruction(
@@ -1127,64 +1091,22 @@ internal static partial class Gen5SpirvTranslator
_uintType, _uintType,
left, left,
right); right);
Store(_scc, IsNotZero(result));
break; break;
case "SAndn2B32": case "SAndn2B32":
result = BitwiseAnd( result = BitwiseAnd(
left, left,
_module.AddInstruction(SpirvOp.Not, _uintType, right)); _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; break;
case "SLshlB32": case "SLshlB32":
result = ShiftLeftLogical(left, right); result = ShiftLeftLogical(left, right);
Store(_scc, IsNotZero(result));
break; break;
case "SLshrB32": case "SLshrB32":
result = ShiftRightLogical( result = ShiftRightLogical(
left, left,
BitwiseAnd(right, UInt(31))); BitwiseAnd(right, UInt(31)));
Store(_scc, IsNotZero(result));
break; break;
case "SAshrI32": case "SAshrI32":
result = ShiftRightArithmetic(left, right); result = ShiftRightArithmetic(left, right);
Store(_scc, IsNotZero(result));
break; break;
case "SBfmB32": case "SBfmB32":
result = _module.AddInstruction( result = _module.AddInstruction(
@@ -1227,7 +1149,6 @@ internal static partial class Gen5SpirvTranslator
left, left,
offset, offset,
width); width);
Store(_scc, IsNotZero(result));
break; break;
} }
case "SCselectB32": case "SCselectB32":
@@ -1240,47 +1161,9 @@ internal static partial class Gen5SpirvTranslator
break; break;
case "SMinU32": case "SMinU32":
result = Ext(38, _uintType, left, right); 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; break;
case "SMaxU32": case "SMaxU32":
result = Ext(41, _uintType, left, right); 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; break;
case "SLshl1AddU32": case "SLshl1AddU32":
case "SLshl2AddU32": case "SLshl2AddU32":
@@ -1425,55 +1308,17 @@ internal static partial class Gen5SpirvTranslator
"SXorSaveexecB64" => _module.AddInstruction( "SXorSaveexecB64" => _module.AddInstruction(
SpirvOp.BitwiseXor, _ulongType, oldExec, left), SpirvOp.BitwiseXor, _ulongType, oldExec, left),
"SAndn2SaveexecB64" => _module.AddInstruction( "SAndn2SaveexecB64" => _module.AddInstruction(
SpirvOp.BitwiseAnd, SpirvOp.BitwiseAnd, _ulongType, oldExec, notLeft),
_ulongType,
left,
_module.AddInstruction(
SpirvOp.Not,
_ulongType,
oldExec)),
"SAndn1SaveexecB64" => _module.AddInstruction( "SAndn1SaveexecB64" => _module.AddInstruction(
SpirvOp.BitwiseAnd, SpirvOp.BitwiseAnd,
_ulongType, _ulongType,
notLeft,
oldExec),
"SOrn1SaveexecB64" => _module.AddInstruction(
SpirvOp.BitwiseOr,
_ulongType,
notLeft,
oldExec),
"SOrn2SaveexecB64" => _module.AddInstruction(
SpirvOp.BitwiseOr,
_ulongType,
left,
_module.AddInstruction( _module.AddInstruction(
SpirvOp.Not, SpirvOp.Not,
_ulongType, _ulongType,
oldExec)), oldExec),
"SNandSaveexecB64" => _module.AddInstruction( left),
SpirvOp.Not, "SOrn2SaveexecB64" => _module.AddInstruction(
_ulongType, SpirvOp.BitwiseOr, _ulongType, oldExec, notLeft),
_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)),
_ => 0u, _ => 0u,
}; };
if (newExec == 0) if (newExec == 0)
@@ -1679,22 +1524,6 @@ internal static partial class Gen5SpirvTranslator
} }
StoreS64(destination, value); 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; return true;
} }
@@ -2281,53 +2110,6 @@ internal static partial class Gen5SpirvTranslator
value, value,
_module.Constant64(_ulongType, 0)); _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) private static bool TryDecodeInlineConstant(uint encoded, out uint value)
{ {
if (encoded == 125) if (encoded == 125)
@@ -1,92 +0,0 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
using System.Collections.Concurrent;
using System.Threading;
namespace SharpEmu.Libs.Audio;
public static class AudioOutExports
{
private static readonly ConcurrentDictionary<int, PortState> Ports = new();
private static int _nextPortHandle;
private sealed record PortState(int UserId, int Type, uint BufferLength, uint Frequency, int Format);
[SysAbiExport(
Nid = "JfEPXVxhFqA",
ExportName = "sceAudioOutInit",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceAudioOut")]
public static int AudioOutInit(CpuContext ctx) => SetReturn(ctx, 0);
[SysAbiExport(
Nid = "ekNvsT22rsY",
ExportName = "sceAudioOutOpen",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceAudioOut")]
public static int AudioOutOpen(CpuContext ctx)
{
var userId = unchecked((int)ctx[CpuRegister.Rdi]);
var type = unchecked((int)ctx[CpuRegister.Rsi]);
var bufferLength = unchecked((uint)ctx[CpuRegister.Rcx]);
var frequency = unchecked((uint)ctx[CpuRegister.R8]);
var format = unchecked((int)ctx[CpuRegister.R9]);
if (bufferLength == 0 || frequency == 0)
{
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
var handle = Interlocked.Increment(ref _nextPortHandle);
Ports[handle] = new PortState(userId, type, bufferLength, frequency, format);
return SetReturn(ctx, handle);
}
[SysAbiExport(
Nid = "s1--uE9mBFw",
ExportName = "sceAudioOutClose",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceAudioOut")]
public static int AudioOutClose(CpuContext ctx)
{
var handle = unchecked((int)ctx[CpuRegister.Rdi]);
return SetReturn(
ctx,
Ports.TryRemove(handle, out _)
? 0
: (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
[SysAbiExport(
Nid = "QOQtbeDqsT4",
ExportName = "sceAudioOutOutput",
Target = Generation.Gen5,
LibraryName = "libSceAudioOut")]
public static int AudioOutOutput(CpuContext ctx)
{
ctx[CpuRegister.Rax] = 0;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport(
Nid = "b+uAV89IlxE",
ExportName = "sceAudioOutSetVolume",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceAudioOut")]
public static int AudioOutSetVolume(CpuContext ctx)
{
var handle = unchecked((int)ctx[CpuRegister.Rdi]);
return SetReturn(
ctx,
Ports.ContainsKey(handle)
? 0
: (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
private static int SetReturn(CpuContext ctx, int result)
{
ctx[CpuRegister.Rax] = unchecked((ulong)result);
return result;
}
}
+4 -69
View File
@@ -4,7 +4,6 @@
using SharpEmu.HLE; using SharpEmu.HLE;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices;
namespace SharpEmu.Libs.Pad; namespace SharpEmu.Libs.Pad;
@@ -59,7 +58,6 @@ public static class PadExports
return SetReturn(ctx, OrbisPadErrorDeviceNotConnected); 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); return SetReturn(ctx, PrimaryPadHandle);
} }
@@ -164,19 +162,10 @@ public static class PadExports
{ {
Span<byte> data = stackalloc byte[PadDataSize]; Span<byte> data = stackalloc byte[PadDataSize];
data.Clear(); data.Clear();
var acceptsKeyboardInput = IsEmulatorWindowFocused(); data[0x04] = 128;
var buttons = acceptsKeyboardInput ? ReadKeyboardButtons() : 0; data[0x05] = 128;
BinaryPrimitives.WriteUInt32LittleEndian(data[0x00..], buttons); data[0x06] = 128;
var leftX = acceptsKeyboardInput ? ReadAnalogStick(IsKeyDown(0x41), IsKeyDown(0x44)) : (byte)128; data[0x07] = 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;
BinaryPrimitives.WriteSingleLittleEndian(data[0x18..], 1.0f); BinaryPrimitives.WriteSingleLittleEndian(data[0x18..], 1.0f);
data[0x4C] = 1; data[0x4C] = 1;
var timestampTicks = Stopwatch.GetTimestamp(); var timestampTicks = Stopwatch.GetTimestamp();
@@ -196,58 +185,4 @@ public static class PadExports
ctx[CpuRegister.Rax] = unchecked((ulong)result); ctx[CpuRegister.Rax] = unchecked((ulong)result);
return 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 configured = Environment.GetEnvironmentVariable("SHARPEMU_SAVEDATA_DIR");
var root = string.IsNullOrWhiteSpace(configured) var root = string.IsNullOrWhiteSpace(configured)
? Path.Combine(AppContext.BaseDirectory, "user", "savedata") ? Path.Combine(Environment.CurrentDirectory, "user", "savedata")
: configured; : configured;
return Path.GetFullPath(root); return Path.GetFullPath(root);
} }
@@ -4,7 +4,6 @@
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;
@@ -49,13 +48,6 @@ 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)
{ {
@@ -820,46 +812,9 @@ 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)
+111 -349
View File
@@ -8,9 +8,7 @@ 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;
@@ -599,24 +597,6 @@ 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,
@@ -887,50 +867,14 @@ 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;
@@ -2034,16 +1978,81 @@ 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 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[ var poolSizes = new DescriptorPoolSize[
(sampledImageCount == 0 ? 0 : 1) + (sampledImageCount == 0 ? 0 : 1) +
@@ -2193,21 +2202,6 @@ 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");
@@ -2359,8 +2353,6 @@ 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,
@@ -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)] [MethodImpl(MethodImplOptions.NoInlining)]
private void CreateComputePipeline( private void CreateComputePipeline(
TranslatedDrawResources resources, TranslatedDrawResources resources,
@@ -2910,7 +2779,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");
var resource = new TextureResource return new TextureResource
{ {
Address = texture.Address, Address = texture.Address,
StagingBuffer = stagingBuffer, StagingBuffer = stagingBuffer,
@@ -2926,38 +2795,6 @@ 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(
@@ -3058,11 +2895,6 @@ 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);
@@ -3089,7 +2921,6 @@ 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;
} }
@@ -3204,31 +3035,11 @@ 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 capacity = BitOperations.RoundUpToPowerOf2(size); var buffer = CreateBuffer(
var key = new HostBufferPoolKey(usage, capacity); size,
if (!_hostBufferPool.TryGetValue(key, out var available)) usage,
{ MemoryPropertyFlags.HostVisibleBit | MemoryPropertyFlags.HostCoherentBit,
available = new Stack<HostBufferAllocation>(); out memory);
_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
@@ -3247,28 +3058,7 @@ internal static unsafe class VulkanVideoPresenter
_vk.UnmapMemory(_device, memory); _vk.UnmapMemory(_device, memory);
} }
return allocation.Buffer; return 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) =>
@@ -3818,7 +3608,6 @@ 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" +
@@ -4007,7 +3796,6 @@ 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);
@@ -4710,7 +4498,6 @@ 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();
@@ -4718,7 +4505,6 @@ 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);
} }
@@ -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) private bool ShouldTraceGuestImageContents(GuestImageResource image)
{ {
if (image.Address == 0) if (image.Address == 0)
@@ -5483,6 +5248,11 @@ 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)
{ {
@@ -5497,7 +5267,15 @@ internal static unsafe class VulkanVideoPresenter
continue; 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) foreach (var vertexBuffer in resources.VertexBuffers)
@@ -5507,10 +5285,26 @@ internal static unsafe class VulkanVideoPresenter
continue; 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) if (!resources.PipelineCached && resources.Pipeline.Handle != 0)
{ {
@@ -5522,14 +5316,12 @@ internal static unsafe class VulkanVideoPresenter
_vk.DestroyDescriptorPool(_device, resources.DescriptorPool, null); _vk.DestroyDescriptorPool(_device, resources.DescriptorPool, null);
} }
if (!resources.DescriptorLayoutCached && if (resources.PipelineLayout.Handle != 0)
resources.PipelineLayout.Handle != 0)
{ {
_vk.DestroyPipelineLayout(_device, resources.PipelineLayout, null); _vk.DestroyPipelineLayout(_device, resources.PipelineLayout, null);
} }
if (!resources.DescriptorLayoutCached && if (resources.DescriptorSetLayout.Handle != 0)
resources.DescriptorSetLayout.Handle != 0)
{ {
_vk.DestroyDescriptorSetLayout(_device, resources.DescriptorSetLayout, null); _vk.DestroyDescriptorSetLayout(_device, resources.DescriptorSetLayout, null);
} }
@@ -5964,36 +5756,6 @@ 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);