Skip to content

Commit af61ec2

Browse files
authored
Incorporate viewport position into clamping in camera_system (#18242)
# Objective In its existing form, the clamping that's done in `camera_system` doesn't work well when the `physical_position` of the associated viewport is nonzero. In such cases, it may produce invalid viewport rectangles (i.e. not lying inside the render target), which may result in crashes during the render pass. The goal of this PR is to eliminate this possibility by making the clamping behavior always result in a valid viewport rectangle when possible. ## Solution Incorporate the `physical_position` information into the clamping behavior. In particular, always cut off enough so that it's contained in the render target rather than clamping it to the same dimensions as the target itself. In weirder situations, still try to produce a valid viewport rectangle to avoid crashes. ## Testing Tested these changes on my work branch where I encountered the crash.
1 parent fecf2d2 commit af61ec2

File tree

1 file changed

+37
-6
lines changed

1 file changed

+37
-6
lines changed

crates/bevy_render/src/camera/camera.rs

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,42 @@ impl Default for Viewport {
7676
}
7777
}
7878

79+
impl Viewport {
80+
/// Cut the viewport rectangle so that it lies inside a rectangle of the
81+
/// given size.
82+
///
83+
/// If either of the viewport's position coordinates lies outside the given
84+
/// dimensions, it will be moved just inside first. If either of the given
85+
/// dimensions is zero, the position and size of the viewport rectangle will
86+
/// both be set to zero in that dimension.
87+
pub fn clamp_to_size(&mut self, size: UVec2) {
88+
// If the origin of the viewport rect is outside, then adjust so that
89+
// it's just barely inside. Then, cut off the part that is outside.
90+
if self.physical_size.x + self.physical_position.x > size.x {
91+
if self.physical_position.x < size.x {
92+
self.physical_size.x = size.x - self.physical_position.x;
93+
} else if size.x > 0 {
94+
self.physical_position.x = size.x - 1;
95+
self.physical_size.x = 1;
96+
} else {
97+
self.physical_position.x = 0;
98+
self.physical_size.x = 0;
99+
}
100+
}
101+
if self.physical_size.y + self.physical_position.y > size.y {
102+
if self.physical_position.y < size.y {
103+
self.physical_size.y = size.y - self.physical_position.y;
104+
} else if size.y > 0 {
105+
self.physical_position.y = size.y - 1;
106+
self.physical_size.y = 1;
107+
} else {
108+
self.physical_position.y = 0;
109+
self.physical_size.y = 0;
110+
}
111+
}
112+
}
113+
}
114+
79115
/// Settings to define a camera sub view.
80116
///
81117
/// When [`Camera::sub_camera_view`] is `Some`, only the sub-section of the
@@ -991,12 +1027,7 @@ pub fn camera_system(
9911027
if let Some(viewport) = &mut camera.viewport {
9921028
let target_info = &new_computed_target_info;
9931029
if let Some(target) = target_info {
994-
if viewport.physical_size.x > target.physical_size.x {
995-
viewport.physical_size.x = target.physical_size.x;
996-
}
997-
if viewport.physical_size.y > target.physical_size.y {
998-
viewport.physical_size.y = target.physical_size.y;
999-
}
1030+
viewport.clamp_to_size(target.physical_size);
10001031
}
10011032
}
10021033
camera.computed.target_info = new_computed_target_info;

0 commit comments

Comments
 (0)