wait for render app when main world is dropped (#11737)
# Objective - Try not to drop the render world on the render thread, and drop the main world after the render world. - The render world has a drop check that will panic if it is dropped off the main thread. ## Solution - Keep track of where the render world is and wait for it to come back when the channel resource is dropped. --- ## Changelog - Wait for the render world when the main world is dropped. ## Migration Guide - If you were using the pipelined rendering channels, `MainToRenderAppSender` and `RenderToMainAppReceiver`, they have been combined into the single resource `RenderAppChannels`. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Friz64 <friz64@protonmail.com>
This commit is contained in:
parent
44c36ce98e
commit
76b6666965
@ -17,13 +17,52 @@ use crate::RenderApp;
|
|||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
|
||||||
pub struct RenderExtractApp;
|
pub struct RenderExtractApp;
|
||||||
|
|
||||||
/// Channel to send the render app from the main thread to the rendering thread
|
/// Channels used by the main app to send and receive the render app.
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct MainToRenderAppSender(pub Sender<SubApp>);
|
pub struct RenderAppChannels {
|
||||||
|
app_to_render_sender: Sender<SubApp>,
|
||||||
|
render_to_app_receiver: Receiver<SubApp>,
|
||||||
|
render_app_in_render_thread: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// Channel to send the render app from the render thread to the main thread
|
impl RenderAppChannels {
|
||||||
#[derive(Resource)]
|
/// Create a `RenderAppChannels` from a [`async_channel::Receiver`] and [`async_channel::Sender`]
|
||||||
pub struct RenderToMainAppReceiver(pub Receiver<SubApp>);
|
pub fn new(
|
||||||
|
app_to_render_sender: Sender<SubApp>,
|
||||||
|
render_to_app_receiver: Receiver<SubApp>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
app_to_render_sender,
|
||||||
|
render_to_app_receiver,
|
||||||
|
render_app_in_render_thread: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send the `render_app` to the rendering thread.
|
||||||
|
pub fn send_blocking(&mut self, render_app: SubApp) {
|
||||||
|
self.app_to_render_sender.send_blocking(render_app).unwrap();
|
||||||
|
self.render_app_in_render_thread = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receive the `render_app` from the rendering thread.
|
||||||
|
pub async fn recv(&mut self) -> SubApp {
|
||||||
|
let render_app = self.render_to_app_receiver.recv().await.unwrap();
|
||||||
|
self.render_app_in_render_thread = false;
|
||||||
|
render_app
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for RenderAppChannels {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.render_app_in_render_thread {
|
||||||
|
// Any non-send data in the render world was initialized on the main thread.
|
||||||
|
// So on dropping the main world and ending the app, we block and wait for
|
||||||
|
// the render world to return to drop it. Which allows the non-send data
|
||||||
|
// drop methods to run on the correct thread.
|
||||||
|
self.render_to_app_receiver.recv_blocking().ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The [`PipelinedRenderingPlugin`] can be added to your application to enable pipelined rendering.
|
/// The [`PipelinedRenderingPlugin`] can be added to your application to enable pipelined rendering.
|
||||||
/// This moves rendering into a different thread, so that the Nth frame's rendering can
|
/// This moves rendering into a different thread, so that the Nth frame's rendering can
|
||||||
@ -96,8 +135,10 @@ impl Plugin for PipelinedRenderingPlugin {
|
|||||||
|
|
||||||
render_to_app_sender.send_blocking(render_app).unwrap();
|
render_to_app_sender.send_blocking(render_app).unwrap();
|
||||||
|
|
||||||
app.insert_resource(MainToRenderAppSender(app_to_render_sender));
|
app.insert_resource(RenderAppChannels::new(
|
||||||
app.insert_resource(RenderToMainAppReceiver(render_to_app_receiver));
|
app_to_render_sender,
|
||||||
|
render_to_app_receiver,
|
||||||
|
));
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
#[cfg(feature = "trace")]
|
#[cfg(feature = "trace")]
|
||||||
@ -136,21 +177,19 @@ impl Plugin for PipelinedRenderingPlugin {
|
|||||||
// runs extract, and then sends the rendering world back to the render thread.
|
// runs extract, and then sends the rendering world back to the render thread.
|
||||||
fn update_rendering(app_world: &mut World, _sub_app: &mut App) {
|
fn update_rendering(app_world: &mut World, _sub_app: &mut App) {
|
||||||
app_world.resource_scope(|world, main_thread_executor: Mut<MainThreadExecutor>| {
|
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
|
world.resource_scope(|world, mut render_channels: Mut<RenderAppChannels>| {
|
||||||
// while we wait for the render world to be received.
|
// we use a scope here to run any main thread tasks that the render world still needs to run
|
||||||
let mut render_app = ComputeTaskPool::get()
|
// while we wait for the render world to be received.
|
||||||
.scope_with_executor(true, Some(&*main_thread_executor.0), |s| {
|
let mut render_app = ComputeTaskPool::get()
|
||||||
s.spawn(async {
|
.scope_with_executor(true, Some(&*main_thread_executor.0), |s| {
|
||||||
let receiver = world.get_resource::<RenderToMainAppReceiver>().unwrap();
|
s.spawn(async { render_channels.recv().await });
|
||||||
receiver.0.recv().await.unwrap()
|
})
|
||||||
});
|
.pop()
|
||||||
})
|
.unwrap();
|
||||||
.pop()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
render_app.extract(world);
|
render_app.extract(world);
|
||||||
|
|
||||||
let sender = world.resource::<MainToRenderAppSender>();
|
render_channels.send_blocking(render_app);
|
||||||
sender.0.send_blocking(render_app).unwrap();
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user