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); ...@@ -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::OutputVariable> *GetOutputVariables(const ShHandle handle);
const std::vector<sh::InterfaceBlock> *GetInterfaceBlocks(const ShHandle handle); const std::vector<sh::InterfaceBlock> *GetInterfaceBlocks(const ShHandle handle);
sh::WorkGroupSize GetComputeShaderLocalGroupSize(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); int GetVertexShaderNumViews(const ShHandle handle);
// Returns true if the passed in variables pack in maxVectors followingthe packing rules from the // 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, ...@@ -168,6 +168,8 @@ LinkResult MemoryProgramCache::Deserialize(const Context *context,
state->mComputeShaderLocalSize[1] = stream.readInt<int>(); state->mComputeShaderLocalSize[1] = stream.readInt<int>();
state->mComputeShaderLocalSize[2] = stream.readInt<int>(); state->mComputeShaderLocalSize[2] = stream.readInt<int>();
state->mNumViews = stream.readInt<int>();
static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8, static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
"Too many vertex attribs for mask"); "Too many vertex attribs for mask");
state->mActiveAttribLocationsMask = stream.readInt<unsigned long>(); state->mActiveAttribLocationsMask = stream.readInt<unsigned long>();
...@@ -359,6 +361,8 @@ void MemoryProgramCache::Serialize(const Context *context, ...@@ -359,6 +361,8 @@ void MemoryProgramCache::Serialize(const Context *context,
stream.writeInt(computeLocalSize[1]); stream.writeInt(computeLocalSize[1]);
stream.writeInt(computeLocalSize[2]); stream.writeInt(computeLocalSize[2]);
stream.writeInt(state.mNumViews);
stream.writeInt(state.getActiveAttribLocationsMask().to_ulong()); stream.writeInt(state.getActiveAttribLocationsMask().to_ulong());
stream.writeInt(state.getAttributes().size()); stream.writeInt(state.getAttributes().size());
......
...@@ -298,7 +298,8 @@ ProgramState::ProgramState() ...@@ -298,7 +298,8 @@ ProgramState::ProgramState()
mSamplerUniformRange(0, 0), mSamplerUniformRange(0, 0),
mImageUniformRange(0, 0), mImageUniformRange(0, 0),
mAtomicCounterUniformRange(0, 0), mAtomicCounterUniformRange(0, 0),
mBinaryRetrieveableHint(false) mBinaryRetrieveableHint(false),
mNumViews(-1)
{ {
mComputeShaderLocalSize.fill(1); mComputeShaderLocalSize.fill(1);
} }
...@@ -739,6 +740,8 @@ Error Program::link(const gl::Context *context) ...@@ -739,6 +740,8 @@ Error Program::link(const gl::Context *context)
return NoError(); return NoError();
} }
mState.mNumViews = vertexShader->getNumViews(context);
linkOutputVariables(context); linkOutputVariables(context);
// Validate we can pack the varyings. // Validate we can pack the varyings.
...@@ -797,6 +800,7 @@ void Program::unlink() ...@@ -797,6 +800,7 @@ void Program::unlink()
mState.mComputeShaderLocalSize.fill(1); mState.mComputeShaderLocalSize.fill(1);
mState.mSamplerBindings.clear(); mState.mSamplerBindings.clear();
mState.mImageBindings.clear(); mState.mImageBindings.clear();
mState.mNumViews = -1;
mValidated = false; mValidated = false;
......
...@@ -324,6 +324,9 @@ class ProgramState final : angle::NonCopyable ...@@ -324,6 +324,9 @@ class ProgramState final : angle::NonCopyable
bool mBinaryRetrieveableHint; bool mBinaryRetrieveableHint;
bool mSeparable; bool mSeparable;
// ANGLE_multiview.
int mNumViews;
}; };
class Program final : angle::NonCopyable, public LabeledObject class Program final : angle::NonCopyable, public LabeledObject
...@@ -523,6 +526,8 @@ 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 &getUniformLocationBindings() const { return mUniformLocationBindings; }
const Bindings &getFragmentInputBindings() const { return mFragmentInputBindings; } const Bindings &getFragmentInputBindings() const { return mFragmentInputBindings; }
int getNumViews() const { return mState.mNumViews; }
private: private:
~Program(); ~Program();
......
...@@ -78,6 +78,7 @@ ShaderState::ShaderState(GLenum shaderType) ...@@ -78,6 +78,7 @@ ShaderState::ShaderState(GLenum shaderType)
: mLabel(), : mLabel(),
mShaderType(shaderType), mShaderType(shaderType),
mShaderVersion(100), mShaderVersion(100),
mNumViews(-1),
mCompileStatus(CompileStatus::NOT_COMPILED) mCompileStatus(CompileStatus::NOT_COMPILED)
{ {
mLocalSize.fill(-1); mLocalSize.fill(-1);
...@@ -272,6 +273,7 @@ void Shader::compile(const Context *context) ...@@ -272,6 +273,7 @@ void Shader::compile(const Context *context)
mState.mInterfaceBlocks.clear(); mState.mInterfaceBlocks.clear();
mState.mActiveAttributes.clear(); mState.mActiveAttributes.clear();
mState.mActiveOutputVariables.clear(); mState.mActiveOutputVariables.clear();
mState.mNumViews = -1;
mState.mCompileStatus = CompileStatus::COMPILE_REQUESTED; mState.mCompileStatus = CompileStatus::COMPILE_REQUESTED;
mBoundCompiler.set(context, context->getCompiler()); mBoundCompiler.set(context, context->getCompiler());
...@@ -370,7 +372,11 @@ void Shader::resolveCompile(const Context *context) ...@@ -370,7 +372,11 @@ void Shader::resolveCompile(const Context *context)
} }
case GL_VERTEX_SHADER: case GL_VERTEX_SHADER:
{ {
mState.mActiveAttributes = GetActiveShaderVariables(sh::GetAttributes(compilerHandle)); {
mState.mActiveAttributes =
GetActiveShaderVariables(sh::GetAttributes(compilerHandle));
mState.mNumViews = sh::GetVertexShaderNumViews(compilerHandle);
}
break; break;
} }
case GL_FRAGMENT_SHADER: case GL_FRAGMENT_SHADER:
...@@ -469,6 +475,12 @@ const sh::WorkGroupSize &Shader::getWorkGroupSize(const Context *context) ...@@ -469,6 +475,12 @@ const sh::WorkGroupSize &Shader::getWorkGroupSize(const Context *context)
return mState.mLocalSize; return mState.mLocalSize;
} }
int Shader::getNumViews(const Context *context)
{
resolveCompile(context);
return mState.mNumViews;
}
const std::string &Shader::getCompilerResourcesString() const const std::string &Shader::getCompilerResourcesString() const
{ {
ASSERT(mBoundCompiler.get()); ASSERT(mBoundCompiler.get());
......
...@@ -91,6 +91,9 @@ class ShaderState final : angle::NonCopyable ...@@ -91,6 +91,9 @@ class ShaderState final : angle::NonCopyable
std::vector<sh::Attribute> mActiveAttributes; std::vector<sh::Attribute> mActiveAttributes;
std::vector<sh::OutputVariable> mActiveOutputVariables; std::vector<sh::OutputVariable> mActiveOutputVariables;
// ANGLE_multiview.
int mNumViews;
// Indicates if this shader has been successfully compiled // Indicates if this shader has been successfully compiled
CompileStatus mCompileStatus; CompileStatus mCompileStatus;
}; };
...@@ -151,6 +154,8 @@ class Shader final : angle::NonCopyable, public LabeledObject ...@@ -151,6 +154,8 @@ class Shader final : angle::NonCopyable, public LabeledObject
const sh::WorkGroupSize &getWorkGroupSize(const Context *context); const sh::WorkGroupSize &getWorkGroupSize(const Context *context);
int getNumViews(const Context *context);
const std::string &getCompilerResourcesString() const; const std::string &getCompilerResourcesString() const;
private: private:
......
...@@ -2819,6 +2819,17 @@ bool ValidateDrawBase(ValidationContext *context, GLenum mode, GLsizei count) ...@@ -2819,6 +2819,17 @@ bool ValidateDrawBase(ValidationContext *context, GLenum mode, GLsizei count)
return false; 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 // Uniform buffer validation
for (unsigned int uniformBlockIndex = 0; for (unsigned int uniformBlockIndex = 0;
uniformBlockIndex < program->getActiveUniformBlockCount(); uniformBlockIndex++) uniformBlockIndex < program->getActiveUniformBlockCount(); uniformBlockIndex++)
......
...@@ -21,6 +21,7 @@ class MultiviewDrawTest : public ANGLETest ...@@ -21,6 +21,7 @@ class MultiviewDrawTest : public ANGLETest
setWindowHeight(128); setWindowHeight(128);
setWebGLCompatibilityEnabled(true); setWebGLCompatibilityEnabled(true);
} }
virtual ~MultiviewDrawTest() {}
void SetUp() override void SetUp() override
{ {
...@@ -49,23 +50,52 @@ class MultiviewDrawTest : public ANGLETest ...@@ -49,23 +50,52 @@ class MultiviewDrawTest : public ANGLETest
PFNGLREQUESTEXTENSIONANGLEPROC glRequestExtensionANGLE = nullptr; 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: // The test verifies that glDraw*Indirect:
// 1) generates an INVALID_OPERATION error if the number of views in the draw framebuffer is greater // 1) generates an INVALID_OPERATION error if the number of views in the draw framebuffer is greater
// than 1. // than 1.
// 2) does not generate any error if the draw framebuffer has exactly 1 view. // 2) does not generate any error if the draw framebuffer has exactly 1 view.
TEST_P(MultiviewDrawTest, IndirectDraw) TEST_P(MultiviewDrawValidationTest, IndirectDraw)
{ {
if (!requestMultiviewExtension()) if (!requestMultiviewExtension())
{ {
return; 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}; const GLint viewportOffsets[4] = {0, 0, 2, 0};
ASSERT_GL_NO_ERROR();
const std::string fsSource = const std::string fsSource =
"#version 300 es\n" "#version 300 es\n"
...@@ -74,31 +104,12 @@ TEST_P(MultiviewDrawTest, IndirectDraw) ...@@ -74,31 +104,12 @@ TEST_P(MultiviewDrawTest, IndirectDraw)
"void main()\n" "void main()\n"
"{}\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; GLBuffer commandBuffer;
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, commandBuffer); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, commandBuffer);
const GLuint commandData[] = {1u, 1u, 0u, 0u, 0u}; const GLuint commandData[] = {1u, 1u, 0u, 0u, 0u};
glBufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(GLuint) * 5u, &commandData[0], GL_STATIC_DRAW); glBufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(GLuint) * 5u, &commandData[0], GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
// Check for a GL_INVALID_OPERATION error with the framebuffer having 2 views. // Check for a GL_INVALID_OPERATION error with the framebuffer having 2 views.
{ {
const std::string &vsSource = const std::string &vsSource =
...@@ -110,8 +121,8 @@ TEST_P(MultiviewDrawTest, IndirectDraw) ...@@ -110,8 +121,8 @@ TEST_P(MultiviewDrawTest, IndirectDraw)
ANGLE_GL_PROGRAM(program, vsSource, fsSource); ANGLE_GL_PROGRAM(program, vsSource, fsSource);
glUseProgram(program); glUseProgram(program);
glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex2d, 0, glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
2, &viewportOffsets[0]); 0, 2, &viewportOffsets[0]);
glDrawArraysIndirect(GL_TRIANGLES, nullptr); glDrawArraysIndirect(GL_TRIANGLES, nullptr);
EXPECT_GL_ERROR(GL_INVALID_OPERATION); EXPECT_GL_ERROR(GL_INVALID_OPERATION);
...@@ -131,8 +142,8 @@ TEST_P(MultiviewDrawTest, IndirectDraw) ...@@ -131,8 +142,8 @@ TEST_P(MultiviewDrawTest, IndirectDraw)
ANGLE_GL_PROGRAM(program, vsSource, fsSource); ANGLE_GL_PROGRAM(program, vsSource, fsSource);
glUseProgram(program); glUseProgram(program);
glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex2d, 0, glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d,
1, &viewportOffsets[0]); 0, 1, &viewportOffsets[0]);
glDrawArraysIndirect(GL_TRIANGLES, nullptr); glDrawArraysIndirect(GL_TRIANGLES, nullptr);
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
...@@ -142,4 +153,85 @@ TEST_P(MultiviewDrawTest, IndirectDraw) ...@@ -142,4 +153,85 @@ TEST_P(MultiviewDrawTest, IndirectDraw)
} }
} }
ANGLE_INSTANTIATE_TEST(MultiviewDrawTest, ES31_OPENGL()); // The test verifies that glDraw*:
\ No newline at end of file // 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