OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "cc/tiles/image_controller.h" | 5 #include "cc/tiles/image_controller.h" |
6 | 6 |
| 7 #include "base/bind.h" |
| 8 #include "cc/tiles/tile_task_manager.h" |
| 9 |
7 namespace cc { | 10 namespace cc { |
| 11 namespace { |
| 12 class WorkerThread : public base::SimpleThread { |
| 13 public: |
| 14 WorkerThread(const std::string& name_prefix, |
| 15 const Options& options, |
| 16 ImageController* image_manager) |
| 17 : base::SimpleThread(name_prefix, options), |
| 18 image_manager_(image_manager) {} |
8 | 19 |
9 ImageController::ImageController() = default; | 20 void Run() override { image_manager_->ProcessImageDecodes(); } |
10 ImageController::~ImageController() = default; | 21 |
| 22 private: |
| 23 ImageController* image_manager_ = nullptr; |
| 24 }; |
| 25 |
| 26 const int kNumFramesToLock = 2; |
| 27 } // namespace |
| 28 |
| 29 ImageController::ImageDecodeRequestId |
| 30 ImageController::s_next_image_decode_queue_id_ = 1; |
| 31 |
| 32 ImageController::ImageController(base::SequencedTaskRunner* task_runner) |
| 33 : task_runner_(task_runner), |
| 34 work_available_cv_(&lock_), |
| 35 weak_ptr_factory_(this) { |
| 36 // Worker thread will be started when we get a controller. |
| 37 } |
| 38 |
| 39 ImageController::~ImageController() { |
| 40 StopWorkerThread(); |
| 41 } |
| 42 |
| 43 void ImageController::StartWorkerThread() { |
| 44 DCHECK(!worker_thread_); |
| 45 |
| 46 shutdown_ = false; |
| 47 worker_thread_.reset(new WorkerThread( |
| 48 "ImageDecodeWorker", |
| 49 base::SimpleThread::Options(base::ThreadPriority::BACKGROUND), this)); |
| 50 worker_thread_->Start(); |
| 51 } |
| 52 |
| 53 void ImageController::StopWorkerThread() { |
| 54 if (shutdown_) { |
| 55 DCHECK(!cache_); |
| 56 DCHECK(!worker_thread_); |
| 57 return; |
| 58 } |
| 59 |
| 60 // First thing, join the thread so we don't have any races in the rest of the |
| 61 // function and so we don't need to acquire the lock. |
| 62 shutdown_ = true; |
| 63 work_available_cv_.Signal(); |
| 64 worker_thread_->Join(); |
| 65 worker_thread_.reset(); |
| 66 |
| 67 // Invalidate the weak ptrs, so any tasks to complete requests won't run (we |
| 68 // will complete everything in this function). |
| 69 weak_ptr_factory_.InvalidateWeakPtrs(); |
| 70 |
| 71 // Unlock all of the locked images (note that this vector would only be |
| 72 // populated if we actually need to unref the image. |
| 73 for (auto image_pair : requested_locked_images_) |
| 74 cache_->UnrefImage(image_pair.first); |
| 75 requested_locked_images_.clear(); |
| 76 |
| 77 // Now, complete the tasks that already ran but haven't completed. These would |
| 78 // be posted in the run loop, but since we invalidated the weak ptrs, we need |
| 79 // to run everything manually. |
| 80 for (auto& request_to_complete : requests_needing_completion_) { |
| 81 ImageDecodeRequestId id = request_to_complete.first; |
| 82 ImageDecodeRequest& request = request_to_complete.second; |
| 83 |
| 84 // The task (if one exists) would have run already, so we just need to |
| 85 // complete it. |
| 86 if (request.task) |
| 87 request.task->DidComplete(); |
| 88 |
| 89 // Issue the callback, and unref the image immediately. This is so that any |
| 90 // code waiting on the callback can proceed, although we're breaking the |
| 91 // promise of having this image decoded. This is unfortunate, but it seems |
| 92 // like the least complexity to process an image decode controller becoming |
| 93 // nullptr. |
| 94 request.callback.Run(id); |
| 95 if (request.need_unref) |
| 96 cache_->UnrefImage(request.draw_image); |
| 97 } |
| 98 requests_needing_completion_.clear(); |
| 99 |
| 100 // Finally, complete all of the tasks that never started running. This is |
| 101 // similar to the |requests_needing_completion_|, but happens at a different |
| 102 // stage in the pipeline. |
| 103 for (auto& request_pair : image_decode_queue_) { |
| 104 ImageDecodeRequestId id = request_pair.first; |
| 105 ImageDecodeRequest& request = request_pair.second; |
| 106 |
| 107 if (request.task) { |
| 108 // This task may have run via a different request, so only cancel it if |
| 109 // it's "new". |
| 110 if (request.task->state().IsNew()) |
| 111 request.task->state().DidCancel(); |
| 112 request.task->DidComplete(); |
| 113 } |
| 114 // Run the callback and unref the image. |
| 115 request.callback.Run(id); |
| 116 cache_->UnrefImage(request.draw_image); |
| 117 } |
| 118 image_decode_queue_.clear(); |
| 119 } |
11 | 120 |
12 void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) { | 121 void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) { |
13 // We can only switch from null to non-null and back. | 122 // We can only switch from null to non-null and back. |
14 // CHECK to debug crbug.com/650234. | 123 // CHECK to debug crbug.com/650234. |
15 CHECK(cache || cache_); | 124 CHECK(cache || cache_); |
16 CHECK(!cache || !cache_); | 125 CHECK(!cache || !cache_); |
17 | 126 |
18 if (!cache) { | 127 if (!cache) { |
19 SetPredecodeImages(std::vector<DrawImage>(), | 128 SetPredecodeImages(std::vector<DrawImage>(), |
20 ImageDecodeCache::TracingInfo()); | 129 ImageDecodeCache::TracingInfo()); |
| 130 StopWorkerThread(); |
21 } | 131 } |
22 cache_ = cache; | 132 cache_ = cache; |
| 133 |
| 134 if (cache_) |
| 135 StartWorkerThread(); |
| 136 |
23 // Debugging information for crbug.com/650234. | 137 // Debugging information for crbug.com/650234. |
24 ++num_times_cache_was_set_; | 138 ++num_times_cache_was_set_; |
25 } | 139 } |
26 | 140 |
27 void ImageController::GetTasksForImagesAndRef( | 141 void ImageController::GetTasksForImagesAndRef( |
28 std::vector<DrawImage>* images, | 142 std::vector<DrawImage>* images, |
29 std::vector<scoped_refptr<TileTask>>* tasks, | 143 std::vector<scoped_refptr<TileTask>>* tasks, |
30 const ImageDecodeCache::TracingInfo& tracing_info) { | 144 const ImageDecodeCache::TracingInfo& tracing_info) { |
31 DCHECK(cache_); | 145 DCHECK(cache_); |
32 for (auto it = images->begin(); it != images->end();) { | 146 for (auto it = images->begin(); it != images->end();) { |
(...skipping 25 matching lines...) Expand all Loading... |
58 std::vector<scoped_refptr<TileTask>> ImageController::SetPredecodeImages( | 172 std::vector<scoped_refptr<TileTask>> ImageController::SetPredecodeImages( |
59 std::vector<DrawImage> images, | 173 std::vector<DrawImage> images, |
60 const ImageDecodeCache::TracingInfo& tracing_info) { | 174 const ImageDecodeCache::TracingInfo& tracing_info) { |
61 std::vector<scoped_refptr<TileTask>> new_tasks; | 175 std::vector<scoped_refptr<TileTask>> new_tasks; |
62 GetTasksForImagesAndRef(&images, &new_tasks, tracing_info); | 176 GetTasksForImagesAndRef(&images, &new_tasks, tracing_info); |
63 UnrefImages(predecode_locked_images_); | 177 UnrefImages(predecode_locked_images_); |
64 predecode_locked_images_ = std::move(images); | 178 predecode_locked_images_ = std::move(images); |
65 return new_tasks; | 179 return new_tasks; |
66 } | 180 } |
67 | 181 |
| 182 ImageController::ImageDecodeRequestId ImageController::QueueImageDecode( |
| 183 sk_sp<const SkImage> image, |
| 184 const ImageDecodedCallback& callback) { |
| 185 // Generate the next id. |
| 186 ImageDecodeRequestId id = s_next_image_decode_queue_id_++; |
| 187 |
| 188 DCHECK(image); |
| 189 auto image_bounds = image->bounds(); |
| 190 DrawImage draw_image(std::move(image), image_bounds, kNone_SkFilterQuality, |
| 191 SkMatrix::I()); |
| 192 |
| 193 // Get the tasks for this decode. |
| 194 scoped_refptr<TileTask> task; |
| 195 bool need_unref = |
| 196 cache_->GetOutOfRasterDecodeTaskForImageAndRef(draw_image, &task); |
| 197 // If we don't need to unref this, we don't actually have a task. |
| 198 DCHECK(need_unref || !task); |
| 199 |
| 200 // Schedule the task and signal that there is more work. |
| 201 base::AutoLock hold(lock_); |
| 202 image_decode_queue_[id] = |
| 203 ImageDecodeRequest(id, draw_image, callback, std::move(task), need_unref); |
| 204 work_available_cv_.Signal(); |
| 205 return id; |
| 206 } |
| 207 |
| 208 void ImageController::NotifyFrameFinished() { |
| 209 // Reduce the locked frame count on all images and unlock them if the count |
| 210 // reaches 0. |
| 211 for (auto it = requested_locked_images_.begin(); |
| 212 it != requested_locked_images_.end();) { |
| 213 if (--it->second == 0) { |
| 214 UnrefImages({it->first}); |
| 215 it = requested_locked_images_.erase(it); |
| 216 } else { |
| 217 ++it; |
| 218 } |
| 219 } |
| 220 } |
| 221 |
| 222 void ImageController::ProcessImageDecodes() { |
| 223 base::AutoLock hold(lock_); |
| 224 for (;;) { |
| 225 // Exit on shutdown, so the thread can be joined. |
| 226 if (shutdown_) |
| 227 break; |
| 228 |
| 229 // If we don't have any work, wait until we get some. |
| 230 if (image_decode_queue_.empty()) { |
| 231 work_available_cv_.Wait(); |
| 232 continue; |
| 233 } |
| 234 |
| 235 // Take the next request from the queue. |
| 236 auto decode_it = image_decode_queue_.begin(); |
| 237 DCHECK(decode_it != image_decode_queue_.end()); |
| 238 ImageDecodeRequest decode = std::move(decode_it->second); |
| 239 image_decode_queue_.erase(decode_it); |
| 240 |
| 241 // Notify that the task will need completion. Note that there are two cases |
| 242 // where we process this. First, we might complete this task as a response |
| 243 // to the posted task below. Second, we might complete it in |
| 244 // StopWorkerThread(). In either case, the task would have already run |
| 245 // (either post task happens after running, or the thread was already joined |
| 246 // which means the task ran). This means that we can put the decode into |
| 247 // |requests_needing_completion_| here before actually running the task. |
| 248 requests_needing_completion_[decode.id] = decode; |
| 249 |
| 250 { |
| 251 base::AutoUnlock unlock(lock_); |
| 252 // Run the task if we need to run it. If the task state isn't new, then |
| 253 // there is another task that is responsible for finishing it and cleaning |
| 254 // up (and it already ran); we just need to post a completion callback. |
| 255 // Note that the other tasks's completion will also run first, since the |
| 256 // requests are ordered. So, when we process this task's completion, we |
| 257 // won't actually do anything with the task and simply issue the callback. |
| 258 if (decode.task && decode.task->state().IsNew()) { |
| 259 decode.task->state().DidSchedule(); |
| 260 decode.task->state().DidStart(); |
| 261 decode.task->RunOnWorkerThread(); |
| 262 decode.task->state().DidFinish(); |
| 263 } |
| 264 task_runner_->PostTask( |
| 265 FROM_HERE, base::Bind(&ImageController::ImageDecodeCompleted, |
| 266 weak_ptr_factory_.GetWeakPtr(), decode.id)); |
| 267 } |
| 268 } |
| 269 } |
| 270 |
| 271 void ImageController::ImageDecodeCompleted(ImageDecodeRequestId id) { |
| 272 ImageDecodedCallback callback; |
| 273 { |
| 274 base::AutoLock hold(lock_); |
| 275 |
| 276 auto request_it = requests_needing_completion_.find(id); |
| 277 DCHECK(request_it != requests_needing_completion_.end()); |
| 278 id = request_it->first; |
| 279 ImageDecodeRequest& request = request_it->second; |
| 280 |
| 281 // If we need to urnef this decode, then we have to put it into the locked |
| 282 // images vector. |
| 283 if (request.need_unref) { |
| 284 requested_locked_images_.push_back( |
| 285 std::make_pair(std::move(request.draw_image), kNumFramesToLock)); |
| 286 } |
| 287 // If we have a task that isn't completed yet, we need to complete it. |
| 288 if (request.task && !request.task->HasCompleted()) { |
| 289 request.task->OnTaskCompleted(); |
| 290 request.task->DidComplete(); |
| 291 } |
| 292 // Finally, save the callback so we can run it without the lock, and erase |
| 293 // the request from |requests_needing_completion_|. |
| 294 callback = std::move(request.callback); |
| 295 requests_needing_completion_.erase(request_it); |
| 296 } |
| 297 callback.Run(id); |
| 298 } |
| 299 |
| 300 ImageController::ImageDecodeRequest::ImageDecodeRequest() = default; |
| 301 ImageController::ImageDecodeRequest::ImageDecodeRequest( |
| 302 ImageDecodeRequestId id, |
| 303 const DrawImage& draw_image, |
| 304 const ImageDecodedCallback& callback, |
| 305 scoped_refptr<TileTask> task, |
| 306 bool need_unref) |
| 307 : id(id), |
| 308 draw_image(draw_image), |
| 309 callback(callback), |
| 310 task(std::move(task)), |
| 311 need_unref(need_unref) {} |
| 312 ImageController::ImageDecodeRequest::ImageDecodeRequest( |
| 313 ImageDecodeRequest&& other) = default; |
| 314 ImageController::ImageDecodeRequest::ImageDecodeRequest( |
| 315 const ImageDecodeRequest& other) = default; |
| 316 ImageController::ImageDecodeRequest::~ImageDecodeRequest() = default; |
| 317 |
| 318 ImageController::ImageDecodeRequest& ImageController::ImageDecodeRequest:: |
| 319 operator=(ImageDecodeRequest&& other) = default; |
| 320 ImageController::ImageDecodeRequest& ImageController::ImageDecodeRequest:: |
| 321 operator=(const ImageDecodeRequest& other) = default; |
| 322 |
68 } // namespace cc | 323 } // namespace cc |
OLD | NEW |