
# Objective This is a follow up to #9822, which automatically adds sync points during the Schedule build process. However, the implementation in #9822 feels very "special case" to me. As the number of things we want to do with the `Schedule` grows, we need a modularized way to manage those behaviors. For example, in one of my current experiments I want to automatically add systems to apply GPU pipeline barriers between systems accessing GPU resources. For dynamic modifications of the schedule, we mostly need these capabilities: - Storing custom data on schedule edges - Storing custom data on schedule nodes - Modify the schedule graph whenever it builds These should be enough to allows us to add "hooks" to the schedule build process for various reasons. cc @hymm ## Solution This PR abstracts the process of schedule modification and created a new trait, `ScheduleBuildPass`. Most of the logics in #9822 were moved to an implementation of `ScheduleBuildPass`, `AutoInsertApplyDeferredPass`. Whether a dependency edge should "ignore deferred" is now indicated by the presence of a marker struct, `IgnoreDeferred`. This PR has no externally visible effects. However, in a future PR I propose to change the `before_ignore_deferred` and `after_ignore_deferred` API into a more general form, `before_with_options` and `after_with_options`. ```rs schedule.add_systems( system.before_with_options(another_system, IgnoreDeferred) ); schedule.add_systems( system.before_with_options(another_system, ( IgnoreDeferred, AnyOtherOption { key: value } )) ); schedule.add_systems( system.before_with_options(another_system, ()) ); ```
80 lines
2.8 KiB
Rust
80 lines
2.8 KiB
Rust
use alloc::{boxed::Box, vec::Vec};
|
|
use core::any::{Any, TypeId};
|
|
|
|
use super::{DiGraph, NodeId, ScheduleBuildError, ScheduleGraph};
|
|
use crate::world::World;
|
|
use bevy_utils::TypeIdMap;
|
|
use core::fmt::Debug;
|
|
|
|
/// A pass for modular modification of the dependency graph.
|
|
pub trait ScheduleBuildPass: Send + Sync + Debug + 'static {
|
|
/// Custom options for dependencies between sets or systems.
|
|
type EdgeOptions: 'static;
|
|
|
|
/// Called when a dependency between sets or systems was explicitly added to the graph.
|
|
fn add_dependency(&mut self, from: NodeId, to: NodeId, options: Option<&Self::EdgeOptions>);
|
|
|
|
/// Called while flattening the dependency graph. For each `set`, this method is called
|
|
/// with the `systems` associated with the set as well as an immutable reference to the current graph.
|
|
/// Instead of modifying the graph directly, this method should return an iterator of edges to add to the graph.
|
|
fn collapse_set(
|
|
&mut self,
|
|
set: NodeId,
|
|
systems: &[NodeId],
|
|
dependency_flattened: &DiGraph,
|
|
) -> impl Iterator<Item = (NodeId, NodeId)>;
|
|
|
|
/// The implementation will be able to modify the `ScheduleGraph` here.
|
|
fn build(
|
|
&mut self,
|
|
world: &mut World,
|
|
graph: &mut ScheduleGraph,
|
|
dependency_flattened: &mut DiGraph,
|
|
) -> Result<(), ScheduleBuildError>;
|
|
}
|
|
|
|
/// Object safe version of [`ScheduleBuildPass`].
|
|
pub(super) trait ScheduleBuildPassObj: Send + Sync + Debug {
|
|
fn build(
|
|
&mut self,
|
|
world: &mut World,
|
|
graph: &mut ScheduleGraph,
|
|
dependency_flattened: &mut DiGraph,
|
|
) -> Result<(), ScheduleBuildError>;
|
|
|
|
fn collapse_set(
|
|
&mut self,
|
|
set: NodeId,
|
|
systems: &[NodeId],
|
|
dependency_flattened: &DiGraph,
|
|
dependencies_to_add: &mut Vec<(NodeId, NodeId)>,
|
|
);
|
|
fn add_dependency(&mut self, from: NodeId, to: NodeId, all_options: &TypeIdMap<Box<dyn Any>>);
|
|
}
|
|
impl<T: ScheduleBuildPass> ScheduleBuildPassObj for T {
|
|
fn build(
|
|
&mut self,
|
|
world: &mut World,
|
|
graph: &mut ScheduleGraph,
|
|
dependency_flattened: &mut DiGraph,
|
|
) -> Result<(), ScheduleBuildError> {
|
|
self.build(world, graph, dependency_flattened)
|
|
}
|
|
fn collapse_set(
|
|
&mut self,
|
|
set: NodeId,
|
|
systems: &[NodeId],
|
|
dependency_flattened: &DiGraph,
|
|
dependencies_to_add: &mut Vec<(NodeId, NodeId)>,
|
|
) {
|
|
let iter = self.collapse_set(set, systems, dependency_flattened);
|
|
dependencies_to_add.extend(iter);
|
|
}
|
|
fn add_dependency(&mut self, from: NodeId, to: NodeId, all_options: &TypeIdMap<Box<dyn Any>>) {
|
|
let option = all_options
|
|
.get(&TypeId::of::<T::EdgeOptions>())
|
|
.and_then(|x| x.downcast_ref::<T::EdgeOptions>());
|
|
self.add_dependency(from, to, option);
|
|
}
|
|
}
|