[M108 Migration][VD] Avoid pending frame counter becoming negative
[platform/framework/web/chromium-efl.git] / cc / tiles / checker_image_tracker.cc
1 // Copyright 2017 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/checker_image_tracker.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <sstream>
10 #include <string>
11 #include <utility>
12
13 #include "base/bind.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/notreached.h"
16 #include "base/trace_event/trace_event.h"
17
18 namespace cc {
19 namespace {
20 enum class CheckerImagingDecision {
21   kCanChecker,
22
23   // Animation State vetoes.
24   kVetoedAnimatedImage,
25   kVetoedVideoFrame,
26   kVetoedMultipartImage,
27
28   // Load state vetoes.
29   kVetoedPartiallyLoadedImage,
30
31   // Size associated vetoes.
32   kVetoedSmallerThanCheckeringSize,
33   kVetoedLargerThanCacheSize,
34
35   // Vetoed because checkering of images has been disabled.
36   kVetoedForceDisable,
37
38   // Sync was requested by the embedder.
39   kVetoedSyncRequested,
40
41   kCheckerImagingDecisionCount
42 };
43
44 std::string ToString(PaintImage::Id paint_image_id,
45                      CheckerImagingDecision decision) {
46   std::ostringstream str;
47   str << "paint_image_id[" << paint_image_id << "] decision["
48       << static_cast<int>(decision) << "]";
49   return str.str();
50 }
51
52 CheckerImagingDecision GetAnimationDecision(const PaintImage& image) {
53   if (image.is_multipart())
54     return CheckerImagingDecision::kVetoedMultipartImage;
55
56   switch (image.animation_type()) {
57     case PaintImage::AnimationType::ANIMATED:
58       return CheckerImagingDecision::kVetoedAnimatedImage;
59     case PaintImage::AnimationType::VIDEO:
60       return CheckerImagingDecision::kVetoedVideoFrame;
61     case PaintImage::AnimationType::STATIC:
62       return CheckerImagingDecision::kCanChecker;
63   }
64
65   NOTREACHED();
66   return CheckerImagingDecision::kCanChecker;
67 }
68
69 CheckerImagingDecision GetLoadDecision(const PaintImage& image) {
70   switch (image.completion_state()) {
71     case PaintImage::CompletionState::DONE:
72       return CheckerImagingDecision::kCanChecker;
73     case PaintImage::CompletionState::PARTIALLY_DONE:
74       return CheckerImagingDecision::kVetoedPartiallyLoadedImage;
75   }
76
77   NOTREACHED();
78   return CheckerImagingDecision::kCanChecker;
79 }
80
81 CheckerImagingDecision GetSizeDecision(const SkIRect& src_rect,
82                                        size_t min_bytes,
83                                        size_t max_bytes) {
84   // Ideally we would use the original image rect here to estimate the decode
85   // duration for this image. But in the case of sprites/atlases, where small
86   // subsets of this image are used across multiple tiles, re-invalidating for
87   // replacing these images can incur heavy raster cost. So we use the src_rect
88   // here instead.
89   // TODO(khushalsagar): May be we should look at the invalidation rect for an
90   // image here to detect these cases instead?
91   base::CheckedNumeric<size_t> checked_size = 4;
92   checked_size *= src_rect.width();
93   checked_size *= src_rect.height();
94   size_t size = checked_size.ValueOrDefault(std::numeric_limits<size_t>::max());
95
96   if (size < min_bytes)
97     return CheckerImagingDecision::kVetoedSmallerThanCheckeringSize;
98   else if (size > max_bytes)
99     return CheckerImagingDecision::kVetoedLargerThanCacheSize;
100   else
101     return CheckerImagingDecision::kCanChecker;
102 }
103
104 CheckerImagingDecision GetCheckerImagingDecision(const PaintImage& image,
105                                                  const SkIRect& src_rect,
106                                                  size_t min_bytes,
107                                                  size_t max_bytes) {
108   CheckerImagingDecision decision = GetAnimationDecision(image);
109   if (decision != CheckerImagingDecision::kCanChecker)
110     return decision;
111
112   decision = GetLoadDecision(image);
113   if (decision != CheckerImagingDecision::kCanChecker)
114     return decision;
115
116   return GetSizeDecision(src_rect, min_bytes, max_bytes);
117 }
118
119 }  // namespace
120
121 // static
122 const int CheckerImageTracker::kNoDecodeAllowedPriority = -1;
123
124 CheckerImageTracker::ImageDecodeRequest::ImageDecodeRequest(
125     PaintImage paint_image,
126     DecodeType type)
127     : paint_image(std::move(paint_image)), type(type) {}
128
129 CheckerImageTracker::CheckerImageTracker(ImageController* image_controller,
130                                          CheckerImageTrackerClient* client,
131                                          bool enable_checker_imaging,
132                                          size_t min_image_bytes_to_checker)
133     : image_controller_(image_controller),
134       client_(client),
135       enable_checker_imaging_(enable_checker_imaging),
136       min_image_bytes_to_checker_(min_image_bytes_to_checker) {}
137
138 CheckerImageTracker::~CheckerImageTracker() = default;
139
140 void CheckerImageTracker::SetNoDecodesAllowed() {
141   decode_priority_allowed_ = kNoDecodeAllowedPriority;
142 }
143
144 void CheckerImageTracker::SetMaxDecodePriorityAllowed(DecodeType decode_type) {
145   DCHECK_GT(decode_type, kNoDecodeAllowedPriority);
146   DCHECK_GE(decode_type, decode_priority_allowed_);
147   DCHECK_LE(decode_type, DecodeType::kLast);
148
149   if (decode_priority_allowed_ == decode_type)
150     return;
151   decode_priority_allowed_ = decode_type;
152
153   // This will start the next decode if applicable.
154   ScheduleNextImageDecode();
155 }
156
157 void CheckerImageTracker::ScheduleImageDecodeQueue(
158     ImageDecodeQueue image_decode_queue) {
159   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
160                "CheckerImageTracker::ScheduleImageDecodeQueue");
161 #if DCHECK_IS_ON()
162   // The decodes in the queue should be prioritized correctly.
163   DecodeType type = DecodeType::kRaster;
164   for (const auto& image_request : image_decode_queue) {
165     DCHECK_GE(image_request.type, type);
166     type = image_request.type;
167   }
168 #endif
169
170   image_decode_queue_ = std::move(image_decode_queue);
171   ScheduleNextImageDecode();
172 }
173
174 const PaintImageIdFlatSet&
175 CheckerImageTracker::TakeImagesToInvalidateOnSyncTree() {
176   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
177                "CheckerImageTracker::TakeImagesToInvalidateOnSyncTree");
178   DCHECK_EQ(invalidated_images_on_current_sync_tree_.size(), 0u)
179       << "Sync tree can not be invalidated more than once";
180
181   invalidated_images_on_current_sync_tree_.swap(images_pending_invalidation_);
182   images_pending_invalidation_.clear();
183   return invalidated_images_on_current_sync_tree_;
184 }
185
186 void CheckerImageTracker::DidActivateSyncTree() {
187   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
188                "CheckerImageTracker::DidActivateSyncTree");
189   for (auto image_id : invalidated_images_on_current_sync_tree_)
190     image_id_to_decode_.erase(image_id);
191   invalidated_images_on_current_sync_tree_.clear();
192 }
193
194 void CheckerImageTracker::ClearTracker(bool can_clear_decode_policy_tracking) {
195   // Unlock all images and tracking for images pending invalidation. The
196   // |images_invalidated_on_current_sync_tree_| will be cleared when the sync
197   // tree is activated.
198   //
199   // Note that we assume that any images with DecodePolicy::ASYNC, which may be
200   // checkered, are safe to stop tracking here and will either be re-checkered
201   // and invalidated when the decode completes or be invalidated externally.
202   // This is because the policy decision for checkering an image is based on
203   // inputs received from a PaintImage in the DisplayItemList. The policy chosen
204   // for a PaintImage should remain unchanged.
205   // If the external inputs for deciding the decode policy for an image change,
206   // they should be accompanied with an invalidation during paint.
207   image_id_to_decode_.clear();
208
209   if (can_clear_decode_policy_tracking) {
210     decoding_mode_map_.clear();
211     image_async_decode_state_.clear();
212   } else {
213     // If we can't clear the decode policy, we need to make sure we still
214     // re-decode and checker images that were pending invalidation.
215     for (auto image_id : images_pending_invalidation_) {
216       auto it = image_async_decode_state_.find(image_id);
217       DCHECK(it != image_async_decode_state_.end());
218       DCHECK_EQ(it->second.policy, DecodePolicy::SYNC);
219       it->second.policy = DecodePolicy::ASYNC;
220     }
221   }
222   images_pending_invalidation_.clear();
223 }
224
225 void CheckerImageTracker::DisallowCheckeringForImage(const PaintImage& image) {
226   image_async_decode_state_.insert(
227       std::make_pair(image.stable_id(), DecodeState()));
228 }
229
230 void CheckerImageTracker::DidFinishImageDecode(
231     PaintImage::Id image_id,
232     ImageController::ImageDecodeRequestId request_id,
233     ImageController::ImageDecodeResult result) {
234   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
235                "CheckerImageTracker::DidFinishImageDecode");
236   TRACE_EVENT_NESTABLE_ASYNC_END0("cc", "CheckerImageTracker::DeferImageDecode",
237                                   TRACE_ID_LOCAL(image_id));
238
239   DCHECK_NE(ImageController::ImageDecodeResult::DECODE_NOT_REQUIRED, result);
240   DCHECK_EQ(outstanding_image_decode_.value().stable_id(), image_id);
241   outstanding_image_decode_.reset();
242
243   // The async decode state may have been cleared if the tracker was cleared
244   // before this decode could be finished.
245   auto it = image_async_decode_state_.find(image_id);
246   if (it == image_async_decode_state_.end()) {
247     DCHECK_EQ(image_id_to_decode_.count(image_id), 0u);
248     return;
249   }
250
251   // We might have flipped this to sync while updating the hints. That function
252   // would have also requested an invalidation, so we can just schedule the next
253   // decode here.
254   if (it->second.policy == DecodePolicy::SYNC) {
255     DCHECK(decoding_mode_map_.find(image_id) != decoding_mode_map_.end());
256     DCHECK_EQ(decoding_mode_map_[image_id], PaintImage::DecodingMode::kSync);
257
258     ScheduleNextImageDecode();
259     return;
260   }
261
262   it->second.policy = DecodePolicy::SYNC;
263   images_pending_invalidation_.insert(image_id);
264   ScheduleNextImageDecode();
265   client_->NeedsInvalidationForCheckerImagedTiles();
266 }
267
268 bool CheckerImageTracker::ShouldCheckerImage(const DrawImage& draw_image,
269                                              WhichTree tree) {
270   const PaintImage& image = draw_image.paint_image();
271   PaintImage::Id image_id = image.stable_id();
272   TRACE_EVENT1("cc.debug", "CheckerImageTracker::ShouldCheckerImage",
273                "image_id", image_id);
274
275   // Checkering of all images is disabled.
276   if (!enable_checker_imaging_)
277     return false;
278
279   if (!image.IsLazyGenerated())
280     return false;
281
282   // If the image was invalidated on the current sync tree and the tile is
283   // for the active tree, continue checkering it on the active tree to ensure
284   // the image update is atomic for the frame.
285   if (invalidated_images_on_current_sync_tree_.count(image_id) != 0 &&
286       tree == WhichTree::ACTIVE_TREE) {
287     return true;
288   }
289
290   // If the image is pending invalidation, continue checkering it. All tiles
291   // for these images will be invalidated on the next pending tree.
292   if (images_pending_invalidation_.find(image_id) !=
293       images_pending_invalidation_.end()) {
294     return true;
295   }
296
297   auto decoding_mode_it = decoding_mode_map_.find(image_id);
298   PaintImage::DecodingMode decoding_mode_hint =
299       decoding_mode_it == decoding_mode_map_.end()
300           ? PaintImage::DecodingMode::kUnspecified
301           : decoding_mode_it->second;
302
303   // We only checker images if the developer specifies async decoding mode.
304   if (decoding_mode_hint != PaintImage::DecodingMode::kAsync)
305     return false;
306
307   auto insert_result = image_async_decode_state_.insert(
308       std::pair<PaintImage::Id, DecodeState>(image_id, DecodeState()));
309   auto it = insert_result.first;
310   if (insert_result.second) {
311     // The following conditions must be true for an image to be checkerable:
312     //
313     // 1) Complete: The data for the image should have been completely loaded.
314     //
315     // 2) Static: Animated images/video frames can not be checkered.
316     //
317     // 3) Size constraints: Small images for which the decode is expected to
318     // be fast and large images which would breach the image cache budget and
319     // go through the at-raster decode path are not checkered.
320     //
321     // 4) Multipart images: Multipart images can be used to display mjpg video
322     // frames, checkering which would cause each video frame to flash and
323     // therefore should not be checkered.
324     //
325     // Note that we only need to do this check if we didn't veto above in this
326     // block.
327     CheckerImagingDecision decision = GetCheckerImagingDecision(
328         image, draw_image.src_rect(), min_image_bytes_to_checker_,
329         image_controller_->image_cache_max_limit_bytes());
330
331     if (decision == CheckerImagingDecision::kCanChecker && force_disabled_) {
332       // Get the decision for all the veto reasons first, so we can UMA the
333       // images that were not checkered only because checker-imaging was force
334       // disabled.
335       decision = CheckerImagingDecision::kVetoedForceDisable;
336     }
337
338     it->second.policy = decision == CheckerImagingDecision::kCanChecker
339                             ? DecodePolicy::ASYNC
340                             : DecodePolicy::SYNC;
341
342     TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
343                  "CheckerImageTracker::CheckerImagingDecision", "image_params",
344                  ToString(image_id, decision));
345   }
346
347   // Update the decode state from the latest image we have seen. Note that it
348   // is not necessary to perform this in the early out cases above since in
349   // each of those cases the image has already been decoded.
350   UpdateDecodeState(draw_image, image_id, &it->second);
351
352   return it->second.policy == DecodePolicy::ASYNC;
353 }
354
355 void CheckerImageTracker::UpdateDecodeState(const DrawImage& draw_image,
356                                             PaintImage::Id paint_image_id,
357                                             DecodeState* decode_state) {
358   // If the policy is not async then either we decoded this image already or
359   // we decided not to ever checker it.
360   if (decode_state->policy != DecodePolicy::ASYNC)
361     return;
362
363   // If the decode is already in flight, then we will have to live with what we
364   // have now.
365   if (outstanding_image_decode_.has_value() &&
366       outstanding_image_decode_.value().stable_id() == paint_image_id) {
367     return;
368   }
369
370   // Choose the max scale and filter quality. This keeps the memory usage to the
371   // minimum possible while still increasing the possibility of getting a cache
372   // hit.
373   decode_state->scale = SkSize::Make(
374       std::max(decode_state->scale.fWidth, draw_image.scale().fWidth),
375       std::max(decode_state->scale.fHeight, draw_image.scale().fHeight));
376   decode_state->use_dark_mode = draw_image.use_dark_mode();
377   decode_state->filter_quality =
378       std::max(decode_state->filter_quality, draw_image.filter_quality());
379   decode_state->target_color_params = draw_image.target_color_params();
380   decode_state->frame_index = draw_image.frame_index();
381 }
382
383 void CheckerImageTracker::ScheduleNextImageDecode() {
384   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
385                "CheckerImageTracker::ScheduleNextImageDecode");
386   // We can have only one outstanding decode pending completion with the decode
387   // service. We'll come back here when it is completed.
388   if (outstanding_image_decode_.has_value())
389     return;
390
391   if (image_decode_queue_.empty())
392     return;
393
394   // If scheduling decodes for this priority is not allowed right now, don't
395   // schedule them. We will come back here when the allowed priority changes.
396   if (image_decode_queue_.front().type > decode_priority_allowed_)
397     return;
398
399   DrawImage draw_image;
400   while (!image_decode_queue_.empty()) {
401     auto candidate = std::move(image_decode_queue_.front().paint_image);
402     image_decode_queue_.erase(image_decode_queue_.begin());
403
404     // Once an image has been decoded, it can still be present in the decode
405     // queue (duplicate entries), or while an image is still being skipped on
406     // the active tree. Check if the image is still ASYNC to see if a decode is
407     // needed.
408     PaintImage::Id image_id = candidate.stable_id();
409     auto it = image_async_decode_state_.find(image_id);
410     DCHECK(it != image_async_decode_state_.end());
411     if (it->second.policy != DecodePolicy::ASYNC)
412       continue;
413
414     draw_image = DrawImage(
415         candidate, it->second.use_dark_mode,
416         SkIRect::MakeWH(candidate.width(), candidate.height()),
417         it->second.filter_quality,
418         SkM44::Scale(it->second.scale.width(), it->second.scale.height()),
419         it->second.frame_index, it->second.target_color_params);
420     outstanding_image_decode_.emplace(candidate);
421     break;
422   }
423
424   // We either found an image to decode or we reached the end of the queue. If
425   // we couldn't find an image, we're done.
426   if (!outstanding_image_decode_.has_value()) {
427     DCHECK(image_decode_queue_.empty());
428     return;
429   }
430
431   PaintImage::Id image_id = outstanding_image_decode_.value().stable_id();
432   DCHECK_EQ(image_id_to_decode_.count(image_id), 0u);
433   TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
434       "cc", "CheckerImageTracker::DeferImageDecode", TRACE_ID_LOCAL(image_id));
435   ImageController::ImageDecodeRequestId request_id =
436       image_controller_->QueueImageDecode(
437           draw_image, base::BindOnce(&CheckerImageTracker::DidFinishImageDecode,
438                                      weak_factory_.GetWeakPtr(), image_id));
439
440   image_id_to_decode_.emplace(image_id, std::make_unique<ScopedDecodeHolder>(
441                                             image_controller_, request_id));
442 }
443
444 void CheckerImageTracker::UpdateImageDecodingHints(
445     base::flat_map<PaintImage::Id, PaintImage::DecodingMode>
446         decoding_mode_map) {
447   if (!enable_checker_imaging_)
448     return;
449
450   // Merge the |decoding_mode_map| with our member map, keeping the more
451   // conservative values.
452   // TODO(vmpstr): Figure out if and how do we clear this value to ensure that
453   // if we no longer have any kSync images, for example, then we can loosen the
454   // requirement on the decoding mode for that image id.
455   for (auto pair : decoding_mode_map) {
456     PaintImage::Id id = pair.first;
457     PaintImage::DecodingMode decoding_mode = pair.second;
458
459     // In case we already have this image as async, it implies that we are
460     // currently displaying this content as checkered. We can flip the state to
461     // sync here and add the image to be invalidated. The invalidation should
462     // happen shortly after, since this function should be called in a commit.
463     auto state_it = image_async_decode_state_.find(id);
464     if (state_it != image_async_decode_state_.end()) {
465       auto& state = state_it->second;
466       if (state.policy == DecodePolicy::ASYNC &&
467           decoding_mode == PaintImage::DecodingMode::kSync) {
468         state.policy = DecodePolicy::SYNC;
469         images_pending_invalidation_.insert(id);
470       }
471     }
472
473     // Update the decoding hints map.
474     auto decoding_mode_it = decoding_mode_map_.find(id);
475     if (decoding_mode_it == decoding_mode_map_.end()) {
476       decoding_mode_map_[id] = decoding_mode;
477     } else {
478       decoding_mode_it->second =
479           PaintImage::GetConservative(decoding_mode_it->second, decoding_mode);
480     }
481   }
482 }
483
484 }  // namespace cc