diff options
Diffstat (limited to 'src/3rdparty/angle/src/common')
46 files changed, 6653 insertions, 571 deletions
diff --git a/src/3rdparty/angle/src/common/BitSetIterator.h b/src/3rdparty/angle/src/common/BitSetIterator.h deleted file mode 100644 index 3248ce44c9..0000000000 --- a/src/3rdparty/angle/src/common/BitSetIterator.h +++ /dev/null @@ -1,156 +0,0 @@ -// -// Copyright 2015 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. -// -// BitSetIterator: -// A helper class to quickly bitscan bitsets for set bits. -// - -#ifndef COMMON_BITSETITERATOR_H_ -#define COMMON_BITSETITERATOR_H_ - -#include <stdint.h> - -#include <bitset> - -#include "common/angleutils.h" -#include "common/debug.h" -#include "common/mathutil.h" -#include "common/platform.h" - -namespace angle -{ -template <size_t N> -class BitSetIterator final -{ - public: - BitSetIterator(const std::bitset<N> &bitset); - BitSetIterator(const BitSetIterator &other); - BitSetIterator &operator=(const BitSetIterator &other); - - class Iterator final - { - public: - Iterator(const std::bitset<N> &bits); - Iterator &operator++(); - - bool operator==(const Iterator &other) const; - bool operator!=(const Iterator &other) const; - unsigned long operator*() const { return mCurrentBit; } - - private: - unsigned long getNextBit(); - - static const size_t BitsPerWord = sizeof(unsigned long) * 8; - std::bitset<N> mBits; - unsigned long mCurrentBit; - unsigned long mOffset; - }; - - Iterator begin() const { return Iterator(mBits); } - Iterator end() const { return Iterator(std::bitset<N>(0)); } - - private: - const std::bitset<N> mBits; -}; - -template <size_t N> -BitSetIterator<N>::BitSetIterator(const std::bitset<N> &bitset) - : mBits(bitset) -{ -} - -template <size_t N> -BitSetIterator<N>::BitSetIterator(const BitSetIterator &other) - : mBits(other.mBits) -{ -} - -template <size_t N> -BitSetIterator<N> &BitSetIterator<N>::operator=(const BitSetIterator &other) -{ - mBits = other.mBits; - return *this; -} - -template <size_t N> -BitSetIterator<N>::Iterator::Iterator(const std::bitset<N> &bits) - : mBits(bits), mCurrentBit(0), mOffset(0) -{ - if (bits.any()) - { - mCurrentBit = getNextBit(); - } - else - { - mOffset = static_cast<unsigned long>(rx::roundUp(N, BitsPerWord)); - } -} - -template <size_t N> -typename BitSetIterator<N>::Iterator &BitSetIterator<N>::Iterator::operator++() -{ - ASSERT(mBits.any()); - mBits.set(mCurrentBit - mOffset, 0); - mCurrentBit = getNextBit(); - return *this; -} - -inline unsigned long ScanForward(unsigned long bits) -{ - ASSERT(bits != 0); -#if defined(ANGLE_PLATFORM_WINDOWS) - unsigned long firstBitIndex = 0ul; - unsigned char ret = _BitScanForward(&firstBitIndex, bits); - ASSERT(ret != 0); - UNUSED_ASSERTION_VARIABLE(ret); - return firstBitIndex; -#elif defined(ANGLE_PLATFORM_POSIX) - return static_cast<unsigned long>(__builtin_ctzl(bits)); -#else -#error Please implement bit-scan-forward for your platform! -#endif -} - -template <size_t N> -bool BitSetIterator<N>::Iterator::operator==(const Iterator &other) const -{ - return mOffset == other.mOffset && mBits == other.mBits; -} - -template <size_t N> -bool BitSetIterator<N>::Iterator::operator!=(const Iterator &other) const -{ - return !(*this == other); -} - -template <size_t N> -unsigned long BitSetIterator<N>::Iterator::getNextBit() -{ - static std::bitset<N> wordMask(std::numeric_limits<unsigned long>::max()); - - while (mOffset < N) - { - unsigned long wordBits = (mBits & wordMask).to_ulong(); - if (wordBits != 0ul) - { - return ScanForward(wordBits) + mOffset; - } - - mBits >>= BitsPerWord; - mOffset += BitsPerWord; - } - return 0; -} - -// Helper to avoid needing to specify the template parameter size -template <size_t N> -BitSetIterator<N> IterateBitSet(const std::bitset<N> &bitset) -{ - return BitSetIterator<N>(bitset); -} - -} // angle - -#endif // COMMON_BITSETITERATOR_H_ diff --git a/src/3rdparty/angle/src/common/Color.h b/src/3rdparty/angle/src/common/Color.h new file mode 100644 index 0000000000..2b4d2f6fba --- /dev/null +++ b/src/3rdparty/angle/src/common/Color.h @@ -0,0 +1,53 @@ +// +// Copyright (c) 2016 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. +// + +// Color.h : Defines the Color type used throughout the ANGLE libraries + +#ifndef COMMON_COLOR_H_ +#define COMMON_COLOR_H_ + +namespace angle +{ + +template <typename T> +struct Color +{ + T red; + T green; + T blue; + T alpha; + + Color(); + Color(T r, T g, T b, T a); +}; + +template <typename T> +bool operator==(const Color<T> &a, const Color<T> &b); + +template <typename T> +bool operator!=(const Color<T> &a, const Color<T> &b); + +typedef Color<float> ColorF; +typedef Color<int> ColorI; +typedef Color<unsigned int> ColorUI; + +} // namespace angle + +// TODO: Move this fully into the angle namespace +namespace gl +{ + +template <typename T> +using Color = angle::Color<T>; +using ColorF = angle::ColorF; +using ColorI = angle::ColorI; +using ColorUI = angle::ColorUI; + +} // namespace gl + +#include "Color.inl" + +#endif // COMMON_COLOR_H_ diff --git a/src/3rdparty/angle/src/common/Color.inl b/src/3rdparty/angle/src/common/Color.inl new file mode 100644 index 0000000000..c3073256b5 --- /dev/null +++ b/src/3rdparty/angle/src/common/Color.inl @@ -0,0 +1,37 @@ +// +// Copyright (c) 2016 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. +// + +// Color.inl : Inline definitions of some functions from Color.h + +namespace angle +{ + +template <typename T> +Color<T>::Color() : Color(0, 0, 0, 0) +{ +} + +template <typename T> +Color<T>::Color(T r, T g, T b, T a) : red(r), green(g), blue(b), alpha(a) +{ +} + +template <typename T> +bool operator==(const Color<T> &a, const Color<T> &b) +{ + return a.red == b.red && + a.green == b.green && + a.blue == b.blue && + a.alpha == b.alpha; +} + +template <typename T> +bool operator!=(const Color<T> &a, const Color<T> &b) +{ + return !(a == b); +} + +} // namespace angle diff --git a/src/3rdparty/angle/src/common/MemoryBuffer.cpp b/src/3rdparty/angle/src/common/MemoryBuffer.cpp index e7a3fb4a2b..6f5188c69c 100644 --- a/src/3rdparty/angle/src/common/MemoryBuffer.cpp +++ b/src/3rdparty/angle/src/common/MemoryBuffer.cpp @@ -11,19 +11,18 @@ #include "common/debug.h" -namespace rx +namespace angle { -MemoryBuffer::MemoryBuffer() - : mSize(0), - mData(NULL) +// MemoryBuffer implementation. +MemoryBuffer::MemoryBuffer() : mSize(0), mData(nullptr) { } MemoryBuffer::~MemoryBuffer() { free(mData); - mData = NULL; + mData = nullptr; } bool MemoryBuffer::resize(size_t size) @@ -31,7 +30,7 @@ bool MemoryBuffer::resize(size_t size) if (size == 0) { free(mData); - mData = NULL; + mData = nullptr; mSize = 0; return true; } @@ -42,8 +41,8 @@ bool MemoryBuffer::resize(size_t size) } // Only reallocate if the size has changed. - uint8_t *newMemory = reinterpret_cast<uint8_t*>(malloc(sizeof(uint8_t) * size)); - if (newMemory == NULL) + uint8_t *newMemory = reinterpret_cast<uint8_t *>(malloc(sizeof(uint8_t) * size)); + if (newMemory == nullptr) { return false; } @@ -61,20 +60,96 @@ bool MemoryBuffer::resize(size_t size) return true; } -size_t MemoryBuffer::size() const +void MemoryBuffer::fill(uint8_t datum) { - return mSize; + if (!empty()) + { + std::fill(mData, mData + mSize, datum); + } +} + +MemoryBuffer::MemoryBuffer(MemoryBuffer &&other) : MemoryBuffer() +{ + *this = std::move(other); } -const uint8_t *MemoryBuffer::data() const +MemoryBuffer &MemoryBuffer::operator=(MemoryBuffer &&other) { - return mData; + std::swap(mSize, other.mSize); + std::swap(mData, other.mData); + return *this; } -uint8_t *MemoryBuffer::data() +// ScratchBuffer implementation. + +ScratchBuffer::ScratchBuffer(uint32_t lifetime) : mLifetime(lifetime), mResetCounter(lifetime) { - ASSERT(mData); - return mData; } +ScratchBuffer::~ScratchBuffer() +{ +} + +bool ScratchBuffer::get(size_t requestedSize, MemoryBuffer **memoryBufferOut) +{ + return getImpl(requestedSize, memoryBufferOut, Optional<uint8_t>::Invalid()); } + +bool ScratchBuffer::getInitialized(size_t requestedSize, + MemoryBuffer **memoryBufferOut, + uint8_t initValue) +{ + return getImpl(requestedSize, memoryBufferOut, Optional<uint8_t>(initValue)); +} + +bool ScratchBuffer::getImpl(size_t requestedSize, + MemoryBuffer **memoryBufferOut, + Optional<uint8_t> initValue) +{ + if (mScratchMemory.size() == requestedSize) + { + mResetCounter = mLifetime; + *memoryBufferOut = &mScratchMemory; + return true; + } + + if (mScratchMemory.size() > requestedSize) + { + tick(); + } + + if (mResetCounter == 0 || mScratchMemory.size() < requestedSize) + { + mScratchMemory.resize(0); + if (!mScratchMemory.resize(requestedSize)) + { + return false; + } + mResetCounter = mLifetime; + if (initValue.valid()) + { + mScratchMemory.fill(initValue.value()); + } + } + + ASSERT(mScratchMemory.size() >= requestedSize); + + *memoryBufferOut = &mScratchMemory; + return true; +} + +void ScratchBuffer::tick() +{ + if (mResetCounter > 0) + { + --mResetCounter; + } +} + +void ScratchBuffer::clear() +{ + mResetCounter = mLifetime; + mScratchMemory.resize(0); +} + +} // namespace angle diff --git a/src/3rdparty/angle/src/common/MemoryBuffer.h b/src/3rdparty/angle/src/common/MemoryBuffer.h index ec621cbca7..f76b9ee62e 100644 --- a/src/3rdparty/angle/src/common/MemoryBuffer.h +++ b/src/3rdparty/angle/src/common/MemoryBuffer.h @@ -7,32 +7,71 @@ #ifndef COMMON_MEMORYBUFFER_H_ #define COMMON_MEMORYBUFFER_H_ +#include "common/Optional.h" #include "common/angleutils.h" +#include "common/debug.h" -#include <cstddef> #include <stdint.h> +#include <cstddef> -namespace rx +namespace angle { -class MemoryBuffer : angle::NonCopyable +class MemoryBuffer final : NonCopyable { public: MemoryBuffer(); ~MemoryBuffer(); + MemoryBuffer(MemoryBuffer &&other); + MemoryBuffer &operator=(MemoryBuffer &&other); + bool resize(size_t size); - size_t size() const; + size_t size() const { return mSize; } bool empty() const { return mSize == 0; } - const uint8_t *data() const; - uint8_t *data(); + const uint8_t *data() const { return mData; } + uint8_t *data() + { + ASSERT(mData); + return mData; + } + + void fill(uint8_t datum); private: size_t mSize; uint8_t *mData; }; -} +class ScratchBuffer final : NonCopyable +{ + public: + // If we request a scratch buffer requesting a smaller size this many times, release and + // recreate the scratch buffer. This ensures we don't have a degenerate case where we are stuck + // hogging memory. + ScratchBuffer(uint32_t lifetime); + ~ScratchBuffer(); + + // Returns true with a memory buffer of the requested size, or false on failure. + bool get(size_t requestedSize, MemoryBuffer **memoryBufferOut); + + // Same as get, but ensures new values are initialized to a fixed constant. + bool getInitialized(size_t requestedSize, MemoryBuffer **memoryBufferOut, uint8_t initValue); + + // Ticks the release counter for the scratch buffer. Also done implicitly in get(). + void tick(); + + void clear(); + + private: + bool getImpl(size_t requestedSize, MemoryBuffer **memoryBufferOut, Optional<uint8_t> initValue); + + const uint32_t mLifetime; + uint32_t mResetCounter; + MemoryBuffer mScratchMemory; +}; + +} // namespace angle -#endif // COMMON_MEMORYBUFFER_H_ +#endif // COMMON_MEMORYBUFFER_H_ diff --git a/src/3rdparty/angle/src/common/Optional.h b/src/3rdparty/angle/src/common/Optional.h index 256f38f329..822de4de51 100644 --- a/src/3rdparty/angle/src/common/Optional.h +++ b/src/3rdparty/angle/src/common/Optional.h @@ -10,23 +10,16 @@ #ifndef COMMON_OPTIONAL_H_ #define COMMON_OPTIONAL_H_ +#include <utility> + template <class T> struct Optional { - Optional() - : mValid(false), - mValue(T()) - {} + Optional() : mValid(false), mValue(T()) {} - explicit Optional(const T &valueIn) - : mValid(true), - mValue(valueIn) - {} + Optional(const T &valueIn) : mValid(true), mValue(valueIn) {} - Optional(const Optional &other) - : mValid(other.mValid), - mValue(other.mValue) - {} + Optional(const Optional &other) : mValid(other.mValid), mValue(other.mValue) {} Optional &operator=(const Optional &other) { @@ -49,10 +42,7 @@ struct Optional return *this; } - void reset() - { - mValid = false; - } + void reset() { mValid = false; } static Optional Invalid() { return Optional(); } @@ -64,14 +54,15 @@ struct Optional return ((mValid == other.mValid) && (!mValid || (mValue == other.mValue))); } - bool operator!=(const Optional &other) const - { - return !(*this == other); - } + bool operator!=(const Optional &other) const { return !(*this == other); } + + bool operator==(const T &value) const { return mValid && (mValue == value); } + + bool operator!=(const T &value) const { return !(*this == value); } private: bool mValid; T mValue; }; -#endif // COMMON_OPTIONAL_H_ +#endif // COMMON_OPTIONAL_H_ diff --git a/src/3rdparty/angle/src/common/angleutils.cpp b/src/3rdparty/angle/src/common/angleutils.cpp index 7099c21730..739d12a767 100644 --- a/src/3rdparty/angle/src/common/angleutils.cpp +++ b/src/3rdparty/angle/src/common/angleutils.cpp @@ -14,39 +14,61 @@ namespace angle { +// dirtyPointer is a special value that will make the comparison with any valid pointer fail and +// force the renderer to re-apply the state. const uintptr_t DirtyPointer = std::numeric_limits<uintptr_t>::max(); } +std::string ArrayString(unsigned int i) +{ + // We assume that UINT_MAX and GL_INVALID_INDEX are equal. + ASSERT(i != UINT_MAX); + + std::stringstream strstr; + strstr << "["; + strstr << i; + strstr << "]"; + return strstr.str(); +} + +std::string ArrayIndexString(const std::vector<unsigned int> &indices) +{ + std::stringstream strstr; + + for (auto indicesIt = indices.rbegin(); indicesIt != indices.rend(); ++indicesIt) + { + // We assume that UINT_MAX and GL_INVALID_INDEX are equal. + ASSERT(*indicesIt != UINT_MAX); + strstr << "["; + strstr << (*indicesIt); + strstr << "]"; + } + + return strstr.str(); +} + size_t FormatStringIntoVector(const char *fmt, va_list vararg, std::vector<char>& outBuffer) { + // The state of the va_list passed to vsnprintf is undefined after the call, do a copy in case + // we need to grow the buffer. + va_list varargCopy; + va_copy(varargCopy, vararg); + // Attempt to just print to the current buffer - int len = vsnprintf(&(outBuffer.front()), outBuffer.size(), fmt, vararg); + int len = vsnprintf(&(outBuffer.front()), outBuffer.size(), fmt, varargCopy); + va_end(varargCopy); + if (len < 0 || static_cast<size_t>(len) >= outBuffer.size()) { // Buffer was not large enough, calculate the required size and resize the buffer - len = vsnprintf(NULL, 0, fmt, vararg); + len = vsnprintf(nullptr, 0, fmt, vararg); outBuffer.resize(len + 1); // Print again - len = vsnprintf(&(outBuffer.front()), outBuffer.size(), fmt, vararg); + va_copy(varargCopy, vararg); + len = vsnprintf(&(outBuffer.front()), outBuffer.size(), fmt, varargCopy); + va_end(varargCopy); } ASSERT(len >= 0); return static_cast<size_t>(len); } - -std::string FormatString(const char *fmt, va_list vararg) -{ - static std::vector<char> buffer(512); - - size_t len = FormatStringIntoVector(fmt, vararg, buffer); - return std::string(&buffer[0], len); -} - -std::string FormatString(const char *fmt, ...) -{ - va_list vararg; - va_start(vararg, fmt); - std::string result = FormatString(fmt, vararg); - va_end(vararg); - return result; -} diff --git a/src/3rdparty/angle/src/common/angleutils.h b/src/3rdparty/angle/src/common/angleutils.h index a0178fd414..ad32a2f03a 100644 --- a/src/3rdparty/angle/src/common/angleutils.h +++ b/src/3rdparty/angle/src/common/angleutils.h @@ -23,25 +23,59 @@ namespace angle { +#if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11) +using Microsoft::WRL::ComPtr; +#endif // defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11) + class NonCopyable { - public: + protected: NonCopyable() = default; ~NonCopyable() = default; - protected: + + private: NonCopyable(const NonCopyable&) = delete; void operator=(const NonCopyable&) = delete; }; extern const uintptr_t DirtyPointer; -} + +} // namespace angle template <typename T, size_t N> -inline size_t ArraySize(T(&)[N]) +constexpr inline size_t ArraySize(T (&)[N]) { return N; } +template <typename T> +class WrappedArray final : angle::NonCopyable +{ + public: + template <size_t N> + constexpr WrappedArray(const T (&data)[N]) : mArray(&data[0]), mSize(N) + { + } + + constexpr WrappedArray() : mArray(nullptr), mSize(0) {} + constexpr WrappedArray(const T *data, size_t size) : mArray(data), mSize(size) {} + + WrappedArray(WrappedArray &&other) : WrappedArray() + { + std::swap(mArray, other.mArray); + std::swap(mSize, other.mSize); + } + + ~WrappedArray() {} + + constexpr const T *get() const { return mArray; } + constexpr size_t size() const { return mSize; } + + private: + const T *mArray; + size_t mSize; +}; + template <typename T, unsigned int N> void SafeRelease(T (&resourceBlock)[N]) { @@ -57,15 +91,15 @@ void SafeRelease(T& resource) if (resource) { resource->Release(); - resource = NULL; + resource = nullptr; } } template <typename T> -void SafeDelete(T*& resource) +void SafeDelete(T *&resource) { delete resource; - resource = NULL; + resource = nullptr; } template <typename T> @@ -82,7 +116,7 @@ template <typename T> void SafeDeleteArray(T*& resource) { delete[] resource; - resource = NULL; + resource = nullptr; } // Provide a less-than function for comparing structs @@ -126,23 +160,11 @@ inline const char* MakeStaticString(const std::string &str) return strings.insert(str).first->c_str(); } -inline std::string ArrayString(unsigned int i) -{ - // We assume UINT_MAX and GL_INVALID_INDEX are equal - // See DynamicHLSL.cpp - if (i == UINT_MAX) - { - return ""; - } +std::string ArrayString(unsigned int i); - std::stringstream strstr; - - strstr << "["; - strstr << i; - strstr << "]"; - - return strstr.str(); -} +// Indices are stored in vectors with the outermost index in the back. In the output of the function +// the indices are reversed. +std::string ArrayIndexString(const std::vector<unsigned int> &indices); inline std::string Str(int i) { @@ -156,17 +178,76 @@ size_t FormatStringIntoVector(const char *fmt, va_list vararg, std::vector<char> std::string FormatString(const char *fmt, va_list vararg); std::string FormatString(const char *fmt, ...); +template <typename T> +std::string ToString(const T &value) +{ + std::ostringstream o; + o << value; + return o.str(); +} + // snprintf is not defined with MSVC prior to to msvc14 #if defined(_MSC_VER) && _MSC_VER < 1900 #define snprintf _snprintf #endif +#define GL_BGRX8_ANGLEX 0x6ABA +#define GL_BGR565_ANGLEX 0x6ABB #define GL_BGRA4_ANGLEX 0x6ABC #define GL_BGR5_A1_ANGLEX 0x6ABD #define GL_INT_64_ANGLEX 0x6ABE -#define GL_STRUCT_ANGLEX 0x6ABF +#define GL_UINT_64_ANGLEX 0x6ABF +#define GL_BGRA8_SRGB_ANGLEX 0x6AC0 // Hidden enum for the NULL D3D device type. #define EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE 0x6AC0 +// TODO(jmadill): Clean this up at some point. +#define EGL_PLATFORM_ANGLE_PLATFORM_METHODS_ANGLEX 0x9999 + +#define ANGLE_TRY_CHECKED_MATH(result) \ + if (!result.IsValid()) \ + { \ + return gl::InternalError() << "Integer overflow."; \ + } + +// The below inlining code lifted from V8. +#if defined(__clang__) || (defined(__GNUC__) && defined(__has_attribute)) +#define ANGLE_HAS_ATTRIBUTE_ALWAYS_INLINE (__has_attribute(always_inline)) +#define ANGLE_HAS___FORCEINLINE 0 +#elif defined(_MSC_VER) +#define ANGLE_HAS_ATTRIBUTE_ALWAYS_INLINE 0 +#define ANGLE_HAS___FORCEINLINE 1 +#else +#define ANGLE_HAS_ATTRIBUTE_ALWAYS_INLINE 0 +#define ANGLE_HAS___FORCEINLINE 0 +#endif + +#if defined(NDEBUG) && ANGLE_HAS_ATTRIBUTE_ALWAYS_INLINE +#define ANGLE_INLINE inline __attribute__((always_inline)) +#elif defined(NDEBUG) && ANGLE_HAS___FORCEINLINE +#define ANGLE_INLINE __forceinline +#else +#define ANGLE_INLINE inline +#endif + +#ifndef ANGLE_STRINGIFY +#define ANGLE_STRINGIFY(x) #x +#endif + +#ifndef ANGLE_MACRO_STRINGIFY +#define ANGLE_MACRO_STRINGIFY(x) ANGLE_STRINGIFY(x) +#endif + +// Detect support for C++17 [[nodiscard]] +#if !defined(__has_cpp_attribute) +#define __has_cpp_attribute(name) 0 +#endif // !defined(__has_cpp_attribute) + +#if __has_cpp_attribute(nodiscard) +#define ANGLE_NO_DISCARD [[nodiscard]] +#else +#define ANGLE_NO_DISCARD +#endif // __has_cpp_attribute(nodiscard) + #endif // COMMON_ANGLEUTILS_H_ diff --git a/src/3rdparty/angle/src/common/bitset_utils.h b/src/3rdparty/angle/src/common/bitset_utils.h new file mode 100644 index 0000000000..4166a562a7 --- /dev/null +++ b/src/3rdparty/angle/src/common/bitset_utils.h @@ -0,0 +1,498 @@ +// +// Copyright 2015 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. +// +// bitset_utils: +// Bitset-related helper classes, such as a fast iterator to scan for set bits. +// + +#ifndef COMMON_BITSETITERATOR_H_ +#define COMMON_BITSETITERATOR_H_ + +#include <stdint.h> + +#include <bitset> + +#include "common/angleutils.h" +#include "common/debug.h" +#include "common/mathutil.h" +#include "common/platform.h" + +namespace angle +{ + +template <size_t N, typename BitsT> +class BitSetT final +{ + public: + class Reference final + { + public: + ~Reference() {} + Reference &operator=(bool x) + { + mParent->set(mBit, x); + return *this; + } + explicit operator bool() const { return mParent->test(mBit); } + + private: + friend class BitSetT; + + Reference(BitSetT *parent, std::size_t bit) : mParent(parent), mBit(bit) {} + + BitSetT *mParent; + std::size_t mBit; + }; + + class Iterator final + { + public: + Iterator(const BitSetT &bits); + Iterator &operator++(); + + bool operator==(const Iterator &other) const; + bool operator!=(const Iterator &other) const; + std::size_t operator*() const; + + private: + std::size_t getNextBit(); + + BitSetT mBitsCopy; + std::size_t mCurrentBit; + }; + + BitSetT(); + BitSetT(BitsT value); + ~BitSetT(); + + BitSetT(const BitSetT &other); + BitSetT &operator=(const BitSetT &other); + + bool operator==(const BitSetT &other) const; + bool operator!=(const BitSetT &other) const; + + constexpr bool operator[](std::size_t pos) const; + Reference operator[](std::size_t pos) { return Reference(this, pos); } + + bool test(std::size_t pos) const; + + bool all() const; + bool any() const; + bool none() const; + std::size_t count() const; + + constexpr std::size_t size() const { return N; } + + BitSetT &operator&=(const BitSetT &other); + BitSetT &operator|=(const BitSetT &other); + BitSetT &operator^=(const BitSetT &other); + BitSetT operator~() const; + + BitSetT operator<<(std::size_t pos) const; + BitSetT &operator<<=(std::size_t pos); + BitSetT operator>>(std::size_t pos) const; + BitSetT &operator>>=(std::size_t pos); + + BitSetT &set(); + BitSetT &set(std::size_t pos, bool value = true); + + BitSetT &reset(); + BitSetT &reset(std::size_t pos); + + BitSetT &flip(); + BitSetT &flip(std::size_t pos); + + unsigned long to_ulong() const { return static_cast<unsigned long>(mBits); } + BitsT bits() const { return mBits; } + + Iterator begin() const { return Iterator(*this); } + Iterator end() const { return Iterator(BitSetT()); } + + private: + constexpr static BitsT Bit(std::size_t x) { return (static_cast<BitsT>(1) << x); } + constexpr static BitsT Mask(std::size_t x) { return ((Bit(x - 1) - 1) << 1) + 1; } + + BitsT mBits; +}; + +template <size_t N> +class IterableBitSet : public std::bitset<N> +{ + public: + IterableBitSet() {} + IterableBitSet(const std::bitset<N> &implicitBitSet) : std::bitset<N>(implicitBitSet) {} + + class Iterator final + { + public: + Iterator(const std::bitset<N> &bits); + Iterator &operator++(); + + bool operator==(const Iterator &other) const; + bool operator!=(const Iterator &other) const; + unsigned long operator*() const { return mCurrentBit; } + + private: + unsigned long getNextBit(); + + static constexpr size_t BitsPerWord = sizeof(uint32_t) * 8; + std::bitset<N> mBits; + unsigned long mCurrentBit; + unsigned long mOffset; + }; + + Iterator begin() const { return Iterator(*this); } + Iterator end() const { return Iterator(std::bitset<N>(0)); } +}; + +template <size_t N> +IterableBitSet<N>::Iterator::Iterator(const std::bitset<N> &bitset) + : mBits(bitset), mCurrentBit(0), mOffset(0) +{ + if (mBits.any()) + { + mCurrentBit = getNextBit(); + } + else + { + mOffset = static_cast<unsigned long>(rx::roundUp(N, BitsPerWord)); + } +} + +template <size_t N> +typename IterableBitSet<N>::Iterator &IterableBitSet<N>::Iterator::operator++() +{ + ASSERT(mBits.any()); + mBits.set(mCurrentBit - mOffset, 0); + mCurrentBit = getNextBit(); + return *this; +} + +template <size_t N> +bool IterableBitSet<N>::Iterator::operator==(const Iterator &other) const +{ + return mOffset == other.mOffset && mBits == other.mBits; +} + +template <size_t N> +bool IterableBitSet<N>::Iterator::operator!=(const Iterator &other) const +{ + return !(*this == other); +} + +template <size_t N> +unsigned long IterableBitSet<N>::Iterator::getNextBit() +{ + // TODO(jmadill): Use 64-bit scan when possible. + static constexpr std::bitset<N> wordMask(std::numeric_limits<uint32_t>::max()); + + while (mOffset < N) + { + uint32_t wordBits = static_cast<uint32_t>((mBits & wordMask).to_ulong()); + if (wordBits != 0) + { + return gl::ScanForward(wordBits) + mOffset; + } + + mBits >>= BitsPerWord; + mOffset += BitsPerWord; + } + return 0; +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT>::BitSetT() : mBits(0) +{ + static_assert(N > 0, "Bitset type cannot support zero bits."); + static_assert(N <= sizeof(BitsT) * 8, "Bitset type cannot support a size this large."); +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT>::BitSetT(BitsT value) : mBits(value & Mask(N)) +{ +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT>::~BitSetT() +{ +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT>::BitSetT(const BitSetT &other) : mBits(other.mBits) +{ +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> &BitSetT<N, BitsT>::operator=(const BitSetT &other) +{ + mBits = other.mBits; + return *this; +} + +template <size_t N, typename BitsT> +bool BitSetT<N, BitsT>::operator==(const BitSetT &other) const +{ + return mBits == other.mBits; +} + +template <size_t N, typename BitsT> +bool BitSetT<N, BitsT>::operator!=(const BitSetT &other) const +{ + return mBits != other.mBits; +} + +template <size_t N, typename BitsT> +constexpr bool BitSetT<N, BitsT>::operator[](std::size_t pos) const +{ + return test(pos); +} + +template <size_t N, typename BitsT> +bool BitSetT<N, BitsT>::test(std::size_t pos) const +{ + return (mBits & Bit(pos)) != 0; +} + +template <size_t N, typename BitsT> +bool BitSetT<N, BitsT>::all() const +{ + ASSERT(mBits == (mBits & Mask(N))); + return mBits == Mask(N); +} + +template <size_t N, typename BitsT> +bool BitSetT<N, BitsT>::any() const +{ + ASSERT(mBits == (mBits & Mask(N))); + return (mBits != 0); +} + +template <size_t N, typename BitsT> +bool BitSetT<N, BitsT>::none() const +{ + ASSERT(mBits == (mBits & Mask(N))); + return (mBits == 0); +} + +template <size_t N, typename BitsT> +std::size_t BitSetT<N, BitsT>::count() const +{ + return gl::BitCount(mBits); +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> &BitSetT<N, BitsT>::operator&=(const BitSetT &other) +{ + mBits &= other.mBits; + return *this; +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> &BitSetT<N, BitsT>::operator|=(const BitSetT &other) +{ + mBits |= other.mBits; + return *this; +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> &BitSetT<N, BitsT>::operator^=(const BitSetT &other) +{ + mBits = (mBits ^ other.mBits) & Mask(N); + return *this; +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> BitSetT<N, BitsT>::operator~() const +{ + return BitSetT<N, BitsT>(~mBits & Mask(N)); +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> BitSetT<N, BitsT>::operator<<(std::size_t pos) const +{ + return BitSetT<N, BitsT>((mBits << pos) & Mask(N)); +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> &BitSetT<N, BitsT>::operator<<=(std::size_t pos) +{ + mBits = (mBits << pos & Mask(N)); + return *this; +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> BitSetT<N, BitsT>::operator>>(std::size_t pos) const +{ + return BitSetT<N, BitsT>(mBits >> pos); +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> &BitSetT<N, BitsT>::operator>>=(std::size_t pos) +{ + mBits = ((mBits >> pos) & Mask(N)); + return *this; +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> &BitSetT<N, BitsT>::set() +{ + mBits = Mask(N); + return *this; +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> &BitSetT<N, BitsT>::set(std::size_t pos, bool value) +{ + if (value) + { + mBits |= Bit(pos); + } + else + { + reset(pos); + } + return *this; +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> &BitSetT<N, BitsT>::reset() +{ + mBits = 0; + return *this; +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> &BitSetT<N, BitsT>::reset(std::size_t pos) +{ + mBits &= ~Bit(pos); + return *this; +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> &BitSetT<N, BitsT>::flip() +{ + mBits ^= Mask(N); + return *this; +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT> &BitSetT<N, BitsT>::flip(std::size_t pos) +{ + mBits ^= Bit(pos); + return *this; +} + +template <size_t N, typename BitsT> +BitSetT<N, BitsT>::Iterator::Iterator(const BitSetT &bits) : mBitsCopy(bits), mCurrentBit(0) +{ + if (bits.any()) + { + mCurrentBit = getNextBit(); + } +} + +template <size_t N, typename BitsT> +typename BitSetT<N, BitsT>::Iterator &BitSetT<N, BitsT>::Iterator::operator++() +{ + ASSERT(mBitsCopy.any()); + mBitsCopy.reset(mCurrentBit); + mCurrentBit = getNextBit(); + return *this; +} + +template <size_t N, typename BitsT> +bool BitSetT<N, BitsT>::Iterator::operator==(const Iterator &other) const +{ + return mBitsCopy == other.mBitsCopy; +} + +template <size_t N, typename BitsT> +bool BitSetT<N, BitsT>::Iterator::operator!=(const Iterator &other) const +{ + return !(*this == other); +} + +template <size_t N, typename BitsT> +std::size_t BitSetT<N, BitsT>::Iterator::operator*() const +{ + return mCurrentBit; +} + +template <size_t N, typename BitsT> +std::size_t BitSetT<N, BitsT>::Iterator::getNextBit() +{ + if (mBitsCopy.none()) + { + return 0; + } + + return gl::ScanForward(mBitsCopy.mBits); +} + +template <size_t N> +using BitSet32 = BitSetT<N, uint32_t>; + +// ScanForward for 64-bits requires a 64-bit implementation. +#if defined(ANGLE_IS_64_BIT_CPU) +template <size_t N> +using BitSet64 = BitSetT<N, uint64_t>; +#endif // defined(ANGLE_IS_64_BIT_CPU) + +namespace priv +{ + +template <size_t N, typename T> +using EnableIfBitsFit = typename std::enable_if<N <= sizeof(T) * 8>::type; + +template <size_t N, typename Enable = void> +struct GetBitSet +{ + using Type = IterableBitSet<N>; +}; + +// Prefer 64-bit bitsets on 64-bit CPUs. They seem faster than 32-bit. +#if defined(ANGLE_IS_64_BIT_CPU) +template <size_t N> +struct GetBitSet<N, EnableIfBitsFit<N, uint64_t>> +{ + using Type = BitSet64<N>; +}; +#else +template <size_t N> +struct GetBitSet<N, EnableIfBitsFit<N, uint32_t>> +{ + using Type = BitSet32<N>; +}; +#endif // defined(ANGLE_IS_64_BIT_CPU) + +} // namespace priv + +template <size_t N> +using BitSet = typename priv::GetBitSet<N>::Type; + +} // angle + +template <size_t N, typename BitsT> +inline angle::BitSetT<N, BitsT> operator&(const angle::BitSetT<N, BitsT> &lhs, + const angle::BitSetT<N, BitsT> &rhs) +{ + return angle::BitSetT<N, BitsT>(lhs.bits() & rhs.bits()); +} + +template <size_t N, typename BitsT> +inline angle::BitSetT<N, BitsT> operator|(const angle::BitSetT<N, BitsT> &lhs, + const angle::BitSetT<N, BitsT> &rhs) +{ + return angle::BitSetT<N, BitsT>(lhs.bits() | rhs.bits()); +} + +template <size_t N, typename BitsT> +inline angle::BitSetT<N, BitsT> operator^(const angle::BitSetT<N, BitsT> &lhs, + const angle::BitSetT<N, BitsT> &rhs) +{ + return angle::BitSetT<N, BitsT>(lhs.bits() ^ rhs.bits()); +} + +#endif // COMMON_BITSETITERATOR_H_ diff --git a/src/3rdparty/angle/src/common/debug.cpp b/src/3rdparty/angle/src/common/debug.cpp index 1fcc062908..b02e80be5f 100644 --- a/src/3rdparty/angle/src/common/debug.cpp +++ b/src/3rdparty/angle/src/common/debug.cpp @@ -7,92 +7,61 @@ // debug.cpp: Debugging utilities. #include "common/debug.h" -#include "common/platform.h" -#include "common/angleutils.h" #include <stdarg.h> -#include <vector> -#include <fstream> + +#include <array> #include <cstdio> +#include <fstream> +#include <ostream> +#include <vector> + +#include "common/angleutils.h" +#include "common/Optional.h" namespace gl { namespace { -enum DebugTraceOutputType -{ - DebugTraceOutputTypeNone, - DebugTraceOutputTypeSetMarker, - DebugTraceOutputTypeBeginEvent -}; DebugAnnotator *g_debugAnnotator = nullptr; -void output(bool traceInDebugOnly, MessageType messageType, DebugTraceOutputType outputType, - const char *format, va_list vararg) -{ - if (DebugAnnotationsActive()) - { - static std::vector<char> buffer(512); - size_t len = FormatStringIntoVector(format, vararg, buffer); - std::wstring formattedWideMessage(buffer.begin(), buffer.begin() + len); - - ASSERT(g_debugAnnotator != nullptr); - switch (outputType) - { - case DebugTraceOutputTypeNone: - break; - case DebugTraceOutputTypeBeginEvent: - g_debugAnnotator->beginEvent(formattedWideMessage.c_str()); - break; - case DebugTraceOutputTypeSetMarker: - g_debugAnnotator->setMarker(formattedWideMessage.c_str()); - break; - } - } +constexpr std::array<const char *, LOG_NUM_SEVERITIES> g_logSeverityNames = { + {"EVENT", "WARN", "ERR"}}; - std::string formattedMessage; - UNUSED_VARIABLE(formattedMessage); +constexpr const char *LogSeverityName(int severity) +{ + return (severity >= 0 && severity < LOG_NUM_SEVERITIES) ? g_logSeverityNames[severity] + : "UNKNOWN"; +} -#if !defined(NDEBUG) && defined(_MSC_VER) - if (messageType == MESSAGE_ERR) - { - if (formattedMessage.empty()) - { - formattedMessage = FormatString(format, vararg); - } - OutputDebugStringA(formattedMessage.c_str()); - } +bool ShouldCreateLogMessage(LogSeverity severity) +{ +#if defined(ANGLE_TRACE_ENABLED) + return true; +#elif defined(ANGLE_ENABLE_ASSERTS) + return severity == LOG_ERR; +#else + return false; #endif +} -#if defined(ANGLE_ENABLE_DEBUG_TRACE) -#if defined(NDEBUG) - if (traceInDebugOnly) - { - return; - } -#endif // NDEBUG - if (formattedMessage.empty()) - { - formattedMessage = FormatString(format, vararg); - } - - static std::ofstream file(TRACE_OUTPUT_FILE, std::ofstream::app); - if (file) - { - file.write(formattedMessage.c_str(), formattedMessage.length()); - file.flush(); - } +} // namespace -#if defined(ANGLE_ENABLE_DEBUG_TRACE_TO_DEBUGGER) - OutputDebugStringA(formattedMessage.c_str()); -#endif // ANGLE_ENABLE_DEBUG_TRACE_TO_DEBUGGER +namespace priv +{ -#endif // ANGLE_ENABLE_DEBUG_TRACE +bool ShouldCreatePlatformLogMessage(LogSeverity severity) +{ +#if defined(ANGLE_TRACE_ENABLED) + return true; +#else + return severity != LOG_EVENT; +#endif } -} // namespace +} // namespace priv bool DebugAnnotationsActive() { @@ -103,6 +72,11 @@ bool DebugAnnotationsActive() #endif } +bool DebugAnnotationsInitialized() +{ + return g_debugAnnotator != nullptr; +} + void InitializeDebugAnnotations(DebugAnnotator *debugAnnotator) { UninitializeDebugAnnotations(); @@ -115,25 +89,20 @@ void UninitializeDebugAnnotations() g_debugAnnotator = nullptr; } -void trace(bool traceInDebugOnly, MessageType messageType, const char *format, ...) -{ - va_list vararg; - va_start(vararg, format); - output(traceInDebugOnly, messageType, DebugTraceOutputTypeSetMarker, format, vararg); - va_end(vararg); -} - -ScopedPerfEventHelper::ScopedPerfEventHelper(const char* format, ...) +ScopedPerfEventHelper::ScopedPerfEventHelper(const char *format, ...) { #if !defined(ANGLE_ENABLE_DEBUG_TRACE) if (!DebugAnnotationsActive()) { return; } -#endif // !ANGLE_ENABLE_DEBUG_TRACE +#endif // !ANGLE_ENABLE_DEBUG_TRACE + va_list vararg; va_start(vararg, format); - output(true, MESSAGE_EVENT, DebugTraceOutputTypeBeginEvent, format, vararg); + std::vector<char> buffer(512); + size_t len = FormatStringIntoVector(format, vararg, buffer); + ANGLE_LOG(EVENT) << std::string(&buffer[0], len); va_end(vararg); } @@ -145,4 +114,107 @@ ScopedPerfEventHelper::~ScopedPerfEventHelper() } } +LogMessage::LogMessage(const char *function, int line, LogSeverity severity) + : mFunction(function), mLine(line), mSeverity(severity) +{ + // EVENT() does not require additional function(line) info. + if (mSeverity != LOG_EVENT) + { + mStream << mFunction << "(" << mLine << "): "; + } +} + +LogMessage::~LogMessage() +{ + if (DebugAnnotationsInitialized() && (mSeverity == LOG_ERR || mSeverity == LOG_WARN)) + { + g_debugAnnotator->logMessage(*this); + } + else + { + Trace(getSeverity(), getMessage().c_str()); + } +} + +void Trace(LogSeverity severity, const char *message) +{ + if (!ShouldCreateLogMessage(severity)) + { + return; + } + + std::string str(message); + + if (DebugAnnotationsActive()) + { + std::wstring formattedWideMessage(str.begin(), str.end()); + + switch (severity) + { + case LOG_EVENT: + g_debugAnnotator->beginEvent(formattedWideMessage.c_str()); + break; + default: + g_debugAnnotator->setMarker(formattedWideMessage.c_str()); + break; + } + } + + if (severity == LOG_ERR) + { + // Note: we use fprintf because <iostream> includes static initializers. + fprintf(stderr, "%s: %s\n", LogSeverityName(severity), str.c_str()); + } + +#if defined(ANGLE_PLATFORM_WINDOWS) && \ + (defined(ANGLE_ENABLE_DEBUG_TRACE_TO_DEBUGGER) || !defined(NDEBUG)) +#if !defined(ANGLE_ENABLE_DEBUG_TRACE_TO_DEBUGGER) + if (severity == LOG_ERR) +#endif // !defined(ANGLE_ENABLE_DEBUG_TRACE_TO_DEBUGGER) + { + OutputDebugStringA(str.c_str()); + } +#endif + +#if defined(ANGLE_ENABLE_DEBUG_TRACE) +#if defined(NDEBUG) + if (severity == LOG_EVENT || severity == LOG_WARN) + { + return; + } +#endif // defined(NDEBUG) + static std::ofstream file(TRACE_OUTPUT_FILE, std::ofstream::app); + if (file) + { + file << LogSeverityName(severity) << ": " << str << std::endl; + file.flush(); + } +#endif // defined(ANGLE_ENABLE_DEBUG_TRACE) } + +LogSeverity LogMessage::getSeverity() const +{ + return mSeverity; +} + +std::string LogMessage::getMessage() const +{ + return mStream.str(); +} + +#if defined(ANGLE_PLATFORM_WINDOWS) +std::ostream &operator<<(std::ostream &os, const FmtHR &fmt) +{ + os << "HRESULT: "; + return FmtHexInt(os, fmt.mHR); +} + +std::ostream &operator<<(std::ostream &os, const FmtErr &fmt) +{ + os << "error: "; + return FmtHexInt(os, fmt.mErr); +} + +#endif // defined(ANGLE_PLATFORM_WINDOWS) + +} // namespace gl diff --git a/src/3rdparty/angle/src/common/debug.h b/src/3rdparty/angle/src/common/debug.h index 64cfef4cd9..290a4e8bb7 100644 --- a/src/3rdparty/angle/src/common/debug.h +++ b/src/3rdparty/angle/src/common/debug.h @@ -11,6 +11,10 @@ #include <assert.h> #include <stdio.h> + +#include <ios> +#include <iomanip> +#include <sstream> #include <string> #include "common/angleutils.h" @@ -22,17 +26,6 @@ namespace gl { -enum MessageType -{ - MESSAGE_TRACE, - MESSAGE_FIXME, - MESSAGE_ERR, - MESSAGE_EVENT, -}; - -// Outputs text to the debugging log, or the debugging window -void trace(bool traceInDebugOnly, MessageType messageType, const char *format, ...); - // Pairs a D3D begin event with an end event. class ScopedPerfEventHelper : angle::NonCopyable { @@ -41,50 +34,177 @@ class ScopedPerfEventHelper : angle::NonCopyable ~ScopedPerfEventHelper(); }; +using LogSeverity = int; +// Note: the log severities are used to index into the array of names, +// see g_logSeverityNames. +constexpr LogSeverity LOG_EVENT = 0; +constexpr LogSeverity LOG_WARN = 1; +constexpr LogSeverity LOG_ERR = 2; +constexpr LogSeverity LOG_NUM_SEVERITIES = 3; + +void Trace(LogSeverity severity, const char *message); + +// This class more or less represents a particular log message. You +// create an instance of LogMessage and then stream stuff to it. +// When you finish streaming to it, ~LogMessage is called and the +// full message gets streamed to the appropriate destination. +// +// You shouldn't actually use LogMessage's constructor to log things, +// though. You should use the ERR() and WARN() macros. +class LogMessage : angle::NonCopyable +{ + public: + // Used for ANGLE_LOG(severity). + LogMessage(const char *function, int line, LogSeverity severity); + ~LogMessage(); + std::ostream &stream() { return mStream; } + + LogSeverity getSeverity() const; + std::string getMessage() const; + + private: + const char *mFunction; + const int mLine; + const LogSeverity mSeverity; + + std::ostringstream mStream; +}; + // Wraps the D3D9/D3D11 debug annotation functions. +// Also handles redirecting logging destination. class DebugAnnotator : angle::NonCopyable { public: - DebugAnnotator() { }; + DebugAnnotator(){}; virtual ~DebugAnnotator() { }; virtual void beginEvent(const wchar_t *eventName) = 0; virtual void endEvent() = 0; virtual void setMarker(const wchar_t *markerName) = 0; virtual bool getStatus() = 0; + // Log Message Handler that gets passed every log message, + // when debug annotations are initialized, + // replacing default handling by LogMessage. + virtual void logMessage(const LogMessage &msg) const = 0; }; void InitializeDebugAnnotations(DebugAnnotator *debugAnnotator); void UninitializeDebugAnnotations(); bool DebugAnnotationsActive(); +bool DebugAnnotationsInitialized(); + +namespace priv +{ +// This class is used to explicitly ignore values in the conditional logging macros. This avoids +// compiler warnings like "value computed is not used" and "statement has no effect". +class LogMessageVoidify +{ + public: + LogMessageVoidify() {} + // This has to be an operator with a precedence lower than << but higher than ?: + void operator&(std::ostream &) {} +}; + +// Used by ANGLE_LOG_IS_ON to lazy-evaluate stream arguments. +bool ShouldCreatePlatformLogMessage(LogSeverity severity); + +template <int N, typename T> +std::ostream &FmtHex(std::ostream &os, T value) +{ + os << "0x"; + + std::ios_base::fmtflags oldFlags = os.flags(); + std::streamsize oldWidth = os.width(); + std::ostream::char_type oldFill = os.fill(); + + os << std::hex << std::uppercase << std::setw(N) << std::setfill('0') << value; + + os.flags(oldFlags); + os.width(oldWidth); + os.fill(oldFill); + + return os; +} +} // namespace priv + +#if defined(ANGLE_PLATFORM_WINDOWS) +class FmtHR +{ + public: + explicit FmtHR(HRESULT hresult) : mHR(hresult) {} + private: + HRESULT mHR; + friend std::ostream &operator<<(std::ostream &os, const FmtHR &fmt); +}; + +class FmtErr +{ + public: + explicit FmtErr(DWORD err) : mErr(err) {} + + private: + DWORD mErr; + friend std::ostream &operator<<(std::ostream &os, const FmtErr &fmt); +}; +#endif // defined(ANGLE_PLATFORM_WINDOWS) + +template <typename T> +std::ostream &FmtHexShort(std::ostream &os, T value) +{ + return priv::FmtHex<4>(os, value); +} +template <typename T> +std::ostream &FmtHexInt(std::ostream &os, T value) +{ + return priv::FmtHex<8>(os, value); } +// A few definitions of macros that don't generate much code. These are used +// by ANGLE_LOG(). Since these are used all over our code, it's +// better to have compact code for these operations. +#define COMPACT_ANGLE_LOG_EX_EVENT(ClassName, ...) \ + ::gl::ClassName(__FUNCTION__, __LINE__, ::gl::LOG_EVENT, ##__VA_ARGS__) +#define COMPACT_ANGLE_LOG_EX_WARN(ClassName, ...) \ + ::gl::ClassName(__FUNCTION__, __LINE__, ::gl::LOG_WARN, ##__VA_ARGS__) +#define COMPACT_ANGLE_LOG_EX_ERR(ClassName, ...) \ + ::gl::ClassName(__FUNCTION__, __LINE__, ::gl::LOG_ERR, ##__VA_ARGS__) + +#define COMPACT_ANGLE_LOG_EVENT COMPACT_ANGLE_LOG_EX_EVENT(LogMessage) +#define COMPACT_ANGLE_LOG_WARN COMPACT_ANGLE_LOG_EX_WARN(LogMessage) +#define COMPACT_ANGLE_LOG_ERR COMPACT_ANGLE_LOG_EX_ERR(LogMessage) + +#define ANGLE_LOG_IS_ON(severity) (::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_##severity)) + +// Helper macro which avoids evaluating the arguments to a stream if the condition doesn't hold. +// Condition is evaluated once and only once. +#define ANGLE_LAZY_STREAM(stream, condition) \ + !(condition) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify() & (stream) + +// We use the preprocessor's merging operator, "##", so that, e.g., +// ANGLE_LOG(EVENT) becomes the token COMPACT_ANGLE_LOG_EVENT. There's some funny +// subtle difference between ostream member streaming functions (e.g., +// ostream::operator<<(int) and ostream non-member streaming functions +// (e.g., ::operator<<(ostream&, string&): it turns out that it's +// impossible to stream something like a string directly to an unnamed +// ostream. We employ a neat hack by calling the stream() member +// function of LogMessage which seems to avoid the problem. +#define ANGLE_LOG_STREAM(severity) COMPACT_ANGLE_LOG_##severity.stream() + +#define ANGLE_LOG(severity) ANGLE_LAZY_STREAM(ANGLE_LOG_STREAM(severity), ANGLE_LOG_IS_ON(severity)) + +} // namespace gl + #if defined(ANGLE_ENABLE_DEBUG_TRACE) || defined(ANGLE_ENABLE_DEBUG_ANNOTATIONS) #define ANGLE_TRACE_ENABLED #endif #define ANGLE_EMPTY_STATEMENT for (;;) break - -// A macro to output a trace of a function call and its arguments to the debugging log -#if defined(ANGLE_TRACE_ENABLED) -#define TRACE(message, ...) gl::trace(true, gl::MESSAGE_TRACE, "trace: %s(%d): " message "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__) -#else -#define TRACE(message, ...) (void(0)) -#endif - -// A macro to output a function call and its arguments to the debugging log, to denote an item in need of fixing. -#if defined(ANGLE_TRACE_ENABLED) -#define FIXME(message, ...) gl::trace(false, gl::MESSAGE_FIXME, "fixme: %s(%d): " message "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__) -#else -#define FIXME(message, ...) (void(0)) +#if !defined(NDEBUG) || defined(ANGLE_ENABLE_RELEASE_ASSERTS) +#define ANGLE_ENABLE_ASSERTS #endif -// A macro to output a function call and its arguments to the debugging log, in case of error. -#if defined(ANGLE_TRACE_ENABLED) -#define ERR(message, ...) gl::trace(false, gl::MESSAGE_ERR, "err: %s(%d): " message "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__) -#else -#define ERR(message, ...) (void(0)) -#endif +#define WARN() ANGLE_LOG(WARN) +#define ERR() ANGLE_LOG(ERR) // A macro to log a performance event around a scope. #if defined(ANGLE_TRACE_ENABLED) @@ -97,54 +217,70 @@ bool DebugAnnotationsActive(); #define EVENT(message, ...) (void(0)) #endif -#if defined(ANGLE_TRACE_ENABLED) -#undef ANGLE_TRACE_ENABLED +#if defined(COMPILER_GCC) || defined(__clang__) +#define ANGLE_CRASH() __builtin_trap() +#else +#define ANGLE_CRASH() ((void)(*(volatile char *)0 = 0)) #endif -// A macro asserting a condition and outputting failures to the debug log #if !defined(NDEBUG) -#define ASSERT(expression) { \ - if(!(expression)) \ - ERR("\t! Assert failed in %s(%d): %s\n", __FUNCTION__, __LINE__, #expression); \ - assert(expression); \ - } ANGLE_EMPTY_STATEMENT -#define UNUSED_ASSERTION_VARIABLE(variable) +#define ANGLE_ASSERT_IMPL(expression) assert(expression) #else -#define ASSERT(expression) (void(0)) -#define UNUSED_ASSERTION_VARIABLE(variable) ((void)variable) -#endif +// TODO(jmadill): Detect if debugger is attached and break. +#define ANGLE_ASSERT_IMPL(expression) ANGLE_CRASH() +#endif // !defined(NDEBUG) + +// A macro asserting a condition and outputting failures to the debug log +#if defined(ANGLE_ENABLE_ASSERTS) +#define ASSERT(expression) \ + (expression ? static_cast<void>(0) : ((ERR() << "\t! Assert failed in " << __FUNCTION__ << "(" \ + << __LINE__ << "): " << #expression), \ + ANGLE_ASSERT_IMPL(expression))) +#else +// These are just dummy values. +#define COMPACT_ANGLE_LOG_EX_ASSERT(ClassName, ...) \ + COMPACT_ANGLE_LOG_EX_EVENT(ClassName, ##__VA_ARGS__) +#define COMPACT_ANGLE_LOG_ASSERT COMPACT_ANGLE_LOG_EVENT +namespace gl +{ +constexpr LogSeverity LOG_ASSERT = LOG_EVENT; +} // namespace gl + +#define ASSERT(condition) \ + ANGLE_LAZY_STREAM(ANGLE_LOG_STREAM(ASSERT), false ? !(condition) : false) \ + << "Check failed: " #condition ". " +#endif // defined(ANGLE_ENABLE_ASSERTS) #define UNUSED_VARIABLE(variable) ((void)variable) // A macro to indicate unimplemented functionality - -#if defined (ANGLE_TEST_CONFIG) +#ifndef NOASSERT_UNIMPLEMENTED #define NOASSERT_UNIMPLEMENTED 1 #endif -// Define NOASSERT_UNIMPLEMENTED to non zero to skip the assert fail in the unimplemented checks -// This will allow us to test with some automated test suites (eg dEQP) without crashing -#ifndef NOASSERT_UNIMPLEMENTED -#define NOASSERT_UNIMPLEMENTED 0 -#endif +#if defined(ANGLE_TRACE_ENABLED) || defined(ANGLE_ENABLE_ASSERTS) +#define UNIMPLEMENTED() \ + { \ + ERR() << "\t! Unimplemented: " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ \ + << ")"; \ + ASSERT(NOASSERT_UNIMPLEMENTED); \ + } \ + ANGLE_EMPTY_STATEMENT -#if !defined(NDEBUG) -#define UNIMPLEMENTED() { \ - FIXME("\t! Unimplemented: %s(%d)\n", __FUNCTION__, __LINE__); \ - assert(NOASSERT_UNIMPLEMENTED); \ - } ANGLE_EMPTY_STATEMENT +// A macro for code which is not expected to be reached under valid assumptions +#define UNREACHABLE() \ + ((ERR() << "\t! Unreachable reached: " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ \ + << ")"), \ + ASSERT(false)) #else - #define UNIMPLEMENTED() FIXME("\t! Unimplemented: %s(%d)\n", __FUNCTION__, __LINE__) -#endif +#define UNIMPLEMENTED() \ + { \ + ASSERT(NOASSERT_UNIMPLEMENTED); \ + } \ + ANGLE_EMPTY_STATEMENT // A macro for code which is not expected to be reached under valid assumptions -#if !defined(NDEBUG) -#define UNREACHABLE() { \ - ERR("\t! Unreachable reached: %s(%d)\n", __FUNCTION__, __LINE__); \ - assert(false); \ - } ANGLE_EMPTY_STATEMENT -#else - #define UNREACHABLE() ERR("\t! Unreachable reached: %s(%d)\n", __FUNCTION__, __LINE__) -#endif +#define UNREACHABLE() ASSERT(false) +#endif // defined(ANGLE_TRACE_ENABLED) || defined(ANGLE_ENABLE_ASSERTS) #endif // COMMON_DEBUG_H_ diff --git a/src/3rdparty/angle/src/common/event_tracer.cpp b/src/3rdparty/angle/src/common/event_tracer.cpp index c9eb5e3073..6dc8458acc 100644 --- a/src/3rdparty/angle/src/common/event_tracer.cpp +++ b/src/3rdparty/angle/src/common/event_tracer.cpp @@ -11,10 +11,11 @@ namespace angle const unsigned char *GetTraceCategoryEnabledFlag(const char *name) { - angle::Platform *platform = ANGLEPlatformCurrent(); + auto *platform = ANGLEPlatformCurrent(); ASSERT(platform); - const unsigned char *categoryEnabledFlag = platform->getTraceCategoryEnabledFlag(name); + const unsigned char *categoryEnabledFlag = + platform->getTraceCategoryEnabledFlag(platform, name); if (categoryEnabledFlag != nullptr) { return categoryEnabledFlag; @@ -24,33 +25,31 @@ const unsigned char *GetTraceCategoryEnabledFlag(const char *name) return &disabled; } -Platform::TraceEventHandle AddTraceEvent(char phase, const unsigned char* categoryGroupEnabled, const char* name, unsigned long long id, - int numArgs, const char** argNames, const unsigned char* argTypes, - const unsigned long long* argValues, unsigned char flags) +angle::TraceEventHandle AddTraceEvent(char phase, + const unsigned char *categoryGroupEnabled, + const char *name, + unsigned long long id, + int numArgs, + const char **argNames, + const unsigned char *argTypes, + const unsigned long long *argValues, + unsigned char flags) { - angle::Platform *platform = ANGLEPlatformCurrent(); + auto *platform = ANGLEPlatformCurrent(); ASSERT(platform); - double timestamp = platform->monotonicallyIncreasingTime(); + double timestamp = platform->monotonicallyIncreasingTime(platform); if (timestamp != 0) { - angle::Platform::TraceEventHandle handle = - platform->addTraceEvent(phase, - categoryGroupEnabled, - name, - id, - timestamp, - numArgs, - argNames, - argTypes, - argValues, - flags); + angle::TraceEventHandle handle = + platform->addTraceEvent(platform, phase, categoryGroupEnabled, name, id, timestamp, + numArgs, argNames, argTypes, argValues, flags); ASSERT(handle != 0); return handle; } - return static_cast<Platform::TraceEventHandle>(0); + return static_cast<angle::TraceEventHandle>(0); } } // namespace angle diff --git a/src/3rdparty/angle/src/common/event_tracer.h b/src/3rdparty/angle/src/common/event_tracer.h index ed70f249d2..9b30c750c1 100644 --- a/src/3rdparty/angle/src/common/event_tracer.h +++ b/src/3rdparty/angle/src/common/event_tracer.h @@ -12,11 +12,15 @@ namespace angle { const unsigned char *GetTraceCategoryEnabledFlag(const char* name); -Platform::TraceEventHandle AddTraceEvent(char phase, const unsigned char* categoryGroupEnabled, const char* name, - unsigned long long id, int numArgs, const char** argNames, - const unsigned char* argTypes, const unsigned long long* argValues, - unsigned char flags); - +angle::TraceEventHandle AddTraceEvent(char phase, + const unsigned char *categoryGroupEnabled, + const char *name, + unsigned long long id, + int numArgs, + const char **argNames, + const unsigned char *argTypes, + const unsigned long long *argValues, + unsigned char flags); } #endif // COMMON_EVENT_TRACER_H_ diff --git a/src/3rdparty/angle/src/common/mathutil.cpp b/src/3rdparty/angle/src/common/mathutil.cpp index 927b6ebebe..5db997c664 100644 --- a/src/3rdparty/angle/src/common/mathutil.cpp +++ b/src/3rdparty/angle/src/common/mathutil.cpp @@ -14,6 +14,9 @@ namespace gl { +namespace +{ + struct RGB9E5Data { unsigned int R : 9; @@ -23,17 +26,20 @@ struct RGB9E5Data }; // B is the exponent bias (15) -static const int g_sharedexp_bias = 15; +constexpr int g_sharedexp_bias = 15; // N is the number of mantissa bits per component (9) -static const int g_sharedexp_mantissabits = 9; +constexpr int g_sharedexp_mantissabits = 9; // Emax is the maximum allowed biased exponent value (31) -static const int g_sharedexp_maxexponent = 31; +constexpr int g_sharedexp_maxexponent = 31; -static const float g_sharedexp_max = ((pow(2.0f, g_sharedexp_mantissabits) - 1) / - pow(2.0f, g_sharedexp_mantissabits)) * - pow(2.0f, g_sharedexp_maxexponent - g_sharedexp_bias); +constexpr float g_sharedexp_max = + ((static_cast<float>(1 << g_sharedexp_mantissabits) - 1) / + static_cast<float>(1 << g_sharedexp_mantissabits)) * + static_cast<float>(1 << (g_sharedexp_maxexponent - g_sharedexp_bias)); + +} // anonymous namespace unsigned int convertRGBFloatsTo999E5(float red, float green, float blue) { @@ -64,4 +70,4 @@ void convert999E5toRGBFloats(unsigned int input, float *red, float *green, float *blue = inputData->B * pow(2.0f, (int)inputData->E - g_sharedexp_bias - g_sharedexp_mantissabits); } -} +} // namespace gl diff --git a/src/3rdparty/angle/src/common/mathutil.h b/src/3rdparty/angle/src/common/mathutil.h index 3de62aef10..88aedddfe8 100644 --- a/src/3rdparty/angle/src/common/mathutil.h +++ b/src/3rdparty/angle/src/common/mathutil.h @@ -9,9 +9,6 @@ #ifndef COMMON_MATHUTIL_H_ #define COMMON_MATHUTIL_H_ -#include "common/debug.h" -#include "common/platform.h" - #include <limits> #include <algorithm> #include <math.h> @@ -19,25 +16,27 @@ #include <stdint.h> #include <stdlib.h> +#include <anglebase/numerics/safe_math.h> + +#include "common/debug.h" +#include "common/platform.h" + +namespace angle +{ +using base::CheckedNumeric; +using base::IsValueInRangeForNumericType; +} + namespace gl { const unsigned int Float32One = 0x3F800000; const unsigned short Float16One = 0x3C00; -struct Vector4 -{ - Vector4() {} - Vector4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {} - - float x; - float y; - float z; - float w; -}; - -inline bool isPow2(int x) +template<typename T> +inline 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); } @@ -61,37 +60,52 @@ inline unsigned int ceilPow2(unsigned int x) return x; } -inline int clampToInt(unsigned int x) -{ - return static_cast<int>(std::min(x, static_cast<unsigned int>(std::numeric_limits<int>::max()))); -} - template <typename DestT, typename SrcT> inline DestT clampCast(SrcT value) { - static const DestT destLo = std::numeric_limits<DestT>::min(); - static const DestT destHi = std::numeric_limits<DestT>::max(); - static const SrcT srcLo = static_cast<SrcT>(destLo); - static const SrcT srcHi = static_cast<SrcT>(destHi); - - // When value is outside of or equal to the limits for DestT we use the DestT limit directly. - // This avoids undefined behaviors due to loss of precision when converting from floats to - // integers: - // destHi for ints is 2147483647 but the closest float number is around 2147483648, so when - // doing a conversion from float to int we run into an UB because the float is outside of the - // range representable by the int. - if (value <= srcLo) - { - return destLo; - } - else if (value >= srcHi) + // 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 const long double destLo = + static_cast<long double>(std::numeric_limits<DestT>::lowest()); + constexpr const long double destHi = + static_cast<long double>(std::numeric_limits<DestT>::max()); + constexpr const long double srcLo = + static_cast<long double>(std::numeric_limits<SrcT>::lowest()); + constexpr long double srcHi = static_cast<long double>(std::numeric_limits<SrcT>::max()); + + if (destHi < srcHi) { - return destHi; + DestT destMax = std::numeric_limits<DestT>::max(); + if (value >= static_cast<SrcT>(destMax)) + { + return destMax; + } } - else + + if (destLo > srcLo) { - return static_cast<DestT>(value); + DestT destLow = std::numeric_limits<DestT>::lowest(); + if (value <= static_cast<SrcT>(destLow)) + { + return destLow; + } } + + return static_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 <> +inline unsigned int clampCast(bool value) +{ + return static_cast<unsigned int>(value); +} + +template <> +inline int clampCast(bool value) +{ + return static_cast<int>(value); } template<typename T, typename MIN, typename MAX> @@ -127,7 +141,7 @@ inline unsigned int unorm(float x) inline bool supportsSSE2() { -#if defined(ANGLE_PLATFORM_WINDOWS) && !defined(_M_ARM) +#if defined(ANGLE_USE_SSE) static bool checked = false; static bool supports = false; @@ -136,21 +150,22 @@ inline bool supportsSSE2() return supports; } - int info[4]; - __cpuid(info, 0); - - if (info[0] >= 1) +#if defined(ANGLE_PLATFORM_WINDOWS) && !defined(_M_ARM) && !defined(_M_ARM64) { - __cpuid(info, 1); + int info[4]; + __cpuid(info, 0); - supports = (info[3] >> 26) & 1; - } + if (info[0] >= 1) + { + __cpuid(info, 1); + supports = (info[3] >> 26) & 1; + } + } +#endif // defined(ANGLE_PLATFORM_WINDOWS) && !defined(_M_ARM) && !defined(_M_ARM64) checked = true; - return supports; -#else - UNIMPLEMENTED(); +#else // defined(ANGLE_USE_SSE) return false; #endif } @@ -467,6 +482,43 @@ inline T shiftData(T input) return (input & mask) << inputBitStart; } +inline unsigned int CountLeadingZeros(uint32_t x) +{ + // Use binary search to find the amount of leading zeros. + unsigned int 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; +} inline unsigned char average(unsigned char a, unsigned char b) { @@ -520,38 +572,65 @@ inline unsigned int averageFloat10(unsigned int a, unsigned int b) } template <typename T> -struct Range +class Range { + public: Range() {} - Range(T lo, T hi) : start(lo), end(hi) { ASSERT(lo <= hi); } - - T start; - T end; + Range(T lo, T hi) : mLow(lo), mHigh(hi) {} - T length() const { return end - start; } + T length() const { return (empty() ? 0 : (mHigh - mLow)); } bool intersects(Range<T> other) { - if (start <= other.start) + if (mLow <= other.mLow) { - return other.start < end; + return other.mLow < mHigh; } else { - return start < other.end; + return mLow < other.mHigh; } } + // Assumes that end is non-inclusive.. for example, extending to 5 will make "end" 6. void extend(T value) { - start = value > start ? value : start; - end = value < end ? value : end; + mLow = value < mLow ? value : mLow; + mHigh = value >= mHigh ? (value + 1) : mHigh; } - bool empty() const + bool empty() const { return mHigh <= mLow; } + + bool contains(T value) const { return value >= mLow && value < mHigh; } + + class Iterator final { - return end <= start; - } + public: + Iterator(T value) : mCurrent(value) {} + + Iterator &operator++() + { + mCurrent++; + return *this; + } + bool operator==(const Iterator &other) const { return mCurrent == other.mCurrent; } + bool operator!=(const Iterator &other) const { return mCurrent != other.mCurrent; } + T operator*() const { return mCurrent; } + + private: + T mCurrent; + }; + + Iterator begin() const { return Iterator(mLow); } + + Iterator end() const { return Iterator(mHigh); } + + T low() const { return mLow; } + T high() const { return mHigh; } + + private: + T mLow; + T mHigh; }; typedef Range<int> RangeI; @@ -577,6 +656,22 @@ struct IndexRange 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. +inline float 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)); + return static_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; @@ -632,6 +727,86 @@ inline void unpackUnorm2x16(uint32_t u, float *f1, float *f2) *f2 = static_cast<float>(mostSignificantBits) / 65535.0f; } +// Helper functions intended to be used only here. +namespace priv +{ + +inline uint8_t ToPackedUnorm8(float f) +{ + return static_cast<uint8_t>(roundf(clamp(f, 0.0f, 1.0f) * 255.0f)); +} + +inline int8_t ToPackedSnorm8(float f) +{ + return static_cast<int8_t>(roundf(clamp(f, -1.0f, 1.0f) * 127.0f)); +} + +} // 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. +inline void 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. +inline void 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. @@ -658,6 +833,179 @@ inline void unpackHalf2x16(uint32_t u, float *f1, float *f2) *f2 = float16ToFloat32(mostSignificantBits); } +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); + } + return static_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; + } + else if (value < 0.0031308f) + { + value = value * 12.92f; + } + else if (value < 1.0f) + { + value = std::pow(value, 0.41666f) * 1.055f - 0.055f; + } + else + { + value = 1.0f; + } + return static_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. +#if defined(ANGLE_PLATFORM_WINDOWS) +#if defined(_M_ARM) || defined(_M_ARM64) +inline int BitCount(uint32_t bits) +{ + bits = bits - ((bits >> 1) & 0x55555555); + bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); + return (((bits + (bits >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; +} +#else // _M_ARM || _M_ARM64 +inline int BitCount(uint32_t bits) +{ + return static_cast<int>(__popcnt(bits)); +} +#if defined(ANGLE_IS_64_BIT_CPU) +inline int BitCount(uint64_t bits) +{ + return static_cast<int>(__popcnt64(bits)); +} +#endif // !_M_ARM +#endif // defined(ANGLE_IS_64_BIT_CPU) +#endif // defined(ANGLE_PLATFORM_WINDOWS) + +#if defined(ANGLE_PLATFORM_POSIX) +inline int BitCount(uint32_t bits) +{ + return __builtin_popcount(bits); +} + +#if defined(ANGLE_IS_64_BIT_CPU) +inline int BitCount(uint64_t bits) +{ + return __builtin_popcountll(bits); +} +#endif // defined(ANGLE_IS_64_BIT_CPU) +#endif // defined(ANGLE_PLATFORM_POSIX) + +#if defined(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. +inline unsigned long ScanForward(uint32_t bits) +{ + ASSERT(bits != 0u); + unsigned long firstBitIndex = 0ul; + unsigned char ret = _BitScanForward(&firstBitIndex, bits); + ASSERT(ret != 0u); + return firstBitIndex; +} + +#if defined(ANGLE_IS_64_BIT_CPU) +inline unsigned long ScanForward(uint64_t bits) +{ + ASSERT(bits != 0u); + unsigned long firstBitIndex = 0ul; + unsigned char ret = _BitScanForward64(&firstBitIndex, bits); + ASSERT(ret != 0u); + return firstBitIndex; +} +#endif // defined(ANGLE_IS_64_BIT_CPU) +#endif // defined(ANGLE_PLATFORM_WINDOWS) + +#if defined(ANGLE_PLATFORM_POSIX) +inline unsigned long ScanForward(uint32_t bits) +{ + ASSERT(bits != 0u); + return static_cast<unsigned long>(__builtin_ctz(bits)); +} + +#if defined(ANGLE_IS_64_BIT_CPU) +inline unsigned long ScanForward(uint64_t bits) +{ + ASSERT(bits != 0u); + return static_cast<unsigned long>(__builtin_ctzll(bits)); +} +#endif // defined(ANGLE_IS_64_BIT_CPU) +#endif // defined(ANGLE_PLATFORM_POSIX) + +// Return the index of the most significant bit set. Indexing is such that bit 0 is the least +// significant bit. +inline unsigned long ScanReverse(unsigned long bits) +{ + ASSERT(bits != 0u); +#if defined(ANGLE_PLATFORM_WINDOWS) + unsigned long lastBitIndex = 0ul; + unsigned char ret = _BitScanReverse(&lastBitIndex, bits); + ASSERT(ret != 0u); + return lastBitIndex; +#elif defined(ANGLE_PLATFORM_POSIX) + return static_cast<unsigned long>(sizeof(unsigned long) * CHAR_BIT - 1 - __builtin_clzl(bits)); +#else +#error Please implement bit-scan-reverse for your platform! +#endif +} + +// 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 + { + return static_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 + { + return static_cast<int>(ScanReverse(bits)); + } +} + // Returns whether the argument is Not a Number. // IEEE 754 single precision NaN representation: Exponent(8 bits) - 255, Mantissa(23 bits) - non-zero. inline bool isNaN(float f) @@ -676,41 +1024,98 @@ inline bool isInf(float f) return ((bitCast<uint32_t>(f) & 0x7f800000u) == 0x7f800000u) && !(bitCast<uint32_t>(f) & 0x7fffffu); } +namespace priv +{ +template <unsigned int N, unsigned int R> +struct iSquareRoot +{ + static constexpr unsigned int solve() + { + return (R * R > N) + ? 0 + : ((R * R == N) ? R : static_cast<unsigned int>(iSquareRoot<N, R + 1>::value)); + } + enum Result + { + value = iSquareRoot::solve() + }; +}; + +template <unsigned int N> +struct iSquareRoot<N, N> +{ + enum result + { + value = N + }; +}; + +} // namespace priv + +template <unsigned int N> +constexpr unsigned int iSquareRoot() +{ + return priv::iSquareRoot<N, 1>::value; } -namespace rx +// 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. + +template <typename T> +inline T WrappingSum(T lhs, T rhs) { + uint32_t lhsUnsigned = static_cast<uint32_t>(lhs); + uint32_t rhsUnsigned = static_cast<uint32_t>(rhs); + return static_cast<T>(lhsUnsigned + rhsUnsigned); +} template <typename T> -T roundUp(const T value, const T alignment) +inline T WrappingDiff(T lhs, T rhs) { - return value + alignment - 1 - (value - 1) % alignment; + uint32_t lhsUnsigned = static_cast<uint32_t>(lhs); + uint32_t rhsUnsigned = static_cast<uint32_t>(rhs); + return static_cast<T>(lhsUnsigned - rhsUnsigned); } -inline unsigned int UnsignedCeilDivide(unsigned int value, unsigned int divisor) +inline int32_t WrappingMul(int32_t lhs, int32_t rhs) { - unsigned int divided = value / divisor; - return (divided + ((value % divisor == 0) ? 0 : 1)); + 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. + return static_cast<int32_t>(resultWide); } -template <class T> -inline bool IsUnsignedAdditionSafe(T lhs, T rhs) +} // namespace gl + +namespace rx +{ + +template <typename T> +T roundUp(const T value, const T alignment) { - static_assert(!std::numeric_limits<T>::is_signed, "T must be unsigned."); - return (rhs <= std::numeric_limits<T>::max() - lhs); + auto temp = value + alignment - static_cast<T>(1); + return temp - temp % alignment; } -template <class T> -inline bool IsUnsignedMultiplicationSafe(T lhs, T rhs) +template <typename T> +angle::CheckedNumeric<T> CheckedRoundUp(const T value, const T alignment) { - static_assert(!std::numeric_limits<T>::is_signed, "T must be unsigned."); - return (lhs == T(0) || rhs == T(0) || (rhs <= std::numeric_limits<T>::max() / lhs)); + angle::CheckedNumeric<T> checkedValue(value); + angle::CheckedNumeric<T> checkedAlignment(alignment); + return roundUp(checkedValue, checkedAlignment); } -template <class SmallIntT, class BigIntT> -inline bool IsIntegerCastSafe(BigIntT bigValue) +inline unsigned int UnsignedCeilDivide(unsigned int value, unsigned int divisor) { - return (static_cast<BigIntT>(static_cast<SmallIntT>(bigValue)) == bigValue); + unsigned int divided = value / divisor; + return (divided + ((value % divisor == 0) ? 0 : 1)); } #if defined(_MSC_VER) @@ -730,8 +1135,8 @@ inline uint16_t RotR16(uint16_t x, int8_t r) return (x >> r) | (x << (16 - r)); } -#define ANGLE_ROTL(x,y) RotL(x,y) -#define ANGLE_ROTR16(x,y) RotR16(x,y) +#define ANGLE_ROTL(x, y) ::rx::RotL(x, y) +#define ANGLE_ROTR16(x, y) ::rx::RotR16(x, y) #endif // namespace rx diff --git a/src/3rdparty/angle/src/common/matrix_utils.h b/src/3rdparty/angle/src/common/matrix_utils.h index 6f3187c3e8..aa3f89536e 100644 --- a/src/3rdparty/angle/src/common/matrix_utils.h +++ b/src/3rdparty/angle/src/common/matrix_utils.h @@ -16,6 +16,7 @@ #include <vector> #include "common/debug.h" +#include "common/mathutil.h" namespace angle { @@ -337,6 +338,42 @@ class Matrix return result; } + void setToIdentity() + { + ASSERT(rows() == columns()); + + const auto one = T(1); + const auto zero = T(0); + + for (auto &e : mElements) + e = zero; + + for (unsigned int i = 0; i < rows(); ++i) + { + const auto pos = i * columns() + (i % columns()); + mElements[pos] = one; + } + } + + template <unsigned int Size> + static void setToIdentity(T(&matrix)[Size]) + { + static_assert(gl::iSquareRoot<Size>() != 0, "Matrix is not square."); + + const auto cols = gl::iSquareRoot<Size>(); + const auto one = T(1); + const auto zero = T(0); + + for (auto &e : matrix) + e = zero; + + for (unsigned int i = 0; i < cols; ++i) + { + const auto pos = i * cols + (i % cols); + matrix[pos] = one; + } + } + private: std::vector<T> mElements; unsigned int mRows; diff --git a/src/3rdparty/angle/src/common/platform.h b/src/3rdparty/angle/src/common/platform.h index be4cb94987..fb251da579 100644 --- a/src/3rdparty/angle/src/common/platform.h +++ b/src/3rdparty/angle/src/common/platform.h @@ -27,7 +27,9 @@ defined(__sun) || \ defined(__GLIBC__) || \ defined(__GNU__) || \ - defined(__QNX__) + defined(__QNX__) || \ + defined(__Fuchsia__) || \ + defined(__HAIKU__) # define ANGLE_PLATFORM_POSIX 1 #else # error Unsupported platform. @@ -57,28 +59,22 @@ # endif # if defined(ANGLE_ENABLE_D3D11) -# include <d3d10_1.h> -# include <d3d11.h> -# include <dxgi.h> -# if defined(__MINGW32__) && !defined(__d3d11sdklayers_h__) -# define ANGLE_MINGW32_COMPAT -# endif -# if defined(_MSC_VER) && _MSC_VER >= 1800 -# define ANGLE_ENABLE_D3D11_1 -# endif -# if defined(ANGLE_ENABLE_D3D11_1) -# include <d3d11_1.h> -# include <dxgi1_2.h> -# endif -# include <d3dcompiler.h> +#include <d3d10_1.h> +#include <d3d11.h> +#include <d3d11_3.h> +#include <d3dcompiler.h> +#include <dxgi.h> +#include <dxgi1_2.h> # endif +#if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11) +#include <wrl.h> +#endif + # if defined(ANGLE_ENABLE_WINDOWS_STORE) # include <dxgi1_3.h> # if defined(_DEBUG) -# if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP # include <DXProgrammableCapture.h> -# endif # include <dxgidebug.h> # endif # endif @@ -87,8 +83,21 @@ # undef far #endif -#if !defined(_M_ARM) && !defined(ANGLE_PLATFORM_ANDROID) -# define ANGLE_USE_SSE +#if defined(_MSC_VER) && !defined(_M_ARM) && !defined(_M_ARM64) +#include <intrin.h> +#define ANGLE_USE_SSE +#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) && !defined(__MINGW32__) +#include <x86intrin.h> +#define ANGLE_USE_SSE #endif +// Mips and arm devices need to include stddef for size_t. +#if defined(__mips__) || defined(__arm__) || defined(__aarch64__) +#include <stddef.h> +#endif + +// The MemoryBarrier function name collides with a macro under Windows +// We will undef the macro so that the function name does not get replaced +#undef MemoryBarrier + #endif // COMMON_PLATFORM_H_ diff --git a/src/3rdparty/angle/src/common/string_utils.cpp b/src/3rdparty/angle/src/common/string_utils.cpp index acb0376bf9..26f384bb2a 100644 --- a/src/3rdparty/angle/src/common/string_utils.cpp +++ b/src/3rdparty/angle/src/common/string_utils.cpp @@ -9,9 +9,14 @@ #include "string_utils.h" +#include <algorithm> +#include <stdlib.h> +#include <string.h> #include <fstream> #include <sstream> +#include "common/platform.h" + namespace angle { @@ -133,4 +138,76 @@ bool ReadFileToString(const std::string &path, std::string *stringOut) return !inFile.fail(); } +Optional<std::vector<wchar_t>> WidenString(size_t length, const char *cString) +{ + std::vector<wchar_t> wcstring(length + 1); +#if !defined(ANGLE_PLATFORM_WINDOWS) + size_t written = mbstowcs(wcstring.data(), cString, length + 1); + if (written == 0) + { + return Optional<std::vector<wchar_t>>::Invalid(); + } +#else + size_t convertedChars = 0; + errno_t err = mbstowcs_s(&convertedChars, wcstring.data(), length + 1, cString, _TRUNCATE); + if (err != 0) + { + return Optional<std::vector<wchar_t>>::Invalid(); + } +#endif + return Optional<std::vector<wchar_t>>(wcstring); +} + +bool BeginsWith(const std::string &str, const std::string &prefix) +{ + return strncmp(str.c_str(), prefix.c_str(), prefix.length()) == 0; +} + +bool BeginsWith(const std::string &str, const char *prefix) +{ + return strncmp(str.c_str(), prefix, strlen(prefix)) == 0; +} + +bool BeginsWith(const char *str, const char *prefix) +{ + return strncmp(str, prefix, strlen(prefix)) == 0; +} + +bool BeginsWith(const std::string &str, const std::string &prefix, const size_t prefixLength) +{ + return strncmp(str.c_str(), prefix.c_str(), prefixLength) == 0; +} + +bool EndsWith(const std::string &str, const char *suffix) +{ + const auto len = strlen(suffix); + if (len > str.size()) + return false; + + const char *end = str.c_str() + str.size() - len; + + return memcmp(end, suffix, len) == 0; +} + +void ToLower(std::string *str) +{ + for (auto &ch : *str) + { + ch = static_cast<char>(::tolower(ch)); + } +} + +bool ReplaceSubstring(std::string *str, + const std::string &substring, + const std::string &replacement) +{ + size_t replacePos = str->find(substring); + if (replacePos == std::string::npos) + { + return false; + } + str->replace(replacePos, substring.size(), replacement); + return true; } + +} // namespace angle diff --git a/src/3rdparty/angle/src/common/string_utils.h b/src/3rdparty/angle/src/common/string_utils.h index 131b17e086..07bf1ff81a 100644 --- a/src/3rdparty/angle/src/common/string_utils.h +++ b/src/3rdparty/angle/src/common/string_utils.h @@ -13,6 +13,8 @@ #include <string> #include <vector> +#include "common/Optional.h" + namespace angle { @@ -44,6 +46,40 @@ bool HexStringToUInt(const std::string &input, unsigned int *uintOut); bool ReadFileToString(const std::string &path, std::string *stringOut); -} +Optional<std::vector<wchar_t>> WidenString(size_t length, const char *cString); + +// Check if the string str begins with the given prefix. +// The comparison is case sensitive. +bool BeginsWith(const std::string &str, const std::string &prefix); + +// Check if the string str begins with the given prefix. +// Prefix may not be NULL and needs to be NULL terminated. +// The comparison is case sensitive. +bool BeginsWith(const std::string &str, const char *prefix); + +// Check if the string str begins with the given prefix. +// str and prefix may not be NULL and need to be NULL terminated. +// The comparison is case sensitive. +bool BeginsWith(const char *str, const char *prefix); + +// Check if the string str begins with the first prefixLength characters of the given prefix. +// The length of the prefix string should be greater than or equal to prefixLength. +// The comparison is case sensitive. +bool BeginsWith(const std::string &str, const std::string &prefix, const size_t prefixLength); + +// Check if the string str ends with the given suffix. +// Suffix may not be NUL and needs to be NULL terminated. +// The comparison is case sensitive. +bool EndsWith(const std::string& str, const char* suffix); + +// Convert to lower-case. +void ToLower(std::string *str); + +// Replaces the substring 'substring' in 'str' with 'replacement'. Returns true if successful. +bool ReplaceSubstring(std::string *str, + const std::string &substring, + const std::string &replacement); + +} // namespace angle #endif // LIBANGLE_STRING_UTILS_H_ diff --git a/src/3rdparty/angle/src/common/system_utils.h b/src/3rdparty/angle/src/common/system_utils.h new file mode 100644 index 0000000000..d61faa53fd --- /dev/null +++ b/src/3rdparty/angle/src/common/system_utils.h @@ -0,0 +1,27 @@ +// +// Copyright (c) 2014 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. +// + +// system_utils.h: declaration of OS-specific utility functions + +#ifndef COMMON_SYSTEM_UTILS_H_ +#define COMMON_SYSTEM_UTILS_H_ + +#include "common/angleutils.h" +#include "common/Optional.h" + +namespace angle +{ + +const char *GetExecutablePath(); +const char *GetExecutableDirectory(); +const char *GetSharedLibraryExtension(); +Optional<std::string> GetCWD(); +bool SetCWD(const char *dirName); +bool SetEnvironmentVar(const char *variableName, const char *value); + +} // namespace angle + +#endif // COMMON_SYSTEM_UTILS_H_ diff --git a/src/3rdparty/angle/src/common/system_utils_linux.cpp b/src/3rdparty/angle/src/common/system_utils_linux.cpp new file mode 100644 index 0000000000..98ab4cce6d --- /dev/null +++ b/src/3rdparty/angle/src/common/system_utils_linux.cpp @@ -0,0 +1,89 @@ +// +// Copyright (c) 2015 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. +// + +// system_utils_linux.cpp: Implementation of OS-specific functions for Linux + +#include "system_utils.h" + +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include <array> + +namespace angle +{ + +namespace +{ + +std::string GetExecutablePathImpl() +{ + // We cannot use lstat to get the size of /proc/self/exe as it always returns 0 + // so we just use a big buffer and hope the path fits in it. + char path[4096]; + + ssize_t result = readlink("/proc/self/exe", path, sizeof(path) - 1); + if (result < 0 || static_cast<size_t>(result) >= sizeof(path) - 1) + { + return ""; + } + + path[result] = '\0'; + return path; +} + +std::string GetExecutableDirectoryImpl() +{ + std::string executablePath = GetExecutablePath(); + size_t lastPathSepLoc = executablePath.find_last_of("/"); + return (lastPathSepLoc != std::string::npos) ? executablePath.substr(0, lastPathSepLoc) : ""; +} + +} // anonymous namespace + +const char *GetExecutablePath() +{ + // TODO(jmadill): Make global static string thread-safe. + const static std::string &exePath = GetExecutablePathImpl(); + return exePath.c_str(); +} + +const char *GetExecutableDirectory() +{ + // TODO(jmadill): Make global static string thread-safe. + const static std::string &exeDir = GetExecutableDirectoryImpl(); + return exeDir.c_str(); +} + +const char *GetSharedLibraryExtension() +{ + return "so"; +} + +Optional<std::string> GetCWD() +{ + std::array<char, 4096> pathBuf; + char *result = getcwd(pathBuf.data(), pathBuf.size()); + if (result == nullptr) + { + return Optional<std::string>::Invalid(); + } + return std::string(pathBuf.data()); +} + +bool SetCWD(const char *dirName) +{ + return (chdir(dirName) == 0); +} + +bool SetEnvironmentVar(const char *variableName, const char *value) +{ + return (setenv(variableName, value, 1) == 0); +} + +} // namespace angle diff --git a/src/3rdparty/angle/src/common/system_utils_mac.cpp b/src/3rdparty/angle/src/common/system_utils_mac.cpp new file mode 100644 index 0000000000..03b9185ab1 --- /dev/null +++ b/src/3rdparty/angle/src/common/system_utils_mac.cpp @@ -0,0 +1,94 @@ +// +// Copyright (c) 2015 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. +// + +// system_utils_osx.cpp: Implementation of OS-specific functions for OSX + +#include "system_utils.h" + +#include <unistd.h> + +#include <cstdlib> +#include <mach-o/dyld.h> +#include <vector> + +#include <array> + +namespace angle +{ + +namespace +{ + +std::string GetExecutablePathImpl() +{ + std::string result; + + uint32_t size = 0; + _NSGetExecutablePath(nullptr, &size); + + std::vector<char> buffer; + buffer.resize(size + 1); + + _NSGetExecutablePath(buffer.data(), &size); + buffer[size] = '\0'; + + if (!strrchr(buffer.data(), '/')) + { + return ""; + } + return buffer.data(); +} + +std::string GetExecutableDirectoryImpl() +{ + std::string executablePath = GetExecutablePath(); + size_t lastPathSepLoc = executablePath.find_last_of("/"); + return (lastPathSepLoc != std::string::npos) ? executablePath.substr(0, lastPathSepLoc) : ""; +} + +} // anonymous namespace + +const char *GetExecutablePath() +{ + // TODO(jmadill): Make global static string thread-safe. + const static std::string &exePath = GetExecutablePathImpl(); + return exePath.c_str(); +} + +const char *GetExecutableDirectory() +{ + // TODO(jmadill): Make global static string thread-safe. + const static std::string &exeDir = GetExecutableDirectoryImpl(); + return exeDir.c_str(); +} + +const char *GetSharedLibraryExtension() +{ + return "dylib"; +} + +Optional<std::string> GetCWD() +{ + std::array<char, 4096> pathBuf; + char *result = getcwd(pathBuf.data(), pathBuf.size()); + if (result == nullptr) + { + return Optional<std::string>::Invalid(); + } + return std::string(pathBuf.data()); +} + +bool SetCWD(const char *dirName) +{ + return (chdir(dirName) == 0); +} + +bool SetEnvironmentVar(const char *variableName, const char *value) +{ + return (setenv(variableName, value, 1) == 0); +} + +} // namespace angle diff --git a/src/3rdparty/angle/src/common/system_utils_win.cpp b/src/3rdparty/angle/src/common/system_utils_win.cpp new file mode 100644 index 0000000000..6bb2bfbd3f --- /dev/null +++ b/src/3rdparty/angle/src/common/system_utils_win.cpp @@ -0,0 +1,79 @@ +// +// Copyright (c) 2014 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. +// + +// system_utils_win.cpp: Implementation of OS-specific functions for Windows + +#include "system_utils.h" + +#include <stdarg.h> +#include <windows.h> +#include <array> +#include <vector> + +namespace angle +{ + +namespace +{ + +std::string GetExecutablePathImpl() +{ + std::array<char, MAX_PATH> executableFileBuf; + DWORD executablePathLen = GetModuleFileNameA(nullptr, executableFileBuf.data(), + static_cast<DWORD>(executableFileBuf.size())); + return (executablePathLen > 0 ? std::string(executableFileBuf.data()) : ""); +} + +std::string GetExecutableDirectoryImpl() +{ + std::string executablePath = GetExecutablePath(); + size_t lastPathSepLoc = executablePath.find_last_of("\\/"); + return (lastPathSepLoc != std::string::npos) ? executablePath.substr(0, lastPathSepLoc) : ""; +} + +} // anonymous namespace + +const char *GetExecutablePath() +{ + // TODO(jmadill): Make global static string thread-safe. + const static std::string &exePath = GetExecutablePathImpl(); + return exePath.c_str(); +} + +const char *GetExecutableDirectory() +{ + // TODO(jmadill): Make global static string thread-safe. + const static std::string &exeDir = GetExecutableDirectoryImpl(); + return exeDir.c_str(); +} + +const char *GetSharedLibraryExtension() +{ + return "dll"; +} + +Optional<std::string> GetCWD() +{ + std::array<char, MAX_PATH> pathBuf; + DWORD result = GetCurrentDirectoryA(static_cast<DWORD>(pathBuf.size()), pathBuf.data()); + if (result == 0) + { + return Optional<std::string>::Invalid(); + } + return std::string(pathBuf.data()); +} + +bool SetCWD(const char *dirName) +{ + return (SetCurrentDirectoryA(dirName) == TRUE); +} + +bool SetEnvironmentVar(const char *variableName, const char *value) +{ + return (SetEnvironmentVariableA(variableName, value) == TRUE); +} + +} // namespace angle diff --git a/src/3rdparty/angle/src/common/third_party/base/README.angle b/src/3rdparty/angle/src/common/third_party/base/README.angle new file mode 100644 index 0000000000..ca0943bc99 --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/base/README.angle @@ -0,0 +1,27 @@ +Name: Chromium base:: helper Classes +Short Name: base::numerics, base::MRUCachem, base::SHA1 +Version: +URL: https://chromium.googlesource.com/chromium/src/base/+/master +SOURCE CODE: Copy the Chromium folder manually into this folder and run git cl format. +Date: 24/05/2017 +Revision: 28b5bbb227d331c01e6ff9b2f8729732135aadc7 (Chromium) +Security Critical: no +License: Chromium +License File: LICENSE in Chromium/src + +Description: +base::numerics is a library for doing some simple safe math and conversions. +base::MRUCache is a few collections of most-recently-used caching structures. +base::SHA1 is a secure hashing algorithm. + +To update the checkout, simply overwrite the folder with Chromium's latest, apply +the appropriate namespace, and make sure the paths are correct (anglebase/ instead of +base/), and update the header guards and macros. + +Modifications: + +- the file scope is now anglebase/ from base/ to prevent include conflicts. +- anglebase/logging.h defines (D)CHECK to be ASSERT to be compatible with ANGLE. +- the headers use namespace angle::base instead of base:: to avoid ODR + violations when ANGLE code is mixed with Chromium code. +- header guards and macros are changed from BASE to ANGLEBASE to prevent conflicts. diff --git a/src/3rdparty/angle/src/common/third_party/base/anglebase/base_export.h b/src/3rdparty/angle/src/common/third_party/base/anglebase/base_export.h new file mode 100644 index 0000000000..1af5485336 --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/base/anglebase/base_export.h @@ -0,0 +1,13 @@ +// +// Copyright 2017 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. +// +// base_export.h: Compatiblity hacks for importing Chromium's base/SHA1. + +#ifndef ANGLEBASE_BASE_EXPORT_H_ +#define ANGLEBASE_BASE_EXPORT_H_ + +#define ANGLEBASE_EXPORT + +#endif // ANGLEBASE_BASE_EXPORT_H_
\ No newline at end of file diff --git a/src/3rdparty/angle/src/common/third_party/base/anglebase/containers/mru_cache.h b/src/3rdparty/angle/src/common/third_party/base/anglebase/containers/mru_cache.h new file mode 100644 index 0000000000..fe4fec5768 --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/base/anglebase/containers/mru_cache.h @@ -0,0 +1,275 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains a template for a Most Recently Used cache that allows +// constant-time access to items using a key, but easy identification of the +// least-recently-used items for removal. Each key can only be associated with +// one payload item at a time. +// +// The key object will be stored twice, so it should support efficient copying. +// +// NOTE: While all operations are O(1), this code is written for +// legibility rather than optimality. If future profiling identifies this as +// a bottleneck, there is room for smaller values of 1 in the O(1). :] + +#ifndef ANGLEBASE_CONTAINERS_MRU_CACHE_H_ +#define ANGLEBASE_CONTAINERS_MRU_CACHE_H_ + +#include <stddef.h> + +#include <algorithm> +#include <functional> +#include <list> +#include <map> +#include <unordered_map> +#include <utility> + +#include "anglebase/logging.h" +#include "anglebase/macros.h" + +namespace angle +{ + +namespace base +{ + +// MRUCacheBase ---------------------------------------------------------------- + +// This template is used to standardize map type containers that can be used +// by MRUCacheBase. This level of indirection is necessary because of the way +// that template template params and default template params interact. +template <class KeyType, class ValueType, class CompareType> +struct MRUCacheStandardMap +{ + typedef std::map<KeyType, ValueType, CompareType> Type; +}; + +// Base class for the MRU cache specializations defined below. +template <class KeyType, + class PayloadType, + class HashOrCompareType, + template <typename, typename, typename> class MapType = MRUCacheStandardMap> +class MRUCacheBase +{ + public: + // The payload of the list. This maintains a copy of the key so we can + // efficiently delete things given an element of the list. + typedef std::pair<KeyType, PayloadType> value_type; + + private: + typedef std::list<value_type> PayloadList; + typedef + typename MapType<KeyType, typename PayloadList::iterator, HashOrCompareType>::Type KeyIndex; + + public: + typedef typename PayloadList::size_type size_type; + + typedef typename PayloadList::iterator iterator; + typedef typename PayloadList::const_iterator const_iterator; + typedef typename PayloadList::reverse_iterator reverse_iterator; + typedef typename PayloadList::const_reverse_iterator const_reverse_iterator; + + enum + { + NO_AUTO_EVICT = 0 + }; + + // The max_size is the size at which the cache will prune its members to when + // a new item is inserted. If the caller wants to manager this itself (for + // example, maybe it has special work to do when something is evicted), it + // can pass NO_AUTO_EVICT to not restrict the cache size. + explicit MRUCacheBase(size_type max_size) : max_size_(max_size) {} + + virtual ~MRUCacheBase() {} + + size_type max_size() const { return max_size_; } + + // Inserts a payload item with the given key. If an existing item has + // the same key, it is removed prior to insertion. An iterator indicating the + // inserted item will be returned (this will always be the front of the list). + // + // The payload will be forwarded. + template <typename Payload> + iterator Put(const KeyType &key, Payload &&payload) + { + // Remove any existing payload with that key. + typename KeyIndex::iterator index_iter = index_.find(key); + if (index_iter != index_.end()) + { + // Erase the reference to it. The index reference will be replaced in the + // code below. + Erase(index_iter->second); + } + else if (max_size_ != NO_AUTO_EVICT) + { + // New item is being inserted which might make it larger than the maximum + // size: kick the oldest thing out if necessary. + ShrinkToSize(max_size_ - 1); + } + + ordering_.emplace_front(key, std::forward<Payload>(payload)); + index_.emplace(key, ordering_.begin()); + return ordering_.begin(); + } + + // Retrieves the contents of the given key, or end() if not found. This method + // has the side effect of moving the requested item to the front of the + // recency list. + iterator Get(const KeyType &key) + { + typename KeyIndex::iterator index_iter = index_.find(key); + if (index_iter == index_.end()) + return end(); + typename PayloadList::iterator iter = index_iter->second; + + // Move the touched item to the front of the recency ordering. + ordering_.splice(ordering_.begin(), ordering_, iter); + return ordering_.begin(); + } + + // Retrieves the payload associated with a given key and returns it via + // result without affecting the ordering (unlike Get). + iterator Peek(const KeyType &key) + { + typename KeyIndex::const_iterator index_iter = index_.find(key); + if (index_iter == index_.end()) + return end(); + return index_iter->second; + } + + const_iterator Peek(const KeyType &key) const + { + typename KeyIndex::const_iterator index_iter = index_.find(key); + if (index_iter == index_.end()) + return end(); + return index_iter->second; + } + + // Exchanges the contents of |this| by the contents of the |other|. + void Swap(MRUCacheBase &other) + { + ordering_.swap(other.ordering_); + index_.swap(other.index_); + std::swap(max_size_, other.max_size_); + } + + // Erases the item referenced by the given iterator. An iterator to the item + // following it will be returned. The iterator must be valid. + iterator Erase(iterator pos) + { + index_.erase(pos->first); + return ordering_.erase(pos); + } + + // MRUCache entries are often processed in reverse order, so we add this + // convenience function (not typically defined by STL containers). + reverse_iterator Erase(reverse_iterator pos) + { + // We have to actually give it the incremented iterator to delete, since + // the forward iterator that base() returns is actually one past the item + // being iterated over. + return reverse_iterator(Erase((++pos).base())); + } + + // Shrinks the cache so it only holds |new_size| items. If |new_size| is + // bigger or equal to the current number of items, this will do nothing. + void ShrinkToSize(size_type new_size) + { + for (size_type i = size(); i > new_size; i--) + Erase(rbegin()); + } + + // Deletes everything from the cache. + void Clear() + { + index_.clear(); + ordering_.clear(); + } + + // Returns the number of elements in the cache. + size_type size() const + { + // We don't use ordering_.size() for the return value because + // (as a linked list) it can be O(n). + DCHECK(index_.size() == ordering_.size()); + return index_.size(); + } + + // Allows iteration over the list. Forward iteration starts with the most + // recent item and works backwards. + // + // Note that since these iterators are actually iterators over a list, you + // can keep them as you insert or delete things (as long as you don't delete + // the one you are pointing to) and they will still be valid. + iterator begin() { return ordering_.begin(); } + const_iterator begin() const { return ordering_.begin(); } + iterator end() { return ordering_.end(); } + const_iterator end() const { return ordering_.end(); } + + reverse_iterator rbegin() { return ordering_.rbegin(); } + const_reverse_iterator rbegin() const { return ordering_.rbegin(); } + reverse_iterator rend() { return ordering_.rend(); } + const_reverse_iterator rend() const { return ordering_.rend(); } + + bool empty() const { return ordering_.empty(); } + + private: + PayloadList ordering_; + KeyIndex index_; + + size_type max_size_; + + DISALLOW_COPY_AND_ASSIGN(MRUCacheBase); +}; + +// MRUCache -------------------------------------------------------------------- + +// A container that does not do anything to free its data. Use this when storing +// value types (as opposed to pointers) in the list. +template <class KeyType, class PayloadType, class CompareType = std::less<KeyType>> +class MRUCache : public MRUCacheBase<KeyType, PayloadType, CompareType> +{ + private: + using ParentType = MRUCacheBase<KeyType, PayloadType, CompareType>; + + public: + // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT. + explicit MRUCache(typename ParentType::size_type max_size) : ParentType(max_size) {} + virtual ~MRUCache() {} + + private: + DISALLOW_COPY_AND_ASSIGN(MRUCache); +}; + +// HashingMRUCache ------------------------------------------------------------ + +template <class KeyType, class ValueType, class HashType> +struct MRUCacheHashMap +{ + typedef std::unordered_map<KeyType, ValueType, HashType> Type; +}; + +// This class is similar to MRUCache, except that it uses std::unordered_map as +// the map type instead of std::map. Note that your KeyType must be hashable to +// use this cache or you need to provide a hashing class. +template <class KeyType, class PayloadType, class HashType = std::hash<KeyType>> +class HashingMRUCache : public MRUCacheBase<KeyType, PayloadType, HashType, MRUCacheHashMap> +{ + private: + using ParentType = MRUCacheBase<KeyType, PayloadType, HashType, MRUCacheHashMap>; + + public: + // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT. + explicit HashingMRUCache(typename ParentType::size_type max_size) : ParentType(max_size) {} + virtual ~HashingMRUCache() {} + + private: + DISALLOW_COPY_AND_ASSIGN(HashingMRUCache); +}; + +} // namespace base + +} // namespace angle + +#endif // ANGLEBASE_CONTAINERS_MRU_CACHE_H_ diff --git a/src/3rdparty/angle/src/common/third_party/base/anglebase/logging.h b/src/3rdparty/angle/src/common/third_party/base/anglebase/logging.h new file mode 100644 index 0000000000..85ad82b47d --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/base/anglebase/logging.h @@ -0,0 +1,26 @@ +// +// Copyright 2016 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. +// +// logging.h: Compatiblity hacks for importing Chromium's base/numerics. + +#ifndef ANGLEBASE_LOGGING_H_ +#define ANGLEBASE_LOGGING_H_ + +#include "common/debug.h" + +#ifndef DCHECK +#define DCHECK(X) ASSERT(X) +#endif + +#ifndef CHECK +#define CHECK(X) ASSERT(X) +#endif + +// Unfortunately ANGLE relies on ASSERT being an empty statement, which these libs don't respect. +#ifndef NOTREACHED +#define NOTREACHED() UNREACHABLE() +#endif + +#endif // ANGLEBASE_LOGGING_H_ diff --git a/src/3rdparty/angle/src/common/third_party/base/anglebase/macros.h b/src/3rdparty/angle/src/common/third_party/base/anglebase/macros.h new file mode 100644 index 0000000000..06391784e4 --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/base/anglebase/macros.h @@ -0,0 +1,17 @@ +// +// Copyright 2017 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. +// +// macros.h: Compatiblity hacks for importing Chromium's MRUCache. + +#ifndef ANGLEBASE_MACROS_H_ +#define ANGLEBASE_MACROS_H_ + +// A macro to disallow the copy constructor and operator= functions. +// This should be used in the private: declarations for a class. +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName &) = delete; \ + void operator=(const TypeName &) = delete + +#endif // ANGLEBASE_MACROS_H_ diff --git a/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/OWNERS b/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/OWNERS new file mode 100644 index 0000000000..41f35fc79b --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/OWNERS @@ -0,0 +1,3 @@ +jschuh@chromium.org +tsepez@chromium.org + diff --git a/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_conversions.h b/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_conversions.h new file mode 100644 index 0000000000..43babc31a8 --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_conversions.h @@ -0,0 +1,179 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ANGLEBASE_NUMERICS_SAFE_CONVERSIONS_H_ +#define ANGLEBASE_NUMERICS_SAFE_CONVERSIONS_H_ + +#include <stddef.h> + +#include <limits> +#include <type_traits> + +#include "anglebase/logging.h" +#include "anglebase/numerics/safe_conversions_impl.h" + +namespace angle +{ + +namespace base +{ + +// Convenience function that returns true if the supplied value is in range +// for the destination type. +template <typename Dst, typename Src> +constexpr bool IsValueInRangeForNumericType(Src value) +{ + return internal::DstRangeRelationToSrcRange<Dst>(value) == internal::RANGE_VALID; +} + +// Convenience function for determining if a numeric value is negative without +// throwing compiler warnings on: unsigned(value) < 0. +template <typename T> +constexpr typename std::enable_if<std::numeric_limits<T>::is_signed, bool>::type IsValueNegative( + T value) +{ + static_assert(std::numeric_limits<T>::is_specialized, "Argument must be numeric."); + return value < 0; +} + +template <typename T> +constexpr typename std::enable_if<!std::numeric_limits<T>::is_signed, bool>::type IsValueNegative(T) +{ + static_assert(std::numeric_limits<T>::is_specialized, "Argument must be numeric."); + return false; +} + +// checked_cast<> is analogous to static_cast<> for numeric types, +// except that it CHECKs that the specified numeric conversion will not +// overflow or underflow. NaN source will always trigger a CHECK. +template <typename Dst, typename Src> +inline Dst checked_cast(Src value) +{ + CHECK(IsValueInRangeForNumericType<Dst>(value)); + return static_cast<Dst>(value); +} + +// HandleNaN will cause this class to CHECK(false). +struct SaturatedCastNaNBehaviorCheck +{ + template <typename T> + static T HandleNaN() + { + CHECK(false); + return T(); + } +}; + +// HandleNaN will return 0 in this case. +struct SaturatedCastNaNBehaviorReturnZero +{ + template <typename T> + static constexpr T HandleNaN() + { + return T(); + } +}; + +namespace internal +{ +// This wrapper is used for C++11 constexpr support by avoiding the declaration +// of local variables in the saturated_cast template function. +template <typename Dst, class NaNHandler, typename Src> +constexpr Dst saturated_cast_impl(const Src value, const RangeConstraint constraint) +{ + return constraint == RANGE_VALID + ? static_cast<Dst>(value) + : (constraint == RANGE_UNDERFLOW + ? std::numeric_limits<Dst>::min() + : (constraint == RANGE_OVERFLOW + ? std::numeric_limits<Dst>::max() + : (constraint == RANGE_INVALID + ? NaNHandler::template HandleNaN<Dst>() + : (NOTREACHED(), static_cast<Dst>(value))))); +} +} // namespace internal + +// saturated_cast<> is analogous to static_cast<> for numeric types, except +// that the specified numeric conversion will saturate rather than overflow or +// underflow. NaN assignment to an integral will defer the behavior to a +// specified class. By default, it will return 0. +template <typename Dst, class NaNHandler = SaturatedCastNaNBehaviorReturnZero, typename Src> +constexpr Dst saturated_cast(Src value) +{ + return std::numeric_limits<Dst>::is_iec559 + ? static_cast<Dst>(value) // Floating point optimization. + : internal::saturated_cast_impl<Dst, NaNHandler>( + value, internal::DstRangeRelationToSrcRange<Dst>(value)); +} + +// strict_cast<> is analogous to static_cast<> for numeric types, except that +// it will cause a compile failure if the destination type is not large enough +// to contain any value in the source type. It performs no runtime checking. +template <typename Dst, typename Src> +constexpr Dst strict_cast(Src value) +{ + static_assert(std::numeric_limits<Src>::is_specialized, "Argument must be numeric."); + static_assert(std::numeric_limits<Dst>::is_specialized, "Result must be numeric."); + static_assert((internal::StaticDstRangeRelationToSrcRange<Dst, Src>::value == + internal::NUMERIC_RANGE_CONTAINED), + "The numeric conversion is out of range for this type. You " + "should probably use one of the following conversion " + "mechanisms on the value you want to pass:\n" + "- base::checked_cast\n" + "- base::saturated_cast\n" + "- base::CheckedNumeric"); + + return static_cast<Dst>(value); +} + +// StrictNumeric implements compile time range checking between numeric types by +// wrapping assignment operations in a strict_cast. This class is intended to be +// used for function arguments and return types, to ensure the destination type +// can always contain the source type. This is essentially the same as enforcing +// -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied +// incrementally at API boundaries, making it easier to convert code so that it +// compiles cleanly with truncation warnings enabled. +// This template should introduce no runtime overhead, but it also provides no +// runtime checking of any of the associated mathematical operations. Use +// CheckedNumeric for runtime range checks of the actual value being assigned. +template <typename T> +class StrictNumeric +{ + public: + typedef T type; + + constexpr StrictNumeric() : value_(0) {} + + // Copy constructor. + template <typename Src> + constexpr StrictNumeric(const StrictNumeric<Src> &rhs) : value_(strict_cast<T>(rhs.value_)) + { + } + + // This is not an explicit constructor because we implicitly upgrade regular + // numerics to StrictNumerics to make them easier to use. + template <typename Src> + constexpr StrictNumeric(Src value) : value_(strict_cast<T>(value)) + { + } + + // The numeric cast operator basically handles all the magic. + template <typename Dst> + constexpr operator Dst() const + { + return strict_cast<Dst>(value_); + } + + private: + const T value_; +}; + +// Explicitly make a shorter size_t typedef for convenience. +typedef StrictNumeric<size_t> SizeT; + +} // namespace base + +} // namespace angle + +#endif // ANGLEBASE_NUMERICS_SAFE_CONVERSIONS_H_ diff --git a/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_conversions_impl.h b/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_conversions_impl.h new file mode 100644 index 0000000000..10ed6c7a7f --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_conversions_impl.h @@ -0,0 +1,274 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ANGLEBASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ +#define ANGLEBASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ + +#include <limits.h> +#include <stdint.h> + +#include <climits> +#include <limits> + +namespace angle +{ + +namespace base +{ +namespace internal +{ + +// The std library doesn't provide a binary max_exponent for integers, however +// we can compute one by adding one to the number of non-sign bits. This allows +// for accurate range comparisons between floating point and integer types. +template <typename NumericType> +struct MaxExponent +{ + static_assert(std::is_arithmetic<NumericType>::value, "Argument must be numeric."); + static const int value = + std::numeric_limits<NumericType>::is_iec559 + ? std::numeric_limits<NumericType>::max_exponent + : (sizeof(NumericType) * CHAR_BIT + 1 - std::numeric_limits<NumericType>::is_signed); +}; + +enum IntegerRepresentation +{ + INTEGER_REPRESENTATION_UNSIGNED, + INTEGER_REPRESENTATION_SIGNED +}; + +// A range for a given nunmeric Src type is contained for a given numeric Dst +// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and +// numeric_limits<Src>::min() >= numeric_limits<Dst>::min() are true. +// We implement this as template specializations rather than simple static +// comparisons to ensure type correctness in our comparisons. +enum NumericRangeRepresentation +{ + NUMERIC_RANGE_NOT_CONTAINED, + NUMERIC_RANGE_CONTAINED +}; + +// Helper templates to statically determine if our destination type can contain +// maximum and minimum values represented by the source type. + +template <typename Dst, + typename Src, + IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED, + IntegerRepresentation SrcSign = std::numeric_limits<Src>::is_signed + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED> +struct StaticDstRangeRelationToSrcRange; + +// Same sign: Dst is guaranteed to contain Src only if its range is equal or +// larger. +template <typename Dst, typename Src, IntegerRepresentation Sign> +struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> +{ + static const NumericRangeRepresentation value = + MaxExponent<Dst>::value >= MaxExponent<Src>::value ? NUMERIC_RANGE_CONTAINED + : NUMERIC_RANGE_NOT_CONTAINED; +}; + +// Unsigned to signed: Dst is guaranteed to contain source only if its range is +// larger. +template <typename Dst, typename Src> +struct StaticDstRangeRelationToSrcRange<Dst, + Src, + INTEGER_REPRESENTATION_SIGNED, + INTEGER_REPRESENTATION_UNSIGNED> +{ + static const NumericRangeRepresentation value = + MaxExponent<Dst>::value > MaxExponent<Src>::value ? NUMERIC_RANGE_CONTAINED + : NUMERIC_RANGE_NOT_CONTAINED; +}; + +// Signed to unsigned: Dst cannot be statically determined to contain Src. +template <typename Dst, typename Src> +struct StaticDstRangeRelationToSrcRange<Dst, + Src, + INTEGER_REPRESENTATION_UNSIGNED, + INTEGER_REPRESENTATION_SIGNED> +{ + static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; +}; + +enum RangeConstraint : unsigned char +{ + RANGE_VALID = 0x0, // Value can be represented by the destination type. + RANGE_UNDERFLOW = 0x1, // Value would overflow. + RANGE_OVERFLOW = 0x2, // Value would underflow. + RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN). +}; + +// Helper function for coercing an int back to a RangeContraint. +constexpr RangeConstraint GetRangeConstraint(int integer_range_constraint) +{ + // TODO(jschuh): Once we get full C++14 support we want this + // assert(integer_range_constraint >= RANGE_VALID && + // integer_range_constraint <= RANGE_INVALID) + return static_cast<RangeConstraint>(integer_range_constraint); +} + +// This function creates a RangeConstraint from an upper and lower bound +// check by taking advantage of the fact that only NaN can be out of range in +// both directions at once. +constexpr inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound, bool is_in_lower_bound) +{ + return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) | + (is_in_lower_bound ? 0 : RANGE_UNDERFLOW)); +} + +// The following helper template addresses a corner case in range checks for +// conversion from a floating-point type to an integral type of smaller range +// but larger precision (e.g. float -> unsigned). The problem is as follows: +// 1. Integral maximum is always one less than a power of two, so it must be +// truncated to fit the mantissa of the floating point. The direction of +// rounding is implementation defined, but by default it's always IEEE +// floats, which round to nearest and thus result in a value of larger +// magnitude than the integral value. +// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX +// // is 4294967295u. +// 2. If the floating point value is equal to the promoted integral maximum +// value, a range check will erroneously pass. +// Example: (4294967296f <= 4294967295u) // This is true due to a precision +// // loss in rounding up to float. +// 3. When the floating point value is then converted to an integral, the +// resulting value is out of range for the target integral type and +// thus is implementation defined. +// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0. +// To fix this bug we manually truncate the maximum value when the destination +// type is an integral of larger precision than the source floating-point type, +// such that the resulting maximum is represented exactly as a floating point. +template <typename Dst, typename Src> +struct NarrowingRange +{ + typedef typename std::numeric_limits<Src> SrcLimits; + typedef typename std::numeric_limits<Dst> DstLimits; + // The following logic avoids warnings where the max function is + // instantiated with invalid values for a bit shift (even though + // such a function can never be called). + static const int shift = (MaxExponent<Src>::value > MaxExponent<Dst>::value && + SrcLimits::digits < DstLimits::digits && + SrcLimits::is_iec559 && + DstLimits::is_integer) + ? (DstLimits::digits - SrcLimits::digits) + : 0; + + static constexpr Dst max() + { + // We use UINTMAX_C below to avoid compiler warnings about shifting floating + // points. Since it's a compile time calculation, it shouldn't have any + // performance impact. + return DstLimits::max() - static_cast<Dst>((UINTMAX_C(1) << shift) - 1); + } + + static constexpr Dst min() + { + return std::numeric_limits<Dst>::is_iec559 ? -DstLimits::max() : DstLimits::min(); + } +}; + +template <typename Dst, + typename Src, + IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED, + IntegerRepresentation SrcSign = std::numeric_limits<Src>::is_signed + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED, + NumericRangeRepresentation DstRange = StaticDstRangeRelationToSrcRange<Dst, Src>::value> +struct DstRangeRelationToSrcRangeImpl; + +// The following templates are for ranges that must be verified at runtime. We +// split it into checks based on signedness to avoid confusing casts and +// compiler warnings on signed an unsigned comparisons. + +// Dst range is statically determined to contain Src: Nothing to check. +template <typename Dst, typename Src, IntegerRepresentation DstSign, IntegerRepresentation SrcSign> +struct DstRangeRelationToSrcRangeImpl<Dst, Src, DstSign, SrcSign, NUMERIC_RANGE_CONTAINED> +{ + static constexpr RangeConstraint Check(Src value) { return RANGE_VALID; } +}; + +// Signed to signed narrowing: Both the upper and lower boundaries may be +// exceeded. +template <typename Dst, typename Src> +struct DstRangeRelationToSrcRangeImpl<Dst, + Src, + INTEGER_REPRESENTATION_SIGNED, + INTEGER_REPRESENTATION_SIGNED, + NUMERIC_RANGE_NOT_CONTAINED> +{ + static constexpr RangeConstraint Check(Src value) + { + return GetRangeConstraint((value <= NarrowingRange<Dst, Src>::max()), + (value >= NarrowingRange<Dst, Src>::min())); + } +}; + +// Unsigned to unsigned narrowing: Only the upper boundary can be exceeded. +template <typename Dst, typename Src> +struct DstRangeRelationToSrcRangeImpl<Dst, + Src, + INTEGER_REPRESENTATION_UNSIGNED, + INTEGER_REPRESENTATION_UNSIGNED, + NUMERIC_RANGE_NOT_CONTAINED> +{ + static constexpr RangeConstraint Check(Src value) + { + return GetRangeConstraint(value <= NarrowingRange<Dst, Src>::max(), true); + } +}; + +// Unsigned to signed: The upper boundary may be exceeded. +template <typename Dst, typename Src> +struct DstRangeRelationToSrcRangeImpl<Dst, + Src, + INTEGER_REPRESENTATION_SIGNED, + INTEGER_REPRESENTATION_UNSIGNED, + NUMERIC_RANGE_NOT_CONTAINED> +{ + static constexpr RangeConstraint Check(Src value) + { + return sizeof(Dst) > sizeof(Src) + ? RANGE_VALID + : GetRangeConstraint(value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()), + true); + } +}; + +// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, +// and any negative value exceeds the lower boundary. +template <typename Dst, typename Src> +struct DstRangeRelationToSrcRangeImpl<Dst, + Src, + INTEGER_REPRESENTATION_UNSIGNED, + INTEGER_REPRESENTATION_SIGNED, + NUMERIC_RANGE_NOT_CONTAINED> +{ + static constexpr RangeConstraint Check(Src value) + { + return (MaxExponent<Dst>::value >= MaxExponent<Src>::value) + ? GetRangeConstraint(true, value >= static_cast<Src>(0)) + : GetRangeConstraint(value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()), + value >= static_cast<Src>(0)); + } +}; + +template <typename Dst, typename Src> +constexpr RangeConstraint DstRangeRelationToSrcRange(Src value) +{ + static_assert(std::numeric_limits<Src>::is_specialized, "Argument must be numeric."); + static_assert(std::numeric_limits<Dst>::is_specialized, "Result must be numeric."); + return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value); +} + +} // namespace internal +} // namespace base + +} // namespace angle + +#endif // ANGLEBASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ diff --git a/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_math.h b/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_math.h new file mode 100644 index 0000000000..3af4db63f7 --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_math.h @@ -0,0 +1,329 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ANGLEBASE_NUMERICS_SAFE_MATH_H_ +#define ANGLEBASE_NUMERICS_SAFE_MATH_H_ + +#include <stddef.h> + +#include <limits> +#include <type_traits> + +#include "anglebase/logging.h" +#include "anglebase/numerics/safe_math_impl.h" + +namespace angle +{ + +namespace base +{ + +namespace internal +{ + +// CheckedNumeric implements all the logic and operators for detecting integer +// boundary conditions such as overflow, underflow, and invalid conversions. +// The CheckedNumeric type implicitly converts from floating point and integer +// data types, and contains overloads for basic arithmetic operations (i.e.: +, +// -, *, /, %). +// +// The following methods convert from CheckedNumeric to standard numeric values: +// IsValid() - Returns true if the underlying numeric value is valid (i.e. has +// has not wrapped and is not the result of an invalid conversion). +// ValueOrDie() - Returns the underlying value. If the state is not valid this +// call will crash on a CHECK. +// ValueOrDefault() - Returns the current value, or the supplied default if the +// state is not valid. +// ValueFloating() - Returns the underlying floating point value (valid only +// only for floating point CheckedNumeric types). +// +// Bitwise operations are explicitly not supported, because correct +// handling of some cases (e.g. sign manipulation) is ambiguous. Comparison +// operations are explicitly not supported because they could result in a crash +// on a CHECK condition. You should use patterns like the following for these +// operations: +// Bitwise operation: +// CheckedNumeric<int> checked_int = untrusted_input_value; +// int x = checked_int.ValueOrDefault(0) | kFlagValues; +// Comparison: +// CheckedNumeric<size_t> checked_size = untrusted_input_value; +// checked_size += HEADER LENGTH; +// if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size) +// Do stuff... +template <typename T> +class CheckedNumeric +{ + static_assert(std::is_arithmetic<T>::value, "CheckedNumeric<T>: T must be a numeric type."); + + public: + typedef T type; + + CheckedNumeric() {} + + // Copy constructor. + template <typename Src> + CheckedNumeric(const CheckedNumeric<Src> &rhs) : state_(rhs.ValueUnsafe(), rhs.validity()) + { + } + + template <typename Src> + CheckedNumeric(Src value, RangeConstraint validity) : state_(value, validity) + { + } + + // This is not an explicit constructor because we implicitly upgrade regular + // numerics to CheckedNumerics to make them easier to use. + template <typename Src> + CheckedNumeric(Src value) // NOLINT(runtime/explicit) + : state_(value) + { + static_assert(std::numeric_limits<Src>::is_specialized, "Argument must be numeric."); + } + + // This is not an explicit constructor because we want a seamless conversion + // from StrictNumeric types. + template <typename Src> + CheckedNumeric(StrictNumeric<Src> value) // NOLINT(runtime/explicit) + : state_(static_cast<Src>(value)) + { + } + + // IsValid() is the public API to test if a CheckedNumeric is currently valid. + bool IsValid() const { return validity() == RANGE_VALID; } + + // ValueOrDie() The primary accessor for the underlying value. If the current + // state is not valid it will CHECK and crash. + T ValueOrDie() const + { + CHECK(IsValid()); + return state_.value(); + } + + // ValueOrDefault(T default_value) A convenience method that returns the + // current value if the state is valid, and the supplied default_value for + // any other state. + T ValueOrDefault(T default_value) const { return IsValid() ? state_.value() : default_value; } + + // ValueFloating() - Since floating point values include their validity state, + // we provide an easy method for extracting them directly, without a risk of + // crashing on a CHECK. + T ValueFloating() const + { + static_assert(std::numeric_limits<T>::is_iec559, "Argument must be float."); + return CheckedNumeric<T>::cast(*this).ValueUnsafe(); + } + + // validity() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now for + // tests and to avoid a big matrix of friend operator overloads. But the + // values it returns are likely to change in the future. + // Returns: current validity state (i.e. valid, overflow, underflow, nan). + // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for + // saturation/wrapping so we can expose this state consistently and implement + // saturated arithmetic. + RangeConstraint validity() const { return state_.validity(); } + + // ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now + // for tests and to avoid a big matrix of friend operator overloads. But the + // values it returns are likely to change in the future. + // Returns: the raw numeric value, regardless of the current state. + // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for + // saturation/wrapping so we can expose this state consistently and implement + // saturated arithmetic. + T ValueUnsafe() const { return state_.value(); } + + // Prototypes for the supported arithmetic operator overloads. + template <typename Src> + CheckedNumeric &operator+=(Src rhs); + template <typename Src> + CheckedNumeric &operator-=(Src rhs); + template <typename Src> + CheckedNumeric &operator*=(Src rhs); + template <typename Src> + CheckedNumeric &operator/=(Src rhs); + template <typename Src> + CheckedNumeric &operator%=(Src rhs); + + CheckedNumeric operator-() const + { + RangeConstraint validity; + T value = CheckedNeg(state_.value(), &validity); + // Negation is always valid for floating point. + if (std::numeric_limits<T>::is_iec559) + return CheckedNumeric<T>(value); + + validity = GetRangeConstraint(state_.validity() | validity); + return CheckedNumeric<T>(value, validity); + } + + CheckedNumeric Abs() const + { + RangeConstraint validity; + T value = CheckedAbs(state_.value(), &validity); + // Absolute value is always valid for floating point. + if (std::numeric_limits<T>::is_iec559) + return CheckedNumeric<T>(value); + + validity = GetRangeConstraint(state_.validity() | validity); + return CheckedNumeric<T>(value, validity); + } + + // This function is available only for integral types. It returns an unsigned + // integer of the same width as the source type, containing the absolute value + // of the source, and properly handling signed min. + CheckedNumeric<typename UnsignedOrFloatForSize<T>::type> UnsignedAbs() const + { + return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>( + CheckedUnsignedAbs(state_.value()), state_.validity()); + } + + CheckedNumeric &operator++() + { + *this += 1; + return *this; + } + + CheckedNumeric operator++(int) + { + CheckedNumeric value = *this; + *this += 1; + return value; + } + + CheckedNumeric &operator--() + { + *this -= 1; + return *this; + } + + CheckedNumeric operator--(int) + { + CheckedNumeric value = *this; + *this -= 1; + return value; + } + + // These static methods behave like a convenience cast operator targeting + // the desired CheckedNumeric type. As an optimization, a reference is + // returned when Src is the same type as T. + template <typename Src> + static CheckedNumeric<T> cast( + Src u, + typename std::enable_if<std::numeric_limits<Src>::is_specialized, int>::type = 0) + { + return u; + } + + template <typename Src> + static CheckedNumeric<T> cast( + const CheckedNumeric<Src> &u, + typename std::enable_if<!std::is_same<Src, T>::value, int>::type = 0) + { + return u; + } + + static const CheckedNumeric<T> &cast(const CheckedNumeric<T> &u) { return u; } + + private: + template <typename NumericType> + struct UnderlyingType + { + using type = NumericType; + }; + + template <typename NumericType> + struct UnderlyingType<CheckedNumeric<NumericType>> + { + using type = NumericType; + }; + + CheckedNumericState<T> state_; +}; + +// This is the boilerplate for the standard arithmetic operator overloads. A +// macro isn't the prettiest solution, but it beats rewriting these five times. +// Some details worth noting are: +// * We apply the standard arithmetic promotions. +// * We skip range checks for floating points. +// * We skip range checks for destination integers with sufficient range. +// TODO(jschuh): extract these out into templates. +#define ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \ + /* Binary arithmetic operator for CheckedNumerics of the same type. */ \ + template <typename T> \ + CheckedNumeric<typename ArithmeticPromotion<T>::type> operator OP( \ + const CheckedNumeric<T> &lhs, const CheckedNumeric<T> &rhs) \ + { \ + typedef typename ArithmeticPromotion<T>::type Promotion; \ + /* Floating point always takes the fast path */ \ + if (std::numeric_limits<T>::is_iec559) \ + return CheckedNumeric<T>(lhs.ValueUnsafe() OP rhs.ValueUnsafe()); \ + if (IsIntegerArithmeticSafe<Promotion, T, T>::value) \ + return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \ + GetRangeConstraint(rhs.validity() | lhs.validity())); \ + RangeConstraint validity = RANGE_VALID; \ + T result = \ + static_cast<T>(Checked##NAME(static_cast<Promotion>(lhs.ValueUnsafe()), \ + static_cast<Promotion>(rhs.ValueUnsafe()), &validity)); \ + return CheckedNumeric<Promotion>( \ + result, GetRangeConstraint(validity | lhs.validity() | rhs.validity())); \ + } \ + /* Assignment arithmetic operator implementation from CheckedNumeric. */ \ + template <typename T> \ + template <typename Src> \ + CheckedNumeric<T> &CheckedNumeric<T>::operator COMPOUND_OP(Src rhs) \ + { \ + *this = CheckedNumeric<T>::cast(*this) \ + OP CheckedNumeric<typename UnderlyingType<Src>::type>::cast(rhs); \ + return *this; \ + } \ + /* Binary arithmetic operator for CheckedNumeric of different type. */ \ + template <typename T, typename Src> \ + CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \ + const CheckedNumeric<Src> &lhs, const CheckedNumeric<T> &rhs) \ + { \ + typedef typename ArithmeticPromotion<T, Src>::type Promotion; \ + if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \ + return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \ + GetRangeConstraint(rhs.validity() | lhs.validity())); \ + return CheckedNumeric<Promotion>::cast(lhs) OP CheckedNumeric<Promotion>::cast(rhs); \ + } \ + /* Binary arithmetic operator for left CheckedNumeric and right numeric. */ \ + template <typename T, typename Src, \ + typename std::enable_if<std::is_arithmetic<Src>::value>::type * = nullptr> \ + CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \ + const CheckedNumeric<T> &lhs, Src rhs) \ + { \ + typedef typename ArithmeticPromotion<T, Src>::type Promotion; \ + if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \ + return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs, lhs.validity()); \ + return CheckedNumeric<Promotion>::cast(lhs) OP CheckedNumeric<Promotion>::cast(rhs); \ + } \ + /* Binary arithmetic operator for left numeric and right CheckedNumeric. */ \ + template <typename T, typename Src, \ + typename std::enable_if<std::is_arithmetic<Src>::value>::type * = nullptr> \ + CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \ + Src lhs, const CheckedNumeric<T> &rhs) \ + { \ + typedef typename ArithmeticPromotion<T, Src>::type Promotion; \ + if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \ + return CheckedNumeric<Promotion>(lhs OP rhs.ValueUnsafe(), rhs.validity()); \ + return CheckedNumeric<Promotion>::cast(lhs) OP CheckedNumeric<Promotion>::cast(rhs); \ + } + +ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=) +ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=) +ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=) +ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=) +ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=) + +#undef ANGLEBASE_NUMERIC_ARITHMETIC_OPERATORS + +} // namespace internal + +using internal::CheckedNumeric; + +} // namespace base + +} // namespace angle + +#endif // ANGLEBASE_NUMERICS_SAFE_MATH_H_ diff --git a/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_math_impl.h b/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_math_impl.h new file mode 100644 index 0000000000..2831cc6ceb --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_math_impl.h @@ -0,0 +1,575 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ANGLEBASE_NUMERICS_SAFE_MATH_IMPL_H_ +#define ANGLEBASE_NUMERICS_SAFE_MATH_IMPL_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <climits> +#include <cmath> +#include <cstdlib> +#include <limits> +#include <type_traits> + +#include "anglebase/numerics/safe_conversions.h" + +namespace angle +{ + +namespace base +{ +namespace internal +{ + +// Everything from here up to the floating point operations is portable C++, +// but it may not be fast. This code could be split based on +// platform/architecture and replaced with potentially faster implementations. + +// Integer promotion templates used by the portable checked integer arithmetic. +template <size_t Size, bool IsSigned> +struct IntegerForSizeAndSign; +template <> +struct IntegerForSizeAndSign<1, true> +{ + typedef int8_t type; +}; +template <> +struct IntegerForSizeAndSign<1, false> +{ + typedef uint8_t type; +}; +template <> +struct IntegerForSizeAndSign<2, true> +{ + typedef int16_t type; +}; +template <> +struct IntegerForSizeAndSign<2, false> +{ + typedef uint16_t type; +}; +template <> +struct IntegerForSizeAndSign<4, true> +{ + typedef int32_t type; +}; +template <> +struct IntegerForSizeAndSign<4, false> +{ + typedef uint32_t type; +}; +template <> +struct IntegerForSizeAndSign<8, true> +{ + typedef int64_t type; +}; +template <> +struct IntegerForSizeAndSign<8, false> +{ + typedef uint64_t type; +}; + +// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to +// support 128-bit math, then the ArithmeticPromotion template below will need +// to be updated (or more likely replaced with a decltype expression). + +template <typename Integer> +struct UnsignedIntegerForSize +{ + typedef + typename std::enable_if<std::numeric_limits<Integer>::is_integer, + typename IntegerForSizeAndSign<sizeof(Integer), false>::type>::type + type; +}; + +template <typename Integer> +struct SignedIntegerForSize +{ + typedef + typename std::enable_if<std::numeric_limits<Integer>::is_integer, + typename IntegerForSizeAndSign<sizeof(Integer), true>::type>::type + type; +}; + +template <typename Integer> +struct TwiceWiderInteger +{ + typedef typename std::enable_if< + std::numeric_limits<Integer>::is_integer, + typename IntegerForSizeAndSign<sizeof(Integer) * 2, + std::numeric_limits<Integer>::is_signed>::type>::type type; +}; + +template <typename Integer> +struct PositionOfSignBit +{ + static const typename std::enable_if<std::numeric_limits<Integer>::is_integer, size_t>::type + value = CHAR_BIT * sizeof(Integer) - 1; +}; + +// This is used for UnsignedAbs, where we need to support floating-point +// template instantiations even though we don't actually support the operations. +// However, there is no corresponding implementation of e.g. CheckedUnsignedAbs, +// so the float versions will not compile. +template <typename Numeric, + bool IsInteger = std::numeric_limits<Numeric>::is_integer, + bool IsFloat = std::numeric_limits<Numeric>::is_iec559> +struct UnsignedOrFloatForSize; + +template <typename Numeric> +struct UnsignedOrFloatForSize<Numeric, true, false> +{ + typedef typename UnsignedIntegerForSize<Numeric>::type type; +}; + +template <typename Numeric> +struct UnsignedOrFloatForSize<Numeric, false, true> +{ + typedef Numeric type; +}; + +// Helper templates for integer manipulations. + +template <typename T> +constexpr bool HasSignBit(T x) +{ + // Cast to unsigned since right shift on signed is undefined. + return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >> + PositionOfSignBit<T>::value); +} + +// This wrapper undoes the standard integer promotions. +template <typename T> +constexpr T BinaryComplement(T x) +{ + return static_cast<T>(~x); +} + +// Here are the actual portable checked integer math implementations. +// TODO(jschuh): Break this code out from the enable_if pattern and find a clean +// way to coalesce things into the CheckedNumericState specializations below. + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type +CheckedAdd(T x, T y, RangeConstraint *validity) +{ + // Since the value of x+y is undefined if we have a signed type, we compute + // it using the unsigned type of the same size. + typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; + UnsignedDst ux = static_cast<UnsignedDst>(x); + UnsignedDst uy = static_cast<UnsignedDst>(y); + UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy); + // Addition is valid if the sign of (x + y) is equal to either that of x or + // that of y. + if (std::numeric_limits<T>::is_signed) + { + if (HasSignBit(BinaryComplement(static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy))))) + { + *validity = RANGE_VALID; + } + else + { // Direction of wrap is inverse of result sign. + *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW; + } + } + else + { // Unsigned is either valid or overflow. + *validity = BinaryComplement(x) >= y ? RANGE_VALID : RANGE_OVERFLOW; + } + return static_cast<T>(uresult); +} + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type +CheckedSub(T x, T y, RangeConstraint *validity) +{ + // Since the value of x+y is undefined if we have a signed type, we compute + // it using the unsigned type of the same size. + typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; + UnsignedDst ux = static_cast<UnsignedDst>(x); + UnsignedDst uy = static_cast<UnsignedDst>(y); + UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); + // Subtraction is valid if either x and y have same sign, or (x-y) and x have + // the same sign. + if (std::numeric_limits<T>::is_signed) + { + if (HasSignBit(BinaryComplement(static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy))))) + { + *validity = RANGE_VALID; + } + else + { // Direction of wrap is inverse of result sign. + *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW; + } + } + else + { // Unsigned is either valid or underflow. + *validity = x >= y ? RANGE_VALID : RANGE_UNDERFLOW; + } + return static_cast<T>(uresult); +} + +// Integer multiplication is a bit complicated. In the fast case we just +// we just promote to a twice wider type, and range check the result. In the +// slow case we need to manually check that the result won't be truncated by +// checking with division against the appropriate bound. +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_integer && sizeof(T) * 2 <= sizeof(uintmax_t), + T>::type +CheckedMul(T x, T y, RangeConstraint *validity) +{ + typedef typename TwiceWiderInteger<T>::type IntermediateType; + IntermediateType tmp = static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y); + *validity = DstRangeRelationToSrcRange<T>(tmp); + return static_cast<T>(tmp); +} + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed && + (sizeof(T) * 2 > sizeof(uintmax_t)), + T>::type +CheckedMul(T x, T y, RangeConstraint *validity) +{ + // If either side is zero then the result will be zero. + if (!x || !y) + { + *validity = RANGE_VALID; + return static_cast<T>(0); + } + else if (x > 0) + { + if (y > 0) + *validity = x <= std::numeric_limits<T>::max() / y ? RANGE_VALID : RANGE_OVERFLOW; + else + *validity = y >= std::numeric_limits<T>::min() / x ? RANGE_VALID : RANGE_UNDERFLOW; + } + else + { + if (y > 0) + *validity = x >= std::numeric_limits<T>::min() / y ? RANGE_VALID : RANGE_UNDERFLOW; + else + *validity = y >= std::numeric_limits<T>::max() / x ? RANGE_VALID : RANGE_OVERFLOW; + } + + return static_cast<T>(x * y); +} + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed && + (sizeof(T) * 2 > sizeof(uintmax_t)), + T>::type +CheckedMul(T x, T y, RangeConstraint *validity) +{ + *validity = (y == 0 || x <= std::numeric_limits<T>::max() / y) ? RANGE_VALID : RANGE_OVERFLOW; + return static_cast<T>(x * y); +} + +// Division just requires a check for an invalid negation on signed min/-1. +template <typename T> +T CheckedDiv(T x, + T y, + RangeConstraint *validity, + typename std::enable_if<std::numeric_limits<T>::is_integer, int>::type = 0) +{ + if (std::numeric_limits<T>::is_signed && x == std::numeric_limits<T>::min() && + y == static_cast<T>(-1)) + { + *validity = RANGE_OVERFLOW; + return std::numeric_limits<T>::min(); + } + + *validity = RANGE_VALID; + return static_cast<T>(x / y); +} + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed, + T>::type +CheckedMod(T x, T y, RangeConstraint *validity) +{ + *validity = y > 0 ? RANGE_VALID : RANGE_INVALID; + return static_cast<T>(x % y); +} + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed, + T>::type +CheckedMod(T x, T y, RangeConstraint *validity) +{ + *validity = RANGE_VALID; + return static_cast<T>(x % y); +} + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed, + T>::type +CheckedNeg(T value, RangeConstraint *validity) +{ + *validity = value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW; + // The negation of signed min is min, so catch that one. + return static_cast<T>(-value); +} + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed, + T>::type +CheckedNeg(T value, RangeConstraint *validity) +{ + // The only legal unsigned negation is zero. + *validity = value ? RANGE_UNDERFLOW : RANGE_VALID; + return static_cast<T>(-static_cast<typename SignedIntegerForSize<T>::type>(value)); +} + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed, + T>::type +CheckedAbs(T value, RangeConstraint *validity) +{ + *validity = value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW; + return static_cast<T>(std::abs(value)); +} + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed, + T>::type +CheckedAbs(T value, RangeConstraint *validity) +{ + // T is unsigned, so |value| must already be positive. + *validity = RANGE_VALID; + return value; +} + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed, + typename UnsignedIntegerForSize<T>::type>::type +CheckedUnsignedAbs(T value) +{ + typedef typename UnsignedIntegerForSize<T>::type UnsignedT; + return value == std::numeric_limits<T>::min() + ? static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1 + : static_cast<UnsignedT>(std::abs(value)); +} + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed, + T>::type +CheckedUnsignedAbs(T value) +{ + // T is unsigned, so |value| must already be positive. + return static_cast<T>(value); +} + +// These are the floating point stubs that the compiler needs to see. Only the +// negation operation is ever called. +#define ANGLEBASE_FLOAT_ARITHMETIC_STUBS(NAME) \ + template <typename T> \ + typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type Checked##NAME( \ + T, T, RangeConstraint *) \ + { \ + NOTREACHED(); \ + return static_cast<T>(0); \ + } + +ANGLEBASE_FLOAT_ARITHMETIC_STUBS(Add) +ANGLEBASE_FLOAT_ARITHMETIC_STUBS(Sub) +ANGLEBASE_FLOAT_ARITHMETIC_STUBS(Mul) +ANGLEBASE_FLOAT_ARITHMETIC_STUBS(Div) +ANGLEBASE_FLOAT_ARITHMETIC_STUBS(Mod) + +#undef ANGLEBASE_FLOAT_ARITHMETIC_STUBS + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg(T value, + RangeConstraint *) +{ + return static_cast<T>(-value); +} + +template <typename T> +typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs(T value, + RangeConstraint *) +{ + return static_cast<T>(std::abs(value)); +} + +// Floats carry around their validity state with them, but integers do not. So, +// we wrap the underlying value in a specialization in order to hide that detail +// and expose an interface via accessors. +enum NumericRepresentation +{ + NUMERIC_INTEGER, + NUMERIC_FLOATING, + NUMERIC_UNKNOWN +}; + +template <typename NumericType> +struct GetNumericRepresentation +{ + static const NumericRepresentation value = + std::numeric_limits<NumericType>::is_integer + ? NUMERIC_INTEGER + : (std::numeric_limits<NumericType>::is_iec559 ? NUMERIC_FLOATING : NUMERIC_UNKNOWN); +}; + +template <typename T, NumericRepresentation type = GetNumericRepresentation<T>::value> +class CheckedNumericState +{ +}; + +// Integrals require quite a bit of additional housekeeping to manage state. +template <typename T> +class CheckedNumericState<T, NUMERIC_INTEGER> +{ + private: + T value_; + RangeConstraint validity_ : CHAR_BIT; // Actually requires only two bits. + + public: + template <typename Src, NumericRepresentation type> + friend class CheckedNumericState; + + CheckedNumericState() : value_(0), validity_(RANGE_VALID) {} + + template <typename Src> + CheckedNumericState(Src value, RangeConstraint validity) + : value_(static_cast<T>(value)), + validity_(GetRangeConstraint(validity | DstRangeRelationToSrcRange<T>(value))) + { + static_assert(std::numeric_limits<Src>::is_specialized, "Argument must be numeric."); + } + + // Copy constructor. + template <typename Src> + CheckedNumericState(const CheckedNumericState<Src> &rhs) + : value_(static_cast<T>(rhs.value())), + validity_(GetRangeConstraint(rhs.validity() | DstRangeRelationToSrcRange<T>(rhs.value()))) + { + } + + template <typename Src> + explicit CheckedNumericState( + Src value, + typename std::enable_if<std::numeric_limits<Src>::is_specialized, int>::type = 0) + : value_(static_cast<T>(value)), validity_(DstRangeRelationToSrcRange<T>(value)) + { + } + + RangeConstraint validity() const { return validity_; } + T value() const { return value_; } +}; + +// Floating points maintain their own validity, but need translation wrappers. +template <typename T> +class CheckedNumericState<T, NUMERIC_FLOATING> +{ + private: + T value_; + + public: + template <typename Src, NumericRepresentation type> + friend class CheckedNumericState; + + CheckedNumericState() : value_(0.0) {} + + template <typename Src> + CheckedNumericState( + Src value, + RangeConstraint validity, + typename std::enable_if<std::numeric_limits<Src>::is_integer, int>::type = 0) + { + switch (DstRangeRelationToSrcRange<T>(value)) + { + case RANGE_VALID: + value_ = static_cast<T>(value); + break; + + case RANGE_UNDERFLOW: + value_ = -std::numeric_limits<T>::infinity(); + break; + + case RANGE_OVERFLOW: + value_ = std::numeric_limits<T>::infinity(); + break; + + case RANGE_INVALID: + value_ = std::numeric_limits<T>::quiet_NaN(); + break; + + default: + NOTREACHED(); + } + } + + template <typename Src> + explicit CheckedNumericState( + Src value, + typename std::enable_if<std::numeric_limits<Src>::is_specialized, int>::type = 0) + : value_(static_cast<T>(value)) + { + } + + // Copy constructor. + template <typename Src> + CheckedNumericState(const CheckedNumericState<Src> &rhs) : value_(static_cast<T>(rhs.value())) + { + } + + RangeConstraint validity() const + { + return GetRangeConstraint(value_ <= std::numeric_limits<T>::max(), + value_ >= -std::numeric_limits<T>::max()); + } + T value() const { return value_; } +}; + +// For integers less than 128-bit and floats 32-bit or larger, we have the type +// with the larger maximum exponent take precedence. +enum ArithmeticPromotionCategory +{ + LEFT_PROMOTION, + RIGHT_PROMOTION +}; + +template <typename Lhs, + typename Rhs = Lhs, + ArithmeticPromotionCategory Promotion = + (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) ? LEFT_PROMOTION + : RIGHT_PROMOTION> +struct ArithmeticPromotion; + +template <typename Lhs, typename Rhs> +struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> +{ + typedef Lhs type; +}; + +template <typename Lhs, typename Rhs> +struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> +{ + typedef Rhs type; +}; + +// We can statically check if operations on the provided types can wrap, so we +// can skip the checked operations if they're not needed. So, for an integer we +// care if the destination type preserves the sign and is twice the width of +// the source. +template <typename T, typename Lhs, typename Rhs> +struct IsIntegerArithmeticSafe +{ + static const bool value = + !std::numeric_limits<T>::is_iec559 && + StaticDstRangeRelationToSrcRange<T, Lhs>::value == NUMERIC_RANGE_CONTAINED && + sizeof(T) >= (2 * sizeof(Lhs)) && + StaticDstRangeRelationToSrcRange<T, Rhs>::value != NUMERIC_RANGE_CONTAINED && + sizeof(T) >= (2 * sizeof(Rhs)); +}; + +} // namespace internal +} // namespace base + +} // namespace angle + +#endif // ANGLEBASE_NUMERICS_SAFE_MATH_IMPL_H_ diff --git a/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_numerics_unittest.cc b/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_numerics_unittest.cc new file mode 100644 index 0000000000..052d850427 --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/base/anglebase/numerics/safe_numerics_unittest.cc @@ -0,0 +1,771 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stddef.h> +#include <stdint.h> + +#include <limits> +#include <type_traits> + +#include "base/compiler_specific.h" +#include "base/numerics/safe_conversions.h" +#include "base/numerics/safe_math.h" +#include "build/build_config.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS) +#include <mmintrin.h> +#endif + +using std::numeric_limits; +using base::CheckedNumeric; +using base::checked_cast; +using base::IsValueInRangeForNumericType; +using base::IsValueNegative; +using base::SizeT; +using base::StrictNumeric; +using base::saturated_cast; +using base::strict_cast; +using base::internal::MaxExponent; +using base::internal::RANGE_VALID; +using base::internal::RANGE_INVALID; +using base::internal::RANGE_OVERFLOW; +using base::internal::RANGE_UNDERFLOW; +using base::internal::SignedIntegerForSize; + +// These tests deliberately cause arithmetic overflows. If the compiler is +// aggressive enough, it can const fold these overflows. Disable warnings about +// overflows for const expressions. +#if defined(OS_WIN) +#pragma warning(disable : 4756) +#endif + +// This is a helper function for finding the maximum value in Src that can be +// wholy represented as the destination floating-point type. +template <typename Dst, typename Src> +Dst GetMaxConvertibleToFloat() +{ + typedef numeric_limits<Dst> DstLimits; + typedef numeric_limits<Src> SrcLimits; + static_assert(SrcLimits::is_specialized, "Source must be numeric."); + static_assert(DstLimits::is_specialized, "Destination must be numeric."); + CHECK(DstLimits::is_iec559); + + if (SrcLimits::digits <= DstLimits::digits && + MaxExponent<Src>::value <= MaxExponent<Dst>::value) + return SrcLimits::max(); + Src max = SrcLimits::max() / 2 + (SrcLimits::is_integer ? 1 : 0); + while (max != static_cast<Src>(static_cast<Dst>(max))) + { + max /= 2; + } + return static_cast<Dst>(max); +} + +// Helper macros to wrap displaying the conversion types and line numbers. +#define TEST_EXPECTED_VALIDITY(expected, actual) \ + EXPECT_EQ(expected, CheckedNumeric<Dst>(actual).IsValid()) \ + << "Result test: Value " << +(actual).ValueUnsafe() << " as " << dst << " on line " \ + << line; + +#define TEST_EXPECTED_SUCCESS(actual) TEST_EXPECTED_VALIDITY(true, actual) +#define TEST_EXPECTED_FAILURE(actual) TEST_EXPECTED_VALIDITY(false, actual) + +#define TEST_EXPECTED_VALUE(expected, actual) \ + EXPECT_EQ(static_cast<Dst>(expected), CheckedNumeric<Dst>(actual).ValueUnsafe()) \ + << "Result test: Value " << +((actual).ValueUnsafe()) << " as " << dst << " on line " \ + << line; + +// Signed integer arithmetic. +template <typename Dst> +static void TestSpecializedArithmetic( + const char *dst, + int line, + typename std::enable_if<numeric_limits<Dst>::is_integer && numeric_limits<Dst>::is_signed, + int>::type = 0) +{ + typedef numeric_limits<Dst> DstLimits; + TEST_EXPECTED_FAILURE(-CheckedNumeric<Dst>(DstLimits::min())); + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()).Abs()); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs()); + + TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) + -1); + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) + -1); + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) + -DstLimits::max()); + + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) - 1); + TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) - -1); + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) - -DstLimits::max()); + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) - DstLimits::max()); + + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) * 2); + + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) / -1); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(-1) / 2); + + // Modulus is legal only for integers. + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1); + TEST_EXPECTED_VALUE(-1, CheckedNumeric<Dst>(-1) % 2); + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-1) % -2); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) % 2); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2); + // Test all the different modulus combinations. + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(0, 1 % CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1); + CheckedNumeric<Dst> checked_dst = 1; + TEST_EXPECTED_VALUE(0, checked_dst %= 1); +} + +// Unsigned integer arithmetic. +template <typename Dst> +static void TestSpecializedArithmetic( + const char *dst, + int line, + typename std::enable_if<numeric_limits<Dst>::is_integer && !numeric_limits<Dst>::is_signed, + int>::type = 0) +{ + typedef numeric_limits<Dst> DstLimits; + TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::min())); + TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()).Abs()); + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) + -1); + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) - 1); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) * 2); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) / 2); + TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()).UnsignedAbs()); + TEST_EXPECTED_SUCCESS(CheckedNumeric<typename SignedIntegerForSize<Dst>::type>( + std::numeric_limits<typename SignedIntegerForSize<Dst>::type>::min()) + .UnsignedAbs()); + + // Modulus is legal only for integers. + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) % 2); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) % 2); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2); + // Test all the different modulus combinations. + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(0, 1 % CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1); + CheckedNumeric<Dst> checked_dst = 1; + TEST_EXPECTED_VALUE(0, checked_dst %= 1); +} + +// Floating point arithmetic. +template <typename Dst> +void TestSpecializedArithmetic( + const char *dst, + int line, + typename std::enable_if<numeric_limits<Dst>::is_iec559, int>::type = 0) +{ + typedef numeric_limits<Dst> DstLimits; + TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::min())); + + TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()).Abs()); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs()); + + TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) + -1); + TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) + 1); + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) + -DstLimits::max()); + + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) - -DstLimits::max()); + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) - DstLimits::max()); + + TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) * 2); + + TEST_EXPECTED_VALUE(-0.5, CheckedNumeric<Dst>(-1.0) / 2); + EXPECT_EQ(static_cast<Dst>(1.0), CheckedNumeric<Dst>(1.0).ValueFloating()); +} + +// Generic arithmetic tests. +template <typename Dst> +static void TestArithmetic(const char *dst, int line) +{ + typedef numeric_limits<Dst> DstLimits; + + EXPECT_EQ(true, CheckedNumeric<Dst>().IsValid()); + EXPECT_EQ( + false, + CheckedNumeric<Dst>(CheckedNumeric<Dst>(DstLimits::max()) * DstLimits::max()).IsValid()); + EXPECT_EQ(static_cast<Dst>(0), CheckedNumeric<Dst>().ValueOrDie()); + EXPECT_EQ(static_cast<Dst>(0), CheckedNumeric<Dst>().ValueOrDefault(1)); + EXPECT_EQ(static_cast<Dst>(1), + CheckedNumeric<Dst>(CheckedNumeric<Dst>(DstLimits::max()) * DstLimits::max()) + .ValueOrDefault(1)); + + // Test the operator combinations. + TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) + CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) - CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) * CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(2, 1 + CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(0, 1 - CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(1, 1 * CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(1, 1 / CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) + 1); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) - 1); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) * 1); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / 1); + CheckedNumeric<Dst> checked_dst = 1; + TEST_EXPECTED_VALUE(2, checked_dst += 1); + checked_dst = 1; + TEST_EXPECTED_VALUE(0, checked_dst -= 1); + checked_dst = 1; + TEST_EXPECTED_VALUE(1, checked_dst *= 1); + checked_dst = 1; + TEST_EXPECTED_VALUE(1, checked_dst /= 1); + + // Generic negation. + TEST_EXPECTED_VALUE(0, -CheckedNumeric<Dst>()); + TEST_EXPECTED_VALUE(-1, -CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(1, -CheckedNumeric<Dst>(-1)); + TEST_EXPECTED_VALUE(static_cast<Dst>(DstLimits::max() * -1), + -CheckedNumeric<Dst>(DstLimits::max())); + + // Generic absolute value. + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>().Abs()); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1).Abs()); + TEST_EXPECTED_VALUE(DstLimits::max(), CheckedNumeric<Dst>(DstLimits::max()).Abs()); + + // Generic addition. + TEST_EXPECTED_VALUE(1, (CheckedNumeric<Dst>() + 1)); + TEST_EXPECTED_VALUE(2, (CheckedNumeric<Dst>(1) + 1)); + TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(-1) + 1)); + TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) + 1); + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) + DstLimits::max()); + + // Generic subtraction. + TEST_EXPECTED_VALUE(-1, (CheckedNumeric<Dst>() - 1)); + TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(1) - 1)); + TEST_EXPECTED_VALUE(-2, (CheckedNumeric<Dst>(-1) - 1)); + TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) - 1); + + // Generic multiplication. + TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>() * 1)); + TEST_EXPECTED_VALUE(1, (CheckedNumeric<Dst>(1) * 1)); + TEST_EXPECTED_VALUE(-2, (CheckedNumeric<Dst>(-1) * 2)); + TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(0) * 0)); + TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(-1) * 0)); + TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(0) * -1)); + TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) * DstLimits::max()); + + // Generic division. + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() / 1); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / 1); + TEST_EXPECTED_VALUE(DstLimits::min() / 2, CheckedNumeric<Dst>(DstLimits::min()) / 2); + TEST_EXPECTED_VALUE(DstLimits::max() / 2, CheckedNumeric<Dst>(DstLimits::max()) / 2); + + TestSpecializedArithmetic<Dst>(dst, line); +} + +// Helper macro to wrap displaying the conversion types and line numbers. +#define TEST_ARITHMETIC(Dst) TestArithmetic<Dst>(#Dst, __LINE__) + +TEST(SafeNumerics, SignedIntegerMath) +{ + TEST_ARITHMETIC(int8_t); + TEST_ARITHMETIC(int); + TEST_ARITHMETIC(intptr_t); + TEST_ARITHMETIC(intmax_t); +} + +TEST(SafeNumerics, UnsignedIntegerMath) +{ + TEST_ARITHMETIC(uint8_t); + TEST_ARITHMETIC(unsigned int); + TEST_ARITHMETIC(uintptr_t); + TEST_ARITHMETIC(uintmax_t); +} + +TEST(SafeNumerics, FloatingPointMath) +{ + TEST_ARITHMETIC(float); + TEST_ARITHMETIC(double); +} + +// Enumerates the five different conversions types we need to test. +enum NumericConversionType +{ + SIGN_PRESERVING_VALUE_PRESERVING, + SIGN_PRESERVING_NARROW, + SIGN_TO_UNSIGN_WIDEN_OR_EQUAL, + SIGN_TO_UNSIGN_NARROW, + UNSIGN_TO_SIGN_NARROW_OR_EQUAL, +}; + +// Template covering the different conversion tests. +template <typename Dst, typename Src, NumericConversionType conversion> +struct TestNumericConversion +{ +}; + +// EXPECT_EQ wrappers providing specific detail on test failures. +#define TEST_EXPECTED_RANGE(expected, actual) \ + EXPECT_EQ(expected, base::internal::DstRangeRelationToSrcRange<Dst>(actual)) \ + << "Conversion test: " << src << " value " << actual << " to " << dst << " on line " \ + << line; + +template <typename Dst, typename Src> +struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_VALUE_PRESERVING> +{ + static void Test(const char *dst, const char *src, int line) + { + typedef numeric_limits<Src> SrcLimits; + typedef numeric_limits<Dst> DstLimits; + // Integral to floating. + static_assert( + (DstLimits::is_iec559 && SrcLimits::is_integer) || + // Not floating to integral and... + (!(DstLimits::is_integer && SrcLimits::is_iec559) && + // Same sign, same numeric, source is narrower or same. + ((SrcLimits::is_signed == DstLimits::is_signed && sizeof(Dst) >= sizeof(Src)) || + // Or signed destination and source is smaller + (DstLimits::is_signed && sizeof(Dst) > sizeof(Src)))), + "Comparison must be sign preserving and value preserving"); + + const CheckedNumeric<Dst> checked_dst = SrcLimits::max(); + TEST_EXPECTED_SUCCESS(checked_dst); + if (MaxExponent<Dst>::value > MaxExponent<Src>::value) + { + if (MaxExponent<Dst>::value >= MaxExponent<Src>::value * 2 - 1) + { + // At least twice larger type. + TEST_EXPECTED_SUCCESS(SrcLimits::max() * checked_dst); + } + else + { // Larger, but not at least twice as large. + TEST_EXPECTED_FAILURE(SrcLimits::max() * checked_dst); + TEST_EXPECTED_SUCCESS(checked_dst + 1); + } + } + else + { // Same width type. + TEST_EXPECTED_FAILURE(checked_dst + 1); + } + + TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max()); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1)); + if (SrcLimits::is_iec559) + { + TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max() * static_cast<Src>(-1)); + TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity()); + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1); + TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN()); + } + else if (numeric_limits<Src>::is_signed) + { + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1)); + TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min()); + } + } +}; + +template <typename Dst, typename Src> +struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_NARROW> +{ + static void Test(const char *dst, const char *src, int line) + { + typedef numeric_limits<Src> SrcLimits; + typedef numeric_limits<Dst> DstLimits; + static_assert(SrcLimits::is_signed == DstLimits::is_signed, + "Destination and source sign must be the same"); + static_assert(sizeof(Dst) < sizeof(Src) || (DstLimits::is_integer && SrcLimits::is_iec559), + "Destination must be narrower than source"); + + const CheckedNumeric<Dst> checked_dst; + TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max()); + TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1)); + TEST_EXPECTED_FAILURE(checked_dst - SrcLimits::max()); + + TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max()); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1)); + if (SrcLimits::is_iec559) + { + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::max() * -1); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1)); + TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity()); + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1); + TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN()); + if (DstLimits::is_integer) + { + if (SrcLimits::digits < DstLimits::digits) + { + TEST_EXPECTED_RANGE(RANGE_OVERFLOW, static_cast<Src>(DstLimits::max())); + } + else + { + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::max())); + } + TEST_EXPECTED_RANGE(RANGE_VALID, + static_cast<Src>(GetMaxConvertibleToFloat<Src, Dst>())); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::min())); + } + } + else if (SrcLimits::is_signed) + { + TEST_EXPECTED_VALUE(-1, checked_dst - static_cast<Src>(1)); + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min()); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1)); + } + else + { + TEST_EXPECTED_FAILURE(checked_dst - static_cast<Src>(1)); + TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min()); + } + } +}; + +template <typename Dst, typename Src> +struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL> +{ + static void Test(const char *dst, const char *src, int line) + { + typedef numeric_limits<Src> SrcLimits; + typedef numeric_limits<Dst> DstLimits; + static_assert(sizeof(Dst) >= sizeof(Src), + "Destination must be equal or wider than source."); + static_assert(SrcLimits::is_signed, "Source must be signed"); + static_assert(!DstLimits::is_signed, "Destination must be unsigned"); + + const CheckedNumeric<Dst> checked_dst; + TEST_EXPECTED_VALUE(SrcLimits::max(), checked_dst + SrcLimits::max()); + TEST_EXPECTED_FAILURE(checked_dst + static_cast<Src>(-1)); + TEST_EXPECTED_FAILURE(checked_dst + -SrcLimits::max()); + + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min()); + TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max()); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1)); + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1)); + } +}; + +template <typename Dst, typename Src> +struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_NARROW> +{ + static void Test(const char *dst, const char *src, int line) + { + typedef numeric_limits<Src> SrcLimits; + typedef numeric_limits<Dst> DstLimits; + static_assert( + (DstLimits::is_integer && SrcLimits::is_iec559) || (sizeof(Dst) < sizeof(Src)), + "Destination must be narrower than source."); + static_assert(SrcLimits::is_signed, "Source must be signed."); + static_assert(!DstLimits::is_signed, "Destination must be unsigned."); + + const CheckedNumeric<Dst> checked_dst; + TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1)); + TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max()); + TEST_EXPECTED_FAILURE(checked_dst + static_cast<Src>(-1)); + TEST_EXPECTED_FAILURE(checked_dst + -SrcLimits::max()); + + TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max()); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1)); + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1)); + if (SrcLimits::is_iec559) + { + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::max() * -1); + TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity()); + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1); + TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN()); + if (DstLimits::is_integer) + { + if (SrcLimits::digits < DstLimits::digits) + { + TEST_EXPECTED_RANGE(RANGE_OVERFLOW, static_cast<Src>(DstLimits::max())); + } + else + { + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::max())); + } + TEST_EXPECTED_RANGE(RANGE_VALID, + static_cast<Src>(GetMaxConvertibleToFloat<Src, Dst>())); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::min())); + } + } + else + { + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min()); + } + } +}; + +template <typename Dst, typename Src> +struct TestNumericConversion<Dst, Src, UNSIGN_TO_SIGN_NARROW_OR_EQUAL> +{ + static void Test(const char *dst, const char *src, int line) + { + typedef numeric_limits<Src> SrcLimits; + typedef numeric_limits<Dst> DstLimits; + static_assert(sizeof(Dst) <= sizeof(Src), + "Destination must be narrower or equal to source."); + static_assert(!SrcLimits::is_signed, "Source must be unsigned."); + static_assert(DstLimits::is_signed, "Destination must be signed."); + + const CheckedNumeric<Dst> checked_dst; + TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1)); + TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max()); + TEST_EXPECTED_VALUE(SrcLimits::min(), checked_dst + SrcLimits::min()); + + TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min()); + TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max()); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1)); + } +}; + +// Helper macro to wrap displaying the conversion types and line numbers +#define TEST_NUMERIC_CONVERSION(d, s, t) TestNumericConversion<d, s, t>::Test(#d, #s, __LINE__) + +TEST(SafeNumerics, IntMinOperations) +{ + TEST_NUMERIC_CONVERSION(int8_t, int8_t, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(uint8_t, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING); + + TEST_NUMERIC_CONVERSION(int8_t, int, SIGN_PRESERVING_NARROW); + TEST_NUMERIC_CONVERSION(uint8_t, unsigned int, SIGN_PRESERVING_NARROW); + TEST_NUMERIC_CONVERSION(int8_t, float, SIGN_PRESERVING_NARROW); + + TEST_NUMERIC_CONVERSION(uint8_t, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL); + + TEST_NUMERIC_CONVERSION(uint8_t, int, SIGN_TO_UNSIGN_NARROW); + TEST_NUMERIC_CONVERSION(uint8_t, intmax_t, SIGN_TO_UNSIGN_NARROW); + TEST_NUMERIC_CONVERSION(uint8_t, float, SIGN_TO_UNSIGN_NARROW); + + TEST_NUMERIC_CONVERSION(int8_t, unsigned int, UNSIGN_TO_SIGN_NARROW_OR_EQUAL); + TEST_NUMERIC_CONVERSION(int8_t, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL); +} + +TEST(SafeNumerics, IntOperations) +{ + TEST_NUMERIC_CONVERSION(int, int, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(unsigned int, unsigned int, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(int, int8_t, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(unsigned int, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(int, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING); + + TEST_NUMERIC_CONVERSION(int, intmax_t, SIGN_PRESERVING_NARROW); + TEST_NUMERIC_CONVERSION(unsigned int, uintmax_t, SIGN_PRESERVING_NARROW); + TEST_NUMERIC_CONVERSION(int, float, SIGN_PRESERVING_NARROW); + TEST_NUMERIC_CONVERSION(int, double, SIGN_PRESERVING_NARROW); + + TEST_NUMERIC_CONVERSION(unsigned int, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL); + TEST_NUMERIC_CONVERSION(unsigned int, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL); + + TEST_NUMERIC_CONVERSION(unsigned int, intmax_t, SIGN_TO_UNSIGN_NARROW); + TEST_NUMERIC_CONVERSION(unsigned int, float, SIGN_TO_UNSIGN_NARROW); + TEST_NUMERIC_CONVERSION(unsigned int, double, SIGN_TO_UNSIGN_NARROW); + + TEST_NUMERIC_CONVERSION(int, unsigned int, UNSIGN_TO_SIGN_NARROW_OR_EQUAL); + TEST_NUMERIC_CONVERSION(int, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL); +} + +TEST(SafeNumerics, IntMaxOperations) +{ + TEST_NUMERIC_CONVERSION(intmax_t, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(uintmax_t, uintmax_t, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(intmax_t, int, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(uintmax_t, unsigned int, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(intmax_t, unsigned int, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(intmax_t, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING); + + TEST_NUMERIC_CONVERSION(intmax_t, float, SIGN_PRESERVING_NARROW); + TEST_NUMERIC_CONVERSION(intmax_t, double, SIGN_PRESERVING_NARROW); + + TEST_NUMERIC_CONVERSION(uintmax_t, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL); + TEST_NUMERIC_CONVERSION(uintmax_t, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL); + + TEST_NUMERIC_CONVERSION(uintmax_t, float, SIGN_TO_UNSIGN_NARROW); + TEST_NUMERIC_CONVERSION(uintmax_t, double, SIGN_TO_UNSIGN_NARROW); + + TEST_NUMERIC_CONVERSION(intmax_t, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL); +} + +TEST(SafeNumerics, FloatOperations) +{ + TEST_NUMERIC_CONVERSION(float, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(float, uintmax_t, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(float, int, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(float, unsigned int, SIGN_PRESERVING_VALUE_PRESERVING); + + TEST_NUMERIC_CONVERSION(float, double, SIGN_PRESERVING_NARROW); +} + +TEST(SafeNumerics, DoubleOperations) +{ + TEST_NUMERIC_CONVERSION(double, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(double, uintmax_t, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(double, int, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(double, unsigned int, SIGN_PRESERVING_VALUE_PRESERVING); +} + +TEST(SafeNumerics, SizeTOperations) +{ + TEST_NUMERIC_CONVERSION(size_t, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL); + TEST_NUMERIC_CONVERSION(int, size_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL); +} + +TEST(SafeNumerics, CastTests) +{ +// MSVC catches and warns that we're forcing saturation in these tests. +// Since that's intentional, we need to shut this warning off. +#if defined(COMPILER_MSVC) +#pragma warning(disable : 4756) +#endif + + int small_positive = 1; + int small_negative = -1; + double double_small = 1.0; + double double_large = numeric_limits<double>::max(); + double double_infinity = numeric_limits<float>::infinity(); + double double_large_int = numeric_limits<int>::max(); + double double_small_int = numeric_limits<int>::min(); + + // Just test that the casts compile, since the other tests cover logic. + EXPECT_EQ(0, checked_cast<int>(static_cast<size_t>(0))); + EXPECT_EQ(0, strict_cast<int>(static_cast<char>(0))); + EXPECT_EQ(0, strict_cast<int>(static_cast<unsigned char>(0))); + EXPECT_EQ(0U, strict_cast<unsigned>(static_cast<unsigned char>(0))); + EXPECT_EQ(1ULL, static_cast<uint64_t>(StrictNumeric<size_t>(1U))); + EXPECT_EQ(1ULL, static_cast<uint64_t>(SizeT(1U))); + EXPECT_EQ(1U, static_cast<size_t>(StrictNumeric<unsigned>(1U))); + + EXPECT_TRUE(CheckedNumeric<uint64_t>(StrictNumeric<unsigned>(1U)).IsValid()); + EXPECT_TRUE(CheckedNumeric<int>(StrictNumeric<unsigned>(1U)).IsValid()); + EXPECT_FALSE(CheckedNumeric<unsigned>(StrictNumeric<int>(-1)).IsValid()); + + EXPECT_TRUE(IsValueNegative(-1)); + EXPECT_TRUE(IsValueNegative(numeric_limits<int>::min())); + EXPECT_FALSE(IsValueNegative(numeric_limits<unsigned>::min())); + EXPECT_TRUE(IsValueNegative(-numeric_limits<double>::max())); + EXPECT_FALSE(IsValueNegative(0)); + EXPECT_FALSE(IsValueNegative(1)); + EXPECT_FALSE(IsValueNegative(0u)); + EXPECT_FALSE(IsValueNegative(1u)); + EXPECT_FALSE(IsValueNegative(numeric_limits<int>::max())); + EXPECT_FALSE(IsValueNegative(numeric_limits<unsigned>::max())); + EXPECT_FALSE(IsValueNegative(numeric_limits<double>::max())); + + // These casts and coercions will fail to compile: + // EXPECT_EQ(0, strict_cast<int>(static_cast<size_t>(0))); + // EXPECT_EQ(0, strict_cast<size_t>(static_cast<int>(0))); + // EXPECT_EQ(1ULL, StrictNumeric<size_t>(1)); + // EXPECT_EQ(1, StrictNumeric<size_t>(1U)); + + // Test various saturation corner cases. + EXPECT_EQ(saturated_cast<int>(small_negative), static_cast<int>(small_negative)); + EXPECT_EQ(saturated_cast<int>(small_positive), static_cast<int>(small_positive)); + EXPECT_EQ(saturated_cast<unsigned>(small_negative), static_cast<unsigned>(0)); + EXPECT_EQ(saturated_cast<int>(double_small), static_cast<int>(double_small)); + EXPECT_EQ(saturated_cast<int>(double_large), numeric_limits<int>::max()); + EXPECT_EQ(saturated_cast<float>(double_large), double_infinity); + EXPECT_EQ(saturated_cast<float>(-double_large), -double_infinity); + EXPECT_EQ(numeric_limits<int>::min(), saturated_cast<int>(double_small_int)); + EXPECT_EQ(numeric_limits<int>::max(), saturated_cast<int>(double_large_int)); + + float not_a_number = + std::numeric_limits<float>::infinity() - std::numeric_limits<float>::infinity(); + EXPECT_TRUE(std::isnan(not_a_number)); + EXPECT_EQ(0, saturated_cast<int>(not_a_number)); +} + +#if GTEST_HAS_DEATH_TEST + +TEST(SafeNumerics, SaturatedCastChecks) +{ + float not_a_number = + std::numeric_limits<float>::infinity() - std::numeric_limits<float>::infinity(); + EXPECT_TRUE(std::isnan(not_a_number)); + EXPECT_DEATH((saturated_cast<int, base::SaturatedCastNaNBehaviorCheck>(not_a_number)), ""); +} + +#endif // GTEST_HAS_DEATH_TEST + +TEST(SafeNumerics, IsValueInRangeForNumericType) +{ + EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(0)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(1)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(2)); + EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(-1)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(0xffffffffu)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0xffffffff))); + EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0x100000000))); + EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0x100000001))); + EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(std::numeric_limits<int32_t>::min())); + EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(std::numeric_limits<int64_t>::min())); + + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(0)); + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(1)); + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(2)); + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(-1)); + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(0x7fffffff)); + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(0x7fffffffu)); + EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(0x80000000u)); + EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(0xffffffffu)); + EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0x80000000))); + EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0xffffffff))); + EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0x100000000))); + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(std::numeric_limits<int32_t>::min())); + EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>( + static_cast<int64_t>(std::numeric_limits<int32_t>::min()))); + EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>( + static_cast<int64_t>(std::numeric_limits<int32_t>::min()) - 1)); + EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(std::numeric_limits<int64_t>::min())); + + EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(0)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(1)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(2)); + EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(-1)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(0xffffffffu)); + EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0xffffffff))); + EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0x100000000))); + EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0x100000001))); + EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(std::numeric_limits<int32_t>::min())); + EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(INT64_C(-1))); + EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(std::numeric_limits<int64_t>::min())); + + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(1)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(2)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(-1)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0x7fffffff)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0x7fffffffu)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0x80000000u)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0xffffffffu)); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(INT64_C(0x80000000))); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(INT64_C(0xffffffff))); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(INT64_C(0x100000000))); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(INT64_C(0x7fffffffffffffff))); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(UINT64_C(0x7fffffffffffffff))); + EXPECT_FALSE(IsValueInRangeForNumericType<int64_t>(UINT64_C(0x8000000000000000))); + EXPECT_FALSE(IsValueInRangeForNumericType<int64_t>(UINT64_C(0xffffffffffffffff))); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(std::numeric_limits<int32_t>::min())); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>( + static_cast<int64_t>(std::numeric_limits<int32_t>::min()))); + EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(std::numeric_limits<int64_t>::min())); +} + +TEST(SafeNumerics, CompoundNumericOperations) +{ + CheckedNumeric<int> a = 1; + CheckedNumeric<int> b = 2; + CheckedNumeric<int> c = 3; + CheckedNumeric<int> d = 4; + a += b; + EXPECT_EQ(3, a.ValueOrDie()); + a -= c; + EXPECT_EQ(0, a.ValueOrDie()); + d /= b; + EXPECT_EQ(2, d.ValueOrDie()); + d *= d; + EXPECT_EQ(4, d.ValueOrDie()); + + CheckedNumeric<int> too_large = std::numeric_limits<int>::max(); + EXPECT_TRUE(too_large.IsValid()); + too_large += d; + EXPECT_FALSE(too_large.IsValid()); + too_large -= d; + EXPECT_FALSE(too_large.IsValid()); + too_large /= d; + EXPECT_FALSE(too_large.IsValid()); +} diff --git a/src/3rdparty/angle/src/common/third_party/base/anglebase/sha1.cc b/src/3rdparty/angle/src/common/third_party/base/anglebase/sha1.cc new file mode 100644 index 0000000000..cb88ba06e1 --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/base/anglebase/sha1.cc @@ -0,0 +1,245 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "anglebase/sha1.h" + +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include "anglebase/sys_byteorder.h" + +namespace angle +{ + +namespace base +{ + +// Implementation of SHA-1. Only handles data in byte-sized blocks, +// which simplifies the code a fair bit. + +// Identifier names follow notation in FIPS PUB 180-3, where you'll +// also find a description of the algorithm: +// http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf + +// Usage example: +// +// SecureHashAlgorithm sha; +// while(there is data to hash) +// sha.Update(moredata, size of data); +// sha.Final(); +// memcpy(somewhere, sha.Digest(), 20); +// +// to reuse the instance of sha, call sha.Init(); + +// TODO(jhawkins): Replace this implementation with a per-platform +// implementation using each platform's crypto library. See +// http://crbug.com/47218 + +class SecureHashAlgorithm +{ + public: + SecureHashAlgorithm() { Init(); } + + static const int kDigestSizeBytes; + + void Init(); + void Update(const void *data, size_t nbytes); + void Final(); + + // 20 bytes of message digest. + const unsigned char *Digest() const { return reinterpret_cast<const unsigned char *>(H); } + + private: + void Pad(); + void Process(); + + uint32_t A, B, C, D, E; + + uint32_t H[5]; + + union { + uint32_t W[80]; + uint8_t M[64]; + }; + + uint32_t cursor; + uint64_t l; +}; + +static inline uint32_t f(uint32_t t, uint32_t B, uint32_t C, uint32_t D) +{ + if (t < 20) + { + return (B & C) | ((~B) & D); + } + else if (t < 40) + { + return B ^ C ^ D; + } + else if (t < 60) + { + return (B & C) | (B & D) | (C & D); + } + else + { + return B ^ C ^ D; + } +} + +static inline uint32_t S(uint32_t n, uint32_t X) +{ + return (X << n) | (X >> (32 - n)); +} + +static inline uint32_t K(uint32_t t) +{ + if (t < 20) + { + return 0x5a827999; + } + else if (t < 40) + { + return 0x6ed9eba1; + } + else if (t < 60) + { + return 0x8f1bbcdc; + } + else + { + return 0xca62c1d6; + } +} + +const int SecureHashAlgorithm::kDigestSizeBytes = 20; + +void SecureHashAlgorithm::Init() +{ + A = 0; + B = 0; + C = 0; + D = 0; + E = 0; + cursor = 0; + l = 0; + H[0] = 0x67452301; + H[1] = 0xefcdab89; + H[2] = 0x98badcfe; + H[3] = 0x10325476; + H[4] = 0xc3d2e1f0; +} + +void SecureHashAlgorithm::Final() +{ + Pad(); + Process(); + + for (int t = 0; t < 5; ++t) + H[t] = ByteSwap(H[t]); +} + +void SecureHashAlgorithm::Update(const void *data, size_t nbytes) +{ + const uint8_t *d = reinterpret_cast<const uint8_t *>(data); + while (nbytes--) + { + M[cursor++] = *d++; + if (cursor >= 64) + Process(); + l += 8; + } +} + +void SecureHashAlgorithm::Pad() +{ + M[cursor++] = 0x80; + + if (cursor > 64 - 8) + { + // pad out to next block + while (cursor < 64) + M[cursor++] = 0; + + Process(); + } + + while (cursor < 64 - 8) + M[cursor++] = 0; + + M[cursor++] = (l >> 56) & 0xff; + M[cursor++] = (l >> 48) & 0xff; + M[cursor++] = (l >> 40) & 0xff; + M[cursor++] = (l >> 32) & 0xff; + M[cursor++] = (l >> 24) & 0xff; + M[cursor++] = (l >> 16) & 0xff; + M[cursor++] = (l >> 8) & 0xff; + M[cursor++] = l & 0xff; +} + +void SecureHashAlgorithm::Process() +{ + uint32_t t; + + // Each a...e corresponds to a section in the FIPS 180-3 algorithm. + + // a. + // + // W and M are in a union, so no need to memcpy. + // memcpy(W, M, sizeof(M)); + for (t = 0; t < 16; ++t) + W[t] = ByteSwap(W[t]); + + // b. + for (t = 16; t < 80; ++t) + W[t] = S(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]); + + // c. + A = H[0]; + B = H[1]; + C = H[2]; + D = H[3]; + E = H[4]; + + // d. + for (t = 0; t < 80; ++t) + { + uint32_t TEMP = S(5, A) + f(t, B, C, D) + E + W[t] + K(t); + E = D; + D = C; + C = S(30, B); + B = A; + A = TEMP; + } + + // e. + H[0] += A; + H[1] += B; + H[2] += C; + H[3] += D; + H[4] += E; + + cursor = 0; +} + +std::string SHA1HashString(const std::string &str) +{ + char hash[SecureHashAlgorithm::kDigestSizeBytes]; + SHA1HashBytes(reinterpret_cast<const unsigned char *>(str.c_str()), str.length(), + reinterpret_cast<unsigned char *>(hash)); + return std::string(hash, SecureHashAlgorithm::kDigestSizeBytes); +} + +void SHA1HashBytes(const unsigned char *data, size_t len, unsigned char *hash) +{ + SecureHashAlgorithm sha; + sha.Update(data, len); + sha.Final(); + + memcpy(hash, sha.Digest(), SecureHashAlgorithm::kDigestSizeBytes); +} + +} // namespace base + +} // namespace angle diff --git a/src/3rdparty/angle/src/common/third_party/base/anglebase/sha1.h b/src/3rdparty/angle/src/common/third_party/base/anglebase/sha1.h new file mode 100644 index 0000000000..a60908814f --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/base/anglebase/sha1.h @@ -0,0 +1,36 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ANGLEBASE_SHA1_H_ +#define ANGLEBASE_SHA1_H_ + +#include <stddef.h> + +#include <string> + +#include "anglebase/base_export.h" + +namespace angle +{ + +namespace base +{ + +// These functions perform SHA-1 operations. + +static const size_t kSHA1Length = 20; // Length in bytes of a SHA-1 hash. + +// Computes the SHA-1 hash of the input string |str| and returns the full +// hash. +ANGLEBASE_EXPORT std::string SHA1HashString(const std::string &str); + +// Computes the SHA-1 hash of the |len| bytes in |data| and puts the hash +// in |hash|. |hash| must be kSHA1Length bytes long. +ANGLEBASE_EXPORT void SHA1HashBytes(const unsigned char *data, size_t len, unsigned char *hash); + +} // namespace base + +} // namespace angle + +#endif // ANGLEBASE_SHA1_H_ diff --git a/src/3rdparty/angle/src/common/third_party/base/anglebase/sys_byteorder.h b/src/3rdparty/angle/src/common/third_party/base/anglebase/sys_byteorder.h new file mode 100644 index 0000000000..43d1777f26 --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/base/anglebase/sys_byteorder.h @@ -0,0 +1,49 @@ +// +// Copyright 2017 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. +// +// sys_byteorder.h: Compatiblity hacks for importing Chromium's base/SHA1. + +#ifndef ANGLEBASE_SYS_BYTEORDER_H_ +#define ANGLEBASE_SYS_BYTEORDER_H_ + +namespace angle +{ + +namespace base +{ + +// Returns a value with all bytes in |x| swapped, i.e. reverses the endianness. +inline uint16_t ByteSwap(uint16_t x) +{ +#if defined(_MSC_VER) + return _byteswap_ushort(x); +#else + return __builtin_bswap16(x); +#endif +} + +inline uint32_t ByteSwap(uint32_t x) +{ +#if defined(_MSC_VER) + return _byteswap_ulong(x); +#else + return __builtin_bswap32(x); +#endif +} + +inline uint64_t ByteSwap(uint64_t x) +{ +#if defined(_MSC_VER) + return _byteswap_uint64(x); +#else + return __builtin_bswap64(x); +#endif +} + +} // namespace base + +} // namespace angle + +#endif // ANGLEBASE_SYS_BYTEORDER_H_
\ No newline at end of file diff --git a/src/3rdparty/angle/src/common/third_party/smhasher/LICENSE b/src/3rdparty/angle/src/common/third_party/smhasher/LICENSE new file mode 100644 index 0000000000..3f18a844ad --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/smhasher/LICENSE @@ -0,0 +1,23 @@ +All MurmurHash source files are placed in the public domain. + +The license below applies to all other code in SMHasher: + +Copyright (c) 2011 Google, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/3rdparty/angle/src/common/third_party/smhasher/README.angle b/src/3rdparty/angle/src/common/third_party/smhasher/README.angle new file mode 100644 index 0000000000..b84ea3249b --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/smhasher/README.angle @@ -0,0 +1,14 @@ +Name: SMHasher +URL: http://code.google.com/p/smhasher/ +Version: 0 +Revision: 147 +License: MIT, Public Domain +License File: LICENSE +Security Critical: yes + +Description: +This is a library containing the MurmurHash3 function, and a hashing function +test suite. + +Licenses are MIT (SMHasher) and Public Domain (MurmurHash). + diff --git a/src/3rdparty/angle/src/common/third_party/smhasher/src/PMurHash.cpp b/src/3rdparty/angle/src/common/third_party/smhasher/src/PMurHash.cpp new file mode 100644 index 0000000000..071bc31539 --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/smhasher/src/PMurHash.cpp @@ -0,0 +1,320 @@ +/*----------------------------------------------------------------------------- + * MurmurHash3 was written by Austin Appleby, and is placed in the public + * domain. + * + * This implementation was written by Shane Day, and is also public domain. + * + * This is a portable ANSI C implementation of MurmurHash3_x86_32 (Murmur3A) + * with support for progressive processing. + */ + +/*----------------------------------------------------------------------------- + +If you want to understand the MurmurHash algorithm you would be much better +off reading the original source. Just point your browser at: +http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + + +What this version provides? + +1. Progressive data feeding. Useful when the entire payload to be hashed +does not fit in memory or when the data is streamed through the application. +Also useful when hashing a number of strings with a common prefix. A partial +hash of a prefix string can be generated and reused for each suffix string. + +2. Portability. Plain old C so that it should compile on any old compiler. +Both CPU endian and access-alignment neutral, but avoiding inefficient code +when possible depending on CPU capabilities. + +3. Drop in. I personally like nice self contained public domain code, making it +easy to pilfer without loads of refactoring to work properly in the existing +application code & makefile structure and mucking around with licence files. +Just copy PMurHash.h and PMurHash.c and you're ready to go. + + +How does it work? + +We can only process entire 32 bit chunks of input, except for the very end +that may be shorter. So along with the partial hash we need to give back to +the caller a carry containing up to 3 bytes that we were unable to process. +This carry also needs to record the number of bytes the carry holds. I use +the low 2 bits as a count (0..3) and the carry bytes are shifted into the +high byte in stream order. + +To handle endianess I simply use a macro that reads a uint32_t and define +that macro to be a direct read on little endian machines, a read and swap +on big endian machines, or a byte-by-byte read if the endianess is unknown. + +-----------------------------------------------------------------------------*/ + + +#include "PMurHash.h" + +/* I used ugly type names in the header to avoid potential conflicts with + * application or system typedefs & defines. Since I'm not including any more + * headers below here I can rename these so that the code reads like C99 */ +#undef uint32_t +#define uint32_t MH_UINT32 +#undef uint8_t +#define uint8_t MH_UINT8 + +/* MSVC warnings we choose to ignore */ +#if defined(_MSC_VER) + #pragma warning(disable: 4127) /* conditional expression is constant */ +#endif + +/*----------------------------------------------------------------------------- + * Endianess, misalignment capabilities and util macros + * + * The following 3 macros are defined in this section. The other macros defined + * are only needed to help derive these 3. + * + * READ_UINT32(x) Read a little endian unsigned 32-bit int + * UNALIGNED_SAFE Defined if READ_UINT32 works on non-word boundaries + * ROTL32(x,r) Rotate x left by r bits + */ + +/* Convention is to define __BYTE_ORDER == to one of these values */ +#if !defined(__BIG_ENDIAN) + #define __BIG_ENDIAN 4321 +#endif +#if !defined(__LITTLE_ENDIAN) + #define __LITTLE_ENDIAN 1234 +#endif + +/* I386 */ +#if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(i386) + #define __BYTE_ORDER __LITTLE_ENDIAN + #define UNALIGNED_SAFE +#endif + +/* gcc 'may' define __LITTLE_ENDIAN__ or __BIG_ENDIAN__ to 1 (Note the trailing __), + * or even _LITTLE_ENDIAN or _BIG_ENDIAN (Note the single _ prefix) */ +#if !defined(__BYTE_ORDER) + #if defined(__LITTLE_ENDIAN__) && __LITTLE_ENDIAN__==1 || defined(_LITTLE_ENDIAN) && _LITTLE_ENDIAN==1 + #define __BYTE_ORDER __LITTLE_ENDIAN + #elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1 || defined(_BIG_ENDIAN) && _BIG_ENDIAN==1 + #define __BYTE_ORDER __BIG_ENDIAN + #endif +#endif + +/* gcc (usually) defines xEL/EB macros for ARM and MIPS endianess */ +#if !defined(__BYTE_ORDER) + #if defined(__ARMEL__) || defined(__MIPSEL__) + #define __BYTE_ORDER __LITTLE_ENDIAN + #endif + #if defined(__ARMEB__) || defined(__MIPSEB__) + #define __BYTE_ORDER __BIG_ENDIAN + #endif +#endif + +/* Now find best way we can to READ_UINT32 */ +#if __BYTE_ORDER==__LITTLE_ENDIAN + /* CPU endian matches murmurhash algorithm, so read 32-bit word directly */ + #define READ_UINT32(ptr) (*((uint32_t*)(ptr))) +#elif __BYTE_ORDER==__BIG_ENDIAN + /* TODO: Add additional cases below where a compiler provided bswap32 is available */ + #if defined(__GNUC__) && (__GNUC__>4 || (__GNUC__==4 && __GNUC_MINOR__>=3)) + #define READ_UINT32(ptr) (__builtin_bswap32(*((uint32_t*)(ptr)))) + #else + /* Without a known fast bswap32 we're just as well off doing this */ + #define READ_UINT32(ptr) (ptr[0]|ptr[1]<<8|ptr[2]<<16|ptr[3]<<24) + #define UNALIGNED_SAFE + #endif +#else + /* Unknown endianess so last resort is to read individual bytes */ + #define READ_UINT32(ptr) (ptr[0]|ptr[1]<<8|ptr[2]<<16|ptr[3]<<24) + + /* Since we're not doing word-reads we can skip the messing about with realignment */ + #define UNALIGNED_SAFE +#endif + +/* Find best way to ROTL32 */ +#if defined(_MSC_VER) + #include <stdlib.h> /* Microsoft put _rotl declaration in here */ + #define ROTL32(x,r) _rotl(x,r) +#else + /* gcc recognises this code and generates a rotate instruction for CPUs with one */ + #define ROTL32(x,r) (((uint32_t)x << r) | ((uint32_t)x >> (32 - r))) +#endif + + +/*----------------------------------------------------------------------------- + * Core murmurhash algorithm macros */ + +#define C1 (0xcc9e2d51) +#define C2 (0x1b873593) + +/* This is the main processing body of the algorithm. It operates + * on each full 32-bits of input. */ +#define DOBLOCK(h1, k1) do{ \ + k1 *= C1; \ + k1 = ROTL32(k1,15); \ + k1 *= C2; \ + \ + h1 ^= k1; \ + h1 = ROTL32(h1,13); \ + h1 = h1*5+0xe6546b64; \ + }while(0) + + +/* Append unaligned bytes to carry, forcing hash churn if we have 4 bytes */ +/* cnt=bytes to process, h1=name of h1 var, c=carry, n=bytes in c, ptr/len=payload */ +#define DOBYTES(cnt, h1, c, n, ptr, len) do{ \ + int _i = cnt; \ + while(_i--) { \ + c = c>>8 | *ptr++<<24; \ + n++; len--; \ + if(n==4) { \ + DOBLOCK(h1, c); \ + n = 0; \ + } \ + } }while(0) + +/*---------------------------------------------------------------------------*/ + +namespace angle +{ +/* Main hashing function. Initialise carry to 0 and h1 to 0 or an initial seed + * if wanted. Both ph1 and pcarry are required arguments. */ +void PMurHash32_Process(uint32_t *ph1, uint32_t *pcarry, const void *key, int len) +{ + uint32_t h1 = *ph1; + uint32_t c = *pcarry; + + const uint8_t *ptr = (uint8_t*)key; + const uint8_t *end; + + /* Extract carry count from low 2 bits of c value */ + int n = c & 3; + +#if defined(UNALIGNED_SAFE) + /* This CPU handles unaligned word access */ + + /* Consume any carry bytes */ + int i = (4-n) & 3; + if(i && i <= len) { + DOBYTES(i, h1, c, n, ptr, len); + } + + /* Process 32-bit chunks */ + end = ptr + len/4*4; + for( ; ptr < end ; ptr+=4) { + uint32_t k1 = READ_UINT32(ptr); + DOBLOCK(h1, k1); + } + +#else /*UNALIGNED_SAFE*/ + /* This CPU does not handle unaligned word access */ + + /* Consume enough so that the next data byte is word aligned */ + int i = -(long)ptr & 3; + if(i && i <= len) { + DOBYTES(i, h1, c, n, ptr, len); + } + + /* We're now aligned. Process in aligned blocks. Specialise for each possible carry count */ + end = ptr + len/4*4; + switch(n) { /* how many bytes in c */ + case 0: /* c=[----] w=[3210] b=[3210]=w c'=[----] */ + for( ; ptr < end ; ptr+=4) { + uint32_t k1 = READ_UINT32(ptr); + DOBLOCK(h1, k1); + } + break; + case 1: /* c=[0---] w=[4321] b=[3210]=c>>24|w<<8 c'=[4---] */ + for( ; ptr < end ; ptr+=4) { + uint32_t k1 = c>>24; + c = READ_UINT32(ptr); + k1 |= c<<8; + DOBLOCK(h1, k1); + } + break; + case 2: /* c=[10--] w=[5432] b=[3210]=c>>16|w<<16 c'=[54--] */ + for( ; ptr < end ; ptr+=4) { + uint32_t k1 = c>>16; + c = READ_UINT32(ptr); + k1 |= c<<16; + DOBLOCK(h1, k1); + } + break; + case 3: /* c=[210-] w=[6543] b=[3210]=c>>8|w<<24 c'=[654-] */ + for( ; ptr < end ; ptr+=4) { + uint32_t k1 = c>>8; + c = READ_UINT32(ptr); + k1 |= c<<24; + DOBLOCK(h1, k1); + } + } +#endif /*UNALIGNED_SAFE*/ + + /* Advance over whole 32-bit chunks, possibly leaving 1..3 bytes */ + len -= len/4*4; + + /* Append any remaining bytes into carry */ + DOBYTES(len, h1, c, n, ptr, len); + + /* Copy out new running hash and carry */ + *ph1 = h1; + *pcarry = (c & ~0xff) | n; +} + +/*---------------------------------------------------------------------------*/ + +/* Finalize a hash. To match the original Murmur3A the total_length must be provided */ +uint32_t PMurHash32_Result(uint32_t h, uint32_t carry, uint32_t total_length) +{ + uint32_t k1; + int n = carry & 3; + if(n) { + k1 = carry >> (4-n)*8; + k1 *= C1; k1 = ROTL32(k1,15); k1 *= C2; h ^= k1; + } + h ^= total_length; + + /* fmix */ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +/*---------------------------------------------------------------------------*/ + +/* Murmur3A compatable all-at-once */ +uint32_t PMurHash32(uint32_t seed, const void *key, int len) +{ + uint32_t h1=seed, carry=0; + PMurHash32_Process(&h1, &carry, key, len); + return PMurHash32_Result(h1, carry, len); +} + +/*---------------------------------------------------------------------------*/ + +/* Provide an API suitable for smhasher */ +void PMurHash32_test(const void *key, int len, uint32_t seed, void *out) +{ + uint32_t h1=seed, carry=0; + const uint8_t *ptr = (uint8_t*)key; + const uint8_t *end = ptr + len; + +#if 0 /* Exercise the progressive processing */ + while(ptr < end) { + //const uint8_t *mid = ptr + rand()%(end-ptr)+1; + const uint8_t *mid = ptr + (rand()&0xF); + mid = mid<end?mid:end; + PMurHash32_Process(&h1, &carry, ptr, mid-ptr); + ptr = mid; + } +#else + PMurHash32_Process(&h1, &carry, ptr, (int)(end-ptr)); +#endif + h1 = PMurHash32_Result(h1, carry, len); + *(uint32_t*)out = h1; +} +} + +/*---------------------------------------------------------------------------*/ diff --git a/src/3rdparty/angle/src/common/third_party/smhasher/src/PMurHash.h b/src/3rdparty/angle/src/common/third_party/smhasher/src/PMurHash.h new file mode 100644 index 0000000000..57c56c538d --- /dev/null +++ b/src/3rdparty/angle/src/common/third_party/smhasher/src/PMurHash.h @@ -0,0 +1,59 @@ +/*----------------------------------------------------------------------------- + * MurmurHash3 was written by Austin Appleby, and is placed in the public + * domain. + * + * This implementation was written by Shane Day, and is also public domain. + * + * This is a portable ANSI C implementation of MurmurHash3_x86_32 (Murmur3A) + * with support for progressive processing. + */ + +/* ------------------------------------------------------------------------- */ +/* Determine what native type to use for uint32_t */ + +/* We can't use the name 'uint32_t' here because it will conflict with + * any version provided by the system headers or application. */ + +/* First look for special cases */ +#if defined(_MSC_VER) + #define MH_UINT32 unsigned long +#endif + +/* If the compiler says it's C99 then take its word for it */ +#if !defined(MH_UINT32) && ( \ + defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L ) + #include <stdint.h> + #define MH_UINT32 uint32_t +#endif + +/* Otherwise try testing against max value macros from limit.h */ +#if !defined(MH_UINT32) + #include <limits.h> + #if (USHRT_MAX == 0xffffffffUL) + #define MH_UINT32 unsigned short + #elif (UINT_MAX == 0xffffffffUL) + #define MH_UINT32 unsigned int + #elif (ULONG_MAX == 0xffffffffUL) + #define MH_UINT32 unsigned long + #endif +#endif + +#if !defined(MH_UINT32) + #error Unable to determine type name for unsigned 32-bit int +#endif + +/* I'm yet to work on a platform where 'unsigned char' is not 8 bits */ +#define MH_UINT8 unsigned char + + +/* ------------------------------------------------------------------------- */ +/* Prototypes */ + +namespace angle +{ +void PMurHash32_Process(MH_UINT32 *ph1, MH_UINT32 *pcarry, const void *key, int len); +MH_UINT32 PMurHash32_Result(MH_UINT32 h1, MH_UINT32 carry, MH_UINT32 total_length); +MH_UINT32 PMurHash32(MH_UINT32 seed, const void *key, int len); + +void PMurHash32_test(const void *key, int len, MH_UINT32 seed, void *out); +} diff --git a/src/3rdparty/angle/src/common/tls.cpp b/src/3rdparty/angle/src/common/tls.cpp index cb1b32d325..10a10252f4 100644 --- a/src/3rdparty/angle/src/common/tls.cpp +++ b/src/3rdparty/angle/src/common/tls.cpp @@ -56,7 +56,7 @@ TLSIndex CreateTLSIndex() #elif defined(ANGLE_PLATFORM_POSIX) // Create global pool key - if ((pthread_key_create(&index, NULL)) != 0) + if ((pthread_key_create(&index, nullptr)) != 0) { index = TLS_INVALID_INDEX; } @@ -133,7 +133,7 @@ void *GetTLSValue(TLSIndex index) assert(index != TLS_INVALID_INDEX && "GetTLSValue(): Invalid TLS Index"); if (index == TLS_INVALID_INDEX) { - return NULL; + return nullptr; } #ifdef ANGLE_PLATFORM_WINDOWS diff --git a/src/3rdparty/angle/src/common/uniform_type_info_autogen.cpp b/src/3rdparty/angle/src/common/uniform_type_info_autogen.cpp new file mode 100644 index 0000000000..9c199128a4 --- /dev/null +++ b/src/3rdparty/angle/src/common/uniform_type_info_autogen.cpp @@ -0,0 +1,275 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by gen_uniform_type_table.py. +// +// Copyright 2017 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. +// +// Uniform type info table: +// Metadata about a particular uniform format, indexed by GL type. + +#include <array> +#include "common/utilities.h" + +using namespace angle; + +namespace gl +{ + +namespace +{ +constexpr std::array<UniformTypeInfo, 59> kInfoTable = { + {{GL_NONE, GL_NONE, GL_NONE, GL_NONE, GL_NONE, 0, 0, 0, 0, 0 * 0, 0 * 0, false, false, false}, + {GL_BOOL, GL_BOOL, GL_NONE, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), sizeof(GLint) * 4, + sizeof(GLint) * 1, false, false, false}, + {GL_BOOL_VEC2, GL_BOOL, GL_NONE, GL_NONE, GL_BOOL_VEC2, 1, 2, 2, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 2, false, false, false}, + {GL_BOOL_VEC3, GL_BOOL, GL_NONE, GL_NONE, GL_BOOL_VEC3, 1, 3, 3, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 3, false, false, false}, + {GL_BOOL_VEC4, GL_BOOL, GL_NONE, GL_NONE, GL_BOOL_VEC4, 1, 4, 4, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 4, false, false, false}, + {GL_FLOAT, GL_FLOAT, GL_NONE, GL_NONE, GL_BOOL, 1, 1, 1, sizeof(GLfloat), sizeof(GLfloat) * 4, + sizeof(GLfloat) * 1, false, false, false}, + {GL_FLOAT_MAT2, GL_FLOAT, GL_NONE, GL_FLOAT_MAT2, GL_NONE, 2, 2, 4, sizeof(GLfloat), + sizeof(GLfloat) * 8, sizeof(GLfloat) * 4, false, true, false}, + {GL_FLOAT_MAT2x3, GL_FLOAT, GL_NONE, GL_FLOAT_MAT3x2, GL_NONE, 3, 2, 6, sizeof(GLfloat), + sizeof(GLfloat) * 12, sizeof(GLfloat) * 6, false, true, false}, + {GL_FLOAT_MAT2x4, GL_FLOAT, GL_NONE, GL_FLOAT_MAT4x2, GL_NONE, 4, 2, 8, sizeof(GLfloat), + sizeof(GLfloat) * 16, sizeof(GLfloat) * 8, false, true, false}, + {GL_FLOAT_MAT3, GL_FLOAT, GL_NONE, GL_FLOAT_MAT3, GL_NONE, 3, 3, 9, sizeof(GLfloat), + sizeof(GLfloat) * 12, sizeof(GLfloat) * 9, false, true, false}, + {GL_FLOAT_MAT3x2, GL_FLOAT, GL_NONE, GL_FLOAT_MAT2x3, GL_NONE, 2, 3, 6, sizeof(GLfloat), + sizeof(GLfloat) * 8, sizeof(GLfloat) * 6, false, true, false}, + {GL_FLOAT_MAT3x4, GL_FLOAT, GL_NONE, GL_FLOAT_MAT4x3, GL_NONE, 4, 3, 12, sizeof(GLfloat), + sizeof(GLfloat) * 16, sizeof(GLfloat) * 12, false, true, false}, + {GL_FLOAT_MAT4, GL_FLOAT, GL_NONE, GL_FLOAT_MAT4, GL_NONE, 4, 4, 16, sizeof(GLfloat), + sizeof(GLfloat) * 16, sizeof(GLfloat) * 16, false, true, false}, + {GL_FLOAT_MAT4x2, GL_FLOAT, GL_NONE, GL_FLOAT_MAT2x4, GL_NONE, 2, 4, 8, sizeof(GLfloat), + sizeof(GLfloat) * 8, sizeof(GLfloat) * 8, false, true, false}, + {GL_FLOAT_MAT4x3, GL_FLOAT, GL_NONE, GL_FLOAT_MAT3x4, GL_NONE, 3, 4, 12, sizeof(GLfloat), + sizeof(GLfloat) * 12, sizeof(GLfloat) * 12, false, true, false}, + {GL_FLOAT_VEC2, GL_FLOAT, GL_NONE, GL_NONE, GL_BOOL_VEC2, 1, 2, 2, sizeof(GLfloat), + sizeof(GLfloat) * 4, sizeof(GLfloat) * 2, false, false, false}, + {GL_FLOAT_VEC3, GL_FLOAT, GL_NONE, GL_NONE, GL_BOOL_VEC3, 1, 3, 3, sizeof(GLfloat), + sizeof(GLfloat) * 4, sizeof(GLfloat) * 3, false, false, false}, + {GL_FLOAT_VEC4, GL_FLOAT, GL_NONE, GL_NONE, GL_BOOL_VEC4, 1, 4, 4, sizeof(GLfloat), + sizeof(GLfloat) * 4, sizeof(GLfloat) * 4, false, false, false}, + {GL_IMAGE_2D, GL_INT, GL_TEXTURE_2D, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, false, false, true}, + {GL_IMAGE_2D_ARRAY, GL_INT, GL_TEXTURE_2D_ARRAY, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, false, false, true}, + {GL_IMAGE_3D, GL_INT, GL_TEXTURE_3D, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, false, false, true}, + {GL_IMAGE_CUBE, GL_INT, GL_TEXTURE_CUBE_MAP, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, false, false, true}, + {GL_INT, GL_INT, GL_NONE, GL_NONE, GL_BOOL, 1, 1, 1, sizeof(GLint), sizeof(GLint) * 4, + sizeof(GLint) * 1, false, false, false}, + {GL_INT_IMAGE_2D, GL_INT, GL_TEXTURE_2D, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, false, false, true}, + {GL_INT_IMAGE_2D_ARRAY, GL_INT, GL_TEXTURE_2D_ARRAY, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, false, false, true}, + {GL_INT_IMAGE_3D, GL_INT, GL_TEXTURE_3D, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, false, false, true}, + {GL_INT_IMAGE_CUBE, GL_INT, GL_TEXTURE_CUBE_MAP, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, false, false, true}, + {GL_INT_SAMPLER_2D, GL_INT, GL_TEXTURE_2D, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_INT_SAMPLER_2D_ARRAY, GL_INT, GL_TEXTURE_2D_ARRAY, GL_NONE, GL_NONE, 1, 1, 1, + sizeof(GLint), sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_INT_SAMPLER_2D_MULTISAMPLE, GL_INT, GL_TEXTURE_2D_MULTISAMPLE, GL_NONE, GL_NONE, 1, 1, 1, + sizeof(GLint), sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_INT_SAMPLER_3D, GL_INT, GL_TEXTURE_3D, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_INT_SAMPLER_CUBE, GL_INT, GL_TEXTURE_CUBE_MAP, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_INT_VEC2, GL_INT, GL_NONE, GL_NONE, GL_BOOL_VEC2, 1, 2, 2, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 2, false, false, false}, + {GL_INT_VEC3, GL_INT, GL_NONE, GL_NONE, GL_BOOL_VEC3, 1, 3, 3, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 3, false, false, false}, + {GL_INT_VEC4, GL_INT, GL_NONE, GL_NONE, GL_BOOL_VEC4, 1, 4, 4, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 4, false, false, false}, + {GL_SAMPLER_2D, GL_INT, GL_TEXTURE_2D, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_SAMPLER_2D_ARRAY, GL_INT, GL_TEXTURE_2D_ARRAY, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_SAMPLER_2D_ARRAY_SHADOW, GL_INT, GL_TEXTURE_2D_ARRAY, GL_NONE, GL_NONE, 1, 1, 1, + sizeof(GLint), sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_SAMPLER_2D_MULTISAMPLE, GL_INT, GL_TEXTURE_2D_MULTISAMPLE, GL_NONE, GL_NONE, 1, 1, 1, + sizeof(GLint), sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_SAMPLER_2D_RECT_ANGLE, GL_INT, GL_TEXTURE_2D, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_SAMPLER_2D_SHADOW, GL_INT, GL_TEXTURE_2D, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_SAMPLER_3D, GL_INT, GL_TEXTURE_3D, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_SAMPLER_CUBE, GL_INT, GL_TEXTURE_CUBE_MAP, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_SAMPLER_CUBE_SHADOW, GL_INT, GL_TEXTURE_CUBE_MAP, GL_NONE, GL_NONE, 1, 1, 1, sizeof(GLint), + sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_SAMPLER_EXTERNAL_OES, GL_INT, GL_TEXTURE_EXTERNAL_OES, GL_NONE, GL_NONE, 1, 1, 1, + sizeof(GLint), sizeof(GLint) * 4, sizeof(GLint) * 1, true, false, false}, + {GL_UNSIGNED_INT, GL_UNSIGNED_INT, GL_NONE, GL_NONE, GL_BOOL, 1, 1, 1, sizeof(GLuint), + sizeof(GLuint) * 4, sizeof(GLuint) * 1, false, false, false}, + {GL_UNSIGNED_INT_ATOMIC_COUNTER, GL_UNSIGNED_INT, GL_NONE, GL_NONE, GL_NONE, 1, 1, 1, + sizeof(GLuint), sizeof(GLuint) * 4, sizeof(GLuint) * 1, false, false, false}, + {GL_UNSIGNED_INT_IMAGE_2D, GL_UNSIGNED_INT, GL_TEXTURE_2D, GL_NONE, GL_NONE, 1, 1, 1, + sizeof(GLuint), sizeof(GLuint) * 4, sizeof(GLuint) * 1, false, false, true}, + {GL_UNSIGNED_INT_IMAGE_2D_ARRAY, GL_UNSIGNED_INT, GL_TEXTURE_2D_ARRAY, GL_NONE, GL_NONE, 1, 1, + 1, sizeof(GLuint), sizeof(GLuint) * 4, sizeof(GLuint) * 1, false, false, true}, + {GL_UNSIGNED_INT_IMAGE_3D, GL_UNSIGNED_INT, GL_TEXTURE_3D, GL_NONE, GL_NONE, 1, 1, 1, + sizeof(GLuint), sizeof(GLuint) * 4, sizeof(GLuint) * 1, false, false, true}, + {GL_UNSIGNED_INT_IMAGE_CUBE, GL_UNSIGNED_INT, GL_TEXTURE_CUBE_MAP, GL_NONE, GL_NONE, 1, 1, 1, + sizeof(GLuint), sizeof(GLuint) * 4, sizeof(GLuint) * 1, false, false, true}, + {GL_UNSIGNED_INT_SAMPLER_2D, GL_UNSIGNED_INT, GL_TEXTURE_2D, GL_NONE, GL_NONE, 1, 1, 1, + sizeof(GLuint), sizeof(GLuint) * 4, sizeof(GLuint) * 1, true, false, false}, + {GL_UNSIGNED_INT_SAMPLER_2D_ARRAY, GL_UNSIGNED_INT, GL_TEXTURE_2D_ARRAY, GL_NONE, GL_NONE, 1, + 1, 1, sizeof(GLuint), sizeof(GLuint) * 4, sizeof(GLuint) * 1, true, false, false}, + {GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE, GL_UNSIGNED_INT, GL_TEXTURE_2D_MULTISAMPLE, GL_NONE, + GL_NONE, 1, 1, 1, sizeof(GLuint), sizeof(GLuint) * 4, sizeof(GLuint) * 1, true, false, false}, + {GL_UNSIGNED_INT_SAMPLER_3D, GL_UNSIGNED_INT, GL_TEXTURE_3D, GL_NONE, GL_NONE, 1, 1, 1, + sizeof(GLuint), sizeof(GLuint) * 4, sizeof(GLuint) * 1, true, false, false}, + {GL_UNSIGNED_INT_SAMPLER_CUBE, GL_UNSIGNED_INT, GL_TEXTURE_CUBE_MAP, GL_NONE, GL_NONE, 1, 1, 1, + sizeof(GLuint), sizeof(GLuint) * 4, sizeof(GLuint) * 1, true, false, false}, + {GL_UNSIGNED_INT_VEC2, GL_UNSIGNED_INT, GL_NONE, GL_NONE, GL_BOOL_VEC2, 1, 2, 2, + sizeof(GLuint), sizeof(GLuint) * 4, sizeof(GLuint) * 2, false, false, false}, + {GL_UNSIGNED_INT_VEC3, GL_UNSIGNED_INT, GL_NONE, GL_NONE, GL_BOOL_VEC3, 1, 3, 3, + sizeof(GLuint), sizeof(GLuint) * 4, sizeof(GLuint) * 3, false, false, false}, + {GL_UNSIGNED_INT_VEC4, GL_UNSIGNED_INT, GL_NONE, GL_NONE, GL_BOOL_VEC4, 1, 4, 4, + sizeof(GLuint), sizeof(GLuint) * 4, sizeof(GLuint) * 4, false, false, false}}}; + +size_t GetTypeInfoIndex(GLenum uniformType) +{ + switch (uniformType) + { + case GL_NONE: + return 0; + case GL_BOOL: + return 1; + case GL_BOOL_VEC2: + return 2; + case GL_BOOL_VEC3: + return 3; + case GL_BOOL_VEC4: + return 4; + case GL_FLOAT: + return 5; + case GL_FLOAT_MAT2: + return 6; + case GL_FLOAT_MAT2x3: + return 7; + case GL_FLOAT_MAT2x4: + return 8; + case GL_FLOAT_MAT3: + return 9; + case GL_FLOAT_MAT3x2: + return 10; + case GL_FLOAT_MAT3x4: + return 11; + case GL_FLOAT_MAT4: + return 12; + case GL_FLOAT_MAT4x2: + return 13; + case GL_FLOAT_MAT4x3: + return 14; + case GL_FLOAT_VEC2: + return 15; + case GL_FLOAT_VEC3: + return 16; + case GL_FLOAT_VEC4: + return 17; + case GL_IMAGE_2D: + return 18; + case GL_IMAGE_2D_ARRAY: + return 19; + case GL_IMAGE_3D: + return 20; + case GL_IMAGE_CUBE: + return 21; + case GL_INT: + return 22; + case GL_INT_IMAGE_2D: + return 23; + case GL_INT_IMAGE_2D_ARRAY: + return 24; + case GL_INT_IMAGE_3D: + return 25; + case GL_INT_IMAGE_CUBE: + return 26; + case GL_INT_SAMPLER_2D: + return 27; + case GL_INT_SAMPLER_2D_ARRAY: + return 28; + case GL_INT_SAMPLER_2D_MULTISAMPLE: + return 29; + case GL_INT_SAMPLER_3D: + return 30; + case GL_INT_SAMPLER_CUBE: + return 31; + case GL_INT_VEC2: + return 32; + case GL_INT_VEC3: + return 33; + case GL_INT_VEC4: + return 34; + case GL_SAMPLER_2D: + return 35; + case GL_SAMPLER_2D_ARRAY: + return 36; + case GL_SAMPLER_2D_ARRAY_SHADOW: + return 37; + case GL_SAMPLER_2D_MULTISAMPLE: + return 38; + case GL_SAMPLER_2D_RECT_ANGLE: + return 39; + case GL_SAMPLER_2D_SHADOW: + return 40; + case GL_SAMPLER_3D: + return 41; + case GL_SAMPLER_CUBE: + return 42; + case GL_SAMPLER_CUBE_SHADOW: + return 43; + case GL_SAMPLER_EXTERNAL_OES: + return 44; + case GL_UNSIGNED_INT: + return 45; + case GL_UNSIGNED_INT_ATOMIC_COUNTER: + return 46; + case GL_UNSIGNED_INT_IMAGE_2D: + return 47; + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + return 48; + case GL_UNSIGNED_INT_IMAGE_3D: + return 49; + case GL_UNSIGNED_INT_IMAGE_CUBE: + return 50; + case GL_UNSIGNED_INT_SAMPLER_2D: + return 51; + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + return 52; + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + return 53; + case GL_UNSIGNED_INT_SAMPLER_3D: + return 54; + case GL_UNSIGNED_INT_SAMPLER_CUBE: + return 55; + case GL_UNSIGNED_INT_VEC2: + return 56; + case GL_UNSIGNED_INT_VEC3: + return 57; + case GL_UNSIGNED_INT_VEC4: + return 58; + default: + UNREACHABLE(); + return 0; + } +} +} // anonymous namespace + +const UniformTypeInfo &GetUniformTypeInfo(GLenum uniformType) +{ + ASSERT(kInfoTable[GetTypeInfoIndex(uniformType)].type == uniformType); + return kInfoTable[GetTypeInfoIndex(uniformType)]; +} + +} // namespace gl diff --git a/src/3rdparty/angle/src/common/utilities.cpp b/src/3rdparty/angle/src/common/utilities.cpp index 2ab913b10f..6dae9cc51f 100644 --- a/src/3rdparty/angle/src/common/utilities.cpp +++ b/src/3rdparty/angle/src/common/utilities.cpp @@ -124,24 +124,42 @@ GLenum VariableComponentType(GLenum type) return GL_FLOAT; case GL_INT: case GL_SAMPLER_2D: + case GL_SAMPLER_2D_RECT_ANGLE: case GL_SAMPLER_3D: case GL_SAMPLER_CUBE: case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_EXTERNAL_OES: + case GL_SAMPLER_2D_MULTISAMPLE: case GL_INT_SAMPLER_2D: case GL_INT_SAMPLER_3D: case GL_INT_SAMPLER_CUBE: case GL_INT_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE: case GL_UNSIGNED_INT_SAMPLER_2D: case GL_UNSIGNED_INT_SAMPLER_3D: case GL_UNSIGNED_INT_SAMPLER_CUBE: case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: case GL_SAMPLER_2D_SHADOW: case GL_SAMPLER_CUBE_SHADOW: case GL_SAMPLER_2D_ARRAY_SHADOW: case GL_INT_VEC2: case GL_INT_VEC3: case GL_INT_VEC4: - return GL_INT; + case GL_IMAGE_2D: + case GL_INT_IMAGE_2D: + case GL_UNSIGNED_INT_IMAGE_2D: + case GL_IMAGE_3D: + case GL_INT_IMAGE_3D: + case GL_UNSIGNED_INT_IMAGE_3D: + case GL_IMAGE_2D_ARRAY: + case GL_INT_IMAGE_2D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + case GL_IMAGE_CUBE: + case GL_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_ATOMIC_COUNTER: + return GL_INT; case GL_UNSIGNED_INT: case GL_UNSIGNED_INT_VEC2: case GL_UNSIGNED_INT_VEC3: @@ -211,7 +229,6 @@ int VariableRowCount(GLenum type) switch (type) { case GL_NONE: - case GL_STRUCT_ANGLEX: return 0; case GL_BOOL: case GL_FLOAT: @@ -234,19 +251,35 @@ int VariableRowCount(GLenum type) case GL_SAMPLER_CUBE: case GL_SAMPLER_2D_ARRAY: case GL_SAMPLER_EXTERNAL_OES: - case GL_SAMPLER_2D_RECT_ARB: + case GL_SAMPLER_2D_RECT_ANGLE: + case GL_SAMPLER_2D_MULTISAMPLE: case GL_INT_SAMPLER_2D: case GL_INT_SAMPLER_3D: case GL_INT_SAMPLER_CUBE: case GL_INT_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE: case GL_UNSIGNED_INT_SAMPLER_2D: case GL_UNSIGNED_INT_SAMPLER_3D: case GL_UNSIGNED_INT_SAMPLER_CUBE: case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: case GL_SAMPLER_2D_SHADOW: case GL_SAMPLER_CUBE_SHADOW: case GL_SAMPLER_2D_ARRAY_SHADOW: - return 1; + case GL_IMAGE_2D: + case GL_INT_IMAGE_2D: + case GL_UNSIGNED_INT_IMAGE_2D: + case GL_IMAGE_2D_ARRAY: + case GL_INT_IMAGE_2D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + case GL_IMAGE_3D: + case GL_INT_IMAGE_3D: + case GL_UNSIGNED_INT_IMAGE_3D: + case GL_IMAGE_CUBE: + case GL_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_ATOMIC_COUNTER: + return 1; case GL_FLOAT_MAT2: case GL_FLOAT_MAT3x2: case GL_FLOAT_MAT4x2: @@ -271,7 +304,6 @@ int VariableColumnCount(GLenum type) switch (type) { case GL_NONE: - case GL_STRUCT_ANGLEX: return 0; case GL_BOOL: case GL_FLOAT: @@ -281,20 +313,36 @@ int VariableColumnCount(GLenum type) case GL_SAMPLER_3D: case GL_SAMPLER_CUBE: case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_2D_MULTISAMPLE: case GL_INT_SAMPLER_2D: case GL_INT_SAMPLER_3D: case GL_INT_SAMPLER_CUBE: case GL_INT_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE: case GL_SAMPLER_EXTERNAL_OES: - case GL_SAMPLER_2D_RECT_ARB: + case GL_SAMPLER_2D_RECT_ANGLE: case GL_UNSIGNED_INT_SAMPLER_2D: case GL_UNSIGNED_INT_SAMPLER_3D: case GL_UNSIGNED_INT_SAMPLER_CUBE: case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: case GL_SAMPLER_2D_SHADOW: case GL_SAMPLER_CUBE_SHADOW: case GL_SAMPLER_2D_ARRAY_SHADOW: - return 1; + case GL_IMAGE_2D: + case GL_INT_IMAGE_2D: + case GL_UNSIGNED_INT_IMAGE_2D: + case GL_IMAGE_3D: + case GL_INT_IMAGE_3D: + case GL_UNSIGNED_INT_IMAGE_3D: + case GL_IMAGE_2D_ARRAY: + case GL_INT_IMAGE_2D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + case GL_IMAGE_CUBE: + case GL_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_ATOMIC_COUNTER: + return 1; case GL_BOOL_VEC2: case GL_FLOAT_VEC2: case GL_INT_VEC2: @@ -334,14 +382,19 @@ bool IsSamplerType(GLenum type) case GL_SAMPLER_3D: case GL_SAMPLER_CUBE: case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_EXTERNAL_OES: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_RECT_ANGLE: case GL_INT_SAMPLER_2D: case GL_INT_SAMPLER_3D: case GL_INT_SAMPLER_CUBE: case GL_INT_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE: case GL_UNSIGNED_INT_SAMPLER_2D: case GL_UNSIGNED_INT_SAMPLER_3D: case GL_UNSIGNED_INT_SAMPLER_CUBE: case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: case GL_SAMPLER_2D_SHADOW: case GL_SAMPLER_CUBE_SHADOW: case GL_SAMPLER_2D_ARRAY_SHADOW: @@ -351,6 +404,38 @@ bool IsSamplerType(GLenum type) return false; } +bool IsImageType(GLenum type) +{ + switch (type) + { + case GL_IMAGE_2D: + case GL_INT_IMAGE_2D: + case GL_UNSIGNED_INT_IMAGE_2D: + case GL_IMAGE_3D: + case GL_INT_IMAGE_3D: + case GL_UNSIGNED_INT_IMAGE_3D: + case GL_IMAGE_2D_ARRAY: + case GL_INT_IMAGE_2D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + case GL_IMAGE_CUBE: + case GL_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_IMAGE_CUBE: + return true; + } + return false; +} + +bool IsAtomicCounterType(GLenum type) +{ + return type == GL_UNSIGNED_INT_ATOMIC_COUNTER; +} + +bool IsOpaqueType(GLenum type) +{ + // ESSL 3.10 section 4.1.7 defines opaque types as: samplers, images and atomic counters. + return IsImageType(type) || IsSamplerType(type) || IsAtomicCounterType(type); +} + GLenum SamplerTypeToTextureType(GLenum samplerType) { switch (samplerType) @@ -361,6 +446,9 @@ GLenum SamplerTypeToTextureType(GLenum samplerType) case GL_SAMPLER_2D_SHADOW: return GL_TEXTURE_2D; + case GL_SAMPLER_EXTERNAL_OES: + return GL_TEXTURE_EXTERNAL_OES; + case GL_SAMPLER_CUBE: case GL_INT_SAMPLER_CUBE: case GL_UNSIGNED_INT_SAMPLER_CUBE: @@ -378,6 +466,14 @@ GLenum SamplerTypeToTextureType(GLenum samplerType) case GL_UNSIGNED_INT_SAMPLER_3D: return GL_TEXTURE_3D; + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + return GL_TEXTURE_2D_MULTISAMPLE; + + case GL_SAMPLER_2D_RECT_ANGLE: + return GL_TEXTURE_RECTANGLE_ANGLE; + default: UNREACHABLE(); return 0; @@ -531,6 +627,21 @@ bool IsTriangleMode(GLenum drawMode) return false; } +bool IsIntegerFormat(GLenum unsizedFormat) +{ + switch (unsizedFormat) + { + case GL_RGBA_INTEGER: + case GL_RGB_INTEGER: + case GL_RG_INTEGER: + case GL_RED_INTEGER: + return true; + + default: + return false; + } +} + // [OpenGL ES SL 3.00.4] Section 11 p. 120 // Vertex Outs/Fragment Ins packing priorities int VariableSortOrder(GLenum type) @@ -586,21 +697,37 @@ int VariableSortOrder(GLenum type) case GL_SAMPLER_2D: case GL_SAMPLER_CUBE: case GL_SAMPLER_EXTERNAL_OES: - case GL_SAMPLER_2D_RECT_ARB: + case GL_SAMPLER_2D_RECT_ANGLE: case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_2D_MULTISAMPLE: case GL_SAMPLER_3D: case GL_INT_SAMPLER_2D: case GL_INT_SAMPLER_3D: case GL_INT_SAMPLER_CUBE: case GL_INT_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE: case GL_UNSIGNED_INT_SAMPLER_2D: case GL_UNSIGNED_INT_SAMPLER_3D: case GL_UNSIGNED_INT_SAMPLER_CUBE: case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: case GL_SAMPLER_2D_SHADOW: case GL_SAMPLER_2D_ARRAY_SHADOW: case GL_SAMPLER_CUBE_SHADOW: - return 6; + case GL_IMAGE_2D: + case GL_INT_IMAGE_2D: + case GL_UNSIGNED_INT_IMAGE_2D: + case GL_IMAGE_3D: + case GL_INT_IMAGE_3D: + case GL_UNSIGNED_INT_IMAGE_3D: + case GL_IMAGE_2D_ARRAY: + case GL_INT_IMAGE_2D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + case GL_IMAGE_CUBE: + case GL_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_ATOMIC_COUNTER: + return 6; default: UNREACHABLE(); @@ -608,51 +735,128 @@ int VariableSortOrder(GLenum type) } } -std::string ParseUniformName(const std::string &name, size_t *outSubscript) +std::string ParseResourceName(const std::string &name, std::vector<unsigned int> *outSubscripts) { - // Strip any trailing array operator and retrieve the subscript - size_t open = name.find_last_of('['); - size_t close = name.find_last_of(']'); - bool hasIndex = (open != std::string::npos) && (close == name.length() - 1); - if (!hasIndex) + if (outSubscripts) { - if (outSubscript) + outSubscripts->clear(); + } + // Strip any trailing array indexing operators and retrieve the subscripts. + size_t baseNameLength = name.length(); + bool hasIndex = true; + while (hasIndex) + { + size_t open = name.find_last_of('[', baseNameLength - 1); + size_t close = name.find_last_of(']', baseNameLength - 1); + hasIndex = (open != std::string::npos) && (close == baseNameLength - 1); + if (hasIndex) { - *outSubscript = GL_INVALID_INDEX; + baseNameLength = open; + if (outSubscripts) + { + int index = atoi(name.substr(open + 1).c_str()); + if (index >= 0) + { + outSubscripts->push_back(index); + } + else + { + outSubscripts->push_back(GL_INVALID_INDEX); + } + } } - return name; } - if (outSubscript) + return name.substr(0, baseNameLength); +} + +unsigned int ArraySizeProduct(const std::vector<unsigned int> &arraySizes) +{ + unsigned int arraySizeProduct = 1u; + for (unsigned int arraySize : arraySizes) + { + arraySizeProduct *= arraySize; + } + return arraySizeProduct; +} + +unsigned int ParseArrayIndex(const std::string &name, size_t *nameLengthWithoutArrayIndexOut) +{ + ASSERT(nameLengthWithoutArrayIndexOut != nullptr); + + // Strip any trailing array operator and retrieve the subscript + size_t open = name.find_last_of('['); + if (open != std::string::npos && name.back() == ']') { - int index = atoi(name.substr(open + 1).c_str()); - if (index >= 0) + bool indexIsValidDecimalNumber = true; + for (size_t i = open + 1; i < name.length() - 1u; ++i) { - *outSubscript = index; + if (!isdigit(name[i])) + { + indexIsValidDecimalNumber = false; + break; + } } - else + if (indexIsValidDecimalNumber) { - *outSubscript = GL_INVALID_INDEX; + errno = 0; // reset global error flag. + unsigned long subscript = + strtoul(name.c_str() + open + 1, /*endptr*/ nullptr, /*radix*/ 10); + + // Check if resulting integer is out-of-range or conversion error. + if ((subscript <= static_cast<unsigned long>(UINT_MAX)) && + !(subscript == ULONG_MAX && errno == ERANGE) && !(errno != 0 && subscript == 0)) + { + *nameLengthWithoutArrayIndexOut = open; + return static_cast<unsigned int>(subscript); + } } } - return name.substr(0, open); + *nameLengthWithoutArrayIndexOut = name.length(); + return GL_INVALID_INDEX; } -unsigned int ParseAndStripArrayIndex(std::string *name) +const char *GetGenericErrorMessage(GLenum error) { - unsigned int subscript = GL_INVALID_INDEX; - - // Strip any trailing array operator and retrieve the subscript - size_t open = name->find_last_of('['); - size_t close = name->find_last_of(']'); - if (open != std::string::npos && close == name->length() - 1) + switch (error) { - subscript = atoi(name->c_str() + open + 1); - name->erase(open); + case GL_NO_ERROR: + return ""; + case GL_INVALID_ENUM: + return "Invalid enum."; + case GL_INVALID_VALUE: + return "Invalid value."; + case GL_INVALID_OPERATION: + return "Invalid operation."; + case GL_STACK_OVERFLOW: + return "Stack overflow."; + case GL_STACK_UNDERFLOW: + return "Stack underflow."; + case GL_OUT_OF_MEMORY: + return "Out of memory."; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "Invalid framebuffer operation."; + default: + UNREACHABLE(); + return "Unknown error."; } +} - return subscript; +unsigned int ElementTypeSize(GLenum elementType) +{ + switch (elementType) + { + case GL_UNSIGNED_BYTE: + return sizeof(GLubyte); + case GL_UNSIGNED_SHORT: + return sizeof(GLushort); + case GL_UNSIGNED_INT: + return sizeof(GLuint); + default: + UNREACHABLE(); + return 0; + } } } // namespace gl @@ -710,8 +914,53 @@ bool IsRenderbufferTarget(EGLenum target) { return target == EGL_GL_RENDERBUFFER_KHR; } + +const char *GetGenericErrorMessage(EGLint error) +{ + switch (error) + { + case EGL_SUCCESS: + return ""; + case EGL_NOT_INITIALIZED: + return "Not initialized."; + case EGL_BAD_ACCESS: + return "Bad access."; + case EGL_BAD_ALLOC: + return "Bad allocation."; + case EGL_BAD_ATTRIBUTE: + return "Bad attribute."; + case EGL_BAD_CONFIG: + return "Bad config."; + case EGL_BAD_CONTEXT: + return "Bad context."; + case EGL_BAD_CURRENT_SURFACE: + return "Bad current surface."; + case EGL_BAD_DISPLAY: + return "Bad display."; + case EGL_BAD_MATCH: + return "Bad match."; + case EGL_BAD_NATIVE_WINDOW: + return "Bad native window."; + case EGL_BAD_PARAMETER: + return "Bad parameter."; + case EGL_BAD_SURFACE: + return "Bad surface."; + case EGL_CONTEXT_LOST: + return "Context lost."; + case EGL_BAD_STREAM_KHR: + return "Bad stream."; + case EGL_BAD_STATE_KHR: + return "Bad state."; + case EGL_BAD_DEVICE_EXT: + return "Bad device."; + default: + UNREACHABLE(); + return "Unknown error."; + } } +} // namespace egl + namespace egl_gl { GLenum EGLCubeMapTargetToGLCubeMapTarget(EGLenum eglTarget) @@ -748,7 +997,26 @@ GLuint EGLClientBufferToGLObjectHandle(EGLClientBuffer buffer) { return static_cast<GLuint>(reinterpret_cast<uintptr_t>(buffer)); } +} // namespace egl_gl + +namespace gl_egl +{ +EGLenum GLComponentTypeToEGLColorComponentType(GLenum glComponentType) +{ + switch (glComponentType) + { + case GL_FLOAT: + return EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT; + + case GL_UNSIGNED_NORMALIZED: + return EGL_COLOR_COMPONENT_TYPE_FIXED_EXT; + + default: + UNREACHABLE(); + return EGL_NONE; + } } +} // namespace gl_egl #if !defined(ANGLE_ENABLE_WINDOWS_STORE) std::string getTempPath() diff --git a/src/3rdparty/angle/src/common/utilities.h b/src/3rdparty/angle/src/common/utilities.h index dc09011a26..f2f9c63d0e 100644 --- a/src/3rdparty/angle/src/common/utilities.h +++ b/src/3rdparty/angle/src/common/utilities.h @@ -12,9 +12,10 @@ #include <EGL/egl.h> #include <EGL/eglext.h> -#include "angle_gl.h" -#include <string> #include <math.h> +#include <string> +#include <vector> +#include "angle_gl.h" #include "common/mathutil.h" @@ -26,10 +27,12 @@ GLenum VariableComponentType(GLenum type); size_t VariableComponentSize(GLenum type); size_t VariableInternalSize(GLenum type); size_t VariableExternalSize(GLenum type); -GLenum VariableBoolVectorType(GLenum type); int VariableRowCount(GLenum type); int VariableColumnCount(GLenum type); bool IsSamplerType(GLenum type); +bool IsImageType(GLenum type); +bool IsAtomicCounterType(GLenum type); +bool IsOpaqueType(GLenum type); GLenum SamplerTypeToTextureType(GLenum samplerType); bool IsMatrixType(GLenum type); GLenum TransposeMatrixType(GLenum type); @@ -37,6 +40,7 @@ int VariableRegisterCount(GLenum type); int MatrixRegisterCount(GLenum type, bool isRowMajorMatrix); int MatrixComponentCount(GLenum type, bool isRowMajorMatrix); int VariableSortOrder(GLenum type); +GLenum VariableBoolVectorType(GLenum type); int AllocateFirstFreeBits(unsigned int *bits, unsigned int allocationSize, unsigned int bitsSize); @@ -46,9 +50,12 @@ bool IsCubeMapTextureTarget(GLenum target); size_t CubeMapTextureTargetToLayerIndex(GLenum target); GLenum LayerIndexToCubeMapTextureTarget(size_t index); -// Parse the base uniform name and array index. Returns the base name of the uniform. outSubscript is -// set to GL_INVALID_INDEX if the provided name is not an array or the array index is invalid. -std::string ParseUniformName(const std::string &name, size_t *outSubscript); +// Parse the base resource name and array indices. Returns the base name of the resource. +// If the provided name doesn't index an array, the outSubscripts vector will be empty. +// If the provided name indexes an array, the outSubscripts vector will contain indices with +// outermost array indices in the back. If an array index is invalid, GL_INVALID_INDEX is added to +// outSubscripts. +std::string ParseResourceName(const std::string &name, std::vector<unsigned int> *outSubscripts); // Find the range of index values in the provided indices pointer. Primitive restart indices are // only counted in the range if primitive restart is disabled. @@ -61,14 +68,71 @@ IndexRange ComputeIndexRange(GLenum indexType, GLuint GetPrimitiveRestartIndex(GLenum indexType); bool IsTriangleMode(GLenum drawMode); +bool IsIntegerFormat(GLenum unsizedFormat); -// [OpenGL ES 3.0.2] Section 2.3.1 page 14 -// Data Conversion For State-Setting Commands -// Floating-point values are rounded to the nearest integer, instead of truncated, as done by static_cast. -template <typename outT> outT iround(GLfloat value) { return static_cast<outT>(value > 0.0f ? floor(value + 0.5f) : ceil(value - 0.5f)); } -template <typename outT> outT uiround(GLfloat value) { return static_cast<outT>(value + 0.5f); } +// Returns the product of the sizes in the vector, or 1 if the vector is empty. Doesn't currently +// perform overflow checks. +unsigned int ArraySizeProduct(const std::vector<unsigned int> &arraySizes); -unsigned int ParseAndStripArrayIndex(std::string *name); +// Return the array index at the end of name, and write the length of name before the final array +// index into nameLengthWithoutArrayIndexOut. In case name doesn't include an array index, return +// GL_INVALID_INDEX and write the length of the original string. +unsigned int ParseArrayIndex(const std::string &name, size_t *nameLengthWithoutArrayIndexOut); + +struct UniformTypeInfo final : angle::NonCopyable +{ + constexpr UniformTypeInfo(GLenum type, + GLenum componentType, + GLenum samplerTextureType, + GLenum transposedMatrixType, + GLenum boolVectorType, + int rowCount, + int columnCount, + int componentCount, + size_t componentSize, + size_t internalSize, + size_t externalSize, + bool isSampler, + bool isMatrixType, + bool isImageType) + : type(type), + componentType(componentType), + samplerTextureType(samplerTextureType), + transposedMatrixType(transposedMatrixType), + boolVectorType(boolVectorType), + rowCount(rowCount), + columnCount(columnCount), + componentCount(componentCount), + componentSize(componentSize), + internalSize(internalSize), + externalSize(externalSize), + isSampler(isSampler), + isMatrixType(isMatrixType), + isImageType(isImageType) + { + } + + GLenum type; + GLenum componentType; + GLenum samplerTextureType; + GLenum transposedMatrixType; + GLenum boolVectorType; + int rowCount; + int columnCount; + int componentCount; + size_t componentSize; + size_t internalSize; + size_t externalSize; + bool isSampler; + bool isMatrixType; + bool isImageType; +}; + +const UniformTypeInfo &GetUniformTypeInfo(GLenum uniformType); + +const char *GetGenericErrorMessage(GLenum error); + +unsigned int ElementTypeSize(GLenum elementType); } // namespace gl @@ -81,7 +145,9 @@ size_t CubeMapTextureTargetToLayerIndex(EGLenum target); EGLenum LayerIndexToCubeMapTextureTarget(size_t index); bool IsTextureTarget(EGLenum target); bool IsRenderbufferTarget(EGLenum target); -} + +const char *GetGenericErrorMessage(EGLint error); +} // namespace egl namespace egl_gl { @@ -90,6 +156,11 @@ GLenum EGLImageTargetToGLTextureTarget(EGLenum eglTarget); GLuint EGLClientBufferToGLObjectHandle(EGLClientBuffer buffer); } +namespace gl_egl +{ +EGLenum GLComponentTypeToEGLColorComponentType(GLenum glComponentType); +} // namespace gl_egl + #if !defined(ANGLE_ENABLE_WINDOWS_STORE) std::string getTempPath(); void writeFile(const char* path, const void* data, size_t size); diff --git a/src/3rdparty/angle/src/common/vector_utils.h b/src/3rdparty/angle/src/common/vector_utils.h new file mode 100644 index 0000000000..9f5bee1a4a --- /dev/null +++ b/src/3rdparty/angle/src/common/vector_utils.h @@ -0,0 +1,523 @@ +// +// Copyright 2016 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. +// +// vector_utils.h: Utility classes implementing various vector operations + +#ifndef COMMON_VECTOR_UTILS_H_ +#define COMMON_VECTOR_UTILS_H_ + +#include <cmath> +#include <cstddef> +#include <ostream> +#include <type_traits> + +namespace angle +{ + +template <size_t Dimension, typename Type> +class Vector; + +using Vector2 = Vector<2, float>; +using Vector3 = Vector<3, float>; +using Vector4 = Vector<4, float>; + +using Vector2I = Vector<2, int>; +using Vector3I = Vector<3, int>; +using Vector4I = Vector<4, int>; + +using Vector2U = Vector<2, unsigned int>; +using Vector3U = Vector<3, unsigned int>; +using Vector4U = Vector<4, unsigned int>; + +template <size_t Dimension, typename Type> +class VectorBase +{ + public: + using VectorN = Vector<Dimension, Type>; + + // Constructors + VectorBase() = default; + explicit VectorBase(Type element); + + template <typename Type2> + VectorBase(const VectorBase<Dimension, Type2> &other); + + template <typename Arg1, typename Arg2, typename... Args> + VectorBase(const Arg1 &arg1, const Arg2 &arg2, const Args &... args); + + // Access the vector backing storage directly + const Type *data() const { return mData; } + Type *data() { return mData; } + constexpr size_t size() const { return Dimension; } + + // Load or store the pointer from / to raw data + static VectorN Load(const Type *source); + static void Store(const VectorN &source, Type *destination); + + // Index the vector + Type &operator[](size_t i) { return mData[i]; } + const Type &operator[](size_t i) const { return mData[i]; } + + // Basic arithmetic operations + VectorN operator+() const; + VectorN operator-() const; + VectorN operator+(const VectorN &other) const; + VectorN operator-(const VectorN &other) const; + VectorN operator*(const VectorN &other) const; + VectorN operator/(const VectorN &other) const; + VectorN operator*(Type other) const; + VectorN operator/(Type other) const; + friend VectorN operator*(Type a, const VectorN &b) { return b * a; } + + // Compound arithmetic operations + VectorN &operator+=(const VectorN &other); + VectorN &operator-=(const VectorN &other); + VectorN &operator*=(const VectorN &other); + VectorN &operator/=(const VectorN &other); + VectorN &operator*=(Type other); + VectorN &operator/=(Type other); + + // Comparison operators + bool operator==(const VectorN &other) const; + bool operator!=(const VectorN &other) const; + + // Other arithmetic operations + Type length() const; + Type lengthSquared() const; + Type dot(const VectorBase<Dimension, Type> &other) const; + VectorN normalized() const; + + protected: + template <size_t CurrentIndex, size_t OtherDimension, typename OtherType, typename... Args> + void initWithList(const Vector<OtherDimension, OtherType> &arg1, const Args &... args); + + // Some old compilers consider this function an alternative for initWithList(Vector) + // when the variant above is more precise. Use SFINAE on the return value to hide + // this variant for non-arithmetic types. The return value is still void. + template <size_t CurrentIndex, typename OtherType, typename... Args> + typename std::enable_if<std::is_arithmetic<OtherType>::value>::type initWithList( + OtherType arg1, + const Args &... args); + + template <size_t CurrentIndex> + void initWithList() const; + + template <size_t Dimension2, typename Type2> + friend class VectorBase; + + Type mData[Dimension]; +}; + +template <size_t Dimension, typename Type> +std::ostream &operator<<(std::ostream &ostream, const VectorBase<Dimension, Type> &vector); + +template <typename Type> +class Vector<2, Type> : public VectorBase<2, Type> +{ + public: + // Import the constructors defined in VectorBase + using VectorBase<2, Type>::VectorBase; + + // Element shorthands + Type &x() { return this->mData[0]; } + Type &y() { return this->mData[1]; } + + const Type &x() const { return this->mData[0]; } + const Type &y() const { return this->mData[1]; } +}; + +template <typename Type> +std::ostream &operator<<(std::ostream &ostream, const Vector<2, Type> &vector); + +template <typename Type> +class Vector<3, Type> : public VectorBase<3, Type> +{ + public: + // Import the constructors defined in VectorBase + using VectorBase<3, Type>::VectorBase; + + // Additional operations + Vector<3, Type> cross(const Vector<3, Type> &other) const; + + // Element shorthands + Type &x() { return this->mData[0]; } + Type &y() { return this->mData[1]; } + Type &z() { return this->mData[2]; } + + const Type &x() const { return this->mData[0]; } + const Type &y() const { return this->mData[1]; } + const Type &z() const { return this->mData[2]; } +}; + +template <typename Type> +std::ostream &operator<<(std::ostream &ostream, const Vector<3, Type> &vector); + +template <typename Type> +class Vector<4, Type> : public VectorBase<4, Type> +{ + public: + // Import the constructors defined in VectorBase + using VectorBase<4, Type>::VectorBase; + + // Element shorthands + Type &x() { return this->mData[0]; } + Type &y() { return this->mData[1]; } + Type &z() { return this->mData[2]; } + Type &w() { return this->mData[3]; } + + const Type &x() const { return this->mData[0]; } + const Type &y() const { return this->mData[1]; } + const Type &z() const { return this->mData[2]; } + const Type &w() const { return this->mData[3]; } +}; + +template <typename Type> +std::ostream &operator<<(std::ostream &ostream, const Vector<4, Type> &vector); + +// Implementation of constructors and misc operations + +template <size_t Dimension, typename Type> +VectorBase<Dimension, Type>::VectorBase(Type element) +{ + for (size_t i = 0; i < Dimension; ++i) + { + mData[i] = element; + } +} + +template <size_t Dimension, typename Type> +template <typename Type2> +VectorBase<Dimension, Type>::VectorBase(const VectorBase<Dimension, Type2> &other) +{ + for (size_t i = 0; i < Dimension; ++i) + { + mData[i] = static_cast<Type>(other.mData[i]); + } +} + +// Ideally we would like to have only two constructors: +// - a scalar constructor that takes Type as a parameter +// - a compound constructor +// However if we define the compound constructor for when it has a single arguments, then calling +// Vector2(0.0) will be ambiguous. To solve this we take advantage of there being a single compound +// constructor with a single argument, which is the copy constructor. We end up with three +// constructors: +// - the scalar constructor +// - the copy constructor +// - the compound constructor for two or more arguments, hence the arg1, and arg2 here. +template <size_t Dimension, typename Type> +template <typename Arg1, typename Arg2, typename... Args> +VectorBase<Dimension, Type>::VectorBase(const Arg1 &arg1, const Arg2 &arg2, const Args &... args) +{ + initWithList<0>(arg1, arg2, args...); +} + +template <size_t Dimension, typename Type> +template <size_t CurrentIndex, size_t OtherDimension, typename OtherType, typename... Args> +void VectorBase<Dimension, Type>::initWithList(const Vector<OtherDimension, OtherType> &arg1, + const Args &... args) +{ + static_assert(CurrentIndex + OtherDimension <= Dimension, + "Too much data in the vector constructor."); + for (size_t i = 0; i < OtherDimension; ++i) + { + mData[CurrentIndex + i] = static_cast<Type>(arg1.mData[i]); + } + initWithList<CurrentIndex + OtherDimension>(args...); +} + +template <size_t Dimension, typename Type> +template <size_t CurrentIndex, typename OtherType, typename... Args> +typename std::enable_if<std::is_arithmetic<OtherType>::value>::type +VectorBase<Dimension, Type>::initWithList(OtherType arg1, const Args &... args) +{ + static_assert(CurrentIndex + 1 <= Dimension, "Too much data in the vector constructor."); + mData[CurrentIndex] = static_cast<Type>(arg1); + initWithList<CurrentIndex + 1>(args...); +} + +template <size_t Dimension, typename Type> +template <size_t CurrentIndex> +void VectorBase<Dimension, Type>::initWithList() const +{ + static_assert(CurrentIndex == Dimension, "Not enough data in the vector constructor."); +} + +template <size_t Dimension, typename Type> +Vector<Dimension, Type> VectorBase<Dimension, Type>::Load(const Type *source) +{ + Vector<Dimension, Type> result; + for (size_t i = 0; i < Dimension; ++i) + { + result.mData[i] = source[i]; + } + return result; +} + +template <size_t Dimension, typename Type> +void VectorBase<Dimension, Type>::Store(const Vector<Dimension, Type> &source, Type *destination) +{ + for (size_t i = 0; i < Dimension; ++i) + { + destination[i] = source.mData[i]; + } +} + +// Implementation of basic arithmetic operations +template <size_t Dimension, typename Type> +Vector<Dimension, Type> VectorBase<Dimension, Type>::operator+() const +{ + Vector<Dimension, Type> result; + for (size_t i = 0; i < Dimension; ++i) + { + result.mData[i] = +mData[i]; + } + return result; +} + +template <size_t Dimension, typename Type> +Vector<Dimension, Type> VectorBase<Dimension, Type>::operator-() const +{ + Vector<Dimension, Type> result; + for (size_t i = 0; i < Dimension; ++i) + { + result.mData[i] = -mData[i]; + } + return result; +} + +template <size_t Dimension, typename Type> +Vector<Dimension, Type> VectorBase<Dimension, Type>::operator+( + const Vector<Dimension, Type> &other) const +{ + Vector<Dimension, Type> result; + for (size_t i = 0; i < Dimension; ++i) + { + result.mData[i] = mData[i] + other.mData[i]; + } + return result; +} + +template <size_t Dimension, typename Type> +Vector<Dimension, Type> VectorBase<Dimension, Type>::operator-( + const Vector<Dimension, Type> &other) const +{ + Vector<Dimension, Type> result; + for (size_t i = 0; i < Dimension; ++i) + { + result.mData[i] = mData[i] - other.mData[i]; + } + return result; +} + +template <size_t Dimension, typename Type> +Vector<Dimension, Type> VectorBase<Dimension, Type>::operator*( + const Vector<Dimension, Type> &other) const +{ + Vector<Dimension, Type> result; + for (size_t i = 0; i < Dimension; ++i) + { + result.mData[i] = mData[i] * other.mData[i]; + } + return result; +} + +template <size_t Dimension, typename Type> +Vector<Dimension, Type> VectorBase<Dimension, Type>::operator/( + const Vector<Dimension, Type> &other) const +{ + Vector<Dimension, Type> result; + for (size_t i = 0; i < Dimension; ++i) + { + result.mData[i] = mData[i] / other.mData[i]; + } + return result; +} + +template <size_t Dimension, typename Type> +Vector<Dimension, Type> VectorBase<Dimension, Type>::operator*(Type other) const +{ + Vector<Dimension, Type> result; + for (size_t i = 0; i < Dimension; ++i) + { + result.mData[i] = mData[i] * other; + } + return result; +} + +template <size_t Dimension, typename Type> +Vector<Dimension, Type> VectorBase<Dimension, Type>::operator/(Type other) const +{ + Vector<Dimension, Type> result; + for (size_t i = 0; i < Dimension; ++i) + { + result.mData[i] = mData[i] / other; + } + return result; +} + +// Implementation of compound arithmetic operations +template <size_t Dimension, typename Type> +Vector<Dimension, Type> &VectorBase<Dimension, Type>::operator+=( + const Vector<Dimension, Type> &other) +{ + for (size_t i = 0; i < Dimension; ++i) + { + mData[i] += other.mData[i]; + } + return *reinterpret_cast<Vector<Dimension, Type> *>(this); +} + +template <size_t Dimension, typename Type> +Vector<Dimension, Type> &VectorBase<Dimension, Type>::operator-=( + const Vector<Dimension, Type> &other) +{ + for (size_t i = 0; i < Dimension; ++i) + { + mData[i] -= other.mData[i]; + } + return *reinterpret_cast<Vector<Dimension, Type> *>(this); +} + +template <size_t Dimension, typename Type> +Vector<Dimension, Type> &VectorBase<Dimension, Type>::operator*=( + const Vector<Dimension, Type> &other) +{ + for (size_t i = 0; i < Dimension; ++i) + { + mData[i] *= other.mData[i]; + } + return *reinterpret_cast<Vector<Dimension, Type> *>(this); +} + +template <size_t Dimension, typename Type> +Vector<Dimension, Type> &VectorBase<Dimension, Type>::operator/=( + const Vector<Dimension, Type> &other) +{ + for (size_t i = 0; i < Dimension; ++i) + { + mData[i] /= other.mData[i]; + } + return *reinterpret_cast<Vector<Dimension, Type> *>(this); +} + +template <size_t Dimension, typename Type> +Vector<Dimension, Type> &VectorBase<Dimension, Type>::operator*=(Type other) +{ + for (size_t i = 0; i < Dimension; ++i) + { + mData[i] *= other; + } + return *reinterpret_cast<Vector<Dimension, Type> *>(this); +} + +template <size_t Dimension, typename Type> +Vector<Dimension, Type> &VectorBase<Dimension, Type>::operator/=(Type other) +{ + for (size_t i = 0; i < Dimension; ++i) + { + mData[i] /= other; + } + return *reinterpret_cast<Vector<Dimension, Type> *>(this); +} + +// Implementation of comparison operators +template <size_t Dimension, typename Type> +bool VectorBase<Dimension, Type>::operator==(const Vector<Dimension, Type> &other) const +{ + for (size_t i = 0; i < Dimension; ++i) + { + if (mData[i] != other.mData[i]) + { + return false; + } + } + return true; +} + +template <size_t Dimension, typename Type> +bool VectorBase<Dimension, Type>::operator!=(const Vector<Dimension, Type> &other) const +{ + return !(*this == other); +} + +// Implementation of other arithmetic operations +template <size_t Dimension, typename Type> +Type VectorBase<Dimension, Type>::length() const +{ + static_assert(std::is_floating_point<Type>::value, + "VectorN::length is only defined for floating point vectors"); + return std::sqrt(lengthSquared()); +} + +template <size_t Dimension, typename Type> +Type VectorBase<Dimension, Type>::lengthSquared() const +{ + return dot(*this); +} + +template <size_t Dimension, typename Type> +Type VectorBase<Dimension, Type>::dot(const VectorBase<Dimension, Type> &other) const +{ + Type sum = Type(); + for (size_t i = 0; i < Dimension; ++i) + { + sum += mData[i] * other.mData[i]; + } + return sum; +} + +template <size_t Dimension, typename Type> +std::ostream &operator<<(std::ostream &ostream, const VectorBase<Dimension, Type> &vector) +{ + ostream << "[ "; + for (size_t elementIdx = 0; elementIdx < Dimension; elementIdx++) + { + if (elementIdx > 0) + { + ostream << ", "; + } + ostream << vector.data()[elementIdx]; + } + ostream << " ]"; + return ostream; +} + +template <size_t Dimension, typename Type> +Vector<Dimension, Type> VectorBase<Dimension, Type>::normalized() const +{ + static_assert(std::is_floating_point<Type>::value, + "VectorN::normalized is only defined for floating point vectors"); + return *this / length(); +} + +template <typename Type> +std::ostream &operator<<(std::ostream &ostream, const Vector<2, Type> &vector) +{ + return ostream << static_cast<const VectorBase<2, Type> &>(vector); +} + +template <typename Type> +Vector<3, Type> Vector<3, Type>::cross(const Vector<3, Type> &other) const +{ + return Vector<3, Type>(y() * other.z() - z() * other.y(), z() * other.x() - x() * other.z(), + x() * other.y() - y() * other.x()); +} + +template <typename Type> +std::ostream &operator<<(std::ostream &ostream, const Vector<3, Type> &vector) +{ + return ostream << static_cast<const VectorBase<3, Type> &>(vector); +} + +template <typename Type> +std::ostream &operator<<(std::ostream &ostream, const Vector<4, Type> &vector) +{ + return ostream << static_cast<const VectorBase<4, Type> &>(vector); +} + +} // namespace angle + +#endif // COMMON_VECTOR_UTILS_H_ |