aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlli Etuaho <oetuaho@nvidia.com>2014-10-21 16:42:57 +0300
committerJamie Madill <jmadill@chromium.org>2014-10-28 15:23:51 +0000
commitd2a67b9621bcbbaf5792f8ea764bdd5b43e3653b (patch)
tree2cc8fb7bcce6090ef83e3b9ea2034a11f652d93a
parente2fecdd38ebdd757d7af2fa40a51af4b8cb622f5 (diff)
downloadangle-d2a67b9621bcbbaf5792f8ea764bdd5b43e3653b.tar.gz
Fix precision tracking for built-in function return values
Previously, the type of the return value of all function calls was set to the type of the return value in the function signature. This did not carry precision information. This patch changes this so that the return value precision is set correctly for built-in functions. For single-argument math functions, it mostly depends on that addUnaryMath sets the type of the return value to be the same as the type of the operand. The type is replaced but the precision information from the operand type is retained when needed. For multi-argument math functions, precision is determined based on all the nodes in the aggregate after the type has been set. For texture functions, the precision is set according the sampler type as per ESSL 1.0 spec. For textureSize, the precision is always highp as per ESSL 3.0 spec. BUG=angle:787 Change-Id: I48448e3ffe38656b91177dee9b60dd07a03cd095 Reviewed-on: https://chromium-review.googlesource.com/224951 Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Nicolas Capens <capn@chromium.org> Tested-by: Olli Etuaho <oetuaho@nvidia.com>
-rw-r--r--src/compiler/translator/IntermNode.cpp54
-rw-r--r--src/compiler/translator/IntermNode.h4
-rw-r--r--src/compiler/translator/glslang.y29
-rw-r--r--src/compiler/translator/glslang_tab.cpp83
-rw-r--r--src/compiler/translator/glslang_tab.h2
-rw-r--r--tests/compiler_tests/TypeTracking_test.cpp199
6 files changed, 331 insertions, 40 deletions
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index bf8649c2..aa0f31d1 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -133,6 +133,14 @@ bool CompareStructure(const TType &leftNodeType,
//
////////////////////////////////////////////////////////////////
+void TIntermTyped::setTypePreservePrecision(const TType &t)
+{
+ TPrecision precision = getPrecision();
+ mType = t;
+ ASSERT(mType.getBasicType() != EbtBool || precision == EbpUndefined);
+ mType.setPrecision(precision);
+}
+
#define REPLACE_IF_IS(node, type, original, replacement) \
if (node == original) { \
node = static_cast<type *>(replacement); \
@@ -237,6 +245,52 @@ void TIntermAggregate::enqueueChildren(std::queue<TIntermNode *> *nodeQueue) con
}
}
+void TIntermAggregate::setPrecisionFromChildren()
+{
+ if (getBasicType() == EbtBool)
+ {
+ mType.setPrecision(EbpUndefined);
+ return;
+ }
+
+ TPrecision precision = EbpUndefined;
+ TIntermSequence::iterator childIter = mSequence.begin();
+ while (childIter != mSequence.end())
+ {
+ TIntermTyped *typed = (*childIter)->getAsTyped();
+ if (typed)
+ precision = GetHigherPrecision(typed->getPrecision(), precision);
+ ++childIter;
+ }
+ mType.setPrecision(precision);
+}
+
+void TIntermAggregate::setBuiltInFunctionPrecision()
+{
+ // All built-ins returning bool should be handled as ops, not functions.
+ ASSERT(getBasicType() != EbtBool);
+
+ TPrecision precision = EbpUndefined;
+ TIntermSequence::iterator childIter = mSequence.begin();
+ while (childIter != mSequence.end())
+ {
+ TIntermTyped *typed = (*childIter)->getAsTyped();
+ // ESSL spec section 8: texture functions get their precision from the sampler.
+ if (typed && IsSampler(typed->getBasicType()))
+ {
+ precision = typed->getPrecision();
+ break;
+ }
+ ++childIter;
+ }
+ // ESSL 3.0 spec section 8: textureSize always gets highp precision.
+ // All other functions that take a sampler are assumed to be texture functions.
+ if (mName.find("textureSize") == 0)
+ mType.setPrecision(EbpHigh);
+ else
+ mType.setPrecision(precision);
+}
+
bool TIntermSelection::replaceChildNode(
TIntermNode *original, TIntermNode *replacement)
{
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index 0f2fec52..32c70f46 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -266,6 +266,7 @@ class TIntermTyped : public TIntermNode
virtual bool hasSideEffects() const = 0;
void setType(const TType &t) { mType = t; }
+ void setTypePreservePrecision(const TType &t);
const TType &getType() const { return mType; }
TType *getTypePointer() { return &mType; }
@@ -614,6 +615,9 @@ class TIntermAggregate : public TIntermOperator
virtual void enqueueChildren(std::queue<TIntermNode *> *nodeQueue) const;
+ void setPrecisionFromChildren();
+ void setBuiltInFunctionPrecision();
+
protected:
TIntermAggregate(const TIntermAggregate &); // disallow copy constructor
TIntermAggregate &operator=(const TIntermAggregate &); // disallow assignment operator
diff --git a/src/compiler/translator/glslang.y b/src/compiler/translator/glslang.y
index f8c4115e..ba2df936 100644
--- a/src/compiler/translator/glslang.y
+++ b/src/compiler/translator/glslang.y
@@ -354,6 +354,15 @@ function_call
// Treat it like a built-in unary operator.
//
$$ = context->intermediate.addUnaryMath(op, $1.intermNode, @1);
+ const TType& returnType = fnCandidate->getReturnType();
+ if (returnType.getBasicType() == EbtBool) {
+ // Bool types should not have precision, so we'll override any precision
+ // that might have been set by addUnaryMath.
+ $$->setType(returnType);
+ } else {
+ // addUnaryMath has set the precision of the node based on the operand.
+ $$->setTypePreservePrecision(returnType);
+ }
if ($$ == 0) {
std::stringstream extraInfoStream;
extraInfoStream << "built in unary operator function. Type: " << static_cast<TIntermTyped*>($1.intermNode)->getCompleteString();
@@ -362,20 +371,29 @@ function_call
YYERROR;
}
} else {
- $$ = context->intermediate.setAggregateOperator($1.intermAggregate, op, @1);
+ TIntermAggregate *aggregate = context->intermediate.setAggregateOperator($1.intermAggregate, op, @1);
+ aggregate->setType(fnCandidate->getReturnType());
+ aggregate->setPrecisionFromChildren();
+ $$ = aggregate;
}
} else {
// This is a real function call
- $$ = context->intermediate.setAggregateOperator($1.intermAggregate, EOpFunctionCall, @1);
- $$->setType(fnCandidate->getReturnType());
+ TIntermAggregate *aggregate = context->intermediate.setAggregateOperator($1.intermAggregate, EOpFunctionCall, @1);
+ aggregate->setType(fnCandidate->getReturnType());
// this is how we know whether the given function is a builtIn function or a user defined function
// if builtIn == false, it's a userDefined -> could be an overloaded builtIn function also
// if builtIn == true, it's definitely a builtIn function with EOpNull
if (!builtIn)
- $$->getAsAggregate()->setUserDefined();
- $$->getAsAggregate()->setName(fnCandidate->getMangledName());
+ aggregate->setUserDefined();
+ aggregate->setName(fnCandidate->getMangledName());
+
+ // This needs to happen after the name is set
+ if (builtIn)
+ aggregate->setBuiltInFunctionPrecision();
+
+ $$ = aggregate;
TQualifier qual;
for (size_t i = 0; i < fnCandidate->getParamCount(); ++i) {
@@ -388,7 +406,6 @@ function_call
}
}
}
- $$->setType(fnCandidate->getReturnType());
} else {
// error message was put out by PaFindFunction()
// Put on a dummy node for error recovery
diff --git a/src/compiler/translator/glslang_tab.cpp b/src/compiler/translator/glslang_tab.cpp
index 567ba432..06960c05 100644
--- a/src/compiler/translator/glslang_tab.cpp
+++ b/src/compiler/translator/glslang_tab.cpp
@@ -1,4 +1,4 @@
-/* A Bison parser, made by GNU Bison 2.7.1. */
+/* A Bison parser, made by GNU Bison 2.7.12-4996. */
/* Bison implementation for Yacc-like parsers in C
@@ -44,7 +44,7 @@
#define YYBISON 1
/* Bison version. */
-#define YYBISON_VERSION "2.7.1"
+#define YYBISON_VERSION "2.7.12-4996"
/* Skeleton name. */
#define YYSKELETON_NAME "yacc.c"
@@ -796,31 +796,31 @@ static const yytype_int16 yyrhs[] =
static const yytype_uint16 yyrline[] =
{
0, 206, 206, 207, 210, 234, 237, 242, 247, 252,
- 257, 263, 266, 269, 272, 275, 285, 298, 306, 406,
- 409, 417, 420, 426, 430, 437, 443, 452, 460, 463,
- 473, 476, 486, 496, 517, 518, 519, 524, 525, 533,
- 544, 545, 553, 564, 568, 569, 579, 589, 599, 612,
- 613, 623, 636, 640, 644, 648, 649, 662, 663, 676,
- 677, 690, 691, 708, 709, 722, 723, 724, 725, 726,
- 730, 733, 744, 752, 760, 787, 793, 804, 808, 812,
- 816, 823, 879, 882, 889, 897, 918, 939, 949, 977,
- 982, 992, 997, 1007, 1010, 1013, 1016, 1022, 1029, 1032,
- 1036, 1040, 1044, 1051, 1055, 1059, 1066, 1070, 1074, 1081,
- 1090, 1096, 1099, 1105, 1111, 1118, 1127, 1136, 1144, 1147,
- 1154, 1158, 1165, 1168, 1172, 1176, 1185, 1194, 1202, 1212,
- 1224, 1227, 1230, 1236, 1243, 1246, 1252, 1255, 1258, 1264,
- 1267, 1282, 1286, 1290, 1294, 1298, 1302, 1307, 1312, 1317,
- 1322, 1327, 1332, 1337, 1342, 1347, 1352, 1357, 1362, 1367,
- 1372, 1377, 1382, 1387, 1392, 1397, 1402, 1407, 1411, 1415,
- 1419, 1423, 1427, 1431, 1435, 1439, 1443, 1447, 1451, 1455,
- 1459, 1463, 1467, 1475, 1483, 1487, 1500, 1500, 1503, 1503,
- 1509, 1512, 1528, 1531, 1540, 1544, 1550, 1557, 1572, 1576,
- 1580, 1581, 1587, 1588, 1589, 1590, 1591, 1595, 1596, 1596,
- 1596, 1606, 1607, 1611, 1611, 1612, 1612, 1617, 1620, 1630,
- 1633, 1639, 1640, 1644, 1652, 1656, 1666, 1671, 1688, 1688,
- 1693, 1693, 1700, 1700, 1708, 1711, 1717, 1720, 1726, 1730,
- 1737, 1744, 1751, 1758, 1769, 1778, 1782, 1789, 1792, 1798,
- 1798
+ 257, 263, 266, 269, 272, 275, 285, 298, 306, 423,
+ 426, 434, 437, 443, 447, 454, 460, 469, 477, 480,
+ 490, 493, 503, 513, 535, 536, 537, 542, 543, 551,
+ 562, 563, 571, 582, 586, 587, 597, 607, 617, 630,
+ 631, 641, 654, 658, 662, 666, 667, 680, 681, 694,
+ 695, 708, 709, 726, 727, 740, 741, 742, 743, 744,
+ 748, 751, 762, 770, 778, 805, 811, 822, 826, 830,
+ 834, 841, 897, 900, 907, 915, 936, 957, 967, 995,
+ 1000, 1010, 1015, 1025, 1028, 1031, 1034, 1040, 1047, 1050,
+ 1054, 1058, 1062, 1069, 1073, 1077, 1084, 1088, 1092, 1099,
+ 1108, 1114, 1117, 1123, 1129, 1136, 1145, 1154, 1162, 1165,
+ 1172, 1176, 1183, 1186, 1190, 1194, 1203, 1212, 1220, 1230,
+ 1242, 1245, 1248, 1254, 1261, 1264, 1270, 1273, 1276, 1282,
+ 1285, 1300, 1304, 1308, 1312, 1316, 1320, 1325, 1330, 1335,
+ 1340, 1345, 1350, 1355, 1360, 1365, 1370, 1375, 1380, 1385,
+ 1390, 1395, 1400, 1405, 1410, 1415, 1420, 1425, 1429, 1433,
+ 1437, 1441, 1445, 1449, 1453, 1457, 1461, 1465, 1469, 1473,
+ 1477, 1481, 1485, 1493, 1501, 1505, 1518, 1518, 1521, 1521,
+ 1527, 1530, 1546, 1549, 1558, 1562, 1568, 1575, 1590, 1594,
+ 1598, 1599, 1605, 1606, 1607, 1608, 1609, 1613, 1614, 1614,
+ 1614, 1624, 1625, 1629, 1629, 1630, 1630, 1635, 1638, 1648,
+ 1651, 1657, 1658, 1662, 1670, 1674, 1684, 1689, 1706, 1706,
+ 1711, 1711, 1718, 1718, 1726, 1729, 1735, 1738, 1744, 1748,
+ 1755, 1762, 1769, 1776, 1787, 1796, 1800, 1807, 1810, 1816,
+ 1816
};
#endif
@@ -2736,6 +2736,15 @@ yyreduce:
// Treat it like a built-in unary operator.
//
(yyval.interm.intermTypedNode) = context->intermediate.addUnaryMath(op, (yyvsp[(1) - (1)].interm).intermNode, (yylsp[(1) - (1)]));
+ const TType& returnType = fnCandidate->getReturnType();
+ if (returnType.getBasicType() == EbtBool) {
+ // Bool types should not have precision, so we'll override any precision
+ // that might have been set by addUnaryMath.
+ (yyval.interm.intermTypedNode)->setType(returnType);
+ } else {
+ // addUnaryMath has set the precision of the node based on the operand.
+ (yyval.interm.intermTypedNode)->setTypePreservePrecision(returnType);
+ }
if ((yyval.interm.intermTypedNode) == 0) {
std::stringstream extraInfoStream;
extraInfoStream << "built in unary operator function. Type: " << static_cast<TIntermTyped*>((yyvsp[(1) - (1)].interm).intermNode)->getCompleteString();
@@ -2744,20 +2753,29 @@ yyreduce:
YYERROR;
}
} else {
- (yyval.interm.intermTypedNode) = context->intermediate.setAggregateOperator((yyvsp[(1) - (1)].interm).intermAggregate, op, (yylsp[(1) - (1)]));
+ TIntermAggregate *aggregate = context->intermediate.setAggregateOperator((yyvsp[(1) - (1)].interm).intermAggregate, op, (yylsp[(1) - (1)]));
+ aggregate->setType(fnCandidate->getReturnType());
+ aggregate->setPrecisionFromChildren();
+ (yyval.interm.intermTypedNode) = aggregate;
}
} else {
// This is a real function call
- (yyval.interm.intermTypedNode) = context->intermediate.setAggregateOperator((yyvsp[(1) - (1)].interm).intermAggregate, EOpFunctionCall, (yylsp[(1) - (1)]));
- (yyval.interm.intermTypedNode)->setType(fnCandidate->getReturnType());
+ TIntermAggregate *aggregate = context->intermediate.setAggregateOperator((yyvsp[(1) - (1)].interm).intermAggregate, EOpFunctionCall, (yylsp[(1) - (1)]));
+ aggregate->setType(fnCandidate->getReturnType());
// this is how we know whether the given function is a builtIn function or a user defined function
// if builtIn == false, it's a userDefined -> could be an overloaded builtIn function also
// if builtIn == true, it's definitely a builtIn function with EOpNull
if (!builtIn)
- (yyval.interm.intermTypedNode)->getAsAggregate()->setUserDefined();
- (yyval.interm.intermTypedNode)->getAsAggregate()->setName(fnCandidate->getMangledName());
+ aggregate->setUserDefined();
+ aggregate->setName(fnCandidate->getMangledName());
+
+ // This needs to happen after the name is set
+ if (builtIn)
+ aggregate->setBuiltInFunctionPrecision();
+
+ (yyval.interm.intermTypedNode) = aggregate;
TQualifier qual;
for (size_t i = 0; i < fnCandidate->getParamCount(); ++i) {
@@ -2770,7 +2788,6 @@ yyreduce:
}
}
}
- (yyval.interm.intermTypedNode)->setType(fnCandidate->getReturnType());
} else {
// error message was put out by PaFindFunction()
// Put on a dummy node for error recovery
diff --git a/src/compiler/translator/glslang_tab.h b/src/compiler/translator/glslang_tab.h
index c6a61dcd..41a188c8 100644
--- a/src/compiler/translator/glslang_tab.h
+++ b/src/compiler/translator/glslang_tab.h
@@ -1,4 +1,4 @@
-/* A Bison parser, made by GNU Bison 2.7.1. */
+/* A Bison parser, made by GNU Bison 2.7.12-4996. */
/* Bison interface for Yacc-like parsers in C
diff --git a/tests/compiler_tests/TypeTracking_test.cpp b/tests/compiler_tests/TypeTracking_test.cpp
new file mode 100644
index 00000000..838ac93a
--- /dev/null
+++ b/tests/compiler_tests/TypeTracking_test.cpp
@@ -0,0 +1,199 @@
+//
+// 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.
+//
+// TypeTracking_test.cpp:
+// Test for tracking types resulting from math operations, including their
+// precision.
+//
+
+#include "angle_gl.h"
+#include "gtest/gtest.h"
+#include "GLSLANG/ShaderLang.h"
+#include "compiler/translator/TranslatorESSL.h"
+
+class TypeTrackingTest : public testing::Test
+{
+ public:
+ TypeTrackingTest() {}
+
+ protected:
+ virtual void SetUp()
+ {
+ ShBuiltInResources resources;
+ ShInitBuiltInResources(&resources);
+
+ mTranslator = new TranslatorESSL(GL_FRAGMENT_SHADER, SH_GLES2_SPEC);
+ ASSERT_TRUE(mTranslator->Init(resources));
+ }
+
+ virtual void TearDown()
+ {
+ delete mTranslator;
+ }
+
+ void compile(const std::string& shaderString)
+ {
+ const char *shaderStrings[] = { shaderString.c_str() };
+ bool compilationSuccess = mTranslator->compile(shaderStrings, 1, SH_INTERMEDIATE_TREE);
+ TInfoSink &infoSink = mTranslator->getInfoSink();
+ mInfoLog = infoSink.info.c_str();
+ if (!compilationSuccess)
+ FAIL() << "Shader compilation failed " << mInfoLog;
+ }
+
+ bool foundInIntermediateTree(const char* stringToFind)
+ {
+ return mInfoLog.find(stringToFind) != std::string::npos;
+ }
+
+ private:
+ TranslatorESSL *mTranslator;
+ std::string mInfoLog;
+};
+
+TEST_F(TypeTrackingTest, BuiltInFunctionResultPrecision)
+{
+ const std::string &shaderString =
+ "precision mediump float;\n"
+ "uniform float f;\n"
+ "void main() {\n"
+ " float ff = sin(f);\n"
+ " gl_FragColor = vec4(ff);\n"
+ "}\n";
+ compile(shaderString);
+ ASSERT_TRUE(foundInIntermediateTree("sine (mediump float)"));
+};
+
+TEST_F(TypeTrackingTest, BinaryMathResultPrecision)
+{
+ const std::string &shaderString =
+ "precision mediump float;\n"
+ "uniform float f;\n"
+ "void main() {\n"
+ " float ff = f * 0.5;\n"
+ " gl_FragColor = vec4(ff);\n"
+ "}\n";
+ compile(shaderString);
+ ASSERT_TRUE(foundInIntermediateTree("multiply (mediump float)"));
+};
+
+TEST_F(TypeTrackingTest, BuiltInVecFunctionResultTypeAndPrecision)
+{
+ const std::string &shaderString =
+ "precision mediump float;\n"
+ "uniform vec2 a;\n"
+ "void main() {\n"
+ " float b = length(a);\n"
+ " float c = dot(a, vec2(0.5));\n"
+ " float d = distance(vec2(0.5), a);\n"
+ " gl_FragColor = vec4(b, c, d, 1.0);\n"
+ "}\n";
+ compile(shaderString);
+ ASSERT_TRUE(foundInIntermediateTree("length (mediump float)"));
+ ASSERT_TRUE(foundInIntermediateTree("dot-product (mediump float)"));
+ ASSERT_TRUE(foundInIntermediateTree("distance (mediump float)"));
+};
+
+TEST_F(TypeTrackingTest, BuiltInFunctionChoosesHigherPrecision)
+{
+ const std::string &shaderString =
+ "precision lowp float;\n"
+ "uniform mediump vec2 a;\n"
+ "uniform lowp vec2 b;\n"
+ "void main() {\n"
+ " float c = dot(a, b);\n"
+ " float d = distance(b, a);\n"
+ " gl_FragColor = vec4(c, d, 0.0, 1.0);\n"
+ "}\n";
+ compile(shaderString);
+ ASSERT_TRUE(foundInIntermediateTree("dot-product (mediump float)"));
+ ASSERT_TRUE(foundInIntermediateTree("distance (mediump float)"));
+};
+
+TEST_F(TypeTrackingTest, BuiltInBoolFunctionResultType)
+{
+ const std::string &shaderString =
+ "uniform bvec4 bees;\n"
+ "void main() {\n"
+ " bool b = any(bees);\n"
+ " bool c = all(bees);\n"
+ " bvec4 d = not(bees);\n"
+ " gl_FragColor = vec4(b ? 1.0 : 0.0, c ? 1.0 : 0.0, d.x ? 1.0 : 0.0, 1.0);\n"
+ "}\n";
+ compile(shaderString);
+ ASSERT_TRUE(foundInIntermediateTree("any (bool)"));
+ ASSERT_TRUE(foundInIntermediateTree("all (bool)"));
+ ASSERT_TRUE(foundInIntermediateTree("Negate conditional (4-component vector of bool)"));
+};
+
+TEST_F(TypeTrackingTest, BuiltInVecToBoolFunctionResultType)
+{
+ const std::string &shaderString =
+ "precision mediump float;\n"
+ "uniform vec2 apples;\n"
+ "uniform vec2 oranges;\n"
+ "uniform ivec2 foo;\n"
+ "uniform ivec2 bar;\n"
+ "void main() {\n"
+ " bvec2 a = lessThan(apples, oranges);\n"
+ " bvec2 b = greaterThan(foo, bar);\n"
+ " gl_FragColor = vec4(any(a) ? 1.0 : 0.0, any(b) ? 1.0 : 0.0, 0.0, 1.0);\n"
+ "}\n";
+ compile(shaderString);
+ ASSERT_TRUE(foundInIntermediateTree("Less Than (2-component vector of bool)"));
+ ASSERT_TRUE(foundInIntermediateTree("Greater Than (2-component vector of bool)"));
+};
+
+TEST_F(TypeTrackingTest, Texture2DResultTypeAndPrecision)
+{
+ // ESSL spec section 4.5.3: sampler2D and samplerCube are lowp by default
+ // ESSL spec section 8: For the texture functions, the precision of the return type matches the precision of the sampler type.
+ const std::string &shaderString =
+ "precision mediump float;\n"
+ "uniform sampler2D s;\n"
+ "uniform vec2 a;\n"
+ "void main() {\n"
+ " vec4 c = texture2D(s, a);\n"
+ " gl_FragColor = c;\n"
+ "}\n";
+ compile(shaderString);
+ ASSERT_TRUE(foundInIntermediateTree("texture2D(s21;vf2; (lowp 4-component vector of float)"));
+};
+
+TEST_F(TypeTrackingTest, TextureCubeResultTypeAndPrecision)
+{
+ // ESSL spec section 4.5.3: sampler2D and samplerCube are lowp by default
+ // ESSL spec section 8: For the texture functions, the precision of the return type matches the precision of the sampler type.
+ const std::string &shaderString =
+ "precision mediump float;\n"
+ "uniform samplerCube sc;\n"
+ "uniform vec3 a;\n"
+ "void main() {\n"
+ " vec4 c = textureCube(sc, a);\n"
+ " gl_FragColor = c;\n"
+ "}\n";
+ compile(shaderString);
+ ASSERT_TRUE(foundInIntermediateTree("textureCube(sC1;vf3; (lowp 4-component vector of float)"));
+};
+
+TEST_F(TypeTrackingTest, TextureSizeResultTypeAndPrecision)
+{
+ // ESSL 3.0 spec section 8: textureSize has predefined precision highp
+ const std::string &shaderString =
+ "#version 300 es\n"
+ "precision mediump float;\n"
+ "out vec4 my_FragColor;\n"
+ "uniform sampler2D s;\n"
+ "void main() {\n"
+ " ivec2 size = textureSize(s, 0);\n"
+ " if (size.x > 100) {\n"
+ " my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
+ " } else {\n"
+ " my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
+ " }\n"
+ "}\n";
+ compile(shaderString);
+ ASSERT_TRUE(foundInIntermediateTree("textureSize(s21;i1; (highp 2-component vector of int)"));
+};