Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/sync_world.rs
6595 views
1
use bevy_app::Plugin;
2
use bevy_derive::{Deref, DerefMut};
3
use bevy_ecs::entity::EntityHash;
4
use bevy_ecs::lifecycle::{Add, Remove};
5
use bevy_ecs::{
6
component::Component,
7
entity::{ContainsEntity, Entity, EntityEquivalent},
8
observer::On,
9
query::With,
10
reflect::ReflectComponent,
11
resource::Resource,
12
system::{Local, Query, ResMut, SystemState},
13
world::{Mut, World},
14
};
15
use bevy_platform::collections::{HashMap, HashSet};
16
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
17
18
/// A plugin that synchronizes entities with [`SyncToRenderWorld`] between the main world and the render world.
19
///
20
/// All entities with the [`SyncToRenderWorld`] component are kept in sync. It
21
/// is automatically added as a required component by [`ExtractComponentPlugin`]
22
/// and [`SyncComponentPlugin`], so it doesn't need to be added manually when
23
/// spawning or as a required component when either of these plugins are used.
24
///
25
/// # Implementation
26
///
27
/// Bevy's renderer is architected independently from the main app.
28
/// It operates in its own separate ECS [`World`], so the renderer logic can run in parallel with the main world logic.
29
/// This is called "Pipelined Rendering", see [`PipelinedRenderingPlugin`] for more information.
30
///
31
/// [`SyncWorldPlugin`] is the first thing that runs every frame and it maintains an entity-to-entity mapping
32
/// between the main world and the render world.
33
/// It does so by spawning and despawning entities in the render world, to match spawned and despawned entities in the main world.
34
/// The link between synced entities is maintained by the [`RenderEntity`] and [`MainEntity`] components.
35
///
36
/// The [`RenderEntity`] contains the corresponding render world entity of a main world entity, while [`MainEntity`] contains
37
/// the corresponding main world entity of a render world entity.
38
/// For convenience, [`QueryData`](bevy_ecs::query::QueryData) implementations are provided for both components:
39
/// adding [`MainEntity`] to a query (without a `&`) will return the corresponding main world [`Entity`],
40
/// and adding [`RenderEntity`] will return the corresponding render world [`Entity`].
41
/// If you have access to the component itself, the underlying entities can be accessed by calling `.id()`.
42
///
43
/// Synchronization is necessary preparation for extraction ([`ExtractSchedule`](crate::ExtractSchedule)), which copies over component data from the main
44
/// to the render world for these entities.
45
///
46
/// ```text
47
/// |--------------------------------------------------------------------|
48
/// | | | Main world update |
49
/// | sync | extract |---------------------------------------------------|
50
/// | | | Render world update |
51
/// |--------------------------------------------------------------------|
52
/// ```
53
///
54
/// An example for synchronized main entities 1v1 and 18v1
55
///
56
/// ```text
57
/// |---------------------------Main World------------------------------|
58
/// | Entity | Component |
59
/// |-------------------------------------------------------------------|
60
/// | ID: 1v1 | PointLight | RenderEntity(ID: 3V1) | SyncToRenderWorld |
61
/// | ID: 18v1 | PointLight | RenderEntity(ID: 5V1) | SyncToRenderWorld |
62
/// |-------------------------------------------------------------------|
63
///
64
/// |----------Render World-----------|
65
/// | Entity | Component |
66
/// |---------------------------------|
67
/// | ID: 3v1 | MainEntity(ID: 1V1) |
68
/// | ID: 5v1 | MainEntity(ID: 18V1) |
69
/// |---------------------------------|
70
///
71
/// ```
72
///
73
/// Note that this effectively establishes a link between the main world entity and the render world entity.
74
/// Not every entity needs to be synchronized, however; only entities with the [`SyncToRenderWorld`] component are synced.
75
/// Adding [`SyncToRenderWorld`] to a main world component will establish such a link.
76
/// Once a synchronized main entity is despawned, its corresponding render entity will be automatically
77
/// despawned in the next `sync`.
78
///
79
/// The sync step does not copy any of component data between worlds, since its often not necessary to transfer over all
80
/// the components of a main world entity.
81
/// The render world probably cares about a `Position` component, but not a `Velocity` component.
82
/// The extraction happens in its own step, independently from, and after synchronization.
83
///
84
/// Moreover, [`SyncWorldPlugin`] only synchronizes *entities*. [`RenderAsset`](crate::render_asset::RenderAsset)s like meshes and textures are handled
85
/// differently.
86
///
87
/// [`PipelinedRenderingPlugin`]: crate::pipelined_rendering::PipelinedRenderingPlugin
88
/// [`ExtractComponentPlugin`]: crate::extract_component::ExtractComponentPlugin
89
/// [`SyncComponentPlugin`]: crate::sync_component::SyncComponentPlugin
90
#[derive(Default)]
91
pub struct SyncWorldPlugin;
92
93
impl Plugin for SyncWorldPlugin {
94
fn build(&self, app: &mut bevy_app::App) {
95
app.init_resource::<PendingSyncEntity>();
96
app.add_observer(
97
|event: On<Add, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
98
pending.push(EntityRecord::Added(event.entity()));
99
},
100
);
101
app.add_observer(
102
|event: On<Remove, SyncToRenderWorld>,
103
mut pending: ResMut<PendingSyncEntity>,
104
query: Query<&RenderEntity>| {
105
if let Ok(e) = query.get(event.entity()) {
106
pending.push(EntityRecord::Removed(*e));
107
};
108
},
109
);
110
}
111
}
112
/// Marker component that indicates that its entity needs to be synchronized to the render world.
113
///
114
/// This component is automatically added as a required component by [`ExtractComponentPlugin`] and [`SyncComponentPlugin`].
115
/// For more information see [`SyncWorldPlugin`].
116
///
117
/// NOTE: This component should persist throughout the entity's entire lifecycle.
118
/// If this component is removed from its entity, the entity will be despawned.
119
///
120
/// [`ExtractComponentPlugin`]: crate::extract_component::ExtractComponentPlugin
121
/// [`SyncComponentPlugin`]: crate::sync_component::SyncComponentPlugin
122
#[derive(Component, Copy, Clone, Debug, Default, Reflect)]
123
#[reflect[Component, Default, Clone]]
124
#[component(storage = "SparseSet")]
125
pub struct SyncToRenderWorld;
126
127
/// Component added on the main world entities that are synced to the Render World in order to keep track of the corresponding render world entity.
128
///
129
/// Can also be used as a newtype wrapper for render world entities.
130
#[derive(Component, Deref, Copy, Clone, Debug, Eq, Hash, PartialEq, Reflect)]
131
#[component(clone_behavior = Ignore)]
132
#[reflect(Component, Clone)]
133
pub struct RenderEntity(Entity);
134
impl RenderEntity {
135
#[inline]
136
pub fn id(&self) -> Entity {
137
self.0
138
}
139
}
140
141
impl From<Entity> for RenderEntity {
142
fn from(entity: Entity) -> Self {
143
RenderEntity(entity)
144
}
145
}
146
147
impl ContainsEntity for RenderEntity {
148
fn entity(&self) -> Entity {
149
self.id()
150
}
151
}
152
153
// SAFETY: RenderEntity is a newtype around Entity that derives its comparison traits.
154
unsafe impl EntityEquivalent for RenderEntity {}
155
156
/// Component added on the render world entities to keep track of the corresponding main world entity.
157
///
158
/// Can also be used as a newtype wrapper for main world entities.
159
#[derive(Component, Deref, Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Reflect)]
160
#[reflect(Component, Clone)]
161
pub struct MainEntity(Entity);
162
impl MainEntity {
163
#[inline]
164
pub fn id(&self) -> Entity {
165
self.0
166
}
167
}
168
169
impl From<Entity> for MainEntity {
170
fn from(entity: Entity) -> Self {
171
MainEntity(entity)
172
}
173
}
174
175
impl ContainsEntity for MainEntity {
176
fn entity(&self) -> Entity {
177
self.id()
178
}
179
}
180
181
// SAFETY: RenderEntity is a newtype around Entity that derives its comparison traits.
182
unsafe impl EntityEquivalent for MainEntity {}
183
184
/// A [`HashMap`] pre-configured to use [`EntityHash`] hashing with a [`MainEntity`].
185
pub type MainEntityHashMap<V> = HashMap<MainEntity, V, EntityHash>;
186
187
/// A [`HashSet`] pre-configured to use [`EntityHash`] hashing with a [`MainEntity`]..
188
pub type MainEntityHashSet = HashSet<MainEntity, EntityHash>;
189
190
/// Marker component that indicates that its entity needs to be despawned at the end of the frame.
191
#[derive(Component, Copy, Clone, Debug, Default, Reflect)]
192
#[reflect(Component, Default, Clone)]
193
pub struct TemporaryRenderEntity;
194
195
/// A record enum to what entities with [`SyncToRenderWorld`] have been added or removed.
196
#[derive(Debug)]
197
pub(crate) enum EntityRecord {
198
/// When an entity is spawned on the main world, notify the render world so that it can spawn a corresponding
199
/// entity. This contains the main world entity.
200
Added(Entity),
201
/// When an entity is despawned on the main world, notify the render world so that the corresponding entity can be
202
/// despawned. This contains the render world entity.
203
Removed(RenderEntity),
204
/// When a component is removed from an entity, notify the render world so that the corresponding component can be
205
/// removed. This contains the main world entity.
206
ComponentRemoved(Entity),
207
}
208
209
// Entity Record in MainWorld pending to Sync
210
#[derive(Resource, Default, Deref, DerefMut)]
211
pub(crate) struct PendingSyncEntity {
212
records: Vec<EntityRecord>,
213
}
214
215
pub(crate) fn entity_sync_system(main_world: &mut World, render_world: &mut World) {
216
main_world.resource_scope(|world, mut pending: Mut<PendingSyncEntity>| {
217
// TODO : batching record
218
for record in pending.drain(..) {
219
match record {
220
EntityRecord::Added(e) => {
221
if let Ok(mut main_entity) = world.get_entity_mut(e) {
222
match main_entity.entry::<RenderEntity>() {
223
bevy_ecs::world::ComponentEntry::Occupied(_) => {
224
panic!("Attempting to synchronize an entity that has already been synchronized!");
225
}
226
bevy_ecs::world::ComponentEntry::Vacant(entry) => {
227
let id = render_world.spawn(MainEntity(e)).id();
228
229
entry.insert(RenderEntity(id));
230
}
231
};
232
}
233
}
234
EntityRecord::Removed(render_entity) => {
235
if let Ok(ec) = render_world.get_entity_mut(render_entity.id()) {
236
ec.despawn();
237
};
238
}
239
EntityRecord::ComponentRemoved(main_entity) => {
240
let Some(mut render_entity) = world.get_mut::<RenderEntity>(main_entity) else {
241
continue;
242
};
243
if let Ok(render_world_entity) = render_world.get_entity_mut(render_entity.id()) {
244
// In order to handle components that extract to derived components, we clear the entity
245
// and let the extraction system re-add the components.
246
render_world_entity.despawn();
247
248
let id = render_world.spawn(MainEntity(main_entity)).id();
249
render_entity.0 = id;
250
}
251
},
252
}
253
}
254
});
255
}
256
257
pub(crate) fn despawn_temporary_render_entities(
258
world: &mut World,
259
state: &mut SystemState<Query<Entity, With<TemporaryRenderEntity>>>,
260
mut local: Local<Vec<Entity>>,
261
) {
262
let query = state.get(world);
263
264
local.extend(query.iter());
265
266
// Ensure next frame allocation keeps order
267
local.sort_unstable_by_key(|e| e.index());
268
for e in local.drain(..).rev() {
269
world.despawn(e);
270
}
271
}
272
273
/// This module exists to keep the complex unsafe code out of the main module.
274
///
275
/// The implementations for both [`MainEntity`] and [`RenderEntity`] should stay in sync,
276
/// and are based off of the `&T` implementation in `bevy_ecs`.
277
mod render_entities_world_query_impls {
278
use super::{MainEntity, RenderEntity};
279
280
use bevy_ecs::{
281
archetype::Archetype,
282
component::{ComponentId, Components, Tick},
283
entity::Entity,
284
query::{FilteredAccess, QueryData, ReadOnlyQueryData, ReleaseStateQueryData, WorldQuery},
285
storage::{Table, TableRow},
286
world::{unsafe_world_cell::UnsafeWorldCell, World},
287
};
288
289
/// SAFETY: defers completely to `&RenderEntity` implementation,
290
/// and then only modifies the output safely.
291
unsafe impl WorldQuery for RenderEntity {
292
type Fetch<'w> = <&'static RenderEntity as WorldQuery>::Fetch<'w>;
293
type State = <&'static RenderEntity as WorldQuery>::State;
294
295
fn shrink_fetch<'wlong: 'wshort, 'wshort>(
296
fetch: Self::Fetch<'wlong>,
297
) -> Self::Fetch<'wshort> {
298
fetch
299
}
300
301
#[inline]
302
unsafe fn init_fetch<'w, 's>(
303
world: UnsafeWorldCell<'w>,
304
component_id: &'s ComponentId,
305
last_run: Tick,
306
this_run: Tick,
307
) -> Self::Fetch<'w> {
308
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
309
unsafe {
310
<&RenderEntity as WorldQuery>::init_fetch(world, component_id, last_run, this_run)
311
}
312
}
313
314
const IS_DENSE: bool = <&'static RenderEntity as WorldQuery>::IS_DENSE;
315
316
#[inline]
317
unsafe fn set_archetype<'w, 's>(
318
fetch: &mut Self::Fetch<'w>,
319
component_id: &'s ComponentId,
320
archetype: &'w Archetype,
321
table: &'w Table,
322
) {
323
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
324
unsafe {
325
<&RenderEntity as WorldQuery>::set_archetype(fetch, component_id, archetype, table);
326
}
327
}
328
329
#[inline]
330
unsafe fn set_table<'w, 's>(
331
fetch: &mut Self::Fetch<'w>,
332
&component_id: &'s ComponentId,
333
table: &'w Table,
334
) {
335
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
336
unsafe { <&RenderEntity as WorldQuery>::set_table(fetch, &component_id, table) }
337
}
338
339
fn update_component_access(&component_id: &ComponentId, access: &mut FilteredAccess) {
340
<&RenderEntity as WorldQuery>::update_component_access(&component_id, access);
341
}
342
343
fn init_state(world: &mut World) -> ComponentId {
344
<&RenderEntity as WorldQuery>::init_state(world)
345
}
346
347
fn get_state(components: &Components) -> Option<Self::State> {
348
<&RenderEntity as WorldQuery>::get_state(components)
349
}
350
351
fn matches_component_set(
352
&state: &ComponentId,
353
set_contains_id: &impl Fn(ComponentId) -> bool,
354
) -> bool {
355
<&RenderEntity as WorldQuery>::matches_component_set(&state, set_contains_id)
356
}
357
}
358
359
// SAFETY: Component access of Self::ReadOnly is a subset of Self.
360
// Self::ReadOnly matches exactly the same archetypes/tables as Self.
361
unsafe impl QueryData for RenderEntity {
362
const IS_READ_ONLY: bool = true;
363
type ReadOnly = RenderEntity;
364
type Item<'w, 's> = Entity;
365
366
fn shrink<'wlong: 'wshort, 'wshort, 's>(
367
item: Self::Item<'wlong, 's>,
368
) -> Self::Item<'wshort, 's> {
369
item
370
}
371
372
#[inline(always)]
373
unsafe fn fetch<'w, 's>(
374
state: &'s Self::State,
375
fetch: &mut Self::Fetch<'w>,
376
entity: Entity,
377
table_row: TableRow,
378
) -> Self::Item<'w, 's> {
379
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
380
let component =
381
unsafe { <&RenderEntity as QueryData>::fetch(state, fetch, entity, table_row) };
382
component.id()
383
}
384
}
385
386
// SAFETY: the underlying `Entity` is copied, and no mutable access is provided.
387
unsafe impl ReadOnlyQueryData for RenderEntity {}
388
389
impl ReleaseStateQueryData for RenderEntity {
390
fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> {
391
item
392
}
393
}
394
395
/// SAFETY: defers completely to `&RenderEntity` implementation,
396
/// and then only modifies the output safely.
397
unsafe impl WorldQuery for MainEntity {
398
type Fetch<'w> = <&'static MainEntity as WorldQuery>::Fetch<'w>;
399
type State = <&'static MainEntity as WorldQuery>::State;
400
401
fn shrink_fetch<'wlong: 'wshort, 'wshort>(
402
fetch: Self::Fetch<'wlong>,
403
) -> Self::Fetch<'wshort> {
404
fetch
405
}
406
407
#[inline]
408
unsafe fn init_fetch<'w, 's>(
409
world: UnsafeWorldCell<'w>,
410
component_id: &'s ComponentId,
411
last_run: Tick,
412
this_run: Tick,
413
) -> Self::Fetch<'w> {
414
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
415
unsafe {
416
<&MainEntity as WorldQuery>::init_fetch(world, component_id, last_run, this_run)
417
}
418
}
419
420
const IS_DENSE: bool = <&'static MainEntity as WorldQuery>::IS_DENSE;
421
422
#[inline]
423
unsafe fn set_archetype<'w, 's>(
424
fetch: &mut Self::Fetch<'w>,
425
component_id: &ComponentId,
426
archetype: &'w Archetype,
427
table: &'w Table,
428
) {
429
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
430
unsafe {
431
<&MainEntity as WorldQuery>::set_archetype(fetch, component_id, archetype, table);
432
}
433
}
434
435
#[inline]
436
unsafe fn set_table<'w, 's>(
437
fetch: &mut Self::Fetch<'w>,
438
&component_id: &'s ComponentId,
439
table: &'w Table,
440
) {
441
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
442
unsafe { <&MainEntity as WorldQuery>::set_table(fetch, &component_id, table) }
443
}
444
445
fn update_component_access(&component_id: &ComponentId, access: &mut FilteredAccess) {
446
<&MainEntity as WorldQuery>::update_component_access(&component_id, access);
447
}
448
449
fn init_state(world: &mut World) -> ComponentId {
450
<&MainEntity as WorldQuery>::init_state(world)
451
}
452
453
fn get_state(components: &Components) -> Option<Self::State> {
454
<&MainEntity as WorldQuery>::get_state(components)
455
}
456
457
fn matches_component_set(
458
&state: &ComponentId,
459
set_contains_id: &impl Fn(ComponentId) -> bool,
460
) -> bool {
461
<&MainEntity as WorldQuery>::matches_component_set(&state, set_contains_id)
462
}
463
}
464
465
// SAFETY: Component access of Self::ReadOnly is a subset of Self.
466
// Self::ReadOnly matches exactly the same archetypes/tables as Self.
467
unsafe impl QueryData for MainEntity {
468
const IS_READ_ONLY: bool = true;
469
type ReadOnly = MainEntity;
470
type Item<'w, 's> = Entity;
471
472
fn shrink<'wlong: 'wshort, 'wshort, 's>(
473
item: Self::Item<'wlong, 's>,
474
) -> Self::Item<'wshort, 's> {
475
item
476
}
477
478
#[inline(always)]
479
unsafe fn fetch<'w, 's>(
480
state: &'s Self::State,
481
fetch: &mut Self::Fetch<'w>,
482
entity: Entity,
483
table_row: TableRow,
484
) -> Self::Item<'w, 's> {
485
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
486
let component =
487
unsafe { <&MainEntity as QueryData>::fetch(state, fetch, entity, table_row) };
488
component.id()
489
}
490
}
491
492
// SAFETY: the underlying `Entity` is copied, and no mutable access is provided.
493
unsafe impl ReadOnlyQueryData for MainEntity {}
494
495
impl ReleaseStateQueryData for MainEntity {
496
fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> {
497
item
498
}
499
}
500
}
501
502
#[cfg(test)]
503
mod tests {
504
use bevy_ecs::{
505
component::Component,
506
entity::Entity,
507
lifecycle::{Add, Remove},
508
observer::On,
509
query::With,
510
system::{Query, ResMut},
511
world::World,
512
};
513
514
use super::{
515
entity_sync_system, EntityRecord, MainEntity, PendingSyncEntity, RenderEntity,
516
SyncToRenderWorld,
517
};
518
519
#[derive(Component)]
520
struct RenderDataComponent;
521
522
#[test]
523
fn sync_world() {
524
let mut main_world = World::new();
525
let mut render_world = World::new();
526
main_world.init_resource::<PendingSyncEntity>();
527
528
main_world.add_observer(
529
|event: On<Add, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
530
pending.push(EntityRecord::Added(event.entity()));
531
},
532
);
533
main_world.add_observer(
534
|event: On<Remove, SyncToRenderWorld>,
535
mut pending: ResMut<PendingSyncEntity>,
536
query: Query<&RenderEntity>| {
537
if let Ok(e) = query.get(event.entity()) {
538
pending.push(EntityRecord::Removed(*e));
539
};
540
},
541
);
542
543
// spawn some empty entities for test
544
for _ in 0..99 {
545
main_world.spawn_empty();
546
}
547
548
// spawn
549
let main_entity = main_world
550
.spawn(RenderDataComponent)
551
// indicates that its entity needs to be synchronized to the render world
552
.insert(SyncToRenderWorld)
553
.id();
554
555
entity_sync_system(&mut main_world, &mut render_world);
556
557
let mut q = render_world.query_filtered::<Entity, With<MainEntity>>();
558
559
// Only one synchronized entity
560
assert!(q.iter(&render_world).count() == 1);
561
562
let render_entity = q.single(&render_world).unwrap();
563
let render_entity_component = main_world.get::<RenderEntity>(main_entity).unwrap();
564
565
assert!(render_entity_component.id() == render_entity);
566
567
let main_entity_component = render_world
568
.get::<MainEntity>(render_entity_component.id())
569
.unwrap();
570
571
assert!(main_entity_component.id() == main_entity);
572
573
// despawn
574
main_world.despawn(main_entity);
575
576
entity_sync_system(&mut main_world, &mut render_world);
577
578
// Only one synchronized entity
579
assert!(q.iter(&render_world).count() == 0);
580
}
581
}
582
583