1use super::super::*;
2
3impl<H: UiHost> UiTree<H> {
4 #[track_caller]
5 pub fn remove_subtree(&mut self, services: &mut dyn UiServices, root: NodeId) -> Vec<NodeId> {
6 #[cfg(feature = "diagnostics")]
7 let remove_record = if self.debug_enabled {
8 let location = std::panic::Location::caller();
9 let pre_exists = self.nodes.contains_key(root);
10 let root_element = self.nodes.get(root).and_then(|n| n.element);
11 let root_parent = self.nodes.get(root).and_then(|n| n.parent);
12 let root_parent_element =
13 root_parent.and_then(|p| self.nodes.get(p).and_then(|n| n.element));
14 let root_root = self.node_root(root);
15 let root_layer = self.node_layer(root);
16 let root_layer_visible =
17 root_layer.and_then(|layer| self.layers.get(layer).map(|l| l.visible));
18 let root_children_len = self
19 .nodes
20 .get(root)
21 .map(|n| n.children.len().min(u32::MAX as usize) as u32)
22 .unwrap_or(0);
23 let root_parent_children_len = root_parent.and_then(|p| {
24 self.nodes
25 .get(p)
26 .map(|n| n.children.len().min(u32::MAX as usize) as u32)
27 });
28 let root_parent_children_contains_root =
29 root_parent.and_then(|p| self.nodes.get(p).map(|n| n.children.contains(&root)));
30 let frame_context = self.debug_remove_subtree_frame_context.remove(&root);
31 let reachable_from_layer_roots = pre_exists
32 && frame_context
33 .as_ref()
34 .map(|ctx| ctx.root_reachable_from_layer_roots)
35 .unwrap_or_else(|| self.debug_is_reachable_from_layer_roots(root));
36 let mut root_path: [u64; 16] = [0u64; 16];
37 let mut root_path_nodes: [Option<NodeId>; 16] = [None; 16];
38 let mut root_path_len: u8 = 0;
39 let mut root_path_truncated = false;
40 let mut current = Some(root);
41 while let Some(id) = current {
42 if (root_path_len as usize) >= root_path.len() {
43 root_path_truncated = true;
44 break;
45 }
46 root_path_nodes[root_path_len as usize] = Some(id);
47 root_path[root_path_len as usize] = id.data().as_ffi();
48 root_path_len = root_path_len.saturating_add(1);
49 current = self.nodes.get(id).and_then(|n| n.parent);
50 }
51 let root_path_edge_len = root_path_len.saturating_sub(1);
52 let mut root_path_edge_ui_contains_child: [u8; 16] = [2u8; 16];
53 for idx in 0..(root_path_edge_len as usize).min(root_path_edge_ui_contains_child.len())
54 {
55 let Some(child) = root_path_nodes[idx] else {
56 continue;
57 };
58 let Some(parent) = root_path_nodes[idx + 1] else {
59 continue;
60 };
61 let contains = self.nodes.get(parent).map(|n| n.children.contains(&child));
62 root_path_edge_ui_contains_child[idx] = match contains {
63 Some(true) => 1,
64 Some(false) => 0,
65 None => 2,
66 };
67 }
68 Some((
69 location.file(),
70 location.line(),
71 location.column(),
72 pre_exists,
73 root_element,
74 root_parent,
75 root_parent_element,
76 root_root,
77 root_layer,
78 root_layer_visible,
79 reachable_from_layer_roots,
80 root_children_len,
81 root_parent_children_len,
82 root_parent_children_contains_root,
83 frame_context,
84 root_path_len,
85 root_path,
86 root_path_truncated,
87 root_path_edge_len,
88 root_path_edge_ui_contains_child,
89 ))
90 } else {
91 None
92 };
93
94 if self.root_to_layer.contains_key(&root) {
95 #[cfg(feature = "diagnostics")]
96 if let Some((
97 file,
98 line,
99 column,
100 _pre_exists,
101 root_element,
102 root_parent,
103 root_parent_element,
104 root_root,
105 root_layer,
106 root_layer_visible,
107 reachable_from_layer_roots,
108 root_children_len,
109 root_parent_children_len,
110 root_parent_children_contains_root,
111 frame_context,
112 root_path_len,
113 root_path,
114 root_path_truncated,
115 root_path_edge_len,
116 root_path_edge_ui_contains_child,
117 )) = remove_record
118 {
119 let root_path_edge_frame_contains_child = frame_context
120 .map(|ctx| ctx.path_edge_frame_contains_child)
121 .unwrap_or([2u8; 16]);
122 let reachable_from_view_cache_roots =
123 frame_context.and_then(|ctx| ctx.root_reachable_from_view_cache_roots);
124 let unreachable_from_liveness_roots = !reachable_from_layer_roots
125 && !matches!(reachable_from_view_cache_roots, Some(true));
126 let trigger_element = frame_context.and_then(|ctx| ctx.trigger_element);
127 let trigger_element_root = frame_context.and_then(|ctx| ctx.trigger_element_root);
128 let trigger_element_in_view_cache_keep_alive =
129 frame_context.and_then(|ctx| ctx.trigger_element_in_view_cache_keep_alive);
130 let trigger_element_listed_under_reuse_root =
131 frame_context.and_then(|ctx| ctx.trigger_element_listed_under_reuse_root);
132 let liveness_layer_roots_len =
133 frame_context.map(|ctx| ctx.liveness_layer_roots_len);
134 let view_cache_reuse_roots_len =
135 frame_context.map(|ctx| ctx.view_cache_reuse_roots_len);
136 let view_cache_reuse_root_nodes_len =
137 frame_context.map(|ctx| ctx.view_cache_reuse_root_nodes_len);
138 let (root_parent_frame_children_len, root_parent_frame_children_contains_root) =
139 frame_context
140 .map(|ctx| {
141 (
142 ctx.parent_frame_children_len,
143 ctx.parent_frame_children_contains_root,
144 )
145 })
146 .unwrap_or((None, None));
147 let (root_frame_instance_present, root_frame_children_len) = frame_context
148 .map(|ctx| {
149 (
150 Some(ctx.root_frame_instance_present),
151 ctx.root_frame_children_len,
152 )
153 })
154 .unwrap_or((None, None));
155 self.debug_removed_subtrees
156 .push(UiDebugRemoveSubtreeRecord {
157 outcome: UiDebugRemoveSubtreeOutcome::SkippedLayerRoot,
158 frame_id: self.debug_stats.frame_id,
159 root,
160 root_element,
161 root_parent,
162 root_parent_element,
163 root_root,
164 root_layer,
165 root_layer_visible,
166 reachable_from_layer_roots,
167 reachable_from_view_cache_roots,
168 unreachable_from_liveness_roots,
169 liveness_layer_roots_len,
170 view_cache_reuse_roots_len,
171 view_cache_reuse_root_nodes_len,
172 trigger_element,
173 trigger_element_root,
174 trigger_element_in_view_cache_keep_alive,
175 trigger_element_listed_under_reuse_root,
176 root_children_len,
177 root_parent_children_len,
178 root_parent_children_contains_root,
179 root_parent_frame_children_len,
180 root_parent_frame_children_contains_root,
181 root_frame_instance_present,
182 root_frame_children_len,
183 root_path_len,
184 root_path,
185 root_path_truncated,
186 root_path_edge_len,
187 root_path_edge_ui_contains_child,
188 root_path_edge_frame_contains_child,
189 removed_nodes: 0,
190 removed_head_len: 0,
191 removed_head: [0u64; 16],
192 removed_tail_len: 0,
193 removed_tail: [0u64; 16],
194 file,
195 line,
196 column,
197 });
198 }
199 return Vec::new();
200 }
201 let mut removed: Vec<NodeId> = Vec::new();
202 self.remove_subtree_inner(services, root, &mut removed);
203
204 #[cfg(feature = "diagnostics")]
205 if let Some((
206 file,
207 line,
208 column,
209 pre_exists,
210 root_element,
211 root_parent,
212 root_parent_element,
213 root_root,
214 root_layer,
215 root_layer_visible,
216 reachable_from_layer_roots,
217 root_children_len,
218 root_parent_children_len,
219 root_parent_children_contains_root,
220 frame_context,
221 root_path_len,
222 root_path,
223 root_path_truncated,
224 root_path_edge_len,
225 root_path_edge_ui_contains_child,
226 )) = remove_record
227 {
228 let root_path_edge_frame_contains_child = frame_context
229 .map(|ctx| ctx.path_edge_frame_contains_child)
230 .unwrap_or([2u8; 16]);
231 let reachable_from_view_cache_roots =
232 frame_context.and_then(|ctx| ctx.root_reachable_from_view_cache_roots);
233 let unreachable_from_liveness_roots = !reachable_from_layer_roots
234 && !matches!(reachable_from_view_cache_roots, Some(true));
235 let trigger_element = frame_context.and_then(|ctx| ctx.trigger_element);
236 let trigger_element_root = frame_context.and_then(|ctx| ctx.trigger_element_root);
237 let trigger_element_in_view_cache_keep_alive =
238 frame_context.and_then(|ctx| ctx.trigger_element_in_view_cache_keep_alive);
239 let trigger_element_listed_under_reuse_root =
240 frame_context.and_then(|ctx| ctx.trigger_element_listed_under_reuse_root);
241 let liveness_layer_roots_len = frame_context.map(|ctx| ctx.liveness_layer_roots_len);
242 let view_cache_reuse_roots_len =
243 frame_context.map(|ctx| ctx.view_cache_reuse_roots_len);
244 let view_cache_reuse_root_nodes_len =
245 frame_context.map(|ctx| ctx.view_cache_reuse_root_nodes_len);
246 let (root_parent_frame_children_len, root_parent_frame_children_contains_root) =
247 frame_context
248 .map(|ctx| {
249 (
250 ctx.parent_frame_children_len,
251 ctx.parent_frame_children_contains_root,
252 )
253 })
254 .unwrap_or((None, None));
255 let (root_frame_instance_present, root_frame_children_len) = frame_context
256 .map(|ctx| {
257 (
258 Some(ctx.root_frame_instance_present),
259 ctx.root_frame_children_len,
260 )
261 })
262 .unwrap_or((None, None));
263 let outcome = if pre_exists {
264 UiDebugRemoveSubtreeOutcome::Removed
265 } else {
266 UiDebugRemoveSubtreeOutcome::RootMissing
267 };
268
269 let mut removed_head: [u64; 16] = [0u64; 16];
270 let mut removed_head_len: u8 = 0;
271 for (idx, node) in removed.iter().take(16).enumerate() {
272 removed_head[idx] = node.data().as_ffi();
273 removed_head_len = removed_head_len.saturating_add(1);
274 }
275
276 let mut removed_tail: [u64; 16] = [0u64; 16];
277 let mut removed_tail_len: u8 = 0;
278 for (idx, node) in removed.iter().rev().take(16).enumerate() {
279 removed_tail[idx] = node.data().as_ffi();
280 removed_tail_len = removed_tail_len.saturating_add(1);
281 }
282
283 self.debug_removed_subtrees
284 .push(UiDebugRemoveSubtreeRecord {
285 outcome,
286 frame_id: self.debug_stats.frame_id,
287 root,
288 root_element,
289 root_parent,
290 root_parent_element,
291 root_root,
292 root_layer,
293 root_layer_visible,
294 reachable_from_layer_roots,
295 reachable_from_view_cache_roots,
296 unreachable_from_liveness_roots,
297 liveness_layer_roots_len,
298 view_cache_reuse_roots_len,
299 view_cache_reuse_root_nodes_len,
300 trigger_element,
301 trigger_element_root,
302 trigger_element_in_view_cache_keep_alive,
303 trigger_element_listed_under_reuse_root,
304 root_children_len,
305 root_parent_children_len,
306 root_parent_children_contains_root,
307 root_parent_frame_children_len,
308 root_parent_frame_children_contains_root,
309 root_frame_instance_present,
310 root_frame_children_len,
311 root_path_len,
312 root_path,
313 root_path_truncated,
314 root_path_edge_len,
315 root_path_edge_ui_contains_child,
316 root_path_edge_frame_contains_child,
317 removed_nodes: removed.len().min(u32::MAX as usize) as u32,
318 removed_head_len,
319 removed_head,
320 removed_tail_len,
321 removed_tail,
322 file,
323 line,
324 column,
325 });
326 }
327
328 removed
329 }
330
331 pub(in crate::tree) fn remove_subtree_inner(
332 &mut self,
333 services: &mut dyn UiServices,
334 root: NodeId,
335 removed: &mut Vec<NodeId>,
336 ) {
337 let mut stack: Vec<(NodeId, bool)> = Vec::new();
341 stack.push((root, false));
342
343 while let Some((node, children_pushed)) = stack.pop() {
344 if self.root_to_layer.contains_key(&node) {
345 continue;
346 }
347 let Some(n) = self.nodes.get(node) else {
348 continue;
349 };
350 let layout_invalidated = n.invalidation.layout;
351 let subtree_layout_dirty_count = n.subtree_layout_dirty_count;
352
353 if !children_pushed {
354 let children = n.children.clone();
355 stack.push((node, true));
356 for child in children {
357 stack.push((child, false));
358 }
359 continue;
360 }
361
362 let parent = self.nodes.get(node).and_then(|n| n.parent);
363 if let Some(parent) = parent
364 && let Some(p) = self.nodes.get_mut(parent)
365 {
366 p.children.retain(|&c| c != node);
367 if self.subtree_layout_dirty_aggregation_enabled() && subtree_layout_dirty_count > 0
368 {
369 let delta = -(subtree_layout_dirty_count.min(i32::MAX as u32) as i32);
370 self.apply_subtree_layout_dirty_delta_to_node_and_ancestors(parent, delta);
371 }
372 }
373
374 if self.focus == Some(node) {
375 self.set_focus_unchecked(None, "mutation/remove: removed focused node");
376 }
377 self.captured.retain(|_, n| *n != node);
378
379 self.cleanup_node_resources(services, node);
380 if let Some(n) = self.nodes.get(node) {
381 self.update_invalidation_counters(n.invalidation, InvalidationFlags::default());
382 }
383 if layout_invalidated {
384 record_layout_invalidation_transition(
385 &mut self.layout_invalidations_count,
386 true,
387 false,
388 );
389 }
390 self.nodes.remove(node);
391 self.observed_in_layout.remove_node(node);
392 self.observed_in_paint.remove_node(node);
393 self.observed_globals_in_layout.remove_node(node);
394 self.observed_globals_in_paint.remove_node(node);
395 removed.push(node);
396 }
397 }
398}