Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
hymm committed Nov 10, 2022
1 parent 792fc38 commit b55f5a8
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 63 deletions.
20 changes: 10 additions & 10 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub struct App {
/// the application's event loop and advancing the [`Schedule`].
/// Typically, it is not configured manually, but set by one of Bevy's built-in plugins.
/// See `bevy::winit::WinitPlugin` and [`ScheduleRunnerPlugin`](crate::schedule_runner::ScheduleRunnerPlugin).
pub runner: Box<dyn Fn(App) + Send + Sync>, // send/sync bound is only required to make App Send/Sync
pub runner: Box<dyn Fn(App) + Send + Sync>, // Send + Sync bound is only required to make App Send + Sync
/// A container of [`Stage`]s set to be run in a linear order.
pub schedule: Schedule,
sub_apps: HashMap<AppLabelId, SubApp>,
Expand All @@ -89,7 +89,7 @@ impl Debug for App {
pub struct SubApp {
app: App,
extract: Box<dyn Fn(&mut World, &mut App) + Send + Sync>, // Send + Sync bound is only required to make SubApp send sync
runner: Box<dyn Fn(&mut App) + Send + Sync>, // this send sync bound is required since we're actually sending this function to another thread
runner: Box<dyn Fn(&mut App) + Send + Sync>, // this Send + Sync bound is required since we're running this function on another thread
}

impl SubApp {
Expand Down Expand Up @@ -161,7 +161,7 @@ impl App {
/// See [`add_sub_app`](Self::add_sub_app) and [`run_once`](Schedule::run_once) for more details.
pub fn update(&mut self) {
#[cfg(feature = "trace")]
let _bevy_frame_update_span = info_span!("main_app").entered();
let _bevy_frame_update_span = info_span!("main app").entered();
self.schedule.run(&mut self.world);
for sub_app in self.sub_apps.values_mut() {
sub_app.extract(&mut self.world);
Expand Down Expand Up @@ -997,15 +997,15 @@ impl App {

/// Adds an [`App`] as a child of the current one.
///
/// The provided function `f` is called by the [`update`](Self::update) method. The [`World`]
/// The provided functions `extract` and `runner` are normally called by the [`update`](Self::update) method. The [`World`]
/// parameter represents the main app world, while the [`App`] parameter is just a mutable
/// reference to the `SubApp` itself.
pub fn add_sub_app(
&mut self,
label: impl AppLabel,
mut app: App,
sub_app_extract: impl Fn(&mut World, &mut App) + 'static + Send + Sync,
sub_app_runner: impl Fn(&mut App) + 'static + Send + Sync,
extract: impl Fn(&mut World, &mut App) + 'static + Send + Sync,
runner: impl Fn(&mut App) + 'static + Send + Sync,
) -> &mut Self {
if let Some(executor) = self.world.get_resource::<MainThreadExecutor>() {
app.world.insert_resource(executor.clone());
Expand All @@ -1014,8 +1014,8 @@ impl App {
label.as_label(),
SubApp {
app,
extract: Box::new(sub_app_extract),
runner: Box::new(sub_app_runner),
extract: Box::new(extract),
runner: Box::new(runner),
},
);
self
Expand Down Expand Up @@ -1055,12 +1055,12 @@ impl App {
}
}

/// inserts an existing sub app into the app
/// Inserts an existing sub app into the app
pub fn insert_sub_app(&mut self, label: impl AppLabel, sub_app: SubApp) {
self.sub_apps.insert(label.as_label(), sub_app);
}

/// remove a sub app from the app
/// Removes a sub app from the app. Returns None if the label doesn't exist.
pub fn remove_sub_app(&mut self, label: impl AppLabel) -> Option<SubApp> {
self.sub_apps.remove(&label.as_label())
}
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/schedule/executor_parallel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ 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)]
pub struct MainThreadExecutor(pub Arc<ThreadExecutor>);

Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ basis-universal = { version = "0.2.0", optional = true }
encase = { version = "0.4", features = ["glam"] }
# For wgpu profiling using tracing. Use `RUST_LOG=info` to also capture the wgpu spans.
profiling = { version = "1", features = ["profile-with-tracing"], optional = true }
async-channel = "1.4"
async-channel = "1.4"
7 changes: 6 additions & 1 deletion crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ use std::{

/// Contains the default Bevy rendering backend based on wgpu.
pub struct RenderPlugin {
/// Pipelined rendering runs the rendering simultaneously with the main app.
/// Use this to turn pipelined rendering on or off. By default it's on in native
/// environments and off in wasm.
pub use_pipelined_rendering: bool,
}

Expand Down Expand Up @@ -237,7 +240,7 @@ impl Plugin for RenderPlugin {

app.add_sub_app(RenderApp, render_app, move |app_world, render_app| {
#[cfg(feature = "trace")]
let _render_span = bevy_utils::tracing::info_span!("extract").entered();
let _render_span = bevy_utils::tracing::info_span!("extract main to render app").entered();
{
#[cfg(feature = "trace")]
let _stage_span =
Expand Down Expand Up @@ -272,6 +275,8 @@ impl Plugin for RenderPlugin {
extract(app_world, render_app);
}
}, |render_app| {
#[cfg(feature = "trace")]
let _render_span = bevy_utils::tracing::info_span!("render app").entered();
{
#[cfg(feature = "trace")]
let _stage_span =
Expand Down
70 changes: 32 additions & 38 deletions crates/bevy_render/src/pipelined_rendering.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use async_channel::{Receiver, Sender};

use bevy_app::{App, SubApp};
use bevy_ecs::{
schedule::MainThreadExecutor,
Expand All @@ -7,22 +8,19 @@ use bevy_ecs::{
};
use bevy_tasks::ComputeTaskPool;

#[cfg(feature = "trace")]
use bevy_utils::tracing::Instrument;

use crate::{PipelinedRenderingApp, RenderApp};

/// Resource to be used for pipelined rendering for sending the render app from the main thread to the rendering thread
/// Resource for pipelined rendering to send the render app from the main thread to the rendering thread
#[derive(Resource)]
pub struct MainToRenderAppSender(pub Sender<SubApp>);

/// Resource used by pipelined rendering to send the render app from the render thread to the main thread
/// Resource for pipelined rendering to send the render app from the render thread to the main thread
#[derive(Resource)]
pub struct RenderToMainAppReceiver(pub Receiver<SubApp>);

/// sets up the render thread and insert resource into the main app for controlling the render thread
/// Sets up the render thread and inserts resources into the main app used for controlling the render thread
/// This does nothing if pipelined rendering is not enabled.
pub fn setup_rendering(app: &mut App) {
// skip this if pipelined rendering is not enabled
if app.get_sub_app(PipelinedRenderingApp).is_err() {
return;
}
Expand All @@ -36,42 +34,38 @@ pub fn setup_rendering(app: &mut App) {
app.insert_resource(MainToRenderAppSender(app_to_render_sender));
app.insert_resource(RenderToMainAppReceiver(render_to_app_receiver));

let render_task = async move {
loop {
// TODO: exit loop when app is exited
let recv_task = app_to_render_receiver.recv();
let mut sub_app = recv_task.await.unwrap();
sub_app.run();
render_to_app_sender.send(sub_app).await.unwrap();
}
};
#[cfg(feature = "trace")]
let span = bevy_utils::tracing::info_span!("render app");
#[cfg(feature = "trace")]
let render_task = render_task.instrument(span);
ComputeTaskPool::get().spawn(render_task).detach();
ComputeTaskPool::get()
.spawn(async move {
loop {
// TODO: exit loop when app is exited
let recv_task = app_to_render_receiver.recv();
let mut sub_app = recv_task.await.unwrap();
sub_app.run();
render_to_app_sender.send(sub_app).await.unwrap();
}
})
.detach();
}

/// This function is used for synchronizing the main app with the render world.
/// Do not call this function if pipelined rendering is not setup.
pub fn update_rendering(app_world: &mut World) {
// wait to get the render app back to signal that rendering is finished
let mut render_app = app_world
.resource_scope(|world, main_thread_executor: Mut<MainThreadExecutor>| {
ComputeTaskPool::get()
.scope(Some(main_thread_executor.0.clone()), |s| {
s.spawn(async {
let receiver = world.get_resource::<RenderToMainAppReceiver>().unwrap();
let recv = receiver.0.recv();
recv.await.unwrap()
});
})
.pop()
})
.unwrap();
app_world.resource_scope(|world, main_thread_executor: Mut<MainThreadExecutor>| {
// we use a scope here to run any main thread tasks that the render world still needs to run
// while we wait for the render world to be received.
let mut render_app = ComputeTaskPool::get()
.scope(Some(main_thread_executor.0.clone()), |s| {
s.spawn(async {
let receiver = world.get_resource::<RenderToMainAppReceiver>().unwrap();
receiver.0.recv().await.unwrap()
});
})
.pop()
.unwrap();

render_app.extract(app_world);
render_app.extract(world);

app_world.resource_scope(|_world, sender: Mut<MainToRenderAppSender>| {
let sender = world.get_resource::<MainToRenderAppSender>().unwrap();
sender.0.send_blocking(render_app).unwrap();
});
// frame pacing plugin should run here somehow. i.e. after rendering, but before input handling
}
18 changes: 10 additions & 8 deletions crates/bevy_tasks/src/main_thread_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,17 @@ impl ThreadExecutor {

/// Gets the `[MainThreadSpawner]` for the thread executor.
/// Use this to spawn tasks that run on the thread this was instatiated on.
pub fn spawner(&self) -> MainThreadSpawner<'static> {
MainThreadSpawner(self.executor.clone())
pub fn spawner(&self) -> ThreadSpawner<'static> {
ThreadSpawner(self.executor.clone())
}

/// Gets the `[MainThreadTicker]` for this executor.
/// Use this to tick the executor.
/// It only returns the ticker if it's on the thread the executor was created on
/// and returns `None` otherwise.
pub fn ticker(&self) -> Option<MainThreadTicker> {
pub fn ticker(&self) -> Option<ThreadTicker> {
if thread::current().id() == self.thread_id {
return Some(MainThreadTicker {
return Some(ThreadTicker {
executor: self.executor.clone(),
_marker: PhantomData::default(),
});
Expand All @@ -50,22 +50,24 @@ impl ThreadExecutor {
}
}

/// Used to spawn on the [`ThreadExecutor`]
#[derive(Debug)]
pub struct MainThreadSpawner<'a>(Arc<Executor<'a>>);
impl<'a> MainThreadSpawner<'a> {
pub struct ThreadSpawner<'a>(Arc<Executor<'a>>);
impl<'a> ThreadSpawner<'a> {
/// Spawn a task on the main thread
pub fn spawn<T: Send + 'a>(&self, future: impl Future<Output = T> + Send + 'a) -> Task<T> {
self.0.spawn(future)
}
}

/// Used to tick the [`ThreadExecutor`]
#[derive(Debug)]
pub struct MainThreadTicker {
pub struct ThreadTicker {
executor: Arc<Executor<'static>>,
// make type not send or sync
_marker: PhantomData<*const ()>,
}
impl MainThreadTicker {
impl ThreadTicker {
/// Tick the main thread executor.
/// This needs to be called manually on the thread if it is not being used with
/// a `[TaskPool::scope]`.
Expand Down
8 changes: 4 additions & 4 deletions crates/bevy_tasks/src/task_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use concurrent_queue::ConcurrentQueue;
use futures_lite::{future, FutureExt};

use crate::Task;
use crate::{main_thread_executor::MainThreadSpawner, ThreadExecutor};
use crate::{main_thread_executor::ThreadSpawner, ThreadExecutor};

/// Used to create a [`TaskPool`]
#[derive(Debug, Default, Clone)]
Expand Down Expand Up @@ -157,7 +157,7 @@ impl TaskPool {
///
/// This is similar to `rayon::scope` and `crossbeam::scope`
///
/// The `thread_executor` optional parameter can be used to pass a `[ThreadExecutor]` to
/// The `thread_executor` optional parameter can be used to pass a [`ThreadExecutor`] to
/// spawn tasks on when calling `spawn_on_scope`. This can be useful for spawning tasks that
/// must run on the main thread. If `None` is passed then `spawn_on_scope` runs tasks on
/// the thread `scope` is run on.
Expand Down Expand Up @@ -257,7 +257,7 @@ impl TaskPool {
Arc::new(ThreadExecutor::new())
};
let thread_spawner = thread_executor.spawner();
let thread_spawner: MainThreadSpawner<'env> = unsafe { mem::transmute(thread_spawner) };
let thread_spawner: ThreadSpawner<'env> = unsafe { mem::transmute(thread_spawner) };
let spawned: ConcurrentQueue<async_executor::Task<T>> = ConcurrentQueue::unbounded();
let spawned_ref: &'env ConcurrentQueue<async_executor::Task<T>> =
unsafe { mem::transmute(&spawned) };
Expand Down Expand Up @@ -371,7 +371,7 @@ impl Drop for TaskPool {
#[derive(Debug)]
pub struct Scope<'scope, 'env: 'scope, T> {
executor: &'scope async_executor::Executor<'scope>,
thread_spawner: MainThreadSpawner<'scope>,
thread_spawner: ThreadSpawner<'scope>,
spawned: &'scope ConcurrentQueue<async_executor::Task<T>>,
// make `Scope` invariant over 'scope and 'env
scope: PhantomData<&'scope mut &'scope ()>,
Expand Down

0 comments on commit b55f5a8

Please sign in to comment.