Skip to content

Commit 56f8e52

Browse files
Trashtalk217re0312cBournhonesquewilk10ecoskey
authored
The Cooler 'Retain Rendering World' (#15320)
- Adopted from #14449 - Still fixes #12144. ## Migration Guide The retained render world is a complex change: migrating might take one of a few different forms depending on the patterns you're using. For every example, we specify in which world the code is run. Most of the changes affect render world code, so for the average Bevy user who's using Bevy's high-level rendering APIs, these changes are unlikely to affect your code. ### Spawning entities in the render world Previously, if you spawned an entity with `world.spawn(...)`, `commands.spawn(...)` or some other method in the rendering world, it would be despawned at the end of each frame. In 0.15, this is no longer the case and so your old code could leak entities. This can be mitigated by either re-architecting your code to no longer continuously spawn entities (like you're used to in the main world), or by adding the `bevy_render::world_sync::TemporaryRenderEntity` component to the entity you're spawning. Entities tagged with `TemporaryRenderEntity` will be removed at the end of each frame (like before). ### Extract components with `ExtractComponentPlugin` ``` // main world app.add_plugins(ExtractComponentPlugin::<ComponentToExtract>::default()); ``` `ExtractComponentPlugin` has been changed to only work with synced entities. Entities are automatically synced if `ComponentToExtract` is added to them. However, entities are not "unsynced" if any given `ComponentToExtract` is removed, because an entity may have multiple components to extract. This would cause the other components to no longer get extracted because the entity is not synced. So be careful when only removing extracted components from entities in the render world, because it might leave an entity behind in the render world. The solution here is to avoid only removing extracted components and instead despawn the entire entity. ### Manual extraction using `Extract<Query<(Entity, ...)>>` ```rust // in render world, inspired by bevy_pbr/src/cluster/mod.rs pub fn extract_clusters( mut commands: Commands, views: Extract<Query<(Entity, &Clusters, &Camera)>>, ) { for (entity, clusters, camera) in &views { // some code commands.get_or_spawn(entity).insert(...); } } ``` One of the primary consequences of the retained rendering world is that there's no longer a one-to-one mapping from entity IDs in the main world to entity IDs in the render world. Unlike in Bevy 0.14, Entity 42 in the main world doesn't necessarily map to entity 42 in the render world. Previous code which called `get_or_spawn(main_world_entity)` in the render world (`Extract<Query<(Entity, ...)>>` returns main world entities). Instead, you should use `&RenderEntity` and `render_entity.id()` to get the correct entity in the render world. Note that this entity does need to be synced first in order to have a `RenderEntity`. When performing manual abstraction, this won't happen automatically (like with `ExtractComponentPlugin`) so add a `SyncToRenderWorld` marker component to the entities you want to extract. This results in the following code: ```rust // in render world, inspired by bevy_pbr/src/cluster/mod.rs pub fn extract_clusters( mut commands: Commands, views: Extract<Query<(&RenderEntity, &Clusters, &Camera)>>, ) { for (render_entity, clusters, camera) in &views { // some code commands.get_or_spawn(render_entity.id()).insert(...); } } // in main world, when spawning world.spawn(Clusters::default(), Camera::default(), SyncToRenderWorld) ``` ### Looking up `Entity` ids in the render world As previously stated, there's now no correspondence between main world and render world `Entity` identifiers. Querying for `Entity` in the render world will return the `Entity` id in the render world: query for `MainEntity` (and use its `id()` method) to get the corresponding entity in the main world. This is also a good way to tell the difference between synced and unsynced entities in the render world, because unsynced entities won't have a `MainEntity` component. --------- Co-authored-by: re0312 <[email protected]> Co-authored-by: re0312 <[email protected]> Co-authored-by: Periwink <[email protected]> Co-authored-by: Anselmo Sampietro <[email protected]> Co-authored-by: Emerson Coskey <[email protected]> Co-authored-by: Alice Cecile <[email protected]> Co-authored-by: Christian Hughes <[email protected]>
1 parent 01dce47 commit 56f8e52

File tree

28 files changed

+690
-241
lines changed

28 files changed

+690
-241
lines changed

crates/bevy_core_pipeline/src/auto_exposure/buffers.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use bevy_ecs::prelude::*;
22
use bevy_render::{
33
render_resource::{StorageBuffer, UniformBuffer},
44
renderer::{RenderDevice, RenderQueue},
5+
world_sync::RenderEntity,
56
Extract,
67
};
78
use bevy_utils::{Entry, HashMap};
@@ -26,13 +27,13 @@ pub(super) struct ExtractedStateBuffers {
2627

2728
pub(super) fn extract_buffers(
2829
mut commands: Commands,
29-
changed: Extract<Query<(Entity, &AutoExposure), Changed<AutoExposure>>>,
30+
changed: Extract<Query<(&RenderEntity, &AutoExposure), Changed<AutoExposure>>>,
3031
mut removed: Extract<RemovedComponents<AutoExposure>>,
3132
) {
3233
commands.insert_resource(ExtractedStateBuffers {
3334
changed: changed
3435
.iter()
35-
.map(|(entity, settings)| (entity, settings.clone()))
36+
.map(|(entity, settings)| (entity.id(), settings.clone()))
3637
.collect(),
3738
removed: removed.read().collect(),
3839
});

crates/bevy_core_pipeline/src/core_2d/camera_2d.rs

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{
44
};
55
use bevy_ecs::prelude::*;
66
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
7+
use bevy_render::world_sync::SyncToRenderWorld;
78
use bevy_render::{
89
camera::{
910
Camera, CameraMainTextureUsages, CameraProjection, CameraRenderGraph,
@@ -35,6 +36,8 @@ pub struct Camera2dBundle {
3536
pub deband_dither: DebandDither,
3637
pub main_texture_usages: CameraMainTextureUsages,
3738
pub msaa: Msaa,
39+
/// Marker component that indicates that its entity needs to be synchronized to the render world
40+
pub sync: SyncToRenderWorld,
3841
}
3942

4043
impl Default for Camera2dBundle {
@@ -55,6 +58,7 @@ impl Default for Camera2dBundle {
5558
deband_dither: DebandDither::Disabled,
5659
main_texture_usages: Default::default(),
5760
msaa: Default::default(),
61+
sync: Default::default(),
5862
}
5963
}
6064
}
@@ -88,6 +92,7 @@ impl Camera2dBundle {
8892
deband_dither: DebandDither::Disabled,
8993
main_texture_usages: Default::default(),
9094
msaa: Default::default(),
95+
sync: Default::default(),
9196
}
9297
}
9398
}

crates/bevy_core_pipeline/src/core_2d/mod.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ use bevy_render::{
5757
renderer::RenderDevice,
5858
texture::TextureCache,
5959
view::{Msaa, ViewDepthTexture},
60+
world_sync::RenderEntity,
6061
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
6162
};
6263

@@ -357,11 +358,10 @@ impl CachedRenderPipelinePhaseItem for Transparent2d {
357358
}
358359

359360
pub fn extract_core_2d_camera_phases(
360-
mut commands: Commands,
361361
mut transparent_2d_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
362362
mut opaque_2d_phases: ResMut<ViewBinnedRenderPhases<Opaque2d>>,
363363
mut alpha_mask_2d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask2d>>,
364-
cameras_2d: Extract<Query<(Entity, &Camera), With<Camera2d>>>,
364+
cameras_2d: Extract<Query<(&RenderEntity, &Camera), With<Camera2d>>>,
365365
mut live_entities: Local<EntityHashSet>,
366366
) {
367367
live_entities.clear();
@@ -370,8 +370,7 @@ pub fn extract_core_2d_camera_phases(
370370
if !camera.is_active {
371371
continue;
372372
}
373-
374-
commands.get_or_spawn(entity);
373+
let entity = entity.id();
375374
transparent_2d_phases.insert_or_clear(entity);
376375
opaque_2d_phases.insert_or_clear(entity);
377376
alpha_mask_2d_phases.insert_or_clear(entity);

crates/bevy_core_pipeline/src/core_3d/camera_3d.rs

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use bevy_render::{
1010
primitives::Frustum,
1111
render_resource::{LoadOp, TextureUsages},
1212
view::{ColorGrading, Msaa, VisibleEntities},
13+
world_sync::SyncToRenderWorld,
1314
};
1415
use bevy_transform::prelude::{GlobalTransform, Transform};
1516
use serde::{Deserialize, Serialize};
@@ -153,6 +154,8 @@ pub struct Camera3dBundle {
153154
pub exposure: Exposure,
154155
pub main_texture_usages: CameraMainTextureUsages,
155156
pub msaa: Msaa,
157+
/// Marker component that indicates that its entity needs to be synchronized to the render world
158+
pub sync: SyncToRenderWorld,
156159
}
157160

158161
// NOTE: ideally Perspective and Orthographic defaults can share the same impl, but sadly it breaks rust's type inference
@@ -173,6 +176,7 @@ impl Default for Camera3dBundle {
173176
main_texture_usages: Default::default(),
174177
deband_dither: DebandDither::Enabled,
175178
msaa: Default::default(),
179+
sync: Default::default(),
176180
}
177181
}
178182
}

crates/bevy_core_pipeline/src/core_3d/mod.rs

+14-9
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ use bevy_render::{
9191
renderer::RenderDevice,
9292
texture::{BevyDefault, ColorAttachment, Image, TextureCache},
9393
view::{ExtractedView, ViewDepthTexture, ViewTarget},
94+
world_sync::RenderEntity,
9495
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
9596
};
9697
use bevy_utils::{tracing::warn, HashMap};
@@ -504,23 +505,21 @@ impl CachedRenderPipelinePhaseItem for Transparent3d {
504505
}
505506

506507
pub fn extract_core_3d_camera_phases(
507-
mut commands: Commands,
508508
mut opaque_3d_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
509509
mut alpha_mask_3d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
510510
mut transmissive_3d_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
511511
mut transparent_3d_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
512-
cameras_3d: Extract<Query<(Entity, &Camera), With<Camera3d>>>,
512+
cameras_3d: Extract<Query<(&RenderEntity, &Camera), With<Camera3d>>>,
513513
mut live_entities: Local<EntityHashSet>,
514514
) {
515515
live_entities.clear();
516516

517-
for (entity, camera) in &cameras_3d {
517+
for (render_entity, camera) in &cameras_3d {
518518
if !camera.is_active {
519519
continue;
520520
}
521521

522-
commands.get_or_spawn(entity);
523-
522+
let entity = render_entity.id();
524523
opaque_3d_phases.insert_or_clear(entity);
525524
alpha_mask_3d_phases.insert_or_clear(entity);
526525
transmissive_3d_phases.insert_or_clear(entity);
@@ -545,7 +544,7 @@ pub fn extract_camera_prepass_phase(
545544
cameras_3d: Extract<
546545
Query<
547546
(
548-
Entity,
547+
&RenderEntity,
549548
&Camera,
550549
Has<DepthPrepass>,
551550
Has<NormalPrepass>,
@@ -559,13 +558,20 @@ pub fn extract_camera_prepass_phase(
559558
) {
560559
live_entities.clear();
561560

562-
for (entity, camera, depth_prepass, normal_prepass, motion_vector_prepass, deferred_prepass) in
563-
cameras_3d.iter()
561+
for (
562+
render_entity,
563+
camera,
564+
depth_prepass,
565+
normal_prepass,
566+
motion_vector_prepass,
567+
deferred_prepass,
568+
) in cameras_3d.iter()
564569
{
565570
if !camera.is_active {
566571
continue;
567572
}
568573

574+
let entity = render_entity.id();
569575
if depth_prepass || normal_prepass || motion_vector_prepass {
570576
opaque_3d_prepass_phases.insert_or_clear(entity);
571577
alpha_mask_3d_prepass_phases.insert_or_clear(entity);
@@ -581,7 +587,6 @@ pub fn extract_camera_prepass_phase(
581587
opaque_3d_deferred_phases.remove(&entity);
582588
alpha_mask_3d_deferred_phases.remove(&entity);
583589
}
584-
585590
live_entities.insert(entity);
586591

587592
commands

crates/bevy_core_pipeline/src/dof/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ use bevy_render::{
5151
prepare_view_targets, ExtractedView, Msaa, ViewDepthTexture, ViewTarget, ViewUniform,
5252
ViewUniformOffset, ViewUniforms,
5353
},
54+
world_sync::RenderEntity,
5455
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
5556
};
5657
use bevy_utils::{info_once, prelude::default, warn_once};
@@ -809,7 +810,7 @@ impl SpecializedRenderPipeline for DepthOfFieldPipeline {
809810
/// Extracts all [`DepthOfField`] components into the render world.
810811
fn extract_depth_of_field_settings(
811812
mut commands: Commands,
812-
mut query: Extract<Query<(Entity, &DepthOfField, &Projection)>>,
813+
mut query: Extract<Query<(&RenderEntity, &DepthOfField, &Projection)>>,
813814
) {
814815
if !DEPTH_TEXTURE_SAMPLING_SUPPORTED {
815816
info_once!(
@@ -819,6 +820,7 @@ fn extract_depth_of_field_settings(
819820
}
820821

821822
for (entity, depth_of_field, projection) in query.iter_mut() {
823+
let entity = entity.id();
822824
// Depth of field is nonsensical without a perspective projection.
823825
let Projection::Perspective(ref perspective_projection) = *projection else {
824826
continue;

crates/bevy_core_pipeline/src/taa/mod.rs

+15-8
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use bevy_render::{
3232
renderer::{RenderContext, RenderDevice},
3333
texture::{BevyDefault, CachedTexture, TextureCache},
3434
view::{ExtractedView, Msaa, ViewTarget},
35+
world_sync::RenderEntity,
3536
ExtractSchedule, MainWorld, Render, RenderApp, RenderSet,
3637
};
3738
use bevy_utils::tracing::warn;
@@ -351,20 +352,26 @@ impl SpecializedRenderPipeline for TaaPipeline {
351352
}
352353

353354
fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut<MainWorld>) {
354-
let mut cameras_3d = main_world
355-
.query_filtered::<(Entity, &Camera, &Projection, &mut TemporalAntiAliasing), (
356-
With<Camera3d>,
357-
With<TemporalJitter>,
358-
With<DepthPrepass>,
359-
With<MotionVectorPrepass>,
360-
)>();
355+
let mut cameras_3d = main_world.query_filtered::<(
356+
&RenderEntity,
357+
&Camera,
358+
&Projection,
359+
&mut TemporalAntiAliasing,
360+
), (
361+
With<Camera3d>,
362+
With<TemporalJitter>,
363+
With<DepthPrepass>,
364+
With<MotionVectorPrepass>,
365+
)>();
361366

362367
for (entity, camera, camera_projection, mut taa_settings) in
363368
cameras_3d.iter_mut(&mut main_world)
364369
{
365370
let has_perspective_projection = matches!(camera_projection, Projection::Perspective(_));
366371
if camera.is_active && has_perspective_projection {
367-
commands.get_or_spawn(entity).insert(taa_settings.clone());
372+
commands
373+
.get_or_spawn(entity.id())
374+
.insert(taa_settings.clone());
368375
taa_settings.reset = false;
369376
}
370377
}

crates/bevy_gizmos/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ use {
103103
ShaderStages, ShaderType, VertexFormat,
104104
},
105105
renderer::RenderDevice,
106+
world_sync::TemporaryRenderEntity,
106107
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
107108
},
108109
bytemuck::cast_slice,
@@ -113,7 +114,6 @@ use {
113114
any(feature = "bevy_pbr", feature = "bevy_sprite"),
114115
))]
115116
use bevy_render::render_resource::{VertexAttribute, VertexBufferLayout, VertexStepMode};
116-
117117
use bevy_time::Fixed;
118118
use bevy_utils::TypeIdMap;
119119
use config::{
@@ -459,6 +459,7 @@ fn extract_gizmo_data(
459459
(*handle).clone_weak(),
460460
#[cfg(any(feature = "bevy_pbr", feature = "bevy_sprite"))]
461461
config::GizmoMeshConfig::from(config),
462+
TemporaryRenderEntity,
462463
));
463464
}
464465
}

crates/bevy_pbr/src/bundle.rs

+7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use bevy_render::{
1515
mesh::Mesh,
1616
primitives::{CascadesFrusta, CubemapFrusta, Frustum},
1717
view::{InheritedVisibility, ViewVisibility, Visibility},
18+
world_sync::SyncToRenderWorld,
1819
};
1920
use bevy_transform::components::{GlobalTransform, Transform};
2021

@@ -108,6 +109,8 @@ pub struct PointLightBundle {
108109
pub inherited_visibility: InheritedVisibility,
109110
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
110111
pub view_visibility: ViewVisibility,
112+
/// Marker component that indicates that its entity needs to be synchronized to the render world
113+
pub sync: SyncToRenderWorld,
111114
}
112115

113116
/// A component bundle for spot light entities
@@ -124,6 +127,8 @@ pub struct SpotLightBundle {
124127
pub inherited_visibility: InheritedVisibility,
125128
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
126129
pub view_visibility: ViewVisibility,
130+
/// Marker component that indicates that its entity needs to be synchronized to the render world
131+
pub sync: SyncToRenderWorld,
127132
}
128133

129134
/// A component bundle for [`DirectionalLight`] entities.
@@ -142,4 +147,6 @@ pub struct DirectionalLightBundle {
142147
pub inherited_visibility: InheritedVisibility,
143148
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
144149
pub view_visibility: ViewVisibility,
150+
/// Marker component that indicates that its entity needs to be synchronized to the render world
151+
pub sync: SyncToRenderWorld,
145152
}

crates/bevy_pbr/src/cluster/mod.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use bevy_render::{
2020
UniformBuffer,
2121
},
2222
renderer::{RenderDevice, RenderQueue},
23+
world_sync::RenderEntity,
2324
Extract,
2425
};
2526
use bevy_utils::{hashbrown::HashSet, tracing::warn};
@@ -525,7 +526,8 @@ pub(crate) fn clusterable_object_order(
525526
/// Extracts clusters from the main world from the render world.
526527
pub fn extract_clusters(
527528
mut commands: Commands,
528-
views: Extract<Query<(Entity, &Clusters, &Camera)>>,
529+
views: Extract<Query<(&RenderEntity, &Clusters, &Camera)>>,
530+
mapper: Extract<Query<&RenderEntity>>,
529531
) {
530532
for (entity, clusters, camera) in &views {
531533
if !camera.is_active {
@@ -544,13 +546,15 @@ pub fn extract_clusters(
544546
cluster_objects.spot_light_count as u32,
545547
));
546548
for clusterable_entity in &cluster_objects.entities {
547-
data.push(ExtractedClusterableObjectElement::ClusterableObjectEntity(
548-
*clusterable_entity,
549-
));
549+
if let Ok(entity) = mapper.get(*clusterable_entity) {
550+
data.push(ExtractedClusterableObjectElement::ClusterableObjectEntity(
551+
entity.id(),
552+
));
553+
}
550554
}
551555
}
552556

553-
commands.get_or_spawn(entity).insert((
557+
commands.get_or_spawn(entity.id()).insert((
554558
ExtractedClusterableObjects { data },
555559
ExtractedClusterConfig {
556560
near: clusters.near,

crates/bevy_pbr/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,9 @@ impl Plugin for PbrPlugin {
439439
)
440440
.init_resource::<LightMeta>();
441441

442+
render_app.world_mut().observe(add_light_view_entities);
443+
render_app.world_mut().observe(remove_light_view_entities);
444+
442445
let shadow_pass_node = ShadowPassNode::new(render_app.world_mut());
443446
let mut graph = render_app.world_mut().resource_mut::<RenderGraph>();
444447
let draw_3d_graph = graph.get_sub_graph_mut(Core3d).unwrap();

0 commit comments

Comments
 (0)