Commit b0761934 by Martin Radev Committed by Commit Bot

Handle Clear* commands for side-by-side framebuffers

Clear* commands for side-by-side framebuffers require special handling because only the scissor rectangle of the first viewport is used in the scissor test as defined in the OpenGL 4.1+ specs. To enable clearing of each view of a side-by-side framebuffer all views are iterated over, the corresponding scissor rectangle is set as first, and a Clear* call is made to the driver. Afterwards the scissor state is restored. BUG=angleproject:2062 TEST=angle_end2end_tests Change-Id: I138663ea61b4f0c9302872108e7dfbadf451b3ec Reviewed-on: https://chromium-review.googlesource.com/590233 Commit-Queue: Martin Radev <mradev@nvidia.com> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent 44183cce
...@@ -103,6 +103,12 @@ void RetrieveMultiviewFieldsFromAttachment(const gl::FramebufferAttachment *atta ...@@ -103,6 +103,12 @@ void RetrieveMultiviewFieldsFromAttachment(const gl::FramebufferAttachment *atta
} }
} }
bool RequiresMultipleClears(const FramebufferAttachment *attachment)
{
return attachment != nullptr &&
attachment->getMultiviewLayout() == GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE;
}
} // namespace } // namespace
FramebufferGL::FramebufferGL(const FramebufferState &state, FramebufferGL::FramebufferGL(const FramebufferState &state,
...@@ -213,7 +219,16 @@ Error FramebufferGL::clear(const gl::Context *context, GLbitfield mask) ...@@ -213,7 +219,16 @@ Error FramebufferGL::clear(const gl::Context *context, GLbitfield mask)
{ {
syncClearState(context, mask); syncClearState(context, mask);
mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
mFunctions->clear(mask);
if (RequiresMultipleClears(mState.getFirstNonNullAttachment()))
{
genericSideBySideClear(context, ClearCommandType::Clear, mask, GL_NONE, 0, nullptr, 0.0f,
0);
}
else
{
mFunctions->clear(mask);
}
return gl::NoError(); return gl::NoError();
} }
...@@ -225,7 +240,17 @@ Error FramebufferGL::clearBufferfv(const gl::Context *context, ...@@ -225,7 +240,17 @@ Error FramebufferGL::clearBufferfv(const gl::Context *context,
{ {
syncClearBufferState(context, buffer, drawbuffer); syncClearBufferState(context, buffer, drawbuffer);
mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
mFunctions->clearBufferfv(buffer, drawbuffer, values);
if (RequiresMultipleClears(mState.getFirstNonNullAttachment()))
{
genericSideBySideClear(context, ClearCommandType::ClearBufferfv,
static_cast<GLbitfield>(0u), buffer, drawbuffer,
reinterpret_cast<const uint8_t *>(values), 0.0f, 0);
}
else
{
mFunctions->clearBufferfv(buffer, drawbuffer, values);
}
return gl::NoError(); return gl::NoError();
} }
...@@ -237,7 +262,17 @@ Error FramebufferGL::clearBufferuiv(const gl::Context *context, ...@@ -237,7 +262,17 @@ Error FramebufferGL::clearBufferuiv(const gl::Context *context,
{ {
syncClearBufferState(context, buffer, drawbuffer); syncClearBufferState(context, buffer, drawbuffer);
mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
mFunctions->clearBufferuiv(buffer, drawbuffer, values);
if (RequiresMultipleClears(mState.getFirstNonNullAttachment()))
{
genericSideBySideClear(context, ClearCommandType::ClearBufferuiv,
static_cast<GLbitfield>(0u), buffer, drawbuffer,
reinterpret_cast<const uint8_t *>(values), 0.0f, 0);
}
else
{
mFunctions->clearBufferuiv(buffer, drawbuffer, values);
}
return gl::NoError(); return gl::NoError();
} }
...@@ -249,7 +284,17 @@ Error FramebufferGL::clearBufferiv(const gl::Context *context, ...@@ -249,7 +284,17 @@ Error FramebufferGL::clearBufferiv(const gl::Context *context,
{ {
syncClearBufferState(context, buffer, drawbuffer); syncClearBufferState(context, buffer, drawbuffer);
mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
mFunctions->clearBufferiv(buffer, drawbuffer, values);
if (RequiresMultipleClears(mState.getFirstNonNullAttachment()))
{
genericSideBySideClear(context, ClearCommandType::ClearBufferiv,
static_cast<GLbitfield>(0u), buffer, drawbuffer,
reinterpret_cast<const uint8_t *>(values), 0.0f, 0);
}
else
{
mFunctions->clearBufferiv(buffer, drawbuffer, values);
}
return gl::NoError(); return gl::NoError();
} }
...@@ -262,11 +307,70 @@ Error FramebufferGL::clearBufferfi(const gl::Context *context, ...@@ -262,11 +307,70 @@ Error FramebufferGL::clearBufferfi(const gl::Context *context,
{ {
syncClearBufferState(context, buffer, drawbuffer); syncClearBufferState(context, buffer, drawbuffer);
mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
mFunctions->clearBufferfi(buffer, drawbuffer, depth, stencil);
if (RequiresMultipleClears(mState.getFirstNonNullAttachment()))
{
genericSideBySideClear(context, ClearCommandType::ClearBufferfi,
static_cast<GLbitfield>(0u), buffer, drawbuffer, nullptr, depth,
stencil);
}
else
{
mFunctions->clearBufferfi(buffer, drawbuffer, depth, stencil);
}
return gl::NoError(); return gl::NoError();
} }
void FramebufferGL::genericSideBySideClear(const gl::Context *context,
ClearCommandType clearCommandType,
GLbitfield mask,
GLenum buffer,
GLint drawbuffer,
const uint8_t *values,
GLfloat depth,
GLint stencil)
{
// For side-by-side framebuffers we have to go over each view and set its scissor rectangle
// as the first one and just then call Clear*. This is necessary because Clear* commands use
// only the first viewport's scissor rectangle.
const auto &scissorBase = context->getGLState().getScissor();
const FramebufferAttachment *attachment = mState.getFirstNonNullAttachment();
ASSERT(attachment != nullptr);
const auto &viewportOffsets = attachment->getMultiviewViewportOffsets();
for (size_t i = 0u; i < viewportOffsets.size(); ++i)
{
gl::Rectangle scissor(scissorBase.x + viewportOffsets[i].x,
scissorBase.y + viewportOffsets[i].y, scissorBase.width,
scissorBase.height);
mStateManager->setScissorIndexed(0u, scissor);
switch (clearCommandType)
{
case ClearCommandType::Clear:
mFunctions->clear(mask);
break;
case ClearCommandType::ClearBufferfv:
mFunctions->clearBufferfv(buffer, drawbuffer,
reinterpret_cast<const GLfloat *>(values));
break;
case ClearCommandType::ClearBufferuiv:
mFunctions->clearBufferuiv(buffer, drawbuffer,
reinterpret_cast<const GLuint *>(values));
break;
case ClearCommandType::ClearBufferiv:
mFunctions->clearBufferiv(buffer, drawbuffer,
reinterpret_cast<const GLint *>(values));
break;
case ClearCommandType::ClearBufferfi:
mFunctions->clearBufferfi(buffer, drawbuffer, depth, stencil);
break;
default:
UNREACHABLE();
}
}
}
GLenum FramebufferGL::getImplementationColorReadFormat(const gl::Context *context) const GLenum FramebufferGL::getImplementationColorReadFormat(const gl::Context *context) const
{ {
const auto *readAttachment = mState.getReadAttachment(); const auto *readAttachment = mState.getReadAttachment();
......
...@@ -95,6 +95,17 @@ class FramebufferGL : public FramebufferImpl ...@@ -95,6 +95,17 @@ class FramebufferGL : public FramebufferImpl
void maskOutInactiveOutputDrawBuffers(gl::DrawBufferMask maxSet); void maskOutInactiveOutputDrawBuffers(gl::DrawBufferMask maxSet);
private: private:
// Enum containing the different types of Clear* commands.
enum class ClearCommandType
{
Clear,
ClearBufferfv,
ClearBufferuiv,
ClearBufferiv,
ClearBufferfi
};
private:
void syncClearState(const gl::Context *context, GLbitfield mask); void syncClearState(const gl::Context *context, GLbitfield mask);
void syncClearBufferState(const gl::Context *context, GLenum buffer, GLint drawBuffer); void syncClearBufferState(const gl::Context *context, GLenum buffer, GLint drawBuffer);
...@@ -118,6 +129,15 @@ class FramebufferGL : public FramebufferImpl ...@@ -118,6 +129,15 @@ class FramebufferGL : public FramebufferImpl
GLubyte *pixels, GLubyte *pixels,
bool readLastRowSeparately) const; bool readLastRowSeparately) const;
void genericSideBySideClear(const gl::Context *context,
ClearCommandType clearCommandType,
GLbitfield mask,
GLenum buffer,
GLint drawbuffer,
const uint8_t *values,
GLfloat depth,
GLint stencil);
const FunctionsGL *mFunctions; const FunctionsGL *mFunctions;
StateManagerGL *mStateManager; StateManagerGL *mStateManager;
const WorkaroundsGL &mWorkarounds; const WorkaroundsGL &mWorkarounds;
......
...@@ -1069,6 +1069,18 @@ void StateManagerGL::setScissorArrayv(GLuint first, const std::vector<gl::Rectan ...@@ -1069,6 +1069,18 @@ void StateManagerGL::setScissorArrayv(GLuint first, const std::vector<gl::Rectan
} }
} }
void StateManagerGL::setScissorIndexed(GLuint index, const gl::Rectangle &scissor)
{
ASSERT(mFunctions->scissorIndexed != nullptr);
ASSERT(static_cast<size_t>(index) < mScissors.size());
if (mScissors[index] != scissor)
{
mScissors[index] = scissor;
mFunctions->scissorIndexed(index, scissor.x, scissor.y, scissor.width, scissor.height);
mLocalDirtyBits.set(gl::State::DIRTY_BIT_SCISSOR);
}
}
void StateManagerGL::setViewport(const gl::Rectangle &viewport) void StateManagerGL::setViewport(const gl::Rectangle &viewport)
{ {
if (!AllRectanglesMatch(viewport, mViewports)) if (!AllRectanglesMatch(viewport, mViewports))
......
...@@ -76,6 +76,7 @@ class StateManagerGL final : angle::NonCopyable ...@@ -76,6 +76,7 @@ class StateManagerGL final : angle::NonCopyable
void setScissorTestEnabled(bool enabled); void setScissorTestEnabled(bool enabled);
void setScissor(const gl::Rectangle &scissor); void setScissor(const gl::Rectangle &scissor);
void setScissorIndexed(GLuint index, const gl::Rectangle &scissor);
void setScissorArrayv(GLuint first, const std::vector<gl::Rectangle> &viewports); void setScissorArrayv(GLuint first, const std::vector<gl::Rectangle> &viewports);
void setViewport(const gl::Rectangle &viewport); void setViewport(const gl::Rectangle &viewport);
......
...@@ -14,15 +14,24 @@ using namespace angle; ...@@ -14,15 +14,24 @@ using namespace angle;
namespace namespace
{ {
GLuint CreateTexture2D(GLenum internalFormat, GLenum format, GLenum type) GLuint CreateTexture2D(GLenum internalFormat,
GLenum format,
GLenum type,
GLsizei width,
GLsizei height)
{ {
GLuint tex; GLuint tex;
glGenTextures(1, &tex); glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex); glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, 1, 1, 0, format, type, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, nullptr);
return tex; return tex;
} }
GLuint CreateTexture2D(GLenum internalFormat, GLenum format, GLenum type)
{
return CreateTexture2D(internalFormat, format, type, 1, 1);
}
} // namespace } // namespace
class FramebufferMultiviewTest : public ANGLETest class FramebufferMultiviewTest : public ANGLETest
...@@ -481,4 +490,56 @@ TEST_P(FramebufferMultiviewTest, InvalidReadPixels) ...@@ -481,4 +490,56 @@ TEST_P(FramebufferMultiviewTest, InvalidReadPixels)
EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION); EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
} }
// Test that glClear clears only the contents of each view.
TEST_P(FramebufferMultiviewTest, SideBySideClear)
{
if (!requestMultiviewExtension())
{
return;
}
mTexture2D = CreateTexture2D(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, 4, 2);
const GLint viewportOffsets[4] = {1, 0, 3, 0};
glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTexture2D,
0, 2, &viewportOffsets[0]);
// Create and bind a normal framebuffer to access the 2D texture.
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture2D, 0);
// Clear the contents of the texture.
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
// Bind and specify viewport/scissor dimensions for each view.
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glViewport(0, 0, 1, 2);
glScissor(0, 0, 1, 2);
glClearColor(1, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// column 0
EXPECT_PIXEL_EQ(0, 0, 0, 0, 0, 0);
EXPECT_PIXEL_EQ(0, 1, 0, 0, 0, 0);
// column 1
EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
EXPECT_PIXEL_EQ(1, 1, 255, 0, 0, 0);
// column 2
EXPECT_PIXEL_EQ(2, 0, 0, 0, 0, 0);
EXPECT_PIXEL_EQ(2, 1, 0, 0, 0, 0);
// column 3
EXPECT_PIXEL_EQ(3, 0, 255, 0, 0, 0);
EXPECT_PIXEL_EQ(3, 1, 255, 0, 0, 0);
glDeleteFramebuffers(1, &fbo);
}
ANGLE_INSTANTIATE_TEST(FramebufferMultiviewTest, ES3_OPENGL()); ANGLE_INSTANTIATE_TEST(FramebufferMultiviewTest, ES3_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