Commit 592539fd by Clemen Deng Committed by Commit Bot

Implicit conversions for Desktop GL shaders

Need to support implicit conversions between types for GL shaders Other small fixes to support GL shaders Bug: angleproject:3673 Change-Id: I5341cb7195054ccc4cd36aad5dc8c801c7e1a14f Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1698649 Commit-Queue: Clemen Deng <clemendeng@google.com> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org>
parent 3dcd8ebb
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
// //
#include "compiler/translator/FunctionLookup.h" #include "compiler/translator/FunctionLookup.h"
#include "compiler/translator/ImmutableStringBuilder.h"
namespace sh namespace sh
{ {
...@@ -19,6 +20,27 @@ const char kFunctionMangledNameSeparator = '('; ...@@ -19,6 +20,27 @@ const char kFunctionMangledNameSeparator = '(';
constexpr const ImmutableString kEmptyName(""); constexpr const ImmutableString kEmptyName("");
// Helper function for GetMangledNames
// Gets all ordered combinations of elements in list[currentIndex, end]
std::vector<std::vector<int>> GetImplicitConversionCombinations(const std::vector<int> &list)
{
std::vector<std::vector<int>> target;
target.push_back(std::vector<int>());
for (size_t currentIndex = 0; currentIndex < list.size(); currentIndex++)
{
size_t prevIterSize = target.size();
for (size_t copyIndex = 0; copyIndex < prevIterSize; copyIndex++)
{
std::vector<int> combination = target[copyIndex];
combination.push_back(list[currentIndex]);
target.push_back(combination);
}
}
return target;
}
} // anonymous namespace } // anonymous namespace
TFunctionLookup::TFunctionLookup(const ImmutableString &name, TFunctionLookup::TFunctionLookup(const ImmutableString &name,
...@@ -65,6 +87,60 @@ ImmutableString TFunctionLookup::GetMangledName(const char *functionName, ...@@ -65,6 +87,60 @@ ImmutableString TFunctionLookup::GetMangledName(const char *functionName,
return ImmutableString(newName); return ImmutableString(newName);
} }
std::vector<ImmutableString> GetMangledNames(const char *functionName,
const TIntermSequence &arguments)
{
std::vector<ImmutableString> target;
std::vector<int> indexes;
for (int i = 0; i < static_cast<int>(arguments.size()); i++)
{
TIntermNode *argument = arguments[i];
TBasicType argType = argument->getAsTyped()->getType().getBasicType();
if (argType == EbtInt || argType == EbtUInt)
{
indexes.push_back(i);
}
}
std::vector<std::vector<int>> combinations = GetImplicitConversionCombinations(indexes);
for (const std::vector<int> &combination : combinations)
{
// combination: ordered list of indexes for arguments that should be converted to float
std::string newName(functionName);
newName += kFunctionMangledNameSeparator;
// combination[currentIndex] represents index of next argument to be converted
int currentIndex = 0;
for (int i = 0; i < (int)arguments.size(); i++)
{
TIntermNode *argument = arguments[i];
if (currentIndex != static_cast<int>(combination.size()) &&
combination[currentIndex] == i)
{
// Convert
TType type = argument->getAsTyped()->getType();
type.setBasicType(EbtFloat);
newName += type.getMangledName();
currentIndex++;
}
else
{
// Don't convert
newName += argument->getAsTyped()->getType().getMangledName();
}
}
target.push_back(ImmutableString(newName));
}
return target;
}
std::vector<ImmutableString> TFunctionLookup::getMangledNamesForImplicitConversions() const
{
return GetMangledNames(mName.data(), mArguments);
}
bool TFunctionLookup::isConstructor() const bool TFunctionLookup::isConstructor() const
{ {
return mConstructorType != nullptr; return mConstructorType != nullptr;
......
...@@ -28,6 +28,7 @@ class TFunctionLookup : angle::NonCopyable ...@@ -28,6 +28,7 @@ class TFunctionLookup : angle::NonCopyable
ImmutableString getMangledName() const; ImmutableString getMangledName() const;
static ImmutableString GetMangledName(const char *functionName, static ImmutableString GetMangledName(const char *functionName,
const TIntermSequence &arguments); const TIntermSequence &arguments);
std::vector<ImmutableString> getMangledNamesForImplicitConversions() const;
bool isConstructor() const; bool isConstructor() const;
const TType &constructorType() const; const TType &constructorType() const;
......
...@@ -358,6 +358,7 @@ class TIntermConstantUnion : public TIntermExpression ...@@ -358,6 +358,7 @@ class TIntermConstantUnion : public TIntermExpression
int index); int index);
static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate, static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate,
TDiagnostics *diagnostics); TDiagnostics *diagnostics);
static bool IsFloatDivision(TBasicType t1, TBasicType t2);
protected: protected:
// Same data may be shared between multiple constant unions, so it can't be modified. // Same data may be shared between multiple constant unions, so it can't be modified.
......
...@@ -417,4 +417,4 @@ bool IsAtomicFunction(TOperator op) ...@@ -417,4 +417,4 @@ bool IsAtomicFunction(TOperator op)
default: default:
return false; return false;
} }
} }
\ No newline at end of file
...@@ -5217,9 +5217,11 @@ bool TParseContext::binaryOpCommonCheck(TOperator op, ...@@ -5217,9 +5217,11 @@ bool TParseContext::binaryOpCommonCheck(TOperator op,
break; break;
} }
// GLSL ES 1.00 and 3.00 do not support implicit type casting. ImplicitTypeConversion conversion = GetConversion(left->getBasicType(), right->getBasicType());
// So the basic type should usually match.
if (!isBitShift && left->getBasicType() != right->getBasicType()) // Implicit type casting only supported for GL shaders
if (!isBitShift && conversion != ImplicitTypeConversion::Same &&
(!IsDesktopGLSpec(mShaderSpec) || !IsValidImplicitConversion(conversion, op)))
{ {
return false; return false;
} }
...@@ -5913,6 +5915,14 @@ TIntermTyped *TParseContext::addNonConstructorFunctionCall(TFunctionLookup *fnCa ...@@ -5913,6 +5915,14 @@ TIntermTyped *TParseContext::addNonConstructorFunctionCall(TFunctionLookup *fnCa
// There are no inner functions, so it's enough to look for user-defined functions in the // There are no inner functions, so it's enough to look for user-defined functions in the
// global scope. // global scope.
const TSymbol *symbol = symbolTable.findGlobal(fnCall->getMangledName()); const TSymbol *symbol = symbolTable.findGlobal(fnCall->getMangledName());
if (symbol == nullptr && IsDesktopGLSpec(mShaderSpec))
{
// If using Desktop GL spec, need to check for implicit conversion
symbol = symbolTable.findGlobalWithConversion(
fnCall->getMangledNamesForImplicitConversions());
}
if (symbol != nullptr) if (symbol != nullptr)
{ {
// A user-defined function - could be an overloaded built-in as well. // A user-defined function - could be an overloaded built-in as well.
...@@ -5927,11 +5937,15 @@ TIntermTyped *TParseContext::addNonConstructorFunctionCall(TFunctionLookup *fnCa ...@@ -5927,11 +5937,15 @@ TIntermTyped *TParseContext::addNonConstructorFunctionCall(TFunctionLookup *fnCa
} }
symbol = symbolTable.findBuiltIn(fnCall->getMangledName(), mShaderVersion); symbol = symbolTable.findBuiltIn(fnCall->getMangledName(), mShaderVersion);
if (symbol == nullptr)
if (symbol == nullptr && IsDesktopGLSpec(mShaderSpec))
{ {
error(loc, "no matching overloaded function found", fnCall->name()); // If using Desktop GL spec, need to check for implicit conversion
symbol = symbolTable.findBuiltInWithConversion(
fnCall->getMangledNamesForImplicitConversions(), mShaderVersion);
} }
else
if (symbol != nullptr)
{ {
// A built-in function. // A built-in function.
ASSERT(symbol->symbolType() == SymbolType::BuiltIn); ASSERT(symbol->symbolType() == SymbolType::BuiltIn);
...@@ -5979,6 +5993,10 @@ TIntermTyped *TParseContext::addNonConstructorFunctionCall(TFunctionLookup *fnCa ...@@ -5979,6 +5993,10 @@ TIntermTyped *TParseContext::addNonConstructorFunctionCall(TFunctionLookup *fnCa
functionCallRValueLValueErrorCheck(fnCandidate, callNode); functionCallRValueLValueErrorCheck(fnCandidate, callNode);
return callNode; return callNode;
} }
else
{
error(loc, "no matching overloaded function found", fnCall->name());
}
} }
// Error message was already written. Put on a dummy node for error recovery. // Error message was already written. Put on a dummy node for error recovery.
......
...@@ -252,6 +252,32 @@ const TSymbol *TSymbolTable::findGlobal(const ImmutableString &name) const ...@@ -252,6 +252,32 @@ const TSymbol *TSymbolTable::findGlobal(const ImmutableString &name) const
return mTable[0]->find(name); return mTable[0]->find(name);
} }
const TSymbol *TSymbolTable::findGlobalWithConversion(
const std::vector<ImmutableString> &names) const
{
const TSymbol *target;
for (ImmutableString name : names)
{
target = findGlobal(name);
if (target != nullptr)
break;
}
return target;
}
const TSymbol *TSymbolTable::findBuiltInWithConversion(const std::vector<ImmutableString> &names,
int shaderVersion) const
{
const TSymbol *target;
for (ImmutableString name : names)
{
target = findBuiltIn(name, shaderVersion);
if (target != nullptr)
break;
}
return target;
}
bool TSymbolTable::declare(TSymbol *symbol) bool TSymbolTable::declare(TSymbol *symbol)
{ {
ASSERT(!mTable.empty()); ASSERT(!mTable.empty());
...@@ -338,20 +364,29 @@ void TSymbolTable::initializeBuiltIns(sh::GLenum type, ...@@ -338,20 +364,29 @@ void TSymbolTable::initializeBuiltIns(sh::GLenum type,
// We need just one precision stack level for predefined precisions. // We need just one precision stack level for predefined precisions.
mPrecisionStack.emplace_back(new PrecisionStackLevel); mPrecisionStack.emplace_back(new PrecisionStackLevel);
switch (type) if (IsDesktopGLSpec(spec))
{ {
case GL_FRAGMENT_SHADER: setDefaultPrecision(EbtInt, EbpUndefined);
setDefaultPrecision(EbtInt, EbpMedium); setDefaultPrecision(EbtFloat, EbpUndefined);
break; }
case GL_VERTEX_SHADER: else
case GL_COMPUTE_SHADER: {
case GL_GEOMETRY_SHADER_EXT: switch (type)
setDefaultPrecision(EbtInt, EbpHigh); {
setDefaultPrecision(EbtFloat, EbpHigh); case GL_FRAGMENT_SHADER:
break; setDefaultPrecision(EbtInt, EbpMedium);
default: break;
UNREACHABLE(); case GL_VERTEX_SHADER:
case GL_COMPUTE_SHADER:
case GL_GEOMETRY_SHADER_EXT:
setDefaultPrecision(EbtInt, EbpHigh);
setDefaultPrecision(EbtFloat, EbpHigh);
break;
default:
UNREACHABLE();
}
} }
// Set defaults for sampler types that have default precision, even those that are // Set defaults for sampler types that have default precision, even those that are
// only available if an extension exists. // only available if an extension exists.
// New sampler types in ESSL3 don't have default precision. ESSL1 types do. // New sampler types in ESSL3 don't have default precision. ESSL1 types do.
......
...@@ -116,8 +116,11 @@ class TSymbolTable : angle::NonCopyable, TSymbolTableBase ...@@ -116,8 +116,11 @@ class TSymbolTable : angle::NonCopyable, TSymbolTableBase
TFunction *findUserDefinedFunction(const ImmutableString &name) const; TFunction *findUserDefinedFunction(const ImmutableString &name) const;
const TSymbol *findGlobal(const ImmutableString &name) const; const TSymbol *findGlobal(const ImmutableString &name) const;
const TSymbol *findGlobalWithConversion(const std::vector<ImmutableString> &names) const;
const TSymbol *findBuiltIn(const ImmutableString &name, int shaderVersion) const; const TSymbol *findBuiltIn(const ImmutableString &name, int shaderVersion) const;
const TSymbol *findBuiltInWithConversion(const std::vector<ImmutableString> &names,
int shaderVersion) const;
void setDefaultPrecision(TBasicType type, TPrecision prec); void setDefaultPrecision(TBasicType type, TPrecision prec);
......
...@@ -79,7 +79,6 @@ bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node) ...@@ -79,7 +79,6 @@ bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node)
return true; return true;
} }
ASSERT(constantExponent->getBasicType() == EbtFloat);
float exponentValue = constantExponent->getConstantValue()->getFConst(); float exponentValue = constantExponent->getConstantValue()->getFConst();
// Test 2: exponentValue is in the problematic range. // Test 2: exponentValue is in the problematic range.
......
...@@ -825,4 +825,112 @@ bool IsSpecWithFunctionBodyNewScope(ShShaderSpec shaderSpec, int shaderVersion) ...@@ -825,4 +825,112 @@ bool IsSpecWithFunctionBodyNewScope(ShShaderSpec shaderSpec, int shaderVersion)
return (shaderVersion == 100 && !sh::IsWebGLBasedSpec(shaderSpec)); return (shaderVersion == 100 && !sh::IsWebGLBasedSpec(shaderSpec));
} }
ImplicitTypeConversion GetConversion(TBasicType t1, TBasicType t2)
{
if (t1 == t2)
return ImplicitTypeConversion::Same;
switch (t1)
{
case EbtInt:
switch (t2)
{
case EbtInt:
UNREACHABLE();
break;
case EbtUInt:
return ImplicitTypeConversion::Invalid;
case EbtFloat:
return ImplicitTypeConversion::Left;
default:
return ImplicitTypeConversion::Invalid;
}
break;
case EbtUInt:
switch (t2)
{
case EbtInt:
return ImplicitTypeConversion::Invalid;
case EbtUInt:
UNREACHABLE();
break;
case EbtFloat:
return ImplicitTypeConversion::Left;
default:
return ImplicitTypeConversion::Invalid;
}
break;
case EbtFloat:
switch (t2)
{
case EbtInt:
case EbtUInt:
return ImplicitTypeConversion::Right;
case EbtFloat:
UNREACHABLE();
break;
default:
return ImplicitTypeConversion::Invalid;
}
break;
default:
return ImplicitTypeConversion::Invalid;
}
return ImplicitTypeConversion::Invalid;
}
bool IsValidImplicitConversion(sh::ImplicitTypeConversion conversion, TOperator op)
{
switch (conversion)
{
case sh::ImplicitTypeConversion::Same:
return true;
case sh::ImplicitTypeConversion::Left:
switch (op)
{
case EOpEqual:
case EOpNotEqual:
case EOpLessThan:
case EOpGreaterThan:
case EOpLessThanEqual:
case EOpGreaterThanEqual:
case EOpAdd:
case EOpSub:
case EOpMul:
case EOpDiv:
return true;
default:
break;
}
break;
case sh::ImplicitTypeConversion::Right:
switch (op)
{
case EOpAssign:
case EOpInitialize:
case EOpEqual:
case EOpNotEqual:
case EOpLessThan:
case EOpGreaterThan:
case EOpLessThanEqual:
case EOpGreaterThanEqual:
case EOpAdd:
case EOpSub:
case EOpMul:
case EOpDiv:
case EOpAddAssign:
case EOpSubAssign:
case EOpMulAssign:
case EOpDivAssign:
return true;
default:
break;
}
break;
case sh::ImplicitTypeConversion::Invalid:
break;
}
return false;
}
} // namespace sh } // namespace sh
...@@ -23,6 +23,18 @@ bool atoi_clamp(const char *str, unsigned int *value); ...@@ -23,6 +23,18 @@ bool atoi_clamp(const char *str, unsigned int *value);
namespace sh namespace sh
{ {
// Keeps track of whether an implicit conversion from int/uint to float is possible.
// These conversions are supported in desktop GLSL shaders only.
// Also keeps track of which side of operation should be converted.
enum class ImplicitTypeConversion
{
Same,
Left,
Right,
Invalid,
};
class TIntermBlock; class TIntermBlock;
class TSymbolTable; class TSymbolTable;
class TIntermTyped; class TIntermTyped;
...@@ -70,6 +82,11 @@ bool IsInShaderStorageBlock(TIntermTyped *node); ...@@ -70,6 +82,11 @@ bool IsInShaderStorageBlock(TIntermTyped *node);
GLenum GetImageInternalFormatType(TLayoutImageInternalFormat iifq); GLenum GetImageInternalFormatType(TLayoutImageInternalFormat iifq);
// ESSL 1.00 shaders nest function body scope within function parameter scope // ESSL 1.00 shaders nest function body scope within function parameter scope
bool IsSpecWithFunctionBodyNewScope(ShShaderSpec shaderSpec, int shaderVersion); bool IsSpecWithFunctionBodyNewScope(ShShaderSpec shaderSpec, int shaderVersion);
// Helper functions for implicit conversions
ImplicitTypeConversion GetConversion(TBasicType t1, TBasicType t2);
bool IsValidImplicitConversion(ImplicitTypeConversion conversion, TOperator op);
} // namespace sh } // namespace sh
#endif // COMPILER_TRANSLATOR_UTIL_H_ #endif // COMPILER_TRANSLATOR_UTIL_H_
...@@ -244,15 +244,15 @@ class ShCompileDesktopGLTest : public ShCompileTest ...@@ -244,15 +244,15 @@ class ShCompileDesktopGLTest : public ShCompileTest
}; };
// Test calling sh::Compile with fragment shader source string // Test calling sh::Compile with fragment shader source string
TEST_F(ShCompileDesktopGLTest, FragmentShaderString) TEST_F(ShCompileDesktopGLTest, DesktopGLString)
{ {
constexpr char kComputeShaderString[] = constexpr char kFragmentShaderString[] =
R"(#version 330 R"(#version 330
void main() void main()
{ {
})"; })";
const char *shaderStrings[] = {kComputeShaderString}; const char *shaderStrings[] = {kFragmentShaderString};
testCompile(shaderStrings, 1, true); testCompile(shaderStrings, 1, true);
} }
...@@ -260,35 +260,97 @@ TEST_F(ShCompileDesktopGLTest, FragmentShaderString) ...@@ -260,35 +260,97 @@ TEST_F(ShCompileDesktopGLTest, FragmentShaderString)
// Test calling sh::Compile with core version // Test calling sh::Compile with core version
TEST_F(ShCompileDesktopGLTest, FragmentShaderCoreVersion) TEST_F(ShCompileDesktopGLTest, FragmentShaderCoreVersion)
{ {
constexpr char kComputeShaderString[] = constexpr char kFragmentShaderString[] =
R"(#version 330 core R"(#version 330 core
void main() void main()
{ {
})"; })";
const char *shaderStrings[] = {kComputeShaderString}; const char *shaderStrings[] = {kFragmentShaderString};
testCompile(shaderStrings, 1, true); testCompile(shaderStrings, 1, true);
} }
// Test calling sh::Compile with core version // Implicit conversions in basic operations
TEST_F(ShCompileDesktopGLTest, DISABLED_FragmentShaderAdditionConversion) TEST_F(ShCompileDesktopGLTest, ImplicitConversionBasicOperation)
{ {
constexpr char kComputeShaderString[] = constexpr char kFragmentShaderString[] =
R"(#version 330 core R"(#version 330 core
void main() void main()
{ {
float f = 1 + 1.5; //float a = 1 + 1.5;
//float b = 1 - 1.5;
//float c = 1 * 1.5;
//float d = 1 / 1.5;
//float e = 1.5 + 1;
//float f = 1.5 - 1;
float g = 1.5 * 1;
//float h = 1.5 / 1;
})"; })";
const char *shaderStrings[] = {kComputeShaderString}; const char *shaderStrings[] = {kFragmentShaderString};
testCompile(shaderStrings, 1, true);
}
// Implicit conversions when assigning
TEST_F(ShCompileDesktopGLTest, ImplicitConversionAssign)
{
constexpr char kFragmentShaderString[] =
R"(#version 330 core
void main()
{
float a = 1;
uint b = 2u;
a = b;
a += b;
a -= b;
a *= b;
a /= b;
})";
const char *shaderStrings[] = {kFragmentShaderString};
testCompile(shaderStrings, 1, true);
}
// Implicit conversions for vectors
TEST_F(ShCompileDesktopGLTest, ImplicitConversionVector)
{
constexpr char kFragmentShaderString[] =
R"(#version 330 core
void main()
{
vec3 a;
ivec3 b = ivec3(1, 1, 1);
a = b;
})";
const char *shaderStrings[] = {kFragmentShaderString};
testCompile(shaderStrings, 1, true); testCompile(shaderStrings, 1, true);
} }
// Implicit conversions should not convert between ints and uints
TEST_F(ShCompileDesktopGLTest, ImplicitConversionAssignFailed)
{
constexpr char kFragmentShaderString[] =
R"(#version 330 core
void main()
{
int a = 1;
uint b = 2;
a = b;
})";
const char *shaderStrings[] = {kFragmentShaderString};
testCompile(shaderStrings, 1, false);
}
// GL shaders use implicit conversions between types // GL shaders use implicit conversions between types
// Testing internal implicit conversions // Testing internal implicit conversions
TEST_F(ShCompileDesktopGLTest, DISABLED_FragmentShaderFunctionConversion) TEST_F(ShCompileDesktopGLTest, ImplicitConversionFunction)
{ {
constexpr char kFragmentShaderString[] = constexpr char kFragmentShaderString[] =
R"(#version 330 core R"(#version 330 core
......
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