// // Copyright 2002 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //
// mathutil.h: Math and bit manipulation functions.
template <typename T> inline constexpr bool isPow2(T x)
{
static_assert(std::is_integral<T>::value, "isPow2 must be called on an integer type."); return (x & (x - 1)) == 0 && (x != 0);
}
template <typename T> inlineint log2(T x)
{
static_assert(std::is_integral<T>::value, "log2 must be called on an integer type."); int r = 0; while ((x >> r) > 1)
r++; return r;
}
inlineunsignedint ceilPow2(unsignedint x)
{ if (x != 0)
x--;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
x++;
return x;
}
template <typename DestT, typename SrcT> inline DestT clampCast(SrcT value)
{ // For floating-point types with denormalization, min returns the minimum positive normalized // value. To find the value that has no values less than it, use numeric_limits::lowest.
constexpr constlongdouble destLo = static_cast<longdouble>(std::numeric_limits<DestT>::lowest());
constexpr constlongdouble destHi = static_cast<longdouble>(std::numeric_limits<DestT>::max());
constexpr constlongdouble srcLo = static_cast<longdouble>(std::numeric_limits<SrcT>::lowest());
constexpr longdouble srcHi = static_cast<longdouble>(std::numeric_limits<SrcT>::max());
if (destHi < srcHi)
{
DestT destMax = std::numeric_limits<DestT>::max(); if (value >= static_cast<SrcT>(destMax))
{ return destMax;
}
}
if (destLo > srcLo)
{
DestT destLow = std::numeric_limits<DestT>::lowest(); if (value <= static_cast<SrcT>(destLow))
{ return destLow;
}
}
returnstatic_cast<DestT>(value);
}
// Specialize clampCast for bool->int conversion to avoid MSVS 2015 performance warning when the max // value is casted to the source type. template <> inlineunsignedint clampCast(bool value)
{ returnstatic_cast<unsignedint>(value);
}
template <typename T, typename MIN, typename MAX> inline T clamp(T x, MIN min, MAX max)
{ // Since NaNs fail all comparison tests, a NaN value will default to min return x > min ? (x > max ? max : x) : min;
}
template <typename T>
T clampForBitCount(T value, size_t bitCount)
{
static_assert(std::numeric_limits<T>::is_integer, "T must be an integer.");
if (bitCount == 0)
{
constexpr T kZero = 0; return kZero;
}
ASSERT(bitCount <= sizeof(T) * 8);
if ((float32Val & float32ExponentMask) == float32ExponentMask)
{ // INF or NAN if ((float32Val & float32MantissaMask) != 0)
{ return float11ExponentMask |
(((float32Val >> 17) | (float32Val >> 11) | (float32Val >> 6) | (float32Val)) &
float11MantissaMask);
} elseif (float32Sign)
{ // -INF is clamped to 0 since float11 is positive only return 0;
} else
{ return float11ExponentMask;
}
} elseif (float32Sign)
{ // float11 is positive only, so clamp to zero return 0;
} elseif (float32Val > float32Maxfloat11)
{ // The number is too large to be represented as a float11, set to max return float11Max;
} elseif (float32Val < float32MinDenormfloat11)
{ // The number is too small to be represented as a denormalized float11, set to 0 return 0;
} else
{ if (float32Val < float32MinNormfloat11)
{ // The number is too small to be represented as a normalized float11 // Convert it to a denormalized value. constunsignedint shift = (float32ExponentBias - float11ExponentBias) -
(float32Val >> float32ExponentFirstBit);
ASSERT(shift < 32);
float32Val =
((1 << float32ExponentFirstBit) | (float32Val & float32MantissaMask)) >> shift;
} else
{ // Rebias the exponent to represent the value as a normalized float11
float32Val += 0xC8000000;
}
if ((float32Val & float32ExponentMask) == float32ExponentMask)
{ // INF or NAN if ((float32Val & float32MantissaMask) != 0)
{ return float10ExponentMask |
(((float32Val >> 18) | (float32Val >> 13) | (float32Val >> 3) | (float32Val)) &
float10MantissaMask);
} elseif (float32Sign)
{ // -INF is clamped to 0 since float10 is positive only return 0;
} else
{ return float10ExponentMask;
}
} elseif (float32Sign)
{ // float10 is positive only, so clamp to zero return 0;
} elseif (float32Val > float32Maxfloat10)
{ // The number is too large to be represented as a float10, set to max return float10Max;
} elseif (float32Val < float32MinDenormfloat10)
{ // The number is too small to be represented as a denormalized float10, set to 0 return 0;
} else
{ if (float32Val < float32MinNormfloat10)
{ // The number is too small to be represented as a normalized float10 // Convert it to a denormalized value. constunsignedint shift = (float32ExponentBias - float10ExponentBias) -
(float32Val >> float32ExponentFirstBit);
ASSERT(shift < 32);
float32Val =
((1 << float32ExponentFirstBit) | (float32Val & float32MantissaMask)) >> shift;
} else
{ // Rebias the exponent to represent the value as a normalized float10
float32Val += 0xC8000000;
}
// Converts to and from float and 16.16 fixed point format. inlinefloat ConvertFixedToFloat(int32_t fixedInput)
{ returnstatic_cast<float>(fixedInput) / 65536.0f;
}
template <typename T> inlinefloat normalizedToFloat(T input)
{
static_assert(std::numeric_limits<T>::is_integer, "T must be an integer.");
if (sizeof(T) > 2)
{ // float has only a 23 bit mantissa, so we need to do the calculation in double precision
constexpr double inverseMax = 1.0 / std::numeric_limits<T>::max(); returnstatic_cast<float>(input * inverseMax);
} else
{
constexpr float inverseMax = 1.0f / std::numeric_limits<T>::max(); return input * inverseMax;
}
}
template <unsignedint inputBitCount, typename T> inlinefloat normalizedToFloat(T input)
{
static_assert(std::numeric_limits<T>::is_integer, "T must be an integer.");
static_assert(inputBitCount < (sizeof(T) * 8), "T must have more bits than inputBitCount.");
ASSERT((input & ~((1 << inputBitCount) - 1)) == 0);
if (inputBitCount > 23)
{ // float has only a 23 bit mantissa, so we need to do the calculation in double precision
constexpr double inverseMax = 1.0 / ((1 << inputBitCount) - 1); returnstatic_cast<float>(input * inverseMax);
} else
{
constexpr float inverseMax = 1.0f / ((1 << inputBitCount) - 1); return input * inverseMax;
}
}
template <typename T> inline T floatToNormalized(float input)
{ if constexpr (sizeof(T) > 2)
{ // float has only a 23 bit mantissa, so we need to do the calculation in double precision returnstatic_cast<T>(std::numeric_limits<T>::max() * static_cast<double>(input) + 0.5);
} else
{ returnstatic_cast<T>(std::numeric_limits<T>::max() * input + 0.5f);
}
}
template <unsignedint outputBitCount, typename T> inline T floatToNormalized(float input)
{
static_assert(outputBitCount < (sizeof(T) * 8), "T must have more bits than outputBitCount.");
if (outputBitCount > 23)
{ // float has only a 23 bit mantissa, so we need to do the calculation in double precision returnstatic_cast<T>(((1 << outputBitCount) - 1) * static_cast<double>(input) + 0.5);
} else
{ returnstatic_cast<T>(((1 << outputBitCount) - 1) * input + 0.5f);
}
}
template <unsignedint inputBitCount, unsignedint inputBitStart, typename T> inline T getShiftedData(T input)
{
static_assert(inputBitCount + inputBitStart <= (sizeof(T) * 8), "T must have at least as many bits as inputBitCount + inputBitStart."); const T mask = (1 << inputBitCount) - 1; return (input >> inputBitStart) & mask;
}
template <unsignedint inputBitCount, unsignedint inputBitStart, typename T> inline T shiftData(T input)
{
static_assert(inputBitCount + inputBitStart <= (sizeof(T) * 8), "T must have at least as many bits as inputBitCount + inputBitStart."); const T mask = (1 << inputBitCount) - 1; return (input & mask) << inputBitStart;
}
inlineunsignedint CountLeadingZeros(uint32_t x)
{ // Use binary search to find the amount of leading zeros. unsignedint zeros = 32u;
uint32_t y;
y = x >> 16u; if (y != 0)
{
zeros = zeros - 16u;
x = y;
}
y = x >> 8u; if (y != 0)
{
zeros = zeros - 8u;
x = y;
}
y = x >> 4u; if (y != 0)
{
zeros = zeros - 4u;
x = y;
}
y = x >> 2u; if (y != 0)
{
zeros = zeros - 2u;
x = y;
}
y = x >> 1u; if (y != 0)
{ return zeros - 2u;
} return zeros - x;
}
inlineunsignedchar average(unsignedchar a, unsignedchar b)
{ return ((a ^ b) >> 1) + (a & b);
}
inlinesignedchar average(signedchar a, signedchar b)
{ return ((short)a + (short)b) / 2;
}
inlineunsignedshort average(unsignedshort a, unsignedshort b)
{ return ((a ^ b) >> 1) + (a & b);
}
inlinesignedshort average(signedshort a, signedshort b)
{ return ((int)a + (int)b) / 2;
}
inlineunsignedint average(unsignedint a, unsignedint b)
{ return ((a ^ b) >> 1) + (a & b);
}
inlineint average(int a, int b)
{ longlong average = (static_cast<longlong>(a) + static_cast<longlong>(b)) / 2LL; returnstatic_cast<int>(average);
}
inlinefloat average(float a, float b)
{ return (a + b) * 0.5f;
}
inlineunsignedshort averageHalfFloat(unsignedshort a, unsignedshort b)
{ return float32ToFloat16((float16ToFloat32(a) + float16ToFloat32(b)) * 0.5f);
}
inlineunsignedint averageFloat11(unsignedint a, unsignedint b)
{ return float32ToFloat11((float11ToFloat32(static_cast<unsignedshort>(a)) +
float11ToFloat32(static_cast<unsignedshort>(b))) *
0.5f);
}
inlineunsignedint averageFloat10(unsignedint a, unsignedint b)
{ return float32ToFloat10((float10ToFloat32(static_cast<unsignedshort>(a)) +
float10ToFloat32(static_cast<unsignedshort>(b))) *
0.5f);
}
template <typename T> class Range
{ public:
Range() {}
Range(T lo, T hi) : mLow(lo), mHigh(hi) {}
// Assumes that end is non-inclusive.. for example, extending to 5 will make "end" 6. void extend(T value)
{
mLow = value < mLow ? value : mLow;
mHigh = value >= mHigh ? (value + 1) : mHigh;
}
bool empty() const { return mHigh <= mLow; }
bool contains(T value) const { return value >= mLow && value < mHigh; }
class Iterator final
{ public:
Iterator(T value) : mCurrent(value) {}
// Number of vertices in the range.
size_t vertexCount() const { return (end - start) + 1; }
// Inclusive range of indices that are not primitive restart
size_t start;
size_t end;
// Number of non-primitive restart indices
size_t vertexIndexCount;
};
// Combine a floating-point value representing a mantissa (x) and an integer exponent (exp) into a // floating-point value. As in GLSL ldexp() built-in. inlinefloat Ldexp(float x, int exp)
{ if (exp > 128)
{ return std::numeric_limits<float>::infinity();
} if (exp < -126)
{ return 0.0f;
} double result = static_cast<double>(x) * std::pow(2.0, static_cast<double>(exp)); returnstatic_cast<float>(result);
}
// First, both normalized floating-point values are converted into 16-bit integer values. // Then, the results are packed into the returned 32-bit unsigned integer. // The first float value will be written to the least significant bits of the output; // the last float value will be written to the most significant bits. // The conversion of each value to fixed point is done as follows : // packSnorm2x16 : round(clamp(c, -1, +1) * 32767.0) inline uint32_t packSnorm2x16(float f1, float f2)
{
int16_t leastSignificantBits = static_cast<int16_t>(roundf(clamp(f1, -1.0f, 1.0f) * 32767.0f));
int16_t mostSignificantBits = static_cast<int16_t>(roundf(clamp(f2, -1.0f, 1.0f) * 32767.0f)); returnstatic_cast<uint32_t>(mostSignificantBits) << 16 |
(static_cast<uint32_t>(leastSignificantBits) & 0xFFFF);
}
// First, unpacks a single 32-bit unsigned integer u into a pair of 16-bit unsigned integers. Then, // each component is converted to a normalized floating-point value to generate the returned two // float values. The first float value will be extracted from the least significant bits of the // input; the last float value will be extracted from the most-significant bits. The conversion for // unpacked fixed-point value to floating point is done as follows: unpackSnorm2x16 : clamp(f / // 32767.0, -1, +1) inlinevoid unpackSnorm2x16(uint32_t u, float *f1, float *f2)
{
int16_t leastSignificantBits = static_cast<int16_t>(u & 0xFFFF);
int16_t mostSignificantBits = static_cast<int16_t>(u >> 16);
*f1 = clamp(static_cast<float>(leastSignificantBits) / 32767.0f, -1.0f, 1.0f);
*f2 = clamp(static_cast<float>(mostSignificantBits) / 32767.0f, -1.0f, 1.0f);
}
// First, both normalized floating-point values are converted into 16-bit integer values. // Then, the results are packed into the returned 32-bit unsigned integer. // The first float value will be written to the least significant bits of the output; // the last float value will be written to the most significant bits. // The conversion of each value to fixed point is done as follows: // packUnorm2x16 : round(clamp(c, 0, +1) * 65535.0) inline uint32_t packUnorm2x16(float f1, float f2)
{
uint16_t leastSignificantBits = static_cast<uint16_t>(roundf(clamp(f1, 0.0f, 1.0f) * 65535.0f));
uint16_t mostSignificantBits = static_cast<uint16_t>(roundf(clamp(f2, 0.0f, 1.0f) * 65535.0f)); returnstatic_cast<uint32_t>(mostSignificantBits) << 16 | static_cast<uint32_t>(leastSignificantBits);
}
// First, unpacks a single 32-bit unsigned integer u into a pair of 16-bit unsigned integers. Then, // each component is converted to a normalized floating-point value to generate the returned two // float values. The first float value will be extracted from the least significant bits of the // input; the last float value will be extracted from the most-significant bits. The conversion for // unpacked fixed-point value to floating point is done as follows: unpackUnorm2x16 : f / 65535.0 inlinevoid unpackUnorm2x16(uint32_t u, float *f1, float *f2)
{
uint16_t leastSignificantBits = static_cast<uint16_t>(u & 0xFFFF);
uint16_t mostSignificantBits = static_cast<uint16_t>(u >> 16);
*f1 = static_cast<float>(leastSignificantBits) / 65535.0f;
*f2 = static_cast<float>(mostSignificantBits) / 65535.0f;
}
// Helper functions intended to be used only here. namespace priv
{
// Packs 4 normalized unsigned floating-point values to a single 32-bit unsigned integer. Works // similarly to packUnorm2x16. The floats are clamped to the range 0.0 to 1.0, and written to the // unsigned integer starting from the least significant bits. inline uint32_t PackUnorm4x8(float f1, float f2, float f3, float f4)
{
uint8_t bits[4];
bits[0] = priv::ToPackedUnorm8(f1);
bits[1] = priv::ToPackedUnorm8(f2);
bits[2] = priv::ToPackedUnorm8(f3);
bits[3] = priv::ToPackedUnorm8(f4);
uint32_t result = 0u; for (int i = 0; i < 4; ++i)
{ int shift = i * 8;
result |= (static_cast<uint32_t>(bits[i]) << shift);
} return result;
}
// Unpacks 4 normalized unsigned floating-point values from a single 32-bit unsigned integer into f. // Works similarly to unpackUnorm2x16. The floats are unpacked starting from the least significant // bits. inlinevoid UnpackUnorm4x8(uint32_t u, float *f)
{ for (int i = 0; i < 4; ++i)
{ int shift = i * 8;
uint8_t bits = static_cast<uint8_t>((u >> shift) & 0xFF);
f[i] = static_cast<float>(bits) / 255.0f;
}
}
// Packs 4 normalized signed floating-point values to a single 32-bit unsigned integer. The floats // are clamped to the range -1.0 to 1.0, and written to the unsigned integer starting from the least // significant bits. inline uint32_t PackSnorm4x8(float f1, float f2, float f3, float f4)
{
int8_t bits[4];
bits[0] = priv::ToPackedSnorm8(f1);
bits[1] = priv::ToPackedSnorm8(f2);
bits[2] = priv::ToPackedSnorm8(f3);
bits[3] = priv::ToPackedSnorm8(f4);
uint32_t result = 0u; for (int i = 0; i < 4; ++i)
{ int shift = i * 8;
result |= ((static_cast<uint32_t>(bits[i]) & 0xFF) << shift);
} return result;
}
// Unpacks 4 normalized signed floating-point values from a single 32-bit unsigned integer into f. // Works similarly to unpackSnorm2x16. The floats are unpacked starting from the least significant // bits, and clamped to the range -1.0 to 1.0. inlinevoid UnpackSnorm4x8(uint32_t u, float *f)
{ for (int i = 0; i < 4; ++i)
{ int shift = i * 8;
int8_t bits = static_cast<int8_t>((u >> shift) & 0xFF);
f[i] = clamp(static_cast<float>(bits) / 127.0f, -1.0f, 1.0f);
}
}
// Returns an unsigned integer obtained by converting the two floating-point values to the 16-bit // floating-point representation found in the OpenGL ES Specification, and then packing these // two 16-bit integers into a 32-bit unsigned integer. // f1: The 16 least-significant bits of the result; // f2: The 16 most-significant bits. inline uint32_t packHalf2x16(float f1, float f2)
{
uint16_t leastSignificantBits = static_cast<uint16_t>(float32ToFloat16(f1));
uint16_t mostSignificantBits = static_cast<uint16_t>(float32ToFloat16(f2)); returnstatic_cast<uint32_t>(mostSignificantBits) << 16 | static_cast<uint32_t>(leastSignificantBits);
}
// Returns two floating-point values obtained by unpacking a 32-bit unsigned integer into a pair of // 16-bit values, interpreting those values as 16-bit floating-point numbers according to the OpenGL // ES Specification, and converting them to 32-bit floating-point values. The first float value is // obtained from the 16 least-significant bits of u; the second component is obtained from the 16 // most-significant bits of u. inlinevoid unpackHalf2x16(uint32_t u, float *f1, float *f2)
{
uint16_t leastSignificantBits = static_cast<uint16_t>(u & 0xFFFF);
uint16_t mostSignificantBits = static_cast<uint16_t>(u >> 16);
inline uint8_t sRGBToLinear(uint8_t srgbValue)
{ float value = srgbValue / 255.0f; if (value <= 0.04045f)
{
value = value / 12.92f;
} else
{
value = std::pow((value + 0.055f) / 1.055f, 2.4f);
} returnstatic_cast<uint8_t>(clamp(value * 255.0f + 0.5f, 0.0f, 255.0f));
}
inline uint8_t linearToSRGB(uint8_t linearValue)
{ float value = linearValue / 255.0f; if (value <= 0.0f)
{
value = 0.0f;
} elseif (value < 0.0031308f)
{
value = value * 12.92f;
} elseif (value < 1.0f)
{
value = std::pow(value, 0.41666f) * 1.055f - 0.055f;
} else
{
value = 1.0f;
} returnstatic_cast<uint8_t>(clamp(value * 255.0f + 0.5f, 0.0f, 255.0f));
}
// Reverse the order of the bits. inline uint32_t BitfieldReverse(uint32_t value)
{ // TODO(oetuaho@nvidia.com): Optimize this if needed. There don't seem to be compiler intrinsics // for this, and right now it's not used in performance-critical paths.
uint32_t result = 0u; for (size_t j = 0u; j < 32u; ++j)
{
result |= (((value >> j) & 1u) << (31u - j));
} return result;
}
// Count the 1 bits. #ifdefined(_MSC_VER) && !defined(__clang__) # ifdefined(_M_IX86) || defined(_M_X64) namespace priv
{ // Check POPCNT instruction support and cache the result. // https://docs.microsoft.com/en-us/cpp/intrinsics/popcnt16-popcnt-popcnt64#remarks staticconstbool kHasPopcnt = [] { int info[4];
__cpuid(&info[0], 1); returnstatic_cast<bool>(info[2] & 0x800000);
}();
} // namespace priv
// MSVC's _CountOneBits* intrinsics are not defined for ARM64, moreover they do not use dedicated // NEON instructions.
inlineint BitCount(uint32_t bits)
{ // cast bits to 8x8 datatype and use VCNT on it const uint8x8_t vsum = vcnt_u8(vcreate_u8(static_cast<uint64_t>(bits)));
#ifdefined(ANGLE_PLATFORM_WINDOWS) // Return the index of the least significant bit set. Indexing is such that bit 0 is the least // significant bit. Implemented for different bit widths on different platforms. inlineunsignedlong ScanForward(uint32_t bits)
{
ASSERT(bits != 0u); unsignedlong firstBitIndex = 0ul; unsignedchar ret = _BitScanForward(&firstBitIndex, bits);
ASSERT(ret != 0u); return firstBitIndex;
}
// Return the index of the most significant bit set. Indexing is such that bit 0 is the least // significant bit. inlineunsignedlong ScanReverse(uint32_t bits)
{
ASSERT(bits != 0u); unsignedlong lastBitIndex = 0ul; unsignedchar ret = _BitScanReverse(&lastBitIndex, bits);
ASSERT(ret != 0u); return lastBitIndex;
}
// Returns -1 on 0, otherwise the index of the least significant 1 bit as in GLSL. template <typename T> int FindLSB(T bits)
{
static_assert(std::is_integral<T>::value, "must be integral type."); if (bits == 0u)
{ return -1;
} else
{ returnstatic_cast<int>(ScanForward(bits));
}
}
// Returns -1 on 0, otherwise the index of the most significant 1 bit as in GLSL. template <typename T> int FindMSB(T bits)
{
static_assert(std::is_integral<T>::value, "must be integral type."); if (bits == 0u)
{ return -1;
} else
{ returnstatic_cast<int>(ScanReverse(bits));
}
}
// Sum, difference and multiplication operations for signed ints that wrap on 32-bit overflow. // // Unsigned types are defined to do arithmetic modulo 2^n in C++. For signed types, overflow // behavior is undefined.
inline int32_t WrappingMul(int32_t lhs, int32_t rhs)
{
int64_t lhsWide = static_cast<int64_t>(lhs);
int64_t rhsWide = static_cast<int64_t>(rhs); // The multiplication is guaranteed not to overflow.
int64_t resultWide = lhsWide * rhsWide; // Implement the desired wrapping behavior by masking out the high-order 32 bits.
resultWide = resultWide & 0xffffffffLL; // Casting to a narrower signed type is fine since the casted value is representable in the // narrower type. returnstatic_cast<int32_t>(resultWide);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.