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> ...@@ -787,8 +787,11 @@ std::string ParseResourceName(const std::string &name, std::vector<unsigned int>
std::string StripLastArrayIndex(const std::string &name) std::string StripLastArrayIndex(const std::string &name)
{ {
size_t strippedNameLength = name.find_last_of('['); size_t strippedNameLength = name.find_last_of('[');
ASSERT(strippedNameLength != std::string::npos && name.back() == ']'); if (strippedNameLength != std::string::npos && name.back() == ']')
return name.substr(0, strippedNameLength); {
return name.substr(0, strippedNameLength);
}
return name;
} }
const sh::ShaderVariable *FindShaderVarField(const sh::ShaderVariable &var, const sh::ShaderVariable *FindShaderVarField(const sh::ShaderVariable &var,
......
...@@ -59,6 +59,15 @@ HashStream &operator<<(HashStream &stream, const ProgramBindings &bindings) ...@@ -59,6 +59,15 @@ HashStream &operator<<(HashStream &stream, const ProgramBindings &bindings)
{ {
for (const auto &binding : 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; stream << binding.first << binding.second.location;
} }
return stream; return stream;
......
...@@ -126,8 +126,40 @@ GLuint GetResourceIndexFromName(const std::vector<VarT> &list, const std::string ...@@ -126,8 +126,40 @@ GLuint GetResourceIndexFromName(const std::vector<VarT> &list, const std::string
return GL_INVALID_INDEX; return GL_INVALID_INDEX;
} }
template <typename VarT> GLint GetVariableLocation(const std::vector<sh::ShaderVariable> &list,
GLint GetVariableLocation(const std::vector<VarT> &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::vector<VariableLocation> &locationList,
const std::string &name) const std::string &name)
{ {
...@@ -142,13 +174,12 @@ GLint GetVariableLocation(const std::vector<VarT> &list, ...@@ -142,13 +174,12 @@ GLint GetVariableLocation(const std::vector<VarT> &list,
continue; 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 // 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 // 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. // seemingly depends on the opposite behavior, so only enable it for output variables.
if (angle::BeginsWith(variable.name, name) && if (angle::BeginsWith(variable.name, name) && (variableLocation.arrayIndex == 0))
(!std::is_base_of<sh::ShaderVariable, VarT>::value || variableLocation.arrayIndex == 0))
{ {
if (name.length() == variable.name.length()) if (name.length() == variable.name.length())
{ {
...@@ -902,6 +933,37 @@ ProgramBindings::~ProgramBindings() {} ...@@ -902,6 +933,37 @@ ProgramBindings::~ProgramBindings() {}
void ProgramBindings::bindLocation(GLuint index, const std::string &name) 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); mBindings[name] = ProgramBinding(index);
// EXT_blend_func_extended spec: "If it specifies the base name of an array, // 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) ...@@ -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); auto iter = mBindings.find(name);
return (iter != mBindings.end()) ? iter->second.location : -1; 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; const std::string &name = variable.name;
...@@ -949,17 +1011,29 @@ int ProgramBindings::getBinding(const sh::ShaderVariable &variable) const ...@@ -949,17 +1011,29 @@ int ProgramBindings::getBinding(const sh::ShaderVariable &variable) const
return iter->second.location; 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); return getBindingByName(name);
} }
ProgramBindings::const_iterator ProgramBindings::begin() const ProgramAliasedBindings::const_iterator ProgramAliasedBindings::begin() const
{ {
return mBindings.begin(); return mBindings.begin();
} }
ProgramBindings::const_iterator ProgramBindings::end() const ProgramAliasedBindings::const_iterator ProgramAliasedBindings::end() const
{ {
return mBindings.end(); return mBindings.end();
} }
...@@ -1137,6 +1211,26 @@ ShaderType ProgramState::getFirstAttachedShaderStageType() const ...@@ -1137,6 +1211,26 @@ ShaderType ProgramState::getFirstAttachedShaderStageType() const
return ShaderType::InvalidEnum; 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) Program::Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, ShaderProgramID handle)
: mProgram(factory->createProgram(mState)), : mProgram(factory->createProgram(mState)),
mValidated(false), mValidated(false),
...@@ -1279,7 +1373,7 @@ BindingInfo Program::getFragmentInputBindingInfo(GLint index) const ...@@ -1279,7 +1373,7 @@ BindingInfo Program::getFragmentInputBindingInfo(GLint index) const
for (const auto &binding : mFragmentInputBindings) for (const auto &binding : mFragmentInputBindings)
{ {
if (binding.second.location != static_cast<GLuint>(index)) if (binding.second != static_cast<GLuint>(index))
continue; continue;
ret.valid = true; ret.valid = true;
...@@ -1510,7 +1604,6 @@ angle::Result Program::link(const Context *context) ...@@ -1510,7 +1604,6 @@ angle::Result Program::link(const Context *context)
} }
updateLinkedShaderStages(); updateLinkedShaderStages();
mState.updateProgramInterfaceInputs();
mLinkingState.reset(new LinkingState()); mLinkingState.reset(new LinkingState());
mLinkingState->linkingFromBinary = false; mLinkingState->linkingFromBinary = false;
...@@ -1519,6 +1612,10 @@ angle::Result Program::link(const Context *context) ...@@ -1519,6 +1612,10 @@ angle::Result Program::link(const Context *context)
mLinkingState->resources = std::move(resources); mLinkingState->resources = std::move(resources);
mLinkResolved = false; mLinkResolved = false;
// Must be after mProgram->link() to avoid misleading the linker about output variables.
mState.updateProgramInterfaceInputs();
mState.updateProgramInterfaceOutputs();
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -1690,6 +1787,44 @@ void ProgramState::updateProgramInterfaceInputs() ...@@ -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 // Returns the program object to an unlinked state, before re-linking, or at destruction
void Program::unlink() void Program::unlink()
{ {
...@@ -2031,38 +2166,6 @@ GLint Program::getGeometryShaderMaxVertices() const ...@@ -2031,38 +2166,6 @@ GLint Program::getGeometryShaderMaxVertices() const
return mState.mGeometryShaderMaxVertices; 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 const sh::ShaderVariable &Program::getInputResource(size_t index) const
{ {
ASSERT(mLinkResolved); ASSERT(mLinkResolved);
...@@ -2073,7 +2176,7 @@ const sh::ShaderVariable &Program::getInputResource(size_t index) const ...@@ -2073,7 +2176,7 @@ const sh::ShaderVariable &Program::getInputResource(size_t index) const
GLuint Program::getInputResourceIndex(const GLchar *name) const GLuint Program::getInputResourceIndex(const GLchar *name) const
{ {
ASSERT(mLinkResolved); ASSERT(mLinkResolved);
const std::string nameString = stripArraySubscriptFromName(name); const std::string nameString = StripLastArrayIndex(name);
for (size_t index = 0; index < mState.mProgramInputs.size(); index++) for (size_t index = 0; index < mState.mProgramInputs.size(); index++)
{ {
...@@ -2087,25 +2190,57 @@ GLuint Program::getInputResourceIndex(const GLchar *name) const ...@@ -2087,25 +2190,57 @@ GLuint Program::getInputResourceIndex(const GLchar *name) const
return GL_INVALID_INDEX; 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 GLuint Program::getInputResourceMaxNameSize() const
{ {
GLint max = 0; 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()) return max;
{ }
max = std::max(max, clampCast<GLint>((resource.name + "[0]").size()));
} GLuint Program::getOutputResourceMaxNameSize() const
else {
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 GLuint Program::getInputResourceLocation(const GLchar *name) const
...@@ -2117,19 +2252,38 @@ GLuint Program::getInputResourceLocation(const GLchar *name) const ...@@ -2117,19 +2252,38 @@ GLuint Program::getInputResourceLocation(const GLchar *name) const
} }
const sh::ShaderVariable &variable = getInputResource(index); 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 GLuint Program::getOutputResourceIndex(const GLchar *name) const
{ {
ASSERT(mLinkResolved); 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 size_t Program::getOutputResourceCount() const
...@@ -2184,8 +2338,7 @@ void Program::getOutputResourceName(GLuint index, ...@@ -2184,8 +2338,7 @@ void Program::getOutputResourceName(GLuint index,
GLchar *name) const GLchar *name) const
{ {
ASSERT(mLinkResolved); ASSERT(mLinkResolved);
ASSERT(index < mState.mOutputVariables.size()); getResourceName(getOutputResourceName(index), bufSize, length, name);
getResourceName(mState.mOutputVariables[index].name, bufSize, length, name);
} }
void Program::getUniformResourceName(GLuint index, void Program::getUniformResourceName(GLuint index,
...@@ -2208,12 +2361,9 @@ void Program::getBufferVariableResourceName(GLuint index, ...@@ -2208,12 +2361,9 @@ void Program::getBufferVariableResourceName(GLuint index,
getResourceName(mState.mBufferVariables[index].name, bufSize, length, name); 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); std::string resourceName = resource.name;
const sh::ShaderVariable &resource = getInputResource(index);
std::string resourceName = resource.name;
if (resource.isArray()) if (resource.isArray())
{ {
...@@ -2223,7 +2373,23 @@ const std::string Program::getInputResourceName(GLuint index) const ...@@ -2223,7 +2373,23 @@ const std::string Program::getInputResourceName(GLuint index) const
return resourceName; 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(mLinkResolved);
ASSERT(index < mState.mOutputVariables.size()); ASSERT(index < mState.mOutputVariables.size());
...@@ -2235,7 +2401,7 @@ const ProgramBindings &Program::getAttributeBindings() const ...@@ -2235,7 +2401,7 @@ const ProgramBindings &Program::getAttributeBindings() const
ASSERT(mLinkResolved); ASSERT(mLinkResolved);
return mAttributeBindings; return mAttributeBindings;
} }
const ProgramBindings &Program::getUniformLocationBindings() const const ProgramAliasedBindings &Program::getUniformLocationBindings() const
{ {
ASSERT(mLinkResolved); ASSERT(mLinkResolved);
return mUniformLocationBindings; return mUniformLocationBindings;
...@@ -3316,7 +3482,7 @@ bool Program::linkValidateFragmentInputBindings(gl::InfoLog &infoLog) const ...@@ -3316,7 +3482,7 @@ bool Program::linkValidateFragmentInputBindings(gl::InfoLog &infoLog) const
bool Program::linkUniforms(const Caps &caps, bool Program::linkUniforms(const Caps &caps,
InfoLog &infoLog, InfoLog &infoLog,
const ProgramBindings &uniformLocationBindings, const ProgramAliasedBindings &uniformLocationBindings,
GLuint *combinedImageUniformsCount, GLuint *combinedImageUniformsCount,
std::vector<UnusedUniform> *unusedUniforms) std::vector<UnusedUniform> *unusedUniforms)
{ {
...@@ -4231,7 +4397,8 @@ void AssignOutputLocations(std::vector<VariableLocation> &outputLocations, ...@@ -4231,7 +4397,8 @@ void AssignOutputLocations(std::vector<VariableLocation> &outputLocations,
unsigned int baseLocation, unsigned int baseLocation,
unsigned int elementCount, unsigned int elementCount,
const std::vector<VariableLocation> &reservedLocations, const std::vector<VariableLocation> &reservedLocations,
unsigned int variableIndex) unsigned int variableIndex,
sh::ShaderVariable &outputVariable)
{ {
if (baseLocation + elementCount > outputLocations.size()) if (baseLocation + elementCount > outputLocations.size())
{ {
...@@ -4243,6 +4410,7 @@ void AssignOutputLocations(std::vector<VariableLocation> &outputLocations, ...@@ -4243,6 +4410,7 @@ void AssignOutputLocations(std::vector<VariableLocation> &outputLocations,
if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) == if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) ==
reservedLocations.end()) reservedLocations.end())
{ {
outputVariable.location = baseLocation;
const unsigned int location = baseLocation + elementIndex; const unsigned int location = baseLocation + elementIndex;
outputLocations[location] = locationInfo; outputLocations[location] = locationInfo;
} }
...@@ -4330,17 +4498,6 @@ bool Program::linkOutputVariables(const Caps &caps, ...@@ -4330,17 +4498,6 @@ bool Program::linkOutputVariables(const Caps &caps,
mState.mOutputVariables = outputVariables; mState.mOutputVariables = outputVariables;
// TODO(jmadill): any caps validation here? // 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 // EXT_blend_func_extended doesn't specify anything related to binding specific elements of an
// output array in explicit terms. // output array in explicit terms.
// //
...@@ -4444,7 +4601,7 @@ bool Program::linkOutputVariables(const Caps &caps, ...@@ -4444,7 +4601,7 @@ bool Program::linkOutputVariables(const Caps &caps,
return false; return false;
} }
AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations, 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 // 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, ...@@ -4492,7 +4649,8 @@ bool Program::linkOutputVariables(const Caps &caps,
baseLocation++; baseLocation++;
} }
AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations, AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations,
outputVariableIndex); outputVariableIndex,
mState.mOutputVariables[outputVariableIndex]);
} }
// Check for any elements assigned above the max location that are actually used. // Check for any elements assigned above the max location that are actually used.
......
...@@ -401,6 +401,7 @@ class ProgramState final : angle::NonCopyable ...@@ -401,6 +401,7 @@ class ProgramState final : angle::NonCopyable
return mActiveSamplerFormats[textureUnitIndex]; return mActiveSamplerFormats[textureUnitIndex];
} }
ShaderType getFirstAttachedShaderStageType() const; ShaderType getFirstAttachedShaderStageType() const;
ShaderType getLastAttachedShaderStageType() const;
private: private:
friend class MemoryProgramCache; friend class MemoryProgramCache;
...@@ -410,6 +411,7 @@ class ProgramState final : angle::NonCopyable ...@@ -410,6 +411,7 @@ class ProgramState final : angle::NonCopyable
void updateActiveSamplers(); void updateActiveSamplers();
void updateActiveImages(); void updateActiveImages();
void updateProgramInterfaceInputs(); void updateProgramInterfaceInputs();
void updateProgramInterfaceOutputs();
// Scans the sampler bindings for type conflicts with sampler 'textureUnitIndex'. // Scans the sampler bindings for type conflicts with sampler 'textureUnitIndex'.
void setSamplerUniformTextureTypeAndFormat(size_t textureUnitIndex); void setSamplerUniformTextureTypeAndFormat(size_t textureUnitIndex);
...@@ -534,6 +536,25 @@ class ProgramBindings final : angle::NonCopyable ...@@ -534,6 +536,25 @@ class ProgramBindings final : angle::NonCopyable
int getBindingByName(const std::string &name) const; int getBindingByName(const std::string &name) const;
int getBinding(const sh::ShaderVariable &variable) 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; using const_iterator = std::unordered_map<std::string, ProgramBinding>::const_iterator;
const_iterator begin() const; const_iterator begin() const;
const_iterator end() const; const_iterator end() const;
...@@ -883,14 +904,20 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -883,14 +904,20 @@ class Program final : angle::NonCopyable, public LabeledObject
GLsizei *length, GLsizei *length,
GLchar *name) const; GLchar *name) const;
const sh::ShaderVariable &getInputResource(size_t index) const; const sh::ShaderVariable &getInputResource(size_t index) const;
GLuint getResourceMaxNameSize(const sh::ShaderVariable &resource, GLint max) const;
GLuint getInputResourceMaxNameSize() const; GLuint getInputResourceMaxNameSize() const;
GLuint getOutputResourceMaxNameSize() const;
GLuint getResourceLocation(const GLchar *name, const sh::ShaderVariable &variable) const;
GLuint getInputResourceLocation(const GLchar *name) 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 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 &getAttributeBindings() const;
const ProgramBindings &getUniformLocationBindings() const;
const ProgramBindings &getFragmentInputBindings() const; const ProgramBindings &getFragmentInputBindings() const;
const ProgramAliasedBindings &getUniformLocationBindings() const;
int getNumViews() const int getNumViews() const
{ {
...@@ -942,8 +969,6 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -942,8 +969,6 @@ class Program final : angle::NonCopyable, public LabeledObject
// Writes a program's binary to the output memory buffer. // Writes a program's binary to the output memory buffer.
void serialize(const Context *context, angle::MemoryBuffer *binaryOut) const; void serialize(const Context *context, angle::MemoryBuffer *binaryOut) const;
int getArrayIndexFromName(const GLchar *name) const;
private: private:
struct LinkingState; struct LinkingState;
...@@ -966,7 +991,7 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -966,7 +991,7 @@ class Program final : angle::NonCopyable, public LabeledObject
bool linkUniforms(const Caps &caps, bool linkUniforms(const Caps &caps,
InfoLog &infoLog, InfoLog &infoLog,
const ProgramBindings &uniformLocationBindings, const ProgramAliasedBindings &uniformLocationBindings,
GLuint *combinedImageUniformsCount, GLuint *combinedImageUniformsCount,
std::vector<UnusedUniform> *unusedUniforms); std::vector<UnusedUniform> *unusedUniforms);
void linkSamplerAndImageBindings(GLuint *combinedImageUniformsCount); void linkSamplerAndImageBindings(GLuint *combinedImageUniformsCount);
...@@ -1050,8 +1075,6 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -1050,8 +1075,6 @@ class Program final : angle::NonCopyable, public LabeledObject
void postResolveLink(const gl::Context *context); void postResolveLink(const gl::Context *context);
std::string stripArraySubscriptFromName(const GLchar *name) const;
ProgramState mState; ProgramState mState;
rx::ProgramImpl *mProgram; rx::ProgramImpl *mProgram;
...@@ -1061,14 +1084,14 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -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 // 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. // uniforms in GLES3.1+. It is used to pre-set the location of uniforms.
ProgramBindings mUniformLocationBindings; ProgramAliasedBindings mUniformLocationBindings;
// CHROMIUM_path_rendering // CHROMIUM_path_rendering
ProgramBindings mFragmentInputBindings; ProgramBindings mFragmentInputBindings;
// EXT_blend_func_extended // EXT_blend_func_extended
ProgramBindings mFragmentOutputLocations; ProgramAliasedBindings mFragmentOutputLocations;
ProgramBindings mFragmentOutputIndexes; ProgramAliasedBindings mFragmentOutputIndexes;
bool mLinked; bool mLinked;
bool mLinkResolved; bool mLinkResolved;
......
...@@ -670,7 +670,7 @@ void UniformLinker::getResults(std::vector<LinkedUniform> *uniforms, ...@@ -670,7 +670,7 @@ void UniformLinker::getResults(std::vector<LinkedUniform> *uniforms,
bool UniformLinker::link(const Caps &caps, bool UniformLinker::link(const Caps &caps,
InfoLog &infoLog, InfoLog &infoLog,
const ProgramBindings &uniformLocationBindings) const ProgramAliasedBindings &uniformLocationBindings)
{ {
if (mState.getAttachedShader(ShaderType::Vertex) && if (mState.getAttachedShader(ShaderType::Vertex) &&
mState.getAttachedShader(ShaderType::Fragment)) mState.getAttachedShader(ShaderType::Fragment))
...@@ -735,7 +735,8 @@ bool UniformLinker::validateGraphicsUniforms(InfoLog &infoLog) const ...@@ -735,7 +735,8 @@ bool UniformLinker::validateGraphicsUniforms(InfoLog &infoLog) const
return true; 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. // Locations which have been allocated for an unused uniform.
std::set<GLuint> ignoredLocations; std::set<GLuint> ignoredLocations;
...@@ -830,7 +831,7 @@ bool UniformLinker::indexUniforms(InfoLog &infoLog, const ProgramBindings &unifo ...@@ -830,7 +831,7 @@ bool UniformLinker::indexUniforms(InfoLog &infoLog, const ProgramBindings &unifo
bool UniformLinker::gatherUniformLocationsAndCheckConflicts( bool UniformLinker::gatherUniformLocationsAndCheckConflicts(
InfoLog &infoLog, InfoLog &infoLog,
const ProgramBindings &uniformLocationBindings, const ProgramAliasedBindings &uniformLocationBindings,
std::set<GLuint> *ignoredLocations, std::set<GLuint> *ignoredLocations,
int *maxUniformLocation) int *maxUniformLocation)
{ {
......
...@@ -40,6 +40,7 @@ enum class LinkMismatchError; ...@@ -40,6 +40,7 @@ enum class LinkMismatchError;
struct LinkedUniform; struct LinkedUniform;
class ProgramState; class ProgramState;
class ProgramBindings; class ProgramBindings;
class ProgramAliasedBindings;
class Shader; class Shader;
struct ShaderVariableBuffer; struct ShaderVariableBuffer;
struct UnusedUniform; struct UnusedUniform;
...@@ -53,7 +54,9 @@ class UniformLinker final : angle::NonCopyable ...@@ -53,7 +54,9 @@ class UniformLinker final : angle::NonCopyable
UniformLinker(const ProgramState &state); UniformLinker(const ProgramState &state);
~UniformLinker(); ~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, void getResults(std::vector<LinkedUniform> *uniforms,
std::vector<UnusedUniform> *unusedUniforms, std::vector<UnusedUniform> *unusedUniforms,
...@@ -73,11 +76,12 @@ class UniformLinker final : angle::NonCopyable ...@@ -73,11 +76,12 @@ class UniformLinker final : angle::NonCopyable
bool flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog); bool flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog);
bool checkMaxCombinedAtomicCounters(const Caps &caps, InfoLog &infoLog); bool checkMaxCombinedAtomicCounters(const Caps &caps, InfoLog &infoLog);
bool indexUniforms(InfoLog &infoLog, const ProgramBindings &uniformLocationBindings); bool indexUniforms(InfoLog &infoLog, const ProgramAliasedBindings &uniformLocationBindings);
bool gatherUniformLocationsAndCheckConflicts(InfoLog &infoLog, bool gatherUniformLocationsAndCheckConflicts(
const ProgramBindings &uniformLocationBindings, InfoLog &infoLog,
std::set<GLuint> *ignoredLocations, const ProgramAliasedBindings &uniformLocationBindings,
int *maxUniformLocation); std::set<GLuint> *ignoredLocations,
int *maxUniformLocation);
void pruneUnusedUniforms(); void pruneUnusedUniforms();
const ProgramState &mState; const ProgramState &mState;
......
...@@ -650,28 +650,40 @@ GLint GetInputResourceProperty(const Program *program, GLuint index, GLenum prop ...@@ -650,28 +650,40 @@ GLint GetInputResourceProperty(const Program *program, GLuint index, GLenum prop
GLint GetOutputResourceProperty(const Program *program, GLuint index, const 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) switch (prop)
{ {
case GL_TYPE: case GL_TYPE:
case GL_ARRAY_SIZE: case GL_ARRAY_SIZE:
case GL_NAME_LENGTH:
return GetCommonVariableProperty(outputVariable, prop); return GetCommonVariableProperty(outputVariable, prop);
case GL_NAME_LENGTH:
return clampCast<GLint>(program->getOutputResourceName(index).size() + 1u);
case GL_LOCATION: case GL_LOCATION:
return program->getFragDataLocation(outputVariable.name); return outputVariable.location;
case GL_LOCATION_INDEX_EXT: case GL_LOCATION_INDEX_EXT:
// EXT_blend_func_extended // EXT_blend_func_extended
return program->getFragDataIndex(outputVariable.name); if (program->getState().getLastAttachedShaderStageType() == gl::ShaderType::Fragment)
{
case GL_REFERENCED_BY_FRAGMENT_SHADER: return program->getFragDataIndex(outputVariable.name);
return 1; }
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: 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: case GL_REFERENCED_BY_COMPUTE_SHADER:
return program->getState().getLastAttachedShaderStageType() == ShaderType::Compute;
case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT: case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT:
return 0; return program->getState().getLastAttachedShaderStageType() == ShaderType::Geometry;
default: default:
UNREACHABLE(); UNREACHABLE();
...@@ -756,8 +768,7 @@ GLint QueryProgramInterfaceMaxNameLength(const Program *program, GLenum programI ...@@ -756,8 +768,7 @@ GLint QueryProgramInterfaceMaxNameLength(const Program *program, GLenum programI
break; break;
case GL_PROGRAM_OUTPUT: case GL_PROGRAM_OUTPUT:
maxNameLength = maxNameLength = program->getOutputResourceMaxNameSize();
FindMaxSize(program->getState().getOutputVariables(), &sh::ShaderVariable::name);
break; break;
case GL_UNIFORM: case GL_UNIFORM:
...@@ -1847,7 +1858,7 @@ GLint QueryProgramResourceLocation(const Program *program, ...@@ -1847,7 +1858,7 @@ GLint QueryProgramResourceLocation(const Program *program,
return program->getInputResourceLocation(name); return program->getInputResourceLocation(name);
case GL_PROGRAM_OUTPUT: case GL_PROGRAM_OUTPUT:
return program->getFragDataLocation(name); return program->getOutputResourceLocation(name);
case GL_UNIFORM: case GL_UNIFORM:
return program->getUniformLocation(name); return program->getUniformLocation(name);
......
...@@ -522,13 +522,6 @@ void AssignAttributeLocations(const gl::ProgramState &programState, ...@@ -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, void AssignOutputLocations(const gl::ProgramState &programState,
IntermediateShaderSource *fragmentSource) IntermediateShaderSource *fragmentSource)
{ {
...@@ -545,21 +538,7 @@ void AssignOutputLocations(const gl::ProgramState &programState, ...@@ -545,21 +538,7 @@ void AssignOutputLocations(const gl::ProgramState &programState,
{ {
const sh::ShaderVariable &outputVar = outputVariables[outputLocation.index]; 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; std::string name = outputVar.name;
if (outputVar.isArray())
{
name = RemoveArrayZeroSubscript(name);
if (outputVar.isArrayOfArrays())
{
name = RemoveArrayZeroSubscript(name);
}
}
std::string locationString; std::string locationString;
if (outputVar.location != -1) if (outputVar.location != -1)
{ {
......
...@@ -798,6 +798,7 @@ void ProgramVk::initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap ...@@ -798,6 +798,7 @@ void ProgramVk::initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap
{ {
// Gets the uniform name without the [0] at the end. // Gets the uniform name without the [0] at the end.
uniformName = gl::StripLastArrayIndex(uniformName); uniformName = gl::StripLastArrayIndex(uniformName);
ASSERT(uniformName.size() != uniform.name.size());
} }
bool found = false; bool found = false;
......
...@@ -620,16 +620,6 @@ ...@@ -620,16 +620,6 @@
// SSBO and Image qualifiers: // SSBO and Image qualifiers:
3602 VULKAN : dEQP-GLES31.functional.synchronization.in_invocation.ssbo_alias_overwrite = FAIL 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: // Block name matching failure:
3459 VULKAN : dEQP-GLES31.functional.shaders.linkage.es31.shader_storage_block.mismatch_with_and_without_instance_name = FAIL 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 ...@@ -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: private:
bool mIsES31OrNewer; bool mIsES31OrNewer;
}; };
...@@ -421,6 +431,121 @@ void main() { ...@@ -421,6 +431,121 @@ void main() {
drawTest(); 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 an ESSL 3.00 program with a link-time fragment output location conflict.
TEST_P(EXTBlendFuncExtendedTestES3, FragmentOutputLocationConflict) TEST_P(EXTBlendFuncExtendedTestES3, FragmentOutputLocationConflict)
{ {
...@@ -592,7 +717,9 @@ ANGLE_INSTANTIATE_TEST(EXTBlendFuncExtendedTestES3, ...@@ -592,7 +717,9 @@ ANGLE_INSTANTIATE_TEST(EXTBlendFuncExtendedTestES3,
ES3_OPENGL(), ES3_OPENGL(),
ES3_OPENGLES(), ES3_OPENGLES(),
ES31_OPENGL(), ES31_OPENGL(),
ES31_OPENGLES()); ES31_OPENGLES(),
ES3_VULKAN(),
ES31_VULKAN());
ANGLE_INSTANTIATE_TEST(EXTBlendFuncExtendedDrawTest, ANGLE_INSTANTIATE_TEST(EXTBlendFuncExtendedDrawTest,
ES2_OPENGL(), ES2_OPENGL(),
ES2_OPENGLES(), ES2_OPENGLES(),
...@@ -603,4 +730,6 @@ ANGLE_INSTANTIATE_TEST(EXTBlendFuncExtendedDrawTestES3, ...@@ -603,4 +730,6 @@ ANGLE_INSTANTIATE_TEST(EXTBlendFuncExtendedDrawTestES3,
ES3_OPENGL(), ES3_OPENGL(),
ES3_OPENGLES(), ES3_OPENGLES(),
ES31_OPENGL(), ES31_OPENGL(),
ES31_OPENGLES()); ES31_OPENGLES(),
ES3_VULKAN(),
ES31_VULKAN());
...@@ -1195,6 +1195,10 @@ void main() { ...@@ -1195,6 +1195,10 @@ void main() {
glDeleteProgram(program); 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 } // 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