Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(166)

Side by Side Diff: cc/tiles/image_controller.cc

Issue 2537683002: cc: Add image decode queue functionality to image manager. (Closed)
Patch Set: image-decode: update Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « cc/tiles/image_controller.h ('k') | cc/tiles/image_controller_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
OLDNEW
« no previous file with comments | « cc/tiles/image_controller.h ('k') | cc/tiles/image_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698