gled-core 2.31.1

The engine of gled without a user interface: inputs, timing, rendering, Art-Net/DMX output and storage
Documentation
//! Where each output buffer slot goes. Resolving a recipient looks up the
//! output device and searches its universes, so it is done once whenever the
//! patch's universes, the routings or the assets change, not for every
//! universe of every frame.

use std::{
    collections::{BTreeSet, HashMap},
    sync::Arc,
};

use gled_gpu::constants::UNIVERSE_SLOTS;
use gled_model::{
    asset::output_device::{
        OutputDevice,
        routing::{OutputRouting, OutputRoutings},
    },
    asset_id::AssetId,
};

use super::Recipient;
use crate::{pipeline::extract_output::OutputConfig, storage::collections::Collections};

/// Where one slot's universe goes.
#[derive(Debug, Clone, PartialEq)]
pub(super) struct Route {
    /// The patch's universe, which its lamps' colour orders belong to.
    pub(super) universe: u16,
    pub(super) routing: OutputRouting,
    pub(super) device: AssetId<OutputDevice>,
    /// `None` when the device is missing or does not list the universe.
    pub(super) recipient: Option<Recipient>,
}

#[derive(Debug, Default)]
pub(super) struct Routes {
    universes: Arc<BTreeSet<u16>>,
    routings: Arc<OutputRoutings>,
    /// The assets' generation the routes were resolved with.
    generation: Option<u64>,
    slots: Vec<Option<Route>>,
    slot_of: HashMap<Recipient, usize>,
}

impl Routes {
    /// Resolves the routes again if the config or the assets changed since.
    pub(super) fn update(&mut self, config: &OutputConfig, collections: &Collections) {
        if self.generation == Some(collections.generation())
            && Arc::ptr_eq(&self.universes, &config.universes)
            && Arc::ptr_eq(&self.routings, &config.routings)
        {
            return;
        }
        self.universes = config.universes.clone();
        self.routings = config.routings.clone();
        self.generation = Some(collections.generation());
        self.slots = config
            .universes
            .iter()
            .take(UNIVERSE_SLOTS as usize)
            .map(|universe| {
                let routing = config.routings.get(universe)?;
                Some(Route {
                    universe: *universe,
                    routing: routing.clone(),
                    device: routing.device?,
                    recipient: routing.recipient(collections),
                })
            })
            .collect();
        self.slot_of = self
            .slots
            .iter()
            .enumerate()
            .filter_map(|(slot, route)| Some((route.as_ref()?.recipient.clone()?, slot)))
            .collect();
    }

    /// The route of output buffer slot `slot`, if its universe has one.
    pub(super) fn slot(&self, slot: usize) -> Option<&Route> {
        self.slots.get(slot)?.as_ref()
    }

    /// The slots there are routes for, routed or not.
    pub(super) fn len(&self) -> usize {
        self.slots.len()
    }

    /// The slot whose universe goes to `recipient`.
    pub(super) fn slot_of(&self, recipient: &Recipient) -> Option<usize> {
        self.slot_of.get(recipient).copied()
    }
}