diff options
Diffstat (limited to 'chromium/cc/resources/pixel_buffer_raster_worker_pool.cc')
-rw-r--r-- | chromium/cc/resources/pixel_buffer_raster_worker_pool.cc | 683 |
1 files changed, 683 insertions, 0 deletions
diff --git a/chromium/cc/resources/pixel_buffer_raster_worker_pool.cc b/chromium/cc/resources/pixel_buffer_raster_worker_pool.cc new file mode 100644 index 00000000000..a03ed81f4e1 --- /dev/null +++ b/chromium/cc/resources/pixel_buffer_raster_worker_pool.cc @@ -0,0 +1,683 @@ +// Copyright 2013 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 "cc/resources/pixel_buffer_raster_worker_pool.h" + +#include "base/containers/stack_container.h" +#include "base/debug/trace_event.h" +#include "base/values.h" +#include "cc/debug/traced_value.h" +#include "cc/resources/resource.h" +#include "third_party/skia/include/core/SkDevice.h" + +namespace cc { + +namespace { + +class PixelBufferWorkerPoolTaskImpl : public internal::WorkerPoolTask { + public: + typedef base::Callback<void(bool was_canceled, bool needs_upload)> Reply; + + PixelBufferWorkerPoolTaskImpl(internal::RasterWorkerPoolTask* task, + uint8_t* buffer, + const Reply& reply) + : task_(task), + buffer_(buffer), + reply_(reply), + needs_upload_(false) { + } + + // Overridden from internal::WorkerPoolTask: + virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE { + // |buffer_| can be NULL in lost context situations. + if (!buffer_) { + // |needs_upload_| still needs to be true as task has not + // been canceled. + needs_upload_ = true; + return; + } + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, + task_->resource()->size().width(), + task_->resource()->size().height()); + bitmap.setPixels(buffer_); + SkDevice device(bitmap); + needs_upload_ = task_->RunOnWorkerThread(&device, thread_index); + } + virtual void CompleteOnOriginThread() OVERRIDE { + // |needs_upload_| must be be false if task didn't run. + DCHECK(HasFinishedRunning() || !needs_upload_); + reply_.Run(!HasFinishedRunning(), needs_upload_); + } + + private: + virtual ~PixelBufferWorkerPoolTaskImpl() {} + + scoped_refptr<internal::RasterWorkerPoolTask> task_; + uint8_t* buffer_; + const Reply reply_; + bool needs_upload_; + + DISALLOW_COPY_AND_ASSIGN(PixelBufferWorkerPoolTaskImpl); +}; + +// If we raster too fast we become upload bound, and pending +// uploads consume memory. For maximum upload throughput, we would +// want to allow for upload_throughput * pipeline_time of pending +// uploads, after which we are just wasting memory. Since we don't +// know our upload throughput yet, this just caps our memory usage. +#if defined(OS_ANDROID) +// For reference Nexus10 can upload 1MB in about 2.5ms. +const size_t kMaxBytesUploadedPerMs = (2 * 1024 * 1024) / 5; +#else +// For reference Chromebook Pixel can upload 1MB in about 0.5ms. +const size_t kMaxBytesUploadedPerMs = 1024 * 1024 * 2; +#endif + +// Assuming a two frame deep pipeline. +const size_t kMaxPendingUploadBytes = 16 * 2 * kMaxBytesUploadedPerMs; + +const int kCheckForCompletedRasterTasksDelayMs = 6; + +const size_t kMaxScheduledRasterTasks = 48; + +typedef base::StackVector<internal::GraphNode*, + kMaxScheduledRasterTasks> NodeVector; + +void AddDependenciesToGraphNode( + internal::GraphNode* node, + const NodeVector::ContainerType& dependencies) { + for (NodeVector::ContainerType::const_iterator it = dependencies.begin(); + it != dependencies.end(); ++it) { + internal::GraphNode* dependency = *it; + + node->add_dependency(); + dependency->add_dependent(node); + } +} + +// Only used as std::find_if predicate for DCHECKs. +bool WasCanceled(const internal::RasterWorkerPoolTask* task) { + return task->WasCanceled(); +} + +} // namespace + +PixelBufferRasterWorkerPool::PixelBufferRasterWorkerPool( + ResourceProvider* resource_provider, + size_t num_threads) + : RasterWorkerPool(resource_provider, num_threads), + shutdown_(false), + scheduled_raster_task_count_(0), + bytes_pending_upload_(0), + has_performed_uploads_since_last_flush_(false), + check_for_completed_raster_tasks_pending_(false), + should_notify_client_if_no_tasks_are_pending_(false), + should_notify_client_if_no_tasks_required_for_activation_are_pending_( + false) { +} + +PixelBufferRasterWorkerPool::~PixelBufferRasterWorkerPool() { + DCHECK(shutdown_); + DCHECK(!check_for_completed_raster_tasks_pending_); + DCHECK_EQ(0u, pixel_buffer_tasks_.size()); + DCHECK_EQ(0u, tasks_with_pending_upload_.size()); + DCHECK_EQ(0u, completed_tasks_.size()); +} + +void PixelBufferRasterWorkerPool::Shutdown() { + shutdown_ = true; + RasterWorkerPool::Shutdown(); + RasterWorkerPool::CheckForCompletedTasks(); + CheckForCompletedUploads(); + check_for_completed_raster_tasks_callback_.Cancel(); + check_for_completed_raster_tasks_pending_ = false; + for (TaskMap::iterator it = pixel_buffer_tasks_.begin(); + it != pixel_buffer_tasks_.end(); ++it) { + internal::RasterWorkerPoolTask* task = it->first; + internal::WorkerPoolTask* pixel_buffer_task = it->second.get(); + + // All inactive tasks needs to be canceled. + if (!pixel_buffer_task && !task->HasFinishedRunning()) { + task->DidRun(true); + completed_tasks_.push_back(task); + } + } + DCHECK_EQ(completed_tasks_.size(), pixel_buffer_tasks_.size()); +} + +void PixelBufferRasterWorkerPool::ScheduleTasks(RasterTask::Queue* queue) { + TRACE_EVENT0("cc", "PixelBufferRasterWorkerPool::ScheduleTasks"); + + RasterWorkerPool::SetRasterTasks(queue); + + if (!should_notify_client_if_no_tasks_are_pending_) + TRACE_EVENT_ASYNC_BEGIN0("cc", "ScheduledTasks", this); + + should_notify_client_if_no_tasks_are_pending_ = true; + should_notify_client_if_no_tasks_required_for_activation_are_pending_ = true; + + // Build new pixel buffer task set. + TaskMap new_pixel_buffer_tasks; + for (RasterTaskVector::const_iterator it = raster_tasks().begin(); + it != raster_tasks().end(); ++it) { + internal::RasterWorkerPoolTask* task = it->get(); + DCHECK(new_pixel_buffer_tasks.find(task) == new_pixel_buffer_tasks.end()); + DCHECK(!task->HasCompleted()); + + // Use existing pixel buffer task if available. + TaskMap::iterator pixel_buffer_it = pixel_buffer_tasks_.find(task); + if (pixel_buffer_it == pixel_buffer_tasks_.end()) { + new_pixel_buffer_tasks[task] = NULL; + continue; + } + + new_pixel_buffer_tasks[task] = pixel_buffer_it->second; + pixel_buffer_tasks_.erase(task); + } + + // Transfer remaining pixel buffer tasks to |new_pixel_buffer_tasks| + // and cancel all remaining inactive tasks. + for (TaskMap::iterator it = pixel_buffer_tasks_.begin(); + it != pixel_buffer_tasks_.end(); ++it) { + internal::RasterWorkerPoolTask* task = it->first; + internal::WorkerPoolTask* pixel_buffer_task = it->second.get(); + + // Move task to |new_pixel_buffer_tasks| + new_pixel_buffer_tasks[task] = pixel_buffer_task; + + // Inactive task can be canceled. + if (!pixel_buffer_task && !task->HasFinishedRunning()) { + task->DidRun(true); + DCHECK(std::find(completed_tasks_.begin(), + completed_tasks_.end(), + task) == completed_tasks_.end()); + completed_tasks_.push_back(task); + } + } + + tasks_required_for_activation_.clear(); + for (TaskMap::iterator it = new_pixel_buffer_tasks.begin(); + it != new_pixel_buffer_tasks.end(); ++it) { + internal::RasterWorkerPoolTask* task = it->first; + if (IsRasterTaskRequiredForActivation(task)) + tasks_required_for_activation_.insert(task); + } + + // |tasks_required_for_activation_| contains all tasks that need to + // complete before we can send a "ready to activate" signal. Tasks + // that have already completed should not be part of this set. + for (TaskDeque::const_iterator it = completed_tasks_.begin(); + it != completed_tasks_.end(); ++it) { + tasks_required_for_activation_.erase(*it); + } + + pixel_buffer_tasks_.swap(new_pixel_buffer_tasks); + + // Check for completed tasks when ScheduleTasks() is called as + // priorities might have changed and this maximizes the number + // of top priority tasks that are scheduled. + RasterWorkerPool::CheckForCompletedTasks(); + CheckForCompletedUploads(); + FlushUploads(); + + // Schedule new tasks. + ScheduleMoreTasks(); + + // Cancel any pending check for completed raster tasks and schedule + // another check. + check_for_completed_raster_tasks_callback_.Cancel(); + check_for_completed_raster_tasks_pending_ = false; + ScheduleCheckForCompletedRasterTasks(); + + TRACE_EVENT_ASYNC_STEP1( + "cc", "ScheduledTasks", this, StateName(), + "state", TracedValue::FromValue(StateAsValue().release())); +} + +void PixelBufferRasterWorkerPool::CheckForCompletedTasks() { + TRACE_EVENT0("cc", "PixelBufferRasterWorkerPool::CheckForCompletedTasks"); + + RasterWorkerPool::CheckForCompletedTasks(); + CheckForCompletedUploads(); + FlushUploads(); + + TaskDeque completed_tasks; + completed_tasks_.swap(completed_tasks); + + while (!completed_tasks.empty()) { + internal::RasterWorkerPoolTask* task = completed_tasks.front().get(); + DCHECK(pixel_buffer_tasks_.find(task) != pixel_buffer_tasks_.end()); + + pixel_buffer_tasks_.erase(task); + + task->WillComplete(); + task->CompleteOnOriginThread(); + task->DidComplete(); + + completed_tasks.pop_front(); + } +} + +void PixelBufferRasterWorkerPool::OnRasterTasksFinished() { + // |should_notify_client_if_no_tasks_are_pending_| can be set to false as + // a result of a scheduled CheckForCompletedRasterTasks() call. No need to + // perform another check in that case as we've already notified the client. + if (!should_notify_client_if_no_tasks_are_pending_) + return; + + // Call CheckForCompletedRasterTasks() when we've finished running all + // raster tasks needed since last time ScheduleTasks() was called. + // This reduces latency between the time when all tasks have finished + // running and the time when the client is notified. + CheckForCompletedRasterTasks(); +} + +void PixelBufferRasterWorkerPool::OnRasterTasksRequiredForActivationFinished() { + // Analogous to OnRasterTasksFinished(), there's no need to call + // CheckForCompletedRasterTasks() if the client has already been notified. + if (!should_notify_client_if_no_tasks_required_for_activation_are_pending_) + return; + + // This reduces latency between the time when all tasks required for + // activation have finished running and the time when the client is + // notified. + CheckForCompletedRasterTasks(); +} + +void PixelBufferRasterWorkerPool::FlushUploads() { + if (!has_performed_uploads_since_last_flush_) + return; + + resource_provider()->ShallowFlushIfSupported(); + has_performed_uploads_since_last_flush_ = false; +} + +void PixelBufferRasterWorkerPool::CheckForCompletedUploads() { + TaskDeque tasks_with_completed_uploads; + + // First check if any have completed. + while (!tasks_with_pending_upload_.empty()) { + internal::RasterWorkerPoolTask* task = + tasks_with_pending_upload_.front().get(); + + // Uploads complete in the order they are issued. + if (!resource_provider()->DidSetPixelsComplete(task->resource()->id())) + break; + + tasks_with_completed_uploads.push_back(task); + tasks_with_pending_upload_.pop_front(); + } + + DCHECK(client()); + bool should_force_some_uploads_to_complete = + shutdown_ || client()->ShouldForceTasksRequiredForActivationToComplete(); + + if (should_force_some_uploads_to_complete) { + TaskDeque tasks_with_uploads_to_force; + TaskDeque::iterator it = tasks_with_pending_upload_.begin(); + while (it != tasks_with_pending_upload_.end()) { + internal::RasterWorkerPoolTask* task = it->get(); + DCHECK(pixel_buffer_tasks_.find(task) != pixel_buffer_tasks_.end()); + + // Force all uploads required for activation to complete. + // During shutdown, force all pending uploads to complete. + if (shutdown_ || IsRasterTaskRequiredForActivation(task)) { + tasks_with_uploads_to_force.push_back(task); + tasks_with_completed_uploads.push_back(task); + it = tasks_with_pending_upload_.erase(it); + continue; + } + + ++it; + } + + // Force uploads in reverse order. Since forcing can cause a wait on + // all previous uploads, we would rather wait only once downstream. + for (TaskDeque::reverse_iterator it = tasks_with_uploads_to_force.rbegin(); + it != tasks_with_uploads_to_force.rend(); + ++it) { + resource_provider()->ForceSetPixelsToComplete((*it)->resource()->id()); + has_performed_uploads_since_last_flush_ = true; + } + } + + // Release shared memory and move tasks with completed uploads + // to |completed_tasks_|. + while (!tasks_with_completed_uploads.empty()) { + internal::RasterWorkerPoolTask* task = + tasks_with_completed_uploads.front().get(); + + // It's now safe to release the pixel buffer and the shared memory. + resource_provider()->ReleasePixelBuffer(task->resource()->id()); + + bytes_pending_upload_ -= task->resource()->bytes(); + + task->DidRun(false); + + DCHECK(std::find(completed_tasks_.begin(), + completed_tasks_.end(), + task) == completed_tasks_.end()); + completed_tasks_.push_back(task); + + tasks_required_for_activation_.erase(task); + + tasks_with_completed_uploads.pop_front(); + } +} + +void PixelBufferRasterWorkerPool::ScheduleCheckForCompletedRasterTasks() { + if (check_for_completed_raster_tasks_pending_) + return; + + check_for_completed_raster_tasks_callback_.Reset( + base::Bind(&PixelBufferRasterWorkerPool::CheckForCompletedRasterTasks, + base::Unretained(this))); + base::MessageLoopProxy::current()->PostDelayedTask( + FROM_HERE, + check_for_completed_raster_tasks_callback_.callback(), + base::TimeDelta::FromMilliseconds(kCheckForCompletedRasterTasksDelayMs)); + check_for_completed_raster_tasks_pending_ = true; +} + +void PixelBufferRasterWorkerPool::CheckForCompletedRasterTasks() { + TRACE_EVENT0( + "cc", "PixelBufferRasterWorkerPool::CheckForCompletedRasterTasks"); + + DCHECK(should_notify_client_if_no_tasks_are_pending_); + + check_for_completed_raster_tasks_callback_.Cancel(); + check_for_completed_raster_tasks_pending_ = false; + + RasterWorkerPool::CheckForCompletedTasks(); + CheckForCompletedUploads(); + FlushUploads(); + + // Determine what client notifications to generate. + bool will_notify_client_that_no_tasks_required_for_activation_are_pending = + (should_notify_client_if_no_tasks_required_for_activation_are_pending_ && + !HasPendingTasksRequiredForActivation()); + bool will_notify_client_that_no_tasks_are_pending = + (should_notify_client_if_no_tasks_are_pending_ && + !HasPendingTasks()); + + // Adjust the need to generate notifications before scheduling more tasks. + should_notify_client_if_no_tasks_required_for_activation_are_pending_ &= + !will_notify_client_that_no_tasks_required_for_activation_are_pending; + should_notify_client_if_no_tasks_are_pending_ &= + !will_notify_client_that_no_tasks_are_pending; + + scheduled_raster_task_count_ = 0; + if (PendingRasterTaskCount()) + ScheduleMoreTasks(); + + TRACE_EVENT_ASYNC_STEP1( + "cc", "ScheduledTasks", this, StateName(), + "state", TracedValue::FromValue(StateAsValue().release())); + + // Schedule another check for completed raster tasks while there are + // pending raster tasks or pending uploads. + if (HasPendingTasks()) + ScheduleCheckForCompletedRasterTasks(); + + // Generate client notifications. + if (will_notify_client_that_no_tasks_required_for_activation_are_pending) { + DCHECK(std::find_if(raster_tasks_required_for_activation().begin(), + raster_tasks_required_for_activation().end(), + WasCanceled) == + raster_tasks_required_for_activation().end()); + client()->DidFinishRunningTasksRequiredForActivation(); + } + if (will_notify_client_that_no_tasks_are_pending) { + TRACE_EVENT_ASYNC_END0("cc", "ScheduledTasks", this); + DCHECK(!HasPendingTasksRequiredForActivation()); + client()->DidFinishRunningTasks(); + } +} + +void PixelBufferRasterWorkerPool::ScheduleMoreTasks() { + TRACE_EVENT0("cc", "PixelBufferRasterWorkerPool::ScheduleMoreTasks"); + + enum RasterTaskType { + PREPAINT_TYPE = 0, + REQUIRED_FOR_ACTIVATION_TYPE = 1, + NUM_TYPES = 2 + }; + NodeVector tasks[NUM_TYPES]; + unsigned priority = 2u; // 0-1 reserved for RasterFinished tasks. + TaskGraph graph; + + size_t bytes_pending_upload = bytes_pending_upload_; + + for (RasterTaskVector::const_iterator it = raster_tasks().begin(); + it != raster_tasks().end(); ++it) { + internal::RasterWorkerPoolTask* task = it->get(); + + // |pixel_buffer_tasks_| contains all tasks that have not yet completed. + TaskMap::iterator pixel_buffer_it = pixel_buffer_tasks_.find(task); + if (pixel_buffer_it == pixel_buffer_tasks_.end()) + continue; + + // HasFinishedRunning() will return true when set pixels has completed. + if (task->HasFinishedRunning()) { + DCHECK(std::find(completed_tasks_.begin(), + completed_tasks_.end(), + task) != completed_tasks_.end()); + continue; + } + + // All raster tasks need to be throttled by bytes of pending uploads. + size_t new_bytes_pending_upload = bytes_pending_upload; + new_bytes_pending_upload += task->resource()->bytes(); + if (new_bytes_pending_upload > kMaxPendingUploadBytes) + break; + + internal::WorkerPoolTask* pixel_buffer_task = pixel_buffer_it->second.get(); + + // If raster has finished, just update |bytes_pending_upload|. + if (pixel_buffer_task && pixel_buffer_task->HasCompleted()) { + bytes_pending_upload = new_bytes_pending_upload; + continue; + } + + // Throttle raster tasks based on kMaxScheduledRasterTasks. + size_t scheduled_raster_task_count = + tasks[PREPAINT_TYPE].container().size() + + tasks[REQUIRED_FOR_ACTIVATION_TYPE].container().size(); + if (scheduled_raster_task_count >= kMaxScheduledRasterTasks) + break; + + // Update |bytes_pending_upload| now that task has cleared all + // throttling limits. + bytes_pending_upload = new_bytes_pending_upload; + + RasterTaskType type = IsRasterTaskRequiredForActivation(task) ? + REQUIRED_FOR_ACTIVATION_TYPE : + PREPAINT_TYPE; + + // Use existing pixel buffer task if available. + if (pixel_buffer_task) { + tasks[type].container().push_back( + CreateGraphNodeForRasterTask(pixel_buffer_task, + task->dependencies(), + priority++, + &graph)); + continue; + } + + // Request a pixel buffer. This will reserve shared memory. + resource_provider()->AcquirePixelBuffer(task->resource()->id()); + + // MapPixelBuffer() returns NULL if context was lost at the time + // AcquirePixelBuffer() was called. For simplicity we still post + // a raster task that is essentially a noop in these situations. + uint8* buffer = resource_provider()->MapPixelBuffer( + task->resource()->id()); + + scoped_refptr<internal::WorkerPoolTask> new_pixel_buffer_task( + new PixelBufferWorkerPoolTaskImpl( + task, + buffer, + base::Bind(&PixelBufferRasterWorkerPool::OnRasterTaskCompleted, + base::Unretained(this), + make_scoped_refptr(task)))); + pixel_buffer_tasks_[task] = new_pixel_buffer_task; + tasks[type].container().push_back( + CreateGraphNodeForRasterTask(new_pixel_buffer_task.get(), + task->dependencies(), + priority++, + &graph)); + } + + scoped_refptr<internal::WorkerPoolTask> + new_raster_required_for_activation_finished_task; + + size_t scheduled_raster_task_required_for_activation_count = + tasks[REQUIRED_FOR_ACTIVATION_TYPE].container().size(); + DCHECK_LE(scheduled_raster_task_required_for_activation_count, + tasks_required_for_activation_.size()); + // Schedule OnRasterTasksRequiredForActivationFinished call only when + // notification is pending and throttling is not preventing all pending + // tasks required for activation from being scheduled. + if (scheduled_raster_task_required_for_activation_count == + tasks_required_for_activation_.size() && + should_notify_client_if_no_tasks_required_for_activation_are_pending_) { + new_raster_required_for_activation_finished_task = + CreateRasterRequiredForActivationFinishedTask(); + internal::GraphNode* raster_required_for_activation_finished_node = + CreateGraphNodeForTask( + new_raster_required_for_activation_finished_task.get(), + 0u, // Priority 0 + &graph); + AddDependenciesToGraphNode( + raster_required_for_activation_finished_node, + tasks[REQUIRED_FOR_ACTIVATION_TYPE].container()); + } + + scoped_refptr<internal::WorkerPoolTask> new_raster_finished_task; + + size_t scheduled_raster_task_count = + tasks[PREPAINT_TYPE].container().size() + + tasks[REQUIRED_FOR_ACTIVATION_TYPE].container().size(); + DCHECK_LE(scheduled_raster_task_count, PendingRasterTaskCount()); + // Schedule OnRasterTasksFinished call only when notification is pending + // and throttling is not preventing all pending tasks from being scheduled. + if (scheduled_raster_task_count == PendingRasterTaskCount() && + should_notify_client_if_no_tasks_are_pending_) { + new_raster_finished_task = CreateRasterFinishedTask(); + internal::GraphNode* raster_finished_node = + CreateGraphNodeForTask(new_raster_finished_task.get(), + 1u, // Priority 1 + &graph); + for (unsigned type = 0; type < NUM_TYPES; ++type) { + AddDependenciesToGraphNode( + raster_finished_node, + tasks[type].container()); + } + } + + SetTaskGraph(&graph); + + scheduled_raster_task_count_ = scheduled_raster_task_count; + + set_raster_finished_task(new_raster_finished_task); + set_raster_required_for_activation_finished_task( + new_raster_required_for_activation_finished_task); +} + +void PixelBufferRasterWorkerPool::OnRasterTaskCompleted( + scoped_refptr<internal::RasterWorkerPoolTask> task, + bool was_canceled, + bool needs_upload) { + DCHECK(pixel_buffer_tasks_.find(task.get()) != pixel_buffer_tasks_.end()); + + // Balanced with MapPixelBuffer() call in ScheduleMoreTasks(). + resource_provider()->UnmapPixelBuffer(task->resource()->id()); + + if (!needs_upload) { + resource_provider()->ReleasePixelBuffer(task->resource()->id()); + + if (was_canceled) { + // When priorites change, a raster task can be canceled as a result of + // no longer being of high enough priority to fit in our throttled + // raster task budget. The task has not yet completed in this case. + RasterTaskVector::const_iterator it = std::find(raster_tasks().begin(), + raster_tasks().end(), + task); + if (it != raster_tasks().end()) { + pixel_buffer_tasks_[task.get()] = NULL; + return; + } + } + + task->DidRun(was_canceled); + DCHECK(std::find(completed_tasks_.begin(), + completed_tasks_.end(), + task) == completed_tasks_.end()); + completed_tasks_.push_back(task); + tasks_required_for_activation_.erase(task); + return; + } + + DCHECK(!was_canceled); + + resource_provider()->BeginSetPixels(task->resource()->id()); + has_performed_uploads_since_last_flush_ = true; + + bytes_pending_upload_ += task->resource()->bytes(); + tasks_with_pending_upload_.push_back(task); +} + +unsigned PixelBufferRasterWorkerPool::PendingRasterTaskCount() const { + unsigned num_completed_raster_tasks = + tasks_with_pending_upload_.size() + completed_tasks_.size(); + DCHECK_GE(pixel_buffer_tasks_.size(), num_completed_raster_tasks); + return pixel_buffer_tasks_.size() - num_completed_raster_tasks; +} + +bool PixelBufferRasterWorkerPool::HasPendingTasks() const { + return PendingRasterTaskCount() || !tasks_with_pending_upload_.empty(); +} + +bool PixelBufferRasterWorkerPool::HasPendingTasksRequiredForActivation() const { + return !tasks_required_for_activation_.empty(); +} + +const char* PixelBufferRasterWorkerPool::StateName() const { + if (scheduled_raster_task_count_) + return "rasterizing"; + if (PendingRasterTaskCount()) + return "throttled"; + if (!tasks_with_pending_upload_.empty()) + return "waiting_for_uploads"; + + return "finishing"; +} + +scoped_ptr<base::Value> PixelBufferRasterWorkerPool::StateAsValue() const { + scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue); + + state->SetInteger("completed_count", completed_tasks_.size()); + state->SetInteger("pending_count", pixel_buffer_tasks_.size()); + state->SetInteger("pending_upload_count", tasks_with_pending_upload_.size()); + state->SetInteger("required_for_activation_count", + tasks_required_for_activation_.size()); + state->Set("scheduled_state", ScheduledStateAsValue().release()); + state->Set("throttle_state", ThrottleStateAsValue().release()); + return state.PassAs<base::Value>(); +} + +scoped_ptr<base::Value> PixelBufferRasterWorkerPool::ThrottleStateAsValue() + const { + scoped_ptr<base::DictionaryValue> throttle_state(new base::DictionaryValue); + + throttle_state->SetInteger("bytes_available_for_upload", + kMaxPendingUploadBytes - bytes_pending_upload_); + throttle_state->SetInteger("bytes_pending_upload", bytes_pending_upload_); + throttle_state->SetInteger("scheduled_raster_task_count", + scheduled_raster_task_count_); + return throttle_state.PassAs<base::Value>(); +} + +} // namespace cc |