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.
5 #include "cc/tiles/image_controller.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"
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);
28 ImageController::ImageDecodeRequestId
29 ImageController::s_next_image_decode_queue_id_ = 1;
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());
39 ImageController::~ImageController() {
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_)));
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;
58 void ImageController::StopWorkerTasks() {
59 // We can't have worker threads without a cache_ or a worker_task_runner_, so
61 if (!cache_ || !worker_task_runner_)
64 base::AutoLock hold(worker_state_->lock);
65 worker_state_->abort_task = true;
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();
79 DCHECK_EQ(worker_state_->task_state, WorkerTaskState::kNoTask);
81 // Reset the abort flag so that new tasks can be scheduled.
82 worker_state_->abort_task = false;
84 // Now, begin cleanup.
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();
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;
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();
106 if (request.need_unref)
107 cache_->UnrefImage(request.draw_image);
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));
114 worker_state_->requests_needing_completion.clear();
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;
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();
129 if (!request.task->HasCompleted()) {
130 request.task->OnTaskCompleted();
131 request.task->DidComplete();
135 if (request.need_unref)
136 cache_->UnrefImage(request.draw_image);
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));
143 worker_state_->image_decode_queue.clear();
146 void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) {
147 DCHECK(!cache_ || !cache);
150 SetPredecodeImages(std::vector<DrawImage>(),
151 ImageDecodeCache::TracingInfo());
153 image_cache_max_limit_bytes_ = 0u;
159 image_cache_max_limit_bytes_ = cache_->GetMaximumMemoryLimitBytes();
160 GenerateTasksForOrphanedRequests();
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) {
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());
181 ImageDecodeCache::TaskResult result =
182 cache_->GetTaskForImageAndRef(*it, tracing_info);
183 *has_at_raster_images |= result.is_at_raster_decode;
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);
197 tasks->push_back(std::move(result.task));
198 if (result.need_unref)
201 it = sync_decoded_images->erase(it);
205 void ImageController::UnrefImages(const std::vector<DrawImage>& images) {
206 for (auto& image : images)
207 cache_->UnrefImage(image);
210 void ImageController::ReduceMemoryUsage() {
212 cache_->ReduceCacheUsage();
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);
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_);
240 // Generate the next id.
241 ImageDecodeRequestId id = s_next_image_decode_queue_id_++;
243 DCHECK(draw_image.paint_image());
244 bool is_image_lazy = draw_image.paint_image().IsLazyGenerated();
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);
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);
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();
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())
272 UnrefImages({std::move(it->second)});
273 requested_locked_images_.erase(it);
277 void ImageController::ProcessNextImageDecodeOnWorkerThread(
278 WorkerState* worker_state) {
279 TRACE_EVENT0("cc", "ImageController::ProcessNextImageDecodeOnWorkerThread");
281 base::AutoLock hold(worker_state->lock);
282 DCHECK_EQ(worker_state->task_state, WorkerTaskState::kQueuedTask);
283 worker_state->task_state = WorkerTaskState::kRunningTask;
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;
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;
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);
307 worker_state->image_decode_queue.erase(decode_it);
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();
323 worker_state->origin_task_runner->PostTask(
324 FROM_HERE, base::BindOnce(&ImageController::ImageDecodeCompleted,
325 worker_state->weak_ptr, decode_id));
327 DCHECK_EQ(worker_state->task_state, WorkerTaskState::kRunningTask);
328 worker_state->task_state = WorkerTaskState::kNoTask;
331 void ImageController::ImageDecodeCompleted(ImageDecodeRequestId id) {
332 ImageDecodedCallback callback;
333 ImageDecodeResult result = ImageDecodeResult::SUCCESS;
335 base::AutoLock hold(worker_state_->lock);
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())
341 id = request_it->first;
342 ImageDecodeRequest& request = request_it->second;
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;
355 result = ImageDecodeResult::SUCCESS;
357 // If we need to unref this decode, then we have to put it into the locked
359 if (request.need_unref)
360 requested_locked_images_[id] = std::move(request.draw_image);
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();
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);
373 ScheduleImageDecodeOnWorkerIfNeeded();
376 // Finally run the requested callback.
377 std::move(callback).Run(id, result);
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());
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;
396 worker_state_->image_decode_queue[request.id] = std::move(request);
399 orphaned_decode_requests_.clear();
400 ScheduleImageDecodeOnWorkerIfNeeded();
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(
412 base::BindOnce(&ImageController::ProcessNextImageDecodeOnWorkerThread,
413 base::Unretained(worker_state_.get())));
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,
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;
433 ImageController::ImageDecodeRequest& ImageController::ImageDecodeRequest::
434 operator=(ImageDecodeRequest&& other) = default;