summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/angle/src/compiler/translator
diff options
context:
space:
mode:
authorOliver Wolff <oliver.wolff@theqtcompany.com>2016-03-24 12:38:18 +0100
committerOliver Wolff <oliver.wolff@qt.io>2016-04-25 05:57:38 +0000
commite12ba07322cd61c5cf50c25ed8d1f08f6b1ff879 (patch)
treed31a44c9f123ed764a00eff7b4fff656a07d54ab /src/3rdparty/angle/src/compiler/translator
parentd3dcc6f610b97be7cbfbb0a65988e5940568c825 (diff)
Update ANGLE to chromium/2651
Change-Id: I1cd32b780b1a0b913fab870e155ae1f4f9ac40d7 Reviewed-by: Maurice Kalinowski <maurice.kalinowski@qt.io>
Diffstat (limited to 'src/3rdparty/angle/src/compiler/translator')
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.cpp451
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ASTMetadataHLSL.h58
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp206
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h16
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BaseTypes.h126
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp144
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulator.h46
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp162
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h8
-rw-r--r--src/3rdparty/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp47
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Cache.cpp100
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Cache.h90
-rw-r--r--src/3rdparty/angle/src/compiler/translator/CallDAG.cpp293
-rw-r--r--src/3rdparty/angle/src/compiler/translator/CallDAG.h75
-rw-r--r--src/3rdparty/angle/src/compiler/translator/CodeGen.cpp38
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Common.h27
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Compiler.cpp329
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Compiler.h62
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ConstantUnion.h60
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Diagnostics.cpp7
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Diagnostics.h8
-rw-r--r--src/3rdparty/angle/src/compiler/translator/DirectiveHandler.cpp20
-rw-r--r--src/3rdparty/angle/src/compiler/translator/DirectiveHandler.h31
-rw-r--r--src/3rdparty/angle/src/compiler/translator/EmulatePrecision.cpp127
-rw-r--r--src/3rdparty/angle/src/compiler/translator/EmulatePrecision.h26
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ExtensionBehavior.h6
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.cpp100
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ExtensionGLSL.h39
-rw-r--r--src/3rdparty/angle/src/compiler/translator/FlagStd140Structs.h10
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.cpp29
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ForLoopUnroll.h15
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Initialize.cpp123
-rw-r--r--src/3rdparty/angle/src/compiler/translator/InitializeDll.cpp4
-rw-r--r--src/3rdparty/angle/src/compiler/translator/InitializeParseContext.h2
-rw-r--r--src/3rdparty/angle/src/compiler/translator/InitializeVariables.cpp9
-rw-r--r--src/3rdparty/angle/src/compiler/translator/InitializeVariables.h17
-rw-r--r--src/3rdparty/angle/src/compiler/translator/IntermNode.cpp2366
-rw-r--r--src/3rdparty/angle/src/compiler/translator/IntermNode.h524
-rw-r--r--src/3rdparty/angle/src/compiler/translator/IntermTraverse.cpp660
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Intermediate.cpp189
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Intermediate.h22
-rw-r--r--src/3rdparty/angle/src/compiler/translator/NodeSearch.h25
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Operator.cpp6
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Operator.h7
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputESSL.h3
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputGLSL.cpp15
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputGLSL.h6
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.cpp322
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputGLSLBase.h31
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputHLSL.cpp1568
-rw-r--r--src/3rdparty/angle/src/compiler/translator/OutputHLSL.h59
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ParseContext.cpp3530
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ParseContext.h473
-rw-r--r--src/3rdparty/angle/src/compiler/translator/PoolAlloc.h25
-rw-r--r--src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.cpp81
-rw-r--r--src/3rdparty/angle/src/compiler/translator/PruneEmptyDeclarations.h15
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.cpp157
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RecordConstantPrecision.h23
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.cpp2
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RegenerateStructNames.h7
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.cpp513
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemoveDynamicIndexing.h21
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemovePow.cpp105
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RemovePow.h18
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RenameFunction.h2
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.cpp163
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RewriteDoWhile.h16
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.cpp58
-rw-r--r--src/3rdparty/angle/src/compiler/translator/RewriteElseBlocks.h2
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp22
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h5
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SearchSymbol.cpp4
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SearchSymbol.h2
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.cpp92
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SeparateArrayInitialization.h25
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.cpp77
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SeparateDeclarations.h23
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp169
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SeparateExpressionsReturningArrays.h19
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ShaderLang.cpp67
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ShaderVars.cpp74
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SymbolTable.cpp80
-rw-r--r--src/3rdparty/angle/src/compiler/translator/SymbolTable.h173
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TranslatorESSL.cpp24
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TranslatorESSL.h2
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.cpp159
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TranslatorGLSL.h4
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.cpp36
-rw-r--r--src/3rdparty/angle/src/compiler/translator/TranslatorHLSL.h7
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Types.cpp45
-rw-r--r--src/3rdparty/angle/src/compiler/translator/Types.h151
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp4
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitAST.h7
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp368
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UnfoldShortCircuitToIf.h18
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UniformHLSL.cpp128
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UniformHLSL.h12
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UtilsHLSL.cpp234
-rw-r--r--src/3rdparty/angle/src/compiler/translator/UtilsHLSL.h47
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.cpp112
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateGlobalInitializer.h16
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateLimitations.cpp73
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateLimitations.h16
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateOutputs.cpp98
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateOutputs.h18
-rw-r--r--src/3rdparty/angle/src/compiler/translator/ValidateSwitch.h2
-rw-r--r--src/3rdparty/angle/src/compiler/translator/VariableInfo.cpp216
-rw-r--r--src/3rdparty/angle/src/compiler/translator/VariableInfo.h17
-rw-r--r--src/3rdparty/angle/src/compiler/translator/VersionGLSL.cpp59
-rw-r--r--src/3rdparty/angle/src/compiler/translator/VersionGLSL.h26
-rw-r--r--src/3rdparty/angle/src/compiler/translator/blocklayout.cpp5
-rw-r--r--src/3rdparty/angle/src/compiler/translator/blocklayout.h21
-rw-r--r--src/3rdparty/angle/src/compiler/translator/blocklayoutHLSL.cpp12
-rw-r--r--src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.cpp2
-rw-r--r--src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraph.h55
-rw-r--r--src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphBuilder.h10
-rw-r--r--src/3rdparty/angle/src/compiler/translator/depgraph/DependencyGraphOutput.cpp3
-rw-r--r--src/3rdparty/angle/src/compiler/translator/glslang.h2
-rw-r--r--src/3rdparty/angle/src/compiler/translator/glslang.l97
-rw-r--r--src/3rdparty/angle/src/compiler/translator/glslang.y407
-rw-r--r--src/3rdparty/angle/src/compiler/translator/intermOut.cpp61
-rw-r--r--src/3rdparty/angle/src/compiler/translator/timing/RestrictFragmentShaderTiming.cpp10
-rw-r--r--src/3rdparty/angle/src/compiler/translator/timing/RestrictVertexShaderTiming.h3
-rw-r--r--src/3rdparty/angle/src/compiler/translator/util.cpp20
-rw-r--r--src/3rdparty/angle/src/compiler/translator/util.h9
125 files changed, 13031 insertions, 4342 deletions
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<TIntermNode*> 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<TIntermNode*> mLoopsAndSwitches;
+ std::vector<TIntermSelection*> 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 <set>
+#include <vector>
+
+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<TIntermNode*> mControlFlowsContainingGradient;
+
+ // Remember information about the discontinuous loops and which functions
+ // are called in such loops.
+ bool mCalledInDiscontinuousLoop;
+ bool mHasGradientLoopInCallGraph;
+ std::set<TIntermLoop*> mDiscontinuousLoops;
+ std::set<TIntermSelection *> mIfsContainingGradientLoop;
+
+ // Will we need to generate a Lod0 version of the function.
+ bool mNeedsLod0;
+};
+
+typedef std::vector<ASTMetadataHLSL> 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 &param : *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 &param)
{
- return SetFunctionCalled(FunctionId(op, param));
+ return SetFunctionCalled(FunctionId(op, &param));
}
-bool BuiltInFunctionEmulator::SetFunctionCalled(
- TOperator op, const TType& param1, const TType& param2)
+bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, const TType &param1, const TType &param2)
{
- return SetFunctionCalled(FunctionId(op, param1, param2));
+ return SetFunctionCalled(FunctionId(op, &param1, &param2));
}
-bool BuiltInFunctionEmulator::SetFunctionCalled(
- TOperator op, const TType& param1, const TType& param2, const TType& param3)
+bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op,
+ const TType &param1, const TType &param2, const TType &param3)
{
- return SetFunctionCalled(FunctionId(op, param1, param2, param3));
+ return SetFunctionCalled(FunctionId(op, &param1, &param2, &param3));
}
-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 &param);
+ bool SetFunctionCalled(TOperator op, const TType &param1, const TType &param2);
+ bool SetFunctionCalled(TOperator op, const TType &param1, const TType &param2, const TType &param3);
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<FunctionId, std::string> 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 <limits>
+
+#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<EnumComponentType>::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<EnumComponentType>(basicType);
+ components.precision = static_cast<EnumComponentType>(precision);
+ components.qualifier = static_cast<EnumComponentType>(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 <stdint.h>
+#include <string.h>
+#include <map>
+
+#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<TypeKey, const TType*> 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<Record> *records, std::map<int, int> *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<int>(callee->index));
+ }
+
+ (*idToIndex)[data.node->getFunctionId()] = static_cast<int>(data.index);
+ }
+ }
+
+ private:
+
+ struct CreatorFunctionData
+ {
+ CreatorFunctionData()
+ : node(nullptr),
+ index(0),
+ indexAssigned(false),
+ visiting(false)
+ {
+ }
+
+ std::set<CreatorFunctionData*> 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<TString, CreatorFunctionData> mFunctions;
+ CreatorFunctionData *mCurrentFunction;
+ size_t mCurrentIndex;
+};
+
+// CallDAG
+
+CallDAG::CallDAG()
+{
+}
+
+CallDAG::~CallDAG()
+{
+}
+
+const size_t CallDAG::InvalidIndex = std::numeric_limits<size_t>::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 <map>
+
+#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<int> 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<Record> mRecords;
+ std::map<int, int> 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 <limits>
#include <stdio.h>
-#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 T> class TVector : public std::vector<T, pool_allocator<T> > {
-public:
- typedef typename std::vector<T, pool_allocator<T> >::size_type size_type;
- TVector() : std::vector<T, pool_allocator<T> >() {}
- TVector(const pool_allocator<T>& a) : std::vector<T, pool_allocator<T> >(a) {}
- TVector(size_type i): std::vector<T, pool_allocator<T> >(i) {}
+template <class T>
+class TVector : public std::vector<T, pool_allocator<T>>
+{
+ public:
+ typedef typename std::vector<T, pool_allocator<T>>::size_type size_type;
+ TVector() : std::vector<T, pool_allocator<T>>() {}
+ TVector(const pool_allocator<T> &a) : std::vector<T, pool_allocator<T>>(a) {}
+ TVector(size_type i) : std::vector<T, pool_allocator<T>>(i) {}
};
-template <class K, class D, class CMP = std::less<K> >
-class TMap : public std::map<K, D, CMP, pool_allocator<std::pair<const K, D> > > {
-public:
- typedef pool_allocator<std::pair<const K, D> > tAllocator;
+template <class K, class D, class CMP = std::less<K>>
+class TMap : public std::map<K, D, CMP, pool_allocator<std::pair<const K, D>>>
+{
+ public:
+ typedef pool_allocator<std::pair<const K, D>> tAllocator;
TMap() : std::map<K, D, CMP, tAllocator>() {}
// 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<TBasicType>(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<int> 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<int>(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<FunctionMetadata> *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<FunctionMetadata> *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"
@@ -36,6 +37,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.
//
class TShHandleBase {
@@ -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<sh::Attribute> &getAttributes() const { return attributes; }
- const std::vector<sh::Attribute> &getOutputVariables() const { return outputVariables; }
+ const std::vector<sh::OutputVariable> &getOutputVariables() const { return outputVariables; }
const std::vector<sh::Uniform> &getUniforms() const { return uniforms; }
const std::vector<sh::Varying> &getVaryings() const { return varyings; }
const std::vector<sh::InterfaceBlock> &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<sh::Attribute> attributes;
- std::vector<sh::Attribute> outputVariables;
+ std::vector<sh::OutputVariable> outputVariables;
std::vector<sh::Uniform> uniforms;
std::vector<sh::ShaderVariable> expandedUniforms;
std::vector<sh::Varying> varyings;
std::vector<sh::InterfaceBlock> 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> 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 <assert.h>
-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 <sstream>
-#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<TIntermSequence::const_iterator> 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<TString, TIntermSequence*, TStringComparator> 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<std::string, TBehavior> 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<std::string> &TExtensionGLSL::getEnabledExtensions() const
+{
+ return mEnabledExtensions;
+}
+
+const std::set<std::string> &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 <set>
+#include <string>
+
+#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<std::string> &getEnabledExtensions() const;
+ const std::set<std::string> &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<std::string> mEnabledExtensions;
+ std::set<std::string> 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<TIntermTyped *> 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<unsigned char>(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<InitVariableInfo> 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 <float.h>
#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
#include <algorithm>
+#include <vector>
+#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<float> GetMatrix(const TConstantUnion *paramArray,
+ const unsigned int &rows,
+ const unsigned int &cols)
+{
+ std::vector<float> 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<float>(elements, rows, cols).transpose();
+}
+
+angle::Matrix<float> GetMatrix(const TConstantUnion *paramArray, const unsigned int &size)
+{
+ std::vector<float> 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<float>(elements, size).transpose();
+}
+
+void SetUnionArrayFromMatrix(const angle::Matrix<float> &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<float> result = m.transpose();
+ std::vector<float> 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<unsigned char>(mRight->getCols()), 1));
}
else
{
mOp = EOpMatrixTimesScalar;
- setType(TType(basicType, higherPrecision, EvqTemporary,
- mRight->getCols(), mRight->getRows()));
+ setType(TType(basicType, higherPrecision, resultQualifier,
+ static_cast<unsigned char>(mRight->getCols()),
+ static_cast<unsigned char>(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<unsigned char>(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<unsigned char>(mRight->getCols()),
+ static_cast<unsigned char>(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<unsigned char>(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<unsigned char>(mRight->getCols()),
+ static_cast<unsigned char>(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<unsigned char>(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<unsigned char>(nominalSize),
+ static_cast<unsigned char>(secondarySize)));
if (mLeft->isArray())
{
ASSERT(mLeft->getArraySize() == mRight->getArraySize());
@@ -639,560 +818,1778 @@ 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:
+ {
+ if (rightNode->getBasicType() != EbtFloat)
+ {
+ infoSink.info.message(EPrefixInternalError, getLine(),
+ "Constant Folding cannot be done for matrix times vector");
+ return nullptr;
+ }
- case EOpMatrixTimesVector:
+ const int matrixCols = getCols();
+ const int matrixRows = getRows();
+
+ resultArray = new TConstantUnion[matrixRows];
+
+ for (int matrixRow = 0; matrixRow < matrixRows; matrixRow++)
{
- if (node->getBasicType() != EbtFloat)
+ resultArray[matrixRow].setFConst(0.0f);
+ for (int col = 0; col < matrixCols; col++)
{
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Constant Folding cannot be done for matrix times vector");
- return NULL;
+ resultArray[matrixRow].setFConst(resultArray[matrixRow].getFConst() +
+ leftArray[col * matrixRows + matrixRow].getFConst() *
+ rightArray[col].getFConst());
}
+ }
+ }
+ break;
+
+ case EOpVectorTimesMatrix:
+ {
+ if (getType().getBasicType() != EbtFloat)
+ {
+ infoSink.info.message(EPrefixInternalError, getLine(),
+ "Constant Folding cannot be done for vector times matrix");
+ return nullptr;
+ }
- const int matrixCols = getCols();
- const int matrixRows = getRows();
+ const int matrixCols = rightNode->getType().getCols();
+ const int matrixRows = rightNode->getType().getRows();
- tempConstArray = new ConstantUnion[matrixRows];
+ resultArray = new TConstantUnion[matrixCols];
+ for (int matrixCol = 0; matrixCol < matrixCols; matrixCol++)
+ {
+ resultArray[matrixCol].setFConst(0.0f);
for (int matrixRow = 0; matrixRow < matrixRows; matrixRow++)
{
- 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[matrixCol].setFConst(resultArray[matrixCol].getFConst() +
+ leftArray[matrixRow].getFConst() *
+ rightArray[matrixCol * matrixRows + matrixRow].getFConst());
}
+ }
+ }
+ break;
- returnType = node->getType();
- returnType.setPrimarySize(matrixRows);
-
- tempNode = new TIntermConstantUnion(tempConstArray, returnType);
- tempNode->setLine(getLine());
+ case EOpLogicalAnd:
+ {
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ {
+ resultArray[i] = leftArray[i] && rightArray[i];
+ }
+ }
+ break;
- return tempNode;
+ case EOpLogicalOr:
+ {
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ {
+ resultArray[i] = leftArray[i] || rightArray[i];
}
+ }
+ break;
- case EOpVectorTimesMatrix:
+ case EOpLogicalXor:
+ {
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
{
- if (getType().getBasicType() != EbtFloat)
+ switch (getType().getBasicType())
{
- infoSink.info.message(
- EPrefixInternalError, getLine(),
- "Constant Folding cannot be done for vector times matrix");
- return NULL;
+ case EbtBool:
+ resultArray[i].setBConst(leftArray[i] != rightArray[i]);
+ break;
+ default:
+ UNREACHABLE();
+ break;
}
+ }
+ }
+ break;
- const int matrixCols = constantNode->getType().getCols();
- const int matrixRows = constantNode->getType().getRows();
+ 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;
- tempConstArray = new ConstantUnion[matrixCols];
+ case EOpGreaterThan:
+ ASSERT(objectSize == 1);
+ resultArray = new TConstantUnion[1];
+ resultArray->setBConst(*leftArray > *rightArray);
+ break;
- for (int matrixCol = 0; matrixCol < matrixCols; matrixCol++)
+ 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])
{
- 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());
- }
+ 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;
- returnType.setPrimarySize(matrixCols);
+ 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 EOpLogicalAnd:
- // this code is written for possible future use,
- // will not get executed currently
+ case EOpAll:
+ if (getType().getBasicType() == EbtBool)
+ {
+ resultArray = new TConstantUnion();
+ resultArray->setBConst(true);
+ for (size_t i = 0; i < objectSize; i++)
{
- tempConstArray = new ConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
+ if (!operandArray[i].getBConst())
{
- tempConstArray[i] = unionArray[i] && rightUnionArray[i];
+ resultArray->setBConst(false);
+ break;
}
}
break;
+ }
+ else
+ {
+ infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
+ return nullptr;
+ }
- case EOpLogicalOr:
+ 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<float> 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<float> 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<unsigned int>(
+ -static_cast<int>(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<unsigned int>(
+ static_cast<int>(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())
{
- tempConstArray = new ConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
- {
- tempConstArray[i] = unionArray[i] || rightUnionArray[i];
- }
+ 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 EOpLogicalXor:
+ 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)
{
- tempConstArray = new ConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
+ 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:
{
- switch (getType().getBasicType())
- {
- case EbtBool:
- tempConstArray[i].setBConst(
- unionArray[i] == rightUnionArray[i] ? false : true);
- break;
- default:
- UNREACHABLE();
- break;
- }
+ 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 EOpBitwiseAnd:
- tempConstArray = new ConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
- tempConstArray[i] = unionArray[i] & rightUnionArray[i];
+ case EOpFloor:
+ if (!foldFloatTypeUnary(operandArray[i], &floorf, infoSink, &resultArray[i]))
+ return nullptr;
break;
- case EOpBitwiseXor:
- tempConstArray = new ConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
- tempConstArray[i] = unionArray[i] ^ rightUnionArray[i];
+
+ case EOpTrunc:
+ if (!foldFloatTypeUnary(operandArray[i], &truncf, infoSink, &resultArray[i]))
+ return nullptr;
break;
- case EOpBitwiseOr:
- tempConstArray = new ConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
- tempConstArray[i] = unionArray[i] | rightUnionArray[i];
+
+ case EOpRound:
+ if (!foldFloatTypeUnary(operandArray[i], &roundf, infoSink, &resultArray[i]))
+ return nullptr;
break;
- case EOpBitShiftLeft:
- tempConstArray = new ConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
- tempConstArray[i] = unionArray[i] << rightUnionArray[i];
+
+ 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 EOpBitShiftRight:
- tempConstArray = new ConstantUnion[objectSize];
- for (size_t i = 0; i < objectSize; i++)
- tempConstArray[i] = unionArray[i] >> rightUnionArray[i];
+
+ 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<int32_t>(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<uint32_t>(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<float>(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<float>(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 EOpLessThan:
- ASSERT(objectSize == 1);
- tempConstArray = new ConstantUnion[1];
- tempConstArray->setBConst(*unionArray < *rightUnionArray);
- returnType = TType(EbtBool, EbpUndefined, EvqConst);
+ 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 EOpGreaterThan:
- ASSERT(objectSize == 1);
- tempConstArray = new ConstantUnion[1];
- tempConstArray->setBConst(*unionArray > *rightUnionArray);
- returnType = TType(EbtBool, EbpUndefined, EvqConst);
+ case EOpExp2:
+ if (!foldFloatTypeUnary(operandArray[i], &exp2f, infoSink, &resultArray[i]))
+ return nullptr;
break;
- case EOpLessThanEqual:
+ 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)
{
- ASSERT(objectSize == 1);
- ConstantUnion constant;
- constant.setBConst(*unionArray > *rightUnionArray);
- tempConstArray = new ConstantUnion[1];
- tempConstArray->setBConst(!constant.getBConst());
- returnType = TType(EbtBool, EbpUndefined, EvqConst);
+ resultArray[i].setBConst(!operandArray[i].getBConst());
break;
}
+ infoSink.info.message(
+ EPrefixInternalError, getLine(),
+ "Unary operation not folded into constant");
+ return nullptr;
- case EOpGreaterThanEqual:
+ case EOpNormalize:
+ if (getType().getBasicType() == EbtFloat)
{
- ASSERT(objectSize == 1);
- ConstantUnion constant;
- constant.setBConst(*unionArray < *rightUnionArray);
- tempConstArray = new ConstantUnion[1];
- tempConstArray->setBConst(!constant.getBConst());
- returnType = TType(EbtBool, EbpUndefined, EvqConst);
+ 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 EOpEqual:
- if (getType().getBasicType() == EbtStruct)
+ case EOpDFdx:
+ case EOpDFdy:
+ case EOpFwidth:
+ if (getType().getBasicType() == EbtFloat)
{
- if (!CompareStructure(node->getType(),
- node->getUnionArrayPointer(),
- unionArray))
+ // 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 &parameter, 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)
{
- boolNodeFlag = true;
+ for (int row = 0; row < resultRows; ++row)
+ {
+ if (col == row)
+ {
+ resultArray[resultIndex].cast(basicType, argumentUnionArray[0]);
+ }
+ else
+ {
+ resultArray[resultIndex].setFConst(0.0f);
+ }
+ ++resultIndex;
+ }
}
}
else
{
- for (size_t i = 0; i < objectSize; i++)
+ while (resultIndex < resultSize)
{
- if (unionArray[i] != rightUnionArray[i])
+ 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)
{
- boolNodeFlag = true;
- break; // break out of for loop
+ resultArray[resultIndex].setFConst(1.0f);
}
+ else
+ {
+ resultArray[resultIndex].setFConst(0.0f);
+ }
+ ++resultIndex;
}
}
+ ASSERT(resultIndex == resultSize);
+ return resultArray;
+ }
+ }
- tempConstArray = new ConstantUnion[1];
- if (!boolNodeFlag)
+ 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<unsigned int>(sequence->size());
+ std::vector<const TConstantUnion *> unionArrays(paramsCount);
+ std::vector<size_t> 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:
{
- tempConstArray->setBConst(true);
+ 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();
}
- else
+ break;
+
+ case EOpPow:
{
- tempConstArray->setBConst(false);
+ 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;
- tempNode = new TIntermConstantUnion(
- tempConstArray, TType(EbtBool, EbpUndefined, EvqConst));
- tempNode->setLine(getLine());
+ case EOpMod:
+ {
+ 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();
+ resultArray[i].setFConst(x - y * floorf(x / y));
+ }
+ }
+ else
+ UNREACHABLE();
+ }
+ break;
- return tempNode;
+ case EOpMin:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; 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 EOpNotEqual:
- if (getType().getBasicType() == EbtStruct)
+ case EOpMax:
{
- 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].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();
+ break;
+ }
}
}
- else
+ break;
+
+ case EOpStep:
{
- for (size_t i = 0; i < objectSize; i++)
+ if (basicType == EbtFloat)
{
- if (unionArray[i] == rightUnionArray[i])
+ 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:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; 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;
+ default:
+ UNREACHABLE();
+ break;
}
}
}
+ break;
+
+ 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 EOpGreaterThan:
+ {
+ 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:
+ {
+ 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 EOpVectorEqual:
+ {
+ 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;
+ case EbtBool:
+ resultArray[i].setBConst(unionArrays[0][i].getBConst() == unionArrays[1][i].getBConst());
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ }
+ break;
+
+ case EOpVectorNotEqual:
+ {
+ 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;
+ case EbtBool:
+ resultArray[i].setBConst(unionArrays[0][i].getBConst() != unionArrays[1][i].getBConst());
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ }
+ break;
+
+ case EOpDistance:
+ if (basicType == EbtFloat)
+ {
+ 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
+ UNREACHABLE();
+ break;
+
+ case EOpDot:
+
+ if (basicType == EbtFloat)
+ {
+ resultArray = new TConstantUnion();
+ resultArray->setFConst(VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize));
+ }
+ else
+ UNREACHABLE();
+ break;
- tempConstArray = new ConstantUnion[1];
- if (!boolNodeFlag)
+ case EOpCross:
+ if (basicType == EbtFloat && maxObjectSize == 3)
{
- tempConstArray->setBConst(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)
{
- tempConstArray->setBConst(false);
+ // 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++)
+ {
+ float result = unionArrays[0][i].getFConst() -
+ 2.0f * dotProduct * unionArrays[1][i].getFConst();
+ resultArray[i].setFConst(result);
+ }
}
+ else
+ UNREACHABLE();
+ break;
- tempNode = new TIntermConstantUnion(
- tempConstArray, TType(EbtBool, EbpUndefined, EvqConst));
- tempNode->setLine(getLine());
+ case EOpMul:
+ if (basicType == EbtFloat && (*sequence)[0]->getAsTyped()->isMatrix() &&
+ (*sequence)[1]->getAsTyped()->isMatrix())
+ {
+ // Perform component-wise matrix multiplication.
+ resultArray = new TConstantUnion[maxObjectSize];
+ int size = (*sequence)[0]->getAsTyped()->getNominalSize();
+ angle::Matrix<float> result =
+ GetMatrix(unionArrays[0], size).compMult(GetMatrix(unionArrays[1], size));
+ SetUnionArrayFromMatrix(result, resultArray);
+ }
+ else
+ UNREACHABLE();
+ break;
- return tempNode;
+ case EOpOuterProduct:
+ if (basicType == EbtFloat)
+ {
+ size_t numRows = (*sequence)[0]->getAsTyped()->getType().getObjectSize();
+ size_t numCols = (*sequence)[1]->getAsTyped()->getType().getObjectSize();
+ resultArray = new TConstantUnion[numRows * numCols];
+ angle::Matrix<float> result =
+ GetMatrix(unionArrays[0], 1, static_cast<int>(numCols))
+ .outerProduct(GetMatrix(unionArrays[1], static_cast<int>(numRows), 1));
+ SetUnionArrayFromMatrix(result, resultArray);
+ }
+ 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<unsigned int>(
- -static_cast<int>(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<unsigned int>(
- static_cast<int>(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 &parameter, 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<TIntermNode *> 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<TIntermNode *> 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<NodeUpdateEntry> mReplacements;
+ std::vector<NodeReplaceWithMultipleEntry> mMultiReplacements;
+ std::vector<NodeInsertMultipleEntry> 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<ParentBlock> 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<TName, TIntermSequence *, TNameComparator> 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<TFunction *>(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<FindDiscard>
}
};
-class FindSideEffectRewriting : public NodeSearchTraverser<FindSideEffectRewriting>
-{
- 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 <cfloat>
@@ -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 <stdio.h>
#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<TIntermTyped*> &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<int4> x, SamplerState s"; hlslCoords = 2; break;
- case EbtISampler3D: out << "Texture3D<int4> x, SamplerState s"; hlslCoords = 3; break;
- case EbtISamplerCube: out << "Texture2DArray<int4> x, SamplerState s"; hlslCoords = 3; break;
- case EbtISampler2DArray: out << "Texture2DArray<int4> x, SamplerState s"; hlslCoords = 3; break;
- case EbtUSampler2D: out << "Texture2D<uint4> x, SamplerState s"; hlslCoords = 2; break;
- case EbtUSampler3D: out << "Texture3D<uint4> x, SamplerState s"; hlslCoords = 3; break;
- case EbtUSamplerCube: out << "Texture2DArray<uint4> x, SamplerState s"; hlslCoords = 3; break;
- case EbtUSampler2DArray: out << "Texture2DArray<uint4> 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 <stack>
#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<TIntermTyped *> &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<TIntermTyped*, TString> mFlaggedStructMappedNames;
std::map<TIntermTyped*, TString> 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<std::pair<TIntermSymbol*, TIntermTyped*>> 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<ArrayHelperFunction*> mArrayEqualityFunctions;
std::vector<ArrayHelperFunction> 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<ArrayHelperFunction> 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 <stdio.h>
#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 &param = 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<size_t>(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<int>(function.getParamCount()));
+ }
+ else if (static_cast<size_t>(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<int>(unsignedSize);
+ size = static_cast<int>(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<const TVariable*>(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<TVariable*>(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<const TVariable *>(
+ 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))
+ switch (publicType.qualifier)
+ {
+ 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");
+ 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<const TVariable*>(symbol);
+ variable = static_cast<const TVariable *>(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<const TFunction*>(symbol);
+ return static_cast<const TFunction *>(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<const TVariable*>(symbol);
+ *intermNode = nullptr;
+ return false;
+ }
+ else if (initializer->getAsSymbolNode())
+ {
+ const TSymbol *symbol =
+ symbolTable.find(initializer->getAsSymbolNode()->getSymbol(), 0);
+ const TVariable *tVar = static_cast<const TVariable *>(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);
+
+ bool emptyDeclaration = (identifier == "");
+
+ mDeferredSingleDeclarationErrorCheck = emptyDeclaration;
- if (identifier != "")
+ if (emptyDeclaration)
{
- if (singleDeclarationErrorCheck(publicType, identifierLocation, identifier))
+ 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, 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 NULL;
+ return nullptr;
}
}
-TIntermAggregate* TParseContext::parseInvariantDeclaration(const TSourceLoc &invariantLoc,
+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 nullptr;
+ }
+}
+
+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 declaratorList;
+ return aggregateDeclaration;
}
}
else
{
recover();
- return NULL;
+ 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 aggregateDeclaration;
+ }
+ }
+ else
+ {
+ recover();
+ 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<TFunction *>(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 &param = 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;
+}
+
+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<TFunction *>(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 &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 (!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<TFunction *>(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(TPublicType publicType)
+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();
-
- 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;
- }
+ const TConstantUnion *unionArray = node->getUnionArrayPointer();
+ ASSERT(unionArray);
- 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;
+ outOfRangeError(outOfRangeIndexIsError, line, "", "[", extraInfo.c_str());
+ index = node->getType().getCols() - 1;
}
- 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;
- }
-
- 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<unsigned char>(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)
{
- *fatalError = false;
- TOperator op = fnCall->getBuiltInOp();
+ 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();
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<TIntermTyped*>(node)->getCompleteString();
+ extraInfoStream
+ << "built in unary operator function. Type: "
+ << static_cast<TIntermTyped *>(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<T>& p) : allocator(p.allocator) { }
-
- template <class Other>
- pool_allocator<T>& operator=(const pool_allocator<Other>& p) {
- allocator = p.allocator;
- return *this;
- }
+ pool_allocator() { }
template<class Other>
- pool_allocator(const pool_allocator<Other>& p) : allocator(&p.getAllocator()) { }
+ pool_allocator(const pool_allocator<Other>& p) { }
+
+ template <class Other>
+ pool_allocator<T>& operator=(const pool_allocator<Other>& 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<size_type>(-1) / sizeof(T); }
size_type max_size(int size) const { return static_cast<size_type>(-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<unsigned char>(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<TType> mIndexedVecAndMatrixTypes;
+ std::set<TType> 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 <algorithm>
@@ -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 <typename VarT>
-const std::vector<VarT> *GetVariableList(const TCompiler *compiler, ShaderVariableType variableType);
+const std::vector<VarT> *GetVariableList(const TCompiler *compiler);
template <>
-const std::vector<sh::Uniform> *GetVariableList(const TCompiler *compiler, ShaderVariableType)
+const std::vector<sh::Uniform> *GetVariableList(const TCompiler *compiler)
{
return &compiler->getUniforms();
}
template <>
-const std::vector<sh::Varying> *GetVariableList(const TCompiler *compiler, ShaderVariableType)
+const std::vector<sh::Varying> *GetVariableList(const TCompiler *compiler)
{
return &compiler->getVaryings();
}
template <>
-const std::vector<sh::Attribute> *GetVariableList(const TCompiler *compiler, ShaderVariableType variableType)
+const std::vector<sh::Attribute> *GetVariableList(const TCompiler *compiler)
+{
+ return &compiler->getAttributes();
+}
+
+template <>
+const std::vector<sh::OutputVariable> *GetVariableList(const TCompiler *compiler)
{
- return (variableType == SHADERVAR_ATTRIBUTE ?
- &compiler->getAttributes() :
- &compiler->getOutputVariables());
+ return &compiler->getOutputVariables();
}
template <>
-const std::vector<sh::InterfaceBlock> *GetVariableList(const TCompiler *compiler, ShaderVariableType)
+const std::vector<sh::InterfaceBlock> *GetVariableList(const TCompiler *compiler)
{
return &compiler->getInterfaceBlocks();
}
template <typename VarT>
-const std::vector<VarT> *GetShaderVariables(const ShHandle handle, ShaderVariableType variableType)
+const std::vector<VarT> *GetShaderVariables(const ShHandle handle)
{
if (!handle)
{
@@ -83,7 +78,7 @@ const std::vector<VarT> *GetShaderVariables(const ShHandle handle, ShaderVariabl
return NULL;
}
- return GetVariableList<VarT>(compiler, variableType);
+ return GetVariableList<VarT>(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<TShHandleBase*>(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<std::string, std::string> *ShGetNameHashingMap(
const std::vector<sh::Uniform> *ShGetUniforms(const ShHandle handle)
{
- return GetShaderVariables<sh::Uniform>(handle, SHADERVAR_UNIFORM);
+ return GetShaderVariables<sh::Uniform>(handle);
}
const std::vector<sh::Varying> *ShGetVaryings(const ShHandle handle)
{
- return GetShaderVariables<sh::Varying>(handle, SHADERVAR_VARYING);
+ return GetShaderVariables<sh::Varying>(handle);
}
const std::vector<sh::Attribute> *ShGetAttributes(const ShHandle handle)
{
- return GetShaderVariables<sh::Attribute>(handle, SHADERVAR_ATTRIBUTE);
+ return GetShaderVariables<sh::Attribute>(handle);
}
-const std::vector<sh::Attribute> *ShGetOutputVariables(const ShHandle handle)
+const std::vector<sh::OutputVariable> *ShGetOutputVariables(const ShHandle handle)
{
- return GetShaderVariables<sh::Attribute>(handle, SHADERVAR_OUTPUTVARIABLE);
+ return GetShaderVariables<sh::OutputVariable>(handle);
}
const std::vector<sh::InterfaceBlock> *ShGetInterfaceBlocks(const ShHandle handle)
{
- return GetShaderVariables<sh::InterfaceBlock>(handle, SHADERVAR_INTERFACEBLOCK);
+ return GetShaderVariables<sh::InterfaceBlock>(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 <GLSLANG/ShaderLang.h>
-#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)
{}
@@ -305,9 +349,14 @@ 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 <stdio.h>
#include <algorithm>
@@ -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<unsigned char>(size));
+ case EbtGenIType: return TCache::getType(EbtInt, static_cast<unsigned char>(size));
+ case EbtGenUType: return TCache::getType(EbtUInt, static_cast<unsigned char>(size));
+ case EbtGenBType: return TCache::getType(EbtBool, static_cast<unsigned char>(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<unsigned char>(size));
+ case EbtIVec: return TCache::getType(EbtInt, static_cast<unsigned char>(size));
+ case EbtUVec: return TCache::getType(EbtUInt, static_cast<unsigned char>(size));
+ case EbtBVec: return TCache::getType(EbtBool, static_cast<unsigned char>(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<int>(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<TParameter> TParamList;
+ const TString *buildMangledName() const;
+
+ typedef TVector<TConstParameter> 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<int>(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<std::string, unsigned int> mInterfaceBlockRegisterMap;
std::map<std::string, unsigned int> 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, &registerCount);
+}
+
+void UniformHLSL::outputHLSLSamplerUniformGroup(TInfoSinkBase &out,
+ const HLSLTextureSamplerGroup textureGroup,
+ const TVector<const TIntermSymbol *> &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, &registerCount);
+ 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<TVector<const TIntermSymbol *>> 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 &registerString = 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<const TIntermSymbol *> &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<int4>";
- case EbtISampler3D: return "Texture3D<int4>";
- case EbtISamplerCube: return "Texture2DArray<int4>";
- case EbtISampler2DArray: return "Texture2DArray<int4>";
- case EbtUSampler2D: return "Texture2D<uint4>";
- case EbtUSampler3D: return "Texture3D<uint4>";
- case EbtUSamplerCube: return "Texture2DArray<uint4>";
- case EbtUSampler2DArray: return "Texture2DArray<uint4>";
- 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<int4>";
+ case HLSL_TEXTURE_3D_INT4:
+ return "Texture3D<int4>";
+ case HLSL_TEXTURE_2D_ARRAY_INT4:
+ return "Texture2DArray<int4>";
+ case HLSL_TEXTURE_2D_UINT4:
+ return "Texture2D<uint4>";
+ case HLSL_TEXTURE_3D_UINT4:
+ return "Texture3D<uint4>";
+ case HLSL_TEXTURE_2D_ARRAY_UINT4:
+ return "Texture2DArray<uint4>";
+ 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 "<unknown texture type>";
}
+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 "<unknown texture type>";
+}
+
+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<const TVariable *>(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<TFunction *>(symbol);
for (ParamIndex::const_iterator i = pIndex.begin();
i != pIndex.end(); ++i)
{
- const TParameter &param = function->getParam(*i);
+ const TConstParameter &param = 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<size_t>(type.isArray() ? type.getArraySize() : 1);
+ const size_t location = static_cast<size_t>(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 <set>
@@ -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<int, TIntermSymbol*> OutputMap;
- OutputMap mOutputMap;
+ typedef std::vector<TIntermSymbol *> OutputVector;
+ OutputVector mOutputs;
+ OutputVector mUnspecifiedLocationOutputs;
std::set<TString> 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<sh::Attribute> *attribs,
- std::vector<sh::Attribute> *outputVariables,
+ std::vector<sh::OutputVariable> *outputVariables,
std::vector<sh::Uniform> *uniforms,
std::vector<sh::Varying> *varyings,
std::vector<sh::InterfaceBlock> *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<sh::Attribute> *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<const TVariable *>(
+ 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<const TVariable *>(
+ 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<const TVariable *>(
+ 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();
@@ -367,6 +512,26 @@ void CollectVariables::visitVariable(const TIntermSymbol *variable,
template <>
void CollectVariables::visitVariable(const TIntermSymbol *variable,
+ std::vector<OutputVariable> *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<unsigned int>(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<InterfaceBlock> *infoList) const
{
InterfaceBlock interfaceBlock;
@@ -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<Attribute> *attribs,
- std::vector<Attribute> *outputVariables,
+ std::vector<OutputVariable> *outputVariables,
std::vector<Uniform> *uniforms,
std::vector<Varying> *varyings,
std::vector<InterfaceBlock> *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 <typename VarT>
@@ -40,13 +40,14 @@ class CollectVariables : public TIntermTraverser
void visitInfoList(const TIntermSequence &sequence, std::vector<VarT> *infoList) const;
std::vector<Attribute> *mAttribs;
- std::vector<Attribute> *mOutputVariables;
+ std::vector<OutputVariable> *mOutputVariables;
std::vector<Uniform> *mUniforms;
std::vector<Varying> *mVaryings;
std::vector<InterfaceBlock> *mInterfaceBlocks;
std::map<std::string, InterfaceBlockField *> 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<int>(mCurrentOffset * BytesPerComponent),
+ static_cast<int>(arrayStride * BytesPerComponent),
+ static_cast<int>(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<TGraphSymbol *> 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<TVariable*>(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<int>(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 <interm.intermNode> translation_unit function_definition
%type <interm.intermNode> statement simple_statement
-%type <interm.intermAggregate> statement_list compound_statement
+%type <interm.intermAggregate> statement_list compound_statement compound_statement_no_new_scope
%type <interm.intermNode> declaration_statement selection_statement expression_statement
%type <interm.intermNode> declaration external_declaration
-%type <interm.intermNode> for_init_statement compound_statement_no_new_scope
+%type <interm.intermNode> for_init_statement
%type <interm.nodePair> selection_rest_statement for_rest_statement
%type <interm.intermSwitch> switch_statement
%type <interm.intermCase> 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 &param = 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<TFunction*>(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<TIntermTyped*>($5.node1), reinterpret_cast<TIntermTyped*>($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<TFunction*>(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<int>::max();
+ *value = std::numeric_limits<unsigned int>::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 <typename VarT>
void traverse(const TType &type, const TString &name, std::vector<VarT> *output);