Skip to content

quest(m1/rune-spike): the Rune spike report, stay on egui for now - #98

Merged
kixelated merged 3 commits into
mainfrom
quest/m1/rune-spike
Nov 7, 2026
Merged

kixelated merged 3 commits into
mainfrom
quest/m1/rune-spike

Conversation

@kixelated

@kixelated kixelated commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Problem

quest/m1/rune-spike.md asked whether the studio's UI should move from egui to Rune, by prototyping its hardest part, the Preview and Program monitors (live compositor textures, drag-to-edit items, the Cut button), and reporting frame time against egui, docking, sharing the compositor's wgpu textures, how rune-kit fits, headless testing, and the cost of a pre-release dependency that takes no pull requests. moq-dev/studio#85 (rune-fx inside egui) feeds the verdict.

This PR deletes the quest. The prototype stays off main, on spike/rune-ui (fb395b3).

Recommendation: stay on egui for now

Rune draws the monitors as well as egui does, but moving buys no measurable frame time and costs a fork of Rune today, a hand-built dock, and a dependency that changes about a hundred commits a day. Revisit if Rune gains an "adopt the app's device" API and a dock, or cuts releases. Keep using rune-fx inside egui as moq-dev/studio#85 does.

The prototype

With the rune feature and STUDIO_UI=rune, moq-studio show.toml opens a Rune window instead of the egui one (rs/moq-studio/src/spike.rs, 760 lines, about 100 of them benchmark logging):

  • Monitors: the compositor's preview and program textures, drawn at Rune effect slots (El::effect) by a Compositor hook, sampled in place on the device the compositor renders on. No copy.
  • Drag-to-edit: select, move, and resize the preview scene's items with the egui window's own snap module (hit testing, aspect-keeping resize, smart guides, Shift and Ctrl), committed through the same edit and undo gestures. Selection handles, hover outline, and guides are Rune elements over the picture.
  • Cut and Transition buttons and a scene picker, through the same Controls the egui panels and hotkeys fire.

The headless run below: Break picked, its person dragged and selected, then Cut. The Program monitor still shows Main's frame; see Headless testing.

Rune spike

Findings

Frame time: parity, at the display's rate

Measured as moq-dev/studio#96 (merged) found egui must be: the studio as a Wayland client of headless sway on nixos2 (Intel composites a 1600x1000 output at 60 Hz, the RTX 2070 renders the studio), not Xvfb. One release build (--features rune,openh264) serves both UIs, with #96's temporary egui instrumentation applied. Scene: 1920x1080 at 30 fps, a color, an image, and a looping H.264 clip (NVDEC) in two scenes. Runs alternated egui, egui, Rune, twice; each line is the median of 32 to 34 windows of 60 frames after a 10 s warm-up, and the worst window's max.

UI Load UI frame (worst max) UI CPU per frame UI lock wait per frame Program render Program copy
egui, full dock idle 16.77-16.78 ms (19.6) 1.42-1.48 ms 0.000 ms 0.35-0.36 ms
egui, Preview and Program only idle 16.75-16.77 ms (22.1) 0.60-0.71 ms 0.000 ms 0.28-0.39 ms
Rune idle 16.24-16.27 ms (27.2) 0.47-0.52 ms 0.001 ms 0.26-0.29 ms
egui, full dock recording 16.74-16.78 ms (27.6) 1.02-1.51 ms 0.000 ms 0.24-0.38 ms 0.33-0.35 ms
egui, Preview and Program only recording 16.77-16.78 ms (25.4) 0.68-0.78 ms 0.000 ms 0.32-0.41 ms 0.35-0.36 ms
Rune recording 16.34-16.36 ms (20.8) 0.78-0.85 ms 0.003-0.007 ms 0.37-0.45 ms 0.33-0.34 ms
  • UI CPU is the UI thread's frame minus waiting for a drawable or present: eframe's cpu_usage for egui, Rune's update_ms + encode_ms for Rune. On the same two monitors the two are within 0.2 ms of each other, both well under 1 ms. The full egui dock costs about 0.8 ms more, for fourteen more panels.
  • Frame rate: both draw every refresh. egui paces on the output's 16.7 ms; Rune draws every 16.2-16.4 ms, about 2.5% more frames than the output shows, so it does slightly more work than egui for the same picture. Rune's Linux windows pace on their own timer rather than Wayland frame callbacks; not investigated further.
  • The program is unaffected: render and copy times match across UIs.
  • egui's first round had one window each with a 0.5 s and a 0.7 s frame (idle, full dock and monitors); dropped from the max column above, kept in the logs. Rune had none.
  • Headless (Rune's virtual clock, no present), a scripted scene pick, drag, and Cut cost 0.27 ms CPU per frame at the median and 0.82 ms at p95.

Sharing the compositor's wgpu textures: needs a Rune change

  • Rune always opens its own device; nothing public takes one. The studio's device is opened by moq_video::render::request_device with DMA-BUF import and an exported timeline semaphore for zero-copy capture and encode, which Rune's device lacks, so Rune has to adopt the studio's. sh/rune-spike/rune.patch adds rune::adopt_gpu(Gpu), used by windows and headless runs (about 30 lines). With it, the textures draw in place.
  • Drawing them takes a custom Compositor with its own WGSL, pipeline per target format, bind groups, and Rune's clip maths (about 250 lines), where egui needs register_native_texture (about 30). It handles clipping, isolated layers, and both blend modes correctly, so it is more capable, but every app-drawn texture costs that.
  • On macOS Rune defaults to its own Metal backend (metal-backend), whose device is not wgpu's; sharing would need GpuBackend::Wgpu there. Not built or tested.
  • Compositor::render gets f.device, and wgpu compares devices by id, which can collide across instances (Rune's own comment, hex#982), so the spike cannot reliably detect a missing adoption.

Docking: hand-built

rune-kit has no dock. Its shell is a fixed SplitView (resizable, collapsible sidebar and inspector around a main pane). egui_dock's tabbed, splittable, rearrangeable fifteen-panel layout would be the studio's to build: split panes from PaneState-like drag handles and tab strips from SegmentedControl, with drag-to-rearrange and floating windows from scratch. The prototype lays the two monitors side by side and does not attempt it.

rune-kit fit: good for controls, gaps for an editor

  • Button, SegmentedControl, and Checkbox dropped in with a neutral dark theme and look more finished than egui's defaults; Select, Menu, TextInput, Tree, and Table exist for the other panels.
  • Missing for this UI: a dock (above), a numeric drag field like egui's DragValue (used for durations, sizes, filter amounts), and diagonal resize cursors (Cursor has only column and row resize; the prototype shows a crosshair at corners, or would need CursorImage PNGs).
  • The model is a retained view plus poll: the view re-runs only when poll says what it shows changed. The studio's state lives behind the engine's lock and changes on the program clock, so each panel needs a snapshot to diff (Shown in the prototype). egui's immediate mode needs none. The lock waits stay near zero either way.

Headless testing: works, with the patch, and one gap

  • RUNE_ARGS="--script drag.json --snap out.png --tree tree.json" ran a scripted scene pick, a drag (the item moved and saved to the scene file, snapped), and Cut, then wrote a PNG with live compositor textures and an element tree with keys, text, and rects (Preview: Break, Program: Break, cut to \Break``). egui has nothing comparable in this repo.
  • It needed the patch: unpatched, a headless run opens its own device and the textures cannot be sampled (it panicked on a foreign texture view).
  • Rune's headless clock is virtual while the engine renders on the wall clock, so the PNG's Program monitor still showed the old scene after the tree said Cut had happened. Deterministic snapshots would need the engine on a paused kio::clock.

The pre-release dependency

  • Churn: the repository started 2026-10-22 and has 1,652 commits on dev in 17 days (24 to 273 a day), 476 changelog entries, no releases, and 87 open issues. Apps pin a dev rev.
  • No pull requests: both fixes the spike needed would be issues to wait on:
    • adopt_gpu, above.
    • rune-taffy matches taffy's FlexWrap exhaustively, and moq-scene's blitz (HTML overlays) turns on taffy's flexbox_balance, which adds variants, so rune-taffy does not compile in the studio's graph. The patch adds a wildcard arm. Cargo also resolved taffy 0.15.1 before the spike pinned 0.15.0 to match Rune's lock.
  • Weight: Rune and rune-kit are about 178,000 lines; the spike adds 67 crates to Cargo.lock (accesskit, zbus, symphonia, resvg, among others).
  • Build: Rune links libxkbcommon and libwayland-client at build time; the dev shell provides neither, so the spike linked with LIBRARY_PATH from nixpkgs. A move would add them to the flake.
  • wgpu lockstep: Rune pins ~30.0.1, the same as moq-video today; a wgpu bump in moq would wait on Rune's, and the other way round.
  • feat(studio): rune-fx effects in the studio UI #85's experience agrees: rune-fx needed its own f64 timing because Rune's helpers take f32 seconds, and the issue for it is unfiled, since Rune takes no PRs.

Impact

  • Deletes quest/m1/rune-spike.md and its link in quest/m1/README.md, m1's last child, so m1's ## Required heading goes too.
  • No code on main; public API and wire: none.

Choices made without asking

  • Pinned the spike's Rune to b99c7b0, the rev rune-fx already uses, so one Rune is in the graph.
  • Kept the prototype runnable only with Rune patched locally (sh/rune-spike/setup.sh applies the patch where the root [patch] points), rather than forking Rune on GitHub.
  • Compared Rune against egui in the same build, and against egui docked to only Preview and Program, since the full dock draws fourteen more panels.
  • Built release with fat LTO off for both UIs: with it, the link fails on duplicate assembly symbols from rind 0.1.12 and 0.2.0, both in main's lockfile (see Follow-ups).

Follow-ups

  • If the verdict is to move: plan it with /quest-plan, starting with Rune issues for adopt_gpu and the taffy wildcard, a dock, and a numeric field.
  • moq-dev/moq: main's graph carries rind 0.1.12 (the moq workspace) and 0.2.0 (through rind-rcgen 0.1.14 in moq-tokio), and the fat-LTO release link of moq-studio --features rune,openh264 failed with symbol '.Lrsaz_2k_loop20' is already defined and similar. Not reproduced on a clean main build here, so worth confirming before planning.

Review

Skipped as too trivial, at head 0115574: the diff against main only deletes quest/m1/rune-spike.md and its link and heading in quest/m1/README.md (30 lines, no code). quest check --base origin/main passes (35 documents ok). The prototype on spike/rune-ui is evidence for this report, not for merging, and was not reviewed.

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

Quest-Claim: 4471c34739a6a3ef
@pullfrog

pullfrog Bot commented Nov 7, 2026

Copy link
Copy Markdown

Pullfrog  | Review this ➔

@kixelated kixelated changed the title Rune UI spike quest(m1/rune-spike): the Rune spike report, stay on egui for now Nov 7, 2026
@kixelated
kixelated marked this pull request as ready for review November 7, 2026 18:52
@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 57 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: 34b59a8b-765c-4cf7-be82-527d8299e65d
Commits

Reviewing files that changed from the base of the PR and between 0e09fec and 0115574.

Files selected for processing: 2

  • quest/m1/README.md
  • quest/m1/rune-spike.md
  • 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 01155741b7ad044433d493a600315796316bf1bf against base 0e09fec4b72074315cced19229c1faf89b0b8d5f, with the separate prototype at fb395b35678f8f96654ea87f463738df4e57721c as supporting evidence.

No actionable findings. The report covers the quest's requested comparison and keeps prototype code off main. The prototype's GPU adoption and direct texture bindings support the same-device/no-copy account; drag/edit and Cut use existing studio machinery. Benchmark scripts compare both UIs in one build, including a monitors-only egui layout. The missing dock and deterministic headless-cut limitation are disclosed.

Direction: retaining egui is a proportionate recommendation given the reported frame-time parity and additional integration work; no production migration is proposed here.

Verification limits: GitHub-only static review of both changed documents, prototype integration, GPU patch and benchmark scripts. I did not reproduce timings, run or build either UI, inspect rendered pixels, or validate macOS. Raw benchmark logs and element-tree output were not available in the pinned prototype. Repository guidance and pinned quest context were read as source; quest context was not executed.

(Written by review (OpenAI))

@kixelated

Copy link
Copy Markdown
Contributor Author

Decision (maintainer, /quest-complete): ✅ Stay on egui, and file the Rune issues so a later move is cheaper. Filed: anomalyco/rune#230 (an app can't give Rune its wgpu device) and anomalyco/rune#231 (rune-taffy vs taffy's flexbox_balance). spike/rune-ui stays as a reference branch.

(Written by Claude Opus 5.5)

@kixelated
kixelated merged commit b4a41b6 into main Nov 7, 2026
4 checks passed
@kixelated
kixelated deleted the quest/m1/rune-spike branch November 7, 2026 19:13
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