fret_ui/tree/paint/
entry.rs1use super::super::*;
2
3impl<H: UiHost> UiTree<H> {
4 #[stacksafe::stacksafe]
5 pub fn paint_all(
6 &mut self,
7 app: &mut H,
8 services: &mut dyn UiServices,
9 bounds: Rect,
10 scene: &mut Scene,
11 scale_factor: f32,
12 ) {
13 let trace_paint = tracing::enabled!(tracing::Level::TRACE);
14 let window = self.window;
15 let frame_id = app.frame_id();
16
17 let paint_pass = self.paint_pass.saturating_add(1);
21 self.paint_pass = paint_pass;
22
23 let ((), paint_elapsed) = fret_perf::measure_span(
24 self.debug_enabled,
25 trace_paint,
26 || {
27 tracing::trace_span!(
28 "fret.ui.paint_all",
29 window = ?window,
30 frame_id = frame_id.0,
31 paint_pass,
32 scale_factor,
33 )
34 },
35 || {
36 if let Some(window) = self.window {
37 let frame_id = app.frame_id();
38 app.with_global_mut_untracked(
39 fret_core::WindowFrameClockService::default,
40 |svc, _host| svc.record_frame(window, frame_id),
41 );
42 }
43 if self.debug_enabled {
44 self.begin_debug_frame_if_needed(app.frame_id());
45 self.debug_stats.frame_id = app.frame_id();
46 self.debug_stats.paint_nodes = 0;
47 self.debug_stats.paint_nodes_performed = 0;
48 self.debug_stats.paint_cache_hits = 0;
49 self.debug_stats.paint_cache_misses = 0;
50 self.debug_stats.paint_cache_replayed_ops = 0;
51 self.debug_stats.paint_cache_hit_test_only_replay_allowed = 0;
52 self.debug_stats
53 .paint_cache_hit_test_only_replay_rejected_key_mismatch = 0;
54 self.debug_stats.paint_cache_replay_time = Duration::default();
55 self.debug_stats.paint_cache_bounds_translate_time = Duration::default();
56 self.debug_stats.paint_cache_bounds_translated_nodes = 0;
57 self.debug_stats.paint_record_visual_bounds_time = Duration::default();
58 self.debug_stats.paint_record_visual_bounds_calls = 0;
59 self.debug_stats.paint_cache_key_time = Duration::default();
60 self.debug_stats.paint_cache_hit_check_time = Duration::default();
61 self.debug_stats.paint_widget_time = Duration::default();
62 self.debug_stats.paint_observation_record_time = Duration::default();
63 self.debug_stats.paint_host_widget_observed_models_time = Duration::default();
64 self.debug_stats.paint_host_widget_observed_models_items = 0;
65 self.debug_stats.paint_host_widget_observed_globals_time = Duration::default();
66 self.debug_stats.paint_host_widget_observed_globals_items = 0;
67 self.debug_stats.paint_host_widget_instance_lookup_time = Duration::default();
68 self.debug_stats.paint_host_widget_instance_lookup_calls = 0;
69 self.debug_stats.paint_text_prepare_time = Duration::default();
70 self.debug_stats.paint_text_prepare_calls = 0;
71 self.debug_stats.paint_text_prepare_reason_blob_missing = 0;
72 self.debug_stats.paint_text_prepare_reason_scale_changed = 0;
73 self.debug_stats.paint_text_prepare_reason_text_changed = 0;
74 self.debug_stats.paint_text_prepare_reason_rich_changed = 0;
75 self.debug_stats.paint_text_prepare_reason_style_changed = 0;
76 self.debug_stats.paint_text_prepare_reason_wrap_changed = 0;
77 self.debug_stats.paint_text_prepare_reason_overflow_changed = 0;
78 self.debug_stats.paint_text_prepare_reason_width_changed = 0;
79 self.debug_stats
80 .paint_text_prepare_reason_font_stack_changed = 0;
81 self.debug_paint_widget_exclusive_started = None;
82 self.debug_stats.paint_input_context_time = Duration::default();
83 self.debug_stats.paint_scroll_handle_invalidation_time = Duration::default();
84 self.debug_stats.paint_collect_roots_time = Duration::default();
85 self.debug_stats.paint_publish_text_input_snapshot_time = Duration::default();
86 self.debug_stats.paint_collapse_observations_time = Duration::default();
87 self.debug_stats.view_cache_active = self.view_cache_active();
88 self.debug_stats.focus = self.focus;
89 self.debug_stats.captured = self.captured_for(fret_core::PointerId(0));
90 }
91
92 let focus_is_text_input = self.focus_is_text_input(app);
95 self.set_ime_allowed(app, focus_is_text_input);
96 let input_ctx_started = self.debug_enabled.then(Instant::now);
97 let (active_layers, barrier_root) = self.active_input_layers();
98 let _ = active_layers;
99 let input_ctx = self.current_window_input_context(
100 app,
101 barrier_root.is_some(),
102 focus_is_text_input,
103 );
104 self.publish_window_input_context_snapshot_untracked(app, &input_ctx, true);
105 if let Some(input_ctx_started) = input_ctx_started {
106 self.debug_stats.paint_input_context_time = self
107 .debug_stats
108 .paint_input_context_time
109 .saturating_add(input_ctx_started.elapsed());
110 }
111
112 let (_, scroll_inv_elapsed) = fret_perf::measure(self.debug_enabled, || {
117 self.invalidate_scroll_handle_bindings_for_changed_handles(
118 app,
119 crate::layout_pass::LayoutPassKind::Final,
120 false,
121 true,
122 );
123 });
124 if let Some(scroll_inv_elapsed) = scroll_inv_elapsed {
125 self.debug_stats.paint_scroll_handle_invalidation_time = self
126 .debug_stats
127 .paint_scroll_handle_invalidation_time
128 .saturating_add(scroll_inv_elapsed);
129 }
130
131 let cache_enabled = self.paint_cache_enabled();
132 if cache_enabled {
133 self.paint_cache.begin_frame();
134 } else {
135 self.paint_cache.invalidate_recording();
136 }
137
138 self.scratch_visual_bounds_records.clear();
139
140 let (roots, roots_elapsed) = fret_perf::measure(self.debug_enabled, || {
141 self.visible_layers_in_paint_order()
142 .map(|layer| self.layers[layer].root)
143 .collect::<Vec<NodeId>>()
144 });
145 if let Some(roots_elapsed) = roots_elapsed {
146 self.debug_stats.paint_collect_roots_time = self
147 .debug_stats
148 .paint_collect_roots_time
149 .saturating_add(roots_elapsed);
150 }
151 for root in roots {
152 self.paint(app, services, root, bounds, scene, scale_factor);
153 }
154
155 if let Some(window) = self.window
156 && !self.scratch_visual_bounds_records.is_empty()
157 {
158 let mut records = std::mem::take(&mut self.scratch_visual_bounds_records);
159 let (_, flush_elapsed) = fret_perf::measure(self.debug_enabled, || {
160 crate::elements::with_window_state(app, window, |st| {
161 for (element, visual) in records.drain(..) {
162 if st
163 .current_bounds(element)
164 .is_some_and(|bounds| bounds == visual)
165 {
166 continue;
167 }
168 st.record_visual_bounds(element, visual);
169 }
170 });
171 });
172 self.scratch_visual_bounds_records = records;
173 if let Some(flush_elapsed) = flush_elapsed {
174 self.debug_stats.paint_record_visual_bounds_time = self
175 .debug_stats
176 .paint_record_visual_bounds_time
177 .saturating_add(flush_elapsed);
178 }
179 }
180
181 if let Some(window) = self.window {
184 let (_, text_snapshot_elapsed) = fret_perf::measure(self.debug_enabled, || {
185 let mut next = if focus_is_text_input {
186 if let Some(focus) = self.focus
187 && let Some(snapshot) =
188 crate::declarative::frame::with_element_record_for_node(
189 app,
190 window,
191 focus,
192 |r| match &r.instance {
193 crate::declarative::ElementInstance::TextInputRegion(
194 props,
195 ) => Some(
196 super::super::ui_tree_text_input::text_input_region_platform_text_input_snapshot(props),
197 ),
198 _ => None,
199 },
200 )
201 .flatten()
202 {
203 snapshot
204 } else {
205 self.focus
206 .and_then(|focus| self.nodes.get(focus))
207 .and_then(|n| n.widget.as_ref())
208 .and_then(|w| w.platform_text_input_snapshot())
209 .unwrap_or_else(|| fret_runtime::WindowTextInputSnapshot {
210 focus_is_text_input: true,
211 ..Default::default()
212 })
213 }
214 } else {
215 fret_runtime::WindowTextInputSnapshot::default()
216 };
217 next.focus_is_text_input = focus_is_text_input;
218
219 let needs_update = app
220 .global::<fret_runtime::WindowTextInputSnapshotService>()
221 .and_then(|svc| svc.snapshot(window))
222 .is_none_or(|prev| prev != &next);
223 if needs_update {
224 app.with_global_mut(
225 fret_runtime::WindowTextInputSnapshotService::default,
226 |svc, _app| {
227 svc.set_snapshot(window, next);
228 },
229 );
230 }
231 });
232 if let Some(text_snapshot_elapsed) = text_snapshot_elapsed {
233 self.debug_stats.paint_publish_text_input_snapshot_time = self
234 .debug_stats
235 .paint_publish_text_input_snapshot_time
236 .saturating_add(text_snapshot_elapsed);
237 }
238 }
239
240 if cache_enabled {
241 self.paint_cache.finish_frame();
242 if self.debug_enabled {
243 self.debug_stats.paint_cache_hits = self.paint_cache.hits;
244 self.debug_stats.paint_cache_misses = self.paint_cache.misses;
245 self.debug_stats.paint_cache_replayed_ops = self.paint_cache.replayed_ops;
246 }
247 }
248
249 let (_, collapse_elapsed) = fret_perf::measure(self.debug_enabled, || {
250 self.collapse_paint_observations_to_view_cache_roots_if_needed();
251 });
252 if let Some(collapse_elapsed) = collapse_elapsed {
253 self.debug_stats.paint_collapse_observations_time = self
254 .debug_stats
255 .paint_collapse_observations_time
256 .saturating_add(collapse_elapsed);
257 }
258 },
259 );
260 if let Some(paint_elapsed) = paint_elapsed {
261 self.debug_stats.paint_time = paint_elapsed;
262 }
263 }
264
265 pub fn paint(
266 &mut self,
267 app: &mut H,
268 services: &mut dyn UiServices,
269 root: NodeId,
270 bounds: Rect,
271 scene: &mut Scene,
272 scale_factor: f32,
273 ) {
274 self.paint_node(
275 app,
276 services,
277 root,
278 bounds,
279 scene,
280 scale_factor,
281 crate::tree::paint_style::PaintStyleState::default(),
282 Transform2D::IDENTITY,
283 );
284 }
285
286 #[cfg(test)]
287 pub(crate) fn test_set_paint_cache_allow_hit_test_only_override(value: Option<bool>) {
288 super::set_paint_cache_allow_hit_test_only_for_test(value);
289 }
290}