Skate 3 Android v0.1.14
This release has no download. Its APK was removed during an upload that was
interrupted, and the exact binary was not kept. Use
v0.1.15
instead - it contains everything below plus the image and build-identity checks.
Skate 3 running as native ARM64 code on Android. This is not an emulator: the
Xbox 360 executable was translated ahead of time into C++ and compiled for the
phone, so the skating, physics and career mode are the retail game's own code
running natively. During gameplay the Xbox GPU is not emulated either, a native
renderer reads the game's scene state and draws it through Vulkan directly.
No game content is included and none is bundled in the APK. You supply your
own Skate 3 Xbox 360 disc image.
What you need
- An arm64 Android phone, Android 9 or newer, with Vulkan. Built and measured on
a Galaxy S23 FE. - Your own Skate 3 Xbox 360 disc image, on the phone or a USB drive.
- About 7 GB free. Roughly 6 GB is extracted from the image and stays on the phone.
Setting it up
- Install the APK and open Skate 3 Android.
- Choose Install from a disc image and pick your image. It is read where it
lies and extracted, which takes a while. - Choose Download title update. The game cannot boot without Title Update 3
and it is not shipped here. You can also pick your own copy of the package
instead. Either way the engine verifies both patch payloads by hash and
refuses anything that does not match. - Press Play.
New in this release
This one is mostly about where the time actually goes, and it began by proving
the previous assumption wrong.
The render thread was spinning, not waiting. The plan said the hitches when
new map areas stream in were disc reads or lock contention. A scheduler trace
said otherwise: the two file threads slept for 31 and 48 seconds of a 50 second
window, and the render thread never blocked for longer than 13 ms. Inside an
80 ms hitch it was running for 73 of those milliseconds. It was spinning
inside the game's own frame-end call, waiting for the emulated command
processor - which the same trace caught runnable with no core for 7.8 of those
50 seconds.
Four changes give that thread its core back, each verified in a profile:
- The frame cap spun for 2 ms at the end of every wait, which was 14.4% of the
render thread, an eighth of every frame, held on a performance core doing
nothing. It sleeps to the deadline now and spins 300 us. Measured after: 1.6%.
Tunable asskate3_guest_fps_cap_spin_usif a device wakes threads slowly. - The command processor's own idle poll yielded 500 times before blocking, worth
another 14.4% of that thread. Low-RAM devices already dropped it to 32 for
exactly this reason; every Android device does now. Having more busy threads
than fast cores is not a low-memory property. - The draw hook matched shader names with
strstrin five separate places, each
reading 119 bytes of guest memory a byte at a time first, and none of the
memos survived a full session. That was 4.9% of the render thread and 14%
inside the hitches, its single largest entry. Three of those probes look for
water and keep searching until they find some, so across most of the map,
where there is none, they never stopped. - Two smaller ones on the command processor: a register-table lookup made only
to decide whether to print a message the log level discards, and a per-packet
call into the trace writer that exists to return immediately when no trace is
open.
Being honest about the result: together these free roughly a fifth of the
render thread, and they do not remove the streaming hitch. After them the
hitch has no hot spot left - just dozens of guest functions each running once at
50 to 140 times their normal rate, which is the game setting up a zone's
objects. That is real work; the Xbox 360 hitched there too. Steady state is
unaffected either way: 95% of frames land on time.
The AYN Thor crash is found. Testers on that device described a hang. It is
not a hang - it is a crash, four to seven minutes in, every time, and the
Android tombstones in a recent report are five copies of the same one. An audio
buffer setup calls memset(buffer, 0, size) and that size arrives as -12.
PowerPC memset reads the count as unsigned and computes a 16-byte block count of
size >> 4; the crash dump's r0 is 0x0FFFFFFF, which is exactly
0xFFFFFFF4 >> 4. It then writes through 4.29 GB of address space until it
reaches memory that is reserved but not committed, and dies there. The
0x70000000 address in every Thor report going back months was never
meaningful - it is simply how far the memset got, and every theory built on it
was chasing a number with no content. A negative byte count is now clamped and
the caller logged.
Reports say more. A thirty-second heartbeat logs at warning level, so a
normal log can finally distinguish a frozen game from one that is running but
not drawing the world - the Thor's living world was updating sixty times a
second while a tester watched a still screen, and nothing in the log could say
so. The audio trap dumps the registered codec formats on both failing paths, and
the diagnostic report now carries sizes and hashes of your disc files, the
locale and the engine's startup arguments.
Tuning
The engine reads files/user/android_args.txt at startup, one setting per line.
The android_args/ folder in the repository has profiles to start from.
streaming.txt is the one for hitch work; v0114-ab-old.txt reverts this
release's timing changes so a single build can be measured both ways.
skate3_diagnostics is worth leaving off for normal play. Its per-frame
accounting runs whether or not a line is printed, so anything measured with it
on is a floor rather than what the game actually does.