diff options
author | Oliver Wolff <oliver.wolff@theqtcompany.com> | 2016-03-24 12:38:18 +0100 |
---|---|---|
committer | Oliver Wolff <oliver.wolff@qt.io> | 2016-04-25 05:57:38 +0000 |
commit | e12ba07322cd61c5cf50c25ed8d1f08f6b1ff879 (patch) | |
tree | d31a44c9f123ed764a00eff7b4fff656a07d54ab /src/3rdparty/angle/src | |
parent | d3dcc6f610b97be7cbfbb0a65988e5940568c825 (diff) |
Update ANGLE to chromium/2651
Change-Id: I1cd32b780b1a0b913fab870e155ae1f4f9ac40d7
Reviewed-by: Maurice Kalinowski <maurice.kalinowski@qt.io>
Diffstat (limited to 'src/3rdparty/angle/src')
457 files changed, 60625 insertions, 19075 deletions
diff --git a/src/3rdparty/angle/src/common/BitSetIterator.h b/src/3rdparty/angle/src/common/BitSetIterator.h new file mode 100644 index 0000000000..3248ce44c9 --- /dev/null +++ b/src/3rdparty/angle/src/common/BitSetIterator.h @@ -0,0 +1,156 @@ +// +// 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/libANGLE/Float16ToFloat32.cpp b/src/3rdparty/angle/src/common/Float16ToFloat32.cpp index 5bf7b3fce8..acd0d88b60 100644 --- a/src/3rdparty/angle/src/libANGLE/Float16ToFloat32.cpp +++ b/src/3rdparty/angle/src/common/Float16ToFloat32.cpp @@ -6,6 +6,8 @@ // This file is automatically generated. +#include "common/mathutil.h" + namespace gl { @@ -2197,7 +2199,7 @@ const static unsigned g_offset[64] = { float float16ToFloat32(unsigned short h) { unsigned i32 = g_mantissa[g_offset[h >> 10] + (h & 0x3ff)] + g_exponent[h >> 10]; - return *(float*) &i32; + return bitCast<float>(i32); } } diff --git a/src/3rdparty/angle/src/common/Optional.h b/src/3rdparty/angle/src/common/Optional.h index 9665b7ddf6..256f38f329 100644 --- a/src/3rdparty/angle/src/common/Optional.h +++ b/src/3rdparty/angle/src/common/Optional.h @@ -35,11 +35,27 @@ struct Optional return *this; } - static Optional None() + Optional &operator=(const T &value) { - return Optional(); + mValue = value; + mValid = true; + return *this; + } + + Optional &operator=(T &&value) + { + mValue = std::move(value); + mValid = true; + return *this; } + void reset() + { + mValid = false; + } + + static Optional Invalid() { return Optional(); } + bool valid() const { return mValid; } const T &value() const { return mValue; } diff --git a/src/3rdparty/angle/src/common/angleutils.cpp b/src/3rdparty/angle/src/common/angleutils.cpp index af5eb6c447..7099c21730 100644 --- a/src/3rdparty/angle/src/common/angleutils.cpp +++ b/src/3rdparty/angle/src/common/angleutils.cpp @@ -8,8 +8,15 @@ #include "common/debug.h" #include <stdio.h> + +#include <limits> #include <vector> +namespace angle +{ +const uintptr_t DirtyPointer = std::numeric_limits<uintptr_t>::max(); +} + size_t FormatStringIntoVector(const char *fmt, va_list vararg, std::vector<char>& outBuffer) { // Attempt to just print to the current buffer diff --git a/src/3rdparty/angle/src/common/angleutils.h b/src/3rdparty/angle/src/common/angleutils.h index 4cf84a3182..a0178fd414 100644 --- a/src/3rdparty/angle/src/common/angleutils.h +++ b/src/3rdparty/angle/src/common/angleutils.h @@ -25,16 +25,15 @@ namespace angle class NonCopyable { -#if !defined(_MSC_VER) || (_MSC_VER >= 1800) public: NonCopyable() = default; ~NonCopyable() = default; protected: NonCopyable(const NonCopyable&) = delete; void operator=(const NonCopyable&) = delete; -#endif }; +extern const uintptr_t DirtyPointer; } template <typename T, size_t N> @@ -72,9 +71,9 @@ void SafeDelete(T*& resource) template <typename T> void SafeDeleteContainer(T& resource) { - for (typename T::iterator i = resource.begin(); i != resource.end(); i++) + for (auto &element : resource) { - SafeDelete(*i); + SafeDelete(element); } resource.clear(); } diff --git a/src/3rdparty/angle/src/common/debug.cpp b/src/3rdparty/angle/src/common/debug.cpp index 2fc0a2984a..1fcc062908 100644 --- a/src/3rdparty/angle/src/common/debug.cpp +++ b/src/3rdparty/angle/src/common/debug.cpp @@ -44,16 +44,16 @@ void output(bool traceInDebugOnly, MessageType messageType, DebugTraceOutputType case DebugTraceOutputTypeNone: break; case DebugTraceOutputTypeBeginEvent: - g_debugAnnotator->beginEvent(formattedWideMessage); + g_debugAnnotator->beginEvent(formattedWideMessage.c_str()); break; case DebugTraceOutputTypeSetMarker: - g_debugAnnotator->setMarker(formattedWideMessage); + g_debugAnnotator->setMarker(formattedWideMessage.c_str()); break; } } std::string formattedMessage; - UNUSED_TRACE_VARIABLE(formattedMessage); + UNUSED_VARIABLE(formattedMessage); #if !defined(NDEBUG) && defined(_MSC_VER) if (messageType == MESSAGE_ERR) diff --git a/src/3rdparty/angle/src/common/debug.h b/src/3rdparty/angle/src/common/debug.h index c4f118ebae..64cfef4cd9 100644 --- a/src/3rdparty/angle/src/common/debug.h +++ b/src/3rdparty/angle/src/common/debug.h @@ -16,7 +16,7 @@ #include "common/angleutils.h" #if !defined(TRACE_OUTPUT_FILE) -#define TRACE_OUTPUT_FILE "debug.txt" +#define TRACE_OUTPUT_FILE "angle_debug.txt" #endif namespace gl @@ -47,9 +47,9 @@ class DebugAnnotator : angle::NonCopyable public: DebugAnnotator() { }; virtual ~DebugAnnotator() { }; - virtual void beginEvent(const std::wstring &eventName) = 0; + virtual void beginEvent(const wchar_t *eventName) = 0; virtual void endEvent() = 0; - virtual void setMarker(const std::wstring &markerName) = 0; + virtual void setMarker(const wchar_t *markerName) = 0; virtual bool getStatus() = 0; }; @@ -63,6 +63,8 @@ bool DebugAnnotationsActive(); #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__) @@ -89,7 +91,7 @@ bool DebugAnnotationsActive(); #if defined(_MSC_VER) #define EVENT(message, ...) gl::ScopedPerfEventHelper scopedPerfEventHelper ## __LINE__("%s" message "\n", __FUNCTION__, __VA_ARGS__); #else -#define EVENT(message, ...) gl::ScopedPerfEventHelper scopedPerfEventHelper(message "\n", ##__VA_ARGS__); +#define EVENT(message, ...) gl::ScopedPerfEventHelper scopedPerfEventHelper("%s" message "\n", __FUNCTION__, ##__VA_ARGS__); #endif // _MSC_VER #else #define EVENT(message, ...) (void(0)) @@ -101,22 +103,18 @@ bool DebugAnnotationsActive(); // A macro asserting a condition and outputting failures to the debug log #if !defined(NDEBUG) -#define ASSERT(expression) do { \ +#define ASSERT(expression) { \ if(!(expression)) \ - ERR("\t! Assert failed in %s(%d): "#expression"\n", __FUNCTION__, __LINE__); \ + ERR("\t! Assert failed in %s(%d): %s\n", __FUNCTION__, __LINE__, #expression); \ assert(expression); \ - } while(0) + } ANGLE_EMPTY_STATEMENT #define UNUSED_ASSERTION_VARIABLE(variable) #else #define ASSERT(expression) (void(0)) #define UNUSED_ASSERTION_VARIABLE(variable) ((void)variable) #endif -#ifndef ANGLE_ENABLE_DEBUG_TRACE -#define UNUSED_TRACE_VARIABLE(variable) ((void)variable) -#else -#define UNUSED_TRACE_VARIABLE(variable) -#endif +#define UNUSED_VARIABLE(variable) ((void)variable) // A macro to indicate unimplemented functionality @@ -131,29 +129,22 @@ bool DebugAnnotationsActive(); #endif #if !defined(NDEBUG) -#define UNIMPLEMENTED() do { \ +#define UNIMPLEMENTED() { \ FIXME("\t! Unimplemented: %s(%d)\n", __FUNCTION__, __LINE__); \ assert(NOASSERT_UNIMPLEMENTED); \ - } while(0) + } ANGLE_EMPTY_STATEMENT #else #define UNIMPLEMENTED() FIXME("\t! Unimplemented: %s(%d)\n", __FUNCTION__, __LINE__) #endif // A macro for code which is not expected to be reached under valid assumptions #if !defined(NDEBUG) -#define UNREACHABLE() do { \ +#define UNREACHABLE() { \ ERR("\t! Unreachable reached: %s(%d)\n", __FUNCTION__, __LINE__); \ assert(false); \ - } while(0) + } ANGLE_EMPTY_STATEMENT #else #define UNREACHABLE() ERR("\t! Unreachable reached: %s(%d)\n", __FUNCTION__, __LINE__) #endif -// A macro that determines whether an object has a given runtime type. -#if !defined(NDEBUG) && (!defined(_MSC_VER) || defined(_CPPRTTI)) && (!defined(__GNUC__) || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || defined(__GXX_RTTI)) -#define HAS_DYNAMIC_TYPE(type, obj) (dynamic_cast<type >(obj) != NULL) -#else -#define HAS_DYNAMIC_TYPE(type, obj) true -#endif - #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 eb0c98c9e5..c9eb5e3073 100644 --- a/src/3rdparty/angle/src/common/event_tracer.cpp +++ b/src/3rdparty/angle/src/common/event_tracer.cpp @@ -4,35 +4,53 @@ #include "common/event_tracer.h" -namespace gl -{ - -GetCategoryEnabledFlagFunc g_getCategoryEnabledFlag; -AddTraceEventFunc g_addTraceEvent; +#include "common/debug.h" -} // namespace gl - -namespace gl +namespace angle { -const unsigned char* TraceGetTraceCategoryEnabledFlag(const char* name) +const unsigned char *GetTraceCategoryEnabledFlag(const char *name) { - if (g_getCategoryEnabledFlag) + angle::Platform *platform = ANGLEPlatformCurrent(); + ASSERT(platform); + + const unsigned char *categoryEnabledFlag = platform->getTraceCategoryEnabledFlag(name); + if (categoryEnabledFlag != nullptr) { - return g_getCategoryEnabledFlag(name); + return categoryEnabledFlag; } + static unsigned char disabled = 0; return &disabled; } -void TraceAddTraceEvent(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) +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) { - if (g_addTraceEvent) + angle::Platform *platform = ANGLEPlatformCurrent(); + ASSERT(platform); + + double timestamp = platform->monotonicallyIncreasingTime(); + + if (timestamp != 0) { - g_addTraceEvent(phase, categoryGroupEnabled, name, id, numArgs, argNames, argTypes, argValues, flags); + angle::Platform::TraceEventHandle handle = + platform->addTraceEvent(phase, + categoryGroupEnabled, + name, + id, + timestamp, + numArgs, + argNames, + argTypes, + argValues, + flags); + ASSERT(handle != 0); + return handle; } + + return static_cast<Platform::TraceEventHandle>(0); } -} // namespace gl +} // namespace angle diff --git a/src/3rdparty/angle/src/common/event_tracer.h b/src/3rdparty/angle/src/common/event_tracer.h index dbe4c1bef9..ed70f249d2 100644 --- a/src/3rdparty/angle/src/common/event_tracer.h +++ b/src/3rdparty/angle/src/common/event_tracer.h @@ -6,28 +6,16 @@ #define COMMON_EVENT_TRACER_H_ #include "common/platform.h" +#include "platform/Platform.h" -extern "C" { - -typedef const unsigned char* (*GetCategoryEnabledFlagFunc)(const char* name); -typedef void (*AddTraceEventFunc)(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); - -} - -namespace gl +namespace angle { -extern GetCategoryEnabledFlagFunc g_getCategoryEnabledFlag; -extern AddTraceEventFunc g_addTraceEvent; - -const unsigned char* TraceGetTraceCategoryEnabledFlag(const char* name); - -void TraceAddTraceEvent(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); +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); } diff --git a/src/3rdparty/angle/src/common/mathutil.cpp b/src/3rdparty/angle/src/common/mathutil.cpp index 496633632b..927b6ebebe 100644 --- a/src/3rdparty/angle/src/common/mathutil.cpp +++ b/src/3rdparty/angle/src/common/mathutil.cpp @@ -43,16 +43,16 @@ unsigned int convertRGBFloatsTo999E5(float red, float green, float blue) const float max_c = std::max<float>(std::max<float>(red_c, green_c), blue_c); const float exp_p = std::max<float>(-g_sharedexp_bias - 1, floor(log(max_c))) + 1 + g_sharedexp_bias; - const int max_s = floor((max_c / (pow(2.0f, exp_p - g_sharedexp_bias - g_sharedexp_mantissabits))) + 0.5f); - const int exp_s = (max_s < pow(2.0f, g_sharedexp_mantissabits)) ? exp_p : exp_p + 1; + const int max_s = static_cast<int>(floor((max_c / (pow(2.0f, exp_p - g_sharedexp_bias - g_sharedexp_mantissabits))) + 0.5f)); + const int exp_s = static_cast<int>((max_s < pow(2.0f, g_sharedexp_mantissabits)) ? exp_p : exp_p + 1); RGB9E5Data output; - output.R = floor((red_c / (pow(2.0f, exp_s - g_sharedexp_bias - g_sharedexp_mantissabits))) + 0.5f); - output.G = floor((green_c / (pow(2.0f, exp_s - g_sharedexp_bias - g_sharedexp_mantissabits))) + 0.5f); - output.B = floor((blue_c / (pow(2.0f, exp_s - g_sharedexp_bias - g_sharedexp_mantissabits))) + 0.5f); + output.R = static_cast<unsigned int>(floor((red_c / (pow(2.0f, exp_s - g_sharedexp_bias - g_sharedexp_mantissabits))) + 0.5f)); + output.G = static_cast<unsigned int>(floor((green_c / (pow(2.0f, exp_s - g_sharedexp_bias - g_sharedexp_mantissabits))) + 0.5f)); + output.B = static_cast<unsigned int>(floor((blue_c / (pow(2.0f, exp_s - g_sharedexp_bias - g_sharedexp_mantissabits))) + 0.5f)); output.E = exp_s; - return *reinterpret_cast<unsigned int*>(&output); + return bitCast<unsigned int>(output); } void convert999E5toRGBFloats(unsigned int input, float *red, float *green, float *blue) diff --git a/src/3rdparty/angle/src/common/mathutil.h b/src/3rdparty/angle/src/common/mathutil.h index 1015bd2312..3de62aef10 100644 --- a/src/3rdparty/angle/src/common/mathutil.h +++ b/src/3rdparty/angle/src/common/mathutil.h @@ -14,7 +14,9 @@ #include <limits> #include <algorithm> +#include <math.h> #include <string.h> +#include <stdint.h> #include <stdlib.h> namespace gl @@ -67,14 +69,29 @@ inline int clampToInt(unsigned int x) template <typename DestT, typename SrcT> inline DestT clampCast(SrcT value) { - // This assumes SrcT can properly represent DestT::min/max - // Unfortunately we can't use META_ASSERT without C++11 constexpr support - ASSERT(static_cast<DestT>(static_cast<SrcT>(std::numeric_limits<DestT>::min())) == std::numeric_limits<DestT>::min()); - ASSERT(static_cast<DestT>(static_cast<SrcT>(std::numeric_limits<DestT>::max())) == std::numeric_limits<DestT>::max()); - - SrcT lo = static_cast<SrcT>(std::numeric_limits<DestT>::min()); - SrcT hi = static_cast<SrcT>(std::numeric_limits<DestT>::max()); - return static_cast<DestT>(value > lo ? (value > hi ? hi : value) : lo); + 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) + { + return destHi; + } + else + { + return static_cast<DestT>(value); + } } template<typename T, typename MIN, typename MAX> @@ -119,9 +136,6 @@ inline bool supportsSSE2() return supports; } -#if defined(__GNUC__) - supports = __builtin_cpu_supports("sse2"); -#else int info[4]; __cpuid(info, 0); @@ -131,7 +145,6 @@ inline bool supportsSSE2() supports = (info[3] >> 26) & 1; } -#endif checked = true; @@ -153,13 +166,13 @@ destType bitCast(const sourceType &source) inline unsigned short float32ToFloat16(float fp32) { - unsigned int fp32i = (unsigned int&)fp32; + unsigned int fp32i = bitCast<unsigned int>(fp32); unsigned int sign = (fp32i & 0x80000000) >> 16; unsigned int abs = fp32i & 0x7FFFFFFF; if(abs > 0x47FFEFFF) // Infinity { - return sign | 0x7FFF; + return static_cast<unsigned short>(sign | 0x7FFF); } else if(abs < 0x38800000) // Denormal { @@ -175,11 +188,11 @@ inline unsigned short float32ToFloat16(float fp32) abs = 0; } - return sign | (abs + 0x00000FFF + ((abs >> 13) & 1)) >> 13; + return static_cast<unsigned short>(sign | (abs + 0x00000FFF + ((abs >> 13) & 1)) >> 13); } else { - return sign | (abs + 0xC8000000 + 0x00000FFF + ((abs >> 13) & 1)) >> 13; + return static_cast<unsigned short>(sign | (abs + 0xC8000000 + 0x00000FFF + ((abs >> 13) & 1)) >> 13); } } @@ -426,14 +439,14 @@ inline float normalizedToFloat(T input) template <typename T> inline T floatToNormalized(float input) { - return std::numeric_limits<T>::max() * input + 0.5f; + return static_cast<T>(std::numeric_limits<T>::max() * input + 0.5f); } template <unsigned int outputBitCount, typename T> inline T floatToNormalized(float input) { static_assert(outputBitCount < (sizeof(T) * 8), "T must have more bits than outputBitCount."); - return ((1 << outputBitCount) - 1) * input + 0.5f; + return static_cast<T>(((1 << outputBitCount) - 1) * input + 0.5f); } template <unsigned int inputBitCount, unsigned int inputBitStart, typename T> @@ -480,9 +493,10 @@ inline unsigned int average(unsigned int a, unsigned int b) return ((a ^ b) >> 1) + (a & b); } -inline signed int average(signed int a, signed int b) +inline int average(int a, int b) { - return ((long long)a + (long long)b) / 2; + long long average = (static_cast<long long>(a) + static_cast<long long>(b)) / 2ll; + return static_cast<int>(average); } inline float average(float a, float b) @@ -497,20 +511,14 @@ inline unsigned short averageHalfFloat(unsigned short a, unsigned short b) inline unsigned int averageFloat11(unsigned int a, unsigned int b) { - return float32ToFloat11((float11ToFloat32(a) + float11ToFloat32(b)) * 0.5f); + return float32ToFloat11((float11ToFloat32(static_cast<unsigned short>(a)) + float11ToFloat32(static_cast<unsigned short>(b))) * 0.5f); } inline unsigned int averageFloat10(unsigned int a, unsigned int b) { - return float32ToFloat10((float10ToFloat32(a) + float10ToFloat32(b)) * 0.5f); -} - + return float32ToFloat10((float10ToFloat32(static_cast<unsigned short>(a)) + float10ToFloat32(static_cast<unsigned short>(b))) * 0.5f); } -namespace rx -{ - -// Represents intervals of the type [a, b) template <typename T> struct Range { @@ -533,11 +541,146 @@ struct Range return start < other.end; } } + + void extend(T value) + { + start = value > start ? value : start; + end = value < end ? value : end; + } + + bool empty() const + { + return end <= start; + } }; typedef Range<int> RangeI; typedef Range<unsigned int> RangeUI; +struct IndexRange +{ + IndexRange() : IndexRange(0, 0, 0) {} + IndexRange(size_t start_, size_t end_, size_t vertexIndexCount_) + : start(start_), end(end_), vertexIndexCount(vertexIndexCount_) + { + ASSERT(start <= end); + } + + // Number of vertices in the range. + size_t vertexCount() const { return (end - start) + 1; } + + // Inclusive range of indices that are not primitive restart + size_t start; + size_t end; + + // Number of non-primitive restart indices + size_t vertexIndexCount; +}; + +// First, both normalized floating-point values are converted into 16-bit integer values. +// Then, the results are packed into the returned 32-bit unsigned integer. +// The first float value will be written to the least significant bits of the output; +// the last float value will be written to the most significant bits. +// The conversion of each value to fixed point is done as follows : +// packSnorm2x16 : round(clamp(c, -1, +1) * 32767.0) +inline uint32_t packSnorm2x16(float f1, float f2) +{ + int16_t leastSignificantBits = static_cast<int16_t>(roundf(clamp(f1, -1.0f, 1.0f) * 32767.0f)); + int16_t mostSignificantBits = static_cast<int16_t>(roundf(clamp(f2, -1.0f, 1.0f) * 32767.0f)); + return static_cast<uint32_t>(mostSignificantBits) << 16 | + (static_cast<uint32_t>(leastSignificantBits) & 0xFFFF); +} + +// First, unpacks a single 32-bit unsigned integer u into a pair of 16-bit unsigned integers. Then, each +// component is converted to a normalized floating-point value to generate the returned two float values. +// The first float value will be extracted from the least significant bits of the input; +// the last float value will be extracted from the most-significant bits. +// The conversion for unpacked fixed-point value to floating point is done as follows: +// unpackSnorm2x16 : clamp(f / 32767.0, -1, +1) +inline void unpackSnorm2x16(uint32_t u, float *f1, float *f2) +{ + int16_t leastSignificantBits = static_cast<int16_t>(u & 0xFFFF); + int16_t mostSignificantBits = static_cast<int16_t>(u >> 16); + *f1 = clamp(static_cast<float>(leastSignificantBits) / 32767.0f, -1.0f, 1.0f); + *f2 = clamp(static_cast<float>(mostSignificantBits) / 32767.0f, -1.0f, 1.0f); +} + +// First, both normalized floating-point values are converted into 16-bit integer values. +// Then, the results are packed into the returned 32-bit unsigned integer. +// The first float value will be written to the least significant bits of the output; +// the last float value will be written to the most significant bits. +// The conversion of each value to fixed point is done as follows: +// packUnorm2x16 : round(clamp(c, 0, +1) * 65535.0) +inline uint32_t packUnorm2x16(float f1, float f2) +{ + uint16_t leastSignificantBits = static_cast<uint16_t>(roundf(clamp(f1, 0.0f, 1.0f) * 65535.0f)); + uint16_t mostSignificantBits = static_cast<uint16_t>(roundf(clamp(f2, 0.0f, 1.0f) * 65535.0f)); + return static_cast<uint32_t>(mostSignificantBits) << 16 | static_cast<uint32_t>(leastSignificantBits); +} + +// First, unpacks a single 32-bit unsigned integer u into a pair of 16-bit unsigned integers. Then, each +// component is converted to a normalized floating-point value to generate the returned two float values. +// The first float value will be extracted from the least significant bits of the input; +// the last float value will be extracted from the most-significant bits. +// The conversion for unpacked fixed-point value to floating point is done as follows: +// unpackUnorm2x16 : f / 65535.0 +inline void unpackUnorm2x16(uint32_t u, float *f1, float *f2) +{ + uint16_t leastSignificantBits = static_cast<uint16_t>(u & 0xFFFF); + uint16_t mostSignificantBits = static_cast<uint16_t>(u >> 16); + *f1 = static_cast<float>(leastSignificantBits) / 65535.0f; + *f2 = static_cast<float>(mostSignificantBits) / 65535.0f; +} + +// Returns an unsigned integer obtained by converting the two floating-point values to the 16-bit +// floating-point representation found in the OpenGL ES Specification, and then packing these +// two 16-bit integers into a 32-bit unsigned integer. +// f1: The 16 least-significant bits of the result; +// f2: The 16 most-significant bits. +inline uint32_t packHalf2x16(float f1, float f2) +{ + uint16_t leastSignificantBits = static_cast<uint16_t>(float32ToFloat16(f1)); + uint16_t mostSignificantBits = static_cast<uint16_t>(float32ToFloat16(f2)); + return static_cast<uint32_t>(mostSignificantBits) << 16 | static_cast<uint32_t>(leastSignificantBits); +} + +// Returns two floating-point values obtained by unpacking a 32-bit unsigned integer into a pair of 16-bit values, +// interpreting those values as 16-bit floating-point numbers according to the OpenGL ES Specification, +// and converting them to 32-bit floating-point values. +// The first float value is obtained from the 16 least-significant bits of u; +// the second component is obtained from the 16 most-significant bits of u. +inline void unpackHalf2x16(uint32_t u, float *f1, float *f2) +{ + uint16_t leastSignificantBits = static_cast<uint16_t>(u & 0xFFFF); + uint16_t mostSignificantBits = static_cast<uint16_t>(u >> 16); + + *f1 = float16ToFloat32(leastSignificantBits); + *f2 = float16ToFloat32(mostSignificantBits); +} + +// 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) +{ + // Exponent mask: ((1u << 8) - 1u) << 23 = 0x7f800000u + // Mantissa mask: ((1u << 23) - 1u) = 0x7fffffu + return ((bitCast<uint32_t>(f) & 0x7f800000u) == 0x7f800000u) && (bitCast<uint32_t>(f) & 0x7fffffu); +} + +// Returns whether the argument is infinity. +// IEEE 754 single precision infinity representation: Exponent(8 bits) - 255, Mantissa(23 bits) - zero. +inline bool isInf(float f) +{ + // Exponent mask: ((1u << 8) - 1u) << 23 = 0x7f800000u + // Mantissa mask: ((1u << 23) - 1u) = 0x7fffffu + return ((bitCast<uint32_t>(f) & 0x7f800000u) == 0x7f800000u) && !(bitCast<uint32_t>(f) & 0x7fffffu); +} + +} + +namespace rx +{ + template <typename T> T roundUp(const T value, const T alignment) { @@ -573,6 +716,7 @@ inline bool IsIntegerCastSafe(BigIntT bigValue) #if defined(_MSC_VER) #define ANGLE_ROTL(x,y) _rotl(x,y) +#define ANGLE_ROTR16(x,y) _rotr16(x,y) #else @@ -581,7 +725,13 @@ inline uint32_t RotL(uint32_t x, int8_t r) return (x << r) | (x >> (32 - r)); } +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) #endif // namespace rx diff --git a/src/3rdparty/angle/src/common/matrix_utils.h b/src/3rdparty/angle/src/common/matrix_utils.h new file mode 100644 index 0000000000..6f3187c3e8 --- /dev/null +++ b/src/3rdparty/angle/src/common/matrix_utils.h @@ -0,0 +1,349 @@ +// +// 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. +// +// Matrix: +// Utility class implementing various matrix operations. +// Supports matrices with minimum 2 and maximum 4 number of rows/columns. +// +// TODO: Check if we can merge Matrix.h in sample_util with this and replace it with this implementation. +// TODO: Rename this file to Matrix.h once we remove Matrix.h in sample_util. + +#ifndef COMMON_MATRIX_UTILS_H_ +#define COMMON_MATRIX_UTILS_H_ + +#include <vector> + +#include "common/debug.h" + +namespace angle +{ + +template<typename T> +class Matrix +{ + public: + Matrix(const std::vector<T> &elements, const unsigned int &numRows, const unsigned int &numCols) + : mElements(elements), + mRows(numRows), + mCols(numCols) + { + ASSERT(rows() >= 1 && rows() <= 4); + ASSERT(columns() >= 1 && columns() <= 4); + } + + Matrix(const std::vector<T> &elements, const unsigned int &size) + : mElements(elements), + mRows(size), + mCols(size) + { + ASSERT(rows() >= 1 && rows() <= 4); + ASSERT(columns() >= 1 && columns() <= 4); + } + + Matrix(const T *elements, const unsigned int &size) + : mRows(size), + mCols(size) + { + ASSERT(rows() >= 1 && rows() <= 4); + ASSERT(columns() >= 1 && columns() <= 4); + for (size_t i = 0; i < size * size; i++) + mElements.push_back(elements[i]); + } + + const T &operator()(const unsigned int &rowIndex, const unsigned int &columnIndex) const + { + return mElements[rowIndex * columns() + columnIndex]; + } + + T &operator()(const unsigned int &rowIndex, const unsigned int &columnIndex) + { + return mElements[rowIndex * columns() + columnIndex]; + } + + const T &at(const unsigned int &rowIndex, const unsigned int &columnIndex) const + { + return operator()(rowIndex, columnIndex); + } + + Matrix<T> operator*(const Matrix<T> &m) + { + ASSERT(columns() == m.rows()); + + unsigned int resultRows = rows(); + unsigned int resultCols = m.columns(); + Matrix<T> result(std::vector<T>(resultRows * resultCols), resultRows, resultCols); + for (unsigned int i = 0; i < resultRows; i++) + { + for (unsigned int j = 0; j < resultCols; j++) + { + T tmp = 0.0f; + for (unsigned int k = 0; k < columns(); k++) + tmp += at(i, k) * m(k, j); + result(i, j) = tmp; + } + } + + return result; + } + + unsigned int size() const + { + ASSERT(rows() == columns()); + return rows(); + } + + unsigned int rows() const { return mRows; } + + unsigned int columns() const { return mCols; } + + std::vector<T> elements() const { return mElements; } + + Matrix<T> compMult(const Matrix<T> &mat1) const + { + Matrix result(std::vector<T>(mElements.size()), size()); + for (unsigned int i = 0; i < columns(); i++) + for (unsigned int j = 0; j < rows(); j++) + result(i, j) = at(i, j) * mat1(i, j); + + return result; + } + + Matrix<T> outerProduct(const Matrix<T> &mat1) const + { + unsigned int cols = mat1.columns(); + Matrix result(std::vector<T>(rows() * cols), rows(), cols); + for (unsigned int i = 0; i < rows(); i++) + for (unsigned int j = 0; j < cols; j++) + result(i, j) = at(i, 0) * mat1(0, j); + + return result; + } + + Matrix<T> transpose() const + { + Matrix result(std::vector<T>(mElements.size()), columns(), rows()); + for (unsigned int i = 0; i < columns(); i++) + for (unsigned int j = 0; j < rows(); j++) + result(i, j) = at(j, i); + + return result; + } + + T determinant() const + { + ASSERT(rows() == columns()); + + switch (size()) + { + case 2: + return at(0, 0) * at(1, 1) - at(0, 1) * at(1, 0); + + case 3: + return at(0, 0) * at(1, 1) * at(2, 2) + + at(0, 1) * at(1, 2) * at(2, 0) + + at(0, 2) * at(1, 0) * at(2, 1) - + at(0, 2) * at(1, 1) * at(2, 0) - + at(0, 1) * at(1, 0) * at(2, 2) - + at(0, 0) * at(1, 2) * at(2, 1); + + case 4: + { + const float minorMatrices[4][3 * 3] = + { + { + at(1, 1), at(2, 1), at(3, 1), + at(1, 2), at(2, 2), at(3, 2), + at(1, 3), at(2, 3), at(3, 3), + }, + { + at(1, 0), at(2, 0), at(3, 0), + at(1, 2), at(2, 2), at(3, 2), + at(1, 3), at(2, 3), at(3, 3), + }, + { + at(1, 0), at(2, 0), at(3, 0), + at(1, 1), at(2, 1), at(3, 1), + at(1, 3), at(2, 3), at(3, 3), + }, + { + at(1, 0), at(2, 0), at(3, 0), + at(1, 1), at(2, 1), at(3, 1), + at(1, 2), at(2, 2), at(3, 2), + } + }; + return at(0, 0) * Matrix<T>(minorMatrices[0], 3).determinant() - + at(0, 1) * Matrix<T>(minorMatrices[1], 3).determinant() + + at(0, 2) * Matrix<T>(minorMatrices[2], 3).determinant() - + at(0, 3) * Matrix<T>(minorMatrices[3], 3).determinant(); + } + + default: + UNREACHABLE(); + break; + } + + return T(); + } + + Matrix<T> inverse() const + { + ASSERT(rows() == columns()); + + Matrix<T> cof(std::vector<T>(mElements.size()), rows(), columns()); + switch (size()) + { + case 2: + cof(0, 0) = at(1, 1); + cof(0, 1) = -at(1, 0); + cof(1, 0) = -at(0, 1); + cof(1, 1) = at(0, 0); + break; + + case 3: + cof(0, 0) = at(1, 1) * at(2, 2) - + at(2, 1) * at(1, 2); + cof(0, 1) = -(at(1, 0) * at(2, 2) - + at(2, 0) * at(1, 2)); + cof(0, 2) = at(1, 0) * at(2, 1) - + at(2, 0) * at(1, 1); + cof(1, 0) = -(at(0, 1) * at(2, 2) - + at(2, 1) * at(0, 2)); + cof(1, 1) = at(0, 0) * at(2, 2) - + at(2, 0) * at(0, 2); + cof(1, 2) = -(at(0, 0) * at(2, 1) - + at(2, 0) * at(0, 1)); + cof(2, 0) = at(0, 1) * at(1, 2) - + at(1, 1) * at(0, 2); + cof(2, 1) = -(at(0, 0) * at(1, 2) - + at(1, 0) * at(0, 2)); + cof(2, 2) = at(0, 0) * at(1, 1) - + at(1, 0) * at(0, 1); + break; + + case 4: + cof(0, 0) = at(1, 1) * at(2, 2) * at(3, 3) + + at(2, 1) * at(3, 2) * at(1, 3) + + at(3, 1) * at(1, 2) * at(2, 3) - + at(1, 1) * at(3, 2) * at(2, 3) - + at(2, 1) * at(1, 2) * at(3, 3) - + at(3, 1) * at(2, 2) * at(1, 3); + cof(0, 1) = -(at(1, 0) * at(2, 2) * at(3, 3) + + at(2, 0) * at(3, 2) * at(1, 3) + + at(3, 0) * at(1, 2) * at(2, 3) - + at(1, 0) * at(3, 2) * at(2, 3) - + at(2, 0) * at(1, 2) * at(3, 3) - + at(3, 0) * at(2, 2) * at(1, 3)); + cof(0, 2) = at(1, 0) * at(2, 1) * at(3, 3) + + at(2, 0) * at(3, 1) * at(1, 3) + + at(3, 0) * at(1, 1) * at(2, 3) - + at(1, 0) * at(3, 1) * at(2, 3) - + at(2, 0) * at(1, 1) * at(3, 3) - + at(3, 0) * at(2, 1) * at(1, 3); + cof(0, 3) = -(at(1, 0) * at(2, 1) * at(3, 2) + + at(2, 0) * at(3, 1) * at(1, 2) + + at(3, 0) * at(1, 1) * at(2, 2) - + at(1, 0) * at(3, 1) * at(2, 2) - + at(2, 0) * at(1, 1) * at(3, 2) - + at(3, 0) * at(2, 1) * at(1, 2)); + cof(1, 0) = -(at(0, 1) * at(2, 2) * at(3, 3) + + at(2, 1) * at(3, 2) * at(0, 3) + + at(3, 1) * at(0, 2) * at(2, 3) - + at(0, 1) * at(3, 2) * at(2, 3) - + at(2, 1) * at(0, 2) * at(3, 3) - + at(3, 1) * at(2, 2) * at(0, 3)); + cof(1, 1) = at(0, 0) * at(2, 2) * at(3, 3) + + at(2, 0) * at(3, 2) * at(0, 3) + + at(3, 0) * at(0, 2) * at(2, 3) - + at(0, 0) * at(3, 2) * at(2, 3) - + at(2, 0) * at(0, 2) * at(3, 3) - + at(3, 0) * at(2, 2) * at(0, 3); + cof(1, 2) = -(at(0, 0) * at(2, 1) * at(3, 3) + + at(2, 0) * at(3, 1) * at(0, 3) + + at(3, 0) * at(0, 1) * at(2, 3) - + at(0, 0) * at(3, 1) * at(2, 3) - + at(2, 0) * at(0, 1) * at(3, 3) - + at(3, 0) * at(2, 1) * at(0, 3)); + cof(1, 3) = at(0, 0) * at(2, 1) * at(3, 2) + + at(2, 0) * at(3, 1) * at(0, 2) + + at(3, 0) * at(0, 1) * at(2, 2) - + at(0, 0) * at(3, 1) * at(2, 2) - + at(2, 0) * at(0, 1) * at(3, 2) - + at(3, 0) * at(2, 1) * at(0, 2); + cof(2, 0) = at(0, 1) * at(1, 2) * at(3, 3) + + at(1, 1) * at(3, 2) * at(0, 3) + + at(3, 1) * at(0, 2) * at(1, 3) - + at(0, 1) * at(3, 2) * at(1, 3) - + at(1, 1) * at(0, 2) * at(3, 3) - + at(3, 1) * at(1, 2) * at(0, 3); + cof(2, 1) = -(at(0, 0) * at(1, 2) * at(3, 3) + + at(1, 0) * at(3, 2) * at(0, 3) + + at(3, 0) * at(0, 2) * at(1, 3) - + at(0, 0) * at(3, 2) * at(1, 3) - + at(1, 0) * at(0, 2) * at(3, 3) - + at(3, 0) * at(1, 2) * at(0, 3)); + cof(2, 2) = at(0, 0) * at(1, 1) * at(3, 3) + + at(1, 0) * at(3, 1) * at(0, 3) + + at(3, 0) * at(0, 1) * at(1, 3) - + at(0, 0) * at(3, 1) * at(1, 3) - + at(1, 0) * at(0, 1) * at(3, 3) - + at(3, 0) * at(1, 1) * at(0, 3); + cof(2, 3) = -(at(0, 0) * at(1, 1) * at(3, 2) + + at(1, 0) * at(3, 1) * at(0, 2) + + at(3, 0) * at(0, 1) * at(1, 2) - + at(0, 0) * at(3, 1) * at(1, 2) - + at(1, 0) * at(0, 1) * at(3, 2) - + at(3, 0) * at(1, 1) * at(0, 2)); + cof(3, 0) = -(at(0, 1) * at(1, 2) * at(2, 3) + + at(1, 1) * at(2, 2) * at(0, 3) + + at(2, 1) * at(0, 2) * at(1, 3) - + at(0, 1) * at(2, 2) * at(1, 3) - + at(1, 1) * at(0, 2) * at(2, 3) - + at(2, 1) * at(1, 2) * at(0, 3)); + cof(3, 1) = at(0, 0) * at(1, 2) * at(2, 3) + + at(1, 0) * at(2, 2) * at(0, 3) + + at(2, 0) * at(0, 2) * at(1, 3) - + at(0, 0) * at(2, 2) * at(1, 3) - + at(1, 0) * at(0, 2) * at(2, 3) - + at(2, 0) * at(1, 2) * at(0, 3); + cof(3, 2) = -(at(0, 0) * at(1, 1) * at(2, 3) + + at(1, 0) * at(2, 1) * at(0, 3) + + at(2, 0) * at(0, 1) * at(1, 3) - + at(0, 0) * at(2, 1) * at(1, 3) - + at(1, 0) * at(0, 1) * at(2, 3) - + at(2, 0) * at(1, 1) * at(0, 3)); + cof(3, 3) = at(0, 0) * at(1, 1) * at(2, 2) + + at(1, 0) * at(2, 1) * at(0, 2) + + at(2, 0) * at(0, 1) * at(1, 2) - + at(0, 0) * at(2, 1) * at(1, 2) - + at(1, 0) * at(0, 1) * at(2, 2) - + at(2, 0) * at(1, 1) * at(0, 2); + break; + + default: + UNREACHABLE(); + break; + } + + // The inverse of A is the transpose of the cofactor matrix times the reciprocal of the determinant of A. + Matrix<T> adjugateMatrix(cof.transpose()); + T det = determinant(); + Matrix<T> result(std::vector<T>(mElements.size()), rows(), columns()); + for (unsigned int i = 0; i < rows(); i++) + for (unsigned int j = 0; j < columns(); j++) + result(i, j) = det ? adjugateMatrix(i, j) / det : T(); + + return result; + } + + private: + std::vector<T> mElements; + unsigned int mRows; + unsigned int mCols; +}; + +} // namespace angle + +#endif // COMMON_MATRIX_UTILS_H_ + diff --git a/src/3rdparty/angle/src/common/platform.h b/src/3rdparty/angle/src/common/platform.h index 3a2aa91bed..be4cb94987 100644 --- a/src/3rdparty/angle/src/common/platform.h +++ b/src/3rdparty/angle/src/common/platform.h @@ -53,9 +53,7 @@ # if defined(ANGLE_ENABLE_D3D9) # include <d3d9.h> -# if !defined(ANGLE_TRANSLATOR_IMPLEMENTATION) # include <d3dcompiler.h> -# endif # endif # if defined(ANGLE_ENABLE_D3D11) @@ -72,9 +70,7 @@ # include <d3d11_1.h> # include <dxgi1_2.h> # endif -# if !defined(ANGLE_TRANSLATOR_IMPLEMENTATION) # include <d3dcompiler.h> -# endif # endif # if defined(ANGLE_ENABLE_WINDOWS_STORE) @@ -87,11 +83,6 @@ # endif # endif -# if defined(_MSC_VER) && (_MSC_VER <= 1600) -# define final -# define override -# endif - # undef near # undef far #endif diff --git a/src/3rdparty/angle/src/common/string_utils.cpp b/src/3rdparty/angle/src/common/string_utils.cpp new file mode 100644 index 0000000000..acb0376bf9 --- /dev/null +++ b/src/3rdparty/angle/src/common/string_utils.cpp @@ -0,0 +1,136 @@ +// +// 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. +// +// string_utils: +// String helper functions. +// + +#include "string_utils.h" + +#include <fstream> +#include <sstream> + +namespace angle +{ + +const char kWhitespaceASCII[] = " \f\n\r\t\v"; + +std::vector<std::string> SplitString(const std::string &input, + const std::string &delimiters, + WhitespaceHandling whitespace, + SplitResult resultType) +{ + std::vector<std::string> result; + if (input.empty()) + { + return result; + } + + std::string::size_type start = 0; + while (start != std::string::npos) + { + auto end = input.find_first_of(delimiters, start); + + std::string piece; + if (end == std::string::npos) + { + piece = input.substr(start); + start = std::string::npos; + } + else + { + piece = input.substr(start, end - start); + start = end + 1; + } + + if (whitespace == TRIM_WHITESPACE) + { + piece = TrimString(piece, kWhitespaceASCII); + } + + if (resultType == SPLIT_WANT_ALL || !piece.empty()) + { + result.push_back(piece); + } + } + + return result; +} + +void SplitStringAlongWhitespace(const std::string &input, + std::vector<std::string> *tokensOut) +{ + + std::istringstream stream(input); + std::string line; + + while (std::getline(stream, line)) + { + size_t prev = 0, pos; + while ((pos = line.find_first_of(kWhitespaceASCII, prev)) != std::string::npos) + { + if (pos > prev) + tokensOut->push_back(line.substr(prev, pos - prev)); + prev = pos + 1; + } + if (prev < line.length()) + tokensOut->push_back(line.substr(prev, std::string::npos)); + } +} + +std::string TrimString(const std::string &input, const std::string &trimChars) +{ + auto begin = input.find_first_not_of(trimChars); + if (begin == std::string::npos) + { + return ""; + } + + std::string::size_type end = input.find_last_not_of(trimChars); + if (end == std::string::npos) + { + return input.substr(begin); + } + + return input.substr(begin, end - begin + 1); +} + +bool HexStringToUInt(const std::string &input, unsigned int *uintOut) +{ + unsigned int offset = 0; + + if (input.size() >= 2 && input[0] == '0' && input[1] == 'x') + { + offset = 2u; + } + + // Simple validity check + if (input.find_first_not_of("0123456789ABCDEFabcdef", offset) != std::string::npos) + { + return false; + } + + std::stringstream inStream(input); + inStream >> std::hex >> *uintOut; + return !inStream.fail(); +} + +bool ReadFileToString(const std::string &path, std::string *stringOut) +{ + std::ifstream inFile(path.c_str()); + if (inFile.fail()) + { + return false; + } + + inFile.seekg(0, std::ios::end); + stringOut->reserve(static_cast<std::string::size_type>(inFile.tellg())); + inFile.seekg(0, std::ios::beg); + + stringOut->assign(std::istreambuf_iterator<char>(inFile), std::istreambuf_iterator<char>()); + return !inFile.fail(); +} + +} diff --git a/src/3rdparty/angle/src/common/string_utils.h b/src/3rdparty/angle/src/common/string_utils.h new file mode 100644 index 0000000000..131b17e086 --- /dev/null +++ b/src/3rdparty/angle/src/common/string_utils.h @@ -0,0 +1,49 @@ +// +// 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. +// +// string_utils: +// String helper functions. +// + +#ifndef LIBANGLE_STRING_UTILS_H_ +#define LIBANGLE_STRING_UTILS_H_ + +#include <string> +#include <vector> + +namespace angle +{ + +extern const char kWhitespaceASCII[]; + +enum WhitespaceHandling +{ + KEEP_WHITESPACE, + TRIM_WHITESPACE, +}; + +enum SplitResult +{ + SPLIT_WANT_ALL, + SPLIT_WANT_NONEMPTY, +}; + +std::vector<std::string> SplitString(const std::string &input, + const std::string &delimiters, + WhitespaceHandling whitespace, + SplitResult resultType); + +void SplitStringAlongWhitespace(const std::string &input, + std::vector<std::string> *tokensOut); + +std::string TrimString(const std::string &input, const std::string &trimChars); + +bool HexStringToUInt(const std::string &input, unsigned int *uintOut); + +bool ReadFileToString(const std::string &path, std::string *stringOut); + +} + +#endif // LIBANGLE_STRING_UTILS_H_ diff --git a/src/3rdparty/angle/src/common/utilities.cpp b/src/3rdparty/angle/src/common/utilities.cpp index 501e9c2564..2ab913b10f 100644 --- a/src/3rdparty/angle/src/common/utilities.cpp +++ b/src/3rdparty/angle/src/common/utilities.cpp @@ -19,6 +19,78 @@ # include <windows.graphics.display.h> #endif +namespace +{ + +template <class IndexType> +gl::IndexRange ComputeTypedIndexRange(const IndexType *indices, + size_t count, + bool primitiveRestartEnabled, + GLuint primitiveRestartIndex) +{ + ASSERT(count > 0); + + IndexType minIndex = 0; + IndexType maxIndex = 0; + size_t nonPrimitiveRestartIndices = 0; + + if (primitiveRestartEnabled) + { + // Find the first non-primitive restart index to initialize the min and max values + size_t i = 0; + for (; i < count; i++) + { + if (indices[i] != primitiveRestartIndex) + { + minIndex = indices[i]; + maxIndex = indices[i]; + nonPrimitiveRestartIndices++; + break; + } + } + + // Loop over the rest of the indices + for (; i < count; i++) + { + if (indices[i] != primitiveRestartIndex) + { + if (minIndex > indices[i]) + { + minIndex = indices[i]; + } + if (maxIndex < indices[i]) + { + maxIndex = indices[i]; + } + nonPrimitiveRestartIndices++; + } + } + } + else + { + minIndex = indices[0]; + maxIndex = indices[0]; + nonPrimitiveRestartIndices = count; + + for (size_t i = 1; i < count; i++) + { + if (minIndex > indices[i]) + { + minIndex = indices[i]; + } + if (maxIndex < indices[i]) + { + maxIndex = indices[i]; + } + } + } + + return gl::IndexRange(static_cast<size_t>(minIndex), static_cast<size_t>(maxIndex), + nonPrimitiveRestartIndices); +} + +} // anonymous namespace + namespace gl { @@ -279,6 +351,39 @@ bool IsSamplerType(GLenum type) return false; } +GLenum SamplerTypeToTextureType(GLenum samplerType) +{ + switch (samplerType) + { + case GL_SAMPLER_2D: + case GL_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_SAMPLER_2D_SHADOW: + return GL_TEXTURE_2D; + + case GL_SAMPLER_CUBE: + case GL_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_SAMPLER_CUBE_SHADOW: + return GL_TEXTURE_CUBE_MAP; + + case GL_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_SAMPLER_2D_ARRAY_SHADOW: + return GL_TEXTURE_2D_ARRAY; + + case GL_SAMPLER_3D: + case GL_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_3D: + return GL_TEXTURE_3D; + + default: + UNREACHABLE(); + return 0; + } +} + bool IsMatrixType(GLenum type) { return VariableRowCount(type) > 1; @@ -366,6 +471,47 @@ GLenum LayerIndexToCubeMapTextureTarget(size_t index) return FirstCubeMapTextureTarget + static_cast<GLenum>(index); } +IndexRange ComputeIndexRange(GLenum indexType, + const GLvoid *indices, + size_t count, + bool primitiveRestartEnabled) +{ + switch (indexType) + { + case GL_UNSIGNED_BYTE: + return ComputeTypedIndexRange(static_cast<const GLubyte *>(indices), count, + primitiveRestartEnabled, + GetPrimitiveRestartIndex(indexType)); + case GL_UNSIGNED_SHORT: + return ComputeTypedIndexRange(static_cast<const GLushort *>(indices), count, + primitiveRestartEnabled, + GetPrimitiveRestartIndex(indexType)); + case GL_UNSIGNED_INT: + return ComputeTypedIndexRange(static_cast<const GLuint *>(indices), count, + primitiveRestartEnabled, + GetPrimitiveRestartIndex(indexType)); + default: + UNREACHABLE(); + return IndexRange(); + } +} + +GLuint GetPrimitiveRestartIndex(GLenum indexType) +{ + switch (indexType) + { + case GL_UNSIGNED_BYTE: + return 0xFF; + case GL_UNSIGNED_SHORT: + return 0xFFFF; + case GL_UNSIGNED_INT: + return 0xFFFFFFFF; + default: + UNREACHABLE(); + return 0; + } +} + bool IsTriangleMode(GLenum drawMode) { switch (drawMode) @@ -462,6 +608,146 @@ int VariableSortOrder(GLenum type) } } +std::string ParseUniformName(const std::string &name, size_t *outSubscript) +{ + // 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 (outSubscript) + { + *outSubscript = GL_INVALID_INDEX; + } + return name; + } + + if (outSubscript) + { + int index = atoi(name.substr(open + 1).c_str()); + if (index >= 0) + { + *outSubscript = index; + } + else + { + *outSubscript = GL_INVALID_INDEX; + } + } + + return name.substr(0, open); +} + +unsigned int ParseAndStripArrayIndex(std::string *name) +{ + 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) + { + subscript = atoi(name->c_str() + open + 1); + name->erase(open); + } + + return subscript; +} + +} // namespace gl + +namespace egl +{ +static_assert(EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR - EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR == 1, + "Unexpected EGL cube map enum value."); +static_assert(EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR - EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR == 2, + "Unexpected EGL cube map enum value."); +static_assert(EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR - EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR == 3, + "Unexpected EGL cube map enum value."); +static_assert(EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR - EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR == 4, + "Unexpected EGL cube map enum value."); +static_assert(EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR - EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR == 5, + "Unexpected EGL cube map enum value."); + +bool IsCubeMapTextureTarget(EGLenum target) +{ + return (target >= FirstCubeMapTextureTarget && target <= LastCubeMapTextureTarget); +} + +size_t CubeMapTextureTargetToLayerIndex(EGLenum target) +{ + ASSERT(IsCubeMapTextureTarget(target)); + return target - static_cast<size_t>(FirstCubeMapTextureTarget); +} + +EGLenum LayerIndexToCubeMapTextureTarget(size_t index) +{ + ASSERT(index <= (LastCubeMapTextureTarget - FirstCubeMapTextureTarget)); + return FirstCubeMapTextureTarget + static_cast<GLenum>(index); +} + +bool IsTextureTarget(EGLenum target) +{ + switch (target) + { + case EGL_GL_TEXTURE_2D_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR: + case EGL_GL_TEXTURE_3D_KHR: + return true; + + default: + return false; + } +} + +bool IsRenderbufferTarget(EGLenum target) +{ + return target == EGL_GL_RENDERBUFFER_KHR; +} +} + +namespace egl_gl +{ +GLenum EGLCubeMapTargetToGLCubeMapTarget(EGLenum eglTarget) +{ + ASSERT(egl::IsCubeMapTextureTarget(eglTarget)); + return gl::LayerIndexToCubeMapTextureTarget(egl::CubeMapTextureTargetToLayerIndex(eglTarget)); +} + +GLenum EGLImageTargetToGLTextureTarget(EGLenum eglTarget) +{ + switch (eglTarget) + { + case EGL_GL_TEXTURE_2D_KHR: + return GL_TEXTURE_2D; + + case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR: + case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR: + return EGLCubeMapTargetToGLCubeMapTarget(eglTarget); + + case EGL_GL_TEXTURE_3D_KHR: + return GL_TEXTURE_3D; + + default: + UNREACHABLE(); + return GL_NONE; + } +} + +GLuint EGLClientBufferToGLObjectHandle(EGLClientBuffer buffer) +{ + return static_cast<GLuint>(reinterpret_cast<uintptr_t>(buffer)); +} } #if !defined(ANGLE_ENABLE_WINDOWS_STORE) @@ -511,32 +797,7 @@ void writeFile(const char* path, const void* content, size_t size) // to run, the function returns immediately, and the thread continues execution. void ScheduleYield() { -#if defined(ANGLE_ENABLE_WINDOWS_STORE) - // This implementation of Sleep exists because it is not available prior to Update 4. - static HANDLE singletonEvent = nullptr; - HANDLE sleepEvent = singletonEvent; - if (!sleepEvent) - { - sleepEvent = CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); - - if (!sleepEvent) - return; - - HANDLE previousEvent = InterlockedCompareExchangePointerRelease(&singletonEvent, sleepEvent, nullptr); - - if (previousEvent) - { - // Back out if multiple threads try to demand create at the same time. - CloseHandle(sleepEvent); - sleepEvent = previousEvent; - } - } - - // Emulate sleep by waiting with timeout on an event that is never signalled. - WaitForSingleObjectEx(sleepEvent, 0, false); -#else Sleep(0); -#endif } #endif diff --git a/src/3rdparty/angle/src/common/utilities.h b/src/3rdparty/angle/src/common/utilities.h index 9f7f5e03c0..dc09011a26 100644 --- a/src/3rdparty/angle/src/common/utilities.h +++ b/src/3rdparty/angle/src/common/utilities.h @@ -9,10 +9,15 @@ #ifndef COMMON_UTILITIES_H_ #define COMMON_UTILITIES_H_ +#include <EGL/egl.h> +#include <EGL/eglext.h> + #include "angle_gl.h" #include <string> #include <math.h> +#include "common/mathutil.h" + namespace gl { @@ -25,6 +30,7 @@ GLenum VariableBoolVectorType(GLenum type); int VariableRowCount(GLenum type); int VariableColumnCount(GLenum type); bool IsSamplerType(GLenum type); +GLenum SamplerTypeToTextureType(GLenum samplerType); bool IsMatrixType(GLenum type); GLenum TransposeMatrixType(GLenum type); int VariableRegisterCount(GLenum type); @@ -40,6 +46,20 @@ 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); + +// 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. +IndexRange ComputeIndexRange(GLenum indexType, + const GLvoid *indices, + size_t count, + bool primitiveRestartEnabled); + +// Get the primitive restart index value for the given index type. +GLuint GetPrimitiveRestartIndex(GLenum indexType); + bool IsTriangleMode(GLenum drawMode); // [OpenGL ES 3.0.2] Section 2.3.1 page 14 @@ -48,6 +68,26 @@ bool IsTriangleMode(GLenum drawMode); 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); } +unsigned int ParseAndStripArrayIndex(std::string *name); + +} // namespace gl + +namespace egl +{ +static const EGLenum FirstCubeMapTextureTarget = EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR; +static const EGLenum LastCubeMapTextureTarget = EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR; +bool IsCubeMapTextureTarget(EGLenum target); +size_t CubeMapTextureTargetToLayerIndex(EGLenum target); +EGLenum LayerIndexToCubeMapTextureTarget(size_t index); +bool IsTextureTarget(EGLenum target); +bool IsRenderbufferTarget(EGLenum target); +} + +namespace egl_gl +{ +GLenum EGLCubeMapTargetToGLCubeMapTarget(EGLenum eglTarget); +GLenum EGLImageTargetToGLTextureTarget(EGLenum eglTarget); +GLuint EGLClientBufferToGLObjectHandle(EGLClientBuffer buffer); } #if !defined(ANGLE_ENABLE_WINDOWS_STORE) diff --git a/src/3rdparty/angle/src/common/version.h b/src/3rdparty/angle/src/common/version.h index 758c78d44a..e7ffa7cab3 100644 --- a/src/3rdparty/angle/src/common/version.h +++ b/src/3rdparty/angle/src/common/version.h @@ -12,12 +12,17 @@ #define ANGLE_MAJOR_VERSION 2 #define ANGLE_MINOR_VERSION 1 +#ifndef ANGLE_REVISION +#define ANGLE_REVISION 0 +#endif + #define ANGLE_STRINGIFY(x) #x #define ANGLE_MACRO_STRINGIFY(x) ANGLE_STRINGIFY(x) #define ANGLE_VERSION_STRING \ ANGLE_MACRO_STRINGIFY(ANGLE_MAJOR_VERSION) "." \ ANGLE_MACRO_STRINGIFY(ANGLE_MINOR_VERSION) "." \ + ANGLE_MACRO_STRINGIFY(ANGLE_REVISION) "." \ ANGLE_COMMIT_HASH #endif // COMMON_VERSION_H_ diff --git a/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.cpp b/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.cpp index cf60bc2349..68c6e9cea4 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.cpp +++ b/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.cpp @@ -78,6 +78,8 @@ std::string Diagnostics::message(ID id) return "Not enough arguments for macro"; case PP_MACRO_TOO_MANY_ARGS: return "Too many arguments for macro"; + case PP_MACRO_DUPLICATE_PARAMETER_NAMES: + return "duplicate macro parameter name"; case PP_CONDITIONAL_ENDIF_WITHOUT_IF: return "unexpected #endif found without a matching #if"; case PP_CONDITIONAL_ELSE_WITHOUT_IF: @@ -103,12 +105,16 @@ std::string Diagnostics::message(ID id) case PP_VERSION_NOT_FIRST_STATEMENT: return "#version directive must occur before anything else, " "except for comments and white space"; + case PP_VERSION_NOT_FIRST_LINE_ESSL3: + return "#version directive must occur on the first line of the shader"; case PP_INVALID_LINE_NUMBER: return "invalid line number"; case PP_INVALID_FILE_NUMBER: return "invalid file number"; case PP_INVALID_LINE_DIRECTIVE: return "invalid line directive"; + case PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3: + return "extension directive must occur before any non-preprocessor tokens in ESSL3"; // Errors end. // Warnings begin. case PP_EOF_IN_DIRECTIVE: @@ -117,6 +123,10 @@ std::string Diagnostics::message(ID id) return "unexpected token after conditional expression"; case PP_UNRECOGNIZED_PRAGMA: return "unrecognized pragma"; + case PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1: + return "extension directive should occur before any non-preprocessor tokens"; + case PP_WARNING_MACRO_NAME_RESERVED: + return "macro name with a double underscore is reserved - unintented behavior is possible"; // Warnings end. default: assert(false); diff --git a/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.h b/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.h index 5922d03857..d26c174f01 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.h @@ -46,27 +46,32 @@ class Diagnostics PP_MACRO_UNTERMINATED_INVOCATION, PP_MACRO_TOO_FEW_ARGS, PP_MACRO_TOO_MANY_ARGS, + PP_MACRO_DUPLICATE_PARAMETER_NAMES, PP_CONDITIONAL_ENDIF_WITHOUT_IF, PP_CONDITIONAL_ELSE_WITHOUT_IF, PP_CONDITIONAL_ELSE_AFTER_ELSE, PP_CONDITIONAL_ELIF_WITHOUT_IF, PP_CONDITIONAL_ELIF_AFTER_ELSE, PP_CONDITIONAL_UNTERMINATED, + PP_CONDITIONAL_UNEXPECTED_TOKEN, PP_INVALID_EXTENSION_NAME, PP_INVALID_EXTENSION_BEHAVIOR, PP_INVALID_EXTENSION_DIRECTIVE, PP_INVALID_VERSION_NUMBER, PP_INVALID_VERSION_DIRECTIVE, PP_VERSION_NOT_FIRST_STATEMENT, + PP_VERSION_NOT_FIRST_LINE_ESSL3, PP_INVALID_LINE_NUMBER, PP_INVALID_FILE_NUMBER, PP_INVALID_LINE_DIRECTIVE, + PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3, PP_ERROR_END, PP_WARNING_BEGIN, PP_EOF_IN_DIRECTIVE, - PP_CONDITIONAL_UNEXPECTED_TOKEN, PP_UNRECOGNIZED_PRAGMA, + PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1, + PP_WARNING_MACRO_NAME_RESERVED, PP_WARNING_END }; diff --git a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.cpp b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.cpp index 7803ee845a..2faa331378 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.cpp +++ b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.cpp @@ -6,6 +6,7 @@ #include "DirectiveParser.h" +#include <algorithm> #include <cassert> #include <cstdlib> #include <sstream> @@ -118,14 +119,12 @@ void skipUntilEOD(pp::Lexer *lexer, pp::Token *token) bool isMacroNameReserved(const std::string &name) { // Names prefixed with "GL_" are reserved. - if (name.substr(0, 3) == "GL_") - return true; - - // Names containing two consecutive underscores are reserved. - if (name.find("__") != std::string::npos) - return true; + return (name.substr(0, 3) == "GL_"); +} - return false; +bool hasDoubleUnderscores(const std::string &name) +{ + return (name.find("__") != std::string::npos); } bool isMacroPredefined(const std::string &name, @@ -140,80 +139,17 @@ bool isMacroPredefined(const std::string &name, namespace pp { -class DefinedParser : public Lexer -{ - public: - DefinedParser(Lexer *lexer, - const MacroSet *macroSet, - Diagnostics *diagnostics) - : mLexer(lexer), - mMacroSet(macroSet), - mDiagnostics(diagnostics) - { - } - - protected: - virtual void lex(Token *token) - { - const char kDefined[] = "defined"; - - mLexer->lex(token); - if (token->type != Token::IDENTIFIER) - return; - if (token->text != kDefined) - return; - - bool paren = false; - mLexer->lex(token); - if (token->type == '(') - { - paren = true; - mLexer->lex(token); - } - - if (token->type != Token::IDENTIFIER) - { - mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, - token->location, token->text); - skipUntilEOD(mLexer, token); - return; - } - MacroSet::const_iterator iter = mMacroSet->find(token->text); - std::string expression = iter != mMacroSet->end() ? "1" : "0"; - - if (paren) - { - mLexer->lex(token); - if (token->type != ')') - { - mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, - token->location, token->text); - skipUntilEOD(mLexer, token); - return; - } - } - - // We have a valid defined operator. - // Convert the current token into a CONST_INT token. - token->type = Token::CONST_INT; - token->text = expression; - } - - private: - Lexer *mLexer; - const MacroSet *mMacroSet; - Diagnostics *mDiagnostics; -}; - DirectiveParser::DirectiveParser(Tokenizer *tokenizer, MacroSet *macroSet, Diagnostics *diagnostics, DirectiveHandler *directiveHandler) : mPastFirstStatement(false), + mSeenNonPreprocessorToken(false), mTokenizer(tokenizer), mMacroSet(macroSet), mDiagnostics(diagnostics), - mDirectiveHandler(directiveHandler) + mDirectiveHandler(directiveHandler), + mShaderVersion(100) { } @@ -228,6 +164,10 @@ void DirectiveParser::lex(Token *token) parseDirective(token); mPastFirstStatement = true; } + else if (!isEOD(token)) + { + mSeenNonPreprocessorToken = true; + } if (token->type == Token::LAST) { @@ -349,6 +289,16 @@ void DirectiveParser::parseDefine(Token *token) token->location, token->text); return; } + // Using double underscores is allowed, but may result in unintended + // behavior, so a warning is issued. At the time of writing this was + // specified in ESSL 3.10, but the intent judging from Khronos + // discussions and dEQP tests was that double underscores should be + // allowed in earlier ESSL versions too. + if (hasDoubleUnderscores(token->text)) + { + mDiagnostics->report(Diagnostics::PP_WARNING_MACRO_NAME_RESERVED, token->location, + token->text); + } Macro macro; macro.type = Macro::kTypeObj; @@ -364,6 +314,14 @@ void DirectiveParser::parseDefine(Token *token) mTokenizer->lex(token); if (token->type != Token::IDENTIFIER) break; + + if (std::find(macro.parameters.begin(), macro.parameters.end(), token->text) != macro.parameters.end()) + { + mDiagnostics->report(Diagnostics::PP_MACRO_DUPLICATE_PARAMETER_NAMES, + token->location, token->text); + return; + } + macro.parameters.push_back(token->text); mTokenizer->lex(token); // Get ','. @@ -435,6 +393,12 @@ void DirectiveParser::parseUndef(Token *token) } mTokenizer->lex(token); + if (!isEOD(token)) + { + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, + token->location, token->text); + skipUntilEOD(mTokenizer, token); + } } void DirectiveParser::parseIf(Token *token) @@ -486,7 +450,7 @@ void DirectiveParser::parseElse(Token *token) block.skipGroup = block.foundValidGroup; block.foundValidGroup = true; - // Warn if there are extra tokens after #else. + // Check if there are extra tokens after #else. mTokenizer->lex(token); if (!isEOD(token)) { @@ -550,7 +514,7 @@ void DirectiveParser::parseEndif(Token *token) mConditionalStack.pop_back(); - // Warn if there are tokens after #endif. + // Check if there are tokens after #endif. mTokenizer->lex(token); if (!isEOD(token)) { @@ -699,6 +663,20 @@ void DirectiveParser::parseExtension(Token *token) token->location, token->text); valid = false; } + if (valid && mSeenNonPreprocessorToken) + { + if (mShaderVersion >= 300) + { + mDiagnostics->report(Diagnostics::PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3, + token->location, token->text); + valid = false; + } + else + { + mDiagnostics->report(Diagnostics::PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1, + token->location, token->text); + } + } if (valid) mDirectiveHandler->handleExtension(token->location, name, behavior); } @@ -775,9 +753,18 @@ void DirectiveParser::parseVersion(Token *token) valid = false; } + if (valid && version >= 300 && token->location.line > 1) + { + mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_LINE_ESSL3, + token->location, token->text); + valid = false; + } + if (valid) { mDirectiveHandler->handleVersion(token->location, version); + mShaderVersion = version; + PredefineMacro(mMacroSet, "__VERSION__", version); } } @@ -785,72 +772,60 @@ void DirectiveParser::parseLine(Token *token) { assert(getDirective(token) == DIRECTIVE_LINE); - enum State - { - LINE_NUMBER, - FILE_NUMBER - }; - bool valid = true; + bool parsedFileNumber = false; int line = 0, file = 0; - int state = LINE_NUMBER; - MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics); + MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, false); + + // Lex the first token after "#line" so we can check it for EOD. macroExpander.lex(token); - while ((token->type != '\n') && (token->type != Token::LAST)) + + if (isEOD(token)) { - switch (state++) + mDiagnostics->report(Diagnostics::PP_INVALID_LINE_DIRECTIVE, token->location, token->text); + valid = false; + } + else + { + ExpressionParser expressionParser(¯oExpander, mDiagnostics); + ExpressionParser::ErrorSettings errorSettings; + + // See GLES3 section 12.42 + errorSettings.integerLiteralsMustFit32BitSignedRange = true; + + errorSettings.unexpectedIdentifier = Diagnostics::PP_INVALID_LINE_NUMBER; + // The first token was lexed earlier to check if it was EOD. Include + // the token in parsing for a second time by setting the + // parsePresetToken flag to true. + expressionParser.parse(token, &line, true, errorSettings, &valid); + if (!isEOD(token) && valid) + { + errorSettings.unexpectedIdentifier = Diagnostics::PP_INVALID_FILE_NUMBER; + // After parsing the line expression expressionParser has also + // advanced to the first token of the file expression - this is the + // token that makes the parser reduce the "input" rule for the line + // expression and stop. So we're using parsePresetToken = true here + // as well. + expressionParser.parse(token, &file, true, errorSettings, &valid); + parsedFileNumber = true; + } + if (!isEOD(token)) { - case LINE_NUMBER: - if (valid && (token->type != Token::CONST_INT)) - { - mDiagnostics->report(Diagnostics::PP_INVALID_LINE_NUMBER, - token->location, token->text); - valid = false; - } - if (valid && !token->iValue(&line)) - { - mDiagnostics->report(Diagnostics::PP_INTEGER_OVERFLOW, - token->location, token->text); - valid = false; - } - break; - case FILE_NUMBER: - if (valid && (token->type != Token::CONST_INT)) - { - mDiagnostics->report(Diagnostics::PP_INVALID_FILE_NUMBER, - token->location, token->text); - valid = false; - } - if (valid && !token->iValue(&file)) - { - mDiagnostics->report(Diagnostics::PP_INTEGER_OVERFLOW, - token->location, token->text); - valid = false; - } - break; - default: if (valid) { mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text); valid = false; } - break; + skipUntilEOD(mTokenizer, token); } - macroExpander.lex(token); } - if (valid && (state != FILE_NUMBER) && (state != FILE_NUMBER + 1)) - { - mDiagnostics->report(Diagnostics::PP_INVALID_LINE_DIRECTIVE, - token->location, token->text); - valid = false; - } if (valid) { mTokenizer->setLineNumber(line); - if (state == FILE_NUMBER + 1) + if (parsedFileNumber) mTokenizer->setFileNumber(file); } } @@ -910,15 +885,18 @@ int DirectiveParser::parseExpressionIf(Token *token) assert((getDirective(token) == DIRECTIVE_IF) || (getDirective(token) == DIRECTIVE_ELIF)); - DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics); - MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics); + MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, true); ExpressionParser expressionParser(¯oExpander, mDiagnostics); int expression = 0; - macroExpander.lex(token); - expressionParser.parse(token, &expression); + ExpressionParser::ErrorSettings errorSettings; + errorSettings.integerLiteralsMustFit32BitSignedRange = false; + errorSettings.unexpectedIdentifier = Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN; + + bool valid = true; + expressionParser.parse(token, &expression, false, errorSettings, &valid); - // Warn if there are tokens after #if expression. + // Check if there are tokens after #if expression. if (!isEOD(token)) { mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, @@ -946,7 +924,7 @@ int DirectiveParser::parseExpressionIfdef(Token *token) MacroSet::const_iterator iter = mMacroSet->find(token->text); int expression = iter != mMacroSet->end() ? 1 : 0; - // Warn if there are tokens after #ifdef expression. + // Check if there are tokens after #ifdef expression. mTokenizer->lex(token); if (!isEOD(token)) { diff --git a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.h b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.h index e1acdbb8d0..2888e289ce 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.h @@ -27,7 +27,7 @@ class DirectiveParser : public Lexer Diagnostics *diagnostics, DirectiveHandler *directiveHandler); - virtual void lex(Token *token); + void lex(Token *token) override; private: PP_DISALLOW_COPY_AND_ASSIGN(DirectiveParser); @@ -70,11 +70,14 @@ class DirectiveParser : public Lexer } }; bool mPastFirstStatement; + bool mSeenNonPreprocessorToken; // Tracks if a non-preprocessor token has been seen yet. Some macros, such as + // #extension must be declared before all shader code. std::vector<ConditionalBlock> mConditionalStack; Tokenizer *mTokenizer; MacroSet *mMacroSet; Diagnostics *mDiagnostics; DirectiveHandler *mDirectiveHandler; + int mShaderVersion; }; } // namespace pp diff --git a/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.h b/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.h index 4b80ba7261..841c67b61c 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.h @@ -7,21 +7,31 @@ #ifndef COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_ #define COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_ +#include "DiagnosticsBase.h" #include "pp_utils.h" namespace pp { -class Diagnostics; class Lexer; struct Token; class ExpressionParser { public: + struct ErrorSettings + { + Diagnostics::ID unexpectedIdentifier; + bool integerLiteralsMustFit32BitSignedRange; + }; + ExpressionParser(Lexer *lexer, Diagnostics *diagnostics); - bool parse(Token *token, int *result); + bool parse(Token *token, + int *result, + bool parsePresetToken, + const ErrorSettings &errorSettings, + bool *valid); private: PP_DISALLOW_COPY_AND_ASSIGN(ExpressionParser); diff --git a/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.y b/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.y index 8caf36bfc8..7b5d9e9cee 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.y +++ b/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.y @@ -28,7 +28,7 @@ WHICH GENERATES THE GLSL ES preprocessor expression parser. #pragma GCC diagnostic ignored "-Wuninitialized" #endif #elif defined(_MSC_VER) -#pragma warning(disable: 4065 4701 4702) +#pragma warning(disable: 4065 4244 4701 4702) #endif #include "ExpressionParser.h" @@ -52,6 +52,7 @@ typedef __int64 YYSTYPE; #include <stdint.h> typedef intmax_t YYSTYPE; #endif // _MSC_VER + #define YYENABLE_NLS 0 #define YYLTYPE_IS_TRIVIAL 1 #define YYSTYPE_IS_TRIVIAL 1 @@ -64,6 +65,17 @@ struct Context pp::Lexer* lexer; pp::Token* token; int* result; + bool parsePresetToken; + + pp::ExpressionParser::ErrorSettings errorSettings; + bool *valid; + + void startIgnoreErrors() { ++ignoreErrors; } + void endIgnoreErrors() { --ignoreErrors; } + + bool isIgnoringErrors() { return ignoreErrors > 0; } + + int ignoreErrors; }; } // namespace %} @@ -79,6 +91,7 @@ static void yyerror(Context* context, const char* reason); %} %token TOK_CONST_INT +%token TOK_IDENTIFIER %left TOK_OP_OR %left TOK_OP_AND %left '|' @@ -102,11 +115,58 @@ input expression : TOK_CONST_INT - | expression TOK_OP_OR expression { - $$ = $1 || $3; + | TOK_IDENTIFIER { + if (!context->isIgnoringErrors()) + { + // This rule should be applied right after the token is lexed, so we can + // refer to context->token in the error message. + context->diagnostics->report(context->errorSettings.unexpectedIdentifier, + context->token->location, context->token->text); + *(context->valid) = false; + } + $$ = $1; } - | expression TOK_OP_AND expression { - $$ = $1 && $3; + | expression TOK_OP_OR { + if ($1 != 0) + { + // Ignore errors in the short-circuited part of the expression. + // ESSL3.00 section 3.4: + // If an operand is not evaluated, the presence of undefined identifiers + // in the operand will not cause an error. + // Unevaluated division by zero should not cause an error either. + context->startIgnoreErrors(); + } + } expression { + if ($1 != 0) + { + context->endIgnoreErrors(); + $$ = static_cast<YYSTYPE>(1); + } + else + { + $$ = $1 || $4; + } + } + | expression TOK_OP_AND { + if ($1 == 0) + { + // Ignore errors in the short-circuited part of the expression. + // ESSL3.00 section 3.4: + // If an operand is not evaluated, the presence of undefined identifiers + // in the operand will not cause an error. + // Unevaluated division by zero should not cause an error either. + context->startIgnoreErrors(); + } + } expression { + if ($1 == 0) + { + context->endIgnoreErrors(); + $$ = static_cast<YYSTYPE>(0); + } + else + { + $$ = $1 && $4; + } } | expression '|' expression { $$ = $1 | $3; @@ -148,28 +208,42 @@ expression $$ = $1 + $3; } | expression '%' expression { - if ($3 == 0) { - std::ostringstream stream; - stream << $1 << " % " << $3; - std::string text = stream.str(); - context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO, - context->token->location, - text.c_str()); - YYABORT; - } else { + if ($3 == 0) + { + if (!context->isIgnoringErrors()) + { + std::ostringstream stream; + stream << $1 << " % " << $3; + std::string text = stream.str(); + context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO, + context->token->location, + text.c_str()); + *(context->valid) = false; + } + $$ = static_cast<YYSTYPE>(0); + } + else + { $$ = $1 % $3; } } | expression '/' expression { - if ($3 == 0) { - std::ostringstream stream; - stream << $1 << " / " << $3; - std::string text = stream.str(); - context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO, - context->token->location, - text.c_str()); - YYABORT; - } else { + if ($3 == 0) + { + if (!context->isIgnoringErrors()) + { + std::ostringstream stream; + stream << $1 << " / " << $3; + std::string text = stream.str(); + context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO, + context->token->location, + text.c_str()); + *(context->valid) = false; + } + $$ = static_cast<YYSTYPE>(0); + } + else + { $$ = $1 / $3; } } @@ -197,22 +271,35 @@ expression int yylex(YYSTYPE *lvalp, Context *context) { + pp::Token *token = context->token; + if (!context->parsePresetToken) + { + context->lexer->lex(token); + } + context->parsePresetToken = false; + int type = 0; - pp::Token *token = context->token; switch (token->type) { case pp::Token::CONST_INT: { unsigned int val = 0; - if (!token->uValue(&val)) + int testVal = 0; + if (!token->uValue(&val) || (!token->iValue(&testVal) && + context->errorSettings.integerLiteralsMustFit32BitSignedRange)) { context->diagnostics->report(pp::Diagnostics::PP_INTEGER_OVERFLOW, token->location, token->text); + *(context->valid) = false; } *lvalp = static_cast<YYSTYPE>(val); type = TOK_CONST_INT; break; } + case pp::Token::IDENTIFIER: + *lvalp = static_cast<YYSTYPE>(-1); + type = TOK_IDENTIFIER; + break; case pp::Token::OP_OR: type = TOK_OP_OR; break; @@ -258,10 +345,6 @@ int yylex(YYSTYPE *lvalp, Context *context) break; } - // Advance to the next token if the current one is valid. - if (type != 0) - context->lexer->lex(token); - return type; } @@ -280,13 +363,21 @@ ExpressionParser::ExpressionParser(Lexer *lexer, Diagnostics *diagnostics) { } -bool ExpressionParser::parse(Token *token, int *result) +bool ExpressionParser::parse(Token *token, + int *result, + bool parsePresetToken, + const ErrorSettings &errorSettings, + bool *valid) { Context context; context.diagnostics = mDiagnostics; context.lexer = mLexer; context.token = token; context.result = result; + context.ignoreErrors = 0; + context.parsePresetToken = parsePresetToken; + context.errorSettings = errorSettings; + context.valid = valid; int ret = yyparse(&context); switch (ret) { diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Input.cpp b/src/3rdparty/angle/src/compiler/preprocessor/Input.cpp index f9910a6cc3..5541d46f72 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/Input.cpp +++ b/src/3rdparty/angle/src/compiler/preprocessor/Input.cpp @@ -29,13 +29,75 @@ Input::Input(size_t count, const char *const string[], const int length[]) : } } -size_t Input::read(char *buf, size_t maxSize) +const char *Input::skipChar() +{ + // This function should only be called when there is a character to skip. + assert(mReadLoc.cIndex < mLength[mReadLoc.sIndex]); + ++mReadLoc.cIndex; + if (mReadLoc.cIndex == mLength[mReadLoc.sIndex]) + { + ++mReadLoc.sIndex; + mReadLoc.cIndex = 0; + } + if (mReadLoc.sIndex >= mCount) + { + return nullptr; + } + return mString[mReadLoc.sIndex] + mReadLoc.cIndex; +} + +size_t Input::read(char *buf, size_t maxSize, int *lineNo) { size_t nRead = 0; - while ((nRead < maxSize) && (mReadLoc.sIndex < mCount)) + // The previous call to read might have stopped copying the string when encountering a line + // continuation. Check for this possibility first. + if (mReadLoc.sIndex < mCount && maxSize > 0) + { + const char *c = mString[mReadLoc.sIndex] + mReadLoc.cIndex; + if ((*c) == '\\') + { + c = skipChar(); + if (c != nullptr && (*c) == '\n') + { + // Line continuation of backslash + newline. + skipChar(); + ++(*lineNo); + } + else if (c != nullptr && (*c) == '\r') + { + // Line continuation. Could be backslash + '\r\n' or just backslash + '\r'. + c = skipChar(); + if (c != nullptr && (*c) == '\n') + { + skipChar(); + } + ++(*lineNo); + } + else + { + // Not line continuation, so write the skipped backslash to buf. + *buf = '\\'; + ++nRead; + } + } + } + + size_t maxRead = maxSize; + while ((nRead < maxRead) && (mReadLoc.sIndex < mCount)) { size_t size = mLength[mReadLoc.sIndex] - mReadLoc.cIndex; size = std::min(size, maxSize); + for (size_t i = 0; i < size; ++i) + { + // Stop if a possible line continuation is encountered. + // It will be processed on the next call on input, which skips it + // and increments line number if necessary. + if (*(mString[mReadLoc.sIndex] + mReadLoc.cIndex + i) == '\\') + { + size = i; + maxRead = nRead + size; // Stop reading right before the backslash. + } + } std::memcpy(buf + nRead, mString[mReadLoc.sIndex] + mReadLoc.cIndex, size); nRead += size; mReadLoc.cIndex += size; diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Input.h b/src/3rdparty/angle/src/compiler/preprocessor/Input.h index e951cb4d5f..a1de7ddd86 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/Input.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/Input.h @@ -33,7 +33,7 @@ class Input return mLength[index]; } - size_t read(char *buf, size_t maxSize); + size_t read(char *buf, size_t maxSize, int *lineNo); struct Location { @@ -49,6 +49,10 @@ class Input const Location &readLoc() const { return mReadLoc; } private: + // Skip a character and return the next character after the one that was skipped. + // Return nullptr if data runs out. + const char *skipChar(); + // Input. size_t mCount; const char * const *mString; diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Macro.cpp b/src/3rdparty/angle/src/compiler/preprocessor/Macro.cpp index 13cb14e3dc..4c4d5fd2e2 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/Macro.cpp +++ b/src/3rdparty/angle/src/compiler/preprocessor/Macro.cpp @@ -6,6 +6,8 @@ #include "Macro.h" +#include <sstream> + #include "Token.h" namespace pp @@ -19,5 +21,23 @@ bool Macro::equals(const Macro &other) const (replacements == other.replacements); } +void PredefineMacro(MacroSet *macroSet, const char *name, int value) +{ + std::ostringstream stream; + stream << value; + + Token token; + token.type = Token::CONST_INT; + token.text = stream.str(); + + Macro macro; + macro.predefined = true; + macro.type = Macro::kTypeObj; + macro.name = name; + macro.replacements.push_back(token); + + (*macroSet)[name] = macro; +} + } // namespace pp diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Macro.h b/src/3rdparty/angle/src/compiler/preprocessor/Macro.h index 7662a9c5a2..31ee22c26a 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/Macro.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/Macro.h @@ -45,6 +45,8 @@ struct Macro typedef std::map<std::string, Macro> MacroSet; +void PredefineMacro(MacroSet *macroSet, const char *name, int value); + } // namespace pp #endif // COMPILER_PREPROCESSOR_MACRO_H_ diff --git a/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.cpp b/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.cpp index 69e2f39069..e878ee345a 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.cpp +++ b/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.cpp @@ -26,7 +26,7 @@ class TokenLexer : public Lexer mIter = mTokens.begin(); } - virtual void lex(Token *token) + void lex(Token *token) override { if (mIter == mTokens.end()) { @@ -48,10 +48,9 @@ class TokenLexer : public Lexer MacroExpander::MacroExpander(Lexer *lexer, MacroSet *macroSet, - Diagnostics *diagnostics) - : mLexer(lexer), - mMacroSet(macroSet), - mDiagnostics(diagnostics) + Diagnostics *diagnostics, + bool parseDefined) + : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics), mParseDefined(parseDefined) { } @@ -67,11 +66,54 @@ void MacroExpander::lex(Token *token) { while (true) { + const char kDefined[] = "defined"; + getToken(token); if (token->type != Token::IDENTIFIER) break; + // Defined operator is parsed here since it may be generated by macro expansion. + // Defined operator produced by macro expansion has undefined behavior according to C++ + // spec, which the GLSL spec references (see C++14 draft spec section 16.1.4), but this + // behavior is needed for passing dEQP tests, which enforce stricter compatibility between + // implementations. + if (mParseDefined && token->text == kDefined) + { + bool paren = false; + getToken(token); + if (token->type == '(') + { + paren = true; + getToken(token); + } + if (token->type != Token::IDENTIFIER) + { + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, + token->text); + break; + } + auto iter = mMacroSet->find(token->text); + std::string expression = iter != mMacroSet->end() ? "1" : "0"; + + if (paren) + { + getToken(token); + if (token->type != ')') + { + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, + token->text); + break; + } + } + + // We have a valid defined operator. + // Convert the current token into a CONST_INT token. + token->type = Token::CONST_INT; + token->text = expression; + break; + } + if (token->expansionDisabled()) break; @@ -187,6 +229,12 @@ bool MacroExpander::expandMacro(const Macro ¯o, std::vector<Token> *replacements) { replacements->clear(); + + // In the case of an object-like macro, the replacement list gets its location + // from the identifier, but in the case of a function-like macro, the replacement + // list gets its location from the closing parenthesis of the macro invocation. + // This is tested by dEQP-GLES3.functional.shaders.preprocessor.predefined_macros.* + SourceLocation replacementLocation = identifier.location; if (macro.type == Macro::kTypeObj) { replacements->assign(macro.replacements.begin(), @@ -218,7 +266,7 @@ bool MacroExpander::expandMacro(const Macro ¯o, assert(macro.type == Macro::kTypeFunc); std::vector<MacroArg> args; args.reserve(macro.parameters.size()); - if (!collectMacroArgs(macro, identifier, &args)) + if (!collectMacroArgs(macro, identifier, &args, &replacementLocation)) return false; replaceMacroParams(macro, args, replacements); @@ -234,14 +282,15 @@ bool MacroExpander::expandMacro(const Macro ¯o, repl.setAtStartOfLine(identifier.atStartOfLine()); repl.setHasLeadingSpace(identifier.hasLeadingSpace()); } - repl.location = identifier.location; + repl.location = replacementLocation; } return true; } bool MacroExpander::collectMacroArgs(const Macro ¯o, const Token &identifier, - std::vector<MacroArg> *args) + std::vector<MacroArg> *args, + SourceLocation *closingParenthesisLocation) { Token token; getToken(&token); @@ -271,6 +320,7 @@ bool MacroExpander::collectMacroArgs(const Macro ¯o, case ')': --openParens; isArg = openParens != 0; + *closingParenthesisLocation = token.location; break; case ',': // The individual arguments are separated by comma tokens, but @@ -317,7 +367,7 @@ bool MacroExpander::collectMacroArgs(const Macro ¯o, { MacroArg &arg = args->at(i); TokenLexer lexer(&arg); - MacroExpander expander(&lexer, mMacroSet, mDiagnostics); + MacroExpander expander(&lexer, mMacroSet, mDiagnostics, mParseDefined); arg.clear(); expander.lex(&token); diff --git a/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.h b/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.h index 5a0c7751a8..3cc860d753 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.h @@ -19,14 +19,15 @@ namespace pp { class Diagnostics; +struct SourceLocation; class MacroExpander : public Lexer { public: - MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics); - virtual ~MacroExpander(); + MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics, bool parseDefined); + ~MacroExpander() override; - virtual void lex(Token *token); + void lex(Token *token) override; private: PP_DISALLOW_COPY_AND_ASSIGN(MacroExpander); @@ -45,7 +46,8 @@ class MacroExpander : public Lexer typedef std::vector<Token> MacroArg; bool collectMacroArgs(const Macro ¯o, const Token &identifier, - std::vector<MacroArg> *args); + std::vector<MacroArg> *args, + SourceLocation *closingParenthesisLocation); void replaceMacroParams(const Macro ¯o, const std::vector<MacroArg> &args, std::vector<Token> *replacements); @@ -79,6 +81,7 @@ class MacroExpander : public Lexer Lexer *mLexer; MacroSet *mMacroSet; Diagnostics *mDiagnostics; + bool mParseDefined; std::auto_ptr<Token> mReserveToken; std::vector<MacroContext *> mContextStack; diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.cpp b/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.cpp index 3522fa1abb..aeb9c46f9d 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.cpp +++ b/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.cpp @@ -7,7 +7,6 @@ #include "Preprocessor.h" #include <cassert> -#include <sstream> #include "DiagnosticsBase.h" #include "DirectiveParser.h" @@ -27,12 +26,11 @@ struct PreprocessorImpl DirectiveParser directiveParser; MacroExpander macroExpander; - PreprocessorImpl(Diagnostics *diag, - DirectiveHandler *directiveHandler) + PreprocessorImpl(Diagnostics *diag, DirectiveHandler *directiveHandler) : diagnostics(diag), tokenizer(diag), directiveParser(&tokenizer, ¯oSet, diag, directiveHandler), - macroExpander(&directiveParser, ¯oSet, diag) + macroExpander(&directiveParser, ¯oSet, diag, false) { } }; @@ -52,12 +50,12 @@ bool Preprocessor::init(size_t count, const char * const string[], const int length[]) { - static const int kGLSLVersion = 100; + static const int kDefaultGLSLVersion = 100; // Add standard pre-defined macros. predefineMacro("__LINE__", 0); predefineMacro("__FILE__", 0); - predefineMacro("__VERSION__", kGLSLVersion); + predefineMacro("__VERSION__", kDefaultGLSLVersion); predefineMacro("GL_ES", 1); return mImpl->tokenizer.init(count, string, length); @@ -65,20 +63,7 @@ bool Preprocessor::init(size_t count, void Preprocessor::predefineMacro(const char *name, int value) { - std::ostringstream stream; - stream << value; - - Token token; - token.type = Token::CONST_INT; - token.text = stream.str(); - - Macro macro; - macro.predefined = true; - macro.type = Macro::kTypeObj; - macro.name = name; - macro.replacements.push_back(token); - - mImpl->macroSet[name] = macro; + PredefineMacro(&mImpl->macroSet, name, value); } void Preprocessor::lex(Token *token) diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.h b/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.h index 78eb86dd3b..49e64fa209 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.h @@ -42,7 +42,7 @@ class Tokenizer : public Lexer void setLineNumber(int line); void setMaxTokenSize(size_t maxTokenSize); - virtual void lex(Token *token); + void lex(Token *token) override; private: PP_DISALLOW_COPY_AND_ASSIGN(Tokenizer); diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.l b/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.l index 89cb5c8596..d316da88b1 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.l +++ b/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.l @@ -23,6 +23,10 @@ IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh. } %{ +#if defined(_MSC_VER) +#pragma warning(disable: 4005) +#endif + #include "Tokenizer.h" #include "DiagnosticsBase.h" @@ -31,6 +35,15 @@ IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh. #if defined(__GNUC__) // Triggered by the auto-generated yy_fatal_error function. #pragma GCC diagnostic ignored "-Wmissing-noreturn" +#elif defined(_MSC_VER) +#pragma warning(disable: 4244) +#endif + +// Workaround for flex using the register keyword, deprecated in C++11. +#ifdef __cplusplus +#if __cplusplus > 199711L +#define register +#endif #endif typedef std::string YYSTYPE; @@ -64,7 +77,7 @@ typedef pp::SourceLocation YYLTYPE; } while(0); #define YY_INPUT(buf, result, maxSize) \ - result = yyextra->input.read(buf, maxSize); + result = yyextra->input.read(buf, maxSize, &yylineno); %} @@ -93,7 +106,7 @@ FRACTIONAL_CONSTANT ({DIGIT}*"."{DIGIT}+)|({DIGIT}+".") /* Block comment */ /* Line breaks are just counted - not returned. */ - /* The comment is replaced by a single space. */ + /* The comment is replaced by a single space. */ "/*" { BEGIN(COMMENT); } <COMMENT>[^*\r\n]+ <COMMENT>"*" diff --git a/src/3rdparty/angle/src/compiler/preprocessor/numeric_lex.h b/src/3rdparty/angle/src/compiler/preprocessor/numeric_lex.h index 58c51b0961..b32e42253f 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/numeric_lex.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/numeric_lex.h @@ -48,6 +48,15 @@ bool numeric_lex_int(const std::string &str, IntType *value) template<typename FloatType> bool numeric_lex_float(const std::string &str, FloatType *value) { +// On 64-bit Intel Android, istringstream is broken. Until this is fixed in +// a newer NDK, don't use it. Android doesn't have locale support, so this +// doesn't have to force the C locale. +// TODO(thakis): Remove this once this bug has been fixed in the NDK and +// that NDK has been rolled into chromium. +#if defined(ANGLE_PLATFORM_ANDROID) && __x86_64__ + *value = strtod(str.c_str(), nullptr); + return errno != ERANGE; +#else std::istringstream stream(str); // Force "C" locale so that decimal character is always '.', and // not dependent on the current locale. @@ -55,6 +64,7 @@ bool numeric_lex_float(const std::string &str, FloatType *value) stream >> (*value); return !stream.fail(); +#endif } } // namespace pp. diff --git a/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.cpp new file mode 100644 index 0000000000..31bfae9966 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.cpp @@ -0,0 +1,451 @@ +// +// Copyright (c) 2002-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. +// + +// Analysis of the AST needed for HLSL generation + +#include "compiler/translator/ASTMetadataHLSL.h" + +#include "compiler/translator/CallDAG.h" +#include "compiler/translator/SymbolTable.h" + +namespace +{ + +// Class used to traverse the AST of a function definition, checking if the +// function uses a gradient, and writing the set of control flow using gradients. +// It assumes that the analysis has already been made for the function's +// callees. +class PullGradient : public TIntermTraverser +{ + public: + PullGradient(MetadataList *metadataList, size_t index, const CallDAG &dag) + : TIntermTraverser(true, false, true), + mMetadataList(metadataList), + mMetadata(&(*metadataList)[index]), + mIndex(index), + mDag(dag) + { + ASSERT(index < metadataList->size()); + } + + void traverse(TIntermAggregate *node) + { + node->traverse(this); + ASSERT(mParents.empty()); + } + + // Called when a gradient operation or a call to a function using a gradient is found. + void onGradient() + { + mMetadata->mUsesGradient = true; + // Mark the latest control flow as using a gradient. + if (!mParents.empty()) + { + mMetadata->mControlFlowsContainingGradient.insert(mParents.back()); + } + } + + void visitControlFlow(Visit visit, TIntermNode *node) + { + if (visit == PreVisit) + { + mParents.push_back(node); + } + else if (visit == PostVisit) + { + ASSERT(mParents.back() == node); + mParents.pop_back(); + // A control flow's using a gradient means its parents are too. + if (mMetadata->mControlFlowsContainingGradient.count(node)> 0 && !mParents.empty()) + { + mMetadata->mControlFlowsContainingGradient.insert(mParents.back()); + } + } + } + + bool visitLoop(Visit visit, TIntermLoop *loop) override + { + visitControlFlow(visit, loop); + return true; + } + + bool visitSelection(Visit visit, TIntermSelection *selection) override + { + visitControlFlow(visit, selection); + return true; + } + + bool visitUnary(Visit visit, TIntermUnary *node) override + { + if (visit == PreVisit) + { + switch (node->getOp()) + { + case EOpDFdx: + case EOpDFdy: + onGradient(); + default: + break; + } + } + + return true; + } + + bool visitAggregate(Visit visit, TIntermAggregate *node) override + { + if (visit == PreVisit) + { + if (node->getOp() == EOpFunctionCall) + { + if (node->isUserDefined()) + { + size_t calleeIndex = mDag.findIndex(node); + ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex); + UNUSED_ASSERTION_VARIABLE(mIndex); + + if ((*mMetadataList)[calleeIndex].mUsesGradient) { + onGradient(); + } + } + else + { + TString name = TFunction::unmangleName(node->getName()); + + if (name == "texture2D" || + name == "texture2DProj" || + name == "textureCube") + { + onGradient(); + } + } + } + } + + return true; + } + + private: + MetadataList *mMetadataList; + ASTMetadataHLSL *mMetadata; + size_t mIndex; + const CallDAG &mDag; + + // Contains a stack of the control flow nodes that are parents of the node being + // currently visited. It is used to mark control flows using a gradient. + std::vector<TIntermNode*> mParents; +}; + +// Traverses the AST of a function definition to compute the the discontinuous loops +// and the if statements containing gradient loops. It assumes that the gradient loops +// (loops that contain a gradient) have already been computed and that it has already +// traversed the current function's callees. +class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser +{ + public: + PullComputeDiscontinuousAndGradientLoops(MetadataList *metadataList, + size_t index, + const CallDAG &dag) + : TIntermTraverser(true, false, true), + mMetadataList(metadataList), + mMetadata(&(*metadataList)[index]), + mIndex(index), + mDag(dag) + { + } + + void traverse(TIntermAggregate *node) + { + node->traverse(this); + ASSERT(mLoopsAndSwitches.empty()); + ASSERT(mIfs.empty()); + } + + // Called when traversing a gradient loop or a call to a function with a + // gradient loop in its call graph. + void onGradientLoop() + { + mMetadata->mHasGradientLoopInCallGraph = true; + // Mark the latest if as using a discontinuous loop. + if (!mIfs.empty()) + { + mMetadata->mIfsContainingGradientLoop.insert(mIfs.back()); + } + } + + bool visitLoop(Visit visit, TIntermLoop *loop) override + { + if (visit == PreVisit) + { + mLoopsAndSwitches.push_back(loop); + + if (mMetadata->hasGradientInCallGraph(loop)) + { + onGradientLoop(); + } + } + else if (visit == PostVisit) + { + ASSERT(mLoopsAndSwitches.back() == loop); + mLoopsAndSwitches.pop_back(); + } + + return true; + } + + bool visitSelection(Visit visit, TIntermSelection *node) override + { + if (visit == PreVisit) + { + mIfs.push_back(node); + } + else if (visit == PostVisit) + { + ASSERT(mIfs.back() == node); + mIfs.pop_back(); + // An if using a discontinuous loop means its parents ifs are also discontinuous. + if (mMetadata->mIfsContainingGradientLoop.count(node) > 0 && !mIfs.empty()) + { + mMetadata->mIfsContainingGradientLoop.insert(mIfs.back()); + } + } + + return true; + } + + bool visitBranch(Visit visit, TIntermBranch *node) override + { + if (visit == PreVisit) + { + switch (node->getFlowOp()) + { + case EOpBreak: + { + ASSERT(!mLoopsAndSwitches.empty()); + TIntermLoop *loop = mLoopsAndSwitches.back()->getAsLoopNode(); + if (loop != nullptr) + { + mMetadata->mDiscontinuousLoops.insert(loop); + } + } + break; + case EOpContinue: + { + ASSERT(!mLoopsAndSwitches.empty()); + TIntermLoop *loop = nullptr; + size_t i = mLoopsAndSwitches.size(); + while (loop == nullptr && i > 0) + { + --i; + loop = mLoopsAndSwitches.at(i)->getAsLoopNode(); + } + ASSERT(loop != nullptr); + mMetadata->mDiscontinuousLoops.insert(loop); + } + break; + case EOpKill: + case EOpReturn: + // A return or discard jumps out of all the enclosing loops + if (!mLoopsAndSwitches.empty()) + { + for (TIntermNode *intermNode : mLoopsAndSwitches) + { + TIntermLoop *loop = intermNode->getAsLoopNode(); + if (loop) + { + mMetadata->mDiscontinuousLoops.insert(loop); + } + } + } + break; + default: + UNREACHABLE(); + } + } + + return true; + } + + bool visitAggregate(Visit visit, TIntermAggregate *node) override + { + if (visit == PreVisit && node->getOp() == EOpFunctionCall) + { + if (node->isUserDefined()) + { + size_t calleeIndex = mDag.findIndex(node); + ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex); + UNUSED_ASSERTION_VARIABLE(mIndex); + + if ((*mMetadataList)[calleeIndex].mHasGradientLoopInCallGraph) + { + onGradientLoop(); + } + } + } + + return true; + } + + bool visitSwitch(Visit visit, TIntermSwitch *node) override + { + if (visit == PreVisit) + { + mLoopsAndSwitches.push_back(node); + } + else if (visit == PostVisit) + { + ASSERT(mLoopsAndSwitches.back() == node); + mLoopsAndSwitches.pop_back(); + } + return true; + } + + private: + MetadataList *mMetadataList; + ASTMetadataHLSL *mMetadata; + size_t mIndex; + const CallDAG &mDag; + + std::vector<TIntermNode*> mLoopsAndSwitches; + std::vector<TIntermSelection*> mIfs; +}; + +// Tags all the functions called in a discontinuous loop +class PushDiscontinuousLoops : public TIntermTraverser +{ + public: + PushDiscontinuousLoops(MetadataList *metadataList, size_t index, const CallDAG &dag) + : TIntermTraverser(true, true, true), + mMetadataList(metadataList), + mMetadata(&(*metadataList)[index]), + mIndex(index), + mDag(dag), + mNestedDiscont(mMetadata->mCalledInDiscontinuousLoop ? 1 : 0) + { + } + + void traverse(TIntermAggregate *node) + { + node->traverse(this); + ASSERT(mNestedDiscont == (mMetadata->mCalledInDiscontinuousLoop ? 1 : 0)); + } + + bool visitLoop(Visit visit, TIntermLoop *loop) override + { + bool isDiscontinuous = mMetadata->mDiscontinuousLoops.count(loop) > 0; + + if (visit == PreVisit && isDiscontinuous) + { + mNestedDiscont++; + } + else if (visit == PostVisit && isDiscontinuous) + { + mNestedDiscont--; + } + + return true; + } + + bool visitAggregate(Visit visit, TIntermAggregate *node) override + { + switch (node->getOp()) + { + case EOpFunctionCall: + if (visit == PreVisit && node->isUserDefined() && mNestedDiscont > 0) + { + size_t calleeIndex = mDag.findIndex(node); + ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex); + UNUSED_ASSERTION_VARIABLE(mIndex); + + (*mMetadataList)[calleeIndex].mCalledInDiscontinuousLoop = true; + } + break; + default: + break; + } + return true; + } + + private: + MetadataList *mMetadataList; + ASTMetadataHLSL *mMetadata; + size_t mIndex; + const CallDAG &mDag; + + int mNestedDiscont; +}; + +} + +bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermLoop *node) +{ + return mControlFlowsContainingGradient.count(node) > 0; +} + +bool ASTMetadataHLSL::hasGradientLoop(TIntermSelection *node) +{ + return mIfsContainingGradientLoop.count(node) > 0; +} + +MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag) +{ + MetadataList metadataList(callDag.size()); + + // Compute all the information related to when gradient operations are used. + // We want to know for each function and control flow operation if they have + // a gradient operation in their call graph (shortened to "using a gradient" + // in the rest of the file). + // + // This computation is logically split in three steps: + // 1 - For each function compute if it uses a gradient in its body, ignoring + // calls to other user-defined functions. + // 2 - For each function determine if it uses a gradient in its call graph, + // using the result of step 1 and the CallDAG to know its callees. + // 3 - For each control flow statement of each function, check if it uses a + // gradient in the function's body, or if it calls a user-defined function that + // uses a gradient. + // + // We take advantage of the call graph being a DAG and instead compute 1, 2 and 3 + // for leaves first, then going down the tree. This is correct because 1 doesn't + // depend on other functions, and 2 and 3 depend only on callees. + for (size_t i = 0; i < callDag.size(); i++) + { + PullGradient pull(&metadataList, i, callDag); + pull.traverse(callDag.getRecordFromIndex(i).node); + } + + // Compute which loops are discontinuous and which function are called in + // these loops. The same way computing gradient usage is a "pull" process, + // computing "bing used in a discont. loop" is a push process. However we also + // need to know what ifs have a discontinuous loop inside so we do the same type + // of callgraph analysis as for the gradient. + + // First compute which loops are discontinuous (no specific order) and pull + // the ifs and functions using a gradient loop. + for (size_t i = 0; i < callDag.size(); i++) + { + PullComputeDiscontinuousAndGradientLoops pull(&metadataList, i, callDag); + pull.traverse(callDag.getRecordFromIndex(i).node); + } + + // Then push the information to callees, either from the a local discontinuous + // loop or from the caller being called in a discontinuous loop already + for (size_t i = callDag.size(); i-- > 0;) + { + PushDiscontinuousLoops push(&metadataList, i, callDag); + push.traverse(callDag.getRecordFromIndex(i).node); + } + + // We create "Lod0" version of functions with the gradient operations replaced + // by non-gradient operations so that the D3D compiler is happier with discont + // loops. + for (auto &metadata : metadataList) + { + metadata.mNeedsLod0 = metadata.mCalledInDiscontinuousLoop && metadata.mUsesGradient; + } + + return metadataList; +} diff --git a/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.h b/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.h new file mode 100644 index 0000000000..39e671e3e0 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.h @@ -0,0 +1,58 @@ +// +// Copyright (c) 2002-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. +// + +// Defines analyses of the AST needed for HLSL generation + +#ifndef COMPILER_TRANSLATOR_ASTMETADATAHLSL_H_ +#define COMPILER_TRANSLATOR_ASTMETADATAHLSL_H_ + +#include <set> +#include <vector> + +class CallDAG; +class TIntermNode; +class TIntermSelection; +class TIntermLoop; + +struct ASTMetadataHLSL +{ + ASTMetadataHLSL() + : mUsesGradient(false), + mCalledInDiscontinuousLoop(false), + mHasGradientLoopInCallGraph(false), + mNeedsLod0(false) + { + } + + // Here "something uses a gradient" means here that it either contains a + // gradient operation, or a call to a function that uses a gradient. + bool hasGradientInCallGraph(TIntermLoop *node); + bool hasGradientLoop(TIntermSelection *node); + + // Does the function use a gradient. + bool mUsesGradient; + + // Even if usesGradient is true, some control flow might not use a gradient + // so we store the set of all gradient-using control flows. + std::set<TIntermNode*> mControlFlowsContainingGradient; + + // Remember information about the discontinuous loops and which functions + // are called in such loops. + bool mCalledInDiscontinuousLoop; + bool mHasGradientLoopInCallGraph; + std::set<TIntermLoop*> mDiscontinuousLoops; + std::set<TIntermSelection *> mIfsContainingGradientLoop; + + // Will we need to generate a Lod0 version of the function. + bool mNeedsLod0; +}; + +typedef std::vector<ASTMetadataHLSL> MetadataList; + +// Return the AST analysis result, in the order defined by the call DAG +MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag); + +#endif // COMPILER_TRANSLATOR_ASTMETADATAHLSL_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp b/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp new file mode 100644 index 0000000000..510ade84c1 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp @@ -0,0 +1,206 @@ +// +// Copyright (c) 2002-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. +// +// The ArrayReturnValueToOutParameter function changes return values of an array type to out parameters in +// function definitions, prototypes, and call sites. + +#include "compiler/translator/ArrayReturnValueToOutParameter.h" + +#include "compiler/translator/IntermNode.h" + +namespace +{ + +void CopyAggregateChildren(TIntermAggregate *from, TIntermAggregate *to) +{ + const TIntermSequence *fromSequence = from->getSequence(); + for (size_t ii = 0; ii < fromSequence->size(); ++ii) + { + to->getSequence()->push_back(fromSequence->at(ii)); + } +} + +TIntermSymbol *CreateReturnValueSymbol(const TType &type) +{ + TIntermSymbol *node = new TIntermSymbol(0, "angle_return", type); + node->setInternal(true); + return node; +} + +TIntermSymbol *CreateReturnValueOutSymbol(const TType &type) +{ + TType outType(type); + outType.setQualifier(EvqOut); + return CreateReturnValueSymbol(outType); +} + +TIntermAggregate *CreateReplacementCall(TIntermAggregate *originalCall, TIntermTyped *returnValueTarget) +{ + TIntermAggregate *replacementCall = new TIntermAggregate(EOpFunctionCall); + replacementCall->setType(TType(EbtVoid)); + replacementCall->setUserDefined(); + replacementCall->setNameObj(originalCall->getNameObj()); + replacementCall->setFunctionId(originalCall->getFunctionId()); + replacementCall->setLine(originalCall->getLine()); + TIntermSequence *replacementParameters = replacementCall->getSequence(); + TIntermSequence *originalParameters = originalCall->getSequence(); + for (auto ¶m : *originalParameters) + { + replacementParameters->push_back(param); + } + replacementParameters->push_back(returnValueTarget); + return replacementCall; +} + +class ArrayReturnValueToOutParameterTraverser : private TIntermTraverser +{ + public: + static void apply(TIntermNode *root, unsigned int *temporaryIndex); + private: + ArrayReturnValueToOutParameterTraverser(); + + bool visitAggregate(Visit visit, TIntermAggregate *node) override; + bool visitBranch(Visit visit, TIntermBranch *node) override; + bool visitBinary(Visit visit, TIntermBinary *node) override; + + bool mInFunctionWithArrayReturnValue; +}; + +void ArrayReturnValueToOutParameterTraverser::apply(TIntermNode *root, unsigned int *temporaryIndex) +{ + ArrayReturnValueToOutParameterTraverser arrayReturnValueToOutParam; + arrayReturnValueToOutParam.useTemporaryIndex(temporaryIndex); + root->traverse(&arrayReturnValueToOutParam); + arrayReturnValueToOutParam.updateTree(); +} + +ArrayReturnValueToOutParameterTraverser::ArrayReturnValueToOutParameterTraverser() + : TIntermTraverser(true, false, true), + mInFunctionWithArrayReturnValue(false) +{ +} + +bool ArrayReturnValueToOutParameterTraverser::visitAggregate(Visit visit, TIntermAggregate *node) +{ + if (visit == PreVisit) + { + if (node->isArray()) + { + if (node->getOp() == EOpFunction) + { + // Replace the parameters child node of the function definition with another node + // that has the out parameter added. + // Also set the function to return void. + + TIntermAggregate *params = node->getSequence()->front()->getAsAggregate(); + ASSERT(params != nullptr && params->getOp() == EOpParameters); + + TIntermAggregate *replacementParams = new TIntermAggregate; + replacementParams->setOp(EOpParameters); + CopyAggregateChildren(params, replacementParams); + replacementParams->getSequence()->push_back(CreateReturnValueOutSymbol(node->getType())); + replacementParams->setLine(params->getLine()); + + mReplacements.push_back(NodeUpdateEntry(node, params, replacementParams, false)); + + node->setType(TType(EbtVoid)); + + mInFunctionWithArrayReturnValue = true; + } + else if (node->getOp() == EOpPrototype) + { + // Replace the whole prototype node with another node that has the out parameter added. + TIntermAggregate *replacement = new TIntermAggregate; + replacement->setOp(EOpPrototype); + CopyAggregateChildren(node, replacement); + replacement->getSequence()->push_back(CreateReturnValueOutSymbol(node->getType())); + replacement->setUserDefined(); + replacement->setNameObj(node->getNameObj()); + replacement->setFunctionId(node->getFunctionId()); + replacement->setLine(node->getLine()); + replacement->setType(TType(EbtVoid)); + + mReplacements.push_back(NodeUpdateEntry(getParentNode(), node, replacement, false)); + } + else if (node->getOp() == EOpFunctionCall) + { + // Handle call sites where the returned array is not assigned. + // Examples where f() is a function returning an array: + // 1. f(); + // 2. another_array == f(); + // 3. another_function(f()); + // 4. return f(); + // Cases 2 to 4 are already converted to simpler cases by SeparateExpressionsReturningArrays, so we + // only need to worry about the case where a function call returning an array forms an expression by + // itself. + TIntermAggregate *parentAgg = getParentNode()->getAsAggregate(); + if (parentAgg != nullptr && parentAgg->getOp() == EOpSequence) + { + nextTemporaryIndex(); + TIntermSequence replacements; + replacements.push_back(createTempDeclaration(node->getType())); + TIntermSymbol *returnSymbol = createTempSymbol(node->getType()); + replacements.push_back(CreateReplacementCall(node, returnSymbol)); + mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(parentAgg, node, replacements)); + } + return false; + } + } + } + else if (visit == PostVisit) + { + if (node->getOp() == EOpFunction) + { + mInFunctionWithArrayReturnValue = false; + } + } + return true; +} + +bool ArrayReturnValueToOutParameterTraverser::visitBranch(Visit visit, TIntermBranch *node) +{ + if (mInFunctionWithArrayReturnValue && node->getFlowOp() == EOpReturn) + { + // Instead of returning a value, assign to the out parameter and then return. + TIntermSequence replacements; + + TIntermBinary *replacementAssignment = new TIntermBinary(EOpAssign); + TIntermTyped *expression = node->getExpression(); + ASSERT(expression != nullptr); + replacementAssignment->setLeft(CreateReturnValueSymbol(expression->getType())); + replacementAssignment->setRight(node->getExpression()); + replacementAssignment->setType(expression->getType()); + replacementAssignment->setLine(expression->getLine()); + replacements.push_back(replacementAssignment); + + TIntermBranch *replacementBranch = new TIntermBranch(EOpReturn, nullptr); + replacementBranch->setLine(node->getLine()); + replacements.push_back(replacementBranch); + + mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(getParentNode()->getAsAggregate(), node, replacements)); + } + return false; +} + +bool ArrayReturnValueToOutParameterTraverser::visitBinary(Visit visit, TIntermBinary *node) +{ + if (node->getOp() == EOpAssign && node->getLeft()->isArray()) + { + TIntermAggregate *rightAgg = node->getRight()->getAsAggregate(); + if (rightAgg != nullptr && rightAgg->getOp() == EOpFunctionCall && rightAgg->isUserDefined()) + { + TIntermAggregate *replacementCall = CreateReplacementCall(rightAgg, node->getLeft()); + mReplacements.push_back(NodeUpdateEntry(getParentNode(), node, replacementCall, false)); + } + } + return false; +} + +} // namespace + +void ArrayReturnValueToOutParameter(TIntermNode *root, unsigned int *temporaryIndex) +{ + ArrayReturnValueToOutParameterTraverser::apply(root, temporaryIndex); +} diff --git a/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h b/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h new file mode 100644 index 0000000000..983e203e62 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h @@ -0,0 +1,16 @@ +// +// Copyright (c) 2002-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. +// +// The ArrayReturnValueToOutParameter function changes return values of an array type to out parameters in +// function definitions, prototypes and call sites. + +#ifndef COMPILER_TRANSLATOR_ARRAYRETURNVALUETOOUTPARAMETER_H_ +#define COMPILER_TRANSLATOR_ARRAYRETURNVALUETOOUTPARAMETER_H_ + +class TIntermNode; + +void ArrayReturnValueToOutParameter(TIntermNode *root, unsigned int *temporaryIndex); + +#endif // COMPILER_TRANSLATOR_ARRAYRETURNVALUETOOUTPARAMETER_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/BaseTypes.h b/src/3rdparty/angle/src/compiler/translator/BaseTypes.h index ee1428b2d3..0ed6d0e62f 100644 --- a/src/3rdparty/angle/src/compiler/translator/BaseTypes.h +++ b/src/3rdparty/angle/src/compiler/translator/BaseTypes.h @@ -7,7 +7,7 @@ #ifndef COMPILER_TRANSLATOR_BASETYPES_H_ #define COMPILER_TRANSLATOR_BASETYPES_H_ -#include "compiler/translator/compilerdebug.h" +#include "common/debug.h" // // Precision qualifiers @@ -18,7 +18,10 @@ enum TPrecision EbpUndefined, EbpLow, EbpMedium, - EbpHigh + EbpHigh, + + // end of list + EbpLast }; inline const char* getPrecisionString(TPrecision p) @@ -77,6 +80,9 @@ enum TBasicType EbtStruct, EbtInterfaceBlock, EbtAddress, // should be deprecated?? + + // end of list + EbtLast }; const char* getBasicString(TBasicType t); @@ -284,21 +290,18 @@ inline bool SupportsPrecision(TBasicType type) // enum TQualifier { - EvqTemporary, // For temporaries (within a function), read/write - EvqGlobal, // For globals read/write - EvqInternal, // For internal use, not visible to the user - EvqConst, // User defined constants and non-output parameters in functions - EvqAttribute, // Readonly - EvqVaryingIn, // readonly, fragment shaders only - EvqVaryingOut, // vertex shaders only read/write - EvqInvariantVaryingIn, // readonly, fragment shaders only - EvqInvariantVaryingOut, // vertex shaders only read/write - EvqUniform, // Readonly, vertex and fragment - - EvqVertexIn, // Vertex shader input - EvqFragmentOut, // Fragment shader output - EvqVertexOut, // Vertex shader output - EvqFragmentIn, // Fragment shader input + EvqTemporary, // For temporaries (within a function), read/write + EvqGlobal, // For globals read/write + EvqConst, // User defined constants and non-output parameters in functions + EvqAttribute, // Readonly + EvqVaryingIn, // readonly, fragment shaders only + EvqVaryingOut, // vertex shaders only read/write + EvqUniform, // Readonly, vertex and fragment + + EvqVertexIn, // Vertex shader input + EvqFragmentOut, // Fragment shader output + EvqVertexOut, // Vertex shader output + EvqFragmentIn, // Fragment shader input // parameters EvqIn, @@ -321,21 +324,26 @@ enum TQualifier // built-ins written by fragment shader EvqFragColor, EvqFragData, - EvqFragDepth, + + EvqFragDepth, // gl_FragDepth for ESSL300. + EvqFragDepthEXT, // gl_FragDepthEXT for ESSL100, EXT_frag_depth. + + EvqSecondaryFragColorEXT, // EXT_blend_func_extended + EvqSecondaryFragDataEXT, // EXT_blend_func_extended // built-ins written by the shader_framebuffer_fetch extension(s) EvqLastFragColor, EvqLastFragData, // GLSL ES 3.0 vertex output and fragment input - EvqSmooth, // Incomplete qualifier, smooth is the default - EvqFlat, // Incomplete qualifier + EvqSmooth, // Incomplete qualifier, smooth is the default + EvqFlat, // Incomplete qualifier EvqSmoothOut = EvqSmooth, - EvqFlatOut = EvqFlat, - EvqCentroidOut, // Implies smooth + EvqFlatOut = EvqFlat, + EvqCentroidOut, // Implies smooth EvqSmoothIn, EvqFlatIn, - EvqCentroidIn, // Implies smooth + EvqCentroidIn, // Implies smooth // end of list EvqLast @@ -384,43 +392,47 @@ struct TLayoutQualifier // inline const char* getQualifierString(TQualifier q) { + // clang-format off switch(q) { - case EvqTemporary: return "Temporary"; break; - case EvqGlobal: return "Global"; break; - case EvqConst: return "const"; break; - case EvqConstReadOnly: return "const"; break; - case EvqAttribute: return "attribute"; break; - case EvqVaryingIn: return "varying"; break; - case EvqVaryingOut: return "varying"; break; - case EvqInvariantVaryingIn: return "invariant varying"; break; - case EvqInvariantVaryingOut:return "invariant varying"; break; - case EvqUniform: return "uniform"; break; - case EvqVertexIn: return "in"; break; - case EvqFragmentOut: return "out"; break; - case EvqVertexOut: return "out"; break; - case EvqFragmentIn: return "in"; break; - case EvqIn: return "in"; break; - case EvqOut: return "out"; break; - case EvqInOut: return "inout"; break; - case EvqInstanceID: return "InstanceID"; break; - case EvqPosition: return "Position"; break; - case EvqPointSize: return "PointSize"; break; - case EvqFragCoord: return "FragCoord"; break; - case EvqFrontFacing: return "FrontFacing"; break; - case EvqFragColor: return "FragColor"; break; - case EvqFragData: return "FragData"; break; - case EvqFragDepth: return "FragDepth"; break; - case EvqSmoothOut: return "smooth out"; break; - case EvqCentroidOut: return "centroid out"; break; - case EvqFlatOut: return "flat out"; break; - case EvqSmoothIn: return "smooth in"; break; - case EvqCentroidIn: return "centroid in"; break; - case EvqFlatIn: return "flat in"; break; - case EvqLastFragColor: return "LastFragColor"; break; - case EvqLastFragData: return "LastFragData"; break; - default: UNREACHABLE(); return "unknown qualifier"; + case EvqTemporary: return "Temporary"; + case EvqGlobal: return "Global"; + case EvqConst: return "const"; + case EvqAttribute: return "attribute"; + case EvqVaryingIn: return "varying"; + case EvqVaryingOut: return "varying"; + case EvqUniform: return "uniform"; + case EvqVertexIn: return "in"; + case EvqFragmentOut: return "out"; + case EvqVertexOut: return "out"; + case EvqFragmentIn: return "in"; + case EvqIn: return "in"; + case EvqOut: return "out"; + case EvqInOut: return "inout"; + case EvqConstReadOnly: return "const"; + case EvqInstanceID: return "InstanceID"; + case EvqPosition: return "Position"; + case EvqPointSize: return "PointSize"; + case EvqFragCoord: return "FragCoord"; + case EvqFrontFacing: return "FrontFacing"; + case EvqPointCoord: return "PointCoord"; + case EvqFragColor: return "FragColor"; + case EvqFragData: return "FragData"; + case EvqFragDepthEXT: return "FragDepth"; + case EvqFragDepth: return "FragDepth"; + case EvqSecondaryFragColorEXT: return "SecondaryFragColorEXT"; + case EvqSecondaryFragDataEXT: return "SecondaryFragDataEXT"; + case EvqLastFragColor: return "LastFragColor"; + case EvqLastFragData: return "LastFragData"; + case EvqSmoothOut: return "smooth out"; + case EvqCentroidOut: return "centroid out"; + case EvqFlatOut: return "flat out"; + case EvqSmoothIn: return "smooth in"; + case EvqFlatIn: return "flat in"; + case EvqCentroidIn: return "centroid in"; + default: UNREACHABLE(); return "unknown qualifier"; } + // clang-format on } inline const char* getMatrixPackingString(TLayoutMatrixPacking mpq) diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp index 51461207c5..0c7f149ee6 100644 --- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp +++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp @@ -11,28 +11,31 @@ class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTraverser { public: - BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator& emulator) - : mEmulator(emulator) + BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator &emulator) + : TIntermTraverser(true, false, false), + mEmulator(emulator) { } - virtual bool visitUnary(Visit visit, TIntermUnary* node) + bool visitUnary(Visit visit, TIntermUnary *node) override { - if (visit == PreVisit) { - bool needToEmulate = mEmulator.SetFunctionCalled( - node->getOp(), node->getOperand()->getType()); + if (visit == PreVisit) + { + bool needToEmulate = mEmulator.SetFunctionCalled(node->getOp(), node->getOperand()->getType()); if (needToEmulate) node->setUseEmulatedFunction(); } return true; } - virtual bool visitAggregate(Visit visit, TIntermAggregate* node) + bool visitAggregate(Visit visit, TIntermAggregate *node) override { - if (visit == PreVisit) { + if (visit == PreVisit) + { // Here we handle all the built-in functions instead of the ones we // currently identified as problematic. - switch (node->getOp()) { + switch (node->getOp()) + { case EOpLessThan: case EOpGreaterThan: case EOpLessThanEqual: @@ -59,14 +62,14 @@ class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTr break; default: return true; - }; - const TIntermSequence& sequence = *(node->getSequence()); + } + const TIntermSequence &sequence = *(node->getSequence()); bool needToEmulate = false; // Right now we only handle built-in functions with two or three parameters. if (sequence.size() == 2) { - TIntermTyped* param1 = sequence[0]->getAsTyped(); - TIntermTyped* param2 = sequence[1]->getAsTyped(); + TIntermTyped *param1 = sequence[0]->getAsTyped(); + TIntermTyped *param2 = sequence[1]->getAsTyped(); if (!param1 || !param2) return true; needToEmulate = mEmulator.SetFunctionCalled( @@ -74,9 +77,9 @@ class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTr } else if (sequence.size() == 3) { - TIntermTyped* param1 = sequence[0]->getAsTyped(); - TIntermTyped* param2 = sequence[1]->getAsTyped(); - TIntermTyped* param3 = sequence[2]->getAsTyped(); + TIntermTyped *param1 = sequence[0]->getAsTyped(); + TIntermTyped *param2 = sequence[1]->getAsTyped(); + TIntermTyped *param3 = sequence[2]->getAsTyped(); if (!param1 || !param2 || !param3) return true; needToEmulate = mEmulator.SetFunctionCalled( @@ -94,34 +97,28 @@ class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTr } private: - BuiltInFunctionEmulator& mEmulator; + BuiltInFunctionEmulator &mEmulator; }; BuiltInFunctionEmulator::BuiltInFunctionEmulator() {} -void BuiltInFunctionEmulator::addEmulatedFunction( - TOperator op, const TType& param, - const char* emulatedFunctionDefinition) +void BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, const TType *param, + const char *emulatedFunctionDefinition) { - mEmulatedFunctions[FunctionId(op, param)] = - std::string(emulatedFunctionDefinition); + mEmulatedFunctions[FunctionId(op, param)] = std::string(emulatedFunctionDefinition); } -void BuiltInFunctionEmulator::addEmulatedFunction( - TOperator op, const TType& param1, const TType& param2, - const char* emulatedFunctionDefinition) +void BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, const TType *param1, const TType *param2, + const char *emulatedFunctionDefinition) { - mEmulatedFunctions[FunctionId(op, param1, param2)] = - std::string(emulatedFunctionDefinition); + mEmulatedFunctions[FunctionId(op, param1, param2)] = std::string(emulatedFunctionDefinition); } -void BuiltInFunctionEmulator::addEmulatedFunction( - TOperator op, const TType& param1, const TType& param2, const TType& param3, - const char* emulatedFunctionDefinition) +void BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, const TType *param1, const TType *param2, + const TType *param3, const char *emulatedFunctionDefinition) { - mEmulatedFunctions[FunctionId(op, param1, param2, param3)] = - std::string(emulatedFunctionDefinition); + mEmulatedFunctions[FunctionId(op, param1, param2, param3)] = std::string(emulatedFunctionDefinition); } bool BuiltInFunctionEmulator::IsOutputEmpty() const @@ -129,48 +126,48 @@ bool BuiltInFunctionEmulator::IsOutputEmpty() const return (mFunctions.size() == 0); } -void BuiltInFunctionEmulator::OutputEmulatedFunctions( - TInfoSinkBase& out) const +void BuiltInFunctionEmulator::OutputEmulatedFunctions(TInfoSinkBase &out) const { - for (size_t i = 0; i < mFunctions.size(); ++i) { + for (size_t i = 0; i < mFunctions.size(); ++i) + { out << mEmulatedFunctions.find(mFunctions[i])->second << "\n\n"; } } -bool BuiltInFunctionEmulator::SetFunctionCalled( - TOperator op, const TType& param) +bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, const TType ¶m) { - return SetFunctionCalled(FunctionId(op, param)); + return SetFunctionCalled(FunctionId(op, ¶m)); } -bool BuiltInFunctionEmulator::SetFunctionCalled( - TOperator op, const TType& param1, const TType& param2) +bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, const TType ¶m1, const TType ¶m2) { - return SetFunctionCalled(FunctionId(op, param1, param2)); + return SetFunctionCalled(FunctionId(op, ¶m1, ¶m2)); } -bool BuiltInFunctionEmulator::SetFunctionCalled( - TOperator op, const TType& param1, const TType& param2, const TType& param3) +bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, + const TType ¶m1, const TType ¶m2, const TType ¶m3) { - return SetFunctionCalled(FunctionId(op, param1, param2, param3)); + return SetFunctionCalled(FunctionId(op, ¶m1, ¶m2, ¶m3)); } -bool BuiltInFunctionEmulator::SetFunctionCalled( - const FunctionId& functionId) { +bool BuiltInFunctionEmulator::SetFunctionCalled(const FunctionId &functionId) +{ if (mEmulatedFunctions.find(functionId) != mEmulatedFunctions.end()) { - for (size_t i = 0; i < mFunctions.size(); ++i) { + for (size_t i = 0; i < mFunctions.size(); ++i) + { if (mFunctions[i] == functionId) return true; } - mFunctions.push_back(functionId); + // Copy the functionId if it needs to be stored, to make sure that the TType pointers inside + // remain valid and constant. + mFunctions.push_back(functionId.getCopy()); return true; } return false; } -void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation( - TIntermNode* root) +void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation(TIntermNode *root) { ASSERT(root); @@ -188,32 +185,30 @@ void BuiltInFunctionEmulator::Cleanup() //static TString BuiltInFunctionEmulator::GetEmulatedFunctionName( - const TString& name) + const TString &name) { ASSERT(name[name.length() - 1] == '('); return "webgl_" + name.substr(0, name.length() - 1) + "_emu("; } -BuiltInFunctionEmulator::FunctionId::FunctionId - (TOperator op, const TType& param) +BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op, const TType *param) : mOp(op), mParam1(param), - mParam2(EbtVoid), - mParam3(EbtVoid) + mParam2(new TType(EbtVoid)), + mParam3(new TType(EbtVoid)) { } -BuiltInFunctionEmulator::FunctionId::FunctionId - (TOperator op, const TType& param1, const TType& param2) +BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op, const TType *param1, const TType *param2) : mOp(op), mParam1(param1), mParam2(param2), - mParam3(EbtVoid) + mParam3(new TType(EbtVoid)) { } -BuiltInFunctionEmulator::FunctionId::FunctionId - (TOperator op, const TType& param1, const TType& param2, const TType& param3) +BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op, + const TType *param1, const TType *param2, const TType *param3) : mOp(op), mParam1(param1), mParam2(param2), @@ -221,25 +216,28 @@ BuiltInFunctionEmulator::FunctionId::FunctionId { } -bool BuiltInFunctionEmulator::FunctionId::operator== - (const BuiltInFunctionEmulator::FunctionId& other) const +bool BuiltInFunctionEmulator::FunctionId::operator==(const BuiltInFunctionEmulator::FunctionId &other) const { return (mOp == other.mOp && - mParam1 == other.mParam1 && - mParam2 == other.mParam2 && - mParam3 == other.mParam3); + *mParam1 == *other.mParam1 && + *mParam2 == *other.mParam2 && + *mParam3 == *other.mParam3); } -bool BuiltInFunctionEmulator::FunctionId::operator< - (const BuiltInFunctionEmulator::FunctionId& other) const +bool BuiltInFunctionEmulator::FunctionId::operator<(const BuiltInFunctionEmulator::FunctionId &other) const { if (mOp != other.mOp) return mOp < other.mOp; - if (mParam1 != other.mParam1) - return mParam1 < other.mParam1; - if (mParam2 != other.mParam2) - return mParam2 < other.mParam2; - if (mParam3 != other.mParam3) - return mParam3 < other.mParam3; + if (*mParam1 != *other.mParam1) + return *mParam1 < *other.mParam1; + if (*mParam2 != *other.mParam2) + return *mParam2 < *other.mParam2; + if (*mParam3 != *other.mParam3) + return *mParam3 < *other.mParam3; return false; // all fields are equal } + +BuiltInFunctionEmulator::FunctionId BuiltInFunctionEmulator::FunctionId::getCopy() const +{ + return FunctionId(mOp, new TType(*mParam1), new TType(*mParam2), new TType(*mParam3)); +} diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.h b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.h index df556985e1..6976edfd57 100644 --- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.h +++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.h @@ -21,23 +21,25 @@ class BuiltInFunctionEmulator public: BuiltInFunctionEmulator(); - void MarkBuiltInFunctionsForEmulation(TIntermNode* root); + void MarkBuiltInFunctionsForEmulation(TIntermNode *root); void Cleanup(); // "name(" becomes "webgl_name_emu(". - static TString GetEmulatedFunctionName(const TString& name); + static TString GetEmulatedFunctionName(const TString &name); bool IsOutputEmpty() const; // Output function emulation definition. This should be before any other // shader source. - void OutputEmulatedFunctions(TInfoSinkBase& out) const; + void OutputEmulatedFunctions(TInfoSinkBase &out) const; // Add functions that need to be emulated. - void addEmulatedFunction(TOperator op, const TType& param, const char* emulatedFunctionDefinition); - void addEmulatedFunction(TOperator op, const TType& param1, const TType& param2, const char* emulatedFunctionDefinition); - void addEmulatedFunction(TOperator op, const TType& param1, const TType& param2, const TType& param3, const char* emulatedFunctionDefinition); + void addEmulatedFunction(TOperator op, const TType *param, const char *emulatedFunctionDefinition); + void addEmulatedFunction(TOperator op, const TType *param1, const TType *param2, + const char *emulatedFunctionDefinition); + void addEmulatedFunction(TOperator op, const TType *param1, const TType *param2, const TType *param3, + const char *emulatedFunctionDefinition); private: class BuiltInFunctionEmulationMarker; @@ -46,28 +48,32 @@ class BuiltInFunctionEmulator // emulated. If the function is not in mEmulatedFunctions, this becomes a // no-op. Returns true if the function call needs to be replaced with an // emulated one. - bool SetFunctionCalled(TOperator op, const TType& param); - bool SetFunctionCalled( - TOperator op, const TType& param1, const TType& param2); - bool SetFunctionCalled( - TOperator op, const TType& param1, const TType& param2, const TType& param3); + bool SetFunctionCalled(TOperator op, const TType ¶m); + bool SetFunctionCalled(TOperator op, const TType ¶m1, const TType ¶m2); + bool SetFunctionCalled(TOperator op, const TType ¶m1, const TType ¶m2, const TType ¶m3); class FunctionId { public: - FunctionId(TOperator op, const TType& param); - FunctionId(TOperator op, const TType& param1, const TType& param2); - FunctionId(TOperator op, const TType& param1, const TType& param2, const TType& param3); + FunctionId(TOperator op, const TType *param); + FunctionId(TOperator op, const TType *param1, const TType *param2); + FunctionId(TOperator op, const TType *param1, const TType *param2, const TType *param3); - bool operator==(const FunctionId& other) const; - bool operator<(const FunctionId& other) const; + bool operator==(const FunctionId &other) const; + bool operator<(const FunctionId &other) const; + + FunctionId getCopy() const; private: TOperator mOp; - TType mParam1; - TType mParam2; - TType mParam3; + + // The memory that these TType objects use is freed by PoolAllocator. The BuiltInFunctionEmulator's lifetime + // can extend until after the memory pool is freed, but that's not an issue since this class never destructs + // these objects. + const TType *mParam1; + const TType *mParam2; + const TType *mParam3; }; - bool SetFunctionCalled(const FunctionId& functionId); + bool SetFunctionCalled(const FunctionId &functionId); // Map from function id to emulated function definition std::map<FunctionId, std::string> mEmulatedFunctions; diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp index 9de99831ad..098560d110 100644 --- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp @@ -7,9 +7,11 @@ #include "angle_gl.h" #include "compiler/translator/BuiltInFunctionEmulator.h" #include "compiler/translator/BuiltInFunctionEmulatorGLSL.h" +#include "compiler/translator/Cache.h" #include "compiler/translator/SymbolTable.h" +#include "compiler/translator/VersionGLSL.h" -void InitBuiltInFunctionEmulatorForGLSL(BuiltInFunctionEmulator *emu, sh::GLenum shaderType) +void InitBuiltInFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, sh::GLenum shaderType) { // we use macros here instead of function definitions to work around more GLSL // compiler bugs, in particular on NVIDIA hardware on Mac OSX. Macros are @@ -17,10 +19,10 @@ void InitBuiltInFunctionEmulatorForGLSL(BuiltInFunctionEmulator *emu, sh::GLenum // evaluated. This is unlikely to show up in real shaders, but is something to // consider. - TType float1(EbtFloat); - TType float2(EbtFloat, 2); - TType float3(EbtFloat, 3); - TType float4(EbtFloat, 4); + const TType *float1 = TCache::getType(EbtFloat); + const TType *float2 = TCache::getType(EbtFloat, 2); + const TType *float3 = TCache::getType(EbtFloat, 3); + const TType *float4 = TCache::getType(EbtFloat, 4); if (shaderType == GL_FRAGMENT_SHADER) { @@ -35,3 +37,153 @@ void InitBuiltInFunctionEmulatorForGLSL(BuiltInFunctionEmulator *emu, sh::GLenum emu->addEmulatedFunction(EOpNormalize, float1, "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))"); emu->addEmulatedFunction(EOpReflect, float1, float1, "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))"); } + +// Emulate built-in functions missing from GLSL 1.30 and higher +void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, sh::GLenum shaderType, + int targetGLSLVersion) +{ + // Emulate packSnorm2x16, packHalf2x16, unpackSnorm2x16, and unpackHalf2x16 (GLSL 4.20) + // by using floatBitsToInt, floatBitsToUint, intBitsToFloat, and uintBitsToFloat (GLSL 3.30). + if (targetGLSLVersion >= GLSL_VERSION_330 && targetGLSLVersion < GLSL_VERSION_420) + { + const TType *float2 = TCache::getType(EbtFloat, 2); + const TType *uint1 = TCache::getType(EbtUInt); + + // clang-format off + emu->addEmulatedFunction(EOpPackSnorm2x16, float2, + "uint webgl_packSnorm2x16_emu(vec2 v)\n" + "{\n" + " #if defined(GL_ARB_shading_language_packing)\n" + " return packSnorm2x16(v);\n" + " #else\n" + " int x = int(round(clamp(v.x, -1.0, 1.0) * 32767.0));\n" + " int y = int(round(clamp(v.y, -1.0, 1.0) * 32767.0));\n" + " return uint((y << 16) | (x & 0xFFFF));\n" + " #endif\n" + "}\n"); + emu->addEmulatedFunction(EOpUnpackSnorm2x16, uint1, + "#if !defined(GL_ARB_shading_language_packing)\n" + " float webgl_fromSnorm(uint x)\n" + " {\n" + " int xi = (int(x) & 0x7FFF) - (int(x) & 0x8000);\n" + " return clamp(float(xi) / 32767.0, -1.0, 1.0);\n" + " }\n" + "#endif\n" + "\n" + "vec2 webgl_unpackSnorm2x16_emu(uint u)\n" + "{\n" + " #if defined(GL_ARB_shading_language_packing)\n" + " return unpackSnorm2x16(u);\n" + " #else\n" + " uint y = (u >> 16);\n" + " uint x = u;\n" + " return vec2(webgl_fromSnorm(x), webgl_fromSnorm(y));\n" + " #endif\n" + "}\n"); + // Functions uint webgl_f32tof16(float val) and float webgl_f16tof32(uint val) are + // based on the OpenGL redbook Appendix Session "Floating-Point Formats Used in OpenGL". + emu->addEmulatedFunction(EOpPackHalf2x16, float2, + "#if !defined(GL_ARB_shading_language_packing)\n" + " uint webgl_f32tof16(float val)\n" + " {\n" + " uint f32 = floatBitsToUint(val);\n" + " uint f16 = 0u;\n" + " uint sign = (f32 >> 16) & 0x8000u;\n" + " int exponent = int((f32 >> 23) & 0xFFu) - 127;\n" + " uint mantissa = f32 & 0x007FFFFFu;\n" + " if (exponent == 128)\n" + " {\n" + " // Infinity or NaN\n" + " // NaN bits that are masked out by 0x3FF get discarded.\n" + " // This can turn some NaNs to infinity, but this is allowed by the spec.\n" + " f16 = sign | (0x1Fu << 10);\n" + " f16 |= (mantissa & 0x3FFu);\n" + " }\n" + " else if (exponent > 15)\n" + " {\n" + " // Overflow - flush to Infinity\n" + " f16 = sign | (0x1Fu << 10);\n" + " }\n" + " else if (exponent > -15)\n" + " {\n" + " // Representable value\n" + " exponent += 15;\n" + " mantissa >>= 13;\n" + " f16 = sign | uint(exponent << 10) | mantissa;\n" + " }\n" + " else\n" + " {\n" + " f16 = sign;\n" + " }\n" + " return f16;\n" + " }\n" + "#endif\n" + "\n" + "uint webgl_packHalf2x16_emu(vec2 v)\n" + "{\n" + " #if defined(GL_ARB_shading_language_packing)\n" + " return packHalf2x16(v);\n" + " #else\n" + " uint x = webgl_f32tof16(v.x);\n" + " uint y = webgl_f32tof16(v.y);\n" + " return (y << 16) | x;\n" + " #endif\n" + "}\n"); + emu->addEmulatedFunction(EOpUnpackHalf2x16, uint1, + "#if !defined(GL_ARB_shading_language_packing)\n" + " float webgl_f16tof32(uint val)\n" + " {\n" + " uint sign = (val & 0x8000u) << 16;\n" + " int exponent = int((val & 0x7C00u) >> 10);\n" + " uint mantissa = val & 0x03FFu;\n" + " float f32 = 0.0;\n" + " if(exponent == 0)\n" + " {\n" + " if (mantissa != 0u)\n" + " {\n" + " const float scale = 1.0 / (1 << 24);\n" + " f32 = scale * mantissa;\n" + " }\n" + " }\n" + " else if (exponent == 31)\n" + " {\n" + " return uintBitsToFloat(sign | 0x7F800000u | mantissa);\n" + " }\n" + " else\n" + " {\n" + " exponent -= 15;\n" + " float scale;\n" + " if(exponent < 0)\n" + " {\n" + " scale = 1.0 / (1 << -exponent);\n" + " }\n" + " else\n" + " {\n" + " scale = 1 << exponent;\n" + " }\n" + " float decimal = 1.0 + float(mantissa) / float(1 << 10);\n" + " f32 = scale * decimal;\n" + " }\n" + "\n" + " if (sign != 0u)\n" + " {\n" + " f32 = -f32;\n" + " }\n" + "\n" + " return f32;\n" + " }\n" + "#endif\n" + "\n" + "vec2 webgl_unpackHalf2x16_emu(uint u)\n" + "{\n" + " #if defined(GL_ARB_shading_language_packing)\n" + " return unpackHalf2x16(u);\n" + " #else\n" + " uint y = (u >> 16);\n" + " uint x = u & 0xFFFFu;\n" + " return vec2(webgl_f16tof32(x), webgl_f16tof32(y));\n" + " #endif\n" + "}\n"); + // clang-format on + } +} diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h index 5707a4b35a..56242598af 100644 --- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h +++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h @@ -14,6 +14,12 @@ class BuiltInFunctionEmulator; // // This is only a workaround for OpenGL driver bugs, and isn't needed in general. // -void InitBuiltInFunctionEmulatorForGLSL(BuiltInFunctionEmulator *emu, sh::GLenum shaderType); +void InitBuiltInFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, sh::GLenum shaderType); + +// +// This function is emulating built-in functions missing from GLSL 1.30 and higher. +// +void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, sh::GLenum shaderType, + int targetGLSLVersion); #endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp index 7123a0d5c0..50e15cbc28 100644 --- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp @@ -11,10 +11,10 @@ void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu) { - TType float1(EbtFloat); - TType float2(EbtFloat, 2); - TType float3(EbtFloat, 3); - TType float4(EbtFloat, 4); + TType *float1 = new TType(EbtFloat); + TType *float2 = new TType(EbtFloat, 2); + TType *float3 = new TType(EbtFloat, 3); + TType *float4 = new TType(EbtFloat, 4); emu->addEmulatedFunction(EOpMod, float1, float1, "float webgl_mod_emu(float x, float y)\n" @@ -250,7 +250,7 @@ void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu) " return (y << 16) | x;\n" "}\n"); - TType uint1(EbtUInt); + TType *uint1 = new TType(EbtUInt); emu->addEmulatedFunction(EOpUnpackSnorm2x16, uint1, "float webgl_fromSnorm(in uint x) {\n" @@ -327,9 +327,9 @@ void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu) " return mul(float4x1(r), float1x3(c));\n" "}\n"); - TType mat2(EbtFloat, 2, 2); - TType mat3(EbtFloat, 3, 3); - TType mat4(EbtFloat, 4, 4); + TType *mat2 = new TType(EbtFloat, 2, 2); + TType *mat3 = new TType(EbtFloat, 3, 3); + TType *mat4 = new TType(EbtFloat, 4, 4); // Remember here that the parameter matrix is actually the transpose // of the matrix that we're trying to invert, and the resulting matrix @@ -407,4 +407,35 @@ void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu) " cof02, cof12, cof22, cof32, cof03, cof13, cof23, cof33 };\n" " return cof / determinant(transpose(m));\n" "}\n"); + + TType *bool1 = new TType(EbtBool); + TType *bool2 = new TType(EbtBool, 2); + TType *bool3 = new TType(EbtBool, 3); + TType *bool4 = new TType(EbtBool, 4); + + // Emulate ESSL3 variant of mix that takes last argument as boolean vector. + // genType mix (genType x, genType y, genBType a): Selects which vector each returned component comes from. + // For a component of 'a' that is false, the corresponding component of 'x' is returned.For a component of 'a' that is true, + // the corresponding component of 'y' is returned. + emu->addEmulatedFunction(EOpMix, float1, float1, bool1, + "float webgl_mix_emu(float x, float y, bool a)\n" + "{\n" + " return a ? y : x;\n" + "}\n"); + emu->addEmulatedFunction(EOpMix, float2, float2, bool2, + "float2 webgl_mix_emu(float2 x, float2 y, bool2 a)\n" + "{\n" + " return a ? y : x;\n" + "}\n"); + emu->addEmulatedFunction(EOpMix, float3, float3, bool3, + "float3 webgl_mix_emu(float3 x, float3 y, bool3 a)\n" + "{\n" + " return a ? y : x;\n" + "}\n"); + emu->addEmulatedFunction(EOpMix, float4, float4, bool4, + "float4 webgl_mix_emu(float4 x, float4 y, bool4 a)\n" + "{\n" + " return a ? y : x;\n" + "}\n"); + } diff --git a/src/3rdparty/angle/src/compiler/translator/Cache.cpp b/src/3rdparty/angle/src/compiler/translator/Cache.cpp new file mode 100644 index 0000000000..57a43700ca --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/Cache.cpp @@ -0,0 +1,100 @@ +// +// 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. +// + +// Cache.cpp: Implements a cache for various commonly created objects. + +#include <limits> + +#include "common/angleutils.h" +#include "common/debug.h" +#include "compiler/translator/Cache.h" + +namespace +{ + +class TScopedAllocator : angle::NonCopyable +{ + public: + TScopedAllocator(TPoolAllocator *allocator) + : mPreviousAllocator(GetGlobalPoolAllocator()) + { + SetGlobalPoolAllocator(allocator); + } + ~TScopedAllocator() + { + SetGlobalPoolAllocator(mPreviousAllocator); + } + + private: + TPoolAllocator *mPreviousAllocator; +}; + +} // namespace + +TCache::TypeKey::TypeKey(TBasicType basicType, + TPrecision precision, + TQualifier qualifier, + unsigned char primarySize, + unsigned char secondarySize) +{ + static_assert(sizeof(components) <= sizeof(value), + "TypeKey::value is too small"); + + const size_t MaxEnumValue = std::numeric_limits<EnumComponentType>::max(); + UNUSED_ASSERTION_VARIABLE(MaxEnumValue); + + // TODO: change to static_assert() once we deprecate MSVC 2013 support + ASSERT(MaxEnumValue >= EbtLast && + MaxEnumValue >= EbpLast && + MaxEnumValue >= EvqLast && + "TypeKey::EnumComponentType is too small"); + + value = 0; + components.basicType = static_cast<EnumComponentType>(basicType); + components.precision = static_cast<EnumComponentType>(precision); + components.qualifier = static_cast<EnumComponentType>(qualifier); + components.primarySize = primarySize; + components.secondarySize = secondarySize; +} + +TCache *TCache::sCache = nullptr; + +void TCache::initialize() +{ + if (sCache == nullptr) + { + sCache = new TCache(); + } +} + +void TCache::destroy() +{ + SafeDelete(sCache); +} + +const TType *TCache::getType(TBasicType basicType, + TPrecision precision, + TQualifier qualifier, + unsigned char primarySize, + unsigned char secondarySize) +{ + TypeKey key(basicType, precision, qualifier, + primarySize, secondarySize); + auto it = sCache->mTypes.find(key); + if (it != sCache->mTypes.end()) + { + return it->second; + } + + TScopedAllocator scopedAllocator(&sCache->mAllocator); + + TType *type = new TType(basicType, precision, qualifier, + primarySize, secondarySize); + type->realize(); + sCache->mTypes.insert(std::make_pair(key, type)); + + return type; +} diff --git a/src/3rdparty/angle/src/compiler/translator/Cache.h b/src/3rdparty/angle/src/compiler/translator/Cache.h new file mode 100644 index 0000000000..1d2abb77e1 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/Cache.h @@ -0,0 +1,90 @@ +// +// 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. +// + +// Cache.h: Implements a cache for various commonly created objects. + +#ifndef COMPILER_TRANSLATOR_CACHE_H_ +#define COMPILER_TRANSLATOR_CACHE_H_ + +#include <stdint.h> +#include <string.h> +#include <map> + +#include "compiler/translator/Types.h" +#include "compiler/translator/PoolAlloc.h" + +class TCache +{ + public: + + static void initialize(); + static void destroy(); + + static const TType *getType(TBasicType basicType, + TPrecision precision) + { + return getType(basicType, precision, EvqTemporary, + 1, 1); + } + static const TType *getType(TBasicType basicType, + unsigned char primarySize = 1, + unsigned char secondarySize = 1) + { + return getType(basicType, EbpUndefined, EvqGlobal, + primarySize, secondarySize); + } + static const TType *getType(TBasicType basicType, + TQualifier qualifier, + unsigned char primarySize = 1, + unsigned char secondarySize = 1) + { + return getType(basicType, EbpUndefined, qualifier, + primarySize, secondarySize); + } + static const TType *getType(TBasicType basicType, + TPrecision precision, + TQualifier qualifier, + unsigned char primarySize, + unsigned char secondarySize); + + private: + TCache() + { + } + + union TypeKey + { + TypeKey(TBasicType basicType, + TPrecision precision, + TQualifier qualifier, + unsigned char primarySize, + unsigned char secondarySize); + + typedef uint8_t EnumComponentType; + struct + { + EnumComponentType basicType; + EnumComponentType precision; + EnumComponentType qualifier; + unsigned char primarySize; + unsigned char secondarySize; + } components; + uint64_t value; + + bool operator < (const TypeKey &other) const + { + return value < other.value; + } + }; + typedef std::map<TypeKey, const TType*> TypeMap; + + TypeMap mTypes; + TPoolAllocator mAllocator; + + static TCache *sCache; +}; + +#endif // COMPILER_TRANSLATOR_CACHE_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/CallDAG.cpp b/src/3rdparty/angle/src/compiler/translator/CallDAG.cpp new file mode 100644 index 0000000000..10f0eb937c --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/CallDAG.cpp @@ -0,0 +1,293 @@ +// +// Copyright (c) 2002-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. +// + +// CallDAG.h: Implements a call graph DAG of functions to be re-used accross +// analyses, allows to efficiently traverse the functions in topological +// order. + +#include "compiler/translator/CallDAG.h" +#include "compiler/translator/InfoSink.h" + +// The CallDAGCreator does all the processing required to create the CallDAG +// structure so that the latter contains only the necessary variables. +class CallDAG::CallDAGCreator : public TIntermTraverser +{ + public: + CallDAGCreator(TInfoSinkBase *info) + : TIntermTraverser(true, false, true), + mCreationInfo(info), + mCurrentFunction(nullptr), + mCurrentIndex(0) + { + } + + InitResult assignIndices() + { + int skipped = 0; + for (auto &it : mFunctions) + { + // Skip unimplemented functions + if (it.second.node) + { + InitResult result = assignIndicesInternal(&it.second); + if (result != INITDAG_SUCCESS) + { + *mCreationInfo << "\n"; + return result; + } + } + else + { + skipped++; + } + } + ASSERT(mFunctions.size() == mCurrentIndex + skipped); + return INITDAG_SUCCESS; + } + + void fillDataStructures(std::vector<Record> *records, std::map<int, int> *idToIndex) + { + ASSERT(records->empty()); + ASSERT(idToIndex->empty()); + + records->resize(mCurrentIndex); + + for (auto &it : mFunctions) + { + CreatorFunctionData &data = it.second; + // Skip unimplemented functions + if (!data.node) + { + continue; + } + ASSERT(data.index < records->size()); + Record &record = (*records)[data.index]; + + record.name = data.name.data(); + record.node = data.node; + + record.callees.reserve(data.callees.size()); + for (auto &callee : data.callees) + { + record.callees.push_back(static_cast<int>(callee->index)); + } + + (*idToIndex)[data.node->getFunctionId()] = static_cast<int>(data.index); + } + } + + private: + + struct CreatorFunctionData + { + CreatorFunctionData() + : node(nullptr), + index(0), + indexAssigned(false), + visiting(false) + { + } + + std::set<CreatorFunctionData*> callees; + TIntermAggregate *node; + TString name; + size_t index; + bool indexAssigned; + bool visiting; + }; + + // Aggregates the AST node for each function as well as the name of the functions called by it + bool visitAggregate(Visit visit, TIntermAggregate *node) override + { + switch (node->getOp()) + { + case EOpPrototype: + if (visit == PreVisit) + { + // Function declaration, create an empty record. + auto& record = mFunctions[node->getName()]; + record.name = node->getName(); + } + break; + case EOpFunction: + { + // Function definition, create the record if need be and remember the node. + if (visit == PreVisit) + { + auto it = mFunctions.find(node->getName()); + + if (it == mFunctions.end()) + { + mCurrentFunction = &mFunctions[node->getName()]; + } + else + { + mCurrentFunction = &it->second; + } + + mCurrentFunction->node = node; + mCurrentFunction->name = node->getName(); + + } + else if (visit == PostVisit) + { + mCurrentFunction = nullptr; + } + break; + } + case EOpFunctionCall: + { + // Function call, add the callees + if (visit == PreVisit) + { + // Do not handle calls to builtin functions + if (node->isUserDefined()) + { + auto it = mFunctions.find(node->getName()); + ASSERT(it != mFunctions.end()); + + // We might be in a top-level function call to set a global variable + if (mCurrentFunction) + { + mCurrentFunction->callees.insert(&it->second); + } + } + } + break; + } + default: + break; + } + return true; + } + + // Recursively assigns indices to a sub DAG + InitResult assignIndicesInternal(CreatorFunctionData *function) + { + ASSERT(function); + + if (!function->node) + { + *mCreationInfo << "Undefined function '" << function->name + << ")' used in the following call chain:"; + return INITDAG_UNDEFINED; + } + + if (function->indexAssigned) + { + return INITDAG_SUCCESS; + } + + if (function->visiting) + { + if (mCreationInfo) + { + *mCreationInfo << "Recursive function call in the following call chain:" << function->name; + } + return INITDAG_RECURSION; + } + function->visiting = true; + + for (auto &callee : function->callees) + { + InitResult result = assignIndicesInternal(callee); + if (result != INITDAG_SUCCESS) + { + // We know that there is an issue with the call chain in the AST, + // print the link of the chain we were processing. + if (mCreationInfo) + { + *mCreationInfo << " <- " << function->name << ")"; + } + return result; + } + } + + function->index = mCurrentIndex++; + function->indexAssigned = true; + + function->visiting = false; + return INITDAG_SUCCESS; + } + + TInfoSinkBase *mCreationInfo; + + std::map<TString, CreatorFunctionData> mFunctions; + CreatorFunctionData *mCurrentFunction; + size_t mCurrentIndex; +}; + +// CallDAG + +CallDAG::CallDAG() +{ +} + +CallDAG::~CallDAG() +{ +} + +const size_t CallDAG::InvalidIndex = std::numeric_limits<size_t>::max(); + +size_t CallDAG::findIndex(const TIntermAggregate *function) const +{ + TOperator op = function->getOp(); + ASSERT(op == EOpPrototype || op == EOpFunction || op == EOpFunctionCall); + UNUSED_ASSERTION_VARIABLE(op); + + auto it = mFunctionIdToIndex.find(function->getFunctionId()); + + if (it == mFunctionIdToIndex.end()) + { + return InvalidIndex; + } + else + { + return it->second; + } +} + +const CallDAG::Record &CallDAG::getRecordFromIndex(size_t index) const +{ + ASSERT(index != InvalidIndex && index < mRecords.size()); + return mRecords[index]; +} + +const CallDAG::Record &CallDAG::getRecord(const TIntermAggregate *function) const +{ + size_t index = findIndex(function); + ASSERT(index != InvalidIndex && index < mRecords.size()); + return mRecords[index]; +} + +size_t CallDAG::size() const +{ + return mRecords.size(); +} + +void CallDAG::clear() +{ + mRecords.clear(); + mFunctionIdToIndex.clear(); +} + +CallDAG::InitResult CallDAG::init(TIntermNode *root, TInfoSinkBase *info) +{ + CallDAGCreator creator(info); + + // Creates the mapping of functions to callees + root->traverse(&creator); + + // Does the topological sort and detects recursions + InitResult result = creator.assignIndices(); + if (result != INITDAG_SUCCESS) + { + return result; + } + + creator.fillDataStructures(&mRecords, &mFunctionIdToIndex); + return INITDAG_SUCCESS; +} diff --git a/src/3rdparty/angle/src/compiler/translator/CallDAG.h b/src/3rdparty/angle/src/compiler/translator/CallDAG.h new file mode 100644 index 0000000000..06c377db00 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/CallDAG.h @@ -0,0 +1,75 @@ +// +// Copyright (c) 2002-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. +// + +// CallDAG.h: Defines a call graph DAG of functions to be re-used accross +// analyses, allows to efficiently traverse the functions in topological +// order. + +#ifndef COMPILER_TRANSLATOR_CALLDAG_H_ +#define COMPILER_TRANSLATOR_CALLDAG_H_ + +#include <map> + +#include "compiler/translator/IntermNode.h" +#include "compiler/translator/VariableInfo.h" + + +// The translator needs to analyze the the graph of the function calls +// to run checks and analyses; since in GLSL recursion is not allowed +// that graph is a DAG. +// This class is used to precompute that function call DAG so that it +// can be reused by multiple analyses. +// +// It stores a vector of function records, with one record per function. +// Records are accessed by index but a mangled function name can be converted +// to the index of the corresponding record. The records mostly contain the +// AST node of the function and the indices of the function's callees. +// +// In addition, records are in reverse topological order: a function F being +// called by a function G will have index index(F) < index(G), that way +// depth-first analysis becomes analysis in the order of indices. + +class CallDAG : angle::NonCopyable +{ + public: + CallDAG(); + ~CallDAG(); + + struct Record + { + std::string name; + TIntermAggregate *node; + std::vector<int> callees; + }; + + enum InitResult + { + INITDAG_SUCCESS, + INITDAG_RECURSION, + INITDAG_UNDEFINED, + }; + + // Returns INITDAG_SUCCESS if it was able to create the DAG, otherwise prints + // the initialization error in info, if present. + InitResult init(TIntermNode *root, TInfoSinkBase *info); + + // Returns InvalidIndex if the function wasn't found + size_t findIndex(const TIntermAggregate *function) const; + + const Record &getRecordFromIndex(size_t index) const; + const Record &getRecord(const TIntermAggregate *function) const; + size_t size() const; + void clear(); + + const static size_t InvalidIndex; + private: + std::vector<Record> mRecords; + std::map<int, int> mFunctionIdToIndex; + + class CallDAGCreator; +}; + +#endif // COMPILER_TRANSLATOR_CALLDAG_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/CodeGen.cpp b/src/3rdparty/angle/src/compiler/translator/CodeGen.cpp index 5e3eb1cc05..f099bccf15 100644 --- a/src/3rdparty/angle/src/compiler/translator/CodeGen.cpp +++ b/src/3rdparty/angle/src/compiler/translator/CodeGen.cpp @@ -4,8 +4,14 @@ // found in the LICENSE file. // +#ifdef ANGLE_ENABLE_ESSL #include "compiler/translator/TranslatorESSL.h" +#endif + +#ifdef ANGLE_ENABLE_GLSL #include "compiler/translator/TranslatorGLSL.h" +#endif + #ifdef ANGLE_ENABLE_HLSL #include "compiler/translator/TranslatorHLSL.h" #endif // ANGLE_ENABLE_HLSL @@ -20,22 +26,44 @@ TCompiler* ConstructCompiler( { switch (output) { case SH_ESSL_OUTPUT: +#ifdef ANGLE_ENABLE_ESSL return new TranslatorESSL(type, spec); - case SH_GLSL_CORE_OUTPUT: +#else + // This compiler is not supported in this + // configuration. Return NULL per the ShConstructCompiler API. + return nullptr; +#endif // ANGLE_ENABLE_ESSL + case SH_GLSL_130_OUTPUT: + case SH_GLSL_140_OUTPUT: + case SH_GLSL_150_CORE_OUTPUT: + case SH_GLSL_330_CORE_OUTPUT: + case SH_GLSL_400_CORE_OUTPUT: + case SH_GLSL_410_CORE_OUTPUT: + case SH_GLSL_420_CORE_OUTPUT: + case SH_GLSL_430_CORE_OUTPUT: + case SH_GLSL_440_CORE_OUTPUT: + case SH_GLSL_450_CORE_OUTPUT: case SH_GLSL_COMPATIBILITY_OUTPUT: +#ifdef ANGLE_ENABLE_GLSL return new TranslatorGLSL(type, spec, output); - case SH_HLSL9_OUTPUT: - case SH_HLSL11_OUTPUT: +#else + // This compiler is not supported in this + // configuration. Return NULL per the ShConstructCompiler API. + return nullptr; +#endif // ANGLE_ENABLE_GLSL + case SH_HLSL_3_0_OUTPUT: + case SH_HLSL_4_1_OUTPUT: + case SH_HLSL_4_0_FL9_3_OUTPUT: #ifdef ANGLE_ENABLE_HLSL return new TranslatorHLSL(type, spec, output); #else // This compiler is not supported in this // configuration. Return NULL per the ShConstructCompiler API. - return NULL; + return nullptr; #endif // ANGLE_ENABLE_HLSL default: // Unknown format. Return NULL per the ShConstructCompiler API. - return NULL; + return nullptr; } } diff --git a/src/3rdparty/angle/src/compiler/translator/Common.h b/src/3rdparty/angle/src/compiler/translator/Common.h index ac1aef0f4c..60223232af 100644 --- a/src/3rdparty/angle/src/compiler/translator/Common.h +++ b/src/3rdparty/angle/src/compiler/translator/Common.h @@ -14,9 +14,9 @@ #include <limits> #include <stdio.h> -#include "compiler/translator/PoolAlloc.h" -#include "compiler/translator/compilerdebug.h" #include "common/angleutils.h" +#include "common/debug.h" +#include "compiler/translator/PoolAlloc.h" struct TSourceLoc { int first_file; @@ -60,18 +60,21 @@ inline TString* NewPoolTString(const char* s) // // Pool allocator versions of vectors, lists, and maps // -template <class T> class TVector : public std::vector<T, pool_allocator<T> > { -public: - typedef typename std::vector<T, pool_allocator<T> >::size_type size_type; - TVector() : std::vector<T, pool_allocator<T> >() {} - TVector(const pool_allocator<T>& a) : std::vector<T, pool_allocator<T> >(a) {} - TVector(size_type i): std::vector<T, pool_allocator<T> >(i) {} +template <class T> +class TVector : public std::vector<T, pool_allocator<T>> +{ + public: + typedef typename std::vector<T, pool_allocator<T>>::size_type size_type; + TVector() : std::vector<T, pool_allocator<T>>() {} + TVector(const pool_allocator<T> &a) : std::vector<T, pool_allocator<T>>(a) {} + TVector(size_type i) : std::vector<T, pool_allocator<T>>(i) {} }; -template <class K, class D, class CMP = std::less<K> > -class TMap : public std::map<K, D, CMP, pool_allocator<std::pair<const K, D> > > { -public: - typedef pool_allocator<std::pair<const K, D> > tAllocator; +template <class K, class D, class CMP = std::less<K>> +class TMap : public std::map<K, D, CMP, pool_allocator<std::pair<const K, D>>> +{ + public: + typedef pool_allocator<std::pair<const K, D>> tAllocator; TMap() : std::map<K, D, CMP, tAllocator>() {} // use correct two-stage name lookup supported in gcc 3.4 and above diff --git a/src/3rdparty/angle/src/compiler/translator/Compiler.cpp b/src/3rdparty/angle/src/compiler/translator/Compiler.cpp index 534861ca70..18524ce569 100644 --- a/src/3rdparty/angle/src/compiler/translator/Compiler.cpp +++ b/src/3rdparty/angle/src/compiler/translator/Compiler.cpp @@ -4,15 +4,19 @@ // found in the LICENSE file. // +#include "compiler/translator/Cache.h" #include "compiler/translator/Compiler.h" -#include "compiler/translator/DetectCallDepth.h" +#include "compiler/translator/CallDAG.h" #include "compiler/translator/ForLoopUnroll.h" #include "compiler/translator/Initialize.h" #include "compiler/translator/InitializeParseContext.h" #include "compiler/translator/InitializeVariables.h" #include "compiler/translator/ParseContext.h" +#include "compiler/translator/PruneEmptyDeclarations.h" #include "compiler/translator/RegenerateStructNames.h" +#include "compiler/translator/RemovePow.h" #include "compiler/translator/RenameFunction.h" +#include "compiler/translator/RewriteDoWhile.h" #include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h" #include "compiler/translator/UnfoldShortCircuitAST.h" #include "compiler/translator/ValidateLimitations.h" @@ -33,6 +37,20 @@ bool IsWebGLBasedSpec(ShShaderSpec spec) spec == SH_WEBGL2_SPEC); } +bool IsGLSL130OrNewer(ShShaderOutput output) +{ + return (output == SH_GLSL_130_OUTPUT || + output == SH_GLSL_140_OUTPUT || + output == SH_GLSL_150_CORE_OUTPUT || + output == SH_GLSL_330_CORE_OUTPUT || + output == SH_GLSL_400_CORE_OUTPUT || + output == SH_GLSL_410_CORE_OUTPUT || + output == SH_GLSL_420_CORE_OUTPUT || + output == SH_GLSL_430_CORE_OUTPUT || + output == SH_GLSL_440_CORE_OUTPUT || + output == SH_GLSL_450_CORE_OUTPUT); +} + size_t GetGlobalMaxTokenSize(ShShaderSpec spec) { // WebGL defines a max token legnth of 256, while ES2 leaves max token @@ -126,7 +144,8 @@ TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output) fragmentPrecisionHigh(false), clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC), builtInFunctionEmulator(), - mSourcePath(NULL) + mSourcePath(NULL), + mTemporaryIndex(0) { } @@ -134,6 +153,15 @@ TCompiler::~TCompiler() { } +bool TCompiler::shouldRunLoopAndIndexingValidation(int compileOptions) const +{ + // If compiling an ESSL 1.00 shader for WebGL, or if its been requested through the API, + // validate loop and indexing as well (to verify that the shader only uses minimal functionality + // of ESSL 1.00 as in Appendix A of the spec). + return (IsWebGLBasedSpec(shaderSpec) && shaderVersion == 100) || + (compileOptions & SH_VALIDATE_LOOP_INDEXING); +} + bool TCompiler::Init(const ShBuiltInResources& resources) { shaderVersion = 100; @@ -165,8 +193,9 @@ TIntermNode *TCompiler::compileTreeForTesting(const char* const shaderStrings[], return compileTreeImpl(shaderStrings, numStrings, compileOptions); } -TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], - size_t numStrings, int compileOptions) +TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[], + size_t numStrings, + const int compileOptions) { clearResults(); @@ -176,10 +205,6 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], // Reset the extension behavior for each compilation unit. ResetExtensionBehavior(extensionBehavior); - // If compiling for WebGL, validate loop and indexing as well. - if (IsWebGLBasedSpec(shaderSpec)) - compileOptions |= SH_VALIDATE_LOOP_INDEXING; - // First string is path of source file if flag is set. The actual source follows. size_t firstSource = 0; if (compileOptions & SH_SOURCE_PATH) @@ -188,13 +213,11 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], ++firstSource; } - bool debugShaderPrecision = getResources().WEBGL_debug_shader_precision == 1; TIntermediate intermediate(infoSink); - TParseContext parseContext(symbolTable, extensionBehavior, intermediate, - shaderType, shaderSpec, compileOptions, true, - infoSink, debugShaderPrecision); + TParseContext parseContext(symbolTable, extensionBehavior, intermediate, shaderType, shaderSpec, + compileOptions, true, infoSink, getResources()); - parseContext.fragmentPrecisionHigh = fragmentPrecisionHigh; + parseContext.setFragmentPrecisionHighOnESSL1(fragmentPrecisionHigh); SetGlobalParseContext(&parseContext); // We preserve symbols at the built-in level from compile-to-compile. @@ -203,8 +226,8 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], // Parse shader. bool success = - (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], NULL, &parseContext) == 0) && - (parseContext.treeRoot != NULL); + (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], nullptr, &parseContext) == 0) && + (parseContext.getTreeRoot() != nullptr); shaderVersion = parseContext.getShaderVersion(); if (success && MapSpecToShaderVersion(shaderSpec) < shaderVersion) @@ -214,7 +237,7 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], success = false; } - TIntermNode *root = NULL; + TIntermNode *root = nullptr; if (success) { @@ -224,20 +247,42 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], symbolTable.setGlobalInvariant(); } - root = parseContext.treeRoot; - success = intermediate.postProcess(root); + root = parseContext.getTreeRoot(); + root = intermediate.postProcess(root); + + // Highp might have been auto-enabled based on shader version + fragmentPrecisionHigh = parseContext.getFragmentPrecisionHigh(); // Disallow expressions deemed too complex. if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY)) success = limitExpressionComplexity(root); + // Create the function DAG and check there is no recursion + if (success) + success = initCallDag(root); + + if (success && (compileOptions & SH_LIMIT_CALL_STACK_DEPTH)) + success = checkCallDepth(); + + // Checks which functions are used and if "main" exists if (success) - success = detectCallDepth(root, infoSink, (compileOptions & SH_LIMIT_CALL_STACK_DEPTH) != 0); + { + functionMetadata.clear(); + functionMetadata.resize(mCallDag.size()); + success = tagUsedFunctions(); + } + + if (success && !(compileOptions & SH_DONT_PRUNE_UNUSED_FUNCTIONS)) + success = pruneUnusedFunctions(root); + + // Prune empty declarations to work around driver bugs and to keep declaration output simple. + if (success) + PruneEmptyDeclarations(root); if (success && shaderVersion == 300 && shaderType == GL_FRAGMENT_SHADER) success = validateOutputs(root); - if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING)) + if (success && shouldRunLoopAndIndexingValidation(compileOptions)) success = validateLimitations(root); if (success && (compileOptions & SH_TIMING_RESTRICTIONS)) @@ -249,12 +294,14 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], // Unroll for-loop markup needs to happen after validateLimitations pass. if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX)) { - ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex); + ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex, + shouldRunLoopAndIndexingValidation(compileOptions)); root->traverse(&marker); } if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX)) { - ForLoopUnrollMarker marker(ForLoopUnrollMarker::kSamplerArrayIndex); + ForLoopUnrollMarker marker(ForLoopUnrollMarker::kSamplerArrayIndex, + shouldRunLoopAndIndexingValidation(compileOptions)); root->traverse(&marker); if (marker.samplerArrayIndexIsFloatLoopIndex()) { @@ -275,9 +322,16 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS)) arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root); - if (success && shaderType == GL_VERTEX_SHADER && (compileOptions & SH_INIT_GL_POSITION)) + // gl_Position is always written in compatibility output mode + if (success && shaderType == GL_VERTEX_SHADER && + ((compileOptions & SH_INIT_GL_POSITION) || + (outputType == SH_GLSL_COMPATIBILITY_OUTPUT))) initializeGLPosition(root); + // This pass might emit short circuits so keep it before the short circuit unfolding + if (success && (compileOptions & SH_REWRITE_DO_WHILE_LOOPS)) + RewriteDoWhile(root, getTemporaryIndex()); + if (success && (compileOptions & SH_UNFOLD_SHORT_CIRCUIT)) { UnfoldShortCircuitAST unfoldShortCircuit; @@ -285,7 +339,12 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], unfoldShortCircuit.updateTree(); } - if (success && (compileOptions & SH_VARIABLES)) + if (success && (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT)) + { + RemovePow(root); + } + + if (success && shouldCollectVariables(compileOptions)) { collectVariables(root); if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS) @@ -369,11 +428,6 @@ bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources) floatingPoint.secondarySize = 1; floatingPoint.array = false; - TPublicType sampler; - sampler.primarySize = 1; - sampler.secondarySize = 1; - sampler.array = false; - switch(shaderType) { case GL_FRAGMENT_SHADER: @@ -386,14 +440,15 @@ bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources) default: assert(false && "Language not supported"); } - // We set defaults for all the sampler types, even those that are + // Set defaults for sampler types that have default precision, even those that are // only available if an extension exists. - for (int samplerType = EbtGuardSamplerBegin + 1; - samplerType < EbtGuardSamplerEnd; ++samplerType) - { - sampler.type = static_cast<TBasicType>(samplerType); - symbolTable.setDefaultPrecision(sampler, EbpLow); - } + // New sampler types in ESSL3 don't have default precision. ESSL1 types do. + initSamplerDefaultPrecision(EbtSampler2D); + initSamplerDefaultPrecision(EbtSamplerCube); + // SamplerExternalOES is specified in the extension to have default precision. + initSamplerDefaultPrecision(EbtSamplerExternalOES); + // It isn't specified whether Sampler2DRect has default precision. + initSamplerDefaultPrecision(EbtSampler2DRect); InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable); @@ -402,6 +457,17 @@ bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources) return true; } +void TCompiler::initSamplerDefaultPrecision(TBasicType samplerType) +{ + ASSERT(samplerType > EbtGuardSamplerBegin && samplerType < EbtGuardSamplerEnd); + TPublicType sampler; + sampler.primarySize = 1; + sampler.secondarySize = 1; + sampler.array = false; + sampler.type = samplerType; + symbolTable.setDefaultPrecision(sampler, EbpLow); +} + void TCompiler::setResourceString() { std::ostringstream strstream; @@ -420,6 +486,7 @@ void TCompiler::setResourceString() << ":FragmentPrecisionHigh:" << compileResources.FragmentPrecisionHigh << ":MaxExpressionComplexity:" << compileResources.MaxExpressionComplexity << ":MaxCallStackDepth:" << compileResources.MaxCallStackDepth + << ":EXT_blend_func_extended:" << compileResources.EXT_blend_func_extended << ":EXT_frag_depth:" << compileResources.EXT_frag_depth << ":EXT_shader_texture_lod:" << compileResources.EXT_shader_texture_lod << ":EXT_shader_framebuffer_fetch:" << compileResources.EXT_shader_framebuffer_fetch @@ -429,6 +496,7 @@ void TCompiler::setResourceString() << ":MaxFragmentInputVectors:" << compileResources.MaxFragmentInputVectors << ":MinProgramTexelOffset:" << compileResources.MinProgramTexelOffset << ":MaxProgramTexelOffset:" << compileResources.MaxProgramTexelOffset + << ":MaxDualSourceDrawBuffers:" << compileResources.MaxDualSourceDrawBuffers << ":NV_draw_buffers:" << compileResources.NV_draw_buffers << ":WEBGL_debug_shader_precision:" << compileResources.WEBGL_debug_shader_precision; @@ -454,39 +522,175 @@ void TCompiler::clearResults() nameMap.clear(); mSourcePath = NULL; + mTemporaryIndex = 0; } -bool TCompiler::detectCallDepth(TIntermNode* inputRoot, TInfoSink& inputInfoSink, bool limitCallStackDepth) +bool TCompiler::initCallDag(TIntermNode *root) { - DetectCallDepth detect(inputInfoSink, limitCallStackDepth, maxCallStackDepth); - inputRoot->traverse(&detect); - switch (detect.detectCallDepth()) + mCallDag.clear(); + + switch (mCallDag.init(root, &infoSink.info)) { - case DetectCallDepth::kErrorNone: + case CallDAG::INITDAG_SUCCESS: return true; - case DetectCallDepth::kErrorMissingMain: - inputInfoSink.info.prefix(EPrefixError); - inputInfoSink.info << "Missing main()"; - return false; - case DetectCallDepth::kErrorRecursion: - inputInfoSink.info.prefix(EPrefixError); - inputInfoSink.info << "Function recursion detected"; - return false; - case DetectCallDepth::kErrorMaxDepthExceeded: - inputInfoSink.info.prefix(EPrefixError); - inputInfoSink.info << "Function call stack too deep"; + case CallDAG::INITDAG_RECURSION: + infoSink.info.prefix(EPrefixError); + infoSink.info << "Function recursion detected"; return false; - default: - UNREACHABLE(); + case CallDAG::INITDAG_UNDEFINED: + infoSink.info.prefix(EPrefixError); + infoSink.info << "Unimplemented function detected"; return false; } + + UNREACHABLE(); + return true; +} + +bool TCompiler::checkCallDepth() +{ + std::vector<int> depths(mCallDag.size()); + + for (size_t i = 0; i < mCallDag.size(); i++) + { + int depth = 0; + auto &record = mCallDag.getRecordFromIndex(i); + + for (auto &calleeIndex : record.callees) + { + depth = std::max(depth, depths[calleeIndex] + 1); + } + + depths[i] = depth; + + if (depth >= maxCallStackDepth) + { + // Trace back the function chain to have a meaningful info log. + infoSink.info.prefix(EPrefixError); + infoSink.info << "Call stack too deep (larger than " << maxCallStackDepth + << ") with the following call chain: " << record.name; + + int currentFunction = static_cast<int>(i); + int currentDepth = depth; + + while (currentFunction != -1) + { + infoSink.info << " -> " << mCallDag.getRecordFromIndex(currentFunction).name; + + int nextFunction = -1; + for (auto& calleeIndex : mCallDag.getRecordFromIndex(currentFunction).callees) + { + if (depths[calleeIndex] == currentDepth - 1) + { + currentDepth--; + nextFunction = calleeIndex; + } + } + + currentFunction = nextFunction; + } + + return false; + } + } + + return true; +} + +bool TCompiler::tagUsedFunctions() +{ + // Search from main, starting from the end of the DAG as it usually is the root. + for (size_t i = mCallDag.size(); i-- > 0;) + { + if (mCallDag.getRecordFromIndex(i).name == "main(") + { + internalTagUsedFunction(i); + return true; + } + } + + infoSink.info.prefix(EPrefixError); + infoSink.info << "Missing main()\n"; + return false; +} + +void TCompiler::internalTagUsedFunction(size_t index) +{ + if (functionMetadata[index].used) + { + return; + } + + functionMetadata[index].used = true; + + for (int calleeIndex : mCallDag.getRecordFromIndex(index).callees) + { + internalTagUsedFunction(calleeIndex); + } +} + +// A predicate for the stl that returns if a top-level node is unused +class TCompiler::UnusedPredicate +{ + public: + UnusedPredicate(const CallDAG *callDag, const std::vector<FunctionMetadata> *metadatas) + : mCallDag(callDag), + mMetadatas(metadatas) + { + } + + bool operator ()(TIntermNode *node) + { + const TIntermAggregate *asAggregate = node->getAsAggregate(); + + if (asAggregate == nullptr) + { + return false; + } + + if (!(asAggregate->getOp() == EOpFunction || asAggregate->getOp() == EOpPrototype)) + { + return false; + } + + size_t callDagIndex = mCallDag->findIndex(asAggregate); + if (callDagIndex == CallDAG::InvalidIndex) + { + // This happens only for unimplemented prototypes which are thus unused + ASSERT(asAggregate->getOp() == EOpPrototype); + return true; + } + + ASSERT(callDagIndex < mMetadatas->size()); + return !(*mMetadatas)[callDagIndex].used; + } + + private: + const CallDAG *mCallDag; + const std::vector<FunctionMetadata> *mMetadatas; +}; + +bool TCompiler::pruneUnusedFunctions(TIntermNode *root) +{ + TIntermAggregate *rootNode = root->getAsAggregate(); + ASSERT(rootNode != nullptr); + + UnusedPredicate isUnused(&mCallDag, &functionMetadata); + TIntermSequence *sequence = rootNode->getSequence(); + + if (!sequence->empty()) + { + sequence->erase(std::remove_if(sequence->begin(), sequence->end(), isUnused), sequence->end()); + } + + return true; } bool TCompiler::validateOutputs(TIntermNode* root) { - ValidateOutputs validateOutputs(infoSink.info, compileResources.MaxDrawBuffers); + ValidateOutputs validateOutputs(getExtensionBehavior(), compileResources.MaxDrawBuffers); root->traverse(&validateOutputs); - return (validateOutputs.numErrors() == 0); + return (validateOutputs.validateAndCountErrors(infoSink.info) == 0); } void TCompiler::rewriteCSSShader(TIntermNode* root) @@ -497,7 +701,7 @@ void TCompiler::rewriteCSSShader(TIntermNode* root) bool TCompiler::validateLimitations(TIntermNode* root) { - ValidateLimitations validate(shaderType, infoSink.info); + ValidateLimitations validate(shaderType, &infoSink.info); root->traverse(&validate); return validate.numErrors() == 0; } @@ -543,17 +747,6 @@ bool TCompiler::limitExpressionComplexity(TIntermNode* root) return false; } - TDependencyGraph graph(root); - - for (TFunctionCallVector::const_iterator iter = graph.beginUserDefinedFunctionCalls(); - iter != graph.endUserDefinedFunctionCalls(); - ++iter) - { - TGraphFunctionCall* samplerSymbol = *iter; - TDependencyGraphTraverser graphTraverser; - samplerSymbol->traverse(&graphTraverser); - } - return true; } diff --git a/src/3rdparty/angle/src/compiler/translator/Compiler.h b/src/3rdparty/angle/src/compiler/translator/Compiler.h index bcdb0d4c9d..c00a8f97aa 100644 --- a/src/3rdparty/angle/src/compiler/translator/Compiler.h +++ b/src/3rdparty/angle/src/compiler/translator/Compiler.h @@ -15,6 +15,7 @@ // #include "compiler/translator/BuiltInFunctionEmulator.h" +#include "compiler/translator/CallDAG.h" #include "compiler/translator/ExtensionBehavior.h" #include "compiler/translator/HashNames.h" #include "compiler/translator/InfoSink.h" @@ -36,6 +37,11 @@ class TranslatorHLSL; bool IsWebGLBasedSpec(ShShaderSpec spec); // +// Helper function to check if the shader type is GLSL. +// +bool IsGLSL130OrNewer(ShShaderOutput output); + +// // The base class used to back handles returned to the driver. // class TShHandleBase { @@ -61,8 +67,8 @@ class TCompiler : public TShHandleBase { public: TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output); - virtual ~TCompiler(); - virtual TCompiler* getAsCompiler() { return this; } + ~TCompiler() override; + TCompiler *getAsCompiler() override { return this; } bool Init(const ShBuiltInResources& resources); @@ -79,8 +85,11 @@ class TCompiler : public TShHandleBase int getShaderVersion() const { return shaderVersion; } TInfoSink& getInfoSink() { return infoSink; } + // Clears the results from the previous compilation. + void clearResults(); + const std::vector<sh::Attribute> &getAttributes() const { return attributes; } - const std::vector<sh::Attribute> &getOutputVariables() const { return outputVariables; } + const std::vector<sh::OutputVariable> &getOutputVariables() const { return outputVariables; } const std::vector<sh::Uniform> &getUniforms() const { return uniforms; } const std::vector<sh::Varying> &getVaryings() const { return varyings; } const std::vector<sh::InterfaceBlock> &getInterfaceBlocks() const { return interfaceBlocks; } @@ -92,6 +101,8 @@ class TCompiler : public TShHandleBase ShShaderOutput getOutputType() const { return outputType; } const std::string &getBuiltInResourcesString() const { return builtInResourcesString; } + bool shouldRunLoopAndIndexingValidation(int compileOptions) const; + // Get the resources set by InitBuiltInSymbolTable const ShBuiltInResources& getResources() const; @@ -101,10 +112,8 @@ class TCompiler : public TShHandleBase bool InitBuiltInSymbolTable(const ShBuiltInResources& resources); // Compute the string representation of the built-in resources void setResourceString(); - // Clears the results from the previous compilation. - void clearResults(); - // Return true if function recursion is detected or call depth exceeded. - bool detectCallDepth(TIntermNode* root, TInfoSink& infoSink, bool limitCallStackDepth); + // Return false if the call depth is exceeded. + bool checkCallDepth(); // Returns true if a program has no conflicting or missing fragment outputs bool validateOutputs(TIntermNode* root); // Rewrites a shader's intermediate tree according to the CSS Shaders spec. @@ -145,26 +154,57 @@ class TCompiler : public TShHandleBase const char *getSourcePath() const; const TPragma& getPragma() const { return mPragma; } void writePragma(); + unsigned int *getTemporaryIndex() { return &mTemporaryIndex; } const ArrayBoundsClamper& getArrayBoundsClamper() const; ShArrayIndexClampingStrategy getArrayIndexClampingStrategy() const; const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const; std::vector<sh::Attribute> attributes; - std::vector<sh::Attribute> outputVariables; + std::vector<sh::OutputVariable> outputVariables; std::vector<sh::Uniform> uniforms; std::vector<sh::ShaderVariable> expandedUniforms; std::vector<sh::Varying> varyings; std::vector<sh::InterfaceBlock> interfaceBlocks; + virtual bool shouldCollectVariables(int compileOptions) + { + return (compileOptions & SH_VARIABLES) != 0; + } + private: - TIntermNode *compileTreeImpl(const char* const shaderStrings[], - size_t numStrings, int compileOptions); + // Creates the function call DAG for further analysis, returning false if there is a recursion + bool initCallDag(TIntermNode *root); + // Return false if "main" doesn't exist + bool tagUsedFunctions(); + void internalTagUsedFunction(size_t index); + + void initSamplerDefaultPrecision(TBasicType samplerType); + + // Removes unused function declarations and prototypes from the AST + class UnusedPredicate; + bool pruneUnusedFunctions(TIntermNode *root); + + TIntermNode *compileTreeImpl(const char *const shaderStrings[], + size_t numStrings, + const int compileOptions); sh::GLenum shaderType; ShShaderSpec shaderSpec; ShShaderOutput outputType; + struct FunctionMetadata + { + FunctionMetadata() + : used(false) + { + } + bool used; + }; + + CallDAG mCallDag; + std::vector<FunctionMetadata> functionMetadata; + int maxUniformVectors; int maxExpressionComplexity; int maxCallStackDepth; @@ -193,6 +233,8 @@ class TCompiler : public TShHandleBase NameMap nameMap; TPragma mPragma; + + unsigned int mTemporaryIndex; }; // diff --git a/src/3rdparty/angle/src/compiler/translator/ConstantUnion.h b/src/3rdparty/angle/src/compiler/translator/ConstantUnion.h index 31ff2ccfa7..a86d27f3ff 100644 --- a/src/3rdparty/angle/src/compiler/translator/ConstantUnion.h +++ b/src/3rdparty/angle/src/compiler/translator/ConstantUnion.h @@ -9,16 +9,18 @@ #include <assert.h> -class ConstantUnion { +#include "compiler/translator/BaseTypes.h" + +class TConstantUnion { public: POOL_ALLOCATOR_NEW_DELETE(); - ConstantUnion() + TConstantUnion() { iConst = 0; type = EbtVoid; } - bool cast(TBasicType newType, const ConstantUnion &constant) + bool cast(TBasicType newType, const TConstantUnion &constant) { switch (newType) { @@ -109,7 +111,7 @@ public: return b == bConst; } - bool operator==(const ConstantUnion& constant) const + bool operator==(const TConstantUnion& constant) const { if (constant.type != type) return false; @@ -148,12 +150,12 @@ public: return !operator==(b); } - bool operator!=(const ConstantUnion& constant) const + bool operator!=(const TConstantUnion& constant) const { return !operator==(constant); } - bool operator>(const ConstantUnion& constant) const + bool operator>(const TConstantUnion& constant) const { assert(type == constant.type); switch (type) { @@ -168,7 +170,7 @@ public: } } - bool operator<(const ConstantUnion& constant) const + bool operator<(const TConstantUnion& constant) const { assert(type == constant.type); switch (type) { @@ -183,9 +185,9 @@ public: } } - ConstantUnion operator+(const ConstantUnion& constant) const + TConstantUnion operator+(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst + constant.iConst); break; @@ -197,9 +199,9 @@ public: return returnValue; } - ConstantUnion operator-(const ConstantUnion& constant) const + TConstantUnion operator-(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst - constant.iConst); break; @@ -211,9 +213,9 @@ public: return returnValue; } - ConstantUnion operator*(const ConstantUnion& constant) const + TConstantUnion operator*(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst * constant.iConst); break; @@ -225,9 +227,9 @@ public: return returnValue; } - ConstantUnion operator%(const ConstantUnion& constant) const + TConstantUnion operator%(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst % constant.iConst); break; @@ -238,9 +240,9 @@ public: return returnValue; } - ConstantUnion operator>>(const ConstantUnion& constant) const + TConstantUnion operator>>(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst >> constant.iConst); break; @@ -251,9 +253,9 @@ public: return returnValue; } - ConstantUnion operator<<(const ConstantUnion& constant) const + TConstantUnion operator<<(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; // The signedness of the second parameter might be different, but we // don't care, since the result is undefined if the second parameter is // negative, and aliasing should not be a problem with unions. @@ -267,9 +269,9 @@ public: return returnValue; } - ConstantUnion operator&(const ConstantUnion& constant) const + TConstantUnion operator&(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(constant.type == EbtInt || constant.type == EbtUInt); switch (type) { case EbtInt: returnValue.setIConst(iConst & constant.iConst); break; @@ -280,9 +282,9 @@ public: return returnValue; } - ConstantUnion operator|(const ConstantUnion& constant) const + TConstantUnion operator|(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst | constant.iConst); break; @@ -293,9 +295,9 @@ public: return returnValue; } - ConstantUnion operator^(const ConstantUnion& constant) const + TConstantUnion operator^(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst ^ constant.iConst); break; @@ -306,9 +308,9 @@ public: return returnValue; } - ConstantUnion operator&&(const ConstantUnion& constant) const + TConstantUnion operator&&(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtBool: returnValue.setBConst(bConst && constant.bConst); break; @@ -318,9 +320,9 @@ public: return returnValue; } - ConstantUnion operator||(const ConstantUnion& constant) const + TConstantUnion operator||(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtBool: returnValue.setBConst(bConst || constant.bConst); break; diff --git a/src/3rdparty/angle/src/compiler/translator/Diagnostics.cpp b/src/3rdparty/angle/src/compiler/translator/Diagnostics.cpp index 92db3e55cf..593137fb0a 100644 --- a/src/3rdparty/angle/src/compiler/translator/Diagnostics.cpp +++ b/src/3rdparty/angle/src/compiler/translator/Diagnostics.cpp @@ -6,7 +6,7 @@ #include "compiler/translator/Diagnostics.h" -#include "compiler/translator/compilerdebug.h" +#include "common/debug.h" #include "compiler/translator/InfoSink.h" #include "compiler/preprocessor/SourceLocation.h" @@ -50,11 +50,6 @@ void TDiagnostics::writeInfo(Severity severity, sink << "'" << token << "' : " << reason << " " << extra << "\n"; } -void TDiagnostics::writeDebug(const std::string& str) -{ - mInfoSink.debug << str; -} - void TDiagnostics::print(ID id, const pp::SourceLocation& loc, const std::string& text) diff --git a/src/3rdparty/angle/src/compiler/translator/Diagnostics.h b/src/3rdparty/angle/src/compiler/translator/Diagnostics.h index 078bc97772..bc26e4584f 100644 --- a/src/3rdparty/angle/src/compiler/translator/Diagnostics.h +++ b/src/3rdparty/angle/src/compiler/translator/Diagnostics.h @@ -16,7 +16,7 @@ class TDiagnostics : public pp::Diagnostics, angle::NonCopyable { public: TDiagnostics(TInfoSink& infoSink); - virtual ~TDiagnostics(); + ~TDiagnostics() override; TInfoSink& infoSink() { return mInfoSink; } @@ -29,12 +29,8 @@ class TDiagnostics : public pp::Diagnostics, angle::NonCopyable const std::string& token, const std::string& extra); - void writeDebug(const std::string& str); - protected: - virtual void print(ID id, - const pp::SourceLocation& loc, - const std::string& text); + void print(ID id, const pp::SourceLocation &loc, const std::string &text) override; private: TInfoSink& mInfoSink; diff --git a/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.cpp b/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.cpp index 936c00a56c..ff8a69efa5 100644 --- a/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.cpp +++ b/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.cpp @@ -8,7 +8,8 @@ #include <sstream> -#include "compiler/translator/compilerdebug.h" +#include "angle_gl.h" +#include "common/debug.h" #include "compiler/translator/Diagnostics.h" static TBehavior getBehavior(const std::string& str) @@ -25,13 +26,15 @@ static TBehavior getBehavior(const std::string& str) return EBhUndefined; } -TDirectiveHandler::TDirectiveHandler(TExtensionBehavior& extBehavior, - TDiagnostics& diagnostics, - int& shaderVersion, +TDirectiveHandler::TDirectiveHandler(TExtensionBehavior &extBehavior, + TDiagnostics &diagnostics, + int &shaderVersion, + sh::GLenum shaderType, bool debugShaderPrecisionSupported) : mExtensionBehavior(extBehavior), mDiagnostics(diagnostics), mShaderVersion(shaderVersion), + mShaderType(shaderType), mDebugShaderPrecisionSupported(debugShaderPrecisionSupported) { } @@ -57,7 +60,16 @@ void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc, const char kAll[] = "all"; if (name == kInvariant && value == kAll) + { + if (mShaderVersion == 300 && mShaderType == GL_FRAGMENT_SHADER) + { + // ESSL 3.00.4 section 4.6.1 + mDiagnostics.writeInfo( + pp::Diagnostics::PP_ERROR, loc, + "#pragma STDGL invariant(all) can not be used in fragment shader", name, value); + } mPragma.stdgl.invariantAll = true; + } // The STDGL pragma is used to reserve pragmas for use by future // revisions of GLSL. Do not generate an error on unexpected // name and value. diff --git a/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.h b/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.h index 2a81ee5707..00eb49114e 100644 --- a/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.h +++ b/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.h @@ -11,41 +11,42 @@ #include "compiler/translator/ExtensionBehavior.h" #include "compiler/translator/Pragma.h" #include "compiler/preprocessor/DirectiveHandlerBase.h" +#include "GLSLANG/ShaderLang.h" class TDiagnostics; class TDirectiveHandler : public pp::DirectiveHandler, angle::NonCopyable { public: - TDirectiveHandler(TExtensionBehavior& extBehavior, - TDiagnostics& diagnostics, - int& shaderVersion, + TDirectiveHandler(TExtensionBehavior &extBehavior, + TDiagnostics &diagnostics, + int &shaderVersion, + sh::GLenum shaderType, bool debugShaderPrecisionSupported); - virtual ~TDirectiveHandler(); + ~TDirectiveHandler() override; const TPragma& pragma() const { return mPragma; } const TExtensionBehavior& extensionBehavior() const { return mExtensionBehavior; } - virtual void handleError(const pp::SourceLocation& loc, - const std::string& msg); + void handleError(const pp::SourceLocation &loc, const std::string &msg) override; - virtual void handlePragma(const pp::SourceLocation& loc, - const std::string& name, - const std::string& value, - bool stdgl); + void handlePragma(const pp::SourceLocation &loc, + const std::string &name, + const std::string &value, + bool stdgl) override; - virtual void handleExtension(const pp::SourceLocation& loc, - const std::string& name, - const std::string& behavior); + void handleExtension(const pp::SourceLocation &loc, + const std::string &name, + const std::string &behavior) override; - virtual void handleVersion(const pp::SourceLocation& loc, - int version); + void handleVersion(const pp::SourceLocation &loc, int version) override; private: TPragma mPragma; TExtensionBehavior& mExtensionBehavior; TDiagnostics& mDiagnostics; int& mShaderVersion; + sh::GLenum mShaderType; bool mDebugShaderPrecisionSupported; }; diff --git a/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.cpp b/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.cpp index 697e042954..4a7fa54155 100644 --- a/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.cpp +++ b/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.cpp @@ -179,11 +179,50 @@ const char *getFloatTypeStr(const TType& type) case 1: return "float"; case 2: - return type.getSecondarySize() > 1 ? "mat2" : "vec2"; + switch(type.getSecondarySize()) + { + case 1: + return "vec2"; + case 2: + return "mat2"; + case 3: + return "mat2x3"; + case 4: + return "mat2x4"; + default: + UNREACHABLE(); + return NULL; + } case 3: - return type.getSecondarySize() > 1 ? "mat3" : "vec3"; + switch(type.getSecondarySize()) + { + case 1: + return "vec3"; + case 2: + return "mat3x2"; + case 3: + return "mat3"; + case 4: + return "mat3x4"; + default: + UNREACHABLE(); + return NULL; + } case 4: - return type.getSecondarySize() > 1 ? "mat4" : "vec4"; + switch(type.getSecondarySize()) + { + case 1: + return "vec4"; + case 2: + return "mat4x2"; + case 3: + return "mat4x3"; + case 4: + return "mat4"; + default: + UNREACHABLE(); + return NULL; + } default: UNREACHABLE(); return NULL; @@ -199,8 +238,10 @@ bool canRoundFloat(const TType &type) TIntermAggregate *createInternalFunctionCallNode(TString name, TIntermNode *child) { TIntermAggregate *callNode = new TIntermAggregate(); - callNode->setOp(EOpInternalFunctionCall); - callNode->setName(name); + callNode->setOp(EOpFunctionCall); + TName nameObj(TFunction::mangleName(name)); + nameObj.setInternal(true); + callNode->setNameObj(nameObj); callNode->getSequence()->push_back(child); return callNode; } @@ -252,17 +293,14 @@ bool parentUsesResult(TIntermNode* parent, TIntermNode* node) } // namespace anonymous -EmulatePrecision::EmulatePrecision() - : TIntermTraverser(true, true, true), - mDeclaringVariables(false), - mInLValue(false), - mInFunctionCallOutParameter(false) +EmulatePrecision::EmulatePrecision(const TSymbolTable &symbolTable, int shaderVersion) + : TLValueTrackingTraverser(true, true, true, symbolTable, shaderVersion), + mDeclaringVariables(false) {} void EmulatePrecision::visitSymbol(TIntermSymbol *node) { - if (canRoundFloat(node->getType()) && - !mDeclaringVariables && !mInLValue && !mInFunctionCallOutParameter) + if (canRoundFloat(node->getType()) && !mDeclaringVariables && !isLValueRequiredHere()) { TIntermNode *parent = getParentNode(); TIntermNode *replacement = createRoundingFunctionCallNode(node); @@ -275,14 +313,6 @@ bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node) { bool visitChildren = true; - if (node->isAssignment()) - { - if (visit == PreVisit) - mInLValue = true; - else if (visit == InVisit) - mInLValue = false; - } - TOperator op = node->getOp(); // RHS of initialize is not being declared. @@ -376,22 +406,9 @@ bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node) { case EOpSequence: case EOpConstructStruct: - // No special handling - break; case EOpFunction: - if (visit == PreVisit) - { - const TIntermSequence &sequence = *(node->getSequence()); - TIntermSequence::const_iterator seqIter = sequence.begin(); - TIntermAggregate *params = (*seqIter)->getAsAggregate(); - ASSERT(params != NULL); - ASSERT(params->getOp() == EOpParameters); - mFunctionMap[node->getName()] = params->getSequence(); - } break; case EOpPrototype: - if (visit == PreVisit) - mFunctionMap[node->getName()] = node->getSequence(); visitChildren = false; break; case EOpParameters: @@ -418,50 +435,17 @@ bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node) case EOpFunctionCall: { // Function call. - bool inFunctionMap = (mFunctionMap.find(node->getName()) != mFunctionMap.end()); if (visit == PreVisit) { // User-defined function return values are not rounded, this relies on that // calculations producing the value were rounded. TIntermNode *parent = getParentNode(); - if (canRoundFloat(node->getType()) && !inFunctionMap && parentUsesResult(parent, node)) + if (canRoundFloat(node->getType()) && !isInFunctionMap(node) && + parentUsesResult(parent, node)) { TIntermNode *replacement = createRoundingFunctionCallNode(node); mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true)); } - - if (inFunctionMap) - { - mSeqIterStack.push_back(mFunctionMap[node->getName()]->begin()); - if (mSeqIterStack.back() != mFunctionMap[node->getName()]->end()) - { - TQualifier qualifier = (*mSeqIterStack.back())->getAsTyped()->getQualifier(); - mInFunctionCallOutParameter = (qualifier == EvqOut || qualifier == EvqInOut); - } - } - else - { - // The function is not user-defined - it is likely built-in texture function. - // Assume that those do not have out parameters. - mInFunctionCallOutParameter = false; - } - } - else if (visit == InVisit) - { - if (inFunctionMap) - { - ++mSeqIterStack.back(); - TQualifier qualifier = (*mSeqIterStack.back())->getAsTyped()->getQualifier(); - mInFunctionCallOutParameter = (qualifier == EvqOut || qualifier == EvqInOut); - } - } - else - { - if (inFunctionMap) - { - mSeqIterStack.pop_back(); - mInFunctionCallOutParameter = false; - } } break; } @@ -484,15 +468,10 @@ bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node) case EOpNegative: case EOpVectorLogicalNot: case EOpLogicalNot: - break; case EOpPostIncrement: case EOpPostDecrement: case EOpPreIncrement: case EOpPreDecrement: - if (visit == PreVisit) - mInLValue = true; - else if (visit == PostVisit) - mInLValue = false; break; default: if (canRoundFloat(node->getType()) && visit == PreVisit) @@ -511,7 +490,7 @@ void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase& sink, ShShaderOutput { // Other languages not yet supported ASSERT(outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT || - outputLanguage == SH_GLSL_CORE_OUTPUT || + IsGLSL130OrNewer(outputLanguage) || outputLanguage == SH_ESSL_OUTPUT); writeCommonPrecisionEmulationHelpers(sink, outputLanguage); diff --git a/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.h b/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.h index f1f560aa85..08177b3414 100644 --- a/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.h +++ b/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.h @@ -8,6 +8,7 @@ #define COMPILER_TRANSLATOR_EMULATE_PRECISION_H_ #include "common/angleutils.h" +#include "compiler/translator/Compiler.h" #include "compiler/translator/InfoSink.h" #include "compiler/translator/IntermNode.h" #include "GLSLANG/ShaderLang.h" @@ -17,15 +18,15 @@ // need to write a huge number of variations of the emulated compound assignment // to every translated shader with emulation enabled. -class EmulatePrecision : public TIntermTraverser +class EmulatePrecision : public TLValueTrackingTraverser { public: - EmulatePrecision(); + EmulatePrecision(const TSymbolTable &symbolTable, int shaderVersion); - virtual void visitSymbol(TIntermSymbol *node); - virtual bool visitBinary(Visit visit, TIntermBinary *node); - virtual bool visitUnary(Visit visit, TIntermUnary *node); - virtual bool visitAggregate(Visit visit, TIntermAggregate *node); + void visitSymbol(TIntermSymbol *node) override; + bool visitBinary(Visit visit, TIntermBinary *node) override; + bool visitUnary(Visit visit, TIntermUnary *node) override; + bool visitAggregate(Visit visit, TIntermAggregate *node) override; void writeEmulationHelpers(TInfoSinkBase& sink, ShShaderOutput outputLanguage); @@ -55,20 +56,7 @@ class EmulatePrecision : public TIntermTraverser EmulationSet mEmulateCompoundMul; EmulationSet mEmulateCompoundDiv; - // Stack of function call parameter iterators - std::vector<TIntermSequence::const_iterator> mSeqIterStack; - bool mDeclaringVariables; - bool mInLValue; - bool mInFunctionCallOutParameter; - - struct TStringComparator - { - bool operator() (const TString& a, const TString& b) const { return a.compare(b) < 0; } - }; - - // Map from function names to their parameter sequences - std::map<TString, TIntermSequence*, TStringComparator> mFunctionMap; }; #endif // COMPILER_TRANSLATOR_EMULATE_PRECISION_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.h b/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.h index cf4d7fba31..782c1c9217 100644 --- a/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.h +++ b/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.h @@ -34,4 +34,10 @@ inline const char* getBehaviorString(TBehavior b) // Mapping between extension name and behavior. typedef std::map<std::string, TBehavior> TExtensionBehavior; +inline bool IsExtensionEnabled(const TExtensionBehavior &extBehavior, const char *extension) +{ + auto iter = extBehavior.find(extension); + return iter != extBehavior.end() && (iter->second == EBhEnable || iter->second == EBhRequire); +} + #endif // COMPILER_TRANSLATOR_EXTENSIONBEHAVIOR_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.cpp b/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.cpp new file mode 100644 index 0000000000..d7f45f7eef --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.cpp @@ -0,0 +1,100 @@ +// +// 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. +// +// ExtensionGLSL.cpp: Implements the TExtensionGLSL class that tracks GLSL extension requirements +// of shaders. + +#include "compiler/translator/ExtensionGLSL.h" + +#include "compiler/translator/VersionGLSL.h" + +TExtensionGLSL::TExtensionGLSL(ShShaderOutput output) + : TIntermTraverser(true, false, false), mTargetVersion(ShaderOutputTypeToGLSLVersion(output)) +{ +} + +const std::set<std::string> &TExtensionGLSL::getEnabledExtensions() const +{ + return mEnabledExtensions; +} + +const std::set<std::string> &TExtensionGLSL::getRequiredExtensions() const +{ + return mRequiredExtensions; +} + +bool TExtensionGLSL::visitUnary(Visit, TIntermUnary *node) +{ + checkOperator(node); + + return true; +} + +bool TExtensionGLSL::visitAggregate(Visit, TIntermAggregate *node) +{ + checkOperator(node); + + return true; +} + +void TExtensionGLSL::checkOperator(TIntermOperator *node) +{ + if (mTargetVersion < GLSL_VERSION_130) + { + return; + } + + switch (node->getOp()) + { + case EOpAbs: + break; + + case EOpSign: + break; + + case EOpMix: + break; + + case EOpFloatBitsToInt: + case EOpFloatBitsToUint: + case EOpIntBitsToFloat: + case EOpUintBitsToFloat: + if (mTargetVersion < GLSL_VERSION_330) + { + // Bit conversion functions cannot be emulated. + mRequiredExtensions.insert("GL_ARB_shader_bit_encoding"); + } + break; + + case EOpPackSnorm2x16: + case EOpPackHalf2x16: + case EOpUnpackSnorm2x16: + case EOpUnpackHalf2x16: + if (mTargetVersion < GLSL_VERSION_420) + { + mEnabledExtensions.insert("GL_ARB_shading_language_packing"); + + if (mTargetVersion < GLSL_VERSION_330) + { + // floatBitsToUint and uintBitsToFloat are needed to emulate + // packHalf2x16 and unpackHalf2x16 respectively and cannot be + // emulated themselves. + mRequiredExtensions.insert("GL_ARB_shader_bit_encoding"); + } + } + break; + + case EOpPackUnorm2x16: + case EOpUnpackUnorm2x16: + if (mTargetVersion < GLSL_VERSION_410) + { + mEnabledExtensions.insert("GL_ARB_shading_language_packing"); + } + break; + + default: + break; + } +} diff --git a/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.h b/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.h new file mode 100644 index 0000000000..6bb84d612d --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.h @@ -0,0 +1,39 @@ +// +// 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. +// +// ExtensionGLSL.h: Defines the TExtensionGLSL class that tracks GLSL extension requirements of +// shaders. + +#ifndef COMPILER_TRANSLATOR_EXTENSIONGLSL_H_ +#define COMPILER_TRANSLATOR_EXTENSIONGLSL_H_ + +#include <set> +#include <string> + +#include "compiler/translator/IntermNode.h" + +// Traverses the intermediate tree to determine which GLSL extensions are required +// to support the shader. +class TExtensionGLSL : public TIntermTraverser +{ + public: + TExtensionGLSL(ShShaderOutput output); + + const std::set<std::string> &getEnabledExtensions() const; + const std::set<std::string> &getRequiredExtensions() const; + + bool visitUnary(Visit visit, TIntermUnary *node) override; + bool visitAggregate(Visit visit, TIntermAggregate *node) override; + + private: + void checkOperator(TIntermOperator *node); + + int mTargetVersion; + + std::set<std::string> mEnabledExtensions; + std::set<std::string> mRequiredExtensions; +}; + +#endif // COMPILER_TRANSLATOR_EXTENSIONGLSL_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.h b/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.h index 07b9a72c5c..cfcd775af7 100644 --- a/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.h +++ b/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.h @@ -18,11 +18,17 @@ namespace sh class FlagStd140Structs : public TIntermTraverser { public: + + FlagStd140Structs() + : TIntermTraverser(true, false, false) + { + } + const std::vector<TIntermTyped *> getFlaggedNodes() const { return mFlaggedNodes; } protected: - virtual bool visitBinary(Visit visit, TIntermBinary *binaryNode); - virtual void visitSymbol(TIntermSymbol *symbol); + bool visitBinary(Visit visit, TIntermBinary *binaryNode) override; + void visitSymbol(TIntermSymbol *symbol) override; private: bool isInStd140InterfaceBlock(TIntermTyped *node) const; diff --git a/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.cpp b/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.cpp index f3be20d978..4cc1c26a13 100644 --- a/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.cpp +++ b/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.cpp @@ -6,6 +6,9 @@ #include "compiler/translator/ForLoopUnroll.h" +#include "compiler/translator/ValidateLimitations.h" +#include "angle_gl.h" + bool ForLoopUnrollMarker::visitBinary(Visit, TIntermBinary *node) { if (mUnrollCondition != kSamplerArrayIndex) @@ -38,11 +41,16 @@ bool ForLoopUnrollMarker::visitBinary(Visit, TIntermBinary *node) bool ForLoopUnrollMarker::visitLoop(Visit, TIntermLoop *node) { - if (mUnrollCondition == kIntegerIndex) + bool canBeUnrolled = mHasRunLoopValidation; + if (!mHasRunLoopValidation) + { + canBeUnrolled = ValidateLimitations::IsLimitedForLoop(node); + } + if (mUnrollCondition == kIntegerIndex && canBeUnrolled) { // Check if loop index type is integer. - // This is called after ValidateLimitations pass, so all the calls - // should be valid. See ValidateLimitations::validateForLoopInit(). + // This is called after ValidateLimitations pass, so the loop has the limited form specified + // in ESSL 1.00 appendix A. TIntermSequence *declSeq = node->getInit()->getAsAggregate()->getSequence(); TIntermSymbol *symbol = (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode(); if (symbol->getBasicType() == EbtInt) @@ -50,11 +58,18 @@ bool ForLoopUnrollMarker::visitLoop(Visit, TIntermLoop *node) } TIntermNode *body = node->getBody(); - if (body != NULL) + if (body != nullptr) { - mLoopStack.push(node); - body->traverse(this); - mLoopStack.pop(); + if (canBeUnrolled) + { + mLoopStack.push(node); + body->traverse(this); + mLoopStack.pop(); + } + else + { + body->traverse(this); + } } // The loop is fully processed - no need to visit children. return false; diff --git a/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.h b/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.h index c8787d55a0..9c49ecad33 100644 --- a/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.h +++ b/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.h @@ -24,16 +24,18 @@ class ForLoopUnrollMarker : public TIntermTraverser kSamplerArrayIndex }; - ForLoopUnrollMarker(UnrollCondition condition) - : mUnrollCondition(condition), + ForLoopUnrollMarker(UnrollCondition condition, bool hasRunLoopValidation) + : TIntermTraverser(true, false, false), + mUnrollCondition(condition), mSamplerArrayIndexIsFloatLoopIndex(false), - mVisitSamplerArrayIndexNodeInsideLoop(false) + mVisitSamplerArrayIndexNodeInsideLoop(false), + mHasRunLoopValidation(hasRunLoopValidation) { } - virtual bool visitBinary(Visit, TIntermBinary *node); - virtual bool visitLoop(Visit, TIntermLoop *node); - virtual void visitSymbol(TIntermSymbol *node); + bool visitBinary(Visit, TIntermBinary *node) override; + bool visitLoop(Visit, TIntermLoop *node) override; + void visitSymbol(TIntermSymbol *node) override; bool samplerArrayIndexIsFloatLoopIndex() const { @@ -45,6 +47,7 @@ class ForLoopUnrollMarker : public TIntermTraverser TLoopStack mLoopStack; bool mSamplerArrayIndexIsFloatLoopIndex; bool mVisitSamplerArrayIndexNodeInsideLoop; + bool mHasRunLoopValidation; }; #endif // COMPILER_TRANSLATOR_FORLOOPUNROLL_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/Initialize.cpp b/src/3rdparty/angle/src/compiler/translator/Initialize.cpp index 9e11405758..2f51aada7f 100644 --- a/src/3rdparty/angle/src/compiler/translator/Initialize.cpp +++ b/src/3rdparty/angle/src/compiler/translator/Initialize.cpp @@ -11,25 +11,26 @@ // #include "compiler/translator/Initialize.h" +#include "compiler/translator/Cache.h" #include "compiler/translator/IntermNode.h" #include "angle_gl.h" void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInResources &resources, TSymbolTable &symbolTable) { - TType *float1 = new TType(EbtFloat); - TType *float2 = new TType(EbtFloat, 2); - TType *float3 = new TType(EbtFloat, 3); - TType *float4 = new TType(EbtFloat, 4); - TType *int1 = new TType(EbtInt); - TType *int2 = new TType(EbtInt, 2); - TType *int3 = new TType(EbtInt, 3); - TType *uint1 = new TType(EbtUInt); - TType *bool1 = new TType(EbtBool); - TType *genType = new TType(EbtGenType); - TType *genIType = new TType(EbtGenIType); - TType *genUType = new TType(EbtGenUType); - TType *genBType = new TType(EbtGenBType); + const TType *float1 = TCache::getType(EbtFloat); + const TType *float2 = TCache::getType(EbtFloat, 2); + const TType *float3 = TCache::getType(EbtFloat, 3); + const TType *float4 = TCache::getType(EbtFloat, 4); + const TType *int1 = TCache::getType(EbtInt); + const TType *int2 = TCache::getType(EbtInt, 2); + const TType *int3 = TCache::getType(EbtInt, 3); + const TType *uint1 = TCache::getType(EbtUInt); + const TType *bool1 = TCache::getType(EbtBool); + const TType *genType = TCache::getType(EbtGenType); + const TType *genIType = TCache::getType(EbtGenIType); + const TType *genUType = TCache::getType(EbtGenUType); + const TType *genBType = TCache::getType(EbtGenBType); // // Angle and Trigonometric Functions. @@ -96,19 +97,16 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpClamp, genUType, "clamp", genUType, genUType, genUType); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMix, genType, "mix", genType, genType, float1); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMix, genType, "mix", genType, genType, genType); + symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMix, genType, "mix", genType, genType, genBType); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpStep, genType, "step", genType, genType); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpStep, genType, "step", float1, genType); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpSmoothStep, genType, "smoothstep", genType, genType, genType); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpSmoothStep, genType, "smoothstep", float1, float1, genType); - TType *outFloat1 = new TType(EbtFloat); - TType *outFloat2 = new TType(EbtFloat, 2); - TType *outFloat3 = new TType(EbtFloat, 3); - TType *outFloat4 = new TType(EbtFloat, 4); - outFloat1->setQualifier(EvqOut); - outFloat2->setQualifier(EvqOut); - outFloat3->setQualifier(EvqOut); - outFloat4->setQualifier(EvqOut); + const TType *outFloat1 = TCache::getType(EbtFloat, EvqOut); + const TType *outFloat2 = TCache::getType(EbtFloat, EvqOut, 2); + const TType *outFloat3 = TCache::getType(EbtFloat, EvqOut, 3); + const TType *outFloat4 = TCache::getType(EbtFloat, EvqOut, 4); symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpModf, float1, "modf", float1, outFloat1); symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpModf, float2, "modf", float2, outFloat2); @@ -141,15 +139,15 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpReflect, genType, "reflect", genType, genType); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpRefract, genType, "refract", genType, genType, float1); - TType *mat2 = new TType(EbtFloat, 2, 2); - TType *mat3 = new TType(EbtFloat, 3, 3); - TType *mat4 = new TType(EbtFloat, 4, 4); - TType *mat2x3 = new TType(EbtFloat, 2, 3); - TType *mat3x2 = new TType(EbtFloat, 3, 2); - TType *mat2x4 = new TType(EbtFloat, 2, 4); - TType *mat4x2 = new TType(EbtFloat, 4, 2); - TType *mat3x4 = new TType(EbtFloat, 3, 4); - TType *mat4x3 = new TType(EbtFloat, 4, 3); + const TType *mat2 = TCache::getType(EbtFloat, 2, 2); + const TType *mat3 = TCache::getType(EbtFloat, 3, 3); + const TType *mat4 = TCache::getType(EbtFloat, 4, 4); + const TType *mat2x3 = TCache::getType(EbtFloat, 2, 3); + const TType *mat3x2 = TCache::getType(EbtFloat, 3, 2); + const TType *mat2x4 = TCache::getType(EbtFloat, 2, 4); + const TType *mat4x2 = TCache::getType(EbtFloat, 4, 2); + const TType *mat3x4 = TCache::getType(EbtFloat, 3, 4); + const TType *mat4x3 = TCache::getType(EbtFloat, 4, 3); // // Matrix Functions. @@ -192,10 +190,10 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpInverse, mat3, "inverse", mat3); symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpInverse, mat4, "inverse", mat4); - TType *vec = new TType(EbtVec); - TType *ivec = new TType(EbtIVec); - TType *uvec = new TType(EbtUVec); - TType *bvec = new TType(EbtBVec); + const TType *vec = TCache::getType(EbtVec); + const TType *ivec = TCache::getType(EbtIVec); + const TType *uvec = TCache::getType(EbtUVec); + const TType *bvec = TCache::getType(EbtBVec); // // Vector relational functions. @@ -224,8 +222,8 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAll, bool1, "all", bvec); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorLogicalNot, bvec, "not", bvec); - TType *sampler2D = new TType(EbtSampler2D); - TType *samplerCube = new TType(EbtSamplerCube); + const TType *sampler2D = TCache::getType(EbtSampler2D); + const TType *samplerCube = TCache::getType(EbtSamplerCube); // // Texture Functions for GLSL ES 1.0 @@ -237,7 +235,7 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR if (resources.OES_EGL_image_external) { - TType *samplerExternalOES = new TType(EbtSamplerExternalOES); + const TType *samplerExternalOES = TCache::getType(EbtSamplerExternalOES); symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2D", samplerExternalOES, float2); symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", samplerExternalOES, float3); @@ -246,7 +244,7 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR if (resources.ARB_texture_rectangle) { - TType *sampler2DRect = new TType(EbtSampler2DRect); + const TType *sampler2DRect = TCache::getType(EbtSampler2DRect); symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DRect", sampler2DRect, float2); symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DRectProj", sampler2DRect, float3); @@ -295,12 +293,12 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "textureCubeLod", samplerCube, float3, float1); } - TType *gvec4 = new TType(EbtGVec4); + const TType *gvec4 = TCache::getType(EbtGVec4); - TType *gsampler2D = new TType(EbtGSampler2D); - TType *gsamplerCube = new TType(EbtGSamplerCube); - TType *gsampler3D = new TType(EbtGSampler3D); - TType *gsampler2DArray = new TType(EbtGSampler2DArray); + const TType *gsampler2D = TCache::getType(EbtGSampler2D); + const TType *gsamplerCube = TCache::getType(EbtGSamplerCube); + const TType *gsampler3D = TCache::getType(EbtGSampler3D); + const TType *gsampler2DArray = TCache::getType(EbtGSampler2DArray); // // Texture Functions for GLSL ES 3.0 @@ -328,9 +326,9 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProj", gsampler3D, float4, float1); } - TType *sampler2DShadow = new TType(EbtSampler2DShadow); - TType *samplerCubeShadow = new TType(EbtSamplerCubeShadow); - TType *sampler2DArrayShadow = new TType(EbtSampler2DArrayShadow); + const TType *sampler2DShadow = TCache::getType(EbtSampler2DShadow); + const TType *samplerCubeShadow = TCache::getType(EbtSamplerCubeShadow); + const TType *sampler2DArrayShadow = TCache::getType(EbtSampler2DArrayShadow); symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", sampler2DShadow, float3); symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", samplerCubeShadow, float4); @@ -466,6 +464,12 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR if (spec != SH_CSS_SHADERS_SPEC) { symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxDrawBuffers", resources.MaxDrawBuffers); + if (resources.EXT_blend_func_extended) + { + symbolTable.insertConstIntExt(COMMON_BUILTINS, "GL_EXT_blend_func_extended", + "gl_MaxDualSourceDrawBuffersEXT", + resources.MaxDualSourceDrawBuffers); + } } symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxVertexOutputVectors", resources.MaxVertexOutputVectors); @@ -504,12 +508,33 @@ void IdentifyBuiltIns(sh::GLenum type, ShShaderSpec spec, fragData.setArraySize(resources.MaxDrawBuffers); symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragData"), fragData)); + if (resources.EXT_blend_func_extended) + { + symbolTable.insert( + ESSL1_BUILTINS, "GL_EXT_blend_func_extended", + new TVariable(NewPoolTString("gl_SecondaryFragColorEXT"), + TType(EbtFloat, EbpMedium, EvqSecondaryFragColorEXT, 4))); + TType secondaryFragData(EbtFloat, EbpMedium, EvqSecondaryFragDataEXT, 4, 1, true); + secondaryFragData.setArraySize(resources.MaxDualSourceDrawBuffers); + symbolTable.insert( + ESSL1_BUILTINS, "GL_EXT_blend_func_extended", + new TVariable(NewPoolTString("gl_SecondaryFragDataEXT"), secondaryFragData)); + } + if (resources.EXT_frag_depth) { - symbolTable.insert(ESSL1_BUILTINS, "GL_EXT_frag_depth", new TVariable(NewPoolTString("gl_FragDepthEXT"), - TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium, EvqFragDepth, 1))); + symbolTable.insert( + ESSL1_BUILTINS, "GL_EXT_frag_depth", + new TVariable( + NewPoolTString("gl_FragDepthEXT"), + TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium, + EvqFragDepthEXT, 1))); } + symbolTable.insert(ESSL3_BUILTINS, + new TVariable(NewPoolTString("gl_FragDepth"), + TType(EbtFloat, EbpHigh, EvqFragDepth, 1))); + if (resources.EXT_shader_framebuffer_fetch || resources.NV_shader_framebuffer_fetch) { TType lastFragData(EbtFloat, EbpMedium, EvqLastFragData, 4, 1, true); @@ -569,6 +594,8 @@ void InitExtensionBehavior(const ShBuiltInResources& resources, extBehavior["GL_OES_EGL_image_external"] = EBhUndefined; if (resources.ARB_texture_rectangle) extBehavior["GL_ARB_texture_rectangle"] = EBhUndefined; + if (resources.EXT_blend_func_extended) + extBehavior["GL_EXT_blend_func_extended"] = EBhUndefined; if (resources.EXT_draw_buffers) extBehavior["GL_EXT_draw_buffers"] = EBhUndefined; if (resources.EXT_frag_depth) diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeDll.cpp b/src/3rdparty/angle/src/compiler/translator/InitializeDll.cpp index c98430662a..713965389f 100644 --- a/src/3rdparty/angle/src/compiler/translator/InitializeDll.cpp +++ b/src/3rdparty/angle/src/compiler/translator/InitializeDll.cpp @@ -4,6 +4,7 @@ // found in the LICENSE file. // +#include "compiler/translator/Cache.h" #include "compiler/translator/InitializeDll.h" #include "compiler/translator/InitializeGlobals.h" #include "compiler/translator/InitializeParseContext.h" @@ -24,6 +25,8 @@ bool InitProcess() return false; } + TCache::initialize(); + return true; } @@ -31,4 +34,5 @@ void DetachProcess() { FreeParseContextIndex(); FreePoolIndex(); + TCache::destroy(); } diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeParseContext.h b/src/3rdparty/angle/src/compiler/translator/InitializeParseContext.h index fa9b885e80..70dac702e2 100644 --- a/src/3rdparty/angle/src/compiler/translator/InitializeParseContext.h +++ b/src/3rdparty/angle/src/compiler/translator/InitializeParseContext.h @@ -10,7 +10,7 @@ bool InitializeParseContextIndex(); void FreeParseContextIndex(); -struct TParseContext; +class TParseContext; extern void SetGlobalParseContext(TParseContext* context); extern TParseContext* GetGlobalParseContext(); diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeVariables.cpp b/src/3rdparty/angle/src/compiler/translator/InitializeVariables.cpp index 0e3e2ebe55..86d3e6bc83 100644 --- a/src/3rdparty/angle/src/compiler/translator/InitializeVariables.cpp +++ b/src/3rdparty/angle/src/compiler/translator/InitializeVariables.cpp @@ -5,7 +5,8 @@ // #include "compiler/translator/InitializeVariables.h" -#include "compiler/translator/compilerdebug.h" + +#include "common/debug.h" namespace { @@ -13,10 +14,10 @@ namespace TIntermConstantUnion *constructFloatConstUnionNode(const TType &type) { TType myType = type; - unsigned char size = myType.getNominalSize(); + unsigned char size = static_cast<unsigned char>(myType.getNominalSize()); if (myType.isMatrix()) size *= size; - ConstantUnion *u = new ConstantUnion[size]; + TConstantUnion *u = new TConstantUnion[size]; for (int ii = 0; ii < size; ++ii) u[ii].setFConst(0.0f); @@ -28,7 +29,7 @@ TIntermConstantUnion *constructFloatConstUnionNode(const TType &type) TIntermConstantUnion *constructIndexNode(int index) { - ConstantUnion *u = new ConstantUnion[1]; + TConstantUnion *u = new TConstantUnion[1]; u[0].setIConst(index); TType type(EbtInt, EbpUndefined, EvqConst, 1); diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeVariables.h b/src/3rdparty/angle/src/compiler/translator/InitializeVariables.h index 4a81266498..2a141ec91c 100644 --- a/src/3rdparty/angle/src/compiler/translator/InitializeVariables.h +++ b/src/3rdparty/angle/src/compiler/translator/InitializeVariables.h @@ -26,19 +26,20 @@ class InitializeVariables : public TIntermTraverser typedef TVector<InitVariableInfo> InitVariableInfoList; InitializeVariables(const InitVariableInfoList &vars) - : mCodeInserted(false), - mVariables(vars) + : TIntermTraverser(true, false, false), + mVariables(vars), + mCodeInserted(false) { } protected: - virtual bool visitBinary(Visit, TIntermBinary *node) { return false; } - virtual bool visitUnary(Visit, TIntermUnary *node) { return false; } - virtual bool visitSelection(Visit, TIntermSelection *node) { return false; } - virtual bool visitLoop(Visit, TIntermLoop *node) { return false; } - virtual bool visitBranch(Visit, TIntermBranch *node) { return false; } + bool visitBinary(Visit, TIntermBinary *node) override { return false; } + bool visitUnary(Visit, TIntermUnary *node) override { return false; } + bool visitSelection(Visit, TIntermSelection *node) override { return false; } + bool visitLoop(Visit, TIntermLoop *node) override { return false; } + bool visitBranch(Visit, TIntermBranch *node) override { return false; } - virtual bool visitAggregate(Visit visit, TIntermAggregate* node); + bool visitAggregate(Visit visit, TIntermAggregate *node) override; private: void insertInitCode(TIntermSequence *sequence); diff --git a/src/3rdparty/angle/src/compiler/translator/IntermNode.cpp b/src/3rdparty/angle/src/compiler/translator/IntermNode.cpp index 266e3c8e3d..2a92860088 100644 --- a/src/3rdparty/angle/src/compiler/translator/IntermNode.cpp +++ b/src/3rdparty/angle/src/compiler/translator/IntermNode.cpp @@ -10,8 +10,13 @@ #include <float.h> #include <limits.h> +#include <math.h> +#include <stdlib.h> #include <algorithm> +#include <vector> +#include "common/mathutil.h" +#include "common/matrix_utils.h" #include "compiler/translator/HashNames.h" #include "compiler/translator/IntermNode.h" #include "compiler/translator/SymbolTable.h" @@ -19,6 +24,10 @@ namespace { +const float kPi = 3.14159265358979323846f; +const float kDegreesToRadiansMultiplier = kPi / 180.0f; +const float kRadiansToDegreesMultiplier = 180.0f / kPi; + TPrecision GetHigherPrecision(TPrecision left, TPrecision right) { return left > right ? left : right; @@ -57,71 +66,107 @@ bool ValidateMultiplication(TOperator op, const TType &left, const TType &right) } } -bool CompareStructure(const TType& leftNodeType, - ConstantUnion *rightUnionArray, - ConstantUnion *leftUnionArray); - -bool CompareStruct(const TType &leftNodeType, - ConstantUnion *rightUnionArray, - ConstantUnion *leftUnionArray) +TConstantUnion *Vectorize(const TConstantUnion &constant, size_t size) { - const TFieldList &fields = leftNodeType.getStruct()->fields(); + TConstantUnion *constUnion = new TConstantUnion[size]; + for (unsigned int i = 0; i < size; ++i) + constUnion[i] = constant; + + return constUnion; +} - size_t structSize = fields.size(); - size_t index = 0; +void UndefinedConstantFoldingError(const TSourceLoc &loc, TOperator op, TBasicType basicType, + TInfoSink &infoSink, TConstantUnion *result) +{ + std::stringstream constantFoldingErrorStream; + constantFoldingErrorStream << "'" << GetOperatorString(op) + << "' operation result is undefined for the values passed in"; + infoSink.info.message(EPrefixWarning, loc, constantFoldingErrorStream.str().c_str()); - for (size_t j = 0; j < structSize; j++) + switch (basicType) { - size_t size = fields[j]->type()->getObjectSize(); - for (size_t i = 0; i < size; i++) - { - if (fields[j]->type()->getBasicType() == EbtStruct) - { - if (!CompareStructure(*fields[j]->type(), - &rightUnionArray[index], - &leftUnionArray[index])) - { - return false; - } - } - else - { - if (leftUnionArray[index] != rightUnionArray[index]) - return false; - index++; - } - } + case EbtFloat : + result->setFConst(0.0f); + break; + case EbtInt: + result->setIConst(0); + break; + case EbtUInt: + result->setUConst(0u); + break; + case EbtBool: + result->setBConst(false); + break; + default: + break; } - return true; } -bool CompareStructure(const TType &leftNodeType, - ConstantUnion *rightUnionArray, - ConstantUnion *leftUnionArray) +float VectorLength(const TConstantUnion *paramArray, size_t paramArraySize) { - if (leftNodeType.isArray()) + float result = 0.0f; + for (size_t i = 0; i < paramArraySize; i++) { - TType typeWithoutArrayness = leftNodeType; - typeWithoutArrayness.clearArrayness(); + float f = paramArray[i].getFConst(); + result += f * f; + } + return sqrtf(result); +} - size_t arraySize = leftNodeType.getArraySize(); +float VectorDotProduct(const TConstantUnion *paramArray1, + const TConstantUnion *paramArray2, + size_t paramArraySize) +{ + float result = 0.0f; + for (size_t i = 0; i < paramArraySize; i++) + result += paramArray1[i].getFConst() * paramArray2[i].getFConst(); + return result; +} - for (size_t i = 0; i < arraySize; ++i) - { - size_t offset = typeWithoutArrayness.getObjectSize() * i; - if (!CompareStruct(typeWithoutArrayness, - &rightUnionArray[offset], - &leftUnionArray[offset])) - { - return false; - } - } - } - else +TIntermTyped *CreateFoldedNode(TConstantUnion *constArray, + const TIntermTyped *originalNode, + TQualifier qualifier) +{ + if (constArray == nullptr) { - return CompareStruct(leftNodeType, rightUnionArray, leftUnionArray); + return nullptr; } - return true; + TIntermTyped *folded = new TIntermConstantUnion(constArray, originalNode->getType()); + folded->getTypePointer()->setQualifier(qualifier); + folded->setLine(originalNode->getLine()); + return folded; +} + +angle::Matrix<float> GetMatrix(const TConstantUnion *paramArray, + const unsigned int &rows, + const unsigned int &cols) +{ + std::vector<float> elements; + for (size_t i = 0; i < rows * cols; i++) + elements.push_back(paramArray[i].getFConst()); + // Transpose is used since the Matrix constructor expects arguments in row-major order, + // whereas the paramArray is in column-major order. + return angle::Matrix<float>(elements, rows, cols).transpose(); +} + +angle::Matrix<float> GetMatrix(const TConstantUnion *paramArray, const unsigned int &size) +{ + std::vector<float> elements; + for (size_t i = 0; i < size * size; i++) + elements.push_back(paramArray[i].getFConst()); + // Transpose is used since the Matrix constructor expects arguments in row-major order, + // whereas the paramArray is in column-major order. + return angle::Matrix<float>(elements, size).transpose(); +} + +void SetUnionArrayFromMatrix(const angle::Matrix<float> &m, TConstantUnion *resultArray) +{ + // Transpose is used since the input Matrix is in row-major order, + // whereas the actual result should be in column-major order. + angle::Matrix<float> result = m.transpose(); + std::vector<float> resultElements = result.elements(); + for (size_t i = 0; i < resultElements.size(); i++) + resultArray[i].setFConst(resultElements[i]); } } // namespace anonymous @@ -153,7 +198,7 @@ bool TIntermLoop::replaceChildNode( REPLACE_IF_IS(mInit, TIntermNode, original, replacement); REPLACE_IF_IS(mCond, TIntermTyped, original, replacement); REPLACE_IF_IS(mExpr, TIntermTyped, original, replacement); - REPLACE_IF_IS(mBody, TIntermNode, original, replacement); + REPLACE_IF_IS(mBody, TIntermAggregate, original, replacement); return false; } @@ -189,8 +234,47 @@ bool TIntermAggregate::replaceChildNode( return false; } +bool TIntermAggregate::replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements) +{ + for (auto it = mSequence.begin(); it < mSequence.end(); ++it) + { + if (*it == original) + { + it = mSequence.erase(it); + mSequence.insert(it, replacements.begin(), replacements.end()); + return true; + } + } + return false; +} + +bool TIntermAggregate::insertChildNodes(TIntermSequence::size_type position, TIntermSequence insertions) +{ + if (position > mSequence.size()) + { + return false; + } + auto it = mSequence.begin() + position; + mSequence.insert(it, insertions.begin(), insertions.end()); + return true; +} + +bool TIntermAggregate::areChildrenConstQualified() +{ + for (TIntermNode *&child : mSequence) + { + TIntermTyped *typed = child->getAsTyped(); + if (typed && typed->getQualifier() != EvqConst) + { + return false; + } + } + return true; +} + void TIntermAggregate::setPrecisionFromChildren() { + mGotPrecisionFromChildren = true; if (getBasicType() == EbtBool) { mType.setPrecision(EbpUndefined); @@ -229,7 +313,7 @@ void TIntermAggregate::setBuiltInFunctionPrecision() } // ESSL 3.0 spec section 8: textureSize always gets highp precision. // All other functions that take a sampler are assumed to be texture functions. - if (mName.find("textureSize") == 0) + if (mName.getString().find("textureSize") == 0) mType.setPrecision(EbpHigh); else mType.setPrecision(precision); @@ -259,6 +343,70 @@ bool TIntermCase::replaceChildNode( return false; } +TIntermTyped::TIntermTyped(const TIntermTyped &node) : TIntermNode(), mType(node.mType) +{ + // Copy constructor is disallowed for TIntermNode in order to disallow it for subclasses that + // don't explicitly allow it, so normal TIntermNode constructor is used to construct the copy. + // We need to manually copy any fields of TIntermNode besides handling fields in TIntermTyped. + mLine = node.mLine; +} + +TIntermConstantUnion::TIntermConstantUnion(const TIntermConstantUnion &node) : TIntermTyped(node) +{ + mUnionArrayPointer = node.mUnionArrayPointer; +} + +TIntermAggregate::TIntermAggregate(const TIntermAggregate &node) + : TIntermOperator(node), + mName(node.mName), + mUserDefined(node.mUserDefined), + mFunctionId(node.mFunctionId), + mUseEmulatedFunction(node.mUseEmulatedFunction), + mGotPrecisionFromChildren(node.mGotPrecisionFromChildren) +{ + for (TIntermNode *child : node.mSequence) + { + TIntermTyped *typedChild = child->getAsTyped(); + ASSERT(typedChild != nullptr); + TIntermTyped *childCopy = typedChild->deepCopy(); + mSequence.push_back(childCopy); + } +} + +TIntermBinary::TIntermBinary(const TIntermBinary &node) + : TIntermOperator(node), mAddIndexClamp(node.mAddIndexClamp) +{ + TIntermTyped *leftCopy = node.mLeft->deepCopy(); + TIntermTyped *rightCopy = node.mRight->deepCopy(); + ASSERT(leftCopy != nullptr && rightCopy != nullptr); + mLeft = leftCopy; + mRight = rightCopy; +} + +TIntermUnary::TIntermUnary(const TIntermUnary &node) + : TIntermOperator(node), mUseEmulatedFunction(node.mUseEmulatedFunction) +{ + TIntermTyped *operandCopy = node.mOperand->deepCopy(); + ASSERT(operandCopy != nullptr); + mOperand = operandCopy; +} + +TIntermSelection::TIntermSelection(const TIntermSelection &node) : TIntermTyped(node) +{ + // Only supported for ternary nodes, not if statements. + TIntermTyped *trueTyped = node.mTrueBlock->getAsTyped(); + TIntermTyped *falseTyped = node.mFalseBlock->getAsTyped(); + ASSERT(trueTyped != nullptr); + ASSERT(falseTyped != nullptr); + TIntermTyped *conditionCopy = node.mCondition->deepCopy(); + TIntermTyped *trueCopy = trueTyped->deepCopy(); + TIntermTyped *falseCopy = falseTyped->deepCopy(); + ASSERT(conditionCopy != nullptr && trueCopy != nullptr && falseCopy != nullptr); + mCondition = conditionCopy; + mTrueBlock = trueCopy; + mFalseBlock = falseCopy; +} + // // Say whether or not an operation node changes the value of a variable. // @@ -291,6 +439,22 @@ bool TIntermOperator::isAssignment() const } } +bool TIntermOperator::isMultiplication() const +{ + switch (mOp) + { + case EOpMul: + case EOpMatrixTimesMatrix: + case EOpMatrixTimesVector: + case EOpMatrixTimesScalar: + case EOpVectorTimesMatrix: + case EOpVectorTimesScalar: + return true; + default: + return false; + } +} + // // returns true if the operator is for one of the constructors // @@ -302,7 +466,13 @@ bool TIntermOperator::isConstructor() const case EOpConstructVec3: case EOpConstructVec4: case EOpConstructMat2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: case EOpConstructMat3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: case EOpConstructMat4: case EOpConstructFloat: case EOpConstructIVec2: @@ -364,7 +534,10 @@ void TIntermUnary::promote(const TType *funcReturnType) } } - mType.setQualifier(EvqTemporary); + if (mOperand->getQualifier() == EvqConst) + mType.setQualifier(EvqConst); + else + mType.setQualifier(EvqTemporary); } // @@ -389,10 +562,12 @@ bool TIntermBinary::promote(TInfoSink &infoSink) mLeft->getPrecision(), mRight->getPrecision()); getTypePointer()->setPrecision(higherPrecision); + TQualifier resultQualifier = EvqConst; // Binary operations results in temporary variables unless both // operands are const. if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst) { + resultQualifier = EvqTemporary; getTypePointer()->setQualifier(EvqTemporary); } @@ -447,14 +622,15 @@ bool TIntermBinary::promote(TInfoSink &infoSink) if (mLeft->isVector()) { mOp = EOpVectorTimesMatrix; - setType(TType(basicType, higherPrecision, EvqTemporary, - mRight->getCols(), 1)); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast<unsigned char>(mRight->getCols()), 1)); } else { mOp = EOpMatrixTimesScalar; - setType(TType(basicType, higherPrecision, EvqTemporary, - mRight->getCols(), mRight->getRows())); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast<unsigned char>(mRight->getCols()), + static_cast<unsigned char>(mRight->getRows()))); } } else if (mLeft->isMatrix() && !mRight->isMatrix()) @@ -462,8 +638,8 @@ bool TIntermBinary::promote(TInfoSink &infoSink) if (mRight->isVector()) { mOp = EOpMatrixTimesVector; - setType(TType(basicType, higherPrecision, EvqTemporary, - mLeft->getRows(), 1)); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast<unsigned char>(mLeft->getRows()), 1)); } else { @@ -473,8 +649,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink) else if (mLeft->isMatrix() && mRight->isMatrix()) { mOp = EOpMatrixTimesMatrix; - setType(TType(basicType, higherPrecision, EvqTemporary, - mRight->getCols(), mLeft->getRows())); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast<unsigned char>(mRight->getCols()), + static_cast<unsigned char>(mLeft->getRows()))); } else if (!mLeft->isMatrix() && !mRight->isMatrix()) { @@ -485,8 +662,8 @@ bool TIntermBinary::promote(TInfoSink &infoSink) else if (mLeft->isVector() || mRight->isVector()) { mOp = EOpVectorTimesScalar; - setType(TType(basicType, higherPrecision, EvqTemporary, - nominalSize, 1)); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast<unsigned char>(nominalSize), 1)); } } else @@ -528,8 +705,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink) else if (mLeft->isMatrix() && mRight->isMatrix()) { mOp = EOpMatrixTimesMatrixAssign; - setType(TType(basicType, higherPrecision, EvqTemporary, - mRight->getCols(), mLeft->getRows())); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast<unsigned char>(mRight->getCols()), + static_cast<unsigned char>(mLeft->getRows()))); } else if (!mLeft->isMatrix() && !mRight->isMatrix()) { @@ -542,8 +720,8 @@ bool TIntermBinary::promote(TInfoSink &infoSink) if (!mLeft->isVector()) return false; mOp = EOpVectorTimesScalarAssign; - setType(TType(basicType, higherPrecision, EvqTemporary, - mLeft->getNominalSize(), 1)); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast<unsigned char>(mLeft->getNominalSize()), 1)); } } else @@ -612,8 +790,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink) { const int secondarySize = std::max( mLeft->getSecondarySize(), mRight->getSecondarySize()); - setType(TType(basicType, higherPrecision, EvqTemporary, - nominalSize, secondarySize)); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast<unsigned char>(nominalSize), + static_cast<unsigned char>(secondarySize))); if (mLeft->isArray()) { ASSERT(mLeft->getArraySize() == mRight->getArraySize()); @@ -639,560 +818,1778 @@ bool TIntermBinary::promote(TInfoSink &infoSink) return true; } +TIntermTyped *TIntermBinary::fold(TInfoSink &infoSink) +{ + TIntermConstantUnion *leftConstant = mLeft->getAsConstantUnion(); + TIntermConstantUnion *rightConstant = mRight->getAsConstantUnion(); + if (leftConstant == nullptr || rightConstant == nullptr) + { + return nullptr; + } + TConstantUnion *constArray = leftConstant->foldBinary(mOp, rightConstant, infoSink); + + // Nodes may be constant folded without being qualified as constant. + TQualifier resultQualifier = EvqConst; + if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst) + { + resultQualifier = EvqTemporary; + } + return CreateFoldedNode(constArray, this, resultQualifier); +} + +TIntermTyped *TIntermUnary::fold(TInfoSink &infoSink) +{ + TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion(); + if (operandConstant == nullptr) + { + return nullptr; + } + + TConstantUnion *constArray = nullptr; + switch (mOp) + { + case EOpAny: + case EOpAll: + case EOpLength: + case EOpTranspose: + case EOpDeterminant: + case EOpInverse: + case EOpPackSnorm2x16: + case EOpUnpackSnorm2x16: + case EOpPackUnorm2x16: + case EOpUnpackUnorm2x16: + case EOpPackHalf2x16: + case EOpUnpackHalf2x16: + constArray = operandConstant->foldUnaryWithDifferentReturnType(mOp, infoSink); + break; + default: + constArray = operandConstant->foldUnaryWithSameReturnType(mOp, infoSink); + break; + } + + // Nodes may be constant folded without being qualified as constant. + TQualifier resultQualifier = mOperand->getQualifier() == EvqConst ? EvqConst : EvqTemporary; + return CreateFoldedNode(constArray, this, resultQualifier); +} + +TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink) +{ + // Make sure that all params are constant before actual constant folding. + for (auto *param : *getSequence()) + { + if (param->getAsConstantUnion() == nullptr) + { + return nullptr; + } + } + TConstantUnion *constArray = nullptr; + if (isConstructor()) + constArray = TIntermConstantUnion::FoldAggregateConstructor(this, infoSink); + else + constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, infoSink); + + // Nodes may be constant folded without being qualified as constant. + TQualifier resultQualifier = areChildrenConstQualified() ? EvqConst : EvqTemporary; + return CreateFoldedNode(constArray, this, resultQualifier); +} + // // The fold functions see if an operation on a constant can be done in place, // without generating run-time code. // -// Returns the node to keep using, which may or may not be the node passed in. +// Returns the constant value to keep using or nullptr. // -TIntermTyped *TIntermConstantUnion::fold( - TOperator op, TIntermTyped *constantNode, TInfoSink &infoSink) +TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op, TIntermConstantUnion *rightNode, TInfoSink &infoSink) { - ConstantUnion *unionArray = getUnionArrayPointer(); + const TConstantUnion *leftArray = getUnionArrayPointer(); + const TConstantUnion *rightArray = rightNode->getUnionArrayPointer(); - if (!unionArray) - return NULL; + if (!leftArray) + return nullptr; + if (!rightArray) + return nullptr; size_t objectSize = getType().getObjectSize(); - if (constantNode) + // for a case like float f = vec4(2, 3, 4, 5) + 1.2; + if (rightNode->getType().getObjectSize() == 1 && objectSize > 1) + { + rightArray = Vectorize(*rightNode->getUnionArrayPointer(), objectSize); + } + else if (rightNode->getType().getObjectSize() > 1 && objectSize == 1) { - // binary operations - TIntermConstantUnion *node = constantNode->getAsConstantUnion(); - ConstantUnion *rightUnionArray = node->getUnionArrayPointer(); - TType returnType = getType(); + // for a case like float f = 1.2 + vec4(2, 3, 4, 5); + leftArray = Vectorize(*getUnionArrayPointer(), rightNode->getType().getObjectSize()); + objectSize = rightNode->getType().getObjectSize(); + } - if (!rightUnionArray) - return NULL; + TConstantUnion *resultArray = nullptr; - // for a case like float f = 1.2 + vec4(2,3,4,5); - if (constantNode->getType().getObjectSize() == 1 && objectSize > 1) - { - rightUnionArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; ++i) - { - rightUnionArray[i] = *node->getUnionArrayPointer(); - } - returnType = getType(); - } - else if (constantNode->getType().getObjectSize() > 1 && objectSize == 1) - { - // for a case like float f = vec4(2,3,4,5) + 1.2; - unionArray = new ConstantUnion[constantNode->getType().getObjectSize()]; - for (size_t i = 0; i < constantNode->getType().getObjectSize(); ++i) - { - unionArray[i] = *getUnionArrayPointer(); - } - returnType = node->getType(); - objectSize = constantNode->getType().getObjectSize(); - } + switch(op) + { + case EOpAdd: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] + rightArray[i]; + break; + case EOpSub: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] - rightArray[i]; + break; - ConstantUnion *tempConstArray = NULL; - TIntermConstantUnion *tempNode; + case EOpMul: + case EOpVectorTimesScalar: + case EOpMatrixTimesScalar: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] * rightArray[i]; + break; - bool boolNodeFlag = false; - switch(op) + case EOpMatrixTimesMatrix: { - case EOpAdd: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] + rightUnionArray[i]; - break; - case EOpSub: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] - rightUnionArray[i]; - break; - - case EOpMul: - case EOpVectorTimesScalar: - case EOpMatrixTimesScalar: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] * rightUnionArray[i]; - break; - - case EOpMatrixTimesMatrix: + if (getType().getBasicType() != EbtFloat || + rightNode->getBasicType() != EbtFloat) { - if (getType().getBasicType() != EbtFloat || - node->getBasicType() != EbtFloat) - { - infoSink.info.message( - EPrefixInternalError, getLine(), - "Constant Folding cannot be done for matrix multiply"); - return NULL; - } + infoSink.info.message( + EPrefixInternalError, getLine(), + "Constant Folding cannot be done for matrix multiply"); + return nullptr; + } - const int leftCols = getCols(); - const int leftRows = getRows(); - const int rightCols = constantNode->getType().getCols(); - const int rightRows = constantNode->getType().getRows(); - const int resultCols = rightCols; - const int resultRows = leftRows; + const int leftCols = getCols(); + const int leftRows = getRows(); + const int rightCols = rightNode->getType().getCols(); + const int rightRows = rightNode->getType().getRows(); + const int resultCols = rightCols; + const int resultRows = leftRows; - tempConstArray = new ConstantUnion[resultCols*resultRows]; - for (int row = 0; row < resultRows; row++) + resultArray = new TConstantUnion[resultCols * resultRows]; + for (int row = 0; row < resultRows; row++) + { + for (int column = 0; column < resultCols; column++) { - for (int column = 0; column < resultCols; column++) + resultArray[resultRows * column + row].setFConst(0.0f); + for (int i = 0; i < leftCols; i++) { - tempConstArray[resultRows * column + row].setFConst(0.0f); - for (int i = 0; i < leftCols; i++) - { - tempConstArray[resultRows * column + row].setFConst( - tempConstArray[resultRows * column + row].getFConst() + - unionArray[i * leftRows + row].getFConst() * - rightUnionArray[column * rightRows + i].getFConst()); - } + resultArray[resultRows * column + row].setFConst( + resultArray[resultRows * column + row].getFConst() + + leftArray[i * leftRows + row].getFConst() * + rightArray[column * rightRows + i].getFConst()); } } - - // update return type for matrix product - returnType.setPrimarySize(resultCols); - returnType.setSecondarySize(resultRows); } - break; + } + break; - case EOpDiv: - case EOpIMod: + case EOpDiv: + case EOpIMod: + { + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) { - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) + switch (getType().getBasicType()) { - switch (getType().getBasicType()) + case EbtFloat: + if (rightArray[i] == 0.0f) { - case EbtFloat: - if (rightUnionArray[i] == 0.0f) - { - infoSink.info.message( - EPrefixWarning, getLine(), - "Divide by zero error during constant folding"); - tempConstArray[i].setFConst( - unionArray[i].getFConst() < 0 ? -FLT_MAX : FLT_MAX); - } - else - { - ASSERT(op == EOpDiv); - tempConstArray[i].setFConst( - unionArray[i].getFConst() / - rightUnionArray[i].getFConst()); - } - break; + infoSink.info.message(EPrefixWarning, getLine(), + "Divide by zero error during constant folding"); + resultArray[i].setFConst(leftArray[i].getFConst() < 0 ? -FLT_MAX : FLT_MAX); + } + else + { + ASSERT(op == EOpDiv); + resultArray[i].setFConst(leftArray[i].getFConst() / rightArray[i].getFConst()); + } + break; - case EbtInt: - if (rightUnionArray[i] == 0) + case EbtInt: + if (rightArray[i] == 0) + { + infoSink.info.message(EPrefixWarning, getLine(), + "Divide by zero error during constant folding"); + resultArray[i].setIConst(INT_MAX); + } + else + { + if (op == EOpDiv) { - infoSink.info.message( - EPrefixWarning, getLine(), - "Divide by zero error during constant folding"); - tempConstArray[i].setIConst(INT_MAX); + resultArray[i].setIConst(leftArray[i].getIConst() / rightArray[i].getIConst()); } else { - if (op == EOpDiv) - { - tempConstArray[i].setIConst( - unionArray[i].getIConst() / - rightUnionArray[i].getIConst()); - } - else - { - ASSERT(op == EOpIMod); - tempConstArray[i].setIConst( - unionArray[i].getIConst() % - rightUnionArray[i].getIConst()); - } + ASSERT(op == EOpIMod); + resultArray[i].setIConst(leftArray[i].getIConst() % rightArray[i].getIConst()); } - break; + } + break; - case EbtUInt: - if (rightUnionArray[i] == 0) + case EbtUInt: + if (rightArray[i] == 0) + { + infoSink.info.message(EPrefixWarning, getLine(), + "Divide by zero error during constant folding"); + resultArray[i].setUConst(UINT_MAX); + } + else + { + if (op == EOpDiv) { - infoSink.info.message( - EPrefixWarning, getLine(), - "Divide by zero error during constant folding"); - tempConstArray[i].setUConst(UINT_MAX); + resultArray[i].setUConst(leftArray[i].getUConst() / rightArray[i].getUConst()); } else { - if (op == EOpDiv) - { - tempConstArray[i].setUConst( - unionArray[i].getUConst() / - rightUnionArray[i].getUConst()); - } - else - { - ASSERT(op == EOpIMod); - tempConstArray[i].setUConst( - unionArray[i].getUConst() % - rightUnionArray[i].getUConst()); - } + ASSERT(op == EOpIMod); + resultArray[i].setUConst(leftArray[i].getUConst() % rightArray[i].getUConst()); } - break; - - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Constant folding cannot be done for \"/\""); - return NULL; } + break; + + default: + infoSink.info.message(EPrefixInternalError, getLine(), + "Constant folding cannot be done for \"/\""); + return nullptr; } } - break; + } + break; + + case EOpMatrixTimesVector: + { + if (rightNode->getBasicType() != EbtFloat) + { + infoSink.info.message(EPrefixInternalError, getLine(), + "Constant Folding cannot be done for matrix times vector"); + return nullptr; + } - case EOpMatrixTimesVector: + const int matrixCols = getCols(); + const int matrixRows = getRows(); + + resultArray = new TConstantUnion[matrixRows]; + + for (int matrixRow = 0; matrixRow < matrixRows; matrixRow++) { - if (node->getBasicType() != EbtFloat) + resultArray[matrixRow].setFConst(0.0f); + for (int col = 0; col < matrixCols; col++) { - infoSink.info.message( - EPrefixInternalError, getLine(), - "Constant Folding cannot be done for matrix times vector"); - return NULL; + resultArray[matrixRow].setFConst(resultArray[matrixRow].getFConst() + + leftArray[col * matrixRows + matrixRow].getFConst() * + rightArray[col].getFConst()); } + } + } + break; + + case EOpVectorTimesMatrix: + { + if (getType().getBasicType() != EbtFloat) + { + infoSink.info.message(EPrefixInternalError, getLine(), + "Constant Folding cannot be done for vector times matrix"); + return nullptr; + } - const int matrixCols = getCols(); - const int matrixRows = getRows(); + const int matrixCols = rightNode->getType().getCols(); + const int matrixRows = rightNode->getType().getRows(); - tempConstArray = new ConstantUnion[matrixRows]; + resultArray = new TConstantUnion[matrixCols]; + for (int matrixCol = 0; matrixCol < matrixCols; matrixCol++) + { + resultArray[matrixCol].setFConst(0.0f); for (int matrixRow = 0; matrixRow < matrixRows; matrixRow++) { - tempConstArray[matrixRow].setFConst(0.0f); - for (int col = 0; col < matrixCols; col++) - { - tempConstArray[matrixRow].setFConst( - tempConstArray[matrixRow].getFConst() + - unionArray[col * matrixRows + matrixRow].getFConst() * - rightUnionArray[col].getFConst()); - } + resultArray[matrixCol].setFConst(resultArray[matrixCol].getFConst() + + leftArray[matrixRow].getFConst() * + rightArray[matrixCol * matrixRows + matrixRow].getFConst()); } + } + } + break; - returnType = node->getType(); - returnType.setPrimarySize(matrixRows); - - tempNode = new TIntermConstantUnion(tempConstArray, returnType); - tempNode->setLine(getLine()); + case EOpLogicalAnd: + { + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + { + resultArray[i] = leftArray[i] && rightArray[i]; + } + } + break; - return tempNode; + case EOpLogicalOr: + { + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + { + resultArray[i] = leftArray[i] || rightArray[i]; } + } + break; - case EOpVectorTimesMatrix: + case EOpLogicalXor: + { + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) { - if (getType().getBasicType() != EbtFloat) + switch (getType().getBasicType()) { - infoSink.info.message( - EPrefixInternalError, getLine(), - "Constant Folding cannot be done for vector times matrix"); - return NULL; + case EbtBool: + resultArray[i].setBConst(leftArray[i] != rightArray[i]); + break; + default: + UNREACHABLE(); + break; } + } + } + break; - const int matrixCols = constantNode->getType().getCols(); - const int matrixRows = constantNode->getType().getRows(); + case EOpBitwiseAnd: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] & rightArray[i]; + break; + case EOpBitwiseXor: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] ^ rightArray[i]; + break; + case EOpBitwiseOr: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] | rightArray[i]; + break; + case EOpBitShiftLeft: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] << rightArray[i]; + break; + case EOpBitShiftRight: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] >> rightArray[i]; + break; + + case EOpLessThan: + ASSERT(objectSize == 1); + resultArray = new TConstantUnion[1]; + resultArray->setBConst(*leftArray < *rightArray); + break; - tempConstArray = new ConstantUnion[matrixCols]; + case EOpGreaterThan: + ASSERT(objectSize == 1); + resultArray = new TConstantUnion[1]; + resultArray->setBConst(*leftArray > *rightArray); + break; - for (int matrixCol = 0; matrixCol < matrixCols; matrixCol++) + case EOpLessThanEqual: + ASSERT(objectSize == 1); + resultArray = new TConstantUnion[1]; + resultArray->setBConst(!(*leftArray > *rightArray)); + break; + + case EOpGreaterThanEqual: + ASSERT(objectSize == 1); + resultArray = new TConstantUnion[1]; + resultArray->setBConst(!(*leftArray < *rightArray)); + break; + + case EOpEqual: + case EOpNotEqual: + { + resultArray = new TConstantUnion[1]; + bool equal = true; + for (size_t i = 0; i < objectSize; i++) + { + if (leftArray[i] != rightArray[i]) { - tempConstArray[matrixCol].setFConst(0.0f); - for (int matrixRow = 0; matrixRow < matrixRows; matrixRow++) - { - tempConstArray[matrixCol].setFConst( - tempConstArray[matrixCol].getFConst() + - unionArray[matrixRow].getFConst() * - rightUnionArray[matrixCol * matrixRows + matrixRow].getFConst()); - } + equal = false; + break; // break out of for loop } + } + if (op == EOpEqual) + { + resultArray->setBConst(equal); + } + else + { + resultArray->setBConst(!equal); + } + } + break; + + default: + infoSink.info.message( + EPrefixInternalError, getLine(), + "Invalid operator for constant folding"); + return nullptr; + } + return resultArray; +} + +// +// The fold functions see if an operation on a constant can be done in place, +// without generating run-time code. +// +// Returns the constant value to keep using or nullptr. +// +TConstantUnion *TIntermConstantUnion::foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink) +{ + // + // Do operations where the return type has a different number of components compared to the operand type. + // + + const TConstantUnion *operandArray = getUnionArrayPointer(); + if (!operandArray) + return nullptr; - returnType.setPrimarySize(matrixCols); + size_t objectSize = getType().getObjectSize(); + TConstantUnion *resultArray = nullptr; + switch (op) + { + case EOpAny: + if (getType().getBasicType() == EbtBool) + { + resultArray = new TConstantUnion(); + resultArray->setBConst(false); + for (size_t i = 0; i < objectSize; i++) + { + if (operandArray[i].getBConst()) + { + resultArray->setBConst(true); + break; + } } break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } - case EOpLogicalAnd: - // this code is written for possible future use, - // will not get executed currently + case EOpAll: + if (getType().getBasicType() == EbtBool) + { + resultArray = new TConstantUnion(); + resultArray->setBConst(true); + for (size_t i = 0; i < objectSize; i++) { - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) + if (!operandArray[i].getBConst()) { - tempConstArray[i] = unionArray[i] && rightUnionArray[i]; + resultArray->setBConst(false); + break; } } break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } - case EOpLogicalOr: + case EOpLength: + if (getType().getBasicType() == EbtFloat) + { + resultArray = new TConstantUnion(); + resultArray->setFConst(VectorLength(operandArray, objectSize)); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpTranspose: + if (getType().getBasicType() == EbtFloat) + { + resultArray = new TConstantUnion[objectSize]; + angle::Matrix<float> result = + GetMatrix(operandArray, getType().getNominalSize(), getType().getSecondarySize()).transpose(); + SetUnionArrayFromMatrix(result, resultArray); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpDeterminant: + if (getType().getBasicType() == EbtFloat) + { + unsigned int size = getType().getNominalSize(); + ASSERT(size >= 2 && size <= 4); + resultArray = new TConstantUnion(); + resultArray->setFConst(GetMatrix(operandArray, size).determinant()); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpInverse: + if (getType().getBasicType() == EbtFloat) + { + unsigned int size = getType().getNominalSize(); + ASSERT(size >= 2 && size <= 4); + resultArray = new TConstantUnion[objectSize]; + angle::Matrix<float> result = GetMatrix(operandArray, size).inverse(); + SetUnionArrayFromMatrix(result, resultArray); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpPackSnorm2x16: + if (getType().getBasicType() == EbtFloat) + { + ASSERT(getType().getNominalSize() == 2); + resultArray = new TConstantUnion(); + resultArray->setUConst(gl::packSnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst())); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpUnpackSnorm2x16: + if (getType().getBasicType() == EbtUInt) + { + resultArray = new TConstantUnion[2]; + float f1, f2; + gl::unpackSnorm2x16(operandArray[0].getUConst(), &f1, &f2); + resultArray[0].setFConst(f1); + resultArray[1].setFConst(f2); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpPackUnorm2x16: + if (getType().getBasicType() == EbtFloat) + { + ASSERT(getType().getNominalSize() == 2); + resultArray = new TConstantUnion(); + resultArray->setUConst(gl::packUnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst())); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpUnpackUnorm2x16: + if (getType().getBasicType() == EbtUInt) + { + resultArray = new TConstantUnion[2]; + float f1, f2; + gl::unpackUnorm2x16(operandArray[0].getUConst(), &f1, &f2); + resultArray[0].setFConst(f1); + resultArray[1].setFConst(f2); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpPackHalf2x16: + if (getType().getBasicType() == EbtFloat) + { + ASSERT(getType().getNominalSize() == 2); + resultArray = new TConstantUnion(); + resultArray->setUConst(gl::packHalf2x16(operandArray[0].getFConst(), operandArray[1].getFConst())); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpUnpackHalf2x16: + if (getType().getBasicType() == EbtUInt) + { + resultArray = new TConstantUnion[2]; + float f1, f2; + gl::unpackHalf2x16(operandArray[0].getUConst(), &f1, &f2); + resultArray[0].setFConst(f1); + resultArray[1].setFConst(f2); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + break; + + default: + break; + } + + return resultArray; +} + +TConstantUnion *TIntermConstantUnion::foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink) +{ + // + // Do unary operations where the return type is the same as operand type. + // + + const TConstantUnion *operandArray = getUnionArrayPointer(); + if (!operandArray) + return nullptr; + + size_t objectSize = getType().getObjectSize(); + + TConstantUnion *resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + { + switch(op) + { + case EOpNegative: + switch (getType().getBasicType()) + { + case EbtFloat: + resultArray[i].setFConst(-operandArray[i].getFConst()); + break; + case EbtInt: + resultArray[i].setIConst(-operandArray[i].getIConst()); + break; + case EbtUInt: + resultArray[i].setUConst(static_cast<unsigned int>( + -static_cast<int>(operandArray[i].getUConst()))); + break; + default: + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + } + break; + + case EOpPositive: + switch (getType().getBasicType()) + { + case EbtFloat: + resultArray[i].setFConst(operandArray[i].getFConst()); + break; + case EbtInt: + resultArray[i].setIConst(operandArray[i].getIConst()); + break; + case EbtUInt: + resultArray[i].setUConst(static_cast<unsigned int>( + static_cast<int>(operandArray[i].getUConst()))); + break; + default: + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + } + break; + + case EOpLogicalNot: // this code is written for possible future use, // will not get executed currently + switch (getType().getBasicType()) { - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - { - tempConstArray[i] = unionArray[i] || rightUnionArray[i]; - } + case EbtBool: + resultArray[i].setBConst(!operandArray[i].getBConst()); + break; + default: + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; } break; - case EOpLogicalXor: + case EOpBitwiseNot: + switch (getType().getBasicType()) + { + case EbtInt: + resultArray[i].setIConst(~operandArray[i].getIConst()); + break; + case EbtUInt: + resultArray[i].setUConst(~operandArray[i].getUConst()); + break; + default: + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + } + break; + + case EOpRadians: + if (getType().getBasicType() == EbtFloat) + { + resultArray[i].setFConst(kDegreesToRadiansMultiplier * operandArray[i].getFConst()); + break; + } + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + + case EOpDegrees: + if (getType().getBasicType() == EbtFloat) { - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) + resultArray[i].setFConst(kRadiansToDegreesMultiplier * operandArray[i].getFConst()); + break; + } + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + + case EOpSin: + if (!foldFloatTypeUnary(operandArray[i], &sinf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpCos: + if (!foldFloatTypeUnary(operandArray[i], &cosf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpTan: + if (!foldFloatTypeUnary(operandArray[i], &tanf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpAsin: + // For asin(x), results are undefined if |x| > 1, we are choosing to set result to 0. + if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) > 1.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &asinf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpAcos: + // For acos(x), results are undefined if |x| > 1, we are choosing to set result to 0. + if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) > 1.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &acosf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpAtan: + if (!foldFloatTypeUnary(operandArray[i], &atanf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpSinh: + if (!foldFloatTypeUnary(operandArray[i], &sinhf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpCosh: + if (!foldFloatTypeUnary(operandArray[i], &coshf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpTanh: + if (!foldFloatTypeUnary(operandArray[i], &tanhf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpAsinh: + if (!foldFloatTypeUnary(operandArray[i], &asinhf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpAcosh: + // For acosh(x), results are undefined if x < 1, we are choosing to set result to 0. + if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() < 1.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &acoshf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpAtanh: + // For atanh(x), results are undefined if |x| >= 1, we are choosing to set result to 0. + if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) >= 1.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &atanhf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpAbs: + switch (getType().getBasicType()) + { + case EbtFloat: + resultArray[i].setFConst(fabsf(operandArray[i].getFConst())); + break; + case EbtInt: + resultArray[i].setIConst(abs(operandArray[i].getIConst())); + break; + default: + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + } + break; + + case EOpSign: + switch (getType().getBasicType()) + { + case EbtFloat: { - switch (getType().getBasicType()) - { - case EbtBool: - tempConstArray[i].setBConst( - unionArray[i] == rightUnionArray[i] ? false : true); - break; - default: - UNREACHABLE(); - break; - } + float fConst = operandArray[i].getFConst(); + float fResult = 0.0f; + if (fConst > 0.0f) + fResult = 1.0f; + else if (fConst < 0.0f) + fResult = -1.0f; + resultArray[i].setFConst(fResult); + } + break; + case EbtInt: + { + int iConst = operandArray[i].getIConst(); + int iResult = 0; + if (iConst > 0) + iResult = 1; + else if (iConst < 0) + iResult = -1; + resultArray[i].setIConst(iResult); } + break; + default: + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; } break; - case EOpBitwiseAnd: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] & rightUnionArray[i]; + case EOpFloor: + if (!foldFloatTypeUnary(operandArray[i], &floorf, infoSink, &resultArray[i])) + return nullptr; break; - case EOpBitwiseXor: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] ^ rightUnionArray[i]; + + case EOpTrunc: + if (!foldFloatTypeUnary(operandArray[i], &truncf, infoSink, &resultArray[i])) + return nullptr; break; - case EOpBitwiseOr: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] | rightUnionArray[i]; + + case EOpRound: + if (!foldFloatTypeUnary(operandArray[i], &roundf, infoSink, &resultArray[i])) + return nullptr; break; - case EOpBitShiftLeft: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] << rightUnionArray[i]; + + case EOpRoundEven: + if (getType().getBasicType() == EbtFloat) + { + float x = operandArray[i].getFConst(); + float result; + float fractPart = modff(x, &result); + if (fabsf(fractPart) == 0.5f) + result = 2.0f * roundf(x / 2.0f); + else + result = roundf(x); + resultArray[i].setFConst(result); + break; + } + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + + case EOpCeil: + if (!foldFloatTypeUnary(operandArray[i], &ceilf, infoSink, &resultArray[i])) + return nullptr; break; - case EOpBitShiftRight: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] >> rightUnionArray[i]; + + case EOpFract: + if (getType().getBasicType() == EbtFloat) + { + float x = operandArray[i].getFConst(); + resultArray[i].setFConst(x - floorf(x)); + break; + } + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + + case EOpIsNan: + if (getType().getBasicType() == EbtFloat) + { + resultArray[i].setBConst(gl::isNaN(operandArray[0].getFConst())); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + case EOpIsInf: + if (getType().getBasicType() == EbtFloat) + { + resultArray[i].setBConst(gl::isInf(operandArray[0].getFConst())); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + case EOpFloatBitsToInt: + if (getType().getBasicType() == EbtFloat) + { + resultArray[i].setIConst(gl::bitCast<int32_t>(operandArray[0].getFConst())); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + case EOpFloatBitsToUint: + if (getType().getBasicType() == EbtFloat) + { + resultArray[i].setUConst(gl::bitCast<uint32_t>(operandArray[0].getFConst())); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + case EOpIntBitsToFloat: + if (getType().getBasicType() == EbtInt) + { + resultArray[i].setFConst(gl::bitCast<float>(operandArray[0].getIConst())); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + case EOpUintBitsToFloat: + if (getType().getBasicType() == EbtUInt) + { + resultArray[i].setFConst(gl::bitCast<float>(operandArray[0].getUConst())); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + case EOpExp: + if (!foldFloatTypeUnary(operandArray[i], &expf, infoSink, &resultArray[i])) + return nullptr; break; - case EOpLessThan: - ASSERT(objectSize == 1); - tempConstArray = new ConstantUnion[1]; - tempConstArray->setBConst(*unionArray < *rightUnionArray); - returnType = TType(EbtBool, EbpUndefined, EvqConst); + case EOpLog: + // For log(x), results are undefined if x <= 0, we are choosing to set result to 0. + if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &logf, infoSink, &resultArray[i])) + return nullptr; break; - case EOpGreaterThan: - ASSERT(objectSize == 1); - tempConstArray = new ConstantUnion[1]; - tempConstArray->setBConst(*unionArray > *rightUnionArray); - returnType = TType(EbtBool, EbpUndefined, EvqConst); + case EOpExp2: + if (!foldFloatTypeUnary(operandArray[i], &exp2f, infoSink, &resultArray[i])) + return nullptr; break; - case EOpLessThanEqual: + case EOpLog2: + // For log2(x), results are undefined if x <= 0, we are choosing to set result to 0. + // And log2f is not available on some plarforms like old android, so just using log(x)/log(2) here. + if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &logf, infoSink, &resultArray[i])) + return nullptr; + else + resultArray[i].setFConst(resultArray[i].getFConst() / logf(2.0f)); + break; + + case EOpSqrt: + // For sqrt(x), results are undefined if x < 0, we are choosing to set result to 0. + if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() < 0.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &sqrtf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpInverseSqrt: + // There is no stdlib built-in function equavalent for GLES built-in inversesqrt(), + // so getting the square root first using builtin function sqrt() and then taking its inverse. + // Also, for inversesqrt(x), results are undefined if x <= 0, we are choosing to set result to 0. + if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &sqrtf, infoSink, &resultArray[i])) + return nullptr; + else + resultArray[i].setFConst(1.0f / resultArray[i].getFConst()); + break; + + case EOpVectorLogicalNot: + if (getType().getBasicType() == EbtBool) { - ASSERT(objectSize == 1); - ConstantUnion constant; - constant.setBConst(*unionArray > *rightUnionArray); - tempConstArray = new ConstantUnion[1]; - tempConstArray->setBConst(!constant.getBConst()); - returnType = TType(EbtBool, EbpUndefined, EvqConst); + resultArray[i].setBConst(!operandArray[i].getBConst()); break; } + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; - case EOpGreaterThanEqual: + case EOpNormalize: + if (getType().getBasicType() == EbtFloat) { - ASSERT(objectSize == 1); - ConstantUnion constant; - constant.setBConst(*unionArray < *rightUnionArray); - tempConstArray = new ConstantUnion[1]; - tempConstArray->setBConst(!constant.getBConst()); - returnType = TType(EbtBool, EbpUndefined, EvqConst); + float x = operandArray[i].getFConst(); + float length = VectorLength(operandArray, objectSize); + if (length) + resultArray[i].setFConst(x / length); + else + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, + &resultArray[i]); break; } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; - case EOpEqual: - if (getType().getBasicType() == EbtStruct) + case EOpDFdx: + case EOpDFdy: + case EOpFwidth: + if (getType().getBasicType() == EbtFloat) { - if (!CompareStructure(node->getType(), - node->getUnionArrayPointer(), - unionArray)) + // Derivatives of constant arguments should be 0. + resultArray[i].setFConst(0.0f); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + default: + return nullptr; + } + } + + return resultArray; +} + +bool TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion ¶meter, FloatTypeUnaryFunc builtinFunc, + TInfoSink &infoSink, TConstantUnion *result) const +{ + ASSERT(builtinFunc); + + if (getType().getBasicType() == EbtFloat) + { + result->setFConst(builtinFunc(parameter.getFConst())); + return true; + } + + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return false; +} + +// static +TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate *aggregate, + TInfoSink &infoSink) +{ + ASSERT(aggregate->getSequence()->size() > 0u); + size_t resultSize = aggregate->getType().getObjectSize(); + TConstantUnion *resultArray = new TConstantUnion[resultSize]; + TBasicType basicType = aggregate->getBasicType(); + + size_t resultIndex = 0u; + + if (aggregate->getSequence()->size() == 1u) + { + TIntermNode *argument = aggregate->getSequence()->front(); + TIntermConstantUnion *argumentConstant = argument->getAsConstantUnion(); + const TConstantUnion *argumentUnionArray = argumentConstant->getUnionArrayPointer(); + // Check the special case of constructing a matrix diagonal from a single scalar, + // or a vector from a single scalar. + if (argumentConstant->getType().getObjectSize() == 1u) + { + if (aggregate->isMatrix()) + { + int resultCols = aggregate->getType().getCols(); + int resultRows = aggregate->getType().getRows(); + for (int col = 0; col < resultCols; ++col) { - boolNodeFlag = true; + for (int row = 0; row < resultRows; ++row) + { + if (col == row) + { + resultArray[resultIndex].cast(basicType, argumentUnionArray[0]); + } + else + { + resultArray[resultIndex].setFConst(0.0f); + } + ++resultIndex; + } } } else { - for (size_t i = 0; i < objectSize; i++) + while (resultIndex < resultSize) { - if (unionArray[i] != rightUnionArray[i]) + resultArray[resultIndex].cast(basicType, argumentUnionArray[0]); + ++resultIndex; + } + } + ASSERT(resultIndex == resultSize); + return resultArray; + } + else if (aggregate->isMatrix() && argumentConstant->isMatrix()) + { + // The special case of constructing a matrix from a matrix. + int argumentCols = argumentConstant->getType().getCols(); + int argumentRows = argumentConstant->getType().getRows(); + int resultCols = aggregate->getType().getCols(); + int resultRows = aggregate->getType().getRows(); + for (int col = 0; col < resultCols; ++col) + { + for (int row = 0; row < resultRows; ++row) + { + if (col < argumentCols && row < argumentRows) + { + resultArray[resultIndex].cast(basicType, + argumentUnionArray[col * argumentRows + row]); + } + else if (col == row) { - boolNodeFlag = true; - break; // break out of for loop + resultArray[resultIndex].setFConst(1.0f); } + else + { + resultArray[resultIndex].setFConst(0.0f); + } + ++resultIndex; } } + ASSERT(resultIndex == resultSize); + return resultArray; + } + } - tempConstArray = new ConstantUnion[1]; - if (!boolNodeFlag) + for (TIntermNode *&argument : *aggregate->getSequence()) + { + TIntermConstantUnion *argumentConstant = argument->getAsConstantUnion(); + size_t argumentSize = argumentConstant->getType().getObjectSize(); + const TConstantUnion *argumentUnionArray = argumentConstant->getUnionArrayPointer(); + for (size_t i = 0u; i < argumentSize; ++i) + { + if (resultIndex >= resultSize) + break; + resultArray[resultIndex].cast(basicType, argumentUnionArray[i]); + ++resultIndex; + } + } + ASSERT(resultIndex == resultSize); + return resultArray; +} + +// static +TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink) +{ + TOperator op = aggregate->getOp(); + TIntermSequence *sequence = aggregate->getSequence(); + unsigned int paramsCount = static_cast<unsigned int>(sequence->size()); + std::vector<const TConstantUnion *> unionArrays(paramsCount); + std::vector<size_t> objectSizes(paramsCount); + size_t maxObjectSize = 0; + TBasicType basicType = EbtVoid; + TSourceLoc loc; + for (unsigned int i = 0; i < paramsCount; i++) + { + TIntermConstantUnion *paramConstant = (*sequence)[i]->getAsConstantUnion(); + ASSERT(paramConstant != nullptr); // Should be checked already. + + if (i == 0) + { + basicType = paramConstant->getType().getBasicType(); + loc = paramConstant->getLine(); + } + unionArrays[i] = paramConstant->getUnionArrayPointer(); + objectSizes[i] = paramConstant->getType().getObjectSize(); + if (objectSizes[i] > maxObjectSize) + maxObjectSize = objectSizes[i]; + } + + if (!(*sequence)[0]->getAsTyped()->isMatrix()) + { + for (unsigned int i = 0; i < paramsCount; i++) + if (objectSizes[i] != maxObjectSize) + unionArrays[i] = Vectorize(*unionArrays[i], maxObjectSize); + } + + TConstantUnion *resultArray = nullptr; + if (paramsCount == 2) + { + // + // Binary built-in + // + switch (op) + { + case EOpAtan: { - tempConstArray->setBConst(true); + if (basicType == EbtFloat) + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + float y = unionArrays[0][i].getFConst(); + float x = unionArrays[1][i].getFConst(); + // Results are undefined if x and y are both 0. + if (x == 0.0f && y == 0.0f) + UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + else + resultArray[i].setFConst(atan2f(y, x)); + } + } + else + UNREACHABLE(); } - else + break; + + case EOpPow: { - tempConstArray->setBConst(false); + if (basicType == EbtFloat) + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + float x = unionArrays[0][i].getFConst(); + float y = unionArrays[1][i].getFConst(); + // Results are undefined if x < 0. + // Results are undefined if x = 0 and y <= 0. + if (x < 0.0f) + UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + else if (x == 0.0f && y <= 0.0f) + UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + else + resultArray[i].setFConst(powf(x, y)); + } + } + else + UNREACHABLE(); } + break; - tempNode = new TIntermConstantUnion( - tempConstArray, TType(EbtBool, EbpUndefined, EvqConst)); - tempNode->setLine(getLine()); + case EOpMod: + { + if (basicType == EbtFloat) + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + float x = unionArrays[0][i].getFConst(); + float y = unionArrays[1][i].getFConst(); + resultArray[i].setFConst(x - y * floorf(x / y)); + } + } + else + UNREACHABLE(); + } + break; - return tempNode; + case EOpMin: + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + switch (basicType) + { + case EbtFloat: + resultArray[i].setFConst(std::min(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst())); + break; + case EbtInt: + resultArray[i].setIConst(std::min(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst())); + break; + case EbtUInt: + resultArray[i].setUConst(std::min(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst())); + break; + default: + UNREACHABLE(); + break; + } + } + } + break; - case EOpNotEqual: - if (getType().getBasicType() == EbtStruct) + case EOpMax: { - if (CompareStructure(node->getType(), - node->getUnionArrayPointer(), - unionArray)) + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) { - boolNodeFlag = true; + switch (basicType) + { + case EbtFloat: + resultArray[i].setFConst(std::max(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst())); + break; + case EbtInt: + resultArray[i].setIConst(std::max(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst())); + break; + case EbtUInt: + resultArray[i].setUConst(std::max(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst())); + break; + default: + UNREACHABLE(); + break; + } } } - else + break; + + case EOpStep: { - for (size_t i = 0; i < objectSize; i++) + if (basicType == EbtFloat) { - if (unionArray[i] == rightUnionArray[i]) + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + resultArray[i].setFConst(unionArrays[1][i].getFConst() < unionArrays[0][i].getFConst() ? 0.0f : 1.0f); + } + else + UNREACHABLE(); + } + break; + + case EOpLessThan: + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + switch (basicType) { - boolNodeFlag = true; - break; // break out of for loop + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() < unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() < unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() < unionArrays[1][i].getUConst()); + break; + default: + UNREACHABLE(); + break; } } } + break; + + case EOpLessThanEqual: + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + switch (basicType) + { + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() <= unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() <= unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() <= unionArrays[1][i].getUConst()); + break; + default: + UNREACHABLE(); + break; + } + } + } + break; + + case EOpGreaterThan: + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + switch (basicType) + { + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() > unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() > unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() > unionArrays[1][i].getUConst()); + break; + default: + UNREACHABLE(); + break; + } + } + } + break; + + case EOpGreaterThanEqual: + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + switch (basicType) + { + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() >= unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() >= unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() >= unionArrays[1][i].getUConst()); + break; + default: + UNREACHABLE(); + break; + } + } + } + break; + + case EOpVectorEqual: + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + switch (basicType) + { + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() == unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() == unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() == unionArrays[1][i].getUConst()); + break; + case EbtBool: + resultArray[i].setBConst(unionArrays[0][i].getBConst() == unionArrays[1][i].getBConst()); + break; + default: + UNREACHABLE(); + break; + } + } + } + break; + + case EOpVectorNotEqual: + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + switch (basicType) + { + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() != unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() != unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() != unionArrays[1][i].getUConst()); + break; + case EbtBool: + resultArray[i].setBConst(unionArrays[0][i].getBConst() != unionArrays[1][i].getBConst()); + break; + default: + UNREACHABLE(); + break; + } + } + } + break; + + case EOpDistance: + if (basicType == EbtFloat) + { + TConstantUnion *distanceArray = new TConstantUnion[maxObjectSize]; + resultArray = new TConstantUnion(); + for (size_t i = 0; i < maxObjectSize; i++) + { + float x = unionArrays[0][i].getFConst(); + float y = unionArrays[1][i].getFConst(); + distanceArray[i].setFConst(x - y); + } + resultArray->setFConst(VectorLength(distanceArray, maxObjectSize)); + } + else + UNREACHABLE(); + break; + + case EOpDot: + + if (basicType == EbtFloat) + { + resultArray = new TConstantUnion(); + resultArray->setFConst(VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize)); + } + else + UNREACHABLE(); + break; - tempConstArray = new ConstantUnion[1]; - if (!boolNodeFlag) + case EOpCross: + if (basicType == EbtFloat && maxObjectSize == 3) { - tempConstArray->setBConst(true); + resultArray = new TConstantUnion[maxObjectSize]; + float x0 = unionArrays[0][0].getFConst(); + float x1 = unionArrays[0][1].getFConst(); + float x2 = unionArrays[0][2].getFConst(); + float y0 = unionArrays[1][0].getFConst(); + float y1 = unionArrays[1][1].getFConst(); + float y2 = unionArrays[1][2].getFConst(); + resultArray[0].setFConst(x1 * y2 - y1 * x2); + resultArray[1].setFConst(x2 * y0 - y2 * x0); + resultArray[2].setFConst(x0 * y1 - y0 * x1); } else + UNREACHABLE(); + break; + + case EOpReflect: + if (basicType == EbtFloat) { - tempConstArray->setBConst(false); + // genType reflect (genType I, genType N) : + // For the incident vector I and surface orientation N, returns the reflection direction: + // I - 2 * dot(N, I) * N. + resultArray = new TConstantUnion[maxObjectSize]; + float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize); + for (size_t i = 0; i < maxObjectSize; i++) + { + float result = unionArrays[0][i].getFConst() - + 2.0f * dotProduct * unionArrays[1][i].getFConst(); + resultArray[i].setFConst(result); + } } + else + UNREACHABLE(); + break; - tempNode = new TIntermConstantUnion( - tempConstArray, TType(EbtBool, EbpUndefined, EvqConst)); - tempNode->setLine(getLine()); + case EOpMul: + if (basicType == EbtFloat && (*sequence)[0]->getAsTyped()->isMatrix() && + (*sequence)[1]->getAsTyped()->isMatrix()) + { + // Perform component-wise matrix multiplication. + resultArray = new TConstantUnion[maxObjectSize]; + int size = (*sequence)[0]->getAsTyped()->getNominalSize(); + angle::Matrix<float> result = + GetMatrix(unionArrays[0], size).compMult(GetMatrix(unionArrays[1], size)); + SetUnionArrayFromMatrix(result, resultArray); + } + else + UNREACHABLE(); + break; - return tempNode; + case EOpOuterProduct: + if (basicType == EbtFloat) + { + size_t numRows = (*sequence)[0]->getAsTyped()->getType().getObjectSize(); + size_t numCols = (*sequence)[1]->getAsTyped()->getType().getObjectSize(); + resultArray = new TConstantUnion[numRows * numCols]; + angle::Matrix<float> result = + GetMatrix(unionArrays[0], 1, static_cast<int>(numCols)) + .outerProduct(GetMatrix(unionArrays[1], static_cast<int>(numRows), 1)); + SetUnionArrayFromMatrix(result, resultArray); + } + else + UNREACHABLE(); + break; default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Invalid operator for constant folding"); - return NULL; + UNREACHABLE(); + // TODO: Add constant folding support for other built-in operations that take 2 parameters and not handled above. + return nullptr; } - tempNode = new TIntermConstantUnion(tempConstArray, returnType); - tempNode->setLine(getLine()); - - return tempNode; } - else + else if (paramsCount == 3) { // - // Do unary operations + // Ternary built-in // - TIntermConstantUnion *newNode = 0; - ConstantUnion* tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) + switch (op) { - switch(op) + case EOpClamp: { - case EOpNegative: - switch (getType().getBasicType()) + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) { - case EbtFloat: - tempConstArray[i].setFConst(-unionArray[i].getFConst()); - break; - case EbtInt: - tempConstArray[i].setIConst(-unionArray[i].getIConst()); - break; - case EbtUInt: - tempConstArray[i].setUConst(static_cast<unsigned int>( - -static_cast<int>(unionArray[i].getUConst()))); - break; - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return NULL; + switch (basicType) + { + case EbtFloat: + { + float x = unionArrays[0][i].getFConst(); + float min = unionArrays[1][i].getFConst(); + float max = unionArrays[2][i].getFConst(); + // Results are undefined if min > max. + if (min > max) + UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + else + resultArray[i].setFConst(gl::clamp(x, min, max)); + } + break; + case EbtInt: + { + int x = unionArrays[0][i].getIConst(); + int min = unionArrays[1][i].getIConst(); + int max = unionArrays[2][i].getIConst(); + // Results are undefined if min > max. + if (min > max) + UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + else + resultArray[i].setIConst(gl::clamp(x, min, max)); + } + break; + case EbtUInt: + { + unsigned int x = unionArrays[0][i].getUConst(); + unsigned int min = unionArrays[1][i].getUConst(); + unsigned int max = unionArrays[2][i].getUConst(); + // Results are undefined if min > max. + if (min > max) + UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + else + resultArray[i].setUConst(gl::clamp(x, min, max)); + } + break; + default: + UNREACHABLE(); + break; + } } - break; + } + break; - case EOpPositive: - switch (getType().getBasicType()) + case EOpMix: + { + if (basicType == EbtFloat) { - case EbtFloat: - tempConstArray[i].setFConst(unionArray[i].getFConst()); - break; - case EbtInt: - tempConstArray[i].setIConst(unionArray[i].getIConst()); - break; - case EbtUInt: - tempConstArray[i].setUConst(static_cast<unsigned int>( - static_cast<int>(unionArray[i].getUConst()))); - break; - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return NULL; + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + float x = unionArrays[0][i].getFConst(); + float y = unionArrays[1][i].getFConst(); + TBasicType type = (*sequence)[2]->getAsTyped()->getType().getBasicType(); + if (type == EbtFloat) + { + // Returns the linear blend of x and y, i.e., x * (1 - a) + y * a. + float a = unionArrays[2][i].getFConst(); + resultArray[i].setFConst(x * (1.0f - a) + y * a); + } + else // 3rd parameter is EbtBool + { + ASSERT(type == EbtBool); + // Selects which vector each returned component comes from. + // For a component of a that is false, the corresponding component of x is returned. + // For a component of a that is true, the corresponding component of y is returned. + bool a = unionArrays[2][i].getBConst(); + resultArray[i].setFConst(a ? y : x); + } + } } - break; + else + UNREACHABLE(); + } + break; - case EOpLogicalNot: - // this code is written for possible future use, - // will not get executed currently - switch (getType().getBasicType()) + case EOpSmoothStep: + { + if (basicType == EbtFloat) { - case EbtBool: - tempConstArray[i].setBConst(!unionArray[i].getBConst()); - break; - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return NULL; + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + float edge0 = unionArrays[0][i].getFConst(); + float edge1 = unionArrays[1][i].getFConst(); + float x = unionArrays[2][i].getFConst(); + // Results are undefined if edge0 >= edge1. + if (edge0 >= edge1) + { + UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + } + else + { + // Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and performs smooth + // Hermite interpolation between 0 and 1 when edge0 < x < edge1. + float t = gl::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f); + resultArray[i].setFConst(t * t * (3.0f - 2.0f * t)); + } + } } - break; + else + UNREACHABLE(); + } + break; - case EOpBitwiseNot: - switch (getType().getBasicType()) + case EOpFaceForward: + if (basicType == EbtFloat) + { + // genType faceforward(genType N, genType I, genType Nref) : + // If dot(Nref, I) < 0 return N, otherwise return -N. + resultArray = new TConstantUnion[maxObjectSize]; + float dotProduct = VectorDotProduct(unionArrays[2], unionArrays[1], maxObjectSize); + for (size_t i = 0; i < maxObjectSize; i++) { - case EbtInt: - tempConstArray[i].setIConst(~unionArray[i].getIConst()); - break; - case EbtUInt: - tempConstArray[i].setUConst(~unionArray[i].getUConst()); - break; - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return NULL; + if (dotProduct < 0) + resultArray[i].setFConst(unionArrays[0][i].getFConst()); + else + resultArray[i].setFConst(-unionArrays[0][i].getFConst()); } - break; + } + else + UNREACHABLE(); + break; - default: - return NULL; + case EOpRefract: + if (basicType == EbtFloat) + { + // genType refract(genType I, genType N, float eta) : + // For the incident vector I and surface normal N, and the ratio of indices of refraction eta, + // return the refraction vector. The result is computed by + // k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I)) + // if (k < 0.0) + // return genType(0.0) + // else + // return eta * I - (eta * dot(N, I) + sqrt(k)) * N + resultArray = new TConstantUnion[maxObjectSize]; + float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize); + for (size_t i = 0; i < maxObjectSize; i++) + { + float eta = unionArrays[2][i].getFConst(); + float k = 1.0f - eta * eta * (1.0f - dotProduct * dotProduct); + if (k < 0.0f) + resultArray[i].setFConst(0.0f); + else + resultArray[i].setFConst(eta * unionArrays[0][i].getFConst() - + (eta * dotProduct + sqrtf(k)) * unionArrays[1][i].getFConst()); + } } + else + UNREACHABLE(); + break; + + default: + UNREACHABLE(); + // TODO: Add constant folding support for other built-in operations that take 3 parameters and not handled above. + return nullptr; } - newNode = new TIntermConstantUnion(tempConstArray, getType()); - newNode->setLine(getLine()); - return newNode; } + return resultArray; } // static @@ -1209,26 +2606,59 @@ TString TIntermTraverser::hash(const TString &name, ShHashFunction64 hashFunctio void TIntermTraverser::updateTree() { + for (size_t ii = 0; ii < mInsertions.size(); ++ii) + { + const NodeInsertMultipleEntry &insertion = mInsertions[ii]; + ASSERT(insertion.parent); + if (!insertion.insertionsAfter.empty()) + { + bool inserted = insertion.parent->insertChildNodes(insertion.position + 1, + insertion.insertionsAfter); + ASSERT(inserted); + UNUSED_ASSERTION_VARIABLE(inserted); + } + if (!insertion.insertionsBefore.empty()) + { + bool inserted = + insertion.parent->insertChildNodes(insertion.position, insertion.insertionsBefore); + ASSERT(inserted); + UNUSED_ASSERTION_VARIABLE(inserted); + } + } for (size_t ii = 0; ii < mReplacements.size(); ++ii) { - const NodeUpdateEntry& entry = mReplacements[ii]; - ASSERT(entry.parent); - bool replaced = entry.parent->replaceChildNode( - entry.original, entry.replacement); + const NodeUpdateEntry &replacement = mReplacements[ii]; + ASSERT(replacement.parent); + bool replaced = replacement.parent->replaceChildNode( + replacement.original, replacement.replacement); ASSERT(replaced); + UNUSED_ASSERTION_VARIABLE(replaced); - if (!entry.originalBecomesChildOfReplacement) + if (!replacement.originalBecomesChildOfReplacement) { // In AST traversing, a parent is visited before its children. - // After we replace a node, if an immediate child is to + // After we replace a node, if its immediate child is to // be replaced, we need to make sure we don't update the replaced // node; instead, we update the replacement node. for (size_t jj = ii + 1; jj < mReplacements.size(); ++jj) { - NodeUpdateEntry& entry2 = mReplacements[jj]; - if (entry2.parent == entry.original) - entry2.parent = entry.replacement; + NodeUpdateEntry &replacement2 = mReplacements[jj]; + if (replacement2.parent == replacement.original) + replacement2.parent = replacement.replacement; } } } + for (size_t ii = 0; ii < mMultiReplacements.size(); ++ii) + { + const NodeReplaceWithMultipleEntry &replacement = mMultiReplacements[ii]; + ASSERT(replacement.parent); + bool replaced = replacement.parent->replaceChildNodeWithMultiple( + replacement.original, replacement.replacements); + ASSERT(replaced); + UNUSED_ASSERTION_VARIABLE(replaced); + } + + mInsertions.clear(); + mReplacements.clear(); + mMultiReplacements.clear(); } diff --git a/src/3rdparty/angle/src/compiler/translator/IntermNode.h b/src/3rdparty/angle/src/compiler/translator/IntermNode.h index 9f732cbb00..ad500e2b1f 100644 --- a/src/3rdparty/angle/src/compiler/translator/IntermNode.h +++ b/src/3rdparty/angle/src/compiler/translator/IntermNode.h @@ -23,9 +23,9 @@ #include "common/angleutils.h" #include "compiler/translator/Common.h" -#include "compiler/translator/Types.h" #include "compiler/translator/ConstantUnion.h" #include "compiler/translator/Operator.h" +#include "compiler/translator/Types.h" class TIntermTraverser; class TIntermAggregate; @@ -42,10 +42,33 @@ class TInfoSink; class TInfoSinkBase; class TIntermRaw; +class TSymbolTable; + +// Encapsulate an identifier string and track whether it is coming from the original shader code +// (not internal) or from ANGLE (internal). Usually internal names shouldn't be decorated or hashed. +class TName +{ + public: + POOL_ALLOCATOR_NEW_DELETE(); + explicit TName(const TString &name) : mName(name), mIsInternal(false) {} + TName() : mName(), mIsInternal(false) {} + TName(const TName &) = default; + TName &operator=(const TName &) = default; + + const TString &getString() const { return mName; } + void setString(const TString &string) { mName = string; } + bool isInternal() const { return mIsInternal; } + void setInternal(bool isInternal) { mIsInternal = isInternal; } + + private: + TString mName; + bool mIsInternal; +}; + // // Base class for the tree nodes // -class TIntermNode +class TIntermNode : angle::NonCopyable { public: POOL_ALLOCATOR_NEW_DELETE(); @@ -99,7 +122,10 @@ class TIntermTyped : public TIntermNode { public: TIntermTyped(const TType &t) : mType(t) { } - virtual TIntermTyped *getAsTyped() { return this; } + + virtual TIntermTyped *deepCopy() const = 0; + + TIntermTyped *getAsTyped() override { return this; } virtual bool hasSideEffects() const = 0; @@ -123,13 +149,14 @@ class TIntermTyped : public TIntermNode bool isScalar() const { return mType.isScalar(); } bool isScalarInt() const { return mType.isScalarInt(); } const char *getBasicString() const { return mType.getBasicString(); } - const char *getQualifierString() const { return mType.getQualifierString(); } TString getCompleteString() const { return mType.getCompleteString(); } int getArraySize() const { return mType.getArraySize(); } protected: TType mType; + + TIntermTyped(const TIntermTyped &node); }; // @@ -146,25 +173,23 @@ class TIntermLoop : public TIntermNode { public: TIntermLoop(TLoopType type, - TIntermNode *init, TIntermTyped *cond, TIntermTyped *expr, - TIntermNode *body) - : mType(type), - mInit(init), - mCond(cond), - mExpr(expr), - mBody(body), - mUnrollFlag(false) { } + TIntermNode *init, + TIntermTyped *cond, + TIntermTyped *expr, + TIntermAggregate *body) + : mType(type), mInit(init), mCond(cond), mExpr(expr), mBody(body), mUnrollFlag(false) + { + } - virtual TIntermLoop *getAsLoopNode() { return this; } - virtual void traverse(TIntermTraverser *); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); + TIntermLoop *getAsLoopNode() override { return this; } + void traverse(TIntermTraverser *it) override; + bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; TLoopType getType() const { return mType; } TIntermNode *getInit() { return mInit; } TIntermTyped *getCondition() { return mCond; } TIntermTyped *getExpression() { return mExpr; } - TIntermNode *getBody() { return mBody; } + TIntermAggregate *getBody() { return mBody; } void setUnrollFlag(bool flag) { mUnrollFlag = flag; } bool getUnrollFlag() const { return mUnrollFlag; } @@ -174,7 +199,7 @@ class TIntermLoop : public TIntermNode TIntermNode *mInit; // for-loop initialization TIntermTyped *mCond; // loop exit condition TIntermTyped *mExpr; // for-loop expression - TIntermNode *mBody; // loop body + TIntermAggregate *mBody; // loop body bool mUnrollFlag; // Whether the loop should be unrolled or not. }; @@ -189,9 +214,8 @@ class TIntermBranch : public TIntermNode : mFlowOp(op), mExpression(e) { } - virtual void traverse(TIntermTraverser *); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); + void traverse(TIntermTraverser *it) override; + bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; TOperator getFlowOp() { return mFlowOp; } TIntermTyped* getExpression() { return mExpression; } @@ -211,26 +235,32 @@ class TIntermSymbol : public TIntermTyped // If sym comes from per process globalpoolallocator, then it causes increased memory usage // per compile it is essential to use "symbol = sym" to assign to symbol TIntermSymbol(int id, const TString &symbol, const TType &type) - : TIntermTyped(type), - mId(id) + : TIntermTyped(type), mId(id), mSymbol(symbol) { - mSymbol = symbol; } - virtual bool hasSideEffects() const { return false; } + TIntermTyped *deepCopy() const override { return new TIntermSymbol(*this); } + + bool hasSideEffects() const override { return false; } int getId() const { return mId; } - const TString &getSymbol() const { return mSymbol; } + const TString &getSymbol() const { return mSymbol.getString(); } + const TName &getName() const { return mSymbol; } void setId(int newId) { mId = newId; } - virtual void traverse(TIntermTraverser *); - virtual TIntermSymbol *getAsSymbolNode() { return this; } - virtual bool replaceChildNode(TIntermNode *, TIntermNode *) { return false; } + void setInternal(bool internal) { mSymbol.setInternal(internal); } + + void traverse(TIntermTraverser *it) override; + TIntermSymbol *getAsSymbolNode() override { return this; } + bool replaceChildNode(TIntermNode *, TIntermNode *) override { return false; } protected: int mId; - TString mSymbol; + TName mSymbol; + + private: + TIntermSymbol(const TIntermSymbol &) = default; // Note: not deleted, just private! }; // A Raw node stores raw code, that the translator will insert verbatim @@ -242,30 +272,46 @@ class TIntermRaw : public TIntermTyped TIntermRaw(const TType &type, const TString &rawText) : TIntermTyped(type), mRawText(rawText) { } + TIntermRaw(const TIntermRaw &) = delete; + + TIntermTyped *deepCopy() const override + { + UNREACHABLE(); + return nullptr; + } - virtual bool hasSideEffects() const { return false; } + bool hasSideEffects() const override { return false; } TString getRawText() const { return mRawText; } - virtual void traverse(TIntermTraverser *); + void traverse(TIntermTraverser *it) override; - virtual TIntermRaw *getAsRawNode() { return this; } - virtual bool replaceChildNode(TIntermNode *, TIntermNode *) { return false; } + TIntermRaw *getAsRawNode() override { return this; } + bool replaceChildNode(TIntermNode *, TIntermNode *) override { return false; } protected: TString mRawText; }; +// Constant folded node. +// Note that nodes may be constant folded and not be constant expressions with the EvqConst +// qualifier. This happens for example when the following expression is processed: +// "true ? 1.0 : non_constant" +// Other nodes than TIntermConstantUnion may also be constant expressions. +// class TIntermConstantUnion : public TIntermTyped { public: - TIntermConstantUnion(ConstantUnion *unionPointer, const TType &type) - : TIntermTyped(type), - mUnionArrayPointer(unionPointer) { } + TIntermConstantUnion(const TConstantUnion *unionPointer, const TType &type) + : TIntermTyped(type), mUnionArrayPointer(unionPointer) + { + } + + TIntermTyped *deepCopy() const override { return new TIntermConstantUnion(*this); } - virtual bool hasSideEffects() const { return false; } + bool hasSideEffects() const override { return false; } - ConstantUnion *getUnionArrayPointer() const { return mUnionArrayPointer; } + const TConstantUnion *getUnionArrayPointer() const { return mUnionArrayPointer; } int getIConst(size_t index) const { @@ -284,14 +330,33 @@ class TIntermConstantUnion : public TIntermTyped return mUnionArrayPointer ? mUnionArrayPointer[index].getBConst() : false; } - virtual TIntermConstantUnion *getAsConstantUnion() { return this; } - virtual void traverse(TIntermTraverser *); - virtual bool replaceChildNode(TIntermNode *, TIntermNode *) { return false; } + void replaceConstantUnion(const TConstantUnion *safeConstantUnion) + { + // Previous union pointer freed on pool deallocation. + mUnionArrayPointer = safeConstantUnion; + } - TIntermTyped *fold(TOperator, TIntermTyped *, TInfoSink &); + TIntermConstantUnion *getAsConstantUnion() override { return this; } + void traverse(TIntermTraverser *it) override; + bool replaceChildNode(TIntermNode *, TIntermNode *) override { return false; } + + TConstantUnion *foldBinary(TOperator op, TIntermConstantUnion *rightNode, TInfoSink &infoSink); + TConstantUnion *foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink); + TConstantUnion *foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink); + + static TConstantUnion *FoldAggregateConstructor(TIntermAggregate *aggregate, + TInfoSink &infoSink); + static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink); protected: - ConstantUnion *mUnionArrayPointer; + // Same data may be shared between multiple constant unions, so it can't be modified. + const TConstantUnion *mUnionArrayPointer; + + private: + typedef float(*FloatTypeUnaryFunc) (float); + bool foldFloatTypeUnary(const TConstantUnion ¶meter, FloatTypeUnaryFunc builtinFunc, TInfoSink &infoSink, TConstantUnion *result) const; + + TIntermConstantUnion(const TIntermConstantUnion &node); // Note: not deleted, just private! }; // @@ -304,9 +369,10 @@ class TIntermOperator : public TIntermTyped void setOp(TOperator op) { mOp = op; } bool isAssignment() const; + bool isMultiplication() const; bool isConstructor() const; - virtual bool hasSideEffects() const { return isAssignment(); } + bool hasSideEffects() const override { return isAssignment(); } protected: TIntermOperator(TOperator op) @@ -316,6 +382,8 @@ class TIntermOperator : public TIntermTyped : TIntermTyped(type), mOp(op) {} + TIntermOperator(const TIntermOperator &) = default; + TOperator mOp; }; @@ -329,12 +397,13 @@ class TIntermBinary : public TIntermOperator : TIntermOperator(op), mAddIndexClamp(false) {} - virtual TIntermBinary *getAsBinaryNode() { return this; } - virtual void traverse(TIntermTraverser *); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); + TIntermTyped *deepCopy() const override { return new TIntermBinary(*this); } + + TIntermBinary *getAsBinaryNode() override { return this; }; + void traverse(TIntermTraverser *it) override; + bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; - virtual bool hasSideEffects() const + bool hasSideEffects() const override { return isAssignment() || mLeft->hasSideEffects() || mRight->hasSideEffects(); } @@ -344,6 +413,7 @@ class TIntermBinary : public TIntermOperator TIntermTyped *getLeft() const { return mLeft; } TIntermTyped *getRight() const { return mRight; } bool promote(TInfoSink &); + TIntermTyped *fold(TInfoSink &infoSink); void setAddIndexClamp() { mAddIndexClamp = true; } bool getAddIndexClamp() { return mAddIndexClamp; } @@ -354,6 +424,9 @@ class TIntermBinary : public TIntermOperator // If set to true, wrap any EOpIndexIndirect with a clamp to bounds. bool mAddIndexClamp; + + private: + TIntermBinary(const TIntermBinary &node); // Note: not deleted, just private! }; // @@ -371,19 +444,18 @@ class TIntermUnary : public TIntermOperator mOperand(NULL), mUseEmulatedFunction(false) {} - virtual void traverse(TIntermTraverser *); - virtual TIntermUnary *getAsUnaryNode() { return this; } - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); + TIntermTyped *deepCopy() const override { return new TIntermUnary(*this); } - virtual bool hasSideEffects() const - { - return isAssignment() || mOperand->hasSideEffects(); - } + void traverse(TIntermTraverser *it) override; + TIntermUnary *getAsUnaryNode() override { return this; } + bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; + + bool hasSideEffects() const override { return isAssignment() || mOperand->hasSideEffects(); } void setOperand(TIntermTyped *operand) { mOperand = operand; } TIntermTyped *getOperand() { return mOperand; } void promote(const TType *funcReturnType); + TIntermTyped *fold(TInfoSink &infoSink); void setUseEmulatedFunction() { mUseEmulatedFunction = true; } bool getUseEmulatedFunction() { return mUseEmulatedFunction; } @@ -394,6 +466,9 @@ class TIntermUnary : public TIntermOperator // If set to true, replace the built-in function call with an emulated one // to work around driver bugs. bool mUseEmulatedFunction; + + private: + TIntermUnary(const TIntermUnary &node); // note: not deleted, just private! }; typedef TVector<TIntermNode *> TIntermSequence; @@ -408,52 +483,68 @@ class TIntermAggregate : public TIntermOperator TIntermAggregate() : TIntermOperator(EOpNull), mUserDefined(false), - mUseEmulatedFunction(false) { } + mUseEmulatedFunction(false), + mGotPrecisionFromChildren(false) + { + } TIntermAggregate(TOperator op) : TIntermOperator(op), - mUseEmulatedFunction(false) { } + mUseEmulatedFunction(false), + mGotPrecisionFromChildren(false) + { + } ~TIntermAggregate() { } - virtual TIntermAggregate *getAsAggregate() { return this; } - virtual void traverse(TIntermTraverser *); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); + // Note: only supported for nodes that can be a part of an expression. + TIntermTyped *deepCopy() const override { return new TIntermAggregate(*this); } + TIntermAggregate *getAsAggregate() override { return this; } + void traverse(TIntermTraverser *it) override; + bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; + bool replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements); + bool insertChildNodes(TIntermSequence::size_type position, TIntermSequence insertions); // Conservatively assume function calls and other aggregate operators have side-effects - virtual bool hasSideEffects() const { return true; } + bool hasSideEffects() const override { return true; } + TIntermTyped *fold(TInfoSink &infoSink); TIntermSequence *getSequence() { return &mSequence; } - void setName(const TString &name) { mName = name; } - const TString &getName() const { return mName; } + void setNameObj(const TName &name) { mName = name; } + const TName &getNameObj() const { return mName; } + + void setName(const TString &name) { mName.setString(name); } + const TString &getName() const { return mName.getString(); } void setUserDefined() { mUserDefined = true; } bool isUserDefined() const { return mUserDefined; } - void setOptimize(bool optimize) { mOptimize = optimize; } - bool getOptimize() const { return mOptimize; } - void setDebug(bool debug) { mDebug = debug; } - bool getDebug() const { return mDebug; } + void setFunctionId(int functionId) { mFunctionId = functionId; } + int getFunctionId() const { return mFunctionId; } void setUseEmulatedFunction() { mUseEmulatedFunction = true; } bool getUseEmulatedFunction() { return mUseEmulatedFunction; } + bool areChildrenConstQualified(); void setPrecisionFromChildren(); void setBuiltInFunctionPrecision(); + // Returns true if changing parameter precision may affect the return value. + bool gotPrecisionFromChildren() const { return mGotPrecisionFromChildren; } + protected: - TIntermAggregate(const TIntermAggregate &); // disallow copy constructor - TIntermAggregate &operator=(const TIntermAggregate &); // disallow assignment operator TIntermSequence mSequence; - TString mName; + TName mName; bool mUserDefined; // used for user defined function names - - bool mOptimize; - bool mDebug; + int mFunctionId; // If set to true, replace the built-in function call with an emulated one // to work around driver bugs. bool mUseEmulatedFunction; + + bool mGotPrecisionFromChildren; + + private: + TIntermAggregate(const TIntermAggregate &node); // note: not deleted, just private! }; // @@ -474,23 +565,28 @@ class TIntermSelection : public TIntermTyped mTrueBlock(trueB), mFalseBlock(falseB) {} - virtual void traverse(TIntermTraverser *); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); + // Note: only supported for ternary operator nodes. + TIntermTyped *deepCopy() const override { return new TIntermSelection(*this); } + + void traverse(TIntermTraverser *it) override; + bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; // Conservatively assume selections have side-effects - virtual bool hasSideEffects() const { return true; } + bool hasSideEffects() const override { return true; } bool usesTernaryOperator() const { return getBasicType() != EbtVoid; } TIntermNode *getCondition() const { return mCondition; } TIntermNode *getTrueBlock() const { return mTrueBlock; } TIntermNode *getFalseBlock() const { return mFalseBlock; } - TIntermSelection *getAsSelectionNode() { return this; } + TIntermSelection *getAsSelectionNode() override { return this; } -protected: + protected: TIntermTyped *mCondition; TIntermNode *mTrueBlock; TIntermNode *mFalseBlock; + + private: + TIntermSelection(const TIntermSelection &node); // Note: not deleted, just private! }; // @@ -512,6 +608,7 @@ class TIntermSwitch : public TIntermNode TIntermSwitch *getAsSwitchNode() override { return this; } + TIntermTyped *getInit() { return mInit; } TIntermAggregate *getStatementList() { return mStatementList; } void setStatementList(TIntermAggregate *statementList) { mStatementList = statementList; } @@ -553,9 +650,12 @@ enum Visit }; // -// For traversing the tree. User should derive from this, -// put their traversal specific data in it, and then pass -// it to a Traverse method. +// For traversing the tree. User should derive from this class overriding the visit functions, +// and then pass an object of the subclass to a traverse method of a node. +// +// The traverse*() functions may also be overridden do other bookkeeping on the tree to provide +// contextual information to the visit functions, such as whether the node is the target of an +// assignment. // // When using this, just fill in the methods for nodes you want visited. // Return false from a pre-visit to skip visiting that node's subtree. @@ -564,31 +664,59 @@ class TIntermTraverser : angle::NonCopyable { public: POOL_ALLOCATOR_NEW_DELETE(); - // TODO(zmo): remove default values. - TIntermTraverser(bool preVisit = true, bool inVisit = false, bool postVisit = false, - bool rightToLeft = false) + TIntermTraverser(bool preVisit, bool inVisit, bool postVisit) : preVisit(preVisit), inVisit(inVisit), postVisit(postVisit), - rightToLeft(rightToLeft), mDepth(0), - mMaxDepth(0) {} + mMaxDepth(0), + mTemporaryIndex(nullptr) + { + } virtual ~TIntermTraverser() {} - virtual void visitSymbol(TIntermSymbol *) {} - virtual void visitRaw(TIntermRaw *) {} - virtual void visitConstantUnion(TIntermConstantUnion *) {} - virtual bool visitBinary(Visit, TIntermBinary *) { return true; } - virtual bool visitUnary(Visit, TIntermUnary *) { return true; } - virtual bool visitSelection(Visit, TIntermSelection *) { return true; } - virtual bool visitSwitch(Visit, TIntermSwitch *) { return true; } - virtual bool visitCase(Visit, TIntermCase *) { return true; } - virtual bool visitAggregate(Visit, TIntermAggregate *) { return true; } - virtual bool visitLoop(Visit, TIntermLoop *) { return true; } - virtual bool visitBranch(Visit, TIntermBranch *) { return true; } + virtual void visitSymbol(TIntermSymbol *node) {} + virtual void visitRaw(TIntermRaw *node) {} + virtual void visitConstantUnion(TIntermConstantUnion *node) {} + virtual bool visitBinary(Visit visit, TIntermBinary *node) { return true; } + virtual bool visitUnary(Visit visit, TIntermUnary *node) { return true; } + virtual bool visitSelection(Visit visit, TIntermSelection *node) { return true; } + virtual bool visitSwitch(Visit visit, TIntermSwitch *node) { return true; } + virtual bool visitCase(Visit visit, TIntermCase *node) { return true; } + virtual bool visitAggregate(Visit visit, TIntermAggregate *node) { return true; } + virtual bool visitLoop(Visit visit, TIntermLoop *node) { return true; } + virtual bool visitBranch(Visit visit, TIntermBranch *node) { return true; } + + // The traverse functions contain logic for iterating over the children of the node + // and calling the visit functions in the appropriate places. They also track some + // context that may be used by the visit functions. + virtual void traverseSymbol(TIntermSymbol *node); + virtual void traverseRaw(TIntermRaw *node); + virtual void traverseConstantUnion(TIntermConstantUnion *node); + virtual void traverseBinary(TIntermBinary *node); + virtual void traverseUnary(TIntermUnary *node); + virtual void traverseSelection(TIntermSelection *node); + virtual void traverseSwitch(TIntermSwitch *node); + virtual void traverseCase(TIntermCase *node); + virtual void traverseAggregate(TIntermAggregate *node); + virtual void traverseLoop(TIntermLoop *node); + virtual void traverseBranch(TIntermBranch *node); int getMaxDepth() const { return mMaxDepth; } + // Return the original name if hash function pointer is NULL; + // otherwise return the hashed name. + static TString hash(const TString &name, ShHashFunction64 hashFunction); + + // If traversers need to replace nodes, they can add the replacements in + // mReplacements/mMultiReplacements during traversal and the user of the traverser should call + // this function after traversal to perform them. + void updateTree(); + + // Start creating temporary symbols from the given temporary symbol index + 1. + void useTemporaryIndex(unsigned int *temporaryIndex); + + protected: void incrementDepth(TIntermNode *current) { mDepth++; @@ -607,27 +735,26 @@ class TIntermTraverser : angle::NonCopyable return mPath.size() == 0 ? NULL : mPath.back(); } - // Return the original name if hash function pointer is NULL; - // otherwise return the hashed name. - static TString hash(const TString& name, ShHashFunction64 hashFunction); + void pushParentBlock(TIntermAggregate *node); + void incrementParentBlockPos(); + void popParentBlock(); + + bool parentNodeIsBlock() + { + return !mParentBlockStack.empty() && getParentNode() == mParentBlockStack.back().node; + } const bool preVisit; const bool inVisit; const bool postVisit; - const bool rightToLeft; - - // If traversers need to replace nodes, they can add the replacements in - // mReplacements during traversal and the user of the traverser should call - // this function after traversal to perform them. - void updateTree(); - protected: int mDepth; int mMaxDepth; // All the nodes from root to the current node's parent during traversing. TVector<TIntermNode *> mPath; + // To replace a single node with another on the parent node struct NodeUpdateEntry { NodeUpdateEntry(TIntermNode *_parent, @@ -645,9 +772,166 @@ class TIntermTraverser : angle::NonCopyable bool originalBecomesChildOfReplacement; }; + // To replace a single node with multiple nodes on the parent aggregate node + struct NodeReplaceWithMultipleEntry + { + NodeReplaceWithMultipleEntry(TIntermAggregate *_parent, TIntermNode *_original, TIntermSequence _replacements) + : parent(_parent), + original(_original), + replacements(_replacements) + { + } + + TIntermAggregate *parent; + TIntermNode *original; + TIntermSequence replacements; + }; + + // To insert multiple nodes on the parent aggregate node + struct NodeInsertMultipleEntry + { + NodeInsertMultipleEntry(TIntermAggregate *_parent, + TIntermSequence::size_type _position, + TIntermSequence _insertionsBefore, + TIntermSequence _insertionsAfter) + : parent(_parent), + position(_position), + insertionsBefore(_insertionsBefore), + insertionsAfter(_insertionsAfter) + { + } + + TIntermAggregate *parent; + TIntermSequence::size_type position; + TIntermSequence insertionsBefore; + TIntermSequence insertionsAfter; + }; + // During traversing, save all the changes that need to happen into - // mReplacements, then do them by calling updateTree(). + // mReplacements/mMultiReplacements, then do them by calling updateTree(). + // Multi replacements are processed after single replacements. std::vector<NodeUpdateEntry> mReplacements; + std::vector<NodeReplaceWithMultipleEntry> mMultiReplacements; + std::vector<NodeInsertMultipleEntry> mInsertions; + + // Helper to insert statements in the parent block (sequence) of the node currently being traversed. + // The statements will be inserted before the node being traversed once updateTree is called. + // Should only be called during PreVisit or PostVisit from sequence nodes. + // Note that inserting more than one set of nodes to the same parent node on a single updateTree call is not + // supported. + void insertStatementsInParentBlock(const TIntermSequence &insertions); + + // Same as above, but supports simultaneous insertion of statements before and after the node + // currently being traversed. + void insertStatementsInParentBlock(const TIntermSequence &insertionsBefore, + const TIntermSequence &insertionsAfter); + + // Helper to create a temporary symbol node with the given qualifier. + TIntermSymbol *createTempSymbol(const TType &type, TQualifier qualifier); + // Helper to create a temporary symbol node. + TIntermSymbol *createTempSymbol(const TType &type); + // Create a node that declares but doesn't initialize a temporary symbol. + TIntermAggregate *createTempDeclaration(const TType &type); + // Create a node that initializes the current temporary symbol with initializer having the given qualifier. + TIntermAggregate *createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier); + // Create a node that initializes the current temporary symbol with initializer. + TIntermAggregate *createTempInitDeclaration(TIntermTyped *initializer); + // Create a node that assigns rightNode to the current temporary symbol. + TIntermBinary *createTempAssignment(TIntermTyped *rightNode); + // Increment temporary symbol index. + void nextTemporaryIndex(); + + private: + struct ParentBlock + { + ParentBlock(TIntermAggregate *nodeIn, TIntermSequence::size_type posIn) + : node(nodeIn), + pos(posIn) + { + } + + TIntermAggregate *node; + TIntermSequence::size_type pos; + }; + // All the code blocks from the root to the current node's parent during traversal. + std::vector<ParentBlock> mParentBlockStack; + + unsigned int *mTemporaryIndex; +}; + +// Traverser parent class that tracks where a node is a destination of a write operation and so is +// required to be an l-value. +class TLValueTrackingTraverser : public TIntermTraverser +{ + public: + TLValueTrackingTraverser(bool preVisit, + bool inVisit, + bool postVisit, + const TSymbolTable &symbolTable, + int shaderVersion) + : TIntermTraverser(preVisit, inVisit, postVisit), + mOperatorRequiresLValue(false), + mInFunctionCallOutParameter(false), + mSymbolTable(symbolTable), + mShaderVersion(shaderVersion) + { + } + virtual ~TLValueTrackingTraverser() {} + + void traverseBinary(TIntermBinary *node) override; + void traverseUnary(TIntermUnary *node) override; + void traverseAggregate(TIntermAggregate *node) override; + + protected: + bool isLValueRequiredHere() const + { + return mOperatorRequiresLValue || mInFunctionCallOutParameter; + } + + // Return true if the prototype or definition of the function being called has been encountered + // during traversal. + bool isInFunctionMap(const TIntermAggregate *callNode) const; + + private: + // Track whether an l-value is required in the node that is currently being traversed by the + // surrounding operator. + // Use isLValueRequiredHere to check all conditions which require an l-value. + void setOperatorRequiresLValue(bool lValueRequired) + { + mOperatorRequiresLValue = lValueRequired; + } + bool operatorRequiresLValue() const { return mOperatorRequiresLValue; } + + // Add a function encountered during traversal to the function map. + void addToFunctionMap(const TName &name, TIntermSequence *paramSequence); + + // Return the parameters sequence from the function definition or prototype. + TIntermSequence *getFunctionParameters(const TIntermAggregate *callNode); + + // Track whether an l-value is required inside a function call. + void setInFunctionCallOutParameter(bool inOutParameter); + bool isInFunctionCallOutParameter() const; + + bool mOperatorRequiresLValue; + bool mInFunctionCallOutParameter; + + struct TNameComparator + { + bool operator()(const TName &a, const TName &b) const + { + int compareResult = a.getString().compare(b.getString()); + if (compareResult != 0) + return compareResult < 0; + // Internal functions may have same names as non-internal functions. + return !a.isInternal() && b.isInternal(); + } + }; + + // Map from mangled function names to their parameter sequences + TMap<TName, TIntermSequence *, TNameComparator> mFunctionMap; + + const TSymbolTable &mSymbolTable; + const int mShaderVersion; }; // @@ -659,15 +943,15 @@ class TMaxDepthTraverser : public TIntermTraverser public: POOL_ALLOCATOR_NEW_DELETE(); TMaxDepthTraverser(int depthLimit) - : TIntermTraverser(true, true, false, false), + : TIntermTraverser(true, true, false), mDepthLimit(depthLimit) { } - virtual bool visitBinary(Visit, TIntermBinary *) { return depthCheck(); } - virtual bool visitUnary(Visit, TIntermUnary *) { return depthCheck(); } - virtual bool visitSelection(Visit, TIntermSelection *) { return depthCheck(); } - virtual bool visitAggregate(Visit, TIntermAggregate *) { return depthCheck(); } - virtual bool visitLoop(Visit, TIntermLoop *) { return depthCheck(); } - virtual bool visitBranch(Visit, TIntermBranch *) { return depthCheck(); } + bool visitBinary(Visit, TIntermBinary *) override { return depthCheck(); } + bool visitUnary(Visit, TIntermUnary *) override { return depthCheck(); } + bool visitSelection(Visit, TIntermSelection *) override { return depthCheck(); } + bool visitAggregate(Visit, TIntermAggregate *) override { return depthCheck(); } + bool visitLoop(Visit, TIntermLoop *) override { return depthCheck(); } + bool visitBranch(Visit, TIntermBranch *) override { return depthCheck(); } protected: bool depthCheck() const { return mMaxDepth < mDepthLimit; } diff --git a/src/3rdparty/angle/src/compiler/translator/IntermTraverse.cpp b/src/3rdparty/angle/src/compiler/translator/IntermTraverse.cpp index 7a7efb71f5..7b588ca5a3 100644 --- a/src/3rdparty/angle/src/compiler/translator/IntermTraverse.cpp +++ b/src/3rdparty/angle/src/compiler/translator/IntermTraverse.cpp @@ -5,6 +5,187 @@ // #include "compiler/translator/IntermNode.h" +#include "compiler/translator/InfoSink.h" +#include "compiler/translator/SymbolTable.h" + +void TIntermSymbol::traverse(TIntermTraverser *it) +{ + it->traverseSymbol(this); +} + +void TIntermRaw::traverse(TIntermTraverser *it) +{ + it->traverseRaw(this); +} + +void TIntermConstantUnion::traverse(TIntermTraverser *it) +{ + it->traverseConstantUnion(this); +} + +void TIntermBinary::traverse(TIntermTraverser *it) +{ + it->traverseBinary(this); +} + +void TIntermUnary::traverse(TIntermTraverser *it) +{ + it->traverseUnary(this); +} + +void TIntermSelection::traverse(TIntermTraverser *it) +{ + it->traverseSelection(this); +} + +void TIntermSwitch::traverse(TIntermTraverser *it) +{ + it->traverseSwitch(this); +} + +void TIntermCase::traverse(TIntermTraverser *it) +{ + it->traverseCase(this); +} + +void TIntermAggregate::traverse(TIntermTraverser *it) +{ + it->traverseAggregate(this); +} + +void TIntermLoop::traverse(TIntermTraverser *it) +{ + it->traverseLoop(this); +} + +void TIntermBranch::traverse(TIntermTraverser *it) +{ + it->traverseBranch(this); +} + +void TIntermTraverser::pushParentBlock(TIntermAggregate *node) +{ + mParentBlockStack.push_back(ParentBlock(node, 0)); +} + +void TIntermTraverser::incrementParentBlockPos() +{ + ++mParentBlockStack.back().pos; +} + +void TIntermTraverser::popParentBlock() +{ + ASSERT(!mParentBlockStack.empty()); + mParentBlockStack.pop_back(); +} + +void TIntermTraverser::insertStatementsInParentBlock(const TIntermSequence &insertions) +{ + TIntermSequence emptyInsertionsAfter; + insertStatementsInParentBlock(insertions, emptyInsertionsAfter); +} + +void TIntermTraverser::insertStatementsInParentBlock(const TIntermSequence &insertionsBefore, + const TIntermSequence &insertionsAfter) +{ + ASSERT(!mParentBlockStack.empty()); + NodeInsertMultipleEntry insert(mParentBlockStack.back().node, mParentBlockStack.back().pos, + insertionsBefore, insertionsAfter); + mInsertions.push_back(insert); +} + +TIntermSymbol *TIntermTraverser::createTempSymbol(const TType &type, TQualifier qualifier) +{ + // Each traversal uses at most one temporary variable, so the index stays the same within a single traversal. + TInfoSinkBase symbolNameOut; + ASSERT(mTemporaryIndex != nullptr); + symbolNameOut << "s" << (*mTemporaryIndex); + TString symbolName = symbolNameOut.c_str(); + + TIntermSymbol *node = new TIntermSymbol(0, symbolName, type); + node->setInternal(true); + node->getTypePointer()->setQualifier(qualifier); + return node; +} + +TIntermSymbol *TIntermTraverser::createTempSymbol(const TType &type) +{ + return createTempSymbol(type, EvqTemporary); +} + +TIntermAggregate *TIntermTraverser::createTempDeclaration(const TType &type) +{ + TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration); + tempDeclaration->getSequence()->push_back(createTempSymbol(type)); + return tempDeclaration; +} + +TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier) +{ + ASSERT(initializer != nullptr); + TIntermSymbol *tempSymbol = createTempSymbol(initializer->getType(), qualifier); + TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration); + TIntermBinary *tempInit = new TIntermBinary(EOpInitialize); + tempInit->setLeft(tempSymbol); + tempInit->setRight(initializer); + tempInit->setType(tempSymbol->getType()); + tempDeclaration->getSequence()->push_back(tempInit); + return tempDeclaration; +} + +TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer) +{ + return createTempInitDeclaration(initializer, EvqTemporary); +} + +TIntermBinary *TIntermTraverser::createTempAssignment(TIntermTyped *rightNode) +{ + ASSERT(rightNode != nullptr); + TIntermSymbol *tempSymbol = createTempSymbol(rightNode->getType()); + TIntermBinary *assignment = new TIntermBinary(EOpAssign); + assignment->setLeft(tempSymbol); + assignment->setRight(rightNode); + assignment->setType(tempSymbol->getType()); + return assignment; +} + +void TIntermTraverser::useTemporaryIndex(unsigned int *temporaryIndex) +{ + mTemporaryIndex = temporaryIndex; +} + +void TIntermTraverser::nextTemporaryIndex() +{ + ASSERT(mTemporaryIndex != nullptr); + ++(*mTemporaryIndex); +} + +void TLValueTrackingTraverser::addToFunctionMap(const TName &name, TIntermSequence *paramSequence) +{ + mFunctionMap[name] = paramSequence; +} + +bool TLValueTrackingTraverser::isInFunctionMap(const TIntermAggregate *callNode) const +{ + ASSERT(callNode->getOp() == EOpFunctionCall); + return (mFunctionMap.find(callNode->getNameObj()) != mFunctionMap.end()); +} + +TIntermSequence *TLValueTrackingTraverser::getFunctionParameters(const TIntermAggregate *callNode) +{ + ASSERT(isInFunctionMap(callNode)); + return mFunctionMap[callNode->getNameObj()]; +} + +void TLValueTrackingTraverser::setInFunctionCallOutParameter(bool inOutParameter) +{ + mInFunctionCallOutParameter = inOutParameter; +} + +bool TLValueTrackingTraverser::isInFunctionCallOutParameter() const +{ + return mInFunctionCallOutParameter; +} // // Traverse the intermediate representation tree, and @@ -16,308 +197,473 @@ // if preVisit is turned on and the type specific function // returns false. // -// preVisit, postVisit, and rightToLeft control what order -// nodes are visited in. -// // // Traversal functions for terminals are straighforward.... // -void TIntermSymbol::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseSymbol(TIntermSymbol *node) { - it->visitSymbol(this); + visitSymbol(node); } -void TIntermConstantUnion::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseConstantUnion(TIntermConstantUnion *node) { - it->visitConstantUnion(this); + visitConstantUnion(node); } // // Traverse a binary node. // -void TIntermBinary::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseBinary(TIntermBinary *node) { bool visit = true; // // visit the node before children if pre-visiting. // - if (it->preVisit) - visit = it->visitBinary(PreVisit, this); + if (preVisit) + visit = visitBinary(PreVisit, node); // // Visit the children, in the right order. // if (visit) { - it->incrementDepth(this); + incrementDepth(node); - if (it->rightToLeft) - { - if (mRight) - mRight->traverse(it); + if (node->getLeft()) + node->getLeft()->traverse(this); - if (it->inVisit) - visit = it->visitBinary(InVisit, this); + if (inVisit) + visit = visitBinary(InVisit, node); - if (visit && mLeft) - mLeft->traverse(it); - } - else + if (visit && node->getRight()) + node->getRight()->traverse(this); + + decrementDepth(); + } + + // + // Visit the node after the children, if requested and the traversal + // hasn't been cancelled yet. + // + if (visit && postVisit) + visitBinary(PostVisit, node); +} + +void TLValueTrackingTraverser::traverseBinary(TIntermBinary *node) +{ + bool visit = true; + + // + // visit the node before children if pre-visiting. + // + if (preVisit) + visit = visitBinary(PreVisit, node); + + // + // Visit the children, in the right order. + // + if (visit) + { + incrementDepth(node); + + // Some binary operations like indexing can be inside an expression which must be an + // l-value. + bool parentOperatorRequiresLValue = operatorRequiresLValue(); + bool parentInFunctionCallOutParameter = isInFunctionCallOutParameter(); + if (node->isAssignment()) { - if (mLeft) - mLeft->traverse(it); + ASSERT(!isLValueRequiredHere()); + setOperatorRequiresLValue(true); + } - if (it->inVisit) - visit = it->visitBinary(InVisit, this); + if (node->getLeft()) + node->getLeft()->traverse(this); - if (visit && mRight) - mRight->traverse(it); + if (inVisit) + visit = visitBinary(InVisit, node); + + if (node->isAssignment()) + setOperatorRequiresLValue(false); + + // Index is not required to be an l-value even when the surrounding expression is required + // to be an l-value. + TOperator op = node->getOp(); + if (op == EOpIndexDirect || op == EOpIndexDirectInterfaceBlock || + op == EOpIndexDirectStruct || op == EOpIndexIndirect) + { + setOperatorRequiresLValue(false); + setInFunctionCallOutParameter(false); } - it->decrementDepth(); + if (visit && node->getRight()) + node->getRight()->traverse(this); + + setOperatorRequiresLValue(parentOperatorRequiresLValue); + setInFunctionCallOutParameter(parentInFunctionCallOutParameter); + + decrementDepth(); } // // Visit the node after the children, if requested and the traversal // hasn't been cancelled yet. // - if (visit && it->postVisit) - it->visitBinary(PostVisit, this); + if (visit && postVisit) + visitBinary(PostVisit, node); } // // Traverse a unary node. Same comments in binary node apply here. // -void TIntermUnary::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseUnary(TIntermUnary *node) { bool visit = true; - if (it->preVisit) - visit = it->visitUnary(PreVisit, this); + if (preVisit) + visit = visitUnary(PreVisit, node); + + if (visit) + { + incrementDepth(node); - if (visit) { - it->incrementDepth(this); - mOperand->traverse(it); - it->decrementDepth(); + node->getOperand()->traverse(this); + + decrementDepth(); } - if (visit && it->postVisit) - it->visitUnary(PostVisit, this); + if (visit && postVisit) + visitUnary(PostVisit, node); +} + +void TLValueTrackingTraverser::traverseUnary(TIntermUnary *node) +{ + bool visit = true; + + if (preVisit) + visit = visitUnary(PreVisit, node); + + if (visit) + { + incrementDepth(node); + + ASSERT(!operatorRequiresLValue()); + switch (node->getOp()) + { + case EOpPostIncrement: + case EOpPostDecrement: + case EOpPreIncrement: + case EOpPreDecrement: + setOperatorRequiresLValue(true); + break; + default: + break; + } + + node->getOperand()->traverse(this); + + setOperatorRequiresLValue(false); + + decrementDepth(); + } + + if (visit && postVisit) + visitUnary(PostVisit, node); } // // Traverse an aggregate node. Same comments in binary node apply here. // -void TIntermAggregate::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseAggregate(TIntermAggregate *node) { bool visit = true; - if (it->preVisit) - visit = it->visitAggregate(PreVisit, this); + TIntermSequence *sequence = node->getSequence(); + + if (preVisit) + visit = visitAggregate(PreVisit, node); if (visit) { - it->incrementDepth(this); + incrementDepth(node); - if (it->rightToLeft) + if (node->getOp() == EOpSequence) + pushParentBlock(node); + + for (auto *child : *sequence) { - for (TIntermSequence::reverse_iterator sit = mSequence.rbegin(); - sit != mSequence.rend(); sit++) + child->traverse(this); + if (visit && inVisit) { - (*sit)->traverse(it); + if (child != sequence->back()) + visit = visitAggregate(InVisit, node); + } + + if (node->getOp() == EOpSequence) + incrementParentBlockPos(); + } + + if (node->getOp() == EOpSequence) + popParentBlock(); + + decrementDepth(); + } - if (visit && it->inVisit) + if (visit && postVisit) + visitAggregate(PostVisit, node); +} + +void TLValueTrackingTraverser::traverseAggregate(TIntermAggregate *node) +{ + bool visit = true; + + TIntermSequence *sequence = node->getSequence(); + switch (node->getOp()) + { + case EOpFunction: + { + TIntermAggregate *params = sequence->front()->getAsAggregate(); + ASSERT(params != nullptr); + ASSERT(params->getOp() == EOpParameters); + addToFunctionMap(node->getNameObj(), params->getSequence()); + break; + } + case EOpPrototype: + addToFunctionMap(node->getNameObj(), sequence); + break; + default: + break; + } + + if (preVisit) + visit = visitAggregate(PreVisit, node); + + if (visit) + { + bool inFunctionMap = false; + if (node->getOp() == EOpFunctionCall) + { + inFunctionMap = isInFunctionMap(node); + if (!inFunctionMap) + { + // The function is not user-defined - it is likely built-in texture function. + // Assume that those do not have out parameters. + setInFunctionCallOutParameter(false); + } + } + + incrementDepth(node); + + if (inFunctionMap) + { + TIntermSequence *params = getFunctionParameters(node); + TIntermSequence::iterator paramIter = params->begin(); + for (auto *child : *sequence) + { + ASSERT(paramIter != params->end()); + TQualifier qualifier = (*paramIter)->getAsTyped()->getQualifier(); + setInFunctionCallOutParameter(qualifier == EvqOut || qualifier == EvqInOut); + + child->traverse(this); + if (visit && inVisit) { - if (*sit != mSequence.front()) - visit = it->visitAggregate(InVisit, this); + if (child != sequence->back()) + visit = visitAggregate(InVisit, node); } + + ++paramIter; } + + setInFunctionCallOutParameter(false); } else { - for (TIntermSequence::iterator sit = mSequence.begin(); - sit != mSequence.end(); sit++) + if (node->getOp() == EOpSequence) + pushParentBlock(node); + + // Find the built-in function corresponding to this op so that we can determine the + // in/out qualifiers of its parameters. + TFunction *builtInFunc = nullptr; + TString opString = GetOperatorString(node->getOp()); + if (!node->isConstructor() && !opString.empty()) + { + // The return type doesn't affect the mangled name of the function, which is used + // to look it up from the symbol table. + TType dummyReturnType; + TFunction call(&opString, &dummyReturnType, node->getOp()); + for (auto *child : *sequence) + { + TType *paramType = child->getAsTyped()->getTypePointer(); + TConstParameter p(paramType); + call.addParameter(p); + } + + TSymbol *sym = mSymbolTable.findBuiltIn(call.getMangledName(), mShaderVersion); + if (sym != nullptr && sym->isFunction()) + { + builtInFunc = static_cast<TFunction *>(sym); + ASSERT(builtInFunc->getParamCount() == sequence->size()); + } + } + + size_t paramIndex = 0; + + for (auto *child : *sequence) { - (*sit)->traverse(it); + TQualifier qualifier = EvqIn; + if (builtInFunc != nullptr) + qualifier = builtInFunc->getParam(paramIndex).type->getQualifier(); + setInFunctionCallOutParameter(qualifier == EvqOut || qualifier == EvqInOut); + child->traverse(this); - if (visit && it->inVisit) + if (visit && inVisit) { - if (*sit != mSequence.back()) - visit = it->visitAggregate(InVisit, this); + if (child != sequence->back()) + visit = visitAggregate(InVisit, node); } + + if (node->getOp() == EOpSequence) + incrementParentBlockPos(); + + ++paramIndex; } + + setInFunctionCallOutParameter(false); + + if (node->getOp() == EOpSequence) + popParentBlock(); } - it->decrementDepth(); + decrementDepth(); } - if (visit && it->postVisit) - it->visitAggregate(PostVisit, this); + if (visit && postVisit) + visitAggregate(PostVisit, node); } // // Traverse a selection node. Same comments in binary node apply here. // -void TIntermSelection::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseSelection(TIntermSelection *node) { bool visit = true; - if (it->preVisit) - visit = it->visitSelection(PreVisit, this); + if (preVisit) + visit = visitSelection(PreVisit, node); if (visit) { - it->incrementDepth(this); - if (it->rightToLeft) - { - if (mFalseBlock) - mFalseBlock->traverse(it); - if (mTrueBlock) - mTrueBlock->traverse(it); - mCondition->traverse(it); - } - else - { - mCondition->traverse(it); - if (mTrueBlock) - mTrueBlock->traverse(it); - if (mFalseBlock) - mFalseBlock->traverse(it); - } - it->decrementDepth(); + incrementDepth(node); + node->getCondition()->traverse(this); + if (node->getTrueBlock()) + node->getTrueBlock()->traverse(this); + if (node->getFalseBlock()) + node->getFalseBlock()->traverse(this); + decrementDepth(); } - if (visit && it->postVisit) - it->visitSelection(PostVisit, this); + if (visit && postVisit) + visitSelection(PostVisit, node); } // // Traverse a switch node. Same comments in binary node apply here. // -void TIntermSwitch::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseSwitch(TIntermSwitch *node) { bool visit = true; - if (it->preVisit) - visit = it->visitSwitch(PreVisit, this); + if (preVisit) + visit = visitSwitch(PreVisit, node); if (visit) { - it->incrementDepth(this); - if (it->rightToLeft) - { - if (mStatementList) - mStatementList->traverse(it); - if (it->inVisit) - visit = it->visitSwitch(InVisit, this); - if (visit) - mInit->traverse(it); - } - else - { - mInit->traverse(it); - if (it->inVisit) - visit = it->visitSwitch(InVisit, this); - if (visit && mStatementList) - mStatementList->traverse(it); - } - it->decrementDepth(); + incrementDepth(node); + node->getInit()->traverse(this); + if (inVisit) + visit = visitSwitch(InVisit, node); + if (visit && node->getStatementList()) + node->getStatementList()->traverse(this); + decrementDepth(); } - if (visit && it->postVisit) - it->visitSwitch(PostVisit, this); + if (visit && postVisit) + visitSwitch(PostVisit, node); } // -// Traverse a switch node. Same comments in binary node apply here. +// Traverse a case node. Same comments in binary node apply here. // -void TIntermCase::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseCase(TIntermCase *node) { bool visit = true; - if (it->preVisit) - visit = it->visitCase(PreVisit, this); + if (preVisit) + visit = visitCase(PreVisit, node); - if (visit && mCondition) - mCondition->traverse(it); + if (visit && node->getCondition()) + node->getCondition()->traverse(this); - if (visit && it->postVisit) - it->visitCase(PostVisit, this); + if (visit && postVisit) + visitCase(PostVisit, node); } // // Traverse a loop node. Same comments in binary node apply here. // -void TIntermLoop::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseLoop(TIntermLoop *node) { bool visit = true; - if (it->preVisit) - visit = it->visitLoop(PreVisit, this); + if (preVisit) + visit = visitLoop(PreVisit, node); if (visit) { - it->incrementDepth(this); - - if (it->rightToLeft) - { - if (mExpr) - mExpr->traverse(it); - - if (mBody) - mBody->traverse(it); - - if (mCond) - mCond->traverse(it); + incrementDepth(node); - if (mInit) - mInit->traverse(it); - } - else - { - if (mInit) - mInit->traverse(it); + if (node->getInit()) + node->getInit()->traverse(this); - if (mCond) - mCond->traverse(it); + if (node->getCondition()) + node->getCondition()->traverse(this); - if (mBody) - mBody->traverse(it); + if (node->getBody()) + node->getBody()->traverse(this); - if (mExpr) - mExpr->traverse(it); - } + if (node->getExpression()) + node->getExpression()->traverse(this); - it->decrementDepth(); + decrementDepth(); } - if (visit && it->postVisit) - it->visitLoop(PostVisit, this); + if (visit && postVisit) + visitLoop(PostVisit, node); } // // Traverse a branch node. Same comments in binary node apply here. // -void TIntermBranch::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseBranch(TIntermBranch *node) { bool visit = true; - if (it->preVisit) - visit = it->visitBranch(PreVisit, this); + if (preVisit) + visit = visitBranch(PreVisit, node); - if (visit && mExpression) { - it->incrementDepth(this); - mExpression->traverse(it); - it->decrementDepth(); + if (visit && node->getExpression()) + { + incrementDepth(node); + node->getExpression()->traverse(this); + decrementDepth(); } - if (visit && it->postVisit) - it->visitBranch(PostVisit, this); + if (visit && postVisit) + visitBranch(PostVisit, node); } -void TIntermRaw::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseRaw(TIntermRaw *node) { - it->visitRaw(this); + visitRaw(node); } diff --git a/src/3rdparty/angle/src/compiler/translator/Intermediate.cpp b/src/3rdparty/angle/src/compiler/translator/Intermediate.cpp index 320056f8ce..0adb7212b7 100644 --- a/src/3rdparty/angle/src/compiler/translator/Intermediate.cpp +++ b/src/3rdparty/angle/src/compiler/translator/Intermediate.cpp @@ -57,19 +57,10 @@ TIntermTyped *TIntermediate::addBinaryMath( if (!node->promote(mInfoSink)) return NULL; - // // See if we can fold constants. - // - TIntermConstantUnion *leftTempConstant = left->getAsConstantUnion(); - TIntermConstantUnion *rightTempConstant = right->getAsConstantUnion(); - if (leftTempConstant && rightTempConstant) - { - TIntermTyped *typedReturnNode = - leftTempConstant->fold(node->getOp(), rightTempConstant, mInfoSink); - - if (typedReturnNode) - return typedReturnNode; - } + TIntermTyped *foldedNode = node->fold(mInfoSink); + if (foldedNode) + return foldedNode; return node; } @@ -129,10 +120,6 @@ TIntermTyped *TIntermediate::addIndex( TIntermTyped *TIntermediate::addUnaryMath( TOperator op, TIntermTyped *child, const TSourceLoc &line, const TType *funcReturnType) { - TIntermConstantUnion *childTempConstant = 0; - if (child->getAsConstantUnion()) - childTempConstant = child->getAsConstantUnion(); - // // Make a new node for the operator. // @@ -141,13 +128,9 @@ TIntermTyped *TIntermediate::addUnaryMath( node->setOperand(child); node->promote(funcReturnType); - if (childTempConstant) - { - TIntermTyped *newChild = childTempConstant->fold(op, 0, mInfoSink); - - if (newChild) - return newChild; - } + TIntermTyped *foldedNode = node->fold(mInfoSink); + if (foldedNode) + return foldedNode; return node; } @@ -246,6 +229,22 @@ TIntermAggregate *TIntermediate::makeAggregate( return aggNode; } +// If the input node is nullptr, return nullptr. +// If the input node is a sequence (block) node, return it. +// If the input node is not a sequence node, put it inside a sequence node and return that. +TIntermAggregate *TIntermediate::ensureSequence(TIntermNode *node) +{ + if (node == nullptr) + return nullptr; + TIntermAggregate *aggNode = node->getAsAggregate(); + if (aggNode != nullptr && aggNode->getOp() == EOpSequence) + return aggNode; + + aggNode = makeAggregate(node, node->getLine()); + aggNode->setOp(EOpSequence); + return aggNode; +} + // // For "if" test nodes. There are three children; a condition, // a true path, and a false path. The two paths are in the @@ -261,7 +260,7 @@ TIntermNode *TIntermediate::addSelection( // test now. // - if (cond->getAsTyped() && cond->getAsTyped()->getAsConstantUnion()) + if (cond->getAsConstantUnion()) { if (cond->getAsConstantUnion()->getBConst(0) == true) { @@ -276,28 +275,38 @@ TIntermNode *TIntermediate::addSelection( } TIntermSelection *node = new TIntermSelection( - cond, nodePair.node1, nodePair.node2); + cond, ensureSequence(nodePair.node1), ensureSequence(nodePair.node2)); node->setLine(line); return node; } -TIntermTyped *TIntermediate::addComma( - TIntermTyped *left, TIntermTyped *right, const TSourceLoc &line) +TIntermTyped *TIntermediate::addComma(TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &line, + int shaderVersion) { - if (left->getType().getQualifier() == EvqConst && - right->getType().getQualifier() == EvqConst) + TQualifier resultQualifier = EvqConst; + // ESSL3.00 section 12.43: The result of a sequence operator is not a constant-expression. + if (shaderVersion >= 300 || left->getQualifier() != EvqConst || + right->getQualifier() != EvqConst) { - return right; + resultQualifier = EvqTemporary; + } + + TIntermTyped *commaNode = nullptr; + if (!left->hasSideEffects()) + { + commaNode = right; } else { - TIntermTyped *commaAggregate = growAggregate(left, right, line); - commaAggregate->getAsAggregate()->setOp(EOpComma); - commaAggregate->setType(right->getType()); - commaAggregate->getTypePointer()->setQualifier(EvqTemporary); - return commaAggregate; + commaNode = growAggregate(left, right, line); + commaNode->getAsAggregate()->setOp(EOpComma); + commaNode->setType(right->getType()); } + commaNode->getTypePointer()->setQualifier(resultQualifier); + return commaNode; } // @@ -305,38 +314,38 @@ TIntermTyped *TIntermediate::addComma( // a true path, and a false path. The two paths are specified // as separate parameters. // -// Returns the selection node created, or 0 if one could not be. +// Returns the selection node created, or one of trueBlock and falseBlock if the expression could be folded. // -TIntermTyped *TIntermediate::addSelection( - TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, - const TSourceLoc &line) +TIntermTyped *TIntermediate::addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, + const TSourceLoc &line) { - if (!cond || !trueBlock || !falseBlock || - trueBlock->getType() != falseBlock->getType()) + TQualifier resultQualifier = EvqTemporary; + if (cond->getQualifier() == EvqConst && trueBlock->getQualifier() == EvqConst && + falseBlock->getQualifier() == EvqConst) { - return NULL; + resultQualifier = EvqConst; } - - // - // See if all the operands are constant, then fold it otherwise not. - // - - if (cond->getAsConstantUnion() && - trueBlock->getAsConstantUnion() && - falseBlock->getAsConstantUnion()) + // Note that the node resulting from here can be a constant union without being qualified as + // constant. + if (cond->getAsConstantUnion()) { if (cond->getAsConstantUnion()->getBConst(0)) + { + trueBlock->getTypePointer()->setQualifier(resultQualifier); return trueBlock; + } else + { + falseBlock->getTypePointer()->setQualifier(resultQualifier); return falseBlock; + } } // // Make a selection node. // - TIntermSelection *node = new TIntermSelection( - cond, trueBlock, falseBlock, trueBlock->getType()); - node->getTypePointer()->setQualifier(EvqTemporary); + TIntermSelection *node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType()); + node->getTypePointer()->setQualifier(resultQualifier); node->setLine(line); return node; @@ -366,10 +375,11 @@ TIntermCase *TIntermediate::addCase( // Returns the constant union node created. // -TIntermConstantUnion *TIntermediate::addConstantUnion( - ConstantUnion *unionArrayPointer, const TType &t, const TSourceLoc &line) +TIntermConstantUnion *TIntermediate::addConstantUnion(const TConstantUnion *constantUnion, + const TType &type, + const TSourceLoc &line) { - TIntermConstantUnion *node = new TIntermConstantUnion(unionArrayPointer, t); + TIntermConstantUnion *node = new TIntermConstantUnion(constantUnion, type); node->setLine(line); return node; @@ -384,11 +394,11 @@ TIntermTyped *TIntermediate::addSwizzle( node->setLine(line); TIntermConstantUnion *constIntNode; TIntermSequence *sequenceVector = node->getSequence(); - ConstantUnion *unionArray; + TConstantUnion *unionArray; for (int i = 0; i < fields.num; i++) { - unionArray = new ConstantUnion[1]; + unionArray = new TConstantUnion[1]; unionArray->setIConst(fields.offsets[i]); constIntNode = addConstantUnion( unionArray, TType(EbtInt, EbpUndefined, EvqConst), line); @@ -405,7 +415,7 @@ TIntermNode *TIntermediate::addLoop( TLoopType type, TIntermNode *init, TIntermTyped *cond, TIntermTyped *expr, TIntermNode *body, const TSourceLoc &line) { - TIntermNode *node = new TIntermLoop(type, init, cond, expr, body); + TIntermNode *node = new TIntermLoop(type, init, cond, expr, ensureSequence(body)); node->setLine(line); return node; @@ -433,17 +443,66 @@ TIntermBranch* TIntermediate::addBranch( // This is to be executed once the final root is put on top by the parsing // process. // -bool TIntermediate::postProcess(TIntermNode *root) +TIntermAggregate *TIntermediate::postProcess(TIntermNode *root) { - if (root == NULL) - return true; + if (root == nullptr) + return nullptr; // - // First, finish off the top level sequence, if any + // Finish off the top level sequence, if any // TIntermAggregate *aggRoot = root->getAsAggregate(); - if (aggRoot && aggRoot->getOp() == EOpNull) + if (aggRoot != nullptr && aggRoot->getOp() == EOpNull) + { aggRoot->setOp(EOpSequence); + } + else if (aggRoot == nullptr || aggRoot->getOp() != EOpSequence) + { + aggRoot = new TIntermAggregate(EOpSequence); + aggRoot->setLine(root->getLine()); + aggRoot->getSequence()->push_back(root); + } + + return aggRoot; +} + +TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate) +{ + switch (aggregate->getOp()) + { + case EOpAtan: + case EOpPow: + case EOpMod: + case EOpMin: + case EOpMax: + case EOpClamp: + case EOpMix: + case EOpStep: + case EOpSmoothStep: + case EOpMul: + case EOpOuterProduct: + case EOpLessThan: + case EOpLessThanEqual: + case EOpGreaterThan: + case EOpGreaterThanEqual: + case EOpVectorEqual: + case EOpVectorNotEqual: + case EOpDistance: + case EOpDot: + case EOpCross: + case EOpFaceForward: + case EOpReflect: + case EOpRefract: + return aggregate->fold(mInfoSink); + default: + // TODO: Add support for folding array constructors + if (aggregate->isConstructor() && !aggregate->isArray()) + { + return aggregate->fold(mInfoSink); + } + // Constant folding not supported for the built-in. + return nullptr; + } - return true; + return nullptr; } diff --git a/src/3rdparty/angle/src/compiler/translator/Intermediate.h b/src/3rdparty/angle/src/compiler/translator/Intermediate.h index ec73e22834..f723fc7648 100644 --- a/src/3rdparty/angle/src/compiler/translator/Intermediate.h +++ b/src/3rdparty/angle/src/compiler/translator/Intermediate.h @@ -39,29 +39,33 @@ class TIntermediate TIntermAggregate *growAggregate( TIntermNode *left, TIntermNode *right, const TSourceLoc &); TIntermAggregate *makeAggregate(TIntermNode *node, const TSourceLoc &); + TIntermAggregate *ensureSequence(TIntermNode *node); TIntermAggregate *setAggregateOperator(TIntermNode *, TOperator, const TSourceLoc &); TIntermNode *addSelection(TIntermTyped *cond, TIntermNodePair code, const TSourceLoc &); - TIntermTyped *addSelection( - TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, const TSourceLoc &); + TIntermTyped *addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, + const TSourceLoc &line); TIntermSwitch *addSwitch( TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &line); TIntermCase *addCase( TIntermTyped *condition, const TSourceLoc &line); - TIntermTyped *addComma( - TIntermTyped *left, TIntermTyped *right, const TSourceLoc &); - TIntermConstantUnion *addConstantUnion(ConstantUnion *, const TType &, const TSourceLoc &); - // TODO(zmo): Get rid of default value. - bool parseConstTree(const TSourceLoc &, TIntermNode *, ConstantUnion *, - TOperator, TType, bool singleConstantParam = false); + TIntermTyped *addComma(TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &line, + int shaderVersion); + TIntermConstantUnion *addConstantUnion(const TConstantUnion *constantUnion, + const TType &type, + const TSourceLoc &line); TIntermNode *addLoop(TLoopType, TIntermNode *, TIntermTyped *, TIntermTyped *, TIntermNode *, const TSourceLoc &); TIntermBranch *addBranch(TOperator, const TSourceLoc &); TIntermBranch *addBranch(TOperator, TIntermTyped *, const TSourceLoc &); TIntermTyped *addSwizzle(TVectorFields &, const TSourceLoc &); - bool postProcess(TIntermNode *); + TIntermAggregate *postProcess(TIntermNode *root); static void outputTree(TIntermNode *, TInfoSinkBase &); + TIntermTyped *foldAggregateBuiltIn(TIntermAggregate *aggregate); + private: void operator=(TIntermediate &); // prevent assignments diff --git a/src/3rdparty/angle/src/compiler/translator/NodeSearch.h b/src/3rdparty/angle/src/compiler/translator/NodeSearch.h index 8ffed614c3..b13b1baabb 100644 --- a/src/3rdparty/angle/src/compiler/translator/NodeSearch.h +++ b/src/3rdparty/angle/src/compiler/translator/NodeSearch.h @@ -19,7 +19,8 @@ class NodeSearchTraverser : public TIntermTraverser { public: NodeSearchTraverser() - : mFound(false) + : TIntermTraverser(true, false, false), + mFound(false) {} bool found() const { return mFound; } @@ -53,28 +54,6 @@ class FindDiscard : public NodeSearchTraverser<FindDiscard> } }; -class FindSideEffectRewriting : public NodeSearchTraverser<FindSideEffectRewriting> -{ - public: - virtual bool visitBinary(Visit visit, TIntermBinary *node) - { - switch (node->getOp()) - { - case EOpLogicalOr: - case EOpLogicalAnd: - if (node->getRight()->hasSideEffects()) - { - mFound = true; - } - break; - - default: break; - } - - return !mFound; - } -}; - } #endif // COMPILER_TRANSLATOR_NODESEARCH_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/Operator.cpp b/src/3rdparty/angle/src/compiler/translator/Operator.cpp index ae4512bd44..20e47f290e 100644 --- a/src/3rdparty/angle/src/compiler/translator/Operator.cpp +++ b/src/3rdparty/angle/src/compiler/translator/Operator.cpp @@ -164,7 +164,13 @@ const char *GetOperatorString(TOperator op) case EOpConstructUVec3: return "uvec3"; case EOpConstructUVec4: return "uvec4"; case EOpConstructMat2: return "mat2"; + case EOpConstructMat2x3: return "mat2x3"; + case EOpConstructMat2x4: return "mat2x4"; + case EOpConstructMat3x2: return "mat3x2"; case EOpConstructMat3: return "mat3"; + case EOpConstructMat3x4: return "mat3x4"; + case EOpConstructMat4x2: return "mat4x2"; + case EOpConstructMat4x3: return "mat4x3"; case EOpConstructMat4: return "mat4"; // Note: EOpConstructStruct can't be handled here diff --git a/src/3rdparty/angle/src/compiler/translator/Operator.h b/src/3rdparty/angle/src/compiler/translator/Operator.h index 8290f952fc..b0efb8f48b 100644 --- a/src/3rdparty/angle/src/compiler/translator/Operator.h +++ b/src/3rdparty/angle/src/compiler/translator/Operator.h @@ -15,7 +15,6 @@ enum TOperator EOpNull, // if in a node, should only mean a node is still being built EOpSequence, // denotes a list of statements, or parameters, etc. EOpFunctionCall, - EOpInternalFunctionCall, // Call to an internal helper function EOpFunction, // For function definition EOpParameters, // an aggregate listing the parameters to a function @@ -192,7 +191,13 @@ enum TOperator EOpConstructUVec3, EOpConstructUVec4, EOpConstructMat2, + EOpConstructMat2x3, + EOpConstructMat2x4, + EOpConstructMat3x2, EOpConstructMat3, + EOpConstructMat3x4, + EOpConstructMat4x2, + EOpConstructMat4x3, EOpConstructMat4, EOpConstructStruct, diff --git a/src/3rdparty/angle/src/compiler/translator/OutputESSL.h b/src/3rdparty/angle/src/compiler/translator/OutputESSL.h index 813f1e944b..c5a963499e 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputESSL.h +++ b/src/3rdparty/angle/src/compiler/translator/OutputESSL.h @@ -21,7 +21,8 @@ public: bool forceHighp); protected: - virtual bool writeVariablePrecision(TPrecision precision); + bool writeVariablePrecision(TPrecision precision) override; + private: bool mForceHighp; }; diff --git a/src/3rdparty/angle/src/compiler/translator/OutputGLSL.cpp b/src/3rdparty/angle/src/compiler/translator/OutputGLSL.cpp index 9badf0e2fc..431425020a 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputGLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/OutputGLSL.cpp @@ -37,14 +37,22 @@ void TOutputGLSL::visitSymbol(TIntermSymbol *node) { out << "gl_FragDepth"; } - else if (symbol == "gl_FragColor" && getShaderOutput() == SH_GLSL_CORE_OUTPUT) + else if (symbol == "gl_FragColor" && IsGLSL130OrNewer(getShaderOutput())) { out << "webgl_FragColor"; } - else if (symbol == "gl_FragData" && getShaderOutput() == SH_GLSL_CORE_OUTPUT) + else if (symbol == "gl_FragData" && IsGLSL130OrNewer(getShaderOutput())) { out << "webgl_FragData"; } + else if (symbol == "gl_SecondaryFragColorEXT") + { + out << "angle_SecondaryFragColor"; + } + else if (symbol == "gl_SecondaryFragDataEXT") + { + out << "angle_SecondaryFragData"; + } else { TOutputGLSLBase::visitSymbol(node); @@ -67,6 +75,7 @@ TString TOutputGLSL::translateTextureFunction(TString &name) "texture2DProj", "textureProj", "texture2DLod", "textureLod", "texture2DProjLod", "textureProjLod", + "texture2DRect", "texture", "textureCube", "texture", "textureCubeLod", "textureLod", // Extensions @@ -78,7 +87,7 @@ TString TOutputGLSL::translateTextureFunction(TString &name) "textureCubeGradEXT", "textureGrad", NULL, NULL }; - const char **mapping = (getShaderOutput() == SH_GLSL_CORE_OUTPUT) ? + const char **mapping = (IsGLSL130OrNewer(getShaderOutput())) ? legacyToCoreRename : simpleRename; for (int i = 0; mapping[i] != NULL; i += 2) diff --git a/src/3rdparty/angle/src/compiler/translator/OutputGLSL.h b/src/3rdparty/angle/src/compiler/translator/OutputGLSL.h index 21b2d079d3..9b1aca4eab 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputGLSL.h +++ b/src/3rdparty/angle/src/compiler/translator/OutputGLSL.h @@ -21,9 +21,9 @@ class TOutputGLSL : public TOutputGLSLBase ShShaderOutput output); protected: - virtual bool writeVariablePrecision(TPrecision); - virtual void visitSymbol(TIntermSymbol* node); - virtual TString translateTextureFunction(TString& name); + bool writeVariablePrecision(TPrecision) override; + void visitSymbol(TIntermSymbol *node) override; + TString translateTextureFunction(TString &name) override; }; #endif // COMPILER_TRANSLATOR_OUTPUTGLSL_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.cpp b/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.cpp index 4bb6305d05..f048b050b7 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.cpp +++ b/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.cpp @@ -5,7 +5,8 @@ // #include "compiler/translator/OutputGLSLBase.h" -#include "compiler/translator/compilerdebug.h" + +#include "common/debug.h" #include <cfloat> @@ -88,30 +89,46 @@ void TOutputGLSLBase::writeBuiltInFunctionTriplet( writeTriplet(visit, preString.c_str(), ", ", ")"); } +void TOutputGLSLBase::writeLayoutQualifier(const TType &type) +{ + if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn) + { + const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier(); + if (layoutQualifier.location >= 0) + { + TInfoSinkBase &out = objSink(); + out << "layout(location = " << layoutQualifier.location << ") "; + } + } +} + void TOutputGLSLBase::writeVariableType(const TType &type) { TInfoSinkBase &out = objSink(); + if (type.isInvariant()) + { + out << "invariant "; + } + if (type.getBasicType() == EbtInterfaceBlock) + { + TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); + declareInterfaceBlockLayout(interfaceBlock); + } TQualifier qualifier = type.getQualifier(); if (qualifier != EvqTemporary && qualifier != EvqGlobal) { - if (mOutput == SH_GLSL_CORE_OUTPUT) + if (IsGLSL130OrNewer(mOutput)) { switch (qualifier) { case EvqAttribute: - out << "in" << " "; + out << "in "; break; case EvqVaryingIn: - out << "in" << " "; + out << "in "; break; case EvqVaryingOut: - out << "out" << " "; - break; - case EvqInvariantVaryingIn: - out << "invariant in" << " "; - break; - case EvqInvariantVaryingOut: - out << "invariant out" << " "; + out << "out "; break; default: out << type.getQualifierString() << " "; @@ -135,6 +152,11 @@ void TOutputGLSLBase::writeVariableType(const TType &type) mDeclaredStructs.insert(structure->uniqueId()); } } + else if (type.getBasicType() == EbtInterfaceBlock) + { + TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); + declareInterfaceBlock(interfaceBlock); + } else { if (writeVariablePrecision(type.getPrecision())) @@ -167,8 +189,8 @@ void TOutputGLSLBase::writeFunctionParameters(const TIntermSequence &args) } } -const ConstantUnion *TOutputGLSLBase::writeConstantUnion( - const TType &type, const ConstantUnion *pConstUnion) +const TConstantUnion *TOutputGLSLBase::writeConstantUnion( + const TType &type, const TConstantUnion *pConstUnion) { TInfoSinkBase &out = objSink(); @@ -221,6 +243,28 @@ const ConstantUnion *TOutputGLSLBase::writeConstantUnion( return pConstUnion; } +void TOutputGLSLBase::writeConstructorTriplet(Visit visit, const TType &type, const char *constructorBaseType) +{ + TInfoSinkBase &out = objSink(); + if (visit == PreVisit) + { + if (type.isArray()) + { + out << constructorBaseType; + out << arrayBrackets(type); + out << "("; + } + else + { + out << constructorBaseType << "("; + } + } + else + { + writeTriplet(visit, nullptr, ", ", ")"); + } +} + void TOutputGLSLBase::visitSymbol(TIntermSymbol *node) { TInfoSinkBase &out = objSink(); @@ -352,6 +396,22 @@ bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary *node) visitChildren = false; } break; + case EOpIndexDirectInterfaceBlock: + if (visit == InVisit) + { + out << "."; + const TInterfaceBlock *interfaceBlock = node->getLeft()->getType().getInterfaceBlock(); + const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion(); + const TField *field = interfaceBlock->fields()[index->getIConst(0)]; + + TString fieldName = field->name(); + ASSERT(!mSymbolTable.findBuiltIn(interfaceBlock->name(), mShaderVersion)); + fieldName = hashName(fieldName); + + out << fieldName; + visitChildren = false; + } + break; case EOpVectorSwizzle: if (visit == InVisit) { @@ -363,7 +423,7 @@ bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary *node) TIntermConstantUnion *element = (*sit)->getAsConstantUnion(); ASSERT(element->getBasicType() == EbtInt); ASSERT(element->getNominalSize() == 1); - const ConstantUnion& data = element->getUnionArrayPointer()[0]; + const TConstantUnion& data = element->getUnionArrayPointer()[0]; ASSERT(data.getType() == EbtInt); switch (data.getIConst()) { @@ -722,7 +782,6 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) { bool visitChildren = true; TInfoSinkBase &out = objSink(); - TString preString; bool useEmulatedFunction = (visit == PreVisit && node->getUseEmulatedFunction()); switch (node->getOp()) { @@ -756,8 +815,14 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) case EOpPrototype: // Function declaration. ASSERT(visit == PreVisit); - writeVariableType(node->getType()); - out << " " << hashFunctionName(node->getName()); + { + const TType &type = node->getType(); + writeVariableType(type); + if (type.isArray()) + out << arrayBrackets(type); + } + + out << " " << hashFunctionNameIfNeeded(node->getNameObj()); out << "("; writeFunctionParameters(*(node->getSequence())); @@ -768,8 +833,14 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) case EOpFunction: { // Function definition. ASSERT(visit == PreVisit); - writeVariableType(node->getType()); - out << " " << hashFunctionName(node->getName()); + { + const TType &type = node->getType(); + writeVariableType(type); + if (type.isArray()) + out << arrayBrackets(type); + } + + out << " " << hashFunctionNameIfNeeded(node->getNameObj()); incrementDepth(node); // Function definition node contains one or two children nodes @@ -798,16 +869,7 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) case EOpFunctionCall: // Function call. if (visit == PreVisit) - out << hashFunctionName(node->getName()) << "("; - else if (visit == InVisit) - out << ", "; - else - out << ")"; - break; - case EOpInternalFunctionCall: - // Function call to an internal helper function. - if (visit == PreVisit) - out << node->getName() << "("; + out << hashFunctionNameIfNeeded(node->getNameObj()) << "("; else if (visit == InVisit) out << ", "; else @@ -827,6 +889,7 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) { const TIntermSequence &sequence = *(node->getSequence()); const TIntermTyped *variable = sequence.front()->getAsTyped(); + writeLayoutQualifier(variable->getType()); writeVariableType(variable->getType()); out << " "; mDeclaringVariables = true; @@ -854,66 +917,88 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) visitChildren = false; break; case EOpConstructFloat: - writeTriplet(visit, "float(", NULL, ")"); + writeConstructorTriplet(visit, node->getType(), "float"); break; case EOpConstructVec2: - writeBuiltInFunctionTriplet(visit, "vec2(", false); + writeConstructorTriplet(visit, node->getType(), "vec2"); break; case EOpConstructVec3: - writeBuiltInFunctionTriplet(visit, "vec3(", false); + writeConstructorTriplet(visit, node->getType(), "vec3"); break; case EOpConstructVec4: - writeBuiltInFunctionTriplet(visit, "vec4(", false); + writeConstructorTriplet(visit, node->getType(), "vec4"); break; case EOpConstructBool: - writeTriplet(visit, "bool(", NULL, ")"); + writeConstructorTriplet(visit, node->getType(), "bool"); break; case EOpConstructBVec2: - writeBuiltInFunctionTriplet(visit, "bvec2(", false); + writeConstructorTriplet(visit, node->getType(), "bvec2"); break; case EOpConstructBVec3: - writeBuiltInFunctionTriplet(visit, "bvec3(", false); + writeConstructorTriplet(visit, node->getType(), "bvec3"); break; case EOpConstructBVec4: - writeBuiltInFunctionTriplet(visit, "bvec4(", false); + writeConstructorTriplet(visit, node->getType(), "bvec4"); break; case EOpConstructInt: - writeTriplet(visit, "int(", NULL, ")"); + writeConstructorTriplet(visit, node->getType(), "int"); break; case EOpConstructIVec2: - writeBuiltInFunctionTriplet(visit, "ivec2(", false); + writeConstructorTriplet(visit, node->getType(), "ivec2"); break; case EOpConstructIVec3: - writeBuiltInFunctionTriplet(visit, "ivec3(", false); + writeConstructorTriplet(visit, node->getType(), "ivec3"); break; case EOpConstructIVec4: - writeBuiltInFunctionTriplet(visit, "ivec4(", false); + writeConstructorTriplet(visit, node->getType(), "ivec4"); + break; + case EOpConstructUInt: + writeConstructorTriplet(visit, node->getType(), "uint"); + break; + case EOpConstructUVec2: + writeConstructorTriplet(visit, node->getType(), "uvec2"); + break; + case EOpConstructUVec3: + writeConstructorTriplet(visit, node->getType(), "uvec3"); + break; + case EOpConstructUVec4: + writeConstructorTriplet(visit, node->getType(), "uvec4"); break; case EOpConstructMat2: - writeBuiltInFunctionTriplet(visit, "mat2(", false); + writeConstructorTriplet(visit, node->getType(), "mat2"); + break; + case EOpConstructMat2x3: + writeConstructorTriplet(visit, node->getType(), "mat2x3"); + break; + case EOpConstructMat2x4: + writeConstructorTriplet(visit, node->getType(), "mat2x4"); + break; + case EOpConstructMat3x2: + writeConstructorTriplet(visit, node->getType(), "mat3x2"); break; case EOpConstructMat3: - writeBuiltInFunctionTriplet(visit, "mat3(", false); + writeConstructorTriplet(visit, node->getType(), "mat3"); + break; + case EOpConstructMat3x4: + writeConstructorTriplet(visit, node->getType(), "mat3x4"); + break; + case EOpConstructMat4x2: + writeConstructorTriplet(visit, node->getType(), "mat4x2"); + break; + case EOpConstructMat4x3: + writeConstructorTriplet(visit, node->getType(), "mat4x3"); break; case EOpConstructMat4: - writeBuiltInFunctionTriplet(visit, "mat4(", false); + writeConstructorTriplet(visit, node->getType(), "mat4"); break; case EOpConstructStruct: - if (visit == PreVisit) { const TType &type = node->getType(); ASSERT(type.getBasicType() == EbtStruct); - out << hashName(type.getStruct()->name()) << "("; - } - else if (visit == InVisit) - { - out << ", "; - } - else - { - out << ")"; + TString constructorName = hashName(type.getStruct()->name()); + writeConstructorTriplet(visit, node->getType(), constructorName.c_str()); + break; } - break; case EOpOuterProduct: writeBuiltInFunctionTriplet(visit, "outerProduct(", useEmulatedFunction); @@ -1004,8 +1089,12 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node) TInfoSinkBase &out = objSink(); incrementDepth(node); - // Loop header. + TLoopType loopType = node->getType(); + + // Only for loops can be unrolled + ASSERT(!node->getUnrollFlag() || loopType == ELoopFor); + if (loopType == ELoopFor) // for loop { if (!node->getUnrollFlag()) @@ -1022,6 +1111,8 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node) if (node->getExpression()) node->getExpression()->traverse(this); out << ")\n"; + + visitCodeBlock(node->getBody()); } else { @@ -1034,6 +1125,16 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node) out << "for (int " << name << " = 0; " << name << " < 1; " << "++" << name << ")\n"; + + out << "{\n"; + mLoopUnrollStack.push(node); + while (mLoopUnrollStack.satisfiesLoopCondition()) + { + visitCodeBlock(node->getBody()); + mLoopUnrollStack.step(); + } + mLoopUnrollStack.pop(); + out << "}\n"; } } else if (loopType == ELoopWhile) // while loop @@ -1042,39 +1143,22 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node) ASSERT(node->getCondition() != NULL); node->getCondition()->traverse(this); out << ")\n"; + + visitCodeBlock(node->getBody()); } else // do-while loop { ASSERT(loopType == ELoopDoWhile); out << "do\n"; - } - // Loop body. - if (node->getUnrollFlag()) - { - out << "{\n"; - mLoopUnrollStack.push(node); - while (mLoopUnrollStack.satisfiesLoopCondition()) - { - visitCodeBlock(node->getBody()); - mLoopUnrollStack.step(); - } - mLoopUnrollStack.pop(); - out << "}\n"; - } - else - { visitCodeBlock(node->getBody()); - } - // Loop footer. - if (loopType == ELoopDoWhile) // do-while loop - { out << "while ("; ASSERT(node->getCondition() != NULL); node->getCondition()->traverse(this); out << ");\n"; } + decrementDepth(); // No need to visit children. They have been already processed in @@ -1129,6 +1213,10 @@ TString TOutputGLSLBase::getTypeName(const TType &type) { out << "mat"; out << type.getNominalSize(); + if (type.getSecondarySize() != type.getNominalSize()) + { + out << "x" << type.getSecondarySize(); + } } else if (type.isVector()) { @@ -1143,6 +1231,9 @@ TString TOutputGLSLBase::getTypeName(const TType &type) case EbtBool: out << "bvec"; break; + case EbtUInt: + out << "uvec"; + break; default: UNREACHABLE(); } @@ -1177,12 +1268,16 @@ TString TOutputGLSLBase::hashVariableName(const TString &name) return hashName(name); } -TString TOutputGLSLBase::hashFunctionName(const TString &mangled_name) +TString TOutputGLSLBase::hashFunctionNameIfNeeded(const TName &mangledName) { - TString name = TFunction::unmangleName(mangled_name); - if (mSymbolTable.findBuiltIn(mangled_name, mShaderVersion) != NULL || name == "main") + TString mangledStr = mangledName.getString(); + TString name = TFunction::unmangleName(mangledStr); + if (mSymbolTable.findBuiltIn(mangledStr, mShaderVersion) != nullptr || name == "main") return translateTextureFunction(name); - return hashName(name); + if (mangledName.isInternal()) + return name; + else + return hashName(name); } bool TOutputGLSLBase::structDeclared(const TStructure *structure) const @@ -1215,3 +1310,70 @@ void TOutputGLSLBase::declareStruct(const TStructure *structure) out << "}"; } +void TOutputGLSLBase::declareInterfaceBlockLayout(const TInterfaceBlock *interfaceBlock) +{ + TInfoSinkBase &out = objSink(); + + out << "layout("; + + switch (interfaceBlock->blockStorage()) + { + case EbsUnspecified: + case EbsShared: + // Default block storage is shared. + out << "shared"; + break; + + case EbsPacked: + out << "packed"; + break; + + case EbsStd140: + out << "std140"; + break; + + default: + UNREACHABLE(); + break; + } + + out << ", "; + + switch (interfaceBlock->matrixPacking()) + { + case EmpUnspecified: + case EmpColumnMajor: + // Default matrix packing is column major. + out << "column_major"; + break; + + case EmpRowMajor: + out << "row_major"; + break; + + default: + UNREACHABLE(); + break; + } + + out << ") "; +} + +void TOutputGLSLBase::declareInterfaceBlock(const TInterfaceBlock *interfaceBlock) +{ + TInfoSinkBase &out = objSink(); + + out << hashName(interfaceBlock->name()) << "{\n"; + const TFieldList &fields = interfaceBlock->fields(); + for (size_t i = 0; i < fields.size(); ++i) + { + const TField *field = fields[i]; + if (writeVariablePrecision(field->type()->getPrecision())) + out << " "; + out << getTypeName(*field->type()) << " " << hashName(field->name()); + if (field->type()->isArray()) + out << arrayBrackets(*field->type()); + out << ";\n"; + } + out << "}"; +} diff --git a/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.h b/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.h index 4e66059c21..2ae82d15b2 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.h +++ b/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.h @@ -32,22 +32,24 @@ class TOutputGLSLBase : public TIntermTraverser protected: TInfoSinkBase &objSink() { return mObjSink; } void writeTriplet(Visit visit, const char *preStr, const char *inStr, const char *postStr); + void writeLayoutQualifier(const TType &type); void writeVariableType(const TType &type); virtual bool writeVariablePrecision(TPrecision precision) = 0; void writeFunctionParameters(const TIntermSequence &args); - const ConstantUnion *writeConstantUnion(const TType &type, const ConstantUnion *pConstUnion); + const TConstantUnion *writeConstantUnion(const TType &type, const TConstantUnion *pConstUnion); + void writeConstructorTriplet(Visit visit, const TType &type, const char *constructorBaseType); TString getTypeName(const TType &type); - virtual void visitSymbol(TIntermSymbol *node); - virtual void visitConstantUnion(TIntermConstantUnion *node); - virtual bool visitBinary(Visit visit, TIntermBinary *node); - virtual bool visitUnary(Visit visit, TIntermUnary *node); - virtual bool visitSelection(Visit visit, TIntermSelection *node); - virtual bool visitSwitch(Visit visit, TIntermSwitch *node); - virtual bool visitCase(Visit visit, TIntermCase *node); - virtual bool visitAggregate(Visit visit, TIntermAggregate *node); - virtual bool visitLoop(Visit visit, TIntermLoop *node); - virtual bool visitBranch(Visit visit, TIntermBranch *node); + void visitSymbol(TIntermSymbol *node) override; + void visitConstantUnion(TIntermConstantUnion *node) override; + bool visitBinary(Visit visit, TIntermBinary *node) override; + bool visitUnary(Visit visit, TIntermUnary *node) override; + bool visitSelection(Visit visit, TIntermSelection *node) override; + bool visitSwitch(Visit visit, TIntermSwitch *node) override; + bool visitCase(Visit visit, TIntermCase *node) override; + bool visitAggregate(Visit visit, TIntermAggregate *node) override; + bool visitLoop(Visit visit, TIntermLoop *node) override; + bool visitBranch(Visit visit, TIntermBranch *node) override; void visitCodeBlock(TIntermNode *node); @@ -56,8 +58,8 @@ class TOutputGLSLBase : public TIntermTraverser TString hashName(const TString &name); // Same as hashName(), but without hashing built-in variables. TString hashVariableName(const TString &name); - // Same as hashName(), but without hashing built-in functions. - TString hashFunctionName(const TString &mangled_name); + // Same as hashName(), but without hashing built-in functions and with unmangling. + TString hashFunctionNameIfNeeded(const TName &mangledName); // Used to translate function names for differences between ESSL and GLSL virtual TString translateTextureFunction(TString &name) { return name; } @@ -65,6 +67,9 @@ class TOutputGLSLBase : public TIntermTraverser bool structDeclared(const TStructure *structure) const; void declareStruct(const TStructure *structure); + void declareInterfaceBlockLayout(const TInterfaceBlock *interfaceBlock); + void declareInterfaceBlock(const TInterfaceBlock *interfaceBlock); + void writeBuiltInFunctionTriplet(Visit visit, const char *preStr, bool useEmulatedFunction); TInfoSinkBase &mObjSink; diff --git a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp index 94225b81c4..253b96696c 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp @@ -11,45 +11,81 @@ #include <stdio.h> #include "common/angleutils.h" +#include "common/debug.h" #include "common/utilities.h" #include "compiler/translator/BuiltInFunctionEmulator.h" #include "compiler/translator/BuiltInFunctionEmulatorHLSL.h" -#include "compiler/translator/DetectDiscontinuity.h" #include "compiler/translator/FlagStd140Structs.h" #include "compiler/translator/InfoSink.h" #include "compiler/translator/NodeSearch.h" #include "compiler/translator/RemoveSwitchFallThrough.h" -#include "compiler/translator/RewriteElseBlocks.h" #include "compiler/translator/SearchSymbol.h" #include "compiler/translator/StructureHLSL.h" #include "compiler/translator/TranslatorHLSL.h" -#include "compiler/translator/UnfoldShortCircuit.h" #include "compiler/translator/UniformHLSL.h" #include "compiler/translator/UtilsHLSL.h" #include "compiler/translator/blocklayout.h" -#include "compiler/translator/compilerdebug.h" #include "compiler/translator/util.h" -namespace sh +namespace { -TString OutputHLSL::TextureFunction::name() const +bool IsSequence(TIntermNode *node) { - TString name = "gl_texture"; + return node->getAsAggregate() != nullptr && node->getAsAggregate()->getOp() == EOpSequence; +} - if (IsSampler2D(sampler)) - { - name += "2D"; - } - else if (IsSampler3D(sampler)) - { - name += "3D"; +void WriteSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion) +{ + ASSERT(constUnion != nullptr); + switch (constUnion->getType()) + { + case EbtFloat: + out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst())); + break; + case EbtInt: + out << constUnion->getIConst(); + break; + case EbtUInt: + out << constUnion->getUConst(); + break; + case EbtBool: + out << constUnion->getBConst(); + break; + default: + UNREACHABLE(); } - else if (IsSamplerCube(sampler)) +} + +const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out, + const TConstantUnion *const constUnion, + const size_t size) +{ + const TConstantUnion *constUnionIterated = constUnion; + for (size_t i = 0; i < size; i++, constUnionIterated++) { - name += "Cube"; + WriteSingleConstant(out, constUnionIterated); + + if (i != size - 1) + { + out << ", "; + } } - else UNREACHABLE(); + return constUnionIterated; +} + +} // namespace + +namespace sh +{ + +TString OutputHLSL::TextureFunction::name() const +{ + TString name = "gl_texture"; + + // We need to include full the sampler type in the function name to make the signature unique + // on D3D11, where samplers are passed to texture functions as indices. + name += TextureTypeSuffix(this->sampler); if (proj) { @@ -108,10 +144,10 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, mExtensionBehavior(extensionBehavior), mSourcePath(sourcePath), mOutputType(outputType), + mCompileOptions(compileOptions), mNumRenderTargets(numRenderTargets), - mCompileOptions(compileOptions) + mCurrentFunctionMetadata(nullptr) { - mUnfoldShortCircuit = new UnfoldShortCircuit(this); mInsideFunction = false; mUsesFragColor = false; @@ -130,8 +166,6 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, mUniqueIndex = 0; - mContainsLoopDiscontinuity = false; - mContainsAnyLoop = false; mOutputLod0Function = false; mInsideDiscontinuousLoop = false; mNestedLoopDepth = 0; @@ -141,7 +175,7 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, mStructureHLSL = new StructureHLSL; mUniformHLSL = new UniformHLSL(mStructureHLSL, outputType, uniforms); - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { // Fragment shaders need dx_DepthRange, dx_ViewCoords and dx_DepthFront. // Vertex shaders need a slightly different set: dx_DepthRange, dx_ViewCoords and dx_ViewAdjust. @@ -155,37 +189,33 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, OutputHLSL::~OutputHLSL() { - SafeDelete(mUnfoldShortCircuit); SafeDelete(mStructureHLSL); SafeDelete(mUniformHLSL); - for (auto it = mStructEqualityFunctions.begin(); it != mStructEqualityFunctions.end(); ++it) + for (auto &eqFunction : mStructEqualityFunctions) { - SafeDelete(*it); + SafeDelete(eqFunction); } - for (auto it = mArrayEqualityFunctions.begin(); it != mArrayEqualityFunctions.end(); ++it) + for (auto &eqFunction : mArrayEqualityFunctions) { - SafeDelete(*it); + SafeDelete(eqFunction); } } void OutputHLSL::output(TIntermNode *treeRoot, TInfoSinkBase &objSink) { - mContainsLoopDiscontinuity = mShaderType == GL_FRAGMENT_SHADER && containsLoopDiscontinuity(treeRoot); - mContainsAnyLoop = containsAnyLoop(treeRoot); const std::vector<TIntermTyped*> &flaggedStructs = FlagStd140ValueStructs(treeRoot); makeFlaggedStructMaps(flaggedStructs); - // Work around D3D9 bug that would manifest in vertex shaders with selection blocks which - // use a vertex attribute as a condition, and some related computation in the else block. - if (mOutputType == SH_HLSL9_OUTPUT && mShaderType == GL_VERTEX_SHADER) - { - RewriteElseBlocks(treeRoot); - } - BuiltInFunctionEmulator builtInFunctionEmulator; InitBuiltInFunctionEmulatorForHLSL(&builtInFunctionEmulator); builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(treeRoot); + // Now that we are done changing the AST, do the analyses need for HLSL generation + CallDAG::InitResult success = mCallDag.init(treeRoot, &objSink); + ASSERT(success == CallDAG::INITDAG_SUCCESS); + UNUSED_ASSERTION_VARIABLE(success); + mASTMetadataList = CreateASTMetadataHLSL(treeRoot, mCallDag); + // Output the body and footer first to determine what has to go in the header mInfoSinkStack.push(&mBody); treeRoot->traverse(this); @@ -199,7 +229,7 @@ void OutputHLSL::output(TIntermNode *treeRoot, TInfoSinkBase &objSink) mInfoSinkStack.pop(); mInfoSinkStack.push(&mHeader); - header(&builtInFunctionEmulator); + header(mHeader, &builtInFunctionEmulator); mInfoSinkStack.pop(); objSink << mHeader.c_str(); @@ -294,10 +324,8 @@ TString OutputHLSL::structInitializerString(int indent, const TStructure &struct return init; } -void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) +void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator) { - TInfoSinkBase &out = getInfoSink(); - TString varyings; TString attributes; TString flaggedStructs; @@ -334,23 +362,31 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << mStructureHLSL->structsHeader(); - out << mUniformHLSL->uniformsHeader(mOutputType, mReferencedUniforms); + mUniformHLSL->uniformsHeader(out, mOutputType, mReferencedUniforms); out << mUniformHLSL->interfaceBlocksHeader(mReferencedInterfaceBlocks); if (!mEqualityFunctions.empty()) { out << "\n// Equality functions\n\n"; - for (auto it = mEqualityFunctions.cbegin(); it != mEqualityFunctions.cend(); ++it) + for (const auto &eqFunction : mEqualityFunctions) { - out << (*it)->functionDefinition << "\n"; + out << eqFunction->functionDefinition << "\n"; } } if (!mArrayAssignmentFunctions.empty()) { out << "\n// Assignment functions\n\n"; - for (auto it = mArrayAssignmentFunctions.cbegin(); it != mArrayAssignmentFunctions.cend(); ++it) + for (const auto &assignmentFunction : mArrayAssignmentFunctions) { - out << it->functionDefinition << "\n"; + out << assignmentFunction.functionDefinition << "\n"; + } + } + if (!mArrayConstructIntoFunctions.empty()) + { + out << "\n// Array constructor functions\n\n"; + for (const auto &constructIntoFunction : mArrayConstructIntoFunctions) + { + out << constructIntoFunction.functionDefinition << "\n"; } } @@ -449,7 +485,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) "\n"; } - if (mOutputType == SH_HLSL11_OUTPUT) + if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { out << "cbuffer DriverConstants : register(b1)\n" "{\n"; @@ -469,6 +505,13 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " float3 dx_DepthFront : packoffset(c2);\n"; } + if (mUsesFragCoord) + { + // dx_ViewScale is only used in the fragment shader to correct + // the value for glFragCoord if necessary + out << " float2 dx_ViewScale : packoffset(c3);\n"; + } + out << "};\n"; } else @@ -553,7 +596,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) "\n"; } - if (mOutputType == SH_HLSL11_OUTPUT) + if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { out << "cbuffer DriverConstants : register(b1)\n" "{\n"; @@ -563,11 +606,13 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " float3 dx_DepthRange : packoffset(c0);\n"; } - // dx_ViewAdjust and dx_ViewCoords will only be used in Feature Level 9 shaders. - // However, we declare it for all shaders (including Feature Level 10+). - // The bytecode is the same whether we declare it or not, since D3DCompiler removes it if it's unused. + // dx_ViewAdjust and dx_ViewCoords will only be used in Feature Level 9 + // shaders. However, we declare it for all shaders (including Feature Level 10+). + // The bytecode is the same whether we declare it or not, since D3DCompiler removes it + // if it's unused. out << " float4 dx_ViewAdjust : packoffset(c1);\n"; out << " float2 dx_ViewCoords : packoffset(c2);\n"; + out << " float2 dx_ViewScale : packoffset(c3);\n"; out << "};\n" "\n"; @@ -653,7 +698,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) // Argument list int hlslCoords = 4; - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { switch(textureFunction->sampler) { @@ -672,29 +717,20 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) default: UNREACHABLE(); } } - else if (mOutputType == SH_HLSL11_OUTPUT) + else { - switch(textureFunction->sampler) + hlslCoords = HLSLTextureCoordsCount(textureFunction->sampler); + if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { - case EbtSampler2D: out << "Texture2D x, SamplerState s"; hlslCoords = 2; break; - case EbtSampler3D: out << "Texture3D x, SamplerState s"; hlslCoords = 3; break; - case EbtSamplerCube: out << "TextureCube x, SamplerState s"; hlslCoords = 3; break; - case EbtSampler2DArray: out << "Texture2DArray x, SamplerState s"; hlslCoords = 3; break; - case EbtISampler2D: out << "Texture2D<int4> x, SamplerState s"; hlslCoords = 2; break; - case EbtISampler3D: out << "Texture3D<int4> x, SamplerState s"; hlslCoords = 3; break; - case EbtISamplerCube: out << "Texture2DArray<int4> x, SamplerState s"; hlslCoords = 3; break; - case EbtISampler2DArray: out << "Texture2DArray<int4> x, SamplerState s"; hlslCoords = 3; break; - case EbtUSampler2D: out << "Texture2D<uint4> x, SamplerState s"; hlslCoords = 2; break; - case EbtUSampler3D: out << "Texture3D<uint4> x, SamplerState s"; hlslCoords = 3; break; - case EbtUSamplerCube: out << "Texture2DArray<uint4> x, SamplerState s"; hlslCoords = 3; break; - case EbtUSampler2DArray: out << "Texture2DArray<uint4> x, SamplerState s"; hlslCoords = 3; break; - case EbtSampler2DShadow: out << "Texture2D x, SamplerComparisonState s"; hlslCoords = 2; break; - case EbtSamplerCubeShadow: out << "TextureCube x, SamplerComparisonState s"; hlslCoords = 3; break; - case EbtSampler2DArrayShadow: out << "Texture2DArray x, SamplerComparisonState s"; hlslCoords = 3; break; - default: UNREACHABLE(); + out << TextureString(textureFunction->sampler) << " x, " + << SamplerString(textureFunction->sampler) << " s"; + } + else + { + ASSERT(mOutputType == SH_HLSL_4_1_OUTPUT); + out << "const uint samplerIndex"; } } - else UNREACHABLE(); if (textureFunction->method == TextureFunction::FETCH) // Integer coordinates { @@ -785,6 +821,31 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << ")\n" "{\n"; + // In some cases we use a variable to store the texture/sampler objects, but to work around + // a D3D11 compiler bug related to discard inside a loop that is conditional on texture + // sampling we need to call the function directly on a reference to the array. The bug was + // found using dEQP-GLES3.functional.shaders.discard*loop_texture* tests. + TString textureReference("x"); + TString samplerReference("s"); + if (mOutputType == SH_HLSL_4_1_OUTPUT) + { + TString suffix = TextureGroupSuffix(textureFunction->sampler); + if (TextureGroup(textureFunction->sampler) == HLSL_TEXTURE_2D) + { + textureReference = TString("textures") + suffix + "[samplerIndex]"; + samplerReference = TString("samplers") + suffix + "[samplerIndex]"; + } + else + { + out << " const uint textureIndex = samplerIndex - textureIndexOffset" << suffix + << ";\n"; + textureReference = TString("textures") + suffix + "[textureIndex]"; + out << " const uint samplerArrayIndex = samplerIndex - samplerIndexOffset" + << suffix << ";\n"; + samplerReference = TString("samplers") + suffix + "[samplerArrayIndex]"; + } + } + if (textureFunction->method == TextureFunction::SIZE) { if (IsSampler2D(textureFunction->sampler) || IsSamplerCube(textureFunction->sampler)) @@ -792,18 +853,21 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) if (IsSamplerArray(textureFunction->sampler)) { out << " uint width; uint height; uint layers; uint numberOfLevels;\n" - " x.GetDimensions(lod, width, height, layers, numberOfLevels);\n"; + << " " << textureReference + << ".GetDimensions(lod, width, height, layers, numberOfLevels);\n"; } else { out << " uint width; uint height; uint numberOfLevels;\n" - " x.GetDimensions(lod, width, height, numberOfLevels);\n"; + << " " << textureReference + << ".GetDimensions(lod, width, height, numberOfLevels);\n"; } } else if (IsSampler3D(textureFunction->sampler)) { out << " uint width; uint height; uint depth; uint numberOfLevels;\n" - " x.GetDimensions(lod, width, height, depth, numberOfLevels);\n"; + << " " << textureReference + << ".GetDimensions(lod, width, height, depth, numberOfLevels);\n"; } else UNREACHABLE(); @@ -835,7 +899,8 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " uint mip = 0;\n"; - out << " x.GetDimensions(mip, width, height, layers, levels);\n"; + out << " " << textureReference + << ".GetDimensions(mip, width, height, layers, levels);\n"; out << " bool xMajor = abs(t.x) > abs(t.y) && abs(t.x) > abs(t.z);\n"; out << " bool yMajor = abs(t.y) > abs(t.z) && abs(t.y) > abs(t.x);\n"; @@ -856,6 +921,18 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " t.x = (u * 0.5f / m) + 0.5f;\n"; out << " t.y = (v * 0.5f / m) + 0.5f;\n"; + + // Mip level computation. + if (textureFunction->method == TextureFunction::IMPLICIT) + { + out << " float2 tSized = float2(t.x * width, t.y * height);\n" + " float2 dx = ddx(tSized);\n" + " float2 dy = ddy(tSized);\n" + " float lod = 0.5f * log2(max(dot(dx, dx), dot(dy, dy)));\n" + " mip = uint(min(max(round(lod), 0), levels - 1));\n" + << " " << textureReference + << ".GetDimensions(mip, width, height, layers, levels);\n"; + } } else if (IsIntegerSampler(textureFunction->sampler) && textureFunction->method != TextureFunction::FETCH) @@ -876,11 +953,13 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } |