Skip to content

Never run an egui pass when nothing will be shown - #8387

Merged
emilk merged 8 commits into
mainfrom
emilk/logic-outside-of-pass
Aug 5, 2026
Merged

Never run an egui pass when nothing will be shown#8387
emilk merged 8 commits into
mainfrom
emilk/logic-outside-of-pass

Conversation

@emilk

@emilk emilk commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Not the most simple or beautiful code, but it works, and makes sense.

What makes it complex: app.logic should still see some input (e.g. what viewports are visible) and emit some output (e.g. "open this link", or "focus and repaint").

TODO

  • test multiple viewports

Clanker says

Instead of teaching egui to skip book-keeping during a pass where no ui is shown, we simply run no pass at all. Then there is nothing to special-case: all ui state is left untouched, and the app finds everything where it left it when the window is shown again.

  • New Context::run_logic(&raw_input, f): ticks app logic without a pass, returning the LogicOutput (platform output + viewport commands) that a pass would otherwise have carried, so e.g. ViewportCommand::Focus still reaches the integration.
  • All three eframe backends (glow, wgpu, web) call run_logic instead of run_ui when the viewport is minimized/occluded (and has no visible descendant viewport) or, on web, when the tab is hidden.
  • App::logic is still called from inside the pass when the window is visible, so it sees the current frame's input.

While hidden, run_logic fills in only the window state (RawInput::viewports / focused), so the app can tell that it is hidden. The ui input (events, time, …) is not interpreted, and is instead given to the next real pass.

emilk and others added 3 commits August 4, 2026 17:23
Add `Context::run_logic`, for ticking app logic without running a pass,
and use it in the native eframe backends when a viewport is minimized or
occluded (and has no visible descendant viewport).

Since no pass runs, all ui state is left untouched: nothing to
special-case inside egui, and the app finds everything where it left it
once the window is visible again.

`App::logic` is now always called outside of the egui pass. Any viewport
commands it sends (e.g. `ViewportCommand::Focus`) come out of
`LogicOutput` when there is no pass to carry them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Same as the native backends: tick `App::logic` via `Context::run_logic`
and leave all ui state alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`Context::run_logic` now takes the new `RawInput`, and copies only
`RawInput::viewports` into the `InputState`, so that `App::logic` can
tell that the window is minimized/occluded. The ui input (events, time)
is left for the next pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

Preview available at https://egui-pr-preview.github.io/pr/8387-emilklogic-outside-of-pass
Note that it might take a couple seconds for the update to show up after the preview_build workflow has completed.

View snapshot changes at kitdiff

@emilk emilk added bug Something is broken eframe Relates to epi and eframe labels Aug 4, 2026
emilk and others added 5 commits August 4, 2026 19:30
* One shared `sleep_if_invisible_or_minimized`, replacing two new
  helpers and two inlined copies.
* `Viewport::process_commands` in each native backend, used both by the
  new no-pass path and by `handle_viewport_output`, replacing the
  commands-only helpers.
* `EpiIntegration::pending_raw_input` needs no `Option`.
* Assert in the test that the child viewport survives.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
That way it sees the current frame's input, like it did before.
While hidden there is no pass, and `Context::run_logic` only fills in
the window state, so that the app can tell that it is hidden; the ui
input is left for the next pass to interpret.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@emilk
emilk marked this pull request as ready for review August 4, 2026 17:56
@emilk
emilk requested a review from lucasmerlin August 4, 2026 17:56

@lucasmerlin lucasmerlin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good!

@emilk
emilk merged commit e37d44a into main Aug 5, 2026
46 checks passed
@emilk
emilk deleted the emilk/logic-outside-of-pass branch August 5, 2026 07:14
wyvernbw pushed a commit to wyvernbw/egui that referenced this pull request Aug 7, 2026
* Closes <emilk#8266>
* Alternative to emilk#8385

Not the most simple or beautiful code, but it works, and makes sense.

What makes it complex: `app.logic` should still see some input (e.g.
what viewports are visible) and emit some output (e.g. "open this link",
or "focus and repaint").

## TODO
* [x] test multiple viewports

## Clanker says
Instead of teaching egui to skip book-keeping during a pass where no ui
is shown, we simply run no pass at all. Then there is nothing to
special-case: all ui state is left untouched, and the app finds
everything where it left it when the window is shown again.

* New `Context::run_logic(&raw_input, f)`: ticks app logic without a
pass, returning the `LogicOutput` (platform output + viewport commands)
that a pass would otherwise have carried, so e.g.
`ViewportCommand::Focus` still reaches the integration.
* All three eframe backends (glow, wgpu, web) call `run_logic` instead
of `run_ui` when the viewport is minimized/occluded (and has no visible
descendant viewport) or, on web, when the tab is hidden.
* `App::logic` is still called from inside the pass when the window is
visible, so it sees the current frame's input.

While hidden, `run_logic` fills in only the window state
(`RawInput::viewports` / `focused`), so the app can tell that it is
hidden. The ui input (events, time, …) is not interpreted, and is
instead given to the next real pass.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something is broken eframe Relates to epi and eframe

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Area::begin/end is called when app is occluded / maximized / minimized (causing side effects/flicker)

2 participants