Skip to content

v2.5.0 "Web Awakens"

Choose a tag to compare

@doublegate doublegate released this 02 Jul 18:01

Rusty2600 v2.5.0 "Web Awakens"

Second release of the RustyNES gap-closure arc — the headline item here is
the single largest capability gap the whole gap analysis found: a real
winit+wgpu+egui rendering build on the web, replacing the bare
canvas-2D bootstrap that's carried the GH-Pages demo since it first shipped.

Shipped through PR #14, reviewed by GitHub Copilot and Gemini Code Assist —
7 findings, all genuine, all fixed (including a real memory leak in the new
wasm ROM-loading path and a latent wasm32 feature-gate compile-error risk).

Real winit+wgpu+egui rendering on wasm32 (the headline gap)

The GH-Pages demo has always been a bare canvas-2D requestAnimationFrame
bootstrap — a real winit+wgpu+egui build matching the native binary was
explicitly never attempted, honestly documented as such throughout this
project's history. This release makes the real attempt.

The SAME App/Gfx/shader_pass/emu_thread shell the native build uses
now compiles for wasm32-unknown-unknown behind a real wasm-winit
feature (previously an empty placeholder). The hard architectural problem —
Gfx::new_async's adapter/device acquisition can't block the browser's
single JS thread the way native's pollster::block_on does — is solved via
wasm_bindgen_futures::spawn_local, populating a shared
Rc<RefCell<Option<Active>>> cell once ready; every ApplicationHandler
callback already had to treat a not-yet-resumed Active as a no-op, so
this needed no further changes elsewhere. ROM loading routes through a
hidden <input type=file> instead of rfd (a native-only dependency).

Honest status, stated plainly: it compiles cleanly, and a real trunk build produces a working bundle — confirmed loading in headless Chromium,
with run_winit() executing and logging correctly. But the wgpu
adapter-request step itself could not be verified actually rendering a
frame: this sandbox's headless-Chromium-with-SwiftShader environment has no
navigator.gpu and, in this specific setup, wgpu 29's adapter request also
failed to fall back to the compiled-in GL/WebGL2 backend (gl support not compiled in / webgpu found no adapters) — independently reproduced
twice, once during implementation and once during coordinator verification
on a fresh build. This is a likely wgpu-internal webgpu-vs-wgpu_core
backend-dispatch limitation for this target, not something fixable from
this project's own Cargo.toml alone (egui-wgpu's own unconditional
wgpu dependency reintroduces the webgpu feature via Cargo's per-crate
feature unification regardless of what this crate's own Cargo.toml
requests). Real desktop browsers with genuine GPU access — real WebGPU
support, or a real hardware-accelerated GL driver instead of SwiftShader —
may well work where this sandboxed environment could not; that's a real,
open question for a future session with real browser access to confirm.

wasm-canvas (the proven, complete canvas-2D bootstrap) stays the actual
GH-Pages-deployed build for now. wasm-winit is real, committed, and
available to build — just not promoted to the live demo until rendering is
confirmed in a real browser.

GH Pages /api/ rustdoc hosting — already done

Checked before starting any new work: this was already live (confirmed via
a direct HTTP check against the deployed site). No code needed.

Debugger Lua console panel

Lua's print() previously went nowhere useful — real stdout, invisible in
a GUI app. crates/rusty2600-script's Lua VM now overrides the print
global to route into a new capped ScriptLog ring buffer (500 lines,
oldest dropped first), matching real Lua semantics (arguments tab-joined,
re-resolving the global tostring on every call so a script that
overrides tostring still sees that reflected). onFrame runtime errors
are captured into the same log automatically. A new Debug -> Lua Console
panel renders the captured history live, errors in red, with a Clear
button. Output-only — not an interactive REPL (would need to respect the
same WritesLocked determinism gate the normal onFrame tick already
enforces; real additional design work deliberately out of scope here).

Keyboard Controller / Trak-Ball — researched, not modeled

A documentation-only decision, no code change. Checked Stella's own
implementation and properties database rather than assumption: the
Trak-Ball has zero official Atari 2600 releases (only 2 homebrew ROMs in
Stella's database); the Keyboard/Keypad Controller has 40 ROMs including
one real official release (Brain Games, 1978) but remains niche relative
to the whole catalogue. Neither is modeled this arc — deliberately
deprioritized, not a permanent "never."

PR review, for real

7 findings from GitHub Copilot and Gemini Code Assist, all genuine, all
fixed — none dismissed:

  • A real memory leak (Gemini, high priority): the wasm ROM FileReader's
    on_load callback leaked its heap allocation on every single ROM load
    (Closure::<dyn FnMut()>::new + .forget() instead of the
    once-per-read-appropriate Closure::once_into_js).
  • A latent wasm32 feature-gate mismatch (Gemini): app.rs's shell.render
    call site gated cheevos/script arguments on bare feature flags while
    shell.rs gates the matching parameters on all(not(wasm32), feature = "...") — a wasm32 build with either feature enabled would have failed
    to compile.
  • Lua's print() now re-resolves tostring on every call instead of
    capturing it once at install time, matching real Lua semantics.
  • The wasm ROM-picker <input> now clears its value before .click(), so
    re-selecting the same file reliably fires change again.
  • A stale doc comment (log_error no longer needs to be called by a host
    after tick_frame — it already calls it internally) and two doc/comment
    drift fixes (claiming web/index.html builds wasm-winit when it
    actually builds wasm-canvas).

Test count

333 tests passing on default features (337 with --features test-roms),
up from 327/331 at v2.4.0. Full CI green — Linux/macOS/Windows, the perf
regression gate, and the no_std gate.

What's next

v2.6.0 "Rollback Bridge" — netplay's WebRTC transport, starting with an
explicit ADR on how far the async-runtime surface it needs can stay
contained to the netplay-feature-gated, wasm-target code path. See
to-dos/ROADMAP.md for the complete v2.4.0 -> v3.0.0 roadmap.