Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/release-notes/v2.7.5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# RustyNES v2.7.5 — "Tally"

The sixth and last release of the audit line that [ADR 0041](https://github.com/doublegate/RustyNES/blob/main/docs/adr/0041-hardware-release-is-v3.0.0.md) put between v2.6.23 and the SuperStation One core (**v3.0.0**). It closes the v2.7.x half of that line: every row of the [core ledger](https://github.com/doublegate/RustyNES/blob/main/docs/audits/core-disposition.md) and the [frontend ledger](https://github.com/doublegate/RustyNES/blob/main/docs/audits/frontend-disposition.md) now carries a verdict with its evidence. The libretro and RTL audits are v2.8.x.

Its subject is the claims the earlier releases left for last: the core audit's performance proposals, the interfaces it called dead, and the documentation it called stale. Each was measured or checked before anything changed, and most of the performance work is a record of what did **not** help. **No emulation behaviour changes**; **AccuracyCoin 144/144** and **nestest** were re-measured on the release tree anyway, and the full `--features test-roms` workspace suite passes **2,721 tests, 0 failed (v2.7.4: 2,716)**.

**The MiSTer sibling does not change in this release, and no new bitstream is cut.** No hardware has run any bitstream; the board session is v2.9.2.

## Twelve performance proposals: one adopted, eleven rejected

The core audit proposed twelve hot-path changes: caching the CPU clock divider, removing a per-cycle store, dropping a per-dot interrupt-edge sample, branchless status flags, CPU inlining, redundant PPU stores, a branchless palette lookup, audio-buffer capacity, sweep-mute caching, skipping a per-read virtual call, and two more. This project's record was already against micro-optimising this core (a v2.3.1 campaign measured ten such items and adopted none), so v2.7.5 measured before building.

The first measurement applied **most of the proposals at their maximum at once**, deleting the work outright where the real change would have been subtle and knowingly breaking correctness, to bound what they could win. On two clean runs that bound was **zero**: within the benchmark's own order-bias control, with no consistent sign. If deleting the work buys nothing, no correct version of it can. A bound on a set could still hide one change that helps behind another that hurts, so the one rewrite in the set (branchless status flags) was also run alone, twice: zero. CPU inlining and cold-path outlining, whose premise was true (the helpers really were out of line), got its own runs: zero. So did the branchless palette mirror, on a path taken 61,440 times a frame: zero. Of the last two, one (hoisting a per-dot adapter) was measured and rejected in v2.3.1, and the other (folding a `match` in the PPU's fast path) has no ceiling to measure and was not pursued, because removing work from that same function measured zero.

**One is adopted: the audio buffer keeps its capacity between frames.** Handing a frame's audio to the app used to leave an empty buffer that the next frame regrew from nothing, about nine reallocations a frame. It now starts at the size the last frame needed. On a benchmark that runs a frame and takes its audio, over and over, it measured **-0.89% frame time on two separate runs**, with the reference re-run on either side as a control. It helps the apps that take audio a frame at a time, the mobile apps and the web build; the desktop was already allocation-free here. The audio itself is byte-identical. It was nearly rejected unmeasured: the first bound credited it with a zero, but the standard benchmarks never take audio at all, so they could not see it. Review on this release's pull request prompted the check that found that.

The audit also proposed capping the buffered audio samples. That one is rejected on behaviour, not speed: every host drains audio each frame, so the cap would bound nothing real, and the one consumer that does accumulate would silently lose audio.

The whole record, with every number and the audit premises that turned out to be wrong, is in [`docs/performance.md`](https://github.com/doublegate/RustyNES/blob/main/docs/performance.md). The core's frame cost is still the work the accuracy model requires.

## Interfaces marked for removal

The audit listed 22 methods of the CPU's bus trait as never called. Text search cannot check that, because the same names are also ordinary methods on the bus and on every mapper. So the compiler checked it instead: every trait method was temporarily marked deprecated, and every build CI runs reported the real call sites. **Eighteen are dead. Five of the audit's 22 are live**, and of two it did not list, one is dead and one is called under a trace feature. The eighteen, and the `ApuBus` trait that nothing implements, are now `#[deprecated]`, so a new call to any of them fails the lint. Whether v3.0.0 removes them is decided at v2.9.0.

## Fixes

- **An HD pack no longer loses capacity to a corrupt image.** Each image reserves its decoded size from the pack's budget as soon as its header is read. A PNG whose pixel data then failed to decode kept that reservation, so a valid image later in the pack could be refused as over budget. The reservation is now returned.
- **The FDS error says what to do.** Opening a Famicom Disk System image where only cartridges load, as in the mobile apps, reported that FDS support was "planned for v2.2.0", although it shipped in v2.2.0. The message now says the image needs the disk loader and a BIOS.
- **The architecture documentation describes the scheduler the emulator actually has.** The project's agent guide and `docs/scheduler.md` still described the dot-lockstep design that v2.0.0 retired, a bus type that does not exist, and an APU bus trait that nothing implements.

## Verification

| Check | Result |
| --- | --- |
| AccuracyCoin (RAM decoder) | 144 / 144, 0 fail, 0 not run |
| nestest | pass |
| `--features test-roms` workspace suite | 154 suites, 2,721 passed, 0 failed |
| fmt, clippy (default, `scripting,hd-pack`, `retroachievements`, `full`, both wasm32), `cargo doc -D warnings`, `no_std` build | clean |
| Performance | `scripts/perf/ab_check.sh`, 25 s per sample set, four shipped workloads, each with an A/B/A order-bias control, and a frame-then-drain A/B/A for the audio buffer; every verdict rests on two runs in which the candidate is read against its own control, and runs whose control swung by several percent under background load are reported as void, not discarded |
| Deprecations | the workspace and every CI feature set build with `-D warnings`, which fails on any call to a deprecated item |
| Red first | the HD-pack test failed on v2.7.4 (0 of 16,384 bytes left after the failed decode), and the audio-buffer test failed before its change (capacity 0 against 717 samples); both mutations are caught |

**Deferred:** removing the per-frame copy of the picture through the mobile bridge. The fix needs the bridge generator (UniFFI) to support writing into a buffer the app owns. That exists only in its unreleased development branch; the newest release, 0.32.2, supports read-only buffers only.

**Next:** v2.8.0 begins the libretro and RTL audits.
Loading
Loading