Commit 7cf6166a by Martin Radev Committed by Commit Bot

Generate error on program-framebuffer num views mismatch

According to the ANGLE_multiview spec Draw* commands should generate an INVALID_OPERATION error if the program uses the multiview extension and the number of views in the active draw framebuffer and active program differs. The patch addresses this by extending the base draw call validation. BUG=angleproject:2062 TEST=angle_end2end_tests Change-Id: I369070beb5ccb17211dbe61ebec40bfcbcf5bc4e Reviewed-on: https://chromium-review.googlesource.com/586605 Commit-Queue: Martin Radev <mradev@nvidia.com> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent 6938285b
......@@ -540,6 +540,8 @@ const std::vector<sh::Attribute> *GetAttributes(const ShHandle handle);
const std::vector<sh::OutputVariable> *GetOutputVariables(const ShHandle handle);
const std::vector<sh::InterfaceBlock> *GetInterfaceBlocks(const ShHandle handle);
sh::WorkGroupSize GetComputeShaderLocalGroupSize(const ShHandle handle);
// Returns the number of views specified through the num_views layout qualifier. If num_views is
// not set, the function returns -1.
int GetVertexShaderNumViews(const ShHandle handle);
// Returns true if the passed in variables pack in maxVectors followingthe packing rules from the
......
......@@ -168,6 +168,8 @@ LinkResult MemoryProgramCache::Deserialize(const Context *context,
state->mComputeShaderLocalSize[1] = stream.readInt<int>();
state->mComputeShaderLocalSize[2] = stream.readInt<int>();
state->mNumViews = stream.readInt<int>();
static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
"Too many vertex attribs for mask");
state->mActiveAttribLocationsMask = stream.readInt<unsigned long>();
......@@ -359,6 +361,8 @@ void MemoryProgramCache::Serialize(const Context *context,
stream.writeInt(computeLocalSize[1]);
stream.writeInt(computeLocalSize[2]);
stream.writeInt(state.mNumViews);
stream.writeInt(state.getActiveAttribLocationsMask().to_ulong());
stream.writeInt(state.getAttributes().size());
......
......@@ -298,7 +298,8 @@ ProgramState::ProgramState()
mSamplerUniformRange(0, 0),
mImageUniformRange(0, 0),
mAtomicCounterUniformRange(0, 0),
mBinaryRetrieveableHint(false)
mBinaryRetrieveableHint(false),
mNumViews(-1)
{
mComputeShaderLocalSize.fill(1);
}
......@@ -739,6 +740,8 @@ Error Program::link(const gl::Context *context)
return NoError();
}
mState.mNumViews = vertexShader->getNumViews(context);
linkOutputVariables(context);
// Validate we can pack the varyings.
......@@ -797,6 +800,7 @@ void Program::unlink()
mState.mComputeShaderLocalSize.fill(1);
mState.mSamplerBindings.clear();
mState.mImageBindings.clear();
mState.mNumViews = -1;
mValidated = false;
......
......@@ -324,6 +324,9 @@ class ProgramState final : angle::NonCopyable
bool mBinaryRetrieveableHint;
bool mSeparable;
// ANGLE_multiview.
int mNumViews;
};
class Program final : angle::NonCopyable, public LabeledObject
......@@ -523,6 +526,8 @@ class Program final : angle::NonCopyable, public LabeledObject
const Bindings &getUniformLocationBindings() const { return mUniformLocationBindings; }
const Bindings &getFragmentInputBindings() const { return mFragmentInputBindings; }
int getNumViews() const { return mState.mNumViews; }
private:
~Program();
......
......@@ -78,6 +78,7 @@ ShaderState::ShaderState(GLenum shaderType)
: mLabel(),
mShaderType(shaderType),
mShaderVersion(100),
mNumViews(-1),
mCompileStatus(CompileStatus::NOT_COMPILED)
{
mLocalSize.fill(-1);
......@@ -272,6 +273,7 @@ void Shader::compile(const Context *context)
mState.mInterfaceBlocks.clear();
mState.mActiveAttributes.clear();
mState.mActiveOutputVariables.clear();
mState.mNumViews = -1;
mState.mCompileStatus = CompileStatus::COMPILE_REQUESTED;
mBoundCompiler.set(context, context->getCompiler());
......@@ -370,7 +372,11 @@ void Shader::resolveCompile(const Context *context)
}
case GL_VERTEX_SHADER:
{
mState.mActiveAttributes = GetActiveShaderVariables(sh::GetAttributes(compilerHandle));
{
mState.mActiveAttributes =
GetActiveShaderVariables(sh::GetAttributes(compilerHandle));
mState.mNumViews = sh::GetVertexShaderNumViews(compilerHandle);
}
break;
}
case GL_FRAGMENT_SHADER:
......@@ -469,6 +475,12 @@ const sh::WorkGroupSize &Shader::getWorkGroupSize(const Context *context)
return mState.mLocalSize;
}
int Shader::getNumViews(const Context *context)
{
resolveCompile(context);
return mState.mNumViews;
}
const std::string &Shader::getCompilerResourcesString() const
{
ASSERT(mBoundCompiler.get());
......
......@@ -91,6 +91,9 @@ class ShaderState final : angle::NonCopyable
std::vector<sh::Attribute> mActiveAttributes;
std::vector<sh::OutputVariable> mActiveOutputVariables;
// ANGLE_multiview.
int mNumViews;
// Indicates if this shader has been successfully compiled
CompileStatus mCompileStatus;
};
......@@ -151,6 +154,8 @@ class Shader final : angle::NonCopyable, public LabeledObject
const sh::WorkGroupSize &getWorkGroupSize(const Context *context);
int getNumViews(const Context *context);
const std::string &getCompilerResourcesString() const;
private:
......
......@@ -2819,6 +2819,17 @@ bool ValidateDrawBase(ValidationContext *context, GLenum mode, GLsizei count)
return false;
}
if (context->getExtensions().multiview)
{
const int programNumViews = program->getNumViews();
if (programNumViews != -1 && framebuffer->getNumViews() != programNumViews)
{
context->handleError(InvalidOperation() << "The number of views in the active program "
"and draw framebuffer does not match.");
return false;
}
}
// Uniform buffer validation
for (unsigned int uniformBlockIndex = 0;
uniformBlockIndex < program->getActiveUniformBlockCount(); uniformBlockIndex++)
......
......@@ -21,6 +21,7 @@ class MultiviewDrawTest : public ANGLETest
setWindowHeight(128);
setWebGLCompatibilityEnabled(true);
}
virtual ~MultiviewDrawTest() {}
void SetUp() override
{
......@@ -49,23 +50,52 @@ class MultiviewDrawTest : public ANGLETest
PFNGLREQUESTEXTENSIONANGLEPROC glRequestExtensionANGLE = nullptr;
};
class MultiviewDrawValidationTest : public MultiviewDrawTest
{
protected:
MultiviewDrawValidationTest() {}
void SetUp() override
{
MultiviewDrawTest::SetUp();
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glBindTexture(GL_TEXTURE_2D, mTex2d);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glBindVertexArray(mVao);
const float kVertexData[3] = {0.0f};
glBindBuffer(GL_ARRAY_BUFFER, mVbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3u, &kVertexData[0], GL_STATIC_DRAW);
const unsigned int kIndices[3] = {0u, 1u, 2u};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIbo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * 3, &kIndices[0],
GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR();
}
GLTexture mTex2d;
GLVertexArray mVao;
GLBuffer mVbo;
GLBuffer mIbo;
GLFramebuffer mFramebuffer;
};
// The test verifies that glDraw*Indirect:
// 1) generates an INVALID_OPERATION error if the number of views in the draw framebuffer is greater
// than 1.
// 2) does not generate any error if the draw framebuffer has exactly 1 view.
TEST_P(MultiviewDrawTest, IndirectDraw)
TEST_P(MultiviewDrawValidationTest, IndirectDraw)
{
if (!requestMultiviewExtension())
{
return;
}
GLTexture tex2d;
glBindTexture(GL_TEXTURE_2D, tex2d);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
const GLint viewportOffsets[4] = {0, 0, 2, 0};
ASSERT_GL_NO_ERROR();
const std::string fsSource =
"#version 300 es\n"
......@@ -74,31 +104,12 @@ TEST_P(MultiviewDrawTest, IndirectDraw)
"void main()\n"
"{}\n";
GLVertexArray vao;
glBindVertexArray(vao);
const float kVertexData[3] = {0.0f};
const unsigned int kIndices[3] = {0u, 1u, 2u};
GLBuffer vbo;
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3u, &kVertexData[0], GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR();
GLBuffer ibo;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * 3, &kIndices[0], GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR();
GLBuffer commandBuffer;
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, commandBuffer);
const GLuint commandData[] = {1u, 1u, 0u, 0u, 0u};
glBufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(GLuint) * 5u, &commandData[0], GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR();
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
// Check for a GL_INVALID_OPERATION error with the framebuffer having 2 views.
{
const std::string &vsSource =
......@@ -110,8 +121,8 @@ TEST_P(MultiviewDrawTest, IndirectDraw)
ANGLE_GL_PROGRAM(program, vsSource, fsSource);
glUseProgram(program);
glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex2d, 0,
2, &viewportOffsets[0]);
glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
0, 2, &viewportOffsets[0]);
glDrawArraysIndirect(GL_TRIANGLES, nullptr);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
......@@ -131,8 +142,8 @@ TEST_P(MultiviewDrawTest, IndirectDraw)
ANGLE_GL_PROGRAM(program, vsSource, fsSource);
glUseProgram(program);
glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex2d, 0,
1, &viewportOffsets[0]);
glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
0, 1, &viewportOffsets[0]);
glDrawArraysIndirect(GL_TRIANGLES, nullptr);
EXPECT_GL_NO_ERROR();
......@@ -142,4 +153,85 @@ TEST_P(MultiviewDrawTest, IndirectDraw)
}
}
ANGLE_INSTANTIATE_TEST(MultiviewDrawTest, ES31_OPENGL());
\ No newline at end of file
// The test verifies that glDraw*:
// 1) generates an INVALID_OPERATION error if the number of views in the active draw framebuffer and
// program differs.
// 2) does not generate any error if the number of views is the same.
// 3) does not generate any error if the program does not use the multiview extension.
TEST_P(MultiviewDrawValidationTest, NumViewsMismatch)
{
if (!requestMultiviewExtension())
{
return;
}
const GLint viewportOffsets[4] = {0, 0, 2, 0};
const std::string &vsSource =
"#version 300 es\n"
"#extension GL_OVR_multiview : require\n"
"layout(num_views = 2) in;\n"
"void main()\n"
"{}\n";
const std::string &fsSource =
"#version 300 es\n"
"#extension GL_OVR_multiview : require\n"
"precision mediump float;\n"
"void main()\n"
"{}\n";
ANGLE_GL_PROGRAM(program, vsSource, fsSource);
glUseProgram(program);
// Check for a GL_INVALID_OPERATION error with the framebuffer and program having different
// number of views.
{
// The framebuffer has only 1 view.
glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
0, 1, &viewportOffsets[0]);
glDrawArrays(GL_TRIANGLES, 0, 3);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Check that no errors are generated if the number of views in both program and draw
// framebuffer matches.
{
glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
0, 2, &viewportOffsets[0]);
glDrawArrays(GL_TRIANGLES, 0, 3);
EXPECT_GL_NO_ERROR();
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr);
EXPECT_GL_NO_ERROR();
}
// Check that no errors are generated if the program does not use the multiview extension.
{
const std::string &vsSourceNoMultiview =
"#version 300 es\n"
"void main()\n"
"{}\n";
const std::string &fsSourceNoMultiview =
"#version 300 es\n"
"precision mediump float;\n"
"void main()\n"
"{}\n";
ANGLE_GL_PROGRAM(programNoMultiview, vsSourceNoMultiview, fsSourceNoMultiview);
glUseProgram(programNoMultiview);
glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
0, 2, &viewportOffsets[0]);
glDrawArrays(GL_TRIANGLES, 0, 3);
EXPECT_GL_NO_ERROR();
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr);
EXPECT_GL_NO_ERROR();
}
}
ANGLE_INSTANTIATE_TEST(MultiviewDrawValidationTest, ES31_OPENGL());
\ No newline at end of file
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