From 9d1f0000b840ffaf29ceced04cb05c44ddce59f9 Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Tue, 2 Jul 2024 10:10:12 +0200 Subject: [PATCH] scene: Extrace dependency resolver --- renderer/src/scene/dependency_resolver.rs | 87 ++++++++++++++++++++++ renderer/src/scene/mod.rs | 88 +---------------------- 2 files changed, 90 insertions(+), 85 deletions(-) create mode 100644 renderer/src/scene/dependency_resolver.rs diff --git a/renderer/src/scene/dependency_resolver.rs b/renderer/src/scene/dependency_resolver.rs new file mode 100644 index 0000000..99d3195 --- /dev/null +++ b/renderer/src/scene/dependency_resolver.rs @@ -0,0 +1,87 @@ +use super::versioning::{Computed, Version, Versioned}; +use massive_scene::Id; + +/// Resolve a computed value. +/// +/// Invoking this function ensures that the computed value `id` is up to date with its dependencies +/// at `head_version`. +/// +/// We don't return a reference to the result here, because the borrow checker would make this +/// recursive function invocation unnecessarily more complex. +/// +/// TODO: Unrecurse this. There might be degenerate cases of large dependency chains. +pub fn resolve( + head_version: Version, + shared_storage: &Resolver::SharedStorage, + caches: &mut Resolver::ComputedStorage, + id: Id, +) where + Computed: Default, +{ + // Already validated at the latest version? Done. + // + // `get_or_default` must be used here. This is the only situation in which the cache may + // need to be resized. + let computed = Resolver::computed_mut(caches, id); + if computed.validated_at == head_version { + return; + } + // Save the current max dependencies version for later. + // + // In theory this could be overwritten if there are cycles in the dependency graph, but in + // practice they are not (and everything may blow up anyway). + let computed_max_deps = computed.max_deps_version; + + let source = Resolver::source(shared_storage, id); + let max_deps_version = + Resolver::resolve_dependencies(head_version, source, shared_storage, caches); + + // If the max_deps_version is smaller or equal to the one of the computed value, the value is ok + // and can be marked as validated at `head_version`. + if max_deps_version <= computed_max_deps { + Resolver::computed_mut(caches, id).validated_at = head_version; + return; + } + + // Compute a new value and store it. + let new_value = Resolver::compute(shared_storage, caches, source); + *Resolver::computed_mut(caches, id) = Computed { + validated_at: head_version, + max_deps_version, + value: new_value, + }; +} + +pub trait DependencyResolver { + /// Type of the shared table storage. + type SharedStorage; + /// Type of the computed table storage. + type ComputedStorage; + + /// The symmetric _versioned_ source value type. There must be a source value for every computed + /// value with the same id. + type Source; + /// The computed value type (must implement Default for now, use Option<> otherwise) + type Computed; + + /// Retrieve a reference to the versioned source value. + fn source(scene: &Self::SharedStorage, id: Id) -> &Versioned; + + /// Make sure that all dependencies are up to date and return their maximum version. + fn resolve_dependencies( + head_version: Version, + source: &Versioned, + shared: &Self::SharedStorage, + computed: &mut Self::ComputedStorage, + ) -> Version; + + fn compute( + shared: &Self::SharedStorage, + computed: &Self::ComputedStorage, + source: &Self::Source, + ) -> Self::Computed; + + fn computed_mut(computed: &mut Self::ComputedStorage, id: Id) -> &mut Computed + where + Computed: Default; +} diff --git a/renderer/src/scene/mod.rs b/renderer/src/scene/mod.rs index dc4ce93..2635d98 100644 --- a/renderer/src/scene/mod.rs +++ b/renderer/src/scene/mod.rs @@ -1,11 +1,14 @@ use std::{cell::RefCell, collections::HashMap}; use euclid::num::Zero; + +use dependency_resolver::{resolve, DependencyResolver}; use id_table::IdTable; use massive_geometry::Matrix4; use massive_scene::{Change, Id, PositionRenderObj, PositionedRenderShape, SceneChange, Shape}; use versioning::{Computed, Version, Versioned}; +mod dependency_resolver; mod id_table; mod versioning; @@ -81,91 +84,6 @@ impl Scene { } } -/// Resolve a computed value. -/// -/// Invoking this function ensures that the computed value `id` is up to date with its dependencies -/// at `head_version`. -/// -/// We don't return a reference to the result here, because the borrow checker would make this -/// recursive function invocation unnecessarily more complex. -/// -/// TODO: Unrecurse this. There might be degenerate cases of large dependency chains. -fn resolve( - head_version: Version, - shared_storage: &Resolver::SharedStorage, - caches: &mut Resolver::ComputedStorage, - id: Id, -) where - Computed: Default, -{ - // Already validated at the latest version? Done. - // - // `get_or_default` must be used here. This is the only situation in which the cache may - // need to be resized. - let computed = Resolver::computed_mut(caches, id); - if computed.validated_at == head_version { - return; - } - // Save the current max dependencies version for later. - // - // In theory this could be overwritten if there are cycles in the dependency graph, but in - // practice they are not (and everything may blow up anyway). - let computed_max_deps = computed.max_deps_version; - - let source = Resolver::source(shared_storage, id); - let max_deps_version = - Resolver::resolve_dependencies(head_version, source, shared_storage, caches); - - // If the max_deps_version is smaller or equal to the current one, the value is ok and can - // be marked as validated at `head_version`. - if max_deps_version <= computed_max_deps { - Resolver::computed_mut(caches, id).validated_at = head_version; - return; - } - - // Compute a new value and store it. - let new_value = Resolver::compute(shared_storage, caches, source); - *Resolver::computed_mut(caches, id) = Computed { - validated_at: head_version, - max_deps_version, - value: new_value, - }; -} - -trait DependencyResolver { - /// Type of the shared table storage. - type SharedStorage; - /// Type of the computed table storage. - type ComputedStorage; - - /// The symmetric _versioned_ source value type. There must be a source value for every computed - /// value with the same id. - type Source; - /// The computed value type (must implement Default for now, use Option<> otherwise) - type Computed; - - /// Retrieve a reference to the versioned source value. - fn source(scene: &Self::SharedStorage, id: Id) -> &Versioned; - - /// Make sure that all dependencies are up to date and return their maximum version. - fn resolve_dependencies( - head_version: Version, - source: &Versioned, - shared: &Self::SharedStorage, - computed: &mut Self::ComputedStorage, - ) -> Version; - - fn compute( - shared: &Self::SharedStorage, - computed: &Self::ComputedStorage, - source: &Self::Source, - ) -> Self::Computed; - - fn computed_mut(computed: &mut Self::ComputedStorage, id: Id) -> &mut Computed - where - Computed: Default; -} - /// The dependency resolver for finally positioned matrix. struct PositionedMatrix;