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