molgfx-render 0.1.1

The render graph, passes and the engine that drives a frame.
Documentation
//! Structure placement reconciliation against shared physical asset buffers.

use super::GpuScene;
use crate::RenderError;
use crate::scene_gpu::asset::{GpuAsset, GpuAssetIdentity};
use crate::scene_gpu::structure::GpuStructure;
use molgfx_core::Scene;
use molgfx_gpu::Device;
use std::sync::Arc;

#[cfg(test)]
pub(crate) struct TestAssetLayout {
    pub(crate) buffers: [u32; 3],
    pub(crate) offsets: [u64; 3],
    pub(crate) lengths: [u64; 3],
    pub(crate) model: u32,
}

impl<D: Device> GpuScene<D> {
    pub(super) fn reconcile_structures(
        &mut self,
        device: &D,
        queue: &D::Queue,
        scene: &Scene,
    ) -> Result<bool, RenderError> {
        let revision = scene.structure_revision();
        if self.structure_revision == Some(revision) {
            return Ok(false);
        }
        let mut old = std::mem::take(&mut self.structures);
        let mut uploaded = false;
        let arena_revision = self.asset_arena.revision();
        for (handle, placed) in scene.structures() {
            let identity = GpuAssetIdentity::from_placed(placed);
            self.validate_asset_identity(identity)?;
            let asset =
                if let Some(asset) = self.assets.iter().find(|asset| asset.identity == identity) {
                    Arc::clone(asset)
                } else {
                    let asset = Arc::new(GpuAsset::upload(
                        device,
                        queue,
                        &mut self.asset_arena,
                        placed,
                        identity,
                    )?);
                    self.assets.push(Arc::clone(&asset));
                    uploaded = true;
                    asset
                };
            if let Some(index) = old.iter().position(|gpu| gpu.handle == handle) {
                let mut structure = old.swap_remove(index);
                structure.replace_asset(asset);
                self.structures.push(structure);
            } else {
                self.structures.push(GpuStructure::new(handle, asset));
            }
        }
        drop(old);
        self.release_unused_assets()?;
        if self.asset_arena.revision() != arena_revision {
            for structure in &mut self.structures {
                structure.invalidate_asset_binding();
            }
        }
        self.structure_revision = Some(revision);
        Ok(uploaded)
    }

    fn release_unused_assets(&mut self) -> Result<(), RenderError> {
        let mut index = self.assets.len();
        while index > 0 {
            index -= 1;
            if Arc::strong_count(&self.assets[index]) == 1 {
                self.assets[index].release(&mut self.asset_arena)?;
                self.assets.swap_remove(index);
            }
        }
        Ok(())
    }

    pub(super) fn release_all_assets(&mut self) -> Result<(), RenderError> {
        for asset in &self.assets {
            asset.release(&mut self.asset_arena)?;
        }
        self.assets.clear();
        Ok(())
    }

    fn validate_asset_identity(&self, incoming: GpuAssetIdentity) -> Result<(), RenderError> {
        let conflict = self
            .assets
            .iter()
            .map(|asset| asset.identity)
            .find(|resident| resident.conflicts_with(incoming));
        match conflict {
            Some(resident) => Err(RenderError::AssetIdentityCollision {
                dataset: incoming.dataset,
                resident_fingerprint: resident.fingerprint,
                incoming_fingerprint: incoming.fingerprint,
            }),
            None => Ok(()),
        }
    }

    #[cfg(test)]
    pub(crate) fn asset_counts(&self) -> (usize, usize, u64) {
        (
            self.assets.len(),
            self.structures.len(),
            self.assets.iter().map(|asset| asset.resident_bytes()).sum(),
        )
    }

    #[cfg(test)]
    pub(crate) fn asset_layouts<F>(&self, mut identify: F) -> Vec<TestAssetLayout>
    where
        F: FnMut(&D::Buffer) -> u32,
    {
        let arena_id = identify(self.asset_arena.buffer());
        self.structures
            .iter()
            .map(|structure| {
                let ranges = structure.test_ranges();
                let ids = [arena_id; 3];
                let offsets = ranges.map(super::super::asset_arena::AssetRange::offset);
                let lengths = ranges.map(super::super::asset_arena::AssetRange::allocated_bytes);
                let model = match structure.test_model() {
                    Some(buffer) => identify(buffer),
                    None => arena_id,
                };
                TestAssetLayout {
                    buffers: ids,
                    offsets,
                    lengths,
                    model,
                }
            })
            .collect()
    }
}