Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2415)

Unified Diff: cc/tiles/image_controller.cc

Issue 2537683002: cc: Add image decode queue functionality to image manager. (Closed)
Patch Set: image-queue: update Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: cc/tiles/image_controller.cc
diff --git a/cc/tiles/image_controller.cc b/cc/tiles/image_controller.cc
index b0eaa101ebf5f45f2f79d1049bf4735b92021e78..886a00e2983f8b587cfbbc33cce1c931bcf8dab0 100644
--- a/cc/tiles/image_controller.cc
+++ b/cc/tiles/image_controller.cc
@@ -4,10 +4,119 @@
#include "cc/tiles/image_controller.h"
+#include "base/bind.h"
+#include "cc/tiles/tile_task_manager.h"
+
namespace cc {
+namespace {
+class WorkerThread : public base::SimpleThread {
+ public:
+ WorkerThread(const std::string& name_prefix,
+ const Options& options,
+ ImageController* image_manager)
+ : base::SimpleThread(name_prefix, options),
+ image_manager_(image_manager) {}
+
+ void Run() override { image_manager_->ProcessImageDecodes(); }
+
+ private:
+ ImageController* image_manager_ = nullptr;
+};
+
+const int kNumFramesToLock = 2;
+} // namespace
+
+ImageController::ImageDecodeRequestId
+ ImageController::s_next_image_decode_queue_id_ = 1;
+
+ImageController::ImageController(base::SequencedTaskRunner* task_runner)
+ : task_runner_(task_runner),
+ work_available_cv_(&lock_),
+ weak_ptr_factory_(this) {
+ // Worker thread will be started when we get a controller.
+}
-ImageController::ImageController() = default;
-ImageController::~ImageController() = default;
+ImageController::~ImageController() {
+ StopWorkerThread();
+}
+
+void ImageController::StartWorkerThread() {
+ DCHECK(!worker_thread_);
+
+ shutdown_ = false;
+ worker_thread_.reset(new WorkerThread(
+ "ImageDecodeWorker",
+ base::SimpleThread::Options(base::ThreadPriority::BACKGROUND), this));
+ worker_thread_->Start();
+}
+
+void ImageController::StopWorkerThread() {
+ if (shutdown_) {
+ DCHECK(!cache_);
+ DCHECK(!worker_thread_);
+ return;
+ }
+
+ // First thing, join the thread so we don't have any races in the rest of the
+ // function and so we don't need to acquire the lock.
+ shutdown_ = true;
+ work_available_cv_.Signal();
+ worker_thread_->Join();
+ worker_thread_.reset();
+
+ // Invalidate the weak ptrs, so any tasks to complete requests won't run (we
+ // will complete everything in this function).
+ weak_ptr_factory_.InvalidateWeakPtrs();
+
+ // Unlock all of the locked images (note that this vector would only be
+ // populated if we actually need to unref the image.
+ for (auto image_pair : requested_locked_images_)
+ cache_->UnrefImage(image_pair.first);
+ requested_locked_images_.clear();
+
+ // Now, complete the tasks that already ran but haven't completed. These would
+ // be posted in the run loop, but since we invalidated the weak ptrs, we need
+ // to run everything manually.
+ for (auto& request_to_complete : requests_needing_completion_) {
+ ImageDecodeRequestId id = request_to_complete.first;
+ ImageDecodeRequest& request = request_to_complete.second;
+
+ // The task (if one exists) would have run already, so we just need to
+ // complete it.
+ if (request.task)
+ request.task->DidComplete();
+
+ // Issue the callback, and unref the image immediately. This is so that any
+ // code waiting on the callback can proceed, although we're breaking the
+ // promise of having this image decoded. This is unfortunate, but it seems
+ // like the least complexity to process an image decode controller becoming
+ // nullptr.
+ request.callback.Run(id);
+ if (request.need_unref)
+ cache_->UnrefImage(request.draw_image);
+ }
+ requests_needing_completion_.clear();
+
+ // Finally, complete all of the tasks that never started running. This is
+ // similar to the |requests_needing_completion_|, but happens at a different
+ // stage in the pipeline.
+ for (auto& request_pair : image_decode_queue_) {
+ ImageDecodeRequestId id = request_pair.first;
+ ImageDecodeRequest& request = request_pair.second;
+
+ if (request.task) {
+ // This task may have run via a different request, so only cancel it if
+ // it's "new".
+ if (request.task->state().IsNew())
+ request.task->state().DidCancel();
+ request.task->DidComplete();
+ }
+ // Run the callback and unref the image.
+ request.callback.Run(id);
+ cache_->UnrefImage(request.draw_image);
+ }
+ image_decode_queue_.clear();
+}
void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) {
// We can only switch from null to non-null and back.
@@ -18,8 +127,13 @@ void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) {
if (!cache) {
SetPredecodeImages(std::vector<DrawImage>(),
ImageDecodeCache::TracingInfo());
+ StopWorkerThread();
}
cache_ = cache;
+
+ if (cache_)
+ StartWorkerThread();
+
// Debugging information for crbug.com/650234.
++num_times_cache_was_set_;
}
@@ -65,4 +179,145 @@ std::vector<scoped_refptr<TileTask>> ImageController::SetPredecodeImages(
return new_tasks;
}
+ImageController::ImageDecodeRequestId ImageController::QueueImageDecode(
+ sk_sp<const SkImage> image,
+ const ImageDecodedCallback& callback) {
+ // Generate the next id.
+ ImageDecodeRequestId id = s_next_image_decode_queue_id_++;
+
+ DCHECK(image);
+ auto image_bounds = image->bounds();
+ DrawImage draw_image(std::move(image), image_bounds, kNone_SkFilterQuality,
+ SkMatrix::I());
+
+ // Get the tasks for this decode.
+ scoped_refptr<TileTask> task;
+ bool need_unref =
+ cache_->GetOutOfRasterDecodeTaskForImageAndRef(draw_image, &task);
+ // If we don't need to unref this, we don't actually have a task.
+ DCHECK(need_unref || !task);
+
+ // Schedule the task and signal that there is more work.
+ base::AutoLock hold(lock_);
+ image_decode_queue_[id] =
+ ImageDecodeRequest(id, draw_image, callback, std::move(task), need_unref);
+ work_available_cv_.Signal();
+ return id;
+}
+
+void ImageController::NotifyFrameFinished() {
+ // Reduce the locked frame count on all images and unlock them if the count
+ // reaches 0.
+ for (auto it = requested_locked_images_.begin();
+ it != requested_locked_images_.end();) {
+ if (--it->second == 0) {
+ UnrefImages({it->first});
+ it = requested_locked_images_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+void ImageController::ProcessImageDecodes() {
+ base::AutoLock hold(lock_);
+ for (;;) {
+ // Exit on shutdown, so the thread can be joined.
+ if (shutdown_)
+ break;
+
+ // If we don't have any work, wait until we get some.
+ if (image_decode_queue_.empty()) {
+ work_available_cv_.Wait();
+ continue;
+ }
+
+ // Take the next request from the queue.
+ auto decode_it = image_decode_queue_.begin();
+ DCHECK(decode_it != image_decode_queue_.end());
+ ImageDecodeRequest decode = std::move(decode_it->second);
+ image_decode_queue_.erase(decode_it);
+
+ // Notify that the task will need completion. Note that there are two cases
+ // where we process this. First, we might complete this task as a response
+ // to the posted task below. Second, we might complete it in
+ // StopWorkerThread(). In either case, the task would have already run
+ // (either post task happens after running, or the thread was already joined
+ // which means the task ran). This means that we can put the decode into
+ // |requests_needing_completion_| here before actually running the task.
+ requests_needing_completion_[decode.id] = decode;
+
+ {
+ base::AutoUnlock unlock(lock_);
+ // Run the task if we need to run it. If the task state isn't new, then
+ // there is another task that is responsible for finishing it and cleaning
+ // up (and it already ran); we just need to post a completion callback.
+ // Note that the other tasks's completion will also run first, since the
+ // requests are ordered. So, when we process this task's completion, we
+ // won't actually do anything with the task and simply issue the callback.
+ if (decode.task && decode.task->state().IsNew()) {
+ decode.task->state().DidSchedule();
+ decode.task->state().DidStart();
+ decode.task->RunOnWorkerThread();
+ decode.task->state().DidFinish();
+ }
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ImageController::ImageDecodeCompleted,
+ weak_ptr_factory_.GetWeakPtr(), decode.id));
+ }
+ }
+}
+
+void ImageController::ImageDecodeCompleted(ImageDecodeRequestId id) {
+ ImageDecodedCallback callback;
+ {
+ base::AutoLock hold(lock_);
+
+ auto request_it = requests_needing_completion_.find(id);
+ DCHECK(request_it != requests_needing_completion_.end());
+ id = request_it->first;
+ ImageDecodeRequest& request = request_it->second;
+
+ // If we need to urnef this decode, then we have to put it into the locked
+ // images vector.
+ if (request.need_unref) {
+ requested_locked_images_.push_back(
+ std::make_pair(std::move(request.draw_image), kNumFramesToLock));
+ }
+ // If we have a task that isn't completed yet, we need to complete it.
+ if (request.task && !request.task->HasCompleted()) {
+ request.task->OnTaskCompleted();
+ request.task->DidComplete();
+ }
+ // Finally, save the callback so we can run it without the lock, and erase
+ // the request from |requests_needing_completion_|.
+ callback = std::move(request.callback);
+ requests_needing_completion_.erase(request_it);
+ }
+ callback.Run(id);
+}
+
+ImageController::ImageDecodeRequest::ImageDecodeRequest() = default;
+ImageController::ImageDecodeRequest::ImageDecodeRequest(
+ ImageDecodeRequestId id,
+ const DrawImage& draw_image,
+ const ImageDecodedCallback& callback,
+ scoped_refptr<TileTask> task,
+ bool need_unref)
+ : id(id),
+ draw_image(draw_image),
+ callback(callback),
+ task(std::move(task)),
+ need_unref(need_unref) {}
+ImageController::ImageDecodeRequest::ImageDecodeRequest(
+ ImageDecodeRequest&& other) = default;
+ImageController::ImageDecodeRequest::ImageDecodeRequest(
+ const ImageDecodeRequest& other) = default;
+ImageController::ImageDecodeRequest::~ImageDecodeRequest() = default;
+
+ImageController::ImageDecodeRequest& ImageController::ImageDecodeRequest::
+operator=(ImageDecodeRequest&& other) = default;
+ImageController::ImageDecodeRequest& ImageController::ImageDecodeRequest::
+operator=(const ImageDecodeRequest& other) = default;
+
} // namespace cc

Powered by Google App Engine
This is Rietveld 408576698