mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-29 22:19:39 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c30b4763c6 | |||
| aa152de934 | |||
| 39dd12a11b | |||
| 4c6fc3052c |
+1
-1
@@ -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 |
@@ -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
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ arm64/
|
|||||||
*.userosscache
|
*.userosscache
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
|
|
||||||
.packages
|
|
||||||
packages/
|
packages/
|
||||||
*.nupkg
|
*.nupkg
|
||||||
.nuget/
|
.nuget/
|
||||||
|
|||||||
+1
-3
@@ -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 |
@@ -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>
|
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
@@ -1185,11 +1180,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 +1194,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;
|
||||||
|
|||||||
@@ -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,13 +473,9 @@ 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(
|
if (TryResolveRegionOffset(virtualAddress, (ulong)destination.Length, region, out var offset))
|
||||||
virtualAddress,
|
|
||||||
(ulong)destination.Length,
|
|
||||||
region,
|
|
||||||
out var offset))
|
|
||||||
{
|
{
|
||||||
var srcPtr = (void*)(region.VirtualAddress + offset);
|
var srcPtr = (void*)(region.VirtualAddress + offset);
|
||||||
if (destination.IsEmpty)
|
if (destination.IsEmpty)
|
||||||
@@ -498,9 +494,9 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
if (!CanReadWithoutProtectionChange((ulong)srcPtr, (ulong)destination.Length, region))
|
if (!CanReadWithoutProtectionChange((ulong)srcPtr, (ulong)destination.Length, region))
|
||||||
{
|
{
|
||||||
requiresExclusiveAccess = true;
|
requiresExclusiveAccess = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
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,13 +533,9 @@ 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(
|
if (TryResolveRegionOffset(virtualAddress, (ulong)source.Length, region, out var offset))
|
||||||
virtualAddress,
|
|
||||||
(ulong)source.Length,
|
|
||||||
region,
|
|
||||||
out var offset))
|
|
||||||
{
|
{
|
||||||
var destPtr = (void*)(region.VirtualAddress + offset);
|
var destPtr = (void*)(region.VirtualAddress + offset);
|
||||||
if (source.IsEmpty)
|
if (source.IsEmpty)
|
||||||
@@ -562,9 +554,9 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
if (!CanWriteWithoutProtectionChange((ulong)destPtr, (ulong)source.Length, region))
|
if (!CanWriteWithoutProtectionChange((ulong)destPtr, (ulong)source.Length, region))
|
||||||
{
|
{
|
||||||
requiresExclusiveAccess = true;
|
requiresExclusiveAccess = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -142,7 +142,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 +155,6 @@ public static class AgcExports
|
|||||||
(ulong Cs, ulong State, uint LocalX, uint LocalY, uint LocalZ),
|
(ulong Cs, ulong State, uint LocalX, uint LocalY, uint LocalZ),
|
||||||
byte[]> _computeSpirvCache = new();
|
byte[]> _computeSpirvCache = new();
|
||||||
private static readonly Dictionary<ulong, ulong> _shaderHeadersByCode = new();
|
private static readonly Dictionary<ulong, ulong> _shaderHeadersByCode = new();
|
||||||
private static readonly bool _traceAgc = string.Equals(
|
|
||||||
Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"),
|
|
||||||
"1",
|
|
||||||
StringComparison.Ordinal);
|
|
||||||
private static readonly bool _traceAgcShader =
|
|
||||||
_traceAgc ||
|
|
||||||
string.Equals(
|
|
||||||
Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC_SHADER"),
|
|
||||||
"1",
|
|
||||||
StringComparison.Ordinal);
|
|
||||||
private static long _dcbWriteDataTraceCount;
|
private static long _dcbWriteDataTraceCount;
|
||||||
private static long _dcbWaitRegMemTraceCount;
|
private static long _dcbWaitRegMemTraceCount;
|
||||||
private static long _createShaderTraceCount;
|
private static long _createShaderTraceCount;
|
||||||
@@ -2798,7 +2788,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;
|
||||||
}
|
}
|
||||||
@@ -2882,9 +2872,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,
|
||||||
@@ -3044,11 +3033,6 @@ 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,
|
||||||
@@ -3493,8 +3477,7 @@ public static class AgcExports
|
|||||||
',',
|
',',
|
||||||
draw.VertexInputs.Select(input =>
|
draw.VertexInputs.Select(input =>
|
||||||
$"{input.Location}:pc=0x{input.Pc:X}:0x{input.BaseAddress:X16}" +
|
$"{input.Location}:pc=0x{input.Pc:X}:0x{input.BaseAddress:X16}" +
|
||||||
$":stride{input.Stride}:off{input.OffsetBytes}:c{input.ComponentCount}" +
|
$":stride{input.Stride}:off{input.OffsetBytes}:c{input.ComponentCount}"));
|
||||||
$":fmt{input.DataFormat}/num{input.NumberFormat}"));
|
|
||||||
var scissor = draw.RenderState.Scissor is { } drawScissor
|
var scissor = draw.RenderState.Scissor is { } drawScissor
|
||||||
? $"{drawScissor.X},{drawScissor.Y},{drawScissor.Width}x{drawScissor.Height}"
|
? $"{drawScissor.X},{drawScissor.Y},{drawScissor.Width}x{drawScissor.Height}"
|
||||||
: "full";
|
: "full";
|
||||||
@@ -3503,29 +3486,6 @@ public static class AgcExports
|
|||||||
$"{drawViewport.Width:0.###}x{drawViewport.Height:0.###}:" +
|
$"{drawViewport.Width:0.###}x{drawViewport.Height:0.###}:" +
|
||||||
$"{drawViewport.MinDepth:0.###}-{drawViewport.MaxDepth:0.###}"
|
$"{drawViewport.MinDepth:0.###}-{drawViewport.MaxDepth:0.###}"
|
||||||
: "full";
|
: "full";
|
||||||
var rasterRegisters = new (string Name, uint Offset)[]
|
|
||||||
{
|
|
||||||
("screen_tl", PaScScreenScissorTl),
|
|
||||||
("screen_br", PaScScreenScissorBr),
|
|
||||||
("window_off", PaScWindowOffset),
|
|
||||||
("window_tl", PaScWindowScissorTl),
|
|
||||||
("window_br", PaScWindowScissorBr),
|
|
||||||
("generic_tl", PaScGenericScissorTl),
|
|
||||||
("generic_br", PaScGenericScissorBr),
|
|
||||||
("vport_tl", PaScVportScissor0Tl),
|
|
||||||
("vport_br", PaScVportScissor0Br),
|
|
||||||
("mode", PaScModeCntl0),
|
|
||||||
("xscale", PaClVportXScale),
|
|
||||||
("xoffset", PaClVportXOffset),
|
|
||||||
("yscale", PaClVportYScale),
|
|
||||||
("yoffset", PaClVportYOffset),
|
|
||||||
};
|
|
||||||
var raster = string.Join(
|
|
||||||
',',
|
|
||||||
rasterRegisters.Select(entry =>
|
|
||||||
state.CxRegisters.TryGetValue(entry.Offset, out var value)
|
|
||||||
? $"{entry.Name}=0x{value:X8}"
|
|
||||||
: $"{entry.Name}=missing"));
|
|
||||||
var blend = draw.RenderState.Blend;
|
var blend = draw.RenderState.Blend;
|
||||||
TraceAgcShader(
|
TraceAgcShader(
|
||||||
$"agc.shader_draw es=0x{draw.ExportShaderAddress:X16} " +
|
$"agc.shader_draw es=0x{draw.ExportShaderAddress:X16} " +
|
||||||
@@ -3533,7 +3493,6 @@ public static class AgcExports
|
|||||||
$"primitive=0x{draw.PrimitiveType:X} " +
|
$"primitive=0x{draw.PrimitiveType:X} " +
|
||||||
$"blend={(blend.Enable ? 1 : 0)}:{blend.ColorSrcFactor}/{blend.ColorDstFactor}/{blend.ColorFunc} " +
|
$"blend={(blend.Enable ? 1 : 0)}:{blend.ColorSrcFactor}/{blend.ColorDstFactor}/{blend.ColorFunc} " +
|
||||||
$"write_mask=0x{blend.WriteMask:X} scissor={scissor} viewport={viewport} " +
|
$"write_mask=0x{blend.WriteMask:X} scissor={scissor} viewport={viewport} " +
|
||||||
$"raster=[{raster}] " +
|
|
||||||
$"ps_ena=0x{psInputEna:X8} ps_addr=0x{psInputAddr:X8} " +
|
$"ps_ena=0x{psInputEna:X8} ps_addr=0x{psInputAddr:X8} " +
|
||||||
$"targets=[{targets}] textures=[{textures}] " +
|
$"targets=[{targets}] textures=[{textures}] " +
|
||||||
$"buffers=[{buffers}] vertex=[{vertexInputs}] indices=[{indices}]");
|
$"buffers=[{buffers}] vertex=[{vertexInputs}] indices=[{indices}]");
|
||||||
@@ -3591,8 +3550,6 @@ public static class AgcExports
|
|||||||
buffers[index] = new VulkanGuestVertexBuffer(
|
buffers[index] = new VulkanGuestVertexBuffer(
|
||||||
binding.Location,
|
binding.Location,
|
||||||
binding.ComponentCount,
|
binding.ComponentCount,
|
||||||
binding.DataFormat,
|
|
||||||
binding.NumberFormat,
|
|
||||||
binding.BaseAddress,
|
binding.BaseAddress,
|
||||||
binding.Stride,
|
binding.Stride,
|
||||||
binding.OffsetBytes,
|
binding.OffsetBytes,
|
||||||
@@ -3622,10 +3579,7 @@ public static class AgcExports
|
|||||||
}
|
}
|
||||||
|
|
||||||
var sourceWidth = descriptor.TileMode == 0
|
var sourceWidth = descriptor.TileMode == 0
|
||||||
? GetLinearTexturePitch(
|
? Math.Max(descriptor.Width, descriptor.Pitch)
|
||||||
Math.Max(descriptor.Width, descriptor.Pitch),
|
|
||||||
descriptor.Height,
|
|
||||||
descriptor.Format)
|
|
||||||
: descriptor.Width;
|
: descriptor.Width;
|
||||||
var sourceByteCount = GetTextureByteCount(
|
var sourceByteCount = GetTextureByteCount(
|
||||||
descriptor.Format,
|
descriptor.Format,
|
||||||
@@ -3639,31 +3593,6 @@ public static class AgcExports
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isStorage &&
|
|
||||||
descriptor.Address != 0 &&
|
|
||||||
VulkanVideoPresenter.IsGuestImageAvailable(
|
|
||||||
descriptor.Address,
|
|
||||||
descriptor.Format,
|
|
||||||
descriptor.NumberType))
|
|
||||||
{
|
|
||||||
texture = new VulkanGuestDrawTexture(
|
|
||||||
descriptor.Address,
|
|
||||||
descriptor.Width,
|
|
||||||
descriptor.Height,
|
|
||||||
descriptor.Format,
|
|
||||||
descriptor.NumberType,
|
|
||||||
[],
|
|
||||||
IsFallback: false,
|
|
||||||
IsStorage: false,
|
|
||||||
MipLevels: descriptor.MipLevels,
|
|
||||||
MipLevel: mipLevel,
|
|
||||||
Pitch: sourceWidth,
|
|
||||||
TileMode: descriptor.TileMode,
|
|
||||||
DstSelect: descriptor.DstSelect,
|
|
||||||
Sampler: ToVulkanSampler(samplerDescriptor));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isStorage)
|
if (isStorage)
|
||||||
{
|
{
|
||||||
var initialPixels = Array.Empty<byte>();
|
var initialPixels = Array.Empty<byte>();
|
||||||
@@ -3688,7 +3617,7 @@ public static class AgcExports
|
|||||||
IsStorage: true,
|
IsStorage: true,
|
||||||
MipLevels: descriptor.MipLevels,
|
MipLevels: descriptor.MipLevels,
|
||||||
MipLevel: mipLevel,
|
MipLevel: mipLevel,
|
||||||
Pitch: sourceWidth,
|
Pitch: descriptor.Pitch,
|
||||||
TileMode: descriptor.TileMode,
|
TileMode: descriptor.TileMode,
|
||||||
DstSelect: descriptor.DstSelect,
|
DstSelect: descriptor.DstSelect,
|
||||||
Sampler: ToVulkanSampler(samplerDescriptor));
|
Sampler: ToVulkanSampler(samplerDescriptor));
|
||||||
@@ -3734,7 +3663,7 @@ public static class AgcExports
|
|||||||
IsStorage: isStorage,
|
IsStorage: isStorage,
|
||||||
MipLevels: descriptor.MipLevels,
|
MipLevels: descriptor.MipLevels,
|
||||||
MipLevel: mipLevel,
|
MipLevel: mipLevel,
|
||||||
Pitch: sourceWidth,
|
Pitch: descriptor.Pitch,
|
||||||
TileMode: descriptor.TileMode,
|
TileMode: descriptor.TileMode,
|
||||||
DstSelect: descriptor.DstSelect,
|
DstSelect: descriptor.DstSelect,
|
||||||
Sampler: ToVulkanSampler(samplerDescriptor));
|
Sampler: ToVulkanSampler(samplerDescriptor));
|
||||||
@@ -4397,28 +4326,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,
|
||||||
@@ -4687,10 +4594,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
|
||||||
@@ -5440,7 +5343,7 @@ public static class AgcExports
|
|||||||
|
|
||||||
private static void TraceAgc(string message)
|
private static void TraceAgc(string message)
|
||||||
{
|
{
|
||||||
if (!_traceAgc)
|
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"), "1", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -5450,7 +5353,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;
|
||||||
}
|
}
|
||||||
@@ -5463,12 +5367,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,
|
||||||
|
|||||||
@@ -276,8 +276,6 @@ internal sealed record Gen5VertexInputBinding(
|
|||||||
uint Pc,
|
uint Pc,
|
||||||
uint Location,
|
uint Location,
|
||||||
uint ComponentCount,
|
uint ComponentCount,
|
||||||
uint DataFormat,
|
|
||||||
uint NumberFormat,
|
|
||||||
ulong BaseAddress,
|
ulong BaseAddress,
|
||||||
uint Stride,
|
uint Stride,
|
||||||
uint OffsetBytes,
|
uint OffsetBytes,
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ internal static class Gen5ShaderScalarEvaluator
|
|||||||
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,
|
||||||
@@ -402,21 +400,14 @@ internal static class Gen5ShaderScalarEvaluator
|
|||||||
return false;
|
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(
|
binding = new Gen5VertexInputBinding(
|
||||||
instruction.Pc,
|
instruction.Pc,
|
||||||
location,
|
location,
|
||||||
control.DwordCount,
|
control.DwordCount,
|
||||||
bindingDataFormat,
|
|
||||||
bindingNumberFormat,
|
|
||||||
descriptor.BaseAddress,
|
descriptor.BaseAddress,
|
||||||
bindingStride,
|
descriptor.Stride,
|
||||||
bindingOffset,
|
unchecked((uint)control.OffsetBytes + scalarOffset),
|
||||||
bindingData);
|
data);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -812,11 +803,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 +829,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 +846,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 +926,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 +935,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;
|
|
||||||
result = (uint)wide;
|
|
||||||
scalarConditionCode = wide > uint.MaxValue;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case "SLshl2AddU32":
|
case "SLshl2AddU32":
|
||||||
{
|
result = (left << 2) + right;
|
||||||
var wide = ((ulong)left << 2) + right;
|
|
||||||
result = (uint)wide;
|
|
||||||
scalarConditionCode = wide > uint.MaxValue;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case "SLshl3AddU32":
|
case "SLshl3AddU32":
|
||||||
{
|
result = (left << 3) + right;
|
||||||
var wide = ((ulong)left << 3) + right;
|
|
||||||
result = (uint)wide;
|
|
||||||
scalarConditionCode = wide > uint.MaxValue;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case "SLshl4AddU32":
|
case "SLshl4AddU32":
|
||||||
{
|
result = (left << 4) + right;
|
||||||
var wide = ((ulong)left << 4) + right;
|
|
||||||
result = (uint)wide;
|
|
||||||
scalarConditionCode = wide > uint.MaxValue;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case "SPackLlB32B16":
|
case "SPackLlB32B16":
|
||||||
result = (left & 0xFFFFu) | (right << 16);
|
result = (left & 0xFFFFu) | (right << 16);
|
||||||
break;
|
break;
|
||||||
@@ -1031,8 +993,7 @@ internal static class Gen5ShaderScalarEvaluator
|
|||||||
"SNandSaveexecB64" or
|
"SNandSaveexecB64" or
|
||||||
"SNorSaveexecB64" or
|
"SNorSaveexecB64" or
|
||||||
"SXnorSaveexecB64" or
|
"SXnorSaveexecB64" or
|
||||||
"SAndn1SaveexecB64" or
|
"SAndn1SaveexecB64"))
|
||||||
"SOrn1SaveexecB64"))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1060,12 +1021,11 @@ internal static class Gen5ShaderScalarEvaluator
|
|||||||
"SAndSaveexecB64" => oldExec & source,
|
"SAndSaveexecB64" => oldExec & source,
|
||||||
"SOrSaveexecB64" => oldExec | source,
|
"SOrSaveexecB64" => oldExec | source,
|
||||||
"SXorSaveexecB64" => oldExec ^ source,
|
"SXorSaveexecB64" => oldExec ^ source,
|
||||||
"SAndn1SaveexecB64" => ~source & oldExec,
|
"SAndn1SaveexecB64" => ~oldExec & source,
|
||||||
"SAndn2SaveexecB64" => source & ~oldExec,
|
"SAndn2SaveexecB64" => oldExec & ~source,
|
||||||
"SOrn1SaveexecB64" => ~source | oldExec,
|
"SOrn2SaveexecB64" => oldExec | ~source,
|
||||||
"SOrn2SaveexecB64" => source | ~oldExec,
|
"SNandSaveexecB64" => ~(oldExec & source),
|
||||||
"SNandSaveexecB64" => ~(source & oldExec),
|
"SNorSaveexecB64" => ~(oldExec | source),
|
||||||
"SNorSaveexecB64" => ~(source | oldExec),
|
|
||||||
_ => ~(oldExec ^ source),
|
_ => ~(oldExec ^ source),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1135,12 +1095,6 @@ internal static class Gen5ShaderScalarEvaluator
|
|||||||
|
|
||||||
private static ulong MaskWaveValue(ulong value) => value & RdnaWaveMask;
|
private static ulong MaskWaveValue(ulong value) => value & RdnaWaveMask;
|
||||||
|
|
||||||
private static bool SignedAddOverflow(uint left, uint right, uint result) =>
|
|
||||||
((left ^ result) & (right ^ result) & 0x80000000u) != 0;
|
|
||||||
|
|
||||||
private static bool SignedSubOverflow(uint left, uint right, uint result) =>
|
|
||||||
((left ^ right) & (left ^ result) & 0x80000000u) != 0;
|
|
||||||
|
|
||||||
private static bool TryExecuteScalarCompare(
|
private static bool TryExecuteScalarCompare(
|
||||||
Gen5ShaderInstruction instruction,
|
Gen5ShaderInstruction instruction,
|
||||||
uint[] registers,
|
uint[] registers,
|
||||||
@@ -1462,7 +1416,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 +1428,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,
|
||||||
|
|||||||
@@ -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,53 +117,18 @@ 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (program is null)
|
|
||||||
{
|
|
||||||
if (!TryDecodeProgram(ctx, shaderAddress, out program, out error))
|
|
||||||
{
|
{
|
||||||
return false;
|
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;
|
|
||||||
lock (cache.Gate)
|
|
||||||
{
|
|
||||||
metadataCached = cache.Metadata.TryGetValue(shaderHeaderAddress, out metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!metadataCached)
|
|
||||||
{
|
|
||||||
if (Gen5ShaderMetadataReader.TryRead(
|
|
||||||
ctx,
|
|
||||||
shaderHeaderAddress,
|
|
||||||
out var decodedMetadata))
|
|
||||||
{
|
{
|
||||||
metadata = decodedMetadata;
|
metadata = decodedMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (cache.Gate)
|
|
||||||
{
|
|
||||||
cache.Metadata.TryAdd(shaderHeaderAddress, metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var userData = new uint[GetUserDataDwordCount(metadata)];
|
var userData = new uint[GetUserDataDwordCount(metadata)];
|
||||||
for (uint index = 0; index < userData.Length; index++)
|
for (uint index = 0; index < userData.Length; index++)
|
||||||
{
|
{
|
||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -212,26 +212,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);
|
||||||
@@ -649,29 +635,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 +945,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 +971,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 +985,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 +1021,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 +1033,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 +1061,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 +1068,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 +1075,22 @@ internal static partial class Gen5SpirvTranslator
|
|||||||
_uintType,
|
_uintType,
|
||||||
left,
|
left,
|
||||||
right);
|
right);
|
||||||
Store(_scc, IsNotZero(result));
|
|
||||||
break;
|
break;
|
||||||
case "SAndn2B32":
|
case "SAndn2B32":
|
||||||
result = BitwiseAnd(
|
result = BitwiseAnd(
|
||||||
left,
|
left,
|
||||||
_module.AddInstruction(SpirvOp.Not, _uintType, right));
|
_module.AddInstruction(SpirvOp.Not, _uintType, right));
|
||||||
Store(_scc, IsNotZero(result));
|
|
||||||
break;
|
|
||||||
case "SOrn2B32":
|
|
||||||
result = _module.AddInstruction(
|
|
||||||
SpirvOp.BitwiseOr,
|
|
||||||
_uintType,
|
|
||||||
left,
|
|
||||||
_module.AddInstruction(SpirvOp.Not, _uintType, right));
|
|
||||||
Store(_scc, IsNotZero(result));
|
|
||||||
break;
|
|
||||||
case "SNandB32":
|
|
||||||
result = _module.AddInstruction(
|
|
||||||
SpirvOp.Not,
|
|
||||||
_uintType,
|
|
||||||
BitwiseAnd(left, right));
|
|
||||||
Store(_scc, IsNotZero(result));
|
|
||||||
break;
|
|
||||||
case "SNorB32":
|
|
||||||
result = _module.AddInstruction(
|
|
||||||
SpirvOp.Not,
|
|
||||||
_uintType,
|
|
||||||
_module.AddInstruction(
|
|
||||||
SpirvOp.BitwiseOr,
|
|
||||||
_uintType,
|
|
||||||
left,
|
|
||||||
right));
|
|
||||||
Store(_scc, IsNotZero(result));
|
|
||||||
break;
|
|
||||||
case "SXnorB32":
|
|
||||||
result = _module.AddInstruction(
|
|
||||||
SpirvOp.Not,
|
|
||||||
_uintType,
|
|
||||||
_module.AddInstruction(
|
|
||||||
SpirvOp.BitwiseXor,
|
|
||||||
_uintType,
|
|
||||||
left,
|
|
||||||
right));
|
|
||||||
Store(_scc, IsNotZero(result));
|
|
||||||
break;
|
break;
|
||||||
case "SLshlB32":
|
case "SLshlB32":
|
||||||
result = ShiftLeftLogical(left, right);
|
result = ShiftLeftLogical(left, right);
|
||||||
Store(_scc, IsNotZero(result));
|
|
||||||
break;
|
break;
|
||||||
case "SLshrB32":
|
case "SLshrB32":
|
||||||
result = ShiftRightLogical(
|
result = ShiftRightLogical(
|
||||||
left,
|
left,
|
||||||
BitwiseAnd(right, UInt(31)));
|
BitwiseAnd(right, UInt(31)));
|
||||||
Store(_scc, IsNotZero(result));
|
|
||||||
break;
|
break;
|
||||||
case "SAshrI32":
|
case "SAshrI32":
|
||||||
result = ShiftRightArithmetic(left, right);
|
result = ShiftRightArithmetic(left, right);
|
||||||
Store(_scc, IsNotZero(result));
|
|
||||||
break;
|
break;
|
||||||
case "SBfmB32":
|
case "SBfmB32":
|
||||||
result = _module.AddInstruction(
|
result = _module.AddInstruction(
|
||||||
@@ -1227,7 +1133,6 @@ internal static partial class Gen5SpirvTranslator
|
|||||||
left,
|
left,
|
||||||
offset,
|
offset,
|
||||||
width);
|
width);
|
||||||
Store(_scc, IsNotZero(result));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "SCselectB32":
|
case "SCselectB32":
|
||||||
@@ -1240,47 +1145,9 @@ internal static partial class Gen5SpirvTranslator
|
|||||||
break;
|
break;
|
||||||
case "SMinU32":
|
case "SMinU32":
|
||||||
result = Ext(38, _uintType, left, right);
|
result = Ext(38, _uintType, left, right);
|
||||||
Store(
|
|
||||||
_scc,
|
|
||||||
_module.AddInstruction(
|
|
||||||
SpirvOp.ULessThan,
|
|
||||||
_boolType,
|
|
||||||
left,
|
|
||||||
right));
|
|
||||||
break;
|
|
||||||
case "SMinI32":
|
|
||||||
result = Bitcast(
|
|
||||||
_uintType,
|
|
||||||
Ext(39, _intType, Bitcast(_intType, left), Bitcast(_intType, right)));
|
|
||||||
Store(
|
|
||||||
_scc,
|
|
||||||
_module.AddInstruction(
|
|
||||||
SpirvOp.SLessThan,
|
|
||||||
_boolType,
|
|
||||||
Bitcast(_intType, left),
|
|
||||||
Bitcast(_intType, right)));
|
|
||||||
break;
|
break;
|
||||||
case "SMaxU32":
|
case "SMaxU32":
|
||||||
result = Ext(41, _uintType, left, right);
|
result = Ext(41, _uintType, left, right);
|
||||||
Store(
|
|
||||||
_scc,
|
|
||||||
_module.AddInstruction(
|
|
||||||
SpirvOp.UGreaterThan,
|
|
||||||
_boolType,
|
|
||||||
left,
|
|
||||||
right));
|
|
||||||
break;
|
|
||||||
case "SMaxI32":
|
|
||||||
result = Bitcast(
|
|
||||||
_uintType,
|
|
||||||
Ext(42, _intType, Bitcast(_intType, left), Bitcast(_intType, right)));
|
|
||||||
Store(
|
|
||||||
_scc,
|
|
||||||
_module.AddInstruction(
|
|
||||||
SpirvOp.SGreaterThan,
|
|
||||||
_boolType,
|
|
||||||
Bitcast(_intType, left),
|
|
||||||
Bitcast(_intType, right)));
|
|
||||||
break;
|
break;
|
||||||
case "SLshl1AddU32":
|
case "SLshl1AddU32":
|
||||||
case "SLshl2AddU32":
|
case "SLshl2AddU32":
|
||||||
@@ -1425,55 +1292,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,
|
_module.AddInstruction(
|
||||||
oldExec),
|
SpirvOp.Not,
|
||||||
"SOrn1SaveexecB64" => _module.AddInstruction(
|
|
||||||
SpirvOp.BitwiseOr,
|
|
||||||
_ulongType,
|
_ulongType,
|
||||||
notLeft,
|
|
||||||
oldExec),
|
oldExec),
|
||||||
|
left),
|
||||||
"SOrn2SaveexecB64" => _module.AddInstruction(
|
"SOrn2SaveexecB64" => _module.AddInstruction(
|
||||||
SpirvOp.BitwiseOr,
|
SpirvOp.BitwiseOr, _ulongType, oldExec, notLeft),
|
||||||
_ulongType,
|
|
||||||
left,
|
|
||||||
_module.AddInstruction(
|
|
||||||
SpirvOp.Not,
|
|
||||||
_ulongType,
|
|
||||||
oldExec)),
|
|
||||||
"SNandSaveexecB64" => _module.AddInstruction(
|
|
||||||
SpirvOp.Not,
|
|
||||||
_ulongType,
|
|
||||||
_module.AddInstruction(
|
|
||||||
SpirvOp.BitwiseAnd,
|
|
||||||
_ulongType,
|
|
||||||
left,
|
|
||||||
oldExec)),
|
|
||||||
"SNorSaveexecB64" => _module.AddInstruction(
|
|
||||||
SpirvOp.Not,
|
|
||||||
_ulongType,
|
|
||||||
_module.AddInstruction(
|
|
||||||
SpirvOp.BitwiseOr,
|
|
||||||
_ulongType,
|
|
||||||
left,
|
|
||||||
oldExec)),
|
|
||||||
"SXnorSaveexecB64" => _module.AddInstruction(
|
|
||||||
SpirvOp.Not,
|
|
||||||
_ulongType,
|
|
||||||
_module.AddInstruction(
|
|
||||||
SpirvOp.BitwiseXor,
|
|
||||||
_ulongType,
|
|
||||||
left,
|
|
||||||
oldExec)),
|
|
||||||
_ => 0u,
|
_ => 0u,
|
||||||
};
|
};
|
||||||
if (newExec == 0)
|
if (newExec == 0)
|
||||||
@@ -1679,22 +1508,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2254,14 +2067,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 +2086,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)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
using SharpEmu.HLE;
|
using SharpEmu.HLE;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SharpEmu.Libs.Pad;
|
namespace SharpEmu.Libs.Pad;
|
||||||
|
|
||||||
@@ -59,7 +58,6 @@ public static class PadExports
|
|||||||
return SetReturn(ctx, OrbisPadErrorDeviceNotConnected);
|
return SetReturn(ctx, OrbisPadErrorDeviceNotConnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.Error.WriteLine("[LOADER][INFO] Keyboard controls: Arrow keys = D-pad, WASD = left stick, IJKL = right stick, Z/Enter = Cross, X/Esc = Circle, C = Square, V = Triangle, Q = L1, E = R1, R = L2, F = R2, Tab/Backspace = Options");
|
|
||||||
return SetReturn(ctx, PrimaryPadHandle);
|
return SetReturn(ctx, PrimaryPadHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,19 +162,10 @@ public static class PadExports
|
|||||||
{
|
{
|
||||||
Span<byte> data = stackalloc byte[PadDataSize];
|
Span<byte> data = stackalloc byte[PadDataSize];
|
||||||
data.Clear();
|
data.Clear();
|
||||||
var acceptsKeyboardInput = IsEmulatorWindowFocused();
|
data[0x04] = 128;
|
||||||
var buttons = acceptsKeyboardInput ? ReadKeyboardButtons() : 0;
|
data[0x05] = 128;
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(data[0x00..], buttons);
|
data[0x06] = 128;
|
||||||
var leftX = acceptsKeyboardInput ? ReadAnalogStick(IsKeyDown(0x41), IsKeyDown(0x44)) : (byte)128;
|
data[0x07] = 128;
|
||||||
var leftY = acceptsKeyboardInput ? ReadAnalogStick(IsKeyDown(0x57), IsKeyDown(0x53)) : (byte)128;
|
|
||||||
var rightX = acceptsKeyboardInput ? ReadAnalogStick(IsKeyDown(0x4A), IsKeyDown(0x4C)) : (byte)128;
|
|
||||||
var rightY = acceptsKeyboardInput ? ReadAnalogStick(IsKeyDown(0x49), IsKeyDown(0x4B)) : (byte)128;
|
|
||||||
data[0x04] = leftX;
|
|
||||||
data[0x05] = leftY;
|
|
||||||
data[0x06] = rightX;
|
|
||||||
data[0x07] = rightY;
|
|
||||||
data[0x08] = acceptsKeyboardInput && IsKeyDown(0x52) ? (byte)255 : (byte)0;
|
|
||||||
data[0x09] = acceptsKeyboardInput && IsKeyDown(0x46) ? (byte)255 : (byte)0;
|
|
||||||
BinaryPrimitives.WriteSingleLittleEndian(data[0x18..], 1.0f);
|
BinaryPrimitives.WriteSingleLittleEndian(data[0x18..], 1.0f);
|
||||||
data[0x4C] = 1;
|
data[0x4C] = 1;
|
||||||
var timestampTicks = Stopwatch.GetTimestamp();
|
var timestampTicks = Stopwatch.GetTimestamp();
|
||||||
@@ -196,58 +185,4 @@ public static class PadExports
|
|||||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
private static extern short GetAsyncKeyState(int vKey);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
private static extern nint GetForegroundWindow();
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
private static extern uint GetWindowThreadProcessId(nint hWnd, out uint processId);
|
|
||||||
|
|
||||||
private static bool IsKeyDown(int vk) =>
|
|
||||||
(GetAsyncKeyState(vk) & 0x8000) != 0;
|
|
||||||
|
|
||||||
private static bool IsEmulatorWindowFocused()
|
|
||||||
{
|
|
||||||
var foregroundWindow = GetForegroundWindow();
|
|
||||||
if (foregroundWindow == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetWindowThreadProcessId(foregroundWindow, out var processId);
|
|
||||||
return processId == (uint)Environment.ProcessId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static uint ReadKeyboardButtons()
|
|
||||||
{
|
|
||||||
uint buttons = 0;
|
|
||||||
// D-pad
|
|
||||||
if (IsKeyDown(0x25)) buttons |= 0x0080; // Left
|
|
||||||
if (IsKeyDown(0x27)) buttons |= 0x0020; // Right
|
|
||||||
if (IsKeyDown(0x26)) buttons |= 0x0010; // Up
|
|
||||||
if (IsKeyDown(0x28)) buttons |= 0x0040; // Down
|
|
||||||
// Face buttons
|
|
||||||
if (IsKeyDown(0x5A) || IsKeyDown(0x0D)) buttons |= 0x4000; // Z / Enter = Cross
|
|
||||||
if (IsKeyDown(0x58) || IsKeyDown(0x1B)) buttons |= 0x2000; // X / Escape = Circle
|
|
||||||
if (IsKeyDown(0x43)) buttons |= 0x8000; // C = Square
|
|
||||||
if (IsKeyDown(0x56)) buttons |= 0x1000; // V = Triangle
|
|
||||||
// Shoulder buttons
|
|
||||||
if (IsKeyDown(0x51)) buttons |= 0x0400; // Q = L1
|
|
||||||
if (IsKeyDown(0x45)) buttons |= 0x0800; // E = R1
|
|
||||||
if (IsKeyDown(0x52)) buttons |= 0x0100; // R = L2 (digital)
|
|
||||||
if (IsKeyDown(0x46)) buttons |= 0x0200; // F = R2 (digital)
|
|
||||||
// Options (Start)
|
|
||||||
if (IsKeyDown(0x09) || IsKeyDown(0x08)) buttons |= 0x0008; // Tab / Backspace = Options
|
|
||||||
return buttons;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte ReadAnalogStick(bool negative, bool positive)
|
|
||||||
{
|
|
||||||
if (negative && !positive) return 0;
|
|
||||||
if (positive && !negative) return 255;
|
|
||||||
return 128;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -400,7 +400,7 @@ public static class SaveDataExports
|
|||||||
{
|
{
|
||||||
var configured = Environment.GetEnvironmentVariable("SHARPEMU_SAVEDATA_DIR");
|
var configured = Environment.GetEnvironmentVariable("SHARPEMU_SAVEDATA_DIR");
|
||||||
var root = string.IsNullOrWhiteSpace(configured)
|
var root = string.IsNullOrWhiteSpace(configured)
|
||||||
? Path.Combine(AppContext.BaseDirectory, "user", "savedata")
|
? Path.Combine(Environment.CurrentDirectory, "user", "savedata")
|
||||||
: configured;
|
: configured;
|
||||||
return Path.GetFullPath(root);
|
return Path.GetFullPath(root);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
using SharpEmu.HLE;
|
using SharpEmu.HLE;
|
||||||
using SharpEmu.Libs.Kernel;
|
using SharpEmu.Libs.Kernel;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
@@ -49,13 +48,6 @@ public static class VideoOutExports
|
|||||||
private static int _frameDumpCount;
|
private static int _frameDumpCount;
|
||||||
private static long _nextFrameDumpIndex;
|
private static long _nextFrameDumpIndex;
|
||||||
private static string _windowTitle = "SharpEmu VideoOut";
|
private static string _windowTitle = "SharpEmu VideoOut";
|
||||||
private static readonly bool _logFrameRate = string.Equals(
|
|
||||||
Environment.GetEnvironmentVariable("SHARPEMU_LOG_VIDEOOUT_FPS"),
|
|
||||||
"1",
|
|
||||||
StringComparison.Ordinal);
|
|
||||||
private static long _frameRateWindowStart = Stopwatch.GetTimestamp();
|
|
||||||
private static long _submittedFrameCount;
|
|
||||||
private static long _presentedFrameCount;
|
|
||||||
|
|
||||||
public static void ConfigureApplicationInfo(string? title, string? titleId, string? version)
|
public static void ConfigureApplicationInfo(string? title, string? titleId, string? version)
|
||||||
{
|
{
|
||||||
@@ -820,46 +812,9 @@ public static class VideoOutExports
|
|||||||
}
|
}
|
||||||
|
|
||||||
TraceVideoOut($"videoout.submit_flip handle={handle} index={bufferIndex} mode={flipMode} arg={flipArg} events={flipEvents.Count}");
|
TraceVideoOut($"videoout.submit_flip handle={handle} index={bufferIndex} mode={flipMode} arg={flipArg} events={flipEvents.Count}");
|
||||||
ReportFrameRate(presented: false);
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void ReportPresentedFrame() =>
|
|
||||||
ReportFrameRate(presented: true);
|
|
||||||
|
|
||||||
private static void ReportFrameRate(bool presented)
|
|
||||||
{
|
|
||||||
if (!_logFrameRate)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (presented)
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _presentedFrameCount);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _submittedFrameCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
var started = Volatile.Read(ref _frameRateWindowStart);
|
|
||||||
var now = Stopwatch.GetTimestamp();
|
|
||||||
var elapsedTicks = now - started;
|
|
||||||
if (elapsedTicks < Stopwatch.Frequency ||
|
|
||||||
Interlocked.CompareExchange(ref _frameRateWindowStart, now, started) != started)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var elapsedSeconds = (double)elapsedTicks / Stopwatch.Frequency;
|
|
||||||
var submitted = Interlocked.Exchange(ref _submittedFrameCount, 0);
|
|
||||||
var presentedCount = Interlocked.Exchange(ref _presentedFrameCount, 0);
|
|
||||||
Console.Error.WriteLine(
|
|
||||||
$"[LOADER][PERF] videoout submitted_fps={submitted / elapsedSeconds:F1} " +
|
|
||||||
$"presented_fps={presentedCount / elapsedSeconds:F1}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int RegisterBufferRange(VideoOutPortState port, int startIndex, ReadOnlySpan<ulong> addresses, BufferAttribute attribute, int requestedGroupIndex = -1)
|
private static int RegisterBufferRange(VideoOutPortState port, int startIndex, ReadOnlySpan<ulong> addresses, BufferAttribute attribute, int requestedGroupIndex = -1)
|
||||||
{
|
{
|
||||||
lock (_stateGate)
|
lock (_stateGate)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user