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
return *this;
}
Optional &operator=(T &&value)
{
mValue = std::move(value);
mValid = true;
return *this;
}
void reset()
{
mValid = false;
......
......@@ -2065,6 +2065,9 @@ void Context::copyTexImage2D(GLenum target,
GLsizei height,
GLint border)
{
// Only sync the read FBO
mState.syncDirtyObject(GL_READ_FRAMEBUFFER);
Rectangle sourceArea(x, y, width, height);
const Framebuffer *framebuffer = mState.getReadFramebuffer();
......@@ -2086,6 +2089,9 @@ void Context::copyTexSubImage2D(GLenum target,
GLsizei width,
GLsizei height)
{
// Only sync the read FBO
mState.syncDirtyObject(GL_READ_FRAMEBUFFER);
Offset destOffset(xoffset, yoffset, 0);
Rectangle sourceArea(x, y, width, height);
......@@ -2109,6 +2115,9 @@ void Context::copyTexSubImage3D(GLenum target,
GLsizei width,
GLsizei height)
{
// Only sync the read FBO
mState.syncDirtyObject(GL_READ_FRAMEBUFFER);
Offset destOffset(xoffset, yoffset, zoffset);
Rectangle sourceArea(x, y, width, height);
......@@ -2152,6 +2161,8 @@ void Context::framebufferTexture2D(GLenum target,
{
framebuffer->resetAttachment(attachment);
}
mState.setObjectDirty(target);
}
void Context::framebufferRenderbuffer(GLenum target,
......@@ -2172,6 +2183,8 @@ void Context::framebufferRenderbuffer(GLenum target,
{
framebuffer->resetAttachment(attachment);
}
mState.setObjectDirty(target);
}
void Context::framebufferTextureLayer(GLenum target,
......@@ -2205,6 +2218,8 @@ void Context::framebufferTextureLayer(GLenum target,
{
framebuffer->resetAttachment(attachment);
}
mState.setObjectDirty(target);
}
void Context::drawBuffers(GLsizei n, const GLenum *bufs)
......@@ -2212,16 +2227,21 @@ void Context::drawBuffers(GLsizei n, const GLenum *bufs)
Framebuffer *framebuffer = mState.getDrawFramebuffer();
ASSERT(framebuffer);
framebuffer->setDrawBuffers(n, bufs);
mState.setObjectDirty(GL_DRAW_FRAMEBUFFER);
}
void Context::readBuffer(GLenum mode)
{
Framebuffer *readFBO = mState.getReadFramebuffer();
readFBO->setReadBuffer(mode);
mState.setObjectDirty(GL_READ_FRAMEBUFFER);
}
void Context::discardFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments)
{
// Only sync the FBO
mState.syncDirtyObject(target);
Framebuffer *framebuffer = mState.getTargetFramebuffer(target);
ASSERT(framebuffer);
......@@ -2238,6 +2258,9 @@ void Context::invalidateFramebuffer(GLenum target,
GLsizei numAttachments,
const GLenum *attachments)
{
// Only sync the FBO
mState.syncDirtyObject(target);
Framebuffer *framebuffer = mState.getTargetFramebuffer(target);
ASSERT(framebuffer);
......@@ -2260,6 +2283,9 @@ void Context::invalidateSubFramebuffer(GLenum target,
GLsizei width,
GLsizei height)
{
// Only sync the FBO
mState.syncDirtyObject(target);
Framebuffer *framebuffer = mState.getTargetFramebuffer(target);
ASSERT(framebuffer);
......
......@@ -300,7 +300,7 @@ void Framebuffer::setDrawBuffers(size_t count, const GLenum *buffers)
ASSERT(count <= drawStates.size());
std::copy(buffers, buffers + count, drawStates.begin());
std::fill(drawStates.begin() + count, drawStates.end(), GL_NONE);
mImpl->setDrawBuffers(count, buffers);
mDirtyBits.set(DIRTY_BIT_DRAW_BUFFERS);
}
GLenum Framebuffer::getReadBufferState() const
......@@ -314,7 +314,7 @@ void Framebuffer::setReadBuffer(GLenum buffer)
(buffer >= GL_COLOR_ATTACHMENT0 &&
(buffer - GL_COLOR_ATTACHMENT0) < mData.mColorAttachments.size()));
mData.mReadBufferState = buffer;
mImpl->setReadBuffer(buffer);
mDirtyBits.set(DIRTY_BIT_READ_BUFFER);
}
bool Framebuffer::isEnabledColorAttachment(size_t colorAttachment) const
......@@ -567,6 +567,7 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const
return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
}
syncState();
if (!mImpl->checkStatus())
{
return GL_FRAMEBUFFER_UNSUPPORTED;
......@@ -740,32 +741,33 @@ void Framebuffer::setAttachment(GLenum type,
mData.mDepthAttachment.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
{
switch (binding)
{
case GL_DEPTH:
case GL_DEPTH_ATTACHMENT:
mData.mDepthAttachment.attach(type, binding, textureIndex, resource);
mImpl->onUpdateDepthAttachment();
case GL_DEPTH:
case GL_DEPTH_ATTACHMENT:
mData.mDepthAttachment.attach(type, binding, textureIndex, resource);
mDirtyBits.set(DIRTY_BIT_DEPTH_ATTACHMENT);
break;
case GL_STENCIL:
case GL_STENCIL_ATTACHMENT:
mData.mStencilAttachment.attach(type, binding, textureIndex, resource);
mImpl->onUpdateStencilAttachment();
case GL_STENCIL:
case GL_STENCIL_ATTACHMENT:
mData.mStencilAttachment.attach(type, binding, textureIndex, resource);
mDirtyBits.set(DIRTY_BIT_STENCIL_ATTACHMENT);
break;
case GL_BACK:
mData.mColorAttachments[0].attach(type, binding, textureIndex, resource);
mImpl->onUpdateColorAttachment(0);
case GL_BACK:
mData.mColorAttachments[0].attach(type, binding, textureIndex, resource);
mDirtyBits.set(DIRTY_BIT_COLOR_ATTACHMENT_0);
break;
default:
default:
{
size_t colorIndex = binding - GL_COLOR_ATTACHMENT0;
ASSERT(colorIndex < mData.mColorAttachments.size());
mData.mColorAttachments[colorIndex].attach(type, binding, textureIndex, resource);
mImpl->onUpdateColorAttachment(colorIndex);
mDirtyBits.set(DIRTY_BIT_COLOR_ATTACHMENT_0 + colorIndex);
}
break;
}
......@@ -777,4 +779,13 @@ void Framebuffer::resetAttachment(GLenum binding)
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
const FramebufferAttachment *getDepthStencilAttachment() const;
const std::vector<GLenum> &getDrawBufferStates() const { return mDrawBufferStates; }
GLenum getReadBufferState() const { return mReadBufferState; }
const std::vector<FramebufferAttachment> &getColorAttachments() const { return mColorAttachments; }
bool attachmentsHaveSameDimensions() const;
......@@ -168,12 +169,33 @@ class Framebuffer final : public LabeledObject
GLenum filter,
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:
void detachResourceById(GLenum resourceType, GLuint resourceId);
Data mData;
rx::FramebufferImpl *mImpl;
GLuint mId;
// TODO(jmadill): See if we can make this non-mutable.
mutable DirtyBits mDirtyBits;
};
}
......
......@@ -835,22 +835,44 @@ void State::detachRenderbuffer(GLuint renderbuffer)
void State::setReadFramebufferBinding(Framebuffer *framebuffer)
{
if (mReadFramebuffer == framebuffer)
return;
mReadFramebuffer = framebuffer;
mDirtyBits.set(DIRTY_BIT_READ_FRAMEBUFFER_BINDING);
if (mReadFramebuffer && mReadFramebuffer->hasAnyDirtyBit())
{
mDirtyObjects.set(DIRTY_OBJECT_READ_FRAMEBUFFER);
}
}
void State::setDrawFramebufferBinding(Framebuffer *framebuffer)
{
if (mDrawFramebuffer == framebuffer)
return;
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
{
switch (target)
{
case GL_READ_FRAMEBUFFER_ANGLE: return mReadFramebuffer;
case GL_DRAW_FRAMEBUFFER_ANGLE:
case GL_FRAMEBUFFER: return mDrawFramebuffer;
default: UNREACHABLE(); return NULL;
case GL_READ_FRAMEBUFFER_ANGLE:
return mReadFramebuffer;
case GL_DRAW_FRAMEBUFFER_ANGLE:
case GL_FRAMEBUFFER:
return mDrawFramebuffer;
default:
UNREACHABLE();
return NULL;
}
}
......@@ -879,7 +901,7 @@ bool State::removeReadFramebufferBinding(GLuint framebuffer)
if (mReadFramebuffer != nullptr &&
mReadFramebuffer->id() == framebuffer)
{
mReadFramebuffer = NULL;
setReadFramebufferBinding(nullptr);
return true;
}
......@@ -891,7 +913,7 @@ bool State::removeDrawFramebufferBinding(GLuint framebuffer)
if (mReadFramebuffer != nullptr &&
mDrawFramebuffer->id() == framebuffer)
{
mDrawFramebuffer = NULL;
setDrawFramebufferBinding(nullptr);
return true;
}
......@@ -1720,17 +1742,25 @@ void State::syncDirtyObjects()
if (!mDirtyObjects.any())
return;
for (auto dirtyObject : angle::IterateBitSet(mDirtyObjects))
syncDirtyObjects(mDirtyObjects);
}
void State::syncDirtyObjects(const DirtyObjects &bitset)
{
for (auto dirtyObject : angle::IterateBitSet(bitset))
{
switch (dirtyObject)
{
case DIRTY_OBJECT_READ_FRAMEBUFFER:
// TODO(jmadill): implement this
ASSERT(mReadFramebuffer);
mReadFramebuffer->syncState();
break;
case DIRTY_OBJECT_DRAW_FRAMEBUFFER:
// TODO(jmadill): implement this
ASSERT(mDrawFramebuffer);
mDrawFramebuffer->syncState();
break;
case DIRTY_OBJECT_VERTEX_ARRAY:
ASSERT(mVertexArray);
mVertexArray->syncImplState();
break;
case DIRTY_OBJECT_PROGRAM:
......@@ -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
......@@ -359,6 +359,9 @@ class State : angle::NonCopyable
void clearDirtyObjects() { mDirtyObjects.reset(); }
void setAllDirtyObjects() { mDirtyObjects.set(); }
void syncDirtyObjects();
void syncDirtyObjects(const DirtyObjects &bitset);
void syncDirtyObject(GLenum target);
void setObjectDirty(GLenum target);
// Dirty bit masks
const DirtyBits &unpackStateBitMask() const { return mUnpackStateBitMask; }
......
......@@ -51,10 +51,6 @@ TEST(SurfaceTest, DestructionDeletesImpl)
EXPECT_CALL(*impl, getSwapBehavior());
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::Surface *surface = new egl::Surface(impl, EGL_WINDOW_BIT, &config, egl::AttributeMap());
......
......@@ -31,14 +31,6 @@ class FramebufferImpl : angle::NonCopyable
explicit FramebufferImpl(const gl::Framebuffer::Data &data) : mData(data) { }
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 invalidate(size_t count, const GLenum *attachments) = 0;
virtual gl::Error invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area) = 0;
......@@ -71,6 +63,8 @@ class FramebufferImpl : angle::NonCopyable
virtual bool checkStatus() const = 0;
virtual void syncState(const gl::Framebuffer::DirtyBits &dirtyBits) = 0;
const gl::Framebuffer::Data &getData() const { return mData; }
protected:
......
......@@ -23,14 +23,6 @@ class MockFramebufferImpl : public rx::FramebufferImpl
MockFramebufferImpl() : rx::FramebufferImpl(gl::Framebuffer::Data()) {}
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(invalidate, gl::Error(size_t, const GLenum *));
MOCK_METHOD3(invalidateSub, gl::Error(size_t, const GLenum *, const gl::Rectangle &));
......@@ -57,9 +49,24 @@ class MockFramebufferImpl : public rx::FramebufferImpl
MOCK_CONST_METHOD0(checkStatus, bool());
MOCK_METHOD1(syncState, void(const gl::Framebuffer::DirtyBits &));
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
#endif // LIBANGLE_RENDERER_FRAMEBUFFERIMPLMOCK_H_
......@@ -60,6 +60,16 @@ class MockProgramImpl : public rx::ProgramImpl
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
#endif // LIBANGLE_RENDERER_PROGRAMIMPLMOCK_H_
......@@ -8,6 +8,7 @@
#include "libANGLE/renderer/d3d/FramebufferD3D.h"
#include "common/BitSetIterator.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/Framebuffer.h"
#include "libANGLE/FramebufferAttachment.h"
......@@ -84,10 +85,8 @@ ClearParameters GetClearParameters(const gl::State &state, GLbitfield mask)
}
FramebufferD3D::FramebufferD3D(const gl::Framebuffer::Data &data)
: FramebufferImpl(data),
mColorAttachmentsForRender(mData.getColorAttachments().size(), nullptr),
mInvalidateColorAttachmentCache(true)
FramebufferD3D::FramebufferD3D(const gl::Framebuffer::Data &data, RendererD3D *renderer)
: FramebufferImpl(data), mRenderer(renderer)
{
}
......@@ -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)
{
const gl::State &state = *data.state;
......@@ -354,19 +327,36 @@ bool FramebufferD3D::checkStatus() const
return true;
}
const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender(
const WorkaroundsD3D &workarounds) const
void FramebufferD3D::syncState(const gl::Framebuffer::DirtyBits &dirtyBits)
{
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
mColorAttachmentsForRender.clear();
gl::AttachmentList colorAttachmentsForRender;
const auto &colorAttachments = mData.getColorAttachments();
const auto &drawBufferStates = mData.getDrawBufferStates();
const auto &workarounds = mRenderer->getWorkarounds();
for (size_t attachmentIndex = 0; attachmentIndex < colorAttachments.size(); ++attachmentIndex)
{
......@@ -376,16 +366,21 @@ const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender(
if (colorAttachment.isAttached() && drawBufferState != GL_NONE)
{
ASSERT(drawBufferState == GL_BACK || drawBufferState == (GL_COLOR_ATTACHMENT0_EXT + attachmentIndex));
mColorAttachmentsForRender.push_back(&colorAttachment);
colorAttachmentsForRender.push_back(&colorAttachment);
}
else if (!workarounds.mrtPerfWorkaround)
{
mColorAttachmentsForRender.push_back(nullptr);
colorAttachmentsForRender.push_back(nullptr);
}
}
mInvalidateColorAttachmentCache = false;
return mColorAttachmentsForRender;
mColorAttachmentsForRender = std::move(colorAttachmentsForRender);
}
const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender() const
{
ASSERT(mColorAttachmentsForRender.valid());
return mColorAttachmentsForRender.value();
}
} // namespace rx
......@@ -12,6 +12,7 @@
#include <vector>
#include <cstdint>
#include "common/Optional.h"
#include "libANGLE/angletypes.h"
#include "libANGLE/renderer/FramebufferImpl.h"
......@@ -26,6 +27,7 @@ typedef std::vector<const FramebufferAttachment *> AttachmentList;
namespace rx
{
class RendererD3D;
class RenderTargetD3D;
struct WorkaroundsD3D;
......@@ -55,17 +57,9 @@ struct ClearParameters
class FramebufferD3D : public FramebufferImpl
{
public:
FramebufferD3D(const gl::Framebuffer::Data &data);
FramebufferD3D(const gl::Framebuffer::Data &data, RendererD3D *renderer);
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 clearBufferfv(const gl::Data &data,
GLenum buffer,
......@@ -94,12 +88,9 @@ class FramebufferD3D : public FramebufferImpl
bool checkStatus() const override;
const gl::AttachmentList &getColorAttachmentsForRender(const WorkaroundsD3D &workarounds) const;
void syncState(const gl::Framebuffer::DirtyBits &dirtyBits) override;
protected:
// Cache variable
mutable gl::AttachmentList mColorAttachmentsForRender;
mutable bool mInvalidateColorAttachmentCache;
const gl::AttachmentList &getColorAttachmentsForRender() const;
private:
virtual gl::Error clear(const gl::Data &data, const ClearParameters &clearParams) = 0;
......@@ -116,6 +107,9 @@ class FramebufferD3D : public FramebufferImpl
const gl::Framebuffer *sourceFramebuffer) = 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
mPixelShaderOutputFormatCache.clear();
const FramebufferD3D *fboD3D = GetImplAs<FramebufferD3D>(fbo);
const gl::AttachmentList &colorbuffers =
fboD3D->getColorAttachmentsForRender(mRenderer->getWorkarounds());
const gl::AttachmentList &colorbuffers = fboD3D->getColorAttachmentsForRender();
for (size_t colorAttachment = 0; colorAttachment < colorbuffers.size(); ++colorAttachment)
{
......
......@@ -25,8 +25,7 @@ namespace rx
{
Framebuffer11::Framebuffer11(const gl::Framebuffer::Data &data, Renderer11 *renderer)
: FramebufferD3D(data),
mRenderer(renderer)
: FramebufferD3D(data, renderer), mRenderer(renderer)
{
ASSERT(mRenderer != nullptr);
}
......
......@@ -96,7 +96,7 @@ gl::Error RenderStateCache::getBlendState(const gl::Framebuffer *framebuffer, co
bool mrt = false;
const FramebufferD3D *framebufferD3D = GetImplAs<FramebufferD3D>(framebuffer);
const gl::AttachmentList &colorbuffers = framebufferD3D->getColorAttachmentsForRender(mRenderer->getWorkarounds());
const gl::AttachmentList &colorbuffers = framebufferD3D->getColorAttachmentsForRender();
BlendStateKey key = {};
key.blendState = blendState;
......
......@@ -1551,7 +1551,7 @@ gl::Error Renderer11::applyRenderTarget(const gl::Framebuffer *framebuffer)
bool missingColorRenderTarget = true;
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)
{
......
......@@ -22,8 +22,7 @@ namespace rx
{
Framebuffer9::Framebuffer9(const gl::Framebuffer::Data &data, Renderer9 *renderer)
: FramebufferD3D(data),
mRenderer(renderer)
: FramebufferD3D(data, renderer), mRenderer(renderer)
{
ASSERT(mRenderer != nullptr);
}
......
......@@ -36,14 +36,6 @@ class FramebufferGL : public FramebufferImpl
StateManagerGL *stateManager);
~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 invalidate(size_t count, const GLenum *attachments) override;
gl::Error invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area) override;
......@@ -76,6 +68,8 @@ class FramebufferGL : public FramebufferImpl
bool checkStatus() const override;
void syncState(const gl::Framebuffer::DirtyBits &dirtyBits) override;
void syncDrawState() const;
GLuint getFramebufferID() const;
......
......@@ -20,6 +20,7 @@
using namespace gl;
using namespace rx;
using testing::_;
using testing::NiceMock;
using testing::Return;
namespace
......@@ -72,19 +73,11 @@ MockValidationContext::MockValidationContext(GLint clientVersion,
// but we want a test to ensure we maintain this behaviour.
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.
MockFramebufferImpl *framebufferImpl = new MockFramebufferImpl();
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;
NiceMock<MockFactory> mockFactory;
EXPECT_CALL(mockFactory, createFramebuffer(_)).WillOnce(Return(framebufferImpl));
EXPECT_CALL(mockFactory, createProgram(_)).WillOnce(Return(programImpl));
EXPECT_CALL(mockFactory, createVertexArray(_)).WillOnce(Return(nullptr));
......@@ -101,9 +94,10 @@ TEST(ValidationESTest, DrawElementsWithMaxIndexGivesError)
caps.maxColorAttachments = 1;
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, destructor()).Times(1).RetiresOnSaturation();
Texture *texture = new Texture(textureImpl, 0, GL_TEXTURE_2D);
texture->addRef();
texture->setStorage(GL_TEXTURE_2D, 1, GL_RGBA8, Extents(1, 1, 0));
......@@ -118,8 +112,8 @@ TEST(ValidationESTest, DrawElementsWithMaxIndexGivesError)
state.setDrawFramebufferBinding(framebuffer);
state.setProgram(program);
MockValidationContext testContext(3, state, caps, textureCaps, extensions, nullptr, limitations,
false);
NiceMock<MockValidationContext> testContext(3, state, caps, textureCaps, extensions, nullptr,
limitations, false);
// Set the expectation for the validation error here.
Error expectedError(GL_INVALID_OPERATION, g_ExceedsMaxElementErrorMessage);
......
......@@ -53,6 +53,7 @@
'<(angle_path)/src/tests/gl_tests/SimpleOperationTest.cpp',
'<(angle_path)/src/tests/gl_tests/SixteenBppTextureTest.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/TextureTest.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)
mEGLWindow->setDebugEnabled(enabled);
}
void ANGLETest::setNoErrorEnabled(bool enabled)
{
mEGLWindow->setNoErrorEnabled(enabled);
}
int ANGLETest::getClientVersion() const
{
return mEGLWindow->getClientMajorVersion();
......
......@@ -103,6 +103,7 @@ class ANGLETest : public ::testing::TestWithParam<angle::PlatformParameters>
void setConfigStencilBits(int bits);
void setMultisampleEnabled(bool enabled);
void setDebugEnabled(bool enabled);
void setNoErrorEnabled(bool enabled);
int getClientVersion() const;
......
......@@ -86,6 +86,7 @@ EGLWindow::EGLWindow(EGLint glesMajorVersion,
mStencilBits(-1),
mMultisample(false),
mDebug(false),
mNoError(false),
mSwapInterval(-1)
{
}
......@@ -230,6 +231,13 @@ bool EGLWindow::initializeGL(OSWindow *osWindow)
contextAttributes.push_back(EGL_CONTEXT_OPENGL_DEBUG);
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);
......
......@@ -60,6 +60,7 @@ class EGLWindow : angle::NonCopyable
void setConfigStencilBits(int bits) { mStencilBits = bits; }
void setMultisample(bool multisample) { mMultisample = multisample; }
void setDebugEnabled(bool debug) { mDebug = debug; }
void setNoErrorEnabled(bool noError) { mNoError = noError; }
void setSwapInterval(EGLint swapInterval) { mSwapInterval = swapInterval; }
static EGLBoolean FindEGLConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *config);
......@@ -104,6 +105,7 @@ class EGLWindow : angle::NonCopyable
int mStencilBits;
bool mMultisample;
bool mDebug;
bool mNoError;
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