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