summaryrefslogtreecommitdiffstats
path: root/gpu/command_buffer/service
diff options
context:
space:
mode:
authorreveman@chromium.org <reveman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-19 10:22:56 +0000
committerreveman@chromium.org <reveman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-19 10:22:56 +0000
commit8a48e732c0dc67b22f0b2cdce8bf750e273a4628 (patch)
treea3f0260baa264de95d1ffb21815ebcf223dfdee2 /gpu/command_buffer/service
parent10ebc9183caa8dd392ec89cc6c571332c4194741 (diff)
downloadchromium_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.cc26
-rw-r--r--gpu/command_buffer/service/framebuffer_manager.h28
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc117
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc140
-rw-r--r--gpu/command_buffer/service/texture_manager.cc40
-rw-r--r--gpu/command_buffer/service/texture_manager.h17
-rw-r--r--gpu/command_buffer/service/texture_manager_unittest.cc60
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