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
3 // found in the LICENSE file.
4
5 #include "cc/tiles/image_controller.h"
6
7 #include "base/bind.h"
8 #include "base/task/post_task.h"
9 #include "base/task/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 {
16
17 ImageController::ImageDecodeRequestId
18 ImageController::s_next_image_decode_queue_id_ = 1;
19
ImageController(base::SequencedTaskRunner * origin_task_runner,scoped_refptr<base::SequencedTaskRunner> worker_task_runner)20 ImageController::ImageController(
21 base::SequencedTaskRunner* origin_task_runner,
22 scoped_refptr<base::SequencedTaskRunner> worker_task_runner)
23 : worker_task_runner_(std::move(worker_task_runner)),
24 origin_task_runner_(origin_task_runner) {
25 weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
26 }
27
~ImageController()28 ImageController::~ImageController() {
29 StopWorkerTasks();
30 for (auto& request : orphaned_decode_requests_)
31 std::move(request.callback).Run(request.id, ImageDecodeResult::FAILURE);
32 }
33
StopWorkerTasks()34 void ImageController::StopWorkerTasks() {
35 // We can't have worker threads without a cache_ or a worker_task_runner_, so
36 // terminate early.
37 if (!cache_ || !worker_task_runner_)
38 return;
39
40 // Abort all tasks that are currently scheduled to run (we'll wait for them to
41 // finish next).
42 {
43 base::AutoLock hold(lock_);
44 abort_tasks_ = true;
45 }
46
47 // Post a task that will simply signal a completion event to ensure that we
48 // "flush" any scheduled tasks (they will abort).
49 CompletionEvent completion_event;
50 worker_task_runner_->PostTask(
51 FROM_HERE, base::BindOnce([](CompletionEvent* event) { event->Signal(); },
52 base::Unretained(&completion_event)));
53 completion_event.Wait();
54
55 // Reset the abort flag so that new tasks can be scheduled.
56 {
57 base::AutoLock hold(lock_);
58 abort_tasks_ = false;
59 }
60
61 // Now that we flushed everything, if there was a task running and it
62 // finished, it would have posted a completion callback back to the compositor
63 // thread. We don't want that, so invalidate the weak ptrs again. Note that
64 // nothing can start running between wait and this invalidate, since it would
65 // only run on the current (compositor) thread.
66 weak_ptr_factory_.InvalidateWeakPtrs();
67 weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
68
69 // Now, begin cleanup.
70
71 // Unlock all of the locked images (note that this vector would only be
72 // populated if we actually need to unref the image.
73 for (auto& image_pair : requested_locked_images_)
74 cache_->UnrefImage(image_pair.second);
75 requested_locked_images_.clear();
76
77 // Now, complete the tasks that already ran but haven't completed. These would
78 // be posted in the run loop, but since we invalidated the weak ptrs, we need
79 // to run everything manually.
80 for (auto& request_to_complete : requests_needing_completion_) {
81 ImageDecodeRequest& request = request_to_complete.second;
82
83 // The task (if one exists) would have run already, we just need to make
84 // sure it was completed. Multiple requests for the same image use the same
85 // task so it could have already been completed.
86 if (request.task && !request.task->HasCompleted()) {
87 request.task->OnTaskCompleted();
88 request.task->DidComplete();
89 }
90
91 if (request.need_unref)
92 cache_->UnrefImage(request.draw_image);
93
94 // Orphan the request so that we can still run it when a new cache is set.
95 request.task = nullptr;
96 request.need_unref = false;
97 orphaned_decode_requests_.push_back(std::move(request));
98 }
99 requests_needing_completion_.clear();
100
101 // Finally, complete all of the tasks that never started running. This is
102 // similar to the |requests_needing_completion_|, but happens at a different
103 // stage in the pipeline.
104 for (auto& request_pair : image_decode_queue_) {
105 ImageDecodeRequest& request = request_pair.second;
106
107 if (request.task) {
108 // This task may have run via a different request, so only cancel it if
109 // it's "new". That is, the same task could have been referenced by
110 // several different image deque requests for the same image.
111 if (request.task->state().IsNew())
112 request.task->state().DidCancel();
113
114 if (!request.task->HasCompleted()) {
115 request.task->OnTaskCompleted();
116 request.task->DidComplete();
117 }
118 }
119
120 if (request.need_unref)
121 cache_->UnrefImage(request.draw_image);
122
123 // Orphan the request so that we can still run it when a new cache is set.
124 request.task = nullptr;
125 request.need_unref = false;
126 orphaned_decode_requests_.push_back(std::move(request));
127 }
128 image_decode_queue_.clear();
129 }
130
SetImageDecodeCache(ImageDecodeCache * cache)131 void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) {
132 DCHECK(!cache_ || !cache);
133
134 if (!cache) {
135 SetPredecodeImages(std::vector<DrawImage>(),
136 ImageDecodeCache::TracingInfo());
137 StopWorkerTasks();
138 image_cache_max_limit_bytes_ = 0u;
139 }
140
141 cache_ = cache;
142
143 if (cache_) {
144 image_cache_max_limit_bytes_ = cache_->GetMaximumMemoryLimitBytes();
145 GenerateTasksForOrphanedRequests();
146 }
147 }
148
ConvertImagesToTasks(std::vector<DrawImage> * sync_decoded_images,std::vector<scoped_refptr<TileTask>> * tasks,bool * has_at_raster_images,bool * has_hardware_accelerated_jpeg_candidates,bool * has_hardware_accelerated_webp_candidates,const ImageDecodeCache::TracingInfo & tracing_info)149 void ImageController::ConvertImagesToTasks(
150 std::vector<DrawImage>* sync_decoded_images,
151 std::vector<scoped_refptr<TileTask>>* tasks,
152 bool* has_at_raster_images,
153 bool* has_hardware_accelerated_jpeg_candidates,
154 bool* has_hardware_accelerated_webp_candidates,
155 const ImageDecodeCache::TracingInfo& tracing_info) {
156 DCHECK(cache_);
157 *has_at_raster_images = false;
158 *has_hardware_accelerated_jpeg_candidates = false;
159 *has_hardware_accelerated_webp_candidates = false;
160 for (auto it = sync_decoded_images->begin();
161 it != sync_decoded_images->end();) {
162 // PaintWorklet images should not be included in this set; they have already
163 // been painted before raster and so do not need raster-time work.
164 DCHECK(!it->paint_image().IsPaintWorklet());
165
166 ImageDecodeCache::TaskResult result =
167 cache_->GetTaskForImageAndRef(*it, tracing_info);
168 *has_at_raster_images |= result.is_at_raster_decode;
169
170 ImageType image_type =
171 it->paint_image().GetImageHeaderMetadata()
172 ? it->paint_image().GetImageHeaderMetadata()->image_type
173 : ImageType::kInvalid;
174 *has_hardware_accelerated_jpeg_candidates |=
175 (result.can_do_hardware_accelerated_decode &&
176 image_type == ImageType::kJPEG);
177 *has_hardware_accelerated_webp_candidates |=
178 (result.can_do_hardware_accelerated_decode &&
179 image_type == ImageType::kWEBP);
180
181 if (result.task)
182 tasks->push_back(std::move(result.task));
183 if (result.need_unref)
184 ++it;
185 else
186 it = sync_decoded_images->erase(it);
187 }
188 }
189
UnrefImages(const std::vector<DrawImage> & images)190 void ImageController::UnrefImages(const std::vector<DrawImage>& images) {
191 for (auto& image : images)
192 cache_->UnrefImage(image);
193 }
194
ReduceMemoryUsage()195 void ImageController::ReduceMemoryUsage() {
196 DCHECK(cache_);
197 cache_->ReduceCacheUsage();
198 }
199
SetPredecodeImages(std::vector<DrawImage> images,const ImageDecodeCache::TracingInfo & tracing_info)200 std::vector<scoped_refptr<TileTask>> ImageController::SetPredecodeImages(
201 std::vector<DrawImage> images,
202 const ImageDecodeCache::TracingInfo& tracing_info) {
203 std::vector<scoped_refptr<TileTask>> new_tasks;
204 // The images here are in a pre-decode area: we decode them in advance, but
205 // they're not dependencies for raster tasks. If these images do end up
206 // getting rasterized, we will still have a chance to record the raster
207 // scheduling delay UMAs when we create and run the raster task.
208 bool has_at_raster_images = false;
209 bool has_hardware_accelerated_jpeg_candidates = false;
210 bool has_hardware_accelerated_webp_candidates = false;
211 ConvertImagesToTasks(&images, &new_tasks, &has_at_raster_images,
212 &has_hardware_accelerated_jpeg_candidates,
213 &has_hardware_accelerated_webp_candidates, tracing_info);
214 UnrefImages(predecode_locked_images_);
215 predecode_locked_images_ = std::move(images);
216 return new_tasks;
217 }
218
QueueImageDecode(const DrawImage & draw_image,ImageDecodedCallback callback)219 ImageController::ImageDecodeRequestId ImageController::QueueImageDecode(
220 const DrawImage& draw_image,
221 ImageDecodedCallback callback) {
222 // We must not receive any image requests if we have no worker.
223 CHECK(worker_task_runner_);
224
225 // Generate the next id.
226 ImageDecodeRequestId id = s_next_image_decode_queue_id_++;
227
228 DCHECK(draw_image.paint_image());
229 bool is_image_lazy = draw_image.paint_image().IsLazyGenerated();
230
231 // Get the tasks for this decode.
232 ImageDecodeCache::TaskResult result(
233 /*need_unref=*/false,
234 /*is_at_raster_decode=*/false,
235 /*can_do_hardware_accelerated_decode=*/false);
236 if (is_image_lazy)
237 result = cache_->GetOutOfRasterDecodeTaskForImageAndRef(draw_image);
238 // If we don't need to unref this, we don't actually have a task.
239 DCHECK(result.need_unref || !result.task);
240
241 // Schedule the task and signal that there is more work.
242 base::AutoLock hold(lock_);
243 image_decode_queue_[id] =
244 ImageDecodeRequest(id, draw_image, std::move(callback),
245 std::move(result.task), result.need_unref);
246
247 // If this is the only image decode request, schedule a task to run.
248 // Otherwise, the task will be scheduled in the previou task's completion.
249 if (image_decode_queue_.size() == 1) {
250 // Post a worker task.
251 worker_task_runner_->PostTask(
252 FROM_HERE,
253 base::BindOnce(&ImageController::ProcessNextImageDecodeOnWorkerThread,
254 base::Unretained(this)));
255 }
256
257 return id;
258 }
259
UnlockImageDecode(ImageDecodeRequestId id)260 void ImageController::UnlockImageDecode(ImageDecodeRequestId id) {
261 // If the image exists, ie we actually need to unlock it, then do so.
262 auto it = requested_locked_images_.find(id);
263 if (it == requested_locked_images_.end())
264 return;
265
266 UnrefImages({std::move(it->second)});
267 requested_locked_images_.erase(it);
268 }
269
ProcessNextImageDecodeOnWorkerThread()270 void ImageController::ProcessNextImageDecodeOnWorkerThread() {
271 TRACE_EVENT0("cc", "ImageController::ProcessNextImageDecodeOnWorkerThread");
272 scoped_refptr<TileTask> decode_task;
273 ImageDecodeRequestId decode_id;
274 {
275 base::AutoLock hold(lock_);
276
277 // If we don't have any work, abort.
278 if (image_decode_queue_.empty() || abort_tasks_)
279 return;
280
281 // Take the next request from the queue.
282 auto decode_it = image_decode_queue_.begin();
283 DCHECK(decode_it != image_decode_queue_.end());
284 decode_task = decode_it->second.task;
285 decode_id = decode_it->second.id;
286
287 // Notify that the task will need completion. Note that there are two cases
288 // where we process this. First, we might complete this task as a response
289 // to the posted task below. Second, we might complete it in
290 // StopWorkerTasks(). In either case, the task would have already run
291 // (either post task happens after running, or the thread was already joined
292 // which means the task ran). This means that we can put the decode into
293 // |requests_needing_completion_| here before actually running the task.
294 requests_needing_completion_[decode_id] = std::move(decode_it->second);
295
296 image_decode_queue_.erase(decode_it);
297 }
298
299 // Run the task if we need to run it. If the task state isn't new, then
300 // there is another task that is responsible for finishing it and cleaning
301 // up (and it already ran); we just need to post a completion callback.
302 // Note that the other tasks's completion will also run first, since the
303 // requests are ordered. So, when we process this task's completion, we
304 // won't actually do anything with the task and simply issue the callback.
305 if (decode_task && decode_task->state().IsNew()) {
306 decode_task->state().DidSchedule();
307 decode_task->state().DidStart();
308 decode_task->RunOnWorkerThread();
309 decode_task->state().DidFinish();
310 }
311 origin_task_runner_->PostTask(
312 FROM_HERE, base::BindOnce(&ImageController::ImageDecodeCompleted,
313 weak_ptr_, decode_id));
314 }
315
ImageDecodeCompleted(ImageDecodeRequestId id)316 void ImageController::ImageDecodeCompleted(ImageDecodeRequestId id) {
317 ImageDecodedCallback callback;
318 ImageDecodeResult result = ImageDecodeResult::SUCCESS;
319 {
320 base::AutoLock hold(lock_);
321
322 auto request_it = requests_needing_completion_.find(id);
323 DCHECK(request_it != requests_needing_completion_.end());
324 id = request_it->first;
325 ImageDecodeRequest& request = request_it->second;
326
327 // First, Determine the status of the decode. This has to happen here, since
328 // we conditionally move from the draw image below.
329 // Also note that if we don't need an unref for a lazy decoded images, it
330 // implies that we never attempted the decode. Some of the reasons for this
331 // would be that the image is of an empty size, or if the image doesn't fit
332 // into memory. In all cases, this implies that the decode was a failure.
333 if (!request.draw_image.paint_image().IsLazyGenerated())
334 result = ImageDecodeResult::DECODE_NOT_REQUIRED;
335 else if (!request.need_unref)
336 result = ImageDecodeResult::FAILURE;
337 else
338 result = ImageDecodeResult::SUCCESS;
339
340 // If we need to unref this decode, then we have to put it into the locked
341 // images vector.
342 if (request.need_unref)
343 requested_locked_images_[id] = std::move(request.draw_image);
344
345 // If we have a task that isn't completed yet, we need to complete it.
346 if (request.task && !request.task->HasCompleted()) {
347 request.task->OnTaskCompleted();
348 request.task->DidComplete();
349 }
350
351 // Finally, save the callback so we can run it without the lock, and erase
352 // the request from |requests_needing_completion_|.
353 callback = std::move(request.callback);
354 requests_needing_completion_.erase(request_it);
355 }
356
357 // Post another task to run.
358 worker_task_runner_->PostTask(
359 FROM_HERE,
360 base::BindOnce(&ImageController::ProcessNextImageDecodeOnWorkerThread,
361 base::Unretained(this)));
362
363 // Finally run the requested callback.
364 std::move(callback).Run(id, result);
365 }
366
GenerateTasksForOrphanedRequests()367 void ImageController::GenerateTasksForOrphanedRequests() {
368 base::AutoLock hold(lock_);
369 DCHECK_EQ(0u, image_decode_queue_.size());
370 DCHECK_EQ(0u, requests_needing_completion_.size());
371 DCHECK(cache_);
372
373 for (auto& request : orphaned_decode_requests_) {
374 DCHECK(!request.task);
375 DCHECK(!request.need_unref);
376 if (request.draw_image.paint_image().IsLazyGenerated()) {
377 // Get the task for this decode.
378 ImageDecodeCache::TaskResult result =
379 cache_->GetOutOfRasterDecodeTaskForImageAndRef(request.draw_image);
380 request.need_unref = result.need_unref;
381 request.task = result.task;
382 }
383 image_decode_queue_[request.id] = std::move(request);
384 }
385
386 orphaned_decode_requests_.clear();
387 if (!image_decode_queue_.empty()) {
388 // Post a worker task.
389 worker_task_runner_->PostTask(
390 FROM_HERE,
391 base::BindOnce(&ImageController::ProcessNextImageDecodeOnWorkerThread,
392 base::Unretained(this)));
393 }
394 }
395
396 ImageController::ImageDecodeRequest::ImageDecodeRequest() = default;
ImageDecodeRequest(ImageDecodeRequestId id,const DrawImage & draw_image,ImageDecodedCallback callback,scoped_refptr<TileTask> task,bool need_unref)397 ImageController::ImageDecodeRequest::ImageDecodeRequest(
398 ImageDecodeRequestId id,
399 const DrawImage& draw_image,
400 ImageDecodedCallback callback,
401 scoped_refptr<TileTask> task,
402 bool need_unref)
403 : id(id),
404 draw_image(draw_image),
405 callback(std::move(callback)),
406 task(std::move(task)),
407 need_unref(need_unref) {}
408 ImageController::ImageDecodeRequest::ImageDecodeRequest(
409 ImageDecodeRequest&& other) = default;
410 ImageController::ImageDecodeRequest::~ImageDecodeRequest() = default;
411
412 ImageController::ImageDecodeRequest& ImageController::ImageDecodeRequest::
413 operator=(ImageDecodeRequest&& other) = default;
414
415 } // namespace cc
416