mirror of
https://github.com/bevyengine/bevy
synced 2025-02-16 14:08:32 +00:00
ecs: add thread local system support to parallel executor
This commit is contained in:
parent
98ed29aacc
commit
0dc810a37a
12 changed files with 238 additions and 163 deletions
|
@ -20,10 +20,10 @@ impl AppPlugin for DiagnosticsPlugin {
|
|||
#[cfg(feature = "profiler")]
|
||||
{
|
||||
use bevy_ecs::IntoQuerySystem;
|
||||
app.add_resource_ecs::<Box<dyn bevy_ecs::profiler::Profiler>>(Box::new(
|
||||
app.add_resource::<Box<dyn bevy_ecs::profiler::Profiler>>(Box::new(
|
||||
system_profiler::SystemProfiler::default(),
|
||||
))
|
||||
.add_system_to_stage_ecs(
|
||||
.add_system_to_stage(
|
||||
bevy_app::stage::LAST,
|
||||
system_profiler::profiler_diagnostic_system.system(),
|
||||
);
|
||||
|
|
|
@ -296,7 +296,7 @@ impl Commands {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn apply(self, world: &mut World, resources: &mut Resources) {
|
||||
pub fn apply(&self, world: &mut World, resources: &mut Resources) {
|
||||
let mut commands = self.commands.lock().unwrap();
|
||||
for command in commands.commands.drain(..) {
|
||||
match command {
|
||||
|
|
|
@ -65,6 +65,16 @@ impl Default for ExecutorStage {
|
|||
}
|
||||
}
|
||||
|
||||
enum RunReadyResult {
|
||||
Ok,
|
||||
ThreadLocalReady(usize),
|
||||
}
|
||||
|
||||
enum RunReadyType {
|
||||
All,
|
||||
Dependents(usize),
|
||||
}
|
||||
|
||||
impl ExecutorStage {
|
||||
pub fn prepare(&mut self, world: &World, systems: &Vec<Arc<Mutex<Box<dyn System>>>>) {
|
||||
self.system_dependencies = vec![FixedBitSet::with_capacity(systems.len()); systems.len()];
|
||||
|
@ -79,17 +89,25 @@ impl ExecutorStage {
|
|||
}
|
||||
|
||||
let mut current_archetype_access = ArchetypeAccess::default();
|
||||
let mut last_thread_local_index: Option<usize> = None;
|
||||
for (system_index, system) in systems.iter().enumerate() {
|
||||
let system = system.lock().unwrap();
|
||||
if let Some(archetype_access) = system.get_archetype_access() {
|
||||
// TODO: check if thread local and add full sync
|
||||
// if any system before this one conflicts, check all systems that came before for compatibility
|
||||
if current_archetype_access.is_compatible(archetype_access) == false {
|
||||
for earlier_system_index in 0..system_index {
|
||||
let earlier_system = systems[earlier_system_index].lock().unwrap();
|
||||
if let Some(earlier_archetype_access) =
|
||||
earlier_system.get_archetype_access()
|
||||
{
|
||||
let archetype_access = system.get_archetype_access();
|
||||
match system.thread_local_execution() {
|
||||
ThreadLocalExecution::NextFlush => {
|
||||
// if any system before this one conflicts, check all systems that came before for compatibility
|
||||
if current_archetype_access.is_compatible(archetype_access) == false {
|
||||
for earlier_system_index in 0..system_index {
|
||||
let earlier_system = systems[earlier_system_index].lock().unwrap();
|
||||
|
||||
// ignore "immediate" thread local systems, we handle them separately
|
||||
if let ThreadLocalExecution::Immediate =
|
||||
earlier_system.thread_local_execution()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let earlier_archetype_access = earlier_system.get_archetype_access();
|
||||
// if earlier system is incompatible, make the current system dependent
|
||||
if earlier_archetype_access.is_compatible(archetype_access) == false {
|
||||
self.system_dependents[earlier_system_index].push(system_index);
|
||||
|
@ -97,62 +115,87 @@ impl ExecutorStage {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
current_archetype_access.union(archetype_access);
|
||||
current_archetype_access.union(archetype_access);
|
||||
|
||||
if let Some(last_thread_local_index) = last_thread_local_index {
|
||||
self.system_dependents[last_thread_local_index].push(system_index);
|
||||
self.system_dependencies[system_index].insert(last_thread_local_index);
|
||||
}
|
||||
}
|
||||
ThreadLocalExecution::Immediate => {
|
||||
last_thread_local_index = Some(system_index);
|
||||
for earlier_system_index in 0..system_index {
|
||||
// treat all earlier systems as "incompatible" to ensure we run this thread local system exclusively
|
||||
self.system_dependents[earlier_system_index].push(system_index);
|
||||
self.system_dependencies[system_index].insert(earlier_system_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_ready_systems<'run>(
|
||||
fn run_ready_systems<'run>(
|
||||
&mut self,
|
||||
systems: &[Arc<Mutex<Box<dyn System>>>],
|
||||
run_ready_type: RunReadyType,
|
||||
scope: &ScopeFifo<'run>,
|
||||
world: &'run World,
|
||||
resources: &'run Resources,
|
||||
) {
|
||||
for i in 0..systems.len() {
|
||||
Self::try_run_system(
|
||||
systems,
|
||||
i,
|
||||
&mut self.running_systems,
|
||||
&self.finished_systems,
|
||||
&self.system_dependencies,
|
||||
&self.sender,
|
||||
scope,
|
||||
world,
|
||||
resources,
|
||||
);
|
||||
}
|
||||
}
|
||||
) -> RunReadyResult {
|
||||
// produce a system index iterator based on the passed in RunReadyType
|
||||
let mut all;
|
||||
let mut dependents;
|
||||
let system_index_iter: &mut dyn Iterator<Item = usize> = match run_ready_type {
|
||||
RunReadyType::All => {
|
||||
all = 0..systems.len();
|
||||
&mut all
|
||||
}
|
||||
RunReadyType::Dependents(system_index) => {
|
||||
dependents = self.system_dependents[system_index].iter().cloned();
|
||||
&mut dependents
|
||||
}
|
||||
};
|
||||
|
||||
#[inline]
|
||||
pub fn try_run_system<'run>(
|
||||
systems: &[Arc<Mutex<Box<dyn System>>>],
|
||||
system_index: usize,
|
||||
running_systems: &mut FixedBitSet,
|
||||
finished_systems: &FixedBitSet,
|
||||
system_dependencies: &[FixedBitSet],
|
||||
sender: &Sender<usize>,
|
||||
scope: &ScopeFifo<'run>,
|
||||
world: &'run World,
|
||||
resources: &'run Resources,
|
||||
) {
|
||||
if running_systems.contains(system_index) {
|
||||
return;
|
||||
let mut systems_currently_running = false;
|
||||
for system_index in system_index_iter {
|
||||
// if this system has already been run, don't try to run it again
|
||||
if self.running_systems.contains(system_index) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if all system dependencies are finished, queue up the system to run
|
||||
if self.system_dependencies[system_index].is_subset(&self.finished_systems) {
|
||||
let system = systems[system_index].clone();
|
||||
|
||||
// handle thread local system
|
||||
{
|
||||
let system = system.lock().unwrap();
|
||||
if let ThreadLocalExecution::Immediate = system.thread_local_execution() {
|
||||
if systems_currently_running {
|
||||
// if systems are currently running, we can't run this thread local system yet
|
||||
continue;
|
||||
} else {
|
||||
// if no systems are running, return this thread local system to be run exclusively
|
||||
return RunReadyResult::ThreadLocalReady(system_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle multi-threaded system
|
||||
let sender = self.sender.clone();
|
||||
self.running_systems.insert(system_index);
|
||||
scope.spawn_fifo(move |_| {
|
||||
let mut system = system.lock().unwrap();
|
||||
system.run(world, resources);
|
||||
sender.send(system_index).unwrap();
|
||||
});
|
||||
|
||||
systems_currently_running = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if all system dependencies are finished, queue up the system to run
|
||||
if system_dependencies[system_index].is_subset(&finished_systems) {
|
||||
let system = systems[system_index].clone();
|
||||
let sender = sender.clone();
|
||||
running_systems.insert(system_index);
|
||||
scope.spawn_fifo(move |_| {
|
||||
let mut system = system.lock().unwrap();
|
||||
system.run(world, resources);
|
||||
sender.send(system_index).unwrap();
|
||||
})
|
||||
}
|
||||
RunReadyResult::Ok
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
|
@ -163,46 +206,60 @@ impl ExecutorStage {
|
|||
) {
|
||||
self.finished_systems.clear();
|
||||
self.running_systems.clear();
|
||||
{
|
||||
let world = &*world;
|
||||
let resources = &*resources;
|
||||
let mut run_ready_result = RunReadyResult::Ok;
|
||||
rayon::scope_fifo(|scope| {
|
||||
run_ready_result =
|
||||
self.run_ready_systems(systems, RunReadyType::All, scope, world, resources);
|
||||
});
|
||||
loop {
|
||||
// if all systems in the stage are finished, break out of the loop
|
||||
if self.finished_systems.count_ones(..) == systems.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
rayon::scope_fifo(move |scope| {
|
||||
self.run_ready_systems(systems, scope, world, resources);
|
||||
loop {
|
||||
if self.finished_systems.count_ones(..) == systems.len() {
|
||||
break;
|
||||
}
|
||||
if let RunReadyResult::ThreadLocalReady(thread_local_index) = run_ready_result {
|
||||
// if a thread local system is ready to run, run it exclusively on the main thread
|
||||
let mut system = systems[thread_local_index].lock().unwrap();
|
||||
self.running_systems.insert(thread_local_index);
|
||||
system.run(world, resources);
|
||||
system.run_thread_local(world, resources);
|
||||
self.finished_systems.insert(thread_local_index);
|
||||
self.sender.send(thread_local_index).unwrap();
|
||||
run_ready_result = RunReadyResult::Ok;
|
||||
} else {
|
||||
// wait for a system to finish, then run its dependents
|
||||
rayon::scope_fifo(|scope| {
|
||||
loop {
|
||||
// if all systems in the stage are finished, break out of the loop
|
||||
if self.finished_systems.count_ones(..) == systems.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
let finished_system = self.receiver.recv().unwrap();
|
||||
self.finished_systems.insert(finished_system);
|
||||
for dependent_system in self.system_dependents[finished_system].iter() {
|
||||
Self::try_run_system(
|
||||
let finished_system = self.receiver.recv().unwrap();
|
||||
self.finished_systems.insert(finished_system);
|
||||
run_ready_result = self.run_ready_systems(
|
||||
systems,
|
||||
*dependent_system,
|
||||
&mut self.running_systems,
|
||||
&self.finished_systems,
|
||||
&self.system_dependencies,
|
||||
&self.sender,
|
||||
RunReadyType::Dependents(finished_system),
|
||||
scope,
|
||||
world,
|
||||
resources,
|
||||
);
|
||||
|
||||
// if the next ready system is thread local, break out of this loop/rayon scope so it can be run
|
||||
if let RunReadyResult::ThreadLocalReady(_) = run_ready_result {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// "flush"
|
||||
// NOTE: when this is made parallel a full sync is required here
|
||||
for system in systems.iter() {
|
||||
let mut system = system.lock().unwrap();
|
||||
match system.thread_local_execution() {
|
||||
ThreadLocalExecution::NextFlush => system.run_thread_local(world, resources),
|
||||
ThreadLocalExecution::Immediate => {
|
||||
// TODO: this should happen immediately after thread local systems
|
||||
system.run_thread_local(world, resources)
|
||||
}
|
||||
ThreadLocalExecution::Immediate => { /* already ran */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +284,7 @@ impl ArchetypeAccess {
|
|||
self.immutable.union_with(&other.immutable);
|
||||
}
|
||||
|
||||
pub fn set_bits_for_query<Q>(&mut self, world: &World)
|
||||
pub fn set_access_for_query<Q>(&mut self, world: &World)
|
||||
where
|
||||
Q: Query,
|
||||
{
|
||||
|
@ -251,7 +308,7 @@ impl ArchetypeAccess {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Executor;
|
||||
use crate::{IntoQuerySystem, Query, Res, Resources, Schedule, World};
|
||||
use crate::{IntoQuerySystem, IntoThreadLocalSystem, Query, Res, Resources, Schedule, World};
|
||||
use fixedbitset::FixedBitSet;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
|
@ -264,7 +321,7 @@ mod tests {
|
|||
let mut count = counter.count.lock().unwrap();
|
||||
assert!(
|
||||
*count < 2,
|
||||
"read_32 should run be one of the first two systems to run"
|
||||
"read_32 should be one of the first two systems to run"
|
||||
);
|
||||
*count += 1;
|
||||
}
|
||||
|
@ -273,7 +330,7 @@ mod tests {
|
|||
let mut count = counter.count.lock().unwrap();
|
||||
assert!(
|
||||
*count < 2,
|
||||
"write_float should run be one of the first two systems to run"
|
||||
"write_float should be one of the first two systems to run"
|
||||
);
|
||||
*count += 1;
|
||||
}
|
||||
|
@ -305,6 +362,25 @@ mod tests {
|
|||
*count += 1;
|
||||
}
|
||||
|
||||
fn thread_local_system(_world: &mut World, resources: &mut Resources) {
|
||||
let counter = resources.get::<Counter>().unwrap();
|
||||
let mut count = counter.count.lock().unwrap();
|
||||
assert_eq!(
|
||||
*count, 5,
|
||||
"thread_local_system should always be the sixth system to run"
|
||||
);
|
||||
*count += 1;
|
||||
}
|
||||
|
||||
fn write_f32(counter: Res<Counter>, _query: Query<&mut f32>) {
|
||||
let mut count = counter.count.lock().unwrap();
|
||||
assert_eq!(
|
||||
*count, 6,
|
||||
"write_f32 should always be the seventh system to run"
|
||||
);
|
||||
*count += 1;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn schedule() {
|
||||
let mut world = World::new();
|
||||
|
@ -324,15 +400,27 @@ mod tests {
|
|||
schedule.add_system_to_stage("A", read_u32_write_u64.system());
|
||||
schedule.add_system_to_stage("A", read_u64.system());
|
||||
schedule.add_system_to_stage("B", write_u64.system());
|
||||
schedule.add_system_to_stage("B", thread_local_system.thread_local_system());
|
||||
schedule.add_system_to_stage("B", write_f32.system());
|
||||
|
||||
let mut executor = Executor::default();
|
||||
executor.prepare(&mut schedule, &world);
|
||||
run_executor_and_validate(&mut executor, &mut schedule, &mut world, &mut resources);
|
||||
// run again (with counter reset) to ensure executor works correctly across runs
|
||||
*resources.get::<Counter>().unwrap().count.lock().unwrap() = 0;
|
||||
run_executor_and_validate(&mut executor, &mut schedule, &mut world, &mut resources);
|
||||
}
|
||||
|
||||
fn run_executor_and_validate(executor: &mut Executor, schedule: &mut Schedule, world: &mut World, resources: &mut Resources) {
|
||||
executor.prepare(schedule, world);
|
||||
|
||||
assert_eq!(
|
||||
executor.stages[0].system_dependents,
|
||||
vec![vec![2], vec![], vec![3], vec![]]
|
||||
);
|
||||
assert_eq!(executor.stages[1].system_dependents, vec![vec![]]);
|
||||
assert_eq!(
|
||||
executor.stages[1].system_dependents,
|
||||
vec![vec![1], vec![2], vec![]]
|
||||
);
|
||||
|
||||
let stage_0_len = executor.stages[0].system_dependencies.len();
|
||||
let mut read_u32_write_u64_deps = FixedBitSet::with_capacity(stage_0_len);
|
||||
|
@ -350,19 +438,26 @@ mod tests {
|
|||
]
|
||||
);
|
||||
|
||||
let stage_1_len = executor.stages[1].system_dependencies.len();
|
||||
let mut thread_local_deps = FixedBitSet::with_capacity(stage_1_len);
|
||||
thread_local_deps.insert(0);
|
||||
let mut write_f64_deps = FixedBitSet::with_capacity(stage_1_len);
|
||||
write_f64_deps.insert(1);
|
||||
assert_eq!(
|
||||
executor.stages[1].system_dependencies,
|
||||
vec![
|
||||
FixedBitSet::with_capacity(1),
|
||||
FixedBitSet::with_capacity(stage_1_len),
|
||||
thread_local_deps,
|
||||
write_f64_deps
|
||||
]
|
||||
);
|
||||
|
||||
executor.run(&mut schedule, &mut world, &mut resources);
|
||||
executor.run(schedule, world, resources);
|
||||
|
||||
let counter = resources.get::<Counter>().unwrap();
|
||||
assert_eq!(
|
||||
*counter.count.lock().unwrap(),
|
||||
5,
|
||||
7,
|
||||
"counter should have been incremented once for each system"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{
|
||||
executor::ArchetypeAccess,
|
||||
resource_query::{FetchResource, ResourceQuery, UnsafeClone},
|
||||
system::{System, SystemId, ThreadLocalExecution},
|
||||
Commands, Resources, executor::ArchetypeAccess,
|
||||
Commands, Resources,
|
||||
};
|
||||
use core::marker::PhantomData;
|
||||
use hecs::{
|
||||
|
@ -9,26 +10,27 @@ use hecs::{
|
|||
};
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub struct SystemFn<F, Init, SetArchetypeAccess>
|
||||
pub struct SystemFn<F, ThreadLocalF, Init, SetArchetypeAccess>
|
||||
where
|
||||
F: FnMut(Commands, &World, &Resources) + Send + Sync,
|
||||
F: FnMut(&World, &Resources) + Send + Sync,
|
||||
ThreadLocalF: FnMut(&mut World, &mut Resources) + Send + Sync,
|
||||
Init: FnMut(&mut Resources) + Send + Sync,
|
||||
SetArchetypeAccess: FnMut(&World, &mut ArchetypeAccess) + Send + Sync,
|
||||
{
|
||||
pub func: F,
|
||||
pub thread_local_func: ThreadLocalF,
|
||||
pub init_func: Init,
|
||||
pub commands: Commands,
|
||||
pub thread_local_execution: ThreadLocalExecution,
|
||||
pub name: Cow<'static, str>,
|
||||
pub id: SystemId,
|
||||
pub archetype_access: ArchetypeAccess,
|
||||
pub set_archetype_access: SetArchetypeAccess,
|
||||
// TODO: add dependency info here
|
||||
}
|
||||
|
||||
impl<F, Init, SetArchetypeAccess> System for SystemFn<F, Init, SetArchetypeAccess>
|
||||
impl<F, ThreadLocalF, Init, SetArchetypeAccess> System for SystemFn<F, ThreadLocalF, Init, SetArchetypeAccess>
|
||||
where
|
||||
F: FnMut(Commands, &World, &Resources) + Send + Sync,
|
||||
F: FnMut(&World, &Resources) + Send + Sync,
|
||||
ThreadLocalF: FnMut(&mut World, &mut Resources) + Send + Sync,
|
||||
Init: FnMut(&mut Resources) + Send + Sync,
|
||||
SetArchetypeAccess: FnMut(&World, &mut ArchetypeAccess) + Send + Sync,
|
||||
{
|
||||
|
@ -40,8 +42,8 @@ where
|
|||
(self.set_archetype_access)(world, &mut self.archetype_access);
|
||||
}
|
||||
|
||||
fn get_archetype_access(&self) -> Option<&ArchetypeAccess> {
|
||||
Some(&self.archetype_access)
|
||||
fn get_archetype_access(&self) -> &ArchetypeAccess {
|
||||
&self.archetype_access
|
||||
}
|
||||
|
||||
fn thread_local_execution(&self) -> ThreadLocalExecution {
|
||||
|
@ -49,12 +51,11 @@ where
|
|||
}
|
||||
|
||||
fn run(&mut self, world: &World, resources: &Resources) {
|
||||
(self.func)(self.commands.clone(), world, resources);
|
||||
(self.func)(world, resources);
|
||||
}
|
||||
|
||||
fn run_thread_local(&mut self, world: &mut World, resources: &mut Resources) {
|
||||
let commands = core::mem::replace(&mut self.commands, Commands::default());
|
||||
commands.apply(world, resources);
|
||||
(self.thread_local_func)(world, resources);
|
||||
}
|
||||
|
||||
fn initialize(&mut self, resources: &mut Resources) {
|
||||
|
@ -89,12 +90,13 @@ macro_rules! impl_into_foreach_system {
|
|||
#[allow(unused_unsafe)]
|
||||
fn system(mut self) -> Box<dyn System> {
|
||||
let id = SystemId::new();
|
||||
let commands = Commands::default();
|
||||
let thread_local_commands = commands.clone();
|
||||
Box::new(SystemFn {
|
||||
commands: Commands::default(),
|
||||
thread_local_execution: ThreadLocalExecution::NextFlush,
|
||||
name: core::any::type_name::<Self>().into(),
|
||||
id,
|
||||
func: move |commands, world, resources| {
|
||||
func: move |world, resources| {
|
||||
<<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::borrow(&resources.resource_archetypes);
|
||||
{
|
||||
let ($($resource,)*) = resources.query_system::<($($resource,)*)>(id);
|
||||
|
@ -104,13 +106,16 @@ macro_rules! impl_into_foreach_system {
|
|||
}
|
||||
<<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::release(&resources.resource_archetypes);
|
||||
},
|
||||
thread_local_func: move |world, resources| {
|
||||
thread_local_commands.apply(world, resources);
|
||||
},
|
||||
init_func: move |resources| {
|
||||
<($($resource,)*)>::initialize(resources, Some(id));
|
||||
},
|
||||
archetype_access: ArchetypeAccess::default(),
|
||||
set_archetype_access: |world, archetype_access| {
|
||||
for archetype in world.archetypes() {
|
||||
archetype_access.set_bits_for_query::<($($component,)*)>(world);
|
||||
archetype_access.set_access_for_query::<($($component,)*)>(world);
|
||||
}
|
||||
},
|
||||
})
|
||||
|
@ -162,12 +167,13 @@ macro_rules! impl_into_query_system {
|
|||
#[allow(unused_unsafe)]
|
||||
fn system(mut self) -> Box<dyn System> {
|
||||
let id = SystemId::new();
|
||||
let commands = Commands::default();
|
||||
let thread_local_commands = commands.clone();
|
||||
Box::new(SystemFn {
|
||||
commands: Commands::default(),
|
||||
thread_local_execution: ThreadLocalExecution::NextFlush,
|
||||
id,
|
||||
name: core::any::type_name::<Self>().into(),
|
||||
func: move |commands, world, resources| {
|
||||
func: move |world, resources| {
|
||||
<<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::borrow(&resources.resource_archetypes);
|
||||
{
|
||||
let ($($resource,)*) = resources.query_system::<($($resource,)*)>(id);
|
||||
|
@ -180,13 +186,16 @@ macro_rules! impl_into_query_system {
|
|||
}
|
||||
<<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::release(&resources.resource_archetypes);
|
||||
},
|
||||
thread_local_func: move |world, resources| {
|
||||
thread_local_commands.apply(world, resources);
|
||||
},
|
||||
init_func: move |resources| {
|
||||
<($($resource,)*)>::initialize(resources, Some(id));
|
||||
},
|
||||
archetype_access: ArchetypeAccess::default(),
|
||||
set_archetype_access: |world, archetype_access| {
|
||||
for archetype in world.archetypes() {
|
||||
$(archetype_access.set_bits_for_query::<$query>(world);)*
|
||||
$(archetype_access.set_access_for_query::<$query>(world);)*
|
||||
}
|
||||
},
|
||||
})
|
||||
|
@ -281,27 +290,27 @@ impl_into_systems!(Ra,Rb,Rc,Rd,Re,Rf,Rg,Rh,Ri);
|
|||
#[rustfmt::skip]
|
||||
impl_into_systems!(Ra,Rb,Rc,Rd,Re,Rf,Rg,Rh,Ri,Rj);
|
||||
|
||||
pub struct ThreadLocalSystem<F>
|
||||
where
|
||||
F: ThreadLocalSystemFn,
|
||||
{
|
||||
func: F,
|
||||
name: Cow<'static, str>,
|
||||
id: SystemId,
|
||||
// TODO: add dependency info here
|
||||
pub trait IntoThreadLocalSystem {
|
||||
fn thread_local_system(self) -> Box<dyn System>;
|
||||
}
|
||||
impl<F> ThreadLocalSystem<F>
|
||||
where
|
||||
F: ThreadLocalSystemFn,
|
||||
{
|
||||
pub fn new(func: F) -> Box<dyn System> {
|
||||
Box::new(Self {
|
||||
func,
|
||||
|
||||
impl<F> IntoThreadLocalSystem for F where F: ThreadLocalSystemFn {
|
||||
fn thread_local_system(mut self) -> Box<dyn System> {
|
||||
Box::new(SystemFn {
|
||||
thread_local_func: move |world, resources| {
|
||||
self.run(world, resources);
|
||||
},
|
||||
func: |_, _| {},
|
||||
init_func: |_| {},
|
||||
set_archetype_access: |_, _| {},
|
||||
thread_local_execution: ThreadLocalExecution::Immediate,
|
||||
name: core::any::type_name::<F>().into(),
|
||||
id: SystemId::new(),
|
||||
archetype_access: ArchetypeAccess::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ThreadLocalSystemFn: Send + Sync + 'static {
|
||||
fn run(&mut self, world: &mut World, resource: &mut Resources);
|
||||
}
|
||||
|
@ -313,31 +322,4 @@ where
|
|||
fn run(&mut self, world: &mut World, resources: &mut Resources) {
|
||||
self(world, resources);
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> System for ThreadLocalSystem<F>
|
||||
where
|
||||
F: ThreadLocalSystemFn + Send + Sync,
|
||||
{
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
fn thread_local_execution(&self) -> ThreadLocalExecution {
|
||||
ThreadLocalExecution::Immediate
|
||||
}
|
||||
|
||||
fn run(&mut self, _world: &World, _resources: &Resources) {}
|
||||
|
||||
fn run_thread_local(&mut self, world: &mut World, resources: &mut Resources) {
|
||||
self.func.run(world, resources);
|
||||
}
|
||||
fn id(&self) -> SystemId {
|
||||
self.id
|
||||
}
|
||||
fn update_archetype_access(&mut self, _world: &World) {
|
||||
}
|
||||
fn get_archetype_access(&self) -> Option<&ArchetypeAccess> {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ mod system;
|
|||
mod world_builder;
|
||||
|
||||
pub use commands::{Commands, CommandsInternal};
|
||||
pub use into_system::{IntoForEachSystem, IntoQuerySystem, Query, ThreadLocalSystem};
|
||||
pub use into_system::{IntoForEachSystem, IntoQuerySystem, IntoThreadLocalSystem, Query};
|
||||
pub use resource_query::{FetchResource, Local, Res, ResMut, ResourceQuery};
|
||||
pub use resources::{FromResources, Resource, Resources};
|
||||
pub use schedule::Schedule;
|
||||
|
|
|
@ -20,7 +20,7 @@ pub trait System: Send + Sync {
|
|||
fn name(&self) -> Cow<'static, str>;
|
||||
fn id(&self) -> SystemId;
|
||||
fn update_archetype_access(&mut self, world: &World);
|
||||
fn get_archetype_access(&self) -> Option<&ArchetypeAccess>;
|
||||
fn get_archetype_access(&self) -> &ArchetypeAccess;
|
||||
fn thread_local_execution(&self) -> ThreadLocalExecution;
|
||||
fn run(&mut self, world: &World, resources: &Resources);
|
||||
fn run_thread_local(&mut self, world: &mut World, resources: &mut Resources);
|
||||
|
|
|
@ -34,7 +34,7 @@ use self::{
|
|||
use base_render_graph::{BaseRenderGraphBuilder, BaseRenderGraphConfig};
|
||||
use bevy_app::{AppBuilder, AppPlugin};
|
||||
use bevy_asset::AddAsset;
|
||||
use bevy_ecs::{IntoQuerySystem, ThreadLocalSystem};
|
||||
use bevy_ecs::{IntoQuerySystem, IntoThreadLocalSystem};
|
||||
use bevy_type_registry::RegisterType;
|
||||
use draw::{clear_draw_system, Draw};
|
||||
use mesh::mesh_resource_provider_system;
|
||||
|
@ -124,7 +124,7 @@ impl AppPlugin for RenderPlugin {
|
|||
)
|
||||
.add_system_to_stage(
|
||||
stage::RENDER_GRAPH_SYSTEMS,
|
||||
ThreadLocalSystem::new(render_graph_schedule_executor_system),
|
||||
render_graph_schedule_executor_system.thread_local_system(),
|
||||
)
|
||||
.add_system_to_stage(stage::DRAW, draw_render_pipelines_system.system())
|
||||
.add_system_to_stage(stage::POST_RENDER, clear_shader_defs_system.system());
|
||||
|
|
|
@ -9,7 +9,7 @@ pub use scene_spawner::*;
|
|||
|
||||
use bevy_app::{stage, AppBuilder, AppPlugin};
|
||||
use bevy_asset::AddAsset;
|
||||
use bevy_ecs::ThreadLocalSystem;
|
||||
use bevy_ecs::IntoThreadLocalSystem;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ScenePlugin;
|
||||
|
@ -22,8 +22,6 @@ impl AppPlugin for ScenePlugin {
|
|||
.add_asset_loader::<Scene, SceneLoader>()
|
||||
.init_resource::<SceneSpawner>()
|
||||
.add_stage_after(stage::EVENT_UPDATE, SCENE_STAGE)
|
||||
.add_system_to_stage(SCENE_STAGE, ThreadLocalSystem::new(scene_spawner_system));
|
||||
// TODO: port scenes to bevy_ecs
|
||||
// .add_system_to_stage_ecs(SCENE_STAGE, ThreadLocalSystem::new(scene_spawner_system));
|
||||
.add_system_to_stage(SCENE_STAGE, scene_spawner_system.thread_local_system());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ pub use wgpu_renderer::*;
|
|||
pub use wgpu_resources::*;
|
||||
|
||||
use bevy_app::{AppBuilder, AppPlugin};
|
||||
use bevy_ecs::{IntoQuerySystem, Resources, ThreadLocalSystem, World};
|
||||
use bevy_ecs::{IntoQuerySystem, Resources, IntoThreadLocalSystem, World};
|
||||
use bevy_render::{
|
||||
render_resource::{free_shared_buffers_system, SharedBuffers},
|
||||
renderer::RenderResourceContext,
|
||||
|
@ -25,7 +25,7 @@ impl AppPlugin for WgpuPlugin {
|
|||
let render_system = wgpu_render_system(app.resources_mut());
|
||||
app.add_system_to_stage(
|
||||
bevy_render::stage::RENDER,
|
||||
ThreadLocalSystem::new(render_system),
|
||||
render_system.thread_local_system(),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
bevy_render::stage::POST_RENDER,
|
||||
|
|
|
@ -256,7 +256,7 @@ fn main() {
|
|||
.init_resource::<GameState>()
|
||||
// Startup systems run exactly once BEFORE all other systems. These are generally used for
|
||||
// app initialization code (ex: adding entities and resources)
|
||||
.add_startup_system(ThreadLocalSystem::new(startup_system))
|
||||
.add_startup_system(startup_system.thread_local_system())
|
||||
// my_system.system() calls converts normal rust functions into ECS systems:
|
||||
.add_system(print_message_system.system())
|
||||
//
|
||||
|
|
|
@ -10,7 +10,7 @@ fn main() {
|
|||
// The core Bevy plugins already register their components, so you only need this step for custom components.
|
||||
.register_component::<ComponentA>()
|
||||
.register_component::<ComponentB>()
|
||||
.add_startup_system(ThreadLocalSystem::new(save_scene_system))
|
||||
.add_startup_system(save_scene_system.thread_local_system())
|
||||
.add_startup_system(load_scene_system.system())
|
||||
.add_system(print_system.system())
|
||||
.run();
|
||||
|
|
|
@ -11,7 +11,7 @@ pub use crate::{
|
|||
diagnostic::DiagnosticsPlugin,
|
||||
ecs::{
|
||||
Bundle, Commands, Component, Entity, FromResources, IntoForEachSystem, IntoQuerySystem,
|
||||
Local, Query, Ref, RefMut, Res, ResMut, Resource, Resources, System, ThreadLocalSystem,
|
||||
IntoThreadLocalSystem, Local, Query, Ref, RefMut, Res, ResMut, Resource, Resources, System,
|
||||
World, WorldBuilderSource,
|
||||
},
|
||||
input::{keyboard::KeyCode, mouse::MouseButton, Input},
|
||||
|
|
Loading…
Add table
Reference in a new issue