//! (d) of IRIS_TODO.md's "Benchmarks" item: 1,000 image rows, checking that //! standalone-image bind-group *creation* -- a real `wgpu` resource, unlike //! the counters in `benches/message_lazy_span.rs` -- goes to zero once every //! image has loaded. This needs an actual `wgpu` device (`GpuTextures`, //! `UiRenderNode`), so unlike the rest of the suite it cannot run as a //! plain binary; run it through `iris/run-headless.sh bench_images`, which //! gives it a real (headless, GPU-accelerated) compositor and surface. See //! `run-bench.sh` for the wrapper that greps its output into one line. //! //! Each `RedrawRequested` prints the frame number and //! `UiRenderNode::take_image_bind_group_creates()` for that frame, then //! requests another redraw (nothing else marks the scene dirty, so without //! this the app would only ever draw once). The first frame is expected to //! report 1,000 (one create per image, on first load); the steady state //! IRIS_TODO.md asks this scenario to prove is every frame after settling //! down to 0. //! //! After `SETTLE_FRAMES` it appends one *new* image row (a transcript //! receiving one more message) and keeps counting -- a chat transcript's //! real access pattern is "one more image arrives," not "reload the whole //! list," so the steady-state question that actually matters is the //! *incremental* cost of that one append, not just whether an untouched //! scene costs zero. It exits after `FRAMES`. use iris::prelude::*; const ROWS: usize = 1000; const SETTLE_FRAMES: usize = 4; const FRAMES: usize = 6; #[derive(DefaultUiState)] struct State { ui_state: DefaultUiState, span: WeakWidget, frame: usize, appended: bool, } impl DefaultAppState for State { fn new( mut ui_state: DefaultUiState, rsc: &mut DefaultRsc, _: Proxy, ) -> Self { let mut span = Span::empty(Dir::DOWN); for _ in 0..ROWS { let img = image::DynamicImage::new_rgba8(32, 32); let widget = image::>(img)(rsc); let widget = rsc.ui.widgets.add_strong(widget); span.push(widget.any()); } let span = rsc.ui.widgets.add_strong(span); let span_weak = span.weak(); let root = rsc .ui .widgets .add_strong(ScrollArea::new(span.any(), Axis::Y, Pin::End)); ui_state.set_root(root.any()); Self { ui_state, span: span_weak, frame: 0, appended: false, } } fn window_event( &mut self, event: winit::event::WindowEvent, rsc: &mut DefaultRsc, _render: &mut UiRenderState, ) { if !matches!(event, winit::event::WindowEvent::RedrawRequested) { return; } self.frame += 1; let creates = self.ui_state.renderer.ui.take_image_bind_group_creates(); println!( "BENCH_IMAGES frame={} bind_group_creates={creates}", self.frame ); if self.frame == SETTLE_FRAMES && !self.appended { self.appended = true; let img = image::DynamicImage::new_rgba8(32, 32); let widget = image::>(img)(rsc); let widget = rsc.ui.widgets.add_strong(widget); rsc.ui .widgets .get_mut(&self.span) .unwrap() .push(widget.any()); println!("BENCH_IMAGES appended one image after settling"); } if self.frame < FRAMES { self.ui_state.window.request_redraw(); } else { std::process::exit(0); } } } fn main() { DefaultApp::::run(); }