Commit 9250cb24 by Olli Etuaho Committed by Commit Bot

Add ESSL 3.10 integer math built-ins

This adds built-ins found in ESSL 3.10 section 8.8 Integer functions. This includes constant folding support for functions that may be constant folded, and support for both GLSL and HLSL output. In HLSL several of the functions need to be emulated. The precision qualification for the return value of some of these functions is determined by special rules, that are now part of type promotion for TIntermUnary nodes and determining the type of TIntermAggregate nodes. BUG=angleproject:1730 TEST=angle_unittests TEST=dEQP-GLES31.functional.shaders.builtin_functions.integer.* Change-Id: Ib0056c17671c42b6496c2f0ef059b99f8f25c122 Reviewed-on: https://chromium-review.googlesource.com/431310 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 2add15ef
......@@ -97,21 +97,6 @@ typename BitSetIterator<N>::Iterator &BitSetIterator<N>::Iterator::operator++()
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
{
......@@ -134,7 +119,7 @@ unsigned long BitSetIterator<N>::Iterator::getNextBit()
unsigned long wordBits = (mBits & wordMask).to_ulong();
if (wordBits != 0ul)
{
return ScanForward(wordBits) + mOffset;
return gl::ScanForward(wordBits) + mOffset;
}
mBits >>= BitsPerWord;
......
......@@ -695,6 +695,91 @@ inline void unpackHalf2x16(uint32_t u, float *f1, float *f2)
*f2 = float16ToFloat32(mostSignificantBits);
}
// Reverse the order of the bits.
inline uint32_t BitfieldReverse(uint32_t value)
{
// TODO(oetuaho@nvidia.com): Optimize this if needed. There don't seem to be compiler intrinsics
// for this, and right now it's not used in performance-critical paths.
uint32_t result = 0u;
for (size_t j = 0u; j < 32u; ++j)
{
result |= (((value >> j) & 1u) << (31u - j));
}
return result;
}
// Count the 1 bits.
inline int BitCount(unsigned int bits)
{
#if defined(ANGLE_PLATFORM_WINDOWS)
return static_cast<int>(__popcnt(bits));
#elif defined(ANGLE_PLATFORM_POSIX)
return __builtin_popcount(bits);
#else
#error Please implement bit count for your platform!
#endif
}
// Return the index of the least significant bit set. Indexing is such that bit 0 is the least
// significant bit.
inline unsigned long ScanForward(unsigned long bits)
{
ASSERT(bits != 0u);
#if defined(ANGLE_PLATFORM_WINDOWS)
unsigned long firstBitIndex = 0ul;
unsigned char ret = _BitScanForward(&firstBitIndex, bits);
ASSERT(ret != 0u);
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
}
// Return the index of the most significant bit set. Indexing is such that bit 0 is the least
// significant bit.
inline unsigned long ScanReverse(unsigned long bits)
{
ASSERT(bits != 0u);
#if defined(ANGLE_PLATFORM_WINDOWS)
unsigned long lastBitIndex = 0ul;
unsigned char ret = _BitScanReverse(&lastBitIndex, bits);
ASSERT(ret != 0u);
return lastBitIndex;
#elif defined(ANGLE_PLATFORM_POSIX)
return static_cast<unsigned long>(sizeof(unsigned long) * CHAR_BIT - 1 - __builtin_clzl(bits));
#else
#error Please implement bit-scan-reverse for your platform!
#endif
}
// Returns -1 on 0, otherwise the index of the least significant 1 bit as in GLSL.
inline int FindLSB(uint32_t bits)
{
if (bits == 0u)
{
return -1;
}
else
{
return static_cast<int>(ScanForward(bits));
}
}
// Returns -1 on 0, otherwise the index of the most significant 1 bit as in GLSL.
inline int FindMSB(uint32_t bits)
{
if (bits == 0u)
{
return -1;
}
else
{
return static_cast<int>(ScanReverse(bits));
}
}
// Returns whether the argument is Not a Number.
// IEEE 754 single precision NaN representation: Exponent(8 bits) - 255, Mantissa(23 bits) - non-zero.
inline bool isNaN(float f)
......
......@@ -207,4 +207,57 @@ TEST(MathUtilTest, CheckedRoundUpInvalid)
ASSERT_FALSE(checkedLimit.IsValid());
}
// Test BitfieldReverse which reverses the order of the bits in an integer.
TEST(MathUtilTest, BitfieldReverse)
{
EXPECT_EQ(0u, gl::BitfieldReverse(0u));
EXPECT_EQ(0x80000000u, gl::BitfieldReverse(1u));
EXPECT_EQ(0x1u, gl::BitfieldReverse(0x80000000u));
uint32_t bits = (1u << 4u) | (1u << 7u);
uint32_t reversed = (1u << (31u - 4u)) | (1u << (31u - 7u));
EXPECT_EQ(reversed, gl::BitfieldReverse(bits));
}
// Test BitCount, which counts 1 bits in an integer.
TEST(MathUtilTest, BitCount)
{
EXPECT_EQ(0, gl::BitCount(0u));
EXPECT_EQ(32, gl::BitCount(0xFFFFFFFFu));
EXPECT_EQ(10, gl::BitCount(0x17103121u));
}
// Test ScanForward, which scans for the least significant 1 bit from a non-zero integer.
TEST(MathUtilTest, ScanForward)
{
EXPECT_EQ(0ul, gl::ScanForward(1ul));
EXPECT_EQ(16ul, gl::ScanForward(0x80010000ul));
EXPECT_EQ(31ul, gl::ScanForward(0x80000000ul));
}
// Test ScanReverse, which scans for the most significant 1 bit from a non-zero integer.
TEST(MathUtilTest, ScanReverse)
{
EXPECT_EQ(0ul, gl::ScanReverse(1ul));
EXPECT_EQ(16ul, gl::ScanReverse(0x00010030ul));
EXPECT_EQ(31ul, gl::ScanReverse(0x80000000ul));
}
// Test FindLSB, which finds the least significant 1 bit.
TEST(MathUtilTest, FindLSB)
{
EXPECT_EQ(-1, gl::FindLSB(0u));
EXPECT_EQ(0, gl::FindLSB(1u));
EXPECT_EQ(16, gl::FindLSB(0x80010000u));
EXPECT_EQ(31, gl::FindLSB(0x80000000u));
}
// Test FindMSB, which finds the most significant 1 bit.
TEST(MathUtilTest, FindMSB)
{
EXPECT_EQ(-1, gl::FindMSB(0u));
EXPECT_EQ(0, gl::FindMSB(1u));
EXPECT_EQ(16, gl::FindMSB(0x00010030u));
EXPECT_EQ(31, gl::FindMSB(0x80000000u));
}
} // anonymous namespace
......@@ -110,15 +110,9 @@ void InsertBuiltInFunctions(sh::GLenum type,
symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpSmoothStep, genType, genType, genType, genType);
symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpSmoothStep, genType, float1, float1, genType);
const TType *outFloat1 = TCache::getType(EbtFloat, EvqOut);
const TType *outFloat2 = TCache::getType(EbtFloat, EvqOut, 2);
const TType *outFloat3 = TCache::getType(EbtFloat, EvqOut, 3);
const TType *outFloat4 = TCache::getType(EbtFloat, EvqOut, 4);
const TType *outGenType = TCache::getType(EbtGenType, EvqOut);
symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpModf, float1, float1, outFloat1);
symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpModf, float2, float2, outFloat2);
symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpModf, float3, float3, outFloat3);
symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpModf, float4, float4, outFloat4);
symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpModf, genType, genType, outGenType);
symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpIsNan, genBType, genType);
symbolTable.insertBuiltInOp(ESSL3_BUILTINS, EOpIsInf, genBType, genType);
......@@ -231,6 +225,37 @@ void InsertBuiltInFunctions(sh::GLenum type,
symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpAll, bool1, bvec);
symbolTable.insertBuiltInOp(COMMON_BUILTINS, EOpLogicalNotComponentWise, bvec, bvec);
//
// Integer functions
//
const TType *outGenUType = TCache::getType(EbtGenUType, EvqOut);
const TType *outGenIType = TCache::getType(EbtGenIType, EvqOut);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitfieldExtract, genIType, genIType, int1,
int1);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitfieldExtract, genUType, genUType, int1,
int1);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitfieldInsert, genIType, genIType, genIType,
int1, int1);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitfieldInsert, genUType, genUType, genUType,
int1, int1);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitfieldReverse, genIType, genIType);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitfieldReverse, genUType, genUType);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitCount, genIType, genIType);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpBitCount, genIType, genUType);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpFindLSB, genIType, genIType);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpFindLSB, genIType, genUType);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpFindMSB, genIType, genIType);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpFindMSB, genIType, genUType);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpUaddCarry, genUType, genUType, genUType,
outGenUType);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpUsubBorrow, genUType, genUType, genUType,
outGenUType);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpUmulExtended, voidType, genUType, genUType,
outGenUType, outGenUType);
symbolTable.insertBuiltInOp(ESSL3_1_BUILTINS, EOpImulExtended, voidType, genIType, genIType,
outGenIType, outGenIType);
const TType *sampler2D = TCache::getType(EbtSampler2D);
const TType *samplerCube = TCache::getType(EbtSamplerCube);
......
......@@ -618,7 +618,12 @@ class TIntermAggregate : public TIntermOperator, public TIntermAggregateBase
bool getUseEmulatedFunction() { return mUseEmulatedFunction; }
bool areChildrenConstQualified();
void setPrecisionForBuiltInOp();
void setPrecisionFromChildren();
// Used for built-in functions under EOpFunctionCall.
void setBuiltInFunctionPrecision();
// Returns true if changing parameter precision may affect the return value.
......@@ -641,6 +646,9 @@ class TIntermAggregate : public TIntermOperator, public TIntermAggregateBase
private:
TIntermAggregate(const TIntermAggregate &node); // note: not deleted, just private!
// Returns true if precision was set according to special rules for this built-in.
bool setPrecisionForSpecialBuiltInOp();
};
// A list of statements. Either the root node which contains declarations and function definitions,
......
......@@ -378,6 +378,8 @@ TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate,
case EOpFaceForward:
case EOpReflect:
case EOpRefract:
case EOpBitfieldExtract:
case EOpBitfieldInsert:
return aggregate->fold(diagnostics);
default:
// TODO: Add support for folding array constructors
......
......@@ -252,6 +252,27 @@ const char *GetOperatorString(TOperator op)
case EOpLogicalNotComponentWise:
return "not";
case EOpBitfieldExtract:
return "bitfieldExtract";
case EOpBitfieldInsert:
return "bitfieldInsert";
case EOpBitfieldReverse:
return "bitfieldReverse";
case EOpBitCount:
return "bitCount";
case EOpFindLSB:
return "findLSB";
case EOpFindMSB:
return "findMSB";
case EOpUaddCarry:
return "uaddCarry";
case EOpUsubBorrow:
return "usubBorrow";
case EOpUmulExtended:
return "umulExtended";
case EOpImulExtended:
return "imulExtended";
case EOpKill:
return "kill";
case EOpReturn:
......
......@@ -160,6 +160,17 @@ enum TOperator
EOpAll,
EOpLogicalNotComponentWise,
EOpBitfieldExtract,
EOpBitfieldInsert,
EOpBitfieldReverse,
EOpBitCount,
EOpFindLSB,
EOpFindMSB,
EOpUaddCarry,
EOpUsubBorrow,
EOpUmulExtended,
EOpImulExtended,
//
// Branch
//
......
......@@ -725,6 +725,10 @@ bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary *node)
case EOpAny:
case EOpAll:
case EOpLogicalNotComponentWise:
case EOpBitfieldReverse:
case EOpBitCount:
case EOpFindLSB:
case EOpFindMSB:
writeBuiltInFunctionTriplet(visit, node->getOp(), node->getUseEmulatedFunction());
return true;
default:
......@@ -935,6 +939,12 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node)
case EOpRefract:
case EOpMulMatrixComponentWise:
case EOpOuterProduct:
case EOpBitfieldExtract:
case EOpBitfieldInsert:
case EOpUaddCarry:
case EOpUsubBorrow:
case EOpUmulExtended:
case EOpImulExtended:
case EOpBarrier:
case EOpMemoryBarrier:
case EOpMemoryBarrierAtomicCounter:
......
......@@ -1473,6 +1473,22 @@ bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node)
case EOpLogicalNotComponentWise:
outputTriplet(out, visit, "(!", "", ")");
break;
case EOpBitfieldReverse:
outputTriplet(out, visit, "reversebits(", "", ")");
break;
case EOpBitCount:
outputTriplet(out, visit, "countbits(", "", ")");
break;
case EOpFindLSB:
// Note that it's unclear from the HLSL docs what this returns for 0, but this is tested
// in GLSLTest and results are consistent with GL.
outputTriplet(out, visit, "firstbitlow(", "", ")");
break;
case EOpFindMSB:
// Note that it's unclear from the HLSL docs what this returns for 0 or -1, but this is
// tested in GLSLTest and results are consistent with GL.
outputTriplet(out, visit, "firstbithigh(", "", ")");
break;
default:
UNREACHABLE();
}
......@@ -1997,6 +2013,15 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
case EOpMulMatrixComponentWise:
outputTriplet(out, visit, "(", " * ", ")");
break;
case EOpBitfieldExtract:
case EOpBitfieldInsert:
case EOpUaddCarry:
case EOpUsubBorrow:
case EOpUmulExtended:
case EOpImulExtended:
ASSERT(node->getUseEmulatedFunction());
writeEmulatedFunctionTriplet(out, visit, node->getOp());
break;
default:
UNREACHABLE();
}
......
......@@ -4432,7 +4432,7 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall,
TIntermAggregate *aggregate =
intermediate.setAggregateOperator(paramNode, op, loc);
aggregate->setType(fnCandidate->getReturnType());
aggregate->setPrecisionFromChildren();
aggregate->setPrecisionForBuiltInOp();
if (aggregate->areChildrenConstQualified())
{
aggregate->getTypePointer()->setQualifier(EvqConst);
......
......@@ -223,13 +223,14 @@ const TType *SpecificType(const TType *type, int size)
switch (type->getBasicType())
{
case EbtGenType:
return TCache::getType(EbtFloat, static_cast<unsigned char>(size));
return TCache::getType(EbtFloat, type->getQualifier(),
static_cast<unsigned char>(size));
case EbtGenIType:
return TCache::getType(EbtInt, static_cast<unsigned char>(size));
return TCache::getType(EbtInt, type->getQualifier(), static_cast<unsigned char>(size));
case EbtGenUType:
return TCache::getType(EbtUInt, static_cast<unsigned char>(size));
return TCache::getType(EbtUInt, type->getQualifier(), static_cast<unsigned char>(size));
case EbtGenBType:
return TCache::getType(EbtBool, static_cast<unsigned char>(size));
return TCache::getType(EbtBool, type->getQualifier(), static_cast<unsigned char>(size));
default:
return type;
}
......@@ -364,18 +365,19 @@ void TSymbolTable::insertBuiltIn(ESymbolLevel level,
insertBuiltIn(level, rvalue, name, unsignedImage, ptype2, ptype3, ptype4, ptype5);
}
}
else if (IsGenType(rvalue) || IsGenType(ptype1) || IsGenType(ptype2) || IsGenType(ptype3))
else if (IsGenType(rvalue) || IsGenType(ptype1) || IsGenType(ptype2) || IsGenType(ptype3) ||
IsGenType(ptype4))
{
ASSERT(!ptype4 && !ptype5);
ASSERT(!ptype5);
insertUnmangledBuiltInName(name, level);
insertBuiltIn(level, op, ext, SpecificType(rvalue, 1), name, SpecificType(ptype1, 1),
SpecificType(ptype2, 1), SpecificType(ptype3, 1));
SpecificType(ptype2, 1), SpecificType(ptype3, 1), SpecificType(ptype4, 1));
insertBuiltIn(level, op, ext, SpecificType(rvalue, 2), name, SpecificType(ptype1, 2),
SpecificType(ptype2, 2), SpecificType(ptype3, 2));
SpecificType(ptype2, 2), SpecificType(ptype3, 2), SpecificType(ptype4, 2));
insertBuiltIn(level, op, ext, SpecificType(rvalue, 3), name, SpecificType(ptype1, 3),
SpecificType(ptype2, 3), SpecificType(ptype3, 3));
SpecificType(ptype2, 3), SpecificType(ptype3, 3), SpecificType(ptype4, 3));
insertBuiltIn(level, op, ext, SpecificType(rvalue, 4), name, SpecificType(ptype1, 4),
SpecificType(ptype2, 4), SpecificType(ptype3, 4));
SpecificType(ptype2, 4), SpecificType(ptype3, 4), SpecificType(ptype4, 4));
}
else if (IsVecType(rvalue) || IsVecType(ptype1) || IsVecType(ptype2) || IsVecType(ptype3))
{
......
......@@ -1244,3 +1244,112 @@ TEST_F(ConstantFoldingExpressionTest, FoldDivideByInfinity)
evaluateFloat(floatString);
ASSERT_TRUE(constantFoundInAST(0.0f));
}
// Test that unsigned bitfieldExtract is folded correctly.
TEST_F(ConstantFoldingExpressionTest, FoldUnsignedBitfieldExtract)
{
const std::string &uintString = "bitfieldExtract(0x00110000u, 16, 5)";
evaluateUint(uintString);
ASSERT_TRUE(constantFoundInAST(0x11u));
}
// Test that unsigned bitfieldExtract to extract 32 bits is folded correctly.
TEST_F(ConstantFoldingExpressionTest, FoldUnsignedBitfieldExtract32Bits)
{
const std::string &uintString = "bitfieldExtract(0xff0000ffu, 0, 32)";
evaluateUint(uintString);
ASSERT_TRUE(constantFoundInAST(0xff0000ffu));
}
// Test that signed bitfieldExtract is folded correctly. The higher bits should be set to 1 if the
// most significant bit of the extracted value is 1.
TEST_F(ConstantFoldingExpressionTest, FoldSignedBitfieldExtract)
{
const std::string &intString = "bitfieldExtract(0x00110000, 16, 5)";
evaluateInt(intString);
// 0xfffffff1 == -15
ASSERT_TRUE(constantFoundInAST(-15));
}
// Test that bitfieldInsert is folded correctly.
TEST_F(ConstantFoldingExpressionTest, FoldBitfieldInsert)
{
const std::string &uintString = "bitfieldInsert(0x04501701u, 0x11u, 8, 5)";
evaluateUint(uintString);
ASSERT_TRUE(constantFoundInAST(0x04501101u));
}
// Test that bitfieldInsert to insert 32 bits is folded correctly.
TEST_F(ConstantFoldingExpressionTest, FoldBitfieldInsert32Bits)
{
const std::string &uintString = "bitfieldInsert(0xff0000ffu, 0x11u, 0, 32)";
evaluateUint(uintString);
ASSERT_TRUE(constantFoundInAST(0x11u));
}
// Test that bitfieldReverse is folded correctly.
TEST_F(ConstantFoldingExpressionTest, FoldBitfieldReverse)
{
const std::string &uintString = "bitfieldReverse((1u << 4u) | (1u << 7u))";
evaluateUint(uintString);
uint32_t flag1 = 1u << (31u - 4u);
uint32_t flag2 = 1u << (31u - 7u);
ASSERT_TRUE(constantFoundInAST(flag1 | flag2));
}
// Test that bitCount is folded correctly.
TEST_F(ConstantFoldingExpressionTest, FoldBitCount)
{
const std::string &intString = "bitCount(0x17103121u)";
evaluateInt(intString);
ASSERT_TRUE(constantFoundInAST(10));
}
// Test that findLSB is folded correctly.
TEST_F(ConstantFoldingExpressionTest, FoldFindLSB)
{
const std::string &intString = "findLSB(0x80010000u)";
evaluateInt(intString);
ASSERT_TRUE(constantFoundInAST(16));
}
// Test that findLSB is folded correctly when the operand is zero.
TEST_F(ConstantFoldingExpressionTest, FoldFindLSBZero)
{
const std::string &intString = "findLSB(0u)";
evaluateInt(intString);
ASSERT_TRUE(constantFoundInAST(-1));
}
// Test that findMSB is folded correctly.
TEST_F(ConstantFoldingExpressionTest, FoldFindMSB)
{
const std::string &intString = "findMSB(0x01000008u)";
evaluateInt(intString);
ASSERT_TRUE(constantFoundInAST(24));
}
// Test that findMSB is folded correctly when the operand is zero.
TEST_F(ConstantFoldingExpressionTest, FoldFindMSBZero)
{
const std::string &intString = "findMSB(0u)";
evaluateInt(intString);
ASSERT_TRUE(constantFoundInAST(-1));
}
// Test that findMSB is folded correctly for a negative integer.
// It is supposed to return the index of the most significant bit set to 0.
TEST_F(ConstantFoldingExpressionTest, FoldFindMSBNegativeInt)
{
const std::string &intString = "findMSB(-8)";
evaluateInt(intString);
ASSERT_TRUE(constantFoundInAST(2));
}
// Test that findMSB is folded correctly for -1.
TEST_F(ConstantFoldingExpressionTest, FoldFindMSBMinusOne)
{
const std::string &intString = "findMSB(-1)";
evaluateInt(intString);
ASSERT_TRUE(constantFoundInAST(-1));
}
......@@ -27,7 +27,7 @@ class TypeTrackingTest : public testing::Test
InitBuiltInResources(&resources);
resources.FragmentPrecisionHigh = 1;
mTranslator = new TranslatorESSL(GL_FRAGMENT_SHADER, SH_GLES3_SPEC);
mTranslator = new TranslatorESSL(GL_FRAGMENT_SHADER, SH_GLES3_1_SPEC);
ASSERT_TRUE(mTranslator->Init(resources));
}
......@@ -454,3 +454,192 @@ TEST_F(TypeTrackingTest, BuiltInIntBitsToFloatResultTypeAndPrecision)
ASSERT_TRUE(foundInIntermediateTree("intBitsToFloat (highp float)"));
ASSERT_TRUE(foundInIntermediateTree("uintBitsToFloat (highp float)"));
}
// Test that bitfieldExtract returns a precision consistent with its "value" parameter.
TEST_F(TypeTrackingTest, BuiltInBitfieldExtractResultTypeAndPrecision)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"uniform mediump int i;\n"
"uniform highp uint u;\n"
"uniform lowp int offset;\n"
"uniform highp int bits;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" lowp int i2 = bitfieldExtract(i, offset, bits);\n"
" lowp uint u2 = bitfieldExtract(u, offset, bits);\n"
" my_FragColor = vec4(i2, u2, 0.0, 1.0); \n"
"}\n";
compile(shaderString);
ASSERT_FALSE(foundErrorInIntermediateTree());
ASSERT_TRUE(foundInIntermediateTree("bitfieldExtract (mediump int)"));
ASSERT_TRUE(foundInIntermediateTree("bitfieldExtract (highp uint)"));
}
// Test that signed bitfieldInsert returns a precision consistent with its "base/insert" parameters.
TEST_F(TypeTrackingTest, BuiltInBitfieldInsertResultTypeAndPrecision)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"uniform lowp int iBase;\n"
"uniform mediump int iInsert;\n"
"uniform highp uint uBase;\n"
"uniform mediump uint uInsert;\n"
"uniform highp int offset;\n"
"uniform highp int bits;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" lowp int i = bitfieldInsert(iBase, iInsert, offset, bits);\n"
" lowp uint u = bitfieldInsert(uBase, uInsert, offset, bits);\n"
" my_FragColor = vec4(i, u, 0.0, 1.0); \n"
"}\n";
compile(shaderString);
ASSERT_FALSE(foundErrorInIntermediateTree());
ASSERT_TRUE(foundInIntermediateTree("bitfieldInsert (mediump int)"));
ASSERT_TRUE(foundInIntermediateTree("bitfieldInsert (highp uint)"));
}
// Test that signed bitfieldInsert returns a precision consistent with its "base/insert" parameters.
// Another variation on parameter precisions.
TEST_F(TypeTrackingTest, BuiltInBitfieldInsertResultTypeAndPrecision2)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"uniform highp int iBase;\n"
"uniform mediump int iInsert;\n"
"uniform lowp uint uBase;\n"
"uniform mediump uint uInsert;\n"
"uniform lowp int offset;\n"
"uniform lowp int bits;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" lowp int i = bitfieldInsert(iBase, iInsert, offset, bits);\n"
" lowp uint u = bitfieldInsert(uBase, uInsert, offset, bits);\n"
" my_FragColor = vec4(i, u, 0.0, 1.0); \n"
"}\n";
compile(shaderString);
ASSERT_FALSE(foundErrorInIntermediateTree());
ASSERT_TRUE(foundInIntermediateTree("bitfieldInsert (highp int)"));
ASSERT_TRUE(foundInIntermediateTree("bitfieldInsert (mediump uint)"));
}
// Test that bitfieldReverse always results in highp precision.
TEST_F(TypeTrackingTest, BuiltInBitfieldReversePrecision)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"uniform lowp uint u;\n"
"uniform mediump int i;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" lowp uint u2 = bitfieldReverse(u);\n"
" lowp int i2 = bitfieldReverse(i);\n"
" my_FragColor = vec4(u2, i2, 0.0, 1.0); \n"
"}\n";
compile(shaderString);
ASSERT_FALSE(foundErrorInIntermediateTree());
ASSERT_TRUE(foundInIntermediateTree("bitfieldReverse (highp uint)"));
ASSERT_TRUE(foundInIntermediateTree("bitfieldReverse (highp int)"));
}
// Test that bitCount always results in lowp precision integer.
TEST_F(TypeTrackingTest, BuiltInBitCountTypeAndPrecision)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"uniform highp uint u;\n"
"uniform mediump int i;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" highp int count1 = bitCount(u);\n"
" highp int count2 = bitCount(i);\n"
" my_FragColor = vec4(count1, count2, 0.0, 1.0); \n"
"}\n";
compile(shaderString);
ASSERT_FALSE(foundErrorInIntermediateTree());
ASSERT_TRUE(foundInIntermediateTree("bitCount (lowp int)"));
ASSERT_FALSE(foundInIntermediateTree("bitCount (lowp uint)"));
}
// Test that findLSB always results in a lowp precision integer.
TEST_F(TypeTrackingTest, BuiltInFindLSBTypeAndPrecision)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"uniform highp uint u;\n"
"uniform mediump int i;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" highp int index1 = findLSB(u);\n"
" highp int index2 = findLSB(i);\n"
" my_FragColor = vec4(index1, index2, 0.0, 1.0); \n"
"}\n";
compile(shaderString);
ASSERT_FALSE(foundErrorInIntermediateTree());
ASSERT_TRUE(foundInIntermediateTree("findLSB (lowp int)"));
ASSERT_FALSE(foundInIntermediateTree("findLSB (lowp uint)"));
}
// Test that findMSB always results in a lowp precision integer.
TEST_F(TypeTrackingTest, BuiltInFindMSBTypeAndPrecision)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"uniform highp uint u;\n"
"uniform mediump int i;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" highp int index1 = findMSB(u);\n"
" highp int index2 = findMSB(i);\n"
" my_FragColor = vec4(index1, index2, 0.0, 1.0); \n"
"}\n";
compile(shaderString);
ASSERT_FALSE(foundErrorInIntermediateTree());
ASSERT_TRUE(foundInIntermediateTree("findMSB (lowp int)"));
ASSERT_FALSE(foundInIntermediateTree("findMSB (lowp uint)"));
}
// Test that uaddCarry always results in highp precision.
TEST_F(TypeTrackingTest, BuiltInUaddCarryPrecision)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"uniform mediump uvec2 x;\n"
"uniform mediump uvec2 y;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" lowp uvec2 carry;\n"
" mediump uvec2 result = uaddCarry(x, y, carry);\n"
" my_FragColor = vec4(result.x, carry.y, 0.0, 1.0); \n"
"}\n";
compile(shaderString);
ASSERT_FALSE(foundErrorInIntermediateTree());
ASSERT_TRUE(foundInIntermediateTree("uaddCarry (highp 2-component vector of uint)"));
}
// Test that usubBorrow always results in highp precision.
TEST_F(TypeTrackingTest, BuiltInUsubBorrowPrecision)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"uniform mediump uvec2 x;\n"
"uniform mediump uvec2 y;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" lowp uvec2 borrow;\n"
" mediump uvec2 result = usubBorrow(x, y, borrow);\n"
" my_FragColor = vec4(result.x, borrow.y, 0.0, 1.0); \n"
"}\n";
compile(shaderString);
ASSERT_FALSE(foundErrorInIntermediateTree());
ASSERT_TRUE(foundInIntermediateTree("usubBorrow (highp 2-component vector of uint)"));
}
\ No newline at end of file
......@@ -458,6 +458,22 @@ class GLSLTest_ES3 : public GLSLTest
}
};
class GLSLTest_ES31 : public GLSLTest
{
void SetUp() override
{
ANGLETest::SetUp();
mSimpleVSSource =
"#version 310 es\n"
"in vec4 inputAttribute;"
"void main()"
"{"
" gl_Position = inputAttribute;"
"}";
}
};
TEST_P(GLSLTest, NamelessScopedStructs)
{
const std::string fragmentShaderSource = SHADER_SOURCE
......@@ -2591,6 +2607,30 @@ TEST_P(WebGLGLSLTest, MaxVaryingVec3ArrayAndMaxPlusOneFloatArray)
} // anonymous namespace
// Test that FindLSB and FindMSB return correct values in their corner cases.
TEST_P(GLSLTest_ES31, FindMSBAndFindLSBCornerCases)
{
const std::string &fragmentShader =
"#version 310 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"uniform int u_zero;\n"
"void main() {\n"
" if (findLSB(u_zero) == -1 && findMSB(u_zero) == -1 && findMSB(u_zero - 1) == -1)\n"
" {\n"
" my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
" }\n"
" else\n"
" {\n"
" my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
" }\n"
"}\n";
ANGLE_GL_PROGRAM(program, mSimpleVSSource, fragmentShader);
drawQuad(program.get(), "inputAttribute", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(GLSLTest,
ES2_D3D9(),
......@@ -2605,3 +2645,5 @@ ANGLE_INSTANTIATE_TEST(GLSLTest,
ANGLE_INSTANTIATE_TEST(GLSLTest_ES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(WebGLGLSLTest, ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES());
ANGLE_INSTANTIATE_TEST(GLSLTest_ES31, ES31_D3D11(), ES31_OPENGL(), ES31_OPENGLES());
......@@ -27,3 +27,29 @@ void ConstantFoldingExpressionTest::evaluateFloat(const std::string &floatExpres
<< "}\n";
compileAssumeSuccess(shaderStream.str());
}
void ConstantFoldingExpressionTest::evaluateInt(const std::string &intExpression)
{
std::stringstream shaderStream;
shaderStream << "#version 310 es\n"
"precision mediump int;\n"
"out int my_FragColor;\n"
"void main()\n"
"{\n"
<< " my_FragColor = " << intExpression << ";\n"
<< "}\n";
compileAssumeSuccess(shaderStream.str());
}
void ConstantFoldingExpressionTest::evaluateUint(const std::string &uintExpression)
{
std::stringstream shaderStream;
shaderStream << "#version 310 es\n"
"precision mediump int;\n"
"out uint my_FragColor;\n"
"void main()\n"
"{\n"
<< " my_FragColor = " << uintExpression << ";\n"
<< "}\n";
compileAssumeSuccess(shaderStream.str());
}
......@@ -138,7 +138,7 @@ class ConstantFoldingTest : public ShaderCompileTreeTest
protected:
::GLenum getShaderType() const override { return GL_FRAGMENT_SHADER; }
ShShaderSpec getShaderSpec() const override { return SH_GLES3_SPEC; }
ShShaderSpec getShaderSpec() const override { return SH_GLES3_1_SPEC; }
template <typename T>
bool constantFoundInAST(T constant)
......@@ -177,6 +177,8 @@ class ConstantFoldingExpressionTest : public ConstantFoldingTest
ConstantFoldingExpressionTest() {}
void evaluateFloat(const std::string &floatExpression);
void evaluateInt(const std::string &intExpression);
void evaluateUint(const std::string &uintExpression);
};
} // namespace sh
......
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