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;
......
......@@ -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