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 "base/task_scheduler/post_task.h" |
| 9 #include "base/task_scheduler/task_traits.h" |
| 10 #include "base/threading/thread_restrictions.h" |
| 11 #include "base/trace_event/trace_event.h" |
| 12 #include "cc/base/completion_event.h" |
| 13 #include "cc/tiles/tile_task_manager.h" |
| 14 |
7 namespace cc { | 15 namespace cc { |
8 | 16 |
9 ImageController::ImageController() = default; | 17 ImageController::ImageDecodeRequestId |
10 ImageController::~ImageController() = default; | 18 ImageController::s_next_image_decode_queue_id_ = 1; |
| 19 |
| 20 ImageController::ImageController( |
| 21 base::SequencedTaskRunner* origin_task_runner, |
| 22 scoped_refptr<base::SequencedTaskRunner> worker_task_runner) |
| 23 : origin_task_runner_(origin_task_runner), |
| 24 worker_task_runner_(std::move(worker_task_runner)), |
| 25 weak_ptr_factory_(this) {} |
| 26 |
| 27 ImageController::~ImageController() { |
| 28 StopWorkerTasks(); |
| 29 } |
| 30 |
| 31 void ImageController::StopWorkerTasks() { |
| 32 // We can't have worker threads without a cache_ or a worker_task_runner_, so |
| 33 // terminate early. |
| 34 if (!cache_ || !worker_task_runner_) |
| 35 return; |
| 36 |
| 37 // Abort all tasks that are currently scheduled to run (we'll wait for them to |
| 38 // finish next). |
| 39 { |
| 40 base::AutoLock hold(lock_); |
| 41 abort_tasks_ = true; |
| 42 } |
| 43 |
| 44 // Post a task that will simply signal a completion event to ensure that we |
| 45 // "flush" any scheduled tasks (they will abort). |
| 46 CompletionEvent completion_event; |
| 47 worker_task_runner_->PostTask( |
| 48 FROM_HERE, base::Bind([](CompletionEvent* event) { event->Signal(); }, |
| 49 base::Unretained(&completion_event))); |
| 50 completion_event.Wait(); |
| 51 |
| 52 // Reset the abort flag so that new tasks can be scheduled. |
| 53 { |
| 54 base::AutoLock hold(lock_); |
| 55 abort_tasks_ = false; |
| 56 } |
| 57 |
| 58 // Now that we flushed everything, if there was a task running and it |
| 59 // finished, it would have posted a completion callback back to the compositor |
| 60 // thread. We don't want that, so invalidate the weak ptrs again. Note that |
| 61 // nothing can start running between wait and this invalidate, since it would |
| 62 // only run on the current (compositor) thread. |
| 63 weak_ptr_factory_.InvalidateWeakPtrs(); |
| 64 |
| 65 // Now, begin cleanup. |
| 66 |
| 67 // Unlock all of the locked images (note that this vector would only be |
| 68 // populated if we actually need to unref the image. |
| 69 for (auto image_pair : requested_locked_images_) |
| 70 cache_->UnrefImage(image_pair.second); |
| 71 requested_locked_images_.clear(); |
| 72 |
| 73 // Now, complete the tasks that already ran but haven't completed. These would |
| 74 // be posted in the run loop, but since we invalidated the weak ptrs, we need |
| 75 // to run everything manually. |
| 76 for (auto& request_to_complete : requests_needing_completion_) { |
| 77 ImageDecodeRequestId id = request_to_complete.first; |
| 78 ImageDecodeRequest& request = request_to_complete.second; |
| 79 |
| 80 // The task (if one exists) would have run already, so we just need to |
| 81 // complete it. |
| 82 if (request.task) |
| 83 request.task->DidComplete(); |
| 84 |
| 85 // Issue the callback, and unref the image immediately. This is so that any |
| 86 // code waiting on the callback can proceed, although we're breaking the |
| 87 // promise of having this image decoded. This is unfortunate, but it seems |
| 88 // like the least complexity to process an image decode controller becoming |
| 89 // nullptr. |
| 90 request.callback.Run(id); |
| 91 if (request.need_unref) |
| 92 cache_->UnrefImage(request.draw_image); |
| 93 } |
| 94 requests_needing_completion_.clear(); |
| 95 |
| 96 // Finally, complete all of the tasks that never started running. This is |
| 97 // similar to the |requests_needing_completion_|, but happens at a different |
| 98 // stage in the pipeline. |
| 99 for (auto& request_pair : image_decode_queue_) { |
| 100 ImageDecodeRequestId id = request_pair.first; |
| 101 ImageDecodeRequest& request = request_pair.second; |
| 102 |
| 103 if (request.task) { |
| 104 // This task may have run via a different request, so only cancel it if |
| 105 // it's "new". That is, the same task could have been referenced by |
| 106 // several different image deque requests for the same image. |
| 107 if (request.task->state().IsNew()) |
| 108 request.task->state().DidCancel(); |
| 109 request.task->DidComplete(); |
| 110 } |
| 111 // Run the callback and unref the image. |
| 112 request.callback.Run(id); |
| 113 cache_->UnrefImage(request.draw_image); |
| 114 } |
| 115 image_decode_queue_.clear(); |
| 116 } |
11 | 117 |
12 void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) { | 118 void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) { |
13 if (!cache) { | 119 if (!cache) { |
14 SetPredecodeImages(std::vector<DrawImage>(), | 120 SetPredecodeImages(std::vector<DrawImage>(), |
15 ImageDecodeCache::TracingInfo()); | 121 ImageDecodeCache::TracingInfo()); |
| 122 StopWorkerTasks(); |
16 } | 123 } |
17 cache_ = cache; | 124 cache_ = cache; |
18 } | 125 } |
19 | 126 |
20 void ImageController::GetTasksForImagesAndRef( | 127 void ImageController::GetTasksForImagesAndRef( |
21 std::vector<DrawImage>* images, | 128 std::vector<DrawImage>* images, |
22 std::vector<scoped_refptr<TileTask>>* tasks, | 129 std::vector<scoped_refptr<TileTask>>* tasks, |
23 const ImageDecodeCache::TracingInfo& tracing_info) { | 130 const ImageDecodeCache::TracingInfo& tracing_info) { |
24 DCHECK(cache_); | 131 DCHECK(cache_); |
25 for (auto it = images->begin(); it != images->end();) { | 132 for (auto it = images->begin(); it != images->end();) { |
(...skipping 23 matching lines...) Expand all Loading... |
49 std::vector<scoped_refptr<TileTask>> ImageController::SetPredecodeImages( | 156 std::vector<scoped_refptr<TileTask>> ImageController::SetPredecodeImages( |
50 std::vector<DrawImage> images, | 157 std::vector<DrawImage> images, |
51 const ImageDecodeCache::TracingInfo& tracing_info) { | 158 const ImageDecodeCache::TracingInfo& tracing_info) { |
52 std::vector<scoped_refptr<TileTask>> new_tasks; | 159 std::vector<scoped_refptr<TileTask>> new_tasks; |
53 GetTasksForImagesAndRef(&images, &new_tasks, tracing_info); | 160 GetTasksForImagesAndRef(&images, &new_tasks, tracing_info); |
54 UnrefImages(predecode_locked_images_); | 161 UnrefImages(predecode_locked_images_); |
55 predecode_locked_images_ = std::move(images); | 162 predecode_locked_images_ = std::move(images); |
56 return new_tasks; | 163 return new_tasks; |
57 } | 164 } |
58 | 165 |
| 166 ImageController::ImageDecodeRequestId ImageController::QueueImageDecode( |
| 167 sk_sp<const SkImage> image, |
| 168 const ImageDecodedCallback& callback) { |
| 169 // We must not receive any image requests if we have no worker. |
| 170 CHECK(worker_task_runner_); |
| 171 |
| 172 // Generate the next id. |
| 173 ImageDecodeRequestId id = s_next_image_decode_queue_id_++; |
| 174 |
| 175 DCHECK(image); |
| 176 auto image_bounds = image->bounds(); |
| 177 DrawImage draw_image(std::move(image), image_bounds, kNone_SkFilterQuality, |
| 178 SkMatrix::I()); |
| 179 |
| 180 // Get the tasks for this decode. |
| 181 scoped_refptr<TileTask> task; |
| 182 bool need_unref = |
| 183 cache_->GetOutOfRasterDecodeTaskForImageAndRef(draw_image, &task); |
| 184 // If we don't need to unref this, we don't actually have a task. |
| 185 DCHECK(need_unref || !task); |
| 186 |
| 187 // Schedule the task and signal that there is more work. |
| 188 base::AutoLock hold(lock_); |
| 189 image_decode_queue_[id] = |
| 190 ImageDecodeRequest(id, draw_image, callback, std::move(task), need_unref); |
| 191 |
| 192 // If this is the only image decode request, schedule a task to run. |
| 193 // Otherwise, the task will be scheduled in the previou task's completion. |
| 194 if (image_decode_queue_.size() == 1) { |
| 195 // Post a worker task. |
| 196 worker_task_runner_->PostTask( |
| 197 FROM_HERE, |
| 198 base::Bind(&ImageController::ProcessNextImageDecodeOnWorkerThread, |
| 199 base::Unretained(this))); |
| 200 } |
| 201 |
| 202 return id; |
| 203 } |
| 204 |
| 205 void ImageController::UnlockImageDecode(ImageDecodeRequestId id) { |
| 206 // If the image exists, ie we actually need to unlock it, then do so. |
| 207 auto it = requested_locked_images_.find(id); |
| 208 if (it == requested_locked_images_.end()) |
| 209 return; |
| 210 |
| 211 UnrefImages({it->second}); |
| 212 requested_locked_images_.erase(it); |
| 213 } |
| 214 |
| 215 void ImageController::ProcessNextImageDecodeOnWorkerThread() { |
| 216 TRACE_EVENT0("cc", "ImageController::ProcessNextImageDecodeOnWorkerThread"); |
| 217 ImageDecodeRequest decode; |
| 218 { |
| 219 base::AutoLock hold(lock_); |
| 220 |
| 221 // If we don't have any work, abort. |
| 222 if (image_decode_queue_.empty() || abort_tasks_) |
| 223 return; |
| 224 |
| 225 // Take the next request from the queue. |
| 226 auto decode_it = image_decode_queue_.begin(); |
| 227 DCHECK(decode_it != image_decode_queue_.end()); |
| 228 decode = std::move(decode_it->second); |
| 229 image_decode_queue_.erase(decode_it); |
| 230 |
| 231 // Notify that the task will need completion. Note that there are two cases |
| 232 // where we process this. First, we might complete this task as a response |
| 233 // to the posted task below. Second, we might complete it in |
| 234 // StopWorkerTasks(). In either case, the task would have already run |
| 235 // (either post task happens after running, or the thread was already joined |
| 236 // which means the task ran). This means that we can put the decode into |
| 237 // |requests_needing_completion_| here before actually running the task. |
| 238 requests_needing_completion_[decode.id] = decode; |
| 239 } |
| 240 |
| 241 // Run the task if we need to run it. If the task state isn't new, then |
| 242 // there is another task that is responsible for finishing it and cleaning |
| 243 // up (and it already ran); we just need to post a completion callback. |
| 244 // Note that the other tasks's completion will also run first, since the |
| 245 // requests are ordered. So, when we process this task's completion, we |
| 246 // won't actually do anything with the task and simply issue the callback. |
| 247 if (decode.task && decode.task->state().IsNew()) { |
| 248 decode.task->state().DidSchedule(); |
| 249 decode.task->state().DidStart(); |
| 250 decode.task->RunOnWorkerThread(); |
| 251 decode.task->state().DidFinish(); |
| 252 } |
| 253 origin_task_runner_->PostTask( |
| 254 FROM_HERE, base::Bind(&ImageController::ImageDecodeCompleted, |
| 255 weak_ptr_factory_.GetWeakPtr(), decode.id)); |
| 256 } |
| 257 |
| 258 void ImageController::ImageDecodeCompleted(ImageDecodeRequestId id) { |
| 259 ImageDecodedCallback callback; |
| 260 { |
| 261 base::AutoLock hold(lock_); |
| 262 |
| 263 auto request_it = requests_needing_completion_.find(id); |
| 264 DCHECK(request_it != requests_needing_completion_.end()); |
| 265 id = request_it->first; |
| 266 ImageDecodeRequest& request = request_it->second; |
| 267 |
| 268 // If we need to unref this decode, then we have to put it into the locked |
| 269 // images vector. |
| 270 if (request.need_unref) |
| 271 requested_locked_images_[id] = std::move(request.draw_image); |
| 272 |
| 273 // If we have a task that isn't completed yet, we need to complete it. |
| 274 if (request.task && !request.task->HasCompleted()) { |
| 275 request.task->OnTaskCompleted(); |
| 276 request.task->DidComplete(); |
| 277 } |
| 278 // Finally, save the callback so we can run it without the lock, and erase |
| 279 // the request from |requests_needing_completion_|. |
| 280 callback = std::move(request.callback); |
| 281 requests_needing_completion_.erase(request_it); |
| 282 } |
| 283 |
| 284 // Post another task to run. |
| 285 worker_task_runner_->PostTask( |
| 286 FROM_HERE, |
| 287 base::Bind(&ImageController::ProcessNextImageDecodeOnWorkerThread, |
| 288 base::Unretained(this))); |
| 289 |
| 290 // Finally run the requested callback. |
| 291 callback.Run(id); |
| 292 } |
| 293 |
| 294 ImageController::ImageDecodeRequest::ImageDecodeRequest() = default; |
| 295 ImageController::ImageDecodeRequest::ImageDecodeRequest( |
| 296 ImageDecodeRequestId id, |
| 297 const DrawImage& draw_image, |
| 298 const ImageDecodedCallback& callback, |
| 299 scoped_refptr<TileTask> task, |
| 300 bool need_unref) |
| 301 : id(id), |
| 302 draw_image(draw_image), |
| 303 callback(callback), |
| 304 task(std::move(task)), |
| 305 need_unref(need_unref) {} |
| 306 ImageController::ImageDecodeRequest::ImageDecodeRequest( |
| 307 ImageDecodeRequest&& other) = default; |
| 308 ImageController::ImageDecodeRequest::ImageDecodeRequest( |
| 309 const ImageDecodeRequest& other) = default; |
| 310 ImageController::ImageDecodeRequest::~ImageDecodeRequest() = default; |
| 311 |
| 312 ImageController::ImageDecodeRequest& ImageController::ImageDecodeRequest:: |
| 313 operator=(ImageDecodeRequest&& other) = default; |
| 314 ImageController::ImageDecodeRequest& ImageController::ImageDecodeRequest:: |
| 315 operator=(const ImageDecodeRequest& other) = default; |
| 316 |
59 } // namespace cc | 317 } // namespace cc |
OLD | NEW |