/* * Copyright 2014 Google Inc. All rights reserved. * * 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. */ #ifndef MATHFU_UTILITIES_H_ #define MATHFU_UTILITIES_H_ #include #include #include #include #include #include /// @file mathfu/utilities.h Utilities /// @brief Utility macros and functions. /// @addtogroup mathfu_build_config /// /// By default MathFu will attempt to build with SIMD optimizations enabled /// based upon the target architecture and compiler options. However, it's /// possible to change the default build configuration using the following /// macros: /// /// @li @ref MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT /// @li @ref MATHFU_COMPILE_FORCE_PADDING /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// ///
MATHFU_COMPILE_WITHOUT_SIMD_SUPPORTMATHFU_COMPILE_FORCE_PADDINGConfiguration
undefinedundefined or 1Default build configuration, SIMD optimization is enabled based upon /// the target architecture, compiler options and MathFu library /// support.
undefined0If SIMD is supported, padding of data structures is disabled. See /// @ref MATHFU_COMPILE_FORCE_PADDING for more information.
definedundefined/0/1Builds MathFu with explicit SIMD optimization disabled. The compiler /// could still potentially optimize some code paths with SIMD /// instructions based upon the compiler options.
#ifdef DOXYGEN /// @addtogroup mathfu_build_config /// @{ /// @def MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT /// @brief Disable SIMD build configuration. /// /// When defined, this macro disables the default behavior of trying to /// build the library with SIMD enabled based upon the target architecture /// and compiler options. /// /// To use this build option, this macro must be defined in all modules /// of the project. #define MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT /// @} #endif // DOXYGEN #if !defined(MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT) #if defined(__SSE__) #define MATHFU_COMPILE_WITH_SIMD #elif defined(__ARM_NEON__) #define MATHFU_COMPILE_WITH_SIMD #elif defined(_M_IX86_FP) // MSVC #if _M_IX86_FP >= 1 // SSE enabled #define MATHFU_COMPILE_WITH_SIMD #endif // _M_IX86_FP >= 1 #endif #endif // !defined(MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT) #ifdef DOXYGEN /// @addtogroup mathfu_build_config /// @{ /// @def MATHFU_COMPILE_FORCE_PADDING /// @brief Enable / disable padding of data structures. /// /// By default, when @ref MATHFU_COMPILE_FORCE_PADDING is not defined, /// data structures are padded when SIMD is enabled /// (i.e when @ref MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT is also not defined). /// /// If @ref MATHFU_COMPILE_FORCE_PADDING is defined as 1, all data /// structures are padded to a power of 2 size which enables more efficient /// SIMD operations. This is the default build configuration when SIMD is /// enabled. /// /// If @ref MATHFU_COMPILE_FORCE_PADDING is defined as 0, all data /// structures are packed by the compiler (with no padding) even when the SIMD /// build configuration is enabled. This build option can be useful in the /// rare occasion an application is CPU memory bandwidth constrained, at the /// expense of additional instructions to copy to / from SIMD registers. /// /// To use this build option, this macro must be defined in all modules /// of the project. /// /// @see MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT #define MATHFU_COMPILE_FORCE_PADDING /// @} #endif // DOXYGEN #ifdef MATHFU_COMPILE_WITH_SIMD /// @cond MATHFU_INTERNAL /// @addtogroup mathfu_build_config /// @{ /// @def MATHFU_COMPILE_WITH_PADDING /// @brief Enable padding of data structures to be efficient with SIMD. /// /// When defined, this option enables padding of some data structures (e.g /// @ref vec3) to be more efficient with SIMD operations. This option is /// only applicable when @ref MATHFU_COMPILE_WITHOUT_SIMD is not defined and /// the target architecture and compiler support SIMD. /// /// To use this build option, this macro must be defined in all modules /// of the project. /// @see MATHFU_COMPILE_FORCE_PADDING #define MATHFU_COMPILE_WITH_PADDING /// @} #if defined(MATHFU_COMPILE_FORCE_PADDING) #if MATHFU_COMPILE_FORCE_PADDING == 1 #if !defined(MATHFU_COMPILE_WITH_PADDING) #define MATHFU_COMPILE_WITH_PADDING #endif // !defined(MATHFU_COMPILE_WITH_PADDING) #else #if defined(MATHFU_COMPILE_WITH_PADDING) #undef MATHFU_COMPILE_WITH_PADDING #endif // MATHFU_COMPILE_WITH_PADDING #endif // MATHFU_COMPILE_FORCE_PADDING == 1 #endif // MATHFU_COMPILE_FORCE_PADDING /// @endcond #endif // MATHFU_COMPILE_WITH_SIMD /// @addtogroup mathfu_version /// @{ /// @def MATHFU_VERSION_MAJOR /// @brief Major version number of the library. /// @see kMathFuVersionString #define MATHFU_VERSION_MAJOR 1 /// @def MATHFU_VERSION_MINOR /// @brief Minor version number of the library. /// @see kMathFuVersionString #define MATHFU_VERSION_MINOR 1 /// @def MATHFU_VERSION_REVISION /// @brief Revision number of the library. /// @see kMathFuVersionString #define MATHFU_VERSION_REVISION 0 /// @} /// @cond MATHFU_INTERNAL #define MATHFU_STRING_EXPAND(X) #X #define MATHFU_STRING(X) MATHFU_STRING_EXPAND(X) /// @endcond /// @cond MATHFU_INTERNAL // Generate string which contains build options for the library. #if defined(MATHFU_COMPILE_WITH_SIMD) #define MATHFU_BUILD_OPTIONS_SIMD "[simd]" #else #define MATHFU_BUILD_OPTIONS_SIMD "[no simd]" #endif // defined(MATHFU_COMPILE_WITH_SIMD) #if defined(MATHFU_COMPILE_WITH_PADDING) #define MATHFU_BUILD_OPTIONS_PADDING "[padding]" #else #define MATHFU_BUILD_OPTIONS_PADDING "[no padding]" #endif // defined(MATHFU_COMPILE_WITH_PADDING) /// @endcond /// @addtogroup mathfu_version /// @{ /// @def MATHFU_BUILD_OPTIONS_STRING /// @brief String that describes the library's build configuration. #define MATHFU_BUILD_OPTIONS_STRING \ (MATHFU_BUILD_OPTIONS_SIMD " " MATHFU_BUILD_OPTIONS_PADDING) /// @} // Weak linkage is culled by VS & doesn't work on cygwin. #if !defined(_WIN32) && !defined(__CYGWIN__) extern volatile __attribute__((weak)) const char *kMathFuVersionString; /// @addtogroup mathfu_version /// @{ /// @var kMathFuVersionString /// @brief String which identifies the current version of MathFu. /// /// @ref kMathFuVersionString is used by Google developers to identify which /// applications uploaded to Google Play are using this library. This allows /// the development team at Google to determine the popularity of the library. /// How it works: Applications that are uploaded to the Google Play Store are /// scanned for this version string. We track which applications are using it /// to measure popularity. You are free to remove it (of course) but we would /// appreciate if you left it in. /// /// @see MATHFU_VERSION_MAJOR /// @see MATHFU_VERSION_MINOR /// @see MATHFU_VERSION_REVISION volatile __attribute__((weak)) const char *kMathFuVersionString = "MathFu " MATHFU_STRING(MATHFU_VERSION_MAJOR) "." MATHFU_STRING( MATHFU_VERSION_MINOR) "." MATHFU_STRING(MATHFU_VERSION_REVISION); /// @} #endif // !defined(_WIN32) && !defined(__CYGWIN__) /// @cond MATHFU_INTERNAL template struct static_assert_util; template <> struct static_assert_util {}; /// @endcond /// @addtogroup mathfu_utilities /// @{ /// @def MATHFU_STATIC_ASSERT /// @brief Compile time assert for pre-C++11 compilers. /// /// For example: ///
/// MATHFU_STATIC_ASSERT(0 == 1); ///
will result in a compile error. #define MATHFU_STATIC_ASSERT(x) static_assert_util<(x)>() /// @} /// @cond MATHFU_INTERNAL /// Unroll an loop up to 4 iterations, where iterator is the identifier /// used in each operation (e.g "i"), number_of_iterations is a constant which /// specifies the number of times to perform the operation and "operation" is /// the statement to execute for each iteration of the loop (e.g data[i] = v). #define MATHFU_UNROLLED_LOOP(iterator, number_of_iterations, operation) \ { \ const int iterator = 0; \ { operation; } \ if ((number_of_iterations) > 1) { \ const int iterator = 1; \ { operation; } \ if ((number_of_iterations) > 2) { \ const int iterator = 2; \ { operation; } \ if ((number_of_iterations) > 3) { \ const int iterator = 3; \ { operation; } \ if ((number_of_iterations) > 4) { \ for (int iterator = 4; iterator < (number_of_iterations); \ ++iterator) { \ operation; \ } \ } \ } \ } \ } \ } /// @endcond namespace mathfu { /// @addtogroup mathfu_utilities /// @{ /// @brief Clamp x within [lower, upper]. /// @anchor mathfu_Clamp /// /// @note Results are undefined if lower > upper. /// /// @param x Value to clamp. /// @param lower Lower value of the range. /// @param upper Upper value of the range. /// @returns Clamped value. template T Clamp(const T &x, const T &lower, const T &upper) { return std::max(lower, std::min(x, upper)); } /// @brief Linearly interpolate between range_start and range_end, based on /// percent. /// @anchor mathfu_Lerp /// /// @param range_start Start of the range. /// @param range_end End of the range. /// @param percent Value between 0.0 and 1.0 used to interpolate between /// range_start and range_end. Where a value of 0.0 results in a return /// value of range_start and 1.0 results in a return value of range_end. /// @return Value between range_start and range_end. /// /// @tparam T Type of the range to interpolate over. /// @tparam T2 Type of the value used to perform interpolation /// (e.g float or double). template T Lerp(const T &range_start, const T &range_end, const T2 &percent) { const T2 one_minus_percent = static_cast(1.0) - percent; return range_start * one_minus_percent + range_end * percent; } /// @brief Linearly interpolate between range_start and range_end, based on /// percent. /// @anchor mathfu_Lerp2 /// /// @param range_start Start of the range. /// @param range_end End of the range. /// @param percent Value between 0.0 and 1.0 used to interpolate between /// range_start and range_end. Where a value of 0.0 results in a return /// value of range_start and 1.0 results in a return value of range_end. /// @return Value between range_start and range_end. /// /// @tparam T Type of the range to interpolate over. template T Lerp(const T &range_start, const T &range_end, const T &percent) { return Lerp(range_start, range_end, percent); } /// @brief Check if val is within [range_start..range_end). /// @anchor mathfu_InRange /// /// @param val Value to be tested. /// @param range_start Starting point of the range (inclusive). /// @param range_end Ending point of the range (non-inclusive). /// @return Bool indicating success. /// /// @tparam T Type of values to test. template bool InRange(T val, T range_start, T range_end) { return val >= range_start && val < range_end; } /// @brief Generate a random value of type T. /// @anchor mathfu_Random /// /// This method generates a random value of type T, greater than or equal to /// 0.0 and less than 1.0. /// /// This function uses the standard C library function rand() from math.h to /// generate the random number. /// /// @returns Random number greater than or equal to 0.0 and less than 1.0. /// /// @see RandomRange() /// @see RandomInRange() template inline T Random() { return static_cast(rand()) / static_cast(RAND_MAX); } /// @cond MATHFU_INTERNAL template <> inline float Random() { return static_cast(rand() >> 8) / (static_cast((RAND_MAX >> 8) + 1)); } /// @endcond /// @cond MATHFU_INTERNAL template <> inline double Random() { return static_cast(rand()) / (static_cast(RAND_MAX + 1LL)); } /// @endcond /// @brief Generate a random value of type T in the range -range...+range /// @anchor mathfu_RandomRange /// /// This function uses the standard C library function rand() from math.h to /// generate the random number. /// /// @param range Range of the random value to generate. /// @return Random value in the range -range to +range /// /// @see Random() template inline T RandomRange(T range) { return (Random() * range * 2) - range; } /// @brief Generate a random number between [range_start, range_end] /// @anchor mathfu_RandomInRange /// /// This function uses the standard C library function rand() from math.h to /// generate the random number. /// /// @param range_start Minimum value. /// @param range_end Maximum value. /// @return Random value in the range [range_start, range_end]. /// /// @see Lerp() /// @see Random() template inline T RandomInRange(T range_start, T range_end) { return Lerp(range_start, range_end, Random()); } /// @cond MATHFU_INTERNAL template <> inline int RandomInRange(int range_start, int range_end) { return static_cast(RandomInRange(static_cast(range_start), static_cast(range_end))); } /// @endcond /// @brief Round a value up to the nearest power of 2. /// /// @param x Value to round up. /// @returns Value rounded up to the nearest power of 2. template T RoundUpToPowerOf2(T x) { return static_cast( pow(static_cast(2), ceil(log(x) / log(static_cast(2))))); } /// @brief Specialized version of RoundUpToPowerOf2 for int32_t. template <> inline int32_t RoundUpToPowerOf2<>(int32_t x) { x--; x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; x++; return x; } /// @brief Round a value up to the type's size boundary. /// /// @param v Value to round up. /// @returns Value rounded up to the type's size boundary. template uint32_t RoundUpToTypeBoundary(uint32_t v) { return (v + sizeof(T) - 1) & ~(sizeof(T) - 1); } /// @} /// @addtogroup mathfu_allocator /// /// If you use MathFu with SIMD (SSE in particular), you need to have all /// your allocations be 16-byte aligned (which isn't the case with the default /// allocators on most platforms except OS X). /// /// You can either use simd_allocator, which solves the problem for /// any STL containers, but not for manual dynamic allocations or the /// new/delete override MATHFU_DEFINE_GLOBAL_SIMD_AWARE_NEW_DELETE will /// solve it for all allocations, at the cost of MATHFU_ALIGNMENT bytes per /// allocation. /// @addtogroup mathfu_allocator /// @{ /// @def MATHFU_ALIGNMENT /// @brief Alignment (in bytes) of memory allocated by AllocateAligned. /// /// @see mathfu::AllocateAligned() /// @see mathfu::simd_allocator #define MATHFU_ALIGNMENT 16 /// @brief Allocate an aligned block of memory. /// @anchor mathfu_AllocateAligned /// /// This function allocates a block of memory aligned to MATHFU_ALIGNMENT /// bytes. /// /// @param n Size of memory to allocate. /// @return Pointer to aligned block of allocated memory or NULL if /// allocation failed. inline void *AllocateAligned(size_t n) { #if defined(_MSC_VER) && _MSC_VER >= 1900 // MSVC 2015 return _aligned_malloc(n, MATHFU_ALIGNMENT); #else // We need to allocate extra bytes to guarantee alignment, // and to store the pointer to the original buffer. uint8_t *buf = reinterpret_cast(malloc(n + MATHFU_ALIGNMENT)); if (!buf) return NULL; // Align to next higher multiple of MATHFU_ALIGNMENT. uint8_t *aligned_buf = reinterpret_cast( (reinterpret_cast(buf) + MATHFU_ALIGNMENT) & ~(MATHFU_ALIGNMENT - 1)); // Write out original buffer pointer before aligned buffer. // The assert will fail if the allocator granularity is less than the pointer // size, or if MATHFU_ALIGNMENT doesn't fit two pointers. assert(static_cast(aligned_buf - buf) > sizeof(void *)); *(reinterpret_cast(aligned_buf) - 1) = buf; return aligned_buf; #endif // defined(_MSC_VER) && _MSC_VER >= 1900 // MSVC 2015 } /// @brief Deallocate a block of memory allocated with AllocateAligned(). /// @anchor mathfu_FreeAligned /// /// @param p Pointer to memory to deallocate. inline void FreeAligned(void *p) { #if defined(_MSC_VER) && _MSC_VER >= 1900 // MSVC 2015 _aligned_free(p); #else if (p == NULL) return; free(*(reinterpret_cast(p) - 1)); #endif // defined(_MSC_VER) && _MSC_VER >= 1900 // MSVC 2015 } /// @brief SIMD-safe memory allocator, for use with STL types like std::vector. /// /// For example: ///
/// std::vector> myvector;
/// 
/// /// @see MATHFU_DEFINE_GLOBAL_SIMD_AWARE_NEW_DELETE /// @tparam T type allocated by this object. template class simd_allocator : public std::allocator { public: /// Size type. typedef size_t size_type; /// Pointer of type T. typedef T *pointer; /// Const pointer of type T. typedef const T *const_pointer; /// Constructs a simd_allocator. simd_allocator() throw() : std::allocator() {} /// @brief Constructs and copies a simd_allocator. /// /// @param a Allocator to copy. simd_allocator(const simd_allocator &a) throw() : std::allocator(a) {} /// @brief Constructs and copies a simd_allocator. /// /// @param a Allocator to copy. /// @tparam U type of the object allocated by the allocator to copy. template simd_allocator(const simd_allocator &a) throw() : std::allocator(a) {} /// @brief Destructs a simd_allocator. ~simd_allocator() throw() {} /// @brief Obtains an allocator of a different type. /// /// @tparam _Tp1 type of the new allocator. template struct rebind { /// @brief Allocator of type _Tp1. typedef simd_allocator<_Tp1> other; }; /// @brief Allocate memory for object T. /// /// @param n Number of types to allocate. /// @return Pointer to the newly allocated memory. pointer allocate(size_type n) { return reinterpret_cast(AllocateAligned(n * sizeof(T))); } /// Deallocate memory referenced by pointer p. /// /// @param p Pointer to memory to deallocate. void deallocate(pointer p, size_type) { FreeAligned(p); } }; #if defined(_MSC_VER) #if _MSC_VER <= 1800 // MSVC 2013 #if !defined(noexcept) #define noexcept #endif // !defined(noexcept) #endif // _MSC_VER <= 1800 #endif // defined(_MSC_VER) /// @def MATHFU_DEFINE_GLOBAL_SIMD_AWARE_NEW_DELETE /// @brief Macro which overrides the default new and delete allocators. /// /// To globally override new and delete, simply add the line: ///
/// MATHFU_DEFINE_GLOBAL_SIMD_AWARE_NEW_DELETE
/// 
/// to the end of your main .cpp file. #define MATHFU_DEFINE_GLOBAL_SIMD_AWARE_NEW_DELETE \ void *operator new(std::size_t n) { return mathfu::AllocateAligned(n); } \ void *operator new[](std::size_t n) { return mathfu::AllocateAligned(n); } \ void operator delete(void *p) noexcept { mathfu::FreeAligned(p); } \ void operator delete[](void *p) noexcept { mathfu::FreeAligned(p); } /// @def MATHFU_DEFINE_CLASS_SIMD_AWARE_NEW_DELETE /// @brief Macro which defines the new and delete for MathFu classes. #define MATHFU_DEFINE_CLASS_SIMD_AWARE_NEW_DELETE \ static void *operator new(std::size_t n) { \ return mathfu::AllocateAligned(n); \ } \ static void *operator new[](std::size_t n) { \ return mathfu::AllocateAligned(n); \ } \ static void *operator new(std::size_t /*n*/, void *p) { return p; } \ static void *operator new[](std::size_t /*n*/, void *p) { return p; } \ static void operator delete(void *p) { mathfu::FreeAligned(p); } \ static void operator delete[](void *p) { mathfu::FreeAligned(p); } \ static void operator delete(void * /*p*/, void * /*place*/) {} \ static void operator delete[](void * /*p*/, void * /*place*/) {} /// @} } // namespace mathfu #endif // MATHFU_UTILITIES_H_