vor 0.2.1

Cross-platform performance instrumentation with an in-app egui panel and live system and GPU metrics.
Documentation
//! End-to-end check: a live vor panel in a window.
//!
//! Runs some instrumented work each frame, then draws the panel so
//! you can watch the flame chart, frame bars, and system rows update.
//! Use it to confirm a platform backend end to end:
//!
//!   macOS:   cargo run --example live_panel --features viz,mac
//!   NVIDIA:  cargo run --example live_panel --features viz,cuda
//!
//! With `mac` or `cuda` you should also see live `gpu_*` rows; without
//! a GPU backend the panel still shows frame_ms / memory / io.
//!
//! Web is browser-only (needs trunk + eframe's WebRunner); verify the
//! build with `cargo build --target wasm32-unknown-unknown --features web,viz`.

use std::collections::VecDeque;

use eframe::egui;
use vor::viz::{Metric, PanelConfig, PanelState, show};

#[derive(Clone, Copy)]
struct WorkRecord {
    ops: u32,
}

const fn ops_of(r: &WorkRecord) -> f64 {
    r.ops as f64
}
const METRICS: &[Metric<WorkRecord>] = &[Metric::new("ops", ops_of, "ops")
    .as_integer()
    .describe("Synthetic per-frame work (hover demo: caller-defined metric).")];

/// Stand-in workload. `#[all_functions]` profiles each method so the
/// flame chart has named scopes to show.
struct Workload {
    seed: u32,
}

#[vor::all_functions]
impl Workload {
    fn sort(&self, n: u32) -> u32 {
        let mut acc = self.seed ^ n;
        for k in 0..n {
            acc = acc.wrapping_mul(2_654_435_761).wrapping_add(k);
        }
        acc
    }

    fn shade(&self, splats: u32) -> u32 {
        let mut acc = splats;
        for k in 0..splats / 2 {
            acc = acc.rotate_left(1).wrapping_add(k);
        }
        acc
    }
}

struct App {
    state: PanelState,
    history: VecDeque<WorkRecord>,
    workload: Workload,
    frame: u32,
}

impl App {
    fn new() -> Self {
        Self {
            state: PanelState::new(PanelConfig::FRAME_MS),
            history: VecDeque::with_capacity(PanelConfig::FRAME_MS.history_capacity),
            workload: Workload { seed: 0x9E37_79B9 },
            frame: 0,
        }
    }
}

impl eframe::App for App {
    fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
        // While paused, skip the work + tick + push so every graph
        // freezes together on a stable frame. Otherwise:
        // sweep the per-frame load so the bars and flame chart visibly
        // move. Each call gets the same bounded `ops` (~50k..1M cheap
        // iterations); black_box keeps the work from being optimized
        // away. Don't chain — sort returns a huge hash that would make
        // shade loop billions of times.
        if !self.state.is_paused() {
            let ops = 50_000 + (self.frame % 240) * 4_000;
            std::hint::black_box(self.workload.sort(ops));
            std::hint::black_box(self.workload.shade(ops));

            self.state.tick();
            if self.history.len() >= PanelConfig::FRAME_MS.history_capacity {
                self.history.pop_front();
            }
            self.history.push_back(WorkRecord { ops });
            self.frame = self.frame.wrapping_add(1);
        }

        show(ui, &mut self.state, &self.history, METRICS);
        ui.ctx().request_repaint(); // keep the panel live + interactive
    }
}

fn main() -> eframe::Result {
    // Surface eframe / winit / wgpu logs. Run with e.g.
    // `RUST_LOG=debug` to see window + render-surface creation.
    env_logger::init();
    vor::enable();
    // Give the window a size and ask the OS to focus it; without
    // `with_active` a terminal-launched macOS window opens behind and
    // can look like nothing happened.
    let options = eframe::NativeOptions {
        viewport: egui::ViewportBuilder::default()
            .with_title("vor live panel")
            .with_inner_size([960.0, 720.0])
            .with_active(true),
        ..Default::default()
    };
    eframe::run_native(
        "vor live panel",
        options,
        Box::new(|_cc| Ok(Box::new(App::new()))),
    )
}