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