Skip to content

Commit 67633b3

Browse files
authored
Convert RenderTarget to Component (#20917)
# Objective #20830 created the possibility that we may want to have render targets that produce a number of outputs, e.g. depth and normals. This is a first step towards something like that (e.g. a `RendersTo` relation) by converting `RenderTarget` to be a component. This is also useful for out-of-tree render targets that may want to do something like `#[require(RenderTarget::Image)]` once BSN lands. ## Solution Make it a component.
1 parent 63af390 commit 67633b3

File tree

24 files changed

+151
-149
lines changed

24 files changed

+151
-149
lines changed

crates/bevy_camera/src/camera.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,8 @@ pub enum ViewportConversionError {
343343
CameraMainTextureUsages,
344344
VisibleEntities,
345345
Transform,
346-
Visibility
346+
Visibility,
347+
RenderTarget
347348
)]
348349
pub struct Camera {
349350
/// If set, this camera will render to the given [`Viewport`] rectangle within the configured [`RenderTarget`].
@@ -356,8 +357,7 @@ pub struct Camera {
356357
/// Computed values for this camera, such as the projection matrix and the render target size.
357358
#[reflect(ignore, clone)]
358359
pub computed: ComputedCameraValues,
359-
/// The "target" that this camera will render to.
360-
pub target: RenderTarget,
360+
// todo: reflect this when #6042 lands
361361
/// The [`CameraOutputMode`] for this camera.
362362
pub output_mode: CameraOutputMode,
363363
/// Controls when MSAA writeback occurs for this camera.
@@ -385,7 +385,6 @@ impl Default for Camera {
385385
order: 0,
386386
viewport: None,
387387
computed: Default::default(),
388-
target: Default::default(),
389388
output_mode: Default::default(),
390389
msaa_writeback: MsaaWriteback::default(),
391390
clear_color: Default::default(),
@@ -811,7 +810,7 @@ impl Default for CameraOutputMode {
811810

812811
/// The "target" that a [`Camera`] will render to. For example, this could be a `Window`
813812
/// swapchain or an [`Image`].
814-
#[derive(Debug, Clone, Reflect, From)]
813+
#[derive(Component, Debug, Clone, Reflect, From)]
815814
#[reflect(Clone)]
816815
pub enum RenderTarget {
817816
/// Window to which the camera's view is rendered.

crates/bevy_core_pipeline/src/oit/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Order Independent Transparency (OIT) for 3d rendering. See [`OrderIndependentTransparencyPlugin`] for more details.
22
33
use bevy_app::prelude::*;
4-
use bevy_camera::{Camera, Camera3d};
4+
use bevy_camera::{Camera3d, RenderTarget};
55
use bevy_ecs::{component::*, lifecycle::ComponentHook, prelude::*};
66
use bevy_math::UVec2;
77
use bevy_platform::collections::HashSet;
@@ -135,8 +135,8 @@ impl Plugin for OrderIndependentTransparencyPlugin {
135135
// bevy reuses the same depth texture so we need to set this on all cameras with the same render target.
136136
fn configure_depth_texture_usages(
137137
p: Query<Entity, With<PrimaryWindow>>,
138-
cameras: Query<(&Camera, Has<OrderIndependentTransparencySettings>)>,
139-
mut new_cameras: Query<(&mut Camera3d, &Camera), Added<Camera3d>>,
138+
cameras: Query<(&RenderTarget, Has<OrderIndependentTransparencySettings>)>,
139+
mut new_cameras: Query<(&mut Camera3d, &RenderTarget), Added<Camera3d>>,
140140
) {
141141
if new_cameras.is_empty() {
142142
return;
@@ -145,15 +145,15 @@ fn configure_depth_texture_usages(
145145
// Find all the render target that potentially uses OIT
146146
let primary_window = p.single().ok();
147147
let mut render_target_has_oit = <HashSet<_>>::default();
148-
for (camera, has_oit) in &cameras {
148+
for (render_target, has_oit) in &cameras {
149149
if has_oit {
150-
render_target_has_oit.insert(camera.target.normalize(primary_window));
150+
render_target_has_oit.insert(render_target.normalize(primary_window));
151151
}
152152
}
153153

154154
// Update the depth texture usage for cameras with a render target that has OIT
155-
for (mut camera_3d, camera) in &mut new_cameras {
156-
if render_target_has_oit.contains(&camera.target.normalize(primary_window)) {
155+
for (mut camera_3d, render_target) in &mut new_cameras {
156+
if render_target_has_oit.contains(&render_target.normalize(primary_window)) {
157157
let mut usages = TextureUsages::from(camera_3d.depth_texture_usages);
158158
usages |= TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING;
159159
camera_3d.depth_texture_usages = usages.into();

crates/bevy_dev_tools/src/picking_debug.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use bevy_app::prelude::*;
44
use bevy_camera::visibility::Visibility;
5-
use bevy_camera::Camera;
5+
use bevy_camera::{Camera, RenderTarget};
66
use bevy_color::prelude::*;
77
use bevy_ecs::prelude::*;
88
use bevy_picking::backend::HitData;
@@ -243,7 +243,7 @@ pub fn update_debug_data(
243243
/// Draw text on each cursor with debug info
244244
pub fn debug_draw(
245245
mut commands: Commands,
246-
camera_query: Query<(Entity, &Camera)>,
246+
camera_query: Query<(Entity, &Camera, &RenderTarget)>,
247247
primary_window: Query<Entity, With<bevy_window::PrimaryWindow>>,
248248
pointers: Query<(Entity, &PointerId, &PointerDebug)>,
249249
scale: Res<UiScale>,
@@ -254,17 +254,16 @@ pub fn debug_draw(
254254
};
255255
let text = format!("{id:?}\n{debug}");
256256

257-
for (camera, _) in camera_query.iter().filter(|(_, camera)| {
258-
camera
259-
.target
257+
for (camera, _, _) in camera_query.iter().filter(|(_, _, render_target)| {
258+
render_target
260259
.normalize(primary_window.single().ok())
261260
.is_some_and(|target| target == pointer_location.target)
262261
}) {
263262
let mut pointer_pos = pointer_location.position;
264263
if let Some(viewport) = camera_query
265264
.get(camera)
266265
.ok()
267-
.and_then(|(_, camera)| camera.logical_viewport_rect())
266+
.and_then(|(_, camera, _)| camera.logical_viewport_rect())
268267
{
269268
pointer_pos -= viewport.min;
270269
}

crates/bevy_picking/src/backend.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ pub mod ray {
129129
//! Types and systems for constructing rays from cameras and pointers.
130130
131131
use crate::backend::prelude::{PointerId, PointerLocation};
132-
use bevy_camera::Camera;
132+
use bevy_camera::{Camera, RenderTarget};
133133
use bevy_ecs::prelude::*;
134134
use bevy_math::Ray3d;
135135
use bevy_platform::collections::{hash_map::Iter, HashMap};
@@ -196,20 +196,24 @@ pub mod ray {
196196
pub fn repopulate(
197197
mut ray_map: ResMut<Self>,
198198
primary_window_entity: Query<Entity, With<PrimaryWindow>>,
199-
cameras: Query<(Entity, &Camera, &GlobalTransform)>,
199+
cameras: Query<(Entity, &Camera, &RenderTarget, &GlobalTransform)>,
200200
pointers: Query<(&PointerId, &PointerLocation)>,
201201
) {
202202
ray_map.map.clear();
203203

204-
for (camera_entity, camera, camera_tfm) in &cameras {
204+
for (camera_entity, camera, render_target, camera_tfm) in &cameras {
205205
if !camera.is_active {
206206
continue;
207207
}
208208

209209
for (&pointer_id, pointer_loc) in &pointers {
210-
if let Some(ray) =
211-
make_ray(&primary_window_entity, camera, camera_tfm, pointer_loc)
212-
{
210+
if let Some(ray) = make_ray(
211+
&primary_window_entity,
212+
camera,
213+
render_target,
214+
camera_tfm,
215+
pointer_loc,
216+
) {
213217
ray_map
214218
.map
215219
.insert(RayId::new(camera_entity, pointer_id), ray);
@@ -222,11 +226,12 @@ pub mod ray {
222226
fn make_ray(
223227
primary_window_entity: &Query<Entity, With<PrimaryWindow>>,
224228
camera: &Camera,
229+
render_target: &RenderTarget,
225230
camera_tfm: &GlobalTransform,
226231
pointer_loc: &PointerLocation,
227232
) -> Option<Ray3d> {
228233
let pointer_loc = pointer_loc.location()?;
229-
if !pointer_loc.is_in_viewport(camera, primary_window_entity) {
234+
if !pointer_loc.is_in_viewport(camera, render_target, primary_window_entity) {
230235
return None;
231236
}
232237
camera

crates/bevy_picking/src/pointer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
//! The purpose of this module is primarily to provide a common interface that can be
99
//! driven by lower-level input devices and consumed by higher-level interaction systems.
1010
11-
use bevy_camera::Camera;
1211
use bevy_camera::NormalizedRenderTarget;
12+
use bevy_camera::{Camera, RenderTarget};
1313
use bevy_ecs::prelude::*;
1414
use bevy_input::mouse::MouseScrollUnit;
1515
use bevy_math::Vec2;
@@ -223,10 +223,10 @@ impl Location {
223223
pub fn is_in_viewport(
224224
&self,
225225
camera: &Camera,
226+
render_target: &RenderTarget,
226227
primary_window: &Query<Entity, With<PrimaryWindow>>,
227228
) -> bool {
228-
if camera
229-
.target
229+
if render_target
230230
.normalize(Some(match primary_window.single() {
231231
Ok(w) => w,
232232
Err(_) => return false,

crates/bevy_render/src/camera.rs

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use bevy_camera::{
2121
visibility::{self, RenderLayers, VisibleEntities},
2222
Camera, Camera2d, Camera3d, CameraMainTextureUsages, CameraOutputMode, CameraUpdateSystems,
2323
ClearColor, ClearColorConfig, Exposure, ManualTextureViewHandle, MsaaWriteback,
24-
NormalizedRenderTarget, Projection, RenderTargetInfo, Viewport,
24+
NormalizedRenderTarget, Projection, RenderTarget, RenderTargetInfo, Viewport,
2525
};
2626
use bevy_derive::{Deref, DerefMut};
2727
use bevy_ecs::{
@@ -312,7 +312,7 @@ pub fn camera_system(
312312
windows: Query<(Entity, &Window)>,
313313
images: Res<Assets<Image>>,
314314
manual_texture_views: Res<ManualTextureViews>,
315-
mut cameras: Query<(&mut Camera, &mut Projection)>,
315+
mut cameras: Query<(&mut Camera, &RenderTarget, &mut Projection)>,
316316
) -> Result<(), BevyError> {
317317
let primary_window = primary_window.iter().next();
318318

@@ -333,13 +333,13 @@ pub fn camera_system(
333333
})
334334
.collect();
335335

336-
for (mut camera, mut camera_projection) in &mut cameras {
336+
for (mut camera, render_target, mut camera_projection) in &mut cameras {
337337
let mut viewport_size = camera
338338
.viewport
339339
.as_ref()
340340
.map(|viewport| viewport.physical_size);
341341

342-
if let Some(normalized_target) = &camera.target.normalize(primary_window)
342+
if let Some(normalized_target) = render_target.normalize(primary_window)
343343
&& (normalized_target.is_changed(&changed_window_ids, &changed_image_handles)
344344
|| camera.is_added()
345345
|| camera_projection.is_changed()
@@ -423,18 +423,21 @@ pub fn extract_cameras(
423423
Entity,
424424
RenderEntity,
425425
&Camera,
426+
&RenderTarget,
426427
&CameraRenderGraph,
427428
&GlobalTransform,
428429
&VisibleEntities,
429430
&Frustum,
430-
Has<Hdr>,
431-
Option<&ColorGrading>,
432-
Option<&Exposure>,
433-
Option<&TemporalJitter>,
434-
Option<&MipBias>,
435-
Option<&RenderLayers>,
436-
Option<&Projection>,
437-
Has<NoIndirectDrawing>,
431+
(
432+
Has<Hdr>,
433+
Option<&ColorGrading>,
434+
Option<&Exposure>,
435+
Option<&TemporalJitter>,
436+
Option<&MipBias>,
437+
Option<&RenderLayers>,
438+
Option<&Projection>,
439+
Has<NoIndirectDrawing>,
440+
),
438441
)>,
439442
>,
440443
primary_window: Extract<Query<Entity, With<PrimaryWindow>>>,
@@ -457,18 +460,21 @@ pub fn extract_cameras(
457460
main_entity,
458461
render_entity,
459462
camera,
463+
render_target,
460464
camera_render_graph,
461465
transform,
462466
visible_entities,
463467
frustum,
464-
hdr,
465-
color_grading,
466-
exposure,
467-
temporal_jitter,
468-
mip_bias,
469-
render_layers,
470-
projection,
471-
no_indirect_drawing,
468+
(
469+
hdr,
470+
color_grading,
471+
exposure,
472+
temporal_jitter,
473+
mip_bias,
474+
render_layers,
475+
projection,
476+
no_indirect_drawing,
477+
),
472478
) in query.iter()
473479
{
474480
if !camera.is_active {
@@ -523,7 +529,7 @@ pub fn extract_cameras(
523529
let mut commands = commands.entity(render_entity);
524530
commands.insert((
525531
ExtractedCamera {
526-
target: camera.target.normalize(primary_window),
532+
target: render_target.normalize(primary_window),
527533
viewport: camera.viewport.clone(),
528534
physical_viewport_size: Some(viewport_size),
529535
physical_target_size: Some(target_size),

crates/bevy_sprite/src/picking_backend.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use bevy_app::prelude::*;
1515
use bevy_asset::prelude::*;
1616
use bevy_camera::{
1717
visibility::{RenderLayers, ViewVisibility},
18-
Camera, Projection,
18+
Camera, Projection, RenderTarget,
1919
};
2020
use bevy_color::Alpha;
2121
use bevy_ecs::prelude::*;
@@ -88,6 +88,7 @@ fn sprite_picking(
8888
cameras: Query<(
8989
Entity,
9090
&Camera,
91+
&RenderTarget,
9192
&GlobalTransform,
9293
&Projection,
9394
Has<SpritePickingCamera>,
@@ -135,6 +136,7 @@ fn sprite_picking(
135136
let Ok((
136137
cam_entity,
137138
camera,
139+
render_target,
138140
cam_transform,
139141
Projection::Orthographic(cam_ortho),
140142
cam_can_pick,
@@ -156,8 +158,7 @@ fn sprite_picking(
156158
None
157159
})?;
158160

159-
if camera
160-
.target
161+
if render_target
161162
.normalize(primary_window)
162163
.is_none_or(|x| x != location.target)
163164
{

crates/bevy_ui/src/focus.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
ui_transform::UiGlobalTransform, ComputedNode, ComputedUiTargetCamera, Node, OverrideClip,
33
UiStack,
44
};
5-
use bevy_camera::{visibility::InheritedVisibility, Camera, NormalizedRenderTarget};
5+
use bevy_camera::{visibility::InheritedVisibility, Camera, NormalizedRenderTarget, RenderTarget};
66
use bevy_ecs::{
77
change_detection::DetectChangesMut,
88
entity::{ContainsEntity, Entity},
@@ -149,7 +149,7 @@ pub struct NodeQuery {
149149
pub fn ui_focus_system(
150150
mut hovered_nodes: Local<Vec<Entity>>,
151151
mut state: Local<State>,
152-
camera_query: Query<(Entity, &Camera)>,
152+
camera_query: Query<(Entity, &Camera, &RenderTarget)>,
153153
primary_window: Query<Entity, With<PrimaryWindow>>,
154154
windows: Query<&Window>,
155155
mouse_button_input: Res<ButtonInput<MouseButton>>,
@@ -189,10 +189,10 @@ pub fn ui_focus_system(
189189

190190
let camera_cursor_positions: HashMap<Entity, Vec2> = camera_query
191191
.iter()
192-
.filter_map(|(entity, camera)| {
192+
.filter_map(|(entity, camera, render_target)| {
193193
// Interactions are only supported for cameras rendering to a window.
194194
let Some(NormalizedRenderTarget::Window(window_ref)) =
195-
camera.target.normalize(primary_window)
195+
render_target.normalize(primary_window)
196196
else {
197197
return None;
198198
};

0 commit comments

Comments
 (0)