From c6ae6302da283916bbcb52c893ebcb4b6635cf99 Mon Sep 17 00:00:00 2001 From: "boliu@chromium.org" Date: Fri, 23 May 2014 10:45:45 +0000 Subject: [PATCH] cc: Compositor without MessageLoopProxy 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 --- cc/cc_tests.gyp | 1 + cc/trees/blocking_task_runner.cc | 47 ++-- cc/trees/blocking_task_runner.h | 6 +- ...ayer_tree_host_unittest_no_message_loop.cc | 235 ++++++++++++++++++ cc/trees/proxy.cc | 9 +- cc/trees/proxy.h | 2 + 6 files changed, 272 insertions(+), 28 deletions(-) create mode 100644 cc/trees/layer_tree_host_unittest_no_message_loop.cc diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp index 127bb1d36b7dea..857d098c11911c 100644 --- a/cc/cc_tests.gyp +++ b/cc/cc_tests.gyp @@ -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', diff --git a/cc/trees/blocking_task_runner.cc b/cc/trees/blocking_task_runner.cc index 576aa9b1bf9360..5c33f40d4fcef5 100644 --- a/cc/trees/blocking_task_runner.cc +++ b/cc/trees/blocking_task_runner.cc @@ -12,16 +12,13 @@ namespace cc { -typedef std::pair > TaskRunnerPair; - struct TaskRunnerPairs { static TaskRunnerPairs* GetInstance() { return Singleton::get(); } base::Lock lock; - std::vector pairs; + std::vector > runners; private: friend struct DefaultSingletonTraits; @@ -30,43 +27,47 @@ struct TaskRunnerPairs { // static scoped_refptr 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 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 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 runner = new BlockingTaskRunner(current); - task_runners->pairs.push_back(TaskRunnerPair(current, runner)); + scoped_refptr runner = + new BlockingTaskRunner(base::MessageLoopProxy::current()); + task_runners->runners.push_back(runner); return runner; } BlockingTaskRunner::BlockingTaskRunner( scoped_refptr 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); diff --git a/cc/trees/blocking_task_runner.h b/cc/trees/blocking_task_runner.h index 28633171c20930..8388a881e607e6 100644 --- a/cc/trees/blocking_task_runner.h +++ b/cc/trees/blocking_task_runner.h @@ -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 { @@ -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 @@ -82,6 +81,7 @@ class CC_EXPORT BlockingTaskRunner void SetCapture(bool capture); + base::PlatformThreadId thread_id_; scoped_refptr task_runner_; base::Lock lock_; diff --git a/cc/trees/layer_tree_host_unittest_no_message_loop.cc b/cc/trees/layer_tree_host_unittest_no_message_loop.cc new file mode 100644 index 00000000000000..4c6796f30211a2 --- /dev/null +++ b/cc/trees/layer_tree_host_unittest_no_message_loop.cc @@ -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 CreateOutputSurface( + bool fallback) OVERRIDE { + return make_scoped_ptr(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 layer_tree_host_; + scoped_refptr 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 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 CreateFrameDataWithResource( + ResourceProvider::ResourceId resource_id) { + scoped_ptr frame(new DelegatedFrameData); + gfx::Rect frame_rect(size_); + + scoped_ptr 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 resource_collection_; + scoped_refptr frame_provider_; + scoped_refptr delegated_layer_; +}; + +TEST_F(LayerTreeHostNoMessageLoopDelegatedLayer, SingleDelegatedLayer) { + RunTest(); +} + +} // namespace +} // namespace cc diff --git a/cc/trees/proxy.cc b/cc/trees/proxy.cc index 66967a645c230e..a8f7b26fb04a15 100644 --- a/cc/trees/proxy.cc +++ b/cc/trees/proxy.cc @@ -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 @@ -68,6 +72,7 @@ Proxy::Proxy(scoped_refptr 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 diff --git a/cc/trees/proxy.h b/cc/trees/proxy.h index af7a9267711ace..4066513773c01c 100644 --- a/cc/trees/proxy.h +++ b/cc/trees/proxy.h @@ -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" @@ -107,6 +108,7 @@ class CC_EXPORT Proxy { scoped_refptr main_task_runner_; scoped_refptr impl_task_runner_; #if DCHECK_IS_ON + const base::PlatformThreadId main_thread_id_; bool impl_thread_is_overridden_; bool is_main_thread_blocked_; #endif