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
This diff is collapsed.
......@@ -9,9 +9,6 @@
#include "base/macros.h"
#include "base/memory/scoped_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"
namespace base {
......@@ -29,6 +26,10 @@ namespace gfx {
class GpuMemoryBuffer;
}
namespace gpu {
struct SyncToken;
}
namespace exo {
// This class provides the content for a Surface. The mechanism by which a
......@@ -50,7 +51,7 @@ class Buffer : public base::SupportsWeakPtr<Buffer> {
// 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
// must be called before a new texture mailbox can be acquired.
scoped_ptr<cc::SingleReleaseCallback> AcquireTextureMailbox(
scoped_ptr<cc::SingleReleaseCallback> ProduceTextureMailbox(
cc::TextureMailbox* mailbox);
// Returns the size of the buffer.
......@@ -60,18 +61,38 @@ class Buffer : public base::SupportsWeakPtr<Buffer> {
scoped_refptr<base::trace_event::TracedValue> AsTracedValue() const;
private:
static void Release(base::WeakPtr<Buffer> buffer,
unsigned texture_target,
unsigned texture_id,
unsigned image_id,
class Texture;
// Decrements the use count of buffer and notifies the client that buffer
// 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,
bool is_lost);
// The GPU memory buffer that contains the contents of this buffer.
scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_;
// Texture target that must be used when creating a texture for buffer.
const unsigned texture_target_;
unsigned texture_id_;
unsigned image_id_;
gpu::Mailbox mailbox_;
// This is incremented when a texture mailbox is produced and decremented
// 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_;
DISALLOW_COPY_AND_ASSIGN(Buffer);
......
......@@ -2,14 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <GLES2/gl2extchromium.h>
#include "base/bind.h"
#include "cc/output/context_provider.h"
#include "cc/resources/single_release_callback.h"
#include "components/exo/buffer.h"
#include "components/exo/surface.h"
#include "components/exo/test/exo_test_base.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 "third_party/khronos/GLES2/gl2.h"
#include "ui/aura/env.h"
#include "ui/compositor/compositor.h"
#include "ui/gfx/gpu_memory_buffer.h"
namespace exo {
......@@ -32,15 +38,12 @@ TEST_F(BufferTest, ReleaseCallback) {
buffer->set_release_callback(
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;
scoped_ptr<cc::SingleReleaseCallback> buffer_release_callback =
buffer->AcquireTextureMailbox(&texture_mailbox);
buffer->ProduceTextureMailbox(&texture_mailbox);
ASSERT_TRUE(buffer_release_callback);
// Trying to acquire an already in-use buffer should fail.
EXPECT_FALSE(buffer->AcquireTextureMailbox(&texture_mailbox));
// Release buffer.
buffer_release_callback->Run(gpu::SyncToken(), false);
......@@ -48,5 +51,39 @@ TEST_F(BufferTest, ReleaseCallback) {
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 exo
......@@ -41,8 +41,9 @@ bool ListContainsEntry(T& list, U key) {
// Surface, public:
Surface::Surface()
: needs_commit_surface_hierarchy_(false),
has_contents_(false),
: has_pending_contents_(false),
needs_commit_surface_hierarchy_(false),
update_contents_after_successful_compositing_(false),
compositor_(nullptr),
delegate_(nullptr) {
SetLayer(new ui::Layer(ui::LAYER_SOLID_COLOR));
......@@ -71,6 +72,7 @@ Surface::~Surface() {
void Surface::Attach(Buffer* buffer) {
TRACE_EVENT1("exo", "Surface::Attach", "buffer", buffer->AsTracedValue());
has_pending_contents_ = true;
pending_buffer_ = buffer ? buffer->AsWeakPtr() : base::WeakPtr<Buffer>();
PreferredSizeChanged();
}
......@@ -197,17 +199,18 @@ void Surface::CommitSurfaceHierarchy() {
DCHECK(needs_commit_surface_hierarchy_);
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;
scoped_ptr<cc::SingleReleaseCallback> texture_mailbox_release_callback;
if (pending_buffer_) {
if (current_buffer_) {
texture_mailbox_release_callback =
pending_buffer_->AcquireTextureMailbox(&texture_mailbox);
pending_buffer_.reset();
has_contents_ = true;
} else {
// Show solid color content if there is no pending buffer.
layer()->SetShowSolidColorContent();
has_contents_ = false;
current_buffer_->ProduceTextureMailbox(&texture_mailbox);
}
if (texture_mailbox_release_callback) {
......@@ -220,11 +223,17 @@ void Surface::CommitSurfaceHierarchy() {
texture_mailbox.size_in_pixels()));
layer()->SetFillsBoundsOpaquely(pending_opaque_region_.contains(
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.
layer()->SchedulePaint(pending_damage_);
pending_damage_ = gfx::Rect();
}
ui::Compositor* compositor = layer()->GetCompositor();
if (compositor && !pending_frame_callbacks_.empty()) {
......@@ -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) {
compositor->RemoveObserver(this);
compositor_ = nullptr;
......
......@@ -98,8 +98,8 @@ class Surface : public views::View, public ui::CompositorObserver {
void OnCompositingDidCommit(ui::Compositor* compositor) override;
void OnCompositingStarted(ui::Compositor* compositor,
base::TimeTicks start_time) override;
void OnCompositingEnded(ui::Compositor* compositor) override {}
void OnCompositingAborted(ui::Compositor* compositor) override {}
void OnCompositingEnded(ui::Compositor* compositor) override;
void OnCompositingAborted(ui::Compositor* compositor) override;
void OnCompositingLockStateChanged(ui::Compositor* compositor) override {}
void OnCompositingShuttingDown(ui::Compositor* compositor) override;
......@@ -108,7 +108,12 @@ class Surface : public views::View, public ui::CompositorObserver {
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.
base::WeakPtr<Buffer> pending_buffer_;
......@@ -138,12 +143,16 @@ class Surface : public views::View, public ui::CompositorObserver {
using SubSurfaceEntryList = std::list<SubSurfaceEntry>;
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
// CommitSurfaceHierarchy() has not yet been called.
bool needs_commit_surface_hierarchy_;
// This is true when surface has some contents assigned to it.
bool has_contents_;
// This is true when the contents of the surface should be updated next time
// the compositor successfully ends compositing.
bool update_contents_after_successful_compositing_;
// The compsitor being observer or null if not observing a compositor.
ui::Compositor* compositor_;
......
......@@ -32,23 +32,21 @@ TEST_F(SurfaceTest, Attach) {
buffer->set_release_callback(
base::Bind(&ReleaseBuffer, base::Unretained(&release_buffer_call_count)));
scoped_ptr<Surface> surface1(new Surface);
scoped_ptr<Surface> surface2(new Surface);
scoped_ptr<Surface> surface(new Surface);
// Attach the buffer to surface1.
surface1->Attach(buffer.get());
surface1->Commit();
surface->Attach(buffer.get());
surface->Commit();
// Attaching buffer to surface2 when it is already attached to surface1
// should fail and buffer should remain attached to surface1.
surface2->Attach(buffer.get());
surface2->Commit();
// Commit without calling Attach() should have no effect.
surface->Commit();
EXPECT_EQ(0, release_buffer_call_count);
// Attach a null buffer to surface1, this should release the previously
// Attach a null buffer to surface, this should release the previously
// attached buffer.
surface1->Attach(nullptr);
surface1->Commit();
ASSERT_EQ(release_buffer_call_count, 1);
surface->Attach(nullptr);
surface->Commit();
ASSERT_EQ(1, release_buffer_call_count);
}
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