diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc index 11eaab07a287c4d3282aa24e8a17c9dda2da8ef9..573fe303de47cd329df42fd07d7598216295e3fb 100644 --- a/cc/resources/resource_provider.cc +++ b/cc/resources/resource_provider.cc @@ -86,6 +86,8 @@ ResourceProvider::Resource::Resource() format(0), filter(0), image_id(0), + bound_image_id(0), + dirty_image(false), texture_pool(0), hint(TextureUsageAny), type(static_cast<ResourceType>(0)) {} @@ -118,6 +120,8 @@ ResourceProvider::Resource::Resource( format(format), filter(filter), image_id(0), + bound_image_id(0), + dirty_image(false), texture_pool(texture_pool), hint(hint), type(GLTexture) {} @@ -143,6 +147,8 @@ ResourceProvider::Resource::Resource( format(format), filter(filter), image_id(0), + bound_image_id(0), + dirty_image(false), texture_pool(0), hint(TextureUsageAny), type(Bitmap) {} @@ -620,7 +626,6 @@ ResourceProvider::ScopedSamplerGL::ScopedSamplerGL( } ResourceProvider::ScopedSamplerGL::~ScopedSamplerGL() { - resource_provider_->UnbindForSampling(resource_id_, target_, unit_); } ResourceProvider::ScopedWriteLockGL::ScopedWriteLockGL( @@ -1116,32 +1121,14 @@ void ResourceProvider::BindForSampling(ResourceProvider::ResourceId resource_id, resource->filter = filter; } - if (resource->image_id) + if (resource->image_id && resource->dirty_image) { + // Release image currently bound to texture. + if (resource->bound_image_id) + context3d->releaseTexImage2DCHROMIUM(target, resource->bound_image_id); context3d->bindTexImage2DCHROMIUM(target, resource->image_id); - - // Active unit being GL_TEXTURE0 is effectively the ground state. - if (unit != GL_TEXTURE0) - GLC(context3d, context3d->activeTexture(GL_TEXTURE0)); -} - -void ResourceProvider::UnbindForSampling( - ResourceProvider::ResourceId resource_id, GLenum target, GLenum unit) { - DCHECK(thread_checker_.CalledOnValidThread()); - ResourceMap::iterator it = resources_.find(resource_id); - DCHECK(it != resources_.end()); - Resource* resource = &it->second; - - if (!resource->image_id) - return; - - WebGraphicsContext3D* context3d = output_surface_->context3d(); - DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(context3d)); - if (unit != GL_TEXTURE0) - GLC(context3d, context3d->activeTexture(unit)); - context3d->releaseTexImage2DCHROMIUM(target, resource->image_id); - // Active unit being GL_TEXTURE0 is effectively the ground state. - if (unit != GL_TEXTURE0) - GLC(context3d, context3d->activeTexture(GL_TEXTURE0)); + resource->bound_image_id = resource->image_id; + resource->dirty_image = false; + } } void ResourceProvider::BeginSetPixels(ResourceId id) { @@ -1374,6 +1361,8 @@ void ResourceProvider::ReleaseImage(ResourceId id) { DCHECK(context3d); context3d->destroyImageCHROMIUM(resource->image_id); resource->image_id = 0; + resource->bound_image_id = 0; + resource->dirty_image = false; resource->allocated = false; } @@ -1413,6 +1402,7 @@ void ResourceProvider::UnmapImage(ResourceId id) { WebGraphicsContext3D* context3d = output_surface_->context3d(); DCHECK(context3d); context3d->unmapImageCHROMIUM(resource->image_id); + resource->dirty_image = true; } } diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h index b5dccc8f046a71c6a2a33906d745d37727b05ca6..f1b5dadea112b6f910f983ddd616075310e0c431 100644 --- a/cc/resources/resource_provider.h +++ b/cc/resources/resource_provider.h @@ -368,6 +368,8 @@ class CC_EXPORT ResourceProvider { // TODO(skyostil): Use a separate sampler object for filter state. GLenum filter; unsigned image_id; + unsigned bound_image_id; + bool dirty_image; GLenum texture_pool; TextureUsageHint hint; ResourceType type; @@ -417,9 +419,6 @@ class CC_EXPORT ResourceProvider { GLenum target, GLenum unit, GLenum filter); - void UnbindForSampling(ResourceProvider::ResourceId resource_id, - GLenum target, - GLenum unit); OutputSurface* output_surface_; bool lost_output_surface_; diff --git a/cc/resources/resource_provider_unittest.cc b/cc/resources/resource_provider_unittest.cc index a23af0b577c7f5ce3ed255ff2881055eac99f8ff..32c5cf60dd3e54cac6b80ff71092bda0f77157db 100644 --- a/cc/resources/resource_provider_unittest.cc +++ b/cc/resources/resource_provider_unittest.cc @@ -1638,9 +1638,30 @@ TEST_P(ResourceProviderTest, Image_GLTexture) { EXPECT_CALL(*context, bindTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId)) .Times(1) .RetiresOnSaturation(); + { + ResourceProvider::ScopedSamplerGL lock_gl( + resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR); + EXPECT_EQ(kTextureId, lock_gl.texture_id()); + } + + EXPECT_CALL(*context, mapImageCHROMIUM(kImageId, GL_READ_WRITE)) + .WillOnce(Return(dummy_mapped_buffer_address)) + .RetiresOnSaturation(); + resource_provider->MapImage(id); + + EXPECT_CALL(*context, unmapImageCHROMIUM(kImageId)) + .Times(1) + .RetiresOnSaturation(); + resource_provider->UnmapImage(id); + + EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId)).Times(1) + .RetiresOnSaturation(); EXPECT_CALL(*context, releaseTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId)) .Times(1) .RetiresOnSaturation(); + EXPECT_CALL(*context, bindTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId)) + .Times(1) + .RetiresOnSaturation(); EXPECT_CALL(*context, deleteTexture(kTextureId)) .Times(1) .RetiresOnSaturation(); diff --git a/gpu/command_buffer/service/framebuffer_manager.cc b/gpu/command_buffer/service/framebuffer_manager.cc index b468262ecef9c2a2604773cfec36f089840bf1e3..c118af2638b8085a65428e4cbd3ce530b618c7ba 100644 --- a/gpu/command_buffer/service/framebuffer_manager.cc +++ b/gpu/command_buffer/service/framebuffer_manager.cc @@ -84,7 +84,7 @@ class RenderbufferAttachment return true; } - virtual void DetachFromFramebuffer() const OVERRIDE { + virtual void DetachFromFramebuffer(Framebuffer* framebuffer) const OVERRIDE { // Nothing to do for renderbuffers. } @@ -188,8 +188,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( @@ -225,6 +227,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), @@ -247,7 +253,7 @@ void Framebuffer::MarkAsDeleted() { deleted_ = true; while (!attachments_.empty()) { Attachment* attachment = attachments_.begin()->second.get(); - attachment->DetachFromFramebuffer(); + attachment->DetachFromFramebuffer(this); attachments_.erase(attachments_.begin()); } } @@ -532,7 +538,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)); @@ -547,7 +553,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)); @@ -568,6 +574,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. @@ -605,6 +615,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 176e3e2db99bbef6d0a80536754d8ce6d1aae829..850e70281b1b3cdf721e7611937a72dfae12b436 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>; @@ -184,6 +187,17 @@ class GPU_EXPORT Framebuffer : public base::RefCounted<Framebuffer> { // 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(); @@ -217,12 +231,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; @@ -241,6 +265,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 20c75d40ffd6012c2ccbb9188bb12b6d8936892c..85ecf2c16634847dbc81fe5034b52656d1ef3733 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -524,7 +524,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 { @@ -647,6 +648,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 ScopedGLErrorSuppressor; @@ -1406,9 +1411,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( @@ -2537,6 +2547,8 @@ bool GLES2DecoderImpl::Initialize( AsyncPixelTransferManager::Create(context.get())); async_pixel_transfer_manager_->Initialize(texture_manager()); + framebuffer_manager()->AddObserver(this); + return true; } @@ -3257,6 +3269,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; @@ -4924,6 +4938,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); @@ -4944,6 +4961,10 @@ void GLES2DecoderImpl::DoFramebufferTexture2DCommon( if (framebuffer == state_.bound_draw_framebuffer.get()) { clear_state_dirty_ = true; } + + if (texture_ref) + DoDidUseTexImageIfNeeded(texture_ref->texture(), textarget); + OnFboChanged(); } @@ -5663,11 +5684,49 @@ void GLES2DecoderImpl::UpdateStreamTextureIfNeeded(Texture* texture, } } +void GLES2DecoderImpl::DoWillUseTexImageIfNeeded( + Texture* texture, GLenum textarget) { + // This might be supported in the future. + if (textarget != GL_TEXTURE_2D) + return; + // 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", + this); + glBindTexture(textarget, texture->service_id()); + image->WillUseTexImage(); + RestoreCurrentTexture2DBindings(); + } + } +} + +void GLES2DecoderImpl::DoDidUseTexImageIfNeeded( + Texture* texture, GLenum textarget) { + // This might be supported in the future. + if (textarget != GL_TEXTURE_2D) + return; + // 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", + this); + 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; } @@ -5682,16 +5741,14 @@ 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))) { + GLenum textarget = GetBindTargetForSamplerType(uniform_info->type); + if (!texture_ref || !texture_manager()->CanRender(texture_ref)) { textures_set = true; glActiveTexture(GL_TEXTURE0 + texture_unit_index); glBindTexture( - GetBindTargetForSamplerType(uniform_info->type), + textarget, texture_manager()->black_texture_id(uniform_info->type)); LOCAL_RENDER_WARNING( std::string("texture bound to texture unit ") + @@ -5699,7 +5756,23 @@ 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(); + if (textarget == GL_TEXTURE_2D) { + gfx::GLImage* image = texture->GetLevelImage(textarget, 0); + if (image && !texture->IsAttachedToFramebuffer()) { + ScopedGLErrorSuppressor suppressor( + "GLES2DecoderImpl::PrepareTexturesForRender", this); + textures_set = true; + glActiveTexture(GL_TEXTURE0 + texture_unit_index); + image->WillUseTexImage(); + continue; + } + } + + UpdateStreamTextureIfNeeded(texture, texture_unit_index); } // else: should this be an error? } @@ -5707,7 +5780,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(); @@ -5720,9 +5793,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. @@ -5731,6 +5802,20 @@ 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(); + if (texture_unit.bind_target == GL_TEXTURE_2D) { + gfx::GLImage* image = texture->GetLevelImage( + texture_unit.bind_target, 0); + if (image && !texture->IsAttachedToFramebuffer()) { + ScopedGLErrorSuppressor suppressor( + "GLES2DecoderImpl::RestoreStateForTextures", this); + glActiveTexture(GL_TEXTURE0 + texture_unit_index); + image->DidUseTexImage(); + continue; + } } } } @@ -6073,7 +6158,7 @@ error::Error GLES2DecoderImpl::DoDrawArrays( } ProcessPendingQueries(); if (textures_set) { - RestoreStateForNonRenderableTextures(); + RestoreStateForTextures(); } if (simulated_fixed_attribs) { RestoreStateForSimulatedFixedAttribs(); @@ -6207,7 +6292,7 @@ error::Error GLES2DecoderImpl::DoDrawElements( ProcessPendingQueries(); if (textures_set) { - RestoreStateForNonRenderableTextures(); + RestoreStateForTextures(); } if (simulated_fixed_attribs) { RestoreStateForSimulatedFixedAttribs(); @@ -9789,6 +9874,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) { @@ -9821,6 +9908,8 @@ void GLES2DecoderImpl::DoCopyTextureCHROMIUM( unpack_premultiply_alpha_, unpack_unpremultiply_alpha_); } + + DoDidUseTexImageIfNeeded(source_texture, source_texture->target()); } static GLenum ExtractTypeFromStorageFormat(GLenum internalformat) { @@ -10484,6 +10573,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 14acb4061cff7f9ac6a783818a80530dd7d9f2f6..52caaa0aa5785f19f18052d3a96dec48cd701457 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc @@ -8051,6 +8051,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 486bdacad32f013153380a74dc3ad9d343a2e681..9f4c1d94522a3c2409b57296223f87e915adf0b2 100644 --- a/gpu/command_buffer/service/texture_manager.cc +++ b/gpu/command_buffer/service/texture_manager.cc @@ -79,6 +79,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) { @@ -117,6 +118,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) { } @@ -433,6 +435,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(); @@ -481,6 +506,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. @@ -782,10 +808,10 @@ void Texture::SetLevelImage( DCHECK_EQ(info.level, level); info.image = image; UpdateCanRenderCondition(); + UpdateHasImages(); } -gfx::GLImage* Texture::GetLevelImage( - GLint target, GLint level) const { +gfx::GLImage* Texture::GetLevelImage(GLint target, GLint level) const { size_t face_index = GLTargetToFaceIndex(target); if (level >= 0 && face_index < level_infos_.size() && static_cast<size_t>(level) < level_infos_[face_index].size()) { @@ -846,6 +872,7 @@ TextureManager::TextureManager( 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) { @@ -1131,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) { @@ -1145,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_; @@ -1230,10 +1263,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(); - } } // namespace gles2 diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h index cbc659caf4e08a5fe11102925d92b002cfe2f5b1..dea11a66c091971502850c8ce5869a6ef68d58a0 100644 --- a/gpu/command_buffer/service/texture_manager.h +++ b/gpu/command_buffer/service/texture_manager.h @@ -112,6 +112,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( @@ -315,6 +319,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(); @@ -376,6 +384,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_; @@ -629,6 +640,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: @@ -687,6 +702,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); @@ -710,6 +726,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 402cc8d0eff7b1394e8794b36d9b483eef7393aa..ec5c91961a5e242391a1c16ebf8c00e8678c8b3f 100644 --- a/gpu/command_buffer/service/texture_manager_unittest.cc +++ b/gpu/command_buffer/service/texture_manager_unittest.cc @@ -2349,5 +2349,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 diff --git a/ui/gl/gl_image.cc b/ui/gl/gl_image.cc index aaefb9453e994e3f80d5706418b836c19e56f2b9..ff7eb5681701a780bb83b1af76954a10377cd788 100644 --- a/ui/gl/gl_image.cc +++ b/ui/gl/gl_image.cc @@ -19,6 +19,14 @@ void GLImage::ReleaseTexImage() { NOTIMPLEMENTED(); } +void GLImage::WillUseTexImage() { + NOTIMPLEMENTED(); +} + +void GLImage::DidUseTexImage() { + NOTIMPLEMENTED(); +} + GLImage::~GLImage() {} } // namespace gfx diff --git a/ui/gl/gl_image.h b/ui/gl/gl_image.h index f1d05371f6beeae8de923774023a8e8978b05dac..36ba9043af75088a7bb231c4303c17f25309080a 100644 --- a/ui/gl/gl_image.h +++ b/ui/gl/gl_image.h @@ -33,6 +33,12 @@ class GL_EXPORT GLImage : public base::RefCounted<GLImage> { // Release image from texture currently bound to GL_TEXTURE_2D target. virtual void ReleaseTexImage(); + // Called before the texture is used for drawing. + virtual void WillUseTexImage(); + + // Called after the texture has been used for drawing. + virtual void DidUseTexImage(); + // Create a GL image for a window. static scoped_refptr<GLImage> CreateGLImage(gfx::PluginWindowHandle window); diff --git a/ui/gl/gl_image_egl.cc b/ui/gl/gl_image_egl.cc index 4b8a3563081d8bd253525baa400d8ac809af865e..cb8a44cfb2c1ba2a06aeae9355832ad82a177bb6 100644 --- a/ui/gl/gl_image_egl.cc +++ b/ui/gl/gl_image_egl.cc @@ -33,7 +33,8 @@ namespace gfx { GLImageEGL::GLImageEGL(gfx::Size size) : egl_image_(EGL_NO_IMAGE_KHR), - size_(size) { + size_(size), + in_use_(false) { } GLImageEGL::~GLImageEGL() { @@ -62,25 +63,6 @@ bool GLImageEGL::Initialize(gfx::GpuMemoryBufferHandle buffer) { return true; } -bool GLImageEGL::BindTexImage() { - if (egl_image_ == EGL_NO_IMAGE_KHR) { - LOG(ERROR) << "NULL EGLImage in BindTexImage"; - return false; - } - - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_); - - if (glGetError() != GL_NO_ERROR) { - return false; - } - - return true; -} - -gfx::Size GLImageEGL::GetSize() { - return size_; -} - void GLImageEGL::Destroy() { if (egl_image_ == EGL_NO_IMAGE_KHR) return; @@ -96,7 +78,40 @@ void GLImageEGL::Destroy() { egl_image_ = EGL_NO_IMAGE_KHR; } +gfx::Size GLImageEGL::GetSize() { + return size_; +} + +bool GLImageEGL::BindTexImage() { + if (egl_image_ == EGL_NO_IMAGE_KHR) { + LOG(ERROR) << "NULL EGLImage in BindTexImage"; + return false; + } + + // Defer ImageTargetTexture2D if not currently in use. + if (!in_use_) + return true; + + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_); + DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); + return true; +} + void GLImageEGL::ReleaseTexImage() { + // Nothing to do here as image is released after each use. +} + +void GLImageEGL::WillUseTexImage() { + DCHECK(egl_image_); + DCHECK(!in_use_); + in_use_ = true; + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_); + DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); +} + +void GLImageEGL::DidUseTexImage() { + DCHECK(in_use_); + in_use_ = false; // === START ANDROID WORKAROUND b/11392857 static bool requires_gralloc_unbind = RequiresGrallocUnbind(); if (!requires_gralloc_unbind) return; diff --git a/ui/gl/gl_image_egl.h b/ui/gl/gl_image_egl.h index 1c66bb95989f28b976da3e6c0d8be92598e57e01..2c64f4d6becec430849a5fb9f89d76580c680062 100644 --- a/ui/gl/gl_image_egl.h +++ b/ui/gl/gl_image_egl.h @@ -21,6 +21,8 @@ class GL_EXPORT GLImageEGL : public GLImage { virtual gfx::Size GetSize() OVERRIDE; virtual bool BindTexImage() OVERRIDE; virtual void ReleaseTexImage() OVERRIDE; + virtual void WillUseTexImage() OVERRIDE; + virtual void DidUseTexImage() OVERRIDE; protected: virtual ~GLImageEGL(); @@ -28,6 +30,7 @@ class GL_EXPORT GLImageEGL : public GLImage { private: EGLImageKHR egl_image_; gfx::Size size_; + bool in_use_; DISALLOW_COPY_AND_ASSIGN(GLImageEGL); }; diff --git a/ui/gl/gl_image_glx.cc b/ui/gl/gl_image_glx.cc index 85254f346b58e70f7cc2d9de7ea2bbf1e022b66d..739b4d47d1d605d9a78946708318306bb0a964c0 100644 --- a/ui/gl/gl_image_glx.cc +++ b/ui/gl/gl_image_glx.cc @@ -52,6 +52,10 @@ GLImageGLX::GLImageGLX(gfx::PluginWindowHandle window) glx_pixmap_(0) { } +GLImageGLX::~GLImageGLX() { + Destroy(); +} + bool GLImageGLX::Initialize() { if (!GLSurfaceGLX::IsTextureFromPixmapSupported()) { LOG(ERROR) << "GLX_EXT_texture_from_pixmap not supported."; @@ -170,8 +174,10 @@ void GLImageGLX::ReleaseTexImage() { glXReleaseTexImageEXT(display_, glx_pixmap_, GLX_FRONT_LEFT_EXT); } -GLImageGLX::~GLImageGLX() { - Destroy(); +void GLImageGLX::WillUseTexImage() { +} + +void GLImageGLX::DidUseTexImage() { } } // namespace gfx diff --git a/ui/gl/gl_image_glx.h b/ui/gl/gl_image_glx.h index a3cec2d48b091df95c73f762393d5c2aba718b49..86329349b4e61463b11ef656b3eae3880f1304b6 100644 --- a/ui/gl/gl_image_glx.h +++ b/ui/gl/gl_image_glx.h @@ -23,6 +23,8 @@ class GL_EXPORT GLImageGLX : public GLImage { virtual gfx::Size GetSize() OVERRIDE; virtual bool BindTexImage() OVERRIDE; virtual void ReleaseTexImage() OVERRIDE; + virtual void WillUseTexImage() OVERRIDE; + virtual void DidUseTexImage() OVERRIDE; protected: virtual ~GLImageGLX(); diff --git a/ui/gl/gl_image_shm.cc b/ui/gl/gl_image_shm.cc index 14ed8364721e7d70146037e38b8bc91849fd1f2f..ccee11b0622375d664f632fa0bb678c5fcc25d0d 100644 --- a/ui/gl/gl_image_shm.cc +++ b/ui/gl/gl_image_shm.cc @@ -36,6 +36,13 @@ bool GLImageShm::Initialize(gfx::GpuMemoryBufferHandle buffer) { return true; } +void GLImageShm::Destroy() { +} + +gfx::Size GLImageShm::GetSize() { + return size_; +} + bool GLImageShm::BindTexImage() { TRACE_EVENT0("gpu", "GLImageShm::BindTexImage"); DCHECK(shared_memory_); @@ -63,14 +70,13 @@ bool GLImageShm::BindTexImage() { return true; } -gfx::Size GLImageShm::GetSize() { - return size_; +void GLImageShm::ReleaseTexImage() { } -void GLImageShm::Destroy() { +void GLImageShm::WillUseTexImage() { } -void GLImageShm::ReleaseTexImage() { +void GLImageShm::DidUseTexImage() { } } // namespace gfx diff --git a/ui/gl/gl_image_shm.h b/ui/gl/gl_image_shm.h index 70233f9a3be9960f2ff3a6990a3a146316ba67dd..c94ca85a5aeb19fc61d7fb9985501136e0b25289 100644 --- a/ui/gl/gl_image_shm.h +++ b/ui/gl/gl_image_shm.h @@ -21,6 +21,8 @@ class GL_EXPORT GLImageShm : public GLImage { virtual gfx::Size GetSize() OVERRIDE; virtual bool BindTexImage() OVERRIDE; virtual void ReleaseTexImage() OVERRIDE; + virtual void WillUseTexImage() OVERRIDE; + virtual void DidUseTexImage() OVERRIDE; protected: virtual ~GLImageShm(); diff --git a/ui/gl/gl_image_stub.cc b/ui/gl/gl_image_stub.cc index 86efe6e51ec72c2af62740968767c1f3b3cb338f..a1a8a85c277eb903336b4d32b6d032300105ce21 100644 --- a/ui/gl/gl_image_stub.cc +++ b/ui/gl/gl_image_stub.cc @@ -6,6 +6,13 @@ namespace gfx { +GLImageStub::GLImageStub() { +} + +GLImageStub::~GLImageStub() { + Destroy(); +} + void GLImageStub::Destroy() { } @@ -20,6 +27,10 @@ bool GLImageStub::BindTexImage() { void GLImageStub::ReleaseTexImage() { } -GLImageStub::~GLImageStub() {} +void GLImageStub::WillUseTexImage() { +} + +void GLImageStub::DidUseTexImage() { +} } // namespace gfx diff --git a/ui/gl/gl_image_stub.h b/ui/gl/gl_image_stub.h index 8b473585b62bee53703e180d1b8dd2a38705abe1..ec232fab3e765e5905997fb63a0239b085808703 100644 --- a/ui/gl/gl_image_stub.h +++ b/ui/gl/gl_image_stub.h @@ -12,11 +12,15 @@ namespace gfx { // A GLImage that does nothing for unit tests. class GL_EXPORT GLImageStub : public GLImage { public: + GLImageStub(); + // Implement GLImage. virtual void Destroy() OVERRIDE; virtual gfx::Size GetSize() OVERRIDE; virtual bool BindTexImage() OVERRIDE; virtual void ReleaseTexImage() OVERRIDE; + virtual void WillUseTexImage() OVERRIDE; + virtual void DidUseTexImage() OVERRIDE; protected: virtual ~GLImageStub();