diff options
author | reveman@chromium.org <reveman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-19 10:22:56 +0000 |
---|---|---|
committer | reveman@chromium.org <reveman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-19 10:22:56 +0000 |
commit | 8a48e732c0dc67b22f0b2cdce8bf750e273a4628 (patch) | |
tree | a3f0260baa264de95d1ffb21815ebcf223dfdee2 /gpu/command_buffer/service | |
parent | 10ebc9183caa8dd392ec89cc6c571332c4194741 (diff) | |
download | chromium_src-8a48e732c0dc67b22f0b2cdce8bf750e273a4628.zip chromium_src-8a48e732c0dc67b22f0b2cdce8bf750e273a4628.tar.gz chromium_src-8a48e732c0dc67b22f0b2cdce8bf750e273a4628.tar.bz2 |
gpu: Add Will/DidUseTexImage to GLImage API.
WillUseTexImage/DidUseTexImage is called before/after the image is
used for sampling. The result is that the client only has to call
bind/releaseTexImage2D when contents have changed, which allows
for more efficient GLImage implementations as work required before
use can be separated from work required when contents have changed.
BUG=261649
TEST=gpu_unittests --gtest_filter=SharedTextureTest.Images && gpu_unittests --gtest_filter=GLES2DecoderWithShaderTest.UseTexImage && cc_unittests --gtest_filter=ResourceProviderTests/ResourceProviderTest.Image_GLTexture* && gl_tests --gtest_filter=MockGpuMemoryBufferTest.Lifecycle
Review URL: https://codereview.chromium.org/23129010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@229532 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu/command_buffer/service')
-rw-r--r-- | gpu/command_buffer/service/framebuffer_manager.cc | 26 | ||||
-rw-r--r-- | gpu/command_buffer/service/framebuffer_manager.h | 28 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder.cc | 117 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc | 140 | ||||
-rw-r--r-- | gpu/command_buffer/service/texture_manager.cc | 40 | ||||
-rw-r--r-- | gpu/command_buffer/service/texture_manager.h | 17 | ||||
-rw-r--r-- | gpu/command_buffer/service/texture_manager_unittest.cc | 60 |
7 files changed, 404 insertions, 24 deletions
diff --git a/gpu/command_buffer/service/framebuffer_manager.cc b/gpu/command_buffer/service/framebuffer_manager.cc index aa30cd3..194fe37 100644 --- a/gpu/command_buffer/service/framebuffer_manager.cc +++ b/gpu/command_buffer/service/framebuffer_manager.cc @@ -93,7 +93,7 @@ class RenderbufferAttachment return true; } - virtual void DetachFromFramebuffer() const OVERRIDE { + virtual void DetachFromFramebuffer(Framebuffer* framebuffer) const OVERRIDE { // Nothing to do for renderbuffers. } @@ -197,8 +197,10 @@ class TextureAttachment return texture_ref_->texture()->CanRenderTo(); } - virtual void DetachFromFramebuffer() const OVERRIDE { + virtual void DetachFromFramebuffer(Framebuffer* framebuffer) + const OVERRIDE { texture_ref_->texture()->DetachFromFramebuffer(); + framebuffer->OnTextureRefDetached(texture_ref_.get()); } virtual bool ValidForAttachmentType( @@ -241,6 +243,10 @@ class TextureAttachment DISALLOW_COPY_AND_ASSIGN(TextureAttachment); }; +FramebufferManager::TextureDetachObserver::TextureDetachObserver() {} + +FramebufferManager::TextureDetachObserver::~TextureDetachObserver() {} + FramebufferManager::FramebufferManager( uint32 max_draw_buffers, uint32 max_color_attachments) : framebuffer_state_change_count_(1), @@ -263,7 +269,7 @@ void Framebuffer::MarkAsDeleted() { deleted_ = true; while (!attachments_.empty()) { Attachment* attachment = attachments_.begin()->second.get(); - attachment->DetachFromFramebuffer(); + attachment->DetachFromFramebuffer(this); attachments_.erase(attachments_.begin()); } } @@ -548,7 +554,7 @@ void Framebuffer::AttachRenderbuffer( GLenum attachment, Renderbuffer* renderbuffer) { const Attachment* a = GetAttachment(attachment); if (a) - a->DetachFromFramebuffer(); + a->DetachFromFramebuffer(this); if (renderbuffer) { attachments_[attachment] = scoped_refptr<Attachment>( new RenderbufferAttachment(renderbuffer)); @@ -563,7 +569,7 @@ void Framebuffer::AttachTexture( GLint level, GLsizei samples) { const Attachment* a = GetAttachment(attachment); if (a) - a->DetachFromFramebuffer(); + a->DetachFromFramebuffer(this); if (texture_ref) { attachments_[attachment] = scoped_refptr<Attachment>( new TextureAttachment(texture_ref, target, level, samples)); @@ -584,6 +590,10 @@ const Framebuffer::Attachment* return NULL; } +void Framebuffer::OnTextureRefDetached(TextureRef* texture) { + manager_->OnTextureRefDetached(texture); +} + bool FramebufferManager::GetClientId( GLuint service_id, GLuint* client_id) const { // This doesn't need to be fast. It's only used during slow queries. @@ -621,6 +631,12 @@ bool FramebufferManager::IsComplete( framebuffer_state_change_count_; } +void FramebufferManager::OnTextureRefDetached(TextureRef* texture) { + FOR_EACH_OBSERVER(TextureDetachObserver, + texture_detach_observers_, + OnTextureRefDetachedFromFramebuffer(texture)); +} + } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/service/framebuffer_manager.h b/gpu/command_buffer/service/framebuffer_manager.h index 7955ac1..06a75b4 100644 --- a/gpu/command_buffer/service/framebuffer_manager.h +++ b/gpu/command_buffer/service/framebuffer_manager.h @@ -9,6 +9,7 @@ #include "base/containers/hash_tables.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/observer_list.h" #include "gpu/command_buffer/service/gl_utils.h" #include "gpu/gpu_export.h" @@ -41,7 +42,7 @@ class GPU_EXPORT Framebuffer : public base::RefCounted<Framebuffer> { virtual bool IsRenderbuffer( Renderbuffer* renderbuffer) const = 0; virtual bool CanRenderTo() const = 0; - virtual void DetachFromFramebuffer() const = 0; + virtual void DetachFromFramebuffer(Framebuffer* framebuffer) const = 0; virtual bool ValidForAttachmentType( GLenum attachment_type, uint32 max_color_attachments) = 0; virtual void AddToSignature( @@ -130,6 +131,8 @@ class GPU_EXPORT Framebuffer : public base::RefCounted<Framebuffer> { return allow_framebuffer_combo_complete_map_; } + void OnTextureRefDetached(TextureRef* texture); + private: friend class FramebufferManager; friend class base::RefCounted<Framebuffer>; @@ -197,6 +200,17 @@ struct DecoderFramebufferState { // so we can correctly clear them. class GPU_EXPORT FramebufferManager { public: + class GPU_EXPORT TextureDetachObserver { + public: + TextureDetachObserver(); + virtual ~TextureDetachObserver(); + + virtual void OnTextureRefDetachedFromFramebuffer(TextureRef* texture) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(TextureDetachObserver); + }; + FramebufferManager(uint32 max_draw_buffers, uint32 max_color_attachments); ~FramebufferManager(); @@ -230,12 +244,22 @@ class GPU_EXPORT FramebufferManager { (framebuffer_state_change_count_ + 1) | 0x80000000U; } + void AddObserver(TextureDetachObserver* observer) { + texture_detach_observers_.AddObserver(observer); + } + + void RemoveObserver(TextureDetachObserver* observer) { + texture_detach_observers_.RemoveObserver(observer); + } + private: friend class Framebuffer; void StartTracking(Framebuffer* framebuffer); void StopTracking(Framebuffer* framebuffer); + void OnTextureRefDetached(TextureRef* texture); + // Info for each framebuffer in the system. typedef base::hash_map<GLuint, scoped_refptr<Framebuffer> > FramebufferMap; @@ -254,6 +278,8 @@ class GPU_EXPORT FramebufferManager { uint32 max_draw_buffers_; uint32 max_color_attachments_; + ObserverList<TextureDetachObserver> texture_detach_observers_; + DISALLOW_COPY_AND_ASSIGN(FramebufferManager); }; diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index dd02c9c..5145daa 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -521,7 +521,8 @@ bool GLES2Decoder::IsAngle() { // This class implements GLES2Decoder so we don't have to expose all the GLES2 // cmd stuff to outside this class. -class GLES2DecoderImpl : public GLES2Decoder { +class GLES2DecoderImpl : public GLES2Decoder, + public FramebufferManager::TextureDetachObserver { public: // Used by PrepForSetUniformByLocation to validate types. struct BaseUniformInfo { @@ -642,6 +643,10 @@ class GLES2DecoderImpl : public GLES2Decoder { virtual error::ContextLostReason GetContextLostReason() OVERRIDE; + // Overridden from FramebufferManager::TextureDetachObserver: + virtual void OnTextureRefDetachedFromFramebuffer( + TextureRef* texture) OVERRIDE; + private: friend class ScopedFrameBufferBinder; friend class ScopedResolvedFrameBufferBinder; @@ -1377,9 +1382,14 @@ class GLES2DecoderImpl : public GLES2Decoder { // buffer and bind the texture implicitly. void UpdateStreamTextureIfNeeded(Texture* texture, GLuint texture_unit_index); - // Returns false if unrenderable textures were replaced. + // If an image is bound to texture, this will call Will/DidUseTexImage + // if needed. + void DoWillUseTexImageIfNeeded(Texture* texture, GLenum textarget); + void DoDidUseTexImageIfNeeded(Texture* texture, GLenum textarget); + + // Returns false if textures were replaced. bool PrepareTexturesForRender(); - void RestoreStateForNonRenderableTextures(); + void RestoreStateForTextures(); // Returns true if GL_FIXED attribs were simulated. bool SimulateFixedAttribs( @@ -2472,6 +2482,8 @@ bool GLES2DecoderImpl::Initialize( AsyncPixelTransferManager::Create(context.get())); async_pixel_transfer_manager_->Initialize(texture_manager()); + framebuffer_manager()->AddObserver(this); + return true; } @@ -3203,6 +3215,8 @@ void GLES2DecoderImpl::Destroy(bool have_context) { // by the context group. async_pixel_transfer_manager_.reset(); + framebuffer_manager()->RemoveObserver(this); + if (group_.get()) { group_->Destroy(this, have_context); group_ = NULL; @@ -4866,6 +4880,9 @@ void GLES2DecoderImpl::DoFramebufferTexture2DCommon( return; } + if (texture_ref) + DoWillUseTexImageIfNeeded(texture_ref->texture(), textarget); + LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(name); if (0 == samples) { glFramebufferTexture2DEXT(target, attachment, textarget, service_id, level); @@ -4886,6 +4903,10 @@ void GLES2DecoderImpl::DoFramebufferTexture2DCommon( if (framebuffer == framebuffer_state_.bound_draw_framebuffer.get()) { framebuffer_state_.clear_state_dirty = true; } + + if (texture_ref) + DoDidUseTexImageIfNeeded(texture_ref->texture(), textarget); + OnFboChanged(); } @@ -5723,11 +5744,43 @@ void GLES2DecoderImpl::UpdateStreamTextureIfNeeded(Texture* texture, } } +void GLES2DecoderImpl::DoWillUseTexImageIfNeeded( + Texture* texture, GLenum textarget) { + // Image is already in use if texture is attached to a framebuffer. + if (texture && !texture->IsAttachedToFramebuffer()) { + gfx::GLImage* image = texture->GetLevelImage(textarget, 0); + if (image) { + ScopedGLErrorSuppressor suppressor( + "GLES2DecoderImpl::DoWillUseTexImageIfNeeded", + GetErrorState()); + glBindTexture(textarget, texture->service_id()); + image->WillUseTexImage(); + RestoreCurrentTexture2DBindings(); + } + } +} + +void GLES2DecoderImpl::DoDidUseTexImageIfNeeded( + Texture* texture, GLenum textarget) { + // Image is still in use if texture is attached to a framebuffer. + if (texture && !texture->IsAttachedToFramebuffer()) { + gfx::GLImage* image = texture->GetLevelImage(textarget, 0); + if (image) { + ScopedGLErrorSuppressor suppressor( + "GLES2DecoderImpl::DoDidUseTexImageIfNeeded", + GetErrorState()); + glBindTexture(textarget, texture->service_id()); + image->DidUseTexImage(); + RestoreCurrentTexture2DBindings(); + } + } +} + bool GLES2DecoderImpl::PrepareTexturesForRender() { DCHECK(state_.current_program.get()); - bool have_unrenderable_textures = - texture_manager()->HaveUnrenderableTextures(); - if (!have_unrenderable_textures && !features().oes_egl_image_external) { + if (!texture_manager()->HaveUnrenderableTextures() && + !texture_manager()->HaveImages() && + !features().oes_egl_image_external) { return true; } @@ -5742,12 +5795,9 @@ bool GLES2DecoderImpl::PrepareTexturesForRender() { GLuint texture_unit_index = uniform_info->texture_units[jj]; if (texture_unit_index < state_.texture_units.size()) { TextureUnit& texture_unit = state_.texture_units[texture_unit_index]; - TextureRef* texture = + TextureRef* texture_ref = texture_unit.GetInfoForSamplerType(uniform_info->type).get(); - if (texture) - UpdateStreamTextureIfNeeded(texture->texture(), texture_unit_index); - if (have_unrenderable_textures && - (!texture || !texture_manager()->CanRender(texture))) { + if (!texture_ref || !texture_manager()->CanRender(texture_ref)) { textures_set = true; glActiveTexture(GL_TEXTURE0 + texture_unit_index); glBindTexture( @@ -5759,7 +5809,21 @@ bool GLES2DecoderImpl::PrepareTexturesForRender() { " is not renderable. It maybe non-power-of-2 and have" " incompatible texture filtering or is not" " 'texture complete'"); + continue; + } + + Texture* texture = texture_ref->texture(); + gfx::GLImage* image = texture->GetLevelImage(texture->target(), 0); + if (image && !texture->IsAttachedToFramebuffer()) { + ScopedGLErrorSuppressor suppressor( + "GLES2DecoderImpl::PrepareTexturesForRender", GetErrorState()); + textures_set = true; + glActiveTexture(GL_TEXTURE0 + texture_unit_index); + image->WillUseTexImage(); + continue; } + + UpdateStreamTextureIfNeeded(texture, texture_unit_index); } // else: should this be an error? } @@ -5767,7 +5831,7 @@ bool GLES2DecoderImpl::PrepareTexturesForRender() { return !textures_set; } -void GLES2DecoderImpl::RestoreStateForNonRenderableTextures() { +void GLES2DecoderImpl::RestoreStateForTextures() { DCHECK(state_.current_program.get()); const Program::SamplerIndices& sampler_indices = state_.current_program->sampler_indices(); @@ -5780,9 +5844,7 @@ void GLES2DecoderImpl::RestoreStateForNonRenderableTextures() { if (texture_unit_index < state_.texture_units.size()) { TextureUnit& texture_unit = state_.texture_units[texture_unit_index]; TextureRef* texture_ref = - uniform_info->type == GL_SAMPLER_2D - ? texture_unit.bound_texture_2d.get() - : texture_unit.bound_texture_cube_map.get(); + texture_unit.GetInfoForSamplerType(uniform_info->type).get(); if (!texture_ref || !texture_manager()->CanRender(texture_ref)) { glActiveTexture(GL_TEXTURE0 + texture_unit_index); // Get the texture_ref info that was previously bound here. @@ -5791,6 +5853,17 @@ void GLES2DecoderImpl::RestoreStateForNonRenderableTextures() { : texture_unit.bound_texture_cube_map.get(); glBindTexture(texture_unit.bind_target, texture_ref ? texture_ref->service_id() : 0); + continue; + } + + Texture* texture = texture_ref->texture(); + gfx::GLImage* image = texture->GetLevelImage(texture->target(), 0); + if (image && !texture->IsAttachedToFramebuffer()) { + ScopedGLErrorSuppressor suppressor( + "GLES2DecoderImpl::RestoreStateForTextures", GetErrorState()); + glActiveTexture(GL_TEXTURE0 + texture_unit_index); + image->DidUseTexImage(); + continue; } } } @@ -6133,7 +6206,7 @@ error::Error GLES2DecoderImpl::DoDrawArrays( } ProcessPendingQueries(); if (textures_set) { - RestoreStateForNonRenderableTextures(); + RestoreStateForTextures(); } if (simulated_fixed_attribs) { RestoreStateForSimulatedFixedAttribs(); @@ -6267,7 +6340,7 @@ error::Error GLES2DecoderImpl::DoDrawElements( ProcessPendingQueries(); if (textures_set) { - RestoreStateForNonRenderableTextures(); + RestoreStateForTextures(); } if (simulated_fixed_attribs) { RestoreStateForSimulatedFixedAttribs(); @@ -9601,6 +9674,8 @@ void GLES2DecoderImpl::DoCopyTextureCHROMIUM( dest_texture_ref, GL_TEXTURE_2D, level, true); } + DoWillUseTexImageIfNeeded(source_texture, source_texture->target()); + // GL_TEXTURE_EXTERNAL_OES texture requires apply a transform matrix // before presenting. if (source_texture->target() == GL_TEXTURE_EXTERNAL_OES) { @@ -9633,6 +9708,8 @@ void GLES2DecoderImpl::DoCopyTextureCHROMIUM( unpack_premultiply_alpha_, unpack_unpremultiply_alpha_); } + + DoDidUseTexImageIfNeeded(source_texture, source_texture->target()); } static GLenum ExtractTypeFromStorageFormat(GLenum internalformat) { @@ -10298,6 +10375,12 @@ error::Error GLES2DecoderImpl::HandleWaitAsyncTexImage2DCHROMIUM( return error::kNoError; } +void GLES2DecoderImpl::OnTextureRefDetachedFromFramebuffer( + TextureRef* texture_ref) { + Texture* texture = texture_ref->texture(); + DoDidUseTexImageIfNeeded(texture, texture->target()); +} + // Include the auto-generated part of this file. We split this because it means // we can easily edit the non-auto generated parts right here in this file // instead of having to edit some template or the code generator. diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc index 72c2fcd..573ba6e 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc @@ -7886,6 +7886,146 @@ TEST_F(GLES2DecoderTest, ReleaseTexImage2DCHROMIUM) { EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 0) == NULL); } +class MockGLImage : public gfx::GLImage { + public: + MockGLImage() {} + + // Overridden from gfx::GLImage: + MOCK_METHOD0(Destroy, void()); + MOCK_METHOD0(GetSize, gfx::Size()); + MOCK_METHOD0(BindTexImage, bool()); + MOCK_METHOD0(ReleaseTexImage, void()); + MOCK_METHOD0(WillUseTexImage, void()); + MOCK_METHOD0(DidUseTexImage, void()); + + protected: + virtual ~MockGLImage() {} +}; + +TEST_F(GLES2DecoderWithShaderTest, UseTexImage) { + DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); + DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, + kSharedMemoryId, kSharedMemoryOffset); + + TextureRef* texture_ref = group().texture_manager()->GetTexture( + client_texture_id_); + ASSERT_TRUE(texture_ref != NULL); + Texture* texture = texture_ref->texture(); + EXPECT_EQ(kServiceTextureId, texture->service_id()); + + const int32 kImageId = 1; + scoped_refptr<MockGLImage> image(new MockGLImage); + group().image_manager()->AddImage(image.get(), kImageId); + + // Bind image to texture. + EXPECT_CALL(*image, BindTexImage()) + .Times(1) + .WillOnce(Return(true)) + .RetiresOnSaturation(); + EXPECT_CALL(*image, GetSize()) + .Times(1) + .WillOnce(Return(gfx::Size(1, 1))) + .RetiresOnSaturation(); + // ScopedGLErrorSuppressor calls GetError on its constructor and destructor. + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + BindTexImage2DCHROMIUM bind_tex_image_2d_cmd; + bind_tex_image_2d_cmd.Init(GL_TEXTURE_2D, kImageId); + EXPECT_EQ(error::kNoError, ExecuteCmd(bind_tex_image_2d_cmd)); + + AddExpectationsForSimulatedAttrib0(kNumVertices, 0); + SetupExpectationsForApplyingDefaultDirtyState(); + + // ScopedGLErrorSuppressor calls GetError on its constructor and destructor. + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .WillOnce(Return(GL_NO_ERROR)) + .WillOnce(Return(GL_NO_ERROR)) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0)) + .Times(3) + .RetiresOnSaturation(); + EXPECT_CALL(*image, WillUseTexImage()) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*image, DidUseTexImage()) + .Times(1) + .RetiresOnSaturation(); + 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()); + + DoBindFramebuffer(GL_FRAMEBUFFER, client_framebuffer_id_, + kServiceFramebufferId); + // ScopedGLErrorSuppressor calls GetError on its constructor and destructor. + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_2D, kServiceTextureId)) + .Times(2) + .RetiresOnSaturation(); + // Image will be 'in use' as long as bound to a framebuffer. + EXPECT_CALL(*image, WillUseTexImage()) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, FramebufferTexture2DEXT( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + kServiceTextureId, 0)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + 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)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + + // ScopedGLErrorSuppressor calls GetError on its constructor and destructor. + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, FramebufferRenderbufferEXT( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, + kServiceRenderbufferId)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_2D, kServiceTextureId)) + .Times(2) + .RetiresOnSaturation(); + // Image should no longer be 'in use' after being unbound from framebuffer. + EXPECT_CALL(*image, DidUseTexImage()) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, GetError()) + .WillOnce(Return(GL_NO_ERROR)) + .WillOnce(Return(GL_NO_ERROR)) + .RetiresOnSaturation(); + FramebufferRenderbuffer fbrb_cmd; + fbrb_cmd.Init( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, + client_renderbuffer_id_); + EXPECT_EQ(error::kNoError, ExecuteCmd(fbrb_cmd)); +} + TEST_F(GLES2DecoderManualInitTest, GpuMemoryManagerCHROMIUM) { InitDecoder( "GL_ARB_texture_rectangle", // extensions diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc index 9684493..a7f32e0 100644 --- a/gpu/command_buffer/service/texture_manager.cc +++ b/gpu/command_buffer/service/texture_manager.cc @@ -80,6 +80,7 @@ TextureManager::~TextureManager() { DCHECK_EQ(0, num_unrenderable_textures_); DCHECK_EQ(0, num_unsafe_textures_); DCHECK_EQ(0, num_uncleared_mips_); + DCHECK_EQ(0, num_images_); } void TextureManager::Destroy(bool have_context) { @@ -118,6 +119,7 @@ Texture::Texture(GLuint service_id) framebuffer_attachment_count_(0), stream_texture_(false), immutable_(false), + has_images_(false), estimated_size_(0), can_render_condition_(CAN_RENDER_ALWAYS) { } @@ -431,6 +433,29 @@ void Texture::UpdateCanRenderCondition() { can_render_condition_ = can_render_condition; } +void Texture::UpdateHasImages() { + if (level_infos_.empty()) + return; + + bool has_images = false; + for (size_t ii = 0; ii < level_infos_.size(); ++ii) { + for (size_t jj = 0; jj < level_infos_[ii].size(); ++jj) { + const Texture::LevelInfo& info = level_infos_[ii][jj]; + if (info.image.get() != NULL) { + has_images = true; + break; + } + } + } + + if (has_images_ == has_images) + return; + has_images_ = has_images; + int delta = has_images ? +1 : -1; + for (RefSet::iterator it = refs_.begin(); it != refs_.end(); ++it) + (*it)->manager()->UpdateNumImages(delta); +} + void Texture::IncAllFramebufferStateChangeCount() { for (RefSet::iterator it = refs_.begin(); it != refs_.end(); ++it) (*it)->manager()->IncFramebufferStateChangeCount(); @@ -479,6 +504,7 @@ void Texture::SetLevelInfo( Update(feature_info); UpdateCleared(); UpdateCanRenderCondition(); + UpdateHasImages(); if (IsAttachedToFramebuffer()) { // TODO(gman): If textures tracked which framebuffers they were attached to // we could just mark those framebuffers as not complete. @@ -780,6 +806,7 @@ void Texture::SetLevelImage( DCHECK_EQ(info.level, level); info.image = image; UpdateCanRenderCondition(); + UpdateHasImages(); } gfx::GLImage* Texture::GetLevelImage( @@ -845,6 +872,7 @@ TextureManager::TextureManager(MemoryTracker* memory_tracker, num_unrenderable_textures_(0), num_unsafe_textures_(0), num_uncleared_mips_(0), + num_images_(0), texture_count_(0), have_context_(true) { for (int ii = 0; ii < kNumDefaultTextures; ++ii) { @@ -1130,6 +1158,8 @@ void TextureManager::StartTracking(TextureRef* ref) { ++num_unsafe_textures_; if (!texture->CanRender(feature_info_.get())) ++num_unrenderable_textures_; + if (texture->HasImages()) + ++num_images_; } void TextureManager::StopTracking(TextureRef* ref) { @@ -1144,6 +1174,10 @@ void TextureManager::StopTracking(TextureRef* ref) { } --texture_count_; + if (texture->HasImages()) { + DCHECK_NE(0, num_images_); + --num_images_; + } if (!texture->CanRender(feature_info_.get())) { DCHECK_NE(0, num_unrenderable_textures_); --num_unrenderable_textures_; @@ -1237,10 +1271,14 @@ void TextureManager::UpdateCanRenderCondition( ++num_unrenderable_textures_; } +void TextureManager::UpdateNumImages(int delta) { + num_images_ += delta; + DCHECK_GE(num_images_, 0); +} + void TextureManager::IncFramebufferStateChangeCount() { if (framebuffer_manager_) framebuffer_manager_->IncFramebufferStateChangeCount(); - } bool TextureManager::ValidateTextureParameters( diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h index 0a6e776..3dabf6d 100644 --- a/gpu/command_buffer/service/texture_manager.h +++ b/gpu/command_buffer/service/texture_manager.h @@ -114,6 +114,10 @@ class GPU_EXPORT Texture { // does not exist. gfx::GLImage* GetLevelImage(GLint target, GLint level) const; + bool HasImages() const { + return has_images_; + } + // Returns true of the given dimensions are inside the dimensions of the // level and if the format and type match the level. bool ValidForTexture( @@ -317,6 +321,10 @@ class GPU_EXPORT Texture { // texture. void UpdateCanRenderCondition(); + // Updates the images count in all the managers referencing this + // texture. + void UpdateHasImages(); + // Increment the framebuffer state change count in all the managers // referencing this texture. void IncAllFramebufferStateChangeCount(); @@ -378,6 +386,9 @@ class GPU_EXPORT Texture { // or dimensions of the texture object can be made. bool immutable_; + // Whether or not this texture has images. + bool has_images_; + // Size in bytes this texture is assumed to take in memory. uint32 estimated_size_; @@ -656,6 +667,10 @@ class GPU_EXPORT TextureManager { return num_uncleared_mips_ > 0; } + bool HaveImages() const { + return num_images_ > 0; + } + GLuint black_texture_id(GLenum target) const { switch (target) { case GL_SAMPLER_2D: @@ -758,6 +773,7 @@ class GPU_EXPORT TextureManager { void UpdateUnclearedMips(int delta); void UpdateCanRenderCondition(Texture::CanRenderCondition old_condition, Texture::CanRenderCondition new_condition); + void UpdateNumImages(int delta); void IncFramebufferStateChangeCount(); MemoryTypeTracker* GetMemTracker(GLenum texture_pool); @@ -781,6 +797,7 @@ class GPU_EXPORT TextureManager { int num_unrenderable_textures_; int num_unsafe_textures_; int num_uncleared_mips_; + int num_images_; // Counts the number of Textures allocated with 'this' as its manager. // Allows to check no Texture will outlive this. diff --git a/gpu/command_buffer/service/texture_manager_unittest.cc b/gpu/command_buffer/service/texture_manager_unittest.cc index 2f5f036..312adfd 100644 --- a/gpu/command_buffer/service/texture_manager_unittest.cc +++ b/gpu/command_buffer/service/texture_manager_unittest.cc @@ -2367,5 +2367,65 @@ TEST_F(SharedTextureTest, Memory) { memory_tracker2_->GetSize(MemoryTracker::kUnmanaged)); } +TEST_F(SharedTextureTest, Images) { + scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10); + scoped_refptr<TextureRef> ref2 = + texture_manager2_->Consume(20, ref1->texture()); + + texture_manager1_->SetTarget(ref1.get(), GL_TEXTURE_2D); + texture_manager1_->SetLevelInfo(ref1.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 2, + 2, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(ref1->texture()->HasImages()); + EXPECT_FALSE(ref2->texture()->HasImages()); + EXPECT_FALSE(texture_manager1_->HaveImages()); + EXPECT_FALSE(texture_manager2_->HaveImages()); + texture_manager1_->SetLevelImage(ref1.get(), + GL_TEXTURE_2D, + 1, + gfx::GLImage::CreateGLImage(0).get()); + EXPECT_TRUE(ref1->texture()->HasImages()); + EXPECT_TRUE(ref2->texture()->HasImages()); + EXPECT_TRUE(texture_manager1_->HaveImages()); + EXPECT_TRUE(texture_manager2_->HaveImages()); + texture_manager1_->SetLevelImage(ref1.get(), + GL_TEXTURE_2D, + 1, + gfx::GLImage::CreateGLImage(0).get()); + EXPECT_TRUE(ref1->texture()->HasImages()); + EXPECT_TRUE(ref2->texture()->HasImages()); + EXPECT_TRUE(texture_manager1_->HaveImages()); + EXPECT_TRUE(texture_manager2_->HaveImages()); + texture_manager1_->SetLevelInfo(ref1.get(), + GL_TEXTURE_2D, + 1, + GL_RGBA, + 2, + 2, + 1, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + true); + EXPECT_FALSE(ref1->texture()->HasImages()); + EXPECT_FALSE(ref2->texture()->HasImages()); + EXPECT_FALSE(texture_manager1_->HaveImages()); + EXPECT_FALSE(texture_manager1_->HaveImages()); + + EXPECT_CALL(*gl_, DeleteTextures(1, _)) + .Times(1) + .RetiresOnSaturation(); + texture_manager1_->RemoveTexture(10); + texture_manager2_->RemoveTexture(20); +} + } // namespace gles2 } // namespace gpu |