/** * @file llvector4a.h * @brief LLVector4a class header file - memory aligned and vectorized 4 component vector * * $LicenseInfo:firstyear=2010&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LL_LLVECTOR4A_H #define LL_LLVECTOR4A_H class LLRotation; #include #include "llpreprocessor.h" #include "llmemory.h" /////////////////////////////////// // FIRST TIME USERS PLEASE READ ////////////////////////////////// // This is just the beginning of LLVector4a. There are many more useful functions // yet to be implemented. For example, setNeg to negate a vector, rotate() to apply // a matrix rotation, various functions to manipulate only the X, Y, and Z elements // and many others (including a whole variety of accessors). So if you don't see a // function here that you need, please contact Falcon or someone else with SSE // experience (Richard, I think, has some and davep has a little as of the time // of this writing, July 08, 2010) about getting it implemented before you resort to // LLVector3/LLVector4. ///////////////////////////////// class alignas(16) LLVector4a { LL_ALIGN_NEW public: /////////////////////////////////// // STATIC METHODS /////////////////////////////////// // Call initClass() at startup to avoid 15,000+ cycle penalties from denormalized numbers static void initClass() { _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); } // Return a vector of all zeros static inline const LLVector4a& getZero() { extern const LLVector4a LL_V4A_ZERO; return LL_V4A_ZERO; } // Return a vector of all epsilon, where epsilon is a small float suitable for approximate equality checks static inline const LLVector4a& getEpsilon() { extern const LLVector4a LL_V4A_EPSILON; return LL_V4A_EPSILON; } // Copy 16 bytes from src to dst. Source and destination must be 16-byte aligned static inline void copy4a(F32* dst, const F32* src) { _mm_store_ps(dst, _mm_load_ps(src)); } // Copy words 16-byte blocks from src to dst. Source and destination must not overlap. // Source and dest must be 16-byte aligned and size must be multiple of 16. static void memcpyNonAliased16(F32* __restrict dst, const F32* __restrict src, size_t bytes); //////////////////////////////////// // CONSTRUCTORS //////////////////////////////////// //LLVector4a is plain data which should never have a default constructor or destructor(malloc&free won't trigger it) LLVector4a() { //DO NOT INITIALIZE -- The overhead is completely unnecessary ll_assert_aligned(this,16); } LLVector4a(F32 x, F32 y, F32 z, F32 w = 0.f) { set(x,y,z,w); } LLVector4a(F32 x) { splat(x); } LLVector4a(const LLSimdScalar& x) { splat(x); } LLVector4a(LLQuad q) { mQ = q; } bool operator==(const LLVector4a& rhs) const { return equals4(rhs); } bool operator!=(const LLVector4a& rhs) const { return !(*this == rhs); } const LLVector4a& operator+=(const LLVector4a& rhs) { add(rhs); return *this; } const LLVector4a& operator-=(const LLVector4a& rhs) { sub(rhs); return *this; } LLVector4a operator+(const LLVector4a& rhs) const { LLVector4a result = *this; result.add(rhs); return result; } LLVector4a operator-(const LLVector4a& rhs) const { LLVector4a result = *this; result.sub(rhs); return result; } LLVector4a cross3(const LLVector4a& b) const { LLVector4a result; result.setCross3(*this, b); return result; } //////////////////////////////////// // LOAD/STORE //////////////////////////////////// // Load from 16-byte aligned src array (preferred method of loading) inline void load4a(const F32* src); // Load from unaligned src array (NB: Significantly slower than load4a) inline void loadua(const F32* src); // Load only three floats beginning at address 'src'. Slowest method. inline void load3(const F32* src); // Store to a 16-byte aligned memory address inline void store4a(F32* dst) const; //////////////////////////////////// // BASIC GET/SET //////////////////////////////////// // Return a "this" as an F32 pointer. inline F32* getF32ptr(); // Return a "this" as a const F32 pointer. inline const F32* const getF32ptr() const; // Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates // the data at the whole vector level or you will incur a substantial penalty. Consider using the splat functions instead inline F32 operator[](const S32 idx) const; // Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time. inline LLSimdScalar getScalarAt(const S32 idx) const; // Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time. template LL_FORCE_INLINE LLSimdScalar getScalarAt() const; // Set to an x, y, z and optional w provided inline void set(F32 x, F32 y, F32 z, F32 w = 0.f); // Set to all zeros. This is preferred to using ::getZero() inline void clear(); // Set all elements to 'x' inline void splat(const F32 x); // Set all elements to 'x' inline void splat(const LLSimdScalar& x); // Set all 4 elements to element N of src, with N known at compile time template void splat(const LLVector4a& src); // Set all 4 elements to element i of v, with i NOT known at compile time inline void splat(const LLVector4a& v, U32 i); // Select bits from sourceIfTrue and sourceIfFalse according to bits in mask inline void setSelectWithMask( const LLVector4Logical& mask, const LLVector4a& sourceIfTrue, const LLVector4a& sourceIfFalse ); //////////////////////////////////// // ALGEBRAIC //////////////////////////////////// // Set this to the element-wise (a + b) inline void setAdd(const LLVector4a& a, const LLVector4a& b); // Set this to element-wise (a - b) inline void setSub(const LLVector4a& a, const LLVector4a& b); // Set this to element-wise multiply (a * b) inline void setMul(const LLVector4a& a, const LLVector4a& b); // Set this to element-wise quotient (a / b) inline void setDiv(const LLVector4a& a, const LLVector4a& b); // Set this to the element-wise absolute value of src inline void setAbs(const LLVector4a& src); // Add to each component in this vector the corresponding component in rhs inline void add(const LLVector4a& rhs); // Subtract from each component in this vector the corresponding component in rhs inline void sub(const LLVector4a& rhs); // Multiply each component in this vector by the corresponding component in rhs inline void mul(const LLVector4a& rhs); // Divide each component in this vector by the corresponding component in rhs inline void div(const LLVector4a& rhs); // Multiply this vector by x in a scalar fashion inline void mul(const F32 x); // Set this to (a x b) (geometric cross-product) inline void setCross3(const LLVector4a& a, const LLVector4a& b); // Set all elements to the dot product of the x, y, and z elements in a and b inline void setAllDot3(const LLVector4a& a, const LLVector4a& b); // Set all elements to the dot product of the x, y, z, and w elements in a and b inline void setAllDot4(const LLVector4a& a, const LLVector4a& b); // Return the 3D dot product of this vector and b inline LLSimdScalar dot3(const LLVector4a& b) const; // Return the 4D dot product of this vector and b inline LLSimdScalar dot4(const LLVector4a& b) const; // Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed // Note that this does not consider zero length vectors! inline void normalize3(); // Same as normalize3() but with respect to all 4 components inline void normalize4(); // Same as normalize3(), but returns length as a SIMD scalar inline LLSimdScalar normalize3withLength(); // Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed // Note that this does not consider zero length vectors! inline void normalize3fast(); // Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed // Same as above except substitutes default vector contents if the vector is non-finite or degenerate due to zero length. // inline void normalize3fast_checked(LLVector4a* d = 0); // Return true if this vector is normalized with respect to x,y,z up to tolerance inline LLBool32 isNormalized3( F32 tolerance = 1e-3 ) const; // Return true if this vector is normalized with respect to all components up to tolerance inline LLBool32 isNormalized4( F32 tolerance = 1e-3 ) const; // Set all elements to the length of vector 'v' inline void setAllLength3( const LLVector4a& v ); // Get this vector's length inline LLSimdScalar getLength3() const; // Set the components of this vector to the minimum of the corresponding components of lhs and rhs inline void setMin(const LLVector4a& lhs, const LLVector4a& rhs); // Set the components of this vector to the maximum of the corresponding components of lhs and rhs inline void setMax(const LLVector4a& lhs, const LLVector4a& rhs); // Clamps this vector to be within the component-wise range low to high (inclusive) inline void clamp( const LLVector4a& low, const LLVector4a& high ); // Set this to (c * lhs) + rhs * ( 1 - c) inline void setLerp(const LLVector4a& lhs, const LLVector4a& rhs, F32 c); // Return true (nonzero) if x, y, z (and w for Finite4) are all finite floats inline LLBool32 isFinite3() const; inline LLBool32 isFinite4() const; // Set this vector to 'vec' rotated by the LLRotation or LLQuaternion2 provided void setRotated( const LLRotation& rot, const LLVector4a& vec ); void setRotated( const class LLQuaternion2& quat, const LLVector4a& vec ); // Set this vector to 'vec' rotated by the INVERSE of the LLRotation or LLQuaternion2 provided inline void setRotatedInv( const LLRotation& rot, const LLVector4a& vec ); inline void setRotatedInv( const class LLQuaternion2& quat, const LLVector4a& vec ); // Quantize this vector to 8 or 16 bit precision void quantize8( const LLVector4a& low, const LLVector4a& high ); void quantize16( const LLVector4a& low, const LLVector4a& high ); //////////////////////////////////// // LOGICAL //////////////////////////////////// // The functions in this section will compare the elements in this vector // to those in rhs and return an LLVector4Logical with all bits set in elements // where the comparison was true and all bits unset in elements where the comparison // was false. See llvector4logica.h //////////////////////////////////// // WARNING: Other than equals3 and equals4, these functions do NOT account // for floating point tolerance. You should include the appropriate tolerance // in the inputs. //////////////////////////////////// inline LLVector4Logical greaterThan(const LLVector4a& rhs) const; inline LLVector4Logical lessThan(const LLVector4a& rhs) const; inline LLVector4Logical greaterEqual(const LLVector4a& rhs) const; inline LLVector4Logical lessEqual(const LLVector4a& rhs) const; inline LLVector4Logical equal(const LLVector4a& rhs) const; // Returns true if this and rhs are componentwise equal up to the specified absolute tolerance inline bool equals4(const LLVector4a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const; inline bool equals3(const LLVector4a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const; //////////////////////////////////// // OPERATORS //////////////////////////////////// // Do NOT add aditional operators without consulting someone with SSE experience inline const LLVector4a& operator= ( const LLVector4a& rhs ); inline const LLVector4a& operator= ( const LLQuad& rhs ); inline operator LLQuad() const; private: LLQuad mQ{}; }; inline void update_min_max(LLVector4a& min, LLVector4a& max, const LLVector4a& p) { min.setMin(min, p); max.setMax(max, p); } inline std::ostream& operator<<(std::ostream& s, const LLVector4a& v) { s << "(" << v[0] << ", " << v[1] << ", " << v[2] << ", " << v[3] << ")"; return s; } #endif