diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 96a13fd5d504d..d552000d0b5fd 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -28,7 +28,7 @@ use bevy_ecs::{ world::DeferredWorld, }; use bevy_image::Image; -use bevy_math::{ops, vec2, Dir3, Mat4, Ray3d, Rect, URect, UVec2, UVec4, Vec2, Vec3}; +use bevy_math::{ops, vec2, Dir3, FloatOrd, Mat4, Ray3d, Rect, URect, UVec2, UVec4, Vec2, Vec3}; use bevy_reflect::prelude::*; use bevy_render_macros::ExtractComponent; use bevy_transform::components::{GlobalTransform, Transform}; @@ -718,12 +718,37 @@ pub enum RenderTarget { /// Window to which the camera's view is rendered. Window(WindowRef), /// Image to which the camera's view is rendered. - Image(Handle), + Image(ImageRenderTarget), /// Texture View to which the camera's view is rendered. /// Useful when the texture view needs to be created outside of Bevy, for example OpenXR. TextureView(ManualTextureViewHandle), } +/// A render target that renders to an [`Image`]. +#[derive(Debug, Clone, Reflect, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct ImageRenderTarget { + /// The image to render to. + pub handle: Handle, + /// The scale factor of the render target image, corresponding to the scale + /// factor for a window target. This should almost always be 1.0. + pub scale_factor: FloatOrd, +} + +impl From> for RenderTarget { + fn from(handle: Handle) -> Self { + Self::Image(handle.into()) + } +} + +impl From> for ImageRenderTarget { + fn from(handle: Handle) -> Self { + Self { + handle, + scale_factor: FloatOrd(1.0), + } + } +} + impl Default for RenderTarget { fn default() -> Self { Self::Window(Default::default()) @@ -738,7 +763,7 @@ pub enum NormalizedRenderTarget { /// Window to which the camera's view is rendered. Window(NormalizedWindowRef), /// Image to which the camera's view is rendered. - Image(Handle), + Image(ImageRenderTarget), /// Texture View to which the camera's view is rendered. /// Useful when the texture view needs to be created outside of Bevy, for example OpenXR. TextureView(ManualTextureViewHandle), @@ -759,8 +784,8 @@ impl RenderTarget { /// Get a handle to the render target's image, /// or `None` if the render target is another variant. pub fn as_image(&self) -> Option<&Handle> { - if let Self::Image(handle) = self { - Some(handle) + if let Self::Image(image_target) = self { + Some(&image_target.handle) } else { None } @@ -778,9 +803,9 @@ impl NormalizedRenderTarget { NormalizedRenderTarget::Window(window_ref) => windows .get(&window_ref.entity()) .and_then(|window| window.swap_chain_texture_view.as_ref()), - NormalizedRenderTarget::Image(image_handle) => { - images.get(image_handle).map(|image| &image.texture_view) - } + NormalizedRenderTarget::Image(image_target) => images + .get(&image_target.handle) + .map(|image| &image.texture_view), NormalizedRenderTarget::TextureView(id) => { manual_texture_views.get(id).map(|tex| &tex.texture_view) } @@ -798,9 +823,9 @@ impl NormalizedRenderTarget { NormalizedRenderTarget::Window(window_ref) => windows .get(&window_ref.entity()) .and_then(|window| window.swap_chain_texture_format), - NormalizedRenderTarget::Image(image_handle) => { - images.get(image_handle).map(|image| image.texture_format) - } + NormalizedRenderTarget::Image(image_target) => images + .get(&image_target.handle) + .map(|image| image.texture_format), NormalizedRenderTarget::TextureView(id) => { manual_texture_views.get(id).map(|tex| tex.format) } @@ -821,11 +846,11 @@ impl NormalizedRenderTarget { physical_size: window.physical_size(), scale_factor: window.resolution.scale_factor(), }), - NormalizedRenderTarget::Image(image_handle) => { - let image = images.get(image_handle)?; + NormalizedRenderTarget::Image(image_target) => { + let image = images.get(&image_target.handle)?; Some(RenderTargetInfo { physical_size: image.size(), - scale_factor: 1.0, + scale_factor: image_target.scale_factor.0, }) } NormalizedRenderTarget::TextureView(id) => { @@ -847,8 +872,8 @@ impl NormalizedRenderTarget { NormalizedRenderTarget::Window(window_ref) => { changed_window_ids.contains(&window_ref.entity()) } - NormalizedRenderTarget::Image(image_handle) => { - changed_image_handles.contains(&image_handle.id()) + NormalizedRenderTarget::Image(image_target) => { + changed_image_handles.contains(&image_target.handle.id()) } NormalizedRenderTarget::TextureView(_) => true, } diff --git a/crates/bevy_render/src/view/window/screenshot.rs b/crates/bevy_render/src/view/window/screenshot.rs index 5bc194878c4be..aab9b08c680d7 100644 --- a/crates/bevy_render/src/view/window/screenshot.rs +++ b/crates/bevy_render/src/view/window/screenshot.rs @@ -95,7 +95,7 @@ impl Screenshot { /// Capture a screenshot of the provided render target image. pub fn image(image: Handle) -> Self { - Self(RenderTarget::Image(image)) + Self(RenderTarget::Image(image.into())) } /// Capture a screenshot of the provided manual texture view. @@ -297,7 +297,7 @@ fn prepare_screenshots( ); } NormalizedRenderTarget::Image(image) => { - let Some(gpu_image) = images.get(image) else { + let Some(gpu_image) = images.get(&image.handle) else { warn!("Unknown image for screenshot, skipping: {:?}", image); continue; }; @@ -533,7 +533,7 @@ pub(crate) fn submit_screenshot_commands(world: &World, encoder: &mut CommandEnc ); } NormalizedRenderTarget::Image(image) => { - let Some(gpu_image) = gpu_images.get(image) else { + let Some(gpu_image) = gpu_images.get(&image.handle) else { warn!("Unknown image for screenshot, skipping: {:?}", image); continue; }; diff --git a/examples/2d/pixel_grid_snap.rs b/examples/2d/pixel_grid_snap.rs index 3d9de7b84d476..9d701e003cf2b 100644 --- a/examples/2d/pixel_grid_snap.rs +++ b/examples/2d/pixel_grid_snap.rs @@ -117,7 +117,7 @@ fn setup_camera(mut commands: Commands, mut images: ResMut>) { Camera { // render before the "main pass" camera order: -1, - target: RenderTarget::Image(image_handle.clone()), + target: RenderTarget::Image(image_handle.clone().into()), ..default() }, Msaa::Off, diff --git a/examples/app/headless_renderer.rs b/examples/app/headless_renderer.rs index 2518a55c55515..3c8865148e2c1 100644 --- a/examples/app/headless_renderer.rs +++ b/examples/app/headless_renderer.rs @@ -268,7 +268,7 @@ fn setup_render_target( scene_controller.state = SceneState::Render(pre_roll_frames); scene_controller.name = scene_name; - RenderTarget::Image(render_target_image_handle) + RenderTarget::Image(render_target_image_handle.into()) } /// Setups image saver diff --git a/examples/ui/render_ui_to_texture.rs b/examples/ui/render_ui_to_texture.rs index 69083dd2ece14..119230c808073 100644 --- a/examples/ui/render_ui_to_texture.rs +++ b/examples/ui/render_ui_to_texture.rs @@ -57,7 +57,7 @@ fn setup( .spawn(( Camera2d, Camera { - target: RenderTarget::Image(image_handle.clone()), + target: RenderTarget::Image(image_handle.clone().into()), ..default() }, ))