Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/extract_plugin.rs
9311 views
1
use crate::{
2
sync_world::{despawn_temporary_render_entities, entity_sync_system, SyncWorldPlugin},
3
Render, RenderApp, RenderSystems,
4
};
5
use bevy_app::{App, Plugin, SubApp};
6
use bevy_derive::{Deref, DerefMut};
7
use bevy_ecs::{
8
resource::Resource,
9
schedule::{IntoScheduleConfigs, Schedule, ScheduleBuildSettings, ScheduleLabel, Schedules},
10
world::{Mut, World},
11
};
12
use bevy_utils::default;
13
14
/// Plugin that sets up the [`RenderApp`] and handles extracting data from the
15
/// main world to the render world.
16
pub struct ExtractPlugin {
17
/// Function that gets run at the beginning of each extraction.
18
///
19
/// Gets the main world and render world as arguments (in that order).
20
pub pre_extract: fn(&mut World, &mut World),
21
}
22
23
impl Default for ExtractPlugin {
24
fn default() -> Self {
25
Self {
26
pre_extract: |_, _| {},
27
}
28
}
29
}
30
31
impl Plugin for ExtractPlugin {
32
fn build(&self, app: &mut App) {
33
app.add_plugins(SyncWorldPlugin);
34
app.init_resource::<ScratchMainWorld>();
35
36
let mut render_app = SubApp::new();
37
38
let mut extract_schedule = Schedule::new(ExtractSchedule);
39
// We skip applying any commands during the ExtractSchedule
40
// so commands can be applied on the render thread.
41
extract_schedule.set_build_settings(ScheduleBuildSettings {
42
auto_insert_apply_deferred: false,
43
..default()
44
});
45
extract_schedule.set_apply_final_deferred(false);
46
47
render_app.add_schedule(Render::base_schedule());
48
render_app.add_schedule(extract_schedule);
49
render_app.add_systems(
50
Render,
51
(
52
// This set applies the commands from the extract schedule while the render schedule
53
// is running in parallel with the main app.
54
apply_extract_commands.in_set(RenderSystems::ExtractCommands),
55
despawn_temporary_render_entities.in_set(RenderSystems::PostCleanup),
56
),
57
);
58
59
let pre_extract = self.pre_extract;
60
render_app.set_extract(move |main_world, render_world| {
61
pre_extract(main_world, render_world);
62
63
{
64
#[cfg(feature = "trace")]
65
let _stage_span = bevy_log::info_span!("entity_sync").entered();
66
entity_sync_system(main_world, render_world);
67
}
68
69
// run extract schedule
70
extract(main_world, render_world);
71
});
72
73
let (sender, receiver) = bevy_time::create_time_channels();
74
render_app.insert_resource(sender);
75
app.insert_resource(receiver);
76
app.insert_sub_app(RenderApp, render_app);
77
}
78
}
79
80
/// Schedule in which data from the main world is 'extracted' into the render world.
81
///
82
/// This step should be kept as short as possible to increase the "pipelining potential" for
83
/// running the next frame while rendering the current frame.
84
///
85
/// This schedule is run on the render world, but it also has access to the main world.
86
/// See [`MainWorld`] and [`Extract`](crate::Extract) for details on how to access main world data from this schedule.
87
#[derive(ScheduleLabel, PartialEq, Eq, Debug, Clone, Hash, Default)]
88
pub struct ExtractSchedule;
89
90
/// Applies the commands from the extract schedule. This happens during
91
/// the render schedule rather than during extraction to allow the commands to run in parallel with the
92
/// main app when pipelined rendering is enabled.
93
fn apply_extract_commands(render_world: &mut World) {
94
render_world.resource_scope(|render_world, mut schedules: Mut<Schedules>| {
95
schedules
96
.get_mut(ExtractSchedule)
97
.unwrap()
98
.apply_deferred(render_world);
99
});
100
}
101
/// The simulation [`World`] of the application, stored as a resource.
102
///
103
/// This resource is only available during [`ExtractSchedule`] and not
104
/// during command application of that schedule.
105
/// See [`Extract`](crate::Extract) for more details.
106
#[derive(Resource, Default, Deref, DerefMut)]
107
pub struct MainWorld(World);
108
109
/// A "scratch" world used to avoid allocating new worlds every frame when
110
/// swapping out the [`MainWorld`] for [`ExtractSchedule`].
111
#[derive(Resource, Default)]
112
struct ScratchMainWorld(World);
113
114
/// Executes the [`ExtractSchedule`] step of the renderer.
115
/// This updates the render world with the extracted ECS data of the current frame.
116
pub fn extract(main_world: &mut World, render_world: &mut World) {
117
// temporarily add the app world to the render world as a resource
118
let scratch_world = main_world.remove_resource::<ScratchMainWorld>().unwrap();
119
let inserted_world = core::mem::replace(main_world, scratch_world.0);
120
render_world.insert_resource(MainWorld(inserted_world));
121
render_world.run_schedule(ExtractSchedule);
122
123
// move the app world back, as if nothing happened.
124
let inserted_world = render_world.remove_resource::<MainWorld>().unwrap();
125
let scratch_world = core::mem::replace(main_world, inserted_world.0);
126
main_world.insert_resource(ScratchMainWorld(scratch_world));
127
}
128
129
#[cfg(test)]
130
mod test {
131
use bevy_app::{App, Startup};
132
use bevy_ecs::{prelude::*, schedule::ScheduleLabel};
133
134
use crate::{
135
extract_component::{ExtractComponent, ExtractComponentPlugin},
136
extract_plugin::ExtractPlugin,
137
sync_component::SyncComponent,
138
sync_world::MainEntity,
139
Render, RenderApp,
140
};
141
142
#[derive(Component, Clone, Debug)]
143
struct RenderComponent;
144
145
#[derive(Component, Clone, Debug)]
146
struct RenderComponentExtra;
147
148
#[derive(Component, Clone, Debug, ExtractComponent)]
149
struct RenderComponentSeparate;
150
151
#[derive(Component, Clone, Debug)]
152
struct RenderComponentNoExtract;
153
154
impl SyncComponent for RenderComponent {
155
type Out = (RenderComponent, RenderComponentExtra);
156
}
157
158
impl ExtractComponent for RenderComponent {
159
type QueryData = &'static Self;
160
161
type QueryFilter = ();
162
163
fn extract_component(
164
_item: bevy_ecs::query::QueryItem<'_, '_, Self::QueryData>,
165
) -> Option<Self::Out> {
166
Some((RenderComponent, RenderComponentExtra))
167
}
168
}
169
170
#[test]
171
fn extraction_works() {
172
let mut app = App::new();
173
174
app.add_plugins(ExtractPlugin::default());
175
app.add_plugins(ExtractComponentPlugin::<RenderComponent>::default());
176
app.add_plugins(ExtractComponentPlugin::<RenderComponentSeparate>::default());
177
app.add_systems(Startup, |mut commands: Commands| {
178
commands.spawn((RenderComponent, RenderComponentSeparate));
179
});
180
181
let render_app = app.get_sub_app_mut(RenderApp).unwrap();
182
183
// Normally RenderPlugin sets the RenderRecovery schedule as update, but for
184
// testing we just use the Render schedule directly.
185
render_app.update_schedule = Some(Render.intern());
186
187
render_app.world_mut().add_observer(
188
|event: On<Add, (RenderComponent, RenderComponentExtra)>, mut commands: Commands| {
189
// Simulate data that's not extracted
190
commands
191
.entity(event.entity)
192
.insert(RenderComponentNoExtract);
193
},
194
);
195
196
app.update();
197
198
// Check that all components have been extracted
199
{
200
let render_app = app.get_sub_app_mut(RenderApp).unwrap();
201
render_app
202
.world_mut()
203
.run_system_cached(
204
|entity: Single<(
205
&MainEntity,
206
Option<&RenderComponent>,
207
Option<&RenderComponentExtra>,
208
Option<&RenderComponentSeparate>,
209
Option<&RenderComponentNoExtract>,
210
)>| {
211
assert!(entity.1.is_some());
212
assert!(entity.2.is_some());
213
assert!(entity.3.is_some());
214
assert!(entity.4.is_some());
215
},
216
)
217
.unwrap();
218
}
219
220
// Remove RenderComponent
221
app.world_mut()
222
.run_system_cached(
223
|mut commands: Commands, query: Query<Entity, With<RenderComponent>>| {
224
for entity in query {
225
commands.entity(entity).remove::<RenderComponent>();
226
}
227
},
228
)
229
.unwrap();
230
231
app.update();
232
233
// Check that the extracted components have been removed
234
{
235
let render_app = app.get_sub_app_mut(RenderApp).unwrap();
236
render_app
237
.world_mut()
238
.run_system_cached(
239
|entity: Single<(
240
&MainEntity,
241
Option<&RenderComponent>,
242
Option<&RenderComponentExtra>,
243
Option<&RenderComponentSeparate>,
244
Option<&RenderComponentNoExtract>,
245
)>| {
246
assert!(entity.1.is_none());
247
assert!(entity.2.is_none());
248
assert!(entity.3.is_some());
249
assert!(entity.4.is_some());
250
},
251
)
252
.unwrap();
253
}
254
}
255
}
256
257