Compare commits

..

2 Commits

41 changed files with 700 additions and 4987 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/
+1 -1
View File
@@ -2,6 +2,7 @@
Copyright (C) 2026 SharpEmu Emulator Project Copyright (C) 2026 SharpEmu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later SPDX-License-Identifier: GPL-2.0-or-later
--> -->
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
@@ -9,7 +10,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
<ItemGroup> <ItemGroup>
<PackageVersion Include="Iced" Version="1.21.0" /> <PackageVersion Include="Iced" Version="1.21.0" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" /> <PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.23.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" /> <PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" />
<PackageVersion Include="Silk.NET.Windowing" Version="2.23.0" /> <PackageVersion Include="Silk.NET.Windowing" Version="2.23.0" />
</ItemGroup> </ItemGroup>
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>
-11
View File
@@ -81,7 +81,6 @@
"dependencies": { "dependencies": {
"SharpEmu.HLE": "[1.0.0, )", "SharpEmu.HLE": "[1.0.0, )",
"Silk.NET.Vulkan": "[2.23.0, )", "Silk.NET.Vulkan": "[2.23.0, )",
"Silk.NET.Vulkan.Extensions.EXT": "[2.23.0, )",
"Silk.NET.Vulkan.Extensions.KHR": "[2.23.0, )", "Silk.NET.Vulkan.Extensions.KHR": "[2.23.0, )",
"Silk.NET.Windowing": "[2.23.0, )" "Silk.NET.Windowing": "[2.23.0, )"
} }
@@ -104,16 +103,6 @@
"Silk.NET.Core": "2.23.0" "Silk.NET.Core": "2.23.0"
} }
}, },
"Silk.NET.Vulkan.Extensions.EXT": {
"type": "CentralTransitive",
"requested": "[2.23.0, )",
"resolved": "2.23.0",
"contentHash": "+Oth189ksRiL6HvGCwIdnsYHawqrbO8y49u1H61z3wsfcHhQZeVDYe/wF5LD7fk3NcdgDvwFD3mLm1QWhdZySw==",
"dependencies": {
"Silk.NET.Core": "2.23.0",
"Silk.NET.Vulkan": "2.23.0"
}
},
"Silk.NET.Vulkan.Extensions.KHR": { "Silk.NET.Vulkan.Extensions.KHR": {
"type": "CentralTransitive", "type": "CentralTransitive",
"requested": "[2.23.0, )", "requested": "[2.23.0, )",
@@ -338,15 +338,10 @@ public sealed partial class DirectExecutionBackend
{ {
orbisGen2Result = DispatchBootstrapBridge(); orbisGen2Result = DispatchBootstrapBridge();
} }
else if (string.Equals(importStubEntry.Nid, RuntimeStubNids.KernelDynlibDlsym, StringComparison.Ordinal) || else if (string.Equals(importStubEntry.Nid, RuntimeStubNids.KernelDynlibDlsym, StringComparison.Ordinal))
string.Equals(importStubEntry.Nid, "LwG8g3niqwA", StringComparison.Ordinal))
{ {
orbisGen2Result = DispatchKernelDynlibDlsym(); orbisGen2Result = DispatchKernelDynlibDlsym();
} }
else if (string.Equals(importStubEntry.Nid, "r8mvOaWdi28", StringComparison.Ordinal))
{
orbisGen2Result = DispatchIl2CppApiLookupSymbol();
}
else if (importStubEntry.Export is { } cachedExport && else if (importStubEntry.Export is { } cachedExport &&
(cachedExport.Target & cpuContext.TargetGeneration) != 0) (cachedExport.Target & cpuContext.TargetGeneration) != 0)
{ {
@@ -728,7 +723,6 @@ public sealed partial class DirectExecutionBackend
"Vo5V8KAwCmk" or // sceSystemServiceHideSplashScreen "Vo5V8KAwCmk" or // sceSystemServiceHideSplashScreen
"TywrFKCoLGY" or // sceSaveDataInitialize3 "TywrFKCoLGY" or // sceSaveDataInitialize3
"dyIhnXq-0SM" or // sceSaveDataDirNameSearch "dyIhnXq-0SM" or // sceSaveDataDirNameSearch
"ZP4e7rlzOUk" or // sceSaveDataMount3
"ERKzksauAJA" or // sceSaveDataDialogGetStatus "ERKzksauAJA" or // sceSaveDataDialogGetStatus
"KK3Bdg1RWK0" or // sceSaveDataDialogUpdateStatus "KK3Bdg1RWK0" or // sceSaveDataDialogUpdateStatus
"en7gNVnh878" or // sceSaveDataDialogIsReadyToDisplay "en7gNVnh878" or // sceSaveDataDialogIsReadyToDisplay
@@ -1185,11 +1179,8 @@ public sealed partial class DirectExecutionBackend
cpuContext[CpuRegister.Rax] = 18446744073709551615uL; cpuContext[CpuRegister.Rax] = 18446744073709551615uL;
return OrbisGen2Result.ORBIS_GEN2_OK; return OrbisGen2Result.ORBIS_GEN2_OK;
} }
if (!TryResolveRuntimeSymbolAddress(symbolName, out var resolvedAddress) && if (!TryResolveRuntimeSymbolAddress(symbolName, out var resolvedAddress))
!TryResolveRuntimeSymbolAlias(symbolName, out resolvedAddress))
{ {
Console.Error.WriteLine(
$"[LOADER][WARN] sceKernelDlsym failed: handle=0x{cpuContext[CpuRegister.Rdi]:X} symbol='{symbolName}'");
cpuContext[CpuRegister.Rax] = 18446744073709551615uL; cpuContext[CpuRegister.Rax] = 18446744073709551615uL;
return OrbisGen2Result.ORBIS_GEN2_OK; return OrbisGen2Result.ORBIS_GEN2_OK;
} }
@@ -1202,62 +1193,6 @@ public sealed partial class DirectExecutionBackend
return OrbisGen2Result.ORBIS_GEN2_OK; return OrbisGen2Result.ORBIS_GEN2_OK;
} }
private bool TryResolveRuntimeSymbolAlias(string symbolName, out ulong address)
{
address = 0;
var alias = symbolName switch
{
"scriptingGetMem" => "malloc",
"scriptingFreeMem" => "free",
"scriptingRealloc" => "realloc",
"scriptingCalloc" => "calloc",
_ => null,
};
return alias != null && TryResolveRuntimeSymbolAddress(alias, out address);
}
private OrbisGen2Result DispatchIl2CppApiLookupSymbol()
{
var cpuContext = ActiveCpuContext;
if (cpuContext == null)
{
return OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
var symbolNameAddress = cpuContext[CpuRegister.Rdi];
var outputAddress = cpuContext[CpuRegister.Rsi];
if (!TryReadAsciiZ(symbolNameAddress, 512, out var symbolName) ||
outputAddress == 0 ||
!TryResolveIl2CppApiAddress(symbolName, out var resolvedAddress) ||
!TryWriteUInt64Compat(outputAddress, resolvedAddress))
{
Console.Error.WriteLine(
$"[LOADER][WARN] il2cpp_api_lookup_symbol failed: name='{symbolName}' out=0x{outputAddress:X16}");
if (outputAddress != 0)
{
_ = TryWriteUInt64Compat(outputAddress, 0);
}
cpuContext[CpuRegister.Rax] = ulong.MaxValue;
return OrbisGen2Result.ORBIS_GEN2_OK;
}
cpuContext[CpuRegister.Rax] = 0;
return OrbisGen2Result.ORBIS_GEN2_OK;
}
private bool TryResolveIl2CppApiAddress(string symbolName, out ulong address)
{
if (TryResolveRuntimeSymbolAddress(symbolName, out address))
{
return true;
}
return Aerolib.Instance.TryGetByExportName(symbolName, out var symbol) &&
TryResolveRuntimeSymbolAddress(symbol.Nid, out address);
}
private OrbisGen2Result DispatchBootstrapBridge() private OrbisGen2Result DispatchBootstrapBridge()
{ {
var cpuContext = ActiveCpuContext; var cpuContext = ActiveCpuContext;
@@ -355,8 +355,6 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
public GuestThreadRunState State { get; set; } public GuestThreadRunState State { get; set; }
public ulong ExitValue { get; set; }
public string? BlockReason { get; set; } public string? BlockReason { get; set; }
public bool HasBlockedContinuation { get; set; } public bool HasBlockedContinuation { get; set; }
@@ -1268,7 +1266,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
libraryName.IndexOf("Kernel", StringComparison.OrdinalIgnoreCase) >= 0; libraryName.IndexOf("Kernel", StringComparison.OrdinalIgnoreCase) >= 0;
} }
private bool PreferLleForLibcExport(string exportName) private static bool PreferLleForLibcExport(string exportName)
{ {
if (string.IsNullOrWhiteSpace(exportName)) if (string.IsNullOrWhiteSpace(exportName))
{ {
@@ -1289,10 +1287,6 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
{ {
return true; return true;
} }
if (IsLibcAllocatorExport(exportName))
{
return CanUseLleLibcAllocatorFamily();
}
if (string.Equals(value, "0", StringComparison.Ordinal)) if (string.Equals(value, "0", StringComparison.Ordinal))
{ {
return true; return true;
@@ -1304,51 +1298,6 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
return IsSafeLleLibcExport(exportName); return IsSafeLleLibcExport(exportName);
} }
private bool CanUseLleLibcAllocatorFamily()
{
return HasUsableLleLibcExport("gQX+4GDQjpM", "malloc") &&
HasUsableLleLibcExport("tIhsqj0qsFE", "free") &&
HasUsableLleLibcExport("2X5agFjKxMc", "calloc") &&
HasUsableLleLibcExport("Y7aJ1uydPMo", "realloc") &&
HasUsableLleLibcExport("Ujf3KzMvRmI", "memalign") &&
HasUsableLleLibcExport("2Btkg8k24Zg", "aligned_alloc") &&
HasUsableLleLibcExport("cVSk9y8URbc", "posix_memalign");
}
private bool HasUsableLleLibcExport(string nid, string exportName)
{
if (TryResolveRuntimeSymbolAddress(nid, out var address) && IsDirectImportTargetUsable(address))
{
return true;
}
foreach (var candidate in EnumerateRuntimeSymbolCandidates(exportName))
{
if (TryResolveRuntimeSymbolAddress(candidate, out address) && IsDirectImportTargetUsable(address))
{
return true;
}
}
return false;
}
private static bool IsLibcAllocatorExport(string exportName)
{
return exportName switch
{
"malloc" or
"free" or
"calloc" or
"realloc" or
"memalign" or
"aligned_alloc" or
"posix_memalign" or
"malloc_usable_size" => true,
_ => false,
};
}
private static bool IsSafeLleLibcExport(string exportName) private static bool IsSafeLleLibcExport(string exportName)
{ {
return exportName switch return exportName switch
@@ -2457,69 +2406,6 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
public bool SupportsGuestContextTransfer => true; public bool SupportsGuestContextTransfer => true;
public bool TryJoinThread(
CpuContext callerContext,
ulong threadHandle,
out ulong returnValue,
out string? error)
{
returnValue = 0;
error = null;
if (threadHandle == 0)
{
error = "thread handle is zero";
return false;
}
if (threadHandle == GuestThreadExecution.CurrentGuestThreadHandle)
{
error = "thread cannot join itself";
return false;
}
while (!ActiveForcedGuestExit)
{
Thread? hostThread;
lock (_guestThreadGate)
{
if (!_guestThreads.TryGetValue(threadHandle, out var thread))
{
error = $"unknown guest thread 0x{threadHandle:X16}";
return false;
}
if (thread.State == GuestThreadRunState.Exited)
{
returnValue = thread.ExitValue;
return true;
}
if (thread.State == GuestThreadRunState.Faulted)
{
error =
$"guest thread 0x{threadHandle:X16} faulted: " +
(thread.BlockReason ?? "unknown error");
return false;
}
hostThread = thread.HostThread;
}
if (hostThread is not null &&
!ReferenceEquals(hostThread, Thread.CurrentThread))
{
hostThread.Join(1);
}
else
{
Thread.Sleep(1);
}
}
error = "guest execution stopped while joining thread";
return false;
}
public void Pump(CpuContext callerContext, string reason) public void Pump(CpuContext callerContext, string reason)
{ {
_ = callerContext; _ = callerContext;
@@ -3368,7 +3254,6 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
switch (exitReason) switch (exitReason)
{ {
case GuestNativeCallExitReason.Returned: case GuestNativeCallExitReason.Returned:
thread.ExitValue = thread.Context[CpuRegister.Rax];
thread.State = GuestThreadRunState.Exited; thread.State = GuestThreadRunState.Exited;
break; break;
case GuestNativeCallExitReason.Blocked: case GuestNativeCallExitReason.Blocked:
+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)
+2 -4
View File
@@ -543,7 +543,6 @@ public sealed class SharpEmuRuntime : ISharpEmuRuntime
{ {
Path.Combine(ebootDirectory, "sce_module"), Path.Combine(ebootDirectory, "sce_module"),
Path.Combine(ebootDirectory, "sce_modules"), Path.Combine(ebootDirectory, "sce_modules"),
Path.Combine(ebootDirectory, "Media", "Modules"),
} }
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.Where(Directory.Exists) .Where(Directory.Exists)
@@ -555,9 +554,7 @@ public sealed class SharpEmuRuntime : ISharpEmuRuntime
} }
var allModulePaths = moduleDirectories var allModulePaths = moduleDirectories
.SelectMany(directory => Directory .SelectMany(Directory.EnumerateFiles)
.EnumerateFiles(directory)
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase))
.Where(path => .Where(path =>
{ {
var extension = Path.GetExtension(path); var extension = Path.GetExtension(path);
@@ -565,6 +562,7 @@ public sealed class SharpEmuRuntime : ISharpEmuRuntime
string.Equals(extension, ".sprx", StringComparison.OrdinalIgnoreCase); string.Equals(extension, ".sprx", StringComparison.OrdinalIgnoreCase);
}) })
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
.ToArray(); .ToArray();
var modulePaths = allModulePaths var modulePaths = allModulePaths
-11
View File
@@ -72,7 +72,6 @@
"dependencies": { "dependencies": {
"SharpEmu.HLE": "[1.0.0, )", "SharpEmu.HLE": "[1.0.0, )",
"Silk.NET.Vulkan": "[2.23.0, )", "Silk.NET.Vulkan": "[2.23.0, )",
"Silk.NET.Vulkan.Extensions.EXT": "[2.23.0, )",
"Silk.NET.Vulkan.Extensions.KHR": "[2.23.0, )", "Silk.NET.Vulkan.Extensions.KHR": "[2.23.0, )",
"Silk.NET.Windowing": "[2.23.0, )" "Silk.NET.Windowing": "[2.23.0, )"
} }
@@ -89,16 +88,6 @@
"Silk.NET.Core": "2.23.0" "Silk.NET.Core": "2.23.0"
} }
}, },
"Silk.NET.Vulkan.Extensions.EXT": {
"type": "CentralTransitive",
"requested": "[2.23.0, )",
"resolved": "2.23.0",
"contentHash": "+Oth189ksRiL6HvGCwIdnsYHawqrbO8y49u1H61z3wsfcHhQZeVDYe/wF5LD7fk3NcdgDvwFD3mLm1QWhdZySw==",
"dependencies": {
"Silk.NET.Core": "2.23.0",
"Silk.NET.Vulkan": "2.23.0"
}
},
"Silk.NET.Vulkan.Extensions.KHR": { "Silk.NET.Vulkan.Extensions.KHR": {
"type": "CentralTransitive", "type": "CentralTransitive",
"requested": "[2.23.0, )", "requested": "[2.23.0, )",
-6
View File
@@ -29,12 +29,6 @@ public interface IGuestThreadScheduler
bool TryStartThread(CpuContext creatorContext, GuestThreadStartRequest request, out string? error); bool TryStartThread(CpuContext creatorContext, GuestThreadStartRequest request, out string? error);
bool TryJoinThread(
CpuContext callerContext,
ulong threadHandle,
out ulong returnValue,
out string? error);
void Pump(CpuContext callerContext, string reason); void Pump(CpuContext callerContext, string reason);
int WakeBlockedThreads(string wakeKey, int maxCount = int.MaxValue); int WakeBlockedThreads(string wakeKey, int maxCount = int.MaxValue);
+34 -637
View File
@@ -68,35 +68,13 @@ public static class AgcExports
private const uint ComputeNumThreadZ = 0x209; private const uint ComputeNumThreadZ = 0x209;
private const uint SpiPsInputCntl0 = 0x191; private const uint SpiPsInputCntl0 = 0x191;
private const uint VgtPrimitiveType = 0x242; private const uint VgtPrimitiveType = 0x242;
private const uint PaScScreenScissorTl = 0x0C;
private const uint PaScScreenScissorBr = 0x0D;
private const uint CbTargetMask = 0x8E; private const uint CbTargetMask = 0x8E;
private const uint PaScWindowOffset = 0x80;
private const uint PaScWindowScissorTl = 0x81;
private const uint PaScWindowScissorBr = 0x82;
private const uint PaScGenericScissorTl = 0x90;
private const uint PaScGenericScissorBr = 0x91;
private const uint PaScVportScissor0Tl = 0x94;
private const uint PaScVportScissor0Br = 0x95;
private const uint PaClVportXScale = 0x10F;
private const uint PaClVportXOffset = 0x110;
private const uint PaClVportYScale = 0x111;
private const uint PaClVportYOffset = 0x112;
private const uint PaScVportZMin0 = 0xB4;
private const uint PaScVportZMax0 = 0xB5;
private const uint CbBlendRed = 0x105;
private const uint CbBlendGreen = 0x106;
private const uint CbBlendBlue = 0x107;
private const uint CbBlendAlpha = 0x108;
private const uint CbColorControl = 0x202;
private const uint CbColor0Base = 0x318; private const uint CbColor0Base = 0x318;
private const uint CbColorRegisterStride = 15; private const uint CbColorRegisterStride = 15;
private const uint CbColor0Info = 0x31C; private const uint CbColor0Info = 0x31C;
private const uint CbColor0BaseExt = 0x390; private const uint CbColor0BaseExt = 0x390;
private const uint CbColor0Attrib2 = 0x3B0; private const uint CbColor0Attrib2 = 0x3B0;
private const uint CbColor0Attrib3 = 0x3B8; private const uint CbColor0Attrib3 = 0x3B8;
private const uint CbBlend0Control = 0x1E0;
private const uint PaScModeCntl0 = 0x292;
private const int ColorTargetCount = 8; private const int ColorTargetCount = 8;
private const uint PsTextureUserDataRegister = 0xC; private const uint PsTextureUserDataRegister = 0xC;
private const uint VsUserDataRegister = 0x4C; private const uint VsUserDataRegister = 0x4C;
@@ -142,7 +120,7 @@ public static class AgcExports
private static readonly HashSet<uint> _tracedDcbSizes = new(); private static readonly HashSet<uint> _tracedDcbSizes = new();
private static readonly HashSet<(ulong Es, ulong Ps, GuestDrawKind Kind)> _tracedShaderTranslations = new(); private static readonly HashSet<(ulong Es, ulong Ps, GuestDrawKind Kind)> _tracedShaderTranslations = new();
private static readonly HashSet<(ulong Es, ulong Ps)> _tracedShaderDecodePairs = new(); private static readonly HashSet<(ulong Es, ulong Ps)> _tracedShaderDecodePairs = new();
private static readonly HashSet<(ulong Es, ulong Ps, ulong Target, ulong Texture, uint VertexCount)> _tracedShaderDraws = new(); private static readonly HashSet<(ulong Es, ulong Ps, ulong Target)> _tracedShaderDraws = new();
private static readonly HashSet<(ulong Ps, string Error)> _tracedShaderFailures = new(); private static readonly HashSet<(ulong Ps, string Error)> _tracedShaderFailures = new();
private static readonly HashSet<(int Handle, int Index, ulong Address, string Path)> _tracedDisplayBuffers = new(); private static readonly HashSet<(int Handle, int Index, ulong Address, string Path)> _tracedDisplayBuffers = new();
private static readonly HashSet<ulong> _tracedComputeShaders = new(); private static readonly HashSet<ulong> _tracedComputeShaders = new();
@@ -155,16 +133,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;
@@ -183,25 +151,9 @@ public static class AgcExports
private static readonly RegisterDefaultGroup[] PrimaryRegisterDefaults = private static readonly RegisterDefaultGroup[] PrimaryRegisterDefaults =
[ [
new(0, 0, 0xE24F806D, [new(CbColorControl, 0x00CC0010)]),
new(0, 3, 0x0BC65DA4, [new(0x08F, 0)]), new(0, 3, 0x0BC65DA4, [new(0x08F, 0)]),
new(0, 4, 0x9E5AD592, [new(0x08E, 0)]), new(0, 4, 0x9E5AD592, [new(0x08E, 0)]),
new(0, 12, 0x6DE4C312, [new(0x203, 0)]), new(0, 12, 0x6DE4C312, [new(0x203, 0)]),
new(0, 28, 0x1EB8D73A, [new(PaScModeCntl0, 0x00000002)]),
new(0, 31, 0xA20EFC70, [new(PaScWindowOffset, 0)]),
new(0, 58, 0x43FBD769,
[
new(CbBlendRed, 0),
new(CbBlendBlue, 0),
new(CbBlendGreen, 0),
new(CbBlendAlpha, 0),
]),
new(0, 59, 0xEF550356, [new(CbBlend0Control, 0x20010001)]),
new(0, 67, 0x918106BB,
[
new(PaScGenericScissorTl, 0x80000000),
new(PaScGenericScissorBr, 0x40004000),
]),
new(0, 72, 0x38E92C91, new(0, 72, 0x38E92C91,
[ [
new(0x318, 0), new(0x318, 0),
@@ -236,11 +188,6 @@ public static class AgcExports
new(0x0B4, 0), new(0x0B4, 0),
new(0x0B5, 0), new(0x0B5, 0),
]), ]),
new(0, 77, 0x078D7060,
[
new(PaScWindowScissorTl, 0x80000000),
new(PaScWindowScissorBr, 0x40004000),
]),
new(1, 13, 0xC918DF3E, [new(0x20C, 0), new(0x20D, 0)]), new(1, 13, 0xC918DF3E, [new(0x20C, 0), new(0x20D, 0)]),
new(1, 14, 0xC9751C9C, [new(0x0C8, 0), new(0x0C9, 0)]), new(1, 14, 0xC9751C9C, [new(0x0C8, 0), new(0x0C9, 0)]),
new(1, 18, 0xC9E01B31, [new(0x008, 0), new(0x009, 0)]), new(1, 18, 0xC9E01B31, [new(0x008, 0), new(0x009, 0)]),
@@ -284,9 +231,7 @@ public static class AgcExports
uint TileMode, uint TileMode,
uint Type, uint Type,
uint BaseLevel, uint BaseLevel,
uint LastLevel, uint LastLevel)
uint Pitch,
uint DstSelect)
{ {
public uint MipLevels public uint MipLevels
{ {
@@ -329,15 +274,12 @@ public static class AgcExports
VulkanGuestIndexBuffer? IndexBuffer, VulkanGuestIndexBuffer? IndexBuffer,
IReadOnlyList<TranslatedImageBinding> Textures, IReadOnlyList<TranslatedImageBinding> Textures,
IReadOnlyList<Gen5GlobalMemoryBinding> GlobalMemoryBindings, IReadOnlyList<Gen5GlobalMemoryBinding> GlobalMemoryBindings,
IReadOnlyList<Gen5VertexInputBinding> VertexInputs, IReadOnlyList<RenderTargetDescriptor> RenderTargets);
IReadOnlyList<RenderTargetDescriptor> RenderTargets,
VulkanGuestRenderState RenderState);
private sealed record TranslatedImageBinding( private sealed record TranslatedImageBinding(
TextureDescriptor Descriptor, TextureDescriptor Descriptor,
bool IsStorage, bool IsStorage,
uint MipLevel, uint MipLevel);
IReadOnlyList<uint> SamplerDescriptor);
private readonly record struct RenderTargetWriter( private readonly record struct RenderTargetWriter(
ulong Sequence, ulong Sequence,
@@ -1204,51 +1146,6 @@ public static class AgcExports
return ReturnPointer(ctx, commandAddress); return ReturnPointer(ctx, commandAddress);
} }
[SysAbiExport(
Nid = "q88lQ+GP5Yk",
ExportName = "sceAgcDcbDrawIndex",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int DcbDrawIndex(CpuContext ctx)
{
var commandBufferAddress = ctx[CpuRegister.Rdi];
var indexCount = (uint)ctx[CpuRegister.Rsi];
var indexAddress = ctx[CpuRegister.Rdx];
var modifier = (uint)ctx[CpuRegister.Rcx];
if (commandBufferAddress == 0 || modifier != 0x4000_0000)
{
return ReturnPointer(ctx, 0);
}
if (!TryAllocateCommandDwords(ctx, commandBufferAddress, 5, out var baseCommand) ||
!TryWriteUInt32(ctx, baseCommand, Pm4(3, ItIndexBase, 0)) ||
!TryWriteUInt32(ctx, baseCommand + 4, (uint)indexAddress) ||
!TryWriteUInt32(ctx, baseCommand + 8, (uint)(indexAddress >> 32)) ||
!TryWriteUInt32(ctx, baseCommand + 12, Pm4(2, ItIndexBufferSize, 0)) ||
!TryWriteUInt32(ctx, baseCommand + 16, indexCount))
{
return ReturnPointer(ctx, 0);
}
if (!TryAllocateCommandDwords(ctx, commandBufferAddress, 5, out var drawCommand) ||
!TryWriteUInt32(ctx, drawCommand, Pm4(5, ItDrawIndex2, 0)) ||
!TryWriteUInt32(ctx, drawCommand + 4, indexCount) ||
!TryWriteUInt32(ctx, drawCommand + 8, 0) ||
!TryWriteUInt32(ctx, drawCommand + 12, 0) ||
!TryWriteUInt32(ctx, drawCommand + 16, 0))
{
return ReturnPointer(ctx, 0);
}
TraceAgc(
$"agc.dcb_draw_index buf=0x{commandBufferAddress:X16} " +
$"base=0x{baseCommand:X16} draw=0x{drawCommand:X16} " +
$"count={indexCount} index=0x{indexAddress:X16}");
return ReturnPointer(ctx, drawCommand);
}
[SysAbiExport( [SysAbiExport(
Nid = "Yw0jKSqop+E", Nid = "Yw0jKSqop+E",
ExportName = "sceAgcDcbDrawIndexAuto", ExportName = "sceAgcDcbDrawIndexAuto",
@@ -2051,88 +1948,6 @@ public static class AgcExports
return (int)OrbisGen2Result.ORBIS_GEN2_OK; return (int)OrbisGen2Result.ORBIS_GEN2_OK;
} }
[SysAbiExport(
Nid = "uJziRsODk1c",
ExportName = "sceAgcDriverGetResourceRegistrationMaxNameLength",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int DriverGetResourceRegistrationMaxNameLength(CpuContext ctx)
{
var outAddress = ctx[CpuRegister.Rdi];
if (outAddress == 0)
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
if (!TryWriteUInt32(ctx, outAddress, 256))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
TraceAgc($"agc.driver_get_resource_registration_max_name_length out=0x{outAddress:X16} value=256");
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
}
private const uint DefaultAgcOwner = 1;
[SysAbiExport(
Nid = "F0ZXt5q0ZTA",
ExportName = "sceAgcDriverGetDefaultOwner",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int DriverGetDefaultOwner(CpuContext ctx)
{
var ownerAddress = ctx[CpuRegister.Rdi];
if (ownerAddress == 0)
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
if (!TryWriteUInt32(ctx, ownerAddress, DefaultAgcOwner))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
TraceAgc($"agc.driver_get_default_owner out=0x{ownerAddress:X16} owner={DefaultAgcOwner}");
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
}
[SysAbiExport(
Nid = "W5z4eZrjEas",
ExportName = "sceAgcDriverRegisterResource",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int DriverRegisterResource(CpuContext ctx)
{
var resourceAddress = ctx[CpuRegister.Rdi];
var owner = (uint)ctx[CpuRegister.Rsi];
var nameAddress = ctx[CpuRegister.Rdx];
var type = (uint)ctx[CpuRegister.R8];
var flags = (uint)ctx[CpuRegister.R9];
TraceAgc(
$"agc.driver_register_resource resource=0x{resourceAddress:X16} owner={owner} " +
$"name=0x{nameAddress:X16} type={type} flags={flags}");
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
}
[SysAbiExport(
Nid = "-KRzWekV120",
ExportName = "sceAgcDriverUnknown_KRzWekV120",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int DriverUnknownKRzWekV120(CpuContext ctx)
{
TraceAgc(
$"agc.driver_unknown_krz rdi=0x{ctx[CpuRegister.Rdi]:X16} " +
$"rsi=0x{ctx[CpuRegister.Rsi]:X16} rdx=0x{ctx[CpuRegister.Rdx]:X16} " +
$"rcx=0x{ctx[CpuRegister.Rcx]:X16} r8=0x{ctx[CpuRegister.R8]:X16} r9=0x{ctx[CpuRegister.R9]:X16}");
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
}
[SysAbiExport( [SysAbiExport(
Nid = "h9z6+0hEydk", Nid = "h9z6+0hEydk",
ExportName = "sceAgcSuspendPoint", ExportName = "sceAgcSuspendPoint",
@@ -2925,7 +2740,7 @@ public static class AgcExports
$"ps=0x{(hasPixelShader ? pixelShaderAddress : 0):X16}"); $"ps=0x{(hasPixelShader ? pixelShaderAddress : 0):X16}");
} }
if (vertexCount == 0 || vertexCount > 1_048_576) if (vertexCount is not (3 or 4 or 6))
{ {
return; return;
} }
@@ -2955,13 +2770,11 @@ public static class AgcExports
out _); out _);
var globalMemoryBuffers = var globalMemoryBuffers =
CreateVulkanGuestMemoryBuffers(translatedDraw.GlobalMemoryBindings); CreateVulkanGuestMemoryBuffers(translatedDraw.GlobalMemoryBindings);
var vertexBuffers = VulkanVideoPresenter.SubmitOffscreenTranslatedDraw(
CreateVulkanGuestVertexBuffers(translatedDraw.VertexInputs); translatedDraw.PixelSpirv,
VulkanVideoPresenter.SubmitOffscreenTranslatedDraw( textures,
translatedDraw.PixelSpirv, globalMemoryBuffers,
textures, translatedDraw.AttributeCount,
globalMemoryBuffers,
translatedDraw.AttributeCount,
new VulkanGuestRenderTarget( new VulkanGuestRenderTarget(
firstTarget.Address, firstTarget.Address,
firstTarget.Width, firstTarget.Width,
@@ -2972,9 +2785,7 @@ public static class AgcExports
translatedDraw.VertexCount, translatedDraw.VertexCount,
translatedDraw.InstanceCount, translatedDraw.InstanceCount,
translatedDraw.PrimitiveType, translatedDraw.PrimitiveType,
translatedDraw.IndexBuffer, translatedDraw.IndexBuffer);
vertexBuffers,
translatedDraw.RenderState);
} }
else else
{ {
@@ -3009,9 +2820,8 @@ public static class AgcExports
lock (_submitTraceGate) lock (_submitTraceGate)
{ {
var firstTextureAddress = translatedDraw.Textures.FirstOrDefault()?.Descriptor.Address ?? 0;
if (_tracedShaderDraws.Add( if (_tracedShaderDraws.Add(
(exportShaderAddress, pixelShaderAddress, firstTarget.Address, firstTextureAddress, vertexCount))) (exportShaderAddress, pixelShaderAddress, firstTarget.Address)))
{ {
TraceTranslatedGuestDraw( TraceTranslatedGuestDraw(
ctx, ctx,
@@ -3074,8 +2884,7 @@ public static class AgcExports
ctx, ctx,
exportState, exportState,
out var exportEvaluation, out var exportEvaluation,
out error, out error) ||
resolveVertexInputs: true) ||
!Gen5ShaderTranslator.TryCreateState( !Gen5ShaderTranslator.TryCreateState(
ctx, ctx,
pixelShaderAddress, pixelShaderAddress,
@@ -3171,24 +2980,16 @@ public static class AgcExports
return false; return false;
} }
TraceAgcShader(
$"agc.texture_binding ps=0x{pixelShaderAddress:X16} es=0x{exportShaderAddress:X16} " +
$"pc=0x{binding.Pc:X} op={binding.Opcode} storage={(Gen5ShaderTranslator.IsStorageImageOperation(binding.Opcode) ? 1 : 0)} " +
$"decoded={FormatTextureDescriptor(texture)} " +
$"raw={FormatShaderDwords(binding.ResourceDescriptor)} sampler={FormatShaderDwords(binding.SamplerDescriptor)}");
textures.Add( textures.Add(
new TranslatedImageBinding( new TranslatedImageBinding(
texture, texture,
Gen5ShaderTranslator.IsStorageImageOperation(binding.Opcode), Gen5ShaderTranslator.IsStorageImageOperation(binding.Opcode),
binding.MipLevel ?? 0, binding.MipLevel ?? 0));
binding.SamplerDescriptor));
} }
var globalMemoryBindings = pixelEvaluation.GlobalMemoryBindings var globalMemoryBindings = pixelEvaluation.GlobalMemoryBindings
.Concat(exportEvaluation.GlobalMemoryBindings) .Concat(exportEvaluation.GlobalMemoryBindings)
.ToArray(); .ToArray();
IReadOnlyList<Gen5VertexInputBinding> vertexInputs =
exportEvaluation.VertexInputs ?? [];
state.UcRegisters.TryGetValue(VgtPrimitiveType, out var primitiveType); state.UcRegisters.TryGetValue(VgtPrimitiveType, out var primitiveType);
draw = new TranslatedGuestDraw( draw = new TranslatedGuestDraw(
exportShaderAddress, exportShaderAddress,
@@ -3202,9 +3003,7 @@ public static class AgcExports
indexed ? CreateVulkanIndexBuffer(ctx, state, vertexCount) : null, indexed ? CreateVulkanIndexBuffer(ctx, state, vertexCount) : null,
textures, textures,
globalMemoryBindings, globalMemoryBindings,
vertexInputs, renderTargets);
renderTargets,
CreateRenderState(state.CxRegisters, renderTargets.FirstOrDefault()));
return true; return true;
} }
@@ -3316,246 +3115,6 @@ public static class AgcExports
return targets; return targets;
} }
private static VulkanGuestRenderState CreateRenderState(
IReadOnlyDictionary<uint, uint> registers,
RenderTargetDescriptor target)
{
var scissor = DecodeScissor(registers, target.Width, target.Height);
return new VulkanGuestRenderState(
DecodeBlendState(registers, target.Slot),
scissor,
DecodeViewport(registers, target.Width, target.Height, scissor));
}
private static VulkanGuestBlendState DecodeBlendState(
IReadOnlyDictionary<uint, uint> registers,
uint slot)
{
var writeMask = 0xFu;
if (registers.TryGetValue(CbTargetMask, out var targetMask))
{
writeMask = (targetMask >> checked((int)(slot * 4))) & 0xFu;
}
registers.TryGetValue(CbBlend0Control + slot, out var control);
return new VulkanGuestBlendState(
((control >> 30) & 1u) != 0,
control & 0x1Fu,
(control >> 8) & 0x1Fu,
(control >> 5) & 0x7u,
(control >> 16) & 0x1Fu,
(control >> 24) & 0x1Fu,
(control >> 21) & 0x7u,
((control >> 29) & 1u) != 0,
writeMask == 0 ? 0xFu : writeMask);
}
private static VulkanGuestRect? DecodeScissor(
IReadOnlyDictionary<uint, uint> registers,
uint targetWidth,
uint targetHeight)
{
if (targetWidth == 0 || targetHeight == 0)
{
return new VulkanGuestRect(0, 0, 0, 0);
}
var left = 0;
var top = 0;
var right = checked((int)Math.Min(targetWidth, int.MaxValue));
var bottom = checked((int)Math.Min(targetHeight, int.MaxValue));
var windowOffsetX = 0;
var windowOffsetY = 0;
var enableWindowOffset = true;
if (registers.TryGetValue(PaScWindowScissorTl, out var windowScissorTl))
{
enableWindowOffset = (windowScissorTl & 0x80000000u) == 0;
}
if (enableWindowOffset &&
registers.TryGetValue(PaScWindowOffset, out var windowOffset))
{
windowOffsetX = (short)(windowOffset & 0xFFFFu);
windowOffsetY = (short)(windowOffset >> 16);
}
IntersectScissorPair(registers, PaScScreenScissorTl, PaScScreenScissorBr, ref left, ref top, ref right, ref bottom);
IntersectScissorPair(
registers,
PaScWindowScissorTl,
PaScWindowScissorBr,
ref left,
ref top,
ref right,
ref bottom,
windowOffsetX,
windowOffsetY);
IntersectScissorPair(
registers,
PaScGenericScissorTl,
PaScGenericScissorBr,
ref left,
ref top,
ref right,
ref bottom,
windowOffsetX,
windowOffsetY);
var vportScissorEnabled =
!registers.TryGetValue(PaScModeCntl0, out var modeControl) ||
((modeControl >> 1) & 1u) != 0;
if (vportScissorEnabled)
{
IntersectScissorPair(registers, PaScVportScissor0Tl, PaScVportScissor0Br, ref left, ref top, ref right, ref bottom);
}
left = Math.Clamp(left, 0, checked((int)targetWidth));
top = Math.Clamp(top, 0, checked((int)targetHeight));
right = Math.Clamp(right, left, checked((int)targetWidth));
bottom = Math.Clamp(bottom, top, checked((int)targetHeight));
if (left == 0 &&
top == 0 &&
right == (int)targetWidth &&
bottom == (int)targetHeight)
{
return null;
}
return new VulkanGuestRect(
left,
top,
checked((uint)(right - left)),
checked((uint)(bottom - top)));
}
private static VulkanGuestViewport? DecodeViewport(
IReadOnlyDictionary<uint, uint> registers,
uint targetWidth,
uint targetHeight,
VulkanGuestRect? scissor)
{
if (targetWidth == 0 || targetHeight == 0)
{
return new VulkanGuestViewport(0, 0, 0, 0, 0, 1);
}
var minDepth = 0f;
var maxDepth = 1f;
if (registers.TryGetValue(PaScVportZMin0, out var zMinBits) &&
registers.TryGetValue(PaScVportZMax0, out var zMaxBits))
{
var decodedMin = BitConverter.UInt32BitsToSingle(zMinBits);
var decodedMax = BitConverter.UInt32BitsToSingle(zMaxBits);
if (float.IsFinite(decodedMin) &&
float.IsFinite(decodedMax) &&
decodedMax > decodedMin)
{
minDepth = decodedMin;
maxDepth = decodedMax;
}
}
if (TryDecodeFiniteFloat(registers, PaClVportXScale, out var xScale) &&
TryDecodeFiniteFloat(registers, PaClVportXOffset, out var xOffset) &&
TryDecodeFiniteFloat(registers, PaClVportYScale, out var yScale) &&
TryDecodeFiniteFloat(registers, PaClVportYOffset, out var yOffset) &&
xScale > 0f &&
yScale != 0f)
{
return new VulkanGuestViewport(
xOffset - xScale,
yOffset - yScale,
xScale * 2f,
yScale * 2f,
minDepth,
maxDepth);
}
if (scissor is not { } rect)
{
return minDepth == 0f && maxDepth == 1f
? null
: new VulkanGuestViewport(0, 0, targetWidth, targetHeight, minDepth, maxDepth);
}
return new VulkanGuestViewport(
rect.X,
rect.Y,
rect.Width,
rect.Height,
minDepth,
maxDepth);
}
private static bool TryDecodeFiniteFloat(
IReadOnlyDictionary<uint, uint> registers,
uint register,
out float value)
{
value = 0;
if (!registers.TryGetValue(register, out var bits))
{
return false;
}
value = BitConverter.UInt32BitsToSingle(bits);
return float.IsFinite(value);
}
private static void IntersectScissorPair(
IReadOnlyDictionary<uint, uint> registers,
uint tlRegister,
uint brRegister,
ref int left,
ref int top,
ref int right,
ref int bottom,
int offsetX = 0,
int offsetY = 0)
{
if (!TryDecodeScissorPair(registers, tlRegister, brRegister, out var pairLeft, out var pairTop, out var pairRight, out var pairBottom))
{
return;
}
pairLeft += offsetX;
pairTop += offsetY;
pairRight += offsetX;
pairBottom += offsetY;
left = Math.Max(left, pairLeft);
top = Math.Max(top, pairTop);
right = Math.Min(right, pairRight);
bottom = Math.Min(bottom, pairBottom);
}
private static bool TryDecodeScissorPair(
IReadOnlyDictionary<uint, uint> registers,
uint tlRegister,
uint brRegister,
out int left,
out int top,
out int right,
out int bottom)
{
left = 0;
top = 0;
right = 0;
bottom = 0;
if (!registers.TryGetValue(tlRegister, out var tl) ||
!registers.TryGetValue(brRegister, out var br))
{
return false;
}
left = (int)(tl & 0x7FFFu);
top = (int)((tl >> 16) & 0x7FFFu);
right = (int)(br & 0x7FFFu);
bottom = (int)((br >> 16) & 0x7FFFu);
return true;
}
private static void TraceTranslatedGuestDraw( private static void TraceTranslatedGuestDraw(
CpuContext ctx, CpuContext ctx,
SubmittedGpuState gpuState, SubmittedGpuState gpuState,
@@ -3609,61 +3168,18 @@ public static class AgcExports
',', ',',
draw.GlobalMemoryBindings.Select((binding, index) => draw.GlobalMemoryBindings.Select((binding, index) =>
$"{index}:0x{binding.BaseAddress:X16}:{binding.Data.Length}:" + $"{index}:0x{binding.BaseAddress:X16}:{binding.Data.Length}:" +
Convert.ToHexString(binding.Data.AsSpan(0, Math.Min(binding.Data.Length, 256))))); Convert.ToHexString(binding.Data.AsSpan(0, Math.Min(binding.Data.Length, 32)))));
var indices = draw.IndexBuffer is { } indexBuffer var indices = draw.IndexBuffer is { } indexBuffer
? $"{(indexBuffer.Is32Bit ? 32 : 16)}:" + ? $"{(indexBuffer.Is32Bit ? 32 : 16)}:" +
Convert.ToHexString(indexBuffer.Data.AsSpan(0, Math.Min(indexBuffer.Data.Length, 32))) Convert.ToHexString(indexBuffer.Data.AsSpan(0, Math.Min(indexBuffer.Data.Length, 32)))
: "none"; : "none";
var vertexInputs = draw.VertexInputs.Count == 0
? "none"
: string.Join(
',',
draw.VertexInputs.Select(input =>
$"{input.Location}:pc=0x{input.Pc:X}:0x{input.BaseAddress:X16}" +
$":stride{input.Stride}:off{input.OffsetBytes}:c{input.ComponentCount}" +
$":fmt{input.DataFormat}/num{input.NumberFormat}"));
var scissor = draw.RenderState.Scissor is { } drawScissor
? $"{drawScissor.X},{drawScissor.Y},{drawScissor.Width}x{drawScissor.Height}"
: "full";
var viewport = draw.RenderState.Viewport is { } drawViewport
? $"{drawViewport.X:0.###},{drawViewport.Y:0.###}," +
$"{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( TraceAgcShader(
$"agc.shader_draw es=0x{draw.ExportShaderAddress:X16} " + $"agc.shader_draw es=0x{draw.ExportShaderAddress:X16} " +
$"ps=0x{draw.PixelShaderAddress:X16} spirv={draw.PixelSpirv.Length} " + $"ps=0x{draw.PixelShaderAddress:X16} spirv={draw.PixelSpirv.Length} " +
$"primitive=0x{draw.PrimitiveType:X} " + $"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} " + $"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}] indices=[{indices}]");
} }
private static IReadOnlyList<VulkanGuestDrawTexture> CreateVulkanGuestDrawTextures( private static IReadOnlyList<VulkanGuestDrawTexture> CreateVulkanGuestDrawTextures(
@@ -3680,7 +3196,6 @@ public static class AgcExports
binding.Descriptor, binding.Descriptor,
binding.IsStorage, binding.IsStorage,
binding.MipLevel, binding.MipLevel,
binding.SamplerDescriptor,
out var texture)) out var texture))
{ {
textures.Add(texture); textures.Add(texture);
@@ -3708,33 +3223,11 @@ public static class AgcExports
return buffers; return buffers;
} }
private static IReadOnlyList<VulkanGuestVertexBuffer> CreateVulkanGuestVertexBuffers(
IReadOnlyList<Gen5VertexInputBinding> bindings)
{
var buffers = new VulkanGuestVertexBuffer[bindings.Count];
for (var index = 0; index < bindings.Count; index++)
{
var binding = bindings[index];
buffers[index] = new VulkanGuestVertexBuffer(
binding.Location,
binding.ComponentCount,
binding.DataFormat,
binding.NumberFormat,
binding.BaseAddress,
binding.Stride,
binding.OffsetBytes,
binding.Data);
}
return buffers;
}
private static bool TryCreateVulkanGuestDrawTexture( private static bool TryCreateVulkanGuestDrawTexture(
CpuContext ctx, CpuContext ctx,
TextureDescriptor descriptor, TextureDescriptor descriptor,
bool isStorage, bool isStorage,
uint mipLevel, uint mipLevel,
IReadOnlyList<uint> samplerDescriptor,
out VulkanGuestDrawTexture texture) out VulkanGuestDrawTexture texture)
{ {
texture = default!; texture = default!;
@@ -3744,50 +3237,19 @@ public static class AgcExports
descriptor.Width > 8192 || descriptor.Width > 8192 ||
descriptor.Height > 8192) descriptor.Height > 8192)
{ {
texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType); texture = CreateFallbackGuestDrawTexture(isStorage);
return true; return true;
} }
var sourceWidth = descriptor.TileMode == 0
? GetLinearTexturePitch(
Math.Max(descriptor.Width, descriptor.Pitch),
descriptor.Height,
descriptor.Format)
: descriptor.Width;
var sourceByteCount = GetTextureByteCount( var sourceByteCount = GetTextureByteCount(
descriptor.Format, descriptor.Format,
sourceWidth, descriptor.Width,
descriptor.Height); descriptor.Height);
if (sourceByteCount == 0 || if (sourceByteCount == 0 ||
sourceByteCount > MaxPresentedTextureBytes || sourceByteCount > MaxPresentedTextureBytes ||
sourceByteCount > int.MaxValue) sourceByteCount > int.MaxValue)
{ {
texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType); texture = CreateFallbackGuestDrawTexture(isStorage);
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; return true;
} }
@@ -3814,18 +3276,14 @@ public static class AgcExports
IsFallback: descriptor.Address == 0, IsFallback: descriptor.Address == 0,
IsStorage: true, IsStorage: true,
MipLevels: descriptor.MipLevels, MipLevels: descriptor.MipLevels,
MipLevel: mipLevel, MipLevel: mipLevel);
Pitch: sourceWidth,
TileMode: descriptor.TileMode,
DstSelect: descriptor.DstSelect,
Sampler: ToVulkanSampler(samplerDescriptor));
return true; return true;
} }
var source = new byte[(int)sourceByteCount]; var source = new byte[(int)sourceByteCount];
if (!ctx.Memory.TryRead(descriptor.Address, source)) if (!ctx.Memory.TryRead(descriptor.Address, source))
{ {
texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType); texture = CreateFallbackGuestDrawTexture(isStorage);
return true; return true;
} }
@@ -3845,8 +3303,7 @@ public static class AgcExports
TraceAgcShader( TraceAgcShader(
$"agc.texture_source addr=0x{descriptor.Address:X16} " + $"agc.texture_source addr=0x{descriptor.Address:X16} " +
$"fmt={descriptor.Format} num={descriptor.NumberType} tile={descriptor.TileMode} " + $"fmt={descriptor.Format} num={descriptor.NumberType} tile={descriptor.TileMode} " +
$"size={descriptor.Width}x{descriptor.Height} pitch={descriptor.Pitch} " + $"size={descriptor.Width}x{descriptor.Height} " +
$"dst=0x{descriptor.DstSelect:X3} " +
$"bytes={source.Length} nonzero64={nonZero}"); $"bytes={source.Length} nonzero64={nonZero}");
var rgba = source; var rgba = source;
@@ -3860,42 +3317,22 @@ public static class AgcExports
IsFallback: false, IsFallback: false,
IsStorage: isStorage, IsStorage: isStorage,
MipLevels: descriptor.MipLevels, MipLevels: descriptor.MipLevels,
MipLevel: mipLevel, MipLevel: mipLevel);
Pitch: sourceWidth,
TileMode: descriptor.TileMode,
DstSelect: descriptor.DstSelect,
Sampler: ToVulkanSampler(samplerDescriptor));
return true; return true;
} }
private static VulkanGuestDrawTexture CreateFallbackGuestDrawTexture( private static VulkanGuestDrawTexture CreateFallbackGuestDrawTexture(bool isStorage = false) =>
bool isStorage, new(
uint format,
uint numberType)
{
var fallbackFormat = format == 0 ? 10u : format;
var fallbackNumberType = numberType;
return new(
0, 0,
1, 1,
1, 1,
fallbackFormat, 56,
fallbackNumberType, NumberType: 0,
[0, 0, 0, 255], [0, 0, 0, 255],
IsFallback: true, IsFallback: true,
IsStorage: isStorage, IsStorage: isStorage,
MipLevels: 1, MipLevels: 1,
MipLevel: 0); MipLevel: 0);
}
private static VulkanGuestSampler ToVulkanSampler(IReadOnlyList<uint> descriptor) =>
descriptor.Count >= 4
? new VulkanGuestSampler(
descriptor[0],
descriptor[1],
descriptor[2],
descriptor[3])
: default;
private static byte[] ConvertRgba16FloatToRgba8(ReadOnlySpan<byte> source, uint width, uint height) private static byte[] ConvertRgba16FloatToRgba8(ReadOnlySpan<byte> source, uint width, uint height)
{ {
@@ -4049,8 +3486,7 @@ public static class AgcExports
new TranslatedImageBinding( new TranslatedImageBinding(
texture, texture,
isStorage, isStorage,
binding.MipLevel ?? 0, binding.MipLevel ?? 0));
binding.SamplerDescriptor));
hasStorageBinding |= isStorage; hasStorageBinding |= isStorage;
var descriptorState = descriptorValid ? string.Empty : "/invalid-desc"; var descriptorState = descriptorValid ? string.Empty : "/invalid-desc";
@@ -4531,28 +3967,6 @@ public static class AgcExports
: checked(((ulong)width + 3) / 4 * (((ulong)height + 3) / 4) * blockBytes); : checked(((ulong)width + 3) / 4 * (((ulong)height + 3) / 4) * blockBytes);
} }
private static uint GetLinearTexturePitch(uint pitch, uint height, uint format)
{
var bytesPerTexel = GetTextureBytesPerTexel(format);
if (bytesPerTexel == 0 || height == 0)
{
return pitch;
}
var pitchAlignment = Math.Max(8UL, 64UL / bytesPerTexel);
var alignedPitch = AlignUp(pitch, pitchAlignment);
var sliceAlignment = Math.Max(64UL, 256UL / bytesPerTexel);
while ((alignedPitch * height) % sliceAlignment != 0)
{
alignedPitch += pitchAlignment;
}
return checked((uint)alignedPitch);
}
private static ulong AlignUp(ulong value, ulong alignment) =>
(value + alignment - 1) & ~(alignment - 1);
private static void TraceShaderTranslationMiss( private static void TraceShaderTranslationMiss(
CpuContext ctx, CpuContext ctx,
SubmittedDcbState state, SubmittedDcbState state,
@@ -4821,10 +4235,6 @@ public static class AgcExports
return false; return false;
} }
// GFX10/RDNA2 T# layout: WIDTH is split across word1[31:30] (lo 2 bits)
// and word2[11:0] (hi 12 bits); FORMAT is the combined 9-bit field at
// word1[28:20]. Verified against Kyty's decode of the same game
// descriptors (fmt=56=8_8_8_8_UNORM, extent 1280x720, sw_mode 27).
// GNM T# exposes a 38-bit baseaddr256 field, but RPCSX and the // GNM T# exposes a 38-bit baseaddr256 field, but RPCSX and the
// Demon's Souls descriptors both show that only the low 32 bits are // Demon's Souls descriptors both show that only the low 32 bits are
// part of the guest GPU VA. The upper baseaddr bits carry resource // part of the guest GPU VA. The upper baseaddr bits carry resource
@@ -4838,10 +4248,6 @@ public static class AgcExports
var type = (fields[3] >> 28) & 0xFu; var type = (fields[3] >> 28) & 0xFu;
var baseLevel = (fields[3] >> 12) & 0xFu; var baseLevel = (fields[3] >> 12) & 0xFu;
var lastLevel = (fields[3] >> 16) & 0xFu; var lastLevel = (fields[3] >> 16) & 0xFu;
var pitch = fields.Count >= 5
? ((fields[4] >> 13) & 0x3FFFu) + 1
: width;
var dstSelect = fields[3] & 0xFFFu;
if (address == 0 || width == 0 || height == 0) if (address == 0 || width == 0 || height == 0)
{ {
return false; return false;
@@ -4856,9 +4262,7 @@ public static class AgcExports
tileMode, tileMode,
type, type,
baseLevel, baseLevel,
lastLevel, lastLevel);
pitch,
dstSelect);
return true; return true;
} }
@@ -4887,9 +4291,7 @@ public static class AgcExports
TileMode: tileMode, TileMode: tileMode,
Type: Gen5TextureType2D, Type: Gen5TextureType2D,
BaseLevel: 0, BaseLevel: 0,
LastLevel: 0, LastLevel: 0);
Pitch: 1,
DstSelect: 0xFAC);
} }
private static bool TrySoftwarePresent( private static bool TrySoftwarePresent(
@@ -5574,7 +4976,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;
} }
@@ -5584,7 +4986,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;
} }
@@ -5597,12 +5000,6 @@ public static class AgcExports
? "none" ? "none"
: string.Join(',', values.Select(static value => $"{value:X8}")); : string.Join(',', values.Select(static value => $"{value:X8}"));
private static string FormatTextureDescriptor(TextureDescriptor descriptor) =>
$"addr=0x{descriptor.Address:X16} {descriptor.Width}x{descriptor.Height} " +
$"fmt={descriptor.Format} num={descriptor.NumberType} tile={descriptor.TileMode} " +
$"type={descriptor.Type} levels={descriptor.BaseLevel}-{descriptor.LastLevel} " +
$"pitch={descriptor.Pitch} dst=0x{descriptor.DstSelect:X3}";
private static void DumpSpirv( private static void DumpSpirv(
string stage, string stage,
ulong shaderAddress, ulong shaderAddress,
+2 -15
View File
@@ -60,8 +60,7 @@ internal sealed record Gen5SpirvShader(
byte[] Spirv, byte[] Spirv,
IReadOnlyList<Gen5GlobalMemoryBinding> GlobalMemoryBindings, IReadOnlyList<Gen5GlobalMemoryBinding> GlobalMemoryBindings,
IReadOnlyList<Gen5ImageBinding> ImageBindings, IReadOnlyList<Gen5ImageBinding> ImageBindings,
uint AttributeCount, uint AttributeCount);
IReadOnlyList<Gen5VertexInputBinding> VertexInputs);
internal readonly record struct Gen5ShaderResourceMapping( internal readonly record struct Gen5ShaderResourceMapping(
Gen5ShaderResourceKind Kind, Gen5ShaderResourceKind Kind,
@@ -272,17 +271,6 @@ internal sealed record Gen5GlobalMemoryBinding(
IReadOnlyList<uint> InstructionPcs, IReadOnlyList<uint> InstructionPcs,
byte[] Data); byte[] Data);
internal sealed record Gen5VertexInputBinding(
uint Pc,
uint Location,
uint ComponentCount,
uint DataFormat,
uint NumberFormat,
ulong BaseAddress,
uint Stride,
uint OffsetBytes,
byte[] Data);
internal sealed record Gen5ShaderEvaluation( internal sealed record Gen5ShaderEvaluation(
IReadOnlyList<uint> InitialScalarRegisters, IReadOnlyList<uint> InitialScalarRegisters,
IReadOnlyList<uint> ScalarRegisters, IReadOnlyList<uint> ScalarRegisters,
@@ -290,8 +278,7 @@ internal sealed record Gen5ShaderEvaluation(
IReadOnlyList<Gen5ImageBinding> ImageBindings, IReadOnlyList<Gen5ImageBinding> ImageBindings,
IReadOnlyList<Gen5GlobalMemoryBinding> GlobalMemoryBindings, IReadOnlyList<Gen5GlobalMemoryBinding> GlobalMemoryBindings,
Gen5ComputeSystemRegisters? ComputeSystemRegisters = null, Gen5ComputeSystemRegisters? ComputeSystemRegisters = null,
IReadOnlySet<uint>? RuntimeScalarRegisters = null, IReadOnlySet<uint>? RuntimeScalarRegisters = null);
IReadOnlyList<Gen5VertexInputBinding>? VertexInputs = null);
internal sealed record Gen5ShaderInstruction( internal sealed record Gen5ShaderInstruction(
uint Pc, uint Pc,
@@ -14,15 +14,12 @@ internal static class Gen5ShaderScalarEvaluator
private const int ImageDescriptorDwords = 8; private const int ImageDescriptorDwords = 8;
private const int SamplerDescriptorDwords = 4; private const int SamplerDescriptorDwords = 4;
private const int MaxGlobalMemoryBindingBytes = 16 * 1024 * 1024; private const int MaxGlobalMemoryBindingBytes = 16 * 1024 * 1024;
private const ulong RdnaWaveMask = 0xFFFF_FFFFUL;
private readonly record struct BufferDescriptor( private readonly record struct BufferDescriptor(
ulong BaseAddress, ulong BaseAddress,
uint Stride, uint Stride,
uint NumRecords, uint NumRecords,
ulong SizeBytes, ulong SizeBytes);
uint NumberFormat,
uint DataFormat);
public static bool TryResolveImageBindings( public static bool TryResolveImageBindings(
CpuContext ctx, CpuContext ctx,
@@ -44,8 +41,7 @@ internal static class Gen5ShaderScalarEvaluator
CpuContext ctx, CpuContext ctx,
Gen5ShaderState state, Gen5ShaderState state,
out Gen5ShaderEvaluation evaluation, out Gen5ShaderEvaluation evaluation,
out string error, out string error)
bool resolveVertexInputs = false)
{ {
evaluation = default!; evaluation = default!;
error = string.Empty; error = string.Empty;
@@ -64,15 +60,14 @@ internal static class Gen5ShaderScalarEvaluator
computeSystemRegisters.ClearStaticValues(scalarRegisters); computeSystemRegisters.ClearStaticValues(scalarRegisters);
} }
var execMask = RdnaWaveMask; var execMask = ulong.MaxValue;
WriteScalarPair(scalarRegisters, 106, 0, ref execMask); WriteScalarPair(scalarRegisters, 106, ulong.MaxValue, ref execMask);
WriteScalarPair(scalarRegisters, 126, execMask, ref execMask); WriteScalarPair(scalarRegisters, 126, execMask, ref execMask);
var initialScalarRegisters = (uint[])scalarRegisters.Clone(); var initialScalarRegisters = (uint[])scalarRegisters.Clone();
var resolved = new List<Gen5ImageBinding>(); var resolved = new List<Gen5ImageBinding>();
var globalMemoryBindings = new List<Gen5GlobalMemoryBinding>(); var globalMemoryBindings = new List<Gen5GlobalMemoryBinding>();
var globalMemoryByAddress = new Dictionary<(uint ScalarAddress, ulong BaseAddress), Gen5GlobalMemoryBinding>(); var globalMemoryByAddress = new Dictionary<(uint ScalarAddress, ulong BaseAddress), Gen5GlobalMemoryBinding>();
var vertexInputBindings = new List<Gen5VertexInputBinding>();
var runtimeScalarRegisters = CollectRuntimeScalarRegisters(state.Program); var runtimeScalarRegisters = CollectRuntimeScalarRegisters(state.Program);
var scalarRegisterSnapshots = new Dictionary<uint, IReadOnlyList<uint>>(); var scalarRegisterSnapshots = new Dictionary<uint, IReadOnlyList<uint>>();
var scalarConditionCode = false; var scalarConditionCode = false;
@@ -253,42 +248,6 @@ internal static class Gen5ShaderScalarEvaluator
return false; return false;
} }
if (resolveVertexInputs &&
IsVertexFetchCandidate(instruction, bufferMemory, bufferDescriptor))
{
if (!TryReadGlobalMemory(
ctx,
bufferDescriptor.BaseAddress,
bufferDescriptor.SizeBytes,
out var vertexData))
{
error =
$"vertex-buffer-read-failed pc=0x{instruction.Pc:X} " +
$"address=0x{bufferDescriptor.BaseAddress:X16} " +
$"bytes={bufferDescriptor.SizeBytes} " +
$"stride={bufferDescriptor.Stride} records={bufferDescriptor.NumRecords}";
return false;
}
if (!TryCreateVertexInputBinding(
instruction,
bufferMemory,
bufferDescriptor,
vertexData,
(uint)vertexInputBindings.Count,
scalarRegisters,
out var vertexInputBinding))
{
error =
$"vertex-input-binding-failed pc=0x{instruction.Pc:X} " +
$"s{bufferMemory.ScalarResource}";
return false;
}
vertexInputBindings.Add(vertexInputBinding);
continue;
}
var key = (bufferMemory.ScalarResource, bufferDescriptor.BaseAddress); var key = (bufferMemory.ScalarResource, bufferDescriptor.BaseAddress);
if (globalMemoryByAddress.TryGetValue(key, out var existingBinding)) if (globalMemoryByAddress.TryGetValue(key, out var existingBinding))
{ {
@@ -380,58 +339,10 @@ internal static class Gen5ShaderScalarEvaluator
resolved, resolved,
globalMemoryBindings, globalMemoryBindings,
state.ComputeSystemRegisters, state.ComputeSystemRegisters,
runtimeScalarRegisters, runtimeScalarRegisters);
vertexInputBindings);
return true; return true;
} }
private static bool TryCreateVertexInputBinding(
Gen5ShaderInstruction instruction,
Gen5BufferMemoryControl control,
BufferDescriptor descriptor,
byte[] data,
uint location,
uint[] scalarRegisters,
out Gen5VertexInputBinding binding)
{
binding = default!;
if (!IsVertexFetchCandidate(instruction, control, descriptor) ||
instruction.Sources.Count <= 2 ||
!TryEvaluateScalarOperand(instruction.Sources[2], scalarRegisters, out var scalarOffset))
{
return false;
}
var bindingData = data;
var bindingStride = descriptor.Stride;
var bindingOffset = unchecked((uint)control.OffsetBytes + scalarOffset);
var bindingDataFormat = descriptor.DataFormat;
var bindingNumberFormat = descriptor.NumberFormat;
binding = new Gen5VertexInputBinding(
instruction.Pc,
location,
control.DwordCount,
bindingDataFormat,
bindingNumberFormat,
descriptor.BaseAddress,
bindingStride,
bindingOffset,
bindingData);
return true;
}
private static bool IsVertexFetchCandidate(
Gen5ShaderInstruction instruction,
Gen5BufferMemoryControl control,
BufferDescriptor descriptor) =>
control.IndexEnabled &&
!control.OffsetEnabled &&
control.DwordCount is >= 1 and <= 4 &&
descriptor.BaseAddress != 0 &&
descriptor.Stride != 0 &&
(instruction.Opcode.StartsWith("BufferLoadFormat", StringComparison.Ordinal) ||
instruction.Opcode.StartsWith("TBufferLoadFormat", StringComparison.Ordinal));
private static HashSet<uint> CollectRuntimeScalarRegisters(Gen5ShaderProgram program) private static HashSet<uint> CollectRuntimeScalarRegisters(Gen5ShaderProgram program)
{ {
var registers = new HashSet<uint>(); var registers = new HashSet<uint>();
@@ -812,11 +723,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 +749,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 +766,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 +846,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 +855,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 +913,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,19 +941,18 @@ 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),
}; };
WriteScalarPair(registers, destination.Value, oldExec, ref execMask); WriteScalarPair(registers, destination.Value, oldExec, ref execMask);
execMask = MaskWaveValue(newExec); execMask = newExec;
WriteScalarPair(registers, 126, execMask, ref execMask); WriteScalarPair(registers, 126, execMask, ref execMask);
scalarConditionCode = execMask != 0; scalarConditionCode = newExec != 0;
return true; return true;
} }
@@ -1120,11 +1000,6 @@ internal static class Gen5ShaderScalarEvaluator
return; return;
} }
if (destination == 126)
{
value = MaskWaveValue(value);
}
registers[destination] = (uint)value; registers[destination] = (uint)value;
registers[destination + 1] = (uint)(value >> 32); registers[destination + 1] = (uint)(value >> 32);
if (destination == 126) if (destination == 126)
@@ -1133,14 +1008,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( private static bool TryExecuteScalarCompare(
Gen5ShaderInstruction instruction, Gen5ShaderInstruction instruction,
uint[] registers, uint[] registers,
@@ -1462,7 +1329,7 @@ internal static class Gen5ShaderScalarEvaluator
word2 == 0 && word2 == 0 &&
word3 == 0) word3 == 0)
{ {
descriptor = new BufferDescriptor(0, 0, 0, 0, 0, 0); descriptor = new BufferDescriptor(0, 0, 0, 0);
return true; return true;
} }
@@ -1474,64 +1341,19 @@ internal static class Gen5ShaderScalarEvaluator
return false; return false;
} }
descriptor = new BufferDescriptor(0, 0, 0, 0, 0, 0); descriptor = new BufferDescriptor(0, 0, 0, 0);
return true; return true;
} }
var baseAddress = word0 | ((ulong)(word1 & 0xFFFFu) << 32); var baseAddress = word0 | ((ulong)(word1 & 0x0FFFu) << 32);
var stride = (word1 >> 16) & 0x3FFFu; var stride = (word1 >> 16) & 0x3FFFu;
var unifiedFormat = (word3 >> 12) & 0x7Fu;
var (dataFormat, numberFormat) =
DecodeGfx10BufferFormat(unifiedFormat);
var sizeBytes = stride == 0 var sizeBytes = stride == 0
? word2 ? word2
: (ulong)stride * word2; : (ulong)stride * word2;
descriptor = new BufferDescriptor(baseAddress, stride, word2, sizeBytes, numberFormat, dataFormat); descriptor = new BufferDescriptor(baseAddress, stride, word2, sizeBytes);
return true; return true;
} }
private static (uint DataFormat, uint NumberFormat)
DecodeGfx10BufferFormat(uint format) =>
format switch
{
0 => (0, 0),
>= 1 and <= 6 => (1, format - 1),
>= 7 and <= 13 => (2, DecodeUnifiedNumber(format - 7, 7)),
>= 14 and <= 19 => (3, format - 14),
>= 20 and <= 22 => (4, DecodeIntegerOrFloatNumber(format - 20)),
>= 23 and <= 29 => (5, DecodeUnifiedNumber(format - 23, 7)),
>= 30 and <= 36 => (6, DecodeUnifiedNumber(format - 30, 7)),
>= 37 and <= 43 => (7, DecodeUnifiedNumber(format - 37, 7)),
>= 44 and <= 49 => (8, format - 44),
>= 50 and <= 55 => (9, format - 50),
>= 56 and <= 61 => (10, format - 56),
>= 62 and <= 64 => (11, DecodeIntegerOrFloatNumber(format - 62)),
>= 65 and <= 71 => (12, DecodeUnifiedNumber(format - 65, 7)),
>= 72 and <= 74 => (13, DecodeIntegerOrFloatNumber(format - 72)),
>= 75 and <= 77 => (14, DecodeIntegerOrFloatNumber(format - 75)),
128 => (1, 9),
129 => (3, 9),
130 => (10, 9),
132 => (34, 7),
133 => (16, 0),
134 => (17, 0),
135 => (18, 0),
136 => (19, 0),
140 => (4, 7),
_ => (0, 0),
};
private static uint DecodeUnifiedNumber(uint offset, uint formatCount) =>
offset == formatCount - 1 ? 7u : offset;
private static uint DecodeIntegerOrFloatNumber(uint offset) =>
offset switch
{
0 => 4,
1 => 5,
_ => 7,
};
private static bool TryReadUserDataScalarLoad( private static bool TryReadUserDataScalarLoad(
Gen5ShaderState state, Gen5ShaderState state,
Gen5ShaderInstruction instruction, Gen5ShaderInstruction instruction,
+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,
}; };
+110 -358
View File
@@ -133,7 +133,9 @@ internal static partial class Gen5SpirvTranslator
SpirvOp.ConvertFToU, SpirvOp.ConvertFToU,
_uintType, _uintType,
GetFloatSource(instruction, 0)); GetFloatSource(instruction, 0));
var offset = ShiftLeftLogical( var offset = _module.AddInstruction(
SpirvOp.ShiftLeftLogical,
_uintType,
BitwiseAnd(GetRawSource(instruction, 1), UInt(3)), BitwiseAnd(GetRawSource(instruction, 1), UInt(3)),
UInt(3)); UInt(3));
result = _module.AddInstruction( result = _module.AddInstruction(
@@ -212,26 +214,12 @@ internal static partial class Gen5SpirvTranslator
case "VSinF32": case "VSinF32":
result = EmitFloatResult( result = EmitFloatResult(
instruction, instruction,
Ext( Ext(13, _floatType, GetFloatSource(instruction, 0)));
13,
_floatType,
_module.AddInstruction(
SpirvOp.FMul,
_floatType,
GetFloatSource(instruction, 0),
Float(MathF.Tau))));
break; break;
case "VCosF32": case "VCosF32":
result = EmitFloatResult( result = EmitFloatResult(
instruction, instruction,
Ext( Ext(14, _floatType, GetFloatSource(instruction, 0)));
14,
_floatType,
_module.AddInstruction(
SpirvOp.FMul,
_floatType,
GetFloatSource(instruction, 0),
Float(MathF.Tau))));
break; break;
case "VAddF32": case "VAddF32":
result = EmitFloatBinary(instruction, SpirvOp.FAdd); result = EmitFloatBinary(instruction, SpirvOp.FAdd);
@@ -364,7 +352,9 @@ internal static partial class Gen5SpirvTranslator
result = _module.AddInstruction( result = _module.AddInstruction(
SpirvOp.UConvert, SpirvOp.UConvert,
_uintType, _uintType,
ShiftRightLogical64( _module.AddInstruction(
SpirvOp.ShiftRightLogical,
_ulongType,
product, product,
_module.Constant64(_ulongType, 32))); _module.Constant64(_ulongType, 32)));
break; break;
@@ -427,12 +417,21 @@ internal static partial class Gen5SpirvTranslator
var reverse = instruction.Opcode == "VAshrrevI32"; var reverse = instruction.Opcode == "VAshrrevI32";
var left = GetRawSource(instruction, reverse ? 1 : 0); var left = GetRawSource(instruction, reverse ? 1 : 0);
var right = GetRawSource(instruction, reverse ? 0 : 1); var right = GetRawSource(instruction, reverse ? 0 : 1);
result = ShiftRightArithmetic(left, right); right = BitwiseAnd(right, UInt(31));
result = Bitcast(
_uintType,
_module.AddInstruction(
SpirvOp.ShiftRightArithmetic,
_intType,
Bitcast(_intType, left),
right));
break; break;
} }
case "VLshlAddU32": case "VLshlAddU32":
{ {
var shifted = ShiftLeftLogical( var shifted = _module.AddInstruction(
SpirvOp.ShiftLeftLogical,
_uintType,
GetRawSource(instruction, 0), GetRawSource(instruction, 0),
BitwiseAnd(GetRawSource(instruction, 1), UInt(31))); BitwiseAnd(GetRawSource(instruction, 1), UInt(31)));
result = IAdd(shifted, GetRawSource(instruction, 2)); result = IAdd(shifted, GetRawSource(instruction, 2));
@@ -440,7 +439,9 @@ internal static partial class Gen5SpirvTranslator
} }
case "VLshlOrU32": case "VLshlOrU32":
{ {
var shifted = ShiftLeftLogical( var shifted = _module.AddInstruction(
SpirvOp.ShiftLeftLogical,
_uintType,
GetRawSource(instruction, 0), GetRawSource(instruction, 0),
BitwiseAnd(GetRawSource(instruction, 1), UInt(31))); BitwiseAnd(GetRawSource(instruction, 1), UInt(31)));
result = BitwiseOr( result = BitwiseOr(
@@ -473,7 +474,11 @@ internal static partial class Gen5SpirvTranslator
var added = IAdd( var added = IAdd(
GetRawSource(instruction, 0), GetRawSource(instruction, 0),
GetRawSource(instruction, 1)); GetRawSource(instruction, 1));
result = ShiftLeftLogical(added, GetRawSource(instruction, 2)); result = _module.AddInstruction(
SpirvOp.ShiftLeftLogical,
_uintType,
added,
BitwiseAnd(GetRawSource(instruction, 2), UInt(31)));
break; break;
} }
case "VAdd3U32": case "VAdd3U32":
@@ -649,29 +654,13 @@ 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 second = TruncateFloat32ForPack(GetFloatSource(instruction, 1));
var vector = _module.AddInstruction( var vector = _module.AddInstruction(
SpirvOp.CompositeConstruct, SpirvOp.CompositeConstruct,
_vec2Type, _vec2Type,
first, GetFloatSource(instruction, 0),
second); GetFloatSource(instruction, 1));
result = Ext(58, _uintType, vector); result = Ext(58, _uintType, vector);
break; break;
} }
@@ -975,16 +964,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 +990,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 +1004,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 +1040,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 +1052,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 +1080,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 +1087,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 +1094,32 @@ 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 = _module.AddInstruction(
Store(_scc, IsNotZero(result)); SpirvOp.ShiftLeftLogical,
_uintType,
left,
BitwiseAnd(right, UInt(31)));
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 = Bitcast(
Store(_scc, IsNotZero(result)); _uintType,
_module.AddInstruction(
SpirvOp.ShiftRightArithmetic,
_intType,
Bitcast(_intType, left),
BitwiseAnd(right, UInt(31))));
break; break;
case "SBfmB32": case "SBfmB32":
result = _module.AddInstruction( result = _module.AddInstruction(
@@ -1227,7 +1162,6 @@ internal static partial class Gen5SpirvTranslator
left, left,
offset, offset,
width); width);
Store(_scc, IsNotZero(result));
break; break;
} }
case "SCselectB32": case "SCselectB32":
@@ -1240,47 +1174,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":
@@ -1289,7 +1185,11 @@ internal static partial class Gen5SpirvTranslator
{ {
var shift = (uint)(instruction.Opcode[5] - '0'); var shift = (uint)(instruction.Opcode[5] - '0');
result = IAdd( result = IAdd(
ShiftLeftLogical(left, UInt(shift)), _module.AddInstruction(
SpirvOp.ShiftLeftLogical,
_uintType,
left,
UInt(shift)),
right); right);
break; break;
} }
@@ -1425,55 +1325,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)
@@ -1500,10 +1362,14 @@ internal static partial class Gen5SpirvTranslator
var shift = _module.AddInstruction( var shift = _module.AddInstruction(
SpirvOp.UConvert, SpirvOp.UConvert,
_ulongType, _ulongType,
GetRawSource(instruction, 1)); BitwiseAnd(GetRawSource(instruction, 1), UInt(63)));
var shiftedValue = instruction.Opcode == "SLshlB64" var shiftedValue = _module.AddInstruction(
? ShiftLeftLogical64(left, shift) instruction.Opcode == "SLshlB64"
: ShiftRightLogical64(left, shift); ? SpirvOp.ShiftLeftLogical
: SpirvOp.ShiftRightLogical,
_ulongType,
left,
shift);
StoreS64(destination, shiftedValue); StoreS64(destination, shiftedValue);
Store(_scc, IsNotZero64(shiftedValue)); Store(_scc, IsNotZero64(shiftedValue));
return true; return true;
@@ -1532,71 +1398,21 @@ internal static partial class Gen5SpirvTranslator
_uintType, _uintType,
requestedWidth, requestedWidth,
remaining); remaining);
var offset64 = _module.AddInstruction( var extracted = instruction.Opcode == "SBfeI64"
SpirvOp.UConvert, ? Bitcast(
_ulongType,
offset);
var width64 = _module.AddInstruction(
SpirvOp.UConvert,
_ulongType,
width);
var one64 = _module.Constant64(_ulongType, 1);
var shifted = ShiftRightLogical64(left, offset64);
var partialMask = _module.AddInstruction(
SpirvOp.ISub,
_ulongType,
ShiftLeftLogical64(one64, width64),
one64);
var fullWidth = _module.AddInstruction(
SpirvOp.IEqual,
_boolType,
width,
UInt(64));
var mask = _module.AddInstruction(
SpirvOp.Select,
_ulongType,
fullWidth,
_module.Constant64(_ulongType, ulong.MaxValue),
partialMask);
var extracted = _module.AddInstruction(
SpirvOp.BitwiseAnd,
_ulongType,
shifted,
mask);
if (instruction.Opcode == "SBfeI64")
{
var signShift = _module.AddInstruction(
SpirvOp.ISub,
_uintType,
width,
UInt(1));
var signBit = ShiftLeftLogical64(
one64,
_module.AddInstruction(
SpirvOp.UConvert,
_ulongType,
signShift));
var signExtended = _module.AddInstruction(
SpirvOp.ISub,
_ulongType, _ulongType,
_module.AddInstruction( _module.AddInstruction(
SpirvOp.BitwiseXor, SpirvOp.BitFieldSExtract,
_ulongType, _longType,
extracted, Bitcast(_longType, left),
signBit), offset,
signBit); width))
extracted = _module.AddInstruction( : _module.AddInstruction(
SpirvOp.Select, SpirvOp.BitFieldUExtract,
_ulongType, _ulongType,
_module.AddInstruction( left,
SpirvOp.IEqual, offset,
_boolType, width);
width,
UInt(0)),
_module.Constant64(_ulongType, 0),
signExtended);
}
StoreS64(destination, extracted); StoreS64(destination, extracted);
Store(_scc, IsNotZero64(extracted)); Store(_scc, IsNotZero64(extracted));
return true; return true;
@@ -1679,22 +1495,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;
} }
@@ -1815,7 +1615,11 @@ internal static partial class Gen5SpirvTranslator
SpirvOp.UConvert, SpirvOp.UConvert,
_ulongType, _ulongType,
LoadS(register + 1)); LoadS(register + 1));
high = ShiftLeftLogical64(high, _module.Constant64(_ulongType, 32)); high = _module.AddInstruction(
SpirvOp.ShiftLeftLogical,
_ulongType,
high,
_module.Constant64(_ulongType, 32));
return _module.AddInstruction(SpirvOp.BitwiseOr, _ulongType, low, high); return _module.AddInstruction(SpirvOp.BitwiseOr, _ulongType, low, high);
} }
@@ -1824,7 +1628,9 @@ internal static partial class Gen5SpirvTranslator
StoreS( StoreS(
register, register,
_module.AddInstruction(SpirvOp.UConvert, _uintType, value)); _module.AddInstruction(SpirvOp.UConvert, _uintType, value));
var high = ShiftRightLogical64( var high = _module.AddInstruction(
SpirvOp.ShiftRightLogical,
_ulongType,
value, value,
_module.Constant64(_ulongType, 32)); _module.Constant64(_ulongType, 32));
StoreS( StoreS(
@@ -1876,19 +1682,12 @@ internal static partial class Gen5SpirvTranslator
{ {
var left = GetRawSource(instruction, reverse ? 1 : 0); var left = GetRawSource(instruction, reverse ? 1 : 0);
var right = GetRawSource(instruction, reverse ? 0 : 1); var right = GetRawSource(instruction, reverse ? 0 : 1);
if (operation == SpirvOp.ShiftLeftLogical) if (operation is
SpirvOp.ShiftLeftLogical or
SpirvOp.ShiftRightLogical or
SpirvOp.ShiftRightArithmetic)
{ {
return ShiftLeftLogical(left, right); right = BitwiseAnd(right, UInt(31));
}
if (operation == SpirvOp.ShiftRightLogical)
{
return ShiftRightLogical(left, right);
}
if (operation == SpirvOp.ShiftRightArithmetic)
{
return ShiftRightArithmetic(left, right);
} }
return _module.AddInstruction(operation, _uintType, left, right); return _module.AddInstruction(operation, _uintType, left, right);
@@ -2187,13 +1986,21 @@ internal static partial class Gen5SpirvTranslator
_boolType, _boolType,
localLane, localLane,
UInt(8)); UInt(8));
var lowShift = ShiftLeftLogical(localLane, UInt(2)); var lowShift = _module.AddInstruction(
SpirvOp.ShiftLeftLogical,
_uintType,
localLane,
UInt(2));
var highLane = _module.AddInstruction( var highLane = _module.AddInstruction(
SpirvOp.ISub, SpirvOp.ISub,
_uintType, _uintType,
localLane, localLane,
UInt(8)); UInt(8));
var highShift = ShiftLeftLogical(highLane, UInt(2)); var highShift = _module.AddInstruction(
SpirvOp.ShiftLeftLogical,
_uintType,
highLane,
UInt(2));
var lowSelector = BitwiseAnd( var lowSelector = BitwiseAnd(
ShiftRightLogical(selectorLow, lowShift), ShiftRightLogical(selectorLow, lowShift),
UInt(15)); UInt(15));
@@ -2254,14 +2061,6 @@ internal static partial class Gen5SpirvTranslator
return Bitcast(_uintType, value); return Bitcast(_uintType, value);
} }
private uint TruncateFloat32ForPack(uint value)
{
var raw = BitwiseAnd(
Bitcast(_uintType, value),
UInt(0xFFFF_E000));
return Bitcast(_floatType, raw);
}
private uint Ext(uint operation, uint resultType, params uint[] operands) private uint Ext(uint operation, uint resultType, params uint[] operands)
{ {
var values = new uint[2 + operands.Length]; var values = new uint[2 + operands.Length];
@@ -2281,53 +2080,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)
+72 -314
View File
@@ -8,7 +8,6 @@ internal static partial class Gen5SpirvTranslator
private const uint ScalarRegisterCount = 256; private const uint ScalarRegisterCount = 256;
private const uint VectorRegisterCount = 512; private const uint VectorRegisterCount = 512;
private const uint LdsDwordCount = 8192; private const uint LdsDwordCount = 8192;
private const uint RdnaWaveLaneCount = 32;
public static bool TryCompilePixelShader( public static bool TryCompilePixelShader(
Gen5ShaderState state, Gen5ShaderState state,
@@ -96,7 +95,6 @@ internal static partial class Gen5SpirvTranslator
private readonly List<uint> _interfaces = []; private readonly List<uint> _interfaces = [];
private readonly Dictionary<uint, uint> _pixelInputs = []; private readonly Dictionary<uint, uint> _pixelInputs = [];
private readonly Dictionary<uint, uint> _vertexOutputs = []; private readonly Dictionary<uint, uint> _vertexOutputs = [];
private readonly Dictionary<uint, SpirvVertexInput> _vertexInputsByPc = [];
private readonly List<SpirvImageResource> _imageResources = []; private readonly List<SpirvImageResource> _imageResources = [];
private readonly Dictionary<uint, int> _imageBindingByPc = []; private readonly Dictionary<uint, int> _imageBindingByPc = [];
private readonly Dictionary<uint, int> _bufferBindingByPc = []; private readonly Dictionary<uint, int> _bufferBindingByPc = [];
@@ -152,11 +150,6 @@ internal static partial class Gen5SpirvTranslator
ImageComponentKind ComponentKind, ImageComponentKind ComponentKind,
bool IsStorage); bool IsStorage);
private readonly record struct SpirvVertexInput(
uint Variable,
uint Type,
uint ComponentCount);
public CompilationContext( public CompilationContext(
Gen5SpirvStage stage, Gen5SpirvStage stage,
Gen5ShaderState state, Gen5ShaderState state,
@@ -290,10 +283,7 @@ internal static partial class Gen5SpirvTranslator
_module.Build(), _module.Build(),
_evaluation.GlobalMemoryBindings, _evaluation.GlobalMemoryBindings,
_evaluation.ImageBindings, _evaluation.ImageBindings,
attributeCount, attributeCount);
_stage == Gen5SpirvStage.Vertex
? _evaluation.VertexInputs ?? []
: []);
return true; return true;
} }
catch (Exception exception) catch (Exception exception)
@@ -308,19 +298,6 @@ internal static partial class Gen5SpirvTranslator
_module.AddCapability(SpirvCapability.Shader); _module.AddCapability(SpirvCapability.Shader);
_module.AddCapability(SpirvCapability.Int64); _module.AddCapability(SpirvCapability.Int64);
_module.AddCapability(SpirvCapability.ImageQuery); _module.AddCapability(SpirvCapability.ImageQuery);
if (_evaluation.ImageBindings.Any(
static binding =>
(binding.Opcode.StartsWith(
"ImageSample",
StringComparison.Ordinal) ||
binding.Opcode.StartsWith(
"ImageGather4",
StringComparison.Ordinal)) &&
binding.Opcode.EndsWith("O", StringComparison.Ordinal)))
{
_module.AddCapability(SpirvCapability.ImageGatherExtended);
}
if (UsesSubgroupOperations()) if (UsesSubgroupOperations())
{ {
_module.AddCapability(SpirvCapability.GroupNonUniform); _module.AddCapability(SpirvCapability.GroupNonUniform);
@@ -412,6 +389,10 @@ internal static partial class Gen5SpirvTranslator
} }
var ldsArrayType = _module.TypeArray(_uintType, LdsDwordCount); var ldsArrayType = _module.TypeArray(_uintType, LdsDwordCount);
_module.AddDecoration(
ldsArrayType,
SpirvDecoration.ArrayStride,
sizeof(uint));
var ldsPointer = var ldsPointer =
_module.TypePointer(SpirvStorageClass.Workgroup, ldsArrayType); _module.TypePointer(SpirvStorageClass.Workgroup, ldsArrayType);
_workgroupUintPointer = _workgroupUintPointer =
@@ -467,6 +448,9 @@ internal static partial class Gen5SpirvTranslator
_imageBindingByPc.TryAdd(binding.Pc, index); _imageBindingByPc.TryAdd(binding.Pc, index);
var isStorage = var isStorage =
Gen5ShaderTranslator.IsStorageImageOperation(binding.Opcode); Gen5ShaderTranslator.IsStorageImageOperation(binding.Opcode);
var isDepth =
binding.Opcode.Contains("SampleC", StringComparison.Ordinal) ||
binding.Opcode.Contains("Gather4C", StringComparison.Ordinal);
var (format, componentKind) = var (format, componentKind) =
DecodeImageFormat(binding.ResourceDescriptor); DecodeImageFormat(binding.ResourceDescriptor);
var componentType = componentKind switch var componentType = componentKind switch
@@ -482,16 +466,11 @@ internal static partial class Gen5SpirvTranslator
_module.AddCapability( _module.AddCapability(
SpirvCapability.StorageImageWriteWithoutFormat); SpirvCapability.StorageImageWriteWithoutFormat);
} }
else if (isStorage && RequiresExtendedStorageImageFormat(format))
{
_module.AddCapability(
SpirvCapability.StorageImageExtendedFormats);
}
var imageType = _module.TypeImage( var imageType = _module.TypeImage(
componentType, componentType,
SpirvImageDim.Dim2D, SpirvImageDim.Dim2D,
depth: false, depth: isDepth,
arrayed: false, arrayed: false,
multisampled: false, multisampled: false,
sampled: isStorage ? 2u : 1u, sampled: isStorage ? 2u : 1u,
@@ -524,13 +503,6 @@ internal static partial class Gen5SpirvTranslator
} }
} }
private static bool RequiresExtendedStorageImageFormat(
SpirvImageFormat format) =>
format is not SpirvImageFormat.Unknown and
not SpirvImageFormat.Rgba32f and
not SpirvImageFormat.Rgba32i and
not SpirvImageFormat.Rgba32ui;
private static (SpirvImageFormat Format, ImageComponentKind Kind) private static (SpirvImageFormat Format, ImageComponentKind Kind)
DecodeImageFormat(IReadOnlyList<uint> descriptor) DecodeImageFormat(IReadOnlyList<uint> descriptor)
{ {
@@ -611,8 +583,6 @@ internal static partial class Gen5SpirvTranslator
if (_stage == Gen5SpirvStage.Vertex) if (_stage == Gen5SpirvStage.Vertex)
{ {
DeclareVertexInputs();
var inputPointer = var inputPointer =
_module.TypePointer(SpirvStorageClass.Input, _uintType); _module.TypePointer(SpirvStorageClass.Input, _uintType);
_vertexIndexInput = _module.AddGlobalVariable( _vertexIndexInput = _module.AddGlobalVariable(
@@ -658,6 +628,7 @@ internal static partial class Gen5SpirvTranslator
outputPointer, outputPointer,
SpirvStorageClass.Output); SpirvStorageClass.Output);
_module.AddDecoration(variable, SpirvDecoration.Location, parameter); _module.AddDecoration(variable, SpirvDecoration.Location, parameter);
_module.AddDecoration(variable, SpirvDecoration.NoPerspective);
_vertexOutputs.Add(parameter, variable); _vertexOutputs.Add(parameter, variable);
_interfaces.Add(variable); _interfaces.Add(variable);
} }
@@ -679,6 +650,7 @@ internal static partial class Gen5SpirvTranslator
inputVec4Pointer, inputVec4Pointer,
SpirvStorageClass.Input); SpirvStorageClass.Input);
_module.AddDecoration(variable, SpirvDecoration.Location, attribute); _module.AddDecoration(variable, SpirvDecoration.Location, attribute);
_module.AddDecoration(variable, SpirvDecoration.NoPerspective);
_pixelInputs.Add(attribute, variable); _pixelInputs.Add(attribute, variable);
_interfaces.Add(variable); _interfaces.Add(variable);
} }
@@ -729,42 +701,6 @@ internal static partial class Gen5SpirvTranslator
} }
} }
private void DeclareVertexInputs()
{
foreach (var input in _evaluation.VertexInputs ?? [])
{
var type = input.ComponentCount switch
{
1u => _floatType,
2u => _vec2Type,
3u => _vec3Type,
4u => _vec4Type,
_ => 0u,
};
if (type == 0)
{
continue;
}
var pointer = _module.TypePointer(SpirvStorageClass.Input, type);
var variable = _module.AddGlobalVariable(
pointer,
SpirvStorageClass.Input);
_module.AddName(variable, $"attr{input.Location}");
_module.AddDecoration(
variable,
SpirvDecoration.Location,
input.Location);
_vertexInputsByPc.TryAdd(
input.Pc,
new SpirvVertexInput(
variable,
type,
input.ComponentCount));
_interfaces.Add(variable);
}
}
private void EmitInitialState() private void EmitInitialState()
{ {
for (uint index = 0; for (uint index = 0;
@@ -1351,19 +1287,6 @@ internal static partial class Gen5SpirvTranslator
out string error) out string error)
{ {
error = string.Empty; error = string.Empty;
if (_stage == Gen5SpirvStage.Vertex &&
_vertexInputsByPc.TryGetValue(instruction.Pc, out var vertexInput))
{
return TryEmitVertexInputFetch(control, vertexInput, out error);
}
if (_stage == Gen5SpirvStage.Vertex &&
IsFormatBufferLoad(instruction.Opcode))
{
error = $"missing vertex input for {instruction.Opcode} pc=0x{instruction.Pc:X}";
return false;
}
if (!_bufferBindingByPc.TryGetValue(instruction.Pc, out var bindingIndex)) if (!_bufferBindingByPc.TryGetValue(instruction.Pc, out var bindingIndex))
{ {
error = "missing buffer-memory binding"; error = "missing buffer-memory binding";
@@ -1449,41 +1372,6 @@ internal static partial class Gen5SpirvTranslator
return true; return true;
} }
private static bool IsFormatBufferLoad(string opcode) =>
opcode.StartsWith("BufferLoadFormat", StringComparison.Ordinal) ||
opcode.StartsWith("TBufferLoadFormat", StringComparison.Ordinal);
private bool TryEmitVertexInputFetch(
Gen5BufferMemoryControl control,
SpirvVertexInput input,
out string error)
{
error = string.Empty;
if (control.DwordCount == 0 ||
control.DwordCount > input.ComponentCount)
{
error =
$"invalid vertex input fetch components={control.DwordCount} " +
$"input={input.ComponentCount}";
return false;
}
var loaded = Load(input.Type, input.Variable);
for (uint component = 0; component < control.DwordCount; component++)
{
var value = input.ComponentCount == 1
? loaded
: _module.AddInstruction(
SpirvOp.CompositeExtract,
_floatType,
loaded,
component);
StoreV(control.VectorData + component, Bitcast(_uintType, value));
}
return true;
}
private bool TryEmitImage( private bool TryEmitImage(
Gen5ShaderInstruction instruction, Gen5ShaderInstruction instruction,
Gen5ImageControl image, Gen5ImageControl image,
@@ -1501,20 +1389,11 @@ internal static partial class Gen5SpirvTranslator
var imageObject = Load(resource.ObjectType, resource.Variable); var imageObject = Load(resource.ObjectType, resource.Variable);
if (instruction.Opcode == "ImageGetResinfo") if (instruction.Opcode == "ImageGetResinfo")
{ {
var queryImage = resource.IsStorage
? imageObject
: _module.AddInstruction(
SpirvOp.Image,
resource.ImageType,
imageObject);
var size = _module.AddInstruction( var size = _module.AddInstruction(
resource.IsStorage SpirvOp.ImageQuerySizeLod,
? SpirvOp.ImageQuerySize
: SpirvOp.ImageQuerySizeLod,
_module.TypeVector(_intType, 2), _module.TypeVector(_intType, 2),
resource.IsStorage imageObject,
? [queryImage] UInt(0));
: [queryImage, UInt(0)]);
uint outputIndex = 0; uint outputIndex = 0;
for (uint component = 0; component < 4; component++) for (uint component = 0; component < 4; component++)
{ {
@@ -1625,14 +1504,10 @@ internal static partial class Gen5SpirvTranslator
? BuildClampedIntegerCoordinates(image, 0, width, height) ? BuildClampedIntegerCoordinates(image, 0, width, height)
: BuildIntegerCoordinates(image, 0); : BuildIntegerCoordinates(image, 0);
var mipLevel = _evaluation.ImageBindings[bindingIndex].MipLevel ?? 0; var mipLevel = _evaluation.ImageBindings[bindingIndex].MipLevel ?? 0;
var fetchedImage = _module.AddInstruction(
SpirvOp.Image,
resource.ImageType,
imageObject);
sampled = _module.AddInstruction( sampled = _module.AddInstruction(
SpirvOp.ImageFetch, SpirvOp.ImageFetch,
resource.VectorType, resource.VectorType,
fetchedImage, imageObject,
coordinates, coordinates,
2, 2,
UInt(mipLevel)); UInt(mipLevel));
@@ -1658,14 +1533,18 @@ internal static partial class Gen5SpirvTranslator
var offset = hasOffset ? BuildImageOffset(image, 0) : 0u; var offset = hasOffset ? BuildImageOffset(image, 0) : 0u;
var imageOperands = var imageOperands =
(explicitLod ? 2u : 0u) | (hasOffset ? 0x10u : 0u); (explicitLod ? 2u : 0u) | (hasOffset ? 0x10u : 0u);
var reference = hasCompare
? Bitcast(_floatType, LoadV(image.GetAddressRegister(hasOffset ? 1 : 0)))
: 0u;
var operands = new List<uint> var operands = new List<uint>
{ {
imageObject, imageObject,
coordinates, coordinates,
}; };
if (hasCompare)
{
operands.Add(
Bitcast(
_floatType,
LoadV(image.GetAddressRegister(hasOffset ? 1 : 0))));
}
if (imageOperands != 0) if (imageOperands != 0)
{ {
@@ -1682,14 +1561,29 @@ internal static partial class Gen5SpirvTranslator
} }
sampled = _module.AddInstruction( sampled = _module.AddInstruction(
explicitLod hasCompare
? SpirvOp.ImageSampleExplicitLod ? explicitLod
: SpirvOp.ImageSampleImplicitLod, ? SpirvOp.ImageSampleDrefExplicitLod
resource.VectorType, : SpirvOp.ImageSampleDrefImplicitLod
: explicitLod
? SpirvOp.ImageSampleExplicitLod
: SpirvOp.ImageSampleImplicitLod,
hasCompare ? resource.ComponentType : resource.VectorType,
[.. operands]); [.. operands]);
if (hasCompare) if (hasCompare)
{ {
sampled = EmitManualDepthCompare(resource, sampled, reference); var scalar = sampled;
sampled = _module.AddInstruction(
SpirvOp.CompositeConstruct,
resource.VectorType,
scalar,
scalar,
scalar,
resource.ComponentKind == ImageComponentKind.Float
? Float(1)
: resource.ComponentKind == ImageComponentKind.Uint
? UInt(1)
: _module.Constant(_intType, 1));
} }
} }
else if (instruction.Opcode.StartsWith( else if (instruction.Opcode.StartsWith(
@@ -1703,9 +1597,6 @@ internal static partial class Gen5SpirvTranslator
var start = (hasOffset ? 1 : 0) + (hasCompare ? 1 : 0); var start = (hasOffset ? 1 : 0) + (hasCompare ? 1 : 0);
var coordinates = BuildFloatCoordinates(image, start); var coordinates = BuildFloatCoordinates(image, start);
var offset = hasOffset ? BuildImageOffset(image, 0) : 0u; var offset = hasOffset ? BuildImageOffset(image, 0) : 0u;
var reference = hasCompare
? Bitcast(_floatType, LoadV(image.GetAddressRegister(hasOffset ? 1 : 0)))
: 0u;
var operands = new List<uint> var operands = new List<uint>
{ {
imageObject, imageObject,
@@ -1713,7 +1604,10 @@ internal static partial class Gen5SpirvTranslator
}; };
if (hasCompare) if (hasCompare)
{ {
operands.Add(UInt(0)); operands.Add(
Bitcast(
_floatType,
LoadV(image.GetAddressRegister(hasOffset ? 1 : 0))));
} }
else else
{ {
@@ -1734,28 +1628,11 @@ internal static partial class Gen5SpirvTranslator
} }
sampled = _module.AddInstruction( sampled = _module.AddInstruction(
SpirvOp.ImageGather, hasCompare
? SpirvOp.ImageDrefGather
: SpirvOp.ImageGather,
resource.VectorType, resource.VectorType,
[.. operands]); [.. operands]);
if (hasCompare)
{
var compared = new uint[4];
for (var component = 0u; component < 4; component++)
{
var texel = _module.AddInstruction(
SpirvOp.CompositeExtract,
resource.ComponentType,
sampled,
component);
compared[component] = EmitDepthCompareScalar(resource, texel, reference);
}
sampled = _module.AddInstruction(
SpirvOp.CompositeConstruct,
resource.VectorType,
compared);
}
writeAllComponents = true; writeAllComponents = true;
} }
else else
@@ -1789,67 +1666,6 @@ internal static partial class Gen5SpirvTranslator
return true; return true;
} }
private uint EmitDepthCompareScalar(
SpirvImageResource resource,
uint texel,
uint reference)
{
var texelAsFloat = resource.ComponentKind switch
{
ImageComponentKind.Uint => _module.AddInstruction(
SpirvOp.ConvertUToF, _floatType, texel),
ImageComponentKind.Sint => _module.AddInstruction(
SpirvOp.ConvertSToF, _floatType, texel),
_ => texel,
};
var passes = _module.AddInstruction(
SpirvOp.FOrdLessThanEqual,
_boolType,
reference,
texelAsFloat);
return _module.AddInstruction(
SpirvOp.Select,
resource.ComponentType,
passes,
resource.ComponentKind switch
{
ImageComponentKind.Uint => UInt(1),
ImageComponentKind.Sint => _module.Constant(_intType, 1),
_ => Float(1),
},
resource.ComponentKind switch
{
ImageComponentKind.Uint => UInt(0),
ImageComponentKind.Sint => _module.Constant(_intType, 0),
_ => Float(0),
});
}
private uint EmitManualDepthCompare(
SpirvImageResource resource,
uint sampledVector,
uint reference)
{
var texel = _module.AddInstruction(
SpirvOp.CompositeExtract,
resource.ComponentType,
sampledVector,
0u);
var scalar = EmitDepthCompareScalar(resource, texel, reference);
return _module.AddInstruction(
SpirvOp.CompositeConstruct,
resource.VectorType,
scalar,
scalar,
scalar,
resource.ComponentKind switch
{
ImageComponentKind.Uint => UInt(1),
ImageComponentKind.Sint => _module.Constant(_intType, 1),
_ => Float(1),
});
}
private uint BuildFloatCoordinates(Gen5ImageControl image, int start) private uint BuildFloatCoordinates(Gen5ImageControl image, int start)
{ {
var x = Bitcast( var x = Bitcast(
@@ -2241,13 +2057,18 @@ internal static partial class Gen5SpirvTranslator
private void StoreS(uint register, uint value) private void StoreS(uint register, uint value)
{ {
Store(ScalarPointer(register), value); Store(ScalarPointer(register), value);
if (_subgroupInvocationIdInput == 0)
{
return;
}
if (register is 106 or 107) if (register is 106 or 107)
{ {
Store(_vcc, IsWaveMaskActive(LoadS64(106))); Store(_vcc, IsCurrentLaneSet(LoadS64(106)));
} }
else if (register is 126 or 127) else if (register is 126 or 127)
{ {
Store(_exec, IsWaveMaskActive(LoadS64(126))); Store(_exec, IsCurrentLaneSet(LoadS64(126)));
} }
} }
@@ -2284,49 +2105,12 @@ internal static partial class Gen5SpirvTranslator
private uint IAdd(uint left, uint right) => private uint IAdd(uint left, uint right) =>
_module.AddInstruction(SpirvOp.IAdd, _uintType, left, right); _module.AddInstruction(SpirvOp.IAdd, _uintType, left, right);
private uint ShiftLeftLogical(uint left, uint right) =>
_module.AddInstruction(
SpirvOp.ShiftLeftLogical,
_uintType,
left,
BitwiseAnd(right, UInt(31)));
private uint ShiftRightLogical(uint left, uint right) => private uint ShiftRightLogical(uint left, uint right) =>
_module.AddInstruction( _module.AddInstruction(SpirvOp.ShiftRightLogical, _uintType, left, right);
SpirvOp.ShiftRightLogical,
_uintType,
left,
BitwiseAnd(right, UInt(31)));
private uint ShiftRightArithmetic(uint left, uint right) =>
Bitcast(
_uintType,
_module.AddInstruction(
SpirvOp.ShiftRightArithmetic,
_intType,
Bitcast(_intType, left),
BitwiseAnd(right, UInt(31))));
private uint ShiftLeftLogical64(uint left, uint right) =>
_module.AddInstruction(
SpirvOp.ShiftLeftLogical,
_ulongType,
left,
BitwiseAnd64(right, _module.Constant64(_ulongType, 63)));
private uint ShiftRightLogical64(uint left, uint right) =>
_module.AddInstruction(
SpirvOp.ShiftRightLogical,
_ulongType,
left,
BitwiseAnd64(right, _module.Constant64(_ulongType, 63)));
private uint BitwiseAnd(uint left, uint right) => private uint BitwiseAnd(uint left, uint right) =>
_module.AddInstruction(SpirvOp.BitwiseAnd, _uintType, left, right); _module.AddInstruction(SpirvOp.BitwiseAnd, _uintType, left, right);
private uint BitwiseAnd64(uint left, uint right) =>
_module.AddInstruction(SpirvOp.BitwiseAnd, _ulongType, left, right);
private uint BitwiseOr(uint left, uint right) => private uint BitwiseOr(uint left, uint right) =>
_module.AddInstruction(SpirvOp.BitwiseOr, _uintType, left, right); _module.AddInstruction(SpirvOp.BitwiseOr, _uintType, left, right);
@@ -2337,44 +2121,25 @@ internal static partial class Gen5SpirvTranslator
_module.AddInstruction(SpirvOp.LogicalNot, _boolType, value); _module.AddInstruction(SpirvOp.LogicalNot, _boolType, value);
private uint SubgroupAny(uint condition) => private uint SubgroupAny(uint condition) =>
_subgroupInvocationIdInput == 0 _module.AddInstruction(
? condition SpirvOp.GroupNonUniformAny,
: _module.AddInstruction( _boolType,
SpirvOp.GroupNonUniformAny, UInt(3),
_boolType, condition);
UInt(3),
condition);
private uint CurrentLaneBit() private uint CurrentLaneBit()
{ {
if (_subgroupInvocationIdInput == 0) var lane = _module.AddInstruction(
{ SpirvOp.UConvert,
return _module.Constant64(_ulongType, 1);
}
var lane = Load(_uintType, _subgroupInvocationIdInput);
var maskedLane = BitwiseAnd(lane, UInt(RdnaWaveLaneCount - 1));
var shifted = ShiftLeftLogical64(
_module.Constant64(_ulongType, 1),
_module.AddInstruction(
SpirvOp.UConvert,
_ulongType,
maskedLane));
return _module.AddInstruction(
SpirvOp.Select,
_ulongType, _ulongType,
IsCurrentLaneInRdnaWave(), Load(_uintType, _subgroupInvocationIdInput));
shifted, return _module.AddInstruction(
_module.Constant64(_ulongType, 0)); SpirvOp.ShiftLeftLogical,
_ulongType,
_module.Constant64(_ulongType, 1),
lane);
} }
private uint IsCurrentLaneInRdnaWave() =>
_module.AddInstruction(
SpirvOp.ULessThan,
_boolType,
Load(_uintType, _subgroupInvocationIdInput),
UInt(RdnaWaveLaneCount));
private uint BooleanToLaneMask(uint condition) => private uint BooleanToLaneMask(uint condition) =>
_module.AddInstruction( _module.AddInstruction(
SpirvOp.Select, SpirvOp.Select,
@@ -2383,11 +2148,6 @@ internal static partial class Gen5SpirvTranslator
CurrentLaneBit(), CurrentLaneBit(),
_module.Constant64(_ulongType, 0)); _module.Constant64(_ulongType, 0));
private uint IsWaveMaskActive(uint mask) =>
_subgroupInvocationIdInput == 0
? IsNotZero64(mask)
: IsCurrentLaneSet(mask);
private uint IsCurrentLaneSet(uint mask) => private uint IsCurrentLaneSet(uint mask) =>
IsNotZero64( IsNotZero64(
_module.AddInstruction( _module.AddInstruction(
@@ -2403,11 +2163,10 @@ internal static partial class Gen5SpirvTranslator
{ {
var activeLabel = _module.AllocateId(); var activeLabel = _module.AllocateId();
var mergeLabel = _module.AllocateId(); var mergeLabel = _module.AllocateId();
var active = Load(_boolType, _exec);
_module.AddStatement(SpirvOp.SelectionMerge, mergeLabel, 0); _module.AddStatement(SpirvOp.SelectionMerge, mergeLabel, 0);
_module.AddStatement( _module.AddStatement(
SpirvOp.BranchConditional, SpirvOp.BranchConditional,
active, Load(_boolType, _exec),
activeLabel, activeLabel,
mergeLabel); mergeLabel);
_module.AddLabel(activeLabel); _module.AddLabel(activeLabel);
@@ -2434,8 +2193,7 @@ internal static partial class Gen5SpirvTranslator
instruction.Destinations.Any(IsWaveMaskOperand)); instruction.Destinations.Any(IsWaveMaskOperand));
private bool UsesSubgroupOperations() => private bool UsesSubgroupOperations() =>
_stage == Gen5SpirvStage.Compute && UsesSubgroupShuffle() || UsesWaveControl();
(UsesSubgroupShuffle() || UsesWaveControl());
private static bool IsWaveMaskOperand(Gen5Operand operand) => private static bool IsWaveMaskOperand(Gen5Operand operand) =>
operand.Kind == Gen5OperandKind.ScalarRegister && operand.Kind == Gen5OperandKind.ScalarRegister &&
@@ -62,7 +62,6 @@ internal enum SpirvOp : ushort
ImageDrefGather = 97, ImageDrefGather = 97,
ImageRead = 98, ImageRead = 98,
ImageWrite = 99, ImageWrite = 99,
Image = 100,
ImageQuerySizeLod = 103, ImageQuerySizeLod = 103,
ImageQuerySize = 104, ImageQuerySize = 104,
ImageQueryLod = 105, ImageQueryLod = 105,
@@ -174,8 +173,6 @@ internal enum SpirvCapability : uint
Float64 = 10, Float64 = 10,
Int64 = 11, Int64 = 11,
Int16 = 22, Int16 = 22,
ImageGatherExtended = 25,
StorageImageExtendedFormats = 49,
ImageQuery = 50, ImageQuery = 50,
StorageImageReadWithoutFormat = 55, StorageImageReadWithoutFormat = 55,
StorageImageWriteWithoutFormat = 56, StorageImageWriteWithoutFormat = 56,
@@ -313,18 +310,6 @@ internal sealed class SpirvModuleBuilder
private readonly Dictionary<(uint Width, bool Signed), uint> _integerTypes = []; private readonly Dictionary<(uint Width, bool Signed), uint> _integerTypes = [];
private readonly Dictionary<uint, uint> _floatTypes = []; private readonly Dictionary<uint, uint> _floatTypes = [];
private readonly Dictionary<(uint Component, uint Count), uint> _vectorTypes = []; private readonly Dictionary<(uint Component, uint Count), uint> _vectorTypes = [];
private readonly Dictionary<
(
uint SampledType,
SpirvImageDim Dimension,
bool Depth,
bool Arrayed,
bool Multisampled,
uint Sampled,
SpirvImageFormat Format
),
uint> _imageTypes = [];
private readonly Dictionary<uint, uint> _sampledImageTypes = [];
private readonly Dictionary<(SpirvStorageClass Storage, uint Type), uint> _pointerTypes = []; private readonly Dictionary<(SpirvStorageClass Storage, uint Type), uint> _pointerTypes = [];
private readonly Dictionary<(uint Element, uint Count), uint> _arrayTypes = []; private readonly Dictionary<(uint Element, uint Count), uint> _arrayTypes = [];
private readonly Dictionary<uint, uint> _runtimeArrayTypes = []; private readonly Dictionary<uint, uint> _runtimeArrayTypes = [];
@@ -493,19 +478,6 @@ internal sealed class SpirvModuleBuilder
uint sampled, uint sampled,
SpirvImageFormat format) SpirvImageFormat format)
{ {
var key = (
sampledType,
dimension,
depth,
arrayed,
multisampled,
sampled,
format);
if (_imageTypes.TryGetValue(key, out var existing))
{
return existing;
}
var id = AllocateId(); var id = AllocateId();
Emit( Emit(
_typesConstantsGlobals, _typesConstantsGlobals,
@@ -518,20 +490,13 @@ internal sealed class SpirvModuleBuilder
multisampled ? 1u : 0u, multisampled ? 1u : 0u,
sampled, sampled,
(uint)format); (uint)format);
_imageTypes.Add(key, id);
return id; return id;
} }
public uint TypeSampledImage(uint imageType) public uint TypeSampledImage(uint imageType)
{ {
if (_sampledImageTypes.TryGetValue(imageType, out var existing))
{
return existing;
}
var id = AllocateId(); var id = AllocateId();
Emit(_typesConstantsGlobals, SpirvOp.TypeSampledImage, id, imageType); Emit(_typesConstantsGlobals, SpirvOp.TypeSampledImage, id, imageType);
_sampledImageTypes.Add(imageType, id);
return id; return id;
} }
@@ -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;
}
}
@@ -1,77 +0,0 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
using SharpEmu.Libs.Kernel;
namespace SharpEmu.Libs.AvPlayer;
public static class AvPlayerExports
{
private const int InvalidParameters = unchecked((int)0x806A0001);
private static readonly object StateGate = new();
private static readonly HashSet<ulong> Players = new();
[SysAbiExport(
Nid = "aS66RI0gGgo",
ExportName = "sceAvPlayerInit",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceAvPlayer")]
public static int AvPlayerInit(CpuContext ctx)
{
if (ctx[CpuRegister.Rdi] == 0 ||
!KernelMemoryCompatExports.TryAllocateHleData(ctx, 0x40, 16, out var handle))
{
ctx[CpuRegister.Rax] = 0;
return 0;
}
lock (StateGate)
{
Players.Add(handle);
}
ctx[CpuRegister.Rax] = handle;
return unchecked((int)handle);
}
[SysAbiExport(
Nid = "HD1YKVU26-M",
ExportName = "sceAvPlayerPostInit",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceAvPlayer")]
public static int AvPlayerPostInit(CpuContext ctx)
{
var handle = ctx[CpuRegister.Rdi];
var dataAddress = ctx[CpuRegister.Rsi];
lock (StateGate)
{
return SetReturn(
ctx,
handle != 0 && dataAddress != 0 && Players.Contains(handle)
? 0
: InvalidParameters);
}
}
[SysAbiExport(
Nid = "NkJwDzKmIlw",
ExportName = "sceAvPlayerClose",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceAvPlayer")]
public static int AvPlayerClose(CpuContext ctx)
{
lock (StateGate)
{
return SetReturn(
ctx,
Players.Remove(ctx[CpuRegister.Rdi]) ? 0 : InvalidParameters);
}
}
private static int SetReturn(CpuContext ctx, int result)
{
ctx[CpuRegister.Rax] = unchecked((ulong)result);
return result;
}
}
@@ -1,27 +0,0 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
using System.Threading;
namespace SharpEmu.Libs.CommonDialog;
public static class MsgDialogExports
{
private const int AlreadyInitialized = unchecked((int)0x80B80002);
private static int _initialized;
[SysAbiExport(
Nid = "lDqxaY1UbEo",
ExportName = "sceMsgDialogInitialize",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceMsgDialog")]
public static int MsgDialogInitialize(CpuContext ctx)
{
var result = Interlocked.Exchange(ref _initialized, 1) == 0
? 0
: AlreadyInitialized;
ctx[CpuRegister.Rax] = unchecked((ulong)result);
return result;
}
}
+4 -24
View File
@@ -306,6 +306,10 @@ public static class KernelExports
{ {
var threadId = ctx[CpuRegister.Rdi]; var threadId = ctx[CpuRegister.Rdi];
var returnValueAddress = ctx[CpuRegister.Rsi]; var returnValueAddress = ctx[CpuRegister.Rsi];
if (returnValueAddress != 0 && !ctx.TryWriteUInt64(returnValueAddress, 0))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
if (ShouldTracePthread()) if (ShouldTracePthread())
{ {
@@ -313,30 +317,6 @@ public static class KernelExports
$"[LOADER][TRACE] pthread_join: thread=0x{threadId:X16} retval_out=0x{returnValueAddress:X16}"); $"[LOADER][TRACE] pthread_join: thread=0x{threadId:X16} retval_out=0x{returnValueAddress:X16}");
} }
var returnValue = 0UL;
if (GuestThreadExecution.Scheduler is { } scheduler &&
!scheduler.TryJoinThread(ctx, threadId, out returnValue, out var error))
{
Console.Error.WriteLine(
$"[LOADER][ERROR] pthread_join: thread=0x{threadId:X16}: {error}");
var result = string.Equals(
error,
"thread cannot join itself",
StringComparison.Ordinal)
? OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT
: OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
ctx[CpuRegister.Rax] = unchecked((ulong)(int)result);
return (int)result;
}
if (returnValueAddress != 0 &&
!ctx.TryWriteUInt64(returnValueAddress, returnValue))
{
ctx[CpuRegister.Rax] =
unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
ctx[CpuRegister.Rax] = 0; ctx[CpuRegister.Rax] = 0;
return (int)OrbisGen2Result.ORBIS_GEN2_OK; return (int)OrbisGen2Result.ORBIS_GEN2_OK;
} }
@@ -99,13 +99,10 @@ public static class KernelMemoryCompatExports
private static readonly object _tlsGate = new(); private static readonly object _tlsGate = new();
private static readonly object _ioTraceGate = new(); private static readonly object _ioTraceGate = new();
private static readonly object _statCacheGate = new(); private static readonly object _statCacheGate = new();
private static readonly object _guestMountGate = new();
private static readonly Dictionary<ulong, DirectAllocation> _directAllocations = new(); private static readonly Dictionary<ulong, DirectAllocation> _directAllocations = new();
private static readonly Dictionary<ulong, LibcHeapAllocation> _libcAllocations = new(); private static readonly Dictionary<ulong, LibcHeapAllocation> _libcAllocations = new();
private static readonly Dictionary<ulong, MappedRegion> _mappedRegions = new(); private static readonly Dictionary<ulong, MappedRegion> _mappedRegions = new();
private static readonly Dictionary<ulong, string> _mappedRegionNames = new();
private static readonly Dictionary<ulong, ulong> _tlsModuleBlocks = new(); private static readonly Dictionary<ulong, ulong> _tlsModuleBlocks = new();
private static readonly Dictionary<string, string> _guestMounts = new(StringComparer.OrdinalIgnoreCase);
private static readonly HashSet<string> _tracedStatResults = new(StringComparer.Ordinal); private static readonly HashSet<string> _tracedStatResults = new(StringComparer.Ordinal);
private static readonly HashSet<string> _negativeStatCache = new(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> _negativeStatCache = new(StringComparer.OrdinalIgnoreCase);
private static long _nextFileDescriptor = 2; private static long _nextFileDescriptor = 2;
@@ -156,32 +153,6 @@ public static class KernelMemoryCompatExports
private readonly record struct MappedRegion(ulong Address, ulong Length, int Protection, bool IsFlexible, bool IsDirect, ulong DirectStart); private readonly record struct MappedRegion(ulong Address, ulong Length, int Protection, bool IsFlexible, bool IsDirect, ulong DirectStart);
private readonly record struct BatchMapEntry(ulong Start, ulong Offset, ulong Length, byte Protection, byte Type, int Operation); private readonly record struct BatchMapEntry(ulong Start, ulong Offset, ulong Length, byte Protection, byte Type, int Operation);
public static void RegisterGuestPathMount(string guestMountPoint, string hostRoot)
{
ArgumentException.ThrowIfNullOrWhiteSpace(guestMountPoint);
ArgumentException.ThrowIfNullOrWhiteSpace(hostRoot);
var normalizedMountPoint = NormalizeGuestStatCachePath(guestMountPoint);
if (normalizedMountPoint is null || normalizedMountPoint == "/")
{
throw new ArgumentException("Guest mount point must name a directory.", nameof(guestMountPoint));
}
var normalizedHostRoot = Path.GetFullPath(hostRoot);
Directory.CreateDirectory(normalizedHostRoot);
lock (_guestMountGate)
{
_guestMounts[normalizedMountPoint] = normalizedHostRoot;
}
lock (_statCacheGate)
{
_negativeStatCache.RemoveWhere(path =>
string.Equals(path, normalizedMountPoint, StringComparison.OrdinalIgnoreCase) ||
path.StartsWith(normalizedMountPoint + "/", StringComparison.OrdinalIgnoreCase));
}
}
internal static bool TryAllocateHleData( internal static bool TryAllocateHleData(
CpuContext ctx, CpuContext ctx,
ulong length, ulong length,
@@ -232,25 +203,6 @@ public static class KernelMemoryCompatExports
return true; return true;
} }
internal static void RegisterReservedVirtualRange(ulong address, ulong length)
{
if (address == 0 || length == 0)
{
return;
}
lock (_memoryGate)
{
_mappedRegions[address] = new MappedRegion(
address,
length,
Protection: 0,
IsFlexible: false,
IsDirect: false,
DirectStart: 0);
}
}
[SysAbiExport( [SysAbiExport(
Nid = "8zTFvBIAIN8", Nid = "8zTFvBIAIN8",
ExportName = "memset", ExportName = "memset",
@@ -2455,36 +2407,6 @@ public static class KernelMemoryCompatExports
return (int)OrbisGen2Result.ORBIS_GEN2_OK; return (int)OrbisGen2Result.ORBIS_GEN2_OK;
} }
[SysAbiExport(
Nid = "hwVSPCmp5tM",
ExportName = "sceKernelCheckedReleaseDirectMemory",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int KernelCheckedReleaseDirectMemory(CpuContext ctx)
{
var start = ctx[CpuRegister.Rdi];
var length = ctx[CpuRegister.Rsi];
if (length == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
lock (_memoryGate)
{
if (!_directAllocations.TryGetValue(start, out var allocation) ||
allocation.Length != length)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
}
_directAllocations.Remove(start);
_nextPhysicalAddress = GetDirectMemoryHighWaterMarkLocked();
}
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport( [SysAbiExport(
Nid = "L-Q3LEjIbgA", Nid = "L-Q3LEjIbgA",
ExportName = "sceKernelMapDirectMemory", ExportName = "sceKernelMapDirectMemory",
@@ -2664,16 +2586,6 @@ public static class KernelMemoryCompatExports
return KernelMapNamedFlexibleMemory(ctx); return KernelMapNamedFlexibleMemory(ctx);
} }
[SysAbiExport(
Nid = "4h6F1LLbTiw",
ExportName = "sceKernelMapFlexibleMemoryInternal",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int KernelMapFlexibleMemoryInternal(CpuContext ctx)
{
return KernelMapNamedFlexibleMemory(ctx);
}
[SysAbiExport( [SysAbiExport(
Nid = "2SKEx6bSq-4", Nid = "2SKEx6bSq-4",
ExportName = "sceKernelBatchMap", ExportName = "sceKernelBatchMap",
@@ -2727,44 +2639,6 @@ public static class KernelMemoryCompatExports
return (int)OrbisGen2Result.ORBIS_GEN2_OK; return (int)OrbisGen2Result.ORBIS_GEN2_OK;
} }
[SysAbiExport(
Nid = "DGMG3JshrZU",
ExportName = "sceKernelSetVirtualRangeName",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int KernelSetVirtualRangeName(CpuContext ctx)
{
var address = ctx[CpuRegister.Rdi];
var length = ctx[CpuRegister.Rsi];
var nameAddress = ctx[CpuRegister.Rdx];
if (nameAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
if (!TryReadCString(ctx, nameAddress, OrbisKernelMaximumNameLength, out var nameBytes))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
var name = Encoding.UTF8.GetString(nameBytes);
lock (_memoryGate)
{
if (!TryFindVirtualQueryRegionLocked(address, findNext: false, out var region) ||
length > region.Length ||
address < region.Address ||
length > region.Address + region.Length - address)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
}
_mappedRegionNames[region.Address] = name;
}
ctx[CpuRegister.Rax] = 0;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport( [SysAbiExport(
Nid = "rVjRvHJ0X6c", Nid = "rVjRvHJ0X6c",
ExportName = "sceKernelVirtualQuery", ExportName = "sceKernelVirtualQuery",
@@ -2822,16 +2696,11 @@ public static class KernelMemoryCompatExports
BinaryPrimitives.WriteInt32LittleEndian(payload[28..32], memoryType); BinaryPrimitives.WriteInt32LittleEndian(payload[28..32], memoryType);
payload[32] = unchecked((byte)stateFlags); payload[32] = unchecked((byte)stateFlags);
string name; var name = region.IsDirect
lock (_memoryGate) ? "direct"
{ : region.IsFlexible
name = _mappedRegionNames.GetValueOrDefault(region.Address) ? "flexible"
?? (region.IsDirect : string.Empty;
? "direct"
: region.IsFlexible
? "flexible"
: string.Empty);
}
if (!string.IsNullOrEmpty(name)) if (!string.IsNullOrEmpty(name))
{ {
var nameBytes = Encoding.ASCII.GetBytes(name); var nameBytes = Encoding.ASCII.GetBytes(name);
@@ -4164,11 +4033,6 @@ public static class KernelMemoryCompatExports
return guestPath; return guestPath;
} }
if (TryResolveRegisteredGuestMount(guestPath, out var mountedPath))
{
return mountedPath;
}
if (guestPath.StartsWith("/devlog/app/", StringComparison.OrdinalIgnoreCase)) if (guestPath.StartsWith("/devlog/app/", StringComparison.OrdinalIgnoreCase))
{ {
var relative = NormalizeMountRelativePath(guestPath["/devlog/app/".Length..]); var relative = NormalizeMountRelativePath(guestPath["/devlog/app/".Length..]);
@@ -4267,51 +4131,6 @@ public static class KernelMemoryCompatExports
return guestPath; return guestPath;
} }
private static bool TryResolveRegisteredGuestMount(string guestPath, out string hostPath)
{
hostPath = string.Empty;
var normalizedGuestPath = NormalizeGuestStatCachePath(guestPath);
if (normalizedGuestPath is null)
{
return false;
}
string? matchedMountPoint = null;
string? matchedHostRoot = null;
lock (_guestMountGate)
{
foreach (var (mountPoint, hostRoot) in _guestMounts)
{
if ((string.Equals(normalizedGuestPath, mountPoint, StringComparison.OrdinalIgnoreCase) ||
normalizedGuestPath.StartsWith(mountPoint + "/", StringComparison.OrdinalIgnoreCase)) &&
(matchedMountPoint is null || mountPoint.Length > matchedMountPoint.Length))
{
matchedMountPoint = mountPoint;
matchedHostRoot = hostRoot;
}
}
}
if (matchedMountPoint is null || matchedHostRoot is null)
{
return false;
}
var relativePath = normalizedGuestPath[matchedMountPoint.Length..].TrimStart('/');
var candidate = Path.GetFullPath(Path.Combine(
matchedHostRoot,
NormalizeMountRelativePath(relativePath)));
var rootWithSeparator = Path.TrimEndingDirectorySeparator(matchedHostRoot) + Path.DirectorySeparatorChar;
if (!string.Equals(candidate, matchedHostRoot, StringComparison.OrdinalIgnoreCase) &&
!candidate.StartsWith(rootWithSeparator, StringComparison.OrdinalIgnoreCase))
{
return false;
}
hostPath = candidate;
return true;
}
private static string? ResolveApp0Root() private static string? ResolveApp0Root()
{ {
var cached = Volatile.Read(ref _cachedApp0Root); var cached = Volatile.Read(ref _cachedApp0Root);
@@ -717,35 +717,6 @@ public static class KernelPthreadExtendedCompatExports
return (int)OrbisGen2Result.ORBIS_GEN2_OK; return (int)OrbisGen2Result.ORBIS_GEN2_OK;
} }
[SysAbiExport(
Nid = "FXPWHNk8Of0",
ExportName = "scePthreadAttrGetschedparam",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int PthreadAttrGetschedparam(CpuContext ctx)
{
var attrAddress = ctx[CpuRegister.Rdi];
var schedParamAddress = ctx[CpuRegister.Rsi];
if (attrAddress == 0 || schedParamAddress == 0)
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
}
PthreadAttrState state;
lock (_stateGate)
{
state = GetOrCreateAttrStateLocked(attrAddress);
}
if (!TryWriteInt32(ctx, schedParamAddress, state.SchedPriority))
{
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
ctx[CpuRegister.Rax] = 0;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
[SysAbiExport( [SysAbiExport(
Nid = "DzES9hQF4f4", Nid = "DzES9hQF4f4",
ExportName = "scePthreadAttrSetschedparam", ExportName = "scePthreadAttrSetschedparam",
@@ -752,7 +752,6 @@ public static class KernelRuntimeCompatExports
_nextReservedVirtualBase = Math.Max(_nextReservedVirtualBase, mappedAddress + length); _nextReservedVirtualBase = Math.Max(_nextReservedVirtualBase, mappedAddress + length);
} }
KernelMemoryCompatExports.RegisterReservedVirtualRange(mappedAddress, length);
return (int)OrbisGen2Result.ORBIS_GEN2_OK; return (int)OrbisGen2Result.ORBIS_GEN2_OK;
} }
-322
View File
@@ -1,322 +0,0 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
using SharpEmu.Libs.Kernel;
using System.Buffers.Binary;
using System.Threading;
namespace SharpEmu.Libs.Ngs2;
public static class Ngs2Exports
{
private const int OrbisNgs2ErrorInvalidOutAddress = unchecked((int)0x804A0053);
private const int OrbisNgs2ErrorInvalidSystemHandle = unchecked((int)0x804A0230);
private const int OrbisNgs2ErrorInvalidRackHandle = unchecked((int)0x804A0261);
private const int OrbisNgs2ErrorInvalidVoiceHandle = unchecked((int)0x804A0300);
private const ulong HandleStorageSize = 0x20;
private const int RenderBufferInfoSize = 0x18;
private const ulong MaximumRenderBufferSize = 16 * 1024 * 1024;
private static readonly object StateGate = new();
private static readonly Dictionary<ulong, SystemState> Systems = new();
private static readonly Dictionary<ulong, RackState> Racks = new();
private static readonly Dictionary<ulong, VoiceState> Voices = new();
private static long _nextUid;
private static long _renderCount;
private sealed record SystemState(uint Uid);
private sealed record RackState(ulong SystemHandle, uint RackId);
private sealed record VoiceState(ulong RackHandle, uint VoiceIndex);
[SysAbiExport(
Nid = "mPYgU4oYpuY",
ExportName = "sceNgs2SystemCreateWithAllocator",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceNgs2")]
public static int Ngs2SystemCreateWithAllocator(CpuContext ctx)
{
var outHandleAddress = ctx[CpuRegister.Rdx];
if (outHandleAddress == 0)
{
return SetReturn(ctx, OrbisNgs2ErrorInvalidOutAddress);
}
if (!TryCreateHandle(ctx, type: 1, ownerHandle: 0, out var handle) ||
!ctx.TryWriteUInt64(outHandleAddress, handle))
{
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
lock (StateGate)
{
Systems[handle] = new SystemState(unchecked((uint)Interlocked.Increment(ref _nextUid)));
}
return SetReturn(ctx, 0);
}
[SysAbiExport(
Nid = "u-WrYDaJA3k",
ExportName = "sceNgs2SystemDestroy",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceNgs2")]
public static int Ngs2SystemDestroy(CpuContext ctx)
{
var handle = ctx[CpuRegister.Rdi];
lock (StateGate)
{
if (!Systems.Remove(handle))
{
return SetReturn(ctx, OrbisNgs2ErrorInvalidSystemHandle);
}
var rackHandles = Racks
.Where(pair => pair.Value.SystemHandle == handle)
.Select(pair => pair.Key)
.ToArray();
foreach (var rackHandle in rackHandles)
{
RemoveRackLocked(rackHandle);
}
}
return SetReturn(ctx, 0);
}
[SysAbiExport(
Nid = "U546k6orxQo",
ExportName = "sceNgs2RackCreateWithAllocator",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceNgs2")]
public static int Ngs2RackCreateWithAllocator(CpuContext ctx)
{
var systemHandle = ctx[CpuRegister.Rdi];
var rackId = unchecked((uint)ctx[CpuRegister.Rsi]);
var outHandleAddress = ctx[CpuRegister.R8];
lock (StateGate)
{
if (!Systems.ContainsKey(systemHandle))
{
return SetReturn(ctx, OrbisNgs2ErrorInvalidSystemHandle);
}
}
if (outHandleAddress == 0)
{
return SetReturn(ctx, OrbisNgs2ErrorInvalidOutAddress);
}
if (!TryCreateHandle(ctx, type: 2, systemHandle, out var handle) ||
!ctx.TryWriteUInt64(outHandleAddress, handle))
{
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
lock (StateGate)
{
Racks[handle] = new RackState(systemHandle, rackId);
}
return SetReturn(ctx, 0);
}
[SysAbiExport(
Nid = "lCqD7oycmIM",
ExportName = "sceNgs2RackDestroy",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceNgs2")]
public static int Ngs2RackDestroy(CpuContext ctx)
{
var handle = ctx[CpuRegister.Rdi];
lock (StateGate)
{
if (!Racks.ContainsKey(handle))
{
return SetReturn(ctx, OrbisNgs2ErrorInvalidRackHandle);
}
RemoveRackLocked(handle);
}
return SetReturn(ctx, 0);
}
[SysAbiExport(
Nid = "MwmHz8pAdAo",
ExportName = "sceNgs2RackGetVoiceHandle",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceNgs2")]
public static int Ngs2RackGetVoiceHandle(CpuContext ctx)
{
var rackHandle = ctx[CpuRegister.Rdi];
var voiceIndex = unchecked((uint)ctx[CpuRegister.Rsi]);
var outHandleAddress = ctx[CpuRegister.Rdx];
lock (StateGate)
{
if (!Racks.ContainsKey(rackHandle))
{
return SetReturn(ctx, OrbisNgs2ErrorInvalidRackHandle);
}
var existing = Voices.FirstOrDefault(
pair => pair.Value.RackHandle == rackHandle && pair.Value.VoiceIndex == voiceIndex);
if (existing.Key != 0)
{
return ctx.TryWriteUInt64(outHandleAddress, existing.Key)
? SetReturn(ctx, 0)
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
}
if (outHandleAddress == 0)
{
return SetReturn(ctx, OrbisNgs2ErrorInvalidOutAddress);
}
if (!TryCreateHandle(ctx, type: 4, rackHandle, out var handle) ||
!ctx.TryWriteUInt64(outHandleAddress, handle))
{
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
lock (StateGate)
{
Voices[handle] = new VoiceState(rackHandle, voiceIndex);
}
return SetReturn(ctx, 0);
}
[SysAbiExport(
Nid = "uu94irFOGpA",
ExportName = "sceNgs2VoiceControl",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceNgs2")]
public static int Ngs2VoiceControl(CpuContext ctx)
{
lock (StateGate)
{
return SetReturn(
ctx,
Voices.ContainsKey(ctx[CpuRegister.Rdi]) ? 0 : OrbisNgs2ErrorInvalidVoiceHandle);
}
}
[SysAbiExport(
Nid = "AbYvTOZ8Pts",
ExportName = "sceNgs2VoiceRunCommands",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceNgs2")]
public static int Ngs2VoiceRunCommands(CpuContext ctx) => Ngs2VoiceControl(ctx);
[SysAbiExport(
Nid = "i0VnXM-C9fc",
ExportName = "sceNgs2SystemRender",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceNgs2")]
public static int Ngs2SystemRender(CpuContext ctx)
{
var systemHandle = ctx[CpuRegister.Rdi];
var bufferInfoAddress = ctx[CpuRegister.Rsi];
var bufferInfoCount = unchecked((uint)ctx[CpuRegister.Rdx]);
lock (StateGate)
{
if (!Systems.ContainsKey(systemHandle))
{
return SetReturn(ctx, OrbisNgs2ErrorInvalidSystemHandle);
}
}
if (bufferInfoCount != 0 && bufferInfoAddress == 0)
{
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
for (uint i = 0; i < bufferInfoCount; i++)
{
var entryAddress = bufferInfoAddress + (i * RenderBufferInfoSize);
if (!ctx.TryReadUInt64(entryAddress, out var bufferAddress) ||
!ctx.TryReadUInt64(entryAddress + 8, out var bufferSize))
{
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
if (bufferAddress != 0 && bufferSize != 0)
{
if (bufferSize > MaximumRenderBufferSize || !TryClearGuestBuffer(ctx, bufferAddress, bufferSize))
{
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
}
}
var count = Interlocked.Increment(ref _renderCount);
if (ShouldTrace() && (count <= 4 || count % 10_000 == 0))
{
Console.Error.WriteLine(
$"[LOADER][TRACE] ngs2.render#{count} system=0x{systemHandle:X16} buffers={bufferInfoCount}");
}
return SetReturn(ctx, 0);
}
private static bool TryCreateHandle(CpuContext ctx, uint type, ulong ownerHandle, out ulong handle)
{
handle = 0;
if (!KernelMemoryCompatExports.TryAllocateHleData(ctx, HandleStorageSize, 16, out handle))
{
return false;
}
Span<byte> data = stackalloc byte[(int)HandleStorageSize];
data.Clear();
BinaryPrimitives.WriteUInt64LittleEndian(data[0..8], handle);
BinaryPrimitives.WriteUInt64LittleEndian(data[8..16], ownerHandle);
BinaryPrimitives.WriteUInt32LittleEndian(data[16..20], 1);
BinaryPrimitives.WriteUInt32LittleEndian(data[24..28], type);
return ctx.Memory.TryWrite(handle, data);
}
private static bool TryClearGuestBuffer(CpuContext ctx, ulong address, ulong length)
{
Span<byte> zeroes = stackalloc byte[4096];
zeroes.Clear();
for (ulong offset = 0; offset < length;)
{
var chunkSize = (int)Math.Min((ulong)zeroes.Length, length - offset);
if (!ctx.Memory.TryWrite(address + offset, zeroes[..chunkSize]))
{
return false;
}
offset += unchecked((uint)chunkSize);
}
return true;
}
private static void RemoveRackLocked(ulong rackHandle)
{
Racks.Remove(rackHandle);
foreach (var voiceHandle in Voices
.Where(pair => pair.Value.RackHandle == rackHandle)
.Select(pair => pair.Key)
.ToArray())
{
Voices.Remove(voiceHandle);
}
}
private static bool ShouldTrace() =>
string.Equals(
Environment.GetEnvironmentVariable("SHARPEMU_LOG_NGS2"),
"1",
StringComparison.Ordinal);
private static int SetReturn(CpuContext ctx, int result)
{
ctx[CpuRegister.Rax] = unchecked((ulong)result);
return result;
}
}
+4 -79
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);
} }
@@ -160,33 +158,14 @@ public static class PadExports
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT); : SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
} }
[SysAbiExport(
Nid = "W2G-yoyMF5U",
ExportName = "scePadSetVibrationMode",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libScePad")]
public static int PadSetVibrationMode(CpuContext ctx)
{
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_OK);
}
private static bool WriteNeutralPadData(CpuContext ctx, ulong dataAddress) private static bool WriteNeutralPadData(CpuContext ctx, ulong dataAddress)
{ {
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();
@@ -206,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;
}
} }
+1 -126
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE; using SharpEmu.HLE;
using SharpEmu.Libs.Kernel;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Text; using System.Text;
@@ -11,8 +10,6 @@ namespace SharpEmu.Libs.SaveData;
public static class SaveDataExports public static class SaveDataExports
{ {
private const int OrbisSaveDataErrorParameter = unchecked((int)0x809F0000); private const int OrbisSaveDataErrorParameter = unchecked((int)0x809F0000);
private const int OrbisSaveDataErrorExists = unchecked((int)0x809F0007);
private const int OrbisSaveDataErrorNotFound = unchecked((int)0x809F0008);
private const int OrbisSaveDataErrorInternal = unchecked((int)0x809F000B); private const int OrbisSaveDataErrorInternal = unchecked((int)0x809F000B);
private const int SaveDataTitleIdSize = 10; private const int SaveDataTitleIdSize = 10;
private const int SaveDataDirNameSize = 32; private const int SaveDataDirNameSize = 32;
@@ -26,9 +23,6 @@ public static class SaveDataExports
private const ulong ResultInfosOffset = 0x20; private const ulong ResultInfosOffset = 0x20;
private const uint SortKeyFreeBlocks = 5; private const uint SortKeyFreeBlocks = 5;
private const uint SortOrderDescent = 1; private const uint SortOrderDescent = 1;
private const uint MountModeCreate = 1u << 2;
private const uint MountModeCreate2 = 1u << 5;
private const int MountResultSize = 0x40;
private static readonly object _stateGate = new(); private static readonly object _stateGate = new();
private static string? _titleId; private static string? _titleId;
@@ -155,125 +149,6 @@ public static class SaveDataExports
} }
} }
[SysAbiExport(
Nid = "ZP4e7rlzOUk",
ExportName = "sceSaveDataMount3",
Target = Generation.Gen5,
LibraryName = "libSceSaveData")]
public static int SaveDataMount3(CpuContext ctx)
{
var mountAddress = ctx[CpuRegister.Rdi];
var resultAddress = ctx[CpuRegister.Rsi];
if (mountAddress == 0 || resultAddress == 0)
{
return SetReturn(ctx, OrbisSaveDataErrorParameter);
}
if (!TryReadInt32(ctx, mountAddress, out var userId) ||
!ctx.TryReadUInt64(mountAddress + 0x08, out var dirNameAddress) ||
!ctx.TryReadUInt64(mountAddress + 0x10, out var blocks) ||
!ctx.TryReadUInt64(mountAddress + 0x18, out var systemBlocks) ||
!TryReadUInt32(ctx, mountAddress + 0x20, out var mountMode) ||
!TryReadUInt32(ctx, mountAddress + 0x24, out var resource) ||
!TryReadUInt32(ctx, mountAddress + 0x28, out var mode) ||
dirNameAddress == 0 ||
!TryReadFixedAscii(ctx, dirNameAddress, SaveDataDirNameSize, out var dirName))
{
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
if (userId < 0 || string.IsNullOrWhiteSpace(dirName))
{
return SetReturn(ctx, OrbisSaveDataErrorParameter);
}
try
{
var titleId = ResolveConfiguredTitleId();
var savePath = Path.Combine(
ResolveTitleSaveRoot(userId, titleId),
SanitizePathSegment(dirName));
var existed = Directory.Exists(savePath);
var create = (mountMode & MountModeCreate) != 0;
var createIfMissing = (mountMode & MountModeCreate2) != 0;
if (!existed && !create && !createIfMissing)
{
return SetReturn(ctx, OrbisSaveDataErrorNotFound);
}
if (existed && create)
{
return SetReturn(ctx, OrbisSaveDataErrorExists);
}
if (!existed)
{
Directory.CreateDirectory(savePath);
}
const string mountPoint = "/savedata0";
KernelMemoryCompatExports.RegisterGuestPathMount(mountPoint, savePath);
Span<byte> result = stackalloc byte[MountResultSize];
result.Clear();
WriteAscii(result[..16], mountPoint);
BinaryPrimitives.WriteUInt32LittleEndian(result[0x1C..], createIfMissing && !existed ? 1u : 0u);
if (!ctx.Memory.TryWrite(resultAddress, result))
{
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
TraceSaveData(
$"mount3 user={userId} title={titleId} dir={dirName} blocks={blocks} " +
$"system_blocks={systemBlocks} mount_mode=0x{mountMode:X} resource={resource} mode={mode} " +
$"mount_point={mountPoint} created={!existed} root='{savePath}'");
return SetReturn(ctx, 0);
}
catch (IOException)
{
return SetReturn(ctx, OrbisSaveDataErrorInternal);
}
catch (UnauthorizedAccessException)
{
return SetReturn(ctx, OrbisSaveDataErrorInternal);
}
catch (ArgumentException)
{
return SetReturn(ctx, OrbisSaveDataErrorParameter);
}
}
private static int _nextTransactionResource;
[SysAbiExport(
Nid = "gjRZNnw0JPE",
ExportName = "sceSaveDataCreateTransactionResource",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceSaveData")]
public static int SaveDataCreateTransactionResource(CpuContext ctx)
{
var userId = unchecked((int)ctx[CpuRegister.Rdi]);
var reserved = ctx[CpuRegister.Rsi];
var resourceAddress = ctx[CpuRegister.Rdx];
if (resourceAddress == 0)
{
return SetReturn(ctx, OrbisSaveDataErrorParameter);
}
var id = (uint)Interlocked.Increment(ref _nextTransactionResource);
if (!TryWriteUInt32(ctx, resourceAddress, id))
{
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
TraceSaveData(
$"create_transaction_resource user={userId} reserved=0x{reserved:X} resource_addr=0x{resourceAddress:X} id={id}");
return SetReturn(ctx, 0);
}
private static bool TryReadSearchCond(CpuContext ctx, ulong address, out SearchCond cond) private static bool TryReadSearchCond(CpuContext ctx, ulong address, out SearchCond cond)
{ {
cond = default; cond = default;
@@ -430,7 +305,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);
} }
-1
View File
@@ -10,7 +10,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
<ItemGroup> <ItemGroup>
<PackageReference Include="Silk.NET.Vulkan" /> <PackageReference Include="Silk.NET.Vulkan" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" /> <PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
<PackageReference Include="Silk.NET.Windowing" /> <PackageReference Include="Silk.NET.Windowing" />
</ItemGroup> </ItemGroup>
@@ -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)
File diff suppressed because it is too large Load Diff
-10
View File
@@ -11,16 +11,6 @@
"Silk.NET.Core": "2.23.0" "Silk.NET.Core": "2.23.0"
} }
}, },
"Silk.NET.Vulkan.Extensions.EXT": {
"type": "Direct",
"requested": "[2.23.0, )",
"resolved": "2.23.0",
"contentHash": "+Oth189ksRiL6HvGCwIdnsYHawqrbO8y49u1H61z3wsfcHhQZeVDYe/wF5LD7fk3NcdgDvwFD3mLm1QWhdZySw==",
"dependencies": {
"Silk.NET.Core": "2.23.0",
"Silk.NET.Vulkan": "2.23.0"
}
},
"Silk.NET.Vulkan.Extensions.KHR": { "Silk.NET.Vulkan.Extensions.KHR": {
"type": "Direct", "type": "Direct",
"requested": "[2.23.0, )", "requested": "[2.23.0, )",