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 | |
15 namespace cc { | 7 namespace cc { |
16 | 8 |
17 ImageController::ImageDecodeRequestId | 9 ImageController::ImageController() = default; |
18 ImageController::s_next_image_decode_queue_id_ = 1; | 10 ImageController::~ImageController() = default; |
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 } | |
117 | 11 |
118 void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) { | 12 void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) { |
119 if (!cache) { | 13 if (!cache) { |
120 SetPredecodeImages(std::vector<DrawImage>(), | 14 SetPredecodeImages(std::vector<DrawImage>(), |
121 ImageDecodeCache::TracingInfo()); | 15 ImageDecodeCache::TracingInfo()); |
122 StopWorkerTasks(); | |
123 } | 16 } |
124 cache_ = cache; | 17 cache_ = cache; |
125 } | 18 } |
126 | 19 |
127 void ImageController::GetTasksForImagesAndRef( | 20 void ImageController::GetTasksForImagesAndRef( |
128 std::vector<DrawImage>* images, | 21 std::vector<DrawImage>* images, |
129 std::vector<scoped_refptr<TileTask>>* tasks, | 22 std::vector<scoped_refptr<TileTask>>* tasks, |
130 const ImageDecodeCache::TracingInfo& tracing_info) { | 23 const ImageDecodeCache::TracingInfo& tracing_info) { |
131 DCHECK(cache_); | 24 DCHECK(cache_); |
132 for (auto it = images->begin(); it != images->end();) { | 25 for (auto it = images->begin(); it != images->end();) { |
(...skipping 23 matching lines...) Expand all Loading... |
156 std::vector<scoped_refptr<TileTask>> ImageController::SetPredecodeImages( | 49 std::vector<scoped_refptr<TileTask>> ImageController::SetPredecodeImages( |
157 std::vector<DrawImage> images, | 50 std::vector<DrawImage> images, |
158 const ImageDecodeCache::TracingInfo& tracing_info) { | 51 const ImageDecodeCache::TracingInfo& tracing_info) { |
159 std::vector<scoped_refptr<TileTask>> new_tasks; | 52 std::vector<scoped_refptr<TileTask>> new_tasks; |
160 GetTasksForImagesAndRef(&images, &new_tasks, tracing_info); | 53 GetTasksForImagesAndRef(&images, &new_tasks, tracing_info); |
161 UnrefImages(predecode_locked_images_); | 54 UnrefImages(predecode_locked_images_); |
162 predecode_locked_images_ = std::move(images); | 55 predecode_locked_images_ = std::move(images); |
163 return new_tasks; | 56 return new_tasks; |
164 } | 57 } |
165 | 58 |
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 | |
317 } // namespace cc | 59 } // namespace cc |
OLD | NEW |