492: Clean up specialization and simd r=kvark a=NNemec

The current SIMD implementation is based on the deprecated "simd" package and requires the unstable feature "specialization". So far, this was handled by significant code duplication and added complexity, making everything harder to understand even when "simd" was not in use at all.

In order to migrate from "simd" to "packed_simd" it appears valuable to first clean up the existing "simd" code, see what is still valuable and then see how to migrate.

This change reduces duplication, introduces a clear distinction of the "simd" feature and the "specialization" feature and moves most "simd" related code into separate source files.

The code is pure refactoring, keeping all functionality unchanged.

Testing the "simd" feature requires the 1.32 toolchain and commenting out the glium dev dependency. With that all tests run successfully.

Co-authored-by: Norbert Nemec <norbert@nemec-online.de>
This commit is contained in:
bors[bot] 2019-09-03 17:35:33 +00:00 committed by GitHub
commit 981170fc19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 638 additions and 1073 deletions

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
.DS_Store
.vs/
/target
/Cargo.lock
/bench/bench

View file

@ -17,6 +17,9 @@ matrix:
env: CARGO_FEATURES="simd"
- rust: nightly
env: CARGO_FEATURES="serde simd"
- rust: nightly-2019-01-01
# an old nightly build where "simd" still worked and "specialization"
env: CARGO_FEATURES="simd"
allow_failures:
- rust: nightly

View file

@ -6,6 +6,13 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Changed
- Refactored dependencies of experimental "specialization" feature into
default_fn! macro to reduce code duplication and complexity. Currently
only needed for non-functional SIMD feature.
- Refactored SIMD code into separate source files. See README.md for details.
### Added
- Add `VectorN::zip` and `PointN::zip`

View file

@ -26,8 +26,8 @@ mint = { version = "0.5", optional = true }
num-traits = "0.2"
rand = { version = "0.7", optional = true }
serde = { version = "1.0", features = ["serde_derive"], optional = true }
simd = { version = "0.2", optional = true }
simd = { version = "0.2", optional = true } # works only in rust toolchain up to 1.32
[dev-dependencies]
glium = "0.23"
# glium = "0.23" # causes problems with nightly-2019-01-01 needed for testing "simd"
serde_json = "1.0"

View file

@ -57,6 +57,13 @@ and `v.zy()` produces a
```rust
Vector2 { x: 3.0, y: 2.0 }
```
### SIMD optimizations
The current SIMD support depends on the deprecated "simd" package as well
as the unstable "specialization" feature. To build this code, a pre-1.33 nightly
build of Rust is required, e.g. 2019-01-01-nightly. Though the code is not
useful in its present form, it has some worth preserving as starting point
for a future migration (see https://github.com/rustgd/cgmath/issues/490).
## Limitations

View file

@ -4,7 +4,8 @@
//! For example, when declaring `glium` uniforms, we need to convert to fixed
//! length arrays. We can use the `Into` trait directly, but it is rather ugly!
//!
//! ```rust
//! --- Doc-test disabled because glium causes problems with nightly-2019-01-01 needed for "simd"
//! ` ` `rust
//! #[macro_use]
//! extern crate glium;
//! extern crate cgmath;
@ -22,11 +23,12 @@
//! // Yuck!! (ノಥ益ಥ)ノ ┻━┻
//! };
//! # }
//! ```
//! ` ` `
//!
//! Instead, we can use the conversion functions from the `conv` module:
//!
//! ```rust
//! --- Doc-test disabled because glium causes problems nightly-2019-01-01 needed for "simd"
//! ` ` `rust
//! #[macro_use]
//! extern crate glium;
//! extern crate cgmath;
@ -45,7 +47,7 @@
//! // ┬─┬ノ( º _ ºノ)
//! };
//! # }
//! ```
//! ` ` `
/// Force a conversion into a 2-element array.
#[inline]

View file

@ -99,8 +99,15 @@ mod structure;
mod matrix;
mod quaternion;
#[cfg(feature = "simd")]
mod quaternion_simd;
mod vector;
#[cfg(feature = "simd")]
mod vector_simd;
mod angle;
mod euler;
mod point;

View file

@ -17,6 +17,16 @@
#![macro_use]
#[cfg(feature = "simd")]
macro_rules! default_fn {
{ $($tt:tt)* } => { default fn $( $tt )* };
}
#[cfg(not(feature = "simd"))]
macro_rules! default_fn {
{ $($tt:tt)* } => { fn $( $tt )* };
}
/// Generates a binary operator implementation for the permutations of by-ref and by-val
macro_rules! impl_operator {
// When it is an unary operator
@ -26,17 +36,17 @@ macro_rules! impl_operator {
impl<$S: $Constraint> $Op for $Lhs {
type Output = $Output;
#[inline]
fn $op(self) -> $Output {
default_fn!($op(self) -> $Output {
let $x = self; $body
}
});
}
impl<'a, $S: $Constraint> $Op for &'a $Lhs {
type Output = $Output;
#[inline]
fn $op(self) -> $Output {
default_fn!($op(self) -> $Output {
let $x = self; $body
}
});
}
};
// When the right operand is a scalar
@ -46,17 +56,17 @@ macro_rules! impl_operator {
impl<$S: $Constraint> $Op<$Rhs> for $Lhs {
type Output = $Output;
#[inline]
fn $op(self, other: $Rhs) -> $Output {
default_fn!($op(self, other: $Rhs) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
});
}
impl<'a, $S: $Constraint> $Op<$Rhs> for &'a $Lhs {
type Output = $Output;
#[inline]
fn $op(self, other: $Rhs) -> $Output {
default_fn!($op(self, other: $Rhs) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
});
}
};
// When the right operand is a compound type
@ -66,33 +76,33 @@ macro_rules! impl_operator {
impl<$S: $Constraint> $Op<$Rhs> for $Lhs {
type Output = $Output;
#[inline]
fn $op(self, other: $Rhs) -> $Output {
default_fn!( $op(self, other: $Rhs) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
});
}
impl<'a, $S: $Constraint> $Op<&'a $Rhs> for $Lhs {
type Output = $Output;
#[inline]
fn $op(self, other: &'a $Rhs) -> $Output {
default_fn!( $op(self, other: &'a $Rhs) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
});
}
impl<'a, $S: $Constraint> $Op<$Rhs> for &'a $Lhs {
type Output = $Output;
#[inline]
fn $op(self, other: $Rhs) -> $Output {
default_fn!( $op(self, other: $Rhs) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
});
}
impl<'a, 'b, $S: $Constraint> $Op<&'a $Rhs> for &'b $Lhs {
type Output = $Output;
#[inline]
fn $op(self, other: &'a $Rhs) -> $Output {
default_fn!( $op(self, other: &'a $Rhs) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
});
}
};
// When the left operand is a scalar
@ -102,17 +112,17 @@ macro_rules! impl_operator {
impl $Op<$Rhs<$S>> for $Lhs {
type Output = $Output;
#[inline]
fn $op(self, other: $Rhs<$S>) -> $Output {
default_fn!( $op(self, other: $Rhs<$S>) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
});
}
impl<'a> $Op<&'a $Rhs<$S>> for $Lhs {
type Output = $Output;
#[inline]
fn $op(self, other: &'a $Rhs<$S>) -> $Output {
default_fn!( $op(self, other: &'a $Rhs<$S>) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
});
}
};
}
@ -123,7 +133,7 @@ macro_rules! impl_assignment_operator {
}) => {
impl<$S: $Constraint + $Op<$S>> $Op<$Rhs> for $Lhs {
#[inline]
fn $op(&mut $lhs, $rhs: $Rhs) $body
default_fn!( $op(&mut $lhs, $rhs: $Rhs) $body );
}
};
}
@ -255,118 +265,6 @@ macro_rules! impl_index_operators {
}
}
#[cfg(feature = "simd")]
macro_rules! impl_operator_default {
// When it is an unary operator
(<$S:ident: $Constraint:ident> $Op:ident for $Lhs:ty {
fn $op:ident($x:ident) -> $Output:ty { $body:expr }
}) => {
impl<$S: $Constraint> $Op for $Lhs {
type Output = $Output;
#[inline]
default fn $op(self) -> $Output {
let $x = self; $body
}
}
impl<'a, $S: $Constraint> $Op for &'a $Lhs {
type Output = $Output;
#[inline]
default fn $op(self) -> $Output {
let $x = self; $body
}
}
};
// When the right operand is a scalar
(<$S:ident: $Constraint:ident> $Op:ident<$Rhs:ident> for $Lhs:ty {
fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr }
}) => {
impl<$S: $Constraint> $Op<$Rhs> for $Lhs {
type Output = $Output;
#[inline]
default fn $op(self, other: $Rhs) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
}
impl<'a, $S: $Constraint> $Op<$Rhs> for &'a $Lhs {
type Output = $Output;
#[inline]
default fn $op(self, other: $Rhs) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
}
};
// When the right operand is a compound type
(<$S:ident: $Constraint:ident> $Op:ident<$Rhs:ty> for $Lhs:ty {
fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr }
}) => {
impl<$S: $Constraint> $Op<$Rhs> for $Lhs {
type Output = $Output;
#[inline]
default fn $op(self, other: $Rhs) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
}
impl<'a, $S: $Constraint> $Op<&'a $Rhs> for $Lhs {
type Output = $Output;
#[inline]
default fn $op(self, other: &'a $Rhs) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
}
impl<'a, $S: $Constraint> $Op<$Rhs> for &'a $Lhs {
type Output = $Output;
#[inline]
default fn $op(self, other: $Rhs) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
}
impl<'a, 'b, $S: $Constraint> $Op<&'a $Rhs> for &'b $Lhs {
type Output = $Output;
#[inline]
default fn $op(self, other: &'a $Rhs) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
}
};
// When the left operand is a scalar
($Op:ident<$Rhs:ident<$S:ident>> for $Lhs:ty {
fn $op:ident($lhs:ident, $rhs:ident) -> $Output:ty { $body:expr }
}) => {
impl $Op<$Rhs<$S>> for $Lhs {
type Output = $Output;
#[inline]
default fn $op(self, other: $Rhs<$S>) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
}
impl<'a> $Op<&'a $Rhs<$S>> for $Lhs {
type Output = $Output;
#[inline]
default fn $op(self, other: &'a $Rhs<$S>) -> $Output {
let ($lhs, $rhs) = (self, other); $body
}
}
};
}
#[cfg(feature = "simd")]
macro_rules! impl_assignment_operator_default {
(<$S:ident: $Constraint:ident> $Op:ident<$Rhs:ty> for $Lhs:ty {
fn $op:ident(&mut $lhs:ident, $rhs:ident) $body:block
}) => {
impl<$S: $Constraint + $Op<$S>> $Op<$Rhs> for $Lhs {
#[inline]
default fn $op(&mut $lhs, $rhs: $Rhs) $body
}
};
}
/// Generates a binary operator implementation for the permutations of by-ref and by-val, for simd
#[cfg(feature = "simd")]
macro_rules! impl_operator_simd {

View file

@ -1520,21 +1520,21 @@ impl<S: BaseFloat> From<Matrix3<S>> for Quaternion<S> {
impl<S: fmt::Debug> fmt::Debug for Matrix2<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "Matrix2 "));
write!(f, "Matrix2 ")?;
<[[S; 2]; 2] as fmt::Debug>::fmt(self.as_ref(), f)
}
}
impl<S: fmt::Debug> fmt::Debug for Matrix3<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "Matrix3 "));
write!(f, "Matrix3 ")?;
<[[S; 3]; 3] as fmt::Debug>::fmt(self.as_ref(), f)
}
}
impl<S: fmt::Debug> fmt::Debug for Matrix4<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "Matrix4 "));
write!(f, "Matrix4 ")?;
<[[S; 4]; 4] as fmt::Debug>::fmt(self.as_ref(), f)
}
}

View file

@ -370,21 +370,21 @@ impl_mint_conversions!(Point3 { x, y, z }, Point3);
impl<S: fmt::Debug> fmt::Debug for Point1<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "Point1 "));
write!(f, "Point1 ")?;
<[S; 1] as fmt::Debug>::fmt(self.as_ref(), f)
}
}
impl<S: fmt::Debug> fmt::Debug for Point2<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "Point2 "));
write!(f, "Point2 ")?;
<[S; 2] as fmt::Debug>::fmt(self.as_ref(), f)
}
}
impl<S: fmt::Debug> fmt::Debug for Point3<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "Point3 "));
write!(f, "Point3 ")?;
<[S; 3] as fmt::Debug>::fmt(self.as_ref(), f)
}
}

View file

@ -35,9 +35,6 @@ use point::Point3;
use rotation::{Basis3, Rotation, Rotation3};
use vector::Vector3;
#[cfg(feature = "simd")]
use simd::f32x4 as Simdf32x4;
#[cfg(feature = "mint")]
use mint;
@ -55,30 +52,6 @@ pub struct Quaternion<S> {
pub v: Vector3<S>,
}
#[cfg(feature = "simd")]
impl From<Simdf32x4> for Quaternion<f32> {
#[inline]
fn from(f: Simdf32x4) -> Self {
unsafe {
let mut ret: Self = mem::uninitialized();
{
let ret_mut: &mut [f32; 4] = ret.as_mut();
f.store(ret_mut.as_mut(), 0 as usize);
}
ret
}
}
}
#[cfg(feature = "simd")]
impl Into<Simdf32x4> for Quaternion<f32> {
#[inline]
fn into(self) -> Simdf32x4 {
let self_ref: &[f32; 4] = self.as_ref();
Simdf32x4::load(self_ref.as_ref(), 0 as usize)
}
}
impl<S> Quaternion<S> {
/// Construct a new quaternion from one scalar component and three
/// imaginary components.
@ -260,31 +233,11 @@ impl<S: NumCast + Copy> Quaternion<S> {
}
}
#[cfg(not(feature = "simd"))]
impl<S: BaseFloat> InnerSpace for Quaternion<S> {
#[inline]
fn dot(self, other: Quaternion<S>) -> S {
default_fn!( dot(self, other: Quaternion<S>) -> S {
self.s * other.s + self.v.dot(other.v)
}
}
#[cfg(feature = "simd")]
impl<S: BaseFloat> InnerSpace for Quaternion<S> {
#[inline]
default fn dot(self, other: Quaternion<S>) -> S {
self.s * other.s + self.v.dot(other.v)
}
}
#[cfg(feature = "simd")]
impl InnerSpace for Quaternion<f32> {
#[inline]
fn dot(self, other: Quaternion<f32>) -> f32 {
let lhs: Simdf32x4 = self.into();
let rhs: Simdf32x4 = other.into();
let r = lhs * rhs;
r.extract(0) + r.extract(1) + r.extract(2) + r.extract(3)
}
} );
}
impl<A> From<Euler<A>> for Quaternion<A::Unitless>
@ -311,113 +264,32 @@ where
}
}
#[cfg(not(feature = "simd"))]
impl_operator!(<S: BaseFloat> Neg for Quaternion<S> {
fn neg(quat) -> Quaternion<S> {
Quaternion::from_sv(-quat.s, -quat.v)
}
});
#[cfg(feature = "simd")]
impl_operator_default!(<S: BaseFloat> Neg for Quaternion<S> {
fn neg(quat) -> Quaternion<S> {
Quaternion::from_sv(-quat.s, -quat.v)
}
});
#[cfg(feature = "simd")]
impl_operator_simd!{
[Simdf32x4]; Neg for Quaternion<f32> {
fn neg(lhs) -> Quaternion<f32> {
(-lhs).into()
}
}
}
#[cfg(not(feature = "simd"))]
impl_operator!(<S: BaseFloat> Mul<S> for Quaternion<S> {
fn mul(lhs, rhs) -> Quaternion<S> {
Quaternion::from_sv(lhs.s * rhs, lhs.v * rhs)
}
});
#[cfg(feature = "simd")]
impl_operator_default!(<S: BaseFloat> Mul<S> for Quaternion<S> {
fn mul(lhs, rhs) -> Quaternion<S> {
Quaternion::from_sv(lhs.s * rhs, lhs.v * rhs)
}
});
#[cfg(feature = "simd")]
impl_operator_simd!{@rs
[Simdf32x4]; Mul<f32> for Quaternion<f32> {
fn mul(lhs, rhs) -> Quaternion<f32> {
(lhs * rhs).into()
}
}
}
#[cfg(not(feature = "simd"))]
impl_assignment_operator!(<S: BaseFloat> MulAssign<S> for Quaternion<S> {
fn mul_assign(&mut self, scalar) { self.s *= scalar; self.v *= scalar; }
});
#[cfg(feature = "simd")]
impl_assignment_operator_default!(<S: BaseFloat> MulAssign<S> for Quaternion<S> {
fn mul_assign(&mut self, scalar) { self.s *= scalar; self.v *= scalar; }
});
#[cfg(feature = "simd")]
impl MulAssign<f32> for Quaternion<f32> {
fn mul_assign(&mut self, other: f32) {
let s: Simdf32x4 = (*self).into();
let other = Simdf32x4::splat(other);
*self = (s * other).into();
}
}
#[cfg(not(feature = "simd"))]
impl_operator!(<S: BaseFloat> Div<S> for Quaternion<S> {
fn div(lhs, rhs) -> Quaternion<S> {
Quaternion::from_sv(lhs.s / rhs, lhs.v / rhs)
}
});
#[cfg(feature = "simd")]
impl_operator_default!(<S: BaseFloat> Div<S> for Quaternion<S> {
fn div(lhs, rhs) -> Quaternion<S> {
Quaternion::from_sv(lhs.s / rhs, lhs.v / rhs)
}
});
#[cfg(feature = "simd")]
impl_operator_simd!{@rs
[Simdf32x4]; Div<f32> for Quaternion<f32> {
fn div(lhs, rhs) -> Quaternion<f32> {
(lhs / rhs).into()
}
}
}
#[cfg(not(feature = "simd"))]
impl_assignment_operator!(<S: BaseFloat> DivAssign<S> for Quaternion<S> {
fn div_assign(&mut self, scalar) { self.s /= scalar; self.v /= scalar; }
});
#[cfg(feature = "simd")]
impl_assignment_operator_default!(<S: BaseFloat> DivAssign<S> for Quaternion<S> {
fn div_assign(&mut self, scalar) { self.s /= scalar; self.v /= scalar; }
});
#[cfg(feature = "simd")]
impl DivAssign<f32> for Quaternion<f32> {
fn div_assign(&mut self, other: f32) {
let s: Simdf32x4 = (*self).into();
let other = Simdf32x4::splat(other);
*self = (s / other).into();
}
}
impl_operator!(<S: BaseFloat> Rem<S> for Quaternion<S> {
fn rem(lhs, rhs) -> Quaternion<S> {
Quaternion::from_sv(lhs.s % rhs, lhs.v % rhs)
@ -437,93 +309,26 @@ impl_operator!(<S: BaseFloat> Mul<Vector3<S> > for Quaternion<S> {
}}
});
#[cfg(not(feature = "simd"))]
impl_operator!(<S: BaseFloat> Add<Quaternion<S> > for Quaternion<S> {
fn add(lhs, rhs) -> Quaternion<S> {
Quaternion::from_sv(lhs.s + rhs.s, lhs.v + rhs.v)
}
});
#[cfg(feature = "simd")]
impl_operator_default!(<S: BaseFloat> Add<Quaternion<S> > for Quaternion<S> {
fn add(lhs, rhs) -> Quaternion<S> {
Quaternion::from_sv(lhs.s + rhs.s, lhs.v + rhs.v)
}
});
#[cfg(feature = "simd")]
impl_operator_simd!{
[Simdf32x4]; Add<Quaternion<f32>> for Quaternion<f32> {
fn add(lhs, rhs) -> Quaternion<f32> {
(lhs + rhs).into()
}
}
}
#[cfg(not(feature = "simd"))]
impl_assignment_operator!(<S: BaseFloat> AddAssign<Quaternion<S> > for Quaternion<S> {
fn add_assign(&mut self, other) { self.s += other.s; self.v += other.v; }
});
#[cfg(feature = "simd")]
impl_assignment_operator_default!(<S: BaseFloat> AddAssign<Quaternion<S> > for Quaternion<S> {
fn add_assign(&mut self, other) { self.s += other.s; self.v += other.v; }
});
#[cfg(feature = "simd")]
impl AddAssign for Quaternion<f32> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
let s: Simdf32x4 = (*self).into();
let rhs: Simdf32x4 = rhs.into();
*self = (s + rhs).into();
}
}
#[cfg(not(feature = "simd"))]
impl_operator!(<S: BaseFloat> Sub<Quaternion<S> > for Quaternion<S> {
fn sub(lhs, rhs) -> Quaternion<S> {
Quaternion::from_sv(lhs.s - rhs.s, lhs.v - rhs.v)
}
});
#[cfg(feature = "simd")]
impl_operator_default!(<S: BaseFloat> Sub<Quaternion<S> > for Quaternion<S> {
fn sub(lhs, rhs) -> Quaternion<S> {
Quaternion::from_sv(lhs.s - rhs.s, lhs.v - rhs.v)
}
});
#[cfg(feature = "simd")]
impl_operator_simd!{
[Simdf32x4]; Sub<Quaternion<f32>> for Quaternion<f32> {
fn sub(lhs, rhs) -> Quaternion<f32> {
(lhs - rhs).into()
}
}
}
#[cfg(not(feature = "simd"))]
impl_assignment_operator!(<S: BaseFloat> SubAssign<Quaternion<S> > for Quaternion<S> {
fn sub_assign(&mut self, other) { self.s -= other.s; self.v -= other.v; }
});
#[cfg(feature = "simd")]
impl_assignment_operator_default!(<S: BaseFloat> SubAssign<Quaternion<S> > for Quaternion<S> {
fn sub_assign(&mut self, other) { self.s -= other.s; self.v -= other.v; }
});
#[cfg(feature = "simd")]
impl SubAssign for Quaternion<f32> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
let s: Simdf32x4 = (*self).into();
let rhs: Simdf32x4 = rhs.into();
*self = (s - rhs).into();
}
}
#[cfg(not(feature = "simd"))]
impl_operator!(<S: BaseFloat> Mul<Quaternion<S> > for Quaternion<S> {
fn mul(lhs, rhs) -> Quaternion<S> {
Quaternion::new(
@ -535,39 +340,6 @@ impl_operator!(<S: BaseFloat> Mul<Quaternion<S> > for Quaternion<S> {
}
});
#[cfg(feature = "simd")]
impl_operator_default!(<S: BaseFloat> Mul<Quaternion<S> > for Quaternion<S> {
fn mul(lhs, rhs) -> Quaternion<S> {
Quaternion::new(
lhs.s * rhs.s - lhs.v.x * rhs.v.x - lhs.v.y * rhs.v.y - lhs.v.z * rhs.v.z,
lhs.s * rhs.v.x + lhs.v.x * rhs.s + lhs.v.y * rhs.v.z - lhs.v.z * rhs.v.y,
lhs.s * rhs.v.y + lhs.v.y * rhs.s + lhs.v.z * rhs.v.x - lhs.v.x * rhs.v.z,
lhs.s * rhs.v.z + lhs.v.z * rhs.s + lhs.v.x * rhs.v.y - lhs.v.y * rhs.v.x,
)
}
});
#[cfg(feature = "simd")]
impl_operator_simd!{
[Simdf32x4]; Mul<Quaternion<f32>> for Quaternion<f32> {
fn mul(lhs, rhs) -> Quaternion<f32> {
{
let p0 = Simdf32x4::splat(lhs.extract(0)) * rhs;
let p1 = Simdf32x4::splat(lhs.extract(1)) * Simdf32x4::new(
-rhs.extract(1), rhs.extract(0), -rhs.extract(3), rhs.extract(2)
);
let p2 = Simdf32x4::splat(lhs.extract(2)) * Simdf32x4::new(
-rhs.extract(2), rhs.extract(3), rhs.extract(0), -rhs.extract(1)
);
let p3 = Simdf32x4::splat(lhs.extract(3)) * Simdf32x4::new(
-rhs.extract(3), -rhs.extract(2), rhs.extract(1), rhs.extract(0)
);
(p0 + p1 + p2 + p3).into()
}
}
}
}
macro_rules! impl_scalar_mul {
($S:ident) => {
impl_operator!(Mul<Quaternion<$S>> for $S {

149
src/quaternion_simd.rs Normal file
View file

@ -0,0 +1,149 @@
// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors,
// refer to the Cargo.toml file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use quaternion::*;
use structure::*;
use std::mem;
use std::ops::*;
use simd::f32x4 as Simdf32x4;
impl From<Simdf32x4> for Quaternion<f32> {
#[inline]
fn from(f: Simdf32x4) -> Self {
unsafe {
let mut ret: Self = mem::uninitialized();
{
let ret_mut: &mut [f32; 4] = ret.as_mut();
f.store(ret_mut.as_mut(), 0 as usize);
}
ret
}
}
}
impl Into<Simdf32x4> for Quaternion<f32> {
#[inline]
fn into(self) -> Simdf32x4 {
let self_ref: &[f32; 4] = self.as_ref();
Simdf32x4::load(self_ref.as_ref(), 0 as usize)
}
}
impl InnerSpace for Quaternion<f32> {
#[inline]
fn dot(self, other: Quaternion<f32>) -> f32 {
let lhs: Simdf32x4 = self.into();
let rhs: Simdf32x4 = other.into();
let r = lhs * rhs;
r.extract(0) + r.extract(1) + r.extract(2) + r.extract(3)
}
}
impl_operator_simd!{
[Simdf32x4]; Neg for Quaternion<f32> {
fn neg(lhs) -> Quaternion<f32> {
(-lhs).into()
}
}
}
impl_operator_simd!{@rs
[Simdf32x4]; Mul<f32> for Quaternion<f32> {
fn mul(lhs, rhs) -> Quaternion<f32> {
(lhs * rhs).into()
}
}
}
impl MulAssign<f32> for Quaternion<f32> {
fn mul_assign(&mut self, other: f32) {
let s: Simdf32x4 = (*self).into();
let other = Simdf32x4::splat(other);
*self = (s * other).into();
}
}
impl_operator_simd!{@rs
[Simdf32x4]; Div<f32> for Quaternion<f32> {
fn div(lhs, rhs) -> Quaternion<f32> {
(lhs / rhs).into()
}
}
}
impl DivAssign<f32> for Quaternion<f32> {
fn div_assign(&mut self, other: f32) {
let s: Simdf32x4 = (*self).into();
let other = Simdf32x4::splat(other);
*self = (s / other).into();
}
}
impl_operator_simd!{
[Simdf32x4]; Add<Quaternion<f32>> for Quaternion<f32> {
fn add(lhs, rhs) -> Quaternion<f32> {
(lhs + rhs).into()
}
}
}
impl AddAssign for Quaternion<f32> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
let s: Simdf32x4 = (*self).into();
let rhs: Simdf32x4 = rhs.into();
*self = (s + rhs).into();
}
}
impl_operator_simd!{
[Simdf32x4]; Sub<Quaternion<f32>> for Quaternion<f32> {
fn sub(lhs, rhs) -> Quaternion<f32> {
(lhs - rhs).into()
}
}
}
impl SubAssign for Quaternion<f32> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
let s: Simdf32x4 = (*self).into();
let rhs: Simdf32x4 = rhs.into();
*self = (s - rhs).into();
}
}
impl_operator_simd!{
[Simdf32x4]; Mul<Quaternion<f32>> for Quaternion<f32> {
fn mul(lhs, rhs) -> Quaternion<f32> {
{
let p0 = Simdf32x4::splat(lhs.extract(0)) * rhs;
let p1 = Simdf32x4::splat(lhs.extract(1)) * Simdf32x4::new(
-rhs.extract(1), rhs.extract(0), -rhs.extract(3), rhs.extract(2)
);
let p2 = Simdf32x4::splat(lhs.extract(2)) * Simdf32x4::new(
-rhs.extract(2), rhs.extract(3), rhs.extract(0), -rhs.extract(1)
);
let p3 = Simdf32x4::splat(lhs.extract(3)) * Simdf32x4::new(
-rhs.extract(3), -rhs.extract(2), rhs.extract(1), rhs.extract(0)
);
(p0 + p1 + p2 + p3).into()
}
}
}
}

View file

@ -263,7 +263,7 @@ impl<S: BaseFloat> Rotation2<S> for Basis2<S> {
impl<S: fmt::Debug> fmt::Debug for Basis2<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "Basis2 "));
write!(f, "Basis2 ")?;
<[[S; 2]; 2] as fmt::Debug>::fmt(self.mat.as_ref(), f)
}
}
@ -445,7 +445,7 @@ where
impl<S: fmt::Debug> fmt::Debug for Basis3<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "Basis3 "));
write!(f, "Basis3 ")?;
<[[S; 3]; 3] as fmt::Debug>::fmt(self.mat.as_ref(), f)
}
}

View file

@ -30,13 +30,6 @@ use angle::Rad;
use approx;
use num::{BaseFloat, BaseNum};
#[cfg(feature = "simd")]
use simd::f32x4 as Simdf32x4;
#[cfg(feature = "simd")]
use simd::i32x4 as Simdi32x4;
#[cfg(feature = "simd")]
use simd::u32x4 as Simdu32x4;
#[cfg(feature = "mint")]
use mint;
@ -217,7 +210,7 @@ macro_rules! impl_vector {
type Output = $VectorN<S>;
#[inline]
fn neg(self) -> $VectorN<S> { $VectorN::new($(-self.$field),+) }
default_fn!( neg(self) -> $VectorN<S> { $VectorN::new($(-self.$field),+) } );
}
impl<S: BaseFloat> approx::AbsDiffEq for $VectorN<S> {
@ -316,30 +309,30 @@ macro_rules! impl_vector {
});
impl<S: BaseNum> ElementWise for $VectorN<S> {
#[inline] fn add_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field + rhs.$field),+) }
#[inline] fn sub_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field - rhs.$field),+) }
#[inline] fn mul_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field * rhs.$field),+) }
#[inline] fn div_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field / rhs.$field),+) }
#[inline] default_fn!( add_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field + rhs.$field),+) } );
#[inline] default_fn!( sub_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field - rhs.$field),+) } );
#[inline] default_fn!( mul_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field * rhs.$field),+) } );
#[inline] default_fn!( div_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field / rhs.$field),+) } );
#[inline] fn rem_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field % rhs.$field),+) }
#[inline] fn add_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field += rhs.$field);+ }
#[inline] fn sub_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field -= rhs.$field);+ }
#[inline] fn mul_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field *= rhs.$field);+ }
#[inline] fn div_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field /= rhs.$field);+ }
#[inline] default_fn!( add_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field += rhs.$field);+ } );
#[inline] default_fn!( sub_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field -= rhs.$field);+ } );
#[inline] default_fn!( mul_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field *= rhs.$field);+ } );
#[inline] default_fn!( div_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field /= rhs.$field);+ } );
#[inline] fn rem_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field %= rhs.$field);+ }
}
impl<S: BaseNum> ElementWise<S> for $VectorN<S> {
#[inline] fn add_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field + rhs),+) }
#[inline] fn sub_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field - rhs),+) }
#[inline] fn mul_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field * rhs),+) }
#[inline] fn div_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field / rhs),+) }
#[inline] default_fn!( add_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field + rhs),+) } );
#[inline] default_fn!( sub_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field - rhs),+) } );
#[inline] default_fn!( mul_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field * rhs),+) } );
#[inline] default_fn!( div_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field / rhs),+) } );
#[inline] fn rem_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field % rhs),+) }
#[inline] fn add_assign_element_wise(&mut self, rhs: S) { $(self.$field += rhs);+ }
#[inline] fn sub_assign_element_wise(&mut self, rhs: S) { $(self.$field -= rhs);+ }
#[inline] fn mul_assign_element_wise(&mut self, rhs: S) { $(self.$field *= rhs);+ }
#[inline] fn div_assign_element_wise(&mut self, rhs: S) { $(self.$field /= rhs);+ }
#[inline] default_fn!( add_assign_element_wise(&mut self, rhs: S) { $(self.$field += rhs);+ } );
#[inline] default_fn!( sub_assign_element_wise(&mut self, rhs: S) { $(self.$field -= rhs);+ } );
#[inline] default_fn!( mul_assign_element_wise(&mut self, rhs: S) { $(self.$field *= rhs);+ } );
#[inline] default_fn!( div_assign_element_wise(&mut self, rhs: S) { $(self.$field /= rhs);+ } );
#[inline] fn rem_assign_element_wise(&mut self, rhs: S) { $(self.$field %= rhs);+ }
}
@ -364,256 +357,6 @@ macro_rules! impl_vector {
}
}
// Utility macro for generating associated functions for the vectors
// mainly duplication
#[cfg(feature = "simd")]
macro_rules! impl_vector_default {
($VectorN:ident { $($field:ident),+ }, $n:expr, $constructor:ident) => {
impl<S> $VectorN<S> {
/// Construct a new vector, using the provided values.
#[inline]
pub const fn new($($field: S),+) -> $VectorN<S> {
$VectorN { $($field: $field),+ }
}
}
/// The short constructor.
#[inline]
pub const fn $constructor<S>($($field: S),+) -> $VectorN<S> {
$VectorN::new($($field),+)
}
impl<S: BaseFloat> $VectorN<S> {
/// True if all entries in the vector are finite
pub fn is_finite(&self) -> bool {
$(self.$field.is_finite())&&+
}
}
impl<S: NumCast + Copy> $VectorN<S> {
/// Component-wise casting to another type.
#[inline]
pub fn cast<T: NumCast>(&self) -> Option<$VectorN<T>> {
$(
let $field = match NumCast::from(self.$field) {
Some(field) => field,
None => return None
};
)+
Some($VectorN { $($field),+ })
}
}
impl<S: BaseFloat> MetricSpace for $VectorN<S> {
type Metric = S;
#[inline]
fn distance2(self, other: Self) -> S {
(other - self).magnitude2()
}
}
impl<S: Copy> Array for $VectorN<S> {
type Element = S;
#[inline]
fn len() -> usize {
$n
}
#[inline]
fn from_value(scalar: S) -> $VectorN<S> {
$VectorN { $($field: scalar),+ }
}
#[inline]
fn sum(self) -> S where S: Add<Output = S> {
fold_array!(add, { $(self.$field),+ })
}
#[inline]
fn product(self) -> S where S: Mul<Output = S> {
fold_array!(mul, { $(self.$field),+ })
}
fn is_finite(&self) -> bool where S: BaseFloat {
$(self.$field.is_finite())&&+
}
}
impl<S: BaseNum> Zero for $VectorN<S> {
#[inline]
fn zero() -> $VectorN<S> {
$VectorN::from_value(S::zero())
}
#[inline]
fn is_zero(&self) -> bool {
*self == $VectorN::zero()
}
}
impl<S: BaseNum> iter::Sum for $VectorN<S> {
#[inline]
fn sum<I: Iterator<Item=Self>>(iter: I) -> Self {
iter.fold(Self::zero(), Add::add)
}
}
impl<'a, S: 'a + BaseNum> iter::Sum<&'a Self> for $VectorN<S> {
#[inline]
fn sum<I: Iterator<Item=&'a Self>>(iter: I) -> Self {
iter.fold(Self::zero(), Add::add)
}
}
impl<S: BaseNum> VectorSpace for $VectorN<S> {
type Scalar = S;
}
impl<S: Neg<Output = S>> Neg for $VectorN<S> {
type Output = $VectorN<S>;
#[inline]
default fn neg(self) -> $VectorN<S> { $VectorN::new($(-self.$field),+) }
}
impl<S: BaseFloat> approx::AbsDiffEq for $VectorN<S> {
type Epsilon = S::Epsilon;
#[inline]
fn default_epsilon() -> S::Epsilon {
S::default_epsilon()
}
#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: S::Epsilon) -> bool {
$(S::abs_diff_eq(&self.$field, &other.$field, epsilon))&&+
}
}
impl<S: BaseFloat> approx::RelativeEq for $VectorN<S> {
#[inline]
fn default_max_relative() -> S::Epsilon {
S::default_max_relative()
}
#[inline]
fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool {
$(S::relative_eq(&self.$field, &other.$field, epsilon, max_relative))&&+
}
}
impl<S: BaseFloat> approx::UlpsEq for $VectorN<S> {
#[inline]
fn default_max_ulps() -> u32 {
S::default_max_ulps()
}
#[inline]
fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool {
$(S::ulps_eq(&self.$field, &other.$field, epsilon, max_ulps))&&+
}
}
#[cfg(feature = "rand")]
impl<S> Distribution<$VectorN<S>> for Standard
where S: BaseFloat,
Standard: Distribution<S> {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $VectorN<S> {
$VectorN { $($field: rng.gen()),+ }
}
}
impl_operator_default!(<S: BaseNum> Add<$VectorN<S> > for $VectorN<S> {
fn add(lhs, rhs) -> $VectorN<S> { $VectorN::new($(lhs.$field + rhs.$field),+) }
});
impl_assignment_operator_default!(<S: BaseNum> AddAssign<$VectorN<S> > for $VectorN<S> {
fn add_assign(&mut self, other) { $(self.$field += other.$field);+ }
});
impl_operator_default!(<S: BaseNum> Sub<$VectorN<S> > for $VectorN<S> {
fn sub(lhs, rhs) -> $VectorN<S> { $VectorN::new($(lhs.$field - rhs.$field),+) }
});
impl_assignment_operator_default!(<S: BaseNum> SubAssign<$VectorN<S> > for $VectorN<S> {
fn sub_assign(&mut self, other) { $(self.$field -= other.$field);+ }
});
impl_operator_default!(<S: BaseNum> Mul<S> for $VectorN<S> {
fn mul(vector, scalar) -> $VectorN<S> { $VectorN::new($(vector.$field * scalar),+) }
});
impl_assignment_operator_default!(<S: BaseNum> MulAssign<S> for $VectorN<S> {
fn mul_assign(&mut self, scalar) { $(self.$field *= scalar);+ }
});
impl_operator_default!(<S: BaseNum> Div<S> for $VectorN<S> {
fn div(vector, scalar) -> $VectorN<S> { $VectorN::new($(vector.$field / scalar),+) }
});
impl_assignment_operator_default!(<S: BaseNum> DivAssign<S> for $VectorN<S> {
fn div_assign(&mut self, scalar) { $(self.$field /= scalar);+ }
});
impl_operator!(<S: BaseNum> Rem<S> for $VectorN<S> {
fn rem(vector, scalar) -> $VectorN<S> { $VectorN::new($(vector.$field % scalar),+) }
});
impl_assignment_operator!(<S: BaseNum> RemAssign<S> for $VectorN<S> {
fn rem_assign(&mut self, scalar) { $(self.$field %= scalar);+ }
});
impl<S: BaseNum> ElementWise for $VectorN<S> {
#[inline] default fn add_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field + rhs.$field),+) }
#[inline] default fn sub_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field - rhs.$field),+) }
#[inline] default fn mul_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field * rhs.$field),+) }
#[inline] default fn div_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field / rhs.$field),+) }
#[inline] fn rem_element_wise(self, rhs: $VectorN<S>) -> $VectorN<S> { $VectorN::new($(self.$field % rhs.$field),+) }
#[inline] default fn add_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field += rhs.$field);+ }
#[inline] default fn sub_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field -= rhs.$field);+ }
#[inline] default fn mul_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field *= rhs.$field);+ }
#[inline] default fn div_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field /= rhs.$field);+ }
#[inline] fn rem_assign_element_wise(&mut self, rhs: $VectorN<S>) { $(self.$field %= rhs.$field);+ }
}
impl<S: BaseNum> ElementWise<S> for $VectorN<S> {
#[inline] default fn add_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field + rhs),+) }
#[inline] default fn sub_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field - rhs),+) }
#[inline] default fn mul_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field * rhs),+) }
#[inline] default fn div_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field / rhs),+) }
#[inline] fn rem_element_wise(self, rhs: S) -> $VectorN<S> { $VectorN::new($(self.$field % rhs),+) }
#[inline] default fn add_assign_element_wise(&mut self, rhs: S) { $(self.$field += rhs);+ }
#[inline] default fn sub_assign_element_wise(&mut self, rhs: S) { $(self.$field -= rhs);+ }
#[inline] default fn mul_assign_element_wise(&mut self, rhs: S) { $(self.$field *= rhs);+ }
#[inline] default fn div_assign_element_wise(&mut self, rhs: S) { $(self.$field /= rhs);+ }
#[inline] fn rem_assign_element_wise(&mut self, rhs: S) { $(self.$field %= rhs);+ }
}
impl_scalar_ops!($VectorN<usize> { $($field),+ });
impl_scalar_ops!($VectorN<u8> { $($field),+ });
impl_scalar_ops!($VectorN<u16> { $($field),+ });
impl_scalar_ops_default!($VectorN<u32> { $($field),+ });
impl_scalar_ops!($VectorN<u64> { $($field),+ });
impl_scalar_ops!($VectorN<isize> { $($field),+ });
impl_scalar_ops!($VectorN<i8> { $($field),+ });
impl_scalar_ops!($VectorN<i16> { $($field),+ });
impl_scalar_ops_default!($VectorN<i32> { $($field),+ });
impl_scalar_ops!($VectorN<i64> { $($field),+ });
impl_scalar_ops_default!($VectorN<f32> { $($field),+ });
impl_scalar_ops!($VectorN<f64> { $($field),+ });
impl_index_operators!($VectorN<S>, $n, S, usize);
impl_index_operators!($VectorN<S>, $n, [S], Range<usize>);
impl_index_operators!($VectorN<S>, $n, [S], RangeTo<usize>);
impl_index_operators!($VectorN<S>, $n, [S], RangeFrom<usize>);
impl_index_operators!($VectorN<S>, $n, [S], RangeFull);
}
}
macro_rules! impl_scalar_ops {
($VectorN:ident<$S:ident> { $($field:ident),+ }) => {
impl_operator!(Mul<$VectorN<$S>> for $S {
@ -628,28 +371,10 @@ macro_rules! impl_scalar_ops {
};
}
#[cfg(feature = "simd")]
macro_rules! impl_scalar_ops_default {
($VectorN:ident<$S:ident> { $($field:ident),+ }) => {
impl_operator_default!(Mul<$VectorN<$S>> for $S {
fn mul(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar * vector.$field),+) }
});
impl_operator_default!(Div<$VectorN<$S>> for $S {
fn div(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar / vector.$field),+) }
});
impl_operator_default!(Rem<$VectorN<$S>> for $S {
fn rem(scalar, vector) -> $VectorN<$S> { $VectorN::new($(scalar % vector.$field),+) }
});
};
}
impl_vector!(Vector1 { x }, 1, vec1);
impl_vector!(Vector2 { x, y }, 2, vec2);
impl_vector!(Vector3 { x, y, z }, 3, vec3);
#[cfg(not(feature = "simd"))]
impl_vector!(Vector4 { x, y, z, w }, 4, vec4);
#[cfg(feature = "simd")]
impl_vector_default!(Vector4 { x, y, z, w }, 4, vec4);
impl_fixed_array_conversions!(Vector1<S> { x: 0 }, 1);
impl_fixed_array_conversions!(Vector2<S> { x: 0, y: 1 }, 2);
@ -840,431 +565,32 @@ impl<S: BaseFloat> InnerSpace for Vector4<S> {
impl<S: fmt::Debug> fmt::Debug for Vector1<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "Vector1 "));
write!(f, "Vector1 ")?;
<[S; 1] as fmt::Debug>::fmt(self.as_ref(), f)
}
}
impl<S: fmt::Debug> fmt::Debug for Vector2<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "Vector2 "));
write!(f, "Vector2 ")?;
<[S; 2] as fmt::Debug>::fmt(self.as_ref(), f)
}
}
impl<S: fmt::Debug> fmt::Debug for Vector3<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "Vector3 "));
write!(f, "Vector3 ")?;
<[S; 3] as fmt::Debug>::fmt(self.as_ref(), f)
}
}
impl<S: fmt::Debug> fmt::Debug for Vector4<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "Vector4 "));
write!(f, "Vector4 ")?;
<[S; 4] as fmt::Debug>::fmt(self.as_ref(), f)
}
}
#[cfg(feature = "simd")]
impl From<Simdf32x4> for Vector4<f32> {
#[inline]
fn from(f: Simdf32x4) -> Self {
unsafe {
let mut ret: Self = mem::uninitialized();
{
let ret_mut: &mut [f32; 4] = ret.as_mut();
f.store(ret_mut.as_mut(), 0 as usize);
}
ret
}
}
}
#[cfg(feature = "simd")]
impl Vector4<f32> {
/// Compute and return the square root of each element.
#[inline]
pub fn sqrt_element_wide(self) -> Self {
let s: Simdf32x4 = self.into();
s.sqrt().into()
}
/// Compute and return the reciprocal of the square root of each element.
#[inline]
pub fn rsqrt_element_wide(self) -> Self {
let s: Simdf32x4 = self.into();
s.approx_rsqrt().into()
}
/// Compute and return the reciprocal of each element.
#[inline]
pub fn recip_element_wide(self) -> Self {
let s: Simdf32x4 = self.into();
s.approx_reciprocal().into()
}
}
#[cfg(feature = "simd")]
impl Into<Simdf32x4> for Vector4<f32> {
#[inline]
fn into(self) -> Simdf32x4 {
let self_ref: &[f32; 4] = self.as_ref();
Simdf32x4::load(self_ref.as_ref(), 0 as usize)
}
}
#[cfg(feature = "simd")]
impl_operator_simd!{
[Simdf32x4]; Add<Vector4<f32>> for Vector4<f32> {
fn add(lhs, rhs) -> Vector4<f32> {
(lhs + rhs).into()
}
}
}
#[cfg(feature = "simd")]
impl_operator_simd!{
[Simdf32x4]; Sub<Vector4<f32>> for Vector4<f32> {
fn sub(lhs, rhs) -> Vector4<f32> {
(lhs - rhs).into()
}
}
}
#[cfg(feature = "simd")]
impl_operator_simd!{@rs
[Simdf32x4]; Mul<f32> for Vector4<f32> {
fn mul(lhs, rhs) -> Vector4<f32> {
(lhs * rhs).into()
}
}
}
#[cfg(feature = "simd")]
impl_operator_simd!{@rs
[Simdf32x4]; Div<f32> for Vector4<f32> {
fn div(lhs, rhs) -> Vector4<f32> {
(lhs / rhs).into()
}
}
}
#[cfg(feature = "simd")]
impl_operator_simd!{
[Simdf32x4]; Neg for Vector4<f32> {
fn neg(lhs) -> Vector4<f32> {
(-lhs).into()
}
}
}
#[cfg(feature = "simd")]
impl AddAssign for Vector4<f32> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
let s: Simdf32x4 = (*self).into();
let rhs: Simdf32x4 = rhs.into();
*self = (s + rhs).into();
}
}
#[cfg(feature = "simd")]
impl SubAssign for Vector4<f32> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
let s: Simdf32x4 = (*self).into();
let rhs: Simdf32x4 = rhs.into();
*self = (s - rhs).into();
}
}
#[cfg(feature = "simd")]
impl MulAssign<f32> for Vector4<f32> {
fn mul_assign(&mut self, other: f32) {
let s: Simdf32x4 = (*self).into();
let other = Simdf32x4::splat(other);
*self = (s * other).into();
}
}
#[cfg(feature = "simd")]
impl DivAssign<f32> for Vector4<f32> {
fn div_assign(&mut self, other: f32) {
let s: Simdf32x4 = (*self).into();
let other = Simdf32x4::splat(other);
*self = (s / other).into();
}
}
#[cfg(feature = "simd")]
impl ElementWise for Vector4<f32> {
#[inline]
fn add_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> {
self + rhs
}
#[inline]
fn sub_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> {
self - rhs
}
#[inline]
fn mul_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> {
let s: Simdf32x4 = self.into();
let rhs: Simdf32x4 = rhs.into();
(s * rhs).into()
}
#[inline]
fn div_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> {
let s: Simdf32x4 = self.into();
let rhs: Simdf32x4 = rhs.into();
(s / rhs).into()
}
#[inline]
fn add_assign_element_wise(&mut self, rhs: Vector4<f32>) {
(*self) += rhs;
}
#[inline]
fn sub_assign_element_wise(&mut self, rhs: Vector4<f32>) {
(*self) -= rhs;
}
#[inline]
fn mul_assign_element_wise(&mut self, rhs: Vector4<f32>) {
let s: Simdf32x4 = (*self).into();
let rhs: Simdf32x4 = rhs.into();
*self = (s * rhs).into();
}
#[inline]
fn div_assign_element_wise(&mut self, rhs: Vector4<f32>) {
let s: Simdf32x4 = (*self).into();
let rhs: Simdf32x4 = rhs.into();
*self = (s * rhs).into();
}
}
#[cfg(feature = "simd")]
impl ElementWise<f32> for Vector4<f32> {
#[inline]
fn add_element_wise(self, rhs: f32) -> Vector4<f32> {
let s: Simdf32x4 = self.into();
let rhs = Simdf32x4::splat(rhs);
(s + rhs).into()
}
#[inline]
fn sub_element_wise(self, rhs: f32) -> Vector4<f32> {
let s: Simdf32x4 = self.into();
let rhs = Simdf32x4::splat(rhs);
(s - rhs).into()
}
#[inline]
fn mul_element_wise(self, rhs: f32) -> Vector4<f32> {
self * rhs
}
#[inline]
fn div_element_wise(self, rhs: f32) -> Vector4<f32> {
self / rhs
}
#[inline]
fn add_assign_element_wise(&mut self, rhs: f32) {
let s: Simdf32x4 = (*self).into();
let rhs = Simdf32x4::splat(rhs);
*self = (s + rhs).into();
}
#[inline]
fn sub_assign_element_wise(&mut self, rhs: f32) {
let s: Simdf32x4 = (*self).into();
let rhs = Simdf32x4::splat(rhs);
*self = (s - rhs).into();
}
#[inline]
fn mul_assign_element_wise(&mut self, rhs: f32) {
(*self) *= rhs;
}
#[inline]
fn div_assign_element_wise(&mut self, rhs: f32) {
(*self) /= rhs;
}
}
#[cfg(feature = "simd")]
impl From<Simdi32x4> for Vector4<i32> {
#[inline]
fn from(f: Simdi32x4) -> Self {
unsafe {
let mut ret: Self = mem::uninitialized();
{
let ret_mut: &mut [i32; 4] = ret.as_mut();
f.store(ret_mut.as_mut(), 0 as usize);
}
ret
}
}
}
#[cfg(feature = "simd")]
impl Into<Simdi32x4> for Vector4<i32> {
#[inline]
fn into(self) -> Simdi32x4 {
let self_ref: &[i32; 4] = self.as_ref();
Simdi32x4::load(self_ref.as_ref(), 0 as usize)
}
}
#[cfg(feature = "simd")]
impl_operator_simd!{
[Simdi32x4]; Add<Vector4<i32>> for Vector4<i32> {
fn add(lhs, rhs) -> Vector4<i32> {
(lhs + rhs).into()
}
}
}
#[cfg(feature = "simd")]
impl_operator_simd!{
[Simdi32x4]; Sub<Vector4<i32>> for Vector4<i32> {
fn sub(lhs, rhs) -> Vector4<i32> {
(lhs - rhs).into()
}
}
}
#[cfg(feature = "simd")]
impl_operator_simd!{@rs
[Simdi32x4]; Mul<i32> for Vector4<i32> {
fn mul(lhs, rhs) -> Vector4<i32> {
(lhs * rhs).into()
}
}
}
#[cfg(feature = "simd")]
impl_operator_simd!{
[Simdi32x4]; Neg for Vector4<i32> {
fn neg(lhs) -> Vector4<i32> {
(-lhs).into()
}
}
}
#[cfg(feature = "simd")]
impl AddAssign for Vector4<i32> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
let s: Simdi32x4 = (*self).into();
let rhs: Simdi32x4 = rhs.into();
*self = (s + rhs).into();
}
}
#[cfg(feature = "simd")]
impl SubAssign for Vector4<i32> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
let s: Simdi32x4 = (*self).into();
let rhs: Simdi32x4 = rhs.into();
*self = (s - rhs).into();
}
}
#[cfg(feature = "simd")]
impl MulAssign<i32> for Vector4<i32> {
fn mul_assign(&mut self, other: i32) {
let s: Simdi32x4 = (*self).into();
let other = Simdi32x4::splat(other);
*self = (s * other).into();
}
}
#[cfg(feature = "simd")]
impl From<Simdu32x4> for Vector4<u32> {
#[inline]
fn from(f: Simdu32x4) -> Self {
unsafe {
let mut ret: Self = mem::uninitialized();
{
let ret_mut: &mut [u32; 4] = ret.as_mut();
f.store(ret_mut.as_mut(), 0 as usize);
}
ret
}
}
}
#[cfg(feature = "simd")]
impl Into<Simdu32x4> for Vector4<u32> {
#[inline]
fn into(self) -> Simdu32x4 {
let self_ref: &[u32; 4] = self.as_ref();
Simdu32x4::load(self_ref.as_ref(), 0 as usize)
}
}
#[cfg(feature = "simd")]
impl_operator_simd!{
[Simdu32x4]; Add<Vector4<u32>> for Vector4<u32> {
fn add(lhs, rhs) -> Vector4<u32> {
(lhs + rhs).into()
}
}
}
#[cfg(feature = "simd")]
impl_operator_simd!{
[Simdu32x4]; Sub<Vector4<u32>> for Vector4<u32> {
fn sub(lhs, rhs) -> Vector4<u32> {
(lhs - rhs).into()
}
}
}
#[cfg(feature = "simd")]
impl_operator_simd!{@rs
[Simdu32x4]; Mul<u32> for Vector4<u32> {
fn mul(lhs, rhs) -> Vector4<u32> {
(lhs * rhs).into()
}
}
}
#[cfg(feature = "simd")]
impl AddAssign for Vector4<u32> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
let s: Simdu32x4 = (*self).into();
let rhs: Simdu32x4 = rhs.into();
*self = (s + rhs).into();
}
}
#[cfg(feature = "simd")]
impl SubAssign for Vector4<u32> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
let s: Simdu32x4 = (*self).into();
let rhs: Simdu32x4 = rhs.into();
*self = (s - rhs).into();
}
}
#[cfg(feature = "simd")]
impl MulAssign<u32> for Vector4<u32> {
fn mul_assign(&mut self, other: u32) {
let s: Simdu32x4 = (*self).into();
let other = Simdu32x4::splat(other);
*self = (s * other).into();
}
}
#[cfg(feature = "mint")]
impl_mint_conversions!(Vector2 { x, y }, Vector2);
#[cfg(feature = "mint")]

393
src/vector_simd.rs Normal file
View file

@ -0,0 +1,393 @@
// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors,
// refer to the Cargo.toml file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use vector::*;
use structure::*;
use std::mem;
use std::ops::*;
use simd::f32x4 as Simdf32x4;
use simd::i32x4 as Simdi32x4;
use simd::u32x4 as Simdu32x4;
impl From<Simdf32x4> for Vector4<f32> {
#[inline]
fn from(f: Simdf32x4) -> Self {
unsafe {
let mut ret: Self = mem::uninitialized();
{
let ret_mut: &mut [f32; 4] = ret.as_mut();
f.store(ret_mut.as_mut(), 0 as usize);
}
ret
}
}
}
impl Vector4<f32> {
/// Compute and return the square root of each element.
#[inline]
pub fn sqrt_element_wide(self) -> Self {
let s: Simdf32x4 = self.into();
s.sqrt().into()
}
/// Compute and return the reciprocal of the square root of each element.
#[inline]
pub fn rsqrt_element_wide(self) -> Self {
let s: Simdf32x4 = self.into();
s.approx_rsqrt().into()
}
/// Compute and return the reciprocal of each element.
#[inline]
pub fn recip_element_wide(self) -> Self {
let s: Simdf32x4 = self.into();
s.approx_reciprocal().into()
}
}
impl Into<Simdf32x4> for Vector4<f32> {
#[inline]
fn into(self) -> Simdf32x4 {
let self_ref: &[f32; 4] = self.as_ref();
Simdf32x4::load(self_ref.as_ref(), 0 as usize)
}
}
impl_operator_simd!{
[Simdf32x4]; Add<Vector4<f32>> for Vector4<f32> {
fn add(lhs, rhs) -> Vector4<f32> {
(lhs + rhs).into()
}
}
}
impl_operator_simd!{
[Simdf32x4]; Sub<Vector4<f32>> for Vector4<f32> {
fn sub(lhs, rhs) -> Vector4<f32> {
(lhs - rhs).into()
}
}
}
impl_operator_simd!{@rs
[Simdf32x4]; Mul<f32> for Vector4<f32> {
fn mul(lhs, rhs) -> Vector4<f32> {
(lhs * rhs).into()
}
}
}
impl_operator_simd!{@rs
[Simdf32x4]; Div<f32> for Vector4<f32> {
fn div(lhs, rhs) -> Vector4<f32> {
(lhs / rhs).into()
}
}
}
impl_operator_simd!{
[Simdf32x4]; Neg for Vector4<f32> {
fn neg(lhs) -> Vector4<f32> {
(-lhs).into()
}
}
}
impl AddAssign for Vector4<f32> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
let s: Simdf32x4 = (*self).into();
let rhs: Simdf32x4 = rhs.into();
*self = (s + rhs).into();
}
}
impl SubAssign for Vector4<f32> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
let s: Simdf32x4 = (*self).into();
let rhs: Simdf32x4 = rhs.into();
*self = (s - rhs).into();
}
}
impl MulAssign<f32> for Vector4<f32> {
fn mul_assign(&mut self, other: f32) {
let s: Simdf32x4 = (*self).into();
let other = Simdf32x4::splat(other);
*self = (s * other).into();
}
}
impl DivAssign<f32> for Vector4<f32> {
fn div_assign(&mut self, other: f32) {
let s: Simdf32x4 = (*self).into();
let other = Simdf32x4::splat(other);
*self = (s / other).into();
}
}
impl ElementWise for Vector4<f32> {
#[inline]
fn add_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> {
self + rhs
}
#[inline]
fn sub_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> {
self - rhs
}
#[inline]
fn mul_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> {
let s: Simdf32x4 = self.into();
let rhs: Simdf32x4 = rhs.into();
(s * rhs).into()
}
#[inline]
fn div_element_wise(self, rhs: Vector4<f32>) -> Vector4<f32> {
let s: Simdf32x4 = self.into();
let rhs: Simdf32x4 = rhs.into();
(s / rhs).into()
}
#[inline]
fn add_assign_element_wise(&mut self, rhs: Vector4<f32>) {
(*self) += rhs;
}
#[inline]
fn sub_assign_element_wise(&mut self, rhs: Vector4<f32>) {
(*self) -= rhs;
}
#[inline]
fn mul_assign_element_wise(&mut self, rhs: Vector4<f32>) {
let s: Simdf32x4 = (*self).into();
let rhs: Simdf32x4 = rhs.into();
*self = (s * rhs).into();
}
#[inline]
fn div_assign_element_wise(&mut self, rhs: Vector4<f32>) {
let s: Simdf32x4 = (*self).into();
let rhs: Simdf32x4 = rhs.into();
*self = (s * rhs).into();
}
}
impl ElementWise<f32> for Vector4<f32> {
#[inline]
fn add_element_wise(self, rhs: f32) -> Vector4<f32> {
let s: Simdf32x4 = self.into();
let rhs = Simdf32x4::splat(rhs);
(s + rhs).into()
}
#[inline]
fn sub_element_wise(self, rhs: f32) -> Vector4<f32> {
let s: Simdf32x4 = self.into();
let rhs = Simdf32x4::splat(rhs);
(s - rhs).into()
}
#[inline]
fn mul_element_wise(self, rhs: f32) -> Vector4<f32> {
self * rhs
}
#[inline]
fn div_element_wise(self, rhs: f32) -> Vector4<f32> {
self / rhs
}
#[inline]
fn add_assign_element_wise(&mut self, rhs: f32) {
let s: Simdf32x4 = (*self).into();
let rhs = Simdf32x4::splat(rhs);
*self = (s + rhs).into();
}
#[inline]
fn sub_assign_element_wise(&mut self, rhs: f32) {
let s: Simdf32x4 = (*self).into();
let rhs = Simdf32x4::splat(rhs);
*self = (s - rhs).into();
}
#[inline]
fn mul_assign_element_wise(&mut self, rhs: f32) {
(*self) *= rhs;
}
#[inline]
fn div_assign_element_wise(&mut self, rhs: f32) {
(*self) /= rhs;
}
}
impl From<Simdi32x4> for Vector4<i32> {
#[inline]
fn from(f: Simdi32x4) -> Self {
unsafe {
let mut ret: Self = mem::uninitialized();
{
let ret_mut: &mut [i32; 4] = ret.as_mut();
f.store(ret_mut.as_mut(), 0 as usize);
}
ret
}
}
}
impl Into<Simdi32x4> for Vector4<i32> {
#[inline]
fn into(self) -> Simdi32x4 {
let self_ref: &[i32; 4] = self.as_ref();
Simdi32x4::load(self_ref.as_ref(), 0 as usize)
}
}
impl_operator_simd!{
[Simdi32x4]; Add<Vector4<i32>> for Vector4<i32> {
fn add(lhs, rhs) -> Vector4<i32> {
(lhs + rhs).into()
}
}
}
impl_operator_simd!{
[Simdi32x4]; Sub<Vector4<i32>> for Vector4<i32> {
fn sub(lhs, rhs) -> Vector4<i32> {
(lhs - rhs).into()
}
}
}
impl_operator_simd!{@rs
[Simdi32x4]; Mul<i32> for Vector4<i32> {
fn mul(lhs, rhs) -> Vector4<i32> {
(lhs * rhs).into()
}
}
}
impl_operator_simd!{
[Simdi32x4]; Neg for Vector4<i32> {
fn neg(lhs) -> Vector4<i32> {
(-lhs).into()
}
}
}
impl AddAssign for Vector4<i32> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
let s: Simdi32x4 = (*self).into();
let rhs: Simdi32x4 = rhs.into();
*self = (s + rhs).into();
}
}
impl SubAssign for Vector4<i32> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
let s: Simdi32x4 = (*self).into();
let rhs: Simdi32x4 = rhs.into();
*self = (s - rhs).into();
}
}
impl MulAssign<i32> for Vector4<i32> {
fn mul_assign(&mut self, other: i32) {
let s: Simdi32x4 = (*self).into();
let other = Simdi32x4::splat(other);
*self = (s * other).into();
}
}
impl From<Simdu32x4> for Vector4<u32> {
#[inline]
fn from(f: Simdu32x4) -> Self {
unsafe {
let mut ret: Self = mem::uninitialized();
{
let ret_mut: &mut [u32; 4] = ret.as_mut();
f.store(ret_mut.as_mut(), 0 as usize);
}
ret
}
}
}
impl Into<Simdu32x4> for Vector4<u32> {
#[inline]
fn into(self) -> Simdu32x4 {
let self_ref: &[u32; 4] = self.as_ref();
Simdu32x4::load(self_ref.as_ref(), 0 as usize)
}
}
impl_operator_simd!{
[Simdu32x4]; Add<Vector4<u32>> for Vector4<u32> {
fn add(lhs, rhs) -> Vector4<u32> {
(lhs + rhs).into()
}
}
}
impl_operator_simd!{
[Simdu32x4]; Sub<Vector4<u32>> for Vector4<u32> {
fn sub(lhs, rhs) -> Vector4<u32> {
(lhs - rhs).into()
}
}
}
impl_operator_simd!{@rs
[Simdu32x4]; Mul<u32> for Vector4<u32> {
fn mul(lhs, rhs) -> Vector4<u32> {
(lhs * rhs).into()
}
}
}
impl AddAssign for Vector4<u32> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
let s: Simdu32x4 = (*self).into();
let rhs: Simdu32x4 = rhs.into();
*self = (s + rhs).into();
}
}
impl SubAssign for Vector4<u32> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
let s: Simdu32x4 = (*self).into();
let rhs: Simdu32x4 = rhs.into();
*self = (s - rhs).into();
}
}
impl MulAssign<u32> for Vector4<u32> {
fn mul_assign(&mut self, other: u32) {
let s: Simdu32x4 = (*self).into();
let other = Simdu32x4::splat(other);
*self = (s * other).into();
}
}

View file

@ -281,7 +281,7 @@ pub mod matrix3 {
}
#[test]
fn test_determinant() {;
fn test_determinant() {
assert_eq!(A.determinant(), 0.0f64);
}