Commit c3907efa by Olli Etuaho Committed by Commit Bot

Always use custom float parsing in GLSL

We now always use custom parsing code for parsing floats in GLSL shaders. Previously this code was only used in corner cases that stringstream parsing did not handle according to the GLSL spec. This is slightly faster in compiler perftests, and results in a smaller binary as well. Some new test cases are added to make sure that the custom float parsing behaves correctly. BUG=chromium:849245 TEST=angle_unittests, angle_perftests Change-Id: I2a88ec6a8b427016e34519d72bc98216947a4c64 Reviewed-on: https://chromium-review.googlesource.com/1092697Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent 4b06c1e9
...@@ -65,12 +65,6 @@ bool Token::uValue(unsigned int *value) const ...@@ -65,12 +65,6 @@ bool Token::uValue(unsigned int *value) const
return numeric_lex_int(text, value); 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) std::ostream &operator<<(std::ostream &out, const Token &token)
{ {
if (token.hasLeadingSpace()) if (token.hasLeadingSpace())
......
...@@ -87,7 +87,6 @@ struct Token ...@@ -87,7 +87,6 @@ struct Token
// Returns false if the parsed value cannot fit into an int or float. // Returns false if the parsed value cannot fit into an int or float.
bool iValue(int *value) const; bool iValue(int *value) const;
bool uValue(unsigned int *value) const; bool uValue(unsigned int *value) const;
bool fValue(float *value) const;
int type; int type;
unsigned int flags; unsigned int flags;
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#ifndef COMPILER_PREPROCESSOR_NUMERICLEX_H_ #ifndef COMPILER_PREPROCESSOR_NUMERICLEX_H_
#define COMPILER_PREPROCESSOR_NUMERICLEX_H_ #define COMPILER_PREPROCESSOR_NUMERICLEX_H_
#include <cmath>
#include <sstream> #include <sstream>
namespace angle namespace angle
...@@ -48,22 +47,6 @@ bool numeric_lex_int(const std::string &str, IntType *value) ...@@ -48,22 +47,6 @@ bool numeric_lex_int(const std::string &str, IntType *value)
return !stream.fail(); return !stream.fail();
} }
template <typename FloatType>
bool numeric_lex_float(const std::string &str, FloatType *value)
{
std::istringstream stream(str);
// Android NDK forbids access to locales by always throwing instead of only accepting the C locale.
#if !defined(ANGLE_PLATFORM_ANDROID)
// Force "C" locale so that decimal character is always '.', and not dependent on the current
// locale.
stream.imbue(std::locale::classic());
#endif
stream >> (*value);
return !stream.fail() && std::isfinite(*value);
}
} // namespace pp } // namespace pp
} // namespace angle } // namespace angle
......
...@@ -56,6 +56,10 @@ float NumericLexFloat32OutOfRangeToInfinity(const std::string &str) ...@@ -56,6 +56,10 @@ float NumericLexFloat32OutOfRangeToInfinity(const std::string &str)
// The exponent offset reflects the position of the decimal point. // The exponent offset reflects the position of the decimal point.
int exponentOffset = -1; int exponentOffset = -1;
// This is just a counter for how many decimal digits are written to decimalMantissa.
int mantissaDecimalDigits = 0;
while (i < str.length()) while (i < str.length())
{ {
const char c = str[i]; const char c = str[i];
...@@ -83,6 +87,7 @@ float NumericLexFloat32OutOfRangeToInfinity(const std::string &str) ...@@ -83,6 +87,7 @@ float NumericLexFloat32OutOfRangeToInfinity(const std::string &str)
if (decimalMantissa <= (std::numeric_limits<unsigned int>::max() - 9u) / 10u) if (decimalMantissa <= (std::numeric_limits<unsigned int>::max() - 9u) / 10u)
{ {
decimalMantissa = decimalMantissa * 10u + digit; decimalMantissa = decimalMantissa * 10u + digit;
++mantissaDecimalDigits;
} }
if (!decimalPointSeen) if (!decimalPointSeen)
{ {
...@@ -162,12 +167,7 @@ float NumericLexFloat32OutOfRangeToInfinity(const std::string &str) ...@@ -162,12 +167,7 @@ float NumericLexFloat32OutOfRangeToInfinity(const std::string &str)
double value = decimalMantissa; double value = decimalMantissa;
// Calculate the exponent offset to normalize the mantissa. // Calculate the exponent offset to normalize the mantissa.
int normalizationExponentOffset = 0; int normalizationExponentOffset = 1 - mantissaDecimalDigits;
while (decimalMantissa >= 10u)
{
--normalizationExponentOffset;
decimalMantissa /= 10u;
}
// Apply the exponent. // Apply the exponent.
value *= std::pow(10.0, static_cast<double>(exponent + normalizationExponentOffset)); value *= std::pow(10.0, static_cast<double>(exponent + normalizationExponentOffset));
if (value > static_cast<double>(std::numeric_limits<float>::max())) if (value > static_cast<double>(std::numeric_limits<float>::max()))
...@@ -183,11 +183,7 @@ float NumericLexFloat32OutOfRangeToInfinity(const std::string &str) ...@@ -183,11 +183,7 @@ float NumericLexFloat32OutOfRangeToInfinity(const std::string &str)
bool strtof_clamp(const std::string &str, float *value) bool strtof_clamp(const std::string &str, float *value)
{ {
// Try the standard float parsing path first. // Custom float parsing that can handle the following corner cases:
bool success = angle::pp::numeric_lex_float(str, value);
// If the standard path doesn't succeed, take the path that can handle the following corner
// cases:
// 1. The decimal mantissa is very small but the exponent is very large, putting the resulting // 1. The decimal mantissa is very small but the exponent is very large, putting the resulting
// number inside the float range. // number inside the float range.
// 2. The decimal mantissa is very large but the exponent is very small, putting the resulting // 2. The decimal mantissa is very large but the exponent is very small, putting the resulting
...@@ -195,8 +191,7 @@ bool strtof_clamp(const std::string &str, float *value) ...@@ -195,8 +191,7 @@ bool strtof_clamp(const std::string &str, float *value)
// 3. The value is out-of-range and should be evaluated as infinity. // 3. The value is out-of-range and should be evaluated as infinity.
// 4. The value is too small and should be evaluated as zero. // 4. The value is too small and should be evaluated as zero.
// See ESSL 3.00.6 section 4.1.4 for the relevant specification. // See ESSL 3.00.6 section 4.1.4 for the relevant specification.
if (!success) *value = NumericLexFloat32OutOfRangeToInfinity(str);
*value = NumericLexFloat32OutOfRangeToInfinity(str);
return !gl::isInf(*value); return !gl::isInf(*value);
} }
......
...@@ -29,8 +29,6 @@ class StrtofClampParser ...@@ -29,8 +29,6 @@ class StrtofClampParser
} }
}; };
// NumericLexFloat32OutOfRangeToInfinity usually only comes to play in corner cases of parsing, but
// it's useful to test that it works as expected across the whole range of floats.
class NumericLexFloatParser class NumericLexFloatParser
{ {
public: public:
...@@ -153,6 +151,29 @@ TYPED_TEST(FloatLexTest, SlightlyAboveMaxFloat) ...@@ -153,6 +151,29 @@ TYPED_TEST(FloatLexTest, SlightlyAboveMaxFloat)
TYPED_TEST(FloatLexTest, SlightlyBelowMaxFloat) TYPED_TEST(FloatLexTest, SlightlyBelowMaxFloat)
{ {
ASSERT_FALSE(TestFixture::IsInfinity("3.4028e38")); ASSERT_FALSE(TestFixture::IsInfinity("3.4028e38"));
ASSERT_TRUE(TestFixture::ParsedMatches("3.4028e38", 3.4028e38f));
}
TYPED_TEST(FloatLexTest, SlightlyAboveMaxFloatLargerMantissa)
{
ASSERT_TRUE(TestFixture::IsInfinity("34.029e37"));
}
TYPED_TEST(FloatLexTest, SlightlyBelowMaxFloatLargerMantissa)
{
ASSERT_FALSE(TestFixture::IsInfinity("34.028e37"));
ASSERT_TRUE(TestFixture::ParsedMatches("34.028e37", 3.4028e38f));
}
TYPED_TEST(FloatLexTest, SlightlyAboveMaxFloatSmallerMantissa)
{
ASSERT_TRUE(TestFixture::IsInfinity("0.34029e39"));
}
TYPED_TEST(FloatLexTest, SlightlyBelowMaxFloatSmallerMantissa)
{
ASSERT_FALSE(TestFixture::IsInfinity("0.34028e39"));
ASSERT_TRUE(TestFixture::ParsedMatches("0.34028e39", 3.4028e38f));
} }
TYPED_TEST(FloatLexTest, SlightlyBelowMinSubnormalFloat) TYPED_TEST(FloatLexTest, SlightlyBelowMinSubnormalFloat)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment