Changing a camera's `RenderTarget` doesn't properly update the viewport size
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 16h
- Merged PRs (30d)
- 171
Description
## Bevy version and features
Version: 0.18.1
Features: `2d`
## What you did
I was updating my pixel perfect camera crate, when I noticed I somehow managed to get the pixelation to work without a second viewport camera. How? After some investigation I got it down to this:
```rs
use bevy::{camera::RenderTarget, prelude::*, window::WindowRef};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, (update, change_render_target))
.run();
}
fn setup(mut commands: Commands) {
commands.spawn((
Camera2d,
RenderTarget::None {
size: UVec2::new(40, 20),
},
));
}
fn change_render_target(mut render_target: Single<&mut RenderTarget>, mut counter: Local) {
// We need to let a frame pass or else it doesn't work.
if *counter == 1 {
**render_target = RenderTarget::Window(WindowRef::Primary);
}
if *counter < 2 {
*counter += 1;
}
}
fn update(time: Res
In this example, there's a circle rotating around the center of the screen. By setting the camera's `RenderTarget` to `None` and then setting to the window a frame later, it gets confused, and it keeps the same size as the `RenderTarget::None`. And this makes everything pixelated and zoomed in.
What's even more odd is that resizing the window makes it zoom out such that every world pixel is one screen pixel again. The default behavior.
## What went wrong
I think I narrowed it down to this: https://github.com/bevyengine/bevy/blob/f667c282dad2c1419afb5836ded22a3ec263970e/crates/bevy_render/src/camera.rs#L341-L348
This is the code that updates `camera.computed.target_info` This code isn't checking if `render_target` changed. Adding the check seems to fix it.
```diff
manual_texture_views: Res,
- mut cameras: Query<(&mut Camera, &RenderTarget, &mut Projection)>,
+ mut cameras: Query<(&mut Camera, Ref, &mut Projection)>,
) -> Result<(), BevyError> {
```
```diff
if let Some(normalized_target) = render_target.normalize(primary_window)
&& (normalized_target.is_changed(&changed_window_ids, &changed_image_handles)
|| camera.is_added()
+ || render_target.is_changed()
|| camera.computed.old_viewport_size != viewport_size
|| camera.computed.old_sub_camera_view != camera.sub_camera_view)
```
The reason I'm making an issue and not a pull request, is that this bug is kinda useful? If it wasn't for the window-size caveat, this would be a really simple and fast, albeit limited way to make pixelated cameras.
Contributor guide
Research direction
Reproduce the provided Bevy 0.18.1 example, then inspect crates/bevy_render/src/camera.rs around lines 341-348, where camera.computed.target_info is updated. Compare the behavior when RenderTarget changes with and without a window resize; done means changing the target updates the viewport size immediately and resizing retains the correct behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- computer-graphics, game-dev
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 70/100