Add type comparison, type retrieval, original name retrieval to ShaderVariable.
This is needed to effectively use the new APIs to get shader variable info.
BUG=angle::770
TEST=ShaderVariableTest
Change-Id: Ia591eb567868ebe898f4a7449c64167ad212f59b
Reviewed-on: https://chromium-review.googlesource.com/221388
Tested-by: Zhenyao Mo <zmo@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Reviewed-by: Shannon Woods <shannonwoods@chromium.org>
diff --git a/include/GLSLANG/ShaderVars.h b/include/GLSLANG/ShaderVars.h
index 9c38647..da21c3e 100644
--- a/include/GLSLANG/ShaderVars.h
+++ b/include/GLSLANG/ShaderVars.h
@@ -52,6 +52,21 @@
unsigned int elementCount() const { return std::max(1u, arraySize); }
bool isStruct() const { return !fields.empty(); }
+ // All of the shader's variables are described using nested data
+ // structures. This is needed in order to disambiguate similar looking
+ // types, such as two structs containing the same fields, but in
+ // different orders. "findInfoByMappedName" provides an easy query for
+ // users to dive into the data structure and fetch the unique variable
+ // instance corresponding to a dereferencing chain of the top-level
+ // variable.
+ // Given a mapped name like 'a[0].b.c[0]', return the ShaderVariable
+ // that defines 'c' in |leafVar|, and the original name 'A[0].B.C[0]'
+ // in |originalName|, based on the assumption that |this| defines 'a'.
+ // If no match is found, return false.
+ bool findInfoByMappedName(const std::string &mappedFullName,
+ const ShaderVariable **leafVar,
+ std::string* originalFullName) const;
+
GLenum type;
GLenum precision;
std::string name;
@@ -60,6 +75,16 @@
bool staticUse;
std::vector<ShaderVariable> fields;
std::string structName;
+
+ protected:
+ bool isSameVariableAtLinkTime(const ShaderVariable &other,
+ bool matchPrecision) const;
+
+ bool operator==(const ShaderVariable &other) const;
+ bool operator!=(const ShaderVariable &other) const
+ {
+ return !operator==(other);
+ }
};
struct COMPILER_EXPORT Uniform : public ShaderVariable
@@ -68,6 +93,16 @@
~Uniform();
Uniform(const Uniform &other);
Uniform &operator=(const Uniform &other);
+ bool operator==(const Uniform &other) const;
+ bool operator!=(const Uniform &other) const
+ {
+ return !operator==(other);
+ }
+
+ // Decide whether two uniforms are the same at shader link time,
+ // assuming one from vertex shader and the other from fragment shader.
+ // See GLSL ES Spec 3.00.3, sec 4.3.5.
+ bool isSameUniformAtLinkTime(const Uniform &other) const;
};
struct COMPILER_EXPORT Attribute : public ShaderVariable
@@ -76,6 +111,11 @@
~Attribute();
Attribute(const Attribute &other);
Attribute &operator=(const Attribute &other);
+ bool operator==(const Attribute &other) const;
+ bool operator!=(const Attribute &other) const
+ {
+ return !operator==(other);
+ }
int location;
};
@@ -86,6 +126,18 @@
~InterfaceBlockField();
InterfaceBlockField(const InterfaceBlockField &other);
InterfaceBlockField &operator=(const InterfaceBlockField &other);
+ bool operator==(const InterfaceBlockField &other) const;
+ bool operator!=(const InterfaceBlockField &other) const
+ {
+ return !operator==(other);
+ }
+
+ // Decide whether two InterfaceBlock fields are the same at shader
+ // link time, assuming one from vertex shader and the other from
+ // fragment shader.
+ // See GLSL ES Spec 3.00.3, sec 4.3.7.
+ bool isSameInterfaceBlockFieldAtLinkTime(
+ const InterfaceBlockField &other) const;
bool isRowMajorLayout;
};
@@ -94,8 +146,18 @@
{
Varying();
~Varying();
- Varying(const Varying &other);
+ Varying(const Varying &otherg);
Varying &operator=(const Varying &other);
+ bool operator==(const Varying &other) const;
+ bool operator!=(const Varying &other) const
+ {
+ return !operator==(other);
+ }
+
+ // Decide whether two varyings are the same at shader link time,
+ // assuming one from vertex shader and the other from fragment shader.
+ // See GLSL ES Spec 3.00.3, sec 4.3.9.
+ bool isSameVaryingAtLinkTime(const Varying &other) const;
InterpolationType interpolation;
bool isInvariant;
diff --git a/src/compiler/translator/ShaderVars.cpp b/src/compiler/translator/ShaderVars.cpp
index 822c558..3098a7f 100644
--- a/src/compiler/translator/ShaderVars.cpp
+++ b/src/compiler/translator/ShaderVars.cpp
@@ -9,6 +9,8 @@
#include <GLSLANG/ShaderLang.h>
+#include "compiler/translator/compilerdebug.h"
+
namespace sh
{
@@ -53,6 +55,126 @@
return *this;
}
+bool ShaderVariable::operator==(const ShaderVariable &other) const
+{
+ if (type != other.type ||
+ precision != other.precision ||
+ name != other.name ||
+ mappedName != other.mappedName ||
+ arraySize != other.arraySize ||
+ staticUse != other.staticUse ||
+ fields.size() != other.fields.size() ||
+ structName != other.structName)
+ {
+ return false;
+ }
+ for (size_t ii = 0; ii < fields.size(); ++ii)
+ {
+ if (fields[ii] != other.fields[ii])
+ return false;
+ }
+ return true;
+}
+
+bool ShaderVariable::findInfoByMappedName(
+ const std::string &mappedFullName,
+ const ShaderVariable **leafVar, std::string *originalFullName) const
+{
+ ASSERT(leafVar && originalFullName);
+ // There are three cases:
+ // 1) the top variable is of struct type;
+ // 2) the top variable is an array;
+ // 3) otherwise.
+ size_t pos = mappedFullName.find_first_of(".[");
+ std::string topName;
+
+ if (pos == std::string::npos)
+ {
+ // Case 3.
+ if (mappedFullName != this->mappedName)
+ return false;
+ *originalFullName = this->name;
+ *leafVar = this;
+ return true;
+ }
+ else
+ {
+ std::string topName = mappedFullName.substr(0, pos);
+ if (topName != this->mappedName)
+ return false;
+ std::string originalName = this->name;
+ std::string remaining;
+ if (mappedFullName[pos] == '[')
+ {
+ // Case 2.
+ size_t closePos = mappedFullName.find_first_of(']');
+ if (closePos < pos || closePos == std::string::npos)
+ return false;
+ // Append '[index]'.
+ originalName += mappedFullName.substr(pos, closePos - pos + 1);
+ if (closePos + 1 == mappedFullName.size())
+ {
+ *originalFullName = originalName;
+ *leafVar = this;
+ return true;
+ }
+ else
+ {
+ // In the form of 'a[0].b', so after ']', '.' is expected.
+ if (mappedFullName[closePos + 1] != '.')
+ return false;
+ remaining = mappedFullName.substr(closePos + 2); // Skip "]."
+ }
+ }
+ else
+ {
+ // Case 1.
+ remaining = mappedFullName.substr(pos + 1); // Skip "."
+ }
+ for (size_t ii = 0; ii < this->fields.size(); ++ii)
+ {
+ const ShaderVariable *fieldVar = NULL;
+ std::string originalFieldName;
+ bool found = fields[ii].findInfoByMappedName(
+ remaining, &fieldVar, &originalFieldName);
+ if (found)
+ {
+ *originalFullName = originalName + "." + originalFieldName;
+ *leafVar = fieldVar;
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+bool ShaderVariable::isSameVariableAtLinkTime(
+ const ShaderVariable &other, bool matchPrecision) const
+{
+ if (type != other.type)
+ return false;
+ if (matchPrecision && precision != other.precision)
+ return false;
+ if (name != other.name)
+ return false;
+ ASSERT(mappedName == other.mappedName);
+ if (arraySize != other.arraySize)
+ return false;
+ if (fields.size() != other.fields.size())
+ return false;
+ for (size_t ii = 0; ii < fields.size(); ++ii)
+ {
+ if (!fields[ii].isSameVariableAtLinkTime(other.fields[ii],
+ matchPrecision))
+ {
+ return false;
+ }
+ }
+ if (structName != other.structName)
+ return false;
+ return true;
+}
+
Uniform::Uniform()
{}
@@ -69,6 +191,16 @@
return *this;
}
+bool Uniform::operator==(const Uniform &other) const
+{
+ return ShaderVariable::operator==(other);
+}
+
+bool Uniform::isSameUniformAtLinkTime(const Uniform &other) const
+{
+ return ShaderVariable::isSameVariableAtLinkTime(other, true);
+}
+
Attribute::Attribute()
: location(-1)
{}
@@ -88,6 +220,12 @@
return *this;
}
+bool Attribute::operator==(const Attribute &other) const
+{
+ return (ShaderVariable::operator==(other) &&
+ location == other.location);
+}
+
InterfaceBlockField::InterfaceBlockField()
: isRowMajorLayout(false)
{}
@@ -107,6 +245,19 @@
return *this;
}
+bool InterfaceBlockField::operator==(const InterfaceBlockField &other) const
+{
+ return (ShaderVariable::operator==(other) &&
+ isRowMajorLayout == other.isRowMajorLayout);
+}
+
+bool InterfaceBlockField::isSameInterfaceBlockFieldAtLinkTime(
+ const InterfaceBlockField &other) const
+{
+ return (ShaderVariable::isSameVariableAtLinkTime(other, true) &&
+ isRowMajorLayout == other.isRowMajorLayout);
+}
+
Varying::Varying()
: interpolation(INTERPOLATION_SMOOTH),
isInvariant(false)
@@ -129,6 +280,20 @@
return *this;
}
+bool Varying::operator==(const Varying &other) const
+{
+ return (ShaderVariable::operator==(other) &&
+ interpolation == other.interpolation &&
+ isInvariant == other.isInvariant);
+}
+
+bool Varying::isSameVaryingAtLinkTime(const Varying &other) const
+{
+ return (ShaderVariable::isSameVariableAtLinkTime(other, false) &&
+ interpolation == other.interpolation &&
+ isInvariant == other.isInvariant);
+}
+
InterfaceBlock::InterfaceBlock()
: arraySize(0),
layout(BLOCKLAYOUT_PACKED),
diff --git a/tests/compiler_tests/ShaderVariable_test.cpp b/tests/compiler_tests/ShaderVariable_test.cpp
new file mode 100644
index 0000000..b642260
--- /dev/null
+++ b/tests/compiler_tests/ShaderVariable_test.cpp
@@ -0,0 +1,221 @@
+//
+// Copyright (c) 2014 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.
+//
+// CollectVariables_test.cpp:
+// Some tests for shader inspection
+//
+
+#include "angle_gl.h"
+#include "gtest/gtest.h"
+#include "GLSLANG/ShaderLang.h"
+
+namespace sh
+{
+
+TEST(ShaderVariableTest, FindInfoByMappedName)
+{
+ // struct A {
+ // float x[2];
+ // vec3 y;
+ // };
+ // struct B {
+ // A a[3];
+ // };
+ // B uni[2];
+ ShaderVariable uni;
+ uni.arraySize = 2;
+ uni.name = "uni";
+ uni.mappedName = "m_uni";
+ uni.structName = "B";
+ {
+ ShaderVariable a;
+ a.arraySize = 3;
+ a.name = "a";
+ a.mappedName = "m_a";
+ a.structName = "A";
+ {
+ ShaderVariable x(GL_FLOAT, 2);
+ x.name = "x";
+ x.mappedName = "m_x";
+ a.fields.push_back(x);
+
+ ShaderVariable y(GL_FLOAT_VEC3, 0);
+ y.name = "y";
+ y.mappedName = "m_y";
+ a.fields.push_back(y);
+ }
+ uni.fields.push_back(a);
+ }
+
+ const ShaderVariable *leafVar = NULL;
+ std::string originalFullName;
+
+ std::string mappedFullName = "wrongName";
+ EXPECT_FALSE(uni.findInfoByMappedName(
+ mappedFullName, &leafVar, &originalFullName));
+
+ mappedFullName = "m_uni";
+ EXPECT_TRUE(uni.findInfoByMappedName(
+ mappedFullName, &leafVar, &originalFullName));
+ EXPECT_EQ(&uni, leafVar);
+ EXPECT_STREQ("uni", originalFullName.c_str());
+
+ mappedFullName = "m_uni[0].m_a[1].wrongName";
+ EXPECT_FALSE(uni.findInfoByMappedName(
+ mappedFullName, &leafVar, &originalFullName));
+
+ mappedFullName = "m_uni[0].m_a[1].m_x";
+ EXPECT_TRUE(uni.findInfoByMappedName(
+ mappedFullName, &leafVar, &originalFullName));
+ EXPECT_EQ(&(uni.fields[0].fields[0]), leafVar);
+ EXPECT_STREQ("uni[0].a[1].x", originalFullName.c_str());
+
+ mappedFullName = "m_uni[0].m_a[1].m_x[0]";
+ EXPECT_TRUE(uni.findInfoByMappedName(
+ mappedFullName, &leafVar, &originalFullName));
+ EXPECT_EQ(&(uni.fields[0].fields[0]), leafVar);
+ EXPECT_STREQ("uni[0].a[1].x[0]", originalFullName.c_str());
+
+ mappedFullName = "m_uni[0].m_a[1].m_y";
+ EXPECT_TRUE(uni.findInfoByMappedName(
+ mappedFullName, &leafVar, &originalFullName));
+ EXPECT_EQ(&(uni.fields[0].fields[1]), leafVar);
+ EXPECT_STREQ("uni[0].a[1].y", originalFullName.c_str());
+}
+
+TEST(ShaderVariableTest, IsSameUniformWithDifferentFieldOrder)
+{
+ // struct A {
+ // float x;
+ // float y;
+ // };
+ // uniform A uni;
+ Uniform vx_a;
+ vx_a.arraySize = 0;
+ vx_a.name = "uni";
+ vx_a.mappedName = "m_uni";
+ vx_a.structName = "A";
+ {
+ ShaderVariable x(GL_FLOAT, 0);
+ x.name = "x";
+ x.mappedName = "m_x";
+ vx_a.fields.push_back(x);
+
+ ShaderVariable y(GL_FLOAT, 0);
+ y.name = "y";
+ y.mappedName = "m_y";
+ vx_a.fields.push_back(y);
+ }
+
+ // struct A {
+ // float y;
+ // float x;
+ // };
+ // uniform A uni;
+ Uniform fx_a;
+ fx_a.arraySize = 0;
+ fx_a.name = "uni";
+ fx_a.mappedName = "m_uni";
+ fx_a.structName = "A";
+ {
+ ShaderVariable y(GL_FLOAT, 0);
+ y.name = "y";
+ y.mappedName = "m_y";
+ fx_a.fields.push_back(y);
+
+ ShaderVariable x(GL_FLOAT, 0);
+ x.name = "x";
+ x.mappedName = "m_x";
+ fx_a.fields.push_back(x);
+ }
+
+ EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a));
+}
+
+TEST(ShaderVariableTest, IsSameUniformWithDifferentStructNames)
+{
+ // struct A {
+ // float x;
+ // float y;
+ // };
+ // uniform A uni;
+ Uniform vx_a;
+ vx_a.arraySize = 0;
+ vx_a.name = "uni";
+ vx_a.mappedName = "m_uni";
+ vx_a.structName = "A";
+ {
+ ShaderVariable x(GL_FLOAT, 0);
+ x.name = "x";
+ x.mappedName = "m_x";
+ vx_a.fields.push_back(x);
+
+ ShaderVariable y(GL_FLOAT, 0);
+ y.name = "y";
+ y.mappedName = "m_y";
+ vx_a.fields.push_back(y);
+ }
+
+ // struct B {
+ // float x;
+ // float y;
+ // };
+ // uniform B uni;
+ Uniform fx_a;
+ fx_a.arraySize = 0;
+ fx_a.name = "uni";
+ fx_a.mappedName = "m_uni";
+ {
+ ShaderVariable x(GL_FLOAT, 0);
+ x.name = "x";
+ x.mappedName = "m_x";
+ fx_a.fields.push_back(x);
+
+ ShaderVariable y(GL_FLOAT, 0);
+ y.name = "y";
+ y.mappedName = "m_y";
+ fx_a.fields.push_back(y);
+ }
+
+ fx_a.structName = "B";
+ EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a));
+
+ fx_a.structName = "A";
+ EXPECT_TRUE(vx_a.isSameUniformAtLinkTime(fx_a));
+
+ fx_a.structName = "";
+ EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a));
+}
+
+TEST(ShaderVariableTest, IsSameVaryingWithDifferentInvariance)
+{
+ // invariant varying float vary;
+ Varying vx;
+ vx.type = GL_FLOAT;
+ vx.arraySize = 0;
+ vx.precision = GL_MEDIUM_FLOAT;
+ vx.name = "vary";
+ vx.mappedName = "m_vary";
+ vx.staticUse = true;
+ vx.isInvariant = true;
+
+ // varying float vary;
+ Varying fx;
+ fx.type = GL_FLOAT;
+ fx.arraySize = 0;
+ fx.precision = GL_MEDIUM_FLOAT;
+ fx.name = "vary";
+ fx.mappedName = "m_vary";
+ fx.staticUse = true;
+ fx.isInvariant = false;
+
+ EXPECT_FALSE(vx.isSameVaryingAtLinkTime(fx));
+
+ // invariant varying float vary;
+ fx.isInvariant = true;
+ EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx));
+}
+
+} // namespace sh