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