From 28e066be6abbf0ba558d6ca0525edc517fc1f7eb Mon Sep 17 00:00:00 2001
From: DoubleGate
Date: Sun, 12 Jul 2026 03:56:38 -0400
Subject: [PATCH] =?UTF-8?q?release:=20cut=20v2.1.9=20"Fathom"=20(presentat?=
=?UTF-8?q?ion=20and=20signal=20=E2=80=94=20"Aperture")?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Cuts the v2.1.9 release — the presentation-and-signal step of the v2.1.5 → v2.2.0
"deepen the existing project" run, a marquee display release. Bumps the workspace
version 2.1.8 → 2.1.9, renames CHANGELOG `[Unreleased]` → `[2.1.9]`, refreshes
Cargo.lock, syncs the README badge / current-release paragraph / BibTeX, and
authors `.github/release-notes/v2.1.9.md`.
v2.1.9 (PR #283) is all opt-in / default-off display work, so the shipped default
presentation stays byte-identical (AccuracyCoin 141/141, nestest 0-diff,
visual_regression byte-identical):
- Marquee CRT shader stack: WGSL ports of CRT-Royale, crt-guest-advanced, and
Sony Megatron (HDR), selectable with mask/scanline/curvature controls + per-game
presets.
- Raw NTSC composite signal-decode path: a new rustynes-ppu::raw_signal
un-decoded 2C02 composite model + a signal_decode.wgsl demodulator (rotating
oscillator, edge-consistent phase) for artifact-accurate color.
- GIF + WAV capture (extending av-record) + a generated-NTSC palette editor with
a live preview.
No default-build behavior change.
Co-Authored-By: Claude Opus 4.8
---
.github/release-notes/v2.1.9.md | 31 ++++++++++++++++++++++++++++
CHANGELOG.md | 2 ++
Cargo.lock | 36 ++++++++++++++++-----------------
Cargo.toml | 2 +-
README.md | 27 +++++++++++--------------
5 files changed, 64 insertions(+), 34 deletions(-)
create mode 100644 .github/release-notes/v2.1.9.md
diff --git a/.github/release-notes/v2.1.9.md b/.github/release-notes/v2.1.9.md
new file mode 100644
index 00000000..54b05630
--- /dev/null
+++ b/.github/release-notes/v2.1.9.md
@@ -0,0 +1,31 @@
+# RustyNES v2.1.9 — Fathom (presentation and signal — Aperture)
+
+The **presentation-and-signal** step of the v2.1.5 → v2.2.0 "deepen the existing project" run — a marquee display release. Every shader / signal / capture / editor path is **opt-in and default-off**, so the shipped default presentation stays **byte-identical**: **AccuracyCoin 141/141 (100.00%)**, nestest 0-diff, `visual_regression` byte-identical. Landed in PR #283.
+
+## Added
+
+### Marquee CRT shader stack (B6)
+
+WGSL ports of three reference slang CRT shaders, first-class `BuiltinPass` variants selectable in **Settings → Shaders**, each with `#pragma parameter` sliders wired to the uniform slots:
+
+- **CRT-Royale** — the aperture/shadow/slot-mask + scanline + convergence classic.
+- **crt-guest-advanced / guest-dr-venom** — mask type, scanline weight, beam, glow, curvature.
+- **Sony Megatron** — the HDR CRT model (mask/gamma/HDR controls).
+
+Plus **per-game shader presets** (`PerGameConfig.shader_preset`, resolved on ROM load via `ShaderPresetBank::resolve`; `None`/unknown = byte-identical default) and five showcase built-in presets. All four new shaders parse and validate under the exact naga-29 validator wgpu uses.
+
+### Raw NTSC composite signal-decode path (P4)
+
+A new `rustynes-ppu::raw_signal` module generates the **un-decoded 2C02 composite waveform** (the Bisqwit `nes_ntsc` / Mesen2 "raw palette" model — a 512×12 chroma/level/emphasis table, `no_std`, transcendental-free with a `GOLDEN_SIGNAL` const-eval lock), and a new `signal_decode.wgsl` pass reconstructs the true two-level chip square wave from the index framebuffer and demodulates it — giving **signal-accurate artifact color** (waterfall dithering, transparency tricks) alongside the existing generated-palette + shader ladder. The demodulator uses a rotating-oscillator (3 transcendentals/pixel, not per-tap `sin`/`cos`) and a non-negative modulo so edge pixels stay phase-consistent.
+
+### Capture + palette editor
+
+- **GIF + WAV export** (`Container::{Gif,Wav}`) extending the `av-record` capture pipeline — video-only palettegen/paletteuse GIF at 25 fps, audio-only `pcm_s16le` WAV, with save-dialog filters. The recorder is unchanged (read-only taps; the emulation timeline is never perturbed).
+- **Generated-NTSC palette editor** with a **live 16×4 preview** that regenerates from the current hue/sat/contrast/brightness/gamma params via `generate_base_palette` as you drag.
+
+## Notes
+
+- Version: workspace `2.1.8 → 2.1.9` (Cargo.lock refreshed). No dependency or mobile changes (the mobile apps re-release at v2.3.0).
+- **No default-build behavior change.** All shaders, the raw-composite path, capture, and the palette editor are opt-in; the default presentation and the deterministic core are untouched. AccuracyCoin stays **141/141 (100.00%)**, nestest 0-diff, `visual_regression` byte-identical, save-state and the 60-ROM commercial oracle unchanged.
+- Docs updated in-change: `docs/frontend.md` (the CRT stack, capture, palette editor), `docs/ppu-2c02.md` (raw-composite output), `CHANGELOG.md`.
+- Full host gate green: fmt · clippy (native all-targets + feature combos incl. `av-record` + wasm32) · rustdoc (`-D warnings`) · `no_std` cross-compile (`thumbv7em`) · markdownlint (pinned v0.39.0) · the new-shader naga validation + `shader_pass` / `raw_signal` / `per_game` suites · AccuracyCoin 141/141 + `visual_regression` byte-identical · version consistency.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a8209cef..a49e70cc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,8 @@ cycle-accurate core later replaced.
## [Unreleased]
+## [2.1.9] - 2026-07-12 - "Fathom" (presentation and signal — marquee CRT shader stack (CRT-Royale / guest-advanced / Sony Megatron) + raw NTSC composite signal-decode path + GIF/WAV capture + generated-palette editor — "Aperture")
+
### Added
- **Marquee CRT shader stack + raw NTSC composite signal (v2.1.9 "Presentation
diff --git a/Cargo.lock b/Cargo.lock
index 051054b2..150eda2b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4248,7 +4248,7 @@ dependencies = [
[[package]]
name = "rustynes-android"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"android-activity",
"android_logger",
@@ -4266,7 +4266,7 @@ dependencies = [
[[package]]
name = "rustynes-apu"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"bitflags 2.13.0",
"criterion",
@@ -4279,7 +4279,7 @@ dependencies = [
[[package]]
name = "rustynes-cheevos"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"cc",
"ureq",
@@ -4287,7 +4287,7 @@ dependencies = [
[[package]]
name = "rustynes-core"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"bitflags 2.13.0",
"criterion",
@@ -4304,7 +4304,7 @@ dependencies = [
[[package]]
name = "rustynes-cpu"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"bitflags 2.13.0",
"criterion",
@@ -4315,7 +4315,7 @@ dependencies = [
[[package]]
name = "rustynes-frontend"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"anstyle",
"arboard",
@@ -4369,11 +4369,11 @@ dependencies = [
[[package]]
name = "rustynes-gfx-shaders"
-version = "2.1.8"
+version = "2.1.9"
[[package]]
name = "rustynes-hdpack"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"lewton",
"png",
@@ -4384,7 +4384,7 @@ dependencies = [
[[package]]
name = "rustynes-ios"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"bytemuck",
"cpal",
@@ -4398,7 +4398,7 @@ dependencies = [
[[package]]
name = "rustynes-libretro"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"libc",
"rust-libretro",
@@ -4407,7 +4407,7 @@ dependencies = [
[[package]]
name = "rustynes-mappers"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"bitflags 2.13.0",
"criterion",
@@ -4419,7 +4419,7 @@ dependencies = [
[[package]]
name = "rustynes-mobile"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"rustynes-core",
"rustynes-hdpack",
@@ -4434,14 +4434,14 @@ dependencies = [
[[package]]
name = "rustynes-monetization"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"uniffi",
]
[[package]]
name = "rustynes-netplay"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"futures-util",
"js-sys",
@@ -4457,7 +4457,7 @@ dependencies = [
[[package]]
name = "rustynes-ppu"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"bitflags 2.13.0",
"criterion",
@@ -4469,14 +4469,14 @@ dependencies = [
[[package]]
name = "rustynes-ra"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"rustynes-cheevos",
]
[[package]]
name = "rustynes-script"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"mlua",
"piccolo",
@@ -4487,7 +4487,7 @@ dependencies = [
[[package]]
name = "rustynes-test-harness"
-version = "2.1.8"
+version = "2.1.9"
dependencies = [
"insta",
"png",
diff --git a/Cargo.toml b/Cargo.toml
index cb377f46..8546cdc3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,7 +21,7 @@ members = [
]
[workspace.package]
-version = "2.1.8"
+version = "2.1.9"
edition = "2024"
rust-version = "1.96"
license = "MIT OR Apache-2.0"
diff --git a/README.md b/README.md
index 6dfbe55d..a6ab3c34 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
-

+


@@ -775,19 +775,16 @@ and the Material-for-MkDocs documentation handbook at
## Current Release
-RustyNES's current release is **v2.1.8 "Fathom" ("Tempo")**, the performance step of the
-v2.1.5 → v2.2.0 "deepen the project" run — it adds a **specialized visible-scanline fast
-PPU dot path** (a straight-line renderer for clean dots that drops to the exact 2-cycle-ALE
-loop the instant any sub-scanline disturbance occurs; **~12% faster** on rendering-heavy
-frames), a set of **SIMD-validated software palette→RGBA blitter paths** (scalar / `wide` /
-wasm `+simd128`, all proven byte-identical — but the conversion is a **memory-bound LUT
-gather**, so no SIMD path clears the >3% bar and **scalar stays the default**; the vector
-paths are validated and available, not a shipped speedup), and a **wasm size/startup pass**
-(`wasm-opt -O4` → 3.99 MiB gzip, comfortably under budget). The fast dot path is
-**default-off** and **differential-tested bit-identical** to the exact path across the
-corpus (framebuffer + audio + cycles + snapshot) — so the shipped build stays byte-identical:
-**AccuracyCoin stays 141/141 (100.00%)**, nestest 0-diff, `visual_regression` /
-`pal_apu_tests` unchanged.
+RustyNES's current release is **v2.1.9 "Fathom" ("Aperture")**, the presentation-and-signal
+step of the v2.1.5 → v2.2.0 "deepen the project" run — it adds a **marquee CRT shader stack**
+(WGSL ports of **CRT-Royale**, **crt-guest-advanced**, and **Sony Megatron** HDR, selectable
+in Settings → Shaders with mask/scanline/curvature controls and per-game presets), a
+**raw NTSC composite signal-decode path** (a new `rustynes-ppu::raw_signal` un-decoded 2C02
+composite model plus a `signal_decode.wgsl` pass that reconstructs and demodulates the real
+signal for artifact-accurate color), **GIF + WAV capture** (extending `av-record`), and a
+**generated-NTSC palette editor** with a live preview. Every shader / signal / capture path
+is **opt-in and default-off**, so the shipped default presentation stays byte-identical:
+**AccuracyCoin stays 141/141 (100.00%)**, nestest 0-diff, `visual_regression` byte-identical.
The v2.1.x line opened with **v2.1.0**, the accuracy-remediation release (a display-only
PPU palette-backdrop-override fix, 86 mapper families promoted BestEffort → Curated, and
@@ -913,7 +910,7 @@ If you use RustyNES in academic research, please cite:
author = {RustyNES Contributors},
title = {RustyNES: A Cycle-Accurate NES Emulator in Rust},
year = {2026},
- version = {2.1.8},
+ version = {2.1.9},
url = {https://github.com/doublegate/RustyNES},
note = {Cycle-accurate NES emulator on a master-clock-precise scheduler;
AccuracyCoin 100\% (141/141), nestest 0-diff; 172 mapper families,