gled-ui 2.33.0

gled's egui interface, shared by the desktop app and the browser
Documentation
//! What keeping the interface's project in step with the engine's costs per
//! frame with a large project: ~500 fixtures and a full scene grid. Run with
//! `cargo test -p gled-ui project_edits_bench -- --ignored --nocapture`.

use std::{
    sync::Arc,
    time::{Duration, Instant},
};

use gled_model::{
    asset::{
        project::Project,
        scene::{effect::Effect, grid::GridLocation, instance::SceneInstance},
    },
    collection::Collections,
    patching::{
        fixture::{Fixture, LayoutPosition},
        layout::LayoutRef,
    },
};

use super::project_edits;

const FRAMES: usize = 100;

fn large_project() -> Project {
    let mut project = Project::default();
    project.set_grid_size(8, 6);
    for index in 0..500 {
        project.patch.add_fixture(
            Fixture::new(format!("bar {index}")),
            LayoutPosition::default(),
        );
    }
    for _ in 0..3 {
        let layout = project.patch.add_layout();
        let ids: Vec<_> = project.patch.topology.fixtures.keys().copied().collect();
        for id in ids {
            project.patch.add_member(LayoutRef::Layout(layout), id);
        }
    }
    for row in 0..6 {
        for col in 0..8 {
            let mut instance =
                SceneInstance::from_scene_id(Default::default(), &Collections::default());
            for _ in 0..3 {
                instance.scene.effects.push(Effect::default());
            }
            project.add_scene_instance(GridLocation::new(col, row), instance);
        }
    }
    project
}

fn median(mut times: Vec<Duration>) -> Duration {
    times.sort();
    times[times.len() / 2]
}

/// A frame nothing was edited in and the engine's project did not change:
/// `App::send_local_changes` and the project check of
/// `App::receive_engine_state`, which compares only when the engine's
/// project changed or edits were sent.
#[test]
#[ignore = "a measurement, not a test"]
fn project_edits_bench() {
    let edited = Some(large_project());
    let engine = edited.clone().map(Arc::new);
    let mut confirmed = edited.clone();
    let (mut edits_sent, changes, mut compared) = (false, 1_u64, 0_u64);
    let frame = median(
        (0..FRAMES)
            .map(|_| {
                let start = Instant::now();
                let actions = project_edits(&confirmed, &edited, false);
                if !actions.is_empty() {
                    confirmed = edited.clone();
                    edits_sent = true;
                }
                std::hint::black_box(actions);
                let may_differ = changes != compared || edits_sent;
                (compared, edits_sent) = (changes, false);
                std::hint::black_box(may_differ && engine.as_deref() != confirmed.as_ref());
                start.elapsed()
            })
            .collect(),
    );
    println!("project sync, nothing edited (median): {frame:?} per frame");
}