The cache was written with one flag set and read with another. BuildShaderCache asked for allow_fp16 = true while LsfgShaders, the only consumer, asks for (false, false), and the header stores those flags and is rejected when they differ -- so the cache written at import could never satisfy the read after it. On a desktop that is invisible: the source DLL is still there, LoadShaderModules falls through to re-parsing the executable, and the cache is dead weight rewritten every launch. Android has nothing to fall through to, because the picked file is a copy in app cache that the system may clear, so it surfaced as "no shaders" with a valid cache sitting next to it. Worth telling Camille: Eden and ARMSX2 are both re-parsing Lossless.dll every launch rather than using their cache. The cache also survives a missing source now. It is validated against the source's size and mtime, which is right where an install stays put and wrong here; passing 0 skips those checks, matching what source_hash and variant already do. A replaced Lossless Scaling is no longer noticed automatically, which is the trade -- against losing the cache to a routine cache sweep, and re-importing is explicit. Performance: the fence wait moved from after the submit to before the next one. Waiting at the end put the whole interpolation on the critical path -- the thread sat idle until the GPU finished, every frame -- when nothing required it: frame generation and the present blits share one queue, so submission order already orders them. Waiting at the START only blocks when the previous frame's passes have not finished in time. The queue is now taken from the device rather than a second vkGetDeviceQueue, so that sharing is explicit rather than incidental. Settings: motion detail is a slider rather than three stops, and performance shaders is a switch rather than a pair of buttons.
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