docs: record the first correct in-race frame rate

Measured on both hosts, with the method and the reason a median is the wrong
statistic for the Pixel 6a (bimodal 60/30 - the median sits in the upper lobe
and reports 59.8 where the mean is 39.5).

Also retires the old "51 fps" from task #61: it was not measured in a race
and is not comparable. That is the second time a figure from elsewhere in the
game was nearly quoted as in-race performance, so it is written down plainly.

The consequence is a priority change - the 6a misses the frame budget by a few
percent, so #63 and #64 are worth more than their profile shares suggest, and
the hot path has very little margin left to spend on new work.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-09-23 00:50:44 +03:00
co-authored by Claude
parent 576496e877
commit 5a6b2c1540
+56
View File
@@ -3635,3 +3635,59 @@ One real caveat: the flat guest mapping (task #61, the 1.24x win) exists only
in `tcg/aarch64`. An x86_64 host silently falls back to the software MMU. That
is recorded in a comment next to the `abiFilters` line so the next person does
not read x86 numbers as if the same optimisation were in play.
## 2026-09-22 - the first correct in-race frame rate, on two hosts
Until now no in-race frame rate had ever been measured properly; the log ring
kept wrapping before a race finished. Every speed figure in this document
before this entry was taken somewhere else in the game. In particular the
"51 fps" quoted for the flat-mapping A/B (task #61) was **not** measured in a
race and must not be compared against anything below. This repeats an error
already made once here, when a "60 fps" reading turned out to be the results
screen.
### Method
`dumpsys SurfaceFlinger --latency` on the game's own BLAST layer, six
back-to-back samples, deduplicated by actual-present timestamp, then look at
the **distribution** of intervals rather than an average. A screenshot is
taken with every capture - without it there is no way to know a menu was not
measured instead of a race, which is exactly how the old numbers went wrong.
### Pixel 6a, in race (60 Hz, 16.67 ms), 140 frames
median 16.72 ms (59.8 fps) <- misleading, do not quote
mean 25.32 ms (39.5 fps) <- the honest figure
p95 33.38 ms, worst 66.56 ms
1 vsync (60 fps) 74 frames
2 vsync (30 fps) 61 frames
3 vsync (20 fps) 3 frames
4 vsync (15 fps) 2 frames
The distribution is bimodal. The phone very nearly makes 60 and misses on
roughly half the frames, dropping exactly to 30. The median sat inside the
upper lobe and reported 59.8, which is why a median is the wrong statistic
here and the mean is quoted instead.
### WayDroid x86_64, in race (144 Hz, 6.944 ms), 128 frames
median 34.72 ms (28.8 fps)
mean 35.05 ms (28.5 fps)
p95 41.68 ms, worst 48.61 ms
Every frame lands on exactly five vsync periods. Slower than the phone and
visibly smoother, because steady beats fast-but-uneven - the user described it
as "очень плавно" before any of this was measured, and the numbers agree.
GLThread sits at 96.7% of one core out of sixteen: CPU-bound in translation,
not GPU-bound.
### Why this changes the priorities
The 6a misses the 16.67 ms budget by a few percent. So a 10-15% CPU win does
not buy "a few more fps" - it flips those 61 two-vsync frames back to one, and
the phone lands on a stable 60. That makes task #63 (VFP on host FP instead of
softfloat, ~9% of profile) and task #64 (`helper_lookup_tb_ptr`, 12.18%) worth
far more than their profile shares suggest, and it means anything new added to
the hot path - the multiplayer hooks included - is spending a margin that is
currently only a few percent wide.