Commit ced21f86 authored by reveman's avatar reveman Committed by Commit bot

exo: Handle lost context situations properly.

This implements proper handling of lost context by exosphere. It
also improves support for software compositing by not crashing.
True software compositing support will be added by follow up
patches.

BUG=549781
TEST=exo_unittests --gtest_filter=BufferTest.IsLost

Review URL: https://codereview.chromium.org/1467943002

Cr-Commit-Position: refs/heads/master@{#361228}
parent f94079e6
...@@ -49,32 +49,58 @@ GLenum GLInternalFormat(gfx::BufferFormat format) { ...@@ -49,32 +49,58 @@ GLenum GLInternalFormat(gfx::BufferFormat format) {
return kGLInternalFormats[static_cast<int>(format)]; return kGLInternalFormats[static_cast<int>(format)];
} }
gpu::gles2::GLES2Interface* GetContextGL() {
ui::ContextFactory* context_factory =
aura::Env::GetInstance()->context_factory();
return context_factory->SharedMainThreadContextProvider()->ContextGL();
}
} // namespace } // namespace
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Buffer, public: // Buffer::Texture
Buffer::Buffer(scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer, // Encapsulates the state and logic needed to bind a buffer to a GLES2 texture.
class Buffer::Texture {
public:
Texture(gfx::GpuMemoryBuffer* gpu_memory_buffer,
cc::ContextProvider* context_provider,
unsigned texture_target);
~Texture();
// Returns true if GLES2 resources for texture have been lost.
bool IsLost();
// Binds the content of gpu memory buffer to the texture returned by
// mailbox(). Returns a sync token that can be used to when accessing texture
// from a different context.
gpu::SyncToken BindTexImage();
// Releases the content of gpu memory buffer after |sync_token| has passed.
void ReleaseTexImage(const gpu::SyncToken& sync_token);
// Returns the mailbox for this texture.
gpu::Mailbox mailbox() const { return mailbox_; }
private:
scoped_refptr<cc::ContextProvider> context_provider_;
const unsigned texture_target_;
const gfx::Size size_;
unsigned image_id_;
unsigned texture_id_;
gpu::Mailbox mailbox_;
DISALLOW_COPY_AND_ASSIGN(Texture);
};
Buffer::Texture::Texture(gfx::GpuMemoryBuffer* gpu_memory_buffer,
cc::ContextProvider* context_provider,
unsigned texture_target) unsigned texture_target)
: gpu_memory_buffer_(gpu_memory_buffer.Pass()), : context_provider_(context_provider),
texture_target_(texture_target), texture_target_(texture_target),
texture_id_(0), size_(gpu_memory_buffer->GetSize()),
image_id_(0) { image_id_(0),
gpu::gles2::GLES2Interface* gles2 = GetContextGL(); texture_id_(0) {
// Create an image for |gpu_memory_buffer_|. gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
gfx::Size size = gpu_memory_buffer_->GetSize();
image_id_ = gles2->CreateImageCHROMIUM( image_id_ = gles2->CreateImageCHROMIUM(
gpu_memory_buffer_->AsClientBuffer(), size.width(), size.height(), gpu_memory_buffer->AsClientBuffer(), size_.width(), size_.height(),
GLInternalFormat(gpu_memory_buffer_->GetFormat())); GLInternalFormat(gpu_memory_buffer->GetFormat()));
// Create a texture with |texture_target_|.
gles2->ActiveTexture(GL_TEXTURE0);
gles2->GenTextures(1, &texture_id_); gles2->GenTextures(1, &texture_id_);
gles2->ActiveTexture(GL_TEXTURE0);
gles2->BindTexture(texture_target_, texture_id_); gles2->BindTexture(texture_target_, texture_id_);
gles2->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gles2->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gles2->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gles2->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
...@@ -85,54 +111,97 @@ Buffer::Buffer(scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer, ...@@ -85,54 +111,97 @@ Buffer::Buffer(scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer,
gles2->ProduceTextureCHROMIUM(texture_target_, mailbox_.name); gles2->ProduceTextureCHROMIUM(texture_target_, mailbox_.name);
} }
Buffer::~Buffer() { Buffer::Texture::~Texture() {
gpu::gles2::GLES2Interface* gles2 = GetContextGL(); gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
if (texture_id_) gles2->ActiveTexture(GL_TEXTURE0);
gles2->DeleteTextures(1, &texture_id_); gles2->DeleteTextures(1, &texture_id_);
if (image_id_)
gles2->DestroyImageCHROMIUM(image_id_); gles2->DestroyImageCHROMIUM(image_id_);
} }
scoped_ptr<cc::SingleReleaseCallback> Buffer::AcquireTextureMailbox( bool Buffer::Texture::IsLost() {
cc::TextureMailbox* texture_mailbox) { gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
// Buffer can only be used by one client at a time. If texture id is 0, then a return gles2->GetGraphicsResetStatusKHR() != GL_NO_ERROR;
// previous call to AcquireTextureMailbox() is using this buffer and it has }
// not been released yet.
if (!texture_id_) {
DLOG(WARNING) << "Client tried to use a buffer that has not been released";
return nullptr;
}
// Take ownerhsip of image and texture ids.
unsigned texture_id = 0;
unsigned image_id = 0;
std::swap(texture_id, texture_id_);
DCHECK_NE(image_id_, 0u);
std::swap(image_id, image_id_);
// Bind texture to |texture_target_|. gpu::SyncToken Buffer::Texture::BindTexImage() {
gpu::gles2::GLES2Interface* gles2 = GetContextGL(); gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
gles2->ActiveTexture(GL_TEXTURE0); gles2->ActiveTexture(GL_TEXTURE0);
gles2->BindTexture(texture_target_, texture_id); gles2->BindTexture(texture_target_, texture_id_);
gles2->BindTexImage2DCHROMIUM(texture_target_, image_id_);
// Bind the image to texture. // Create and return a sync token that can be used to ensure that the
gles2->BindTexImage2DCHROMIUM(texture_target_, image_id); // BindTexImage2DCHROMIUM call is processed before issuing any commands
// that will read from the texture on a different context.
// Create a sync token to ensure that the BindTexImage2DCHROMIUM call is
// processed before issuing any commands that will read from texture.
uint64 fence_sync = gles2->InsertFenceSyncCHROMIUM(); uint64 fence_sync = gles2->InsertFenceSyncCHROMIUM();
gles2->ShallowFlushCHROMIUM(); gles2->OrderingBarrierCHROMIUM();
gpu::SyncToken sync_token; gpu::SyncToken sync_token;
gles2->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData()); gles2->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
return sync_token;
}
void Buffer::Texture::ReleaseTexImage(const gpu::SyncToken& sync_token) {
gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
if (sync_token.HasData())
gles2->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
gles2->ActiveTexture(GL_TEXTURE0);
gles2->BindTexture(texture_target_, texture_id_);
gles2->ReleaseTexImage2DCHROMIUM(texture_target_, image_id_);
}
////////////////////////////////////////////////////////////////////////////////
// Buffer, public:
Buffer::Buffer(scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer,
unsigned texture_target)
: gpu_memory_buffer_(gpu_memory_buffer.Pass()),
texture_target_(texture_target),
use_count_(0) {}
Buffer::~Buffer() {}
scoped_ptr<cc::SingleReleaseCallback> Buffer::ProduceTextureMailbox(
cc::TextureMailbox* texture_mailbox) {
DLOG_IF(WARNING, use_count_)
<< "Producing a texture mailbox for a buffer that has not been released";
// Increment the use count for this buffer.
++use_count_;
// Creating a new texture is relatively expensive so we reuse the last
// texture whenever possible.
scoped_ptr<Texture> texture = last_texture_.Pass();
// If texture is lost, destroy it to ensure that we create a new one below.
if (texture && texture->IsLost())
texture.reset();
// Create a new texture if one doesn't already exist. The contents of this
// buffer can be bound to the texture using a call to BindTexImage and must
// be released using a matching ReleaseTexImage call before it can be reused
// or destroyed.
if (!texture) {
// Note: This can fail if GPU acceleration has been disabled.
scoped_refptr<cc::ContextProvider> context_provider =
aura::Env::GetInstance()
->context_factory()
->SharedMainThreadContextProvider();
if (!context_provider) {
DLOG(WARNING) << "Failed to acquire a context provider";
Release(); // Decrements the use count
return nullptr;
}
texture = make_scoped_ptr(new Texture(
gpu_memory_buffer_.get(), context_provider.get(), texture_target_));
}
// This binds the latest contents of this buffer to the texture.
gpu::SyncToken sync_token = texture->BindTexImage();
bool is_overlay_candidate = false; bool is_overlay_candidate = false;
*texture_mailbox = *texture_mailbox =
cc::TextureMailbox(mailbox_, sync_token, texture_target_, cc::TextureMailbox(texture->mailbox(), sync_token, texture_target_,
gpu_memory_buffer_->GetSize(), is_overlay_candidate); gpu_memory_buffer_->GetSize(), is_overlay_candidate);
return cc::SingleReleaseCallback::Create( return cc::SingleReleaseCallback::Create(
base::Bind(&Buffer::Release, AsWeakPtr(), texture_target_, base::Bind(&Buffer::ReleaseTexture, AsWeakPtr(), base::Passed(&texture)));
texture_id, image_id))
.Pass();
} }
gfx::Size Buffer::GetSize() const { gfx::Size Buffer::GetSize() const {
...@@ -142,8 +211,9 @@ gfx::Size Buffer::GetSize() const { ...@@ -142,8 +211,9 @@ gfx::Size Buffer::GetSize() const {
scoped_refptr<base::trace_event::TracedValue> Buffer::AsTracedValue() const { scoped_refptr<base::trace_event::TracedValue> Buffer::AsTracedValue() const {
scoped_refptr<base::trace_event::TracedValue> value = scoped_refptr<base::trace_event::TracedValue> value =
new base::trace_event::TracedValue; new base::trace_event::TracedValue;
value->SetInteger("width", GetSize().width()); gfx::Size size = gpu_memory_buffer_->GetSize();
value->SetInteger("height", GetSize().height()); value->SetInteger("width", size.width());
value->SetInteger("height", size.height());
value->SetInteger("format", value->SetInteger("format",
static_cast<int>(gpu_memory_buffer_->GetFormat())); static_cast<int>(gpu_memory_buffer_->GetFormat()));
return value; return value;
...@@ -152,36 +222,36 @@ scoped_refptr<base::trace_event::TracedValue> Buffer::AsTracedValue() const { ...@@ -152,36 +222,36 @@ scoped_refptr<base::trace_event::TracedValue> Buffer::AsTracedValue() const {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Buffer, private: // Buffer, private:
void Buffer::Release() {
DCHECK_GT(use_count_, 0u);
if (--use_count_)
return;
// Run release callback to notify the client that buffer has been released.
if (!release_callback_.is_null())
release_callback_.Run();
}
// static // static
void Buffer::Release(base::WeakPtr<Buffer> buffer, void Buffer::ReleaseTexture(base::WeakPtr<Buffer> buffer,
unsigned texture_target, scoped_ptr<Texture> texture,
unsigned texture_id,
unsigned image_id,
const gpu::SyncToken& sync_token, const gpu::SyncToken& sync_token,
bool is_lost) { bool is_lost) {
TRACE_EVENT1("exo", "Buffer::Release", "is_lost", is_lost); TRACE_EVENT1("exo", "Buffer::ReleaseTexture", "is_lost", is_lost);
gpu::gles2::GLES2Interface* gles2 = GetContextGL(); // Release image so it can safely be reused or destroyed.
if (sync_token.HasData()) texture->ReleaseTexImage(sync_token);
gles2->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
gles2->ActiveTexture(GL_TEXTURE0);
gles2->BindTexture(texture_target, texture_id);
gles2->ReleaseTexImage2DCHROMIUM(texture_target, image_id);
// Delete resources and return if buffer is gone. // Early out if buffer is gone. This can happen when the client destroyed the
if (!buffer) { // buffer before receiving a release callback.
gles2->DeleteTextures(1, &texture_id); if (!buffer)
gles2->DestroyImageCHROMIUM(image_id);
return; return;
}
DCHECK_EQ(buffer->texture_id_, 0u); // Allow buffer to reused texture if it's not lost.
buffer->texture_id_ = texture_id; if (!is_lost)
DCHECK_EQ(buffer->image_id_, 0u); buffer->last_texture_ = texture.Pass();
buffer->image_id_ = image_id;
if (!buffer->release_callback_.is_null()) buffer->Release();
buffer->release_callback_.Run();
} }
} // namespace exo } // namespace exo
...@@ -9,9 +9,6 @@ ...@@ -9,9 +9,6 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size.h"
namespace base { namespace base {
...@@ -29,6 +26,10 @@ namespace gfx { ...@@ -29,6 +26,10 @@ namespace gfx {
class GpuMemoryBuffer; class GpuMemoryBuffer;
} }
namespace gpu {
struct SyncToken;
}
namespace exo { namespace exo {
// This class provides the content for a Surface. The mechanism by which a // This class provides the content for a Surface. The mechanism by which a
...@@ -50,7 +51,7 @@ class Buffer : public base::SupportsWeakPtr<Buffer> { ...@@ -50,7 +51,7 @@ class Buffer : public base::SupportsWeakPtr<Buffer> {
// This function can be used to acquire a texture mailbox that is bound to // This function can be used to acquire a texture mailbox that is bound to
// the buffer. Returns a release callback on success. The release callback // the buffer. Returns a release callback on success. The release callback
// must be called before a new texture mailbox can be acquired. // must be called before a new texture mailbox can be acquired.
scoped_ptr<cc::SingleReleaseCallback> AcquireTextureMailbox( scoped_ptr<cc::SingleReleaseCallback> ProduceTextureMailbox(
cc::TextureMailbox* mailbox); cc::TextureMailbox* mailbox);
// Returns the size of the buffer. // Returns the size of the buffer.
...@@ -60,18 +61,38 @@ class Buffer : public base::SupportsWeakPtr<Buffer> { ...@@ -60,18 +61,38 @@ class Buffer : public base::SupportsWeakPtr<Buffer> {
scoped_refptr<base::trace_event::TracedValue> AsTracedValue() const; scoped_refptr<base::trace_event::TracedValue> AsTracedValue() const;
private: private:
static void Release(base::WeakPtr<Buffer> buffer, class Texture;
unsigned texture_target,
unsigned texture_id, // Decrements the use count of buffer and notifies the client that buffer
unsigned image_id, // as been released if it reached 0.
void Release();
// This is used by ProduceTextureMailbox() to produce a release callback
// that releases the buffer contents referenced by a texture before the
// texture is destroyed or reused.
// Note: This is a static function as it needs to run even if the buffer
// has been destroyed.
static void ReleaseTexture(base::WeakPtr<Buffer> buffer,
scoped_ptr<Texture> texture,
const gpu::SyncToken& sync_token, const gpu::SyncToken& sync_token,
bool is_lost); bool is_lost);
// The GPU memory buffer that contains the contents of this buffer.
scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_; scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_;
// Texture target that must be used when creating a texture for buffer.
const unsigned texture_target_; const unsigned texture_target_;
unsigned texture_id_;
unsigned image_id_; // This is incremented when a texture mailbox is produced and decremented
gpu::Mailbox mailbox_; // when a texture mailbox is released. It is used to determine when we should
// notify the client that buffer has been released.
unsigned use_count_;
// The last released texture instance. ProduceTextureMailbox() will use this
// instead of creating a new texture when possible.
scoped_ptr<Texture> last_texture_;
// The client release callback.
base::Closure release_callback_; base::Closure release_callback_;
DISALLOW_COPY_AND_ASSIGN(Buffer); DISALLOW_COPY_AND_ASSIGN(Buffer);
......
...@@ -2,14 +2,20 @@ ...@@ -2,14 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <GLES2/gl2extchromium.h>
#include "base/bind.h" #include "base/bind.h"
#include "cc/output/context_provider.h"
#include "cc/resources/single_release_callback.h" #include "cc/resources/single_release_callback.h"
#include "components/exo/buffer.h" #include "components/exo/buffer.h"
#include "components/exo/surface.h" #include "components/exo/surface.h"
#include "components/exo/test/exo_test_base.h" #include "components/exo/test/exo_test_base.h"
#include "components/exo/test/exo_test_helper.h" #include "components/exo/test/exo_test_helper.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2.h"
#include "ui/aura/env.h"
#include "ui/compositor/compositor.h"
#include "ui/gfx/gpu_memory_buffer.h" #include "ui/gfx/gpu_memory_buffer.h"
namespace exo { namespace exo {
...@@ -32,15 +38,12 @@ TEST_F(BufferTest, ReleaseCallback) { ...@@ -32,15 +38,12 @@ TEST_F(BufferTest, ReleaseCallback) {
buffer->set_release_callback( buffer->set_release_callback(
base::Bind(&Release, base::Unretained(&release_call_count))); base::Bind(&Release, base::Unretained(&release_call_count)));
// Acquire a texture mailbox for the contents of the buffer. // Produce a texture mailbox for the contents of the buffer.
cc::TextureMailbox texture_mailbox; cc::TextureMailbox texture_mailbox;
scoped_ptr<cc::SingleReleaseCallback> buffer_release_callback = scoped_ptr<cc::SingleReleaseCallback> buffer_release_callback =
buffer->AcquireTextureMailbox(&texture_mailbox); buffer->ProduceTextureMailbox(&texture_mailbox);
ASSERT_TRUE(buffer_release_callback); ASSERT_TRUE(buffer_release_callback);
// Trying to acquire an already in-use buffer should fail.
EXPECT_FALSE(buffer->AcquireTextureMailbox(&texture_mailbox));
// Release buffer. // Release buffer.
buffer_release_callback->Run(gpu::SyncToken(), false); buffer_release_callback->Run(gpu::SyncToken(), false);
...@@ -48,5 +51,39 @@ TEST_F(BufferTest, ReleaseCallback) { ...@@ -48,5 +51,39 @@ TEST_F(BufferTest, ReleaseCallback) {
ASSERT_EQ(release_call_count, 1); ASSERT_EQ(release_call_count, 1);
} }
TEST_F(BufferTest, IsLost) {
gfx::Size buffer_size(256, 256);
scoped_ptr<Buffer> buffer(
new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size).Pass(),
GL_TEXTURE_2D));
// Acquire a texture mailbox for the contents of the buffer.
cc::TextureMailbox texture_mailbox;
scoped_ptr<cc::SingleReleaseCallback> buffer_release_callback =
buffer->ProduceTextureMailbox(&texture_mailbox);
ASSERT_TRUE(buffer_release_callback);
scoped_refptr<cc::ContextProvider> context_provider =
aura::Env::GetInstance()
->context_factory()
->SharedMainThreadContextProvider();
if (context_provider) {
gpu::gles2::GLES2Interface* gles2 = context_provider->ContextGL();
gles2->LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
GL_INNOCENT_CONTEXT_RESET_ARB);
}
// Release buffer.
bool is_lost = true;
buffer_release_callback->Run(gpu::SyncToken(), is_lost);
// Producing a new texture mailbox for the contents of the buffer.
buffer_release_callback = buffer->ProduceTextureMailbox(&texture_mailbox);
ASSERT_TRUE(buffer_release_callback);
// Release buffer.
buffer_release_callback->Run(gpu::SyncToken(), false);
}
} // namespace } // namespace
} // namespace exo } // namespace exo
...@@ -41,8 +41,9 @@ bool ListContainsEntry(T& list, U key) { ...@@ -41,8 +41,9 @@ bool ListContainsEntry(T& list, U key) {
// Surface, public: // Surface, public:
Surface::Surface() Surface::Surface()
: needs_commit_surface_hierarchy_(false), : has_pending_contents_(false),
has_contents_(false), needs_commit_surface_hierarchy_(false),
update_contents_after_successful_compositing_(false),
compositor_(nullptr), compositor_(nullptr),
delegate_(nullptr) { delegate_(nullptr) {
SetLayer(new ui::Layer(ui::LAYER_SOLID_COLOR)); SetLayer(new ui::Layer(ui::LAYER_SOLID_COLOR));
...@@ -71,6 +72,7 @@ Surface::~Surface() { ...@@ -71,6 +72,7 @@ Surface::~Surface() {
void Surface::Attach(Buffer* buffer) { void Surface::Attach(Buffer* buffer) {
TRACE_EVENT1("exo", "Surface::Attach", "buffer", buffer->AsTracedValue()); TRACE_EVENT1("exo", "Surface::Attach", "buffer", buffer->AsTracedValue());
has_pending_contents_ = true;
pending_buffer_ = buffer ? buffer->AsWeakPtr() : base::WeakPtr<Buffer>(); pending_buffer_ = buffer ? buffer->AsWeakPtr() : base::WeakPtr<Buffer>();
PreferredSizeChanged(); PreferredSizeChanged();
} }
...@@ -197,17 +199,18 @@ void Surface::CommitSurfaceHierarchy() { ...@@ -197,17 +199,18 @@ void Surface::CommitSurfaceHierarchy() {
DCHECK(needs_commit_surface_hierarchy_); DCHECK(needs_commit_surface_hierarchy_);
needs_commit_surface_hierarchy_ = false; needs_commit_surface_hierarchy_ = false;
// We update contents if Attach() has been called since last commit.
if (has_pending_contents_) {
has_pending_contents_ = false;
current_buffer_ = pending_buffer_;
pending_buffer_.reset();
cc::TextureMailbox texture_mailbox; cc::TextureMailbox texture_mailbox;
scoped_ptr<cc::SingleReleaseCallback> texture_mailbox_release_callback; scoped_ptr<cc::SingleReleaseCallback> texture_mailbox_release_callback;
if (pending_buffer_) { if (current_buffer_) {
texture_mailbox_release_callback = texture_mailbox_release_callback =
pending_buffer_->AcquireTextureMailbox(&texture_mailbox); current_buffer_->ProduceTextureMailbox(&texture_mailbox);
pending_buffer_.reset();
has_contents_ = true;
} else {
// Show solid color content if there is no pending buffer.
layer()->SetShowSolidColorContent();
has_contents_ = false;
} }
if (texture_mailbox_release_callback) { if (texture_mailbox_release_callback) {
...@@ -220,11 +223,17 @@ void Surface::CommitSurfaceHierarchy() { ...@@ -220,11 +223,17 @@ void Surface::CommitSurfaceHierarchy() {
texture_mailbox.size_in_pixels())); texture_mailbox.size_in_pixels()));
layer()->SetFillsBoundsOpaquely(pending_opaque_region_.contains( layer()->SetFillsBoundsOpaquely(pending_opaque_region_.contains(
gfx::RectToSkIRect(gfx::Rect(texture_mailbox.size_in_pixels())))); gfx::RectToSkIRect(gfx::Rect(texture_mailbox.size_in_pixels()))));
} else {
// Show solid color content if no buffer is attached or we failed
// to produce a texture mailbox for the currently attached buffer.
layer()->SetShowSolidColorContent();
layer()->SetColor(SK_ColorBLACK);
} }
// Schedule redraw of the damage region. // Schedule redraw of the damage region.
layer()->SchedulePaint(pending_damage_); layer()->SchedulePaint(pending_damage_);
pending_damage_ = gfx::Rect(); pending_damage_ = gfx::Rect();
}
ui::Compositor* compositor = layer()->GetCompositor(); ui::Compositor* compositor = layer()->GetCompositor();
if (compositor && !pending_frame_callbacks_.empty()) { if (compositor && !pending_frame_callbacks_.empty()) {
...@@ -322,6 +331,37 @@ void Surface::OnCompositingStarted(ui::Compositor* compositor, ...@@ -322,6 +331,37 @@ void Surface::OnCompositingStarted(ui::Compositor* compositor,
} }
} }
void Surface::OnCompositingEnded(ui::Compositor* compositor) {
// Nothing to do in here unless this has been set.
if (!update_contents_after_successful_compositing_)
return;
update_contents_after_successful_compositing_ = false;
// Early out if no contents is currently assigned to the surface.
if (!current_buffer_)
return;
// Update contents by producing a new texture mailbox for the current buffer.
cc::TextureMailbox texture_mailbox;
scoped_ptr<cc::SingleReleaseCallback> texture_mailbox_release_callback =
current_buffer_->ProduceTextureMailbox(&texture_mailbox);
if (texture_mailbox_release_callback) {
layer()->SetTextureMailbox(texture_mailbox,
texture_mailbox_release_callback.Pass(),
texture_mailbox.size_in_pixels());
layer()->SetTextureFlipped(false);
layer()->SchedulePaint(gfx::Rect(texture_mailbox.size_in_pixels()));
}
}
void Surface::OnCompositingAborted(ui::Compositor* compositor) {
// The contents of this surface might be lost if compositing aborted because
// of a lost graphics context. We recover from this by updating the contents
// of the surface next time the compositor successfully ends compositing.
update_contents_after_successful_compositing_ = true;
}
void Surface::OnCompositingShuttingDown(ui::Compositor* compositor) { void Surface::OnCompositingShuttingDown(ui::Compositor* compositor) {
compositor->RemoveObserver(this); compositor->RemoveObserver(this);
compositor_ = nullptr; compositor_ = nullptr;
......
...@@ -98,8 +98,8 @@ class Surface : public views::View, public ui::CompositorObserver { ...@@ -98,8 +98,8 @@ class Surface : public views::View, public ui::CompositorObserver {
void OnCompositingDidCommit(ui::Compositor* compositor) override; void OnCompositingDidCommit(ui::Compositor* compositor) override;
void OnCompositingStarted(ui::Compositor* compositor, void OnCompositingStarted(ui::Compositor* compositor,
base::TimeTicks start_time) override; base::TimeTicks start_time) override;
void OnCompositingEnded(ui::Compositor* compositor) override {} void OnCompositingEnded(ui::Compositor* compositor) override;
void OnCompositingAborted(ui::Compositor* compositor) override {} void OnCompositingAborted(ui::Compositor* compositor) override;
void OnCompositingLockStateChanged(ui::Compositor* compositor) override {} void OnCompositingLockStateChanged(ui::Compositor* compositor) override {}
void OnCompositingShuttingDown(ui::Compositor* compositor) override; void OnCompositingShuttingDown(ui::Compositor* compositor) override;
...@@ -108,7 +108,12 @@ class Surface : public views::View, public ui::CompositorObserver { ...@@ -108,7 +108,12 @@ class Surface : public views::View, public ui::CompositorObserver {
return needs_commit_surface_hierarchy_; return needs_commit_surface_hierarchy_;
} }
bool has_contents() const { return has_contents_; } // This returns true when the surface has some contents assigned to it.
bool has_contents() const { return !!current_buffer_; }
// This is true when Attach() has been called and new contents should take
// effect next time Commit() is called.
bool has_pending_contents_;
// The buffer that will become the content of surface when Commit() is called. // The buffer that will become the content of surface when Commit() is called.
base::WeakPtr<Buffer> pending_buffer_; base::WeakPtr<Buffer> pending_buffer_;
...@@ -138,12 +143,16 @@ class Surface : public views::View, public ui::CompositorObserver { ...@@ -138,12 +143,16 @@ class Surface : public views::View, public ui::CompositorObserver {
using SubSurfaceEntryList = std::list<SubSurfaceEntry>; using SubSurfaceEntryList = std::list<SubSurfaceEntry>;
SubSurfaceEntryList pending_sub_surfaces_; SubSurfaceEntryList pending_sub_surfaces_;
// The buffer that is currently set as content of surface.
base::WeakPtr<Buffer> current_buffer_;
// This is true if a call to Commit() as been made but // This is true if a call to Commit() as been made but
// CommitSurfaceHierarchy() has not yet been called. // CommitSurfaceHierarchy() has not yet been called.
bool needs_commit_surface_hierarchy_; bool needs_commit_surface_hierarchy_;
// This is true when surface has some contents assigned to it. // This is true when the contents of the surface should be updated next time
bool has_contents_; // the compositor successfully ends compositing.
bool update_contents_after_successful_compositing_;
// The compsitor being observer or null if not observing a compositor. // The compsitor being observer or null if not observing a compositor.
ui::Compositor* compositor_; ui::Compositor* compositor_;
......
...@@ -32,23 +32,21 @@ TEST_F(SurfaceTest, Attach) { ...@@ -32,23 +32,21 @@ TEST_F(SurfaceTest, Attach) {
buffer->set_release_callback( buffer->set_release_callback(
base::Bind(&ReleaseBuffer, base::Unretained(&release_buffer_call_count))); base::Bind(&ReleaseBuffer, base::Unretained(&release_buffer_call_count)));
scoped_ptr<Surface> surface1(new Surface); scoped_ptr<Surface> surface(new Surface);
scoped_ptr<Surface> surface2(new Surface);
// Attach the buffer to surface1. // Attach the buffer to surface1.
surface1->Attach(buffer.get()); surface->Attach(buffer.get());
surface1->Commit(); surface->Commit();
// Attaching buffer to surface2 when it is already attached to surface1 // Commit without calling Attach() should have no effect.
// should fail and buffer should remain attached to surface1. surface->Commit();
surface2->Attach(buffer.get()); EXPECT_EQ(0, release_buffer_call_count);
surface2->Commit();
// Attach a null buffer to surface1, this should release the previously // Attach a null buffer to surface, this should release the previously
// attached buffer. // attached buffer.
surface1->Attach(nullptr); surface->Attach(nullptr);
surface1->Commit(); surface->Commit();
ASSERT_EQ(release_buffer_call_count, 1); ASSERT_EQ(1, release_buffer_call_count);
} }
TEST_F(SurfaceTest, Damage) { TEST_F(SurfaceTest, Damage) {
......
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