Add support for arrays of arrays to VariableLocation
Array indices are sorted so that the outermost index is in the back.
This is because we want to be consistent with future arrays of arrays
parsing code. In parsing we'll have a utility function to make a
TType object into an array, and there it's most natural to push the
new outermost sizes to the back of the vector.
Further patches will still be needed to parse arrays of arrays and
add support to arrays of arrays into the API.
BUG=angleproject:2125
TEST=angle_unittests, angle_end2end_tests
Change-Id: I6c88edabf68ae9dbd803ec6d20543016c408b702
Reviewed-on: https://chromium-review.googlesource.com/686414
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/common/angleutils.cpp b/src/common/angleutils.cpp
index d3d29a3..7afc6aa 100644
--- a/src/common/angleutils.cpp
+++ b/src/common/angleutils.cpp
@@ -17,6 +17,34 @@
const uintptr_t DirtyPointer = std::numeric_limits<uintptr_t>::max();
}
+std::string ArrayString(unsigned int i)
+{
+ // We assume that UINT_MAX and GL_INVALID_INDEX are equal.
+ ASSERT(i != UINT_MAX);
+
+ std::stringstream strstr;
+ strstr << "[";
+ strstr << i;
+ strstr << "]";
+ return strstr.str();
+}
+
+std::string ArrayIndexString(const std::vector<unsigned int> &indices)
+{
+ std::stringstream strstr;
+
+ for (auto indicesIt = indices.rbegin(); indicesIt != indices.rend(); ++indicesIt)
+ {
+ // We assume that UINT_MAX and GL_INVALID_INDEX are equal.
+ ASSERT(*indicesIt != UINT_MAX);
+ strstr << "[";
+ strstr << (*indicesIt);
+ strstr << "]";
+ }
+
+ return strstr.str();
+}
+
size_t FormatStringIntoVector(const char *fmt, va_list vararg, std::vector<char>& outBuffer)
{
// The state of the va_list passed to vsnprintf is undefined after the call, do a copy in case
diff --git a/src/common/angleutils.h b/src/common/angleutils.h
index 6365f4e..bd28794 100644
--- a/src/common/angleutils.h
+++ b/src/common/angleutils.h
@@ -160,23 +160,11 @@
return strings.insert(str).first->c_str();
}
-inline std::string ArrayString(unsigned int i)
-{
- // We assume UINT_MAX and GL_INVALID_INDEX are equal
- // See DynamicHLSL.cpp
- if (i == UINT_MAX)
- {
- return "";
- }
+std::string ArrayString(unsigned int i);
- std::stringstream strstr;
-
- strstr << "[";
- strstr << i;
- strstr << "]";
-
- return strstr.str();
-}
+// Indices are stored in vectors with the outermost index in the back. In the output of the function
+// the indices are reversed.
+std::string ArrayIndexString(const std::vector<unsigned int> &indices);
inline std::string Str(int i)
{
diff --git a/src/common/angleutils_unittest.cpp b/src/common/angleutils_unittest.cpp
new file mode 100644
index 0000000..73a6756
--- /dev/null
+++ b/src/common/angleutils_unittest.cpp
@@ -0,0 +1,26 @@
+//
+// Copyright (c) 2017 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.
+//
+
+// angleutils_unittest.cpp: Unit tests for ANGLE's common utilities.
+
+#include "gtest/gtest.h"
+
+#include "common/angleutils.h"
+
+namespace
+{
+
+// Test that multiple array indices are written out in the right order.
+TEST(ArrayIndexString, MultipleArrayIndices)
+{
+ std::vector<unsigned int> indices;
+ indices.push_back(12);
+ indices.push_back(34);
+ indices.push_back(56);
+ EXPECT_EQ("[56][34][12]", ArrayIndexString(indices));
+}
+
+} // anonymous namespace
diff --git a/src/common/utilities.cpp b/src/common/utilities.cpp
index efa175a..efb5323 100644
--- a/src/common/utilities.cpp
+++ b/src/common/utilities.cpp
@@ -735,35 +735,39 @@
}
}
-std::string ParseResourceName(const std::string &name, size_t *outSubscript)
+std::string ParseResourceName(const std::string &name, std::vector<unsigned int> *outSubscripts)
{
- // Strip any trailing array operator and retrieve the subscript
- size_t open = name.find_last_of('[');
- size_t close = name.find_last_of(']');
- bool hasIndex = (open != std::string::npos) && (close == name.length() - 1);
- if (!hasIndex)
+ if (outSubscripts)
{
- if (outSubscript)
- {
- *outSubscript = GL_INVALID_INDEX;
- }
- return name;
+ outSubscripts->clear();
}
-
- if (outSubscript)
+ // Strip any trailing array indexing operators and retrieve the subscripts.
+ size_t baseNameLength = name.length();
+ bool hasIndex = true;
+ while (hasIndex)
{
- int index = atoi(name.substr(open + 1).c_str());
- if (index >= 0)
+ size_t open = name.find_last_of('[', baseNameLength - 1);
+ size_t close = name.find_last_of(']', baseNameLength - 1);
+ hasIndex = (open != std::string::npos) && (close == baseNameLength - 1);
+ if (hasIndex)
{
- *outSubscript = index;
- }
- else
- {
- *outSubscript = GL_INVALID_INDEX;
+ baseNameLength = open;
+ if (outSubscripts)
+ {
+ int index = atoi(name.substr(open + 1).c_str());
+ if (index >= 0)
+ {
+ outSubscripts->push_back(index);
+ }
+ else
+ {
+ outSubscripts->push_back(GL_INVALID_INDEX);
+ }
+ }
}
}
- return name.substr(0, open);
+ return name.substr(0, baseNameLength);
}
unsigned int ParseAndStripArrayIndex(std::string *name)
diff --git a/src/common/utilities.h b/src/common/utilities.h
index 84e1ff7..e26f781 100644
--- a/src/common/utilities.h
+++ b/src/common/utilities.h
@@ -12,9 +12,10 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include "angle_gl.h"
-#include <string>
#include <math.h>
+#include <string>
+#include <vector>
+#include "angle_gl.h"
#include "common/mathutil.h"
@@ -49,10 +50,12 @@
size_t CubeMapTextureTargetToLayerIndex(GLenum target);
GLenum LayerIndexToCubeMapTextureTarget(size_t index);
-// Parse the base resource name and array index. Returns the base name of the resource.
-// outSubscript is set to GL_INVALID_INDEX if the provided name is not an array or the array index
-// is invalid.
-std::string ParseResourceName(const std::string &name, size_t *outSubscript);
+// Parse the base resource name and array indices. Returns the base name of the resource.
+// If the provided name doesn't index an array, the outSubscripts vector will be empty.
+// If the provided name indexes an array, the outSubscripts vector will contain indices with
+// outermost array indices in the back. If an array index is invalid, GL_INVALID_INDEX is added to
+// outSubscripts.
+std::string ParseResourceName(const std::string &name, std::vector<unsigned int> *outSubscripts);
// Find the range of index values in the provided indices pointer. Primitive restart indices are
// only counted in the range if primitive restart is disabled.
diff --git a/src/common/utilities_unittest.cpp b/src/common/utilities_unittest.cpp
index 92b8d2d..7447ae4 100644
--- a/src/common/utilities_unittest.cpp
+++ b/src/common/utilities_unittest.cpp
@@ -13,43 +13,66 @@
namespace
{
+// Test parsing valid single array indices
TEST(ParseResourceName, ArrayIndex)
{
- size_t index;
- EXPECT_EQ("foo", gl::ParseResourceName("foo[123]", &index));
- EXPECT_EQ(123u, index);
+ std::vector<unsigned int> indices;
+ EXPECT_EQ("foo", gl::ParseResourceName("foo[123]", &indices));
+ ASSERT_EQ(1u, indices.size());
+ EXPECT_EQ(123u, indices[0]);
- EXPECT_EQ("bar", gl::ParseResourceName("bar[0]", &index));
- EXPECT_EQ(0u, index);
+ EXPECT_EQ("bar", gl::ParseResourceName("bar[0]", &indices));
+ ASSERT_EQ(1u, indices.size());
+ EXPECT_EQ(0u, indices[0]);
}
+// Parsing a negative array index should result in INVALID_INDEX.
TEST(ParseResourceName, NegativeArrayIndex)
{
- size_t index;
- EXPECT_EQ("foo", gl::ParseResourceName("foo[-1]", &index));
- EXPECT_EQ(GL_INVALID_INDEX, index);
+ std::vector<unsigned int> indices;
+ EXPECT_EQ("foo", gl::ParseResourceName("foo[-1]", &indices));
+ ASSERT_EQ(1u, indices.size());
+ EXPECT_EQ(GL_INVALID_INDEX, indices.back());
}
+// Parsing no array indices should result in an empty array.
TEST(ParseResourceName, NoArrayIndex)
{
- size_t index;
- EXPECT_EQ("foo", gl::ParseResourceName("foo", &index));
- EXPECT_EQ(GL_INVALID_INDEX, index);
+ std::vector<unsigned int> indices;
+ EXPECT_EQ("foo", gl::ParseResourceName("foo", &indices));
+ EXPECT_TRUE(indices.empty());
}
-TEST(ParseResourceName, NULLArrayIndex)
+// The ParseResourceName function should work when a nullptr is passed as the indices output vector.
+TEST(ParseResourceName, NULLArrayIndices)
{
EXPECT_EQ("foo", gl::ParseResourceName("foo[10]", nullptr));
}
+// Parsing multiple array indices should result in outermost array indices being last in the vector.
+TEST(ParseResourceName, MultipleArrayIndices)
+{
+ std::vector<unsigned int> indices;
+ EXPECT_EQ("foo", gl::ParseResourceName("foo[12][34][56]", &indices));
+ ASSERT_EQ(3u, indices.size());
+ // Indices are sorted with outermost array index last.
+ EXPECT_EQ(56u, indices[0]);
+ EXPECT_EQ(34u, indices[1]);
+ EXPECT_EQ(12u, indices[2]);
+}
+
+// Trailing whitespace should not be accepted by ParseResourceName.
TEST(ParseResourceName, TrailingWhitespace)
{
- size_t index;
- EXPECT_EQ("foo ", gl::ParseResourceName("foo ", &index));
- EXPECT_EQ(GL_INVALID_INDEX, index);
+ std::vector<unsigned int> indices;
+ EXPECT_EQ("foo ", gl::ParseResourceName("foo ", &indices));
+ EXPECT_TRUE(indices.empty());
- EXPECT_EQ("foo[10] ", gl::ParseResourceName("foo[10] ", &index));
- EXPECT_EQ(GL_INVALID_INDEX, index);
+ EXPECT_EQ("foo[10] ", gl::ParseResourceName("foo[10] ", &indices));
+ EXPECT_TRUE(indices.empty());
+
+ EXPECT_EQ("foo[10][20] ", gl::ParseResourceName("foo[10][20] ", &indices));
+ EXPECT_TRUE(indices.empty());
}
}
diff --git a/src/libANGLE/BinaryStream.h b/src/libANGLE/BinaryStream.h
index 98ba16f..c42381f 100644
--- a/src/libANGLE/BinaryStream.h
+++ b/src/libANGLE/BinaryStream.h
@@ -46,6 +46,16 @@
*outValue = readInt<IntT>();
}
+ template <class IntT, class VectorElementT>
+ void readIntVector(std::vector<VectorElementT> *param)
+ {
+ unsigned int size = readInt<unsigned int>();
+ for (unsigned int index = 0; index < size; ++index)
+ {
+ param->push_back(readInt<IntT>());
+ }
+ }
+
bool readBool()
{
int value = 0;
@@ -197,6 +207,16 @@
}
}
+ template <class IntT>
+ void writeIntVector(std::vector<IntT> param)
+ {
+ writeInt(param.size());
+ for (IntT element : param)
+ {
+ writeIntOrNegOne(element);
+ }
+ }
+
void writeString(const std::string &v)
{
writeInt(v.length());
diff --git a/src/libANGLE/MemoryProgramCache.cpp b/src/libANGLE/MemoryProgramCache.cpp
index 2ca4b56..c28e1be 100644
--- a/src/libANGLE/MemoryProgramCache.cpp
+++ b/src/libANGLE/MemoryProgramCache.cpp
@@ -238,8 +238,9 @@
uniformIndexIndex++)
{
VariableLocation variable;
- stream.readInt(&variable.element);
+ stream.readIntVector<unsigned int>(&variable.arrayIndices);
stream.readInt(&variable.index);
+ stream.readInt(&variable.flattenedArrayOffset);
stream.readBool(&variable.ignored);
state->mUniformLocations.push_back(variable);
@@ -319,8 +320,9 @@
{
int locationIndex = stream.readInt<int>();
VariableLocation locationData;
- stream.readInt(&locationData.element);
+ stream.readIntVector<unsigned int>(&locationData.arrayIndices);
stream.readInt(&locationData.index);
+ stream.readInt(&locationData.flattenedArrayOffset);
stream.readBool(&locationData.ignored);
state->mOutputLocations[locationIndex] = locationData;
}
@@ -427,8 +429,9 @@
stream.writeInt(state.getUniformLocations().size());
for (const auto &variable : state.getUniformLocations())
{
- stream.writeInt(variable.element);
+ stream.writeIntVector(variable.arrayIndices);
stream.writeIntOrNegOne(variable.index);
+ stream.writeInt(variable.flattenedArrayOffset);
stream.writeInt(variable.ignored);
}
@@ -481,8 +484,9 @@
for (const auto &outputPair : state.getOutputLocations())
{
stream.writeInt(outputPair.first);
- stream.writeIntOrNegOne(outputPair.second.element);
+ stream.writeIntVector(outputPair.second.arrayIndices);
stream.writeIntOrNegOne(outputPair.second.index);
+ stream.writeInt(outputPair.second.flattenedArrayOffset);
stream.writeInt(outputPair.second.ignored);
}
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index a13f55b..3b0d904 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -140,13 +140,16 @@
template <typename VarT>
GLuint GetResourceIndexFromName(const std::vector<VarT> &list, const std::string &name)
{
- size_t subscript = GL_INVALID_INDEX;
- std::string baseName = ParseResourceName(name, &subscript);
+ std::vector<unsigned int> subscripts;
+ std::string baseName = ParseResourceName(name, &subscripts);
// The app is not allowed to specify array indices other than 0 for arrays of basic types
- if (subscript != 0 && subscript != GL_INVALID_INDEX)
+ for (unsigned int subscript : subscripts)
{
- return GL_INVALID_INDEX;
+ if (subscript != 0u)
+ {
+ return GL_INVALID_INDEX;
+ }
}
for (size_t index = 0; index < list.size(); index++)
@@ -154,7 +157,9 @@
const VarT &resource = list[index];
if (resource.name == baseName)
{
- if (resource.isArray() || subscript == GL_INVALID_INDEX)
+ // TODO(oetuaho@nvidia.com): Check array nesting >= number of specified
+ // subscripts once arrays of arrays are supported in ShaderVariable.
+ if ((resource.isArray() || subscripts.empty()) && subscripts.size() <= 1u)
{
return static_cast<GLuint>(index);
}
@@ -178,14 +183,14 @@
bool IncludeSameArrayElement(const std::set<std::string> &nameSet, const std::string &name)
{
- size_t subscript = GL_INVALID_INDEX;
- std::string baseName = ParseResourceName(name, &subscript);
- for (auto it = nameSet.begin(); it != nameSet.end(); ++it)
+ std::vector<unsigned int> subscripts;
+ std::string baseName = ParseResourceName(name, &subscripts);
+ for (auto nameInSet : nameSet)
{
- size_t arrayIndex = GL_INVALID_INDEX;
- std::string arrayName = ParseResourceName(*it, &arrayIndex);
- if (baseName == arrayName && (subscript == GL_INVALID_INDEX ||
- arrayIndex == GL_INVALID_INDEX || subscript == arrayIndex))
+ std::vector<unsigned int> arrayIndices;
+ std::string arrayName = ParseResourceName(nameInSet, &arrayIndices);
+ if (baseName == arrayName &&
+ (subscripts.empty() || arrayIndices.empty() || subscripts == arrayIndices))
{
return true;
}
@@ -287,13 +292,26 @@
{
}
-VariableLocation::VariableLocation() : element(0), index(kUnused), ignored(false)
+VariableLocation::VariableLocation() : index(kUnused), flattenedArrayOffset(0u), ignored(false)
{
}
-VariableLocation::VariableLocation(unsigned int element, unsigned int index)
- : element(element), index(index), ignored(false)
+VariableLocation::VariableLocation(unsigned int arrayIndex, unsigned int index)
+ : arrayIndices(1, arrayIndex), index(index), flattenedArrayOffset(arrayIndex), ignored(false)
{
+ ASSERT(arrayIndex != GL_INVALID_INDEX);
+}
+
+bool VariableLocation::areAllArrayIndicesZero() const
+{
+ for (unsigned int arrayIndex : arrayIndices)
+ {
+ if (arrayIndex != 0)
+ {
+ return false;
+ }
+ }
+ return true;
}
void Program::Bindings::bindLocation(GLuint index, const std::string &name)
@@ -344,8 +362,8 @@
GLint ProgramState::getUniformLocation(const std::string &name) const
{
- size_t subscript = GL_INVALID_INDEX;
- std::string baseName = ParseResourceName(name, &subscript);
+ std::vector<unsigned int> subscripts;
+ std::string baseName = ParseResourceName(name, &subscripts);
for (size_t location = 0; location < mUniformLocations.size(); ++location)
{
@@ -361,15 +379,15 @@
{
if (uniform.isArray())
{
- if (uniformLocation.element == subscript ||
- (uniformLocation.element == 0 && subscript == GL_INVALID_INDEX))
+ if (uniformLocation.arrayIndices == subscripts ||
+ (uniformLocation.areAllArrayIndicesZero() && subscripts.empty()))
{
return static_cast<GLint>(location);
}
}
else
{
- if (subscript == GL_INVALID_INDEX)
+ if (subscripts.empty())
{
return static_cast<GLint>(location);
}
@@ -1209,8 +1227,10 @@
{
const VariableLocation &locationInfo = outputPair.second;
const sh::OutputVariable &outputVariable = mState.mOutputVariables[locationInfo.index];
+ ASSERT(locationInfo.arrayIndices.size() <= 1);
if (outputVariable.name == baseName &&
- (arrayIndex == GL_INVALID_INDEX || arrayIndex == locationInfo.element))
+ (arrayIndex == GL_INVALID_INDEX || (!locationInfo.arrayIndices.empty() &&
+ arrayIndex == locationInfo.arrayIndices.back())))
{
return static_cast<GLint>(outputPair.first);
}
@@ -1685,8 +1705,8 @@
GLuint Program::getUniformBlockIndex(const std::string &name) const
{
- size_t subscript = GL_INVALID_INDEX;
- std::string baseName = ParseResourceName(name, &subscript);
+ std::vector<unsigned int> subscripts;
+ std::string baseName = ParseResourceName(name, &subscripts);
unsigned int numUniformBlocks = static_cast<unsigned int>(mState.mUniformBlocks.size());
for (unsigned int blockIndex = 0; blockIndex < numUniformBlocks; blockIndex++)
@@ -1695,9 +1715,10 @@
if (uniformBlock.name == baseName)
{
const bool arrayElementZero =
- (subscript == GL_INVALID_INDEX &&
- (!uniformBlock.isArray || uniformBlock.arrayElement == 0));
- if (subscript == uniformBlock.arrayElement || arrayElementZero)
+ (subscripts.empty() && (!uniformBlock.isArray || uniformBlock.arrayElement == 0));
+ const bool arrayElementMatches =
+ (subscripts.size() == 1 && subscripts[0] == uniformBlock.arrayElement);
+ if (arrayElementMatches || arrayElementZero)
{
return blockIndex;
}
@@ -2466,8 +2487,8 @@
for (const std::string &tfVaryingName : mState.mTransformFeedbackVaryingNames)
{
bool found = false;
- size_t subscript = GL_INVALID_INDEX;
- std::string baseName = ParseResourceName(tfVaryingName, &subscript);
+ std::vector<unsigned int> subscripts;
+ std::string baseName = ParseResourceName(tfVaryingName, &subscripts);
for (const auto &ref : varyings)
{
@@ -2501,8 +2522,7 @@
// TODO(jmadill): Investigate implementation limits on D3D11
size_t elementCount =
- ((varying->isArray() && subscript == GL_INVALID_INDEX) ? varying->elementCount()
- : 1);
+ ((varying->isArray() && subscripts.empty()) ? varying->elementCount() : 1);
size_t componentCount = VariableComponentCount(varying->type) * elementCount;
if (mState.mTransformFeedbackBufferMode == GL_SEPARATE_ATTRIBS &&
componentCount > caps.maxTransformFeedbackSeparateComponents)
@@ -2577,8 +2597,13 @@
mState.mLinkedTransformFeedbackVaryings.clear();
for (const std::string &tfVaryingName : mState.mTransformFeedbackVaryingNames)
{
+ std::vector<unsigned int> subscripts;
+ std::string baseName = ParseResourceName(tfVaryingName, &subscripts);
size_t subscript = GL_INVALID_INDEX;
- std::string baseName = ParseResourceName(tfVaryingName, &subscript);
+ if (!subscripts.empty())
+ {
+ subscript = subscripts.back();
+ }
for (const auto &ref : varyings)
{
const sh::Varying *varying = ref.second.get();
@@ -2653,8 +2678,13 @@
for (const std::string &tfVarying : tfVaryings)
{
+ std::vector<unsigned int> subscripts;
+ std::string baseName = ParseResourceName(tfVarying, &subscripts);
size_t subscript = GL_INVALID_INDEX;
- std::string baseName = ParseResourceName(tfVarying, &subscript);
+ if (!subscripts.empty())
+ {
+ subscript = subscripts.back();
+ }
if (uniqueFullNames.count(tfVarying) > 0)
{
continue;
@@ -2742,8 +2772,17 @@
{
const int location = baseLocation + elementIndex;
ASSERT(mState.mOutputLocations.count(location) == 0);
- unsigned int element = outputVariable.isArray() ? elementIndex : GL_INVALID_INDEX;
- mState.mOutputLocations[location] = VariableLocation(element, outputVariableIndex);
+ if (outputVariable.isArray())
+ {
+ mState.mOutputLocations[location] =
+ VariableLocation(elementIndex, outputVariableIndex);
+ }
+ else
+ {
+ VariableLocation locationInfo;
+ locationInfo.index = outputVariableIndex;
+ mState.mOutputLocations[location] = locationInfo;
+ }
}
}
}
@@ -3034,7 +3073,7 @@
std::vector<GLuint> *boundTextureUnits =
&mState.mSamplerBindings[samplerIndex].boundTextureUnits;
- std::copy(v, v + clampedCount, boundTextureUnits->begin() + locationInfo.element);
+ std::copy(v, v + clampedCount, boundTextureUnits->begin() + locationInfo.flattenedArrayOffset);
// Invalidate the validation cache.
mCachedValidateSamplersResult.reset();
@@ -3053,7 +3092,8 @@
// OpenGL ES 3.0.4 spec pg 67: "Values for any array element that exceeds the highest array
// element index used, as reported by GetActiveUniform, will be ignored by the GL."
- unsigned int remainingElements = linkedUniform.elementCount() - locationInfo.element;
+ unsigned int remainingElements =
+ linkedUniform.elementCount() - locationInfo.flattenedArrayOffset;
GLsizei maxElementCount =
static_cast<GLsizei>(remainingElements * linkedUniform.getElementComponents());
@@ -3082,7 +3122,8 @@
// OpenGL ES 3.0.4 spec pg 67: "Values for any array element that exceeds the highest array
// element index used, as reported by GetActiveUniform, will be ignored by the GL."
- unsigned int remainingElements = linkedUniform.elementCount() - locationInfo.element;
+ unsigned int remainingElements =
+ linkedUniform.elementCount() - locationInfo.flattenedArrayOffset;
return std::min(count, static_cast<GLsizei>(remainingElements));
}
diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h
index 40c0fbb..a2a80d9 100644
--- a/src/libANGLE/Program.h
+++ b/src/libANGLE/Program.h
@@ -138,7 +138,7 @@
static constexpr unsigned int kUnused = GL_INVALID_INDEX;
VariableLocation();
- VariableLocation(unsigned int element, unsigned int index);
+ VariableLocation(unsigned int arrayIndex, unsigned int index);
// If used is false, it means this location is only used to fill an empty space in an array,
// and there is no corresponding uniform variable for this location. It can also mean the
@@ -147,9 +147,18 @@
void markUnused() { index = kUnused; }
void markIgnored() { ignored = true; }
- unsigned int element;
+ bool areAllArrayIndicesZero() const;
+
+ // The "arrayIndices" vector stores indices for the GLSL array. "index" is an index of the
+ // location.
+ std::vector<unsigned int> arrayIndices; // Outermost array indices are in the back.
unsigned int index;
+ unsigned int flattenedArrayOffset; // For non-nested arrays this is the same as the array
+ // index. For arrays of arrays, the indices are converted to
+ // a single offset inside a one-dimensional array made up of
+ // the elements of the innermost arrays.
+
// If this location was bound to an unreferenced uniform. Setting data on this uniform is a
// no-op.
bool ignored;
diff --git a/src/libANGLE/VaryingPacking.cpp b/src/libANGLE/VaryingPacking.cpp
index 152f484..36befcb 100644
--- a/src/libANGLE/VaryingPacking.cpp
+++ b/src/libANGLE/VaryingPacking.cpp
@@ -262,8 +262,7 @@
// Make sure transform feedback varyings aren't optimized out.
for (const std::string &transformFeedbackVaryingName : transformFeedbackVaryings)
{
- size_t subscript = GL_INVALID_INDEX;
- std::string tfVaryingBaseName = ParseResourceName(transformFeedbackVaryingName, &subscript);
+ std::string tfVaryingBaseName = ParseResourceName(transformFeedbackVaryingName, nullptr);
bool found = (uniqueVaryingNames.count(transformFeedbackVaryingName) > 0 ||
uniqueVaryingNames.count(tfVaryingBaseName) > 0);
diff --git a/src/libANGLE/renderer/d3d/DynamicHLSL.cpp b/src/libANGLE/renderer/d3d/DynamicHLSL.cpp
index d1bd72c..a589d89 100644
--- a/src/libANGLE/renderer/d3d/DynamicHLSL.cpp
+++ b/src/libANGLE/renderer/d3d/DynamicHLSL.cpp
@@ -1270,15 +1270,19 @@
const VariableLocation &outputLocation = outputPair.second;
const sh::ShaderVariable &outputVariable = shaderOutputVars[outputLocation.index];
const std::string &variableName = "out_" + outputVariable.name;
+
+ // Fragment outputs can't be arrays of arrays. ESSL 3.10 section 4.3.6.
+ ASSERT(outputLocation.arrayIndices.size() <= 1u);
const std::string &elementString =
- (outputLocation.element == GL_INVALID_INDEX ? "" : Str(outputLocation.element));
+ (outputLocation.arrayIndices.empty() ? ""
+ : Str(outputLocation.arrayIndices.back()));
ASSERT(outputVariable.staticUse);
PixelShaderOutputVariable outputKeyVariable;
outputKeyVariable.type = outputVariable.type;
outputKeyVariable.name = variableName + elementString;
- outputKeyVariable.source = variableName + ArrayString(outputLocation.element);
+ outputKeyVariable.source = variableName + ArrayIndexString(outputLocation.arrayIndices);
outputKeyVariable.outputIndex = outputPair.first;
outPixelShaderKey->push_back(outputKeyVariable);
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index 1a5559f..a15be2b 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -2169,11 +2169,11 @@
{
D3DUniform *targetUniform = mD3DUniforms[locationInfo.index];
const int components = targetUniform->typeInfo.componentCount;
- unsigned int arrayElement = locationInfo.element;
+ unsigned int arrayElementOffset = locationInfo.flattenedArrayOffset;
if (targetUniform->typeInfo.type == uniformType)
{
- T *dest = reinterpret_cast<T *>(targetData) + arrayElement * 4;
+ T *dest = reinterpret_cast<T *>(targetData) + arrayElementOffset * 4;
const T *source = v;
for (GLint i = 0; i < count; i++, dest += 4, source += components)
@@ -2184,7 +2184,7 @@
else
{
ASSERT(targetUniform->typeInfo.type == gl::VariableBoolVectorType(uniformType));
- GLint *boolParams = reinterpret_cast<GLint *>(targetData) + arrayElement * 4;
+ GLint *boolParams = reinterpret_cast<GLint *>(targetData) + arrayElementOffset * 4;
for (GLint i = 0; i < count; i++)
{
@@ -2209,7 +2209,7 @@
{
ASSERT(uniformType == GL_INT);
size_t size = count * sizeof(T);
- auto dest = &targetUniform->mSamplerData[locationInfo.element];
+ auto dest = &targetUniform->mSamplerData[locationInfo.flattenedArrayOffset];
if (memcmp(dest, v, size) != 0)
{
memcpy(dest, v, size);
@@ -2248,12 +2248,13 @@
D3DUniform *targetUniform = getD3DUniformFromLocation(location);
unsigned int elementCount = targetUniform->elementCount();
- unsigned int arrayElement = mState.getUniformLocations()[location].element;
- unsigned int count = std::min(elementCount - arrayElement, static_cast<unsigned int>(countIn));
+ unsigned int arrayElementOffset = mState.getUniformLocations()[location].flattenedArrayOffset;
+ unsigned int count =
+ std::min(elementCount - arrayElementOffset, static_cast<unsigned int>(countIn));
const unsigned int targetMatrixStride = (4 * rows);
- GLfloat *target = reinterpret_cast<GLfloat *>(targetData + arrayElement * sizeof(GLfloat) *
- targetMatrixStride);
+ GLfloat *target = reinterpret_cast<GLfloat *>(
+ targetData + arrayElementOffset * sizeof(GLfloat) * targetMatrixStride);
bool dirty = false;
@@ -2586,8 +2587,13 @@
}
else
{
+ std::vector<unsigned int> subscripts;
+ std::string baseName = gl::ParseResourceName(tfVaryingName, &subscripts);
size_t subscript = GL_INVALID_INDEX;
- std::string baseName = gl::ParseResourceName(tfVaryingName, &subscript);
+ if (!subscripts.empty())
+ {
+ subscript = subscripts.back();
+ }
for (const auto ®isterInfo : varyingPacking.getRegisterList())
{
const auto &varying = *registerInfo.packedVarying->varying;
@@ -2694,7 +2700,8 @@
const gl::LinkedUniform &uniform = mState.getUniforms()[locationInfo.index];
const D3DUniform *targetUniform = getD3DUniformFromLocation(location);
- const uint8_t *srcPointer = targetUniform->getDataPtrToElement(locationInfo.element);
+ const uint8_t *srcPointer = targetUniform->getDataPtrToElement(
+ locationInfo.arrayIndices.empty() ? 0u : locationInfo.flattenedArrayOffset);
if (gl::IsMatrixType(uniform.type))
{
diff --git a/src/libANGLE/renderer/gl/ProgramGL.cpp b/src/libANGLE/renderer/gl/ProgramGL.cpp
index 0b45f12..6f81fd0 100644
--- a/src/libANGLE/renderer/gl/ProgramGL.cpp
+++ b/src/libANGLE/renderer/gl/ProgramGL.cpp
@@ -659,14 +659,18 @@
continue;
}
- // From the spec:
+ // From the GLES 3.0.5 spec:
// "Locations for sequential array indices are not required to be sequential."
const gl::LinkedUniform &uniform = uniforms[entry.index];
std::stringstream fullNameStr;
fullNameStr << uniform.mappedName;
if (uniform.isArray())
{
- fullNameStr << "[" << entry.element << "]";
+ for (auto arrayElementIndexIt = entry.arrayIndices.rbegin();
+ arrayElementIndexIt != entry.arrayIndices.rend(); ++arrayElementIndexIt)
+ {
+ fullNameStr << "[" << (*arrayElementIndexIt) << "]";
+ }
}
const std::string &fullName = fullNameStr.str();
diff --git a/src/tests/angle_unittests.gypi b/src/tests/angle_unittests.gypi
index 145d8f9..bc61dc3 100644
--- a/src/tests/angle_unittests.gypi
+++ b/src/tests/angle_unittests.gypi
@@ -15,6 +15,7 @@
'angle_unittests_sources':
[
'<(angle_path)/src/common/Optional_unittest.cpp',
+ '<(angle_path)/src/common/angleutils_unittest.cpp',
'<(angle_path)/src/common/bitset_utils_unittest.cpp',
'<(angle_path)/src/common/mathutil_unittest.cpp',
'<(angle_path)/src/common/matrix_utils_unittest.cpp',