Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stop using infinities in wgsl shaders #1594

Merged
merged 3 commits into from
Mar 15, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/re_renderer/shader/global_bindings.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ struct FrameUniformBuffer {
pixels_from_point: f32,

/// (tan(fov_y / 2) * aspect_ratio, tan(fov_y /2)), i.e. half ratio of screen dimension to screen distance in x & y.
/// Both values are set to positive infinity for orthographic projection
/// Both values are set to f32max for orthographic projection
tan_half_fov: Vec2,

// Size used for all point radii given with Size::AUTO.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ fn compute_pixel_coords(center_coord: IVec2, unnormalized_edge_pos_a_and_b: Vec4

var pixel_coord_a: Vec2;
if num_edges_a_and_b.x == 0.0 {
pixel_coord_a = Vec2(inf());
pixel_coord_a = Vec2(f32max);
} else {
pixel_coord_a = Vec2(center_coord) + edge_pos_a_and_b.xy;
}
var pixel_coord_b: Vec2;
if num_edges_a_and_b.y == 0.0 {
pixel_coord_b = Vec2(inf());
pixel_coord_b = Vec2(f32max);
} else {
pixel_coord_b = Vec2(center_coord) + edge_pos_a_and_b.zw;
}
Expand Down
8 changes: 4 additions & 4 deletions crates/re_renderer/shader/outlines/jumpflooding_step.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ fn main(in: FragmentInput) -> @location(0) Vec4 {
let pixel_step = Vec2(f32(uniforms.step_width), f32(uniforms.step_width)) / resolution;
let pixel_coordinates = resolution * in.texcoord;

var closest_positions_a = Vec2(-inf());
var closest_distance_sq_a = inf();
var closest_positions_b = Vec2(-inf());
var closest_distance_sq_b = inf();
var closest_positions_a = Vec2(f32min);
var closest_distance_sq_a = f32max;
var closest_positions_b = Vec2(f32min);
var closest_distance_sq_b = f32max;

for (var y: i32 = -1; y <= 1; y += 1) {
for (var x: i32 = -1; x <= 1; x += 1) {
Expand Down
35 changes: 27 additions & 8 deletions crates/re_renderer/shader/types.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,23 @@ type Mat3 = mat3x3<f32>;
type Mat4x3 = mat4x3<f32>;
type Mat4 = mat4x4<f32>;

const f32min = -3.4028235e38;
const f32max = 3.4028235e38;
const f32eps = 0.00000011920928955078125;

// Extreme values as documented by the spec:
// https://www.w3.org/TR/WGSL/#floating-point-types
const f32max = 0x1.fffffep+127f; // Largest positive float value.
const f32min = -0x0.fffffep+127f; // Smallest negative float value.
const f32min_normal = 0x1p-126f; // Smallest positive normal float value.
// F16 is not implemented yet in Naga https://github.com/gfx-rs/naga/issues/1884
//const f16min = 0x0.ffcp+15h; // Smallest negative float value.
//const f16max = 0x1.ffcp+15h; // Largest positive float value.
//const f16min_normal = 0x1p-14h; // Smallest positive normal float value.
// https://www.w3.org/TR/WGSL/#integer-types
const i32min = -0x80000000i;
const i32max = 0x7fffffffi;
const u32min = 0u;
const u32max = 0xFFFFFFFFu;
const u32max = 0xffffffffu;

// Difference between `1.0` and the next larger representable number.
const f32eps = 0.00000011920928955078125;

const X = Vec3(1.0, 0.0, 0.0);
const Y = Vec3(0.0, 1.0, 0.0);
Expand All @@ -26,6 +37,14 @@ const Z = Vec3(0.0, 0.0, 1.0);
const ZERO = Vec4(0.0, 0.0, 0.0, 0.0);
const ONE = Vec4(1.0, 1.0, 1.0, 1.0);

fn inf() -> f32 {
return 1.0 / 0.0;
}

// Do NOT use inf() or nan() in your WGSL shaders. Ever.
// The WGSL spec allows implementations to assume that neither Inf or NaN are ever occuring:
// https://www.w3.org/TR/WGSL/#floating-point-evaluation
//
// It will work most of the time, but there are rare cases where this will break.
// (Notably, we had a case where the following commented inf function would silently break shaders when using ANGLE, i.e. in browsers on Windows!)
//
// fn inf() -> f32 {
// return 1.0 / 0.0;
// }
4 changes: 2 additions & 2 deletions crates/re_renderer/shader/utils/camera.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

// True if the camera is orthographic
fn is_camera_orthographic() -> bool {
return frame.tan_half_fov.x == inf();
return frame.tan_half_fov.x >= f32max;
}

// True if the camera is perspective
fn is_camera_perspective() -> bool {
return frame.tan_half_fov.x != inf();
return frame.tan_half_fov.x < f32max;
}

struct Ray {
Expand Down
8 changes: 4 additions & 4 deletions crates/re_renderer/shader/utils/size.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
fn unresolved_size_to_world(_unresolved_size: f32, camera_distance: f32, auto_size: f32) -> f32 {
// Resolve auto size.
var unresolved_size: f32;
if _unresolved_size == inf() {
// positive inf for small auto size
if _unresolved_size >= f32max {
// positive max for small auto size
unresolved_size = auto_size;
} else if _unresolved_size == -inf() {
// negative inf for large auto size
} else if _unresolved_size <= f32min {
// negative max for large auto size
let large_factor = 1.33;
unresolved_size = auto_size * large_factor;
} else {
Expand Down
2 changes: 1 addition & 1 deletion crates/re_renderer/src/global_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub(crate) struct FrameUniformBuffer {
pub pixels_from_point: f32,

/// (tan(fov_y / 2) * aspect_ratio, tan(fov_y /2)), i.e. half ratio of screen dimension to screen distance in x & y.
/// Both values are set to positive infinity for orthographic projection
/// Both values are set to f32max for orthographic projection
pub tan_half_fov: wgpu_buffer_types::Vec2,

// Size used for all point radii given with Size::AUTO.
Expand Down
4 changes: 2 additions & 2 deletions crates/re_renderer/src/renderer/lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,13 +424,13 @@ impl LineDrawData {
Vec::with_capacity(wgpu::util::align_to(num_segments, POSITION_TEXTURE_SIZE) as usize);
// sentinel at the beginning to facilitate caps.
position_data_staging.push(LineVertex {
position: glam::vec3(f32::INFINITY, f32::INFINITY, f32::INFINITY),
position: glam::vec3(f32::MAX, f32::MAX, f32::MAX),
strip_index: u32::MAX,
});
position_data_staging.extend(vertices.iter());
// placeholder at the end to facilitate caps.
position_data_staging.push(LineVertex {
position: glam::vec3(f32::INFINITY, f32::INFINITY, f32::INFINITY),
position: glam::vec3(f32::MAX, f32::MAX, f32::MAX),
strip_index: u32::MAX,
});
position_data_staging.extend(std::iter::repeat(gpu_data::LineVertex::zeroed()).take(
Expand Down
19 changes: 9 additions & 10 deletions crates/re_renderer/src/size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ pub struct Size(pub f32);

impl Size {
/// Automatically sized, based on a view builder setting.
pub const AUTO: Self = Self(f32::INFINITY);
pub const AUTO: Self = Self(f32::MAX);

/// Like [`Size::AUTO`], but larger by some small factor (~2).
pub const AUTO_LARGE: Self = Self(-f32::INFINITY);
pub const AUTO_LARGE: Self = Self(f32::MIN);

/// Creates a new size in scene units.
///
/// Values passed must be finite positive.
#[inline]
pub fn new_scene(size: f32) -> Self {
debug_assert!(size.is_finite() && size >= 0.0, "Bad size: {size}");
debug_assert!((0.0..f32::MAX).contains(&size), "Bad size: {size}");
Self(size)
}

Expand All @@ -32,27 +32,26 @@ impl Size {
/// Values passed must be finite positive.
#[inline]
pub fn new_points(size: f32) -> Self {
debug_assert!(size.is_finite() && size >= 0.0, "Bad size: {size}");
debug_assert!((0.0..f32::MAX).contains(&size), "Bad size: {size}");
Self(-size)
}

/// Returns true if the size is an automatically determined size ([`Self::AUTO`] or [`Self::AUTO_LARGE`]).
#[inline]
pub fn is_auto(&self) -> bool {
self.0.is_infinite()
self.0 >= f32::MAX || self.0 <= f32::MIN
Wumpf marked this conversation as resolved.
Show resolved Hide resolved
}

/// Get the scene-size of this, if stored as a scene size.
#[inline]
#[allow(unused)] // wgpu is not yet using this
pub fn scene(&self) -> Option<f32> {
(self.0.is_finite() && self.0 >= 0.0).then_some(self.0)
(0.0..f32::MAX).contains(&self.0).then_some(self.0)
}

/// Get the point size of this, if stored as a point size.
#[inline]
pub fn points(&self) -> Option<f32> {
(self.0.is_finite() && self.0 <= 0.0).then_some(-self.0)
(f32::MIN..=0.0).contains(&self.0).then_some(-self.0)
Wumpf marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -68,15 +67,15 @@ impl std::ops::Mul<f32> for Size {

#[inline]
fn mul(self, rhs: f32) -> Self::Output {
debug_assert!(rhs.is_finite() && rhs >= 0.0);
debug_assert!(rhs.is_normal() && rhs >= 0.0);
Wumpf marked this conversation as resolved.
Show resolved Hide resolved
Self(self.0 * rhs)
}
}

impl std::ops::MulAssign<f32> for Size {
#[inline]
fn mul_assign(&mut self, rhs: f32) {
debug_assert!(rhs.is_finite() && rhs >= 0.0);
debug_assert!(rhs.is_normal() && rhs >= 0.0);
Wumpf marked this conversation as resolved.
Show resolved Hide resolved
self.0 *= rhs;
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/re_renderer/src/view_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ impl ViewBuilder {
}
};

let tan_half_fov = glam::vec2(f32::INFINITY, f32::INFINITY);
let tan_half_fov = glam::vec2(f32::MAX, f32::MAX);
let pixel_world_size_from_camera_distance =
vertical_world_size / config.resolution_in_pixel[1] as f32;

Expand Down