Skip to content

Commit

Permalink
cc: Compositor without MessageLoopProxy
Browse files Browse the repository at this point in the history
Support using single-threaded compositor with a single
DelegatedRendererLayer on a thread that does not have a MessageLoop.

First, Allow BlockingTaskRunner to be used on a thread that does not
have a MessageLoopProxy. For these threads, all PostTask calls must
be wrapped in CapturePostTasks. Use PlatformThreadId rather than
MessageLoopProxy::BelongsToCurrent thread to check for current.

Then fix DCHECK failures in Proxy to allow MessageLoopProxy to be
NULL.

Add tests to verify these code paths are indeed working.

BUG=344087

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272473 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
boliu@chromium.org committed May 23, 2014
1 parent bf418af commit c6ae630
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 28 deletions.
1 change: 1 addition & 0 deletions cc/cc_tests.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
'trees/layer_tree_host_unittest_damage.cc',
'trees/layer_tree_host_unittest_delegated.cc',
'trees/layer_tree_host_unittest_occlusion.cc',
'trees/layer_tree_host_unittest_no_message_loop.cc',
'trees/layer_tree_host_unittest_picture.cc',
'trees/layer_tree_host_unittest_proxy.cc',
'trees/layer_tree_host_unittest_scroll.cc',
Expand Down
47 changes: 24 additions & 23 deletions cc/trees/blocking_task_runner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,13 @@

namespace cc {

typedef std::pair<base::SingleThreadTaskRunner*,
scoped_refptr<BlockingTaskRunner> > TaskRunnerPair;

struct TaskRunnerPairs {
static TaskRunnerPairs* GetInstance() {
return Singleton<TaskRunnerPairs>::get();
}

base::Lock lock;
std::vector<TaskRunnerPair> pairs;
std::vector<scoped_refptr<BlockingTaskRunner> > runners;

private:
friend struct DefaultSingletonTraits<TaskRunnerPairs>;
Expand All @@ -30,43 +27,47 @@ struct TaskRunnerPairs {
// static
scoped_refptr<BlockingTaskRunner> BlockingTaskRunner::current() {
TaskRunnerPairs* task_runners = TaskRunnerPairs::GetInstance();
base::PlatformThreadId thread_id = base::PlatformThread::CurrentId();

base::AutoLock lock(task_runners->lock);

for (size_t i = 0; i < task_runners->pairs.size(); ++i) {
if (task_runners->pairs[i].first->HasOneRef()) {
// The SingleThreadTaskRunner is kept alive by its MessageLoop, and we
// hold a second reference in the TaskRunnerPairs array. If the
// SingleThreadTaskRunner has one ref, then it is being held alive only
// by the BlockingTaskRunner and the MessageLoop is gone, so drop the
// BlockingTaskRunner from the TaskRunnerPairs array along with the
// SingleThreadTaskRunner.
task_runners->pairs.erase(task_runners->pairs.begin() + i);
--i;
scoped_refptr<BlockingTaskRunner> current_task_runner;

for (size_t i = 0; i < task_runners->runners.size(); ++i) {
if (task_runners->runners[i]->thread_id_ == thread_id) {
current_task_runner = task_runners->runners[i];
} else if (task_runners->runners[i]->HasOneRef()) {
task_runners->runners.erase(task_runners->runners.begin() + i);
i--;
}
}

scoped_refptr<base::SingleThreadTaskRunner> current =
base::MessageLoopProxy::current();
for (size_t i = 0; i < task_runners->pairs.size(); ++i) {
if (task_runners->pairs[i].first == current.get())
return task_runners->pairs[i].second.get();
}
if (current_task_runner)
return current_task_runner;

scoped_refptr<BlockingTaskRunner> runner = new BlockingTaskRunner(current);
task_runners->pairs.push_back(TaskRunnerPair(current, runner));
scoped_refptr<BlockingTaskRunner> runner =
new BlockingTaskRunner(base::MessageLoopProxy::current());
task_runners->runners.push_back(runner);
return runner;
}

BlockingTaskRunner::BlockingTaskRunner(
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: task_runner_(task_runner), capture_(0) {}
: thread_id_(base::PlatformThread::CurrentId()),
task_runner_(task_runner),
capture_(0) {
}

BlockingTaskRunner::~BlockingTaskRunner() {}

bool BlockingTaskRunner::BelongsToCurrentThread() {
return base::PlatformThread::CurrentId() == thread_id_;
}

bool BlockingTaskRunner::PostTask(const tracked_objects::Location& from_here,
const base::Closure& task) {
base::AutoLock lock(lock_);
DCHECK(task_runner_.get() || capture_);
if (!capture_)
return task_runner_->PostTask(from_here, task);
captured_tasks_.push_back(task);
Expand Down
6 changes: 3 additions & 3 deletions cc/trees/blocking_task_runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/threading/platform_thread.h"
#include "cc/base/cc_export.h"

namespace cc {
Expand Down Expand Up @@ -62,9 +63,7 @@ class CC_EXPORT BlockingTaskRunner

// True if tasks posted to the BlockingTaskRunner will run on the current
// thread.
bool BelongsToCurrentThread() {
return task_runner_->BelongsToCurrentThread();
}
bool BelongsToCurrentThread();

// Posts a task using the contained SingleThreadTaskRunner unless |capture_|
// is true. When |capture_| is true, tasks posted will be caught and stored
Expand All @@ -82,6 +81,7 @@ class CC_EXPORT BlockingTaskRunner

void SetCapture(bool capture);

base::PlatformThreadId thread_id_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;

base::Lock lock_;
Expand Down
235 changes: 235 additions & 0 deletions cc/trees/layer_tree_host_unittest_no_message_loop.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/message_loop/message_loop_proxy.h"
#include "base/threading/simple_thread.h"
#include "cc/layers/delegated_frame_provider.h"
#include "cc/layers/delegated_frame_resource_collection.h"
#include "cc/layers/delegated_renderer_layer.h"
#include "cc/layers/layer.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/output/delegated_frame_data.h"
#include "cc/output/output_surface.h"
#include "cc/output/output_surface_client.h"
#include "cc/resources/resource_provider.h"
#include "cc/test/fake_delegated_renderer_layer.h"
#include "cc/test/test_context_provider.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_host_client.h"
#include "cc/trees/layer_tree_host_single_thread_client.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/frame_time.h"

namespace cc {
namespace {

class NoMessageLoopOutputSurface : public OutputSurface {
public:
NoMessageLoopOutputSurface() : OutputSurface(TestContextProvider::Create()) {}
virtual ~NoMessageLoopOutputSurface() {}

// OutputSurface overrides.
virtual void SwapBuffers(CompositorFrame* frame) OVERRIDE {
DCHECK(client_);
client_->DidSwapBuffers();
client_->DidSwapBuffersComplete();
}
};

class LayerTreeHostNoMessageLoopTest
: public testing::Test,
public base::DelegateSimpleThread::Delegate,
public LayerTreeHostClient,
public LayerTreeHostSingleThreadClient {
public:
LayerTreeHostNoMessageLoopTest()
: did_initialize_output_surface_(false),
did_commit_(false),
did_commit_and_draw_frame_(false),
size_(100, 100),
no_loop_thread_(this, "LayerTreeHostNoMessageLoopTest") {}
virtual ~LayerTreeHostNoMessageLoopTest() {}

// LayerTreeHostClient overrides.
virtual void WillBeginMainFrame(int frame_id) OVERRIDE {}
virtual void DidBeginMainFrame() OVERRIDE {}
virtual void Animate(base::TimeTicks frame_begin_time) OVERRIDE {}
virtual void Layout() OVERRIDE {}
virtual void ApplyScrollAndScale(const gfx::Vector2d& scroll_delta,
float page_scale) OVERRIDE {}
virtual scoped_ptr<OutputSurface> CreateOutputSurface(
bool fallback) OVERRIDE {
return make_scoped_ptr<OutputSurface>(new NoMessageLoopOutputSurface);
}
virtual void DidInitializeOutputSurface() OVERRIDE {
did_initialize_output_surface_ = true;
}
virtual void WillCommit() OVERRIDE {}
virtual void DidCommit() OVERRIDE { did_commit_ = true; }
virtual void DidCommitAndDrawFrame() OVERRIDE {
did_commit_and_draw_frame_ = true;
}
virtual void DidCompleteSwapBuffers() OVERRIDE {}

// LayerTreeHostSingleThreadClient overrides.
virtual void ScheduleComposite() OVERRIDE {}
virtual void ScheduleAnimation() OVERRIDE {}
virtual void DidPostSwapBuffers() OVERRIDE {}
virtual void DidAbortSwapBuffers() OVERRIDE {}

void RunTest() {
no_loop_thread_.Start();
no_loop_thread_.Join();
}

// base::DelegateSimpleThread::Delegate override.
virtual void Run() OVERRIDE {
ASSERT_FALSE(base::MessageLoopProxy::current());
RunTestWithoutMessageLoop();
EXPECT_FALSE(base::MessageLoopProxy::current());
}

protected:
virtual void RunTestWithoutMessageLoop() = 0;

void SetupLayerTreeHost() {
LayerTreeSettings settings;
layer_tree_host_ =
LayerTreeHost::CreateSingleThreaded(this, this, NULL, settings);
layer_tree_host_->SetViewportSize(size_);
layer_tree_host_->SetRootLayer(root_layer_);
}

void Composite() {
did_commit_ = false;
did_commit_and_draw_frame_ = false;
layer_tree_host_->Composite(gfx::FrameTime::Now());
EXPECT_TRUE(did_initialize_output_surface_);
EXPECT_TRUE(did_commit_);
EXPECT_TRUE(did_commit_and_draw_frame_);
}

void TearDownLayerTreeHost() {
// Explicit teardown to make failures easier to debug.
layer_tree_host_.reset();
root_layer_ = NULL;
}

// All protected member variables are accessed only on |no_loop_thread_|.
scoped_ptr<LayerTreeHost> layer_tree_host_;
scoped_refptr<Layer> root_layer_;

bool did_initialize_output_surface_;
bool did_commit_;
bool did_commit_and_draw_frame_;
gfx::Size size_;

private:
base::DelegateSimpleThread no_loop_thread_;
};

class LayerTreeHostNoMessageLoopSmokeTest
: public LayerTreeHostNoMessageLoopTest {
protected:
virtual void RunTestWithoutMessageLoop() OVERRIDE {
gfx::Size size(100, 100);

// Set up root layer.
{
scoped_refptr<SolidColorLayer> solid_color_layer =
SolidColorLayer::Create();
solid_color_layer->SetBackgroundColor(SK_ColorRED);
solid_color_layer->SetBounds(size_);
solid_color_layer->SetIsDrawable(true);
root_layer_ = solid_color_layer;
}

SetupLayerTreeHost();
Composite();
TearDownLayerTreeHost();
}
};

TEST_F(LayerTreeHostNoMessageLoopSmokeTest, SmokeTest) {
RunTest();
}

class LayerTreeHostNoMessageLoopDelegatedLayer
: public LayerTreeHostNoMessageLoopTest,
public DelegatedFrameResourceCollectionClient {
protected:
virtual void RunTestWithoutMessageLoop() OVERRIDE {
resource_collection_ = new DelegatedFrameResourceCollection;
frame_provider_ = new DelegatedFrameProvider(
resource_collection_.get(), CreateFrameDataWithResource(998));

root_layer_ = Layer::Create();
delegated_layer_ = FakeDelegatedRendererLayer::Create(frame_provider_);
delegated_layer_->SetBounds(size_);
delegated_layer_->SetIsDrawable(true);
root_layer_->AddChild(delegated_layer_);

SetupLayerTreeHost();

// Draw first frame.
Composite();

// Prepare and draw second frame.
frame_provider_->SetFrameData(CreateFrameDataWithResource(999));
Composite();

// Resource from first frame should be returned.
CheckReturnedResource(1u);

TearDownLayerTreeHost();
delegated_layer_ = NULL;
frame_provider_ = NULL;

// Resource from second frame should be returned.
CheckReturnedResource(1u);
resource_collection_ = NULL;
}

// DelegatedFrameResourceCollectionClient overrides.
virtual void UnusedResourcesAreAvailable() OVERRIDE {}

private:
scoped_ptr<DelegatedFrameData> CreateFrameDataWithResource(
ResourceProvider::ResourceId resource_id) {
scoped_ptr<DelegatedFrameData> frame(new DelegatedFrameData);
gfx::Rect frame_rect(size_);

scoped_ptr<RenderPass> root_pass(RenderPass::Create());
root_pass->SetNew(
RenderPass::Id(1, 1), frame_rect, frame_rect, gfx::Transform());
frame->render_pass_list.push_back(root_pass.Pass());

TransferableResource resource;
resource.id = resource_id;
resource.mailbox_holder.texture_target = GL_TEXTURE_2D;
resource.mailbox_holder.mailbox = gpu::Mailbox::Generate();
frame->resource_list.push_back(resource);

return frame.Pass();
}

void CheckReturnedResource(size_t expected_num) {
ReturnedResourceArray returned_resources;
resource_collection_->TakeUnusedResourcesForChildCompositor(
&returned_resources);
EXPECT_EQ(expected_num, returned_resources.size());
}

scoped_refptr<DelegatedFrameResourceCollection> resource_collection_;
scoped_refptr<DelegatedFrameProvider> frame_provider_;
scoped_refptr<DelegatedRendererLayer> delegated_layer_;
};

TEST_F(LayerTreeHostNoMessageLoopDelegatedLayer, SingleDelegatedLayer) {
RunTest();
}

} // namespace
} // namespace cc
9 changes: 7 additions & 2 deletions cc/trees/proxy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ base::SingleThreadTaskRunner* Proxy::ImplThreadTaskRunner() const {

bool Proxy::IsMainThread() const {
#if DCHECK_IS_ON
DCHECK(main_task_runner_.get());
if (impl_thread_is_overridden_)
return false;
return main_task_runner_->BelongsToCurrentThread();

bool is_main_thread = base::PlatformThread::CurrentId() == main_thread_id_;
if (is_main_thread && main_task_runner_.get()) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
}
return is_main_thread;
#else
return true;
#endif
Expand Down Expand Up @@ -68,6 +72,7 @@ Proxy::Proxy(scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner)
impl_task_runner_(impl_task_runner) {
#else
impl_task_runner_(impl_task_runner),
main_thread_id_(base::PlatformThread::CurrentId()),
impl_thread_is_overridden_(false),
is_main_thread_blocked_(false) {
#endif
Expand Down
2 changes: 2 additions & 0 deletions cc/trees/proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "base/values.h"
#include "cc/base/cc_export.h"
Expand Down Expand Up @@ -107,6 +108,7 @@ class CC_EXPORT Proxy {
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner_;
#if DCHECK_IS_ON
const base::PlatformThreadId main_thread_id_;
bool impl_thread_is_overridden_;
bool is_main_thread_blocked_;
#endif
Expand Down

0 comments on commit c6ae630

Please sign in to comment.