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

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

Issue 2537683002: cc: Add image decode queue functionality to image manager. (Closed)
Patch Set: update Created 3 years, 12 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
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 {
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698