Three defects from the switchover, all mine. vmaCreateAllocator crashed at pc 0 the first time frame generation was switched on. The allocator was created with physicalDevice and device but no instance and no vulkanApiVersion; Android builds with VK_NO_PROTOTYPES, so VMA resolves its table dynamically through vkGetInstanceProcAddr, which answers only for global functions when the instance is null. It stored nulls and called one. memory.cpp documents this exact failure at its own vmaCreateAllocator -- the two getters were supplied and the instance beside them was not. shader_count() then answered for the wrong cache, twice. First it still counted g_shaders, which import_shaders stopped filling when it moved to the ported extractor: generated_frame_count() multiplies by it, so generate() returned on its first line and frame generation sat on "starting" with nothing logged, because it never got far enough to fail. Replacing that with GetInstalledLosslessStatus() was also wrong -- that validates the DLL PATH, and the UI copies the picked file into app cache, which Android may purge. A re-import wrote a good 322KB shader cache and it still said "no shaders". It now asks whether the cache LOADS, which is the actual question: the shaders are extracted once and the source is not needed again. The dead initialize() call in generate() is gone with it. It built the dlopen'd library's context, nothing uses it, and a failure there could still disable the feature. Settings: the in-game menu offered the multiplier alone. Someone opening it mid-game is there to answer "it is generating but the picture is unsteady" or "it is generating but it is too expensive", and neither is the multiplier. Target rate, flow scale and performance mode now sit beside it. Target rate is new -- 0 keeps the fixed multiplier, non-zero is Camille's adaptive pacing.
ARMSX3
Uses the latest RPCS3 upstream code (the recent ARM64 improvements included).
Building
arm64-v8a and armv8.2 is supported. You need the Android SDK with NDK r27 or newer, CMake 3.30 or newer, and a JDK 17. Android Studio ships all of these.
Clone with submodules, then fetch the two third party checkouts that are not submodules:
git clone --recursive https://github.com/ARMSX2/ARMSX3.git
cd ARMSX3
git clone https://github.com/SnowflakePowered/librashader 3rdparty/librashader
git clone https://github.com/bylaws/libadrenotools android/armsx3-ui/app/src/main/cpp/libadrenotools
Build the core. This is the long part and produces an unstripped library of around 1.3 GB:
export ANDROID_HOME=$HOME/Library/Android/sdk
cmake -B build-android -G Ninja \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_HOME/ndk/<version>/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-31 \
-DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build-android --target rpcsx-android -j8
Strip it and put it where the app expects it:
llvm-strip --strip-unneeded build-android/android/libarmsx3-core.so
cp build-android/android/libarmsx3-core.so \
android/armsx3-ui/app/src/main/jniLibs/arm64-v8a/
Then build the app:
cd android/armsx3-ui
export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
./gradlew :app:assembleRelease
The apk lands in app/build/outputs/apk/release/.
Note that the core library has to be rebuilt and copied again whenever anything under rpcs3/ or android/src/ changes. Gradle does not build it for you.
The Discord Social SDK is proprietary and is not redistributed here. Get it from Discord's developer portal and drop it in app/libs/ and app/src/main/cpp/discord_sdk/ if you want that feature. The build skips it otherwise.
Running it needs PS3 firmware, which is not included. License
GPL-2.0-only, the same as RPCS3. See LICENSE. Some files may be licensed differently, check the file headers.
Based on RPCS3, https://github.com/RPCS3/rpcs3