molgfx-render 0.2.1

The render graph, passes and the engine that drives a frame.
Documentation
use super::tests::{camera, engine, structure};
use molgfx_core::{
    EntityKind, Guide, GuideStyle, InteractionAnchor, InteractionDirection, InteractionEdge,
    InteractionGeometry, InteractionKind, LogicalRow, Scene,
};
use molgfx_math::Vec3;

fn interaction_scene() -> Scene {
    let source = structure();
    let mut scene = Scene::from_structure(&source).unwrap_or_else(|error| panic!("{error}"));
    let Some((owner, _)) = scene.structures().next() else {
        panic!("fixture structure exists")
    };
    for (index, kind) in [
        InteractionKind::HydrogenBond,
        InteractionKind::SaltBridge,
        InteractionKind::PiStacking,
        InteractionKind::Hydrophobic,
        InteractionKind::MetalCoordination,
    ]
    .into_iter()
    .enumerate()
    {
        let y = u16::try_from(index).map_or(f32::from(u16::MAX), f32::from) - 2.0;
        let start = InteractionAnchor::world(Vec3::new(-2.0, y, 0.0))
            .unwrap_or_else(|error| panic!("{error}"));
        let end = InteractionAnchor::world(Vec3::new(2.0, y, 0.0))
            .unwrap_or_else(|error| panic!("{error}"));
        let geometry =
            InteractionGeometry::new(4.0, None).unwrap_or_else(|error| panic!("{error}"));
        let edge = InteractionEdge::new(owner, start, end, kind, geometry, "test:molframe")
            .unwrap_or_else(|error| panic!("{error}"))
            .with_direction(InteractionDirection::Forward);
        scene
            .add_interaction(edge)
            .unwrap_or_else(|error| panic!("{error}"));
    }
    scene
}

#[test]
fn all_interactions_share_one_indirect_draw() {
    let scene = interaction_scene();
    let mut engine = engine();
    engine
        .render(&scene, &camera())
        .unwrap_or_else(|error| panic!("{error}"));
    let draws = engine
        .device
        .log
        .indirect_draws
        .lock()
        .unwrap_or_else(|error| panic!("{error}"));
    assert_eq!(draws.len(), 1);
}

#[test]
fn unchanged_interactions_add_no_steady_frame_uploads() {
    let scene = interaction_scene();
    let mut engine = engine();
    engine
        .render(&scene, &camera())
        .unwrap_or_else(|error| panic!("{error}"));
    let before = engine
        .device
        .log
        .writes
        .lock()
        .map_or_else(|error| panic!("{error}"), |writes| writes.len());
    engine
        .render(&scene, &camera())
        .unwrap_or_else(|error| panic!("{error}"));
    let writes = engine
        .device
        .log
        .writes
        .lock()
        .unwrap_or_else(|error| panic!("{error}"));
    assert_eq!(writes.len() - before, 1, "only frame uniforms change");
}

#[test]
fn interaction_and_guide_row_zero_resolve_distinct_global_namespaces() {
    let source = structure();
    let mut scene = Scene::from_structure(&source).unwrap_or_else(|error| panic!("{error}"));
    let Some((owner, placed)) = scene.structures().next() else {
        panic!("fixture structure exists")
    };
    let dataset = placed.dataset_id();
    let start = Vec3::new(-1.0, 0.0, 0.0);
    let end = Vec3::new(1.0, 0.0, 0.0);
    let geometry = InteractionGeometry::new(2.0, None).unwrap_or_else(|error| panic!("{error}"));
    let edge = InteractionEdge::new(
        owner,
        InteractionAnchor::world(start).unwrap_or_else(|error| panic!("{error}")),
        InteractionAnchor::world(end).unwrap_or_else(|error| panic!("{error}")),
        InteractionKind::HydrogenBond,
        geometry,
        "test:molframe",
    )
    .unwrap_or_else(|error| panic!("{error}"));
    scene
        .add_interaction(edge)
        .unwrap_or_else(|error| panic!("{error}"));
    scene
        .add_guide(
            Guide::new(owner, start, end, GuideStyle::default())
                .unwrap_or_else(|error| panic!("{error}")),
        )
        .unwrap_or_else(|error| panic!("{error}"));

    let mut engine = engine();
    engine
        .render(&scene, &camera())
        .unwrap_or_else(|error| panic!("{error}"));
    let edge_page = engine
        .scene_gpu
        .test_pick_page(dataset, EntityKind::Edge)
        .unwrap_or_else(|| panic!("edge page is resident"));
    let guide_page = engine
        .scene_gpu
        .test_pick_page(dataset, EntityKind::Guide)
        .unwrap_or_else(|| panic!("guide page is resident"));
    assert_ne!(edge_page, guide_page);

    for (page, kind) in [
        (edge_page, EntityKind::Edge),
        (guide_page, EntityKind::Guide),
    ] {
        if let Ok(mut value) = engine.device.log.pick_resident_page.lock() {
            *value = page;
        }
        let pick = engine
            .pick(0, 0)
            .unwrap_or_else(|error| panic!("{error}"))
            .unwrap_or_else(|| panic!("page resolves"));
        let super::PickEntity::Structure(identity) = pick.entity else {
            panic!("global structure identity resolves")
        };
        assert_eq!(identity.dataset(), dataset);
        assert_eq!(identity.kind(), kind);
        assert_eq!(identity.row(), LogicalRow::new(0));
    }
}