/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2019 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** * * @modified Tom Clay, 2026 - Adapted for ReXGlue runtime */ #pragma once #include #include #include #include #include #include #include #include #include #if REX_ARCH_AMD64 #include #endif namespace rex { template constexpr size_t countof(T (&)[N]) { return std::extent::value; } template constexpr bool is_pow2(T value) { return (value & (value - 1)) == 0; } // Rounds up the given value to the given alignment. template constexpr T align(T value, T alignment) { return (value + alignment - 1) & ~(alignment - 1); } // Rounds the given number up to the next highest multiple. template constexpr T round_up(T value, V multiple, bool force_non_zero = true) { if (force_non_zero && !value) { return multiple; } return (value + multiple - 1) / multiple * multiple; } // For NaN, returns min_value (or, if it's NaN too, max_value). // If either of the boundaries is zero, and if the value is at that boundary or // exceeds it, the result will have the sign of that boundary. If both // boundaries are zero, which sign is selected among the argument signs is not // explicitly defined. template T clamp_float(T value, T min_value, T max_value) { float clamped_to_min = std::isgreater(value, min_value) ? value : min_value; return std::isless(clamped_to_min, max_value) ? clamped_to_min : max_value; } // Using the same conventions as in shading languages, returning 0 for NaN. // 0 is always returned as positive. template T saturate(T value) { return clamp_float(value, static_cast(0.0f), static_cast(1.0f)); } // Gets the next power of two value that is greater than or equal to the given // value. template T next_pow2(T value) { value--; value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; value++; return value; } #if __cpp_lib_gcd_lcm template constexpr T greatest_common_divisor(T a, T b) { return std::gcd(a, b); } #else template constexpr T greatest_common_divisor(T a, T b) { // Use the Euclid algorithm to calculate the greatest common divisor while (b) { a = std::exchange(b, a % b); } return a; } #endif template constexpr void reduce_fraction(T& numerator, T& denominator) { auto gcd = greatest_common_divisor(numerator, denominator); numerator /= gcd; denominator /= gcd; } template constexpr void reduce_fraction(std::pair& fraction) { reduce_fraction(fraction.first, fraction.second); } constexpr uint32_t make_bitmask(uint32_t a, uint32_t b) { return (static_cast(-1) >> (31 - b)) & ~((1u << a) - 1); } constexpr uint32_t select_bits(uint32_t value, uint32_t a, uint32_t b) { return (value & make_bitmask(a, b)) >> a; } template constexpr uint32_t bit_count(T v) { return static_cast(std::popcount(v)); } // lzcnt - count leading zeros. // Returns the size of the input operand if value is zero. uint8_t lzcnt(uint8_t v); uint8_t lzcnt(uint16_t v); uint8_t lzcnt(uint32_t v); uint8_t lzcnt(uint64_t v); // tzcnt - count trailing zeros. uint8_t tzcnt(uint8_t v); uint8_t tzcnt(uint16_t v); uint8_t tzcnt(uint32_t v); uint8_t tzcnt(uint64_t v); inline uint8_t lzcnt(int8_t v) { return lzcnt(static_cast(v)); } inline uint8_t lzcnt(int16_t v) { return lzcnt(static_cast(v)); } inline uint8_t lzcnt(int32_t v) { return lzcnt(static_cast(v)); } inline uint8_t lzcnt(int64_t v) { return lzcnt(static_cast(v)); } inline uint8_t tzcnt(int8_t v) { return tzcnt(static_cast(v)); } inline uint8_t tzcnt(int16_t v) { return tzcnt(static_cast(v)); } inline uint8_t tzcnt(int32_t v) { return tzcnt(static_cast(v)); } inline uint8_t tzcnt(int64_t v) { return tzcnt(static_cast(v)); } // BitScanForward (bsf). // Search the value from least significant bit (LSB) to the most significant bit // (MSB) for a set bit (1). // Returns false if no bits are set and the output index is invalid. bool bit_scan_forward(uint32_t v, uint32_t* out_first_set_index); bool bit_scan_forward(uint64_t v, uint32_t* out_first_set_index); inline bool bit_scan_forward(int32_t v, uint32_t* out_first_set_index) { return bit_scan_forward(static_cast(v), out_first_set_index); } inline bool bit_scan_forward(int64_t v, uint32_t* out_first_set_index) { return bit_scan_forward(static_cast(v), out_first_set_index); } template inline T log2_floor(T v) { return sizeof(T) * 8 - 1 - lzcnt(v); } template inline T log2_ceil(T v) { return sizeof(T) * 8 - lzcnt(v - 1); } template inline T rotate_left(T v, uint8_t sh) { return (T(v) << sh) | (T(v) >> ((sizeof(T) * 8) - sh)); } #if REX_ARCH_AMD64 // Utilities for SSE values. template float m128_f32(const __m128& v) { float ret; _mm_store_ss(&ret, _mm_shuffle_ps(v, v, _MM_SHUFFLE(N, N, N, N))); return ret; } template int32_t m128_i32(const __m128& v) { float f; _mm_store_ss(&f, _mm_shuffle_ps(v, v, _MM_SHUFFLE(N, N, N, N))); return std::bit_cast(f); } template double m128_f64(const __m128d& v) { double ret; _mm_store_sd(&ret, _mm_shuffle_pd(v, v, _MM_SHUFFLE2(N, N))); return ret; } template double m128_f64(const __m128& v) { return m128_f64(_mm_castps_pd(v)); } template int64_t m128_i64(const __m128d& v) { double f; _mm_store_sd(&f, _mm_shuffle_pd(v, v, _MM_SHUFFLE2(N, N))); return std::bit_cast(f); } template int64_t m128_i64(const __m128& v) { return m128_i64(_mm_castps_pd(v)); } #endif // Similar to the C++ implementation of XMConvertFloatToHalf and // XMConvertHalfToFloat from DirectXMath 3.00 (pre-3.04, which switched from the // Xenos encoding to IEEE 754), with the extended range instead of infinity and // NaN, and optionally with denormalized numbers - as used in vpkd3d128 (no // denormals, rounding towards zero) and on the Xenos (GL_OES_texture_float // alternative encoding). inline uint16_t float_to_xenos_half(float value, bool preserve_denormal = false, bool round_to_nearest_even = false) { uint32_t integer_value = std::bit_cast(value); uint32_t abs_value = integer_value & 0x7FFFFFFFu; uint32_t result; if (abs_value >= 0x47FFE000u) { // Saturate. result = 0x7FFFu; } else { if (abs_value < 0x38800000u) { // The number is too small to be represented as a normalized half. if (preserve_denormal) { uint32_t shift = std::min(uint32_t(113u - (abs_value >> 23u)), uint32_t(24u)); result = (0x800000u | (abs_value & 0x7FFFFFu)) >> shift; } else { result = 0u; } } else { // Rebias the exponent to represent the value as a normalized half. result = abs_value + 0xC8000000u; } if (round_to_nearest_even) { result += 0xFFFu + ((result >> 13u) & 1u); } result = (result >> 13u) & 0x7FFFu; } return uint16_t(result | ((integer_value & 0x80000000u) >> 16u)); } inline float xenos_half_to_float(uint16_t value, bool preserve_denormal = false) { uint32_t mantissa = value & 0x3FFu; uint32_t exponent = (value >> 10u) & 0x1Fu; if (!exponent) { if (!preserve_denormal) { mantissa = 0; } else if (mantissa) { // Normalize the value in the resulting float. // do { Exponent--; Mantissa <<= 1; } while ((Mantissa & 0x0400) == 0) uint32_t mantissa_lzcnt = rex::lzcnt(mantissa) - (32u - 11u); exponent = uint32_t(1 - int32_t(mantissa_lzcnt)); mantissa = (mantissa << mantissa_lzcnt) & 0x3FFu; } if (!mantissa) { exponent = uint32_t(-112); } } uint32_t result = (uint32_t(value & 0x8000u) << 16u) | ((exponent + 112u) << 23u) | (mantissa << 13u); return std::bit_cast(result); } // https://locklessinc.com/articles/sat_arithmetic/ template inline T sat_add(T a, T b) { using TU = typename std::make_unsigned::type; TU result = TU(a) + TU(b); if (std::is_unsigned::value) { result |= TU(-static_cast::type>(result < TU(a))); } else { TU overflowed = (TU(a) >> (sizeof(T) * 8 - 1)) + std::numeric_limits::max(); if (T((overflowed ^ TU(b)) | ~(TU(b) ^ result)) >= 0) { result = overflowed; } } return T(result); } template inline T sat_sub(T a, T b) { using TU = typename std::make_unsigned::type; TU result = TU(a) - TU(b); if (std::is_unsigned::value) { result &= TU(-static_cast::type>(result <= TU(a))); } else { TU overflowed = (TU(a) >> (sizeof(T) * 8 - 1)) + std::numeric_limits::max(); if (T((overflowed ^ TU(b)) & (overflowed ^ result)) < 0) { result = overflowed; } } return T(result); } } // namespace rex