Compare commits

..

1 Commits

Author SHA1 Message Date
patchzyy 2d7fe7450c replace per-address region table with range map 2026-10-21 20:29:38 +02:00
30 changed files with 78 additions and 692 deletions
+2 -6
View File
@@ -21,8 +21,7 @@ param(
# Discards every cache first. Used when a product was reported broken/blocked, so a possibly
# corrupted translation or build directory can never contribute to the repaired product.
[switch]$ForceCleanBuild,
[int]$Parallel = 0,
[ValidateSet('v2', 'v3')] [string]$X86CpuProfile = 'v3'
[int]$Parallel = 0
)
$ErrorActionPreference = 'Stop'
@@ -398,14 +397,11 @@ try {
$dependencies = Join-Path $Workspace 'Dependencies'
$nativePrebuilt = Resolve-NativePrebuiltDirectory $dependencies $toolchainBin `
(Join-Path $Workspace 'aurora-main') (Join-Path $Workspace 'runtime\third_party')
if ($X86CpuProfile -eq 'v2') {
Write-Host 'MKWCBUILD: This CPU does not support x86-64-v3; building for x86-64-v2'
}
$configure = Get-MkwNativeConfigureArguments `
-SourceDirectory (Join-Path $Workspace 'runtime') -BuildDirectory $build `
-Ninja $ninja -CCompiler $cc -CxxCompiler $cxx -ResourceCompiler $windres `
-DependenciesDirectory $dependencies -NativePrebuiltDirectory $nativePrebuilt `
-AdditionalArguments @("-DMKW_TRANSLATED_COMPILE_JOBS=$translatedJobs", "-DMKW_X86_CPU_PROFILE=$X86CpuProfile")
-AdditionalArguments @("-DMKW_TRANSLATED_COMPILE_JOBS=$translatedJobs")
Invoke-Checked $cmake $configure 'Configuring the bundled native toolchain' -StepId 'configure-native'
if (-not [string]::IsNullOrWhiteSpace($NativeToolchainFingerprint)) {
[ordered]@{ SchemaVersion = 1; NativeToolchainFingerprint = $NativeToolchainFingerprint } |
+14 -57
View File
@@ -33,33 +33,28 @@ if (-not (Test-Path -LiteralPath $auroraCMake -PathType Leaf)) {
$packages = @(
[pscustomobject]@{
Name = 'SDL'; File = 'SDL3-3.4.10.tar.gz'
Sha256 = '12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785'
Uris = @('https://github.com/libsdl-org/SDL/releases/download/release-3.4.10/SDL3-3.4.10.tar.gz')
Pins = @(@{ File = $auroraVersions; Text = '_aurora_dependency_version(AURORA_SDL3_REF "refs/tags/release-3.4.10"' },
@{ File = $auroraSdl; Text = 'URL "https://github.com/libsdl-org/SDL/archive/${AURORA_SDL3_REF}.tar.gz"' })
},
[pscustomobject]@{
Name = 'abseil-cpp'; File = 'abseil-cpp-20240722.0.tar.gz'
Sha256 = 'f50e5ac311a81382da7fa75b97310e4b9006474f9560ac46f54a9967f07d4ae3'
Uris = @('https://github.com/abseil/abseil-cpp/archive/refs/tags/20240722.0.tar.gz')
Pins = @(@{ File = $auroraExtern; Text = 'https://github.com/abseil/abseil-cpp/archive/refs/tags/20240722.0.tar.gz' })
},
[pscustomobject]@{
Name = 'dawn_prebuilt'; File = 'dawn-v20260807.225922-windows-amd64.tar.gz'
Sha256 = '42a7a295c87b2e8a36312e2f84a5823aac63325e8e1d271bc7ced6f83bfb075f'
Uris = @('https://github.com/encounter/dawn/releases/download/v20260807.225922/dawn-windows-amd64.tar.gz')
Pins = @(@{ File = $auroraVersions; Text = '_aurora_dependency_version(AURORA_DAWN_VERSION "v20260807.225922"' },
@{ File = $auroraDawn; Text = 'SHA256=42a7a295c87b2e8a36312e2f84a5823aac63325e8e1d271bc7ced6f83bfb075f' })
},
[pscustomobject]@{
Name = 'fmt'; File = 'fmt-12.1.0.tar.gz'
Sha256 = 'ea7de4299689e12b6dddd392f9896f08fb0777ac7168897a244a6d6085043fea'
Uris = @('https://github.com/fmtlib/fmt/archive/refs/tags/12.1.0.tar.gz')
Pins = @(@{ File = $auroraExtern; Text = 'https://github.com/fmtlib/fmt/archive/refs/tags/12.1.0.tar.gz' })
},
[pscustomobject]@{
Name = 'freetype'; File = 'freetype-2.14.3.tar.gz'
Sha256 = 'e61b31ab26358b946e767ed7eb7f4bb2e507da1cfefeb7a8861ace7fd5c899a1'
Uris = @('https://files.twilitrealm.dev/freetype-2.14.3.tar.gz',
'https://download.savannah.gnu.org/releases/freetype/freetype-2.14.3.tar.gz',
'https://downloads.sourceforge.net/project/freetype/freetype2/2.14.3/freetype-2.14.3.tar.gz')
@@ -67,50 +62,42 @@ $packages = @(
},
[pscustomobject]@{
Name = 'imgui'; File = 'imgui-1.91.9b-docking.tar.gz'
Sha256 = '466fdef9b18de15f0bb6e288e3d00ffa3d82200ec458ce5e4f724a161d9528a5'
Uris = @('https://github.com/ocornut/imgui/archive/refs/tags/v1.91.9b-docking.tar.gz')
Pins = @(@{ File = $auroraImgui; Text = 'https://github.com/ocornut/imgui/archive/refs/tags/v1.91.9b-docking.tar.gz' })
},
[pscustomobject]@{
Name = 'libusb'; File = 'libusb-1.0.30.tar.bz2'
Sha256 = 'fea36f34f9156400209595e300840767ab1a385ede1dc7ee893015aea9c6dbaf'
Uris = @('https://github.com/libusb/libusb/releases/download/v1.0.30/libusb-1.0.30.tar.bz2')
Pins = @(@{ File = $auroraVersions; Text = '_aurora_dependency_version(AURORA_LIBUSB_VERSION "1.0.30"' },
@{ File = $auroraLibUsb; Text = 'releases/download/v${AURORA_LIBUSB_VERSION}/libusb-${AURORA_LIBUSB_VERSION}.tar.bz2' })
},
[pscustomobject]@{
Name = 'png'; File = 'libpng-1.6.58.tar.gz'
Sha256 = 'a9d4df463d36a6e5f9c29bd6f4967312d17e996c1854f3511f833924eb1993cf'
Uris = @('https://github.com/pnggroup/libpng/archive/refs/tags/v1.6.58.tar.gz')
Pins = @(@{ File = $auroraExtern; Text = 'https://github.com/pnggroup/libpng/archive/refs/tags/v1.6.58.tar.gz' })
},
[pscustomobject]@{
Name = 'sqlite3'; File = 'sqlite-amalgamation-3510300.zip'
Sha256 = 'acb1e6f5d832484bf6d32b681e858c38add8b2acdfd42ac5df24b8afb46552b4'
Uris = @('https://sqlite.org/2026/sqlite-amalgamation-3510300.zip')
Pins = @(@{ File = $auroraExtern; Text = 'https://sqlite.org/2026/sqlite-amalgamation-3510300.zip' })
},
[pscustomobject]@{
Name = 'tracy'; File = 'tracy-0.14.1.zip'
Sha256 = '908f3a2917fa86a247abfcf85dcf04bad1db6986a4d40f94b70512f3e9e98d5b'
Uris = @('https://github.com/wolfpld/tracy/archive/refs/tags/v0.14.1.zip')
Pins = @(@{ File = $auroraExtern; Text = 'https://github.com/wolfpld/tracy/archive/refs/tags/v0.14.1.zip' })
},
[pscustomobject]@{
Name = 'xxhash'; File = 'xxHash-0.8.4.tar.gz'
Sha256 = '5738270935e7c3d38a79b3adf7c9692566ce7895a25f67de43ad52ab504acd32'
Uris = @('https://github.com/Cyan4973/xxHash/archive/refs/tags/v0.8.4.tar.gz')
Pins = @(@{ File = $auroraExtern; Text = 'https://github.com/Cyan4973/xxHash/archive/refs/tags/v0.8.4.tar.gz' })
},
[pscustomobject]@{
Name = 'zlib'; File = 'zlib-ng-2.3.3.tar.gz'
Sha256 = 'f9c65aa9c852eb8255b636fd9f07ce1c406f061ec19a2e7d508b318ca0c907d1'
Uris = @('https://github.com/zlib-ng/zlib-ng/archive/refs/tags/2.3.3.tar.gz')
Pins = @(@{ File = $auroraExtern; Text = 'https://github.com/zlib-ng/zlib-ng/archive/refs/tags/2.3.3.tar.gz' })
},
[pscustomobject]@{
Name = 'zstd'; File = 'zstd-1.5.7.tar.gz'
Sha256 = 'eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3'
Uris = @('https://github.com/facebook/zstd/releases/download/v1.5.7/zstd-1.5.7.tar.gz')
Pins = @(@{ File = $auroraExtern; Text = 'https://github.com/facebook/zstd/releases/download/v1.5.7/zstd-1.5.7.tar.gz' })
}
@@ -119,7 +106,6 @@ $packages = @(
# The C++/WinRT compiler (NuGet package = zip) that generates the projection headers.
$cppWinRtTool = [pscustomobject]@{
Name = 'cppwinrt-tool'; File = 'Microsoft.Windows.CppWinRT.3.0.260818.1.nupkg'
Sha256 = 'a8993608ae9263a8288e1a1d9ee2684a5632f4acc59e1e89ddd86b3b889a7367'
Uris = @('https://www.nuget.org/api/v2/package/Microsoft.Windows.CppWinRT/3.0.260818.1')
Pins = @()
}
@@ -142,20 +128,11 @@ function Assert-Pinned($Package) {
}
}
function Get-Sha256([string]$Path) { (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant() }
# Every archive this returns, cached or downloaded, matches its pinned Sha256.
function Get-Archive($Package) {
$archive = Join-Path $downloads $Package.File
if (Test-Path -LiteralPath $archive -PathType Leaf) {
$actual = Get-Sha256 $archive
if ($actual -eq $Package.Sha256) { return $archive }
Write-Warning "$($Package.Name): cached $archive has SHA-256 $actual, expected $($Package.Sha256); discarding it and downloading again."
Remove-Item -LiteralPath $archive -Force
}
if (Test-Path -LiteralPath $archive -PathType Leaf) { return $archive }
$temporary = $archive + '.partial'
$failures = @()
$mismatch = $false
foreach ($uri in $Package.Uris) {
Remove-Item -LiteralPath $temporary -Force -ErrorAction SilentlyContinue
Write-Host "Downloading $($Package.Name) from $uri..."
@@ -165,21 +142,11 @@ function Get-Archive($Package) {
$failures += "$uri ($($_.Exception.Message))"
continue
}
$actual = Get-Sha256 $temporary
if ($actual -ne $Package.Sha256) {
$failures += "$uri (SHA-256 $actual, expected $($Package.Sha256))"
$mismatch = $true
continue
}
Move-Item -LiteralPath $temporary -Destination $archive -Force
return $archive
}
Remove-Item -LiteralPath $temporary -Force -ErrorAction SilentlyContinue
$hint = ''
if ($mismatch) {
$hint = ' If upstream republished this archive, check the new file against an upstream-published checksum before updating its Sha256 in Prepare-Dependencies.ps1.'
}
throw "$($Package.Name) could not be fetched: $($failures -join '; ').$hint"
throw "$($Package.Name) could not be fetched: $($failures -join '; ')"
}
function Expand-Package([string]$Archive, [string]$Target) {
@@ -203,34 +170,25 @@ function Expand-Package([string]$Archive, [string]$Target) {
if (Test-Path -LiteralPath $extract) { Remove-Item -LiteralPath $extract -Recurse -Force }
}
# Records which archive a tree came from, so a pin change replaces a stale tree.
function Get-Stamp($Package) { "$($Package.File) $($Package.Sha256)" }
function Test-Stamp([string]$Target, [string]$Text) {
$stamp = $Target + '.source'
(Test-Path -LiteralPath $stamp -PathType Leaf) -and ([IO.File]::ReadAllText($stamp) -eq $Text)
}
function Install-Package($Package, [string]$Target, [string]$RequiredFile) {
$ready = if ($RequiredFile) { Test-Path -LiteralPath $RequiredFile -PathType Leaf } else { Test-Path -LiteralPath $Target -PathType Container }
if ($ready -and (Test-Stamp $Target (Get-Stamp $Package))) { return }
Expand-Package (Get-Archive $Package) $Target
[IO.File]::WriteAllText($Target + '.source', (Get-Stamp $Package))
Write-Host "Prepared $($Package.Name)"
}
foreach ($package in $packages) {
Assert-Pinned $package
Install-Package $package (Join-Path $Destination $package.Name)
$target = Join-Path $Destination $package.Name
# Records which archive a tree came from, so a pin change replaces a stale tree.
$stamp = $target + '.source'
$current = (Test-Path -LiteralPath $stamp -PathType Leaf) -and ([IO.File]::ReadAllText($stamp) -eq $package.File)
if ($current -and (Test-Path -LiteralPath $target -PathType Container)) { continue }
Expand-Package (Get-Archive $package) $target
[IO.File]::WriteAllText($stamp, $package.File)
Write-Host "Prepared $($package.Name)"
}
$cppWinRt = Join-Path $Destination 'cppwinrt'
$cppWinRtCurrent = (Test-Path -LiteralPath (Join-Path $cppWinRt 'winrt\base.h') -PathType Leaf) -and
(Test-Stamp $cppWinRt (Get-Stamp $cppWinRtTool))
if (-not $cppWinRtCurrent) {
if (-not (Test-Path -LiteralPath (Join-Path $cppWinRt 'winrt\base.h') -PathType Leaf)) {
$toolRoot = Join-Path $PSScriptRoot 'artifacts\cppwinrt-tool'
$compiler = Join-Path $toolRoot 'bin\cppwinrt.exe'
Install-Package $cppWinRtTool $toolRoot $compiler
if (-not (Test-Path -LiteralPath $compiler -PathType Leaf)) {
Expand-Package (Get-Archive $cppWinRtTool) $toolRoot
}
if (-not (Test-Path -LiteralPath $compiler -PathType Leaf)) { throw "cppwinrt.exe is missing: $compiler" }
if (Test-Path -LiteralPath $cppWinRt) { Remove-Item -LiteralPath $cppWinRt -Recurse -Force }
[IO.Directory]::CreateDirectory($cppWinRt) | Out-Null
@@ -241,7 +199,6 @@ if (-not $cppWinRtCurrent) {
throw "cppwinrt.exe produced no winrt\base.h under $cppWinRt"
}
Copy-Item -LiteralPath (Join-Path $toolRoot 'LICENSE') -Destination (Join-Path $cppWinRt 'LICENSE.txt')
[IO.File]::WriteAllText($cppWinRt + '.source', (Get-Stamp $cppWinRtTool))
Write-Host 'Prepared cppwinrt'
}
+2 -3
View File
@@ -5,8 +5,7 @@ param(
[string]$PortableToolsDirectory = 'Launcher/artifacts/portable-tools',
[string]$DependencySourceDirectory = 'Launcher/artifacts/dependencies',
[string]$StageDirectory = 'build/recomp-test',
[ValidateRange(1, 64)] [int]$Parallel = 3,
[ValidateSet('v2', 'v3')] [string]$X86CpuProfile = 'v3'
[ValidateRange(1, 64)] [int]$Parallel = 3
)
$ErrorActionPreference = 'Stop'
@@ -137,7 +136,7 @@ try {
-Ninja $ninja -CCompiler (Join-Path $compilerBin 'x86_64-w64-mingw32-clang.exe') `
-CxxCompiler (Join-Path $compilerBin 'x86_64-w64-mingw32-clang++.exe') `
-ResourceCompiler (Join-Path $compilerBin 'x86_64-w64-mingw32-windres.exe') `
-DependenciesDirectory $dependencies -AdditionalArguments @('-DMKW_BUILD_PRODUCTS=ON', "-DMKW_X86_CPU_PROFILE=$X86CpuProfile")
-DependenciesDirectory $dependencies -AdditionalArguments @('-DMKW_BUILD_PRODUCTS=ON')
Invoke-Checked $cmake $configure 'Configuring the production Windows runtime' `
-WaitForProcessTree $false
Invoke-Checked $cmake @('--build', $nativeBuild, '--target', 'WiiCompiled', '--parallel', "$Parallel") `
@@ -1,99 +0,0 @@
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.X86;
namespace WiiCompiled.Setup.Common;
public enum X86CpuProfile
{
V2,
V3
}
// Setup-time copy of runtime/src/host_cpu_baseline.cpp, checked before anything is built.
public static class HostCpuRequirement
{
private readonly record struct Feature(string Name, uint Leaf, int Register, int Bit, X86CpuProfile Profile);
// Same table and order as the runtime: register 0..3 = eax, ebx, ecx, edx.
private static readonly Feature[] Features =
[
new("SSE3", 1, 2, 0, X86CpuProfile.V2),
new("SSSE3", 1, 2, 9, X86CpuProfile.V2),
new("CMPXCHG16B", 1, 2, 13, X86CpuProfile.V2),
new("SSE4.1", 1, 2, 19, X86CpuProfile.V2),
new("SSE4.2", 1, 2, 20, X86CpuProfile.V2),
new("POPCNT", 1, 2, 23, X86CpuProfile.V2),
new("LAHF-SAHF", 0x80000001u, 2, 0, X86CpuProfile.V2),
new("FMA", 1, 2, 12, X86CpuProfile.V3),
new("MOVBE", 1, 2, 22, X86CpuProfile.V3),
new("OSXSAVE", 1, 2, 27, X86CpuProfile.V3),
new("AVX", 1, 2, 28, X86CpuProfile.V3),
new("F16C", 1, 2, 29, X86CpuProfile.V3),
new("BMI1", 7, 1, 3, X86CpuProfile.V3),
new("AVX2", 7, 1, 5, X86CpuProfile.V3),
new("BMI2", 7, 1, 8, X86CpuProfile.V3),
new("LZCNT", 0x80000001u, 2, 5, X86CpuProfile.V3),
];
public static void EnsureSupported(X86CpuProfile profile)
{
if (RuntimeInformation.ProcessArchitecture != Architecture.X64)
return;
// The JIT only reports AVX when the OS also saves the YMM state (XCR0), which is the
// runtime's last check; there is no managed XGETBV.
var missing = FindMissingFeatures(profile, CpuId, Avx.IsSupported);
if (missing.Count > 0)
throw new PlatformNotSupportedException(Describe(profile, missing));
}
// The highest profile this machine runs; builds fall back to v2 when v3 is missing.
public static X86CpuProfile SelectBuildProfile() =>
RuntimeInformation.ProcessArchitecture != Architecture.X64
? X86CpuProfile.V3
: SelectBuildProfile(CpuId, Avx.IsSupported);
public static X86CpuProfile SelectBuildProfile(Func<uint, uint[]> cpuid, bool osSavesAvxState) =>
FindMissingFeatures(X86CpuProfile.V3, cpuid, osSavesAvxState).Count == 0
? X86CpuProfile.V3
: X86CpuProfile.V2;
public static IReadOnlyList<string> FindMissingFeatures(X86CpuProfile profile,
Func<uint, uint[]> cpuid, bool osSavesAvxState)
{
var maxBasic = cpuid(0)[0];
var maxExtended = cpuid(0x80000000u)[0];
var missing = new List<string>();
foreach (var feature in Features)
{
if (feature.Profile > profile)
continue;
var leafAvailable = (feature.Leaf & 0x80000000u) != 0
? feature.Leaf <= maxExtended
: feature.Leaf <= maxBasic;
if (!leafAvailable || (cpuid(feature.Leaf)[feature.Register] & (1u << feature.Bit)) == 0)
missing.Add(feature.Name);
}
if (missing.Count == 0 && profile >= X86CpuProfile.V3 && !osSavesAvxState)
missing.Add("operating system support for AVX register state (XCR0 YMM bits)");
return missing;
}
public static string Describe(X86CpuProfile profile, IReadOnlyList<string> missing) =>
profile == X86CpuProfile.V2
? "This build needs a processor that supports the x86-64-v2 instruction set.\n\n" +
"Missing on this machine: " + string.Join(", ", missing) + "\n\n" +
"x86-64-v2 covers Intel Core processors from Nehalem (2008) onward and AMD processors " +
"from Jaguar (2013) onward."
: "This build needs a processor that supports the x86-64-v3 instruction set.\n\n" +
"Missing on this machine: " + string.Join(", ", missing) + "\n\n" +
"x86-64-v3 covers Intel Core processors from Haswell (4th generation, 2013) onward " +
"and AMD processors from Excavator (2015) onward.";
private static uint[] CpuId(uint leaf)
{
var (eax, ebx, ecx, edx) = X86Base.CpuId(unchecked((int)leaf), 0);
return [unchecked((uint)eax), unchecked((uint)ebx), unchecked((uint)ecx), unchecked((uint)edx)];
}
}
@@ -74,8 +74,6 @@ internal static class Program
private static async Task InstallAsync(Dictionary<string, string?> flags, IInstallReporter reporter, CancellationToken token)
{
// local-build.sh falls back to an x86-64-v2 build when this CPU lacks v3.
HostCpuRequirement.EnsureSupported(X86CpuProfile.V2);
var retroDir = flags.GetValueOrDefault("retro-dir");
var installsRetro = !string.IsNullOrEmpty(retroDir);
var downloadPayload = flags.ContainsKey("download-retro-wfc-payload");
@@ -60,8 +60,7 @@ internal sealed class LocalBuildService
BuildProfile.RetroRewind => "retro-rewind",
_ => "both"
},
"-OutputDirectory", outputDirectory,
"-X86CpuProfile", HostCpuRequirement.SelectBuildProfile() == X86CpuProfile.V2 ? "v2" : "v3"
"-OutputDirectory", outputDirectory
};
if (baseOutputDirectory is not null)
{
@@ -64,8 +64,6 @@ internal static class Program
if (command.Mode is AppMode.SilentInstall or AppMode.RepairProducts)
{
// CPUs without x86-64-v3 get an x86-64-v2 build.
HostCpuRequirement.EnsureSupported(X86CpuProfile.V2);
using var cancellationSignal = CancellationSignal.ObserveEnvironment();
return command.Mode == AppMode.SilentInstall
? ConsoleCommands.Install(command, cancellationSignal.Token).GetAwaiter().GetResult()
@@ -36,7 +36,6 @@ internal static class SelfTests
Test("Toolkit accompanies the workspace", TestToolkitRefreshDecision, failures);
Test("Retro-WFC payload snapshot reconciliation", TestRetroWfcPayloadSnapshotReconciliation, failures);
Test("Retro-WFC transient retry policy", TestRetroWfcTransientRetryPolicy, failures);
Test("Host CPU requirement", TestHostCpuRequirement, failures);
Test("Shared runtime configuration", () => TestPathSettingRoundTrip("dvd_root",
RuntimeConfiguration.SetDvdRoot, RuntimeConfiguration.RemoveDvdRootIfOwned), failures);
Test("Canonical Retro Rewind configuration", () => TestPathSettingRoundTrip("retro_rewind_root",
@@ -995,57 +994,6 @@ internal static class SelfTests
throw new Exception("A non-I/O failure would be retried.");
}
private static void TestHostCpuRequirement()
{
// Leaf 1 ecx, leaf 7 ebx, leaf 0x80000001 ecx of a v2-only and a v3 processor.
const uint v2Leaf1 = (1u << 0) | (1u << 9) | (1u << 13) | (1u << 19) | (1u << 20) | (1u << 23);
const uint v3Leaf1 = v2Leaf1 | (1u << 12) | (1u << 22) | (1u << 27) | (1u << 28) | (1u << 29);
const uint v3Leaf7 = (1u << 3) | (1u << 5) | (1u << 8);
Func<uint, uint[]> Cpu(uint maxBasic, uint leaf1, uint leaf7, uint extended) => leaf => leaf switch
{
0 => [maxBasic, 0, 0, 0],
1 => [0, 0, leaf1, 0],
7 => [0, leaf7, 0, 0],
0x80000000u => [0x80000001u, 0, 0, 0],
0x80000001u => [0, 0, extended, 0],
_ => [0, 0, 0, 0]
};
var nehalem = Cpu(11, v2Leaf1, 0, 1u);
var haswell = Cpu(13, v3Leaf1, v3Leaf7, 1u | (1u << 5));
void Expect(IReadOnlyList<string> actual, string expected, string what)
{
if (string.Join(", ", actual) != expected)
throw new Exception($"{what}: expected [{expected}], got [{string.Join(", ", actual)}].");
}
Expect(HostCpuRequirement.FindMissingFeatures(X86CpuProfile.V2, nehalem, false), "",
"A v2 processor was rejected for the v2 build");
Expect(HostCpuRequirement.FindMissingFeatures(X86CpuProfile.V3, nehalem, false),
"FMA, MOVBE, OSXSAVE, AVX, F16C, BMI1, AVX2, BMI2, LZCNT", "A v2 processor for the v3 build");
Expect(HostCpuRequirement.FindMissingFeatures(X86CpuProfile.V3, haswell, true), "",
"A v3 processor was rejected");
Expect(HostCpuRequirement.FindMissingFeatures(X86CpuProfile.V2, haswell, false), "",
"A v3 processor was rejected for the v2 build");
Expect(HostCpuRequirement.FindMissingFeatures(X86CpuProfile.V3, haswell, false),
"operating system support for AVX register state (XCR0 YMM bits)", "Disabled AVX state");
Expect(HostCpuRequirement.FindMissingFeatures(X86CpuProfile.V3, Cpu(6, v3Leaf1, v3Leaf7, 1u | (1u << 5)), true),
"BMI1, AVX2, BMI2", "Unavailable leaf 7");
Expect(HostCpuRequirement.FindMissingFeatures(X86CpuProfile.V2, Cpu(11, v2Leaf1, 0, 0), false),
"LAHF-SAHF", "Missing LAHF/SAHF");
if (HostCpuRequirement.SelectBuildProfile(nehalem, false) != X86CpuProfile.V2)
throw new Exception("A v2 processor was not given the v2 build.");
if (HostCpuRequirement.SelectBuildProfile(haswell, true) != X86CpuProfile.V3)
throw new Exception("A v3 processor was not given the v3 build.");
if (HostCpuRequirement.SelectBuildProfile(haswell, false) != X86CpuProfile.V2)
throw new Exception("A v3 processor without AVX state was given the v3 build.");
var message = HostCpuRequirement.Describe(X86CpuProfile.V3, ["AVX2"]);
if (!message.Contains("x86-64-v3") || !message.Contains("Missing on this machine: AVX2"))
throw new Exception("The unsupported-processor message lost its detail.");
}
private static void TestRetroWfcTransientRetryPolicy()
{
if (!InputValidation.IsTransientRetroWfcDownloadFailure(
+1 -12
View File
@@ -86,17 +86,6 @@ assets="$workspace/Assets"; generated="$workspace/generated"
functions="$generated/functions"; metadata="$generated/base_translation_output.json"; manifest_dir="$workspace/build/base"
manifest="$manifest_dir/mkwii_base_manifest.json"; shards="$generated/build_shards"
native_build="$workspace/native-build-macos-$macos_arch"
x86_cpu_profile=v3
if [[ "$macos_arch" == x86_64 ]]; then
native_macros=$(clang -march=native -dM -E -x c /dev/null 2>/dev/null || true)
for macro in __AVX__ __AVX2__ __BMI__ __BMI2__ __F16C__ __FMA__ __LZCNT__ __MOVBE__; do
if ! grep -q "^#define $macro " <<<"$native_macros"; then
x86_cpu_profile=v2
printf 'MKWCBUILD: This CPU does not support x86-64-v3; building for x86-64-v2\n'
break
fi
done
fi
if [[ -n "$game" ]]; then "$script_dir/macos/extract-disc.command" --workspace "$workspace" --game "$game" --assets-dir "$assets" --nodtool "$nodtool"; fi
assert_file "$assets/main.dol" 'extracted main.dol'; assert_file "$assets/StaticR.rel" 'extracted StaticR.rel'
# The translation project is the one pinned to the extracted main.dol, which decides the region.
@@ -183,7 +172,7 @@ fi
step configure-native 'Configuring the native toolchain'
# Use Aurora's pinned SDL3 source on macOS. A system SDL3 can be older than
# Aurora's required API even when find_package() succeeds.
"$cmake_bin" -S "$workspace/runtime" -B "$native_build" -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_MAKE_PROGRAM="$ninja_bin" -DCMAKE_OSX_ARCHITECTURES="$macos_arch" -DCMAKE_OSX_DEPLOYMENT_TARGET="$macos_deployment_target" -DMKW_TRANSLATED_COMPILE_JOBS="$translated_jobs" -DMKW_X86_CPU_PROFILE="$x86_cpu_profile" -DAURORA_SDL3_PROVIDER=vendor -DMKW_SETUP_VERSION="$setup_version"
"$cmake_bin" -S "$workspace/runtime" -B "$native_build" -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_MAKE_PROGRAM="$ninja_bin" -DCMAKE_OSX_ARCHITECTURES="$macos_arch" -DCMAKE_OSX_DEPLOYMENT_TARGET="$macos_deployment_target" -DMKW_TRANSLATED_COMPILE_JOBS="$translated_jobs" -DAURORA_SDL3_PROVIDER=vendor -DMKW_SETUP_VERSION="$setup_version"
mkw_write_identity "$toolchain_provenance" ToolchainFingerprint "$toolchain_fingerprint"
targets=(); [[ "$profile" != retro-rewind ]] && targets+=(WiiCompiled); [[ "$profile" != base ]] && targets+=(RetroRewind)
step compile "Compiling ${targets[*]} locally"; "$cmake_bin" --build "$native_build" --target "${targets[@]}" --parallel "$global_jobs"
-22
View File
@@ -41,28 +41,6 @@ done
host_arch=$(uname -m)
case "$host_arch" in arm64|x86_64) ;; *) fail "unsupported macOS architecture: $host_arch" ;; esac
# Same x86-64-v2 check as runtime/src/host_cpu_baseline.cpp, before anything is built;
# local-build-macos.command builds for v2 when the CPU lacks v3.
if [[ "$host_arch" == x86_64 ]]; then
cpu_features=$(/usr/sbin/sysctl -n machdep.cpu.features machdep.cpu.extfeatures 2>/dev/null | tr '\n' ' ' || true)
if [[ -n "${cpu_features// /}" ]]; then
missing_features=""
for feature in SSE3=SSE3 SSSE3=SSSE3 CX16=CMPXCHG16B SSE4.1=SSE4.1 SSE4.2=SSE4.2 POPCNT=POPCNT \
LAHF=LAHF-SAHF; do
[[ " $cpu_features " == *" ${feature%%=*} "* ]] || missing_features+="${missing_features:+, }${feature#*=}"
done
if [[ -n "$missing_features" ]]; then
message="This build needs a processor that supports the x86-64-v2 instruction set.
Missing on this machine: $missing_features
x86-64-v2 covers Intel Core processors from Nehalem (2008) onward and AMD processors from Jaguar (2013) onward."
printf 'WiiCompiled Setup: %s\n' "$message" >&2
notice "$message"
exit 1
fi
fi
fi
host_tools="$resources/tools/$host_arch"
nodtool="$host_tools/nodtool"
translator="$host_tools/Translator.Cli"
-14
View File
@@ -293,21 +293,8 @@ function(mkw_configure_product target)
endif()
endfunction()
if(MKW_PLATFORM_WINDOWS)
enable_language(RC)
endif()
# Own object library so the products' C++ options never reach the resource compiler.
function(mkw_add_product_icon target)
if(MKW_PLATFORM_WINDOWS)
add_library(${target}_icon OBJECT "${MKW_RUNTIME_SOURCE_DIR}/resources/${target}.rc")
target_sources(${target} PRIVATE $<TARGET_OBJECTS:${target}_icon>)
endif()
endfunction()
add_executable(WiiCompiled "${MKW_BASE_PRODUCT_SOURCE}")
mkw_configure_product(WiiCompiled)
mkw_add_product_icon(WiiCompiled)
target_sources(WiiCompiled PRIVATE $<TARGET_OBJECTS:mkw_base_registration>)
target_precompile_headers(WiiCompiled PRIVATE
"${MKW_RUNTIME_SOURCE_DIR}/include/mkw_pch.h")
@@ -318,7 +305,6 @@ endif()
if(MKW_HAVE_RETRO_REWIND)
add_executable(RetroRewind "${MKW_RETRO_REWIND_PRODUCT_SOURCE}")
mkw_configure_product(RetroRewind)
mkw_add_product_icon(RetroRewind)
target_precompile_headers(RetroRewind REUSE_FROM WiiCompiled)
target_sources(RetroRewind PRIVATE $<TARGET_OBJECTS:mkw_retro_registration>)
if(TARGET mkw_retro_sensitive)
-15
View File
@@ -44,7 +44,6 @@ struct RuntimeUserConfig {
std::optional<uint32_t> frameInterpolationFps;
std::optional<bool> metalFxSpatialUpscaling;
std::optional<bool> skipUnreadyPipelines;
std::optional<bool> vsync;
std::optional<bool> gxWorker;
std::optional<bool> disableCopyFilter;
std::optional<bool> textureReplacements;
@@ -277,7 +276,6 @@ inline void EnsureConfigFile() {
"display_mode = \"windowed\"\n"
"graphics_api = \"auto\"\n"
"skip_unready_pipelines = true\n"
"vsync = false\n"
"# Runs GX command processing on its own thread. Read once, at startup.\n"
"gx_worker = true\n"
"disable_copy_filter = true\n"
@@ -437,7 +435,6 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
config.metalFxSpatialUpscaling =
FindConfigValue<bool>(document, "video", "metalfx_spatial_upscaling");
config.skipUnreadyPipelines = FindConfigValue<bool>(document, "video", "skip_unready_pipelines");
config.vsync = FindConfigValue<bool>(document, "video", "vsync");
config.gxWorker = FindConfigValue<bool>(document, "video", "gx_worker");
config.disableCopyFilter = FindConfigValue<bool>(document, "video", "disable_copy_filter");
config.showFps = FindConfigValue<bool>(document, "video", "show_fps");
@@ -643,11 +640,6 @@ inline bool SetSkipUnreadyPipelines(bool value) {
return WriteSetting("video", "skip_unready_pipelines", value ? "true" : "false");
}
inline bool SetVsync(bool value) {
Mutable().vsync = value;
return WriteSetting("video", "vsync", value ? "true" : "false");
}
inline bool SetDisableCopyFilter(bool value) {
Mutable().disableCopyFilter = value;
return WriteSetting("video", "disable_copy_filter", value ? "true" : "false");
@@ -899,10 +891,6 @@ inline bool SkipUnreadyPipelines(bool fallback = true) {
return Get().skipUnreadyPipelines.value_or(fallback);
}
inline bool Vsync(bool fallback = false) {
return Get().vsync.value_or(fallback);
}
inline bool DisableCopyFilter(bool fallback = true) {
return Get().disableCopyFilter.value_or(fallback);
}
@@ -1015,9 +1003,6 @@ inline void LogLoadedConfig() {
if (config.skipUnreadyPipelines) {
std::cout << " skip_unready_pipelines=" << (*config.skipUnreadyPipelines ? "true" : "false");
}
if (config.vsync) {
std::cout << " vsync=" << (*config.vsync ? "true" : "false");
}
if (config.disableCopyFilter) {
std::cout << " disable_copy_filter=" << (*config.disableCopyFilter ? "true" : "false");
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

-1
View File
@@ -1 +0,0 @@
1 ICON "RetroRewind.ico"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

-1
View File
@@ -1 +0,0 @@
1 ICON "WiiCompiled.ico"
+4 -37
View File
@@ -7,7 +7,6 @@
#include "memory.h"
#include "runtime_product.h"
#include <SDL3/SDL_clipboard.h>
#include <SDL3/SDL_events.h>
#include <atomic>
@@ -28,32 +27,11 @@ constexpr uint32_t kPageStateActive = 4;
constexpr uint32_t kVtableGetTypeInfoOffset = 0x60;
constexpr int kBackspace = -1;
constexpr size_t kMaxPendingKeys = 32;
constexpr size_t kFriendCodeDigits = 12;
std::mutex g_pendingMutex;
std::vector<int> g_pending;
std::atomic_bool g_pageFocused{false};
bool IsPaste(const SDL_KeyboardEvent& key) {
return !key.repeat && key.key == SDLK_V && (key.mod & (SDL_KMOD_CTRL | SDL_KMOD_GUI)) != 0 &&
(key.mod & SDL_KMOD_ALT) == 0;
}
void AppendClipboardDigits(std::vector<int>& keys) {
char* text = SDL_GetClipboardText();
if (!text) {
return;
}
size_t digits = 0;
for (const char* c = text; *c && digits < kFriendCodeDigits; ++c) {
if (*c >= '0' && *c <= '9') {
keys.push_back(*c - '0');
++digits;
}
}
SDL_free(text);
}
std::optional<int> KeyValue(const SDL_KeyboardEvent& key) {
if ((key.mod & (SDL_KMOD_CTRL | SDL_KMOD_ALT | SDL_KMOD_GUI)) != 0) {
return std::nullopt;
@@ -120,27 +98,16 @@ void HandleEvents(const AuroraEvent* events) noexcept {
if (!events || !g_pageFocused.load(std::memory_order_acquire) || InputBindings::InputBlocked()) {
return;
}
std::vector<int> keys;
std::lock_guard lock(g_pendingMutex);
for (const AuroraEvent* ev = events; ev->type != AURORA_NONE; ++ev) {
if (ev->type != AURORA_SDL_EVENT || ev->sdl.type != SDL_EVENT_KEY_DOWN) {
continue;
}
if (IsPaste(ev->sdl.key)) {
AppendClipboardDigits(keys);
} else if (const std::optional<int> key = KeyValue(ev->sdl.key)) {
keys.push_back(*key);
const std::optional<int> key = KeyValue(ev->sdl.key);
if (key && g_pending.size() < kMaxPendingKeys) {
g_pending.push_back(*key);
}
}
if (keys.empty()) {
return;
}
std::lock_guard lock(g_pendingMutex);
for (const int key : keys) {
if (g_pending.size() >= kMaxPendingKeys) {
break;
}
g_pending.push_back(key);
}
}
} // namespace FriendCodeKeyboard
+12 -7
View File
@@ -807,17 +807,21 @@ struct DlMayContainDrawVisitor {
bool OnNop(uint8_t) { return true; }
bool OnBpReg(const uint8_t*, uint32_t) { return true; }
// CP writes must reach the HLE vertex state, and indexed XF loads need their
// array resolved, so neither may be passed through raw.
bool OnCpReg(const uint8_t*, uint8_t, uint32_t) { return false; }
// An array base/stride write means the list is setting up indexed geometry.
bool OnCpReg(const uint8_t*, uint8_t reg, uint32_t) { return !(reg >= 0xA0 && reg <= 0xBF); }
bool OnXfReg(const uint8_t*, uint32_t) { return true; }
bool OnIndexedXf(const uint8_t*, uint8_t, uint32_t) { return false; }
bool OnIndexedXf(const uint8_t*, uint8_t, uint32_t) { return true; }
bool OnInvalidateVertexCache(uint8_t) { return true; }
// Draw opcodes reach this too (kHandlesDraw is false): a draw opcode, or a
// command byte this walker does not model, both mean "assume it draws".
bool OnUnknownCommand(uint8_t) { return false; }
// Aurora skips nested calls, so the child has to be flattened.
bool OnCallDisplayList(uint32_t addr, uint32_t size, int) { return addr == 0 || size == 0; }
bool OnCallDisplayList(uint32_t addr, uint32_t size, int depth) {
if (addr == 0 || size == 0) return true;
const uint8_t* nested = static_cast<const uint8_t*>(GuestToHostPtr(addr, size));
if (!nested) return false;
DlMayContainDrawVisitor child;
return WalkDisplayList(nested, size, child, depth + 1);
}
};
static bool DisplayListMayContainDraw(const uint8_t* data, uint32_t nbytes) {
@@ -1138,7 +1142,8 @@ static void ApplyAuroraVtxDesc() {
// GXGetVtxDesc only decodes GX_VA_PNMTXIDX..GX_VA_TEX7; the XF array
// pseudo-attributes above GX_VA_TEX7 have no CP descriptor bits, so
// probing them would always report GX_NONE and republish a no-op.
if (!needsPublish && gxAttr <= GX_VA_TEX7) {
if (!needsPublish && !IsMatrixIndexAttr(gxAttr) && gxAttr <= GX_VA_TEX7 &&
(type == GX_INDEX8 || type == GX_INDEX16)) {
needsPublish = !AuroraVtxDescMatchesLive(gxAttr, type);
}
if (needsPublish) {
+2 -9
View File
@@ -490,16 +490,9 @@ void HleFifoWrite(u32 val, uint32_t sizeBytes) {
const uint16_t vtxCount = ReadBE16(data + 1);
const GXVtxFmt vtxFmt = static_cast<GXVtxFmt>(cmd & GX_VAT_MASK_CMD);
const GXPrimitive prim = OpcodeToGXPrimitive(cmd);
if (vtxCount == 0) {
if (!consumeBytes(3, sink)) break;
continue;
}
uint32_t rawVertexSize = 0;
const uint32_t packetBytes = TryGetRawDirectFifoVertexSize(vtxFmt, rawVertexSize)
? 3u + static_cast<uint32_t>(vtxCount) * rawVertexSize
: 0u;
// A packet that cannot fit the buffer is streamed per vertex instead.
if (packetBytes != 0 && packetBytes <= g_hleGxState.fifoBytes.size()) {
if (TryGetRawDirectFifoVertexSize(vtxFmt, rawVertexSize)) {
const uint32_t packetBytes = 3u + static_cast<uint32_t>(vtxCount) * rawVertexSize;
if (g_hleGxState.fifoByteCount < packetBytes) {
break;
}
+2 -5
View File
@@ -703,11 +703,8 @@ extern "C" void GX__LoadTexObj_80170f2c(uint32_t oa, uint32_t tid) {
}
PPC_NATIVE_OVERRIDE_VOID(80170f2c, GX__LoadTexObj_80170f2c, (uint32_t oa, uint32_t tid), (oa, tid));
extern "C" void GX__LoadTexObjPreLoaded_80170dc8(uint32_t oa, uint32_t region, uint32_t tid) {
(void)region;
GX__LoadTexObj_80170f2c(oa, tid);
}
PPC_NATIVE_OVERRIDE_VOID(80170dc8, GX__LoadTexObjPreLoaded_80170dc8, (uint32_t oa, uint32_t region, uint32_t tid), (oa, region, tid));
extern "C" void GX__LoadTexObjPreLoaded_80170dc8(uint32_t oa, uint32_t tid) { GX__LoadTexObj_80170f2c(oa, tid); }
PPC_NATIVE_OVERRIDE_VOID(80170dc8, GX__LoadTexObjPreLoaded_80170dc8, (uint32_t oa, uint32_t tid), (oa, tid));
// ============================================================================
// Texture Object Getters
+2 -2
View File
@@ -150,7 +150,7 @@ extern "C" void GX__SetVtxAttrFmtv_8016de08(uint32_t vf, uint32_t la) {
while(true){
uint32_t a=Memory::Read32(p);
if(a==0xFFu) break;
list.push_back({a, Memory::Read32(p+4), Memory::Read32(p+8), Memory::Read8(p+12)});
list.push_back({a, Memory::Read32(p+4), Memory::Read32(p+8), Memory::Read32(p+12)&0xFFu});
p+=16;
}
GxWorker::Run([vf, list = std::move(list)] {
@@ -326,7 +326,7 @@ extern "C" void GX__Begin_8016f0f0(uint32_t t, uint32_t vf, uint32_t nv) {
static void GxBeginImpl(uint32_t t, uint32_t vf, uint32_t nv) {
ApplyAuroraVtxStateForBegin(static_cast<GXVtxFmt>(vf));
g_hleGxState.currentVtxFmt=(GXVtxFmt)vf; g_hleGxState.currentPrim=(GXPrimitive)t;
g_hleGxState.vertsRemaining=nv; g_hleGxState.inBegin=nv!=0; g_hleGxState.auroraBeginCalled=false;
g_hleGxState.vertsRemaining=nv; g_hleGxState.inBegin=true; g_hleGxState.auroraBeginCalled=false;
g_hleGxState.fifoReadOffset = 0;
g_hleGxState.fifoByteCount=0; g_hleGxState.ResetVertex();
}
+7 -18
View File
@@ -1031,17 +1031,16 @@ extern "C" int32_t DVDLowInit_80164848() {
}
PPC_NATIVE_OVERRIDE(80164848, DVDLowInit_80164848, int32_t, (), ());
// DVDLowInquiry(DVDDriveInfo* info, DVDLowCallback cb); reports a retail drive.
extern "C" int32_t DVDLowInquiry_80165A30(uint32_t driveInfoPtr, uint32_t callback)
extern "C" int32_t DVDLowInquiry_80165A30(uint32_t cmdBlockPtr, uint32_t callback)
{
if (driveInfoPtr && Memory::Contains(driveInfoPtr, 0x20)) {
Memory::Write32(driveInfoPtr + 0x0, 0x00000002); // revision level, device code
Memory::Write32(driveInfoPtr + 0x4, 0x20060526); // release date
Memory::Write32(driveInfoPtr + 0x8, 0x41000000);
// HLE: acknowledge the drive is present. Must mark the command block complete
// (State = 0) or callers polling this address hang waiting for "Busy" to clear.
if (cmdBlockPtr) {
Memory::Write32(cmdBlockPtr + DVD_CB_OFFSET_STATE, DVD_STATE_END);
}
InvokeDvdLowCallback(callback, DvdReadContract::kInterruptTransferComplete);
CompleteDvdCancelState();
return 1;
return 1; // Return 1 to indicate the command was successfully issued.
}
PPC_NATIVE_OVERRIDE(80165A30, DVDLowInquiry_80165A30, int32_t, (uint32_t b, uint32_t c), (b, c));
@@ -1052,21 +1051,11 @@ extern "C" int32_t DVDLowReadDiskID_80164AAC(uint32_t diskIdPtr, uint32_t callba
Memory::Write16(diskIdPtr + 0x04, 0x3031);
Memory::Write8 (diskIdPtr + 0x06, 0x01);
}
InvokeDvdLowCallback(callback, DvdReadContract::kInterruptTransferComplete);
CompleteDvdCancelState();
return 1; // Success
}
PPC_NATIVE_OVERRIDE(80164AAC, DVDLowReadDiskID_80164AAC, int32_t, (uint32_t p, uint32_t c), (p, c));
// Host files need no head positioning.
extern "C" int32_t DVDLowSeek_801664E0(uint32_t offset, uint32_t callback) {
(void)offset;
InvokeDvdLowCallback(callback, DvdReadContract::kInterruptTransferComplete);
CompleteDvdCancelState();
return 1;
}
PPC_NATIVE_OVERRIDE(801664E0, DVDLowSeek_801664E0, int32_t, (uint32_t o, uint32_t c), (o, c));
// 0x80166330 -> DVDLowRead (And 0x80165708 UnencryptedRead)
// The game calls this to read the Disk Header (offset 0) or raw data.
extern "C" int32_t DVDLowRead_80166330(uint32_t buffer, uint32_t length, uint32_t offset, uint32_t callback)
+1 -1
View File
@@ -964,7 +964,7 @@ static int32_t HandleIsfsReadDir(uint32_t numIn, uint32_t numOut, uint32_t vecto
}
const std::filesystem::path hostPath = TranslateNandPath(wiiPath.c_str());
if (!IsDirectory(hostPath)) {
return PathExists(hostPath) ? ISFS_EINVAL : ISFS_ENOENT;
return ISFS_ENOENT;
}
// NAND names are at most 12 characters; longer host names cannot exist on
+1 -1
View File
@@ -1390,7 +1390,7 @@ int RuntimeMain(int argc, char** argv) {
auroraConfig.allowTextureReplacements = RuntimeConfigFile::TextureReplacements(false);
auroraConfig.allowTextureDumps = auroraConfig.allowTextureReplacements &&
RuntimeConfigFile::TextureDumps(false);
auroraConfig.vsync = RuntimeConfigFile::Vsync();
// No vsync knob: aurora always configures a non-blocking present mode.
auroraConfig.desiredBackend = BACKEND_AUTO;
const float resolutionMultiplier = RuntimeConfigFile::ResolutionMultiplier(1.0f);
ConfigureMkwDynamicAspect(configWidescreen, forceAspect169,
+3 -24
View File
@@ -109,7 +109,6 @@ int g_displayMode = [] {
return static_cast<int>(AURORA_DISPLAY_MODE_WINDOWED);
}();
bool g_skipUnreadyPipelines = RuntimeConfigFile::SkipUnreadyPipelines(true);
bool g_vsync = RuntimeConfigFile::Vsync();
bool g_disableCopyFilter = RuntimeConfigFile::DisableCopyFilter(true);
bool g_showFps = RuntimeConfigFile::ShowFps(true);
bool g_forceAspect169 = RuntimeConfigFile::ForceAspect169Enabled();
@@ -1190,10 +1189,6 @@ void DrawGraphicsSettings() {
aurora_set_skip_unready_pipelines(g_skipUnreadyPipelines);
RuntimeConfigFile::SetSkipUnreadyPipelines(g_skipUnreadyPipelines);
}
if (ImGui::Checkbox("VSync", &g_vsync)) {
aurora_enable_vsync(g_vsync);
RuntimeConfigFile::SetVsync(g_vsync);
}
if (ImGui::Checkbox("Show FPS", &g_showFps)) {
RuntimeConfigFile::SetShowFps(g_showFps);
}
@@ -1204,12 +1199,7 @@ void DrawGraphicsSettings() {
void DrawFpsOverlay() {
AuroraPresentTiming presentTiming{};
aurora_get_present_timing(&presentTiming);
const uint32_t targetFps = kFrameInterpolationTargetFps[static_cast<size_t>(g_frameInterpolationMode)];
const double refreshRate = aurora_get_display_refresh_rate();
const bool interpolationInactive = targetFps > 60 && !aurora_display_shows_interpolation();
const bool unevenPacing =
!g_vsync && !interpolationInactive && refreshRate > 0.0 && targetFps > refreshRate + 1.0;
if (!g_showFps && !unevenPacing && !interpolationInactive) {
if (!g_showFps) {
return;
}
@@ -1226,9 +1216,9 @@ void DrawFpsOverlay() {
ImGuiWindowFlags_NoNav |
ImGuiWindowFlags_NoSavedSettings;
if (ImGui::Begin("FPS Overlay", nullptr, kFlags)) {
if (g_showFps && presentTiming.sampleCount == 0) {
if (presentTiming.sampleCount == 0) {
ImGui::TextUnformatted("FPS: --");
} else if (g_showFps) {
} else {
// Present timing includes the additional frames produced by
// interpolation, so this remains the actual displayed FPS.
ImGui::Text("FPS: %.1f", presentTiming.framesPerSecond);
@@ -1241,17 +1231,6 @@ void DrawFpsOverlay() {
presentTiming.effectiveFramesPerSecond);
}
}
if (interpolationInactive) {
ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.25f, 1.0f),
"%u FPS interpolation has no effect on a %.0f Hz display.", targetFps,
refreshRate);
}
if (unevenPacing) {
ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.25f, 1.0f),
"%u FPS interpolation on a %.0f Hz display without VSync is not smooth.\n"
"Enable VSync for smooth motion.",
targetFps, refreshRate);
}
}
ImGui::End();
}
@@ -253,12 +253,6 @@ public sealed partial class CxxLinearCodeGenerator
? index
: -1;
private static uint ClearPairedBit(uint mask, string destination)
{
var bit = PairedFloatRegisterBit(destination);
return bit >= 0 ? mask & ~(1u << bit) : mask;
}
private static HashSet<string> MaterializePairedFloatSet(uint mask, StringComparer comparer)
{
var set = new HashSet<string>(comparer);
@@ -460,21 +454,11 @@ public sealed partial class CxxLinearCodeGenerator
break;
}
case IrLoad load:
newOut = ClearPairedBit(newOut, load.Destination);
break;
case IrResolvedLoad load:
newOut = ClearPairedBit(newOut, load.Destination);
break;
case IrResolvedLoadPair pair:
newOut = ClearPairedBit(newOut, pair.FirstDestination);
newOut = ClearPairedBit(newOut, pair.SecondDestination);
break;
case IrResolvedPsqLoad load:
{
var destBit = PairedFloatRegisterBit(load.Destination);
if (destBit >= 0)
{
newOut |= 1u << destBit;
newOut &= ~(1u << destBit);
}
break;
}
@@ -70,7 +70,7 @@ internal static class JumpTableDetector
}
entriesAreRelative = HasRelativeEntryAdd(ordered, loadIndex, mtctrIndex, targetRegister, baseReg.Name);
if (!TryFindUpperBound(ordered, loadIndex, indexReg, out var upperBound) || upperBound < 0 || upperBound >= MaxEntryCount)
if (!TryFindUpperBound(ordered, loadIndex, indexReg.Name, out var upperBound) || upperBound < 0 || upperBound >= MaxEntryCount)
{
return false;
}
@@ -381,60 +381,47 @@ internal static class JumpTableDetector
return false;
}
private static bool TryFindUpperBound(IReadOnlyList<PpcInstruction> ordered, int startIndex, PpcRegisterOperand register, out int upperBound)
private static bool TryFindUpperBound(IReadOnlyList<PpcInstruction> ordered, int startIndex, string register, out int upperBound)
{
// Preferred: the compare on the lwzx index, followed back through the copies and
// shifts that derived it. A compare older than the value's definition bounds
// something else.
var tracked = register.Number;
// Preferred: compare directly on the same register used for lwzx indexing.
for (var i = startIndex - 1; i >= 0 && startIndex - i <= MaxBacktrackInstructions; i--)
{
var ins = ordered[i];
if (TryGetCompareImmediate(ins, out var compared, out var bound))
{
if (compared == tracked)
{
upperBound = bound;
return true;
}
continue;
}
if (ins.IsCall)
{
break;
}
if (!DefinesGpr(ins, tracked))
if (ins.Operands.Count < 2 || ins.Operands[0] is not PpcRegisterOperand reg ||
!string.Equals(reg.Name, register, StringComparison.OrdinalIgnoreCase) ||
ins.Operands[1] is not PpcImmediateOperand imm)
{
continue;
}
if (IsIndexDerivation(ins) &&
ins.Operands[0] is PpcRegisterOperand dest && dest.Number == tracked &&
ins.Operands[1] is PpcRegisterOperand source)
if (ins.Mnemonic is "cmplwi" or "cmpwi")
{
tracked = source.Number;
continue;
upperBound = imm.Value;
return true;
}
break;
}
// Fallback: some loops compare a logical counter (e.g. r18) distinct from the byte-offset
// register (e.g. r30) that feeds lwzx, so the bound check may not target the index register.
for (var i = startIndex - 1; i >= 0 && startIndex - i <= MaxBacktrackInstructions; i--)
{
if (ordered[i].Operands.Count > 0 && ordered[i].Operands[0] is PpcConditionRegisterOperand)
{
continue;
}
if (!TryGetCompareImmediate(ordered[i], out _, out var bound) || bound < 0 || bound >= MaxEntryCount)
var ins = ordered[i];
if (ins.Operands.Count < 2 || ins.Operands[1] is not PpcImmediateOperand imm)
{
continue;
}
upperBound = bound;
if (ins.Mnemonic is not ("cmplwi" or "cmpwi"))
{
continue;
}
if (imm.Value < 0 || imm.Value >= MaxEntryCount)
{
continue;
}
upperBound = imm.Value;
return true;
}
@@ -442,70 +429,6 @@ internal static class JumpTableDetector
return false;
}
// Copies, sign extensions and left shifts (slwi, clrlwi, clrlslwi) keep a bound on
// the source a bound on the result's entry index.
private static bool IsIndexDerivation(PpcInstruction ins) => ins.Mnemonic switch
{
"mr" or "extsb" or "extsh" => ins.Operands.Count >= 2,
"rlwinm" => ins.Operands.Count == 5 &&
ins.Operands[2] is PpcImmediateOperand sh &&
ins.Operands[4] is PpcImmediateOperand me &&
me.Value == 31 - sh.Value,
_ => false,
};
// MayWriteGpr treats both register fields of an X-form as written, which also
// counts sources (or, extsb, stwx, cmpw); read the decoded destination instead.
private static bool DefinesGpr(PpcInstruction ins, int register)
{
if (ins.Word >> 26 != 31)
{
return PpcRegisterEffects.MayWriteGpr(ins.Word, register);
}
var mnemonic = ins.Mnemonic;
if (mnemonic.EndsWith("ux", StringComparison.Ordinal) &&
ins.Operands.Count > 1 && ins.Operands[1] is PpcRegisterOperand updated && updated.Number == register)
{
return true;
}
if (mnemonic.StartsWith("st", StringComparison.Ordinal) ||
mnemonic.StartsWith("cmp", StringComparison.Ordinal) ||
mnemonic.StartsWith("mt", StringComparison.Ordinal) ||
mnemonic.StartsWith("dcb", StringComparison.Ordinal) ||
mnemonic.StartsWith("icb", StringComparison.Ordinal) ||
mnemonic.StartsWith("tw", StringComparison.Ordinal))
{
return false;
}
return ins.Operands.Count > 0 && ins.Operands[0] is PpcRegisterOperand dest && dest.Number == register;
}
private static bool TryGetCompareImmediate(PpcInstruction ins, out int register, out int immediate)
{
register = -1;
immediate = 0;
if (ins.Mnemonic is not ("cmplwi" or "cmpwi"))
{
return false;
}
var operands = ins.Operands;
var first = operands.Count > 0 && operands[0] is PpcConditionRegisterOperand ? 1 : 0;
if (operands.Count < first + 2 ||
operands[first] is not PpcRegisterOperand reg ||
operands[first + 1] is not PpcImmediateOperand imm)
{
return false;
}
register = reg.Number;
immediate = imm.Value;
return true;
}
private static bool TryReadWord(ProgramImage image, uint address, out uint value)
{
if (!image.Contains(address, sizeof(uint)))
@@ -538,122 +538,4 @@ public class FunctionTranslatorCoverageTests
Assert.Equal("// generated", result.CxxCode);
Assert.Same(metrics, result.Metrics);
}
[Fact]
public void Translate_JumpTableBoundFollowsShiftedIndexPastStaleCompare()
{
const uint entry = 0x80000000;
const uint table = 0x80000100;
const uint case0 = entry + 0x20;
const uint case1 = entry + 0x28;
const uint case2 = entry + 0x30;
var image = TranslatorCppTestHarness.CreateImage(
(entry + 0x00, 0x2C040001), // cmpwi r4, 1
(entry + 0x04, 0x28030002), // cmplwi r3, 2
(entry + 0x08, 0x5464103A), // slwi r4, r3, 2
(entry + 0x0C, 0x3D808000), // lis r12, 0x8000
(entry + 0x10, 0x398C0100), // addi r12, r12, 0x100
(entry + 0x14, 0x7C0C202E), // lwzx r0, r12, r4
(entry + 0x18, 0x7C0903A6), // mtctr r0
(entry + 0x1C, 0x4E800420), // bctr
(case0 + 0x00, 0x38600007), // li r3, 7
(case0 + 0x04, 0x4E800020), // blr
(case1 + 0x00, 0x38600009), // li r3, 9
(case1 + 0x04, 0x4E800020), // blr
(case2 + 0x00, 0x3860000B), // li r3, 11
(case2 + 0x04, 0x4E800020), // blr
(table + 0x00, case0),
(table + 0x04, case1),
(table + 0x08, case2));
var result = new FunctionTranslator(image).Translate(
entry,
TranslationOptions.Default with
{
MaxBytes = 0x80,
AllowUnsupportedInstructions = true,
KnownFunctionEntryPoints = new HashSet<uint> { entry }
});
Assert.Contains("case 0x80000030u:", result.CxxCode, StringComparison.Ordinal);
}
[Fact]
public void Translate_JumpTableBoundSurvivesCopyOfIndex()
{
const uint entry = 0x80000000;
const uint table = 0x80000100;
const uint case0 = entry + 0x28;
const uint case1 = entry + 0x30;
const uint case2 = entry + 0x38;
var image = TranslatorCppTestHarness.CreateImage(
(entry + 0x00, 0x28030002), // cmplwi r3, 2
(entry + 0x04, 0x7C7F1B78), // mr r31, r3
(entry + 0x08, 0x2C040001), // cmpwi r4, 1
(entry + 0x0C, 0x5460103A), // slwi r0, r3, 2
(entry + 0x10, 0x3D808000), // lis r12, 0x8000
(entry + 0x14, 0x398C0100), // addi r12, r12, 0x100
(entry + 0x18, 0x7C0C002E), // lwzx r0, r12, r0
(entry + 0x1C, 0x7C0903A6), // mtctr r0
(entry + 0x20, 0x4E800420), // bctr
(entry + 0x24, 0x4E800020), // blr
(case0 + 0x00, 0x38600007), // li r3, 7
(case0 + 0x04, 0x4E800020), // blr
(case1 + 0x00, 0x38600009), // li r3, 9
(case1 + 0x04, 0x4E800020), // blr
(case2 + 0x00, 0x3860000B), // li r3, 11
(case2 + 0x04, 0x4E800020), // blr
(table + 0x00, case0),
(table + 0x04, case1),
(table + 0x08, case2));
var result = new FunctionTranslator(image).Translate(
entry,
TranslationOptions.Default with
{
MaxBytes = 0x80,
AllowUnsupportedInstructions = true,
KnownFunctionEntryPoints = new HashSet<uint> { entry }
});
Assert.Contains("case 0x80000038u:", result.CxxCode, StringComparison.Ordinal);
}
[Fact]
public void Translate_JumpTableBoundAcceptsNonCr0Compare()
{
const uint entry = 0x80000000;
const uint table = 0x80000100;
const uint case0 = entry + 0x1C;
const uint case1 = entry + 0x24;
const uint case2 = entry + 0x2C;
var image = TranslatorCppTestHarness.CreateImage(
(entry + 0x00, 0x28830002), // cmplwi cr1, r3, 2
(entry + 0x04, 0x5463103A), // slwi r3, r3, 2
(entry + 0x08, 0x3D808000), // lis r12, 0x8000
(entry + 0x0C, 0x398C0100), // addi r12, r12, 0x100
(entry + 0x10, 0x7C0C182E), // lwzx r0, r12, r3
(entry + 0x14, 0x7C0903A6), // mtctr r0
(entry + 0x18, 0x4E800420), // bctr
(case0 + 0x00, 0x38600007), // li r3, 7
(case0 + 0x04, 0x4E800020), // blr
(case1 + 0x00, 0x38600009), // li r3, 9
(case1 + 0x04, 0x4E800020), // blr
(case2 + 0x00, 0x3860000B), // li r3, 11
(case2 + 0x04, 0x4E800020), // blr
(table + 0x00, case0),
(table + 0x04, case1),
(table + 0x08, case2));
var result = new FunctionTranslator(image).Translate(
entry,
TranslationOptions.Default with
{
MaxBytes = 0x80,
AllowUnsupportedInstructions = true,
KnownFunctionEntryPoints = new HashSet<uint> { entry }
});
Assert.Contains("case 0x8000002Cu:", result.CxxCode, StringComparison.Ordinal);
}
}
@@ -152,69 +152,4 @@ public sealed class PairedFlowCodeGenTests
Assert.Contains("SetCRFloatResident(cr, 0, f31.d, f1.d);", code, StringComparison.Ordinal);
Assert.DoesNotContain("SetCRFloatResident(cr, 0, PPC_PsToScalarInline(f31.d)", code, StringComparison.Ordinal);
}
[Fact]
public void ResolvedScalarLoadEndsPairedStateAcrossEdge()
{
var function = new IrFunction("resolved_load_clears_paired", "entry", new[]
{
new IrBasicBlock("entry", new IrInstruction[]
{
new IrCall("f5", "PPC_PsMul", new[] { IrValue.Register("f1"), IrValue.Register("f2") }),
new IrResolveGuestMemoryRange("range", IrValue.Register("r3"), 0, 8, true, false),
new IrResolvedLoad("f5", "range", new IrAddress("r3", 0), 0, 8),
new IrJump("next")
}),
new IrBasicBlock("next", new IrInstruction[]
{
new IrSetCrField(0, IrValue.Register("f5"), IrValue.Register("f1"), false),
new IrReturn(null)
})
});
var types = new RepresentationEnvironment(new Dictionary<string, ValueRepresentation>
{
["f1"] = ValueRepresentation.Float64,
["f2"] = ValueRepresentation.Float64,
["f5"] = ValueRepresentation.Float64,
["r3"] = ValueRepresentation.UInt32
});
var signature = new FunctionAbiClassification("resolved_load_clears_paired", ValueRepresentation.Void);
var code = new CxxLinearCodeGenerator().Emit(
0x80006008, new SsaTransformer().Convert(function), signature, types);
Assert.Contains("SetCRFloatResident(cr, 0, f5.d, f1.d);", code, StringComparison.Ordinal);
Assert.DoesNotContain("PPC_PsToScalarInline(f5.d)", code, StringComparison.Ordinal);
}
[Fact]
public void ResolvedPsqLoadStaysPairedAcrossEdge()
{
var function = new IrFunction("resolved_psq_load_paired", "entry", new[]
{
new IrBasicBlock("entry", new IrInstruction[]
{
new IrResolveGuestMemoryRange("range", IrValue.Register("r3"), 0, 8, true, false),
new IrResolvedPsqLoad("f5", "range", IrValue.Register("r3"), 0, 0, 0, 0u),
new IrJump("next")
}),
new IrBasicBlock("next", new IrInstruction[]
{
new IrSetCrField(0, IrValue.Register("f5"), IrValue.Register("f1"), false),
new IrReturn(null)
})
});
var types = new RepresentationEnvironment(new Dictionary<string, ValueRepresentation>
{
["f1"] = ValueRepresentation.Float64,
["f5"] = ValueRepresentation.Float64,
["r3"] = ValueRepresentation.UInt32
});
var signature = new FunctionAbiClassification("resolved_psq_load_paired", ValueRepresentation.Void);
var code = new CxxLinearCodeGenerator().Emit(
0x8000600C, new SsaTransformer().Convert(function), signature, types);
Assert.Contains("SetCRFloatResident(cr, 0, PPC_PsToScalarInline(f5.d), f1.d);", code, StringComparison.Ordinal);
}
}