1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-02 06:40:06 -04:00

Untangle logic-only frames: one frame entry point, one output type

Same fixes as the previous commits (run no egui pass at all when
nothing will be shown), but the mechanisms are shared instead of
duplicated:

* `Context::run_frame(input, show_ui, f)` is the one entry point for
  integrations: one `FramePhase::Logic` (always, outside any pass),
  then one `FramePhase::Ui` per pass (none when `show_ui` is false).
  `Context::run_logic` is now a thin wrapper around it, and the
  logic-outside-of-pass sequencing lives in egui, not in each backend.

* egui itself buffers the input of pass-less frames
  (`ViewportState::pending_raw_input`) and prepends it to the next
  pass. This replaces `EpiIntegration::pending_raw_input` and the web
  backend's `input.raw.append`, so an integration cannot lose input.

* `FullOutput` is now `{ platform_output, viewport_commands,
  pass_output: Option<PassOutput> }`, and `LogicOutput` is gone.
  One-shot viewport commands (imperative) are separated from
  `ViewportOutput` (which viewports should exist - declarative), so
  each backend has exactly one command-handling path, shared by frames
  with and without a pass. `pass_output: None` encodes "no pass ran:
  paint nothing, leave the viewports alone" in the type.

* The glow/wgpu `!show_ui` early-return blocks are gone: a hidden root
  viewport flows through the same tail as a visible one, with the
  paint and viewport-structure steps gated on `pass_output`.

Behavioral fixes that fall out:

* `App::logic` now sees the current window state in visible frames
  too (it was one frame stale outside the hidden path).
* Auto-save keeps working while a window is minimized or occluded.
* Commands sent to a freshly created viewport apply in the same frame.
* The Wayland resize workaround now also covers commands from
  pass-less frames.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Lucas Meurer
2026-08-04 19:09:17 +02:00
parent 82d1c63c03
commit 33c85bd62a
18 changed files with 745 additions and 618 deletions

View File

@@ -28,12 +28,13 @@ pub fn criterion_benchmark(c: &mut Criterion) {
// The most end-to-end benchmark.
c.bench_function("demo_with_tessellate__realistic", |b| {
b.iter(|| {
let mut full_output = ctx.run_ui(RawInput::default(), |ui| {
let full_output = ctx.run_ui(RawInput::default(), |ui| {
demo_windows.ui(ui);
});
ctx.tessellate(full_output.shapes, full_output.pixels_per_point);
let mut pass_output = full_output.pass_output.expect("run_ui always runs a pass");
ctx.tessellate(pass_output.shapes, pass_output.pixels_per_point);
full_output.textures_delta.clear(); // Don't panic on drop with unapplied deltas
pass_output.textures_delta.clear(); // Don't panic on drop with unapplied deltas
});
});
@@ -50,7 +51,10 @@ pub fn criterion_benchmark(c: &mut Criterion) {
demo_windows.ui(ui);
});
c.bench_function("demo_only_tessellate", |b| {
b.iter(|| ctx.tessellate(full_output.shapes.clone(), full_output.pixels_per_point));
b.iter(|| {
let pass_output = full_output.expect_pass();
ctx.tessellate(pass_output.shapes.clone(), pass_output.pixels_per_point)
});
});
full_output.drop_without_applying_deltas();
}

View File

@@ -72,12 +72,13 @@ fn test_egui_e2e() {
const NUM_FRAMES: usize = 5;
for _ in 0..NUM_FRAMES {
let mut full_output = ctx.run_ui(raw_input.clone(), |ui| {
let full_output = ctx.run_ui(raw_input.clone(), |ui| {
demo_windows.ui(ui);
});
let clipped_primitives = ctx.tessellate(full_output.shapes, full_output.pixels_per_point);
let mut pass_output = full_output.pass_output.expect("run_ui always runs a pass");
let clipped_primitives = ctx.tessellate(pass_output.shapes, pass_output.pixels_per_point);
assert!(!clipped_primitives.is_empty());
full_output.textures_delta.clear(); // Don't panic on drop with unapplied deltas
pass_output.textures_delta.clear(); // Don't panic on drop with unapplied deltas
}
}
@@ -92,16 +93,17 @@ fn test_egui_zero_window_size() {
const NUM_FRAMES: usize = 5;
for _ in 0..NUM_FRAMES {
let mut full_output = ctx.run_ui(raw_input.clone(), |ui| {
let full_output = ctx.run_ui(raw_input.clone(), |ui| {
demo_windows.ui(ui);
});
let clipped_primitives = ctx.tessellate(full_output.shapes, full_output.pixels_per_point);
let mut pass_output = full_output.pass_output.expect("run_ui always runs a pass");
let clipped_primitives = ctx.tessellate(pass_output.shapes, pass_output.pixels_per_point);
assert!(
clipped_primitives.is_empty(),
"There should be nothing to show, has at least one primitive with clip_rect: {:?}",
clipped_primitives[0].clip_rect
);
full_output.textures_delta.clear(); // Don't panic on drop with unapplied deltas
pass_output.textures_delta.clear(); // Don't panic on drop with unapplied deltas
}
}