Skip to content

feat(studio): rune-fx effects in the studio UI - #85

Merged
kixelated merged 5 commits into
mainfrom
quest/m1/ui-effects
Nov 7, 2026
Merged

kixelated merged 5 commits into
mainfrom
quest/m1/ui-effects

Conversation

@kixelated

@kixelated kixelated commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The studio's UI was flat egui. It didn't show at a glance that the program is on air or recording, or acknowledge a take, and the Health panel only reported a failure as text. quest/m1/ui-effects.md, planned in moq-dev/studio#72, asked for rune-fx effects drawn inside egui, with an Effects setting that follows the OS reduced-motion preference.

Approach

  • rune-fx inside egui. rune-fx is pinned to rev b99c7b0 of rune's dev branch, declared once in the root Cargo.toml. It draws on the window's own wgpu device through egui_wgpu paint callbacks. The new fx module owns one FxRenderer in egui's callback resources. Each frame, every layer goes up in one prepare, uploaded by the first callback that runs, and each callback draws its own range of layers.
  • Marking where effects go. A panel reserves an under-layer and an over-layer shape slot (Mark, Spot) while the dock draws it. Panels::draw_effects fills those slots once the dock has finished. This keeps effects out of the panels' code, and effects never touch the compositor, the program, or an encode.
  • The effects:
    • Program: a red tally glow around the Program monitor while a relay is connected.
    • Takes: a ripple and a sparkle burst on each take, whether fired by the button or a hotkey (State::takes counts cuts and transitions).
    • Recording: a red dot that pulses in the Program header.
    • Controls: a flat liquid-metal Cut button, and plastic keycaps in the Hotkeys panel.
    • Backgrounds: a static grain on panel bodies, and a mesh gradient behind first-launch setup.
    • Health: the Health panel and its tab pulse amber when the stream is Degraded and flicker electric red when it is Unhealthy.
  • Quiet when idle. Each effect warms and cools on its own Light (attack and cool times on egui's f64 clock), and rune-fx culls a layer at zero intensity. Bursts are capped at 4 in flight. Layer time wraps hourly, so f32 precision holds over a long show.
  • Turning effects down. View > Effects offers Follow the system, Full, Reduced, or Off. The setting is kept in eframe storage with the other editor settings. Following the system reads macOS's Reduce motion and Windows' client-area animations each frame. On Linux it reads the desktop Settings portal's reduced-motion key, following its changes. Reduced uses rune-fx's reduced mode, and Off draws nothing.
  • README.md gains an Effects section.

Choices made without asking:

  • "Follow the system" is a fourth menu entry beside Full/Reduced/Off, showing what it currently resolves to, so a pick can be undone.
  • The glow means "connected to a relay" (on air); an electric border is kept for Health so the two never look alike.
  • A recording dot is new in the Program header, so the recording pulse has something to sit on.
  • The take burst is ripple plus sparkles; no backdrop distortion, which would mean sampling the monitor texture.
  • Cut uses LiquidMetal with dome: 0 and a bevel. The default dome's reflection horizon crossed the label and made it unreadable.
  • My own f64 Light replaces rune's Heat and one-shot timers. They take f32 seconds, which lose precision as egui's clock grows.
  • Studio no longer implements eframe::App. Window wraps every frame in fx.begin and fx.finish, so Studio::ui takes the Fx.
  • moq-studio now enables moq-stats' async feature. Without it, cargo check -p moq-studio failed on main (PrefixConsumer::next); it only built inside the workspace through feature unification.

Impact

Measured on nixos (RTX 3070 Ti, Xvfb at 1440x900, release build, openh264 encoding a recording). The program was forced on air, the Health verdict was forced Unhealthy, and a take fired every 2 s, so every effect ran at once. That is 11 to 13 layers, the worst case. Steady-state averages over 300 frames:

Effects Effects GPU UI CPU UI frame Program render Program copy
Full 1.44 ms 1.7-2.1 ms 42-44 ms 18.9-19.9 ms 40.0 ms
Reduced 1.42-1.45 ms 1.8-2.0 ms 43-44 ms 19.0-19.6 ms 39.6-40.0 ms
Off none 1.5-1.6 ms 40.4-40.6 ms 18.2-18.7 ms 36.8-37.0 ms
Full, no timing pass (not timed) 1.5-2.0 ms 41.9-42.2 ms 18.1-19.1 ms 38.1-38.2 ms
Off, rerun none 1.5-1.8 ms 40.1-42.2 ms 18.1-19.2 ms 37.6-38.0 ms
  • The effects' GPU cost was timed with timestamp queries on an offscreen pass that redraws every layer unclipped. That is an upper bound, and the extra pass is itself extra GPU work on the shared device.
  • Reduced costs the same as Full: it stills the motion but draws the same layers.
  • Program render time is unchanged. Without the timing pass, Full and Off fall within the run-to-run noise, which is as wide as the gap between two Off runs. The ~0.7 ms gap in the timed runs comes from the timing pass.
  • The UI's ~40 ms frame (about 25 fps) is the same in every mode, so it is not the effects. It predates this PR and looks like the UI waiting on the engine's state lock while the compositor renders (render is ~19 ms of CPU wall time).
  • All of this was measured with temporary instrumentation (timestamp queries, FX_MODE/FX_FORCE overrides), not committed.

Verified by hand on Xvfb, with screenshots: on-air glow; bursts from the Cut button and an F3 hotkey; recording pulse; Health panel and tab alerts; chrome Cut; keycaps; setup gradient; the View > Effects menu. The macOS reduced-motion read passed cargo check and clippy locally. The Windows read is unbuilt: no Windows target is available here.

Alternatives

  • Moving the UI to Rune now: rejected in planning; the Rune spike weighs it.
  • One paint callback per effect, each with its own prepare: rune-fx's prepare submits its own staging upload, so batching every layer into one upload per frame is cheaper.
  • rune-fx's Heat/OneShot timing helpers: f32 seconds lose precision as egui's clock grows; replaced by an f64 Light.

Follow-ups

  • The UI's 25 fps under a live program: profile the UI's wait on the engine's state lock, which every mode shows equally.
  • rune-fx (rune takes no PRs; suggested issue, not filed): Heat and the one-shot helpers take f32 seconds, so precision decays as the host clock grows; an f64 or wrapping time base would avoid each host re-implementing them.
  • Build the Windows reduced-motion read in CI once a Windows job exists.

Review

Codex (gpt-6.1-sol, medium effort), round 1 of session 01a1b63a-f756-74c1-a14a-7ae49c8f14c6, reviewed head 98f7cd412cb646962ea805e6815aeaae96e0a3a7 against origin/main f760ee7: no findings.

Checks: just ci passed on Linux at this head (281 tests); cargo check and clippy -D warnings for moq-studio --all-targets passed on macOS.

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

Quest-Claim: af438d6a1eab3557
@pullfrog

pullfrog Bot commented Nov 7, 2026

Copy link
Copy Markdown

Pullfrog  | Review this ➔

kixelated and others added 3 commits November 7, 2026 05:11
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…abel readable

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	rs/moq-studio/Cargo.toml
#	rs/moq-studio/src/main.rs
#	rs/moq-studio/src/ui.rs
@kixelated kixelated changed the title Effects in the studio UI feat(studio): rune-fx effects in the studio UI Nov 7, 2026
@kixelated
kixelated marked this pull request as ready for review November 7, 2026 13:40
@coderabbitai

coderabbitai Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 27 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8d739c46-d40b-442e-85a8-0a4d35fc27a9
Commits

Reviewing files that changed from the base of the PR and between 9e4ac59 and 2478663.

Files ignored due to path filters: 1

  • Cargo.lock is excluded by !**/*.lock
Files selected for processing: 12

  • Cargo.toml
  • README.md
  • quest/m1/README.md
  • quest/m1/rune-spike.md
  • quest/m1/ui-effects.md
  • rs/moq-studio/Cargo.toml
  • rs/moq-studio/src/engine.rs
  • rs/moq-studio/src/fx.rs
  • rs/moq-studio/src/fx/motion.rs
  • rs/moq-studio/src/main.rs
  • rs/moq-studio/src/setup.rs
  • rs/moq-studio/src/ui.rs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@kixelated kixelated left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Automated review by review (OpenAI)

Reviewed head 98f7cd412cb646962ea805e6815aeaae96e0a3a7 against base f760ee7227519ff79983bb5c5c95b3c1e7db529c.

No actionable findings. The effects remain on the UI renderer rather than the compositor or encode path. I checked callback batching and clipping against pinned Rune/egui sources, take and recording signals, health severity ordering, setup transitions, reduced-motion handling and preference persistence.

Direction: retain the focused rune-fx integration and the user-selectable Full/Reduced/Off settings. No code changes requested.

Verification limits: GitHub-only static review. I read the pinned quest context as source but did not execute quest context, builds, tests, GPU/OS checks or benchmarks. The reported Linux/macOS checks and performance measurements were not independently reproduced; Windows and native multi-viewport behavior remain unverified here. COMMENT only, not approval.

(Written by review (OpenAI))

@kixelated

Copy link
Copy Markdown
Contributor Author

Pick (/quest-complete, maintainer away under /afk, recommended option): ✅ Merge. Check passed, the OpenAI review found nothing, and CodeRabbit skipped on its rate limit. Merged main (#87, #89), with no conflicts.

(Written by Claude Opus 5.5)

@kixelated
kixelated merged commit b87cc76 into main Nov 7, 2026
3 checks passed
@kixelated
kixelated deleted the quest/m1/ui-effects branch November 7, 2026 14:27
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.

1 participant