Compare commits

..

4 Commits

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

Before

Width:  |  Height:  |  Size: 142 KiB

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

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

Before

Width:  |  Height:  |  Size: 92 KiB

-18
View File
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="globalPackagesFolder" value=".packages" />
</config>
<packageSources>
<clear />
<add key="Nuget" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="Nuget">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
+1 -6
View File
@@ -16,7 +16,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
<RuntimeIdentifiers>win-x64;linux-x64;osx-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;linux-x64;osx-arm64</RuntimeIdentifiers>
<SelfContained>true</SelfContained> <SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>true</PublishSingleFile>
<!-- <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract> --> <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile> <EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -30,11 +30,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile> <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'win-x64' Or '$(RuntimeIdentifier)' == ''">
<ApplicationIcon>..\..\assets\images\SharpEmu.ico</ApplicationIcon>
<Win32Icon>..\..\assets\images\SharpEmu.ico</Win32Icon>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="..\..\LICENSE.txt"> <Content Include="..\..\LICENSE.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
@@ -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)
+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
+10 -112
View File
@@ -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,
-2
View File
@@ -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,
+3 -48
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,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,
}; };
+25 -267
View File
@@ -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 -69
View File
@@ -4,7 +4,6 @@
using SharpEmu.HLE; using SharpEmu.HLE;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices;
namespace SharpEmu.Libs.Pad; namespace SharpEmu.Libs.Pad;
@@ -59,7 +58,6 @@ public static class PadExports
return SetReturn(ctx, OrbisPadErrorDeviceNotConnected); return SetReturn(ctx, OrbisPadErrorDeviceNotConnected);
} }
Console.Error.WriteLine("[LOADER][INFO] Keyboard controls: Arrow keys = D-pad, WASD = left stick, IJKL = right stick, Z/Enter = Cross, X/Esc = Circle, C = Square, V = Triangle, Q = L1, E = R1, R = L2, F = R2, Tab/Backspace = Options");
return SetReturn(ctx, PrimaryPadHandle); return SetReturn(ctx, PrimaryPadHandle);
} }
@@ -164,19 +162,10 @@ public static class PadExports
{ {
Span<byte> data = stackalloc byte[PadDataSize]; Span<byte> data = stackalloc byte[PadDataSize];
data.Clear(); data.Clear();
var acceptsKeyboardInput = IsEmulatorWindowFocused(); data[0x04] = 128;
var buttons = acceptsKeyboardInput ? ReadKeyboardButtons() : 0; data[0x05] = 128;
BinaryPrimitives.WriteUInt32LittleEndian(data[0x00..], buttons); data[0x06] = 128;
var leftX = acceptsKeyboardInput ? ReadAnalogStick(IsKeyDown(0x41), IsKeyDown(0x44)) : (byte)128; data[0x07] = 128;
var leftY = acceptsKeyboardInput ? ReadAnalogStick(IsKeyDown(0x57), IsKeyDown(0x53)) : (byte)128;
var rightX = acceptsKeyboardInput ? ReadAnalogStick(IsKeyDown(0x4A), IsKeyDown(0x4C)) : (byte)128;
var rightY = acceptsKeyboardInput ? ReadAnalogStick(IsKeyDown(0x49), IsKeyDown(0x4B)) : (byte)128;
data[0x04] = leftX;
data[0x05] = leftY;
data[0x06] = rightX;
data[0x07] = rightY;
data[0x08] = acceptsKeyboardInput && IsKeyDown(0x52) ? (byte)255 : (byte)0;
data[0x09] = acceptsKeyboardInput && IsKeyDown(0x46) ? (byte)255 : (byte)0;
BinaryPrimitives.WriteSingleLittleEndian(data[0x18..], 1.0f); BinaryPrimitives.WriteSingleLittleEndian(data[0x18..], 1.0f);
data[0x4C] = 1; data[0x4C] = 1;
var timestampTicks = Stopwatch.GetTimestamp(); var timestampTicks = Stopwatch.GetTimestamp();
@@ -196,58 +185,4 @@ public static class PadExports
ctx[CpuRegister.Rax] = unchecked((ulong)result); ctx[CpuRegister.Rax] = unchecked((ulong)result);
return result; return result;
} }
[DllImport("user32.dll")]
private static extern short GetAsyncKeyState(int vKey);
[DllImport("user32.dll")]
private static extern nint GetForegroundWindow();
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(nint hWnd, out uint processId);
private static bool IsKeyDown(int vk) =>
(GetAsyncKeyState(vk) & 0x8000) != 0;
private static bool IsEmulatorWindowFocused()
{
var foregroundWindow = GetForegroundWindow();
if (foregroundWindow == 0)
{
return false;
}
GetWindowThreadProcessId(foregroundWindow, out var processId);
return processId == (uint)Environment.ProcessId;
}
private static uint ReadKeyboardButtons()
{
uint buttons = 0;
// D-pad
if (IsKeyDown(0x25)) buttons |= 0x0080; // Left
if (IsKeyDown(0x27)) buttons |= 0x0020; // Right
if (IsKeyDown(0x26)) buttons |= 0x0010; // Up
if (IsKeyDown(0x28)) buttons |= 0x0040; // Down
// Face buttons
if (IsKeyDown(0x5A) || IsKeyDown(0x0D)) buttons |= 0x4000; // Z / Enter = Cross
if (IsKeyDown(0x58) || IsKeyDown(0x1B)) buttons |= 0x2000; // X / Escape = Circle
if (IsKeyDown(0x43)) buttons |= 0x8000; // C = Square
if (IsKeyDown(0x56)) buttons |= 0x1000; // V = Triangle
// Shoulder buttons
if (IsKeyDown(0x51)) buttons |= 0x0400; // Q = L1
if (IsKeyDown(0x45)) buttons |= 0x0800; // E = R1
if (IsKeyDown(0x52)) buttons |= 0x0100; // R = L2 (digital)
if (IsKeyDown(0x46)) buttons |= 0x0200; // F = R2 (digital)
// Options (Start)
if (IsKeyDown(0x09) || IsKeyDown(0x08)) buttons |= 0x0008; // Tab / Backspace = Options
return buttons;
}
private static byte ReadAnalogStick(bool negative, bool positive)
{
if (negative && !positive) return 0;
if (positive && !negative) return 255;
return 128;
}
} }
@@ -400,7 +400,7 @@ public static class SaveDataExports
{ {
var configured = Environment.GetEnvironmentVariable("SHARPEMU_SAVEDATA_DIR"); var configured = Environment.GetEnvironmentVariable("SHARPEMU_SAVEDATA_DIR");
var root = string.IsNullOrWhiteSpace(configured) var root = string.IsNullOrWhiteSpace(configured)
? Path.Combine(AppContext.BaseDirectory, "user", "savedata") ? Path.Combine(Environment.CurrentDirectory, "user", "savedata")
: configured; : configured;
return Path.GetFullPath(root); return Path.GetFullPath(root);
} }
@@ -4,7 +4,6 @@
using SharpEmu.HLE; using SharpEmu.HLE;
using SharpEmu.Libs.Kernel; using SharpEmu.Libs.Kernel;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Threading; using System.Threading;
@@ -49,13 +48,6 @@ public static class VideoOutExports
private static int _frameDumpCount; private static int _frameDumpCount;
private static long _nextFrameDumpIndex; private static long _nextFrameDumpIndex;
private static string _windowTitle = "SharpEmu VideoOut"; private static string _windowTitle = "SharpEmu VideoOut";
private static readonly bool _logFrameRate = string.Equals(
Environment.GetEnvironmentVariable("SHARPEMU_LOG_VIDEOOUT_FPS"),
"1",
StringComparison.Ordinal);
private static long _frameRateWindowStart = Stopwatch.GetTimestamp();
private static long _submittedFrameCount;
private static long _presentedFrameCount;
public static void ConfigureApplicationInfo(string? title, string? titleId, string? version) public static void ConfigureApplicationInfo(string? title, string? titleId, string? version)
{ {
@@ -820,46 +812,9 @@ public static class VideoOutExports
} }
TraceVideoOut($"videoout.submit_flip handle={handle} index={bufferIndex} mode={flipMode} arg={flipArg} events={flipEvents.Count}"); TraceVideoOut($"videoout.submit_flip handle={handle} index={bufferIndex} mode={flipMode} arg={flipArg} events={flipEvents.Count}");
ReportFrameRate(presented: false);
return (int)OrbisGen2Result.ORBIS_GEN2_OK; return (int)OrbisGen2Result.ORBIS_GEN2_OK;
} }
internal static void ReportPresentedFrame() =>
ReportFrameRate(presented: true);
private static void ReportFrameRate(bool presented)
{
if (!_logFrameRate)
{
return;
}
if (presented)
{
Interlocked.Increment(ref _presentedFrameCount);
}
else
{
Interlocked.Increment(ref _submittedFrameCount);
}
var started = Volatile.Read(ref _frameRateWindowStart);
var now = Stopwatch.GetTimestamp();
var elapsedTicks = now - started;
if (elapsedTicks < Stopwatch.Frequency ||
Interlocked.CompareExchange(ref _frameRateWindowStart, now, started) != started)
{
return;
}
var elapsedSeconds = (double)elapsedTicks / Stopwatch.Frequency;
var submitted = Interlocked.Exchange(ref _submittedFrameCount, 0);
var presentedCount = Interlocked.Exchange(ref _presentedFrameCount, 0);
Console.Error.WriteLine(
$"[LOADER][PERF] videoout submitted_fps={submitted / elapsedSeconds:F1} " +
$"presented_fps={presentedCount / elapsedSeconds:F1}");
}
private static int RegisterBufferRange(VideoOutPortState port, int startIndex, ReadOnlySpan<ulong> addresses, BufferAttribute attribute, int requestedGroupIndex = -1) private static int RegisterBufferRange(VideoOutPortState port, int startIndex, ReadOnlySpan<ulong> addresses, BufferAttribute attribute, int requestedGroupIndex = -1)
{ {
lock (_stateGate) lock (_stateGate)
File diff suppressed because it is too large Load Diff