summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorreveman@google.com <reveman@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-14 06:57:15 +0000
committerreveman@google.com <reveman@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-14 06:57:15 +0000
commit150c970710a892e40ea148c6c8293e7b67650052 (patch)
tree355dc66aeea1eddc30293618013b24c77d1f1205
parent6e2a612da9dc1f5c6e814bef2461a8781f7390a9 (diff)
downloadchromium_src-150c970710a892e40ea148c6c8293e7b67650052.zip
chromium_src-150c970710a892e40ea148c6c8293e7b67650052.tar.gz
chromium_src-150c970710a892e40ea148c6c8293e7b67650052.tar.bz2
cc: Check for completed raster tasks at interval.
This significantly reduces the context switching overhead for impl-side painting. Instead of posting reply task to the impl thread after completing each raster job, post a reply only when worker pool becomes idle and poll for completed tasks at a 6ms interval. This doesn't just make rasterization more efficient but also reduces the number of shallow flushes, which makes async uploads more efficient. BUG=173802 Review URL: https://codereview.chromium.org/12217105 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@182404 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--cc/raster_worker_pool.cc6
-rw-r--r--cc/raster_worker_pool.h7
-rw-r--r--cc/tile_manager.cc19
-rw-r--r--cc/tile_manager.h14
-rw-r--r--cc/worker_pool.cc132
-rw-r--r--cc/worker_pool.h60
6 files changed, 193 insertions, 45 deletions
diff --git a/cc/raster_worker_pool.cc b/cc/raster_worker_pool.cc
index bc6e50e..13113b5 100644
--- a/cc/raster_worker_pool.cc
+++ b/cc/raster_worker_pool.cc
@@ -23,6 +23,7 @@ class RasterWorkerPoolTaskImpl : public internal::WorkerPoolTask {
virtual void Run(RenderingStats* rendering_stats) OVERRIDE {
task_.Run(picture_pile_.get(), rendering_stats);
+ base::subtle::Release_Store(&completed_, 1);
}
private:
@@ -32,8 +33,9 @@ class RasterWorkerPoolTaskImpl : public internal::WorkerPoolTask {
} // namespace
-RasterWorkerPool::RasterWorkerPool(size_t num_threads)
- : WorkerPool(num_threads) {
+RasterWorkerPool::RasterWorkerPool(
+ WorkerPoolClient* client, size_t num_threads)
+ : WorkerPool(client, num_threads) {
}
RasterWorkerPool::~RasterWorkerPool() {
diff --git a/cc/raster_worker_pool.h b/cc/raster_worker_pool.h
index d58df2d..cc5f7a1 100644
--- a/cc/raster_worker_pool.h
+++ b/cc/raster_worker_pool.h
@@ -20,8 +20,9 @@ class RasterWorkerPool : public WorkerPool {
virtual ~RasterWorkerPool();
- static scoped_ptr<RasterWorkerPool> Create(size_t num_threads) {
- return make_scoped_ptr(new RasterWorkerPool(num_threads));
+ static scoped_ptr<RasterWorkerPool> Create(
+ WorkerPoolClient* client, size_t num_threads) {
+ return make_scoped_ptr(new RasterWorkerPool(client, num_threads));
}
void PostRasterTaskAndReply(PicturePileImpl* picture_pile,
@@ -29,7 +30,7 @@ class RasterWorkerPool : public WorkerPool {
const base::Closure& reply);
private:
- explicit RasterWorkerPool(size_t num_threads);
+ RasterWorkerPool(WorkerPoolClient* client, size_t num_threads);
DISALLOW_COPY_AND_ASSIGN(RasterWorkerPool);
};
diff --git a/cc/tile_manager.cc b/cc/tile_manager.cc
index 9c857a4..6a70ca3 100644
--- a/cc/tile_manager.cc
+++ b/cc/tile_manager.cc
@@ -180,10 +180,11 @@ TileManager::TileManager(
bool use_cheapness_estimator)
: client_(client),
resource_pool_(ResourcePool::Create(resource_provider)),
- raster_worker_pool_(RasterWorkerPool::Create(num_raster_threads)),
+ raster_worker_pool_(RasterWorkerPool::Create(this, num_raster_threads)),
manage_tiles_pending_(false),
manage_tiles_call_count_(0),
bytes_pending_set_pixels_(0),
+ has_performed_uploads_since_last_flush_(false),
ever_exceeded_memory_budget_(false),
record_rendering_stats_(false),
use_cheapness_estimator_(use_cheapness_estimator) {
@@ -538,6 +539,16 @@ bool TileManager::HasPendingWorkScheduled(WhichTree tree) const {
return false;
}
+void TileManager::DidFinishDispatchingWorkerPoolCompletionCallbacks() {
+ // If a flush is needed, do it now before starting to dispatch more tasks.
+ if (has_performed_uploads_since_last_flush_) {
+ resource_pool_->resource_provider()->shallowFlushIfSupported();
+ has_performed_uploads_since_last_flush_ = false;
+ }
+
+ DispatchMoreTasks();
+}
+
void TileManager::AssignGpuMemoryToTiles() {
TRACE_EVENT0("cc", "TileManager::AssignGpuMemoryToTiles");
size_t unreleasable_bytes = 0;
@@ -749,8 +760,6 @@ void TileManager::OnImageDecodeTaskCompleted(
}
}
}
-
- DispatchMoreTasks();
}
scoped_ptr<ResourcePool::Resource> TileManager::PrepareTileForRaster(
@@ -838,7 +847,8 @@ void TileManager::OnRasterCompleted(
managed_tile_state.can_be_freed = false;
resource_pool_->resource_provider()->beginSetPixels(resource->id());
- resource_pool_->resource_provider()->shallowFlushIfSupported();
+ has_performed_uploads_since_last_flush_ = true;
+
managed_tile_state.resource = resource.Pass();
bytes_pending_set_pixels_ += tile->bytes_consumed_if_allocated();
@@ -858,7 +868,6 @@ void TileManager::OnRasterTaskCompleted(
int manage_tiles_call_count_when_dispatched) {
OnRasterCompleted(tile, resource.Pass(),
manage_tiles_call_count_when_dispatched);
- DispatchMoreTasks();
}
void TileManager::DidFinishTileInitialization(Tile* tile) {
diff --git a/cc/tile_manager.h b/cc/tile_manager.h
index c031582..1e0de2e 100644
--- a/cc/tile_manager.h
+++ b/cc/tile_manager.h
@@ -17,6 +17,7 @@
#include "cc/rendering_stats.h"
#include "cc/resource_pool.h"
#include "cc/tile_priority.h"
+#include "cc/worker_pool.h"
namespace cc {
class RasterWorkerPool;
@@ -102,7 +103,7 @@ class CC_EXPORT ManagedTileState {
// should no longer have any memory assigned to them. Tile objects are "owned"
// by layers; they automatically register with the manager when they are
// created, and unregister from the manager when they are deleted.
-class CC_EXPORT TileManager {
+class CC_EXPORT TileManager : public WorkerPoolClient {
public:
TileManager(TileManagerClient* client,
ResourceProvider *resource_provider,
@@ -128,7 +129,12 @@ class CC_EXPORT TileManager {
void GetRenderingStats(RenderingStats* stats);
bool HasPendingWorkScheduled(WhichTree tree) const;
- const MemoryHistory::Entry& memory_stats_from_last_assign() const { return memory_stats_from_last_assign_; }
+ const MemoryHistory::Entry& memory_stats_from_last_assign() const {
+ return memory_stats_from_last_assign_;
+ }
+
+ // Overridden from WorkerPoolClient:
+ virtual void DidFinishDispatchingWorkerPoolCompletionCallbacks() OVERRIDE;
protected:
// Methods called by Tile
@@ -158,7 +164,8 @@ class CC_EXPORT TileManager {
void DispatchOneImageDecodeTask(
scoped_refptr<Tile> tile, skia::LazyPixelRef* pixel_ref);
void OnImageDecodeTaskCompleted(
- scoped_refptr<Tile> tile, uint32_t pixel_ref_id);
+ scoped_refptr<Tile> tile,
+ uint32_t pixel_ref_id);
bool CanDispatchRasterTask(Tile* tile);
scoped_ptr<ResourcePool::Resource> PrepareTileForRaster(Tile* tile);
void DispatchOneRasterTask(scoped_refptr<Tile> tile);
@@ -216,6 +223,7 @@ class CC_EXPORT TileManager {
typedef std::queue<scoped_refptr<Tile> > TileQueue;
TileQueue tiles_with_pending_set_pixels_;
size_t bytes_pending_set_pixels_;
+ bool has_performed_uploads_since_last_flush_;
bool ever_exceeded_memory_budget_;
MemoryHistory::Entry memory_stats_from_last_assign_;
diff --git a/cc/worker_pool.cc b/cc/worker_pool.cc
index 7d3428a..ea572d9b 100644
--- a/cc/worker_pool.cc
+++ b/cc/worker_pool.cc
@@ -28,6 +28,7 @@ class WorkerPoolTaskImpl : public internal::WorkerPoolTask {
virtual void Run(RenderingStats* rendering_stats) OVERRIDE {
task_.Run(rendering_stats);
+ base::subtle::Release_Store(&completed_, 1);
}
private:
@@ -36,22 +37,31 @@ class WorkerPoolTaskImpl : public internal::WorkerPoolTask {
const char* kWorkerThreadNamePrefix = "Compositor";
-// Allow two pending tasks per worker. This keeps resource usage
-// low while making sure workers aren't unnecessarily idle.
-const int kNumPendingTasksPerWorker = 2;
+#if defined(OS_ANDROID)
+const int kNumPendingTasksPerWorker = 8;
+#else
+const int kNumPendingTasksPerWorker = 40;
+#endif
+
+const int kCheckForCompletedTasksDelayMs = 6;
} // namespace
namespace internal {
-WorkerPoolTask::WorkerPoolTask(const base::Closure& reply)
- : reply_(reply) {
+WorkerPoolTask::WorkerPoolTask(const base::Closure& reply) : reply_(reply) {
+ base::subtle::Acquire_Store(&completed_, 0);
}
WorkerPoolTask::~WorkerPoolTask() {
}
-void WorkerPoolTask::Completed() {
+bool WorkerPoolTask::HasCompleted() {
+ return base::subtle::Acquire_Load(&completed_) == 1;
+}
+
+void WorkerPoolTask::DidComplete() {
+ DCHECK_EQ(base::subtle::Acquire_Load(&completed_), 1);
reply_.Run();
}
@@ -60,7 +70,6 @@ void WorkerPoolTask::Completed() {
WorkerPool::Worker::Worker(WorkerPool* worker_pool, const std::string name)
: base::Thread(name.c_str()),
worker_pool_(worker_pool),
- weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
rendering_stats_(make_scoped_ptr(new RenderingStats)),
record_rendering_stats_(false) {
Start();
@@ -80,27 +89,22 @@ void WorkerPool::Worker::StopAfterCompletingAllPendingTasks() {
// all tasks have finished running.
while (!pending_tasks_.empty())
OnTaskCompleted();
-
- // Cancel all pending replies.
- weak_ptr_factory_.InvalidateWeakPtrs();
}
void WorkerPool::Worker::PostTask(scoped_ptr<internal::WorkerPoolTask> task) {
- DCHECK_LT(num_pending_tasks(), kNumPendingTasksPerWorker);
-
RenderingStats* stats =
record_rendering_stats_ ? rendering_stats_.get() : NULL;
- message_loop_proxy()->PostTaskAndReply(
+ worker_pool_->WillPostTask();
+
+ message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(&Worker::RunTask,
base::Unretained(task.get()),
- base::Unretained(stats)),
- base::Bind(&Worker::OnTaskCompleted, weak_ptr_factory_.GetWeakPtr()));
+ base::Unretained(worker_pool_),
+ base::Unretained(stats)));
pending_tasks_.push_back(task.Pass());
-
- worker_pool_->DidNumPendingTasksChange();
}
void WorkerPool::Worker::Init() {
@@ -113,22 +117,43 @@ void WorkerPool::Worker::Init() {
// static
void WorkerPool::Worker::RunTask(
- internal::WorkerPoolTask* task, RenderingStats* rendering_stats) {
+ internal::WorkerPoolTask* task,
+ WorkerPool* worker_pool,
+ RenderingStats* rendering_stats) {
task->Run(rendering_stats);
+ worker_pool->OnWorkCompletedOnWorkerThread();
}
void WorkerPool::Worker::OnTaskCompleted() {
CHECK(!pending_tasks_.empty());
scoped_ptr<internal::WorkerPoolTask> task = pending_tasks_.take_front();
- task->Completed();
- worker_pool_->DidNumPendingTasksChange();
+ // Notify worker pool of task completion.
+ worker_pool_->OnTaskCompleted();
+
+ task->DidComplete();
}
-WorkerPool::WorkerPool(size_t num_threads)
- : workers_need_sorting_(false),
- shutdown_(false) {
+void WorkerPool::Worker::CheckForCompletedTasks() {
+ while (!pending_tasks_.empty()) {
+ if (!pending_tasks_.front()->HasCompleted())
+ return;
+
+ OnTaskCompleted();
+ }
+}
+
+WorkerPool::WorkerPool(WorkerPoolClient* client, size_t num_threads)
+ : client_(client),
+ origin_loop_(base::MessageLoopProxy::current()),
+ weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
+ workers_need_sorting_(false),
+ pending_task_count_(0),
+ shutdown_(false),
+ check_for_completed_tasks_pending_(false),
+ idle_callback_(
+ base::Bind(&WorkerPool::OnIdle, weak_ptr_factory_.GetWeakPtr())) {
const std::string thread_name_prefix = kWorkerThreadNamePrefix;
while (workers_.size() < num_threads) {
int thread_number = workers_.size() + 1;
@@ -136,11 +161,15 @@ WorkerPool::WorkerPool(size_t num_threads)
this,
thread_name_prefix + StringPrintf("Worker%d", thread_number).c_str()));
}
+ base::subtle::Acquire_Store(&pending_task_count_, 0);
}
WorkerPool::~WorkerPool() {
Shutdown();
STLDeleteElements(&workers_);
+ // Cancel all pending callbacks.
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ DCHECK_EQ(base::subtle::Acquire_Load(&pending_task_count_), 0);
}
void WorkerPool::Shutdown() {
@@ -204,7 +233,62 @@ WorkerPool::Worker* WorkerPool::GetWorkerForNextTask() {
return workers_.front();
}
-void WorkerPool::DidNumPendingTasksChange() {
+void WorkerPool::ScheduleCheckForCompletedTasks() {
+ if (check_for_completed_tasks_pending_)
+ return;
+
+ check_for_completed_tasks_callback_.Reset(
+ base::Bind(&WorkerPool::CheckForCompletedTasks,
+ weak_ptr_factory_.GetWeakPtr()));
+ origin_loop_->PostDelayedTask(
+ FROM_HERE,
+ check_for_completed_tasks_callback_.callback(),
+ base::TimeDelta::FromMilliseconds(kCheckForCompletedTasksDelayMs));
+ check_for_completed_tasks_pending_ = true;
+}
+
+void WorkerPool::WillPostTask() {
+ base::subtle::Barrier_AtomicIncrement(&pending_task_count_, 1);
+ ScheduleCheckForCompletedTasks();
+ workers_need_sorting_ = true;
+}
+
+void WorkerPool::OnWorkCompletedOnWorkerThread() {
+ // Post idle handler task when pool work count reaches 0.
+ if (base::subtle::Barrier_AtomicIncrement(&pending_task_count_, -1) == 0) {
+ origin_loop_->PostTask(FROM_HERE, idle_callback_);
+ }
+}
+
+void WorkerPool::OnIdle() {
+ if (base::subtle::Acquire_Load(&pending_task_count_) == 0) {
+ check_for_completed_tasks_callback_.Cancel();
+ CheckForCompletedTasks();
+ }
+}
+
+void WorkerPool::CheckForCompletedTasks() {
+ check_for_completed_tasks_pending_ = false;
+
+ for (WorkerVector::iterator it = workers_.begin();
+ it != workers_.end(); it++) {
+ Worker* worker = *it;
+ worker->CheckForCompletedTasks();
+ }
+
+ client_->DidFinishDispatchingWorkerPoolCompletionCallbacks();
+
+ for (WorkerVector::iterator it = workers_.begin();
+ it != workers_.end(); it++) {
+ Worker* worker = *it;
+ if (worker->num_pending_tasks()) {
+ ScheduleCheckForCompletedTasks();
+ break;
+ }
+ }
+}
+
+void WorkerPool::OnTaskCompleted() {
workers_need_sorting_ = true;
}
diff --git a/cc/worker_pool.h b/cc/worker_pool.h
index f47dd70..26bce7a 100644
--- a/cc/worker_pool.h
+++ b/cc/worker_pool.h
@@ -9,6 +9,7 @@
#include "base/basictypes.h"
#include "base/callback.h"
+#include "base/cancelable_callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread.h"
@@ -24,16 +25,28 @@ class WorkerPoolTask {
virtual void Run(RenderingStats* rendering_stats) = 0;
- void Completed();
+ bool HasCompleted();
+ void DidComplete();
protected:
WorkerPoolTask(const base::Closure& reply);
- base::Closure reply_;
+ const base::Closure reply_;
+
+ // Accessed from multiple threads. Set to 1 when task has completed.
+ base::subtle::Atomic32 completed_;
};
} // namespace internal
+class CC_EXPORT WorkerPoolClient {
+ public:
+ virtual void DidFinishDispatchingWorkerPoolCompletionCallbacks() = 0;
+
+ protected:
+ virtual ~WorkerPoolClient() {}
+};
+
// A worker thread pool that runs rendering tasks and guarantees completion
// of all pending tasks at shutdown.
class WorkerPool {
@@ -42,8 +55,9 @@ class WorkerPool {
virtual ~WorkerPool();
- static scoped_ptr<WorkerPool> Create(size_t num_threads) {
- return make_scoped_ptr(new WorkerPool(num_threads));
+ static scoped_ptr<WorkerPool> Create(
+ WorkerPoolClient* client, size_t num_threads) {
+ return make_scoped_ptr(new WorkerPool(client, num_threads));
}
// Tells the worker pool to shutdown and returns once all pending tasks have
@@ -76,6 +90,9 @@ class WorkerPool {
// Posts a task to the worker thread.
void PostTask(scoped_ptr<internal::WorkerPoolTask> task);
+ // Check for completed tasks and run reply callbacks.
+ void CheckForCompletedTasks();
+
int num_pending_tasks() const { return pending_tasks_.size(); }
void set_record_rendering_stats(bool record_rendering_stats) {
record_rendering_stats_ = record_rendering_stats;
@@ -89,18 +106,19 @@ class WorkerPool {
private:
static void RunTask(
- internal::WorkerPoolTask* task, RenderingStats* rendering_stats);
+ internal::WorkerPoolTask* task,
+ WorkerPool* worker_pool,
+ RenderingStats* rendering_stats);
void OnTaskCompleted();
WorkerPool* worker_pool_;
- base::WeakPtrFactory<Worker> weak_ptr_factory_;
ScopedPtrDeque<internal::WorkerPoolTask> pending_tasks_;
scoped_ptr<RenderingStats> rendering_stats_;
bool record_rendering_stats_;
};
- explicit WorkerPool(size_t num_threads);
+ WorkerPool(WorkerPoolClient* client, size_t num_threads);
WorkerPool::Worker* GetWorkerForNextTask();
@@ -112,13 +130,39 @@ class WorkerPool {
}
};
- void DidNumPendingTasksChange();
+ // Schedule a completed tasks check if not already pending.
+ void ScheduleCheckForCompletedTasks();
+
+ // Called on origin thread before posting task to worker.
+ void WillPostTask();
+
+ // Called on worker thread after completing work.
+ void OnWorkCompletedOnWorkerThread();
+
+ // Called on origin thread after becoming idle.
+ void OnIdle();
+
+ // Check for completed tasks and run reply callbacks.
+ void CheckForCompletedTasks();
+
+ // Called when processing task completion.
+ void OnTaskCompleted();
+
+ // Ensure workers are sorted by number of pending tasks.
void SortWorkersIfNeeded();
typedef std::vector<Worker*> WorkerVector;
WorkerVector workers_;
+ WorkerPoolClient* client_;
+ scoped_refptr<base::MessageLoopProxy> origin_loop_;
+ base::WeakPtrFactory<WorkerPool> weak_ptr_factory_;
bool workers_need_sorting_;
bool shutdown_;
+ base::CancelableClosure check_for_completed_tasks_callback_;
+ bool check_for_completed_tasks_pending_;
+ base::Closure idle_callback_;
+ // Accessed from multiple threads. 0 when worker pool is idle.
+ base::subtle::Atomic32 pending_task_count_;
DISALLOW_COPY_AND_ASSIGN(WorkerPool);
};