From 6871b8c0b7f0d155b68b91d63b41c14416f09ef3 Mon Sep 17 00:00:00 2001 From: Dani Sarfati Date: Tue, 11 Aug 2026 05:54:41 -0400 Subject: [PATCH 1/2] installer: give the R5000 build its own AppId, name, and folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The R4400 and R5000 builds differ only in the emulated CPU, but that is a compile-time choice (the cache model differs deeply), so they are two separate binaries rather than one installer with a runtime switch. They shared an AppId, AppName, and DefaultDirName, so installing one over the other was an in-place upgrade: same folder, same uninstall entry, the previous iris-gui.exe overwritten. Pass /DR5000=1 to ISCC to build the R5000 installer ("IRIS (R5000)", its own GUID and Program Files folder). Without the define nothing changes for the R4400 installer — same AppId as before, so existing installs keep upgrading in place rather than orphaning their uninstall entry. Co-Authored-By: Claude Opus 5 (1M context) --- installer/iris-gui.iss | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/installer/iris-gui.iss b/installer/iris-gui.iss index dccb38a..51859b5 100644 --- a/installer/iris-gui.iss +++ b/installer/iris-gui.iss @@ -38,14 +38,26 @@ #define ArchIdentifier "x64compatible" #endif -#define MyAppName "IRIS" +; CPU variant of the bundled iris-gui.exe. CI passes /DR5000=1 for the R5000 +; build; without it this is the default R4400 build. The two get distinct AppIds, +; names, and default folders so both can be installed side by side — same binary +; layout, different emulated CPU (a compile-time choice, so they can't be one +; installer with a switch). +#ifdef R5000 + #define MyAppName "IRIS (R5000)" + #define MyAppId "{{2B6E5D74-9C41-4A83-B7F0-5E1C8D2A64B9}" +#else + #define MyAppName "IRIS" + #define MyAppId "{{A7F2C91E-3D8B-4F5A-8E2C-1B9D6A3E8F42}" +#endif #define MyAppPublisher "Dani Sarfati" #define MyAppURL "https://github.com/danifunker/iris" #define MyAppExeName "iris-gui.exe" [Setup] -; Stable AppId — do not change across releases. -AppId={{A7F2C91E-3D8B-4F5A-8E2C-1B9D6A3E8F42} +; Stable AppIds — do not change across releases (an AppId change orphans the +; previous install's uninstall entry instead of upgrading it). +AppId={#MyAppId} AppName={#MyAppName} AppVersion={#MyAppVersion} AppPublisher={#MyAppPublisher} From 711d50bc4e87d7c4b8aa152ddad38b1d3e5a9a03 Mon Sep 17 00:00:00 2001 From: Dani Sarfati Date: Tue, 11 Aug 2026 05:54:50 -0400 Subject: [PATCH 2/2] iris-gui: make jit and ultra64 opt-in build features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iris-gui pinned its iris dependency to chd,camera,jit,rex-jit,ultra64, so every GUI build carried the v1 MIPS JIT and the N64 development board whether or not the user could reach them. Neither is usable without setup out of band from the GUI: the JIT needs IRIS_JIT=1 in the environment, and the dev board needs an external gopher64 fork plus a POSIX shm bridge the App Store sandbox blocks outright. Move both behind passthrough features (`--features jit`, `--features ultra64`) and cfg-gate the UI they own — the Ultra64 toggle, its Help window and menu entry, and the About panel's ultra64 line. The Debug tab's JIT knobs stay put but now say when there is no JIT behind them. Two consequences worth noting: - The About panel let "off (sandbox)" outrank not-compiled-in for jit/rex-jit. That read correctly while every build carried the JIT; a build without it would now name the wrong reason, so not-compiled-in is checked first. - HELP.md's Ultra64 setup built without the feature and then told you to set [ultra64] enabled = true, which silently does nothing. It now names the flag for both the CLI and the GUI. cranelift still arrives via rex-jit, so dropping `jit` sheds the MIPS translator rather than the compiler backend. Co-Authored-By: Claude Opus 5 (1M context) --- HELP.md | 6 +++++- iris-gui/Cargo.toml | 24 ++++++++++++++++-------- iris-gui/src/config_ui.rs | 24 ++++++++++++++++++------ iris-gui/src/main.rs | 32 ++++++++++++++++++-------------- 4 files changed, 57 insertions(+), 29 deletions(-) diff --git a/HELP.md b/HELP.md index 5da7678..3328540 100644 --- a/HELP.md +++ b/HELP.md @@ -691,8 +691,12 @@ development board IPC bridge. The IRIS-compatible fork lives at: enabled = true ``` + The dev board is an opt-in build feature — without `ultra64` the `[ultra64]` + section is ignored and no board appears in GIO slot 0: + ```bash - cargo run --release --features lightning,rex-jit + cargo run --release --features lightning,rex-jit,ultra64 # CLI + cargo run --release -p iris-gui --features ultra64 # GUI ``` 2. Build and run the N64 emulator (it can be started at any time — it will diff --git a/iris-gui/Cargo.toml b/iris-gui/Cargo.toml index 4c99214..0410f65 100644 --- a/iris-gui/Cargo.toml +++ b/iris-gui/Cargo.toml @@ -76,16 +76,24 @@ r5k = ["iris/r5k"] # YouTube / max in-process perf: lightning (no GDB hot-path) + idle-pause on the # embedded iris core. Build with: cargo build -p iris-gui --release --features premiere premiere = ["iris/lightning", "iris/idle-pause"] +# v1 MIPS JIT, then armed at runtime with IRIS_JIT=1. Off by default, so the +# Debug tab's JIT section is inert unless you build with this. +# Build with: cargo build -p iris-gui --features jit +jit = ["iris/jit"] +# N64 development board (Ultra64) — GIO slot 0 + POSIX shm bridge to an external +# gopher64. Gates both the core device and this crate's toggle/help window (the +# `ultra64` field on iris's Config only exists with the core feature on). +# Build with: cargo build -p iris-gui --features ultra64 +ultra64 = ["iris/ultra64"] [dependencies] -# Group A (additive) features are always on for iris-gui so the user can -# enable them at runtime via the config UI: chd (.chd disk paths), camera -# (vino source), jit (IRIS_JIT toggle), rex-jit, ultra64 (N64 dev board IPC -# bridge). They add code paths and native dependencies (ultra64: shared_memory -# + raw_sync, both cross-platform) but no runtime cost when unused — the -# ultra64 GIO device + POSIX shm bridge are only created when the user enables -# the board on the General tab. The toggle itself is hidden in App Store builds. -iris = { path = "..", features = ["chd", "camera", "jit", "rex-jit", "ultra64"] } +# Group A (additive) features are always on for iris-gui so the user can enable +# them at runtime via the config UI: chd (.chd disk paths), camera (vino source), +# rex-jit (REX3 draw-shader compiler). They add code paths and native deps but no +# runtime cost when unused. `jit` and `ultra64` used to be here too; they are now +# opt-in passthrough features (above), since neither is usable without extra +# setup — IRIS_JIT=1 for the one, an external gopher64 for the other. +iris = { path = "..", features = ["chd", "camera", "rex-jit"] } eframe = { version = "0.35", default-features = false, features = ["default_fonts", "glow", "wayland", "x11"] } egui = "0.35" crossbeam-channel = "0.5" diff --git a/iris-gui/src/config_ui.rs b/iris-gui/src/config_ui.rs index 397429f..098713e 100644 --- a/iris-gui/src/config_ui.rs +++ b/iris-gui/src/config_ui.rs @@ -345,11 +345,14 @@ fn show_general(ui: &mut Ui, cfg: &mut MachineConfig) -> ConfigAction { // N64 development board (Ultra64). A single runtime toggle — the GIO device // and POSIX shm bridge (/iris_n64_bridge) are only created when this is on, - // read once at VM start. Hidden in App Store builds: the sandbox can't open - // the named shm or run the external gopher64 process it talks to. + // read once at VM start. The toggle exists only in builds that carry the + // board (source builds with --features ultra64; shipped builds don't), and + // never in App Store builds: the sandbox can't open the named shm or run the + // external gopher64 process it talks to. #[cfg(not(feature = "appstore"))] { ui.separator(); + #[cfg(feature = "ultra64")] ui.checkbox(&mut cfg.ultra64.enabled, "N64 development board (Ultra64)") .on_hover_text( "Emulate the SGI Indy N64 development board. Requires the gopher64 \ @@ -357,10 +360,13 @@ fn show_general(ui: &mut Ui, cfg: &mut MachineConfig) -> ConfigAction { IRIX. Applies on next Start. See docs/ultra64.md.", ); ui.label( - RichText::new( + RichText::new(if cfg!(feature = "ultra64") { "Settings autosave ~600 ms after edits (watch for * next to the machine name). \ - Platform, resolution, and Ultra64 apply on the next Stop → Start.", - ) + Platform, resolution, and Ultra64 apply on the next Stop → Start." + } else { + "Settings autosave ~600 ms after edits (watch for * next to the machine name). \ + Platform and resolution apply on the next Stop → Start." + }) .weak() .small(), ); @@ -1405,7 +1411,13 @@ fn show_debug(ui: &mut Ui, cfg: &mut MachineConfig) -> ConfigAction { .small(), ); ui.separator(); - ui.label("JIT (requires `cargo build --features jit`)"); + // The v1 MIPS JIT is an opt-in build feature, so these knobs do nothing + // unless this binary was built with iris-gui's `jit` passthrough. + ui.label(if build_features::JIT { + "JIT" + } else { + "JIT (inert — this build has no JIT; rebuild with `cargo build -p iris-gui --features jit`)" + }); Grid::new("jit_grid").num_columns(2).striped(true).show(ui, |ui| { ui.label("Enable JIT (IRIS_JIT=1)"); ui.checkbox(&mut cfg.jit.enabled, ""); diff --git a/iris-gui/src/main.rs b/iris-gui/src/main.rs index 1f3bd8b..c27d8f1 100644 --- a/iris-gui/src/main.rs +++ b/iris-gui/src/main.rs @@ -262,6 +262,7 @@ struct App { /// Whether the "Mount the shared folder in IRIX" Help window is open. show_nfs_help: bool, /// Whether the "N64 development board (Ultra64)" Help window is open. + #[cfg(feature = "ultra64")] show_ultra64_help: bool, /// Whether the License / Privacy Help windows are open. show_license: bool, @@ -494,6 +495,7 @@ impl App { serial_input: String::new(), show_help_info: false, show_nfs_help: false, + #[cfg(feature = "ultra64")] show_ultra64_help: false, show_license: false, show_privacy: false, @@ -1409,10 +1411,11 @@ impl App { self.show_nfs_help = true; ui.close(); } - // N64 dev board getting-started guide. Hidden in App Store - // builds, where the board can't run (sandbox blocks the POSIX - // shm bridge and there's no way to run the external gopher64). - #[cfg(not(feature = "appstore"))] + // N64 dev board getting-started guide. Only in builds that carry + // the board (source builds with --features ultra64), and never in + // App Store builds, where it can't run anyway (sandbox blocks the + // POSIX shm bridge and there's no way to run the external gopher64). + #[cfg(all(feature = "ultra64", not(feature = "appstore")))] if ui.button("🎮 N64 development board (Ultra64)…") .on_hover_text("How to set up the N64 devkit and run ROMs with gload") .clicked() @@ -1454,20 +1457,19 @@ impl App { // jit/rex-jit are compile-time features, but the sandbox (App // Store) build forces interpreter-only at runtime via IRIS_NO_JIT // (Cranelift's non-MAP_JIT pages get killed under the sandbox). - // Report the runtime reality so a compiled-in "jit: on" doesn't - // read as "the JIT is running" when it can't be. + // Report the runtime reality so a compiled-in "on" doesn't read + // as "the JIT is running" when it can't be. Not-compiled-in is + // plain "off" — shipped builds carry rex-jit but not the v1 jit, + // so "off (sandbox)" there would name the wrong reason. let jit_off = std::env::var_os("IRIS_NO_JIT").is_some(); - let jit_state = |feat: bool| if jit_off { "off (sandbox)" } else if feat { "on" } else { "off" }; + let jit_state = |feat: bool| if !feat { "off" } else if jit_off { "off (sandbox)" } else { "on" }; ui.label(format!(" jit: {}", jit_state(bf::JIT))); ui.label(format!(" rex-jit: {}", jit_state(bf::REX_JIT))); ui.label(format!(" lightning: {}", if bf::LIGHTNING { "on (no debug)" } else { "off" })); - // ultra64 (N64 dev board) is compiled in, but the App Store - // sandbox can't open its POSIX shm bridge — report the runtime - // reality there, matching the jit/rex-jit treatment above. - let ultra64_state = if cfg!(feature = "appstore") { - "off (sandbox)" - } else if bf::ULTRA64 { "on" } else { "off" }; - ui.label(format!(" ultra64: {}", ultra64_state)); + // ultra64 (N64 dev board) is a source-build opt-in; shipped builds + // don't carry it, and the App Store sandbox couldn't open its + // POSIX shm bridge even if they did. + ui.label(format!(" ultra64: {}", if bf::ULTRA64 { "on" } else { "off" })); }); }); } @@ -2754,6 +2756,7 @@ impl App { /// bridge); the N64 itself is the external gopher64 fork, so the guide is /// mostly about wiring the two processes together. Never reachable in App /// Store builds (the menu item that opens it is compiled out there). + #[cfg(feature = "ultra64")] fn ultra64_help_window(&mut self, ctx: &egui::Context) { if !self.show_ultra64_help { return; @@ -3256,6 +3259,7 @@ impl eframe::App for App { self.nfs_help_window(ctx); // Help → "N64 development board (Ultra64)" — devkit getting-started guide. + #[cfg(feature = "ultra64")] self.ultra64_help_window(ctx); // "Synchronizing disks…" modal during the exit-time CHD fold-back.