Commit 60ec6ea7 by Jamie Madill

Implement dirty bits for Framebuffer.

The dirty bits set the stage for performance improvements in D3D, but don't actually reduce any of the redundant work just yet. BUG=angleproject:1260 Change-Id: Ib84e6a9b7aa40c37c41790f492361b22faaf4742 Reviewed-on: https://chromium-review.googlesource.com/318730Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Tryjob-Request: Jamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org> Tested-by: 's avatarJamie Madill <jmadill@chromium.org>
parent b8d28399
...@@ -42,6 +42,13 @@ struct Optional ...@@ -42,6 +42,13 @@ struct Optional
return *this; return *this;
} }
Optional &operator=(T &&value)
{
mValue = std::move(value);
mValid = true;
return *this;
}
void reset() void reset()
{ {
mValid = false; mValid = false;
......
...@@ -2065,6 +2065,9 @@ void Context::copyTexImage2D(GLenum target, ...@@ -2065,6 +2065,9 @@ void Context::copyTexImage2D(GLenum target,
GLsizei height, GLsizei height,
GLint border) GLint border)
{ {
// Only sync the read FBO
mState.syncDirtyObject(GL_READ_FRAMEBUFFER);
Rectangle sourceArea(x, y, width, height); Rectangle sourceArea(x, y, width, height);
const Framebuffer *framebuffer = mState.getReadFramebuffer(); const Framebuffer *framebuffer = mState.getReadFramebuffer();
...@@ -2086,6 +2089,9 @@ void Context::copyTexSubImage2D(GLenum target, ...@@ -2086,6 +2089,9 @@ void Context::copyTexSubImage2D(GLenum target,
GLsizei width, GLsizei width,
GLsizei height) GLsizei height)
{ {
// Only sync the read FBO
mState.syncDirtyObject(GL_READ_FRAMEBUFFER);
Offset destOffset(xoffset, yoffset, 0); Offset destOffset(xoffset, yoffset, 0);
Rectangle sourceArea(x, y, width, height); Rectangle sourceArea(x, y, width, height);
...@@ -2109,6 +2115,9 @@ void Context::copyTexSubImage3D(GLenum target, ...@@ -2109,6 +2115,9 @@ void Context::copyTexSubImage3D(GLenum target,
GLsizei width, GLsizei width,
GLsizei height) GLsizei height)
{ {
// Only sync the read FBO
mState.syncDirtyObject(GL_READ_FRAMEBUFFER);
Offset destOffset(xoffset, yoffset, zoffset); Offset destOffset(xoffset, yoffset, zoffset);
Rectangle sourceArea(x, y, width, height); Rectangle sourceArea(x, y, width, height);
...@@ -2152,6 +2161,8 @@ void Context::framebufferTexture2D(GLenum target, ...@@ -2152,6 +2161,8 @@ void Context::framebufferTexture2D(GLenum target,
{ {
framebuffer->resetAttachment(attachment); framebuffer->resetAttachment(attachment);
} }
mState.setObjectDirty(target);
} }
void Context::framebufferRenderbuffer(GLenum target, void Context::framebufferRenderbuffer(GLenum target,
...@@ -2172,6 +2183,8 @@ void Context::framebufferRenderbuffer(GLenum target, ...@@ -2172,6 +2183,8 @@ void Context::framebufferRenderbuffer(GLenum target,
{ {
framebuffer->resetAttachment(attachment); framebuffer->resetAttachment(attachment);
} }
mState.setObjectDirty(target);
} }
void Context::framebufferTextureLayer(GLenum target, void Context::framebufferTextureLayer(GLenum target,
...@@ -2205,6 +2218,8 @@ void Context::framebufferTextureLayer(GLenum target, ...@@ -2205,6 +2218,8 @@ void Context::framebufferTextureLayer(GLenum target,
{ {
framebuffer->resetAttachment(attachment); framebuffer->resetAttachment(attachment);
} }
mState.setObjectDirty(target);
} }
void Context::drawBuffers(GLsizei n, const GLenum *bufs) void Context::drawBuffers(GLsizei n, const GLenum *bufs)
...@@ -2212,16 +2227,21 @@ void Context::drawBuffers(GLsizei n, const GLenum *bufs) ...@@ -2212,16 +2227,21 @@ void Context::drawBuffers(GLsizei n, const GLenum *bufs)
Framebuffer *framebuffer = mState.getDrawFramebuffer(); Framebuffer *framebuffer = mState.getDrawFramebuffer();
ASSERT(framebuffer); ASSERT(framebuffer);
framebuffer->setDrawBuffers(n, bufs); framebuffer->setDrawBuffers(n, bufs);
mState.setObjectDirty(GL_DRAW_FRAMEBUFFER);
} }
void Context::readBuffer(GLenum mode) void Context::readBuffer(GLenum mode)
{ {
Framebuffer *readFBO = mState.getReadFramebuffer(); Framebuffer *readFBO = mState.getReadFramebuffer();
readFBO->setReadBuffer(mode); readFBO->setReadBuffer(mode);
mState.setObjectDirty(GL_READ_FRAMEBUFFER);
} }
void Context::discardFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments) void Context::discardFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments)
{ {
// Only sync the FBO
mState.syncDirtyObject(target);
Framebuffer *framebuffer = mState.getTargetFramebuffer(target); Framebuffer *framebuffer = mState.getTargetFramebuffer(target);
ASSERT(framebuffer); ASSERT(framebuffer);
...@@ -2238,6 +2258,9 @@ void Context::invalidateFramebuffer(GLenum target, ...@@ -2238,6 +2258,9 @@ void Context::invalidateFramebuffer(GLenum target,
GLsizei numAttachments, GLsizei numAttachments,
const GLenum *attachments) const GLenum *attachments)
{ {
// Only sync the FBO
mState.syncDirtyObject(target);
Framebuffer *framebuffer = mState.getTargetFramebuffer(target); Framebuffer *framebuffer = mState.getTargetFramebuffer(target);
ASSERT(framebuffer); ASSERT(framebuffer);
...@@ -2260,6 +2283,9 @@ void Context::invalidateSubFramebuffer(GLenum target, ...@@ -2260,6 +2283,9 @@ void Context::invalidateSubFramebuffer(GLenum target,
GLsizei width, GLsizei width,
GLsizei height) GLsizei height)
{ {
// Only sync the FBO
mState.syncDirtyObject(target);
Framebuffer *framebuffer = mState.getTargetFramebuffer(target); Framebuffer *framebuffer = mState.getTargetFramebuffer(target);
ASSERT(framebuffer); ASSERT(framebuffer);
......
...@@ -300,7 +300,7 @@ void Framebuffer::setDrawBuffers(size_t count, const GLenum *buffers) ...@@ -300,7 +300,7 @@ void Framebuffer::setDrawBuffers(size_t count, const GLenum *buffers)
ASSERT(count <= drawStates.size()); ASSERT(count <= drawStates.size());
std::copy(buffers, buffers + count, drawStates.begin()); std::copy(buffers, buffers + count, drawStates.begin());
std::fill(drawStates.begin() + count, drawStates.end(), GL_NONE); std::fill(drawStates.begin() + count, drawStates.end(), GL_NONE);
mImpl->setDrawBuffers(count, buffers); mDirtyBits.set(DIRTY_BIT_DRAW_BUFFERS);
} }
GLenum Framebuffer::getReadBufferState() const GLenum Framebuffer::getReadBufferState() const
...@@ -314,7 +314,7 @@ void Framebuffer::setReadBuffer(GLenum buffer) ...@@ -314,7 +314,7 @@ void Framebuffer::setReadBuffer(GLenum buffer)
(buffer >= GL_COLOR_ATTACHMENT0 && (buffer >= GL_COLOR_ATTACHMENT0 &&
(buffer - GL_COLOR_ATTACHMENT0) < mData.mColorAttachments.size())); (buffer - GL_COLOR_ATTACHMENT0) < mData.mColorAttachments.size()));
mData.mReadBufferState = buffer; mData.mReadBufferState = buffer;
mImpl->setReadBuffer(buffer); mDirtyBits.set(DIRTY_BIT_READ_BUFFER);
} }
bool Framebuffer::isEnabledColorAttachment(size_t colorAttachment) const bool Framebuffer::isEnabledColorAttachment(size_t colorAttachment) const
...@@ -567,6 +567,7 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const ...@@ -567,6 +567,7 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const
return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
} }
syncState();
if (!mImpl->checkStatus()) if (!mImpl->checkStatus())
{ {
return GL_FRAMEBUFFER_UNSUPPORTED; return GL_FRAMEBUFFER_UNSUPPORTED;
...@@ -740,32 +741,33 @@ void Framebuffer::setAttachment(GLenum type, ...@@ -740,32 +741,33 @@ void Framebuffer::setAttachment(GLenum type,
mData.mDepthAttachment.attach(type, binding, textureIndex, attachmentObj); mData.mDepthAttachment.attach(type, binding, textureIndex, attachmentObj);
mData.mStencilAttachment.attach(type, binding, textureIndex, attachmentObj); mData.mStencilAttachment.attach(type, binding, textureIndex, attachmentObj);
mImpl->onUpdateDepthStencilAttachment(); mDirtyBits.set(DIRTY_BIT_DEPTH_ATTACHMENT);
mDirtyBits.set(DIRTY_BIT_STENCIL_ATTACHMENT);
} }
else else
{ {
switch (binding) switch (binding)
{ {
case GL_DEPTH: case GL_DEPTH:
case GL_DEPTH_ATTACHMENT: case GL_DEPTH_ATTACHMENT:
mData.mDepthAttachment.attach(type, binding, textureIndex, resource); mData.mDepthAttachment.attach(type, binding, textureIndex, resource);
mImpl->onUpdateDepthAttachment(); mDirtyBits.set(DIRTY_BIT_DEPTH_ATTACHMENT);
break; break;
case GL_STENCIL: case GL_STENCIL:
case GL_STENCIL_ATTACHMENT: case GL_STENCIL_ATTACHMENT:
mData.mStencilAttachment.attach(type, binding, textureIndex, resource); mData.mStencilAttachment.attach(type, binding, textureIndex, resource);
mImpl->onUpdateStencilAttachment(); mDirtyBits.set(DIRTY_BIT_STENCIL_ATTACHMENT);
break; break;
case GL_BACK: case GL_BACK:
mData.mColorAttachments[0].attach(type, binding, textureIndex, resource); mData.mColorAttachments[0].attach(type, binding, textureIndex, resource);
mImpl->onUpdateColorAttachment(0); mDirtyBits.set(DIRTY_BIT_COLOR_ATTACHMENT_0);
break; break;
default: default:
{ {
size_t colorIndex = binding - GL_COLOR_ATTACHMENT0; size_t colorIndex = binding - GL_COLOR_ATTACHMENT0;
ASSERT(colorIndex < mData.mColorAttachments.size()); ASSERT(colorIndex < mData.mColorAttachments.size());
mData.mColorAttachments[colorIndex].attach(type, binding, textureIndex, resource); mData.mColorAttachments[colorIndex].attach(type, binding, textureIndex, resource);
mImpl->onUpdateColorAttachment(colorIndex); mDirtyBits.set(DIRTY_BIT_COLOR_ATTACHMENT_0 + colorIndex);
} }
break; break;
} }
...@@ -777,4 +779,13 @@ void Framebuffer::resetAttachment(GLenum binding) ...@@ -777,4 +779,13 @@ void Framebuffer::resetAttachment(GLenum binding)
setAttachment(GL_NONE, binding, ImageIndex::MakeInvalid(), nullptr); setAttachment(GL_NONE, binding, ImageIndex::MakeInvalid(), nullptr);
} }
void Framebuffer::syncState() const
{
if (mDirtyBits.any())
{
mImpl->syncState(mDirtyBits);
mDirtyBits.reset();
}
} }
} // namespace gl
...@@ -67,6 +67,7 @@ class Framebuffer final : public LabeledObject ...@@ -67,6 +67,7 @@ class Framebuffer final : public LabeledObject
const FramebufferAttachment *getDepthStencilAttachment() const; const FramebufferAttachment *getDepthStencilAttachment() const;
const std::vector<GLenum> &getDrawBufferStates() const { return mDrawBufferStates; } const std::vector<GLenum> &getDrawBufferStates() const { return mDrawBufferStates; }
GLenum getReadBufferState() const { return mReadBufferState; }
const std::vector<FramebufferAttachment> &getColorAttachments() const { return mColorAttachments; } const std::vector<FramebufferAttachment> &getColorAttachments() const { return mColorAttachments; }
bool attachmentsHaveSameDimensions() const; bool attachmentsHaveSameDimensions() const;
...@@ -168,12 +169,33 @@ class Framebuffer final : public LabeledObject ...@@ -168,12 +169,33 @@ class Framebuffer final : public LabeledObject
GLenum filter, GLenum filter,
const Framebuffer *sourceFramebuffer); const Framebuffer *sourceFramebuffer);
enum DirtyBitType
{
DIRTY_BIT_COLOR_ATTACHMENT_0,
DIRTY_BIT_COLOR_ATTACHMENT_MAX =
DIRTY_BIT_COLOR_ATTACHMENT_0 + gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS,
DIRTY_BIT_DEPTH_ATTACHMENT = DIRTY_BIT_COLOR_ATTACHMENT_MAX,
DIRTY_BIT_STENCIL_ATTACHMENT,
DIRTY_BIT_DRAW_BUFFERS,
DIRTY_BIT_READ_BUFFER,
DIRTY_BIT_UNKNOWN,
DIRTY_BIT_MAX = DIRTY_BIT_UNKNOWN,
};
typedef std::bitset<DIRTY_BIT_MAX> DirtyBits;
bool hasAnyDirtyBit() const { return mDirtyBits.any(); }
void syncState() const;
protected: protected:
void detachResourceById(GLenum resourceType, GLuint resourceId); void detachResourceById(GLenum resourceType, GLuint resourceId);
Data mData; Data mData;
rx::FramebufferImpl *mImpl; rx::FramebufferImpl *mImpl;
GLuint mId; GLuint mId;
// TODO(jmadill): See if we can make this non-mutable.
mutable DirtyBits mDirtyBits;
}; };
} }
......
...@@ -835,22 +835,44 @@ void State::detachRenderbuffer(GLuint renderbuffer) ...@@ -835,22 +835,44 @@ void State::detachRenderbuffer(GLuint renderbuffer)
void State::setReadFramebufferBinding(Framebuffer *framebuffer) void State::setReadFramebufferBinding(Framebuffer *framebuffer)
{ {
if (mReadFramebuffer == framebuffer)
return;
mReadFramebuffer = framebuffer; mReadFramebuffer = framebuffer;
mDirtyBits.set(DIRTY_BIT_READ_FRAMEBUFFER_BINDING);
if (mReadFramebuffer && mReadFramebuffer->hasAnyDirtyBit())
{
mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER);
}
} }
void State::setDrawFramebufferBinding(Framebuffer *framebuffer) void State::setDrawFramebufferBinding(Framebuffer *framebuffer)
{ {
if (mDrawFramebuffer == framebuffer)
return;
mDrawFramebuffer = framebuffer; mDrawFramebuffer = framebuffer;
mDirtyBits.set(DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING);
if (mDrawFramebuffer && mDrawFramebuffer->hasAnyDirtyBit())
{
mDirtyObjects.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER);
}
} }
Framebuffer *State::getTargetFramebuffer(GLenum target) const Framebuffer *State::getTargetFramebuffer(GLenum target) const
{ {
switch (target) switch (target)
{ {
case GL_READ_FRAMEBUFFER_ANGLE: return mReadFramebuffer; case GL_READ_FRAMEBUFFER_ANGLE:
case GL_DRAW_FRAMEBUFFER_ANGLE: return mReadFramebuffer;
case GL_FRAMEBUFFER: return mDrawFramebuffer; case GL_DRAW_FRAMEBUFFER_ANGLE:
default: UNREACHABLE(); return NULL; case GL_FRAMEBUFFER:
return mDrawFramebuffer;
default:
UNREACHABLE();
return NULL;
} }
} }
...@@ -879,7 +901,7 @@ bool State::removeReadFramebufferBinding(GLuint framebuffer) ...@@ -879,7 +901,7 @@ bool State::removeReadFramebufferBinding(GLuint framebuffer)
if (mReadFramebuffer != nullptr && if (mReadFramebuffer != nullptr &&
mReadFramebuffer->id() == framebuffer) mReadFramebuffer->id() == framebuffer)
{ {
mReadFramebuffer = NULL; setReadFramebufferBinding(nullptr);
return true; return true;
} }
...@@ -891,7 +913,7 @@ bool State::removeDrawFramebufferBinding(GLuint framebuffer) ...@@ -891,7 +913,7 @@ bool State::removeDrawFramebufferBinding(GLuint framebuffer)
if (mReadFramebuffer != nullptr && if (mReadFramebuffer != nullptr &&
mDrawFramebuffer->id() == framebuffer) mDrawFramebuffer->id() == framebuffer)
{ {
mDrawFramebuffer = NULL; setDrawFramebufferBinding(nullptr);
return true; return true;
} }
...@@ -1720,17 +1742,25 @@ void State::syncDirtyObjects() ...@@ -1720,17 +1742,25 @@ void State::syncDirtyObjects()
if (!mDirtyObjects.any()) if (!mDirtyObjects.any())
return; return;
for (auto dirtyObject : angle::IterateBitSet(mDirtyObjects)) syncDirtyObjects(mDirtyObjects);
}
void State::syncDirtyObjects(const DirtyObjects &bitset)
{
for (auto dirtyObject : angle::IterateBitSet(bitset))
{ {
switch (dirtyObject) switch (dirtyObject)
{ {
case DIRTY_OBJECT_READ_FRAMEBUFFER: case DIRTY_OBJECT_READ_FRAMEBUFFER:
// TODO(jmadill): implement this ASSERT(mReadFramebuffer);
mReadFramebuffer->syncState();
break; break;
case DIRTY_OBJECT_DRAW_FRAMEBUFFER: case DIRTY_OBJECT_DRAW_FRAMEBUFFER:
// TODO(jmadill): implement this ASSERT(mDrawFramebuffer);
mDrawFramebuffer->syncState();
break; break;
case DIRTY_OBJECT_VERTEX_ARRAY: case DIRTY_OBJECT_VERTEX_ARRAY:
ASSERT(mVertexArray);
mVertexArray->syncImplState(); mVertexArray->syncImplState();
break; break;
case DIRTY_OBJECT_PROGRAM: case DIRTY_OBJECT_PROGRAM:
...@@ -1742,7 +1772,57 @@ void State::syncDirtyObjects() ...@@ -1742,7 +1772,57 @@ void State::syncDirtyObjects()
} }
} }
mDirtyObjects.reset(); mDirtyObjects &= ~bitset;
}
void State::syncDirtyObject(GLenum target)
{
DirtyObjects localSet;
switch (target)
{
case GL_READ_FRAMEBUFFER:
localSet.set(DIRTY_OBJECT_READ_FRAMEBUFFER);
break;
case GL_DRAW_FRAMEBUFFER:
localSet.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER);
break;
case GL_FRAMEBUFFER:
localSet.set(DIRTY_OBJECT_READ_FRAMEBUFFER);
localSet.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER);
break;
case GL_VERTEX_ARRAY:
localSet.set(DIRTY_OBJECT_VERTEX_ARRAY);
break;
case GL_PROGRAM:
localSet.set(DIRTY_OBJECT_PROGRAM);
break;
}
syncDirtyObjects(localSet);
}
void State::setObjectDirty(GLenum target)
{
switch (target)
{
case GL_READ_FRAMEBUFFER:
mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER);
break;
case GL_DRAW_FRAMEBUFFER:
mDirtyObjects.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER);
break;
case GL_FRAMEBUFFER:
mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER);
mDirtyObjects.set(DIRTY_OBJECT_DRAW_FRAMEBUFFER);
break;
case GL_VERTEX_ARRAY:
mDirtyObjects.set(DIRTY_OBJECT_VERTEX_ARRAY);
break;
case GL_PROGRAM:
mDirtyObjects.set(DIRTY_OBJECT_PROGRAM);
break;
}
} }
} // namespace gl } // namespace gl
...@@ -359,6 +359,9 @@ class State : angle::NonCopyable ...@@ -359,6 +359,9 @@ class State : angle::NonCopyable
void clearDirtyObjects() { mDirtyObjects.reset(); } void clearDirtyObjects() { mDirtyObjects.reset(); }
void setAllDirtyObjects() { mDirtyObjects.set(); } void setAllDirtyObjects() { mDirtyObjects.set(); }
void syncDirtyObjects(); void syncDirtyObjects();
void syncDirtyObjects(const DirtyObjects &bitset);
void syncDirtyObject(GLenum target);
void setObjectDirty(GLenum target);
// Dirty bit masks // Dirty bit masks
const DirtyBits &unpackStateBitMask() const { return mUnpackStateBitMask; } const DirtyBits &unpackStateBitMask() const { return mUnpackStateBitMask; }
......
...@@ -51,10 +51,6 @@ TEST(SurfaceTest, DestructionDeletesImpl) ...@@ -51,10 +51,6 @@ TEST(SurfaceTest, DestructionDeletesImpl)
EXPECT_CALL(*impl, getSwapBehavior()); EXPECT_CALL(*impl, getSwapBehavior());
EXPECT_CALL(*impl, createDefaultFramebuffer(testing::_)).WillOnce(testing::Return(framebuffer)); EXPECT_CALL(*impl, createDefaultFramebuffer(testing::_)).WillOnce(testing::Return(framebuffer));
EXPECT_CALL(*framebuffer, setDrawBuffers(1, testing::_));
EXPECT_CALL(*framebuffer, setReadBuffer(GL_BACK));
EXPECT_CALL(*framebuffer, onUpdateColorAttachment(0));
egl::Config config; egl::Config config;
egl::Surface *surface = new egl::Surface(impl, EGL_WINDOW_BIT, &config, egl::AttributeMap()); egl::Surface *surface = new egl::Surface(impl, EGL_WINDOW_BIT, &config, egl::AttributeMap());
......
...@@ -31,14 +31,6 @@ class FramebufferImpl : angle::NonCopyable ...@@ -31,14 +31,6 @@ class FramebufferImpl : angle::NonCopyable
explicit FramebufferImpl(const gl::Framebuffer::Data &data) : mData(data) { } explicit FramebufferImpl(const gl::Framebuffer::Data &data) : mData(data) { }
virtual ~FramebufferImpl() { } virtual ~FramebufferImpl() { }
virtual void onUpdateColorAttachment(size_t index) = 0;
virtual void onUpdateDepthAttachment() = 0;
virtual void onUpdateStencilAttachment() = 0;
virtual void onUpdateDepthStencilAttachment() = 0;
virtual void setDrawBuffers(size_t count, const GLenum *buffers) = 0;
virtual void setReadBuffer(GLenum buffer) = 0;
virtual gl::Error discard(size_t count, const GLenum *attachments) = 0; virtual gl::Error discard(size_t count, const GLenum *attachments) = 0;
virtual gl::Error invalidate(size_t count, const GLenum *attachments) = 0; virtual gl::Error invalidate(size_t count, const GLenum *attachments) = 0;
virtual gl::Error invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area) = 0; virtual gl::Error invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area) = 0;
...@@ -71,6 +63,8 @@ class FramebufferImpl : angle::NonCopyable ...@@ -71,6 +63,8 @@ class FramebufferImpl : angle::NonCopyable
virtual bool checkStatus() const = 0; virtual bool checkStatus() const = 0;
virtual void syncState(const gl::Framebuffer::DirtyBits &dirtyBits) = 0;
const gl::Framebuffer::Data &getData() const { return mData; } const gl::Framebuffer::Data &getData() const { return mData; }
protected: protected:
......
...@@ -23,14 +23,6 @@ class MockFramebufferImpl : public rx::FramebufferImpl ...@@ -23,14 +23,6 @@ class MockFramebufferImpl : public rx::FramebufferImpl
MockFramebufferImpl() : rx::FramebufferImpl(gl::Framebuffer::Data()) {} MockFramebufferImpl() : rx::FramebufferImpl(gl::Framebuffer::Data()) {}
virtual ~MockFramebufferImpl() { destroy(); } virtual ~MockFramebufferImpl() { destroy(); }
MOCK_METHOD1(onUpdateColorAttachment, void(size_t));
MOCK_METHOD0(onUpdateDepthAttachment, void());
MOCK_METHOD0(onUpdateStencilAttachment, void());
MOCK_METHOD0(onUpdateDepthStencilAttachment, void());
MOCK_METHOD2(setDrawBuffers, void(size_t, const GLenum *));
MOCK_METHOD1(setReadBuffer, void(GLenum));
MOCK_METHOD2(discard, gl::Error(size_t, const GLenum *)); MOCK_METHOD2(discard, gl::Error(size_t, const GLenum *));
MOCK_METHOD2(invalidate, gl::Error(size_t, const GLenum *)); MOCK_METHOD2(invalidate, gl::Error(size_t, const GLenum *));
MOCK_METHOD3(invalidateSub, gl::Error(size_t, const GLenum *, const gl::Rectangle &)); MOCK_METHOD3(invalidateSub, gl::Error(size_t, const GLenum *, const gl::Rectangle &));
...@@ -57,9 +49,24 @@ class MockFramebufferImpl : public rx::FramebufferImpl ...@@ -57,9 +49,24 @@ class MockFramebufferImpl : public rx::FramebufferImpl
MOCK_CONST_METHOD0(checkStatus, bool()); MOCK_CONST_METHOD0(checkStatus, bool());
MOCK_METHOD1(syncState, void(const gl::Framebuffer::DirtyBits &));
MOCK_METHOD0(destroy, void()); MOCK_METHOD0(destroy, void());
}; };
inline ::testing::NiceMock<MockFramebufferImpl> *MakeFramebufferMock()
{
::testing::NiceMock<MockFramebufferImpl> *framebufferImpl =
new ::testing::NiceMock<MockFramebufferImpl>();
// TODO(jmadill): add ON_CALLS for other returning methods
ON_CALL(*framebufferImpl, checkStatus()).WillByDefault(::testing::Return(true));
// We must mock the destructor since NiceMock doesn't work for destructors.
EXPECT_CALL(*framebufferImpl, destroy()).Times(1).RetiresOnSaturation();
return framebufferImpl;
}
} // namespace rx } // namespace rx
#endif // LIBANGLE_RENDERER_FRAMEBUFFERIMPLMOCK_H_ #endif // LIBANGLE_RENDERER_FRAMEBUFFERIMPLMOCK_H_
...@@ -60,6 +60,16 @@ class MockProgramImpl : public rx::ProgramImpl ...@@ -60,6 +60,16 @@ class MockProgramImpl : public rx::ProgramImpl
MOCK_METHOD0(destroy, void()); MOCK_METHOD0(destroy, void());
}; };
inline ::testing::NiceMock<MockProgramImpl> *MakeProgramMock()
{
::testing::NiceMock<MockProgramImpl> *programImpl = new ::testing::NiceMock<MockProgramImpl>();
// TODO(jmadill): add ON_CALLS for returning methods
// We must mock the destructor since NiceMock doesn't work for destructors.
EXPECT_CALL(*programImpl, destroy()).Times(1).RetiresOnSaturation();
return programImpl;
}
} // namespace rx } // namespace rx
#endif // LIBANGLE_RENDERER_PROGRAMIMPLMOCK_H_ #endif // LIBANGLE_RENDERER_PROGRAMIMPLMOCK_H_
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "libANGLE/renderer/d3d/FramebufferD3D.h" #include "libANGLE/renderer/d3d/FramebufferD3D.h"
#include "common/BitSetIterator.h"
#include "libANGLE/formatutils.h" #include "libANGLE/formatutils.h"
#include "libANGLE/Framebuffer.h" #include "libANGLE/Framebuffer.h"
#include "libANGLE/FramebufferAttachment.h" #include "libANGLE/FramebufferAttachment.h"
...@@ -84,10 +85,8 @@ ClearParameters GetClearParameters(const gl::State &state, GLbitfield mask) ...@@ -84,10 +85,8 @@ ClearParameters GetClearParameters(const gl::State &state, GLbitfield mask)
} }
FramebufferD3D::FramebufferD3D(const gl::Framebuffer::Data &data) FramebufferD3D::FramebufferD3D(const gl::Framebuffer::Data &data, RendererD3D *renderer)
: FramebufferImpl(data), : FramebufferImpl(data), mRenderer(renderer)
mColorAttachmentsForRender(mData.getColorAttachments().size(), nullptr),
mInvalidateColorAttachmentCache(true)
{ {
} }
...@@ -95,32 +94,6 @@ FramebufferD3D::~FramebufferD3D() ...@@ -95,32 +94,6 @@ FramebufferD3D::~FramebufferD3D()
{ {
} }
void FramebufferD3D::onUpdateColorAttachment(size_t /*index*/)
{
mInvalidateColorAttachmentCache = true;
}
void FramebufferD3D::onUpdateDepthAttachment()
{
}
void FramebufferD3D::onUpdateStencilAttachment()
{
}
void FramebufferD3D::onUpdateDepthStencilAttachment()
{
}
void FramebufferD3D::setDrawBuffers(size_t, const GLenum *)
{
mInvalidateColorAttachmentCache = true;
}
void FramebufferD3D::setReadBuffer(GLenum)
{
}
gl::Error FramebufferD3D::clear(const gl::Data &data, GLbitfield mask) gl::Error FramebufferD3D::clear(const gl::Data &data, GLbitfield mask)
{ {
const gl::State &state = *data.state; const gl::State &state = *data.state;
...@@ -354,19 +327,36 @@ bool FramebufferD3D::checkStatus() const ...@@ -354,19 +327,36 @@ bool FramebufferD3D::checkStatus() const
return true; return true;
} }
const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender( void FramebufferD3D::syncState(const gl::Framebuffer::DirtyBits &dirtyBits)
const WorkaroundsD3D &workarounds) const
{ {
if (!mInvalidateColorAttachmentCache) bool invalidateColorAttachmentCache = false;
if (!mColorAttachmentsForRender.valid())
{
invalidateColorAttachmentCache = true;
}
for (auto dirtyBit : angle::IterateBitSet(dirtyBits))
{ {
return mColorAttachmentsForRender; if ((dirtyBit >= gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 &&
dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX) ||
dirtyBit == gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS)
{
invalidateColorAttachmentCache = true;
}
}
if (!invalidateColorAttachmentCache)
{
return;
} }
// Does not actually free memory // Does not actually free memory
mColorAttachmentsForRender.clear(); gl::AttachmentList colorAttachmentsForRender;
const auto &colorAttachments = mData.getColorAttachments(); const auto &colorAttachments = mData.getColorAttachments();
const auto &drawBufferStates = mData.getDrawBufferStates(); const auto &drawBufferStates = mData.getDrawBufferStates();
const auto &workarounds = mRenderer->getWorkarounds();
for (size_t attachmentIndex = 0; attachmentIndex < colorAttachments.size(); ++attachmentIndex) for (size_t attachmentIndex = 0; attachmentIndex < colorAttachments.size(); ++attachmentIndex)
{ {
...@@ -376,16 +366,21 @@ const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender( ...@@ -376,16 +366,21 @@ const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender(
if (colorAttachment.isAttached() && drawBufferState != GL_NONE) if (colorAttachment.isAttached() && drawBufferState != GL_NONE)
{ {
ASSERT(drawBufferState == GL_BACK || drawBufferState == (GL_COLOR_ATTACHMENT0_EXT + attachmentIndex)); ASSERT(drawBufferState == GL_BACK || drawBufferState == (GL_COLOR_ATTACHMENT0_EXT + attachmentIndex));
mColorAttachmentsForRender.push_back(&colorAttachment); colorAttachmentsForRender.push_back(&colorAttachment);
} }
else if (!workarounds.mrtPerfWorkaround) else if (!workarounds.mrtPerfWorkaround)
{ {
mColorAttachmentsForRender.push_back(nullptr); colorAttachmentsForRender.push_back(nullptr);
} }
} }
mInvalidateColorAttachmentCache = false; mColorAttachmentsForRender = std::move(colorAttachmentsForRender);
return mColorAttachmentsForRender;
} }
const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender() const
{
ASSERT(mColorAttachmentsForRender.valid());
return mColorAttachmentsForRender.value();
} }
} // namespace rx
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <vector> #include <vector>
#include <cstdint> #include <cstdint>
#include "common/Optional.h"
#include "libANGLE/angletypes.h" #include "libANGLE/angletypes.h"
#include "libANGLE/renderer/FramebufferImpl.h" #include "libANGLE/renderer/FramebufferImpl.h"
...@@ -26,6 +27,7 @@ typedef std::vector<const FramebufferAttachment *> AttachmentList; ...@@ -26,6 +27,7 @@ typedef std::vector<const FramebufferAttachment *> AttachmentList;
namespace rx namespace rx
{ {
class RendererD3D;
class RenderTargetD3D; class RenderTargetD3D;
struct WorkaroundsD3D; struct WorkaroundsD3D;
...@@ -55,17 +57,9 @@ struct ClearParameters ...@@ -55,17 +57,9 @@ struct ClearParameters
class FramebufferD3D : public FramebufferImpl class FramebufferD3D : public FramebufferImpl
{ {
public: public:
FramebufferD3D(const gl::Framebuffer::Data &data); FramebufferD3D(const gl::Framebuffer::Data &data, RendererD3D *renderer);
virtual ~FramebufferD3D(); virtual ~FramebufferD3D();
void onUpdateColorAttachment(size_t index) override;
void onUpdateDepthAttachment() override;
void onUpdateStencilAttachment() override;
void onUpdateDepthStencilAttachment() override;
void setDrawBuffers(size_t count, const GLenum *buffers) override;
void setReadBuffer(GLenum buffer) override;
gl::Error clear(const gl::Data &data, GLbitfield mask) override; gl::Error clear(const gl::Data &data, GLbitfield mask) override;
gl::Error clearBufferfv(const gl::Data &data, gl::Error clearBufferfv(const gl::Data &data,
GLenum buffer, GLenum buffer,
...@@ -94,12 +88,9 @@ class FramebufferD3D : public FramebufferImpl ...@@ -94,12 +88,9 @@ class FramebufferD3D : public FramebufferImpl
bool checkStatus() const override; bool checkStatus() const override;
const gl::AttachmentList &getColorAttachmentsForRender(const WorkaroundsD3D &workarounds) const; void syncState(const gl::Framebuffer::DirtyBits &dirtyBits) override;
protected: const gl::AttachmentList &getColorAttachmentsForRender() const;
// Cache variable
mutable gl::AttachmentList mColorAttachmentsForRender;
mutable bool mInvalidateColorAttachmentCache;
private: private:
virtual gl::Error clear(const gl::Data &data, const ClearParameters &clearParams) = 0; virtual gl::Error clear(const gl::Data &data, const ClearParameters &clearParams) = 0;
...@@ -116,6 +107,9 @@ class FramebufferD3D : public FramebufferImpl ...@@ -116,6 +107,9 @@ class FramebufferD3D : public FramebufferImpl
const gl::Framebuffer *sourceFramebuffer) = 0; const gl::Framebuffer *sourceFramebuffer) = 0;
virtual GLenum getRenderTargetImplementationFormat(RenderTargetD3D *renderTarget) const = 0; virtual GLenum getRenderTargetImplementationFormat(RenderTargetD3D *renderTarget) const = 0;
RendererD3D *mRenderer;
Optional<gl::AttachmentList> mColorAttachmentsForRender;
}; };
} }
......
...@@ -1114,8 +1114,7 @@ gl::Error ProgramD3D::getPixelExecutableForFramebuffer(const gl::Framebuffer *fb ...@@ -1114,8 +1114,7 @@ gl::Error ProgramD3D::getPixelExecutableForFramebuffer(const gl::Framebuffer *fb
mPixelShaderOutputFormatCache.clear(); mPixelShaderOutputFormatCache.clear();
const FramebufferD3D *fboD3D = GetImplAs<FramebufferD3D>(fbo); const FramebufferD3D *fboD3D = GetImplAs<FramebufferD3D>(fbo);
const gl::AttachmentList &colorbuffers = const gl::AttachmentList &colorbuffers = fboD3D->getColorAttachmentsForRender();
fboD3D->getColorAttachmentsForRender(mRenderer->getWorkarounds());
for (size_t colorAttachment = 0; colorAttachment < colorbuffers.size(); ++colorAttachment) for (size_t colorAttachment = 0; colorAttachment < colorbuffers.size(); ++colorAttachment)
{ {
......
...@@ -25,8 +25,7 @@ namespace rx ...@@ -25,8 +25,7 @@ namespace rx
{ {
Framebuffer11::Framebuffer11(const gl::Framebuffer::Data &data, Renderer11 *renderer) Framebuffer11::Framebuffer11(const gl::Framebuffer::Data &data, Renderer11 *renderer)
: FramebufferD3D(data), : FramebufferD3D(data, renderer), mRenderer(renderer)
mRenderer(renderer)
{ {
ASSERT(mRenderer != nullptr); ASSERT(mRenderer != nullptr);
} }
......
...@@ -96,7 +96,7 @@ gl::Error RenderStateCache::getBlendState(const gl::Framebuffer *framebuffer, co ...@@ -96,7 +96,7 @@ gl::Error RenderStateCache::getBlendState(const gl::Framebuffer *framebuffer, co
bool mrt = false; bool mrt = false;
const FramebufferD3D *framebufferD3D = GetImplAs<FramebufferD3D>(framebuffer); const FramebufferD3D *framebufferD3D = GetImplAs<FramebufferD3D>(framebuffer);
const gl::AttachmentList &colorbuffers = framebufferD3D->getColorAttachmentsForRender(mRenderer->getWorkarounds()); const gl::AttachmentList &colorbuffers = framebufferD3D->getColorAttachmentsForRender();
BlendStateKey key = {}; BlendStateKey key = {};
key.blendState = blendState; key.blendState = blendState;
......
...@@ -1551,7 +1551,7 @@ gl::Error Renderer11::applyRenderTarget(const gl::Framebuffer *framebuffer) ...@@ -1551,7 +1551,7 @@ gl::Error Renderer11::applyRenderTarget(const gl::Framebuffer *framebuffer)
bool missingColorRenderTarget = true; bool missingColorRenderTarget = true;
const FramebufferD3D *framebufferD3D = GetImplAs<FramebufferD3D>(framebuffer); const FramebufferD3D *framebufferD3D = GetImplAs<FramebufferD3D>(framebuffer);
const gl::AttachmentList &colorbuffers = framebufferD3D->getColorAttachmentsForRender(getWorkarounds()); const gl::AttachmentList &colorbuffers = framebufferD3D->getColorAttachmentsForRender();
for (size_t colorAttachment = 0; colorAttachment < colorbuffers.size(); ++colorAttachment) for (size_t colorAttachment = 0; colorAttachment < colorbuffers.size(); ++colorAttachment)
{ {
......
...@@ -22,8 +22,7 @@ namespace rx ...@@ -22,8 +22,7 @@ namespace rx
{ {
Framebuffer9::Framebuffer9(const gl::Framebuffer::Data &data, Renderer9 *renderer) Framebuffer9::Framebuffer9(const gl::Framebuffer::Data &data, Renderer9 *renderer)
: FramebufferD3D(data), : FramebufferD3D(data, renderer), mRenderer(renderer)
mRenderer(renderer)
{ {
ASSERT(mRenderer != nullptr); ASSERT(mRenderer != nullptr);
} }
......
...@@ -36,14 +36,6 @@ class FramebufferGL : public FramebufferImpl ...@@ -36,14 +36,6 @@ class FramebufferGL : public FramebufferImpl
StateManagerGL *stateManager); StateManagerGL *stateManager);
~FramebufferGL() override; ~FramebufferGL() override;
void onUpdateColorAttachment(size_t index) override;
void onUpdateDepthAttachment() override;
void onUpdateStencilAttachment() override;
void onUpdateDepthStencilAttachment() override;
void setDrawBuffers(size_t count, const GLenum *buffers) override;
void setReadBuffer(GLenum buffer) override;
gl::Error discard(size_t count, const GLenum *attachments) override; gl::Error discard(size_t count, const GLenum *attachments) override;
gl::Error invalidate(size_t count, const GLenum *attachments) override; gl::Error invalidate(size_t count, const GLenum *attachments) override;
gl::Error invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area) override; gl::Error invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area) override;
...@@ -76,6 +68,8 @@ class FramebufferGL : public FramebufferImpl ...@@ -76,6 +68,8 @@ class FramebufferGL : public FramebufferImpl
bool checkStatus() const override; bool checkStatus() const override;
void syncState(const gl::Framebuffer::DirtyBits &dirtyBits) override;
void syncDrawState() const; void syncDrawState() const;
GLuint getFramebufferID() const; GLuint getFramebufferID() const;
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
using namespace gl; using namespace gl;
using namespace rx; using namespace rx;
using testing::_; using testing::_;
using testing::NiceMock;
using testing::Return; using testing::Return;
namespace namespace
...@@ -72,19 +73,11 @@ MockValidationContext::MockValidationContext(GLint clientVersion, ...@@ -72,19 +73,11 @@ MockValidationContext::MockValidationContext(GLint clientVersion,
// but we want a test to ensure we maintain this behaviour. // but we want a test to ensure we maintain this behaviour.
TEST(ValidationESTest, DrawElementsWithMaxIndexGivesError) TEST(ValidationESTest, DrawElementsWithMaxIndexGivesError)
{ {
auto framebufferImpl = MakeFramebufferMock();
auto programImpl = MakeProgramMock();
// TODO(jmadill): Generalize some of this code so we can re-use it for other tests. // TODO(jmadill): Generalize some of this code so we can re-use it for other tests.
MockFramebufferImpl *framebufferImpl = new MockFramebufferImpl(); NiceMock<MockFactory> mockFactory;
EXPECT_CALL(*framebufferImpl, onUpdateColorAttachment(_)).Times(1).RetiresOnSaturation();
EXPECT_CALL(*framebufferImpl, checkStatus())
.Times(2)
.WillOnce(Return(true))
.WillOnce(Return(true));
EXPECT_CALL(*framebufferImpl, destroy()).Times(1).RetiresOnSaturation();
MockProgramImpl *programImpl = new MockProgramImpl();
EXPECT_CALL(*programImpl, destroy());
MockFactory mockFactory;
EXPECT_CALL(mockFactory, createFramebuffer(_)).WillOnce(Return(framebufferImpl)); EXPECT_CALL(mockFactory, createFramebuffer(_)).WillOnce(Return(framebufferImpl));
EXPECT_CALL(mockFactory, createProgram(_)).WillOnce(Return(programImpl)); EXPECT_CALL(mockFactory, createProgram(_)).WillOnce(Return(programImpl));
EXPECT_CALL(mockFactory, createVertexArray(_)).WillOnce(Return(nullptr)); EXPECT_CALL(mockFactory, createVertexArray(_)).WillOnce(Return(nullptr));
...@@ -101,9 +94,10 @@ TEST(ValidationESTest, DrawElementsWithMaxIndexGivesError) ...@@ -101,9 +94,10 @@ TEST(ValidationESTest, DrawElementsWithMaxIndexGivesError)
caps.maxColorAttachments = 1; caps.maxColorAttachments = 1;
state.initialize(caps, extensions, 3, false); state.initialize(caps, extensions, 3, false);
MockTextureImpl *textureImpl = new MockTextureImpl(); NiceMock<MockTextureImpl> *textureImpl = new NiceMock<MockTextureImpl>();
EXPECT_CALL(*textureImpl, setStorage(_, _, _, _)).WillOnce(Return(Error(GL_NO_ERROR))); EXPECT_CALL(*textureImpl, setStorage(_, _, _, _)).WillOnce(Return(Error(GL_NO_ERROR)));
EXPECT_CALL(*textureImpl, destructor()).Times(1).RetiresOnSaturation(); EXPECT_CALL(*textureImpl, destructor()).Times(1).RetiresOnSaturation();
Texture *texture = new Texture(textureImpl, 0, GL_TEXTURE_2D); Texture *texture = new Texture(textureImpl, 0, GL_TEXTURE_2D);
texture->addRef(); texture->addRef();
texture->setStorage(GL_TEXTURE_2D, 1, GL_RGBA8, Extents(1, 1, 0)); texture->setStorage(GL_TEXTURE_2D, 1, GL_RGBA8, Extents(1, 1, 0));
...@@ -118,8 +112,8 @@ TEST(ValidationESTest, DrawElementsWithMaxIndexGivesError) ...@@ -118,8 +112,8 @@ TEST(ValidationESTest, DrawElementsWithMaxIndexGivesError)
state.setDrawFramebufferBinding(framebuffer); state.setDrawFramebufferBinding(framebuffer);
state.setProgram(program); state.setProgram(program);
MockValidationContext testContext(3, state, caps, textureCaps, extensions, nullptr, limitations, NiceMock<MockValidationContext> testContext(3, state, caps, textureCaps, extensions, nullptr,
false); limitations, false);
// Set the expectation for the validation error here. // Set the expectation for the validation error here.
Error expectedError(GL_INVALID_OPERATION, g_ExceedsMaxElementErrorMessage); Error expectedError(GL_INVALID_OPERATION, g_ExceedsMaxElementErrorMessage);
......
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
'<(angle_path)/src/tests/gl_tests/SimpleOperationTest.cpp', '<(angle_path)/src/tests/gl_tests/SimpleOperationTest.cpp',
'<(angle_path)/src/tests/gl_tests/SixteenBppTextureTest.cpp', '<(angle_path)/src/tests/gl_tests/SixteenBppTextureTest.cpp',
'<(angle_path)/src/tests/gl_tests/SRGBTextureTest.cpp', '<(angle_path)/src/tests/gl_tests/SRGBTextureTest.cpp',
'<(angle_path)/src/tests/gl_tests/StateChangeTest.cpp',
'<(angle_path)/src/tests/gl_tests/SwizzleTest.cpp', '<(angle_path)/src/tests/gl_tests/SwizzleTest.cpp',
'<(angle_path)/src/tests/gl_tests/TextureTest.cpp', '<(angle_path)/src/tests/gl_tests/TextureTest.cpp',
'<(angle_path)/src/tests/gl_tests/TransformFeedbackTest.cpp', '<(angle_path)/src/tests/gl_tests/TransformFeedbackTest.cpp',
......
//
// Copyright 2015 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// StateChangeTest:
// Specifically designed for an ANGLE implementation of GL, these tests validate that
// ANGLE's dirty bits systems don't get confused by certain sequences of state changes.
//
#include "test_utils/ANGLETest.h"
using namespace angle;
namespace
{
class StateChangeTest : public ANGLETest
{
protected:
StateChangeTest() : mFramebuffer(0)
{
setWindowWidth(64);
setWindowHeight(64);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
// Enable the no error extension to avoid syncing the FBO state on validation.
setNoErrorEnabled(true);
}
void SetUp() override
{
ANGLETest::SetUp();
glGenFramebuffers(1, &mFramebuffer);
mTextures.resize(2, 0);
glGenTextures(2, mTextures.data());
ASSERT_GL_NO_ERROR();
}
void TearDown() override
{
if (mFramebuffer != 0)
{
glDeleteFramebuffers(1, &mFramebuffer);
mFramebuffer = 0;
}
if (!mTextures.empty())
{
glDeleteTextures(static_cast<GLsizei>(mTextures.size()), mTextures.data());
mTextures.clear();
}
ANGLETest::TearDown();
}
GLuint mFramebuffer;
std::vector<GLuint> mTextures;
};
class StateChangeTestES3 : public StateChangeTest
{
protected:
StateChangeTestES3() {}
};
} // anonymous namespace
// Ensure that CopyTexImage2D syncs framebuffer changes.
TEST_P(StateChangeTest, CopyTexImage2DSync)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
// Init first texture to red
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
// Init second texture to green
glBindTexture(GL_TEXTURE_2D, mTextures[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255);
// Copy in the red texture to the green one.
// CopyTexImage should sync the framebuffer attachment change.
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 16, 16, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
ASSERT_GL_NO_ERROR();
}
// Ensure that CopyTexSubImage2D syncs framebuffer changes.
TEST_P(StateChangeTest, CopyTexSubImage2DSync)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
// Init first texture to red
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
// Init second texture to green
glBindTexture(GL_TEXTURE_2D, mTextures[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255);
// Copy in the red texture to the green one.
// CopyTexImage should sync the framebuffer attachment change.
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 16, 16);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
ASSERT_GL_NO_ERROR();
}
// Ensure that CopyTexSubImage3D syncs framebuffer changes.
TEST_P(StateChangeTestES3, CopyTexSubImage3DSync)
{
if (isD3D11())
{
// TODO(jmadill): Fix the bug in the D3D11 back-end.
std::cout << "Test diabled on D3D11." << std::endl;
return;
}
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
// Init first texture to red
glBindTexture(GL_TEXTURE_3D, mTextures[0]);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 16, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[0], 0, 0);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
// Init second texture to green
glBindTexture(GL_TEXTURE_3D, mTextures[1]);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 16, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[1], 0, 0);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255);
// Copy in the red texture to the green one.
// CopyTexImage should sync the framebuffer attachment change.
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[0], 0, 0);
glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 0, 0, 16, 16);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextures[1], 0, 0);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
ASSERT_GL_NO_ERROR();
}
// Ensure that BlitFramebuffer syncs framebuffer changes.
TEST_P(StateChangeTestES3, BlitFramebufferSync)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
// Init first texture to red
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
// Init second texture to green
glBindTexture(GL_TEXTURE_2D, mTextures[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[1], 0);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255);
// Change to the red textures and blit.
// BlitFramebuffer should sync the framebuffer attachment change.
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0],
0);
glBlitFramebuffer(0, 0, 16, 16, 0, 0, 16, 16, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
ASSERT_GL_NO_ERROR();
}
// Ensure that ReadBuffer and DrawBuffers sync framebuffer changes.
TEST_P(StateChangeTestES3, ReadBufferAndDrawBuffersSync)
{
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
// Initialize two FBO attachments
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextures[0], 0);
glBindTexture(GL_TEXTURE_2D, mTextures[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, mTextures[1], 0);
// Clear first attachment to red
GLenum bufs1[] = {GL_COLOR_ATTACHMENT0, GL_NONE};
glDrawBuffers(2, bufs1);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Clear second texture to green
GLenum bufs2[] = {GL_NONE, GL_COLOR_ATTACHMENT1};
glDrawBuffers(2, bufs2);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Verify first attachment is red and second is green
glReadBuffer(GL_COLOR_ATTACHMENT1);
EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255);
glReadBuffer(GL_COLOR_ATTACHMENT0);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
ASSERT_GL_NO_ERROR();
}
ANGLE_INSTANTIATE_TEST(StateChangeTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL());
ANGLE_INSTANTIATE_TEST(StateChangeTestES3, ES3_D3D11(), ES3_OPENGL());
...@@ -310,6 +310,11 @@ void ANGLETest::setDebugEnabled(bool enabled) ...@@ -310,6 +310,11 @@ void ANGLETest::setDebugEnabled(bool enabled)
mEGLWindow->setDebugEnabled(enabled); mEGLWindow->setDebugEnabled(enabled);
} }
void ANGLETest::setNoErrorEnabled(bool enabled)
{
mEGLWindow->setNoErrorEnabled(enabled);
}
int ANGLETest::getClientVersion() const int ANGLETest::getClientVersion() const
{ {
return mEGLWindow->getClientMajorVersion(); return mEGLWindow->getClientMajorVersion();
......
...@@ -103,6 +103,7 @@ class ANGLETest : public ::testing::TestWithParam<angle::PlatformParameters> ...@@ -103,6 +103,7 @@ class ANGLETest : public ::testing::TestWithParam<angle::PlatformParameters>
void setConfigStencilBits(int bits); void setConfigStencilBits(int bits);
void setMultisampleEnabled(bool enabled); void setMultisampleEnabled(bool enabled);
void setDebugEnabled(bool enabled); void setDebugEnabled(bool enabled);
void setNoErrorEnabled(bool enabled);
int getClientVersion() const; int getClientVersion() const;
......
...@@ -86,6 +86,7 @@ EGLWindow::EGLWindow(EGLint glesMajorVersion, ...@@ -86,6 +86,7 @@ EGLWindow::EGLWindow(EGLint glesMajorVersion,
mStencilBits(-1), mStencilBits(-1),
mMultisample(false), mMultisample(false),
mDebug(false), mDebug(false),
mNoError(false),
mSwapInterval(-1) mSwapInterval(-1)
{ {
} }
...@@ -230,6 +231,13 @@ bool EGLWindow::initializeGL(OSWindow *osWindow) ...@@ -230,6 +231,13 @@ bool EGLWindow::initializeGL(OSWindow *osWindow)
contextAttributes.push_back(EGL_CONTEXT_OPENGL_DEBUG); contextAttributes.push_back(EGL_CONTEXT_OPENGL_DEBUG);
contextAttributes.push_back(mDebug ? EGL_TRUE : EGL_FALSE); contextAttributes.push_back(mDebug ? EGL_TRUE : EGL_FALSE);
// TODO(jmadill): Check for the extension string.
// bool hasKHRCreateContextNoError = strstr(displayExtensions,
// "EGL_KHR_create_context_no_error") != nullptr;
contextAttributes.push_back(EGL_CONTEXT_OPENGL_NO_ERROR_KHR);
contextAttributes.push_back(mNoError ? EGL_TRUE : EGL_FALSE);
} }
contextAttributes.push_back(EGL_NONE); contextAttributes.push_back(EGL_NONE);
......
...@@ -60,6 +60,7 @@ class EGLWindow : angle::NonCopyable ...@@ -60,6 +60,7 @@ class EGLWindow : angle::NonCopyable
void setConfigStencilBits(int bits) { mStencilBits = bits; } void setConfigStencilBits(int bits) { mStencilBits = bits; }
void setMultisample(bool multisample) { mMultisample = multisample; } void setMultisample(bool multisample) { mMultisample = multisample; }
void setDebugEnabled(bool debug) { mDebug = debug; } void setDebugEnabled(bool debug) { mDebug = debug; }
void setNoErrorEnabled(bool noError) { mNoError = noError; }
void setSwapInterval(EGLint swapInterval) { mSwapInterval = swapInterval; } void setSwapInterval(EGLint swapInterval) { mSwapInterval = swapInterval; }
static EGLBoolean FindEGLConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *config); static EGLBoolean FindEGLConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *config);
...@@ -104,6 +105,7 @@ class EGLWindow : angle::NonCopyable ...@@ -104,6 +105,7 @@ class EGLWindow : angle::NonCopyable
int mStencilBits; int mStencilBits;
bool mMultisample; bool mMultisample;
bool mDebug; bool mDebug;
bool mNoError;
EGLint mSwapInterval; EGLint mSwapInterval;
}; };
......
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