mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 07:00:30 +02:00
Merge PR #2 from darthcircuit: Quest 1 support
Adds the modernQuest and quest1 headset flavours (Kryo CPU target, direct-VR library entry on Quest 1), records the CPU target in the game kit and checks it wherever a kit or game package is used, and gates the Quest 1 EFB-copy and pipeline-scheduling workarounds on the monterey device. Resolved docs/quest-port.md by keeping both sides: the foveated rendering section and the Quest 1 renderer compatibility section, and both Build-Quest.ps1 command lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
83805a410e
21 files changed
+431
-55
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import javax.inject.Inject
|
||||
import org.gradle.api.provider.Property
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
@@ -145,6 +146,34 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions += "headset"
|
||||
productFlavors {
|
||||
create("modernQuest") {
|
||||
dimension = "headset"
|
||||
manifestPlaceholders["mkwQuestSupportedDevices"] = "quest2|quest3|quest3s|questpro"
|
||||
buildConfigField("boolean", "QUEST1_DIRECT_LAUNCH", "false")
|
||||
buildConfigField("String", "ANDROID_CPU", "\"cortex-a77\"")
|
||||
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")
|
||||
buildConfigField("String", "ANDROID_CPU", "\"kryo\"")
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
// Snapdragon 835. cortex-a77 binaries terminate with SIGILL on Quest 1.
|
||||
arguments += "-DMKW_ANDROID_CPU=kryo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
// aurora-main needs CMake 3.25+, newer than the SDK's bundled 3.22.1;
|
||||
@@ -201,6 +230,9 @@ abstract class ExportQuestGameKit : DefaultTask() {
|
||||
@get:Internal
|
||||
abstract val llvmStrip: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val androidCpu: Property<String>
|
||||
|
||||
@get:Inject
|
||||
abstract val execOperations: ExecOperations
|
||||
|
||||
@@ -212,18 +244,37 @@ abstract class ExportQuestGameKit : DefaultTask() {
|
||||
@TaskAction
|
||||
fun export() {
|
||||
val app = appDir.get().asFile
|
||||
val probe = File(app, "build/intermediates/cxx").walkTopDown()
|
||||
data class Candidate(val probe: File, val binaryDir: File, val cpu: String?)
|
||||
fun cacheValue(cache: String, name: String): String? =
|
||||
Regex("(?m)^${Regex.escape(name)}(?::[^=\\r\\n]*)?=([^\\r\\n]*)$")
|
||||
.find(cache)?.groupValues?.get(1)?.trim()
|
||||
|
||||
val candidates = File(app, "build/intermediates/cxx").walkTopDown()
|
||||
.filter { it.name == "libmkw_quest_kit_probe.so" && it.parentFile.name == "arm64-v8a" }
|
||||
.maxByOrNull { it.lastModified() }
|
||||
?: throw GradleException("No libmkw_quest_kit_probe.so; the native build did not produce the game kit probe")
|
||||
val configuration = probe.parentFile.parentFile.parentFile // <BuildType>/<hash>
|
||||
val binaryDir = File(app, ".cxx/${configuration.parentFile.name}/${configuration.name}/arm64-v8a")
|
||||
if (!File(binaryDir, "build.ninja").isFile) throw GradleException("No CMake tree at $binaryDir")
|
||||
.map { probe ->
|
||||
val configuration = probe.parentFile.parentFile.parentFile // <Variant>/<hash>
|
||||
val binaryDir = File(app, ".cxx/${configuration.parentFile.name}/${configuration.name}/arm64-v8a")
|
||||
val cacheFile = File(binaryDir, "CMakeCache.txt")
|
||||
val cache = cacheFile.takeIf { it.isFile }?.readText().orEmpty()
|
||||
Candidate(probe, binaryDir, cacheValue(cache, "MKW_ANDROID_CPU"))
|
||||
}
|
||||
.toList()
|
||||
val expectedCpu = androidCpu.get()
|
||||
val matching = candidates.filter {
|
||||
it.cpu == expectedCpu && File(it.binaryDir, "build.ninja").isFile
|
||||
}
|
||||
val chosen = matching.maxByOrNull { it.probe.lastModified() }
|
||||
?: throw GradleException(
|
||||
"No CMake tree for Android CPU $expectedCpu. Found: " +
|
||||
candidates.joinToString { "${it.binaryDir} (cpu=${it.cpu})" }
|
||||
)
|
||||
val binaryDir = chosen.binaryDir
|
||||
val kitDir = File(outputDir.get().asFile, "game_kit")
|
||||
execOperations.exec {
|
||||
commandLine(
|
||||
"powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", script.get().asFile.path,
|
||||
"-CMakeBinaryDir", binaryDir.path, "-OutputDir", kitDir.path, "-LlvmStrip", llvmStrip.get().asFile.path,
|
||||
"-AndroidCpu", expectedCpu,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -273,6 +324,8 @@ androidComponents {
|
||||
dependsOn("merge${capitalized}NativeLibs")
|
||||
appDir.set(layout.projectDirectory)
|
||||
script.set(rootProject.layout.projectDirectory.file("Export-QuestGameKit.ps1"))
|
||||
androidCpu.set(if (variant.name.startsWith("quest1", ignoreCase = true)) "kryo" else "cortex-a77")
|
||||
outputDir.set(layout.buildDirectory.dir("generated/assets/questGameKit/${variant.name}"))
|
||||
llvmStrip.set(sdkComponents.ndkDirectory.map {
|
||||
it.file("toolchains/llvm/prebuilt/$host/bin/llvm-strip" + if (host.startsWith("windows")) ".exe" else "")
|
||||
})
|
||||
|
||||
@@ -50,12 +50,13 @@
|
||||
android:label="@string/app_name"
|
||||
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" />
|
||||
<!-- 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" />
|
||||
|
||||
<!-- 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
|
||||
android:name=".launcher.LauncherActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|density"
|
||||
@@ -68,10 +69,6 @@
|
||||
android:defaultHeight="640dp"
|
||||
android:minWidth="760dp"
|
||||
android:minHeight="520dp" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Extracts the player's disc image and imports games built on a PC (launcher process). -->
|
||||
@@ -97,11 +94,6 @@
|
||||
<property
|
||||
android:name="android.window.PROPERTY_XR_ACTIVITY_START_MODE"
|
||||
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>
|
||||
</application>
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ object GameLibrary {
|
||||
if (manifest == null || !library(context, profile).isFile) {
|
||||
return Status.Missing
|
||||
}
|
||||
return if (manifest.optString("kitFingerprint") == kitFingerprint(context)) Status.Ready else Status.Stale
|
||||
return if (manifest.optString("kitFingerprint") == kitFingerprint(context) &&
|
||||
manifest.optString("androidCpu") == BuildConfig.ANDROID_CPU
|
||||
) Status.Ready else Status.Stale
|
||||
}
|
||||
}
|
||||
@@ -140,6 +140,9 @@ object GameBuild {
|
||||
if (kitJson.optInt("schema") != KIT_SCHEMA) {
|
||||
return "This app's game kit is version ${kitJson.optInt("schema")}, which this builder does not know."
|
||||
}
|
||||
if (kitJson.optString("androidCpu") != BuildConfig.ANDROID_CPU) {
|
||||
return "This app contains a game kit for ${kitJson.optString("androidCpu")}, but this build requires ${BuildConfig.ANDROID_CPU}."
|
||||
}
|
||||
val recipe = kitJson.getJSONObject("products").optJSONObject(profile.id)
|
||||
?: return "This app's game kit cannot build ${profile.id}."
|
||||
report(40, Step.Prepare)
|
||||
@@ -646,6 +649,7 @@ object GameBuild {
|
||||
.put("dolSha256", BuildConfig.DISC_DOL_SHA256)
|
||||
.put("relSha256", BuildConfig.DISC_REL_SHA256)
|
||||
.put("kitFingerprint", kitFingerprint)
|
||||
.put("androidCpu", BuildConfig.ANDROID_CPU)
|
||||
.put("library", GameLibrary.LIBRARY_NAME)
|
||||
.put("librarySha256", BuildRecipe.hex(digest.digest()))
|
||||
.put("includesData", false)
|
||||
|
||||
@@ -157,6 +157,9 @@ object GamePackageImport {
|
||||
if (manifest.optString("kitFingerprint") != GameLibrary.kitFingerprint(context)) {
|
||||
return "This game file was built for a different version of this app. Build it again against the version installed on the headset."
|
||||
}
|
||||
if (manifest.optString("androidCpu") != BuildConfig.ANDROID_CPU) {
|
||||
return "This game file targets ${manifest.optString("androidCpu")}, but this app requires ${BuildConfig.ANDROID_CPU}. Build it again for the correct headset."
|
||||
}
|
||||
val expected = manifest.optString("librarySha256")
|
||||
if (librarySha256 == null) {
|
||||
return "This game file has no game library (libmain.so)."
|
||||
|
||||
@@ -815,6 +815,10 @@ class LauncherActivity : Activity() {
|
||||
}
|
||||
|
||||
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) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<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_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>
|
||||
|
||||
@@ -102,6 +103,7 @@
|
||||
<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_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_reset">Reset 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>
|
||||
Reference in new issue
Block a user