Properly validate struct uniform linking between the vertex and pixel shaders.
This also implements proper struct validation for uniform blocks.
TRAC #22932
Signed-off-by: Nicolas Capens
Signed-off-by: Geoff Lang
Author: Jamie Madill
git-svn-id: https://angleproject.googlecode.com/svn/branches/es3proto@2336 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/compiler/OutputHLSL.cpp b/src/compiler/OutputHLSL.cpp
index fd5daa9..3b6ad2d 100644
--- a/src/compiler/OutputHLSL.cpp
+++ b/src/compiler/OutputHLSL.cpp
@@ -3094,50 +3094,38 @@
return index;
}
-void OutputHLSL::declareUniform(const TType &type, const TString &name, int index)
+void OutputHLSL::declareUniformToList(const TType &type, const TString &name, int index, ActiveUniforms& output)
{
const TTypeList *structure = type.getStruct();
if (!structure)
{
- mActiveUniforms.push_back(Uniform(glVariableType(type), glVariablePrecision(type), name.c_str(), (unsigned int)type.getArraySize(), (unsigned int)index));
+ output.push_back(Uniform(glVariableType(type), glVariablePrecision(type), name.c_str(), (unsigned int)type.getArraySize(), (unsigned int)index));
}
else
{
- if (type.isArray())
+ Uniform structUniform(GL_NONE, GL_NONE, name.c_str(), (unsigned int)type.getArraySize(), (unsigned int)index);
+
+ int fieldIndex = index;
+
+ for (size_t i = 0; i < structure->size(); i++)
{
- int elementIndex = index;
+ const TType &fieldType = *(*structure)[i].type;
+ const TString &fieldName = fieldType.getFieldName();
- for (int i = 0; i < type.getArraySize(); i++)
- {
- for (size_t j = 0; j < structure->size(); j++)
- {
- const TType &fieldType = *(*structure)[j].type;
- const TString &fieldName = fieldType.getFieldName();
-
- const TString uniformName = name + "[" + str(i) + "]." + fieldName;
- declareUniform(fieldType, uniformName, elementIndex);
- elementIndex += fieldType.totalRegisterCount();
- }
- }
+ declareUniformToList(fieldType, fieldName, fieldIndex, structUniform.fields);
+ fieldIndex += fieldType.totalRegisterCount();
}
- else
- {
- int fieldIndex = index;
- for (size_t i = 0; i < structure->size(); i++)
- {
- const TType &fieldType = *(*structure)[i].type;
- const TString &fieldName = fieldType.getFieldName();
-
- const TString uniformName = name + "." + fieldName;
- declareUniform(fieldType, uniformName, fieldIndex);
- fieldIndex += fieldType.totalRegisterCount();
- }
- }
+ output.push_back(structUniform);
}
}
+void OutputHLSL::declareUniform(const TType &type, const TString &name, int index)
+{
+ declareUniformToList(type, name, index, mActiveUniforms);
+}
+
GLenum OutputHLSL::glVariableType(const TType &type)
{
if (type.getBasicType() == EbtFloat)
diff --git a/src/compiler/OutputHLSL.h b/src/compiler/OutputHLSL.h
index ddcf27b..4e6e600 100644
--- a/src/compiler/OutputHLSL.h
+++ b/src/compiler/OutputHLSL.h
@@ -168,6 +168,7 @@
TString registerString(TIntermSymbol *operand);
int samplerRegister(TIntermSymbol *sampler);
int uniformRegister(TIntermSymbol *uniform);
+ void declareUniformToList(const TType &type, const TString &name, int index, ActiveUniforms& output);
void declareUniform(const TType &type, const TString &name, int index);
static GLenum glVariableType(const TType &type);
diff --git a/src/compiler/Uniform.h b/src/compiler/Uniform.h
index 91cefb6..5b22de5 100644
--- a/src/compiler/Uniform.h
+++ b/src/compiler/Uniform.h
@@ -27,6 +27,8 @@
unsigned int arraySize;
unsigned int registerIndex;
+
+ std::vector<Uniform> fields;
};
typedef std::vector<Uniform> ActiveUniforms;
diff --git a/src/libGLESv2/ProgramBinary.cpp b/src/libGLESv2/ProgramBinary.cpp
index fa3c178..9298c89 100644
--- a/src/libGLESv2/ProgramBinary.cpp
+++ b/src/libGLESv2/ProgramBinary.cpp
@@ -1936,8 +1936,78 @@
return true;
}
+bool ProgramBinary::areMatchingUniforms(InfoLog &infoLog, const std::string &uniformName, const sh::Uniform &vertexUniform, const sh::Uniform &fragmentUniform)
+{
+ if (vertexUniform.type != fragmentUniform.type)
+ {
+ infoLog.append("Types for %s differ between vertex and fragment shaders", uniformName.c_str());
+ return false;
+ }
+ else if (vertexUniform.arraySize != fragmentUniform.arraySize)
+ {
+ infoLog.append("Array sizes for %s differ between vertex and fragment shaders", uniformName.c_str());
+ return false;
+ }
+ else if (vertexUniform.precision != fragmentUniform.precision)
+ {
+ infoLog.append("Precisions for %s differ between vertex and fragment shaders", uniformName.c_str());
+ return false;
+ }
+ else if (vertexUniform.fields.size() != fragmentUniform.fields.size())
+ {
+ infoLog.append("Structure lengths for %s differ between vertex and fragment shaders", uniformName.c_str());
+ }
+
+ const unsigned int numMembers = vertexUniform.fields.size();
+ for (unsigned int memberIndex = 0; memberIndex < numMembers; memberIndex++)
+ {
+ const sh::Uniform &vertexMember = vertexUniform.fields[memberIndex];
+ const sh::Uniform &fragmentMember = fragmentUniform.fields[memberIndex];
+
+ if (vertexMember.name != fragmentMember.name)
+ {
+ infoLog.append("Name mismatch for field %d of %s: (in vertex: '%s', in fragment: '%s')",
+ memberIndex, uniformName.c_str(), vertexMember.name.c_str(), fragmentMember.name.c_str());
+ return false;
+ }
+
+ const std::string memberName = uniformName + "." + vertexUniform.name;
+ if (!areMatchingUniforms(infoLog, memberName, vertexMember, fragmentMember))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
bool ProgramBinary::linkUniforms(InfoLog &infoLog, const sh::ActiveUniforms &vertexUniforms, const sh::ActiveUniforms &fragmentUniforms)
{
+ // Check that uniforms defined in the vertex and fragment shaders are identical
+ typedef std::map<std::string, const sh::Uniform*> UniformMap;
+ UniformMap linkedUniforms;
+
+ for (unsigned int vertexUniformIndex = 0; vertexUniformIndex < vertexUniforms.size(); vertexUniformIndex++)
+ {
+ const sh::Uniform &vertexUniform = vertexUniforms[vertexUniformIndex];
+ linkedUniforms[vertexUniform.name] = &vertexUniform;
+ }
+
+ for (unsigned int fragmentUniformIndex = 0; fragmentUniformIndex < fragmentUniforms.size(); fragmentUniformIndex++)
+ {
+ const sh::Uniform &fragmentUniform = fragmentUniforms[fragmentUniformIndex];
+ UniformMap::const_iterator entry = linkedUniforms.find(fragmentUniform.name);
+ if (entry != linkedUniforms.end())
+ {
+ const sh::Uniform &vertexUniform = *entry->second;
+ const std::string &uniformName = "uniform " + vertexUniform.name;
+ if (!areMatchingUniforms(infoLog, uniformName, vertexUniform, fragmentUniform))
+ {
+ return false;
+ }
+ }
+ }
+
for (sh::ActiveUniforms::const_iterator uniform = vertexUniforms.begin(); uniform != vertexUniforms.end(); uniform++)
{
if (!defineUniform(GL_VERTEX_SHADER, *uniform, infoLog))
@@ -1957,8 +2027,71 @@
return true;
}
+int totalRegisterCount(const sh::Uniform &uniform)
+{
+ int registerCount = 0;
+
+ if (!uniform.fields.empty())
+ {
+ for (unsigned int fieldIndex = 0; fieldIndex < uniform.fields.size(); fieldIndex++)
+ {
+ registerCount += totalRegisterCount(uniform.fields[fieldIndex]);
+ }
+ }
+ else
+ {
+ registerCount = 1;
+ }
+
+ return (uniform.arraySize > 0) ? uniform.arraySize * registerCount : registerCount;
+}
+
bool ProgramBinary::defineUniform(GLenum shader, const sh::Uniform &constant, InfoLog &infoLog)
{
+ if (!constant.fields.empty())
+ {
+ if (constant.arraySize > 0)
+ {
+ unsigned int elementRegisterIndex = constant.registerIndex;
+
+ for (unsigned int elementIndex = 0; elementIndex < constant.arraySize; elementIndex++)
+ {
+ for (size_t fieldIndex = 0; fieldIndex < constant.fields.size(); fieldIndex++)
+ {
+ const sh::Uniform &field = constant.fields[fieldIndex];
+ const std::string &uniformName = constant.name + "[" + str(elementIndex) + "]." + field.name;
+ const sh::Uniform fieldUniform(field.type, field.precision, uniformName.c_str(), field.arraySize, elementRegisterIndex);
+ if (!defineUniform(shader, fieldUniform, infoLog))
+ {
+ return false;
+ }
+ elementRegisterIndex += totalRegisterCount(field);
+ }
+ }
+ }
+ else
+ {
+ unsigned int fieldRegisterIndex = constant.registerIndex;
+
+ for (size_t fieldIndex = 0; fieldIndex < constant.fields.size(); fieldIndex++)
+ {
+ const sh::Uniform &field = constant.fields[fieldIndex];
+ const std::string &uniformName = constant.name + "." + field.name;
+
+ sh::Uniform fieldUniform(field.type, field.precision, uniformName.c_str(), field.arraySize, fieldRegisterIndex);
+ fieldUniform.fields = field.fields;
+
+ if (!defineUniform(shader, fieldUniform, infoLog))
+ {
+ return false;
+ }
+ fieldRegisterIndex += totalRegisterCount(field);
+ }
+ }
+
+ return true;
+ }
+
if (constant.type == GL_SAMPLER_2D ||
constant.type == GL_SAMPLER_CUBE)
{
@@ -2009,18 +2142,6 @@
if (location >= 0) // Previously defined, type and precision must match
{
uniform = mUniforms[mUniformIndex[location].index];
-
- if (uniform->type != constant.type)
- {
- infoLog.append("Types for uniform %s do not match between the vertex and fragment shader", uniform->name.c_str());
- return false;
- }
-
- if (uniform->precision != constant.precision)
- {
- infoLog.append("Precisions for uniform %s do not match between the vertex and fragment shader", uniform->name.c_str());
- return false;
- }
}
else
{
diff --git a/src/libGLESv2/ProgramBinary.h b/src/libGLESv2/ProgramBinary.h
index 328ffe2..02782f9 100644
--- a/src/libGLESv2/ProgramBinary.h
+++ b/src/libGLESv2/ProgramBinary.h
@@ -150,6 +150,7 @@
bool linkAttributes(InfoLog &infoLog, const AttributeBindings &attributeBindings, FragmentShader *fragmentShader, VertexShader *vertexShader);
+ bool areMatchingUniforms(InfoLog &infoLog, const std::string& uniformName, const sh::Uniform &vertexUniform, const sh::Uniform &fragmentUniform);
bool linkUniforms(InfoLog &infoLog, const sh::ActiveUniforms &vertexUniforms, const sh::ActiveUniforms &fragmentUniforms);
bool defineUniform(GLenum shader, const sh::Uniform &constant, InfoLog &infoLog);