Skip to content

Commit feb4746

Browse files
committed
switch to sending render world back and forth with channels
1 parent b757d81 commit feb4746

File tree

7 files changed

+123
-19
lines changed

7 files changed

+123
-19
lines changed

crates/bevy_app/src/app.rs

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use bevy_ecs::{
1010
system::Resource,
1111
world::World,
1212
};
13-
use bevy_tasks::{ComputeTaskPool, TaskPool};
1413
use bevy_utils::{tracing::debug, HashMap, HashSet};
1514
use std::fmt::Debug;
1615

@@ -74,7 +73,6 @@ pub struct App {
7473
sub_apps: HashMap<AppLabelId, SubApp>,
7574
plugin_registry: Vec<Box<dyn Plugin>>,
7675
plugin_name_added: HashSet<String>,
77-
run_once: bool,
7876
}
7977

8078
impl Debug for App {
@@ -88,12 +86,24 @@ impl Debug for App {
8886
}
8987

9088
/// Each `SubApp` has its own [`Schedule`] and [`World`], enabling a separation of concerns.
91-
struct SubApp {
89+
pub struct SubApp {
9290
app: App,
9391
extract: Box<dyn Fn(&mut World, &mut App) + Send + Sync>, // Send + Sync bound is only required to make SubApp send sync
9492
runner: Box<dyn Fn(&mut App) + Send + Sync>, // this send sync bound is required since we're actually sending this function to another thread
9593
}
9694

95+
impl SubApp {
96+
/// runs the `SubApp` with its runner
97+
pub fn run(&mut self) {
98+
(self.runner)(&mut self.app);
99+
}
100+
101+
/// extract data from main world to sub app
102+
pub fn extract(&mut self, main_world: &mut World) {
103+
(self.extract)(main_world, &mut self.app);
104+
}
105+
}
106+
97107
impl Debug for SubApp {
98108
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99109
write!(f, "SubApp {{ app: ")?;
@@ -141,7 +151,6 @@ impl App {
141151
sub_apps: HashMap::default(),
142152
plugin_registry: Vec::default(),
143153
plugin_name_added: Default::default(),
144-
run_once: false,
145154
}
146155
}
147156

@@ -153,22 +162,10 @@ impl App {
153162
pub fn update(&mut self) {
154163
#[cfg(feature = "trace")]
155164
let _bevy_frame_update_span = info_span!("frame").entered();
156-
let thread_executor = self
157-
.world
158-
.get_resource::<MainThreadExecutor>()
159-
.map(|e| e.0.clone());
160-
ComputeTaskPool::init(TaskPool::default).scope(thread_executor, |scope| {
161-
if self.run_once {
162-
for sub_app in self.sub_apps.values_mut() {
163-
scope.spawn(async { (sub_app.runner)(&mut sub_app.app) });
164-
}
165-
}
166-
self.schedule.run(&mut self.world);
167-
});
165+
self.schedule.run(&mut self.world);
168166
for sub_app in self.sub_apps.values_mut() {
169167
(sub_app.extract)(&mut self.world, &mut sub_app.app);
170168
}
171-
self.run_once = true;
172169
}
173170

174171
/// Starts the application by calling the app's [runner function](Self::set_runner).
@@ -1057,6 +1054,16 @@ impl App {
10571054
}
10581055
}
10591056

1057+
/// inserts an existing sub app into the app
1058+
pub fn insert_sub_app(&mut self, label: impl AppLabel, sub_app: SubApp) {
1059+
self.sub_apps.insert(label.as_label(), sub_app);
1060+
}
1061+
1062+
/// remove a sub app from the app
1063+
pub fn remove_sub_app(&mut self, label: impl AppLabel) -> Option<SubApp> {
1064+
self.sub_apps.remove(&label.as_label())
1065+
}
1066+
10601067
/// Retrieves a `SubApp` inside this [`App`] with the given label, if it exists. Otherwise returns
10611068
/// an [`Err`] containing the given label.
10621069
pub fn get_sub_app(&self, label: impl AppLabel) -> Result<&App, impl AppLabel> {

crates/bevy_internal/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ trace = [
1616
"bevy_ecs/trace",
1717
"bevy_log/trace",
1818
"bevy_render?/trace",
19-
"bevy_hierarchy/trace"
19+
"bevy_hierarchy/trace",
20+
"bevy_winit/trace"
2021
]
2122
trace_chrome = [ "bevy_log/tracing-chrome" ]
2223
trace_tracy = ["bevy_render?/tracing-tracy", "bevy_log/tracing-tracy" ]

crates/bevy_render/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ bevy_math = { path = "../bevy_math", version = "0.9.0-dev" }
4040
bevy_mikktspace = { path = "../bevy_mikktspace", version = "0.9.0-dev" }
4141
bevy_reflect = { path = "../bevy_reflect", version = "0.9.0-dev", features = ["bevy"] }
4242
bevy_render_macros = { path = "macros", version = "0.9.0-dev" }
43+
bevy_tasks = { path = "../bevy_tasks", version = "0.9.0-dev" }
4344
bevy_time = { path = "../bevy_time", version = "0.9.0-dev" }
4445
bevy_transform = { path = "../bevy_transform", version = "0.9.0-dev" }
4546
bevy_window = { path = "../bevy_window", version = "0.9.0-dev" }
@@ -75,3 +76,4 @@ basis-universal = { version = "0.2.0", optional = true }
7576
encase = { version = "0.4", features = ["glam"] }
7677
# For wgpu profiling using tracing. Use `RUST_LOG=info` to also capture the wgpu spans.
7778
profiling = { version = "1", features = ["profile-with-tracing"], optional = true }
79+
async-channel = "1.4"

crates/bevy_render/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod extract_param;
77
pub mod extract_resource;
88
pub mod globals;
99
pub mod mesh;
10+
pub mod pipelined_rendering;
1011
pub mod primitives;
1112
pub mod rangefinder;
1213
pub mod render_asset;
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use async_channel::{Receiver, Sender};
2+
use bevy_app::{App, SubApp};
3+
use bevy_ecs::{schedule::MainThreadExecutor, system::Resource, world::Mut};
4+
use bevy_tasks::ComputeTaskPool;
5+
6+
#[cfg(feature = "trace")]
7+
use bevy_utils::tracing::Instrument;
8+
9+
use crate::RenderApp;
10+
11+
/// Resource to be used for pipelined rendering for sending the render app from the main thread to the rendering thread
12+
#[derive(Resource)]
13+
pub struct MainToRenderAppSender(pub Sender<SubApp>);
14+
15+
/// Resource used by pipelined rendering to send the render app from the render thread to the main thread
16+
#[derive(Resource)]
17+
pub struct RenderToMainAppReceiver(pub Receiver<SubApp>);
18+
19+
/// sets up the render thread and insert resource into the main app for controlling the render thread
20+
pub fn setup_pipelined_rendering(app: &mut App) {
21+
let (app_to_render_sender, app_to_render_receiver) = async_channel::bounded::<SubApp>(1);
22+
let (render_to_app_sender, render_to_app_receiver) = async_channel::bounded::<SubApp>(1);
23+
24+
let render_app = app.remove_sub_app(RenderApp).unwrap();
25+
render_to_app_sender.send_blocking(render_app).unwrap();
26+
27+
app.insert_resource(MainToRenderAppSender(app_to_render_sender));
28+
app.insert_resource(RenderToMainAppReceiver(render_to_app_receiver));
29+
30+
let render_task = async move {
31+
loop {
32+
// TODO: exit loop when app is exited
33+
let recv_task = app_to_render_receiver.recv();
34+
#[cfg(feature = "trace")]
35+
let span = bevy_utils::tracing::info_span!("receive render world from main");
36+
#[cfg(feature = "trace")]
37+
let recv_task = recv_task.instrument(span);
38+
let mut sub_app = recv_task.await.unwrap();
39+
sub_app.run();
40+
render_to_app_sender.send(sub_app).await.unwrap();
41+
}
42+
};
43+
#[cfg(feature = "trace")]
44+
let span = bevy_utils::tracing::info_span!("render task");
45+
#[cfg(feature = "trace")]
46+
let render_task = render_task.instrument(span);
47+
ComputeTaskPool::get().spawn(render_task).detach();
48+
}
49+
50+
pub fn update_rendering(app: &mut App) {
51+
app.update();
52+
53+
// wait to get the render app back to signal that rendering is finished
54+
let mut render_app = app
55+
.world
56+
.resource_scope(|world, main_thread_executor: Mut<MainThreadExecutor>| {
57+
ComputeTaskPool::get()
58+
.scope(Some(main_thread_executor.0.clone()), |s| {
59+
s.spawn(async {
60+
let receiver = world.get_resource::<RenderToMainAppReceiver>().unwrap();
61+
let recv = receiver.0.recv();
62+
#[cfg(feature = "trace")]
63+
let span = bevy_utils::tracing::info_span!("wait for render");
64+
#[cfg(feature = "trace")]
65+
let recv = recv.instrument(span);
66+
recv.await.unwrap()
67+
});
68+
})
69+
.pop()
70+
})
71+
.unwrap();
72+
73+
render_app.extract(&mut app.world);
74+
75+
{
76+
#[cfg(feature = "trace")]
77+
let _span = bevy_utils::tracing::info_span!("send world to render").entered();
78+
app.world
79+
.resource_scope(|_world, sender: Mut<MainToRenderAppSender>| {
80+
sender.0.send_blocking(render_app).unwrap();
81+
});
82+
}
83+
84+
// frame pacing plugin should run here somehow. i.e. after rendering, but before input handling
85+
}

crates/bevy_winit/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ license = "MIT OR Apache-2.0"
99
keywords = ["bevy"]
1010

1111
[features]
12+
trace = []
1213
wayland = ["winit/wayland"]
1314
x11 = ["winit/x11"]
1415

@@ -18,6 +19,7 @@ bevy_app = { path = "../bevy_app", version = "0.9.0-dev" }
1819
bevy_ecs = { path = "../bevy_ecs", version = "0.9.0-dev" }
1920
bevy_input = { path = "../bevy_input", version = "0.9.0-dev" }
2021
bevy_math = { path = "../bevy_math", version = "0.9.0-dev" }
22+
bevy_render = { path = "../bevy_render", version = "0.9.0-dev" }
2123
bevy_window = { path = "../bevy_window", version = "0.9.0-dev" }
2224
bevy_utils = { path = "../bevy_utils", version = "0.9.0-dev" }
2325

crates/bevy_winit/src/lib.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod web_resize;
44
mod winit_config;
55
mod winit_windows;
66

7+
use bevy_render::pipelined_rendering::{setup_pipelined_rendering, update_rendering};
78
use converters::convert_cursor_grab_mode;
89
pub use winit_config::*;
910
pub use winit_windows::*;
@@ -348,11 +349,15 @@ pub fn winit_runner_with(mut app: App) {
348349

349350
let return_from_run = app.world.resource::<WinitSettings>().return_from_run;
350351

352+
setup_pipelined_rendering(&mut app);
353+
351354
trace!("Entering winit event loop");
352355

353356
let event_handler = move |event: Event<()>,
354357
event_loop: &EventLoopWindowTarget<()>,
355358
control_flow: &mut ControlFlow| {
359+
#[cfg(feature = "trace")]
360+
let _span = bevy_utils::tracing::info_span!("winit event_handler").entered();
356361
match event {
357362
event::Event::NewEvents(start) => {
358363
let winit_config = app.world.resource::<WinitSettings>();
@@ -587,7 +592,8 @@ pub fn winit_runner_with(mut app: App) {
587592
};
588593
if update {
589594
winit_state.last_update = Instant::now();
590-
app.update();
595+
update_rendering(&mut app);
596+
// app.update();
591597
}
592598
}
593599
Event::RedrawEventsCleared => {

0 commit comments

Comments
 (0)