port: the frame rate is llvmpipe, not our draw path -- and my finding-4 candidate is downgraded

Last iteration left "is 9.7 fps llvmpipe or something in our draw path" open
after publishing a candidate cause off it. That is not a question to leave open,
so this iteration is the control.

Pre-registered: if the rasteriser is the limit, a near-empty scene in the same
container also runs at 10-25 fps; if our draw path is, it runs far faster, take
>100 fps as the discriminator.

  empty  161.6 fps

The engine loop, viewport and present path are not the limit, by 16x over the
menu.

🔴 AND MY FIRST CONTROL WAS NOT A CONTROL. Its fill modes drew UNTEXTURED rects
while every element the port draws is a texture, so it bounded nothing -- and it
showed: the splashes were achieving ~21 Mpx/s against that control's ~50, which
read as the port being mysteriously slow and was the control being mysteriously
fast. With a matched textured mode:

  empty 161.6 | fill3 31.5 | tex3 23.9 | fill7 12.7 | tex7 11.2

and every port screen lands inside the bracket, ordered by large-quad count:
publisher (1 quad) 17.3-25.0 ~ tex3; developer (1) 16.7-22.8 ~ tex3; title (6)
12.7-17.2 between; main_menu (5) 9.7 ~ tex7's 11.2. The port draws large
textured alpha quads on a software rasteriser at exactly the rate a software
rasteriser draws them.

⚠️ The control's own spread is 1.75x between runs (fill3 measured 18.0 once and
31.5 once), so the absolute figures are noise-dominated and only the ORDERING and
the BRACKET are load-invariant. That is what the conclusion rests on --
TEMPORAL-VERIFICATION §3, prefer the quantities with no phase.

🔴 SO MY OWN CANDIDATE FOR FINDING 4 IS DOWNGRADED, AND THIS SAYS SO PLAINLY.
Last iteration called the frame rate "the first candidate for finding 4 that is
not already dead". The quantisation argument still holds -- a fade drawn in four
steps is not a fade drawn in fifteen -- but it only bites at these rates, and
these rates are a property of software rasterisation in this container. Five to
seven full-screen quads at 720p is nothing to a GPU. Unless the human ran the
port software-rendered, this is not what they saw, and EVERY candidate for
finding 4 is now dead or near-dead.

Written as a dead end rather than left standing as a lead. Leaving a
plausible-sounding cause up after its own control has undercut it is exactly how
"close but not right" got explained four different wrong ways. The one thing that
would revive it costs the human nothing: the rate line prints on every boot now,
so their next run answers it.

What the port does NOT need, established rather than assumed: no draw-path
optimisation (161.6 fps empty, screens on the fill curve where their quad counts
put them), no texture caching (already at load_screen), no max_fps or vsync
change (would not raise the rate here, and is a presentation decision not a
defect).

Not settled: what rate the human's machine manages -- now the only open half of a
candidate I have otherwise talked myself out of; H6's +0x04 exposure; H1 (with
the Decoder); the four red verify-screen rows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018AHUQvXGyNcKonSEWsgWcX
This commit is contained in:
Sylpheed port agent
2026-09-01 18:23:25 +00:00
parent 170d255e82
commit 4be90c2805
2 changed files with 103 additions and 5 deletions

View File

@@ -123,8 +123,16 @@ hitches**, which draws the splash's 45-unit build-in at **1217 distinct alpha
instead of 45** and the companion glow's 15-unit rise at **four to six instead
of fifteen**.
⚠️ That is `llvmpipe` under Xvfb and **not a measurement of the human's
hardware**, so it is a candidate and not a cause. The point is that the line
🔴 **DOWNGRADED the next iteration by a matched control, and the downgrade is
mine.** An empty scene in the same container runs at **161.6 fps**, and matched
textured controls give **23.9 fps for three full-screen quads and 11.2 for
seven** — every port screen lands inside that bracket in the order its quad count
predicts (`main_menu`'s 9.7 against `tex7`'s 11.2). So the rate is software
rasterisation of large textured alpha quads and **the port's draw path has no
case to answer**; on any GPU the fade gets its full 45 steps. Unless the human
ran it software-rendered this is not what they saw, which means **every candidate
for finding 4 is now dead or near-dead**. Recorded as a dead end rather than left
standing as a lead. The point is that the line
now prints on every boot, so **the next play-test answers it for free**. See
[`port-frame-rate.md`](port-frame-rate.md).

View File

@@ -1,8 +1,8 @@
# The port never reported its own frame rate — it does now, and it is 1325 fps here
**Status:****instrument added and measured.** 🟡 **The consequence is a live
candidate for play-test finding 4 and it is environment-dependent, so it is
recorded as a candidate and not as a cause.** Written 2026-09-01 by the Port at
**Status:****instrument added and measured.** 🔴 **The candidate it raised for
play-test finding 4 is now DOWNGRADED to probably-dead by a matched control —
see the last section. The port's draw path has no case to answer.** Written 2026-09-01 by the Port at
`977965e`; HANDOFF on this branch answers `9ca1eb5`.
## The gap this closes
@@ -132,3 +132,93 @@ finding over.
and not decoded per frame — that was checked and is not the cause.
* That capping or vsyncing would help. It would not raise the rate here, and
changing presentation behaviour on my own authority is not mine to do.
---
# The control: it is the software rasteriser, and the port has no case to answer
Written the iteration after the section above, because *"is 9.7 fps llvmpipe or
something in our draw path"* was left open and it is not a question to leave open
after publishing a candidate cause.
## Pre-registered (R2)
> If the rasteriser is the limit, a near-empty Godot scene in this same container
> will also run at roughly 1025 fps. If my draw path is the limit, it will run
> far faster — take **>100 fps** as the discriminator.
Same container, same Xvfb, same 1280×720 viewport, same `[rendering]` settings,
almost nothing drawn:
```
FPSPROBE mode=empty: 651 frames in 4.03 s -- 161.6 fps, worst gap 54 ms
```
**161.6 fps.** The engine loop, the viewport and the present path are not the
limit, by a factor of sixteen over the menu.
## 🔴 And the first control was not a control
Its `fill` modes drew **untextured** `draw_rect`s while every element the port
draws is a **texture**. A control that does not do what its subject does bounds
nothing — and it showed: the port's splashes were achieving ~21 Mpx/s against
that control's ~50, which read as the port being mysteriously slow and was really
the control being mysteriously fast. Adding a matched textured mode:
| mode | full-screen quads | achieved |
|---|---|---|
| `empty` | 0 | **161.6 fps** |
| `fill3` | 3, untextured | 31.5 |
| **`tex3`** | **3, textured** | **23.9** |
| `fill7` | 7, untextured | 12.7 |
| **`tex7`** | **7, textured** | **11.2** |
## The port sits inside the bracket, ordered by large-quad count
| screen | full-screen-ish quads | measured | bracket |
|---|---|---|---|
| `publisher_logo` | 1 | 17.325.0 | ≈ `tex3` |
| `developer_logos` | 1 | 16.722.8 | ≈ `tex3` |
| `title` | 6 | 12.717.2 | between `tex3` and `tex7` |
| **`main_menu`** | **5** | **9.7** | ≈ `tex7` (11.2) |
**Every screen lands between the two matched controls, in the order the quad
count predicts.** The port is drawing large textured alpha quads on a software
rasteriser at exactly the rate a software rasteriser draws large textured alpha
quads.
⚠️ Note the control's own spread: `fill3` measured **18.0** on one run and
**31.5** on another. The container's load swings by ~1.75×, so the absolute
numbers here are noise-dominated and only the **ordering and the bracket** are
load-invariant — which is what `TEMPORAL-VERIFICATION.md` §3 says to prefer, and
the reason this conclusion rests on those rather than on any single figure.
## 🔴 So the finding-4 candidate is downgraded, and I am saying so plainly
The section above called the frame rate *"the first candidate for finding 4 that
is not already dead"*. **That now looks wrong, and it was mine.**
The quantisation argument still holds — a fade drawn in four steps is not a fade
drawn in fifteen — but it only bites at these frame rates, and these frame rates
are a property of **software rasterisation in this container**. Five to seven
full-screen quads at 720p is nothing to any GPU; on real hardware the port would
hit vsync and the fade would get its full 45 steps.
So unless the human ran the port software-rendered, this is not what they saw,
and **every candidate for play-test finding 4 is now dead or near-dead.** That is
an honest dead end rather than a lead, and it is written as one: leaving a
plausible-sounding cause standing when its own control has undercut it is exactly
how *"close but not right"* got explained four different wrong ways.
**The one thing that would revive it costs the human nothing**: the rate line now
prints on every boot. If their next run says 60 fps, this is finished.
## What the port does NOT need
* No draw-path optimisation. 161.6 fps empty says the loop is fine, and the
screens sit on the fill curve exactly where their quad counts put them.
* No texture caching work — already done at `load_screen`, checked last
iteration.
* No `max_fps` or vsync change. It would not raise the rate here, and it is a
presentation decision rather than a defect.