Skip to content

android: run PS4 and PS5 titles on device - #14

Open
Force67 wants to merge 3 commits into
masterfrom
android-support
Open

android: run PS4 and PS5 titles on device#14
Force67 wants to merge 3 commits into
masterfrom
android-support

Conversation

@Force67

@Force67 Force67 commented Jul 27, 2026

Copy link
Copy Markdown
Owner

PS4 and PS5 titles now run on the phone through the NativeActivity app, at the
same point the host is at.

The load-bearing fix is that the Android build had no shader recompiler at all.
delta/gpu enables the direct SPIR-V backend only when pkg_check_modules
finds SPIRV-Tools and SPIRV-Headers, and neither resolves against the NDK
sysroot, so DELTA_HAVE_SPIRV_BACKEND was quietly undefined and Recompile
was compiled out. Every PS5 draw was dropped ([agc] recompile FAILED ... draw dropped) and the frame was black. Building SPIRV-Tools with the NDK and
pointing PKG_CONFIG_LIBDIR at it turns the backend on; the CMake change here
is needed because delta_gpu is a static library, so only the library list
propagates to the final link and pkg-config yields bare -l names when cross
compiling. Two smaller build breaks came with it: memfd_create is only
declared by bionic at API 30 while we target 29, and both Android gfx backends
were missing setTitle/overlaySetPerf.

Presentation was a stretch to fill the surface, which made 16:9 content about
20% too wide on a 21:9 panel. Fitting it is not as simple as it looks: the
swapchain image is portrait while the window is landscape and the compositor
scales each axis independently instead of rotating, so the aspect fit is solved
in landscape screen space and the rect mapped back into image space for the
blit. Touch input and the overlay go through that same rect.

The touch controls were four hard-edged discs alpha-blended at a flat 0.40,
chosen around what Isaac needed. They are now signed distance fields resolved
with a pixel of coverage, laid out in height units so the pad keeps its
proportions on any panel, and cover twin sticks, a d-pad, the four face
buttons, shoulders, touchpad and options.

PS5 titles could not boot on Android at all: an adb-started Activity inherits
no environment, so DELTA_PS5_MODULES (and every other DELTA_* knob) was
unreachable. android_main reads <dataDir>/env.cfg before anything else and
defaults the PS5 module path to the staged firmware. The launcher only browsed
for .pkg, so it now accepts .ffpkg and extracted game folders as well.
DELTA_ANDROID_LOGFILE tees the log to a file, which is what made the
remaining failures diagnosable: logd drops bursts from a chatty uid and
anything still in the pipe is lost when the process dies, which is exactly when
a crash dump is written.

Verified on a OnePlus 15 (SM8850, Android 16): PS4 Isaac in gameplay at ~265
fps, PS4 Undertale at 30 fps and interactive to the name-entry screen, PS5
Isaac Repentance in gameplay at 210-350 fps, all with zero unsupported opcodes
and driven entirely through the on-screen pad. Both Android configurations
(DELTA_ANDROID_APP on and off) build, and each commit builds on its own. The
host build is unaffected: it still resolves SPIRV-Tools to absolute paths and
keeps the backend enabled.

Dead Cells still does not get past sceSysmoduleLoadModule for libc, but it
stops at the same place on the host, so it is not an Android regression. The
difference in symptom is that the SDK assert is stepped over by the
int 0x41/0x44/0x45 handler in crash.cpp, which is #if defined(__x86_64__)
and has no FEX equivalent, so the guest ud2 is fatal on ARM rather than
merely hanging.

Vincent Hengel added 3 commits July 27, 2026 22:01
The recompiler was compiled out of every Android build: delta/gpu gates it on
pkg_check_modules finding SPIRV-Tools, which never resolves against the NDK
sysroot, so DELTA_HAVE_SPIRV_BACKEND was undefined and every guest draw was
dropped. Point PKG_CONFIG_LIBDIR at an NDK-built SPIRV-Tools to enable it;
delta_gpu is a static library so only the library list propagates to the final
link, and pkg-config hands back bare -l names when cross compiling, so resolve
each entry to an absolute path.

memfd_create is only declared by bionic at API 30 and we target 29, and the
Android gfx backends were missing setTitle/overlaySetPerf.
The present blit stretched the framebuffer over the whole surface, so 16:9
content came out ~20% too wide on a 21:9 panel. The fit has to be solved in the
landscape space the player sees and mapped back into image space for the blit:
the swapchain image is portrait while the window is landscape, and the
compositor scales each axis on its own rather than rotating.

The controls were four hard-edged discs alpha-blended at a flat 0.40. They are
now signed distance fields resolved with a pixel of coverage, and cover the pad
the games actually need (twin sticks, d-pad, face buttons, shoulders, touchpad,
options) instead of the four Isaac used. Layout is in height units so it keeps
its proportions on any panel.
An adb-started Activity inherits no environment, so the DELTA_* knobs -- the
emulator's whole debug interface -- were unreachable on Android, and with them
DELTA_PS5_MODULES, without which no PS5 title can resolve a system module.
android_main now reads <dataDir>/env.cfg and defaults DELTA_PS5_MODULES to the
staged firmware. The launcher only browsed for .pkg, so PS5 titles could not be
picked at all; it now takes .ffpkg and extracted game folders too.

DELTA_ANDROID_LOGFILE tees the log pump to a file. logd drops bursts from a
chatty uid and anything still in the pipe dies with the process, which is
exactly when a crash dump is written.

The activity was inset by the display cutout rather than using the whole panel.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant