Repository: bitshifter/glam-rs
Pull request #42 (2019-12-26 14:10:47)

Added Vec2::round(), Vec3::round() and Vec4::round() (scalar and sse2 versions).
Added Vec2::round(), Vec3::round() and Vec4::round() (scalar and sse2 versions).

bitshifter: Away from PC at the moment, looks like you've addressed most things I was going to raise already. In always interested in the use case for new features and how people are using the library, what algorithm are you using `round` in?
michal-z: I'm very interested in pushing the graphics/games development Rust ecosystem forward. I think we need fast and rich math library like DirectXMath. So, I would like to port some features from this C++ lib. Also, I'm working on some creative graphics experiments using Rust and D3D12.  I want to push a lot of preprocessed data from CPU to GPU. I need fast round()/floor()/ceil() for noise-based effects and also sin/cos. In the next PRs I would like to implement vectorized versions of sin/cos/tan if you are interested.
bitshifter: Yep that all makes sense. I did have an sse2 sin_cos that is disabled for now as it lost accuracy for large angle values, so that's probably something that would need to be handled. Or if accuracy was a lot different from std library trig functions it might be better to make them opt in.

You might want to create a new branch from master for future PRs once this is merged since git is including a lot of changes I've already merged in the PR. I have been squashing commits which might be confusing history on your branch.

Patch:
diff --git a/src/f32/funcs.rs b/src/f32/funcs.rs
index 5794464..ef16a53 100644
--- a/src/f32/funcs.rs
+++ b/src/f32/funcs.rs
@@ -1,22 +1,23 @@
-// #[cfg(target_arch = "x86")]
-// use std::arch::x86::*;
-// #[cfg(target_arch = "x86_64")]
-// use std::arch::x86_64::*;
-
-// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-// use super::x86_utils::UnionCast;
-
-// macro_rules! _ps_const_ty {
-//     ($name:ident, $field:ident, $x:expr) => {
-//         #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-//         const $name: UnionCast = UnionCast {
-//             $field: [$x, $x, $x, $x],
-//         };
-//     };
-// }
+#[cfg(target_arch = "x86")]
+use std::arch::x86::*;
+#[cfg(target_arch = "x86_64")]
+use std::arch::x86_64::*;
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+use crate::f32::x86_utils::UnionCast;
+
+macro_rules! _ps_const_ty {
+    ($name:ident, $field:ident, $x:expr) => {
+        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+        const $name: UnionCast = UnionCast {
+            $field: [$x, $x, $x, $x],
+        };
+    };
+}
 
-// _ps_const_ty!(PS_INV_SIGN_MASK, u32x4, !0x8000_0000);
-// _ps_const_ty!(PS_SIGN_MASK, u32x4, 0x8000_0000);
+_ps_const_ty!(PS_INV_SIGN_MASK, u32x4, !0x8000_0000);
+_ps_const_ty!(PS_SIGN_MASK, u32x4, 0x8000_0000);
+_ps_const_ty!(PS_NO_FRACTION, f32x4, 8388608.0);
 
 // _ps_const_ty!(PS_1_0, f32x4, 1.0);
 // _ps_const_ty!(PS_0_5, f32x4, 0.5);
@@ -49,6 +50,21 @@ pub(crate) fn scalar_sin_cos(x: f32) -> (f32, f32) {
     x.sin_cos()
 }
 
+#[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
+#[inline]
+pub unsafe fn m128_round(v: __m128) -> __m128 {
+    // From DirectXMath XMVectorRound.
+    let sign = _mm_and_ps(v, PS_SIGN_MASK.m128);
+    let s_magic = _mm_or_ps(PS_NO_FRACTION.m128, sign);
+    let r1 = _mm_add_ps(v, s_magic);
+    let r1 = _mm_sub_ps(r1, s_magic);
+    let r2 = _mm_and_ps(v, PS_INV_SIGN_MASK.m128);
+    let mask = _mm_cmple_ps(r2, PS_NO_FRACTION.m128);
+    let r2 = _mm_andnot_ps(mask, v);
+    let r1 = _mm_and_ps(r1, mask);
+    _mm_xor_ps(r1, r2)
+}
+
 // Based on http://gruntthepeon.free.fr/ssemath/sse_mathfun.h
 // #[cfg(target_feature = "sse2")]
 // unsafe fn sin_cos_sse2(x: __m128) -> (__m128, __m128) {
diff --git a/src/f32/vec2.rs b/src/f32/vec2.rs
index 9951b8a..c795303 100644
--- a/src/f32/vec2.rs
+++ b/src/f32/vec2.rs
@@ -315,6 +315,11 @@ impl Vec2 {
     pub fn abs(self) -> Self {
         Self(self.0.abs(), self.1.abs())
     }
+
+    #[inline]
+    pub fn round(self) -> Self {
+        Self(self.0.round(), self.1.round())
+    }
 }
 
 impl fmt::Display for Vec2 {
diff --git a/src/f32/vec3_f32.rs b/src/f32/vec3_f32.rs
index 4f21fcc..e07076e 100644
--- a/src/f32/vec3_f32.rs
+++ b/src/f32/vec3_f32.rs
@@ -250,6 +250,11 @@ impl Vec3 {
     pub fn abs(self) -> Self {
         Self(self.0.abs(), self.1.abs(), self.2.abs())
     }
+
+    #[inline]
+    pub fn round(self) -> Self {
+        Self(self.0.round(), self.1.round(), self.2.round())
+    }
 }
 
 impl fmt::Display for Vec3 {
diff --git a/src/f32/vec3_sse2.rs b/src/f32/vec3_sse2.rs
index 0f3d376..08df0a8 100644
--- a/src/f32/vec3_sse2.rs
+++ b/src/f32/vec3_sse2.rs
@@ -6,6 +6,7 @@ use rand::{
     Rng,
 };
 
+use crate::f32::funcs::m128_round;
 use crate::{
     f32::{Vec2, Vec4, X_AXIS, Y_AXIS, Z_AXIS},
     Align16,
@@ -363,6 +364,11 @@ impl Vec3 {
             ))
         }
     }
+
+    #[inline]
+    pub fn round(self) -> Self {
+        unsafe { Self(m128_round(self.0)) }
+    }
 }
 
 impl fmt::Display for Vec3 {
diff --git a/src/f32/vec4_f32.rs b/src/f32/vec4_f32.rs
index 3da05ef..a01803a 100644
--- a/src/f32/vec4_f32.rs
+++ b/src/f32/vec4_f32.rs
@@ -358,6 +358,16 @@ impl Vec4 {
     pub fn abs(self) -> Self {
         Self(self.0.abs(), self.1.abs(), self.2.abs(), self.3.abs())
     }
+
+    #[inline]
+    pub fn round(self) -> Self {
+        Self(
+            self.0.round(),
+            self.1.round(),
+            self.2.round(),
+            self.3.round(),
+        )
+    }
 }
 
 impl fmt::Display for Vec4 {
diff --git a/src/f32/vec4_sse2.rs b/src/f32/vec4_sse2.rs
index 15654d1..02dbb39 100644
--- a/src/f32/vec4_sse2.rs
+++ b/src/f32/vec4_sse2.rs
@@ -6,6 +6,7 @@ use rand::{
     Rng,
 };
 
+use crate::f32::funcs::m128_round;
 use crate::{f32::Vec3, Align16};
 
 #[cfg(target_arch = "x86")]
@@ -395,6 +396,11 @@ impl Vec4 {
             ))
         }
     }
+
+    #[inline]
+    pub fn round(self) -> Self {
+        unsafe { Self(m128_round(self.0)) }
+    }
 }
 
 impl fmt::Display for Vec4 {


Test patch:
diff --git a/tests/vec2.rs b/tests/vec2.rs
index 90d765e..3b770b7 100644
--- a/tests/vec2.rs
+++ b/tests/vec2.rs
@@ -312,6 +312,17 @@ fn test_vec2_abs() {
     assert_eq!((-Vec2::one()).abs(), Vec2::one());
 }
 
+#[test]
+fn test_vec2_round() {
+    assert_eq!(Vec2::new(1.35, 0.0).round().x(), 1.0);
+    assert_eq!(Vec2::new(0.0, 1.5).round().y(), 2.0);
+    assert_eq!(Vec2::new(0.0, -15.5).round().y(), -16.0);
+    assert_eq!(Vec2::new(0.0, 0.0).round().y(), 0.0);
+    assert_eq!(Vec2::new(0.0, 21.1).round().y(), 21.0);
+    assert_eq!(Vec2::new(0.0, 11.123).round().y(), 11.0);
+    assert_eq!(Vec2::new(0.0, 11.499).round().y(), 11.0);
+}
+
 #[cfg(feature = "serde")]
 #[test]
 fn test_vec2_serde() {
diff --git a/tests/vec3.rs b/tests/vec3.rs
index aed684f..33cd63b 100644
--- a/tests/vec3.rs
+++ b/tests/vec3.rs
@@ -357,6 +357,17 @@ fn test_vec3_abs() {
     assert_eq!((-Vec3::one()).abs(), Vec3::one());
 }
 
+#[test]
+fn test_vec3_round() {
+    assert_eq!(Vec3::new(1.35, 0.0, 0.0).round().x(), 1.0);
+    assert_eq!(Vec3::new(0.0, 1.5, 0.0).round().y(), 2.0);
+    assert_eq!(Vec3::new(0.0, 0.0, -15.5).round().z(), -16.0);
+    assert_eq!(Vec3::new(0.0, 0.0, 0.0).round().z(), 0.0);
+    assert_eq!(Vec3::new(0.0, 21.1, 0.0).round().y(), 21.0);
+    assert_eq!(Vec3::new(0.0, 11.123, 0.0).round().y(), 11.0);
+    assert_eq!(Vec3::new(0.0, 11.499, 0.0).round().y(), 11.0);
+}
+
 #[cfg(feature = "serde")]
 #[test]
 fn test_vec3_serde() {
diff --git a/tests/vec4.rs b/tests/vec4.rs
index 4d0e01e..da9635a 100644
--- a/tests/vec4.rs
+++ b/tests/vec4.rs
@@ -384,6 +384,17 @@ fn test_vec4mask_not() {
     assert_eq!((!Vec4Mask::new(false, true, false, true)).bitmask(), 0b0101);
 }
 
+#[test]
+fn test_vec4_round() {
+    assert_eq!(Vec4::new(1.35, 0.0, 0.0, 0.0).round().x(), 1.0);
+    assert_eq!(Vec4::new(0.0, 1.5, 0.0, 0.0).round().y(), 2.0);
+    assert_eq!(Vec4::new(0.0, 0.0, -15.5, 0.0).round().z(), -16.0);
+    assert_eq!(Vec4::new(0.0, 0.0, 0.0, 0.0).round().z(), 0.0);
+    assert_eq!(Vec4::new(0.0, 21.1, 0.0, 0.0).round().y(), 21.0);
+    assert_eq!(Vec4::new(0.0, 0.0, 0.0, 11.123).round().w(), 11.0);
+    assert_eq!(Vec4::new(0.0, 0.0, 11.501, 0.0).round().z(), 12.0);
+}
+
 #[cfg(feature = "serde")]
 #[test]
 fn test_vec4_serde() {