diff options
author | gman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-02 01:32:20 +0000 |
---|---|---|
committer | gman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-02 01:32:20 +0000 |
commit | 0d6bfdcdb3e18a84979ff9be88da7ddcd68526ec (patch) | |
tree | 0e4d33bded0601ac73434c2b24acc93477becd6d | |
parent | 1314341944011bcfff64f32e7db86d27e574c048 (diff) | |
download | chromium_src-0d6bfdcdb3e18a84979ff9be88da7ddcd68526ec.zip chromium_src-0d6bfdcdb3e18a84979ff9be88da7ddcd68526ec.tar.gz chromium_src-0d6bfdcdb3e18a84979ff9be88da7ddcd68526ec.tar.bz2 |
Defer clearing textures and renderbuffers
Textures and Renderbuffers are now cleared at the
last possible moment. This allows them to be cleared
only when absolutely necessary.
TEST=unit tests
BUG=99554
Review URL: http://codereview.chromium.org/8341128
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@108226 0039d316-1c4b-4281-b951-d872f2087c98
16 files changed, 2066 insertions, 575 deletions
diff --git a/gpu/command_buffer/service/framebuffer_manager.cc b/gpu/command_buffer/service/framebuffer_manager.cc index 909a451..0ce9df07 100644 --- a/gpu/command_buffer/service/framebuffer_manager.cc +++ b/gpu/command_buffer/service/framebuffer_manager.cc @@ -13,34 +13,36 @@ class RenderbufferAttachment : public FramebufferManager::FramebufferInfo::Attachment { public: explicit RenderbufferAttachment( - RenderbufferManager::RenderbufferInfo* render_buffer) - : render_buffer_(render_buffer) { + RenderbufferManager::RenderbufferInfo* renderbuffer) + : renderbuffer_(renderbuffer) { } virtual ~RenderbufferAttachment() { } virtual GLsizei width() const { - return render_buffer_->width(); + return renderbuffer_->width(); } virtual GLsizei height() const { - return render_buffer_->height(); + return renderbuffer_->height(); } virtual GLenum internal_format() const { - return render_buffer_->internal_format(); + return renderbuffer_->internal_format(); } virtual GLsizei samples() const { - return render_buffer_->samples(); + return renderbuffer_->samples(); } virtual bool cleared() const { - return render_buffer_->cleared(); + return renderbuffer_->cleared(); } - virtual void set_cleared() { - render_buffer_->set_cleared(); + virtual void SetCleared( + RenderbufferManager* renderbuffer_manager, + TextureManager* /* texture_manager */) { + renderbuffer_manager->SetCleared(renderbuffer_); } virtual bool IsTexture(TextureManager::TextureInfo* /* texture */) const { @@ -51,12 +53,21 @@ class RenderbufferAttachment return true; } - RenderbufferManager::RenderbufferInfo* render_buffer() const { - return render_buffer_.get(); + virtual void DetachFromFramebuffer() { + // Nothing to do for renderbuffers. + } + + virtual bool ValidForAttachmentType(GLenum attachment_type) { + // TODO(gman): Fill this out. + return true; + } + + RenderbufferManager::RenderbufferInfo* renderbuffer() const { + return renderbuffer_.get(); } private: - RenderbufferManager::RenderbufferInfo::Ref render_buffer_; + RenderbufferManager::RenderbufferInfo::Ref renderbuffer_; DISALLOW_COPY_AND_ASSIGN(RenderbufferAttachment); }; @@ -99,12 +110,13 @@ class TextureAttachment } virtual bool cleared() const { - // Textures are cleared on creation. - return true; + return texture_->IsLevelCleared(target_, level_); } - virtual void set_cleared() { - NOTREACHED(); + virtual void SetCleared( + RenderbufferManager* /* renderbuffer_manager */, + TextureManager* texture_manager) { + texture_manager->SetLevelCleared(texture_, target_, level_); } virtual bool IsTexture(TextureManager::TextureInfo* texture) const { @@ -119,6 +131,15 @@ class TextureAttachment return texture_->CanRenderTo(); } + virtual void DetachFromFramebuffer() { + texture_->DetachFromFramebuffer(); + } + + virtual bool ValidForAttachmentType(GLenum attachment_type) { + // TODO(gman): Fill this out. + return true; + } + private: TextureManager::TextureInfo::Ref texture_; GLenum target_; @@ -133,15 +154,24 @@ FramebufferManager::~FramebufferManager() { DCHECK(framebuffer_infos_.empty()); } +void FramebufferManager::FramebufferInfo::MarkAsDeleted() { + service_id_ = 0; + while (!attachments_.empty()) { + Attachment* attachment = attachments_.begin()->second.get(); + attachment->DetachFromFramebuffer(); + attachments_.erase(attachments_.begin()); + } +} + void FramebufferManager::Destroy(bool have_context) { while (!framebuffer_infos_.empty()) { - if (have_context) { - FramebufferInfo* info = framebuffer_infos_.begin()->second; - if (!info->IsDeleted()) { + FramebufferInfo* info = framebuffer_infos_.begin()->second; + if (!info->IsDeleted()) { + if (have_context) { GLuint service_id = info->service_id(); glDeleteFramebuffersEXT(1, &service_id); - info->MarkAsDeleted(); } + info->MarkAsDeleted(); } framebuffer_infos_.erase(framebuffer_infos_.begin()); } @@ -158,8 +188,8 @@ void FramebufferManager::CreateFramebufferInfo( } FramebufferManager::FramebufferInfo::FramebufferInfo(GLuint service_id) - : service_id_(service_id) - , has_been_bound_(false) { + : service_id_(service_id), + has_been_bound_(false) { } FramebufferManager::FramebufferInfo::~FramebufferInfo() {} @@ -175,12 +205,14 @@ bool FramebufferManager::FramebufferInfo::HasUnclearedAttachment( return false; } -void FramebufferManager::FramebufferInfo::MarkAttachedRenderbuffersAsCleared() { +void FramebufferManager::FramebufferInfo::MarkAttachmentsAsCleared( + RenderbufferManager* renderbuffer_manager, + TextureManager* texture_manager) { for (AttachmentMap::iterator it = attachments_.begin(); it != attachments_.end(); ++it) { Attachment* attachment = it->second; if (!attachment->cleared()) { - attachment->set_cleared(); + attachment->SetCleared(renderbuffer_manager, texture_manager); } } } @@ -204,18 +236,52 @@ GLenum FramebufferManager::FramebufferInfo::GetColorAttachmentFormat() const { return attachment->internal_format(); } -bool FramebufferManager::FramebufferInfo::IsNotComplete() const { +GLenum FramebufferManager::FramebufferInfo::IsPossiblyComplete() const { + if (attachments_.empty()) { + return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + } + + GLsizei width = -1; + GLsizei height = -1; for (AttachmentMap::const_iterator it = attachments_.begin(); it != attachments_.end(); ++it) { + GLenum attachment_type = it->first; Attachment* attachment = it->second; - if (attachment->width() == 0 || attachment->height() == 0) { - return true; + if (!attachment->ValidForAttachmentType(attachment_type)) { + return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + } + if (width < 0) { + width = attachment->width(); + height = attachment->height(); + if (width == 0 || height == 0) { + return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + } + } else { + if (attachment->width() != width || attachment->height() != height) { + return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT; + } } + if (!attachment->CanRenderTo()) { - return true; + return GL_FRAMEBUFFER_UNSUPPORTED; } } - return false; + + // This does not mean the framebuffer is actually complete. It just means our + // checks passed. + return GL_FRAMEBUFFER_COMPLETE; +} + +bool FramebufferManager::FramebufferInfo::IsCleared() const { + // are all the attachments cleaared? + for (AttachmentMap::const_iterator it = attachments_.begin(); + it != attachments_.end(); ++it) { + Attachment* attachment = it->second; + if (!attachment->cleared()) { + return false; + } + } + return true; } FramebufferManager::FramebufferInfo* FramebufferManager::GetFramebufferInfo( diff --git a/gpu/command_buffer/service/framebuffer_manager.h b/gpu/command_buffer/service/framebuffer_manager.h index 737dfff..78b3e2f 100644 --- a/gpu/command_buffer/service/framebuffer_manager.h +++ b/gpu/command_buffer/service/framebuffer_manager.h @@ -35,9 +35,13 @@ class FramebufferManager { virtual GLenum internal_format() const = 0; virtual GLsizei samples() const = 0; virtual bool cleared() const = 0; - virtual void set_cleared() = 0; + virtual void SetCleared( + RenderbufferManager* renderbuffer_manager, + TextureManager* texture_manager) = 0; virtual bool IsTexture(TextureManager::TextureInfo* texture) const = 0; virtual bool CanRenderTo() const = 0; + virtual void DetachFromFramebuffer() = 0; + virtual bool ValidForAttachmentType(GLenum attachment_type) = 0; }; explicit FramebufferInfo(GLuint service_id); @@ -58,7 +62,9 @@ class FramebufferManager { GLenum attachment, TextureManager::TextureInfo* texture, GLenum target, GLint level); - void MarkAttachedRenderbuffersAsCleared(); + void MarkAttachmentsAsCleared( + RenderbufferManager* renderbuffer_manager, + TextureManager* texture_manager); const Attachment* GetAttachment(GLenum attachment) const; @@ -78,11 +84,17 @@ class FramebufferManager { bool HasStencilAttachment() const; GLenum GetColorAttachmentFormat() const; - // We can't know if the frame buffer is complete since that is - // implementation dependent and we'd have to check after every glTexImage - // call but we can know in certain cases that it's NOT complete which we - // need to enforce the OpenGL ES 2.0 spec on top of DesktopGL. - bool IsNotComplete() const; + // Verify all the rules in OpenGL ES 2.0.25 4.4.5 are followed. + // Returns GL_FRAMEBUFFER_COMPLETE if there are no reasons we know we can't + // use this combination of attachments. Otherwise returns the value + // that glCheckFramebufferStatus should return for this set of attachments. + // Note that receiving GL_FRAMEBUFFER_COMPLETE from this function does + // not mean the real OpenGL will consider it framebuffer complete. It just + // means it passed our tests. + GLenum IsPossiblyComplete() const; + + // Check all attachments are cleared + bool IsCleared() const; private: friend class FramebufferManager; @@ -90,10 +102,7 @@ class FramebufferManager { ~FramebufferInfo(); - void MarkAsDeleted() { - service_id_ = 0; - attachments_.clear(); - } + void MarkAsDeleted(); // Service side framebuffer id. GLuint service_id_; diff --git a/gpu/command_buffer/service/framebuffer_manager_unittest.cc b/gpu/command_buffer/service/framebuffer_manager_unittest.cc index bdf5d15..264245f 100644 --- a/gpu/command_buffer/service/framebuffer_manager_unittest.cc +++ b/gpu/command_buffer/service/framebuffer_manager_unittest.cc @@ -12,11 +12,21 @@ namespace gpu { namespace gles2 { class FramebufferManagerTest : public testing::Test { + static const GLint kMaxTextureSize = 64; + static const GLint kMaxCubemapSize = 64; + static const GLint kMaxRenderbufferSize = 64; + static const GLint kMaxSamples = 4; + public: - FramebufferManagerTest() { + FramebufferManagerTest() + : texture_manager_(kMaxTextureSize, kMaxCubemapSize), + renderbuffer_manager_(kMaxRenderbufferSize, kMaxSamples) { + } ~FramebufferManagerTest() { manager_.Destroy(false); + texture_manager_.Destroy(false); + renderbuffer_manager_.Destroy(false); } protected: @@ -33,8 +43,17 @@ class FramebufferManagerTest : public testing::Test { // Use StrictMock to make 100% sure we know how GL will be called. scoped_ptr< ::testing::StrictMock< ::gfx::MockGLInterface> > gl_; FramebufferManager manager_; + TextureManager texture_manager_; + RenderbufferManager renderbuffer_manager_; }; +// GCC requires these declarations, but MSVC requires they not be present +#ifndef COMPILER_MSVC +const GLint FramebufferManagerTest::kMaxTextureSize; +const GLint FramebufferManagerTest::kMaxCubemapSize; +const GLint FramebufferManagerTest::kMaxRenderbufferSize; +#endif + TEST_F(FramebufferManagerTest, Basic) { const GLuint kClient1Id = 1; const GLuint kService1Id = 11; @@ -82,11 +101,20 @@ class FramebufferInfoTest : public testing::Test { static const GLuint kClient1Id = 1; static const GLuint kService1Id = 11; + static const GLint kMaxTextureSize = 64; + static const GLint kMaxCubemapSize = 64; + static const GLint kMaxRenderbufferSize = 64; + static const GLint kMaxSamples = 4; + FramebufferInfoTest() - : manager_() { + : manager_(), + texture_manager_(kMaxTextureSize, kMaxCubemapSize), + renderbuffer_manager_(kMaxRenderbufferSize, kMaxSamples) { } ~FramebufferInfoTest() { manager_.Destroy(false); + texture_manager_.Destroy(false); + renderbuffer_manager_.Destroy(false); } protected: @@ -107,12 +135,17 @@ class FramebufferInfoTest : public testing::Test { scoped_ptr< ::testing::StrictMock< ::gfx::MockGLInterface> > gl_; FramebufferManager manager_; FramebufferManager::FramebufferInfo* info_; + TextureManager texture_manager_; + RenderbufferManager renderbuffer_manager_; }; // GCC requires these declarations, but MSVC requires they not be present #ifndef COMPILER_MSVC const GLuint FramebufferInfoTest::kClient1Id; const GLuint FramebufferInfoTest::kService1Id; +const GLint FramebufferInfoTest::kMaxTextureSize; +const GLint FramebufferInfoTest::kMaxCubemapSize; +const GLint FramebufferInfoTest::kMaxRenderbufferSize; #endif TEST_F(FramebufferInfoTest, Basic) { @@ -124,6 +157,9 @@ TEST_F(FramebufferInfoTest, Basic) { EXPECT_TRUE(NULL == info_->GetAttachment(GL_DEPTH_STENCIL_ATTACHMENT)); EXPECT_FALSE(info_->HasDepthAttachment()); EXPECT_FALSE(info_->HasStencilAttachment()); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT), + info_->IsPossiblyComplete()); + EXPECT_TRUE(info_->IsCleared()); EXPECT_EQ(static_cast<GLenum>(0), info_->GetColorAttachmentFormat()); } @@ -132,76 +168,123 @@ TEST_F(FramebufferInfoTest, AttachRenderbuffer) { const GLuint kRenderbufferService1Id = 333; const GLuint kRenderbufferClient2Id = 34; const GLuint kRenderbufferService2Id = 334; - const GLint kMaxRenderbufferSize = 128; - const GLint kMaxSamples = 4; + const GLuint kRenderbufferClient3Id = 35; + const GLuint kRenderbufferService3Id = 335; + const GLuint kRenderbufferClient4Id = 36; + const GLuint kRenderbufferService4Id = 336; const GLsizei kWidth1 = 16; const GLsizei kHeight1 = 32; - const GLenum kFormat1 = GL_STENCIL_INDEX8; + const GLenum kFormat1 = GL_RGBA4; const GLsizei kSamples1 = 0; - const GLsizei kWidth2 = 64; - const GLsizei kHeight2 = 128; - const GLenum kFormat2 = GL_STENCIL_INDEX; + const GLsizei kWidth2 = 16; + const GLsizei kHeight2 = 32; + const GLenum kFormat2 = GL_DEPTH_COMPONENT16; const GLsizei kSamples2 = 0; - const GLsizei kWidth3 = 75; - const GLsizei kHeight3 = 123; + const GLsizei kWidth3 = 16; + const GLsizei kHeight3 = 32; const GLenum kFormat3 = GL_STENCIL_INDEX8; const GLsizei kSamples3 = 0; + const GLsizei kWidth4 = 16; + const GLsizei kHeight4 = 32; + const GLenum kFormat4 = GL_STENCIL_INDEX8; + const GLsizei kSamples4 = 0; EXPECT_FALSE(info_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0)); EXPECT_FALSE(info_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT)); EXPECT_FALSE(info_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT)); EXPECT_FALSE(info_->HasUnclearedAttachment(GL_DEPTH_STENCIL_ATTACHMENT)); - EXPECT_FALSE(info_->IsNotComplete()); - RenderbufferManager rb_manager(kMaxRenderbufferSize, kMaxSamples); - rb_manager.CreateRenderbufferInfo( + renderbuffer_manager_.CreateRenderbufferInfo( kRenderbufferClient1Id, kRenderbufferService1Id); RenderbufferManager::RenderbufferInfo* rb_info1 = - rb_manager.GetRenderbufferInfo(kRenderbufferClient1Id); + renderbuffer_manager_.GetRenderbufferInfo(kRenderbufferClient1Id); ASSERT_TRUE(rb_info1 != NULL); // check adding one attachment info_->AttachRenderbuffer(GL_COLOR_ATTACHMENT0, rb_info1); - EXPECT_TRUE(info_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0)); + EXPECT_FALSE(info_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0)); EXPECT_FALSE(info_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT)); - EXPECT_TRUE(info_->IsNotComplete()); EXPECT_EQ(static_cast<GLenum>(GL_RGBA4), info_->GetColorAttachmentFormat()); EXPECT_FALSE(info_->HasDepthAttachment()); EXPECT_FALSE(info_->HasStencilAttachment()); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT), + info_->IsPossiblyComplete()); + EXPECT_TRUE(info_->IsCleared()); - rb_info1->SetInfo(1, GL_RGB, 0, 0); - EXPECT_EQ(static_cast<GLenum>(GL_RGB), info_->GetColorAttachmentFormat()); + renderbuffer_manager_.SetInfo( + rb_info1, kSamples1, kFormat1, kWidth1, kHeight1); + EXPECT_EQ(static_cast<GLenum>(kFormat1), info_->GetColorAttachmentFormat()); EXPECT_FALSE(info_->HasDepthAttachment()); EXPECT_FALSE(info_->HasStencilAttachment()); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + info_->IsPossiblyComplete()); + EXPECT_FALSE(info_->IsCleared()); // check adding another - info_->AttachRenderbuffer(GL_DEPTH_ATTACHMENT, rb_info1); + renderbuffer_manager_.CreateRenderbufferInfo( + kRenderbufferClient2Id, kRenderbufferService2Id); + RenderbufferManager::RenderbufferInfo* rb_info2 = + renderbuffer_manager_.GetRenderbufferInfo(kRenderbufferClient2Id); + ASSERT_TRUE(rb_info2 != NULL); + info_->AttachRenderbuffer(GL_DEPTH_ATTACHMENT, rb_info2); EXPECT_TRUE(info_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0)); - EXPECT_TRUE(info_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT)); - EXPECT_TRUE(info_->IsNotComplete()); - EXPECT_EQ(static_cast<GLenum>(GL_RGB), info_->GetColorAttachmentFormat()); + EXPECT_FALSE(info_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT)); + EXPECT_EQ(static_cast<GLenum>(kFormat1), info_->GetColorAttachmentFormat()); EXPECT_TRUE(info_->HasDepthAttachment()); EXPECT_FALSE(info_->HasStencilAttachment()); + // The attachment has a size of 0,0 so depending on the order of the map + // of attachments it could either get INCOMPLETE_ATTACHMENT because it's 0,0 + // or INCOMPLETE_DIMENSIONS because it's not the same size as the other + // attachment. + GLenum status = info_->IsPossiblyComplete(); + EXPECT_TRUE( + status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT || + status == GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT); + EXPECT_FALSE(info_->IsCleared()); + + renderbuffer_manager_.SetInfo( + rb_info2, kSamples2, kFormat2, kWidth2, kHeight2); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + info_->IsPossiblyComplete()); + EXPECT_FALSE(info_->IsCleared()); + EXPECT_TRUE(info_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT)); // check marking them as cleared. - info_->MarkAttachedRenderbuffersAsCleared(); + info_->MarkAttachmentsAsCleared(&renderbuffer_manager_, &texture_manager_); EXPECT_FALSE(info_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0)); EXPECT_FALSE(info_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT)); - EXPECT_TRUE(info_->IsNotComplete()); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + info_->IsPossiblyComplete()); + EXPECT_TRUE(info_->IsCleared()); // Check adding one that is already cleared. + renderbuffer_manager_.CreateRenderbufferInfo( + kRenderbufferClient3Id, kRenderbufferService3Id); + RenderbufferManager::RenderbufferInfo* rb_info3 = + renderbuffer_manager_.GetRenderbufferInfo(kRenderbufferClient3Id); + ASSERT_TRUE(rb_info3 != NULL); + renderbuffer_manager_.SetInfo( + rb_info3, kSamples3, kFormat3, kWidth3, kHeight3); + renderbuffer_manager_.SetCleared(rb_info3); + info_->AttachRenderbuffer(GL_STENCIL_ATTACHMENT, rb_info1); EXPECT_FALSE(info_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT)); - EXPECT_EQ(static_cast<GLenum>(GL_RGB), info_->GetColorAttachmentFormat()); + EXPECT_EQ(static_cast<GLenum>(kFormat1), info_->GetColorAttachmentFormat()); EXPECT_TRUE(info_->HasDepthAttachment()); EXPECT_TRUE(info_->HasStencilAttachment()); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + info_->IsPossiblyComplete()); + EXPECT_TRUE(info_->IsCleared()); // Check marking the renderbuffer as unclared. - rb_info1->SetInfo(kSamples1, kFormat1, kWidth1, kHeight1); - EXPECT_FALSE(info_->IsNotComplete()); + renderbuffer_manager_.SetInfo( + rb_info1, kSamples1, kFormat1, kWidth1, kHeight1); EXPECT_EQ(static_cast<GLenum>(kFormat1), info_->GetColorAttachmentFormat()); EXPECT_TRUE(info_->HasDepthAttachment()); EXPECT_TRUE(info_->HasStencilAttachment()); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + info_->IsPossiblyComplete()); + EXPECT_FALSE(info_->IsCleared()); const FramebufferManager::FramebufferInfo::Attachment* attachment = info_->GetAttachment(GL_COLOR_ATTACHMENT0); @@ -215,38 +298,47 @@ TEST_F(FramebufferInfoTest, AttachRenderbuffer) { EXPECT_TRUE(info_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT)); // Clear it. - info_->MarkAttachedRenderbuffersAsCleared(); + info_->MarkAttachmentsAsCleared(&renderbuffer_manager_, &texture_manager_); EXPECT_FALSE(info_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT)); + EXPECT_TRUE(info_->IsCleared()); // Check replacing an attachment - rb_manager.CreateRenderbufferInfo( - kRenderbufferClient2Id, kRenderbufferService2Id); - RenderbufferManager::RenderbufferInfo* rb_info2 = - rb_manager.GetRenderbufferInfo(kRenderbufferClient2Id); - ASSERT_TRUE(rb_info2 != NULL); - rb_info2->SetInfo(kSamples2, kFormat2, kWidth2, kHeight2); - - info_->AttachRenderbuffer(GL_STENCIL_ATTACHMENT, rb_info2); + renderbuffer_manager_.CreateRenderbufferInfo( + kRenderbufferClient4Id, kRenderbufferService4Id); + RenderbufferManager::RenderbufferInfo* rb_info4 = + renderbuffer_manager_.GetRenderbufferInfo(kRenderbufferClient4Id); + ASSERT_TRUE(rb_info4 != NULL); + renderbuffer_manager_.SetInfo( + rb_info4, kSamples4, kFormat4, kWidth4, kHeight4); + + info_->AttachRenderbuffer(GL_STENCIL_ATTACHMENT, rb_info4); EXPECT_TRUE(info_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT)); + EXPECT_FALSE(info_->IsCleared()); attachment = info_->GetAttachment(GL_STENCIL_ATTACHMENT); ASSERT_TRUE(attachment != NULL); - EXPECT_EQ(kWidth2, attachment->width()); - EXPECT_EQ(kHeight2, attachment->height()); - EXPECT_EQ(kSamples2, attachment->samples()); - EXPECT_EQ(kFormat2, attachment->internal_format()); + EXPECT_EQ(kWidth4, attachment->width()); + EXPECT_EQ(kHeight4, attachment->height()); + EXPECT_EQ(kSamples4, attachment->samples()); + EXPECT_EQ(kFormat4, attachment->internal_format()); EXPECT_FALSE(attachment->cleared()); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + info_->IsPossiblyComplete()); // Check changing an attachment. - rb_info2->SetInfo(kSamples3, kFormat3, kWidth3, kHeight3); + renderbuffer_manager_.SetInfo( + rb_info4, kSamples4, kFormat4, kWidth4 + 1, kHeight4); attachment = info_->GetAttachment(GL_STENCIL_ATTACHMENT); ASSERT_TRUE(attachment != NULL); - EXPECT_EQ(kWidth3, attachment->width()); - EXPECT_EQ(kHeight3, attachment->height()); - EXPECT_EQ(kSamples3, attachment->samples()); - EXPECT_EQ(kFormat3, attachment->internal_format()); + EXPECT_EQ(kWidth4 + 1, attachment->width()); + EXPECT_EQ(kHeight4, attachment->height()); + EXPECT_EQ(kSamples4, attachment->samples()); + EXPECT_EQ(kFormat4, attachment->internal_format()); EXPECT_FALSE(attachment->cleared()); + EXPECT_FALSE(info_->IsCleared()); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT), + info_->IsPossiblyComplete()); // Check removing it. info_->AttachRenderbuffer(GL_STENCIL_ATTACHMENT, NULL); @@ -255,7 +347,20 @@ TEST_F(FramebufferInfoTest, AttachRenderbuffer) { EXPECT_TRUE(info_->HasDepthAttachment()); EXPECT_FALSE(info_->HasStencilAttachment()); - rb_manager.Destroy(false); + EXPECT_TRUE(info_->IsCleared()); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + info_->IsPossiblyComplete()); + + // Remove depth, Set color to 0 size. + info_->AttachRenderbuffer(GL_DEPTH_ATTACHMENT, NULL); + renderbuffer_manager_.SetInfo(rb_info1, kSamples1, kFormat1, 0, 0); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT), + info_->IsPossiblyComplete()); + + // Remove color. + info_->AttachRenderbuffer(GL_COLOR_ATTACHMENT0, NULL); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT), + info_->IsPossiblyComplete()); } TEST_F(FramebufferInfoTest, AttachTexture) { @@ -263,7 +368,6 @@ TEST_F(FramebufferInfoTest, AttachTexture) { const GLuint kTextureService1Id = 333; const GLuint kTextureClient2Id = 34; const GLuint kTextureService2Id = 334; - const GLint kMaxTextureSize = 128; const GLint kDepth = 1; const GLint kBorder = 0; const GLenum kType = GL_UNSIGNED_BYTE; @@ -273,8 +377,8 @@ TEST_F(FramebufferInfoTest, AttachTexture) { const GLenum kFormat1 = GL_RGBA; const GLenum kTarget1 = GL_TEXTURE_2D; const GLsizei kSamples1 = 0; - const GLsizei kWidth2 = 64; - const GLsizei kHeight2 = 128; + const GLsizei kWidth2 = 16; + const GLsizei kHeight2 = 32; const GLint kLevel2 = 0; const GLenum kFormat2 = GL_RGB; const GLenum kTarget2 = GL_TEXTURE_2D; @@ -288,27 +392,37 @@ TEST_F(FramebufferInfoTest, AttachTexture) { EXPECT_FALSE(info_->HasUnclearedAttachment(GL_DEPTH_ATTACHMENT)); EXPECT_FALSE(info_->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT)); EXPECT_FALSE(info_->HasUnclearedAttachment(GL_DEPTH_STENCIL_ATTACHMENT)); - EXPECT_FALSE(info_->IsNotComplete()); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT), + info_->IsPossiblyComplete()); FeatureInfo feature_info; - TextureManager tex_manager(kMaxTextureSize, kMaxTextureSize); - tex_manager.CreateTextureInfo( + texture_manager_.CreateTextureInfo( &feature_info, kTextureClient1Id, kTextureService1Id); TextureManager::TextureInfo* tex_info1 = - tex_manager.GetTextureInfo(kTextureClient1Id); + texture_manager_.GetTextureInfo(kTextureClient1Id); ASSERT_TRUE(tex_info1 != NULL); // check adding one attachment info_->AttachTexture(GL_COLOR_ATTACHMENT0, tex_info1, kTarget1, kLevel1); EXPECT_FALSE(info_->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0)); - EXPECT_TRUE(info_->IsNotComplete()); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT), + info_->IsPossiblyComplete()); + EXPECT_TRUE(info_->IsCleared()); EXPECT_EQ(static_cast<GLenum>(0), info_->GetColorAttachmentFormat()); - tex_manager.SetInfoTarget(&feature_info, tex_info1, GL_TEXTURE_2D); - tex_manager.SetLevelInfo( + texture_manager_.SetInfoTarget(&feature_info, tex_info1, GL_TEXTURE_2D); + texture_manager_.SetLevelInfo( &feature_info, tex_info1, GL_TEXTURE_2D, kLevel1, - kFormat1, kWidth1, kHeight1, kDepth, kBorder, kFormat1, kType); - EXPECT_FALSE(info_->IsNotComplete()); + kFormat1, kWidth1, kHeight1, kDepth, kBorder, kFormat1, kType, false); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + info_->IsPossiblyComplete()); + EXPECT_FALSE(info_->IsCleared()); + texture_manager_.SetLevelInfo( + &feature_info, tex_info1, GL_TEXTURE_2D, kLevel1, + kFormat1, kWidth1, kHeight1, kDepth, kBorder, kFormat1, kType, true); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + info_->IsPossiblyComplete()); + EXPECT_TRUE(info_->IsCleared()); EXPECT_EQ(static_cast<GLenum>(kFormat1), info_->GetColorAttachmentFormat()); const FramebufferManager::FramebufferInfo::Attachment* attachment = @@ -321,18 +435,21 @@ TEST_F(FramebufferInfoTest, AttachTexture) { EXPECT_TRUE(attachment->cleared()); // Check replacing an attachment - tex_manager.CreateTextureInfo( + texture_manager_.CreateTextureInfo( &feature_info, kTextureClient2Id, kTextureService2Id); TextureManager::TextureInfo* tex_info2 = - tex_manager.GetTextureInfo(kTextureClient2Id); + texture_manager_.GetTextureInfo(kTextureClient2Id); ASSERT_TRUE(tex_info2 != NULL); - tex_manager.SetInfoTarget(&feature_info, tex_info2, GL_TEXTURE_2D); - tex_manager.SetLevelInfo( + texture_manager_.SetInfoTarget(&feature_info, tex_info2, GL_TEXTURE_2D); + texture_manager_.SetLevelInfo( &feature_info, tex_info2, GL_TEXTURE_2D, kLevel2, - kFormat2, kWidth2, kHeight2, kDepth, kBorder, kFormat2, kType); + kFormat2, kWidth2, kHeight2, kDepth, kBorder, kFormat2, kType, true); info_->AttachTexture(GL_COLOR_ATTACHMENT0, tex_info2, kTarget2, kLevel2); EXPECT_EQ(static_cast<GLenum>(kFormat2), info_->GetColorAttachmentFormat()); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + info_->IsPossiblyComplete()); + EXPECT_TRUE(info_->IsCleared()); attachment = info_->GetAttachment(GL_COLOR_ATTACHMENT0); ASSERT_TRUE(attachment != NULL); @@ -343,24 +460,36 @@ TEST_F(FramebufferInfoTest, AttachTexture) { EXPECT_TRUE(attachment->cleared()); // Check changing attachment - tex_manager.SetLevelInfo( + texture_manager_.SetLevelInfo( &feature_info, tex_info2, GL_TEXTURE_2D, kLevel3, - kFormat3, kWidth3, kHeight3, kDepth, kBorder, kFormat3, kType); + kFormat3, kWidth3, kHeight3, kDepth, kBorder, kFormat3, kType, false); attachment = info_->GetAttachment(GL_COLOR_ATTACHMENT0); ASSERT_TRUE(attachment != NULL); EXPECT_EQ(kWidth3, attachment->width()); EXPECT_EQ(kHeight3, attachment->height()); EXPECT_EQ(kSamples3, attachment->samples()); EXPECT_EQ(kFormat3, attachment->internal_format()); - EXPECT_TRUE(attachment->cleared()); + EXPECT_FALSE(attachment->cleared()); EXPECT_EQ(static_cast<GLenum>(kFormat3), info_->GetColorAttachmentFormat()); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + info_->IsPossiblyComplete()); + EXPECT_FALSE(info_->IsCleared()); + + // Set to size 0 + texture_manager_.SetLevelInfo( + &feature_info, tex_info2, GL_TEXTURE_2D, kLevel3, + kFormat3, 0, 0, kDepth, kBorder, kFormat3, kType, false); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT), + info_->IsPossiblyComplete()); // Check removing it. info_->AttachTexture(GL_COLOR_ATTACHMENT0, NULL, 0, 0); EXPECT_TRUE(info_->GetAttachment(GL_COLOR_ATTACHMENT0) == NULL); EXPECT_EQ(static_cast<GLenum>(0), info_->GetColorAttachmentFormat()); - tex_manager.Destroy(false); + EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT), + info_->IsPossiblyComplete()); + EXPECT_TRUE(info_->IsCleared()); } } // namespace gles2 diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 4b80834..e3b8722 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -353,9 +353,6 @@ class FrameBuffer { // currently bound frame buffer. void AttachRenderBuffer(GLenum target, RenderBuffer* render_buffer); - // Clear the given attached buffers. - void Clear(GLbitfield buffers); - // Destroy the frame buffer. This must be explicitly called before destroying // this object. void Destroy(); @@ -512,6 +509,7 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, virtual gfx::GLSurface* GetGLSurface() { return surface_.get(); } virtual ContextGroup* GetContextGroup() { return group_.get(); } + virtual void SetGLError(GLenum error, const char* msg); virtual void SetResizeCallback(Callback1<gfx::Size>::Type* callback); #if defined(OS_MACOSX) @@ -844,10 +842,29 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, error::Error ShaderSourceHelper( GLuint client_id, const char* data, uint32 data_size); - // Clears any uncleared render buffers attached to the given frame buffer. - void ClearUnclearedRenderbuffers( + // Clear any textures used by the current program. + bool ClearUnclearedTextures(); + + // Clear any uncleared level in texture. + // Returns false if there was a generated GL error. + bool ClearTexture(TextureManager::TextureInfo* info); + + // Clears any uncleared attachments attached to the given frame buffer. + // Returns false if there was a generated GL error. + void ClearUnclearedAttachments( GLenum target, FramebufferManager::FramebufferInfo* info); + // overridden from GLES2Decoder + virtual bool ClearLevel( + unsigned service_id, + unsigned bind_target, + unsigned target, + int level, + unsigned format, + unsigned type, + int width, + int height); + // Restore all GL state that affects clearing. void RestoreClearState(); @@ -855,8 +872,15 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, // Returns: true if glEnable/glDisable should actually be called. bool SetCapabilityState(GLenum cap, bool enabled); - // Check that the current frame buffer is complete. Generates error if not. - bool CheckFramebufferComplete(const char* func_name); + // Check that the currently bound framebuffers are valid. + // Generates GL error if not. + bool CheckBoundFramebuffersValid(const char* func_name); + + // Check if a framebuffer meets our requirements. + bool CheckFramebufferValid( + FramebufferManager::FramebufferInfo* framebuffer, + GLenum target, + const char* func_name); // Checks if the current program exists and is valid. If not generates the // appropriate GL error. Returns true if the current program is in a usable @@ -1091,9 +1115,6 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, // this lets us peek at the error without losing it. GLenum PeekGLError(); - // Sets our wrapper for the GLError. - void SetGLError(GLenum error, const char* msg); - // Copies the real GL errors to the wrapper. This is so we can // make sure there are no native GL errors before calling some GL function // so that on return we know any error generated was for that specific @@ -1186,6 +1207,20 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, return (info && !info->IsDeleted()) ? info : NULL; } + RenderbufferManager::RenderbufferInfo* GetRenderbufferInfoForTarget( + GLenum target) { + RenderbufferManager::RenderbufferInfo* info = NULL; + switch (target) { + case GL_RENDERBUFFER: + info = bound_renderbuffer_; + break; + default: + NOTREACHED(); + break; + } + return (info && !info->IsDeleted()) ? info : NULL; + } + // Validates the program and location for a glGetUniform call and returns // a SizeResult setup to receive the result. Returns true if glGetUniform // should be called. @@ -1669,12 +1704,6 @@ void FrameBuffer::AttachRenderBuffer(GLenum target, attach_id); } -void FrameBuffer::Clear(GLbitfield buffers) { - ScopedGLErrorSuppressor suppressor(decoder_); - ScopedFrameBufferBinder binder(decoder_, id_); - glClear(buffers); -} - void FrameBuffer::Destroy() { if (id_ != 0) { ScopedGLErrorSuppressor suppressor(decoder_); @@ -2239,9 +2268,10 @@ bool GLES2DecoderImpl::MakeCurrent() { } void GLES2DecoderImpl::RestoreCurrentRenderbufferBindings() { + RenderbufferManager::RenderbufferInfo* renderbuffer = + GetRenderbufferInfoForTarget(GL_RENDERBUFFER); glBindRenderbufferEXT( - GL_RENDERBUFFER, - bound_renderbuffer_ ? bound_renderbuffer_->service_id() : 0); + GL_RENDERBUFFER, renderbuffer ? renderbuffer->service_id() : 0); } static void RebindCurrentFramebuffer( @@ -2290,19 +2320,59 @@ void GLES2DecoderImpl::RestoreCurrentTexture2DBindings() { glActiveTexture(GL_TEXTURE0 + active_texture_unit_); } -bool GLES2DecoderImpl::CheckFramebufferComplete(const char* func_name) { - if (bound_draw_framebuffer_ && bound_draw_framebuffer_->IsNotComplete()) { - SetGLError(GL_INVALID_FRAMEBUFFER_OPERATION, - (std::string(func_name) + " framebuffer incomplete").c_str()); +bool GLES2DecoderImpl::CheckFramebufferValid( + FramebufferManager::FramebufferInfo* framebuffer, + GLenum target, const char* func_name) { + if (!framebuffer || framebuffer->IsDeleted()) { + return true; + } + + GLenum completeness = framebuffer->IsPossiblyComplete(); + if (completeness != GL_FRAMEBUFFER_COMPLETE) { + SetGLError( + GL_INVALID_FRAMEBUFFER_OPERATION, + (std::string(func_name) + " framebuffer incomplete").c_str()); return false; } + + // Are all the attachments cleared? + if (renderbuffer_manager()->HaveUnclearedRenderbuffers() || + texture_manager()->HaveUnclearedMips()) { + if (!framebuffer->IsCleared()) { + // Can we clear them? + if (glCheckFramebufferStatusEXT(target) != GL_FRAMEBUFFER_COMPLETE) { + SetGLError( + GL_INVALID_FRAMEBUFFER_OPERATION, + (std::string(func_name) + + " framebuffer incomplete (clear)").c_str()); + return false; + } + ClearUnclearedAttachments(target, framebuffer); + } + } + + // NOTE: At this point we don't know if the framebuffer is complete but + // we DO know that everything that needs to be cleared has been cleared. return true; } +bool GLES2DecoderImpl::CheckBoundFramebuffersValid(const char* func_name) { + if (!feature_info_->feature_flags().chromium_framebuffer_multisample) { + return CheckFramebufferValid( + bound_draw_framebuffer_, GL_FRAMEBUFFER_EXT, func_name); + } + return CheckFramebufferValid( + bound_draw_framebuffer_, GL_DRAW_FRAMEBUFFER_EXT, func_name) && + CheckFramebufferValid( + bound_read_framebuffer_, GL_READ_FRAMEBUFFER_EXT, func_name); +} + gfx::Size GLES2DecoderImpl::GetBoundReadFrameBufferSize() { - if (bound_read_framebuffer_ != 0) { + FramebufferManager::FramebufferInfo* framebuffer = + GetFramebufferInfoForTarget(GL_READ_FRAMEBUFFER); + if (framebuffer != NULL) { const FramebufferManager::FramebufferInfo::Attachment* attachment = - bound_read_framebuffer_->GetAttachment(GL_COLOR_ATTACHMENT0); + framebuffer->GetAttachment(GL_COLOR_ATTACHMENT0); if (attachment) { return gfx::Size(attachment->width(), attachment->height()); } @@ -2315,8 +2385,10 @@ gfx::Size GLES2DecoderImpl::GetBoundReadFrameBufferSize() { } GLenum GLES2DecoderImpl::GetBoundReadFrameBufferInternalFormat() { - if (bound_read_framebuffer_ != 0) { - return bound_read_framebuffer_->GetColorAttachmentFormat(); + FramebufferManager::FramebufferInfo* framebuffer = + GetFramebufferInfoForTarget(GL_READ_FRAMEBUFFER); + if (framebuffer != NULL) { + return framebuffer->GetColorAttachmentFormat(); } else if (offscreen_target_frame_buffer_.get()) { return offscreen_target_color_format_; } else { @@ -2325,8 +2397,10 @@ GLenum GLES2DecoderImpl::GetBoundReadFrameBufferInternalFormat() { } GLenum GLES2DecoderImpl::GetBoundDrawFrameBufferInternalFormat() { - if (bound_draw_framebuffer_ != 0) { - return bound_draw_framebuffer_->GetColorAttachmentFormat(); + FramebufferManager::FramebufferInfo* framebuffer = + GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER); + if (framebuffer != NULL) { + return framebuffer->GetColorAttachmentFormat(); } else if (offscreen_target_frame_buffer_.get()) { return offscreen_target_color_format_; } else { @@ -2357,7 +2431,8 @@ void GLES2DecoderImpl::UpdateParentTextureInfo() { 1, // depth 0, // border GL_RGBA, - GL_UNSIGNED_BYTE); + GL_UNSIGNED_BYTE, + true); parent_texture_manager->SetParameter( feature_info_, info, @@ -2818,8 +2893,10 @@ bool GLES2DecoderImpl::BoundFramebufferHasColorAttachmentWithAlpha() { } bool GLES2DecoderImpl::BoundFramebufferHasDepthAttachment() { - if (bound_draw_framebuffer_) { - return bound_draw_framebuffer_->HasDepthAttachment(); + FramebufferManager::FramebufferInfo* framebuffer = + GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER); + if (framebuffer) { + return framebuffer->HasDepthAttachment(); } if (offscreen_target_frame_buffer_.get()) { return offscreen_target_depth_format_ != 0; @@ -2828,8 +2905,10 @@ bool GLES2DecoderImpl::BoundFramebufferHasDepthAttachment() { } bool GLES2DecoderImpl::BoundFramebufferHasStencilAttachment() { - if (bound_draw_framebuffer_) { - return bound_draw_framebuffer_->HasStencilAttachment(); + FramebufferManager::FramebufferInfo* framebuffer = + GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER); + if (framebuffer) { + return framebuffer->HasStencilAttachment(); } if (offscreen_target_frame_buffer_.get()) { return offscreen_target_stencil_format_ != 0 || @@ -3236,10 +3315,12 @@ bool GLES2DecoderImpl::GetHelper( // case GL_DRAW_FRAMEBUFFER_BINDING_EXT: (same as GL_FRAMEBUFFER_BINDING) *num_written = 1; if (params) { - if (bound_draw_framebuffer_) { + FramebufferManager::FramebufferInfo* framebuffer = + GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER); + if (framebuffer) { GLuint client_id = 0; framebuffer_manager()->GetClientId( - bound_draw_framebuffer_->service_id(), &client_id); + framebuffer->service_id(), &client_id); *params = client_id; } else { *params = 0; @@ -3249,10 +3330,12 @@ bool GLES2DecoderImpl::GetHelper( case GL_READ_FRAMEBUFFER_BINDING: *num_written = 1; if (params) { - if (bound_read_framebuffer_) { + FramebufferManager::FramebufferInfo* framebuffer = + GetFramebufferInfoForTarget(GL_READ_FRAMEBUFFER); + if (framebuffer) { GLuint client_id = 0; framebuffer_manager()->GetClientId( - bound_read_framebuffer_->service_id(), &client_id); + framebuffer->service_id(), &client_id); *params = client_id; } else { *params = 0; @@ -3262,10 +3345,12 @@ bool GLES2DecoderImpl::GetHelper( case GL_RENDERBUFFER_BINDING: *num_written = 1; if (params) { - if (bound_renderbuffer_) { + RenderbufferManager::RenderbufferInfo* renderbuffer = + GetRenderbufferInfoForTarget(GL_RENDERBUFFER); + if (renderbuffer) { GLuint client_id = 0; renderbuffer_manager()->GetClientId( - bound_renderbuffer_->service_id(), &client_id); + renderbuffer->service_id(), &client_id); *params = client_id; } else { *params = 0; @@ -3583,7 +3668,7 @@ error::Error GLES2DecoderImpl::HandleRegisterSharedIdsCHROMIUM( } void GLES2DecoderImpl::DoClear(GLbitfield mask) { - if (CheckFramebufferComplete("glClear")) { + if (CheckBoundFramebuffersValid("glClear")) { ApplyDirtyState(); glClear(mask); } @@ -3616,12 +3701,6 @@ void GLES2DecoderImpl::DoFramebufferRenderbuffer( GLenum error = PeekGLError(); if (error == GL_NO_ERROR) { framebuffer_info->AttachRenderbuffer(attachment, info); - if (service_id == 0 || - glCheckFramebufferStatusEXT(target) == GL_FRAMEBUFFER_COMPLETE) { - if (info) { - ClearUnclearedRenderbuffers(target, framebuffer_info); - } - } } if (framebuffer_info == bound_draw_framebuffer_) { state_dirty_ = true; @@ -3712,20 +3791,24 @@ void GLES2DecoderImpl::DoStencilMaskSeparate(GLenum face, GLuint mask) { state_dirty_ = true; } -// NOTE: There's an assumption here that Texture attachments -// are cleared because they are textures so we only need to clear -// the renderbuffers. -void GLES2DecoderImpl::ClearUnclearedRenderbuffers( +// Assumes framebuffer is complete. +void GLES2DecoderImpl::ClearUnclearedAttachments( GLenum target, FramebufferManager::FramebufferInfo* info) { + DCHECK(!info->IsDeleted()); if (target == GL_READ_FRAMEBUFFER_EXT) { - // TODO(gman): bind this to the DRAW point, clear then bind back to READ + // bind this to the DRAW point, clear then bind back to READ + // TODO(gman): I don't think there is any guarantee that an FBO that + // is complete on the READ attachment will be complete as a DRAW + // attachment. + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, info->service_id()); } GLbitfield clear_bits = 0; if (info->HasUnclearedAttachment(GL_COLOR_ATTACHMENT0)) { glClearColor( - 0, 0, 0, + 0.0f, 0.0f, 0.0f, (GLES2Util::GetChannelsForFormat( - info->GetColorAttachmentFormat()) & 0x0008) != 0 ? 0 : 1); + info->GetColorAttachmentFormat()) & 0x0008) != 0 ? 0.0f : 1.0f); glColorMask(true, true, true, true); clear_bits |= GL_COLOR_BUFFER_BIT; } @@ -3747,12 +3830,16 @@ void GLES2DecoderImpl::ClearUnclearedRenderbuffers( glDisable(GL_SCISSOR_TEST); glClear(clear_bits); - info->MarkAttachedRenderbuffersAsCleared(); + info->MarkAttachmentsAsCleared(renderbuffer_manager(), texture_manager()); RestoreClearState(); if (target == GL_READ_FRAMEBUFFER_EXT) { - // TODO(gman): rebind draw. + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, info->service_id()); + FramebufferManager::FramebufferInfo*framebuffer = + GetFramebufferInfoForTarget(GL_READ_FRAMEBUFFER); + glBindFramebufferEXT( + GL_DRAW_FRAMEBUFFER_EXT, framebuffer ? framebuffer->service_id() : 0); } } @@ -3767,11 +3854,15 @@ void GLES2DecoderImpl::RestoreClearState() { } GLenum GLES2DecoderImpl::DoCheckFramebufferStatus(GLenum target) { - FramebufferManager::FramebufferInfo* info = + FramebufferManager::FramebufferInfo* framebuffer = GetFramebufferInfoForTarget(target); - if (!info) { + if (!framebuffer) { return GL_FRAMEBUFFER_COMPLETE; } + GLenum completeness = framebuffer->IsPossiblyComplete(); + if (completeness != GL_FRAMEBUFFER_COMPLETE) { + return completeness; + } return glCheckFramebufferStatusEXT(target); } @@ -3796,15 +3887,19 @@ void GLES2DecoderImpl::DoFramebufferTexture2D( } service_id = info->service_id(); } + + if (!texture_manager()->ValidForTarget( + feature_info_, textarget, level, 0, 0, 1)) { + SetGLError(GL_INVALID_VALUE, + "glFramebufferTexture2D: level out of range"); + return; + } + CopyRealGLErrorsToWrapper(); glFramebufferTexture2DEXT(target, attachment, textarget, service_id, level); GLenum error = PeekGLError(); if (error == GL_NO_ERROR) { framebuffer_info->AttachTexture(attachment, info, textarget, level); - if (service_id != 0 && - glCheckFramebufferStatusEXT(target) == GL_FRAMEBUFFER_COMPLETE) { - ClearUnclearedRenderbuffers(target, framebuffer_info); - } } if (framebuffer_info == bound_draw_framebuffer_) { state_dirty_ = true; @@ -3844,20 +3939,22 @@ void GLES2DecoderImpl::DoGetFramebufferAttachmentParameteriv( void GLES2DecoderImpl::DoGetRenderbufferParameteriv( GLenum target, GLenum pname, GLint* params) { - if (!bound_renderbuffer_) { + RenderbufferManager::RenderbufferInfo* renderbuffer = + GetRenderbufferInfoForTarget(GL_RENDERBUFFER); + if (!renderbuffer) { SetGLError(GL_INVALID_OPERATION, "glGetRenderbufferParameteriv: no renderbuffer bound"); return; } switch (pname) { case GL_RENDERBUFFER_INTERNAL_FORMAT: - *params = bound_renderbuffer_->internal_format(); + *params = renderbuffer->internal_format(); break; case GL_RENDERBUFFER_WIDTH: - *params = bound_renderbuffer_->width(); + *params = renderbuffer->width(); break; case GL_RENDERBUFFER_HEIGHT: - *params = bound_renderbuffer_->height(); + *params = renderbuffer->height(); break; default: glGetRenderbufferParameterivEXT(target, pname, params); @@ -3891,6 +3988,14 @@ void GLES2DecoderImpl::DoRenderbufferStorageMultisample( return; } + RenderbufferManager::RenderbufferInfo* renderbuffer = + GetRenderbufferInfoForTarget(GL_RENDERBUFFER); + if (!renderbuffer) { + SetGLError(GL_INVALID_OPERATION, + "glGetRenderbufferStorageMultisample: no renderbuffer bound"); + return; + } + if (samples > renderbuffer_manager()->max_samples()) { SetGLError(GL_INVALID_VALUE, "glGetRenderbufferStorageMultisample: samples too large"); @@ -3930,13 +4035,16 @@ void GLES2DecoderImpl::DoRenderbufferStorageMultisample( } GLenum error = PeekGLError(); if (error == GL_NO_ERROR) { - bound_renderbuffer_->SetInfo(samples, internalformat, width, height); + renderbuffer_manager()->SetInfo( + renderbuffer, samples, internalformat, width, height); } } void GLES2DecoderImpl::DoRenderbufferStorage( GLenum target, GLenum internalformat, GLsizei width, GLsizei height) { - if (!bound_renderbuffer_) { + RenderbufferManager::RenderbufferInfo* renderbuffer = + GetRenderbufferInfoForTarget(GL_RENDERBUFFER); + if (!renderbuffer) { SetGLError(GL_INVALID_OPERATION, "glGetRenderbufferStorage: no renderbuffer bound"); return; @@ -3969,7 +4077,8 @@ void GLES2DecoderImpl::DoRenderbufferStorage( glRenderbufferStorageEXT(target, impl_format, width, height); GLenum error = PeekGLError(); if (error == GL_NO_ERROR) { - bound_renderbuffer_->SetInfo(0, internalformat, width, height); + renderbuffer_manager()->SetInfo( + renderbuffer, 0, internalformat, width, height); } } @@ -4389,6 +4498,38 @@ void GLES2DecoderImpl::RestoreStateForNonRenderableTextures() { glActiveTexture(GL_TEXTURE0 + active_texture_unit_); } +bool GLES2DecoderImpl::ClearUnclearedTextures() { + // Only check if there are some uncleared textures. + if (!texture_manager()->HaveUnsafeTextures()) { + return true; + } + + // 1: Check all textures we are about to render with. + if (current_program_) { + const ProgramManager::ProgramInfo::SamplerIndices& sampler_indices = + current_program_->sampler_indices(); + for (size_t ii = 0; ii < sampler_indices.size(); ++ii) { + const ProgramManager::ProgramInfo::UniformInfo* uniform_info = + current_program_->GetUniformInfo(sampler_indices[ii]); + DCHECK(uniform_info); + for (size_t jj = 0; jj < uniform_info->texture_units.size(); ++jj) { + GLuint texture_unit_index = uniform_info->texture_units[jj]; + if (texture_unit_index < group_->max_texture_units()) { + TextureUnit& texture_unit = texture_units_[texture_unit_index]; + TextureManager::TextureInfo* texture_info = + texture_unit.GetInfoForSamplerType(uniform_info->type); + if (texture_info && !texture_info->SafeToRenderFrom()) { + if (!texture_manager()->ClearRenderableLevels(this, texture_info)) { + return false; + } + } + } + } + } + } + return true; +} + bool GLES2DecoderImpl::IsDrawValid(GLuint max_vertex_accessed) { // NOTE: We specifically do not check current_program->IsValid() because // it could never be invalid since glUseProgram would have failed. While @@ -4623,7 +4764,7 @@ error::Error GLES2DecoderImpl::HandleDrawArrays( SetGLError(GL_INVALID_VALUE, "glDrawArrays: count < 0"); return error::kNoError; } - if (!CheckFramebufferComplete("glDrawArrays")) { + if (!CheckBoundFramebuffersValid("glDrawArrays")) { return error::kNoError; } // We have to check this here because the prototype for glDrawArrays @@ -4639,6 +4780,10 @@ error::Error GLES2DecoderImpl::HandleDrawArrays( GLuint max_vertex_accessed = first + count - 1; if (IsDrawValid(max_vertex_accessed)) { + if (!ClearUnclearedTextures()) { + SetGLError(GL_INVALID_VALUE, "glDrawArrays: out of memory"); + return error::kNoError; + } bool simulated_attrib_0 = false; if (!SimulateAttrib0(max_vertex_accessed, &simulated_attrib_0)) { return error::kNoError; @@ -4696,7 +4841,7 @@ error::Error GLES2DecoderImpl::HandleDrawElements( return error::kNoError; } - if (!CheckFramebufferComplete("glDrawElements")) { + if (!CheckBoundFramebuffersValid("glDrawElements")) { return error::kNoError; } @@ -4713,6 +4858,10 @@ error::Error GLES2DecoderImpl::HandleDrawElements( } if (IsDrawValid(max_vertex_accessed)) { + if (!ClearUnclearedTextures()) { + SetGLError(GL_INVALID_VALUE, "glDrawElements: out of memory"); + return error::kNoError; + } bool simulated_attrib_0 = false; if (!SimulateAttrib0(max_vertex_accessed, &simulated_attrib_0)) { return error::kNoError; @@ -5395,6 +5544,10 @@ error::Error GLES2DecoderImpl::HandleReadPixels( return error::kNoError; } + if (!CheckBoundFramebuffersValid("glReadPixels")) { + return error::kNoError; + } + if (x < 0 || y < 0 || max_x > max_size.width() || max_y > max_size.height()) { // The user requested an out of range area. Get the results 1 line // at a time. @@ -5810,6 +5963,31 @@ void GLES2DecoderImpl::DoBufferSubData( glBufferSubData(target, offset, size, data); } +bool GLES2DecoderImpl::ClearLevel( + unsigned service_id, + unsigned bind_target, + unsigned target, + int level, + unsigned format, + unsigned type, + int width, + int height) { + // Assumes the size has already been checked. + uint32 pixels_size = 0; + if (!GLES2Util::ComputeImageDataSize( + width, height, format, type, unpack_alignment_, &pixels_size)) { + return false; + } + scoped_array<char> zero(new char[pixels_size]); + memset(zero.get(), 0, pixels_size); + glBindTexture(bind_target, service_id); + WrappedTexImage2D( + target, level, format, width, height, 0, format, type, zero.get()); + TextureManager::TextureInfo* info = GetTextureInfoForTarget(bind_target); + glBindTexture(bind_target, info ? info->service_id() : 0); + return true; +} + error::Error GLES2DecoderImpl::DoCompressedTexImage2D( GLenum target, GLint level, @@ -5857,7 +6035,8 @@ error::Error GLES2DecoderImpl::DoCompressedTexImage2D( if (error == GL_NO_ERROR) { texture_manager()->SetLevelInfo( feature_info_, - info, target, level, internal_format, width, height, 1, border, 0, 0); + info, target, level, internal_format, width, height, 1, border, 0, 0, + true); } return error::kNoError; } @@ -6033,13 +6212,6 @@ error::Error GLES2DecoderImpl::DoTexImage2D( return error::kNoError; } - scoped_array<int8> zero; - if (!pixels) { - zero.reset(new int8[pixels_size]); - memset(zero.get(), 0, pixels_size); - pixels = zero.get(); - } - if (info->IsAttachedToFramebuffer()) { state_dirty_ = true; } @@ -6056,8 +6228,10 @@ error::Error GLES2DecoderImpl::DoTexImage2D( pixels); GLenum error = PeekGLError(); if (error == GL_NO_ERROR) { - texture_manager()->SetLevelInfo(feature_info_, info, - target, level, internal_format, width, height, 1, border, format, type); + texture_manager()->SetLevelInfo( + feature_info_, info, + target, level, internal_format, width, height, 1, border, format, type, + pixels != NULL); tex_image_2d_failed_ = false; } return error::kNoError; @@ -6157,6 +6331,10 @@ void GLES2DecoderImpl::DoCompressedTexSubImage2D( "glCompressdTexSubImage2D: bad dimensions."); return; } + // Note: There is no need to deal with texture cleared tracking here + // because the validation above means you can only get here if the level + // is already a matching compressed format and in that case + // CompressedTexImage2D already cleared the texture. glCompressedTexSubImage2D( target, level, xoffset, yoffset, width, height, format, image_size, data); } @@ -6178,7 +6356,6 @@ static void Clip( *out_range = range; } - void GLES2DecoderImpl::DoCopyTexImage2D( GLenum target, GLint level, @@ -6232,17 +6409,12 @@ void GLES2DecoderImpl::DoCopyTexImage2D( copyWidth != width || copyHeight != height) { // some part was clipped so clear the texture. - uint32 pixels_size = 0; - if (!GLES2Util::ComputeImageDataSize( - width, height, internal_format, GL_UNSIGNED_BYTE, - unpack_alignment_, &pixels_size)) { - SetGLError(GL_INVALID_VALUE, "glCopyTexImage2D: dimensions too large"); + if (!ClearLevel( + info->service_id(), info->target(), + target, level, internal_format, GL_UNSIGNED_BYTE, width, height)) { + SetGLError(GL_OUT_OF_MEMORY, "glCopyTexImage2D: dimensions too big"); return; } - scoped_array<char> zero(new char[pixels_size]); - memset(zero.get(), 0, pixels_size); - glTexImage2D(target, level, internal_format, width, height, 0, - internal_format, GL_UNSIGNED_BYTE, zero.get()); if (copyHeight > 0 && copyWidth > 0) { GLint dx = copyX - x; GLint dy = copyY - y; @@ -6260,7 +6432,7 @@ void GLES2DecoderImpl::DoCopyTexImage2D( if (error == GL_NO_ERROR) { texture_manager()->SetLevelInfo( feature_info_, info, target, level, internal_format, width, height, 1, - border, internal_format, GL_UNSIGNED_BYTE); + border, internal_format, GL_UNSIGNED_BYTE, true); } } @@ -6308,11 +6480,17 @@ void GLES2DecoderImpl::DoCopyTexSubImage2D( GLint copyHeight = 0; Clip(x, width, size.width(), ©X, ©Width); Clip(y, height, size.height(), ©Y, ©Height); + + if (!texture_manager()->ClearTextureLevel(this, info, target, level)) { + SetGLError(GL_OUT_OF_MEMORY, "glCopyTexSubImage2D: dimensions too big"); + return; + } + if (copyX != x || copyY != y || copyWidth != width || copyHeight != height) { - // some part was clipped so clear the texture. + // some part was clipped so clear the sub rect. uint32 pixels_size = 0; if (!GLES2Util::ComputeImageDataSize( width, height, format, type, unpack_alignment_, &pixels_size)) { @@ -6325,6 +6503,7 @@ void GLES2DecoderImpl::DoCopyTexSubImage2D( target, level, xoffset, yoffset, width, height, format, type, zero.get()); } + if (copyHeight > 0 && copyWidth > 0) { GLint dx = copyX - x; GLint dy = copyY - y; @@ -6389,9 +6568,14 @@ void GLES2DecoderImpl::DoTexSubImage2D( // same as internal_foramt. If that changes we'll need to look them up. WrappedTexImage2D( target, level, format, width, height, 0, format, type, data); + texture_manager()->SetLevelCleared(info, target, level); return; } } + if (!texture_manager()->ClearTextureLevel(this, info, target, level)) { + SetGLError(GL_OUT_OF_MEMORY, "glTexSubImage2D: dimensions too big"); + return; + } glTexSubImage2D( target, level, xoffset, yoffset, width, height, format, type, data); } diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h index 118c27d..6398bff 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder.h @@ -122,6 +122,18 @@ class GLES2Decoder : public CommonDecoder { // Provides detail about a lost context if one occurred. virtual error::ContextLostReason GetContextLostReason() = 0; + // Clears a level of a texture + // Returns false if a GL error should be generated. + virtual bool ClearLevel( + unsigned service_id, + unsigned bind_target, + unsigned target, + int level, + unsigned format, + unsigned type, + int width, + int height) = 0; + protected: GLES2Decoder(); diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h index 6eb313d..7cdd56c 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h @@ -56,6 +56,15 @@ class MockGLES2Decoder : public GLES2Decoder { uint32* service_texture_id)); MOCK_METHOD0(GetContextLostReason, error::ContextLostReason()); MOCK_CONST_METHOD1(GetCommandName, const char*(unsigned int command_id)); + MOCK_METHOD8(ClearLevel, bool( + unsigned service_id, + unsigned bind_target, + unsigned target, + int level, + unsigned format, + unsigned type, + int width, + int height)); DISALLOW_COPY_AND_ASSIGN(MockGLES2Decoder); }; diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc index 2a1e663..2790dab 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc @@ -56,51 +56,6 @@ class GLES2DecoderWithShaderTest : public GLES2DecoderWithShaderTestBase { GLES2DecoderWithShaderTest() : GLES2DecoderWithShaderTestBase() { } - - - void AddExpectationsForSimulatedAttrib0WithError( - GLsizei num_vertices, GLuint buffer_id, GLenum error) { - if (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2) { - return; - } - - EXPECT_CALL(*gl_, GetError()) - .WillOnce(Return(GL_NO_ERROR)) - .WillOnce(Return(error)) - .RetiresOnSaturation(); - EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, kServiceAttrib0BufferId)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*gl_, BufferData(GL_ARRAY_BUFFER, - num_vertices * sizeof(GLfloat) * 4, - _, GL_DYNAMIC_DRAW)) - .Times(1) - .RetiresOnSaturation(); - if (error == GL_NO_ERROR) { - EXPECT_CALL(*gl_, BufferSubData( - GL_ARRAY_BUFFER, 0, num_vertices * sizeof(GLfloat) * 4, _)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*gl_, VertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, 0)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*gl_, VertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, buffer_id)) - .Times(1) - .RetiresOnSaturation(); - } - } - - void AddExpectationsForSimulatedAttrib0( - GLsizei num_vertices, GLuint buffer_id) { - AddExpectationsForSimulatedAttrib0WithError( - num_vertices, buffer_id, GL_NO_ERROR); - } }; class GLES2DecoderRGBBackbufferTest : public GLES2DecoderWithShaderTest { @@ -190,7 +145,7 @@ TEST_F(GLES2DecoderWithShaderTest, DrawArraysBadTextureUsesBlack) { // This is an NPOT texture. As the default filtering requires mips // this should trigger replacing with black textures before rendering. DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 3, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, - 0, 0); + kSharedMemoryId, kSharedMemoryOffset); AddExpectationsForSimulatedAttrib0(kNumVertices, 0); { InSequence sequence; @@ -1455,12 +1410,6 @@ TEST_F(GLES2DecoderTest, GetFramebufferAttachmentParameterivWithRenderbuffer) { EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); - SetupExpectationsForFramebufferAttachment( - GL_COLOR_BUFFER_BIT, // clear bits - 0, 0, 0, 0, // color - 0, // stencil - 1.0f, // depth - false); // scissor test EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) @@ -1511,12 +1460,6 @@ TEST_F(GLES2DecoderTest, GetFramebufferAttachmentParameterivWithTexture) { EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); - SetupExpectationsForFramebufferAttachment( - 0, // clear bits - 0, 0, 0, 0, // color - 0, // stencil - 1.0f, // depth - false); // scissor test EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) @@ -1674,32 +1617,15 @@ void GLES2DecoderTest::CheckReadPixelsOutOfRange( // access if (init) { DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); - DoTexImage2D(GL_TEXTURE_2D, 0, kFormat, kWidth, kHeight, 0, - kFormat, GL_UNSIGNED_BYTE, 0, 0); - DoBindFramebuffer(GL_FRAMEBUFFER, client_framebuffer_id_, - kServiceFramebufferId); - EXPECT_CALL(*gl_, GetError()) - .WillOnce(Return(GL_NO_ERROR)) - .RetiresOnSaturation(); - EXPECT_CALL(*gl_, FramebufferTexture2DEXT( + DoTexImage2D( + GL_TEXTURE_2D, 0, kFormat, kWidth, kHeight, 0, + kFormat, GL_UNSIGNED_BYTE, kSharedMemoryId, + kSharedMemoryOffset); + DoBindFramebuffer( + GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); + DoFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - kServiceTextureId, 0)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*gl_, GetError()) - .WillOnce(Return(GL_NO_ERROR)) - .RetiresOnSaturation(); - SetupExpectationsForFramebufferAttachment( - 0, // clear bits - 0, 0, 0, 0, // color - 0, // stencil - 1.0f, // depth - false); // scissor test - FramebufferTexture2D fbtex_cmd; - fbtex_cmd.Init( - GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, client_texture_id_, - 0); - EXPECT_EQ(error::kNoError, ExecuteCmd(fbtex_cmd)); + client_texture_id_, kServiceTextureId, 0, GL_NO_ERROR); } ReadPixelsEmulator emu( @@ -2559,7 +2485,7 @@ TEST_F(GLES2DecoderTest, TexSubImage2DValidArgs) { DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); DoTexImage2D( GL_TEXTURE_2D, 1, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, - 0, 0); + kSharedMemoryId, kSharedMemoryOffset); EXPECT_CALL(*gl_, TexSubImage2D( GL_TEXTURE_2D, 1, 1, 0, kWidth - 1, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, shared_memory_address_)) @@ -2640,7 +2566,7 @@ TEST_F(GLES2DecoderTest, CopyTexSubImage2DValidArgs) { DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); DoTexImage2D( GL_TEXTURE_2D, 1, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, - 0, 0); + kSharedMemoryId, kSharedMemoryOffset); EXPECT_CALL(*gl_, CopyTexSubImage2D( GL_TEXTURE_2D, 1, 0, 0, 0, 0, kWidth, kHeight)) .Times(1) @@ -2701,9 +2627,6 @@ TEST_F(GLES2DecoderTest, FramebufferRenderbufferClearColor) { EXPECT_CALL(*gl_, ClearColor(0.1f, 0.2f, 0.3f, 0.4f)) .Times(1) .RetiresOnSaturation(); -// EXPECT_CALL(*gl_, ColorMask(0, 1, 0, 1)) -// .Times(0) -// .RetiresOnSaturation(); EXPECT_CALL(*gl_, Enable(GL_SCISSOR_TEST)) .Times(1) .RetiresOnSaturation(); @@ -2718,12 +2641,6 @@ TEST_F(GLES2DecoderTest, FramebufferRenderbufferClearColor) { EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); - SetupExpectationsForFramebufferAttachment( - GL_COLOR_BUFFER_BIT, // clear bits - 0.1f, 0.2f, 0.3f, 0.4f, // color - 0, // stencil - 1.0f, // depth - true); // scissor test EXPECT_EQ(error::kNoError, ExecuteCmd(color_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(color_mask_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(enable_cmd)); @@ -2745,9 +2662,6 @@ TEST_F(GLES2DecoderTest, FramebufferRenderbufferClearDepth) { EXPECT_CALL(*gl_, ClearDepth(0.5f)) .Times(1) .RetiresOnSaturation(); -// EXPECT_CALL(*gl_, DepthMask(0)) -// .Times(1) -// .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); @@ -2759,12 +2673,6 @@ TEST_F(GLES2DecoderTest, FramebufferRenderbufferClearDepth) { EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); - SetupExpectationsForFramebufferAttachment( - GL_DEPTH_BUFFER_BIT, // clear bits - 0, 0, 0, 0, // color - 0, // stencil - 0.5f, // depth - false); // scissor test EXPECT_EQ(error::kNoError, ExecuteCmd(depth_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(depth_mask_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); @@ -2785,9 +2693,6 @@ TEST_F(GLES2DecoderTest, FramebufferRenderbufferClearStencil) { EXPECT_CALL(*gl_, ClearStencil(123)) .Times(1) .RetiresOnSaturation(); -// EXPECT_CALL(*gl_, StencilMaskSeparate(GL_BACK, 0x1234u)) -// .Times(1) -// .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); @@ -2799,12 +2704,6 @@ TEST_F(GLES2DecoderTest, FramebufferRenderbufferClearStencil) { EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); - SetupExpectationsForFramebufferAttachment( - GL_STENCIL_BUFFER_BIT, // clear bits - 0, 0, 0, 0, // color - 123, // stencil - 1.0f, // depth - false); // scissor test EXPECT_EQ(error::kNoError, ExecuteCmd(stencil_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(stencil_mask_separate_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); @@ -2885,12 +2784,6 @@ TEST_F(GLES2DecoderTest, FramebufferRenderbufferClearDepthStencil) { kServiceRenderbufferId)) .Times(1) .RetiresOnSaturation(); - SetupExpectationsForFramebufferAttachment( - GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, // clear bits - 0, 0, 0, 0, // color - 123, // stencil - 0.5f, // depth - false); // scissor test EXPECT_EQ(error::kNoError, ExecuteCmd(depth_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(stencil_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); @@ -3230,15 +3123,20 @@ TEST_F(GLES2DecoderRGBBackbufferTest, RGBBackbufferColorMaskFBO) { EXPECT_EQ(GL_NO_ERROR, GetGLError()); // Setup Frame buffer. - DoBindFramebuffer(GL_FRAMEBUFFER, client_framebuffer_id_, - kServiceFramebufferId); - EXPECT_CALL(*gl_, FramebufferRenderbufferEXT(_, _, _, _)) - .Times(0); - FramebufferRenderbuffer fbrb_cmd; - fbrb_cmd.Init( - GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, - client_renderbuffer_id_); - EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + // needs to be 1x1 or else it's not renderable. + const GLsizei kWidth = 1; + const GLsizei kHeight = 1; + const GLenum kFormat = GL_RGB; + DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); + // Pass some data so the texture will be marked as cleared. + DoTexImage2D( + GL_TEXTURE_2D, 0, kFormat, kWidth, kHeight, 0, + kFormat, GL_UNSIGNED_BYTE, kSharedMemoryId, kSharedMemoryOffset); + DoBindFramebuffer( + GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); + DoFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + client_texture_id_, kServiceTextureId, 0, GL_NO_ERROR); // This time state needs to be set. SetupExpectationsForApplyingDirtyState( @@ -3825,12 +3723,6 @@ TEST_F(GLES2DecoderManualInitTest, PackedDepthStencilRenderbufferDepth) { kServiceRenderbufferId)) .Times(1) .RetiresOnSaturation(); - SetupExpectationsForFramebufferAttachment( - GL_DEPTH_BUFFER_BIT, // clear bits - 0, 0, 0, 0, // color - 0, // stencil - 1.0f, // depth - false); // scissor test FramebufferRenderbuffer fbrb_cmd; fbrb_cmd.Init( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, @@ -3902,12 +3794,6 @@ TEST_F(GLES2DecoderManualInitTest, PackedDepthStencilRenderbufferStencil) { kServiceRenderbufferId)) .Times(1) .RetiresOnSaturation(); - SetupExpectationsForFramebufferAttachment( - GL_STENCIL_BUFFER_BIT, // clear bits - 0, 0, 0, 0, // color - 0, // stencil - 1.0f, // depth - false); // scissor test FramebufferRenderbuffer fbrb_cmd; fbrb_cmd.Init( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, @@ -4078,17 +3964,21 @@ TEST_F(GLES2DecoderTest, TexImage2DRedefinitionSucceeds) { EXPECT_CALL(*gl_, GetError()) .WillRepeatedly(Return(GL_NO_ERROR)); for (int ii = 0; ii < 2; ++ii) { + TexImage2D cmd; if (ii == 0) { EXPECT_CALL(*gl_, TexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, _)) .Times(1) .RetiresOnSaturation(); + cmd.Init( + GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, + GL_UNSIGNED_BYTE, kSharedMemoryId, kSharedMemoryOffset); + } else { + cmd.Init( + GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, + GL_UNSIGNED_BYTE, 0, 0); } - TexImage2D cmd; - cmd.Init( - GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, - GL_UNSIGNED_BYTE, 0, 0); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_CALL(*gl_, TexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight - 1, GL_RGBA, GL_UNSIGNED_BYTE, @@ -5005,6 +4895,512 @@ TEST_F(GLES2DecoderTest, RequestExtensionCHROMIUMBadBucket) { EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); } +TEST_F(GLES2DecoderTest, TexSubImage2DClearsAfterTexImage2DNULL) { + DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); + DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, + 0, 0); + SetupClearTextureExpections( + kServiceTextureId, kServiceTextureId, GL_TEXTURE_2D, GL_TEXTURE_2D, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 2, 2); + EXPECT_CALL(*gl_, TexSubImage2D( + GL_TEXTURE_2D, 0, 1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + shared_memory_address_)) + .Times(1) + .RetiresOnSaturation(); + TexSubImage2D cmd; + cmd.Init( + GL_TEXTURE_2D, 0, 1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + kSharedMemoryId, kSharedMemoryOffset, GL_FALSE); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + // Test if we call it again it does not clear. + EXPECT_CALL(*gl_, TexSubImage2D( + GL_TEXTURE_2D, 0, 1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + shared_memory_address_)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); +} + +TEST_F(GLES2DecoderTest, + TexSubImage2DDoesNotClearAfterTexImage2DWithDataThenNULL) { + DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); + // Put in data (so it should be marked as cleared) + DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, + kSharedMemoryId, kSharedMemoryOffset); + // Put in no data. + TexImage2D tex_cmd; + tex_cmd.Init( + GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0); + // There is no expectation. Same size, no data = no-op. + EXPECT_EQ(error::kNoError, ExecuteCmd(tex_cmd)); + EXPECT_CALL(*gl_, TexSubImage2D( + GL_TEXTURE_2D, 0, 1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + shared_memory_address_)) + .Times(1) + .RetiresOnSaturation(); + TexSubImage2D cmd; + cmd.Init( + GL_TEXTURE_2D, 0, 1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + kSharedMemoryId, kSharedMemoryOffset, GL_FALSE); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawArraysClearsAfterTexImage2DNULL) { + DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); + // Create an uncleared texture with 2 levels. + DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, + 0, 0); + DoTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, + 0, 0); + // Expect 2 levels will be cleared. + SetupClearTextureExpections( + kServiceTextureId, kServiceTextureId, GL_TEXTURE_2D, GL_TEXTURE_2D, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 2, 2); + SetupClearTextureExpections( + kServiceTextureId, kServiceTextureId, GL_TEXTURE_2D, GL_TEXTURE_2D, + 1, GL_RGBA, GL_UNSIGNED_BYTE, 1, 1); + AddExpectationsForSimulatedAttrib0(kNumVertices, 0); + SetupExpectationsForApplyingDefaultDirtyState(); + EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices)) + .Times(1) + .RetiresOnSaturation(); + DrawArrays cmd; + cmd.Init(GL_TRIANGLES, 0, kNumVertices); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawElementsClearsAfterTexImage2DNULL) { + SetupIndexBuffer(); + DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); + // Create an uncleared texture with 2 levels. + DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, + 0, 0); + DoTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, + 0, 0); + // Expect 2 levels will be cleared. + SetupClearTextureExpections( + kServiceTextureId, kServiceTextureId, GL_TEXTURE_2D, GL_TEXTURE_2D, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 2, 2); + SetupClearTextureExpections( + kServiceTextureId, kServiceTextureId, GL_TEXTURE_2D, GL_TEXTURE_2D, + 1, GL_RGBA, GL_UNSIGNED_BYTE, 1, 1); + AddExpectationsForSimulatedAttrib0(kMaxValidIndex + 1, 0); + SetupExpectationsForApplyingDefaultDirtyState(); + EXPECT_CALL(*gl_, DrawElements(GL_TRIANGLES, kValidIndexRangeCount, + GL_UNSIGNED_SHORT, + BufferOffset(kValidIndexRangeStart * 2))) + .Times(1) + .RetiresOnSaturation(); + DrawElements cmd; + cmd.Init(GL_TRIANGLES, kValidIndexRangeCount, GL_UNSIGNED_SHORT, + kValidIndexRangeStart * 2); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawClearsAfterTexImage2DNULLInFBO) { + const GLuint kFBOClientTextureId = 4100; + const GLuint kFBOServiceTextureId = 4101; + + // Register a texture id. + EXPECT_CALL(*gl_, GenTextures(_, _)) + .WillOnce(SetArgumentPointee<1>(kFBOServiceTextureId)) + .RetiresOnSaturation(); + GenHelper<GenTexturesImmediate>(kFBOClientTextureId); + + // Setup "render to" texture. + DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId); + DoTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0); + DoBindFramebuffer( + GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); + DoFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + kFBOClientTextureId, kFBOServiceTextureId, 0, GL_NO_ERROR); + + // Setup "render from" texture. + SetupTexture(); + + SetupExpectationsForFramebufferClearing( + GL_FRAMEBUFFER, // target + GL_COLOR_BUFFER_BIT, // clear bits + 0, 0, 0, 0, // color + 0, // stencil + 1.0f, // depth + false); // scissor test + + AddExpectationsForSimulatedAttrib0(kNumVertices, 0); + SetupExpectationsForApplyingDirtyState( + false, // Framebuffer is RGB + false, // Framebuffer has depth + false, // Framebuffer has stencil + 0x1111, // color bits + false, // depth mask + false, // depth enabled + 0, // front stencil mask + 0, // back stencil mask + false); // stencil enabled + EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices)) + .Times(1) + .RetiresOnSaturation(); + DrawArrays cmd; + cmd.Init(GL_TRIANGLES, 0, kNumVertices); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawWitFBOThatCantClearDoesNotDraw) { + const GLuint kFBOClientTextureId = 4100; + const GLuint kFBOServiceTextureId = 4101; + + // Register a texture id. + EXPECT_CALL(*gl_, GenTextures(_, _)) + .WillOnce(SetArgumentPointee<1>(kFBOServiceTextureId)) + .RetiresOnSaturation(); + GenHelper<GenTexturesImmediate>(kFBOClientTextureId); + + // Setup "render to" texture. + DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId); + DoTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0); + DoBindFramebuffer( + GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); + DoFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + kFBOClientTextureId, kFBOServiceTextureId, 0, GL_NO_ERROR); + + // Setup "render from" texture. + SetupTexture(); + + EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_FRAMEBUFFER)) + .WillOnce(Return(GL_FRAMEBUFFER_UNSUPPORTED)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, DrawArrays(_, _, _)) + .Times(0) + .RetiresOnSaturation(); + DrawArrays cmd; + cmd.Init(GL_TRIANGLES, 0, kNumVertices); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_FRAMEBUFFER_OPERATION, GetGLError()); +} + +TEST_F(GLES2DecoderTest, CopyTexImage2DMarksTextureAsCleared) { + DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); + + TextureManager* manager = group().texture_manager(); + TextureManager::TextureInfo* info = + manager->GetTextureInfo(client_texture_id_); + + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, CopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1, 1, 0)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + CopyTexImage2D cmd; + cmd.Init(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1, 1, 0); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + + EXPECT_TRUE(info->SafeToRenderFrom()); +} + +TEST_F(GLES2DecoderTest, CopyTexSubImage2DClearsUnclearedTexture) { + DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); + DoTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0); + + SetupClearTextureExpections( + kServiceTextureId, kServiceTextureId, GL_TEXTURE_2D, GL_TEXTURE_2D, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 2, 2); + EXPECT_CALL(*gl_, CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1)) + .Times(1) + .RetiresOnSaturation(); + CopyTexSubImage2D cmd; + cmd.Init(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); +} + +TEST_F(GLES2DecoderManualInitTest, CompressedImage2DMarksTextureAsCleared) { + InitDecoder( + "GL_EXT_texture_compression_s3tc", // extensions + false, // has alpha + false, // has depth + false, // has stencil + false, // request alpha + false, // request depth + false, // request stencil + true); // bind generates resource + + DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, CompressedTexImage2D( + GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0, 16, _)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + CompressedTexImage2D cmd; + cmd.Init(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0, + 16, kSharedMemoryId, kSharedMemoryOffset); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + TextureManager* manager = group().texture_manager(); + TextureManager::TextureInfo* info = + manager->GetTextureInfo(client_texture_id_); + EXPECT_TRUE(info->SafeToRenderFrom()); +} + +TEST_F(GLES2DecoderWithShaderTest, UnClearedAttachmentsGetClearedOnClear) { + const GLuint kFBOClientTextureId = 4100; + const GLuint kFBOServiceTextureId = 4101; + + // Register a texture id. + EXPECT_CALL(*gl_, GenTextures(_, _)) + .WillOnce(SetArgumentPointee<1>(kFBOServiceTextureId)) + .RetiresOnSaturation(); + GenHelper<GenTexturesImmediate>(kFBOClientTextureId); + + // Setup "render to" texture. + DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId); + DoTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0); + DoBindFramebuffer( + GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); + DoFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + kFBOClientTextureId, kFBOServiceTextureId, 0, GL_NO_ERROR); + + // Setup "render from" texture. + SetupTexture(); + + SetupExpectationsForFramebufferClearing( + GL_FRAMEBUFFER, // target + GL_COLOR_BUFFER_BIT, // clear bits + 0, 0, 0, 0, // color + 0, // stencil + 1.0f, // depth + false); // scissor test + SetupExpectationsForApplyingDirtyState( + false, // Framebuffer is RGB + false, // Framebuffer has depth + false, // Framebuffer has stencil + 0x1111, // color bits + false, // depth mask + false, // depth enabled + 0, // front stencil mask + 0, // back stencil mask + false); // stencil enabled + EXPECT_CALL(*gl_, Clear(GL_COLOR_BUFFER_BIT)) + .Times(1) + .RetiresOnSaturation(); + + Clear cmd; + cmd.Init(GL_COLOR_BUFFER_BIT); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, UnClearedAttachmentsGetClearedOnReadPixels) { + const GLuint kFBOClientTextureId = 4100; + const GLuint kFBOServiceTextureId = 4101; + + // Register a texture id. + EXPECT_CALL(*gl_, GenTextures(_, _)) + .WillOnce(SetArgumentPointee<1>(kFBOServiceTextureId)) + .RetiresOnSaturation(); + GenHelper<GenTexturesImmediate>(kFBOClientTextureId); + + // Setup "render to" texture. + DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId); + DoTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0); + DoBindFramebuffer( + GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); + DoFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + kFBOClientTextureId, kFBOServiceTextureId, 0, GL_NO_ERROR); + + // Setup "render from" texture. + SetupTexture(); + + SetupExpectationsForFramebufferClearing( + GL_FRAMEBUFFER, // target + GL_COLOR_BUFFER_BIT, // clear bits + 0, 0, 0, 0, // color + 0, // stencil + 1.0f, // depth + false); // scissor test + + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, ReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, _)) + .Times(1) + .RetiresOnSaturation(); + typedef ReadPixels::Result Result; + Result* result = GetSharedMemoryAs<Result*>(); + uint32 result_shm_id = kSharedMemoryId; + uint32 result_shm_offset = kSharedMemoryOffset; + uint32 pixels_shm_id = kSharedMemoryId; + uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(*result); + ReadPixels cmd; + cmd.Init(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + pixels_shm_id, pixels_shm_offset, + result_shm_id, result_shm_offset); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + +TEST_F(GLES2DecoderWithShaderTest, DrawClearsAfterRenderbufferStorageInFBO) { + SetupTexture(); + DoBindRenderbuffer(GL_RENDERBUFFER, client_renderbuffer_id_, + kServiceRenderbufferId); + DoBindFramebuffer(GL_FRAMEBUFFER, client_framebuffer_id_, + kServiceFramebufferId); + DoRenderbufferStorage( + GL_RENDERBUFFER, GL_RGBA4, GL_RGBA, 100, 50, GL_NO_ERROR); + DoFramebufferRenderbuffer( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, + client_renderbuffer_id_, kServiceRenderbufferId, GL_NO_ERROR); + + SetupExpectationsForFramebufferClearing( + GL_FRAMEBUFFER, // target + GL_COLOR_BUFFER_BIT, // clear bits + 0, 0, 0, 0, // color + 0, // stencil + 1.0f, // depth + false); // scissor test + + AddExpectationsForSimulatedAttrib0(kNumVertices, 0); + SetupExpectationsForApplyingDirtyState( + false, // Framebuffer is RGB + false, // Framebuffer has depth + false, // Framebuffer has stencil + 0x1111, // color bits + false, // depth mask + false, // depth enabled + 0, // front stencil mask + 0, // back stencil mask + false); // stencil enabled + EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices)) + .Times(1) + .RetiresOnSaturation(); + DrawArrays cmd; + cmd.Init(GL_TRIANGLES, 0, kNumVertices); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + +TEST_F(GLES2DecoderTest, DrawArraysClearsAfterTexImage2DNULLCubemap) { + static const GLenum faces[] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + }; + SetupCubemapProgram(); + DoBindTexture(GL_TEXTURE_CUBE_MAP, client_texture_id_, kServiceTextureId); + // Fill out all the faces for 2 levels, leave 2 uncleared. + for (int ii = 0; ii < 6; ++ii) { + GLenum face = faces[ii]; + int32 shm_id = + (face == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y) ? 0 : kSharedMemoryId; + uint32 shm_offset = + (face == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y) ? 0 : kSharedMemoryOffset; + DoTexImage2D(face, 0, GL_RGBA, 2, 2, 0, GL_RGBA, + GL_UNSIGNED_BYTE, shm_id, shm_offset); + DoTexImage2D(face, 1, GL_RGBA, 1, 1, 0, GL_RGBA, + GL_UNSIGNED_BYTE, shm_id, shm_offset); + } + // Expect 2 levels will be cleared. + SetupClearTextureExpections( + kServiceTextureId, kServiceTextureId, GL_TEXTURE_CUBE_MAP, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, GL_UNSIGNED_BYTE, 2, 2); + SetupClearTextureExpections( + kServiceTextureId, kServiceTextureId, GL_TEXTURE_CUBE_MAP, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 1, GL_RGBA, GL_UNSIGNED_BYTE, 1, 1); + AddExpectationsForSimulatedAttrib0(kNumVertices, 0); + SetupExpectationsForApplyingDefaultDirtyState(); + EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices)) + .Times(1) + .RetiresOnSaturation(); + DrawArrays cmd; + cmd.Init(GL_TRIANGLES, 0, kNumVertices); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); +} + +TEST_F(GLES2DecoderWithShaderTest, + DrawClearsAfterRenderbuffersWithMultipleAttachments) { + const GLuint kFBOClientTextureId = 4100; + const GLuint kFBOServiceTextureId = 4101; + + // Register a texture id. + EXPECT_CALL(*gl_, GenTextures(_, _)) + .WillOnce(SetArgumentPointee<1>(kFBOServiceTextureId)) + .RetiresOnSaturation(); + GenHelper<GenTexturesImmediate>(kFBOClientTextureId); + + // Setup "render to" texture. + DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId); + DoTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0); + DoBindFramebuffer( + GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); + DoFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + kFBOClientTextureId, kFBOServiceTextureId, 0, GL_NO_ERROR); + + DoBindRenderbuffer(GL_RENDERBUFFER, client_renderbuffer_id_, + kServiceRenderbufferId); + DoBindFramebuffer(GL_FRAMEBUFFER, client_framebuffer_id_, + kServiceFramebufferId); + DoRenderbufferStorage( + GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, + 1, 1, GL_NO_ERROR); + DoFramebufferRenderbuffer( + GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, + client_renderbuffer_id_, kServiceRenderbufferId, GL_NO_ERROR); + + SetupTexture(); + SetupExpectationsForFramebufferClearing( + GL_FRAMEBUFFER, // target + GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, // clear bits + 0, 0, 0, 0, // color + 0, // stencil + 1.0f, // depth + false); // scissor test + + AddExpectationsForSimulatedAttrib0(kNumVertices, 0); + SetupExpectationsForApplyingDirtyState( + false, // Framebuffer is RGB + true, // Framebuffer has depth + false, // Framebuffer has stencil + 0x1111, // color bits + true, // depth mask + false, // depth enabled + 0, // front stencil mask + 0, // back stencil mask + false); // stencil enabled + EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices)) + .Times(1) + .RetiresOnSaturation(); + DrawArrays cmd; + cmd.Init(GL_TRIANGLES, 0, kNumVertices); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); +} + + + // TODO(gman): Complete this test. // TEST_F(GLES2DecoderTest, CompressedTexImage2DGLError) { // } diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc index 5a52f8a..19ea2bc 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc @@ -54,8 +54,16 @@ void GLES2DecoderTestBase::SpecializedSetup<GenerateMipmap, 0>( template <> void GLES2DecoderTestBase::SpecializedSetup<CheckFramebufferStatus, 0>( bool /* valid */) { + // Give it a valid framebuffer. + DoBindRenderbuffer(GL_RENDERBUFFER, client_renderbuffer_id_, + kServiceRenderbufferId); DoBindFramebuffer(GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); + DoRenderbufferStorage( + GL_RENDERBUFFER, GL_RGBA4, GL_RGBA, 1, 1, GL_NO_ERROR); + DoFramebufferRenderbuffer( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, + client_renderbuffer_id_, kServiceRenderbufferId, GL_NO_ERROR); }; template <> @@ -91,7 +99,7 @@ void GLES2DecoderTestBase::SpecializedSetup<CopyTexSubImage2D, 0>(bool valid) { DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); DoTexImage2D( GL_TEXTURE_2D, 2, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, - 0, 0); + kSharedMemoryId, kSharedMemoryOffset); } }; @@ -116,13 +124,6 @@ void GLES2DecoderTestBase::SpecializedSetup<FramebufferRenderbuffer, 0>( if (valid) { EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) - .RetiresOnSaturation(); - // Return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT so the code - // doesn't try to clear the buffer. That is tested else where. - EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_FRAMEBUFFER)) - .WillOnce(Return(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)) - .RetiresOnSaturation(); - EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); } @@ -136,13 +137,6 @@ void GLES2DecoderTestBase::SpecializedSetup<FramebufferTexture2D, 0>( if (valid) { EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) - .RetiresOnSaturation(); - // Return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT so the code - // doesn't try to clear the buffer. That is tested else where. - EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_FRAMEBUFFER)) - .WillOnce(Return(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)) - .RetiresOnSaturation(); - EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); } diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc index 2f6c5e4..70c269d 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc @@ -16,6 +16,7 @@ #include "gpu/command_buffer/service/program_manager.h" #include "gpu/command_buffer/service/test_helper.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/gl/gl_implementation.h" using ::gfx::MockGLInterface; using ::testing::_; @@ -41,7 +42,9 @@ GLES2DecoderTestBase::GLES2DecoderTestBase() client_renderbuffer_id_(103), client_shader_id_(104), client_texture_id_(106), - client_element_buffer_id_(107) { + client_element_buffer_id_(107), + client_vertex_shader_id_(121), + client_fragment_shader_id_(122) { memset(immediate_buffer_, 0xEE, sizeof(immediate_buffer_)); } @@ -302,7 +305,30 @@ void GLES2DecoderTestBase::SetBucketAsCString( } } -void GLES2DecoderTestBase::SetupExpectationsForFramebufferAttachment( +void GLES2DecoderTestBase::SetupClearTextureExpections( + GLuint service_id, + GLuint old_service_id, + GLenum bind_target, + GLenum target, + GLint level, + GLenum format, + GLenum type, + GLsizei width, + GLsizei height) { + EXPECT_CALL(*gl_, BindTexture(bind_target, service_id)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, TexImage2D( + target, level, format, width, height, 0, format, type, _)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, BindTexture(bind_target, old_service_id)) + .Times(1) + .RetiresOnSaturation(); +} + +void GLES2DecoderTestBase::SetupExpectationsForFramebufferClearing( + GLenum target, GLuint clear_bits, GLclampf restore_red, GLclampf restore_green, @@ -311,15 +337,16 @@ void GLES2DecoderTestBase::SetupExpectationsForFramebufferAttachment( GLuint restore_stencil, GLclampf restore_depth, bool restore_scissor_test) { - InSequence sequence; - EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_FRAMEBUFFER)) + // TODO(gman): Figure out why InSequence stopped working. + // InSequence sequence; + EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(target)) .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE)) .RetiresOnSaturation(); if ((clear_bits & GL_COLOR_BUFFER_BIT) != 0) { - EXPECT_CALL(*gl_, ClearColor(0, 0, 0, 0)) + EXPECT_CALL(*gl_, ClearColor(0.0f, 0.0f, 0.0f, 0.0f)) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(*gl_, ColorMask(1, 1, 1, 1)) + EXPECT_CALL(*gl_, ColorMask(true, true, true, true)) .Times(1) .RetiresOnSaturation(); } @@ -578,6 +605,65 @@ void GLES2DecoderTestBase::DoTexImage2D( EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } +void GLES2DecoderTestBase::DoRenderbufferStorage( + GLenum target, GLenum internal_format, GLenum actual_format, + GLsizei width, GLsizei height, GLenum error) { + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, RenderbufferStorageEXT( + target, actual_format, width, height)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(error)) + .RetiresOnSaturation(); + RenderbufferStorage cmd; + cmd.Init(target, internal_format, width, height); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); +} + +void GLES2DecoderTestBase::DoFramebufferTexture2D( + GLenum target, GLenum attachment, GLenum textarget, + GLuint texture_client_id, GLuint texture_service_id, GLint level, + GLenum error) { + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, FramebufferTexture2DEXT( + target, attachment, textarget, texture_service_id, level)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(error)) + .RetiresOnSaturation(); + FramebufferTexture2D cmd; + cmd.Init(target, attachment, textarget, texture_client_id, level); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); +} + +void GLES2DecoderTestBase::DoFramebufferRenderbuffer( + GLenum target, + GLenum attachment, + GLenum renderbuffer_target, + GLuint renderbuffer_client_id, + GLuint renderbuffer_service_id, + GLenum error) { + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, FramebufferRenderbufferEXT( + target, attachment, renderbuffer_target, renderbuffer_service_id)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(error)) + .RetiresOnSaturation(); + FramebufferRenderbuffer cmd; + cmd.Init(target, attachment, renderbuffer_target, renderbuffer_client_id); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); +} + void GLES2DecoderTestBase::DoVertexAttribPointer( GLuint index, GLint size, GLenum type, GLsizei stride, GLuint offset) { EXPECT_CALL(*gl_, @@ -627,14 +713,57 @@ const uint32 GLES2DecoderTestBase::kInvalidClientId; const int GLES2DecoderTestBase::kBackBufferWidth; const int GLES2DecoderTestBase::kBackBufferHeight; + +const GLuint GLES2DecoderTestBase::kServiceVertexShaderId; +const GLuint GLES2DecoderTestBase::kServiceFragmentShaderId; + +const GLsizei GLES2DecoderTestBase::kNumVertices; +const GLsizei GLES2DecoderTestBase::kNumIndices; +const int GLES2DecoderTestBase::kValidIndexRangeStart; +const int GLES2DecoderTestBase::kValidIndexRangeCount; +const int GLES2DecoderTestBase::kInvalidIndexRangeStart; +const int GLES2DecoderTestBase::kInvalidIndexRangeCount; +const int GLES2DecoderTestBase::kOutOfRangeIndexRangeEnd; +const GLuint GLES2DecoderTestBase::kMaxValidIndex; + +const GLint GLES2DecoderTestBase::kMaxAttribLength; +const GLint GLES2DecoderTestBase::kAttrib1Size; +const GLint GLES2DecoderTestBase::kAttrib2Size; +const GLint GLES2DecoderTestBase::kAttrib3Size; +const GLint GLES2DecoderTestBase::kAttrib1Location; +const GLint GLES2DecoderTestBase::kAttrib2Location; +const GLint GLES2DecoderTestBase::kAttrib3Location; +const GLenum GLES2DecoderTestBase::kAttrib1Type; +const GLenum GLES2DecoderTestBase::kAttrib2Type; +const GLenum GLES2DecoderTestBase::kAttrib3Type; +const GLint GLES2DecoderTestBase::kInvalidAttribLocation; +const GLint GLES2DecoderTestBase::kBadAttribIndex; + +const GLint GLES2DecoderTestBase::kMaxUniformLength; +const GLint GLES2DecoderTestBase::kUniform1Size; +const GLint GLES2DecoderTestBase::kUniform2Size; +const GLint GLES2DecoderTestBase::kUniform3Size; +const GLint GLES2DecoderTestBase::kUniform1Location; +const GLint GLES2DecoderTestBase::kUniform2Location; +const GLint GLES2DecoderTestBase::kUniform2ElementLocation; +const GLint GLES2DecoderTestBase::kUniform3Location; +const GLenum GLES2DecoderTestBase::kUniform1Type; +const GLenum GLES2DecoderTestBase::kUniform2Type; +const GLenum GLES2DecoderTestBase::kUniform3Type; +const GLenum GLES2DecoderTestBase::kUniformCubemapType; +const GLint GLES2DecoderTestBase::kInvalidUniformLocation; +const GLint GLES2DecoderTestBase::kBadUniformIndex; + #endif -void GLES2DecoderWithShaderTestBase::SetUp() { - GLES2DecoderTestBase::SetUp(); - SetupDefaultProgram(); -} +const char* GLES2DecoderTestBase::kAttrib1Name = "attrib1"; +const char* GLES2DecoderTestBase::kAttrib2Name = "attrib2"; +const char* GLES2DecoderTestBase::kAttrib3Name = "attrib3"; +const char* GLES2DecoderTestBase::kUniform1Name = "uniform1"; +const char* GLES2DecoderTestBase::kUniform2Name = "uniform2[0]"; +const char* GLES2DecoderTestBase::kUniform3Name = "uniform3[0]"; -void GLES2DecoderWithShaderTestBase::SetupDefaultProgram() { +void GLES2DecoderTestBase::SetupDefaultProgram() { { static AttribInfo attribs[] = { { kAttrib1Name, kAttrib1Size, kAttrib1Type, kAttrib1Location, }, @@ -662,6 +791,34 @@ void GLES2DecoderWithShaderTestBase::SetupDefaultProgram() { } } +void GLES2DecoderTestBase::SetupCubemapProgram() { + { + static AttribInfo attribs[] = { + { kAttrib1Name, kAttrib1Size, kAttrib1Type, kAttrib1Location, }, + { kAttrib2Name, kAttrib2Size, kAttrib2Type, kAttrib2Location, }, + { kAttrib3Name, kAttrib3Size, kAttrib3Type, kAttrib3Location, }, + }; + static UniformInfo uniforms[] = { + { kUniform1Name, kUniform1Size, kUniformCubemapType, kUniform1Location, }, + { kUniform2Name, kUniform2Size, kUniform2Type, kUniform2Location, }, + { kUniform3Name, kUniform3Size, kUniform3Type, kUniform3Location, }, + }; + SetupShader(attribs, arraysize(attribs), uniforms, arraysize(uniforms), + client_program_id_, kServiceProgramId, + client_vertex_shader_id_, kServiceVertexShaderId, + client_fragment_shader_id_, kServiceFragmentShaderId); + } + + { + EXPECT_CALL(*gl_, UseProgram(kServiceProgramId)) + .Times(1) + .RetiresOnSaturation(); + UseProgram cmd; + cmd.Init(client_program_id_); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + } +} + void GLES2DecoderWithShaderTestBase::TearDown() { GLES2DecoderTestBase::TearDown(); } @@ -794,7 +951,7 @@ void GLES2DecoderTestBase::SetupShader( EXPECT_EQ(error::kNoError, ExecuteCmd(link_cmd)); } -void GLES2DecoderWithShaderTestBase::DoEnableVertexAttribArray(GLint index) { +void GLES2DecoderTestBase::DoEnableVertexAttribArray(GLint index) { EXPECT_CALL(*gl_, EnableVertexAttribArray(index)) .Times(1) .RetiresOnSaturation(); @@ -803,7 +960,7 @@ void GLES2DecoderWithShaderTestBase::DoEnableVertexAttribArray(GLint index) { EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } -void GLES2DecoderWithShaderTestBase::DoBufferData(GLenum target, GLsizei size) { +void GLES2DecoderTestBase::DoBufferData(GLenum target, GLsizei size) { EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); @@ -818,7 +975,7 @@ void GLES2DecoderWithShaderTestBase::DoBufferData(GLenum target, GLsizei size) { EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } -void GLES2DecoderWithShaderTestBase::DoBufferSubData( +void GLES2DecoderTestBase::DoBufferSubData( GLenum target, GLint offset, GLsizei size, const void* data) { EXPECT_CALL(*gl_, BufferSubData(target, offset, size, shared_memory_address_)) @@ -830,14 +987,14 @@ void GLES2DecoderWithShaderTestBase::DoBufferSubData( EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } -void GLES2DecoderWithShaderTestBase::SetupVertexBuffer() { +void GLES2DecoderTestBase::SetupVertexBuffer() { DoEnableVertexAttribArray(1); DoBindBuffer(GL_ARRAY_BUFFER, client_buffer_id_, kServiceBufferId); GLfloat f = 0; DoBufferData(GL_ARRAY_BUFFER, kNumVertices * 2 * sizeof(f)); } -void GLES2DecoderWithShaderTestBase::SetupIndexBuffer() { +void GLES2DecoderTestBase::SetupIndexBuffer() { DoBindBuffer(GL_ELEMENT_ARRAY_BUFFER, client_element_buffer_id_, kServiceElementBufferId); @@ -848,68 +1005,68 @@ void GLES2DecoderWithShaderTestBase::SetupIndexBuffer() { DoBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 2, sizeof(indices) - 2, &indices[1]); } -void GLES2DecoderWithShaderTestBase::SetupTexture() { +void GLES2DecoderTestBase::SetupTexture() { DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, - 0, 0); + kSharedMemoryId, kSharedMemoryOffset); }; -void GLES2DecoderWithShaderTestBase::DeleteVertexBuffer() { +void GLES2DecoderTestBase::DeleteVertexBuffer() { DoDeleteBuffer(client_buffer_id_, kServiceBufferId); } -void GLES2DecoderWithShaderTestBase::DeleteIndexBuffer() { +void GLES2DecoderTestBase::DeleteIndexBuffer() { DoDeleteBuffer(client_element_buffer_id_, kServiceElementBufferId); } -// GCC requires these declarations, but MSVC requires they not be present -#ifndef COMPILER_MSVC -const GLuint GLES2DecoderWithShaderTestBase::kServiceVertexShaderId; -const GLuint GLES2DecoderWithShaderTestBase::kServiceFragmentShaderId; - -const GLsizei GLES2DecoderWithShaderTestBase::kNumVertices; -const GLsizei GLES2DecoderWithShaderTestBase::kNumIndices; -const int GLES2DecoderWithShaderTestBase::kValidIndexRangeStart; -const int GLES2DecoderWithShaderTestBase::kValidIndexRangeCount; -const int GLES2DecoderWithShaderTestBase::kInvalidIndexRangeStart; -const int GLES2DecoderWithShaderTestBase::kInvalidIndexRangeCount; -const int GLES2DecoderWithShaderTestBase::kOutOfRangeIndexRangeEnd; -const GLuint GLES2DecoderWithShaderTestBase::kMaxValidIndex; - -const GLint GLES2DecoderWithShaderTestBase::kMaxAttribLength; -const GLint GLES2DecoderWithShaderTestBase::kAttrib1Size; -const GLint GLES2DecoderWithShaderTestBase::kAttrib2Size; -const GLint GLES2DecoderWithShaderTestBase::kAttrib3Size; -const GLint GLES2DecoderWithShaderTestBase::kAttrib1Location; -const GLint GLES2DecoderWithShaderTestBase::kAttrib2Location; -const GLint GLES2DecoderWithShaderTestBase::kAttrib3Location; -const GLenum GLES2DecoderWithShaderTestBase::kAttrib1Type; -const GLenum GLES2DecoderWithShaderTestBase::kAttrib2Type; -const GLenum GLES2DecoderWithShaderTestBase::kAttrib3Type; -const GLint GLES2DecoderWithShaderTestBase::kInvalidAttribLocation; -const GLint GLES2DecoderWithShaderTestBase::kBadAttribIndex; - -const GLint GLES2DecoderWithShaderTestBase::kMaxUniformLength; -const GLint GLES2DecoderWithShaderTestBase::kUniform1Size; -const GLint GLES2DecoderWithShaderTestBase::kUniform2Size; -const GLint GLES2DecoderWithShaderTestBase::kUniform3Size; -const GLint GLES2DecoderWithShaderTestBase::kUniform1Location; -const GLint GLES2DecoderWithShaderTestBase::kUniform2Location; -const GLint GLES2DecoderWithShaderTestBase::kUniform2ElementLocation; -const GLint GLES2DecoderWithShaderTestBase::kUniform3Location; -const GLenum GLES2DecoderWithShaderTestBase::kUniform1Type; -const GLenum GLES2DecoderWithShaderTestBase::kUniform2Type; -const GLenum GLES2DecoderWithShaderTestBase::kUniform3Type; -const GLint GLES2DecoderWithShaderTestBase::kInvalidUniformLocation; -const GLint GLES2DecoderWithShaderTestBase::kBadUniformIndex; -#endif +void GLES2DecoderTestBase::AddExpectationsForSimulatedAttrib0WithError( + GLsizei num_vertices, GLuint buffer_id, GLenum error) { + if (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2) { + return; + } -const char* GLES2DecoderWithShaderTestBase::kAttrib1Name = "attrib1"; -const char* GLES2DecoderWithShaderTestBase::kAttrib2Name = "attrib2"; -const char* GLES2DecoderWithShaderTestBase::kAttrib3Name = "attrib3"; -const char* GLES2DecoderWithShaderTestBase::kUniform1Name = "uniform1"; -const char* GLES2DecoderWithShaderTestBase::kUniform2Name = "uniform2[0]"; -const char* GLES2DecoderWithShaderTestBase::kUniform3Name = "uniform3[0]"; + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .WillOnce(Return(error)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, kServiceAttrib0BufferId)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, BufferData(GL_ARRAY_BUFFER, + num_vertices * sizeof(GLfloat) * 4, + _, GL_DYNAMIC_DRAW)) + .Times(1) + .RetiresOnSaturation(); + if (error == GL_NO_ERROR) { + EXPECT_CALL(*gl_, BufferSubData( + GL_ARRAY_BUFFER, 0, num_vertices * sizeof(GLfloat) * 4, _)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, VertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, 0)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, VertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, buffer_id)) + .Times(1) + .RetiresOnSaturation(); + } +} + +void GLES2DecoderTestBase::AddExpectationsForSimulatedAttrib0( + GLsizei num_vertices, GLuint buffer_id) { + AddExpectationsForSimulatedAttrib0WithError( + num_vertices, buffer_id, GL_NO_ERROR); +} + +void GLES2DecoderWithShaderTestBase::SetUp() { + GLES2DecoderTestBase::SetUp(); + SetupDefaultProgram(); +} } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h index 4ebbe24..3cb5eab 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h @@ -66,6 +66,52 @@ class GLES2DecoderTestBase : public testing::Test { static const int kBackBufferWidth = 128; static const int kBackBufferHeight = 64; + static const GLuint kServiceVertexShaderId = 321; + static const GLuint kServiceFragmentShaderId = 322; + + static const GLsizei kNumVertices = 100; + static const GLsizei kNumIndices = 10; + static const int kValidIndexRangeStart = 1; + static const int kValidIndexRangeCount = 7; + static const int kInvalidIndexRangeStart = 0; + static const int kInvalidIndexRangeCount = 7; + static const int kOutOfRangeIndexRangeEnd = 10; + static const GLuint kMaxValidIndex = 7; + + static const GLint kMaxAttribLength = 10; + static const char* kAttrib1Name; + static const char* kAttrib2Name; + static const char* kAttrib3Name; + static const GLint kAttrib1Size = 1; + static const GLint kAttrib2Size = 1; + static const GLint kAttrib3Size = 1; + static const GLint kAttrib1Location = 0; + static const GLint kAttrib2Location = 1; + static const GLint kAttrib3Location = 2; + static const GLenum kAttrib1Type = GL_FLOAT_VEC4; + static const GLenum kAttrib2Type = GL_FLOAT_VEC2; + static const GLenum kAttrib3Type = GL_FLOAT_VEC3; + static const GLint kInvalidAttribLocation = 30; + static const GLint kBadAttribIndex = kNumVertexAttribs; + + static const GLint kMaxUniformLength = 12; + static const char* kUniform1Name; + static const char* kUniform2Name; + static const char* kUniform3Name; + static const GLint kUniform1Size = 1; + static const GLint kUniform2Size = 3; + static const GLint kUniform3Size = 2; + static const GLint kUniform1Location = 3; + static const GLint kUniform2Location = 10; + static const GLint kUniform2ElementLocation = 12; + static const GLint kUniform3Location = 20; + static const GLenum kUniform1Type = GL_SAMPLER_2D; + static const GLenum kUniform2Type = GL_INT_VEC2; + static const GLenum kUniform3Type = GL_FLOAT_VEC3; + static const GLenum kUniformCubemapType = GL_SAMPLER_CUBE; + static const GLint kInvalidUniformLocation = 30; + static const GLint kBadUniformIndex = 1000; + // Template to call glGenXXX functions. template <typename T> void GenHelper(GLuint client_id) { @@ -197,6 +243,9 @@ class GLES2DecoderTestBase : public testing::Test { // Setups up a shader for testing glUniform. void SetupShaderForUniform(); + void SetupDefaultProgram(); + void SetupCubemapProgram(); + void SetupTexture(); // Note that the error is returned as GLint instead of GLenum. // This is because there is a mismatch in the types of GLenum and @@ -228,14 +277,56 @@ class GLES2DecoderTestBase : public testing::Test { void DoDeleteShader(GLuint client_id, GLuint service_id); void DoDeleteTexture(GLuint client_id, GLuint service_id); - void DoTexImage2D(GLenum target, GLint level, GLenum internal_format, - GLsizei width, GLsizei height, GLint border, - GLenum format, GLenum type, - uint32 shared_memory_id, uint32 shared_memory_offset); + void DoTexImage2D( + GLenum target, GLint level, GLenum internal_format, + GLsizei width, GLsizei height, GLint border, + GLenum format, GLenum type, + uint32 shared_memory_id, uint32 shared_memory_offset); + void DoRenderbufferStorage( + GLenum target, GLenum internal_format, GLenum actual_format, + GLsizei width, GLsizei height, GLenum error); + void DoFramebufferRenderbuffer( + GLenum target, + GLenum attachment, + GLenum renderbuffer_target, + GLuint renderbuffer_client_id, + GLuint renderbuffer_service_id, + GLenum error); + void DoFramebufferTexture2D( + GLenum target, GLenum attachment, GLenum tex_target, + GLuint texture_client_id, GLuint texture_service_id, + GLint level, GLenum error); void DoVertexAttribPointer( GLuint index, GLint size, GLenum type, GLsizei stride, GLuint offset); - void SetupExpectationsForFramebufferAttachment( + void DoEnableVertexAttribArray(GLint index); + + void DoBufferData(GLenum target, GLsizei size); + + void DoBufferSubData( + GLenum target, GLint offset, GLsizei size, const void* data); + + void SetupVertexBuffer(); + + void SetupIndexBuffer(); + + void DeleteVertexBuffer(); + + void DeleteIndexBuffer(); + + void SetupClearTextureExpections( + GLuint service_id, + GLuint old_service_id, + GLenum bind_target, + GLenum target, + GLint level, + GLenum format, + GLenum type, + GLsizei width, + GLsizei height); + + void SetupExpectationsForFramebufferClearing( + GLenum target, GLuint clear_bits, GLclampf restore_red, GLclampf restore_green, @@ -258,6 +349,12 @@ class GLES2DecoderTestBase : public testing::Test { void SetupExpectationsForApplyingDefaultDirtyState(); + void AddExpectationsForSimulatedAttrib0WithError( + GLsizei num_vertices, GLuint buffer_id, GLenum error); + + void AddExpectationsForSimulatedAttrib0( + GLsizei num_vertices, GLuint buffer_id); + GLvoid* BufferOffset(unsigned i) { return static_cast<int8 *>(NULL)+(i); } @@ -286,6 +383,8 @@ class GLES2DecoderTestBase : public testing::Test { GLuint client_shader_id_; GLuint client_texture_id_; GLuint client_element_buffer_id_; + GLuint client_vertex_shader_id_; + GLuint client_fragment_shader_id_; uint32 shared_memory_id_; uint32 shared_memory_offset_; @@ -344,80 +443,13 @@ class GLES2DecoderTestBase : public testing::Test { class GLES2DecoderWithShaderTestBase : public GLES2DecoderTestBase { public: GLES2DecoderWithShaderTestBase() - : GLES2DecoderTestBase(), - client_vertex_shader_id_(121), - client_fragment_shader_id_(122) { + : GLES2DecoderTestBase() { } - static const GLuint kServiceVertexShaderId = 321; - static const GLuint kServiceFragmentShaderId = 322; - - static const GLsizei kNumVertices = 100; - static const GLsizei kNumIndices = 10; - static const int kValidIndexRangeStart = 1; - static const int kValidIndexRangeCount = 7; - static const int kInvalidIndexRangeStart = 0; - static const int kInvalidIndexRangeCount = 7; - static const int kOutOfRangeIndexRangeEnd = 10; - static const GLuint kMaxValidIndex = 7; - - static const GLint kMaxAttribLength = 10; - static const char* kAttrib1Name; - static const char* kAttrib2Name; - static const char* kAttrib3Name; - static const GLint kAttrib1Size = 1; - static const GLint kAttrib2Size = 1; - static const GLint kAttrib3Size = 1; - static const GLint kAttrib1Location = 0; - static const GLint kAttrib2Location = 1; - static const GLint kAttrib3Location = 2; - static const GLenum kAttrib1Type = GL_FLOAT_VEC4; - static const GLenum kAttrib2Type = GL_FLOAT_VEC2; - static const GLenum kAttrib3Type = GL_FLOAT_VEC3; - static const GLint kInvalidAttribLocation = 30; - static const GLint kBadAttribIndex = kNumVertexAttribs; - - static const GLint kMaxUniformLength = 12; - static const char* kUniform1Name; - static const char* kUniform2Name; - static const char* kUniform3Name; - static const GLint kUniform1Size = 1; - static const GLint kUniform2Size = 3; - static const GLint kUniform3Size = 2; - static const GLint kUniform1Location = 3; - static const GLint kUniform2Location = 10; - static const GLint kUniform2ElementLocation = 12; - static const GLint kUniform3Location = 20; - static const GLenum kUniform1Type = GL_SAMPLER_2D; - static const GLenum kUniform2Type = GL_INT_VEC2; - static const GLenum kUniform3Type = GL_FLOAT_VEC3; - static const GLint kInvalidUniformLocation = 30; - static const GLint kBadUniformIndex = 1000; - protected: virtual void SetUp(); virtual void TearDown(); - void SetupDefaultProgram(); - void SetupTexture(); - - void DoEnableVertexAttribArray(GLint index); - - void DoBufferData(GLenum target, GLsizei size); - - void DoBufferSubData( - GLenum target, GLint offset, GLsizei size, const void* data); - - void SetupVertexBuffer(); - - void SetupIndexBuffer(); - - void DeleteVertexBuffer(); - - void DeleteIndexBuffer(); - - GLuint client_vertex_shader_id_; - GLuint client_fragment_shader_id_; }; } // namespace gles2 diff --git a/gpu/command_buffer/service/renderbuffer_manager.cc b/gpu/command_buffer/service/renderbuffer_manager.cc index a12d66e..5c9ca30 100644 --- a/gpu/command_buffer/service/renderbuffer_manager.cc +++ b/gpu/command_buffer/service/renderbuffer_manager.cc @@ -13,7 +13,8 @@ namespace gles2 { RenderbufferManager::RenderbufferManager( GLint max_renderbuffer_size, GLint max_samples) : max_renderbuffer_size_(max_renderbuffer_size), - max_samples_(max_samples) { + max_samples_(max_samples), + num_uncleared_renderbuffers_(0) { } RenderbufferManager::~RenderbufferManager() { @@ -34,14 +35,39 @@ void RenderbufferManager::Destroy(bool have_context) { } } +void RenderbufferManager::SetInfo( + RenderbufferInfo* renderbuffer, + GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { + DCHECK(renderbuffer); + if (!renderbuffer->cleared()) { + --num_uncleared_renderbuffers_; + } + renderbuffer->SetInfo(samples, internalformat, width, height); + if (!renderbuffer->cleared()) { + ++num_uncleared_renderbuffers_; + } +} + +void RenderbufferManager::SetCleared(RenderbufferInfo* renderbuffer) { + DCHECK(renderbuffer); + if (!renderbuffer->cleared()) { + --num_uncleared_renderbuffers_; + } + renderbuffer->set_cleared(); + if (!renderbuffer->cleared()) { + ++num_uncleared_renderbuffers_; + } +} + void RenderbufferManager::CreateRenderbufferInfo( GLuint client_id, GLuint service_id) { + RenderbufferInfo::Ref info(new RenderbufferInfo(service_id)); std::pair<RenderbufferInfoMap::iterator, bool> result = - renderbuffer_infos_.insert( - std::make_pair( - client_id, - RenderbufferInfo::Ref(new RenderbufferInfo(service_id)))); + renderbuffer_infos_.insert(std::make_pair(client_id, info)); DCHECK(result.second); + if (!info->cleared()) { + ++num_uncleared_renderbuffers_; + } } RenderbufferManager::RenderbufferInfo* RenderbufferManager::GetRenderbufferInfo( @@ -53,7 +79,11 @@ RenderbufferManager::RenderbufferInfo* RenderbufferManager::GetRenderbufferInfo( void RenderbufferManager::RemoveRenderbufferInfo(GLuint client_id) { RenderbufferInfoMap::iterator it = renderbuffer_infos_.find(client_id); if (it != renderbuffer_infos_.end()) { - it->second->MarkAsDeleted(); + RenderbufferInfo* info = it->second; + if (!info->cleared()) { + --num_uncleared_renderbuffers_; + } + info->MarkAsDeleted(); renderbuffer_infos_.erase(it); } } diff --git a/gpu/command_buffer/service/renderbuffer_manager.h b/gpu/command_buffer/service/renderbuffer_manager.h index 619fbf2..5cd7e50 100644 --- a/gpu/command_buffer/service/renderbuffer_manager.h +++ b/gpu/command_buffer/service/renderbuffer_manager.h @@ -25,7 +25,7 @@ class RenderbufferManager { explicit RenderbufferInfo(GLuint service_id) : service_id_(service_id), - cleared_(false), + cleared_(true), has_been_bound_(false), samples_(0), internal_format_(GL_RGBA4), @@ -41,10 +41,6 @@ class RenderbufferManager { return cleared_; } - void set_cleared() { - cleared_ = true; - } - GLenum internal_format() const { return internal_format_; } @@ -61,15 +57,6 @@ class RenderbufferManager { return height_; } - void SetInfo( - GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { - samples_ = samples; - internal_format_ = internalformat; - width_ = width; - height_ = height; - cleared_ = false; - } - bool IsDeleted() const { return service_id_ == 0; } @@ -88,6 +75,19 @@ class RenderbufferManager { ~RenderbufferInfo() { } + void set_cleared() { + cleared_ = true; + } + + void SetInfo( + GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { + samples_ = samples; + internal_format_ = internalformat; + width_ = width; + height_ = height; + cleared_ = false; + } + void MarkAsDeleted() { service_id_ = 0; } @@ -123,6 +123,16 @@ class RenderbufferManager { return max_samples_; } + bool HaveUnclearedRenderbuffers() const { + return num_uncleared_renderbuffers_ != 0; + } + + void SetInfo( + RenderbufferInfo* renderbuffer, + GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); + + void SetCleared(RenderbufferInfo* renderbuffer); + // Must call before destruction. void Destroy(bool have_context); @@ -142,6 +152,8 @@ class RenderbufferManager { GLint max_renderbuffer_size_; GLint max_samples_; + int num_uncleared_renderbuffers_; + // Info for each renderbuffer in the system. typedef base::hash_map<GLuint, RenderbufferInfo::Ref> RenderbufferInfoMap; RenderbufferInfoMap renderbuffer_infos_; diff --git a/gpu/command_buffer/service/renderbuffer_manager_unittest.cc b/gpu/command_buffer/service/renderbuffer_manager_unittest.cc index 954778d..63719d7 100644 --- a/gpu/command_buffer/service/renderbuffer_manager_unittest.cc +++ b/gpu/command_buffer/service/renderbuffer_manager_unittest.cc @@ -50,12 +50,14 @@ TEST_F(RenderbufferManagerTest, Basic) { const GLuint kClient2Id = 2; EXPECT_EQ(kMaxSize, manager_.max_renderbuffer_size()); EXPECT_EQ(kMaxSamples, manager_.max_samples()); + EXPECT_FALSE(manager_.HaveUnclearedRenderbuffers()); // Check we can create renderbuffer. manager_.CreateRenderbufferInfo(kClient1Id, kService1Id); // Check renderbuffer got created. RenderbufferManager::RenderbufferInfo* info1 = manager_.GetRenderbufferInfo(kClient1Id); ASSERT_TRUE(info1 != NULL); + EXPECT_FALSE(manager_.HaveUnclearedRenderbuffers()); GLuint client_id = 0; EXPECT_TRUE(manager_.GetClientId(info1->service_id(), &client_id)); EXPECT_EQ(kClient1Id, client_id); @@ -66,6 +68,7 @@ TEST_F(RenderbufferManagerTest, Basic) { // Check we can't get the renderbuffer after we remove it. manager_.RemoveRenderbufferInfo(kClient1Id); EXPECT_TRUE(manager_.GetRenderbufferInfo(kClient1Id) == NULL); + EXPECT_FALSE(manager_.HaveUnclearedRenderbuffers()); } TEST_F(RenderbufferManagerTest, Destroy) { @@ -99,9 +102,6 @@ TEST_F(RenderbufferManagerTest, RenderbufferInfo) { EXPECT_EQ(static_cast<GLenum>(GL_RGBA4), info1->internal_format()); EXPECT_EQ(0, info1->width()); EXPECT_EQ(0, info1->height()); - - EXPECT_FALSE(info1->cleared()); - info1->set_cleared(); EXPECT_TRUE(info1->cleared()); // Check if we set the info it gets marked as not cleared. @@ -109,13 +109,24 @@ TEST_F(RenderbufferManagerTest, RenderbufferInfo) { const GLenum kFormat = GL_RGBA; const GLsizei kWidth = 128; const GLsizei kHeight = 64; - info1->SetInfo(kSamples, kFormat, kWidth, kHeight); + manager_.SetInfo(info1, kSamples, kFormat, kWidth, kHeight); EXPECT_EQ(kSamples, info1->samples()); EXPECT_EQ(kFormat, info1->internal_format()); EXPECT_EQ(kWidth, info1->width()); EXPECT_EQ(kHeight, info1->height()); EXPECT_FALSE(info1->cleared()); EXPECT_FALSE(info1->IsDeleted()); + EXPECT_TRUE(manager_.HaveUnclearedRenderbuffers()); + + manager_.SetCleared(info1); + EXPECT_TRUE(info1->cleared()); + EXPECT_FALSE(manager_.HaveUnclearedRenderbuffers()); + + manager_.SetInfo(info1, kSamples, kFormat, kWidth, kHeight); + EXPECT_TRUE(manager_.HaveUnclearedRenderbuffers()); + + manager_.RemoveRenderbufferInfo(kClient1Id); + EXPECT_FALSE(manager_.HaveUnclearedRenderbuffers()); } } // namespace gles2 diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc index 329a1f8..e5ace9f 100644 --- a/gpu/command_buffer/service/texture_manager.cc +++ b/gpu/command_buffer/service/texture_manager.cc @@ -118,14 +118,15 @@ bool TextureManager::TextureInfo::MarkMipmapsGenerated( GLsizei width = info1.width; GLsizei height = info1.height; GLsizei depth = info1.depth; + GLenum target = target_ == GL_TEXTURE_2D ? GL_TEXTURE_2D : + FaceIndexToGLTarget(ii); int num_mips = ComputeMipMapCount(width, height, depth); for (int level = 1; level < num_mips; ++level) { width = std::max(1, width >> 1); height = std::max(1, height >> 1); depth = std::max(1, depth >> 1); SetLevelInfo(feature_info, - target_ == GL_TEXTURE_2D ? GL_TEXTURE_2D : - FaceIndexToGLTarget(ii), + target, level, info1.internal_format, width, @@ -133,9 +134,11 @@ bool TextureManager::TextureInfo::MarkMipmapsGenerated( depth, info1.border, info1.format, - info1.type); + info1.type, + true); } } + return true; } @@ -165,7 +168,7 @@ bool TextureManager::TextureInfo::CanGenerateMipmaps( // TODO(gman): Check internal_format, format and type. for (size_t ii = 0; ii < level_infos_.size(); ++ii) { const LevelInfo& info = level_infos_[ii][0]; - if ((!info.valid) || + if ((info.target == 0) || (info.width != first.width) || (info.height != first.height) || (info.depth != 1) || @@ -178,6 +181,43 @@ bool TextureManager::TextureInfo::CanGenerateMipmaps( return true; } +void TextureManager::TextureInfo::SetLevelCleared(GLenum target, GLint level) { + DCHECK_GE(level, 0); + DCHECK_LT(static_cast<size_t>(GLTargetToFaceIndex(target)), + level_infos_.size()); + DCHECK_LT(static_cast<size_t>(level), + level_infos_[GLTargetToFaceIndex(target)].size()); + TextureInfo::LevelInfo& info = + level_infos_[GLTargetToFaceIndex(target)][level]; + if (!info.cleared) { + DCHECK_NE(0, num_uncleared_mips_); + --num_uncleared_mips_; + } + info.cleared = true; + UpdateCleared(); +} + +void TextureManager::TextureInfo::UpdateCleared() { + if (level_infos_.empty()) { + return; + } + + const TextureInfo::LevelInfo& first_face = level_infos_[0][0]; + int levels_needed = ComputeMipMapCount( + first_face.width, first_face.height, first_face.depth); + cleared_ = true; + for (size_t ii = 0; ii < level_infos_.size(); ++ii) { + for (GLint jj = 0; jj < levels_needed; ++jj) { + const TextureInfo::LevelInfo& info = level_infos_[ii][jj]; + if (info.width > 0 && info.height > 0 && info.depth > 0 && + !info.cleared) { + cleared_ = false; + return; + } + } + } +} + void TextureManager::TextureInfo::SetLevelInfo( const FeatureInfo* feature_info, GLenum target, @@ -188,7 +228,8 @@ void TextureManager::TextureInfo::SetLevelInfo( GLsizei depth, GLint border, GLenum format, - GLenum type) { + GLenum type, + bool cleared) { DCHECK_GE(level, 0); DCHECK_LT(static_cast<size_t>(GLTargetToFaceIndex(target)), level_infos_.size()); @@ -199,7 +240,8 @@ void TextureManager::TextureInfo::SetLevelInfo( DCHECK_GE(depth, 0); TextureInfo::LevelInfo& info = level_infos_[GLTargetToFaceIndex(target)][level]; - info.valid = true; + info.target = target; + info.level = level; info.internal_format = internal_format; info.width = width; info.height = height; @@ -207,8 +249,17 @@ void TextureManager::TextureInfo::SetLevelInfo( info.border = border; info.format = format; info.type = type; + if (!info.cleared) { + DCHECK_NE(0, num_uncleared_mips_); + --num_uncleared_mips_; + } + info.cleared = cleared; + if (!info.cleared) { + ++num_uncleared_mips_; + } max_level_set_ = std::max(max_level_set_, level); Update(feature_info); + UpdateCleared(); } bool TextureManager::TextureInfo::ValidForTexture( @@ -246,7 +297,7 @@ bool TextureManager::TextureInfo::GetLevelSize( if (!IsDeleted() && level >= 0 && face_index < level_infos_.size() && static_cast<size_t>(level) < level_infos_[face_index].size()) { const LevelInfo& info = level_infos_[GLTargetToFaceIndex(face)][level]; - if (info.valid) { + if (info.target != 0) { *width = info.width; *height = info.height; return true; @@ -263,7 +314,7 @@ bool TextureManager::TextureInfo::GetLevelType( if (!IsDeleted() && level >= 0 && face_index < level_infos_.size() && static_cast<size_t>(level) < level_infos_[face_index].size()) { const LevelInfo& info = level_infos_[GLTargetToFaceIndex(face)][level]; - if (info.valid) { + if (info.target != 0) { *type = info.type; *internal_format = info.internal_format; return true; @@ -318,6 +369,7 @@ bool TextureManager::TextureInfo::SetParameter( return false; } Update(feature_info); + UpdateCleared(); return true; } @@ -331,6 +383,7 @@ void TextureManager::TextureInfo::Update(const FeatureInfo* feature_info) { return; } + // checks that the first mip of any face is npot. for (size_t ii = 0; ii < level_infos_.size(); ++ii) { const TextureInfo::LevelInfo& info = level_infos_[ii][0]; if (GLES2Util::IsNPOT(info.width) || @@ -367,7 +420,7 @@ void TextureManager::TextureInfo::Update(const FeatureInfo* feature_info) { ii < level_infos_.size() && (cube_complete_ || texture_complete_); ++ii) { const TextureInfo::LevelInfo& level0 = level_infos_[ii][0]; - if (!level0.valid || + if (level0.target == 0 || level0.width != first_face.width || level0.height != first_face.height || level0.depth != 1 || @@ -386,7 +439,7 @@ void TextureManager::TextureInfo::Update(const FeatureInfo* feature_info) { height = std::max(1, height >> 1); depth = std::max(1, depth >> 1); const TextureInfo::LevelInfo& info = level_infos_[ii][jj]; - if (!info.valid || + if (info.target == 0 || info.width != width || info.height != height || info.depth != depth || @@ -400,6 +453,80 @@ void TextureManager::TextureInfo::Update(const FeatureInfo* feature_info) { } } +bool TextureManager::TextureInfo::ClearRenderableLevels(GLES2Decoder* decoder) { + DCHECK(decoder); + if (SafeToRenderFrom()) { + return true; + } + + const TextureInfo::LevelInfo& first_face = level_infos_[0][0]; + int levels_needed = ComputeMipMapCount( + first_face.width, first_face.height, first_face.depth); + + for (size_t ii = 0; ii < level_infos_.size(); ++ii) { + for (GLint jj = 0; jj < levels_needed; ++jj) { + TextureInfo::LevelInfo& info = level_infos_[ii][jj]; + if (info.target != 0) { + if (!ClearLevel(decoder, info.target, jj)) { + return false; + } + } + } + } + cleared_ = true; + return true; +} + +bool TextureManager::TextureInfo::IsLevelCleared(GLenum target, GLint level) { + size_t face_index = GLTargetToFaceIndex(target); + if (IsDeleted() || + face_index >= level_infos_.size() || + level >= static_cast<GLint>(level_infos_[face_index].size())) { + return true; + } + + TextureInfo::LevelInfo& info = level_infos_[face_index][level]; + + return info.cleared; +} + +bool TextureManager::TextureInfo::ClearLevel( + GLES2Decoder* decoder, GLenum target, GLint level) { + DCHECK(decoder); + size_t face_index = GLTargetToFaceIndex(target); + if (IsDeleted() || + face_index >= level_infos_.size() || + level >= static_cast<GLint>(level_infos_[face_index].size())) { + return true; + } + + TextureInfo::LevelInfo& info = level_infos_[face_index][level]; + + DCHECK(target == info.target); + + if (info.target == 0 || + info.cleared || + info.width == 0 || + info.height == 0 || + info.depth == 0) { + return true; + } + + DCHECK_NE(0, num_uncleared_mips_); + --num_uncleared_mips_; + + // NOTE: It seems kind of gross to call back into the decoder for this + // but only the decoder knows all the state (like unpack_alignment_) that's + // needed to be able to call GL correctly. + info.cleared = decoder->ClearLevel( + service_id_, target_, info.target, info.level, info.format, info.type, + info.width, info.height); + if (!info.cleared) { + ++num_uncleared_mips_; + } + return info.cleared; +} + TextureManager::TextureManager( GLint max_texture_size, GLint max_cube_map_texture_size) @@ -412,6 +539,8 @@ TextureManager::TextureManager( max_cube_map_texture_size, max_cube_map_texture_size)), num_unrenderable_textures_(0), + num_unsafe_textures_(0), + num_uncleared_mips_(0), black_2d_texture_id_(0), black_cube_texture_id_(0) { } @@ -439,23 +568,30 @@ bool TextureManager::Initialize(const FeatureInfo* feature_info) { glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + // Since we are manually setting up these textures + // we need to manually manipulate some of the their bookkeeping. + num_unrenderable_textures_ += 2; FeatureInfo temp_feature_info; default_texture_2d_ = TextureInfo::Ref(new TextureInfo(ids[1])); SetInfoTarget(feature_info, default_texture_2d_, GL_TEXTURE_2D); - default_texture_2d_->SetLevelInfo(&temp_feature_info, - GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + SetLevelInfo(&temp_feature_info, default_texture_2d_, + GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); default_texture_cube_map_ = TextureInfo::Ref(new TextureInfo(ids[3])); SetInfoTarget(feature_info, default_texture_cube_map_, GL_TEXTURE_CUBE_MAP); for (int ii = 0; ii < GLES2Util::kNumFaces; ++ii) { - default_texture_cube_map_->SetLevelInfo( - &temp_feature_info, GLES2Util::IndexToGLFaceTarget(ii), - 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + SetLevelInfo( + &temp_feature_info, default_texture_cube_map_, + GLES2Util::IndexToGLFaceTarget(ii), + 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); } black_2d_texture_id_ = ids[0]; black_cube_texture_id_ = ids[2]; if (feature_info->feature_flags().oes_egl_image_external) { + // Since we are manually setting up these textures + // we need to manually manipulate some of the their bookkeeping. + num_unrenderable_textures_ += 1; GLuint external_ids[2]; glGenTextures(arraysize(external_ids), external_ids); glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); @@ -466,7 +602,7 @@ bool TextureManager::Initialize(const FeatureInfo* feature_info) { GL_TEXTURE_EXTERNAL_OES); default_texture_external_oes_->SetLevelInfo( &temp_feature_info, GL_TEXTURE_EXTERNAL_OES, 0, - GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); // Sampling a texture not associated with any EGLImage sibling will return // black values according to the spec. @@ -499,9 +635,10 @@ bool TextureManager::ValidForTarget( void TextureManager::SetInfoTarget( const FeatureInfo* feature_info, - TextureInfo* info, GLenum target) { + TextureManager::TextureInfo* info, GLenum target) { DCHECK(info); if (!info->CanRender(feature_info)) { + DCHECK_NE(0, num_unrenderable_textures_); --num_unrenderable_textures_; } info->SetTarget(target, MaxLevelsForTarget(target)); @@ -510,6 +647,65 @@ void TextureManager::SetInfoTarget( } } +void TextureManager::SetLevelCleared( + TextureManager::TextureInfo* info, GLenum target, GLint level) { + DCHECK(info); + DCHECK(!info->IsDeleted()); + if (!info->SafeToRenderFrom()) { + DCHECK_NE(0, num_unsafe_textures_); + --num_unsafe_textures_; + } + num_uncleared_mips_ -= info->num_uncleared_mips(); + DCHECK_GE(num_uncleared_mips_, 0); + info->SetLevelCleared(target, level); + num_uncleared_mips_ += info->num_uncleared_mips(); + if (!info->SafeToRenderFrom()) { + ++num_unsafe_textures_; + } +} + +bool TextureManager::ClearRenderableLevels( + GLES2Decoder* decoder,TextureManager::TextureInfo* info) { + DCHECK(info); + DCHECK(!info->IsDeleted()); + if (info->SafeToRenderFrom()) { + return true; + } + DCHECK_NE(0, num_unsafe_textures_); + --num_unsafe_textures_; + num_uncleared_mips_ -= info->num_uncleared_mips(); + DCHECK_GE(num_uncleared_mips_, 0); + bool result = info->ClearRenderableLevels(decoder); + num_uncleared_mips_ += info->num_uncleared_mips(); + if (!info->SafeToRenderFrom()) { + ++num_unsafe_textures_; + } + return result; +} + +bool TextureManager::ClearTextureLevel( + GLES2Decoder* decoder,TextureManager::TextureInfo* info, + GLenum target, GLint level) { + DCHECK(info); + DCHECK(!info->IsDeleted()); + if (info->num_uncleared_mips() == 0) { + return true; + } + num_uncleared_mips_ -= info->num_uncleared_mips(); + DCHECK_GE(num_uncleared_mips_, 0); + if (!info->SafeToRenderFrom()) { + DCHECK_NE(0, num_unsafe_textures_); + --num_unsafe_textures_; + } + bool result = info->ClearLevel(decoder, target, level); + info->UpdateCleared(); + num_uncleared_mips_ += info->num_uncleared_mips(); + if (!info->SafeToRenderFrom()) { + ++num_unsafe_textures_; + } + return result; +} + void TextureManager::SetLevelInfo( const FeatureInfo* feature_info, TextureManager::TextureInfo* info, @@ -521,18 +717,30 @@ void TextureManager::SetLevelInfo( GLsizei depth, GLint border, GLenum format, - GLenum type) { + GLenum type, + bool cleared) { DCHECK(info); DCHECK(!info->IsDeleted()); if (!info->CanRender(feature_info)) { + DCHECK_NE(0, num_unrenderable_textures_); --num_unrenderable_textures_; } + if (!info->SafeToRenderFrom()) { + DCHECK_NE(0, num_unsafe_textures_); + --num_unsafe_textures_; + } + num_uncleared_mips_ -= info->num_uncleared_mips(); + DCHECK_GE(num_uncleared_mips_, 0); info->SetLevelInfo( feature_info, target, level, internal_format, width, height, depth, - border, format, type); + border, format, type, cleared); + num_uncleared_mips_ += info->num_uncleared_mips(); if (!info->CanRender(feature_info)) { ++num_unrenderable_textures_; } + if (!info->SafeToRenderFrom()) { + ++num_unsafe_textures_; + } } bool TextureManager::SetParameter( @@ -542,12 +750,20 @@ bool TextureManager::SetParameter( DCHECK(info); DCHECK(!info->IsDeleted()); if (!info->CanRender(feature_info)) { + DCHECK_NE(0, num_unrenderable_textures_); --num_unrenderable_textures_; } + if (!info->SafeToRenderFrom()) { + DCHECK_NE(0, num_unsafe_textures_); + --num_unsafe_textures_; + } bool result = info->SetParameter(feature_info, pname, param); if (!info->CanRender(feature_info)) { ++num_unrenderable_textures_; } + if (!info->SafeToRenderFrom()) { + ++num_unsafe_textures_; + } return result; } @@ -557,12 +773,23 @@ bool TextureManager::MarkMipmapsGenerated( DCHECK(info); DCHECK(!info->IsDeleted()); if (!info->CanRender(feature_info)) { + DCHECK_NE(0, num_unrenderable_textures_); --num_unrenderable_textures_; } + if (!info->SafeToRenderFrom()) { + DCHECK_NE(0, num_unsafe_textures_); + --num_unsafe_textures_; + } + num_uncleared_mips_ -= info->num_uncleared_mips(); + DCHECK_GE(num_uncleared_mips_, 0); bool result = info->MarkMipmapsGenerated(feature_info); + num_uncleared_mips_ += info->num_uncleared_mips(); if (!info->CanRender(feature_info)) { ++num_unrenderable_textures_; } + if (!info->SafeToRenderFrom()) { + ++num_unsafe_textures_; + } return result; } @@ -576,6 +803,10 @@ TextureManager::TextureInfo* TextureManager::CreateTextureInfo( if (!info->CanRender(feature_info)) { ++num_unrenderable_textures_; } + if (!info->SafeToRenderFrom()) { + ++num_unsafe_textures_; + } + num_uncleared_mips_ += info->num_uncleared_mips(); return info.get(); } @@ -591,8 +822,15 @@ void TextureManager::RemoveTextureInfo( if (it != texture_infos_.end()) { TextureInfo* info = it->second; if (!info->CanRender(feature_info)) { + DCHECK_NE(0, num_unrenderable_textures_); --num_unrenderable_textures_; } + if (!info->SafeToRenderFrom()) { + DCHECK_NE(0, num_unsafe_textures_); + --num_unsafe_textures_; + } + num_uncleared_mips_ -= info->num_uncleared_mips(); + DCHECK_GE(num_uncleared_mips_, 0); info->MarkAsDeleted(); texture_infos_.erase(it); } diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h index dbb2218..0fccbf6 100644 --- a/gpu/command_buffer/service/texture_manager.h +++ b/gpu/command_buffer/service/texture_manager.h @@ -16,6 +16,7 @@ namespace gpu { namespace gles2 { class FeatureInfo; +class GLES2Decoder; // This class keeps track of the textures and their sizes so we can do NPOT and // texture complete checking. @@ -32,6 +33,8 @@ class TextureManager { explicit TextureInfo(GLuint service_id) : service_id_(service_id), deleted_(false), + cleared_(true), + num_uncleared_mips_(0), target_(0), min_filter_(GL_NEAREST_MIPMAP_LINEAR), mag_filter_(GL_LINEAR), @@ -63,6 +66,10 @@ class TextureManager { return wrap_t_; } + int num_uncleared_mips() const { + return num_uncleared_mips_; + } + // True if this texture meets all the GLES2 criteria for rendering. // See section 3.8.2 of the GLES2 spec. bool CanRender(const FeatureInfo* feature_info) const; @@ -101,6 +108,10 @@ class TextureManager { return npot_; } + bool SafeToRenderFrom() const { + return cleared_; + } + // Returns true if mipmaps can be generated by GL. bool CanGenerateMipmaps(const FeatureInfo* feature_info) const; @@ -146,7 +157,7 @@ class TextureManager { } void DetachFromFramebuffer() { - DCHECK(framebuffer_attachment_count_ > 0); + DCHECK_GT(framebuffer_attachment_count_, 0); --framebuffer_attachment_count_; } @@ -158,6 +169,9 @@ class TextureManager { return stream_texture_; } + // Whether a particular level/face is cleared. + bool IsLevelCleared(GLenum target, GLint level); + private: friend class TextureManager; friend class base::RefCounted<TextureInfo>; @@ -166,7 +180,9 @@ class TextureManager { struct LevelInfo { LevelInfo() - : valid(false), + : cleared(true), + target(0), + level(-1), internal_format(0), width(0), height(0), @@ -176,7 +192,9 @@ class TextureManager { type(0) { } - bool valid; + bool cleared; + GLenum target; + GLint level; GLenum internal_format; GLsizei width; GLsizei height; @@ -197,7 +215,22 @@ class TextureManager { GLsizei depth, GLint border, GLenum format, - GLenum type); + GLenum type, + bool cleared); + + // Marks a particular level as cleared or uncleared. + void SetLevelCleared(GLenum target, GLint level); + + // Updates the cleared flag for this texture by inspecting all the mips. + void UpdateCleared(); + + // Clears any renderable uncleared levels. + // Returns false if a GL error was generated. + bool ClearRenderableLevels(GLES2Decoder* decoder); + + // Clears the level. + // Returns false if a GL error was generated. + bool ClearLevel(GLES2Decoder* decoder, GLenum target, GLint level); // Sets a texture parameter. // TODO(gman): Expand to SetParameteri,f,iv,fv @@ -236,6 +269,11 @@ class TextureManager { // Whether this texture has been deleted. bool deleted_; + // Whether all renderable mips of this texture have been cleared. + bool cleared_; + + int num_uncleared_mips_; + // The target. 0 if unset, otherwise GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP. GLenum target_; @@ -333,7 +371,11 @@ class TextureManager { GLsizei depth, GLint border, GLenum format, - GLenum type); + GLenum type, + bool cleared); + + // Sets a mip as cleared. + void SetLevelCleared(TextureInfo* info, GLenum target, GLint level); // Sets a texture parameter of a TextureInfo // TODO(gman): Expand to SetParameteri,f,iv,fv @@ -343,9 +385,14 @@ class TextureManager { // Makes each of the mip levels as though they were generated. // Returns false if that's not allowed for the given texture. - bool MarkMipmapsGenerated( - const FeatureInfo* feature_info, - TextureManager::TextureInfo* info); + bool MarkMipmapsGenerated(const FeatureInfo* feature_info, TextureInfo* info); + + // Clears any uncleared renderable levels. + bool ClearRenderableLevels(GLES2Decoder* decoder, TextureInfo* info); + + // Clear a specific level. + bool ClearTextureLevel( + GLES2Decoder* decoder,TextureInfo* info, GLenum target, GLint level); // Creates a new texture info. TextureInfo* CreateTextureInfo( @@ -378,6 +425,14 @@ class TextureManager { return num_unrenderable_textures_ > 0; } + bool HaveUnsafeTextures() const { + return num_unsafe_textures_ > 0; + } + + bool HaveUnclearedMips() const { + return num_uncleared_mips_ > 0; + } + GLuint black_texture_id(GLenum target) const { switch (target) { case GL_SAMPLER_2D: @@ -403,6 +458,8 @@ class TextureManager { GLint max_cube_map_levels_; int num_unrenderable_textures_; + int num_unsafe_textures_; + int num_uncleared_mips_; // Black (0,0,0,1) textures for when non-renderable textures are used. // NOTE: There is no corresponding TextureInfo for these textures. diff --git a/gpu/command_buffer/service/texture_manager_unittest.cc b/gpu/command_buffer/service/texture_manager_unittest.cc index 38d1969..44ce830 100644 --- a/gpu/command_buffer/service/texture_manager_unittest.cc +++ b/gpu/command_buffer/service/texture_manager_unittest.cc @@ -7,10 +7,12 @@ #include "base/memory/scoped_ptr.h" #include "gpu/command_buffer/common/gl_mock.h" #include "gpu/command_buffer/service/feature_info.h" +#include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h" #include "gpu/command_buffer/service/test_helper.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::Pointee; +using ::testing::Return; using ::testing::_; namespace gpu { @@ -78,6 +80,8 @@ TEST_F(TextureManagerTest, Basic) { const GLuint kService1Id = 11; const GLuint kClient2Id = 2; EXPECT_FALSE(manager_.HaveUnrenderableTextures()); + EXPECT_FALSE(manager_.HaveUnsafeTextures()); + EXPECT_FALSE(manager_.HaveUnclearedMips()); // Check we can create texture. manager_.CreateTextureInfo(&feature_info_, kClient1Id, kService1Id); // Check texture got created. @@ -99,7 +103,6 @@ TEST_F(TextureManagerTest, Basic) { TEST_F(TextureManagerTest, SetParameter) { const GLuint kClient1Id = 1; const GLuint kService1Id = 11; - EXPECT_FALSE(manager_.HaveUnrenderableTextures()); // Check we can create texture. manager_.CreateTextureInfo(&feature_info_, kClient1Id, kService1Id); // Check texture got created. @@ -134,7 +137,6 @@ TEST_F(TextureManagerTest, SetParameter) { TEST_F(TextureManagerTest, Destroy) { const GLuint kClient1Id = 1; const GLuint kService1Id = 11; - EXPECT_FALSE(manager_.HaveUnrenderableTextures()); // Check we can create texture. manager_.CreateTextureInfo(&feature_info_, kClient1Id, kService1Id); // Check texture got created. @@ -155,7 +157,6 @@ TEST_F(TextureManagerTest, Destroy) { TEST_F(TextureManagerTest, DestroyUnowned) { const GLuint kClient1Id = 1; const GLuint kService1Id = 11; - EXPECT_FALSE(manager_.HaveUnrenderableTextures()); // Check we can create texture. TextureManager::TextureInfo* created_info = manager_.CreateTextureInfo(&feature_info_, kClient1Id, kService1Id); @@ -329,12 +330,15 @@ TEST_F(TextureInfoTest, Basic) { EXPECT_FALSE(info_->cube_complete()); EXPECT_FALSE(info_->CanGenerateMipmaps(&feature_info_)); EXPECT_FALSE(info_->npot()); + EXPECT_EQ(0, info_->num_uncleared_mips()); EXPECT_FALSE(info_->CanRender(&feature_info_)); + EXPECT_TRUE(info_->SafeToRenderFrom()); EXPECT_EQ(static_cast<GLenum>(GL_NEAREST_MIPMAP_LINEAR), info_->min_filter()); EXPECT_EQ(static_cast<GLenum>(GL_LINEAR), info_->mag_filter()); EXPECT_EQ(static_cast<GLenum>(GL_REPEAT), info_->wrap_s()); EXPECT_EQ(static_cast<GLenum>(GL_REPEAT), info_->wrap_t()); EXPECT_TRUE(manager_.HaveUnrenderableTextures()); + EXPECT_FALSE(manager_.HaveUnsafeTextures()); } TEST_F(TextureInfoTest, POT2D) { @@ -342,10 +346,11 @@ TEST_F(TextureInfoTest, POT2D) { EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), info_->target()); // Check Setting level 0 to POT manager_.SetLevelInfo(&feature_info_, info_, - GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_FALSE(info_->npot()); EXPECT_FALSE(info_->texture_complete()); EXPECT_FALSE(info_->CanRender(&feature_info_)); + EXPECT_EQ(0, info_->num_uncleared_mips()); EXPECT_TRUE(manager_.HaveUnrenderableTextures()); // Set filters to something that will work with a single mip. manager_.SetParameter( @@ -365,7 +370,7 @@ TEST_F(TextureInfoTest, POT2D) { EXPECT_FALSE(manager_.HaveUnrenderableTextures()); // Change a mip. manager_.SetLevelInfo(&feature_info_, info_, - GL_TEXTURE_2D, 1, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + GL_TEXTURE_2D, 1, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_FALSE(info_->npot()); EXPECT_FALSE(info_->texture_complete()); EXPECT_TRUE(info_->CanGenerateMipmaps(&feature_info_)); @@ -373,7 +378,7 @@ TEST_F(TextureInfoTest, POT2D) { EXPECT_TRUE(manager_.HaveUnrenderableTextures()); // Set a level past the number of mips that would get generated. manager_.SetLevelInfo(&feature_info_, info_, - GL_TEXTURE_2D, 3, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + GL_TEXTURE_2D, 3, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_TRUE(info_->CanGenerateMipmaps(&feature_info_)); // Make mips. EXPECT_TRUE(manager_.MarkMipmapsGenerated(&feature_info_, info_)); @@ -387,7 +392,7 @@ TEST_F(TextureInfoTest, UnusedMips) { EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), info_->target()); // Set level zero to large size. manager_.SetLevelInfo(&feature_info_, info_, - GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_TRUE(manager_.MarkMipmapsGenerated(&feature_info_, info_)); EXPECT_FALSE(info_->npot()); EXPECT_TRUE(info_->texture_complete()); @@ -395,7 +400,7 @@ TEST_F(TextureInfoTest, UnusedMips) { EXPECT_FALSE(manager_.HaveUnrenderableTextures()); // Set level zero to large smaller (levels unused mips) manager_.SetLevelInfo(&feature_info_, info_, - GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_TRUE(manager_.MarkMipmapsGenerated(&feature_info_, info_)); EXPECT_FALSE(info_->npot()); EXPECT_TRUE(info_->texture_complete()); @@ -403,7 +408,7 @@ TEST_F(TextureInfoTest, UnusedMips) { EXPECT_FALSE(manager_.HaveUnrenderableTextures()); // Set an unused level to some size manager_.SetLevelInfo(&feature_info_, info_, - GL_TEXTURE_2D, 4, GL_RGBA, 16, 16, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + GL_TEXTURE_2D, 4, GL_RGBA, 16, 16, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_FALSE(info_->npot()); EXPECT_TRUE(info_->texture_complete()); EXPECT_TRUE(info_->CanRender(&feature_info_)); @@ -415,7 +420,7 @@ TEST_F(TextureInfoTest, NPOT2D) { EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), info_->target()); // Check Setting level 0 to NPOT manager_.SetLevelInfo(&feature_info_, info_, - GL_TEXTURE_2D, 0, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + GL_TEXTURE_2D, 0, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_TRUE(info_->npot()); EXPECT_FALSE(info_->texture_complete()); EXPECT_FALSE(info_->CanGenerateMipmaps(&feature_info_)); @@ -435,7 +440,7 @@ TEST_F(TextureInfoTest, NPOT2D) { EXPECT_FALSE(manager_.HaveUnrenderableTextures()); // Change it to POT. manager_.SetLevelInfo(&feature_info_, info_, - GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_FALSE(info_->npot()); EXPECT_FALSE(info_->texture_complete()); EXPECT_TRUE(info_->CanGenerateMipmaps(&feature_info_)); @@ -456,7 +461,7 @@ TEST_F(TextureInfoTest, NPOT2DNPOTOK) { EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), info->target()); // Check Setting level 0 to NPOT manager.SetLevelInfo(&feature_info, info, - GL_TEXTURE_2D, 0, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + GL_TEXTURE_2D, 0, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_TRUE(info->npot()); EXPECT_FALSE(info->texture_complete()); EXPECT_TRUE(info->CanGenerateMipmaps(&feature_info)); @@ -475,7 +480,7 @@ TEST_F(TextureInfoTest, POTCubeMap) { // Check Setting level 0 each face to POT manager_.SetLevelInfo(&feature_info_, info_, GL_TEXTURE_CUBE_MAP_POSITIVE_X, - 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_FALSE(info_->npot()); EXPECT_FALSE(info_->texture_complete()); EXPECT_FALSE(info_->cube_complete()); @@ -484,7 +489,7 @@ TEST_F(TextureInfoTest, POTCubeMap) { EXPECT_TRUE(manager_.HaveUnrenderableTextures()); manager_.SetLevelInfo(&feature_info_, info_, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, - 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_FALSE(info_->npot()); EXPECT_FALSE(info_->texture_complete()); EXPECT_FALSE(info_->cube_complete()); @@ -493,7 +498,7 @@ TEST_F(TextureInfoTest, POTCubeMap) { EXPECT_TRUE(manager_.HaveUnrenderableTextures()); manager_.SetLevelInfo(&feature_info_, info_, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, - 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_FALSE(info_->npot()); EXPECT_FALSE(info_->texture_complete()); EXPECT_FALSE(info_->cube_complete()); @@ -502,7 +507,7 @@ TEST_F(TextureInfoTest, POTCubeMap) { EXPECT_TRUE(manager_.HaveUnrenderableTextures()); manager_.SetLevelInfo(&feature_info_, info_, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, - 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_FALSE(info_->npot()); EXPECT_FALSE(info_->texture_complete()); EXPECT_FALSE(info_->cube_complete()); @@ -511,7 +516,7 @@ TEST_F(TextureInfoTest, POTCubeMap) { EXPECT_TRUE(manager_.HaveUnrenderableTextures()); manager_.SetLevelInfo(&feature_info_, info_, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, - 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_FALSE(info_->npot()); EXPECT_FALSE(info_->texture_complete()); EXPECT_FALSE(info_->cube_complete()); @@ -520,7 +525,7 @@ TEST_F(TextureInfoTest, POTCubeMap) { EXPECT_TRUE(manager_.HaveUnrenderableTextures()); manager_.SetLevelInfo(&feature_info_, info_, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, - 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_FALSE(info_->npot()); EXPECT_FALSE(info_->texture_complete()); EXPECT_TRUE(info_->cube_complete()); @@ -538,7 +543,7 @@ TEST_F(TextureInfoTest, POTCubeMap) { // Change a mip. manager_.SetLevelInfo(&feature_info_, info_, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, - 1, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + 1, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_FALSE(info_->npot()); EXPECT_FALSE(info_->texture_complete()); EXPECT_TRUE(info_->cube_complete()); @@ -546,7 +551,7 @@ TEST_F(TextureInfoTest, POTCubeMap) { // Set a level past the number of mips that would get generated. manager_.SetLevelInfo(&feature_info_, info_, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, - 3, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + 3, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); EXPECT_TRUE(info_->CanGenerateMipmaps(&feature_info_)); // Make mips. EXPECT_TRUE(manager_.MarkMipmapsGenerated(&feature_info_, info_)); @@ -557,7 +562,7 @@ TEST_F(TextureInfoTest, POTCubeMap) { TEST_F(TextureInfoTest, GetLevelSize) { manager_.SetInfoTarget(&feature_info_, info_, GL_TEXTURE_2D); manager_.SetLevelInfo(&feature_info_, info_, - GL_TEXTURE_2D, 1, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + GL_TEXTURE_2D, 1, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); GLsizei width = -1; GLsizei height = -1; EXPECT_FALSE(info_->GetLevelSize(GL_TEXTURE_2D, -1, &width, &height)); @@ -573,7 +578,7 @@ TEST_F(TextureInfoTest, GetLevelSize) { TEST_F(TextureInfoTest, GetLevelType) { manager_.SetInfoTarget(&feature_info_, info_, GL_TEXTURE_2D); manager_.SetLevelInfo(&feature_info_, info_, - GL_TEXTURE_2D, 1, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + GL_TEXTURE_2D, 1, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); GLenum type = -1; GLenum format = -1; EXPECT_FALSE(info_->GetLevelType(GL_TEXTURE_2D, -1, &type, &format)); @@ -589,7 +594,7 @@ TEST_F(TextureInfoTest, GetLevelType) { TEST_F(TextureInfoTest, ValidForTexture) { manager_.SetInfoTarget(&feature_info_, info_, GL_TEXTURE_2D); manager_.SetLevelInfo(&feature_info_, info_, - GL_TEXTURE_2D, 1, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE); + GL_TEXTURE_2D, 1, GL_RGBA, 4, 5, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); // Check bad face. EXPECT_FALSE(info_->ValidForTexture( GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, @@ -644,7 +649,7 @@ TEST_F(TextureInfoTest, FloatNotLinear) { manager.SetInfoTarget(&feature_info_, info, GL_TEXTURE_2D); EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), info->target()); manager.SetLevelInfo(&feature_info, info, - GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_FLOAT); + GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_FLOAT, true); EXPECT_FALSE(info->texture_complete()); manager.SetParameter(&feature_info, info, GL_TEXTURE_MAG_FILTER, GL_NEAREST); EXPECT_FALSE(info->texture_complete()); @@ -666,7 +671,7 @@ TEST_F(TextureInfoTest, FloatLinear) { manager.SetInfoTarget(&feature_info_, info, GL_TEXTURE_2D); EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), info->target()); manager.SetLevelInfo(&feature_info, info, - GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_FLOAT); + GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_FLOAT, true); EXPECT_TRUE(info->texture_complete()); manager.Destroy(false); } @@ -683,7 +688,7 @@ TEST_F(TextureInfoTest, HalfFloatNotLinear) { manager.SetInfoTarget(&feature_info_, info, GL_TEXTURE_2D); EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), info->target()); manager.SetLevelInfo(&feature_info, info, - GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_HALF_FLOAT_OES); + GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_HALF_FLOAT_OES, true); EXPECT_FALSE(info->texture_complete()); manager.SetParameter(&feature_info, info, GL_TEXTURE_MAG_FILTER, GL_NEAREST); EXPECT_FALSE(info->texture_complete()); @@ -705,7 +710,7 @@ TEST_F(TextureInfoTest, HalfFloatLinear) { manager.SetInfoTarget(&feature_info_, info, GL_TEXTURE_2D); EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), info->target()); manager.SetLevelInfo(&feature_info, info, - GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_HALF_FLOAT_OES); + GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_HALF_FLOAT_OES, true); EXPECT_TRUE(info->texture_complete()); manager.Destroy(false); } @@ -725,6 +730,156 @@ TEST_F(TextureInfoTest, EGLImageExternal) { manager.Destroy(false); } +TEST_F(TextureInfoTest, SafeUnsafe) { + static const GLuint kClient2Id = 2; + static const GLuint kService2Id = 12; + static const GLuint kClient3Id = 3; + static const GLuint kService3Id = 13; + EXPECT_FALSE(manager_.HaveUnclearedMips()); + EXPECT_EQ(0, info_->num_uncleared_mips()); + manager_.SetInfoTarget(&feature_info_, info_, GL_TEXTURE_2D); + manager_.SetLevelInfo(&feature_info_, info_, + GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false); + EXPECT_FALSE(info_->SafeToRenderFrom()); + EXPECT_TRUE(manager_.HaveUnsafeTextures()); + EXPECT_TRUE(manager_.HaveUnclearedMips()); + EXPECT_EQ(1, info_->num_uncleared_mips()); + manager_.SetLevelCleared(info_, GL_TEXTURE_2D, 0); + EXPECT_TRUE(info_->SafeToRenderFrom()); + EXPECT_FALSE(manager_.HaveUnsafeTextures()); + EXPECT_FALSE(manager_.HaveUnclearedMips()); + EXPECT_EQ(0, info_->num_uncleared_mips()); + manager_.SetLevelInfo(&feature_info_, info_, + GL_TEXTURE_2D, 1, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false); + EXPECT_FALSE(info_->SafeToRenderFrom()); + EXPECT_TRUE(manager_.HaveUnsafeTextures()); + EXPECT_TRUE(manager_.HaveUnclearedMips()); + EXPECT_EQ(1, info_->num_uncleared_mips()); + manager_.SetLevelCleared(info_, GL_TEXTURE_2D, 1); + EXPECT_TRUE(info_->SafeToRenderFrom()); + EXPECT_FALSE(manager_.HaveUnsafeTextures()); + EXPECT_FALSE(manager_.HaveUnclearedMips()); + EXPECT_EQ(0, info_->num_uncleared_mips()); + manager_.SetLevelInfo(&feature_info_, info_, + GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false); + manager_.SetLevelInfo(&feature_info_, info_, + GL_TEXTURE_2D, 1, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false); + EXPECT_FALSE(info_->SafeToRenderFrom()); + EXPECT_TRUE(manager_.HaveUnsafeTextures()); + EXPECT_TRUE(manager_.HaveUnclearedMips()); + EXPECT_EQ(2, info_->num_uncleared_mips()); + manager_.SetLevelCleared(info_, GL_TEXTURE_2D, 0); + EXPECT_FALSE(info_->SafeToRenderFrom()); + EXPECT_TRUE(manager_.HaveUnsafeTextures()); + EXPECT_TRUE(manager_.HaveUnclearedMips()); + EXPECT_EQ(1, info_->num_uncleared_mips()); + manager_.SetLevelCleared(info_, GL_TEXTURE_2D, 1); + EXPECT_TRUE(info_->SafeToRenderFrom()); + EXPECT_FALSE(manager_.HaveUnsafeTextures()); + EXPECT_FALSE(manager_.HaveUnclearedMips()); + EXPECT_EQ(0, info_->num_uncleared_mips()); + manager_.SetLevelInfo(&feature_info_, info_, + GL_TEXTURE_2D, 1, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false); + EXPECT_FALSE(info_->SafeToRenderFrom()); + EXPECT_TRUE(manager_.HaveUnsafeTextures()); + EXPECT_TRUE(manager_.HaveUnclearedMips()); + EXPECT_EQ(1, info_->num_uncleared_mips()); + manager_.MarkMipmapsGenerated(&feature_info_, info_); + EXPECT_TRUE(info_->SafeToRenderFrom()); + EXPECT_FALSE(manager_.HaveUnsafeTextures()); + EXPECT_FALSE(manager_.HaveUnclearedMips()); + EXPECT_EQ(0, info_->num_uncleared_mips()); + + manager_.CreateTextureInfo(&feature_info_, kClient2Id, kService2Id); + TextureManager::TextureInfo::Ref info2 = manager_.GetTextureInfo(kClient2Id); + ASSERT_TRUE(info2.get() != NULL); + manager_.SetInfoTarget(&feature_info_, info2, GL_TEXTURE_2D); + EXPECT_FALSE(manager_.HaveUnsafeTextures()); + EXPECT_FALSE(manager_.HaveUnclearedMips()); + EXPECT_EQ(0, info2->num_uncleared_mips()); + manager_.SetLevelInfo(&feature_info_, info2, + GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, true); + EXPECT_FALSE(manager_.HaveUnsafeTextures()); + EXPECT_FALSE(manager_.HaveUnclearedMips()); + EXPECT_EQ(0, info2->num_uncleared_mips()); + manager_.SetLevelInfo(&feature_info_, info2, + GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false); + EXPECT_TRUE(manager_.HaveUnsafeTextures()); + EXPECT_TRUE(manager_.HaveUnclearedMips()); + EXPECT_EQ(1, info2->num_uncleared_mips()); + + manager_.CreateTextureInfo(&feature_info_, kClient3Id, kService3Id); + TextureManager::TextureInfo::Ref info3 = manager_.GetTextureInfo(kClient3Id); + ASSERT_TRUE(info3.get() != NULL); + manager_.SetInfoTarget(&feature_info_, info3, GL_TEXTURE_2D); + manager_.SetLevelInfo(&feature_info_, info3, + GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false); + EXPECT_TRUE(manager_.HaveUnsafeTextures()); + EXPECT_TRUE(manager_.HaveUnclearedMips()); + EXPECT_EQ(1, info3->num_uncleared_mips()); + manager_.SetLevelCleared(info2, GL_TEXTURE_2D, 0); + EXPECT_TRUE(manager_.HaveUnsafeTextures()); + EXPECT_TRUE(manager_.HaveUnclearedMips()); + EXPECT_EQ(0, info2->num_uncleared_mips()); + manager_.SetLevelCleared(info3, GL_TEXTURE_2D, 0); + EXPECT_FALSE(manager_.HaveUnsafeTextures()); + EXPECT_FALSE(manager_.HaveUnclearedMips()); + EXPECT_EQ(0, info3->num_uncleared_mips()); + + manager_.SetLevelInfo(&feature_info_, info2, + GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false); + manager_.SetLevelInfo(&feature_info_, info3, + GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false); + EXPECT_TRUE(manager_.HaveUnsafeTextures()); + EXPECT_TRUE(manager_.HaveUnclearedMips()); + EXPECT_EQ(1, info2->num_uncleared_mips()); + EXPECT_EQ(1, info3->num_uncleared_mips()); + manager_.RemoveTextureInfo(&feature_info_, kClient3Id); + EXPECT_TRUE(manager_.HaveUnsafeTextures()); + EXPECT_TRUE(manager_.HaveUnclearedMips()); + manager_.RemoveTextureInfo(&feature_info_, kClient2Id); + EXPECT_FALSE(manager_.HaveUnsafeTextures()); + EXPECT_FALSE(manager_.HaveUnclearedMips()); +} + +TEST_F(TextureInfoTest, ClearTexture) { + scoped_ptr<MockGLES2Decoder> decoder(new gles2::MockGLES2Decoder()); + EXPECT_CALL(*decoder, ClearLevel(_, _, _, _, _, _, _, _)) + .WillRepeatedly(Return(true)); + manager_.SetInfoTarget(&feature_info_, info_, GL_TEXTURE_2D); + manager_.SetLevelInfo(&feature_info_, info_, + GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false); + manager_.SetLevelInfo(&feature_info_, info_, + GL_TEXTURE_2D, 1, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false); + EXPECT_FALSE(info_->SafeToRenderFrom()); + EXPECT_TRUE(manager_.HaveUnsafeTextures()); + EXPECT_TRUE(manager_.HaveUnclearedMips()); + EXPECT_EQ(2, info_->num_uncleared_mips()); + manager_.ClearRenderableLevels(decoder.get(), info_); + EXPECT_TRUE(info_->SafeToRenderFrom()); + EXPECT_FALSE(manager_.HaveUnsafeTextures()); + EXPECT_FALSE(manager_.HaveUnclearedMips()); + EXPECT_EQ(0, info_->num_uncleared_mips()); + manager_.SetLevelInfo(&feature_info_, info_, + GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false); + manager_.SetLevelInfo(&feature_info_, info_, + GL_TEXTURE_2D, 1, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, false); + EXPECT_FALSE(info_->SafeToRenderFrom()); + EXPECT_TRUE(manager_.HaveUnsafeTextures()); + EXPECT_TRUE(manager_.HaveUnclearedMips()); + EXPECT_EQ(2, info_->num_uncleared_mips()); + manager_.ClearTextureLevel(decoder.get(), info_, GL_TEXTURE_2D, 0); + EXPECT_FALSE(info_->SafeToRenderFrom()); + EXPECT_TRUE(manager_.HaveUnsafeTextures()); + EXPECT_TRUE(manager_.HaveUnclearedMips()); + EXPECT_EQ(1, info_->num_uncleared_mips()); + manager_.ClearTextureLevel(decoder.get(), info_, GL_TEXTURE_2D, 1); + EXPECT_TRUE(info_->SafeToRenderFrom()); + EXPECT_FALSE(manager_.HaveUnsafeTextures()); + EXPECT_FALSE(manager_.HaveUnclearedMips()); + EXPECT_EQ(0, info_->num_uncleared_mips()); +} + } // namespace gles2 } // namespace gpu |