Skip to content

feat(mobile): v1.14.0 "Foundry" - UniFFI bridge foundations#99

Merged
doublegate merged 2 commits into
mainfrom
feat/v1.14.0-foundry
Jul 12, 2026
Merged

feat(mobile): v1.14.0 "Foundry" - UniFFI bridge foundations#99
doublegate merged 2 commits into
mainfrom
feat/v1.14.0-foundry

Conversation

@doublegate

Copy link
Copy Markdown
Owner

Summary

  • New crate rustysnes-mobile: a UniFFI bridge over rustysnes_core::facade::EmuCore — the same std-only facade the desktop frontend and rustysnes-libretro already drive the emulator through. Adds no new emulation logic, only an FFI-safe wrapper.
  • MVP surface: ROM load/close, run_frame, the peripheral setters (Gamepad/Mouse/Super Scope/Multitap), framebuffer + audio drain, save/load state, reset/power-cycle. 7 host-side unit tests.
  • Verified for real, not just claimed: a genuine cargo ndk -t arm64-v8a build cross-compile against this environment's actual NDK (r29) produced a real ELF 64-bit ARM aarch64 ... for Android 21 shared object (confirmed via file, not just a green exit code). uniffi-bindgen generated real Kotlin AND Swift bindings from the compiled library, inspected for correct method names/types/throws/@Throws.
  • Expands the no_std CI gate from one aggregate rustysnes-core build into a per-crate matrix (rustysnes-{cpu,ppu,apu,cart,core} each build standalone against thumbv7em-none-eabihf) — verified locally before pushing.
  • Reverses v1.0.0's "no mobile appetite" default: new docs/adr/0012-mobile-platform-target.md records the decision, new docs/mobile-readiness.md is the living status page. to-dos/VERSION-PLAN.md/to-dos/ROADMAP.md's stale no-appetite lines are corrected in the same change.
  • Honest scope note: this environment has a real Android SDK/NDK, so v1.15.0's Android work is genuinely buildable/testable here going forward. It has no macOS/Xcode toolchain at all, so v1.16.0's iOS work will be written and Rust-side compile-checked but needs the project owner's own Mac for real Xcode verification — documented explicitly in docs/mobile-readiness.md, not silently glossed over.

Test plan

  • cargo fmt --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace (all green, including 7 new rustysnes-mobile tests)
  • cargo build -p rustysnes-{cpu,ppu,apu,cart,core} --target thumbv7em-none-eabihf --no-default-features (the new per-crate matrix, verified locally for all 5)
  • cargo ndk -t arm64-v8a build -p rustysnes-mobile against a real NDK — output confirmed via file to be a genuine ARM64 Android shared object
  • uniffi-bindgen generate --library <compiled .so> --language kotlin and --language swift — both inspected, every method present with correct names/types
  • RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps
  • markdownlint-cli2 on every touched/new doc — zero new findings (two new files at zero findings; touched files at pre-existing baseline counts)

🤖 Generated with Claude Code

Adds rustysnes-mobile: a UniFFI bridge over rustysnes_core::facade::
EmuCore (the same std-only facade the desktop frontend and
rustysnes-libretro already drive the emulator through). MVP surface:
ROM load/close, run_frame, the peripheral setters (Gamepad/Mouse/
Super Scope/Multitap), framebuffer + audio drain, save/load state,
reset/power-cycle.

Verified for real, not just claimed: a genuine `cargo ndk` cross-
compile to arm64-v8a produced an actual ARM64 .so (confirmed via
`file`), and `uniffi-bindgen` generated correctly-shaped Kotlin and
Swift bindings from the compiled library (inspected method names/
types/throws).

Expands the no_std CI gate from one aggregate rustysnes-core build to
a per-crate matrix (cpu/ppu/apu/cart/core each build standalone).

Reverses v1.0.0's "no mobile appetite" default (docs/adr/0012), adds
docs/mobile-readiness.md as the living status page. Android-first:
this environment has a real NDK, so v1.15.0's Android work can be
genuinely built/tested here. No macOS/Xcode toolchain exists here, so
iOS work (v1.16.0) will be written and compile-checked but needs the
project owner's own Mac for real verification -- flagged honestly,
not silently claimed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 12, 2026 14:39

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the rustysnes-mobile crate, establishing a UniFFI bridge to generate Kotlin and Swift bindings over the EmuCore emulation facade. It also adds architecture decision records and mobile readiness documentation, and expands the no_std CI gate to a per-crate matrix. Feedback suggests optimizing the performance-critical audio draining path by using a caller-provided reusable buffer to avoid heap allocations, and updating the pinned workspace dependency versions in Cargo.toml to prevent version mismatches.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread crates/rustysnes-mobile/src/lib.rs
Comment thread crates/rustysnes-mobile/Cargo.toml

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces the foundations for a mobile embedding by adding a new rustysnes-mobile crate that exposes an FFI-safe UniFFI surface over rustysnes_core::facade::EmuCore, alongside documentation/ADR updates and CI adjustments to strengthen the no_std gate across individual chip crates.

Changes:

  • Add crates/rustysnes-mobile UniFFI bridge crate (ROM lifecycle, frame stepping, framebuffer/audio access, peripherals, save/load state).
  • Expand the no_std CI job from a single aggregate build into a per-crate matrix (rustysnes-{cpu,ppu,apu,cart,core}).
  • Add ADR + “mobile readiness” status doc and update roadmap/version-plan/changelog to reflect the mobile track.

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
to-dos/VERSION-PLAN.md Updates the long-term ladder text to include the mobile track and a v1.14.0 "Foundry" entry.
to-dos/ROADMAP.md Updates roadmap narrative to include the mobile bridge foundations rung.
docs/mobile-readiness.md New living status page describing the mobile bridge surface, what’s verified, and what’s deferred.
docs/adr/0012-mobile-platform-target.md New ADR recording the decision to target mobile via UniFFI and defining constraints/consequences.
crates/rustysnes-mobile/src/lib.rs Implements the UniFFI-exposed MobileCore wrapper over EmuCore + unit tests.
crates/rustysnes-mobile/src/bin/uniffi-bindgen.rs Adds a helper binary entrypoint for running UniFFI bindgen.
crates/rustysnes-mobile/Cargo.toml Adds the new crate manifest (cdylib/staticlib/rlib + UniFFI deps).
CHANGELOG.md Notes the new mobile bridge crate and the no_std CI matrix expansion.
Cargo.toml Adds crates/rustysnes-mobile to the workspace members list.
Cargo.lock Locks new UniFFI-related dependencies.
.github/workflows/ci.yml Updates no_std job into a per-crate matrix build.

Comment thread crates/rustysnes-mobile/src/lib.rs Outdated
Comment thread docs/mobile-readiness.md
Comment thread CHANGELOG.md
Comment thread to-dos/VERSION-PLAN.md Outdated
Comment thread to-dos/ROADMAP.md Outdated
Comment thread crates/rustysnes-mobile/Cargo.toml
Comment thread crates/rustysnes-mobile/src/bin/uniffi-bindgen.rs Outdated
Addresses gemini/copilot findings on PR #99:
- Gemini's &mut Vec<i16> out-parameter suggestion for drain_audio
  doesn't type-check under #[uniffi::export] (its generated FFI glue
  always lowers &mut T to &T -- confirmed by actually trying it, real
  compile error). Kept the owned-Vec return (required by UniFFI's
  calling convention) but pre-sized its capacity to avoid mid-loop
  reallocation, the achievable half of the perf win.
- Copilot: drain_audio's name implied FIFO/pop semantics it doesn't
  have -- it mirrors EmuCore::audio()'s existing non-destructive
  contract (call once per run_frame), exactly like every other
  consumer (rustysnes-frontend's sync and emu-thread paths) already
  relies on. Corrected the doc comment, CHANGELOG, and
  docs/mobile-readiness.md wording instead of inventing new
  drain-tracking logic no other consumer needs.
- Copilot: VERSION-PLAN.md/ROADMAP.md prematurely marked v1.14.0
  RELEASED in this feature PR, before the version bump -- removed;
  the RELEASED marker belongs in the separate chore(release) closeout
  PR, matching every prior rung's two-PR pattern.
- Copilot: uniffi's `cli` feature (askama/clap/goblin -- host-only
  binding-generator tooling) was pulled into every build including
  Android/iOS cross-compiles. Gated behind a new opt-in `bindgen`
  Cargo feature + `required-features` on the uniffi-bindgen binary;
  re-verified the real cargo-ndk cross-compile and Kotlin/Swift
  binding generation both still work with the leaner default.
- Gemini's rustysnes-core/-cart version-pin suggestion (1.4.0 ->
  1.13.0) not applied: matches the exact pre-existing pin convention
  every other crate in this workspace already uses (path deps pin a
  stable minimum-compatible version, not the current release --
  confirmed against rustysnes-frontend's own identical pins).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@doublegate doublegate merged commit cbc9df3 into main Jul 12, 2026
12 checks passed
@doublegate doublegate deleted the feat/v1.14.0-foundry branch July 12, 2026 14:56
@doublegate doublegate mentioned this pull request Jul 12, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants