diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c541da0..d7c87d02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.19.0] "Afterburner" - 2026-07-15 + +Fifteenth release of the RustyNES-parity roadmap: an optional PGO/BOLT pipeline for the +shipping `rustysnes` binary, deliberately last per the plan (after mobile-specific hot-path +work landed, so the profile isn't invalidated). + ### Added - **PGO/BOLT pipeline** (Mobile-track-adjacent, deliberately last per the RustyNES-parity @@ -37,6 +43,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 documented honestly in `docs/performance.md` — this is an expected, not a failure, state; a short/narrow local run isn't representative of CI's real `3600`-frame training sweep). +### Fixed + +- **A real bug in `pgo.yml`'s BOLT stage, found in PR review**: re-invoking the whole + `scripts/pgo/run.sh` between `cargo pgo bolt build` and `cargo pgo bolt optimize` ran a + separate, non-BOLT PGO cycle that never fed BOLT's profile data and could clobber the + bolt-instrumented binary with an unrelated plain-PGO one. Fixed per `cargo-pgo`'s own + documented BOLT+PGO combined workflow: `--with-pgo` on both `cargo pgo bolt build` and + `cargo pgo bolt optimize`, with the erroneous `run.sh` re-invocation removed. Real BOLT profile + gathering (running the actual GUI frontend binary against a workload) stays out of scope — + this project's frontend has no headless CLI mode — so the fix deliberately uses `cargo-pgo`'s + own documented profile-less BOLT fallback instead. + ## [1.18.0] "Dormant" - 2026-07-14 Fourteenth release of the RustyNES-parity roadmap: Mobile Phase 5, monetization scaffolding. diff --git a/Cargo.lock b/Cargo.lock index 0b07c1ec..ab340f7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3703,7 +3703,7 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rustysnes-android" -version = "1.18.0" +version = "1.19.0" dependencies = [ "android_logger", "bytemuck", @@ -3719,7 +3719,7 @@ dependencies = [ [[package]] name = "rustysnes-apu" -version = "1.18.0" +version = "1.19.0" dependencies = [ "bitflags 2.13.0", "rustysnes-savestate", @@ -3727,7 +3727,7 @@ dependencies = [ [[package]] name = "rustysnes-cart" -version = "1.18.0" +version = "1.19.0" dependencies = [ "bitflags 2.13.0", "rustysnes-savestate", @@ -3736,7 +3736,7 @@ dependencies = [ [[package]] name = "rustysnes-cheevos" -version = "1.18.0" +version = "1.19.0" dependencies = [ "cc", "ureq", @@ -3744,7 +3744,7 @@ dependencies = [ [[package]] name = "rustysnes-core" -version = "1.18.0" +version = "1.19.0" dependencies = [ "criterion", "rustysnes-apu", @@ -3759,7 +3759,7 @@ dependencies = [ [[package]] name = "rustysnes-cpu" -version = "1.18.0" +version = "1.19.0" dependencies = [ "bitflags 2.13.0", "rustysnes-savestate", @@ -3767,7 +3767,7 @@ dependencies = [ [[package]] name = "rustysnes-frontend" -version = "1.18.0" +version = "1.19.0" dependencies = [ "anstyle", "bytemuck", @@ -3807,11 +3807,11 @@ dependencies = [ [[package]] name = "rustysnes-gfx-shaders" -version = "1.18.0" +version = "1.19.0" [[package]] name = "rustysnes-ios" -version = "1.18.0" +version = "1.19.0" dependencies = [ "bytemuck", "log", @@ -3824,7 +3824,7 @@ dependencies = [ [[package]] name = "rustysnes-libretro" -version = "1.18.0" +version = "1.19.0" dependencies = [ "libc", "rust-libretro", @@ -3833,7 +3833,7 @@ dependencies = [ [[package]] name = "rustysnes-mobile" -version = "1.18.0" +version = "1.19.0" dependencies = [ "rustysnes-cart", "rustysnes-core", @@ -3843,14 +3843,14 @@ dependencies = [ [[package]] name = "rustysnes-monetization" -version = "1.18.0" +version = "1.19.0" dependencies = [ "uniffi", ] [[package]] name = "rustysnes-netplay" -version = "1.18.0" +version = "1.19.0" dependencies = [ "js-sys", "rustysnes-core", @@ -3862,7 +3862,7 @@ dependencies = [ [[package]] name = "rustysnes-ppu" -version = "1.18.0" +version = "1.19.0" dependencies = [ "bitflags 2.13.0", "rustysnes-cart", @@ -3872,14 +3872,14 @@ dependencies = [ [[package]] name = "rustysnes-savestate" -version = "1.18.0" +version = "1.19.0" dependencies = [ "thiserror 2.0.18", ] [[package]] name = "rustysnes-script" -version = "1.18.0" +version = "1.19.0" dependencies = [ "mlua", "rustysnes-core", @@ -3888,7 +3888,7 @@ dependencies = [ [[package]] name = "rustysnes-test-harness" -version = "1.18.0" +version = "1.19.0" dependencies = [ "insta", "rustysnes-apu", diff --git a/Cargo.toml b/Cargo.toml index 642f6c13..284082fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ members = [ ] [workspace.package] -version = "1.18.0" +version = "1.19.0" edition = "2024" rust-version = "1.96" license = "MIT OR Apache-2.0" diff --git a/crates/rustysnes-android/Cargo.toml b/crates/rustysnes-android/Cargo.toml index a734f113..6d445805 100644 --- a/crates/rustysnes-android/Cargo.toml +++ b/crates/rustysnes-android/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustysnes-android" description = "RustySNES: Android JNI presentation host (wgpu-on-Surface, no emulation logic)" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-apu/Cargo.toml b/crates/rustysnes-apu/Cargo.toml index 0d80bbad..3abe979c 100644 --- a/crates/rustysnes-apu/Cargo.toml +++ b/crates/rustysnes-apu/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustysnes-apu" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-cart/Cargo.toml b/crates/rustysnes-cart/Cargo.toml index ea2aed1b..2fb9a908 100644 --- a/crates/rustysnes-cart/Cargo.toml +++ b/crates/rustysnes-cart/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustysnes-cart" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-cheevos/Cargo.toml b/crates/rustysnes-cheevos/Cargo.toml index a7e8b031..11d3d728 100644 --- a/crates/rustysnes-cheevos/Cargo.toml +++ b/crates/rustysnes-cheevos/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustysnes-cheevos" description = "Native-only RetroAchievements (rcheevos) FFI wrapper for RustySNES" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-core/Cargo.toml b/crates/rustysnes-core/Cargo.toml index e7685159..85a5dba4 100644 --- a/crates/rustysnes-core/Cargo.toml +++ b/crates/rustysnes-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustysnes-core" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-cpu/Cargo.toml b/crates/rustysnes-cpu/Cargo.toml index 2d578477..89329b02 100644 --- a/crates/rustysnes-cpu/Cargo.toml +++ b/crates/rustysnes-cpu/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustysnes-cpu" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-frontend/Cargo.toml b/crates/rustysnes-frontend/Cargo.toml index 81dda14c..3d09fef3 100644 --- a/crates/rustysnes-frontend/Cargo.toml +++ b/crates/rustysnes-frontend/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustysnes-frontend" description = "RustySNES: cross-platform SNES emulator binary (winit + wgpu + cpal + egui)" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-gfx-shaders/Cargo.toml b/crates/rustysnes-gfx-shaders/Cargo.toml index bc4d1bb3..e2a53415 100644 --- a/crates/rustysnes-gfx-shaders/Cargo.toml +++ b/crates/rustysnes-gfx-shaders/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustysnes-gfx-shaders" description = "RustySNES: shared WGSL presentation-shader sources (blit + post-filters)" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-ios/Cargo.toml b/crates/rustysnes-ios/Cargo.toml index 53cb75c8..0d46df3c 100644 --- a/crates/rustysnes-ios/Cargo.toml +++ b/crates/rustysnes-ios/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustysnes-ios" description = "RustySNES: iOS Metal presentation host (wgpu-on-CAMetalLayer, no emulation logic)" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-mobile/Cargo.toml b/crates/rustysnes-mobile/Cargo.toml index db4d316f..0115ad10 100644 --- a/crates/rustysnes-mobile/Cargo.toml +++ b/crates/rustysnes-mobile/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustysnes-mobile" description = "RustySNES: UniFFI mobile bridge (Kotlin/Swift bindings over the emulation core)" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-monetization/Cargo.toml b/crates/rustysnes-monetization/Cargo.toml index a9dd5bc9..83704c4e 100644 --- a/crates/rustysnes-monetization/Cargo.toml +++ b/crates/rustysnes-monetization/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustysnes-monetization" description = "RustySNES: dormant entitlement/ad-pacing policy scaffold (mobile-only, never a dependency of the deterministic core)" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-netplay/Cargo.toml b/crates/rustysnes-netplay/Cargo.toml index 366a29e5..6f1a0e41 100644 --- a/crates/rustysnes-netplay/Cargo.toml +++ b/crates/rustysnes-netplay/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustysnes-netplay" description = "RustySNES: GGPO-style rollback netplay (UDP native, WebRTC browser)" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-ppu/Cargo.toml b/crates/rustysnes-ppu/Cargo.toml index 72d29da7..211a6a3e 100644 --- a/crates/rustysnes-ppu/Cargo.toml +++ b/crates/rustysnes-ppu/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustysnes-ppu" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-savestate/Cargo.toml b/crates/rustysnes-savestate/Cargo.toml index e3e5940f..023ebbbd 100644 --- a/crates/rustysnes-savestate/Cargo.toml +++ b/crates/rustysnes-savestate/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustysnes-savestate" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-script/Cargo.toml b/crates/rustysnes-script/Cargo.toml index 21fee15e..2ced8f7a 100644 --- a/crates/rustysnes-script/Cargo.toml +++ b/crates/rustysnes-script/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustysnes-script" description = "RustySNES: sandboxed Lua 5.4 scripting (memory read/write + a per-frame callback)" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/crates/rustysnes-test-harness/Cargo.toml b/crates/rustysnes-test-harness/Cargo.toml index 55c761ee..59322a18 100644 --- a/crates/rustysnes-test-harness/Cargo.toml +++ b/crates/rustysnes-test-harness/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustysnes-test-harness" -version = "1.18.0" +version = "1.19.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/docs/STATUS.md b/docs/STATUS.md index 3c33abb3..55d9ad88 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -3,12 +3,12 @@ This file is authoritative for per-suite pass counts, the board / coprocessor matrix, and version policy. Everything else defers to it. -**Current release:** `v1.18.0 "Dormant"` (`v0.1.0 "Foundation"`, +**Current release:** `v1.19.0 "Afterburner"` (`v0.1.0 "Foundation"`, `v0.2.0 "Persistence"`, `v0.3.0 "Continuum"`, `v0.4.0 "Completion"`, `v0.5.0 "Fidelity"`, `v0.6.0 "Shippable"`, `v0.7.0 "Resolution"`, `v0.8.0 "Community"`, `v0.9.0 "Threshold"`, `v1.0.0 "Zenith"`, `v1.0.1 "Aftertouch"`, `v1.1.0 "Latchkey"`, `v1.2.0 "Phosphor"`, `v1.3.0 "Palimpsest"`, and `v1.4.0 "Convergence"` precede it; see `to-dos/VERSION-PLAN.md` for the -full ladder). **`v1.5.0`-`v1.18.0`** are the opening rungs of the RustyNES-parity ladder — a CI +full ladder). **`v1.5.0`-`v1.19.0`** are the RustyNES-parity ladder — a CI safety net (`v1.5.0 "Bedrock"`), a MkDocs documentation site + PWA + accuracy ledger (`v1.6.0 "Lighthouse"`), the debugger extracted into its own module plus a hex Memory panel (`v1.7.0 "Telemetry"`, `v1.7.1` patch), a Memory Compare panel + in-app Docs panel @@ -44,7 +44,13 @@ the deterministic core, every pricing/pacing figure an explicit placeholder pend standing "Mobile Phase 6" store-launch gate) wired into both mobile shells as an inert, log-only startup call, real-verified on the Android AVD via `logcat` and compile-verified for iOS via a real macOS CI build after fixing a genuine `xcodebuild` xcframework-modulemap -collision found on that same CI run (`v1.18.0 "Dormant"`) — all frontend/tooling/CI work +collision found on that same CI run (`v1.18.0 "Dormant"`), and finally an optional PGO/BOLT +pipeline for the shipping `rustysnes` binary — `scripts/pgo/run.sh` (instrument → train against +the committed permissive ROM corpus → optimized rebuild) plus `.github/workflows/pgo.yml` +(`workflow_dispatch`/release-tag-only, promotion gated on both a `>3%` Criterion speedup and a +byte-identical `--features test-roms` re-run under the PGO profile, never on speed alone), +real-verified end-to-end in this development environment including a genuine BOLT-stage bug +found and fixed in PR review (`v1.19.0 "Afterburner"`) — all frontend/tooling/CI work with **zero change** to the accuracy dashboard, per-suite pass counts, or coprocessor tier matrix below, which stayed byte-identical throughout; see `CHANGELOG.md` for full per-release detail. `v1.0.0` diff --git a/to-dos/ROADMAP.md b/to-dos/ROADMAP.md index ca09512b..1ab4e677 100644 --- a/to-dos/ROADMAP.md +++ b/to-dos/ROADMAP.md @@ -365,10 +365,15 @@ under `v1.0.0`) and the netplay save-state-cost pre-work. pricing/pacing figure an explicit placeholder; wired into both mobile shells as an inert, log-only startup call, verified for real on the Android AVD via `logcat` and compile-verified for iOS via a real macOS CI build, which caught and fixed a genuine `xcodebuild` - xcframework-modulemap collision) — and a PGO/BOLT pipeline last (`v1.19.0`). Tracked in - lockstep against RustyNES's own continuing development via `to-dos/LOCKSTEP-CHECKLIST.md`, not - a frozen snapshot target. Full detail in `to-dos/VERSION-PLAN.md`'s "RustyNES-parity ladder" - section. + xcframework-modulemap collision), and finally an optional PGO/BOLT pipeline for the shipping + `rustysnes` binary (`v1.19.0 "Afterburner"`, **RELEASED 2026-07-15** — `scripts/pgo/run.sh` + + `.github/workflows/pgo.yml`, promotion gated on both a `>3%` Criterion speedup and a + byte-identical `--features test-roms` re-run under the PGO profile, never on speed alone; + verified for real end-to-end in this development environment, including a genuine BOLT-stage + structural bug found and fixed in PR review). This closes the RustyNES-parity ladder plan. + Tracked in lockstep against RustyNES's own continuing development via + `to-dos/LOCKSTEP-CHECKLIST.md`, not a frozen snapshot target. Full detail in + `to-dos/VERSION-PLAN.md`'s "RustyNES-parity ladder" section. - **Flagged by the 2026-07-12 lockstep re-check — no rung assigned yet, maintainer go/no-go needed.** RustyNES shipped two items since the roadmap's `v2.1.5` baseline that RustySNES's own ladder doesn't currently account for: (1) a **GIF/WAV screen+audio capture subsystem** diff --git a/to-dos/VERSION-PLAN.md b/to-dos/VERSION-PLAN.md index 0c746bfc..72b5c925 100644 --- a/to-dos/VERSION-PLAN.md +++ b/to-dos/VERSION-PLAN.md @@ -1284,12 +1284,28 @@ A store-launch decision (Play + App Store submission, monetization activation) r explicit maintainer go/no-go against `docs/mobile-readiness.md`, not a numbered rung — mirroring RustyNES's own still-pending, twice-deferred launch. -### `v1.19.0 "Afterburner"` — PGO/BOLT pipeline - -Deliberately last: a promotion gate requiring both >3% Criterion speedup AND byte-identical -`--features test-roms` re-run under the PGO profile (cites `docs/adr/0004`'s determinism -contract), deferred until mobile-specific hot-path work has landed so the profile isn't -invalidated mid-ladder. Optional Linux-only BOLT stage. +### `v1.19.0 "Afterburner"` — PGO/BOLT pipeline — **RELEASED 2026-07-15** + +Delivered: `scripts/pgo/run.sh` (instrument → train against the committed permissive ROM corpus +via a new `pgo_trainer` binary → optimized rebuild of the shipping `rustysnes` binary) and +`.github/workflows/pgo.yml` (`workflow_dispatch` + release-tag push only — never the PR gate). +Promotion requires both a `>3%` Criterion speedup over the plain release build **and** a +byte-identical `--features test-roms` re-run under the PGO profile (cites `docs/adr/0004`'s +determinism contract — never promotes on speed alone), deferred until this rung so the profile +isn't invalidated by mobile-specific hot-path work still landing. An optional Linux-only BOLT +post-link stage chains onto an already-promoted PGO binary, best-effort. + +Also fixed a real, latent CI gap found while building this: `rust-toolchain.toml` was missing +`llvm-tools-preview`, and `dtolnay/rust-toolchain` silently ignores the `rust-setup` composite +action's own `components:` input whenever a `rust-toolchain.toml` file exists (the same class of +bug already found and fixed for `ios.yml` in `v1.16.0`). + +Verified for real in this development environment: the full instrument → train → optimized- +rebuild pipeline produces a genuine, running `rustysnes` binary, and the determinism oracle +passes cleanly under the PGO-merged profile. A PR review caught a real structural bug in the +BOLT stage (re-invoking the whole training script mid-stage instead of chaining `--with-pgo` +onto the already-gathered PGO profile, which could have clobbered the bolt-instrumented binary) +— fixed per `cargo-pgo`'s own documented BOLT+PGO workflow. **On version numbers:** RustySNES's own SemVer stays independent of RustyNES's — this ladder reaches RustyNES's current maturity bar at RustySNES's own `v1.19.0`, not a literal "`v2.2.0`."