Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 09:13:47

0001 #ifndef BVH_V2_UTILS_H
0002 #define BVH_V2_UTILS_H
0003 
0004 #include "bvh/v2/platform.h"
0005 
0006 #include <limits>
0007 #include <climits>
0008 #include <cstdint>
0009 #include <cstring>
0010 #include <cmath>
0011 #include <atomic>
0012 
0013 namespace bvh::v2 {
0014 
0015 /// Helper type that gives an unsigned integer type with the given number of bits.
0016 template <size_t Bits>
0017 struct UnsignedInt {};
0018 
0019 template <> struct UnsignedInt< 8> { using Type = uint8_t ; };
0020 template <> struct UnsignedInt<16> { using Type = uint16_t; };
0021 template <> struct UnsignedInt<32> { using Type = uint32_t; };
0022 template <> struct UnsignedInt<64> { using Type = uint64_t; };
0023 
0024 template <size_t Bits>
0025 using UnsignedIntType = typename UnsignedInt<Bits>::Type;
0026 
0027 /// Helper callable object that just ignores its arguments and returns nothing.
0028 struct IgnoreArgs {
0029     template <typename... Args>
0030     void operator () (Args&&...) const {}
0031 };
0032 
0033 /// Generates a bitmask with the given number of bits.
0034 template <typename T, std::enable_if_t<std::is_unsigned_v<T>, bool> = true>
0035 BVH_ALWAYS_INLINE constexpr T make_bitmask(size_t bits) {
0036     return bits >= std::numeric_limits<T>::digits ? static_cast<T>(-1) : (static_cast<T>(1) << bits) - 1;
0037 }
0038 
0039 // These two functions are designed to return the second argument if the first one is a NaN.
0040 template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
0041 BVH_ALWAYS_INLINE T robust_min(T a, T b) { return a < b ? a : b; }
0042 template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
0043 BVH_ALWAYS_INLINE T robust_max(T a, T b) { return a > b ? a : b; }
0044 
0045 /// Adds the given number of ULPs (Units in the Last Place) to the given floating-point number.
0046 template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
0047 BVH_ALWAYS_INLINE T add_ulp_magnitude(T t, unsigned ulp) {
0048     if (!std::isfinite(t))
0049         return t;
0050     UnsignedIntType<sizeof(T) * CHAR_BIT> u;
0051     std::memcpy(&u, &t, sizeof(T));
0052     u += ulp;
0053     std::memcpy(&t, &u, sizeof(T));
0054     return t;
0055 }
0056 
0057 /// Computes the inverse of the given value, always returning a finite value.
0058 template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
0059 BVH_ALWAYS_INLINE T safe_inverse(T x) {
0060     return std::fabs(x) <= std::numeric_limits<T>::epsilon()
0061         ? std::copysign(std::numeric_limits<T>::max(), x)
0062         : static_cast<T>(1.) / x;
0063 }
0064 
0065 /// Fast multiply-add operation. Should translate into an FMA for architectures that support it. On
0066 /// architectures which do not support FMA in hardware, or on which FMA is slow, this defaults to a
0067 /// regular multiplication followed by an addition.
0068 #if defined(_MSC_VER) && !defined(__clang__)
0069 #pragma float_control(push)
0070 #pragma float_control(precise, off)
0071 #pragma fp_contract(on)
0072 #endif
0073 template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
0074 BVH_ALWAYS_INLINE T fast_mul_add(T a, T b, T c) {
0075 #ifdef FP_FAST_FMAF
0076     return std::fma(a, b, c);
0077 #elif defined(__clang__)
0078     BVH_CLANG_ENABLE_FP_CONTRACT
0079 #endif
0080     return a * b + c;
0081 }
0082 #if defined(_MSC_VER) && !defined(__clang__)
0083 #pragma float_control(pop)
0084 #endif
0085 
0086 /// Executes the given function once for every integer in the range `[Begin, End)`.
0087 template <size_t Begin, size_t End, typename F>
0088 BVH_ALWAYS_INLINE void static_for(F&& f) {
0089     if constexpr (Begin < End) {
0090         f(Begin);
0091         static_for<Begin + 1, End>(std::forward<F>(f));
0092     }
0093 }
0094 
0095 /// Computes the (rounded-up) compile-time log in base-2 of an unsigned integer.
0096 template <typename T, std::enable_if_t<std::is_unsigned_v<T>, bool> = true>
0097 inline constexpr T round_up_log2(T i, T p = 0) {
0098     return (static_cast<T>(1) << p) >= i ? p : round_up_log2(i, p + 1);
0099 }
0100 
0101 /// Split an unsigned integer such that its bits are spaced by 2 zeros.
0102 /// For instance, split_bits(0b00110010) = 0b000000001001000000001000.
0103 template <typename T, std::enable_if_t<std::is_unsigned_v<T>, bool> = true>
0104 BVH_ALWAYS_INLINE T split_bits(T x) {
0105     constexpr size_t bit_count = sizeof(T) * CHAR_BIT;
0106     constexpr size_t log_bits = round_up_log2(bit_count);
0107     auto mask = static_cast<T>(-1) >> (bit_count / 2);
0108     x &= mask;
0109     for (size_t i = log_bits - 1, n = size_t{1} << i; i > 0; --i, n >>= 1) {
0110         mask = (mask | (mask << n)) & ~(mask << (n / 2));
0111         x = (x | (x << n)) & mask;
0112     }
0113     return x;
0114 }
0115 
0116 /// Morton-encode three unsigned integers into one.
0117 template <typename T, std::enable_if_t<std::is_unsigned_v<T>, bool> = true>
0118 BVH_ALWAYS_INLINE T morton_encode(T x, T y, T z) {
0119     return split_bits(x) | (split_bits(y) << 1) | (split_bits(z) << 2);
0120 }
0121 
0122 /// Computes the maximum between an atomic variable and a value, and returns the value previously
0123 /// held by the atomic variable.
0124 template <typename T>
0125 BVH_ALWAYS_INLINE T atomic_max(std::atomic<T>& atomic, const T& value) {
0126     auto prev_value = atomic;
0127     while (prev_value < value && !atomic.compare_exchange_weak(prev_value, value));
0128     return prev_value;
0129 }
0130 
0131 } // namespace bvh::v2
0132 
0133 #endif