| 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
|
|
|