Skip to content

Commit

Permalink
move MainThreadExecutor to schedule_v3
Browse files Browse the repository at this point in the history
  • Loading branch information
hymm committed Jan 31, 2023
1 parent 5d514fb commit fabbc47
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 59 deletions.
22 changes: 6 additions & 16 deletions crates/bevy_ecs/src/schedule/executor_parallel.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
use std::sync::Arc;

use crate as bevy_ecs;
use crate::{
archetype::ArchetypeComponentId,
query::Access,
schedule::{ParallelSystemExecutor, SystemContainer},
system::Resource,
schedule_v3::MainThreadExecutor,
world::World,
};
use async_channel::{Receiver, Sender};
use bevy_tasks::{ComputeTaskPool, Scope, TaskPool, ThreadExecutor};
use bevy_tasks::{ComputeTaskPool, Scope, TaskPool};
#[cfg(feature = "trace")]
use bevy_utils::tracing::Instrument;
use event_listener::Event;
Expand All @@ -18,16 +15,6 @@ use fixedbitset::FixedBitSet;
#[cfg(test)]
use scheduling_event::*;

/// New-typed [`ThreadExecutor`] [`Resource`] that is used to run systems on the main thread
#[derive(Resource, Default, Clone)]
pub struct MainThreadExecutor(pub Arc<ThreadExecutor<'static>>);

impl MainThreadExecutor {
pub fn new() -> Self {
MainThreadExecutor(Arc::new(ThreadExecutor::new()))
}
}

struct SystemSchedulingMetadata {
/// Used to signal the system's task to start the system.
start: Event,
Expand Down Expand Up @@ -138,7 +125,10 @@ impl ParallelSystemExecutor for ParallelExecutor {
}
}

let thread_executor = world.get_resource::<MainThreadExecutor>().map(|e| &*e.0);
let thread_executor = world
.get_resource::<MainThreadExecutor>()
.map(|e| e.0.clone());
let thread_executor = thread_executor.as_deref();

ComputeTaskPool::init(TaskPool::default).scope_with_executor(
false,
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/schedule_v3/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod multi_threaded;
mod simple;
mod single_threaded;

pub use self::multi_threaded::MultiThreadedExecutor;
pub use self::multi_threaded::{MainThreadExecutor, MultiThreadedExecutor};
pub use self::simple::SimpleExecutor;
pub use self::single_threaded::SingleThreadedExecutor;

Expand Down
105 changes: 64 additions & 41 deletions crates/bevy_ecs/src/schedule_v3/executor/multi_threaded.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use bevy_tasks::{ComputeTaskPool, Scope, TaskPool};
use bevy_tasks::{ComputeTaskPool, Scope, TaskPool, ThreadExecutor};
use bevy_utils::default;
use bevy_utils::syncunsafecell::SyncUnsafeCell;
#[cfg(feature = "trace")]
use bevy_utils::tracing::{info_span, Instrument};
use std::sync::Arc;

use async_channel::{Receiver, Sender};
use fixedbitset::FixedBitSet;

use crate::{
archetype::ArchetypeComponentId,
prelude::Resource,
query::Access,
schedule_v3::{
is_apply_system_buffers, BoxedCondition, ExecutorKind, SystemExecutor, SystemSchedule,
Expand All @@ -17,6 +19,8 @@ use crate::{
world::World,
};

use crate as bevy_ecs;

/// A funky borrow split of [`SystemSchedule`] required by the [`MultiThreadedExecutor`].
struct SyncUnsafeSchedule<'a> {
systems: &'a [SyncUnsafeCell<BoxedSystem>],
Expand Down Expand Up @@ -145,59 +149,68 @@ impl SystemExecutor for MultiThreadedExecutor {
}
}

let thread_executor = world
.get_resource::<MainThreadExecutor>()
.map(|e| e.0.clone());
let thread_executor = thread_executor.as_deref();

let world = SyncUnsafeCell::from_mut(world);
let SyncUnsafeSchedule {
systems,
mut conditions,
} = SyncUnsafeSchedule::new(schedule);

ComputeTaskPool::init(TaskPool::default).scope(|scope| {
// the executor itself is a `Send` future so that it can run
// alongside systems that claim the local thread
let executor = async {
while self.num_completed_systems < num_systems {
// SAFETY: self.ready_systems does not contain running systems
unsafe {
self.spawn_system_tasks(scope, systems, &mut conditions, world);
}

if self.num_running_systems > 0 {
// wait for systems to complete
let index = self
.receiver
.recv()
.await
.unwrap_or_else(|error| unreachable!("{}", error));
ComputeTaskPool::init(TaskPool::default).scope_with_executor(
false,
thread_executor,
|scope| {
// the executor itself is a `Send` future so that it can run
// alongside systems that claim the local thread
let executor = async {
while self.num_completed_systems < num_systems {
// SAFETY: self.ready_systems does not contain running systems
unsafe {
self.spawn_system_tasks(scope, systems, &mut conditions, world);
}

self.finish_system_and_signal_dependents(index);
if self.num_running_systems > 0 {
// wait for systems to complete
let index = self
.receiver
.recv()
.await
.unwrap_or_else(|error| unreachable!("{}", error));

while let Ok(index) = self.receiver.try_recv() {
self.finish_system_and_signal_dependents(index);
}

self.rebuild_active_access();
while let Ok(index) = self.receiver.try_recv() {
self.finish_system_and_signal_dependents(index);
}

self.rebuild_active_access();
}
}
}

// SAFETY: all systems have completed
let world = unsafe { &mut *world.get() };
apply_system_buffers(&mut self.unapplied_systems, systems, world);

debug_assert!(self.ready_systems.is_clear());
debug_assert!(self.running_systems.is_clear());
debug_assert!(self.unapplied_systems.is_clear());
self.active_access.clear();
self.evaluated_sets.clear();
self.skipped_systems.clear();
self.completed_systems.clear();
};
// SAFETY: all systems have completed
let world = unsafe { &mut *world.get() };
apply_system_buffers(&mut self.unapplied_systems, systems, world);

#[cfg(feature = "trace")]
let executor_span = info_span!("schedule_task");
#[cfg(feature = "trace")]
let executor = executor.instrument(executor_span);
scope.spawn(executor);
});
debug_assert!(self.ready_systems.is_clear());
debug_assert!(self.running_systems.is_clear());
debug_assert!(self.unapplied_systems.is_clear());
self.active_access.clear();
self.evaluated_sets.clear();
self.skipped_systems.clear();
self.completed_systems.clear();
};

#[cfg(feature = "trace")]
let executor_span = info_span!("schedule_task");
#[cfg(feature = "trace")]
let executor = executor.instrument(executor_span);
scope.spawn(executor);
},
);
}
}

Expand Down Expand Up @@ -573,3 +586,13 @@ fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &World
})
.fold(true, |acc, res| acc && res)
}

/// New-typed [`ThreadExecutor`] [`Resource`] that is used to run systems on the main thread
#[derive(Resource, Default, Clone)]
pub struct MainThreadExecutor(pub Arc<ThreadExecutor<'static>>);

impl MainThreadExecutor {
pub fn new() -> Self {
MainThreadExecutor(Arc::new(ThreadExecutor::new()))
}
}
3 changes: 2 additions & 1 deletion crates/bevy_render/src/pipelined_rendering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use async_channel::{Receiver, Sender};

use bevy_app::{App, AppLabel, Plugin, SubApp};
use bevy_ecs::{
schedule::{MainThreadExecutor, StageLabel, SystemStage},
schedule::{StageLabel, SystemStage},
schedule_v3::MainThreadExecutor,
system::Resource,
world::{Mut, World},
};
Expand Down

0 comments on commit fabbc47

Please sign in to comment.