зеркало из https://github.com/AvaloniaUI/angle.git
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/447942 Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Geoff Lang <geofflang@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
This commit is contained in:
Родитель
0ba963ef27
Коммит
6ca2b65c43
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -125,15 +125,15 @@ void UniformStateQueryCastLoop(DestT *dataOut, const uint8_t *srcPointer, int co
|
|||
}
|
||||
}
|
||||
|
||||
bool UniformInList(const std::vector<LinkedUniform> &list, const std::string &name)
|
||||
LinkedUniform *FindUniform(std::vector<LinkedUniform> &list, const std::string &name)
|
||||
{
|
||||
for (const LinkedUniform &uniform : list)
|
||||
for (LinkedUniform &uniform : list)
|
||||
{
|
||||
if (uniform.name == name)
|
||||
return true;
|
||||
return &uniform;
|
||||
}
|
||||
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// true if varying x has a higher priority in packing than y
|
||||
|
@ -262,19 +262,6 @@ const std::string &ProgramState::getLabel()
|
|||
return mLabel;
|
||||
}
|
||||
|
||||
const LinkedUniform *ProgramState::getUniformByName(const std::string &name) const
|
||||
{
|
||||
for (const LinkedUniform &linkedUniform : mUniforms)
|
||||
{
|
||||
if (linkedUniform.name == name)
|
||||
{
|
||||
return &linkedUniform;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GLint ProgramState::getUniformLocation(const std::string &name) const
|
||||
{
|
||||
size_t subscript = GL_INVALID_INDEX;
|
||||
|
@ -1945,9 +1932,9 @@ bool Program::validateVertexAndFragmentUniforms(InfoLog &infoLog) const
|
|||
auto entry = linkedUniforms.find(fragmentUniform.name);
|
||||
if (entry != linkedUniforms.end())
|
||||
{
|
||||
LinkedUniform *vertexUniform = &entry->second;
|
||||
const std::string &uniformName = "uniform '" + vertexUniform->name + "'";
|
||||
if (!linkValidateUniforms(infoLog, uniformName, *vertexUniform, fragmentUniform))
|
||||
LinkedUniform *linkedUniform = &entry->second;
|
||||
const std::string &uniformName = "uniform '" + linkedUniform->name + "'";
|
||||
if (!linkValidateUniforms(infoLog, uniformName, *linkedUniform, fragmentUniform))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1976,23 +1963,122 @@ bool Program::linkUniforms(InfoLog &infoLog,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!indexUniforms(infoLog, caps, uniformLocationBindings))
|
||||
if (!indexUniforms(infoLog, uniformLocationBindings))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
updateSamplerBindings();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Program::gatherUniformLocationsAndCheckConflicts(InfoLog &infoLog,
|
||||
const Bindings &uniformLocationBindings,
|
||||
std::set<GLuint> *reservedLocations,
|
||||
std::set<GLuint> *ignoredLocations,
|
||||
int *maxUniformLocation)
|
||||
{
|
||||
for (const LinkedUniform &uniform : mState.mUniforms)
|
||||
{
|
||||
if (uniform.isBuiltIn())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int apiBoundLocation = uniformLocationBindings.getBinding(uniform.name);
|
||||
int shaderLocation = uniform.location;
|
||||
|
||||
if (shaderLocation != -1)
|
||||
{
|
||||
for (unsigned int arrayIndex = 0; arrayIndex < uniform.elementCount(); arrayIndex++)
|
||||
{
|
||||
// GLSL ES 3.10 section 4.4.3
|
||||
int elementLocation = shaderLocation + arrayIndex;
|
||||
*maxUniformLocation = std::max(*maxUniformLocation, elementLocation);
|
||||
if (reservedLocations->find(elementLocation) != reservedLocations->end())
|
||||
{
|
||||
infoLog << "Multiple uniforms bound to location " << elementLocation << ".";
|
||||
return false;
|
||||
}
|
||||
reservedLocations->insert(elementLocation);
|
||||
if (!uniform.staticUse)
|
||||
{
|
||||
ignoredLocations->insert(elementLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (apiBoundLocation != -1 && uniform.staticUse)
|
||||
{
|
||||
// Only the first location is reserved even if the uniform is an array.
|
||||
*maxUniformLocation = std::max(*maxUniformLocation, apiBoundLocation);
|
||||
if (reservedLocations->find(apiBoundLocation) != reservedLocations->end())
|
||||
{
|
||||
infoLog << "Multiple uniforms bound to location " << apiBoundLocation << ".";
|
||||
return false;
|
||||
}
|
||||
reservedLocations->insert(apiBoundLocation);
|
||||
}
|
||||
}
|
||||
|
||||
// Record the uniform locations that were bound using the API for uniforms that were not found
|
||||
// from the shader. Other uniforms should not be assigned to those locations.
|
||||
for (const auto &locationBinding : uniformLocationBindings)
|
||||
{
|
||||
GLuint location = locationBinding.second;
|
||||
if (reservedLocations->find(location) == reservedLocations->end())
|
||||
{
|
||||
ignoredLocations->insert(location);
|
||||
*maxUniformLocation = std::max(*maxUniformLocation, static_cast<int>(location));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Program::pruneUnusedUniforms()
|
||||
{
|
||||
auto uniformIter = mState.mUniforms.begin();
|
||||
while (uniformIter != mState.mUniforms.end())
|
||||
{
|
||||
if (uniformIter->staticUse)
|
||||
{
|
||||
++uniformIter;
|
||||
}
|
||||
else
|
||||
{
|
||||
uniformIter = mState.mUniforms.erase(uniformIter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Program::indexUniforms(InfoLog &infoLog,
|
||||
const Caps &caps,
|
||||
const Bindings &uniformLocationBindings)
|
||||
{
|
||||
std::vector<VariableLocation> unlocatedUniforms;
|
||||
std::map<GLuint, VariableLocation> preLocatedUniforms;
|
||||
// All the locations where another uniform can't be located.
|
||||
std::set<GLuint> reservedLocations;
|
||||
// Locations which have been allocated for an unused uniform.
|
||||
std::set<GLuint> ignoredLocations;
|
||||
|
||||
int maxUniformLocation = -1;
|
||||
|
||||
// Gather uniform locations that have been set either using the bindUniformLocation API or by
|
||||
// using a location layout qualifier and check conflicts between them.
|
||||
if (!gatherUniformLocationsAndCheckConflicts(infoLog, uniformLocationBindings,
|
||||
&reservedLocations, &ignoredLocations,
|
||||
&maxUniformLocation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Conflicts have been checked, now we can prune non-statically used uniforms. Code further down
|
||||
// the line relies on only having statically used uniforms in mState.mUniforms.
|
||||
pruneUnusedUniforms();
|
||||
|
||||
// Gather uniforms that have their location pre-set and uniforms that don't yet have a location.
|
||||
std::vector<VariableLocation> unlocatedUniforms;
|
||||
std::map<GLuint, VariableLocation> preLocatedUniforms;
|
||||
|
||||
for (size_t uniformIndex = 0; uniformIndex < mState.mUniforms.size(); uniformIndex++)
|
||||
{
|
||||
const LinkedUniform &uniform = mState.mUniforms[uniformIndex];
|
||||
|
@ -2003,13 +2089,11 @@ bool Program::indexUniforms(InfoLog &infoLog,
|
|||
}
|
||||
|
||||
int preSetLocation = uniformLocationBindings.getBinding(uniform.name);
|
||||
int shaderLocation = uniform.location;
|
||||
|
||||
// Verify that this location isn't used twice
|
||||
if (preSetLocation != -1 &&
|
||||
preLocatedUniforms.find(preSetLocation) != preLocatedUniforms.end())
|
||||
if (shaderLocation != -1)
|
||||
{
|
||||
infoLog << "Multiple uniforms bound to location " << preSetLocation << ".";
|
||||
return false;
|
||||
preSetLocation = shaderLocation;
|
||||
}
|
||||
|
||||
for (unsigned int arrayIndex = 0; arrayIndex < uniform.elementCount(); arrayIndex++)
|
||||
|
@ -2017,10 +2101,10 @@ bool Program::indexUniforms(InfoLog &infoLog,
|
|||
VariableLocation location(uniform.name, arrayIndex,
|
||||
static_cast<unsigned int>(uniformIndex));
|
||||
|
||||
if (arrayIndex == 0 && preSetLocation != -1)
|
||||
if ((arrayIndex == 0 && preSetLocation != -1) || shaderLocation != -1)
|
||||
{
|
||||
preLocatedUniforms[preSetLocation] = location;
|
||||
maxUniformLocation = std::max(maxUniformLocation, preSetLocation);
|
||||
int elementLocation = preSetLocation + arrayIndex;
|
||||
preLocatedUniforms[elementLocation] = location;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2029,22 +2113,9 @@ bool Program::indexUniforms(InfoLog &infoLog,
|
|||
}
|
||||
}
|
||||
|
||||
// Gather the reserved locations, ones that are bound but not referenced. Other uniforms should
|
||||
// not be assigned to those locations.
|
||||
std::set<GLuint> reservedLocations;
|
||||
for (const auto &locationBinding : uniformLocationBindings)
|
||||
{
|
||||
GLuint location = locationBinding.second;
|
||||
if (preLocatedUniforms.find(location) == preLocatedUniforms.end())
|
||||
{
|
||||
reservedLocations.insert(location);
|
||||
maxUniformLocation = std::max(maxUniformLocation, static_cast<int>(location));
|
||||
}
|
||||
}
|
||||
|
||||
// Make enough space for all uniforms, with pre-set locations or not.
|
||||
mState.mUniformLocations.resize(
|
||||
std::max(unlocatedUniforms.size() + preLocatedUniforms.size() + reservedLocations.size(),
|
||||
std::max(unlocatedUniforms.size() + preLocatedUniforms.size() + ignoredLocations.size(),
|
||||
static_cast<size_t>(maxUniformLocation + 1)));
|
||||
|
||||
// Assign uniforms with pre-set locations
|
||||
|
@ -2053,10 +2124,10 @@ bool Program::indexUniforms(InfoLog &infoLog,
|
|||
mState.mUniformLocations[uniform.first] = uniform.second;
|
||||
}
|
||||
|
||||
// Assign reserved uniforms
|
||||
for (const auto &reservedLocation : reservedLocations)
|
||||
// Assign ignored uniforms
|
||||
for (const auto &ignoredLocation : ignoredLocations)
|
||||
{
|
||||
mState.mUniformLocations[reservedLocation].ignored = true;
|
||||
mState.mUniformLocations[ignoredLocation].ignored = true;
|
||||
}
|
||||
|
||||
// Automatically assign locations for the rest of the uniforms
|
||||
|
@ -2077,6 +2148,27 @@ bool Program::indexUniforms(InfoLog &infoLog,
|
|||
return true;
|
||||
}
|
||||
|
||||
void Program::updateSamplerBindings()
|
||||
{
|
||||
mState.mSamplerUniformRange.end = static_cast<unsigned int>(mState.mUniforms.size());
|
||||
mState.mSamplerUniformRange.start = mState.mSamplerUniformRange.end;
|
||||
auto samplerIter = mState.mUniforms.rbegin();
|
||||
while (samplerIter != mState.mUniforms.rend() && samplerIter->isSampler())
|
||||
{
|
||||
--mState.mSamplerUniformRange.start;
|
||||
++samplerIter;
|
||||
}
|
||||
// If uniform is a sampler type, insert it into the mSamplerBindings array.
|
||||
for (unsigned int samplerIndex = mState.mSamplerUniformRange.start;
|
||||
samplerIndex < mState.mUniforms.size(); ++samplerIndex)
|
||||
{
|
||||
const auto &samplerUniform = mState.mUniforms[samplerIndex];
|
||||
GLenum textureType = SamplerTypeToTextureType(samplerUniform.type);
|
||||
mState.mSamplerBindings.emplace_back(
|
||||
SamplerBinding(textureType, samplerUniform.elementCount()));
|
||||
}
|
||||
}
|
||||
|
||||
bool Program::linkValidateInterfaceBlockFields(InfoLog &infoLog,
|
||||
const std::string &uniformName,
|
||||
const sh::InterfaceBlockField &vertexUniform,
|
||||
|
@ -2397,7 +2489,6 @@ bool Program::linkValidateVariablesBase(InfoLog &infoLog, const std::string &var
|
|||
}
|
||||
|
||||
// GLSL ES Spec 3.00.3, section 4.3.5.
|
||||
// GLSL ES Spec 3.10.4, section 4.4.5.
|
||||
bool Program::linkValidateUniforms(InfoLog &infoLog, const std::string &uniformName, const sh::Uniform &vertexUniform, const sh::Uniform &fragmentUniform)
|
||||
{
|
||||
#if ANGLE_PROGRAM_LINK_VALIDATE_UNIFORM_PRECISION == ANGLE_ENABLED
|
||||
|
@ -2411,6 +2502,7 @@ bool Program::linkValidateUniforms(InfoLog &infoLog, const std::string &uniformN
|
|||
return false;
|
||||
}
|
||||
|
||||
// GLSL ES Spec 3.10.4, section 4.4.5.
|
||||
if (vertexUniform.binding != -1 && fragmentUniform.binding != -1 &&
|
||||
vertexUniform.binding != fragmentUniform.binding)
|
||||
{
|
||||
|
@ -2419,6 +2511,15 @@ bool Program::linkValidateUniforms(InfoLog &infoLog, const std::string &uniformN
|
|||
return false;
|
||||
}
|
||||
|
||||
// GLSL ES Spec 3.10.4, section 9.2.1.
|
||||
if (vertexUniform.location != -1 && fragmentUniform.location != -1 &&
|
||||
vertexUniform.location != fragmentUniform.location)
|
||||
{
|
||||
infoLog << "Location layout qualifiers for " << uniformName
|
||||
<< " differ between vertex and fragment shaders.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2738,10 +2839,7 @@ bool Program::flattenUniformsAndCheckCapsForShader(const Shader &shader,
|
|||
VectorAndSamplerCount vasCount;
|
||||
for (const sh::Uniform &uniform : shader.getUniforms())
|
||||
{
|
||||
if (uniform.staticUse)
|
||||
{
|
||||
vasCount += flattenUniform(uniform, uniform.name, &samplerUniforms);
|
||||
}
|
||||
vasCount += flattenUniform(uniform, &samplerUniforms);
|
||||
}
|
||||
|
||||
if (vasCount.vectorCount > maxUniformComponents)
|
||||
|
@ -2802,27 +2900,32 @@ bool Program::flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog)
|
|||
}
|
||||
}
|
||||
|
||||
mState.mSamplerUniformRange.start = static_cast<unsigned int>(mState.mUniforms.size());
|
||||
mState.mSamplerUniformRange.end =
|
||||
mState.mSamplerUniformRange.start + static_cast<unsigned int>(samplerUniforms.size());
|
||||
|
||||
mState.mUniforms.insert(mState.mUniforms.end(), samplerUniforms.begin(), samplerUniforms.end());
|
||||
|
||||
// If uniform is a sampler type, insert it into the mSamplerBindings array.
|
||||
for (const auto &samplerUniform : samplerUniforms)
|
||||
{
|
||||
GLenum textureType = SamplerTypeToTextureType(samplerUniform.type);
|
||||
mState.mSamplerBindings.emplace_back(
|
||||
SamplerBinding(textureType, samplerUniform.elementCount()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Program::VectorAndSamplerCount Program::flattenUniform(const sh::ShaderVariable &uniform,
|
||||
const std::string &fullName,
|
||||
Program::VectorAndSamplerCount Program::flattenUniform(const sh::Uniform &uniform,
|
||||
std::vector<LinkedUniform> *samplerUniforms)
|
||||
{
|
||||
int location = uniform.location;
|
||||
VectorAndSamplerCount uniformVasCount = flattenUniformImpl(
|
||||
uniform, uniform.name, samplerUniforms, uniform.staticUse, uniform.binding, &location);
|
||||
if (uniform.staticUse)
|
||||
{
|
||||
return uniformVasCount;
|
||||
}
|
||||
return VectorAndSamplerCount();
|
||||
}
|
||||
|
||||
Program::VectorAndSamplerCount Program::flattenUniformImpl(
|
||||
const sh::ShaderVariable &uniform,
|
||||
const std::string &fullName,
|
||||
std::vector<LinkedUniform> *samplerUniforms,
|
||||
bool markStaticUse,
|
||||
int binding,
|
||||
int *location)
|
||||
{
|
||||
ASSERT(location);
|
||||
VectorAndSamplerCount vectorAndSamplerCount;
|
||||
|
||||
if (uniform.isStruct())
|
||||
|
@ -2836,7 +2939,8 @@ Program::VectorAndSamplerCount Program::flattenUniform(const sh::ShaderVariable
|
|||
const sh::ShaderVariable &field = uniform.fields[fieldIndex];
|
||||
const std::string &fieldFullName = (fullName + elementString + "." + field.name);
|
||||
|
||||
vectorAndSamplerCount += flattenUniform(field, fieldFullName, samplerUniforms);
|
||||
vectorAndSamplerCount += flattenUniformImpl(field, fieldFullName, samplerUniforms,
|
||||
markStaticUse, -1, location);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2845,22 +2949,35 @@ Program::VectorAndSamplerCount Program::flattenUniform(const sh::ShaderVariable
|
|||
|
||||
// Not a struct
|
||||
bool isSampler = IsSamplerType(uniform.type);
|
||||
if (!UniformInList(mState.getUniforms(), fullName) &&
|
||||
!UniformInList(*samplerUniforms, fullName))
|
||||
std::vector<gl::LinkedUniform> *uniformList = &mState.mUniforms;
|
||||
if (isSampler)
|
||||
{
|
||||
// Store sampler uniforms separately, so we'll append them to the end of the list.
|
||||
uniformList = samplerUniforms;
|
||||
}
|
||||
LinkedUniform *existingUniform = FindUniform(*uniformList, fullName);
|
||||
if (existingUniform)
|
||||
{
|
||||
if (binding != -1)
|
||||
{
|
||||
existingUniform->binding = binding;
|
||||
}
|
||||
if (*location != -1)
|
||||
{
|
||||
existingUniform->location = *location;
|
||||
}
|
||||
if (markStaticUse)
|
||||
{
|
||||
existingUniform->staticUse = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LinkedUniform linkedUniform(uniform.type, uniform.precision, fullName, uniform.arraySize,
|
||||
-1, sh::BlockMemberInfo::getDefaultBlockInfo());
|
||||
linkedUniform.staticUse = true;
|
||||
|
||||
// Store sampler uniforms separately, so we'll append them to the end of the list.
|
||||
if (isSampler)
|
||||
{
|
||||
samplerUniforms->push_back(linkedUniform);
|
||||
}
|
||||
else
|
||||
{
|
||||
mState.mUniforms.push_back(linkedUniform);
|
||||
}
|
||||
binding, *location, -1,
|
||||
sh::BlockMemberInfo::getDefaultBlockInfo());
|
||||
linkedUniform.staticUse = markStaticUse;
|
||||
uniformList->push_back(linkedUniform);
|
||||
}
|
||||
|
||||
unsigned int elementCount = uniform.elementCount();
|
||||
|
@ -2871,6 +2988,11 @@ Program::VectorAndSamplerCount Program::flattenUniform(const sh::ShaderVariable
|
|||
(isSampler ? 0 : (VariableRegisterCount(uniform.type) * elementCount));
|
||||
vectorAndSamplerCount.samplerCount = (isSampler ? elementCount : 0);
|
||||
|
||||
if (*location != -1)
|
||||
{
|
||||
*location += elementCount;
|
||||
}
|
||||
|
||||
return vectorAndSamplerCount;
|
||||
}
|
||||
|
||||
|
@ -2972,7 +3094,7 @@ void Program::defineUniformBlockMembers(const std::vector<VarT> &fields,
|
|||
continue;
|
||||
}
|
||||
|
||||
LinkedUniform newUniform(field.type, field.precision, fullName, field.arraySize,
|
||||
LinkedUniform newUniform(field.type, field.precision, fullName, field.arraySize, -1, -1,
|
||||
blockIndex, memberInfo);
|
||||
|
||||
// Since block uniforms have no location, we don't need to store them in the uniform
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче