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 @@ ...@@ -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.
const gpu::SyncToken& sync_token, void Release();
bool is_lost);
// 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_; 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,34 +199,41 @@ void Surface::CommitSurfaceHierarchy() { ...@@ -197,34 +199,41 @@ void Surface::CommitSurfaceHierarchy() {
DCHECK(needs_commit_surface_hierarchy_); DCHECK(needs_commit_surface_hierarchy_);
needs_commit_surface_hierarchy_ = false; needs_commit_surface_hierarchy_ = false;
cc::TextureMailbox texture_mailbox; // We update contents if Attach() has been called since last commit.
scoped_ptr<cc::SingleReleaseCallback> texture_mailbox_release_callback; if (has_pending_contents_) {
if (pending_buffer_) { has_pending_contents_ = false;
texture_mailbox_release_callback =
pending_buffer_->AcquireTextureMailbox(&texture_mailbox); current_buffer_ = pending_buffer_;
pending_buffer_.reset(); 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) { cc::TextureMailbox texture_mailbox;
// Update layer with the new contents. scoped_ptr<cc::SingleReleaseCallback> texture_mailbox_release_callback;
layer()->SetTextureMailbox(texture_mailbox, if (current_buffer_) {
texture_mailbox_release_callback.Pass(), texture_mailbox_release_callback =
texture_mailbox.size_in_pixels()); current_buffer_->ProduceTextureMailbox(&texture_mailbox);
layer()->SetTextureFlipped(false); }
layer()->SetBounds(gfx::Rect(layer()->bounds().origin(),
texture_mailbox.size_in_pixels()));
layer()->SetFillsBoundsOpaquely(pending_opaque_region_.contains(
gfx::RectToSkIRect(gfx::Rect(texture_mailbox.size_in_pixels()))));
}
// Schedule redraw of the damage region. if (texture_mailbox_release_callback) {
layer()->SchedulePaint(pending_damage_); // Update layer with the new contents.
pending_damage_ = gfx::Rect(); layer()->SetTextureMailbox(texture_mailbox,
texture_mailbox_release_callback.Pass(),
texture_mailbox.size_in_pixels());
layer()->SetTextureFlipped(false);
layer()->SetBounds(gfx::Rect(layer()->bounds().origin(),
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(); 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