compiler: Work around a HLSL compiler aliasing opt bug.
BUG=angleproject:1448
Change-Id: I7d5bcbd100069152cea0cb03bc4fa6af1044460b
Reviewed-on: https://chromium-review.googlesource.com/376020
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/compiler/translator/AddDefaultReturnStatements.cpp b/src/compiler/translator/AddDefaultReturnStatements.cpp
index 97111d1..06aa738 100644
--- a/src/compiler/translator/AddDefaultReturnStatements.cpp
+++ b/src/compiler/translator/AddDefaultReturnStatements.cpp
@@ -54,48 +54,13 @@
return true;
}
- static TIntermTyped *GenerateTypeConstructor(const TType &returnType)
- {
- // Base case, constructing a single element
- if (!returnType.isArray())
- {
- size_t objectSize = returnType.getObjectSize();
- TConstantUnion *constantUnion = new TConstantUnion[objectSize];
- for (size_t constantIdx = 0; constantIdx < objectSize; constantIdx++)
- {
- constantUnion[constantIdx].setFConst(0.0f);
- }
-
- TIntermConstantUnion *intermConstantUnion =
- new TIntermConstantUnion(constantUnion, returnType);
- return intermConstantUnion;
- }
-
- // Recursive case, construct an array of single elements
- TIntermAggregate *constructorAggrigate =
- new TIntermAggregate(TypeToConstructorOperator(returnType));
- constructorAggrigate->setType(returnType);
-
- size_t arraySize = returnType.getArraySize();
- for (size_t arrayIdx = 0; arrayIdx < arraySize; arrayIdx++)
- {
- TType arrayElementType(returnType);
- arrayElementType.clearArrayness();
-
- constructorAggrigate->getSequence()->push_back(
- GenerateTypeConstructor(arrayElementType));
- }
-
- return constructorAggrigate;
- }
-
bool visitAggregate(Visit, TIntermAggregate *node) override
{
TType returnType;
if (IsFunctionWithoutReturnStatement(node, &returnType))
{
TIntermBranch *branch =
- new TIntermBranch(EOpReturn, GenerateTypeConstructor(returnType));
+ new TIntermBranch(EOpReturn, TIntermTyped::CreateZero(returnType));
TIntermAggregate *lastNode = node->getSequence()->back()->getAsAggregate();
lastNode->getSequence()->push_back(branch);
diff --git a/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp b/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp
new file mode 100644
index 0000000..bca9338
--- /dev/null
+++ b/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp
@@ -0,0 +1,106 @@
+//
+// Copyright (c) 2016 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.
+//
+
+// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
+// may record a variable as aliasing another. Sometimes the alias information gets garbled
+// so we work around this issue by breaking the aliasing chain in inner loops.
+
+#include "BreakVariableAliasingInInnerLoops.h"
+
+#include "compiler/translator/IntermNode.h"
+
+// A HLSL compiler developer gave us more details on the root cause and the workaround needed:
+// The root problem is that if the HLSL compiler is applying aliasing information even on
+// incomplete simulations (in this case, a single pass). The bug is triggered by an assignment
+// that comes from a series of assignments, possibly with swizzled or ternary operators with
+// known conditionals, where the source is before the loop.
+// So, a workaround is to add a +0 term to variables the first time they are assigned to in
+// an inner loop (if they are declared in an outside scope, otherwise there is no need).
+// This will break the aliasing chain.
+
+// For simplicity here we add a +0 to any assignment that is in at least two nested loops. Because
+// the bug only shows up with swizzles, and ternary assignment, whole array or whole structure
+// assignment don't need a workaround.
+
+namespace
+{
+
+class AliasingBreaker : public TIntermTraverser
+{
+ public:
+ AliasingBreaker() : TIntermTraverser(true, false, true) {}
+
+ protected:
+ bool visitBinary(Visit visit, TIntermBinary *binary)
+ {
+ if (visit != PreVisit)
+ {
+ return false;
+ }
+
+ if (mLoopLevel < 2 || !binary->isAssignment())
+ {
+ return true;
+ }
+
+ TIntermTyped *B = binary->getRight();
+ TType type = B->getType();
+
+ if (!type.isScalar() && !type.isVector() && !type.isMatrix())
+ {
+ return true;
+ }
+
+ if (type.isArray() || IsSampler(type.getBasicType()))
+ {
+ return true;
+ }
+
+ // We have a scalar / vector / matrix assignment with loop depth 2.
+ // Transform it from
+ // A = B
+ // to
+ // A = (B + typeof<B>(0));
+
+ TIntermBinary *bPlusZero = new TIntermBinary(EOpAdd, B, TIntermTyped::CreateZero(type));
+ bPlusZero->setLine(B->getLine());
+
+ binary->setRight(bPlusZero);
+
+ return true;
+ }
+
+ bool visitLoop(Visit visit, TIntermLoop *loop)
+ {
+ if (visit == PreVisit)
+ {
+ mLoopLevel++;
+ }
+ else
+ {
+ ASSERT(mLoopLevel > 0);
+ mLoopLevel--;
+ }
+
+ return true;
+ }
+
+ private:
+ int mLoopLevel = 0;
+};
+
+} // anonymous namespace
+
+namespace sh
+{
+
+void BreakVariableAliasingInInnerLoops(TIntermNode *root)
+{
+ AliasingBreaker breaker;
+ root->traverse(&breaker);
+}
+
+} // namespace sh
diff --git a/src/compiler/translator/BreakVariableAliasingInInnerLoops.h b/src/compiler/translator/BreakVariableAliasingInInnerLoops.h
new file mode 100644
index 0000000..b1d906f
--- /dev/null
+++ b/src/compiler/translator/BreakVariableAliasingInInnerLoops.h
@@ -0,0 +1,23 @@
+//
+// Copyright (c) 2016 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.
+//
+
+// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
+// may record a variable as aliasing another. Sometimes the alias information gets garbled
+// so we work around this issue by breaking the aliasing chain in inner loops.
+
+#ifndef COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
+#define COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
+
+class TIntermNode;
+
+namespace sh
+{
+
+void BreakVariableAliasingInInnerLoops(TIntermNode *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
diff --git a/src/compiler/translator/EmulateGLFragColorBroadcast.cpp b/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
index a664d9e..c3aad7b 100644
--- a/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
+++ b/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
@@ -17,44 +17,18 @@
namespace
{
-TIntermConstantUnion *constructIndexNode(int index)
-{
- TConstantUnion *u = new TConstantUnion[1];
- u[0].setIConst(index);
-
- TType type(EbtInt, EbpUndefined, EvqConst, 1);
- TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
- return node;
-}
-
-TIntermBinary *constructGLFragDataNode(int index)
-{
- TIntermBinary *indexDirect = new TIntermBinary(EOpIndexDirect);
- TIntermSymbol *symbol = new TIntermSymbol(0, "gl_FragData", TType(EbtFloat, 4));
- indexDirect->setLeft(symbol);
- TIntermConstantUnion *indexNode = constructIndexNode(index);
- indexDirect->setRight(indexNode);
- return indexDirect;
-}
-
-TIntermBinary *constructGLFragDataAssignNode(int index)
-{
- TIntermBinary *assign = new TIntermBinary(EOpAssign);
- assign->setLeft(constructGLFragDataNode(index));
- assign->setRight(constructGLFragDataNode(0));
- assign->setType(TType(EbtFloat, 4));
- return assign;
-}
-
class GLFragColorBroadcastTraverser : public TIntermTraverser
{
public:
- GLFragColorBroadcastTraverser()
- : TIntermTraverser(true, false, false), mMainSequence(nullptr), mGLFragColorUsed(false)
+ GLFragColorBroadcastTraverser(int maxDrawBuffers)
+ : TIntermTraverser(true, false, false),
+ mMainSequence(nullptr),
+ mGLFragColorUsed(false),
+ mMaxDrawBuffers(maxDrawBuffers)
{
}
- void broadcastGLFragColor(int maxDrawBuffers);
+ void broadcastGLFragColor();
bool isGLFragColorUsed() const { return mGLFragColorUsed; }
@@ -62,11 +36,39 @@
void visitSymbol(TIntermSymbol *node) override;
bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ TIntermBinary *constructGLFragDataNode(int index) const;
+ TIntermBinary *constructGLFragDataAssignNode(int index) const;
+
private:
TIntermSequence *mMainSequence;
bool mGLFragColorUsed;
+ int mMaxDrawBuffers;
};
+TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataNode(int index) const
+{
+ TType gl_FragDataElementType = TType(EbtFloat, 4);
+ TType gl_FragDataType = gl_FragDataElementType;
+ gl_FragDataType.setArraySize(mMaxDrawBuffers);
+
+ TIntermSymbol *symbol = new TIntermSymbol(0, "gl_FragData", gl_FragDataType);
+ TIntermTyped *indexNode = TIntermTyped::CreateIndexNode(index);
+
+ TIntermBinary *binary = new TIntermBinary(EOpIndexDirect);
+ binary->setLeft(symbol);
+ binary->setRight(indexNode);
+ binary->setType(gl_FragDataElementType);
+ return binary;
+}
+
+TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataAssignNode(int index) const
+{
+ TIntermTyped *fragDataIndex = constructGLFragDataNode(index);
+ TIntermTyped *fragDataZero = constructGLFragDataNode(0);
+
+ return new TIntermBinary(EOpAssign, fragDataIndex, fragDataZero);
+}
+
void GLFragColorBroadcastTraverser::visitSymbol(TIntermSymbol *node)
{
if (node->getSymbol() == "gl_FragColor")
@@ -101,9 +103,9 @@
return true;
}
-void GLFragColorBroadcastTraverser::broadcastGLFragColor(int maxDrawBuffers)
+void GLFragColorBroadcastTraverser::broadcastGLFragColor()
{
- ASSERT(maxDrawBuffers > 1);
+ ASSERT(mMaxDrawBuffers > 1);
if (!mGLFragColorUsed)
{
return;
@@ -113,7 +115,7 @@
// gl_FragData[1] = gl_FragData[0];
// ...
// gl_FragData[maxDrawBuffers - 1] = gl_FragData[0];
- for (int colorIndex = 1; colorIndex < maxDrawBuffers; ++colorIndex)
+ for (int colorIndex = 1; colorIndex < mMaxDrawBuffers; ++colorIndex)
{
mMainSequence->insert(mMainSequence->end(), constructGLFragDataAssignNode(colorIndex));
}
@@ -126,12 +128,12 @@
std::vector<sh::OutputVariable> *outputVariables)
{
ASSERT(maxDrawBuffers > 1);
- GLFragColorBroadcastTraverser traverser;
+ GLFragColorBroadcastTraverser traverser(maxDrawBuffers);
root->traverse(&traverser);
if (traverser.isGLFragColorUsed())
{
traverser.updateTree();
- traverser.broadcastGLFragColor(maxDrawBuffers);
+ traverser.broadcastGLFragColor();
for (auto &var : *outputVariables)
{
if (var.name == "gl_FragColor")
diff --git a/src/compiler/translator/InitializeVariables.cpp b/src/compiler/translator/InitializeVariables.cpp
index 21f0093..6b01d62 100644
--- a/src/compiler/translator/InitializeVariables.cpp
+++ b/src/compiler/translator/InitializeVariables.cpp
@@ -14,46 +14,6 @@
namespace
{
-TIntermConstantUnion *constructConstUnionNode(const TType &type)
-{
- TType myType = type;
- myType.clearArrayness();
- myType.setQualifier(EvqConst);
- size_t size = myType.getObjectSize();
- TConstantUnion *u = new TConstantUnion[size];
- for (size_t ii = 0; ii < size; ++ii)
- {
- switch (type.getBasicType())
- {
- case EbtFloat:
- u[ii].setFConst(0.0f);
- break;
- case EbtInt:
- u[ii].setIConst(0);
- break;
- case EbtUInt:
- u[ii].setUConst(0u);
- break;
- default:
- UNREACHABLE();
- return nullptr;
- }
- }
-
- TIntermConstantUnion *node = new TIntermConstantUnion(u, myType);
- return node;
-}
-
-TIntermConstantUnion *constructIndexNode(int index)
-{
- TConstantUnion *u = new TConstantUnion[1];
- u[0].setIConst(index);
-
- TType type(EbtInt, EbpUndefined, EvqConst, 1);
- TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
- return node;
-}
-
class VariableInitializer : public TIntermTraverser
{
public:
@@ -120,73 +80,44 @@
void VariableInitializer::insertInitCode(TIntermSequence *sequence)
{
- for (size_t ii = 0; ii < mVariables.size(); ++ii)
+ for (const auto &var : mVariables)
{
- const sh::ShaderVariable &var = mVariables[ii];
TString name = TString(var.name.c_str());
+ TType type = sh::GetShaderVariableType(var);
+
+ // Assign the array elements one by one to keep the AST compatible with ESSL 1.00 which
+ // doesn't have array assignment.
if (var.isArray())
{
- TType type = sh::ConvertShaderVariableTypeToTType(var.type);
size_t pos = name.find_last_of('[');
if (pos != TString::npos)
+ {
name = name.substr(0, pos);
- for (int index = static_cast<int>(var.arraySize) - 1; index >= 0; --index)
- {
- TIntermBinary *assign = new TIntermBinary(EOpAssign);
- sequence->insert(sequence->begin(), assign);
-
- TIntermBinary *indexDirect = new TIntermBinary(EOpIndexDirect);
- TIntermSymbol *symbol = new TIntermSymbol(0, name, type);
- indexDirect->setLeft(symbol);
- TIntermConstantUnion *indexNode = constructIndexNode(index);
- indexDirect->setRight(indexNode);
-
- assign->setLeft(indexDirect);
-
- TIntermConstantUnion *zeroConst = constructConstUnionNode(type);
- assign->setRight(zeroConst);
}
- }
- else if (var.isStruct())
- {
- TFieldList *fields = new TFieldList;
- TSourceLoc loc;
- for (auto field : var.fields)
- {
- fields->push_back(new TField(nullptr, new TString(field.name.c_str()), loc));
- }
- TStructure *structure = new TStructure(new TString(var.structName.c_str()), fields);
- TType type;
- type.setStruct(structure);
- for (int fieldIndex = 0; fieldIndex < static_cast<int>(var.fields.size()); ++fieldIndex)
- {
- TIntermBinary *assign = new TIntermBinary(EOpAssign);
- sequence->insert(sequence->begin(), assign);
+ TType elementType = type;
+ elementType.clearArrayness();
- TIntermBinary *indexDirectStruct = new TIntermBinary(EOpIndexDirectStruct);
- TIntermSymbol *symbol = new TIntermSymbol(0, name, type);
- indexDirectStruct->setLeft(symbol);
- TIntermConstantUnion *indexNode = constructIndexNode(fieldIndex);
- indexDirectStruct->setRight(indexNode);
- assign->setLeft(indexDirectStruct);
+ for (unsigned int i = 0; i < var.arraySize; ++i)
+ {
+ TIntermBinary *element = new TIntermBinary(EOpIndexDirect);
+ element->setLeft(new TIntermSymbol(0, name, type));
+ element->setRight(TIntermTyped::CreateIndexNode(i));
+ element->setType(elementType);
- const sh::ShaderVariable &field = var.fields[fieldIndex];
- TType fieldType = sh::ConvertShaderVariableTypeToTType(field.type);
- TIntermConstantUnion *zeroConst = constructConstUnionNode(fieldType);
- assign->setRight(zeroConst);
+ TIntermTyped *zero = TIntermTyped::CreateZero(elementType);
+ TIntermBinary *assignment = new TIntermBinary(EOpAssign, element, zero);
+
+ sequence->insert(sequence->begin(), assignment);
}
}
else
{
- TType type = sh::ConvertShaderVariableTypeToTType(var.type);
- TIntermBinary *assign = new TIntermBinary(EOpAssign);
- sequence->insert(sequence->begin(), assign);
TIntermSymbol *symbol = new TIntermSymbol(0, name, type);
- assign->setLeft(symbol);
- TIntermConstantUnion *zeroConst = constructConstUnionNode(type);
- assign->setRight(zeroConst);
- }
+ TIntermTyped *zero = TIntermTyped::CreateZero(type);
+ TIntermBinary *assign = new TIntermBinary(EOpAssign, symbol, zero);
+ sequence->insert(sequence->begin(), assign);
+ }
}
}
diff --git a/src/compiler/translator/InitializeVariables.h b/src/compiler/translator/InitializeVariables.h
index f826032..dce1083 100644
--- a/src/compiler/translator/InitializeVariables.h
+++ b/src/compiler/translator/InitializeVariables.h
@@ -13,6 +13,7 @@
typedef std::vector<sh::ShaderVariable> InitVariableList;
+// This function cannot currently initialize structures containing arrays for an ESSL 1.00 backend.
void InitializeVariables(TIntermNode *root, const InitVariableList &vars);
#endif // COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index dcf4787..b00022a 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -21,6 +21,7 @@
#include "compiler/translator/HashNames.h"
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/util.h"
namespace
{
@@ -336,6 +337,83 @@
return true;
}
+// static
+TIntermTyped *TIntermTyped::CreateIndexNode(int index)
+{
+ TConstantUnion *u = new TConstantUnion[1];
+ u[0].setIConst(index);
+
+ TType type(EbtInt, EbpUndefined, EvqConst, 1);
+ TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
+ return node;
+}
+
+// static
+TIntermTyped *TIntermTyped::CreateZero(const TType &type)
+{
+ TType constType(type);
+ constType.setQualifier(EvqConst);
+
+ if (!type.isArray() && type.getBasicType() != EbtStruct)
+ {
+ ASSERT(type.isScalar() || type.isVector() || type.isMatrix());
+
+ size_t size = constType.getObjectSize();
+ TConstantUnion *u = new TConstantUnion[size];
+ for (size_t i = 0; i < size; ++i)
+ {
+ switch (type.getBasicType())
+ {
+ case EbtFloat:
+ u[i].setFConst(0.0f);
+ break;
+ case EbtInt:
+ u[i].setIConst(0);
+ break;
+ case EbtUInt:
+ u[i].setUConst(0u);
+ break;
+ case EbtBool:
+ u[i].setBConst(false);
+ break;
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ }
+
+ TIntermConstantUnion *node = new TIntermConstantUnion(u, constType);
+ return node;
+ }
+
+ TIntermAggregate *constructor = new TIntermAggregate(sh::TypeToConstructorOperator(type));
+ constructor->setType(constType);
+
+ if (type.isArray())
+ {
+ TType elementType(type);
+ elementType.clearArrayness();
+
+ size_t arraySize = type.getArraySize();
+ for (size_t i = 0; i < arraySize; ++i)
+ {
+ constructor->getSequence()->push_back(CreateZero(elementType));
+ }
+ }
+ else
+ {
+ ASSERT(type.getBasicType() == EbtStruct);
+
+ TStructure *structure = type.getStruct();
+ for (const auto &field : structure->fields())
+ {
+ constructor->getSequence()->push_back(CreateZero(*field->type()));
+ }
+ }
+
+ return constructor;
+}
+
TIntermConstantUnion::TIntermConstantUnion(const TIntermConstantUnion &node) : TIntermTyped(node)
{
mUnionArrayPointer = node.mUnionArrayPointer;
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index 457e9ba..5b4c5d9 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -159,6 +159,9 @@
bool isConstructorWithOnlyConstantUnionParameters();
+ static TIntermTyped *CreateIndexNode(int index);
+ static TIntermTyped *CreateZero(const TType &type);
+
protected:
TType mType;
diff --git a/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp b/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
index 775c5d8..6a70aa5 100644
--- a/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
+++ b/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
@@ -37,21 +37,11 @@
return false;
}
-TIntermConstantUnion *ConstructIndexNode(int index)
-{
- TConstantUnion *u = new TConstantUnion[1];
- u[0].setIConst(index);
-
- TType type(EbtInt, EbpUndefined, EvqConst, 1);
- TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
- return node;
-}
-
TIntermBinary *ConstructVectorIndexBinaryNode(TIntermSymbol *symbolNode, int index)
{
TIntermBinary *binary = new TIntermBinary(EOpIndexDirect);
binary->setLeft(symbolNode);
- TIntermConstantUnion *indexNode = ConstructIndexNode(index);
+ TIntermTyped *indexNode = TIntermTyped::CreateIndexNode(index);
binary->setRight(indexNode);
return binary;
}
@@ -64,7 +54,7 @@
TIntermBinary *binary = new TIntermBinary(EOpIndexDirect);
binary->setLeft(colVectorNode);
- TIntermConstantUnion *rowIndexNode = ConstructIndexNode(rowIndex);
+ TIntermTyped *rowIndexNode = TIntermTyped::CreateIndexNode(rowIndex);
binary->setRight(rowIndexNode);
return binary;
}
diff --git a/src/compiler/translator/TranslatorHLSL.cpp b/src/compiler/translator/TranslatorHLSL.cpp
index 6721d89..fb08bec 100644
--- a/src/compiler/translator/TranslatorHLSL.cpp
+++ b/src/compiler/translator/TranslatorHLSL.cpp
@@ -8,6 +8,7 @@
#include "compiler/translator/AddDefaultReturnStatements.h"
#include "compiler/translator/ArrayReturnValueToOutParameter.h"
+#include "compiler/translator/BreakVariableAliasingInInnerLoops.h"
#include "compiler/translator/EmulatePrecision.h"
#include "compiler/translator/ExpandIntegerPowExpressions.h"
#include "compiler/translator/IntermNodePatternMatcher.h"
@@ -75,6 +76,12 @@
sh::RewriteElseBlocks(root, getTemporaryIndex());
}
+ // Work around an HLSL compiler frontend aliasing optimization bug.
+ // TODO(cwallez) The date is 2016-08-25, Microsoft said the bug would be fixed
+ // in the next release of d3dcompiler.dll, it would be nice to detect the DLL
+ // version and only apply the workaround if it is too old.
+ sh::BreakVariableAliasingInInnerLoops(root);
+
bool precisionEmulation =
getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision;
diff --git a/src/compiler/translator/util.cpp b/src/compiler/translator/util.cpp
index 9b59ccc..d6339a8 100644
--- a/src/compiler/translator/util.cpp
+++ b/src/compiler/translator/util.cpp
@@ -278,9 +278,9 @@
}
}
-TType ConvertShaderVariableTypeToTType(sh::GLenum type)
+TType GetShaderVariableBasicType(const sh::ShaderVariable &var)
{
- switch (type)
+ switch (var.type)
{
case GL_FLOAT:
return TType(EbtFloat);
@@ -330,6 +330,35 @@
}
}
+TType GetShaderVariableType(const sh::ShaderVariable &var)
+{
+ TType type;
+ if (var.isStruct())
+ {
+ TFieldList *fields = new TFieldList;
+ TSourceLoc loc;
+ for (const auto &field : var.fields)
+ {
+ TType *fieldType = new TType(GetShaderVariableType(field));
+ fields->push_back(new TField(fieldType, new TString(field.name.c_str()), loc));
+ }
+ TStructure *structure = new TStructure(new TString(var.structName.c_str()), fields);
+
+ type.setBasicType(EbtStruct);
+ type.setStruct(structure);
+ }
+ else
+ {
+ type = GetShaderVariableBasicType(var);
+ }
+
+ if (var.isArray())
+ {
+ type.setArraySize(var.elementCount());
+ }
+ return type;
+}
+
TOperator TypeToConstructorOperator(const TType &type)
{
switch (type.getBasicType())
diff --git a/src/compiler/translator/util.h b/src/compiler/translator/util.h
index 3f24f66..4c1713a 100644
--- a/src/compiler/translator/util.h
+++ b/src/compiler/translator/util.h
@@ -37,8 +37,9 @@
bool IsVarying(TQualifier qualifier);
InterpolationType GetInterpolationType(TQualifier qualifier);
TString ArrayString(const TType &type);
-// Handles only basic output variable types.
-TType ConvertShaderVariableTypeToTType(sh::GLenum type);
+
+TType GetShaderVariableBasicType(const sh::ShaderVariable &var);
+TType GetShaderVariableType(const sh::ShaderVariable &var);
TOperator TypeToConstructorOperator(const TType &type);