1#[cfg(any(feature = "inspector", debug_assertions))]
35use crate::InspectorElementPath;
36use crate::{
37 A11ySubtreeBuilder, App, ArenaBox, AvailableSpace, Bounds, Context, DispatchNodeId, ElementId,
38 FocusHandle, InspectorElementId, LayoutId, Pixels, Point, Size, Style, Window,
39 util::FluentBuilder, window::with_element_arena,
40};
41use std::{
42 any::Any,
43 fmt::{self, Debug, Display},
44 mem, panic,
45 sync::Arc,
46};
47
48pub trait Element: 'static + IntoElement {
55 type RequestLayoutState: 'static;
58
59 type PrepaintState: 'static;
62
63 fn id(&self) -> Option<ElementId>;
69
70 fn source_location(&self) -> Option<&'static panic::Location<'static>>;
73
74 fn request_layout(
77 &mut self,
78 id: Option<&GlobalElementId>,
79 window: &mut Window,
80 cx: &mut App,
81 ) -> (LayoutId, Self::RequestLayoutState);
82
83 fn prepaint(
86 &mut self,
87 id: Option<&GlobalElementId>,
88 bounds: Bounds<Pixels>,
89 request_layout: &mut Self::RequestLayoutState,
90 window: &mut Window,
91 cx: &mut App,
92 ) -> Self::PrepaintState;
93
94 fn paint(
97 &mut self,
98 id: Option<&GlobalElementId>,
99 bounds: Bounds<Pixels>,
100 request_layout: &mut Self::RequestLayoutState,
101 prepaint: &mut Self::PrepaintState,
102 window: &mut Window,
103 cx: &mut App,
104 );
105
106 fn a11y_role(&self) -> Option<accesskit::Role> {
113 None
114 }
115
116 fn write_a11y_info(&self, _node: &mut accesskit::Node) {}
121
122 fn a11y_synthetic_children(
132 &mut self,
133 _prepaint: &mut Self::PrepaintState,
134 _builder: &mut A11ySubtreeBuilder,
135 ) {
136 }
137
138 fn into_any(self) -> AnyElement {
140 AnyElement::new(self)
141 }
142}
143
144pub trait IntoElement: Sized {
146 type Element: Element;
149
150 fn into_element(self) -> Self::Element;
152
153 fn into_any_element(self) -> AnyElement {
155 self.into_element().into_any()
156 }
157}
158
159impl<T: IntoElement> FluentBuilder for T {}
160
161pub trait Render: 'static + Sized {
164 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement;
166}
167
168impl Render for Empty {
169 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
170 Empty
171 }
172}
173
174pub trait RenderOnce: 'static {
180 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement;
184}
185
186pub trait ParentElement {
189 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>);
191
192 fn child(mut self, child: impl IntoElement) -> Self
194 where
195 Self: Sized,
196 {
197 self.extend(std::iter::once(child.into_any_element()));
198 self
199 }
200
201 fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self
203 where
204 Self: Sized,
205 {
206 self.extend(children.into_iter().map(|child| child.into_any_element()));
207 self
208 }
209}
210
211#[derive(Clone, Debug)]
219pub struct GlobalElementId {
220 ids: Arc<[ElementId]>,
221 hash: u64,
222}
223
224pub(crate) const EMPTY_PATH_HASH: u64 = 0;
228
229pub(crate) fn extend_path_hash(prefix: u64, id: &ElementId) -> u64 {
231 use std::hash::{Hash, Hasher};
232 let mut hasher = collections::FxHasher::with_seed(prefix as usize);
233 id.hash(&mut hasher);
234 hasher.finish()
235}
236
237impl GlobalElementId {
238 pub(crate) fn new(ids: &[ElementId]) -> Self {
239 let hash = ids.iter().fold(EMPTY_PATH_HASH, extend_path_hash);
240 Self::with_hash(ids, hash)
241 }
242
243 pub(crate) fn with_hash(ids: &[ElementId], hash: u64) -> Self {
245 debug_assert_eq!(hash, ids.iter().fold(EMPTY_PATH_HASH, extend_path_hash));
246 Self {
247 ids: Arc::from(ids),
248 hash,
249 }
250 }
251
252 pub(crate) fn accesskit_node_id(&self) -> accesskit::NodeId {
253 accesskit::NodeId(self.hash)
254 }
255}
256
257impl Default for GlobalElementId {
258 fn default() -> Self {
259 Self::new(&[])
260 }
261}
262
263impl std::ops::Deref for GlobalElementId {
264 type Target = [ElementId];
265
266 fn deref(&self) -> &Self::Target {
267 &self.ids
268 }
269}
270
271impl PartialEq for GlobalElementId {
272 fn eq(&self, other: &Self) -> bool {
273 self.hash == other.hash && (Arc::ptr_eq(&self.ids, &other.ids) || self.ids == other.ids)
274 }
275}
276
277impl Eq for GlobalElementId {}
278
279impl std::hash::Hash for GlobalElementId {
280 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
281 state.write_u64(self.hash);
282 }
283}
284
285impl Display for GlobalElementId {
286 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287 for (i, element_id) in self.ids.iter().enumerate() {
288 if i > 0 {
289 write!(f, ".")?;
290 }
291 write!(f, "{}", element_id)?;
292 }
293 Ok(())
294 }
295}
296
297trait ElementObject {
298 fn inner_element(&mut self) -> &mut dyn Any;
299
300 fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId;
301
302 fn prepaint(&mut self, window: &mut Window, cx: &mut App);
303
304 fn paint(&mut self, window: &mut Window, cx: &mut App);
305
306 fn layout_as_root(
307 &mut self,
308 available_space: Size<AvailableSpace>,
309 window: &mut Window,
310 cx: &mut App,
311 ) -> Size<Pixels>;
312}
313
314pub struct Drawable<E: Element> {
316 pub element: E,
318 phase: ElementDrawPhase<E::RequestLayoutState, E::PrepaintState>,
319}
320
321#[derive(Default)]
322enum ElementDrawPhase<RequestLayoutState, PrepaintState> {
323 #[default]
324 Start,
325 RequestLayout {
326 layout_id: LayoutId,
327 global_id: Option<GlobalElementId>,
328 inspector_id: Option<InspectorElementId>,
329 request_layout: RequestLayoutState,
330 },
331 LayoutComputed {
332 layout_id: LayoutId,
333 global_id: Option<GlobalElementId>,
334 inspector_id: Option<InspectorElementId>,
335 available_space: Size<AvailableSpace>,
336 request_layout: RequestLayoutState,
337 },
338 Prepaint {
339 node_id: DispatchNodeId,
340 global_id: Option<GlobalElementId>,
341 inspector_id: Option<InspectorElementId>,
342 bounds: Bounds<Pixels>,
343 request_layout: RequestLayoutState,
344 prepaint: PrepaintState,
345 },
346 Painted,
347}
348
349impl<E: Element> Drawable<E> {
351 pub(crate) fn new(element: E) -> Self {
352 Drawable {
353 element,
354 phase: ElementDrawPhase::Start,
355 }
356 }
357
358 fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId {
359 match mem::take(&mut self.phase) {
360 ElementDrawPhase::Start => {
361 let global_id = self
362 .element
363 .id()
364 .map(|element_id| prepare_element_id(element_id, window));
365
366 let inspector_id;
367 #[cfg(any(feature = "inspector", debug_assertions))]
368 {
369 inspector_id = if window.inspector_enabled() {
370 self.element
371 .source_location()
372 .map(|source| prepare_inspector_id(source, window))
373 } else {
374 None
375 };
376 }
377 #[cfg(not(any(feature = "inspector", debug_assertions)))]
378 {
379 inspector_id = None;
380 }
381
382 let previous_inspector_id = window.set_inspector_element_id(inspector_id.clone());
383 let (layout_id, request_layout) =
384 self.element.request_layout(global_id.as_ref(), window, cx);
385 window.set_inspector_element_id(previous_inspector_id);
386
387 if global_id.is_some() {
388 window.frame_state.element_id_stack.pop();
389 }
390
391 self.phase = ElementDrawPhase::RequestLayout {
392 layout_id,
393 global_id,
394 inspector_id,
395 request_layout,
396 };
397 layout_id
398 }
399 _ => panic!("must call request_layout only once"),
400 }
401 }
402
403 pub(crate) fn prepaint(&mut self, window: &mut Window, cx: &mut App) {
404 match mem::take(&mut self.phase) {
405 ElementDrawPhase::RequestLayout {
406 layout_id,
407 global_id,
408 inspector_id,
409 mut request_layout,
410 }
411 | ElementDrawPhase::LayoutComputed {
412 layout_id,
413 global_id,
414 inspector_id,
415 mut request_layout,
416 ..
417 } => {
418 if let Some(element_id) = self.element.id() {
419 window.frame_state.element_id_stack.push(element_id);
420 debug_assert_eq!(
421 &**global_id.as_ref().unwrap(),
422 &*window.frame_state.element_id_stack
423 );
424 }
425
426 let bounds = window.layout_bounds(layout_id);
427 let mut pushed_a11y_node = false;
428 if window.core.a11y.is_active() {
429 if let Some(global_id) = global_id.as_ref() {
430 if let Some(role) = self.element.a11y_role() {
431 let node_id = global_id.accesskit_node_id();
432 let mut node = accesskit::Node::new(role);
433 let scale = window.scale_factor();
434 node.set_bounds(accesskit::Rect {
435 x0: (bounds.origin.x.0 * scale) as f64,
436 y0: (bounds.origin.y.0 * scale) as f64,
437 x1: ((bounds.origin.x.0 + bounds.size.width.0) * scale) as f64,
438 y1: ((bounds.origin.y.0 + bounds.size.height.0) * scale) as f64,
439 });
440 self.element.write_a11y_info(&mut node);
441 window.core.a11y.node_bounds.insert(node_id, bounds);
442 pushed_a11y_node = window.core.a11y.nodes.push(node_id, node);
443 #[cfg(debug_assertions)]
444 if pushed_a11y_node {
445 let view = window
446 .core
447 .a11y
448 .view_type_names
449 .get(&window.current_view())
450 .copied();
451 let source_location = self.element.source_location();
452 window.core.a11y.nodes.record_node_info(
453 node_id,
454 crate::window::a11y::debug::NodeDebugInfo {
455 synthetic: false,
456 view,
457 element_id: global_id.last().map(|id| format!("{id:?}")),
458 source_location,
459 },
460 );
461 }
462 }
463 }
464 }
465
466 let node_id = window.frame_state.next_frame.dispatch_tree.push_node();
467 let previous_inspector_id = window.set_inspector_element_id(inspector_id.clone());
468 let mut prepaint = self.element.prepaint(
469 global_id.as_ref(),
470 bounds,
471 &mut request_layout,
472 window,
473 cx,
474 );
475 window.set_inspector_element_id(previous_inspector_id);
476 window.frame_state.next_frame.dispatch_tree.pop_node();
477
478 if pushed_a11y_node {
479 if let Some(global_id) = global_id.as_ref() {
480 #[cfg(debug_assertions)]
481 let creator = crate::window::a11y::debug::NodeCreator {
482 view: window
483 .core
484 .a11y
485 .view_type_names
486 .get(&window.current_view())
487 .copied(),
488 element_id: global_id.last().map(|id| format!("{id:?}")),
489 source_location: self.element.source_location(),
490 };
491 let mut builder = A11ySubtreeBuilder::new(
492 global_id.accesskit_node_id(),
493 &mut window.core.a11y.nodes,
494 );
495 #[cfg(debug_assertions)]
496 {
497 builder = builder.with_creator(creator);
498 }
499 self.element
500 .a11y_synthetic_children(&mut prepaint, &mut builder);
501 }
502 window.core.a11y.nodes.pop();
503 }
504
505 if global_id.is_some() {
506 window.frame_state.element_id_stack.pop();
507 }
508
509 self.phase = ElementDrawPhase::Prepaint {
510 node_id,
511 global_id,
512 inspector_id,
513 bounds,
514 request_layout,
515 prepaint,
516 };
517 }
518 _ => panic!("must call request_layout before prepaint"),
519 }
520 }
521
522 pub(crate) fn paint(
523 &mut self,
524 window: &mut Window,
525 cx: &mut App,
526 ) -> (E::RequestLayoutState, E::PrepaintState) {
527 match mem::take(&mut self.phase) {
528 ElementDrawPhase::Prepaint {
529 node_id,
530 global_id,
531 inspector_id,
532 bounds,
533 mut request_layout,
534 mut prepaint,
535 ..
536 } => {
537 if let Some(element_id) = self.element.id() {
538 window.frame_state.element_id_stack.push(element_id);
539 debug_assert_eq!(
540 &**global_id.as_ref().unwrap(),
541 &*window.frame_state.element_id_stack
542 );
543 }
544
545 window
546 .frame_state
547 .next_frame
548 .dispatch_tree
549 .set_active_node(node_id);
550 let previous_inspector_id = window.set_inspector_element_id(inspector_id);
551 self.element.paint(
552 global_id.as_ref(),
553 bounds,
554 &mut request_layout,
555 &mut prepaint,
556 window,
557 cx,
558 );
559 window.set_inspector_element_id(previous_inspector_id);
560
561 if global_id.is_some() {
562 window.frame_state.element_id_stack.pop();
563 }
564
565 self.phase = ElementDrawPhase::Painted;
566 (request_layout, prepaint)
567 }
568 _ => panic!("must call prepaint before paint"),
569 }
570 }
571
572 pub(crate) fn layout_as_root(
573 &mut self,
574 available_space: Size<AvailableSpace>,
575 window: &mut Window,
576 cx: &mut App,
577 ) -> Size<Pixels> {
578 if matches!(&self.phase, ElementDrawPhase::Start) {
579 self.request_layout(window, cx);
580 }
581
582 let layout_id = match mem::take(&mut self.phase) {
583 ElementDrawPhase::RequestLayout {
584 layout_id,
585 global_id,
586 inspector_id,
587 request_layout,
588 } => {
589 window.compute_layout(layout_id, available_space, cx);
590 self.phase = ElementDrawPhase::LayoutComputed {
591 layout_id,
592 global_id,
593 inspector_id,
594 available_space,
595 request_layout,
596 };
597 layout_id
598 }
599 ElementDrawPhase::LayoutComputed {
600 layout_id,
601 global_id,
602 inspector_id,
603 available_space: prev_available_space,
604 request_layout,
605 } => {
606 if available_space != prev_available_space {
607 window.compute_layout(layout_id, available_space, cx);
608 }
609 self.phase = ElementDrawPhase::LayoutComputed {
610 layout_id,
611 global_id,
612 inspector_id,
613 available_space,
614 request_layout,
615 };
616 layout_id
617 }
618 _ => panic!("cannot measure after painting"),
619 };
620
621 window.layout_bounds(layout_id).size
622 }
623}
624
625impl<E> ElementObject for Drawable<E>
626where
627 E: Element,
628 E::RequestLayoutState: 'static,
629{
630 fn inner_element(&mut self) -> &mut dyn Any {
631 &mut self.element
632 }
633
634 #[inline]
635 fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId {
636 Drawable::request_layout(self, window, cx)
637 }
638
639 #[inline]
640 fn prepaint(&mut self, window: &mut Window, cx: &mut App) {
641 Drawable::prepaint(self, window, cx);
642 }
643
644 #[inline]
645 fn paint(&mut self, window: &mut Window, cx: &mut App) {
646 Drawable::paint(self, window, cx);
647 }
648
649 #[inline]
650 fn layout_as_root(
651 &mut self,
652 available_space: Size<AvailableSpace>,
653 window: &mut Window,
654 cx: &mut App,
655 ) -> Size<Pixels> {
656 Drawable::layout_as_root(self, available_space, window, cx)
657 }
658}
659
660pub struct AnyElement(ArenaBox<dyn ElementObject>);
662
663impl AnyElement {
664 pub(crate) fn new<E>(element: E) -> Self
665 where
666 E: 'static + Element,
667 E::RequestLayoutState: Any,
668 {
669 let element = with_element_arena(|arena| arena.alloc(|| Drawable::new(element)))
670 .map(|element| element as &mut dyn ElementObject);
671 AnyElement(element)
672 }
673
674 pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
676 self.0.inner_element().downcast_mut::<T>()
677 }
678
679 pub fn request_layout(&mut self, window: &mut Window, cx: &mut App) -> LayoutId {
682 self.0.request_layout(window, cx)
683 }
684
685 pub fn prepaint(&mut self, window: &mut Window, cx: &mut App) -> Option<FocusHandle> {
688 let focus_assigned = window.frame_state.next_frame.focus.is_some();
689
690 self.0.prepaint(window, cx);
691
692 if !focus_assigned && let Some(focus_id) = window.frame_state.next_frame.focus {
693 return FocusHandle::for_id(focus_id, &cx.focus_handles);
694 }
695
696 None
697 }
698
699 pub fn paint(&mut self, window: &mut Window, cx: &mut App) {
701 self.0.paint(window, cx);
702 }
703
704 pub fn layout_as_root(
706 &mut self,
707 available_space: Size<AvailableSpace>,
708 window: &mut Window,
709 cx: &mut App,
710 ) -> Size<Pixels> {
711 self.0.layout_as_root(available_space, window, cx)
712 }
713
714 pub fn prepaint_at(
717 &mut self,
718 origin: Point<Pixels>,
719 window: &mut Window,
720 cx: &mut App,
721 ) -> Option<FocusHandle> {
722 window.with_absolute_element_offset(origin, |window| self.prepaint(window, cx))
723 }
724
725 pub fn prepaint_as_root(
728 &mut self,
729 origin: Point<Pixels>,
730 available_space: Size<AvailableSpace>,
731 window: &mut Window,
732 cx: &mut App,
733 ) -> Option<FocusHandle> {
734 self.layout_as_root(available_space, window, cx);
735 window.with_absolute_element_offset(origin, |window| self.prepaint(window, cx))
736 }
737}
738
739impl Element for AnyElement {
740 type RequestLayoutState = ();
741 type PrepaintState = ();
742
743 fn id(&self) -> Option<ElementId> {
744 None
745 }
746
747 fn source_location(&self) -> Option<&'static panic::Location<'static>> {
748 None
749 }
750
751 fn request_layout(
752 &mut self,
753 _: Option<&GlobalElementId>,
754 window: &mut Window,
755 cx: &mut App,
756 ) -> (LayoutId, Self::RequestLayoutState) {
757 let layout_id = self.request_layout(window, cx);
758 (layout_id, ())
759 }
760
761 fn prepaint(
762 &mut self,
763 _: Option<&GlobalElementId>,
764 _: Bounds<Pixels>,
765 _: &mut Self::RequestLayoutState,
766 window: &mut Window,
767 cx: &mut App,
768 ) {
769 self.prepaint(window, cx);
770 }
771
772 fn paint(
773 &mut self,
774 _: Option<&GlobalElementId>,
775 _: Bounds<Pixels>,
776 _: &mut Self::RequestLayoutState,
777 _: &mut Self::PrepaintState,
778 window: &mut Window,
779 cx: &mut App,
780 ) {
781 self.paint(window, cx);
782 }
783}
784
785impl IntoElement for AnyElement {
786 type Element = Self;
787
788 fn into_element(self) -> Self::Element {
789 self
790 }
791
792 fn into_any_element(self) -> AnyElement {
793 self
794 }
795}
796
797pub struct Empty;
799
800impl IntoElement for Empty {
801 type Element = Self;
802
803 fn into_element(self) -> Self::Element {
804 self
805 }
806}
807
808impl Element for Empty {
809 type RequestLayoutState = ();
810 type PrepaintState = ();
811
812 fn id(&self) -> Option<ElementId> {
813 None
814 }
815
816 fn source_location(&self) -> Option<&'static panic::Location<'static>> {
817 None
818 }
819
820 fn request_layout(
821 &mut self,
822 _id: Option<&GlobalElementId>,
823 window: &mut Window,
824 cx: &mut App,
825 ) -> (LayoutId, Self::RequestLayoutState) {
826 (
827 window.request_layout(
828 Style {
829 display: crate::Display::None,
830 ..Default::default()
831 },
832 None,
833 cx,
834 ),
835 (),
836 )
837 }
838
839 fn prepaint(
840 &mut self,
841 _id: Option<&GlobalElementId>,
842 _bounds: Bounds<Pixels>,
843 _state: &mut Self::RequestLayoutState,
844 _window: &mut Window,
845 _cx: &mut App,
846 ) {
847 }
848
849 fn paint(
850 &mut self,
851 _id: Option<&GlobalElementId>,
852 _bounds: Bounds<Pixels>,
853 _request_layout: &mut Self::RequestLayoutState,
854 _prepaint: &mut Self::PrepaintState,
855 _window: &mut Window,
856 _cx: &mut App,
857 ) {
858 }
859}
860
861#[inline(never)]
862fn prepare_element_id(element_id: ElementId, window: &mut Window) -> GlobalElementId {
863 window.frame_state.element_id_stack.push(element_id);
864 window.frame_state.element_id_stack.global_id()
865}
866
867#[cfg(any(feature = "inspector", debug_assertions))]
868#[inline(never)]
869fn prepare_inspector_id(
870 source: &'static panic::Location<'static>,
871 window: &mut Window,
872) -> InspectorElementId {
873 let path = InspectorElementPath {
874 global_id: window.frame_state.element_id_stack.global_id(),
875 source_location: source,
876 };
877 window.build_inspector_element_id(path)
878}
879
880#[cfg(test)]
881mod global_element_id_tests {
882 use super::*;
883 use std::hash::{Hash, Hasher};
884
885 fn path(depth: usize, leaf: u64) -> Vec<ElementId> {
886 let mut ids: Vec<ElementId> = (0..depth as u64)
887 .map(|level| ElementId::NamedInteger("some-container-element".into(), level))
888 .collect();
889 ids.push(ElementId::NamedInteger("leaf".into(), leaf));
890 ids
891 }
892
893 fn fx_hash(value: &impl Hash) -> u64 {
894 let mut hasher = collections::FxHasher::default();
895 value.hash(&mut hasher);
896 hasher.finish()
897 }
898
899 #[test]
900 fn equal_paths_are_equal_and_hash_alike() {
901 let a = GlobalElementId::new(&path(8, 1));
902 let b = GlobalElementId::new(&path(8, 1));
903 let c = GlobalElementId::new(&path(8, 2));
904 let shallower = GlobalElementId::new(&path(7, 1));
905 assert_eq!(a, b);
906 assert_eq!(fx_hash(&a), fx_hash(&b));
907 assert_eq!(a.accesskit_node_id(), b.accesskit_node_id());
908 assert_ne!(a, c);
909 assert_ne!(a, shallower);
910 assert_ne!(fx_hash(&a), fx_hash(&c));
911 assert_eq!(&*a, path(8, 1).as_slice());
912 assert_eq!(a.to_string(), b.to_string());
913 assert_eq!(GlobalElementId::default(), GlobalElementId::new(&[]));
914 }
915
916 struct DeepStatefulTree {
919 items: usize,
920 depth: usize,
921 }
922
923 impl crate::Render for DeepStatefulTree {
924 fn render(&mut self, _: &mut Window, _: &mut crate::Context<Self>) -> impl IntoElement {
925 use crate::{InteractiveElement as _, ParentElement as _, Styled as _, div, px};
926 let depth = self.depth;
927 div()
928 .flex()
929 .flex_wrap()
930 .size_full()
931 .children((0..self.items).map(move |item| {
932 let mut element = div().id(("leaf", item as u64)).size(px(2.));
933 for level in (0..depth).rev() {
934 element = div()
935 .id(("some-container-element", level as u64))
936 .child(element);
937 }
938 element.id(("item", item as u64)).size(px(4.))
939 }))
940 }
941 }
942
943 #[crate::test]
948 #[ignore = "prints timings; run by hand"]
949 fn frame_cost(cx: &mut crate::TestAppContext) {
950 use crate::AppContext as _;
951 for (items, depth) in [(500, 4), (500, 12), (2000, 12)] {
952 let window = cx.add_window(move |_, _| DeepStatefulTree { items, depth });
953 let window = crate::AnyWindowHandle::from(window);
954 let mut frame = |cx: &mut crate::TestAppContext| {
955 cx.update_window(window, |_, window, cx| {
956 window.refresh();
957 let started = std::time::Instant::now();
958 window.draw(cx).clear(cx);
959 started.elapsed()
960 })
961 .unwrap()
962 };
963 for _ in 0..5 {
964 frame(cx);
965 }
966 let mut samples: Vec<_> = (0..40).map(|_| frame(cx)).collect();
967 samples.sort();
968 let median = samples[samples.len() / 2];
969 let stateful = items * (depth + 2);
970 eprintln!(
971 "{items} items x {depth} deep ({stateful} stateful elements): median frame {:.2} ms, {:.0} ns per stateful element",
972 median.as_secs_f64() * 1e3,
973 median.as_nanos() as f64 / stateful as f64,
974 );
975 }
976 }
977
978 #[test]
980 #[ignore = "prints timings; run by hand"]
981 fn hash_cost() {
982 let ids = path(11, 7);
983 let id = GlobalElementId::new(&ids);
984 let n = 1_000_000u64;
985 let mut acc = 0u64;
986 let started = std::time::Instant::now();
987 for _ in 0..n {
988 acc ^= fx_hash(&std::hint::black_box(ids.as_slice()));
989 }
990 let walk = started.elapsed();
991 let started = std::time::Instant::now();
992 for _ in 0..n {
993 acc ^= fx_hash(std::hint::black_box(&id));
994 }
995 let cached = started.elapsed();
996 let started = std::time::Instant::now();
997 for _ in 0..n {
998 acc ^= GlobalElementId::new(std::hint::black_box(&ids)).hash;
999 }
1000 let build = started.elapsed();
1001 eprintln!(
1002 "12-deep path: hash by walking ids {:.1} ns, cached {:.1} ns, build (clone + hash once) {:.1} ns [{acc}]",
1003 walk.as_nanos() as f64 / n as f64,
1004 cached.as_nanos() as f64 / n as f64,
1005 build.as_nanos() as f64 / n as f64,
1006 );
1007 }
1008}