Commit 294545aa authored by reveman@chromium.org's avatar reveman@chromium.org Committed by Bo Liu
Browse files

Cherry-pick: gpu: Add Will/DidUseTexImage to GLImage API.

Cherry-pick of Chromium crrev.com/r230093

BUG: 11392857

Conflicts:
	cc/resources/resource_provider.cc
	gpu/command_buffer/service/gles2_cmd_decoder.cc
	ui/gl/gl_image_egl.cc

Original description:

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.

Change-Id: I3bfa9cdd894fcdd60b7d10ff70afde349cfdd476
parent 4733d06f
......@@ -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;
}
}
......
......@@ -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_;
......
......@@ -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();
......
......@@ -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
......
......@@ -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);
};
......
......@@ -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.
......
......@@ -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
......
......@@ -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
......
......@@ -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.
......
......@@ -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
......@@ -19,6 +19,14 @@ void GLImage::ReleaseTexImage() {
NOTIMPLEMENTED();
}
void GLImage::WillUseTexImage() {
NOTIMPLEMENTED();
}
void GLImage::DidUseTexImage() {
NOTIMPLEMENTED();
}
GLImage::~GLImage() {}
} // namespace gfx
......@@ -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);
......
......@@ -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;
......
......@@ -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);
};
......
......@@ -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
......@@ -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();
......
......@@ -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
......@@ -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();
......
......@@ -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
......@@ -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();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment