From e12ba07322cd61c5cf50c25ed8d1f08f6b1ff879 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Thu, 24 Mar 2016 12:38:18 +0100 Subject: Update ANGLE to chromium/2651 Change-Id: I1cd32b780b1a0b913fab870e155ae1f4f9ac40d7 Reviewed-by: Maurice Kalinowski --- .../src/compiler/preprocessor/DiagnosticsBase.cpp | 10 + .../src/compiler/preprocessor/DiagnosticsBase.h | 7 +- .../src/compiler/preprocessor/DirectiveParser.cpp | 236 +- .../src/compiler/preprocessor/DirectiveParser.h | 5 +- .../src/compiler/preprocessor/ExpressionParser.h | 14 +- .../src/compiler/preprocessor/ExpressionParser.y | 151 +- .../angle/src/compiler/preprocessor/Input.cpp | 66 +- .../angle/src/compiler/preprocessor/Input.h | 6 +- .../angle/src/compiler/preprocessor/Macro.cpp | 20 + .../angle/src/compiler/preprocessor/Macro.h | 2 + .../src/compiler/preprocessor/MacroExpander.cpp | 68 +- .../src/compiler/preprocessor/MacroExpander.h | 11 +- .../src/compiler/preprocessor/Preprocessor.cpp | 25 +- .../angle/src/compiler/preprocessor/Tokenizer.h | 2 +- .../angle/src/compiler/preprocessor/Tokenizer.l | 17 +- .../angle/src/compiler/preprocessor/numeric_lex.h | 10 + .../src/compiler/translator/ASTMetadataHLSL.cpp | 451 +++ .../src/compiler/translator/ASTMetadataHLSL.h | 58 + .../translator/ArrayReturnValueToOutParameter.cpp | 206 ++ .../translator/ArrayReturnValueToOutParameter.h | 16 + .../angle/src/compiler/translator/BaseTypes.h | 126 +- .../translator/BuiltInFunctionEmulator.cpp | 144 +- .../compiler/translator/BuiltInFunctionEmulator.h | 46 +- .../translator/BuiltInFunctionEmulatorGLSL.cpp | 162 +- .../translator/BuiltInFunctionEmulatorGLSL.h | 8 +- .../translator/BuiltInFunctionEmulatorHLSL.cpp | 47 +- .../angle/src/compiler/translator/Cache.cpp | 100 + src/3rdparty/angle/src/compiler/translator/Cache.h | 90 + .../angle/src/compiler/translator/CallDAG.cpp | 293 ++ .../angle/src/compiler/translator/CallDAG.h | 75 + .../angle/src/compiler/translator/CodeGen.cpp | 38 +- .../angle/src/compiler/translator/Common.h | 27 +- .../angle/src/compiler/translator/Compiler.cpp | 329 +- .../angle/src/compiler/translator/Compiler.h | 62 +- .../angle/src/compiler/translator/ConstantUnion.h | 60 +- .../angle/src/compiler/translator/Diagnostics.cpp | 7 +- .../angle/src/compiler/translator/Diagnostics.h | 8 +- .../src/compiler/translator/DirectiveHandler.cpp | 20 +- .../src/compiler/translator/DirectiveHandler.h | 31 +- .../src/compiler/translator/EmulatePrecision.cpp | 127 +- .../src/compiler/translator/EmulatePrecision.h | 26 +- .../src/compiler/translator/ExtensionBehavior.h | 6 + .../src/compiler/translator/ExtensionGLSL.cpp | 100 + .../angle/src/compiler/translator/ExtensionGLSL.h | 39 + .../src/compiler/translator/FlagStd140Structs.h | 10 +- .../src/compiler/translator/ForLoopUnroll.cpp | 29 +- .../angle/src/compiler/translator/ForLoopUnroll.h | 15 +- .../angle/src/compiler/translator/Initialize.cpp | 123 +- .../src/compiler/translator/InitializeDll.cpp | 4 + .../compiler/translator/InitializeParseContext.h | 2 +- .../compiler/translator/InitializeVariables.cpp | 9 +- .../src/compiler/translator/InitializeVariables.h | 17 +- .../angle/src/compiler/translator/IntermNode.cpp | 2392 ++++++++++--- .../angle/src/compiler/translator/IntermNode.h | 524 ++- .../src/compiler/translator/IntermTraverse.cpp | 660 +++- .../angle/src/compiler/translator/Intermediate.cpp | 189 +- .../angle/src/compiler/translator/Intermediate.h | 22 +- .../angle/src/compiler/translator/NodeSearch.h | 25 +- .../angle/src/compiler/translator/Operator.cpp | 6 + .../angle/src/compiler/translator/Operator.h | 7 +- .../angle/src/compiler/translator/OutputESSL.h | 3 +- .../angle/src/compiler/translator/OutputGLSL.cpp | 15 +- .../angle/src/compiler/translator/OutputGLSL.h | 6 +- .../src/compiler/translator/OutputGLSLBase.cpp | 322 +- .../angle/src/compiler/translator/OutputGLSLBase.h | 31 +- .../angle/src/compiler/translator/OutputHLSL.cpp | 1568 ++++++--- .../angle/src/compiler/translator/OutputHLSL.h | 59 +- .../angle/src/compiler/translator/ParseContext.cpp | 3544 +++++++++++++------- .../angle/src/compiler/translator/ParseContext.h | 473 ++- .../angle/src/compiler/translator/PoolAlloc.h | 25 +- .../compiler/translator/PruneEmptyDeclarations.cpp | 81 + .../compiler/translator/PruneEmptyDeclarations.h | 15 + .../translator/RecordConstantPrecision.cpp | 157 + .../compiler/translator/RecordConstantPrecision.h | 23 + .../compiler/translator/RegenerateStructNames.cpp | 2 +- .../compiler/translator/RegenerateStructNames.h | 7 +- .../compiler/translator/RemoveDynamicIndexing.cpp | 513 +++ .../compiler/translator/RemoveDynamicIndexing.h | 21 + .../angle/src/compiler/translator/RemovePow.cpp | 105 + .../angle/src/compiler/translator/RemovePow.h | 18 + .../angle/src/compiler/translator/RenameFunction.h | 2 +- .../src/compiler/translator/RewriteDoWhile.cpp | 163 + .../angle/src/compiler/translator/RewriteDoWhile.h | 16 + .../src/compiler/translator/RewriteElseBlocks.cpp | 58 +- .../src/compiler/translator/RewriteElseBlocks.h | 2 +- .../ScalarizeVecAndMatConstructorArgs.cpp | 22 +- .../translator/ScalarizeVecAndMatConstructorArgs.h | 5 +- .../angle/src/compiler/translator/SearchSymbol.cpp | 4 +- .../angle/src/compiler/translator/SearchSymbol.h | 2 +- .../translator/SeparateArrayInitialization.cpp | 92 + .../translator/SeparateArrayInitialization.h | 25 + .../compiler/translator/SeparateDeclarations.cpp | 77 + .../src/compiler/translator/SeparateDeclarations.h | 23 + .../SeparateExpressionsReturningArrays.cpp | 169 + .../SeparateExpressionsReturningArrays.h | 19 + .../angle/src/compiler/translator/ShaderLang.cpp | 67 +- .../angle/src/compiler/translator/ShaderVars.cpp | 74 +- .../angle/src/compiler/translator/SymbolTable.cpp | 80 +- .../angle/src/compiler/translator/SymbolTable.h | 173 +- .../src/compiler/translator/TranslatorESSL.cpp | 24 +- .../angle/src/compiler/translator/TranslatorESSL.h | 2 +- .../src/compiler/translator/TranslatorGLSL.cpp | 159 +- .../angle/src/compiler/translator/TranslatorGLSL.h | 4 +- .../src/compiler/translator/TranslatorHLSL.cpp | 36 +- .../angle/src/compiler/translator/TranslatorHLSL.h | 7 +- .../angle/src/compiler/translator/Types.cpp | 45 +- src/3rdparty/angle/src/compiler/translator/Types.h | 151 +- .../compiler/translator/UnfoldShortCircuitAST.cpp | 4 +- .../compiler/translator/UnfoldShortCircuitAST.h | 7 +- .../compiler/translator/UnfoldShortCircuitToIf.cpp | 368 ++ .../compiler/translator/UnfoldShortCircuitToIf.h | 18 + .../angle/src/compiler/translator/UniformHLSL.cpp | 128 +- .../angle/src/compiler/translator/UniformHLSL.h | 12 +- .../angle/src/compiler/translator/UtilsHLSL.cpp | 234 +- .../angle/src/compiler/translator/UtilsHLSL.h | 47 +- .../translator/ValidateGlobalInitializer.cpp | 112 + .../translator/ValidateGlobalInitializer.h | 16 + .../compiler/translator/ValidateLimitations.cpp | 73 +- .../src/compiler/translator/ValidateLimitations.h | 16 +- .../src/compiler/translator/ValidateOutputs.cpp | 98 +- .../src/compiler/translator/ValidateOutputs.h | 18 +- .../angle/src/compiler/translator/ValidateSwitch.h | 2 +- .../angle/src/compiler/translator/VariableInfo.cpp | 216 +- .../angle/src/compiler/translator/VariableInfo.h | 17 +- .../angle/src/compiler/translator/VersionGLSL.cpp | 59 +- .../angle/src/compiler/translator/VersionGLSL.h | 26 +- .../angle/src/compiler/translator/blocklayout.cpp | 5 +- .../angle/src/compiler/translator/blocklayout.h | 21 +- .../src/compiler/translator/blocklayoutHLSL.cpp | 12 +- .../translator/depgraph/DependencyGraph.cpp | 2 - .../compiler/translator/depgraph/DependencyGraph.h | 55 +- .../translator/depgraph/DependencyGraphBuilder.h | 10 +- .../translator/depgraph/DependencyGraphOutput.cpp | 3 +- .../angle/src/compiler/translator/glslang.h | 2 +- .../angle/src/compiler/translator/glslang.l | 97 +- .../angle/src/compiler/translator/glslang.y | 407 +-- .../angle/src/compiler/translator/intermOut.cpp | 61 +- .../timing/RestrictFragmentShaderTiming.cpp | 10 +- .../translator/timing/RestrictVertexShaderTiming.h | 3 +- .../angle/src/compiler/translator/util.cpp | 20 +- src/3rdparty/angle/src/compiler/translator/util.h | 9 +- 141 files changed, 13499 insertions(+), 4564 deletions(-) create mode 100644 src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.h create mode 100644 src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h create mode 100644 src/3rdparty/angle/src/compiler/translator/Cache.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/Cache.h create mode 100644 src/3rdparty/angle/src/compiler/translator/CallDAG.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/CallDAG.h create mode 100644 src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.h create mode 100644 src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.h create mode 100644 src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.h create mode 100644 src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.h create mode 100644 src/3rdparty/angle/src/compiler/translator/RemovePow.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/RemovePow.h create mode 100644 src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.h create mode 100644 src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.h create mode 100644 src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.h create mode 100644 src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.h create mode 100644 src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.h create mode 100644 src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.cpp create mode 100644 src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.h (limited to 'src/3rdparty/angle/src/compiler') diff --git a/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.cpp b/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.cpp index cf60bc2349..68c6e9cea4 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.cpp +++ b/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.cpp @@ -78,6 +78,8 @@ std::string Diagnostics::message(ID id) 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: @@ -103,12 +105,16 @@ std::string Diagnostics::message(ID id) 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"; // Errors end. // Warnings begin. case PP_EOF_IN_DIRECTIVE: @@ -117,6 +123,10 @@ std::string Diagnostics::message(ID id) 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: assert(false); diff --git a/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.h b/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.h index 5922d03857..d26c174f01 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/DiagnosticsBase.h @@ -46,27 +46,32 @@ class Diagnostics PP_MACRO_UNTERMINATED_INVOCATION, 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_ERROR_END, PP_WARNING_BEGIN, PP_EOF_IN_DIRECTIVE, - PP_CONDITIONAL_UNEXPECTED_TOKEN, PP_UNRECOGNIZED_PRAGMA, + PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1, + PP_WARNING_MACRO_NAME_RESERVED, PP_WARNING_END }; diff --git a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.cpp b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.cpp index 7803ee845a..2faa331378 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.cpp +++ b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.cpp @@ -6,6 +6,7 @@ #include "DirectiveParser.h" +#include #include #include #include @@ -118,14 +119,12 @@ void skipUntilEOD(pp::Lexer *lexer, pp::Token *token) bool isMacroNameReserved(const std::string &name) { // Names prefixed with "GL_" are reserved. - if (name.substr(0, 3) == "GL_") - return true; - - // Names containing two consecutive underscores are reserved. - if (name.find("__") != std::string::npos) - return true; + return (name.substr(0, 3) == "GL_"); +} - return false; +bool hasDoubleUnderscores(const std::string &name) +{ + return (name.find("__") != std::string::npos); } bool isMacroPredefined(const std::string &name, @@ -140,80 +139,17 @@ bool isMacroPredefined(const std::string &name, namespace pp { -class DefinedParser : public Lexer -{ - public: - DefinedParser(Lexer *lexer, - const MacroSet *macroSet, - Diagnostics *diagnostics) - : mLexer(lexer), - mMacroSet(macroSet), - mDiagnostics(diagnostics) - { - } - - protected: - virtual void lex(Token *token) - { - const char kDefined[] = "defined"; - - mLexer->lex(token); - if (token->type != Token::IDENTIFIER) - return; - if (token->text != kDefined) - return; - - bool paren = false; - mLexer->lex(token); - if (token->type == '(') - { - paren = true; - mLexer->lex(token); - } - - if (token->type != Token::IDENTIFIER) - { - mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, - token->location, token->text); - skipUntilEOD(mLexer, token); - return; - } - MacroSet::const_iterator iter = mMacroSet->find(token->text); - std::string expression = iter != mMacroSet->end() ? "1" : "0"; - - if (paren) - { - mLexer->lex(token); - if (token->type != ')') - { - mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, - token->location, token->text); - skipUntilEOD(mLexer, token); - return; - } - } - - // We have a valid defined operator. - // Convert the current token into a CONST_INT token. - token->type = Token::CONST_INT; - token->text = expression; - } - - private: - Lexer *mLexer; - const MacroSet *mMacroSet; - Diagnostics *mDiagnostics; -}; - DirectiveParser::DirectiveParser(Tokenizer *tokenizer, MacroSet *macroSet, Diagnostics *diagnostics, DirectiveHandler *directiveHandler) : mPastFirstStatement(false), + mSeenNonPreprocessorToken(false), mTokenizer(tokenizer), mMacroSet(macroSet), mDiagnostics(diagnostics), - mDirectiveHandler(directiveHandler) + mDirectiveHandler(directiveHandler), + mShaderVersion(100) { } @@ -228,6 +164,10 @@ void DirectiveParser::lex(Token *token) parseDirective(token); mPastFirstStatement = true; } + else if (!isEOD(token)) + { + mSeenNonPreprocessorToken = true; + } if (token->type == Token::LAST) { @@ -349,6 +289,16 @@ void DirectiveParser::parseDefine(Token *token) token->location, token->text); return; } + // Using double underscores is allowed, but may result in unintended + // behavior, so a warning is issued. At the time of writing this was + // specified in ESSL 3.10, but the intent judging from Khronos + // discussions and dEQP tests was that double underscores should be + // allowed in earlier ESSL versions too. + if (hasDoubleUnderscores(token->text)) + { + mDiagnostics->report(Diagnostics::PP_WARNING_MACRO_NAME_RESERVED, token->location, + token->text); + } Macro macro; macro.type = Macro::kTypeObj; @@ -364,6 +314,14 @@ void DirectiveParser::parseDefine(Token *token) mTokenizer->lex(token); if (token->type != Token::IDENTIFIER) break; + + if (std::find(macro.parameters.begin(), macro.parameters.end(), token->text) != macro.parameters.end()) + { + mDiagnostics->report(Diagnostics::PP_MACRO_DUPLICATE_PARAMETER_NAMES, + token->location, token->text); + return; + } + macro.parameters.push_back(token->text); mTokenizer->lex(token); // Get ','. @@ -435,6 +393,12 @@ void DirectiveParser::parseUndef(Token *token) } mTokenizer->lex(token); + if (!isEOD(token)) + { + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, + token->location, token->text); + skipUntilEOD(mTokenizer, token); + } } void DirectiveParser::parseIf(Token *token) @@ -486,7 +450,7 @@ void DirectiveParser::parseElse(Token *token) block.skipGroup = block.foundValidGroup; block.foundValidGroup = true; - // Warn if there are extra tokens after #else. + // Check if there are extra tokens after #else. mTokenizer->lex(token); if (!isEOD(token)) { @@ -550,7 +514,7 @@ void DirectiveParser::parseEndif(Token *token) mConditionalStack.pop_back(); - // Warn if there are tokens after #endif. + // Check if there are tokens after #endif. mTokenizer->lex(token); if (!isEOD(token)) { @@ -699,6 +663,20 @@ void DirectiveParser::parseExtension(Token *token) token->location, token->text); valid = false; } + if (valid && mSeenNonPreprocessorToken) + { + if (mShaderVersion >= 300) + { + mDiagnostics->report(Diagnostics::PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3, + token->location, token->text); + valid = false; + } + else + { + mDiagnostics->report(Diagnostics::PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1, + token->location, token->text); + } + } if (valid) mDirectiveHandler->handleExtension(token->location, name, behavior); } @@ -775,9 +753,18 @@ void DirectiveParser::parseVersion(Token *token) valid = false; } + if (valid && version >= 300 && token->location.line > 1) + { + mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_LINE_ESSL3, + token->location, token->text); + valid = false; + } + if (valid) { mDirectiveHandler->handleVersion(token->location, version); + mShaderVersion = version; + PredefineMacro(mMacroSet, "__VERSION__", version); } } @@ -785,72 +772,60 @@ void DirectiveParser::parseLine(Token *token) { assert(getDirective(token) == DIRECTIVE_LINE); - enum State - { - LINE_NUMBER, - FILE_NUMBER - }; - bool valid = true; + bool parsedFileNumber = false; int line = 0, file = 0; - int state = LINE_NUMBER; - MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics); + MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, false); + + // Lex the first token after "#line" so we can check it for EOD. macroExpander.lex(token); - while ((token->type != '\n') && (token->type != Token::LAST)) + + if (isEOD(token)) { - switch (state++) + mDiagnostics->report(Diagnostics::PP_INVALID_LINE_DIRECTIVE, token->location, token->text); + valid = false; + } + else + { + ExpressionParser expressionParser(¯oExpander, mDiagnostics); + ExpressionParser::ErrorSettings errorSettings; + + // See GLES3 section 12.42 + errorSettings.integerLiteralsMustFit32BitSignedRange = true; + + errorSettings.unexpectedIdentifier = Diagnostics::PP_INVALID_LINE_NUMBER; + // The first token was lexed earlier to check if it was EOD. Include + // the token in parsing for a second time by setting the + // parsePresetToken flag to true. + expressionParser.parse(token, &line, true, errorSettings, &valid); + if (!isEOD(token) && valid) + { + errorSettings.unexpectedIdentifier = Diagnostics::PP_INVALID_FILE_NUMBER; + // After parsing the line expression expressionParser has also + // advanced to the first token of the file expression - this is the + // token that makes the parser reduce the "input" rule for the line + // expression and stop. So we're using parsePresetToken = true here + // as well. + expressionParser.parse(token, &file, true, errorSettings, &valid); + parsedFileNumber = true; + } + if (!isEOD(token)) { - case LINE_NUMBER: - if (valid && (token->type != Token::CONST_INT)) - { - mDiagnostics->report(Diagnostics::PP_INVALID_LINE_NUMBER, - token->location, token->text); - valid = false; - } - if (valid && !token->iValue(&line)) - { - mDiagnostics->report(Diagnostics::PP_INTEGER_OVERFLOW, - token->location, token->text); - valid = false; - } - break; - case FILE_NUMBER: - if (valid && (token->type != Token::CONST_INT)) - { - mDiagnostics->report(Diagnostics::PP_INVALID_FILE_NUMBER, - token->location, token->text); - valid = false; - } - if (valid && !token->iValue(&file)) - { - mDiagnostics->report(Diagnostics::PP_INTEGER_OVERFLOW, - token->location, token->text); - valid = false; - } - break; - default: if (valid) { mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text); valid = false; } - break; + skipUntilEOD(mTokenizer, token); } - macroExpander.lex(token); } - if (valid && (state != FILE_NUMBER) && (state != FILE_NUMBER + 1)) - { - mDiagnostics->report(Diagnostics::PP_INVALID_LINE_DIRECTIVE, - token->location, token->text); - valid = false; - } if (valid) { mTokenizer->setLineNumber(line); - if (state == FILE_NUMBER + 1) + if (parsedFileNumber) mTokenizer->setFileNumber(file); } } @@ -910,15 +885,18 @@ int DirectiveParser::parseExpressionIf(Token *token) assert((getDirective(token) == DIRECTIVE_IF) || (getDirective(token) == DIRECTIVE_ELIF)); - DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics); - MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics); + MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, true); ExpressionParser expressionParser(¯oExpander, mDiagnostics); int expression = 0; - macroExpander.lex(token); - expressionParser.parse(token, &expression); + ExpressionParser::ErrorSettings errorSettings; + errorSettings.integerLiteralsMustFit32BitSignedRange = false; + errorSettings.unexpectedIdentifier = Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN; + + bool valid = true; + expressionParser.parse(token, &expression, false, errorSettings, &valid); - // Warn if there are tokens after #if expression. + // Check if there are tokens after #if expression. if (!isEOD(token)) { mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, @@ -946,7 +924,7 @@ int DirectiveParser::parseExpressionIfdef(Token *token) MacroSet::const_iterator iter = mMacroSet->find(token->text); int expression = iter != mMacroSet->end() ? 1 : 0; - // Warn if there are tokens after #ifdef expression. + // Check if there are tokens after #ifdef expression. mTokenizer->lex(token); if (!isEOD(token)) { diff --git a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.h b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.h index e1acdbb8d0..2888e289ce 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/DirectiveParser.h @@ -27,7 +27,7 @@ class DirectiveParser : public Lexer Diagnostics *diagnostics, DirectiveHandler *directiveHandler); - virtual void lex(Token *token); + void lex(Token *token) override; private: PP_DISALLOW_COPY_AND_ASSIGN(DirectiveParser); @@ -70,11 +70,14 @@ class DirectiveParser : public Lexer } }; 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 mConditionalStack; Tokenizer *mTokenizer; MacroSet *mMacroSet; Diagnostics *mDiagnostics; DirectiveHandler *mDirectiveHandler; + int mShaderVersion; }; } // namespace pp diff --git a/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.h b/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.h index 4b80ba7261..841c67b61c 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.h @@ -7,21 +7,31 @@ #ifndef COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_ #define COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_ +#include "DiagnosticsBase.h" #include "pp_utils.h" namespace pp { -class Diagnostics; class Lexer; struct Token; class ExpressionParser { public: + struct ErrorSettings + { + Diagnostics::ID unexpectedIdentifier; + bool integerLiteralsMustFit32BitSignedRange; + }; + ExpressionParser(Lexer *lexer, Diagnostics *diagnostics); - bool parse(Token *token, int *result); + bool parse(Token *token, + int *result, + bool parsePresetToken, + const ErrorSettings &errorSettings, + bool *valid); private: PP_DISALLOW_COPY_AND_ASSIGN(ExpressionParser); diff --git a/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.y b/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.y index 8caf36bfc8..7b5d9e9cee 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.y +++ b/src/3rdparty/angle/src/compiler/preprocessor/ExpressionParser.y @@ -28,7 +28,7 @@ WHICH GENERATES THE GLSL ES preprocessor expression parser. #pragma GCC diagnostic ignored "-Wuninitialized" #endif #elif defined(_MSC_VER) -#pragma warning(disable: 4065 4701 4702) +#pragma warning(disable: 4065 4244 4701 4702) #endif #include "ExpressionParser.h" @@ -52,6 +52,7 @@ typedef __int64 YYSTYPE; #include typedef intmax_t YYSTYPE; #endif // _MSC_VER + #define YYENABLE_NLS 0 #define YYLTYPE_IS_TRIVIAL 1 #define YYSTYPE_IS_TRIVIAL 1 @@ -64,6 +65,17 @@ struct Context 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 %} @@ -79,6 +91,7 @@ static void yyerror(Context* context, const char* reason); %} %token TOK_CONST_INT +%token TOK_IDENTIFIER %left TOK_OP_OR %left TOK_OP_AND %left '|' @@ -102,11 +115,58 @@ input expression : TOK_CONST_INT - | expression TOK_OP_OR expression { - $$ = $1 || $3; + | 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_AND expression { - $$ = $1 && $3; + | 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(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(0); + } + else + { + $$ = $1 && $4; + } } | expression '|' expression { $$ = $1 | $3; @@ -148,28 +208,42 @@ expression $$ = $1 + $3; } | expression '%' expression { - if ($3 == 0) { - 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()); - YYABORT; - } else { + 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(0); + } + else + { $$ = $1 % $3; } } | expression '/' expression { - if ($3 == 0) { - 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()); - YYABORT; - } else { + 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(0); + } + else + { $$ = $1 / $3; } } @@ -197,22 +271,35 @@ expression int yylex(YYSTYPE *lvalp, Context *context) { + pp::Token *token = context->token; + if (!context->parsePresetToken) + { + context->lexer->lex(token); + } + context->parsePresetToken = false; + int type = 0; - pp::Token *token = context->token; switch (token->type) { case pp::Token::CONST_INT: { unsigned int val = 0; - if (!token->uValue(&val)) + 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(val); type = TOK_CONST_INT; break; } + case pp::Token::IDENTIFIER: + *lvalp = static_cast(-1); + type = TOK_IDENTIFIER; + break; case pp::Token::OP_OR: type = TOK_OP_OR; break; @@ -258,10 +345,6 @@ int yylex(YYSTYPE *lvalp, Context *context) break; } - // Advance to the next token if the current one is valid. - if (type != 0) - context->lexer->lex(token); - return type; } @@ -280,13 +363,21 @@ ExpressionParser::ExpressionParser(Lexer *lexer, Diagnostics *diagnostics) { } -bool ExpressionParser::parse(Token *token, int *result) +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) { diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Input.cpp b/src/3rdparty/angle/src/compiler/preprocessor/Input.cpp index f9910a6cc3..5541d46f72 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/Input.cpp +++ b/src/3rdparty/angle/src/compiler/preprocessor/Input.cpp @@ -29,13 +29,75 @@ Input::Input(size_t count, const char *const string[], const int length[]) : } } -size_t Input::read(char *buf, size_t maxSize) +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; - while ((nRead < maxSize) && (mReadLoc.sIndex < mCount)) + // 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; diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Input.h b/src/3rdparty/angle/src/compiler/preprocessor/Input.h index e951cb4d5f..a1de7ddd86 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/Input.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/Input.h @@ -33,7 +33,7 @@ class Input return mLength[index]; } - size_t read(char *buf, size_t maxSize); + size_t read(char *buf, size_t maxSize, int *lineNo); struct Location { @@ -49,6 +49,10 @@ class Input 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; diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Macro.cpp b/src/3rdparty/angle/src/compiler/preprocessor/Macro.cpp index 13cb14e3dc..4c4d5fd2e2 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/Macro.cpp +++ b/src/3rdparty/angle/src/compiler/preprocessor/Macro.cpp @@ -6,6 +6,8 @@ #include "Macro.h" +#include + #include "Token.h" namespace pp @@ -19,5 +21,23 @@ bool Macro::equals(const Macro &other) const (replacements == other.replacements); } +void PredefineMacro(MacroSet *macroSet, const char *name, int value) +{ + std::ostringstream stream; + stream << value; + + Token token; + token.type = Token::CONST_INT; + token.text = stream.str(); + + Macro macro; + macro.predefined = true; + macro.type = Macro::kTypeObj; + macro.name = name; + macro.replacements.push_back(token); + + (*macroSet)[name] = macro; +} + } // namespace pp diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Macro.h b/src/3rdparty/angle/src/compiler/preprocessor/Macro.h index 7662a9c5a2..31ee22c26a 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/Macro.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/Macro.h @@ -45,6 +45,8 @@ struct Macro typedef std::map MacroSet; +void PredefineMacro(MacroSet *macroSet, const char *name, int value); + } // namespace pp #endif // COMPILER_PREPROCESSOR_MACRO_H_ diff --git a/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.cpp b/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.cpp index 69e2f39069..e878ee345a 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.cpp +++ b/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.cpp @@ -26,7 +26,7 @@ class TokenLexer : public Lexer mIter = mTokens.begin(); } - virtual void lex(Token *token) + void lex(Token *token) override { if (mIter == mTokens.end()) { @@ -48,10 +48,9 @@ class TokenLexer : public Lexer MacroExpander::MacroExpander(Lexer *lexer, MacroSet *macroSet, - Diagnostics *diagnostics) - : mLexer(lexer), - mMacroSet(macroSet), - mDiagnostics(diagnostics) + Diagnostics *diagnostics, + bool parseDefined) + : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics), mParseDefined(parseDefined) { } @@ -67,11 +66,54 @@ void MacroExpander::lex(Token *token) { while (true) { + const char kDefined[] = "defined"; + getToken(token); if (token->type != Token::IDENTIFIER) break; + // Defined operator is parsed here since it may be generated by macro expansion. + // Defined operator produced by macro expansion has undefined behavior according to C++ + // spec, which the GLSL spec references (see C++14 draft spec section 16.1.4), but this + // behavior is needed for passing dEQP tests, which enforce stricter compatibility between + // implementations. + if (mParseDefined && token->text == kDefined) + { + bool paren = false; + getToken(token); + if (token->type == '(') + { + paren = true; + getToken(token); + } + if (token->type != Token::IDENTIFIER) + { + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, + token->text); + break; + } + auto iter = mMacroSet->find(token->text); + std::string expression = iter != mMacroSet->end() ? "1" : "0"; + + if (paren) + { + getToken(token); + if (token->type != ')') + { + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, + token->text); + break; + } + } + + // We have a valid defined operator. + // Convert the current token into a CONST_INT token. + token->type = Token::CONST_INT; + token->text = expression; + break; + } + if (token->expansionDisabled()) break; @@ -187,6 +229,12 @@ bool MacroExpander::expandMacro(const Macro ¯o, std::vector *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(), @@ -218,7 +266,7 @@ bool MacroExpander::expandMacro(const Macro ¯o, assert(macro.type == Macro::kTypeFunc); std::vector args; args.reserve(macro.parameters.size()); - if (!collectMacroArgs(macro, identifier, &args)) + if (!collectMacroArgs(macro, identifier, &args, &replacementLocation)) return false; replaceMacroParams(macro, args, replacements); @@ -234,14 +282,15 @@ bool MacroExpander::expandMacro(const Macro ¯o, repl.setAtStartOfLine(identifier.atStartOfLine()); repl.setHasLeadingSpace(identifier.hasLeadingSpace()); } - repl.location = identifier.location; + repl.location = replacementLocation; } return true; } bool MacroExpander::collectMacroArgs(const Macro ¯o, const Token &identifier, - std::vector *args) + std::vector *args, + SourceLocation *closingParenthesisLocation) { Token token; getToken(&token); @@ -271,6 +320,7 @@ bool MacroExpander::collectMacroArgs(const Macro ¯o, case ')': --openParens; isArg = openParens != 0; + *closingParenthesisLocation = token.location; break; case ',': // The individual arguments are separated by comma tokens, but @@ -317,7 +367,7 @@ bool MacroExpander::collectMacroArgs(const Macro ¯o, { MacroArg &arg = args->at(i); TokenLexer lexer(&arg); - MacroExpander expander(&lexer, mMacroSet, mDiagnostics); + MacroExpander expander(&lexer, mMacroSet, mDiagnostics, mParseDefined); arg.clear(); expander.lex(&token); diff --git a/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.h b/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.h index 5a0c7751a8..3cc860d753 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/MacroExpander.h @@ -19,14 +19,15 @@ namespace pp { class Diagnostics; +struct SourceLocation; class MacroExpander : public Lexer { public: - MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics); - virtual ~MacroExpander(); + MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics, bool parseDefined); + ~MacroExpander() override; - virtual void lex(Token *token); + void lex(Token *token) override; private: PP_DISALLOW_COPY_AND_ASSIGN(MacroExpander); @@ -45,7 +46,8 @@ class MacroExpander : public Lexer typedef std::vector MacroArg; bool collectMacroArgs(const Macro ¯o, const Token &identifier, - std::vector *args); + std::vector *args, + SourceLocation *closingParenthesisLocation); void replaceMacroParams(const Macro ¯o, const std::vector &args, std::vector *replacements); @@ -79,6 +81,7 @@ class MacroExpander : public Lexer Lexer *mLexer; MacroSet *mMacroSet; Diagnostics *mDiagnostics; + bool mParseDefined; std::auto_ptr mReserveToken; std::vector mContextStack; diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.cpp b/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.cpp index 3522fa1abb..aeb9c46f9d 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.cpp +++ b/src/3rdparty/angle/src/compiler/preprocessor/Preprocessor.cpp @@ -7,7 +7,6 @@ #include "Preprocessor.h" #include -#include #include "DiagnosticsBase.h" #include "DirectiveParser.h" @@ -27,12 +26,11 @@ struct PreprocessorImpl DirectiveParser directiveParser; MacroExpander macroExpander; - PreprocessorImpl(Diagnostics *diag, - DirectiveHandler *directiveHandler) + PreprocessorImpl(Diagnostics *diag, DirectiveHandler *directiveHandler) : diagnostics(diag), tokenizer(diag), directiveParser(&tokenizer, ¯oSet, diag, directiveHandler), - macroExpander(&directiveParser, ¯oSet, diag) + macroExpander(&directiveParser, ¯oSet, diag, false) { } }; @@ -52,12 +50,12 @@ bool Preprocessor::init(size_t count, const char * const string[], const int length[]) { - static const int kGLSLVersion = 100; + static const int kDefaultGLSLVersion = 100; // Add standard pre-defined macros. predefineMacro("__LINE__", 0); predefineMacro("__FILE__", 0); - predefineMacro("__VERSION__", kGLSLVersion); + predefineMacro("__VERSION__", kDefaultGLSLVersion); predefineMacro("GL_ES", 1); return mImpl->tokenizer.init(count, string, length); @@ -65,20 +63,7 @@ bool Preprocessor::init(size_t count, void Preprocessor::predefineMacro(const char *name, int value) { - std::ostringstream stream; - stream << value; - - Token token; - token.type = Token::CONST_INT; - token.text = stream.str(); - - Macro macro; - macro.predefined = true; - macro.type = Macro::kTypeObj; - macro.name = name; - macro.replacements.push_back(token); - - mImpl->macroSet[name] = macro; + PredefineMacro(&mImpl->macroSet, name, value); } void Preprocessor::lex(Token *token) diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.h b/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.h index 78eb86dd3b..49e64fa209 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.h @@ -42,7 +42,7 @@ class Tokenizer : public Lexer void setLineNumber(int line); void setMaxTokenSize(size_t maxTokenSize); - virtual void lex(Token *token); + void lex(Token *token) override; private: PP_DISALLOW_COPY_AND_ASSIGN(Tokenizer); diff --git a/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.l b/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.l index 89cb5c8596..d316da88b1 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.l +++ b/src/3rdparty/angle/src/compiler/preprocessor/Tokenizer.l @@ -23,6 +23,10 @@ IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh. } %{ +#if defined(_MSC_VER) +#pragma warning(disable: 4005) +#endif + #include "Tokenizer.h" #include "DiagnosticsBase.h" @@ -31,6 +35,15 @@ IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh. #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; @@ -64,7 +77,7 @@ typedef pp::SourceLocation YYLTYPE; } while(0); #define YY_INPUT(buf, result, maxSize) \ - result = yyextra->input.read(buf, maxSize); + result = yyextra->input.read(buf, maxSize, &yylineno); %} @@ -93,7 +106,7 @@ FRACTIONAL_CONSTANT ({DIGIT}*"."{DIGIT}+)|({DIGIT}+".") /* Block comment */ /* Line breaks are just counted - not returned. */ - /* The comment is replaced by a single space. */ + /* The comment is replaced by a single space. */ "/*" { BEGIN(COMMENT); } [^*\r\n]+ "*" diff --git a/src/3rdparty/angle/src/compiler/preprocessor/numeric_lex.h b/src/3rdparty/angle/src/compiler/preprocessor/numeric_lex.h index 58c51b0961..b32e42253f 100644 --- a/src/3rdparty/angle/src/compiler/preprocessor/numeric_lex.h +++ b/src/3rdparty/angle/src/compiler/preprocessor/numeric_lex.h @@ -48,6 +48,15 @@ bool numeric_lex_int(const std::string &str, IntType *value) template 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. @@ -55,6 +64,7 @@ bool numeric_lex_float(const std::string &str, FloatType *value) stream >> (*value); return !stream.fail(); +#endif } } // namespace pp. diff --git a/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.cpp new file mode 100644 index 0000000000..31bfae9966 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.cpp @@ -0,0 +1,451 @@ +// +// 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 +{ + +// 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(TIntermAggregate *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 visitSelection(Visit visit, TIntermSelection *selection) override + { + visitControlFlow(visit, selection); + 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); + ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex); + UNUSED_ASSERTION_VARIABLE(mIndex); + + if ((*mMetadataList)[calleeIndex].mUsesGradient) { + onGradient(); + } + } + else + { + TString name = TFunction::unmangleName(node->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 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(TIntermAggregate *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 visitSelection(Visit visit, TIntermSelection *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); + ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex); + UNUSED_ASSERTION_VARIABLE(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 mLoopsAndSwitches; + std::vector 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(TIntermAggregate *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); + ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex); + UNUSED_ASSERTION_VARIABLE(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(TIntermSelection *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; +} diff --git a/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.h b/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.h new file mode 100644 index 0000000000..39e671e3e0 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.h @@ -0,0 +1,58 @@ +// +// 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 +#include + +class CallDAG; +class TIntermNode; +class TIntermSelection; +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(TIntermSelection *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 mControlFlowsContainingGradient; + + // Remember information about the discontinuous loops and which functions + // are called in such loops. + bool mCalledInDiscontinuousLoop; + bool mHasGradientLoopInCallGraph; + std::set mDiscontinuousLoops; + std::set mIfsContainingGradientLoop; + + // Will we need to generate a Lod0 version of the function. + bool mNeedsLod0; +}; + +typedef std::vector MetadataList; + +// Return the AST analysis result, in the order defined by the call DAG +MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag); + +#endif // COMPILER_TRANSLATOR_ASTMETADATAHLSL_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp b/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp new file mode 100644 index 0000000000..510ade84c1 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp @@ -0,0 +1,206 @@ +// +// 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 +{ + +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->setNameObj(originalCall->getNameObj()); + replacementCall->setFunctionId(originalCall->getFunctionId()); + 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 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::visitAggregate(Visit visit, TIntermAggregate *node) +{ + if (visit == PreVisit) + { + if (node->isArray()) + { + if (node->getOp() == EOpFunction) + { + // 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->getSequence()->front()->getAsAggregate(); + 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()); + + mReplacements.push_back(NodeUpdateEntry(node, params, replacementParams, false)); + + node->setType(TType(EbtVoid)); + + mInFunctionWithArrayReturnValue = true; + } + else 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->setNameObj(node->getNameObj()); + replacement->setFunctionId(node->getFunctionId()); + replacement->setLine(node->getLine()); + replacement->setType(TType(EbtVoid)); + + mReplacements.push_back(NodeUpdateEntry(getParentNode(), node, replacement, false)); + } + 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. + TIntermAggregate *parentAgg = getParentNode()->getAsAggregate(); + if (parentAgg != nullptr && parentAgg->getOp() == EOpSequence) + { + nextTemporaryIndex(); + TIntermSequence replacements; + replacements.push_back(createTempDeclaration(node->getType())); + TIntermSymbol *returnSymbol = createTempSymbol(node->getType()); + replacements.push_back(CreateReplacementCall(node, returnSymbol)); + mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(parentAgg, node, replacements)); + } + return false; + } + } + } + else if (visit == PostVisit) + { + if (node->getOp() == EOpFunction) + { + mInFunctionWithArrayReturnValue = 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; + + TIntermBinary *replacementAssignment = new TIntermBinary(EOpAssign); + TIntermTyped *expression = node->getExpression(); + ASSERT(expression != nullptr); + replacementAssignment->setLeft(CreateReturnValueSymbol(expression->getType())); + replacementAssignment->setRight(node->getExpression()); + replacementAssignment->setType(expression->getType()); + 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()->getAsAggregate(), 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()); + mReplacements.push_back(NodeUpdateEntry(getParentNode(), node, replacementCall, false)); + } + } + return false; +} + +} // namespace + +void ArrayReturnValueToOutParameter(TIntermNode *root, unsigned int *temporaryIndex) +{ + ArrayReturnValueToOutParameterTraverser::apply(root, temporaryIndex); +} diff --git a/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h b/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h new file mode 100644 index 0000000000..983e203e62 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h @@ -0,0 +1,16 @@ +// +// 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_ + +class TIntermNode; + +void ArrayReturnValueToOutParameter(TIntermNode *root, unsigned int *temporaryIndex); + +#endif // COMPILER_TRANSLATOR_ARRAYRETURNVALUETOOUTPARAMETER_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/BaseTypes.h b/src/3rdparty/angle/src/compiler/translator/BaseTypes.h index ee1428b2d3..0ed6d0e62f 100644 --- a/src/3rdparty/angle/src/compiler/translator/BaseTypes.h +++ b/src/3rdparty/angle/src/compiler/translator/BaseTypes.h @@ -7,7 +7,7 @@ #ifndef COMPILER_TRANSLATOR_BASETYPES_H_ #define COMPILER_TRANSLATOR_BASETYPES_H_ -#include "compiler/translator/compilerdebug.h" +#include "common/debug.h" // // Precision qualifiers @@ -18,7 +18,10 @@ enum TPrecision EbpUndefined, EbpLow, EbpMedium, - EbpHigh + EbpHigh, + + // end of list + EbpLast }; inline const char* getPrecisionString(TPrecision p) @@ -77,6 +80,9 @@ enum TBasicType EbtStruct, EbtInterfaceBlock, EbtAddress, // should be deprecated?? + + // end of list + EbtLast }; const char* getBasicString(TBasicType t); @@ -284,21 +290,18 @@ inline bool SupportsPrecision(TBasicType type) // enum TQualifier { - EvqTemporary, // For temporaries (within a function), read/write - EvqGlobal, // For globals read/write - EvqInternal, // For internal use, not visible to the user - EvqConst, // User defined constants and non-output parameters in functions - EvqAttribute, // Readonly - EvqVaryingIn, // readonly, fragment shaders only - EvqVaryingOut, // vertex shaders only read/write - EvqInvariantVaryingIn, // readonly, fragment shaders only - EvqInvariantVaryingOut, // 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 + 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, @@ -321,21 +324,26 @@ enum TQualifier // built-ins written by fragment shader EvqFragColor, EvqFragData, - EvqFragDepth, + + 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 + EvqSmooth, // Incomplete qualifier, smooth is the default + EvqFlat, // Incomplete qualifier EvqSmoothOut = EvqSmooth, - EvqFlatOut = EvqFlat, - EvqCentroidOut, // Implies smooth + EvqFlatOut = EvqFlat, + EvqCentroidOut, // Implies smooth EvqSmoothIn, EvqFlatIn, - EvqCentroidIn, // Implies smooth + EvqCentroidIn, // Implies smooth // end of list EvqLast @@ -384,43 +392,47 @@ struct TLayoutQualifier // inline const char* getQualifierString(TQualifier q) { + // clang-format off switch(q) { - case EvqTemporary: return "Temporary"; break; - case EvqGlobal: return "Global"; break; - case EvqConst: return "const"; break; - case EvqConstReadOnly: return "const"; break; - case EvqAttribute: return "attribute"; break; - case EvqVaryingIn: return "varying"; break; - case EvqVaryingOut: return "varying"; break; - case EvqInvariantVaryingIn: return "invariant varying"; break; - case EvqInvariantVaryingOut:return "invariant varying"; break; - case EvqUniform: return "uniform"; break; - case EvqVertexIn: return "in"; break; - case EvqFragmentOut: return "out"; break; - case EvqVertexOut: return "out"; break; - case EvqFragmentIn: return "in"; break; - case EvqIn: return "in"; break; - case EvqOut: return "out"; break; - case EvqInOut: return "inout"; break; - case EvqInstanceID: return "InstanceID"; break; - case EvqPosition: return "Position"; break; - case EvqPointSize: return "PointSize"; break; - case EvqFragCoord: return "FragCoord"; break; - case EvqFrontFacing: return "FrontFacing"; break; - case EvqFragColor: return "FragColor"; break; - case EvqFragData: return "FragData"; break; - case EvqFragDepth: return "FragDepth"; break; - case EvqSmoothOut: return "smooth out"; break; - case EvqCentroidOut: return "centroid out"; break; - case EvqFlatOut: return "flat out"; break; - case EvqSmoothIn: return "smooth in"; break; - case EvqCentroidIn: return "centroid in"; break; - case EvqFlatIn: return "flat in"; break; - case EvqLastFragColor: return "LastFragColor"; break; - case EvqLastFragData: return "LastFragData"; break; - default: UNREACHABLE(); return "unknown qualifier"; + 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 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 "centroid out"; + case EvqFlatOut: return "flat out"; + case EvqSmoothIn: return "smooth in"; + case EvqFlatIn: return "flat in"; + case EvqCentroidIn: return "centroid in"; + default: UNREACHABLE(); return "unknown qualifier"; } + // clang-format on } inline const char* getMatrixPackingString(TLayoutMatrixPacking mpq) diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp index 51461207c5..0c7f149ee6 100644 --- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp +++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp @@ -11,28 +11,31 @@ class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTraverser { public: - BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator& emulator) - : mEmulator(emulator) + BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator &emulator) + : TIntermTraverser(true, false, false), + mEmulator(emulator) { } - virtual bool visitUnary(Visit visit, TIntermUnary* node) + bool visitUnary(Visit visit, TIntermUnary *node) override { - if (visit == PreVisit) { - bool needToEmulate = mEmulator.SetFunctionCalled( - node->getOp(), node->getOperand()->getType()); + if (visit == PreVisit) + { + bool needToEmulate = mEmulator.SetFunctionCalled(node->getOp(), node->getOperand()->getType()); if (needToEmulate) node->setUseEmulatedFunction(); } return true; } - virtual bool visitAggregate(Visit visit, TIntermAggregate* node) + bool visitAggregate(Visit visit, TIntermAggregate *node) override { - if (visit == PreVisit) { + if (visit == PreVisit) + { // Here we handle all the built-in functions instead of the ones we // currently identified as problematic. - switch (node->getOp()) { + switch (node->getOp()) + { case EOpLessThan: case EOpGreaterThan: case EOpLessThanEqual: @@ -59,14 +62,14 @@ class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTr break; default: return true; - }; - const TIntermSequence& sequence = *(node->getSequence()); + } + 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(); + TIntermTyped *param1 = sequence[0]->getAsTyped(); + TIntermTyped *param2 = sequence[1]->getAsTyped(); if (!param1 || !param2) return true; needToEmulate = mEmulator.SetFunctionCalled( @@ -74,9 +77,9 @@ class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTr } else if (sequence.size() == 3) { - TIntermTyped* param1 = sequence[0]->getAsTyped(); - TIntermTyped* param2 = sequence[1]->getAsTyped(); - TIntermTyped* param3 = sequence[2]->getAsTyped(); + TIntermTyped *param1 = sequence[0]->getAsTyped(); + TIntermTyped *param2 = sequence[1]->getAsTyped(); + TIntermTyped *param3 = sequence[2]->getAsTyped(); if (!param1 || !param2 || !param3) return true; needToEmulate = mEmulator.SetFunctionCalled( @@ -94,34 +97,28 @@ class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTr } private: - BuiltInFunctionEmulator& mEmulator; + BuiltInFunctionEmulator &mEmulator; }; BuiltInFunctionEmulator::BuiltInFunctionEmulator() {} -void BuiltInFunctionEmulator::addEmulatedFunction( - TOperator op, const TType& param, - const char* emulatedFunctionDefinition) +void BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, const TType *param, + const char *emulatedFunctionDefinition) { - mEmulatedFunctions[FunctionId(op, param)] = - std::string(emulatedFunctionDefinition); + mEmulatedFunctions[FunctionId(op, param)] = std::string(emulatedFunctionDefinition); } -void BuiltInFunctionEmulator::addEmulatedFunction( - TOperator op, const TType& param1, const TType& param2, - const char* emulatedFunctionDefinition) +void BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, const TType *param1, const TType *param2, + const char *emulatedFunctionDefinition) { - mEmulatedFunctions[FunctionId(op, param1, param2)] = - std::string(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) +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); + mEmulatedFunctions[FunctionId(op, param1, param2, param3)] = std::string(emulatedFunctionDefinition); } bool BuiltInFunctionEmulator::IsOutputEmpty() const @@ -129,48 +126,48 @@ bool BuiltInFunctionEmulator::IsOutputEmpty() const return (mFunctions.size() == 0); } -void BuiltInFunctionEmulator::OutputEmulatedFunctions( - TInfoSinkBase& out) const +void BuiltInFunctionEmulator::OutputEmulatedFunctions(TInfoSinkBase &out) const { - for (size_t i = 0; i < mFunctions.size(); ++i) { + for (size_t i = 0; i < mFunctions.size(); ++i) + { out << mEmulatedFunctions.find(mFunctions[i])->second << "\n\n"; } } -bool BuiltInFunctionEmulator::SetFunctionCalled( - TOperator op, const TType& param) +bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, const TType ¶m) { - return SetFunctionCalled(FunctionId(op, param)); + return SetFunctionCalled(FunctionId(op, ¶m)); } -bool BuiltInFunctionEmulator::SetFunctionCalled( - TOperator op, const TType& param1, const TType& param2) +bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, const TType ¶m1, const TType ¶m2) { - return SetFunctionCalled(FunctionId(op, param1, param2)); + return SetFunctionCalled(FunctionId(op, ¶m1, ¶m2)); } -bool BuiltInFunctionEmulator::SetFunctionCalled( - TOperator op, const TType& param1, const TType& param2, const TType& param3) +bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, + const TType ¶m1, const TType ¶m2, const TType ¶m3) { - return SetFunctionCalled(FunctionId(op, param1, param2, param3)); + return SetFunctionCalled(FunctionId(op, ¶m1, ¶m2, ¶m3)); } -bool BuiltInFunctionEmulator::SetFunctionCalled( - const FunctionId& functionId) { +bool BuiltInFunctionEmulator::SetFunctionCalled(const FunctionId &functionId) +{ if (mEmulatedFunctions.find(functionId) != mEmulatedFunctions.end()) { - for (size_t i = 0; i < mFunctions.size(); ++i) { + for (size_t i = 0; i < mFunctions.size(); ++i) + { if (mFunctions[i] == functionId) return true; } - mFunctions.push_back(functionId); + // 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) +void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation(TIntermNode *root) { ASSERT(root); @@ -188,32 +185,30 @@ void BuiltInFunctionEmulator::Cleanup() //static TString BuiltInFunctionEmulator::GetEmulatedFunctionName( - const TString& name) + 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) +BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op, const TType *param) : mOp(op), mParam1(param), - mParam2(EbtVoid), - mParam3(EbtVoid) + mParam2(new TType(EbtVoid)), + mParam3(new TType(EbtVoid)) { } -BuiltInFunctionEmulator::FunctionId::FunctionId - (TOperator op, const TType& param1, const TType& param2) +BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op, const TType *param1, const TType *param2) : mOp(op), mParam1(param1), mParam2(param2), - mParam3(EbtVoid) + mParam3(new TType(EbtVoid)) { } -BuiltInFunctionEmulator::FunctionId::FunctionId - (TOperator op, const TType& param1, const TType& param2, const TType& param3) +BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op, + const TType *param1, const TType *param2, const TType *param3) : mOp(op), mParam1(param1), mParam2(param2), @@ -221,25 +216,28 @@ BuiltInFunctionEmulator::FunctionId::FunctionId { } -bool BuiltInFunctionEmulator::FunctionId::operator== - (const BuiltInFunctionEmulator::FunctionId& other) const +bool BuiltInFunctionEmulator::FunctionId::operator==(const BuiltInFunctionEmulator::FunctionId &other) const { return (mOp == other.mOp && - mParam1 == other.mParam1 && - mParam2 == other.mParam2 && - mParam3 == other.mParam3); + *mParam1 == *other.mParam1 && + *mParam2 == *other.mParam2 && + *mParam3 == *other.mParam3); } -bool BuiltInFunctionEmulator::FunctionId::operator< - (const BuiltInFunctionEmulator::FunctionId& other) const +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; + 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)); +} diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.h b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.h index df556985e1..6976edfd57 100644 --- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.h +++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.h @@ -21,23 +21,25 @@ class BuiltInFunctionEmulator public: BuiltInFunctionEmulator(); - void MarkBuiltInFunctionsForEmulation(TIntermNode* root); + void MarkBuiltInFunctionsForEmulation(TIntermNode *root); void Cleanup(); // "name(" becomes "webgl_name_emu(". - static TString GetEmulatedFunctionName(const TString& name); + 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; + 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); + 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; @@ -46,28 +48,32 @@ class BuiltInFunctionEmulator // 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& param); - bool SetFunctionCalled( - TOperator op, const TType& param1, const TType& param2); - bool SetFunctionCalled( - TOperator op, const TType& param1, const TType& param2, const TType& param3); + 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); + 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; + bool operator==(const FunctionId &other) const; + bool operator<(const FunctionId &other) const; + + FunctionId getCopy() const; private: TOperator mOp; - TType mParam1; - TType mParam2; - TType mParam3; + + // 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); + bool SetFunctionCalled(const FunctionId &functionId); // Map from function id to emulated function definition std::map mEmulatedFunctions; diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp index 9de99831ad..098560d110 100644 --- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp @@ -7,9 +7,11 @@ #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" -void InitBuiltInFunctionEmulatorForGLSL(BuiltInFunctionEmulator *emu, sh::GLenum shaderType) +void InitBuiltInFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, sh::GLenum shaderType) { // we use macros here instead of function definitions to work around more GLSL // compiler bugs, in particular on NVIDIA hardware on Mac OSX. Macros are @@ -17,10 +19,10 @@ void InitBuiltInFunctionEmulatorForGLSL(BuiltInFunctionEmulator *emu, sh::GLenum // evaluated. This is unlikely to show up in real shaders, but is something to // consider. - TType float1(EbtFloat); - TType float2(EbtFloat, 2); - TType float3(EbtFloat, 3); - TType float4(EbtFloat, 4); + 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); if (shaderType == GL_FRAGMENT_SHADER) { @@ -35,3 +37,153 @@ void InitBuiltInFunctionEmulatorForGLSL(BuiltInFunctionEmulator *emu, sh::GLenum emu->addEmulatedFunction(EOpNormalize, float1, "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))"); emu->addEmulatedFunction(EOpReflect, float1, float1, "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))"); } + +// Emulate built-in functions missing from GLSL 1.30 and higher +void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, sh::GLenum shaderType, + int targetGLSLVersion) +{ + // 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" + " scale = 1.0 / (1 << -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 + } +} diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h index 5707a4b35a..56242598af 100644 --- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h +++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h @@ -14,6 +14,12 @@ class BuiltInFunctionEmulator; // // This is only a workaround for OpenGL driver bugs, and isn't needed in general. // -void InitBuiltInFunctionEmulatorForGLSL(BuiltInFunctionEmulator *emu, sh::GLenum shaderType); +void InitBuiltInFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, sh::GLenum shaderType); + +// +// This function is emulating built-in functions missing from GLSL 1.30 and higher. +// +void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, sh::GLenum shaderType, + int targetGLSLVersion); #endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp index 7123a0d5c0..50e15cbc28 100644 --- a/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp @@ -11,10 +11,10 @@ void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu) { - TType float1(EbtFloat); - TType float2(EbtFloat, 2); - TType float3(EbtFloat, 3); - TType float4(EbtFloat, 4); + 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" @@ -250,7 +250,7 @@ void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu) " return (y << 16) | x;\n" "}\n"); - TType uint1(EbtUInt); + TType *uint1 = new TType(EbtUInt); emu->addEmulatedFunction(EOpUnpackSnorm2x16, uint1, "float webgl_fromSnorm(in uint x) {\n" @@ -327,9 +327,9 @@ void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu) " return mul(float4x1(r), float1x3(c));\n" "}\n"); - TType mat2(EbtFloat, 2, 2); - TType mat3(EbtFloat, 3, 3); - TType mat4(EbtFloat, 4, 4); + 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 @@ -407,4 +407,35 @@ void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu) " 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"); + } diff --git a/src/3rdparty/angle/src/compiler/translator/Cache.cpp b/src/3rdparty/angle/src/compiler/translator/Cache.cpp new file mode 100644 index 0000000000..57a43700ca --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/Cache.cpp @@ -0,0 +1,100 @@ +// +// 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 + +#include "common/angleutils.h" +#include "common/debug.h" +#include "compiler/translator/Cache.h" + +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::max(); + UNUSED_ASSERTION_VARIABLE(MaxEnumValue); + + // 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(basicType); + components.precision = static_cast(precision); + components.qualifier = static_cast(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; +} diff --git a/src/3rdparty/angle/src/compiler/translator/Cache.h b/src/3rdparty/angle/src/compiler/translator/Cache.h new file mode 100644 index 0000000000..1d2abb77e1 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/Cache.h @@ -0,0 +1,90 @@ +// +// 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 +#include +#include + +#include "compiler/translator/Types.h" +#include "compiler/translator/PoolAlloc.h" + +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 TypeMap; + + TypeMap mTypes; + TPoolAllocator mAllocator; + + static TCache *sCache; +}; + +#endif // COMPILER_TRANSLATOR_CACHE_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/CallDAG.cpp b/src/3rdparty/angle/src/compiler/translator/CallDAG.cpp new file mode 100644 index 0000000000..10f0eb937c --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/CallDAG.cpp @@ -0,0 +1,293 @@ +// +// 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" + +// 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 *records, std::map *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(callee->index)); + } + + (*idToIndex)[data.node->getFunctionId()] = static_cast(data.index); + } + } + + private: + + struct CreatorFunctionData + { + CreatorFunctionData() + : node(nullptr), + index(0), + indexAssigned(false), + visiting(false) + { + } + + std::set callees; + TIntermAggregate *node; + TString name; + size_t index; + bool indexAssigned; + bool visiting; + }; + + // 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->getName()]; + record.name = node->getName(); + } + break; + case EOpFunction: + { + // Function definition, create the record if need be and remember the node. + if (visit == PreVisit) + { + auto it = mFunctions.find(node->getName()); + + if (it == mFunctions.end()) + { + mCurrentFunction = &mFunctions[node->getName()]; + } + else + { + mCurrentFunction = &it->second; + } + + mCurrentFunction->node = node; + mCurrentFunction->name = node->getName(); + + } + else if (visit == PostVisit) + { + mCurrentFunction = nullptr; + } + 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->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 *function) + { + ASSERT(function); + + if (!function->node) + { + *mCreationInfo << "Undefined function '" << function->name + << ")' used in the following call chain:"; + return INITDAG_UNDEFINED; + } + + if (function->indexAssigned) + { + return INITDAG_SUCCESS; + } + + if (function->visiting) + { + if (mCreationInfo) + { + *mCreationInfo << "Recursive function call in the following call chain:" << function->name; + } + return INITDAG_RECURSION; + } + function->visiting = true; + + for (auto &callee : function->callees) + { + InitResult result = assignIndicesInternal(callee); + if (result != INITDAG_SUCCESS) + { + // We know that there is an issue with the call chain in the AST, + // print the link of the chain we were processing. + if (mCreationInfo) + { + *mCreationInfo << " <- " << function->name << ")"; + } + return result; + } + } + + function->index = mCurrentIndex++; + function->indexAssigned = true; + + function->visiting = false; + return INITDAG_SUCCESS; + } + + TInfoSinkBase *mCreationInfo; + + std::map mFunctions; + CreatorFunctionData *mCurrentFunction; + size_t mCurrentIndex; +}; + +// CallDAG + +CallDAG::CallDAG() +{ +} + +CallDAG::~CallDAG() +{ +} + +const size_t CallDAG::InvalidIndex = std::numeric_limits::max(); + +size_t CallDAG::findIndex(const TIntermAggregate *function) const +{ + TOperator op = function->getOp(); + ASSERT(op == EOpPrototype || op == EOpFunction || op == EOpFunctionCall); + UNUSED_ASSERTION_VARIABLE(op); + + auto it = mFunctionIdToIndex.find(function->getFunctionId()); + + 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); + 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) +{ + 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; +} diff --git a/src/3rdparty/angle/src/compiler/translator/CallDAG.h b/src/3rdparty/angle/src/compiler/translator/CallDAG.h new file mode 100644 index 0000000000..06c377db00 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/CallDAG.h @@ -0,0 +1,75 @@ +// +// 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 + +#include "compiler/translator/IntermNode.h" +#include "compiler/translator/VariableInfo.h" + + +// 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; + TIntermAggregate *node; + std::vector 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 TIntermAggregate *function) 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 mRecords; + std::map mFunctionIdToIndex; + + class CallDAGCreator; +}; + +#endif // COMPILER_TRANSLATOR_CALLDAG_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/CodeGen.cpp b/src/3rdparty/angle/src/compiler/translator/CodeGen.cpp index 5e3eb1cc05..f099bccf15 100644 --- a/src/3rdparty/angle/src/compiler/translator/CodeGen.cpp +++ b/src/3rdparty/angle/src/compiler/translator/CodeGen.cpp @@ -4,8 +4,14 @@ // found in the LICENSE file. // +#ifdef ANGLE_ENABLE_ESSL #include "compiler/translator/TranslatorESSL.h" +#endif + +#ifdef ANGLE_ENABLE_GLSL #include "compiler/translator/TranslatorGLSL.h" +#endif + #ifdef ANGLE_ENABLE_HLSL #include "compiler/translator/TranslatorHLSL.h" #endif // ANGLE_ENABLE_HLSL @@ -20,22 +26,44 @@ TCompiler* ConstructCompiler( { switch (output) { case SH_ESSL_OUTPUT: +#ifdef ANGLE_ENABLE_ESSL return new TranslatorESSL(type, spec); - case SH_GLSL_CORE_OUTPUT: +#else + // This compiler is not supported in this + // configuration. Return NULL per the ShConstructCompiler 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); - case SH_HLSL9_OUTPUT: - case SH_HLSL11_OUTPUT: +#else + // This compiler is not supported in this + // configuration. Return NULL per the ShConstructCompiler 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 ShConstructCompiler API. - return NULL; + return nullptr; #endif // ANGLE_ENABLE_HLSL default: // Unknown format. Return NULL per the ShConstructCompiler API. - return NULL; + return nullptr; } } diff --git a/src/3rdparty/angle/src/compiler/translator/Common.h b/src/3rdparty/angle/src/compiler/translator/Common.h index ac1aef0f4c..60223232af 100644 --- a/src/3rdparty/angle/src/compiler/translator/Common.h +++ b/src/3rdparty/angle/src/compiler/translator/Common.h @@ -14,9 +14,9 @@ #include #include -#include "compiler/translator/PoolAlloc.h" -#include "compiler/translator/compilerdebug.h" #include "common/angleutils.h" +#include "common/debug.h" +#include "compiler/translator/PoolAlloc.h" struct TSourceLoc { int first_file; @@ -60,18 +60,21 @@ inline TString* NewPoolTString(const char* s) // // Pool allocator versions of vectors, lists, and maps // -template class TVector : public std::vector > { -public: - typedef typename std::vector >::size_type size_type; - TVector() : std::vector >() {} - TVector(const pool_allocator& a) : std::vector >(a) {} - TVector(size_type i): std::vector >(i) {} +template +class TVector : public std::vector> +{ + public: + typedef typename std::vector>::size_type size_type; + TVector() : std::vector>() {} + TVector(const pool_allocator &a) : std::vector>(a) {} + TVector(size_type i) : std::vector>(i) {} }; -template > -class TMap : public std::map > > { -public: - typedef pool_allocator > tAllocator; +template > +class TMap : public std::map>> +{ + public: + typedef pool_allocator> tAllocator; TMap() : std::map() {} // use correct two-stage name lookup supported in gcc 3.4 and above diff --git a/src/3rdparty/angle/src/compiler/translator/Compiler.cpp b/src/3rdparty/angle/src/compiler/translator/Compiler.cpp index 534861ca70..18524ce569 100644 --- a/src/3rdparty/angle/src/compiler/translator/Compiler.cpp +++ b/src/3rdparty/angle/src/compiler/translator/Compiler.cpp @@ -4,15 +4,19 @@ // found in the LICENSE file. // +#include "compiler/translator/Cache.h" #include "compiler/translator/Compiler.h" -#include "compiler/translator/DetectCallDepth.h" +#include "compiler/translator/CallDAG.h" #include "compiler/translator/ForLoopUnroll.h" #include "compiler/translator/Initialize.h" #include "compiler/translator/InitializeParseContext.h" #include "compiler/translator/InitializeVariables.h" #include "compiler/translator/ParseContext.h" +#include "compiler/translator/PruneEmptyDeclarations.h" #include "compiler/translator/RegenerateStructNames.h" +#include "compiler/translator/RemovePow.h" #include "compiler/translator/RenameFunction.h" +#include "compiler/translator/RewriteDoWhile.h" #include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h" #include "compiler/translator/UnfoldShortCircuitAST.h" #include "compiler/translator/ValidateLimitations.h" @@ -33,6 +37,20 @@ bool IsWebGLBasedSpec(ShShaderSpec spec) spec == SH_WEBGL2_SPEC); } +bool IsGLSL130OrNewer(ShShaderOutput output) +{ + return (output == SH_GLSL_130_OUTPUT || + output == SH_GLSL_140_OUTPUT || + output == SH_GLSL_150_CORE_OUTPUT || + output == SH_GLSL_330_CORE_OUTPUT || + output == SH_GLSL_400_CORE_OUTPUT || + output == SH_GLSL_410_CORE_OUTPUT || + output == SH_GLSL_420_CORE_OUTPUT || + output == SH_GLSL_430_CORE_OUTPUT || + output == SH_GLSL_440_CORE_OUTPUT || + output == SH_GLSL_450_CORE_OUTPUT); +} + size_t GetGlobalMaxTokenSize(ShShaderSpec spec) { // WebGL defines a max token legnth of 256, while ES2 leaves max token @@ -126,7 +144,8 @@ TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output) fragmentPrecisionHigh(false), clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC), builtInFunctionEmulator(), - mSourcePath(NULL) + mSourcePath(NULL), + mTemporaryIndex(0) { } @@ -134,6 +153,15 @@ TCompiler::~TCompiler() { } +bool TCompiler::shouldRunLoopAndIndexingValidation(int compileOptions) const +{ + // If compiling an ESSL 1.00 shader for WebGL, or if its been requested through the API, + // validate loop and indexing as well (to verify that the shader only uses minimal functionality + // of ESSL 1.00 as in Appendix A of the spec). + return (IsWebGLBasedSpec(shaderSpec) && shaderVersion == 100) || + (compileOptions & SH_VALIDATE_LOOP_INDEXING); +} + bool TCompiler::Init(const ShBuiltInResources& resources) { shaderVersion = 100; @@ -165,8 +193,9 @@ TIntermNode *TCompiler::compileTreeForTesting(const char* const shaderStrings[], return compileTreeImpl(shaderStrings, numStrings, compileOptions); } -TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], - size_t numStrings, int compileOptions) +TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[], + size_t numStrings, + const int compileOptions) { clearResults(); @@ -176,10 +205,6 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], // Reset the extension behavior for each compilation unit. ResetExtensionBehavior(extensionBehavior); - // If compiling for WebGL, validate loop and indexing as well. - if (IsWebGLBasedSpec(shaderSpec)) - compileOptions |= SH_VALIDATE_LOOP_INDEXING; - // First string is path of source file if flag is set. The actual source follows. size_t firstSource = 0; if (compileOptions & SH_SOURCE_PATH) @@ -188,13 +213,11 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], ++firstSource; } - bool debugShaderPrecision = getResources().WEBGL_debug_shader_precision == 1; TIntermediate intermediate(infoSink); - TParseContext parseContext(symbolTable, extensionBehavior, intermediate, - shaderType, shaderSpec, compileOptions, true, - infoSink, debugShaderPrecision); + TParseContext parseContext(symbolTable, extensionBehavior, intermediate, shaderType, shaderSpec, + compileOptions, true, infoSink, getResources()); - parseContext.fragmentPrecisionHigh = fragmentPrecisionHigh; + parseContext.setFragmentPrecisionHighOnESSL1(fragmentPrecisionHigh); SetGlobalParseContext(&parseContext); // We preserve symbols at the built-in level from compile-to-compile. @@ -203,8 +226,8 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], // Parse shader. bool success = - (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], NULL, &parseContext) == 0) && - (parseContext.treeRoot != NULL); + (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], nullptr, &parseContext) == 0) && + (parseContext.getTreeRoot() != nullptr); shaderVersion = parseContext.getShaderVersion(); if (success && MapSpecToShaderVersion(shaderSpec) < shaderVersion) @@ -214,7 +237,7 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], success = false; } - TIntermNode *root = NULL; + TIntermNode *root = nullptr; if (success) { @@ -224,20 +247,42 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], symbolTable.setGlobalInvariant(); } - root = parseContext.treeRoot; - success = intermediate.postProcess(root); + root = parseContext.getTreeRoot(); + root = intermediate.postProcess(root); + + // Highp might have been auto-enabled based on shader version + fragmentPrecisionHigh = parseContext.getFragmentPrecisionHigh(); // Disallow expressions deemed too complex. if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY)) success = limitExpressionComplexity(root); + // Create the function DAG and check there is no recursion + if (success) + success = initCallDag(root); + + if (success && (compileOptions & SH_LIMIT_CALL_STACK_DEPTH)) + success = checkCallDepth(); + + // Checks which functions are used and if "main" exists if (success) - success = detectCallDepth(root, infoSink, (compileOptions & SH_LIMIT_CALL_STACK_DEPTH) != 0); + { + functionMetadata.clear(); + functionMetadata.resize(mCallDag.size()); + success = tagUsedFunctions(); + } + + if (success && !(compileOptions & SH_DONT_PRUNE_UNUSED_FUNCTIONS)) + success = pruneUnusedFunctions(root); + + // Prune empty declarations to work around driver bugs and to keep declaration output simple. + if (success) + PruneEmptyDeclarations(root); if (success && shaderVersion == 300 && shaderType == GL_FRAGMENT_SHADER) success = validateOutputs(root); - if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING)) + if (success && shouldRunLoopAndIndexingValidation(compileOptions)) success = validateLimitations(root); if (success && (compileOptions & SH_TIMING_RESTRICTIONS)) @@ -249,12 +294,14 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], // Unroll for-loop markup needs to happen after validateLimitations pass. if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX)) { - ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex); + ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex, + shouldRunLoopAndIndexingValidation(compileOptions)); root->traverse(&marker); } if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX)) { - ForLoopUnrollMarker marker(ForLoopUnrollMarker::kSamplerArrayIndex); + ForLoopUnrollMarker marker(ForLoopUnrollMarker::kSamplerArrayIndex, + shouldRunLoopAndIndexingValidation(compileOptions)); root->traverse(&marker); if (marker.samplerArrayIndexIsFloatLoopIndex()) { @@ -275,9 +322,16 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS)) arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root); - if (success && shaderType == GL_VERTEX_SHADER && (compileOptions & SH_INIT_GL_POSITION)) + // gl_Position is always written in compatibility output mode + if (success && shaderType == GL_VERTEX_SHADER && + ((compileOptions & SH_INIT_GL_POSITION) || + (outputType == SH_GLSL_COMPATIBILITY_OUTPUT))) initializeGLPosition(root); + // This pass might emit short circuits so keep it before the short circuit unfolding + if (success && (compileOptions & SH_REWRITE_DO_WHILE_LOOPS)) + RewriteDoWhile(root, getTemporaryIndex()); + if (success && (compileOptions & SH_UNFOLD_SHORT_CIRCUIT)) { UnfoldShortCircuitAST unfoldShortCircuit; @@ -285,7 +339,12 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], unfoldShortCircuit.updateTree(); } - if (success && (compileOptions & SH_VARIABLES)) + if (success && (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT)) + { + RemovePow(root); + } + + if (success && shouldCollectVariables(compileOptions)) { collectVariables(root); if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS) @@ -369,11 +428,6 @@ bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources) floatingPoint.secondarySize = 1; floatingPoint.array = false; - TPublicType sampler; - sampler.primarySize = 1; - sampler.secondarySize = 1; - sampler.array = false; - switch(shaderType) { case GL_FRAGMENT_SHADER: @@ -386,14 +440,15 @@ bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources) default: assert(false && "Language not supported"); } - // We set defaults for all the sampler types, even those that are + // Set defaults for sampler types that have default precision, even those that are // only available if an extension exists. - for (int samplerType = EbtGuardSamplerBegin + 1; - samplerType < EbtGuardSamplerEnd; ++samplerType) - { - sampler.type = static_cast(samplerType); - symbolTable.setDefaultPrecision(sampler, EbpLow); - } + // New sampler types in ESSL3 don't have default precision. ESSL1 types do. + initSamplerDefaultPrecision(EbtSampler2D); + initSamplerDefaultPrecision(EbtSamplerCube); + // SamplerExternalOES is specified in the extension to have default precision. + initSamplerDefaultPrecision(EbtSamplerExternalOES); + // It isn't specified whether Sampler2DRect has default precision. + initSamplerDefaultPrecision(EbtSampler2DRect); InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable); @@ -402,6 +457,17 @@ bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources) return true; } +void TCompiler::initSamplerDefaultPrecision(TBasicType samplerType) +{ + ASSERT(samplerType > EbtGuardSamplerBegin && samplerType < EbtGuardSamplerEnd); + TPublicType sampler; + sampler.primarySize = 1; + sampler.secondarySize = 1; + sampler.array = false; + sampler.type = samplerType; + symbolTable.setDefaultPrecision(sampler, EbpLow); +} + void TCompiler::setResourceString() { std::ostringstream strstream; @@ -420,6 +486,7 @@ void TCompiler::setResourceString() << ":FragmentPrecisionHigh:" << compileResources.FragmentPrecisionHigh << ":MaxExpressionComplexity:" << compileResources.MaxExpressionComplexity << ":MaxCallStackDepth:" << compileResources.MaxCallStackDepth + << ":EXT_blend_func_extended:" << compileResources.EXT_blend_func_extended << ":EXT_frag_depth:" << compileResources.EXT_frag_depth << ":EXT_shader_texture_lod:" << compileResources.EXT_shader_texture_lod << ":EXT_shader_framebuffer_fetch:" << compileResources.EXT_shader_framebuffer_fetch @@ -429,6 +496,7 @@ void TCompiler::setResourceString() << ":MaxFragmentInputVectors:" << compileResources.MaxFragmentInputVectors << ":MinProgramTexelOffset:" << compileResources.MinProgramTexelOffset << ":MaxProgramTexelOffset:" << compileResources.MaxProgramTexelOffset + << ":MaxDualSourceDrawBuffers:" << compileResources.MaxDualSourceDrawBuffers << ":NV_draw_buffers:" << compileResources.NV_draw_buffers << ":WEBGL_debug_shader_precision:" << compileResources.WEBGL_debug_shader_precision; @@ -454,39 +522,175 @@ void TCompiler::clearResults() nameMap.clear(); mSourcePath = NULL; + mTemporaryIndex = 0; } -bool TCompiler::detectCallDepth(TIntermNode* inputRoot, TInfoSink& inputInfoSink, bool limitCallStackDepth) +bool TCompiler::initCallDag(TIntermNode *root) { - DetectCallDepth detect(inputInfoSink, limitCallStackDepth, maxCallStackDepth); - inputRoot->traverse(&detect); - switch (detect.detectCallDepth()) + mCallDag.clear(); + + switch (mCallDag.init(root, &infoSink.info)) { - case DetectCallDepth::kErrorNone: + case CallDAG::INITDAG_SUCCESS: return true; - case DetectCallDepth::kErrorMissingMain: - inputInfoSink.info.prefix(EPrefixError); - inputInfoSink.info << "Missing main()"; - return false; - case DetectCallDepth::kErrorRecursion: - inputInfoSink.info.prefix(EPrefixError); - inputInfoSink.info << "Function recursion detected"; - return false; - case DetectCallDepth::kErrorMaxDepthExceeded: - inputInfoSink.info.prefix(EPrefixError); - inputInfoSink.info << "Function call stack too deep"; + case CallDAG::INITDAG_RECURSION: + infoSink.info.prefix(EPrefixError); + infoSink.info << "Function recursion detected"; return false; - default: - UNREACHABLE(); + case CallDAG::INITDAG_UNDEFINED: + infoSink.info.prefix(EPrefixError); + infoSink.info << "Unimplemented function detected"; return false; } + + UNREACHABLE(); + return true; +} + +bool TCompiler::checkCallDepth() +{ + std::vector depths(mCallDag.size()); + + for (size_t i = 0; i < mCallDag.size(); i++) + { + int depth = 0; + auto &record = mCallDag.getRecordFromIndex(i); + + for (auto &calleeIndex : record.callees) + { + depth = std::max(depth, depths[calleeIndex] + 1); + } + + depths[i] = depth; + + if (depth >= maxCallStackDepth) + { + // Trace back the function chain to have a meaningful info log. + infoSink.info.prefix(EPrefixError); + infoSink.info << "Call stack too deep (larger than " << maxCallStackDepth + << ") with the following call chain: " << record.name; + + int currentFunction = static_cast(i); + int currentDepth = depth; + + while (currentFunction != -1) + { + infoSink.info << " -> " << mCallDag.getRecordFromIndex(currentFunction).name; + + int nextFunction = -1; + for (auto& calleeIndex : mCallDag.getRecordFromIndex(currentFunction).callees) + { + if (depths[calleeIndex] == currentDepth - 1) + { + currentDepth--; + nextFunction = calleeIndex; + } + } + + currentFunction = nextFunction; + } + + return false; + } + } + + return true; +} + +bool TCompiler::tagUsedFunctions() +{ + // Search from main, starting from the end of the DAG as it usually is the root. + for (size_t i = mCallDag.size(); i-- > 0;) + { + if (mCallDag.getRecordFromIndex(i).name == "main(") + { + internalTagUsedFunction(i); + return true; + } + } + + infoSink.info.prefix(EPrefixError); + infoSink.info << "Missing main()\n"; + return false; +} + +void TCompiler::internalTagUsedFunction(size_t index) +{ + if (functionMetadata[index].used) + { + return; + } + + functionMetadata[index].used = true; + + for (int calleeIndex : mCallDag.getRecordFromIndex(index).callees) + { + internalTagUsedFunction(calleeIndex); + } +} + +// A predicate for the stl that returns if a top-level node is unused +class TCompiler::UnusedPredicate +{ + public: + UnusedPredicate(const CallDAG *callDag, const std::vector *metadatas) + : mCallDag(callDag), + mMetadatas(metadatas) + { + } + + bool operator ()(TIntermNode *node) + { + const TIntermAggregate *asAggregate = node->getAsAggregate(); + + if (asAggregate == nullptr) + { + return false; + } + + if (!(asAggregate->getOp() == EOpFunction || asAggregate->getOp() == EOpPrototype)) + { + return false; + } + + size_t callDagIndex = mCallDag->findIndex(asAggregate); + if (callDagIndex == CallDAG::InvalidIndex) + { + // This happens only for unimplemented prototypes which are thus unused + ASSERT(asAggregate->getOp() == EOpPrototype); + return true; + } + + ASSERT(callDagIndex < mMetadatas->size()); + return !(*mMetadatas)[callDagIndex].used; + } + + private: + const CallDAG *mCallDag; + const std::vector *mMetadatas; +}; + +bool TCompiler::pruneUnusedFunctions(TIntermNode *root) +{ + TIntermAggregate *rootNode = root->getAsAggregate(); + ASSERT(rootNode != nullptr); + + UnusedPredicate isUnused(&mCallDag, &functionMetadata); + TIntermSequence *sequence = rootNode->getSequence(); + + if (!sequence->empty()) + { + sequence->erase(std::remove_if(sequence->begin(), sequence->end(), isUnused), sequence->end()); + } + + return true; } bool TCompiler::validateOutputs(TIntermNode* root) { - ValidateOutputs validateOutputs(infoSink.info, compileResources.MaxDrawBuffers); + ValidateOutputs validateOutputs(getExtensionBehavior(), compileResources.MaxDrawBuffers); root->traverse(&validateOutputs); - return (validateOutputs.numErrors() == 0); + return (validateOutputs.validateAndCountErrors(infoSink.info) == 0); } void TCompiler::rewriteCSSShader(TIntermNode* root) @@ -497,7 +701,7 @@ void TCompiler::rewriteCSSShader(TIntermNode* root) bool TCompiler::validateLimitations(TIntermNode* root) { - ValidateLimitations validate(shaderType, infoSink.info); + ValidateLimitations validate(shaderType, &infoSink.info); root->traverse(&validate); return validate.numErrors() == 0; } @@ -543,17 +747,6 @@ bool TCompiler::limitExpressionComplexity(TIntermNode* root) return false; } - TDependencyGraph graph(root); - - for (TFunctionCallVector::const_iterator iter = graph.beginUserDefinedFunctionCalls(); - iter != graph.endUserDefinedFunctionCalls(); - ++iter) - { - TGraphFunctionCall* samplerSymbol = *iter; - TDependencyGraphTraverser graphTraverser; - samplerSymbol->traverse(&graphTraverser); - } - return true; } diff --git a/src/3rdparty/angle/src/compiler/translator/Compiler.h b/src/3rdparty/angle/src/compiler/translator/Compiler.h index bcdb0d4c9d..c00a8f97aa 100644 --- a/src/3rdparty/angle/src/compiler/translator/Compiler.h +++ b/src/3rdparty/angle/src/compiler/translator/Compiler.h @@ -15,6 +15,7 @@ // #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" @@ -35,6 +36,11 @@ class TranslatorHLSL; // bool IsWebGLBasedSpec(ShShaderSpec spec); +// +// Helper function to check if the shader type is GLSL. +// +bool IsGLSL130OrNewer(ShShaderOutput output); + // // The base class used to back handles returned to the driver. // @@ -61,8 +67,8 @@ class TCompiler : public TShHandleBase { public: TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output); - virtual ~TCompiler(); - virtual TCompiler* getAsCompiler() { return this; } + ~TCompiler() override; + TCompiler *getAsCompiler() override { return this; } bool Init(const ShBuiltInResources& resources); @@ -79,8 +85,11 @@ class TCompiler : public TShHandleBase int getShaderVersion() const { return shaderVersion; } TInfoSink& getInfoSink() { return infoSink; } + // Clears the results from the previous compilation. + void clearResults(); + const std::vector &getAttributes() const { return attributes; } - const std::vector &getOutputVariables() const { return outputVariables; } + const std::vector &getOutputVariables() const { return outputVariables; } const std::vector &getUniforms() const { return uniforms; } const std::vector &getVaryings() const { return varyings; } const std::vector &getInterfaceBlocks() const { return interfaceBlocks; } @@ -92,6 +101,8 @@ class TCompiler : public TShHandleBase ShShaderOutput getOutputType() const { return outputType; } const std::string &getBuiltInResourcesString() const { return builtInResourcesString; } + bool shouldRunLoopAndIndexingValidation(int compileOptions) const; + // Get the resources set by InitBuiltInSymbolTable const ShBuiltInResources& getResources() const; @@ -101,10 +112,8 @@ class TCompiler : public TShHandleBase bool InitBuiltInSymbolTable(const ShBuiltInResources& resources); // Compute the string representation of the built-in resources void setResourceString(); - // Clears the results from the previous compilation. - void clearResults(); - // Return true if function recursion is detected or call depth exceeded. - bool detectCallDepth(TIntermNode* root, TInfoSink& infoSink, bool limitCallStackDepth); + // 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); // Rewrites a shader's intermediate tree according to the CSS Shaders spec. @@ -145,26 +154,57 @@ class TCompiler : public TShHandleBase const char *getSourcePath() const; const TPragma& getPragma() const { return mPragma; } void writePragma(); + unsigned int *getTemporaryIndex() { return &mTemporaryIndex; } const ArrayBoundsClamper& getArrayBoundsClamper() const; ShArrayIndexClampingStrategy getArrayIndexClampingStrategy() const; const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const; std::vector attributes; - std::vector outputVariables; + std::vector outputVariables; std::vector uniforms; std::vector expandedUniforms; std::vector varyings; std::vector interfaceBlocks; + virtual bool shouldCollectVariables(int compileOptions) + { + return (compileOptions & SH_VARIABLES) != 0; + } + private: - TIntermNode *compileTreeImpl(const char* const shaderStrings[], - size_t numStrings, int compileOptions); + // 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); + + // Removes unused function declarations and prototypes from the AST + class UnusedPredicate; + bool pruneUnusedFunctions(TIntermNode *root); + + TIntermNode *compileTreeImpl(const char *const shaderStrings[], + size_t numStrings, + const int compileOptions); sh::GLenum shaderType; ShShaderSpec shaderSpec; ShShaderOutput outputType; + struct FunctionMetadata + { + FunctionMetadata() + : used(false) + { + } + bool used; + }; + + CallDAG mCallDag; + std::vector functionMetadata; + int maxUniformVectors; int maxExpressionComplexity; int maxCallStackDepth; @@ -193,6 +233,8 @@ class TCompiler : public TShHandleBase NameMap nameMap; TPragma mPragma; + + unsigned int mTemporaryIndex; }; // diff --git a/src/3rdparty/angle/src/compiler/translator/ConstantUnion.h b/src/3rdparty/angle/src/compiler/translator/ConstantUnion.h index 31ff2ccfa7..a86d27f3ff 100644 --- a/src/3rdparty/angle/src/compiler/translator/ConstantUnion.h +++ b/src/3rdparty/angle/src/compiler/translator/ConstantUnion.h @@ -9,16 +9,18 @@ #include -class ConstantUnion { +#include "compiler/translator/BaseTypes.h" + +class TConstantUnion { public: POOL_ALLOCATOR_NEW_DELETE(); - ConstantUnion() + TConstantUnion() { iConst = 0; type = EbtVoid; } - bool cast(TBasicType newType, const ConstantUnion &constant) + bool cast(TBasicType newType, const TConstantUnion &constant) { switch (newType) { @@ -109,7 +111,7 @@ public: return b == bConst; } - bool operator==(const ConstantUnion& constant) const + bool operator==(const TConstantUnion& constant) const { if (constant.type != type) return false; @@ -148,12 +150,12 @@ public: return !operator==(b); } - bool operator!=(const ConstantUnion& constant) const + bool operator!=(const TConstantUnion& constant) const { return !operator==(constant); } - bool operator>(const ConstantUnion& constant) const + bool operator>(const TConstantUnion& constant) const { assert(type == constant.type); switch (type) { @@ -168,7 +170,7 @@ public: } } - bool operator<(const ConstantUnion& constant) const + bool operator<(const TConstantUnion& constant) const { assert(type == constant.type); switch (type) { @@ -183,9 +185,9 @@ public: } } - ConstantUnion operator+(const ConstantUnion& constant) const + TConstantUnion operator+(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst + constant.iConst); break; @@ -197,9 +199,9 @@ public: return returnValue; } - ConstantUnion operator-(const ConstantUnion& constant) const + TConstantUnion operator-(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst - constant.iConst); break; @@ -211,9 +213,9 @@ public: return returnValue; } - ConstantUnion operator*(const ConstantUnion& constant) const + TConstantUnion operator*(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst * constant.iConst); break; @@ -225,9 +227,9 @@ public: return returnValue; } - ConstantUnion operator%(const ConstantUnion& constant) const + TConstantUnion operator%(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst % constant.iConst); break; @@ -238,9 +240,9 @@ public: return returnValue; } - ConstantUnion operator>>(const ConstantUnion& constant) const + TConstantUnion operator>>(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst >> constant.iConst); break; @@ -251,9 +253,9 @@ public: return returnValue; } - ConstantUnion operator<<(const ConstantUnion& constant) const + TConstantUnion operator<<(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; // The signedness of the second parameter might be different, but we // don't care, since the result is undefined if the second parameter is // negative, and aliasing should not be a problem with unions. @@ -267,9 +269,9 @@ public: return returnValue; } - ConstantUnion operator&(const ConstantUnion& constant) const + TConstantUnion operator&(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(constant.type == EbtInt || constant.type == EbtUInt); switch (type) { case EbtInt: returnValue.setIConst(iConst & constant.iConst); break; @@ -280,9 +282,9 @@ public: return returnValue; } - ConstantUnion operator|(const ConstantUnion& constant) const + TConstantUnion operator|(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst | constant.iConst); break; @@ -293,9 +295,9 @@ public: return returnValue; } - ConstantUnion operator^(const ConstantUnion& constant) const + TConstantUnion operator^(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst ^ constant.iConst); break; @@ -306,9 +308,9 @@ public: return returnValue; } - ConstantUnion operator&&(const ConstantUnion& constant) const + TConstantUnion operator&&(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtBool: returnValue.setBConst(bConst && constant.bConst); break; @@ -318,9 +320,9 @@ public: return returnValue; } - ConstantUnion operator||(const ConstantUnion& constant) const + TConstantUnion operator||(const TConstantUnion& constant) const { - ConstantUnion returnValue; + TConstantUnion returnValue; assert(type == constant.type); switch (type) { case EbtBool: returnValue.setBConst(bConst || constant.bConst); break; diff --git a/src/3rdparty/angle/src/compiler/translator/Diagnostics.cpp b/src/3rdparty/angle/src/compiler/translator/Diagnostics.cpp index 92db3e55cf..593137fb0a 100644 --- a/src/3rdparty/angle/src/compiler/translator/Diagnostics.cpp +++ b/src/3rdparty/angle/src/compiler/translator/Diagnostics.cpp @@ -6,7 +6,7 @@ #include "compiler/translator/Diagnostics.h" -#include "compiler/translator/compilerdebug.h" +#include "common/debug.h" #include "compiler/translator/InfoSink.h" #include "compiler/preprocessor/SourceLocation.h" @@ -50,11 +50,6 @@ void TDiagnostics::writeInfo(Severity severity, sink << "'" << token << "' : " << reason << " " << extra << "\n"; } -void TDiagnostics::writeDebug(const std::string& str) -{ - mInfoSink.debug << str; -} - void TDiagnostics::print(ID id, const pp::SourceLocation& loc, const std::string& text) diff --git a/src/3rdparty/angle/src/compiler/translator/Diagnostics.h b/src/3rdparty/angle/src/compiler/translator/Diagnostics.h index 078bc97772..bc26e4584f 100644 --- a/src/3rdparty/angle/src/compiler/translator/Diagnostics.h +++ b/src/3rdparty/angle/src/compiler/translator/Diagnostics.h @@ -16,7 +16,7 @@ class TDiagnostics : public pp::Diagnostics, angle::NonCopyable { public: TDiagnostics(TInfoSink& infoSink); - virtual ~TDiagnostics(); + ~TDiagnostics() override; TInfoSink& infoSink() { return mInfoSink; } @@ -29,12 +29,8 @@ class TDiagnostics : public pp::Diagnostics, angle::NonCopyable const std::string& token, const std::string& extra); - void writeDebug(const std::string& str); - protected: - virtual void print(ID id, - const pp::SourceLocation& loc, - const std::string& text); + void print(ID id, const pp::SourceLocation &loc, const std::string &text) override; private: TInfoSink& mInfoSink; diff --git a/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.cpp b/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.cpp index 936c00a56c..ff8a69efa5 100644 --- a/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.cpp +++ b/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.cpp @@ -8,7 +8,8 @@ #include -#include "compiler/translator/compilerdebug.h" +#include "angle_gl.h" +#include "common/debug.h" #include "compiler/translator/Diagnostics.h" static TBehavior getBehavior(const std::string& str) @@ -25,13 +26,15 @@ static TBehavior getBehavior(const std::string& str) return EBhUndefined; } -TDirectiveHandler::TDirectiveHandler(TExtensionBehavior& extBehavior, - TDiagnostics& diagnostics, - int& shaderVersion, +TDirectiveHandler::TDirectiveHandler(TExtensionBehavior &extBehavior, + TDiagnostics &diagnostics, + int &shaderVersion, + sh::GLenum shaderType, bool debugShaderPrecisionSupported) : mExtensionBehavior(extBehavior), mDiagnostics(diagnostics), mShaderVersion(shaderVersion), + mShaderType(shaderType), mDebugShaderPrecisionSupported(debugShaderPrecisionSupported) { } @@ -57,7 +60,16 @@ void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc, const char kAll[] = "all"; if (name == kInvariant && value == kAll) + { + if (mShaderVersion == 300 && mShaderType == GL_FRAGMENT_SHADER) + { + // ESSL 3.00.4 section 4.6.1 + mDiagnostics.writeInfo( + pp::Diagnostics::PP_ERROR, loc, + "#pragma STDGL invariant(all) can not be used in fragment shader", name, value); + } mPragma.stdgl.invariantAll = true; + } // The STDGL pragma is used to reserve pragmas for use by future // revisions of GLSL. Do not generate an error on unexpected // name and value. diff --git a/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.h b/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.h index 2a81ee5707..00eb49114e 100644 --- a/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.h +++ b/src/3rdparty/angle/src/compiler/translator/DirectiveHandler.h @@ -11,41 +11,42 @@ #include "compiler/translator/ExtensionBehavior.h" #include "compiler/translator/Pragma.h" #include "compiler/preprocessor/DirectiveHandlerBase.h" +#include "GLSLANG/ShaderLang.h" class TDiagnostics; class TDirectiveHandler : public pp::DirectiveHandler, angle::NonCopyable { public: - TDirectiveHandler(TExtensionBehavior& extBehavior, - TDiagnostics& diagnostics, - int& shaderVersion, + TDirectiveHandler(TExtensionBehavior &extBehavior, + TDiagnostics &diagnostics, + int &shaderVersion, + sh::GLenum shaderType, bool debugShaderPrecisionSupported); - virtual ~TDirectiveHandler(); + ~TDirectiveHandler() override; const TPragma& pragma() const { return mPragma; } const TExtensionBehavior& extensionBehavior() const { return mExtensionBehavior; } - virtual void handleError(const pp::SourceLocation& loc, - const std::string& msg); + void handleError(const pp::SourceLocation &loc, const std::string &msg) override; - virtual void handlePragma(const pp::SourceLocation& loc, - const std::string& name, - const std::string& value, - bool stdgl); + void handlePragma(const pp::SourceLocation &loc, + const std::string &name, + const std::string &value, + bool stdgl) override; - virtual void handleExtension(const pp::SourceLocation& loc, - const std::string& name, - const std::string& behavior); + void handleExtension(const pp::SourceLocation &loc, + const std::string &name, + const std::string &behavior) override; - virtual void handleVersion(const pp::SourceLocation& loc, - int version); + void handleVersion(const pp::SourceLocation &loc, int version) override; private: TPragma mPragma; TExtensionBehavior& mExtensionBehavior; TDiagnostics& mDiagnostics; int& mShaderVersion; + sh::GLenum mShaderType; bool mDebugShaderPrecisionSupported; }; diff --git a/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.cpp b/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.cpp index 697e042954..4a7fa54155 100644 --- a/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.cpp +++ b/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.cpp @@ -179,11 +179,50 @@ const char *getFloatTypeStr(const TType& type) case 1: return "float"; case 2: - return type.getSecondarySize() > 1 ? "mat2" : "vec2"; + switch(type.getSecondarySize()) + { + case 1: + return "vec2"; + case 2: + return "mat2"; + case 3: + return "mat2x3"; + case 4: + return "mat2x4"; + default: + UNREACHABLE(); + return NULL; + } case 3: - return type.getSecondarySize() > 1 ? "mat3" : "vec3"; + switch(type.getSecondarySize()) + { + case 1: + return "vec3"; + case 2: + return "mat3x2"; + case 3: + return "mat3"; + case 4: + return "mat3x4"; + default: + UNREACHABLE(); + return NULL; + } case 4: - return type.getSecondarySize() > 1 ? "mat4" : "vec4"; + switch(type.getSecondarySize()) + { + case 1: + return "vec4"; + case 2: + return "mat4x2"; + case 3: + return "mat4x3"; + case 4: + return "mat4"; + default: + UNREACHABLE(); + return NULL; + } default: UNREACHABLE(); return NULL; @@ -199,8 +238,10 @@ bool canRoundFloat(const TType &type) TIntermAggregate *createInternalFunctionCallNode(TString name, TIntermNode *child) { TIntermAggregate *callNode = new TIntermAggregate(); - callNode->setOp(EOpInternalFunctionCall); - callNode->setName(name); + callNode->setOp(EOpFunctionCall); + TName nameObj(TFunction::mangleName(name)); + nameObj.setInternal(true); + callNode->setNameObj(nameObj); callNode->getSequence()->push_back(child); return callNode; } @@ -252,17 +293,14 @@ bool parentUsesResult(TIntermNode* parent, TIntermNode* node) } // namespace anonymous -EmulatePrecision::EmulatePrecision() - : TIntermTraverser(true, true, true), - mDeclaringVariables(false), - mInLValue(false), - mInFunctionCallOutParameter(false) +EmulatePrecision::EmulatePrecision(const TSymbolTable &symbolTable, int shaderVersion) + : TLValueTrackingTraverser(true, true, true, symbolTable, shaderVersion), + mDeclaringVariables(false) {} void EmulatePrecision::visitSymbol(TIntermSymbol *node) { - if (canRoundFloat(node->getType()) && - !mDeclaringVariables && !mInLValue && !mInFunctionCallOutParameter) + if (canRoundFloat(node->getType()) && !mDeclaringVariables && !isLValueRequiredHere()) { TIntermNode *parent = getParentNode(); TIntermNode *replacement = createRoundingFunctionCallNode(node); @@ -275,14 +313,6 @@ bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node) { bool visitChildren = true; - if (node->isAssignment()) - { - if (visit == PreVisit) - mInLValue = true; - else if (visit == InVisit) - mInLValue = false; - } - TOperator op = node->getOp(); // RHS of initialize is not being declared. @@ -376,22 +406,9 @@ bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node) { case EOpSequence: case EOpConstructStruct: - // No special handling - break; case EOpFunction: - if (visit == PreVisit) - { - const TIntermSequence &sequence = *(node->getSequence()); - TIntermSequence::const_iterator seqIter = sequence.begin(); - TIntermAggregate *params = (*seqIter)->getAsAggregate(); - ASSERT(params != NULL); - ASSERT(params->getOp() == EOpParameters); - mFunctionMap[node->getName()] = params->getSequence(); - } break; case EOpPrototype: - if (visit == PreVisit) - mFunctionMap[node->getName()] = node->getSequence(); visitChildren = false; break; case EOpParameters: @@ -418,50 +435,17 @@ bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node) case EOpFunctionCall: { // Function call. - bool inFunctionMap = (mFunctionMap.find(node->getName()) != mFunctionMap.end()); if (visit == PreVisit) { // User-defined function return values are not rounded, this relies on that // calculations producing the value were rounded. TIntermNode *parent = getParentNode(); - if (canRoundFloat(node->getType()) && !inFunctionMap && parentUsesResult(parent, node)) + if (canRoundFloat(node->getType()) && !isInFunctionMap(node) && + parentUsesResult(parent, node)) { TIntermNode *replacement = createRoundingFunctionCallNode(node); mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true)); } - - if (inFunctionMap) - { - mSeqIterStack.push_back(mFunctionMap[node->getName()]->begin()); - if (mSeqIterStack.back() != mFunctionMap[node->getName()]->end()) - { - TQualifier qualifier = (*mSeqIterStack.back())->getAsTyped()->getQualifier(); - mInFunctionCallOutParameter = (qualifier == EvqOut || qualifier == EvqInOut); - } - } - else - { - // The function is not user-defined - it is likely built-in texture function. - // Assume that those do not have out parameters. - mInFunctionCallOutParameter = false; - } - } - else if (visit == InVisit) - { - if (inFunctionMap) - { - ++mSeqIterStack.back(); - TQualifier qualifier = (*mSeqIterStack.back())->getAsTyped()->getQualifier(); - mInFunctionCallOutParameter = (qualifier == EvqOut || qualifier == EvqInOut); - } - } - else - { - if (inFunctionMap) - { - mSeqIterStack.pop_back(); - mInFunctionCallOutParameter = false; - } } break; } @@ -484,15 +468,10 @@ bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node) case EOpNegative: case EOpVectorLogicalNot: case EOpLogicalNot: - break; case EOpPostIncrement: case EOpPostDecrement: case EOpPreIncrement: case EOpPreDecrement: - if (visit == PreVisit) - mInLValue = true; - else if (visit == PostVisit) - mInLValue = false; break; default: if (canRoundFloat(node->getType()) && visit == PreVisit) @@ -511,7 +490,7 @@ void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase& sink, ShShaderOutput { // Other languages not yet supported ASSERT(outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT || - outputLanguage == SH_GLSL_CORE_OUTPUT || + IsGLSL130OrNewer(outputLanguage) || outputLanguage == SH_ESSL_OUTPUT); writeCommonPrecisionEmulationHelpers(sink, outputLanguage); diff --git a/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.h b/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.h index f1f560aa85..08177b3414 100644 --- a/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.h +++ b/src/3rdparty/angle/src/compiler/translator/EmulatePrecision.h @@ -8,6 +8,7 @@ #define COMPILER_TRANSLATOR_EMULATE_PRECISION_H_ #include "common/angleutils.h" +#include "compiler/translator/Compiler.h" #include "compiler/translator/InfoSink.h" #include "compiler/translator/IntermNode.h" #include "GLSLANG/ShaderLang.h" @@ -17,15 +18,15 @@ // need to write a huge number of variations of the emulated compound assignment // to every translated shader with emulation enabled. -class EmulatePrecision : public TIntermTraverser +class EmulatePrecision : public TLValueTrackingTraverser { public: - EmulatePrecision(); + EmulatePrecision(const TSymbolTable &symbolTable, int shaderVersion); - virtual void visitSymbol(TIntermSymbol *node); - virtual bool visitBinary(Visit visit, TIntermBinary *node); - virtual bool visitUnary(Visit visit, TIntermUnary *node); - virtual bool visitAggregate(Visit visit, TIntermAggregate *node); + void visitSymbol(TIntermSymbol *node) override; + bool visitBinary(Visit visit, TIntermBinary *node) override; + bool visitUnary(Visit visit, TIntermUnary *node) override; + bool visitAggregate(Visit visit, TIntermAggregate *node) override; void writeEmulationHelpers(TInfoSinkBase& sink, ShShaderOutput outputLanguage); @@ -55,20 +56,7 @@ class EmulatePrecision : public TIntermTraverser EmulationSet mEmulateCompoundMul; EmulationSet mEmulateCompoundDiv; - // Stack of function call parameter iterators - std::vector mSeqIterStack; - bool mDeclaringVariables; - bool mInLValue; - bool mInFunctionCallOutParameter; - - struct TStringComparator - { - bool operator() (const TString& a, const TString& b) const { return a.compare(b) < 0; } - }; - - // Map from function names to their parameter sequences - std::map mFunctionMap; }; #endif // COMPILER_TRANSLATOR_EMULATE_PRECISION_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.h b/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.h index cf4d7fba31..782c1c9217 100644 --- a/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.h +++ b/src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.h @@ -34,4 +34,10 @@ inline const char* getBehaviorString(TBehavior b) // Mapping between extension name and behavior. typedef std::map TExtensionBehavior; +inline bool IsExtensionEnabled(const TExtensionBehavior &extBehavior, const char *extension) +{ + auto iter = extBehavior.find(extension); + return iter != extBehavior.end() && (iter->second == EBhEnable || iter->second == EBhRequire); +} + #endif // COMPILER_TRANSLATOR_EXTENSIONBEHAVIOR_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.cpp b/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.cpp new file mode 100644 index 0000000000..d7f45f7eef --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.cpp @@ -0,0 +1,100 @@ +// +// 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. +// +// ExtensionGLSL.cpp: Implements the TExtensionGLSL class that tracks GLSL extension requirements +// of shaders. + +#include "compiler/translator/ExtensionGLSL.h" + +#include "compiler/translator/VersionGLSL.h" + +TExtensionGLSL::TExtensionGLSL(ShShaderOutput output) + : TIntermTraverser(true, false, false), mTargetVersion(ShaderOutputTypeToGLSLVersion(output)) +{ +} + +const std::set &TExtensionGLSL::getEnabledExtensions() const +{ + return mEnabledExtensions; +} + +const std::set &TExtensionGLSL::getRequiredExtensions() const +{ + return mRequiredExtensions; +} + +bool TExtensionGLSL::visitUnary(Visit, TIntermUnary *node) +{ + checkOperator(node); + + return true; +} + +bool TExtensionGLSL::visitAggregate(Visit, TIntermAggregate *node) +{ + checkOperator(node); + + return true; +} + +void TExtensionGLSL::checkOperator(TIntermOperator *node) +{ + if (mTargetVersion < GLSL_VERSION_130) + { + return; + } + + switch (node->getOp()) + { + case EOpAbs: + break; + + case EOpSign: + break; + + case EOpMix: + break; + + case EOpFloatBitsToInt: + case EOpFloatBitsToUint: + case EOpIntBitsToFloat: + case EOpUintBitsToFloat: + if (mTargetVersion < GLSL_VERSION_330) + { + // Bit conversion functions cannot be emulated. + mRequiredExtensions.insert("GL_ARB_shader_bit_encoding"); + } + break; + + case EOpPackSnorm2x16: + case EOpPackHalf2x16: + case EOpUnpackSnorm2x16: + case EOpUnpackHalf2x16: + if (mTargetVersion < GLSL_VERSION_420) + { + mEnabledExtensions.insert("GL_ARB_shading_language_packing"); + + if (mTargetVersion < GLSL_VERSION_330) + { + // floatBitsToUint and uintBitsToFloat are needed to emulate + // packHalf2x16 and unpackHalf2x16 respectively and cannot be + // emulated themselves. + mRequiredExtensions.insert("GL_ARB_shader_bit_encoding"); + } + } + break; + + case EOpPackUnorm2x16: + case EOpUnpackUnorm2x16: + if (mTargetVersion < GLSL_VERSION_410) + { + mEnabledExtensions.insert("GL_ARB_shading_language_packing"); + } + break; + + default: + break; + } +} diff --git a/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.h b/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.h new file mode 100644 index 0000000000..6bb84d612d --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.h @@ -0,0 +1,39 @@ +// +// 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. +// +// ExtensionGLSL.h: Defines the TExtensionGLSL class that tracks GLSL extension requirements of +// shaders. + +#ifndef COMPILER_TRANSLATOR_EXTENSIONGLSL_H_ +#define COMPILER_TRANSLATOR_EXTENSIONGLSL_H_ + +#include +#include + +#include "compiler/translator/IntermNode.h" + +// Traverses the intermediate tree to determine which GLSL extensions are required +// to support the shader. +class TExtensionGLSL : public TIntermTraverser +{ + public: + TExtensionGLSL(ShShaderOutput output); + + const std::set &getEnabledExtensions() const; + const std::set &getRequiredExtensions() const; + + bool visitUnary(Visit visit, TIntermUnary *node) override; + bool visitAggregate(Visit visit, TIntermAggregate *node) override; + + private: + void checkOperator(TIntermOperator *node); + + int mTargetVersion; + + std::set mEnabledExtensions; + std::set mRequiredExtensions; +}; + +#endif // COMPILER_TRANSLATOR_EXTENSIONGLSL_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.h b/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.h index 07b9a72c5c..cfcd775af7 100644 --- a/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.h +++ b/src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.h @@ -18,11 +18,17 @@ namespace sh class FlagStd140Structs : public TIntermTraverser { public: + + FlagStd140Structs() + : TIntermTraverser(true, false, false) + { + } + const std::vector getFlaggedNodes() const { return mFlaggedNodes; } protected: - virtual bool visitBinary(Visit visit, TIntermBinary *binaryNode); - virtual void visitSymbol(TIntermSymbol *symbol); + bool visitBinary(Visit visit, TIntermBinary *binaryNode) override; + void visitSymbol(TIntermSymbol *symbol) override; private: bool isInStd140InterfaceBlock(TIntermTyped *node) const; diff --git a/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.cpp b/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.cpp index f3be20d978..4cc1c26a13 100644 --- a/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.cpp +++ b/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.cpp @@ -6,6 +6,9 @@ #include "compiler/translator/ForLoopUnroll.h" +#include "compiler/translator/ValidateLimitations.h" +#include "angle_gl.h" + bool ForLoopUnrollMarker::visitBinary(Visit, TIntermBinary *node) { if (mUnrollCondition != kSamplerArrayIndex) @@ -38,11 +41,16 @@ bool ForLoopUnrollMarker::visitBinary(Visit, TIntermBinary *node) bool ForLoopUnrollMarker::visitLoop(Visit, TIntermLoop *node) { - if (mUnrollCondition == kIntegerIndex) + bool canBeUnrolled = mHasRunLoopValidation; + if (!mHasRunLoopValidation) + { + canBeUnrolled = ValidateLimitations::IsLimitedForLoop(node); + } + if (mUnrollCondition == kIntegerIndex && canBeUnrolled) { // Check if loop index type is integer. - // This is called after ValidateLimitations pass, so all the calls - // should be valid. See ValidateLimitations::validateForLoopInit(). + // This is called after ValidateLimitations pass, so the loop has the limited form specified + // in ESSL 1.00 appendix A. TIntermSequence *declSeq = node->getInit()->getAsAggregate()->getSequence(); TIntermSymbol *symbol = (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode(); if (symbol->getBasicType() == EbtInt) @@ -50,11 +58,18 @@ bool ForLoopUnrollMarker::visitLoop(Visit, TIntermLoop *node) } TIntermNode *body = node->getBody(); - if (body != NULL) + if (body != nullptr) { - mLoopStack.push(node); - body->traverse(this); - mLoopStack.pop(); + if (canBeUnrolled) + { + mLoopStack.push(node); + body->traverse(this); + mLoopStack.pop(); + } + else + { + body->traverse(this); + } } // The loop is fully processed - no need to visit children. return false; diff --git a/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.h b/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.h index c8787d55a0..9c49ecad33 100644 --- a/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.h +++ b/src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.h @@ -24,16 +24,18 @@ class ForLoopUnrollMarker : public TIntermTraverser kSamplerArrayIndex }; - ForLoopUnrollMarker(UnrollCondition condition) - : mUnrollCondition(condition), + ForLoopUnrollMarker(UnrollCondition condition, bool hasRunLoopValidation) + : TIntermTraverser(true, false, false), + mUnrollCondition(condition), mSamplerArrayIndexIsFloatLoopIndex(false), - mVisitSamplerArrayIndexNodeInsideLoop(false) + mVisitSamplerArrayIndexNodeInsideLoop(false), + mHasRunLoopValidation(hasRunLoopValidation) { } - virtual bool visitBinary(Visit, TIntermBinary *node); - virtual bool visitLoop(Visit, TIntermLoop *node); - virtual void visitSymbol(TIntermSymbol *node); + bool visitBinary(Visit, TIntermBinary *node) override; + bool visitLoop(Visit, TIntermLoop *node) override; + void visitSymbol(TIntermSymbol *node) override; bool samplerArrayIndexIsFloatLoopIndex() const { @@ -45,6 +47,7 @@ class ForLoopUnrollMarker : public TIntermTraverser TLoopStack mLoopStack; bool mSamplerArrayIndexIsFloatLoopIndex; bool mVisitSamplerArrayIndexNodeInsideLoop; + bool mHasRunLoopValidation; }; #endif // COMPILER_TRANSLATOR_FORLOOPUNROLL_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/Initialize.cpp b/src/3rdparty/angle/src/compiler/translator/Initialize.cpp index 9e11405758..2f51aada7f 100644 --- a/src/3rdparty/angle/src/compiler/translator/Initialize.cpp +++ b/src/3rdparty/angle/src/compiler/translator/Initialize.cpp @@ -11,25 +11,26 @@ // #include "compiler/translator/Initialize.h" +#include "compiler/translator/Cache.h" #include "compiler/translator/IntermNode.h" #include "angle_gl.h" void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInResources &resources, TSymbolTable &symbolTable) { - TType *float1 = new TType(EbtFloat); - TType *float2 = new TType(EbtFloat, 2); - TType *float3 = new TType(EbtFloat, 3); - TType *float4 = new TType(EbtFloat, 4); - TType *int1 = new TType(EbtInt); - TType *int2 = new TType(EbtInt, 2); - TType *int3 = new TType(EbtInt, 3); - TType *uint1 = new TType(EbtUInt); - TType *bool1 = new TType(EbtBool); - TType *genType = new TType(EbtGenType); - TType *genIType = new TType(EbtGenIType); - TType *genUType = new TType(EbtGenUType); - TType *genBType = new TType(EbtGenBType); + 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); + const TType *int1 = TCache::getType(EbtInt); + const TType *int2 = TCache::getType(EbtInt, 2); + const TType *int3 = TCache::getType(EbtInt, 3); + const TType *uint1 = TCache::getType(EbtUInt); + const TType *bool1 = TCache::getType(EbtBool); + const TType *genType = TCache::getType(EbtGenType); + const TType *genIType = TCache::getType(EbtGenIType); + const TType *genUType = TCache::getType(EbtGenUType); + const TType *genBType = TCache::getType(EbtGenBType); // // Angle and Trigonometric Functions. @@ -96,19 +97,16 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpClamp, genUType, "clamp", genUType, genUType, genUType); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMix, genType, "mix", genType, genType, float1); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMix, genType, "mix", genType, genType, genType); + symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMix, genType, "mix", genType, genType, genBType); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpStep, genType, "step", genType, genType); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpStep, genType, "step", float1, genType); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpSmoothStep, genType, "smoothstep", genType, genType, genType); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpSmoothStep, genType, "smoothstep", float1, float1, genType); - TType *outFloat1 = new TType(EbtFloat); - TType *outFloat2 = new TType(EbtFloat, 2); - TType *outFloat3 = new TType(EbtFloat, 3); - TType *outFloat4 = new TType(EbtFloat, 4); - outFloat1->setQualifier(EvqOut); - outFloat2->setQualifier(EvqOut); - outFloat3->setQualifier(EvqOut); - outFloat4->setQualifier(EvqOut); + 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); symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpModf, float1, "modf", float1, outFloat1); symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpModf, float2, "modf", float2, outFloat2); @@ -141,15 +139,15 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpReflect, genType, "reflect", genType, genType); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpRefract, genType, "refract", genType, genType, float1); - TType *mat2 = new TType(EbtFloat, 2, 2); - TType *mat3 = new TType(EbtFloat, 3, 3); - TType *mat4 = new TType(EbtFloat, 4, 4); - TType *mat2x3 = new TType(EbtFloat, 2, 3); - TType *mat3x2 = new TType(EbtFloat, 3, 2); - TType *mat2x4 = new TType(EbtFloat, 2, 4); - TType *mat4x2 = new TType(EbtFloat, 4, 2); - TType *mat3x4 = new TType(EbtFloat, 3, 4); - TType *mat4x3 = new TType(EbtFloat, 4, 3); + const TType *mat2 = TCache::getType(EbtFloat, 2, 2); + const TType *mat3 = TCache::getType(EbtFloat, 3, 3); + const TType *mat4 = TCache::getType(EbtFloat, 4, 4); + const TType *mat2x3 = TCache::getType(EbtFloat, 2, 3); + const TType *mat3x2 = TCache::getType(EbtFloat, 3, 2); + const TType *mat2x4 = TCache::getType(EbtFloat, 2, 4); + const TType *mat4x2 = TCache::getType(EbtFloat, 4, 2); + const TType *mat3x4 = TCache::getType(EbtFloat, 3, 4); + const TType *mat4x3 = TCache::getType(EbtFloat, 4, 3); // // Matrix Functions. @@ -192,10 +190,10 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpInverse, mat3, "inverse", mat3); symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpInverse, mat4, "inverse", mat4); - TType *vec = new TType(EbtVec); - TType *ivec = new TType(EbtIVec); - TType *uvec = new TType(EbtUVec); - TType *bvec = new TType(EbtBVec); + const TType *vec = TCache::getType(EbtVec); + const TType *ivec = TCache::getType(EbtIVec); + const TType *uvec = TCache::getType(EbtUVec); + const TType *bvec = TCache::getType(EbtBVec); // // Vector relational functions. @@ -224,8 +222,8 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAll, bool1, "all", bvec); symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorLogicalNot, bvec, "not", bvec); - TType *sampler2D = new TType(EbtSampler2D); - TType *samplerCube = new TType(EbtSamplerCube); + const TType *sampler2D = TCache::getType(EbtSampler2D); + const TType *samplerCube = TCache::getType(EbtSamplerCube); // // Texture Functions for GLSL ES 1.0 @@ -237,7 +235,7 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR if (resources.OES_EGL_image_external) { - TType *samplerExternalOES = new TType(EbtSamplerExternalOES); + const TType *samplerExternalOES = TCache::getType(EbtSamplerExternalOES); symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2D", samplerExternalOES, float2); symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", samplerExternalOES, float3); @@ -246,7 +244,7 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR if (resources.ARB_texture_rectangle) { - TType *sampler2DRect = new TType(EbtSampler2DRect); + const TType *sampler2DRect = TCache::getType(EbtSampler2DRect); symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DRect", sampler2DRect, float2); symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DRectProj", sampler2DRect, float3); @@ -295,12 +293,12 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "textureCubeLod", samplerCube, float3, float1); } - TType *gvec4 = new TType(EbtGVec4); + const TType *gvec4 = TCache::getType(EbtGVec4); - TType *gsampler2D = new TType(EbtGSampler2D); - TType *gsamplerCube = new TType(EbtGSamplerCube); - TType *gsampler3D = new TType(EbtGSampler3D); - TType *gsampler2DArray = new TType(EbtGSampler2DArray); + const TType *gsampler2D = TCache::getType(EbtGSampler2D); + const TType *gsamplerCube = TCache::getType(EbtGSamplerCube); + const TType *gsampler3D = TCache::getType(EbtGSampler3D); + const TType *gsampler2DArray = TCache::getType(EbtGSampler2DArray); // // Texture Functions for GLSL ES 3.0 @@ -328,9 +326,9 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProj", gsampler3D, float4, float1); } - TType *sampler2DShadow = new TType(EbtSampler2DShadow); - TType *samplerCubeShadow = new TType(EbtSamplerCubeShadow); - TType *sampler2DArrayShadow = new TType(EbtSampler2DArrayShadow); + const TType *sampler2DShadow = TCache::getType(EbtSampler2DShadow); + const TType *samplerCubeShadow = TCache::getType(EbtSamplerCubeShadow); + const TType *sampler2DArrayShadow = TCache::getType(EbtSampler2DArrayShadow); symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", sampler2DShadow, float3); symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", samplerCubeShadow, float4); @@ -466,6 +464,12 @@ void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInR if (spec != SH_CSS_SHADERS_SPEC) { symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxDrawBuffers", resources.MaxDrawBuffers); + if (resources.EXT_blend_func_extended) + { + symbolTable.insertConstIntExt(COMMON_BUILTINS, "GL_EXT_blend_func_extended", + "gl_MaxDualSourceDrawBuffersEXT", + resources.MaxDualSourceDrawBuffers); + } } symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxVertexOutputVectors", resources.MaxVertexOutputVectors); @@ -504,12 +508,33 @@ void IdentifyBuiltIns(sh::GLenum type, ShShaderSpec spec, fragData.setArraySize(resources.MaxDrawBuffers); symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragData"), fragData)); + if (resources.EXT_blend_func_extended) + { + symbolTable.insert( + ESSL1_BUILTINS, "GL_EXT_blend_func_extended", + new TVariable(NewPoolTString("gl_SecondaryFragColorEXT"), + TType(EbtFloat, EbpMedium, EvqSecondaryFragColorEXT, 4))); + TType secondaryFragData(EbtFloat, EbpMedium, EvqSecondaryFragDataEXT, 4, 1, true); + secondaryFragData.setArraySize(resources.MaxDualSourceDrawBuffers); + symbolTable.insert( + ESSL1_BUILTINS, "GL_EXT_blend_func_extended", + new TVariable(NewPoolTString("gl_SecondaryFragDataEXT"), secondaryFragData)); + } + if (resources.EXT_frag_depth) { - symbolTable.insert(ESSL1_BUILTINS, "GL_EXT_frag_depth", new TVariable(NewPoolTString("gl_FragDepthEXT"), - TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium, EvqFragDepth, 1))); + symbolTable.insert( + ESSL1_BUILTINS, "GL_EXT_frag_depth", + new TVariable( + NewPoolTString("gl_FragDepthEXT"), + TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium, + EvqFragDepthEXT, 1))); } + symbolTable.insert(ESSL3_BUILTINS, + new TVariable(NewPoolTString("gl_FragDepth"), + TType(EbtFloat, EbpHigh, EvqFragDepth, 1))); + if (resources.EXT_shader_framebuffer_fetch || resources.NV_shader_framebuffer_fetch) { TType lastFragData(EbtFloat, EbpMedium, EvqLastFragData, 4, 1, true); @@ -569,6 +594,8 @@ void InitExtensionBehavior(const ShBuiltInResources& resources, extBehavior["GL_OES_EGL_image_external"] = EBhUndefined; if (resources.ARB_texture_rectangle) extBehavior["GL_ARB_texture_rectangle"] = EBhUndefined; + if (resources.EXT_blend_func_extended) + extBehavior["GL_EXT_blend_func_extended"] = EBhUndefined; if (resources.EXT_draw_buffers) extBehavior["GL_EXT_draw_buffers"] = EBhUndefined; if (resources.EXT_frag_depth) diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeDll.cpp b/src/3rdparty/angle/src/compiler/translator/InitializeDll.cpp index c98430662a..713965389f 100644 --- a/src/3rdparty/angle/src/compiler/translator/InitializeDll.cpp +++ b/src/3rdparty/angle/src/compiler/translator/InitializeDll.cpp @@ -4,6 +4,7 @@ // found in the LICENSE file. // +#include "compiler/translator/Cache.h" #include "compiler/translator/InitializeDll.h" #include "compiler/translator/InitializeGlobals.h" #include "compiler/translator/InitializeParseContext.h" @@ -24,6 +25,8 @@ bool InitProcess() return false; } + TCache::initialize(); + return true; } @@ -31,4 +34,5 @@ void DetachProcess() { FreeParseContextIndex(); FreePoolIndex(); + TCache::destroy(); } diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeParseContext.h b/src/3rdparty/angle/src/compiler/translator/InitializeParseContext.h index fa9b885e80..70dac702e2 100644 --- a/src/3rdparty/angle/src/compiler/translator/InitializeParseContext.h +++ b/src/3rdparty/angle/src/compiler/translator/InitializeParseContext.h @@ -10,7 +10,7 @@ bool InitializeParseContextIndex(); void FreeParseContextIndex(); -struct TParseContext; +class TParseContext; extern void SetGlobalParseContext(TParseContext* context); extern TParseContext* GetGlobalParseContext(); diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeVariables.cpp b/src/3rdparty/angle/src/compiler/translator/InitializeVariables.cpp index 0e3e2ebe55..86d3e6bc83 100644 --- a/src/3rdparty/angle/src/compiler/translator/InitializeVariables.cpp +++ b/src/3rdparty/angle/src/compiler/translator/InitializeVariables.cpp @@ -5,7 +5,8 @@ // #include "compiler/translator/InitializeVariables.h" -#include "compiler/translator/compilerdebug.h" + +#include "common/debug.h" namespace { @@ -13,10 +14,10 @@ namespace TIntermConstantUnion *constructFloatConstUnionNode(const TType &type) { TType myType = type; - unsigned char size = myType.getNominalSize(); + unsigned char size = static_cast(myType.getNominalSize()); if (myType.isMatrix()) size *= size; - ConstantUnion *u = new ConstantUnion[size]; + TConstantUnion *u = new TConstantUnion[size]; for (int ii = 0; ii < size; ++ii) u[ii].setFConst(0.0f); @@ -28,7 +29,7 @@ TIntermConstantUnion *constructFloatConstUnionNode(const TType &type) TIntermConstantUnion *constructIndexNode(int index) { - ConstantUnion *u = new ConstantUnion[1]; + TConstantUnion *u = new TConstantUnion[1]; u[0].setIConst(index); TType type(EbtInt, EbpUndefined, EvqConst, 1); diff --git a/src/3rdparty/angle/src/compiler/translator/InitializeVariables.h b/src/3rdparty/angle/src/compiler/translator/InitializeVariables.h index 4a81266498..2a141ec91c 100644 --- a/src/3rdparty/angle/src/compiler/translator/InitializeVariables.h +++ b/src/3rdparty/angle/src/compiler/translator/InitializeVariables.h @@ -26,19 +26,20 @@ class InitializeVariables : public TIntermTraverser typedef TVector InitVariableInfoList; InitializeVariables(const InitVariableInfoList &vars) - : mCodeInserted(false), - mVariables(vars) + : TIntermTraverser(true, false, false), + mVariables(vars), + mCodeInserted(false) { } protected: - virtual bool visitBinary(Visit, TIntermBinary *node) { return false; } - virtual bool visitUnary(Visit, TIntermUnary *node) { return false; } - virtual bool visitSelection(Visit, TIntermSelection *node) { return false; } - virtual bool visitLoop(Visit, TIntermLoop *node) { return false; } - virtual bool visitBranch(Visit, TIntermBranch *node) { return false; } + bool visitBinary(Visit, TIntermBinary *node) override { return false; } + bool visitUnary(Visit, TIntermUnary *node) override { return false; } + bool visitSelection(Visit, TIntermSelection *node) override { return false; } + bool visitLoop(Visit, TIntermLoop *node) override { return false; } + bool visitBranch(Visit, TIntermBranch *node) override { return false; } - virtual bool visitAggregate(Visit visit, TIntermAggregate* node); + bool visitAggregate(Visit visit, TIntermAggregate *node) override; private: void insertInitCode(TIntermSequence *sequence); diff --git a/src/3rdparty/angle/src/compiler/translator/IntermNode.cpp b/src/3rdparty/angle/src/compiler/translator/IntermNode.cpp index 266e3c8e3d..2a92860088 100644 --- a/src/3rdparty/angle/src/compiler/translator/IntermNode.cpp +++ b/src/3rdparty/angle/src/compiler/translator/IntermNode.cpp @@ -10,8 +10,13 @@ #include #include +#include +#include #include +#include +#include "common/mathutil.h" +#include "common/matrix_utils.h" #include "compiler/translator/HashNames.h" #include "compiler/translator/IntermNode.h" #include "compiler/translator/SymbolTable.h" @@ -19,6 +24,10 @@ namespace { +const float kPi = 3.14159265358979323846f; +const float kDegreesToRadiansMultiplier = kPi / 180.0f; +const float kRadiansToDegreesMultiplier = 180.0f / kPi; + TPrecision GetHigherPrecision(TPrecision left, TPrecision right) { return left > right ? left : right; @@ -57,71 +66,107 @@ bool ValidateMultiplication(TOperator op, const TType &left, const TType &right) } } -bool CompareStructure(const TType& leftNodeType, - ConstantUnion *rightUnionArray, - ConstantUnion *leftUnionArray); - -bool CompareStruct(const TType &leftNodeType, - ConstantUnion *rightUnionArray, - ConstantUnion *leftUnionArray) +TConstantUnion *Vectorize(const TConstantUnion &constant, size_t size) { - const TFieldList &fields = leftNodeType.getStruct()->fields(); + TConstantUnion *constUnion = new TConstantUnion[size]; + for (unsigned int i = 0; i < size; ++i) + constUnion[i] = constant; + + return constUnion; +} - size_t structSize = fields.size(); - size_t index = 0; +void UndefinedConstantFoldingError(const TSourceLoc &loc, TOperator op, TBasicType basicType, + TInfoSink &infoSink, TConstantUnion *result) +{ + std::stringstream constantFoldingErrorStream; + constantFoldingErrorStream << "'" << GetOperatorString(op) + << "' operation result is undefined for the values passed in"; + infoSink.info.message(EPrefixWarning, loc, constantFoldingErrorStream.str().c_str()); - for (size_t j = 0; j < structSize; j++) + switch (basicType) { - size_t size = fields[j]->type()->getObjectSize(); - for (size_t i = 0; i < size; i++) - { - if (fields[j]->type()->getBasicType() == EbtStruct) - { - if (!CompareStructure(*fields[j]->type(), - &rightUnionArray[index], - &leftUnionArray[index])) - { - return false; - } - } - else - { - if (leftUnionArray[index] != rightUnionArray[index]) - return false; - index++; - } - } + case EbtFloat : + result->setFConst(0.0f); + break; + case EbtInt: + result->setIConst(0); + break; + case EbtUInt: + result->setUConst(0u); + break; + case EbtBool: + result->setBConst(false); + break; + default: + break; } - return true; } -bool CompareStructure(const TType &leftNodeType, - ConstantUnion *rightUnionArray, - ConstantUnion *leftUnionArray) +float VectorLength(const TConstantUnion *paramArray, size_t paramArraySize) { - if (leftNodeType.isArray()) + float result = 0.0f; + for (size_t i = 0; i < paramArraySize; i++) { - TType typeWithoutArrayness = leftNodeType; - typeWithoutArrayness.clearArrayness(); + float f = paramArray[i].getFConst(); + result += f * f; + } + return sqrtf(result); +} - size_t arraySize = leftNodeType.getArraySize(); +float VectorDotProduct(const TConstantUnion *paramArray1, + const TConstantUnion *paramArray2, + size_t paramArraySize) +{ + float result = 0.0f; + for (size_t i = 0; i < paramArraySize; i++) + result += paramArray1[i].getFConst() * paramArray2[i].getFConst(); + return result; +} - for (size_t i = 0; i < arraySize; ++i) - { - size_t offset = typeWithoutArrayness.getObjectSize() * i; - if (!CompareStruct(typeWithoutArrayness, - &rightUnionArray[offset], - &leftUnionArray[offset])) - { - return false; - } - } - } - else +TIntermTyped *CreateFoldedNode(TConstantUnion *constArray, + const TIntermTyped *originalNode, + TQualifier qualifier) +{ + if (constArray == nullptr) { - return CompareStruct(leftNodeType, rightUnionArray, leftUnionArray); + return nullptr; } - return true; + TIntermTyped *folded = new TIntermConstantUnion(constArray, originalNode->getType()); + folded->getTypePointer()->setQualifier(qualifier); + folded->setLine(originalNode->getLine()); + return folded; +} + +angle::Matrix GetMatrix(const TConstantUnion *paramArray, + const unsigned int &rows, + const unsigned int &cols) +{ + std::vector elements; + for (size_t i = 0; i < rows * cols; i++) + elements.push_back(paramArray[i].getFConst()); + // Transpose is used since the Matrix constructor expects arguments in row-major order, + // whereas the paramArray is in column-major order. + return angle::Matrix(elements, rows, cols).transpose(); +} + +angle::Matrix GetMatrix(const TConstantUnion *paramArray, const unsigned int &size) +{ + std::vector elements; + for (size_t i = 0; i < size * size; i++) + elements.push_back(paramArray[i].getFConst()); + // Transpose is used since the Matrix constructor expects arguments in row-major order, + // whereas the paramArray is in column-major order. + return angle::Matrix(elements, size).transpose(); +} + +void SetUnionArrayFromMatrix(const angle::Matrix &m, TConstantUnion *resultArray) +{ + // Transpose is used since the input Matrix is in row-major order, + // whereas the actual result should be in column-major order. + angle::Matrix result = m.transpose(); + std::vector resultElements = result.elements(); + for (size_t i = 0; i < resultElements.size(); i++) + resultArray[i].setFConst(resultElements[i]); } } // namespace anonymous @@ -153,7 +198,7 @@ bool TIntermLoop::replaceChildNode( REPLACE_IF_IS(mInit, TIntermNode, original, replacement); REPLACE_IF_IS(mCond, TIntermTyped, original, replacement); REPLACE_IF_IS(mExpr, TIntermTyped, original, replacement); - REPLACE_IF_IS(mBody, TIntermNode, original, replacement); + REPLACE_IF_IS(mBody, TIntermAggregate, original, replacement); return false; } @@ -189,8 +234,47 @@ bool TIntermAggregate::replaceChildNode( return false; } +bool TIntermAggregate::replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements) +{ + for (auto it = mSequence.begin(); it < mSequence.end(); ++it) + { + if (*it == original) + { + it = mSequence.erase(it); + mSequence.insert(it, replacements.begin(), replacements.end()); + return true; + } + } + return false; +} + +bool TIntermAggregate::insertChildNodes(TIntermSequence::size_type position, TIntermSequence insertions) +{ + if (position > mSequence.size()) + { + return false; + } + auto it = mSequence.begin() + position; + mSequence.insert(it, insertions.begin(), insertions.end()); + return true; +} + +bool TIntermAggregate::areChildrenConstQualified() +{ + for (TIntermNode *&child : mSequence) + { + TIntermTyped *typed = child->getAsTyped(); + if (typed && typed->getQualifier() != EvqConst) + { + return false; + } + } + return true; +} + void TIntermAggregate::setPrecisionFromChildren() { + mGotPrecisionFromChildren = true; if (getBasicType() == EbtBool) { mType.setPrecision(EbpUndefined); @@ -229,7 +313,7 @@ void TIntermAggregate::setBuiltInFunctionPrecision() } // 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) + if (mName.getString().find("textureSize") == 0) mType.setPrecision(EbpHigh); else mType.setPrecision(precision); @@ -259,6 +343,70 @@ bool TIntermCase::replaceChildNode( return false; } +TIntermTyped::TIntermTyped(const TIntermTyped &node) : TIntermNode(), mType(node.mType) +{ + // Copy constructor is disallowed for TIntermNode in order to disallow it for subclasses that + // don't explicitly allow it, so normal TIntermNode constructor is used to construct the copy. + // We need to manually copy any fields of TIntermNode besides handling fields in TIntermTyped. + mLine = node.mLine; +} + +TIntermConstantUnion::TIntermConstantUnion(const TIntermConstantUnion &node) : TIntermTyped(node) +{ + mUnionArrayPointer = node.mUnionArrayPointer; +} + +TIntermAggregate::TIntermAggregate(const TIntermAggregate &node) + : TIntermOperator(node), + mName(node.mName), + mUserDefined(node.mUserDefined), + mFunctionId(node.mFunctionId), + mUseEmulatedFunction(node.mUseEmulatedFunction), + mGotPrecisionFromChildren(node.mGotPrecisionFromChildren) +{ + for (TIntermNode *child : node.mSequence) + { + TIntermTyped *typedChild = child->getAsTyped(); + ASSERT(typedChild != nullptr); + TIntermTyped *childCopy = typedChild->deepCopy(); + mSequence.push_back(childCopy); + } +} + +TIntermBinary::TIntermBinary(const TIntermBinary &node) + : TIntermOperator(node), mAddIndexClamp(node.mAddIndexClamp) +{ + TIntermTyped *leftCopy = node.mLeft->deepCopy(); + TIntermTyped *rightCopy = node.mRight->deepCopy(); + ASSERT(leftCopy != nullptr && rightCopy != nullptr); + mLeft = leftCopy; + mRight = rightCopy; +} + +TIntermUnary::TIntermUnary(const TIntermUnary &node) + : TIntermOperator(node), mUseEmulatedFunction(node.mUseEmulatedFunction) +{ + TIntermTyped *operandCopy = node.mOperand->deepCopy(); + ASSERT(operandCopy != nullptr); + mOperand = operandCopy; +} + +TIntermSelection::TIntermSelection(const TIntermSelection &node) : TIntermTyped(node) +{ + // Only supported for ternary nodes, not if statements. + TIntermTyped *trueTyped = node.mTrueBlock->getAsTyped(); + TIntermTyped *falseTyped = node.mFalseBlock->getAsTyped(); + ASSERT(trueTyped != nullptr); + ASSERT(falseTyped != nullptr); + TIntermTyped *conditionCopy = node.mCondition->deepCopy(); + TIntermTyped *trueCopy = trueTyped->deepCopy(); + TIntermTyped *falseCopy = falseTyped->deepCopy(); + ASSERT(conditionCopy != nullptr && trueCopy != nullptr && falseCopy != nullptr); + mCondition = conditionCopy; + mTrueBlock = trueCopy; + mFalseBlock = falseCopy; +} + // // Say whether or not an operation node changes the value of a variable. // @@ -291,6 +439,22 @@ bool TIntermOperator::isAssignment() const } } +bool TIntermOperator::isMultiplication() const +{ + switch (mOp) + { + case EOpMul: + case EOpMatrixTimesMatrix: + case EOpMatrixTimesVector: + case EOpMatrixTimesScalar: + case EOpVectorTimesMatrix: + case EOpVectorTimesScalar: + return true; + default: + return false; + } +} + // // returns true if the operator is for one of the constructors // @@ -302,7 +466,13 @@ bool TIntermOperator::isConstructor() const case EOpConstructVec3: case EOpConstructVec4: case EOpConstructMat2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: case EOpConstructMat3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: case EOpConstructMat4: case EOpConstructFloat: case EOpConstructIVec2: @@ -364,7 +534,10 @@ void TIntermUnary::promote(const TType *funcReturnType) } } - mType.setQualifier(EvqTemporary); + if (mOperand->getQualifier() == EvqConst) + mType.setQualifier(EvqConst); + else + mType.setQualifier(EvqTemporary); } // @@ -389,10 +562,12 @@ bool TIntermBinary::promote(TInfoSink &infoSink) mLeft->getPrecision(), mRight->getPrecision()); getTypePointer()->setPrecision(higherPrecision); + TQualifier resultQualifier = EvqConst; // Binary operations results in temporary variables unless both // operands are const. if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst) { + resultQualifier = EvqTemporary; getTypePointer()->setQualifier(EvqTemporary); } @@ -447,14 +622,15 @@ bool TIntermBinary::promote(TInfoSink &infoSink) if (mLeft->isVector()) { mOp = EOpVectorTimesMatrix; - setType(TType(basicType, higherPrecision, EvqTemporary, - mRight->getCols(), 1)); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(mRight->getCols()), 1)); } else { mOp = EOpMatrixTimesScalar; - setType(TType(basicType, higherPrecision, EvqTemporary, - mRight->getCols(), mRight->getRows())); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(mRight->getCols()), + static_cast(mRight->getRows()))); } } else if (mLeft->isMatrix() && !mRight->isMatrix()) @@ -462,8 +638,8 @@ bool TIntermBinary::promote(TInfoSink &infoSink) if (mRight->isVector()) { mOp = EOpMatrixTimesVector; - setType(TType(basicType, higherPrecision, EvqTemporary, - mLeft->getRows(), 1)); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(mLeft->getRows()), 1)); } else { @@ -473,8 +649,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink) else if (mLeft->isMatrix() && mRight->isMatrix()) { mOp = EOpMatrixTimesMatrix; - setType(TType(basicType, higherPrecision, EvqTemporary, - mRight->getCols(), mLeft->getRows())); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(mRight->getCols()), + static_cast(mLeft->getRows()))); } else if (!mLeft->isMatrix() && !mRight->isMatrix()) { @@ -485,8 +662,8 @@ bool TIntermBinary::promote(TInfoSink &infoSink) else if (mLeft->isVector() || mRight->isVector()) { mOp = EOpVectorTimesScalar; - setType(TType(basicType, higherPrecision, EvqTemporary, - nominalSize, 1)); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(nominalSize), 1)); } } else @@ -528,8 +705,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink) else if (mLeft->isMatrix() && mRight->isMatrix()) { mOp = EOpMatrixTimesMatrixAssign; - setType(TType(basicType, higherPrecision, EvqTemporary, - mRight->getCols(), mLeft->getRows())); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(mRight->getCols()), + static_cast(mLeft->getRows()))); } else if (!mLeft->isMatrix() && !mRight->isMatrix()) { @@ -542,8 +720,8 @@ bool TIntermBinary::promote(TInfoSink &infoSink) if (!mLeft->isVector()) return false; mOp = EOpVectorTimesScalarAssign; - setType(TType(basicType, higherPrecision, EvqTemporary, - mLeft->getNominalSize(), 1)); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(mLeft->getNominalSize()), 1)); } } else @@ -612,8 +790,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink) { const int secondarySize = std::max( mLeft->getSecondarySize(), mRight->getSecondarySize()); - setType(TType(basicType, higherPrecision, EvqTemporary, - nominalSize, secondarySize)); + setType(TType(basicType, higherPrecision, resultQualifier, + static_cast(nominalSize), + static_cast(secondarySize))); if (mLeft->isArray()) { ASSERT(mLeft->getArraySize() == mRight->getArraySize()); @@ -639,308 +818,1332 @@ bool TIntermBinary::promote(TInfoSink &infoSink) return true; } +TIntermTyped *TIntermBinary::fold(TInfoSink &infoSink) +{ + TIntermConstantUnion *leftConstant = mLeft->getAsConstantUnion(); + TIntermConstantUnion *rightConstant = mRight->getAsConstantUnion(); + if (leftConstant == nullptr || rightConstant == nullptr) + { + return nullptr; + } + TConstantUnion *constArray = leftConstant->foldBinary(mOp, rightConstant, infoSink); + + // Nodes may be constant folded without being qualified as constant. + TQualifier resultQualifier = EvqConst; + if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst) + { + resultQualifier = EvqTemporary; + } + return CreateFoldedNode(constArray, this, resultQualifier); +} + +TIntermTyped *TIntermUnary::fold(TInfoSink &infoSink) +{ + TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion(); + if (operandConstant == nullptr) + { + return nullptr; + } + + TConstantUnion *constArray = nullptr; + switch (mOp) + { + case EOpAny: + case EOpAll: + case EOpLength: + case EOpTranspose: + case EOpDeterminant: + case EOpInverse: + case EOpPackSnorm2x16: + case EOpUnpackSnorm2x16: + case EOpPackUnorm2x16: + case EOpUnpackUnorm2x16: + case EOpPackHalf2x16: + case EOpUnpackHalf2x16: + constArray = operandConstant->foldUnaryWithDifferentReturnType(mOp, infoSink); + break; + default: + constArray = operandConstant->foldUnaryWithSameReturnType(mOp, infoSink); + break; + } + + // Nodes may be constant folded without being qualified as constant. + TQualifier resultQualifier = mOperand->getQualifier() == EvqConst ? EvqConst : EvqTemporary; + return CreateFoldedNode(constArray, this, resultQualifier); +} + +TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink) +{ + // Make sure that all params are constant before actual constant folding. + for (auto *param : *getSequence()) + { + if (param->getAsConstantUnion() == nullptr) + { + return nullptr; + } + } + TConstantUnion *constArray = nullptr; + if (isConstructor()) + constArray = TIntermConstantUnion::FoldAggregateConstructor(this, infoSink); + else + constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, infoSink); + + // Nodes may be constant folded without being qualified as constant. + TQualifier resultQualifier = areChildrenConstQualified() ? EvqConst : EvqTemporary; + return CreateFoldedNode(constArray, this, resultQualifier); +} + // // The fold functions see if an operation on a constant can be done in place, // without generating run-time code. // -// Returns the node to keep using, which may or may not be the node passed in. +// Returns the constant value to keep using or nullptr. // -TIntermTyped *TIntermConstantUnion::fold( - TOperator op, TIntermTyped *constantNode, TInfoSink &infoSink) +TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op, TIntermConstantUnion *rightNode, TInfoSink &infoSink) { - ConstantUnion *unionArray = getUnionArrayPointer(); + const TConstantUnion *leftArray = getUnionArrayPointer(); + const TConstantUnion *rightArray = rightNode->getUnionArrayPointer(); - if (!unionArray) - return NULL; + if (!leftArray) + return nullptr; + if (!rightArray) + return nullptr; size_t objectSize = getType().getObjectSize(); - if (constantNode) + // for a case like float f = vec4(2, 3, 4, 5) + 1.2; + if (rightNode->getType().getObjectSize() == 1 && objectSize > 1) + { + rightArray = Vectorize(*rightNode->getUnionArrayPointer(), objectSize); + } + else if (rightNode->getType().getObjectSize() > 1 && objectSize == 1) { - // binary operations - TIntermConstantUnion *node = constantNode->getAsConstantUnion(); - ConstantUnion *rightUnionArray = node->getUnionArrayPointer(); - TType returnType = getType(); + // for a case like float f = 1.2 + vec4(2, 3, 4, 5); + leftArray = Vectorize(*getUnionArrayPointer(), rightNode->getType().getObjectSize()); + objectSize = rightNode->getType().getObjectSize(); + } - if (!rightUnionArray) - return NULL; + TConstantUnion *resultArray = nullptr; - // for a case like float f = 1.2 + vec4(2,3,4,5); - if (constantNode->getType().getObjectSize() == 1 && objectSize > 1) - { - rightUnionArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; ++i) - { - rightUnionArray[i] = *node->getUnionArrayPointer(); - } - returnType = getType(); - } - else if (constantNode->getType().getObjectSize() > 1 && objectSize == 1) - { - // for a case like float f = vec4(2,3,4,5) + 1.2; - unionArray = new ConstantUnion[constantNode->getType().getObjectSize()]; - for (size_t i = 0; i < constantNode->getType().getObjectSize(); ++i) - { - unionArray[i] = *getUnionArrayPointer(); - } - returnType = node->getType(); - objectSize = constantNode->getType().getObjectSize(); - } + switch(op) + { + case EOpAdd: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] + rightArray[i]; + break; + case EOpSub: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] - rightArray[i]; + break; - ConstantUnion *tempConstArray = NULL; - TIntermConstantUnion *tempNode; + case EOpMul: + case EOpVectorTimesScalar: + case EOpMatrixTimesScalar: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] * rightArray[i]; + break; - bool boolNodeFlag = false; - switch(op) + case EOpMatrixTimesMatrix: { - case EOpAdd: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] + rightUnionArray[i]; - break; - case EOpSub: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] - rightUnionArray[i]; - break; - - case EOpMul: - case EOpVectorTimesScalar: - case EOpMatrixTimesScalar: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] * rightUnionArray[i]; - break; - - case EOpMatrixTimesMatrix: + if (getType().getBasicType() != EbtFloat || + rightNode->getBasicType() != EbtFloat) { - if (getType().getBasicType() != EbtFloat || - node->getBasicType() != EbtFloat) - { - infoSink.info.message( - EPrefixInternalError, getLine(), - "Constant Folding cannot be done for matrix multiply"); - return NULL; - } + infoSink.info.message( + EPrefixInternalError, getLine(), + "Constant Folding cannot be done for matrix multiply"); + return nullptr; + } - const int leftCols = getCols(); - const int leftRows = getRows(); - const int rightCols = constantNode->getType().getCols(); - const int rightRows = constantNode->getType().getRows(); - const int resultCols = rightCols; - const int resultRows = leftRows; + const int leftCols = getCols(); + const int leftRows = getRows(); + const int rightCols = rightNode->getType().getCols(); + const int rightRows = rightNode->getType().getRows(); + const int resultCols = rightCols; + const int resultRows = leftRows; - tempConstArray = new ConstantUnion[resultCols*resultRows]; - for (int row = 0; row < resultRows; row++) + resultArray = new TConstantUnion[resultCols * resultRows]; + for (int row = 0; row < resultRows; row++) + { + for (int column = 0; column < resultCols; column++) { - for (int column = 0; column < resultCols; column++) + resultArray[resultRows * column + row].setFConst(0.0f); + for (int i = 0; i < leftCols; i++) { - tempConstArray[resultRows * column + row].setFConst(0.0f); - for (int i = 0; i < leftCols; i++) - { - tempConstArray[resultRows * column + row].setFConst( - tempConstArray[resultRows * column + row].getFConst() + - unionArray[i * leftRows + row].getFConst() * - rightUnionArray[column * rightRows + i].getFConst()); - } + resultArray[resultRows * column + row].setFConst( + resultArray[resultRows * column + row].getFConst() + + leftArray[i * leftRows + row].getFConst() * + rightArray[column * rightRows + i].getFConst()); } } - - // update return type for matrix product - returnType.setPrimarySize(resultCols); - returnType.setSecondarySize(resultRows); } - break; + } + break; - case EOpDiv: - case EOpIMod: + case EOpDiv: + case EOpIMod: + { + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) { - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) + switch (getType().getBasicType()) { - switch (getType().getBasicType()) + case EbtFloat: + if (rightArray[i] == 0.0f) { - case EbtFloat: - if (rightUnionArray[i] == 0.0f) - { - infoSink.info.message( - EPrefixWarning, getLine(), - "Divide by zero error during constant folding"); - tempConstArray[i].setFConst( - unionArray[i].getFConst() < 0 ? -FLT_MAX : FLT_MAX); - } - else - { - ASSERT(op == EOpDiv); - tempConstArray[i].setFConst( - unionArray[i].getFConst() / - rightUnionArray[i].getFConst()); - } - break; + infoSink.info.message(EPrefixWarning, getLine(), + "Divide by zero error during constant folding"); + resultArray[i].setFConst(leftArray[i].getFConst() < 0 ? -FLT_MAX : FLT_MAX); + } + else + { + ASSERT(op == EOpDiv); + resultArray[i].setFConst(leftArray[i].getFConst() / rightArray[i].getFConst()); + } + break; - case EbtInt: - if (rightUnionArray[i] == 0) + case EbtInt: + if (rightArray[i] == 0) + { + infoSink.info.message(EPrefixWarning, getLine(), + "Divide by zero error during constant folding"); + resultArray[i].setIConst(INT_MAX); + } + else + { + if (op == EOpDiv) { - infoSink.info.message( - EPrefixWarning, getLine(), - "Divide by zero error during constant folding"); - tempConstArray[i].setIConst(INT_MAX); + resultArray[i].setIConst(leftArray[i].getIConst() / rightArray[i].getIConst()); } else { - if (op == EOpDiv) - { - tempConstArray[i].setIConst( - unionArray[i].getIConst() / - rightUnionArray[i].getIConst()); - } - else - { - ASSERT(op == EOpIMod); - tempConstArray[i].setIConst( - unionArray[i].getIConst() % - rightUnionArray[i].getIConst()); - } + ASSERT(op == EOpIMod); + resultArray[i].setIConst(leftArray[i].getIConst() % rightArray[i].getIConst()); } - break; + } + break; - case EbtUInt: - if (rightUnionArray[i] == 0) + case EbtUInt: + if (rightArray[i] == 0) + { + infoSink.info.message(EPrefixWarning, getLine(), + "Divide by zero error during constant folding"); + resultArray[i].setUConst(UINT_MAX); + } + else + { + if (op == EOpDiv) { - infoSink.info.message( - EPrefixWarning, getLine(), - "Divide by zero error during constant folding"); - tempConstArray[i].setUConst(UINT_MAX); + resultArray[i].setUConst(leftArray[i].getUConst() / rightArray[i].getUConst()); } else { - if (op == EOpDiv) - { - tempConstArray[i].setUConst( - unionArray[i].getUConst() / - rightUnionArray[i].getUConst()); - } - else - { - ASSERT(op == EOpIMod); - tempConstArray[i].setUConst( - unionArray[i].getUConst() % - rightUnionArray[i].getUConst()); - } + ASSERT(op == EOpIMod); + resultArray[i].setUConst(leftArray[i].getUConst() % rightArray[i].getUConst()); } - break; - - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Constant folding cannot be done for \"/\""); - return NULL; } + break; + + default: + infoSink.info.message(EPrefixInternalError, getLine(), + "Constant folding cannot be done for \"/\""); + return nullptr; } } - break; + } + break; - case EOpMatrixTimesVector: + case EOpMatrixTimesVector: + { + if (rightNode->getBasicType() != EbtFloat) { - if (node->getBasicType() != EbtFloat) - { - infoSink.info.message( - EPrefixInternalError, getLine(), - "Constant Folding cannot be done for matrix times vector"); - return NULL; - } + infoSink.info.message(EPrefixInternalError, getLine(), + "Constant Folding cannot be done for matrix times vector"); + return nullptr; + } - const int matrixCols = getCols(); - const int matrixRows = getRows(); + const int matrixCols = getCols(); + const int matrixRows = getRows(); - tempConstArray = new ConstantUnion[matrixRows]; + resultArray = new TConstantUnion[matrixRows]; - for (int matrixRow = 0; matrixRow < matrixRows; matrixRow++) + for (int matrixRow = 0; matrixRow < matrixRows; matrixRow++) + { + resultArray[matrixRow].setFConst(0.0f); + for (int col = 0; col < matrixCols; col++) { - tempConstArray[matrixRow].setFConst(0.0f); - for (int col = 0; col < matrixCols; col++) - { - tempConstArray[matrixRow].setFConst( - tempConstArray[matrixRow].getFConst() + - unionArray[col * matrixRows + matrixRow].getFConst() * - rightUnionArray[col].getFConst()); - } + resultArray[matrixRow].setFConst(resultArray[matrixRow].getFConst() + + leftArray[col * matrixRows + matrixRow].getFConst() * + rightArray[col].getFConst()); } - - returnType = node->getType(); - returnType.setPrimarySize(matrixRows); - - tempNode = new TIntermConstantUnion(tempConstArray, returnType); - tempNode->setLine(getLine()); - - return tempNode; } + } + break; - case EOpVectorTimesMatrix: + case EOpVectorTimesMatrix: + { + if (getType().getBasicType() != EbtFloat) { - if (getType().getBasicType() != EbtFloat) - { - infoSink.info.message( - EPrefixInternalError, getLine(), - "Constant Folding cannot be done for vector times matrix"); - return NULL; - } + infoSink.info.message(EPrefixInternalError, getLine(), + "Constant Folding cannot be done for vector times matrix"); + return nullptr; + } - const int matrixCols = constantNode->getType().getCols(); - const int matrixRows = constantNode->getType().getRows(); + const int matrixCols = rightNode->getType().getCols(); + const int matrixRows = rightNode->getType().getRows(); - tempConstArray = new ConstantUnion[matrixCols]; + resultArray = new TConstantUnion[matrixCols]; - for (int matrixCol = 0; matrixCol < matrixCols; matrixCol++) + for (int matrixCol = 0; matrixCol < matrixCols; matrixCol++) + { + resultArray[matrixCol].setFConst(0.0f); + for (int matrixRow = 0; matrixRow < matrixRows; matrixRow++) { - tempConstArray[matrixCol].setFConst(0.0f); - for (int matrixRow = 0; matrixRow < matrixRows; matrixRow++) - { - tempConstArray[matrixCol].setFConst( - tempConstArray[matrixCol].getFConst() + - unionArray[matrixRow].getFConst() * - rightUnionArray[matrixCol * matrixRows + matrixRow].getFConst()); - } + resultArray[matrixCol].setFConst(resultArray[matrixCol].getFConst() + + leftArray[matrixRow].getFConst() * + rightArray[matrixCol * matrixRows + matrixRow].getFConst()); } - - returnType.setPrimarySize(matrixCols); } - break; + } + break; - case EOpLogicalAnd: - // this code is written for possible future use, + case EOpLogicalAnd: + { + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + { + resultArray[i] = leftArray[i] && rightArray[i]; + } + } + break; + + case EOpLogicalOr: + { + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + { + resultArray[i] = leftArray[i] || rightArray[i]; + } + } + break; + + case EOpLogicalXor: + { + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + { + switch (getType().getBasicType()) + { + case EbtBool: + resultArray[i].setBConst(leftArray[i] != rightArray[i]); + break; + default: + UNREACHABLE(); + break; + } + } + } + break; + + case EOpBitwiseAnd: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] & rightArray[i]; + break; + case EOpBitwiseXor: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] ^ rightArray[i]; + break; + case EOpBitwiseOr: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] | rightArray[i]; + break; + case EOpBitShiftLeft: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] << rightArray[i]; + break; + case EOpBitShiftRight: + resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + resultArray[i] = leftArray[i] >> rightArray[i]; + break; + + case EOpLessThan: + ASSERT(objectSize == 1); + resultArray = new TConstantUnion[1]; + resultArray->setBConst(*leftArray < *rightArray); + break; + + case EOpGreaterThan: + ASSERT(objectSize == 1); + resultArray = new TConstantUnion[1]; + resultArray->setBConst(*leftArray > *rightArray); + break; + + case EOpLessThanEqual: + ASSERT(objectSize == 1); + resultArray = new TConstantUnion[1]; + resultArray->setBConst(!(*leftArray > *rightArray)); + break; + + case EOpGreaterThanEqual: + ASSERT(objectSize == 1); + resultArray = new TConstantUnion[1]; + resultArray->setBConst(!(*leftArray < *rightArray)); + break; + + case EOpEqual: + case EOpNotEqual: + { + resultArray = new TConstantUnion[1]; + bool equal = true; + for (size_t i = 0; i < objectSize; i++) + { + if (leftArray[i] != rightArray[i]) + { + equal = false; + break; // break out of for loop + } + } + if (op == EOpEqual) + { + resultArray->setBConst(equal); + } + else + { + resultArray->setBConst(!equal); + } + } + break; + + default: + infoSink.info.message( + EPrefixInternalError, getLine(), + "Invalid operator for constant folding"); + return nullptr; + } + return resultArray; +} + +// +// The fold functions see if an operation on a constant can be done in place, +// without generating run-time code. +// +// Returns the constant value to keep using or nullptr. +// +TConstantUnion *TIntermConstantUnion::foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink) +{ + // + // Do operations where the return type has a different number of components compared to the operand type. + // + + const TConstantUnion *operandArray = getUnionArrayPointer(); + if (!operandArray) + return nullptr; + + size_t objectSize = getType().getObjectSize(); + TConstantUnion *resultArray = nullptr; + switch (op) + { + case EOpAny: + if (getType().getBasicType() == EbtBool) + { + resultArray = new TConstantUnion(); + resultArray->setBConst(false); + for (size_t i = 0; i < objectSize; i++) + { + if (operandArray[i].getBConst()) + { + resultArray->setBConst(true); + break; + } + } + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpAll: + if (getType().getBasicType() == EbtBool) + { + resultArray = new TConstantUnion(); + resultArray->setBConst(true); + for (size_t i = 0; i < objectSize; i++) + { + if (!operandArray[i].getBConst()) + { + resultArray->setBConst(false); + break; + } + } + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpLength: + if (getType().getBasicType() == EbtFloat) + { + resultArray = new TConstantUnion(); + resultArray->setFConst(VectorLength(operandArray, objectSize)); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpTranspose: + if (getType().getBasicType() == EbtFloat) + { + resultArray = new TConstantUnion[objectSize]; + angle::Matrix result = + GetMatrix(operandArray, getType().getNominalSize(), getType().getSecondarySize()).transpose(); + SetUnionArrayFromMatrix(result, resultArray); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpDeterminant: + if (getType().getBasicType() == EbtFloat) + { + unsigned int size = getType().getNominalSize(); + ASSERT(size >= 2 && size <= 4); + resultArray = new TConstantUnion(); + resultArray->setFConst(GetMatrix(operandArray, size).determinant()); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpInverse: + if (getType().getBasicType() == EbtFloat) + { + unsigned int size = getType().getNominalSize(); + ASSERT(size >= 2 && size <= 4); + resultArray = new TConstantUnion[objectSize]; + angle::Matrix result = GetMatrix(operandArray, size).inverse(); + SetUnionArrayFromMatrix(result, resultArray); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpPackSnorm2x16: + if (getType().getBasicType() == EbtFloat) + { + ASSERT(getType().getNominalSize() == 2); + resultArray = new TConstantUnion(); + resultArray->setUConst(gl::packSnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst())); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpUnpackSnorm2x16: + if (getType().getBasicType() == EbtUInt) + { + resultArray = new TConstantUnion[2]; + float f1, f2; + gl::unpackSnorm2x16(operandArray[0].getUConst(), &f1, &f2); + resultArray[0].setFConst(f1); + resultArray[1].setFConst(f2); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpPackUnorm2x16: + if (getType().getBasicType() == EbtFloat) + { + ASSERT(getType().getNominalSize() == 2); + resultArray = new TConstantUnion(); + resultArray->setUConst(gl::packUnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst())); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpUnpackUnorm2x16: + if (getType().getBasicType() == EbtUInt) + { + resultArray = new TConstantUnion[2]; + float f1, f2; + gl::unpackUnorm2x16(operandArray[0].getUConst(), &f1, &f2); + resultArray[0].setFConst(f1); + resultArray[1].setFConst(f2); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpPackHalf2x16: + if (getType().getBasicType() == EbtFloat) + { + ASSERT(getType().getNominalSize() == 2); + resultArray = new TConstantUnion(); + resultArray->setUConst(gl::packHalf2x16(operandArray[0].getFConst(), operandArray[1].getFConst())); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + + case EOpUnpackHalf2x16: + if (getType().getBasicType() == EbtUInt) + { + resultArray = new TConstantUnion[2]; + float f1, f2; + gl::unpackHalf2x16(operandArray[0].getUConst(), &f1, &f2); + resultArray[0].setFConst(f1); + resultArray[1].setFConst(f2); + break; + } + else + { + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + } + break; + + default: + break; + } + + return resultArray; +} + +TConstantUnion *TIntermConstantUnion::foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink) +{ + // + // Do unary operations where the return type is the same as operand type. + // + + const TConstantUnion *operandArray = getUnionArrayPointer(); + if (!operandArray) + return nullptr; + + size_t objectSize = getType().getObjectSize(); + + TConstantUnion *resultArray = new TConstantUnion[objectSize]; + for (size_t i = 0; i < objectSize; i++) + { + switch(op) + { + case EOpNegative: + switch (getType().getBasicType()) + { + case EbtFloat: + resultArray[i].setFConst(-operandArray[i].getFConst()); + break; + case EbtInt: + resultArray[i].setIConst(-operandArray[i].getIConst()); + break; + case EbtUInt: + resultArray[i].setUConst(static_cast( + -static_cast(operandArray[i].getUConst()))); + break; + default: + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + } + break; + + case EOpPositive: + switch (getType().getBasicType()) + { + case EbtFloat: + resultArray[i].setFConst(operandArray[i].getFConst()); + break; + case EbtInt: + resultArray[i].setIConst(operandArray[i].getIConst()); + break; + case EbtUInt: + resultArray[i].setUConst(static_cast( + static_cast(operandArray[i].getUConst()))); + break; + default: + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + } + break; + + case EOpLogicalNot: + // this code is written for possible future use, // will not get executed currently + switch (getType().getBasicType()) + { + case EbtBool: + resultArray[i].setBConst(!operandArray[i].getBConst()); + break; + default: + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + } + break; + + case EOpBitwiseNot: + switch (getType().getBasicType()) + { + case EbtInt: + resultArray[i].setIConst(~operandArray[i].getIConst()); + break; + case EbtUInt: + resultArray[i].setUConst(~operandArray[i].getUConst()); + break; + default: + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + } + break; + + case EOpRadians: + if (getType().getBasicType() == EbtFloat) + { + resultArray[i].setFConst(kDegreesToRadiansMultiplier * operandArray[i].getFConst()); + break; + } + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + + case EOpDegrees: + if (getType().getBasicType() == EbtFloat) + { + resultArray[i].setFConst(kRadiansToDegreesMultiplier * operandArray[i].getFConst()); + break; + } + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + + case EOpSin: + if (!foldFloatTypeUnary(operandArray[i], &sinf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpCos: + if (!foldFloatTypeUnary(operandArray[i], &cosf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpTan: + if (!foldFloatTypeUnary(operandArray[i], &tanf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpAsin: + // For asin(x), results are undefined if |x| > 1, we are choosing to set result to 0. + if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) > 1.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &asinf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpAcos: + // For acos(x), results are undefined if |x| > 1, we are choosing to set result to 0. + if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) > 1.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &acosf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpAtan: + if (!foldFloatTypeUnary(operandArray[i], &atanf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpSinh: + if (!foldFloatTypeUnary(operandArray[i], &sinhf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpCosh: + if (!foldFloatTypeUnary(operandArray[i], &coshf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpTanh: + if (!foldFloatTypeUnary(operandArray[i], &tanhf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpAsinh: + if (!foldFloatTypeUnary(operandArray[i], &asinhf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpAcosh: + // For acosh(x), results are undefined if x < 1, we are choosing to set result to 0. + if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() < 1.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &acoshf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpAtanh: + // For atanh(x), results are undefined if |x| >= 1, we are choosing to set result to 0. + if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) >= 1.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &atanhf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpAbs: + switch (getType().getBasicType()) + { + case EbtFloat: + resultArray[i].setFConst(fabsf(operandArray[i].getFConst())); + break; + case EbtInt: + resultArray[i].setIConst(abs(operandArray[i].getIConst())); + break; + default: + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + } + break; + + case EOpSign: + switch (getType().getBasicType()) + { + case EbtFloat: + { + float fConst = operandArray[i].getFConst(); + float fResult = 0.0f; + if (fConst > 0.0f) + fResult = 1.0f; + else if (fConst < 0.0f) + fResult = -1.0f; + resultArray[i].setFConst(fResult); + } + break; + case EbtInt: + { + int iConst = operandArray[i].getIConst(); + int iResult = 0; + if (iConst > 0) + iResult = 1; + else if (iConst < 0) + iResult = -1; + resultArray[i].setIConst(iResult); + } + break; + default: + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + } + break; + + case EOpFloor: + if (!foldFloatTypeUnary(operandArray[i], &floorf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpTrunc: + if (!foldFloatTypeUnary(operandArray[i], &truncf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpRound: + if (!foldFloatTypeUnary(operandArray[i], &roundf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpRoundEven: + if (getType().getBasicType() == EbtFloat) + { + float x = operandArray[i].getFConst(); + float result; + float fractPart = modff(x, &result); + if (fabsf(fractPart) == 0.5f) + result = 2.0f * roundf(x / 2.0f); + else + result = roundf(x); + resultArray[i].setFConst(result); + break; + } + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + + case EOpCeil: + if (!foldFloatTypeUnary(operandArray[i], &ceilf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpFract: + if (getType().getBasicType() == EbtFloat) + { + float x = operandArray[i].getFConst(); + resultArray[i].setFConst(x - floorf(x)); + break; + } + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + + case EOpIsNan: + if (getType().getBasicType() == EbtFloat) + { + resultArray[i].setBConst(gl::isNaN(operandArray[0].getFConst())); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + case EOpIsInf: + if (getType().getBasicType() == EbtFloat) + { + resultArray[i].setBConst(gl::isInf(operandArray[0].getFConst())); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + case EOpFloatBitsToInt: + if (getType().getBasicType() == EbtFloat) + { + resultArray[i].setIConst(gl::bitCast(operandArray[0].getFConst())); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + case EOpFloatBitsToUint: + if (getType().getBasicType() == EbtFloat) + { + resultArray[i].setUConst(gl::bitCast(operandArray[0].getFConst())); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + case EOpIntBitsToFloat: + if (getType().getBasicType() == EbtInt) + { + resultArray[i].setFConst(gl::bitCast(operandArray[0].getIConst())); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + case EOpUintBitsToFloat: + if (getType().getBasicType() == EbtUInt) + { + resultArray[i].setFConst(gl::bitCast(operandArray[0].getUConst())); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + case EOpExp: + if (!foldFloatTypeUnary(operandArray[i], &expf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpLog: + // For log(x), results are undefined if x <= 0, we are choosing to set result to 0. + if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &logf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpExp2: + if (!foldFloatTypeUnary(operandArray[i], &exp2f, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpLog2: + // For log2(x), results are undefined if x <= 0, we are choosing to set result to 0. + // And log2f is not available on some plarforms like old android, so just using log(x)/log(2) here. + if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &logf, infoSink, &resultArray[i])) + return nullptr; + else + resultArray[i].setFConst(resultArray[i].getFConst() / logf(2.0f)); + break; + + case EOpSqrt: + // For sqrt(x), results are undefined if x < 0, we are choosing to set result to 0. + if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() < 0.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &sqrtf, infoSink, &resultArray[i])) + return nullptr; + break; + + case EOpInverseSqrt: + // There is no stdlib built-in function equavalent for GLES built-in inversesqrt(), + // so getting the square root first using builtin function sqrt() and then taking its inverse. + // Also, for inversesqrt(x), results are undefined if x <= 0, we are choosing to set result to 0. + if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f) + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]); + else if (!foldFloatTypeUnary(operandArray[i], &sqrtf, infoSink, &resultArray[i])) + return nullptr; + else + resultArray[i].setFConst(1.0f / resultArray[i].getFConst()); + break; + + case EOpVectorLogicalNot: + if (getType().getBasicType() == EbtBool) + { + resultArray[i].setBConst(!operandArray[i].getBConst()); + break; + } + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return nullptr; + + case EOpNormalize: + if (getType().getBasicType() == EbtFloat) + { + float x = operandArray[i].getFConst(); + float length = VectorLength(operandArray, objectSize); + if (length) + resultArray[i].setFConst(x / length); + else + UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, + &resultArray[i]); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + case EOpDFdx: + case EOpDFdy: + case EOpFwidth: + if (getType().getBasicType() == EbtFloat) + { + // Derivatives of constant arguments should be 0. + resultArray[i].setFConst(0.0f); + break; + } + infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); + return nullptr; + + default: + return nullptr; + } + } + + return resultArray; +} + +bool TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion ¶meter, FloatTypeUnaryFunc builtinFunc, + TInfoSink &infoSink, TConstantUnion *result) const +{ + ASSERT(builtinFunc); + + if (getType().getBasicType() == EbtFloat) + { + result->setFConst(builtinFunc(parameter.getFConst())); + return true; + } + + infoSink.info.message( + EPrefixInternalError, getLine(), + "Unary operation not folded into constant"); + return false; +} + +// static +TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate *aggregate, + TInfoSink &infoSink) +{ + ASSERT(aggregate->getSequence()->size() > 0u); + size_t resultSize = aggregate->getType().getObjectSize(); + TConstantUnion *resultArray = new TConstantUnion[resultSize]; + TBasicType basicType = aggregate->getBasicType(); + + size_t resultIndex = 0u; + + if (aggregate->getSequence()->size() == 1u) + { + TIntermNode *argument = aggregate->getSequence()->front(); + TIntermConstantUnion *argumentConstant = argument->getAsConstantUnion(); + const TConstantUnion *argumentUnionArray = argumentConstant->getUnionArrayPointer(); + // Check the special case of constructing a matrix diagonal from a single scalar, + // or a vector from a single scalar. + if (argumentConstant->getType().getObjectSize() == 1u) + { + if (aggregate->isMatrix()) + { + int resultCols = aggregate->getType().getCols(); + int resultRows = aggregate->getType().getRows(); + for (int col = 0; col < resultCols; ++col) + { + for (int row = 0; row < resultRows; ++row) + { + if (col == row) + { + resultArray[resultIndex].cast(basicType, argumentUnionArray[0]); + } + else + { + resultArray[resultIndex].setFConst(0.0f); + } + ++resultIndex; + } + } + } + else + { + while (resultIndex < resultSize) + { + resultArray[resultIndex].cast(basicType, argumentUnionArray[0]); + ++resultIndex; + } + } + ASSERT(resultIndex == resultSize); + return resultArray; + } + else if (aggregate->isMatrix() && argumentConstant->isMatrix()) + { + // The special case of constructing a matrix from a matrix. + int argumentCols = argumentConstant->getType().getCols(); + int argumentRows = argumentConstant->getType().getRows(); + int resultCols = aggregate->getType().getCols(); + int resultRows = aggregate->getType().getRows(); + for (int col = 0; col < resultCols; ++col) + { + for (int row = 0; row < resultRows; ++row) + { + if (col < argumentCols && row < argumentRows) + { + resultArray[resultIndex].cast(basicType, + argumentUnionArray[col * argumentRows + row]); + } + else if (col == row) + { + resultArray[resultIndex].setFConst(1.0f); + } + else + { + resultArray[resultIndex].setFConst(0.0f); + } + ++resultIndex; + } + } + ASSERT(resultIndex == resultSize); + return resultArray; + } + } + + for (TIntermNode *&argument : *aggregate->getSequence()) + { + TIntermConstantUnion *argumentConstant = argument->getAsConstantUnion(); + size_t argumentSize = argumentConstant->getType().getObjectSize(); + const TConstantUnion *argumentUnionArray = argumentConstant->getUnionArrayPointer(); + for (size_t i = 0u; i < argumentSize; ++i) + { + if (resultIndex >= resultSize) + break; + resultArray[resultIndex].cast(basicType, argumentUnionArray[i]); + ++resultIndex; + } + } + ASSERT(resultIndex == resultSize); + return resultArray; +} + +// static +TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink) +{ + TOperator op = aggregate->getOp(); + TIntermSequence *sequence = aggregate->getSequence(); + unsigned int paramsCount = static_cast(sequence->size()); + std::vector unionArrays(paramsCount); + std::vector objectSizes(paramsCount); + size_t maxObjectSize = 0; + TBasicType basicType = EbtVoid; + TSourceLoc loc; + for (unsigned int i = 0; i < paramsCount; i++) + { + TIntermConstantUnion *paramConstant = (*sequence)[i]->getAsConstantUnion(); + ASSERT(paramConstant != nullptr); // Should be checked already. + + if (i == 0) + { + basicType = paramConstant->getType().getBasicType(); + loc = paramConstant->getLine(); + } + unionArrays[i] = paramConstant->getUnionArrayPointer(); + objectSizes[i] = paramConstant->getType().getObjectSize(); + if (objectSizes[i] > maxObjectSize) + maxObjectSize = objectSizes[i]; + } + + if (!(*sequence)[0]->getAsTyped()->isMatrix()) + { + for (unsigned int i = 0; i < paramsCount; i++) + if (objectSizes[i] != maxObjectSize) + unionArrays[i] = Vectorize(*unionArrays[i], maxObjectSize); + } + + TConstantUnion *resultArray = nullptr; + if (paramsCount == 2) + { + // + // Binary built-in + // + switch (op) + { + case EOpAtan: + { + if (basicType == EbtFloat) + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + float y = unionArrays[0][i].getFConst(); + float x = unionArrays[1][i].getFConst(); + // Results are undefined if x and y are both 0. + if (x == 0.0f && y == 0.0f) + UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + else + resultArray[i].setFConst(atan2f(y, x)); + } + } + else + UNREACHABLE(); + } + break; + + case EOpPow: + { + if (basicType == EbtFloat) + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + float x = unionArrays[0][i].getFConst(); + float y = unionArrays[1][i].getFConst(); + // Results are undefined if x < 0. + // Results are undefined if x = 0 and y <= 0. + if (x < 0.0f) + UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + else if (x == 0.0f && y <= 0.0f) + UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + else + resultArray[i].setFConst(powf(x, y)); + } + } + else + UNREACHABLE(); + } + break; + + case EOpMod: { - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) + if (basicType == EbtFloat) { - tempConstArray[i] = unionArray[i] && rightUnionArray[i]; + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + float x = unionArrays[0][i].getFConst(); + float y = unionArrays[1][i].getFConst(); + resultArray[i].setFConst(x - y * floorf(x / y)); + } } + else + UNREACHABLE(); } break; - case EOpLogicalOr: - // this code is written for possible future use, - // will not get executed currently + case EOpMin: { - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) { - tempConstArray[i] = unionArray[i] || rightUnionArray[i]; + switch (basicType) + { + case EbtFloat: + resultArray[i].setFConst(std::min(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst())); + break; + case EbtInt: + resultArray[i].setIConst(std::min(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst())); + break; + case EbtUInt: + resultArray[i].setUConst(std::min(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst())); + break; + default: + UNREACHABLE(); + break; + } } } break; - case EOpLogicalXor: + case EOpMax: { - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) { - switch (getType().getBasicType()) + switch (basicType) { - case EbtBool: - tempConstArray[i].setBConst( - unionArray[i] == rightUnionArray[i] ? false : true); + case EbtFloat: + resultArray[i].setFConst(std::max(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst())); + break; + case EbtInt: + resultArray[i].setIConst(std::max(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst())); + break; + case EbtUInt: + resultArray[i].setUConst(std::max(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst())); break; default: UNREACHABLE(); @@ -950,249 +2153,443 @@ TIntermTyped *TIntermConstantUnion::fold( } break; - case EOpBitwiseAnd: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] & rightUnionArray[i]; - break; - case EOpBitwiseXor: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] ^ rightUnionArray[i]; - break; - case EOpBitwiseOr: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] | rightUnionArray[i]; - break; - case EOpBitShiftLeft: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] << rightUnionArray[i]; - break; - case EOpBitShiftRight: - tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] >> rightUnionArray[i]; + case EOpStep: + { + if (basicType == EbtFloat) + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + resultArray[i].setFConst(unionArrays[1][i].getFConst() < unionArrays[0][i].getFConst() ? 0.0f : 1.0f); + } + else + UNREACHABLE(); + } break; case EOpLessThan: - ASSERT(objectSize == 1); - tempConstArray = new ConstantUnion[1]; - tempConstArray->setBConst(*unionArray < *rightUnionArray); - returnType = TType(EbtBool, EbpUndefined, EvqConst); + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + switch (basicType) + { + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() < unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() < unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() < unionArrays[1][i].getUConst()); + break; + default: + UNREACHABLE(); + break; + } + } + } break; - case EOpGreaterThan: - ASSERT(objectSize == 1); - tempConstArray = new ConstantUnion[1]; - tempConstArray->setBConst(*unionArray > *rightUnionArray); - returnType = TType(EbtBool, EbpUndefined, EvqConst); + case EOpLessThanEqual: + { + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + switch (basicType) + { + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() <= unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() <= unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() <= unionArrays[1][i].getUConst()); + break; + default: + UNREACHABLE(); + break; + } + } + } break; - case EOpLessThanEqual: + case EOpGreaterThan: { - ASSERT(objectSize == 1); - ConstantUnion constant; - constant.setBConst(*unionArray > *rightUnionArray); - tempConstArray = new ConstantUnion[1]; - tempConstArray->setBConst(!constant.getBConst()); - returnType = TType(EbtBool, EbpUndefined, EvqConst); - break; + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + switch (basicType) + { + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() > unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() > unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() > unionArrays[1][i].getUConst()); + break; + default: + UNREACHABLE(); + break; + } + } } + break; case EOpGreaterThanEqual: { - ASSERT(objectSize == 1); - ConstantUnion constant; - constant.setBConst(*unionArray < *rightUnionArray); - tempConstArray = new ConstantUnion[1]; - tempConstArray->setBConst(!constant.getBConst()); - returnType = TType(EbtBool, EbpUndefined, EvqConst); - break; + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + switch (basicType) + { + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() >= unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() >= unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() >= unionArrays[1][i].getUConst()); + break; + default: + UNREACHABLE(); + break; + } + } } + break; - case EOpEqual: - if (getType().getBasicType() == EbtStruct) + case EOpVectorEqual: { - if (!CompareStructure(node->getType(), - node->getUnionArrayPointer(), - unionArray)) + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) { - boolNodeFlag = true; + switch (basicType) + { + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() == unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() == unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() == unionArrays[1][i].getUConst()); + break; + case EbtBool: + resultArray[i].setBConst(unionArrays[0][i].getBConst() == unionArrays[1][i].getBConst()); + break; + default: + UNREACHABLE(); + break; + } } } - else + break; + + case EOpVectorNotEqual: { - for (size_t i = 0; i < objectSize; i++) + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) { - if (unionArray[i] != rightUnionArray[i]) + switch (basicType) { - boolNodeFlag = true; - break; // break out of for loop + case EbtFloat: + resultArray[i].setBConst(unionArrays[0][i].getFConst() != unionArrays[1][i].getFConst()); + break; + case EbtInt: + resultArray[i].setBConst(unionArrays[0][i].getIConst() != unionArrays[1][i].getIConst()); + break; + case EbtUInt: + resultArray[i].setBConst(unionArrays[0][i].getUConst() != unionArrays[1][i].getUConst()); + break; + case EbtBool: + resultArray[i].setBConst(unionArrays[0][i].getBConst() != unionArrays[1][i].getBConst()); + break; + default: + UNREACHABLE(); + break; } } } + break; - tempConstArray = new ConstantUnion[1]; - if (!boolNodeFlag) + case EOpDistance: + if (basicType == EbtFloat) { - tempConstArray->setBConst(true); + TConstantUnion *distanceArray = new TConstantUnion[maxObjectSize]; + resultArray = new TConstantUnion(); + for (size_t i = 0; i < maxObjectSize; i++) + { + float x = unionArrays[0][i].getFConst(); + float y = unionArrays[1][i].getFConst(); + distanceArray[i].setFConst(x - y); + } + resultArray->setFConst(VectorLength(distanceArray, maxObjectSize)); } else - { - tempConstArray->setBConst(false); - } + UNREACHABLE(); + break; - tempNode = new TIntermConstantUnion( - tempConstArray, TType(EbtBool, EbpUndefined, EvqConst)); - tempNode->setLine(getLine()); + case EOpDot: - return tempNode; + if (basicType == EbtFloat) + { + resultArray = new TConstantUnion(); + resultArray->setFConst(VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize)); + } + else + UNREACHABLE(); + break; - case EOpNotEqual: - if (getType().getBasicType() == EbtStruct) + case EOpCross: + if (basicType == EbtFloat && maxObjectSize == 3) { - if (CompareStructure(node->getType(), - node->getUnionArrayPointer(), - unionArray)) - { - boolNodeFlag = true; - } + resultArray = new TConstantUnion[maxObjectSize]; + float x0 = unionArrays[0][0].getFConst(); + float x1 = unionArrays[0][1].getFConst(); + float x2 = unionArrays[0][2].getFConst(); + float y0 = unionArrays[1][0].getFConst(); + float y1 = unionArrays[1][1].getFConst(); + float y2 = unionArrays[1][2].getFConst(); + resultArray[0].setFConst(x1 * y2 - y1 * x2); + resultArray[1].setFConst(x2 * y0 - y2 * x0); + resultArray[2].setFConst(x0 * y1 - y0 * x1); } else + UNREACHABLE(); + break; + + case EOpReflect: + if (basicType == EbtFloat) { - for (size_t i = 0; i < objectSize; i++) + // genType reflect (genType I, genType N) : + // For the incident vector I and surface orientation N, returns the reflection direction: + // I - 2 * dot(N, I) * N. + resultArray = new TConstantUnion[maxObjectSize]; + float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize); + for (size_t i = 0; i < maxObjectSize; i++) { - if (unionArray[i] == rightUnionArray[i]) - { - boolNodeFlag = true; - break; // break out of for loop - } + float result = unionArrays[0][i].getFConst() - + 2.0f * dotProduct * unionArrays[1][i].getFConst(); + resultArray[i].setFConst(result); } } + else + UNREACHABLE(); + break; - tempConstArray = new ConstantUnion[1]; - if (!boolNodeFlag) + case EOpMul: + if (basicType == EbtFloat && (*sequence)[0]->getAsTyped()->isMatrix() && + (*sequence)[1]->getAsTyped()->isMatrix()) { - tempConstArray->setBConst(true); + // Perform component-wise matrix multiplication. + resultArray = new TConstantUnion[maxObjectSize]; + int size = (*sequence)[0]->getAsTyped()->getNominalSize(); + angle::Matrix result = + GetMatrix(unionArrays[0], size).compMult(GetMatrix(unionArrays[1], size)); + SetUnionArrayFromMatrix(result, resultArray); } else + UNREACHABLE(); + break; + + case EOpOuterProduct: + if (basicType == EbtFloat) { - tempConstArray->setBConst(false); + size_t numRows = (*sequence)[0]->getAsTyped()->getType().getObjectSize(); + size_t numCols = (*sequence)[1]->getAsTyped()->getType().getObjectSize(); + resultArray = new TConstantUnion[numRows * numCols]; + angle::Matrix result = + GetMatrix(unionArrays[0], 1, static_cast(numCols)) + .outerProduct(GetMatrix(unionArrays[1], static_cast(numRows), 1)); + SetUnionArrayFromMatrix(result, resultArray); } - - tempNode = new TIntermConstantUnion( - tempConstArray, TType(EbtBool, EbpUndefined, EvqConst)); - tempNode->setLine(getLine()); - - return tempNode; + else + UNREACHABLE(); + break; default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Invalid operator for constant folding"); - return NULL; + UNREACHABLE(); + // TODO: Add constant folding support for other built-in operations that take 2 parameters and not handled above. + return nullptr; } - tempNode = new TIntermConstantUnion(tempConstArray, returnType); - tempNode->setLine(getLine()); - - return tempNode; } - else + else if (paramsCount == 3) { // - // Do unary operations + // Ternary built-in // - TIntermConstantUnion *newNode = 0; - ConstantUnion* tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) + switch (op) { - switch(op) + case EOpClamp: { - case EOpNegative: - switch (getType().getBasicType()) + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) { - case EbtFloat: - tempConstArray[i].setFConst(-unionArray[i].getFConst()); - break; - case EbtInt: - tempConstArray[i].setIConst(-unionArray[i].getIConst()); - break; - case EbtUInt: - tempConstArray[i].setUConst(static_cast( - -static_cast(unionArray[i].getUConst()))); - break; - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return NULL; + switch (basicType) + { + case EbtFloat: + { + float x = unionArrays[0][i].getFConst(); + float min = unionArrays[1][i].getFConst(); + float max = unionArrays[2][i].getFConst(); + // Results are undefined if min > max. + if (min > max) + UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + else + resultArray[i].setFConst(gl::clamp(x, min, max)); + } + break; + case EbtInt: + { + int x = unionArrays[0][i].getIConst(); + int min = unionArrays[1][i].getIConst(); + int max = unionArrays[2][i].getIConst(); + // Results are undefined if min > max. + if (min > max) + UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + else + resultArray[i].setIConst(gl::clamp(x, min, max)); + } + break; + case EbtUInt: + { + unsigned int x = unionArrays[0][i].getUConst(); + unsigned int min = unionArrays[1][i].getUConst(); + unsigned int max = unionArrays[2][i].getUConst(); + // Results are undefined if min > max. + if (min > max) + UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + else + resultArray[i].setUConst(gl::clamp(x, min, max)); + } + break; + default: + UNREACHABLE(); + break; + } } - break; + } + break; - case EOpPositive: - switch (getType().getBasicType()) + case EOpMix: + { + if (basicType == EbtFloat) { - case EbtFloat: - tempConstArray[i].setFConst(unionArray[i].getFConst()); - break; - case EbtInt: - tempConstArray[i].setIConst(unionArray[i].getIConst()); - break; - case EbtUInt: - tempConstArray[i].setUConst(static_cast( - static_cast(unionArray[i].getUConst()))); - break; - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return NULL; + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + float x = unionArrays[0][i].getFConst(); + float y = unionArrays[1][i].getFConst(); + TBasicType type = (*sequence)[2]->getAsTyped()->getType().getBasicType(); + if (type == EbtFloat) + { + // Returns the linear blend of x and y, i.e., x * (1 - a) + y * a. + float a = unionArrays[2][i].getFConst(); + resultArray[i].setFConst(x * (1.0f - a) + y * a); + } + else // 3rd parameter is EbtBool + { + ASSERT(type == EbtBool); + // 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. + bool a = unionArrays[2][i].getBConst(); + resultArray[i].setFConst(a ? y : x); + } + } } - break; + else + UNREACHABLE(); + } + break; - case EOpLogicalNot: - // this code is written for possible future use, - // will not get executed currently - switch (getType().getBasicType()) + case EOpSmoothStep: + { + if (basicType == EbtFloat) { - case EbtBool: - tempConstArray[i].setBConst(!unionArray[i].getBConst()); - break; - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return NULL; + resultArray = new TConstantUnion[maxObjectSize]; + for (size_t i = 0; i < maxObjectSize; i++) + { + float edge0 = unionArrays[0][i].getFConst(); + float edge1 = unionArrays[1][i].getFConst(); + float x = unionArrays[2][i].getFConst(); + // Results are undefined if edge0 >= edge1. + if (edge0 >= edge1) + { + UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]); + } + else + { + // Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and performs smooth + // Hermite interpolation between 0 and 1 when edge0 < x < edge1. + float t = gl::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f); + resultArray[i].setFConst(t * t * (3.0f - 2.0f * t)); + } + } } - break; + else + UNREACHABLE(); + } + break; - case EOpBitwiseNot: - switch (getType().getBasicType()) + case EOpFaceForward: + if (basicType == EbtFloat) + { + // genType faceforward(genType N, genType I, genType Nref) : + // If dot(Nref, I) < 0 return N, otherwise return -N. + resultArray = new TConstantUnion[maxObjectSize]; + float dotProduct = VectorDotProduct(unionArrays[2], unionArrays[1], maxObjectSize); + for (size_t i = 0; i < maxObjectSize; i++) { - case EbtInt: - tempConstArray[i].setIConst(~unionArray[i].getIConst()); - break; - case EbtUInt: - tempConstArray[i].setUConst(~unionArray[i].getUConst()); - break; - default: - infoSink.info.message( - EPrefixInternalError, getLine(), - "Unary operation not folded into constant"); - return NULL; + if (dotProduct < 0) + resultArray[i].setFConst(unionArrays[0][i].getFConst()); + else + resultArray[i].setFConst(-unionArrays[0][i].getFConst()); } - break; + } + else + UNREACHABLE(); + break; - default: - return NULL; + case EOpRefract: + if (basicType == EbtFloat) + { + // genType refract(genType I, genType N, float eta) : + // For the incident vector I and surface normal N, and the ratio of indices of refraction eta, + // return the refraction vector. The result is computed by + // k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I)) + // if (k < 0.0) + // return genType(0.0) + // else + // return eta * I - (eta * dot(N, I) + sqrt(k)) * N + resultArray = new TConstantUnion[maxObjectSize]; + float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize); + for (size_t i = 0; i < maxObjectSize; i++) + { + float eta = unionArrays[2][i].getFConst(); + float k = 1.0f - eta * eta * (1.0f - dotProduct * dotProduct); + if (k < 0.0f) + resultArray[i].setFConst(0.0f); + else + resultArray[i].setFConst(eta * unionArrays[0][i].getFConst() - + (eta * dotProduct + sqrtf(k)) * unionArrays[1][i].getFConst()); + } } + else + UNREACHABLE(); + break; + + default: + UNREACHABLE(); + // TODO: Add constant folding support for other built-in operations that take 3 parameters and not handled above. + return nullptr; } - newNode = new TIntermConstantUnion(tempConstArray, getType()); - newNode->setLine(getLine()); - return newNode; } + return resultArray; } // static @@ -1209,26 +2606,59 @@ TString TIntermTraverser::hash(const TString &name, ShHashFunction64 hashFunctio void TIntermTraverser::updateTree() { + for (size_t ii = 0; ii < mInsertions.size(); ++ii) + { + const NodeInsertMultipleEntry &insertion = mInsertions[ii]; + ASSERT(insertion.parent); + if (!insertion.insertionsAfter.empty()) + { + bool inserted = insertion.parent->insertChildNodes(insertion.position + 1, + insertion.insertionsAfter); + ASSERT(inserted); + UNUSED_ASSERTION_VARIABLE(inserted); + } + if (!insertion.insertionsBefore.empty()) + { + bool inserted = + insertion.parent->insertChildNodes(insertion.position, insertion.insertionsBefore); + ASSERT(inserted); + UNUSED_ASSERTION_VARIABLE(inserted); + } + } for (size_t ii = 0; ii < mReplacements.size(); ++ii) { - const NodeUpdateEntry& entry = mReplacements[ii]; - ASSERT(entry.parent); - bool replaced = entry.parent->replaceChildNode( - entry.original, entry.replacement); + const NodeUpdateEntry &replacement = mReplacements[ii]; + ASSERT(replacement.parent); + bool replaced = replacement.parent->replaceChildNode( + replacement.original, replacement.replacement); ASSERT(replaced); + UNUSED_ASSERTION_VARIABLE(replaced); - if (!entry.originalBecomesChildOfReplacement) + if (!replacement.originalBecomesChildOfReplacement) { // In AST traversing, a parent is visited before its children. - // After we replace a node, if an immediate child is to + // After we replace a node, if its immediate child is to // be replaced, we need to make sure we don't update the replaced // node; instead, we update the replacement node. for (size_t jj = ii + 1; jj < mReplacements.size(); ++jj) { - NodeUpdateEntry& entry2 = mReplacements[jj]; - if (entry2.parent == entry.original) - entry2.parent = entry.replacement; + NodeUpdateEntry &replacement2 = mReplacements[jj]; + if (replacement2.parent == replacement.original) + replacement2.parent = replacement.replacement; } } } + for (size_t ii = 0; ii < mMultiReplacements.size(); ++ii) + { + const NodeReplaceWithMultipleEntry &replacement = mMultiReplacements[ii]; + ASSERT(replacement.parent); + bool replaced = replacement.parent->replaceChildNodeWithMultiple( + replacement.original, replacement.replacements); + ASSERT(replaced); + UNUSED_ASSERTION_VARIABLE(replaced); + } + + mInsertions.clear(); + mReplacements.clear(); + mMultiReplacements.clear(); } diff --git a/src/3rdparty/angle/src/compiler/translator/IntermNode.h b/src/3rdparty/angle/src/compiler/translator/IntermNode.h index 9f732cbb00..ad500e2b1f 100644 --- a/src/3rdparty/angle/src/compiler/translator/IntermNode.h +++ b/src/3rdparty/angle/src/compiler/translator/IntermNode.h @@ -23,9 +23,9 @@ #include "common/angleutils.h" #include "compiler/translator/Common.h" -#include "compiler/translator/Types.h" #include "compiler/translator/ConstantUnion.h" #include "compiler/translator/Operator.h" +#include "compiler/translator/Types.h" class TIntermTraverser; class TIntermAggregate; @@ -42,10 +42,33 @@ class TInfoSink; class TInfoSinkBase; class TIntermRaw; +class TSymbolTable; + +// Encapsulate an identifier string and track whether it is coming from the original shader code +// (not internal) or from ANGLE (internal). Usually internal names shouldn't be decorated or hashed. +class TName +{ + public: + POOL_ALLOCATOR_NEW_DELETE(); + explicit TName(const TString &name) : mName(name), mIsInternal(false) {} + TName() : mName(), mIsInternal(false) {} + TName(const TName &) = default; + TName &operator=(const TName &) = default; + + const TString &getString() const { return mName; } + void setString(const TString &string) { mName = string; } + bool isInternal() const { return mIsInternal; } + void setInternal(bool isInternal) { mIsInternal = isInternal; } + + private: + TString mName; + bool mIsInternal; +}; + // // Base class for the tree nodes // -class TIntermNode +class TIntermNode : angle::NonCopyable { public: POOL_ALLOCATOR_NEW_DELETE(); @@ -99,7 +122,10 @@ class TIntermTyped : public TIntermNode { public: TIntermTyped(const TType &t) : mType(t) { } - virtual TIntermTyped *getAsTyped() { return this; } + + virtual TIntermTyped *deepCopy() const = 0; + + TIntermTyped *getAsTyped() override { return this; } virtual bool hasSideEffects() const = 0; @@ -123,13 +149,14 @@ class TIntermTyped : public TIntermNode bool isScalar() const { return mType.isScalar(); } bool isScalarInt() const { return mType.isScalarInt(); } const char *getBasicString() const { return mType.getBasicString(); } - const char *getQualifierString() const { return mType.getQualifierString(); } TString getCompleteString() const { return mType.getCompleteString(); } int getArraySize() const { return mType.getArraySize(); } protected: TType mType; + + TIntermTyped(const TIntermTyped &node); }; // @@ -146,25 +173,23 @@ class TIntermLoop : public TIntermNode { public: TIntermLoop(TLoopType type, - TIntermNode *init, TIntermTyped *cond, TIntermTyped *expr, - TIntermNode *body) - : mType(type), - mInit(init), - mCond(cond), - mExpr(expr), - mBody(body), - mUnrollFlag(false) { } + TIntermNode *init, + TIntermTyped *cond, + TIntermTyped *expr, + TIntermAggregate *body) + : mType(type), mInit(init), mCond(cond), mExpr(expr), mBody(body), mUnrollFlag(false) + { + } - virtual TIntermLoop *getAsLoopNode() { return this; } - virtual void traverse(TIntermTraverser *); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); + TIntermLoop *getAsLoopNode() override { return this; } + void traverse(TIntermTraverser *it) override; + bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; TLoopType getType() const { return mType; } TIntermNode *getInit() { return mInit; } TIntermTyped *getCondition() { return mCond; } TIntermTyped *getExpression() { return mExpr; } - TIntermNode *getBody() { return mBody; } + TIntermAggregate *getBody() { return mBody; } void setUnrollFlag(bool flag) { mUnrollFlag = flag; } bool getUnrollFlag() const { return mUnrollFlag; } @@ -174,7 +199,7 @@ class TIntermLoop : public TIntermNode TIntermNode *mInit; // for-loop initialization TIntermTyped *mCond; // loop exit condition TIntermTyped *mExpr; // for-loop expression - TIntermNode *mBody; // loop body + TIntermAggregate *mBody; // loop body bool mUnrollFlag; // Whether the loop should be unrolled or not. }; @@ -189,9 +214,8 @@ class TIntermBranch : public TIntermNode : mFlowOp(op), mExpression(e) { } - virtual void traverse(TIntermTraverser *); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); + void traverse(TIntermTraverser *it) override; + bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; TOperator getFlowOp() { return mFlowOp; } TIntermTyped* getExpression() { return mExpression; } @@ -211,26 +235,32 @@ class TIntermSymbol : public TIntermTyped // If sym comes from per process globalpoolallocator, then it causes increased memory usage // per compile it is essential to use "symbol = sym" to assign to symbol TIntermSymbol(int id, const TString &symbol, const TType &type) - : TIntermTyped(type), - mId(id) + : TIntermTyped(type), mId(id), mSymbol(symbol) { - mSymbol = symbol; } - virtual bool hasSideEffects() const { return false; } + TIntermTyped *deepCopy() const override { return new TIntermSymbol(*this); } + + bool hasSideEffects() const override { return false; } int getId() const { return mId; } - const TString &getSymbol() const { return mSymbol; } + const TString &getSymbol() const { return mSymbol.getString(); } + const TName &getName() const { return mSymbol; } void setId(int newId) { mId = newId; } - virtual void traverse(TIntermTraverser *); - virtual TIntermSymbol *getAsSymbolNode() { return this; } - virtual bool replaceChildNode(TIntermNode *, TIntermNode *) { return false; } + void setInternal(bool internal) { mSymbol.setInternal(internal); } + + void traverse(TIntermTraverser *it) override; + TIntermSymbol *getAsSymbolNode() override { return this; } + bool replaceChildNode(TIntermNode *, TIntermNode *) override { return false; } protected: int mId; - TString mSymbol; + TName mSymbol; + + private: + TIntermSymbol(const TIntermSymbol &) = default; // Note: not deleted, just private! }; // A Raw node stores raw code, that the translator will insert verbatim @@ -242,30 +272,46 @@ class TIntermRaw : public TIntermTyped TIntermRaw(const TType &type, const TString &rawText) : TIntermTyped(type), mRawText(rawText) { } + TIntermRaw(const TIntermRaw &) = delete; + + TIntermTyped *deepCopy() const override + { + UNREACHABLE(); + return nullptr; + } - virtual bool hasSideEffects() const { return false; } + bool hasSideEffects() const override { return false; } TString getRawText() const { return mRawText; } - virtual void traverse(TIntermTraverser *); + void traverse(TIntermTraverser *it) override; - virtual TIntermRaw *getAsRawNode() { return this; } - virtual bool replaceChildNode(TIntermNode *, TIntermNode *) { return false; } + TIntermRaw *getAsRawNode() override { return this; } + bool replaceChildNode(TIntermNode *, TIntermNode *) override { return false; } protected: TString mRawText; }; +// Constant folded node. +// Note that nodes may be constant folded and not be constant expressions with the EvqConst +// qualifier. This happens for example when the following expression is processed: +// "true ? 1.0 : non_constant" +// Other nodes than TIntermConstantUnion may also be constant expressions. +// class TIntermConstantUnion : public TIntermTyped { public: - TIntermConstantUnion(ConstantUnion *unionPointer, const TType &type) - : TIntermTyped(type), - mUnionArrayPointer(unionPointer) { } + TIntermConstantUnion(const TConstantUnion *unionPointer, const TType &type) + : TIntermTyped(type), mUnionArrayPointer(unionPointer) + { + } + + TIntermTyped *deepCopy() const override { return new TIntermConstantUnion(*this); } - virtual bool hasSideEffects() const { return false; } + bool hasSideEffects() const override { return false; } - ConstantUnion *getUnionArrayPointer() const { return mUnionArrayPointer; } + const TConstantUnion *getUnionArrayPointer() const { return mUnionArrayPointer; } int getIConst(size_t index) const { @@ -284,14 +330,33 @@ class TIntermConstantUnion : public TIntermTyped return mUnionArrayPointer ? mUnionArrayPointer[index].getBConst() : false; } - virtual TIntermConstantUnion *getAsConstantUnion() { return this; } - virtual void traverse(TIntermTraverser *); - virtual bool replaceChildNode(TIntermNode *, TIntermNode *) { return false; } + void replaceConstantUnion(const TConstantUnion *safeConstantUnion) + { + // Previous union pointer freed on pool deallocation. + mUnionArrayPointer = safeConstantUnion; + } - TIntermTyped *fold(TOperator, TIntermTyped *, TInfoSink &); + TIntermConstantUnion *getAsConstantUnion() override { return this; } + void traverse(TIntermTraverser *it) override; + bool replaceChildNode(TIntermNode *, TIntermNode *) override { return false; } + + TConstantUnion *foldBinary(TOperator op, TIntermConstantUnion *rightNode, TInfoSink &infoSink); + TConstantUnion *foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink); + TConstantUnion *foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink); + + static TConstantUnion *FoldAggregateConstructor(TIntermAggregate *aggregate, + TInfoSink &infoSink); + static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink); protected: - ConstantUnion *mUnionArrayPointer; + // Same data may be shared between multiple constant unions, so it can't be modified. + const TConstantUnion *mUnionArrayPointer; + + private: + typedef float(*FloatTypeUnaryFunc) (float); + bool foldFloatTypeUnary(const TConstantUnion ¶meter, FloatTypeUnaryFunc builtinFunc, TInfoSink &infoSink, TConstantUnion *result) const; + + TIntermConstantUnion(const TIntermConstantUnion &node); // Note: not deleted, just private! }; // @@ -304,9 +369,10 @@ class TIntermOperator : public TIntermTyped void setOp(TOperator op) { mOp = op; } bool isAssignment() const; + bool isMultiplication() const; bool isConstructor() const; - virtual bool hasSideEffects() const { return isAssignment(); } + bool hasSideEffects() const override { return isAssignment(); } protected: TIntermOperator(TOperator op) @@ -316,6 +382,8 @@ class TIntermOperator : public TIntermTyped : TIntermTyped(type), mOp(op) {} + TIntermOperator(const TIntermOperator &) = default; + TOperator mOp; }; @@ -329,12 +397,13 @@ class TIntermBinary : public TIntermOperator : TIntermOperator(op), mAddIndexClamp(false) {} - virtual TIntermBinary *getAsBinaryNode() { return this; } - virtual void traverse(TIntermTraverser *); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); + TIntermTyped *deepCopy() const override { return new TIntermBinary(*this); } + + TIntermBinary *getAsBinaryNode() override { return this; }; + void traverse(TIntermTraverser *it) override; + bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; - virtual bool hasSideEffects() const + bool hasSideEffects() const override { return isAssignment() || mLeft->hasSideEffects() || mRight->hasSideEffects(); } @@ -344,6 +413,7 @@ class TIntermBinary : public TIntermOperator TIntermTyped *getLeft() const { return mLeft; } TIntermTyped *getRight() const { return mRight; } bool promote(TInfoSink &); + TIntermTyped *fold(TInfoSink &infoSink); void setAddIndexClamp() { mAddIndexClamp = true; } bool getAddIndexClamp() { return mAddIndexClamp; } @@ -354,6 +424,9 @@ class TIntermBinary : public TIntermOperator // If set to true, wrap any EOpIndexIndirect with a clamp to bounds. bool mAddIndexClamp; + + private: + TIntermBinary(const TIntermBinary &node); // Note: not deleted, just private! }; // @@ -371,19 +444,18 @@ class TIntermUnary : public TIntermOperator mOperand(NULL), mUseEmulatedFunction(false) {} - virtual void traverse(TIntermTraverser *); - virtual TIntermUnary *getAsUnaryNode() { return this; } - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); + TIntermTyped *deepCopy() const override { return new TIntermUnary(*this); } - virtual bool hasSideEffects() const - { - return isAssignment() || mOperand->hasSideEffects(); - } + void traverse(TIntermTraverser *it) override; + TIntermUnary *getAsUnaryNode() override { return this; } + bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; + + bool hasSideEffects() const override { return isAssignment() || mOperand->hasSideEffects(); } void setOperand(TIntermTyped *operand) { mOperand = operand; } TIntermTyped *getOperand() { return mOperand; } void promote(const TType *funcReturnType); + TIntermTyped *fold(TInfoSink &infoSink); void setUseEmulatedFunction() { mUseEmulatedFunction = true; } bool getUseEmulatedFunction() { return mUseEmulatedFunction; } @@ -394,6 +466,9 @@ class TIntermUnary : public TIntermOperator // If set to true, replace the built-in function call with an emulated one // to work around driver bugs. bool mUseEmulatedFunction; + + private: + TIntermUnary(const TIntermUnary &node); // note: not deleted, just private! }; typedef TVector TIntermSequence; @@ -408,52 +483,68 @@ class TIntermAggregate : public TIntermOperator TIntermAggregate() : TIntermOperator(EOpNull), mUserDefined(false), - mUseEmulatedFunction(false) { } + mUseEmulatedFunction(false), + mGotPrecisionFromChildren(false) + { + } TIntermAggregate(TOperator op) : TIntermOperator(op), - mUseEmulatedFunction(false) { } + mUseEmulatedFunction(false), + mGotPrecisionFromChildren(false) + { + } ~TIntermAggregate() { } - virtual TIntermAggregate *getAsAggregate() { return this; } - virtual void traverse(TIntermTraverser *); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); + // Note: only supported for nodes that can be a part of an expression. + TIntermTyped *deepCopy() const override { return new TIntermAggregate(*this); } + TIntermAggregate *getAsAggregate() override { return this; } + void traverse(TIntermTraverser *it) override; + bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; + bool replaceChildNodeWithMultiple(TIntermNode *original, TIntermSequence replacements); + bool insertChildNodes(TIntermSequence::size_type position, TIntermSequence insertions); // Conservatively assume function calls and other aggregate operators have side-effects - virtual bool hasSideEffects() const { return true; } + bool hasSideEffects() const override { return true; } + TIntermTyped *fold(TInfoSink &infoSink); TIntermSequence *getSequence() { return &mSequence; } - void setName(const TString &name) { mName = name; } - const TString &getName() const { return mName; } + void setNameObj(const TName &name) { mName = name; } + const TName &getNameObj() const { return mName; } + + void setName(const TString &name) { mName.setString(name); } + const TString &getName() const { return mName.getString(); } void setUserDefined() { mUserDefined = true; } bool isUserDefined() const { return mUserDefined; } - void setOptimize(bool optimize) { mOptimize = optimize; } - bool getOptimize() const { return mOptimize; } - void setDebug(bool debug) { mDebug = debug; } - bool getDebug() const { return mDebug; } + void setFunctionId(int functionId) { mFunctionId = functionId; } + int getFunctionId() const { return mFunctionId; } void setUseEmulatedFunction() { mUseEmulatedFunction = true; } bool getUseEmulatedFunction() { return mUseEmulatedFunction; } + bool areChildrenConstQualified(); void setPrecisionFromChildren(); void setBuiltInFunctionPrecision(); + // Returns true if changing parameter precision may affect the return value. + bool gotPrecisionFromChildren() const { return mGotPrecisionFromChildren; } + protected: - TIntermAggregate(const TIntermAggregate &); // disallow copy constructor - TIntermAggregate &operator=(const TIntermAggregate &); // disallow assignment operator TIntermSequence mSequence; - TString mName; + TName mName; bool mUserDefined; // used for user defined function names - - bool mOptimize; - bool mDebug; + int mFunctionId; // If set to true, replace the built-in function call with an emulated one // to work around driver bugs. bool mUseEmulatedFunction; + + bool mGotPrecisionFromChildren; + + private: + TIntermAggregate(const TIntermAggregate &node); // note: not deleted, just private! }; // @@ -474,23 +565,28 @@ class TIntermSelection : public TIntermTyped mTrueBlock(trueB), mFalseBlock(falseB) {} - virtual void traverse(TIntermTraverser *); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); + // Note: only supported for ternary operator nodes. + TIntermTyped *deepCopy() const override { return new TIntermSelection(*this); } + + void traverse(TIntermTraverser *it) override; + bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override; // Conservatively assume selections have side-effects - virtual bool hasSideEffects() const { return true; } + bool hasSideEffects() const override { return true; } bool usesTernaryOperator() const { return getBasicType() != EbtVoid; } TIntermNode *getCondition() const { return mCondition; } TIntermNode *getTrueBlock() const { return mTrueBlock; } TIntermNode *getFalseBlock() const { return mFalseBlock; } - TIntermSelection *getAsSelectionNode() { return this; } + TIntermSelection *getAsSelectionNode() override { return this; } -protected: + protected: TIntermTyped *mCondition; TIntermNode *mTrueBlock; TIntermNode *mFalseBlock; + + private: + TIntermSelection(const TIntermSelection &node); // Note: not deleted, just private! }; // @@ -512,6 +608,7 @@ class TIntermSwitch : public TIntermNode TIntermSwitch *getAsSwitchNode() override { return this; } + TIntermTyped *getInit() { return mInit; } TIntermAggregate *getStatementList() { return mStatementList; } void setStatementList(TIntermAggregate *statementList) { mStatementList = statementList; } @@ -553,9 +650,12 @@ enum Visit }; // -// For traversing the tree. User should derive from this, -// put their traversal specific data in it, and then pass -// it to a Traverse method. +// For traversing the tree. User should derive from this class overriding the visit functions, +// and then pass an object of the subclass to a traverse method of a node. +// +// The traverse*() functions may also be overridden do other bookkeeping on the tree to provide +// contextual information to the visit functions, such as whether the node is the target of an +// assignment. // // When using this, just fill in the methods for nodes you want visited. // Return false from a pre-visit to skip visiting that node's subtree. @@ -564,31 +664,59 @@ class TIntermTraverser : angle::NonCopyable { public: POOL_ALLOCATOR_NEW_DELETE(); - // TODO(zmo): remove default values. - TIntermTraverser(bool preVisit = true, bool inVisit = false, bool postVisit = false, - bool rightToLeft = false) + TIntermTraverser(bool preVisit, bool inVisit, bool postVisit) : preVisit(preVisit), inVisit(inVisit), postVisit(postVisit), - rightToLeft(rightToLeft), mDepth(0), - mMaxDepth(0) {} + mMaxDepth(0), + mTemporaryIndex(nullptr) + { + } virtual ~TIntermTraverser() {} - virtual void visitSymbol(TIntermSymbol *) {} - virtual void visitRaw(TIntermRaw *) {} - virtual void visitConstantUnion(TIntermConstantUnion *) {} - virtual bool visitBinary(Visit, TIntermBinary *) { return true; } - virtual bool visitUnary(Visit, TIntermUnary *) { return true; } - virtual bool visitSelection(Visit, TIntermSelection *) { return true; } - virtual bool visitSwitch(Visit, TIntermSwitch *) { return true; } - virtual bool visitCase(Visit, TIntermCase *) { return true; } - virtual bool visitAggregate(Visit, TIntermAggregate *) { return true; } - virtual bool visitLoop(Visit, TIntermLoop *) { return true; } - virtual bool visitBranch(Visit, TIntermBranch *) { return true; } + virtual void visitSymbol(TIntermSymbol *node) {} + virtual void visitRaw(TIntermRaw *node) {} + virtual void visitConstantUnion(TIntermConstantUnion *node) {} + virtual bool visitBinary(Visit visit, TIntermBinary *node) { return true; } + virtual bool visitUnary(Visit visit, TIntermUnary *node) { return true; } + virtual bool visitSelection(Visit visit, TIntermSelection *node) { return true; } + virtual bool visitSwitch(Visit visit, TIntermSwitch *node) { return true; } + virtual bool visitCase(Visit visit, TIntermCase *node) { return true; } + virtual bool visitAggregate(Visit visit, TIntermAggregate *node) { return true; } + virtual bool visitLoop(Visit visit, TIntermLoop *node) { return true; } + virtual bool visitBranch(Visit visit, TIntermBranch *node) { return true; } + + // The traverse functions contain logic for iterating over the children of the node + // and calling the visit functions in the appropriate places. They also track some + // context that may be used by the visit functions. + virtual void traverseSymbol(TIntermSymbol *node); + virtual void traverseRaw(TIntermRaw *node); + virtual void traverseConstantUnion(TIntermConstantUnion *node); + virtual void traverseBinary(TIntermBinary *node); + virtual void traverseUnary(TIntermUnary *node); + virtual void traverseSelection(TIntermSelection *node); + virtual void traverseSwitch(TIntermSwitch *node); + virtual void traverseCase(TIntermCase *node); + virtual void traverseAggregate(TIntermAggregate *node); + virtual void traverseLoop(TIntermLoop *node); + virtual void traverseBranch(TIntermBranch *node); int getMaxDepth() const { return mMaxDepth; } + // Return the original name if hash function pointer is NULL; + // otherwise return the hashed name. + static TString hash(const TString &name, ShHashFunction64 hashFunction); + + // If traversers need to replace nodes, they can add the replacements in + // mReplacements/mMultiReplacements during traversal and the user of the traverser should call + // this function after traversal to perform them. + void updateTree(); + + // Start creating temporary symbols from the given temporary symbol index + 1. + void useTemporaryIndex(unsigned int *temporaryIndex); + + protected: void incrementDepth(TIntermNode *current) { mDepth++; @@ -607,27 +735,26 @@ class TIntermTraverser : angle::NonCopyable return mPath.size() == 0 ? NULL : mPath.back(); } - // Return the original name if hash function pointer is NULL; - // otherwise return the hashed name. - static TString hash(const TString& name, ShHashFunction64 hashFunction); + void pushParentBlock(TIntermAggregate *node); + void incrementParentBlockPos(); + void popParentBlock(); + + bool parentNodeIsBlock() + { + return !mParentBlockStack.empty() && getParentNode() == mParentBlockStack.back().node; + } const bool preVisit; const bool inVisit; const bool postVisit; - const bool rightToLeft; - - // If traversers need to replace nodes, they can add the replacements in - // mReplacements during traversal and the user of the traverser should call - // this function after traversal to perform them. - void updateTree(); - protected: int mDepth; int mMaxDepth; // All the nodes from root to the current node's parent during traversing. TVector mPath; + // To replace a single node with another on the parent node struct NodeUpdateEntry { NodeUpdateEntry(TIntermNode *_parent, @@ -645,9 +772,166 @@ class TIntermTraverser : angle::NonCopyable bool originalBecomesChildOfReplacement; }; + // To replace a single node with multiple nodes on the parent aggregate node + struct NodeReplaceWithMultipleEntry + { + NodeReplaceWithMultipleEntry(TIntermAggregate *_parent, TIntermNode *_original, TIntermSequence _replacements) + : parent(_parent), + original(_original), + replacements(_replacements) + { + } + + TIntermAggregate *parent; + TIntermNode *original; + TIntermSequence replacements; + }; + + // To insert multiple nodes on the parent aggregate node + struct NodeInsertMultipleEntry + { + NodeInsertMultipleEntry(TIntermAggregate *_parent, + TIntermSequence::size_type _position, + TIntermSequence _insertionsBefore, + TIntermSequence _insertionsAfter) + : parent(_parent), + position(_position), + insertionsBefore(_insertionsBefore), + insertionsAfter(_insertionsAfter) + { + } + + TIntermAggregate *parent; + TIntermSequence::size_type position; + TIntermSequence insertionsBefore; + TIntermSequence insertionsAfter; + }; + // During traversing, save all the changes that need to happen into - // mReplacements, then do them by calling updateTree(). + // mReplacements/mMultiReplacements, then do them by calling updateTree(). + // Multi replacements are processed after single replacements. std::vector mReplacements; + std::vector mMultiReplacements; + std::vector mInsertions; + + // Helper to insert statements in the parent block (sequence) of the node currently being traversed. + // The statements will be inserted before the node being traversed once updateTree is called. + // Should only be called during PreVisit or PostVisit from sequence nodes. + // Note that inserting more than one set of nodes to the same parent node on a single updateTree call is not + // supported. + void insertStatementsInParentBlock(const TIntermSequence &insertions); + + // Same as above, but supports simultaneous insertion of statements before and after the node + // currently being traversed. + void insertStatementsInParentBlock(const TIntermSequence &insertionsBefore, + const TIntermSequence &insertionsAfter); + + // Helper to create a temporary symbol node with the given qualifier. + TIntermSymbol *createTempSymbol(const TType &type, TQualifier qualifier); + // Helper to create a temporary symbol node. + TIntermSymbol *createTempSymbol(const TType &type); + // Create a node that declares but doesn't initialize a temporary symbol. + TIntermAggregate *createTempDeclaration(const TType &type); + // Create a node that initializes the current temporary symbol with initializer having the given qualifier. + TIntermAggregate *createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier); + // Create a node that initializes the current temporary symbol with initializer. + TIntermAggregate *createTempInitDeclaration(TIntermTyped *initializer); + // Create a node that assigns rightNode to the current temporary symbol. + TIntermBinary *createTempAssignment(TIntermTyped *rightNode); + // Increment temporary symbol index. + void nextTemporaryIndex(); + + private: + struct ParentBlock + { + ParentBlock(TIntermAggregate *nodeIn, TIntermSequence::size_type posIn) + : node(nodeIn), + pos(posIn) + { + } + + TIntermAggregate *node; + TIntermSequence::size_type pos; + }; + // All the code blocks from the root to the current node's parent during traversal. + std::vector mParentBlockStack; + + unsigned int *mTemporaryIndex; +}; + +// Traverser parent class that tracks where a node is a destination of a write operation and so is +// required to be an l-value. +class TLValueTrackingTraverser : public TIntermTraverser +{ + public: + TLValueTrackingTraverser(bool preVisit, + bool inVisit, + bool postVisit, + const TSymbolTable &symbolTable, + int shaderVersion) + : TIntermTraverser(preVisit, inVisit, postVisit), + mOperatorRequiresLValue(false), + mInFunctionCallOutParameter(false), + mSymbolTable(symbolTable), + mShaderVersion(shaderVersion) + { + } + virtual ~TLValueTrackingTraverser() {} + + void traverseBinary(TIntermBinary *node) override; + void traverseUnary(TIntermUnary *node) override; + void traverseAggregate(TIntermAggregate *node) override; + + protected: + bool isLValueRequiredHere() const + { + return mOperatorRequiresLValue || mInFunctionCallOutParameter; + } + + // Return true if the prototype or definition of the function being called has been encountered + // during traversal. + bool isInFunctionMap(const TIntermAggregate *callNode) const; + + private: + // Track whether an l-value is required in the node that is currently being traversed by the + // surrounding operator. + // Use isLValueRequiredHere to check all conditions which require an l-value. + void setOperatorRequiresLValue(bool lValueRequired) + { + mOperatorRequiresLValue = lValueRequired; + } + bool operatorRequiresLValue() const { return mOperatorRequiresLValue; } + + // Add a function encountered during traversal to the function map. + void addToFunctionMap(const TName &name, TIntermSequence *paramSequence); + + // Return the parameters sequence from the function definition or prototype. + TIntermSequence *getFunctionParameters(const TIntermAggregate *callNode); + + // Track whether an l-value is required inside a function call. + void setInFunctionCallOutParameter(bool inOutParameter); + bool isInFunctionCallOutParameter() const; + + bool mOperatorRequiresLValue; + bool mInFunctionCallOutParameter; + + struct TNameComparator + { + bool operator()(const TName &a, const TName &b) const + { + int compareResult = a.getString().compare(b.getString()); + if (compareResult != 0) + return compareResult < 0; + // Internal functions may have same names as non-internal functions. + return !a.isInternal() && b.isInternal(); + } + }; + + // Map from mangled function names to their parameter sequences + TMap mFunctionMap; + + const TSymbolTable &mSymbolTable; + const int mShaderVersion; }; // @@ -659,15 +943,15 @@ class TMaxDepthTraverser : public TIntermTraverser public: POOL_ALLOCATOR_NEW_DELETE(); TMaxDepthTraverser(int depthLimit) - : TIntermTraverser(true, true, false, false), + : TIntermTraverser(true, true, false), mDepthLimit(depthLimit) { } - virtual bool visitBinary(Visit, TIntermBinary *) { return depthCheck(); } - virtual bool visitUnary(Visit, TIntermUnary *) { return depthCheck(); } - virtual bool visitSelection(Visit, TIntermSelection *) { return depthCheck(); } - virtual bool visitAggregate(Visit, TIntermAggregate *) { return depthCheck(); } - virtual bool visitLoop(Visit, TIntermLoop *) { return depthCheck(); } - virtual bool visitBranch(Visit, TIntermBranch *) { return depthCheck(); } + bool visitBinary(Visit, TIntermBinary *) override { return depthCheck(); } + bool visitUnary(Visit, TIntermUnary *) override { return depthCheck(); } + bool visitSelection(Visit, TIntermSelection *) override { return depthCheck(); } + bool visitAggregate(Visit, TIntermAggregate *) override { return depthCheck(); } + bool visitLoop(Visit, TIntermLoop *) override { return depthCheck(); } + bool visitBranch(Visit, TIntermBranch *) override { return depthCheck(); } protected: bool depthCheck() const { return mMaxDepth < mDepthLimit; } diff --git a/src/3rdparty/angle/src/compiler/translator/IntermTraverse.cpp b/src/3rdparty/angle/src/compiler/translator/IntermTraverse.cpp index 7a7efb71f5..7b588ca5a3 100644 --- a/src/3rdparty/angle/src/compiler/translator/IntermTraverse.cpp +++ b/src/3rdparty/angle/src/compiler/translator/IntermTraverse.cpp @@ -5,6 +5,187 @@ // #include "compiler/translator/IntermNode.h" +#include "compiler/translator/InfoSink.h" +#include "compiler/translator/SymbolTable.h" + +void TIntermSymbol::traverse(TIntermTraverser *it) +{ + it->traverseSymbol(this); +} + +void TIntermRaw::traverse(TIntermTraverser *it) +{ + it->traverseRaw(this); +} + +void TIntermConstantUnion::traverse(TIntermTraverser *it) +{ + it->traverseConstantUnion(this); +} + +void TIntermBinary::traverse(TIntermTraverser *it) +{ + it->traverseBinary(this); +} + +void TIntermUnary::traverse(TIntermTraverser *it) +{ + it->traverseUnary(this); +} + +void TIntermSelection::traverse(TIntermTraverser *it) +{ + it->traverseSelection(this); +} + +void TIntermSwitch::traverse(TIntermTraverser *it) +{ + it->traverseSwitch(this); +} + +void TIntermCase::traverse(TIntermTraverser *it) +{ + it->traverseCase(this); +} + +void TIntermAggregate::traverse(TIntermTraverser *it) +{ + it->traverseAggregate(this); +} + +void TIntermLoop::traverse(TIntermTraverser *it) +{ + it->traverseLoop(this); +} + +void TIntermBranch::traverse(TIntermTraverser *it) +{ + it->traverseBranch(this); +} + +void TIntermTraverser::pushParentBlock(TIntermAggregate *node) +{ + mParentBlockStack.push_back(ParentBlock(node, 0)); +} + +void TIntermTraverser::incrementParentBlockPos() +{ + ++mParentBlockStack.back().pos; +} + +void TIntermTraverser::popParentBlock() +{ + ASSERT(!mParentBlockStack.empty()); + mParentBlockStack.pop_back(); +} + +void TIntermTraverser::insertStatementsInParentBlock(const TIntermSequence &insertions) +{ + TIntermSequence emptyInsertionsAfter; + insertStatementsInParentBlock(insertions, emptyInsertionsAfter); +} + +void TIntermTraverser::insertStatementsInParentBlock(const TIntermSequence &insertionsBefore, + const TIntermSequence &insertionsAfter) +{ + ASSERT(!mParentBlockStack.empty()); + NodeInsertMultipleEntry insert(mParentBlockStack.back().node, mParentBlockStack.back().pos, + insertionsBefore, insertionsAfter); + mInsertions.push_back(insert); +} + +TIntermSymbol *TIntermTraverser::createTempSymbol(const TType &type, TQualifier qualifier) +{ + // Each traversal uses at most one temporary variable, so the index stays the same within a single traversal. + TInfoSinkBase symbolNameOut; + ASSERT(mTemporaryIndex != nullptr); + symbolNameOut << "s" << (*mTemporaryIndex); + TString symbolName = symbolNameOut.c_str(); + + TIntermSymbol *node = new TIntermSymbol(0, symbolName, type); + node->setInternal(true); + node->getTypePointer()->setQualifier(qualifier); + return node; +} + +TIntermSymbol *TIntermTraverser::createTempSymbol(const TType &type) +{ + return createTempSymbol(type, EvqTemporary); +} + +TIntermAggregate *TIntermTraverser::createTempDeclaration(const TType &type) +{ + TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration); + tempDeclaration->getSequence()->push_back(createTempSymbol(type)); + return tempDeclaration; +} + +TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier) +{ + ASSERT(initializer != nullptr); + TIntermSymbol *tempSymbol = createTempSymbol(initializer->getType(), qualifier); + TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration); + TIntermBinary *tempInit = new TIntermBinary(EOpInitialize); + tempInit->setLeft(tempSymbol); + tempInit->setRight(initializer); + tempInit->setType(tempSymbol->getType()); + tempDeclaration->getSequence()->push_back(tempInit); + return tempDeclaration; +} + +TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer) +{ + return createTempInitDeclaration(initializer, EvqTemporary); +} + +TIntermBinary *TIntermTraverser::createTempAssignment(TIntermTyped *rightNode) +{ + ASSERT(rightNode != nullptr); + TIntermSymbol *tempSymbol = createTempSymbol(rightNode->getType()); + TIntermBinary *assignment = new TIntermBinary(EOpAssign); + assignment->setLeft(tempSymbol); + assignment->setRight(rightNode); + assignment->setType(tempSymbol->getType()); + return assignment; +} + +void TIntermTraverser::useTemporaryIndex(unsigned int *temporaryIndex) +{ + mTemporaryIndex = temporaryIndex; +} + +void TIntermTraverser::nextTemporaryIndex() +{ + ASSERT(mTemporaryIndex != nullptr); + ++(*mTemporaryIndex); +} + +void TLValueTrackingTraverser::addToFunctionMap(const TName &name, TIntermSequence *paramSequence) +{ + mFunctionMap[name] = paramSequence; +} + +bool TLValueTrackingTraverser::isInFunctionMap(const TIntermAggregate *callNode) const +{ + ASSERT(callNode->getOp() == EOpFunctionCall); + return (mFunctionMap.find(callNode->getNameObj()) != mFunctionMap.end()); +} + +TIntermSequence *TLValueTrackingTraverser::getFunctionParameters(const TIntermAggregate *callNode) +{ + ASSERT(isInFunctionMap(callNode)); + return mFunctionMap[callNode->getNameObj()]; +} + +void TLValueTrackingTraverser::setInFunctionCallOutParameter(bool inOutParameter) +{ + mInFunctionCallOutParameter = inOutParameter; +} + +bool TLValueTrackingTraverser::isInFunctionCallOutParameter() const +{ + return mInFunctionCallOutParameter; +} // // Traverse the intermediate representation tree, and @@ -16,308 +197,473 @@ // if preVisit is turned on and the type specific function // returns false. // -// preVisit, postVisit, and rightToLeft control what order -// nodes are visited in. -// // // Traversal functions for terminals are straighforward.... // -void TIntermSymbol::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseSymbol(TIntermSymbol *node) { - it->visitSymbol(this); + visitSymbol(node); } -void TIntermConstantUnion::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseConstantUnion(TIntermConstantUnion *node) { - it->visitConstantUnion(this); + visitConstantUnion(node); } // // Traverse a binary node. // -void TIntermBinary::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseBinary(TIntermBinary *node) { bool visit = true; // // visit the node before children if pre-visiting. // - if (it->preVisit) - visit = it->visitBinary(PreVisit, this); + if (preVisit) + visit = visitBinary(PreVisit, node); // // Visit the children, in the right order. // if (visit) { - it->incrementDepth(this); + incrementDepth(node); - if (it->rightToLeft) - { - if (mRight) - mRight->traverse(it); + if (node->getLeft()) + node->getLeft()->traverse(this); - if (it->inVisit) - visit = it->visitBinary(InVisit, this); + if (inVisit) + visit = visitBinary(InVisit, node); - if (visit && mLeft) - mLeft->traverse(it); - } - else + if (visit && node->getRight()) + node->getRight()->traverse(this); + + decrementDepth(); + } + + // + // Visit the node after the children, if requested and the traversal + // hasn't been cancelled yet. + // + if (visit && postVisit) + visitBinary(PostVisit, node); +} + +void TLValueTrackingTraverser::traverseBinary(TIntermBinary *node) +{ + bool visit = true; + + // + // visit the node before children if pre-visiting. + // + if (preVisit) + visit = visitBinary(PreVisit, node); + + // + // Visit the children, in the right order. + // + if (visit) + { + incrementDepth(node); + + // Some binary operations like indexing can be inside an expression which must be an + // l-value. + bool parentOperatorRequiresLValue = operatorRequiresLValue(); + bool parentInFunctionCallOutParameter = isInFunctionCallOutParameter(); + if (node->isAssignment()) { - if (mLeft) - mLeft->traverse(it); + ASSERT(!isLValueRequiredHere()); + setOperatorRequiresLValue(true); + } - if (it->inVisit) - visit = it->visitBinary(InVisit, this); + if (node->getLeft()) + node->getLeft()->traverse(this); - if (visit && mRight) - mRight->traverse(it); + if (inVisit) + visit = visitBinary(InVisit, node); + + if (node->isAssignment()) + setOperatorRequiresLValue(false); + + // Index is not required to be an l-value even when the surrounding expression is required + // to be an l-value. + TOperator op = node->getOp(); + if (op == EOpIndexDirect || op == EOpIndexDirectInterfaceBlock || + op == EOpIndexDirectStruct || op == EOpIndexIndirect) + { + setOperatorRequiresLValue(false); + setInFunctionCallOutParameter(false); } - it->decrementDepth(); + if (visit && node->getRight()) + node->getRight()->traverse(this); + + setOperatorRequiresLValue(parentOperatorRequiresLValue); + setInFunctionCallOutParameter(parentInFunctionCallOutParameter); + + decrementDepth(); } // // Visit the node after the children, if requested and the traversal // hasn't been cancelled yet. // - if (visit && it->postVisit) - it->visitBinary(PostVisit, this); + if (visit && postVisit) + visitBinary(PostVisit, node); } // // Traverse a unary node. Same comments in binary node apply here. // -void TIntermUnary::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseUnary(TIntermUnary *node) { bool visit = true; - if (it->preVisit) - visit = it->visitUnary(PreVisit, this); + if (preVisit) + visit = visitUnary(PreVisit, node); + + if (visit) + { + incrementDepth(node); - if (visit) { - it->incrementDepth(this); - mOperand->traverse(it); - it->decrementDepth(); + node->getOperand()->traverse(this); + + decrementDepth(); } - if (visit && it->postVisit) - it->visitUnary(PostVisit, this); + if (visit && postVisit) + visitUnary(PostVisit, node); +} + +void TLValueTrackingTraverser::traverseUnary(TIntermUnary *node) +{ + bool visit = true; + + if (preVisit) + visit = visitUnary(PreVisit, node); + + if (visit) + { + incrementDepth(node); + + ASSERT(!operatorRequiresLValue()); + switch (node->getOp()) + { + case EOpPostIncrement: + case EOpPostDecrement: + case EOpPreIncrement: + case EOpPreDecrement: + setOperatorRequiresLValue(true); + break; + default: + break; + } + + node->getOperand()->traverse(this); + + setOperatorRequiresLValue(false); + + decrementDepth(); + } + + if (visit && postVisit) + visitUnary(PostVisit, node); } // // Traverse an aggregate node. Same comments in binary node apply here. // -void TIntermAggregate::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseAggregate(TIntermAggregate *node) { bool visit = true; - if (it->preVisit) - visit = it->visitAggregate(PreVisit, this); + TIntermSequence *sequence = node->getSequence(); + + if (preVisit) + visit = visitAggregate(PreVisit, node); if (visit) { - it->incrementDepth(this); + incrementDepth(node); - if (it->rightToLeft) + if (node->getOp() == EOpSequence) + pushParentBlock(node); + + for (auto *child : *sequence) { - for (TIntermSequence::reverse_iterator sit = mSequence.rbegin(); - sit != mSequence.rend(); sit++) + child->traverse(this); + if (visit && inVisit) { - (*sit)->traverse(it); + if (child != sequence->back()) + visit = visitAggregate(InVisit, node); + } + + if (node->getOp() == EOpSequence) + incrementParentBlockPos(); + } + + if (node->getOp() == EOpSequence) + popParentBlock(); + + decrementDepth(); + } - if (visit && it->inVisit) + if (visit && postVisit) + visitAggregate(PostVisit, node); +} + +void TLValueTrackingTraverser::traverseAggregate(TIntermAggregate *node) +{ + bool visit = true; + + TIntermSequence *sequence = node->getSequence(); + switch (node->getOp()) + { + case EOpFunction: + { + TIntermAggregate *params = sequence->front()->getAsAggregate(); + ASSERT(params != nullptr); + ASSERT(params->getOp() == EOpParameters); + addToFunctionMap(node->getNameObj(), params->getSequence()); + break; + } + case EOpPrototype: + addToFunctionMap(node->getNameObj(), sequence); + break; + default: + break; + } + + if (preVisit) + visit = visitAggregate(PreVisit, node); + + if (visit) + { + bool inFunctionMap = false; + if (node->getOp() == EOpFunctionCall) + { + inFunctionMap = isInFunctionMap(node); + if (!inFunctionMap) + { + // The function is not user-defined - it is likely built-in texture function. + // Assume that those do not have out parameters. + setInFunctionCallOutParameter(false); + } + } + + incrementDepth(node); + + if (inFunctionMap) + { + TIntermSequence *params = getFunctionParameters(node); + TIntermSequence::iterator paramIter = params->begin(); + for (auto *child : *sequence) + { + ASSERT(paramIter != params->end()); + TQualifier qualifier = (*paramIter)->getAsTyped()->getQualifier(); + setInFunctionCallOutParameter(qualifier == EvqOut || qualifier == EvqInOut); + + child->traverse(this); + if (visit && inVisit) { - if (*sit != mSequence.front()) - visit = it->visitAggregate(InVisit, this); + if (child != sequence->back()) + visit = visitAggregate(InVisit, node); } + + ++paramIter; } + + setInFunctionCallOutParameter(false); } else { - for (TIntermSequence::iterator sit = mSequence.begin(); - sit != mSequence.end(); sit++) + if (node->getOp() == EOpSequence) + pushParentBlock(node); + + // Find the built-in function corresponding to this op so that we can determine the + // in/out qualifiers of its parameters. + TFunction *builtInFunc = nullptr; + TString opString = GetOperatorString(node->getOp()); + if (!node->isConstructor() && !opString.empty()) + { + // The return type doesn't affect the mangled name of the function, which is used + // to look it up from the symbol table. + TType dummyReturnType; + TFunction call(&opString, &dummyReturnType, node->getOp()); + for (auto *child : *sequence) + { + TType *paramType = child->getAsTyped()->getTypePointer(); + TConstParameter p(paramType); + call.addParameter(p); + } + + TSymbol *sym = mSymbolTable.findBuiltIn(call.getMangledName(), mShaderVersion); + if (sym != nullptr && sym->isFunction()) + { + builtInFunc = static_cast(sym); + ASSERT(builtInFunc->getParamCount() == sequence->size()); + } + } + + size_t paramIndex = 0; + + for (auto *child : *sequence) { - (*sit)->traverse(it); + TQualifier qualifier = EvqIn; + if (builtInFunc != nullptr) + qualifier = builtInFunc->getParam(paramIndex).type->getQualifier(); + setInFunctionCallOutParameter(qualifier == EvqOut || qualifier == EvqInOut); + child->traverse(this); - if (visit && it->inVisit) + if (visit && inVisit) { - if (*sit != mSequence.back()) - visit = it->visitAggregate(InVisit, this); + if (child != sequence->back()) + visit = visitAggregate(InVisit, node); } + + if (node->getOp() == EOpSequence) + incrementParentBlockPos(); + + ++paramIndex; } + + setInFunctionCallOutParameter(false); + + if (node->getOp() == EOpSequence) + popParentBlock(); } - it->decrementDepth(); + decrementDepth(); } - if (visit && it->postVisit) - it->visitAggregate(PostVisit, this); + if (visit && postVisit) + visitAggregate(PostVisit, node); } // // Traverse a selection node. Same comments in binary node apply here. // -void TIntermSelection::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseSelection(TIntermSelection *node) { bool visit = true; - if (it->preVisit) - visit = it->visitSelection(PreVisit, this); + if (preVisit) + visit = visitSelection(PreVisit, node); if (visit) { - it->incrementDepth(this); - if (it->rightToLeft) - { - if (mFalseBlock) - mFalseBlock->traverse(it); - if (mTrueBlock) - mTrueBlock->traverse(it); - mCondition->traverse(it); - } - else - { - mCondition->traverse(it); - if (mTrueBlock) - mTrueBlock->traverse(it); - if (mFalseBlock) - mFalseBlock->traverse(it); - } - it->decrementDepth(); + incrementDepth(node); + node->getCondition()->traverse(this); + if (node->getTrueBlock()) + node->getTrueBlock()->traverse(this); + if (node->getFalseBlock()) + node->getFalseBlock()->traverse(this); + decrementDepth(); } - if (visit && it->postVisit) - it->visitSelection(PostVisit, this); + if (visit && postVisit) + visitSelection(PostVisit, node); } // // Traverse a switch node. Same comments in binary node apply here. // -void TIntermSwitch::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseSwitch(TIntermSwitch *node) { bool visit = true; - if (it->preVisit) - visit = it->visitSwitch(PreVisit, this); + if (preVisit) + visit = visitSwitch(PreVisit, node); if (visit) { - it->incrementDepth(this); - if (it->rightToLeft) - { - if (mStatementList) - mStatementList->traverse(it); - if (it->inVisit) - visit = it->visitSwitch(InVisit, this); - if (visit) - mInit->traverse(it); - } - else - { - mInit->traverse(it); - if (it->inVisit) - visit = it->visitSwitch(InVisit, this); - if (visit && mStatementList) - mStatementList->traverse(it); - } - it->decrementDepth(); + incrementDepth(node); + node->getInit()->traverse(this); + if (inVisit) + visit = visitSwitch(InVisit, node); + if (visit && node->getStatementList()) + node->getStatementList()->traverse(this); + decrementDepth(); } - if (visit && it->postVisit) - it->visitSwitch(PostVisit, this); + if (visit && postVisit) + visitSwitch(PostVisit, node); } // -// Traverse a switch node. Same comments in binary node apply here. +// Traverse a case node. Same comments in binary node apply here. // -void TIntermCase::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseCase(TIntermCase *node) { bool visit = true; - if (it->preVisit) - visit = it->visitCase(PreVisit, this); + if (preVisit) + visit = visitCase(PreVisit, node); - if (visit && mCondition) - mCondition->traverse(it); + if (visit && node->getCondition()) + node->getCondition()->traverse(this); - if (visit && it->postVisit) - it->visitCase(PostVisit, this); + if (visit && postVisit) + visitCase(PostVisit, node); } // // Traverse a loop node. Same comments in binary node apply here. // -void TIntermLoop::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseLoop(TIntermLoop *node) { bool visit = true; - if (it->preVisit) - visit = it->visitLoop(PreVisit, this); + if (preVisit) + visit = visitLoop(PreVisit, node); if (visit) { - it->incrementDepth(this); - - if (it->rightToLeft) - { - if (mExpr) - mExpr->traverse(it); - - if (mBody) - mBody->traverse(it); - - if (mCond) - mCond->traverse(it); + incrementDepth(node); - if (mInit) - mInit->traverse(it); - } - else - { - if (mInit) - mInit->traverse(it); + if (node->getInit()) + node->getInit()->traverse(this); - if (mCond) - mCond->traverse(it); + if (node->getCondition()) + node->getCondition()->traverse(this); - if (mBody) - mBody->traverse(it); + if (node->getBody()) + node->getBody()->traverse(this); - if (mExpr) - mExpr->traverse(it); - } + if (node->getExpression()) + node->getExpression()->traverse(this); - it->decrementDepth(); + decrementDepth(); } - if (visit && it->postVisit) - it->visitLoop(PostVisit, this); + if (visit && postVisit) + visitLoop(PostVisit, node); } // // Traverse a branch node. Same comments in binary node apply here. // -void TIntermBranch::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseBranch(TIntermBranch *node) { bool visit = true; - if (it->preVisit) - visit = it->visitBranch(PreVisit, this); + if (preVisit) + visit = visitBranch(PreVisit, node); - if (visit && mExpression) { - it->incrementDepth(this); - mExpression->traverse(it); - it->decrementDepth(); + if (visit && node->getExpression()) + { + incrementDepth(node); + node->getExpression()->traverse(this); + decrementDepth(); } - if (visit && it->postVisit) - it->visitBranch(PostVisit, this); + if (visit && postVisit) + visitBranch(PostVisit, node); } -void TIntermRaw::traverse(TIntermTraverser *it) +void TIntermTraverser::traverseRaw(TIntermRaw *node) { - it->visitRaw(this); + visitRaw(node); } diff --git a/src/3rdparty/angle/src/compiler/translator/Intermediate.cpp b/src/3rdparty/angle/src/compiler/translator/Intermediate.cpp index 320056f8ce..0adb7212b7 100644 --- a/src/3rdparty/angle/src/compiler/translator/Intermediate.cpp +++ b/src/3rdparty/angle/src/compiler/translator/Intermediate.cpp @@ -57,19 +57,10 @@ TIntermTyped *TIntermediate::addBinaryMath( if (!node->promote(mInfoSink)) return NULL; - // // See if we can fold constants. - // - TIntermConstantUnion *leftTempConstant = left->getAsConstantUnion(); - TIntermConstantUnion *rightTempConstant = right->getAsConstantUnion(); - if (leftTempConstant && rightTempConstant) - { - TIntermTyped *typedReturnNode = - leftTempConstant->fold(node->getOp(), rightTempConstant, mInfoSink); - - if (typedReturnNode) - return typedReturnNode; - } + TIntermTyped *foldedNode = node->fold(mInfoSink); + if (foldedNode) + return foldedNode; return node; } @@ -129,10 +120,6 @@ TIntermTyped *TIntermediate::addIndex( TIntermTyped *TIntermediate::addUnaryMath( TOperator op, TIntermTyped *child, const TSourceLoc &line, const TType *funcReturnType) { - TIntermConstantUnion *childTempConstant = 0; - if (child->getAsConstantUnion()) - childTempConstant = child->getAsConstantUnion(); - // // Make a new node for the operator. // @@ -141,13 +128,9 @@ TIntermTyped *TIntermediate::addUnaryMath( node->setOperand(child); node->promote(funcReturnType); - if (childTempConstant) - { - TIntermTyped *newChild = childTempConstant->fold(op, 0, mInfoSink); - - if (newChild) - return newChild; - } + TIntermTyped *foldedNode = node->fold(mInfoSink); + if (foldedNode) + return foldedNode; return node; } @@ -246,6 +229,22 @@ TIntermAggregate *TIntermediate::makeAggregate( return aggNode; } +// If the input node is nullptr, return nullptr. +// If the input node is a sequence (block) node, return it. +// If the input node is not a sequence node, put it inside a sequence node and return that. +TIntermAggregate *TIntermediate::ensureSequence(TIntermNode *node) +{ + if (node == nullptr) + return nullptr; + TIntermAggregate *aggNode = node->getAsAggregate(); + if (aggNode != nullptr && aggNode->getOp() == EOpSequence) + return aggNode; + + aggNode = makeAggregate(node, node->getLine()); + aggNode->setOp(EOpSequence); + return aggNode; +} + // // For "if" test nodes. There are three children; a condition, // a true path, and a false path. The two paths are in the @@ -261,7 +260,7 @@ TIntermNode *TIntermediate::addSelection( // test now. // - if (cond->getAsTyped() && cond->getAsTyped()->getAsConstantUnion()) + if (cond->getAsConstantUnion()) { if (cond->getAsConstantUnion()->getBConst(0) == true) { @@ -276,28 +275,38 @@ TIntermNode *TIntermediate::addSelection( } TIntermSelection *node = new TIntermSelection( - cond, nodePair.node1, nodePair.node2); + cond, ensureSequence(nodePair.node1), ensureSequence(nodePair.node2)); node->setLine(line); return node; } -TIntermTyped *TIntermediate::addComma( - TIntermTyped *left, TIntermTyped *right, const TSourceLoc &line) +TIntermTyped *TIntermediate::addComma(TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &line, + int shaderVersion) { - if (left->getType().getQualifier() == EvqConst && - right->getType().getQualifier() == EvqConst) + TQualifier resultQualifier = EvqConst; + // ESSL3.00 section 12.43: The result of a sequence operator is not a constant-expression. + if (shaderVersion >= 300 || left->getQualifier() != EvqConst || + right->getQualifier() != EvqConst) { - return right; + resultQualifier = EvqTemporary; + } + + TIntermTyped *commaNode = nullptr; + if (!left->hasSideEffects()) + { + commaNode = right; } else { - TIntermTyped *commaAggregate = growAggregate(left, right, line); - commaAggregate->getAsAggregate()->setOp(EOpComma); - commaAggregate->setType(right->getType()); - commaAggregate->getTypePointer()->setQualifier(EvqTemporary); - return commaAggregate; + commaNode = growAggregate(left, right, line); + commaNode->getAsAggregate()->setOp(EOpComma); + commaNode->setType(right->getType()); } + commaNode->getTypePointer()->setQualifier(resultQualifier); + return commaNode; } // @@ -305,38 +314,38 @@ TIntermTyped *TIntermediate::addComma( // a true path, and a false path. The two paths are specified // as separate parameters. // -// Returns the selection node created, or 0 if one could not be. +// Returns the selection node created, or one of trueBlock and falseBlock if the expression could be folded. // -TIntermTyped *TIntermediate::addSelection( - TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, - const TSourceLoc &line) +TIntermTyped *TIntermediate::addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, + const TSourceLoc &line) { - if (!cond || !trueBlock || !falseBlock || - trueBlock->getType() != falseBlock->getType()) + TQualifier resultQualifier = EvqTemporary; + if (cond->getQualifier() == EvqConst && trueBlock->getQualifier() == EvqConst && + falseBlock->getQualifier() == EvqConst) { - return NULL; + resultQualifier = EvqConst; } - - // - // See if all the operands are constant, then fold it otherwise not. - // - - if (cond->getAsConstantUnion() && - trueBlock->getAsConstantUnion() && - falseBlock->getAsConstantUnion()) + // Note that the node resulting from here can be a constant union without being qualified as + // constant. + if (cond->getAsConstantUnion()) { if (cond->getAsConstantUnion()->getBConst(0)) + { + trueBlock->getTypePointer()->setQualifier(resultQualifier); return trueBlock; + } else + { + falseBlock->getTypePointer()->setQualifier(resultQualifier); return falseBlock; + } } // // Make a selection node. // - TIntermSelection *node = new TIntermSelection( - cond, trueBlock, falseBlock, trueBlock->getType()); - node->getTypePointer()->setQualifier(EvqTemporary); + TIntermSelection *node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType()); + node->getTypePointer()->setQualifier(resultQualifier); node->setLine(line); return node; @@ -366,10 +375,11 @@ TIntermCase *TIntermediate::addCase( // Returns the constant union node created. // -TIntermConstantUnion *TIntermediate::addConstantUnion( - ConstantUnion *unionArrayPointer, const TType &t, const TSourceLoc &line) +TIntermConstantUnion *TIntermediate::addConstantUnion(const TConstantUnion *constantUnion, + const TType &type, + const TSourceLoc &line) { - TIntermConstantUnion *node = new TIntermConstantUnion(unionArrayPointer, t); + TIntermConstantUnion *node = new TIntermConstantUnion(constantUnion, type); node->setLine(line); return node; @@ -384,11 +394,11 @@ TIntermTyped *TIntermediate::addSwizzle( node->setLine(line); TIntermConstantUnion *constIntNode; TIntermSequence *sequenceVector = node->getSequence(); - ConstantUnion *unionArray; + TConstantUnion *unionArray; for (int i = 0; i < fields.num; i++) { - unionArray = new ConstantUnion[1]; + unionArray = new TConstantUnion[1]; unionArray->setIConst(fields.offsets[i]); constIntNode = addConstantUnion( unionArray, TType(EbtInt, EbpUndefined, EvqConst), line); @@ -405,7 +415,7 @@ TIntermNode *TIntermediate::addLoop( TLoopType type, TIntermNode *init, TIntermTyped *cond, TIntermTyped *expr, TIntermNode *body, const TSourceLoc &line) { - TIntermNode *node = new TIntermLoop(type, init, cond, expr, body); + TIntermNode *node = new TIntermLoop(type, init, cond, expr, ensureSequence(body)); node->setLine(line); return node; @@ -433,17 +443,66 @@ TIntermBranch* TIntermediate::addBranch( // This is to be executed once the final root is put on top by the parsing // process. // -bool TIntermediate::postProcess(TIntermNode *root) +TIntermAggregate *TIntermediate::postProcess(TIntermNode *root) { - if (root == NULL) - return true; + if (root == nullptr) + return nullptr; // - // First, finish off the top level sequence, if any + // Finish off the top level sequence, if any // TIntermAggregate *aggRoot = root->getAsAggregate(); - if (aggRoot && aggRoot->getOp() == EOpNull) + if (aggRoot != nullptr && aggRoot->getOp() == EOpNull) + { aggRoot->setOp(EOpSequence); + } + else if (aggRoot == nullptr || aggRoot->getOp() != EOpSequence) + { + aggRoot = new TIntermAggregate(EOpSequence); + aggRoot->setLine(root->getLine()); + aggRoot->getSequence()->push_back(root); + } + + return aggRoot; +} + +TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate) +{ + switch (aggregate->getOp()) + { + case EOpAtan: + case EOpPow: + case EOpMod: + case EOpMin: + case EOpMax: + case EOpClamp: + case EOpMix: + case EOpStep: + case EOpSmoothStep: + case EOpMul: + case EOpOuterProduct: + case EOpLessThan: + case EOpLessThanEqual: + case EOpGreaterThan: + case EOpGreaterThanEqual: + case EOpVectorEqual: + case EOpVectorNotEqual: + case EOpDistance: + case EOpDot: + case EOpCross: + case EOpFaceForward: + case EOpReflect: + case EOpRefract: + return aggregate->fold(mInfoSink); + default: + // TODO: Add support for folding array constructors + if (aggregate->isConstructor() && !aggregate->isArray()) + { + return aggregate->fold(mInfoSink); + } + // Constant folding not supported for the built-in. + return nullptr; + } - return true; + return nullptr; } diff --git a/src/3rdparty/angle/src/compiler/translator/Intermediate.h b/src/3rdparty/angle/src/compiler/translator/Intermediate.h index ec73e22834..f723fc7648 100644 --- a/src/3rdparty/angle/src/compiler/translator/Intermediate.h +++ b/src/3rdparty/angle/src/compiler/translator/Intermediate.h @@ -39,29 +39,33 @@ class TIntermediate TIntermAggregate *growAggregate( TIntermNode *left, TIntermNode *right, const TSourceLoc &); TIntermAggregate *makeAggregate(TIntermNode *node, const TSourceLoc &); + TIntermAggregate *ensureSequence(TIntermNode *node); TIntermAggregate *setAggregateOperator(TIntermNode *, TOperator, const TSourceLoc &); TIntermNode *addSelection(TIntermTyped *cond, TIntermNodePair code, const TSourceLoc &); - TIntermTyped *addSelection( - TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, const TSourceLoc &); + TIntermTyped *addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, + const TSourceLoc &line); TIntermSwitch *addSwitch( TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &line); TIntermCase *addCase( TIntermTyped *condition, const TSourceLoc &line); - TIntermTyped *addComma( - TIntermTyped *left, TIntermTyped *right, const TSourceLoc &); - TIntermConstantUnion *addConstantUnion(ConstantUnion *, const TType &, const TSourceLoc &); - // TODO(zmo): Get rid of default value. - bool parseConstTree(const TSourceLoc &, TIntermNode *, ConstantUnion *, - TOperator, TType, bool singleConstantParam = false); + TIntermTyped *addComma(TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &line, + int shaderVersion); + TIntermConstantUnion *addConstantUnion(const TConstantUnion *constantUnion, + const TType &type, + const TSourceLoc &line); TIntermNode *addLoop(TLoopType, TIntermNode *, TIntermTyped *, TIntermTyped *, TIntermNode *, const TSourceLoc &); TIntermBranch *addBranch(TOperator, const TSourceLoc &); TIntermBranch *addBranch(TOperator, TIntermTyped *, const TSourceLoc &); TIntermTyped *addSwizzle(TVectorFields &, const TSourceLoc &); - bool postProcess(TIntermNode *); + TIntermAggregate *postProcess(TIntermNode *root); static void outputTree(TIntermNode *, TInfoSinkBase &); + TIntermTyped *foldAggregateBuiltIn(TIntermAggregate *aggregate); + private: void operator=(TIntermediate &); // prevent assignments diff --git a/src/3rdparty/angle/src/compiler/translator/NodeSearch.h b/src/3rdparty/angle/src/compiler/translator/NodeSearch.h index 8ffed614c3..b13b1baabb 100644 --- a/src/3rdparty/angle/src/compiler/translator/NodeSearch.h +++ b/src/3rdparty/angle/src/compiler/translator/NodeSearch.h @@ -19,7 +19,8 @@ class NodeSearchTraverser : public TIntermTraverser { public: NodeSearchTraverser() - : mFound(false) + : TIntermTraverser(true, false, false), + mFound(false) {} bool found() const { return mFound; } @@ -53,28 +54,6 @@ class FindDiscard : public NodeSearchTraverser } }; -class FindSideEffectRewriting : public NodeSearchTraverser -{ - public: - virtual bool visitBinary(Visit visit, TIntermBinary *node) - { - switch (node->getOp()) - { - case EOpLogicalOr: - case EOpLogicalAnd: - if (node->getRight()->hasSideEffects()) - { - mFound = true; - } - break; - - default: break; - } - - return !mFound; - } -}; - } #endif // COMPILER_TRANSLATOR_NODESEARCH_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/Operator.cpp b/src/3rdparty/angle/src/compiler/translator/Operator.cpp index ae4512bd44..20e47f290e 100644 --- a/src/3rdparty/angle/src/compiler/translator/Operator.cpp +++ b/src/3rdparty/angle/src/compiler/translator/Operator.cpp @@ -164,7 +164,13 @@ const char *GetOperatorString(TOperator op) case EOpConstructUVec3: return "uvec3"; case EOpConstructUVec4: return "uvec4"; case EOpConstructMat2: return "mat2"; + case EOpConstructMat2x3: return "mat2x3"; + case EOpConstructMat2x4: return "mat2x4"; + case EOpConstructMat3x2: return "mat3x2"; case EOpConstructMat3: return "mat3"; + case EOpConstructMat3x4: return "mat3x4"; + case EOpConstructMat4x2: return "mat4x2"; + case EOpConstructMat4x3: return "mat4x3"; case EOpConstructMat4: return "mat4"; // Note: EOpConstructStruct can't be handled here diff --git a/src/3rdparty/angle/src/compiler/translator/Operator.h b/src/3rdparty/angle/src/compiler/translator/Operator.h index 8290f952fc..b0efb8f48b 100644 --- a/src/3rdparty/angle/src/compiler/translator/Operator.h +++ b/src/3rdparty/angle/src/compiler/translator/Operator.h @@ -15,7 +15,6 @@ enum TOperator EOpNull, // if in a node, should only mean a node is still being built EOpSequence, // denotes a list of statements, or parameters, etc. EOpFunctionCall, - EOpInternalFunctionCall, // Call to an internal helper function EOpFunction, // For function definition EOpParameters, // an aggregate listing the parameters to a function @@ -192,7 +191,13 @@ enum TOperator EOpConstructUVec3, EOpConstructUVec4, EOpConstructMat2, + EOpConstructMat2x3, + EOpConstructMat2x4, + EOpConstructMat3x2, EOpConstructMat3, + EOpConstructMat3x4, + EOpConstructMat4x2, + EOpConstructMat4x3, EOpConstructMat4, EOpConstructStruct, diff --git a/src/3rdparty/angle/src/compiler/translator/OutputESSL.h b/src/3rdparty/angle/src/compiler/translator/OutputESSL.h index 813f1e944b..c5a963499e 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputESSL.h +++ b/src/3rdparty/angle/src/compiler/translator/OutputESSL.h @@ -21,7 +21,8 @@ public: bool forceHighp); protected: - virtual bool writeVariablePrecision(TPrecision precision); + bool writeVariablePrecision(TPrecision precision) override; + private: bool mForceHighp; }; diff --git a/src/3rdparty/angle/src/compiler/translator/OutputGLSL.cpp b/src/3rdparty/angle/src/compiler/translator/OutputGLSL.cpp index 9badf0e2fc..431425020a 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputGLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/OutputGLSL.cpp @@ -37,14 +37,22 @@ void TOutputGLSL::visitSymbol(TIntermSymbol *node) { out << "gl_FragDepth"; } - else if (symbol == "gl_FragColor" && getShaderOutput() == SH_GLSL_CORE_OUTPUT) + else if (symbol == "gl_FragColor" && IsGLSL130OrNewer(getShaderOutput())) { out << "webgl_FragColor"; } - else if (symbol == "gl_FragData" && getShaderOutput() == SH_GLSL_CORE_OUTPUT) + else if (symbol == "gl_FragData" && IsGLSL130OrNewer(getShaderOutput())) { out << "webgl_FragData"; } + else if (symbol == "gl_SecondaryFragColorEXT") + { + out << "angle_SecondaryFragColor"; + } + else if (symbol == "gl_SecondaryFragDataEXT") + { + out << "angle_SecondaryFragData"; + } else { TOutputGLSLBase::visitSymbol(node); @@ -67,6 +75,7 @@ TString TOutputGLSL::translateTextureFunction(TString &name) "texture2DProj", "textureProj", "texture2DLod", "textureLod", "texture2DProjLod", "textureProjLod", + "texture2DRect", "texture", "textureCube", "texture", "textureCubeLod", "textureLod", // Extensions @@ -78,7 +87,7 @@ TString TOutputGLSL::translateTextureFunction(TString &name) "textureCubeGradEXT", "textureGrad", NULL, NULL }; - const char **mapping = (getShaderOutput() == SH_GLSL_CORE_OUTPUT) ? + const char **mapping = (IsGLSL130OrNewer(getShaderOutput())) ? legacyToCoreRename : simpleRename; for (int i = 0; mapping[i] != NULL; i += 2) diff --git a/src/3rdparty/angle/src/compiler/translator/OutputGLSL.h b/src/3rdparty/angle/src/compiler/translator/OutputGLSL.h index 21b2d079d3..9b1aca4eab 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputGLSL.h +++ b/src/3rdparty/angle/src/compiler/translator/OutputGLSL.h @@ -21,9 +21,9 @@ class TOutputGLSL : public TOutputGLSLBase ShShaderOutput output); protected: - virtual bool writeVariablePrecision(TPrecision); - virtual void visitSymbol(TIntermSymbol* node); - virtual TString translateTextureFunction(TString& name); + bool writeVariablePrecision(TPrecision) override; + void visitSymbol(TIntermSymbol *node) override; + TString translateTextureFunction(TString &name) override; }; #endif // COMPILER_TRANSLATOR_OUTPUTGLSL_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.cpp b/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.cpp index 4bb6305d05..f048b050b7 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.cpp +++ b/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.cpp @@ -5,7 +5,8 @@ // #include "compiler/translator/OutputGLSLBase.h" -#include "compiler/translator/compilerdebug.h" + +#include "common/debug.h" #include @@ -88,30 +89,46 @@ void TOutputGLSLBase::writeBuiltInFunctionTriplet( writeTriplet(visit, preString.c_str(), ", ", ")"); } +void TOutputGLSLBase::writeLayoutQualifier(const TType &type) +{ + if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn) + { + const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier(); + if (layoutQualifier.location >= 0) + { + TInfoSinkBase &out = objSink(); + out << "layout(location = " << layoutQualifier.location << ") "; + } + } +} + void TOutputGLSLBase::writeVariableType(const TType &type) { TInfoSinkBase &out = objSink(); + if (type.isInvariant()) + { + out << "invariant "; + } + if (type.getBasicType() == EbtInterfaceBlock) + { + TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); + declareInterfaceBlockLayout(interfaceBlock); + } TQualifier qualifier = type.getQualifier(); if (qualifier != EvqTemporary && qualifier != EvqGlobal) { - if (mOutput == SH_GLSL_CORE_OUTPUT) + if (IsGLSL130OrNewer(mOutput)) { switch (qualifier) { case EvqAttribute: - out << "in" << " "; + out << "in "; break; case EvqVaryingIn: - out << "in" << " "; + out << "in "; break; case EvqVaryingOut: - out << "out" << " "; - break; - case EvqInvariantVaryingIn: - out << "invariant in" << " "; - break; - case EvqInvariantVaryingOut: - out << "invariant out" << " "; + out << "out "; break; default: out << type.getQualifierString() << " "; @@ -135,6 +152,11 @@ void TOutputGLSLBase::writeVariableType(const TType &type) mDeclaredStructs.insert(structure->uniqueId()); } } + else if (type.getBasicType() == EbtInterfaceBlock) + { + TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); + declareInterfaceBlock(interfaceBlock); + } else { if (writeVariablePrecision(type.getPrecision())) @@ -167,8 +189,8 @@ void TOutputGLSLBase::writeFunctionParameters(const TIntermSequence &args) } } -const ConstantUnion *TOutputGLSLBase::writeConstantUnion( - const TType &type, const ConstantUnion *pConstUnion) +const TConstantUnion *TOutputGLSLBase::writeConstantUnion( + const TType &type, const TConstantUnion *pConstUnion) { TInfoSinkBase &out = objSink(); @@ -221,6 +243,28 @@ const ConstantUnion *TOutputGLSLBase::writeConstantUnion( return pConstUnion; } +void TOutputGLSLBase::writeConstructorTriplet(Visit visit, const TType &type, const char *constructorBaseType) +{ + TInfoSinkBase &out = objSink(); + if (visit == PreVisit) + { + if (type.isArray()) + { + out << constructorBaseType; + out << arrayBrackets(type); + out << "("; + } + else + { + out << constructorBaseType << "("; + } + } + else + { + writeTriplet(visit, nullptr, ", ", ")"); + } +} + void TOutputGLSLBase::visitSymbol(TIntermSymbol *node) { TInfoSinkBase &out = objSink(); @@ -352,6 +396,22 @@ bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary *node) visitChildren = false; } break; + case EOpIndexDirectInterfaceBlock: + if (visit == InVisit) + { + out << "."; + const TInterfaceBlock *interfaceBlock = node->getLeft()->getType().getInterfaceBlock(); + const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion(); + const TField *field = interfaceBlock->fields()[index->getIConst(0)]; + + TString fieldName = field->name(); + ASSERT(!mSymbolTable.findBuiltIn(interfaceBlock->name(), mShaderVersion)); + fieldName = hashName(fieldName); + + out << fieldName; + visitChildren = false; + } + break; case EOpVectorSwizzle: if (visit == InVisit) { @@ -363,7 +423,7 @@ bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary *node) TIntermConstantUnion *element = (*sit)->getAsConstantUnion(); ASSERT(element->getBasicType() == EbtInt); ASSERT(element->getNominalSize() == 1); - const ConstantUnion& data = element->getUnionArrayPointer()[0]; + const TConstantUnion& data = element->getUnionArrayPointer()[0]; ASSERT(data.getType() == EbtInt); switch (data.getIConst()) { @@ -722,7 +782,6 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) { bool visitChildren = true; TInfoSinkBase &out = objSink(); - TString preString; bool useEmulatedFunction = (visit == PreVisit && node->getUseEmulatedFunction()); switch (node->getOp()) { @@ -756,8 +815,14 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) case EOpPrototype: // Function declaration. ASSERT(visit == PreVisit); - writeVariableType(node->getType()); - out << " " << hashFunctionName(node->getName()); + { + const TType &type = node->getType(); + writeVariableType(type); + if (type.isArray()) + out << arrayBrackets(type); + } + + out << " " << hashFunctionNameIfNeeded(node->getNameObj()); out << "("; writeFunctionParameters(*(node->getSequence())); @@ -768,8 +833,14 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) case EOpFunction: { // Function definition. ASSERT(visit == PreVisit); - writeVariableType(node->getType()); - out << " " << hashFunctionName(node->getName()); + { + const TType &type = node->getType(); + writeVariableType(type); + if (type.isArray()) + out << arrayBrackets(type); + } + + out << " " << hashFunctionNameIfNeeded(node->getNameObj()); incrementDepth(node); // Function definition node contains one or two children nodes @@ -798,16 +869,7 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) case EOpFunctionCall: // Function call. if (visit == PreVisit) - out << hashFunctionName(node->getName()) << "("; - else if (visit == InVisit) - out << ", "; - else - out << ")"; - break; - case EOpInternalFunctionCall: - // Function call to an internal helper function. - if (visit == PreVisit) - out << node->getName() << "("; + out << hashFunctionNameIfNeeded(node->getNameObj()) << "("; else if (visit == InVisit) out << ", "; else @@ -827,6 +889,7 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) { const TIntermSequence &sequence = *(node->getSequence()); const TIntermTyped *variable = sequence.front()->getAsTyped(); + writeLayoutQualifier(variable->getType()); writeVariableType(variable->getType()); out << " "; mDeclaringVariables = true; @@ -854,66 +917,88 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) visitChildren = false; break; case EOpConstructFloat: - writeTriplet(visit, "float(", NULL, ")"); + writeConstructorTriplet(visit, node->getType(), "float"); break; case EOpConstructVec2: - writeBuiltInFunctionTriplet(visit, "vec2(", false); + writeConstructorTriplet(visit, node->getType(), "vec2"); break; case EOpConstructVec3: - writeBuiltInFunctionTriplet(visit, "vec3(", false); + writeConstructorTriplet(visit, node->getType(), "vec3"); break; case EOpConstructVec4: - writeBuiltInFunctionTriplet(visit, "vec4(", false); + writeConstructorTriplet(visit, node->getType(), "vec4"); break; case EOpConstructBool: - writeTriplet(visit, "bool(", NULL, ")"); + writeConstructorTriplet(visit, node->getType(), "bool"); break; case EOpConstructBVec2: - writeBuiltInFunctionTriplet(visit, "bvec2(", false); + writeConstructorTriplet(visit, node->getType(), "bvec2"); break; case EOpConstructBVec3: - writeBuiltInFunctionTriplet(visit, "bvec3(", false); + writeConstructorTriplet(visit, node->getType(), "bvec3"); break; case EOpConstructBVec4: - writeBuiltInFunctionTriplet(visit, "bvec4(", false); + writeConstructorTriplet(visit, node->getType(), "bvec4"); break; case EOpConstructInt: - writeTriplet(visit, "int(", NULL, ")"); + writeConstructorTriplet(visit, node->getType(), "int"); break; case EOpConstructIVec2: - writeBuiltInFunctionTriplet(visit, "ivec2(", false); + writeConstructorTriplet(visit, node->getType(), "ivec2"); break; case EOpConstructIVec3: - writeBuiltInFunctionTriplet(visit, "ivec3(", false); + writeConstructorTriplet(visit, node->getType(), "ivec3"); break; case EOpConstructIVec4: - writeBuiltInFunctionTriplet(visit, "ivec4(", false); + writeConstructorTriplet(visit, node->getType(), "ivec4"); + break; + case EOpConstructUInt: + writeConstructorTriplet(visit, node->getType(), "uint"); + break; + case EOpConstructUVec2: + writeConstructorTriplet(visit, node->getType(), "uvec2"); + break; + case EOpConstructUVec3: + writeConstructorTriplet(visit, node->getType(), "uvec3"); + break; + case EOpConstructUVec4: + writeConstructorTriplet(visit, node->getType(), "uvec4"); break; case EOpConstructMat2: - writeBuiltInFunctionTriplet(visit, "mat2(", false); + writeConstructorTriplet(visit, node->getType(), "mat2"); + break; + case EOpConstructMat2x3: + writeConstructorTriplet(visit, node->getType(), "mat2x3"); + break; + case EOpConstructMat2x4: + writeConstructorTriplet(visit, node->getType(), "mat2x4"); + break; + case EOpConstructMat3x2: + writeConstructorTriplet(visit, node->getType(), "mat3x2"); break; case EOpConstructMat3: - writeBuiltInFunctionTriplet(visit, "mat3(", false); + writeConstructorTriplet(visit, node->getType(), "mat3"); + break; + case EOpConstructMat3x4: + writeConstructorTriplet(visit, node->getType(), "mat3x4"); + break; + case EOpConstructMat4x2: + writeConstructorTriplet(visit, node->getType(), "mat4x2"); + break; + case EOpConstructMat4x3: + writeConstructorTriplet(visit, node->getType(), "mat4x3"); break; case EOpConstructMat4: - writeBuiltInFunctionTriplet(visit, "mat4(", false); + writeConstructorTriplet(visit, node->getType(), "mat4"); break; case EOpConstructStruct: - if (visit == PreVisit) { const TType &type = node->getType(); ASSERT(type.getBasicType() == EbtStruct); - out << hashName(type.getStruct()->name()) << "("; - } - else if (visit == InVisit) - { - out << ", "; - } - else - { - out << ")"; + TString constructorName = hashName(type.getStruct()->name()); + writeConstructorTriplet(visit, node->getType(), constructorName.c_str()); + break; } - break; case EOpOuterProduct: writeBuiltInFunctionTriplet(visit, "outerProduct(", useEmulatedFunction); @@ -1004,8 +1089,12 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node) TInfoSinkBase &out = objSink(); incrementDepth(node); - // Loop header. + TLoopType loopType = node->getType(); + + // Only for loops can be unrolled + ASSERT(!node->getUnrollFlag() || loopType == ELoopFor); + if (loopType == ELoopFor) // for loop { if (!node->getUnrollFlag()) @@ -1022,6 +1111,8 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node) if (node->getExpression()) node->getExpression()->traverse(this); out << ")\n"; + + visitCodeBlock(node->getBody()); } else { @@ -1034,6 +1125,16 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node) out << "for (int " << name << " = 0; " << name << " < 1; " << "++" << name << ")\n"; + + out << "{\n"; + mLoopUnrollStack.push(node); + while (mLoopUnrollStack.satisfiesLoopCondition()) + { + visitCodeBlock(node->getBody()); + mLoopUnrollStack.step(); + } + mLoopUnrollStack.pop(); + out << "}\n"; } } else if (loopType == ELoopWhile) // while loop @@ -1042,39 +1143,22 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node) ASSERT(node->getCondition() != NULL); node->getCondition()->traverse(this); out << ")\n"; + + visitCodeBlock(node->getBody()); } else // do-while loop { ASSERT(loopType == ELoopDoWhile); out << "do\n"; - } - // Loop body. - if (node->getUnrollFlag()) - { - out << "{\n"; - mLoopUnrollStack.push(node); - while (mLoopUnrollStack.satisfiesLoopCondition()) - { - visitCodeBlock(node->getBody()); - mLoopUnrollStack.step(); - } - mLoopUnrollStack.pop(); - out << "}\n"; - } - else - { visitCodeBlock(node->getBody()); - } - // Loop footer. - if (loopType == ELoopDoWhile) // do-while loop - { out << "while ("; ASSERT(node->getCondition() != NULL); node->getCondition()->traverse(this); out << ");\n"; } + decrementDepth(); // No need to visit children. They have been already processed in @@ -1129,6 +1213,10 @@ TString TOutputGLSLBase::getTypeName(const TType &type) { out << "mat"; out << type.getNominalSize(); + if (type.getSecondarySize() != type.getNominalSize()) + { + out << "x" << type.getSecondarySize(); + } } else if (type.isVector()) { @@ -1143,6 +1231,9 @@ TString TOutputGLSLBase::getTypeName(const TType &type) case EbtBool: out << "bvec"; break; + case EbtUInt: + out << "uvec"; + break; default: UNREACHABLE(); } @@ -1177,12 +1268,16 @@ TString TOutputGLSLBase::hashVariableName(const TString &name) return hashName(name); } -TString TOutputGLSLBase::hashFunctionName(const TString &mangled_name) +TString TOutputGLSLBase::hashFunctionNameIfNeeded(const TName &mangledName) { - TString name = TFunction::unmangleName(mangled_name); - if (mSymbolTable.findBuiltIn(mangled_name, mShaderVersion) != NULL || name == "main") + TString mangledStr = mangledName.getString(); + TString name = TFunction::unmangleName(mangledStr); + if (mSymbolTable.findBuiltIn(mangledStr, mShaderVersion) != nullptr || name == "main") return translateTextureFunction(name); - return hashName(name); + if (mangledName.isInternal()) + return name; + else + return hashName(name); } bool TOutputGLSLBase::structDeclared(const TStructure *structure) const @@ -1215,3 +1310,70 @@ void TOutputGLSLBase::declareStruct(const TStructure *structure) out << "}"; } +void TOutputGLSLBase::declareInterfaceBlockLayout(const TInterfaceBlock *interfaceBlock) +{ + TInfoSinkBase &out = objSink(); + + out << "layout("; + + switch (interfaceBlock->blockStorage()) + { + case EbsUnspecified: + case EbsShared: + // Default block storage is shared. + out << "shared"; + break; + + case EbsPacked: + out << "packed"; + break; + + case EbsStd140: + out << "std140"; + break; + + default: + UNREACHABLE(); + break; + } + + out << ", "; + + switch (interfaceBlock->matrixPacking()) + { + case EmpUnspecified: + case EmpColumnMajor: + // Default matrix packing is column major. + out << "column_major"; + break; + + case EmpRowMajor: + out << "row_major"; + break; + + default: + UNREACHABLE(); + break; + } + + out << ") "; +} + +void TOutputGLSLBase::declareInterfaceBlock(const TInterfaceBlock *interfaceBlock) +{ + TInfoSinkBase &out = objSink(); + + out << hashName(interfaceBlock->name()) << "{\n"; + const TFieldList &fields = interfaceBlock->fields(); + for (size_t i = 0; i < fields.size(); ++i) + { + const TField *field = fields[i]; + if (writeVariablePrecision(field->type()->getPrecision())) + out << " "; + out << getTypeName(*field->type()) << " " << hashName(field->name()); + if (field->type()->isArray()) + out << arrayBrackets(*field->type()); + out << ";\n"; + } + out << "}"; +} diff --git a/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.h b/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.h index 4e66059c21..2ae82d15b2 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.h +++ b/src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.h @@ -32,22 +32,24 @@ class TOutputGLSLBase : public TIntermTraverser protected: TInfoSinkBase &objSink() { return mObjSink; } void writeTriplet(Visit visit, const char *preStr, const char *inStr, const char *postStr); + void writeLayoutQualifier(const TType &type); void writeVariableType(const TType &type); virtual bool writeVariablePrecision(TPrecision precision) = 0; void writeFunctionParameters(const TIntermSequence &args); - const ConstantUnion *writeConstantUnion(const TType &type, const ConstantUnion *pConstUnion); + const TConstantUnion *writeConstantUnion(const TType &type, const TConstantUnion *pConstUnion); + void writeConstructorTriplet(Visit visit, const TType &type, const char *constructorBaseType); TString getTypeName(const TType &type); - virtual void visitSymbol(TIntermSymbol *node); - virtual void visitConstantUnion(TIntermConstantUnion *node); - virtual bool visitBinary(Visit visit, TIntermBinary *node); - virtual bool visitUnary(Visit visit, TIntermUnary *node); - virtual bool visitSelection(Visit visit, TIntermSelection *node); - virtual bool visitSwitch(Visit visit, TIntermSwitch *node); - virtual bool visitCase(Visit visit, TIntermCase *node); - virtual bool visitAggregate(Visit visit, TIntermAggregate *node); - virtual bool visitLoop(Visit visit, TIntermLoop *node); - virtual bool visitBranch(Visit visit, TIntermBranch *node); + void visitSymbol(TIntermSymbol *node) override; + void visitConstantUnion(TIntermConstantUnion *node) override; + bool visitBinary(Visit visit, TIntermBinary *node) override; + bool visitUnary(Visit visit, TIntermUnary *node) override; + bool visitSelection(Visit visit, TIntermSelection *node) override; + bool visitSwitch(Visit visit, TIntermSwitch *node) override; + bool visitCase(Visit visit, TIntermCase *node) override; + bool visitAggregate(Visit visit, TIntermAggregate *node) override; + bool visitLoop(Visit visit, TIntermLoop *node) override; + bool visitBranch(Visit visit, TIntermBranch *node) override; void visitCodeBlock(TIntermNode *node); @@ -56,8 +58,8 @@ class TOutputGLSLBase : public TIntermTraverser TString hashName(const TString &name); // Same as hashName(), but without hashing built-in variables. TString hashVariableName(const TString &name); - // Same as hashName(), but without hashing built-in functions. - TString hashFunctionName(const TString &mangled_name); + // Same as hashName(), but without hashing built-in functions and with unmangling. + TString hashFunctionNameIfNeeded(const TName &mangledName); // Used to translate function names for differences between ESSL and GLSL virtual TString translateTextureFunction(TString &name) { return name; } @@ -65,6 +67,9 @@ class TOutputGLSLBase : public TIntermTraverser bool structDeclared(const TStructure *structure) const; void declareStruct(const TStructure *structure); + void declareInterfaceBlockLayout(const TInterfaceBlock *interfaceBlock); + void declareInterfaceBlock(const TInterfaceBlock *interfaceBlock); + void writeBuiltInFunctionTriplet(Visit visit, const char *preStr, bool useEmulatedFunction); TInfoSinkBase &mObjSink; diff --git a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp index 94225b81c4..253b96696c 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp @@ -11,45 +11,81 @@ #include #include "common/angleutils.h" +#include "common/debug.h" #include "common/utilities.h" #include "compiler/translator/BuiltInFunctionEmulator.h" #include "compiler/translator/BuiltInFunctionEmulatorHLSL.h" -#include "compiler/translator/DetectDiscontinuity.h" #include "compiler/translator/FlagStd140Structs.h" #include "compiler/translator/InfoSink.h" #include "compiler/translator/NodeSearch.h" #include "compiler/translator/RemoveSwitchFallThrough.h" -#include "compiler/translator/RewriteElseBlocks.h" #include "compiler/translator/SearchSymbol.h" #include "compiler/translator/StructureHLSL.h" #include "compiler/translator/TranslatorHLSL.h" -#include "compiler/translator/UnfoldShortCircuit.h" #include "compiler/translator/UniformHLSL.h" #include "compiler/translator/UtilsHLSL.h" #include "compiler/translator/blocklayout.h" -#include "compiler/translator/compilerdebug.h" #include "compiler/translator/util.h" -namespace sh +namespace { -TString OutputHLSL::TextureFunction::name() const +bool IsSequence(TIntermNode *node) { - TString name = "gl_texture"; + return node->getAsAggregate() != nullptr && node->getAsAggregate()->getOp() == EOpSequence; +} - if (IsSampler2D(sampler)) - { - name += "2D"; - } - else if (IsSampler3D(sampler)) - { - name += "3D"; +void WriteSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion) +{ + ASSERT(constUnion != nullptr); + switch (constUnion->getType()) + { + case EbtFloat: + out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst())); + break; + case EbtInt: + out << constUnion->getIConst(); + break; + case EbtUInt: + out << constUnion->getUConst(); + break; + case EbtBool: + out << constUnion->getBConst(); + break; + default: + UNREACHABLE(); } - else if (IsSamplerCube(sampler)) +} + +const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out, + const TConstantUnion *const constUnion, + const size_t size) +{ + const TConstantUnion *constUnionIterated = constUnion; + for (size_t i = 0; i < size; i++, constUnionIterated++) { - name += "Cube"; + WriteSingleConstant(out, constUnionIterated); + + if (i != size - 1) + { + out << ", "; + } } - else UNREACHABLE(); + return constUnionIterated; +} + +} // namespace + +namespace sh +{ + +TString OutputHLSL::TextureFunction::name() const +{ + TString name = "gl_texture"; + + // We need to include full the sampler type in the function name to make the signature unique + // on D3D11, where samplers are passed to texture functions as indices. + name += TextureTypeSuffix(this->sampler); if (proj) { @@ -108,10 +144,10 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, mExtensionBehavior(extensionBehavior), mSourcePath(sourcePath), mOutputType(outputType), + mCompileOptions(compileOptions), mNumRenderTargets(numRenderTargets), - mCompileOptions(compileOptions) + mCurrentFunctionMetadata(nullptr) { - mUnfoldShortCircuit = new UnfoldShortCircuit(this); mInsideFunction = false; mUsesFragColor = false; @@ -130,8 +166,6 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, mUniqueIndex = 0; - mContainsLoopDiscontinuity = false; - mContainsAnyLoop = false; mOutputLod0Function = false; mInsideDiscontinuousLoop = false; mNestedLoopDepth = 0; @@ -141,7 +175,7 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, mStructureHLSL = new StructureHLSL; mUniformHLSL = new UniformHLSL(mStructureHLSL, outputType, uniforms); - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { // Fragment shaders need dx_DepthRange, dx_ViewCoords and dx_DepthFront. // Vertex shaders need a slightly different set: dx_DepthRange, dx_ViewCoords and dx_ViewAdjust. @@ -155,37 +189,33 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion, OutputHLSL::~OutputHLSL() { - SafeDelete(mUnfoldShortCircuit); SafeDelete(mStructureHLSL); SafeDelete(mUniformHLSL); - for (auto it = mStructEqualityFunctions.begin(); it != mStructEqualityFunctions.end(); ++it) + for (auto &eqFunction : mStructEqualityFunctions) { - SafeDelete(*it); + SafeDelete(eqFunction); } - for (auto it = mArrayEqualityFunctions.begin(); it != mArrayEqualityFunctions.end(); ++it) + for (auto &eqFunction : mArrayEqualityFunctions) { - SafeDelete(*it); + SafeDelete(eqFunction); } } void OutputHLSL::output(TIntermNode *treeRoot, TInfoSinkBase &objSink) { - mContainsLoopDiscontinuity = mShaderType == GL_FRAGMENT_SHADER && containsLoopDiscontinuity(treeRoot); - mContainsAnyLoop = containsAnyLoop(treeRoot); const std::vector &flaggedStructs = FlagStd140ValueStructs(treeRoot); makeFlaggedStructMaps(flaggedStructs); - // Work around D3D9 bug that would manifest in vertex shaders with selection blocks which - // use a vertex attribute as a condition, and some related computation in the else block. - if (mOutputType == SH_HLSL9_OUTPUT && mShaderType == GL_VERTEX_SHADER) - { - RewriteElseBlocks(treeRoot); - } - BuiltInFunctionEmulator builtInFunctionEmulator; InitBuiltInFunctionEmulatorForHLSL(&builtInFunctionEmulator); builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(treeRoot); + // Now that we are done changing the AST, do the analyses need for HLSL generation + CallDAG::InitResult success = mCallDag.init(treeRoot, &objSink); + ASSERT(success == CallDAG::INITDAG_SUCCESS); + UNUSED_ASSERTION_VARIABLE(success); + mASTMetadataList = CreateASTMetadataHLSL(treeRoot, mCallDag); + // Output the body and footer first to determine what has to go in the header mInfoSinkStack.push(&mBody); treeRoot->traverse(this); @@ -199,7 +229,7 @@ void OutputHLSL::output(TIntermNode *treeRoot, TInfoSinkBase &objSink) mInfoSinkStack.pop(); mInfoSinkStack.push(&mHeader); - header(&builtInFunctionEmulator); + header(mHeader, &builtInFunctionEmulator); mInfoSinkStack.pop(); objSink << mHeader.c_str(); @@ -294,10 +324,8 @@ TString OutputHLSL::structInitializerString(int indent, const TStructure &struct return init; } -void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) +void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator) { - TInfoSinkBase &out = getInfoSink(); - TString varyings; TString attributes; TString flaggedStructs; @@ -334,23 +362,31 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << mStructureHLSL->structsHeader(); - out << mUniformHLSL->uniformsHeader(mOutputType, mReferencedUniforms); + mUniformHLSL->uniformsHeader(out, mOutputType, mReferencedUniforms); out << mUniformHLSL->interfaceBlocksHeader(mReferencedInterfaceBlocks); if (!mEqualityFunctions.empty()) { out << "\n// Equality functions\n\n"; - for (auto it = mEqualityFunctions.cbegin(); it != mEqualityFunctions.cend(); ++it) + for (const auto &eqFunction : mEqualityFunctions) { - out << (*it)->functionDefinition << "\n"; + out << eqFunction->functionDefinition << "\n"; } } if (!mArrayAssignmentFunctions.empty()) { out << "\n// Assignment functions\n\n"; - for (auto it = mArrayAssignmentFunctions.cbegin(); it != mArrayAssignmentFunctions.cend(); ++it) + for (const auto &assignmentFunction : mArrayAssignmentFunctions) { - out << it->functionDefinition << "\n"; + out << assignmentFunction.functionDefinition << "\n"; + } + } + if (!mArrayConstructIntoFunctions.empty()) + { + out << "\n// Array constructor functions\n\n"; + for (const auto &constructIntoFunction : mArrayConstructIntoFunctions) + { + out << constructIntoFunction.functionDefinition << "\n"; } } @@ -449,7 +485,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) "\n"; } - if (mOutputType == SH_HLSL11_OUTPUT) + if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { out << "cbuffer DriverConstants : register(b1)\n" "{\n"; @@ -469,6 +505,13 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " float3 dx_DepthFront : packoffset(c2);\n"; } + if (mUsesFragCoord) + { + // dx_ViewScale is only used in the fragment shader to correct + // the value for glFragCoord if necessary + out << " float2 dx_ViewScale : packoffset(c3);\n"; + } + out << "};\n"; } else @@ -553,7 +596,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) "\n"; } - if (mOutputType == SH_HLSL11_OUTPUT) + if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { out << "cbuffer DriverConstants : register(b1)\n" "{\n"; @@ -563,11 +606,13 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " float3 dx_DepthRange : packoffset(c0);\n"; } - // dx_ViewAdjust and dx_ViewCoords will only be used in Feature Level 9 shaders. - // However, we declare it for all shaders (including Feature Level 10+). - // The bytecode is the same whether we declare it or not, since D3DCompiler removes it if it's unused. + // dx_ViewAdjust and dx_ViewCoords will only be used in Feature Level 9 + // shaders. However, we declare it for all shaders (including Feature Level 10+). + // The bytecode is the same whether we declare it or not, since D3DCompiler removes it + // if it's unused. out << " float4 dx_ViewAdjust : packoffset(c1);\n"; out << " float2 dx_ViewCoords : packoffset(c2);\n"; + out << " float2 dx_ViewScale : packoffset(c3);\n"; out << "};\n" "\n"; @@ -653,7 +698,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) // Argument list int hlslCoords = 4; - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { switch(textureFunction->sampler) { @@ -672,29 +717,20 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) default: UNREACHABLE(); } } - else if (mOutputType == SH_HLSL11_OUTPUT) + else { - switch(textureFunction->sampler) + hlslCoords = HLSLTextureCoordsCount(textureFunction->sampler); + if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { - case EbtSampler2D: out << "Texture2D x, SamplerState s"; hlslCoords = 2; break; - case EbtSampler3D: out << "Texture3D x, SamplerState s"; hlslCoords = 3; break; - case EbtSamplerCube: out << "TextureCube x, SamplerState s"; hlslCoords = 3; break; - case EbtSampler2DArray: out << "Texture2DArray x, SamplerState s"; hlslCoords = 3; break; - case EbtISampler2D: out << "Texture2D x, SamplerState s"; hlslCoords = 2; break; - case EbtISampler3D: out << "Texture3D x, SamplerState s"; hlslCoords = 3; break; - case EbtISamplerCube: out << "Texture2DArray x, SamplerState s"; hlslCoords = 3; break; - case EbtISampler2DArray: out << "Texture2DArray x, SamplerState s"; hlslCoords = 3; break; - case EbtUSampler2D: out << "Texture2D x, SamplerState s"; hlslCoords = 2; break; - case EbtUSampler3D: out << "Texture3D x, SamplerState s"; hlslCoords = 3; break; - case EbtUSamplerCube: out << "Texture2DArray x, SamplerState s"; hlslCoords = 3; break; - case EbtUSampler2DArray: out << "Texture2DArray x, SamplerState s"; hlslCoords = 3; break; - case EbtSampler2DShadow: out << "Texture2D x, SamplerComparisonState s"; hlslCoords = 2; break; - case EbtSamplerCubeShadow: out << "TextureCube x, SamplerComparisonState s"; hlslCoords = 3; break; - case EbtSampler2DArrayShadow: out << "Texture2DArray x, SamplerComparisonState s"; hlslCoords = 3; break; - default: UNREACHABLE(); + out << TextureString(textureFunction->sampler) << " x, " + << SamplerString(textureFunction->sampler) << " s"; + } + else + { + ASSERT(mOutputType == SH_HLSL_4_1_OUTPUT); + out << "const uint samplerIndex"; } } - else UNREACHABLE(); if (textureFunction->method == TextureFunction::FETCH) // Integer coordinates { @@ -785,6 +821,31 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << ")\n" "{\n"; + // In some cases we use a variable to store the texture/sampler objects, but to work around + // a D3D11 compiler bug related to discard inside a loop that is conditional on texture + // sampling we need to call the function directly on a reference to the array. The bug was + // found using dEQP-GLES3.functional.shaders.discard*loop_texture* tests. + TString textureReference("x"); + TString samplerReference("s"); + if (mOutputType == SH_HLSL_4_1_OUTPUT) + { + TString suffix = TextureGroupSuffix(textureFunction->sampler); + if (TextureGroup(textureFunction->sampler) == HLSL_TEXTURE_2D) + { + textureReference = TString("textures") + suffix + "[samplerIndex]"; + samplerReference = TString("samplers") + suffix + "[samplerIndex]"; + } + else + { + out << " const uint textureIndex = samplerIndex - textureIndexOffset" << suffix + << ";\n"; + textureReference = TString("textures") + suffix + "[textureIndex]"; + out << " const uint samplerArrayIndex = samplerIndex - samplerIndexOffset" + << suffix << ";\n"; + samplerReference = TString("samplers") + suffix + "[samplerArrayIndex]"; + } + } + if (textureFunction->method == TextureFunction::SIZE) { if (IsSampler2D(textureFunction->sampler) || IsSamplerCube(textureFunction->sampler)) @@ -792,18 +853,21 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) if (IsSamplerArray(textureFunction->sampler)) { out << " uint width; uint height; uint layers; uint numberOfLevels;\n" - " x.GetDimensions(lod, width, height, layers, numberOfLevels);\n"; + << " " << textureReference + << ".GetDimensions(lod, width, height, layers, numberOfLevels);\n"; } else { out << " uint width; uint height; uint numberOfLevels;\n" - " x.GetDimensions(lod, width, height, numberOfLevels);\n"; + << " " << textureReference + << ".GetDimensions(lod, width, height, numberOfLevels);\n"; } } else if (IsSampler3D(textureFunction->sampler)) { out << " uint width; uint height; uint depth; uint numberOfLevels;\n" - " x.GetDimensions(lod, width, height, depth, numberOfLevels);\n"; + << " " << textureReference + << ".GetDimensions(lod, width, height, depth, numberOfLevels);\n"; } else UNREACHABLE(); @@ -835,7 +899,8 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " uint mip = 0;\n"; - out << " x.GetDimensions(mip, width, height, layers, levels);\n"; + out << " " << textureReference + << ".GetDimensions(mip, width, height, layers, levels);\n"; out << " bool xMajor = abs(t.x) > abs(t.y) && abs(t.x) > abs(t.z);\n"; out << " bool yMajor = abs(t.y) > abs(t.z) && abs(t.y) > abs(t.x);\n"; @@ -856,6 +921,18 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " t.x = (u * 0.5f / m) + 0.5f;\n"; out << " t.y = (v * 0.5f / m) + 0.5f;\n"; + + // Mip level computation. + if (textureFunction->method == TextureFunction::IMPLICIT) + { + out << " float2 tSized = float2(t.x * width, t.y * height);\n" + " float2 dx = ddx(tSized);\n" + " float2 dy = ddy(tSized);\n" + " float lod = 0.5f * log2(max(dot(dx, dx), dot(dy, dy)));\n" + " mip = uint(min(max(round(lod), 0), levels - 1));\n" + << " " << textureReference + << ".GetDimensions(mip, width, height, layers, levels);\n"; + } } else if (IsIntegerSampler(textureFunction->sampler) && textureFunction->method != TextureFunction::FETCH) @@ -876,11 +953,13 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else { + + out << " " << textureReference + << ".GetDimensions(0, width, height, layers, levels);\n"; if (textureFunction->method == TextureFunction::IMPLICIT || textureFunction->method == TextureFunction::BIAS) { - out << " x.GetDimensions(0, width, height, layers, levels);\n" - " float2 tSized = float2(t.x * width, t.y * height);\n" + out << " float2 tSized = float2(t.x * width, t.y * height);\n" " float dx = length(ddx(tSized));\n" " float dy = length(ddy(tSized));\n" " float lod = log2(max(dx, dy));\n"; @@ -892,14 +971,14 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else if (textureFunction->method == TextureFunction::GRAD) { - out << " x.GetDimensions(0, width, height, layers, levels);\n" - " float lod = log2(max(length(ddx), length(ddy)));\n"; + out << " float lod = log2(max(length(ddx), length(ddy)));\n"; } out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; } - out << " x.GetDimensions(mip, width, height, layers, levels);\n"; + out << " " << textureReference + << ".GetDimensions(mip, width, height, layers, levels);\n"; } else { @@ -915,11 +994,13 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else { + out << " " << textureReference + << ".GetDimensions(0, width, height, levels);\n"; + if (textureFunction->method == TextureFunction::IMPLICIT || textureFunction->method == TextureFunction::BIAS) { - out << " x.GetDimensions(0, width, height, levels);\n" - " float2 tSized = float2(t.x * width, t.y * height);\n" + out << " float2 tSized = float2(t.x * width, t.y * height);\n" " float dx = length(ddx(tSized));\n" " float dy = length(ddy(tSized));\n" " float lod = log2(max(dx, dy));\n"; @@ -929,20 +1010,16 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " lod += bias;\n"; } } - else if (textureFunction->method == TextureFunction::LOD) - { - out << " x.GetDimensions(0, width, height, levels);\n"; - } else if (textureFunction->method == TextureFunction::GRAD) { - out << " x.GetDimensions(0, width, height, levels);\n" - " float lod = log2(max(length(ddx), length(ddy)));\n"; + out << " float lod = log2(max(length(ddx), length(ddy)));\n"; } out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; } - out << " x.GetDimensions(mip, width, height, levels);\n"; + out << " " << textureReference + << ".GetDimensions(mip, width, height, levels);\n"; } } else if (IsSampler3D(textureFunction->sampler)) @@ -959,11 +1036,14 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else { + out << " " << textureReference + << ".GetDimensions(0, width, height, depth, levels);\n"; + if (textureFunction->method == TextureFunction::IMPLICIT || textureFunction->method == TextureFunction::BIAS) { - out << " x.GetDimensions(0, width, height, depth, levels);\n" - " float3 tSized = float3(t.x * width, t.y * height, t.z * depth);\n" + out << " float3 tSized = float3(t.x * width, t.y * height, t.z * " + "depth);\n" " float dx = length(ddx(tSized));\n" " float dy = length(ddy(tSized));\n" " float lod = log2(max(dx, dy));\n"; @@ -975,14 +1055,14 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) } else if (textureFunction->method == TextureFunction::GRAD) { - out << " x.GetDimensions(0, width, height, depth, levels);\n" - " float lod = log2(max(length(ddx), length(ddy)));\n"; + out << " float lod = log2(max(length(ddx), length(ddy)));\n"; } out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n"; } - out << " x.GetDimensions(mip, width, height, depth, levels);\n"; + out << " " << textureReference + << ".GetDimensions(mip, width, height, depth, levels);\n"; } else UNREACHABLE(); } @@ -990,7 +1070,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << " return "; // HLSL intrinsic - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { switch(textureFunction->sampler) { @@ -1001,45 +1081,71 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) switch(textureFunction->method) { - case TextureFunction::IMPLICIT: out << "(s, "; break; - case TextureFunction::BIAS: out << "bias(s, "; break; - case TextureFunction::LOD: out << "lod(s, "; break; - case TextureFunction::LOD0: out << "lod(s, "; break; - case TextureFunction::LOD0BIAS: out << "lod(s, "; break; + case TextureFunction::IMPLICIT: + out << "(" << samplerReference << ", "; + break; + case TextureFunction::BIAS: + out << "bias(" << samplerReference << ", "; + break; + case TextureFunction::LOD: + out << "lod(" << samplerReference << ", "; + break; + case TextureFunction::LOD0: + out << "lod(" << samplerReference << ", "; + break; + case TextureFunction::LOD0BIAS: + out << "lod(" << samplerReference << ", "; + break; default: UNREACHABLE(); } } - else if (mOutputType == SH_HLSL11_OUTPUT) + else if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { if (textureFunction->method == TextureFunction::GRAD) { if (IsIntegerSampler(textureFunction->sampler)) { - out << "x.Load("; + out << "" << textureReference << ".Load("; } else if (IsShadowSampler(textureFunction->sampler)) { - out << "x.SampleCmpLevelZero(s, "; + out << "" << textureReference << ".SampleCmpLevelZero(" << samplerReference + << ", "; } else { - out << "x.SampleGrad(s, "; + out << "" << textureReference << ".SampleGrad(" << samplerReference << ", "; } } else if (IsIntegerSampler(textureFunction->sampler) || textureFunction->method == TextureFunction::FETCH) { - out << "x.Load("; + out << "" << textureReference << ".Load("; } else if (IsShadowSampler(textureFunction->sampler)) { switch(textureFunction->method) { - case TextureFunction::IMPLICIT: out << "x.SampleCmp(s, "; break; - case TextureFunction::BIAS: out << "x.SampleCmp(s, "; break; - case TextureFunction::LOD: out << "x.SampleCmp(s, "; break; - case TextureFunction::LOD0: out << "x.SampleCmpLevelZero(s, "; break; - case TextureFunction::LOD0BIAS: out << "x.SampleCmpLevelZero(s, "; break; + case TextureFunction::IMPLICIT: + out << "" << textureReference << ".SampleCmp(" << samplerReference + << ", "; + break; + case TextureFunction::BIAS: + out << "" << textureReference << ".SampleCmp(" << samplerReference + << ", "; + break; + case TextureFunction::LOD: + out << "" << textureReference << ".SampleCmp(" << samplerReference + << ", "; + break; + case TextureFunction::LOD0: + out << "" << textureReference << ".SampleCmpLevelZero(" + << samplerReference << ", "; + break; + case TextureFunction::LOD0BIAS: + out << "" << textureReference << ".SampleCmpLevelZero(" + << samplerReference << ", "; + break; default: UNREACHABLE(); } } @@ -1047,11 +1153,25 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) { switch(textureFunction->method) { - case TextureFunction::IMPLICIT: out << "x.Sample(s, "; break; - case TextureFunction::BIAS: out << "x.SampleBias(s, "; break; - case TextureFunction::LOD: out << "x.SampleLevel(s, "; break; - case TextureFunction::LOD0: out << "x.SampleLevel(s, "; break; - case TextureFunction::LOD0BIAS: out << "x.SampleLevel(s, "; break; + case TextureFunction::IMPLICIT: + out << "" << textureReference << ".Sample(" << samplerReference << ", "; + break; + case TextureFunction::BIAS: + out << "" << textureReference << ".SampleBias(" << samplerReference + << ", "; + break; + case TextureFunction::LOD: + out << "" << textureReference << ".SampleLevel(" << samplerReference + << ", "; + break; + case TextureFunction::LOD0: + out << "" << textureReference << ".SampleLevel(" << samplerReference + << ", "; + break; + case TextureFunction::LOD0BIAS: + out << "" << textureReference << ".SampleLevel(" << samplerReference + << ", "; + break; default: UNREACHABLE(); } } @@ -1121,7 +1241,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << addressx + ("t.x" + proj) + close + ", " + addressy + ("t.y" + proj) + close; - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { if (hlslCoords >= 3) { @@ -1149,7 +1269,7 @@ void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator) out << "));\n"; } - else if (mOutputType == SH_HLSL11_OUTPUT) + else if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) { if (hlslCoords >= 3) { @@ -1311,8 +1431,8 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node) if (qualifier == EvqUniform) { - const TType& nodeType = node->getType(); - const TInterfaceBlock* interfaceBlock = nodeType.getInterfaceBlock(); + const TType &nodeType = node->getType(); + const TInterfaceBlock *interfaceBlock = nodeType.getInterfaceBlock(); if (interfaceBlock) { @@ -1323,6 +1443,8 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node) mReferencedUniforms[name] = node; } + ensureStructDefined(nodeType); + out << DecorateUniform(name, nodeType); } else if (qualifier == EvqAttribute || qualifier == EvqVertexIn) @@ -1375,18 +1497,14 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node) mUsesInstanceID = true; out << name; } - else if (name == "gl_FragDepthEXT") + else if (name == "gl_FragDepthEXT" || name == "gl_FragDepth") { mUsesFragDepth = true; out << "gl_Depth"; } - else if (qualifier == EvqInternal) - { - out << name; - } else { - out << Decorate(name); + out << DecorateIfNeeded(node->getName()); } } } @@ -1402,11 +1520,11 @@ void OutputHLSL::outputEqual(Visit visit, const TType &type, TOperator op, TInfo { if (op == EOpEqual) { - outputTriplet(visit, "(", " == ", ")", out); + outputTriplet(out, visit, "(", " == ", ")"); } else { - outputTriplet(visit, "(", " != ", ")", out); + outputTriplet(out, visit, "(", " != ", ")"); } } else @@ -1419,18 +1537,18 @@ void OutputHLSL::outputEqual(Visit visit, const TType &type, TOperator op, TInfo if (type.isArray()) { const TString &functionName = addArrayEqualityFunction(type); - outputTriplet(visit, (functionName + "(").c_str(), ", ", ")", out); + outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); } else if (type.getBasicType() == EbtStruct) { const TStructure &structure = *type.getStruct(); const TString &functionName = addStructEqualityFunction(structure); - outputTriplet(visit, (functionName + "(").c_str(), ", ", ")", out); + outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); } else { ASSERT(type.isMatrix() || type.isVector()); - outputTriplet(visit, "all(", " == ", ")", out); + outputTriplet(out, visit, "all(", " == ", ")"); } } } @@ -1451,12 +1569,30 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) case EOpAssign: if (node->getLeft()->isArray()) { + TIntermAggregate *rightAgg = node->getRight()->getAsAggregate(); + if (rightAgg != nullptr && rightAgg->isConstructor()) + { + const TString &functionName = addArrayConstructIntoFunction(node->getType()); + out << functionName << "("; + node->getLeft()->traverse(this); + TIntermSequence *seq = rightAgg->getSequence(); + for (auto &arrayElement : *seq) + { + out << ", "; + arrayElement->traverse(this); + } + out << ")"; + return false; + } + // ArrayReturnValueToOutParameter should have eliminated expressions where a function call is assigned. + ASSERT(rightAgg == nullptr || rightAgg->getOp() != EOpFunctionCall); + const TString &functionName = addArrayAssignmentFunction(node->getType()); - outputTriplet(visit, (functionName + "(").c_str(), ", ", ")"); + outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); } else { - outputTriplet(visit, "(", " = ", ")"); + outputTriplet(out, visit, "(", " = ", ")"); } break; case EOpInitialize: @@ -1475,8 +1611,12 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) if (symbolNode->getQualifier() == EvqGlobal && expression->getQualifier() != EvqConst) { // For variables which are not constant, defer their real initialization until - // after we initialize other globals: uniforms, attributes and varyings. - mDeferredGlobalInitializers.push_back(std::make_pair(symbolNode, expression)); + // after we initialize uniforms. + TIntermBinary *deferredInit = new TIntermBinary(EOpAssign); + deferredInit->setLeft(node->getLeft()); + deferredInit->setRight(node->getRight()); + deferredInit->setType(node->getType()); + mDeferredGlobalInitializers.push_back(deferredInit); const TString &initString = initializer(node->getType()); node->setRight(new TIntermRaw(node->getType(), initString)); } @@ -1485,17 +1625,31 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) // Skip initializing the rest of the expression return false; } + else if (writeConstantInitialization(out, symbolNode, expression)) + { + return false; + } } else if (visit == InVisit) { out << " = "; } break; - case EOpAddAssign: outputTriplet(visit, "(", " += ", ")"); break; - case EOpSubAssign: outputTriplet(visit, "(", " -= ", ")"); break; - case EOpMulAssign: outputTriplet(visit, "(", " *= ", ")"); break; - case EOpVectorTimesScalarAssign: outputTriplet(visit, "(", " *= ", ")"); break; - case EOpMatrixTimesScalarAssign: outputTriplet(visit, "(", " *= ", ")"); break; + case EOpAddAssign: + outputTriplet(out, visit, "(", " += ", ")"); + break; + case EOpSubAssign: + outputTriplet(out, visit, "(", " -= ", ")"); + break; + case EOpMulAssign: + outputTriplet(out, visit, "(", " *= ", ")"); + break; + case EOpVectorTimesScalarAssign: + outputTriplet(out, visit, "(", " *= ", ")"); + break; + case EOpMatrixTimesScalarAssign: + outputTriplet(out, visit, "(", " *= ", ")"); + break; case EOpVectorTimesMatrixAssign: if (visit == PreVisit) { @@ -1528,13 +1682,27 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) out << "))))"; } break; - case EOpDivAssign: outputTriplet(visit, "(", " /= ", ")"); break; - case EOpIModAssign: outputTriplet(visit, "(", " %= ", ")"); break; - case EOpBitShiftLeftAssign: outputTriplet(visit, "(", " <<= ", ")"); break; - case EOpBitShiftRightAssign: outputTriplet(visit, "(", " >>= ", ")"); break; - case EOpBitwiseAndAssign: outputTriplet(visit, "(", " &= ", ")"); break; - case EOpBitwiseXorAssign: outputTriplet(visit, "(", " ^= ", ")"); break; - case EOpBitwiseOrAssign: outputTriplet(visit, "(", " |= ", ")"); break; + case EOpDivAssign: + outputTriplet(out, visit, "(", " /= ", ")"); + break; + case EOpIModAssign: + outputTriplet(out, visit, "(", " %= ", ")"); + break; + case EOpBitShiftLeftAssign: + outputTriplet(out, visit, "(", " <<= ", ")"); + break; + case EOpBitShiftRightAssign: + outputTriplet(out, visit, "(", " >>= ", ")"); + break; + case EOpBitwiseAndAssign: + outputTriplet(out, visit, "(", " &= ", ")"); + break; + case EOpBitwiseXorAssign: + outputTriplet(out, visit, "(", " ^= ", ")"); + break; + case EOpBitwiseOrAssign: + outputTriplet(out, visit, "(", " |= ", ")"); + break; case EOpIndexDirect: { const TType& leftType = node->getLeft()->getType(); @@ -1551,14 +1719,14 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) } else { - outputTriplet(visit, "", "[", "]"); + outputTriplet(out, visit, "", "[", "]"); } } break; case EOpIndexIndirect: // We do not currently support indirect references to interface blocks ASSERT(node->getLeft()->getBasicType() != EbtInterfaceBlock); - outputTriplet(visit, "", "[", "]"); + outputTriplet(out, visit, "", "[", "]"); break; case EOpIndexDirectStruct: if (visit == InVisit) @@ -1618,55 +1786,81 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) return false; // Fully processed } break; - case EOpAdd: outputTriplet(visit, "(", " + ", ")"); break; - case EOpSub: outputTriplet(visit, "(", " - ", ")"); break; - case EOpMul: outputTriplet(visit, "(", " * ", ")"); break; - case EOpDiv: outputTriplet(visit, "(", " / ", ")"); break; - case EOpIMod: outputTriplet(visit, "(", " % ", ")"); break; - case EOpBitShiftLeft: outputTriplet(visit, "(", " << ", ")"); break; - case EOpBitShiftRight: outputTriplet(visit, "(", " >> ", ")"); break; - case EOpBitwiseAnd: outputTriplet(visit, "(", " & ", ")"); break; - case EOpBitwiseXor: outputTriplet(visit, "(", " ^ ", ")"); break; - case EOpBitwiseOr: outputTriplet(visit, "(", " | ", ")"); break; + case EOpAdd: + outputTriplet(out, visit, "(", " + ", ")"); + break; + case EOpSub: + outputTriplet(out, visit, "(", " - ", ")"); + break; + case EOpMul: + outputTriplet(out, visit, "(", " * ", ")"); + break; + case EOpDiv: + outputTriplet(out, visit, "(", " / ", ")"); + break; + case EOpIMod: + outputTriplet(out, visit, "(", " % ", ")"); + break; + case EOpBitShiftLeft: + outputTriplet(out, visit, "(", " << ", ")"); + break; + case EOpBitShiftRight: + outputTriplet(out, visit, "(", " >> ", ")"); + break; + case EOpBitwiseAnd: + outputTriplet(out, visit, "(", " & ", ")"); + break; + case EOpBitwiseXor: + outputTriplet(out, visit, "(", " ^ ", ")"); + break; + case EOpBitwiseOr: + outputTriplet(out, visit, "(", " | ", ")"); + break; case EOpEqual: case EOpNotEqual: outputEqual(visit, node->getLeft()->getType(), node->getOp(), out); break; - case EOpLessThan: outputTriplet(visit, "(", " < ", ")"); break; - case EOpGreaterThan: outputTriplet(visit, "(", " > ", ")"); break; - case EOpLessThanEqual: outputTriplet(visit, "(", " <= ", ")"); break; - case EOpGreaterThanEqual: outputTriplet(visit, "(", " >= ", ")"); break; - case EOpVectorTimesScalar: outputTriplet(visit, "(", " * ", ")"); break; - case EOpMatrixTimesScalar: outputTriplet(visit, "(", " * ", ")"); break; - case EOpVectorTimesMatrix: outputTriplet(visit, "mul(", ", transpose(", "))"); break; - case EOpMatrixTimesVector: outputTriplet(visit, "mul(transpose(", "), ", ")"); break; - case EOpMatrixTimesMatrix: outputTriplet(visit, "transpose(mul(transpose(", "), transpose(", ")))"); break; + case EOpLessThan: + outputTriplet(out, visit, "(", " < ", ")"); + break; + case EOpGreaterThan: + outputTriplet(out, visit, "(", " > ", ")"); + break; + case EOpLessThanEqual: + outputTriplet(out, visit, "(", " <= ", ")"); + break; + case EOpGreaterThanEqual: + outputTriplet(out, visit, "(", " >= ", ")"); + break; + case EOpVectorTimesScalar: + outputTriplet(out, visit, "(", " * ", ")"); + break; + case EOpMatrixTimesScalar: + outputTriplet(out, visit, "(", " * ", ")"); + break; + case EOpVectorTimesMatrix: + outputTriplet(out, visit, "mul(", ", transpose(", "))"); + break; + case EOpMatrixTimesVector: + outputTriplet(out, visit, "mul(transpose(", "), ", ")"); + break; + case EOpMatrixTimesMatrix: + outputTriplet(out, visit, "transpose(mul(transpose(", "), transpose(", ")))"); + break; case EOpLogicalOr: - if (node->getRight()->hasSideEffects()) - { - out << "s" << mUnfoldShortCircuit->getNextTemporaryIndex(); - return false; - } - else - { - outputTriplet(visit, "(", " || ", ")"); - return true; - } + // HLSL doesn't short-circuit ||, so we assume that || affected by short-circuiting have been unfolded. + ASSERT(!node->getRight()->hasSideEffects()); + outputTriplet(out, visit, "(", " || ", ")"); + return true; case EOpLogicalXor: mUsesXor = true; - outputTriplet(visit, "xor(", ", ", ")"); + outputTriplet(out, visit, "xor(", ", ", ")"); break; case EOpLogicalAnd: - if (node->getRight()->hasSideEffects()) - { - out << "s" << mUnfoldShortCircuit->getNextTemporaryIndex(); - return false; - } - else - { - outputTriplet(visit, "(", " && ", ")"); - return true; - } + // HLSL doesn't short-circuit &&, so we assume that && affected by short-circuiting have been unfolded. + ASSERT(!node->getRight()->hasSideEffects()); + outputTriplet(out, visit, "(", " && ", ")"); + return true; default: UNREACHABLE(); } @@ -1675,131 +1869,221 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node) { + TInfoSinkBase &out = getInfoSink(); + switch (node->getOp()) { - case EOpNegative: outputTriplet(visit, "(-", "", ")"); break; - case EOpPositive: outputTriplet(visit, "(+", "", ")"); break; - case EOpVectorLogicalNot: outputTriplet(visit, "(!", "", ")"); break; - case EOpLogicalNot: outputTriplet(visit, "(!", "", ")"); break; - case EOpBitwiseNot: outputTriplet(visit, "(~", "", ")"); break; - case EOpPostIncrement: outputTriplet(visit, "(", "", "++)"); break; - case EOpPostDecrement: outputTriplet(visit, "(", "", "--)"); break; - case EOpPreIncrement: outputTriplet(visit, "(++", "", ")"); break; - case EOpPreDecrement: outputTriplet(visit, "(--", "", ")"); break; - case EOpRadians: outputTriplet(visit, "radians(", "", ")"); break; - case EOpDegrees: outputTriplet(visit, "degrees(", "", ")"); break; - case EOpSin: outputTriplet(visit, "sin(", "", ")"); break; - case EOpCos: outputTriplet(visit, "cos(", "", ")"); break; - case EOpTan: outputTriplet(visit, "tan(", "", ")"); break; - case EOpAsin: outputTriplet(visit, "asin(", "", ")"); break; - case EOpAcos: outputTriplet(visit, "acos(", "", ")"); break; - case EOpAtan: outputTriplet(visit, "atan(", "", ")"); break; - case EOpSinh: outputTriplet(visit, "sinh(", "", ")"); break; - case EOpCosh: outputTriplet(visit, "cosh(", "", ")"); break; - case EOpTanh: outputTriplet(visit, "tanh(", "", ")"); break; + case EOpNegative: + outputTriplet(out, visit, "(-", "", ")"); + break; + case EOpPositive: + outputTriplet(out, visit, "(+", "", ")"); + break; + case EOpVectorLogicalNot: + outputTriplet(out, visit, "(!", "", ")"); + break; + case EOpLogicalNot: + outputTriplet(out, visit, "(!", "", ")"); + break; + case EOpBitwiseNot: + outputTriplet(out, visit, "(~", "", ")"); + break; + case EOpPostIncrement: + outputTriplet(out, visit, "(", "", "++)"); + break; + case EOpPostDecrement: + outputTriplet(out, visit, "(", "", "--)"); + break; + case EOpPreIncrement: + outputTriplet(out, visit, "(++", "", ")"); + break; + case EOpPreDecrement: + outputTriplet(out, visit, "(--", "", ")"); + break; + case EOpRadians: + outputTriplet(out, visit, "radians(", "", ")"); + break; + case EOpDegrees: + outputTriplet(out, visit, "degrees(", "", ")"); + break; + case EOpSin: + outputTriplet(out, visit, "sin(", "", ")"); + break; + case EOpCos: + outputTriplet(out, visit, "cos(", "", ")"); + break; + case EOpTan: + outputTriplet(out, visit, "tan(", "", ")"); + break; + case EOpAsin: + outputTriplet(out, visit, "asin(", "", ")"); + break; + case EOpAcos: + outputTriplet(out, visit, "acos(", "", ")"); + break; + case EOpAtan: + outputTriplet(out, visit, "atan(", "", ")"); + break; + case EOpSinh: + outputTriplet(out, visit, "sinh(", "", ")"); + break; + case EOpCosh: + outputTriplet(out, visit, "cosh(", "", ")"); + break; + case EOpTanh: + outputTriplet(out, visit, "tanh(", "", ")"); + break; case EOpAsinh: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "asinh("); + writeEmulatedFunctionTriplet(out, visit, "asinh("); break; case EOpAcosh: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "acosh("); + writeEmulatedFunctionTriplet(out, visit, "acosh("); break; case EOpAtanh: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "atanh("); + writeEmulatedFunctionTriplet(out, visit, "atanh("); break; - case EOpExp: outputTriplet(visit, "exp(", "", ")"); break; - case EOpLog: outputTriplet(visit, "log(", "", ")"); break; - case EOpExp2: outputTriplet(visit, "exp2(", "", ")"); break; - case EOpLog2: outputTriplet(visit, "log2(", "", ")"); break; - case EOpSqrt: outputTriplet(visit, "sqrt(", "", ")"); break; - case EOpInverseSqrt: outputTriplet(visit, "rsqrt(", "", ")"); break; - case EOpAbs: outputTriplet(visit, "abs(", "", ")"); break; - case EOpSign: outputTriplet(visit, "sign(", "", ")"); break; - case EOpFloor: outputTriplet(visit, "floor(", "", ")"); break; - case EOpTrunc: outputTriplet(visit, "trunc(", "", ")"); break; - case EOpRound: outputTriplet(visit, "round(", "", ")"); break; + case EOpExp: + outputTriplet(out, visit, "exp(", "", ")"); + break; + case EOpLog: + outputTriplet(out, visit, "log(", "", ")"); + break; + case EOpExp2: + outputTriplet(out, visit, "exp2(", "", ")"); + break; + case EOpLog2: + outputTriplet(out, visit, "log2(", "", ")"); + break; + case EOpSqrt: + outputTriplet(out, visit, "sqrt(", "", ")"); + break; + case EOpInverseSqrt: + outputTriplet(out, visit, "rsqrt(", "", ")"); + break; + case EOpAbs: + outputTriplet(out, visit, "abs(", "", ")"); + break; + case EOpSign: + outputTriplet(out, visit, "sign(", "", ")"); + break; + case EOpFloor: + outputTriplet(out, visit, "floor(", "", ")"); + break; + case EOpTrunc: + outputTriplet(out, visit, "trunc(", "", ")"); + break; + case EOpRound: + outputTriplet(out, visit, "round(", "", ")"); + break; case EOpRoundEven: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "roundEven("); + writeEmulatedFunctionTriplet(out, visit, "roundEven("); break; - case EOpCeil: outputTriplet(visit, "ceil(", "", ")"); break; - case EOpFract: outputTriplet(visit, "frac(", "", ")"); break; + case EOpCeil: + outputTriplet(out, visit, "ceil(", "", ")"); + break; + case EOpFract: + outputTriplet(out, visit, "frac(", "", ")"); + break; case EOpIsNan: - outputTriplet(visit, "isnan(", "", ")"); + outputTriplet(out, visit, "isnan(", "", ")"); mRequiresIEEEStrictCompiling = true; break; - case EOpIsInf: outputTriplet(visit, "isinf(", "", ")"); break; - case EOpFloatBitsToInt: outputTriplet(visit, "asint(", "", ")"); break; - case EOpFloatBitsToUint: outputTriplet(visit, "asuint(", "", ")"); break; - case EOpIntBitsToFloat: outputTriplet(visit, "asfloat(", "", ")"); break; - case EOpUintBitsToFloat: outputTriplet(visit, "asfloat(", "", ")"); break; + case EOpIsInf: + outputTriplet(out, visit, "isinf(", "", ")"); + break; + case EOpFloatBitsToInt: + outputTriplet(out, visit, "asint(", "", ")"); + break; + case EOpFloatBitsToUint: + outputTriplet(out, visit, "asuint(", "", ")"); + break; + case EOpIntBitsToFloat: + outputTriplet(out, visit, "asfloat(", "", ")"); + break; + case EOpUintBitsToFloat: + outputTriplet(out, visit, "asfloat(", "", ")"); + break; case EOpPackSnorm2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "packSnorm2x16("); + writeEmulatedFunctionTriplet(out, visit, "packSnorm2x16("); break; case EOpPackUnorm2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "packUnorm2x16("); + writeEmulatedFunctionTriplet(out, visit, "packUnorm2x16("); break; case EOpPackHalf2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "packHalf2x16("); + writeEmulatedFunctionTriplet(out, visit, "packHalf2x16("); break; case EOpUnpackSnorm2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "unpackSnorm2x16("); + writeEmulatedFunctionTriplet(out, visit, "unpackSnorm2x16("); break; case EOpUnpackUnorm2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "unpackUnorm2x16("); + writeEmulatedFunctionTriplet(out, visit, "unpackUnorm2x16("); break; case EOpUnpackHalf2x16: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "unpackHalf2x16("); + writeEmulatedFunctionTriplet(out, visit, "unpackHalf2x16("); break; - case EOpLength: outputTriplet(visit, "length(", "", ")"); break; - case EOpNormalize: outputTriplet(visit, "normalize(", "", ")"); break; + case EOpLength: + outputTriplet(out, visit, "length(", "", ")"); + break; + case EOpNormalize: + outputTriplet(out, visit, "normalize(", "", ")"); + break; case EOpDFdx: if(mInsideDiscontinuousLoop || mOutputLod0Function) { - outputTriplet(visit, "(", "", ", 0.0)"); + outputTriplet(out, visit, "(", "", ", 0.0)"); } else { - outputTriplet(visit, "ddx(", "", ")"); + outputTriplet(out, visit, "ddx(", "", ")"); } break; case EOpDFdy: if(mInsideDiscontinuousLoop || mOutputLod0Function) { - outputTriplet(visit, "(", "", ", 0.0)"); + outputTriplet(out, visit, "(", "", ", 0.0)"); } else { - outputTriplet(visit, "ddy(", "", ")"); + outputTriplet(out, visit, "ddy(", "", ")"); } break; case EOpFwidth: if(mInsideDiscontinuousLoop || mOutputLod0Function) { - outputTriplet(visit, "(", "", ", 0.0)"); + outputTriplet(out, visit, "(", "", ", 0.0)"); } else { - outputTriplet(visit, "fwidth(", "", ")"); + outputTriplet(out, visit, "fwidth(", "", ")"); } break; - case EOpTranspose: outputTriplet(visit, "transpose(", "", ")"); break; - case EOpDeterminant: outputTriplet(visit, "determinant(transpose(", "", "))"); break; + case EOpTranspose: + outputTriplet(out, visit, "transpose(", "", ")"); + break; + case EOpDeterminant: + outputTriplet(out, visit, "determinant(transpose(", "", "))"); + break; case EOpInverse: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "inverse("); + writeEmulatedFunctionTriplet(out, visit, "inverse("); break; - case EOpAny: outputTriplet(visit, "any(", "", ")"); break; - case EOpAll: outputTriplet(visit, "all(", "", ")"); break; + case EOpAny: + outputTriplet(out, visit, "any(", "", ")"); + break; + case EOpAll: + outputTriplet(out, visit, "all(", "", ")"); + break; default: UNREACHABLE(); } @@ -1816,26 +2100,30 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) { if (mInsideFunction) { - outputLineDirective(node->getLine().first_line); + outputLineDirective(out, node->getLine().first_line); out << "{\n"; } for (TIntermSequence::iterator sit = node->getSequence()->begin(); sit != node->getSequence()->end(); sit++) { - outputLineDirective((*sit)->getLine().first_line); + outputLineDirective(out, (*sit)->getLine().first_line); - traverseStatements(*sit); + (*sit)->traverse(this); // Don't output ; after case labels, they're terminated by : // This is needed especially since outputting a ; after a case statement would turn empty // case statements into non-empty case statements, disallowing fall-through from them. - if ((*sit)->getAsCaseNode() == nullptr) + // Also no need to output ; after selection (if) statements or sequences. This is done just + // for code clarity. + TIntermSelection *asSelection = (*sit)->getAsSelectionNode(); + ASSERT(asSelection == nullptr || !asSelection->usesTernaryOperator()); + if ((*sit)->getAsCaseNode() == nullptr && asSelection == nullptr && !IsSequence(*sit)) out << ";\n"; } if (mInsideFunction) { - outputLineDirective(node->getLine().last_line); + outputLineDirective(out, node->getLine().last_line); out << "}\n"; } @@ -1846,50 +2134,34 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) { TIntermSequence *sequence = node->getSequence(); TIntermTyped *variable = (*sequence)[0]->getAsTyped(); + ASSERT(sequence->size() == 1); - if (variable && (variable->getQualifier() == EvqTemporary || variable->getQualifier() == EvqGlobal)) + if (variable && + (variable->getQualifier() == EvqTemporary || + variable->getQualifier() == EvqGlobal || variable->getQualifier() == EvqConst)) { - TStructure *structure = variable->getType().getStruct(); - - if (structure) - { - mStructureHLSL->addConstructor(variable->getType(), StructNameString(*structure), NULL); - } + ensureStructDefined(variable->getType()); if (!variable->getAsSymbolNode() || variable->getAsSymbolNode()->getSymbol() != "") // Variable declaration { - for (auto it = sequence->cbegin(); it != sequence->cend(); ++it) + if (!mInsideFunction) { - const auto &seqElement = *it; - if (isSingleStatement(seqElement)) - { - mUnfoldShortCircuit->traverse(seqElement); - } - - if (!mInsideFunction) - { - out << "static "; - } + out << "static "; + } - out << TypeString(variable->getType()) + " "; + out << TypeString(variable->getType()) + " "; - TIntermSymbol *symbol = seqElement->getAsSymbolNode(); + TIntermSymbol *symbol = variable->getAsSymbolNode(); - if (symbol) - { - symbol->traverse(this); - out << ArrayString(symbol->getType()); - out << " = " + initializer(symbol->getType()); - } - else - { - seqElement->traverse(this); - } - - if (seqElement != sequence->back()) - { - out << ";\n"; - } + if (symbol) + { + symbol->traverse(this); + out << ArrayString(symbol->getType()); + out << " = " + initializer(symbol->getType()); + } + else + { + variable->traverse(this); } } else if (variable->getAsSymbolNode() && variable->getAsSymbolNode()->getSymbol() == "") // Type (struct) declaration @@ -1929,7 +2201,16 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) case EOpPrototype: if (visit == PreVisit) { - out << TypeString(node->getType()) << " " << Decorate(TFunction::unmangleName(node->getName())) << (mOutputLod0Function ? "Lod0(" : "("); + size_t index = mCallDag.findIndex(node); + // Skip the prototype if it is not implemented (and thus not used) + if (index == CallDAG::InvalidIndex) + { + return false; + } + + TString name = DecorateFunctionIfNeeded(node->getNameObj()); + out << TypeString(node->getType()) << " " << name + << (mOutputLod0Function ? "Lod0(" : "("); TIntermSequence *arguments = node->getSequence(); @@ -1952,7 +2233,8 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) out << ");\n"; // Also prototype the Lod0 variant if needed - if (mContainsLoopDiscontinuity && !mOutputLod0Function) + bool needsLod0 = mASTMetadataList[index].mNeedsLod0; + if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER) { mOutputLod0Function = true; node->traverse(this); @@ -1962,10 +2244,17 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) return false; } break; - case EOpComma: outputTriplet(visit, "(", ", ", ")"); break; + case EOpComma: + outputTriplet(out, visit, "(", ", ", ")"); + break; case EOpFunction: { - TString name = TFunction::unmangleName(node->getName()); + ASSERT(mCurrentFunctionMetadata == nullptr); + TString name = TFunction::unmangleName(node->getNameObj().getString()); + + size_t index = mCallDag.findIndex(node); + ASSERT(index != CallDAG::InvalidIndex); + mCurrentFunctionMetadata = &mASTMetadataList[index]; out << TypeString(node->getType()) << " "; @@ -1975,7 +2264,8 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) } else { - out << Decorate(name) << (mOutputLod0Function ? "Lod0(" : "("); + out << DecorateFunctionIfNeeded(node->getNameObj()) + << (mOutputLod0Function ? "Lod0(" : "("); } TIntermSequence *sequence = node->getSequence(); @@ -1987,12 +2277,7 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) if (symbol) { - TStructure *structure = symbol->getType().getStruct(); - - if (structure) - { - mStructureHLSL->addConstructor(symbol->getType(), StructNameString(*structure), NULL); - } + ensureStructDefined(symbol->getType()); out << argumentString(symbol); @@ -2004,26 +2289,31 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) else UNREACHABLE(); } - out << ")\n" - "{\n"; + out << ")\n"; if (sequence->size() > 1) { mInsideFunction = true; - (*sequence)[1]->traverse(this); + TIntermNode *body = (*sequence)[1]; + // The function body node will output braces. + ASSERT(IsSequence(body)); + body->traverse(this); mInsideFunction = false; } + else + { + out << "{}\n"; + } - out << "}\n"; + mCurrentFunctionMetadata = nullptr; - if (mContainsLoopDiscontinuity && !mOutputLod0Function) + bool needsLod0 = mASTMetadataList[index].mNeedsLod0; + if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER) { - if (name != "main") - { - mOutputLod0Function = true; - node->traverse(this); - mOutputLod0Function = false; - } + ASSERT(name != "main"); + mOutputLod0Function = true; + node->traverse(this); + mOutputLod0Function = false; } return false; @@ -2031,16 +2321,24 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) break; case EOpFunctionCall: { - TString name = TFunction::unmangleName(node->getName()); - bool lod0 = mInsideDiscontinuousLoop || mOutputLod0Function; TIntermSequence *arguments = node->getSequence(); + bool lod0 = mInsideDiscontinuousLoop || mOutputLod0Function; if (node->isUserDefined()) { - out << Decorate(name) << (lod0 ? "Lod0(" : "("); + if (node->isArray()) + { + UNIMPLEMENTED(); + } + size_t index = mCallDag.findIndex(node); + ASSERT(index != CallDAG::InvalidIndex); + lod0 &= mASTMetadataList[index].mNeedsLod0; + + out << DecorateFunctionIfNeeded(node->getNameObj()) << (lod0 ? "Lod0(" : "("); } else { + TString name = TFunction::unmangleName(node->getNameObj().getString()); TBasicType samplerType = (*arguments)[0]->getAsTyped()->getType().getBasicType(); TextureFunction textureFunction; @@ -2161,7 +2459,8 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) for (TIntermSequence::iterator arg = arguments->begin(); arg != arguments->end(); arg++) { - if (mOutputType == SH_HLSL11_OUTPUT && IsSampler((*arg)->getAsTyped()->getBasicType())) + if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT && + IsSampler((*arg)->getAsTyped()->getBasicType())) { out << "texture_"; (*arg)->traverse(this); @@ -2181,172 +2480,299 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) return false; } break; - case EOpParameters: outputTriplet(visit, "(", ", ", ")\n{\n"); break; - case EOpConstructFloat: outputConstructor(visit, node->getType(), "vec1", node->getSequence()); break; - case EOpConstructVec2: outputConstructor(visit, node->getType(), "vec2", node->getSequence()); break; - case EOpConstructVec3: outputConstructor(visit, node->getType(), "vec3", node->getSequence()); break; - case EOpConstructVec4: outputConstructor(visit, node->getType(), "vec4", node->getSequence()); break; - case EOpConstructBool: outputConstructor(visit, node->getType(), "bvec1", node->getSequence()); break; - case EOpConstructBVec2: outputConstructor(visit, node->getType(), "bvec2", node->getSequence()); break; - case EOpConstructBVec3: outputConstructor(visit, node->getType(), "bvec3", node->getSequence()); break; - case EOpConstructBVec4: outputConstructor(visit, node->getType(), "bvec4", node->getSequence()); break; - case EOpConstructInt: outputConstructor(visit, node->getType(), "ivec1", node->getSequence()); break; - case EOpConstructIVec2: outputConstructor(visit, node->getType(), "ivec2", node->getSequence()); break; - case EOpConstructIVec3: outputConstructor(visit, node->getType(), "ivec3", node->getSequence()); break; - case EOpConstructIVec4: outputConstructor(visit, node->getType(), "ivec4", node->getSequence()); break; - case EOpConstructUInt: outputConstructor(visit, node->getType(), "uvec1", node->getSequence()); break; - case EOpConstructUVec2: outputConstructor(visit, node->getType(), "uvec2", node->getSequence()); break; - case EOpConstructUVec3: outputConstructor(visit, node->getType(), "uvec3", node->getSequence()); break; - case EOpConstructUVec4: outputConstructor(visit, node->getType(), "uvec4", node->getSequence()); break; - case EOpConstructMat2: outputConstructor(visit, node->getType(), "mat2", node->getSequence()); break; - case EOpConstructMat3: outputConstructor(visit, node->getType(), "mat3", node->getSequence()); break; - case EOpConstructMat4: outputConstructor(visit, node->getType(), "mat4", node->getSequence()); break; + case EOpParameters: + outputTriplet(out, visit, "(", ", ", ")\n{\n"); + break; + case EOpConstructFloat: + outputConstructor(out, visit, node->getType(), "vec1", node->getSequence()); + break; + case EOpConstructVec2: + outputConstructor(out, visit, node->getType(), "vec2", node->getSequence()); + break; + case EOpConstructVec3: + outputConstructor(out, visit, node->getType(), "vec3", node->getSequence()); + break; + case EOpConstructVec4: + outputConstructor(out, visit, node->getType(), "vec4", node->getSequence()); + break; + case EOpConstructBool: + outputConstructor(out, visit, node->getType(), "bvec1", node->getSequence()); + break; + case EOpConstructBVec2: + outputConstructor(out, visit, node->getType(), "bvec2", node->getSequence()); + break; + case EOpConstructBVec3: + outputConstructor(out, visit, node->getType(), "bvec3", node->getSequence()); + break; + case EOpConstructBVec4: + outputConstructor(out, visit, node->getType(), "bvec4", node->getSequence()); + break; + case EOpConstructInt: + outputConstructor(out, visit, node->getType(), "ivec1", node->getSequence()); + break; + case EOpConstructIVec2: + outputConstructor(out, visit, node->getType(), "ivec2", node->getSequence()); + break; + case EOpConstructIVec3: + outputConstructor(out, visit, node->getType(), "ivec3", node->getSequence()); + break; + case EOpConstructIVec4: + outputConstructor(out, visit, node->getType(), "ivec4", node->getSequence()); + break; + case EOpConstructUInt: + outputConstructor(out, visit, node->getType(), "uvec1", node->getSequence()); + break; + case EOpConstructUVec2: + outputConstructor(out, visit, node->getType(), "uvec2", node->getSequence()); + break; + case EOpConstructUVec3: + outputConstructor(out, visit, node->getType(), "uvec3", node->getSequence()); + break; + case EOpConstructUVec4: + outputConstructor(out, visit, node->getType(), "uvec4", node->getSequence()); + break; + case EOpConstructMat2: + outputConstructor(out, visit, node->getType(), "mat2", node->getSequence()); + break; + case EOpConstructMat2x3: + outputConstructor(out, visit, node->getType(), "mat2x3", node->getSequence()); + break; + case EOpConstructMat2x4: + outputConstructor(out, visit, node->getType(), "mat2x4", node->getSequence()); + break; + case EOpConstructMat3x2: + outputConstructor(out, visit, node->getType(), "mat3x2", node->getSequence()); + break; + case EOpConstructMat3: + outputConstructor(out, visit, node->getType(), "mat3", node->getSequence()); + break; + case EOpConstructMat3x4: + outputConstructor(out, visit, node->getType(), "mat3x4", node->getSequence()); + break; + case EOpConstructMat4x2: + outputConstructor(out, visit, node->getType(), "mat4x2", node->getSequence()); + break; + case EOpConstructMat4x3: + outputConstructor(out, visit, node->getType(), "mat4x3", node->getSequence()); + break; + case EOpConstructMat4: + outputConstructor(out, visit, node->getType(), "mat4", node->getSequence()); + break; case EOpConstructStruct: { + if (node->getType().isArray()) + { + UNIMPLEMENTED(); + } const TString &structName = StructNameString(*node->getType().getStruct()); mStructureHLSL->addConstructor(node->getType(), structName, node->getSequence()); - outputTriplet(visit, (structName + "_ctor(").c_str(), ", ", ")"); + outputTriplet(out, visit, (structName + "_ctor(").c_str(), ", ", ")"); } break; - case EOpLessThan: outputTriplet(visit, "(", " < ", ")"); break; - case EOpGreaterThan: outputTriplet(visit, "(", " > ", ")"); break; - case EOpLessThanEqual: outputTriplet(visit, "(", " <= ", ")"); break; - case EOpGreaterThanEqual: outputTriplet(visit, "(", " >= ", ")"); break; - case EOpVectorEqual: outputTriplet(visit, "(", " == ", ")"); break; - case EOpVectorNotEqual: outputTriplet(visit, "(", " != ", ")"); break; + case EOpLessThan: + outputTriplet(out, visit, "(", " < ", ")"); + break; + case EOpGreaterThan: + outputTriplet(out, visit, "(", " > ", ")"); + break; + case EOpLessThanEqual: + outputTriplet(out, visit, "(", " <= ", ")"); + break; + case EOpGreaterThanEqual: + outputTriplet(out, visit, "(", " >= ", ")"); + break; + case EOpVectorEqual: + outputTriplet(out, visit, "(", " == ", ")"); + break; + case EOpVectorNotEqual: + outputTriplet(out, visit, "(", " != ", ")"); + break; case EOpMod: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "mod("); + writeEmulatedFunctionTriplet(out, visit, "mod("); break; - case EOpModf: outputTriplet(visit, "modf(", ", ", ")"); break; - case EOpPow: outputTriplet(visit, "pow(", ", ", ")"); break; + case EOpModf: + outputTriplet(out, visit, "modf(", ", ", ")"); + break; + case EOpPow: + outputTriplet(out, visit, "pow(", ", ", ")"); + break; case EOpAtan: ASSERT(node->getSequence()->size() == 2); // atan(x) is a unary operator ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "atan("); + writeEmulatedFunctionTriplet(out, visit, "atan("); + break; + case EOpMin: + outputTriplet(out, visit, "min(", ", ", ")"); + break; + case EOpMax: + outputTriplet(out, visit, "max(", ", ", ")"); + break; + case EOpClamp: + outputTriplet(out, visit, "clamp(", ", ", ")"); + break; + case EOpMix: + { + TIntermTyped *lastParamNode = (*(node->getSequence()))[2]->getAsTyped(); + if (lastParamNode->getType().getBasicType() == EbtBool) + { + // There is no HLSL equivalent for ESSL3 built-in "genType mix (genType x, genType y, genBType a)", + // so use emulated version. + ASSERT(node->getUseEmulatedFunction()); + writeEmulatedFunctionTriplet(out, visit, "mix("); + } + else + { + outputTriplet(out, visit, "lerp(", ", ", ")"); + } + } break; - case EOpMin: outputTriplet(visit, "min(", ", ", ")"); break; - case EOpMax: outputTriplet(visit, "max(", ", ", ")"); break; - case EOpClamp: outputTriplet(visit, "clamp(", ", ", ")"); break; - case EOpMix: outputTriplet(visit, "lerp(", ", ", ")"); break; - case EOpStep: outputTriplet(visit, "step(", ", ", ")"); break; - case EOpSmoothStep: outputTriplet(visit, "smoothstep(", ", ", ")"); break; - case EOpDistance: outputTriplet(visit, "distance(", ", ", ")"); break; - case EOpDot: outputTriplet(visit, "dot(", ", ", ")"); break; - case EOpCross: outputTriplet(visit, "cross(", ", ", ")"); break; + case EOpStep: + outputTriplet(out, visit, "step(", ", ", ")"); + break; + case EOpSmoothStep: + outputTriplet(out, visit, "smoothstep(", ", ", ")"); + break; + case EOpDistance: + outputTriplet(out, visit, "distance(", ", ", ")"); + break; + case EOpDot: + outputTriplet(out, visit, "dot(", ", ", ")"); + break; + case EOpCross: + outputTriplet(out, visit, "cross(", ", ", ")"); + break; case EOpFaceForward: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "faceforward("); + writeEmulatedFunctionTriplet(out, visit, "faceforward("); break; - case EOpReflect: outputTriplet(visit, "reflect(", ", ", ")"); break; - case EOpRefract: outputTriplet(visit, "refract(", ", ", ")"); break; + case EOpReflect: + outputTriplet(out, visit, "reflect(", ", ", ")"); + break; + case EOpRefract: + outputTriplet(out, visit, "refract(", ", ", ")"); + break; case EOpOuterProduct: ASSERT(node->getUseEmulatedFunction()); - writeEmulatedFunctionTriplet(visit, "outerProduct("); + writeEmulatedFunctionTriplet(out, visit, "outerProduct("); break; - case EOpMul: outputTriplet(visit, "(", " * ", ")"); break; + case EOpMul: + outputTriplet(out, visit, "(", " * ", ")"); + break; default: UNREACHABLE(); } return true; } -bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node) +void OutputHLSL::writeSelection(TInfoSinkBase &out, TIntermSelection *node) { - TInfoSinkBase &out = getInfoSink(); + out << "if ("; - if (node->usesTernaryOperator()) - { - out << "s" << mUnfoldShortCircuit->getNextTemporaryIndex(); - } - else // if/else statement - { - mUnfoldShortCircuit->traverse(node->getCondition()); + node->getCondition()->traverse(this); - // D3D errors when there is a gradient operation in a loop in an unflattened if - // however flattening all the ifs in branch heavy shaders made D3D error too. - // As a temporary workaround we flatten the ifs only if there is at least a loop - // present somewhere in the shader. - if (mShaderType == GL_FRAGMENT_SHADER && mContainsAnyLoop) - { - out << "FLATTEN "; - } + out << ")\n"; - out << "if ("; + outputLineDirective(out, node->getLine().first_line); - node->getCondition()->traverse(this); + bool discard = false; - out << ")\n"; + if (node->getTrueBlock()) + { + // The trueBlock child node will output braces. + ASSERT(IsSequence(node->getTrueBlock())); - outputLineDirective(node->getLine().first_line); - out << "{\n"; + node->getTrueBlock()->traverse(this); - bool discard = false; + // Detect true discard + discard = (discard || FindDiscard::search(node->getTrueBlock())); + } + else + { + // TODO(oetuaho): Check if the semicolon inside is necessary. + // It's there as a result of conservative refactoring of the output. + out << "{;}\n"; + } - if (node->getTrueBlock()) - { - traverseStatements(node->getTrueBlock()); + outputLineDirective(out, node->getLine().first_line); - // Detect true discard - discard = (discard || FindDiscard::search(node->getTrueBlock())); - } + if (node->getFalseBlock()) + { + out << "else\n"; - outputLineDirective(node->getLine().first_line); - out << ";\n}\n"; + outputLineDirective(out, node->getFalseBlock()->getLine().first_line); - if (node->getFalseBlock()) - { - out << "else\n"; + // Either this is "else if" or the falseBlock child node will output braces. + ASSERT(IsSequence(node->getFalseBlock()) || node->getFalseBlock()->getAsSelectionNode() != nullptr); - outputLineDirective(node->getFalseBlock()->getLine().first_line); - out << "{\n"; + node->getFalseBlock()->traverse(this); - outputLineDirective(node->getFalseBlock()->getLine().first_line); - traverseStatements(node->getFalseBlock()); + outputLineDirective(out, node->getFalseBlock()->getLine().first_line); - outputLineDirective(node->getFalseBlock()->getLine().first_line); - out << ";\n}\n"; + // Detect false discard + discard = (discard || FindDiscard::search(node->getFalseBlock())); + } - // Detect false discard - discard = (discard || FindDiscard::search(node->getFalseBlock())); - } + // ANGLE issue 486: Detect problematic conditional discard + if (discard) + { + mUsesDiscardRewriting = true; + } +} - // ANGLE issue 486: Detect problematic conditional discard - if (discard && FindSideEffectRewriting::search(node)) - { - mUsesDiscardRewriting = true; - } +bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node) +{ + TInfoSinkBase &out = getInfoSink(); + + ASSERT(!node->usesTernaryOperator()); + + if (!mInsideFunction) + { + // This is part of unfolded global initialization. + mDeferredGlobalInitializers.push_back(node); + return false; } + // D3D errors when there is a gradient operation in a loop in an unflattened if. + if (mShaderType == GL_FRAGMENT_SHADER && mCurrentFunctionMetadata->hasGradientLoop(node)) + { + out << "FLATTEN "; + } + + writeSelection(out, node); + return false; } bool OutputHLSL::visitSwitch(Visit visit, TIntermSwitch *node) { + TInfoSinkBase &out = getInfoSink(); + if (node->getStatementList()) { node->setStatementList(RemoveSwitchFallThrough::removeFallThrough(node->getStatementList())); - outputTriplet(visit, "switch (", ") ", ""); + outputTriplet(out, visit, "switch (", ") ", ""); // The curly braces get written when visiting the statementList aggregate } else { // No statementList, so it won't output curly braces - outputTriplet(visit, "switch (", ") {", "}\n"); + outputTriplet(out, visit, "switch (", ") {", "}\n"); } return true; } bool OutputHLSL::visitCase(Visit visit, TIntermCase *node) { + TInfoSinkBase &out = getInfoSink(); + if (node->hasCondition()) { - outputTriplet(visit, "case (", "", "):\n"); + outputTriplet(out, visit, "case (", "", "):\n"); return true; } else { - TInfoSinkBase &out = getInfoSink(); out << "default:\n"; return false; } @@ -2354,7 +2780,8 @@ bool OutputHLSL::visitCase(Visit visit, TIntermCase *node) void OutputHLSL::visitConstantUnion(TIntermConstantUnion *node) { - writeConstantUnion(node->getType(), node->getUnionArrayPointer()); + TInfoSinkBase &out = getInfoSink(); + writeConstantUnion(out, node->getType(), node->getUnionArrayPointer()); } bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) @@ -2362,15 +2789,14 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) mNestedLoopDepth++; bool wasDiscontinuous = mInsideDiscontinuousLoop; + mInsideDiscontinuousLoop = mInsideDiscontinuousLoop || + mCurrentFunctionMetadata->mDiscontinuousLoops.count(node) > 0; - if (mContainsLoopDiscontinuity && !mInsideDiscontinuousLoop) - { - mInsideDiscontinuousLoop = containsLoopDiscontinuity(node); - } + TInfoSinkBase &out = getInfoSink(); - if (mOutputType == SH_HLSL9_OUTPUT) + if (mOutputType == SH_HLSL_3_0_OUTPUT) { - if (handleExcessiveLoop(node)) + if (handleExcessiveLoop(out, node)) { mInsideDiscontinuousLoop = wasDiscontinuous; mNestedLoopDepth--; @@ -2379,18 +2805,16 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) } } - TInfoSinkBase &out = getInfoSink(); - + const char *unroll = mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : ""; if (node->getType() == ELoopDoWhile) { - out << "{LOOP do\n"; + out << "{" << unroll << " do\n"; - outputLineDirective(node->getLine().first_line); - out << "{\n"; + outputLineDirective(out, node->getLine().first_line); } else { - out << "{LOOP for("; + out << "{" << unroll << " for("; if (node->getInit()) { @@ -2413,21 +2837,27 @@ bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) out << ")\n"; - outputLineDirective(node->getLine().first_line); - out << "{\n"; + outputLineDirective(out, node->getLine().first_line); } if (node->getBody()) { - traverseStatements(node->getBody()); + // The loop body node will output braces. + ASSERT(IsSequence(node->getBody())); + node->getBody()->traverse(this); + } + else + { + // TODO(oetuaho): Check if the semicolon inside is necessary. + // It's there as a result of conservative refactoring of the output. + out << "{;}\n"; } - outputLineDirective(node->getLine().first_line); - out << ";}\n"; + outputLineDirective(out, node->getLine().first_line); if (node->getType() == ELoopDoWhile) { - outputLineDirective(node->getCondition()->getLine().first_line); + outputLineDirective(out, node->getCondition()->getLine().first_line); out << "while(\n"; node->getCondition()->traverse(this); @@ -2450,7 +2880,7 @@ bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node) switch (node->getFlowOp()) { case EOpKill: - outputTriplet(visit, "discard;\n", "", ""); + outputTriplet(out, visit, "discard;\n", "", ""); break; case EOpBreak: if (visit == PreVisit) @@ -2472,7 +2902,9 @@ bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node) } } break; - case EOpContinue: outputTriplet(visit, "continue;\n", "", ""); break; + case EOpContinue: + outputTriplet(out, visit, "continue;\n", "", ""); + break; case EOpReturn: if (visit == PreVisit) { @@ -2499,16 +2931,6 @@ bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node) return true; } -void OutputHLSL::traverseStatements(TIntermNode *node) -{ - if (isSingleStatement(node)) - { - mUnfoldShortCircuit->traverse(node); - } - - node->traverse(this); -} - bool OutputHLSL::isSingleStatement(TIntermNode *node) { TIntermAggregate *aggregate = node->getAsAggregate(); @@ -2544,10 +2966,9 @@ bool OutputHLSL::isSingleStatement(TIntermNode *node) // Handle loops with more than 254 iterations (unsupported by D3D9) by splitting them // (The D3D documentation says 255 iterations, but the compiler complains at anything more than 254). -bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node) +bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) { const int MAX_LOOP_ITERATIONS = 254; - TInfoSinkBase &out = getInfoSink(); // Parse loops of the form: // for(int index = initial; index [comparator] limit; index += increment) @@ -2696,8 +3117,9 @@ bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node) } // for(int index = initial; index < clampedLimit; index += increment) + const char *unroll = mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : ""; - out << "LOOP for("; + out << unroll << " for("; index->traverse(this); out << " = "; out << initial; @@ -2713,7 +3135,7 @@ bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node) out << increment; out << ")\n"; - outputLineDirective(node->getLine().first_line); + outputLineDirective(out, node->getLine().first_line); out << "{\n"; if (node->getBody()) @@ -2721,7 +3143,7 @@ bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node) node->getBody()->traverse(this); } - outputLineDirective(node->getLine().first_line); + outputLineDirective(out, node->getLine().first_line); out << ";}\n"; if (!firstLoopFragment) @@ -2747,7 +3169,11 @@ bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node) return false; // Not handled as an excessive loop } -void OutputHLSL::outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString, TInfoSinkBase &out) +void OutputHLSL::outputTriplet(TInfoSinkBase &out, + Visit visit, + const char *preString, + const char *inString, + const char *postString) { if (visit == PreVisit) { @@ -2763,17 +3189,10 @@ void OutputHLSL::outputTriplet(Visit visit, const char *preString, const char *i } } -void OutputHLSL::outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString) -{ - outputTriplet(visit, preString, inString, postString, getInfoSink()); -} - -void OutputHLSL::outputLineDirective(int line) +void OutputHLSL::outputLineDirective(TInfoSinkBase &out, int line) { if ((mCompileOptions & SH_LINE_DIRECTIVES) && (line > 0)) { - TInfoSinkBase &out = getInfoSink(); - out << "\n"; out << "#line " << line; @@ -2789,25 +3208,37 @@ void OutputHLSL::outputLineDirective(int line) TString OutputHLSL::argumentString(const TIntermSymbol *symbol) { TQualifier qualifier = symbol->getQualifier(); - const TType &type = symbol->getType(); - TString name = symbol->getSymbol(); + const TType &type = symbol->getType(); + const TName &name = symbol->getName(); + TString nameStr; - if (name.empty()) // HLSL demands named arguments, also for prototypes + if (name.getString().empty()) // HLSL demands named arguments, also for prototypes { - name = "x" + str(mUniqueIndex++); + nameStr = "x" + str(mUniqueIndex++); } else { - name = Decorate(name); + nameStr = DecorateIfNeeded(name); } - if (mOutputType == SH_HLSL11_OUTPUT && IsSampler(type.getBasicType())) + if (IsSampler(type.getBasicType())) { - return QualifierString(qualifier) + " " + TextureString(type) + " texture_" + name + ArrayString(type) + ", " + - QualifierString(qualifier) + " " + SamplerString(type) + " sampler_" + name + ArrayString(type); + if (mOutputType == SH_HLSL_4_1_OUTPUT) + { + // Samplers are passed as indices to the sampler array. + ASSERT(qualifier != EvqOut && qualifier != EvqInOut); + return "const uint " + nameStr + ArrayString(type); + } + if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) + { + return QualifierString(qualifier) + " " + TextureString(type.getBasicType()) + + " texture_" + nameStr + ArrayString(type) + ", " + QualifierString(qualifier) + + " " + SamplerString(type.getBasicType()) + " sampler_" + nameStr + + ArrayString(type); + } } - return QualifierString(qualifier) + " " + TypeString(type) + " " + name + ArrayString(type); + return QualifierString(qualifier) + " " + TypeString(type) + " " + nameStr + ArrayString(type); } TString OutputHLSL::initializer(const TType &type) @@ -2828,9 +3259,16 @@ TString OutputHLSL::initializer(const TType &type) return "{" + string + "}"; } -void OutputHLSL::outputConstructor(Visit visit, const TType &type, const char *name, const TIntermSequence *parameters) +void OutputHLSL::outputConstructor(TInfoSinkBase &out, + Visit visit, + const TType &type, + const char *name, + const TIntermSequence *parameters) { - TInfoSinkBase &out = getInfoSink(); + if (type.isArray()) + { + UNIMPLEMENTED(); + } if (visit == PreVisit) { @@ -2848,9 +3286,11 @@ void OutputHLSL::outputConstructor(Visit visit, const TType &type, const char *n } } -const ConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const ConstantUnion *constUnion) +const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out, + const TType &type, + const TConstantUnion *const constUnion) { - TInfoSinkBase &out = getInfoSink(); + const TConstantUnion *constUnionIterated = constUnion; const TStructure* structure = type.getStruct(); if (structure) @@ -2862,7 +3302,7 @@ const ConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const Con for (size_t i = 0; i < fields.size(); i++) { const TType *fieldType = fields[i]->type(); - constUnion = writeConstantUnion(*fieldType, constUnion); + constUnionIterated = writeConstantUnion(out, *fieldType, constUnionIterated); if (i != fields.size() - 1) { @@ -2881,37 +3321,20 @@ const ConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const Con { out << TypeString(type) << "("; } - - for (size_t i = 0; i < size; i++, constUnion++) - { - switch (constUnion->getType()) - { - case EbtFloat: out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst())); break; - case EbtInt: out << constUnion->getIConst(); break; - case EbtUInt: out << constUnion->getUConst(); break; - case EbtBool: out << constUnion->getBConst(); break; - default: UNREACHABLE(); - } - - if (i != size - 1) - { - out << ", "; - } - } - + constUnionIterated = WriteConstantUnionArray(out, constUnionIterated, size); if (writeType) { out << ")"; } } - return constUnion; + return constUnionIterated; } -void OutputHLSL::writeEmulatedFunctionTriplet(Visit visit, const char *preStr) +void OutputHLSL::writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, const char *preStr) { TString preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preStr); - outputTriplet(visit, preString.c_str(), ", ", ")"); + outputTriplet(out, visit, preString.c_str(), ", ", ")"); } bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression) @@ -2935,6 +3358,68 @@ bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *s return false; } +bool OutputHLSL::canWriteAsHLSLLiteral(TIntermTyped *expression) +{ + // We support writing constant unions and constructors that only take constant unions as + // parameters as HLSL literals. + if (expression->getAsConstantUnion()) + { + return true; + } + if (expression->getQualifier() != EvqConst || !expression->getAsAggregate() || + !expression->getAsAggregate()->isConstructor()) + { + return false; + } + TIntermAggregate *constructor = expression->getAsAggregate(); + for (TIntermNode *&node : *constructor->getSequence()) + { + if (!node->getAsConstantUnion()) + return false; + } + return true; +} + +bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out, + TIntermSymbol *symbolNode, + TIntermTyped *expression) +{ + if (canWriteAsHLSLLiteral(expression)) + { + symbolNode->traverse(this); + if (expression->getType().isArray()) + { + out << "[" << expression->getType().getArraySize() << "]"; + } + out << " = {"; + if (expression->getAsConstantUnion()) + { + TIntermConstantUnion *nodeConst = expression->getAsConstantUnion(); + const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer(); + WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize()); + } + else + { + TIntermAggregate *constructor = expression->getAsAggregate(); + ASSERT(constructor != nullptr); + for (TIntermNode *&node : *constructor->getSequence()) + { + TIntermConstantUnion *nodeConst = node->getAsConstantUnion(); + ASSERT(nodeConst); + const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer(); + WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize()); + if (node != constructor->getSequence()->back()) + { + out << ", "; + } + } + } + out << "}"; + return true; + } + return false; +} + void OutputHLSL::writeDeferredGlobalInitializers(TInfoSinkBase &out) { out << "#define ANGLE_USES_DEFERRED_INIT\n" @@ -2942,23 +3427,34 @@ void OutputHLSL::writeDeferredGlobalInitializers(TInfoSinkBase &out) << "void initializeDeferredGlobals()\n" << "{\n"; - for (auto it = mDeferredGlobalInitializers.cbegin(); it != mDeferredGlobalInitializers.cend(); ++it) + for (const auto &deferredGlobal : mDeferredGlobalInitializers) { - const auto &deferredGlobal = *it; - TIntermSymbol *symbol = deferredGlobal.first; - TIntermTyped *expression = deferredGlobal.second; - ASSERT(symbol); - ASSERT(symbol->getQualifier() == EvqGlobal && expression->getQualifier() != EvqConst); + TIntermBinary *binary = deferredGlobal->getAsBinaryNode(); + TIntermSelection *selection = deferredGlobal->getAsSelectionNode(); + if (binary != nullptr) + { + TIntermSymbol *symbol = binary->getLeft()->getAsSymbolNode(); + TIntermTyped *expression = binary->getRight(); + ASSERT(symbol); + ASSERT(symbol->getQualifier() == EvqGlobal && expression->getQualifier() != EvqConst); - out << " " << Decorate(symbol->getSymbol()) << " = "; + out << " " << Decorate(symbol->getSymbol()) << " = "; - if (!writeSameSymbolInitializer(out, symbol, expression)) + if (!writeSameSymbolInitializer(out, symbol, expression)) + { + ASSERT(mInfoSinkStack.top() == &out); + expression->traverse(this); + } + out << ";\n"; + } + else if (selection != nullptr) { - ASSERT(mInfoSinkStack.top() == &out); - expression->traverse(this); + writeSelection(out, selection); + } + else + { + UNREACHABLE(); } - - out << ";\n"; } out << "}\n" @@ -2969,9 +3465,8 @@ TString OutputHLSL::addStructEqualityFunction(const TStructure &structure) { const TFieldList &fields = structure.fields(); - for (auto it = mStructEqualityFunctions.cbegin(); it != mStructEqualityFunctions.cend(); ++it) + for (const auto &eqFunction : mStructEqualityFunctions) { - auto *eqFunction = *it; if (eqFunction->structure == &structure) { return eqFunction->functionName; @@ -3024,9 +3519,8 @@ TString OutputHLSL::addStructEqualityFunction(const TStructure &structure) TString OutputHLSL::addArrayEqualityFunction(const TType& type) { - for (auto it = mArrayEqualityFunctions.cbegin(); it != mArrayEqualityFunctions.cend(); ++it) + for (const auto &eqFunction : mArrayEqualityFunctions) { - const auto &eqFunction = *it; if (eqFunction->type == type) { return eqFunction->functionName; @@ -3076,9 +3570,8 @@ TString OutputHLSL::addArrayEqualityFunction(const TType& type) TString OutputHLSL::addArrayAssignmentFunction(const TType& type) { - for (auto it = mArrayAssignmentFunctions.cbegin(); it != mArrayAssignmentFunctions.cend(); ++it) + for (const auto &assignFunction : mArrayAssignmentFunctions) { - const auto &assignFunction = *it; if (assignFunction.type == type) { return assignFunction.functionName; @@ -3113,4 +3606,59 @@ TString OutputHLSL::addArrayAssignmentFunction(const TType& type) return function.functionName; } +TString OutputHLSL::addArrayConstructIntoFunction(const TType& type) +{ + for (const auto &constructIntoFunction : mArrayConstructIntoFunctions) + { + if (constructIntoFunction.type == type) + { + return constructIntoFunction.functionName; + } + } + + const TString &typeName = TypeString(type); + + ArrayHelperFunction function; + function.type = type; + + TInfoSinkBase fnNameOut; + fnNameOut << "angle_construct_into_" << type.getArraySize() << "_" << typeName; + function.functionName = fnNameOut.c_str(); + + TInfoSinkBase fnOut; + + fnOut << "void " << function.functionName << "(out " + << typeName << " a[" << type.getArraySize() << "]"; + for (int i = 0; i < type.getArraySize(); ++i) + { + fnOut << ", " << typeName << " b" << i; + } + fnOut << ")\n" + "{\n"; + + for (int i = 0; i < type.getArraySize(); ++i) + { + fnOut << " a[" << i << "] = b" << i << ";\n"; + } + fnOut << "}\n"; + + function.functionDefinition = fnOut.c_str(); + + mArrayConstructIntoFunctions.push_back(function); + + return function.functionName; +} + +void OutputHLSL::ensureStructDefined(const TType &type) +{ + TStructure *structure = type.getStruct(); + + if (structure) + { + mStructureHLSL->addConstructor(type, StructNameString(*structure), nullptr); + } +} + + + } diff --git a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.h b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.h index 51da877c72..8756d0ba4c 100644 --- a/src/3rdparty/angle/src/compiler/translator/OutputHLSL.h +++ b/src/3rdparty/angle/src/compiler/translator/OutputHLSL.h @@ -13,6 +13,7 @@ #include #include "angle_gl.h" +#include "compiler/translator/ASTMetadataHLSL.h" #include "compiler/translator/IntermNode.h" #include "compiler/translator/ParseContext.h" @@ -46,8 +47,10 @@ class OutputHLSL : public TIntermTraverser TInfoSinkBase &getInfoSink() { ASSERT(!mInfoSinkStack.empty()); return *mInfoSinkStack.top(); } + static bool canWriteAsHLSLLiteral(TIntermTyped *expression); + protected: - void header(const BuiltInFunctionEmulator *builtInFunctionEmulator); + void header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator); // Visit AST nodes and output their code to the body stream void visitSymbol(TIntermSymbol*); @@ -62,34 +65,52 @@ class OutputHLSL : public TIntermTraverser bool visitLoop(Visit visit, TIntermLoop*); bool visitBranch(Visit visit, TIntermBranch*); - void traverseStatements(TIntermNode *node); bool isSingleStatement(TIntermNode *node); - bool handleExcessiveLoop(TIntermLoop *node); + bool handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node); // Emit one of three strings depending on traverse phase. Called with literal strings so using const char* instead of TString. - void outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString, TInfoSinkBase &out); - void outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString); - void outputLineDirective(int line); + void outputTriplet(TInfoSinkBase &out, + Visit visit, + const char *preString, + const char *inString, + const char *postString); + void outputLineDirective(TInfoSinkBase &out, int line); TString argumentString(const TIntermSymbol *symbol); int vectorSize(const TType &type) const; // Emit constructor. Called with literal names so using const char* instead of TString. - void outputConstructor(Visit visit, const TType &type, const char *name, const TIntermSequence *parameters); - const ConstantUnion *writeConstantUnion(const TType &type, const ConstantUnion *constUnion); + void outputConstructor(TInfoSinkBase &out, + Visit visit, + const TType &type, + const char *name, + const TIntermSequence *parameters); + const TConstantUnion *writeConstantUnion(TInfoSinkBase &out, + const TType &type, + const TConstantUnion *constUnion); void outputEqual(Visit visit, const TType &type, TOperator op, TInfoSinkBase &out); - void writeEmulatedFunctionTriplet(Visit visit, const char *preStr); + void writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, const char *preStr); void makeFlaggedStructMaps(const std::vector &flaggedStructs); // Returns true if it found a 'same symbol' initializer (initializer that references the variable it's initting) bool writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression); + // Returns true if variable initializer could be written using literal {} notation. + bool writeConstantInitialization(TInfoSinkBase &out, + TIntermSymbol *symbolNode, + TIntermTyped *expression); + void writeDeferredGlobalInitializers(TInfoSinkBase &out); + void writeSelection(TInfoSinkBase &out, TIntermSelection *node); // Returns the function name TString addStructEqualityFunction(const TStructure &structure); TString addArrayEqualityFunction(const TType &type); TString addArrayAssignmentFunction(const TType &type); + TString addArrayConstructIntoFunction(const TType &type); + + // Ensures if the type is a struct, the struct is defined + void ensureStructDefined(const TType &type); sh::GLenum mShaderType; int mShaderVersion; @@ -98,7 +119,6 @@ class OutputHLSL : public TIntermTraverser const ShShaderOutput mOutputType; int mCompileOptions; - UnfoldShortCircuit *mUnfoldShortCircuit; bool mInsideFunction; // Output streams @@ -168,8 +188,9 @@ class OutputHLSL : public TIntermTraverser int mUniqueIndex; // For creating unique names - bool mContainsLoopDiscontinuity; - bool mContainsAnyLoop; + CallDAG mCallDag; + MetadataList mASTMetadataList; + ASTMetadataHLSL *mCurrentFunctionMetadata; bool mOutputLod0Function; bool mInsideDiscontinuousLoop; int mNestedLoopDepth; @@ -181,11 +202,10 @@ class OutputHLSL : public TIntermTraverser std::map mFlaggedStructMappedNames; std::map mFlaggedStructOriginalNames; - // Some initializers use varyings, uniforms or attributes, thus we can't evaluate some variables - // at global static scope in HLSL. These variables depend on values which we retrieve from the - // shader input structure, which we set in the D3D main function. Instead, we can initialize - // these static globals after we initialize our other globals. - std::vector> mDeferredGlobalInitializers; + // Some initializers may have been unfolded into if statements, thus we can't evaluate all initializers + // at global static scope in HLSL. Instead, we can initialize these static globals inside a helper function. + // This also enables initialization of globals with uniforms. + TIntermSequence mDeferredGlobalInitializers; struct HelperFunction { @@ -214,6 +234,11 @@ class OutputHLSL : public TIntermTraverser std::vector mArrayEqualityFunctions; std::vector mArrayAssignmentFunctions; + + // The construct-into functions are functions that fill an N-element array passed as an out parameter + // with the other N parameters of the function. This is used to work around that arrays can't be + // return values in HLSL. + std::vector mArrayConstructIntoFunctions; }; } diff --git a/src/3rdparty/angle/src/compiler/translator/ParseContext.cpp b/src/3rdparty/angle/src/compiler/translator/ParseContext.cpp index 7ad3f817ad..235351cf41 100644 --- a/src/3rdparty/angle/src/compiler/translator/ParseContext.cpp +++ b/src/3rdparty/angle/src/compiler/translator/ParseContext.cpp @@ -10,8 +10,11 @@ #include #include "compiler/preprocessor/SourceLocation.h" +#include "compiler/translator/Cache.h" #include "compiler/translator/glslang.h" #include "compiler/translator/ValidateSwitch.h" +#include "compiler/translator/ValidateGlobalInitializer.h" +#include "compiler/translator/util.h" /////////////////////////////////////////////////////////////////////// // @@ -23,86 +26,98 @@ // Look at a '.' field selector string and change it into offsets // for a vector. // -bool TParseContext::parseVectorFields(const TString& compString, int vecSize, TVectorFields& fields, const TSourceLoc& line) +bool TParseContext::parseVectorFields(const TString &compString, + int vecSize, + TVectorFields &fields, + const TSourceLoc &line) { - fields.num = (int) compString.size(); - if (fields.num > 4) { + fields.num = (int)compString.size(); + if (fields.num > 4) + { error(line, "illegal vector field selection", compString.c_str()); return false; } - enum { + enum + { exyzw, ergba, estpq } fieldSet[4]; - for (int i = 0; i < fields.num; ++i) { - switch (compString[i]) { - case 'x': - fields.offsets[i] = 0; - fieldSet[i] = exyzw; - break; - case 'r': - fields.offsets[i] = 0; - fieldSet[i] = ergba; - break; - case 's': - fields.offsets[i] = 0; - fieldSet[i] = estpq; - break; - case 'y': - fields.offsets[i] = 1; - fieldSet[i] = exyzw; - break; - case 'g': - fields.offsets[i] = 1; - fieldSet[i] = ergba; - break; - case 't': - fields.offsets[i] = 1; - fieldSet[i] = estpq; - break; - case 'z': - fields.offsets[i] = 2; - fieldSet[i] = exyzw; - break; - case 'b': - fields.offsets[i] = 2; - fieldSet[i] = ergba; - break; - case 'p': - fields.offsets[i] = 2; - fieldSet[i] = estpq; - break; - - case 'w': - fields.offsets[i] = 3; - fieldSet[i] = exyzw; - break; - case 'a': - fields.offsets[i] = 3; - fieldSet[i] = ergba; - break; - case 'q': - fields.offsets[i] = 3; - fieldSet[i] = estpq; - break; - default: - error(line, "illegal vector field selection", compString.c_str()); - return false; + for (int i = 0; i < fields.num; ++i) + { + switch (compString[i]) + { + case 'x': + fields.offsets[i] = 0; + fieldSet[i] = exyzw; + break; + case 'r': + fields.offsets[i] = 0; + fieldSet[i] = ergba; + break; + case 's': + fields.offsets[i] = 0; + fieldSet[i] = estpq; + break; + case 'y': + fields.offsets[i] = 1; + fieldSet[i] = exyzw; + break; + case 'g': + fields.offsets[i] = 1; + fieldSet[i] = ergba; + break; + case 't': + fields.offsets[i] = 1; + fieldSet[i] = estpq; + break; + case 'z': + fields.offsets[i] = 2; + fieldSet[i] = exyzw; + break; + case 'b': + fields.offsets[i] = 2; + fieldSet[i] = ergba; + break; + case 'p': + fields.offsets[i] = 2; + fieldSet[i] = estpq; + break; + + case 'w': + fields.offsets[i] = 3; + fieldSet[i] = exyzw; + break; + case 'a': + fields.offsets[i] = 3; + fieldSet[i] = ergba; + break; + case 'q': + fields.offsets[i] = 3; + fieldSet[i] = estpq; + break; + default: + error(line, "illegal vector field selection", compString.c_str()); + return false; } } - for (int i = 0; i < fields.num; ++i) { - if (fields.offsets[i] >= vecSize) { - error(line, "vector field selection out of range", compString.c_str()); + for (int i = 0; i < fields.num; ++i) + { + if (fields.offsets[i] >= vecSize) + { + error(line, "vector field selection out of range", compString.c_str()); return false; } - if (i > 0) { - if (fieldSet[i] != fieldSet[i-1]) { - error(line, "illegal - vector component fields not from the same set", compString.c_str()); + if (i > 0) + { + if (fieldSet[i] != fieldSet[i - 1]) + { + error(line, "illegal - vector component fields not from the same set", + compString.c_str()); return false; } } @@ -111,55 +126,6 @@ bool TParseContext::parseVectorFields(const TString& compString, int vecSize, TV return true; } - -// -// Look at a '.' field selector string and change it into offsets -// for a matrix. -// -bool TParseContext::parseMatrixFields(const TString& compString, int matCols, int matRows, TMatrixFields& fields, const TSourceLoc& line) -{ - fields.wholeRow = false; - fields.wholeCol = false; - fields.row = -1; - fields.col = -1; - - if (compString.size() != 2) { - error(line, "illegal length of matrix field selection", compString.c_str()); - return false; - } - - if (compString[0] == '_') { - if (compString[1] < '0' || compString[1] > '3') { - error(line, "illegal matrix field selection", compString.c_str()); - return false; - } - fields.wholeCol = true; - fields.col = compString[1] - '0'; - } else if (compString[1] == '_') { - if (compString[0] < '0' || compString[0] > '3') { - error(line, "illegal matrix field selection", compString.c_str()); - return false; - } - fields.wholeRow = true; - fields.row = compString[0] - '0'; - } else { - if (compString[0] < '0' || compString[0] > '3' || - compString[1] < '0' || compString[1] > '3') { - error(line, "illegal matrix field selection", compString.c_str()); - return false; - } - fields.row = compString[0] - '0'; - fields.col = compString[1] - '0'; - } - - if (fields.row >= matRows || fields.col >= matCols) { - error(line, "matrix field selection out of range", compString.c_str()); - return false; - } - - return true; -} - /////////////////////////////////////////////////////////////////////// // // Errors @@ -176,37 +142,49 @@ void TParseContext::recover() // // Used by flex/bison to output all syntax and parsing errors. // -void TParseContext::error(const TSourceLoc& loc, - const char* reason, const char* token, - const char* extraInfo) +void TParseContext::error(const TSourceLoc &loc, + const char *reason, + const char *token, + const char *extraInfo) { pp::SourceLocation srcLoc; srcLoc.file = loc.first_file; srcLoc.line = loc.first_line; - diagnostics.writeInfo(pp::Diagnostics::PP_ERROR, - srcLoc, reason, token, extraInfo); - + mDiagnostics.writeInfo(pp::Diagnostics::PP_ERROR, srcLoc, reason, token, extraInfo); } -void TParseContext::warning(const TSourceLoc& loc, - const char* reason, const char* token, - const char* extraInfo) { +void TParseContext::warning(const TSourceLoc &loc, + const char *reason, + const char *token, + const char *extraInfo) +{ pp::SourceLocation srcLoc; srcLoc.file = loc.first_file; srcLoc.line = loc.first_line; - diagnostics.writeInfo(pp::Diagnostics::PP_WARNING, - srcLoc, reason, token, extraInfo); + mDiagnostics.writeInfo(pp::Diagnostics::PP_WARNING, srcLoc, reason, token, extraInfo); } -void TParseContext::trace(const char* str) +void TParseContext::outOfRangeError(bool isError, + const TSourceLoc &loc, + const char *reason, + const char *token, + const char *extraInfo) { - diagnostics.writeDebug(str); + if (isError) + { + error(loc, reason, token, extraInfo); + recover(); + } + else + { + warning(loc, reason, token, extraInfo); + } } // // Same error message for all places assignments don't work. // -void TParseContext::assignError(const TSourceLoc& line, const char* op, TString left, TString right) +void TParseContext::assignError(const TSourceLoc &line, const char *op, TString left, TString right) { std::stringstream extraInfoStream; extraInfoStream << "cannot convert from '" << right << "' to '" << left << "'"; @@ -217,11 +195,11 @@ void TParseContext::assignError(const TSourceLoc& line, const char* op, TString // // Same error message for all places unary operations don't work. // -void TParseContext::unaryOpError(const TSourceLoc& line, const char* op, TString operand) +void TParseContext::unaryOpError(const TSourceLoc &line, const char *op, TString operand) { std::stringstream extraInfoStream; - extraInfoStream << "no operation '" << op << "' exists that takes an operand of type " << operand - << " (or there is no acceptable conversion)"; + extraInfoStream << "no operation '" << op << "' exists that takes an operand of type " + << operand << " (or there is no acceptable conversion)"; std::string extraInfo = extraInfoStream.str(); error(line, " wrong operand type", op, extraInfo.c_str()); } @@ -229,33 +207,44 @@ void TParseContext::unaryOpError(const TSourceLoc& line, const char* op, TString // // Same error message for all binary operations don't work. // -void TParseContext::binaryOpError(const TSourceLoc& line, const char* op, TString left, TString right) +void TParseContext::binaryOpError(const TSourceLoc &line, + const char *op, + TString left, + TString right) { std::stringstream extraInfoStream; - extraInfoStream << "no operation '" << op << "' exists that takes a left-hand operand of type '" << left - << "' and a right operand of type '" << right << "' (or there is no acceptable conversion)"; + extraInfoStream << "no operation '" << op << "' exists that takes a left-hand operand of type '" + << left << "' and a right operand of type '" << right + << "' (or there is no acceptable conversion)"; std::string extraInfo = extraInfoStream.str(); - error(line, " wrong operand types ", op, extraInfo.c_str()); + error(line, " wrong operand types ", op, extraInfo.c_str()); } -bool TParseContext::precisionErrorCheck(const TSourceLoc& line, TPrecision precision, TBasicType type){ - if (!checksPrecisionErrors) +bool TParseContext::precisionErrorCheck(const TSourceLoc &line, + TPrecision precision, + TBasicType type) +{ + if (!mChecksPrecisionErrors) return false; - switch( type ){ - case EbtFloat: - if( precision == EbpUndefined ){ - error( line, "No precision specified for (float)", "" ); - return true; - } - break; - case EbtInt: - if( precision == EbpUndefined ){ - error( line, "No precision specified (int)", "" ); - return true; + if (precision == EbpUndefined) + { + switch (type) + { + case EbtFloat: + error(line, "No precision specified for (float)", ""); + return true; + case EbtInt: + case EbtUInt: + UNREACHABLE(); // there's always a predeclared qualifier + error(line, "No precision specified (int)", ""); + return true; + default: + if (IsSampler(type)) + { + error(line, "No precision specified (sampler)", ""); + return true; + } } - break; - default: - return false; } return false; } @@ -266,86 +255,112 @@ bool TParseContext::precisionErrorCheck(const TSourceLoc& line, TPrecision preci // // Returns true if the was an error. // -bool TParseContext::lValueErrorCheck(const TSourceLoc& line, const char* op, TIntermTyped* node) +bool TParseContext::lValueErrorCheck(const TSourceLoc &line, const char *op, TIntermTyped *node) { - TIntermSymbol* symNode = node->getAsSymbolNode(); - TIntermBinary* binaryNode = node->getAsBinaryNode(); + TIntermSymbol *symNode = node->getAsSymbolNode(); + TIntermBinary *binaryNode = node->getAsBinaryNode(); - if (binaryNode) { + if (binaryNode) + { bool errorReturn; - switch(binaryNode->getOp()) { - case EOpIndexDirect: - case EOpIndexIndirect: - case EOpIndexDirectStruct: - case EOpIndexDirectInterfaceBlock: - return lValueErrorCheck(line, op, binaryNode->getLeft()); - case EOpVectorSwizzle: - errorReturn = lValueErrorCheck(line, op, binaryNode->getLeft()); - if (!errorReturn) { - int offset[4] = {0,0,0,0}; - - TIntermTyped* rightNode = binaryNode->getRight(); - TIntermAggregate *aggrNode = rightNode->getAsAggregate(); - - for (TIntermSequence::iterator p = aggrNode->getSequence()->begin(); - p != aggrNode->getSequence()->end(); p++) { - int value = (*p)->getAsTyped()->getAsConstantUnion()->getIConst(0); - offset[value]++; - if (offset[value] > 1) { - error(line, " l-value of swizzle cannot have duplicate components", op); - - return true; + switch (binaryNode->getOp()) + { + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + case EOpIndexDirectInterfaceBlock: + return lValueErrorCheck(line, op, binaryNode->getLeft()); + case EOpVectorSwizzle: + errorReturn = lValueErrorCheck(line, op, binaryNode->getLeft()); + if (!errorReturn) + { + int offset[4] = {0, 0, 0, 0}; + + TIntermTyped *rightNode = binaryNode->getRight(); + TIntermAggregate *aggrNode = rightNode->getAsAggregate(); + + for (TIntermSequence::iterator p = aggrNode->getSequence()->begin(); + p != aggrNode->getSequence()->end(); p++) + { + int value = (*p)->getAsTyped()->getAsConstantUnion()->getIConst(0); + offset[value]++; + if (offset[value] > 1) + { + error(line, " l-value of swizzle cannot have duplicate components", op); + + return true; + } } } - } - return errorReturn; - default: - break; + return errorReturn; + default: + break; } error(line, " l-value required", op); return true; } - - const char* symbol = 0; + const char *symbol = 0; if (symNode != 0) symbol = symNode->getSymbol().c_str(); - const char* message = 0; - switch (node->getQualifier()) { - case EvqConst: message = "can't modify a const"; break; - case EvqConstReadOnly: message = "can't modify a const"; break; - case EvqAttribute: message = "can't modify an attribute"; break; - case EvqFragmentIn: message = "can't modify an input"; break; - case EvqVertexIn: message = "can't modify an input"; break; - case EvqUniform: message = "can't modify a uniform"; break; - case EvqVaryingIn: message = "can't modify a varying"; break; - case EvqFragCoord: message = "can't modify gl_FragCoord"; break; - case EvqFrontFacing: message = "can't modify gl_FrontFacing"; break; - case EvqPointCoord: message = "can't modify gl_PointCoord"; break; - default: - - // - // Type that can't be written to? - // - if (node->getBasicType() == EbtVoid) { - message = "can't modify void"; - } - if (IsSampler(node->getBasicType())) { - message = "can't modify a sampler"; - } + const char *message = 0; + switch (node->getQualifier()) + { + case EvqConst: + message = "can't modify a const"; + break; + case EvqConstReadOnly: + message = "can't modify a const"; + break; + case EvqAttribute: + message = "can't modify an attribute"; + break; + case EvqFragmentIn: + message = "can't modify an input"; + break; + case EvqVertexIn: + message = "can't modify an input"; + break; + case EvqUniform: + message = "can't modify a uniform"; + break; + case EvqVaryingIn: + message = "can't modify a varying"; + break; + case EvqFragCoord: + message = "can't modify gl_FragCoord"; + break; + case EvqFrontFacing: + message = "can't modify gl_FrontFacing"; + break; + case EvqPointCoord: + message = "can't modify gl_PointCoord"; + break; + default: + // + // Type that can't be written to? + // + if (node->getBasicType() == EbtVoid) + { + message = "can't modify void"; + } + if (IsSampler(node->getBasicType())) + { + message = "can't modify a sampler"; + } } - if (message == 0 && binaryNode == 0 && symNode == 0) { + if (message == 0 && binaryNode == 0 && symNode == 0) + { error(line, " l-value required", op); return true; } - // // Everything else is okay, no error. // @@ -355,13 +370,15 @@ bool TParseContext::lValueErrorCheck(const TSourceLoc& line, const char* op, TIn // // If we get here, we have an error and a message. // - if (symNode) { + if (symNode) + { std::stringstream extraInfoStream; extraInfoStream << "\"" << symbol << "\" (" << message << ")"; std::string extraInfo = extraInfoStream.str(); error(line, " l-value required", op, extraInfo.c_str()); } - else { + else + { std::stringstream extraInfoStream; extraInfoStream << "(" << message << ")"; std::string extraInfo = extraInfoStream.str(); @@ -377,7 +394,7 @@ bool TParseContext::lValueErrorCheck(const TSourceLoc& line, const char* op, TIn // // Returns true if the was an error. // -bool TParseContext::constErrorCheck(TIntermTyped* node) +bool TParseContext::constErrorCheck(TIntermTyped *node) { if (node->getQualifier() == EvqConst) return false; @@ -393,7 +410,7 @@ bool TParseContext::constErrorCheck(TIntermTyped* node) // // Returns true if the was an error. // -bool TParseContext::integerErrorCheck(TIntermTyped* node, const char* token) +bool TParseContext::integerErrorCheck(TIntermTyped *node, const char *token) { if (node->isScalarInt()) return false; @@ -409,7 +426,7 @@ bool TParseContext::integerErrorCheck(TIntermTyped* node, const char* token) // // Returns true if the was an error. // -bool TParseContext::globalErrorCheck(const TSourceLoc& line, bool global, const char* token) +bool TParseContext::globalErrorCheck(const TSourceLoc &line, bool global, const char *token) { if (global) return false; @@ -428,30 +445,40 @@ bool TParseContext::globalErrorCheck(const TSourceLoc& line, bool global, const // // Returns true if there was an error. // -bool TParseContext::reservedErrorCheck(const TSourceLoc& line, const TString& identifier) +bool TParseContext::reservedErrorCheck(const TSourceLoc &line, const TString &identifier) { - static const char* reservedErrMsg = "reserved built-in name"; - if (!symbolTable.atBuiltInLevel()) { - if (identifier.compare(0, 3, "gl_") == 0) { + static const char *reservedErrMsg = "reserved built-in name"; + if (!symbolTable.atBuiltInLevel()) + { + if (identifier.compare(0, 3, "gl_") == 0) + { error(line, reservedErrMsg, "gl_"); return true; } - if (IsWebGLBasedSpec(shaderSpec)) { - if (identifier.compare(0, 6, "webgl_") == 0) { + if (IsWebGLBasedSpec(mShaderSpec)) + { + if (identifier.compare(0, 6, "webgl_") == 0) + { error(line, reservedErrMsg, "webgl_"); return true; } - if (identifier.compare(0, 7, "_webgl_") == 0) { + if (identifier.compare(0, 7, "_webgl_") == 0) + { error(line, reservedErrMsg, "_webgl_"); return true; } - if (shaderSpec == SH_CSS_SHADERS_SPEC && identifier.compare(0, 4, "css_") == 0) { + if (mShaderSpec == SH_CSS_SHADERS_SPEC && identifier.compare(0, 4, "css_") == 0) + { error(line, reservedErrMsg, "css_"); return true; } } - if (identifier.find("__") != TString::npos) { - error(line, "identifiers containing two consecutive underscores (__) are reserved as possible future keywords", identifier.c_str()); + if (identifier.find("__") != TString::npos) + { + error(line, + "identifiers containing two consecutive underscores (__) are reserved as " + "possible future keywords", + identifier.c_str()); return true; } } @@ -466,19 +493,30 @@ bool TParseContext::reservedErrorCheck(const TSourceLoc& line, const TString& id // // Returns true if there was an error in construction. // -bool TParseContext::constructorErrorCheck(const TSourceLoc& line, TIntermNode* node, TFunction& function, TOperator op, TType* type) +bool TParseContext::constructorErrorCheck(const TSourceLoc &line, + TIntermNode *argumentsNode, + TFunction &function, + TOperator op, + TType *type) { *type = function.getReturnType(); bool constructingMatrix = false; - switch(op) { - case EOpConstructMat2: - case EOpConstructMat3: - case EOpConstructMat4: - constructingMatrix = true; - break; - default: - break; + switch (op) + { + case EOpConstructMat2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: + case EOpConstructMat3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: + case EOpConstructMat4: + constructingMatrix = true; + break; + default: + break; } // @@ -487,16 +525,17 @@ bool TParseContext::constructorErrorCheck(const TSourceLoc& line, TIntermNode* n // again, there is an extra argument, so 'overfull' will become true. // - size_t size = 0; - bool constType = true; - bool full = false; - bool overFull = false; + size_t size = 0; + bool constType = true; + bool full = false; + bool overFull = false; bool matrixInMatrix = false; bool arrayArg = false; - for (size_t i = 0; i < function.getParamCount(); ++i) { - const TParameter& param = function.getParam(i); + for (size_t i = 0; i < function.getParamCount(); ++i) + { + const TConstParameter ¶m = function.getParam(i); size += param.type->getObjectSize(); - + if (constructingMatrix && param.type->isMatrix()) matrixInMatrix = true; if (full) @@ -508,162 +547,184 @@ bool TParseContext::constructorErrorCheck(const TSourceLoc& line, TIntermNode* n if (param.type->isArray()) arrayArg = true; } - + if (constType) type->setQualifier(EvqConst); - if (type->isArray() && static_cast(type->getArraySize()) != function.getParamCount()) { - error(line, "array constructor needs one argument per array element", "constructor"); - return true; + if (type->isArray()) + { + if (type->isUnsizedArray()) + { + type->setArraySize(static_cast(function.getParamCount())); + } + else if (static_cast(type->getArraySize()) != function.getParamCount()) + { + error(line, "array constructor needs one argument per array element", "constructor"); + return true; + } } - if (arrayArg && op != EOpConstructStruct) { + if (arrayArg && op != EOpConstructStruct) + { error(line, "constructing from a non-dereferenced array", "constructor"); return true; } - if (matrixInMatrix && !type->isArray()) { - if (function.getParamCount() != 1) { - error(line, "constructing matrix from matrix can only take one argument", "constructor"); - return true; + if (matrixInMatrix && !type->isArray()) + { + if (function.getParamCount() != 1) + { + error(line, "constructing matrix from matrix can only take one argument", + "constructor"); + return true; } } - if (overFull) { + if (overFull) + { error(line, "too many arguments", "constructor"); return true; } - - if (op == EOpConstructStruct && !type->isArray() && type->getStruct()->fields().size() != function.getParamCount()) { - error(line, "Number of constructor parameters does not match the number of structure fields", "constructor"); + + if (op == EOpConstructStruct && !type->isArray() && + type->getStruct()->fields().size() != function.getParamCount()) + { + error(line, + "Number of constructor parameters does not match the number of structure fields", + "constructor"); return true; } - if (!type->isMatrix() || !matrixInMatrix) { + if (!type->isMatrix() || !matrixInMatrix) + { if ((op != EOpConstructStruct && size != 1 && size < type->getObjectSize()) || - (op == EOpConstructStruct && size < type->getObjectSize())) { + (op == EOpConstructStruct && size < type->getObjectSize())) + { error(line, "not enough data provided for construction", "constructor"); return true; } } - TIntermTyped *typed = node ? node->getAsTyped() : 0; - if (typed == 0) { - error(line, "constructor argument does not have a type", "constructor"); - return true; - } - if (op != EOpConstructStruct && IsSampler(typed->getBasicType())) { - error(line, "cannot convert a sampler", "constructor"); + if (argumentsNode == nullptr) + { + error(line, "constructor does not have any arguments", "constructor"); return true; } - if (typed->getBasicType() == EbtVoid) { - error(line, "cannot convert a void", "constructor"); - return true; + + TIntermAggregate *argumentsAgg = argumentsNode->getAsAggregate(); + for (TIntermNode *&argNode : *argumentsAgg->getSequence()) + { + TIntermTyped *argTyped = argNode->getAsTyped(); + ASSERT(argTyped != nullptr); + if (op != EOpConstructStruct && IsSampler(argTyped->getBasicType())) + { + error(line, "cannot convert a sampler", "constructor"); + return true; + } + if (argTyped->getBasicType() == EbtVoid) + { + error(line, "cannot convert a void", "constructor"); + return true; + } } return false; } -// This function checks to see if a void variable has been declared and raise an error message for such a case +// This function checks to see if a void variable has been declared and raise an error message for +// such a case // // returns true in case of an error // -bool TParseContext::voidErrorCheck(const TSourceLoc& line, const TString& identifier, const TPublicType& pubType) +bool TParseContext::voidErrorCheck(const TSourceLoc &line, + const TString &identifier, + const TBasicType &type) { - if (pubType.type == EbtVoid) { + if (type == EbtVoid) + { error(line, "illegal use of type 'void'", identifier.c_str()); return true; - } + } return false; } -// This function checks to see if the node (for the expression) contains a scalar boolean expression or not +// This function checks to see if the node (for the expression) contains a scalar boolean expression +// or not // // returns true in case of an error // -bool TParseContext::boolErrorCheck(const TSourceLoc& line, const TIntermTyped* type) +bool TParseContext::boolErrorCheck(const TSourceLoc &line, const TIntermTyped *type) { - if (type->getBasicType() != EbtBool || type->isArray() || type->isMatrix() || type->isVector()) { + if (type->getBasicType() != EbtBool || type->isArray() || type->isMatrix() || type->isVector()) + { error(line, "boolean expression expected", ""); return true; - } + } return false; } -// This function checks to see if the node (for the expression) contains a scalar boolean expression or not +// This function checks to see if the node (for the expression) contains a scalar boolean expression +// or not // // returns true in case of an error // -bool TParseContext::boolErrorCheck(const TSourceLoc& line, const TPublicType& pType) +bool TParseContext::boolErrorCheck(const TSourceLoc &line, const TPublicType &pType) { - if (pType.type != EbtBool || pType.isAggregate()) { + if (pType.type != EbtBool || pType.isAggregate()) + { error(line, "boolean expression expected", ""); - return true; - } - - return false; -} - -bool TParseContext::samplerErrorCheck(const TSourceLoc& line, const TPublicType& pType, const char* reason) -{ - if (pType.type == EbtStruct) { - if (containsSampler(*pType.userDef)) { - error(line, reason, getBasicString(pType.type), "(structure contains a sampler)"); - - return true; - } - - return false; - } else if (IsSampler(pType.type)) { - error(line, reason, getBasicString(pType.type)); - return true; } return false; } -bool TParseContext::structQualifierErrorCheck(const TSourceLoc& line, const TPublicType& pType) +bool TParseContext::samplerErrorCheck(const TSourceLoc &line, + const TPublicType &pType, + const char *reason) { - switch (pType.qualifier) + if (pType.type == EbtStruct) { - case EvqVaryingIn: - case EvqVaryingOut: - case EvqAttribute: - case EvqVertexIn: - case EvqFragmentOut: - if (pType.type == EbtStruct) + if (containsSampler(*pType.userDef)) { - error(line, "cannot be used with a structure", getQualifierString(pType.qualifier)); + error(line, reason, getBasicString(pType.type), "(structure contains a sampler)"); + return true; } - default: break; + return false; } + else if (IsSampler(pType.type)) + { + error(line, reason, getBasicString(pType.type)); - if (pType.qualifier != EvqUniform && samplerErrorCheck(line, pType, "samplers must be uniform")) return true; + } return false; } -bool TParseContext::locationDeclaratorListCheck(const TSourceLoc& line, const TPublicType &pType) +bool TParseContext::locationDeclaratorListCheck(const TSourceLoc &line, const TPublicType &pType) { if (pType.layoutQualifier.location != -1) { - error(line, "location must only be specified for a single input or output variable", "location"); + error(line, "location must only be specified for a single input or output variable", + "location"); return true; } return false; } -bool TParseContext::parameterSamplerErrorCheck(const TSourceLoc& line, TQualifier qualifier, const TType& type) +bool TParseContext::parameterSamplerErrorCheck(const TSourceLoc &line, + TQualifier qualifier, + const TType &type) { - if ((qualifier == EvqOut || qualifier == EvqInOut) && - type.getBasicType() != EbtStruct && IsSampler(type.getBasicType())) { + if ((qualifier == EvqOut || qualifier == EvqInOut) && type.getBasicType() != EbtStruct && + IsSampler(type.getBasicType())) + { error(line, "samplers cannot be output parameters", type.getBasicString()); return true; } @@ -671,14 +732,16 @@ bool TParseContext::parameterSamplerErrorCheck(const TSourceLoc& line, TQualifie return false; } -bool TParseContext::containsSampler(TType& type) +bool TParseContext::containsSampler(const TType &type) { if (IsSampler(type.getBasicType())) return true; - if (type.getBasicType() == EbtStruct || type.isInterfaceBlock()) { - const TFieldList& fields = type.getStruct()->fields(); - for (unsigned int i = 0; i < fields.size(); ++i) { + if (type.getBasicType() == EbtStruct || type.isInterfaceBlock()) + { + const TFieldList &fields = type.getStruct()->fields(); + for (unsigned int i = 0; i < fields.size(); ++i) + { if (containsSampler(*fields[i]->type())) return true; } @@ -692,13 +755,17 @@ bool TParseContext::containsSampler(TType& type) // // Returns true if there was an error. // -bool TParseContext::arraySizeErrorCheck(const TSourceLoc& line, TIntermTyped* expr, int& size) +bool TParseContext::arraySizeErrorCheck(const TSourceLoc &line, TIntermTyped *expr, int &size) { - TIntermConstantUnion* constant = expr->getAsConstantUnion(); + TIntermConstantUnion *constant = expr->getAsConstantUnion(); - if (constant == 0 || !constant->isScalarInt()) + // TODO(oetuaho@nvidia.com): Get rid of the constant == nullptr check here once all constant + // expressions can be folded. Right now we don't allow constant expressions that ANGLE can't + // fold as array size. + if (expr->getQualifier() != EvqConst || constant == nullptr || !constant->isScalarInt()) { error(line, "array size must be a constant integer expression", ""); + size = 1; return true; } @@ -707,7 +774,7 @@ bool TParseContext::arraySizeErrorCheck(const TSourceLoc& line, TIntermTyped* ex if (constant->getBasicType() == EbtUInt) { unsignedSize = constant->getUConst(0); - size = static_cast(unsignedSize); + size = static_cast(unsignedSize); } else { @@ -750,10 +817,13 @@ bool TParseContext::arraySizeErrorCheck(const TSourceLoc& line, TIntermTyped* ex // // Returns true if there is an error. // -bool TParseContext::arrayQualifierErrorCheck(const TSourceLoc& line, TPublicType type) +bool TParseContext::arrayQualifierErrorCheck(const TSourceLoc &line, const TPublicType &type) { - if ((type.qualifier == EvqAttribute) || (type.qualifier == EvqVertexIn) || (type.qualifier == EvqConst)) { - error(line, "cannot declare arrays of this qualifier", TType(type).getCompleteString().c_str()); + if ((type.qualifier == EvqAttribute) || (type.qualifier == EvqVertexIn) || + (type.qualifier == EvqConst && mShaderVersion < 300)) + { + error(line, "cannot declare arrays of this qualifier", + TType(type).getCompleteString().c_str()); return true; } @@ -765,93 +835,25 @@ bool TParseContext::arrayQualifierErrorCheck(const TSourceLoc& line, TPublicType // // Returns true if there is an error. // -bool TParseContext::arrayTypeErrorCheck(const TSourceLoc& line, TPublicType type) +bool TParseContext::arrayTypeErrorCheck(const TSourceLoc &line, const TPublicType &type) { // // Can the type be an array? // - if (type.array) { + if (type.array) + { error(line, "cannot declare arrays of arrays", TType(type).getCompleteString().c_str()); return true; } - - return false; -} - -// -// Do all the semantic checking for declaring an array, with and -// without a size, and make the right changes to the symbol table. -// -// size == 0 means no specified size. -// -// Returns true if there was an error. -// -bool TParseContext::arrayErrorCheck(const TSourceLoc& line, const TString& identifier, const TPublicType &type, TVariable*& variable) -{ - // - // Don't check for reserved word use until after we know it's not in the symbol table, - // because reserved arrays can be redeclared. - // - - bool builtIn = false; - bool sameScope = false; - TSymbol* symbol = symbolTable.find(identifier, 0, &builtIn, &sameScope); - if (symbol == 0 || !sameScope) { - bool needsReservedErrorCheck = true; - - // gl_LastFragData may be redeclared with a new precision qualifier - if (identifier.compare(0, 15, "gl_LastFragData") == 0) { - if (type.arraySize == static_cast(symbolTable.findBuiltIn("gl_MaxDrawBuffers", shaderVersion))->getConstPointer()->getIConst()) { - if (TSymbol* builtInSymbol = symbolTable.findBuiltIn(identifier, shaderVersion)) { - needsReservedErrorCheck = extensionErrorCheck(line, builtInSymbol->getExtension()); - } - } else { - error(line, "redeclaration of array with size != gl_MaxDrawBuffers", identifier.c_str()); - return true; - } - } - - if (needsReservedErrorCheck) - if (reservedErrorCheck(line, identifier)) - return true; - - variable = new TVariable(&identifier, TType(type)); - - if (type.arraySize) - variable->getType().setArraySize(type.arraySize); - - if (! symbolTable.declare(variable)) { - delete variable; - error(line, "INTERNAL ERROR inserting new symbol", identifier.c_str()); - return true; - } - } else { - if (! symbol->isVariable()) { - error(line, "variable expected", identifier.c_str()); - return true; - } - - variable = static_cast(symbol); - if (! variable->getType().isArray()) { - error(line, "redeclaring non-array as array", identifier.c_str()); - return true; - } - if (variable->getType().getArraySize() > 0) { - error(line, "redeclaration of array with size", identifier.c_str()); - return true; - } - - if (! variable->getType().sameElementType(TType(type))) { - error(line, "redeclaration of array with a different type", identifier.c_str()); - return true; - } - - if (type.arraySize) - variable->getType().setArraySize(type.arraySize); - } - - if (voidErrorCheck(line, identifier, type)) + // In ESSL1.00 shaders, structs cannot be varying (section 4.3.5). This is checked elsewhere. + // In ESSL3.00 shaders, struct inputs/outputs are allowed but not arrays of structs (section + // 4.3.4). + if (mShaderVersion >= 300 && type.type == EbtStruct && sh::IsVarying(type.qualifier)) + { + error(line, "cannot declare arrays of structs of this qualifier", + TType(type).getCompleteString().c_str()); return true; + } return false; } @@ -861,20 +863,24 @@ bool TParseContext::arrayErrorCheck(const TSourceLoc& line, const TString& ident // // Returns true if there was an error. // -bool TParseContext::nonInitConstErrorCheck(const TSourceLoc& line, const TString& identifier, TPublicType& type, bool array) +bool TParseContext::nonInitErrorCheck(const TSourceLoc &line, + const TString &identifier, + TPublicType *type) { - if (type.qualifier == EvqConst) + ASSERT(type != nullptr); + if (type->qualifier == EvqConst) { // Make the qualifier make sense. - type.qualifier = EvqTemporary; - - if (array) - { - error(line, "arrays may not be declared constant since they cannot be initialized", identifier.c_str()); - } - else if (type.isStructureContainingArrays()) + type->qualifier = EvqTemporary; + + // Generate informative error messages for ESSL1. + // In ESSL3 arrays and structures containing arrays can be constant. + if (mShaderVersion < 300 && type->isStructureContainingArrays()) { - error(line, "structures containing arrays may not be declared constant since they cannot be initialized", identifier.c_str()); + error(line, + "structures containing arrays may not be declared constant since they cannot be " + "initialized", + identifier.c_str()); } else { @@ -883,44 +889,79 @@ bool TParseContext::nonInitConstErrorCheck(const TSourceLoc& line, const TString return true; } - + if (type->isUnsizedArray()) + { + error(line, "implicitly sized arrays need to be initialized", identifier.c_str()); + return true; + } return false; } -// -// Do semantic checking for a variable declaration that has no initializer, +// Do some simple checks that are shared between all variable declarations, // and update the symbol table. // -// Returns true if there was an error. +// Returns true if declaring the variable succeeded. // -bool TParseContext::nonInitErrorCheck(const TSourceLoc& line, const TString& identifier, const TPublicType& type, TVariable*& variable) +bool TParseContext::declareVariable(const TSourceLoc &line, + const TString &identifier, + const TType &type, + TVariable **variable) { - if (reservedErrorCheck(line, identifier)) - recover(); + ASSERT((*variable) == nullptr); - variable = new TVariable(&identifier, TType(type)); + bool needsReservedErrorCheck = true; - if (! symbolTable.declare(variable)) { - error(line, "redefinition", variable->getName().c_str()); - delete variable; - variable = 0; - return true; + // gl_LastFragData may be redeclared with a new precision qualifier + if (type.isArray() && identifier.compare(0, 15, "gl_LastFragData") == 0) + { + const TVariable *maxDrawBuffers = static_cast( + symbolTable.findBuiltIn("gl_MaxDrawBuffers", mShaderVersion)); + if (type.getArraySize() == maxDrawBuffers->getConstPointer()->getIConst()) + { + if (TSymbol *builtInSymbol = symbolTable.findBuiltIn(identifier, mShaderVersion)) + { + needsReservedErrorCheck = extensionErrorCheck(line, builtInSymbol->getExtension()); + } + } + else + { + error(line, "redeclaration of gl_LastFragData with size != gl_MaxDrawBuffers", + identifier.c_str()); + return false; + } } - if (voidErrorCheck(line, identifier, type)) - return true; + if (needsReservedErrorCheck && reservedErrorCheck(line, identifier)) + return false; - return false; + (*variable) = new TVariable(&identifier, type); + if (!symbolTable.declare(*variable)) + { + error(line, "redefinition", identifier.c_str()); + *variable = nullptr; + return false; + } + + if (voidErrorCheck(line, identifier, type.getBasicType())) + return false; + + return true; } -bool TParseContext::paramErrorCheck(const TSourceLoc& line, TQualifier qualifier, TQualifier paramQualifier, TType* type) -{ - if (qualifier != EvqConst && qualifier != EvqTemporary) { +bool TParseContext::paramErrorCheck(const TSourceLoc &line, + TQualifier qualifier, + TQualifier paramQualifier, + TType *type) +{ + if (qualifier != EvqConst && qualifier != EvqTemporary) + { error(line, "qualifier not allowed on function parameter", getQualifierString(qualifier)); return true; } - if (qualifier == EvqConst && paramQualifier != EvqIn) { - error(line, "qualifier not allowed with ", getQualifierString(qualifier), getQualifierString(paramQualifier)); + if (qualifier == EvqConst && paramQualifier != EvqIn) + { + error(line, "qualifier not allowed with ", getQualifierString(qualifier), + getQualifierString(paramQualifier)); return true; } @@ -932,20 +973,23 @@ bool TParseContext::paramErrorCheck(const TSourceLoc& line, TQualifier qualifier return false; } -bool TParseContext::extensionErrorCheck(const TSourceLoc& line, const TString& extension) +bool TParseContext::extensionErrorCheck(const TSourceLoc &line, const TString &extension) { - const TExtensionBehavior& extBehavior = extensionBehavior(); + const TExtensionBehavior &extBehavior = extensionBehavior(); TExtensionBehavior::const_iterator iter = extBehavior.find(extension.c_str()); - if (iter == extBehavior.end()) { + if (iter == extBehavior.end()) + { error(line, "extension", extension.c_str(), "is not supported"); return true; } // In GLSL ES, an extension's default behavior is "disable". - if (iter->second == EBhDisable || iter->second == EBhUndefined) { + if (iter->second == EBhDisable || iter->second == EBhUndefined) + { error(line, "extension", extension.c_str(), "is disabled"); return true; } - if (iter->second == EBhWarn) { + if (iter->second == EBhWarn) + { warning(line, "extension", extension.c_str(), "is being used"); return false; } @@ -953,27 +997,57 @@ bool TParseContext::extensionErrorCheck(const TSourceLoc& line, const TString& e return false; } -bool TParseContext::singleDeclarationErrorCheck(TPublicType &publicType, const TSourceLoc& identifierLocation, const TString &identifier) +// These checks are common for all declarations starting a declarator list, and declarators that +// follow an empty declaration. +// +bool TParseContext::singleDeclarationErrorCheck(const TPublicType &publicType, + const TSourceLoc &identifierLocation) { - if (structQualifierErrorCheck(identifierLocation, publicType)) - return true; - - // check for layout qualifier issues - const TLayoutQualifier layoutQualifier = publicType.layoutQualifier; - - if (layoutQualifier.matrixPacking != EmpUnspecified) + switch (publicType.qualifier) { - error(identifierLocation, "layout qualifier", getMatrixPackingString(layoutQualifier.matrixPacking), "only valid for interface blocks"); - return true; + case EvqVaryingIn: + case EvqVaryingOut: + case EvqAttribute: + case EvqVertexIn: + case EvqFragmentOut: + if (publicType.type == EbtStruct) + { + error(identifierLocation, "cannot be used with a structure", + getQualifierString(publicType.qualifier)); + return true; + } + + default: + break; + } + + if (publicType.qualifier != EvqUniform && + samplerErrorCheck(identifierLocation, publicType, "samplers must be uniform")) + { + return true; + } + + // check for layout qualifier issues + const TLayoutQualifier layoutQualifier = publicType.layoutQualifier; + + if (layoutQualifier.matrixPacking != EmpUnspecified) + { + error(identifierLocation, "layout qualifier", + getMatrixPackingString(layoutQualifier.matrixPacking), + "only valid for interface blocks"); + return true; } if (layoutQualifier.blockStorage != EbsUnspecified) { - error(identifierLocation, "layout qualifier", getBlockStorageString(layoutQualifier.blockStorage), "only valid for interface blocks"); + error(identifierLocation, "layout qualifier", + getBlockStorageString(layoutQualifier.blockStorage), + "only valid for interface blocks"); return true; } - if (publicType.qualifier != EvqVertexIn && publicType.qualifier != EvqFragmentOut && layoutLocationErrorCheck(identifierLocation, publicType.layoutQualifier)) + if (publicType.qualifier != EvqVertexIn && publicType.qualifier != EvqFragmentOut && + layoutLocationErrorCheck(identifierLocation, publicType.layoutQualifier)) { return true; } @@ -981,18 +1055,21 @@ bool TParseContext::singleDeclarationErrorCheck(TPublicType &publicType, const T return false; } -bool TParseContext::layoutLocationErrorCheck(const TSourceLoc& location, const TLayoutQualifier &layoutQualifier) +bool TParseContext::layoutLocationErrorCheck(const TSourceLoc &location, + const TLayoutQualifier &layoutQualifier) { if (layoutQualifier.location != -1) { - error(location, "invalid layout qualifier:", "location", "only valid on program inputs and outputs"); + error(location, "invalid layout qualifier:", "location", + "only valid on program inputs and outputs"); return true; } return false; } -bool TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *aggregate) +bool TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate, + TIntermAggregate *aggregate) { for (size_t i = 0; i < fnCandidate->getParamCount(); ++i) { @@ -1003,7 +1080,7 @@ bool TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate, T if (lValueErrorCheck(node->getLine(), "assign", node)) { error(node->getLine(), - "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error"); + "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error"); recover(); return true; } @@ -1012,40 +1089,47 @@ bool TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate, T return false; } -bool TParseContext::supportsExtension(const char* extension) +void TParseContext::es3InvariantErrorCheck(const TQualifier qualifier, + const TSourceLoc &invariantLocation) { - const TExtensionBehavior& extbehavior = extensionBehavior(); - TExtensionBehavior::const_iterator iter = extbehavior.find(extension); - return (iter != extbehavior.end()); + if (!sh::IsVaryingOut(qualifier) && qualifier != EvqFragmentOut) + { + error(invariantLocation, "Only out variables can be invariant.", "invariant"); + recover(); + } } -bool TParseContext::isExtensionEnabled(const char* extension) const +bool TParseContext::supportsExtension(const char *extension) { - const TExtensionBehavior& extbehavior = extensionBehavior(); + const TExtensionBehavior &extbehavior = extensionBehavior(); TExtensionBehavior::const_iterator iter = extbehavior.find(extension); + return (iter != extbehavior.end()); +} - if (iter == extbehavior.end()) - { - return false; - } - - return (iter->second == EBhEnable || iter->second == EBhRequire); +bool TParseContext::isExtensionEnabled(const char *extension) const +{ + return ::IsExtensionEnabled(extensionBehavior(), extension); } -void TParseContext::handleExtensionDirective(const TSourceLoc& loc, const char* extName, const char* behavior) +void TParseContext::handleExtensionDirective(const TSourceLoc &loc, + const char *extName, + const char *behavior) { pp::SourceLocation srcLoc; srcLoc.file = loc.first_file; srcLoc.line = loc.first_line; - directiveHandler.handleExtension(srcLoc, extName, behavior); + mDirectiveHandler.handleExtension(srcLoc, extName, behavior); } -void TParseContext::handlePragmaDirective(const TSourceLoc& loc, const char* name, const char* value, bool stdgl) +void TParseContext::handlePragmaDirective(const TSourceLoc &loc, + const char *name, + const char *value, + bool stdgl) { pp::SourceLocation srcLoc; srcLoc.file = loc.first_file; srcLoc.line = loc.first_line; - directiveHandler.handlePragma(srcLoc, name, value, stdgl); + mDirectiveHandler.handlePragma(srcLoc, name, value, stdgl); } ///////////////////////////////////////////////////////////////////////////////// @@ -1072,14 +1156,46 @@ const TVariable *TParseContext::getNamedVariable(const TSourceLoc &location, } else { - variable = static_cast(symbol); + variable = static_cast(symbol); - if (symbolTable.findBuiltIn(variable->getName(), shaderVersion) && + if (symbolTable.findBuiltIn(variable->getName(), mShaderVersion) && !variable->getExtension().empty() && extensionErrorCheck(location, variable->getExtension())) { recover(); } + + // Reject shaders using both gl_FragData and gl_FragColor + TQualifier qualifier = variable->getType().getQualifier(); + if (qualifier == EvqFragData || qualifier == EvqSecondaryFragDataEXT) + { + mUsesFragData = true; + } + else if (qualifier == EvqFragColor || qualifier == EvqSecondaryFragColorEXT) + { + mUsesFragColor = true; + } + if (qualifier == EvqSecondaryFragDataEXT || qualifier == EvqSecondaryFragColorEXT) + { + mUsesSecondaryOutputs = true; + } + + // This validation is not quite correct - it's only an error to write to + // both FragData and FragColor. For simplicity, and because users shouldn't + // be rewarded for reading from undefined varaibles, return an error + // if they are both referenced, rather than assigned. + if (mUsesFragData && mUsesFragColor) + { + const char *errorMessage = "cannot use both gl_FragData and gl_FragColor"; + if (mUsesSecondaryOutputs) + { + errorMessage = + "cannot use both output variable sets (gl_FragData, gl_SecondaryFragDataEXT)" + " and (gl_FragColor, gl_SecondaryFragColorEXT)"; + } + error(location, errorMessage, name->c_str()); + recover(); + } } if (!variable) @@ -1093,32 +1209,56 @@ const TVariable *TParseContext::getNamedVariable(const TSourceLoc &location, return variable; } +TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location, + const TString *name, + const TSymbol *symbol) +{ + const TVariable *variable = getNamedVariable(location, name, symbol); + + if (variable->getConstPointer()) + { + const TConstantUnion *constArray = variable->getConstPointer(); + return intermediate.addConstantUnion(constArray, variable->getType(), location); + } + else + { + return intermediate.addSymbol(variable->getUniqueId(), variable->getName(), + variable->getType(), location); + } +} + // // Look up a function name in the symbol table, and make sure it is a function. // // Return the function symbol if found, otherwise 0. // -const TFunction* TParseContext::findFunction(const TSourceLoc& line, TFunction* call, int inputShaderVersion, bool *builtIn) +const TFunction *TParseContext::findFunction(const TSourceLoc &line, + TFunction *call, + int inputShaderVersion, + bool *builtIn) { // First find by unmangled name to check whether the function name has been // hidden by a variable name or struct typename. // If a function is found, check for one with a matching argument list. - const TSymbol* symbol = symbolTable.find(call->getName(), inputShaderVersion, builtIn); - if (symbol == 0 || symbol->isFunction()) { + const TSymbol *symbol = symbolTable.find(call->getName(), inputShaderVersion, builtIn); + if (symbol == 0 || symbol->isFunction()) + { symbol = symbolTable.find(call->getMangledName(), inputShaderVersion, builtIn); } - if (symbol == 0) { + if (symbol == 0) + { error(line, "no matching overloaded function found", call->getName().c_str()); return 0; } - if (!symbol->isFunction()) { + if (!symbol->isFunction()) + { error(line, "function name expected", call->getName().c_str()); return 0; } - return static_cast(symbol); + return static_cast(symbol); } // @@ -1127,44 +1267,61 @@ const TFunction* TParseContext::findFunction(const TSourceLoc& line, TFunction* // // Returns true on error, false if no error // -bool TParseContext::executeInitializer(const TSourceLoc& line, const TString& identifier, TPublicType& pType, - TIntermTyped* initializer, TIntermNode*& intermNode, TVariable* variable) +bool TParseContext::executeInitializer(const TSourceLoc &line, + const TString &identifier, + const TPublicType &pType, + TIntermTyped *initializer, + TIntermNode **intermNode) { + ASSERT(intermNode != nullptr); TType type = TType(pType); - if (variable == 0) { - if (reservedErrorCheck(line, identifier)) - return true; - - if (voidErrorCheck(line, identifier, pType)) - return true; + TVariable *variable = nullptr; + if (type.isUnsizedArray()) + { + type.setArraySize(initializer->getArraySize()); + } + if (!declareVariable(line, identifier, type, &variable)) + { + return true; + } - // - // add variable to symbol table - // - variable = new TVariable(&identifier, type); - if (! symbolTable.declare(variable)) { - error(line, "redefinition", variable->getName().c_str()); - return true; - // don't delete variable, it's used by error recovery, and the pool - // pop will take care of the memory - } + bool globalInitWarning = false; + if (symbolTable.atGlobalLevel() && + !ValidateGlobalInitializer(initializer, this, &globalInitWarning)) + { + // Error message does not completely match behavior with ESSL 1.00, but + // we want to steer developers towards only using constant expressions. + error(line, "global variable initializers must be constant expressions", "="); + return true; + } + if (globalInitWarning) + { + warning( + line, + "global variable initializers should be constant expressions " + "(uniforms and globals are allowed in global initializers for legacy compatibility)", + "="); } // // identifier must be of type constant, a global, or a temporary // TQualifier qualifier = variable->getType().getQualifier(); - if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal) && (qualifier != EvqConst)) { - error(line, " cannot initialize this type of qualifier ", variable->getType().getQualifierString()); + if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal) && (qualifier != EvqConst)) + { + error(line, " cannot initialize this type of qualifier ", + variable->getType().getQualifierString()); return true; } // // test for and propagate constant // - if (qualifier == EvqConst) { - if (qualifier != initializer->getType().getQualifier()) { + if (qualifier == EvqConst) + { + if (qualifier != initializer->getType().getQualifier()) + { std::stringstream extraInfoStream; extraInfoStream << "'" << variable->getType().getCompleteString() << "'"; std::string extraInfo = extraInfoStream.str(); @@ -1172,78 +1329,74 @@ bool TParseContext::executeInitializer(const TSourceLoc& line, const TString& id variable->getType().setQualifier(EvqTemporary); return true; } - if (type != initializer->getType()) { - error(line, " non-matching types for const initializer ", - variable->getType().getQualifierString()); + if (type != initializer->getType()) + { + error(line, " non-matching types for const initializer ", + variable->getType().getQualifierString()); variable->getType().setQualifier(EvqTemporary); return true; } - if (initializer->getAsConstantUnion()) { + + // Save the constant folded value to the variable if possible. For example array + // initializers are not folded, since that way copying the array literal to multiple places + // in the shader is avoided. + // TODO(oetuaho@nvidia.com): Consider constant folding array initialization in cases where + // it would be beneficial. + if (initializer->getAsConstantUnion()) + { variable->shareConstPointer(initializer->getAsConstantUnion()->getUnionArrayPointer()); - } else if (initializer->getAsSymbolNode()) { - const TSymbol* symbol = symbolTable.find(initializer->getAsSymbolNode()->getSymbol(), 0); - const TVariable* tVar = static_cast(symbol); + *intermNode = nullptr; + return false; + } + else if (initializer->getAsSymbolNode()) + { + const TSymbol *symbol = + symbolTable.find(initializer->getAsSymbolNode()->getSymbol(), 0); + const TVariable *tVar = static_cast(symbol); - ConstantUnion* constArray = tVar->getConstPointer(); - variable->shareConstPointer(constArray); - } else { - std::stringstream extraInfoStream; - extraInfoStream << "'" << variable->getType().getCompleteString() << "'"; - std::string extraInfo = extraInfoStream.str(); - error(line, " cannot assign to", "=", extraInfo.c_str()); - variable->getType().setQualifier(EvqTemporary); - return true; + const TConstantUnion *constArray = tVar->getConstPointer(); + if (constArray) + { + variable->shareConstPointer(constArray); + *intermNode = nullptr; + return false; + } } } - - if (qualifier != EvqConst) { - TIntermSymbol* intermSymbol = intermediate.addSymbol(variable->getUniqueId(), variable->getName(), variable->getType(), line); - intermNode = createAssign(EOpInitialize, intermSymbol, initializer, line); - if (intermNode == 0) { - assignError(line, "=", intermSymbol->getCompleteString(), initializer->getCompleteString()); - return true; - } - } else - intermNode = 0; - return false; -} - -bool TParseContext::areAllChildConst(TIntermAggregate* aggrNode) -{ - ASSERT(aggrNode != NULL); - if (!aggrNode->isConstructor()) - return false; - - bool allConstant = true; - - // check if all the child nodes are constants so that they can be inserted into - // the parent node - TIntermSequence *sequence = aggrNode->getSequence() ; - for (TIntermSequence::iterator p = sequence->begin(); p != sequence->end(); ++p) { - if (!(*p)->getAsTyped()->getAsConstantUnion()) - return false; + TIntermSymbol *intermSymbol = intermediate.addSymbol( + variable->getUniqueId(), variable->getName(), variable->getType(), line); + *intermNode = createAssign(EOpInitialize, intermSymbol, initializer, line); + if (*intermNode == nullptr) + { + assignError(line, "=", intermSymbol->getCompleteString(), initializer->getCompleteString()); + return true; } - return allConstant; + return false; } -TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier, TLayoutQualifier layoutQualifier, const TPublicType& typeSpecifier) +TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier, + bool invariant, + TLayoutQualifier layoutQualifier, + const TPublicType &typeSpecifier) { - TPublicType returnType = typeSpecifier; - returnType.qualifier = qualifier; + TPublicType returnType = typeSpecifier; + returnType.qualifier = qualifier; + returnType.invariant = invariant; returnType.layoutQualifier = layoutQualifier; - if (typeSpecifier.array) + if (mShaderVersion < 300) { - error(typeSpecifier.line, "not supported", "first-class array"); - recover(); - returnType.setArray(false); - } + if (typeSpecifier.array) + { + error(typeSpecifier.line, "not supported", "first-class array"); + recover(); + returnType.clearArrayness(); + } - if (shaderVersion < 300) - { - if (qualifier == EvqAttribute && (typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt)) + if (qualifier == EvqAttribute && + (typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt)) { error(typeSpecifier.line, "cannot be bool or int", getQualifierString(qualifier)); recover(); @@ -1258,134 +1411,258 @@ TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier, TLayoutQu } else { - switch (qualifier) + if (!layoutQualifier.isEmpty()) { - case EvqSmoothIn: - case EvqSmoothOut: - case EvqVertexOut: - case EvqFragmentIn: - case EvqCentroidOut: - case EvqCentroidIn: - if (typeSpecifier.type == EbtBool) + if (globalErrorCheck(typeSpecifier.line, symbolTable.atGlobalLevel(), "layout")) { - error(typeSpecifier.line, "cannot be bool", getQualifierString(qualifier)); recover(); } - if (typeSpecifier.type == EbtInt || typeSpecifier.type == EbtUInt) + } + if (sh::IsVarying(qualifier) || qualifier == EvqVertexIn || qualifier == EvqFragmentOut) + { + es3InputOutputTypeCheck(qualifier, typeSpecifier, typeSpecifier.line); + } + } + + return returnType; +} + +void TParseContext::es3InputOutputTypeCheck(const TQualifier qualifier, + const TPublicType &type, + const TSourceLoc &qualifierLocation) +{ + // An input/output variable can never be bool or a sampler. Samplers are checked elsewhere. + if (type.type == EbtBool) + { + error(qualifierLocation, "cannot be bool", getQualifierString(qualifier)); + recover(); + } + + // Specific restrictions apply for vertex shader inputs and fragment shader outputs. + switch (qualifier) + { + case EvqVertexIn: + // ESSL 3.00 section 4.3.4 + if (type.array) { - error(typeSpecifier.line, "must use 'flat' interpolation here", getQualifierString(qualifier)); + error(qualifierLocation, "cannot be array", getQualifierString(qualifier)); recover(); } - break; - - case EvqVertexIn: - case EvqFragmentOut: - case EvqFlatIn: - case EvqFlatOut: - if (typeSpecifier.type == EbtBool) + // Vertex inputs with a struct type are disallowed in singleDeclarationErrorCheck + return; + case EvqFragmentOut: + // ESSL 3.00 section 4.3.6 + if (type.isMatrix()) { - error(typeSpecifier.line, "cannot be bool", getQualifierString(qualifier)); + error(qualifierLocation, "cannot be matrix", getQualifierString(qualifier)); recover(); } + // Fragment outputs with a struct type are disallowed in singleDeclarationErrorCheck + return; + default: break; + } - default: break; - } + // Vertex shader outputs / fragment shader inputs have a different, slightly more lenient set of + // restrictions. + bool typeContainsIntegers = + (type.type == EbtInt || type.type == EbtUInt || type.isStructureContainingType(EbtInt) || + type.isStructureContainingType(EbtUInt)); + if (typeContainsIntegers && qualifier != EvqFlatIn && qualifier != EvqFlatOut) + { + error(qualifierLocation, "must use 'flat' interpolation here", + getQualifierString(qualifier)); + recover(); } - return returnType; + if (type.type == EbtStruct) + { + // ESSL 3.00 sections 4.3.4 and 4.3.6. + // These restrictions are only implied by the ESSL 3.00 spec, but + // the ESSL 3.10 spec lists these restrictions explicitly. + if (type.array) + { + error(qualifierLocation, "cannot be an array of structures", + getQualifierString(qualifier)); + recover(); + } + if (type.isStructureContainingArrays()) + { + error(qualifierLocation, "cannot be a structure containing an array", + getQualifierString(qualifier)); + recover(); + } + if (type.isStructureContainingType(EbtStruct)) + { + error(qualifierLocation, "cannot be a structure containing a structure", + getQualifierString(qualifier)); + recover(); + } + if (type.isStructureContainingType(EbtBool)) + { + error(qualifierLocation, "cannot be a structure containing a bool", + getQualifierString(qualifier)); + recover(); + } + } } -TIntermAggregate* TParseContext::parseSingleDeclaration(TPublicType &publicType, const TSourceLoc& identifierLocation, const TString &identifier) +TIntermAggregate *TParseContext::parseSingleDeclaration(TPublicType &publicType, + const TSourceLoc &identifierOrTypeLocation, + const TString &identifier) { - TIntermSymbol* symbol = intermediate.addSymbol(0, identifier, TType(publicType), identifierLocation); - TIntermAggregate* aggregate = intermediate.makeAggregate(symbol, identifierLocation); + TIntermSymbol *symbol = + intermediate.addSymbol(0, identifier, TType(publicType), identifierOrTypeLocation); - if (identifier != "") + bool emptyDeclaration = (identifier == ""); + + mDeferredSingleDeclarationErrorCheck = emptyDeclaration; + + if (emptyDeclaration) + { + if (publicType.isUnsizedArray()) + { + // ESSL3 spec section 4.1.9: Array declaration which leaves the size unspecified is an + // error. It is assumed that this applies to empty declarations as well. + error(identifierOrTypeLocation, "empty array declaration needs to specify a size", + identifier.c_str()); + } + } + else { - if (singleDeclarationErrorCheck(publicType, identifierLocation, identifier)) + if (singleDeclarationErrorCheck(publicType, identifierOrTypeLocation)) recover(); - // this error check can mutate the type - if (nonInitConstErrorCheck(identifierLocation, identifier, publicType, false)) + if (nonInitErrorCheck(identifierOrTypeLocation, identifier, &publicType)) recover(); - TVariable* variable = 0; - - if (nonInitErrorCheck(identifierLocation, identifier, publicType, variable)) + TVariable *variable = nullptr; + if (!declareVariable(identifierOrTypeLocation, identifier, TType(publicType), &variable)) recover(); if (variable && symbol) - { symbol->setId(variable->getUniqueId()); - } } - return aggregate; + return intermediate.makeAggregate(symbol, identifierOrTypeLocation); } -TIntermAggregate* TParseContext::parseSingleArrayDeclaration(TPublicType &publicType, const TSourceLoc& identifierLocation, const TString &identifier, const TSourceLoc& indexLocation, TIntermTyped *indexExpression) +TIntermAggregate *TParseContext::parseSingleArrayDeclaration(TPublicType &publicType, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &indexLocation, + TIntermTyped *indexExpression) { - if (singleDeclarationErrorCheck(publicType, identifierLocation, identifier)) + mDeferredSingleDeclarationErrorCheck = false; + + if (singleDeclarationErrorCheck(publicType, identifierLocation)) recover(); - // this error check can mutate the type - if (nonInitConstErrorCheck(identifierLocation, identifier, publicType, true)) + if (nonInitErrorCheck(identifierLocation, identifier, &publicType)) recover(); - if (arrayTypeErrorCheck(indexLocation, publicType) || arrayQualifierErrorCheck(indexLocation, publicType)) + if (arrayTypeErrorCheck(indexLocation, publicType) || + arrayQualifierErrorCheck(indexLocation, publicType)) { recover(); } - TPublicType arrayType = publicType; + TType arrayType(publicType); int size; if (arraySizeErrorCheck(identifierLocation, indexExpression, size)) { recover(); } - else - { - arrayType.setArray(true, size); - } + // Make the type an array even if size check failed. + // This ensures useless error messages regarding the variable's non-arrayness won't follow. + arrayType.setArraySize(size); - TIntermSymbol* symbol = intermediate.addSymbol(0, identifier, TType(arrayType), identifierLocation); - TIntermAggregate* aggregate = intermediate.makeAggregate(symbol, identifierLocation); - TVariable* variable = 0; - - if (arrayErrorCheck(identifierLocation, identifier, arrayType, variable)) + TVariable *variable = nullptr; + if (!declareVariable(identifierLocation, identifier, arrayType, &variable)) recover(); + TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, arrayType, identifierLocation); if (variable && symbol) - { symbol->setId(variable->getUniqueId()); - } - return aggregate; + return intermediate.makeAggregate(symbol, identifierLocation); } -TIntermAggregate* TParseContext::parseSingleInitDeclaration(TPublicType &publicType, const TSourceLoc& identifierLocation, const TString &identifier, const TSourceLoc& initLocation, TIntermTyped *initializer) +TIntermAggregate *TParseContext::parseSingleInitDeclaration(const TPublicType &publicType, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &initLocation, + TIntermTyped *initializer) { - if (singleDeclarationErrorCheck(publicType, identifierLocation, identifier)) + mDeferredSingleDeclarationErrorCheck = false; + + if (singleDeclarationErrorCheck(publicType, identifierLocation)) recover(); - TIntermNode* intermNode; - if (!executeInitializer(identifierLocation, identifier, publicType, initializer, intermNode)) + TIntermNode *intermNode = nullptr; + if (!executeInitializer(identifierLocation, identifier, publicType, initializer, &intermNode)) { // // Build intermediate representation // - return intermNode ? intermediate.makeAggregate(intermNode, initLocation) : NULL; + return intermNode ? intermediate.makeAggregate(intermNode, initLocation) : nullptr; + } + else + { + recover(); + return nullptr; + } +} + +TIntermAggregate *TParseContext::parseSingleArrayInitDeclaration( + TPublicType &publicType, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &indexLocation, + TIntermTyped *indexExpression, + const TSourceLoc &initLocation, + TIntermTyped *initializer) +{ + mDeferredSingleDeclarationErrorCheck = false; + + if (singleDeclarationErrorCheck(publicType, identifierLocation)) + recover(); + + if (arrayTypeErrorCheck(indexLocation, publicType) || + arrayQualifierErrorCheck(indexLocation, publicType)) + { + recover(); + } + + TPublicType arrayType(publicType); + + int size = 0; + // If indexExpression is nullptr, then the array will eventually get its size implicitly from + // the initializer. + if (indexExpression != nullptr && + arraySizeErrorCheck(identifierLocation, indexExpression, size)) + { + recover(); + } + // Make the type an array even if size check failed. + // This ensures useless error messages regarding the variable's non-arrayness won't follow. + arrayType.setArraySize(size); + + // initNode will correspond to the whole of "type b[n] = initializer". + TIntermNode *initNode = nullptr; + if (!executeInitializer(identifierLocation, identifier, arrayType, initializer, &initNode)) + { + return initNode ? intermediate.makeAggregate(initNode, initLocation) : nullptr; } else { recover(); - return NULL; + return nullptr; } } -TIntermAggregate* TParseContext::parseInvariantDeclaration(const TSourceLoc &invariantLoc, +TIntermAggregate *TParseContext::parseInvariantDeclaration(const TSourceLoc &invariantLoc, const TSourceLoc &identifierLoc, const TString *identifier, const TSymbol *symbol) @@ -1400,23 +1677,24 @@ TIntermAggregate* TParseContext::parseInvariantDeclaration(const TSourceLoc &inv { error(identifierLoc, "undeclared identifier declared as invariant", identifier->c_str()); recover(); - return NULL; + return nullptr; } else { const TString kGlFrontFacing("gl_FrontFacing"); if (*identifier == kGlFrontFacing) { - error(identifierLoc, "identifier should not be declared as invariant", identifier->c_str()); + error(identifierLoc, "identifier should not be declared as invariant", + identifier->c_str()); recover(); - return NULL; + return nullptr; } symbolTable.addInvariantVarying(std::string(identifier->c_str())); const TVariable *variable = getNamedVariable(identifierLoc, identifier, symbol); ASSERT(variable); const TType &type = variable->getType(); - TIntermSymbol *intermSymbol = intermediate.addSymbol(variable->getUniqueId(), - *identifier, type, identifierLoc); + TIntermSymbol *intermSymbol = + intermediate.addSymbol(variable->getUniqueId(), *identifier, type, identifierLoc); TIntermAggregate *aggregate = intermediate.makeAggregate(intermSymbol, identifierLoc); aggregate->setOp(EOpInvariantDeclaration); @@ -1424,98 +1702,189 @@ TIntermAggregate* TParseContext::parseInvariantDeclaration(const TSourceLoc &inv } } -TIntermAggregate* TParseContext::parseDeclarator(TPublicType &publicType, TIntermAggregate *aggregateDeclaration, TSymbol *identifierSymbol, const TSourceLoc& identifierLocation, const TString &identifier) +TIntermAggregate *TParseContext::parseDeclarator(TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier) { - TIntermSymbol* symbol = intermediate.addSymbol(0, identifier, TType(publicType), identifierLocation); - TIntermAggregate* intermAggregate = intermediate.growAggregate(aggregateDeclaration, symbol, identifierLocation); - - if (structQualifierErrorCheck(identifierLocation, publicType)) - recover(); + // If the declaration starting this declarator list was empty (example: int,), some checks were + // not performed. + if (mDeferredSingleDeclarationErrorCheck) + { + if (singleDeclarationErrorCheck(publicType, identifierLocation)) + recover(); + mDeferredSingleDeclarationErrorCheck = false; + } if (locationDeclaratorListCheck(identifierLocation, publicType)) recover(); - if (nonInitConstErrorCheck(identifierLocation, identifier, publicType, false)) + if (nonInitErrorCheck(identifierLocation, identifier, &publicType)) recover(); - TVariable* variable = 0; - if (nonInitErrorCheck(identifierLocation, identifier, publicType, variable)) + TVariable *variable = nullptr; + if (!declareVariable(identifierLocation, identifier, TType(publicType), &variable)) recover(); - if (symbol && variable) + + TIntermSymbol *symbol = + intermediate.addSymbol(0, identifier, TType(publicType), identifierLocation); + if (variable && symbol) symbol->setId(variable->getUniqueId()); - return intermAggregate; + return intermediate.growAggregate(aggregateDeclaration, symbol, identifierLocation); } -TIntermAggregate* TParseContext::parseArrayDeclarator(TPublicType &publicType, const TSourceLoc& identifierLocation, const TString &identifier, const TSourceLoc& arrayLocation, TIntermNode *declaratorList, TIntermTyped *indexExpression) +TIntermAggregate *TParseContext::parseArrayDeclarator(TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &arrayLocation, + TIntermTyped *indexExpression) { - if (structQualifierErrorCheck(identifierLocation, publicType)) - recover(); + // If the declaration starting this declarator list was empty (example: int,), some checks were + // not performed. + if (mDeferredSingleDeclarationErrorCheck) + { + if (singleDeclarationErrorCheck(publicType, identifierLocation)) + recover(); + mDeferredSingleDeclarationErrorCheck = false; + } if (locationDeclaratorListCheck(identifierLocation, publicType)) recover(); - if (nonInitConstErrorCheck(identifierLocation, identifier, publicType, true)) + if (nonInitErrorCheck(identifierLocation, identifier, &publicType)) recover(); - if (arrayTypeErrorCheck(arrayLocation, publicType) || arrayQualifierErrorCheck(arrayLocation, publicType)) + if (arrayTypeErrorCheck(arrayLocation, publicType) || + arrayQualifierErrorCheck(arrayLocation, publicType)) { recover(); } - else if (indexExpression) + else { + TType arrayType = TType(publicType); int size; if (arraySizeErrorCheck(arrayLocation, indexExpression, size)) + { recover(); - TPublicType arrayType(publicType); - arrayType.setArray(true, size); - TVariable* variable = NULL; - if (arrayErrorCheck(arrayLocation, identifier, arrayType, variable)) - recover(); - TType type = TType(arrayType); - type.setArraySize(size); + } + arrayType.setArraySize(size); - return intermediate.growAggregate(declaratorList, intermediate.addSymbol(variable ? variable->getUniqueId() : 0, identifier, type, identifierLocation), identifierLocation); - } - else - { - TPublicType arrayType(publicType); - arrayType.setArray(true); - TVariable* variable = NULL; - if (arrayErrorCheck(arrayLocation, identifier, arrayType, variable)) + TVariable *variable = nullptr; + if (!declareVariable(identifierLocation, identifier, arrayType, &variable)) recover(); + + TIntermSymbol *symbol = + intermediate.addSymbol(0, identifier, arrayType, identifierLocation); + if (variable && symbol) + symbol->setId(variable->getUniqueId()); + + return intermediate.growAggregate(aggregateDeclaration, symbol, identifierLocation); } - return NULL; + return nullptr; } -TIntermAggregate* TParseContext::parseInitDeclarator(TPublicType &publicType, TIntermAggregate *declaratorList, const TSourceLoc& identifierLocation, const TString &identifier, const TSourceLoc& initLocation, TIntermTyped *initializer) +TIntermAggregate *TParseContext::parseInitDeclarator(const TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &initLocation, + TIntermTyped *initializer) { - if (structQualifierErrorCheck(identifierLocation, publicType)) - recover(); + // If the declaration starting this declarator list was empty (example: int,), some checks were + // not performed. + if (mDeferredSingleDeclarationErrorCheck) + { + if (singleDeclarationErrorCheck(publicType, identifierLocation)) + recover(); + mDeferredSingleDeclarationErrorCheck = false; + } if (locationDeclaratorListCheck(identifierLocation, publicType)) recover(); - TIntermNode* intermNode; - if (!executeInitializer(identifierLocation, identifier, publicType, initializer, intermNode)) + TIntermNode *intermNode = nullptr; + if (!executeInitializer(identifierLocation, identifier, publicType, initializer, &intermNode)) { // // build the intermediate representation // if (intermNode) { - return intermediate.growAggregate(declaratorList, intermNode, initLocation); + return intermediate.growAggregate(aggregateDeclaration, intermNode, initLocation); + } + else + { + return aggregateDeclaration; + } + } + else + { + recover(); + return nullptr; + } +} + +TIntermAggregate *TParseContext::parseArrayInitDeclarator(const TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &indexLocation, + TIntermTyped *indexExpression, + const TSourceLoc &initLocation, + TIntermTyped *initializer) +{ + // If the declaration starting this declarator list was empty (example: int,), some checks were + // not performed. + if (mDeferredSingleDeclarationErrorCheck) + { + if (singleDeclarationErrorCheck(publicType, identifierLocation)) + recover(); + mDeferredSingleDeclarationErrorCheck = false; + } + + if (locationDeclaratorListCheck(identifierLocation, publicType)) + recover(); + + if (arrayTypeErrorCheck(indexLocation, publicType) || + arrayQualifierErrorCheck(indexLocation, publicType)) + { + recover(); + } + + TPublicType arrayType(publicType); + + int size = 0; + // If indexExpression is nullptr, then the array will eventually get its size implicitly from + // the initializer. + if (indexExpression != nullptr && + arraySizeErrorCheck(identifierLocation, indexExpression, size)) + { + recover(); + } + // Make the type an array even if size check failed. + // This ensures useless error messages regarding the variable's non-arrayness won't follow. + arrayType.setArraySize(size); + + // initNode will correspond to the whole of "b[n] = initializer". + TIntermNode *initNode = nullptr; + if (!executeInitializer(identifierLocation, identifier, arrayType, initializer, &initNode)) + { + if (initNode) + { + return intermediate.growAggregate(aggregateDeclaration, initNode, initLocation); } else { - return declaratorList; + return aggregateDeclaration; } } else { recover(); - return NULL; + return nullptr; } } @@ -1523,7 +1892,8 @@ void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier) { if (typeQualifier.qualifier != EvqUniform) { - error(typeQualifier.line, "invalid qualifier:", getQualifierString(typeQualifier.qualifier), "global layout must be uniform"); + error(typeQualifier.line, "invalid qualifier:", getQualifierString(typeQualifier.qualifier), + "global layout must be uniform"); recover(); return; } @@ -1531,7 +1901,7 @@ void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier) const TLayoutQualifier layoutQualifier = typeQualifier.layoutQualifier; ASSERT(!layoutQualifier.isEmpty()); - if (shaderVersion < 300) + if (mShaderVersion < 300) { error(typeQualifier.line, "layout qualifiers supported in GLSL ES 3.00 only", "layout"); recover(); @@ -1546,17 +1916,269 @@ void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier) if (layoutQualifier.matrixPacking != EmpUnspecified) { - defaultMatrixPacking = layoutQualifier.matrixPacking; + mDefaultMatrixPacking = layoutQualifier.matrixPacking; } if (layoutQualifier.blockStorage != EbsUnspecified) { - defaultBlockStorage = layoutQualifier.blockStorage; + mDefaultBlockStorage = layoutQualifier.blockStorage; + } +} + +TIntermAggregate *TParseContext::addFunctionPrototypeDeclaration(const TFunction &function, + const TSourceLoc &location) +{ + // Note: symbolTableFunction could be the same as function if this is the first declaration. + // Either way the instance in the symbol table is used to track whether the function is declared + // multiple times. + TFunction *symbolTableFunction = + static_cast(symbolTable.find(function.getMangledName(), getShaderVersion())); + if (symbolTableFunction->hasPrototypeDeclaration() && mShaderVersion == 100) + { + // ESSL 1.00.17 section 4.2.7. + // Doesn't apply to ESSL 3.00.4: see section 4.2.3. + error(location, "duplicate function prototype declarations are not allowed", "function"); + recover(); + } + symbolTableFunction->setHasPrototypeDeclaration(); + + TIntermAggregate *prototype = new TIntermAggregate; + prototype->setType(function.getReturnType()); + prototype->setName(function.getMangledName()); + prototype->setFunctionId(function.getUniqueId()); + + for (size_t i = 0; i < function.getParamCount(); i++) + { + const TConstParameter ¶m = function.getParam(i); + if (param.name != 0) + { + TVariable variable(param.name, *param.type); + + TIntermSymbol *paramSymbol = intermediate.addSymbol( + variable.getUniqueId(), variable.getName(), variable.getType(), location); + prototype = intermediate.growAggregate(prototype, paramSymbol, location); + } + else + { + TIntermSymbol *paramSymbol = intermediate.addSymbol(0, "", *param.type, location); + prototype = intermediate.growAggregate(prototype, paramSymbol, location); + } + } + + prototype->setOp(EOpPrototype); + + symbolTable.pop(); + + if (!symbolTable.atGlobalLevel()) + { + // ESSL 3.00.4 section 4.2.4. + error(location, "local function prototype declarations are not allowed", "function"); + recover(); + } + + return prototype; +} + +TIntermAggregate *TParseContext::addFunctionDefinition(const TFunction &function, + TIntermAggregate *functionPrototype, + TIntermAggregate *functionBody, + const TSourceLoc &location) +{ + //?? Check that all paths return a value if return type != void ? + // May be best done as post process phase on intermediate code + if (mCurrentFunctionType->getBasicType() != EbtVoid && !mFunctionReturnsValue) + { + error(location, "function does not return a value:", "", function.getName().c_str()); + recover(); } + + TIntermAggregate *aggregate = + intermediate.growAggregate(functionPrototype, functionBody, location); + intermediate.setAggregateOperator(aggregate, EOpFunction, location); + aggregate->setName(function.getMangledName().c_str()); + aggregate->setType(function.getReturnType()); + aggregate->setFunctionId(function.getUniqueId()); + + symbolTable.pop(); + return aggregate; } -TFunction *TParseContext::addConstructorFunc(TPublicType publicType) +void TParseContext::parseFunctionPrototype(const TSourceLoc &location, + TFunction *function, + TIntermAggregate **aggregateOut) { + const TSymbol *builtIn = + symbolTable.findBuiltIn(function->getMangledName(), getShaderVersion()); + + if (builtIn) + { + error(location, "built-in functions cannot be redefined", function->getName().c_str()); + recover(); + } + + TFunction *prevDec = + static_cast(symbolTable.find(function->getMangledName(), getShaderVersion())); + // + // Note: 'prevDec' could be 'function' if this is the first time we've seen function + // as it would have just been put in the symbol table. Otherwise, we're looking up + // an earlier occurance. + // + if (prevDec->isDefined()) + { + // Then this function already has a body. + error(location, "function already has a body", function->getName().c_str()); + recover(); + } + prevDec->setDefined(); + // + // Overload the unique ID of the definition to be the same unique ID as the declaration. + // Eventually we will probably want to have only a single definition and just swap the + // arguments to be the definition's arguments. + // + function->setUniqueId(prevDec->getUniqueId()); + + // Raise error message if main function takes any parameters or return anything other than void + if (function->getName() == "main") + { + if (function->getParamCount() > 0) + { + error(location, "function cannot take any parameter(s)", function->getName().c_str()); + recover(); + } + if (function->getReturnType().getBasicType() != EbtVoid) + { + error(location, "", function->getReturnType().getBasicString(), + "main function cannot return a value"); + recover(); + } + } + + // + // Remember the return type for later checking for RETURN statements. + // + mCurrentFunctionType = &(prevDec->getReturnType()); + mFunctionReturnsValue = false; + + // + // Insert parameters into the symbol table. + // If the parameter has no name, it's not an error, just don't insert it + // (could be used for unused args). + // + // Also, accumulate the list of parameters into the HIL, so lower level code + // knows where to find parameters. + // + TIntermAggregate *paramNodes = new TIntermAggregate; + for (size_t i = 0; i < function->getParamCount(); i++) + { + const TConstParameter ¶m = function->getParam(i); + if (param.name != 0) + { + TVariable *variable = new TVariable(param.name, *param.type); + // + // Insert the parameters with name in the symbol table. + // + if (!symbolTable.declare(variable)) + { + error(location, "redefinition", variable->getName().c_str()); + recover(); + paramNodes = intermediate.growAggregate( + paramNodes, intermediate.addSymbol(0, "", *param.type, location), location); + continue; + } + + // + // Add the parameter to the HIL + // + TIntermSymbol *symbol = intermediate.addSymbol( + variable->getUniqueId(), variable->getName(), variable->getType(), location); + + paramNodes = intermediate.growAggregate(paramNodes, symbol, location); + } + else + { + paramNodes = intermediate.growAggregate( + paramNodes, intermediate.addSymbol(0, "", *param.type, location), location); + } + } + intermediate.setAggregateOperator(paramNodes, EOpParameters, location); + *aggregateOut = paramNodes; + setLoopNestingLevel(0); +} + +TFunction *TParseContext::parseFunctionDeclarator(const TSourceLoc &location, TFunction *function) +{ + // + // We don't know at this point whether this is a function definition or a prototype. + // The definition production code will check for redefinitions. + // In the case of ESSL 1.00 the prototype production code will also check for redeclarations. + // + // Return types and parameter qualifiers must match in all redeclarations, so those are checked + // here. + // + TFunction *prevDec = + static_cast(symbolTable.find(function->getMangledName(), getShaderVersion())); + if (prevDec) + { + if (prevDec->getReturnType() != function->getReturnType()) + { + error(location, "overloaded functions must have the same return type", + function->getReturnType().getBasicString()); + recover(); + } + for (size_t i = 0; i < prevDec->getParamCount(); ++i) + { + if (prevDec->getParam(i).type->getQualifier() != + function->getParam(i).type->getQualifier()) + { + error(location, "overloaded functions must have the same parameter qualifiers", + function->getParam(i).type->getQualifierString()); + recover(); + } + } + } + + // + // Check for previously declared variables using the same name. + // + TSymbol *prevSym = symbolTable.find(function->getName(), getShaderVersion()); + if (prevSym) + { + if (!prevSym->isFunction()) + { + error(location, "redefinition", function->getName().c_str(), "function"); + recover(); + } + } + else + { + // Insert the unmangled name to detect potential future redefinition as a variable. + TFunction *newFunction = + new TFunction(NewPoolTString(function->getName().c_str()), &function->getReturnType()); + symbolTable.getOuterLevel()->insertUnmangled(newFunction); + } + + // We're at the inner scope level of the function's arguments and body statement. + // Add the function prototype to the surrounding scope instead. + symbolTable.getOuterLevel()->insert(function); + + // + // If this is a redeclaration, it could also be a definition, in which case, we want to use the + // variable names from this one, and not the one that's + // being redeclared. So, pass back up this declaration, not the one in the symbol table. + // + return function; +} + +TFunction *TParseContext::addConstructorFunc(const TPublicType &publicTypeIn) +{ + TPublicType publicType = publicTypeIn; + if (publicType.isStructSpecifier) + { + error(publicType.line, "constructor can't be a structure definition", + getBasicString(publicType.type)); + recover(); + } + TOperator op = EOpNull; if (publicType.userDef) { @@ -1566,60 +2188,131 @@ TFunction *TParseContext::addConstructorFunc(TPublicType publicType) { switch (publicType.type) { - case EbtFloat: - if (publicType.isMatrix()) - { - // TODO: non-square matrices - switch(publicType.getCols()) + case EbtFloat: + if (publicType.isMatrix()) { - case 2: op = EOpConstructMat2; break; - case 3: op = EOpConstructMat3; break; - case 4: op = EOpConstructMat4; break; + switch (publicType.getCols()) + { + case 2: + switch (publicType.getRows()) + { + case 2: + op = EOpConstructMat2; + break; + case 3: + op = EOpConstructMat2x3; + break; + case 4: + op = EOpConstructMat2x4; + break; + } + break; + case 3: + switch (publicType.getRows()) + { + case 2: + op = EOpConstructMat3x2; + break; + case 3: + op = EOpConstructMat3; + break; + case 4: + op = EOpConstructMat3x4; + break; + } + break; + case 4: + switch (publicType.getRows()) + { + case 2: + op = EOpConstructMat4x2; + break; + case 3: + op = EOpConstructMat4x3; + break; + case 4: + op = EOpConstructMat4; + break; + } + break; + } } - } - else - { - switch(publicType.getNominalSize()) + else { - case 1: op = EOpConstructFloat; break; - case 2: op = EOpConstructVec2; break; - case 3: op = EOpConstructVec3; break; - case 4: op = EOpConstructVec4; break; + switch (publicType.getNominalSize()) + { + case 1: + op = EOpConstructFloat; + break; + case 2: + op = EOpConstructVec2; + break; + case 3: + op = EOpConstructVec3; + break; + case 4: + op = EOpConstructVec4; + break; + } } - } - break; + break; - case EbtInt: - switch(publicType.getNominalSize()) - { - case 1: op = EOpConstructInt; break; - case 2: op = EOpConstructIVec2; break; - case 3: op = EOpConstructIVec3; break; - case 4: op = EOpConstructIVec4; break; - } - break; + case EbtInt: + switch (publicType.getNominalSize()) + { + case 1: + op = EOpConstructInt; + break; + case 2: + op = EOpConstructIVec2; + break; + case 3: + op = EOpConstructIVec3; + break; + case 4: + op = EOpConstructIVec4; + break; + } + break; - case EbtUInt: - switch(publicType.getNominalSize()) - { - case 1: op = EOpConstructUInt; break; - case 2: op = EOpConstructUVec2; break; - case 3: op = EOpConstructUVec3; break; - case 4: op = EOpConstructUVec4; break; - } - break; + case EbtUInt: + switch (publicType.getNominalSize()) + { + case 1: + op = EOpConstructUInt; + break; + case 2: + op = EOpConstructUVec2; + break; + case 3: + op = EOpConstructUVec3; + break; + case 4: + op = EOpConstructUVec4; + break; + } + break; - case EbtBool: - switch(publicType.getNominalSize()) - { - case 1: op = EOpConstructBool; break; - case 2: op = EOpConstructBVec2; break; - case 3: op = EOpConstructBVec3; break; - case 4: op = EOpConstructBVec4; break; - } - break; + case EbtBool: + switch (publicType.getNominalSize()) + { + case 1: + op = EOpConstructBool; + break; + case 2: + op = EOpConstructBVec2; + break; + case 3: + op = EOpConstructBVec3; + break; + case 4: + op = EOpConstructBVec4; + break; + } + break; - default: break; + default: + break; } if (op == EOpNull) @@ -1627,40 +2320,58 @@ TFunction *TParseContext::addConstructorFunc(TPublicType publicType) error(publicType.line, "cannot construct this type", getBasicString(publicType.type)); recover(); publicType.type = EbtFloat; - op = EOpConstructFloat; + op = EOpConstructFloat; } } TString tempString; - TType type(publicType); + const TType *type = new TType(publicType); return new TFunction(&tempString, type, op); } -// This function is used to test for the correctness of the parameters passed to various constructor functions -// and also convert them to the right datatype if it is allowed and required. +// This function is used to test for the correctness of the parameters passed to various constructor +// functions and also convert them to the right datatype if it is allowed and required. // // Returns 0 for an error or the constructed node (aggregate or typed) for no error. // -TIntermTyped *TParseContext::addConstructor(TIntermNode *arguments, TType *type, TOperator op, TFunction *fnCall, const TSourceLoc &line) +TIntermTyped *TParseContext::addConstructor(TIntermNode *arguments, + TType *type, + TOperator op, + TFunction *fnCall, + const TSourceLoc &line) { - TIntermAggregate *aggregateArguments = arguments->getAsAggregate(); + TIntermAggregate *constructor = arguments->getAsAggregate(); + ASSERT(constructor != nullptr); - if (!aggregateArguments) + if (type->isArray()) { - aggregateArguments = new TIntermAggregate; - aggregateArguments->getSequence()->push_back(arguments); + // GLSL ES 3.00 section 5.4.4: Each argument must be the same type as the element type of + // the array. + TIntermSequence *args = constructor->getSequence(); + for (size_t i = 0; i < args->size(); i++) + { + const TType &argType = (*args)[i]->getAsTyped()->getType(); + // It has already been checked that the argument is not an array. + ASSERT(!argType.isArray()); + if (!argType.sameElementType(*type)) + { + error(line, "Array constructor argument has an incorrect type", "Error"); + recover(); + return nullptr; + } + } } - - if (op == EOpConstructStruct) + else if (op == EOpConstructStruct) { const TFieldList &fields = type->getStruct()->fields(); - TIntermSequence *args = aggregateArguments->getSequence(); + TIntermSequence *args = constructor->getSequence(); for (size_t i = 0; i < fields.size(); i++) { if (i >= args->size() || (*args)[i]->getAsTyped()->getType() != *fields[i]->type()) { - error(line, "Structure constructor arguments do not match structure fields", "Error"); + error(line, "Structure constructor arguments do not match structure fields", + "Error"); recover(); return 0; @@ -1669,12 +2380,12 @@ TIntermTyped *TParseContext::addConstructor(TIntermNode *arguments, TType *type, } // Turn the argument list itself into a constructor - TIntermAggregate *constructor = intermediate.setAggregateOperator(aggregateArguments, op, line); - TIntermTyped *constConstructor = foldConstConstructor(constructor, *type); - if (constConstructor) - { - return constConstructor; - } + constructor->setOp(op); + constructor->setLine(line); + ASSERT(constructor->isConstructor()); + + // Need to set type before setPrecisionFromChildren() because bool doesn't have precision. + constructor->setType(*type); // Structs should not be precision qualified, the individual members may be. // Built-in types on the other hand should be precision qualified. @@ -1684,173 +2395,142 @@ TIntermTyped *TParseContext::addConstructor(TIntermNode *arguments, TType *type, type->setPrecision(constructor->getPrecision()); } - return constructor; -} - -TIntermTyped* TParseContext::foldConstConstructor(TIntermAggregate* aggrNode, const TType& type) -{ - bool canBeFolded = areAllChildConst(aggrNode); - aggrNode->setType(type); - if (canBeFolded) { - bool returnVal = false; - ConstantUnion* unionArray = new ConstantUnion[type.getObjectSize()]; - if (aggrNode->getSequence()->size() == 1) { - returnVal = intermediate.parseConstTree(aggrNode->getLine(), aggrNode, unionArray, aggrNode->getOp(), type, true); - } - else { - returnVal = intermediate.parseConstTree(aggrNode->getLine(), aggrNode, unionArray, aggrNode->getOp(), type); - } - if (returnVal) - return 0; - - return intermediate.addConstantUnion(unionArray, type, aggrNode->getLine()); + TIntermTyped *constConstructor = intermediate.foldAggregateBuiltIn(constructor); + if (constConstructor) + { + return constConstructor; } - return 0; + return constructor; } // -// This function returns the tree representation for the vector field(s) being accessed from contant vector. -// If only one component of vector is accessed (v.x or v[0] where v is a contant vector), then a contant node is -// returned, else an aggregate node is returned (for v.xy). The input to this function could either be the symbol -// node or it could be the intermediate tree representation of accessing fields in a constant structure or column of -// a constant matrix. +// This function returns the tree representation for the vector field(s) being accessed from contant +// vector. +// If only one component of vector is accessed (v.x or v[0] where v is a contant vector), then a +// contant node is returned, else an aggregate node is returned (for v.xy). The input to this +// function could either be the symbol node or it could be the intermediate tree representation of +// accessing fields in a constant structure or column of a constant matrix. // -TIntermTyped* TParseContext::addConstVectorNode(TVectorFields& fields, TIntermTyped* node, const TSourceLoc& line) +TIntermTyped *TParseContext::addConstVectorNode(TVectorFields &fields, + TIntermConstantUnion *node, + const TSourceLoc &line, + bool outOfRangeIndexIsError) { - TIntermTyped* typedNode; - TIntermConstantUnion* tempConstantNode = node->getAsConstantUnion(); - - ConstantUnion *unionArray; - if (tempConstantNode) { - unionArray = tempConstantNode->getUnionArrayPointer(); + const TConstantUnion *unionArray = node->getUnionArrayPointer(); + ASSERT(unionArray); - if (!unionArray) { - return node; - } - } else { // The node has to be either a symbol node or an aggregate node or a tempConstant node, else, its an error - error(line, "Cannot offset into the vector", "Error"); - recover(); - - return 0; - } - - ConstantUnion* constArray = new ConstantUnion[fields.num]; + TConstantUnion *constArray = new TConstantUnion[fields.num]; - for (int i = 0; i < fields.num; i++) { - if (fields.offsets[i] >= node->getType().getNominalSize()) { + for (int i = 0; i < fields.num; i++) + { + if (fields.offsets[i] >= node->getType().getNominalSize()) + { std::stringstream extraInfoStream; extraInfoStream << "vector field selection out of range '" << fields.offsets[i] << "'"; std::string extraInfo = extraInfoStream.str(); - error(line, "", "[", extraInfo.c_str()); - recover(); - fields.offsets[i] = 0; + outOfRangeError(outOfRangeIndexIsError, line, "", "[", extraInfo.c_str()); + fields.offsets[i] = node->getType().getNominalSize() - 1; } - - constArray[i] = unionArray[fields.offsets[i]]; - } - typedNode = intermediate.addConstantUnion(constArray, node->getType(), line); - return typedNode; + constArray[i] = unionArray[fields.offsets[i]]; + } + return intermediate.addConstantUnion(constArray, node->getType(), line); } // -// This function returns the column being accessed from a constant matrix. The values are retrieved from -// the symbol table and parse-tree is built for a vector (each column of a matrix is a vector). The input -// to the function could either be a symbol node (m[0] where m is a constant matrix)that represents a -// constant matrix or it could be the tree representation of the constant matrix (s.m1[0] where s is a constant structure) +// This function returns the column being accessed from a constant matrix. The values are retrieved +// from the symbol table and parse-tree is built for a vector (each column of a matrix is a vector). +// The input to the function could either be a symbol node (m[0] where m is a constant matrix)that +// represents a constant matrix or it could be the tree representation of the constant matrix +// (s.m1[0] where s is a constant structure) // -TIntermTyped* TParseContext::addConstMatrixNode(int index, TIntermTyped* node, const TSourceLoc& line) +TIntermTyped *TParseContext::addConstMatrixNode(int index, + TIntermConstantUnion *node, + const TSourceLoc &line, + bool outOfRangeIndexIsError) { - TIntermTyped* typedNode; - TIntermConstantUnion* tempConstantNode = node->getAsConstantUnion(); - - if (index >= node->getType().getCols()) { + if (index >= node->getType().getCols()) + { std::stringstream extraInfoStream; extraInfoStream << "matrix field selection out of range '" << index << "'"; std::string extraInfo = extraInfoStream.str(); - error(line, "", "[", extraInfo.c_str()); - recover(); - index = 0; - } - - if (tempConstantNode) { - ConstantUnion* unionArray = tempConstantNode->getUnionArrayPointer(); - int size = tempConstantNode->getType().getCols(); - typedNode = intermediate.addConstantUnion(&unionArray[size*index], tempConstantNode->getType(), line); - } else { - error(line, "Cannot offset into the matrix", "Error"); - recover(); - - return 0; + outOfRangeError(outOfRangeIndexIsError, line, "", "[", extraInfo.c_str()); + index = node->getType().getCols() - 1; } - return typedNode; + const TConstantUnion *unionArray = node->getUnionArrayPointer(); + int size = node->getType().getCols(); + return intermediate.addConstantUnion(&unionArray[size * index], node->getType(), line); } - // -// This function returns an element of an array accessed from a constant array. The values are retrieved from -// the symbol table and parse-tree is built for the type of the element. The input -// to the function could either be a symbol node (a[0] where a is a constant array)that represents a -// constant array or it could be the tree representation of the constant array (s.a1[0] where s is a constant structure) +// This function returns an element of an array accessed from a constant array. The values are +// retrieved from the symbol table and parse-tree is built for the type of the element. The input +// to the function could either be a symbol node (a[0] where a is a constant array)that represents a +// constant array or it could be the tree representation of the constant array (s.a1[0] where s is a +// constant structure) // -TIntermTyped* TParseContext::addConstArrayNode(int index, TIntermTyped* node, const TSourceLoc& line) +TIntermTyped *TParseContext::addConstArrayNode(int index, + TIntermConstantUnion *node, + const TSourceLoc &line, + bool outOfRangeIndexIsError) { - TIntermTyped* typedNode; - TIntermConstantUnion* tempConstantNode = node->getAsConstantUnion(); TType arrayElementType = node->getType(); arrayElementType.clearArrayness(); - if (index >= node->getType().getArraySize()) { + if (index >= node->getType().getArraySize()) + { std::stringstream extraInfoStream; extraInfoStream << "array field selection out of range '" << index << "'"; std::string extraInfo = extraInfoStream.str(); - error(line, "", "[", extraInfo.c_str()); - recover(); - index = 0; - } - - if (tempConstantNode) { - size_t arrayElementSize = arrayElementType.getObjectSize(); - ConstantUnion* unionArray = tempConstantNode->getUnionArrayPointer(); - typedNode = intermediate.addConstantUnion(&unionArray[arrayElementSize * index], tempConstantNode->getType(), line); - } else { - error(line, "Cannot offset into the array", "Error"); - recover(); - - return 0; + outOfRangeError(outOfRangeIndexIsError, line, "", "[", extraInfo.c_str()); + index = node->getType().getArraySize() - 1; } - - return typedNode; + size_t arrayElementSize = arrayElementType.getObjectSize(); + const TConstantUnion *unionArray = node->getUnionArrayPointer(); + return intermediate.addConstantUnion(&unionArray[arrayElementSize * index], node->getType(), + line); } - // -// This function returns the value of a particular field inside a constant structure from the symbol table. -// If there is an embedded/nested struct, it appropriately calls addConstStructNested or addConstStructFromAggr -// function and returns the parse-tree with the values of the embedded/nested struct. +// This function returns the value of a particular field inside a constant structure from the symbol +// table. +// If there is an embedded/nested struct, it appropriately calls addConstStructNested or +// addConstStructFromAggr function and returns the parse-tree with the values of the embedded/nested +// struct. // -TIntermTyped* TParseContext::addConstStruct(const TString &identifier, TIntermTyped *node, const TSourceLoc& line) +TIntermTyped *TParseContext::addConstStruct(const TString &identifier, + TIntermTyped *node, + const TSourceLoc &line) { - const TFieldList& fields = node->getType().getStruct()->fields(); - size_t instanceSize = 0; + const TFieldList &fields = node->getType().getStruct()->fields(); + size_t instanceSize = 0; - for (size_t index = 0; index < fields.size(); ++index) { - if (fields[index]->name() == identifier) { + for (size_t index = 0; index < fields.size(); ++index) + { + if (fields[index]->name() == identifier) + { break; - } else { + } + else + { instanceSize += fields[index]->type()->getObjectSize(); } } TIntermTyped *typedNode; TIntermConstantUnion *tempConstantNode = node->getAsConstantUnion(); - if (tempConstantNode) { - ConstantUnion* constArray = tempConstantNode->getUnionArrayPointer(); + if (tempConstantNode) + { + const TConstantUnion *constArray = tempConstantNode->getUnionArrayPointer(); - typedNode = intermediate.addConstantUnion(constArray+instanceSize, tempConstantNode->getType(), line); // type will be changed in the calling function - } else { + // type will be changed in the calling function + typedNode = intermediate.addConstantUnion(constArray + instanceSize, + tempConstantNode->getType(), line); + } + else + { error(line, "Cannot offset into the structure", "Error"); recover(); @@ -1863,15 +2543,22 @@ TIntermTyped* TParseContext::addConstStruct(const TString &identifier, TIntermTy // // Interface/uniform blocks // -TIntermAggregate* TParseContext::addInterfaceBlock(const TPublicType& typeQualifier, const TSourceLoc& nameLine, const TString& blockName, TFieldList* fieldList, - const TString* instanceName, const TSourceLoc& instanceLine, TIntermTyped* arrayIndex, const TSourceLoc& arrayIndexLine) +TIntermAggregate *TParseContext::addInterfaceBlock(const TPublicType &typeQualifier, + const TSourceLoc &nameLine, + const TString &blockName, + TFieldList *fieldList, + const TString *instanceName, + const TSourceLoc &instanceLine, + TIntermTyped *arrayIndex, + const TSourceLoc &arrayIndexLine) { if (reservedErrorCheck(nameLine, blockName)) recover(); if (typeQualifier.qualifier != EvqUniform) { - error(typeQualifier.line, "invalid qualifier:", getQualifierString(typeQualifier.qualifier), "interface blocks must be uniform"); + error(typeQualifier.line, "invalid qualifier:", getQualifierString(typeQualifier.qualifier), + "interface blocks must be uniform"); recover(); } @@ -1883,39 +2570,44 @@ TIntermAggregate* TParseContext::addInterfaceBlock(const TPublicType& typeQualif if (blockLayoutQualifier.matrixPacking == EmpUnspecified) { - blockLayoutQualifier.matrixPacking = defaultMatrixPacking; + blockLayoutQualifier.matrixPacking = mDefaultMatrixPacking; } if (blockLayoutQualifier.blockStorage == EbsUnspecified) { - blockLayoutQualifier.blockStorage = defaultBlockStorage; + blockLayoutQualifier.blockStorage = mDefaultBlockStorage; } - TSymbol* blockNameSymbol = new TInterfaceBlockName(&blockName); - if (!symbolTable.declare(blockNameSymbol)) { + TSymbol *blockNameSymbol = new TInterfaceBlockName(&blockName); + if (!symbolTable.declare(blockNameSymbol)) + { error(nameLine, "redefinition", blockName.c_str(), "interface block name"); recover(); } // check for sampler types and apply layout qualifiers - for (size_t memberIndex = 0; memberIndex < fieldList->size(); ++memberIndex) { - TField* field = (*fieldList)[memberIndex]; - TType* fieldType = field->type(); - if (IsSampler(fieldType->getBasicType())) { - error(field->line(), "unsupported type", fieldType->getBasicString(), "sampler types are not allowed in interface blocks"); + for (size_t memberIndex = 0; memberIndex < fieldList->size(); ++memberIndex) + { + TField *field = (*fieldList)[memberIndex]; + TType *fieldType = field->type(); + if (IsSampler(fieldType->getBasicType())) + { + error(field->line(), "unsupported type", fieldType->getBasicString(), + "sampler types are not allowed in interface blocks"); recover(); } const TQualifier qualifier = fieldType->getQualifier(); switch (qualifier) { - case EvqGlobal: - case EvqUniform: - break; - default: - error(field->line(), "invalid qualifier on interface block member", getQualifierString(qualifier)); - recover(); - break; + case EvqGlobal: + case EvqUniform: + break; + default: + error(field->line(), "invalid qualifier on interface block member", + getQualifierString(qualifier)); + recover(); + break; } // check layout qualifiers @@ -1927,7 +2619,8 @@ TIntermAggregate* TParseContext::addInterfaceBlock(const TPublicType& typeQualif if (fieldLayoutQualifier.blockStorage != EbsUnspecified) { - error(field->line(), "invalid layout qualifier:", getBlockStorageString(fieldLayoutQualifier.blockStorage), "cannot be used here"); + error(field->line(), "invalid layout qualifier:", + getBlockStorageString(fieldLayoutQualifier.blockStorage), "cannot be used here"); recover(); } @@ -1935,10 +2628,11 @@ TIntermAggregate* TParseContext::addInterfaceBlock(const TPublicType& typeQualif { fieldLayoutQualifier.matrixPacking = blockLayoutQualifier.matrixPacking; } - else if (!fieldType->isMatrix()) + else if (!fieldType->isMatrix() && fieldType->getBasicType() != EbtStruct) { - error(field->line(), "invalid layout qualifier:", getMatrixPackingString(fieldLayoutQualifier.matrixPacking), "can only be used on matrix types"); - recover(); + warning(field->line(), "extraneous layout qualifier:", + getMatrixPackingString(fieldLayoutQualifier.matrixPacking), + "only has an effect on matrix types"); } fieldType->setLayoutQualifier(fieldLayoutQualifier); @@ -1952,62 +2646,74 @@ TIntermAggregate* TParseContext::addInterfaceBlock(const TPublicType& typeQualif recover(); } - TInterfaceBlock* interfaceBlock = new TInterfaceBlock(&blockName, fieldList, instanceName, arraySize, blockLayoutQualifier); - TType interfaceBlockType(interfaceBlock, typeQualifier.qualifier, blockLayoutQualifier, arraySize); + TInterfaceBlock *interfaceBlock = + new TInterfaceBlock(&blockName, fieldList, instanceName, arraySize, blockLayoutQualifier); + TType interfaceBlockType(interfaceBlock, typeQualifier.qualifier, blockLayoutQualifier, + arraySize); TString symbolName = ""; - int symbolId = 0; + int symbolId = 0; if (!instanceName) { // define symbols for the members of the interface block for (size_t memberIndex = 0; memberIndex < fieldList->size(); ++memberIndex) { - TField* field = (*fieldList)[memberIndex]; - TType* fieldType = field->type(); + TField *field = (*fieldList)[memberIndex]; + TType *fieldType = field->type(); // set parent pointer of the field variable fieldType->setInterfaceBlock(interfaceBlock); - TVariable* fieldVariable = new TVariable(&field->name(), *fieldType); + TVariable *fieldVariable = new TVariable(&field->name(), *fieldType); fieldVariable->setQualifier(typeQualifier.qualifier); - if (!symbolTable.declare(fieldVariable)) { - error(field->line(), "redefinition", field->name().c_str(), "interface block member name"); + if (!symbolTable.declare(fieldVariable)) + { + error(field->line(), "redefinition", field->name().c_str(), + "interface block member name"); recover(); } } } else { + if (reservedErrorCheck(instanceLine, *instanceName)) + recover(); + // add a symbol for this interface block - TVariable* instanceTypeDef = new TVariable(instanceName, interfaceBlockType, false); + TVariable *instanceTypeDef = new TVariable(instanceName, interfaceBlockType, false); instanceTypeDef->setQualifier(typeQualifier.qualifier); - if (!symbolTable.declare(instanceTypeDef)) { - error(instanceLine, "redefinition", instanceName->c_str(), "interface block instance name"); + if (!symbolTable.declare(instanceTypeDef)) + { + error(instanceLine, "redefinition", instanceName->c_str(), + "interface block instance name"); recover(); } - symbolId = instanceTypeDef->getUniqueId(); + symbolId = instanceTypeDef->getUniqueId(); symbolName = instanceTypeDef->getName(); } - TIntermAggregate *aggregate = intermediate.makeAggregate(intermediate.addSymbol(symbolId, symbolName, interfaceBlockType, typeQualifier.line), nameLine); + TIntermAggregate *aggregate = intermediate.makeAggregate( + intermediate.addSymbol(symbolId, symbolName, interfaceBlockType, typeQualifier.line), + nameLine); aggregate->setOp(EOpDeclaration); exitStructDeclaration(); return aggregate; } -bool TParseContext::enterStructDeclaration(const TSourceLoc& line, const TString& identifier) +bool TParseContext::enterStructDeclaration(const TSourceLoc &line, const TString &identifier) { - ++structNestingLevel; + ++mStructNestingLevel; // Embedded structure definitions are not supported per GLSL ES spec. // They aren't allowed in GLSL either, but we need to detect this here // so we don't rely on the GLSL compiler to catch it. - if (structNestingLevel > 1) { + if (mStructNestingLevel > 1) + { error(line, "", "Embedded struct definitions are not allowed"); return true; } @@ -2017,33 +2723,34 @@ bool TParseContext::enterStructDeclaration(const TSourceLoc& line, const TString void TParseContext::exitStructDeclaration() { - --structNestingLevel; + --mStructNestingLevel; } -namespace { - +namespace +{ const int kWebGLMaxStructNesting = 4; } // namespace -bool TParseContext::structNestingErrorCheck(const TSourceLoc& line, const TField& field) +bool TParseContext::structNestingErrorCheck(const TSourceLoc &line, const TField &field) { - if (!IsWebGLBasedSpec(shaderSpec)) { + if (!IsWebGLBasedSpec(mShaderSpec)) + { return false; } - if (field.type()->getBasicType() != EbtStruct) { + if (field.type()->getBasicType() != EbtStruct) + { return false; } // We're already inside a structure definition at this point, so add // one to the field's struct nesting. - if (1 + field.type()->getDeepestStructNesting() > kWebGLMaxStructNesting) { + if (1 + field.type()->getDeepestStructNesting() > kWebGLMaxStructNesting) + { std::stringstream reasonStream; - reasonStream << "Reference of struct type " - << field.type()->getStruct()->name().c_str() - << " exceeds maximum allowed nesting level of " - << kWebGLMaxStructNesting; + reasonStream << "Reference of struct type " << field.type()->getStruct()->name().c_str() + << " exceeds maximum allowed nesting level of " << kWebGLMaxStructNesting; std::string reason = reasonStream.str(); error(line, reason.c_str(), field.name().c_str(), ""); return true; @@ -2055,7 +2762,9 @@ bool TParseContext::structNestingErrorCheck(const TSourceLoc& line, const TField // // Parse an array index expression // -TIntermTyped* TParseContext::addIndexExpression(TIntermTyped *baseExpression, const TSourceLoc& location, TIntermTyped *indexExpression) +TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, + const TSourceLoc &location, + TIntermTyped *indexExpression) { TIntermTyped *indexedExpression = NULL; @@ -2063,7 +2772,8 @@ TIntermTyped* TParseContext::addIndexExpression(TIntermTyped *baseExpression, co { if (baseExpression->getAsSymbolNode()) { - error(location, " left of '[' is not of type array, matrix, or vector ", baseExpression->getAsSymbolNode()->getSymbol().c_str()); + error(location, " left of '[' is not of type array, matrix, or vector ", + baseExpression->getAsSymbolNode()->getSymbol().c_str()); } else { @@ -2074,137 +2784,188 @@ TIntermTyped* TParseContext::addIndexExpression(TIntermTyped *baseExpression, co TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion(); - if (indexExpression->getQualifier() == EvqConst && indexConstantUnion) + // TODO(oetuaho@nvidia.com): Get rid of indexConstantUnion == nullptr below once ANGLE is able + // to constant fold all constant expressions. Right now we don't allow indexing interface blocks + // or fragment outputs with expressions that ANGLE is not able to constant fold, even if the + // index is a constant expression. + if (indexExpression->getQualifier() != EvqConst || indexConstantUnion == nullptr) + { + if (baseExpression->isInterfaceBlock()) + { + error( + location, "", "[", + "array indexes for interface blocks arrays must be constant integral expressions"); + recover(); + } + else if (baseExpression->getQualifier() == EvqFragmentOut) + { + error(location, "", "[", + "array indexes for fragment outputs must be constant integral expressions"); + recover(); + } + else if (mShaderSpec == SH_WEBGL2_SPEC && baseExpression->getQualifier() == EvqFragData) + { + error(location, "", "[", "array index for gl_FragData must be constant zero"); + recover(); + } + } + + if (indexConstantUnion) { + // If the index is not qualified as constant, the behavior in the spec is undefined. This + // applies even if ANGLE has been able to constant fold it (ANGLE may constant fold + // expressions that are not constant expressions). The most compatible way to handle this + // case is to report a warning instead of an error and force the index to be in the + // correct range. + bool outOfRangeIndexIsError = indexExpression->getQualifier() == EvqConst; int index = indexConstantUnion->getIConst(0); if (index < 0) { std::stringstream infoStream; infoStream << index; std::string info = infoStream.str(); - error(location, "negative index", info.c_str()); - recover(); + outOfRangeError(outOfRangeIndexIsError, location, "negative index", info.c_str()); index = 0; } - if (baseExpression->getType().getQualifier() == EvqConst) + TIntermConstantUnion *baseConstantUnion = baseExpression->getAsConstantUnion(); + if (baseConstantUnion) { if (baseExpression->isArray()) { - // constant folding for arrays - indexedExpression = addConstArrayNode(index, baseExpression, location); + // constant folding for array indexing + indexedExpression = + addConstArrayNode(index, baseConstantUnion, location, outOfRangeIndexIsError); } else if (baseExpression->isVector()) { - // constant folding for vectors + // constant folding for vector indexing TVectorFields fields; fields.num = 1; - fields.offsets[0] = index; // need to do it this way because v.xy sends fields integer array - indexedExpression = addConstVectorNode(fields, baseExpression, location); + fields.offsets[0] = + index; // need to do it this way because v.xy sends fields integer array + indexedExpression = + addConstVectorNode(fields, baseConstantUnion, location, outOfRangeIndexIsError); } else if (baseExpression->isMatrix()) { - // constant folding for matrices - indexedExpression = addConstMatrixNode(index, baseExpression, location); + // constant folding for matrix indexing + indexedExpression = + addConstMatrixNode(index, baseConstantUnion, location, outOfRangeIndexIsError); } } else { + int safeIndex = -1; + if (baseExpression->isArray()) { - if (index >= baseExpression->getType().getArraySize()) + if (baseExpression->getQualifier() == EvqFragData && index > 0) + { + if (mShaderSpec == SH_WEBGL2_SPEC) + { + // Error has been already generated if index is not const. + if (indexExpression->getQualifier() == EvqConst) + { + error(location, "", "[", + "array index for gl_FragData must be constant zero"); + recover(); + } + safeIndex = 0; + } + else if (!isExtensionEnabled("GL_EXT_draw_buffers")) + { + outOfRangeError(outOfRangeIndexIsError, location, "", "[", + "array index for gl_FragData must be zero when " + "GL_EXT_draw_buffers is disabled"); + safeIndex = 0; + } + } + // Only do generic out-of-range check if similar error hasn't already been reported. + if (safeIndex < 0 && index >= baseExpression->getType().getArraySize()) { std::stringstream extraInfoStream; extraInfoStream << "array index out of range '" << index << "'"; std::string extraInfo = extraInfoStream.str(); - error(location, "", "[", extraInfo.c_str()); - recover(); - index = baseExpression->getType().getArraySize() - 1; - } - else if (baseExpression->getQualifier() == EvqFragData && index > 0 && !isExtensionEnabled("GL_EXT_draw_buffers")) - { - error(location, "", "[", "array indexes for gl_FragData must be zero when GL_EXT_draw_buffers is disabled"); - recover(); - index = 0; + outOfRangeError(outOfRangeIndexIsError, location, "", "[", extraInfo.c_str()); + safeIndex = baseExpression->getType().getArraySize() - 1; } } - else if ((baseExpression->isVector() || baseExpression->isMatrix()) && baseExpression->getType().getNominalSize() <= index) + else if ((baseExpression->isVector() || baseExpression->isMatrix()) && + baseExpression->getType().getNominalSize() <= index) { std::stringstream extraInfoStream; extraInfoStream << "field selection out of range '" << index << "'"; std::string extraInfo = extraInfoStream.str(); - error(location, "", "[", extraInfo.c_str()); - recover(); - index = baseExpression->getType().getNominalSize() - 1; + outOfRangeError(outOfRangeIndexIsError, location, "", "[", extraInfo.c_str()); + safeIndex = baseExpression->getType().getNominalSize() - 1; + } + + // Data of constant unions can't be changed, because it may be shared with other + // constant unions or even builtins, like gl_MaxDrawBuffers. Instead use a new + // sanitized object. + if (safeIndex != -1) + { + TConstantUnion *safeConstantUnion = new TConstantUnion(); + safeConstantUnion->setIConst(safeIndex); + indexConstantUnion->replaceConstantUnion(safeConstantUnion); } - indexConstantUnion->getUnionArrayPointer()->setIConst(index); - indexedExpression = intermediate.addIndex(EOpIndexDirect, baseExpression, indexExpression, location); + indexedExpression = + intermediate.addIndex(EOpIndexDirect, baseExpression, indexExpression, location); } } else { - if (baseExpression->isInterfaceBlock()) - { - error(location, "", "[", "array indexes for interface blocks arrays must be constant integral expressions"); - recover(); - } - else if (baseExpression->getQualifier() == EvqFragmentOut) - { - error(location, "", "[", "array indexes for fragment outputs must be constant integral expressions"); - recover(); - } - - indexedExpression = intermediate.addIndex(EOpIndexIndirect, baseExpression, indexExpression, location); + indexedExpression = + intermediate.addIndex(EOpIndexIndirect, baseExpression, indexExpression, location); } if (indexedExpression == 0) { - ConstantUnion *unionArray = new ConstantUnion[1]; + TConstantUnion *unionArray = new TConstantUnion[1]; unionArray->setFConst(0.0f); - indexedExpression = intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpHigh, EvqConst), location); + indexedExpression = + intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpHigh, EvqConst), location); } else if (baseExpression->isArray()) { - const TType &baseType = baseExpression->getType(); - if (baseType.getStruct()) - { - TType copyOfType(baseType.getStruct()); - indexedExpression->setType(copyOfType); - } - else if (baseType.isInterfaceBlock()) - { - TType copyOfType(baseType.getInterfaceBlock(), baseType.getQualifier(), baseType.getLayoutQualifier(), 0); - indexedExpression->setType(copyOfType); - } - else - { - indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(), EvqTemporary, baseExpression->getNominalSize(), baseExpression->getSecondarySize())); - } - - if (baseExpression->getType().getQualifier() == EvqConst) - { - indexedExpression->getTypePointer()->setQualifier(EvqConst); - } + TType indexedType = baseExpression->getType(); + indexedType.clearArrayness(); + indexedExpression->setType(indexedType); } else if (baseExpression->isMatrix()) { - TQualifier qualifier = baseExpression->getType().getQualifier() == EvqConst ? EvqConst : EvqTemporary; - indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(), qualifier, baseExpression->getRows())); + indexedExpression->setType(TType(baseExpression->getBasicType(), + baseExpression->getPrecision(), EvqTemporary, + static_cast(baseExpression->getRows()))); } else if (baseExpression->isVector()) { - TQualifier qualifier = baseExpression->getType().getQualifier() == EvqConst ? EvqConst : EvqTemporary; - indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(), qualifier)); + indexedExpression->setType( + TType(baseExpression->getBasicType(), baseExpression->getPrecision(), EvqTemporary)); } else { indexedExpression->setType(baseExpression->getType()); } + if (baseExpression->getType().getQualifier() == EvqConst && + indexExpression->getType().getQualifier() == EvqConst) + { + indexedExpression->getTypePointer()->setQualifier(EvqConst); + } + else + { + indexedExpression->getTypePointer()->setQualifier(EvqTemporary); + } + return indexedExpression; } -TIntermTyped* TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpression, const TSourceLoc& dotLocation, const TString &fieldString, const TSourceLoc& fieldLocation) +TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpression, + const TSourceLoc &dotLocation, + const TString &fieldString, + const TSourceLoc &fieldLocation) { TIntermTyped *indexedExpression = NULL; @@ -2217,70 +2978,43 @@ TIntermTyped* TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre if (baseExpression->isVector()) { TVectorFields fields; - if (!parseVectorFields(fieldString, baseExpression->getNominalSize(), fields, fieldLocation)) + if (!parseVectorFields(fieldString, baseExpression->getNominalSize(), fields, + fieldLocation)) { - fields.num = 1; + fields.num = 1; fields.offsets[0] = 0; recover(); } - if (baseExpression->getType().getQualifier() == EvqConst) + if (baseExpression->getAsConstantUnion()) { // constant folding for vector fields - indexedExpression = addConstVectorNode(fields, baseExpression, fieldLocation); - if (indexedExpression == 0) - { - recover(); - indexedExpression = baseExpression; - } - else - { - indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(), EvqConst, (int) (fieldString).size())); - } + indexedExpression = addConstVectorNode(fields, baseExpression->getAsConstantUnion(), + fieldLocation, true); } else { - TString vectorString = fieldString; - TIntermTyped* index = intermediate.addSwizzle(fields, fieldLocation); - indexedExpression = intermediate.addIndex(EOpVectorSwizzle, baseExpression, index, dotLocation); - indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(), EvqTemporary, (int) vectorString.size())); - } - } - else if (baseExpression->isMatrix()) - { - TMatrixFields fields; - if (!parseMatrixFields(fieldString, baseExpression->getCols(), baseExpression->getRows(), fields, fieldLocation)) - { - fields.wholeRow = false; - fields.wholeCol = false; - fields.row = 0; - fields.col = 0; - recover(); + TIntermTyped *index = intermediate.addSwizzle(fields, fieldLocation); + indexedExpression = + intermediate.addIndex(EOpVectorSwizzle, baseExpression, index, dotLocation); } - - if (fields.wholeRow || fields.wholeCol) + if (indexedExpression == nullptr) { - error(dotLocation, " non-scalar fields not implemented yet", "."); recover(); - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setIConst(0); - TIntermTyped* index = intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), fieldLocation); - indexedExpression = intermediate.addIndex(EOpIndexDirect, baseExpression, index, dotLocation); - indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(),EvqTemporary, baseExpression->getCols(), baseExpression->getRows())); + indexedExpression = baseExpression; } else { - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setIConst(fields.col * baseExpression->getRows() + fields.row); - TIntermTyped* index = intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), fieldLocation); - indexedExpression = intermediate.addIndex(EOpIndexDirect, baseExpression, index, dotLocation); - indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision())); + // Note that the qualifier set here will be corrected later. + indexedExpression->setType(TType(baseExpression->getBasicType(), + baseExpression->getPrecision(), EvqTemporary, + (unsigned char)(fieldString).size())); } } else if (baseExpression->getBasicType() == EbtStruct) { - bool fieldFound = false; - const TFieldList& fields = baseExpression->getType().getStruct()->fields(); + bool fieldFound = false; + const TFieldList &fields = baseExpression->getType().getStruct()->fields(); if (fields.empty()) { error(dotLocation, "structure has no fields", "Internal Error"); @@ -2300,7 +3034,7 @@ TIntermTyped* TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre } if (fieldFound) { - if (baseExpression->getType().getQualifier() == EvqConst) + if (baseExpression->getAsConstantUnion()) { indexedExpression = addConstStruct(fieldString, baseExpression, dotLocation); if (indexedExpression == 0) @@ -2311,17 +3045,16 @@ TIntermTyped* TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre else { indexedExpression->setType(*fields[i]->type()); - // change the qualifier of the return type, not of the structure field - // as the structure definition is shared between various structures. - indexedExpression->getTypePointer()->setQualifier(EvqConst); } } else { - ConstantUnion *unionArray = new ConstantUnion[1]; + TConstantUnion *unionArray = new TConstantUnion[1]; unionArray->setIConst(i); - TIntermTyped* index = intermediate.addConstantUnion(unionArray, *fields[i]->type(), fieldLocation); - indexedExpression = intermediate.addIndex(EOpIndexDirectStruct, baseExpression, index, dotLocation); + TIntermTyped *index = intermediate.addConstantUnion( + unionArray, *fields[i]->type(), fieldLocation); + indexedExpression = intermediate.addIndex(EOpIndexDirectStruct, baseExpression, + index, dotLocation); indexedExpression->setType(*fields[i]->type()); } } @@ -2335,8 +3068,8 @@ TIntermTyped* TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre } else if (baseExpression->isInterfaceBlock()) { - bool fieldFound = false; - const TFieldList& fields = baseExpression->getType().getInterfaceBlock()->fields(); + bool fieldFound = false; + const TFieldList &fields = baseExpression->getType().getInterfaceBlock()->fields(); if (fields.empty()) { error(dotLocation, "interface block has no fields", "Internal Error"); @@ -2356,10 +3089,12 @@ TIntermTyped* TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre } if (fieldFound) { - ConstantUnion *unionArray = new ConstantUnion[1]; + TConstantUnion *unionArray = new TConstantUnion[1]; unionArray->setIConst(i); - TIntermTyped* index = intermediate.addConstantUnion(unionArray, *fields[i]->type(), fieldLocation); - indexedExpression = intermediate.addIndex(EOpIndexDirectInterfaceBlock, baseExpression, index, dotLocation); + TIntermTyped *index = + intermediate.addConstantUnion(unionArray, *fields[i]->type(), fieldLocation); + indexedExpression = intermediate.addIndex(EOpIndexDirectInterfaceBlock, + baseExpression, index, dotLocation); indexedExpression->setType(*fields[i]->type()); } else @@ -2372,28 +3107,42 @@ TIntermTyped* TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre } else { - if (shaderVersion < 300) + if (mShaderVersion < 300) { - error(dotLocation, " field selection requires structure, vector, or matrix on left hand side", fieldString.c_str()); + error(dotLocation, " field selection requires structure or vector on left hand side", + fieldString.c_str()); } else { - error(dotLocation, " field selection requires structure, vector, matrix, or interface block on left hand side", fieldString.c_str()); + error(dotLocation, + " field selection requires structure, vector, or interface block on left hand " + "side", + fieldString.c_str()); } recover(); indexedExpression = baseExpression; } + if (baseExpression->getQualifier() == EvqConst) + { + indexedExpression->getTypePointer()->setQualifier(EvqConst); + } + else + { + indexedExpression->getTypePointer()->setQualifier(EvqTemporary); + } + return indexedExpression; } -TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType, const TSourceLoc& qualifierTypeLine) +TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType, + const TSourceLoc &qualifierTypeLine) { TLayoutQualifier qualifier; - qualifier.location = -1; + qualifier.location = -1; qualifier.matrixPacking = EmpUnspecified; - qualifier.blockStorage = EbsUnspecified; + qualifier.blockStorage = EbsUnspecified; if (qualifierType == "shared") { @@ -2417,7 +3166,8 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierTyp } else if (qualifierType == "location") { - error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str(), "location requires an argument"); + error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str(), + "location requires an argument"); recover(); } else @@ -2429,17 +3179,22 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierTyp return qualifier; } -TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType, const TSourceLoc& qualifierTypeLine, const TString &intValueString, int intValue, const TSourceLoc& intValueLine) +TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType, + const TSourceLoc &qualifierTypeLine, + const TString &intValueString, + int intValue, + const TSourceLoc &intValueLine) { TLayoutQualifier qualifier; - qualifier.location = -1; + qualifier.location = -1; qualifier.matrixPacking = EmpUnspecified; - qualifier.blockStorage = EbsUnspecified; + qualifier.blockStorage = EbsUnspecified; if (qualifierType != "location") { - error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str(), "only location may have arguments"); + error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str(), + "only location may have arguments"); recover(); } else @@ -2447,7 +3202,8 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierTyp // must check that location is non-negative if (intValue < 0) { - error(intValueLine, "out of range:", intValueString.c_str(), "location must be non-negative"); + error(intValueLine, "out of range:", intValueString.c_str(), + "location must be non-negative"); recover(); } else @@ -2459,7 +3215,8 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierTyp return qualifier; } -TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualifier, TLayoutQualifier rightQualifier) +TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualifier, + TLayoutQualifier rightQualifier) { TLayoutQualifier joinedQualifier = leftQualifier; @@ -2479,41 +3236,54 @@ TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualif return joinedQualifier; } -TPublicType TParseContext::joinInterpolationQualifiers(const TSourceLoc &interpolationLoc, TQualifier interpolationQualifier, - const TSourceLoc &storageLoc, TQualifier storageQualifier) +TPublicType TParseContext::joinInterpolationQualifiers(const TSourceLoc &interpolationLoc, + TQualifier interpolationQualifier, + const TSourceLoc &storageLoc, + TQualifier storageQualifier) { TQualifier mergedQualifier = EvqSmoothIn; - if (storageQualifier == EvqFragmentIn) { + if (storageQualifier == EvqFragmentIn) + { if (interpolationQualifier == EvqSmooth) mergedQualifier = EvqSmoothIn; else if (interpolationQualifier == EvqFlat) mergedQualifier = EvqFlatIn; - else UNREACHABLE(); + else + UNREACHABLE(); } - else if (storageQualifier == EvqCentroidIn) { + else if (storageQualifier == EvqCentroidIn) + { if (interpolationQualifier == EvqSmooth) mergedQualifier = EvqCentroidIn; else if (interpolationQualifier == EvqFlat) mergedQualifier = EvqFlatIn; - else UNREACHABLE(); + else + UNREACHABLE(); } - else if (storageQualifier == EvqVertexOut) { + else if (storageQualifier == EvqVertexOut) + { if (interpolationQualifier == EvqSmooth) mergedQualifier = EvqSmoothOut; else if (interpolationQualifier == EvqFlat) mergedQualifier = EvqFlatOut; - else UNREACHABLE(); + else + UNREACHABLE(); } - else if (storageQualifier == EvqCentroidOut) { + else if (storageQualifier == EvqCentroidOut) + { if (interpolationQualifier == EvqSmooth) mergedQualifier = EvqCentroidOut; else if (interpolationQualifier == EvqFlat) mergedQualifier = EvqFlatOut; - else UNREACHABLE(); + else + UNREACHABLE(); } - else { - error(interpolationLoc, "interpolation qualifier requires a fragment 'in' or vertex 'out' storage qualifier", getInterpolationString(interpolationQualifier)); + else + { + error(interpolationLoc, + "interpolation qualifier requires a fragment 'in' or vertex 'out' storage qualifier", + getInterpolationString(interpolationQualifier)); recover(); mergedQualifier = storageQualifier; @@ -2524,17 +3294,20 @@ TPublicType TParseContext::joinInterpolationQualifiers(const TSourceLoc &interpo return type; } -TFieldList *TParseContext::addStructDeclaratorList(const TPublicType& typeSpecifier, TFieldList *fieldList) +TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecifier, + TFieldList *fieldList) { - if (voidErrorCheck(typeSpecifier.line, (*fieldList)[0]->name(), typeSpecifier)) { + if (voidErrorCheck(typeSpecifier.line, (*fieldList)[0]->name(), typeSpecifier.type)) + { recover(); } - for (unsigned int i = 0; i < fieldList->size(); ++i) { + for (unsigned int i = 0; i < fieldList->size(); ++i) + { // // Careful not to replace already known aspects of type, like array-ness // - TType* type = (*fieldList)[i]->type(); + TType *type = (*fieldList)[i]->type(); type->setBasicType(typeSpecifier.type); type->setPrimarySize(typeSpecifier.primarySize); type->setSecondarySize(typeSpecifier.secondarySize); @@ -2543,17 +3316,20 @@ TFieldList *TParseContext::addStructDeclaratorList(const TPublicType& typeSpecif type->setLayoutQualifier(typeSpecifier.layoutQualifier); // don't allow arrays of arrays - if (type->isArray()) { + if (type->isArray()) + { if (arrayTypeErrorCheck(typeSpecifier.line, typeSpecifier)) recover(); } if (typeSpecifier.array) type->setArraySize(typeSpecifier.arraySize); - if (typeSpecifier.userDef) { + if (typeSpecifier.userDef) + { type->setStruct(typeSpecifier.userDef->getStruct()); } - if (structNestingErrorCheck(typeSpecifier.line, *(*fieldList)[i])) { + if (structNestingErrorCheck(typeSpecifier.line, *(*fieldList)[i])) + { recover(); } } @@ -2561,10 +3337,13 @@ TFieldList *TParseContext::addStructDeclaratorList(const TPublicType& typeSpecif return fieldList; } -TPublicType TParseContext::addStructure(const TSourceLoc& structLine, const TSourceLoc& nameLine, const TString *structName, TFieldList* fieldList) +TPublicType TParseContext::addStructure(const TSourceLoc &structLine, + const TSourceLoc &nameLine, + const TString *structName, + TFieldList *fieldList) { - TStructure* structure = new TStructure(structName, fieldList); - TType* structureType = new TType(structure); + TStructure *structure = new TStructure(structName, fieldList); + TType *structureType = new TType(structure); // Store a bool in the struct if we're at global scope, to allow us to // skip the local struct scoping workaround in HLSL. @@ -2577,8 +3356,9 @@ TPublicType TParseContext::addStructure(const TSourceLoc& structLine, const TSou { recover(); } - TVariable* userTypeDef = new TVariable(structName, *structureType, true); - if (!symbolTable.declare(userTypeDef)) { + TVariable *userTypeDef = new TVariable(structName, *structureType, true); + if (!symbolTable.declare(userTypeDef)) + { error(nameLine, "redefinition", structName->c_str(), "struct"); recover(); } @@ -2587,37 +3367,40 @@ TPublicType TParseContext::addStructure(const TSourceLoc& structLine, const TSou // ensure we do not specify any storage qualifiers on the struct members for (unsigned int typeListIndex = 0; typeListIndex < fieldList->size(); typeListIndex++) { - const TField &field = *(*fieldList)[typeListIndex]; + const TField &field = *(*fieldList)[typeListIndex]; const TQualifier qualifier = field.type()->getQualifier(); switch (qualifier) { - case EvqGlobal: - case EvqTemporary: - break; - default: - error(field.line(), "invalid qualifier on struct member", getQualifierString(qualifier)); - recover(); - break; + case EvqGlobal: + case EvqTemporary: + break; + default: + error(field.line(), "invalid qualifier on struct member", + getQualifierString(qualifier)); + recover(); + break; } } TPublicType publicType; publicType.setBasic(EbtStruct, EvqTemporary, structLine); publicType.userDef = structureType; + publicType.isStructSpecifier = true; exitStructDeclaration(); return publicType; } -TIntermSwitch *TParseContext::addSwitch(TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &loc) +TIntermSwitch *TParseContext::addSwitch(TIntermTyped *init, + TIntermAggregate *statementList, + const TSourceLoc &loc) { TBasicType switchType = init->getBasicType(); - if ((switchType != EbtInt && switchType != EbtUInt) || - init->isMatrix() || - init->isArray() || + if ((switchType != EbtInt && switchType != EbtUInt) || init->isMatrix() || init->isArray() || init->isVector()) { - error(init->getLine(), "init-expression in a switch statement must be a scalar integer", "switch"); + error(init->getLine(), "init-expression in a switch statement must be a scalar integer", + "switch"); recover(); return nullptr; } @@ -2656,15 +3439,16 @@ TIntermCase *TParseContext::addCase(TIntermTyped *condition, const TSourceLoc &l return nullptr; } if ((condition->getBasicType() != EbtInt && condition->getBasicType() != EbtUInt) || - condition->isMatrix() || - condition->isArray() || - condition->isVector()) + condition->isMatrix() || condition->isArray() || condition->isVector()) { error(condition->getLine(), "case label must be a scalar integer", "case"); recover(); } TIntermConstantUnion *conditionConst = condition->getAsConstantUnion(); - if (conditionConst == nullptr) + // TODO(oetuaho@nvidia.com): Get rid of the conditionConst == nullptr check once all constant + // expressions can be folded. Right now we don't allow constant expressions that ANGLE can't + // fold in case labels. + if (condition->getQualifier() != EvqConst || conditionConst == nullptr) { error(condition->getLine(), "case label must be constant", "case"); recover(); @@ -2697,8 +3481,10 @@ TIntermCase *TParseContext::addDefault(const TSourceLoc &loc) return node; } -TIntermTyped *TParseContext::createUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc, - const TType *funcReturnType) +TIntermTyped *TParseContext::createUnaryMath(TOperator op, + TIntermTyped *child, + const TSourceLoc &loc, + const TType *funcReturnType) { if (child == nullptr) { @@ -2707,38 +3493,34 @@ TIntermTyped *TParseContext::createUnaryMath(TOperator op, TIntermTyped *child, switch (op) { - case EOpLogicalNot: - if (child->getBasicType() != EbtBool || - child->isMatrix() || - child->isArray() || - child->isVector()) - { - return nullptr; - } - break; - case EOpBitwiseNot: - if ((child->getBasicType() != EbtInt && child->getBasicType() != EbtUInt) || - child->isMatrix() || - child->isArray()) - { - return nullptr; - } - break; - case EOpPostIncrement: - case EOpPreIncrement: - case EOpPostDecrement: - case EOpPreDecrement: - case EOpNegative: - case EOpPositive: - if (child->getBasicType() == EbtStruct || - child->getBasicType() == EbtBool || - child->isArray()) - { - return nullptr; - } - // Operators for built-ins are already type checked against their prototype. - default: - break; + case EOpLogicalNot: + if (child->getBasicType() != EbtBool || child->isMatrix() || child->isArray() || + child->isVector()) + { + return nullptr; + } + break; + case EOpBitwiseNot: + if ((child->getBasicType() != EbtInt && child->getBasicType() != EbtUInt) || + child->isMatrix() || child->isArray()) + { + return nullptr; + } + break; + case EOpPostIncrement: + case EOpPreIncrement: + case EOpPostDecrement: + case EOpPreDecrement: + case EOpNegative: + case EOpPositive: + if (child->getBasicType() == EbtStruct || child->getBasicType() == EbtBool || + child->isArray()) + { + return nullptr; + } + // Operators for built-ins are already type checked against their prototype. + default: + break; } return intermediate.addUnaryMath(op, child, loc, funcReturnType); @@ -2756,19 +3538,23 @@ TIntermTyped *TParseContext::addUnaryMath(TOperator op, TIntermTyped *child, con return node; } -TIntermTyped *TParseContext::addUnaryMathLValue(TOperator op, TIntermTyped *child, const TSourceLoc &loc) +TIntermTyped *TParseContext::addUnaryMathLValue(TOperator op, + TIntermTyped *child, + const TSourceLoc &loc) { if (lValueErrorCheck(loc, GetOperatorString(op), child)) recover(); return addUnaryMath(op, child, loc); } -bool TParseContext::binaryOpCommonCheck(TOperator op, TIntermTyped *left, TIntermTyped *right, - const TSourceLoc &loc) +bool TParseContext::binaryOpCommonCheck(TOperator op, + TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &loc) { if (left->isArray() || right->isArray()) { - if (shaderVersion < 300) + if (mShaderVersion < 300) { error(loc, "Invalid operation for arrays", GetOperatorString(op)); return false; @@ -2782,15 +3568,16 @@ bool TParseContext::binaryOpCommonCheck(TOperator op, TIntermTyped *left, TInter switch (op) { - case EOpEqual: - case EOpNotEqual: - case EOpAssign: - case EOpInitialize: - break; - default: - error(loc, "Invalid operation for arrays", GetOperatorString(op)); - return false; + case EOpEqual: + case EOpNotEqual: + case EOpAssign: + case EOpInitialize: + break; + default: + error(loc, "Invalid operation for arrays", GetOperatorString(op)); + return false; } + // At this point, size of implicitly sized arrays should be resolved. if (left->getArraySize() != right->getArraySize()) { error(loc, "array size mismatch", GetOperatorString(op)); @@ -2802,33 +3589,33 @@ bool TParseContext::binaryOpCommonCheck(TOperator op, TIntermTyped *left, TInter bool isBitShift = false; switch (op) { - case EOpBitShiftLeft: - case EOpBitShiftRight: - case EOpBitShiftLeftAssign: - case EOpBitShiftRightAssign: - // Unsigned can be bit-shifted by signed and vice versa, but we need to - // check that the basic type is an integer type. - isBitShift = true; - if (!IsInteger(left->getBasicType()) || !IsInteger(right->getBasicType())) - { - return false; - } - break; - case EOpBitwiseAnd: - case EOpBitwiseXor: - case EOpBitwiseOr: - case EOpBitwiseAndAssign: - case EOpBitwiseXorAssign: - case EOpBitwiseOrAssign: - // It is enough to check the type of only one operand, since later it - // is checked that the operand types match. - if (!IsInteger(left->getBasicType())) - { - return false; - } - break; - default: - break; + case EOpBitShiftLeft: + case EOpBitShiftRight: + case EOpBitShiftLeftAssign: + case EOpBitShiftRightAssign: + // Unsigned can be bit-shifted by signed and vice versa, but we need to + // check that the basic type is an integer type. + isBitShift = true; + if (!IsInteger(left->getBasicType()) || !IsInteger(right->getBasicType())) + { + return false; + } + break; + case EOpBitwiseAnd: + case EOpBitwiseXor: + case EOpBitwiseOr: + case EOpBitwiseAndAssign: + case EOpBitwiseXorAssign: + case EOpBitwiseOrAssign: + // It is enough to check the type of only one operand, since later it + // is checked that the operand types match. + if (!IsInteger(left->getBasicType())) + { + return false; + } + break; + default: + break; } // GLSL ES 1.00 and 3.00 do not support implicit type casting. @@ -2840,132 +3627,144 @@ bool TParseContext::binaryOpCommonCheck(TOperator op, TIntermTyped *left, TInter // Check that type sizes match exactly on ops that require that. // Also check restrictions for structs that contain arrays or samplers. - switch(op) + switch (op) { - case EOpAssign: - case EOpInitialize: - case EOpEqual: - case EOpNotEqual: - // ESSL 1.00 sections 5.7, 5.8, 5.9 - if (shaderVersion < 300 && left->getType().isStructureContainingArrays()) - { - error(loc, "undefined operation for structs containing arrays", GetOperatorString(op)); - return false; - } - // Samplers as l-values are disallowed also in ESSL 3.00, see section 4.1.7, - // we interpret the spec so that this extends to structs containing samplers, - // similarly to ESSL 1.00 spec. - if ((shaderVersion < 300 || op == EOpAssign || op == EOpInitialize) && - left->getType().isStructureContainingSamplers()) - { - error(loc, "undefined operation for structs containing samplers", GetOperatorString(op)); - return false; - } - case EOpLessThan: - case EOpGreaterThan: - case EOpLessThanEqual: - case EOpGreaterThanEqual: - if ((left->getNominalSize() != right->getNominalSize()) || - (left->getSecondarySize() != right->getSecondarySize())) - { - return false; - } - default: - break; + case EOpAssign: + case EOpInitialize: + case EOpEqual: + case EOpNotEqual: + // ESSL 1.00 sections 5.7, 5.8, 5.9 + if (mShaderVersion < 300 && left->getType().isStructureContainingArrays()) + { + error(loc, "undefined operation for structs containing arrays", + GetOperatorString(op)); + return false; + } + // Samplers as l-values are disallowed also in ESSL 3.00, see section 4.1.7, + // we interpret the spec so that this extends to structs containing samplers, + // similarly to ESSL 1.00 spec. + if ((mShaderVersion < 300 || op == EOpAssign || op == EOpInitialize) && + left->getType().isStructureContainingSamplers()) + { + error(loc, "undefined operation for structs containing samplers", + GetOperatorString(op)); + return false; + } + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + if ((left->getNominalSize() != right->getNominalSize()) || + (left->getSecondarySize() != right->getSecondarySize())) + { + return false; + } + default: + break; } return true; } -TIntermTyped *TParseContext::addBinaryMathInternal(TOperator op, TIntermTyped *left, TIntermTyped *right, - const TSourceLoc &loc) +TIntermTyped *TParseContext::addBinaryMathInternal(TOperator op, + TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &loc) { if (!binaryOpCommonCheck(op, left, right, loc)) return nullptr; switch (op) { - case EOpEqual: - case EOpNotEqual: - break; - case EOpLessThan: - case EOpGreaterThan: - case EOpLessThanEqual: - case EOpGreaterThanEqual: - ASSERT(!left->isArray() && !right->isArray()); - if (left->isMatrix() || left->isVector() || - left->getBasicType() == EbtStruct) - { - return nullptr; - } - break; - case EOpLogicalOr: - case EOpLogicalXor: - case EOpLogicalAnd: - ASSERT(!left->isArray() && !right->isArray()); - if (left->getBasicType() != EbtBool || - left->isMatrix() || left->isVector()) - { - return nullptr; - } - break; - case EOpAdd: - case EOpSub: - case EOpDiv: - case EOpMul: - ASSERT(!left->isArray() && !right->isArray()); - if (left->getBasicType() == EbtStruct || left->getBasicType() == EbtBool) - { - return nullptr; - } - break; - case EOpIMod: - ASSERT(!left->isArray() && !right->isArray()); - // Note that this is only for the % operator, not for mod() - if (left->getBasicType() == EbtStruct || left->getBasicType() == EbtBool || left->getBasicType() == EbtFloat) - { - return nullptr; - } - break; - // Note that for bitwise ops, type checking is done in promote() to - // share code between ops and compound assignment - default: - break; + case EOpEqual: + case EOpNotEqual: + break; + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + ASSERT(!left->isArray() && !right->isArray()); + if (left->isMatrix() || left->isVector() || left->getBasicType() == EbtStruct) + { + return nullptr; + } + break; + case EOpLogicalOr: + case EOpLogicalXor: + case EOpLogicalAnd: + ASSERT(!left->isArray() && !right->isArray()); + if (left->getBasicType() != EbtBool || left->isMatrix() || left->isVector()) + { + return nullptr; + } + break; + case EOpAdd: + case EOpSub: + case EOpDiv: + case EOpMul: + ASSERT(!left->isArray() && !right->isArray()); + if (left->getBasicType() == EbtStruct || left->getBasicType() == EbtBool) + { + return nullptr; + } + break; + case EOpIMod: + ASSERT(!left->isArray() && !right->isArray()); + // Note that this is only for the % operator, not for mod() + if (left->getBasicType() == EbtStruct || left->getBasicType() == EbtBool || + left->getBasicType() == EbtFloat) + { + return nullptr; + } + break; + // Note that for bitwise ops, type checking is done in promote() to + // share code between ops and compound assignment + default: + break; } return intermediate.addBinaryMath(op, left, right, loc); } -TIntermTyped *TParseContext::addBinaryMath(TOperator op, TIntermTyped *left, TIntermTyped *right, - const TSourceLoc &loc) +TIntermTyped *TParseContext::addBinaryMath(TOperator op, + TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &loc) { TIntermTyped *node = addBinaryMathInternal(op, left, right, loc); if (node == 0) { - binaryOpError(loc, GetOperatorString(op), left->getCompleteString(), right->getCompleteString()); + binaryOpError(loc, GetOperatorString(op), left->getCompleteString(), + right->getCompleteString()); recover(); return left; } return node; } -TIntermTyped *TParseContext::addBinaryMathBooleanResult(TOperator op, TIntermTyped *left, TIntermTyped *right, - const TSourceLoc &loc) +TIntermTyped *TParseContext::addBinaryMathBooleanResult(TOperator op, + TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &loc) { TIntermTyped *node = addBinaryMathInternal(op, left, right, loc); if (node == 0) { - binaryOpError(loc, GetOperatorString(op), left->getCompleteString(), right->getCompleteString()); + binaryOpError(loc, GetOperatorString(op), left->getCompleteString(), + right->getCompleteString()); recover(); - ConstantUnion *unionArray = new ConstantUnion[1]; + TConstantUnion *unionArray = new TConstantUnion[1]; unionArray->setBConst(false); - return intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), loc); + return intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), + loc); } return node; } -TIntermTyped *TParseContext::createAssign(TOperator op, TIntermTyped *left, TIntermTyped *right, - const TSourceLoc &loc) +TIntermTyped *TParseContext::createAssign(TOperator op, + TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &loc) { if (binaryOpCommonCheck(op, left, right, loc)) { @@ -2974,8 +3773,10 @@ TIntermTyped *TParseContext::createAssign(TOperator op, TIntermTyped *left, TInt return nullptr; } -TIntermTyped *TParseContext::addAssign(TOperator op, TIntermTyped *left, TIntermTyped *right, - const TSourceLoc &loc) +TIntermTyped *TParseContext::addAssign(TOperator op, + TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &loc) { TIntermTyped *node = createAssign(op, left, right, loc); if (node == nullptr) @@ -2987,48 +3788,57 @@ TIntermTyped *TParseContext::addAssign(TOperator op, TIntermTyped *left, TInterm return node; } +TIntermTyped *TParseContext::addComma(TIntermTyped *left, + TIntermTyped *right, + const TSourceLoc &loc) +{ + return intermediate.addComma(left, right, loc, mShaderVersion); +} + TIntermBranch *TParseContext::addBranch(TOperator op, const TSourceLoc &loc) { switch (op) { - case EOpContinue: - if (mLoopNestingLevel <= 0) - { - error(loc, "continue statement only allowed in loops", ""); - recover(); - } - break; - case EOpBreak: - if (mLoopNestingLevel <= 0 && mSwitchNestingLevel <= 0) - { - error(loc, "break statement only allowed in loops and switch statements", ""); - recover(); - } - break; - case EOpReturn: - if (currentFunctionType->getBasicType() != EbtVoid) - { - error(loc, "non-void function must return a value", "return"); - recover(); - } - break; - default: - // No checks for discard - break; + case EOpContinue: + if (mLoopNestingLevel <= 0) + { + error(loc, "continue statement only allowed in loops", ""); + recover(); + } + break; + case EOpBreak: + if (mLoopNestingLevel <= 0 && mSwitchNestingLevel <= 0) + { + error(loc, "break statement only allowed in loops and switch statements", ""); + recover(); + } + break; + case EOpReturn: + if (mCurrentFunctionType->getBasicType() != EbtVoid) + { + error(loc, "non-void function must return a value", "return"); + recover(); + } + break; + default: + // No checks for discard + break; } return intermediate.addBranch(op, loc); } -TIntermBranch *TParseContext::addBranch(TOperator op, TIntermTyped *returnValue, const TSourceLoc &loc) +TIntermBranch *TParseContext::addBranch(TOperator op, + TIntermTyped *returnValue, + const TSourceLoc &loc) { ASSERT(op == EOpReturn); mFunctionReturnsValue = true; - if (currentFunctionType->getBasicType() == EbtVoid) + if (mCurrentFunctionType->getBasicType() == EbtVoid) { error(loc, "void function cannot return a value", "return"); recover(); } - else if (*currentFunctionType != returnValue->getType()) + else if (*mCurrentFunctionType != returnValue->getType()) { error(loc, "function return is not matching type:", "return"); recover(); @@ -3036,14 +3846,113 @@ TIntermBranch *TParseContext::addBranch(TOperator op, TIntermTyped *returnValue, return intermediate.addBranch(op, returnValue, loc); } -TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, TIntermNode *node, - const TSourceLoc &loc, bool *fatalError) +void TParseContext::checkTextureOffsetConst(TIntermAggregate *functionCall) +{ + ASSERT(!functionCall->isUserDefined()); + const TString &name = functionCall->getName(); + TIntermNode *offset = nullptr; + TIntermSequence *arguments = functionCall->getSequence(); + if (name.compare(0, 16, "texelFetchOffset") == 0 || + name.compare(0, 16, "textureLodOffset") == 0 || + name.compare(0, 20, "textureProjLodOffset") == 0 || + name.compare(0, 17, "textureGradOffset") == 0 || + name.compare(0, 21, "textureProjGradOffset") == 0) + { + offset = arguments->back(); + } + else if (name.compare(0, 13, "textureOffset") == 0 || + name.compare(0, 17, "textureProjOffset") == 0) + { + // A bias parameter might follow the offset parameter. + ASSERT(arguments->size() >= 3); + offset = (*arguments)[2]; + } + if (offset != nullptr) + { + TIntermConstantUnion *offsetConstantUnion = offset->getAsConstantUnion(); + if (offset->getAsTyped()->getQualifier() != EvqConst || !offsetConstantUnion) + { + TString unmangledName = TFunction::unmangleName(name); + error(functionCall->getLine(), "Texture offset must be a constant expression", + unmangledName.c_str()); + recover(); + } + else + { + ASSERT(offsetConstantUnion->getBasicType() == EbtInt); + size_t size = offsetConstantUnion->getType().getObjectSize(); + const TConstantUnion *values = offsetConstantUnion->getUnionArrayPointer(); + for (size_t i = 0u; i < size; ++i) + { + int offsetValue = values[i].getIConst(); + if (offsetValue > mMaxProgramTexelOffset || offsetValue < mMinProgramTexelOffset) + { + std::stringstream tokenStream; + tokenStream << offsetValue; + std::string token = tokenStream.str(); + error(offset->getLine(), "Texture offset value out of valid range", + token.c_str()); + recover(); + } + } + } + } +} + +TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, + TIntermNode *paramNode, + TIntermNode *thisNode, + const TSourceLoc &loc, + bool *fatalError) { - *fatalError = false; - TOperator op = fnCall->getBuiltInOp(); + *fatalError = false; + TOperator op = fnCall->getBuiltInOp(); TIntermTyped *callNode = nullptr; - if (op != EOpNull) + if (thisNode != nullptr) + { + TConstantUnion *unionArray = new TConstantUnion[1]; + int arraySize = 0; + TIntermTyped *typedThis = thisNode->getAsTyped(); + if (fnCall->getName() != "length") + { + error(loc, "invalid method", fnCall->getName().c_str()); + recover(); + } + else if (paramNode != nullptr) + { + error(loc, "method takes no parameters", "length"); + recover(); + } + else if (typedThis == nullptr || !typedThis->isArray()) + { + error(loc, "length can only be called on arrays", "length"); + recover(); + } + else + { + arraySize = typedThis->getArraySize(); + if (typedThis->getAsSymbolNode() == nullptr) + { + // This code path can be hit with expressions like these: + // (a = b).length() + // (func()).length() + // (int[3](0, 1, 2)).length() + // ESSL 3.00 section 5.9 defines expressions so that this is not actually a valid + // expression. + // It allows "An array name with the length method applied" in contrast to GLSL 4.4 + // spec section 5.9 which allows "An array, vector or matrix expression with the + // length method applied". + error(loc, "length can only be called on array names, not on array expressions", + "length"); + recover(); + } + } + unionArray->setIConst(arraySize); + callNode = + intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), loc); + } + else if (op != EOpNull) { // // Then this should be a constructor. @@ -3051,12 +3960,12 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, TIntermN // Their parameters will be verified algorithmically. // TType type(EbtVoid, EbpUndefined); // use this to get the type back - if (!constructorErrorCheck(loc, node, *fnCall, op, &type)) + if (!constructorErrorCheck(loc, paramNode, *fnCall, op, &type)) { // // It's a constructor, of type 'type'. // - callNode = addConstructor(node, &type, op, fnCall, loc); + callNode = addConstructor(paramNode, &type, op, fnCall, loc); } if (callNode == nullptr) @@ -3071,9 +3980,9 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, TIntermN // // Not a constructor. Find it in the symbol table. // - const TFunction* fnCandidate; + const TFunction *fnCandidate; bool builtIn; - fnCandidate = findFunction(loc, fnCall, shaderVersion, &builtIn); + fnCandidate = findFunction(loc, fnCall, mShaderVersion, &builtIn); if (fnCandidate) { // @@ -3095,47 +4004,75 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, TIntermN // // Treat it like a built-in unary operator. // - callNode = createUnaryMath(op, node->getAsTyped(), loc, &fnCandidate->getReturnType()); + TIntermAggregate *paramAgg = paramNode->getAsAggregate(); + paramNode = paramAgg->getSequence()->front(); + callNode = createUnaryMath(op, paramNode->getAsTyped(), loc, + &fnCandidate->getReturnType()); if (callNode == nullptr) { std::stringstream extraInfoStream; - extraInfoStream << "built in unary operator function. Type: " - << static_cast(node)->getCompleteString(); + extraInfoStream + << "built in unary operator function. Type: " + << static_cast(paramNode)->getCompleteString(); std::string extraInfo = extraInfoStream.str(); - error(node->getLine(), " wrong operand type", "Internal Error", extraInfo.c_str()); + error(paramNode->getLine(), " wrong operand type", "Internal Error", + extraInfo.c_str()); *fatalError = true; return nullptr; } } else { - TIntermAggregate *aggregate = intermediate.setAggregateOperator(node, op, loc); + TIntermAggregate *aggregate = + intermediate.setAggregateOperator(paramNode, op, loc); aggregate->setType(fnCandidate->getReturnType()); aggregate->setPrecisionFromChildren(); - callNode = aggregate; + if (aggregate->areChildrenConstQualified()) + { + aggregate->getTypePointer()->setQualifier(EvqConst); + } // Some built-in functions have out parameters too. functionCallLValueErrorCheck(fnCandidate, aggregate); + + // See if we can constant fold a built-in. Note that this may be possible even + // if it is not const-qualified. + TIntermTyped *foldedNode = intermediate.foldAggregateBuiltIn(aggregate); + if (foldedNode) + { + callNode = foldedNode; + } + else + { + callNode = aggregate; + } } } else { // This is a real function call - - TIntermAggregate *aggregate = intermediate.setAggregateOperator(node, EOpFunctionCall, loc); + TIntermAggregate *aggregate = + intermediate.setAggregateOperator(paramNode, EOpFunctionCall, loc); 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 + // 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) aggregate->setUserDefined(); aggregate->setName(fnCandidate->getMangledName()); + aggregate->setFunctionId(fnCandidate->getUniqueId()); // This needs to happen after the name is set if (builtIn) + { aggregate->setBuiltInFunctionPrecision(); + checkTextureOffsetConst(aggregate); + } + callNode = aggregate; functionCallLValueErrorCheck(fnCandidate, aggregate); @@ -3145,24 +4082,52 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, TIntermN { // error message was put out by findFunction() // Put on a dummy node for error recovery - ConstantUnion *unionArray = new ConstantUnion[1]; + TConstantUnion *unionArray = new TConstantUnion[1]; unionArray->setFConst(0.0f); - callNode = intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConst), loc); + callNode = intermediate.addConstantUnion(unionArray, + TType(EbtFloat, EbpUndefined, EvqConst), loc); recover(); } } - delete fnCall; return callNode; } +TIntermTyped *TParseContext::addTernarySelection(TIntermTyped *cond, + TIntermTyped *trueBlock, + TIntermTyped *falseBlock, + const TSourceLoc &loc) +{ + if (boolErrorCheck(loc, cond)) + recover(); + + if (trueBlock->getType() != falseBlock->getType()) + { + binaryOpError(loc, ":", trueBlock->getCompleteString(), falseBlock->getCompleteString()); + recover(); + return falseBlock; + } + // ESSL1 sections 5.2 and 5.7: + // ESSL3 section 5.7: + // Ternary operator is not among the operators allowed for structures/arrays. + if (trueBlock->isArray() || trueBlock->getBasicType() == EbtStruct) + { + error(loc, "ternary operator is not allowed for structures or arrays", ":"); + recover(); + return falseBlock; + } + return intermediate.addSelection(cond, trueBlock, falseBlock, loc); +} // // Parse an array of strings using yyparse. // // Returns 0 for success. // -int PaParseStrings(size_t count, const char* const string[], const int length[], - TParseContext* context) { +int PaParseStrings(size_t count, + const char *const string[], + const int length[], + TParseContext *context) +{ if ((count == 0) || (string == NULL)) return 1; @@ -3177,6 +4142,3 @@ int PaParseStrings(size_t count, const char* const string[], const int length[], return (error == 0) && (context->numErrors() == 0) ? 0 : 1; } - - - diff --git a/src/3rdparty/angle/src/compiler/translator/ParseContext.h b/src/3rdparty/angle/src/compiler/translator/ParseContext.h index b6a0f4a637..1eaf1e5b42 100644 --- a/src/3rdparty/angle/src/compiler/translator/ParseContext.h +++ b/src/3rdparty/angle/src/compiler/translator/ParseContext.h @@ -13,7 +13,8 @@ #include "compiler/translator/SymbolTable.h" #include "compiler/preprocessor/Preprocessor.h" -struct TMatrixFields { +struct TMatrixFields +{ bool wholeRow; bool wholeCol; int row; @@ -24,137 +25,275 @@ struct TMatrixFields { // The following are extra variables needed during parsing, grouped together so // they can be passed to the parser without needing a global. // -struct TParseContext { - TParseContext(TSymbolTable& symt, TExtensionBehavior& ext, TIntermediate& interm, sh::GLenum type, ShShaderSpec spec, int options, bool checksPrecErrors, TInfoSink& is, bool debugShaderPrecisionSupported) : - intermediate(interm), - symbolTable(symt), - shaderType(type), - shaderSpec(spec), - compileOptions(options), - treeRoot(0), - mLoopNestingLevel(0), - structNestingLevel(0), - mSwitchNestingLevel(0), - currentFunctionType(NULL), - mFunctionReturnsValue(false), - checksPrecisionErrors(checksPrecErrors), - fragmentPrecisionHigh(false), - defaultMatrixPacking(EmpColumnMajor), - defaultBlockStorage(EbsShared), - diagnostics(is), - shaderVersion(100), - directiveHandler(ext, diagnostics, shaderVersion, debugShaderPrecisionSupported), - preprocessor(&diagnostics, &directiveHandler), - scanner(NULL) { } - TIntermediate& intermediate; // to hold and build a parse tree - TSymbolTable& symbolTable; // symbol table that goes with the language currently being parsed - sh::GLenum shaderType; // vertex or fragment language (future: pack or unpack) - ShShaderSpec shaderSpec; // The language specification compiler conforms to - GLES2 or WebGL. - int shaderVersion; - int compileOptions; - TIntermNode* treeRoot; // root of parse tree being created - int mLoopNestingLevel; // 0 if outside all loops - int structNestingLevel; // incremented while parsing a struct declaration - int mSwitchNestingLevel; // 0 if outside all switch statements - const TType* currentFunctionType; // the return type of the function that's currently being parsed - bool mFunctionReturnsValue; // true if a non-void function has a return - bool checksPrecisionErrors; // true if an error will be generated when a variable is declared without precision, explicit or implicit. - bool fragmentPrecisionHigh; // true if highp precision is supported in the fragment language. - TLayoutMatrixPacking defaultMatrixPacking; - TLayoutBlockStorage defaultBlockStorage; - TString HashErrMsg; - TDiagnostics diagnostics; - TDirectiveHandler directiveHandler; - pp::Preprocessor preprocessor; - void* scanner; - - int getShaderVersion() const { return shaderVersion; } - int numErrors() const { return diagnostics.numErrors(); } - TInfoSink& infoSink() { return diagnostics.infoSink(); } - void error(const TSourceLoc& loc, const char *reason, const char* token, - const char* extraInfo=""); - void warning(const TSourceLoc& loc, const char* reason, const char* token, - const char* extraInfo=""); - void trace(const char* str); +class TParseContext : angle::NonCopyable +{ + public: + TParseContext(TSymbolTable &symt, + TExtensionBehavior &ext, + TIntermediate &interm, + sh::GLenum type, + ShShaderSpec spec, + int options, + bool checksPrecErrors, + TInfoSink &is, + const ShBuiltInResources &resources) + : intermediate(interm), + symbolTable(symt), + mDeferredSingleDeclarationErrorCheck(false), + mShaderType(type), + mShaderSpec(spec), + mShaderVersion(100), + mTreeRoot(nullptr), + mLoopNestingLevel(0), + mStructNestingLevel(0), + mSwitchNestingLevel(0), + mCurrentFunctionType(nullptr), + mFunctionReturnsValue(false), + mChecksPrecisionErrors(checksPrecErrors), + mFragmentPrecisionHighOnESSL1(false), + mDefaultMatrixPacking(EmpColumnMajor), + mDefaultBlockStorage(EbsShared), + mDiagnostics(is), + mDirectiveHandler(ext, + mDiagnostics, + mShaderVersion, + mShaderType, + resources.WEBGL_debug_shader_precision == 1), + mPreprocessor(&mDiagnostics, &mDirectiveHandler), + mScanner(nullptr), + mUsesFragData(false), + mUsesFragColor(false), + mUsesSecondaryOutputs(false), + mMinProgramTexelOffset(resources.MinProgramTexelOffset), + mMaxProgramTexelOffset(resources.MaxProgramTexelOffset) + { + } + + const pp::Preprocessor &getPreprocessor() const { return mPreprocessor; } + pp::Preprocessor &getPreprocessor() { return mPreprocessor; } + void *getScanner() const { return mScanner; } + void setScanner(void *scanner) { mScanner = scanner; } + int getShaderVersion() const { return mShaderVersion; } + sh::GLenum getShaderType() const { return mShaderType; } + ShShaderSpec getShaderSpec() const { return mShaderSpec; } + int numErrors() const { return mDiagnostics.numErrors(); } + TInfoSink &infoSink() { return mDiagnostics.infoSink(); } + void error(const TSourceLoc &loc, const char *reason, const char *token, + const char *extraInfo=""); + void warning(const TSourceLoc &loc, const char *reason, const char *token, + const char *extraInfo=""); + + // If isError is false, a warning will be reported instead. + void outOfRangeError(bool isError, + const TSourceLoc &loc, + const char *reason, + const char *token, + const char *extraInfo = ""); + void recover(); + TIntermNode *getTreeRoot() const { return mTreeRoot; } + void setTreeRoot(TIntermNode *treeRoot) { mTreeRoot = treeRoot; } + + bool getFragmentPrecisionHigh() const + { + return mFragmentPrecisionHighOnESSL1 || mShaderVersion >= 300; + } + void setFragmentPrecisionHighOnESSL1(bool fragmentPrecisionHigh) + { + mFragmentPrecisionHighOnESSL1 = fragmentPrecisionHigh; + } + + void setLoopNestingLevel(int loopNestintLevel) + { + mLoopNestingLevel = loopNestintLevel; + } + + void incrLoopNestingLevel() { ++mLoopNestingLevel; } + void decrLoopNestingLevel() { --mLoopNestingLevel; } + + void incrSwitchNestingLevel() { ++mSwitchNestingLevel; } + void decrSwitchNestingLevel() { --mSwitchNestingLevel; } // This method is guaranteed to succeed, even if no variable with 'name' exists. const TVariable *getNamedVariable(const TSourceLoc &location, const TString *name, const TSymbol *symbol); + TIntermTyped *parseVariableIdentifier(const TSourceLoc &location, + const TString *name, + const TSymbol *symbol); - bool parseVectorFields(const TString&, int vecSize, TVectorFields&, const TSourceLoc& line); - bool parseMatrixFields(const TString&, int matCols, int matRows, TMatrixFields&, const TSourceLoc& line); - - bool reservedErrorCheck(const TSourceLoc& line, const TString& identifier); - void assignError(const TSourceLoc& line, const char* op, TString left, TString right); - void unaryOpError(const TSourceLoc& line, const char* op, TString operand); - void binaryOpError(const TSourceLoc& line, const char* op, TString left, TString right); - bool precisionErrorCheck(const TSourceLoc& line, TPrecision precision, TBasicType type); - bool lValueErrorCheck(const TSourceLoc& line, const char* op, TIntermTyped*); - bool constErrorCheck(TIntermTyped* node); - bool integerErrorCheck(TIntermTyped* node, const char* token); - bool globalErrorCheck(const TSourceLoc& line, bool global, const char* token); - bool constructorErrorCheck(const TSourceLoc& line, TIntermNode*, TFunction&, TOperator, TType*); - bool arraySizeErrorCheck(const TSourceLoc& line, TIntermTyped* expr, int& size); - bool arrayQualifierErrorCheck(const TSourceLoc& line, TPublicType type); - bool arrayTypeErrorCheck(const TSourceLoc& line, TPublicType type); - bool arrayErrorCheck(const TSourceLoc& line, const TString& identifier, const TPublicType &type, TVariable*& variable); - bool voidErrorCheck(const TSourceLoc&, const TString&, const TPublicType&); + bool parseVectorFields(const TString&, int vecSize, TVectorFields&, const TSourceLoc &line); + + bool reservedErrorCheck(const TSourceLoc &line, const TString &identifier); + void assignError(const TSourceLoc &line, const char *op, TString left, TString right); + void unaryOpError(const TSourceLoc &line, const char *op, TString operand); + void binaryOpError(const TSourceLoc &line, const char *op, TString left, TString right); + bool precisionErrorCheck(const TSourceLoc &line, TPrecision precision, TBasicType type); + bool lValueErrorCheck(const TSourceLoc &line, const char *op, TIntermTyped*); + bool constErrorCheck(TIntermTyped *node); + bool integerErrorCheck(TIntermTyped *node, const char *token); + bool globalErrorCheck(const TSourceLoc &line, bool global, const char *token); + bool constructorErrorCheck(const TSourceLoc &line, + TIntermNode *argumentsNode, + TFunction &function, + TOperator op, + TType *type); + bool arraySizeErrorCheck(const TSourceLoc &line, TIntermTyped *expr, int &size); + bool arrayQualifierErrorCheck(const TSourceLoc &line, const TPublicType &type); + bool arrayTypeErrorCheck(const TSourceLoc &line, const TPublicType &type); + bool voidErrorCheck(const TSourceLoc &line, const TString &identifier, const TBasicType &type); bool boolErrorCheck(const TSourceLoc&, const TIntermTyped*); bool boolErrorCheck(const TSourceLoc&, const TPublicType&); - bool samplerErrorCheck(const TSourceLoc& line, const TPublicType& pType, const char* reason); - bool structQualifierErrorCheck(const TSourceLoc& line, const TPublicType& pType); - bool locationDeclaratorListCheck(const TSourceLoc& line, const TPublicType &pType); - bool parameterSamplerErrorCheck(const TSourceLoc& line, TQualifier qualifier, const TType& type); - bool nonInitConstErrorCheck(const TSourceLoc& line, const TString& identifier, TPublicType& type, bool array); - bool nonInitErrorCheck(const TSourceLoc& line, const TString& identifier, const TPublicType& type, TVariable*& variable); - bool paramErrorCheck(const TSourceLoc& line, TQualifier qualifier, TQualifier paramQualifier, TType* type); - bool extensionErrorCheck(const TSourceLoc& line, const TString&); - bool singleDeclarationErrorCheck(TPublicType &publicType, const TSourceLoc& identifierLocation, const TString &identifier); - bool layoutLocationErrorCheck(const TSourceLoc& location, const TLayoutQualifier &layoutQualifier); + bool samplerErrorCheck(const TSourceLoc &line, const TPublicType &pType, const char *reason); + bool locationDeclaratorListCheck(const TSourceLoc &line, const TPublicType &pType); + bool parameterSamplerErrorCheck(const TSourceLoc &line, TQualifier qualifier, const TType &type); + bool paramErrorCheck(const TSourceLoc &line, TQualifier qualifier, TQualifier paramQualifier, TType *type); + bool extensionErrorCheck(const TSourceLoc &line, const TString&); + bool singleDeclarationErrorCheck(const TPublicType &publicType, const TSourceLoc &identifierLocation); + bool layoutLocationErrorCheck(const TSourceLoc &location, const TLayoutQualifier &layoutQualifier); bool functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *); + void es3InvariantErrorCheck(const TQualifier qualifier, const TSourceLoc &invariantLocation); + void es3InputOutputTypeCheck(const TQualifier qualifier, + const TPublicType &type, + const TSourceLoc &qualifierLocation); + + const TPragma &pragma() const { return mDirectiveHandler.pragma(); } + const TExtensionBehavior &extensionBehavior() const { return mDirectiveHandler.extensionBehavior(); } + bool supportsExtension(const char *extension); + bool isExtensionEnabled(const char *extension) const; + void handleExtensionDirective(const TSourceLoc &loc, const char *extName, const char *behavior); + void handlePragmaDirective(const TSourceLoc &loc, const char *name, const char *value, bool stdgl); + + bool containsSampler(const TType &type); + const TFunction* findFunction( + const TSourceLoc &line, TFunction *pfnCall, int inputShaderVersion, bool *builtIn = 0); + bool executeInitializer(const TSourceLoc &line, + const TString &identifier, + const TPublicType &pType, + TIntermTyped *initializer, + TIntermNode **intermNode); + + TPublicType addFullySpecifiedType(TQualifier qualifier, + bool invariant, + TLayoutQualifier layoutQualifier, + const TPublicType &typeSpecifier); + + TIntermAggregate *parseSingleDeclaration(TPublicType &publicType, + const TSourceLoc &identifierOrTypeLocation, + const TString &identifier); + TIntermAggregate *parseSingleArrayDeclaration(TPublicType &publicType, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &indexLocation, + TIntermTyped *indexExpression); + TIntermAggregate *parseSingleInitDeclaration(const TPublicType &publicType, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &initLocation, + TIntermTyped *initializer); + + // Parse a declaration like "type a[n] = initializer" + // Note that this does not apply to declarations like "type[n] a = initializer" + TIntermAggregate *parseSingleArrayInitDeclaration(TPublicType &publicType, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &indexLocation, + TIntermTyped *indexExpression, + const TSourceLoc &initLocation, + TIntermTyped *initializer); + + TIntermAggregate *parseInvariantDeclaration(const TSourceLoc &invariantLoc, + const TSourceLoc &identifierLoc, + const TString *identifier, + const TSymbol *symbol); + + TIntermAggregate *parseDeclarator(TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier); + TIntermAggregate *parseArrayDeclarator(TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &arrayLocation, + TIntermTyped *indexExpression); + TIntermAggregate *parseInitDeclarator(const TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &initLocation, + TIntermTyped *initializer); + + // Parse a declarator like "a[n] = initializer" + TIntermAggregate *parseArrayInitDeclarator(const TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &indexLocation, + TIntermTyped *indexExpression, + const TSourceLoc &initLocation, + TIntermTyped *initializer); - const TPragma& pragma() const { return directiveHandler.pragma(); } - const TExtensionBehavior& extensionBehavior() const { return directiveHandler.extensionBehavior(); } - bool supportsExtension(const char* extension); - bool isExtensionEnabled(const char* extension) const; - void handleExtensionDirective(const TSourceLoc& loc, const char* extName, const char* behavior); - void handlePragmaDirective(const TSourceLoc& loc, const char* name, const char* value, bool stdgl); - - bool containsSampler(TType& type); - bool areAllChildConst(TIntermAggregate* aggrNode); - const TFunction* findFunction(const TSourceLoc& line, TFunction* pfnCall, int inputShaderVersion, bool *builtIn = 0); - bool executeInitializer(const TSourceLoc& line, const TString& identifier, TPublicType& pType, - TIntermTyped* initializer, TIntermNode*& intermNode, TVariable* variable = 0); - - TPublicType addFullySpecifiedType(TQualifier qualifier, const TPublicType& typeSpecifier); - TPublicType addFullySpecifiedType(TQualifier qualifier, TLayoutQualifier layoutQualifier, const TPublicType& typeSpecifier); - TIntermAggregate* parseSingleDeclaration(TPublicType &publicType, const TSourceLoc& identifierLocation, const TString &identifier); - TIntermAggregate* parseSingleArrayDeclaration(TPublicType &publicType, const TSourceLoc& identifierLocation, const TString &identifier, const TSourceLoc& indexLocation, TIntermTyped *indexExpression); - TIntermAggregate* parseSingleInitDeclaration(TPublicType &publicType, const TSourceLoc& identifierLocation, const TString &identifier, const TSourceLoc& initLocation, TIntermTyped *initializer); - TIntermAggregate* parseInvariantDeclaration(const TSourceLoc &invariantLoc, const TSourceLoc &identifierLoc, const TString *identifier, const TSymbol *symbol); - - TIntermAggregate* parseDeclarator(TPublicType &publicType, TIntermAggregate *aggregateDeclaration, TSymbol *identifierSymbol, const TSourceLoc& identifierLocation, const TString &identifier); - TIntermAggregate* parseArrayDeclarator(TPublicType &publicType, const TSourceLoc& identifierLocation, const TString &identifier, const TSourceLoc& arrayLocation, TIntermNode *declaratorList, TIntermTyped *indexExpression); - TIntermAggregate* parseInitDeclarator(TPublicType &publicType, TIntermAggregate *declaratorList, const TSourceLoc& identifierLocation, const TString &identifier, const TSourceLoc& initLocation, TIntermTyped *initializer); void parseGlobalLayoutQualifier(const TPublicType &typeQualifier); - TFunction *addConstructorFunc(TPublicType publicType); - TIntermTyped* addConstructor(TIntermNode*, TType*, TOperator, TFunction*, const TSourceLoc&); - TIntermTyped* foldConstConstructor(TIntermAggregate* aggrNode, const TType& type); - TIntermTyped* addConstVectorNode(TVectorFields&, TIntermTyped*, const TSourceLoc&); - TIntermTyped* addConstMatrixNode(int , TIntermTyped*, const TSourceLoc&); - TIntermTyped* addConstArrayNode(int index, TIntermTyped* node, const TSourceLoc& line); - TIntermTyped* addConstStruct(const TString &identifier, TIntermTyped *node, const TSourceLoc& line); - TIntermTyped* addIndexExpression(TIntermTyped *baseExpression, const TSourceLoc& location, TIntermTyped *indexExpression); - TIntermTyped* addFieldSelectionExpression(TIntermTyped *baseExpression, const TSourceLoc& dotLocation, const TString &fieldString, const TSourceLoc& fieldLocation); - - TFieldList *addStructDeclaratorList(const TPublicType& typeSpecifier, TFieldList *fieldList); - TPublicType addStructure(const TSourceLoc& structLine, const TSourceLoc& nameLine, const TString *structName, TFieldList* fieldList); - - TIntermAggregate* addInterfaceBlock(const TPublicType& typeQualifier, const TSourceLoc& nameLine, const TString& blockName, TFieldList* fieldList, - const TString* instanceName, const TSourceLoc& instanceLine, TIntermTyped* arrayIndex, const TSourceLoc& arrayIndexLine); - - TLayoutQualifier parseLayoutQualifier(const TString &qualifierType, const TSourceLoc& qualifierTypeLine); - TLayoutQualifier parseLayoutQualifier(const TString &qualifierType, const TSourceLoc& qualifierTypeLine, const TString &intValueString, int intValue, const TSourceLoc& intValueLine); + TIntermAggregate *addFunctionPrototypeDeclaration(const TFunction &function, + const TSourceLoc &location); + TIntermAggregate *addFunctionDefinition(const TFunction &function, + TIntermAggregate *functionPrototype, + TIntermAggregate *functionBody, + const TSourceLoc &location); + void parseFunctionPrototype(const TSourceLoc &location, + TFunction *function, + TIntermAggregate **aggregateOut); + TFunction *parseFunctionDeclarator(const TSourceLoc &location, + TFunction *function); + TFunction *addConstructorFunc(const TPublicType &publicType); + TIntermTyped *addConstructor(TIntermNode *arguments, + TType *type, + TOperator op, + TFunction *fnCall, + const TSourceLoc &line); + TIntermTyped *addConstVectorNode(TVectorFields &fields, + TIntermConstantUnion *node, + const TSourceLoc &line, + bool outOfRangeIndexIsError); + TIntermTyped *addConstMatrixNode(int index, + TIntermConstantUnion *node, + const TSourceLoc &line, + bool outOfRangeIndexIsError); + TIntermTyped *addConstArrayNode(int index, + TIntermConstantUnion *node, + const TSourceLoc &line, + bool outOfRangeIndexIsError); + TIntermTyped *addConstStruct( + const TString &identifier, TIntermTyped *node, const TSourceLoc& line); + TIntermTyped *addIndexExpression(TIntermTyped *baseExpression, + const TSourceLoc& location, + TIntermTyped *indexExpression); + TIntermTyped* addFieldSelectionExpression(TIntermTyped *baseExpression, + const TSourceLoc &dotLocation, + const TString &fieldString, + const TSourceLoc &fieldLocation); + + TFieldList *addStructDeclaratorList(const TPublicType &typeSpecifier, TFieldList *fieldList); + TPublicType addStructure(const TSourceLoc &structLine, + const TSourceLoc &nameLine, + const TString *structName, + TFieldList *fieldList); + + TIntermAggregate* addInterfaceBlock(const TPublicType &typeQualifier, + const TSourceLoc &nameLine, + const TString &blockName, + TFieldList *fieldList, + const TString *instanceName, + const TSourceLoc &instanceLine, + TIntermTyped *arrayIndex, + const TSourceLoc& arrayIndexLine); + + TLayoutQualifier parseLayoutQualifier( + const TString &qualifierType, const TSourceLoc &qualifierTypeLine); + TLayoutQualifier parseLayoutQualifier(const TString &qualifierType, + const TSourceLoc &qualifierTypeLine, + const TString &intValueString, + int intValue, + const TSourceLoc &intValueLine); TLayoutQualifier joinLayoutQualifiers(TLayoutQualifier leftQualifier, TLayoutQualifier rightQualifier); TPublicType joinInterpolationQualifiers(const TSourceLoc &interpolationLoc, TQualifier interpolationQualifier, const TSourceLoc &storageLoc, TQualifier storageQualifier); @@ -162,46 +301,92 @@ struct TParseContext { // Performs an error check for embedded struct declarations. // Returns true if an error was raised due to the declaration of // this struct. - bool enterStructDeclaration(const TSourceLoc& line, const TString& identifier); + bool enterStructDeclaration(const TSourceLoc &line, const TString &identifier); void exitStructDeclaration(); - bool structNestingErrorCheck(const TSourceLoc& line, const TField& field); + bool structNestingErrorCheck(const TSourceLoc &line, const TField &field); TIntermSwitch *addSwitch(TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &loc); TIntermCase *addCase(TIntermTyped *condition, const TSourceLoc &loc); TIntermCase *addDefault(const TSourceLoc &loc); - TIntermTyped *addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &); - TIntermTyped *addUnaryMathLValue(TOperator op, TIntermTyped *child, const TSourceLoc &); - TIntermTyped *addBinaryMath(TOperator op, TIntermTyped *left, TIntermTyped *right, - const TSourceLoc &); - TIntermTyped *addBinaryMathBooleanResult(TOperator op, TIntermTyped *left, TIntermTyped *right, - const TSourceLoc &); - TIntermTyped *addAssign(TOperator op, TIntermTyped *left, TIntermTyped *right, - const TSourceLoc &loc); + TIntermTyped *addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc); + TIntermTyped *addUnaryMathLValue(TOperator op, TIntermTyped *child, const TSourceLoc &loc); + TIntermTyped *addBinaryMath( + TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc); + TIntermTyped *addBinaryMathBooleanResult( + TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc); + TIntermTyped *addAssign( + TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc); + + TIntermTyped *addComma(TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc); TIntermBranch *addBranch(TOperator op, const TSourceLoc &loc); TIntermBranch *addBranch(TOperator op, TIntermTyped *returnValue, const TSourceLoc &loc); - TIntermTyped *addFunctionCallOrMethod(TFunction *fnCall, TIntermNode *node, - const TSourceLoc &loc, bool *fatalError); + void checkTextureOffsetConst(TIntermAggregate *functionCall); + TIntermTyped *addFunctionCallOrMethod(TFunction *fnCall, + TIntermNode *paramNode, + TIntermNode *thisNode, + const TSourceLoc &loc, + bool *fatalError); + + TIntermTyped *addTernarySelection( + TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, const TSourceLoc &line); + + // TODO(jmadill): make these private + TIntermediate &intermediate; // to hold and build a parse tree + TSymbolTable &symbolTable; // symbol table that goes with the language currently being parsed private: - TIntermTyped *addBinaryMathInternal(TOperator op, TIntermTyped *left, TIntermTyped *right, - const TSourceLoc &loc); - TIntermTyped *createAssign(TOperator op, TIntermTyped *left, TIntermTyped *right, - const TSourceLoc &loc); + bool declareVariable(const TSourceLoc &line, const TString &identifier, const TType &type, TVariable **variable); + + bool nonInitErrorCheck(const TSourceLoc &line, const TString &identifier, TPublicType *type); + + TIntermTyped *addBinaryMathInternal( + TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc); + TIntermTyped *createAssign( + TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc); // The funcReturnType parameter is expected to be non-null when the operation is a built-in function. // It is expected to be null for other unary operators. - TIntermTyped *createUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc, - const TType *funcReturnType); + TIntermTyped *createUnaryMath( + TOperator op, TIntermTyped *child, const TSourceLoc &loc, const TType *funcReturnType); // Return true if the checks pass - bool binaryOpCommonCheck(TOperator op, TIntermTyped *left, TIntermTyped *right, - const TSourceLoc &loc); + bool binaryOpCommonCheck( + TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc); + + // Set to true when the last/current declarator list was started with an empty declaration. + bool mDeferredSingleDeclarationErrorCheck; + + sh::GLenum mShaderType; // vertex or fragment language (future: pack or unpack) + ShShaderSpec mShaderSpec; // The language specification compiler conforms to - GLES2 or WebGL. + int mShaderVersion; + TIntermNode *mTreeRoot; // root of parse tree being created + int mLoopNestingLevel; // 0 if outside all loops + int mStructNestingLevel; // incremented while parsing a struct declaration + int mSwitchNestingLevel; // 0 if outside all switch statements + const TType *mCurrentFunctionType; // the return type of the function that's currently being parsed + bool mFunctionReturnsValue; // true if a non-void function has a return + bool mChecksPrecisionErrors; // true if an error will be generated when a variable is declared without precision, explicit or implicit. + bool mFragmentPrecisionHighOnESSL1; // true if highp precision is supported when compiling + // ESSL1. + TLayoutMatrixPacking mDefaultMatrixPacking; + TLayoutBlockStorage mDefaultBlockStorage; + TString mHashErrMsg; + TDiagnostics mDiagnostics; + TDirectiveHandler mDirectiveHandler; + pp::Preprocessor mPreprocessor; + void *mScanner; + bool mUsesFragData; // track if we are using both gl_FragData and gl_FragColor + bool mUsesFragColor; + bool mUsesSecondaryOutputs; // Track if we are using either gl_SecondaryFragData or + // gl_Secondary FragColor or both. + int mMinProgramTexelOffset; + int mMaxProgramTexelOffset; }; -int PaParseStrings(size_t count, const char* const string[], const int length[], - TParseContext* context); +int PaParseStrings( + size_t count, const char *const string[], const int length[], TParseContext *context); #endif // COMPILER_TRANSLATOR_PARSECONTEXT_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/PoolAlloc.h b/src/3rdparty/angle/src/compiler/translator/PoolAlloc.h index 6cd8d30114..dab2926c90 100644 --- a/src/3rdparty/angle/src/compiler/translator/PoolAlloc.h +++ b/src/3rdparty/angle/src/compiler/translator/PoolAlloc.h @@ -247,18 +247,13 @@ public: pointer address(reference x) const { return &x; } const_pointer address(const_reference x) const { return &x; } - pool_allocator() : allocator(GetGlobalPoolAllocator()) { } - pool_allocator(TPoolAllocator& a) : allocator(&a) { } - pool_allocator(const pool_allocator& p) : allocator(p.allocator) { } - - template - pool_allocator& operator=(const pool_allocator& p) { - allocator = p.allocator; - return *this; - } + pool_allocator() { } template - pool_allocator(const pool_allocator& p) : allocator(&p.getAllocator()) { } + pool_allocator(const pool_allocator& p) { } + + template + pool_allocator& operator=(const pool_allocator& p) { return *this; } #if defined(__SUNPRO_CC) && !defined(_RWSTD_ALLOCATOR) // libCStd on some platforms have a different allocate/deallocate interface. @@ -284,17 +279,13 @@ public: void construct(pointer p, const T& val) { new ((void *)p) T(val); } void destroy(pointer p) { p->T::~T(); } - bool operator==(const pool_allocator& rhs) const { return &getAllocator() == &rhs.getAllocator(); } - bool operator!=(const pool_allocator& rhs) const { return &getAllocator() != &rhs.getAllocator(); } + bool operator==(const pool_allocator& rhs) const { return true; } + bool operator!=(const pool_allocator& rhs) const { return false; } size_type max_size() const { return static_cast(-1) / sizeof(T); } size_type max_size(int size) const { return static_cast(-1) / size; } - void setAllocator(TPoolAllocator* a) { allocator = a; } - TPoolAllocator& getAllocator() const { return *allocator; } - -protected: - TPoolAllocator* allocator; + TPoolAllocator& getAllocator() const { return *GetGlobalPoolAllocator(); } }; #endif // COMPILER_TRANSLATOR_POOLALLOC_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.cpp b/src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.cpp new file mode 100644 index 0000000000..ef62dbfce7 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.cpp @@ -0,0 +1,81 @@ +// +// 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 PruneEmptyDeclarations function prunes unnecessary empty declarations and declarators from the AST. + +#include "compiler/translator/PruneEmptyDeclarations.h" + +#include "compiler/translator/IntermNode.h" + +namespace +{ + +class PruneEmptyDeclarationsTraverser : private TIntermTraverser +{ + public: + static void apply(TIntermNode *root); + private: + PruneEmptyDeclarationsTraverser(); + bool visitAggregate(Visit, TIntermAggregate *node) override; +}; + +void PruneEmptyDeclarationsTraverser::apply(TIntermNode *root) +{ + PruneEmptyDeclarationsTraverser prune; + root->traverse(&prune); + prune.updateTree(); +} + +PruneEmptyDeclarationsTraverser::PruneEmptyDeclarationsTraverser() + : TIntermTraverser(true, false, false) +{ +} + +bool PruneEmptyDeclarationsTraverser::visitAggregate(Visit, TIntermAggregate *node) +{ + if (node->getOp() == EOpDeclaration) + { + TIntermSequence *sequence = node->getSequence(); + if (sequence->size() >= 1) + { + TIntermSymbol *sym = sequence->front()->getAsSymbolNode(); + // Prune declarations without a variable name, unless it's an interface block declaration. + if (sym != nullptr && sym->getSymbol() == "" && !sym->isInterfaceBlock()) + { + if (sequence->size() > 1) + { + // Generate a replacement that will remove the empty declarator in the beginning of a declarator + // list. Example of a declaration that will be changed: + // float, a; + // will be changed to + // float a; + // This applies also to struct declarations. + TIntermSequence emptyReplacement; + mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(node, sym, emptyReplacement)); + } + else if (sym->getBasicType() != EbtStruct) + { + // Single struct declarations may just declare the struct type and no variables, so they should + // not be pruned. All other single empty declarations can be pruned entirely. Example of an empty + // declaration that will be pruned: + // float; + TIntermSequence emptyReplacement; + TIntermAggregate *parentAgg = getParentNode()->getAsAggregate(); + ASSERT(parentAgg != nullptr); + mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(parentAgg, node, emptyReplacement)); + } + } + } + return false; + } + return true; +} + +} // namespace + +void PruneEmptyDeclarations(TIntermNode *root) +{ + PruneEmptyDeclarationsTraverser::apply(root); +} diff --git a/src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.h b/src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.h new file mode 100644 index 0000000000..122e830902 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.h @@ -0,0 +1,15 @@ +// +// 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 PruneEmptyDeclarations function prunes unnecessary empty declarations and declarators from the AST. + +#ifndef COMPILER_TRANSLATOR_PRUNEEMPTYDECLARATIONS_H_ +#define COMPILER_TRANSLATOR_PRUNEEMPTYDECLARATIONS_H_ + +class TIntermNode; + +void PruneEmptyDeclarations(TIntermNode *root); + +#endif // COMPILER_TRANSLATOR_PRUNEEMPTYDECLARATIONS_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.cpp b/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.cpp new file mode 100644 index 0000000000..14e88b749a --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.cpp @@ -0,0 +1,157 @@ +// +// 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. +// +// During parsing, all constant expressions are folded to constant union nodes. The expressions that have been +// folded may have had precision qualifiers, which should affect the precision of the consuming operation. +// If the folded constant union nodes are written to output as such they won't have any precision qualifiers, +// and their effect on the precision of the consuming operation is lost. +// +// RecordConstantPrecision is an AST traverser that inspects the precision qualifiers of constants and hoists +// the constants outside the containing expression as precision qualified named variables in case that is +// required for correct precision propagation. +// + +#include "compiler/translator/RecordConstantPrecision.h" + +#include "compiler/translator/InfoSink.h" +#include "compiler/translator/IntermNode.h" + +namespace +{ + +class RecordConstantPrecisionTraverser : public TIntermTraverser +{ + public: + RecordConstantPrecisionTraverser(); + + void visitConstantUnion(TIntermConstantUnion *node) override; + + void nextIteration(); + + bool foundHigherPrecisionConstant() const { return mFoundHigherPrecisionConstant; } + protected: + bool operandAffectsParentOperationPrecision(TIntermTyped *operand); + + bool mFoundHigherPrecisionConstant; +}; + +RecordConstantPrecisionTraverser::RecordConstantPrecisionTraverser() + : TIntermTraverser(true, false, true), + mFoundHigherPrecisionConstant(false) +{ +} + +bool RecordConstantPrecisionTraverser::operandAffectsParentOperationPrecision(TIntermTyped *operand) +{ + const TIntermBinary *parentAsBinary = getParentNode()->getAsBinaryNode(); + if (parentAsBinary != nullptr) + { + // If the constant is assigned or is used to initialize a variable, or if it's an index, + // its precision has no effect. + switch (parentAsBinary->getOp()) + { + case EOpInitialize: + case EOpAssign: + case EOpIndexDirect: + case EOpIndexDirectStruct: + case EOpIndexDirectInterfaceBlock: + case EOpIndexIndirect: + return false; + default: + break; + } + + TIntermTyped *otherOperand = parentAsBinary->getRight(); + if (otherOperand == operand) + { + otherOperand = parentAsBinary->getLeft(); + } + // If the precision of the other child is at least as high as the precision of the constant, the precision of + // the constant has no effect. + if (otherOperand->getAsConstantUnion() == nullptr && otherOperand->getPrecision() >= operand->getPrecision()) + { + return false; + } + } + + TIntermAggregate *parentAsAggregate = getParentNode()->getAsAggregate(); + if (parentAsAggregate != nullptr) + { + if (!parentAsAggregate->gotPrecisionFromChildren()) + { + // This can be either: + // * a call to an user-defined function + // * a call to a texture function + // * some other kind of aggregate + // In any of these cases the constant precision has no effect. + return false; + } + if (parentAsAggregate->isConstructor() && parentAsAggregate->getBasicType() == EbtBool) + { + return false; + } + // If the precision of operands does affect the result, but the precision of any of the other children + // has a precision that's at least as high as the precision of the constant, the precision of the constant + // has no effect. + TIntermSequence *parameters = parentAsAggregate->getSequence(); + for (TIntermNode *parameter : *parameters) + { + const TIntermTyped *typedParameter = parameter->getAsTyped(); + if (parameter != operand && typedParameter != nullptr && parameter->getAsConstantUnion() == nullptr && + typedParameter->getPrecision() >= operand->getPrecision()) + { + return false; + } + } + } + return true; +} + +void RecordConstantPrecisionTraverser::visitConstantUnion(TIntermConstantUnion *node) +{ + if (mFoundHigherPrecisionConstant) + return; + + // If the constant has lowp or undefined precision, it can't increase the precision of consuming operations. + if (node->getPrecision() < EbpMedium) + return; + + // It's possible the node has no effect on the precision of the consuming expression, depending on the + // consuming expression, and the precision of the other parameters of the expression. + if (!operandAffectsParentOperationPrecision(node)) + return; + + // Make the constant a precision-qualified named variable to make sure it affects the precision of the consuming + // expression. + TIntermSequence insertions; + insertions.push_back(createTempInitDeclaration(node, EvqConst)); + insertStatementsInParentBlock(insertions); + mReplacements.push_back(NodeUpdateEntry(getParentNode(), node, createTempSymbol(node->getType()), false)); + mFoundHigherPrecisionConstant = true; +} + +void RecordConstantPrecisionTraverser::nextIteration() +{ + nextTemporaryIndex(); + mFoundHigherPrecisionConstant = false; +} + +} // namespace + +void RecordConstantPrecision(TIntermNode *root, unsigned int *temporaryIndex) +{ + RecordConstantPrecisionTraverser traverser; + ASSERT(temporaryIndex != nullptr); + traverser.useTemporaryIndex(temporaryIndex); + // Iterate as necessary, and reset the traverser between iterations. + do + { + traverser.nextIteration(); + root->traverse(&traverser); + if (traverser.foundHigherPrecisionConstant()) + traverser.updateTree(); + } + while (traverser.foundHigherPrecisionConstant()); +} diff --git a/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.h b/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.h new file mode 100644 index 0000000000..2cd401b418 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.h @@ -0,0 +1,23 @@ +// +// 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. +// +// During parsing, all constant expressions are folded to constant union nodes. The expressions that have been +// folded may have had precision qualifiers, which should affect the precision of the consuming operation. +// If the folded constant union nodes are written to output as such they won't have any precision qualifiers, +// and their effect on the precision of the consuming operation is lost. +// +// RecordConstantPrecision is an AST traverser that inspects the precision qualifiers of constants and hoists +// the constants outside the containing expression as precision qualified named variables in case that is +// required for correct precision propagation. +// + +#ifndef COMPILER_TRANSLATOR_RECORDCONSTANTPRECISION_H_ +#define COMPILER_TRANSLATOR_RECORDCONSTANTPRECISION_H_ + +class TIntermNode; + +void RecordConstantPrecision(TIntermNode *root, unsigned int *temporaryIndex); + +#endif // COMPILER_TRANSLATOR_RECORDCONSTANTPRECISION_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.cpp b/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.cpp index 767b18085c..5e0db2ad26 100644 --- a/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.cpp +++ b/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.cpp @@ -4,8 +4,8 @@ // found in the LICENSE file. // +#include "common/debug.h" #include "compiler/translator/RegenerateStructNames.h" -#include "compiler/translator/compilerdebug.h" void RegenerateStructNames::visitSymbol(TIntermSymbol *symbol) { diff --git a/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.h b/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.h index 2acd68be34..3b98e5d709 100644 --- a/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.h +++ b/src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.h @@ -17,13 +17,14 @@ class RegenerateStructNames : public TIntermTraverser public: RegenerateStructNames(const TSymbolTable &symbolTable, int shaderVersion) - : mSymbolTable(symbolTable), + : TIntermTraverser(true, false, false), + mSymbolTable(symbolTable), mShaderVersion(shaderVersion), mScopeDepth(0) {} protected: - virtual void visitSymbol(TIntermSymbol *); - virtual bool visitAggregate(Visit, TIntermAggregate *); + void visitSymbol(TIntermSymbol *) override; + bool visitAggregate(Visit, TIntermAggregate *) override; private: const TSymbolTable &mSymbolTable; diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.cpp b/src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.cpp new file mode 100644 index 0000000000..74814f22a7 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.cpp @@ -0,0 +1,513 @@ +// +// 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. +// +// RemoveDynamicIndexing is an AST traverser to remove dynamic indexing of vectors and matrices, +// replacing them with calls to functions that choose which component to return or write. +// + +#include "compiler/translator/RemoveDynamicIndexing.h" + +#include "compiler/translator/InfoSink.h" +#include "compiler/translator/IntermNode.h" +#include "compiler/translator/SymbolTable.h" + +namespace +{ + +TName GetIndexFunctionName(const TType &type, bool write) +{ + TInfoSinkBase nameSink; + nameSink << "dyn_index_"; + if (write) + { + nameSink << "write_"; + } + if (type.isMatrix()) + { + nameSink << "mat" << type.getCols() << "x" << type.getRows(); + } + else + { + switch (type.getBasicType()) + { + case EbtInt: + nameSink << "ivec"; + break; + case EbtBool: + nameSink << "bvec"; + break; + case EbtUInt: + nameSink << "uvec"; + break; + case EbtFloat: + nameSink << "vec"; + break; + default: + UNREACHABLE(); + } + nameSink << type.getNominalSize(); + } + TString nameString = TFunction::mangleName(nameSink.c_str()); + TName name(nameString); + name.setInternal(true); + return name; +} + +TIntermSymbol *CreateBaseSymbol(const TType &type, TQualifier qualifier) +{ + TIntermSymbol *symbol = new TIntermSymbol(0, "base", type); + symbol->setInternal(true); + symbol->getTypePointer()->setQualifier(qualifier); + return symbol; +} + +TIntermSymbol *CreateIndexSymbol() +{ + TIntermSymbol *symbol = new TIntermSymbol(0, "index", TType(EbtInt, EbpHigh)); + symbol->setInternal(true); + symbol->getTypePointer()->setQualifier(EvqIn); + return symbol; +} + +TIntermSymbol *CreateValueSymbol(const TType &type) +{ + TIntermSymbol *symbol = new TIntermSymbol(0, "value", type); + symbol->setInternal(true); + symbol->getTypePointer()->setQualifier(EvqIn); + return symbol; +} + +TIntermConstantUnion *CreateIntConstantNode(int i) +{ + TConstantUnion *constant = new TConstantUnion(); + constant->setIConst(i); + return new TIntermConstantUnion(constant, TType(EbtInt, EbpHigh)); +} + +TIntermBinary *CreateIndexDirectBaseSymbolNode(const TType &indexedType, + const TType &fieldType, + const int index, + TQualifier baseQualifier) +{ + TIntermBinary *indexNode = new TIntermBinary(EOpIndexDirect); + indexNode->setType(fieldType); + TIntermSymbol *baseSymbol = CreateBaseSymbol(indexedType, baseQualifier); + indexNode->setLeft(baseSymbol); + indexNode->setRight(CreateIntConstantNode(index)); + return indexNode; +} + +TIntermBinary *CreateAssignValueSymbolNode(TIntermTyped *targetNode, const TType &assignedValueType) +{ + TIntermBinary *assignNode = new TIntermBinary(EOpAssign); + assignNode->setType(assignedValueType); + assignNode->setLeft(targetNode); + assignNode->setRight(CreateValueSymbol(assignedValueType)); + return assignNode; +} + +TIntermTyped *EnsureSignedInt(TIntermTyped *node) +{ + if (node->getBasicType() == EbtInt) + return node; + + TIntermAggregate *convertedNode = new TIntermAggregate(EOpConstructInt); + convertedNode->setType(TType(EbtInt)); + convertedNode->getSequence()->push_back(node); + convertedNode->setPrecisionFromChildren(); + return convertedNode; +} + +TType GetFieldType(const TType &indexedType) +{ + if (indexedType.isMatrix()) + { + TType fieldType = TType(indexedType.getBasicType(), indexedType.getPrecision()); + fieldType.setPrimarySize(static_cast(indexedType.getRows())); + return fieldType; + } + else + { + return TType(indexedType.getBasicType(), indexedType.getPrecision()); + } +} + +// Generate a read or write function for one field in a vector/matrix. +// Out-of-range indices are clamped. This is consistent with how ANGLE handles out-of-range +// indices in other places. +// Note that indices can be either int or uint. We create only int versions of the functions, +// and convert uint indices to int at the call site. +// read function example: +// float dyn_index_vec2(in vec2 base, in int index) +// { +// switch(index) +// { +// case (0): +// return base[0]; +// case (1): +// return base[1]; +// default: +// break; +// } +// if (index < 0) +// return base[0]; +// return base[1]; +// } +// write function example: +// void dyn_index_write_vec2(inout vec2 base, in int index, in float value) +// { +// switch(index) +// { +// case (0): +// base[0] = value; +// return; +// case (1): +// base[1] = value; +// return; +// default: +// break; +// } +// if (index < 0) +// { +// base[0] = value; +// return; +// } +// base[1] = value; +// } +// Note that else is not used in above functions to avoid the RewriteElseBlocks transformation. +TIntermAggregate *GetIndexFunctionDefinition(TType type, bool write) +{ + ASSERT(!type.isArray()); + // Conservatively use highp here, even if the indexed type is not highp. That way the code can't + // end up using mediump version of an indexing function for a highp value, if both mediump and + // highp values are being indexed in the shader. For HLSL precision doesn't matter, but in + // principle this code could be used with multiple backends. + type.setPrecision(EbpHigh); + TIntermAggregate *indexingFunction = new TIntermAggregate(EOpFunction); + indexingFunction->setNameObj(GetIndexFunctionName(type, write)); + + TType fieldType = GetFieldType(type); + int numCases = 0; + if (type.isMatrix()) + { + numCases = type.getCols(); + } + else + { + numCases = type.getNominalSize(); + } + if (write) + { + indexingFunction->setType(TType(EbtVoid)); + } + else + { + indexingFunction->setType(fieldType); + } + + TIntermAggregate *paramsNode = new TIntermAggregate(EOpParameters); + TQualifier baseQualifier = EvqInOut; + if (!write) + baseQualifier = EvqIn; + TIntermSymbol *baseParam = CreateBaseSymbol(type, baseQualifier); + paramsNode->getSequence()->push_back(baseParam); + TIntermSymbol *indexParam = CreateIndexSymbol(); + paramsNode->getSequence()->push_back(indexParam); + if (write) + { + TIntermSymbol *valueParam = CreateValueSymbol(fieldType); + paramsNode->getSequence()->push_back(valueParam); + } + indexingFunction->getSequence()->push_back(paramsNode); + + TIntermAggregate *statementList = new TIntermAggregate(EOpSequence); + for (int i = 0; i < numCases; ++i) + { + TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i)); + statementList->getSequence()->push_back(caseNode); + + TIntermBinary *indexNode = + CreateIndexDirectBaseSymbolNode(type, fieldType, i, baseQualifier); + if (write) + { + TIntermBinary *assignNode = CreateAssignValueSymbolNode(indexNode, fieldType); + statementList->getSequence()->push_back(assignNode); + TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr); + statementList->getSequence()->push_back(returnNode); + } + else + { + TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode); + statementList->getSequence()->push_back(returnNode); + } + } + + // Default case + TIntermCase *defaultNode = new TIntermCase(nullptr); + statementList->getSequence()->push_back(defaultNode); + TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr); + statementList->getSequence()->push_back(breakNode); + + TIntermSwitch *switchNode = new TIntermSwitch(CreateIndexSymbol(), statementList); + + TIntermAggregate *bodyNode = new TIntermAggregate(EOpSequence); + bodyNode->getSequence()->push_back(switchNode); + + TIntermBinary *cond = new TIntermBinary(EOpLessThan); + cond->setType(TType(EbtBool, EbpUndefined)); + cond->setLeft(CreateIndexSymbol()); + cond->setRight(CreateIntConstantNode(0)); + + // Two blocks: one accesses (either reads or writes) the first element and returns, + // the other accesses the last element. + TIntermAggregate *useFirstBlock = new TIntermAggregate(EOpSequence); + TIntermAggregate *useLastBlock = new TIntermAggregate(EOpSequence); + TIntermBinary *indexFirstNode = + CreateIndexDirectBaseSymbolNode(type, fieldType, 0, baseQualifier); + TIntermBinary *indexLastNode = + CreateIndexDirectBaseSymbolNode(type, fieldType, numCases - 1, baseQualifier); + if (write) + { + TIntermBinary *assignFirstNode = CreateAssignValueSymbolNode(indexFirstNode, fieldType); + useFirstBlock->getSequence()->push_back(assignFirstNode); + TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr); + useFirstBlock->getSequence()->push_back(returnNode); + + TIntermBinary *assignLastNode = CreateAssignValueSymbolNode(indexLastNode, fieldType); + useLastBlock->getSequence()->push_back(assignLastNode); + } + else + { + TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode); + useFirstBlock->getSequence()->push_back(returnFirstNode); + + TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode); + useLastBlock->getSequence()->push_back(returnLastNode); + } + TIntermSelection *ifNode = new TIntermSelection(cond, useFirstBlock, nullptr); + bodyNode->getSequence()->push_back(ifNode); + bodyNode->getSequence()->push_back(useLastBlock); + + indexingFunction->getSequence()->push_back(bodyNode); + + return indexingFunction; +} + +class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser +{ + public: + RemoveDynamicIndexingTraverser(const TSymbolTable &symbolTable, int shaderVersion); + + bool visitBinary(Visit visit, TIntermBinary *node) override; + + void insertHelperDefinitions(TIntermNode *root); + + void nextIteration(); + + bool usedTreeInsertion() const { return mUsedTreeInsertion; } + + protected: + // Sets of types that are indexed. Note that these can not store multiple variants + // of the same type with different precisions - only one precision gets stored. + std::set mIndexedVecAndMatrixTypes; + std::set mWrittenVecAndMatrixTypes; + + bool mUsedTreeInsertion; + + // When true, the traverser will remove side effects from any indexing expression. + // This is done so that in code like + // V[j++][i]++. + // where V is an array of vectors, j++ will only be evaluated once. + bool mRemoveIndexSideEffectsInSubtree; +}; + +RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(const TSymbolTable &symbolTable, + int shaderVersion) + : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion), + mUsedTreeInsertion(false), + mRemoveIndexSideEffectsInSubtree(false) +{ +} + +void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root) +{ + TIntermAggregate *rootAgg = root->getAsAggregate(); + ASSERT(rootAgg != nullptr && rootAgg->getOp() == EOpSequence); + TIntermSequence insertions; + for (TType type : mIndexedVecAndMatrixTypes) + { + insertions.push_back(GetIndexFunctionDefinition(type, false)); + } + for (TType type : mWrittenVecAndMatrixTypes) + { + insertions.push_back(GetIndexFunctionDefinition(type, true)); + } + mInsertions.push_back(NodeInsertMultipleEntry(rootAgg, 0, insertions, TIntermSequence())); +} + +// Create a call to dyn_index_*() based on an indirect indexing op node +TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node, + TIntermTyped *indexedNode, + TIntermTyped *index) +{ + ASSERT(node->getOp() == EOpIndexIndirect); + TIntermAggregate *indexingCall = new TIntermAggregate(EOpFunctionCall); + indexingCall->setLine(node->getLine()); + indexingCall->setUserDefined(); + indexingCall->setNameObj(GetIndexFunctionName(indexedNode->getType(), false)); + indexingCall->getSequence()->push_back(indexedNode); + indexingCall->getSequence()->push_back(index); + + TType fieldType = GetFieldType(indexedNode->getType()); + indexingCall->setType(fieldType); + return indexingCall; +} + +TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node, + TIntermTyped *index, + TIntermTyped *writtenValue) +{ + // Deep copy the left node so that two pointers to the same node don't end up in the tree. + TIntermNode *leftCopy = node->getLeft()->deepCopy(); + ASSERT(leftCopy != nullptr && leftCopy->getAsTyped() != nullptr); + TIntermAggregate *indexedWriteCall = + CreateIndexFunctionCall(node, leftCopy->getAsTyped(), index); + indexedWriteCall->setNameObj(GetIndexFunctionName(node->getLeft()->getType(), true)); + indexedWriteCall->setType(TType(EbtVoid)); + indexedWriteCall->getSequence()->push_back(writtenValue); + return indexedWriteCall; +} + +bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node) +{ + if (mUsedTreeInsertion) + return false; + + if (node->getOp() == EOpIndexIndirect) + { + if (mRemoveIndexSideEffectsInSubtree) + { + ASSERT(node->getRight()->hasSideEffects()); + // In case we're just removing index side effects, convert + // v_expr[index_expr] + // to this: + // int s0 = index_expr; v_expr[s0]; + // Now v_expr[s0] can be safely executed several times without unintended side effects. + + // Init the temp variable holding the index + TIntermAggregate *initIndex = createTempInitDeclaration(node->getRight()); + TIntermSequence insertions; + insertions.push_back(initIndex); + insertStatementsInParentBlock(insertions); + mUsedTreeInsertion = true; + + // Replace the index with the temp variable + TIntermSymbol *tempIndex = createTempSymbol(node->getRight()->getType()); + NodeUpdateEntry replaceIndex(node, node->getRight(), tempIndex, false); + mReplacements.push_back(replaceIndex); + } + else if (!node->getLeft()->isArray() && node->getLeft()->getBasicType() != EbtStruct) + { + bool write = isLValueRequiredHere(); + + TType type = node->getLeft()->getType(); + mIndexedVecAndMatrixTypes.insert(type); + + if (write) + { + // Convert: + // v_expr[index_expr]++; + // to this: + // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++; + // dyn_index_write(v_expr, s0, s1); + // This works even if index_expr has some side effects. + if (node->getLeft()->hasSideEffects()) + { + // If v_expr has side effects, those need to be removed before proceeding. + // Otherwise the side effects of v_expr would be evaluated twice. + // The only case where an l-value can have side effects is when it is + // indexing. For example, it can be V[j++] where V is an array of vectors. + mRemoveIndexSideEffectsInSubtree = true; + return true; + } + // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value + // only writes it and doesn't need the previous value. http://anglebug.com/1116 + + mWrittenVecAndMatrixTypes.insert(type); + TType fieldType = GetFieldType(type); + + TIntermSequence insertionsBefore; + TIntermSequence insertionsAfter; + + // Store the index in a temporary signed int variable. + TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight()); + TIntermAggregate *initIndex = createTempInitDeclaration(indexInitializer); + initIndex->setLine(node->getLine()); + insertionsBefore.push_back(initIndex); + + TIntermAggregate *indexingCall = CreateIndexFunctionCall( + node, node->getLeft(), createTempSymbol(indexInitializer->getType())); + + // Create a node for referring to the index after the nextTemporaryIndex() call + // below. + TIntermSymbol *tempIndex = createTempSymbol(indexInitializer->getType()); + + nextTemporaryIndex(); // From now on, creating temporary symbols that refer to the + // field value. + insertionsBefore.push_back(createTempInitDeclaration(indexingCall)); + + TIntermAggregate *indexedWriteCall = + CreateIndexedWriteFunctionCall(node, tempIndex, createTempSymbol(fieldType)); + insertionsAfter.push_back(indexedWriteCall); + insertStatementsInParentBlock(insertionsBefore, insertionsAfter); + NodeUpdateEntry replaceIndex(getParentNode(), node, createTempSymbol(fieldType), + false); + mReplacements.push_back(replaceIndex); + mUsedTreeInsertion = true; + } + else + { + // The indexed value is not being written, so we can simply convert + // v_expr[index_expr] + // into + // dyn_index(v_expr, index_expr) + // If the index_expr is unsigned, we'll convert it to signed. + ASSERT(!mRemoveIndexSideEffectsInSubtree); + TIntermAggregate *indexingCall = CreateIndexFunctionCall( + node, node->getLeft(), EnsureSignedInt(node->getRight())); + NodeUpdateEntry replaceIndex(getParentNode(), node, indexingCall, false); + mReplacements.push_back(replaceIndex); + } + } + } + return !mUsedTreeInsertion; +} + +void RemoveDynamicIndexingTraverser::nextIteration() +{ + mUsedTreeInsertion = false; + mRemoveIndexSideEffectsInSubtree = false; + nextTemporaryIndex(); +} + +} // namespace + +void RemoveDynamicIndexing(TIntermNode *root, + unsigned int *temporaryIndex, + const TSymbolTable &symbolTable, + int shaderVersion) +{ + RemoveDynamicIndexingTraverser traverser(symbolTable, shaderVersion); + ASSERT(temporaryIndex != nullptr); + traverser.useTemporaryIndex(temporaryIndex); + do + { + traverser.nextIteration(); + root->traverse(&traverser); + traverser.updateTree(); + } while (traverser.usedTreeInsertion()); + traverser.insertHelperDefinitions(root); + traverser.updateTree(); +} diff --git a/src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.h b/src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.h new file mode 100644 index 0000000000..ae3a93c9b1 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.h @@ -0,0 +1,21 @@ +// +// 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. +// +// RemoveDynamicIndexing is an AST traverser to remove dynamic indexing of vectors and matrices, +// replacing them with calls to functions that choose which component to return or write. +// + +#ifndef COMPILER_TRANSLATOR_REMOVEDYNAMICINDEXING_H_ +#define COMPILER_TRANSLATOR_REMOVEDYNAMICINDEXING_H_ + +class TIntermNode; +class TSymbolTable; + +void RemoveDynamicIndexing(TIntermNode *root, + unsigned int *temporaryIndex, + const TSymbolTable &symbolTable, + int shaderVersion); + +#endif // COMPILER_TRANSLATOR_REMOVEDYNAMICINDEXING_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/RemovePow.cpp b/src/3rdparty/angle/src/compiler/translator/RemovePow.cpp new file mode 100644 index 0000000000..6dbb48da9c --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/RemovePow.cpp @@ -0,0 +1,105 @@ +// +// 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. +// +// RemovePow is an AST traverser to convert pow(x, y) built-in calls where y is a +// constant to exp2(y * log2(x)). This works around an issue in NVIDIA 311 series +// OpenGL drivers. +// + +#include "compiler/translator/RemovePow.h" + +#include "compiler/translator/InfoSink.h" +#include "compiler/translator/IntermNode.h" + +namespace +{ + +bool IsProblematicPow(TIntermTyped *node) +{ + TIntermAggregate *agg = node->getAsAggregate(); + if (agg != nullptr && agg->getOp() == EOpPow) + { + ASSERT(agg->getSequence()->size() == 2); + return agg->getSequence()->at(1)->getAsConstantUnion() != nullptr; + } + return false; +} + +// Traverser that converts all pow operations simultaneously. +class RemovePowTraverser : public TIntermTraverser +{ + public: + RemovePowTraverser(); + + bool visitAggregate(Visit visit, TIntermAggregate *node) override; + + void nextIteration() { mNeedAnotherIteration = false; } + bool needAnotherIteration() const { return mNeedAnotherIteration; } + + protected: + bool mNeedAnotherIteration; +}; + +RemovePowTraverser::RemovePowTraverser() + : TIntermTraverser(true, false, false), + mNeedAnotherIteration(false) +{ +} + +bool RemovePowTraverser::visitAggregate(Visit visit, TIntermAggregate *node) +{ + if (IsProblematicPow(node)) + { + TInfoSink nullSink; + + TIntermTyped *x = node->getSequence()->at(0)->getAsTyped(); + TIntermTyped *y = node->getSequence()->at(1)->getAsTyped(); + + TIntermUnary *log = new TIntermUnary(EOpLog2); + log->setOperand(x); + log->setLine(node->getLine()); + log->setType(x->getType()); + + TIntermBinary *mul = new TIntermBinary(EOpMul); + mul->setLeft(y); + mul->setRight(log); + mul->setLine(node->getLine()); + bool valid = mul->promote(nullSink); + UNUSED_ASSERTION_VARIABLE(valid); + ASSERT(valid); + + TIntermUnary *exp = new TIntermUnary(EOpExp2); + exp->setOperand(mul); + exp->setLine(node->getLine()); + exp->setType(node->getType()); + + NodeUpdateEntry replacePow(getParentNode(), node, exp, false); + mReplacements.push_back(replacePow); + + // If the x parameter also needs to be replaced, we need to do that in another traversal, + // since it's parent node will change in a way that's not handled correctly by updateTree(). + if (IsProblematicPow(x)) + { + mNeedAnotherIteration = true; + return false; + } + } + return true; +} + +} // namespace + +void RemovePow(TIntermNode *root) +{ + RemovePowTraverser traverser; + // Iterate as necessary, and reset the traverser between iterations. + do + { + traverser.nextIteration(); + root->traverse(&traverser); + traverser.updateTree(); + } + while (traverser.needAnotherIteration()); +} diff --git a/src/3rdparty/angle/src/compiler/translator/RemovePow.h b/src/3rdparty/angle/src/compiler/translator/RemovePow.h new file mode 100644 index 0000000000..40f9d672b2 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/RemovePow.h @@ -0,0 +1,18 @@ +// +// 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. +// +// RemovePow is an AST traverser to convert pow(x, y) built-in calls where y is a +// constant to exp2(y * log2(x)). This works around an issue in NVIDIA 311 series +// OpenGL drivers. +// + +#ifndef COMPILER_TRANSLATOR_REMOVEPOW_H_ +#define COMPILER_TRANSLATOR_REMOVEPOW_H_ + +class TIntermNode; + +void RemovePow(TIntermNode *root); + +#endif // COMPILER_TRANSLATOR_REMOVEPOW_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/RenameFunction.h b/src/3rdparty/angle/src/compiler/translator/RenameFunction.h index 868e5d566b..fd6a365fea 100644 --- a/src/3rdparty/angle/src/compiler/translator/RenameFunction.h +++ b/src/3rdparty/angle/src/compiler/translator/RenameFunction.h @@ -20,7 +20,7 @@ public: , mOldFunctionName(oldFunctionName) , mNewFunctionName(newFunctionName) {} - virtual bool visitAggregate(Visit visit, TIntermAggregate* node) + bool visitAggregate(Visit visit, TIntermAggregate *node) override { TOperator op = node->getOp(); if ((op == EOpFunction || op == EOpFunctionCall) && node->getName() == mOldFunctionName) diff --git a/src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.cpp b/src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.cpp new file mode 100644 index 0000000000..8347447546 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.cpp @@ -0,0 +1,163 @@ +// +// 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. +// + +// RewriteDoWhile.cpp: rewrites do-while loops using another equivalent +// construct. + +#include "compiler/translator/RewriteDoWhile.h" + +#include "compiler/translator/IntermNode.h" + +namespace +{ + +// An AST traverser that rewrites loops of the form +// do { +// CODE; +// } while (CONDITION) +// +// to loops of the form +// bool temp = false; +// while (true) { +// if (temp) { +// if (!CONDITION) { +// break; +// } +// } +// temp = true; +// CODE; +// } +// +// The reason we don't use a simpler form, with for example just (temp && !CONDITION) in the +// while condition, is that short-circuit is often badly supported by driver shader compiler. +// The double if has the same effect, but forces shader compilers to behave. +// +// TODO(cwallez) when UnfoldShortCircuitIntoIf handles loops correctly, revisit this as we might +// be able to use while (temp || CONDITION) with temp initially set to true then run +// UnfoldShortCircuitIntoIf +class DoWhileRewriter : public TIntermTraverser +{ + public: + DoWhileRewriter() : TIntermTraverser(true, false, false) {} + + bool visitAggregate(Visit, TIntermAggregate *node) override + { + // A well-formed AST can only have do-while in EOpSequence which represent lists of + // statements. By doing a prefix traversal we are able to replace the do-while in the + // sequence directly as the content of the do-while will be traversed later. + if (node->getOp() != EOpSequence) + { + return true; + } + + TIntermSequence *statements = node->getSequence(); + + // The statements vector will have new statements inserted when we encounter a do-while, + // which prevents us from using a range-based for loop. Using the usual i++ works, as + // the (two) new statements inserted replace the statement at the current position. + for (size_t i = 0; i < statements->size(); i++) + { + TIntermNode *statement = (*statements)[i]; + TIntermLoop *loop = statement->getAsLoopNode(); + + if (loop == nullptr || loop->getType() != ELoopDoWhile) + { + continue; + } + + TType boolType = TType(EbtBool); + + // bool temp = false; + TIntermAggregate *tempDeclaration = nullptr; + { + TConstantUnion *falseConstant = new TConstantUnion(); + falseConstant->setBConst(false); + TIntermTyped *falseValue = new TIntermConstantUnion(falseConstant, boolType); + + tempDeclaration = createTempInitDeclaration(falseValue); + } + + // temp = true; + TIntermBinary *assignTrue = nullptr; + { + TConstantUnion *trueConstant = new TConstantUnion(); + trueConstant->setBConst(true); + TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, boolType); + + assignTrue = createTempAssignment(trueValue); + } + + // if (temp) { + // if (!CONDITION) { + // break; + // } + // } + TIntermSelection *breakIf = nullptr; + { + TIntermBranch *breakStatement = new TIntermBranch(EOpBreak, nullptr); + + TIntermAggregate *breakBlock = new TIntermAggregate(EOpSequence); + breakBlock->getSequence()->push_back(breakStatement); + + TIntermUnary *negatedCondition = new TIntermUnary(EOpLogicalNot); + negatedCondition->setOperand(loop->getCondition()); + + TIntermSelection *innerIf = + new TIntermSelection(negatedCondition, breakBlock, nullptr); + + TIntermAggregate *innerIfBlock = new TIntermAggregate(EOpSequence); + innerIfBlock->getSequence()->push_back(innerIf); + + breakIf = new TIntermSelection(createTempSymbol(boolType), innerIfBlock, nullptr); + } + + // Assemble the replacement loops, reusing the do-while loop's body and inserting our + // statements at the front. + TIntermLoop *newLoop = nullptr; + { + TConstantUnion *trueConstant = new TConstantUnion(); + trueConstant->setBConst(true); + TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, boolType); + + TIntermAggregate *body = nullptr; + if (loop->getBody() != nullptr) + { + body = loop->getBody()->getAsAggregate(); + } + else + { + body = new TIntermAggregate(EOpSequence); + } + auto sequence = body->getSequence(); + sequence->insert(sequence->begin(), assignTrue); + sequence->insert(sequence->begin(), breakIf); + + newLoop = new TIntermLoop(ELoopWhile, nullptr, trueValue, nullptr, body); + } + + TIntermSequence replacement; + replacement.push_back(tempDeclaration); + replacement.push_back(newLoop); + + node->replaceChildNodeWithMultiple(loop, replacement); + + nextTemporaryIndex(); + } + return true; + } +}; + +} // anonymous namespace + +void RewriteDoWhile(TIntermNode *root, unsigned int *temporaryIndex) +{ + ASSERT(temporaryIndex != 0); + + DoWhileRewriter rewriter; + rewriter.useTemporaryIndex(temporaryIndex); + + root->traverse(&rewriter); +} diff --git a/src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.h b/src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.h new file mode 100644 index 0000000000..f6ec1caf06 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.h @@ -0,0 +1,16 @@ +// +// 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. +// + +// RewriteDoWhile.h: rewrite do-while loops as while loops to work around +// driver bugs + +#ifndef COMPILER_TRANSLATOR_REWRITEDOWHILE_H_ +#define COMPILER_TRANSLATOR_REWRITEDOWHILE_H_ + +class TIntermNode; +void RewriteDoWhile(TIntermNode *root, unsigned int *temporaryIndex); + +#endif // COMPILER_TRANSLATOR_REWRITEDOWHILE_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.cpp b/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.cpp index b03beb5c6c..52ede17434 100644 --- a/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.cpp +++ b/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.cpp @@ -23,30 +23,14 @@ class ElseBlockRewriter : public TIntermTraverser ElseBlockRewriter(); protected: - bool visitAggregate(Visit visit, TIntermAggregate *aggregate); + bool visitAggregate(Visit visit, TIntermAggregate *aggregate) override; private: - int mTemporaryIndex; const TType *mFunctionType; TIntermNode *rewriteSelection(TIntermSelection *selection); }; -TIntermSymbol *MakeNewTemporary(const TString &name, TBasicType type) -{ - TType variableType(type, EbpHigh, EvqInternal); - return new TIntermSymbol(-1, name, variableType); -} - -TIntermBinary *MakeNewBinary(TOperator op, TIntermTyped *left, TIntermTyped *right, const TType &resultType) -{ - TIntermBinary *binary = new TIntermBinary(op); - binary->setLeft(left); - binary->setRight(right); - binary->setType(resultType); - return binary; -} - TIntermUnary *MakeNewUnary(TOperator op, TIntermTyped *operand) { TIntermUnary *unary = new TIntermUnary(op, operand->getType()); @@ -55,8 +39,7 @@ TIntermUnary *MakeNewUnary(TOperator op, TIntermTyped *operand) } ElseBlockRewriter::ElseBlockRewriter() - : TIntermTraverser(true, false, true, false), - mTemporaryIndex(0), + : TIntermTraverser(true, false, true), mFunctionType(NULL) {} @@ -71,7 +54,7 @@ bool ElseBlockRewriter::visitAggregate(Visit visit, TIntermAggregate *node) { TIntermNode *statement = (*node->getSequence())[statementIndex]; TIntermSelection *selection = statement->getAsSelectionNode(); - if (selection && selection->getFalseBlock() != NULL) + if (selection && selection->getFalseBlock() != nullptr) { // Check for if / else if TIntermSelection *elseIfBranch = selection->getFalseBlock()->getAsSelectionNode(); @@ -101,20 +84,20 @@ bool ElseBlockRewriter::visitAggregate(Visit visit, TIntermAggregate *node) TIntermNode *ElseBlockRewriter::rewriteSelection(TIntermSelection *selection) { - ASSERT(selection != NULL); + ASSERT(selection != nullptr); + + nextTemporaryIndex(); - TString temporaryName = "cond_" + str(mTemporaryIndex++); TIntermTyped *typedCondition = selection->getCondition()->getAsTyped(); - TType resultType(EbtBool, EbpUndefined); - TIntermSymbol *conditionSymbolInit = MakeNewTemporary(temporaryName, EbtBool); - TIntermBinary *storeCondition = MakeNewBinary(EOpInitialize, conditionSymbolInit, - typedCondition, resultType); - TIntermNode *negatedElse = NULL; + TIntermAggregate *storeCondition = createTempInitDeclaration(typedCondition); - TIntermSelection *falseBlock = NULL; + TIntermSelection *falseBlock = nullptr; + + TType boolType(EbtBool, EbpUndefined, EvqTemporary); if (selection->getFalseBlock()) { + TIntermAggregate *negatedElse = nullptr; // crbug.com/346463 // D3D generates error messages claiming a function has no return value, when rewriting // an if-else clause that returns something non-void in a function. By appending dummy @@ -124,24 +107,22 @@ TIntermNode *ElseBlockRewriter::rewriteSelection(TIntermSelection *selection) TString typeString = mFunctionType->getStruct() ? mFunctionType->getStruct()->name() : mFunctionType->getBasicString(); TString rawText = "return (" + typeString + ")0"; - negatedElse = new TIntermRaw(*mFunctionType, rawText); + TIntermRaw *returnNode = new TIntermRaw(*mFunctionType, rawText); + negatedElse = new TIntermAggregate(EOpSequence); + negatedElse->getSequence()->push_back(returnNode); } - TIntermSymbol *conditionSymbolElse = MakeNewTemporary(temporaryName, EbtBool); + TIntermSymbol *conditionSymbolElse = createTempSymbol(boolType); TIntermUnary *negatedCondition = MakeNewUnary(EOpLogicalNot, conditionSymbolElse); falseBlock = new TIntermSelection(negatedCondition, selection->getFalseBlock(), negatedElse); } - TIntermSymbol *conditionSymbolSel = MakeNewTemporary(temporaryName, EbtBool); - TIntermSelection *newSelection = new TIntermSelection(conditionSymbolSel, - selection->getTrueBlock(), falseBlock); - - TIntermAggregate *declaration = new TIntermAggregate(EOpDeclaration); - declaration->getSequence()->push_back(storeCondition); + TIntermSymbol *conditionSymbolSel = createTempSymbol(boolType); + TIntermSelection *newSelection = new TIntermSelection(conditionSymbolSel, selection->getTrueBlock(), falseBlock); TIntermAggregate *block = new TIntermAggregate(EOpSequence); - block->getSequence()->push_back(declaration); + block->getSequence()->push_back(storeCondition); block->getSequence()->push_back(newSelection); return block; @@ -149,9 +130,10 @@ TIntermNode *ElseBlockRewriter::rewriteSelection(TIntermSelection *selection) } -void RewriteElseBlocks(TIntermNode *node) +void RewriteElseBlocks(TIntermNode *node, unsigned int *temporaryIndex) { ElseBlockRewriter rewriter; + rewriter.useTemporaryIndex(temporaryIndex); node->traverse(&rewriter); } diff --git a/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.h b/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.h index 5527d27f83..24a425e66d 100644 --- a/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.h +++ b/src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.h @@ -15,7 +15,7 @@ namespace sh { -void RewriteElseBlocks(TIntermNode *node); +void RewriteElseBlocks(TIntermNode *node, unsigned int *temporaryIndex); } diff --git a/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp b/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp index 8857ad59bd..775c5d8710 100644 --- a/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp +++ b/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp @@ -4,8 +4,8 @@ // found in the LICENSE file. // +#include "common/debug.h" #include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h" -#include "compiler/translator/compilerdebug.h" #include @@ -39,7 +39,7 @@ bool ContainsVectorNode(const TIntermSequence &sequence) TIntermConstantUnion *ConstructIndexNode(int index) { - ConstantUnion *u = new ConstantUnion[1]; + TConstantUnion *u = new TConstantUnion[1]; u[0].setIConst(index); TType type(EbtInt, EbpUndefined, EvqConst, 1); @@ -109,7 +109,13 @@ bool ScalarizeVecAndMatConstructorArgs::visitAggregate(Visit visit, TIntermAggre scalarizeArgs(node, false, true); break; case EOpConstructMat2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: case EOpConstructMat3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: case EOpConstructMat4: if (ContainsVectorNode(*(node->getSequence()))) scalarizeArgs(node, true, false); @@ -144,9 +150,21 @@ void ScalarizeVecAndMatConstructorArgs::scalarizeArgs( case EOpConstructMat2: size = 4; break; + case EOpConstructMat2x3: + case EOpConstructMat3x2: + size = 6; + break; + case EOpConstructMat2x4: + case EOpConstructMat4x2: + size = 8; + break; case EOpConstructMat3: size = 9; break; + case EOpConstructMat3x4: + case EOpConstructMat4x3: + size = 12; + break; case EOpConstructMat4: size = 16; break; diff --git a/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h b/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h index 0726ed4c64..d7553be23b 100644 --- a/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h +++ b/src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h @@ -14,12 +14,13 @@ class ScalarizeVecAndMatConstructorArgs : public TIntermTraverser public: ScalarizeVecAndMatConstructorArgs(sh::GLenum shaderType, bool fragmentPrecisionHigh) - : mTempVarCount(0), + : TIntermTraverser(true, false, false), + mTempVarCount(0), mShaderType(shaderType), mFragmentPrecisionHigh(fragmentPrecisionHigh) {} protected: - virtual bool visitAggregate(Visit visit, TIntermAggregate *node); + bool visitAggregate(Visit visit, TIntermAggregate *node) override; private: void scalarizeArgs(TIntermAggregate *aggregate, diff --git a/src/3rdparty/angle/src/compiler/translator/SearchSymbol.cpp b/src/3rdparty/angle/src/compiler/translator/SearchSymbol.cpp index fb7a6cdb9b..cccd4d3ff0 100644 --- a/src/3rdparty/angle/src/compiler/translator/SearchSymbol.cpp +++ b/src/3rdparty/angle/src/compiler/translator/SearchSymbol.cpp @@ -12,7 +12,9 @@ namespace sh { -SearchSymbol::SearchSymbol(const TString &symbol) : mSymbol(symbol) +SearchSymbol::SearchSymbol(const TString &symbol) + : TIntermTraverser(true, false, false), + mSymbol(symbol) { match = false; } diff --git a/src/3rdparty/angle/src/compiler/translator/SearchSymbol.h b/src/3rdparty/angle/src/compiler/translator/SearchSymbol.h index 36d5191058..1e5e1700d1 100644 --- a/src/3rdparty/angle/src/compiler/translator/SearchSymbol.h +++ b/src/3rdparty/angle/src/compiler/translator/SearchSymbol.h @@ -20,7 +20,7 @@ class SearchSymbol : public TIntermTraverser SearchSymbol(const TString &symbol); void traverse(TIntermNode *node); - void visitSymbol(TIntermSymbol *symbolNode); + void visitSymbol(TIntermSymbol *symbolNode) override; bool foundMatch() const; diff --git a/src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.cpp b/src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.cpp new file mode 100644 index 0000000000..de9050cd80 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.cpp @@ -0,0 +1,92 @@ +// +// 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 SeparateArrayInitialization function splits each array initialization into a declaration and an assignment. +// Example: +// type[n] a = initializer; +// will effectively become +// type[n] a; +// a = initializer; +// +// Note that if the array is declared as const, the initialization may still be split, making the +// AST technically invalid. Because of that this transformation should only be used when subsequent +// stages don't care about const qualifiers. However, the initialization will not be split if the +// initializer can be written as a HLSL literal. + +#include "compiler/translator/SeparateArrayInitialization.h" + +#include "compiler/translator/IntermNode.h" +#include "compiler/translator/OutputHLSL.h" + +namespace +{ + +class SeparateArrayInitTraverser : private TIntermTraverser +{ + public: + static void apply(TIntermNode *root); + private: + SeparateArrayInitTraverser(); + bool visitAggregate(Visit, TIntermAggregate *node) override; +}; + +void SeparateArrayInitTraverser::apply(TIntermNode *root) +{ + SeparateArrayInitTraverser separateInit; + root->traverse(&separateInit); + separateInit.updateTree(); +} + +SeparateArrayInitTraverser::SeparateArrayInitTraverser() + : TIntermTraverser(true, false, false) +{ +} + +bool SeparateArrayInitTraverser::visitAggregate(Visit, TIntermAggregate *node) +{ + if (node->getOp() == EOpDeclaration) + { + TIntermSequence *sequence = node->getSequence(); + TIntermBinary *initNode = sequence->back()->getAsBinaryNode(); + if (initNode != nullptr && initNode->getOp() == EOpInitialize) + { + TIntermTyped *initializer = initNode->getRight(); + if (initializer->isArray() && !sh::OutputHLSL::canWriteAsHLSLLiteral(initializer)) + { + // We rely on that array declarations have been isolated to single declarations. + ASSERT(sequence->size() == 1); + TIntermTyped *symbol = initNode->getLeft(); + TIntermAggregate *parentAgg = getParentNode()->getAsAggregate(); + ASSERT(parentAgg != nullptr); + + TIntermSequence replacements; + + TIntermAggregate *replacementDeclaration = new TIntermAggregate; + replacementDeclaration->setOp(EOpDeclaration); + replacementDeclaration->getSequence()->push_back(symbol); + replacementDeclaration->setLine(symbol->getLine()); + replacements.push_back(replacementDeclaration); + + TIntermBinary *replacementAssignment = new TIntermBinary(EOpAssign); + replacementAssignment->setLeft(symbol); + replacementAssignment->setRight(initializer); + replacementAssignment->setType(initializer->getType()); + replacementAssignment->setLine(symbol->getLine()); + replacements.push_back(replacementAssignment); + + mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(parentAgg, node, replacements)); + } + } + return false; + } + return true; +} + +} // namespace + +void SeparateArrayInitialization(TIntermNode *root) +{ + SeparateArrayInitTraverser::apply(root); +} diff --git a/src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.h b/src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.h new file mode 100644 index 0000000000..d16357a3af --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.h @@ -0,0 +1,25 @@ +// +// 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 SeparateArrayInitialization function splits each array initialization into a declaration and an assignment. +// Example: +// type[n] a = initializer; +// will effectively become +// type[n] a; +// a = initializer; +// +// Note that if the array is declared as const, the initialization may still be split, making the +// AST technically invalid. Because of that this transformation should only be used when subsequent +// stages don't care about const qualifiers. However, the initialization will not be split if the +// initializer can be written as a HLSL literal. + +#ifndef COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_ +#define COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_ + +class TIntermNode; + +void SeparateArrayInitialization(TIntermNode *root); + +#endif // COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.cpp b/src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.cpp new file mode 100644 index 0000000000..d33747f85b --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.cpp @@ -0,0 +1,77 @@ +// +// 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 SeparateDeclarations function processes declarations, so that in the end each declaration +// contains only one declarator. +// This is useful as an intermediate step when initialization needs to be separated from declaration, +// or when things need to be unfolded out of the initializer. +// Example: +// int a[1] = int[1](1), b[1] = int[1](2); +// gets transformed when run through this class into the AST equivalent of: +// int a[1] = int[1](1); +// int b[1] = int[1](2); + +#include "compiler/translator/SeparateDeclarations.h" + +#include "compiler/translator/IntermNode.h" + +namespace +{ + +class SeparateDeclarationsTraverser : private TIntermTraverser +{ + public: + static void apply(TIntermNode *root); + private: + SeparateDeclarationsTraverser(); + bool visitAggregate(Visit, TIntermAggregate *node) override; +}; + +void SeparateDeclarationsTraverser::apply(TIntermNode *root) +{ + SeparateDeclarationsTraverser separateDecl; + root->traverse(&separateDecl); + separateDecl.updateTree(); +} + +SeparateDeclarationsTraverser::SeparateDeclarationsTraverser() + : TIntermTraverser(true, false, false) +{ +} + +bool SeparateDeclarationsTraverser::visitAggregate(Visit, TIntermAggregate *node) +{ + if (node->getOp() == EOpDeclaration) + { + TIntermSequence *sequence = node->getSequence(); + if (sequence->size() > 1) + { + TIntermAggregate *parentAgg = getParentNode()->getAsAggregate(); + ASSERT(parentAgg != nullptr); + + TIntermSequence replacementDeclarations; + for (size_t ii = 0; ii < sequence->size(); ++ii) + { + TIntermAggregate *replacementDeclaration = new TIntermAggregate; + + replacementDeclaration->setOp(EOpDeclaration); + replacementDeclaration->getSequence()->push_back(sequence->at(ii)); + replacementDeclaration->setLine(sequence->at(ii)->getLine()); + replacementDeclarations.push_back(replacementDeclaration); + } + + mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(parentAgg, node, replacementDeclarations)); + } + return false; + } + return true; +} + +} // namespace + +void SeparateDeclarations(TIntermNode *root) +{ + SeparateDeclarationsTraverser::apply(root); +} diff --git a/src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.h b/src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.h new file mode 100644 index 0000000000..77913ab8bb --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.h @@ -0,0 +1,23 @@ +// +// 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 SeparateDeclarations function processes declarations, so that in the end each declaration +// contains only one declarator. +// This is useful as an intermediate step when initialization needs to be separated from declaration, +// or when things need to be unfolded out of the initializer. +// Example: +// int a[1] = int[1](1), b[1] = int[1](2); +// gets transformed when run through this class into the AST equivalent of: +// int a[1] = int[1](1); +// int b[1] = int[1](2); + +#ifndef COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_ +#define COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_ + +class TIntermNode; + +void SeparateDeclarations(TIntermNode *root); + +#endif // COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp b/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp new file mode 100644 index 0000000000..e8e1a21d9c --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp @@ -0,0 +1,169 @@ +// +// 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. +// +// SeparateExpressionsReturningArrays splits array-returning expressions that are not array names from more complex +// expressions, assigning them to a temporary variable a#. +// Examples where a, b and c are all arrays: +// (a = b) == (a = c) is split into a = b; type[n] a1 = a; a = c; type[n] a2 = a; a1 == a2; +// type d = type[n](...)[i]; is split into type[n] a1 = type[n](...); type d = a1[i]; + +#include "compiler/translator/SeparateExpressionsReturningArrays.h" + +#include "compiler/translator/IntermNode.h" + +namespace +{ + +// Traverser that separates one array expression into a statement at a time. +class SeparateExpressionsTraverser : public TIntermTraverser +{ + public: + SeparateExpressionsTraverser(); + + bool visitBinary(Visit visit, TIntermBinary *node) override; + bool visitAggregate(Visit visit, TIntermAggregate *node) override; + + void nextIteration(); + bool foundArrayExpression() const { return mFoundArrayExpression; } + + protected: + // Marked to true once an operation that needs to be hoisted out of the expression has been found. + // After that, no more AST updates are performed on that traversal. + bool mFoundArrayExpression; +}; + +SeparateExpressionsTraverser::SeparateExpressionsTraverser() + : TIntermTraverser(true, false, false), + mFoundArrayExpression(false) +{ +} + +// Performs a shallow copy of an assignment node. +// These shallow copies are useful when a node gets inserted into an aggregate node +// and also needs to be replaced in its original location by a different node. +TIntermBinary *CopyAssignmentNode(TIntermBinary *node) +{ + TIntermBinary *copyNode = new TIntermBinary(node->getOp()); + copyNode->setLeft(node->getLeft()); + copyNode->setRight(node->getRight()); + copyNode->setType(node->getType()); + return copyNode; +} + +// Performs a shallow copy of a constructor/function call node. +TIntermAggregate *CopyAggregateNode(TIntermAggregate *node) +{ + TIntermAggregate *copyNode = new TIntermAggregate(node->getOp()); + TIntermSequence *copySeq = copyNode->getSequence(); + copySeq->insert(copySeq->begin(), node->getSequence()->begin(), node->getSequence()->end()); + copyNode->setType(node->getType()); + copyNode->setFunctionId(node->getFunctionId()); + if (node->isUserDefined()) + { + copyNode->setUserDefined(); + } + copyNode->setNameObj(node->getNameObj()); + return copyNode; +} + +bool SeparateExpressionsTraverser::visitBinary(Visit visit, TIntermBinary *node) +{ + if (mFoundArrayExpression) + return false; + + // Early return if the expression is not an array or if we're not inside a complex expression. + if (!node->getType().isArray() || parentNodeIsBlock()) + return true; + + switch (node->getOp()) + { + case EOpAssign: + { + mFoundArrayExpression = true; + + TIntermSequence insertions; + insertions.push_back(CopyAssignmentNode(node)); + // TODO(oetuaho): In some cases it would be more optimal to not add the temporary node, but just use the + // original target of the assignment. Care must be taken so that this doesn't happen when the same array + // symbol is a target of assignment more than once in one expression. + insertions.push_back(createTempInitDeclaration(node->getLeft())); + insertStatementsInParentBlock(insertions); + + NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(node->getType()), false); + mReplacements.push_back(replaceVariable); + } + return false; + default: + return true; + } +} + +bool SeparateExpressionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node) +{ + if (mFoundArrayExpression) + return false; // No need to traverse further + + if (getParentNode() != nullptr) + { + TIntermBinary *parentBinary = getParentNode()->getAsBinaryNode(); + bool parentIsAssignment = (parentBinary != nullptr && + (parentBinary->getOp() == EOpAssign || parentBinary->getOp() == EOpInitialize)); + + if (!node->getType().isArray() || parentNodeIsBlock() || parentIsAssignment) + return true; + + if (node->isConstructor()) + { + mFoundArrayExpression = true; + + TIntermSequence insertions; + insertions.push_back(createTempInitDeclaration(CopyAggregateNode(node))); + insertStatementsInParentBlock(insertions); + + NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(node->getType()), false); + mReplacements.push_back(replaceVariable); + + return false; + } + else if (node->getOp() == EOpFunctionCall) + { + mFoundArrayExpression = true; + + TIntermSequence insertions; + insertions.push_back(createTempInitDeclaration(CopyAggregateNode(node))); + insertStatementsInParentBlock(insertions); + + NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(node->getType()), false); + mReplacements.push_back(replaceVariable); + + return false; + } + } + return true; +} + +void SeparateExpressionsTraverser::nextIteration() +{ + mFoundArrayExpression = false; + nextTemporaryIndex(); +} + +} // namespace + +void SeparateExpressionsReturningArrays(TIntermNode *root, unsigned int *temporaryIndex) +{ + SeparateExpressionsTraverser traverser; + ASSERT(temporaryIndex != nullptr); + traverser.useTemporaryIndex(temporaryIndex); + // Separate one expression at a time, and reset the traverser between iterations. + do + { + traverser.nextIteration(); + root->traverse(&traverser); + if (traverser.foundArrayExpression()) + traverser.updateTree(); + } + while (traverser.foundArrayExpression()); +} diff --git a/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.h b/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.h new file mode 100644 index 0000000000..b178ebb3ec --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.h @@ -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. +// +// SeparateExpressionsReturningArrays splits array-returning expressions that are not array names from more complex +// expressions, assigning them to a temporary variable a#. +// Examples where a, b and c are all arrays: +// (a = b) == (a = c) is split into a = b; type[n] a1 = a; a = c; type[n] a2 = a; a1 == a2; +// type d = type[n](...)[i]; is split into type[n] a1 = type[n](...); type d = a1[i]; + +#ifndef COMPILER_TRANSLATOR_SEPARATEEXPRESSIONSRETURNINGARRAYS_H_ +#define COMPILER_TRANSLATOR_SEPARATEEXPRESSIONSRETURNINGARRAYS_H_ + +class TIntermNode; + +void SeparateExpressionsReturningArrays(TIntermNode *root, unsigned int *temporaryIndex); + +#endif // COMPILER_TRANSLATOR_SEPARATEEXPRESSIONSRETURNINGARRAYS_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/ShaderLang.cpp b/src/3rdparty/angle/src/compiler/translator/ShaderLang.cpp index b8040da7f9..e257f93e48 100644 --- a/src/3rdparty/angle/src/compiler/translator/ShaderLang.cpp +++ b/src/3rdparty/angle/src/compiler/translator/ShaderLang.cpp @@ -23,15 +23,6 @@ namespace { -enum ShaderVariableType -{ - SHADERVAR_UNIFORM, - SHADERVAR_VARYING, - SHADERVAR_ATTRIBUTE, - SHADERVAR_OUTPUTVARIABLE, - SHADERVAR_INTERFACEBLOCK -}; - bool isInitialized = false; // @@ -40,36 +31,40 @@ bool isInitialized = false; // template -const std::vector *GetVariableList(const TCompiler *compiler, ShaderVariableType variableType); +const std::vector *GetVariableList(const TCompiler *compiler); template <> -const std::vector *GetVariableList(const TCompiler *compiler, ShaderVariableType) +const std::vector *GetVariableList(const TCompiler *compiler) { return &compiler->getUniforms(); } template <> -const std::vector *GetVariableList(const TCompiler *compiler, ShaderVariableType) +const std::vector *GetVariableList(const TCompiler *compiler) { return &compiler->getVaryings(); } template <> -const std::vector *GetVariableList(const TCompiler *compiler, ShaderVariableType variableType) +const std::vector *GetVariableList(const TCompiler *compiler) +{ + return &compiler->getAttributes(); +} + +template <> +const std::vector *GetVariableList(const TCompiler *compiler) { - return (variableType == SHADERVAR_ATTRIBUTE ? - &compiler->getAttributes() : - &compiler->getOutputVariables()); + return &compiler->getOutputVariables(); } template <> -const std::vector *GetVariableList(const TCompiler *compiler, ShaderVariableType) +const std::vector *GetVariableList(const TCompiler *compiler) { return &compiler->getInterfaceBlocks(); } template -const std::vector *GetShaderVariables(const ShHandle handle, ShaderVariableType variableType) +const std::vector *GetShaderVariables(const ShHandle handle) { if (!handle) { @@ -83,7 +78,7 @@ const std::vector *GetShaderVariables(const ShHandle handle, ShaderVariabl return NULL; } - return GetVariableList(compiler, variableType); + return GetVariableList(compiler); } TCompiler *GetCompilerFromHandle(ShHandle handle) @@ -104,7 +99,7 @@ TranslatorHLSL *GetTranslatorHLSLFromHandle(ShHandle handle) } #endif // ANGLE_ENABLE_HLSL -} // namespace anonymous +} // anonymous namespace // // Driver must call this first, once, before doing any other compiler operations. @@ -154,6 +149,7 @@ void ShInitBuiltInResources(ShBuiltInResources* resources) resources->OES_standard_derivatives = 0; resources->OES_EGL_image_external = 0; resources->ARB_texture_rectangle = 0; + resources->EXT_blend_func_extended = 0; resources->EXT_draw_buffers = 0; resources->EXT_frag_depth = 0; resources->EXT_shader_texture_lod = 0; @@ -173,6 +169,9 @@ void ShInitBuiltInResources(ShBuiltInResources* resources) resources->MinProgramTexelOffset = -8; resources->MaxProgramTexelOffset = 7; + // Extensions constants. + resources->MaxDualSourceDrawBuffers = 0; + // Disable name hashing by default. resources->HashFunction = NULL; @@ -190,9 +189,16 @@ ShHandle ShConstructCompiler(sh::GLenum type, ShShaderSpec spec, const ShBuiltInResources* resources) { TShHandleBase* base = static_cast(ConstructCompiler(type, spec, output)); + if (base == nullptr) + { + return 0; + } + TCompiler* compiler = base->getAsCompiler(); - if (compiler == 0) + if (compiler == nullptr) + { return 0; + } // Generate built-in symbol table. if (!compiler->Init(*resources)) { @@ -240,6 +246,13 @@ bool ShCompile( return compiler->compile(shaderStrings, numStrings, compileOptions); } +void ShClearResults(const ShHandle handle) +{ + TCompiler *compiler = GetCompilerFromHandle(handle); + ASSERT(compiler); + compiler->clearResults(); +} + int ShGetShaderVersion(const ShHandle handle) { TCompiler* compiler = GetCompilerFromHandle(handle); @@ -288,27 +301,27 @@ const std::map *ShGetNameHashingMap( const std::vector *ShGetUniforms(const ShHandle handle) { - return GetShaderVariables(handle, SHADERVAR_UNIFORM); + return GetShaderVariables(handle); } const std::vector *ShGetVaryings(const ShHandle handle) { - return GetShaderVariables(handle, SHADERVAR_VARYING); + return GetShaderVariables(handle); } const std::vector *ShGetAttributes(const ShHandle handle) { - return GetShaderVariables(handle, SHADERVAR_ATTRIBUTE); + return GetShaderVariables(handle); } -const std::vector *ShGetOutputVariables(const ShHandle handle) +const std::vector *ShGetOutputVariables(const ShHandle handle) { - return GetShaderVariables(handle, SHADERVAR_OUTPUTVARIABLE); + return GetShaderVariables(handle); } const std::vector *ShGetInterfaceBlocks(const ShHandle handle) { - return GetShaderVariables(handle, SHADERVAR_INTERFACEBLOCK); + return GetShaderVariables(handle); } bool ShCheckVariablesWithinPackingLimits( diff --git a/src/3rdparty/angle/src/compiler/translator/ShaderVars.cpp b/src/3rdparty/angle/src/compiler/translator/ShaderVars.cpp index 0dbbc9e7f6..8f931b9bdd 100644 --- a/src/3rdparty/angle/src/compiler/translator/ShaderVars.cpp +++ b/src/3rdparty/angle/src/compiler/translator/ShaderVars.cpp @@ -9,7 +9,7 @@ #include -#include "compiler/translator/compilerdebug.h" +#include "common/debug.h" namespace sh { @@ -217,31 +217,75 @@ bool Uniform::isSameUniformAtLinkTime(const Uniform &other) const return ShaderVariable::isSameVariableAtLinkTime(other, true); } -Attribute::Attribute() - : location(-1) +InterfaceVariable::InterfaceVariable() : location(-1) {} -Attribute::~Attribute() +InterfaceVariable::~InterfaceVariable() {} -Attribute::Attribute(const Attribute &other) - : ShaderVariable(other), - location(other.location) +InterfaceVariable::InterfaceVariable(const InterfaceVariable &other) + : ShaderVariable(other), location(other.location) {} -Attribute &Attribute::operator=(const Attribute &other) +InterfaceVariable &InterfaceVariable::operator=(const InterfaceVariable &other) { ShaderVariable::operator=(other); location = other.location; return *this; } -bool Attribute::operator==(const Attribute &other) const +bool InterfaceVariable::operator==(const InterfaceVariable &other) const { return (ShaderVariable::operator==(other) && location == other.location); } +Attribute::Attribute() +{ +} + +Attribute::~Attribute() +{ +} + +Attribute::Attribute(const Attribute &other) : InterfaceVariable(other) +{ +} + +Attribute &Attribute::operator=(const Attribute &other) +{ + InterfaceVariable::operator=(other); + return *this; +} + +bool Attribute::operator==(const Attribute &other) const +{ + return InterfaceVariable::operator==(other); +} + +OutputVariable::OutputVariable() +{ +} + +OutputVariable::~OutputVariable() +{ +} + +OutputVariable::OutputVariable(const OutputVariable &other) : InterfaceVariable(other) +{ +} + +OutputVariable &OutputVariable::operator=(const OutputVariable &other) +{ + InterfaceVariable::operator=(other); + return *this; +} + +bool OutputVariable::operator==(const OutputVariable &other) const +{ + return InterfaceVariable::operator==(other); +} + InterfaceBlockField::InterfaceBlockField() : isRowMajorLayout(false) {} @@ -304,10 +348,15 @@ bool Varying::operator==(const Varying &other) const } bool Varying::isSameVaryingAtLinkTime(const Varying &other) const +{ + return isSameVaryingAtLinkTime(other, 100); +} + +bool Varying::isSameVaryingAtLinkTime(const Varying &other, int shaderVersion) const { return (ShaderVariable::isSameVariableAtLinkTime(other, false) && interpolation == other.interpolation && - isInvariant == other.isInvariant); + (shaderVersion >= 300 || isInvariant == other.isInvariant)); } InterfaceBlock::InterfaceBlock() @@ -344,4 +393,9 @@ InterfaceBlock &InterfaceBlock::operator=(const InterfaceBlock &other) return *this; } +std::string InterfaceBlock::fieldPrefix() const +{ + return instanceName.empty() ? "" : name; } + +} // namespace sh diff --git a/src/3rdparty/angle/src/compiler/translator/SymbolTable.cpp b/src/3rdparty/angle/src/compiler/translator/SymbolTable.cpp index 0eb663f018..dc8f8e3b6b 100644 --- a/src/3rdparty/angle/src/compiler/translator/SymbolTable.cpp +++ b/src/3rdparty/angle/src/compiler/translator/SymbolTable.cpp @@ -14,6 +14,7 @@ #endif #include "compiler/translator/SymbolTable.h" +#include "compiler/translator/Cache.h" #include #include @@ -29,6 +30,18 @@ TFunction::~TFunction() delete (*i).type; } +const TString *TFunction::buildMangledName() const +{ + std::string newName = mangleName(getName()).c_str(); + + for (const auto &p : parameters) + { + newName += p.type->getMangledName().c_str(); + } + + return NewPoolTString(newName.c_str()); +} + // // Symbol table levels are a map of pointers to symbols that have to be deleted. // @@ -139,7 +152,7 @@ bool IsVecType(const TType *type) return false; } -TType *SpecificType(TType *type, int size) +const TType *SpecificType(const TType *type, int size) { ASSERT(size >= 1 && size <= 4); @@ -152,15 +165,15 @@ TType *SpecificType(TType *type, int size) switch(type->getBasicType()) { - case EbtGenType: return new TType(EbtFloat, size); - case EbtGenIType: return new TType(EbtInt, size); - case EbtGenUType: return new TType(EbtUInt, size); - case EbtGenBType: return new TType(EbtBool, size); + case EbtGenType: return TCache::getType(EbtFloat, static_cast(size)); + case EbtGenIType: return TCache::getType(EbtInt, static_cast(size)); + case EbtGenUType: return TCache::getType(EbtUInt, static_cast(size)); + case EbtGenBType: return TCache::getType(EbtBool, static_cast(size)); default: return type; } } -TType *VectorType(TType *type, int size) +const TType *VectorType(const TType *type, int size) { ASSERT(size >= 2 && size <= 4); @@ -173,44 +186,44 @@ TType *VectorType(TType *type, int size) switch(type->getBasicType()) { - case EbtVec: return new TType(EbtFloat, size); - case EbtIVec: return new TType(EbtInt, size); - case EbtUVec: return new TType(EbtUInt, size); - case EbtBVec: return new TType(EbtBool, size); + case EbtVec: return TCache::getType(EbtFloat, static_cast(size)); + case EbtIVec: return TCache::getType(EbtInt, static_cast(size)); + case EbtUVec: return TCache::getType(EbtUInt, static_cast(size)); + case EbtBVec: return TCache::getType(EbtBool, static_cast(size)); default: return type; } } -void TSymbolTable::insertBuiltIn(ESymbolLevel level, TOperator op, const char *ext, TType *rvalue, const char *name, - TType *ptype1, TType *ptype2, TType *ptype3, TType *ptype4, TType *ptype5) +void TSymbolTable::insertBuiltIn(ESymbolLevel level, TOperator op, const char *ext, const TType *rvalue, const char *name, + const TType *ptype1, const TType *ptype2, const TType *ptype3, const TType *ptype4, const TType *ptype5) { if (ptype1->getBasicType() == EbtGSampler2D) { bool gvec4 = (rvalue->getBasicType() == EbtGVec4); - insertBuiltIn(level, gvec4 ? new TType(EbtFloat, 4) : rvalue, name, new TType(EbtSampler2D), ptype2, ptype3, ptype4, ptype5); - insertBuiltIn(level, gvec4 ? new TType(EbtInt, 4) : rvalue, name, new TType(EbtISampler2D), ptype2, ptype3, ptype4, ptype5); - insertBuiltIn(level, gvec4 ? new TType(EbtUInt, 4) : rvalue, name, new TType(EbtUSampler2D), ptype2, ptype3, ptype4, ptype5); + insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name, TCache::getType(EbtSampler2D), ptype2, ptype3, ptype4, ptype5); + insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name, TCache::getType(EbtISampler2D), ptype2, ptype3, ptype4, ptype5); + insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name, TCache::getType(EbtUSampler2D), ptype2, ptype3, ptype4, ptype5); } else if (ptype1->getBasicType() == EbtGSampler3D) { bool gvec4 = (rvalue->getBasicType() == EbtGVec4); - insertBuiltIn(level, gvec4 ? new TType(EbtFloat, 4) : rvalue, name, new TType(EbtSampler3D), ptype2, ptype3, ptype4, ptype5); - insertBuiltIn(level, gvec4 ? new TType(EbtInt, 4) : rvalue, name, new TType(EbtISampler3D), ptype2, ptype3, ptype4, ptype5); - insertBuiltIn(level, gvec4 ? new TType(EbtUInt, 4) : rvalue, name, new TType(EbtUSampler3D), ptype2, ptype3, ptype4, ptype5); + insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name, TCache::getType(EbtSampler3D), ptype2, ptype3, ptype4, ptype5); + insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name, TCache::getType(EbtISampler3D), ptype2, ptype3, ptype4, ptype5); + insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name, TCache::getType(EbtUSampler3D), ptype2, ptype3, ptype4, ptype5); } else if (ptype1->getBasicType() == EbtGSamplerCube) { bool gvec4 = (rvalue->getBasicType() == EbtGVec4); - insertBuiltIn(level, gvec4 ? new TType(EbtFloat, 4) : rvalue, name, new TType(EbtSamplerCube), ptype2, ptype3, ptype4, ptype5); - insertBuiltIn(level, gvec4 ? new TType(EbtInt, 4) : rvalue, name, new TType(EbtISamplerCube), ptype2, ptype3, ptype4, ptype5); - insertBuiltIn(level, gvec4 ? new TType(EbtUInt, 4) : rvalue, name, new TType(EbtUSamplerCube), ptype2, ptype3, ptype4, ptype5); + insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name, TCache::getType(EbtSamplerCube), ptype2, ptype3, ptype4, ptype5); + insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name, TCache::getType(EbtISamplerCube), ptype2, ptype3, ptype4, ptype5); + insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name, TCache::getType(EbtUSamplerCube), ptype2, ptype3, ptype4, ptype5); } else if (ptype1->getBasicType() == EbtGSampler2DArray) { bool gvec4 = (rvalue->getBasicType() == EbtGVec4); - insertBuiltIn(level, gvec4 ? new TType(EbtFloat, 4) : rvalue, name, new TType(EbtSampler2DArray), ptype2, ptype3, ptype4, ptype5); - insertBuiltIn(level, gvec4 ? new TType(EbtInt, 4) : rvalue, name, new TType(EbtISampler2DArray), ptype2, ptype3, ptype4, ptype5); - insertBuiltIn(level, gvec4 ? new TType(EbtUInt, 4) : rvalue, name, new TType(EbtUSampler2DArray), ptype2, ptype3, ptype4, ptype5); + insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name, TCache::getType(EbtSampler2DArray), ptype2, ptype3, ptype4, ptype5); + insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name, TCache::getType(EbtISampler2DArray), ptype2, ptype3, ptype4, ptype5); + insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name, TCache::getType(EbtUSampler2DArray), ptype2, ptype3, ptype4, ptype5); } else if (IsGenType(rvalue) || IsGenType(ptype1) || IsGenType(ptype2) || IsGenType(ptype3)) { @@ -229,33 +242,28 @@ void TSymbolTable::insertBuiltIn(ESymbolLevel level, TOperator op, const char *e } else { - TFunction *function = new TFunction(NewPoolTString(name), *rvalue, op, ext); + TFunction *function = new TFunction(NewPoolTString(name), rvalue, op, ext); - TParameter param1 = {0, ptype1}; - function->addParameter(param1); + function->addParameter(TConstParameter(ptype1)); if (ptype2) { - TParameter param2 = {0, ptype2}; - function->addParameter(param2); + function->addParameter(TConstParameter(ptype2)); } if (ptype3) { - TParameter param3 = {0, ptype3}; - function->addParameter(param3); + function->addParameter(TConstParameter(ptype3)); } if (ptype4) { - TParameter param4 = {0, ptype4}; - function->addParameter(param4); + function->addParameter(TConstParameter(ptype4)); } if (ptype5) { - TParameter param5 = {0, ptype5}; - function->addParameter(param5); + function->addParameter(TConstParameter(ptype5)); } insert(level, function); @@ -272,7 +280,7 @@ TPrecision TSymbolTable::getDefaultPrecision(TBasicType type) const int level = static_cast(precisionStack.size()) - 1; assert(level >= 0); // Just to be safe. Should not happen. - // If we dont find anything we return this. Should we error check this? + // If we dont find anything we return this. Some types don't have predefined default precision. TPrecision prec = EbpUndefined; while (level >= 0) { diff --git a/src/3rdparty/angle/src/compiler/translator/SymbolTable.h b/src/3rdparty/angle/src/compiler/translator/SymbolTable.h index dfc65cb957..2706149e3c 100644 --- a/src/3rdparty/angle/src/compiler/translator/SymbolTable.h +++ b/src/3rdparty/angle/src/compiler/translator/SymbolTable.h @@ -109,13 +109,8 @@ class TVariable : public TSymbol unionArray(0) { } - virtual ~TVariable() - { - } - virtual bool isVariable() const - { - return true; - } + ~TVariable() override {} + bool isVariable() const override { return true; } TType &getType() { return type; @@ -133,40 +128,67 @@ class TVariable : public TSymbol type.setQualifier(qualifier); } - ConstantUnion *getConstPointer() - { - if (!unionArray) - unionArray = new ConstantUnion[type.getObjectSize()]; + const TConstantUnion *getConstPointer() const { return unionArray; } - return unionArray; - } - - ConstantUnion *getConstPointer() const - { - return unionArray; - } - - void shareConstPointer(ConstantUnion *constArray) - { - if (unionArray == constArray) - return; - - delete[] unionArray; - unionArray = constArray; - } + void shareConstPointer(const TConstantUnion *constArray) { unionArray = constArray; } private: TType type; bool userType; // we are assuming that Pool Allocator will free the memory // allocated to unionArray when this object is destroyed. - ConstantUnion *unionArray; + const TConstantUnion *unionArray; +}; + +// Immutable version of TParameter. +struct TConstParameter +{ + TConstParameter() + : name(nullptr), + type(nullptr) + { + } + explicit TConstParameter(const TString *n) + : name(n), + type(nullptr) + { + } + explicit TConstParameter(const TType *t) + : name(nullptr), + type(t) + { + } + TConstParameter(const TString *n, const TType *t) + : name(n), + type(t) + { + } + + // Both constructor arguments must be const. + TConstParameter(TString *n, TType *t) = delete; + TConstParameter(const TString *n, TType *t) = delete; + TConstParameter(TString *n, const TType *t) = delete; + + const TString *name; + const TType *type; }; // The function sub-class of symbols and the parser will need to // share this definition of a function parameter. struct TParameter { + // Destructively converts to TConstParameter. + // This method resets name and type to nullptrs to make sure + // their content cannot be modified after the call. + TConstParameter turnToConst() + { + const TString *constName = name; + const TType *constType = type; + name = nullptr; + type = nullptr; + return TConstParameter(constName, constType); + } + TString *name; TType *type; }; @@ -175,27 +197,21 @@ struct TParameter class TFunction : public TSymbol { public: - TFunction(TOperator o) - : TSymbol(0), - returnType(TType(EbtVoid, EbpUndefined)), - op(o), - defined(false) - { - } - TFunction(const TString *name, const TType &retType, TOperator tOp = EOpNull, const char *ext = "") + TFunction(const TString *name, + const TType *retType, + TOperator tOp = EOpNull, + const char *ext = "") : TSymbol(name), returnType(retType), - mangledName(TFunction::mangleName(*name)), + mangledName(nullptr), op(tOp), - defined(false) + defined(false), + mHasPrototypeDeclaration(false) { relateToExtension(ext); } - virtual ~TFunction(); - virtual bool isFunction() const - { - return true; - } + ~TFunction() override; + bool isFunction() const override { return true; } static TString mangleName(const TString &name) { @@ -206,19 +222,23 @@ class TFunction : public TSymbol return TString(mangledName.c_str(), mangledName.find_first_of('(')); } - void addParameter(TParameter &p) - { + void addParameter(const TConstParameter &p) + { parameters.push_back(p); - mangledName = mangledName + p.type->getMangledName(); + mangledName = nullptr; } - const TString &getMangledName() const + const TString &getMangledName() const override { - return mangledName; + if (mangledName == nullptr) + { + mangledName = buildMangledName(); + } + return *mangledName; } const TType &getReturnType() const { - return returnType; + return *returnType; } TOperator getBuiltInOp() const @@ -226,31 +246,30 @@ class TFunction : public TSymbol return op; } - void setDefined() - { - defined = true; - } - bool isDefined() - { - return defined; - } + void setDefined() { defined = true; } + bool isDefined() { return defined; } + void setHasPrototypeDeclaration() { mHasPrototypeDeclaration = true; } + bool hasPrototypeDeclaration() const { return mHasPrototypeDeclaration; } size_t getParamCount() const { return parameters.size(); } - const TParameter &getParam(size_t i) const + const TConstParameter &getParam(size_t i) const { return parameters[i]; } private: - typedef TVector TParamList; + const TString *buildMangledName() const; + + typedef TVector TParamList; TParamList parameters; - TType returnType; - TString mangledName; + const TType *returnType; + mutable const TString *mangledName; TOperator op; bool defined; + bool mHasPrototypeDeclaration; }; // Interface block name sub-symbol @@ -364,27 +383,39 @@ class TSymbolTable : angle::NonCopyable { TVariable *constant = new TVariable( NewPoolTString(name), TType(EbtInt, EbpUndefined, EvqConst, 1)); - constant->getConstPointer()->setIConst(value); + TConstantUnion *unionArray = new TConstantUnion[1]; + unionArray[0].setIConst(value); + constant->shareConstPointer(unionArray); return insert(level, constant); } - void insertBuiltIn(ESymbolLevel level, TOperator op, const char *ext, TType *rvalue, const char *name, - TType *ptype1, TType *ptype2 = 0, TType *ptype3 = 0, TType *ptype4 = 0, TType *ptype5 = 0); + bool insertConstIntExt(ESymbolLevel level, const char *ext, const char *name, int value) + { + TVariable *constant = + new TVariable(NewPoolTString(name), TType(EbtInt, EbpUndefined, EvqConst, 1)); + TConstantUnion *unionArray = new TConstantUnion[1]; + unionArray[0].setIConst(value); + constant->shareConstPointer(unionArray); + return insert(level, ext, constant); + } + + void insertBuiltIn(ESymbolLevel level, TOperator op, const char *ext, const TType *rvalue, const char *name, + const TType *ptype1, const TType *ptype2 = 0, const TType *ptype3 = 0, const TType *ptype4 = 0, const TType *ptype5 = 0); - void insertBuiltIn(ESymbolLevel level, TType *rvalue, const char *name, - TType *ptype1, TType *ptype2 = 0, TType *ptype3 = 0, TType *ptype4 = 0, TType *ptype5 = 0) + void insertBuiltIn(ESymbolLevel level, const TType *rvalue, const char *name, + const TType *ptype1, const TType *ptype2 = 0, const TType *ptype3 = 0, const TType *ptype4 = 0, const TType *ptype5 = 0) { insertBuiltIn(level, EOpNull, "", rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5); } - void insertBuiltIn(ESymbolLevel level, const char *ext, TType *rvalue, const char *name, - TType *ptype1, TType *ptype2 = 0, TType *ptype3 = 0, TType *ptype4 = 0, TType *ptype5 = 0) + void insertBuiltIn(ESymbolLevel level, const char *ext, const TType *rvalue, const char *name, + const TType *ptype1, const TType *ptype2 = 0, const TType *ptype3 = 0, const TType *ptype4 = 0, const TType *ptype5 = 0) { insertBuiltIn(level, EOpNull, ext, rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5); } - void insertBuiltIn(ESymbolLevel level, TOperator op, TType *rvalue, const char *name, - TType *ptype1, TType *ptype2 = 0, TType *ptype3 = 0, TType *ptype4 = 0, TType *ptype5 = 0) + void insertBuiltIn(ESymbolLevel level, TOperator op, const TType *rvalue, const char *name, + const TType *ptype1, const TType *ptype2 = 0, const TType *ptype3 = 0, const TType *ptype4 = 0, const TType *ptype5 = 0) { insertBuiltIn(level, op, "", rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5); } @@ -405,6 +436,8 @@ class TSymbolTable : angle::NonCopyable { if (!SupportsPrecision(type.type)) return false; + if (type.type == EbtUInt) + return false; // ESSL 3.00.4 section 4.5.4 if (type.isAggregate()) return false; // Not allowed to set for aggregate types int indexOfLastElement = static_cast(precisionStack.size()) - 1; diff --git a/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.cpp b/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.cpp index 238bc97576..76d006fd11 100644 --- a/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.cpp @@ -8,6 +8,7 @@ #include "compiler/translator/BuiltInFunctionEmulatorGLSL.h" #include "compiler/translator/EmulatePrecision.h" +#include "compiler/translator/RecordConstantPrecision.h" #include "compiler/translator/OutputESSL.h" #include "angle_gl.h" @@ -19,16 +20,18 @@ TranslatorESSL::TranslatorESSL(sh::GLenum type, ShShaderSpec spec) void TranslatorESSL::initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, int compileOptions) { if (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS) - InitBuiltInFunctionEmulatorForGLSL(emu, getShaderType()); + { + InitBuiltInFunctionEmulatorForGLSLWorkarounds(emu, getShaderType()); + } } void TranslatorESSL::translate(TIntermNode *root, int) { TInfoSinkBase& sink = getInfoSink().obj; - int shaderVersion = getShaderVersion(); - if (shaderVersion > 100) + int shaderVer = getShaderVersion(); + if (shaderVer > 100) { - sink << "#version " << shaderVersion << " es\n"; + sink << "#version " << shaderVer << " es\n"; } writePragma(); @@ -40,12 +43,14 @@ void TranslatorESSL::translate(TIntermNode *root, int) { if (precisionEmulation) { - EmulatePrecision emulatePrecision; + EmulatePrecision emulatePrecision(getSymbolTable(), shaderVer); root->traverse(&emulatePrecision); emulatePrecision.updateTree(); emulatePrecision.writeEmulationHelpers(sink, SH_ESSL_OUTPUT); } + RecordConstantPrecision(root, getTemporaryIndex()); + // Write emulated built-in functions if needed. if (!getBuiltInFunctionEmulator().IsOutputEmpty()) { @@ -71,13 +76,8 @@ void TranslatorESSL::translate(TIntermNode *root, int) { getArrayBoundsClamper().OutputClampingFunctionDefinition(sink); // Write translated shader. - TOutputESSL outputESSL(sink, - getArrayIndexClampingStrategy(), - getHashFunction(), - getNameMap(), - getSymbolTable(), - shaderVersion, - precisionEmulation); + TOutputESSL outputESSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(), + getSymbolTable(), shaderVer, precisionEmulation); root->traverse(&outputESSL); } diff --git a/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.h b/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.h index 89a3e473e9..2cc61074d4 100644 --- a/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.h +++ b/src/3rdparty/angle/src/compiler/translator/TranslatorESSL.h @@ -17,7 +17,7 @@ class TranslatorESSL : public TCompiler protected: void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, int compileOptions) override; - virtual void translate(TIntermNode *root, int compileOptions); + void translate(TIntermNode *root, int compileOptions) override; private: void writeExtensionBehavior(); diff --git a/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.cpp b/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.cpp index aea3f77c4e..05e9eb310b 100644 --- a/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.cpp @@ -9,53 +9,10 @@ #include "angle_gl.h" #include "compiler/translator/BuiltInFunctionEmulatorGLSL.h" #include "compiler/translator/EmulatePrecision.h" +#include "compiler/translator/ExtensionGLSL.h" #include "compiler/translator/OutputGLSL.h" #include "compiler/translator/VersionGLSL.h" -namespace -{ - -// To search for what output variables are used in a fragment shader. -// We handle gl_FragColor and gl_FragData at the moment. -class TFragmentOutSearcher : public TIntermTraverser -{ - public: - TFragmentOutSearcher() - : mUsesGlFragColor(false), - mUsesGlFragData(false) - { - } - - bool usesGlFragColor() const - { - return mUsesGlFragColor; - } - - bool usesGlFragData() const - { - return mUsesGlFragData; - } - - protected: - virtual void visitSymbol(TIntermSymbol *node) override - { - if (node->getSymbol() == "gl_FragColor") - { - mUsesGlFragColor = true; - } - else if (node->getSymbol() == "gl_FragData") - { - mUsesGlFragData = true; - } - } - - private: - bool mUsesGlFragColor; - bool mUsesGlFragData; -}; - -} // namespace anonymous - TranslatorGLSL::TranslatorGLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output) @@ -65,10 +22,16 @@ TranslatorGLSL::TranslatorGLSL(sh::GLenum type, void TranslatorGLSL::initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, int compileOptions) { if (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS) - InitBuiltInFunctionEmulatorForGLSL(emu, getShaderType()); + { + InitBuiltInFunctionEmulatorForGLSLWorkarounds(emu, getShaderType()); + } + + int targetGLSLVersion = ShaderOutputTypeToGLSLVersion(getOutputType()); + InitBuiltInFunctionEmulatorForGLSLMissingFunctions(emu, getShaderType(), targetGLSLVersion); } -void TranslatorGLSL::translate(TIntermNode *root, int) { +void TranslatorGLSL::translate(TIntermNode *root, int compileOptions) +{ TInfoSinkBase& sink = getInfoSink().obj; // Write GLSL version. @@ -77,13 +40,13 @@ void TranslatorGLSL::translate(TIntermNode *root, int) { writePragma(); // Write extension behaviour as needed - writeExtensionBehavior(); + writeExtensionBehavior(root); bool precisionEmulation = getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision; if (precisionEmulation) { - EmulatePrecision emulatePrecision; + EmulatePrecision emulatePrecision(getSymbolTable(), getShaderVersion()); root->traverse(&emulatePrecision); emulatePrecision.updateTree(); emulatePrecision.writeEmulationHelpers(sink, getOutputType()); @@ -103,20 +66,70 @@ void TranslatorGLSL::translate(TIntermNode *root, int) { // Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData // if it's core profile shaders and they are used. - if (getShaderType() == GL_FRAGMENT_SHADER && - getOutputType() == SH_GLSL_CORE_OUTPUT) + if (getShaderType() == GL_FRAGMENT_SHADER) { - TFragmentOutSearcher searcher; - root->traverse(&searcher); - ASSERT(!(searcher.usesGlFragData() && searcher.usesGlFragColor())); - if (searcher.usesGlFragColor()) + const bool mayHaveESSL1SecondaryOutputs = + IsExtensionEnabled(getExtensionBehavior(), "GL_EXT_blend_func_extended") && + getShaderVersion() == 100; + const bool declareGLFragmentOutputs = IsGLSL130OrNewer(getOutputType()); + + bool hasGLFragColor = false; + bool hasGLFragData = false; + bool hasGLSecondaryFragColor = false; + bool hasGLSecondaryFragData = false; + + for (const auto &outputVar : outputVariables) + { + if (declareGLFragmentOutputs) + { + if (outputVar.name == "gl_FragColor") + { + ASSERT(!hasGLFragColor); + hasGLFragColor = true; + continue; + } + else if (outputVar.name == "gl_FragData") + { + ASSERT(!hasGLFragData); + hasGLFragData = true; + continue; + } + } + if (mayHaveESSL1SecondaryOutputs) + { + if (outputVar.name == "gl_SecondaryFragColorEXT") + { + ASSERT(!hasGLSecondaryFragColor); + hasGLSecondaryFragColor = true; + continue; + } + else if (outputVar.name == "gl_SecondaryFragDataEXT") + { + ASSERT(!hasGLSecondaryFragData); + hasGLSecondaryFragData = true; + continue; + } + } + } + ASSERT(!((hasGLFragColor || hasGLSecondaryFragColor) && + (hasGLFragData || hasGLSecondaryFragData))); + if (hasGLFragColor) { sink << "out vec4 webgl_FragColor;\n"; } - if (searcher.usesGlFragData()) + if (hasGLFragData) { sink << "out vec4 webgl_FragData[gl_MaxDrawBuffers];\n"; } + if (hasGLSecondaryFragColor) + { + sink << "out vec4 angle_SecondaryFragColor;\n"; + } + if (hasGLSecondaryFragData) + { + sink << "out vec4 angle_SecondaryFragData[" << getResources().MaxDualSourceDrawBuffers + << "];\n"; + } } // Write translated shader. @@ -144,19 +157,41 @@ void TranslatorGLSL::writeVersion(TIntermNode *root) } } -void TranslatorGLSL::writeExtensionBehavior() { +void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root) +{ TInfoSinkBase& sink = getInfoSink().obj; const TExtensionBehavior& extBehavior = getExtensionBehavior(); - for (TExtensionBehavior::const_iterator iter = extBehavior.begin(); - iter != extBehavior.end(); ++iter) { - if (iter->second == EBhUndefined) + for (const auto &iter : extBehavior) + { + if (iter.second == EBhUndefined) + { continue; + } // For GLSL output, we don't need to emit most extensions explicitly, // but some we need to translate. - if (iter->first == "GL_EXT_shader_texture_lod") { - sink << "#extension GL_ARB_shader_texture_lod : " - << getBehaviorString(iter->second) << "\n"; + if (iter.first == "GL_EXT_shader_texture_lod") + { + sink << "#extension GL_ARB_shader_texture_lod : " << getBehaviorString(iter.second) + << "\n"; } } + + // GLSL ES 3 explicit location qualifiers need to use an extension before GLSL 330 + if (getShaderVersion() >= 300 && getOutputType() < SH_GLSL_330_CORE_OUTPUT) + { + sink << "#extension GL_ARB_explicit_attrib_location : require\n"; + } + + TExtensionGLSL extensionGLSL(getOutputType()); + root->traverse(&extensionGLSL); + + for (const auto &ext : extensionGLSL.getEnabledExtensions()) + { + sink << "#extension " << ext << " : enable\n"; + } + for (const auto &ext : extensionGLSL.getRequiredExtensions()) + { + sink << "#extension " << ext << " : require\n"; + } } diff --git a/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.h b/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.h index 4a5a641096..4f07b21980 100644 --- a/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.h +++ b/src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.h @@ -17,11 +17,11 @@ class TranslatorGLSL : public TCompiler protected: void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, int compileOptions) override; - virtual void translate(TIntermNode *root, int compileOptions); + void translate(TIntermNode *root, int compileOptions) override; private: void writeVersion(TIntermNode *root); - void writeExtensionBehavior(); + void writeExtensionBehavior(TIntermNode *root); }; #endif // COMPILER_TRANSLATOR_TRANSLATORGLSL_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.cpp index f6275defa1..c5d18d21bf 100644 --- a/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.cpp @@ -6,8 +6,14 @@ #include "compiler/translator/TranslatorHLSL.h" +#include "compiler/translator/ArrayReturnValueToOutParameter.h" #include "compiler/translator/OutputHLSL.h" -#include "compiler/translator/SimplifyArrayAssignment.h" +#include "compiler/translator/RemoveDynamicIndexing.h" +#include "compiler/translator/RewriteElseBlocks.h" +#include "compiler/translator/SeparateArrayInitialization.h" +#include "compiler/translator/SeparateDeclarations.h" +#include "compiler/translator/SeparateExpressionsReturningArrays.h" +#include "compiler/translator/UnfoldShortCircuitToIf.h" TranslatorHLSL::TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output) : TCompiler(type, spec, output) @@ -19,8 +25,32 @@ void TranslatorHLSL::translate(TIntermNode *root, int compileOptions) const ShBuiltInResources &resources = getResources(); int numRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1; - SimplifyArrayAssignment simplify; - root->traverse(&simplify); + SeparateDeclarations(root); + + // Note that SeparateDeclarations needs to be run before UnfoldShortCircuitToIf. + UnfoldShortCircuitToIf(root, getTemporaryIndex()); + + SeparateExpressionsReturningArrays(root, getTemporaryIndex()); + + // Note that SeparateDeclarations needs to be run before SeparateArrayInitialization. + SeparateArrayInitialization(root); + + // HLSL doesn't support arrays as return values, we'll need to make functions that have an array + // as a return value to use an out parameter to transfer the array data instead. + ArrayReturnValueToOutParameter(root, getTemporaryIndex()); + + if (!shouldRunLoopAndIndexingValidation(compileOptions)) + { + // HLSL doesn't support dynamic indexing of vectors and matrices. + RemoveDynamicIndexing(root, getTemporaryIndex(), getSymbolTable(), getShaderVersion()); + } + + // Work around D3D9 bug that would manifest in vertex shaders with selection blocks which + // use a vertex attribute as a condition, and some related computation in the else block. + if (getOutputType() == SH_HLSL_3_0_OUTPUT && getShaderType() == GL_VERTEX_SHADER) + { + sh::RewriteElseBlocks(root, getTemporaryIndex()); + } sh::OutputHLSL outputHLSL(getShaderType(), getShaderVersion(), getExtensionBehavior(), getSourcePath(), getOutputType(), numRenderTargets, getUniforms(), compileOptions); diff --git a/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.h b/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.h index 1920ed5755..907d816744 100644 --- a/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.h +++ b/src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.h @@ -13,7 +13,7 @@ class TranslatorHLSL : public TCompiler { public: TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output); - virtual TranslatorHLSL *getAsTranslatorHLSL() { return this; } + TranslatorHLSL *getAsTranslatorHLSL() override { return this; } bool hasInterfaceBlock(const std::string &interfaceBlockName) const; unsigned int getInterfaceBlockRegister(const std::string &interfaceBlockName) const; @@ -22,7 +22,10 @@ class TranslatorHLSL : public TCompiler unsigned int getUniformRegister(const std::string &uniformName) const; protected: - virtual void translate(TIntermNode *root, int compileOptions); + void translate(TIntermNode *root, int compileOptions) override; + + // collectVariables needs to be run always so registers can be assigned. + bool shouldCollectVariables(int compileOptions) override { return true; } std::map mInterfaceBlockRegisterMap; std::map mUniformRegisterMap; diff --git a/src/3rdparty/angle/src/compiler/translator/Types.cpp b/src/3rdparty/angle/src/compiler/translator/Types.cpp index b970bf5ac4..87fdfe0d54 100644 --- a/src/3rdparty/angle/src/compiler/translator/Types.cpp +++ b/src/3rdparty/angle/src/compiler/translator/Types.cpp @@ -46,9 +46,9 @@ const char* getBasicString(TBasicType t) } TType::TType(const TPublicType &p) - : type(p.type), precision(p.precision), qualifier(p.qualifier), layoutQualifier(p.layoutQualifier), - primarySize(p.primarySize), secondarySize(p.secondarySize), array(p.array), arraySize(p.arraySize), - interfaceBlock(0), structure(0) + : type(p.type), precision(p.precision), qualifier(p.qualifier), invariant(p.invariant), + layoutQualifier(p.layoutQualifier), primarySize(p.primarySize), secondarySize(p.secondarySize), + array(p.array), arraySize(p.arraySize), interfaceBlock(0), structure(0) { if (p.userDef) structure = p.userDef->getStruct(); @@ -59,6 +59,27 @@ bool TStructure::equals(const TStructure &other) const return (uniqueId() == other.uniqueId()); } +TString TType::getCompleteString() const +{ + TStringStream stream; + + if (invariant) + stream << "invariant "; + if (qualifier != EvqTemporary && qualifier != EvqGlobal) + stream << getQualifierString() << " "; + if (precision != EbpUndefined) + stream << getPrecisionString() << " "; + if (array) + stream << "array[" << getArraySize() << "] of "; + if (isMatrix()) + stream << getCols() << "X" << getRows() << " matrix of "; + else if (isVector()) + stream << getNominalSize() << "-component vector of "; + + stream << getBasicString(); + return stream.str(); +} + // // Recursively generate mangled names. // @@ -142,7 +163,8 @@ TString TType::buildMangledName() const mangledName += interfaceBlock->mangledName(); break; default: - UNREACHABLE(); + // EbtVoid, EbtAddress and non types + break; } if (isMatrix()) @@ -200,6 +222,17 @@ bool TStructure::containsArrays() const return false; } +bool TStructure::containsType(TBasicType type) const +{ + for (size_t i = 0; i < mFields->size(); ++i) + { + const TType *fieldType = (*mFields)[i]->type(); + if (fieldType->getBasicType() == type || fieldType->isStructureContainingType(type)) + return true; + } + return false; +} + bool TStructure::containsSamplers() const { for (size_t i = 0; i < mFields->size(); ++i) @@ -211,9 +244,9 @@ bool TStructure::containsSamplers() const return false; } -TString TFieldListCollection::buildMangledName() const +TString TFieldListCollection::buildMangledName(const TString &mangledNamePrefix) const { - TString mangledName(mangledNamePrefix()); + TString mangledName(mangledNamePrefix); mangledName += *mName; for (size_t i = 0; i < mFields->size(); ++i) { diff --git a/src/3rdparty/angle/src/compiler/translator/Types.h b/src/3rdparty/angle/src/compiler/translator/Types.h index 044f22c3c1..c2968dceba 100644 --- a/src/3rdparty/angle/src/compiler/translator/Types.h +++ b/src/3rdparty/angle/src/compiler/translator/Types.h @@ -8,10 +8,10 @@ #define COMPILER_TRANSLATOR_TYPES_H_ #include "common/angleutils.h" +#include "common/debug.h" #include "compiler/translator/BaseTypes.h" #include "compiler/translator/Common.h" -#include "compiler/translator/compilerdebug.h" struct TPublicType; class TType; @@ -73,12 +73,6 @@ class TFieldListCollection : angle::NonCopyable return *mFields; } - const TString &mangledName() const - { - if (mMangledName.empty()) - mMangledName = buildMangledName(); - return mMangledName; - } size_t objectSize() const { if (mObjectSize == 0) @@ -93,9 +87,8 @@ class TFieldListCollection : angle::NonCopyable mObjectSize(0) { } - TString buildMangledName() const; + TString buildMangledName(const TString &mangledNamePrefix) const; size_t calculateObjectSize() const; - virtual TString mangledNamePrefix() const = 0; const TString *mName; TFieldList *mFields; @@ -124,6 +117,7 @@ class TStructure : public TFieldListCollection return mDeepestNesting; } bool containsArrays() const; + bool containsType(TBasicType t) const; bool containsSamplers() const; bool equals(const TStructure &other) const; @@ -149,6 +143,13 @@ class TStructure : public TFieldListCollection return mAtGlobalScope; } + const TString &mangledName() const + { + if (mMangledName.empty()) + mMangledName = buildMangledName("struct-"); + return mMangledName; + } + private: // TODO(zmo): Find a way to get rid of the const_cast in function // setName(). At the moment keep this function private so only @@ -160,10 +161,6 @@ class TStructure : public TFieldListCollection *mutableName = name; } - virtual TString mangledNamePrefix() const - { - return "struct-"; - } int calculateDeepestNesting() const; mutable int mDeepestNesting; @@ -209,13 +206,14 @@ class TInterfaceBlock : public TFieldListCollection { return mMatrixPacking; } - - private: - virtual TString mangledNamePrefix() const + const TString &mangledName() const { - return "iblock-"; + if (mMangledName.empty()) + mMangledName = buildMangledName("iblock-"); + return mMangledName; } + private: const TString *mInstanceName; // for interface block instance names int mArraySize; // 0 if not an array TLayoutBlockStorage mBlockStorage; @@ -230,10 +228,14 @@ class TType public: POOL_ALLOCATOR_NEW_DELETE(); TType() + : type(EbtVoid), precision(EbpUndefined), qualifier(EvqGlobal), invariant(false), + layoutQualifier(TLayoutQualifier::create()), + primarySize(0), secondarySize(0), array(false), arraySize(0), + interfaceBlock(nullptr), structure(nullptr) { } TType(TBasicType t, unsigned char ps = 1, unsigned char ss = 1) - : type(t), precision(EbpUndefined), qualifier(EvqGlobal), + : type(t), precision(EbpUndefined), qualifier(EvqGlobal), invariant(false), layoutQualifier(TLayoutQualifier::create()), primarySize(ps), secondarySize(ss), array(false), arraySize(0), interfaceBlock(0), structure(0) @@ -241,7 +243,7 @@ class TType } TType(TBasicType t, TPrecision p, TQualifier q = EvqTemporary, unsigned char ps = 1, unsigned char ss = 1, bool a = false) - : type(t), precision(p), qualifier(q), + : type(t), precision(p), qualifier(q), invariant(false), layoutQualifier(TLayoutQualifier::create()), primarySize(ps), secondarySize(ss), array(a), arraySize(0), interfaceBlock(0), structure(0) @@ -249,7 +251,7 @@ class TType } explicit TType(const TPublicType &p); TType(TStructure *userDef, TPrecision p = EbpUndefined) - : type(EbtStruct), precision(p), qualifier(EvqTemporary), + : type(EbtStruct), precision(p), qualifier(EvqTemporary), invariant(false), layoutQualifier(TLayoutQualifier::create()), primarySize(1), secondarySize(1), array(false), arraySize(0), interfaceBlock(0), structure(userDef) @@ -258,19 +260,26 @@ class TType TType(TInterfaceBlock *interfaceBlockIn, TQualifier qualifierIn, TLayoutQualifier layoutQualifierIn, int arraySizeIn) : type(EbtInterfaceBlock), precision(EbpUndefined), qualifier(qualifierIn), - layoutQualifier(layoutQualifierIn), + invariant(false), layoutQualifier(layoutQualifierIn), primarySize(1), secondarySize(1), array(arraySizeIn > 0), arraySize(arraySizeIn), interfaceBlock(interfaceBlockIn), structure(0) { } + TType(const TType &) = default; + TType &operator=(const TType &) = default; + TBasicType getBasicType() const { return type; } void setBasicType(TBasicType t) { - type = t; + if (type != t) + { + type = t; + invalidateMangledName(); + } } TPrecision getPrecision() const @@ -291,6 +300,11 @@ class TType qualifier = q; } + bool isInvariant() const + { + return invariant; + } + TLayoutQualifier getLayoutQualifier() const { return layoutQualifier; @@ -320,11 +334,19 @@ class TType } void setPrimarySize(unsigned char ps) { - primarySize = ps; + if (primarySize != ps) + { + primarySize = ps; + invalidateMangledName(); + } } void setSecondarySize(unsigned char ss) { - secondarySize = ss; + if (secondarySize != ss) + { + secondarySize = ss; + invalidateMangledName(); + } } // Full size of single instance of type @@ -340,7 +362,11 @@ class TType } bool isArray() const { - return array ? true : false; + return array; + } + bool isUnsizedArray() const + { + return array && arraySize == 0; } int getArraySize() const { @@ -348,13 +374,21 @@ class TType } void setArraySize(int s) { - array = true; - arraySize = s; + if (!array || arraySize != s) + { + array = true; + arraySize = s; + invalidateMangledName(); + } } void clearArrayness() { - array = false; - arraySize = 0; + if (array) + { + array = false; + arraySize = 0; + invalidateMangledName(); + } } TInterfaceBlock *getInterfaceBlock() const @@ -363,7 +397,11 @@ class TType } void setInterfaceBlock(TInterfaceBlock *interfaceBlockIn) { - interfaceBlock = interfaceBlockIn; + if (interfaceBlock != interfaceBlockIn) + { + interfaceBlock = interfaceBlockIn; + invalidateMangledName(); + } } bool isInterfaceBlock() const { @@ -389,10 +427,14 @@ class TType } void setStruct(TStructure *s) { - structure = s; + if (structure != s) + { + structure = s; + invalidateMangledName(); + } } - const TString &getMangledName() + const TString &getMangledName() const { if (mangled.empty()) { @@ -477,19 +519,31 @@ class TType return structure ? structure->containsArrays() : false; } + bool isStructureContainingType(TBasicType t) const + { + return structure ? structure->containsType(t) : false; + } + bool isStructureContainingSamplers() const { return structure ? structure->containsSamplers() : false; } - protected: + // Initializes all lazily-initialized members. + void realize() + { + getMangledName(); + } + + private: + void invalidateMangledName() { mangled = ""; } TString buildMangledName() const; size_t getStructSize() const; - void computeDeepestStructNesting(); TBasicType type; TPrecision precision; TQualifier qualifier; + bool invariant; TLayoutQualifier layoutQualifier; unsigned char primarySize; // size of vector or cols matrix unsigned char secondarySize; // rows of a matrix @@ -519,6 +573,7 @@ struct TPublicType TBasicType type; TLayoutQualifier layoutQualifier; TQualifier qualifier; + bool invariant; TPrecision precision; unsigned char primarySize; // size of vector or cols of matrix unsigned char secondarySize; // rows of matrix @@ -527,11 +582,15 @@ struct TPublicType TType *userDef; TSourceLoc line; + // true if the type was defined by a struct specifier rather than a reference to a type name. + bool isStructSpecifier; + void setBasic(TBasicType bt, TQualifier q, const TSourceLoc &ln) { type = bt; layoutQualifier = TLayoutQualifier::create(); qualifier = q; + invariant = false; precision = EbpUndefined; primarySize = 1; secondarySize = 1; @@ -539,6 +598,7 @@ struct TPublicType arraySize = 0; userDef = 0; line = ln; + isStructSpecifier = false; } void setAggregate(unsigned char size) @@ -553,11 +613,20 @@ struct TPublicType secondarySize = r; } - void setArray(bool a, int s = 0) + bool isUnsizedArray() const { - array = a; + return array && arraySize == 0; + } + void setArraySize(int s) + { + array = true; arraySize = s; } + void clearArrayness() + { + array = false; + arraySize = 0; + } bool isStructureContainingArrays() const { @@ -569,6 +638,16 @@ struct TPublicType return userDef->isStructureContainingArrays(); } + bool isStructureContainingType(TBasicType t) const + { + if (!userDef) + { + return false; + } + + return userDef->isStructureContainingType(t); + } + bool isMatrix() const { return primarySize > 1 && secondarySize > 1; diff --git a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp index d548d421d2..e50bf202ef 100644 --- a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp +++ b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp @@ -13,7 +13,7 @@ namespace TIntermSelection *UnfoldOR(TIntermTyped *x, TIntermTyped *y) { const TType boolType(EbtBool, EbpUndefined); - ConstantUnion *u = new ConstantUnion; + TConstantUnion *u = new TConstantUnion; u->setBConst(true); TIntermConstantUnion *trueNode = new TIntermConstantUnion( u, TType(EbtBool, EbpUndefined, EvqConst, 1)); @@ -24,7 +24,7 @@ TIntermSelection *UnfoldOR(TIntermTyped *x, TIntermTyped *y) TIntermSelection *UnfoldAND(TIntermTyped *x, TIntermTyped *y) { const TType boolType(EbtBool, EbpUndefined); - ConstantUnion *u = new ConstantUnion; + TConstantUnion *u = new TConstantUnion; u->setBConst(false); TIntermConstantUnion *falseNode = new TIntermConstantUnion( u, TType(EbtBool, EbpUndefined, EvqConst, 1)); diff --git a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.h b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.h index 7b698ccb63..b92a4e9152 100644 --- a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.h +++ b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.h @@ -20,9 +20,12 @@ class UnfoldShortCircuitAST : public TIntermTraverser { public: - UnfoldShortCircuitAST() { } + UnfoldShortCircuitAST() + : TIntermTraverser(true, false, false) + { + } - virtual bool visitBinary(Visit visit, TIntermBinary *); + bool visitBinary(Visit visit, TIntermBinary *) override; }; #endif // COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUITAST_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp new file mode 100644 index 0000000000..be23b524d7 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp @@ -0,0 +1,368 @@ +// +// 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. +// +// UnfoldShortCircuitToIf is an AST traverser to convert short-circuiting operators to if-else statements. +// The results are assigned to s# temporaries, which are used by the main translator instead of +// the original expression. +// + +#include "compiler/translator/UnfoldShortCircuitToIf.h" + +#include "compiler/translator/IntermNode.h" + +namespace +{ + +// Traverser that unfolds one short-circuiting operation at a time. +class UnfoldShortCircuitTraverser : public TIntermTraverser +{ + public: + UnfoldShortCircuitTraverser(); + + bool visitBinary(Visit visit, TIntermBinary *node) override; + bool visitAggregate(Visit visit, TIntermAggregate *node) override; + bool visitSelection(Visit visit, TIntermSelection *node) override; + bool visitLoop(Visit visit, TIntermLoop *node) override; + + void nextIteration(); + bool foundShortCircuit() const { return mFoundShortCircuit; } + + protected: + // Check if the traversal is inside a loop condition or expression, in which case the unfolded + // expression needs to be copied inside the loop. Returns true if the copying is done, in which + // case no further unfolding should be done on the same traversal. + // The parameters are the node that will be unfolded to multiple statements and so can't remain + // inside a loop condition, and its parent. + bool copyLoopConditionOrExpression(TIntermNode *parent, TIntermTyped *node); + + // Marked to true once an operation that needs to be unfolded has been found. + // After that, no more unfolding is performed on that traversal. + bool mFoundShortCircuit; + + // Set to the loop node while a loop condition or expression is being traversed. + TIntermLoop *mParentLoop; + // Parent of the loop node while a loop condition or expression is being traversed. + TIntermNode *mLoopParent; + + bool mInLoopCondition; + bool mInLoopExpression; +}; + +UnfoldShortCircuitTraverser::UnfoldShortCircuitTraverser() + : TIntermTraverser(true, false, true), + mFoundShortCircuit(false), + mParentLoop(nullptr), + mLoopParent(nullptr), + mInLoopCondition(false), + mInLoopExpression(false) +{ +} + +bool UnfoldShortCircuitTraverser::visitBinary(Visit visit, TIntermBinary *node) +{ + if (mFoundShortCircuit) + return false; + // If our right node doesn't have side effects, we know we don't need to unfold this + // expression: there will be no short-circuiting side effects to avoid + // (note: unfolding doesn't depend on the left node -- it will always be evaluated) + if (!node->getRight()->hasSideEffects()) + { + return true; + } + + switch (node->getOp()) + { + case EOpLogicalOr: + mFoundShortCircuit = true; + if (!copyLoopConditionOrExpression(getParentNode(), node)) + { + // "x || y" is equivalent to "x ? true : y", which unfolds to "bool s; if(x) s = true; + // else s = y;", + // and then further simplifies down to "bool s = x; if(!s) s = y;". + + TIntermSequence insertions; + TType boolType(EbtBool, EbpUndefined, EvqTemporary); + + ASSERT(node->getLeft()->getType() == boolType); + insertions.push_back(createTempInitDeclaration(node->getLeft())); + + TIntermAggregate *assignRightBlock = new TIntermAggregate(EOpSequence); + ASSERT(node->getRight()->getType() == boolType); + assignRightBlock->getSequence()->push_back(createTempAssignment(node->getRight())); + + TIntermUnary *notTempSymbol = new TIntermUnary(EOpLogicalNot, boolType); + notTempSymbol->setOperand(createTempSymbol(boolType)); + TIntermSelection *ifNode = new TIntermSelection(notTempSymbol, assignRightBlock, nullptr); + insertions.push_back(ifNode); + + insertStatementsInParentBlock(insertions); + + NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(boolType), false); + mReplacements.push_back(replaceVariable); + } + return false; + case EOpLogicalAnd: + mFoundShortCircuit = true; + if (!copyLoopConditionOrExpression(getParentNode(), node)) + { + // "x && y" is equivalent to "x ? y : false", which unfolds to "bool s; if(x) s = y; + // else s = false;", + // and then further simplifies down to "bool s = x; if(s) s = y;". + TIntermSequence insertions; + TType boolType(EbtBool, EbpUndefined, EvqTemporary); + + ASSERT(node->getLeft()->getType() == boolType); + insertions.push_back(createTempInitDeclaration(node->getLeft())); + + TIntermAggregate *assignRightBlock = new TIntermAggregate(EOpSequence); + ASSERT(node->getRight()->getType() == boolType); + assignRightBlock->getSequence()->push_back(createTempAssignment(node->getRight())); + + TIntermSelection *ifNode = new TIntermSelection(createTempSymbol(boolType), assignRightBlock, nullptr); + insertions.push_back(ifNode); + + insertStatementsInParentBlock(insertions); + + NodeUpdateEntry replaceVariable(getParentNode(), node, createTempSymbol(boolType), false); + mReplacements.push_back(replaceVariable); + } + return false; + default: + return true; + } +} + +bool UnfoldShortCircuitTraverser::visitSelection(Visit visit, TIntermSelection *node) +{ + if (mFoundShortCircuit) + return false; + + // Unfold "b ? x : y" into "type s; if(b) s = x; else s = y;" + if (visit == PreVisit && node->usesTernaryOperator()) + { + mFoundShortCircuit = true; + if (!copyLoopConditionOrExpression(getParentNode(), node)) + { + TIntermSequence insertions; + + TIntermSymbol *tempSymbol = createTempSymbol(node->getType()); + TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration); + tempDeclaration->getSequence()->push_back(tempSymbol); + insertions.push_back(tempDeclaration); + + TIntermAggregate *trueBlock = new TIntermAggregate(EOpSequence); + TIntermBinary *trueAssignment = + createTempAssignment(node->getTrueBlock()->getAsTyped()); + trueBlock->getSequence()->push_back(trueAssignment); + + TIntermAggregate *falseBlock = new TIntermAggregate(EOpSequence); + TIntermBinary *falseAssignment = + createTempAssignment(node->getFalseBlock()->getAsTyped()); + falseBlock->getSequence()->push_back(falseAssignment); + + TIntermSelection *ifNode = + new TIntermSelection(node->getCondition()->getAsTyped(), trueBlock, falseBlock); + insertions.push_back(ifNode); + + insertStatementsInParentBlock(insertions); + + TIntermSymbol *ternaryResult = createTempSymbol(node->getType()); + NodeUpdateEntry replaceVariable(getParentNode(), node, ternaryResult, false); + mReplacements.push_back(replaceVariable); + } + return false; + } + + return true; +} + +bool UnfoldShortCircuitTraverser::visitAggregate(Visit visit, TIntermAggregate *node) +{ + if (visit == PreVisit && mFoundShortCircuit) + return false; // No need to traverse further + + if (node->getOp() == EOpComma) + { + ASSERT(visit != PreVisit || !mFoundShortCircuit); + + if (visit == PostVisit && mFoundShortCircuit) + { + // We can be sure that we arrived here because there was a short-circuiting operator + // inside the sequence operator since we only start traversing the sequence operator in + // case a short-circuiting operator has not been found so far. + // We need to unfold the sequence (comma) operator, otherwise the evaluation order of + // statements would be messed up by unfolded operations inside. + // Don't do any other unfolding on this round of traversal. + mReplacements.clear(); + mMultiReplacements.clear(); + mInsertions.clear(); + + if (!copyLoopConditionOrExpression(getParentNode(), node)) + { + TIntermSequence insertions; + TIntermSequence *seq = node->getSequence(); + + TIntermSequence::size_type i = 0; + ASSERT(!seq->empty()); + while (i < seq->size() - 1) + { + TIntermTyped *child = (*seq)[i]->getAsTyped(); + insertions.push_back(child); + ++i; + } + + insertStatementsInParentBlock(insertions); + + NodeUpdateEntry replaceVariable(getParentNode(), node, (*seq)[i], false); + mReplacements.push_back(replaceVariable); + } + } + } + return true; +} + +bool UnfoldShortCircuitTraverser::visitLoop(Visit visit, TIntermLoop *node) +{ + if (visit == PreVisit) + { + if (mFoundShortCircuit) + return false; // No need to traverse further + + mLoopParent = getParentNode(); + mParentLoop = node; + incrementDepth(node); + + if (node->getInit()) + { + node->getInit()->traverse(this); + if (mFoundShortCircuit) + { + decrementDepth(); + return false; + } + } + + if (node->getCondition()) + { + mInLoopCondition = true; + node->getCondition()->traverse(this); + mInLoopCondition = false; + + if (mFoundShortCircuit) + { + decrementDepth(); + return false; + } + } + + if (node->getExpression()) + { + mInLoopExpression = true; + node->getExpression()->traverse(this); + mInLoopExpression = false; + + if (mFoundShortCircuit) + { + decrementDepth(); + return false; + } + } + + if (node->getBody()) + node->getBody()->traverse(this); + + decrementDepth(); + } + return false; +} + +bool UnfoldShortCircuitTraverser::copyLoopConditionOrExpression(TIntermNode *parent, + TIntermTyped *node) +{ + if (mInLoopCondition) + { + mReplacements.push_back( + NodeUpdateEntry(parent, node, createTempSymbol(node->getType()), false)); + TIntermAggregate *body = mParentLoop->getBody(); + TIntermSequence empty; + if (mParentLoop->getType() == ELoopDoWhile) + { + // Declare the temporary variable before the loop. + TIntermSequence insertionsBeforeLoop; + insertionsBeforeLoop.push_back(createTempDeclaration(node->getType())); + insertStatementsInParentBlock(insertionsBeforeLoop); + + // Move a part of do-while loop condition to inside the loop. + TIntermSequence insertionsInLoop; + insertionsInLoop.push_back(createTempAssignment(node)); + mInsertions.push_back(NodeInsertMultipleEntry(body, body->getSequence()->size() - 1, + empty, insertionsInLoop)); + } + else + { + // The loop initializer expression and one copy of the part of the loop condition are + // executed before the loop. They need to be in a new scope. + TIntermAggregate *loopScope = new TIntermAggregate(EOpSequence); + + TIntermNode *initializer = mParentLoop->getInit(); + if (initializer != nullptr) + { + // Move the initializer to the newly created outer scope, so that condition can + // depend on it. + mReplacements.push_back(NodeUpdateEntry(mParentLoop, initializer, nullptr, false)); + loopScope->getSequence()->push_back(initializer); + } + + loopScope->getSequence()->push_back(createTempInitDeclaration(node)); + loopScope->getSequence()->push_back(mParentLoop); + mReplacements.push_back(NodeUpdateEntry(mLoopParent, mParentLoop, loopScope, true)); + + // The second copy of the part of the loop condition is executed inside the loop. + TIntermSequence insertionsInLoop; + insertionsInLoop.push_back(createTempAssignment(node->deepCopy())); + mInsertions.push_back(NodeInsertMultipleEntry(body, body->getSequence()->size() - 1, + empty, insertionsInLoop)); + } + return true; + } + + if (mInLoopExpression) + { + TIntermTyped *movedExpression = mParentLoop->getExpression(); + mReplacements.push_back(NodeUpdateEntry(mParentLoop, movedExpression, nullptr, false)); + TIntermAggregate *body = mParentLoop->getBody(); + TIntermSequence empty; + TIntermSequence insertions; + insertions.push_back(movedExpression); + mInsertions.push_back( + NodeInsertMultipleEntry(body, body->getSequence()->size() - 1, empty, insertions)); + return true; + } + return false; +} + +void UnfoldShortCircuitTraverser::nextIteration() +{ + mFoundShortCircuit = false; + nextTemporaryIndex(); +} + +} // namespace + +void UnfoldShortCircuitToIf(TIntermNode *root, unsigned int *temporaryIndex) +{ + UnfoldShortCircuitTraverser traverser; + ASSERT(temporaryIndex != nullptr); + traverser.useTemporaryIndex(temporaryIndex); + // Unfold one operator at a time, and reset the traverser between iterations. + do + { + traverser.nextIteration(); + root->traverse(&traverser); + if (traverser.foundShortCircuit()) + traverser.updateTree(); + } + while (traverser.foundShortCircuit()); +} diff --git a/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.h b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.h new file mode 100644 index 0000000000..0fe37b7140 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.h @@ -0,0 +1,18 @@ +// +// Copyright (c) 2002-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. +// +// UnfoldShortCircuitToIf is an AST traverser to convert short-circuiting operators to if-else statements. +// The results are assigned to s# temporaries, which are used by the main translator instead of +// the original expression. +// + +#ifndef COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_ +#define COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_ + +class TIntermNode; + +void UnfoldShortCircuitToIf(TIntermNode *root, unsigned int *temporaryIndex); + +#endif // COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/UniformHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/UniformHLSL.cpp index 71659fe354..20961c44c1 100644 --- a/src/3rdparty/angle/src/compiler/translator/UniformHLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/UniformHLSL.cpp @@ -93,7 +93,9 @@ const Uniform *UniformHLSL::findUniformByName(const TString &name) const return NULL; } -unsigned int UniformHLSL::declareUniformAndAssignRegister(const TType &type, const TString &name) +unsigned int UniformHLSL::declareUniformAndAssignRegister(const TType &type, + const TString &name, + unsigned int *registerCount) { unsigned int registerIndex = (IsSampler(type.getBasicType()) ? mSamplerRegister : mUniformRegister); @@ -102,43 +104,119 @@ unsigned int UniformHLSL::declareUniformAndAssignRegister(const TType &type, con mUniformRegisterMap[uniform->name] = registerIndex; - unsigned int registerCount = HLSLVariableRegisterCount(*uniform, mOutputType); + ASSERT(registerCount); + *registerCount = HLSLVariableRegisterCount(*uniform, mOutputType); if (gl::IsSamplerType(uniform->type)) { - mSamplerRegister += registerCount; + mSamplerRegister += *registerCount; } else { - mUniformRegister += registerCount; + mUniformRegister += *registerCount; } return registerIndex; } -TString UniformHLSL::uniformsHeader(ShShaderOutput outputType, const ReferencedSymbols &referencedUniforms) +unsigned int UniformHLSL::declareUniformAndAssignRegister(const TType &type, const TString &name) +{ + unsigned int registerCount; + return declareUniformAndAssignRegister(type, name, ®isterCount); +} + +void UniformHLSL::outputHLSLSamplerUniformGroup(TInfoSinkBase &out, + const HLSLTextureSamplerGroup textureGroup, + const TVector &group, + unsigned int *groupTextureRegisterIndex) { - TString uniforms; + if (group.empty()) + { + return; + } + unsigned int groupRegisterCount = 0; + for (const TIntermSymbol *uniform : group) + { + const TType &type = uniform->getType(); + const TString &name = uniform->getSymbol(); + unsigned int registerCount; + unsigned int samplerArrayIndex = + declareUniformAndAssignRegister(type, name, ®isterCount); + groupRegisterCount += registerCount; + if (type.isArray()) + { + out << "static const uint " << DecorateIfNeeded(uniform->getName()) << ArrayString(type) + << " = {"; + for (int i = 0; i < type.getArraySize(); ++i) + { + if (i > 0) + out << ", "; + out << (samplerArrayIndex + i); + } + out << "};\n"; + } + else + { + out << "static const uint " << DecorateIfNeeded(uniform->getName()) << " = " + << samplerArrayIndex << ";\n"; + } + } + TString suffix = TextureGroupSuffix(textureGroup); + // Since HLSL_TEXTURE_2D is the first group, it has a fixed offset of zero. + if (textureGroup != HLSL_TEXTURE_2D) + { + out << "static const uint textureIndexOffset" << suffix << " = " + << (*groupTextureRegisterIndex) << ";\n"; + out << "static const uint samplerIndexOffset" << suffix << " = " + << (*groupTextureRegisterIndex) << ";\n"; + } + out << "uniform " << TextureString(textureGroup) << " textures" << suffix << "[" + << groupRegisterCount << "]" + << " : register(t" << (*groupTextureRegisterIndex) << ");\n"; + out << "uniform " << SamplerString(textureGroup) << " samplers" << suffix << "[" + << groupRegisterCount << "]" + << " : register(s" << (*groupTextureRegisterIndex) << ");\n"; + *groupTextureRegisterIndex += groupRegisterCount; +} - for (ReferencedSymbols::const_iterator uniformIt = referencedUniforms.begin(); - uniformIt != referencedUniforms.end(); uniformIt++) +void UniformHLSL::uniformsHeader(TInfoSinkBase &out, + ShShaderOutput outputType, + const ReferencedSymbols &referencedUniforms) +{ + if (!referencedUniforms.empty()) + { + out << "// Uniforms\n\n"; + } + // In the case of HLSL 4, sampler uniforms need to be grouped by type before the code is + // written. They are grouped based on the combination of the HLSL texture type and + // HLSL sampler type, enumerated in HLSLTextureSamplerGroup. + TVector> groupedSamplerUniforms; + groupedSamplerUniforms.resize(HLSL_TEXTURE_MAX + 1); + for (auto &uniformIt : referencedUniforms) { - const TIntermSymbol &uniform = *uniformIt->second; + // Output regular uniforms. Group sampler uniforms by type. + const TIntermSymbol &uniform = *uniformIt.second; const TType &type = uniform.getType(); const TString &name = uniform.getSymbol(); - unsigned int registerIndex = declareUniformAndAssignRegister(type, name); - - if (outputType == SH_HLSL11_OUTPUT && IsSampler(type.getBasicType())) // Also declare the texture + if (outputType == SH_HLSL_4_1_OUTPUT && IsSampler(type.getBasicType())) { - uniforms += "uniform " + SamplerString(type) + " sampler_" + DecorateUniform(name, type) + ArrayString(type) + - " : register(s" + str(registerIndex) + ");\n"; - - uniforms += "uniform " + TextureString(type) + " texture_" + DecorateUniform(name, type) + ArrayString(type) + - " : register(t" + str(registerIndex) + ");\n"; + HLSLTextureSamplerGroup group = TextureGroup(type.getBasicType()); + groupedSamplerUniforms[group].push_back(&uniform); + } + else if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT && IsSampler(type.getBasicType())) + { + unsigned int registerIndex = declareUniformAndAssignRegister(type, name); + out << "uniform " << SamplerString(type.getBasicType()) << " sampler_" + << DecorateUniform(name, type) << ArrayString(type) << " : register(s" + << str(registerIndex) << ");\n"; + out << "uniform " << TextureString(type.getBasicType()) << " texture_" + << DecorateUniform(name, type) << ArrayString(type) << " : register(t" + << str(registerIndex) << ");\n"; } else { + unsigned int registerIndex = declareUniformAndAssignRegister(type, name); const TStructure *structure = type.getStruct(); // If this is a nameless struct, we need to use its full definition, rather than its (empty) name. // TypeString() will invoke defineNameless in this case; qualifier prefixes are unnecessary for @@ -149,11 +227,23 @@ TString UniformHLSL::uniformsHeader(ShShaderOutput outputType, const ReferencedS const TString ®isterString = TString("register(") + UniformRegisterPrefix(type) + str(registerIndex) + ")"; - uniforms += "uniform " + typeName + " " + DecorateUniform(name, type) + ArrayString(type) + " : " + registerString + ";\n"; + out << "uniform " << typeName << " " << DecorateUniform(name, type) << ArrayString(type) + << " : " << registerString << ";\n"; } } - return (uniforms.empty() ? "" : ("// Uniforms\n\n" + uniforms)); + if (outputType == SH_HLSL_4_1_OUTPUT) + { + unsigned int groupTextureRegisterIndex = 0; + // TEXTURE_2D is special, index offset is assumed to be 0 and omitted in that case. + ASSERT(HLSL_TEXTURE_MIN == HLSL_TEXTURE_2D); + for (int groupId = HLSL_TEXTURE_MIN; groupId < HLSL_TEXTURE_MAX; ++groupId) + { + outputHLSLSamplerUniformGroup(out, HLSLTextureSamplerGroup(groupId), + groupedSamplerUniforms[groupId], + &groupTextureRegisterIndex); + } + } } TString UniformHLSL::interfaceBlocksHeader(const ReferencedSymbols &referencedInterfaceBlocks) diff --git a/src/3rdparty/angle/src/compiler/translator/UniformHLSL.h b/src/3rdparty/angle/src/compiler/translator/UniformHLSL.h index 4ab9ccdf53..0f51f349bb 100644 --- a/src/3rdparty/angle/src/compiler/translator/UniformHLSL.h +++ b/src/3rdparty/angle/src/compiler/translator/UniformHLSL.h @@ -11,6 +11,7 @@ #define COMPILER_TRANSLATOR_UNIFORMHLSL_H_ #include "compiler/translator/OutputHLSL.h" +#include "compiler/translator/UtilsHLSL.h" namespace sh { @@ -23,7 +24,13 @@ class UniformHLSL : angle::NonCopyable void reserveUniformRegisters(unsigned int registerCount); void reserveInterfaceBlockRegisters(unsigned int registerCount); - TString uniformsHeader(ShShaderOutput outputType, const ReferencedSymbols &referencedUniforms); + void outputHLSLSamplerUniformGroup(TInfoSinkBase &out, + const HLSLTextureSamplerGroup textureGroup, + const TVector &group, + unsigned int *groupTextureRegisterIndex); + void uniformsHeader(TInfoSinkBase &out, + ShShaderOutput outputType, + const ReferencedSymbols &referencedUniforms); TString interfaceBlocksHeader(const ReferencedSymbols &referencedInterfaceBlocks); // Used for direct index references @@ -45,6 +52,9 @@ class UniformHLSL : angle::NonCopyable const Uniform *findUniformByName(const TString &name) const; // Returns the uniform's register index + unsigned int declareUniformAndAssignRegister(const TType &type, + const TString &name, + unsigned int *registerCount); unsigned int declareUniformAndAssignRegister(const TType &type, const TString &name); unsigned int mUniformRegister; diff --git a/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.cpp index 94e19ac40d..404ccee75d 100644 --- a/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.cpp @@ -8,15 +8,16 @@ // #include "compiler/translator/UtilsHLSL.h" +#include "compiler/translator/IntermNode.h" #include "compiler/translator/StructureHLSL.h" #include "compiler/translator/SymbolTable.h" namespace sh { -TString SamplerString(const TType &type) +TString SamplerString(const TBasicType type) { - if (IsShadowSampler(type.getBasicType())) + if (IsShadowSampler(type)) { return "SamplerComparisonState"; } @@ -26,32 +27,158 @@ TString SamplerString(const TType &type) } } -TString TextureString(const TType &type) +TString SamplerString(HLSLTextureSamplerGroup type) { - switch (type.getBasicType()) + if (type >= HLSL_COMPARISON_SAMPLER_GROUP_BEGIN && type <= HLSL_COMPARISON_SAMPLER_GROUP_END) { - case EbtSampler2D: return "Texture2D"; - case EbtSamplerCube: return "TextureCube"; - case EbtSamplerExternalOES: return "Texture2D"; - case EbtSampler2DArray: return "Texture2DArray"; - case EbtSampler3D: return "Texture3D"; - case EbtISampler2D: return "Texture2D"; - case EbtISampler3D: return "Texture3D"; - case EbtISamplerCube: return "Texture2DArray"; - case EbtISampler2DArray: return "Texture2DArray"; - case EbtUSampler2D: return "Texture2D"; - case EbtUSampler3D: return "Texture3D"; - case EbtUSamplerCube: return "Texture2DArray"; - case EbtUSampler2DArray: return "Texture2DArray"; - case EbtSampler2DShadow: return "Texture2D"; - case EbtSamplerCubeShadow: return "TextureCube"; - case EbtSampler2DArrayShadow: return "Texture2DArray"; - default: UNREACHABLE(); + return "SamplerComparisonState"; + } + else + { + return "SamplerState"; + } +} + +HLSLTextureSamplerGroup TextureGroup(const TBasicType type) +{ + switch (type) + { + case EbtSampler2D: + return HLSL_TEXTURE_2D; + case EbtSamplerCube: + return HLSL_TEXTURE_CUBE; + case EbtSamplerExternalOES: + return HLSL_TEXTURE_2D; + case EbtSampler2DArray: + return HLSL_TEXTURE_2D_ARRAY; + case EbtSampler3D: + return HLSL_TEXTURE_3D; + case EbtISampler2D: + return HLSL_TEXTURE_2D_INT4; + case EbtISampler3D: + return HLSL_TEXTURE_3D_INT4; + case EbtISamplerCube: + return HLSL_TEXTURE_2D_ARRAY_INT4; + case EbtISampler2DArray: + return HLSL_TEXTURE_2D_ARRAY_INT4; + case EbtUSampler2D: + return HLSL_TEXTURE_2D_UINT4; + case EbtUSampler3D: + return HLSL_TEXTURE_3D_UINT4; + case EbtUSamplerCube: + return HLSL_TEXTURE_2D_ARRAY_UINT4; + case EbtUSampler2DArray: + return HLSL_TEXTURE_2D_ARRAY_UINT4; + case EbtSampler2DShadow: + return HLSL_TEXTURE_2D_COMPARISON; + case EbtSamplerCubeShadow: + return HLSL_TEXTURE_CUBE_COMPARISON; + case EbtSampler2DArrayShadow: + return HLSL_TEXTURE_2D_ARRAY_COMPARISON; + default: + UNREACHABLE(); + } + return HLSL_TEXTURE_UNKNOWN; +} + +TString TextureString(const HLSLTextureSamplerGroup type) +{ + switch (type) + { + case HLSL_TEXTURE_2D: + return "Texture2D"; + case HLSL_TEXTURE_CUBE: + return "TextureCube"; + case HLSL_TEXTURE_2D_ARRAY: + return "Texture2DArray"; + case HLSL_TEXTURE_3D: + return "Texture3D"; + case HLSL_TEXTURE_2D_INT4: + return "Texture2D"; + case HLSL_TEXTURE_3D_INT4: + return "Texture3D"; + case HLSL_TEXTURE_2D_ARRAY_INT4: + return "Texture2DArray"; + case HLSL_TEXTURE_2D_UINT4: + return "Texture2D"; + case HLSL_TEXTURE_3D_UINT4: + return "Texture3D"; + case HLSL_TEXTURE_2D_ARRAY_UINT4: + return "Texture2DArray"; + case HLSL_TEXTURE_2D_COMPARISON: + return "Texture2D"; + case HLSL_TEXTURE_CUBE_COMPARISON: + return "TextureCube"; + case HLSL_TEXTURE_2D_ARRAY_COMPARISON: + return "Texture2DArray"; + default: + UNREACHABLE(); } return ""; } +TString TextureString(const TBasicType type) +{ + return TextureString(TextureGroup(type)); +} + +TString TextureGroupSuffix(const HLSLTextureSamplerGroup type) +{ + switch (type) + { + case HLSL_TEXTURE_2D: + return "2D"; + case HLSL_TEXTURE_CUBE: + return "Cube"; + case HLSL_TEXTURE_2D_ARRAY: + return "2DArray"; + case HLSL_TEXTURE_3D: + return "3D"; + case HLSL_TEXTURE_2D_INT4: + return "2D_int4_"; + case HLSL_TEXTURE_3D_INT4: + return "3D_int4_"; + case HLSL_TEXTURE_2D_ARRAY_INT4: + return "2DArray_int4_"; + case HLSL_TEXTURE_2D_UINT4: + return "2D_uint4_"; + case HLSL_TEXTURE_3D_UINT4: + return "3D_uint4_"; + case HLSL_TEXTURE_2D_ARRAY_UINT4: + return "2DArray_uint4_"; + case HLSL_TEXTURE_2D_COMPARISON: + return "2D_comparison"; + case HLSL_TEXTURE_CUBE_COMPARISON: + return "Cube_comparison"; + case HLSL_TEXTURE_2D_ARRAY_COMPARISON: + return "2DArray_comparison"; + default: + UNREACHABLE(); + } + + return ""; +} + +TString TextureGroupSuffix(const TBasicType type) +{ + return TextureGroupSuffix(TextureGroup(type)); +} + +TString TextureTypeSuffix(const TBasicType type) +{ + switch (type) + { + case EbtISamplerCube: + return "Cube_int4_"; + case EbtUSamplerCube: + return "Cube_uint4_"; + default: + // All other types are identified by their group suffix + return TextureGroupSuffix(type); + } +} + TString DecorateUniform(const TString &string, const TType &type) { if (type.getBasicType() == EbtSamplerExternalOES) @@ -87,6 +214,30 @@ TString Decorate(const TString &string) return string; } +TString DecorateIfNeeded(const TName &name) +{ + if (name.isInternal()) + { + return name.getString(); + } + else + { + return Decorate(name.getString()); + } +} + +TString DecorateFunctionIfNeeded(const TName &name) +{ + if (name.isInternal()) + { + return TFunction::unmangleName(name.getString()); + } + else + { + return Decorate(TFunction::unmangleName(name.getString())); + } +} + TString TypeString(const TType &type) { const TStructure* structure = type.getStruct(); @@ -217,13 +368,11 @@ TString InterpolationString(TQualifier qualifier) { case EvqVaryingIn: return ""; case EvqFragmentIn: return ""; - case EvqInvariantVaryingIn: return ""; case EvqSmoothIn: return "linear"; case EvqFlatIn: return "nointerpolation"; case EvqCentroidIn: return "centroid"; case EvqVaryingOut: return ""; case EvqVertexOut: return ""; - case EvqInvariantVaryingOut: return ""; case EvqSmoothOut: return "linear"; case EvqFlatOut: return "nointerpolation"; case EvqCentroidOut: return "centroid"; @@ -247,4 +396,43 @@ TString QualifierString(TQualifier qualifier) return ""; } +int HLSLTextureCoordsCount(const TBasicType samplerType) +{ + switch (samplerType) + { + case EbtSampler2D: + return 2; + case EbtSampler3D: + return 3; + case EbtSamplerCube: + return 3; + case EbtSampler2DArray: + return 3; + case EbtISampler2D: + return 2; + case EbtISampler3D: + return 3; + case EbtISamplerCube: + return 3; + case EbtISampler2DArray: + return 3; + case EbtUSampler2D: + return 2; + case EbtUSampler3D: + return 3; + case EbtUSamplerCube: + return 3; + case EbtUSampler2DArray: + return 3; + case EbtSampler2DShadow: + return 2; + case EbtSamplerCubeShadow: + return 3; + case EbtSampler2DArrayShadow: + return 3; + default: + UNREACHABLE(); + } + return 0; +} } diff --git a/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.h b/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.h index 9800a3bbf3..42444e3a56 100644 --- a/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.h +++ b/src/3rdparty/angle/src/compiler/translator/UtilsHLSL.h @@ -15,13 +15,54 @@ #include "angle_gl.h" +class TName; + namespace sh { -TString TextureString(const TType &type); -TString SamplerString(const TType &type); +// Unique combinations of HLSL Texture type and HLSL Sampler type. +enum HLSLTextureSamplerGroup +{ + // Regular samplers + HLSL_TEXTURE_2D, + HLSL_TEXTURE_MIN = HLSL_TEXTURE_2D, + + HLSL_TEXTURE_CUBE, + HLSL_TEXTURE_2D_ARRAY, + HLSL_TEXTURE_3D, + HLSL_TEXTURE_2D_INT4, + HLSL_TEXTURE_3D_INT4, + HLSL_TEXTURE_2D_ARRAY_INT4, + HLSL_TEXTURE_2D_UINT4, + HLSL_TEXTURE_3D_UINT4, + HLSL_TEXTURE_2D_ARRAY_UINT4, + + // Comparison samplers + + HLSL_TEXTURE_2D_COMPARISON, + HLSL_TEXTURE_CUBE_COMPARISON, + HLSL_TEXTURE_2D_ARRAY_COMPARISON, + + HLSL_COMPARISON_SAMPLER_GROUP_BEGIN = HLSL_TEXTURE_2D_COMPARISON, + HLSL_COMPARISON_SAMPLER_GROUP_END = HLSL_TEXTURE_2D_ARRAY_COMPARISON, + + HLSL_TEXTURE_UNKNOWN, + HLSL_TEXTURE_MAX = HLSL_TEXTURE_UNKNOWN +}; + +HLSLTextureSamplerGroup TextureGroup(const TBasicType type); +TString TextureString(const HLSLTextureSamplerGroup type); +TString TextureString(const TBasicType type); +TString TextureGroupSuffix(const HLSLTextureSamplerGroup type); +TString TextureGroupSuffix(const TBasicType type); +TString TextureTypeSuffix(const TBasicType type); +TString SamplerString(const TBasicType type); +TString SamplerString(HLSLTextureSamplerGroup type); // Prepends an underscore to avoid naming clashes TString Decorate(const TString &string); +TString DecorateIfNeeded(const TName &name); +// Decorates and also unmangles the function name +TString DecorateFunctionIfNeeded(const TName &name); TString DecorateUniform(const TString &string, const TType &type); TString DecorateField(const TString &string, const TStructure &structure); TString DecoratePrivate(const TString &privateText); @@ -31,7 +72,7 @@ TString QualifiedStructNameString(const TStructure &structure, bool useHLSLRowMa bool useStd140Packing); TString InterpolationString(TQualifier qualifier); TString QualifierString(TQualifier qualifier); - +int HLSLTextureCoordsCount(const TBasicType samplerType); } #endif // COMPILER_TRANSLATOR_UTILSHLSL_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.cpp b/src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.cpp new file mode 100644 index 0000000000..2461b6a438 --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.cpp @@ -0,0 +1,112 @@ +// +// 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. +// + +#include "compiler/translator/ValidateGlobalInitializer.h" + +#include "compiler/translator/ParseContext.h" + +namespace +{ + +class ValidateGlobalInitializerTraverser : public TIntermTraverser +{ + public: + ValidateGlobalInitializerTraverser(const TParseContext *context); + + void visitSymbol(TIntermSymbol *node) override; + bool visitAggregate(Visit visit, TIntermAggregate *node) override; + bool visitBinary(Visit visit, TIntermBinary *node) override; + bool visitUnary(Visit visit, TIntermUnary *node) override; + + bool isValid() const { return mIsValid; } + bool issueWarning() const { return mIssueWarning; } + + private: + const TParseContext *mContext; + bool mIsValid; + bool mIssueWarning; +}; + +void ValidateGlobalInitializerTraverser::visitSymbol(TIntermSymbol *node) +{ + const TSymbol *sym = mContext->symbolTable.find(node->getSymbol(), mContext->getShaderVersion()); + if (sym->isVariable()) + { + // ESSL 1.00 section 4.3 (or ESSL 3.00 section 4.3): + // Global initializers must be constant expressions. + const TVariable *var = static_cast(sym); + switch (var->getType().getQualifier()) + { + case EvqConst: + break; + case EvqGlobal: + case EvqTemporary: + case EvqUniform: + // We allow these cases to be compatible with legacy ESSL 1.00 content. + // Implement stricter rules for ESSL 3.00 since there's no legacy content to deal with. + if (mContext->getShaderVersion() >= 300) + { + mIsValid = false; + } + else + { + mIssueWarning = true; + } + break; + default: + mIsValid = false; + } + } +} + +bool ValidateGlobalInitializerTraverser::visitAggregate(Visit visit, TIntermAggregate *node) +{ + // Disallow calls to user-defined functions and texture lookup functions in global variable initializers. + // This is done simply by disabling all function calls - built-in math functions don't use EOpFunctionCall. + if (node->getOp() == EOpFunctionCall) + { + mIsValid = false; + } + return true; +} + +bool ValidateGlobalInitializerTraverser::visitBinary(Visit visit, TIntermBinary *node) +{ + if (node->isAssignment()) + { + mIsValid = false; + } + return true; +} + +bool ValidateGlobalInitializerTraverser::visitUnary(Visit visit, TIntermUnary *node) +{ + if (node->isAssignment()) + { + mIsValid = false; + } + return true; +} + +ValidateGlobalInitializerTraverser::ValidateGlobalInitializerTraverser(const TParseContext *context) + : TIntermTraverser(true, false, false), + mContext(context), + mIsValid(true), + mIssueWarning(false) +{ +} + +} // namespace + +bool ValidateGlobalInitializer(TIntermTyped *initializer, const TParseContext *context, bool *warning) +{ + ValidateGlobalInitializerTraverser validate(context); + initializer->traverse(&validate); + ASSERT(warning != nullptr); + *warning = validate.issueWarning(); + return validate.isValid(); +} + diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.h b/src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.h new file mode 100644 index 0000000000..c3d2a47eba --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.h @@ -0,0 +1,16 @@ +// +// 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. +// + +#ifndef COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_ +#define COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_ + +class TIntermTyped; +class TParseContext; + +// Returns true if the initializer is valid. +bool ValidateGlobalInitializer(TIntermTyped *initializer, const TParseContext *context, bool *warning); + +#endif // COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.cpp b/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.cpp index 12367066e8..ba8cdd0aa8 100644 --- a/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.cpp +++ b/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.cpp @@ -26,12 +26,16 @@ class ValidateConstIndexExpr : public TIntermTraverser { public: ValidateConstIndexExpr(TLoopStack& stack) - : mValid(true), mLoopStack(stack) {} + : TIntermTraverser(true, false, false), + mValid(true), + mLoopStack(stack) + { + } // Returns true if the parsed node represents a constant index expression. bool isValid() const { return mValid; } - virtual void visitSymbol(TIntermSymbol *symbol) + void visitSymbol(TIntermSymbol *symbol) override { // Only constants and loop indices are allowed in a // constant index expression. @@ -49,12 +53,35 @@ class ValidateConstIndexExpr : public TIntermTraverser } // namespace anonymous -ValidateLimitations::ValidateLimitations(sh::GLenum shaderType, - TInfoSinkBase &sink) - : mShaderType(shaderType), +ValidateLimitations::ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase *sink) + : TIntermTraverser(true, false, false), + mShaderType(shaderType), mSink(sink), - mNumErrors(0) + mNumErrors(0), + mValidateIndexing(true), + mValidateInnerLoops(true) +{ +} + +// static +bool ValidateLimitations::IsLimitedForLoop(TIntermLoop *loop) { + // The shader type doesn't matter in this case. + ValidateLimitations validate(GL_FRAGMENT_SHADER, nullptr); + validate.mValidateIndexing = false; + validate.mValidateInnerLoops = false; + if (!validate.validateLoopType(loop)) + return false; + if (!validate.validateForLoopHeader(loop)) + return false; + TIntermNode *body = loop->getBody(); + if (body != nullptr) + { + validate.mLoopStack.push(loop); + body->traverse(&validate); + validate.mLoopStack.pop(); + } + return (validate.mNumErrors == 0); } bool ValidateLimitations::visitBinary(Visit, TIntermBinary *node) @@ -67,10 +94,11 @@ bool ValidateLimitations::visitBinary(Visit, TIntermBinary *node) { case EOpIndexDirect: case EOpIndexIndirect: - validateIndexing(node); - break; + if (mValidateIndexing) + validateIndexing(node); + break; default: - break; + break; } return true; } @@ -97,6 +125,9 @@ bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate *node) bool ValidateLimitations::visitLoop(Visit, TIntermLoop *node) { + if (!mValidateInnerLoops) + return true; + if (!validateLoopType(node)) return false; @@ -118,9 +149,12 @@ bool ValidateLimitations::visitLoop(Visit, TIntermLoop *node) void ValidateLimitations::error(TSourceLoc loc, const char *reason, const char *token) { - mSink.prefix(EPrefixError); - mSink.location(loc); - mSink << "'" << token << "' : " << reason << "\n"; + if (mSink) + { + mSink->prefix(EPrefixError); + mSink->location(loc); + (*mSink) << "'" << token << "' : " << reason << "\n"; + } ++mNumErrors; } @@ -389,13 +423,13 @@ bool ValidateLimitations::validateFunctionCall(TIntermAggregate *node) bool valid = true; TSymbolTable& symbolTable = GetGlobalParseContext()->symbolTable; - TSymbol* symbol = symbolTable.find(node->getName(), GetGlobalParseContext()->shaderVersion); + TSymbol* symbol = symbolTable.find(node->getName(), GetGlobalParseContext()->getShaderVersion()); ASSERT(symbol && symbol->isFunction()); TFunction *function = static_cast(symbol); for (ParamIndex::const_iterator i = pIndex.begin(); i != pIndex.end(); ++i) { - const TParameter ¶m = function->getParam(*i); + const TConstParameter ¶m = function->getParam(*i); TQualifier qual = param.type->getQualifier(); if ((qual == EvqOut) || (qual == EvqInOut)) { @@ -428,8 +462,8 @@ bool ValidateLimitations::validateOperation(TIntermOperator *node, bool ValidateLimitations::isConstExpr(TIntermNode *node) { - ASSERT(node != NULL); - return node->getAsConstantUnion() != NULL; + ASSERT(node != nullptr); + return node->getAsConstantUnion() != nullptr && node->getAsTyped()->getQualifier() == EvqConst; } bool ValidateLimitations::isConstIndexExpr(TIntermNode *node) @@ -448,13 +482,6 @@ bool ValidateLimitations::validateIndexing(TIntermBinary *node) bool valid = true; TIntermTyped *index = node->getRight(); - // The index expression must have integral type. - if (!index->isScalarInt()) { - error(index->getLine(), - "Index expression must have integral type", - index->getCompleteString().c_str()); - valid = false; - } // The index expession must be a constant-index-expression unless // the operand is a uniform in a vertex shader. TIntermTyped *operand = node->getLeft(); diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.h b/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.h index 59cccb565f..666e38ff5c 100644 --- a/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.h +++ b/src/3rdparty/angle/src/compiler/translator/ValidateLimitations.h @@ -17,14 +17,16 @@ class TInfoSinkBase; class ValidateLimitations : public TIntermTraverser { public: - ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase &sink); + ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase *sink); int numErrors() const { return mNumErrors; } - virtual bool visitBinary(Visit, TIntermBinary *); - virtual bool visitUnary(Visit, TIntermUnary *); - virtual bool visitAggregate(Visit, TIntermAggregate *); - virtual bool visitLoop(Visit, TIntermLoop *); + bool visitBinary(Visit, TIntermBinary *) override; + bool visitUnary(Visit, TIntermUnary *) override; + bool visitAggregate(Visit, TIntermAggregate *) override; + bool visitLoop(Visit, TIntermLoop *) override; + + static bool IsLimitedForLoop(TIntermLoop *node); private: void error(TSourceLoc loc, const char *reason, const char *token); @@ -51,9 +53,11 @@ class ValidateLimitations : public TIntermTraverser bool validateIndexing(TIntermBinary *node); sh::GLenum mShaderType; - TInfoSinkBase &mSink; + TInfoSinkBase *mSink; int mNumErrors; TLoopStack mLoopStack; + bool mValidateIndexing; + bool mValidateInnerLoops; }; #endif // COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.cpp b/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.cpp index ac1c10d6b0..cd37aeacd1 100644 --- a/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.cpp +++ b/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.cpp @@ -9,11 +9,23 @@ #include "compiler/translator/InitializeParseContext.h" #include "compiler/translator/ParseContext.h" -ValidateOutputs::ValidateOutputs(TInfoSinkBase& sink, int maxDrawBuffers) - : mSink(sink), +namespace +{ +void error(int *errorCount, TInfoSinkBase &sink, const TIntermSymbol &symbol, const char *reason) +{ + sink.prefix(EPrefixError); + sink.location(symbol.getLine()); + sink << "'" << symbol.getSymbol() << "' : " << reason << "\n"; + (*errorCount)++; +} + +} // namespace + +ValidateOutputs::ValidateOutputs(const TExtensionBehavior &extBehavior, int maxDrawBuffers) + : TIntermTraverser(true, false, false), mMaxDrawBuffers(maxDrawBuffers), - mNumErrors(0), - mHasUnspecifiedOutputLocation(false) + mAllowUnspecifiedOutputLocationResolution( + IsExtensionEnabled(extBehavior, "GL_EXT_blend_func_extended")) { } @@ -29,50 +41,68 @@ void ValidateOutputs::visitSymbol(TIntermSymbol *symbol) if (qualifier == EvqFragmentOut) { - const TType &type = symbol->getType(); - const int location = type.getLayoutQualifier().location; - - if (mHasUnspecifiedOutputLocation) + if (symbol->getType().getLayoutQualifier().location == -1) { - error(symbol->getLine(), "must explicitly specify all locations when using multiple fragment outputs", name.c_str()); + mUnspecifiedLocationOutputs.push_back(symbol); } - else if (location == -1) + else { - mHasUnspecifiedOutputLocation = true; + mOutputs.push_back(symbol); } - else + } +} + +int ValidateOutputs::validateAndCountErrors(TInfoSinkBase &sink) const +{ + OutputVector validOutputs(mMaxDrawBuffers); + int errorCount = 0; + + for (const auto &symbol : mOutputs) + { + const TType &type = symbol->getType(); + const size_t elementCount = static_cast(type.isArray() ? type.getArraySize() : 1); + const size_t location = static_cast(type.getLayoutQualifier().location); + + ASSERT(type.getLayoutQualifier().location != -1); + + if (location + elementCount <= validOutputs.size()) { - OutputMap::iterator mapEntry = mOutputMap.find(location); - if (mapEntry == mOutputMap.end()) + for (size_t elementIndex = 0; elementIndex < elementCount; elementIndex++) { - const int elementCount = type.isArray() ? type.getArraySize() : 1; - if (location + elementCount > mMaxDrawBuffers) + const size_t offsetLocation = location + elementIndex; + if (validOutputs[offsetLocation]) { - error(symbol->getLine(), "output location must be < MAX_DRAW_BUFFERS", name.c_str()); + std::stringstream strstr; + strstr << "conflicting output locations with previously defined output '" + << validOutputs[offsetLocation]->getSymbol() << "'"; + error(&errorCount, sink, *symbol, strstr.str().c_str()); } - - for (int elementIndex = 0; elementIndex < elementCount; elementIndex++) + else { - const int offsetLocation = location + elementIndex; - mOutputMap[offsetLocation] = symbol; + validOutputs[offsetLocation] = symbol; } } - else + } + else + { + if (elementCount > 0) { - std::stringstream strstr; - strstr << "conflicting output locations with previously defined output '" - << mapEntry->second->getSymbol() << "'"; - - error(symbol->getLine(), strstr.str().c_str(), name.c_str()); + error(&errorCount, sink, *symbol, + elementCount > 1 ? "output array locations would exceed MAX_DRAW_BUFFERS" + : "output location must be < MAX_DRAW_BUFFERS"); } } } -} -void ValidateOutputs::error(TSourceLoc loc, const char *reason, const char* token) -{ - mSink.prefix(EPrefixError); - mSink.location(loc); - mSink << "'" << token << "' : " << reason << "\n"; - mNumErrors++; + if (!mAllowUnspecifiedOutputLocationResolution && + ((!mOutputs.empty() && !mUnspecifiedLocationOutputs.empty()) || + mUnspecifiedLocationOutputs.size() > 1)) + { + for (const auto &symbol : mUnspecifiedLocationOutputs) + { + error(&errorCount, sink, *symbol, + "must explicitly specify all locations when using multiple fragment outputs"); + } + } + return errorCount; } diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.h b/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.h index 1538e0f157..06f63994cd 100644 --- a/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.h +++ b/src/3rdparty/angle/src/compiler/translator/ValidateOutputs.h @@ -7,6 +7,7 @@ #ifndef COMPILER_TRANSLATOR_VALIDATEOUTPUTS_H_ #define COMPILER_TRANSLATOR_VALIDATEOUTPUTS_H_ +#include "compiler/translator/ExtensionBehavior.h" #include "compiler/translator/IntermNode.h" #include @@ -16,23 +17,20 @@ class TInfoSinkBase; class ValidateOutputs : public TIntermTraverser { public: - ValidateOutputs(TInfoSinkBase& sink, int maxDrawBuffers); + ValidateOutputs(const TExtensionBehavior &extBehavior, int maxDrawBuffers); - int numErrors() const { return mNumErrors; } + int validateAndCountErrors(TInfoSinkBase &sink) const; - virtual void visitSymbol(TIntermSymbol*); + void visitSymbol(TIntermSymbol *) override; private: - TInfoSinkBase& mSink; int mMaxDrawBuffers; - int mNumErrors; - bool mHasUnspecifiedOutputLocation; + bool mAllowUnspecifiedOutputLocationResolution; - typedef std::map OutputMap; - OutputMap mOutputMap; + typedef std::vector OutputVector; + OutputVector mOutputs; + OutputVector mUnspecifiedLocationOutputs; std::set mVisitedSymbols; - - void error(TSourceLoc loc, const char *reason, const char* token); }; #endif // COMPILER_TRANSLATOR_VALIDATEOUTPUTS_H_ diff --git a/src/3rdparty/angle/src/compiler/translator/ValidateSwitch.h b/src/3rdparty/angle/src/compiler/translator/ValidateSwitch.h index 88b68a500e..ddbefc5619 100644 --- a/src/3rdparty/angle/src/compiler/translator/ValidateSwitch.h +++ b/src/3rdparty/angle/src/compiler/translator/ValidateSwitch.h @@ -9,7 +9,7 @@ #include "compiler/translator/IntermNode.h" -struct TParseContext; +class TParseContext; class ValidateSwitch : public TIntermTraverser { diff --git a/src/3rdparty/angle/src/compiler/translator/VariableInfo.cpp b/src/3rdparty/angle/src/compiler/translator/VariableInfo.cpp index cf229ec96a..3b6aa6a68e 100644 --- a/src/3rdparty/angle/src/compiler/translator/VariableInfo.cpp +++ b/src/3rdparty/angle/src/compiler/translator/VariableInfo.cpp @@ -16,18 +16,6 @@ namespace sh namespace { -TString InterfaceBlockFieldName(const TInterfaceBlock &interfaceBlock, const TField &field) -{ - if (interfaceBlock.hasInstanceName()) - { - return interfaceBlock.name() + "." + field.name(); - } - else - { - return field.name(); - } -} - BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage) { switch (blockStorage) @@ -55,7 +43,8 @@ void ExpandVariable(const ShaderVariable &variable, { if (variable.isArray()) { - for (size_t elementIndex = 0; elementIndex < variable.elementCount(); elementIndex++) + for (unsigned int elementIndex = 0; elementIndex < variable.elementCount(); + elementIndex++) { std::string lname = name + ::ArrayString(elementIndex); std::string lmappedName = mappedName + ::ArrayString(elementIndex); @@ -128,17 +117,19 @@ VarT *FindVariable(const TString &name, } CollectVariables::CollectVariables(std::vector *attribs, - std::vector *outputVariables, + std::vector *outputVariables, std::vector *uniforms, std::vector *varyings, std::vector *interfaceBlocks, ShHashFunction64 hashFunction, const TSymbolTable &symbolTable) - : mAttribs(attribs), + : TIntermTraverser(true, false, false), + mAttribs(attribs), mOutputVariables(outputVariables), mUniforms(uniforms), mVaryings(varyings), mInterfaceBlocks(interfaceBlocks), + mDepthRangeAdded(false), mPointCoordAdded(false), mFrontFacingAdded(false), mFragCoordAdded(false), @@ -146,6 +137,12 @@ CollectVariables::CollectVariables(std::vector *attribs, mPositionAdded(false), mPointSizeAdded(false), mLastFragDataAdded(false), + mFragColorAdded(false), + mFragDataAdded(false), + mFragDepthEXTAdded(false), + mFragDepthAdded(false), + mSecondaryFragColorEXTAdded(false), + mSecondaryFragDataEXTAdded(false), mHashFunction(hashFunction), mSymbolTable(symbolTable) { @@ -170,6 +167,56 @@ void CollectVariables::visitSymbol(TIntermSymbol *symbol) { UNREACHABLE(); } + else if (symbolName == "gl_DepthRange") + { + ASSERT(symbol->getQualifier() == EvqUniform); + + if (!mDepthRangeAdded) + { + Uniform info; + const char kName[] = "gl_DepthRange"; + info.name = kName; + info.mappedName = kName; + info.type = GL_STRUCT_ANGLEX; + info.arraySize = 0; + info.precision = GL_NONE; + info.staticUse = true; + + ShaderVariable nearInfo; + const char kNearName[] = "near"; + nearInfo.name = kNearName; + nearInfo.mappedName = kNearName; + nearInfo.type = GL_FLOAT; + nearInfo.arraySize = 0; + nearInfo.precision = GL_HIGH_FLOAT; + nearInfo.staticUse = true; + + ShaderVariable farInfo; + const char kFarName[] = "far"; + farInfo.name = kFarName; + farInfo.mappedName = kFarName; + farInfo.type = GL_FLOAT; + farInfo.arraySize = 0; + farInfo.precision = GL_HIGH_FLOAT; + farInfo.staticUse = true; + + ShaderVariable diffInfo; + const char kDiffName[] = "diff"; + diffInfo.name = kDiffName; + diffInfo.mappedName = kDiffName; + diffInfo.type = GL_FLOAT; + diffInfo.arraySize = 0; + diffInfo.precision = GL_HIGH_FLOAT; + diffInfo.staticUse = true; + + info.fields.push_back(nearInfo); + info.fields.push_back(farInfo); + info.fields.push_back(diffInfo); + + mUniforms->push_back(info); + mDepthRangeAdded = true; + } + } else { switch (symbol->getQualifier()) @@ -192,7 +239,6 @@ void CollectVariables::visitSymbol(TIntermSymbol *symbol) // Set static use on the parent interface block here namedBlock->staticUse = true; - } else { @@ -200,7 +246,7 @@ void CollectVariables::visitSymbol(TIntermSymbol *symbol) } // It's an internal error to reference an undefined user uniform - ASSERT(symbolName.compare(0, 3, "gl_") == 0 || var); + ASSERT(symbolName.compare(0, 3, "gl_") != 0 || var); } break; case EvqFragCoord: @@ -315,6 +361,105 @@ void CollectVariables::visitSymbol(TIntermSymbol *symbol) mLastFragDataAdded = true; } return; + case EvqFragColor: + if (!mFragColorAdded) + { + OutputVariable info; + const char kName[] = "gl_FragColor"; + info.name = kName; + info.mappedName = kName; + info.type = GL_FLOAT_VEC4; + info.arraySize = 0; + info.precision = GL_MEDIUM_FLOAT; // Defined by spec. + info.staticUse = true; + mOutputVariables->push_back(info); + mFragColorAdded = true; + } + return; + case EvqFragData: + if (!mFragDataAdded) + { + OutputVariable info; + const char kName[] = "gl_FragData"; + info.name = kName; + info.mappedName = kName; + info.type = GL_FLOAT_VEC4; + info.arraySize = static_cast( + mSymbolTable.findBuiltIn("gl_MaxDrawBuffers", 100)) + ->getConstPointer() + ->getIConst(); + info.precision = GL_MEDIUM_FLOAT; // Defined by spec. + info.staticUse = true; + mOutputVariables->push_back(info); + mFragDataAdded = true; + } + return; + case EvqFragDepthEXT: + if (!mFragDepthEXTAdded) + { + OutputVariable info; + const char kName[] = "gl_FragDepthEXT"; + info.name = kName; + info.mappedName = kName; + info.type = GL_FLOAT; + info.arraySize = 0; + info.precision = + GLVariablePrecision(static_cast( + mSymbolTable.findBuiltIn("gl_FragDepthEXT", 100)) + ->getType()); + info.staticUse = true; + mOutputVariables->push_back(info); + mFragDepthEXTAdded = true; + } + return; + case EvqFragDepth: + if (!mFragDepthAdded) + { + OutputVariable info; + const char kName[] = "gl_FragDepth"; + info.name = kName; + info.mappedName = kName; + info.type = GL_FLOAT; + info.arraySize = 0; + info.precision = GL_HIGH_FLOAT; + info.staticUse = true; + mOutputVariables->push_back(info); + mFragDepthAdded = true; + } + return; + case EvqSecondaryFragColorEXT: + if (!mSecondaryFragColorEXTAdded) + { + OutputVariable info; + const char kName[] = "gl_SecondaryFragColorEXT"; + info.name = kName; + info.mappedName = kName; + info.type = GL_FLOAT_VEC4; + info.arraySize = 0; + info.precision = GL_MEDIUM_FLOAT; // Defined by spec. + info.staticUse = true; + mOutputVariables->push_back(info); + mSecondaryFragColorEXTAdded = true; + } + return; + case EvqSecondaryFragDataEXT: + if (!mSecondaryFragDataEXTAdded) + { + OutputVariable info; + const char kName[] = "gl_SecondaryFragDataEXT"; + info.name = kName; + info.mappedName = kName; + info.type = GL_FLOAT_VEC4; + + const TVariable *maxDualSourceDrawBuffersVar = static_cast( + mSymbolTable.findBuiltIn("gl_MaxDualSourceDrawBuffersEXT", 100)); + info.arraySize = maxDualSourceDrawBuffersVar->getConstPointer()->getIConst(); + info.precision = GL_MEDIUM_FLOAT; // Defined by spec. + info.staticUse = true; + mOutputVariables->push_back(info); + mSecondaryFragDataEXTAdded = true; + } + return; default: break; } @@ -335,7 +480,7 @@ class NameHashingTraverser : public GetVariableTraverser {} private: - virtual void visitVariable(ShaderVariable *variable) + void visitVariable(ShaderVariable *variable) override { TString stringName = TString(variable->name.c_str()); variable->mappedName = TIntermTraverser::hash(stringName, mHashFunction).c_str(); @@ -365,6 +510,26 @@ void CollectVariables::visitVariable(const TIntermSymbol *variable, infoList->push_back(attribute); } +template <> +void CollectVariables::visitVariable(const TIntermSymbol *variable, + std::vector *infoList) const +{ + ASSERT(variable); + const TType &type = variable->getType(); + ASSERT(!type.getStruct()); + + OutputVariable attribute; + + attribute.type = GLVariableType(type); + attribute.precision = GLVariablePrecision(type); + attribute.name = variable->getSymbol().c_str(); + attribute.arraySize = static_cast(type.getArraySize()); + attribute.mappedName = TIntermTraverser::hash(variable->getSymbol(), mHashFunction).c_str(); + attribute.location = variable->getType().getLayoutQualifier().location; + + infoList->push_back(attribute); +} + template <> void CollectVariables::visitVariable(const TIntermSymbol *variable, std::vector *infoList) const @@ -374,23 +539,20 @@ void CollectVariables::visitVariable(const TIntermSymbol *variable, ASSERT(blockType); interfaceBlock.name = blockType->name().c_str(); - interfaceBlock.mappedName = TIntermTraverser::hash(variable->getSymbol(), mHashFunction).c_str(); + interfaceBlock.mappedName = + TIntermTraverser::hash(blockType->name().c_str(), mHashFunction).c_str(); interfaceBlock.instanceName = (blockType->hasInstanceName() ? blockType->instanceName().c_str() : ""); interfaceBlock.arraySize = variable->getArraySize(); interfaceBlock.isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor); interfaceBlock.layout = GetBlockLayoutType(blockType->blockStorage()); // Gather field information - const TFieldList &fieldList = blockType->fields(); - - for (size_t fieldIndex = 0; fieldIndex < fieldList.size(); ++fieldIndex) + for (const TField *field : blockType->fields()) { - const TField &field = *fieldList[fieldIndex]; - const TString &fullFieldName = InterfaceBlockFieldName(*blockType, field); - const TType &fieldType = *field.type(); + const TType &fieldType = *field->type(); - GetVariableTraverser traverser(mSymbolTable); - traverser.traverse(fieldType, fullFieldName, &interfaceBlock.fields); + NameHashingTraverser traverser(mHashFunction, mSymbolTable); + traverser.traverse(fieldType, field->name(), &interfaceBlock.fields); interfaceBlock.fields.back().isRowMajorLayout = (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor); } diff --git a/src/3rdparty/angle/src/compiler/translator/VariableInfo.h b/src/3rdparty/angle/src/compiler/translator/VariableInfo.h index bb1328a507..9498e9b3a0 100644 --- a/src/3rdparty/angle/src/compiler/translator/VariableInfo.h +++ b/src/3rdparty/angle/src/compiler/translator/VariableInfo.h @@ -21,16 +21,16 @@ class CollectVariables : public TIntermTraverser { public: CollectVariables(std::vector *attribs, - std::vector *outputVariables, + std::vector *outputVariables, std::vector *uniforms, std::vector *varyings, std::vector *interfaceBlocks, ShHashFunction64 hashFunction, const TSymbolTable &symbolTable); - virtual void visitSymbol(TIntermSymbol *symbol); - virtual bool visitAggregate(Visit, TIntermAggregate *node); - virtual bool visitBinary(Visit visit, TIntermBinary *binaryNode); + void visitSymbol(TIntermSymbol *symbol) override; + bool visitAggregate(Visit, TIntermAggregate *node) override; + bool visitBinary(Visit visit, TIntermBinary *binaryNode) override; private: template @@ -40,13 +40,14 @@ class CollectVariables : public TIntermTraverser void visitInfoList(const TIntermSequence &sequence, std::vector *infoList) const; std::vector *mAttribs; - std::vector *mOutputVariables; + std::vector *mOutputVariables; std::vector *mUniforms; std::vector *mVaryings; std::vector *mInterfaceBlocks; std::map mInterfaceBlockFields; + bool mDepthRangeAdded; bool mPointCoordAdded; bool mFrontFacingAdded; bool mFragCoordAdded; @@ -55,6 +56,12 @@ class CollectVariables : public TIntermTraverser bool mPositionAdded; bool mPointSizeAdded; bool mLastFragDataAdded; + bool mFragColorAdded; + bool mFragDataAdded; + bool mFragDepthEXTAdded; + bool mFragDepthAdded; + bool mSecondaryFragColorEXTAdded; + bool mSecondaryFragDataEXTAdded; ShHashFunction64 mHashFunction; diff --git a/src/3rdparty/angle/src/compiler/translator/VersionGLSL.cpp b/src/3rdparty/angle/src/compiler/translator/VersionGLSL.cpp index f6f568897d..c8718daa10 100644 --- a/src/3rdparty/angle/src/compiler/translator/VersionGLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/VersionGLSL.cpp @@ -6,9 +6,24 @@ #include "compiler/translator/VersionGLSL.h" -static const int GLSL_VERSION_110 = 110; -static const int GLSL_VERSION_120 = 120; -static const int GLSL_VERSION_150 = 150; +int ShaderOutputTypeToGLSLVersion(ShShaderOutput output) +{ + switch (output) + { + case SH_GLSL_130_OUTPUT: return GLSL_VERSION_130; + case SH_GLSL_140_OUTPUT: return GLSL_VERSION_140; + case SH_GLSL_150_CORE_OUTPUT: return GLSL_VERSION_150; + case SH_GLSL_330_CORE_OUTPUT: return GLSL_VERSION_330; + case SH_GLSL_400_CORE_OUTPUT: return GLSL_VERSION_400; + case SH_GLSL_410_CORE_OUTPUT: return GLSL_VERSION_410; + case SH_GLSL_420_CORE_OUTPUT: return GLSL_VERSION_420; + case SH_GLSL_430_CORE_OUTPUT: return GLSL_VERSION_430; + case SH_GLSL_440_CORE_OUTPUT: return GLSL_VERSION_440; + case SH_GLSL_450_CORE_OUTPUT: return GLSL_VERSION_450; + case SH_GLSL_COMPATIBILITY_OUTPUT: return GLSL_VERSION_110; + default: UNREACHABLE(); return 0; + } +} // We need to scan for the following: // 1. "invariant" keyword: This can occur in both - vertex and fragment shaders @@ -30,25 +45,21 @@ static const int GLSL_VERSION_150 = 150; TVersionGLSL::TVersionGLSL(sh::GLenum type, const TPragma &pragma, ShShaderOutput output) + : TIntermTraverser(true, false, false) { - if (output == SH_GLSL_CORE_OUTPUT) + mVersion = ShaderOutputTypeToGLSLVersion(output); + if (pragma.stdgl.invariantAll) { - mVersion = GLSL_VERSION_150; - } - else - { - ASSERT(output == SH_GLSL_COMPATIBILITY_OUTPUT); - if (pragma.stdgl.invariantAll) - mVersion = GLSL_VERSION_120; - else - mVersion = GLSL_VERSION_110; + ensureVersionIsAtLeast(GLSL_VERSION_120); } } void TVersionGLSL::visitSymbol(TIntermSymbol *node) { if (node->getSymbol() == "gl_PointCoord") - updateVersion(GLSL_VERSION_120); + { + ensureVersionIsAtLeast(GLSL_VERSION_120); + } } bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate *node) @@ -64,16 +75,14 @@ bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate *node) case EOpDeclaration: { const TIntermSequence &sequence = *(node->getSequence()); - TQualifier qualifier = sequence.front()->getAsTyped()->getQualifier(); - if ((qualifier == EvqInvariantVaryingIn) || - (qualifier == EvqInvariantVaryingOut)) + if (sequence.front()->getAsTyped()->getType().isInvariant()) { - updateVersion(GLSL_VERSION_120); + ensureVersionIsAtLeast(GLSL_VERSION_120); } break; } case EOpInvariantDeclaration: - updateVersion(GLSL_VERSION_120); + ensureVersionIsAtLeast(GLSL_VERSION_120); break; case EOpParameters: { @@ -87,7 +96,7 @@ bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate *node) TQualifier qualifier = param->getQualifier(); if ((qualifier == EvqOut) || (qualifier == EvqInOut)) { - updateVersion(GLSL_VERSION_120); + ensureVersionIsAtLeast(GLSL_VERSION_120); break; } } @@ -97,7 +106,13 @@ bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate *node) break; } case EOpConstructMat2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: case EOpConstructMat3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: case EOpConstructMat4: { const TIntermSequence &sequence = *(node->getSequence()); @@ -106,7 +121,7 @@ bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate *node) TIntermTyped *typed = sequence.front()->getAsTyped(); if (typed && typed->isMatrix()) { - updateVersion(GLSL_VERSION_120); + ensureVersionIsAtLeast(GLSL_VERSION_120); } } break; @@ -118,7 +133,7 @@ bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate *node) return visitChildren; } -void TVersionGLSL::updateVersion(int version) +void TVersionGLSL::ensureVersionIsAtLeast(int version) { mVersion = std::max(version, mVersion); } diff --git a/src/3rdparty/angle/src/compiler/translator/VersionGLSL.h b/src/3rdparty/angle/src/compiler/translator/VersionGLSL.h index 2b63d5f25d..c41069d42d 100644 --- a/src/3rdparty/angle/src/compiler/translator/VersionGLSL.h +++ b/src/3rdparty/angle/src/compiler/translator/VersionGLSL.h @@ -11,6 +11,21 @@ #include "compiler/translator/Pragma.h" +static const int GLSL_VERSION_110 = 110; +static const int GLSL_VERSION_120 = 120; +static const int GLSL_VERSION_130 = 130; +static const int GLSL_VERSION_140 = 140; +static const int GLSL_VERSION_150 = 150; +static const int GLSL_VERSION_330 = 330; +static const int GLSL_VERSION_400 = 400; +static const int GLSL_VERSION_410 = 410; +static const int GLSL_VERSION_420 = 420; +static const int GLSL_VERSION_430 = 430; +static const int GLSL_VERSION_440 = 440; +static const int GLSL_VERSION_450 = 450; + +int ShaderOutputTypeToGLSLVersion(ShShaderOutput output); + // Traverses the intermediate tree to return the minimum GLSL version // required to legally access all built-in features used in the shader. // GLSL 1.1 which is mandated by OpenGL 2.0 provides: @@ -39,15 +54,14 @@ class TVersionGLSL : public TIntermTraverser // - matrix/matrix constructors // - array "out" parameters // Else 110 is returned. - int getVersion() { return mVersion; } + int getVersion() const { return mVersion; } - virtual void visitSymbol(TIntermSymbol *); - virtual bool visitAggregate(Visit, TIntermAggregate *); - - protected: - void updateVersion(int version); + void visitSymbol(TIntermSymbol *) override; + bool visitAggregate(Visit, TIntermAggregate *) override; private: + void ensureVersionIsAtLeast(int version); + int mVersion; }; diff --git a/src/3rdparty/angle/src/compiler/translator/blocklayout.cpp b/src/3rdparty/angle/src/compiler/translator/blocklayout.cpp index 7c74105680..ba6322848e 100644 --- a/src/3rdparty/angle/src/compiler/translator/blocklayout.cpp +++ b/src/3rdparty/angle/src/compiler/translator/blocklayout.cpp @@ -27,7 +27,10 @@ BlockMemberInfo BlockLayoutEncoder::encodeType(GLenum type, unsigned int arraySi getBlockLayoutInfo(type, arraySize, isRowMajorMatrix, &arrayStride, &matrixStride); - const BlockMemberInfo memberInfo(mCurrentOffset * BytesPerComponent, arrayStride * BytesPerComponent, matrixStride * BytesPerComponent, isRowMajorMatrix); + const BlockMemberInfo memberInfo(static_cast(mCurrentOffset * BytesPerComponent), + static_cast(arrayStride * BytesPerComponent), + static_cast(matrixStride * BytesPerComponent), + isRowMajorMatrix); advanceOffset(type, arraySize, isRowMajorMatrix, arrayStride, matrixStride); diff --git a/src/3rdparty/angle/src/compiler/translator/blocklayout.h b/src/3rdparty/angle/src/compiler/translator/blocklayout.h index c11357fe66..dd5fe07376 100644 --- a/src/3rdparty/angle/src/compiler/translator/blocklayout.h +++ b/src/3rdparty/angle/src/compiler/translator/blocklayout.h @@ -26,6 +26,8 @@ struct InterfaceBlock; struct COMPILER_EXPORT BlockMemberInfo { + BlockMemberInfo() : offset(-1), arrayStride(-1), matrixStride(-1), isRowMajorMatrix(false) {} + BlockMemberInfo(int offset, int arrayStride, int matrixStride, bool isRowMajorMatrix) : offset(offset), arrayStride(arrayStride), @@ -48,12 +50,11 @@ class COMPILER_EXPORT BlockLayoutEncoder { public: BlockLayoutEncoder(); + virtual ~BlockLayoutEncoder() {} BlockMemberInfo encodeType(GLenum type, unsigned int arraySize, bool isRowMajorMatrix); size_t getBlockSize() const { return mCurrentOffset * BytesPerComponent; } - size_t getCurrentRegister() const { return mCurrentOffset / ComponentsPerRegister; } - size_t getCurrentElement() const { return mCurrentOffset % ComponentsPerRegister; } virtual void enterAggregateType() = 0; virtual void exitAggregateType() = 0; @@ -81,12 +82,20 @@ class COMPILER_EXPORT Std140BlockEncoder : public BlockLayoutEncoder public: Std140BlockEncoder(); - virtual void enterAggregateType(); - virtual void exitAggregateType(); + void enterAggregateType() override; + void exitAggregateType() override; protected: - virtual void getBlockLayoutInfo(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut); - virtual void advanceOffset(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int arrayStride, int matrixStride); + void getBlockLayoutInfo(GLenum type, + unsigned int arraySize, + bool isRowMajorMatrix, + int *arrayStrideOut, + int *matrixStrideOut) override; + void advanceOffset(GLenum type, + unsigned int arraySize, + bool isRowMajorMatrix, + int arrayStride, + int matrixStride) override; }; } diff --git a/src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.cpp b/src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.cpp index f32cf2cf89..43119248eb 100644 --- a/src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.cpp +++ b/src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.cpp @@ -113,9 +113,14 @@ HLSLBlockEncoder::HLSLBlockEncoderStrategy HLSLBlockEncoder::GetStrategyFor(ShSh { switch (outputType) { - case SH_HLSL9_OUTPUT: return ENCODE_LOOSE; - case SH_HLSL11_OUTPUT: return ENCODE_PACKED; - default: UNREACHABLE(); return ENCODE_PACKED; + case SH_HLSL_3_0_OUTPUT: + return ENCODE_LOOSE; + case SH_HLSL_4_1_OUTPUT: + case SH_HLSL_4_0_FL9_3_OUTPUT: + return ENCODE_PACKED; + default: + UNREACHABLE(); + return ENCODE_PACKED; } } @@ -156,6 +161,7 @@ unsigned int HLSLVariableRegisterCount(const Varying &variable, bool transposeMa unsigned int HLSLVariableRegisterCount(const Uniform &variable, ShShaderOutput outputType) { HLSLBlockEncoder encoder(HLSLBlockEncoder::GetStrategyFor(outputType)); + encoder.setTransposeMatrices(true); HLSLVariableRegisterCount(variable, &encoder); const size_t registerBytes = (encoder.BytesPerComponent * encoder.ComponentsPerRegister); diff --git a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.cpp b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.cpp index 19ddf5c439..4dee0dbd2e 100644 --- a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.cpp +++ b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.cpp @@ -4,8 +4,6 @@ // found in the LICENSE file. // -#pragma warning(disable: 4718) - #include "compiler/translator/depgraph/DependencyGraph.h" #include "compiler/translator/depgraph/DependencyGraphBuilder.h" diff --git a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.h b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.h index 22db633678..2f7f7b9ab8 100644 --- a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.h +++ b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.h @@ -46,9 +46,10 @@ protected: class TGraphParentNode : public TGraphNode { public: TGraphParentNode(TIntermNode* node) : TGraphNode(node) {} - virtual ~TGraphParentNode() {} + ~TGraphParentNode() override {} void addDependentNode(TGraphNode* node) { if (node != this) mDependentNodes.insert(node); } - virtual void traverse(TDependencyGraphTraverser* graphTraverser); + void traverse(TDependencyGraphTraverser *graphTraverser) override; + private: TGraphNodeSet mDependentNodes; }; @@ -61,10 +62,11 @@ public: TGraphArgument(TIntermAggregate* intermFunctionCall, int argumentNumber) : TGraphParentNode(intermFunctionCall) , mArgumentNumber(argumentNumber) {} - virtual ~TGraphArgument() {} + ~TGraphArgument() override {} const TIntermAggregate* getIntermFunctionCall() const { return intermNode->getAsAggregate(); } int getArgumentNumber() const { return mArgumentNumber; } - virtual void traverse(TDependencyGraphTraverser* graphTraverser); + void traverse(TDependencyGraphTraverser *graphTraverser) override; + private: int mArgumentNumber; }; @@ -76,9 +78,9 @@ class TGraphFunctionCall : public TGraphParentNode { public: TGraphFunctionCall(TIntermAggregate* intermFunctionCall) : TGraphParentNode(intermFunctionCall) {} - virtual ~TGraphFunctionCall() {} + ~TGraphFunctionCall() override {} const TIntermAggregate* getIntermFunctionCall() const { return intermNode->getAsAggregate(); } - virtual void traverse(TDependencyGraphTraverser* graphTraverser); + void traverse(TDependencyGraphTraverser *graphTraverser) override; }; // @@ -87,9 +89,9 @@ public: class TGraphSymbol : public TGraphParentNode { public: TGraphSymbol(TIntermSymbol* intermSymbol) : TGraphParentNode(intermSymbol) {} - virtual ~TGraphSymbol() {} + ~TGraphSymbol() override {} const TIntermSymbol* getIntermSymbol() const { return intermNode->getAsSymbolNode(); } - virtual void traverse(TDependencyGraphTraverser* graphTraverser); + void traverse(TDependencyGraphTraverser *graphTraverser) override; }; // @@ -98,9 +100,9 @@ public: class TGraphSelection : public TGraphNode { public: TGraphSelection(TIntermSelection* intermSelection) : TGraphNode(intermSelection) {} - virtual ~TGraphSelection() {} + ~TGraphSelection() override {} const TIntermSelection* getIntermSelection() const { return intermNode->getAsSelectionNode(); } - virtual void traverse(TDependencyGraphTraverser* graphTraverser); + void traverse(TDependencyGraphTraverser *graphTraverser) override; }; // @@ -109,9 +111,9 @@ public: class TGraphLoop : public TGraphNode { public: TGraphLoop(TIntermLoop* intermLoop) : TGraphNode(intermLoop) {} - virtual ~TGraphLoop() {} + ~TGraphLoop() override {} const TIntermLoop* getIntermLoop() const { return intermNode->getAsLoopNode(); } - virtual void traverse(TDependencyGraphTraverser* graphTraverser); + void traverse(TDependencyGraphTraverser *graphTraverser) override; }; // @@ -120,10 +122,10 @@ public: class TGraphLogicalOp : public TGraphNode { public: TGraphLogicalOp(TIntermBinary* intermLogicalOp) : TGraphNode(intermLogicalOp) {} - virtual ~TGraphLogicalOp() {} + ~TGraphLogicalOp() override {} const TIntermBinary* getIntermLogicalOp() const { return intermNode->getAsBinaryNode(); } const char* getOpString() const; - virtual void traverse(TDependencyGraphTraverser* graphTraverser); + void traverse(TDependencyGraphTraverser *graphTraverser) override; }; // @@ -140,27 +142,11 @@ class TDependencyGraph { public: TDependencyGraph(TIntermNode* intermNode); ~TDependencyGraph(); - TGraphNodeVector::const_iterator begin() const { return mAllNodes.begin(); } - TGraphNodeVector::const_iterator end() const { return mAllNodes.end(); } - - TGraphSymbolVector::const_iterator beginSamplerSymbols() const - { - return mSamplerSymbols.begin(); - } - - TGraphSymbolVector::const_iterator endSamplerSymbols() const - { - return mSamplerSymbols.end(); - } - - TFunctionCallVector::const_iterator beginUserDefinedFunctionCalls() const - { - return mUserDefinedFunctionCalls.begin(); - } - - TFunctionCallVector::const_iterator endUserDefinedFunctionCalls() const + const TGraphNodeVector &allNodes() const { return mAllNodes; } + const TGraphSymbolVector &samplerSymbols() const { return mSamplerSymbols; } + const TFunctionCallVector &userDefinedFunctionCalls() const { - return mUserDefinedFunctionCalls.end(); + return mUserDefinedFunctionCalls; } TGraphArgument* createArgument(TIntermAggregate* intermFunctionCall, int argumentNumber); @@ -189,6 +175,7 @@ private: class TDependencyGraphTraverser : angle::NonCopyable { public: TDependencyGraphTraverser() : mDepth(0) {} + virtual ~TDependencyGraphTraverser() {} virtual void visitSymbol(TGraphSymbol* symbol) {}; virtual void visitArgument(TGraphArgument* selection) {}; diff --git a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.h b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.h index f7b3bd4b43..c7b54f66b7 100644 --- a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.h +++ b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.h @@ -18,11 +18,11 @@ class TDependencyGraphBuilder : public TIntermTraverser public: static void build(TIntermNode *node, TDependencyGraph *graph); - virtual void visitSymbol(TIntermSymbol *); - virtual bool visitBinary(Visit visit, TIntermBinary *); - virtual bool visitSelection(Visit visit, TIntermSelection *); - virtual bool visitAggregate(Visit visit, TIntermAggregate *); - virtual bool visitLoop(Visit visit, TIntermLoop *); + void visitSymbol(TIntermSymbol *) override; + bool visitBinary(Visit visit, TIntermBinary *) override; + bool visitSelection(Visit visit, TIntermSelection *) override; + bool visitAggregate(Visit visit, TIntermAggregate *) override; + bool visitLoop(Visit visit, TIntermLoop *) override; private: typedef std::stack TSymbolStack; diff --git a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp index e226333545..32a2f30141 100644 --- a/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp +++ b/src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp @@ -54,9 +54,8 @@ void TDependencyGraphOutput::outputAllSpanningTrees(TDependencyGraph& graph) { mSink << "\n"; - for (TGraphNodeVector::const_iterator iter = graph.begin(); iter != graph.end(); ++iter) + for (auto symbol : graph.allNodes()) { - TGraphNode* symbol = *iter; mSink << "--- Dependency graph spanning tree ---\n"; clearVisited(); symbol->traverse(this); diff --git a/src/3rdparty/angle/src/compiler/translator/glslang.h b/src/3rdparty/angle/src/compiler/translator/glslang.h index db31e6946c..0555e96d45 100644 --- a/src/3rdparty/angle/src/compiler/translator/glslang.h +++ b/src/3rdparty/angle/src/compiler/translator/glslang.h @@ -7,7 +7,7 @@ #ifndef COMPILER_TRANSLATOR_GLSLANG_H_ #define COMPILER_TRANSLATOR_GLSLANG_H_ -struct TParseContext; +class TParseContext; extern int glslang_initialize(TParseContext* context); extern int glslang_finalize(TParseContext* context); diff --git a/src/3rdparty/angle/src/compiler/translator/glslang.l b/src/3rdparty/angle/src/compiler/translator/glslang.l index ee4d28b6d6..d09358dd8a 100644 --- a/src/3rdparty/angle/src/compiler/translator/glslang.l +++ b/src/3rdparty/angle/src/compiler/translator/glslang.l @@ -28,8 +28,10 @@ WHICH GENERATES THE GLSL ES LEXER (glslang_lex.cpp). #pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic ignored "-Wswitch-enum" #elif defined(_MSC_VER) +#pragma warning(disable: 4005) #pragma warning(disable: 4065) #pragma warning(disable: 4189) +#pragma warning(disable: 4244) #pragma warning(disable: 4505) #pragma warning(disable: 4701) #pragma warning(disable: 4702) @@ -49,6 +51,13 @@ WHICH GENERATES THE GLSL ES LEXER (glslang_lex.cpp). #pragma warning(disable : 4102) #endif +// Workaround for flex using the register keyword, deprecated in C++11. +#ifdef __cplusplus +#if __cplusplus > 199711L +#define register +#endif +#endif + #define YY_USER_ACTION \ yylloc->first_file = yylloc->last_file = yycolumn; \ yylloc->first_line = yylloc->last_line = yylineno; @@ -63,7 +72,7 @@ static int ES2_reserved_ES3_keyword(TParseContext *context, int token); static int ES2_keyword_ES3_reserved(TParseContext *context, int token); static int ES2_ident_ES3_keyword(TParseContext *context, int token); static int uint_constant(TParseContext *context); -static int int_constant(yyscan_t yyscanner); +static int int_constant(TParseContext *context); static int float_constant(yyscan_t yyscanner); static int floatsuffix_check(TParseContext* context); %} @@ -237,7 +246,7 @@ O [0-7] "sampler2DMSArray" | "isampler2DMSArray" | "usampler2DMSArray" { - if (context->shaderVersion < 300) { + if (context->getShaderVersion() < 300) { yylval->lex.string = NewPoolTString(yytext); return check_type(yyscanner); } @@ -246,7 +255,7 @@ O [0-7] /* Reserved keywords in GLSL ES 1.00 that are not reserved in GLSL ES 3.00 */ "packed" { - if (context->shaderVersion >= 300) + if (context->getShaderVersion() >= 300) { yylval->lex.string = NewPoolTString(yytext); return check_type(yyscanner); @@ -312,9 +321,9 @@ O [0-7] return check_type(yyscanner); } -0[xX]{H}+ { return int_constant(yyscanner); } -0{O}+ { return int_constant(yyscanner); } -{D}+ { return int_constant(yyscanner); } +0[xX]{H}+ { return int_constant(context); } +0{O}+ { return int_constant(context); } +{D}+ { return int_constant(context); } 0[xX]{H}+[uU] { return uint_constant(context); } 0{O}+[uU] { return uint_constant(context); } @@ -390,7 +399,7 @@ O [0-7] yy_size_t string_input(char* buf, yy_size_t max_size, yyscan_t yyscanner) { pp::Token token; - yyget_extra(yyscanner)->preprocessor.lex(&token); + yyget_extra(yyscanner)->getPreprocessor().lex(&token); yy_size_t len = token.type == pp::Token::LAST ? 0 : token.text.size(); if (len < max_size) memcpy(buf, token.text.c_str(), len); @@ -408,7 +417,7 @@ int check_type(yyscan_t yyscanner) { struct yyguts_t* yyg = (struct yyguts_t*) yyscanner; int token = IDENTIFIER; - TSymbol* symbol = yyextra->symbolTable.find(yytext, yyextra->shaderVersion); + TSymbol* symbol = yyextra->symbolTable.find(yytext, yyextra->getShaderVersion()); if (symbol && symbol->isVariable()) { TVariable* variable = static_cast(symbol); if (variable->isUserType()) { @@ -429,9 +438,9 @@ int reserved_word(yyscan_t yyscanner) { int ES2_reserved_ES3_keyword(TParseContext *context, int token) { - yyscan_t yyscanner = (yyscan_t) context->scanner; + yyscan_t yyscanner = (yyscan_t) context->getScanner(); - if (context->shaderVersion < 300) + if (context->getShaderVersion() < 300) { return reserved_word(yyscanner); } @@ -441,9 +450,9 @@ int ES2_reserved_ES3_keyword(TParseContext *context, int token) int ES2_keyword_ES3_reserved(TParseContext *context, int token) { - yyscan_t yyscanner = (yyscan_t) context->scanner; + yyscan_t yyscanner = (yyscan_t) context->getScanner(); - if (context->shaderVersion >= 300) + if (context->getShaderVersion() >= 300) { return reserved_word(yyscanner); } @@ -453,11 +462,11 @@ int ES2_keyword_ES3_reserved(TParseContext *context, int token) int ES2_ident_ES3_keyword(TParseContext *context, int token) { - struct yyguts_t* yyg = (struct yyguts_t*) context->scanner; - yyscan_t yyscanner = (yyscan_t) context->scanner; + struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner(); + yyscan_t yyscanner = (yyscan_t) context->getScanner(); // not a reserved word in GLSL ES 1.00, so could be used as an identifier/type name - if (context->shaderVersion < 300) + if (context->getShaderVersion() < 300) { yylval->lex.string = NewPoolTString(yytext); return check_type(yyscanner); @@ -468,34 +477,35 @@ int ES2_ident_ES3_keyword(TParseContext *context, int token) int uint_constant(TParseContext *context) { - struct yyguts_t* yyg = (struct yyguts_t*) context->scanner; - yyscan_t yyscanner = (yyscan_t) context->scanner; + struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner(); - if (context->shaderVersion < 300) + if (context->getShaderVersion() < 300) { context->error(*yylloc, "Unsigned integers are unsupported prior to GLSL ES 3.00", yytext, ""); context->recover(); return 0; } - if (!atoi_clamp(yytext, &(yylval->lex.i))) - yyextra->warning(*yylloc, "Integer overflow", yytext, ""); + if (!atoi_clamp(yytext, &(yylval->lex.u))) + yyextra->error(*yylloc, "Integer overflow", yytext, ""); return UINTCONSTANT; } int floatsuffix_check(TParseContext* context) { - struct yyguts_t* yyg = (struct yyguts_t*) context->scanner; + struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner(); - if (context->shaderVersion < 300) + if (context->getShaderVersion() < 300) { context->error(*yylloc, "Floating-point suffix unsupported prior to GLSL ES 3.00", yytext); context->recover(); return 0; } - if (!atof_clamp(yytext, &(yylval->lex.f))) + std::string text = yytext; + text.resize(text.size() - 1); + if (!strtof_clamp(text, &(yylval->lex.f))) yyextra->warning(*yylloc, "Float overflow", yytext, ""); return(FLOATCONSTANT); @@ -506,18 +516,25 @@ void yyerror(YYLTYPE* lloc, TParseContext* context, void *scanner, const char* r context->recover(); } -int int_constant(yyscan_t yyscanner) { - struct yyguts_t* yyg = (struct yyguts_t*) yyscanner; +int int_constant(TParseContext *context) { + struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner(); - if (!atoi_clamp(yytext, &(yylval->lex.i))) - yyextra->warning(*yylloc, "Integer overflow", yytext, ""); + unsigned int u; + if (!atoi_clamp(yytext, &u)) + { + if (context->getShaderVersion() >= 300) + yyextra->error(*yylloc, "Integer overflow", yytext, ""); + else + yyextra->warning(*yylloc, "Integer overflow", yytext, ""); + } + yylval->lex.i = static_cast(u); return INTCONSTANT; } int float_constant(yyscan_t yyscanner) { struct yyguts_t* yyg = (struct yyguts_t*) yyscanner; - if (!atof_clamp(yytext, &(yylval->lex.f))) + if (!strtof_clamp(yytext, &(yylval->lex.f))) yyextra->warning(*yylloc, "Float overflow", yytext, ""); return FLOATCONSTANT; } @@ -527,15 +544,15 @@ int glslang_initialize(TParseContext* context) { if (yylex_init_extra(context, &scanner)) return 1; - context->scanner = scanner; + context->setScanner(scanner); return 0; } int glslang_finalize(TParseContext* context) { - yyscan_t scanner = context->scanner; + yyscan_t scanner = context->getScanner(); if (scanner == NULL) return 0; - context->scanner = NULL; + context->setScanner(NULL); yylex_destroy(scanner); return 0; @@ -543,24 +560,26 @@ int glslang_finalize(TParseContext* context) { int glslang_scan(size_t count, const char* const string[], const int length[], TParseContext* context) { - yyrestart(NULL, context->scanner); - yyset_column(0, context->scanner); - yyset_lineno(1, context->scanner); + yyrestart(NULL, context->getScanner()); + yyset_column(0, context->getScanner()); + yyset_lineno(1, context->getScanner()); // Initialize preprocessor. - if (!context->preprocessor.init(count, string, length)) + pp::Preprocessor *preprocessor = &context->getPreprocessor(); + + if (!preprocessor->init(count, string, length)) return 1; // Define extension macros. const TExtensionBehavior& extBehavior = context->extensionBehavior(); for (TExtensionBehavior::const_iterator iter = extBehavior.begin(); iter != extBehavior.end(); ++iter) { - context->preprocessor.predefineMacro(iter->first.c_str(), 1); + preprocessor->predefineMacro(iter->first.c_str(), 1); } - if (context->fragmentPrecisionHigh) - context->preprocessor.predefineMacro("GL_FRAGMENT_PRECISION_HIGH", 1); + if (context->getFragmentPrecisionHigh()) + preprocessor->predefineMacro("GL_FRAGMENT_PRECISION_HIGH", 1); - context->preprocessor.setMaxTokenSize(GetGlobalMaxTokenSize(context->shaderSpec)); + preprocessor->setMaxTokenSize(GetGlobalMaxTokenSize(context->getShaderSpec())); return 0; } diff --git a/src/3rdparty/angle/src/compiler/translator/glslang.y b/src/3rdparty/angle/src/compiler/translator/glslang.y index 6024898cb0..aba2706311 100644 --- a/src/3rdparty/angle/src/compiler/translator/glslang.y +++ b/src/3rdparty/angle/src/compiler/translator/glslang.y @@ -30,12 +30,14 @@ WHICH GENERATES THE GLSL ES PARSER (glslang_tab.cpp AND glslang_tab.h). #elif defined(_MSC_VER) #pragma warning(disable: 4065) #pragma warning(disable: 4189) +#pragma warning(disable: 4244) #pragma warning(disable: 4505) #pragma warning(disable: 4701) #pragma warning(disable: 4702) #endif #include "angle_gl.h" +#include "compiler/translator/Cache.h" #include "compiler/translator/SymbolTable.h" #include "compiler/translator/ParseContext.h" #include "GLSLANG/ShaderLang.h" @@ -109,28 +111,28 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons } while (0) #define VERTEX_ONLY(S, L) { \ - if (context->shaderType != GL_VERTEX_SHADER) { \ + if (context->getShaderType() != GL_VERTEX_SHADER) { \ context->error(L, " supported in vertex shaders only ", S); \ context->recover(); \ } \ } #define FRAG_ONLY(S, L) { \ - if (context->shaderType != GL_FRAGMENT_SHADER) { \ + if (context->getShaderType() != GL_FRAGMENT_SHADER) { \ context->error(L, " supported in fragment shaders only ", S); \ context->recover(); \ } \ } #define ES2_ONLY(S, L) { \ - if (context->shaderVersion != 100) { \ + if (context->getShaderVersion() != 100) { \ context->error(L, " supported in GLSL ES 1.00 only ", S); \ context->recover(); \ } \ } #define ES3_ONLY(TOKEN, LINE, REASON) { \ - if (context->shaderVersion != 300) { \ + if (context->getShaderVersion() != 300) { \ context->error(LINE, REASON " supported in GLSL ES 3.00 only ", TOKEN); \ context->recover(); \ } \ @@ -176,10 +178,10 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons %type translation_unit function_definition %type statement simple_statement -%type statement_list compound_statement +%type statement_list compound_statement compound_statement_no_new_scope %type declaration_statement selection_statement expression_statement %type declaration external_declaration -%type for_init_statement compound_statement_no_new_scope +%type for_init_statement %type selection_rest_statement for_rest_statement %type switch_statement %type case_label @@ -213,21 +215,7 @@ identifier variable_identifier : IDENTIFIER { // The symbol table search was done in the lexical phase - const TVariable *variable = context->getNamedVariable(@1, $1.string, $1.symbol); - - if (variable->getType().getQualifier() == EvqConst) - { - ConstantUnion* constArray = variable->getConstPointer(); - TType t(variable->getType()); - $$ = context->intermediate.addConstantUnion(constArray, t, @1); - } - else - { - $$ = context->intermediate.addSymbol(variable->getUniqueId(), - variable->getName(), - variable->getType(), - @1); - } + $$ = context->parseVariableIdentifier(@1, $1.string, $1.symbol); // don't delete $1.string, it's used by error recovery, and the pool // pop will reclaim the memory @@ -239,22 +227,22 @@ primary_expression $$ = $1; } | INTCONSTANT { - ConstantUnion *unionArray = new ConstantUnion[1]; + TConstantUnion *unionArray = new TConstantUnion[1]; unionArray->setIConst($1.i); $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), @1); } | UINTCONSTANT { - ConstantUnion *unionArray = new ConstantUnion[1]; + TConstantUnion *unionArray = new TConstantUnion[1]; unionArray->setUConst($1.u); $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtUInt, EbpUndefined, EvqConst), @1); } | FLOATCONSTANT { - ConstantUnion *unionArray = new ConstantUnion[1]; + TConstantUnion *unionArray = new TConstantUnion[1]; unionArray->setFConst($1.f); $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConst), @1); } | BOOLCONSTANT { - ConstantUnion *unionArray = new ConstantUnion[1]; + TConstantUnion *unionArray = new TConstantUnion[1]; unionArray->setBConst($1.b); $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @1); } @@ -295,7 +283,7 @@ integer_expression function_call : function_call_or_method { bool fatalError = false; - $$ = context->addFunctionCallOrMethod($1.function, $1.intermNode, @1, &fatalError); + $$ = context->addFunctionCallOrMethod($1.function, $1.nodePair.node1, $1.nodePair.node2, @1, &fatalError); if (fatalError) { YYERROR; @@ -306,11 +294,12 @@ function_call function_call_or_method : function_call_generic { $$ = $1; + $$.nodePair.node2 = nullptr; } | postfix_expression DOT function_call_generic { - context->error(@3, "methods are not supported", ""); - context->recover(); + ES3_ONLY("", @3, "methods"); $$ = $3; + $$.nodePair.node2 = $1; } ; @@ -326,26 +315,26 @@ function_call_generic function_call_header_no_parameters : function_call_header VOID_TYPE { $$.function = $1; - $$.intermNode = 0; + $$.nodePair.node1 = nullptr; } | function_call_header { $$.function = $1; - $$.intermNode = 0; + $$.nodePair.node1 = nullptr; } ; function_call_header_with_parameters : function_call_header assignment_expression { - TParameter param = { 0, new TType($2->getType()) }; - $1->addParameter(param); + const TType *type = new TType($2->getType()); + $1->addParameter(TConstParameter(type)); $$.function = $1; - $$.intermNode = $2; + $$.nodePair.node1 = context->intermediate.makeAggregate($2, @2); } | function_call_header_with_parameters COMMA assignment_expression { - TParameter param = { 0, new TType($3->getType()) }; - $1.function->addParameter(param); + const TType *type = new TType($3->getType()); + $1.function->addParameter(TConstParameter(type)); $$.function = $1.function; - $$.intermNode = context->intermediate.growAggregate($1.intermNode, $3, @2); + $$.nodePair.node1 = context->intermediate.growAggregate($1.intermNode, $3, @2); } ; @@ -358,20 +347,23 @@ function_call_header // Grammar Note: Constructors look like functions, but are recognized as types. function_identifier - : type_specifier_nonarray { + : type_specifier_no_prec { + if ($1.array) { + ES3_ONLY("[]", @1, "array constructor"); + } $$ = context->addConstructorFunc($1); } | IDENTIFIER { if (context->reservedErrorCheck(@1, *$1.string)) context->recover(); - TType type(EbtVoid, EbpUndefined); + const TType *type = TCache::getType(EbtVoid, EbpUndefined); TFunction *function = new TFunction($1.string, type); $$ = function; } | FIELD_SELECTION { if (context->reservedErrorCheck(@1, *$1.string)) context->recover(); - TType type(EbtVoid, EbpUndefined); + const TType *type = TCache::getType(EbtVoid, EbpUndefined); TFunction *function = new TFunction($1.string, type); $$ = function; } @@ -517,18 +509,7 @@ logical_or_expression conditional_expression : logical_or_expression { $$ = $1; } | logical_or_expression QUESTION expression COLON assignment_expression { - if (context->boolErrorCheck(@2, $1)) - context->recover(); - - $$ = context->intermediate.addSelection($1, $3, $5, @2); - if ($3->getType() != $5->getType()) - $$ = 0; - - if ($$ == 0) { - context->binaryOpError(@2, ":", $3->getCompleteString(), $5->getCompleteString()); - context->recover(); - $$ = $5; - } + $$ = context->addTernarySelection($1, $3, $5, @2); } ; @@ -578,12 +559,7 @@ expression $$ = $1; } | expression COMMA assignment_expression { - $$ = context->intermediate.addComma($1, $3, @2); - if ($$ == 0) { - context->binaryOpError(@2, ",", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - $$ = $3; - } + $$ = context->addComma($1, $3, @2); } ; @@ -604,32 +580,8 @@ enter_struct ; declaration - : function_prototype SEMICOLON { - TFunction &function = *($1.function); - - TIntermAggregate *prototype = new TIntermAggregate; - prototype->setType(function.getReturnType()); - prototype->setName(function.getMangledName()); - - for (size_t i = 0; i < function.getParamCount(); i++) - { - const TParameter ¶m = function.getParam(i); - if (param.name != 0) - { - TVariable variable(param.name, *param.type); - - prototype = context->intermediate.growAggregate(prototype, context->intermediate.addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), @1), @1); - } - else - { - prototype = context->intermediate.growAggregate(prototype, context->intermediate.addSymbol(0, "", *param.type, @1), @1); - } - } - - prototype->setOp(EOpPrototype); - $$ = prototype; - - context->symbolTable.pop(); + : function_prototype SEMICOLON { + $$ = context->addFunctionPrototypeDeclaration(*($1.function), @1); } | init_declarator_list SEMICOLON { TIntermAggregate *aggNode = $1.intermAggregate; @@ -638,7 +590,7 @@ declaration $$ = aggNode; } | PRECISION precision_qualifier type_specifier_no_prec SEMICOLON { - if (($2 == EbpHigh) && (context->shaderType == GL_FRAGMENT_SHADER) && !context->fragmentPrecisionHigh) { + if (($2 == EbpHigh) && (context->getShaderType() == GL_FRAGMENT_SHADER) && !context->getFragmentPrecisionHigh()) { context->error(@1, "precision is not supported in fragment shader", "highp"); context->recover(); } @@ -668,57 +620,7 @@ declaration function_prototype : function_declarator RIGHT_PAREN { - // - // Multiple declarations of the same function are allowed. - // - // If this is a definition, the definition production code will check for redefinitions - // (we don't know at this point if it's a definition or not). - // - // Redeclarations are allowed. But, return types and parameter qualifiers must match. - // - TFunction* prevDec = static_cast(context->symbolTable.find($1->getMangledName(), context->shaderVersion)); - if (prevDec) { - if (prevDec->getReturnType() != $1->getReturnType()) { - context->error(@2, "overloaded functions must have the same return type", $1->getReturnType().getBasicString()); - context->recover(); - } - for (size_t i = 0; i < prevDec->getParamCount(); ++i) { - if (prevDec->getParam(i).type->getQualifier() != $1->getParam(i).type->getQualifier()) { - context->error(@2, "overloaded functions must have the same parameter qualifiers", $1->getParam(i).type->getQualifierString()); - context->recover(); - } - } - } - - // - // Check for previously declared variables using the same name. - // - TSymbol *prevSym = context->symbolTable.find($1->getName(), context->shaderVersion); - if (prevSym) - { - if (!prevSym->isFunction()) - { - context->error(@2, "redefinition", $1->getName().c_str(), "function"); - context->recover(); - } - } - else - { - // Insert the unmangled name to detect potential future redefinition as a variable. - TFunction *function = new TFunction(NewPoolTString($1->getName().c_str()), $1->getReturnType()); - context->symbolTable.getOuterLevel()->insertUnmangled(function); - } - - // - // If this is a redeclaration, it could also be a definition, - // in which case, we want to use the variable names from this one, and not the one that's - // being redeclared. So, pass back up this declaration, not the one in the symbol table. - // - $$.function = $1; - - // We're at the inner scope level of the function's arguments and body statement. - // Add the function prototype to the surrounding scope instead. - context->symbolTable.getOuterLevel()->insert($$.function); + $$.function = context->parseFunctionDeclarator(@2, $1); } ; @@ -737,7 +639,7 @@ function_header_with_parameters // Add the parameter $$ = $1; if ($2.param.type->getBasicType() != EbtVoid) - $1->addParameter($2.param); + $1->addParameter($2.param.turnToConst()); else delete $2.param.type; } @@ -756,24 +658,30 @@ function_header_with_parameters } else { // Add the parameter $$ = $1; - $1->addParameter($3.param); + $1->addParameter($3.param.turnToConst()); } } ; function_header : fully_specified_type IDENTIFIER LEFT_PAREN { - if ($1.qualifier != EvqGlobal && $1.qualifier != EvqTemporary) { + if ($1.qualifier != EvqGlobal && $1.qualifier != EvqTemporary) + { context->error(@2, "no qualifiers allowed for function return", getQualifierString($1.qualifier)); context->recover(); } + if (!$1.layoutQualifier.isEmpty()) + { + context->error(@2, "no qualifiers allowed for function return", "layout"); + context->recover(); + } // make sure a sampler is not involved as well... - if (context->structQualifierErrorCheck(@2, $1)) + if (context->samplerErrorCheck(@2, $1, "samplers can't be function return values")) context->recover(); // Add the function as a prototype after parsing it (we do not support recursion) TFunction *function; - TType type($1); + const TType *type = new TType($1); function = new TFunction($2.string, type); $$ = function; @@ -804,7 +712,7 @@ parameter_declarator int size; if (context->arraySizeErrorCheck(@3, $4, size)) context->recover(); - $1.setArray(true, size); + $1.setArraySize(size); TType* type = new TType($1); TParameter param = { $2.string, type }; @@ -878,15 +786,21 @@ init_declarator_list } | init_declarator_list COMMA identifier { $$ = $1; - $$.intermAggregate = context->parseDeclarator($$.type, $1.intermAggregate, $3.symbol, @3, *$3.string); + $$.intermAggregate = context->parseDeclarator($$.type, $1.intermAggregate, @3, *$3.string); } - | init_declarator_list COMMA identifier LEFT_BRACKET RIGHT_BRACKET { + | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { $$ = $1; - context->parseArrayDeclarator($$.type, @3, *$3.string, @4, NULL, NULL); + $$.intermAggregate = context->parseArrayDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, $5); } - | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { + | init_declarator_list COMMA identifier LEFT_BRACKET RIGHT_BRACKET EQUAL initializer { + ES3_ONLY("[]", @3, "implicitly sized array"); + $$ = $1; + $$.intermAggregate = context->parseArrayInitDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, nullptr, @6, $7); + } + | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET EQUAL initializer { + ES3_ONLY("=", @7, "first-class arrays (array initializer)"); $$ = $1; - $$.intermAggregate = context->parseArrayDeclarator($$.type, @3, *$3.string, @4, $1.intermNode, $5); + $$.intermAggregate = context->parseArrayInitDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, $5, @7, $8); } | init_declarator_list COMMA identifier EQUAL initializer { $$ = $1; @@ -903,17 +817,20 @@ single_declaration $$.type = $1; $$.intermAggregate = context->parseSingleDeclaration($$.type, @2, *$2.string); } - | fully_specified_type identifier LEFT_BRACKET RIGHT_BRACKET { - context->error(@2, "unsized array declarations not supported", $2.string->c_str()); - context->recover(); - - $$.type = $1; - $$.intermAggregate = context->parseSingleDeclaration($$.type, @2, *$2.string); - } | fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { $$.type = $1; $$.intermAggregate = context->parseSingleArrayDeclaration($$.type, @2, *$2.string, @3, $4); } + | fully_specified_type identifier LEFT_BRACKET RIGHT_BRACKET EQUAL initializer { + ES3_ONLY("[]", @3, "implicitly sized array"); + $$.type = $1; + $$.intermAggregate = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, nullptr, @5, $6); + } + | fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET EQUAL initializer { + ES3_ONLY("=", @6, "first-class arrays (array initializer)"); + $$.type = $1; + $$.intermAggregate = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, $4, @6, $7); + } | fully_specified_type identifier EQUAL initializer { $$.type = $1; $$.intermAggregate = context->parseSingleInitDeclaration($$.type, @2, *$2.string, @3, $4); @@ -929,13 +846,14 @@ fully_specified_type $$ = $1; if ($1.array) { - context->error(@1, "not supported", "first-class array"); - context->recover(); - $1.setArray(false); + ES3_ONLY("[]", @1, "first-class-array"); + if (context->getShaderVersion() != 300) { + $1.clearArrayness(); + } } } | type_qualifier type_specifier { - $$ = context->addFullySpecifiedType($1.qualifier, $1.layoutQualifier, $2); + $$ = context->addFullySpecifiedType($1.qualifier, $1.invariant, $1.layoutQualifier, $2); } ; @@ -966,7 +884,7 @@ type_qualifier ES2_ONLY("varying", @1); if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "varying")) context->recover(); - if (context->shaderType == GL_VERTEX_SHADER) + if (context->getShaderType() == GL_VERTEX_SHADER) $$.setBasic(EbtVoid, EvqVaryingOut, @1); else $$.setBasic(EbtVoid, EvqVaryingIn, @1); @@ -975,18 +893,19 @@ type_qualifier ES2_ONLY("varying", @1); if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "invariant varying")) context->recover(); - if (context->shaderType == GL_VERTEX_SHADER) - $$.setBasic(EbtVoid, EvqInvariantVaryingOut, @1); + if (context->getShaderType() == GL_VERTEX_SHADER) + $$.setBasic(EbtVoid, EvqVaryingOut, @1); else - $$.setBasic(EbtVoid, EvqInvariantVaryingIn, @1); + $$.setBasic(EbtVoid, EvqVaryingIn, @1); + $$.invariant = true; } | storage_qualifier { - if ($1.qualifier != EvqConst && !context->symbolTable.atGlobalLevel()) { + if ($1.qualifier != EvqConst && !context->symbolTable.atGlobalLevel()) + { context->error(@1, "Local variables can only use the const storage qualifier.", getQualifierString($1.qualifier)); context->recover(); - } else { - $$.setBasic(EbtVoid, $1.qualifier, @1); } + $$.setBasic(EbtVoid, $1.qualifier, @1); } | interpolation_qualifier storage_qualifier { $$ = context->joinInterpolationQualifiers(@1, $1.qualifier, @2, $2.qualifier); @@ -1006,6 +925,16 @@ type_qualifier $$.setBasic(EbtVoid, $2.qualifier, @2); $$.layoutQualifier = $1; } + | INVARIANT storage_qualifier { + context->es3InvariantErrorCheck($2.qualifier, @1); + $$.setBasic(EbtVoid, $2.qualifier, @2); + $$.invariant = true; + } + | INVARIANT interpolation_qualifier storage_qualifier { + context->es3InvariantErrorCheck($3.qualifier, @1); + $$ = context->joinInterpolationQualifiers(@2, $2.qualifier, @3, $3.qualifier); + $$.invariant = true; + } ; storage_qualifier @@ -1014,29 +943,29 @@ storage_qualifier } | IN_QUAL { ES3_ONLY("in", @1, "storage qualifier"); - $$.qualifier = (context->shaderType == GL_FRAGMENT_SHADER) ? EvqFragmentIn : EvqVertexIn; + $$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentIn : EvqVertexIn; } | OUT_QUAL { ES3_ONLY("out", @1, "storage qualifier"); - $$.qualifier = (context->shaderType == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqVertexOut; + $$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqVertexOut; } | CENTROID IN_QUAL { ES3_ONLY("centroid in", @1, "storage qualifier"); - if (context->shaderType == GL_VERTEX_SHADER) + if (context->getShaderType() == GL_VERTEX_SHADER) { context->error(@1, "invalid storage qualifier", "it is an error to use 'centroid in' in the vertex shader"); context->recover(); } - $$.qualifier = (context->shaderType == GL_FRAGMENT_SHADER) ? EvqCentroidIn : EvqVertexIn; + $$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqCentroidIn : EvqVertexIn; } | CENTROID OUT_QUAL { ES3_ONLY("centroid out", @1, "storage qualifier"); - if (context->shaderType == GL_FRAGMENT_SHADER) + if (context->getShaderType() == GL_FRAGMENT_SHADER) { context->error(@1, "invalid storage qualifier", "it is an error to use 'centroid out' in the fragment shader"); context->recover(); } - $$.qualifier = (context->shaderType == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqCentroidOut; + $$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqCentroidOut; } | UNIFORM { if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "uniform")) @@ -1111,6 +1040,11 @@ type_specifier_no_prec : type_specifier_nonarray { $$ = $1; } + | type_specifier_nonarray LEFT_BRACKET RIGHT_BRACKET { + ES3_ONLY("[]", @2, "implicitly sized array"); + $$ = $1; + $$.setArraySize(0); + } | type_specifier_nonarray LEFT_BRACKET constant_expression RIGHT_BRACKET { $$ = $1; @@ -1120,7 +1054,7 @@ type_specifier_no_prec int size; if (context->arraySizeErrorCheck(@2, $3, size)) context->recover(); - $$.setArray(true, size); + $$.setArraySize(size); } } ; @@ -1509,9 +1443,9 @@ selection_rest_statement ; switch_statement - : SWITCH LEFT_PAREN expression RIGHT_PAREN { ++context->mSwitchNestingLevel; } compound_statement { + : SWITCH LEFT_PAREN expression RIGHT_PAREN { context->incrSwitchNestingLevel(); } compound_statement { $$ = context->addSwitch($3, $6, @1); - --context->mSwitchNestingLevel; + context->decrSwitchNestingLevel(); } ; @@ -1532,13 +1466,11 @@ condition context->recover(); } | fully_specified_type identifier EQUAL initializer { - TIntermNode* intermNode; - if (context->structQualifierErrorCheck(@2, $1)) - context->recover(); + TIntermNode *intermNode; if (context->boolErrorCheck(@2, $1)) context->recover(); - if (!context->executeInitializer(@2, *$2.string, $1, $4, intermNode)) + if (!context->executeInitializer(@2, *$2.string, $1, $4, &intermNode)) $$ = $4; else { context->recover(); @@ -1548,22 +1480,22 @@ condition ; iteration_statement - : WHILE LEFT_PAREN { context->symbolTable.push(); ++context->mLoopNestingLevel; } condition RIGHT_PAREN statement_no_new_scope { + : WHILE LEFT_PAREN { context->symbolTable.push(); context->incrLoopNestingLevel(); } condition RIGHT_PAREN statement_no_new_scope { context->symbolTable.pop(); $$ = context->intermediate.addLoop(ELoopWhile, 0, $4, 0, $6, @1); - --context->mLoopNestingLevel; + context->decrLoopNestingLevel(); } - | DO { ++context->mLoopNestingLevel; } statement_with_scope WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON { + | DO { context->incrLoopNestingLevel(); } statement_with_scope WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON { if (context->boolErrorCheck(@8, $6)) context->recover(); $$ = context->intermediate.addLoop(ELoopDoWhile, 0, $6, 0, $3, @4); - --context->mLoopNestingLevel; + context->decrLoopNestingLevel(); } - | FOR LEFT_PAREN { context->symbolTable.push(); ++context->mLoopNestingLevel; } for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope { + | FOR LEFT_PAREN { context->symbolTable.push(); context->incrLoopNestingLevel(); } for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope { context->symbolTable.pop(); $$ = context->intermediate.addLoop(ELoopFor, $4, reinterpret_cast($5.node1), reinterpret_cast($5.node2), $7, @1); - --context->mLoopNestingLevel; + context->decrLoopNestingLevel(); } ; @@ -1620,11 +1552,11 @@ jump_statement translation_unit : external_declaration { $$ = $1; - context->treeRoot = $$; + context->setTreeRoot($$); } | translation_unit external_declaration { $$ = context->intermediate.growAggregate($1, $2, @$); - context->treeRoot = $$; + context->setTreeRoot($$); } ; @@ -1639,114 +1571,15 @@ external_declaration function_definition : function_prototype { - TFunction* function = $1.function; - - const TSymbol *builtIn = context->symbolTable.findBuiltIn(function->getMangledName(), context->shaderVersion); - - if (builtIn) - { - context->error(@1, "built-in functions cannot be redefined", function->getName().c_str()); - context->recover(); - } - - TFunction* prevDec = static_cast(context->symbolTable.find(function->getMangledName(), context->shaderVersion)); - // - // Note: 'prevDec' could be 'function' if this is the first time we've seen function - // as it would have just been put in the symbol table. Otherwise, we're looking up - // an earlier occurance. - // - if (prevDec->isDefined()) { - // - // Then this function already has a body. - // - context->error(@1, "function already has a body", function->getName().c_str()); - context->recover(); - } - prevDec->setDefined(); - - // - // Raise error message if main function takes any parameters or return anything other than void - // - if (function->getName() == "main") { - if (function->getParamCount() > 0) { - context->error(@1, "function cannot take any parameter(s)", function->getName().c_str()); - context->recover(); - } - if (function->getReturnType().getBasicType() != EbtVoid) { - context->error(@1, "", function->getReturnType().getBasicString(), "main function cannot return a value"); - context->recover(); - } - } - - // - // Remember the return type for later checking for RETURN statements. - // - context->currentFunctionType = &(prevDec->getReturnType()); - context->mFunctionReturnsValue = false; - - // - // Insert parameters into the symbol table. - // If the parameter has no name, it's not an error, just don't insert it - // (could be used for unused args). - // - // Also, accumulate the list of parameters into the HIL, so lower level code - // knows where to find parameters. - // - TIntermAggregate* paramNodes = new TIntermAggregate; - for (size_t i = 0; i < function->getParamCount(); i++) { - const TParameter& param = function->getParam(i); - if (param.name != 0) { - TVariable *variable = new TVariable(param.name, *param.type); - // - // Insert the parameters with name in the symbol table. - // - if (! context->symbolTable.declare(variable)) { - context->error(@1, "redefinition", variable->getName().c_str()); - context->recover(); - delete variable; - } - - // - // Add the parameter to the HIL - // - paramNodes = context->intermediate.growAggregate( - paramNodes, - context->intermediate.addSymbol(variable->getUniqueId(), - variable->getName(), - variable->getType(), @1), - @1); - } else { - paramNodes = context->intermediate.growAggregate(paramNodes, context->intermediate.addSymbol(0, "", *param.type, @1), @1); - } - } - context->intermediate.setAggregateOperator(paramNodes, EOpParameters, @1); - $1.intermAggregate = paramNodes; - context->mLoopNestingLevel = 0; + context->parseFunctionPrototype(@1, $1.function, &$1.intermAggregate); } compound_statement_no_new_scope { - //?? Check that all paths return a value if return type != void ? - // May be best done as post process phase on intermediate code - if (context->currentFunctionType->getBasicType() != EbtVoid && ! context->mFunctionReturnsValue) { - context->error(@1, "function does not return a value:", "", $1.function->getName().c_str()); - context->recover(); - } - - $$ = context->intermediate.growAggregate($1.intermAggregate, $3, @$); - context->intermediate.setAggregateOperator($$, EOpFunction, @1); - $$->getAsAggregate()->setName($1.function->getMangledName().c_str()); - $$->getAsAggregate()->setType($1.function->getReturnType()); - - // store the pragma information for debug and optimize and other vendor specific - // information. This information can be queried from the parse tree - $$->getAsAggregate()->setOptimize(context->pragma().optimize); - $$->getAsAggregate()->setDebug(context->pragma().debug); - - context->symbolTable.pop(); + $$ = context->addFunctionDefinition(*($1.function), $1.intermAggregate, $3, @1); } ; %% int glslang_parse(TParseContext* context) { - return yyparse(context, context->scanner); + return yyparse(context, context->getScanner()); } diff --git a/src/3rdparty/angle/src/compiler/translator/intermOut.cpp b/src/3rdparty/angle/src/compiler/translator/intermOut.cpp index 07c50f0ce5..6dca547f08 100644 --- a/src/3rdparty/angle/src/compiler/translator/intermOut.cpp +++ b/src/3rdparty/angle/src/compiler/translator/intermOut.cpp @@ -10,6 +10,12 @@ namespace { +void OutputFunction(TInfoSinkBase &out, const char *str, TIntermAggregate *node) +{ + const char *internal = node->getNameObj().isInternal() ? " (internal function)" : ""; + out << str << internal << ": " << node->getNameObj().getString(); +} + // // Two purposes: // 1. Show an example of how to iterate tree. Functions can @@ -27,18 +33,21 @@ class TOutputTraverser : public TIntermTraverser { public: TOutputTraverser(TInfoSinkBase &i) - : sink(i) { } + : TIntermTraverser(true, false, false), + sink(i) + { + } TInfoSinkBase& sink; protected: - void visitSymbol(TIntermSymbol *); - void visitConstantUnion(TIntermConstantUnion *); - bool visitBinary(Visit visit, TIntermBinary *); - bool visitUnary(Visit visit, TIntermUnary *); - bool visitSelection(Visit visit, TIntermSelection *); - bool visitAggregate(Visit visit, TIntermAggregate *); - bool visitLoop(Visit visit, TIntermLoop *); - bool visitBranch(Visit visit, TIntermBranch *); + void visitSymbol(TIntermSymbol *) override; + void visitConstantUnion(TIntermConstantUnion *) override; + bool visitBinary(Visit visit, TIntermBinary *) override; + bool visitUnary(Visit visit, TIntermUnary *) override; + bool visitSelection(Visit visit, TIntermSelection *) override; + bool visitAggregate(Visit visit, TIntermAggregate *) override; + bool visitLoop(Visit visit, TIntermLoop *) override; + bool visitBranch(Visit visit, TIntermBranch *) override; }; // @@ -56,26 +65,6 @@ void OutputTreeText(TInfoSinkBase &sink, TIntermNode *node, const int depth) } // namespace anonymous - -TString TType::getCompleteString() const -{ - TStringStream stream; - - if (qualifier != EvqTemporary && qualifier != EvqGlobal) - stream << getQualifierString() << " "; - if (precision != EbpUndefined) - stream << getPrecisionString() << " "; - if (array) - stream << "array[" << getArraySize() << "] of "; - if (isMatrix()) - stream << getCols() << "X" << getRows() << " matrix of "; - else if (isVector()) - stream << getNominalSize() << "-component vector of "; - - stream << getBasicString(); - return stream.str(); -} - // // The rest of the file are the traversal functions. The last one // is the one that starts the traversal. @@ -265,7 +254,7 @@ bool TOutputTraverser::visitBinary(Visit visit, TIntermBinary *node) OutputTreeText(out, intermConstantUnion, mDepth + 1); // The following code finds the field name from the constant union - const ConstantUnion *constantUnion = intermConstantUnion->getUnionArrayPointer(); + const TConstantUnion *constantUnion = intermConstantUnion->getUnionArrayPointer(); const TStructure *structure = node->getLeft()->getType().getStruct(); const TInterfaceBlock *interfaceBlock = node->getLeft()->getType().getInterfaceBlock(); ASSERT(structure || interfaceBlock); @@ -390,10 +379,10 @@ bool TOutputTraverser::visitAggregate(Visit visit, TIntermAggregate *node) { case EOpSequence: out << "Sequence\n"; return true; case EOpComma: out << "Comma\n"; return true; - case EOpFunction: out << "Function Definition: " << node->getName(); break; - case EOpFunctionCall: out << "Function Call: " << node->getName(); break; + case EOpFunction: OutputFunction(out, "Function Definition", node); break; + case EOpFunctionCall: OutputFunction(out, "Function Call", node); break; case EOpParameters: out << "Function Parameters: "; break; - case EOpPrototype: out << "Function Prototype: " << node->getName(); break; + case EOpPrototype: OutputFunction(out, "Function Prototype", node); break; case EOpConstructFloat: out << "Construct float"; break; case EOpConstructVec2: out << "Construct vec2"; break; @@ -412,7 +401,13 @@ bool TOutputTraverser::visitAggregate(Visit visit, TIntermAggregate *node) case EOpConstructUVec3: out << "Construct uvec3"; break; case EOpConstructUVec4: out << "Construct uvec4"; break; case EOpConstructMat2: out << "Construct mat2"; break; + case EOpConstructMat2x3: out << "Construct mat2x3"; break; + case EOpConstructMat2x4: out << "Construct mat2x4"; break; + case EOpConstructMat3x2: out << "Construct mat3x2"; break; case EOpConstructMat3: out << "Construct mat3"; break; + case EOpConstructMat3x4: out << "Construct mat3x4"; break; + case EOpConstructMat4x2: out << "Construct mat4x2"; break; + case EOpConstructMat4x3: out << "Construct mat4x3"; break; case EOpConstructMat4: out << "Construct mat4"; break; case EOpConstructStruct: out << "Construct structure"; break; diff --git a/src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp b/src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp index 48d44c72d1..790974a2bf 100644 --- a/src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp +++ b/src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp @@ -54,11 +54,8 @@ void RestrictFragmentShaderTiming::enforceRestrictions(const TDependencyGraph& g // Starting from each sampler, traverse the dependency graph and generate an error each time we // hit a node where sampler dependent values are not allowed. - for (TGraphSymbolVector::const_iterator iter = graph.beginSamplerSymbols(); - iter != graph.endSamplerSymbols(); - ++iter) + for (auto samplerSymbol : graph.samplerSymbols()) { - TGraphSymbol* samplerSymbol = *iter; clearVisited(); samplerSymbol->traverse(this); } @@ -66,11 +63,8 @@ void RestrictFragmentShaderTiming::enforceRestrictions(const TDependencyGraph& g void RestrictFragmentShaderTiming::validateUserDefinedFunctionCallUsage(const TDependencyGraph& graph) { - for (TFunctionCallVector::const_iterator iter = graph.beginUserDefinedFunctionCalls(); - iter != graph.endUserDefinedFunctionCalls(); - ++iter) + for (const auto* functionCall : graph.userDefinedFunctionCalls()) { - TGraphFunctionCall* functionCall = *iter; beginError(functionCall->getIntermFunctionCall()); mSink << "A call to a user defined function is not permitted.\n"; } diff --git a/src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.h b/src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.h index 74bfd0b5c2..23a8217722 100644 --- a/src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.h +++ b/src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.h @@ -22,7 +22,8 @@ public: void enforceRestrictions(TIntermNode* root) { root->traverse(this); } int numErrors() { return mNumErrors; } - virtual void visitSymbol(TIntermSymbol*); + void visitSymbol(TIntermSymbol *) override; + private: TInfoSinkBase& mSink; int mNumErrors; diff --git a/src/3rdparty/angle/src/compiler/translator/util.cpp b/src/3rdparty/angle/src/compiler/translator/util.cpp index 42a995ee6f..0131137206 100644 --- a/src/3rdparty/angle/src/compiler/translator/util.cpp +++ b/src/3rdparty/angle/src/compiler/translator/util.cpp @@ -12,7 +12,7 @@ #include "compiler/translator/SymbolTable.h" #include "common/utilities.h" -bool atof_clamp(const char *str, float *value) +bool strtof_clamp(const std::string &str, float *value) { bool success = pp::numeric_lex_float(str, value); if (!success) @@ -20,11 +20,11 @@ bool atof_clamp(const char *str, float *value) return success; } -bool atoi_clamp(const char *str, int *value) +bool atoi_clamp(const char *str, unsigned int *value) { bool success = pp::numeric_lex_int(str, value); if (!success) - *value = std::numeric_limits::max(); + *value = std::numeric_limits::max(); return success; } @@ -219,7 +219,6 @@ bool IsVaryingOut(TQualifier qualifier) switch (qualifier) { case EvqVaryingOut: - case EvqInvariantVaryingOut: case EvqSmoothOut: case EvqFlatOut: case EvqCentroidOut: @@ -237,7 +236,6 @@ bool IsVaryingIn(TQualifier qualifier) switch (qualifier) { case EvqVaryingIn: - case EvqInvariantVaryingIn: case EvqSmoothIn: case EvqFlatIn: case EvqCentroidIn: @@ -269,8 +267,6 @@ InterpolationType GetInterpolationType(TQualifier qualifier) case EvqFragmentIn: case EvqVaryingIn: case EvqVaryingOut: - case EvqInvariantVaryingIn: - case EvqInvariantVaryingOut: return INTERPOLATION_SMOOTH; case EvqCentroidIn: @@ -301,13 +297,13 @@ void GetVariableTraverser::setTypeSpecificInfo( ASSERT(variable); switch (type.getQualifier()) { - case EvqInvariantVaryingIn: - case EvqInvariantVaryingOut: - variable->isInvariant = true; - break; case EvqVaryingIn: case EvqVaryingOut: - if (mSymbolTable.isVaryingInvariant(std::string(name.c_str()))) + case EvqVertexOut: + case EvqSmoothOut: + case EvqFlatOut: + case EvqCentroidOut: + if (mSymbolTable.isVaryingInvariant(std::string(name.c_str())) || type.isInvariant()) { variable->isInvariant = true; } diff --git a/src/3rdparty/angle/src/compiler/translator/util.h b/src/3rdparty/angle/src/compiler/translator/util.h index 68bae66168..ea7a35a352 100644 --- a/src/3rdparty/angle/src/compiler/translator/util.h +++ b/src/3rdparty/angle/src/compiler/translator/util.h @@ -14,15 +14,15 @@ #include "compiler/translator/Types.h" -// atof_clamp is like atof but +// strtof_clamp is like strtof but // 1. it forces C locale, i.e. forcing '.' as decimal point. // 2. it clamps the value to -FLT_MAX or FLT_MAX if overflow happens. // Return false if overflow happens. -extern bool atof_clamp(const char *str, float *value); +bool strtof_clamp(const std::string &str, float *value); -// If overflow happens, clamp the value to INT_MIN or INT_MAX. +// If overflow happens, clamp the value to UINT_MIN or UINT_MAX. // Return false if overflow happens. -extern bool atoi_clamp(const char *str, int *value); +bool atoi_clamp(const char *str, unsigned int *value); class TSymbolTable; @@ -41,6 +41,7 @@ class GetVariableTraverser : angle::NonCopyable { public: GetVariableTraverser(const TSymbolTable &symbolTable); + virtual ~GetVariableTraverser() {} template void traverse(const TType &type, const TString &name, std::vector *output); -- cgit v1.2.3