Skip to main content

gpui_authoring/window/
frame_pipeline.rs

1//! The phases one frame goes through, as a seam.
2//!
3//! A window renders by handing its frame to a [`FramePipeline`]. The default,
4//! [`StandardImmediatePipeline`], re-evaluates the view tree, lays it out and
5//! paints what it finds — the immediate-mode loop described in the
6//! [authoring guide](crate::_authoring). Installing a different one is how a
7//! window renders some other way without knowing about it; see the
8//! [frame pipeline guide](crate::_frame_pipeline).
9//!
10//! Every phase falls back to the `Window` method that implements it, so a
11//! pipeline can override one phase and leave the rest alone.
12
13use crate::{
14    App, ArenaClearNeeded, FocusId, PreparedRoots, Window, WindowMetrics, window::ElementArenaScope,
15};
16
17/// The phases of drawing one frame, in the order [`FramePipeline::draw`] runs
18/// them.
19///
20/// A frame starts with [`should_render`](Self::should_render), which decides
21/// whether the frame's work happens at all. The rest are the passes it is made
22/// of, and each one falls back to the [`Window`] method implementing it, so a
23/// pipeline overrides only what it wants to change and a pass that is not
24/// overridden behaves exactly as [`StandardImmediatePipeline`] does.
25///
26/// See the [frame pipeline guide](crate::_frame_pipeline) for what each pass is
27/// for, how far a pipeline outside this crate can go, and how to install one.
28pub trait FramePipeline: 'static {
29    /// Whether the frame the application is about to draw should be drawn.
30    ///
31    /// Defaults to drawing it. Answering `false` leaves the window exactly as it
32    /// was — no roots gathered, nothing laid out, nothing painted — and leaves the
33    /// work the frame would have done pending, so the next frame draws it. This is
34    /// where a pipeline paces frames: dropping the ones the display cannot keep up
35    /// with, or coalescing several invalidations into one frame.
36    ///
37    /// A window that stays dirty keeps asking, so answer `false` to defer a frame
38    /// rather than to drop it forever, and pace using the `metrics` handed in
39    /// rather than ignoring them.
40    ///
41    /// `is_dirty` says whether something changed since the last frame, as opposed
42    /// to the platform asking for one on its own. Two frames are drawn whatever
43    /// this returns: a window's first frame, and one forced after the GPU device
44    /// was lost, whose cached content may not be replayable.
45    fn should_render(&mut self, is_dirty: bool, _metrics: &WindowMetrics) -> bool {
46        is_dirty
47    }
48
49    /// Opens the frame: samples the platform window, resets the scratch state
50    /// the frame rebuilds, and takes ownership of the invalidations this frame
51    /// owes.
52    fn begin_frame(&mut self, window: &mut Window<'_>, cx: &mut App) {
53        window.begin_frame(cx);
54    }
55
56    /// Gathers the roots this frame will draw.
57    ///
58    /// The roots are not laid out yet, so this is where a pipeline can decide what
59    /// the frame is made of before any of it is measured.
60    fn evaluate_roots(&mut self, window: &mut Window<'_>, cx: &mut App) -> PreparedRoots {
61        window.evaluate_roots(cx)
62    }
63
64    /// Lays out and prepaints `roots`, then hit tests the pointer against what
65    /// they registered.
66    ///
67    /// Returning from here means the frame's geometry is settled and its hitboxes
68    /// are registered, so a pipeline can inspect or replace the roots before they
69    /// paint.
70    fn layout_roots(&mut self, window: &mut Window<'_>, roots: &mut PreparedRoots, cx: &mut App) {
71        window.layout_roots(roots, cx);
72    }
73
74    /// Paints `roots`, in the order they stack.
75    fn paint_roots(&mut self, window: &mut Window<'_>, roots: PreparedRoots, cx: &mut App) {
76        window.paint_roots(roots, cx);
77    }
78
79    /// Lays out and paints the window's roots: the window's own view tree, plus
80    /// a prompt, drag image or tooltip if the frame has one.
81    ///
82    /// Runs this pipeline's [evaluate](Self::evaluate_roots),
83    /// [layout](Self::layout_roots) and [paint](Self::paint_roots) passes in
84    /// sequence, so overriding one of those is enough to intervene between them.
85    /// [`Window::draw_roots`] is the same three passes with no pipeline in the
86    /// way.
87    fn draw_roots(&mut self, window: &mut Window<'_>, cx: &mut App) {
88        let mut roots = self.evaluate_roots(window, cx);
89        self.layout_roots(window, &mut roots, cx);
90        self.paint_roots(window, roots, cx);
91    }
92
93    /// Closes the painted frame: records the views it touched and hands the
94    /// platform the input handler the frame asked for.
95    fn finish_frame(&mut self, window: &mut Window<'_>, cx: &mut App) {
96        window.finish_frame(cx);
97    }
98
99    /// Retires the painted frame, swaps it in, and dispatches the focus changes
100    /// the swap produced.
101    ///
102    /// Returns the focus that was current before the listeners ran: they may
103    /// move it, and the caller has to tell those moves apart from its own.
104    fn complete_frame(&mut self, window: &mut Window<'_>, cx: &mut App) -> Option<FocusId> {
105        window.complete_frame(cx)
106    }
107
108    /// Closes the frame out and marks it ready to present.
109    fn end_frame(
110        &mut self,
111        window: &mut Window<'_>,
112        cx: &mut App,
113        focus_before_listeners: Option<FocusId>,
114    ) {
115        window.end_frame(cx, focus_before_listeners);
116    }
117
118    /// Draws one frame by running the phases above in order.
119    ///
120    /// Override this to drive the phases differently, or override a single phase
121    /// to change what happens inside it.
122    fn draw(&mut self, window: &mut Window<'_>, cx: &mut App) -> ArenaClearNeeded {
123        // Drain every draw in profiler builds so a previous frame's
124        // first-invalidation timestamp can't be attributed to this one.
125        #[cfg(feature = "profiler")]
126        let frame_dirty = window.core.invalidator.take_frame_dirty();
127        #[cfg(feature = "profiler")]
128        window.core.window_profiler.begin_draw();
129
130        // Set up the per-App arena for element allocation during this draw.
131        // This ensures that multiple test Apps have isolated arenas.
132        let arena_scope = ElementArenaScope::enter(&cx.element_arena);
133
134        self.begin_frame(window, cx);
135        window.restore_input_handler();
136        if !cx.mode.skip_drawing() {
137            self.draw_roots(window, cx);
138            #[cfg(feature = "profiler")]
139            {
140                let viewport_size = window.core.viewport_size;
141                let scale_factor = window.scale_factor();
142                window.core.debug_frame_overlay.paint(
143                    &mut window.frame_state.next_frame.scene,
144                    viewport_size,
145                    scale_factor,
146                );
147            }
148        }
149        self.finish_frame(window, cx);
150        let focus_before_listeners = self.complete_frame(window, cx);
151        self.end_frame(window, cx, focus_before_listeners);
152
153        #[cfg(feature = "profiler")]
154        {
155            let draw_duration = window
156                .core
157                .window_profiler
158                .end_draw(frame_dirty.dirty_at, frame_dirty.invalidations);
159            window.core.debug_frame_overlay.record_frame(draw_duration);
160        }
161
162        // Exit the scope to obtain the arena-clear token this draw owes; the
163        // scope's teardown itself happens in `ElementArenaScope::drop`.
164        arena_scope.exit(&cx.element_arena)
165    }
166}
167
168/// The pipeline a window draws with unless another one is installed.
169///
170/// Draws every frame from scratch: the view tree is re-evaluated, laid out and
171/// painted, so what is on screen is always a function of current state.
172pub struct StandardImmediatePipeline;
173
174impl FramePipeline for StandardImmediatePipeline {}