Commit 616a4dc0 by Tim Van Patten Committed by Commit Bot

Vulkan: Full support for program interface queries

Program interface queries are a generic way to query attributes of the program like uniforms, samplers, attributes, etc. This change supports those queries for program outputs. Bug: angleproject:3596 Test: dEQP-GLES31.functional.program_interface_query.* Test: ProgramInterfaceTest.cpp Change-Id: I0f13692949073b45988b6f930eee9eaa6411bbe2 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1801998 Commit-Queue: Tim Van Patten <timvp@google.com> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent f10bf6bf
......@@ -787,8 +787,11 @@ std::string ParseResourceName(const std::string &name, std::vector<unsigned int>
std::string StripLastArrayIndex(const std::string &name)
{
size_t strippedNameLength = name.find_last_of('[');
ASSERT(strippedNameLength != std::string::npos && name.back() == ']');
return name.substr(0, strippedNameLength);
if (strippedNameLength != std::string::npos && name.back() == ']')
{
return name.substr(0, strippedNameLength);
}
return name;
}
const sh::ShaderVariable *FindShaderVarField(const sh::ShaderVariable &var,
......
......@@ -59,6 +59,15 @@ HashStream &operator<<(HashStream &stream, const ProgramBindings &bindings)
{
for (const auto &binding : bindings)
{
stream << binding.first << binding.second;
}
return stream;
}
HashStream &operator<<(HashStream &stream, const ProgramAliasedBindings &bindings)
{
for (const auto &binding : bindings)
{
stream << binding.first << binding.second.location;
}
return stream;
......
......@@ -126,8 +126,40 @@ GLuint GetResourceIndexFromName(const std::vector<VarT> &list, const std::string
return GL_INVALID_INDEX;
}
template <typename VarT>
GLint GetVariableLocation(const std::vector<VarT> &list,
GLint GetVariableLocation(const std::vector<sh::ShaderVariable> &list,
const std::vector<VariableLocation> &locationList,
const std::string &name)
{
size_t nameLengthWithoutArrayIndex;
unsigned int arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
for (size_t location = 0u; location < locationList.size(); ++location)
{
const VariableLocation &variableLocation = locationList[location];
if (!variableLocation.used())
{
continue;
}
const sh::ShaderVariable &variable = list[variableLocation.index];
// Array output variables may be bound out of order, so we need to ensure we only pick the
// first element if given the base name.
if ((variable.name == name) && (variableLocation.arrayIndex == 0))
{
return static_cast<GLint>(location);
}
if (variable.isArray() && variableLocation.arrayIndex == arrayIndex &&
angle::BeginsWith(variable.name, name, nameLengthWithoutArrayIndex))
{
return static_cast<GLint>(location);
}
}
return -1;
}
GLint GetVariableLocation(const std::vector<LinkedUniform> &list,
const std::vector<VariableLocation> &locationList,
const std::string &name)
{
......@@ -142,13 +174,12 @@ GLint GetVariableLocation(const std::vector<VarT> &list,
continue;
}
const VarT &variable = list[variableLocation.index];
const LinkedUniform &variable = list[variableLocation.index];
// Array output variables may be bound out of order, so we need to ensure we only pick the
// first element if given the base name. Uniforms don't allow this behavior and some code
// seemingly depends on the opposite behavior, so only enable it for output variables.
if (angle::BeginsWith(variable.name, name) &&
(!std::is_base_of<sh::ShaderVariable, VarT>::value || variableLocation.arrayIndex == 0))
if (angle::BeginsWith(variable.name, name) && (variableLocation.arrayIndex == 0))
{
if (name.length() == variable.name.length())
{
......@@ -902,6 +933,37 @@ ProgramBindings::~ProgramBindings() {}
void ProgramBindings::bindLocation(GLuint index, const std::string &name)
{
mBindings[name] = index;
}
int ProgramBindings::getBindingByName(const std::string &name) const
{
auto iter = mBindings.find(name);
return (iter != mBindings.end()) ? iter->second : -1;
}
int ProgramBindings::getBinding(const sh::ShaderVariable &variable) const
{
return getBindingByName(variable.name);
}
ProgramBindings::const_iterator ProgramBindings::begin() const
{
return mBindings.begin();
}
ProgramBindings::const_iterator ProgramBindings::end() const
{
return mBindings.end();
}
// ProgramAliasedBindings implementation.
ProgramAliasedBindings::ProgramAliasedBindings() {}
ProgramAliasedBindings::~ProgramAliasedBindings() {}
void ProgramAliasedBindings::bindLocation(GLuint index, const std::string &name)
{
mBindings[name] = ProgramBinding(index);
// EXT_blend_func_extended spec: "If it specifies the base name of an array,
......@@ -923,13 +985,13 @@ void ProgramBindings::bindLocation(GLuint index, const std::string &name)
}
}
int ProgramBindings::getBindingByName(const std::string &name) const
int ProgramAliasedBindings::getBindingByName(const std::string &name) const
{
auto iter = mBindings.find(name);
return (iter != mBindings.end()) ? iter->second.location : -1;
}
int ProgramBindings::getBinding(const sh::ShaderVariable &variable) const
int ProgramAliasedBindings::getBinding(const sh::ShaderVariable &variable) const
{
const std::string &name = variable.name;
......@@ -949,17 +1011,29 @@ int ProgramBindings::getBinding(const sh::ShaderVariable &variable) const
return iter->second.location;
}
}
else if (arrayIndex == GL_INVALID_INDEX)
{
auto iter = mBindings.find(variable.name);
// If "name" exists and is not aliased, that means it was modified more
// recently than its "name[0]" form and should be used instead of that.
if (iter != mBindings.end() && !iter->second.aliased)
{
return iter->second.location;
}
// The base name was aliased, so use the name with the array notation.
return getBindingByName(name + "[0]");
}
}
return getBindingByName(name);
}
ProgramBindings::const_iterator ProgramBindings::begin() const
ProgramAliasedBindings::const_iterator ProgramAliasedBindings::begin() const
{
return mBindings.begin();
}
ProgramBindings::const_iterator ProgramBindings::end() const
ProgramAliasedBindings::const_iterator ProgramAliasedBindings::end() const
{
return mBindings.end();
}
......@@ -1137,6 +1211,26 @@ ShaderType ProgramState::getFirstAttachedShaderStageType() const
return ShaderType::InvalidEnum;
}
ShaderType ProgramState::getLastAttachedShaderStageType() const
{
for (int i = gl::kAllGraphicsShaderTypes.size() - 1; i >= 0; --i)
{
const gl::ShaderType shaderType = gl::kAllGraphicsShaderTypes[i];
if (hasLinkedShaderStage(shaderType))
{
return shaderType;
}
}
if (hasLinkedShaderStage(ShaderType::Compute))
{
return ShaderType::Compute;
}
return ShaderType::InvalidEnum;
}
Program::Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, ShaderProgramID handle)
: mProgram(factory->createProgram(mState)),
mValidated(false),
......@@ -1279,7 +1373,7 @@ BindingInfo Program::getFragmentInputBindingInfo(GLint index) const
for (const auto &binding : mFragmentInputBindings)
{
if (binding.second.location != static_cast<GLuint>(index))
if (binding.second != static_cast<GLuint>(index))
continue;
ret.valid = true;
......@@ -1510,7 +1604,6 @@ angle::Result Program::link(const Context *context)
}
updateLinkedShaderStages();
mState.updateProgramInterfaceInputs();
mLinkingState.reset(new LinkingState());
mLinkingState->linkingFromBinary = false;
......@@ -1519,6 +1612,10 @@ angle::Result Program::link(const Context *context)
mLinkingState->resources = std::move(resources);
mLinkResolved = false;
// Must be after mProgram->link() to avoid misleading the linker about output variables.
mState.updateProgramInterfaceInputs();
mState.updateProgramInterfaceOutputs();
return angle::Result::Continue;
}
......@@ -1690,6 +1787,44 @@ void ProgramState::updateProgramInterfaceInputs()
}
}
void ProgramState::updateProgramInterfaceOutputs()
{
const ShaderType lastAttachedShaderType = getLastAttachedShaderStageType();
if (lastAttachedShaderType == ShaderType::Fragment)
{
// Fragment outputs are already what we need, so nothing to do
return;
}
if (lastAttachedShaderType == ShaderType::Compute)
{
// If the program only contains a Compute Shader, then there are no user-defined outputs.
return;
}
Shader *shader = getAttachedShader(lastAttachedShaderType);
ASSERT(shader);
// Copy over each output varying, since the Shader could go away
for (const sh::ShaderVariable &varying : shader->getOutputVaryings())
{
if (varying.isStruct())
{
for (const sh::ShaderVariable &field : varying.fields)
{
sh::ShaderVariable fieldVarying = sh::ShaderVariable(field);
fieldVarying.location = varying.location;
fieldVarying.name = varying.name + "." + field.name;
mOutputVariables.emplace_back(fieldVarying);
}
}
else
{
mOutputVariables.emplace_back(varying);
}
}
}
// Returns the program object to an unlinked state, before re-linking, or at destruction
void Program::unlink()
{
......@@ -2031,38 +2166,6 @@ GLint Program::getGeometryShaderMaxVertices() const
return mState.mGeometryShaderMaxVertices;
}
std::string Program::stripArraySubscriptFromName(const GLchar *name) const
{
std::string nameString = std::string(name);
std::size_t bracketPos = nameString.find('[');
if (bracketPos != std::string::npos)
{
nameString = nameString.substr(0, bracketPos);
}
return nameString;
}
int Program::getArrayIndexFromName(const GLchar *name) const
{
std::string nameString = std::string(name);
std::size_t openBracketPos = nameString.find('[');
if (openBracketPos != std::string::npos)
{
std::size_t closeBracketPos = nameString.find(']');
if (closeBracketPos != std::string::npos)
{
return std::stoi(
nameString.substr(openBracketPos + 1, closeBracketPos - openBracketPos - 1));
}
}
// An array variable name without any subscripting defaults to "[0]"
return 0;
}
const sh::ShaderVariable &Program::getInputResource(size_t index) const
{
ASSERT(mLinkResolved);
......@@ -2073,7 +2176,7 @@ const sh::ShaderVariable &Program::getInputResource(size_t index) const
GLuint Program::getInputResourceIndex(const GLchar *name) const
{
ASSERT(mLinkResolved);
const std::string nameString = stripArraySubscriptFromName(name);
const std::string nameString = StripLastArrayIndex(name);
for (size_t index = 0; index < mState.mProgramInputs.size(); index++)
{
......@@ -2087,25 +2190,57 @@ GLuint Program::getInputResourceIndex(const GLchar *name) const
return GL_INVALID_INDEX;
}
GLuint Program::getResourceMaxNameSize(const sh::ShaderVariable &resource, GLint max) const
{
if (resource.isArray())
{
return std::max(max, clampCast<GLint>((resource.name + "[0]").size()));
}
else
{
return std::max(max, clampCast<GLint>((resource.name).size()));
}
}
GLuint Program::getInputResourceMaxNameSize() const
{
GLint max = 0;
for (size_t index = 0; index < mState.mProgramInputs.size(); index++)
for (const sh::ShaderVariable &resource : mState.mProgramInputs)
{
const sh::ShaderVariable &resource = getInputResource(index);
max = getResourceMaxNameSize(resource, max);
}
if (resource.isArray())
{
max = std::max(max, clampCast<GLint>((resource.name + "[0]").size()));
}
else
return max;
}
GLuint Program::getOutputResourceMaxNameSize() const
{
GLint max = 0;
for (const sh::ShaderVariable &resource : mState.mOutputVariables)
{
max = getResourceMaxNameSize(resource, max);
}
return max;
}
GLuint Program::getResourceLocation(const GLchar *name, const sh::ShaderVariable &variable) const
{
GLint location = variable.location;
if (variable.isArray())
{
size_t nameLengthWithoutArrayIndexOut;
size_t arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndexOut);
// The 'name' string may not contain the array notation "[0]"
if (arrayIndex != GL_INVALID_INDEX)
{
max = std::max(max, clampCast<GLint>((resource.name).size()));
location += arrayIndex;
}
}
return max;
return location;
}
GLuint Program::getInputResourceLocation(const GLchar *name) const
......@@ -2117,19 +2252,38 @@ GLuint Program::getInputResourceLocation(const GLchar *name) const
}
const sh::ShaderVariable &variable = getInputResource(index);
GLint location = variable.location;
if (variable.isArray())
return getResourceLocation(name, variable);
}
GLuint Program::getOutputResourceLocation(const GLchar *name) const
{
const GLuint index = getOutputResourceIndex(name);
if (index == GL_INVALID_INDEX)
{
location += getArrayIndexFromName(name);
return index;
}
return location;
const sh::ShaderVariable &variable = getOutputResource(index);
return getResourceLocation(name, variable);
}
GLuint Program::getOutputResourceIndex(const GLchar *name) const
{
ASSERT(mLinkResolved);
return GetResourceIndexFromName(mState.mOutputVariables, std::string(name));
const std::string nameString = StripLastArrayIndex(name);
for (size_t index = 0; index < mState.mOutputVariables.size(); index++)
{
sh::ShaderVariable resource = getOutputResource(index);
if (resource.name == nameString)
{
return static_cast<GLuint>(index);
}
}
return GL_INVALID_INDEX;
}
size_t Program::getOutputResourceCount() const
......@@ -2184,8 +2338,7 @@ void Program::getOutputResourceName(GLuint index,
GLchar *name) const
{
ASSERT(mLinkResolved);
ASSERT(index < mState.mOutputVariables.size());
getResourceName(mState.mOutputVariables[index].name, bufSize, length, name);
getResourceName(getOutputResourceName(index), bufSize, length, name);
}
void Program::getUniformResourceName(GLuint index,
......@@ -2208,12 +2361,9 @@ void Program::getBufferVariableResourceName(GLuint index,
getResourceName(mState.mBufferVariables[index].name, bufSize, length, name);
}
const std::string Program::getInputResourceName(GLuint index) const
const std::string Program::getResourceName(const sh::ShaderVariable &resource) const
{
ASSERT(mLinkResolved);
const sh::ShaderVariable &resource = getInputResource(index);
std::string resourceName = resource.name;
std::string resourceName = resource.name;
if (resource.isArray())
{
......@@ -2223,7 +2373,23 @@ const std::string Program::getInputResourceName(GLuint index) const
return resourceName;
}
const sh::ShaderVariable &Program::getOutputResource(GLuint index) const
const std::string Program::getInputResourceName(GLuint index) const
{
ASSERT(mLinkResolved);
const sh::ShaderVariable &resource = getInputResource(index);
return getResourceName(resource);
}
const std::string Program::getOutputResourceName(GLuint index) const
{
ASSERT(mLinkResolved);
const sh::ShaderVariable &resource = getOutputResource(index);
return getResourceName(resource);
}
const sh::ShaderVariable &Program::getOutputResource(size_t index) const
{
ASSERT(mLinkResolved);
ASSERT(index < mState.mOutputVariables.size());
......@@ -2235,7 +2401,7 @@ const ProgramBindings &Program::getAttributeBindings() const
ASSERT(mLinkResolved);
return mAttributeBindings;
}
const ProgramBindings &Program::getUniformLocationBindings() const
const ProgramAliasedBindings &Program::getUniformLocationBindings() const
{
ASSERT(mLinkResolved);
return mUniformLocationBindings;
......@@ -3316,7 +3482,7 @@ bool Program::linkValidateFragmentInputBindings(gl::InfoLog &infoLog) const
bool Program::linkUniforms(const Caps &caps,
InfoLog &infoLog,
const ProgramBindings &uniformLocationBindings,
const ProgramAliasedBindings &uniformLocationBindings,
GLuint *combinedImageUniformsCount,
std::vector<UnusedUniform> *unusedUniforms)
{
......@@ -4231,7 +4397,8 @@ void AssignOutputLocations(std::vector<VariableLocation> &outputLocations,
unsigned int baseLocation,
unsigned int elementCount,
const std::vector<VariableLocation> &reservedLocations,
unsigned int variableIndex)
unsigned int variableIndex,
sh::ShaderVariable &outputVariable)
{
if (baseLocation + elementCount > outputLocations.size())
{
......@@ -4243,6 +4410,7 @@ void AssignOutputLocations(std::vector<VariableLocation> &outputLocations,
if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) ==
reservedLocations.end())
{
outputVariable.location = baseLocation;
const unsigned int location = baseLocation + elementIndex;
outputLocations[location] = locationInfo;
}
......@@ -4330,17 +4498,6 @@ bool Program::linkOutputVariables(const Caps &caps,
mState.mOutputVariables = outputVariables;
// TODO(jmadill): any caps validation here?
for (sh::ShaderVariable &outputVariable : mState.mOutputVariables)
{
if (outputVariable.isArray())
{
// We're following the GLES 3.1 November 2016 spec section 7.3.1.1 Naming Active
// Resources and including [0] at the end of array variable names.
outputVariable.name += "[0]";
outputVariable.mappedName += "[0]";
}
}
// EXT_blend_func_extended doesn't specify anything related to binding specific elements of an
// output array in explicit terms.
//
......@@ -4444,7 +4601,7 @@ bool Program::linkOutputVariables(const Caps &caps,
return false;
}
AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations,
outputVariableIndex);
outputVariableIndex, mState.mOutputVariables[outputVariableIndex]);
}
// Here we assign locations for the output variables that don't yet have them. Note that we're
......@@ -4492,7 +4649,8 @@ bool Program::linkOutputVariables(const Caps &caps,
baseLocation++;
}
AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations,
outputVariableIndex);
outputVariableIndex,
mState.mOutputVariables[outputVariableIndex]);
}
// Check for any elements assigned above the max location that are actually used.
......
......@@ -401,6 +401,7 @@ class ProgramState final : angle::NonCopyable
return mActiveSamplerFormats[textureUnitIndex];
}
ShaderType getFirstAttachedShaderStageType() const;
ShaderType getLastAttachedShaderStageType() const;
private:
friend class MemoryProgramCache;
......@@ -410,6 +411,7 @@ class ProgramState final : angle::NonCopyable
void updateActiveSamplers();
void updateActiveImages();
void updateProgramInterfaceInputs();
void updateProgramInterfaceOutputs();
// Scans the sampler bindings for type conflicts with sampler 'textureUnitIndex'.
void setSamplerUniformTextureTypeAndFormat(size_t textureUnitIndex);
......@@ -534,6 +536,25 @@ class ProgramBindings final : angle::NonCopyable
int getBindingByName(const std::string &name) const;
int getBinding(const sh::ShaderVariable &variable) const;
using const_iterator = std::unordered_map<std::string, GLuint>::const_iterator;
const_iterator begin() const;
const_iterator end() const;
private:
std::unordered_map<std::string, GLuint> mBindings;
};
// Uniforms and Fragment Outputs require special treatment due to array notation (e.g., "[0]")
class ProgramAliasedBindings final : angle::NonCopyable
{
public:
ProgramAliasedBindings();
~ProgramAliasedBindings();
void bindLocation(GLuint index, const std::string &name);
int getBindingByName(const std::string &name) const;
int getBinding(const sh::ShaderVariable &variable) const;
using const_iterator = std::unordered_map<std::string, ProgramBinding>::const_iterator;
const_iterator begin() const;
const_iterator end() const;
......@@ -883,14 +904,20 @@ class Program final : angle::NonCopyable, public LabeledObject
GLsizei *length,
GLchar *name) const;
const sh::ShaderVariable &getInputResource(size_t index) const;
GLuint getResourceMaxNameSize(const sh::ShaderVariable &resource, GLint max) const;
GLuint getInputResourceMaxNameSize() const;
GLuint getOutputResourceMaxNameSize() const;
GLuint getResourceLocation(const GLchar *name, const sh::ShaderVariable &variable) const;
GLuint getInputResourceLocation(const GLchar *name) const;
GLuint getOutputResourceLocation(const GLchar *name) const;
const std::string getResourceName(const sh::ShaderVariable &resource) const;
const std::string getInputResourceName(GLuint index) const;
const sh::ShaderVariable &getOutputResource(GLuint index) const;
const std::string getOutputResourceName(GLuint index) const;
const sh::ShaderVariable &getOutputResource(size_t index) const;
const ProgramBindings &getAttributeBindings() const;
const ProgramBindings &getUniformLocationBindings() const;
const ProgramBindings &getFragmentInputBindings() const;
const ProgramAliasedBindings &getUniformLocationBindings() const;
int getNumViews() const
{
......@@ -942,8 +969,6 @@ class Program final : angle::NonCopyable, public LabeledObject
// Writes a program's binary to the output memory buffer.
void serialize(const Context *context, angle::MemoryBuffer *binaryOut) const;
int getArrayIndexFromName(const GLchar *name) const;
private:
struct LinkingState;
......@@ -966,7 +991,7 @@ class Program final : angle::NonCopyable, public LabeledObject
bool linkUniforms(const Caps &caps,
InfoLog &infoLog,
const ProgramBindings &uniformLocationBindings,
const ProgramAliasedBindings &uniformLocationBindings,
GLuint *combinedImageUniformsCount,
std::vector<UnusedUniform> *unusedUniforms);
void linkSamplerAndImageBindings(GLuint *combinedImageUniformsCount);
......@@ -1050,8 +1075,6 @@ class Program final : angle::NonCopyable, public LabeledObject
void postResolveLink(const gl::Context *context);
std::string stripArraySubscriptFromName(const GLchar *name) const;
ProgramState mState;
rx::ProgramImpl *mProgram;
......@@ -1061,14 +1084,14 @@ class Program final : angle::NonCopyable, public LabeledObject
// Note that this has nothing to do with binding layout qualifiers that can be set for some
// uniforms in GLES3.1+. It is used to pre-set the location of uniforms.
ProgramBindings mUniformLocationBindings;
ProgramAliasedBindings mUniformLocationBindings;
// CHROMIUM_path_rendering
ProgramBindings mFragmentInputBindings;
// EXT_blend_func_extended
ProgramBindings mFragmentOutputLocations;
ProgramBindings mFragmentOutputIndexes;
ProgramAliasedBindings mFragmentOutputLocations;
ProgramAliasedBindings mFragmentOutputIndexes;
bool mLinked;
bool mLinkResolved;
......
......@@ -670,7 +670,7 @@ void UniformLinker::getResults(std::vector<LinkedUniform> *uniforms,
bool UniformLinker::link(const Caps &caps,
InfoLog &infoLog,
const ProgramBindings &uniformLocationBindings)
const ProgramAliasedBindings &uniformLocationBindings)
{
if (mState.getAttachedShader(ShaderType::Vertex) &&
mState.getAttachedShader(ShaderType::Fragment))
......@@ -735,7 +735,8 @@ bool UniformLinker::validateGraphicsUniforms(InfoLog &infoLog) const
return true;
}
bool UniformLinker::indexUniforms(InfoLog &infoLog, const ProgramBindings &uniformLocationBindings)
bool UniformLinker::indexUniforms(InfoLog &infoLog,
const ProgramAliasedBindings &uniformLocationBindings)
{
// Locations which have been allocated for an unused uniform.
std::set<GLuint> ignoredLocations;
......@@ -830,7 +831,7 @@ bool UniformLinker::indexUniforms(InfoLog &infoLog, const ProgramBindings &unifo
bool UniformLinker::gatherUniformLocationsAndCheckConflicts(
InfoLog &infoLog,
const ProgramBindings &uniformLocationBindings,
const ProgramAliasedBindings &uniformLocationBindings,
std::set<GLuint> *ignoredLocations,
int *maxUniformLocation)
{
......
......@@ -40,6 +40,7 @@ enum class LinkMismatchError;
struct LinkedUniform;
class ProgramState;
class ProgramBindings;
class ProgramAliasedBindings;
class Shader;
struct ShaderVariableBuffer;
struct UnusedUniform;
......@@ -53,7 +54,9 @@ class UniformLinker final : angle::NonCopyable
UniformLinker(const ProgramState &state);
~UniformLinker();
bool link(const Caps &caps, InfoLog &infoLog, const ProgramBindings &uniformLocationBindings);
bool link(const Caps &caps,
InfoLog &infoLog,
const ProgramAliasedBindings &uniformLocationBindings);
void getResults(std::vector<LinkedUniform> *uniforms,
std::vector<UnusedUniform> *unusedUniforms,
......@@ -73,11 +76,12 @@ class UniformLinker final : angle::NonCopyable
bool flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog);
bool checkMaxCombinedAtomicCounters(const Caps &caps, InfoLog &infoLog);
bool indexUniforms(InfoLog &infoLog, const ProgramBindings &uniformLocationBindings);
bool gatherUniformLocationsAndCheckConflicts(InfoLog &infoLog,
const ProgramBindings &uniformLocationBindings,
std::set<GLuint> *ignoredLocations,
int *maxUniformLocation);
bool indexUniforms(InfoLog &infoLog, const ProgramAliasedBindings &uniformLocationBindings);
bool gatherUniformLocationsAndCheckConflicts(
InfoLog &infoLog,
const ProgramAliasedBindings &uniformLocationBindings,
std::set<GLuint> *ignoredLocations,
int *maxUniformLocation);
void pruneUnusedUniforms();
const ProgramState &mState;
......
......@@ -650,28 +650,40 @@ GLint GetInputResourceProperty(const Program *program, GLuint index, GLenum prop
GLint GetOutputResourceProperty(const Program *program, GLuint index, const GLenum prop)
{
const auto &outputVariable = program->getOutputResource(index);
const sh::ShaderVariable &outputVariable = program->getOutputResource(index);
switch (prop)
{
case GL_TYPE:
case GL_ARRAY_SIZE:
case GL_NAME_LENGTH:
return GetCommonVariableProperty(outputVariable, prop);
case GL_NAME_LENGTH:
return clampCast<GLint>(program->getOutputResourceName(index).size() + 1u);
case GL_LOCATION:
return program->getFragDataLocation(outputVariable.name);
return outputVariable.location;
case GL_LOCATION_INDEX_EXT:
// EXT_blend_func_extended
return program->getFragDataIndex(outputVariable.name);
case GL_REFERENCED_BY_FRAGMENT_SHADER:
return 1;
if (program->getState().getLastAttachedShaderStageType() == gl::ShaderType::Fragment)
{
return program->getFragDataIndex(outputVariable.name);
}
return GL_INVALID_INDEX;
// The set of active user-defined outputs from the final shader stage in this program. If
// the final stage is a Fragment Shader, then this represents the fragment outputs that get
// written to individual color buffers. If the program only contains a Compute Shader, then
// there are no user-defined outputs.
case GL_REFERENCED_BY_VERTEX_SHADER:
return program->getState().getLastAttachedShaderStageType() == ShaderType::Vertex;
case GL_REFERENCED_BY_FRAGMENT_SHADER:
return program->getState().getLastAttachedShaderStageType() == ShaderType::Fragment;
case GL_REFERENCED_BY_COMPUTE_SHADER:
return program->getState().getLastAttachedShaderStageType() == ShaderType::Compute;
case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT:
return 0;
return program->getState().getLastAttachedShaderStageType() == ShaderType::Geometry;
default:
UNREACHABLE();
......@@ -756,8 +768,7 @@ GLint QueryProgramInterfaceMaxNameLength(const Program *program, GLenum programI
break;
case GL_PROGRAM_OUTPUT:
maxNameLength =
FindMaxSize(program->getState().getOutputVariables(), &sh::ShaderVariable::name);
maxNameLength = program->getOutputResourceMaxNameSize();
break;
case GL_UNIFORM:
......@@ -1847,7 +1858,7 @@ GLint QueryProgramResourceLocation(const Program *program,
return program->getInputResourceLocation(name);
case GL_PROGRAM_OUTPUT:
return program->getFragDataLocation(name);
return program->getOutputResourceLocation(name);
case GL_UNIFORM:
return program->getUniformLocation(name);
......
......@@ -522,13 +522,6 @@ void AssignAttributeLocations(const gl::ProgramState &programState,
}
}
std::string RemoveArrayZeroSubscript(const std::string &expression)
{
ASSERT(expression.size() > 3);
ASSERT(expression.substr(expression.size() - 3) == "[0]");
return expression.substr(0, expression.size() - 3);
}
void AssignOutputLocations(const gl::ProgramState &programState,
IntermediateShaderSource *fragmentSource)
{
......@@ -545,21 +538,7 @@ void AssignOutputLocations(const gl::ProgramState &programState,
{
const sh::ShaderVariable &outputVar = outputVariables[outputLocation.index];
// In the following:
//
// out vec4 fragOutput[N];
//
// The varying name is |fragOutput[0]|. We need to remove the extra |[0]|.
std::string name = outputVar.name;
if (outputVar.isArray())
{
name = RemoveArrayZeroSubscript(name);
if (outputVar.isArrayOfArrays())
{
name = RemoveArrayZeroSubscript(name);
}
}
std::string locationString;
if (outputVar.location != -1)
{
......
......@@ -798,6 +798,7 @@ void ProgramVk::initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap
{
// Gets the uniform name without the [0] at the end.
uniformName = gl::StripLastArrayIndex(uniformName);
ASSERT(uniformName.size() != uniform.name.size());
}
bool found = false;
......
......@@ -620,16 +620,6 @@
// SSBO and Image qualifiers:
3602 VULKAN : dEQP-GLES31.functional.synchronization.in_invocation.ssbo_alias_overwrite = FAIL
// Separate shader objects:
3570 VULKAN : dEQP-GLES31.functional.program_interface_query.program_output.resource_list.separable_vertex.* = FAIL
3570 VULKAN : dEQP-GLES31.functional.program_interface_query.program_output.array_size.separable_vertex.var* = FAIL
3570 VULKAN : dEQP-GLES31.functional.program_interface_query.program_output.location.separable_vertex.var* = FAIL
3570 VULKAN : dEQP-GLES31.functional.program_interface_query.program_output.name_length.separable_vertex.var* = FAIL
3570 VULKAN : dEQP-GLES31.functional.program_interface_query.program_output.referenced_by.referenced_by_separable_vertex = FAIL
3570 VULKAN : dEQP-GLES31.functional.program_interface_query.program_output.type.separable_vertex.basic_type.* = FAIL
3570 VULKAN : dEQP-GLES31.functional.program_interface_query.program_output.type.separable_vertex.array.* = FAIL
3570 VULKAN : dEQP-GLES31.functional.program_interface_query.program_output.type.separable_vertex.struct.* = FAIL
// Block name matching failure:
3459 VULKAN : dEQP-GLES31.functional.shaders.linkage.es31.shader_storage_block.mismatch_with_and_without_instance_name = FAIL
......
......@@ -231,6 +231,16 @@ class EXTBlendFuncExtendedDrawTestES3 : public EXTBlendFuncExtendedDrawTest
}
}
void LinkProgram()
{
glLinkProgram(mProgram);
GLint linked = 0;
glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
EXPECT_NE(0, linked);
glUseProgram(mProgram);
return;
}
private:
bool mIsES31OrNewer;
};
......@@ -421,6 +431,121 @@ void main() {
drawTest();
}
// Ported from TranslatorVariants/EXTBlendFuncExtendedES3DrawTest
// Test that tests glBindFragDataLocationEXT, glBindFragDataLocationIndexedEXT,
// glGetFragDataLocation, glGetFragDataIndexEXT work correctly with
// GLSL array output variables. The output variable can be bound by
// referring to the variable name with or without the first element array
// accessor. The getters can query location of the individual elements in
// the array. The test does not actually use the base test drawing,
// since the drivers at the time of writing do not support multiple
// buffers and dual source blending.
TEST_P(EXTBlendFuncExtendedDrawTestES3, ES3GettersArray)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
// TODO(zmo): Figure out why this fails on AMD. crbug.com/585132.
// Also fails on the Intel Mesa driver, see
// https://bugs.freedesktop.org/show_bug.cgi?id=96765
ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD());
ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel());
const GLint kTestArraySize = 2;
const GLint kFragData0Location = 2;
const GLint kFragData1Location = 1;
const GLint kUnusedLocation = 5;
// The test binds kTestArraySize -sized array to location 1 for test purposes.
// The GL_MAX_DRAW_BUFFERS must be > kTestArraySize, since an
// array will be bound to continuous locations, starting from the first
// location.
GLint maxDrawBuffers = 0;
glGetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &maxDrawBuffers);
EXPECT_LT(kTestArraySize, maxDrawBuffers);
constexpr char kFragColorShader[] = R"(#version 300 es
#extension GL_EXT_blend_func_extended : require
precision mediump float;
uniform vec4 src;
uniform vec4 src1;
out vec4 FragData[2];
void main() {
FragData[0] = src;
FragData[1] = src1;
})";
struct testCase
{
std::string unusedLocationName;
std::string fragData0LocationName;
std::string fragData1LocationName;
};
testCase testCases[4]{{"FragData[0]", "FragData", "FragData[1]"},
{"FragData", "FragData[0]", "FragData[1]"},
{"FragData[0]", "FragData", "FragData[1]"},
{"FragData", "FragData[0]", "FragData[1]"}};
for (const testCase &test : testCases)
{
mProgram =
CompileProgram(essl3_shaders::vs::Simple(), kFragColorShader, [&](GLuint program) {
glBindFragDataLocationEXT(program, kUnusedLocation,
test.unusedLocationName.c_str());
glBindFragDataLocationEXT(program, kFragData0Location,
test.fragData0LocationName.c_str());
glBindFragDataLocationEXT(program, kFragData1Location,
test.fragData1LocationName.c_str());
});
EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
LinkProgram();
EXPECT_EQ(kFragData0Location, glGetFragDataLocation(mProgram, "FragData"));
EXPECT_EQ(0, glGetFragDataIndexEXT(mProgram, "FragData"));
EXPECT_EQ(kFragData0Location, glGetFragDataLocation(mProgram, "FragData[0]"));
EXPECT_EQ(0, glGetFragDataIndexEXT(mProgram, "FragData[0]"));
EXPECT_EQ(kFragData1Location, glGetFragDataLocation(mProgram, "FragData[1]"));
EXPECT_EQ(0, glGetFragDataIndexEXT(mProgram, "FragData[1]"));
// Index bigger than the GLSL variable array length does not find anything.
EXPECT_EQ(-1, glGetFragDataLocation(mProgram, "FragData[3]"));
}
}
// Ported from TranslatorVariants/EXTBlendFuncExtendedES3DrawTest
TEST_P(EXTBlendFuncExtendedDrawTestES3, ESSL3BindSimpleVarAsArrayNoBind)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
constexpr char kFragDataShader[] = R"(#version 300 es
#extension GL_EXT_blend_func_extended : require
precision mediump float;
uniform vec4 src;
uniform vec4 src1;
out vec4 FragData;
out vec4 SecondaryFragData;
void main() {
FragData = src;
SecondaryFragData = src1;
})";
mProgram = CompileProgram(essl3_shaders::vs::Simple(), kFragDataShader, [](GLuint program) {
glBindFragDataLocationEXT(program, 0, "FragData[0]");
glBindFragDataLocationIndexedEXT(program, 0, 1, "SecondaryFragData[0]");
});
LinkProgram();
EXPECT_EQ(-1, glGetFragDataLocation(mProgram, "FragData[0]"));
EXPECT_EQ(0, glGetFragDataLocation(mProgram, "FragData"));
EXPECT_EQ(1, glGetFragDataLocation(mProgram, "SecondaryFragData"));
// Did not bind index.
EXPECT_EQ(0, glGetFragDataIndexEXT(mProgram, "SecondaryFragData"));
glBindFragDataLocationEXT(mProgram, 0, "FragData");
glBindFragDataLocationIndexedEXT(mProgram, 0, 1, "SecondaryFragData");
LinkProgram();
}
// Test an ESSL 3.00 program with a link-time fragment output location conflict.
TEST_P(EXTBlendFuncExtendedTestES3, FragmentOutputLocationConflict)
{
......@@ -592,7 +717,9 @@ ANGLE_INSTANTIATE_TEST(EXTBlendFuncExtendedTestES3,
ES3_OPENGL(),
ES3_OPENGLES(),
ES31_OPENGL(),
ES31_OPENGLES());
ES31_OPENGLES(),
ES3_VULKAN(),
ES31_VULKAN());
ANGLE_INSTANTIATE_TEST(EXTBlendFuncExtendedDrawTest,
ES2_OPENGL(),
ES2_OPENGLES(),
......@@ -603,4 +730,6 @@ ANGLE_INSTANTIATE_TEST(EXTBlendFuncExtendedDrawTestES3,
ES3_OPENGL(),
ES3_OPENGLES(),
ES31_OPENGL(),
ES31_OPENGLES());
ES31_OPENGLES(),
ES3_VULKAN(),
ES31_VULKAN());
......@@ -1195,6 +1195,10 @@ void main() {
glDeleteProgram(program);
}
ANGLE_INSTANTIATE_TEST(ProgramInterfaceTestES31, ES31_OPENGL(), ES31_OPENGLES(), ES31_D3D11());
ANGLE_INSTANTIATE_TEST(ProgramInterfaceTestES31,
ES31_OPENGL(),
ES31_OPENGLES(),
ES31_D3D11(),
ES31_VULKAN());
} // anonymous 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