Quest: make Quest 1 builds reproducible

This commit is contained in:
darthcircuit committed 2026-09-22 11:14:31 -06:00
1 parent 83fe04d160
commit 6e6406c200
9 files changed
+124 -17

No files matched your search

+11 -4
View File
@@ -1,7 +1,7 @@
# Builds the standalone Meta Quest APK on a Windows host. # Builds the standalone Meta Quest APK on a Windows host.
# #
# powershell -ExecutionPolicy Bypass -File android/Build-Quest.ps1 [-Generated <dir>] # powershell -ExecutionPolicy Bypass -File android/Build-Quest.ps1 [-Generated <dir>]
# [-Configuration debug|release] [-Install] # [-Headset modern|quest1] [-Configuration debug|release] [-Install]
# #
# One app offers both games: the APK carries a kit for the base game, and for Retro Rewind too # One app offers both games: the APK carries a kit for the base game, and for Retro Rewind too
# when -Generated holds a translation that includes the mod. # when -Generated holds a translation that includes the mod.
@@ -20,6 +20,7 @@ param(
[string]$Generated = '', [string]$Generated = '',
[string]$Dependencies = '', [string]$Dependencies = '',
[string]$CMakeDir = '', [string]$CMakeDir = '',
[ValidateSet('modern', 'quest1')] [string]$Headset = 'modern',
[ValidateSet('debug', 'release')] [string]$Configuration = 'debug', [ValidateSet('debug', 'release')] [string]$Configuration = 'debug',
[switch]$Install [switch]$Install
) )
@@ -73,7 +74,8 @@ if (-not $Dependencies) {
} }
$variant = (Get-Culture).TextInfo.ToTitleCase($Configuration) $variant = (Get-Culture).TextInfo.ToTitleCase($Configuration)
$task = "app:assemble$variant" $flavour = if ($Headset -eq 'quest1') { 'Quest1' } else { 'ModernQuest' }
$task = "app:assemble$flavour$variant"
$gradleArgs = @( $gradleArgs = @(
'--project-dir', $root, '--project-dir', $root,
"-PmkwGeneratedDir=$Generated" "-PmkwGeneratedDir=$Generated"
@@ -84,7 +86,8 @@ Write-Host "gradlew $($gradleArgs -join ' ')"
& (Join-Path $root 'gradlew.bat') @gradleArgs & (Join-Path $root 'gradlew.bat') @gradleArgs
if ($LASTEXITCODE -ne 0) { throw "Gradle failed ($LASTEXITCODE)" } if ($LASTEXITCODE -ne 0) { throw "Gradle failed ($LASTEXITCODE)" }
$apkDir = Join-Path $root "app\build\outputs\apk\$Configuration" $flavourDir = if ($Headset -eq 'quest1') { 'quest1' } else { 'modernQuest' }
$apkDir = Join-Path $root "app\build\outputs\apk\$flavourDir\$Configuration"
$apk = Get-ChildItem -Path $apkDir -Filter '*.apk' | Select-Object -First 1 $apk = Get-ChildItem -Path $apkDir -Filter '*.apk' | Select-Object -First 1
if (-not $apk) { throw "No APK under $apkDir" } if (-not $apk) { throw "No APK under $apkDir" }
Write-Host "APK: $($apk.FullName)" Write-Host "APK: $($apk.FullName)"
@@ -106,5 +109,9 @@ if ($Install) {
$adb = Join-Path $sdkRoot 'platform-tools\adb.exe' $adb = Join-Path $sdkRoot 'platform-tools\adb.exe'
& $adb install -r $apk.FullName & $adb install -r $apk.FullName
if ($LASTEXITCODE -ne 0) { throw "adb install failed ($LASTEXITCODE)" } if ($LASTEXITCODE -ne 0) { throw "adb install failed ($LASTEXITCODE)" }
Write-Host 'Installed. Build a game with Build on this Quest in the launcher, or on this PC with android/Build-QuestGame.ps1 -Install.' if ($Headset -eq 'quest1') {
Write-Host 'Installed. Use WiiCompiled Settings for setup, then launch WiiCompiled VR directly from the library.'
} else {
Write-Host 'Installed. Build a game with Build on this Quest in the launcher, or on this PC with android/Build-QuestGame.ps1 -Install.'
}
} }
+26
View File
@@ -139,6 +139,32 @@ android {
} }
} }
flavorDimensions += "headset"
productFlavors {
create("modernQuest") {
dimension = "headset"
manifestPlaceholders["mkwQuestSupportedDevices"] = "quest2|quest3|quest3s|questpro"
buildConfigField("boolean", "QUEST1_DIRECT_LAUNCH", "false")
externalNativeBuild {
cmake {
// Snapdragon XR2 and newer. Keep this as the default build target.
arguments += "-DMKW_ANDROID_CPU=cortex-a77"
}
}
}
create("quest1") {
dimension = "headset"
manifestPlaceholders["mkwQuestSupportedDevices"] = "quest|quest2"
buildConfigField("boolean", "QUEST1_DIRECT_LAUNCH", "true")
externalNativeBuild {
cmake {
// Snapdragon 835. cortex-a77 binaries terminate with SIGILL on Quest 1.
arguments += "-DMKW_ANDROID_CPU=kryo"
}
}
}
}
externalNativeBuild { externalNativeBuild {
cmake { cmake {
// aurora-main needs CMake 3.25+, newer than the SDK's bundled 3.22.1; // aurora-main needs CMake 3.25+, newer than the SDK's bundled 3.22.1;
+3 -11
View File
@@ -37,12 +37,13 @@
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/Theme.WiiCompiled.Quest"> android:theme="@style/Theme.WiiCompiled.Quest">
<meta-data android:name="com.oculus.supportedDevices" android:value="quest2|quest3|quest3s|questpro" /> <meta-data android:name="com.oculus.supportedDevices" android:value="${mkwQuestSupportedDevices}" />
<meta-data android:name="com.oculus.vr.focusaware" android:value="true" /> <meta-data android:name="com.oculus.vr.focusaware" android:value="true" />
<!-- Let the runtime move CPU budget to the GPU; Aurora's replay is GPU-bound. --> <!-- Let the runtime move CPU budget to the GPU; Aurora's replay is GPU-bound. -->
<meta-data android:name="com.oculus.trade_cpu_for_gpu_amount" android:value="-1" /> <meta-data android:name="com.oculus.trade_cpu_for_gpu_amount" android:value="-1" />
<!-- The app opens on this 2D panel: Play starts QuestActivity, Settings edits Config.toml. --> <!-- The setup panel: Play starts QuestActivity on modern Horizon OS; the
Quest 1 flavour exposes it as a separate Settings library entry. -->
<activity <activity
android:name=".launcher.LauncherActivity" android:name=".launcher.LauncherActivity"
android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|density" android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|density"
@@ -55,10 +56,6 @@
android:defaultHeight="640dp" android:defaultHeight="640dp"
android:minWidth="760dp" android:minWidth="760dp"
android:minHeight="520dp" /> android:minHeight="520dp" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity> </activity>
<!-- Extracts the player's disc image and imports games built on a PC (launcher process). --> <!-- Extracts the player's disc image and imports games built on a PC (launcher process). -->
@@ -84,11 +81,6 @@
<property <property
android:name="android.window.PROPERTY_XR_ACTIVITY_START_MODE" android:name="android.window.PROPERTY_XR_ACTIVITY_START_MODE"
android:value="XR_ACTIVITY_START_MODE_FULL_SPACE_UNMANAGED" /> android:value="XR_ACTIVITY_START_MODE_FULL_SPACE_UNMANAGED" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.oculus.intent.category.VR" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity> </activity>
</application> </application>
@@ -585,6 +585,10 @@ class LauncherActivity : Activity() {
} }
private fun play() { private fun play() {
if (BuildConfig.QUEST1_DIRECT_LAUNCH) {
Toast.makeText(this, R.string.home_quest1_launch_from_library, Toast.LENGTH_LONG).show()
return
}
if (launching) { if (launching) {
return return
} }
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">WiiCompiled VR</string> <string name="app_name">WiiCompiled VR</string>
<string name="quest1_settings_name">WiiCompiled Settings</string>
<string name="missing_game_data_title">Game data not found</string> <string name="missing_game_data_title">Game data not found</string>
<string name="missing_game_data_message">Copy the extracted DATA directory of your own PAL RMCP01 Mario Kart Wii disc to:\n\n%1$s\n\nFor example with adb: adb push DATA /sdcard/Android/data/org.wiicompiled.quest/files/WiiCompiledOpenXRVR/DATA\n\nThen start the app again.</string> <string name="missing_game_data_message">Copy the extracted DATA directory of your own PAL RMCP01 Mario Kart Wii disc to:\n\n%1$s\n\nFor example with adb: adb push DATA /sdcard/Android/data/org.wiicompiled.quest/files/WiiCompiledOpenXRVR/DATA\n\nThen start the app again.</string>
@@ -77,6 +78,7 @@
<string name="home_replace_title">Replace the game files?</string> <string name="home_replace_title">Replace the game files?</string>
<string name="home_replace_message">Your current DATA folder stays until the new disc image has been checked and completely extracted.</string> <string name="home_replace_message">Your current DATA folder stays until the new disc image has been checked and completely extracted.</string>
<string name="home_launch_failed">The game could not be started.</string> <string name="home_launch_failed">The game could not be started.</string>
<string name="home_quest1_launch_from_library">Quest 1 cannot switch this settings panel into VR. Close it, then launch WiiCompiled VR from your library.</string>
<string name="home_close_game_first">Close the game first.</string> <string name="home_close_game_first">Close the game first.</string>
<string name="home_reset">Reset installation</string> <string name="home_reset">Reset installation</string>
<string name="home_reset_title">Reset the installation?</string> <string name="home_reset_title">Reset the installation?</string>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<!-- Modern Horizon OS can move from this 2D setup panel into immersive VR. -->
<activity android:name=".launcher.LauncherActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".QuestActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.oculus.intent.category.VR" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<!-- Quest 1's final Horizon OS cannot promote a running 2D panel to an
immersive activity. Put the game itself in the library instead. -->
<activity android:name=".QuestActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.oculus.intent.category.VR" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter android:priority="1">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity-alias
android:name=".SettingsLauncher"
android:exported="true"
android:label="@string/quest1_settings_name"
android:targetActivity=".launcher.LauncherActivity">
<intent-filter android:priority="-1">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
</application>
</manifest>
+8 -1
View File
@@ -154,6 +154,13 @@ elseif (_aurora_dawn_provider STREQUAL "package")
endif () endif ()
set(AURORA_DAWN_PACKAGE_URL set(AURORA_DAWN_PACKAGE_URL
"https://github.com/theofficialgman/dawn-build/releases/download/${AURORA_DAWN_VERSION}/dawn-${_dawn_system}-${_dawn_arch}.tar.gz") "https://github.com/theofficialgman/dawn-build/releases/download/${AURORA_DAWN_VERSION}/dawn-${_dawn_system}-${_dawn_arch}.tar.gz")
# Meta's Quest 1 firmware advertises VK_EXT_debug_utils but rejects it at
# vkCreateInstance. This pinned Android package disables that extension on
# Android; it is also the package validated on modern Quest headsets.
if (_dawn_system STREQUAL "android" AND _dawn_arch STREQUAL "aarch64")
set(AURORA_DAWN_PACKAGE_URL
"https://github.com/encounter/dawn-build/releases/download/${AURORA_DAWN_VERSION}/dawn-android-aarch64.tar.gz")
endif ()
# A release asset is mutable: the same tag has already served two different windows-amd64 archives, # A release asset is mutable: the same tag has already served two different windows-amd64 archives,
# and a cached extraction is never re-verified. Pin the digest for the combinations we ship. # and a cached extraction is never re-verified. Pin the digest for the combinations we ship.
@@ -181,7 +188,7 @@ elseif (_aurora_dawn_provider STREQUAL "package")
"SHA256=f97701d26fd1f25bbcc260b4c31736ede134c730c12556029e2470fde967f424") "SHA256=f97701d26fd1f25bbcc260b4c31736ede134c730c12556029e2470fde967f424")
elseif (_dawn_system STREQUAL "android" AND _dawn_arch STREQUAL "aarch64") elseif (_dawn_system STREQUAL "android" AND _dawn_arch STREQUAL "aarch64")
set(AURORA_DAWN_PACKAGE_URL_HASH set(AURORA_DAWN_PACKAGE_URL_HASH
"SHA256=0e63e8cbf53551f703f582d1306f4257c0380353f66b53369d96952ce6d9f934") "SHA256=27d910dee1201fd1e5b6ac567f0ba2306ebf2135e9f40b6929976c365d38b09b")
endif () endif ()
endif () endif ()
endif () endif ()
+21 -1
View File
@@ -107,6 +107,22 @@ the result. Live RGB5A3 menu copies use a minimal RGBA passthrough shader for
the same reason. Other Android devices retain the normal filtered and the same reason. Other Android devices retain the normal filtered and
format-converting paths. format-converting paths.
Quest 1 must be built with the `quest1` flavour (`Build-Quest.ps1 -Headset
quest1`). It targets the Snapdragon 835's Kryo CPU; the modern flavour targets
`cortex-a77`, whose instructions can terminate the game with `SIGILL` on Quest
1. Android builds use a pinned Dawn package which does not enable
`VK_EXT_debug_utils`: the old Quest firmware advertises that extension but
rejects it when Dawn creates the Vulkan instance.
The final Quest 1 firmware also cannot reliably promote this app's 2D setup
panel into an immersive activity. Its APK therefore exposes two library
entries: **WiiCompiled VR** starts the game directly in VR, while
**WiiCompiled Settings** opens the setup/settings panel. After changing a
setting, close the panel and start WiiCompiled VR from the library. The Play
button in the settings panel explains this limitation instead of opening a
black panel. Modern Quest builds keep the normal single launcher, whose Play
button starts VR directly.
The workaround was isolated on a physical Quest 1: it changed the boot/menu The workaround was isolated on a physical Quest 1: it changed the boot/menu
output from flickering black or white frames to a complete menu with character output from flickering black or white frames to a complete menu with character
and vehicle previews. It does intentionally skip Wii-era RGB5A3 quantization on and vehicle previews. It does intentionally skip Wii-era RGB5A3 quantization on
@@ -459,7 +475,10 @@ Android facts this design rests on, all measured on a Quest 3:
also learned `--target-os windows|macos|linux|android` for also learned `--target-os windows|macos|linux|android` for
`generate-data-init` and `translate-mod`, for pipelines that generate on `generate-data-init` and `translate-mod`, for pipelines that generate on
another host. another host.
- `android/`: the Gradle project, one app with no flavours. - `android/`: the Gradle project, with `modernQuest` (the default script
target) and `quest1` headset flavours. They share the application ID and
storage, but select the appropriate CPU baseline, supported-device manifest,
and launcher behavior.
`app/src/main/cpp/CMakeLists.txt` adds the repository's `runtime/` as a `app/src/main/cpp/CMakeLists.txt` adds the repository's `runtime/` as a
subdirectory with those Android choices and builds both game kit probes subdirectory with those Android choices and builds both game kit probes
(the Retro Rewind one only when the translation includes the mod), which (the Retro Rewind one only when the translation includes the mod), which
@@ -481,6 +500,7 @@ installer's `BuildWorkspace/generated`, produced by the normal Windows pipeline)
```powershell ```powershell
powershell -ExecutionPolicy Bypass -File android/Prepare-QuestDependencies.ps1 # SDL3 3.4.4 AAR into android/app/libs powershell -ExecutionPolicy Bypass -File android/Prepare-QuestDependencies.ps1 # SDL3 3.4.4 AAR into android/app/libs
powershell -ExecutionPolicy Bypass -File android/Build-Quest.ps1 -Install # the app, its game kit and toolchain, debug-signed powershell -ExecutionPolicy Bypass -File android/Build-Quest.ps1 -Install # the app, its game kit and toolchain, debug-signed
powershell -ExecutionPolicy Bypass -File android/Build-Quest.ps1 -Headset quest1 -Install # Quest 1: Kryo CPU and direct-VR library entry
powershell -ExecutionPolicy Bypass -File android/Build-QuestGame.ps1 -Install # your game, against that kit, into Import (or WheelWizard VR's Build for Quest) powershell -ExecutionPolicy Bypass -File android/Build-QuestGame.ps1 -Install # your game, against that kit, into Import (or WheelWizard VR's Build for Quest)
powershell -ExecutionPolicy Bypass -File android/Build-QuestGame.ps1 -Product retro_rewind -Mod <RetroRewind6> -Install # the mod and its pack (needs translate-mod output) powershell -ExecutionPolicy Bypass -File android/Build-QuestGame.ps1 -Product retro_rewind -Mod <RetroRewind6> -Install # the mod and its pack (needs translate-mod output)
adb push MarioKart.iso /sdcard/Download/ # then Select disc image in the launcher adb push MarioKart.iso /sdcard/Download/ # then Select disc image in the launcher