mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-25 09:57:32 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
310
gfx/angle/src/angle.gyp
Normal file
310
gfx/angle/src/angle.gyp
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
# Copyright (c) 2012 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.
|
||||
|
||||
{
|
||||
'variables':
|
||||
{
|
||||
'angle_code': 1,
|
||||
'angle_gen_path': '<(SHARED_INTERMEDIATE_DIR)/angle',
|
||||
'angle_id_script_base': 'commit_id.py',
|
||||
'angle_id_script': '<(angle_gen_path)/<(angle_id_script_base)',
|
||||
'angle_id_header_base': 'commit.h',
|
||||
'angle_id_header': '<(angle_gen_path)/id/<(angle_id_header_base)',
|
||||
'angle_use_commit_id%': '<!(python <(angle_id_script_base) check ..)',
|
||||
'angle_enable_d3d9%': 0,
|
||||
'angle_enable_d3d11%': 0,
|
||||
'angle_enable_gl%': 0,
|
||||
'angle_enable_vulkan%': 0,
|
||||
'angle_enable_essl%': 1, # Enable this for all configs by default
|
||||
'angle_enable_glsl%': 1, # Enable this for all configs by default
|
||||
'angle_enable_hlsl%': 0,
|
||||
'angle_link_glx%': 0,
|
||||
'angle_gl_library_type%': 'shared_library',
|
||||
'dcheck_always_on%': 0,
|
||||
'conditions':
|
||||
[
|
||||
['OS=="win"',
|
||||
{
|
||||
'angle_enable_gl%': 1,
|
||||
'angle_enable_d3d9%': 1,
|
||||
'angle_enable_d3d11%': 1,
|
||||
'angle_enable_hlsl%': 1,
|
||||
'angle_enable_vulkan%': 1,
|
||||
}],
|
||||
['OS=="linux" and use_x11==1 and chromeos==0',
|
||||
{
|
||||
'angle_enable_gl%': 1,
|
||||
}],
|
||||
['OS=="mac"',
|
||||
{
|
||||
'angle_enable_gl%': 1,
|
||||
}],
|
||||
['use_ozone==1',
|
||||
{
|
||||
'angle_enable_gl%': 1,
|
||||
}],
|
||||
],
|
||||
'angle_enable_null%': 1, # Available on all platforms
|
||||
},
|
||||
'includes':
|
||||
[
|
||||
'compiler.gypi',
|
||||
'libGLESv2.gypi',
|
||||
'libEGL.gypi'
|
||||
],
|
||||
|
||||
'targets':
|
||||
[
|
||||
{
|
||||
'target_name': 'angle_common',
|
||||
'type': 'static_library',
|
||||
'includes': [ '../gyp/common_defines.gypi', ],
|
||||
'sources':
|
||||
[
|
||||
'<@(libangle_common_sources)',
|
||||
],
|
||||
'include_dirs':
|
||||
[
|
||||
'.',
|
||||
'../include',
|
||||
'common/third_party/numerics',
|
||||
],
|
||||
'dependencies':
|
||||
[
|
||||
'commit_id',
|
||||
],
|
||||
'direct_dependent_settings':
|
||||
{
|
||||
'include_dirs':
|
||||
[
|
||||
'<(angle_path)/include',
|
||||
'<(angle_path)/src',
|
||||
'<(angle_path)/src/common/third_party/numerics',
|
||||
],
|
||||
'conditions':
|
||||
[
|
||||
['dcheck_always_on==1',
|
||||
{
|
||||
'configurations':
|
||||
{
|
||||
'Release_Base':
|
||||
{
|
||||
'defines':
|
||||
[
|
||||
'ANGLE_ENABLE_RELEASE_ASSERTS',
|
||||
],
|
||||
},
|
||||
},
|
||||
}],
|
||||
['OS=="win"',
|
||||
{
|
||||
'configurations':
|
||||
{
|
||||
'Debug_Base':
|
||||
{
|
||||
'defines':
|
||||
[
|
||||
'ANGLE_ENABLE_DEBUG_ANNOTATIONS'
|
||||
],
|
||||
},
|
||||
},
|
||||
}],
|
||||
],
|
||||
},
|
||||
'conditions':
|
||||
[
|
||||
['dcheck_always_on==1',
|
||||
{
|
||||
'configurations':
|
||||
{
|
||||
'Release_Base':
|
||||
{
|
||||
'defines':
|
||||
[
|
||||
'ANGLE_ENABLE_RELEASE_ASSERTS',
|
||||
],
|
||||
},
|
||||
},
|
||||
}],
|
||||
['OS=="win"',
|
||||
{
|
||||
'configurations':
|
||||
{
|
||||
'Debug_Base':
|
||||
{
|
||||
'defines':
|
||||
[
|
||||
'ANGLE_ENABLE_DEBUG_ANNOTATIONS'
|
||||
],
|
||||
},
|
||||
},
|
||||
}],
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
'target_name': 'angle_image_util',
|
||||
'type': 'static_library',
|
||||
'includes': [ '../gyp/common_defines.gypi', ],
|
||||
'sources':
|
||||
[
|
||||
'<@(libangle_image_util_sources)',
|
||||
],
|
||||
'include_dirs':
|
||||
[
|
||||
'.',
|
||||
'../include',
|
||||
],
|
||||
'dependencies':
|
||||
[
|
||||
'angle_common',
|
||||
],
|
||||
'direct_dependent_settings':
|
||||
{
|
||||
'include_dirs':
|
||||
[
|
||||
'<(angle_path)/include',
|
||||
'<(angle_path)/src',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
'target_name': 'copy_scripts',
|
||||
'type': 'none',
|
||||
'includes': [ '../gyp/common_defines.gypi', ],
|
||||
'hard_dependency': 1,
|
||||
'copies':
|
||||
[
|
||||
{
|
||||
'destination': '<(angle_gen_path)',
|
||||
'files': [ 'copy_compiler_dll.bat', '<(angle_id_script_base)' ],
|
||||
},
|
||||
],
|
||||
'conditions':
|
||||
[
|
||||
['angle_build_winrt==1',
|
||||
{
|
||||
'type' : 'shared_library',
|
||||
}],
|
||||
],
|
||||
},
|
||||
],
|
||||
'conditions':
|
||||
[
|
||||
['angle_use_commit_id!=0',
|
||||
{
|
||||
'targets':
|
||||
[
|
||||
{
|
||||
'target_name': 'commit_id',
|
||||
'type': 'none',
|
||||
'includes': [ '../gyp/common_defines.gypi', ],
|
||||
'dependencies': [ 'copy_scripts', ],
|
||||
'hard_dependency': 1,
|
||||
'actions':
|
||||
[
|
||||
{
|
||||
'action_name': 'Generate ANGLE Commit ID Header',
|
||||
'message': 'Generating ANGLE Commit ID',
|
||||
# reference the git index as an input, so we rebuild on changes to the index
|
||||
'inputs': [ '<(angle_id_script)', '<(angle_path)/.git/index' ],
|
||||
'outputs': [ '<(angle_id_header)' ],
|
||||
'msvs_cygwin_shell': 0,
|
||||
'action':
|
||||
[
|
||||
'python', '<(angle_id_script)', 'gen', '<(angle_path)', '<(angle_id_header)'
|
||||
],
|
||||
},
|
||||
],
|
||||
'all_dependent_settings':
|
||||
{
|
||||
'include_dirs':
|
||||
[
|
||||
'<(angle_gen_path)',
|
||||
],
|
||||
},
|
||||
'conditions':
|
||||
[
|
||||
['angle_build_winrt==1',
|
||||
{
|
||||
'type' : 'shared_library',
|
||||
}],
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
{ # angle_use_commit_id==0
|
||||
'targets':
|
||||
[
|
||||
{
|
||||
'target_name': 'commit_id',
|
||||
'type': 'none',
|
||||
'hard_dependency': 1,
|
||||
'includes': [ '../gyp/common_defines.gypi', ],
|
||||
'copies':
|
||||
[
|
||||
{
|
||||
'destination': '<(angle_gen_path)/id',
|
||||
'files': [ '<(angle_id_header_base)' ]
|
||||
}
|
||||
],
|
||||
'all_dependent_settings':
|
||||
{
|
||||
'include_dirs':
|
||||
[
|
||||
'<(angle_gen_path)',
|
||||
],
|
||||
},
|
||||
'conditions':
|
||||
[
|
||||
['angle_build_winrt==1',
|
||||
{
|
||||
'type' : 'shared_library',
|
||||
}],
|
||||
],
|
||||
}
|
||||
]
|
||||
}],
|
||||
['OS=="win"',
|
||||
{
|
||||
'targets':
|
||||
[
|
||||
{
|
||||
'target_name': 'copy_compiler_dll',
|
||||
'type': 'none',
|
||||
'dependencies': [ 'copy_scripts', ],
|
||||
'includes': [ '../gyp/common_defines.gypi', ],
|
||||
'conditions':
|
||||
[
|
||||
['angle_build_winrt==0',
|
||||
{
|
||||
'actions':
|
||||
[
|
||||
{
|
||||
'action_name': 'copy_dll',
|
||||
'message': 'Copying D3D Compiler DLL...',
|
||||
'msvs_cygwin_shell': 0,
|
||||
'inputs': [ 'copy_compiler_dll.bat' ],
|
||||
'outputs': [ '<(PRODUCT_DIR)/d3dcompiler_47.dll' ],
|
||||
'action':
|
||||
[
|
||||
"<(angle_gen_path)/copy_compiler_dll.bat",
|
||||
"$(PlatformName)",
|
||||
"<(windows_sdk_path)",
|
||||
"<(PRODUCT_DIR)"
|
||||
],
|
||||
},
|
||||
], #actions
|
||||
}],
|
||||
['angle_build_winrt==1',
|
||||
{
|
||||
'type' : 'shared_library',
|
||||
}],
|
||||
]
|
||||
},
|
||||
], # targets
|
||||
}],
|
||||
] # conditions
|
||||
}
|
||||
3
gfx/angle/src/commit.h
Normal file
3
gfx/angle/src/commit.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#define ANGLE_COMMIT_HASH "2a250c8a0e15"
|
||||
#define ANGLE_COMMIT_HASH_SIZE 12
|
||||
#define ANGLE_COMMIT_DATE "2016-11-23 17:58:16 +0800"
|
||||
42
gfx/angle/src/commit_id.py
Normal file
42
gfx/angle/src/commit_id.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import subprocess as sp
|
||||
import sys
|
||||
import os
|
||||
|
||||
usage = """\
|
||||
Usage: commit_id.py check <angle_dir> - check if git is present
|
||||
commit_id.py gen <angle_dir> <file_to_write> - generate commit.h"""
|
||||
|
||||
def grab_output(command, cwd):
|
||||
return sp.Popen(command, stdout=sp.PIPE, shell=True, cwd=cwd).communicate()[0].strip()
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
sys.exit(usage)
|
||||
|
||||
operation = sys.argv[1]
|
||||
cwd = sys.argv[2]
|
||||
|
||||
if operation == 'check':
|
||||
index_path = os.path.join(cwd, '.git', 'index')
|
||||
if os.path.exists(index_path):
|
||||
print("1")
|
||||
else:
|
||||
print("0")
|
||||
sys.exit(0)
|
||||
|
||||
output_file = sys.argv[3]
|
||||
commit_id_size = 12
|
||||
|
||||
try:
|
||||
commit_id = grab_output('git rev-parse --short=%d HEAD' % commit_id_size, cwd)
|
||||
commit_date = grab_output('git show -s --format=%ci HEAD', cwd)
|
||||
except:
|
||||
commit_id = 'invalid-hash'
|
||||
commit_date = 'invalid-date'
|
||||
|
||||
hfile = open(output_file, 'w')
|
||||
|
||||
hfile.write('#define ANGLE_COMMIT_HASH "%s"\n' % commit_id)
|
||||
hfile.write('#define ANGLE_COMMIT_HASH_SIZE %d\n' % commit_id_size)
|
||||
hfile.write('#define ANGLE_COMMIT_DATE "%s"\n' % commit_date)
|
||||
|
||||
hfile.close()
|
||||
155
gfx/angle/src/common/BitSetIterator.h
Normal file
155
gfx/angle/src/common/BitSetIterator.h
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
//
|
||||
// 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);
|
||||
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_
|
||||
91
gfx/angle/src/common/BitSetIterator_unittest.cpp
Normal file
91
gfx/angle/src/common/BitSetIterator_unittest.cpp
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// 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.
|
||||
//
|
||||
// BitSetIteratorTest:
|
||||
// Test the IterableBitSet class.
|
||||
//
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/BitSetIterator.h"
|
||||
|
||||
using namespace angle;
|
||||
|
||||
namespace
|
||||
{
|
||||
class BitSetIteratorTest : public testing::Test
|
||||
{
|
||||
protected:
|
||||
std::bitset<40> mStateBits;
|
||||
};
|
||||
|
||||
// Simple iterator test.
|
||||
TEST_F(BitSetIteratorTest, Iterator)
|
||||
{
|
||||
std::set<unsigned long> originalValues;
|
||||
originalValues.insert(2);
|
||||
originalValues.insert(6);
|
||||
originalValues.insert(8);
|
||||
originalValues.insert(35);
|
||||
|
||||
for (unsigned long value : originalValues)
|
||||
{
|
||||
mStateBits.set(value);
|
||||
}
|
||||
|
||||
std::set<unsigned long> readValues;
|
||||
for (unsigned long bit : IterateBitSet(mStateBits))
|
||||
{
|
||||
EXPECT_EQ(1u, originalValues.count(bit));
|
||||
EXPECT_EQ(0u, readValues.count(bit));
|
||||
readValues.insert(bit);
|
||||
}
|
||||
|
||||
EXPECT_EQ(originalValues.size(), readValues.size());
|
||||
}
|
||||
|
||||
// Test an empty iterator.
|
||||
TEST_F(BitSetIteratorTest, EmptySet)
|
||||
{
|
||||
// We don't use the FAIL gtest macro here since it returns immediately,
|
||||
// causing an unreachable code warning in MSVS
|
||||
bool sawBit = false;
|
||||
for (unsigned long bit : IterateBitSet(mStateBits))
|
||||
{
|
||||
sawBit = true;
|
||||
UNUSED_VARIABLE(bit);
|
||||
}
|
||||
EXPECT_FALSE(sawBit);
|
||||
}
|
||||
|
||||
// Test iterating a result of combining two bitsets.
|
||||
TEST_F(BitSetIteratorTest, NonLValueBitset)
|
||||
{
|
||||
std::bitset<40> otherBits;
|
||||
|
||||
mStateBits.set(1);
|
||||
mStateBits.set(2);
|
||||
mStateBits.set(3);
|
||||
mStateBits.set(4);
|
||||
|
||||
otherBits.set(0);
|
||||
otherBits.set(1);
|
||||
otherBits.set(3);
|
||||
otherBits.set(5);
|
||||
|
||||
std::set<unsigned long> seenBits;
|
||||
|
||||
for (unsigned long bit : IterateBitSet(mStateBits & otherBits))
|
||||
{
|
||||
EXPECT_EQ(0u, seenBits.count(bit));
|
||||
seenBits.insert(bit);
|
||||
EXPECT_TRUE(mStateBits[bit]);
|
||||
EXPECT_TRUE(otherBits[bit]);
|
||||
}
|
||||
|
||||
EXPECT_EQ((mStateBits & otherBits).count(), seenBits.size());
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
53
gfx/angle/src/common/Color.h
Normal file
53
gfx/angle/src/common/Color.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
// Color.h : Defines the Color type used throughout the ANGLE libraries
|
||||
|
||||
#ifndef COMMON_COLOR_H_
|
||||
#define COMMON_COLOR_H_
|
||||
|
||||
namespace angle
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct Color
|
||||
{
|
||||
T red;
|
||||
T green;
|
||||
T blue;
|
||||
T alpha;
|
||||
|
||||
Color();
|
||||
Color(T r, T g, T b, T a);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
bool operator==(const Color<T> &a, const Color<T> &b);
|
||||
|
||||
template <typename T>
|
||||
bool operator!=(const Color<T> &a, const Color<T> &b);
|
||||
|
||||
typedef Color<float> ColorF;
|
||||
typedef Color<int> ColorI;
|
||||
typedef Color<unsigned int> ColorUI;
|
||||
|
||||
} // namespace angle
|
||||
|
||||
// TODO: Move this fully into the angle namespace
|
||||
namespace gl
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
using Color = angle::Color<T>;
|
||||
using ColorF = angle::ColorF;
|
||||
using ColorI = angle::ColorI;
|
||||
using ColorUI = angle::ColorUI;
|
||||
|
||||
} // namespace gl
|
||||
|
||||
#include "Color.inl"
|
||||
|
||||
#endif // COMMON_COLOR_H_
|
||||
37
gfx/angle/src/common/Color.inl
Normal file
37
gfx/angle/src/common/Color.inl
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
// Color.inl : Inline definitions of some functions from Color.h
|
||||
|
||||
namespace angle
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
Color<T>::Color() : Color(0, 0, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Color<T>::Color(T r, T g, T b, T a) : red(r), green(g), blue(b), alpha(a)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator==(const Color<T> &a, const Color<T> &b)
|
||||
{
|
||||
return a.red == b.red &&
|
||||
a.green == b.green &&
|
||||
a.blue == b.blue &&
|
||||
a.alpha == b.alpha;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator!=(const Color<T> &a, const Color<T> &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
} // namespace angle
|
||||
2205
gfx/angle/src/common/Float16ToFloat32.cpp
Normal file
2205
gfx/angle/src/common/Float16ToFloat32.cpp
Normal file
File diff suppressed because it is too large
Load diff
80
gfx/angle/src/common/Float16ToFloat32.py
Normal file
80
gfx/angle/src/common/Float16ToFloat32.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# Copyright (c) 2012 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.
|
||||
#
|
||||
|
||||
# This script generates a function that converts 16-bit precision floating
|
||||
# point numbers to 32-bit.
|
||||
# It is based on ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf.
|
||||
|
||||
#include "common/mathutil.h"
|
||||
|
||||
def convertMantissa(i):
|
||||
if i == 0:
|
||||
return 0
|
||||
elif i < 1024:
|
||||
m = i << 13
|
||||
e = 0
|
||||
while not (m & 0x00800000):
|
||||
e -= 0x00800000
|
||||
m = m << 1
|
||||
m &= ~0x00800000
|
||||
e += 0x38800000
|
||||
return m | e
|
||||
else:
|
||||
return 0x38000000 + ((i - 1024) << 13)
|
||||
|
||||
def convertExponent(i):
|
||||
if i == 0:
|
||||
return 0
|
||||
elif i in range(1, 31):
|
||||
return i << 23
|
||||
elif i == 31:
|
||||
return 0x47800000
|
||||
elif i == 32:
|
||||
return 0x80000000
|
||||
elif i in range(33, 63):
|
||||
return 0x80000000 + ((i - 32) << 23)
|
||||
else:
|
||||
return 0xC7800000
|
||||
|
||||
def convertOffset(i):
|
||||
if i == 0 or i == 32:
|
||||
return 0
|
||||
else:
|
||||
return 1024
|
||||
|
||||
print """//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
// This file is automatically generated.
|
||||
|
||||
namespace gl
|
||||
{
|
||||
"""
|
||||
|
||||
print "const static unsigned g_mantissa[2048] = {"
|
||||
for i in range(0, 2048):
|
||||
print " %#010x," % convertMantissa(i)
|
||||
print "};\n"
|
||||
|
||||
print "const static unsigned g_exponent[64] = {"
|
||||
for i in range(0, 64):
|
||||
print " %#010x," % convertExponent(i)
|
||||
print "};\n"
|
||||
|
||||
print "const static unsigned g_offset[64] = {"
|
||||
for i in range(0, 64):
|
||||
print " %#010x," % convertOffset(i)
|
||||
print "};\n"
|
||||
|
||||
print """float float16ToFloat32(unsigned short h)
|
||||
{
|
||||
unsigned i32 = g_mantissa[g_offset[h >> 10] + (h & 0x3ff)] + g_exponent[h >> 10];
|
||||
return bitCast<float>(i32);
|
||||
}
|
||||
}
|
||||
"""
|
||||
80
gfx/angle/src/common/MemoryBuffer.cpp
Normal file
80
gfx/angle/src/common/MemoryBuffer.cpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
#include "common/MemoryBuffer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace rx
|
||||
{
|
||||
|
||||
MemoryBuffer::MemoryBuffer()
|
||||
: mSize(0),
|
||||
mData(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
MemoryBuffer::~MemoryBuffer()
|
||||
{
|
||||
free(mData);
|
||||
mData = NULL;
|
||||
}
|
||||
|
||||
bool MemoryBuffer::resize(size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
free(mData);
|
||||
mData = NULL;
|
||||
mSize = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (size == mSize)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only reallocate if the size has changed.
|
||||
uint8_t *newMemory = reinterpret_cast<uint8_t*>(malloc(sizeof(uint8_t) * size));
|
||||
if (newMemory == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mData)
|
||||
{
|
||||
// Copy the intersection of the old data and the new data
|
||||
std::copy(mData, mData + std::min(mSize, size), newMemory);
|
||||
free(mData);
|
||||
}
|
||||
|
||||
mData = newMemory;
|
||||
mSize = size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t MemoryBuffer::size() const
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
|
||||
const uint8_t *MemoryBuffer::data() const
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
uint8_t *MemoryBuffer::data()
|
||||
{
|
||||
ASSERT(mData);
|
||||
return mData;
|
||||
}
|
||||
|
||||
}
|
||||
38
gfx/angle/src/common/MemoryBuffer.h
Normal file
38
gfx/angle/src/common/MemoryBuffer.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
#ifndef COMMON_MEMORYBUFFER_H_
|
||||
#define COMMON_MEMORYBUFFER_H_
|
||||
|
||||
#include "common/angleutils.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace rx
|
||||
{
|
||||
|
||||
class MemoryBuffer : angle::NonCopyable
|
||||
{
|
||||
public:
|
||||
MemoryBuffer();
|
||||
~MemoryBuffer();
|
||||
|
||||
bool resize(size_t size);
|
||||
size_t size() const;
|
||||
bool empty() const { return mSize == 0; }
|
||||
|
||||
const uint8_t *data() const;
|
||||
uint8_t *data();
|
||||
|
||||
private:
|
||||
size_t mSize;
|
||||
uint8_t *mData;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // COMMON_MEMORYBUFFER_H_
|
||||
74
gfx/angle/src/common/Optional.h
Normal file
74
gfx/angle/src/common/Optional.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// 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.
|
||||
//
|
||||
// Optional.h:
|
||||
// Represents a type that may be invalid, similar to std::optional.
|
||||
//
|
||||
|
||||
#ifndef COMMON_OPTIONAL_H_
|
||||
#define COMMON_OPTIONAL_H_
|
||||
|
||||
template <class T>
|
||||
struct Optional
|
||||
{
|
||||
Optional()
|
||||
: mValid(false),
|
||||
mValue(T())
|
||||
{}
|
||||
|
||||
Optional(const T &valueIn) : mValid(true), mValue(valueIn) {}
|
||||
|
||||
Optional(const Optional &other)
|
||||
: mValid(other.mValid),
|
||||
mValue(other.mValue)
|
||||
{}
|
||||
|
||||
Optional &operator=(const Optional &other)
|
||||
{
|
||||
this->mValid = other.mValid;
|
||||
this->mValue = other.mValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Optional &operator=(const T &value)
|
||||
{
|
||||
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; }
|
||||
|
||||
bool operator==(const Optional &other) const
|
||||
{
|
||||
return ((mValid == other.mValid) && (!mValid || (mValue == other.mValue)));
|
||||
}
|
||||
|
||||
bool operator!=(const Optional &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
bool mValid;
|
||||
T mValue;
|
||||
};
|
||||
|
||||
#endif // COMMON_OPTIONAL_H_
|
||||
47
gfx/angle/src/common/Optional_unittest.cpp
Normal file
47
gfx/angle/src/common/Optional_unittest.cpp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// 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.
|
||||
//
|
||||
// Unit tests for ANGLE's Optional helper class.
|
||||
//
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "common/Optional.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
TEST(OptionalTest, BasicInvalid)
|
||||
{
|
||||
Optional<int> testInvalid;
|
||||
ASSERT_FALSE(testInvalid.valid());
|
||||
ASSERT_EQ(Optional<int>::Invalid(), testInvalid);
|
||||
}
|
||||
|
||||
TEST(OptionalTest, BasicValid)
|
||||
{
|
||||
Optional<int> testValid(3);
|
||||
ASSERT_TRUE(testValid.valid());
|
||||
ASSERT_EQ(3, testValid.value());
|
||||
ASSERT_NE(Optional<int>::Invalid(), testValid);
|
||||
}
|
||||
|
||||
TEST(OptionalTest, Copies)
|
||||
{
|
||||
Optional<int> testValid(3);
|
||||
Optional<int> testInvalid;
|
||||
|
||||
Optional<int> testCopy = testInvalid;
|
||||
ASSERT_FALSE(testCopy.valid());
|
||||
ASSERT_EQ(testInvalid, testCopy);
|
||||
|
||||
testCopy = testValid;
|
||||
ASSERT_TRUE(testCopy.valid());
|
||||
ASSERT_EQ(3, testCopy.value());
|
||||
ASSERT_EQ(testValid, testCopy);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
52
gfx/angle/src/common/angleutils.cpp
Normal file
52
gfx/angle/src/common/angleutils.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
#include "common/angleutils.h"
|
||||
#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
|
||||
int len = vsnprintf(&(outBuffer.front()), outBuffer.size(), fmt, vararg);
|
||||
if (len < 0 || static_cast<size_t>(len) >= outBuffer.size())
|
||||
{
|
||||
// Buffer was not large enough, calculate the required size and resize the buffer
|
||||
len = vsnprintf(NULL, 0, fmt, vararg);
|
||||
outBuffer.resize(len + 1);
|
||||
|
||||
// Print again
|
||||
len = vsnprintf(&(outBuffer.front()), outBuffer.size(), fmt, vararg);
|
||||
}
|
||||
ASSERT(len >= 0);
|
||||
return static_cast<size_t>(len);
|
||||
}
|
||||
|
||||
std::string FormatString(const char *fmt, va_list vararg)
|
||||
{
|
||||
static std::vector<char> buffer(512);
|
||||
|
||||
size_t len = FormatStringIntoVector(fmt, vararg, buffer);
|
||||
return std::string(&buffer[0], len);
|
||||
}
|
||||
|
||||
std::string FormatString(const char *fmt, ...)
|
||||
{
|
||||
va_list vararg;
|
||||
va_start(vararg, fmt);
|
||||
std::string result = FormatString(fmt, vararg);
|
||||
va_end(vararg);
|
||||
return result;
|
||||
}
|
||||
187
gfx/angle/src/common/angleutils.h
Normal file
187
gfx/angle/src/common/angleutils.h
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
//
|
||||
// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
// angleutils.h: Common ANGLE utilities.
|
||||
|
||||
#ifndef COMMON_ANGLEUTILS_H_
|
||||
#define COMMON_ANGLEUTILS_H_
|
||||
|
||||
#include "common/platform.h"
|
||||
|
||||
#include <climits>
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
// A helper class to disallow copy and assignment operators
|
||||
namespace angle
|
||||
{
|
||||
|
||||
class NonCopyable
|
||||
{
|
||||
public:
|
||||
NonCopyable() = default;
|
||||
~NonCopyable() = default;
|
||||
protected:
|
||||
NonCopyable(const NonCopyable&) = delete;
|
||||
void operator=(const NonCopyable&) = delete;
|
||||
};
|
||||
|
||||
extern const uintptr_t DirtyPointer;
|
||||
} // namespace angle
|
||||
|
||||
template <typename T, size_t N>
|
||||
constexpr inline size_t ArraySize(T (&)[N])
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
template <typename T, unsigned int N>
|
||||
void SafeRelease(T (&resourceBlock)[N])
|
||||
{
|
||||
for (unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
SafeRelease(resourceBlock[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SafeRelease(T& resource)
|
||||
{
|
||||
if (resource)
|
||||
{
|
||||
resource->Release();
|
||||
resource = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SafeDelete(T *&resource)
|
||||
{
|
||||
delete resource;
|
||||
resource = NULL;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SafeDeleteContainer(T& resource)
|
||||
{
|
||||
for (auto &element : resource)
|
||||
{
|
||||
SafeDelete(element);
|
||||
}
|
||||
resource.clear();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SafeDeleteArray(T*& resource)
|
||||
{
|
||||
delete[] resource;
|
||||
resource = NULL;
|
||||
}
|
||||
|
||||
// Provide a less-than function for comparing structs
|
||||
// Note: struct memory must be initialized to zero, because of packing gaps
|
||||
template <typename T>
|
||||
inline bool StructLessThan(const T &a, const T &b)
|
||||
{
|
||||
return (memcmp(&a, &b, sizeof(T)) < 0);
|
||||
}
|
||||
|
||||
// Provide a less-than function for comparing structs
|
||||
// Note: struct memory must be initialized to zero, because of packing gaps
|
||||
template <typename T>
|
||||
inline bool StructEquals(const T &a, const T &b)
|
||||
{
|
||||
return (memcmp(&a, &b, sizeof(T)) == 0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void StructZero(T *obj)
|
||||
{
|
||||
memset(obj, 0, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool IsMaskFlagSet(T mask, T flag)
|
||||
{
|
||||
// Handles multibit flags as well
|
||||
return (mask & flag) == flag;
|
||||
}
|
||||
|
||||
inline const char* MakeStaticString(const std::string &str)
|
||||
{
|
||||
static std::set<std::string> strings;
|
||||
std::set<std::string>::iterator it = strings.find(str);
|
||||
if (it != strings.end())
|
||||
{
|
||||
return it->c_str();
|
||||
}
|
||||
|
||||
return strings.insert(str).first->c_str();
|
||||
}
|
||||
|
||||
inline std::string ArrayString(unsigned int i)
|
||||
{
|
||||
// We assume UINT_MAX and GL_INVALID_INDEX are equal
|
||||
// See DynamicHLSL.cpp
|
||||
if (i == UINT_MAX)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::stringstream strstr;
|
||||
|
||||
strstr << "[";
|
||||
strstr << i;
|
||||
strstr << "]";
|
||||
|
||||
return strstr.str();
|
||||
}
|
||||
|
||||
inline std::string Str(int i)
|
||||
{
|
||||
std::stringstream strstr;
|
||||
strstr << i;
|
||||
return strstr.str();
|
||||
}
|
||||
|
||||
size_t FormatStringIntoVector(const char *fmt, va_list vararg, std::vector<char>& buffer);
|
||||
|
||||
std::string FormatString(const char *fmt, va_list vararg);
|
||||
std::string FormatString(const char *fmt, ...);
|
||||
|
||||
template <typename T>
|
||||
std::string ToString(const T &value)
|
||||
{
|
||||
std::ostringstream o;
|
||||
o << value;
|
||||
return o.str();
|
||||
}
|
||||
|
||||
// snprintf is not defined with MSVC prior to to msvc14
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#define GL_BGR565_ANGLEX 0x6ABB
|
||||
#define GL_BGRA4_ANGLEX 0x6ABC
|
||||
#define GL_BGR5_A1_ANGLEX 0x6ABD
|
||||
#define GL_INT_64_ANGLEX 0x6ABE
|
||||
#define GL_STRUCT_ANGLEX 0x6ABF
|
||||
|
||||
// Hidden enum for the NULL D3D device type.
|
||||
#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE 0x6AC0
|
||||
|
||||
#define ANGLE_TRY_CHECKED_MATH(result) \
|
||||
if (!result.IsValid()) \
|
||||
{ \
|
||||
return gl::Error(GL_INVALID_OPERATION, "Integer overflow."); \
|
||||
}
|
||||
|
||||
#endif // COMMON_ANGLEUTILS_H_
|
||||
179
gfx/angle/src/common/debug.cpp
Normal file
179
gfx/angle/src/common/debug.cpp
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
//
|
||||
// Copyright (c) 2002-2010 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.
|
||||
//
|
||||
|
||||
// debug.cpp: Debugging utilities.
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "common/angleutils.h"
|
||||
#include "common/platform.h"
|
||||
#include "common/Optional.h"
|
||||
|
||||
namespace gl
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class FormattedString final : angle::NonCopyable
|
||||
{
|
||||
public:
|
||||
FormattedString(const char *format, va_list vararg) : mFormat(format)
|
||||
{
|
||||
va_copy(mVarArg, vararg);
|
||||
}
|
||||
|
||||
const char *c_str() { return str().c_str(); }
|
||||
|
||||
const std::string &str()
|
||||
{
|
||||
if (!mMessage.valid())
|
||||
{
|
||||
mMessage = FormatString(mFormat, mVarArg);
|
||||
}
|
||||
return mMessage.value();
|
||||
}
|
||||
|
||||
size_t length()
|
||||
{
|
||||
c_str();
|
||||
return mMessage.value().length();
|
||||
}
|
||||
|
||||
private:
|
||||
const char *mFormat;
|
||||
va_list mVarArg;
|
||||
Optional<std::string> mMessage;
|
||||
};
|
||||
enum DebugTraceOutputType
|
||||
{
|
||||
DebugTraceOutputTypeNone,
|
||||
DebugTraceOutputTypeSetMarker,
|
||||
DebugTraceOutputTypeBeginEvent
|
||||
};
|
||||
|
||||
DebugAnnotator *g_debugAnnotator = nullptr;
|
||||
|
||||
void output(bool traceInDebugOnly, MessageType messageType, DebugTraceOutputType outputType,
|
||||
const char *format, va_list vararg)
|
||||
{
|
||||
if (DebugAnnotationsActive())
|
||||
{
|
||||
static std::vector<char> buffer(512);
|
||||
size_t len = FormatStringIntoVector(format, vararg, buffer);
|
||||
std::wstring formattedWideMessage(buffer.begin(), buffer.begin() + len);
|
||||
|
||||
ASSERT(g_debugAnnotator != nullptr);
|
||||
switch (outputType)
|
||||
{
|
||||
case DebugTraceOutputTypeNone:
|
||||
break;
|
||||
case DebugTraceOutputTypeBeginEvent:
|
||||
g_debugAnnotator->beginEvent(formattedWideMessage.c_str());
|
||||
break;
|
||||
case DebugTraceOutputTypeSetMarker:
|
||||
g_debugAnnotator->setMarker(formattedWideMessage.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FormattedString formattedMessage(format, vararg);
|
||||
|
||||
if (messageType == MESSAGE_ERR)
|
||||
{
|
||||
std::cerr << formattedMessage.c_str();
|
||||
#if !defined(NDEBUG) && defined(_MSC_VER)
|
||||
OutputDebugStringA(formattedMessage.c_str());
|
||||
#endif // !defined(NDEBUG) && defined(_MSC_VER)
|
||||
}
|
||||
|
||||
#if defined(ANGLE_ENABLE_DEBUG_TRACE)
|
||||
#if defined(NDEBUG)
|
||||
if (traceInDebugOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif // NDEBUG
|
||||
static std::ofstream file(TRACE_OUTPUT_FILE, std::ofstream::app);
|
||||
if (file)
|
||||
{
|
||||
file.write(formattedMessage.c_str(), formattedMessage.length());
|
||||
file.flush();
|
||||
}
|
||||
|
||||
#if defined(ANGLE_ENABLE_DEBUG_TRACE_TO_DEBUGGER)
|
||||
OutputDebugStringA(formattedMessage.c_str());
|
||||
#endif // ANGLE_ENABLE_DEBUG_TRACE_TO_DEBUGGER
|
||||
|
||||
#endif // ANGLE_ENABLE_DEBUG_TRACE
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool DebugAnnotationsActive()
|
||||
{
|
||||
#if defined(ANGLE_ENABLE_DEBUG_ANNOTATIONS)
|
||||
return g_debugAnnotator != nullptr && g_debugAnnotator->getStatus();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void InitializeDebugAnnotations(DebugAnnotator *debugAnnotator)
|
||||
{
|
||||
UninitializeDebugAnnotations();
|
||||
g_debugAnnotator = debugAnnotator;
|
||||
}
|
||||
|
||||
void UninitializeDebugAnnotations()
|
||||
{
|
||||
// Pointer is not managed.
|
||||
g_debugAnnotator = nullptr;
|
||||
}
|
||||
|
||||
void trace(bool traceInDebugOnly, MessageType messageType, const char *format, ...)
|
||||
{
|
||||
va_list vararg;
|
||||
va_start(vararg, format);
|
||||
output(traceInDebugOnly, messageType, DebugTraceOutputTypeSetMarker, format, vararg);
|
||||
va_end(vararg);
|
||||
}
|
||||
|
||||
ScopedPerfEventHelper::ScopedPerfEventHelper(const char* format, ...)
|
||||
{
|
||||
#if !defined(ANGLE_ENABLE_DEBUG_TRACE)
|
||||
if (!DebugAnnotationsActive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif // !ANGLE_ENABLE_DEBUG_TRACE
|
||||
va_list vararg;
|
||||
va_start(vararg, format);
|
||||
output(true, MESSAGE_EVENT, DebugTraceOutputTypeBeginEvent, format, vararg);
|
||||
va_end(vararg);
|
||||
}
|
||||
|
||||
ScopedPerfEventHelper::~ScopedPerfEventHelper()
|
||||
{
|
||||
if (DebugAnnotationsActive())
|
||||
{
|
||||
g_debugAnnotator->endEvent();
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream &DummyStream()
|
||||
{
|
||||
return std::cout;
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
174
gfx/angle/src/common/debug.h
Normal file
174
gfx/angle/src/common/debug.h
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
//
|
||||
// Copyright (c) 2002-2010 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.
|
||||
//
|
||||
|
||||
// debug.h: Debugging utilities.
|
||||
|
||||
#ifndef COMMON_DEBUG_H_
|
||||
#define COMMON_DEBUG_H_
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#include "common/angleutils.h"
|
||||
|
||||
#if !defined(TRACE_OUTPUT_FILE)
|
||||
#define TRACE_OUTPUT_FILE "angle_debug.txt"
|
||||
#endif
|
||||
|
||||
namespace gl
|
||||
{
|
||||
|
||||
enum MessageType
|
||||
{
|
||||
MESSAGE_TRACE,
|
||||
MESSAGE_FIXME,
|
||||
MESSAGE_ERR,
|
||||
MESSAGE_EVENT,
|
||||
};
|
||||
|
||||
// Outputs text to the debugging log, or the debugging window
|
||||
void trace(bool traceInDebugOnly, MessageType messageType, const char *format, ...);
|
||||
|
||||
// Pairs a D3D begin event with an end event.
|
||||
class ScopedPerfEventHelper : angle::NonCopyable
|
||||
{
|
||||
public:
|
||||
ScopedPerfEventHelper(const char* format, ...);
|
||||
~ScopedPerfEventHelper();
|
||||
};
|
||||
|
||||
// Wraps the D3D9/D3D11 debug annotation functions.
|
||||
class DebugAnnotator : angle::NonCopyable
|
||||
{
|
||||
public:
|
||||
DebugAnnotator() { };
|
||||
virtual ~DebugAnnotator() { };
|
||||
virtual void beginEvent(const wchar_t *eventName) = 0;
|
||||
virtual void endEvent() = 0;
|
||||
virtual void setMarker(const wchar_t *markerName) = 0;
|
||||
virtual bool getStatus() = 0;
|
||||
};
|
||||
|
||||
void InitializeDebugAnnotations(DebugAnnotator *debugAnnotator);
|
||||
void UninitializeDebugAnnotations();
|
||||
bool DebugAnnotationsActive();
|
||||
|
||||
// This class is used to explicitly ignore values in the conditional logging macros. This avoids
|
||||
// compiler warnings like "value computed is not used" and "statement has no effect".
|
||||
class LogMessageVoidify
|
||||
{
|
||||
public:
|
||||
LogMessageVoidify() {}
|
||||
// This has to be an operator with a precedence lower than << but higher than ?:
|
||||
void operator&(std::ostream &) {}
|
||||
};
|
||||
|
||||
// This can be any ostream, it is unused, but needs to be a valid reference.
|
||||
std::ostream &DummyStream();
|
||||
|
||||
} // namespace gl
|
||||
|
||||
#if defined(ANGLE_ENABLE_DEBUG_TRACE) || defined(ANGLE_ENABLE_DEBUG_ANNOTATIONS)
|
||||
#define ANGLE_TRACE_ENABLED
|
||||
#endif
|
||||
|
||||
#define ANGLE_EMPTY_STATEMENT for (;;) break
|
||||
#if !defined(NDEBUG) || defined(ANGLE_ENABLE_RELEASE_ASSERTS)
|
||||
#define ANGLE_ENABLE_ASSERTS
|
||||
#endif
|
||||
|
||||
// A macro to output a trace of a function call and its arguments to the debugging log
|
||||
#if defined(ANGLE_TRACE_ENABLED)
|
||||
#define TRACE(message, ...) gl::trace(true, gl::MESSAGE_TRACE, "trace: %s(%d): " message "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define TRACE(message, ...) (void(0))
|
||||
#endif
|
||||
|
||||
// A macro to output a function call and its arguments to the debugging log, to denote an item in need of fixing.
|
||||
#if defined(ANGLE_TRACE_ENABLED)
|
||||
#define FIXME(message, ...) gl::trace(false, gl::MESSAGE_FIXME, "fixme: %s(%d): " message "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define FIXME(message, ...) (void(0))
|
||||
#endif
|
||||
|
||||
// A macro to output a function call and its arguments to the debugging log, in case of error.
|
||||
#if defined(ANGLE_TRACE_ENABLED) || defined(ANGLE_ENABLE_ASSERTS)
|
||||
#define ERR(message, ...) gl::trace(false, gl::MESSAGE_ERR, "err: %s(%d): " message "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define ERR(message, ...) (void(0))
|
||||
#endif
|
||||
|
||||
// A macro to log a performance event around a scope.
|
||||
#if defined(ANGLE_TRACE_ENABLED)
|
||||
#if defined(_MSC_VER)
|
||||
#define EVENT(message, ...) gl::ScopedPerfEventHelper scopedPerfEventHelper ## __LINE__("%s" message "\n", __FUNCTION__, __VA_ARGS__);
|
||||
#else
|
||||
#define EVENT(message, ...) gl::ScopedPerfEventHelper scopedPerfEventHelper("%s" message "\n", __FUNCTION__, ##__VA_ARGS__);
|
||||
#endif // _MSC_VER
|
||||
#else
|
||||
#define EVENT(message, ...) (void(0))
|
||||
#endif
|
||||
|
||||
#if defined(ANGLE_TRACE_ENABLED)
|
||||
#undef ANGLE_TRACE_ENABLED
|
||||
#endif
|
||||
|
||||
#if defined(COMPILER_GCC) || defined(__clang__)
|
||||
#define ANGLE_CRASH() __builtin_trap()
|
||||
#else
|
||||
#define ANGLE_CRASH() ((void)(*(volatile char *)0 = 0))
|
||||
#endif
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#define ANGLE_ASSERT_IMPL(expression) assert(expression)
|
||||
#else
|
||||
// TODO(jmadill): Detect if debugger is attached and break.
|
||||
#define ANGLE_ASSERT_IMPL(expression) ANGLE_CRASH()
|
||||
#endif // !defined(NDEBUG)
|
||||
|
||||
// Helper macro which avoids evaluating the arguments to a stream if the condition doesn't hold.
|
||||
// Condition is evaluated once and only once.
|
||||
#define ANGLE_LAZY_STREAM(stream, condition) \
|
||||
!(condition) ? static_cast<void>(0) : ::gl::LogMessageVoidify() & (stream)
|
||||
|
||||
#if defined(NDEBUG) && !defined(ANGLE_ENABLE_ASSERTS)
|
||||
#define ANGLE_ASSERTS_ON 0
|
||||
#else
|
||||
#define ANGLE_ASSERTS_ON 1
|
||||
#endif
|
||||
|
||||
// A macro asserting a condition and outputting failures to the debug log
|
||||
#if ANGLE_ASSERTS_ON
|
||||
#define ASSERT(expression) \
|
||||
(expression ? static_cast<void>(0) \
|
||||
: (ERR("\t! Assert failed in %s(%d): %s\n", __FUNCTION__, __LINE__, #expression), \
|
||||
ANGLE_ASSERT_IMPL(expression)))
|
||||
#else
|
||||
#define ASSERT(condition) \
|
||||
ANGLE_LAZY_STREAM(::gl::DummyStream(), ANGLE_ASSERTS_ON ? !(condition) : false) \
|
||||
<< "Check failed: " #condition ". "
|
||||
#endif // ANGLE_ASSERTS_ON
|
||||
|
||||
#define UNUSED_VARIABLE(variable) ((void)variable)
|
||||
|
||||
// A macro to indicate unimplemented functionality
|
||||
#ifndef NOASSERT_UNIMPLEMENTED
|
||||
#define NOASSERT_UNIMPLEMENTED 1
|
||||
#endif
|
||||
|
||||
#define UNIMPLEMENTED() \
|
||||
{ \
|
||||
ERR("\t! Unimplemented: %s(%d)\n", __FUNCTION__, __LINE__); \
|
||||
ASSERT(NOASSERT_UNIMPLEMENTED); \
|
||||
} \
|
||||
ANGLE_EMPTY_STATEMENT
|
||||
|
||||
// A macro for code which is not expected to be reached under valid assumptions
|
||||
#define UNREACHABLE() \
|
||||
(ERR("\t! Unreachable reached: %s(%d)\n", __FUNCTION__, __LINE__), ASSERT(false))
|
||||
|
||||
#endif // COMMON_DEBUG_H_
|
||||
56
gfx/angle/src/common/event_tracer.cpp
Normal file
56
gfx/angle/src/common/event_tracer.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2012 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.
|
||||
|
||||
#include "common/event_tracer.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace angle
|
||||
{
|
||||
|
||||
const unsigned char *GetTraceCategoryEnabledFlag(const char *name)
|
||||
{
|
||||
angle::Platform *platform = ANGLEPlatformCurrent();
|
||||
ASSERT(platform);
|
||||
|
||||
const unsigned char *categoryEnabledFlag = platform->getTraceCategoryEnabledFlag(name);
|
||||
if (categoryEnabledFlag != nullptr)
|
||||
{
|
||||
return categoryEnabledFlag;
|
||||
}
|
||||
|
||||
static unsigned char disabled = 0;
|
||||
return &disabled;
|
||||
}
|
||||
|
||||
Platform::TraceEventHandle AddTraceEvent(char phase, const unsigned char* categoryGroupEnabled, const char* name, unsigned long long id,
|
||||
int numArgs, const char** argNames, const unsigned char* argTypes,
|
||||
const unsigned long long* argValues, unsigned char flags)
|
||||
{
|
||||
angle::Platform *platform = ANGLEPlatformCurrent();
|
||||
ASSERT(platform);
|
||||
|
||||
double timestamp = platform->monotonicallyIncreasingTime();
|
||||
|
||||
if (timestamp != 0)
|
||||
{
|
||||
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 angle
|
||||
22
gfx/angle/src/common/event_tracer.h
Normal file
22
gfx/angle/src/common/event_tracer.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2012 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.
|
||||
|
||||
#ifndef COMMON_EVENT_TRACER_H_
|
||||
#define COMMON_EVENT_TRACER_H_
|
||||
|
||||
#include "common/platform.h"
|
||||
#include "platform/Platform.h"
|
||||
|
||||
namespace angle
|
||||
{
|
||||
|
||||
const unsigned char *GetTraceCategoryEnabledFlag(const char* name);
|
||||
Platform::TraceEventHandle AddTraceEvent(char phase, const unsigned char* categoryGroupEnabled, const char* name,
|
||||
unsigned long long id, int numArgs, const char** argNames,
|
||||
const unsigned char* argTypes, const unsigned long long* argValues,
|
||||
unsigned char flags);
|
||||
|
||||
}
|
||||
|
||||
#endif // COMMON_EVENT_TRACER_H_
|
||||
73
gfx/angle/src/common/mathutil.cpp
Normal file
73
gfx/angle/src/common/mathutil.cpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
// mathutil.cpp: Math and bit manipulation functions.
|
||||
|
||||
#include "common/mathutil.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
|
||||
namespace gl
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct RGB9E5Data
|
||||
{
|
||||
unsigned int R : 9;
|
||||
unsigned int G : 9;
|
||||
unsigned int B : 9;
|
||||
unsigned int E : 5;
|
||||
};
|
||||
|
||||
// B is the exponent bias (15)
|
||||
constexpr int g_sharedexp_bias = 15;
|
||||
|
||||
// N is the number of mantissa bits per component (9)
|
||||
constexpr int g_sharedexp_mantissabits = 9;
|
||||
|
||||
// Emax is the maximum allowed biased exponent value (31)
|
||||
constexpr int g_sharedexp_maxexponent = 31;
|
||||
|
||||
constexpr float g_sharedexp_max =
|
||||
((static_cast<float>(1 << g_sharedexp_mantissabits) - 1) /
|
||||
static_cast<float>(1 << g_sharedexp_mantissabits)) *
|
||||
static_cast<float>(1 << (g_sharedexp_maxexponent - g_sharedexp_bias));
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
unsigned int convertRGBFloatsTo999E5(float red, float green, float blue)
|
||||
{
|
||||
const float red_c = std::max<float>(0, std::min(g_sharedexp_max, red));
|
||||
const float green_c = std::max<float>(0, std::min(g_sharedexp_max, green));
|
||||
const float blue_c = std::max<float>(0, std::min(g_sharedexp_max, 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 = 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 = 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 bitCast<unsigned int>(output);
|
||||
}
|
||||
|
||||
void convert999E5toRGBFloats(unsigned int input, float *red, float *green, float *blue)
|
||||
{
|
||||
const RGB9E5Data *inputData = reinterpret_cast<const RGB9E5Data*>(&input);
|
||||
|
||||
*red = inputData->R * pow(2.0f, (int)inputData->E - g_sharedexp_bias - g_sharedexp_mantissabits);
|
||||
*green = inputData->G * pow(2.0f, (int)inputData->E - g_sharedexp_bias - g_sharedexp_mantissabits);
|
||||
*blue = inputData->B * pow(2.0f, (int)inputData->E - g_sharedexp_bias - g_sharedexp_mantissabits);
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
852
gfx/angle/src/common/mathutil.h
Normal file
852
gfx/angle/src/common/mathutil.h
Normal file
|
|
@ -0,0 +1,852 @@
|
|||
//
|
||||
// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
// mathutil.h: Math and bit manipulation functions.
|
||||
|
||||
#ifndef COMMON_MATHUTIL_H_
|
||||
#define COMMON_MATHUTIL_H_
|
||||
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <base/numerics/safe_math.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/platform.h"
|
||||
|
||||
namespace angle
|
||||
{
|
||||
using base::CheckedNumeric;
|
||||
using base::IsValueInRangeForNumericType;
|
||||
}
|
||||
|
||||
namespace gl
|
||||
{
|
||||
|
||||
const unsigned int Float32One = 0x3F800000;
|
||||
const unsigned short Float16One = 0x3C00;
|
||||
|
||||
struct Vector4
|
||||
{
|
||||
Vector4() {}
|
||||
Vector4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {}
|
||||
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float w;
|
||||
};
|
||||
|
||||
struct Vector2
|
||||
{
|
||||
Vector2() {}
|
||||
Vector2(float x, float y) : x(x), y(y) {}
|
||||
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
inline bool isPow2(int x)
|
||||
{
|
||||
return (x & (x - 1)) == 0 && (x != 0);
|
||||
}
|
||||
|
||||
inline int log2(int x)
|
||||
{
|
||||
int r = 0;
|
||||
while ((x >> r) > 1) r++;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline unsigned int ceilPow2(unsigned int x)
|
||||
{
|
||||
if (x != 0) x--;
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
x++;
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
inline int clampToInt(unsigned int x)
|
||||
{
|
||||
return static_cast<int>(std::min(x, static_cast<unsigned int>(std::numeric_limits<int>::max())));
|
||||
}
|
||||
|
||||
template <typename DestT, typename SrcT>
|
||||
inline DestT clampCast(SrcT value)
|
||||
{
|
||||
static const DestT destLo = std::numeric_limits<DestT>::min();
|
||||
static const DestT destHi = std::numeric_limits<DestT>::max();
|
||||
static const SrcT srcLo = static_cast<SrcT>(destLo);
|
||||
static const SrcT srcHi = static_cast<SrcT>(destHi);
|
||||
|
||||
// When value is outside of or equal to the limits for DestT we use the DestT limit directly.
|
||||
// This avoids undefined behaviors due to loss of precision when converting from floats to
|
||||
// integers:
|
||||
// destHi for ints is 2147483647 but the closest float number is around 2147483648, so when
|
||||
// doing a conversion from float to int we run into an UB because the float is outside of the
|
||||
// range representable by the int.
|
||||
if (value <= srcLo)
|
||||
{
|
||||
return destLo;
|
||||
}
|
||||
else if (value >= srcHi)
|
||||
{
|
||||
return destHi;
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<DestT>(value);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename MIN, typename MAX>
|
||||
inline T clamp(T x, MIN min, MAX max)
|
||||
{
|
||||
// Since NaNs fail all comparison tests, a NaN value will default to min
|
||||
return x > min ? (x > max ? max : x) : min;
|
||||
}
|
||||
|
||||
inline float clamp01(float x)
|
||||
{
|
||||
return clamp(x, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
template<const int n>
|
||||
inline unsigned int unorm(float x)
|
||||
{
|
||||
const unsigned int max = 0xFFFFFFFF >> (32 - n);
|
||||
|
||||
if (x > 1)
|
||||
{
|
||||
return max;
|
||||
}
|
||||
else if (x < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (unsigned int)(max * x + 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool supportsSSE2()
|
||||
{
|
||||
#if defined(ANGLE_USE_SSE)
|
||||
static bool checked = false;
|
||||
static bool supports = false;
|
||||
|
||||
if (checked)
|
||||
{
|
||||
return supports;
|
||||
}
|
||||
|
||||
#if defined(ANGLE_PLATFORM_WINDOWS) && !defined(_M_ARM)
|
||||
{
|
||||
int info[4];
|
||||
__cpuid(info, 0);
|
||||
|
||||
if (info[0] >= 1)
|
||||
{
|
||||
__cpuid(info, 1);
|
||||
|
||||
supports = (info[3] >> 26) & 1;
|
||||
}
|
||||
}
|
||||
#endif // defined(ANGLE_PLATFORM_WINDOWS) && !defined(_M_ARM)
|
||||
checked = true;
|
||||
return supports;
|
||||
#else // defined(ANGLE_USE_SSE)
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename destType, typename sourceType>
|
||||
destType bitCast(const sourceType &source)
|
||||
{
|
||||
size_t copySize = std::min(sizeof(destType), sizeof(sourceType));
|
||||
destType output;
|
||||
memcpy(&output, &source, copySize);
|
||||
return output;
|
||||
}
|
||||
|
||||
inline unsigned short float32ToFloat16(float fp32)
|
||||
{
|
||||
unsigned int fp32i = bitCast<unsigned int>(fp32);
|
||||
unsigned int sign = (fp32i & 0x80000000) >> 16;
|
||||
unsigned int abs = fp32i & 0x7FFFFFFF;
|
||||
|
||||
if(abs > 0x47FFEFFF) // Infinity
|
||||
{
|
||||
return static_cast<unsigned short>(sign | 0x7FFF);
|
||||
}
|
||||
else if(abs < 0x38800000) // Denormal
|
||||
{
|
||||
unsigned int mantissa = (abs & 0x007FFFFF) | 0x00800000;
|
||||
int e = 113 - (abs >> 23);
|
||||
|
||||
if(e < 24)
|
||||
{
|
||||
abs = mantissa >> e;
|
||||
}
|
||||
else
|
||||
{
|
||||
abs = 0;
|
||||
}
|
||||
|
||||
return static_cast<unsigned short>(sign | (abs + 0x00000FFF + ((abs >> 13) & 1)) >> 13);
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<unsigned short>(sign | (abs + 0xC8000000 + 0x00000FFF + ((abs >> 13) & 1)) >> 13);
|
||||
}
|
||||
}
|
||||
|
||||
float float16ToFloat32(unsigned short h);
|
||||
|
||||
unsigned int convertRGBFloatsTo999E5(float red, float green, float blue);
|
||||
void convert999E5toRGBFloats(unsigned int input, float *red, float *green, float *blue);
|
||||
|
||||
inline unsigned short float32ToFloat11(float fp32)
|
||||
{
|
||||
const unsigned int float32MantissaMask = 0x7FFFFF;
|
||||
const unsigned int float32ExponentMask = 0x7F800000;
|
||||
const unsigned int float32SignMask = 0x80000000;
|
||||
const unsigned int float32ValueMask = ~float32SignMask;
|
||||
const unsigned int float32ExponentFirstBit = 23;
|
||||
const unsigned int float32ExponentBias = 127;
|
||||
|
||||
const unsigned short float11Max = 0x7BF;
|
||||
const unsigned short float11MantissaMask = 0x3F;
|
||||
const unsigned short float11ExponentMask = 0x7C0;
|
||||
const unsigned short float11BitMask = 0x7FF;
|
||||
const unsigned int float11ExponentBias = 14;
|
||||
|
||||
const unsigned int float32Maxfloat11 = 0x477E0000;
|
||||
const unsigned int float32Minfloat11 = 0x38800000;
|
||||
|
||||
const unsigned int float32Bits = bitCast<unsigned int>(fp32);
|
||||
const bool float32Sign = (float32Bits & float32SignMask) == float32SignMask;
|
||||
|
||||
unsigned int float32Val = float32Bits & float32ValueMask;
|
||||
|
||||
if ((float32Val & float32ExponentMask) == float32ExponentMask)
|
||||
{
|
||||
// INF or NAN
|
||||
if ((float32Val & float32MantissaMask) != 0)
|
||||
{
|
||||
return float11ExponentMask | (((float32Val >> 17) | (float32Val >> 11) | (float32Val >> 6) | (float32Val)) & float11MantissaMask);
|
||||
}
|
||||
else if (float32Sign)
|
||||
{
|
||||
// -INF is clamped to 0 since float11 is positive only
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return float11ExponentMask;
|
||||
}
|
||||
}
|
||||
else if (float32Sign)
|
||||
{
|
||||
// float11 is positive only, so clamp to zero
|
||||
return 0;
|
||||
}
|
||||
else if (float32Val > float32Maxfloat11)
|
||||
{
|
||||
// The number is too large to be represented as a float11, set to max
|
||||
return float11Max;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (float32Val < float32Minfloat11)
|
||||
{
|
||||
// The number is too small to be represented as a normalized float11
|
||||
// Convert it to a denormalized value.
|
||||
const unsigned int shift = (float32ExponentBias - float11ExponentBias) - (float32Val >> float32ExponentFirstBit);
|
||||
float32Val = ((1 << float32ExponentFirstBit) | (float32Val & float32MantissaMask)) >> shift;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rebias the exponent to represent the value as a normalized float11
|
||||
float32Val += 0xC8000000;
|
||||
}
|
||||
|
||||
return ((float32Val + 0xFFFF + ((float32Val >> 17) & 1)) >> 17) & float11BitMask;
|
||||
}
|
||||
}
|
||||
|
||||
inline unsigned short float32ToFloat10(float fp32)
|
||||
{
|
||||
const unsigned int float32MantissaMask = 0x7FFFFF;
|
||||
const unsigned int float32ExponentMask = 0x7F800000;
|
||||
const unsigned int float32SignMask = 0x80000000;
|
||||
const unsigned int float32ValueMask = ~float32SignMask;
|
||||
const unsigned int float32ExponentFirstBit = 23;
|
||||
const unsigned int float32ExponentBias = 127;
|
||||
|
||||
const unsigned short float10Max = 0x3DF;
|
||||
const unsigned short float10MantissaMask = 0x1F;
|
||||
const unsigned short float10ExponentMask = 0x3E0;
|
||||
const unsigned short float10BitMask = 0x3FF;
|
||||
const unsigned int float10ExponentBias = 14;
|
||||
|
||||
const unsigned int float32Maxfloat10 = 0x477C0000;
|
||||
const unsigned int float32Minfloat10 = 0x38800000;
|
||||
|
||||
const unsigned int float32Bits = bitCast<unsigned int>(fp32);
|
||||
const bool float32Sign = (float32Bits & float32SignMask) == float32SignMask;
|
||||
|
||||
unsigned int float32Val = float32Bits & float32ValueMask;
|
||||
|
||||
if ((float32Val & float32ExponentMask) == float32ExponentMask)
|
||||
{
|
||||
// INF or NAN
|
||||
if ((float32Val & float32MantissaMask) != 0)
|
||||
{
|
||||
return float10ExponentMask | (((float32Val >> 18) | (float32Val >> 13) | (float32Val >> 3) | (float32Val)) & float10MantissaMask);
|
||||
}
|
||||
else if (float32Sign)
|
||||
{
|
||||
// -INF is clamped to 0 since float11 is positive only
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return float10ExponentMask;
|
||||
}
|
||||
}
|
||||
else if (float32Sign)
|
||||
{
|
||||
// float10 is positive only, so clamp to zero
|
||||
return 0;
|
||||
}
|
||||
else if (float32Val > float32Maxfloat10)
|
||||
{
|
||||
// The number is too large to be represented as a float11, set to max
|
||||
return float10Max;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (float32Val < float32Minfloat10)
|
||||
{
|
||||
// The number is too small to be represented as a normalized float11
|
||||
// Convert it to a denormalized value.
|
||||
const unsigned int shift = (float32ExponentBias - float10ExponentBias) - (float32Val >> float32ExponentFirstBit);
|
||||
float32Val = ((1 << float32ExponentFirstBit) | (float32Val & float32MantissaMask)) >> shift;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rebias the exponent to represent the value as a normalized float11
|
||||
float32Val += 0xC8000000;
|
||||
}
|
||||
|
||||
return ((float32Val + 0x1FFFF + ((float32Val >> 18) & 1)) >> 18) & float10BitMask;
|
||||
}
|
||||
}
|
||||
|
||||
inline float float11ToFloat32(unsigned short fp11)
|
||||
{
|
||||
unsigned short exponent = (fp11 >> 6) & 0x1F;
|
||||
unsigned short mantissa = fp11 & 0x3F;
|
||||
|
||||
if (exponent == 0x1F)
|
||||
{
|
||||
// INF or NAN
|
||||
return bitCast<float>(0x7f800000 | (mantissa << 17));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (exponent != 0)
|
||||
{
|
||||
// normalized
|
||||
}
|
||||
else if (mantissa != 0)
|
||||
{
|
||||
// The value is denormalized
|
||||
exponent = 1;
|
||||
|
||||
do
|
||||
{
|
||||
exponent--;
|
||||
mantissa <<= 1;
|
||||
}
|
||||
while ((mantissa & 0x40) == 0);
|
||||
|
||||
mantissa = mantissa & 0x3F;
|
||||
}
|
||||
else // The value is zero
|
||||
{
|
||||
exponent = static_cast<unsigned short>(-112);
|
||||
}
|
||||
|
||||
return bitCast<float>(((exponent + 112) << 23) | (mantissa << 17));
|
||||
}
|
||||
}
|
||||
|
||||
inline float float10ToFloat32(unsigned short fp11)
|
||||
{
|
||||
unsigned short exponent = (fp11 >> 5) & 0x1F;
|
||||
unsigned short mantissa = fp11 & 0x1F;
|
||||
|
||||
if (exponent == 0x1F)
|
||||
{
|
||||
// INF or NAN
|
||||
return bitCast<float>(0x7f800000 | (mantissa << 17));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (exponent != 0)
|
||||
{
|
||||
// normalized
|
||||
}
|
||||
else if (mantissa != 0)
|
||||
{
|
||||
// The value is denormalized
|
||||
exponent = 1;
|
||||
|
||||
do
|
||||
{
|
||||
exponent--;
|
||||
mantissa <<= 1;
|
||||
}
|
||||
while ((mantissa & 0x20) == 0);
|
||||
|
||||
mantissa = mantissa & 0x1F;
|
||||
}
|
||||
else // The value is zero
|
||||
{
|
||||
exponent = static_cast<unsigned short>(-112);
|
||||
}
|
||||
|
||||
return bitCast<float>(((exponent + 112) << 23) | (mantissa << 18));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline float normalizedToFloat(T input)
|
||||
{
|
||||
static_assert(std::numeric_limits<T>::is_integer, "T must be an integer.");
|
||||
|
||||
const float inverseMax = 1.0f / std::numeric_limits<T>::max();
|
||||
return input * inverseMax;
|
||||
}
|
||||
|
||||
template <unsigned int inputBitCount, typename T>
|
||||
inline float normalizedToFloat(T input)
|
||||
{
|
||||
static_assert(std::numeric_limits<T>::is_integer, "T must be an integer.");
|
||||
static_assert(inputBitCount < (sizeof(T) * 8), "T must have more bits than inputBitCount.");
|
||||
|
||||
const float inverseMax = 1.0f / ((1 << inputBitCount) - 1);
|
||||
return input * inverseMax;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T floatToNormalized(float input)
|
||||
{
|
||||
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 static_cast<T>(((1 << outputBitCount) - 1) * input + 0.5f);
|
||||
}
|
||||
|
||||
template <unsigned int inputBitCount, unsigned int inputBitStart, typename T>
|
||||
inline T getShiftedData(T input)
|
||||
{
|
||||
static_assert(inputBitCount + inputBitStart <= (sizeof(T) * 8),
|
||||
"T must have at least as many bits as inputBitCount + inputBitStart.");
|
||||
const T mask = (1 << inputBitCount) - 1;
|
||||
return (input >> inputBitStart) & mask;
|
||||
}
|
||||
|
||||
template <unsigned int inputBitCount, unsigned int inputBitStart, typename T>
|
||||
inline T shiftData(T input)
|
||||
{
|
||||
static_assert(inputBitCount + inputBitStart <= (sizeof(T) * 8),
|
||||
"T must have at least as many bits as inputBitCount + inputBitStart.");
|
||||
const T mask = (1 << inputBitCount) - 1;
|
||||
return (input & mask) << inputBitStart;
|
||||
}
|
||||
|
||||
inline unsigned int CountLeadingZeros(uint32_t x)
|
||||
{
|
||||
// Use binary search to find the amount of leading zeros.
|
||||
unsigned int zeros = 32u;
|
||||
uint32_t y;
|
||||
|
||||
y = x >> 16u;
|
||||
if (y != 0)
|
||||
{
|
||||
zeros = zeros - 16u;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 8u;
|
||||
if (y != 0)
|
||||
{
|
||||
zeros = zeros - 8u;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 4u;
|
||||
if (y != 0)
|
||||
{
|
||||
zeros = zeros - 4u;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 2u;
|
||||
if (y != 0)
|
||||
{
|
||||
zeros = zeros - 2u;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 1u;
|
||||
if (y != 0)
|
||||
{
|
||||
return zeros - 2u;
|
||||
}
|
||||
return zeros - x;
|
||||
}
|
||||
|
||||
inline unsigned char average(unsigned char a, unsigned char b)
|
||||
{
|
||||
return ((a ^ b) >> 1) + (a & b);
|
||||
}
|
||||
|
||||
inline signed char average(signed char a, signed char b)
|
||||
{
|
||||
return ((short)a + (short)b) / 2;
|
||||
}
|
||||
|
||||
inline unsigned short average(unsigned short a, unsigned short b)
|
||||
{
|
||||
return ((a ^ b) >> 1) + (a & b);
|
||||
}
|
||||
|
||||
inline signed short average(signed short a, signed short b)
|
||||
{
|
||||
return ((int)a + (int)b) / 2;
|
||||
}
|
||||
|
||||
inline unsigned int average(unsigned int a, unsigned int b)
|
||||
{
|
||||
return ((a ^ b) >> 1) + (a & b);
|
||||
}
|
||||
|
||||
inline int average(int a, int b)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return (a + b) * 0.5f;
|
||||
}
|
||||
|
||||
inline unsigned short averageHalfFloat(unsigned short a, unsigned short b)
|
||||
{
|
||||
return float32ToFloat16((float16ToFloat32(a) + float16ToFloat32(b)) * 0.5f);
|
||||
}
|
||||
|
||||
inline unsigned int averageFloat11(unsigned int a, unsigned int b)
|
||||
{
|
||||
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(static_cast<unsigned short>(a)) + float10ToFloat32(static_cast<unsigned short>(b))) * 0.5f);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct Range
|
||||
{
|
||||
Range() {}
|
||||
Range(T lo, T hi) : start(lo), end(hi) { ASSERT(lo <= hi); }
|
||||
|
||||
T start;
|
||||
T end;
|
||||
|
||||
T length() const { return end - start; }
|
||||
|
||||
bool intersects(Range<T> other)
|
||||
{
|
||||
if (start <= other.start)
|
||||
{
|
||||
return other.start < end;
|
||||
}
|
||||
else
|
||||
{
|
||||
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 priv
|
||||
{
|
||||
template <unsigned int N, unsigned int R>
|
||||
struct iSquareRoot
|
||||
{
|
||||
static constexpr unsigned int solve()
|
||||
{
|
||||
return (R * R > N)
|
||||
? 0
|
||||
: ((R * R == N) ? R : static_cast<unsigned int>(iSquareRoot<N, R + 1>::value));
|
||||
}
|
||||
enum Result
|
||||
{
|
||||
value = iSquareRoot::solve()
|
||||
};
|
||||
};
|
||||
|
||||
template <unsigned int N>
|
||||
struct iSquareRoot<N, N>
|
||||
{
|
||||
enum result
|
||||
{
|
||||
value = N
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace priv
|
||||
|
||||
template <unsigned int N>
|
||||
constexpr unsigned int iSquareRoot()
|
||||
{
|
||||
return priv::iSquareRoot<N, 1>::value;
|
||||
}
|
||||
|
||||
// Sum, difference and multiplication operations for signed ints that wrap on 32-bit overflow.
|
||||
//
|
||||
// Unsigned types are defined to do arithmetic modulo 2^n in C++. For signed types, overflow
|
||||
// behavior is undefined.
|
||||
|
||||
template <typename T>
|
||||
inline T WrappingSum(T lhs, T rhs)
|
||||
{
|
||||
uint32_t lhsUnsigned = static_cast<uint32_t>(lhs);
|
||||
uint32_t rhsUnsigned = static_cast<uint32_t>(rhs);
|
||||
return static_cast<T>(lhsUnsigned + rhsUnsigned);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T WrappingDiff(T lhs, T rhs)
|
||||
{
|
||||
uint32_t lhsUnsigned = static_cast<uint32_t>(lhs);
|
||||
uint32_t rhsUnsigned = static_cast<uint32_t>(rhs);
|
||||
return static_cast<T>(lhsUnsigned - rhsUnsigned);
|
||||
}
|
||||
|
||||
inline int32_t WrappingMul(int32_t lhs, int32_t rhs)
|
||||
{
|
||||
int64_t lhsWide = static_cast<int64_t>(lhs);
|
||||
int64_t rhsWide = static_cast<int64_t>(rhs);
|
||||
// The multiplication is guaranteed not to overflow.
|
||||
int64_t resultWide = lhsWide * rhsWide;
|
||||
// Implement the desired wrapping behavior by masking out the high-order 32 bits.
|
||||
resultWide = resultWide & 0xffffffffll;
|
||||
// Casting to a narrower signed type is fine since the casted value is representable in the
|
||||
// narrower type.
|
||||
return static_cast<int32_t>(resultWide);
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
|
||||
namespace rx
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
T roundUp(const T value, const T alignment)
|
||||
{
|
||||
auto temp = value + alignment - static_cast<T>(1);
|
||||
return temp - temp % alignment;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
angle::CheckedNumeric<T> CheckedRoundUp(const T value, const T alignment)
|
||||
{
|
||||
angle::CheckedNumeric<T> checkedValue(value);
|
||||
angle::CheckedNumeric<T> checkedAlignment(alignment);
|
||||
return roundUp(checkedValue, checkedAlignment);
|
||||
}
|
||||
|
||||
inline unsigned int UnsignedCeilDivide(unsigned int value, unsigned int divisor)
|
||||
{
|
||||
unsigned int divided = value / divisor;
|
||||
return (divided + ((value % divisor == 0) ? 0 : 1));
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
#define ANGLE_ROTL(x,y) _rotl(x,y)
|
||||
#define ANGLE_ROTR16(x,y) _rotr16(x,y)
|
||||
|
||||
#else
|
||||
|
||||
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) ::rx::RotL(x, y)
|
||||
#define ANGLE_ROTR16(x, y) ::rx::RotR16(x, y)
|
||||
|
||||
#endif // namespace rx
|
||||
|
||||
}
|
||||
|
||||
#endif // COMMON_MATHUTIL_H_
|
||||
210
gfx/angle/src/common/mathutil_unittest.cpp
Normal file
210
gfx/angle/src/common/mathutil_unittest.cpp
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
//
|
||||
// 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.
|
||||
//
|
||||
// mathutil_unittest:
|
||||
// Unit tests for the utils defined in mathutil.h
|
||||
//
|
||||
|
||||
#include "mathutil.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace gl;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Test the correctness of packSnorm2x16 and unpackSnorm2x16 functions.
|
||||
// For floats f1 and f2, unpackSnorm2x16(packSnorm2x16(f1, f2)) should be same as f1 and f2.
|
||||
TEST(MathUtilTest, packAndUnpackSnorm2x16)
|
||||
{
|
||||
const float input[8][2] =
|
||||
{
|
||||
{ 0.0f, 0.0f },
|
||||
{ 1.0f, 1.0f },
|
||||
{ -1.0f, 1.0f },
|
||||
{ -1.0f, -1.0f },
|
||||
{ 0.875f, 0.75f },
|
||||
{ 0.00392f, -0.99215f },
|
||||
{ -0.000675f, 0.004954f },
|
||||
{ -0.6937f, -0.02146f }
|
||||
};
|
||||
const float floatFaultTolerance = 0.0001f;
|
||||
float outputVal1, outputVal2;
|
||||
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
{
|
||||
unpackSnorm2x16(packSnorm2x16(input[i][0], input[i][1]), &outputVal1, &outputVal2);
|
||||
EXPECT_NEAR(input[i][0], outputVal1, floatFaultTolerance);
|
||||
EXPECT_NEAR(input[i][1], outputVal2, floatFaultTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
// Test the correctness of packSnorm2x16 and unpackSnorm2x16 functions with infinity values,
|
||||
// result should be clamped to [-1, 1].
|
||||
TEST(MathUtilTest, packAndUnpackSnorm2x16Infinity)
|
||||
{
|
||||
const float floatFaultTolerance = 0.0001f;
|
||||
float outputVal1, outputVal2;
|
||||
|
||||
unpackSnorm2x16(packSnorm2x16(std::numeric_limits<float>::infinity(),
|
||||
std::numeric_limits<float>::infinity()), &outputVal1, &outputVal2);
|
||||
EXPECT_NEAR(1.0f, outputVal1, floatFaultTolerance);
|
||||
EXPECT_NEAR(1.0f, outputVal2, floatFaultTolerance);
|
||||
|
||||
unpackSnorm2x16(packSnorm2x16(std::numeric_limits<float>::infinity(),
|
||||
-std::numeric_limits<float>::infinity()), &outputVal1, &outputVal2);
|
||||
EXPECT_NEAR(1.0f, outputVal1, floatFaultTolerance);
|
||||
EXPECT_NEAR(-1.0f, outputVal2, floatFaultTolerance);
|
||||
|
||||
unpackSnorm2x16(packSnorm2x16(-std::numeric_limits<float>::infinity(),
|
||||
-std::numeric_limits<float>::infinity()), &outputVal1, &outputVal2);
|
||||
EXPECT_NEAR(-1.0f, outputVal1, floatFaultTolerance);
|
||||
EXPECT_NEAR(-1.0f, outputVal2, floatFaultTolerance);
|
||||
}
|
||||
|
||||
// Test the correctness of packUnorm2x16 and unpackUnorm2x16 functions.
|
||||
// For floats f1 and f2, unpackUnorm2x16(packUnorm2x16(f1, f2)) should be same as f1 and f2.
|
||||
TEST(MathUtilTest, packAndUnpackUnorm2x16)
|
||||
{
|
||||
const float input[8][2] =
|
||||
{
|
||||
{ 0.0f, 0.0f },
|
||||
{ 1.0f, 1.0f },
|
||||
{ -1.0f, 1.0f },
|
||||
{ -1.0f, -1.0f },
|
||||
{ 0.875f, 0.75f },
|
||||
{ 0.00392f, -0.99215f },
|
||||
{ -0.000675f, 0.004954f },
|
||||
{ -0.6937f, -0.02146f }
|
||||
};
|
||||
const float floatFaultTolerance = 0.0001f;
|
||||
float outputVal1, outputVal2;
|
||||
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
{
|
||||
unpackUnorm2x16(packUnorm2x16(input[i][0], input[i][1]), &outputVal1, &outputVal2);
|
||||
float expected = input[i][0] < 0.0f ? 0.0f : input[i][0];
|
||||
EXPECT_NEAR(expected, outputVal1, floatFaultTolerance);
|
||||
expected = input[i][1] < 0.0f ? 0.0f : input[i][1];
|
||||
EXPECT_NEAR(expected, outputVal2, floatFaultTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
// Test the correctness of packUnorm2x16 and unpackUnorm2x16 functions with infinity values,
|
||||
// result should be clamped to [0, 1].
|
||||
TEST(MathUtilTest, packAndUnpackUnorm2x16Infinity)
|
||||
{
|
||||
const float floatFaultTolerance = 0.0001f;
|
||||
float outputVal1, outputVal2;
|
||||
|
||||
unpackUnorm2x16(packUnorm2x16(std::numeric_limits<float>::infinity(),
|
||||
std::numeric_limits<float>::infinity()), &outputVal1, &outputVal2);
|
||||
EXPECT_NEAR(1.0f, outputVal1, floatFaultTolerance);
|
||||
EXPECT_NEAR(1.0f, outputVal2, floatFaultTolerance);
|
||||
|
||||
unpackUnorm2x16(packUnorm2x16(std::numeric_limits<float>::infinity(),
|
||||
-std::numeric_limits<float>::infinity()), &outputVal1, &outputVal2);
|
||||
EXPECT_NEAR(1.0f, outputVal1, floatFaultTolerance);
|
||||
EXPECT_NEAR(0.0f, outputVal2, floatFaultTolerance);
|
||||
|
||||
unpackUnorm2x16(packUnorm2x16(-std::numeric_limits<float>::infinity(),
|
||||
-std::numeric_limits<float>::infinity()), &outputVal1, &outputVal2);
|
||||
EXPECT_NEAR(0.0f, outputVal1, floatFaultTolerance);
|
||||
EXPECT_NEAR(0.0f, outputVal2, floatFaultTolerance);
|
||||
}
|
||||
|
||||
// Test the correctness of packHalf2x16 and unpackHalf2x16 functions.
|
||||
// For floats f1 and f2, unpackHalf2x16(packHalf2x16(f1, f2)) should be same as f1 and f2.
|
||||
TEST(MathUtilTest, packAndUnpackHalf2x16)
|
||||
{
|
||||
const float input[8][2] =
|
||||
{
|
||||
{ 0.0f, 0.0f },
|
||||
{ 1.0f, 1.0f },
|
||||
{ -1.0f, 1.0f },
|
||||
{ -1.0f, -1.0f },
|
||||
{ 0.875f, 0.75f },
|
||||
{ 0.00392f, -0.99215f },
|
||||
{ -0.000675f, 0.004954f },
|
||||
{ -0.6937f, -0.02146f },
|
||||
};
|
||||
const float floatFaultTolerance = 0.0005f;
|
||||
float outputVal1, outputVal2;
|
||||
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
{
|
||||
unpackHalf2x16(packHalf2x16(input[i][0], input[i][1]), &outputVal1, &outputVal2);
|
||||
EXPECT_NEAR(input[i][0], outputVal1, floatFaultTolerance);
|
||||
EXPECT_NEAR(input[i][1], outputVal2, floatFaultTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
// Test the correctness of gl::isNaN function.
|
||||
TEST(MathUtilTest, isNaN)
|
||||
{
|
||||
EXPECT_TRUE(isNaN(bitCast<float>(0xffu << 23 | 1u)));
|
||||
EXPECT_TRUE(isNaN(bitCast<float>(1u << 31 | 0xffu << 23 | 1u)));
|
||||
EXPECT_TRUE(isNaN(bitCast<float>(1u << 31 | 0xffu << 23 | 0x400000u)));
|
||||
EXPECT_TRUE(isNaN(bitCast<float>(1u << 31 | 0xffu << 23 | 0x7fffffu)));
|
||||
EXPECT_FALSE(isNaN(0.0f));
|
||||
EXPECT_FALSE(isNaN(bitCast<float>(1u << 31 | 0xffu << 23)));
|
||||
EXPECT_FALSE(isNaN(bitCast<float>(0xffu << 23)));
|
||||
}
|
||||
|
||||
// Test the correctness of gl::isInf function.
|
||||
TEST(MathUtilTest, isInf)
|
||||
{
|
||||
EXPECT_TRUE(isInf(bitCast<float>(0xffu << 23)));
|
||||
EXPECT_TRUE(isInf(bitCast<float>(1u << 31 | 0xffu << 23)));
|
||||
EXPECT_FALSE(isInf(0.0f));
|
||||
EXPECT_FALSE(isInf(bitCast<float>(0xffu << 23 | 1u)));
|
||||
EXPECT_FALSE(isInf(bitCast<float>(1u << 31 | 0xffu << 23 | 1u)));
|
||||
EXPECT_FALSE(isInf(bitCast<float>(1u << 31 | 0xffu << 23 | 0x400000u)));
|
||||
EXPECT_FALSE(isInf(bitCast<float>(1u << 31 | 0xffu << 23 | 0x7fffffu)));
|
||||
EXPECT_FALSE(isInf(bitCast<float>(0xfeu << 23 | 0x7fffffu)));
|
||||
EXPECT_FALSE(isInf(bitCast<float>(1u << 31 | 0xfeu << 23 | 0x7fffffu)));
|
||||
}
|
||||
|
||||
TEST(MathUtilTest, CountLeadingZeros)
|
||||
{
|
||||
for (unsigned int i = 0; i < 32u; ++i)
|
||||
{
|
||||
uint32_t iLeadingZeros = 1u << (31u - i);
|
||||
EXPECT_EQ(i, CountLeadingZeros(iLeadingZeros));
|
||||
}
|
||||
EXPECT_EQ(32u, CountLeadingZeros(0));
|
||||
}
|
||||
|
||||
// Some basic tests. Tests that rounding up zero produces zero.
|
||||
TEST(MathUtilTest, BasicRoundUp)
|
||||
{
|
||||
EXPECT_EQ(0u, rx::roundUp(0u, 4u));
|
||||
EXPECT_EQ(4u, rx::roundUp(1u, 4u));
|
||||
EXPECT_EQ(4u, rx::roundUp(4u, 4u));
|
||||
}
|
||||
|
||||
// Test that rounding up zero produces zero for checked ints.
|
||||
TEST(MathUtilTest, CheckedRoundUpZero)
|
||||
{
|
||||
auto checkedValue = rx::CheckedRoundUp(0u, 4u);
|
||||
ASSERT_TRUE(checkedValue.IsValid());
|
||||
ASSERT_EQ(0u, checkedValue.ValueOrDie());
|
||||
}
|
||||
|
||||
// Test out-of-bounds with CheckedRoundUp
|
||||
TEST(MathUtilTest, CheckedRoundUpInvalid)
|
||||
{
|
||||
// The answer to this query is out of bounds.
|
||||
auto limit = std::numeric_limits<unsigned int>::max();
|
||||
auto checkedValue = rx::CheckedRoundUp(limit, limit - 1);
|
||||
ASSERT_FALSE(checkedValue.IsValid());
|
||||
|
||||
// Our implementation can't handle this query, despite the parameters being in range.
|
||||
auto checkedLimit = rx::CheckedRoundUp(limit - 1, limit);
|
||||
ASSERT_FALSE(checkedLimit.IsValid());
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
386
gfx/angle/src/common/matrix_utils.h
Normal file
386
gfx/angle/src/common/matrix_utils.h
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
//
|
||||
// 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"
|
||||
#include "common/mathutil.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;
|
||||
}
|
||||
|
||||
void setToIdentity()
|
||||
{
|
||||
ASSERT(rows() == columns());
|
||||
|
||||
const auto one = T(1);
|
||||
const auto zero = T(0);
|
||||
|
||||
for (auto &e : mElements)
|
||||
e = zero;
|
||||
|
||||
for (unsigned int i = 0; i < rows(); ++i)
|
||||
{
|
||||
const auto pos = i * columns() + (i % columns());
|
||||
mElements[pos] = one;
|
||||
}
|
||||
}
|
||||
|
||||
template <unsigned int Size>
|
||||
static void setToIdentity(T(&matrix)[Size])
|
||||
{
|
||||
static_assert(gl::iSquareRoot<Size>() != 0, "Matrix is not square.");
|
||||
|
||||
const auto cols = gl::iSquareRoot<Size>();
|
||||
const auto one = T(1);
|
||||
const auto zero = T(0);
|
||||
|
||||
for (auto &e : matrix)
|
||||
e = zero;
|
||||
|
||||
for (unsigned int i = 0; i < cols; ++i)
|
||||
{
|
||||
const auto pos = i * cols + (i % cols);
|
||||
matrix[pos] = one;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<T> mElements;
|
||||
unsigned int mRows;
|
||||
unsigned int mCols;
|
||||
};
|
||||
|
||||
} // namespace angle
|
||||
|
||||
#endif // COMMON_MATRIX_UTILS_H_
|
||||
|
||||
184
gfx/angle/src/common/matrix_utils_unittest.cpp
Normal file
184
gfx/angle/src/common/matrix_utils_unittest.cpp
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
//
|
||||
// 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_utils_unittests:
|
||||
// Unit tests for the matrix utils.
|
||||
//
|
||||
|
||||
#include "matrix_utils.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace angle;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const unsigned int minDimensions = 2;
|
||||
const unsigned int maxDimensions = 4;
|
||||
|
||||
TEST(MatrixUtilsTest, MatrixConstructorTest)
|
||||
{
|
||||
for (unsigned int i = minDimensions; i <= maxDimensions; i++)
|
||||
{
|
||||
for (unsigned int j = minDimensions; j <= maxDimensions; j++)
|
||||
{
|
||||
unsigned int numElements = i * j;
|
||||
Matrix<float> m(std::vector<float>(numElements, 1.0f), i, j);
|
||||
EXPECT_EQ(m.rows(), i);
|
||||
EXPECT_EQ(m.columns(), j);
|
||||
EXPECT_EQ(m.elements(), std::vector<float>(numElements, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = minDimensions; i <= maxDimensions; i++)
|
||||
{
|
||||
unsigned int numElements = i * i;
|
||||
Matrix<float> m(std::vector<float>(numElements, 1.0f), i);
|
||||
EXPECT_EQ(m.size(), i);
|
||||
EXPECT_EQ(m.columns(), m.columns());
|
||||
EXPECT_EQ(m.elements(), std::vector<float>(numElements, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MatrixUtilsTest, MatrixCompMultTest)
|
||||
{
|
||||
for (unsigned int i = minDimensions; i <= maxDimensions; i++)
|
||||
{
|
||||
unsigned int numElements = i * i;
|
||||
Matrix<float> m1(std::vector<float>(numElements, 2.0f), i);
|
||||
Matrix<float> actualResult = m1.compMult(m1);
|
||||
std::vector<float> actualResultElements = actualResult.elements();
|
||||
std::vector<float> expectedResultElements(numElements, 4.0f);
|
||||
EXPECT_EQ(expectedResultElements, actualResultElements);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MatrixUtilsTest, MatrixOuterProductTest)
|
||||
{
|
||||
for (unsigned int i = minDimensions; i <= maxDimensions; i++)
|
||||
{
|
||||
for (unsigned int j = minDimensions; j <= maxDimensions; j++)
|
||||
{
|
||||
unsigned int numElements = i * j;
|
||||
Matrix<float> m1(std::vector<float>(numElements, 2.0f), i, 1);
|
||||
Matrix<float> m2(std::vector<float>(numElements, 2.0f), 1, j);
|
||||
Matrix<float> actualResult = m1.outerProduct(m2);
|
||||
EXPECT_EQ(actualResult.rows(), i);
|
||||
EXPECT_EQ(actualResult.columns(), j);
|
||||
std::vector<float> actualResultElements = actualResult.elements();
|
||||
std::vector<float> expectedResultElements(numElements, 4.0f);
|
||||
EXPECT_EQ(expectedResultElements, actualResultElements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MatrixUtilsTest, MatrixTransposeTest)
|
||||
{
|
||||
for (unsigned int i = minDimensions; i <= maxDimensions; i++)
|
||||
{
|
||||
for (unsigned int j = minDimensions; j <= maxDimensions; j++)
|
||||
{
|
||||
unsigned int numElements = i * j;
|
||||
Matrix<float> m1(std::vector<float>(numElements, 2.0f), i, j);
|
||||
Matrix<float> expectedResult = Matrix<float>(std::vector<float>(numElements, 2.0f), j, i);
|
||||
Matrix<float> actualResult = m1.transpose();
|
||||
EXPECT_EQ(expectedResult.elements(), actualResult.elements());
|
||||
EXPECT_EQ(actualResult.rows(), expectedResult.rows());
|
||||
EXPECT_EQ(actualResult.columns(), expectedResult.columns());
|
||||
// transpose(transpose(A)) = A
|
||||
Matrix<float> m2 = actualResult.transpose();
|
||||
EXPECT_EQ(m1.elements(), m2.elements());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MatrixUtilsTest, MatrixDeterminantTest)
|
||||
{
|
||||
for (unsigned int i = minDimensions; i <= maxDimensions; i++)
|
||||
{
|
||||
unsigned int numElements = i * i;
|
||||
Matrix<float> m(std::vector<float>(numElements, 2.0f), i);
|
||||
EXPECT_EQ(m.determinant(), 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MatrixUtilsTest, 2x2MatrixInverseTest)
|
||||
{
|
||||
float inputElements[] =
|
||||
{
|
||||
2.0f, 5.0f,
|
||||
3.0f, 7.0f
|
||||
};
|
||||
unsigned int numElements = 4;
|
||||
std::vector<float> input(inputElements, inputElements + numElements);
|
||||
Matrix<float> inputMatrix(input, 2);
|
||||
float identityElements[] =
|
||||
{
|
||||
1.0f, 0.0f,
|
||||
0.0f, 1.0f
|
||||
};
|
||||
std::vector<float> identityMatrix(identityElements, identityElements + numElements);
|
||||
// A * inverse(A) = I, where I is identity matrix.
|
||||
Matrix<float> result = inputMatrix * inputMatrix.inverse();
|
||||
EXPECT_EQ(identityMatrix, result.elements());
|
||||
}
|
||||
|
||||
TEST(MatrixUtilsTest, 3x3MatrixInverseTest)
|
||||
{
|
||||
float inputElements[] =
|
||||
{
|
||||
11.0f, 23.0f, 37.0f,
|
||||
13.0f, 29.0f, 41.0f,
|
||||
19.0f, 31.0f, 43.0f
|
||||
};
|
||||
unsigned int numElements = 9;
|
||||
std::vector<float> input(inputElements, inputElements + numElements);
|
||||
Matrix<float> inputMatrix(input, 3);
|
||||
float identityElements[] =
|
||||
{
|
||||
1.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f
|
||||
};
|
||||
std::vector<float> identityMatrix(identityElements, identityElements + numElements);
|
||||
// A * inverse(A) = I, where I is identity matrix.
|
||||
Matrix<float> result = inputMatrix * inputMatrix.inverse();
|
||||
std::vector<float> resultElements = result.elements();
|
||||
const float floatFaultTolarance = 0.000001f;
|
||||
for (size_t i = 0; i < numElements; i++)
|
||||
EXPECT_NEAR(resultElements[i], identityMatrix[i], floatFaultTolarance);
|
||||
}
|
||||
|
||||
TEST(MatrixUtilsTest, 4x4MatrixInverseTest)
|
||||
{
|
||||
float inputElements[] =
|
||||
{
|
||||
29.0f, 43.0f, 61.0f, 79.0f,
|
||||
31.0f, 47.0f, 67.0f, 83.0f,
|
||||
37.0f, 53.0f, 71.0f, 89.0f,
|
||||
41.0f, 59.0f, 73.0f, 97.0f
|
||||
};
|
||||
unsigned int numElements = 16;
|
||||
std::vector<float> input(inputElements, inputElements + numElements);
|
||||
Matrix<float> inputMatrix(input, 4);
|
||||
float identityElements[] =
|
||||
{
|
||||
1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
std::vector<float> identityMatrix(identityElements, identityElements + numElements);
|
||||
// A * inverse(A) = I, where I is identity matrix.
|
||||
Matrix<float> result = inputMatrix * inputMatrix.inverse();
|
||||
std::vector<float> resultElements = result.elements();
|
||||
const float floatFaultTolarance = 0.00001f;
|
||||
for (unsigned int i = 0; i < numElements; i++)
|
||||
EXPECT_NEAR(resultElements[i], identityMatrix[i], floatFaultTolarance);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
92
gfx/angle/src/common/platform.h
Normal file
92
gfx/angle/src/common/platform.h
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
// platform.h: Operating system specific includes and defines.
|
||||
|
||||
#ifndef COMMON_PLATFORM_H_
|
||||
#define COMMON_PLATFORM_H_
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
# define ANGLE_PLATFORM_WINDOWS 1
|
||||
#elif defined(__APPLE__)
|
||||
# define ANGLE_PLATFORM_APPLE 1
|
||||
# define ANGLE_PLATFORM_POSIX 1
|
||||
#elif defined(ANDROID)
|
||||
# define ANGLE_PLATFORM_ANDROID 1
|
||||
# define ANGLE_PLATFORM_POSIX 1
|
||||
#elif defined(__linux__) || defined(EMSCRIPTEN)
|
||||
# define ANGLE_PLATFORM_LINUX 1
|
||||
# define ANGLE_PLATFORM_POSIX 1
|
||||
#elif defined(__FreeBSD__) || \
|
||||
defined(__OpenBSD__) || \
|
||||
defined(__NetBSD__) || \
|
||||
defined(__DragonFly__) || \
|
||||
defined(__sun) || \
|
||||
defined(__GLIBC__) || \
|
||||
defined(__GNU__) || \
|
||||
defined(__QNX__)
|
||||
# define ANGLE_PLATFORM_POSIX 1
|
||||
#else
|
||||
# error Unsupported platform.
|
||||
#endif
|
||||
|
||||
#ifdef ANGLE_PLATFORM_WINDOWS
|
||||
# ifndef STRICT
|
||||
# define STRICT 1
|
||||
# endif
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN 1
|
||||
# endif
|
||||
# ifndef NOMINMAX
|
||||
# define NOMINMAX 1
|
||||
# endif
|
||||
|
||||
# include <windows.h>
|
||||
# include <intrin.h>
|
||||
|
||||
# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP)
|
||||
# define ANGLE_ENABLE_WINDOWS_STORE 1
|
||||
# endif
|
||||
|
||||
# if defined(ANGLE_ENABLE_D3D9)
|
||||
# include <d3d9.h>
|
||||
# include <d3dcompiler.h>
|
||||
# endif
|
||||
|
||||
# if defined(ANGLE_ENABLE_D3D11)
|
||||
# include <d3d10_1.h>
|
||||
# include <d3d11.h>
|
||||
# include <d3d11_1.h>
|
||||
# include <dxgi.h>
|
||||
# include <dxgi1_2.h>
|
||||
# include <d3dcompiler.h>
|
||||
# endif
|
||||
|
||||
# if defined(ANGLE_ENABLE_WINDOWS_STORE)
|
||||
# include <dxgi1_3.h>
|
||||
# if defined(_DEBUG)
|
||||
# include <DXProgrammableCapture.h>
|
||||
# include <dxgidebug.h>
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# undef near
|
||||
# undef far
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_M_ARM)
|
||||
#include <intrin.h>
|
||||
#define ANGLE_USE_SSE
|
||||
#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
|
||||
#include <x86intrin.h>
|
||||
#define ANGLE_USE_SSE
|
||||
#endif
|
||||
|
||||
// The MemoryBarrier function name collides with a macro under Windows
|
||||
// We will undef the macro so that the function name does not get replaced
|
||||
#undef MemoryBarrier
|
||||
|
||||
#endif // COMMON_PLATFORM_H_
|
||||
182
gfx/angle/src/common/string_utils.cpp
Normal file
182
gfx/angle/src/common/string_utils.cpp
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
//
|
||||
// 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 <algorithm>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "common/platform.h"
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
Optional<std::vector<wchar_t>> WidenString(size_t length, const char *cString)
|
||||
{
|
||||
std::vector<wchar_t> wcstring(length + 1);
|
||||
#if !defined(ANGLE_PLATFORM_WINDOWS)
|
||||
size_t written = mbstowcs(wcstring.data(), cString, length + 1);
|
||||
if (written == 0)
|
||||
{
|
||||
return Optional<std::vector<wchar_t>>::Invalid();
|
||||
}
|
||||
#else
|
||||
size_t convertedChars = 0;
|
||||
errno_t err = mbstowcs_s(&convertedChars, wcstring.data(), length + 1, cString, _TRUNCATE);
|
||||
if (err != 0)
|
||||
{
|
||||
return Optional<std::vector<wchar_t>>::Invalid();
|
||||
}
|
||||
#endif
|
||||
return Optional<std::vector<wchar_t>>(wcstring);
|
||||
}
|
||||
|
||||
bool BeginsWith(const std::string &str, const char *prefix)
|
||||
{
|
||||
return strncmp(str.c_str(), prefix, strlen(prefix)) == 0;
|
||||
}
|
||||
|
||||
bool BeginsWith(const char *str, const char *prefix)
|
||||
{
|
||||
return strncmp(str, prefix, strlen(prefix)) == 0;
|
||||
}
|
||||
|
||||
bool EndsWith(const std::string &str, const char *suffix)
|
||||
{
|
||||
const auto len = strlen(suffix);
|
||||
if (len > str.size())
|
||||
return false;
|
||||
|
||||
const char *end = str.c_str() + str.size() - len;
|
||||
|
||||
return memcmp(end, suffix, len) == 0;
|
||||
}
|
||||
|
||||
} // namespace angle
|
||||
67
gfx/angle/src/common/string_utils.h
Normal file
67
gfx/angle/src/common/string_utils.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
//
|
||||
// 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>
|
||||
|
||||
#include "common/Optional.h"
|
||||
|
||||
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);
|
||||
|
||||
Optional<std::vector<wchar_t>> WidenString(size_t length, const char *cString);
|
||||
|
||||
// Check if the string str begins with the given prefix.
|
||||
// Prefix may not be NULL and needs to be NULL terminated.
|
||||
// The comparison is case sensitive.
|
||||
bool BeginsWith(const std::string &str, const char *prefix);
|
||||
|
||||
// Check if the string str begins with the given prefix.
|
||||
// str and prefix may not be NULL and need to be NULL terminated.
|
||||
// The comparison is case sensitive.
|
||||
bool BeginsWith(const char *str, const char *prefix);
|
||||
|
||||
// Check if the string str ends with the given suffix.
|
||||
// Suffix may not be NUL and needs to be NULL terminated.
|
||||
// The comparison is case sensitive.
|
||||
bool EndsWith(const std::string& str, const char* suffix);
|
||||
}
|
||||
|
||||
#endif // LIBANGLE_STRING_UTILS_H_
|
||||
163
gfx/angle/src/common/string_utils_unittest.cpp
Normal file
163
gfx/angle/src/common/string_utils_unittest.cpp
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
//
|
||||
// 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_unittests:
|
||||
// Unit tests for the string utils.
|
||||
//
|
||||
|
||||
#include "string_utils.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace angle;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Basic SplitString tests
|
||||
TEST(StringUtilsTest, SplitString_Basics)
|
||||
{
|
||||
std::vector<std::string> r;
|
||||
|
||||
r = SplitString(std::string(), ",:;", KEEP_WHITESPACE, SPLIT_WANT_ALL);
|
||||
EXPECT_TRUE(r.empty());
|
||||
|
||||
// Empty separator list
|
||||
r = SplitString("hello, world", "", KEEP_WHITESPACE, SPLIT_WANT_ALL);
|
||||
ASSERT_EQ(1u, r.size());
|
||||
EXPECT_EQ("hello, world", r[0]);
|
||||
|
||||
// Should split on any of the separators.
|
||||
r = SplitString("::,,;;", ",:;", KEEP_WHITESPACE, SPLIT_WANT_ALL);
|
||||
ASSERT_EQ(7u, r.size());
|
||||
for (auto str : r)
|
||||
ASSERT_TRUE(str.empty());
|
||||
|
||||
r = SplitString("red, green; blue:", ",:;", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
|
||||
ASSERT_EQ(3u, r.size());
|
||||
EXPECT_EQ("red", r[0]);
|
||||
EXPECT_EQ("green", r[1]);
|
||||
EXPECT_EQ("blue", r[2]);
|
||||
|
||||
// Want to split a string along whitespace sequences.
|
||||
r = SplitString(" red green \tblue\n", " \t\n", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
|
||||
ASSERT_EQ(3u, r.size());
|
||||
EXPECT_EQ("red", r[0]);
|
||||
EXPECT_EQ("green", r[1]);
|
||||
EXPECT_EQ("blue", r[2]);
|
||||
|
||||
// Weird case of splitting on spaces but not trimming.
|
||||
r = SplitString(" red ", " ", TRIM_WHITESPACE, SPLIT_WANT_ALL);
|
||||
ASSERT_EQ(3u, r.size());
|
||||
EXPECT_EQ("", r[0]); // Before the first space.
|
||||
EXPECT_EQ("red", r[1]);
|
||||
EXPECT_EQ("", r[2]); // After the last space.
|
||||
}
|
||||
|
||||
// Check different whitespace and result types for SplitString
|
||||
TEST(StringUtilsTest, SplitString_WhitespaceAndResultType)
|
||||
{
|
||||
std::vector<std::string> r;
|
||||
|
||||
// Empty input handling.
|
||||
r = SplitString(std::string(), ",", KEEP_WHITESPACE, SPLIT_WANT_ALL);
|
||||
EXPECT_TRUE(r.empty());
|
||||
r = SplitString(std::string(), ",", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
|
||||
EXPECT_TRUE(r.empty());
|
||||
|
||||
// Input string is space and we're trimming.
|
||||
r = SplitString(" ", ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
|
||||
ASSERT_EQ(1u, r.size());
|
||||
EXPECT_EQ("", r[0]);
|
||||
r = SplitString(" ", ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
|
||||
EXPECT_TRUE(r.empty());
|
||||
|
||||
// Test all 4 combinations of flags on ", ,".
|
||||
r = SplitString(", ,", ",", KEEP_WHITESPACE, SPLIT_WANT_ALL);
|
||||
ASSERT_EQ(3u, r.size());
|
||||
EXPECT_EQ("", r[0]);
|
||||
EXPECT_EQ(" ", r[1]);
|
||||
EXPECT_EQ("", r[2]);
|
||||
r = SplitString(", ,", ",", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
|
||||
ASSERT_EQ(1u, r.size());
|
||||
ASSERT_EQ(" ", r[0]);
|
||||
r = SplitString(", ,", ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
|
||||
ASSERT_EQ(3u, r.size());
|
||||
EXPECT_EQ("", r[0]);
|
||||
EXPECT_EQ("", r[1]);
|
||||
EXPECT_EQ("", r[2]);
|
||||
r = SplitString(", ,", ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
|
||||
ASSERT_TRUE(r.empty());
|
||||
}
|
||||
|
||||
// Tests for TrimString
|
||||
TEST(StringUtilsTest, TrimString)
|
||||
{
|
||||
// Basic tests
|
||||
EXPECT_EQ("a", TrimString("a", kWhitespaceASCII));
|
||||
EXPECT_EQ("a", TrimString(" a", kWhitespaceASCII));
|
||||
EXPECT_EQ("a", TrimString("a ", kWhitespaceASCII));
|
||||
EXPECT_EQ("a", TrimString(" a ", kWhitespaceASCII));
|
||||
|
||||
// Tests with empty strings
|
||||
EXPECT_EQ("", TrimString("", kWhitespaceASCII));
|
||||
EXPECT_EQ("", TrimString(" \n\r\t", kWhitespaceASCII));
|
||||
EXPECT_EQ(" foo ", TrimString(" foo ", ""));
|
||||
|
||||
// Tests it doesn't removes characters in the middle
|
||||
EXPECT_EQ("foo bar", TrimString(" foo bar ", kWhitespaceASCII));
|
||||
|
||||
// Test with non-whitespace trimChars
|
||||
EXPECT_EQ(" ", TrimString("foo bar", "abcdefghijklmnopqrstuvwxyz"));
|
||||
}
|
||||
|
||||
// Basic functionality tests for HexStringToUInt
|
||||
TEST(StringUtilsTest, HexStringToUIntBasic)
|
||||
{
|
||||
unsigned int uintValue;
|
||||
|
||||
std::string emptyString;
|
||||
ASSERT_FALSE(HexStringToUInt(emptyString, &uintValue));
|
||||
|
||||
std::string testStringA("0xBADF00D");
|
||||
ASSERT_TRUE(HexStringToUInt(testStringA, &uintValue));
|
||||
EXPECT_EQ(0xBADF00Du, uintValue);
|
||||
|
||||
std::string testStringB("0xBADFOOD");
|
||||
EXPECT_FALSE(HexStringToUInt(testStringB, &uintValue));
|
||||
|
||||
std::string testStringC("BADF00D");
|
||||
EXPECT_TRUE(HexStringToUInt(testStringC, &uintValue));
|
||||
EXPECT_EQ(0xBADF00Du, uintValue);
|
||||
|
||||
std::string testStringD("0x BADF00D");
|
||||
EXPECT_FALSE(HexStringToUInt(testStringD, &uintValue));
|
||||
}
|
||||
|
||||
// Note: ReadFileToString is harder to test
|
||||
|
||||
|
||||
TEST(StringUtilsTest, BeginsEndsWith)
|
||||
{
|
||||
ASSERT_FALSE(BeginsWith("foo", "bar"));
|
||||
ASSERT_FALSE(BeginsWith("", "foo"));
|
||||
ASSERT_FALSE(BeginsWith("foo", "foobar"));
|
||||
|
||||
ASSERT_TRUE(BeginsWith("foobar", "foo"));
|
||||
ASSERT_TRUE(BeginsWith("foobar", ""));
|
||||
ASSERT_TRUE(BeginsWith("foo", "foo"));
|
||||
ASSERT_TRUE(BeginsWith("", ""));
|
||||
|
||||
ASSERT_FALSE(EndsWith("foo", "bar"));
|
||||
ASSERT_FALSE(EndsWith("", "bar"));
|
||||
ASSERT_FALSE(EndsWith("foo", "foobar"));
|
||||
|
||||
ASSERT_TRUE(EndsWith("foobar", "bar"));
|
||||
ASSERT_TRUE(EndsWith("foobar", ""));
|
||||
ASSERT_TRUE(EndsWith("bar", "bar"));
|
||||
ASSERT_TRUE(EndsWith("", ""));
|
||||
}
|
||||
|
||||
}
|
||||
16
gfx/angle/src/common/third_party/numerics/README.angle
vendored
Normal file
16
gfx/angle/src/common/third_party/numerics/README.angle
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
Name: Chromium: base/numerics
|
||||
Short Name: base::numerics
|
||||
Version:
|
||||
URL: https://chromium.googlesource.com/chromium/src.git/+/lkcr/base/numerics/
|
||||
SOURCE CODE: Copy the Chromium folder manually into this folder and run git cl format.
|
||||
Date: 30/05/2016
|
||||
Revision: 28b5bbb227d331c01e6ff9b2f8729732135aadc7 (Chromium)
|
||||
Security Critical: no
|
||||
License: Chromium
|
||||
License File: LICENSE in Chromium/src
|
||||
|
||||
Description:
|
||||
base::numerics is a library for doing some simple safe math and conversions. To update the checkout, simply
|
||||
overwrite the base/numerics folder with Chromium's latest. The only modifications are to the base/logging.h
|
||||
file which defines CHECK to be ASSERT to be compatible with ANGLE.
|
||||
|
||||
22
gfx/angle/src/common/third_party/numerics/base/logging.h
vendored
Normal file
22
gfx/angle/src/common/third_party/numerics/base/logging.h
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// logging.h: Compatiblity hacks for importing Chromium's base/numerics.
|
||||
|
||||
#ifndef BASE_LOGGING_H_
|
||||
#define BASE_LOGGING_H_
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
#ifndef CHECK
|
||||
#define CHECK(X) ASSERT(X)
|
||||
#endif
|
||||
|
||||
// Unfortunately ANGLE relies on ASSERT being an empty statement, which these libs don't respect.
|
||||
#ifndef NOTREACHED
|
||||
#define NOTREACHED() UNREACHABLE()
|
||||
#endif
|
||||
|
||||
#endif // BASE_LOGGING_H_
|
||||
3
gfx/angle/src/common/third_party/numerics/base/numerics/OWNERS
vendored
Normal file
3
gfx/angle/src/common/third_party/numerics/base/numerics/OWNERS
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
jschuh@chromium.org
|
||||
tsepez@chromium.org
|
||||
|
||||
174
gfx/angle/src/common/third_party/numerics/base/numerics/safe_conversions.h
vendored
Normal file
174
gfx/angle/src/common/third_party/numerics/base/numerics/safe_conversions.h
vendored
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_
|
||||
#define BASE_NUMERICS_SAFE_CONVERSIONS_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/numerics/safe_conversions_impl.h"
|
||||
|
||||
namespace base
|
||||
{
|
||||
|
||||
// Convenience function that returns true if the supplied value is in range
|
||||
// for the destination type.
|
||||
template <typename Dst, typename Src>
|
||||
constexpr bool IsValueInRangeForNumericType(Src value)
|
||||
{
|
||||
return internal::DstRangeRelationToSrcRange<Dst>(value) == internal::RANGE_VALID;
|
||||
}
|
||||
|
||||
// Convenience function for determining if a numeric value is negative without
|
||||
// throwing compiler warnings on: unsigned(value) < 0.
|
||||
template <typename T>
|
||||
constexpr typename std::enable_if<std::numeric_limits<T>::is_signed, bool>::type IsValueNegative(
|
||||
T value)
|
||||
{
|
||||
static_assert(std::numeric_limits<T>::is_specialized, "Argument must be numeric.");
|
||||
return value < 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr typename std::enable_if<!std::numeric_limits<T>::is_signed, bool>::type IsValueNegative(T)
|
||||
{
|
||||
static_assert(std::numeric_limits<T>::is_specialized, "Argument must be numeric.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// checked_cast<> is analogous to static_cast<> for numeric types,
|
||||
// except that it CHECKs that the specified numeric conversion will not
|
||||
// overflow or underflow. NaN source will always trigger a CHECK.
|
||||
template <typename Dst, typename Src>
|
||||
inline Dst checked_cast(Src value)
|
||||
{
|
||||
CHECK(IsValueInRangeForNumericType<Dst>(value));
|
||||
return static_cast<Dst>(value);
|
||||
}
|
||||
|
||||
// HandleNaN will cause this class to CHECK(false).
|
||||
struct SaturatedCastNaNBehaviorCheck
|
||||
{
|
||||
template <typename T>
|
||||
static T HandleNaN()
|
||||
{
|
||||
CHECK(false);
|
||||
return T();
|
||||
}
|
||||
};
|
||||
|
||||
// HandleNaN will return 0 in this case.
|
||||
struct SaturatedCastNaNBehaviorReturnZero
|
||||
{
|
||||
template <typename T>
|
||||
static constexpr T HandleNaN()
|
||||
{
|
||||
return T();
|
||||
}
|
||||
};
|
||||
|
||||
namespace internal
|
||||
{
|
||||
// This wrapper is used for C++11 constexpr support by avoiding the declaration
|
||||
// of local variables in the saturated_cast template function.
|
||||
template <typename Dst, class NaNHandler, typename Src>
|
||||
constexpr Dst saturated_cast_impl(const Src value, const RangeConstraint constraint)
|
||||
{
|
||||
return constraint == RANGE_VALID
|
||||
? static_cast<Dst>(value)
|
||||
: (constraint == RANGE_UNDERFLOW
|
||||
? std::numeric_limits<Dst>::min()
|
||||
: (constraint == RANGE_OVERFLOW
|
||||
? std::numeric_limits<Dst>::max()
|
||||
: (constraint == RANGE_INVALID
|
||||
? NaNHandler::template HandleNaN<Dst>()
|
||||
: (NOTREACHED(), static_cast<Dst>(value)))));
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
// saturated_cast<> is analogous to static_cast<> for numeric types, except
|
||||
// that the specified numeric conversion will saturate rather than overflow or
|
||||
// underflow. NaN assignment to an integral will defer the behavior to a
|
||||
// specified class. By default, it will return 0.
|
||||
template <typename Dst, class NaNHandler = SaturatedCastNaNBehaviorReturnZero, typename Src>
|
||||
constexpr Dst saturated_cast(Src value)
|
||||
{
|
||||
return std::numeric_limits<Dst>::is_iec559
|
||||
? static_cast<Dst>(value) // Floating point optimization.
|
||||
: internal::saturated_cast_impl<Dst, NaNHandler>(
|
||||
value, internal::DstRangeRelationToSrcRange<Dst>(value));
|
||||
}
|
||||
|
||||
// strict_cast<> is analogous to static_cast<> for numeric types, except that
|
||||
// it will cause a compile failure if the destination type is not large enough
|
||||
// to contain any value in the source type. It performs no runtime checking.
|
||||
template <typename Dst, typename Src>
|
||||
constexpr Dst strict_cast(Src value)
|
||||
{
|
||||
static_assert(std::numeric_limits<Src>::is_specialized, "Argument must be numeric.");
|
||||
static_assert(std::numeric_limits<Dst>::is_specialized, "Result must be numeric.");
|
||||
static_assert((internal::StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
|
||||
internal::NUMERIC_RANGE_CONTAINED),
|
||||
"The numeric conversion is out of range for this type. You "
|
||||
"should probably use one of the following conversion "
|
||||
"mechanisms on the value you want to pass:\n"
|
||||
"- base::checked_cast\n"
|
||||
"- base::saturated_cast\n"
|
||||
"- base::CheckedNumeric");
|
||||
|
||||
return static_cast<Dst>(value);
|
||||
}
|
||||
|
||||
// StrictNumeric implements compile time range checking between numeric types by
|
||||
// wrapping assignment operations in a strict_cast. This class is intended to be
|
||||
// used for function arguments and return types, to ensure the destination type
|
||||
// can always contain the source type. This is essentially the same as enforcing
|
||||
// -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied
|
||||
// incrementally at API boundaries, making it easier to convert code so that it
|
||||
// compiles cleanly with truncation warnings enabled.
|
||||
// This template should introduce no runtime overhead, but it also provides no
|
||||
// runtime checking of any of the associated mathematical operations. Use
|
||||
// CheckedNumeric for runtime range checks of the actual value being assigned.
|
||||
template <typename T>
|
||||
class StrictNumeric
|
||||
{
|
||||
public:
|
||||
typedef T type;
|
||||
|
||||
constexpr StrictNumeric() : value_(0) {}
|
||||
|
||||
// Copy constructor.
|
||||
template <typename Src>
|
||||
constexpr StrictNumeric(const StrictNumeric<Src> &rhs) : value_(strict_cast<T>(rhs.value_))
|
||||
{
|
||||
}
|
||||
|
||||
// This is not an explicit constructor because we implicitly upgrade regular
|
||||
// numerics to StrictNumerics to make them easier to use.
|
||||
template <typename Src>
|
||||
constexpr StrictNumeric(Src value) : value_(strict_cast<T>(value))
|
||||
{
|
||||
}
|
||||
|
||||
// The numeric cast operator basically handles all the magic.
|
||||
template <typename Dst>
|
||||
constexpr operator Dst() const
|
||||
{
|
||||
return strict_cast<Dst>(value_);
|
||||
}
|
||||
|
||||
private:
|
||||
const T value_;
|
||||
};
|
||||
|
||||
// Explicitly make a shorter size_t typedef for convenience.
|
||||
typedef StrictNumeric<size_t> SizeT;
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_
|
||||
269
gfx/angle/src/common/third_party/numerics/base/numerics/safe_conversions_impl.h
vendored
Normal file
269
gfx/angle/src/common/third_party/numerics/base/numerics/safe_conversions_impl.h
vendored
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|
||||
#define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <climits>
|
||||
#include <limits>
|
||||
|
||||
namespace base
|
||||
{
|
||||
namespace internal
|
||||
{
|
||||
|
||||
// The std library doesn't provide a binary max_exponent for integers, however
|
||||
// we can compute one by adding one to the number of non-sign bits. This allows
|
||||
// for accurate range comparisons between floating point and integer types.
|
||||
template <typename NumericType>
|
||||
struct MaxExponent
|
||||
{
|
||||
static_assert(std::is_arithmetic<NumericType>::value, "Argument must be numeric.");
|
||||
static const int value =
|
||||
std::numeric_limits<NumericType>::is_iec559
|
||||
? std::numeric_limits<NumericType>::max_exponent
|
||||
: (sizeof(NumericType) * CHAR_BIT + 1 - std::numeric_limits<NumericType>::is_signed);
|
||||
};
|
||||
|
||||
enum IntegerRepresentation
|
||||
{
|
||||
INTEGER_REPRESENTATION_UNSIGNED,
|
||||
INTEGER_REPRESENTATION_SIGNED
|
||||
};
|
||||
|
||||
// A range for a given nunmeric Src type is contained for a given numeric Dst
|
||||
// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
|
||||
// numeric_limits<Src>::min() >= numeric_limits<Dst>::min() are true.
|
||||
// We implement this as template specializations rather than simple static
|
||||
// comparisons to ensure type correctness in our comparisons.
|
||||
enum NumericRangeRepresentation
|
||||
{
|
||||
NUMERIC_RANGE_NOT_CONTAINED,
|
||||
NUMERIC_RANGE_CONTAINED
|
||||
};
|
||||
|
||||
// Helper templates to statically determine if our destination type can contain
|
||||
// maximum and minimum values represented by the source type.
|
||||
|
||||
template <typename Dst,
|
||||
typename Src,
|
||||
IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
|
||||
? INTEGER_REPRESENTATION_SIGNED
|
||||
: INTEGER_REPRESENTATION_UNSIGNED,
|
||||
IntegerRepresentation SrcSign = std::numeric_limits<Src>::is_signed
|
||||
? INTEGER_REPRESENTATION_SIGNED
|
||||
: INTEGER_REPRESENTATION_UNSIGNED>
|
||||
struct StaticDstRangeRelationToSrcRange;
|
||||
|
||||
// Same sign: Dst is guaranteed to contain Src only if its range is equal or
|
||||
// larger.
|
||||
template <typename Dst, typename Src, IntegerRepresentation Sign>
|
||||
struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign>
|
||||
{
|
||||
static const NumericRangeRepresentation value =
|
||||
MaxExponent<Dst>::value >= MaxExponent<Src>::value ? NUMERIC_RANGE_CONTAINED
|
||||
: NUMERIC_RANGE_NOT_CONTAINED;
|
||||
};
|
||||
|
||||
// Unsigned to signed: Dst is guaranteed to contain source only if its range is
|
||||
// larger.
|
||||
template <typename Dst, typename Src>
|
||||
struct StaticDstRangeRelationToSrcRange<Dst,
|
||||
Src,
|
||||
INTEGER_REPRESENTATION_SIGNED,
|
||||
INTEGER_REPRESENTATION_UNSIGNED>
|
||||
{
|
||||
static const NumericRangeRepresentation value =
|
||||
MaxExponent<Dst>::value > MaxExponent<Src>::value ? NUMERIC_RANGE_CONTAINED
|
||||
: NUMERIC_RANGE_NOT_CONTAINED;
|
||||
};
|
||||
|
||||
// Signed to unsigned: Dst cannot be statically determined to contain Src.
|
||||
template <typename Dst, typename Src>
|
||||
struct StaticDstRangeRelationToSrcRange<Dst,
|
||||
Src,
|
||||
INTEGER_REPRESENTATION_UNSIGNED,
|
||||
INTEGER_REPRESENTATION_SIGNED>
|
||||
{
|
||||
static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
|
||||
};
|
||||
|
||||
enum RangeConstraint : unsigned char
|
||||
{
|
||||
RANGE_VALID = 0x0, // Value can be represented by the destination type.
|
||||
RANGE_UNDERFLOW = 0x1, // Value would overflow.
|
||||
RANGE_OVERFLOW = 0x2, // Value would underflow.
|
||||
RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN).
|
||||
};
|
||||
|
||||
// Helper function for coercing an int back to a RangeContraint.
|
||||
constexpr RangeConstraint GetRangeConstraint(int integer_range_constraint)
|
||||
{
|
||||
// TODO(jschuh): Once we get full C++14 support we want this
|
||||
// assert(integer_range_constraint >= RANGE_VALID &&
|
||||
// integer_range_constraint <= RANGE_INVALID)
|
||||
return static_cast<RangeConstraint>(integer_range_constraint);
|
||||
}
|
||||
|
||||
// This function creates a RangeConstraint from an upper and lower bound
|
||||
// check by taking advantage of the fact that only NaN can be out of range in
|
||||
// both directions at once.
|
||||
constexpr inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound, bool is_in_lower_bound)
|
||||
{
|
||||
return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) |
|
||||
(is_in_lower_bound ? 0 : RANGE_UNDERFLOW));
|
||||
}
|
||||
|
||||
// The following helper template addresses a corner case in range checks for
|
||||
// conversion from a floating-point type to an integral type of smaller range
|
||||
// but larger precision (e.g. float -> unsigned). The problem is as follows:
|
||||
// 1. Integral maximum is always one less than a power of two, so it must be
|
||||
// truncated to fit the mantissa of the floating point. The direction of
|
||||
// rounding is implementation defined, but by default it's always IEEE
|
||||
// floats, which round to nearest and thus result in a value of larger
|
||||
// magnitude than the integral value.
|
||||
// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
|
||||
// // is 4294967295u.
|
||||
// 2. If the floating point value is equal to the promoted integral maximum
|
||||
// value, a range check will erroneously pass.
|
||||
// Example: (4294967296f <= 4294967295u) // This is true due to a precision
|
||||
// // loss in rounding up to float.
|
||||
// 3. When the floating point value is then converted to an integral, the
|
||||
// resulting value is out of range for the target integral type and
|
||||
// thus is implementation defined.
|
||||
// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
|
||||
// To fix this bug we manually truncate the maximum value when the destination
|
||||
// type is an integral of larger precision than the source floating-point type,
|
||||
// such that the resulting maximum is represented exactly as a floating point.
|
||||
template <typename Dst, typename Src>
|
||||
struct NarrowingRange
|
||||
{
|
||||
typedef typename std::numeric_limits<Src> SrcLimits;
|
||||
typedef typename std::numeric_limits<Dst> DstLimits;
|
||||
// The following logic avoids warnings where the max function is
|
||||
// instantiated with invalid values for a bit shift (even though
|
||||
// such a function can never be called).
|
||||
static const int shift = (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
|
||||
SrcLimits::digits < DstLimits::digits &&
|
||||
SrcLimits::is_iec559 &&
|
||||
DstLimits::is_integer)
|
||||
? (DstLimits::digits - SrcLimits::digits)
|
||||
: 0;
|
||||
|
||||
static constexpr Dst max()
|
||||
{
|
||||
// We use UINTMAX_C below to avoid compiler warnings about shifting floating
|
||||
// points. Since it's a compile time calculation, it shouldn't have any
|
||||
// performance impact.
|
||||
return DstLimits::max() - static_cast<Dst>((UINTMAX_C(1) << shift) - 1);
|
||||
}
|
||||
|
||||
static constexpr Dst min()
|
||||
{
|
||||
return std::numeric_limits<Dst>::is_iec559 ? -DstLimits::max() : DstLimits::min();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Dst,
|
||||
typename Src,
|
||||
IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
|
||||
? INTEGER_REPRESENTATION_SIGNED
|
||||
: INTEGER_REPRESENTATION_UNSIGNED,
|
||||
IntegerRepresentation SrcSign = std::numeric_limits<Src>::is_signed
|
||||
? INTEGER_REPRESENTATION_SIGNED
|
||||
: INTEGER_REPRESENTATION_UNSIGNED,
|
||||
NumericRangeRepresentation DstRange = StaticDstRangeRelationToSrcRange<Dst, Src>::value>
|
||||
struct DstRangeRelationToSrcRangeImpl;
|
||||
|
||||
// The following templates are for ranges that must be verified at runtime. We
|
||||
// split it into checks based on signedness to avoid confusing casts and
|
||||
// compiler warnings on signed an unsigned comparisons.
|
||||
|
||||
// Dst range is statically determined to contain Src: Nothing to check.
|
||||
template <typename Dst, typename Src, IntegerRepresentation DstSign, IntegerRepresentation SrcSign>
|
||||
struct DstRangeRelationToSrcRangeImpl<Dst, Src, DstSign, SrcSign, NUMERIC_RANGE_CONTAINED>
|
||||
{
|
||||
static constexpr RangeConstraint Check(Src value) { return RANGE_VALID; }
|
||||
};
|
||||
|
||||
// Signed to signed narrowing: Both the upper and lower boundaries may be
|
||||
// exceeded.
|
||||
template <typename Dst, typename Src>
|
||||
struct DstRangeRelationToSrcRangeImpl<Dst,
|
||||
Src,
|
||||
INTEGER_REPRESENTATION_SIGNED,
|
||||
INTEGER_REPRESENTATION_SIGNED,
|
||||
NUMERIC_RANGE_NOT_CONTAINED>
|
||||
{
|
||||
static constexpr RangeConstraint Check(Src value)
|
||||
{
|
||||
return GetRangeConstraint((value <= NarrowingRange<Dst, Src>::max()),
|
||||
(value >= NarrowingRange<Dst, Src>::min()));
|
||||
}
|
||||
};
|
||||
|
||||
// Unsigned to unsigned narrowing: Only the upper boundary can be exceeded.
|
||||
template <typename Dst, typename Src>
|
||||
struct DstRangeRelationToSrcRangeImpl<Dst,
|
||||
Src,
|
||||
INTEGER_REPRESENTATION_UNSIGNED,
|
||||
INTEGER_REPRESENTATION_UNSIGNED,
|
||||
NUMERIC_RANGE_NOT_CONTAINED>
|
||||
{
|
||||
static constexpr RangeConstraint Check(Src value)
|
||||
{
|
||||
return GetRangeConstraint(value <= NarrowingRange<Dst, Src>::max(), true);
|
||||
}
|
||||
};
|
||||
|
||||
// Unsigned to signed: The upper boundary may be exceeded.
|
||||
template <typename Dst, typename Src>
|
||||
struct DstRangeRelationToSrcRangeImpl<Dst,
|
||||
Src,
|
||||
INTEGER_REPRESENTATION_SIGNED,
|
||||
INTEGER_REPRESENTATION_UNSIGNED,
|
||||
NUMERIC_RANGE_NOT_CONTAINED>
|
||||
{
|
||||
static constexpr RangeConstraint Check(Src value)
|
||||
{
|
||||
return sizeof(Dst) > sizeof(Src)
|
||||
? RANGE_VALID
|
||||
: GetRangeConstraint(value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()),
|
||||
true);
|
||||
}
|
||||
};
|
||||
|
||||
// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
|
||||
// and any negative value exceeds the lower boundary.
|
||||
template <typename Dst, typename Src>
|
||||
struct DstRangeRelationToSrcRangeImpl<Dst,
|
||||
Src,
|
||||
INTEGER_REPRESENTATION_UNSIGNED,
|
||||
INTEGER_REPRESENTATION_SIGNED,
|
||||
NUMERIC_RANGE_NOT_CONTAINED>
|
||||
{
|
||||
static constexpr RangeConstraint Check(Src value)
|
||||
{
|
||||
return (MaxExponent<Dst>::value >= MaxExponent<Src>::value)
|
||||
? GetRangeConstraint(true, value >= static_cast<Src>(0))
|
||||
: GetRangeConstraint(value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()),
|
||||
value >= static_cast<Src>(0));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
constexpr RangeConstraint DstRangeRelationToSrcRange(Src value)
|
||||
{
|
||||
static_assert(std::numeric_limits<Src>::is_specialized, "Argument must be numeric.");
|
||||
static_assert(std::numeric_limits<Dst>::is_specialized, "Result must be numeric.");
|
||||
return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|
||||
324
gfx/angle/src/common/third_party/numerics/base/numerics/safe_math.h
vendored
Normal file
324
gfx/angle/src/common/third_party/numerics/base/numerics/safe_math.h
vendored
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_SAFE_MATH_H_
|
||||
#define BASE_NUMERICS_SAFE_MATH_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/numerics/safe_math_impl.h"
|
||||
|
||||
namespace base
|
||||
{
|
||||
|
||||
namespace internal
|
||||
{
|
||||
|
||||
// CheckedNumeric implements all the logic and operators for detecting integer
|
||||
// boundary conditions such as overflow, underflow, and invalid conversions.
|
||||
// The CheckedNumeric type implicitly converts from floating point and integer
|
||||
// data types, and contains overloads for basic arithmetic operations (i.e.: +,
|
||||
// -, *, /, %).
|
||||
//
|
||||
// The following methods convert from CheckedNumeric to standard numeric values:
|
||||
// IsValid() - Returns true if the underlying numeric value is valid (i.e. has
|
||||
// has not wrapped and is not the result of an invalid conversion).
|
||||
// ValueOrDie() - Returns the underlying value. If the state is not valid this
|
||||
// call will crash on a CHECK.
|
||||
// ValueOrDefault() - Returns the current value, or the supplied default if the
|
||||
// state is not valid.
|
||||
// ValueFloating() - Returns the underlying floating point value (valid only
|
||||
// only for floating point CheckedNumeric types).
|
||||
//
|
||||
// Bitwise operations are explicitly not supported, because correct
|
||||
// handling of some cases (e.g. sign manipulation) is ambiguous. Comparison
|
||||
// operations are explicitly not supported because they could result in a crash
|
||||
// on a CHECK condition. You should use patterns like the following for these
|
||||
// operations:
|
||||
// Bitwise operation:
|
||||
// CheckedNumeric<int> checked_int = untrusted_input_value;
|
||||
// int x = checked_int.ValueOrDefault(0) | kFlagValues;
|
||||
// Comparison:
|
||||
// CheckedNumeric<size_t> checked_size = untrusted_input_value;
|
||||
// checked_size += HEADER LENGTH;
|
||||
// if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size)
|
||||
// Do stuff...
|
||||
template <typename T>
|
||||
class CheckedNumeric
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "CheckedNumeric<T>: T must be a numeric type.");
|
||||
|
||||
public:
|
||||
typedef T type;
|
||||
|
||||
CheckedNumeric() {}
|
||||
|
||||
// Copy constructor.
|
||||
template <typename Src>
|
||||
CheckedNumeric(const CheckedNumeric<Src> &rhs) : state_(rhs.ValueUnsafe(), rhs.validity())
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Src>
|
||||
CheckedNumeric(Src value, RangeConstraint validity) : state_(value, validity)
|
||||
{
|
||||
}
|
||||
|
||||
// This is not an explicit constructor because we implicitly upgrade regular
|
||||
// numerics to CheckedNumerics to make them easier to use.
|
||||
template <typename Src>
|
||||
CheckedNumeric(Src value) // NOLINT(runtime/explicit)
|
||||
: state_(value)
|
||||
{
|
||||
static_assert(std::numeric_limits<Src>::is_specialized, "Argument must be numeric.");
|
||||
}
|
||||
|
||||
// This is not an explicit constructor because we want a seamless conversion
|
||||
// from StrictNumeric types.
|
||||
template <typename Src>
|
||||
CheckedNumeric(StrictNumeric<Src> value) // NOLINT(runtime/explicit)
|
||||
: state_(static_cast<Src>(value))
|
||||
{
|
||||
}
|
||||
|
||||
// IsValid() is the public API to test if a CheckedNumeric is currently valid.
|
||||
bool IsValid() const { return validity() == RANGE_VALID; }
|
||||
|
||||
// ValueOrDie() The primary accessor for the underlying value. If the current
|
||||
// state is not valid it will CHECK and crash.
|
||||
T ValueOrDie() const
|
||||
{
|
||||
CHECK(IsValid());
|
||||
return state_.value();
|
||||
}
|
||||
|
||||
// ValueOrDefault(T default_value) A convenience method that returns the
|
||||
// current value if the state is valid, and the supplied default_value for
|
||||
// any other state.
|
||||
T ValueOrDefault(T default_value) const { return IsValid() ? state_.value() : default_value; }
|
||||
|
||||
// ValueFloating() - Since floating point values include their validity state,
|
||||
// we provide an easy method for extracting them directly, without a risk of
|
||||
// crashing on a CHECK.
|
||||
T ValueFloating() const
|
||||
{
|
||||
static_assert(std::numeric_limits<T>::is_iec559, "Argument must be float.");
|
||||
return CheckedNumeric<T>::cast(*this).ValueUnsafe();
|
||||
}
|
||||
|
||||
// validity() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now for
|
||||
// tests and to avoid a big matrix of friend operator overloads. But the
|
||||
// values it returns are likely to change in the future.
|
||||
// Returns: current validity state (i.e. valid, overflow, underflow, nan).
|
||||
// TODO(jschuh): crbug.com/332611 Figure out and implement semantics for
|
||||
// saturation/wrapping so we can expose this state consistently and implement
|
||||
// saturated arithmetic.
|
||||
RangeConstraint validity() const { return state_.validity(); }
|
||||
|
||||
// ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now
|
||||
// for tests and to avoid a big matrix of friend operator overloads. But the
|
||||
// values it returns are likely to change in the future.
|
||||
// Returns: the raw numeric value, regardless of the current state.
|
||||
// TODO(jschuh): crbug.com/332611 Figure out and implement semantics for
|
||||
// saturation/wrapping so we can expose this state consistently and implement
|
||||
// saturated arithmetic.
|
||||
T ValueUnsafe() const { return state_.value(); }
|
||||
|
||||
// Prototypes for the supported arithmetic operator overloads.
|
||||
template <typename Src>
|
||||
CheckedNumeric &operator+=(Src rhs);
|
||||
template <typename Src>
|
||||
CheckedNumeric &operator-=(Src rhs);
|
||||
template <typename Src>
|
||||
CheckedNumeric &operator*=(Src rhs);
|
||||
template <typename Src>
|
||||
CheckedNumeric &operator/=(Src rhs);
|
||||
template <typename Src>
|
||||
CheckedNumeric &operator%=(Src rhs);
|
||||
|
||||
CheckedNumeric operator-() const
|
||||
{
|
||||
RangeConstraint validity;
|
||||
T value = CheckedNeg(state_.value(), &validity);
|
||||
// Negation is always valid for floating point.
|
||||
if (std::numeric_limits<T>::is_iec559)
|
||||
return CheckedNumeric<T>(value);
|
||||
|
||||
validity = GetRangeConstraint(state_.validity() | validity);
|
||||
return CheckedNumeric<T>(value, validity);
|
||||
}
|
||||
|
||||
CheckedNumeric Abs() const
|
||||
{
|
||||
RangeConstraint validity;
|
||||
T value = CheckedAbs(state_.value(), &validity);
|
||||
// Absolute value is always valid for floating point.
|
||||
if (std::numeric_limits<T>::is_iec559)
|
||||
return CheckedNumeric<T>(value);
|
||||
|
||||
validity = GetRangeConstraint(state_.validity() | validity);
|
||||
return CheckedNumeric<T>(value, validity);
|
||||
}
|
||||
|
||||
// This function is available only for integral types. It returns an unsigned
|
||||
// integer of the same width as the source type, containing the absolute value
|
||||
// of the source, and properly handling signed min.
|
||||
CheckedNumeric<typename UnsignedOrFloatForSize<T>::type> UnsignedAbs() const
|
||||
{
|
||||
return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(
|
||||
CheckedUnsignedAbs(state_.value()), state_.validity());
|
||||
}
|
||||
|
||||
CheckedNumeric &operator++()
|
||||
{
|
||||
*this += 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CheckedNumeric operator++(int)
|
||||
{
|
||||
CheckedNumeric value = *this;
|
||||
*this += 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
CheckedNumeric &operator--()
|
||||
{
|
||||
*this -= 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CheckedNumeric operator--(int)
|
||||
{
|
||||
CheckedNumeric value = *this;
|
||||
*this -= 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
// These static methods behave like a convenience cast operator targeting
|
||||
// the desired CheckedNumeric type. As an optimization, a reference is
|
||||
// returned when Src is the same type as T.
|
||||
template <typename Src>
|
||||
static CheckedNumeric<T> cast(
|
||||
Src u,
|
||||
typename std::enable_if<std::numeric_limits<Src>::is_specialized, int>::type = 0)
|
||||
{
|
||||
return u;
|
||||
}
|
||||
|
||||
template <typename Src>
|
||||
static CheckedNumeric<T> cast(
|
||||
const CheckedNumeric<Src> &u,
|
||||
typename std::enable_if<!std::is_same<Src, T>::value, int>::type = 0)
|
||||
{
|
||||
return u;
|
||||
}
|
||||
|
||||
static const CheckedNumeric<T> &cast(const CheckedNumeric<T> &u) { return u; }
|
||||
|
||||
private:
|
||||
template <typename NumericType>
|
||||
struct UnderlyingType
|
||||
{
|
||||
using type = NumericType;
|
||||
};
|
||||
|
||||
template <typename NumericType>
|
||||
struct UnderlyingType<CheckedNumeric<NumericType>>
|
||||
{
|
||||
using type = NumericType;
|
||||
};
|
||||
|
||||
CheckedNumericState<T> state_;
|
||||
};
|
||||
|
||||
// This is the boilerplate for the standard arithmetic operator overloads. A
|
||||
// macro isn't the prettiest solution, but it beats rewriting these five times.
|
||||
// Some details worth noting are:
|
||||
// * We apply the standard arithmetic promotions.
|
||||
// * We skip range checks for floating points.
|
||||
// * We skip range checks for destination integers with sufficient range.
|
||||
// TODO(jschuh): extract these out into templates.
|
||||
#define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \
|
||||
/* Binary arithmetic operator for CheckedNumerics of the same type. */ \
|
||||
template <typename T> \
|
||||
CheckedNumeric<typename ArithmeticPromotion<T>::type> operator OP( \
|
||||
const CheckedNumeric<T> &lhs, const CheckedNumeric<T> &rhs) \
|
||||
{ \
|
||||
typedef typename ArithmeticPromotion<T>::type Promotion; \
|
||||
/* Floating point always takes the fast path */ \
|
||||
if (std::numeric_limits<T>::is_iec559) \
|
||||
return CheckedNumeric<T>(lhs.ValueUnsafe() OP rhs.ValueUnsafe()); \
|
||||
if (IsIntegerArithmeticSafe<Promotion, T, T>::value) \
|
||||
return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \
|
||||
GetRangeConstraint(rhs.validity() | lhs.validity())); \
|
||||
RangeConstraint validity = RANGE_VALID; \
|
||||
T result = \
|
||||
static_cast<T>(Checked##NAME(static_cast<Promotion>(lhs.ValueUnsafe()), \
|
||||
static_cast<Promotion>(rhs.ValueUnsafe()), &validity)); \
|
||||
return CheckedNumeric<Promotion>( \
|
||||
result, GetRangeConstraint(validity | lhs.validity() | rhs.validity())); \
|
||||
} \
|
||||
/* Assignment arithmetic operator implementation from CheckedNumeric. */ \
|
||||
template <typename T> \
|
||||
template <typename Src> \
|
||||
CheckedNumeric<T> &CheckedNumeric<T>::operator COMPOUND_OP(Src rhs) \
|
||||
{ \
|
||||
*this = CheckedNumeric<T>::cast(*this) \
|
||||
OP CheckedNumeric<typename UnderlyingType<Src>::type>::cast(rhs); \
|
||||
return *this; \
|
||||
} \
|
||||
/* Binary arithmetic operator for CheckedNumeric of different type. */ \
|
||||
template <typename T, typename Src> \
|
||||
CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
|
||||
const CheckedNumeric<Src> &lhs, const CheckedNumeric<T> &rhs) \
|
||||
{ \
|
||||
typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
|
||||
if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
|
||||
return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \
|
||||
GetRangeConstraint(rhs.validity() | lhs.validity())); \
|
||||
return CheckedNumeric<Promotion>::cast(lhs) OP CheckedNumeric<Promotion>::cast(rhs); \
|
||||
} \
|
||||
/* Binary arithmetic operator for left CheckedNumeric and right numeric. */ \
|
||||
template <typename T, typename Src, \
|
||||
typename std::enable_if<std::is_arithmetic<Src>::value>::type * = nullptr> \
|
||||
CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
|
||||
const CheckedNumeric<T> &lhs, Src rhs) \
|
||||
{ \
|
||||
typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
|
||||
if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
|
||||
return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs, lhs.validity()); \
|
||||
return CheckedNumeric<Promotion>::cast(lhs) OP CheckedNumeric<Promotion>::cast(rhs); \
|
||||
} \
|
||||
/* Binary arithmetic operator for left numeric and right CheckedNumeric. */ \
|
||||
template <typename T, typename Src, \
|
||||
typename std::enable_if<std::is_arithmetic<Src>::value>::type * = nullptr> \
|
||||
CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
|
||||
Src lhs, const CheckedNumeric<T> &rhs) \
|
||||
{ \
|
||||
typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
|
||||
if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
|
||||
return CheckedNumeric<Promotion>(lhs OP rhs.ValueUnsafe(), rhs.validity()); \
|
||||
return CheckedNumeric<Promotion>::cast(lhs) OP CheckedNumeric<Promotion>::cast(rhs); \
|
||||
}
|
||||
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, +=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /=)
|
||||
BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %=)
|
||||
|
||||
#undef BASE_NUMERIC_ARITHMETIC_OPERATORS
|
||||
|
||||
} // namespace internal
|
||||
|
||||
using internal::CheckedNumeric;
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_SAFE_MATH_H_
|
||||
570
gfx/angle/src/common/third_party/numerics/base/numerics/safe_math_impl.h
vendored
Normal file
570
gfx/angle/src/common/third_party/numerics/base/numerics/safe_math_impl.h
vendored
Normal file
|
|
@ -0,0 +1,570 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_
|
||||
#define BASE_NUMERICS_SAFE_MATH_IMPL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
|
||||
namespace base
|
||||
{
|
||||
namespace internal
|
||||
{
|
||||
|
||||
// Everything from here up to the floating point operations is portable C++,
|
||||
// but it may not be fast. This code could be split based on
|
||||
// platform/architecture and replaced with potentially faster implementations.
|
||||
|
||||
// Integer promotion templates used by the portable checked integer arithmetic.
|
||||
template <size_t Size, bool IsSigned>
|
||||
struct IntegerForSizeAndSign;
|
||||
template <>
|
||||
struct IntegerForSizeAndSign<1, true>
|
||||
{
|
||||
typedef int8_t type;
|
||||
};
|
||||
template <>
|
||||
struct IntegerForSizeAndSign<1, false>
|
||||
{
|
||||
typedef uint8_t type;
|
||||
};
|
||||
template <>
|
||||
struct IntegerForSizeAndSign<2, true>
|
||||
{
|
||||
typedef int16_t type;
|
||||
};
|
||||
template <>
|
||||
struct IntegerForSizeAndSign<2, false>
|
||||
{
|
||||
typedef uint16_t type;
|
||||
};
|
||||
template <>
|
||||
struct IntegerForSizeAndSign<4, true>
|
||||
{
|
||||
typedef int32_t type;
|
||||
};
|
||||
template <>
|
||||
struct IntegerForSizeAndSign<4, false>
|
||||
{
|
||||
typedef uint32_t type;
|
||||
};
|
||||
template <>
|
||||
struct IntegerForSizeAndSign<8, true>
|
||||
{
|
||||
typedef int64_t type;
|
||||
};
|
||||
template <>
|
||||
struct IntegerForSizeAndSign<8, false>
|
||||
{
|
||||
typedef uint64_t type;
|
||||
};
|
||||
|
||||
// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
|
||||
// support 128-bit math, then the ArithmeticPromotion template below will need
|
||||
// to be updated (or more likely replaced with a decltype expression).
|
||||
|
||||
template <typename Integer>
|
||||
struct UnsignedIntegerForSize
|
||||
{
|
||||
typedef
|
||||
typename std::enable_if<std::numeric_limits<Integer>::is_integer,
|
||||
typename IntegerForSizeAndSign<sizeof(Integer), false>::type>::type
|
||||
type;
|
||||
};
|
||||
|
||||
template <typename Integer>
|
||||
struct SignedIntegerForSize
|
||||
{
|
||||
typedef
|
||||
typename std::enable_if<std::numeric_limits<Integer>::is_integer,
|
||||
typename IntegerForSizeAndSign<sizeof(Integer), true>::type>::type
|
||||
type;
|
||||
};
|
||||
|
||||
template <typename Integer>
|
||||
struct TwiceWiderInteger
|
||||
{
|
||||
typedef typename std::enable_if<
|
||||
std::numeric_limits<Integer>::is_integer,
|
||||
typename IntegerForSizeAndSign<sizeof(Integer) * 2,
|
||||
std::numeric_limits<Integer>::is_signed>::type>::type type;
|
||||
};
|
||||
|
||||
template <typename Integer>
|
||||
struct PositionOfSignBit
|
||||
{
|
||||
static const typename std::enable_if<std::numeric_limits<Integer>::is_integer, size_t>::type
|
||||
value = CHAR_BIT * sizeof(Integer) - 1;
|
||||
};
|
||||
|
||||
// This is used for UnsignedAbs, where we need to support floating-point
|
||||
// template instantiations even though we don't actually support the operations.
|
||||
// However, there is no corresponding implementation of e.g. CheckedUnsignedAbs,
|
||||
// so the float versions will not compile.
|
||||
template <typename Numeric,
|
||||
bool IsInteger = std::numeric_limits<Numeric>::is_integer,
|
||||
bool IsFloat = std::numeric_limits<Numeric>::is_iec559>
|
||||
struct UnsignedOrFloatForSize;
|
||||
|
||||
template <typename Numeric>
|
||||
struct UnsignedOrFloatForSize<Numeric, true, false>
|
||||
{
|
||||
typedef typename UnsignedIntegerForSize<Numeric>::type type;
|
||||
};
|
||||
|
||||
template <typename Numeric>
|
||||
struct UnsignedOrFloatForSize<Numeric, false, true>
|
||||
{
|
||||
typedef Numeric type;
|
||||
};
|
||||
|
||||
// Helper templates for integer manipulations.
|
||||
|
||||
template <typename T>
|
||||
constexpr bool HasSignBit(T x)
|
||||
{
|
||||
// Cast to unsigned since right shift on signed is undefined.
|
||||
return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >>
|
||||
PositionOfSignBit<T>::value);
|
||||
}
|
||||
|
||||
// This wrapper undoes the standard integer promotions.
|
||||
template <typename T>
|
||||
constexpr T BinaryComplement(T x)
|
||||
{
|
||||
return static_cast<T>(~x);
|
||||
}
|
||||
|
||||
// Here are the actual portable checked integer math implementations.
|
||||
// TODO(jschuh): Break this code out from the enable_if pattern and find a clean
|
||||
// way to coalesce things into the CheckedNumericState specializations below.
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type
|
||||
CheckedAdd(T x, T y, RangeConstraint *validity)
|
||||
{
|
||||
// Since the value of x+y is undefined if we have a signed type, we compute
|
||||
// it using the unsigned type of the same size.
|
||||
typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
|
||||
UnsignedDst ux = static_cast<UnsignedDst>(x);
|
||||
UnsignedDst uy = static_cast<UnsignedDst>(y);
|
||||
UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
|
||||
// Addition is valid if the sign of (x + y) is equal to either that of x or
|
||||
// that of y.
|
||||
if (std::numeric_limits<T>::is_signed)
|
||||
{
|
||||
if (HasSignBit(BinaryComplement(static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))))
|
||||
{
|
||||
*validity = RANGE_VALID;
|
||||
}
|
||||
else
|
||||
{ // Direction of wrap is inverse of result sign.
|
||||
*validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Unsigned is either valid or overflow.
|
||||
*validity = BinaryComplement(x) >= y ? RANGE_VALID : RANGE_OVERFLOW;
|
||||
}
|
||||
return static_cast<T>(uresult);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type
|
||||
CheckedSub(T x, T y, RangeConstraint *validity)
|
||||
{
|
||||
// Since the value of x+y is undefined if we have a signed type, we compute
|
||||
// it using the unsigned type of the same size.
|
||||
typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
|
||||
UnsignedDst ux = static_cast<UnsignedDst>(x);
|
||||
UnsignedDst uy = static_cast<UnsignedDst>(y);
|
||||
UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
|
||||
// Subtraction is valid if either x and y have same sign, or (x-y) and x have
|
||||
// the same sign.
|
||||
if (std::numeric_limits<T>::is_signed)
|
||||
{
|
||||
if (HasSignBit(BinaryComplement(static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))))
|
||||
{
|
||||
*validity = RANGE_VALID;
|
||||
}
|
||||
else
|
||||
{ // Direction of wrap is inverse of result sign.
|
||||
*validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Unsigned is either valid or underflow.
|
||||
*validity = x >= y ? RANGE_VALID : RANGE_UNDERFLOW;
|
||||
}
|
||||
return static_cast<T>(uresult);
|
||||
}
|
||||
|
||||
// Integer multiplication is a bit complicated. In the fast case we just
|
||||
// we just promote to a twice wider type, and range check the result. In the
|
||||
// slow case we need to manually check that the result won't be truncated by
|
||||
// checking with division against the appropriate bound.
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_integer && sizeof(T) * 2 <= sizeof(uintmax_t),
|
||||
T>::type
|
||||
CheckedMul(T x, T y, RangeConstraint *validity)
|
||||
{
|
||||
typedef typename TwiceWiderInteger<T>::type IntermediateType;
|
||||
IntermediateType tmp = static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y);
|
||||
*validity = DstRangeRelationToSrcRange<T>(tmp);
|
||||
return static_cast<T>(tmp);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed &&
|
||||
(sizeof(T) * 2 > sizeof(uintmax_t)),
|
||||
T>::type
|
||||
CheckedMul(T x, T y, RangeConstraint *validity)
|
||||
{
|
||||
// If either side is zero then the result will be zero.
|
||||
if (!x || !y)
|
||||
{
|
||||
*validity = RANGE_VALID;
|
||||
return static_cast<T>(0);
|
||||
}
|
||||
else if (x > 0)
|
||||
{
|
||||
if (y > 0)
|
||||
*validity = x <= std::numeric_limits<T>::max() / y ? RANGE_VALID : RANGE_OVERFLOW;
|
||||
else
|
||||
*validity = y >= std::numeric_limits<T>::min() / x ? RANGE_VALID : RANGE_UNDERFLOW;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (y > 0)
|
||||
*validity = x >= std::numeric_limits<T>::min() / y ? RANGE_VALID : RANGE_UNDERFLOW;
|
||||
else
|
||||
*validity = y >= std::numeric_limits<T>::max() / x ? RANGE_VALID : RANGE_OVERFLOW;
|
||||
}
|
||||
|
||||
return static_cast<T>(x * y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed &&
|
||||
(sizeof(T) * 2 > sizeof(uintmax_t)),
|
||||
T>::type
|
||||
CheckedMul(T x, T y, RangeConstraint *validity)
|
||||
{
|
||||
*validity = (y == 0 || x <= std::numeric_limits<T>::max() / y) ? RANGE_VALID : RANGE_OVERFLOW;
|
||||
return static_cast<T>(x * y);
|
||||
}
|
||||
|
||||
// Division just requires a check for an invalid negation on signed min/-1.
|
||||
template <typename T>
|
||||
T CheckedDiv(T x,
|
||||
T y,
|
||||
RangeConstraint *validity,
|
||||
typename std::enable_if<std::numeric_limits<T>::is_integer, int>::type = 0)
|
||||
{
|
||||
if (std::numeric_limits<T>::is_signed && x == std::numeric_limits<T>::min() &&
|
||||
y == static_cast<T>(-1))
|
||||
{
|
||||
*validity = RANGE_OVERFLOW;
|
||||
return std::numeric_limits<T>::min();
|
||||
}
|
||||
|
||||
*validity = RANGE_VALID;
|
||||
return static_cast<T>(x / y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed,
|
||||
T>::type
|
||||
CheckedMod(T x, T y, RangeConstraint *validity)
|
||||
{
|
||||
*validity = y > 0 ? RANGE_VALID : RANGE_INVALID;
|
||||
return static_cast<T>(x % y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
|
||||
T>::type
|
||||
CheckedMod(T x, T y, RangeConstraint *validity)
|
||||
{
|
||||
*validity = RANGE_VALID;
|
||||
return static_cast<T>(x % y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed,
|
||||
T>::type
|
||||
CheckedNeg(T value, RangeConstraint *validity)
|
||||
{
|
||||
*validity = value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
|
||||
// The negation of signed min is min, so catch that one.
|
||||
return static_cast<T>(-value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
|
||||
T>::type
|
||||
CheckedNeg(T value, RangeConstraint *validity)
|
||||
{
|
||||
// The only legal unsigned negation is zero.
|
||||
*validity = value ? RANGE_UNDERFLOW : RANGE_VALID;
|
||||
return static_cast<T>(-static_cast<typename SignedIntegerForSize<T>::type>(value));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed,
|
||||
T>::type
|
||||
CheckedAbs(T value, RangeConstraint *validity)
|
||||
{
|
||||
*validity = value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
|
||||
return static_cast<T>(std::abs(value));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
|
||||
T>::type
|
||||
CheckedAbs(T value, RangeConstraint *validity)
|
||||
{
|
||||
// T is unsigned, so |value| must already be positive.
|
||||
*validity = RANGE_VALID;
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed,
|
||||
typename UnsignedIntegerForSize<T>::type>::type
|
||||
CheckedUnsignedAbs(T value)
|
||||
{
|
||||
typedef typename UnsignedIntegerForSize<T>::type UnsignedT;
|
||||
return value == std::numeric_limits<T>::min()
|
||||
? static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1
|
||||
: static_cast<UnsignedT>(std::abs(value));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
|
||||
T>::type
|
||||
CheckedUnsignedAbs(T value)
|
||||
{
|
||||
// T is unsigned, so |value| must already be positive.
|
||||
return static_cast<T>(value);
|
||||
}
|
||||
|
||||
// These are the floating point stubs that the compiler needs to see. Only the
|
||||
// negation operation is ever called.
|
||||
#define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
|
||||
template <typename T> \
|
||||
typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type Checked##NAME( \
|
||||
T, T, RangeConstraint *) \
|
||||
{ \
|
||||
NOTREACHED(); \
|
||||
return static_cast<T>(0); \
|
||||
}
|
||||
|
||||
BASE_FLOAT_ARITHMETIC_STUBS(Add)
|
||||
BASE_FLOAT_ARITHMETIC_STUBS(Sub)
|
||||
BASE_FLOAT_ARITHMETIC_STUBS(Mul)
|
||||
BASE_FLOAT_ARITHMETIC_STUBS(Div)
|
||||
BASE_FLOAT_ARITHMETIC_STUBS(Mod)
|
||||
|
||||
#undef BASE_FLOAT_ARITHMETIC_STUBS
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg(T value,
|
||||
RangeConstraint *)
|
||||
{
|
||||
return static_cast<T>(-value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs(T value,
|
||||
RangeConstraint *)
|
||||
{
|
||||
return static_cast<T>(std::abs(value));
|
||||
}
|
||||
|
||||
// Floats carry around their validity state with them, but integers do not. So,
|
||||
// we wrap the underlying value in a specialization in order to hide that detail
|
||||
// and expose an interface via accessors.
|
||||
enum NumericRepresentation
|
||||
{
|
||||
NUMERIC_INTEGER,
|
||||
NUMERIC_FLOATING,
|
||||
NUMERIC_UNKNOWN
|
||||
};
|
||||
|
||||
template <typename NumericType>
|
||||
struct GetNumericRepresentation
|
||||
{
|
||||
static const NumericRepresentation value =
|
||||
std::numeric_limits<NumericType>::is_integer
|
||||
? NUMERIC_INTEGER
|
||||
: (std::numeric_limits<NumericType>::is_iec559 ? NUMERIC_FLOATING : NUMERIC_UNKNOWN);
|
||||
};
|
||||
|
||||
template <typename T, NumericRepresentation type = GetNumericRepresentation<T>::value>
|
||||
class CheckedNumericState
|
||||
{
|
||||
};
|
||||
|
||||
// Integrals require quite a bit of additional housekeeping to manage state.
|
||||
template <typename T>
|
||||
class CheckedNumericState<T, NUMERIC_INTEGER>
|
||||
{
|
||||
private:
|
||||
T value_;
|
||||
RangeConstraint validity_ : CHAR_BIT; // Actually requires only two bits.
|
||||
|
||||
public:
|
||||
template <typename Src, NumericRepresentation type>
|
||||
friend class CheckedNumericState;
|
||||
|
||||
CheckedNumericState() : value_(0), validity_(RANGE_VALID) {}
|
||||
|
||||
template <typename Src>
|
||||
CheckedNumericState(Src value, RangeConstraint validity)
|
||||
: value_(static_cast<T>(value)),
|
||||
validity_(GetRangeConstraint(validity | DstRangeRelationToSrcRange<T>(value)))
|
||||
{
|
||||
static_assert(std::numeric_limits<Src>::is_specialized, "Argument must be numeric.");
|
||||
}
|
||||
|
||||
// Copy constructor.
|
||||
template <typename Src>
|
||||
CheckedNumericState(const CheckedNumericState<Src> &rhs)
|
||||
: value_(static_cast<T>(rhs.value())),
|
||||
validity_(GetRangeConstraint(rhs.validity() | DstRangeRelationToSrcRange<T>(rhs.value())))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Src>
|
||||
explicit CheckedNumericState(
|
||||
Src value,
|
||||
typename std::enable_if<std::numeric_limits<Src>::is_specialized, int>::type = 0)
|
||||
: value_(static_cast<T>(value)), validity_(DstRangeRelationToSrcRange<T>(value))
|
||||
{
|
||||
}
|
||||
|
||||
RangeConstraint validity() const { return validity_; }
|
||||
T value() const { return value_; }
|
||||
};
|
||||
|
||||
// Floating points maintain their own validity, but need translation wrappers.
|
||||
template <typename T>
|
||||
class CheckedNumericState<T, NUMERIC_FLOATING>
|
||||
{
|
||||
private:
|
||||
T value_;
|
||||
|
||||
public:
|
||||
template <typename Src, NumericRepresentation type>
|
||||
friend class CheckedNumericState;
|
||||
|
||||
CheckedNumericState() : value_(0.0) {}
|
||||
|
||||
template <typename Src>
|
||||
CheckedNumericState(
|
||||
Src value,
|
||||
RangeConstraint validity,
|
||||
typename std::enable_if<std::numeric_limits<Src>::is_integer, int>::type = 0)
|
||||
{
|
||||
switch (DstRangeRelationToSrcRange<T>(value))
|
||||
{
|
||||
case RANGE_VALID:
|
||||
value_ = static_cast<T>(value);
|
||||
break;
|
||||
|
||||
case RANGE_UNDERFLOW:
|
||||
value_ = -std::numeric_limits<T>::infinity();
|
||||
break;
|
||||
|
||||
case RANGE_OVERFLOW:
|
||||
value_ = std::numeric_limits<T>::infinity();
|
||||
break;
|
||||
|
||||
case RANGE_INVALID:
|
||||
value_ = std::numeric_limits<T>::quiet_NaN();
|
||||
break;
|
||||
|
||||
default:
|
||||
NOTREACHED();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Src>
|
||||
explicit CheckedNumericState(
|
||||
Src value,
|
||||
typename std::enable_if<std::numeric_limits<Src>::is_specialized, int>::type = 0)
|
||||
: value_(static_cast<T>(value))
|
||||
{
|
||||
}
|
||||
|
||||
// Copy constructor.
|
||||
template <typename Src>
|
||||
CheckedNumericState(const CheckedNumericState<Src> &rhs) : value_(static_cast<T>(rhs.value()))
|
||||
{
|
||||
}
|
||||
|
||||
RangeConstraint validity() const
|
||||
{
|
||||
return GetRangeConstraint(value_ <= std::numeric_limits<T>::max(),
|
||||
value_ >= -std::numeric_limits<T>::max());
|
||||
}
|
||||
T value() const { return value_; }
|
||||
};
|
||||
|
||||
// For integers less than 128-bit and floats 32-bit or larger, we have the type
|
||||
// with the larger maximum exponent take precedence.
|
||||
enum ArithmeticPromotionCategory
|
||||
{
|
||||
LEFT_PROMOTION,
|
||||
RIGHT_PROMOTION
|
||||
};
|
||||
|
||||
template <typename Lhs,
|
||||
typename Rhs = Lhs,
|
||||
ArithmeticPromotionCategory Promotion =
|
||||
(MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) ? LEFT_PROMOTION
|
||||
: RIGHT_PROMOTION>
|
||||
struct ArithmeticPromotion;
|
||||
|
||||
template <typename Lhs, typename Rhs>
|
||||
struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION>
|
||||
{
|
||||
typedef Lhs type;
|
||||
};
|
||||
|
||||
template <typename Lhs, typename Rhs>
|
||||
struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION>
|
||||
{
|
||||
typedef Rhs type;
|
||||
};
|
||||
|
||||
// We can statically check if operations on the provided types can wrap, so we
|
||||
// can skip the checked operations if they're not needed. So, for an integer we
|
||||
// care if the destination type preserves the sign and is twice the width of
|
||||
// the source.
|
||||
template <typename T, typename Lhs, typename Rhs>
|
||||
struct IsIntegerArithmeticSafe
|
||||
{
|
||||
static const bool value =
|
||||
!std::numeric_limits<T>::is_iec559 &&
|
||||
StaticDstRangeRelationToSrcRange<T, Lhs>::value == NUMERIC_RANGE_CONTAINED &&
|
||||
sizeof(T) >= (2 * sizeof(Lhs)) &&
|
||||
StaticDstRangeRelationToSrcRange<T, Rhs>::value != NUMERIC_RANGE_CONTAINED &&
|
||||
sizeof(T) >= (2 * sizeof(Rhs));
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_
|
||||
771
gfx/angle/src/common/third_party/numerics/base/numerics/safe_numerics_unittest.cc
vendored
Normal file
771
gfx/angle/src/common/third_party/numerics/base/numerics/safe_numerics_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,771 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/numerics/safe_math.h"
|
||||
#include "build/build_config.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS)
|
||||
#include <mmintrin.h>
|
||||
#endif
|
||||
|
||||
using std::numeric_limits;
|
||||
using base::CheckedNumeric;
|
||||
using base::checked_cast;
|
||||
using base::IsValueInRangeForNumericType;
|
||||
using base::IsValueNegative;
|
||||
using base::SizeT;
|
||||
using base::StrictNumeric;
|
||||
using base::saturated_cast;
|
||||
using base::strict_cast;
|
||||
using base::internal::MaxExponent;
|
||||
using base::internal::RANGE_VALID;
|
||||
using base::internal::RANGE_INVALID;
|
||||
using base::internal::RANGE_OVERFLOW;
|
||||
using base::internal::RANGE_UNDERFLOW;
|
||||
using base::internal::SignedIntegerForSize;
|
||||
|
||||
// These tests deliberately cause arithmetic overflows. If the compiler is
|
||||
// aggressive enough, it can const fold these overflows. Disable warnings about
|
||||
// overflows for const expressions.
|
||||
#if defined(OS_WIN)
|
||||
#pragma warning(disable : 4756)
|
||||
#endif
|
||||
|
||||
// This is a helper function for finding the maximum value in Src that can be
|
||||
// wholy represented as the destination floating-point type.
|
||||
template <typename Dst, typename Src>
|
||||
Dst GetMaxConvertibleToFloat()
|
||||
{
|
||||
typedef numeric_limits<Dst> DstLimits;
|
||||
typedef numeric_limits<Src> SrcLimits;
|
||||
static_assert(SrcLimits::is_specialized, "Source must be numeric.");
|
||||
static_assert(DstLimits::is_specialized, "Destination must be numeric.");
|
||||
CHECK(DstLimits::is_iec559);
|
||||
|
||||
if (SrcLimits::digits <= DstLimits::digits &&
|
||||
MaxExponent<Src>::value <= MaxExponent<Dst>::value)
|
||||
return SrcLimits::max();
|
||||
Src max = SrcLimits::max() / 2 + (SrcLimits::is_integer ? 1 : 0);
|
||||
while (max != static_cast<Src>(static_cast<Dst>(max)))
|
||||
{
|
||||
max /= 2;
|
||||
}
|
||||
return static_cast<Dst>(max);
|
||||
}
|
||||
|
||||
// Helper macros to wrap displaying the conversion types and line numbers.
|
||||
#define TEST_EXPECTED_VALIDITY(expected, actual) \
|
||||
EXPECT_EQ(expected, CheckedNumeric<Dst>(actual).IsValid()) \
|
||||
<< "Result test: Value " << +(actual).ValueUnsafe() << " as " << dst << " on line " \
|
||||
<< line;
|
||||
|
||||
#define TEST_EXPECTED_SUCCESS(actual) TEST_EXPECTED_VALIDITY(true, actual)
|
||||
#define TEST_EXPECTED_FAILURE(actual) TEST_EXPECTED_VALIDITY(false, actual)
|
||||
|
||||
#define TEST_EXPECTED_VALUE(expected, actual) \
|
||||
EXPECT_EQ(static_cast<Dst>(expected), CheckedNumeric<Dst>(actual).ValueUnsafe()) \
|
||||
<< "Result test: Value " << +((actual).ValueUnsafe()) << " as " << dst << " on line " \
|
||||
<< line;
|
||||
|
||||
// Signed integer arithmetic.
|
||||
template <typename Dst>
|
||||
static void TestSpecializedArithmetic(
|
||||
const char *dst,
|
||||
int line,
|
||||
typename std::enable_if<numeric_limits<Dst>::is_integer && numeric_limits<Dst>::is_signed,
|
||||
int>::type = 0)
|
||||
{
|
||||
typedef numeric_limits<Dst> DstLimits;
|
||||
TEST_EXPECTED_FAILURE(-CheckedNumeric<Dst>(DstLimits::min()));
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()).Abs());
|
||||
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs());
|
||||
|
||||
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) + -1);
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) + -1);
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) + -DstLimits::max());
|
||||
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) - 1);
|
||||
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) - -1);
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) - -DstLimits::max());
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) - DstLimits::max());
|
||||
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) * 2);
|
||||
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) / -1);
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(-1) / 2);
|
||||
|
||||
// Modulus is legal only for integers.
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1);
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
|
||||
TEST_EXPECTED_VALUE(-1, CheckedNumeric<Dst>(-1) % 2);
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-1) % -2);
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) % 2);
|
||||
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2);
|
||||
// Test all the different modulus combinations.
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % CheckedNumeric<Dst>(1));
|
||||
TEST_EXPECTED_VALUE(0, 1 % CheckedNumeric<Dst>(1));
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
|
||||
CheckedNumeric<Dst> checked_dst = 1;
|
||||
TEST_EXPECTED_VALUE(0, checked_dst %= 1);
|
||||
}
|
||||
|
||||
// Unsigned integer arithmetic.
|
||||
template <typename Dst>
|
||||
static void TestSpecializedArithmetic(
|
||||
const char *dst,
|
||||
int line,
|
||||
typename std::enable_if<numeric_limits<Dst>::is_integer && !numeric_limits<Dst>::is_signed,
|
||||
int>::type = 0)
|
||||
{
|
||||
typedef numeric_limits<Dst> DstLimits;
|
||||
TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::min()));
|
||||
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()).Abs());
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) + -1);
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::min()) - 1);
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) * 2);
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) / 2);
|
||||
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()).UnsignedAbs());
|
||||
TEST_EXPECTED_SUCCESS(CheckedNumeric<typename SignedIntegerForSize<Dst>::type>(
|
||||
std::numeric_limits<typename SignedIntegerForSize<Dst>::type>::min())
|
||||
.UnsignedAbs());
|
||||
|
||||
// Modulus is legal only for integers.
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1);
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
|
||||
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) % 2);
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) % 2);
|
||||
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2);
|
||||
// Test all the different modulus combinations.
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % CheckedNumeric<Dst>(1));
|
||||
TEST_EXPECTED_VALUE(0, 1 % CheckedNumeric<Dst>(1));
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
|
||||
CheckedNumeric<Dst> checked_dst = 1;
|
||||
TEST_EXPECTED_VALUE(0, checked_dst %= 1);
|
||||
}
|
||||
|
||||
// Floating point arithmetic.
|
||||
template <typename Dst>
|
||||
void TestSpecializedArithmetic(
|
||||
const char *dst,
|
||||
int line,
|
||||
typename std::enable_if<numeric_limits<Dst>::is_iec559, int>::type = 0)
|
||||
{
|
||||
typedef numeric_limits<Dst> DstLimits;
|
||||
TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::min()));
|
||||
|
||||
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()).Abs());
|
||||
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs());
|
||||
|
||||
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) + -1);
|
||||
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) + 1);
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) + -DstLimits::max());
|
||||
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) - -DstLimits::max());
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-DstLimits::max()) - DstLimits::max());
|
||||
|
||||
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) * 2);
|
||||
|
||||
TEST_EXPECTED_VALUE(-0.5, CheckedNumeric<Dst>(-1.0) / 2);
|
||||
EXPECT_EQ(static_cast<Dst>(1.0), CheckedNumeric<Dst>(1.0).ValueFloating());
|
||||
}
|
||||
|
||||
// Generic arithmetic tests.
|
||||
template <typename Dst>
|
||||
static void TestArithmetic(const char *dst, int line)
|
||||
{
|
||||
typedef numeric_limits<Dst> DstLimits;
|
||||
|
||||
EXPECT_EQ(true, CheckedNumeric<Dst>().IsValid());
|
||||
EXPECT_EQ(
|
||||
false,
|
||||
CheckedNumeric<Dst>(CheckedNumeric<Dst>(DstLimits::max()) * DstLimits::max()).IsValid());
|
||||
EXPECT_EQ(static_cast<Dst>(0), CheckedNumeric<Dst>().ValueOrDie());
|
||||
EXPECT_EQ(static_cast<Dst>(0), CheckedNumeric<Dst>().ValueOrDefault(1));
|
||||
EXPECT_EQ(static_cast<Dst>(1),
|
||||
CheckedNumeric<Dst>(CheckedNumeric<Dst>(DstLimits::max()) * DstLimits::max())
|
||||
.ValueOrDefault(1));
|
||||
|
||||
// Test the operator combinations.
|
||||
TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) + CheckedNumeric<Dst>(1));
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) - CheckedNumeric<Dst>(1));
|
||||
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) * CheckedNumeric<Dst>(1));
|
||||
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / CheckedNumeric<Dst>(1));
|
||||
TEST_EXPECTED_VALUE(2, 1 + CheckedNumeric<Dst>(1));
|
||||
TEST_EXPECTED_VALUE(0, 1 - CheckedNumeric<Dst>(1));
|
||||
TEST_EXPECTED_VALUE(1, 1 * CheckedNumeric<Dst>(1));
|
||||
TEST_EXPECTED_VALUE(1, 1 / CheckedNumeric<Dst>(1));
|
||||
TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) + 1);
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) - 1);
|
||||
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) * 1);
|
||||
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / 1);
|
||||
CheckedNumeric<Dst> checked_dst = 1;
|
||||
TEST_EXPECTED_VALUE(2, checked_dst += 1);
|
||||
checked_dst = 1;
|
||||
TEST_EXPECTED_VALUE(0, checked_dst -= 1);
|
||||
checked_dst = 1;
|
||||
TEST_EXPECTED_VALUE(1, checked_dst *= 1);
|
||||
checked_dst = 1;
|
||||
TEST_EXPECTED_VALUE(1, checked_dst /= 1);
|
||||
|
||||
// Generic negation.
|
||||
TEST_EXPECTED_VALUE(0, -CheckedNumeric<Dst>());
|
||||
TEST_EXPECTED_VALUE(-1, -CheckedNumeric<Dst>(1));
|
||||
TEST_EXPECTED_VALUE(1, -CheckedNumeric<Dst>(-1));
|
||||
TEST_EXPECTED_VALUE(static_cast<Dst>(DstLimits::max() * -1),
|
||||
-CheckedNumeric<Dst>(DstLimits::max()));
|
||||
|
||||
// Generic absolute value.
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>().Abs());
|
||||
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1).Abs());
|
||||
TEST_EXPECTED_VALUE(DstLimits::max(), CheckedNumeric<Dst>(DstLimits::max()).Abs());
|
||||
|
||||
// Generic addition.
|
||||
TEST_EXPECTED_VALUE(1, (CheckedNumeric<Dst>() + 1));
|
||||
TEST_EXPECTED_VALUE(2, (CheckedNumeric<Dst>(1) + 1));
|
||||
TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(-1) + 1));
|
||||
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::min()) + 1);
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) + DstLimits::max());
|
||||
|
||||
// Generic subtraction.
|
||||
TEST_EXPECTED_VALUE(-1, (CheckedNumeric<Dst>() - 1));
|
||||
TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(1) - 1));
|
||||
TEST_EXPECTED_VALUE(-2, (CheckedNumeric<Dst>(-1) - 1));
|
||||
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) - 1);
|
||||
|
||||
// Generic multiplication.
|
||||
TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>() * 1));
|
||||
TEST_EXPECTED_VALUE(1, (CheckedNumeric<Dst>(1) * 1));
|
||||
TEST_EXPECTED_VALUE(-2, (CheckedNumeric<Dst>(-1) * 2));
|
||||
TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(0) * 0));
|
||||
TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(-1) * 0));
|
||||
TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(0) * -1));
|
||||
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) * DstLimits::max());
|
||||
|
||||
// Generic division.
|
||||
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() / 1);
|
||||
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / 1);
|
||||
TEST_EXPECTED_VALUE(DstLimits::min() / 2, CheckedNumeric<Dst>(DstLimits::min()) / 2);
|
||||
TEST_EXPECTED_VALUE(DstLimits::max() / 2, CheckedNumeric<Dst>(DstLimits::max()) / 2);
|
||||
|
||||
TestSpecializedArithmetic<Dst>(dst, line);
|
||||
}
|
||||
|
||||
// Helper macro to wrap displaying the conversion types and line numbers.
|
||||
#define TEST_ARITHMETIC(Dst) TestArithmetic<Dst>(#Dst, __LINE__)
|
||||
|
||||
TEST(SafeNumerics, SignedIntegerMath)
|
||||
{
|
||||
TEST_ARITHMETIC(int8_t);
|
||||
TEST_ARITHMETIC(int);
|
||||
TEST_ARITHMETIC(intptr_t);
|
||||
TEST_ARITHMETIC(intmax_t);
|
||||
}
|
||||
|
||||
TEST(SafeNumerics, UnsignedIntegerMath)
|
||||
{
|
||||
TEST_ARITHMETIC(uint8_t);
|
||||
TEST_ARITHMETIC(unsigned int);
|
||||
TEST_ARITHMETIC(uintptr_t);
|
||||
TEST_ARITHMETIC(uintmax_t);
|
||||
}
|
||||
|
||||
TEST(SafeNumerics, FloatingPointMath)
|
||||
{
|
||||
TEST_ARITHMETIC(float);
|
||||
TEST_ARITHMETIC(double);
|
||||
}
|
||||
|
||||
// Enumerates the five different conversions types we need to test.
|
||||
enum NumericConversionType
|
||||
{
|
||||
SIGN_PRESERVING_VALUE_PRESERVING,
|
||||
SIGN_PRESERVING_NARROW,
|
||||
SIGN_TO_UNSIGN_WIDEN_OR_EQUAL,
|
||||
SIGN_TO_UNSIGN_NARROW,
|
||||
UNSIGN_TO_SIGN_NARROW_OR_EQUAL,
|
||||
};
|
||||
|
||||
// Template covering the different conversion tests.
|
||||
template <typename Dst, typename Src, NumericConversionType conversion>
|
||||
struct TestNumericConversion
|
||||
{
|
||||
};
|
||||
|
||||
// EXPECT_EQ wrappers providing specific detail on test failures.
|
||||
#define TEST_EXPECTED_RANGE(expected, actual) \
|
||||
EXPECT_EQ(expected, base::internal::DstRangeRelationToSrcRange<Dst>(actual)) \
|
||||
<< "Conversion test: " << src << " value " << actual << " to " << dst << " on line " \
|
||||
<< line;
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_VALUE_PRESERVING>
|
||||
{
|
||||
static void Test(const char *dst, const char *src, int line)
|
||||
{
|
||||
typedef numeric_limits<Src> SrcLimits;
|
||||
typedef numeric_limits<Dst> DstLimits;
|
||||
// Integral to floating.
|
||||
static_assert(
|
||||
(DstLimits::is_iec559 && SrcLimits::is_integer) ||
|
||||
// Not floating to integral and...
|
||||
(!(DstLimits::is_integer && SrcLimits::is_iec559) &&
|
||||
// Same sign, same numeric, source is narrower or same.
|
||||
((SrcLimits::is_signed == DstLimits::is_signed && sizeof(Dst) >= sizeof(Src)) ||
|
||||
// Or signed destination and source is smaller
|
||||
(DstLimits::is_signed && sizeof(Dst) > sizeof(Src)))),
|
||||
"Comparison must be sign preserving and value preserving");
|
||||
|
||||
const CheckedNumeric<Dst> checked_dst = SrcLimits::max();
|
||||
TEST_EXPECTED_SUCCESS(checked_dst);
|
||||
if (MaxExponent<Dst>::value > MaxExponent<Src>::value)
|
||||
{
|
||||
if (MaxExponent<Dst>::value >= MaxExponent<Src>::value * 2 - 1)
|
||||
{
|
||||
// At least twice larger type.
|
||||
TEST_EXPECTED_SUCCESS(SrcLimits::max() * checked_dst);
|
||||
}
|
||||
else
|
||||
{ // Larger, but not at least twice as large.
|
||||
TEST_EXPECTED_FAILURE(SrcLimits::max() * checked_dst);
|
||||
TEST_EXPECTED_SUCCESS(checked_dst + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Same width type.
|
||||
TEST_EXPECTED_FAILURE(checked_dst + 1);
|
||||
}
|
||||
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max());
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
|
||||
if (SrcLimits::is_iec559)
|
||||
{
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max() * static_cast<Src>(-1));
|
||||
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity());
|
||||
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1);
|
||||
TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN());
|
||||
}
|
||||
else if (numeric_limits<Src>::is_signed)
|
||||
{
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1));
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_NARROW>
|
||||
{
|
||||
static void Test(const char *dst, const char *src, int line)
|
||||
{
|
||||
typedef numeric_limits<Src> SrcLimits;
|
||||
typedef numeric_limits<Dst> DstLimits;
|
||||
static_assert(SrcLimits::is_signed == DstLimits::is_signed,
|
||||
"Destination and source sign must be the same");
|
||||
static_assert(sizeof(Dst) < sizeof(Src) || (DstLimits::is_integer && SrcLimits::is_iec559),
|
||||
"Destination must be narrower than source");
|
||||
|
||||
const CheckedNumeric<Dst> checked_dst;
|
||||
TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max());
|
||||
TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1));
|
||||
TEST_EXPECTED_FAILURE(checked_dst - SrcLimits::max());
|
||||
|
||||
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
|
||||
if (SrcLimits::is_iec559)
|
||||
{
|
||||
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::max() * -1);
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1));
|
||||
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity());
|
||||
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1);
|
||||
TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN());
|
||||
if (DstLimits::is_integer)
|
||||
{
|
||||
if (SrcLimits::digits < DstLimits::digits)
|
||||
{
|
||||
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, static_cast<Src>(DstLimits::max()));
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::max()));
|
||||
}
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID,
|
||||
static_cast<Src>(GetMaxConvertibleToFloat<Src, Dst>()));
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::min()));
|
||||
}
|
||||
}
|
||||
else if (SrcLimits::is_signed)
|
||||
{
|
||||
TEST_EXPECTED_VALUE(-1, checked_dst - static_cast<Src>(1));
|
||||
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min());
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1));
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_EXPECTED_FAILURE(checked_dst - static_cast<Src>(1));
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL>
|
||||
{
|
||||
static void Test(const char *dst, const char *src, int line)
|
||||
{
|
||||
typedef numeric_limits<Src> SrcLimits;
|
||||
typedef numeric_limits<Dst> DstLimits;
|
||||
static_assert(sizeof(Dst) >= sizeof(Src),
|
||||
"Destination must be equal or wider than source.");
|
||||
static_assert(SrcLimits::is_signed, "Source must be signed");
|
||||
static_assert(!DstLimits::is_signed, "Destination must be unsigned");
|
||||
|
||||
const CheckedNumeric<Dst> checked_dst;
|
||||
TEST_EXPECTED_VALUE(SrcLimits::max(), checked_dst + SrcLimits::max());
|
||||
TEST_EXPECTED_FAILURE(checked_dst + static_cast<Src>(-1));
|
||||
TEST_EXPECTED_FAILURE(checked_dst + -SrcLimits::max());
|
||||
|
||||
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min());
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max());
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
|
||||
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_NARROW>
|
||||
{
|
||||
static void Test(const char *dst, const char *src, int line)
|
||||
{
|
||||
typedef numeric_limits<Src> SrcLimits;
|
||||
typedef numeric_limits<Dst> DstLimits;
|
||||
static_assert(
|
||||
(DstLimits::is_integer && SrcLimits::is_iec559) || (sizeof(Dst) < sizeof(Src)),
|
||||
"Destination must be narrower than source.");
|
||||
static_assert(SrcLimits::is_signed, "Source must be signed.");
|
||||
static_assert(!DstLimits::is_signed, "Destination must be unsigned.");
|
||||
|
||||
const CheckedNumeric<Dst> checked_dst;
|
||||
TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1));
|
||||
TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max());
|
||||
TEST_EXPECTED_FAILURE(checked_dst + static_cast<Src>(-1));
|
||||
TEST_EXPECTED_FAILURE(checked_dst + -SrcLimits::max());
|
||||
|
||||
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
|
||||
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1));
|
||||
if (SrcLimits::is_iec559)
|
||||
{
|
||||
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::max() * -1);
|
||||
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity());
|
||||
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1);
|
||||
TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN());
|
||||
if (DstLimits::is_integer)
|
||||
{
|
||||
if (SrcLimits::digits < DstLimits::digits)
|
||||
{
|
||||
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, static_cast<Src>(DstLimits::max()));
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::max()));
|
||||
}
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID,
|
||||
static_cast<Src>(GetMaxConvertibleToFloat<Src, Dst>()));
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(DstLimits::min()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
struct TestNumericConversion<Dst, Src, UNSIGN_TO_SIGN_NARROW_OR_EQUAL>
|
||||
{
|
||||
static void Test(const char *dst, const char *src, int line)
|
||||
{
|
||||
typedef numeric_limits<Src> SrcLimits;
|
||||
typedef numeric_limits<Dst> DstLimits;
|
||||
static_assert(sizeof(Dst) <= sizeof(Src),
|
||||
"Destination must be narrower or equal to source.");
|
||||
static_assert(!SrcLimits::is_signed, "Source must be unsigned.");
|
||||
static_assert(DstLimits::is_signed, "Destination must be signed.");
|
||||
|
||||
const CheckedNumeric<Dst> checked_dst;
|
||||
TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1));
|
||||
TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max());
|
||||
TEST_EXPECTED_VALUE(SrcLimits::min(), checked_dst + SrcLimits::min());
|
||||
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min());
|
||||
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
|
||||
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
|
||||
}
|
||||
};
|
||||
|
||||
// Helper macro to wrap displaying the conversion types and line numbers
|
||||
#define TEST_NUMERIC_CONVERSION(d, s, t) TestNumericConversion<d, s, t>::Test(#d, #s, __LINE__)
|
||||
|
||||
TEST(SafeNumerics, IntMinOperations)
|
||||
{
|
||||
TEST_NUMERIC_CONVERSION(int8_t, int8_t, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(uint8_t, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
|
||||
TEST_NUMERIC_CONVERSION(int8_t, int, SIGN_PRESERVING_NARROW);
|
||||
TEST_NUMERIC_CONVERSION(uint8_t, unsigned int, SIGN_PRESERVING_NARROW);
|
||||
TEST_NUMERIC_CONVERSION(int8_t, float, SIGN_PRESERVING_NARROW);
|
||||
|
||||
TEST_NUMERIC_CONVERSION(uint8_t, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
|
||||
|
||||
TEST_NUMERIC_CONVERSION(uint8_t, int, SIGN_TO_UNSIGN_NARROW);
|
||||
TEST_NUMERIC_CONVERSION(uint8_t, intmax_t, SIGN_TO_UNSIGN_NARROW);
|
||||
TEST_NUMERIC_CONVERSION(uint8_t, float, SIGN_TO_UNSIGN_NARROW);
|
||||
|
||||
TEST_NUMERIC_CONVERSION(int8_t, unsigned int, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
|
||||
TEST_NUMERIC_CONVERSION(int8_t, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
|
||||
}
|
||||
|
||||
TEST(SafeNumerics, IntOperations)
|
||||
{
|
||||
TEST_NUMERIC_CONVERSION(int, int, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(unsigned int, unsigned int, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(int, int8_t, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(unsigned int, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(int, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
|
||||
TEST_NUMERIC_CONVERSION(int, intmax_t, SIGN_PRESERVING_NARROW);
|
||||
TEST_NUMERIC_CONVERSION(unsigned int, uintmax_t, SIGN_PRESERVING_NARROW);
|
||||
TEST_NUMERIC_CONVERSION(int, float, SIGN_PRESERVING_NARROW);
|
||||
TEST_NUMERIC_CONVERSION(int, double, SIGN_PRESERVING_NARROW);
|
||||
|
||||
TEST_NUMERIC_CONVERSION(unsigned int, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
|
||||
TEST_NUMERIC_CONVERSION(unsigned int, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
|
||||
|
||||
TEST_NUMERIC_CONVERSION(unsigned int, intmax_t, SIGN_TO_UNSIGN_NARROW);
|
||||
TEST_NUMERIC_CONVERSION(unsigned int, float, SIGN_TO_UNSIGN_NARROW);
|
||||
TEST_NUMERIC_CONVERSION(unsigned int, double, SIGN_TO_UNSIGN_NARROW);
|
||||
|
||||
TEST_NUMERIC_CONVERSION(int, unsigned int, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
|
||||
TEST_NUMERIC_CONVERSION(int, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
|
||||
}
|
||||
|
||||
TEST(SafeNumerics, IntMaxOperations)
|
||||
{
|
||||
TEST_NUMERIC_CONVERSION(intmax_t, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(uintmax_t, uintmax_t, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(intmax_t, int, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(uintmax_t, unsigned int, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(intmax_t, unsigned int, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(intmax_t, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
|
||||
TEST_NUMERIC_CONVERSION(intmax_t, float, SIGN_PRESERVING_NARROW);
|
||||
TEST_NUMERIC_CONVERSION(intmax_t, double, SIGN_PRESERVING_NARROW);
|
||||
|
||||
TEST_NUMERIC_CONVERSION(uintmax_t, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
|
||||
TEST_NUMERIC_CONVERSION(uintmax_t, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
|
||||
|
||||
TEST_NUMERIC_CONVERSION(uintmax_t, float, SIGN_TO_UNSIGN_NARROW);
|
||||
TEST_NUMERIC_CONVERSION(uintmax_t, double, SIGN_TO_UNSIGN_NARROW);
|
||||
|
||||
TEST_NUMERIC_CONVERSION(intmax_t, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
|
||||
}
|
||||
|
||||
TEST(SafeNumerics, FloatOperations)
|
||||
{
|
||||
TEST_NUMERIC_CONVERSION(float, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(float, uintmax_t, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(float, int, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(float, unsigned int, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
|
||||
TEST_NUMERIC_CONVERSION(float, double, SIGN_PRESERVING_NARROW);
|
||||
}
|
||||
|
||||
TEST(SafeNumerics, DoubleOperations)
|
||||
{
|
||||
TEST_NUMERIC_CONVERSION(double, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(double, uintmax_t, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(double, int, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
TEST_NUMERIC_CONVERSION(double, unsigned int, SIGN_PRESERVING_VALUE_PRESERVING);
|
||||
}
|
||||
|
||||
TEST(SafeNumerics, SizeTOperations)
|
||||
{
|
||||
TEST_NUMERIC_CONVERSION(size_t, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
|
||||
TEST_NUMERIC_CONVERSION(int, size_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
|
||||
}
|
||||
|
||||
TEST(SafeNumerics, CastTests)
|
||||
{
|
||||
// MSVC catches and warns that we're forcing saturation in these tests.
|
||||
// Since that's intentional, we need to shut this warning off.
|
||||
#if defined(COMPILER_MSVC)
|
||||
#pragma warning(disable : 4756)
|
||||
#endif
|
||||
|
||||
int small_positive = 1;
|
||||
int small_negative = -1;
|
||||
double double_small = 1.0;
|
||||
double double_large = numeric_limits<double>::max();
|
||||
double double_infinity = numeric_limits<float>::infinity();
|
||||
double double_large_int = numeric_limits<int>::max();
|
||||
double double_small_int = numeric_limits<int>::min();
|
||||
|
||||
// Just test that the casts compile, since the other tests cover logic.
|
||||
EXPECT_EQ(0, checked_cast<int>(static_cast<size_t>(0)));
|
||||
EXPECT_EQ(0, strict_cast<int>(static_cast<char>(0)));
|
||||
EXPECT_EQ(0, strict_cast<int>(static_cast<unsigned char>(0)));
|
||||
EXPECT_EQ(0U, strict_cast<unsigned>(static_cast<unsigned char>(0)));
|
||||
EXPECT_EQ(1ULL, static_cast<uint64_t>(StrictNumeric<size_t>(1U)));
|
||||
EXPECT_EQ(1ULL, static_cast<uint64_t>(SizeT(1U)));
|
||||
EXPECT_EQ(1U, static_cast<size_t>(StrictNumeric<unsigned>(1U)));
|
||||
|
||||
EXPECT_TRUE(CheckedNumeric<uint64_t>(StrictNumeric<unsigned>(1U)).IsValid());
|
||||
EXPECT_TRUE(CheckedNumeric<int>(StrictNumeric<unsigned>(1U)).IsValid());
|
||||
EXPECT_FALSE(CheckedNumeric<unsigned>(StrictNumeric<int>(-1)).IsValid());
|
||||
|
||||
EXPECT_TRUE(IsValueNegative(-1));
|
||||
EXPECT_TRUE(IsValueNegative(numeric_limits<int>::min()));
|
||||
EXPECT_FALSE(IsValueNegative(numeric_limits<unsigned>::min()));
|
||||
EXPECT_TRUE(IsValueNegative(-numeric_limits<double>::max()));
|
||||
EXPECT_FALSE(IsValueNegative(0));
|
||||
EXPECT_FALSE(IsValueNegative(1));
|
||||
EXPECT_FALSE(IsValueNegative(0u));
|
||||
EXPECT_FALSE(IsValueNegative(1u));
|
||||
EXPECT_FALSE(IsValueNegative(numeric_limits<int>::max()));
|
||||
EXPECT_FALSE(IsValueNegative(numeric_limits<unsigned>::max()));
|
||||
EXPECT_FALSE(IsValueNegative(numeric_limits<double>::max()));
|
||||
|
||||
// These casts and coercions will fail to compile:
|
||||
// EXPECT_EQ(0, strict_cast<int>(static_cast<size_t>(0)));
|
||||
// EXPECT_EQ(0, strict_cast<size_t>(static_cast<int>(0)));
|
||||
// EXPECT_EQ(1ULL, StrictNumeric<size_t>(1));
|
||||
// EXPECT_EQ(1, StrictNumeric<size_t>(1U));
|
||||
|
||||
// Test various saturation corner cases.
|
||||
EXPECT_EQ(saturated_cast<int>(small_negative), static_cast<int>(small_negative));
|
||||
EXPECT_EQ(saturated_cast<int>(small_positive), static_cast<int>(small_positive));
|
||||
EXPECT_EQ(saturated_cast<unsigned>(small_negative), static_cast<unsigned>(0));
|
||||
EXPECT_EQ(saturated_cast<int>(double_small), static_cast<int>(double_small));
|
||||
EXPECT_EQ(saturated_cast<int>(double_large), numeric_limits<int>::max());
|
||||
EXPECT_EQ(saturated_cast<float>(double_large), double_infinity);
|
||||
EXPECT_EQ(saturated_cast<float>(-double_large), -double_infinity);
|
||||
EXPECT_EQ(numeric_limits<int>::min(), saturated_cast<int>(double_small_int));
|
||||
EXPECT_EQ(numeric_limits<int>::max(), saturated_cast<int>(double_large_int));
|
||||
|
||||
float not_a_number =
|
||||
std::numeric_limits<float>::infinity() - std::numeric_limits<float>::infinity();
|
||||
EXPECT_TRUE(std::isnan(not_a_number));
|
||||
EXPECT_EQ(0, saturated_cast<int>(not_a_number));
|
||||
}
|
||||
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
|
||||
TEST(SafeNumerics, SaturatedCastChecks)
|
||||
{
|
||||
float not_a_number =
|
||||
std::numeric_limits<float>::infinity() - std::numeric_limits<float>::infinity();
|
||||
EXPECT_TRUE(std::isnan(not_a_number));
|
||||
EXPECT_DEATH((saturated_cast<int, base::SaturatedCastNaNBehaviorCheck>(not_a_number)), "");
|
||||
}
|
||||
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
TEST(SafeNumerics, IsValueInRangeForNumericType)
|
||||
{
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(0));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(1));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(2));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(-1));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(0xffffffffu));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0xffffffff)));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0x100000000)));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(UINT64_C(0x100000001)));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(std::numeric_limits<int32_t>::min()));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<uint32_t>(std::numeric_limits<int64_t>::min()));
|
||||
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(0));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(1));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(2));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(-1));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(0x7fffffff));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(0x7fffffffu));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(0x80000000u));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(0xffffffffu));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0x80000000)));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0xffffffff)));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(INT64_C(0x100000000)));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(std::numeric_limits<int32_t>::min()));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int32_t>(
|
||||
static_cast<int64_t>(std::numeric_limits<int32_t>::min())));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(
|
||||
static_cast<int64_t>(std::numeric_limits<int32_t>::min()) - 1));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<int32_t>(std::numeric_limits<int64_t>::min()));
|
||||
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(0));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(1));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(2));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(-1));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(0xffffffffu));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0xffffffff)));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0x100000000)));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<uint64_t>(UINT64_C(0x100000001)));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(std::numeric_limits<int32_t>::min()));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(INT64_C(-1)));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<uint64_t>(std::numeric_limits<int64_t>::min()));
|
||||
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(1));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(2));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(-1));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0x7fffffff));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0x7fffffffu));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0x80000000u));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(0xffffffffu));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(INT64_C(0x80000000)));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(INT64_C(0xffffffff)));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(INT64_C(0x100000000)));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(INT64_C(0x7fffffffffffffff)));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(UINT64_C(0x7fffffffffffffff)));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<int64_t>(UINT64_C(0x8000000000000000)));
|
||||
EXPECT_FALSE(IsValueInRangeForNumericType<int64_t>(UINT64_C(0xffffffffffffffff)));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(std::numeric_limits<int32_t>::min()));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(
|
||||
static_cast<int64_t>(std::numeric_limits<int32_t>::min())));
|
||||
EXPECT_TRUE(IsValueInRangeForNumericType<int64_t>(std::numeric_limits<int64_t>::min()));
|
||||
}
|
||||
|
||||
TEST(SafeNumerics, CompoundNumericOperations)
|
||||
{
|
||||
CheckedNumeric<int> a = 1;
|
||||
CheckedNumeric<int> b = 2;
|
||||
CheckedNumeric<int> c = 3;
|
||||
CheckedNumeric<int> d = 4;
|
||||
a += b;
|
||||
EXPECT_EQ(3, a.ValueOrDie());
|
||||
a -= c;
|
||||
EXPECT_EQ(0, a.ValueOrDie());
|
||||
d /= b;
|
||||
EXPECT_EQ(2, d.ValueOrDie());
|
||||
d *= d;
|
||||
EXPECT_EQ(4, d.ValueOrDie());
|
||||
|
||||
CheckedNumeric<int> too_large = std::numeric_limits<int>::max();
|
||||
EXPECT_TRUE(too_large.IsValid());
|
||||
too_large += d;
|
||||
EXPECT_FALSE(too_large.IsValid());
|
||||
too_large -= d;
|
||||
EXPECT_FALSE(too_large.IsValid());
|
||||
too_large /= d;
|
||||
EXPECT_FALSE(too_large.IsValid());
|
||||
}
|
||||
156
gfx/angle/src/common/tls.cpp
Normal file
156
gfx/angle/src/common/tls.cpp
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
//
|
||||
// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
// tls.cpp: Simple cross-platform interface for thread local storage.
|
||||
|
||||
#include "common/tls.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef ANGLE_ENABLE_WINDOWS_STORE
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
#include <wrl/client.h>
|
||||
#include <wrl/async.h>
|
||||
#include <Windows.System.Threading.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace ABI::Windows::System::Threading;
|
||||
|
||||
// Thread local storage for Windows Store support
|
||||
typedef vector<void*> ThreadLocalData;
|
||||
|
||||
static __declspec(thread) ThreadLocalData* currentThreadData = nullptr;
|
||||
static set<ThreadLocalData*> allThreadData;
|
||||
static DWORD nextTlsIndex = 0;
|
||||
static vector<DWORD> freeTlsIndices;
|
||||
|
||||
#endif
|
||||
|
||||
TLSIndex CreateTLSIndex()
|
||||
{
|
||||
TLSIndex index;
|
||||
|
||||
#ifdef ANGLE_PLATFORM_WINDOWS
|
||||
#ifdef ANGLE_ENABLE_WINDOWS_STORE
|
||||
if (!freeTlsIndices.empty())
|
||||
{
|
||||
DWORD result = freeTlsIndices.back();
|
||||
freeTlsIndices.pop_back();
|
||||
index = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
index = nextTlsIndex++;
|
||||
}
|
||||
#else
|
||||
index = TlsAlloc();
|
||||
#endif
|
||||
|
||||
#elif defined(ANGLE_PLATFORM_POSIX)
|
||||
// Create global pool key
|
||||
if ((pthread_key_create(&index, NULL)) != 0)
|
||||
{
|
||||
index = TLS_INVALID_INDEX;
|
||||
}
|
||||
#endif
|
||||
|
||||
assert(index != TLS_INVALID_INDEX && "CreateTLSIndex(): Unable to allocate Thread Local Storage");
|
||||
return index;
|
||||
}
|
||||
|
||||
bool DestroyTLSIndex(TLSIndex index)
|
||||
{
|
||||
assert(index != TLS_INVALID_INDEX && "DestroyTLSIndex(): Invalid TLS Index");
|
||||
if (index == TLS_INVALID_INDEX)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ANGLE_PLATFORM_WINDOWS
|
||||
#ifdef ANGLE_ENABLE_WINDOWS_STORE
|
||||
assert(index < nextTlsIndex);
|
||||
assert(find(freeTlsIndices.begin(), freeTlsIndices.end(), index) == freeTlsIndices.end());
|
||||
|
||||
freeTlsIndices.push_back(index);
|
||||
for (auto threadData : allThreadData)
|
||||
{
|
||||
if (threadData->size() > index)
|
||||
{
|
||||
threadData->at(index) = nullptr;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return (TlsFree(index) == TRUE);
|
||||
#endif
|
||||
#elif defined(ANGLE_PLATFORM_POSIX)
|
||||
return (pthread_key_delete(index) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SetTLSValue(TLSIndex index, void *value)
|
||||
{
|
||||
assert(index != TLS_INVALID_INDEX && "SetTLSValue(): Invalid TLS Index");
|
||||
if (index == TLS_INVALID_INDEX)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ANGLE_PLATFORM_WINDOWS
|
||||
#ifdef ANGLE_ENABLE_WINDOWS_STORE
|
||||
ThreadLocalData* threadData = currentThreadData;
|
||||
if (!threadData)
|
||||
{
|
||||
threadData = new ThreadLocalData(index + 1, nullptr);
|
||||
allThreadData.insert(threadData);
|
||||
currentThreadData = threadData;
|
||||
}
|
||||
else if (threadData->size() <= index)
|
||||
{
|
||||
threadData->resize(index + 1, nullptr);
|
||||
}
|
||||
|
||||
threadData->at(index) = value;
|
||||
return true;
|
||||
#else
|
||||
return (TlsSetValue(index, value) == TRUE);
|
||||
#endif
|
||||
#elif defined(ANGLE_PLATFORM_POSIX)
|
||||
return (pthread_setspecific(index, value) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void *GetTLSValue(TLSIndex index)
|
||||
{
|
||||
assert(index != TLS_INVALID_INDEX && "GetTLSValue(): Invalid TLS Index");
|
||||
if (index == TLS_INVALID_INDEX)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef ANGLE_PLATFORM_WINDOWS
|
||||
#ifdef ANGLE_ENABLE_WINDOWS_STORE
|
||||
ThreadLocalData* threadData = currentThreadData;
|
||||
if (threadData && threadData->size() > index)
|
||||
{
|
||||
return threadData->at(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return TlsGetValue(index);
|
||||
#endif
|
||||
#elif defined(ANGLE_PLATFORM_POSIX)
|
||||
return pthread_getspecific(index);
|
||||
#endif
|
||||
}
|
||||
46
gfx/angle/src/common/tls.h
Normal file
46
gfx/angle/src/common/tls.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
// tls.h: Simple cross-platform interface for thread local storage.
|
||||
|
||||
#ifndef COMMON_TLS_H_
|
||||
#define COMMON_TLS_H_
|
||||
|
||||
#include "common/platform.h"
|
||||
|
||||
#ifdef ANGLE_PLATFORM_WINDOWS
|
||||
|
||||
// TLS does not exist for Windows Store and needs to be emulated
|
||||
# ifdef ANGLE_ENABLE_WINDOWS_STORE
|
||||
# ifndef TLS_OUT_OF_INDEXES
|
||||
# define TLS_OUT_OF_INDEXES static_cast<DWORD>(0xFFFFFFFF)
|
||||
# endif
|
||||
# ifndef CREATE_SUSPENDED
|
||||
# define CREATE_SUSPENDED 0x00000004
|
||||
# endif
|
||||
# endif
|
||||
typedef DWORD TLSIndex;
|
||||
# define TLS_INVALID_INDEX (TLS_OUT_OF_INDEXES)
|
||||
#elif defined(ANGLE_PLATFORM_POSIX)
|
||||
# include <pthread.h>
|
||||
# include <semaphore.h>
|
||||
# include <errno.h>
|
||||
typedef pthread_key_t TLSIndex;
|
||||
# define TLS_INVALID_INDEX (static_cast<TLSIndex>(-1))
|
||||
#else
|
||||
# error Unsupported platform.
|
||||
#endif
|
||||
|
||||
// TODO(kbr): for POSIX platforms this will have to be changed to take
|
||||
// in a destructor function pointer, to allow the thread-local storage
|
||||
// to be properly deallocated upon thread exit.
|
||||
TLSIndex CreateTLSIndex();
|
||||
bool DestroyTLSIndex(TLSIndex index);
|
||||
|
||||
bool SetTLSValue(TLSIndex index, void *value);
|
||||
void *GetTLSValue(TLSIndex index);
|
||||
|
||||
#endif // COMMON_TLS_H_
|
||||
843
gfx/angle/src/common/utilities.cpp
Normal file
843
gfx/angle/src/common/utilities.cpp
Normal file
|
|
@ -0,0 +1,843 @@
|
|||
//
|
||||
// Copyright (c) 2002-2013 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.
|
||||
//
|
||||
|
||||
// utilities.cpp: Conversion functions and other utility routines.
|
||||
|
||||
#include "common/utilities.h"
|
||||
#include "common/mathutil.h"
|
||||
#include "common/platform.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#if defined(ANGLE_ENABLE_WINDOWS_STORE)
|
||||
# include <wrl.h>
|
||||
# include <wrl/wrappers/corewrappers.h>
|
||||
# include <windows.applicationmodel.core.h>
|
||||
# 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
|
||||
{
|
||||
|
||||
int VariableComponentCount(GLenum type)
|
||||
{
|
||||
return VariableRowCount(type) * VariableColumnCount(type);
|
||||
}
|
||||
|
||||
GLenum VariableComponentType(GLenum type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case GL_BOOL:
|
||||
case GL_BOOL_VEC2:
|
||||
case GL_BOOL_VEC3:
|
||||
case GL_BOOL_VEC4:
|
||||
return GL_BOOL;
|
||||
case GL_FLOAT:
|
||||
case GL_FLOAT_VEC2:
|
||||
case GL_FLOAT_VEC3:
|
||||
case GL_FLOAT_VEC4:
|
||||
case GL_FLOAT_MAT2:
|
||||
case GL_FLOAT_MAT3:
|
||||
case GL_FLOAT_MAT4:
|
||||
case GL_FLOAT_MAT2x3:
|
||||
case GL_FLOAT_MAT3x2:
|
||||
case GL_FLOAT_MAT2x4:
|
||||
case GL_FLOAT_MAT4x2:
|
||||
case GL_FLOAT_MAT3x4:
|
||||
case GL_FLOAT_MAT4x3:
|
||||
return GL_FLOAT;
|
||||
case GL_INT:
|
||||
case GL_SAMPLER_2D:
|
||||
case GL_SAMPLER_3D:
|
||||
case GL_SAMPLER_CUBE:
|
||||
case GL_SAMPLER_2D_ARRAY:
|
||||
case GL_SAMPLER_EXTERNAL_OES:
|
||||
case GL_INT_SAMPLER_2D:
|
||||
case GL_INT_SAMPLER_3D:
|
||||
case GL_INT_SAMPLER_CUBE:
|
||||
case GL_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_3D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_CUBE:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_SAMPLER_2D_SHADOW:
|
||||
case GL_SAMPLER_CUBE_SHADOW:
|
||||
case GL_SAMPLER_2D_ARRAY_SHADOW:
|
||||
case GL_INT_VEC2:
|
||||
case GL_INT_VEC3:
|
||||
case GL_INT_VEC4:
|
||||
return GL_INT;
|
||||
case GL_UNSIGNED_INT:
|
||||
case GL_UNSIGNED_INT_VEC2:
|
||||
case GL_UNSIGNED_INT_VEC3:
|
||||
case GL_UNSIGNED_INT_VEC4:
|
||||
return GL_UNSIGNED_INT;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return GL_NONE;
|
||||
}
|
||||
|
||||
size_t VariableComponentSize(GLenum type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case GL_BOOL: return sizeof(GLint);
|
||||
case GL_FLOAT: return sizeof(GLfloat);
|
||||
case GL_INT: return sizeof(GLint);
|
||||
case GL_UNSIGNED_INT: return sizeof(GLuint);
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t VariableInternalSize(GLenum type)
|
||||
{
|
||||
// Expanded to 4-element vectors
|
||||
return VariableComponentSize(VariableComponentType(type)) * VariableRowCount(type) * 4;
|
||||
}
|
||||
|
||||
size_t VariableExternalSize(GLenum type)
|
||||
{
|
||||
return VariableComponentSize(VariableComponentType(type)) * VariableComponentCount(type);
|
||||
}
|
||||
|
||||
GLenum VariableBoolVectorType(GLenum type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GL_FLOAT:
|
||||
case GL_INT:
|
||||
case GL_UNSIGNED_INT:
|
||||
return GL_BOOL;
|
||||
case GL_FLOAT_VEC2:
|
||||
case GL_INT_VEC2:
|
||||
case GL_UNSIGNED_INT_VEC2:
|
||||
return GL_BOOL_VEC2;
|
||||
case GL_FLOAT_VEC3:
|
||||
case GL_INT_VEC3:
|
||||
case GL_UNSIGNED_INT_VEC3:
|
||||
return GL_BOOL_VEC3;
|
||||
case GL_FLOAT_VEC4:
|
||||
case GL_INT_VEC4:
|
||||
case GL_UNSIGNED_INT_VEC4:
|
||||
return GL_BOOL_VEC4;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return GL_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
int VariableRowCount(GLenum type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GL_NONE:
|
||||
case GL_STRUCT_ANGLEX:
|
||||
return 0;
|
||||
case GL_BOOL:
|
||||
case GL_FLOAT:
|
||||
case GL_INT:
|
||||
case GL_UNSIGNED_INT:
|
||||
case GL_BOOL_VEC2:
|
||||
case GL_FLOAT_VEC2:
|
||||
case GL_INT_VEC2:
|
||||
case GL_UNSIGNED_INT_VEC2:
|
||||
case GL_BOOL_VEC3:
|
||||
case GL_FLOAT_VEC3:
|
||||
case GL_INT_VEC3:
|
||||
case GL_UNSIGNED_INT_VEC3:
|
||||
case GL_BOOL_VEC4:
|
||||
case GL_FLOAT_VEC4:
|
||||
case GL_INT_VEC4:
|
||||
case GL_UNSIGNED_INT_VEC4:
|
||||
case GL_SAMPLER_2D:
|
||||
case GL_SAMPLER_3D:
|
||||
case GL_SAMPLER_CUBE:
|
||||
case GL_SAMPLER_2D_ARRAY:
|
||||
case GL_SAMPLER_EXTERNAL_OES:
|
||||
case GL_SAMPLER_2D_RECT_ARB:
|
||||
case GL_INT_SAMPLER_2D:
|
||||
case GL_INT_SAMPLER_3D:
|
||||
case GL_INT_SAMPLER_CUBE:
|
||||
case GL_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_3D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_CUBE:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_SAMPLER_2D_SHADOW:
|
||||
case GL_SAMPLER_CUBE_SHADOW:
|
||||
case GL_SAMPLER_2D_ARRAY_SHADOW:
|
||||
case GL_IMAGE_2D:
|
||||
case GL_INT_IMAGE_2D:
|
||||
case GL_UNSIGNED_INT_IMAGE_2D:
|
||||
case GL_IMAGE_2D_ARRAY:
|
||||
case GL_INT_IMAGE_2D_ARRAY:
|
||||
case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
|
||||
case GL_IMAGE_3D:
|
||||
case GL_INT_IMAGE_3D:
|
||||
case GL_UNSIGNED_INT_IMAGE_3D:
|
||||
case GL_IMAGE_CUBE:
|
||||
case GL_INT_IMAGE_CUBE:
|
||||
case GL_UNSIGNED_INT_IMAGE_CUBE:
|
||||
return 1;
|
||||
case GL_FLOAT_MAT2:
|
||||
case GL_FLOAT_MAT3x2:
|
||||
case GL_FLOAT_MAT4x2:
|
||||
return 2;
|
||||
case GL_FLOAT_MAT3:
|
||||
case GL_FLOAT_MAT2x3:
|
||||
case GL_FLOAT_MAT4x3:
|
||||
return 3;
|
||||
case GL_FLOAT_MAT4:
|
||||
case GL_FLOAT_MAT2x4:
|
||||
case GL_FLOAT_MAT3x4:
|
||||
return 4;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VariableColumnCount(GLenum type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GL_NONE:
|
||||
case GL_STRUCT_ANGLEX:
|
||||
return 0;
|
||||
case GL_BOOL:
|
||||
case GL_FLOAT:
|
||||
case GL_INT:
|
||||
case GL_UNSIGNED_INT:
|
||||
case GL_SAMPLER_2D:
|
||||
case GL_SAMPLER_3D:
|
||||
case GL_SAMPLER_CUBE:
|
||||
case GL_SAMPLER_2D_ARRAY:
|
||||
case GL_INT_SAMPLER_2D:
|
||||
case GL_INT_SAMPLER_3D:
|
||||
case GL_INT_SAMPLER_CUBE:
|
||||
case GL_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_SAMPLER_EXTERNAL_OES:
|
||||
case GL_SAMPLER_2D_RECT_ARB:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_3D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_CUBE:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_SAMPLER_2D_SHADOW:
|
||||
case GL_SAMPLER_CUBE_SHADOW:
|
||||
case GL_SAMPLER_2D_ARRAY_SHADOW:
|
||||
return 1;
|
||||
case GL_BOOL_VEC2:
|
||||
case GL_FLOAT_VEC2:
|
||||
case GL_INT_VEC2:
|
||||
case GL_UNSIGNED_INT_VEC2:
|
||||
case GL_FLOAT_MAT2:
|
||||
case GL_FLOAT_MAT2x3:
|
||||
case GL_FLOAT_MAT2x4:
|
||||
return 2;
|
||||
case GL_BOOL_VEC3:
|
||||
case GL_FLOAT_VEC3:
|
||||
case GL_INT_VEC3:
|
||||
case GL_UNSIGNED_INT_VEC3:
|
||||
case GL_FLOAT_MAT3:
|
||||
case GL_FLOAT_MAT3x2:
|
||||
case GL_FLOAT_MAT3x4:
|
||||
return 3;
|
||||
case GL_BOOL_VEC4:
|
||||
case GL_FLOAT_VEC4:
|
||||
case GL_INT_VEC4:
|
||||
case GL_UNSIGNED_INT_VEC4:
|
||||
case GL_FLOAT_MAT4:
|
||||
case GL_FLOAT_MAT4x2:
|
||||
case GL_FLOAT_MAT4x3:
|
||||
return 4;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool IsSamplerType(GLenum type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GL_SAMPLER_2D:
|
||||
case GL_SAMPLER_3D:
|
||||
case GL_SAMPLER_CUBE:
|
||||
case GL_SAMPLER_2D_ARRAY:
|
||||
case GL_SAMPLER_EXTERNAL_OES:
|
||||
case GL_INT_SAMPLER_2D:
|
||||
case GL_INT_SAMPLER_3D:
|
||||
case GL_INT_SAMPLER_CUBE:
|
||||
case GL_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_3D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_CUBE:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_SAMPLER_2D_SHADOW:
|
||||
case GL_SAMPLER_CUBE_SHADOW:
|
||||
case GL_SAMPLER_2D_ARRAY_SHADOW:
|
||||
return true;
|
||||
}
|
||||
|
||||
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_EXTERNAL_OES:
|
||||
return GL_TEXTURE_EXTERNAL_OES;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
GLenum TransposeMatrixType(GLenum type)
|
||||
{
|
||||
if (!IsMatrixType(type))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GL_FLOAT_MAT2: return GL_FLOAT_MAT2;
|
||||
case GL_FLOAT_MAT3: return GL_FLOAT_MAT3;
|
||||
case GL_FLOAT_MAT4: return GL_FLOAT_MAT4;
|
||||
case GL_FLOAT_MAT2x3: return GL_FLOAT_MAT3x2;
|
||||
case GL_FLOAT_MAT3x2: return GL_FLOAT_MAT2x3;
|
||||
case GL_FLOAT_MAT2x4: return GL_FLOAT_MAT4x2;
|
||||
case GL_FLOAT_MAT4x2: return GL_FLOAT_MAT2x4;
|
||||
case GL_FLOAT_MAT3x4: return GL_FLOAT_MAT4x3;
|
||||
case GL_FLOAT_MAT4x3: return GL_FLOAT_MAT3x4;
|
||||
default: UNREACHABLE(); return GL_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
int MatrixRegisterCount(GLenum type, bool isRowMajorMatrix)
|
||||
{
|
||||
ASSERT(IsMatrixType(type));
|
||||
return isRowMajorMatrix ? VariableRowCount(type) : VariableColumnCount(type);
|
||||
}
|
||||
|
||||
int MatrixComponentCount(GLenum type, bool isRowMajorMatrix)
|
||||
{
|
||||
ASSERT(IsMatrixType(type));
|
||||
return isRowMajorMatrix ? VariableColumnCount(type) : VariableRowCount(type);
|
||||
}
|
||||
|
||||
int VariableRegisterCount(GLenum type)
|
||||
{
|
||||
return IsMatrixType(type) ? VariableColumnCount(type) : 1;
|
||||
}
|
||||
|
||||
int AllocateFirstFreeBits(unsigned int *bits, unsigned int allocationSize, unsigned int bitsSize)
|
||||
{
|
||||
ASSERT(allocationSize <= bitsSize);
|
||||
|
||||
unsigned int mask = std::numeric_limits<unsigned int>::max() >> (std::numeric_limits<unsigned int>::digits - allocationSize);
|
||||
|
||||
for (unsigned int i = 0; i < bitsSize - allocationSize + 1; i++)
|
||||
{
|
||||
if ((*bits & mask) == 0)
|
||||
{
|
||||
*bits |= mask;
|
||||
return i;
|
||||
}
|
||||
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static_assert(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1, "Unexpected GL cube map enum value.");
|
||||
static_assert(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2, "Unexpected GL cube map enum value.");
|
||||
static_assert(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3, "Unexpected GL cube map enum value.");
|
||||
static_assert(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4, "Unexpected GL cube map enum value.");
|
||||
static_assert(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5, "Unexpected GL cube map enum value.");
|
||||
|
||||
bool IsCubeMapTextureTarget(GLenum target)
|
||||
{
|
||||
return (target >= FirstCubeMapTextureTarget && target <= LastCubeMapTextureTarget);
|
||||
}
|
||||
|
||||
size_t CubeMapTextureTargetToLayerIndex(GLenum target)
|
||||
{
|
||||
ASSERT(IsCubeMapTextureTarget(target));
|
||||
return target - static_cast<size_t>(FirstCubeMapTextureTarget);
|
||||
}
|
||||
|
||||
GLenum LayerIndexToCubeMapTextureTarget(size_t index)
|
||||
{
|
||||
ASSERT(index <= (LastCubeMapTextureTarget - FirstCubeMapTextureTarget));
|
||||
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)
|
||||
{
|
||||
case GL_TRIANGLES:
|
||||
case GL_TRIANGLE_FAN:
|
||||
case GL_TRIANGLE_STRIP:
|
||||
return true;
|
||||
case GL_POINTS:
|
||||
case GL_LINES:
|
||||
case GL_LINE_LOOP:
|
||||
case GL_LINE_STRIP:
|
||||
return false;
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// [OpenGL ES SL 3.00.4] Section 11 p. 120
|
||||
// Vertex Outs/Fragment Ins packing priorities
|
||||
int VariableSortOrder(GLenum type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
// 1. Arrays of mat4 and mat4
|
||||
// Non-square matrices of type matCxR consume the same space as a square
|
||||
// matrix of type matN where N is the greater of C and R
|
||||
case GL_FLOAT_MAT4:
|
||||
case GL_FLOAT_MAT2x4:
|
||||
case GL_FLOAT_MAT3x4:
|
||||
case GL_FLOAT_MAT4x2:
|
||||
case GL_FLOAT_MAT4x3:
|
||||
return 0;
|
||||
|
||||
// 2. Arrays of mat2 and mat2 (since they occupy full rows)
|
||||
case GL_FLOAT_MAT2:
|
||||
return 1;
|
||||
|
||||
// 3. Arrays of vec4 and vec4
|
||||
case GL_FLOAT_VEC4:
|
||||
case GL_INT_VEC4:
|
||||
case GL_BOOL_VEC4:
|
||||
case GL_UNSIGNED_INT_VEC4:
|
||||
return 2;
|
||||
|
||||
// 4. Arrays of mat3 and mat3
|
||||
case GL_FLOAT_MAT3:
|
||||
case GL_FLOAT_MAT2x3:
|
||||
case GL_FLOAT_MAT3x2:
|
||||
return 3;
|
||||
|
||||
// 5. Arrays of vec3 and vec3
|
||||
case GL_FLOAT_VEC3:
|
||||
case GL_INT_VEC3:
|
||||
case GL_BOOL_VEC3:
|
||||
case GL_UNSIGNED_INT_VEC3:
|
||||
return 4;
|
||||
|
||||
// 6. Arrays of vec2 and vec2
|
||||
case GL_FLOAT_VEC2:
|
||||
case GL_INT_VEC2:
|
||||
case GL_BOOL_VEC2:
|
||||
case GL_UNSIGNED_INT_VEC2:
|
||||
return 5;
|
||||
|
||||
// 7. Single component types
|
||||
case GL_FLOAT:
|
||||
case GL_INT:
|
||||
case GL_BOOL:
|
||||
case GL_UNSIGNED_INT:
|
||||
case GL_SAMPLER_2D:
|
||||
case GL_SAMPLER_CUBE:
|
||||
case GL_SAMPLER_EXTERNAL_OES:
|
||||
case GL_SAMPLER_2D_RECT_ARB:
|
||||
case GL_SAMPLER_2D_ARRAY:
|
||||
case GL_SAMPLER_3D:
|
||||
case GL_INT_SAMPLER_2D:
|
||||
case GL_INT_SAMPLER_3D:
|
||||
case GL_INT_SAMPLER_CUBE:
|
||||
case GL_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_3D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_CUBE:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_SAMPLER_2D_SHADOW:
|
||||
case GL_SAMPLER_2D_ARRAY_SHADOW:
|
||||
case GL_SAMPLER_CUBE_SHADOW:
|
||||
return 6;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
template <>
|
||||
GLuint ConvertToGLuint(GLfloat param)
|
||||
{
|
||||
return uiround<GLuint>(param);
|
||||
}
|
||||
|
||||
template <>
|
||||
GLint ConvertToGLint(GLfloat param)
|
||||
{
|
||||
return iround<GLint>(param);
|
||||
}
|
||||
|
||||
template <>
|
||||
GLint ConvertFromGLfloat(GLfloat param)
|
||||
{
|
||||
return iround<GLint>(param);
|
||||
}
|
||||
template <>
|
||||
GLuint ConvertFromGLfloat(GLfloat param)
|
||||
{
|
||||
return uiround<GLuint>(param);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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));
|
||||
}
|
||||
} // namespace egl_gl
|
||||
|
||||
#if !defined(ANGLE_ENABLE_WINDOWS_STORE)
|
||||
std::string getTempPath()
|
||||
{
|
||||
#ifdef ANGLE_PLATFORM_WINDOWS
|
||||
char path[MAX_PATH];
|
||||
DWORD pathLen = GetTempPathA(sizeof(path) / sizeof(path[0]), path);
|
||||
if (pathLen == 0)
|
||||
{
|
||||
UNREACHABLE();
|
||||
return std::string();
|
||||
}
|
||||
|
||||
UINT unique = GetTempFileNameA(path, "sh", 0, path);
|
||||
if (unique == 0)
|
||||
{
|
||||
UNREACHABLE();
|
||||
return std::string();
|
||||
}
|
||||
|
||||
return path;
|
||||
#else
|
||||
UNIMPLEMENTED();
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
void writeFile(const char* path, const void* content, size_t size)
|
||||
{
|
||||
FILE* file = fopen(path, "w");
|
||||
if (!file)
|
||||
{
|
||||
UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
|
||||
fwrite(content, sizeof(char), size, file);
|
||||
fclose(file);
|
||||
}
|
||||
#endif // !ANGLE_ENABLE_WINDOWS_STORE
|
||||
|
||||
#if defined (ANGLE_PLATFORM_WINDOWS)
|
||||
|
||||
// Causes the thread to relinquish the remainder of its time slice to any
|
||||
// other thread that is ready to run.If there are no other threads ready
|
||||
// to run, the function returns immediately, and the thread continues execution.
|
||||
void ScheduleYield()
|
||||
{
|
||||
Sleep(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
172
gfx/angle/src/common/utilities.h
Normal file
172
gfx/angle/src/common/utilities.h
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
//
|
||||
// Copyright (c) 2002-2013 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.
|
||||
//
|
||||
|
||||
// utilities.h: Conversion functions and other utility routines.
|
||||
|
||||
#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
|
||||
{
|
||||
|
||||
int VariableComponentCount(GLenum type);
|
||||
GLenum VariableComponentType(GLenum type);
|
||||
size_t VariableComponentSize(GLenum type);
|
||||
size_t VariableInternalSize(GLenum type);
|
||||
size_t VariableExternalSize(GLenum type);
|
||||
GLenum VariableBoolVectorType(GLenum type);
|
||||
int VariableRowCount(GLenum type);
|
||||
int VariableColumnCount(GLenum type);
|
||||
bool IsSamplerType(GLenum type);
|
||||
GLenum SamplerTypeToTextureType(GLenum samplerType);
|
||||
bool IsMatrixType(GLenum type);
|
||||
GLenum TransposeMatrixType(GLenum type);
|
||||
int VariableRegisterCount(GLenum type);
|
||||
int MatrixRegisterCount(GLenum type, bool isRowMajorMatrix);
|
||||
int MatrixComponentCount(GLenum type, bool isRowMajorMatrix);
|
||||
int VariableSortOrder(GLenum type);
|
||||
|
||||
int AllocateFirstFreeBits(unsigned int *bits, unsigned int allocationSize, unsigned int bitsSize);
|
||||
|
||||
static const GLenum FirstCubeMapTextureTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
|
||||
static const GLenum LastCubeMapTextureTarget = GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
|
||||
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
|
||||
// Data Conversion For State-Setting Commands
|
||||
// Floating-point values are rounded to the nearest integer, instead of truncated, as done by static_cast.
|
||||
template <typename outT> outT iround(GLfloat value) { return static_cast<outT>(value > 0.0f ? floor(value + 0.5f) : ceil(value - 0.5f)); }
|
||||
template <typename outT> outT uiround(GLfloat value) { return static_cast<outT>(value + 0.5f); }
|
||||
|
||||
// Helper for converting arbitrary GL types to other GL types used in queries and state setting
|
||||
template <typename ParamType>
|
||||
GLuint ConvertToGLuint(ParamType param)
|
||||
{
|
||||
return static_cast<GLuint>(param);
|
||||
}
|
||||
template <>
|
||||
GLuint ConvertToGLuint(GLfloat param);
|
||||
|
||||
template <typename ParamType>
|
||||
GLint ConvertToGLint(ParamType param)
|
||||
{
|
||||
return static_cast<GLint>(param);
|
||||
}
|
||||
template <>
|
||||
GLint ConvertToGLint(GLfloat param);
|
||||
|
||||
// Same conversion as uint
|
||||
template <typename ParamType>
|
||||
GLenum ConvertToGLenum(ParamType param)
|
||||
{
|
||||
return static_cast<GLenum>(ConvertToGLuint(param));
|
||||
}
|
||||
|
||||
template <typename ParamType>
|
||||
GLfloat ConvertToGLfloat(ParamType param)
|
||||
{
|
||||
return static_cast<GLfloat>(param);
|
||||
}
|
||||
|
||||
template <typename ParamType>
|
||||
ParamType ConvertFromGLfloat(GLfloat param)
|
||||
{
|
||||
return static_cast<ParamType>(param);
|
||||
}
|
||||
template <>
|
||||
GLint ConvertFromGLfloat(GLfloat param);
|
||||
template <>
|
||||
GLuint ConvertFromGLfloat(GLfloat param);
|
||||
|
||||
template <typename ParamType>
|
||||
ParamType ConvertFromGLenum(GLenum param)
|
||||
{
|
||||
return static_cast<ParamType>(param);
|
||||
}
|
||||
|
||||
template <typename ParamType>
|
||||
ParamType ConvertFromGLuint(GLuint param)
|
||||
{
|
||||
return static_cast<ParamType>(param);
|
||||
}
|
||||
|
||||
template <typename ParamType>
|
||||
ParamType ConvertFromGLint(GLint param)
|
||||
{
|
||||
return static_cast<ParamType>(param);
|
||||
}
|
||||
|
||||
template <typename ParamType>
|
||||
ParamType ConvertFromGLboolean(GLboolean param)
|
||||
{
|
||||
return static_cast<ParamType>(param ? GL_TRUE : GL_FALSE);
|
||||
}
|
||||
|
||||
template <typename ParamType>
|
||||
ParamType ConvertFromGLint64(GLint64 param)
|
||||
{
|
||||
return clampCast<ParamType>(param);
|
||||
}
|
||||
|
||||
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)
|
||||
std::string getTempPath();
|
||||
void writeFile(const char* path, const void* data, size_t size);
|
||||
#endif
|
||||
|
||||
#if defined (ANGLE_PLATFORM_WINDOWS)
|
||||
void ScheduleYield();
|
||||
#endif
|
||||
|
||||
#endif // COMMON_UTILITIES_H_
|
||||
55
gfx/angle/src/common/utilities_unittest.cpp
Normal file
55
gfx/angle/src/common/utilities_unittest.cpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// 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.
|
||||
|
||||
// utilities_unittest.cpp: Unit tests for ANGLE's GL utility functions
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "common/utilities.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
TEST(ParseUniformName, ArrayIndex)
|
||||
{
|
||||
size_t index;
|
||||
EXPECT_EQ("foo", gl::ParseUniformName("foo[123]", &index));
|
||||
EXPECT_EQ(123u, index);
|
||||
|
||||
EXPECT_EQ("bar", gl::ParseUniformName("bar[0]", &index));
|
||||
EXPECT_EQ(0u, index);
|
||||
}
|
||||
|
||||
TEST(ParseUniformName, NegativeArrayIndex)
|
||||
{
|
||||
size_t index;
|
||||
EXPECT_EQ("foo", gl::ParseUniformName("foo[-1]", &index));
|
||||
EXPECT_EQ(GL_INVALID_INDEX, index);
|
||||
}
|
||||
|
||||
TEST(ParseUniformName, NoArrayIndex)
|
||||
{
|
||||
size_t index;
|
||||
EXPECT_EQ("foo", gl::ParseUniformName("foo", &index));
|
||||
EXPECT_EQ(GL_INVALID_INDEX, index);
|
||||
}
|
||||
|
||||
TEST(ParseUniformName, NULLArrayIndex)
|
||||
{
|
||||
EXPECT_EQ("foo", gl::ParseUniformName("foo[10]", nullptr));
|
||||
}
|
||||
|
||||
TEST(ParseUniformName, TrailingWhitespace)
|
||||
{
|
||||
size_t index;
|
||||
EXPECT_EQ("foo ", gl::ParseUniformName("foo ", &index));
|
||||
EXPECT_EQ(GL_INVALID_INDEX, index);
|
||||
|
||||
EXPECT_EQ("foo[10] ", gl::ParseUniformName("foo[10] ", &index));
|
||||
EXPECT_EQ(GL_INVALID_INDEX, index);
|
||||
}
|
||||
|
||||
}
|
||||
28
gfx/angle/src/common/version.h
Normal file
28
gfx/angle/src/common/version.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
#ifndef COMMON_VERSION_H_
|
||||
#define COMMON_VERSION_H_
|
||||
|
||||
#include "commit.h"
|
||||
|
||||
#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_
|
||||
336
gfx/angle/src/compiler.gypi
Normal file
336
gfx/angle/src/compiler.gypi
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
# Copyright (c) 2013 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.
|
||||
|
||||
{
|
||||
'variables':
|
||||
{
|
||||
# These file lists are shared with the GN build.
|
||||
'angle_translator_sources':
|
||||
[
|
||||
'../include/EGL/egl.h',
|
||||
'../include/EGL/eglext.h',
|
||||
'../include/EGL/eglplatform.h',
|
||||
'../include/GLES2/gl2.h',
|
||||
'../include/GLES2/gl2ext.h',
|
||||
'../include/GLES2/gl2platform.h',
|
||||
'../include/GLES3/gl3.h',
|
||||
'../include/GLES3/gl3platform.h',
|
||||
'../include/GLES3/gl31.h',
|
||||
'../include/GLES3/gl32.h',
|
||||
'../include/GLSLANG/ShaderLang.h',
|
||||
'../include/GLSLANG/ShaderVars.h',
|
||||
'../include/KHR/khrplatform.h',
|
||||
'../include/angle_gl.h',
|
||||
'compiler/translator/AddAndTrueToLoopCondition.cpp',
|
||||
'compiler/translator/AddAndTrueToLoopCondition.h',
|
||||
'compiler/translator/BaseTypes.h',
|
||||
'compiler/translator/BuiltInFunctionEmulator.cpp',
|
||||
'compiler/translator/BuiltInFunctionEmulator.h',
|
||||
'compiler/translator/BreakVariableAliasingInInnerLoops.cpp',
|
||||
'compiler/translator/BreakVariableAliasingInInnerLoops.h',
|
||||
'compiler/translator/Cache.cpp',
|
||||
'compiler/translator/Cache.h',
|
||||
'compiler/translator/CallDAG.cpp',
|
||||
'compiler/translator/CallDAG.h',
|
||||
'compiler/translator/CodeGen.cpp',
|
||||
'compiler/translator/Common.h',
|
||||
'compiler/translator/Compiler.cpp',
|
||||
'compiler/translator/Compiler.h',
|
||||
'compiler/translator/ConstantUnion.cpp',
|
||||
'compiler/translator/ConstantUnion.h',
|
||||
'compiler/translator/DeferGlobalInitializers.cpp',
|
||||
'compiler/translator/DeferGlobalInitializers.h',
|
||||
'compiler/translator/Diagnostics.cpp',
|
||||
'compiler/translator/Diagnostics.h',
|
||||
'compiler/translator/DirectiveHandler.cpp',
|
||||
'compiler/translator/DirectiveHandler.h',
|
||||
'compiler/translator/EmulateGLFragColorBroadcast.cpp',
|
||||
'compiler/translator/EmulateGLFragColorBroadcast.h',
|
||||
'compiler/translator/EmulatePrecision.cpp',
|
||||
'compiler/translator/EmulatePrecision.h',
|
||||
'compiler/translator/ExpandIntegerPowExpressions.cpp',
|
||||
'compiler/translator/ExpandIntegerPowExpressions.h',
|
||||
'compiler/translator/ExtensionBehavior.h',
|
||||
'compiler/translator/FlagStd140Structs.cpp',
|
||||
'compiler/translator/FlagStd140Structs.h',
|
||||
'compiler/translator/ForLoopUnroll.cpp',
|
||||
'compiler/translator/ForLoopUnroll.h',
|
||||
'compiler/translator/HashNames.h',
|
||||
'compiler/translator/InfoSink.cpp',
|
||||
'compiler/translator/InfoSink.h',
|
||||
'compiler/translator/Initialize.cpp',
|
||||
'compiler/translator/Initialize.h',
|
||||
'compiler/translator/InitializeDll.cpp',
|
||||
'compiler/translator/InitializeDll.h',
|
||||
'compiler/translator/InitializeGlobals.h',
|
||||
'compiler/translator/InitializeParseContext.cpp',
|
||||
'compiler/translator/InitializeParseContext.h',
|
||||
'compiler/translator/InitializeVariables.cpp',
|
||||
'compiler/translator/InitializeVariables.h',
|
||||
'compiler/translator/IntermNode.h',
|
||||
'compiler/translator/IntermNode.cpp',
|
||||
'compiler/translator/IntermTraverse.cpp',
|
||||
'compiler/translator/Intermediate.h',
|
||||
'compiler/translator/Intermediate.cpp',
|
||||
'compiler/translator/LoopInfo.cpp',
|
||||
'compiler/translator/LoopInfo.h',
|
||||
'compiler/translator/MMap.h',
|
||||
'compiler/translator/NodeSearch.h',
|
||||
'compiler/translator/Operator.cpp',
|
||||
'compiler/translator/Operator.h',
|
||||
'compiler/translator/ParseContext.cpp',
|
||||
'compiler/translator/ParseContext.h',
|
||||
'compiler/translator/PoolAlloc.cpp',
|
||||
'compiler/translator/PoolAlloc.h',
|
||||
'compiler/translator/Pragma.h',
|
||||
'compiler/translator/PruneEmptyDeclarations.cpp',
|
||||
'compiler/translator/PruneEmptyDeclarations.h',
|
||||
'compiler/translator/QualifierTypes.h',
|
||||
'compiler/translator/QualifierTypes.cpp',
|
||||
'compiler/translator/RecordConstantPrecision.cpp',
|
||||
'compiler/translator/RecordConstantPrecision.h',
|
||||
'compiler/translator/RegenerateStructNames.cpp',
|
||||
'compiler/translator/RegenerateStructNames.h',
|
||||
'compiler/translator/RemoveInvariantDeclaration.cpp',
|
||||
'compiler/translator/RemoveInvariantDeclaration.h',
|
||||
'compiler/translator/RemovePow.cpp',
|
||||
'compiler/translator/RemovePow.h',
|
||||
'compiler/translator/RewriteDoWhile.cpp',
|
||||
'compiler/translator/RewriteDoWhile.h',
|
||||
'compiler/translator/RewriteTexelFetchOffset.cpp',
|
||||
'compiler/translator/RewriteTexelFetchOffset.h',
|
||||
'compiler/translator/RewriteUnaryMinusOperatorInt.cpp',
|
||||
'compiler/translator/RewriteUnaryMinusOperatorInt.h',
|
||||
'compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp',
|
||||
'compiler/translator/ScalarizeVecAndMatConstructorArgs.h',
|
||||
'compiler/translator/SearchSymbol.cpp',
|
||||
'compiler/translator/SearchSymbol.h',
|
||||
'compiler/translator/ShaderLang.cpp',
|
||||
'compiler/translator/ShaderVars.cpp',
|
||||
'compiler/translator/SymbolTable.cpp',
|
||||
'compiler/translator/SymbolTable.h',
|
||||
'compiler/translator/Types.cpp',
|
||||
'compiler/translator/Types.h',
|
||||
'compiler/translator/UnfoldShortCircuitAST.cpp',
|
||||
'compiler/translator/UnfoldShortCircuitAST.h',
|
||||
'compiler/translator/UseInterfaceBlockFields.cpp',
|
||||
'compiler/translator/UseInterfaceBlockFields.h',
|
||||
'compiler/translator/ValidateGlobalInitializer.cpp',
|
||||
'compiler/translator/ValidateGlobalInitializer.h',
|
||||
'compiler/translator/ValidateLimitations.cpp',
|
||||
'compiler/translator/ValidateLimitations.h',
|
||||
'compiler/translator/ValidateMaxParameters.h',
|
||||
'compiler/translator/ValidateMaxParameters.cpp',
|
||||
'compiler/translator/ValidateOutputs.cpp',
|
||||
'compiler/translator/ValidateOutputs.h',
|
||||
'compiler/translator/ValidateSwitch.cpp',
|
||||
'compiler/translator/ValidateSwitch.h',
|
||||
'compiler/translator/VariableInfo.cpp',
|
||||
'compiler/translator/VariableInfo.h',
|
||||
'compiler/translator/VariablePacker.cpp',
|
||||
'compiler/translator/VariablePacker.h',
|
||||
'compiler/translator/blocklayout.cpp',
|
||||
'compiler/translator/blocklayout.h',
|
||||
'compiler/translator/glslang.h',
|
||||
'compiler/translator/glslang.l',
|
||||
'compiler/translator/glslang.y',
|
||||
'compiler/translator/glslang_lex.cpp',
|
||||
'compiler/translator/glslang_tab.cpp',
|
||||
'compiler/translator/glslang_tab.h',
|
||||
'compiler/translator/intermOut.cpp',
|
||||
'compiler/translator/length_limits.h',
|
||||
'compiler/translator/util.cpp',
|
||||
'compiler/translator/util.h',
|
||||
'third_party/compiler/ArrayBoundsClamper.cpp',
|
||||
'third_party/compiler/ArrayBoundsClamper.h',
|
||||
],
|
||||
'angle_translator_essl_sources':
|
||||
[
|
||||
'compiler/translator/OutputESSL.cpp',
|
||||
'compiler/translator/OutputESSL.h',
|
||||
'compiler/translator/TranslatorESSL.cpp',
|
||||
'compiler/translator/TranslatorESSL.h',
|
||||
],
|
||||
'angle_translator_glsl_sources':
|
||||
[
|
||||
'compiler/translator/BuiltInFunctionEmulatorGLSL.cpp',
|
||||
'compiler/translator/BuiltInFunctionEmulatorGLSL.h',
|
||||
'compiler/translator/ExtensionGLSL.cpp',
|
||||
'compiler/translator/ExtensionGLSL.h',
|
||||
'compiler/translator/OutputGLSL.cpp',
|
||||
'compiler/translator/OutputGLSL.h',
|
||||
'compiler/translator/OutputGLSLBase.cpp',
|
||||
'compiler/translator/OutputGLSLBase.h',
|
||||
'compiler/translator/TranslatorGLSL.cpp',
|
||||
'compiler/translator/TranslatorGLSL.h',
|
||||
'compiler/translator/VersionGLSL.cpp',
|
||||
'compiler/translator/VersionGLSL.h',
|
||||
],
|
||||
'angle_translator_hlsl_sources':
|
||||
[
|
||||
'compiler/translator/AddDefaultReturnStatements.cpp',
|
||||
'compiler/translator/AddDefaultReturnStatements.h',
|
||||
'compiler/translator/ArrayReturnValueToOutParameter.cpp',
|
||||
'compiler/translator/ArrayReturnValueToOutParameter.h',
|
||||
'compiler/translator/ASTMetadataHLSL.cpp',
|
||||
'compiler/translator/ASTMetadataHLSL.h',
|
||||
'compiler/translator/blocklayoutHLSL.cpp',
|
||||
'compiler/translator/blocklayoutHLSL.h',
|
||||
'compiler/translator/BuiltInFunctionEmulatorHLSL.cpp',
|
||||
'compiler/translator/BuiltInFunctionEmulatorHLSL.h',
|
||||
'compiler/translator/IntermNodePatternMatcher.cpp',
|
||||
'compiler/translator/IntermNodePatternMatcher.h',
|
||||
'compiler/translator/OutputHLSL.cpp',
|
||||
'compiler/translator/OutputHLSL.h',
|
||||
'compiler/translator/RemoveDynamicIndexing.cpp',
|
||||
'compiler/translator/RemoveDynamicIndexing.h',
|
||||
'compiler/translator/RemoveSwitchFallThrough.cpp',
|
||||
'compiler/translator/RemoveSwitchFallThrough.h',
|
||||
'compiler/translator/RewriteElseBlocks.cpp',
|
||||
'compiler/translator/RewriteElseBlocks.h',
|
||||
'compiler/translator/SeparateArrayInitialization.cpp',
|
||||
'compiler/translator/SeparateArrayInitialization.h',
|
||||
'compiler/translator/SeparateDeclarations.cpp',
|
||||
'compiler/translator/SeparateDeclarations.h',
|
||||
'compiler/translator/SeparateExpressionsReturningArrays.cpp',
|
||||
'compiler/translator/SeparateExpressionsReturningArrays.h',
|
||||
'compiler/translator/SimplifyLoopConditions.cpp',
|
||||
'compiler/translator/SimplifyLoopConditions.h',
|
||||
'compiler/translator/SplitSequenceOperator.cpp',
|
||||
'compiler/translator/SplitSequenceOperator.h',
|
||||
'compiler/translator/StructureHLSL.cpp',
|
||||
'compiler/translator/StructureHLSL.h',
|
||||
'compiler/translator/TextureFunctionHLSL.cpp',
|
||||
'compiler/translator/TextureFunctionHLSL.h',
|
||||
'compiler/translator/TranslatorHLSL.cpp',
|
||||
'compiler/translator/TranslatorHLSL.h',
|
||||
'compiler/translator/UnfoldShortCircuitToIf.cpp',
|
||||
'compiler/translator/UnfoldShortCircuitToIf.h',
|
||||
'compiler/translator/UniformHLSL.cpp',
|
||||
'compiler/translator/UniformHLSL.h',
|
||||
'compiler/translator/UtilsHLSL.cpp',
|
||||
'compiler/translator/UtilsHLSL.h',
|
||||
],
|
||||
'angle_preprocessor_sources':
|
||||
[
|
||||
'compiler/preprocessor/DiagnosticsBase.cpp',
|
||||
'compiler/preprocessor/DiagnosticsBase.h',
|
||||
'compiler/preprocessor/DirectiveHandlerBase.cpp',
|
||||
'compiler/preprocessor/DirectiveHandlerBase.h',
|
||||
'compiler/preprocessor/DirectiveParser.cpp',
|
||||
'compiler/preprocessor/DirectiveParser.h',
|
||||
'compiler/preprocessor/ExpressionParser.cpp',
|
||||
'compiler/preprocessor/ExpressionParser.h',
|
||||
'compiler/preprocessor/ExpressionParser.y',
|
||||
'compiler/preprocessor/Input.cpp',
|
||||
'compiler/preprocessor/Input.h',
|
||||
'compiler/preprocessor/Lexer.cpp',
|
||||
'compiler/preprocessor/Lexer.h',
|
||||
'compiler/preprocessor/Macro.cpp',
|
||||
'compiler/preprocessor/Macro.h',
|
||||
'compiler/preprocessor/MacroExpander.cpp',
|
||||
'compiler/preprocessor/MacroExpander.h',
|
||||
'compiler/preprocessor/Preprocessor.cpp',
|
||||
'compiler/preprocessor/Preprocessor.h',
|
||||
'compiler/preprocessor/SourceLocation.h',
|
||||
'compiler/preprocessor/Token.cpp',
|
||||
'compiler/preprocessor/Token.h',
|
||||
'compiler/preprocessor/Tokenizer.cpp',
|
||||
'compiler/preprocessor/Tokenizer.h',
|
||||
'compiler/preprocessor/Tokenizer.l',
|
||||
'compiler/preprocessor/numeric_lex.h',
|
||||
],
|
||||
},
|
||||
# Everything below this is duplicated in the GN build. If you change
|
||||
# anything also change angle/BUILD.gn
|
||||
'targets':
|
||||
[
|
||||
{
|
||||
'target_name': 'preprocessor',
|
||||
'type': 'static_library',
|
||||
'dependencies': [ 'angle_common' ],
|
||||
'includes': [ '../gyp/common_defines.gypi', ],
|
||||
'sources': [ '<@(angle_preprocessor_sources)', ],
|
||||
},
|
||||
{
|
||||
'target_name': 'translator',
|
||||
'type': 'static_library',
|
||||
'dependencies': [ 'preprocessor', 'angle_common' ],
|
||||
'includes': [ '../gyp/common_defines.gypi', ],
|
||||
'include_dirs':
|
||||
[
|
||||
'.',
|
||||
'../include',
|
||||
],
|
||||
'sources':
|
||||
[
|
||||
'<@(angle_translator_sources)',
|
||||
],
|
||||
'msvs_settings':
|
||||
{
|
||||
'VCLibrarianTool':
|
||||
{
|
||||
'AdditionalOptions': ['/ignore:4221']
|
||||
},
|
||||
},
|
||||
'conditions':
|
||||
[
|
||||
['angle_enable_essl==1',
|
||||
{
|
||||
'defines':
|
||||
[
|
||||
'ANGLE_ENABLE_ESSL',
|
||||
],
|
||||
'direct_dependent_settings':
|
||||
{
|
||||
'defines':
|
||||
[
|
||||
'ANGLE_ENABLE_ESSL',
|
||||
],
|
||||
},
|
||||
'sources':
|
||||
[
|
||||
'<@(angle_translator_essl_sources)',
|
||||
],
|
||||
}],
|
||||
['angle_enable_glsl==1',
|
||||
{
|
||||
'defines':
|
||||
[
|
||||
'ANGLE_ENABLE_GLSL',
|
||||
],
|
||||
'direct_dependent_settings':
|
||||
{
|
||||
'defines':
|
||||
[
|
||||
'ANGLE_ENABLE_GLSL',
|
||||
],
|
||||
},
|
||||
'sources':
|
||||
[
|
||||
'<@(angle_translator_glsl_sources)',
|
||||
],
|
||||
}],
|
||||
['angle_enable_hlsl==1',
|
||||
{
|
||||
'defines':
|
||||
[
|
||||
'ANGLE_ENABLE_HLSL',
|
||||
],
|
||||
'direct_dependent_settings':
|
||||
{
|
||||
'defines':
|
||||
[
|
||||
'ANGLE_ENABLE_HLSL',
|
||||
],
|
||||
},
|
||||
'sources':
|
||||
[
|
||||
'<@(angle_translator_hlsl_sources)',
|
||||
],
|
||||
}],
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
161
gfx/angle/src/compiler/fuzz/translator_fuzzer.cpp
Normal file
161
gfx/angle/src/compiler/fuzz/translator_fuzzer.cpp
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
//
|
||||
// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
// translator_fuzzer.cpp: A libfuzzer fuzzer for the shader translator.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
|
||||
#include "compiler/translator/Compiler.h"
|
||||
#include "angle_gl.h"
|
||||
|
||||
using namespace sh;
|
||||
|
||||
struct TranslatorCacheKey
|
||||
{
|
||||
bool operator==(const TranslatorCacheKey &other) const
|
||||
{
|
||||
return type == other.type && spec == other.spec && output == other.output;
|
||||
}
|
||||
|
||||
uint32_t type = 0;
|
||||
uint32_t spec = 0;
|
||||
uint32_t output = 0;
|
||||
};
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <>
|
||||
struct hash<TranslatorCacheKey>
|
||||
{
|
||||
std::size_t operator()(const TranslatorCacheKey &k) const
|
||||
{
|
||||
return (hash<uint32_t>()(k.type) << 1) ^ (hash<uint32_t>()(k.spec) >> 1) ^
|
||||
hash<uint32_t>()(k.output);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
static std::unordered_map<TranslatorCacheKey, TCompiler *> translators;
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
// Reserve some size for future compile options
|
||||
const size_t kHeaderSize = 128;
|
||||
|
||||
if (size <= kHeaderSize)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Make sure the rest of data will be a valid C string so that we don't have to copy it.
|
||||
if (data[size - 1] != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t type = *reinterpret_cast<const uint32_t *>(data);
|
||||
uint32_t spec = *reinterpret_cast<const uint32_t *>(data + 4);
|
||||
uint32_t output = *reinterpret_cast<const uint32_t *>(data + 8);
|
||||
uint64_t options = *reinterpret_cast<const uint64_t *>(data + 12);
|
||||
|
||||
if (type != GL_FRAGMENT_SHADER && type != GL_VERTEX_SHADER)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (spec != SH_GLES2_SPEC && type != SH_WEBGL_SPEC && spec != SH_GLES3_SPEC &&
|
||||
spec != SH_WEBGL2_SPEC)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> validOutputs;
|
||||
validOutputs.push_back(SH_ESSL_OUTPUT);
|
||||
validOutputs.push_back(SH_GLSL_COMPATIBILITY_OUTPUT);
|
||||
validOutputs.push_back(SH_GLSL_130_OUTPUT);
|
||||
validOutputs.push_back(SH_GLSL_140_OUTPUT);
|
||||
validOutputs.push_back(SH_GLSL_150_CORE_OUTPUT);
|
||||
validOutputs.push_back(SH_GLSL_330_CORE_OUTPUT);
|
||||
validOutputs.push_back(SH_GLSL_400_CORE_OUTPUT);
|
||||
validOutputs.push_back(SH_GLSL_410_CORE_OUTPUT);
|
||||
validOutputs.push_back(SH_GLSL_420_CORE_OUTPUT);
|
||||
validOutputs.push_back(SH_GLSL_430_CORE_OUTPUT);
|
||||
validOutputs.push_back(SH_GLSL_440_CORE_OUTPUT);
|
||||
validOutputs.push_back(SH_GLSL_450_CORE_OUTPUT);
|
||||
validOutputs.push_back(SH_HLSL_3_0_OUTPUT);
|
||||
validOutputs.push_back(SH_HLSL_4_1_OUTPUT);
|
||||
validOutputs.push_back(SH_HLSL_4_0_FL9_3_OUTPUT);
|
||||
bool found = false;
|
||||
for (auto valid : validOutputs)
|
||||
{
|
||||
found = found || (valid == output);
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size -= kHeaderSize;
|
||||
data += kHeaderSize;
|
||||
|
||||
if (!ShInitialize())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
TranslatorCacheKey key;
|
||||
key.type = type;
|
||||
key.spec = spec;
|
||||
key.output = output;
|
||||
|
||||
if (translators.find(key) == translators.end())
|
||||
{
|
||||
TCompiler *translator = ConstructCompiler(type, static_cast<ShShaderSpec>(spec),
|
||||
static_cast<ShShaderOutput>(output));
|
||||
|
||||
if (!translator)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ShBuiltInResources resources;
|
||||
ShInitBuiltInResources(&resources);
|
||||
|
||||
// Enable all the extensions to have more coverage
|
||||
resources.OES_standard_derivatives = 1;
|
||||
resources.OES_EGL_image_external = 1;
|
||||
resources.OES_EGL_image_external_essl3 = 1;
|
||||
resources.NV_EGL_stream_consumer_external = 1;
|
||||
resources.ARB_texture_rectangle = 1;
|
||||
resources.EXT_blend_func_extended = 1;
|
||||
resources.EXT_draw_buffers = 1;
|
||||
resources.EXT_frag_depth = 1;
|
||||
resources.EXT_shader_texture_lod = 1;
|
||||
resources.WEBGL_debug_shader_precision = 1;
|
||||
resources.EXT_shader_framebuffer_fetch = 1;
|
||||
resources.NV_shader_framebuffer_fetch = 1;
|
||||
resources.ARM_shader_framebuffer_fetch = 1;
|
||||
|
||||
if (!translator->Init(resources))
|
||||
{
|
||||
DeleteCompiler(translator);
|
||||
return 0;
|
||||
}
|
||||
|
||||
translators[key] = translator;
|
||||
}
|
||||
|
||||
TCompiler *translator = translators[key];
|
||||
|
||||
const char *shaderStrings[] = {reinterpret_cast<const char *>(data)};
|
||||
translator->compile(shaderStrings, 1, options);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
diff --git a/src/compiler/preprocessor/Tokenizer.cpp b/src/compiler/preprocessor/Tokenizer.cpp
|
||||
index 0d7ad58..5ef0e5e 100644
|
||||
--- a/src/compiler/preprocessor/Tokenizer.cpp
|
||||
+++ b/src/compiler/preprocessor/Tokenizer.cpp
|
||||
@@ -1703,7 +1703,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
|
||||
else
|
||||
{
|
||||
int num_to_read =
|
||||
- YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
|
||||
+ static_cast<int>(YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1);
|
||||
|
||||
while ( num_to_read <= 0 )
|
||||
{ /* Not enough room in the buffer - grow it. */
|
||||
@@ -1737,8 +1737,8 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
|
||||
|
||||
yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
|
||||
|
||||
- num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
|
||||
- number_to_move - 1;
|
||||
+ num_to_read = static_cast<int>(YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
|
||||
+ number_to_move - 1);
|
||||
|
||||
}
|
||||
|
||||
@@ -1746,8 +1746,10 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
|
||||
num_to_read = YY_READ_BUF_SIZE;
|
||||
|
||||
/* Read in more data. */
|
||||
+ yy_size_t ret = 0;
|
||||
YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
|
||||
- yyg->yy_n_chars, num_to_read );
|
||||
+ ret, num_to_read );
|
||||
+ yyg->yy_n_chars = static_cast<int>(ret);
|
||||
|
||||
YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
|
||||
}
|
||||
@@ -1773,13 +1775,13 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
|
||||
|
||||
if ((int) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
|
||||
/* Extend the array by 50%, plus the number we really need. */
|
||||
- int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
|
||||
+ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
|
||||
YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) pprealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
|
||||
if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
|
||||
YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
|
||||
}
|
||||
|
||||
- yyg->yy_n_chars += number_to_move;
|
||||
+ yyg->yy_n_chars += static_cast<int>(number_to_move);
|
||||
YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
|
||||
YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
|
||||
|
||||
@@ -2171,7 +2173,7 @@ void pppop_buffer_state (yyscan_t yyscanner)
|
||||
*/
|
||||
static void ppensure_buffer_stack (yyscan_t yyscanner)
|
||||
{
|
||||
- int num_to_alloc;
|
||||
+ yy_size_t num_to_alloc;
|
||||
struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
|
||||
|
||||
if (!yyg->yy_buffer_stack) {
|
||||
@@ -2238,7 +2240,7 @@ YY_BUFFER_STATE pp_scan_buffer (char * base, yy_size_t size , yyscan_t yyscann
|
||||
if ( ! b )
|
||||
YY_FATAL_ERROR( "out of dynamic memory in pp_scan_buffer()" );
|
||||
|
||||
- b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
|
||||
+ b->yy_buf_size = static_cast<int>(size - 2); /* "- 2" to take care of EOB's */
|
||||
b->yy_buf_pos = b->yy_ch_buf = base;
|
||||
b->yy_is_our_buffer = 0;
|
||||
b->yy_input_file = NULL;
|
||||
@@ -2293,7 +2295,7 @@ YY_BUFFER_STATE pp_scan_bytes (yyconst char * yybytes, int _yybytes_len , yysc
|
||||
if ( ! buf )
|
||||
YY_FATAL_ERROR( "out of dynamic memory in pp_scan_bytes()" );
|
||||
|
||||
- for ( i = 0; i < _yybytes_len; ++i )
|
||||
+ for ( i = 0; i < static_cast<yy_size_t>(_yybytes_len); ++i )
|
||||
buf[i] = yybytes[i];
|
||||
|
||||
buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
|
||||
141
gfx/angle/src/compiler/preprocessor/DiagnosticsBase.cpp
Normal file
141
gfx/angle/src/compiler/preprocessor/DiagnosticsBase.cpp
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
#include "compiler/preprocessor/DiagnosticsBase.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
Diagnostics::~Diagnostics()
|
||||
{
|
||||
}
|
||||
|
||||
void Diagnostics::report(ID id,
|
||||
const SourceLocation &loc,
|
||||
const std::string &text)
|
||||
{
|
||||
// TODO(alokp): Keep a count of errors and warnings.
|
||||
print(id, loc, text);
|
||||
}
|
||||
|
||||
Diagnostics::Severity Diagnostics::severity(ID id)
|
||||
{
|
||||
if ((id > PP_ERROR_BEGIN) && (id < PP_ERROR_END))
|
||||
return PP_ERROR;
|
||||
|
||||
if ((id > PP_WARNING_BEGIN) && (id < PP_WARNING_END))
|
||||
return PP_WARNING;
|
||||
|
||||
UNREACHABLE();
|
||||
return PP_ERROR;
|
||||
}
|
||||
|
||||
std::string Diagnostics::message(ID id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
// Errors begin.
|
||||
case PP_INTERNAL_ERROR:
|
||||
return "internal error";
|
||||
case PP_OUT_OF_MEMORY:
|
||||
return "out of memory";
|
||||
case PP_INVALID_CHARACTER:
|
||||
return "invalid character";
|
||||
case PP_INVALID_NUMBER:
|
||||
return "invalid number";
|
||||
case PP_INTEGER_OVERFLOW:
|
||||
return "integer overflow";
|
||||
case PP_FLOAT_OVERFLOW:
|
||||
return "float overflow";
|
||||
case PP_TOKEN_TOO_LONG:
|
||||
return "token too long";
|
||||
case PP_INVALID_EXPRESSION:
|
||||
return "invalid expression";
|
||||
case PP_DIVISION_BY_ZERO:
|
||||
return "division by zero";
|
||||
case PP_EOF_IN_COMMENT:
|
||||
return "unexpected end of file found in comment";
|
||||
case PP_UNEXPECTED_TOKEN:
|
||||
return "unexpected token";
|
||||
case PP_DIRECTIVE_INVALID_NAME:
|
||||
return "invalid directive name";
|
||||
case PP_MACRO_NAME_RESERVED:
|
||||
return "macro name is reserved";
|
||||
case PP_MACRO_REDEFINED:
|
||||
return "macro redefined";
|
||||
case PP_MACRO_PREDEFINED_REDEFINED:
|
||||
return "predefined macro redefined";
|
||||
case PP_MACRO_PREDEFINED_UNDEFINED:
|
||||
return "predefined macro undefined";
|
||||
case PP_MACRO_UNTERMINATED_INVOCATION:
|
||||
return "unterminated macro invocation";
|
||||
case PP_MACRO_UNDEFINED_WHILE_INVOKED:
|
||||
return "macro undefined while being invoked";
|
||||
case PP_MACRO_TOO_FEW_ARGS:
|
||||
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:
|
||||
return "unexpected #else found without a matching #if";
|
||||
case PP_CONDITIONAL_ELSE_AFTER_ELSE:
|
||||
return "unexpected #else found after another #else";
|
||||
case PP_CONDITIONAL_ELIF_WITHOUT_IF:
|
||||
return "unexpected #elif found without a matching #if";
|
||||
case PP_CONDITIONAL_ELIF_AFTER_ELSE:
|
||||
return "unexpected #elif found after #else";
|
||||
case PP_CONDITIONAL_UNTERMINATED:
|
||||
return "unexpected end of file found in conditional block";
|
||||
case PP_INVALID_EXTENSION_NAME:
|
||||
return "invalid extension name";
|
||||
case PP_INVALID_EXTENSION_BEHAVIOR:
|
||||
return "invalid extension behavior";
|
||||
case PP_INVALID_EXTENSION_DIRECTIVE:
|
||||
return "invalid extension directive";
|
||||
case PP_INVALID_VERSION_NUMBER:
|
||||
return "invalid version number";
|
||||
case PP_INVALID_VERSION_DIRECTIVE:
|
||||
return "invalid version directive";
|
||||
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";
|
||||
case PP_UNDEFINED_SHIFT:
|
||||
return "shift exponent is negative or undefined";
|
||||
// Errors end.
|
||||
// Warnings begin.
|
||||
case PP_EOF_IN_DIRECTIVE:
|
||||
return "unexpected end of file found in directive";
|
||||
case PP_CONDITIONAL_UNEXPECTED_TOKEN:
|
||||
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:
|
||||
UNREACHABLE();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pp
|
||||
95
gfx/angle/src/compiler/preprocessor/DiagnosticsBase.h
Normal file
95
gfx/angle/src/compiler/preprocessor/DiagnosticsBase.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_PREPROCESSOR_DIAGNOSTICSBASE_H_
|
||||
#define COMPILER_PREPROCESSOR_DIAGNOSTICSBASE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
struct SourceLocation;
|
||||
|
||||
// Base class for reporting diagnostic messages.
|
||||
// Derived classes are responsible for formatting and printing the messages.
|
||||
class Diagnostics
|
||||
{
|
||||
public:
|
||||
enum Severity
|
||||
{
|
||||
PP_ERROR,
|
||||
PP_WARNING
|
||||
};
|
||||
enum ID
|
||||
{
|
||||
PP_ERROR_BEGIN,
|
||||
PP_INTERNAL_ERROR,
|
||||
PP_OUT_OF_MEMORY,
|
||||
PP_INVALID_CHARACTER,
|
||||
PP_INVALID_NUMBER,
|
||||
PP_INTEGER_OVERFLOW,
|
||||
PP_FLOAT_OVERFLOW,
|
||||
PP_TOKEN_TOO_LONG,
|
||||
PP_INVALID_EXPRESSION,
|
||||
PP_DIVISION_BY_ZERO,
|
||||
PP_EOF_IN_COMMENT,
|
||||
PP_UNEXPECTED_TOKEN,
|
||||
PP_DIRECTIVE_INVALID_NAME,
|
||||
PP_MACRO_NAME_RESERVED,
|
||||
PP_MACRO_REDEFINED,
|
||||
PP_MACRO_PREDEFINED_REDEFINED,
|
||||
PP_MACRO_PREDEFINED_UNDEFINED,
|
||||
PP_MACRO_UNTERMINATED_INVOCATION,
|
||||
PP_MACRO_UNDEFINED_WHILE_INVOKED,
|
||||
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_UNDEFINED_SHIFT,
|
||||
PP_ERROR_END,
|
||||
|
||||
PP_WARNING_BEGIN,
|
||||
PP_EOF_IN_DIRECTIVE,
|
||||
PP_UNRECOGNIZED_PRAGMA,
|
||||
PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1,
|
||||
PP_WARNING_MACRO_NAME_RESERVED,
|
||||
PP_WARNING_END
|
||||
};
|
||||
|
||||
virtual ~Diagnostics();
|
||||
|
||||
void report(ID id, const SourceLocation &loc, const std::string &text);
|
||||
|
||||
protected:
|
||||
Severity severity(ID id);
|
||||
std::string message(ID id);
|
||||
|
||||
virtual void print(ID id,
|
||||
const SourceLocation &loc,
|
||||
const std::string &text) = 0;
|
||||
};
|
||||
|
||||
} // namespace pp
|
||||
|
||||
#endif // COMPILER_PREPROCESSOR_DIAGNOSTICSBASE_H_
|
||||
16
gfx/angle/src/compiler/preprocessor/DirectiveHandlerBase.cpp
Normal file
16
gfx/angle/src/compiler/preprocessor/DirectiveHandlerBase.cpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
#include "compiler/preprocessor/DirectiveHandlerBase.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
DirectiveHandler::~DirectiveHandler()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace pp
|
||||
45
gfx/angle/src/compiler/preprocessor/DirectiveHandlerBase.h
Normal file
45
gfx/angle/src/compiler/preprocessor/DirectiveHandlerBase.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_PREPROCESSOR_DIRECTIVEHANDLERBASE_H_
|
||||
#define COMPILER_PREPROCESSOR_DIRECTIVEHANDLERBASE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
struct SourceLocation;
|
||||
|
||||
// Base class for handling directives.
|
||||
// Preprocessor uses this class to notify the clients about certain
|
||||
// preprocessor directives. Derived classes are responsible for
|
||||
// handling them in an appropriate manner.
|
||||
class DirectiveHandler
|
||||
{
|
||||
public:
|
||||
virtual ~DirectiveHandler();
|
||||
|
||||
virtual void handleError(const SourceLocation &loc,
|
||||
const std::string &msg) = 0;
|
||||
|
||||
// Handle pragma of form: #pragma name[(value)]
|
||||
virtual void handlePragma(const SourceLocation &loc,
|
||||
const std::string &name,
|
||||
const std::string &value,
|
||||
bool stdgl) = 0;
|
||||
|
||||
virtual void handleExtension(const SourceLocation &loc,
|
||||
const std::string &name,
|
||||
const std::string &behavior) = 0;
|
||||
|
||||
virtual void handleVersion(const SourceLocation &loc,
|
||||
int version) = 0;
|
||||
};
|
||||
|
||||
} // namespace pp
|
||||
|
||||
#endif // COMPILER_PREPROCESSOR_DIRECTIVEHANDLERBASE_H_
|
||||
1004
gfx/angle/src/compiler/preprocessor/DirectiveParser.cpp
Normal file
1004
gfx/angle/src/compiler/preprocessor/DirectiveParser.cpp
Normal file
File diff suppressed because it is too large
Load diff
83
gfx/angle/src/compiler/preprocessor/DirectiveParser.h
Normal file
83
gfx/angle/src/compiler/preprocessor/DirectiveParser.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_PREPROCESSOR_DIRECTIVEPARSER_H_
|
||||
#define COMPILER_PREPROCESSOR_DIRECTIVEPARSER_H_
|
||||
|
||||
#include "compiler/preprocessor/Lexer.h"
|
||||
#include "compiler/preprocessor/Macro.h"
|
||||
#include "compiler/preprocessor/SourceLocation.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
class Diagnostics;
|
||||
class DirectiveHandler;
|
||||
class Tokenizer;
|
||||
|
||||
class DirectiveParser : public Lexer
|
||||
{
|
||||
public:
|
||||
DirectiveParser(Tokenizer *tokenizer,
|
||||
MacroSet *macroSet,
|
||||
Diagnostics *diagnostics,
|
||||
DirectiveHandler *directiveHandler);
|
||||
|
||||
void lex(Token *token) override;
|
||||
|
||||
private:
|
||||
|
||||
void parseDirective(Token *token);
|
||||
void parseDefine(Token *token);
|
||||
void parseUndef(Token *token);
|
||||
void parseIf(Token *token);
|
||||
void parseIfdef(Token *token);
|
||||
void parseIfndef(Token *token);
|
||||
void parseElse(Token *token);
|
||||
void parseElif(Token *token);
|
||||
void parseEndif(Token *token);
|
||||
void parseError(Token *token);
|
||||
void parsePragma(Token *token);
|
||||
void parseExtension(Token *token);
|
||||
void parseVersion(Token *token);
|
||||
void parseLine(Token *token);
|
||||
|
||||
bool skipping() const;
|
||||
void parseConditionalIf(Token *token);
|
||||
int parseExpressionIf(Token *token);
|
||||
int parseExpressionIfdef(Token *token);
|
||||
|
||||
struct ConditionalBlock
|
||||
{
|
||||
std::string type;
|
||||
SourceLocation location;
|
||||
bool skipBlock;
|
||||
bool skipGroup;
|
||||
bool foundValidGroup;
|
||||
bool foundElseGroup;
|
||||
|
||||
ConditionalBlock()
|
||||
: skipBlock(false),
|
||||
skipGroup(false),
|
||||
foundValidGroup(false),
|
||||
foundElseGroup(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
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
|
||||
|
||||
#endif // COMPILER_PREPROCESSOR_DIRECTIVEPARSER_H_
|
||||
2050
gfx/angle/src/compiler/preprocessor/ExpressionParser.cpp
Normal file
2050
gfx/angle/src/compiler/preprocessor/ExpressionParser.cpp
Normal file
File diff suppressed because it is too large
Load diff
43
gfx/angle/src/compiler/preprocessor/ExpressionParser.h
Normal file
43
gfx/angle/src/compiler/preprocessor/ExpressionParser.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_
|
||||
#define COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_
|
||||
|
||||
#include "common/angleutils.h"
|
||||
#include "compiler/preprocessor/DiagnosticsBase.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
class Lexer;
|
||||
struct Token;
|
||||
|
||||
class ExpressionParser : angle::NonCopyable
|
||||
{
|
||||
public:
|
||||
struct ErrorSettings
|
||||
{
|
||||
Diagnostics::ID unexpectedIdentifier;
|
||||
bool integerLiteralsMustFit32BitSignedRange;
|
||||
};
|
||||
|
||||
ExpressionParser(Lexer *lexer, Diagnostics *diagnostics);
|
||||
|
||||
bool parse(Token *token,
|
||||
int *result,
|
||||
bool parsePresetToken,
|
||||
const ErrorSettings &errorSettings,
|
||||
bool *valid);
|
||||
|
||||
private:
|
||||
Lexer *mLexer;
|
||||
Diagnostics *mDiagnostics;
|
||||
};
|
||||
|
||||
} // namespace pp
|
||||
|
||||
#endif // COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_
|
||||
465
gfx/angle/src/compiler/preprocessor/ExpressionParser.y
Normal file
465
gfx/angle/src/compiler/preprocessor/ExpressionParser.y
Normal file
|
|
@ -0,0 +1,465 @@
|
|||
/*
|
||||
//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
This file contains the Yacc grammar for GLSL ES preprocessor expression.
|
||||
|
||||
IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh,
|
||||
WHICH GENERATES THE GLSL ES preprocessor expression parser.
|
||||
*/
|
||||
|
||||
%{
|
||||
//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
// This file is auto-generated by generate_parser.sh. DO NOT EDIT!
|
||||
|
||||
#if defined(__GNUC__)
|
||||
// Triggered by the auto-generated pplval variable.
|
||||
#if !defined(__clang__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#else
|
||||
#pragma GCC diagnostic ignored "-Wuninitialized"
|
||||
#endif
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(disable: 4065 4244 4701 4702)
|
||||
#endif
|
||||
|
||||
#include "ExpressionParser.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "DiagnosticsBase.h"
|
||||
#include "Lexer.h"
|
||||
#include "Token.h"
|
||||
#include "common/mathutil.h"
|
||||
|
||||
typedef int32_t YYSTYPE;
|
||||
typedef uint32_t UNSIGNED_TYPE;
|
||||
|
||||
#define YYENABLE_NLS 0
|
||||
#define YYLTYPE_IS_TRIVIAL 1
|
||||
#define YYSTYPE_IS_TRIVIAL 1
|
||||
#define YYSTYPE_IS_DECLARED 1
|
||||
|
||||
namespace {
|
||||
struct Context
|
||||
{
|
||||
pp::Diagnostics* diagnostics;
|
||||
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
|
||||
%}
|
||||
|
||||
%pure-parser
|
||||
%name-prefix "pp"
|
||||
%parse-param {Context *context}
|
||||
%lex-param {Context *context}
|
||||
|
||||
%{
|
||||
static int yylex(YYSTYPE* lvalp, Context* context);
|
||||
static void yyerror(Context* context, const char* reason);
|
||||
%}
|
||||
|
||||
%token TOK_CONST_INT
|
||||
%token TOK_IDENTIFIER
|
||||
%left TOK_OP_OR
|
||||
%left TOK_OP_AND
|
||||
%left '|'
|
||||
%left '^'
|
||||
%left '&'
|
||||
%left TOK_OP_EQ TOK_OP_NE
|
||||
%left '<' '>' TOK_OP_LE TOK_OP_GE
|
||||
%left TOK_OP_LEFT TOK_OP_RIGHT
|
||||
%left '+' '-'
|
||||
%left '*' '/' '%'
|
||||
%right TOK_UNARY
|
||||
|
||||
%%
|
||||
|
||||
input
|
||||
: expression {
|
||||
*(context->result) = static_cast<int>($1);
|
||||
YYACCEPT;
|
||||
}
|
||||
;
|
||||
|
||||
expression
|
||||
: TOK_CONST_INT
|
||||
| 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_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;
|
||||
}
|
||||
| expression '^' expression {
|
||||
$$ = $1 ^ $3;
|
||||
}
|
||||
| expression '&' expression {
|
||||
$$ = $1 & $3;
|
||||
}
|
||||
| expression TOK_OP_NE expression {
|
||||
$$ = $1 != $3;
|
||||
}
|
||||
| expression TOK_OP_EQ expression {
|
||||
$$ = $1 == $3;
|
||||
}
|
||||
| expression TOK_OP_GE expression {
|
||||
$$ = $1 >= $3;
|
||||
}
|
||||
| expression TOK_OP_LE expression {
|
||||
$$ = $1 <= $3;
|
||||
}
|
||||
| expression '>' expression {
|
||||
$$ = $1 > $3;
|
||||
}
|
||||
| expression '<' expression {
|
||||
$$ = $1 < $3;
|
||||
}
|
||||
| expression TOK_OP_RIGHT expression {
|
||||
if ($3 < 0 || $3 > 31)
|
||||
{
|
||||
if (!context->isIgnoringErrors())
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << $1 << " >> " << $3;
|
||||
std::string text = stream.str();
|
||||
context->diagnostics->report(pp::Diagnostics::PP_UNDEFINED_SHIFT,
|
||||
context->token->location,
|
||||
text.c_str());
|
||||
*(context->valid) = false;
|
||||
}
|
||||
$$ = static_cast<YYSTYPE>(0);
|
||||
}
|
||||
else if ($1 < 0)
|
||||
{
|
||||
// Logical shift right.
|
||||
$$ = static_cast<YYSTYPE>(static_cast<UNSIGNED_TYPE>($1) >> $3);
|
||||
}
|
||||
else
|
||||
{
|
||||
$$ = $1 >> $3;
|
||||
}
|
||||
}
|
||||
| expression TOK_OP_LEFT expression {
|
||||
if ($3 < 0 || $3 > 31)
|
||||
{
|
||||
if (!context->isIgnoringErrors())
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << $1 << " << " << $3;
|
||||
std::string text = stream.str();
|
||||
context->diagnostics->report(pp::Diagnostics::PP_UNDEFINED_SHIFT,
|
||||
context->token->location,
|
||||
text.c_str());
|
||||
*(context->valid) = false;
|
||||
}
|
||||
$$ = static_cast<YYSTYPE>(0);
|
||||
}
|
||||
else if ($1 < 0)
|
||||
{
|
||||
// Logical shift left.
|
||||
$$ = static_cast<YYSTYPE>(static_cast<UNSIGNED_TYPE>($1) << $3);
|
||||
}
|
||||
else
|
||||
{
|
||||
$$ = $1 << $3;
|
||||
}
|
||||
}
|
||||
| expression '-' expression {
|
||||
$$ = gl::WrappingDiff<YYSTYPE>($1, $3);
|
||||
}
|
||||
| expression '+' expression {
|
||||
$$ = gl::WrappingSum<YYSTYPE>($1, $3);
|
||||
}
|
||||
| expression '%' expression {
|
||||
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 if (($1 == std::numeric_limits<YYSTYPE>::min()) && ($3 == -1))
|
||||
{
|
||||
// Check for the special case where the minimum representable number is
|
||||
// divided by -1. If left alone this has undefined results.
|
||||
$$ = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
$$ = $1 % $3;
|
||||
}
|
||||
}
|
||||
| expression '/' expression {
|
||||
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 if (($1 == std::numeric_limits<YYSTYPE>::min()) && ($3 == -1))
|
||||
{
|
||||
// Check for the special case where the minimum representable number is
|
||||
// divided by -1. If left alone this leads to integer overflow in C++, which
|
||||
// has undefined results.
|
||||
$$ = std::numeric_limits<YYSTYPE>::max();
|
||||
}
|
||||
else
|
||||
{
|
||||
$$ = $1 / $3;
|
||||
}
|
||||
}
|
||||
| expression '*' expression {
|
||||
$$ = gl::WrappingMul($1, $3);
|
||||
}
|
||||
| '!' expression %prec TOK_UNARY {
|
||||
$$ = ! $2;
|
||||
}
|
||||
| '~' expression %prec TOK_UNARY {
|
||||
$$ = ~ $2;
|
||||
}
|
||||
| '-' expression %prec TOK_UNARY {
|
||||
// Check for negation of minimum representable integer to prevent undefined signed int
|
||||
// overflow.
|
||||
if ($2 == std::numeric_limits<YYSTYPE>::min())
|
||||
{
|
||||
$$ = std::numeric_limits<YYSTYPE>::min();
|
||||
}
|
||||
else
|
||||
{
|
||||
$$ = -$2;
|
||||
}
|
||||
}
|
||||
| '+' expression %prec TOK_UNARY {
|
||||
$$ = + $2;
|
||||
}
|
||||
| '(' expression ')' {
|
||||
$$ = $2;
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
int yylex(YYSTYPE *lvalp, Context *context)
|
||||
{
|
||||
pp::Token *token = context->token;
|
||||
if (!context->parsePresetToken)
|
||||
{
|
||||
context->lexer->lex(token);
|
||||
}
|
||||
context->parsePresetToken = false;
|
||||
|
||||
int type = 0;
|
||||
|
||||
switch (token->type)
|
||||
{
|
||||
case pp::Token::CONST_INT: {
|
||||
unsigned int val = 0;
|
||||
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;
|
||||
case pp::Token::OP_AND:
|
||||
type = TOK_OP_AND;
|
||||
break;
|
||||
case pp::Token::OP_NE:
|
||||
type = TOK_OP_NE;
|
||||
break;
|
||||
case pp::Token::OP_EQ:
|
||||
type = TOK_OP_EQ;
|
||||
break;
|
||||
case pp::Token::OP_GE:
|
||||
type = TOK_OP_GE;
|
||||
break;
|
||||
case pp::Token::OP_LE:
|
||||
type = TOK_OP_LE;
|
||||
break;
|
||||
case pp::Token::OP_RIGHT:
|
||||
type = TOK_OP_RIGHT;
|
||||
break;
|
||||
case pp::Token::OP_LEFT:
|
||||
type = TOK_OP_LEFT;
|
||||
break;
|
||||
case '|':
|
||||
case '^':
|
||||
case '&':
|
||||
case '>':
|
||||
case '<':
|
||||
case '-':
|
||||
case '+':
|
||||
case '%':
|
||||
case '/':
|
||||
case '*':
|
||||
case '!':
|
||||
case '~':
|
||||
case '(':
|
||||
case ')':
|
||||
type = token->type;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
void yyerror(Context *context, const char *reason)
|
||||
{
|
||||
context->diagnostics->report(pp::Diagnostics::PP_INVALID_EXPRESSION,
|
||||
context->token->location,
|
||||
reason);
|
||||
}
|
||||
|
||||
namespace pp {
|
||||
|
||||
ExpressionParser::ExpressionParser(Lexer *lexer, Diagnostics *diagnostics)
|
||||
: mLexer(lexer),
|
||||
mDiagnostics(diagnostics)
|
||||
{
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
break;
|
||||
|
||||
case 2:
|
||||
mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token->location, "");
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
mDiagnostics->report(Diagnostics::PP_INTERNAL_ERROR, token->location, "");
|
||||
break;
|
||||
}
|
||||
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
} // namespace pp
|
||||
117
gfx/angle/src/compiler/preprocessor/Input.cpp
Normal file
117
gfx/angle/src/compiler/preprocessor/Input.cpp
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
//
|
||||
// Copyright (c) 2011 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.
|
||||
//
|
||||
|
||||
#include "compiler/preprocessor/Input.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
Input::Input() : mCount(0), mString(0)
|
||||
{
|
||||
}
|
||||
|
||||
Input::Input(size_t count, const char *const string[], const int length[]) :
|
||||
mCount(count),
|
||||
mString(string)
|
||||
{
|
||||
mLength.reserve(mCount);
|
||||
for (size_t i = 0; i < mCount; ++i)
|
||||
{
|
||||
int len = length ? length[i] : -1;
|
||||
mLength.push_back(len < 0 ? std::strlen(mString[i]) : len);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
// 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;
|
||||
|
||||
// Advance string if we reached the end of current string.
|
||||
if (mReadLoc.cIndex == mLength[mReadLoc.sIndex])
|
||||
{
|
||||
++mReadLoc.sIndex;
|
||||
mReadLoc.cIndex = 0;
|
||||
}
|
||||
}
|
||||
return nRead;
|
||||
}
|
||||
|
||||
} // namespace pp
|
||||
|
||||
66
gfx/angle/src/compiler/preprocessor/Input.h
Normal file
66
gfx/angle/src/compiler/preprocessor/Input.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// Copyright (c) 2011 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_PREPROCESSOR_INPUT_H_
|
||||
#define COMPILER_PREPROCESSOR_INPUT_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
// Holds and reads input for Lexer.
|
||||
class Input
|
||||
{
|
||||
public:
|
||||
Input();
|
||||
Input(size_t count, const char *const string[], const int length[]);
|
||||
|
||||
size_t count() const
|
||||
{
|
||||
return mCount;
|
||||
}
|
||||
const char *string(size_t index) const
|
||||
{
|
||||
return mString[index];
|
||||
}
|
||||
size_t length(size_t index) const
|
||||
{
|
||||
return mLength[index];
|
||||
}
|
||||
|
||||
size_t read(char *buf, size_t maxSize, int *lineNo);
|
||||
|
||||
struct Location
|
||||
{
|
||||
size_t sIndex; // String index;
|
||||
size_t cIndex; // Char index.
|
||||
|
||||
Location()
|
||||
: sIndex(0),
|
||||
cIndex(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
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;
|
||||
std::vector<size_t> mLength;
|
||||
|
||||
Location mReadLoc;
|
||||
};
|
||||
|
||||
} // namespace pp
|
||||
|
||||
#endif // COMPILER_PREPROCESSOR_INPUT_H_
|
||||
16
gfx/angle/src/compiler/preprocessor/Lexer.cpp
Normal file
16
gfx/angle/src/compiler/preprocessor/Lexer.cpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
#include "compiler/preprocessor/Lexer.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
Lexer::~Lexer()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace pp
|
||||
27
gfx/angle/src/compiler/preprocessor/Lexer.h
Normal file
27
gfx/angle/src/compiler/preprocessor/Lexer.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_PREPROCESSOR_LEXER_H_
|
||||
#define COMPILER_PREPROCESSOR_LEXER_H_
|
||||
|
||||
#include "common/angleutils.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
struct Token;
|
||||
|
||||
class Lexer : angle::NonCopyable
|
||||
{
|
||||
public:
|
||||
virtual ~Lexer();
|
||||
|
||||
virtual void lex(Token *token) = 0;
|
||||
};
|
||||
|
||||
} // namespace pp
|
||||
|
||||
#endif // COMPILER_PREPROCESSOR_LEXER_H_
|
||||
39
gfx/angle/src/compiler/preprocessor/Macro.cpp
Normal file
39
gfx/angle/src/compiler/preprocessor/Macro.cpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// Copyright (c) 2011 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.
|
||||
//
|
||||
|
||||
#include "compiler/preprocessor/Macro.h"
|
||||
|
||||
#include "common/angleutils.h"
|
||||
#include "compiler/preprocessor/Token.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
bool Macro::equals(const Macro &other) const
|
||||
{
|
||||
return (type == other.type) &&
|
||||
(name == other.name) &&
|
||||
(parameters == other.parameters) &&
|
||||
(replacements == other.replacements);
|
||||
}
|
||||
|
||||
void PredefineMacro(MacroSet *macroSet, const char *name, int value)
|
||||
{
|
||||
Token token;
|
||||
token.type = Token::CONST_INT;
|
||||
token.text = ToString(value);
|
||||
|
||||
Macro macro;
|
||||
macro.predefined = true;
|
||||
macro.type = Macro::kTypeObj;
|
||||
macro.name = name;
|
||||
macro.replacements.push_back(token);
|
||||
|
||||
(*macroSet)[name] = macro;
|
||||
}
|
||||
|
||||
} // namespace pp
|
||||
|
||||
48
gfx/angle/src/compiler/preprocessor/Macro.h
Normal file
48
gfx/angle/src/compiler/preprocessor/Macro.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_PREPROCESSOR_MACRO_H_
|
||||
#define COMPILER_PREPROCESSOR_MACRO_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
struct Token;
|
||||
|
||||
struct Macro
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
kTypeObj,
|
||||
kTypeFunc
|
||||
};
|
||||
typedef std::vector<std::string> Parameters;
|
||||
typedef std::vector<Token> Replacements;
|
||||
|
||||
Macro() : predefined(false), disabled(false), expansionCount(0), type(kTypeObj) {}
|
||||
bool equals(const Macro &other) const;
|
||||
|
||||
bool predefined;
|
||||
mutable bool disabled;
|
||||
mutable int expansionCount;
|
||||
|
||||
Type type;
|
||||
std::string name;
|
||||
Parameters parameters;
|
||||
Replacements replacements;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, Macro> MacroSet;
|
||||
|
||||
void PredefineMacro(MacroSet *macroSet, const char *name, int value);
|
||||
|
||||
} // namespace pp
|
||||
|
||||
#endif // COMPILER_PREPROCESSOR_MACRO_H_
|
||||
465
gfx/angle/src/compiler/preprocessor/MacroExpander.cpp
Normal file
465
gfx/angle/src/compiler/preprocessor/MacroExpander.cpp
Normal file
|
|
@ -0,0 +1,465 @@
|
|||
//
|
||||
// Copyright (c) 2011 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.
|
||||
//
|
||||
|
||||
#include "compiler/preprocessor/MacroExpander.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "compiler/preprocessor/DiagnosticsBase.h"
|
||||
#include "compiler/preprocessor/Token.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const size_t kMaxContextTokens = 10000;
|
||||
|
||||
class TokenLexer : public Lexer
|
||||
{
|
||||
public:
|
||||
typedef std::vector<Token> TokenVector;
|
||||
|
||||
TokenLexer(TokenVector *tokens)
|
||||
{
|
||||
tokens->swap(mTokens);
|
||||
mIter = mTokens.begin();
|
||||
}
|
||||
|
||||
void lex(Token *token) override
|
||||
{
|
||||
if (mIter == mTokens.end())
|
||||
{
|
||||
token->reset();
|
||||
token->type = Token::LAST;
|
||||
}
|
||||
else
|
||||
{
|
||||
*token = *mIter++;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TokenVector mTokens;
|
||||
TokenVector::const_iterator mIter;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class MacroExpander::ScopedMacroReenabler final : angle::NonCopyable
|
||||
{
|
||||
public:
|
||||
ScopedMacroReenabler(MacroExpander *expander);
|
||||
~ScopedMacroReenabler();
|
||||
|
||||
private:
|
||||
MacroExpander *mExpander;
|
||||
};
|
||||
|
||||
MacroExpander::ScopedMacroReenabler::ScopedMacroReenabler(MacroExpander *expander)
|
||||
: mExpander(expander)
|
||||
{
|
||||
mExpander->mDeferReenablingMacros = true;
|
||||
}
|
||||
|
||||
MacroExpander::ScopedMacroReenabler::~ScopedMacroReenabler()
|
||||
{
|
||||
mExpander->mDeferReenablingMacros = false;
|
||||
for (auto *macro : mExpander->mMacrosToReenable)
|
||||
{
|
||||
macro->disabled = false;
|
||||
}
|
||||
mExpander->mMacrosToReenable.clear();
|
||||
}
|
||||
|
||||
MacroExpander::MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics)
|
||||
: mLexer(lexer),
|
||||
mMacroSet(macroSet),
|
||||
mDiagnostics(diagnostics),
|
||||
mTotalTokensInContexts(0),
|
||||
mDeferReenablingMacros(false)
|
||||
{
|
||||
}
|
||||
|
||||
MacroExpander::~MacroExpander()
|
||||
{
|
||||
for (MacroContext *context : mContextStack)
|
||||
{
|
||||
delete context;
|
||||
}
|
||||
}
|
||||
|
||||
void MacroExpander::lex(Token *token)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
getToken(token);
|
||||
|
||||
if (token->type != Token::IDENTIFIER)
|
||||
break;
|
||||
|
||||
if (token->expansionDisabled())
|
||||
break;
|
||||
|
||||
MacroSet::const_iterator iter = mMacroSet->find(token->text);
|
||||
if (iter == mMacroSet->end())
|
||||
break;
|
||||
|
||||
const Macro& macro = iter->second;
|
||||
if (macro.disabled)
|
||||
{
|
||||
// If a particular token is not expanded, it is never expanded.
|
||||
token->setExpansionDisabled(true);
|
||||
break;
|
||||
}
|
||||
|
||||
// Bump the expansion count before peeking if the next token is a '('
|
||||
// otherwise there could be a #undef of the macro before the next token.
|
||||
macro.expansionCount++;
|
||||
if ((macro.type == Macro::kTypeFunc) && !isNextTokenLeftParen())
|
||||
{
|
||||
// If the token immediately after the macro name is not a '(',
|
||||
// this macro should not be expanded.
|
||||
macro.expansionCount--;
|
||||
break;
|
||||
}
|
||||
|
||||
pushMacro(macro, *token);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroExpander::getToken(Token *token)
|
||||
{
|
||||
if (mReserveToken.get())
|
||||
{
|
||||
*token = *mReserveToken;
|
||||
mReserveToken.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// First pop all empty macro contexts.
|
||||
while (!mContextStack.empty() && mContextStack.back()->empty())
|
||||
{
|
||||
popMacro();
|
||||
}
|
||||
|
||||
if (!mContextStack.empty())
|
||||
{
|
||||
*token = mContextStack.back()->get();
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(mTotalTokensInContexts == 0);
|
||||
mLexer->lex(token);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroExpander::ungetToken(const Token &token)
|
||||
{
|
||||
if (!mContextStack.empty())
|
||||
{
|
||||
MacroContext *context = mContextStack.back();
|
||||
context->unget();
|
||||
ASSERT(context->replacements[context->index] == token);
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(!mReserveToken.get());
|
||||
mReserveToken.reset(new Token(token));
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroExpander::isNextTokenLeftParen()
|
||||
{
|
||||
Token token;
|
||||
getToken(&token);
|
||||
|
||||
bool lparen = token.type == '(';
|
||||
ungetToken(token);
|
||||
|
||||
return lparen;
|
||||
}
|
||||
|
||||
bool MacroExpander::pushMacro(const Macro ¯o, const Token &identifier)
|
||||
{
|
||||
ASSERT(!macro.disabled);
|
||||
ASSERT(!identifier.expansionDisabled());
|
||||
ASSERT(identifier.type == Token::IDENTIFIER);
|
||||
ASSERT(identifier.text == macro.name);
|
||||
|
||||
std::vector<Token> replacements;
|
||||
if (!expandMacro(macro, identifier, &replacements))
|
||||
return false;
|
||||
|
||||
// Macro is disabled for expansion until it is popped off the stack.
|
||||
macro.disabled = true;
|
||||
|
||||
MacroContext *context = new MacroContext;
|
||||
context->macro = ¯o;
|
||||
context->replacements.swap(replacements);
|
||||
mContextStack.push_back(context);
|
||||
mTotalTokensInContexts += context->replacements.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroExpander::popMacro()
|
||||
{
|
||||
ASSERT(!mContextStack.empty());
|
||||
|
||||
MacroContext *context = mContextStack.back();
|
||||
mContextStack.pop_back();
|
||||
|
||||
ASSERT(context->empty());
|
||||
ASSERT(context->macro->disabled);
|
||||
ASSERT(context->macro->expansionCount > 0);
|
||||
if (mDeferReenablingMacros)
|
||||
{
|
||||
mMacrosToReenable.push_back(context->macro);
|
||||
}
|
||||
else
|
||||
{
|
||||
context->macro->disabled = false;
|
||||
}
|
||||
context->macro->expansionCount--;
|
||||
mTotalTokensInContexts -= context->replacements.size();
|
||||
delete context;
|
||||
}
|
||||
|
||||
bool MacroExpander::expandMacro(const Macro ¯o,
|
||||
const Token &identifier,
|
||||
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(),
|
||||
macro.replacements.end());
|
||||
|
||||
if (macro.predefined)
|
||||
{
|
||||
const char kLine[] = "__LINE__";
|
||||
const char kFile[] = "__FILE__";
|
||||
|
||||
ASSERT(replacements->size() == 1);
|
||||
Token& repl = replacements->front();
|
||||
if (macro.name == kLine)
|
||||
{
|
||||
repl.text = ToString(identifier.location.line);
|
||||
}
|
||||
else if (macro.name == kFile)
|
||||
{
|
||||
repl.text = ToString(identifier.location.file);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(macro.type == Macro::kTypeFunc);
|
||||
std::vector<MacroArg> args;
|
||||
args.reserve(macro.parameters.size());
|
||||
if (!collectMacroArgs(macro, identifier, &args, &replacementLocation))
|
||||
return false;
|
||||
|
||||
replaceMacroParams(macro, args, replacements);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < replacements->size(); ++i)
|
||||
{
|
||||
Token& repl = replacements->at(i);
|
||||
if (i == 0)
|
||||
{
|
||||
// The first token in the replacement list inherits the padding
|
||||
// properties of the identifier token.
|
||||
repl.setAtStartOfLine(identifier.atStartOfLine());
|
||||
repl.setHasLeadingSpace(identifier.hasLeadingSpace());
|
||||
}
|
||||
repl.location = replacementLocation;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroExpander::collectMacroArgs(const Macro ¯o,
|
||||
const Token &identifier,
|
||||
std::vector<MacroArg> *args,
|
||||
SourceLocation *closingParenthesisLocation)
|
||||
{
|
||||
Token token;
|
||||
getToken(&token);
|
||||
ASSERT(token.type == '(');
|
||||
|
||||
args->push_back(MacroArg());
|
||||
|
||||
// Defer reenabling macros until args collection is finished to avoid the possibility of
|
||||
// infinite recursion. Otherwise infinite recursion might happen when expanding the args after
|
||||
// macros have been popped from the context stack when parsing the args.
|
||||
ScopedMacroReenabler deferReenablingMacros(this);
|
||||
|
||||
int openParens = 1;
|
||||
while (openParens != 0)
|
||||
{
|
||||
getToken(&token);
|
||||
|
||||
if (token.type == Token::LAST)
|
||||
{
|
||||
mDiagnostics->report(Diagnostics::PP_MACRO_UNTERMINATED_INVOCATION,
|
||||
identifier.location, identifier.text);
|
||||
// Do not lose EOF token.
|
||||
ungetToken(token);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isArg = false; // True if token is part of the current argument.
|
||||
switch (token.type)
|
||||
{
|
||||
case '(':
|
||||
++openParens;
|
||||
isArg = true;
|
||||
break;
|
||||
case ')':
|
||||
--openParens;
|
||||
isArg = openParens != 0;
|
||||
*closingParenthesisLocation = token.location;
|
||||
break;
|
||||
case ',':
|
||||
// The individual arguments are separated by comma tokens, but
|
||||
// the comma tokens between matching inner parentheses do not
|
||||
// seperate arguments.
|
||||
if (openParens == 1)
|
||||
args->push_back(MacroArg());
|
||||
isArg = openParens != 1;
|
||||
break;
|
||||
default:
|
||||
isArg = true;
|
||||
break;
|
||||
}
|
||||
if (isArg)
|
||||
{
|
||||
MacroArg &arg = args->back();
|
||||
// Initial whitespace is not part of the argument.
|
||||
if (arg.empty())
|
||||
token.setHasLeadingSpace(false);
|
||||
arg.push_back(token);
|
||||
}
|
||||
}
|
||||
|
||||
const Macro::Parameters ¶ms = macro.parameters;
|
||||
// If there is only one empty argument, it is equivalent to no argument.
|
||||
if (params.empty() && (args->size() == 1) && args->front().empty())
|
||||
{
|
||||
args->clear();
|
||||
}
|
||||
// Validate the number of arguments.
|
||||
if (args->size() != params.size())
|
||||
{
|
||||
Diagnostics::ID id = args->size() < macro.parameters.size() ?
|
||||
Diagnostics::PP_MACRO_TOO_FEW_ARGS :
|
||||
Diagnostics::PP_MACRO_TOO_MANY_ARGS;
|
||||
mDiagnostics->report(id, identifier.location, identifier.text);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pre-expand each argument before substitution.
|
||||
// This step expands each argument individually before they are
|
||||
// inserted into the macro body.
|
||||
size_t numTokens = 0;
|
||||
for (auto &arg : *args)
|
||||
{
|
||||
TokenLexer lexer(&arg);
|
||||
MacroExpander expander(&lexer, mMacroSet, mDiagnostics);
|
||||
|
||||
arg.clear();
|
||||
expander.lex(&token);
|
||||
while (token.type != Token::LAST)
|
||||
{
|
||||
arg.push_back(token);
|
||||
expander.lex(&token);
|
||||
numTokens++;
|
||||
if (numTokens + mTotalTokensInContexts > kMaxContextTokens)
|
||||
{
|
||||
mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token.location, token.text);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacroExpander::replaceMacroParams(const Macro ¯o,
|
||||
const std::vector<MacroArg> &args,
|
||||
std::vector<Token> *replacements)
|
||||
{
|
||||
for (std::size_t i = 0; i < macro.replacements.size(); ++i)
|
||||
{
|
||||
if (!replacements->empty() &&
|
||||
replacements->size() + mTotalTokensInContexts > kMaxContextTokens)
|
||||
{
|
||||
const Token &token = replacements->back();
|
||||
mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token.location, token.text);
|
||||
return;
|
||||
}
|
||||
|
||||
const Token &repl = macro.replacements[i];
|
||||
if (repl.type != Token::IDENTIFIER)
|
||||
{
|
||||
replacements->push_back(repl);
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO(alokp): Optimize this.
|
||||
// There is no need to search for macro params every time.
|
||||
// The param index can be cached with the replacement token.
|
||||
Macro::Parameters::const_iterator iter = std::find(
|
||||
macro.parameters.begin(), macro.parameters.end(), repl.text);
|
||||
if (iter == macro.parameters.end())
|
||||
{
|
||||
replacements->push_back(repl);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::size_t iArg = std::distance(macro.parameters.begin(), iter);
|
||||
const MacroArg &arg = args[iArg];
|
||||
if (arg.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
std::size_t iRepl = replacements->size();
|
||||
replacements->insert(replacements->end(), arg.begin(), arg.end());
|
||||
// The replacement token inherits padding properties from
|
||||
// macro replacement token.
|
||||
replacements->at(iRepl).setHasLeadingSpace(repl.hasLeadingSpace());
|
||||
}
|
||||
}
|
||||
|
||||
MacroExpander::MacroContext::MacroContext() : macro(0), index(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool MacroExpander::MacroContext::empty() const
|
||||
{
|
||||
return index == replacements.size();
|
||||
}
|
||||
|
||||
const Token &MacroExpander::MacroContext::get()
|
||||
{
|
||||
return replacements[index++];
|
||||
}
|
||||
|
||||
void MacroExpander::MacroContext::unget()
|
||||
{
|
||||
ASSERT(index > 0);
|
||||
--index;
|
||||
}
|
||||
|
||||
} // namespace pp
|
||||
|
||||
79
gfx/angle/src/compiler/preprocessor/MacroExpander.h
Normal file
79
gfx/angle/src/compiler/preprocessor/MacroExpander.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_PREPROCESSOR_MACROEXPANDER_H_
|
||||
#define COMPILER_PREPROCESSOR_MACROEXPANDER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "compiler/preprocessor/Lexer.h"
|
||||
#include "compiler/preprocessor/Macro.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
class Diagnostics;
|
||||
struct SourceLocation;
|
||||
|
||||
class MacroExpander : public Lexer
|
||||
{
|
||||
public:
|
||||
MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics);
|
||||
~MacroExpander() override;
|
||||
|
||||
void lex(Token *token) override;
|
||||
|
||||
private:
|
||||
void getToken(Token *token);
|
||||
void ungetToken(const Token &token);
|
||||
bool isNextTokenLeftParen();
|
||||
|
||||
bool pushMacro(const Macro ¯o, const Token &identifier);
|
||||
void popMacro();
|
||||
|
||||
bool expandMacro(const Macro ¯o,
|
||||
const Token &identifier,
|
||||
std::vector<Token> *replacements);
|
||||
|
||||
typedef std::vector<Token> MacroArg;
|
||||
bool collectMacroArgs(const Macro ¯o,
|
||||
const Token &identifier,
|
||||
std::vector<MacroArg> *args,
|
||||
SourceLocation *closingParenthesisLocation);
|
||||
void replaceMacroParams(const Macro ¯o,
|
||||
const std::vector<MacroArg> &args,
|
||||
std::vector<Token> *replacements);
|
||||
|
||||
struct MacroContext
|
||||
{
|
||||
MacroContext();
|
||||
bool empty() const;
|
||||
const Token &get();
|
||||
void unget();
|
||||
|
||||
const Macro *macro;
|
||||
std::size_t index;
|
||||
std::vector<Token> replacements;
|
||||
};
|
||||
|
||||
Lexer *mLexer;
|
||||
MacroSet *mMacroSet;
|
||||
Diagnostics *mDiagnostics;
|
||||
|
||||
std::unique_ptr<Token> mReserveToken;
|
||||
std::vector<MacroContext *> mContextStack;
|
||||
size_t mTotalTokensInContexts;
|
||||
|
||||
bool mDeferReenablingMacros;
|
||||
std::vector<const Macro *> mMacrosToReenable;
|
||||
|
||||
class ScopedMacroReenabler;
|
||||
};
|
||||
|
||||
} // namespace pp
|
||||
|
||||
#endif // COMPILER_PREPROCESSOR_MACROEXPANDER_H_
|
||||
102
gfx/angle/src/compiler/preprocessor/Preprocessor.cpp
Normal file
102
gfx/angle/src/compiler/preprocessor/Preprocessor.cpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// Copyright (c) 2011 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.
|
||||
//
|
||||
|
||||
#include "compiler/preprocessor/Preprocessor.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "compiler/preprocessor/DiagnosticsBase.h"
|
||||
#include "compiler/preprocessor/DirectiveParser.h"
|
||||
#include "compiler/preprocessor/Macro.h"
|
||||
#include "compiler/preprocessor/MacroExpander.h"
|
||||
#include "compiler/preprocessor/Token.h"
|
||||
#include "compiler/preprocessor/Tokenizer.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
struct PreprocessorImpl
|
||||
{
|
||||
Diagnostics *diagnostics;
|
||||
MacroSet macroSet;
|
||||
Tokenizer tokenizer;
|
||||
DirectiveParser directiveParser;
|
||||
MacroExpander macroExpander;
|
||||
|
||||
PreprocessorImpl(Diagnostics *diag, DirectiveHandler *directiveHandler)
|
||||
: diagnostics(diag),
|
||||
tokenizer(diag),
|
||||
directiveParser(&tokenizer, ¯oSet, diag, directiveHandler),
|
||||
macroExpander(&directiveParser, ¯oSet, diag)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
Preprocessor::Preprocessor(Diagnostics *diagnostics,
|
||||
DirectiveHandler *directiveHandler)
|
||||
{
|
||||
mImpl = new PreprocessorImpl(diagnostics, directiveHandler);
|
||||
}
|
||||
|
||||
Preprocessor::~Preprocessor()
|
||||
{
|
||||
delete mImpl;
|
||||
}
|
||||
|
||||
bool Preprocessor::init(size_t count,
|
||||
const char * const string[],
|
||||
const int length[])
|
||||
{
|
||||
static const int kDefaultGLSLVersion = 100;
|
||||
|
||||
// Add standard pre-defined macros.
|
||||
predefineMacro("__LINE__", 0);
|
||||
predefineMacro("__FILE__", 0);
|
||||
predefineMacro("__VERSION__", kDefaultGLSLVersion);
|
||||
predefineMacro("GL_ES", 1);
|
||||
|
||||
return mImpl->tokenizer.init(count, string, length);
|
||||
}
|
||||
|
||||
void Preprocessor::predefineMacro(const char *name, int value)
|
||||
{
|
||||
PredefineMacro(&mImpl->macroSet, name, value);
|
||||
}
|
||||
|
||||
void Preprocessor::lex(Token *token)
|
||||
{
|
||||
bool validToken = false;
|
||||
while (!validToken)
|
||||
{
|
||||
mImpl->macroExpander.lex(token);
|
||||
switch (token->type)
|
||||
{
|
||||
// We should not be returning internal preprocessing tokens.
|
||||
// Convert preprocessing tokens to compiler tokens or report
|
||||
// diagnostics.
|
||||
case Token::PP_HASH:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
case Token::PP_NUMBER:
|
||||
mImpl->diagnostics->report(Diagnostics::PP_INVALID_NUMBER,
|
||||
token->location, token->text);
|
||||
break;
|
||||
case Token::PP_OTHER:
|
||||
mImpl->diagnostics->report(Diagnostics::PP_INVALID_CHARACTER,
|
||||
token->location, token->text);
|
||||
break;
|
||||
default:
|
||||
validToken = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Preprocessor::setMaxTokenSize(size_t maxTokenSize)
|
||||
{
|
||||
mImpl->tokenizer.setMaxTokenSize(maxTokenSize);
|
||||
}
|
||||
|
||||
} // namespace pp
|
||||
52
gfx/angle/src/compiler/preprocessor/Preprocessor.h
Normal file
52
gfx/angle/src/compiler/preprocessor/Preprocessor.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// Copyright (c) 2011 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_PREPROCESSOR_PREPROCESSOR_H_
|
||||
#define COMPILER_PREPROCESSOR_PREPROCESSOR_H_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "common/angleutils.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
class Diagnostics;
|
||||
class DirectiveHandler;
|
||||
struct PreprocessorImpl;
|
||||
struct Token;
|
||||
|
||||
class Preprocessor : angle::NonCopyable
|
||||
{
|
||||
public:
|
||||
Preprocessor(Diagnostics *diagnostics, DirectiveHandler *directiveHandler);
|
||||
~Preprocessor();
|
||||
|
||||
// count: specifies the number of elements in the string and length arrays.
|
||||
// string: specifies an array of pointers to strings.
|
||||
// length: specifies an array of string lengths.
|
||||
// If length is NULL, each string is assumed to be null terminated.
|
||||
// If length is a value other than NULL, it points to an array containing
|
||||
// a string length for each of the corresponding elements of string.
|
||||
// Each element in the length array may contain the length of the
|
||||
// corresponding string or a value less than 0 to indicate that the string
|
||||
// is null terminated.
|
||||
bool init(size_t count, const char * const string[], const int length[]);
|
||||
// Adds a pre-defined macro.
|
||||
void predefineMacro(const char *name, int value);
|
||||
|
||||
void lex(Token *token);
|
||||
|
||||
// Set maximum preprocessor token size
|
||||
void setMaxTokenSize(size_t maxTokenSize);
|
||||
|
||||
private:
|
||||
PreprocessorImpl *mImpl;
|
||||
};
|
||||
|
||||
} // namespace pp
|
||||
|
||||
#endif // COMPILER_PREPROCESSOR_PREPROCESSOR_H_
|
||||
47
gfx/angle/src/compiler/preprocessor/SourceLocation.h
Normal file
47
gfx/angle/src/compiler/preprocessor/SourceLocation.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_PREPROCESSOR_SOURCELOCATION_H_
|
||||
#define COMPILER_PREPROCESSOR_SOURCELOCATION_H_
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
struct SourceLocation
|
||||
{
|
||||
SourceLocation()
|
||||
: file(0),
|
||||
line(0)
|
||||
{
|
||||
}
|
||||
SourceLocation(int f, int l)
|
||||
: file(f),
|
||||
line(l)
|
||||
{
|
||||
}
|
||||
|
||||
bool equals(const SourceLocation &other) const
|
||||
{
|
||||
return (file == other.file) && (line == other.line);
|
||||
}
|
||||
|
||||
int file;
|
||||
int line;
|
||||
};
|
||||
|
||||
inline bool operator==(const SourceLocation &lhs, const SourceLocation &rhs)
|
||||
{
|
||||
return lhs.equals(rhs);
|
||||
}
|
||||
|
||||
inline bool operator!=(const SourceLocation &lhs, const SourceLocation &rhs)
|
||||
{
|
||||
return !lhs.equals(rhs);
|
||||
}
|
||||
|
||||
} // namespace pp
|
||||
|
||||
#endif // COMPILER_PREPROCESSOR_SOURCELOCATION_H_
|
||||
82
gfx/angle/src/compiler/preprocessor/Token.cpp
Normal file
82
gfx/angle/src/compiler/preprocessor/Token.cpp
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// Copyright (c) 2011 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.
|
||||
//
|
||||
|
||||
#include "compiler/preprocessor/Token.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "compiler/preprocessor/numeric_lex.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
void Token::reset()
|
||||
{
|
||||
type = 0;
|
||||
flags = 0;
|
||||
location = SourceLocation();
|
||||
text.clear();
|
||||
}
|
||||
|
||||
bool Token::equals(const Token &other) const
|
||||
{
|
||||
return (type == other.type) &&
|
||||
(flags == other.flags) &&
|
||||
(location == other.location) &&
|
||||
(text == other.text);
|
||||
}
|
||||
|
||||
void Token::setAtStartOfLine(bool start)
|
||||
{
|
||||
if (start)
|
||||
flags |= AT_START_OF_LINE;
|
||||
else
|
||||
flags &= ~AT_START_OF_LINE;
|
||||
}
|
||||
|
||||
void Token::setHasLeadingSpace(bool space)
|
||||
{
|
||||
if (space)
|
||||
flags |= HAS_LEADING_SPACE;
|
||||
else
|
||||
flags &= ~HAS_LEADING_SPACE;
|
||||
}
|
||||
|
||||
void Token::setExpansionDisabled(bool disable)
|
||||
{
|
||||
if (disable)
|
||||
flags |= EXPANSION_DISABLED;
|
||||
else
|
||||
flags &= ~EXPANSION_DISABLED;
|
||||
}
|
||||
|
||||
bool Token::iValue(int *value) const
|
||||
{
|
||||
ASSERT(type == CONST_INT);
|
||||
return numeric_lex_int(text, value);
|
||||
}
|
||||
|
||||
bool Token::uValue(unsigned int *value) const
|
||||
{
|
||||
ASSERT(type == CONST_INT);
|
||||
return numeric_lex_int(text, value);
|
||||
}
|
||||
|
||||
bool Token::fValue(float *value) const
|
||||
{
|
||||
ASSERT(type == CONST_FLOAT);
|
||||
return numeric_lex_float(text, value);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Token &token)
|
||||
{
|
||||
if (token.hasLeadingSpace())
|
||||
out << " ";
|
||||
|
||||
out << token.text;
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace pp
|
||||
120
gfx/angle/src/compiler/preprocessor/Token.h
Normal file
120
gfx/angle/src/compiler/preprocessor/Token.h
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
//
|
||||
// Copyright (c) 2011 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_PREPROCESSOR_TOKEN_H_
|
||||
#define COMPILER_PREPROCESSOR_TOKEN_H_
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
#include "compiler/preprocessor/SourceLocation.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
struct Token
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
LAST = 0, // EOF.
|
||||
|
||||
IDENTIFIER = 258,
|
||||
|
||||
CONST_INT,
|
||||
CONST_FLOAT,
|
||||
|
||||
OP_INC,
|
||||
OP_DEC,
|
||||
OP_LEFT,
|
||||
OP_RIGHT,
|
||||
OP_LE,
|
||||
OP_GE,
|
||||
OP_EQ,
|
||||
OP_NE,
|
||||
OP_AND,
|
||||
OP_XOR,
|
||||
OP_OR,
|
||||
OP_ADD_ASSIGN,
|
||||
OP_SUB_ASSIGN,
|
||||
OP_MUL_ASSIGN,
|
||||
OP_DIV_ASSIGN,
|
||||
OP_MOD_ASSIGN,
|
||||
OP_LEFT_ASSIGN,
|
||||
OP_RIGHT_ASSIGN,
|
||||
OP_AND_ASSIGN,
|
||||
OP_XOR_ASSIGN,
|
||||
OP_OR_ASSIGN,
|
||||
|
||||
// Preprocessing token types.
|
||||
// These types are used by the preprocessor internally.
|
||||
// Preprocessor clients must not depend or check for them.
|
||||
PP_HASH,
|
||||
PP_NUMBER,
|
||||
PP_OTHER
|
||||
};
|
||||
enum Flags
|
||||
{
|
||||
AT_START_OF_LINE = 1 << 0,
|
||||
HAS_LEADING_SPACE = 1 << 1,
|
||||
EXPANSION_DISABLED = 1 << 2
|
||||
};
|
||||
|
||||
Token()
|
||||
: type(0),
|
||||
flags(0)
|
||||
{
|
||||
}
|
||||
|
||||
void reset();
|
||||
bool equals(const Token &other) const;
|
||||
|
||||
// Returns true if this is the first token on line.
|
||||
// It disregards any leading whitespace.
|
||||
bool atStartOfLine() const
|
||||
{
|
||||
return (flags & AT_START_OF_LINE) != 0;
|
||||
}
|
||||
void setAtStartOfLine(bool start);
|
||||
|
||||
bool hasLeadingSpace() const
|
||||
{
|
||||
return (flags & HAS_LEADING_SPACE) != 0;
|
||||
}
|
||||
void setHasLeadingSpace(bool space);
|
||||
|
||||
bool expansionDisabled() const
|
||||
{
|
||||
return (flags & EXPANSION_DISABLED) != 0;
|
||||
}
|
||||
void setExpansionDisabled(bool disable);
|
||||
|
||||
// Converts text into numeric value for CONST_INT and CONST_FLOAT token.
|
||||
// Returns false if the parsed value cannot fit into an int or float.
|
||||
bool iValue(int *value) const;
|
||||
bool uValue(unsigned int *value) const;
|
||||
bool fValue(float *value) const;
|
||||
|
||||
int type;
|
||||
unsigned int flags;
|
||||
SourceLocation location;
|
||||
std::string text;
|
||||
};
|
||||
|
||||
inline bool operator==(const Token &lhs, const Token &rhs)
|
||||
{
|
||||
return lhs.equals(rhs);
|
||||
}
|
||||
|
||||
inline bool operator!=(const Token &lhs, const Token &rhs)
|
||||
{
|
||||
return !lhs.equals(rhs);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Token &token);
|
||||
|
||||
} // namepsace pp
|
||||
|
||||
#endif // COMPILER_PREPROCESSOR_TOKEN_H_
|
||||
2872
gfx/angle/src/compiler/preprocessor/Tokenizer.cpp
Normal file
2872
gfx/angle/src/compiler/preprocessor/Tokenizer.cpp
Normal file
File diff suppressed because it is too large
Load diff
58
gfx/angle/src/compiler/preprocessor/Tokenizer.h
Normal file
58
gfx/angle/src/compiler/preprocessor/Tokenizer.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// Copyright (c) 2012-2014 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_PREPROCESSOR_TOKENIZER_H_
|
||||
#define COMPILER_PREPROCESSOR_TOKENIZER_H_
|
||||
|
||||
#include "common/angleutils.h"
|
||||
#include "compiler/preprocessor/Input.h"
|
||||
#include "compiler/preprocessor/Lexer.h"
|
||||
|
||||
namespace pp
|
||||
{
|
||||
|
||||
class Diagnostics;
|
||||
|
||||
class Tokenizer : public Lexer
|
||||
{
|
||||
public:
|
||||
struct Context
|
||||
{
|
||||
Diagnostics *diagnostics;
|
||||
|
||||
Input input;
|
||||
// The location where yytext points to. Token location should track
|
||||
// scanLoc instead of Input::mReadLoc because they may not be the same
|
||||
// if text is buffered up in the scanner input buffer.
|
||||
Input::Location scanLoc;
|
||||
|
||||
bool leadingSpace;
|
||||
bool lineStart;
|
||||
};
|
||||
|
||||
Tokenizer(Diagnostics *diagnostics);
|
||||
~Tokenizer();
|
||||
|
||||
bool init(size_t count, const char * const string[], const int length[]);
|
||||
|
||||
void setFileNumber(int file);
|
||||
void setLineNumber(int line);
|
||||
void setMaxTokenSize(size_t maxTokenSize);
|
||||
|
||||
void lex(Token *token) override;
|
||||
|
||||
private:
|
||||
bool initScanner();
|
||||
void destroyScanner();
|
||||
|
||||
void *mHandle; // Scanner handle.
|
||||
Context mContext; // Scanner extra.
|
||||
size_t mMaxTokenSize; // Maximum token size
|
||||
};
|
||||
|
||||
} // namespace pp
|
||||
|
||||
#endif // COMPILER_PREPROCESSOR_TOKENIZER_H_
|
||||
357
gfx/angle/src/compiler/preprocessor/Tokenizer.l
Normal file
357
gfx/angle/src/compiler/preprocessor/Tokenizer.l
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
/*
|
||||
//
|
||||
// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
This file contains the Lex specification for GLSL ES preprocessor.
|
||||
Based on Microsoft Visual Studio 2010 Preprocessor Grammar:
|
||||
http://msdn.microsoft.com/en-us/library/2scxys89.aspx
|
||||
|
||||
IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh.
|
||||
*/
|
||||
|
||||
%top{
|
||||
//
|
||||
// Copyright (c) 2011-2014 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
// This file is auto-generated by generate_parser.sh. DO NOT EDIT!
|
||||
}
|
||||
|
||||
%{
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(disable: 4005)
|
||||
#endif
|
||||
|
||||
#include "compiler/preprocessor/Tokenizer.h"
|
||||
|
||||
#include "compiler/preprocessor/DiagnosticsBase.h"
|
||||
#include "compiler/preprocessor/Token.h"
|
||||
|
||||
#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;
|
||||
typedef pp::SourceLocation YYLTYPE;
|
||||
|
||||
// Use the unused yycolumn variable to track file (string) number.
|
||||
#define yyfileno yycolumn
|
||||
|
||||
#define YY_USER_INIT \
|
||||
do { \
|
||||
yyfileno = 0; \
|
||||
yylineno = 1; \
|
||||
yyextra->leadingSpace = false; \
|
||||
yyextra->lineStart = true; \
|
||||
} while(0);
|
||||
|
||||
#define YY_USER_ACTION \
|
||||
do \
|
||||
{ \
|
||||
pp::Input* input = &yyextra->input; \
|
||||
pp::Input::Location* scanLoc = &yyextra->scanLoc; \
|
||||
while ((scanLoc->sIndex < input->count()) && \
|
||||
(scanLoc->cIndex >= input->length(scanLoc->sIndex))) \
|
||||
{ \
|
||||
scanLoc->cIndex -= input->length(scanLoc->sIndex++); \
|
||||
++yyfileno; yylineno = 1; \
|
||||
} \
|
||||
yylloc->file = yyfileno; \
|
||||
yylloc->line = yylineno; \
|
||||
scanLoc->cIndex += yyleng; \
|
||||
} while(0);
|
||||
|
||||
#define YY_INPUT(buf, result, maxSize) \
|
||||
result = yyextra->input.read(buf, maxSize, &yylineno);
|
||||
|
||||
%}
|
||||
|
||||
%option noyywrap nounput never-interactive
|
||||
%option reentrant bison-bridge bison-locations
|
||||
%option prefix="pp"
|
||||
%option extra-type="pp::Tokenizer::Context*"
|
||||
%x COMMENT
|
||||
|
||||
NEWLINE \n|\r|\r\n
|
||||
IDENTIFIER [_a-zA-Z][_a-zA-Z0-9]*
|
||||
PUNCTUATOR [][<>(){}.+-/*%^|&~=!:;,?]
|
||||
|
||||
DECIMAL_CONSTANT [1-9][0-9]*[uU]?
|
||||
OCTAL_CONSTANT 0[0-7]*[uU]?
|
||||
HEXADECIMAL_CONSTANT 0[xX][0-9a-fA-F]+[uU]?
|
||||
|
||||
DIGIT [0-9]
|
||||
EXPONENT_PART [eE][+-]?{DIGIT}+
|
||||
FRACTIONAL_CONSTANT ({DIGIT}*"."{DIGIT}+)|({DIGIT}+".")
|
||||
|
||||
%%
|
||||
|
||||
/* Line comment */
|
||||
"//"[^\r\n]*
|
||||
|
||||
/* Block comment */
|
||||
/* Line breaks are just counted - not returned. */
|
||||
/* The comment is replaced by a single space. */
|
||||
"/*" { BEGIN(COMMENT); }
|
||||
<COMMENT>[^*\r\n]+
|
||||
<COMMENT>"*"
|
||||
<COMMENT>{NEWLINE} { ++yylineno; }
|
||||
<COMMENT>"*/" {
|
||||
yyextra->leadingSpace = true;
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
|
||||
# {
|
||||
// # is only valid at start of line for preprocessor directives.
|
||||
yylval->assign(1, yytext[0]);
|
||||
return yyextra->lineStart ? pp::Token::PP_HASH : pp::Token::PP_OTHER;
|
||||
}
|
||||
|
||||
{IDENTIFIER} {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::IDENTIFIER;
|
||||
}
|
||||
|
||||
({DECIMAL_CONSTANT}[uU]?)|({OCTAL_CONSTANT}[uU]?)|({HEXADECIMAL_CONSTANT}[uU]?) {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::CONST_INT;
|
||||
}
|
||||
|
||||
({DIGIT}+{EXPONENT_PART}[fF]?)|({FRACTIONAL_CONSTANT}{EXPONENT_PART}?[fF]?) {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::CONST_FLOAT;
|
||||
}
|
||||
|
||||
/* Anything that starts with a {DIGIT} or .{DIGIT} must be a number. */
|
||||
/* Rule to catch all invalid integers and floats. */
|
||||
({DIGIT}+[_a-zA-Z0-9.]*)|("."{DIGIT}+[_a-zA-Z0-9.]*) {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::PP_NUMBER;
|
||||
}
|
||||
|
||||
"++" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_INC;
|
||||
}
|
||||
"--" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_DEC;
|
||||
}
|
||||
"<<" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_LEFT;
|
||||
}
|
||||
">>" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_RIGHT;
|
||||
}
|
||||
"<=" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_LE;
|
||||
}
|
||||
">=" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_GE;
|
||||
}
|
||||
"==" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_EQ;
|
||||
}
|
||||
"!=" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_NE;
|
||||
}
|
||||
"&&" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_AND;
|
||||
}
|
||||
"^^" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_XOR;
|
||||
}
|
||||
"||" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_OR;
|
||||
}
|
||||
"+=" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_ADD_ASSIGN;
|
||||
}
|
||||
"-=" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_SUB_ASSIGN;
|
||||
}
|
||||
"*=" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_MUL_ASSIGN;
|
||||
}
|
||||
"/=" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_DIV_ASSIGN;
|
||||
}
|
||||
"%=" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_MOD_ASSIGN;
|
||||
}
|
||||
"<<=" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_LEFT_ASSIGN;
|
||||
}
|
||||
">>=" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_RIGHT_ASSIGN;
|
||||
}
|
||||
"&=" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_AND_ASSIGN;
|
||||
}
|
||||
"^=" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_XOR_ASSIGN;
|
||||
}
|
||||
"|=" {
|
||||
yylval->assign(yytext, yyleng);
|
||||
return pp::Token::OP_OR_ASSIGN;
|
||||
}
|
||||
|
||||
{PUNCTUATOR} {
|
||||
yylval->assign(1, yytext[0]);
|
||||
return yytext[0];
|
||||
}
|
||||
|
||||
[ \t\v\f]+ { yyextra->leadingSpace = true; }
|
||||
|
||||
{NEWLINE} {
|
||||
++yylineno;
|
||||
yylval->assign(1, '\n');
|
||||
return '\n';
|
||||
}
|
||||
|
||||
\\{NEWLINE} { ++yylineno; }
|
||||
|
||||
. {
|
||||
yylval->assign(1, yytext[0]);
|
||||
return pp::Token::PP_OTHER;
|
||||
}
|
||||
|
||||
<*><<EOF>> {
|
||||
// YY_USER_ACTION is not invoked for handling EOF.
|
||||
// Set the location for EOF token manually.
|
||||
pp::Input* input = &yyextra->input;
|
||||
pp::Input::Location* scanLoc = &yyextra->scanLoc;
|
||||
yy_size_t sIndexMax = input->count() ? input->count() - 1 : 0;
|
||||
if (scanLoc->sIndex != sIndexMax)
|
||||
{
|
||||
// We can only reach here if there are empty strings at the
|
||||
// end of the input.
|
||||
scanLoc->sIndex = sIndexMax; scanLoc->cIndex = 0;
|
||||
// FIXME: this is not 64-bit clean.
|
||||
yyfileno = static_cast<int>(sIndexMax); yylineno = 1;
|
||||
}
|
||||
yylloc->file = yyfileno;
|
||||
yylloc->line = yylineno;
|
||||
yylval->clear();
|
||||
|
||||
if (YY_START == COMMENT)
|
||||
{
|
||||
yyextra->diagnostics->report(pp::Diagnostics::PP_EOF_IN_COMMENT,
|
||||
pp::SourceLocation(yyfileno, yylineno),
|
||||
"");
|
||||
}
|
||||
yyterminate();
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
namespace pp {
|
||||
|
||||
Tokenizer::Tokenizer(Diagnostics *diagnostics) : mHandle(nullptr), mMaxTokenSize(256)
|
||||
{
|
||||
mContext.diagnostics = diagnostics;
|
||||
}
|
||||
|
||||
Tokenizer::~Tokenizer()
|
||||
{
|
||||
destroyScanner();
|
||||
}
|
||||
|
||||
bool Tokenizer::init(size_t count, const char * const string[], const int length[])
|
||||
{
|
||||
if ((count > 0) && (string == 0))
|
||||
return false;
|
||||
|
||||
mContext.input = Input(count, string, length);
|
||||
return initScanner();
|
||||
}
|
||||
|
||||
void Tokenizer::setFileNumber(int file)
|
||||
{
|
||||
// We use column number as file number.
|
||||
// See macro yyfileno.
|
||||
yyset_column(file, mHandle);
|
||||
}
|
||||
|
||||
void Tokenizer::setLineNumber(int line)
|
||||
{
|
||||
yyset_lineno(line, mHandle);
|
||||
}
|
||||
|
||||
void Tokenizer::setMaxTokenSize(size_t maxTokenSize)
|
||||
{
|
||||
mMaxTokenSize = maxTokenSize;
|
||||
}
|
||||
|
||||
void Tokenizer::lex(Token *token)
|
||||
{
|
||||
token->type = yylex(&token->text, &token->location, mHandle);
|
||||
if (token->text.size() > mMaxTokenSize)
|
||||
{
|
||||
mContext.diagnostics->report(Diagnostics::PP_TOKEN_TOO_LONG,
|
||||
token->location, token->text);
|
||||
token->text.erase(mMaxTokenSize);
|
||||
}
|
||||
|
||||
token->flags = 0;
|
||||
|
||||
token->setAtStartOfLine(mContext.lineStart);
|
||||
mContext.lineStart = token->type == '\n';
|
||||
|
||||
token->setHasLeadingSpace(mContext.leadingSpace);
|
||||
mContext.leadingSpace = false;
|
||||
}
|
||||
|
||||
bool Tokenizer::initScanner()
|
||||
{
|
||||
if ((mHandle == nullptr) && yylex_init_extra(&mContext, &mHandle))
|
||||
return false;
|
||||
|
||||
yyrestart(0, mHandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Tokenizer::destroyScanner()
|
||||
{
|
||||
if (mHandle == nullptr)
|
||||
return;
|
||||
|
||||
yylex_destroy(mHandle);
|
||||
mHandle = nullptr;
|
||||
}
|
||||
|
||||
} // namespace pp
|
||||
|
||||
27
gfx/angle/src/compiler/preprocessor/generate_parser.sh
Normal file
27
gfx/angle/src/compiler/preprocessor/generate_parser.sh
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#!/bin/bash
|
||||
# Copyright (c) 2012 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.
|
||||
|
||||
# Generates various components of GLSL ES preprocessor.
|
||||
|
||||
run_flex()
|
||||
{
|
||||
input_file=$script_dir/$1
|
||||
output_source=$script_dir/$2
|
||||
flex --noline --nounistd --outfile=$output_source $input_file
|
||||
}
|
||||
|
||||
run_bison()
|
||||
{
|
||||
input_file=$script_dir/$1
|
||||
output_source=$script_dir/$2
|
||||
bison --no-lines --skeleton=yacc.c --output=$output_source $input_file
|
||||
}
|
||||
|
||||
script_dir=$(dirname $0)
|
||||
|
||||
# Generate preprocessor
|
||||
run_flex Tokenizer.l Tokenizer.cpp
|
||||
run_bison ExpressionParser.y ExpressionParser.cpp
|
||||
patch --silent --forward < 64bit-tokenizer-safety.patch
|
||||
73
gfx/angle/src/compiler/preprocessor/numeric_lex.h
Normal file
73
gfx/angle/src/compiler/preprocessor/numeric_lex.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// Copyright (c) 2012 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.
|
||||
//
|
||||
|
||||
// numeric_lex.h: Functions to extract numeric values from string.
|
||||
|
||||
#ifndef COMPILER_PREPROCESSOR_NUMERICLEX_H_
|
||||
#define COMPILER_PREPROCESSOR_NUMERICLEX_H_
|
||||
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
|
||||
namespace pp {
|
||||
|
||||
inline std::ios::fmtflags numeric_base_int(const std::string &str)
|
||||
{
|
||||
if ((str.size() >= 2) &&
|
||||
(str[0] == '0') &&
|
||||
(str[1] == 'x' || str[1] == 'X'))
|
||||
{
|
||||
return std::ios::hex;
|
||||
}
|
||||
if ((str.size() >= 1) && (str[0] == '0'))
|
||||
{
|
||||
return std::ios::oct;
|
||||
}
|
||||
return std::ios::dec;
|
||||
}
|
||||
|
||||
// The following functions parse the given string to extract a numerical
|
||||
// value of the given type. These functions assume that the string is
|
||||
// of the correct form. They can only fail if the parsed value is too big,
|
||||
// in which case false is returned.
|
||||
|
||||
template<typename IntType>
|
||||
bool numeric_lex_int(const std::string &str, IntType *value)
|
||||
{
|
||||
std::istringstream stream(str);
|
||||
// This should not be necessary, but MSVS has a buggy implementation.
|
||||
// It returns incorrect results if the base is not specified.
|
||||
stream.setf(numeric_base_int(str), std::ios::basefield);
|
||||
|
||||
stream >> (*value);
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
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.
|
||||
stream.imbue(std::locale::classic());
|
||||
|
||||
stream >> (*value);
|
||||
return !stream.fail() && std::isfinite(*value);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace pp.
|
||||
|
||||
#endif // COMPILER_PREPROCESSOR_NUMERICLEX_H_
|
||||
122
gfx/angle/src/compiler/translator/64bit-lexer-safety.patch
Normal file
122
gfx/angle/src/compiler/translator/64bit-lexer-safety.patch
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
diff --git a/src/compiler/translator/glslang_lex.cpp b/src/compiler/translator/glslang_lex.cpp
|
||||
index 1ba63df..2a206ab 100644
|
||||
--- a/src/compiler/translator/glslang_lex.cpp
|
||||
+++ b/src/compiler/translator/glslang_lex.cpp
|
||||
@@ -1,4 +1,3 @@
|
||||
-#line 17 "./glslang.l"
|
||||
//
|
||||
// Copyright (c) 2012-2013 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
@@ -149,6 +148,7 @@ typedef int16_t flex_int16_t;
|
||||
typedef uint16_t flex_uint16_t;
|
||||
typedef int32_t flex_int32_t;
|
||||
typedef uint32_t flex_uint32_t;
|
||||
+typedef uint64_t flex_uint64_t;
|
||||
#else
|
||||
typedef signed char flex_int8_t;
|
||||
typedef short int flex_int16_t;
|
||||
@@ -335,6 +335,11 @@ typedef size_t yy_size_t;
|
||||
|
||||
|
||||
|
||||
+#ifndef YY_TYPEDEF_YY_SIZE_T
|
||||
+#define YY_TYPEDEF_YY_SIZE_T
|
||||
+typedef size_t yy_size_t;
|
||||
+#endif
|
||||
+
|
||||
#define EOB_ACT_CONTINUE_SCAN 0
|
||||
#define EOB_ACT_END_OF_FILE 1
|
||||
#define EOB_ACT_LAST_MATCH 2
|
||||
@@ -351,8 +356,8 @@ typedef size_t yy_size_t;
|
||||
*/
|
||||
#define YY_LESS_LINENO(n) \
|
||||
do { \
|
||||
- int yyl;\
|
||||
- for ( yyl = n; yyl < yyleng; ++yyl )\
|
||||
+ yy_size_t yyl;\
|
||||
+ for ( yyl = n; yyl < static_cast<yy_site_t>(yyleng); ++yyl )\
|
||||
if ( yytext[yyl] == '\n' )\
|
||||
--yylineno;\
|
||||
}while(0)
|
||||
@@ -1692,7 +1697,7 @@ yy_find_action:
|
||||
if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
|
||||
{
|
||||
yy_size_t yyl;
|
||||
- for ( yyl = 0; yyl < yyleng; ++yyl )
|
||||
+ for ( yyl = 0; yyl < static_cast<yy_size_t>(yyleng); ++yyl )
|
||||
if ( yytext[yyl] == '\n' )
|
||||
|
||||
do{ yylineno++;
|
||||
@@ -2655,7 +2660,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
|
||||
else
|
||||
{
|
||||
int num_to_read =
|
||||
- YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
|
||||
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - static_cast<int>(number_to_move) - 1;
|
||||
|
||||
while ( num_to_read <= 0 )
|
||||
{ /* Not enough room in the buffer - grow it. */
|
||||
@@ -2690,7 +2695,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
|
||||
yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
|
||||
|
||||
num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
|
||||
- number_to_move - 1;
|
||||
+ static_cast<int>(number_to_move) - 1;
|
||||
|
||||
}
|
||||
|
||||
@@ -2698,8 +2703,10 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
|
||||
num_to_read = YY_READ_BUF_SIZE;
|
||||
|
||||
/* Read in more data. */
|
||||
+ size_t result = 0;
|
||||
YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
|
||||
- yyg->yy_n_chars, num_to_read );
|
||||
+ result, num_to_read );
|
||||
+ yyg->yy_n_chars = static_cast<int>(result);
|
||||
|
||||
YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
|
||||
}
|
||||
@@ -2725,13 +2732,13 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
|
||||
|
||||
if ((int) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
|
||||
/* Extend the array by 50%, plus the number we really need. */
|
||||
- int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
|
||||
+ int new_size = yyg->yy_n_chars + static_cast<int>(number_to_move) + (yyg->yy_n_chars >> 1);
|
||||
YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
|
||||
if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
|
||||
YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
|
||||
}
|
||||
|
||||
- yyg->yy_n_chars += number_to_move;
|
||||
+ yyg->yy_n_chars += static_cast<int>(number_to_move);
|
||||
YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
|
||||
YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
|
||||
|
||||
@@ -3158,7 +3165,7 @@ static void yyensure_buffer_stack (yyscan_t yyscanner)
|
||||
/* Increase the buffer to prepare for a possible push. */
|
||||
yy_size_t grow_size = 8 /* arbitrary grow size */;
|
||||
|
||||
- num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
|
||||
+ num_to_alloc = static_cast<int>(yyg->yy_buffer_stack_max + grow_size);
|
||||
yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc
|
||||
(yyg->yy_buffer_stack,
|
||||
num_to_alloc * sizeof(struct yy_buffer_state*)
|
||||
@@ -3196,7 +3203,7 @@ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscann
|
||||
if ( ! b )
|
||||
YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
|
||||
|
||||
- b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
|
||||
+ b->yy_buf_size = static_cast<int>(size) - 2; /* "- 2" to take care of EOB's */
|
||||
b->yy_buf_pos = b->yy_ch_buf = base;
|
||||
b->yy_is_our_buffer = 0;
|
||||
b->yy_input_file = NULL;
|
||||
@@ -3251,7 +3258,7 @@ YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yysc
|
||||
if ( ! buf )
|
||||
YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
|
||||
|
||||
- for ( i = 0; i < _yybytes_len; ++i )
|
||||
+ for ( i = 0; i < static_cast<yy_size_t>(_yybytes_len); ++i )
|
||||
buf[i] = yybytes[i];
|
||||
|
||||
buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
|
||||
454
gfx/angle/src/compiler/translator/ASTMetadataHLSL.cpp
Normal file
454
gfx/angle/src/compiler/translator/ASTMetadataHLSL.cpp
Normal file
|
|
@ -0,0 +1,454 @@
|
|||
//
|
||||
// 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 sh
|
||||
{
|
||||
|
||||
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(TIntermFunctionDefinition *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 visitIfElse(Visit visit, TIntermIfElse *ifElse) override
|
||||
{
|
||||
visitControlFlow(visit, ifElse);
|
||||
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->getFunctionSymbolInfo());
|
||||
ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
|
||||
|
||||
if ((*mMetadataList)[calleeIndex].mUsesGradient) {
|
||||
onGradient();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TString name =
|
||||
TFunction::unmangleName(node->getFunctionSymbolInfo()->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(TIntermFunctionDefinition *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 visitIfElse(Visit visit, TIntermIfElse *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->getFunctionSymbolInfo());
|
||||
ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < 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<TIntermIfElse *> 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(TIntermFunctionDefinition *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->getFunctionSymbolInfo());
|
||||
ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < 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(TIntermIfElse *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;
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
63
gfx/angle/src/compiler/translator/ASTMetadataHLSL.h
Normal file
63
gfx/angle/src/compiler/translator/ASTMetadataHLSL.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// 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>
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
class CallDAG;
|
||||
class TIntermNode;
|
||||
class TIntermIfElse;
|
||||
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(TIntermIfElse *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<TIntermIfElse *> 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);
|
||||
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_ASTMETADATAHLSL_H_
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
#include "compiler/translator/AddAndTrueToLoopCondition.h"
|
||||
|
||||
#include "compiler/translator/IntermNode.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// An AST traverser that rewrites for and while loops by replacing "condition" with
|
||||
// "condition && true" to work around condition bug on Intel Mac.
|
||||
class AddAndTrueToLoopConditionTraverser : public TIntermTraverser
|
||||
{
|
||||
public:
|
||||
AddAndTrueToLoopConditionTraverser() : TIntermTraverser(true, false, false) {}
|
||||
|
||||
bool visitLoop(Visit, TIntermLoop *loop) override
|
||||
{
|
||||
// do-while loop doesn't have this bug.
|
||||
if (loop->getType() != ELoopFor && loop->getType() != ELoopWhile)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// For loop may not have a condition.
|
||||
if (loop->getCondition() == nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Constant true.
|
||||
TConstantUnion *trueConstant = new TConstantUnion();
|
||||
trueConstant->setBConst(true);
|
||||
TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, TType(EbtBool));
|
||||
|
||||
// CONDITION && true.
|
||||
TIntermBinary *andOp = new TIntermBinary(EOpLogicalAnd, loop->getCondition(), trueValue);
|
||||
loop->setCondition(andOp);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void AddAndTrueToLoopCondition(TIntermNode *root)
|
||||
{
|
||||
AddAndTrueToLoopConditionTraverser traverser;
|
||||
root->traverse(&traverser);
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
// Rewrite condition in for and while loops to work around driver bug on Intel Mac.
|
||||
|
||||
#ifndef COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_
|
||||
#define COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_
|
||||
|
||||
class TIntermNode;
|
||||
namespace sh
|
||||
{
|
||||
|
||||
void AddAndTrueToLoopCondition(TIntermNode *root);
|
||||
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// AddDefaultReturnStatements.cpp: Add default return statements to functions that do not end in a
|
||||
// return.
|
||||
//
|
||||
|
||||
#include "compiler/translator/AddDefaultReturnStatements.h"
|
||||
|
||||
#include "compiler/translator/IntermNode.h"
|
||||
#include "compiler/translator/util.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class AddDefaultReturnStatementsTraverser : private TIntermTraverser
|
||||
{
|
||||
public:
|
||||
static void Apply(TIntermNode *root)
|
||||
{
|
||||
AddDefaultReturnStatementsTraverser separateInit;
|
||||
root->traverse(&separateInit);
|
||||
separateInit.updateTree();
|
||||
}
|
||||
|
||||
private:
|
||||
AddDefaultReturnStatementsTraverser() : TIntermTraverser(true, false, false) {}
|
||||
|
||||
static bool IsFunctionWithoutReturnStatement(TIntermFunctionDefinition *node, TType *returnType)
|
||||
{
|
||||
*returnType = node->getType();
|
||||
if (node->getType().getBasicType() == EbtVoid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TIntermBlock *bodyNode = node->getBody();
|
||||
TIntermBranch *returnNode = bodyNode->getSequence()->back()->getAsBranchNode();
|
||||
if (returnNode != nullptr && returnNode->getFlowOp() == EOpReturn)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visitFunctionDefinition(Visit, TIntermFunctionDefinition *node) override
|
||||
{
|
||||
TType returnType;
|
||||
if (IsFunctionWithoutReturnStatement(node, &returnType))
|
||||
{
|
||||
TIntermBranch *branch =
|
||||
new TIntermBranch(EOpReturn, TIntermTyped::CreateZero(returnType));
|
||||
|
||||
TIntermBlock *bodyNode = node->getBody();
|
||||
bodyNode->getSequence()->push_back(branch);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
void AddDefaultReturnStatements(TIntermNode *node)
|
||||
{
|
||||
AddDefaultReturnStatementsTraverser::Apply(node);
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// AddDefaultReturnStatements.h: Add default return statements to functions that do not end in a
|
||||
// return.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_TRANSLATOR_ADDDEFAULTRETURNSTATEMENTS_H_
|
||||
#define COMPILER_TRANSLATOR_ADDDEFAULTRETURNSTATEMENTS_H_
|
||||
|
||||
class TIntermNode;
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
void AddDefaultReturnStatements(TIntermNode *node);
|
||||
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_ADDDEFAULTRETURNSTATEMENTS_H_
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
//
|
||||
// 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 sh
|
||||
{
|
||||
|
||||
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->getFunctionSymbolInfo() = *originalCall->getFunctionSymbolInfo();
|
||||
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 visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
|
||||
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::visitFunctionDefinition(
|
||||
Visit visit,
|
||||
TIntermFunctionDefinition *node)
|
||||
{
|
||||
if (node->isArray() && visit == PreVisit)
|
||||
{
|
||||
// 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->getFunctionParameters();
|
||||
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());
|
||||
|
||||
queueReplacementWithParent(node, params, replacementParams, OriginalNode::IS_DROPPED);
|
||||
|
||||
node->setType(TType(EbtVoid));
|
||||
|
||||
mInFunctionWithArrayReturnValue = true;
|
||||
}
|
||||
if (visit == PostVisit)
|
||||
{
|
||||
// This isn't conditional on node->isArray() since the type has already been changed on
|
||||
// PreVisit.
|
||||
mInFunctionWithArrayReturnValue = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArrayReturnValueToOutParameterTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
|
||||
{
|
||||
if (visit == PreVisit)
|
||||
{
|
||||
if (node->isArray())
|
||||
{
|
||||
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->getFunctionSymbolInfo() = *node->getFunctionSymbolInfo();
|
||||
replacement->setLine(node->getLine());
|
||||
replacement->setType(TType(EbtVoid));
|
||||
|
||||
queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
|
||||
}
|
||||
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.
|
||||
TIntermBlock *parentBlock = getParentNode()->getAsBlock();
|
||||
if (parentBlock)
|
||||
{
|
||||
nextTemporaryIndex();
|
||||
TIntermSequence replacements;
|
||||
replacements.push_back(createTempDeclaration(node->getType()));
|
||||
TIntermSymbol *returnSymbol = createTempSymbol(node->getType());
|
||||
replacements.push_back(CreateReplacementCall(node, returnSymbol));
|
||||
mMultiReplacements.push_back(
|
||||
NodeReplaceWithMultipleEntry(parentBlock, node, replacements));
|
||||
}
|
||||
return 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;
|
||||
|
||||
TIntermTyped *expression = node->getExpression();
|
||||
ASSERT(expression != nullptr);
|
||||
TIntermSymbol *returnValueSymbol = CreateReturnValueSymbol(expression->getType());
|
||||
TIntermBinary *replacementAssignment =
|
||||
new TIntermBinary(EOpAssign, returnValueSymbol, expression);
|
||||
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()->getAsBlock(), 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());
|
||||
queueReplacement(node, replacementCall, OriginalNode::IS_DROPPED);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ArrayReturnValueToOutParameter(TIntermNode *root, unsigned int *temporaryIndex)
|
||||
{
|
||||
ArrayReturnValueToOutParameterTraverser::apply(root, temporaryIndex);
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// 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_
|
||||
|
||||
namespace sh
|
||||
{
|
||||
class TIntermNode;
|
||||
|
||||
void ArrayReturnValueToOutParameter(TIntermNode *root, unsigned int *temporaryIndex);
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_ARRAYRETURNVALUETOOUTPARAMETER_H_
|
||||
781
gfx/angle/src/compiler/translator/BaseTypes.h
Normal file
781
gfx/angle/src/compiler/translator/BaseTypes.h
Normal file
|
|
@ -0,0 +1,781 @@
|
|||
//
|
||||
// Copyright (c) 2002-2013 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_TRANSLATOR_BASETYPES_H_
|
||||
#define COMPILER_TRANSLATOR_BASETYPES_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "GLSLANG/ShaderLang.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
//
|
||||
// Precision qualifiers
|
||||
//
|
||||
enum TPrecision
|
||||
{
|
||||
// These need to be kept sorted
|
||||
EbpUndefined,
|
||||
EbpLow,
|
||||
EbpMedium,
|
||||
EbpHigh,
|
||||
|
||||
// end of list
|
||||
EbpLast
|
||||
};
|
||||
|
||||
inline const char* getPrecisionString(TPrecision p)
|
||||
{
|
||||
switch(p)
|
||||
{
|
||||
case EbpHigh: return "highp";
|
||||
case EbpMedium: return "mediump";
|
||||
case EbpLow: return "lowp";
|
||||
default: return "mediump"; // Safest fallback
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Basic type. Arrays, vectors, etc., are orthogonal to this.
|
||||
//
|
||||
enum TBasicType
|
||||
{
|
||||
EbtVoid,
|
||||
EbtFloat,
|
||||
EbtInt,
|
||||
EbtUInt,
|
||||
EbtBool,
|
||||
EbtGVec4, // non type: represents vec4, ivec4, and uvec4
|
||||
EbtGenType, // non type: represents float, vec2, vec3, and vec4
|
||||
EbtGenIType, // non type: represents int, ivec2, ivec3, and ivec4
|
||||
EbtGenUType, // non type: represents uint, uvec2, uvec3, and uvec4
|
||||
EbtGenBType, // non type: represents bool, bvec2, bvec3, and bvec4
|
||||
EbtVec, // non type: represents vec2, vec3, and vec4
|
||||
EbtIVec, // non type: represents ivec2, ivec3, and ivec4
|
||||
EbtUVec, // non type: represents uvec2, uvec3, and uvec4
|
||||
EbtBVec, // non type: represents bvec2, bvec3, and bvec4
|
||||
EbtGuardSamplerBegin, // non type: see implementation of IsSampler()
|
||||
EbtSampler2D,
|
||||
EbtSampler3D,
|
||||
EbtSamplerCube,
|
||||
EbtSampler2DArray,
|
||||
EbtSamplerExternalOES, // Only valid if OES_EGL_image_external exists.
|
||||
EbtSampler2DRect, // Only valid if GL_ARB_texture_rectangle exists.
|
||||
EbtISampler2D,
|
||||
EbtISampler3D,
|
||||
EbtISamplerCube,
|
||||
EbtISampler2DArray,
|
||||
EbtUSampler2D,
|
||||
EbtUSampler3D,
|
||||
EbtUSamplerCube,
|
||||
EbtUSampler2DArray,
|
||||
EbtSampler2DShadow,
|
||||
EbtSamplerCubeShadow,
|
||||
EbtSampler2DArrayShadow,
|
||||
EbtGuardSamplerEnd, // non type: see implementation of IsSampler()
|
||||
EbtGSampler2D, // non type: represents sampler2D, isampler2D, and usampler2D
|
||||
EbtGSampler3D, // non type: represents sampler3D, isampler3D, and usampler3D
|
||||
EbtGSamplerCube, // non type: represents samplerCube, isamplerCube, and usamplerCube
|
||||
EbtGSampler2DArray, // non type: represents sampler2DArray, isampler2DArray, and
|
||||
// usampler2DArray
|
||||
|
||||
// images
|
||||
EbtGuardImageBegin,
|
||||
EbtImage2D,
|
||||
EbtIImage2D,
|
||||
EbtUImage2D,
|
||||
EbtImage3D,
|
||||
EbtIImage3D,
|
||||
EbtUImage3D,
|
||||
EbtImage2DArray,
|
||||
EbtIImage2DArray,
|
||||
EbtUImage2DArray,
|
||||
EbtImageCube,
|
||||
EbtIImageCube,
|
||||
EbtUImageCube,
|
||||
EbtGuardImageEnd,
|
||||
|
||||
EbtGuardGImageBegin,
|
||||
EbtGImage2D, // non type: represents image2D, uimage2D, iimage2D
|
||||
EbtGImage3D, // non type: represents image3D, uimage3D, iimage3D
|
||||
EbtGImage2DArray, // non type: represents image2DArray, uimage2DArray, iimage2DArray
|
||||
EbtGImageCube, // non type: represents imageCube, uimageCube, iimageCube
|
||||
EbtGuardGImageEnd,
|
||||
|
||||
EbtStruct,
|
||||
EbtInterfaceBlock,
|
||||
EbtAddress, // should be deprecated??
|
||||
|
||||
// end of list
|
||||
EbtLast
|
||||
};
|
||||
|
||||
inline TBasicType convertGImageToFloatImage(TBasicType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EbtGImage2D:
|
||||
return EbtImage2D;
|
||||
case EbtGImage3D:
|
||||
return EbtImage3D;
|
||||
case EbtGImage2DArray:
|
||||
return EbtImage2DArray;
|
||||
case EbtGImageCube:
|
||||
return EbtImageCube;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return EbtLast;
|
||||
}
|
||||
|
||||
inline TBasicType convertGImageToIntImage(TBasicType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EbtGImage2D:
|
||||
return EbtIImage2D;
|
||||
case EbtGImage3D:
|
||||
return EbtIImage3D;
|
||||
case EbtGImage2DArray:
|
||||
return EbtIImage2DArray;
|
||||
case EbtGImageCube:
|
||||
return EbtIImageCube;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return EbtLast;
|
||||
}
|
||||
|
||||
inline TBasicType convertGImageToUnsignedImage(TBasicType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EbtGImage2D:
|
||||
return EbtUImage2D;
|
||||
case EbtGImage3D:
|
||||
return EbtUImage3D;
|
||||
case EbtGImage2DArray:
|
||||
return EbtUImage2DArray;
|
||||
case EbtGImageCube:
|
||||
return EbtUImageCube;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return EbtLast;
|
||||
}
|
||||
|
||||
const char* getBasicString(TBasicType t);
|
||||
|
||||
inline bool IsSampler(TBasicType type)
|
||||
{
|
||||
return type > EbtGuardSamplerBegin && type < EbtGuardSamplerEnd;
|
||||
}
|
||||
|
||||
inline bool IsImage(TBasicType type)
|
||||
{
|
||||
return type > EbtGuardImageBegin && type < EbtGuardImageEnd;
|
||||
}
|
||||
|
||||
inline bool IsGImage(TBasicType type)
|
||||
{
|
||||
return type > EbtGuardGImageBegin && type < EbtGuardGImageEnd;
|
||||
}
|
||||
|
||||
inline bool IsOpaqueType(TBasicType type)
|
||||
{
|
||||
// TODO (mradev): add atomic types as opaque.
|
||||
return IsSampler(type) || IsImage(type);
|
||||
}
|
||||
|
||||
inline bool IsIntegerSampler(TBasicType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EbtISampler2D:
|
||||
case EbtISampler3D:
|
||||
case EbtISamplerCube:
|
||||
case EbtISampler2DArray:
|
||||
case EbtUSampler2D:
|
||||
case EbtUSampler3D:
|
||||
case EbtUSamplerCube:
|
||||
case EbtUSampler2DArray:
|
||||
return true;
|
||||
case EbtSampler2D:
|
||||
case EbtSampler3D:
|
||||
case EbtSamplerCube:
|
||||
case EbtSamplerExternalOES:
|
||||
case EbtSampler2DRect:
|
||||
case EbtSampler2DArray:
|
||||
case EbtSampler2DShadow:
|
||||
case EbtSamplerCubeShadow:
|
||||
case EbtSampler2DArrayShadow:
|
||||
return false;
|
||||
default:
|
||||
assert(!IsSampler(type));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool IsFloatImage(TBasicType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EbtImage2D:
|
||||
case EbtImage3D:
|
||||
case EbtImage2DArray:
|
||||
case EbtImageCube:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool IsIntegerImage(TBasicType type)
|
||||
{
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case EbtIImage2D:
|
||||
case EbtIImage3D:
|
||||
case EbtIImage2DArray:
|
||||
case EbtIImageCube:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool IsUnsignedImage(TBasicType type)
|
||||
{
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case EbtUImage2D:
|
||||
case EbtUImage3D:
|
||||
case EbtUImage2DArray:
|
||||
case EbtUImageCube:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool IsSampler2D(TBasicType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EbtSampler2D:
|
||||
case EbtISampler2D:
|
||||
case EbtUSampler2D:
|
||||
case EbtSampler2DArray:
|
||||
case EbtISampler2DArray:
|
||||
case EbtUSampler2DArray:
|
||||
case EbtSampler2DRect:
|
||||
case EbtSamplerExternalOES:
|
||||
case EbtSampler2DShadow:
|
||||
case EbtSampler2DArrayShadow:
|
||||
return true;
|
||||
case EbtSampler3D:
|
||||
case EbtISampler3D:
|
||||
case EbtUSampler3D:
|
||||
case EbtISamplerCube:
|
||||
case EbtUSamplerCube:
|
||||
case EbtSamplerCube:
|
||||
case EbtSamplerCubeShadow:
|
||||
return false;
|
||||
default:
|
||||
assert(!IsSampler(type));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool IsSamplerCube(TBasicType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EbtSamplerCube:
|
||||
case EbtISamplerCube:
|
||||
case EbtUSamplerCube:
|
||||
case EbtSamplerCubeShadow:
|
||||
return true;
|
||||
case EbtSampler2D:
|
||||
case EbtSampler3D:
|
||||
case EbtSamplerExternalOES:
|
||||
case EbtSampler2DRect:
|
||||
case EbtSampler2DArray:
|
||||
case EbtISampler2D:
|
||||
case EbtISampler3D:
|
||||
case EbtISampler2DArray:
|
||||
case EbtUSampler2D:
|
||||
case EbtUSampler3D:
|
||||
case EbtUSampler2DArray:
|
||||
case EbtSampler2DShadow:
|
||||
case EbtSampler2DArrayShadow:
|
||||
return false;
|
||||
default:
|
||||
assert(!IsSampler(type));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool IsSampler3D(TBasicType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EbtSampler3D:
|
||||
case EbtISampler3D:
|
||||
case EbtUSampler3D:
|
||||
return true;
|
||||
case EbtSampler2D:
|
||||
case EbtSamplerCube:
|
||||
case EbtSamplerExternalOES:
|
||||
case EbtSampler2DRect:
|
||||
case EbtSampler2DArray:
|
||||
case EbtISampler2D:
|
||||
case EbtISamplerCube:
|
||||
case EbtISampler2DArray:
|
||||
case EbtUSampler2D:
|
||||
case EbtUSamplerCube:
|
||||
case EbtUSampler2DArray:
|
||||
case EbtSampler2DShadow:
|
||||
case EbtSamplerCubeShadow:
|
||||
case EbtSampler2DArrayShadow:
|
||||
return false;
|
||||
default:
|
||||
assert(!IsSampler(type));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool IsSamplerArray(TBasicType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EbtSampler2DArray:
|
||||
case EbtISampler2DArray:
|
||||
case EbtUSampler2DArray:
|
||||
case EbtSampler2DArrayShadow:
|
||||
return true;
|
||||
case EbtSampler2D:
|
||||
case EbtISampler2D:
|
||||
case EbtUSampler2D:
|
||||
case EbtSampler2DRect:
|
||||
case EbtSamplerExternalOES:
|
||||
case EbtSampler3D:
|
||||
case EbtISampler3D:
|
||||
case EbtUSampler3D:
|
||||
case EbtISamplerCube:
|
||||
case EbtUSamplerCube:
|
||||
case EbtSamplerCube:
|
||||
case EbtSampler2DShadow:
|
||||
case EbtSamplerCubeShadow:
|
||||
return false;
|
||||
default:
|
||||
assert(!IsSampler(type));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool IsShadowSampler(TBasicType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EbtSampler2DShadow:
|
||||
case EbtSamplerCubeShadow:
|
||||
case EbtSampler2DArrayShadow:
|
||||
return true;
|
||||
case EbtISampler2D:
|
||||
case EbtISampler3D:
|
||||
case EbtISamplerCube:
|
||||
case EbtISampler2DArray:
|
||||
case EbtUSampler2D:
|
||||
case EbtUSampler3D:
|
||||
case EbtUSamplerCube:
|
||||
case EbtUSampler2DArray:
|
||||
case EbtSampler2D:
|
||||
case EbtSampler3D:
|
||||
case EbtSamplerCube:
|
||||
case EbtSamplerExternalOES:
|
||||
case EbtSampler2DRect:
|
||||
case EbtSampler2DArray:
|
||||
return false;
|
||||
default:
|
||||
assert(!IsSampler(type));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool IsInteger(TBasicType type)
|
||||
{
|
||||
return type == EbtInt || type == EbtUInt;
|
||||
}
|
||||
|
||||
inline bool SupportsPrecision(TBasicType type)
|
||||
{
|
||||
return type == EbtFloat || type == EbtInt || type == EbtUInt || IsOpaqueType(type);
|
||||
}
|
||||
|
||||
//
|
||||
// Qualifiers and built-ins. These are mainly used to see what can be read
|
||||
// or written, and by the machine dependent translator to know which registers
|
||||
// to allocate variables in. Since built-ins tend to go to different registers
|
||||
// than varying or uniform, it makes sense they are peers, not sub-classes.
|
||||
//
|
||||
enum TQualifier
|
||||
{
|
||||
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,
|
||||
EvqOut,
|
||||
EvqInOut,
|
||||
EvqConstReadOnly,
|
||||
|
||||
// built-ins read by vertex shader
|
||||
EvqInstanceID,
|
||||
EvqVertexID,
|
||||
|
||||
// built-ins written by vertex shader
|
||||
EvqPosition,
|
||||
EvqPointSize,
|
||||
|
||||
// built-ins read by fragment shader
|
||||
EvqFragCoord,
|
||||
EvqFrontFacing,
|
||||
EvqPointCoord,
|
||||
|
||||
// built-ins written by fragment shader
|
||||
EvqFragColor,
|
||||
EvqFragData,
|
||||
|
||||
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
|
||||
EvqCentroid, // Incomplete qualifier
|
||||
EvqSmoothOut,
|
||||
EvqFlatOut,
|
||||
EvqCentroidOut, // Implies smooth
|
||||
EvqSmoothIn,
|
||||
EvqFlatIn,
|
||||
EvqCentroidIn, // Implies smooth
|
||||
|
||||
// GLSL ES 3.1 compute shader special variables
|
||||
EvqComputeIn,
|
||||
EvqNumWorkGroups,
|
||||
EvqWorkGroupSize,
|
||||
EvqWorkGroupID,
|
||||
EvqLocalInvocationID,
|
||||
EvqGlobalInvocationID,
|
||||
EvqLocalInvocationIndex,
|
||||
|
||||
// GLSL ES 3.1 memory qualifiers
|
||||
EvqReadOnly,
|
||||
EvqWriteOnly,
|
||||
EvqCoherent,
|
||||
EvqRestrict,
|
||||
EvqVolatile,
|
||||
|
||||
// end of list
|
||||
EvqLast
|
||||
};
|
||||
|
||||
inline bool IsQualifierUnspecified(TQualifier qualifier)
|
||||
{
|
||||
return (qualifier == EvqTemporary || qualifier == EvqGlobal);
|
||||
}
|
||||
|
||||
enum TLayoutImageInternalFormat
|
||||
{
|
||||
EiifUnspecified,
|
||||
EiifRGBA32F,
|
||||
EiifRGBA16F,
|
||||
EiifR32F,
|
||||
EiifRGBA32UI,
|
||||
EiifRGBA16UI,
|
||||
EiifRGBA8UI,
|
||||
EiifR32UI,
|
||||
EiifRGBA32I,
|
||||
EiifRGBA16I,
|
||||
EiifRGBA8I,
|
||||
EiifR32I,
|
||||
EiifRGBA8,
|
||||
EiifRGBA8_SNORM
|
||||
};
|
||||
|
||||
enum TLayoutMatrixPacking
|
||||
{
|
||||
EmpUnspecified,
|
||||
EmpRowMajor,
|
||||
EmpColumnMajor
|
||||
};
|
||||
|
||||
enum TLayoutBlockStorage
|
||||
{
|
||||
EbsUnspecified,
|
||||
EbsShared,
|
||||
EbsPacked,
|
||||
EbsStd140
|
||||
};
|
||||
|
||||
struct TLayoutQualifier
|
||||
{
|
||||
int location;
|
||||
unsigned int locationsSpecified;
|
||||
TLayoutMatrixPacking matrixPacking;
|
||||
TLayoutBlockStorage blockStorage;
|
||||
|
||||
// Compute shader layout qualifiers.
|
||||
sh::WorkGroupSize localSize;
|
||||
|
||||
// Image format layout qualifier
|
||||
TLayoutImageInternalFormat imageInternalFormat;
|
||||
|
||||
static TLayoutQualifier create()
|
||||
{
|
||||
TLayoutQualifier layoutQualifier;
|
||||
|
||||
layoutQualifier.location = -1;
|
||||
layoutQualifier.locationsSpecified = 0;
|
||||
layoutQualifier.matrixPacking = EmpUnspecified;
|
||||
layoutQualifier.blockStorage = EbsUnspecified;
|
||||
|
||||
layoutQualifier.localSize.fill(-1);
|
||||
|
||||
layoutQualifier.imageInternalFormat = EiifUnspecified;
|
||||
return layoutQualifier;
|
||||
}
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
return location == -1 && matrixPacking == EmpUnspecified &&
|
||||
blockStorage == EbsUnspecified && !localSize.isAnyValueSet() &&
|
||||
imageInternalFormat == EiifUnspecified;
|
||||
}
|
||||
|
||||
bool isCombinationValid() const
|
||||
{
|
||||
bool workSizeSpecified = localSize.isAnyValueSet();
|
||||
bool otherLayoutQualifiersSpecified =
|
||||
(location != -1 || matrixPacking != EmpUnspecified || blockStorage != EbsUnspecified ||
|
||||
imageInternalFormat != EiifUnspecified);
|
||||
|
||||
// we can have either the work group size specified, or the other layout qualifiers
|
||||
return !(workSizeSpecified && otherLayoutQualifiersSpecified);
|
||||
}
|
||||
|
||||
bool isLocalSizeEqual(const sh::WorkGroupSize &localSizeIn) const
|
||||
{
|
||||
return localSize.isWorkGroupSizeMatching(localSizeIn);
|
||||
}
|
||||
};
|
||||
|
||||
struct TMemoryQualifier
|
||||
{
|
||||
// GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers
|
||||
// An image can be qualified as both readonly and writeonly. It still can be can be used with
|
||||
// imageSize().
|
||||
bool readonly;
|
||||
bool writeonly;
|
||||
bool coherent;
|
||||
|
||||
// restrict and volatile are reserved keywords in C/C++
|
||||
bool restrictQualifier;
|
||||
bool volatileQualifier;
|
||||
static TMemoryQualifier create()
|
||||
{
|
||||
TMemoryQualifier memoryQualifier;
|
||||
|
||||
memoryQualifier.readonly = false;
|
||||
memoryQualifier.writeonly = false;
|
||||
memoryQualifier.coherent = false;
|
||||
memoryQualifier.restrictQualifier = false;
|
||||
memoryQualifier.volatileQualifier = false;
|
||||
|
||||
return memoryQualifier;
|
||||
}
|
||||
|
||||
bool isEmpty()
|
||||
{
|
||||
return !readonly && !writeonly && !coherent && !restrictQualifier && !volatileQualifier;
|
||||
}
|
||||
};
|
||||
|
||||
inline const char *getWorkGroupSizeString(size_t dimension)
|
||||
{
|
||||
switch (dimension)
|
||||
{
|
||||
case 0u:
|
||||
return "local_size_x";
|
||||
case 1u:
|
||||
return "local_size_y";
|
||||
case 2u:
|
||||
return "local_size_z";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return "dimension out of bounds";
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// This is just for debug print out, carried along with the definitions above.
|
||||
//
|
||||
inline const char* getQualifierString(TQualifier q)
|
||||
{
|
||||
// clang-format off
|
||||
switch(q)
|
||||
{
|
||||
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 EvqVertexID: return "VertexID";
|
||||
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 "smooth centroid out";
|
||||
case EvqFlatOut: return "flat out";
|
||||
case EvqSmoothIn: return "smooth in";
|
||||
case EvqFlatIn: return "flat in";
|
||||
case EvqCentroidIn: return "smooth centroid in";
|
||||
case EvqCentroid: return "centroid";
|
||||
case EvqFlat: return "flat";
|
||||
case EvqSmooth: return "smooth";
|
||||
case EvqComputeIn: return "in";
|
||||
case EvqNumWorkGroups: return "NumWorkGroups";
|
||||
case EvqWorkGroupSize: return "WorkGroupSize";
|
||||
case EvqWorkGroupID: return "WorkGroupID";
|
||||
case EvqLocalInvocationID: return "LocalInvocationID";
|
||||
case EvqGlobalInvocationID: return "GlobalInvocationID";
|
||||
case EvqLocalInvocationIndex: return "LocalInvocationIndex";
|
||||
case EvqReadOnly: return "readonly";
|
||||
case EvqWriteOnly: return "writeonly";
|
||||
default: UNREACHABLE(); return "unknown qualifier";
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
inline const char* getMatrixPackingString(TLayoutMatrixPacking mpq)
|
||||
{
|
||||
switch (mpq)
|
||||
{
|
||||
case EmpUnspecified: return "mp_unspecified";
|
||||
case EmpRowMajor: return "row_major";
|
||||
case EmpColumnMajor: return "column_major";
|
||||
default: UNREACHABLE(); return "unknown matrix packing";
|
||||
}
|
||||
}
|
||||
|
||||
inline const char* getBlockStorageString(TLayoutBlockStorage bsq)
|
||||
{
|
||||
switch (bsq)
|
||||
{
|
||||
case EbsUnspecified: return "bs_unspecified";
|
||||
case EbsShared: return "shared";
|
||||
case EbsPacked: return "packed";
|
||||
case EbsStd140: return "std140";
|
||||
default: UNREACHABLE(); return "unknown block storage";
|
||||
}
|
||||
}
|
||||
|
||||
inline const char *getImageInternalFormatString(TLayoutImageInternalFormat iifq)
|
||||
{
|
||||
switch (iifq)
|
||||
{
|
||||
case EiifRGBA32F:
|
||||
return "rgba32f";
|
||||
case EiifRGBA16F:
|
||||
return "rgba16f";
|
||||
case EiifR32F:
|
||||
return "r32f";
|
||||
case EiifRGBA32UI:
|
||||
return "rgba32ui";
|
||||
case EiifRGBA16UI:
|
||||
return "rgba16ui";
|
||||
case EiifRGBA8UI:
|
||||
return "rgba8ui";
|
||||
case EiifR32UI:
|
||||
return "r32ui";
|
||||
case EiifRGBA32I:
|
||||
return "rgba32i";
|
||||
case EiifRGBA16I:
|
||||
return "rgba16i";
|
||||
case EiifRGBA8I:
|
||||
return "rgba8i";
|
||||
case EiifR32I:
|
||||
return "r32i";
|
||||
case EiifRGBA8:
|
||||
return "rgba8";
|
||||
case EiifRGBA8_SNORM:
|
||||
return "rgba8_snorm";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return "unknown internal image format";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_BASETYPES_H_
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
//
|
||||
// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
|
||||
// may record a variable as aliasing another. Sometimes the alias information gets garbled
|
||||
// so we work around this issue by breaking the aliasing chain in inner loops.
|
||||
|
||||
#include "BreakVariableAliasingInInnerLoops.h"
|
||||
|
||||
#include "compiler/translator/IntermNode.h"
|
||||
|
||||
// A HLSL compiler developer gave us more details on the root cause and the workaround needed:
|
||||
// The root problem is that if the HLSL compiler is applying aliasing information even on
|
||||
// incomplete simulations (in this case, a single pass). The bug is triggered by an assignment
|
||||
// that comes from a series of assignments, possibly with swizzled or ternary operators with
|
||||
// known conditionals, where the source is before the loop.
|
||||
// So, a workaround is to add a +0 term to variables the first time they are assigned to in
|
||||
// an inner loop (if they are declared in an outside scope, otherwise there is no need).
|
||||
// This will break the aliasing chain.
|
||||
|
||||
// For simplicity here we add a +0 to any assignment that is in at least two nested loops. Because
|
||||
// the bug only shows up with swizzles, and ternary assignment, whole array or whole structure
|
||||
// assignment don't need a workaround.
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class AliasingBreaker : public TIntermTraverser
|
||||
{
|
||||
public:
|
||||
AliasingBreaker() : TIntermTraverser(true, false, true) {}
|
||||
|
||||
protected:
|
||||
bool visitBinary(Visit visit, TIntermBinary *binary)
|
||||
{
|
||||
if (visit != PreVisit)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mLoopLevel < 2 || !binary->isAssignment())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
TIntermTyped *B = binary->getRight();
|
||||
TType type = B->getType();
|
||||
|
||||
if (!type.isScalar() && !type.isVector() && !type.isMatrix())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.isArray() || IsSampler(type.getBasicType()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// We have a scalar / vector / matrix assignment with loop depth 2.
|
||||
// Transform it from
|
||||
// A = B
|
||||
// to
|
||||
// A = (B + typeof<B>(0));
|
||||
|
||||
TIntermBinary *bPlusZero = new TIntermBinary(EOpAdd, B, TIntermTyped::CreateZero(type));
|
||||
bPlusZero->setLine(B->getLine());
|
||||
|
||||
binary->replaceChildNode(B, bPlusZero);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visitLoop(Visit visit, TIntermLoop *loop)
|
||||
{
|
||||
if (visit == PreVisit)
|
||||
{
|
||||
mLoopLevel++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(mLoopLevel > 0);
|
||||
mLoopLevel--;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
int mLoopLevel = 0;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void BreakVariableAliasingInInnerLoops(TIntermNode *root)
|
||||
{
|
||||
AliasingBreaker breaker;
|
||||
root->traverse(&breaker);
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
|
||||
// may record a variable as aliasing another. Sometimes the alias information gets garbled
|
||||
// so we work around this issue by breaking the aliasing chain in inner loops.
|
||||
|
||||
#ifndef COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
|
||||
#define COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
|
||||
|
||||
class TIntermNode;
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
void BreakVariableAliasingInInnerLoops(TIntermNode *root);
|
||||
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
|
||||
249
gfx/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp
Normal file
249
gfx/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
//
|
||||
// Copyright (c) 2002-2011 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.
|
||||
//
|
||||
|
||||
#include "angle_gl.h"
|
||||
#include "compiler/translator/BuiltInFunctionEmulator.h"
|
||||
#include "compiler/translator/SymbolTable.h"
|
||||
#include "compiler/translator/Cache.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTraverser
|
||||
{
|
||||
public:
|
||||
BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator &emulator)
|
||||
: TIntermTraverser(true, false, false),
|
||||
mEmulator(emulator)
|
||||
{
|
||||
}
|
||||
|
||||
bool visitUnary(Visit visit, TIntermUnary *node) override
|
||||
{
|
||||
if (visit == PreVisit)
|
||||
{
|
||||
bool needToEmulate = mEmulator.SetFunctionCalled(node->getOp(), node->getOperand()->getType());
|
||||
if (needToEmulate)
|
||||
node->setUseEmulatedFunction();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visitAggregate(Visit visit, TIntermAggregate *node) override
|
||||
{
|
||||
if (visit == PreVisit)
|
||||
{
|
||||
// Here we handle all the built-in functions instead of the ones we
|
||||
// currently identified as problematic.
|
||||
switch (node->getOp())
|
||||
{
|
||||
case EOpLessThan:
|
||||
case EOpGreaterThan:
|
||||
case EOpLessThanEqual:
|
||||
case EOpGreaterThanEqual:
|
||||
case EOpVectorEqual:
|
||||
case EOpVectorNotEqual:
|
||||
case EOpMod:
|
||||
case EOpPow:
|
||||
case EOpAtan:
|
||||
case EOpMin:
|
||||
case EOpMax:
|
||||
case EOpClamp:
|
||||
case EOpMix:
|
||||
case EOpStep:
|
||||
case EOpSmoothStep:
|
||||
case EOpDistance:
|
||||
case EOpDot:
|
||||
case EOpCross:
|
||||
case EOpFaceForward:
|
||||
case EOpReflect:
|
||||
case EOpRefract:
|
||||
case EOpOuterProduct:
|
||||
case EOpMul:
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
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();
|
||||
if (!param1 || !param2)
|
||||
return true;
|
||||
needToEmulate = mEmulator.SetFunctionCalled(
|
||||
node->getOp(), param1->getType(), param2->getType());
|
||||
}
|
||||
else if (sequence.size() == 3)
|
||||
{
|
||||
TIntermTyped *param1 = sequence[0]->getAsTyped();
|
||||
TIntermTyped *param2 = sequence[1]->getAsTyped();
|
||||
TIntermTyped *param3 = sequence[2]->getAsTyped();
|
||||
if (!param1 || !param2 || !param3)
|
||||
return true;
|
||||
needToEmulate = mEmulator.SetFunctionCalled(
|
||||
node->getOp(), param1->getType(), param2->getType(), param3->getType());
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (needToEmulate)
|
||||
node->setUseEmulatedFunction();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
BuiltInFunctionEmulator &mEmulator;
|
||||
};
|
||||
|
||||
BuiltInFunctionEmulator::BuiltInFunctionEmulator()
|
||||
{}
|
||||
|
||||
void BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, const TType *param,
|
||||
const char *emulatedFunctionDefinition)
|
||||
{
|
||||
mEmulatedFunctions[FunctionId(op, param)] = std::string(emulatedFunctionDefinition);
|
||||
}
|
||||
|
||||
void BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, const TType *param1, const TType *param2,
|
||||
const char *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)
|
||||
{
|
||||
mEmulatedFunctions[FunctionId(op, param1, param2, param3)] = std::string(emulatedFunctionDefinition);
|
||||
}
|
||||
|
||||
bool BuiltInFunctionEmulator::IsOutputEmpty() const
|
||||
{
|
||||
return (mFunctions.size() == 0);
|
||||
}
|
||||
|
||||
void BuiltInFunctionEmulator::OutputEmulatedFunctions(TInfoSinkBase &out) const
|
||||
{
|
||||
for (size_t i = 0; i < mFunctions.size(); ++i)
|
||||
{
|
||||
out << mEmulatedFunctions.find(mFunctions[i])->second << "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, const TType ¶m)
|
||||
{
|
||||
return SetFunctionCalled(FunctionId(op, ¶m));
|
||||
}
|
||||
|
||||
bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, const TType ¶m1, const TType ¶m2)
|
||||
{
|
||||
return SetFunctionCalled(FunctionId(op, ¶m1, ¶m2));
|
||||
}
|
||||
|
||||
bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op,
|
||||
const TType ¶m1, const TType ¶m2, const TType ¶m3)
|
||||
{
|
||||
return SetFunctionCalled(FunctionId(op, ¶m1, ¶m2, ¶m3));
|
||||
}
|
||||
|
||||
bool BuiltInFunctionEmulator::SetFunctionCalled(const FunctionId &functionId)
|
||||
{
|
||||
if (mEmulatedFunctions.find(functionId) != mEmulatedFunctions.end())
|
||||
{
|
||||
for (size_t i = 0; i < mFunctions.size(); ++i)
|
||||
{
|
||||
if (mFunctions[i] == functionId)
|
||||
return true;
|
||||
}
|
||||
// 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)
|
||||
{
|
||||
ASSERT(root);
|
||||
|
||||
if (mEmulatedFunctions.empty())
|
||||
return;
|
||||
|
||||
BuiltInFunctionEmulationMarker marker(*this);
|
||||
root->traverse(&marker);
|
||||
}
|
||||
|
||||
void BuiltInFunctionEmulator::Cleanup()
|
||||
{
|
||||
mFunctions.clear();
|
||||
}
|
||||
|
||||
//static
|
||||
TString BuiltInFunctionEmulator::GetEmulatedFunctionName(
|
||||
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)
|
||||
: mOp(op),
|
||||
mParam1(param),
|
||||
mParam2(TCache::getType(EbtVoid)),
|
||||
mParam3(TCache::getType(EbtVoid))
|
||||
{
|
||||
}
|
||||
|
||||
BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op, const TType *param1, const TType *param2)
|
||||
: mOp(op),
|
||||
mParam1(param1),
|
||||
mParam2(param2),
|
||||
mParam3(TCache::getType(EbtVoid))
|
||||
{
|
||||
}
|
||||
|
||||
BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op,
|
||||
const TType *param1, const TType *param2, const TType *param3)
|
||||
: mOp(op),
|
||||
mParam1(param1),
|
||||
mParam2(param2),
|
||||
mParam3(param3)
|
||||
{
|
||||
}
|
||||
|
||||
bool BuiltInFunctionEmulator::FunctionId::operator==(const BuiltInFunctionEmulator::FunctionId &other) const
|
||||
{
|
||||
return (mOp == other.mOp &&
|
||||
*mParam1 == *other.mParam1 &&
|
||||
*mParam2 == *other.mParam2 &&
|
||||
*mParam3 == *other.mParam3);
|
||||
}
|
||||
|
||||
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;
|
||||
return false; // all fields are equal
|
||||
}
|
||||
|
||||
BuiltInFunctionEmulator::FunctionId BuiltInFunctionEmulator::FunctionId::getCopy() const
|
||||
{
|
||||
return FunctionId(mOp, new TType(*mParam1), new TType(*mParam2), new TType(*mParam3));
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
90
gfx/angle/src/compiler/translator/BuiltInFunctionEmulator.h
Normal file
90
gfx/angle/src/compiler/translator/BuiltInFunctionEmulator.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// Copyright (c) 2011 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATOR_H_
|
||||
#define COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATOR_H_
|
||||
|
||||
#include "compiler/translator/InfoSink.h"
|
||||
#include "compiler/translator/IntermNode.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
//
|
||||
// This class decides which built-in functions need to be replaced with the
|
||||
// emulated ones.
|
||||
// It can be used to work around driver bugs or implement functions that are
|
||||
// not natively implemented on a specific platform.
|
||||
//
|
||||
class BuiltInFunctionEmulator
|
||||
{
|
||||
public:
|
||||
BuiltInFunctionEmulator();
|
||||
|
||||
void MarkBuiltInFunctionsForEmulation(TIntermNode *root);
|
||||
|
||||
void Cleanup();
|
||||
|
||||
// "name(" becomes "webgl_name_emu(".
|
||||
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;
|
||||
|
||||
// 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);
|
||||
|
||||
private:
|
||||
class BuiltInFunctionEmulationMarker;
|
||||
|
||||
// Records that a function is called by the shader and might need to be
|
||||
// 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 ¶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);
|
||||
|
||||
bool operator==(const FunctionId &other) const;
|
||||
bool operator<(const FunctionId &other) const;
|
||||
|
||||
FunctionId getCopy() const;
|
||||
private:
|
||||
TOperator mOp;
|
||||
|
||||
// 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);
|
||||
|
||||
// Map from function id to emulated function definition
|
||||
std::map<FunctionId, std::string> mEmulatedFunctions;
|
||||
|
||||
// Called function ids
|
||||
std::vector<FunctionId> mFunctions;
|
||||
};
|
||||
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATOR_H_
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
//
|
||||
// Copyright (c) 2002-2011 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.
|
||||
//
|
||||
|
||||
#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"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
void InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
|
||||
sh::GLenum shaderType)
|
||||
{
|
||||
if (shaderType == GL_VERTEX_SHADER)
|
||||
{
|
||||
const TType *int1 = TCache::getType(EbtInt);
|
||||
emu->addEmulatedFunction(EOpAbs, int1, "int webgl_abs_emu(int x) { return x * sign(x); }");
|
||||
}
|
||||
}
|
||||
|
||||
void InitBuiltInIsnanFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
|
||||
int targetGLSLVersion)
|
||||
{
|
||||
// isnan() is supported since GLSL 1.3.
|
||||
if (targetGLSLVersion < GLSL_VERSION_130)
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
// !(x > 0.0 || x < 0.0 || x == 0.0) will be optimized and always equal to false.
|
||||
emu->addEmulatedFunction(
|
||||
EOpIsNan, float1,
|
||||
"bool webgl_isnan_emu(float x) { return (x > 0.0 || x < 0.0) ? false : x != 0.0; }");
|
||||
emu->addEmulatedFunction(
|
||||
EOpIsNan, float2,
|
||||
"bvec2 webgl_isnan_emu(vec2 x)\n"
|
||||
"{\n"
|
||||
" bvec2 isnan;\n"
|
||||
" for (int i = 0; i < 2; i++)\n"
|
||||
" {\n"
|
||||
" isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
|
||||
" }\n"
|
||||
" return isnan;\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(
|
||||
EOpIsNan, float3,
|
||||
"bvec3 webgl_isnan_emu(vec3 x)\n"
|
||||
"{\n"
|
||||
" bvec3 isnan;\n"
|
||||
" for (int i = 0; i < 3; i++)\n"
|
||||
" {\n"
|
||||
" isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
|
||||
" }\n"
|
||||
" return isnan;\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(
|
||||
EOpIsNan, float4,
|
||||
"bvec4 webgl_isnan_emu(vec4 x)\n"
|
||||
"{\n"
|
||||
" bvec4 isnan;\n"
|
||||
" for (int i = 0; i < 4; i++)\n"
|
||||
" {\n"
|
||||
" isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
|
||||
" }\n"
|
||||
" return isnan;\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
// Emulate built-in functions missing from GLSL 1.30 and higher
|
||||
void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, sh::GLenum shaderType,
|
||||
int targetGLSLVersion)
|
||||
{
|
||||
// Emulate packUnorm2x16 and unpackUnorm2x16 (GLSL 4.10)
|
||||
if (targetGLSLVersion < GLSL_VERSION_410)
|
||||
{
|
||||
const TType *float2 = TCache::getType(EbtFloat, 2);
|
||||
const TType *uint1 = TCache::getType(EbtUInt);
|
||||
|
||||
// clang-format off
|
||||
emu->addEmulatedFunction(EOpPackUnorm2x16, float2,
|
||||
"uint webgl_packUnorm2x16_emu(vec2 v)\n"
|
||||
"{\n"
|
||||
" int x = int(round(clamp(v.x, 0.0, 1.0) * 65535.0));\n"
|
||||
" int y = int(round(clamp(v.y, 0.0, 1.0) * 65535.0));\n"
|
||||
" return uint((y << 16) | (x & 0xFFFF));\n"
|
||||
"}\n");
|
||||
|
||||
emu->addEmulatedFunction(EOpUnpackUnorm2x16, uint1,
|
||||
"vec2 webgl_unpackUnorm2x16_emu(uint u)\n"
|
||||
"{\n"
|
||||
" float x = float(u & 0xFFFFu) / 65535.0;\n"
|
||||
" float y = float(u >> 16) / 65535.0;\n"
|
||||
" return vec2(x, y);\n"
|
||||
"}\n");
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
// 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"
|
||||
" // The negative unary operator is buggy on OSX.\n"
|
||||
" // Work around this by using abs instead.\n"
|
||||
" scale = 1.0 / (1 << abs(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
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Copyright (c) 2011 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_
|
||||
#define COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_
|
||||
|
||||
#include "GLSLANG/ShaderLang.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
class BuiltInFunctionEmulator;
|
||||
|
||||
//
|
||||
// This works around bug in Intel Mac drivers.
|
||||
//
|
||||
void InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
|
||||
sh::GLenum shaderType);
|
||||
|
||||
//
|
||||
// This works around isnan() bug in Intel Mac drivers
|
||||
//
|
||||
void InitBuiltInIsnanFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
|
||||
int targetGLSLVersion);
|
||||
|
||||
//
|
||||
// This function is emulating built-in functions missing from GLSL 1.30 and higher.
|
||||
//
|
||||
void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, sh::GLenum shaderType,
|
||||
int targetGLSLVersion);
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_
|
||||
|
|
@ -0,0 +1,499 @@
|
|||
//
|
||||
// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
#include "angle_gl.h"
|
||||
#include "compiler/translator/BuiltInFunctionEmulator.h"
|
||||
#include "compiler/translator/BuiltInFunctionEmulatorHLSL.h"
|
||||
#include "compiler/translator/SymbolTable.h"
|
||||
#include "compiler/translator/VersionGLSL.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
void InitBuiltInIsnanFunctionEmulatorForHLSLWorkarounds(BuiltInFunctionEmulator *emu,
|
||||
int targetGLSLVersion)
|
||||
{
|
||||
if (targetGLSLVersion < GLSL_VERSION_130)
|
||||
return;
|
||||
|
||||
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(EOpIsNan, float1,
|
||||
"bool webgl_isnan_emu(float x)\n"
|
||||
"{\n"
|
||||
" return (x > 0.0 || x < 0.0) ? false : x != 0.0;\n"
|
||||
"}\n"
|
||||
"\n");
|
||||
|
||||
emu->addEmulatedFunction(EOpIsNan, float2,
|
||||
"bool2 webgl_isnan_emu(float2 x)\n"
|
||||
"{\n"
|
||||
" bool2 isnan;\n"
|
||||
" for (int i = 0; i < 2; i++)\n"
|
||||
" {\n"
|
||||
" isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
|
||||
" }\n"
|
||||
" return isnan;\n"
|
||||
"}\n");
|
||||
|
||||
emu->addEmulatedFunction(EOpIsNan, float3,
|
||||
"bool3 webgl_isnan_emu(float3 x)\n"
|
||||
"{\n"
|
||||
" bool3 isnan;\n"
|
||||
" for (int i = 0; i < 3; i++)\n"
|
||||
" {\n"
|
||||
" isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
|
||||
" }\n"
|
||||
" return isnan;\n"
|
||||
"}\n");
|
||||
|
||||
emu->addEmulatedFunction(EOpIsNan, float4,
|
||||
"bool4 webgl_isnan_emu(float4 x)\n"
|
||||
"{\n"
|
||||
" bool4 isnan;\n"
|
||||
" for (int i = 0; i < 4; i++)\n"
|
||||
" {\n"
|
||||
" isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
|
||||
" }\n"
|
||||
" return isnan;\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu)
|
||||
{
|
||||
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"
|
||||
"{\n"
|
||||
" return x - y * floor(x / y);\n"
|
||||
"}\n"
|
||||
"\n");
|
||||
emu->addEmulatedFunction(EOpMod, float2, float2,
|
||||
"float2 webgl_mod_emu(float2 x, float2 y)\n"
|
||||
"{\n"
|
||||
" return x - y * floor(x / y);\n"
|
||||
"}\n"
|
||||
"\n");
|
||||
emu->addEmulatedFunction(EOpMod, float2, float1,
|
||||
"float2 webgl_mod_emu(float2 x, float y)\n"
|
||||
"{\n"
|
||||
" return x - y * floor(x / y);\n"
|
||||
"}\n"
|
||||
"\n");
|
||||
emu->addEmulatedFunction(EOpMod, float3, float3,
|
||||
"float3 webgl_mod_emu(float3 x, float3 y)\n"
|
||||
"{\n"
|
||||
" return x - y * floor(x / y);\n"
|
||||
"}\n"
|
||||
"\n");
|
||||
emu->addEmulatedFunction(EOpMod, float3, float1,
|
||||
"float3 webgl_mod_emu(float3 x, float y)\n"
|
||||
"{\n"
|
||||
" return x - y * floor(x / y);\n"
|
||||
"}\n"
|
||||
"\n");
|
||||
emu->addEmulatedFunction(EOpMod, float4, float4,
|
||||
"float4 webgl_mod_emu(float4 x, float4 y)\n"
|
||||
"{\n"
|
||||
" return x - y * floor(x / y);\n"
|
||||
"}\n"
|
||||
"\n");
|
||||
emu->addEmulatedFunction(EOpMod, float4, float1,
|
||||
"float4 webgl_mod_emu(float4 x, float y)\n"
|
||||
"{\n"
|
||||
" return x - y * floor(x / y);\n"
|
||||
"}\n"
|
||||
"\n");
|
||||
|
||||
emu->addEmulatedFunction(EOpFaceForward, float1, float1, float1,
|
||||
"float webgl_faceforward_emu(float N, float I, float Nref)\n"
|
||||
"{\n"
|
||||
" if(dot(Nref, I) >= 0)\n"
|
||||
" {\n"
|
||||
" return -N;\n"
|
||||
" }\n"
|
||||
" else\n"
|
||||
" {\n"
|
||||
" return N;\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"\n");
|
||||
emu->addEmulatedFunction(EOpFaceForward, float2, float2, float2,
|
||||
"float2 webgl_faceforward_emu(float2 N, float2 I, float2 Nref)\n"
|
||||
"{\n"
|
||||
" if(dot(Nref, I) >= 0)\n"
|
||||
" {\n"
|
||||
" return -N;\n"
|
||||
" }\n"
|
||||
" else\n"
|
||||
" {\n"
|
||||
" return N;\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"\n");
|
||||
emu->addEmulatedFunction(EOpFaceForward, float3, float3, float3,
|
||||
"float3 webgl_faceforward_emu(float3 N, float3 I, float3 Nref)\n"
|
||||
"{\n"
|
||||
" if(dot(Nref, I) >= 0)\n"
|
||||
" {\n"
|
||||
" return -N;\n"
|
||||
" }\n"
|
||||
" else\n"
|
||||
" {\n"
|
||||
" return N;\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"\n");
|
||||
emu->addEmulatedFunction(EOpFaceForward, float4, float4, float4,
|
||||
"float4 webgl_faceforward_emu(float4 N, float4 I, float4 Nref)\n"
|
||||
"{\n"
|
||||
" if(dot(Nref, I) >= 0)\n"
|
||||
" {\n"
|
||||
" return -N;\n"
|
||||
" }\n"
|
||||
" else\n"
|
||||
" {\n"
|
||||
" return N;\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"\n");
|
||||
|
||||
emu->addEmulatedFunction(EOpAtan, float1, float1,
|
||||
"float webgl_atan_emu(float y, float x)\n"
|
||||
"{\n"
|
||||
" if(x == 0 && y == 0) x = 1;\n" // Avoid producing a NaN
|
||||
" return atan2(y, x);\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpAtan, float2, float2,
|
||||
"float2 webgl_atan_emu(float2 y, float2 x)\n"
|
||||
"{\n"
|
||||
" if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
|
||||
" if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
|
||||
" return float2(atan2(y[0], x[0]), atan2(y[1], x[1]));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpAtan, float3, float3,
|
||||
"float3 webgl_atan_emu(float3 y, float3 x)\n"
|
||||
"{\n"
|
||||
" if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
|
||||
" if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
|
||||
" if(x[2] == 0 && y[2] == 0) x[2] = 1;\n"
|
||||
" return float3(atan2(y[0], x[0]), atan2(y[1], x[1]), atan2(y[2], x[2]));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpAtan, float4, float4,
|
||||
"float4 webgl_atan_emu(float4 y, float4 x)\n"
|
||||
"{\n"
|
||||
" if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
|
||||
" if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
|
||||
" if(x[2] == 0 && y[2] == 0) x[2] = 1;\n"
|
||||
" if(x[3] == 0 && y[3] == 0) x[3] = 1;\n"
|
||||
" return float4(atan2(y[0], x[0]), atan2(y[1], x[1]), atan2(y[2], x[2]), atan2(y[3], x[3]));\n"
|
||||
"}\n");
|
||||
|
||||
emu->addEmulatedFunction(EOpAsinh, float1,
|
||||
"float webgl_asinh_emu(in float x) {\n"
|
||||
" return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpAsinh, float2,
|
||||
"float2 webgl_asinh_emu(in float2 x) {\n"
|
||||
" return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpAsinh, float3,
|
||||
"float3 webgl_asinh_emu(in float3 x) {\n"
|
||||
" return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpAsinh, float4,
|
||||
"float4 webgl_asinh_emu(in float4 x) {\n"
|
||||
" return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
|
||||
"}\n");
|
||||
|
||||
emu->addEmulatedFunction(EOpAcosh, float1,
|
||||
"float webgl_acosh_emu(in float x) {\n"
|
||||
" return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpAcosh, float2,
|
||||
"float2 webgl_acosh_emu(in float2 x) {\n"
|
||||
" return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpAcosh, float3,
|
||||
"float3 webgl_acosh_emu(in float3 x) {\n"
|
||||
" return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpAcosh, float4,
|
||||
"float4 webgl_acosh_emu(in float4 x) {\n"
|
||||
" return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
|
||||
"}\n");
|
||||
|
||||
emu->addEmulatedFunction(EOpAtanh, float1,
|
||||
"float webgl_atanh_emu(in float x) {\n"
|
||||
" return 0.5 * log((1.0 + x) / (1.0 - x));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpAtanh, float2,
|
||||
"float2 webgl_atanh_emu(in float2 x) {\n"
|
||||
" return 0.5 * log((1.0 + x) / (1.0 - x));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpAtanh, float3,
|
||||
"float3 webgl_atanh_emu(in float3 x) {\n"
|
||||
" return 0.5 * log((1.0 + x) / (1.0 - x));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpAtanh, float4,
|
||||
"float4 webgl_atanh_emu(in float4 x) {\n"
|
||||
" return 0.5 * log((1.0 + x) / (1.0 - x));\n"
|
||||
"}\n");
|
||||
|
||||
emu->addEmulatedFunction(EOpRoundEven, float1,
|
||||
"float webgl_roundEven_emu(in float x) {\n"
|
||||
" return (frac(x) == 0.5 && trunc(x) % 2.0 == 0.0) ? trunc(x) : round(x);\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpRoundEven, float2,
|
||||
"float2 webgl_roundEven_emu(in float2 x) {\n"
|
||||
" float2 v;\n"
|
||||
" v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);\n"
|
||||
" v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);\n"
|
||||
" return v;\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpRoundEven, float3,
|
||||
"float3 webgl_roundEven_emu(in float3 x) {\n"
|
||||
" float3 v;\n"
|
||||
" v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);\n"
|
||||
" v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);\n"
|
||||
" v[2] = (frac(x[2]) == 0.5 && trunc(x[2]) % 2.0 == 0.0) ? trunc(x[2]) : round(x[2]);\n"
|
||||
" return v;\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpRoundEven, float4,
|
||||
"float4 webgl_roundEven_emu(in float4 x) {\n"
|
||||
" float4 v;\n"
|
||||
" v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);\n"
|
||||
" v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);\n"
|
||||
" v[2] = (frac(x[2]) == 0.5 && trunc(x[2]) % 2.0 == 0.0) ? trunc(x[2]) : round(x[2]);\n"
|
||||
" v[3] = (frac(x[3]) == 0.5 && trunc(x[3]) % 2.0 == 0.0) ? trunc(x[3]) : round(x[3]);\n"
|
||||
" return v;\n"
|
||||
"}\n");
|
||||
|
||||
emu->addEmulatedFunction(EOpPackSnorm2x16, float2,
|
||||
"int webgl_toSnorm(in float x) {\n"
|
||||
" return int(round(clamp(x, -1.0, 1.0) * 32767.0));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"uint webgl_packSnorm2x16_emu(in float2 v) {\n"
|
||||
" int x = webgl_toSnorm(v.x);\n"
|
||||
" int y = webgl_toSnorm(v.y);\n"
|
||||
" return (asuint(y) << 16) | (asuint(x) & 0xffffu);\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpPackUnorm2x16, float2,
|
||||
"uint webgl_toUnorm(in float x) {\n"
|
||||
" return uint(round(clamp(x, 0.0, 1.0) * 65535.0));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"uint webgl_packUnorm2x16_emu(in float2 v) {\n"
|
||||
" uint x = webgl_toUnorm(v.x);\n"
|
||||
" uint y = webgl_toUnorm(v.y);\n"
|
||||
" return (y << 16) | x;\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpPackHalf2x16, float2,
|
||||
"uint webgl_packHalf2x16_emu(in float2 v) {\n"
|
||||
" uint x = f32tof16(v.x);\n"
|
||||
" uint y = f32tof16(v.y);\n"
|
||||
" return (y << 16) | x;\n"
|
||||
"}\n");
|
||||
|
||||
TType *uint1 = new TType(EbtUInt);
|
||||
|
||||
emu->addEmulatedFunction(EOpUnpackSnorm2x16, uint1,
|
||||
"float webgl_fromSnorm(in uint x) {\n"
|
||||
" int xi = asint(x & 0x7fffu) - asint(x & 0x8000u);\n"
|
||||
" return clamp(float(xi) / 32767.0, -1.0, 1.0);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"float2 webgl_unpackSnorm2x16_emu(in uint u) {\n"
|
||||
" uint y = (u >> 16);\n"
|
||||
" uint x = u;\n"
|
||||
" return float2(webgl_fromSnorm(x), webgl_fromSnorm(y));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpUnpackUnorm2x16, uint1,
|
||||
"float webgl_fromUnorm(in uint x) {\n"
|
||||
" return float(x) / 65535.0;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"float2 webgl_unpackUnorm2x16_emu(in uint u) {\n"
|
||||
" uint y = (u >> 16);\n"
|
||||
" uint x = u & 0xffffu;\n"
|
||||
" return float2(webgl_fromUnorm(x), webgl_fromUnorm(y));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpUnpackHalf2x16, uint1,
|
||||
"float2 webgl_unpackHalf2x16_emu(in uint u) {\n"
|
||||
" uint y = (u >> 16);\n"
|
||||
" uint x = u & 0xffffu;\n"
|
||||
" return float2(f16tof32(x), f16tof32(y));\n"
|
||||
"}\n");
|
||||
|
||||
// The matrix resulting from outer product needs to be transposed
|
||||
// (matrices are stored as transposed to simplify element access in HLSL).
|
||||
// So the function should return transpose(c * r) where c is a column vector
|
||||
// and r is a row vector. This can be simplified by using the following
|
||||
// formula:
|
||||
// transpose(c * r) = transpose(r) * transpose(c)
|
||||
// transpose(r) and transpose(c) are in a sense free, since to get the
|
||||
// transpose of r, we simply can build a column matrix out of the original
|
||||
// vector instead of a row matrix.
|
||||
emu->addEmulatedFunction(EOpOuterProduct, float2, float2,
|
||||
"float2x2 webgl_outerProduct_emu(in float2 c, in float2 r) {\n"
|
||||
" return mul(float2x1(r), float1x2(c));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpOuterProduct, float3, float3,
|
||||
"float3x3 webgl_outerProduct_emu(in float3 c, in float3 r) {\n"
|
||||
" return mul(float3x1(r), float1x3(c));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpOuterProduct, float4, float4,
|
||||
"float4x4 webgl_outerProduct_emu(in float4 c, in float4 r) {\n"
|
||||
" return mul(float4x1(r), float1x4(c));\n"
|
||||
"}\n");
|
||||
|
||||
emu->addEmulatedFunction(EOpOuterProduct, float3, float2,
|
||||
"float2x3 webgl_outerProduct_emu(in float3 c, in float2 r) {\n"
|
||||
" return mul(float2x1(r), float1x3(c));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpOuterProduct, float2, float3,
|
||||
"float3x2 webgl_outerProduct_emu(in float2 c, in float3 r) {\n"
|
||||
" return mul(float3x1(r), float1x2(c));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpOuterProduct, float4, float2,
|
||||
"float2x4 webgl_outerProduct_emu(in float4 c, in float2 r) {\n"
|
||||
" return mul(float2x1(r), float1x4(c));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpOuterProduct, float2, float4,
|
||||
"float4x2 webgl_outerProduct_emu(in float2 c, in float4 r) {\n"
|
||||
" return mul(float4x1(r), float1x2(c));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpOuterProduct, float4, float3,
|
||||
"float3x4 webgl_outerProduct_emu(in float4 c, in float3 r) {\n"
|
||||
" return mul(float3x1(r), float1x4(c));\n"
|
||||
"}\n");
|
||||
emu->addEmulatedFunction(EOpOuterProduct, float3, float4,
|
||||
"float4x3 webgl_outerProduct_emu(in float3 c, in float4 r) {\n"
|
||||
" return mul(float4x1(r), float1x3(c));\n"
|
||||
"}\n");
|
||||
|
||||
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
|
||||
// should also be the transpose of the inverse.
|
||||
|
||||
// When accessing the parameter matrix with m[a][b] it can be thought of so
|
||||
// that a is the column and b is the row of the matrix that we're inverting.
|
||||
|
||||
// We calculate the inverse as the adjugate matrix divided by the
|
||||
// determinant of the matrix being inverted. However, as the result needs
|
||||
// to be transposed, we actually use of the transpose of the adjugate matrix
|
||||
// which happens to be the cofactor matrix. That's stored in "cof".
|
||||
|
||||
// We don't need to care about divide-by-zero since results are undefined
|
||||
// for singular or poorly-conditioned matrices.
|
||||
|
||||
emu->addEmulatedFunction(EOpInverse, mat2,
|
||||
"float2x2 webgl_inverse_emu(in float2x2 m) {\n"
|
||||
" float2x2 cof = { m[1][1], -m[0][1], -m[1][0], m[0][0] };\n"
|
||||
" return cof / determinant(transpose(m));\n"
|
||||
"}\n");
|
||||
|
||||
// cofAB is the cofactor for column A and row B.
|
||||
|
||||
emu->addEmulatedFunction(EOpInverse, mat3,
|
||||
"float3x3 webgl_inverse_emu(in float3x3 m) {\n"
|
||||
" float cof00 = m[1][1] * m[2][2] - m[2][1] * m[1][2];\n"
|
||||
" float cof01 = -(m[1][0] * m[2][2] - m[2][0] * m[1][2]);\n"
|
||||
" float cof02 = m[1][0] * m[2][1] - m[2][0] * m[1][1];\n"
|
||||
" float cof10 = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]);\n"
|
||||
" float cof11 = m[0][0] * m[2][2] - m[2][0] * m[0][2];\n"
|
||||
" float cof12 = -(m[0][0] * m[2][1] - m[2][0] * m[0][1]);\n"
|
||||
" float cof20 = m[0][1] * m[1][2] - m[1][1] * m[0][2];\n"
|
||||
" float cof21 = -(m[0][0] * m[1][2] - m[1][0] * m[0][2]);\n"
|
||||
" float cof22 = m[0][0] * m[1][1] - m[1][0] * m[0][1];\n"
|
||||
" float3x3 cof = { cof00, cof10, cof20, cof01, cof11, cof21, cof02, cof12, cof22 };\n"
|
||||
" return cof / determinant(transpose(m));\n"
|
||||
"}\n");
|
||||
|
||||
emu->addEmulatedFunction(EOpInverse, mat4,
|
||||
"float4x4 webgl_inverse_emu(in float4x4 m) {\n"
|
||||
" float cof00 = m[1][1] * m[2][2] * m[3][3] + m[2][1] * m[3][2] * m[1][3] + m[3][1] * m[1][2] * m[2][3]"
|
||||
" - m[1][1] * m[3][2] * m[2][3] - m[2][1] * m[1][2] * m[3][3] - m[3][1] * m[2][2] * m[1][3];\n"
|
||||
" float cof01 = -(m[1][0] * m[2][2] * m[3][3] + m[2][0] * m[3][2] * m[1][3] + m[3][0] * m[1][2] * m[2][3]"
|
||||
" - m[1][0] * m[3][2] * m[2][3] - m[2][0] * m[1][2] * m[3][3] - m[3][0] * m[2][2] * m[1][3]);\n"
|
||||
" float cof02 = m[1][0] * m[2][1] * m[3][3] + m[2][0] * m[3][1] * m[1][3] + m[3][0] * m[1][1] * m[2][3]"
|
||||
" - m[1][0] * m[3][1] * m[2][3] - m[2][0] * m[1][1] * m[3][3] - m[3][0] * m[2][1] * m[1][3];\n"
|
||||
" float cof03 = -(m[1][0] * m[2][1] * m[3][2] + m[2][0] * m[3][1] * m[1][2] + m[3][0] * m[1][1] * m[2][2]"
|
||||
" - m[1][0] * m[3][1] * m[2][2] - m[2][0] * m[1][1] * m[3][2] - m[3][0] * m[2][1] * m[1][2]);\n"
|
||||
" float cof10 = -(m[0][1] * m[2][2] * m[3][3] + m[2][1] * m[3][2] * m[0][3] + m[3][1] * m[0][2] * m[2][3]"
|
||||
" - m[0][1] * m[3][2] * m[2][3] - m[2][1] * m[0][2] * m[3][3] - m[3][1] * m[2][2] * m[0][3]);\n"
|
||||
" float cof11 = m[0][0] * m[2][2] * m[3][3] + m[2][0] * m[3][2] * m[0][3] + m[3][0] * m[0][2] * m[2][3]"
|
||||
" - m[0][0] * m[3][2] * m[2][3] - m[2][0] * m[0][2] * m[3][3] - m[3][0] * m[2][2] * m[0][3];\n"
|
||||
" float cof12 = -(m[0][0] * m[2][1] * m[3][3] + m[2][0] * m[3][1] * m[0][3] + m[3][0] * m[0][1] * m[2][3]"
|
||||
" - m[0][0] * m[3][1] * m[2][3] - m[2][0] * m[0][1] * m[3][3] - m[3][0] * m[2][1] * m[0][3]);\n"
|
||||
" float cof13 = m[0][0] * m[2][1] * m[3][2] + m[2][0] * m[3][1] * m[0][2] + m[3][0] * m[0][1] * m[2][2]"
|
||||
" - m[0][0] * m[3][1] * m[2][2] - m[2][0] * m[0][1] * m[3][2] - m[3][0] * m[2][1] * m[0][2];\n"
|
||||
" float cof20 = m[0][1] * m[1][2] * m[3][3] + m[1][1] * m[3][2] * m[0][3] + m[3][1] * m[0][2] * m[1][3]"
|
||||
" - m[0][1] * m[3][2] * m[1][3] - m[1][1] * m[0][2] * m[3][3] - m[3][1] * m[1][2] * m[0][3];\n"
|
||||
" float cof21 = -(m[0][0] * m[1][2] * m[3][3] + m[1][0] * m[3][2] * m[0][3] + m[3][0] * m[0][2] * m[1][3]"
|
||||
" - m[0][0] * m[3][2] * m[1][3] - m[1][0] * m[0][2] * m[3][3] - m[3][0] * m[1][2] * m[0][3]);\n"
|
||||
" float cof22 = m[0][0] * m[1][1] * m[3][3] + m[1][0] * m[3][1] * m[0][3] + m[3][0] * m[0][1] * m[1][3]"
|
||||
" - m[0][0] * m[3][1] * m[1][3] - m[1][0] * m[0][1] * m[3][3] - m[3][0] * m[1][1] * m[0][3];\n"
|
||||
" float cof23 = -(m[0][0] * m[1][1] * m[3][2] + m[1][0] * m[3][1] * m[0][2] + m[3][0] * m[0][1] * m[1][2]"
|
||||
" - m[0][0] * m[3][1] * m[1][2] - m[1][0] * m[0][1] * m[3][2] - m[3][0] * m[1][1] * m[0][2]);\n"
|
||||
" float cof30 = -(m[0][1] * m[1][2] * m[2][3] + m[1][1] * m[2][2] * m[0][3] + m[2][1] * m[0][2] * m[1][3]"
|
||||
" - m[0][1] * m[2][2] * m[1][3] - m[1][1] * m[0][2] * m[2][3] - m[2][1] * m[1][2] * m[0][3]);\n"
|
||||
" float cof31 = m[0][0] * m[1][2] * m[2][3] + m[1][0] * m[2][2] * m[0][3] + m[2][0] * m[0][2] * m[1][3]"
|
||||
" - m[0][0] * m[2][2] * m[1][3] - m[1][0] * m[0][2] * m[2][3] - m[2][0] * m[1][2] * m[0][3];\n"
|
||||
" float cof32 = -(m[0][0] * m[1][1] * m[2][3] + m[1][0] * m[2][1] * m[0][3] + m[2][0] * m[0][1] * m[1][3]"
|
||||
" - m[0][0] * m[2][1] * m[1][3] - m[1][0] * m[0][1] * m[2][3] - m[2][0] * m[1][1] * m[0][3]);\n"
|
||||
" float cof33 = m[0][0] * m[1][1] * m[2][2] + m[1][0] * m[2][1] * m[0][2] + m[2][0] * m[0][1] * m[1][2]"
|
||||
" - m[0][0] * m[2][1] * m[1][2] - m[1][0] * m[0][1] * m[2][2] - m[2][0] * m[1][1] * m[0][2];\n"
|
||||
" float4x4 cof = { cof00, cof10, cof20, cof30, cof01, cof11, cof21, cof31,"
|
||||
" 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");
|
||||
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORHLSL_H_
|
||||
#define COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORHLSL_H_
|
||||
|
||||
#include "GLSLANG/ShaderLang.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
class BuiltInFunctionEmulator;
|
||||
|
||||
void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu);
|
||||
|
||||
//
|
||||
// This works around isnan() bug on some Intel drivers.
|
||||
//
|
||||
void InitBuiltInIsnanFunctionEmulatorForHLSLWorkarounds(BuiltInFunctionEmulator *emu,
|
||||
int targetGLSLVersion);
|
||||
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORHLSL_H_
|
||||
104
gfx/angle/src/compiler/translator/Cache.cpp
Normal file
104
gfx/angle/src/compiler/translator/Cache.cpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
//
|
||||
// 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 sh
|
||||
{
|
||||
|
||||
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();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
95
gfx/angle/src/compiler/translator/Cache.h
Normal file
95
gfx/angle/src/compiler/translator/Cache.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
//
|
||||
// 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"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_CACHE_H_
|
||||
348
gfx/angle/src/compiler/translator/CallDAG.cpp
Normal file
348
gfx/angle/src/compiler/translator/CallDAG.cpp
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
//
|
||||
// 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"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
// 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->getFunctionSymbolInfo()->getId()] =
|
||||
static_cast<int>(data.index);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
struct CreatorFunctionData
|
||||
{
|
||||
CreatorFunctionData()
|
||||
: node(nullptr),
|
||||
index(0),
|
||||
indexAssigned(false),
|
||||
visiting(false)
|
||||
{
|
||||
}
|
||||
|
||||
std::set<CreatorFunctionData*> callees;
|
||||
TIntermFunctionDefinition *node;
|
||||
TString name;
|
||||
size_t index;
|
||||
bool indexAssigned;
|
||||
bool visiting;
|
||||
};
|
||||
|
||||
bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
|
||||
{
|
||||
// Create the record if need be and remember the node.
|
||||
if (visit == PreVisit)
|
||||
{
|
||||
auto it = mFunctions.find(node->getFunctionSymbolInfo()->getName());
|
||||
|
||||
if (it == mFunctions.end())
|
||||
{
|
||||
mCurrentFunction = &mFunctions[node->getFunctionSymbolInfo()->getName()];
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentFunction = &it->second;
|
||||
}
|
||||
|
||||
mCurrentFunction->node = node;
|
||||
mCurrentFunction->name = node->getFunctionSymbolInfo()->getName();
|
||||
}
|
||||
else if (visit == PostVisit)
|
||||
{
|
||||
mCurrentFunction = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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->getFunctionSymbolInfo()->getName()];
|
||||
record.name = node->getFunctionSymbolInfo()->getName();
|
||||
}
|
||||
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->getFunctionSymbolInfo()->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 *root)
|
||||
{
|
||||
// Iterative implementation of the index assignment algorithm. A recursive version
|
||||
// would be prettier but since the CallDAG creation runs before the limiting of the
|
||||
// call depth, we might get stack overflows (computation of the call depth uses the
|
||||
// CallDAG).
|
||||
|
||||
ASSERT(root);
|
||||
|
||||
if (root->indexAssigned)
|
||||
{
|
||||
return INITDAG_SUCCESS;
|
||||
}
|
||||
|
||||
// If we didn't have to detect recursion, functionsToProcess could be a simple queue
|
||||
// in which we add the function being processed's callees. However in order to detect
|
||||
// recursion we need to know which functions we are currently visiting. For that reason
|
||||
// functionsToProcess will look like a concatenation of segments of the form
|
||||
// [F visiting = true, subset of F callees with visiting = false] and the following
|
||||
// segment (if any) will be start with a callee of F.
|
||||
// This way we can remember when we started visiting a function, to put visiting back
|
||||
// to false.
|
||||
TVector<CreatorFunctionData *> functionsToProcess;
|
||||
functionsToProcess.push_back(root);
|
||||
|
||||
InitResult result = INITDAG_SUCCESS;
|
||||
|
||||
while (!functionsToProcess.empty())
|
||||
{
|
||||
CreatorFunctionData *function = functionsToProcess.back();
|
||||
|
||||
if (function->visiting)
|
||||
{
|
||||
function->visiting = false;
|
||||
function->index = mCurrentIndex++;
|
||||
function->indexAssigned = true;
|
||||
|
||||
functionsToProcess.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!function->node)
|
||||
{
|
||||
*mCreationInfo << "Undefined function '" << function->name
|
||||
<< ")' used in the following call chain:";
|
||||
result = INITDAG_UNDEFINED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (function->indexAssigned)
|
||||
{
|
||||
functionsToProcess.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
function->visiting = true;
|
||||
|
||||
for (auto callee : function->callees)
|
||||
{
|
||||
functionsToProcess.push_back(callee);
|
||||
|
||||
// Check if the callee is already being visited after pushing it so that it appears
|
||||
// in the chain printed in the info log.
|
||||
if (callee->visiting)
|
||||
{
|
||||
*mCreationInfo << "Recursive function call in the following call chain:";
|
||||
result = INITDAG_RECURSION;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result != INITDAG_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The call chain is made of the function we were visiting when the error was detected.
|
||||
if (result != INITDAG_SUCCESS)
|
||||
{
|
||||
bool first = true;
|
||||
for (auto function : functionsToProcess)
|
||||
{
|
||||
if (function->visiting)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
*mCreationInfo << " -> ";
|
||||
}
|
||||
*mCreationInfo << function->name << ")";
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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 TFunctionSymbolInfo *functionInfo) const
|
||||
{
|
||||
auto it = mFunctionIdToIndex.find(functionInfo->getId());
|
||||
|
||||
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->getFunctionSymbolInfo());
|
||||
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)
|
||||
{
|
||||
ASSERT(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;
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
79
gfx/angle/src/compiler/translator/CallDAG.h
Normal file
79
gfx/angle/src/compiler/translator/CallDAG.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
//
|
||||
// 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"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
// 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;
|
||||
TIntermFunctionDefinition *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 TFunctionSymbolInfo *functionInfo) 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;
|
||||
};
|
||||
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_CALLDAG_H_
|
||||
84
gfx/angle/src/compiler/translator/CodeGen.cpp
Normal file
84
gfx/angle/src/compiler/translator/CodeGen.cpp
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// Copyright (c) 2013 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.
|
||||
//
|
||||
|
||||
#ifdef ANGLE_ENABLE_ESSL
|
||||
#include "compiler/translator/TranslatorESSL.h"
|
||||
#endif // ANGLE_ENABLE_ESSL
|
||||
|
||||
#ifdef ANGLE_ENABLE_GLSL
|
||||
#include "compiler/translator/TranslatorGLSL.h"
|
||||
#endif // ANGLE_ENABLE_GLSL
|
||||
|
||||
#ifdef ANGLE_ENABLE_HLSL
|
||||
#include "compiler/translator/TranslatorHLSL.h"
|
||||
#endif // ANGLE_ENABLE_HLSL
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
//
|
||||
// This function must be provided to create the actual
|
||||
// compile object used by higher level code. It returns
|
||||
// a subclass of TCompiler.
|
||||
//
|
||||
TCompiler *ConstructCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
|
||||
{
|
||||
switch (output)
|
||||
{
|
||||
case SH_ESSL_OUTPUT:
|
||||
#ifdef ANGLE_ENABLE_ESSL
|
||||
return new TranslatorESSL(type, spec);
|
||||
#else
|
||||
// This compiler is not supported in this configuration. Return NULL per the
|
||||
// sh::ConstructCompiler 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);
|
||||
#else
|
||||
// This compiler is not supported in this configuration. Return NULL per the
|
||||
// sh::ConstructCompiler 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
|
||||
// sh::ConstructCompiler API.
|
||||
return nullptr;
|
||||
#endif // ANGLE_ENABLE_HLSL
|
||||
|
||||
default:
|
||||
// Unknown format. Return NULL per the sh::ConstructCompiler API.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Delete the compiler made by ConstructCompiler
|
||||
//
|
||||
void DeleteCompiler(TCompiler *compiler)
|
||||
{
|
||||
SafeDelete(compiler);
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
100
gfx/angle/src/compiler/translator/Common.h
Normal file
100
gfx/angle/src/compiler/translator/Common.h
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// Copyright (c) 2002-2010 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_TRANSLATOR_COMMON_H_
|
||||
#define COMPILER_TRANSLATOR_COMMON_H_
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common/angleutils.h"
|
||||
#include "common/debug.h"
|
||||
#include "compiler/translator/PoolAlloc.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
struct TSourceLoc {
|
||||
int first_file;
|
||||
int first_line;
|
||||
int last_file;
|
||||
int last_line;
|
||||
};
|
||||
|
||||
//
|
||||
// Put POOL_ALLOCATOR_NEW_DELETE in base classes to make them use this scheme.
|
||||
//
|
||||
#define POOL_ALLOCATOR_NEW_DELETE() \
|
||||
void* operator new(size_t s) { return GetGlobalPoolAllocator()->allocate(s); } \
|
||||
void* operator new(size_t, void *_Where) { return (_Where); } \
|
||||
void operator delete(void*) { } \
|
||||
void operator delete(void *, void *) { } \
|
||||
void* operator new[](size_t s) { return GetGlobalPoolAllocator()->allocate(s); } \
|
||||
void* operator new[](size_t, void *_Where) { return (_Where); } \
|
||||
void operator delete[](void*) { } \
|
||||
void operator delete[](void *, void *) { }
|
||||
|
||||
//
|
||||
// Pool version of string.
|
||||
//
|
||||
typedef pool_allocator<char> TStringAllocator;
|
||||
typedef std::basic_string <char, std::char_traits<char>, TStringAllocator> TString;
|
||||
typedef std::basic_ostringstream<char, std::char_traits<char>, TStringAllocator> TStringStream;
|
||||
inline TString* NewPoolTString(const char* s)
|
||||
{
|
||||
void* memory = GetGlobalPoolAllocator()->allocate(sizeof(TString));
|
||||
return new(memory) TString(s);
|
||||
}
|
||||
|
||||
//
|
||||
// Persistent string memory. Should only be used for strings that survive
|
||||
// across compiles.
|
||||
//
|
||||
#define TPersistString std::string
|
||||
#define TPersistStringStream std::ostringstream
|
||||
|
||||
//
|
||||
// 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 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
|
||||
TMap(const tAllocator& a) : std::map<K, D, CMP, tAllocator>(std::map<K, D, CMP, tAllocator>::key_compare(), a) {}
|
||||
};
|
||||
|
||||
// Integer to TString conversion
|
||||
template <typename T>
|
||||
inline TString str(T i)
|
||||
{
|
||||
ASSERT(std::numeric_limits<T>::is_integer);
|
||||
char buffer[((8 * sizeof(T)) / 3) + 3];
|
||||
const char *formatStr = std::numeric_limits<T>::is_signed ? "%d" : "%u";
|
||||
snprintf(buffer, sizeof(buffer), formatStr, i);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_COMMON_H_
|
||||
1015
gfx/angle/src/compiler/translator/Compiler.cpp
Normal file
1015
gfx/angle/src/compiler/translator/Compiler.cpp
Normal file
File diff suppressed because it is too large
Load diff
274
gfx/angle/src/compiler/translator/Compiler.h
Normal file
274
gfx/angle/src/compiler/translator/Compiler.h
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
//
|
||||
// Copyright (c) 2002-2013 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.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_TRANSLATOR_COMPILER_H_
|
||||
#define COMPILER_TRANSLATOR_COMPILER_H_
|
||||
|
||||
//
|
||||
// Machine independent part of the compiler private objects
|
||||
// sent as ShHandle to the driver.
|
||||
//
|
||||
// This should not be included by driver code.
|
||||
//
|
||||
|
||||
#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"
|
||||
#include "compiler/translator/Pragma.h"
|
||||
#include "compiler/translator/SymbolTable.h"
|
||||
#include "compiler/translator/VariableInfo.h"
|
||||
#include "third_party/compiler/ArrayBoundsClamper.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
class TCompiler;
|
||||
#ifdef ANGLE_ENABLE_HLSL
|
||||
class TranslatorHLSL;
|
||||
#endif // ANGLE_ENABLE_HLSL
|
||||
|
||||
//
|
||||
// Helper function to identify specs that are based on the WebGL spec.
|
||||
//
|
||||
bool IsWebGLBasedSpec(ShShaderSpec spec);
|
||||
|
||||
//
|
||||
// Helper function to check if the shader type is GLSL.
|
||||
//
|
||||
bool IsGLSL130OrNewer(ShShaderOutput output);
|
||||
bool IsGLSL420OrNewer(ShShaderOutput output);
|
||||
bool IsGLSL410OrOlder(ShShaderOutput output);
|
||||
|
||||
//
|
||||
// Helper function to check if the invariant qualifier can be removed.
|
||||
//
|
||||
bool RemoveInvariant(sh::GLenum shaderType,
|
||||
int shaderVersion,
|
||||
ShShaderOutput outputType,
|
||||
ShCompileOptions compileOptions);
|
||||
|
||||
//
|
||||
// The base class used to back handles returned to the driver.
|
||||
//
|
||||
class TShHandleBase {
|
||||
public:
|
||||
TShHandleBase();
|
||||
virtual ~TShHandleBase();
|
||||
virtual TCompiler* getAsCompiler() { return 0; }
|
||||
#ifdef ANGLE_ENABLE_HLSL
|
||||
virtual TranslatorHLSL* getAsTranslatorHLSL() { return 0; }
|
||||
#endif // ANGLE_ENABLE_HLSL
|
||||
|
||||
protected:
|
||||
// Memory allocator. Allocates and tracks memory required by the compiler.
|
||||
// Deallocates all memory when compiler is destructed.
|
||||
TPoolAllocator allocator;
|
||||
};
|
||||
|
||||
//
|
||||
// The base class for the machine dependent compiler to derive from
|
||||
// for managing object code from the compile.
|
||||
//
|
||||
class TCompiler : public TShHandleBase
|
||||
{
|
||||
public:
|
||||
TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
|
||||
~TCompiler() override;
|
||||
TCompiler *getAsCompiler() override { return this; }
|
||||
|
||||
bool Init(const ShBuiltInResources& resources);
|
||||
|
||||
// compileTreeForTesting should be used only when tests require access to
|
||||
// the AST. Users of this function need to manually manage the global pool
|
||||
// allocator. Returns nullptr whenever there are compilation errors.
|
||||
TIntermBlock *compileTreeForTesting(const char *const shaderStrings[],
|
||||
size_t numStrings,
|
||||
ShCompileOptions compileOptions);
|
||||
|
||||
bool compile(const char *const shaderStrings[],
|
||||
size_t numStrings,
|
||||
ShCompileOptions compileOptions);
|
||||
|
||||
// Get results of the last compilation.
|
||||
int getShaderVersion() const { return shaderVersion; }
|
||||
TInfoSink& getInfoSink() { return infoSink; }
|
||||
|
||||
bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
|
||||
const sh::WorkGroupSize &getComputeShaderLocalSize() { return mComputeShaderLocalSize; }
|
||||
|
||||
// Clears the results from the previous compilation.
|
||||
void clearResults();
|
||||
|
||||
const std::vector<sh::Attribute> &getAttributes() const { return attributes; }
|
||||
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; }
|
||||
|
||||
ShHashFunction64 getHashFunction() const { return hashFunction; }
|
||||
NameMap& getNameMap() { return nameMap; }
|
||||
TSymbolTable& getSymbolTable() { return symbolTable; }
|
||||
ShShaderSpec getShaderSpec() const { return shaderSpec; }
|
||||
ShShaderOutput getOutputType() const { return outputType; }
|
||||
const std::string &getBuiltInResourcesString() const { return builtInResourcesString; }
|
||||
|
||||
bool shouldRunLoopAndIndexingValidation(ShCompileOptions compileOptions) const;
|
||||
|
||||
// Get the resources set by InitBuiltInSymbolTable
|
||||
const ShBuiltInResources& getResources() const;
|
||||
|
||||
protected:
|
||||
sh::GLenum getShaderType() const { return shaderType; }
|
||||
// Initialize symbol-table with built-in symbols.
|
||||
bool InitBuiltInSymbolTable(const ShBuiltInResources& resources);
|
||||
// Compute the string representation of the built-in resources
|
||||
void setResourceString();
|
||||
// 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);
|
||||
// Returns true if the given shader does not exceed the minimum
|
||||
// functionality mandated in GLSL 1.0 spec Appendix A.
|
||||
bool validateLimitations(TIntermNode* root);
|
||||
// Add emulated functions to the built-in function emulator.
|
||||
virtual void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
|
||||
ShCompileOptions compileOptions){};
|
||||
// Translate to object code.
|
||||
virtual void translate(TIntermNode *root, ShCompileOptions compileOptions) = 0;
|
||||
// Returns true if, after applying the packing rules in the GLSL 1.017 spec
|
||||
// Appendix A, section 7, the shader does not use too many uniforms.
|
||||
bool enforcePackingRestrictions();
|
||||
// Insert statements to reference all members in unused uniform blocks with standard and shared
|
||||
// layout. This is to work around a Mac driver that treats unused standard/shared
|
||||
// uniform blocks as inactive.
|
||||
void useAllMembersInUnusedStandardAndSharedBlocks(TIntermNode *root);
|
||||
// Insert statements to initialize output variables in the beginning of main().
|
||||
// This is to avoid undefined behaviors.
|
||||
void initializeOutputVariables(TIntermNode *root);
|
||||
// Insert gl_Position = vec4(0,0,0,0) to the beginning of main().
|
||||
// It is to work around a Linux driver bug where missing this causes compile failure
|
||||
// while spec says it is allowed.
|
||||
// This function should only be applied to vertex shaders.
|
||||
void initializeGLPosition(TIntermNode* root);
|
||||
// Return true if the maximum expression complexity is below the limit.
|
||||
bool limitExpressionComplexity(TIntermNode* root);
|
||||
// Get built-in extensions with default behavior.
|
||||
const TExtensionBehavior& getExtensionBehavior() const;
|
||||
const char *getSourcePath() const;
|
||||
const TPragma& getPragma() const { return mPragma; }
|
||||
void writePragma(ShCompileOptions compileOptions);
|
||||
unsigned int *getTemporaryIndex() { return &mTemporaryIndex; }
|
||||
// Relies on collectVariables having been called.
|
||||
bool isVaryingDefined(const char *varyingName);
|
||||
|
||||
const ArrayBoundsClamper& getArrayBoundsClamper() const;
|
||||
ShArrayIndexClampingStrategy getArrayIndexClampingStrategy() const;
|
||||
const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const;
|
||||
|
||||
virtual bool shouldFlattenPragmaStdglInvariantAll() = 0;
|
||||
virtual bool shouldCollectVariables(ShCompileOptions compileOptions);
|
||||
|
||||
bool wereVariablesCollected() const;
|
||||
std::vector<sh::Attribute> attributes;
|
||||
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;
|
||||
|
||||
private:
|
||||
// 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);
|
||||
|
||||
// Collect info for all attribs, uniforms, varyings.
|
||||
void collectVariables(TIntermNode *root);
|
||||
|
||||
bool variablesCollected;
|
||||
|
||||
// Removes unused function declarations and prototypes from the AST
|
||||
class UnusedPredicate;
|
||||
bool pruneUnusedFunctions(TIntermBlock *root);
|
||||
|
||||
TIntermBlock *compileTreeImpl(const char *const shaderStrings[],
|
||||
size_t numStrings,
|
||||
const ShCompileOptions 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;
|
||||
int maxFunctionParameters;
|
||||
|
||||
ShBuiltInResources compileResources;
|
||||
std::string builtInResourcesString;
|
||||
|
||||
// Built-in symbol table for the given language, spec, and resources.
|
||||
// It is preserved from compile-to-compile.
|
||||
TSymbolTable symbolTable;
|
||||
// Built-in extensions with default behavior.
|
||||
TExtensionBehavior extensionBehavior;
|
||||
bool fragmentPrecisionHigh;
|
||||
|
||||
ArrayBoundsClamper arrayBoundsClamper;
|
||||
ShArrayIndexClampingStrategy clampingStrategy;
|
||||
BuiltInFunctionEmulator builtInFunctionEmulator;
|
||||
|
||||
// Results of compilation.
|
||||
int shaderVersion;
|
||||
TInfoSink infoSink; // Output sink.
|
||||
const char *mSourcePath; // Path of source file or NULL
|
||||
|
||||
// compute shader local group size
|
||||
bool mComputeShaderLocalSizeDeclared;
|
||||
sh::WorkGroupSize mComputeShaderLocalSize;
|
||||
|
||||
// name hashing.
|
||||
ShHashFunction64 hashFunction;
|
||||
NameMap nameMap;
|
||||
|
||||
TPragma mPragma;
|
||||
|
||||
unsigned int mTemporaryIndex;
|
||||
};
|
||||
|
||||
//
|
||||
// This is the interface between the machine independent code
|
||||
// and the machine dependent code.
|
||||
//
|
||||
// The machine dependent code should derive from the classes
|
||||
// above. Then Construct*() and Delete*() will create and
|
||||
// destroy the machine dependent objects, which contain the
|
||||
// above machine independent information.
|
||||
//
|
||||
TCompiler* ConstructCompiler(
|
||||
sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
|
||||
void DeleteCompiler(TCompiler*);
|
||||
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_COMPILER_H_
|
||||
642
gfx/angle/src/compiler/translator/ConstantUnion.cpp
Normal file
642
gfx/angle/src/compiler/translator/ConstantUnion.cpp
Normal file
|
|
@ -0,0 +1,642 @@
|
|||
//
|
||||
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// ConstantUnion: Constant folding helper class.
|
||||
|
||||
#include "compiler/translator/ConstantUnion.h"
|
||||
|
||||
#include "base/numerics/safe_math.h"
|
||||
#include "common/mathutil.h"
|
||||
#include "compiler/translator/Diagnostics.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
T CheckedSum(base::CheckedNumeric<T> lhs,
|
||||
base::CheckedNumeric<T> rhs,
|
||||
TDiagnostics *diag,
|
||||
const TSourceLoc &line)
|
||||
{
|
||||
ASSERT(lhs.IsValid() && rhs.IsValid());
|
||||
auto result = lhs + rhs;
|
||||
if (!result.IsValid())
|
||||
{
|
||||
diag->error(line, "Addition out of range", "*", "");
|
||||
return 0;
|
||||
}
|
||||
return result.ValueOrDefault(0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T CheckedDiff(base::CheckedNumeric<T> lhs,
|
||||
base::CheckedNumeric<T> rhs,
|
||||
TDiagnostics *diag,
|
||||
const TSourceLoc &line)
|
||||
{
|
||||
ASSERT(lhs.IsValid() && rhs.IsValid());
|
||||
auto result = lhs - rhs;
|
||||
if (!result.IsValid())
|
||||
{
|
||||
diag->error(line, "Difference out of range", "*", "");
|
||||
return 0;
|
||||
}
|
||||
return result.ValueOrDefault(0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T CheckedMul(base::CheckedNumeric<T> lhs,
|
||||
base::CheckedNumeric<T> rhs,
|
||||
TDiagnostics *diag,
|
||||
const TSourceLoc &line)
|
||||
{
|
||||
ASSERT(lhs.IsValid() && rhs.IsValid());
|
||||
auto result = lhs * rhs;
|
||||
if (!result.IsValid())
|
||||
{
|
||||
diag->error(line, "Multiplication out of range", "*", "");
|
||||
return 0;
|
||||
}
|
||||
return result.ValueOrDefault(0);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TConstantUnion::TConstantUnion()
|
||||
{
|
||||
iConst = 0;
|
||||
type = EbtVoid;
|
||||
}
|
||||
|
||||
bool TConstantUnion::cast(TBasicType newType, const TConstantUnion &constant)
|
||||
{
|
||||
switch (newType)
|
||||
{
|
||||
case EbtFloat:
|
||||
switch (constant.type)
|
||||
{
|
||||
case EbtInt:
|
||||
setFConst(static_cast<float>(constant.getIConst()));
|
||||
break;
|
||||
case EbtUInt:
|
||||
setFConst(static_cast<float>(constant.getUConst()));
|
||||
break;
|
||||
case EbtBool:
|
||||
setFConst(static_cast<float>(constant.getBConst()));
|
||||
break;
|
||||
case EbtFloat:
|
||||
setFConst(static_cast<float>(constant.getFConst()));
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case EbtInt:
|
||||
switch (constant.type)
|
||||
{
|
||||
case EbtInt:
|
||||
setIConst(static_cast<int>(constant.getIConst()));
|
||||
break;
|
||||
case EbtUInt:
|
||||
setIConst(static_cast<int>(constant.getUConst()));
|
||||
break;
|
||||
case EbtBool:
|
||||
setIConst(static_cast<int>(constant.getBConst()));
|
||||
break;
|
||||
case EbtFloat:
|
||||
setIConst(static_cast<int>(constant.getFConst()));
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case EbtUInt:
|
||||
switch (constant.type)
|
||||
{
|
||||
case EbtInt:
|
||||
setUConst(static_cast<unsigned int>(constant.getIConst()));
|
||||
break;
|
||||
case EbtUInt:
|
||||
setUConst(static_cast<unsigned int>(constant.getUConst()));
|
||||
break;
|
||||
case EbtBool:
|
||||
setUConst(static_cast<unsigned int>(constant.getBConst()));
|
||||
break;
|
||||
case EbtFloat:
|
||||
setUConst(static_cast<unsigned int>(constant.getFConst()));
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case EbtBool:
|
||||
switch (constant.type)
|
||||
{
|
||||
case EbtInt:
|
||||
setBConst(constant.getIConst() != 0);
|
||||
break;
|
||||
case EbtUInt:
|
||||
setBConst(constant.getUConst() != 0);
|
||||
break;
|
||||
case EbtBool:
|
||||
setBConst(constant.getBConst());
|
||||
break;
|
||||
case EbtFloat:
|
||||
setBConst(constant.getFConst() != 0.0f);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case EbtStruct: // Struct fields don't get cast
|
||||
switch (constant.type)
|
||||
{
|
||||
case EbtInt:
|
||||
setIConst(constant.getIConst());
|
||||
break;
|
||||
case EbtUInt:
|
||||
setUConst(constant.getUConst());
|
||||
break;
|
||||
case EbtBool:
|
||||
setBConst(constant.getBConst());
|
||||
break;
|
||||
case EbtFloat:
|
||||
setFConst(constant.getFConst());
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TConstantUnion::operator==(const int i) const
|
||||
{
|
||||
return i == iConst;
|
||||
}
|
||||
|
||||
bool TConstantUnion::operator==(const unsigned int u) const
|
||||
{
|
||||
return u == uConst;
|
||||
}
|
||||
|
||||
bool TConstantUnion::operator==(const float f) const
|
||||
{
|
||||
return f == fConst;
|
||||
}
|
||||
|
||||
bool TConstantUnion::operator==(const bool b) const
|
||||
{
|
||||
return b == bConst;
|
||||
}
|
||||
|
||||
bool TConstantUnion::operator==(const TConstantUnion &constant) const
|
||||
{
|
||||
if (constant.type != type)
|
||||
return false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case EbtInt:
|
||||
return constant.iConst == iConst;
|
||||
case EbtUInt:
|
||||
return constant.uConst == uConst;
|
||||
case EbtFloat:
|
||||
return constant.fConst == fConst;
|
||||
case EbtBool:
|
||||
return constant.bConst == bConst;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool TConstantUnion::operator!=(const int i) const
|
||||
{
|
||||
return !operator==(i);
|
||||
}
|
||||
|
||||
bool TConstantUnion::operator!=(const unsigned int u) const
|
||||
{
|
||||
return !operator==(u);
|
||||
}
|
||||
|
||||
bool TConstantUnion::operator!=(const float f) const
|
||||
{
|
||||
return !operator==(f);
|
||||
}
|
||||
|
||||
bool TConstantUnion::operator!=(const bool b) const
|
||||
{
|
||||
return !operator==(b);
|
||||
}
|
||||
|
||||
bool TConstantUnion::operator!=(const TConstantUnion &constant) const
|
||||
{
|
||||
return !operator==(constant);
|
||||
}
|
||||
|
||||
bool TConstantUnion::operator>(const TConstantUnion &constant) const
|
||||
{
|
||||
ASSERT(type == constant.type);
|
||||
switch (type)
|
||||
{
|
||||
case EbtInt:
|
||||
return iConst > constant.iConst;
|
||||
case EbtUInt:
|
||||
return uConst > constant.uConst;
|
||||
case EbtFloat:
|
||||
return fConst > constant.fConst;
|
||||
default:
|
||||
return false; // Invalid operation, handled at semantic analysis
|
||||
}
|
||||
}
|
||||
|
||||
bool TConstantUnion::operator<(const TConstantUnion &constant) const
|
||||
{
|
||||
ASSERT(type == constant.type);
|
||||
switch (type)
|
||||
{
|
||||
case EbtInt:
|
||||
return iConst < constant.iConst;
|
||||
case EbtUInt:
|
||||
return uConst < constant.uConst;
|
||||
case EbtFloat:
|
||||
return fConst < constant.fConst;
|
||||
default:
|
||||
return false; // Invalid operation, handled at semantic analysis
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
TConstantUnion TConstantUnion::add(const TConstantUnion &lhs,
|
||||
const TConstantUnion &rhs,
|
||||
TDiagnostics *diag,
|
||||
const TSourceLoc &line)
|
||||
{
|
||||
TConstantUnion returnValue;
|
||||
ASSERT(lhs.type == rhs.type);
|
||||
switch (lhs.type)
|
||||
{
|
||||
case EbtInt:
|
||||
returnValue.setIConst(gl::WrappingSum<int>(lhs.iConst, rhs.iConst));
|
||||
break;
|
||||
case EbtUInt:
|
||||
returnValue.setUConst(gl::WrappingSum<unsigned int>(lhs.uConst, rhs.uConst));
|
||||
break;
|
||||
case EbtFloat:
|
||||
returnValue.setFConst(CheckedSum<float>(lhs.fConst, rhs.fConst, diag, line));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
// static
|
||||
TConstantUnion TConstantUnion::sub(const TConstantUnion &lhs,
|
||||
const TConstantUnion &rhs,
|
||||
TDiagnostics *diag,
|
||||
const TSourceLoc &line)
|
||||
{
|
||||
TConstantUnion returnValue;
|
||||
ASSERT(lhs.type == rhs.type);
|
||||
switch (lhs.type)
|
||||
{
|
||||
case EbtInt:
|
||||
returnValue.setIConst(gl::WrappingDiff<int>(lhs.iConst, rhs.iConst));
|
||||
break;
|
||||
case EbtUInt:
|
||||
returnValue.setUConst(gl::WrappingDiff<unsigned int>(lhs.uConst, rhs.uConst));
|
||||
break;
|
||||
case EbtFloat:
|
||||
returnValue.setFConst(CheckedDiff<float>(lhs.fConst, rhs.fConst, diag, line));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
// static
|
||||
TConstantUnion TConstantUnion::mul(const TConstantUnion &lhs,
|
||||
const TConstantUnion &rhs,
|
||||
TDiagnostics *diag,
|
||||
const TSourceLoc &line)
|
||||
{
|
||||
TConstantUnion returnValue;
|
||||
ASSERT(lhs.type == rhs.type);
|
||||
switch (lhs.type)
|
||||
{
|
||||
case EbtInt:
|
||||
returnValue.setIConst(gl::WrappingMul(lhs.iConst, rhs.iConst));
|
||||
break;
|
||||
case EbtUInt:
|
||||
// Unsigned integer math in C++ is defined to be done in modulo 2^n, so we rely on that
|
||||
// to implement wrapping multiplication.
|
||||
returnValue.setUConst(lhs.uConst * rhs.uConst);
|
||||
break;
|
||||
case EbtFloat:
|
||||
returnValue.setFConst(CheckedMul<float>(lhs.fConst, rhs.fConst, diag, line));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
TConstantUnion TConstantUnion::operator%(const TConstantUnion &constant) const
|
||||
{
|
||||
TConstantUnion returnValue;
|
||||
ASSERT(type == constant.type);
|
||||
switch (type)
|
||||
{
|
||||
case EbtInt:
|
||||
returnValue.setIConst(iConst % constant.iConst);
|
||||
break;
|
||||
case EbtUInt:
|
||||
returnValue.setUConst(uConst % constant.uConst);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
// static
|
||||
TConstantUnion TConstantUnion::rshift(const TConstantUnion &lhs,
|
||||
const TConstantUnion &rhs,
|
||||
TDiagnostics *diag,
|
||||
const TSourceLoc &line)
|
||||
{
|
||||
TConstantUnion returnValue;
|
||||
ASSERT(lhs.type == EbtInt || lhs.type == EbtUInt);
|
||||
ASSERT(rhs.type == EbtInt || rhs.type == EbtUInt);
|
||||
if ((rhs.type == EbtInt && (rhs.iConst < 0 || rhs.iConst > 31)) ||
|
||||
(rhs.type == EbtUInt && rhs.uConst > 31u))
|
||||
{
|
||||
diag->error(line, "Undefined shift (operand out of range)", ">>", "");
|
||||
switch (lhs.type)
|
||||
{
|
||||
case EbtInt:
|
||||
returnValue.setIConst(0);
|
||||
break;
|
||||
case EbtUInt:
|
||||
returnValue.setUConst(0u);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
switch (lhs.type)
|
||||
{
|
||||
case EbtInt:
|
||||
{
|
||||
unsigned int shiftOffset = 0;
|
||||
switch (rhs.type)
|
||||
{
|
||||
case EbtInt:
|
||||
shiftOffset = static_cast<unsigned int>(rhs.iConst);
|
||||
break;
|
||||
case EbtUInt:
|
||||
shiftOffset = rhs.uConst;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
if (shiftOffset > 0)
|
||||
{
|
||||
// ESSL 3.00.6 section 5.9: "If E1 is a signed integer, the right-shift will extend
|
||||
// the sign bit." In C++ shifting negative integers is undefined, so we implement
|
||||
// extending the sign bit manually.
|
||||
int lhsSafe = lhs.iConst;
|
||||
if (lhsSafe == std::numeric_limits<int>::min())
|
||||
{
|
||||
// The min integer needs special treatment because only bit it has set is the
|
||||
// sign bit, which we clear later to implement safe right shift of negative
|
||||
// numbers.
|
||||
lhsSafe = -0x40000000;
|
||||
--shiftOffset;
|
||||
}
|
||||
if (shiftOffset > 0)
|
||||
{
|
||||
bool extendSignBit = false;
|
||||
if (lhsSafe < 0)
|
||||
{
|
||||
extendSignBit = true;
|
||||
// Clear the sign bit so that bitshift right is defined in C++.
|
||||
lhsSafe &= 0x7fffffff;
|
||||
ASSERT(lhsSafe > 0);
|
||||
}
|
||||
returnValue.setIConst(lhsSafe >> shiftOffset);
|
||||
|
||||
// Manually fill in the extended sign bit if necessary.
|
||||
if (extendSignBit)
|
||||
{
|
||||
int extendedSignBit = static_cast<int>(0xffffffffu << (31 - shiftOffset));
|
||||
returnValue.setIConst(returnValue.getIConst() | extendedSignBit);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
returnValue.setIConst(lhsSafe);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
returnValue.setIConst(lhs.iConst);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EbtUInt:
|
||||
switch (rhs.type)
|
||||
{
|
||||
case EbtInt:
|
||||
returnValue.setUConst(lhs.uConst >> rhs.iConst);
|
||||
break;
|
||||
case EbtUInt:
|
||||
returnValue.setUConst(lhs.uConst >> rhs.uConst);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
// static
|
||||
TConstantUnion TConstantUnion::lshift(const TConstantUnion &lhs,
|
||||
const TConstantUnion &rhs,
|
||||
TDiagnostics *diag,
|
||||
const TSourceLoc &line)
|
||||
{
|
||||
TConstantUnion returnValue;
|
||||
ASSERT(lhs.type == EbtInt || lhs.type == EbtUInt);
|
||||
ASSERT(rhs.type == EbtInt || rhs.type == EbtUInt);
|
||||
if ((rhs.type == EbtInt && (rhs.iConst < 0 || rhs.iConst > 31)) ||
|
||||
(rhs.type == EbtUInt && rhs.uConst > 31u))
|
||||
{
|
||||
diag->error(line, "Undefined shift (operand out of range)", "<<", "");
|
||||
switch (lhs.type)
|
||||
{
|
||||
case EbtInt:
|
||||
returnValue.setIConst(0);
|
||||
break;
|
||||
case EbtUInt:
|
||||
returnValue.setUConst(0u);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
switch (lhs.type)
|
||||
{
|
||||
case EbtInt:
|
||||
switch (rhs.type)
|
||||
{
|
||||
// Cast to unsigned integer before shifting, since ESSL 3.00.6 section 5.9 says that
|
||||
// lhs is "interpreted as a bit pattern". This also avoids the possibility of signed
|
||||
// integer overflow or undefined shift of a negative integer.
|
||||
case EbtInt:
|
||||
returnValue.setIConst(
|
||||
static_cast<int>(static_cast<uint32_t>(lhs.iConst) << rhs.iConst));
|
||||
break;
|
||||
case EbtUInt:
|
||||
returnValue.setIConst(
|
||||
static_cast<int>(static_cast<uint32_t>(lhs.iConst) << rhs.uConst));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
break;
|
||||
|
||||
case EbtUInt:
|
||||
switch (rhs.type)
|
||||
{
|
||||
case EbtInt:
|
||||
returnValue.setUConst(lhs.uConst << rhs.iConst);
|
||||
break;
|
||||
case EbtUInt:
|
||||
returnValue.setUConst(lhs.uConst << rhs.uConst);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
TConstantUnion TConstantUnion::operator&(const TConstantUnion &constant) const
|
||||
{
|
||||
TConstantUnion returnValue;
|
||||
ASSERT(constant.type == EbtInt || constant.type == EbtUInt);
|
||||
switch (type)
|
||||
{
|
||||
case EbtInt:
|
||||
returnValue.setIConst(iConst & constant.iConst);
|
||||
break;
|
||||
case EbtUInt:
|
||||
returnValue.setUConst(uConst & constant.uConst);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
TConstantUnion TConstantUnion::operator|(const TConstantUnion &constant) const
|
||||
{
|
||||
TConstantUnion returnValue;
|
||||
ASSERT(type == constant.type);
|
||||
switch (type)
|
||||
{
|
||||
case EbtInt:
|
||||
returnValue.setIConst(iConst | constant.iConst);
|
||||
break;
|
||||
case EbtUInt:
|
||||
returnValue.setUConst(uConst | constant.uConst);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
TConstantUnion TConstantUnion::operator^(const TConstantUnion &constant) const
|
||||
{
|
||||
TConstantUnion returnValue;
|
||||
ASSERT(type == constant.type);
|
||||
switch (type)
|
||||
{
|
||||
case EbtInt:
|
||||
returnValue.setIConst(iConst ^ constant.iConst);
|
||||
break;
|
||||
case EbtUInt:
|
||||
returnValue.setUConst(uConst ^ constant.uConst);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
TConstantUnion TConstantUnion::operator&&(const TConstantUnion &constant) const
|
||||
{
|
||||
TConstantUnion returnValue;
|
||||
ASSERT(type == constant.type);
|
||||
switch (type)
|
||||
{
|
||||
case EbtBool:
|
||||
returnValue.setBConst(bConst && constant.bConst);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
TConstantUnion TConstantUnion::operator||(const TConstantUnion &constant) const
|
||||
{
|
||||
TConstantUnion returnValue;
|
||||
ASSERT(type == constant.type);
|
||||
switch (type)
|
||||
{
|
||||
case EbtBool:
|
||||
returnValue.setBConst(bConst || constant.bConst);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
91
gfx/angle/src/compiler/translator/ConstantUnion.h
Normal file
91
gfx/angle/src/compiler/translator/ConstantUnion.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
|
||||
#ifndef COMPILER_TRANSLATOR_CONSTANTUNION_H_
|
||||
#define COMPILER_TRANSLATOR_CONSTANTUNION_H_
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "compiler/translator/Common.h"
|
||||
#include "compiler/translator/BaseTypes.h"
|
||||
|
||||
namespace sh
|
||||
{
|
||||
|
||||
class TDiagnostics;
|
||||
|
||||
class TConstantUnion
|
||||
{
|
||||
public:
|
||||
POOL_ALLOCATOR_NEW_DELETE();
|
||||
TConstantUnion();
|
||||
|
||||
bool cast(TBasicType newType, const TConstantUnion &constant);
|
||||
|
||||
void setIConst(int i) {iConst = i; type = EbtInt; }
|
||||
void setUConst(unsigned int u) { uConst = u; type = EbtUInt; }
|
||||
void setFConst(float f) {fConst = f; type = EbtFloat; }
|
||||
void setBConst(bool b) {bConst = b; type = EbtBool; }
|
||||
|
||||
int getIConst() const { return iConst; }
|
||||
unsigned int getUConst() const { return uConst; }
|
||||
float getFConst() const { return fConst; }
|
||||
bool getBConst() const { return bConst; }
|
||||
|
||||
bool operator==(const int i) const;
|
||||
bool operator==(const unsigned int u) const;
|
||||
bool operator==(const float f) const;
|
||||
bool operator==(const bool b) const;
|
||||
bool operator==(const TConstantUnion &constant) const;
|
||||
bool operator!=(const int i) const;
|
||||
bool operator!=(const unsigned int u) const;
|
||||
bool operator!=(const float f) const;
|
||||
bool operator!=(const bool b) const;
|
||||
bool operator!=(const TConstantUnion &constant) const;
|
||||
bool operator>(const TConstantUnion &constant) const;
|
||||
bool operator<(const TConstantUnion &constant) const;
|
||||
static TConstantUnion add(const TConstantUnion &lhs,
|
||||
const TConstantUnion &rhs,
|
||||
TDiagnostics *diag,
|
||||
const TSourceLoc &line);
|
||||
static TConstantUnion sub(const TConstantUnion &lhs,
|
||||
const TConstantUnion &rhs,
|
||||
TDiagnostics *diag,
|
||||
const TSourceLoc &line);
|
||||
static TConstantUnion mul(const TConstantUnion &lhs,
|
||||
const TConstantUnion &rhs,
|
||||
TDiagnostics *diag,
|
||||
const TSourceLoc &line);
|
||||
TConstantUnion operator%(const TConstantUnion &constant) const;
|
||||
static TConstantUnion rshift(const TConstantUnion &lhs,
|
||||
const TConstantUnion &rhs,
|
||||
TDiagnostics *diag,
|
||||
const TSourceLoc &line);
|
||||
static TConstantUnion lshift(const TConstantUnion &lhs,
|
||||
const TConstantUnion &rhs,
|
||||
TDiagnostics *diag,
|
||||
const TSourceLoc &line);
|
||||
TConstantUnion operator&(const TConstantUnion &constant) const;
|
||||
TConstantUnion operator|(const TConstantUnion &constant) const;
|
||||
TConstantUnion operator^(const TConstantUnion &constant) const;
|
||||
TConstantUnion operator&&(const TConstantUnion &constant) const;
|
||||
TConstantUnion operator||(const TConstantUnion &constant) const;
|
||||
|
||||
TBasicType getType() const { return type; }
|
||||
private:
|
||||
union {
|
||||
int iConst; // used for ivec, scalar ints
|
||||
unsigned int uConst; // used for uvec, scalar uints
|
||||
bool bConst; // used for bvec, scalar bools
|
||||
float fConst; // used for vec, mat, scalar floats
|
||||
};
|
||||
|
||||
TBasicType type;
|
||||
};
|
||||
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_CONSTANTUNION_H_
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue