Repository navigation
Android port: build an arm64 APK from your own disc (Vulkan, touch controls, on-device training) - #13
LiquidAzir wants to merge 6 commits into
Conversation
…on-device training scripts/android/build.py builds BlueWake for arm64 Android on top of the Windows builder: the same pins, disc checks, translation, verified digest, mods and source steps, then the game module and the app compiled with the NDK's clang (Aurora through Dawn on Vulkan, SDL3 static in libmain.so) and an APK assembled without Gradle. --device trains the optimization profile on the phone itself (an instrumented module played headless to player control on Outset), as the Windows builder trains on the PC. android/ holds the app: the entry shim (paths in the app's storage, the session log, launch.env), the touch overlay and its bridge to Aurora's virtual pad, and the activity (a 60 Hz display request). Two changes are made to SDL's Java as the builder copies it: the game thread's stack (64 MB, as on Windows) and SDLSurface telling Aurora when its surface is ready, which Aurora's Android window code waits for. Shared code: Android's Back opens the options menu (settings_menu.cpp), and cmake/composite accepts extra sources (the training module's profile flush). Verified on a Galaxy Z Fold 7 (Snapdragon 8 Elite): the verified digest, boot to play on Outset with touch, the trained module; docs/ANDROID.md has the numbers, including the phone's thermal frequency limit that sets sustained speed.
--train-app (with --device) trains libmain.so as --device trains the game module: an instrumented app (the host, GXRuntime, Aurora and SDL) installed with the real module plays the opening once, drawn and at the game's pace, and the app is compiled with the counts (build/android/pgo-app, used by every later app build; --no-app-profile leaves it out). The module's training is headless, so it never runs the GX worker or the render thread. The entry shim writes the app's counts itself after the host returns, naming the file again (the profiling runtime read LLVM_PROFILE_FILE before launch.env set it). libmain.so is linked with -Bsymbolic-functions: its exported functions were called through the PLT even from within it. On the Fold 7, over the title's sea, the GX worker went from 51 to 47 M cycles a frame (docs/ANDROID.md).
…nd zlib, as on iOS)
The folders adb makes in the app's storage are shell's (group ext_data_rw, 2770). On the Fold 7 (Android 16) a newly installed app could not search them and stopped with "the prepared game executable is missing"; the files themselves were readable. install.py now makes the two folders 2775.
There was a problem hiding this comment.
All reported issues were addressed across 14 files
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
…the builder and installer) - Touch: pointer ids 16-31 are tracked; an earlier short tap's timer no longer ends a newer tap's latch; while the options menu is open, a touch away from the controls is the menu's (bluewake_settings_is_open), not the C-stick. - The adb-only showWhenLocked switch counts only while files/launch.env exists (written over adb; no other app can), and every launch decides again (onNewIntent), so an ordinary launch turns it off. - The manifest requires Vulkan 1.1, the renderer's API, and not OpenGL ES. - The session log's pump starts before stdout and stderr are redirected, and a training run's exit waits for it to drain instead of a fixed 300 ms. - build.py: the app's profile goes to CMake as a path (a path with spaces works); SDL's Java is found beside the app build being packaged; the label is escaped for the manifest; the module training's check run is inside the cleanup; --train-app reuses its profile while its inputs are unchanged. - install.py: main.dol and the disc image are skipped only when the size and SHA-1 match, and every REL is checked.
|
Thanks for the review. e3f0947 addresses all 15 comments (replies in each thread). Checked so far: the branch builds (Java, libmain.so and the APK); the manifest now requires Vulkan 1.1 and makes OpenGL ES optional; Still to do: an on-device check of the touch and launch changes (the menu's right side by touch, rapid taps, and the lock-screen switch only with |
There was a problem hiding this comment.
All reported issues were addressed across 10 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
On-device check of e3f0947 done on the Galaxy Z Fold 7 (cover screen), from a fresh
|
The Android touch overlay reads it from its own thread (bluewake_settings_is_open); every access now goes through the atomic.
|
Hi @LiquidAzir, thanks for all the work on this. Wind Waker Recomp is moving into BlueWake, which Elliott and I now maintain together. Could you open this against https://github.com/chrissotraidis/bluewake? An Android port is new platform work, so we'd review it there as its own project, with your authorship. Building the APK from the player's own disc fits how BlueWake handles phones and tablets. |
Hi! I got Wind Waker Recomp running natively on Android and wanted to offer the port back in case it's useful.
It follows the project's model exactly: the player builds their own APK from their own disc, and nothing
game-derived is in this PR.
What it is
scripts/android/build.pybuilds on top ofscripts/windows/build.py: the same RecompCore/DolRecomp pins, discchecks, translation, verified digest (
54f54434), mods and source steps run on the Windows PC, then the gamemodule and the app are compiled for arm64 Android with the NDK's clang. Aurora already supports Android, so it
draws through Dawn on Vulkan (Aurora's prebuilt
dawn-android-aarch64), with SDL3 statically linked intolibmain.so. The APK is assembled without Gradle (javac, d8, aapt2, apksigner from the SDK).android/: an entry shim (default paths in the app's storage, a session log mirrored to logcat, an optionallaunch.env), a touch overlay (stick, A/B/X/Y, L/R/Z, START, D-pad, drag on the right half for the C-stick)that feeds Aurora's virtual pad the way the iOS overlay does and hides when a controller connects, and the
activity (a 60 Hz display request).
scripts/android/install.pyinstalls the APK and pushesmain.dol, the RELs and the disc image to the app'sfolder over adb.
--device SERIALruns the local optimization training on the phone: an instrumented module plays theWindows builder's training route headless to player control on Outset (plain, then widescreen + Better Wind
Waker), and the counts guide the real compile. The profile stays local, as on the other platforms.
--train-app(with--device) also trainslibmain.soon the phone. The module's training is headless, soit never runs the GX worker or the render thread; this one plays the opening once, drawn and paced, with an
instrumented app and the real module. With
libmain.solinked-Bsymbolic-functions(its exported functionswere called through the PLT even from within), the GX worker went from 51 to 47 M cycles a frame on the
title's sea.
settings_menu.cpp, one line), andcmake/compositeaccepts optional extra sources (used only for the training module's profile flush).Two things Android needed that might be worth knowing
SDLSurfaceto callauroraNativeSetSurfaceReady, which stock SDLnever does, so the game hangs before its first frame. The builder patches SDL's Java as it copies it (along with
a 64 MB stack for SDL's game thread, matching the Windows link).
CPU limit from 4.47 GHz to about 2.0/1.7/1.4 GHz as the surface passes 38/40/42 °C. Work per frame and memory
stay flat (measured with
simpleperf stat), so late-session slowdowns are the frequency cap rather than a leak.docs/ANDROID.mdhas the numbers.Tested on one device, a Galaxy Z Fold 7 (Snapdragon 8 Elite, Adreno 830, Android 16), from a Redump-verified
disc. This branch, rebased on
windows-release8cc06de (RecompCore 9618e9d), generates the verified composite(
54f54434), builds with the default options and, freshly installed withinstall.py, plays the scripted routeto player control on Outset. Before the rebase the port was played on the phone for some hours: it boots and plays
on both screens with touch, and runs at 30 FPS in most of the opening, with dips in the
heaviest views once the phone throttles. Not yet tried: other devices and GPUs, controllers on Android, saving and reloading, the
new save states and climbing on Android, the HD packs and the later game. See
docs/ANDROID.md.Things you may want to change
dev.bluewake.BlueWake) and the label to "Wind WakerRecomp". Both are builder options.
subclassing of
scripts/builder.--ez showWhenLocked trueextra, for testing and training on a locked phone.Drop it if you'd rather not have it.
docs/ANDROID.mdis written in the style ofdocs/WINDOWS.md.This was developed with substantial AI assistance (Claude), like the rest of the project.
🤖 Generated with Claude Code
Summary by cubic
Adds a native Android (arm64) port:
scripts/android/build.pybuilds a signed APK on a Windows PC from your own disc, reusing the Windows builder's pins, disc checks, translation, and verified composite digest. The game module and app are compiled with the NDK's clang, drawing through Aurora on Vulkan with SDL3, and the optimization profile can be trained on the phone itself.What's in it
android/app: entry shim, touch overlay (stick, buttons, D-pad, C-stick drag) feeding Aurora's virtual pad, activity, and docs.scripts/android/install.pyinstalls the APK and pushesmain.dol, the RELs, and the disc image over adb.--device SERIALtrains the game module on the phone;--train-appalso trainslibmain.so(-Bsymbolic-functionscut the GX worker from 51 to 47 M cycles a frame on the title's sea).Noteworthy details
SDLSurfaceis ready (stock SDL never does, so the game would hang before its first frame).bluewake_settings_is_openhook (an atomic flag for cross-thread reads) leaves a touch away from the controls to the menu;cmake/compositeaccepts optional extra sources for the training module's profile flush.climb.c,save_state.cand zlib, as on iOS), andinstall.pymakes the pushed game folders 2775 so a new app can read them.launch.envis present; the manifest requires Vulkan 1.1;install.pyskips existing files only on size plus SHA-1 match.docs/ANDROID.mddocuments the phone's thermal throttling that sets sustained speed.Written for commit b66adcb. Summary will update on new commits.