Commit 6ca2b65c by Olli Etuaho Committed by Commit Bot

Implement location layout qualifier for uniforms

This is a complete implementation of the uniform location layout qualifier. Uniform location set in the shader is plumbed to shader linking, which does several link-time checks for conflicts and recursively applies the location to struct members. Validate that location is consistent as specified in the table in section 9.2.1 of the ESSL 3.10.4 spec. The location set in the shader overrides the one set via the CHROMIUM_bind_uniform_location API. Location conflicts must be checked even if the uniforms are not statically used. Because of this unused uniforms are now recorded during uniform linking. After linking checks are done, unused uniforms are pruned from the program state. Location is validated against the maximum number of uniform locations at compile time as specified in section 4.4.3 of the ESSL 3.10.4 spec. All dEQP uniform location tests don't yet pass due to unrelated bugs. BUG=angleproject:1442 TEST=angle_end2end_tests, dEQP-GLES31.functional.uniform_location.* Change-Id: I1f968e971f521fbc804b01e1a7c2b4d14f24d20f Reviewed-on: https://chromium-review.googlesource.com/447942Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent 0ba963ef
......@@ -70,6 +70,14 @@ Issues
If you need to set individual elements of a uniform array you must query the
location of the each element you wish to set.
If a uniform has its location explicitly set within the shader text and a
different location set via the API, the assignment in the shader text is
used.
If the location of a statically used uniform set via the API conflicts with
the location of a different uniform set in the shader text, linking must
fail.
New Tokens
None
......@@ -129,3 +137,5 @@ Revision History
are bound to the same location.
11/6/2015 Require inactive and non-existing, bound uniform locations
to behave like location -1.
3/9/2017 Locations set in the shader override ones set by the binding
API.
......@@ -25,7 +25,7 @@
// Version number for shader translation API.
// It is incremented every time the API changes.
#define ANGLE_SH_VERSION 172
#define ANGLE_SH_VERSION 173
enum ShShaderSpec
{
......@@ -329,6 +329,9 @@ struct ShBuiltInResources
// maximum total number of image uniforms in a program
int MaxCombinedImageUniforms;
// maximum number of uniform locations
int MaxUniformLocations;
// maximum number of ssbos and images in a shader
int MaxCombinedShaderOutputResources;
......
......@@ -94,7 +94,21 @@ struct ShaderVariable
}
};
struct Uniform : public ShaderVariable
// A variable with an integer location to pass back to the GL API: either uniform (can have location
// in GLES3.1+), vertex shader input or fragment shader output.
struct VariableWithLocation : public ShaderVariable
{
VariableWithLocation();
~VariableWithLocation();
VariableWithLocation(const VariableWithLocation &other);
VariableWithLocation &operator=(const VariableWithLocation &other);
bool operator==(const VariableWithLocation &other) const;
bool operator!=(const VariableWithLocation &other) const { return !operator==(other); }
int location;
};
struct Uniform : public VariableWithLocation
{
Uniform();
~Uniform();
......@@ -115,22 +129,7 @@ struct Uniform : public ShaderVariable
bool isSameUniformAtLinkTime(const Uniform &other) const;
};
// An interface variable is a variable which passes data between the GL data structures and the
// shader execution: either vertex shader inputs or fragment shader outputs. These variables can
// have integer locations to pass back to the GL API.
struct InterfaceVariable : public ShaderVariable
{
InterfaceVariable();
~InterfaceVariable();
InterfaceVariable(const InterfaceVariable &other);
InterfaceVariable &operator=(const InterfaceVariable &other);
bool operator==(const InterfaceVariable &other) const;
bool operator!=(const InterfaceVariable &other) const { return !operator==(other); }
int location;
};
struct Attribute : public InterfaceVariable
struct Attribute : public VariableWithLocation
{
Attribute();
~Attribute();
......@@ -140,7 +139,7 @@ struct Attribute : public InterfaceVariable
bool operator!=(const Attribute &other) const { return !operator==(other); }
};
struct OutputVariable : public InterfaceVariable
struct OutputVariable : public VariableWithLocation
{
OutputVariable();
~OutputVariable();
......
......@@ -130,6 +130,7 @@ TParseContext::TParseContext(TSymbolTable &symt,
mMaxNumViews(resources.MaxViewsOVR),
mMaxImageUnits(resources.MaxImageUnits),
mMaxCombinedTextureImageUnits(resources.MaxCombinedTextureImageUnits),
mMaxUniformLocations(resources.MaxUniformLocations),
mDeclaringFunction(false)
{
mComputeShaderLocalSize.fill(-1);
......@@ -845,8 +846,13 @@ void TParseContext::checkLocationIsNotSpecified(const TSourceLoc &location,
{
if (layoutQualifier.location != -1)
{
error(location, "invalid layout qualifier: only valid on program inputs and outputs",
"location");
const char *errorMsg = "invalid layout qualifier: only valid on program inputs and outputs";
if (mShaderVersion >= 310)
{
errorMsg =
"invalid layout qualifier: only valid on program inputs, outputs, and uniforms";
}
error(location, errorMsg, "location");
}
}
......@@ -1199,7 +1205,23 @@ void TParseContext::singleDeclarationErrorCheck(const TPublicType &publicType,
return;
}
if (publicType.qualifier != EvqVertexIn && publicType.qualifier != EvqFragmentOut)
bool canHaveLocation =
publicType.qualifier == EvqVertexIn || publicType.qualifier == EvqFragmentOut;
if (mShaderVersion >= 310 && publicType.qualifier == EvqUniform)
{
canHaveLocation = true;
// Valid uniform declarations can't be unsized arrays since uniforms can't be initialized.
// But invalid shaders may still reach here with an unsized array declaration.
if (!publicType.isUnsizedArray())
{
TType type(publicType);
checkUniformLocationInRange(identifierLocation, type.getLocationCount(),
publicType.layoutQualifier);
}
}
if (!canHaveLocation)
{
checkLocationIsNotSpecified(identifierLocation, publicType.layoutQualifier);
}
......@@ -1369,6 +1391,17 @@ void TParseContext::checkSamplerBindingIsValid(const TSourceLoc &location,
}
}
void TParseContext::checkUniformLocationInRange(const TSourceLoc &location,
int objectLocationCount,
const TLayoutQualifier &layoutQualifier)
{
int loc = layoutQualifier.location;
if (loc >= 0 && loc + objectLocationCount > mMaxUniformLocations)
{
error(location, "Uniform location out of range", "location");
}
}
void TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate,
TIntermAggregate *fnCall)
{
......
......@@ -395,6 +395,10 @@ class TParseContext : angle::NonCopyable
void checkImageBindingIsValid(const TSourceLoc &location, int binding, int arraySize);
void checkSamplerBindingIsValid(const TSourceLoc &location, int binding, int arraySize);
void checkUniformLocationInRange(const TSourceLoc &location,
int objectLocationCount,
const TLayoutQualifier &layoutQualifier);
TIntermTyped *addBinaryMathInternal(TOperator op,
TIntermTyped *left,
TIntermTyped *right,
......@@ -469,6 +473,7 @@ class TParseContext : angle::NonCopyable
int mMaxNumViews;
int mMaxImageUnits;
int mMaxCombinedTextureImageUnits;
int mMaxUniformLocations;
// keeps track whether we are declaring / defining a function
bool mDeclaringFunction;
};
......
......@@ -196,6 +196,8 @@ void InitBuiltInResources(ShBuiltInResources *resources)
resources->MaxComputeImageUniforms = 4;
resources->MaxCombinedImageUniforms = 4;
resources->MaxUniformLocations = 1024;
resources->MaxCombinedShaderOutputResources = 4;
resources->MaxComputeWorkGroupCount[0] = 65535;
......
......@@ -189,20 +189,20 @@ Uniform::~Uniform()
{
}
Uniform::Uniform(const Uniform &other) : ShaderVariable(other), binding(other.binding)
Uniform::Uniform(const Uniform &other) : VariableWithLocation(other), binding(other.binding)
{
}
Uniform &Uniform::operator=(const Uniform &other)
{
ShaderVariable::operator=(other);
VariableWithLocation::operator=(other);
binding = other.binding;
return *this;
}
bool Uniform::operator==(const Uniform &other) const
{
return ShaderVariable::operator==(other) && binding == other.binding;
return VariableWithLocation::operator==(other) && binding == other.binding;
}
bool Uniform::isSameUniformAtLinkTime(const Uniform &other) const
......@@ -211,30 +211,34 @@ bool Uniform::isSameUniformAtLinkTime(const Uniform &other) const
{
return false;
}
return ShaderVariable::isSameVariableAtLinkTime(other, true);
if (location != -1 && other.location != -1 && location != other.location)
{
return false;
}
return VariableWithLocation::isSameVariableAtLinkTime(other, true);
}
InterfaceVariable::InterfaceVariable() : location(-1)
VariableWithLocation::VariableWithLocation() : location(-1)
{
}
InterfaceVariable::~InterfaceVariable()
VariableWithLocation::~VariableWithLocation()
{
}
InterfaceVariable::InterfaceVariable(const InterfaceVariable &other)
VariableWithLocation::VariableWithLocation(const VariableWithLocation &other)
: ShaderVariable(other), location(other.location)
{
}
InterfaceVariable &InterfaceVariable::operator=(const InterfaceVariable &other)
VariableWithLocation &VariableWithLocation::operator=(const VariableWithLocation &other)
{
ShaderVariable::operator=(other);
location = other.location;
return *this;
}
bool InterfaceVariable::operator==(const InterfaceVariable &other) const
bool VariableWithLocation::operator==(const VariableWithLocation &other) const
{
return (ShaderVariable::operator==(other) && location == other.location);
}
......@@ -247,19 +251,19 @@ Attribute::~Attribute()
{
}
Attribute::Attribute(const Attribute &other) : InterfaceVariable(other)
Attribute::Attribute(const Attribute &other) : VariableWithLocation(other)
{
}
Attribute &Attribute::operator=(const Attribute &other)
{
InterfaceVariable::operator=(other);
VariableWithLocation::operator=(other);
return *this;
}
bool Attribute::operator==(const Attribute &other) const
{
return InterfaceVariable::operator==(other);
return VariableWithLocation::operator==(other);
}
OutputVariable::OutputVariable()
......@@ -270,19 +274,19 @@ OutputVariable::~OutputVariable()
{
}
OutputVariable::OutputVariable(const OutputVariable &other) : InterfaceVariable(other)
OutputVariable::OutputVariable(const OutputVariable &other) : VariableWithLocation(other)
{
}
OutputVariable &OutputVariable::operator=(const OutputVariable &other)
{
InterfaceVariable::operator=(other);
VariableWithLocation::operator=(other);
return *this;
}
bool OutputVariable::operator==(const OutputVariable &other) const
{
return InterfaceVariable::operator==(other);
return VariableWithLocation::operator==(other);
}
InterfaceBlockField::InterfaceBlockField() : isRowMajorLayout(false)
......
......@@ -445,6 +445,36 @@ size_t TType::getObjectSize() const
return totalSize;
}
int TType::getLocationCount() const
{
int count = 1;
if (getBasicType() == EbtStruct)
{
count = structure->getLocationCount();
}
if (isArray())
{
if (count == 0)
{
return 0;
}
unsigned int currentArraySize = getArraySize();
if (currentArraySize > static_cast<unsigned int>(std::numeric_limits<int>::max() / count))
{
count = std::numeric_limits<int>::max();
}
else
{
count *= static_cast<int>(currentArraySize);
}
}
return count;
}
TStructure::TStructure(const TString *name, TFieldList *fields)
: TFieldListCollection(name, fields),
mDeepestNesting(0),
......@@ -583,9 +613,9 @@ TString TFieldListCollection::buildMangledName(const TString &mangledNamePrefix)
size_t TFieldListCollection::calculateObjectSize() const
{
size_t size = 0;
for (size_t i = 0; i < mFields->size(); ++i)
for (const TField *field : *mFields)
{
size_t fieldSize = (*mFields)[i]->type()->getObjectSize();
size_t fieldSize = field->type()->getObjectSize();
if (fieldSize > INT_MAX - size)
size = INT_MAX;
else
......@@ -594,6 +624,24 @@ size_t TFieldListCollection::calculateObjectSize() const
return size;
}
int TFieldListCollection::getLocationCount() const
{
int count = 0;
for (const TField *field : *mFields)
{
int fieldCount = field->type()->getLocationCount();
if (fieldCount > std::numeric_limits<int>::max() - count)
{
count = std::numeric_limits<int>::max();
}
else
{
count += fieldCount;
}
}
return count;
}
int TStructure::calculateDeepestNesting() const
{
int maxNesting = 0;
......
......@@ -62,7 +62,10 @@ class TFieldListCollection : angle::NonCopyable
if (mObjectSize == 0)
mObjectSize = calculateObjectSize();
return mObjectSize;
};
}
// How many locations the field list consumes as a uniform.
int getLocationCount() const;
protected:
TFieldListCollection(const TString *name, TFieldList *fields)
......@@ -332,6 +335,9 @@ class TType
// Full size of single instance of type
size_t getObjectSize() const;
// Get how many locations this type consumes as a uniform.
int getLocationCount() const;
bool isMatrix() const { return primarySize > 1 && secondarySize > 1; }
bool isNonSquareMatrix() const { return isMatrix() && primarySize != secondarySize; }
bool isArray() const { return array; }
......
......@@ -570,6 +570,7 @@ Uniform CollectVariables::recordUniform(const TIntermSymbol &variable) const
Uniform uniform;
setCommonVariableProperties(variable.getType(), variable.getSymbol(), &uniform);
uniform.binding = variable.getType().getLayoutQualifier().binding;
uniform.location = variable.getType().getLayoutQualifier().location;
return uniform;
}
......
......@@ -82,13 +82,14 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const ContextState &state)
mResources.MinProgramTexelOffset = caps.minProgramTexelOffset;
mResources.MaxProgramTexelOffset = caps.maxProgramTexelOffset;
// GLSL ES 3.1 compute shader constants
// GLSL ES 3.1 constants
mResources.MaxImageUnits = caps.maxImageUnits;
mResources.MaxVertexImageUniforms = caps.maxVertexImageUniforms;
mResources.MaxFragmentImageUniforms = caps.maxFragmentImageUniforms;
mResources.MaxComputeImageUniforms = caps.maxComputeImageUniforms;
mResources.MaxCombinedImageUniforms = caps.maxCombinedImageUniforms;
mResources.MaxCombinedShaderOutputResources = caps.maxCombinedShaderOutputResources;
mResources.MaxUniformLocations = caps.maxUniformLocations;
for (size_t index = 0u; index < 3u; ++index)
{
......
......@@ -211,7 +211,6 @@ class ProgramState final : angle::NonCopyable
const std::vector<UniformBlock> &getUniformBlocks() const { return mUniformBlocks; }
const std::vector<SamplerBinding> &getSamplerBindings() const { return mSamplerBindings; }
const LinkedUniform *getUniformByName(const std::string &name) const;
GLint getUniformLocation(const std::string &name) const;
GLuint getUniformIndexFromName(const std::string &name) const;
GLuint getUniformIndexFromLocation(GLint location) const;
......@@ -446,9 +445,19 @@ class Program final : angle::NonCopyable, public LabeledObject
InfoLog &infoLog) const;
bool linkUniformBlocks(InfoLog &infoLog, const Caps &caps);
bool linkVaryings(InfoLog &infoLog) const;
bool validateVertexAndFragmentUniforms(InfoLog &infoLog) const;
bool linkUniforms(InfoLog &infoLog, const Caps &caps, const Bindings &uniformLocationBindings);
bool indexUniforms(InfoLog &infoLog, const Caps &caps, const Bindings &uniformLocationBindings);
bool validateVertexAndFragmentUniforms(InfoLog &infoLog) const;
bool indexUniforms(InfoLog &infoLog, const Bindings &uniformLocationBindings);
bool gatherUniformLocationsAndCheckConflicts(InfoLog &infoLog,
const Bindings &uniformLocationBindings,
std::set<GLuint> *reservedLocations,
std::set<GLuint> *ignoredLocations,
int *maxUniformLocation);
void pruneUnusedUniforms();
void updateSamplerBindings();
bool areMatchingInterfaceBlocks(InfoLog &infoLog,
const sh::InterfaceBlock &vertexInterfaceBlock,
const sh::InterfaceBlock &fragmentInterfaceBlock) const;
......@@ -501,10 +510,17 @@ class Program final : angle::NonCopyable, public LabeledObject
unsigned int samplerCount;
};
VectorAndSamplerCount flattenUniform(const sh::ShaderVariable &uniform,
const std::string &fullName,
VectorAndSamplerCount flattenUniform(const sh::Uniform &uniform,
std::vector<LinkedUniform> *samplerUniforms);
// staticUse is given as a separate parameter because it is tracked here at struct granularity.
VectorAndSamplerCount flattenUniformImpl(const sh::ShaderVariable &uniform,
const std::string &fullName,
std::vector<LinkedUniform> *samplerUniforms,
bool markStaticUse,
int binding,
int *location);
void gatherInterfaceBlockInfo();
template <typename VarT>
void defineUniformBlockMembers(const std::vector<VarT> &fields,
......
......@@ -22,6 +22,8 @@ LinkedUniform::LinkedUniform(GLenum typeIn,
GLenum precisionIn,
const std::string &nameIn,
unsigned int arraySizeIn,
const int bindingIn,
const int locationIn,
const int blockIndexIn,
const sh::BlockMemberInfo &blockInfoIn)
: blockIndex(blockIndexIn), blockInfo(blockInfoIn)
......@@ -30,6 +32,8 @@ LinkedUniform::LinkedUniform(GLenum typeIn,
precision = precisionIn;
name = nameIn;
arraySize = arraySizeIn;
binding = bindingIn;
location = locationIn;
}
LinkedUniform::LinkedUniform(const sh::Uniform &uniform)
......
......@@ -23,7 +23,14 @@ namespace gl
struct LinkedUniform : public sh::Uniform
{
LinkedUniform();
LinkedUniform(GLenum type, GLenum precision, const std::string &name, unsigned int arraySize, const int blockIndex, const sh::BlockMemberInfo &blockInfo);
LinkedUniform(GLenum type,
GLenum precision,
const std::string &name,
unsigned int arraySize,
const int binding,
const int location,
const int blockIndex,
const sh::BlockMemberInfo &blockInfo);
LinkedUniform(const sh::Uniform &uniform);
LinkedUniform(const LinkedUniform &uniform);
LinkedUniform &operator=(const LinkedUniform &uniform);
......
......@@ -1260,6 +1260,9 @@ void GenerateCaps(ID3D11Device *device, ID3D11DeviceContext *deviceContext, cons
caps->maxUniformBufferBindings = caps->maxVertexUniformBlocks + caps->maxFragmentUniformBlocks;
caps->maxUniformBlockSize = GetMaximumConstantBufferSize(featureLevel);
// TODO(oetuaho): Get a more accurate limit. For now using the minimum requirement for GLES 3.1.
caps->maxUniformLocations = 1024;
// With DirectX 11.1, constant buffer offset and size must be a multiple of 16 constants of 16 bytes each.
// https://msdn.microsoft.com/en-us/library/windows/desktop/hh404649%28v=vs.85%29.aspx
// With DirectX 11.0, we emulate UBO offsets using copies of ranges of the UBO however
......
......@@ -1373,13 +1373,9 @@
1442 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.query.geometry_shader_invocations = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.vertex_attribute_binding.* = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.separate_shader.* = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.uniform_location.basic.* = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.uniform_location.array.* = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.uniform_location.nested_array.* = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.uniform_location.struct.* = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.uniform_location.nested_struct.* = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.uniform_location.min_max.* = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.uniform_location.link.* = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.debug.error_filters.case_11 = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.debug.error_groups.case_11 = FAIL
1442 OPENGL D3D11 : dEQP-GLES31.functional.debug.negative_coverage.callbacks.buffer.bind_buffer_range = FAIL
......
......@@ -472,6 +472,222 @@ TEST_P(BindUniformLocationTest, UseSamplerWhenUnusedUniforms)
EXPECT_GL_NO_ERROR();
}
// Test for binding a statically used uniform to the same location as a non-statically used uniform.
// This is valid according to the extension spec.
TEST_P(BindUniformLocationTest, SameLocationForUsedAndUnusedUniform)
{
if (!extensionEnabled("GL_CHROMIUM_bind_uniform_location"))
{
std::cout << "Test skipped because GL_CHROMIUM_bind_uniform_location is not available."
<< std::endl;
return;
}
ASSERT_NE(nullptr, mBindUniformLocation);
// clang-format off
const std::string vsSource = SHADER_SOURCE
(
void main()
{
gl_Position = vec4(0);
}
);
const std::string fsSource = SHADER_SOURCE
(
precision mediump float;
uniform vec4 a;
uniform vec4 b;
void main()
{
gl_FragColor = a;
}
);
// clang-format on
const GLuint location = 54;
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
mProgram = glCreateProgram();
mBindUniformLocation(mProgram, location, "a");
mBindUniformLocation(mProgram, location, "b");
glAttachShader(mProgram, vs);
glDeleteShader(vs);
glAttachShader(mProgram, fs);
glDeleteShader(fs);
glLinkProgram(mProgram);
GLint linked = GL_FALSE;
glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
ASSERT_GL_TRUE(linked);
glUseProgram(mProgram);
glUniform4f(location, 0.0, 1.0, 0.0, 1.0);
EXPECT_GL_NO_ERROR();
}
class BindUniformLocationES31Test : public BindUniformLocationTest
{
protected:
BindUniformLocationES31Test() : BindUniformLocationTest() {}
void linkProgramWithUniformLocation(GLuint vs,
GLuint fs,
const char *uniformName,
GLint uniformLocation)
{
mProgram = glCreateProgram();
mBindUniformLocation(mProgram, uniformLocation, uniformName);
glAttachShader(mProgram, vs);
glDeleteShader(vs);
glAttachShader(mProgram, fs);
glDeleteShader(fs);
glLinkProgram(mProgram);
}
};
// Test for when the shader specifies an explicit uniform location with a layout qualifier and the
// bindUniformLocation API sets a consistent location.
TEST_P(BindUniformLocationES31Test, ConsistentWithLocationLayoutQualifier)
{
if (!extensionEnabled("GL_CHROMIUM_bind_uniform_location"))
{
std::cout << "Test skipped because GL_CHROMIUM_bind_uniform_location is not available."
<< std::endl;
return;
}
const std::string vsSource =
"#version 310 es\n"
"void main()\n"
"{\n"
" gl_Position = vec4(0);\n"
"}\n";
const std::string fsSource =
"#version 310 es\n"
"uniform layout(location=2) highp sampler2D tex;\n"
"out highp vec4 my_FragColor;\n"
"void main()\n"
"{\n"
" my_FragColor = texture(tex, vec2(1));\n"
"}\n";
const GLuint texLocation = 2;
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
EXPECT_NE(0u, vs);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
EXPECT_NE(0u, fs);
linkProgramWithUniformLocation(vs, fs, "tex", texLocation);
GLint linked = GL_FALSE;
glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
ASSERT_GL_TRUE(linked);
EXPECT_EQ(static_cast<GLint>(texLocation), glGetUniformLocation(mProgram, "tex"));
glUseProgram(mProgram);
glUniform1i(texLocation, 0);
EXPECT_GL_NO_ERROR();
}
// Test for when the shader specifies an explicit uniform location with a layout qualifier and the
// bindUniformLocation API sets a conflicting location for the same variable. The shader-set
// location should prevail.
TEST_P(BindUniformLocationES31Test, LocationLayoutQualifierOverridesAPIBinding)
{
if (!extensionEnabled("GL_CHROMIUM_bind_uniform_location"))
{
std::cout << "Test skipped because GL_CHROMIUM_bind_uniform_location is not available."
<< std::endl;
return;
}
const std::string vsSource =
"#version 310 es\n"
"void main()\n"
"{\n"
" gl_Position = vec4(0);\n"
"}\n";
const std::string fsSource =
"#version 310 es\n"
"uniform layout(location=2) highp sampler2D tex;\n"
"out highp vec4 my_FragColor;\n"
"void main()\n"
"{\n"
" my_FragColor = texture(tex, vec2(1));\n"
"}\n";
const GLuint shaderTexLocation = 2;
const GLuint texLocation = 3;
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
EXPECT_NE(0u, vs);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
EXPECT_NE(0u, fs);
linkProgramWithUniformLocation(vs, fs, "tex", texLocation);
GLint linked = GL_FALSE;
glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
ASSERT_GL_TRUE(linked);
EXPECT_EQ(static_cast<GLint>(shaderTexLocation), glGetUniformLocation(mProgram, "tex"));
glUseProgram(mProgram);
glUniform1i(shaderTexLocation, 1);
EXPECT_GL_NO_ERROR();
glUniform1i(texLocation, 2);
EXPECT_GL_NO_ERROR();
}
// Test for when the shader specifies an explicit uniform location with a layout qualifier and the
// bindUniformLocation API sets a conflicting location for a different variable. Linking should
// fail.
TEST_P(BindUniformLocationES31Test, LocationLayoutQualifierConflictsWithAPIBinding)
{
if (!extensionEnabled("GL_CHROMIUM_bind_uniform_location"))
{
std::cout << "Test skipped because GL_CHROMIUM_bind_uniform_location is not available."
<< std::endl;
return;
}
const std::string vsSource =
"#version 310 es\n"
"void main()\n"
"{\n"
" gl_Position = vec4(0);\n"
"}\n";
const std::string fsSource =
"#version 310 es\n"
"uniform layout(location=2) highp sampler2D tex;\n"
"uniform highp sampler2D tex2;\n"
"out highp vec4 my_FragColor;\n"
"void main()\n"
"{\n"
" my_FragColor = texture(tex2, vec2(1));\n"
"}\n";
const GLuint tex2Location = 2;
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
EXPECT_NE(0u, vs);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
EXPECT_NE(0u, fs);
linkProgramWithUniformLocation(vs, fs, "tex2", tex2Location);
GLint linked = GL_FALSE;
glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
ASSERT_GL_FALSE(linked);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST(BindUniformLocationTest,
......@@ -481,4 +697,6 @@ ANGLE_INSTANTIATE_TEST(BindUniformLocationTest,
ES2_OPENGL(),
ES2_OPENGLES());
ANGLE_INSTANTIATE_TEST(BindUniformLocationES31Test, ES31_D3D11(), ES31_OPENGL(), ES31_OPENGLES())
} // namespace
......@@ -752,6 +752,136 @@ TEST_P(UniformTestES3, ReturnsOnlyOneArrayElement)
}
}
class UniformTestES31 : public ANGLETest
{
protected:
UniformTestES31() : mProgram(0) {}
void SetUp() override { ANGLETest::SetUp(); }
void TearDown() override
{
if (mProgram != 0)
{
glDeleteProgram(mProgram);
mProgram = 0;
}
}
GLuint mProgram;
};
// Test that uniform locations get set correctly for structure members.
// ESSL 3.10.4 section 4.4.3.
TEST_P(UniformTestES31, StructLocationLayoutQualifier)
{
const std::string &vertShader =
"#version 310 es\n"
"void main()\n"
"{\n"
" gl_Position = vec4(0);\n"
"}";
const std::string &fragShader =
"#version 310 es\n"
"out highp vec4 my_FragColor;\n"
"struct S\n"
"{\n"
" highp float f;\n"
" highp float f2;\n"
"};\n"
"uniform layout(location=12) S uS;\n"
"void main()\n"
"{\n"
" my_FragColor = vec4(uS.f, uS.f2, 0, 1);\n"
"}";
ANGLE_GL_PROGRAM(program, vertShader, fragShader);
EXPECT_EQ(12, glGetUniformLocation(program.get(), "uS.f"));
EXPECT_EQ(13, glGetUniformLocation(program.get(), "uS.f2"));
}
// Set uniform location with a layout qualifier in the fragment shader. The same uniform exists in
// the vertex shader, but doesn't have a location specified there.
TEST_P(UniformTestES31, UniformLocationInFragmentShader)
{
const std::string &vertShader =
"#version 310 es\n"
"uniform highp sampler2D tex2D;\n"
"void main()\n"
"{\n"
" gl_Position = texture(tex2D, vec2(0));\n"
"}";
const std::string &fragShader =
"#version 310 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"uniform layout(location=12) highp sampler2D tex2D;\n"
"void main()\n"
"{\n"
" my_FragColor = texture(tex2D, vec2(0));\n"
"}";
ANGLE_GL_PROGRAM(program, vertShader, fragShader);
EXPECT_EQ(12, glGetUniformLocation(program.get(), "tex2D"));
}
// Test two unused uniforms that have the same location.
// ESSL 3.10.4 section 4.4.3: "No two default-block uniform variables in the program can have the
// same location, even if they are unused, otherwise a compiler or linker error will be generated."
TEST_P(UniformTestES31, UnusedUniformsConflictingLocation)
{
const std::string &vertShader =
"#version 310 es\n"
"uniform layout(location=12) highp sampler2D texA;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(0);\n"
"}";
const std::string &fragShader =
"#version 310 es\n"
"out highp vec4 my_FragColor;\n"
"uniform layout(location=12) highp sampler2D texB;\n"
"void main()\n"
"{\n"
" my_FragColor = vec4(0);\n"
"}";
mProgram = CompileProgram(vertShader, fragShader);
EXPECT_EQ(0u, mProgram);
}
// Test two unused uniforms that have overlapping locations once all array elements are taken into
// account.
// ESSL 3.10.4 section 4.4.3: "No two default-block uniform variables in the program can have the
// same location, even if they are unused, otherwise a compiler or linker error will be generated."
TEST_P(UniformTestES31, UnusedUniformArraysConflictingLocation)
{
const std::string &vertShader =
"#version 310 es\n"
"uniform layout(location=11) highp vec4 uA[2];\n"
"void main()\n"
"{\n"
" gl_Position = vec4(0);\n"
"}";
const std::string &fragShader =
"#version 310 es\n"
"out highp vec4 my_FragColor;\n"
"uniform layout(location=12) highp vec4 uB;\n"
"void main()\n"
"{\n"
" my_FragColor = vec4(0);\n"
"}";
mProgram = CompileProgram(vertShader, fragShader);
EXPECT_EQ(0u, mProgram);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(UniformTest,
ES2_D3D9(),
......@@ -760,5 +890,6 @@ ANGLE_INSTANTIATE_TEST(UniformTest,
ES2_OPENGL(),
ES2_OPENGLES());
ANGLE_INSTANTIATE_TEST(UniformTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(UniformTestES31, ES31_D3D11(), ES31_OPENGL(), ES31_OPENGLES());
} // namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment