mirror of
https://github.com/libretro/virtualjaguar-libretro.git
synced 2026-11-07 04:36:00 -10:00
fix(ocgrid): stop on INT/TERM, refuse a saturated stock cell as baseline
Two review findings, both in the measurement guards the script exists to enforce. The trap cleaned up but never stopped. On Ctrl-C the handler ran rm and the sweep carried on: the signal killed only the current cells frame_hash_ab, that cell was recorded FAILED, and six more multi-minute cells followed. TERM from timeout was ignored the same way. Split into an EXIT trap that cleans up and an INT/TERM trap that exits 130. A saturated stock cell still became the baseline. The doc is emphatic that SATURATED means unmeasured and arms must not be compared -- but any non-saturated cell would still print an authoritative-looking percentage against a ceiling-pinned number. Only a measured stock cell can set the baseline now; otherwise every row reports NO USABLE STOCK BASELINE. Super Burnout hit exactly this case in the same run that prompted the fix: 1799/1800 in all seven cells, stock included. Also records gate 1 complete across all eight candidates. One passes. Cybermorph -48.7% / +44.7% / +72.8% PASSES I-War -18.2% / +6.3% / +1.1% noise, capped at stock Missile Command 3D -0.2% / -14.3% / -11.2% overclock actively harmful Doom, Club Drive, Iron Soldier flat locks Super Burnout saturated at stock Val D Isere still needs a gameplay save The structural-null hypothesis holds as the general rule but not as a law: Cybermorph disproves the strong form. Doom reads 593/1800 = 19.8 fps in gameplay against real hardwares ~20 renders/s, which is a lead for #401 -- the 2x-too-fast figure came from the attract demo, a different code path. Refs #378 Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
1 parent
ac7a8e0e3f
commit
2b180cb6d4
2 files changed
+89
-58
No files matched your search
@@ -74,16 +74,28 @@ states measured above were older versions that loaded with only a size warning.
|
||||
|
||||
ROMs for all eight candidates are present in `jaguar-roms-private/ROMS/`.
|
||||
|
||||
### Measured 2026-08-25
|
||||
### Measured 2026-08-25 — all eight candidates
|
||||
|
||||
| Title | Savestate | Result |
|
||||
|---|---|---|
|
||||
| **Cybermorph** | `Cybermorph (1993).state1` | **CLEARS GATE 1** — +44.7% at RISC 1.5x, +72.8% at 2x |
|
||||
| Iron Soldier | `iron_soldier_v104_f2400.state` | Null — clock-insensitive, confirmed by underclock control |
|
||||
| Val D'Isere | `... (1994).state.auto` | **UNMEASURED** — near-static scene, needs a real gameplay state |
|
||||
Gate 1 is **complete**. One title passes.
|
||||
|
||||
**Cybermorph is the first title to clear gate 1.** All seven cells, one 2400-frame window
|
||||
(saturation ceiling 2399):
|
||||
| Title | control 0.5x | RISC 1.5x | RISC 2x | verdict |
|
||||
|---|---:|---:|---:|---|
|
||||
| **Cybermorph** | −48.7% | **+44.7%** | **+72.8%** | **PASSES** |
|
||||
| I-War | −18.2% | +6.3% | +1.1% | fails — noise, non-monotonic |
|
||||
| Missile Command 3D | −0.2% | **−14.3%** | −11.2% | fails — overclock makes it *worse* |
|
||||
| Doom | ±0.0% | ±0.0% | ±0.0% | flat lock, 593/1800 |
|
||||
| Club Drive | ±0.0% | ±0.0% | ±0.0% | flat lock, 1471/1800 |
|
||||
| Iron Soldier | −0.9% | ±0.0% | ±0.0% | flat lock, 116/900 |
|
||||
| Super Burnout | SATURATED | SATURATED | SATURATED | unmeasurable — already at the ceiling |
|
||||
| Val D'Isere | ±0.0% | ±0.0% | ±0.0% | **unmeasured** — static savestate |
|
||||
|
||||
**One pass in eight.** The structural-null hypothesis from #378 survives as the *general* rule —
|
||||
most Jaguar titles are paced by a field lock or their own frame cap, not by clock speed — but it
|
||||
is not universal, and Cybermorph is the counterexample that disproves the strong form of it.
|
||||
|
||||
#### Cybermorph — PASSES
|
||||
|
||||
All seven cells, one 2400-frame window (saturation ceiling 2399):
|
||||
|
||||
| cell | transitions | ≈ fps | vs stock |
|
||||
|---|---:|---:|---:|
|
||||
@@ -95,61 +107,69 @@ ROMs for all eight candidates are present in `jaguar-roms-private/ROMS/`.
|
||||
| RISC 1.5x, 68K 1.5x | 1994/2400 | ~50 | +46.7% |
|
||||
| RISC 2x, 68K 1.5x | 2311/2400 | ~58 | +70.1% |
|
||||
|
||||
The 68K axis is inert (+0.2% on its own): this is a RISC-bound title, so only
|
||||
`risc_clock_scale` matters, and stacking the 68K scale on top adds nothing.
|
||||
The 68K axis is inert (+0.2% on its own): RISC-bound title, so only `risc_clock_scale` matters,
|
||||
and stacking the 68K scale on top adds nothing.
|
||||
|
||||
**The control is what makes the rest trustworthy.** RISC 0.5x costs 48.7% — near-linear
|
||||
against the +44.7% that 1.5x gains — so the metric tracks clock speed in both directions on
|
||||
this title. Every figure above comes from the same 2400-frame window; nothing is spliced
|
||||
across window lengths.
|
||||
**The control is what makes the rest trustworthy.** RISC 0.5x costs 48.7% — near-linear against
|
||||
the +44.7% that 1.5x gains — so the metric tracks clock speed in both directions here. Every
|
||||
figure comes from the same 2400-frame window; nothing is spliced across window lengths.
|
||||
|
||||
At 2x, 2349 of 2400 fields carry a new frame — Cybermorph is now presenting at essentially
|
||||
the display rate. That is a real ceiling rather than the trap-2 counter artefact, but it does
|
||||
mean **+72.8% is a floor on the 2x gain, not the exact figure**; the engine may have headroom
|
||||
the display cannot show.
|
||||
At 2x, 2349 of 2400 fields carry a new frame: it is presenting at essentially the display rate.
|
||||
That is a real ceiling rather than the trap-2 counter artefact, but it does mean **+72.8% is a
|
||||
floor on the 2x gain, not the exact figure**.
|
||||
|
||||
> **Two blockers before this becomes a DB preset.** Cybermorph is *also* the title with the
|
||||
> open overclock crash report — #463, "Codex-level crash + erratic ship movement under
|
||||
> overclock", currently `blocked` for want of reporter artefacts. The one title measured to
|
||||
> benefit is the one with an unconfirmed overclock crash against it, so #463 has to be settled
|
||||
> first. And gate 2 still applies: a title can render more frames while its logic runs too
|
||||
> fast (#401). Play-test before writing a row.
|
||||
> **Two blockers before this becomes a DB preset.** Cybermorph is *also* the title with the open
|
||||
> overclock crash report — #463, "Codex-level crash + erratic ship movement under overclock",
|
||||
> `blocked` for want of reporter artefacts. The one title measured to benefit is the one with an
|
||||
> unconfirmed overclock crash against it, so #463 has to be settled first. And gate 2 still
|
||||
> applies: a title can render more frames while its logic runs too fast (#401).
|
||||
|
||||
**Iron Soldier is a genuine null, and the control proves it.** 0.5x reads 115 against 1x's
|
||||
116 — halving the RISC clock costs it *nothing*, so it was never clock-bound at 1x. That fits
|
||||
the profile data: Iron Soldier is the extreme DSP case, 67% of frame time in the DSP with
|
||||
99.7% of that spinning in an idle loop. It needs **idle-skip**, not overclock.
|
||||
#### Missile Command 3D — fails, and interestingly
|
||||
|
||||
> **Always run the underclock arm.** A flat 1x/1.5x/2x row is ambiguous — genuine cap, or dead
|
||||
> measurement? 0.5x separates them. Cybermorph's control (−48.7% at 0.5x, against +44.7% at
|
||||
> 1.5x) is what proves the metric was live for the whole sweep. `scripts/ocgrid.sh` runs this
|
||||
> arm unconditionally so it cannot be forgotten.
|
||||
The only title where overclocking is actively **harmful**: −14.3% at RISC 1.5x, −11.2% at 2x,
|
||||
while the 0.5x control is flat (−0.2%). Not compute-bound at stock, and giving the RISC more
|
||||
cycles *costs* presented frames. Worth a look on its own terms — a title that renders less when
|
||||
given more cycles suggests a timing-sensitive path, not merely an absent benefit.
|
||||
|
||||
**Val D'Isere is not a null — it is unmeasured.** 15 transitions in 900 frames, identical even
|
||||
at 0.5x. That is one changed frame every ~60, i.e. a static or paused screen; the state is a
|
||||
`.state.auto` autosave that landed wherever the session ended. Needs a real gameplay save
|
||||
(see below).
|
||||
#### I-War — fails, capped at stock
|
||||
|
||||
### Need a savestate — checklist
|
||||
Control drops cleanly (−18.2%), so the sweep is valid, but the gains are noise and
|
||||
non-monotonic: +6.3% at 1.5x, +1.1% at 2x, −3.7% at 2x with the 68K scale. Underclocking hurts
|
||||
while overclocking does nothing — the signature of a cap sitting at or just above stock.
|
||||
|
||||
- [ ] **Doom** — `Doom - Evil Unleashed (1994).jag`
|
||||
*Where:* in a level, walking down a corridor with at least one monster active.
|
||||
**Not** the attract demo (its pace is the #401 subject and it desyncs across cycle
|
||||
changes, so it is invalid evidence either way) and **not** the menu (its speed is the
|
||||
game's own code — see #396 notes).
|
||||
- [ ] **Club Drive** — `Club Drive (1994).jag`
|
||||
*Where:* mid-race, car moving, scenery scrolling.
|
||||
**Note:** this title used to abort at `risc_clock_scale=2x` (`dsp_pc_escape`, #565).
|
||||
That is **fixed** as of v3.5.0, so the 2x arm should now complete — if it dies, that is
|
||||
a regression worth its own issue.
|
||||
- [ ] **I-War** — `I-War (1995).jag`
|
||||
*Where:* in flight with geometry on screen. Earlier runs **saturated** here (600/600),
|
||||
so favour a calmer stretch and consider `FRAMES=1800`.
|
||||
- [ ] **Missile Command 3D** — `Missile Command 3D (1995).jag`
|
||||
*Where:* a wave in progress with incoming missiles.
|
||||
Earlier runs were corrupted by generic input (halved at 1.5x) — a savestate fixes that.
|
||||
- [ ] **Super Burnout** — `Super Burnout (1995).jag`
|
||||
*Where:* mid-race at speed.
|
||||
#### Doom, Club Drive, Iron Soldier — flat locks
|
||||
|
||||
Identical counts in all seven cells, control included. Iron Soldier reads 115 at 0.5x against
|
||||
116 at 1x, so halving the clock costs it nothing.
|
||||
|
||||
A flat control would normally void a sweep, and the script says so. It does not here, because
|
||||
the harness is demonstrably live on the same binary in the same session: Cybermorph, I-War and
|
||||
Missile Command 3D all responded. These are genuine caps, not dead measurements.
|
||||
|
||||
**Doom's number is worth recording for #401.** 593/1800 is one new frame per 3.03 fields,
|
||||
**≈19.8 fps** — and real hardware Doom level 1 is ~20 renders/s typical. In *gameplay* we may
|
||||
be far closer to hardware than the attract demo suggests, where #434 measured us at 2.00
|
||||
fields/flip against hardware's 4.0. One savestate is not a calibration, and the demo and
|
||||
gameplay are different code paths, so this is a lead rather than a result.
|
||||
|
||||
#### Super Burnout — unmeasurable, and the reason matters
|
||||
|
||||
1799/1800 in every cell: every frame differs, so the counter is pinned to the ceiling and a gain
|
||||
has nowhere to show. Lengthening the window does **not** help — a title that renders a new frame
|
||||
every field is saturated at any window length.
|
||||
|
||||
But the reason it saturates is itself the answer: **stock already presents at the display rate**,
|
||||
so there is no headroom for an overclock to deliver. Formally unmeasured by this metric;
|
||||
practically, there is nothing to win.
|
||||
|
||||
#### Val D'Isere — the one still outstanding
|
||||
|
||||
15 transitions in 900 frames, identical even at 0.5x — one changed frame every ~60, i.e. a
|
||||
static or paused screen. The state is a `.state.auto` autosave that landed wherever the session
|
||||
ended.
|
||||
|
||||
- [ ] **Val D'Isere** — `Val D'Isere Skiing & Snowboarding (1994).jag`. Needs a real gameplay
|
||||
save: mid-run down a slope, scenery moving, not the menu or a results screen.
|
||||
|
||||
### Already measured out — do not re-run
|
||||
|
||||
|
||||
+14
-3
@@ -30,7 +30,12 @@ STATE=$1; shift
|
||||
FRAMES=${FRAMES:-900}
|
||||
|
||||
TMP=$(mktemp -d)
|
||||
trap 'command rm -rf "$TMP"' EXIT INT TERM
|
||||
# EXIT does the cleanup; INT/TERM must additionally *stop*. Without the
|
||||
# second trap a Ctrl-C killed only the current cell's frame_hash_ab (the
|
||||
# process group gets the signal), that cell was recorded FAILED, and the
|
||||
# sweep carried on through six more multi-minute cells.
|
||||
trap 'command rm -rf "$TMP"' EXIT
|
||||
trap 'exit 130' INT TERM
|
||||
|
||||
emit() { printf '%s\n' "$*" | tee -a "$OUT"; }
|
||||
|
||||
@@ -64,7 +69,13 @@ for cell in $cells; do
|
||||
t=$(awk -F, 'NR>1{if(p!=""&&$6!=p)n++;p=$6}END{print n+0}' "$csv")
|
||||
n=$(awk 'NR>1' "$csv" | wc -l | tr -d ' ')
|
||||
printf '%s %s\n' "$t" "$n" > "$TMP/$tag.res"
|
||||
[ "$cell" = "risc=1x,m68k=1x" ] && base=$t
|
||||
# Only a MEASURED stock cell may become the baseline. A saturated one
|
||||
# is pinned to the ceiling, so quoting other arms against it would
|
||||
# print authoritative-looking percentages against a meaningless
|
||||
# number -- the exact trap this script exists to make un-missable.
|
||||
if [ "$cell" = "risc=1x,m68k=1x" ] && [ "$n" -gt 0 ] && [ "$t" -lt "$((n - 1))" ]; then
|
||||
base=$t
|
||||
fi
|
||||
done
|
||||
|
||||
# --- phase 2: report --------------------------------------------------------
|
||||
@@ -82,7 +93,7 @@ for cell in $cells; do
|
||||
elif [ "$t" -ge "$((n - 1))" ]; then
|
||||
v="SATURATED -- unmeasured, lengthen the window"
|
||||
elif [ -z "$base" ] || [ "$base" -eq 0 ]; then
|
||||
v="no usable stock baseline -- static scene, re-save the state"
|
||||
v="NO USABLE STOCK BASELINE -- stock cell saturated or static; nothing here is comparable"
|
||||
else
|
||||
v="$(awk -v a="$t" -v b="$base" 'BEGIN{printf "%+.1f%% vs stock", (a-b)*100.0/b}')"
|
||||
[ "$cell" = "risc=0.5x,m68k=1x" ] && v="$v <- CONTROL: must drop, or the sweep is void"
|
||||
|
||||
Reference in new issue
Block a user