[M108 Migration][VD] Avoid pending frame counter becoming negative
[platform/framework/web/chromium-efl.git] / cc / tiles / software_image_decode_cache_utils.cc
1 // Copyright 2018 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/software_image_decode_cache_utils.h"
6
7 #include <algorithm>
8 #include <sstream>
9 #include <utility>
10
11 #include "base/atomic_sequence_num.h"
12 #include "base/callback_helpers.h"
13 #include "base/hash/hash.h"
14 #include "base/memory/discardable_memory_allocator.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/process/memory.h"
17 #include "base/trace_event/trace_event.h"
18 #include "cc/paint/paint_flags.h"
19 #include "cc/tiles/mipmap_util.h"
20 #include "third_party/skia/include/core/SkColorSpace.h"
21 #include "ui/gfx/geometry/skia_conversions.h"
22
23 namespace cc {
24 namespace {
25 // If the size of the original sized image breaches kMemoryRatioToSubrect but we
26 // don't need to scale the image, consider caching only the needed subrect.
27 // The second part that much be true is that we cache only the needed subrect if
28 // the total size needed for the subrect is at most kMemoryRatioToSubrect *
29 // (size needed for the full original image).
30 // Note that at least one of the dimensions has to be at least
31 // kMinDimensionToSubrect before an image can breach the threshold.
32 const size_t kMemoryThresholdToSubrect = 64 * 1024 * 1024;
33 const int kMinDimensionToSubrect = 4 * 1024;
34 const float kMemoryRatioToSubrect = 0.5f;
35
36 // Tracing ID sequence for use in CacheEntry.
37 base::AtomicSequenceNumber g_next_tracing_id_;
38
39 gfx::Rect GetSrcRect(const DrawImage& image) {
40   const SkIRect& src_rect = image.src_rect();
41   int x = std::max(0, src_rect.x());
42   int y = std::max(0, src_rect.y());
43   int right = std::min(image.paint_image().width(), src_rect.right());
44   int bottom = std::min(image.paint_image().height(), src_rect.bottom());
45   if (x >= right || y >= bottom)
46     return gfx::Rect();
47   return gfx::Rect(x, y, right - x, bottom - y);
48 }
49
50 SkImageInfo CreateImageInfo(const SkISize& size, SkColorType color_type) {
51   return SkImageInfo::Make(size.width(), size.height(), color_type,
52                            kPremul_SkAlphaType);
53 }
54
55 // Does *not* return nullptr.
56 std::unique_ptr<base::DiscardableMemory> AllocateDiscardable(
57     const SkImageInfo& info,
58     base::OnceClosure on_no_memory) {
59   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "AllocateDiscardable");
60   size_t size = info.minRowBytes() * info.height();
61   auto* allocator = base::DiscardableMemoryAllocator::GetInstance();
62   return allocator->AllocateLockedDiscardableMemoryWithRetryOrDie(
63       size, std::move(on_no_memory));
64 }
65
66 }  // namespace
67
68 // static
69 std::unique_ptr<SoftwareImageDecodeCacheUtils::CacheEntry>
70 SoftwareImageDecodeCacheUtils::DoDecodeImage(
71     const CacheKey& key,
72     const PaintImage& paint_image,
73     SkColorType color_type,
74     PaintImage::GeneratorClientId client_id,
75     base::OnceClosure on_no_memory) {
76   SkISize target_size =
77       SkISize::Make(key.target_size().width(), key.target_size().height());
78   DCHECK(target_size == paint_image.GetSupportedDecodeSize(target_size));
79
80   SkImageInfo target_info = CreateImageInfo(target_size, color_type);
81   std::unique_ptr<base::DiscardableMemory> target_pixels =
82       AllocateDiscardable(target_info, std::move(on_no_memory));
83   if (!target_pixels->data())
84     return nullptr;
85
86   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
87                "SoftwareImageDecodeCacheUtils::DoDecodeImage - "
88                "decode");
89   bool result =
90       paint_image.Decode(target_pixels->data(), &target_info,
91                          key.target_color_params().color_space.ToSkColorSpace(),
92                          key.frame_key().frame_index(), client_id);
93   if (!result) {
94     target_pixels->Unlock();
95     return nullptr;
96   }
97   return std::make_unique<CacheEntry>(target_info, std::move(target_pixels),
98                                       SkSize::Make(0, 0));
99 }
100
101 // static
102 std::unique_ptr<SoftwareImageDecodeCacheUtils::CacheEntry>
103 SoftwareImageDecodeCacheUtils::GenerateCacheEntryFromCandidate(
104     const CacheKey& key,
105     const DecodedDrawImage& candidate_image,
106     bool needs_extract_subset,
107     SkColorType color_type) {
108   SkISize target_size =
109       SkISize::Make(key.target_size().width(), key.target_size().height());
110   SkImageInfo target_info = CreateImageInfo(target_size, color_type);
111   // TODO(crbug.com/983348): If this turns into a crasher, pass an actual
112   // "free memory" closure.
113   std::unique_ptr<base::DiscardableMemory> target_pixels =
114       AllocateDiscardable(target_info, base::DoNothing());
115
116   if (key.type() == CacheKey::kSubrectOriginal) {
117     DCHECK(needs_extract_subset);
118     TRACE_EVENT0(
119         TRACE_DISABLED_BY_DEFAULT("cc.debug"),
120         "SoftwareImageDecodeCacheUtils::GenerateCacheEntryFromCandidate - "
121         "subrect");
122     bool result = candidate_image.image()->readPixels(
123         target_info, target_pixels->data(), target_info.minRowBytes(),
124         key.src_rect().x(), key.src_rect().y(), SkImage::kDisallow_CachingHint);
125     // We have a decoded image, and we're reading into already allocated memory.
126     // This should never fail.
127     DCHECK(result) << key.ToString();
128     return std::make_unique<CacheEntry>(
129         target_info.makeColorSpace(candidate_image.image()->refColorSpace()),
130         std::move(target_pixels),
131         SkSize::Make(-key.src_rect().x(), -key.src_rect().y()));
132   }
133
134   DCHECK_EQ(key.type(), CacheKey::kSubrectAndScale);
135   TRACE_EVENT0(
136       TRACE_DISABLED_BY_DEFAULT("cc.debug"),
137       "SoftwareImageDecodeCacheUtils::GenerateCacheEntryFromCandidate - "
138       "scale");
139   SkPixmap decoded_pixmap;
140   // We don't need to subrect this image, since all candidates passed in would
141   // already have a src_rect applied to them.
142   bool result = candidate_image.image()->peekPixels(&decoded_pixmap);
143   DCHECK(result) << key.ToString();
144   if (needs_extract_subset) {
145     result = decoded_pixmap.extractSubset(&decoded_pixmap,
146                                           gfx::RectToSkIRect(key.src_rect()));
147     DCHECK(result) << key.ToString();
148   }
149
150   // Nearest neighbor would only be set in the unscaled case.
151   DCHECK(!key.is_nearest_neighbor());
152   SkPixmap target_pixmap(target_info, target_pixels->data(),
153                          target_info.minRowBytes());
154   PaintFlags::FilterQuality filter_quality = PaintFlags::FilterQuality::kMedium;
155   result = decoded_pixmap.scalePixels(
156       target_pixmap,
157       PaintFlags::FilterQualityToSkSamplingOptions(filter_quality));
158   DCHECK(result) << key.ToString();
159
160   return std::make_unique<CacheEntry>(
161       target_info.makeColorSpace(candidate_image.image()->refColorSpace()),
162       std::move(target_pixels),
163       SkSize::Make(-key.src_rect().x(), -key.src_rect().y()));
164 }
165
166 // CacheKey --------------------------------------------------------------------
167 // static
168 SoftwareImageDecodeCacheUtils::CacheKey
169 SoftwareImageDecodeCacheUtils::CacheKey::FromDrawImage(const DrawImage& image,
170                                                        SkColorType color_type) {
171   DCHECK(!image.paint_image().IsTextureBacked());
172
173   const PaintImage::FrameKey frame_key = image.frame_key();
174   const PaintImage::Id stable_id = image.paint_image().stable_id();
175
176   const SkSize& scale = image.scale();
177   // If the src_rect falls outside of the image, we need to clip it since
178   // otherwise we might end up with uninitialized memory in the decode process.
179   // Note that the scale is still unchanged and the target size is now a
180   // function of the new src_rect.
181   const gfx::Rect& src_rect = GetSrcRect(image);
182
183   // Start with the exact target size. However, this will be adjusted below to
184   // be either a mip level, the original size, or a subrect size. This is done
185   // to keep memory accounting correct.
186   gfx::Size target_size(
187       SkScalarRoundToInt(std::abs(src_rect.width() * scale.width())),
188       SkScalarRoundToInt(std::abs(src_rect.height() * scale.height())));
189
190   // If the target size is empty, then we'll be skipping the decode anyway, so
191   // the filter quality doesn't matter. Early out instead.
192   if (target_size.IsEmpty()) {
193     return CacheKey(frame_key, stable_id, kSubrectAndScale, false,
194                     image.paint_image().may_be_lcp_candidate(), src_rect,
195                     target_size, image.target_color_params());
196   }
197
198   ProcessingType type = kOriginal;
199   bool is_nearest_neighbor =
200       image.filter_quality() == PaintFlags::FilterQuality::kNone;
201   int mip_level = MipMapUtil::GetLevelForSize(src_rect.size(), target_size);
202   // If any of the following conditions hold, then use at most low filter
203   // quality and adjust the target size to match the original image:
204   // - Quality is none: We need a pixelated image, so we can't upgrade it.
205   // - Mip level is 0: The required mip is the original image, so just use low
206   //   filter quality.
207   // - Matrix is not decomposable: There's perspective on this image and we
208   //   can't determine the size, so use the original.
209   if (is_nearest_neighbor || mip_level == 0 ||
210       !image.matrix_is_decomposable()) {
211     type = kOriginal;
212     // Update the size to be the original image size.
213     target_size =
214         gfx::Size(image.paint_image().width(), image.paint_image().height());
215   } else {
216     type = kSubrectAndScale;
217     // Update the target size to be a mip level size.
218     target_size = MipMapUtil::GetSizeForLevel(src_rect.size(), mip_level);
219   }
220
221   // If the original image is large, we might want to do a subrect instead if
222   // the subrect would be kMemoryRatioToSubrect times smaller.
223   if (type == kOriginal &&
224       (image.paint_image().width() >= kMinDimensionToSubrect ||
225        image.paint_image().height() >= kMinDimensionToSubrect)) {
226     base::CheckedNumeric<size_t> checked_original_size = 4u;
227     checked_original_size *= image.paint_image().width();
228     checked_original_size *= image.paint_image().height();
229     size_t original_size = checked_original_size.ValueOrDefault(
230         std::numeric_limits<size_t>::max());
231
232     base::CheckedNumeric<size_t> checked_src_rect_size = 4u;
233     checked_src_rect_size *= src_rect.width();
234     checked_src_rect_size *= src_rect.height();
235     size_t src_rect_size = checked_src_rect_size.ValueOrDefault(
236         std::numeric_limits<size_t>::max());
237
238     // If the sizes are such that we get good savings by subrecting, then do
239     // that. Also update the target size to be the src rect size since that's
240     // the rect we want to use.
241     if (original_size > kMemoryThresholdToSubrect &&
242         src_rect_size <= original_size * kMemoryRatioToSubrect) {
243       type = kSubrectOriginal;
244       target_size = src_rect.size();
245     }
246   }
247
248   return CacheKey(frame_key, stable_id, type, is_nearest_neighbor,
249                   image.paint_image().may_be_lcp_candidate(), src_rect,
250                   target_size, image.target_color_params());
251 }
252
253 SoftwareImageDecodeCacheUtils::CacheKey::CacheKey(
254     PaintImage::FrameKey frame_key,
255     PaintImage::Id stable_id,
256     ProcessingType type,
257     bool is_nearest_neighbor,
258     bool may_be_lcp_candidate,
259     const gfx::Rect& src_rect,
260     const gfx::Size& target_size,
261     const TargetColorParams& target_color_params)
262     : frame_key_(frame_key),
263       stable_id_(stable_id),
264       type_(type),
265       is_nearest_neighbor_(is_nearest_neighbor),
266       may_be_lcp_candidate_(may_be_lcp_candidate),
267       src_rect_(src_rect),
268       target_size_(target_size),
269       target_color_params_(target_color_params) {
270   if (type == kOriginal) {
271     hash_ = frame_key_.hash();
272   } else {
273     // TODO(vmpstr): This is a mess. Maybe it's faster to just search the vector
274     // always (forwards or backwards to account for LRU).
275     uint64_t src_rect_hash = base::HashInts(
276         static_cast<uint64_t>(base::HashInts(src_rect_.x(), src_rect_.y())),
277         static_cast<uint64_t>(
278             base::HashInts(src_rect_.width(), src_rect_.height())));
279
280     uint64_t target_size_hash =
281         base::HashInts(target_size_.width(), target_size_.height());
282
283     hash_ = base::HashInts(base::HashInts(src_rect_hash, target_size_hash),
284                            frame_key_.hash());
285   }
286   // Include the target color space in the hash regardless of scaling.
287   hash_ = base::HashInts(hash_, target_color_params.GetHash());
288 }
289
290 SoftwareImageDecodeCacheUtils::CacheKey::CacheKey(const CacheKey& other) =
291     default;
292
293 SoftwareImageDecodeCacheUtils::CacheKey&
294 SoftwareImageDecodeCacheUtils::CacheKey::operator=(const CacheKey& other) =
295     default;
296
297 std::string SoftwareImageDecodeCacheUtils::CacheKey::ToString() const {
298   std::ostringstream str;
299   str << "frame_key[" << frame_key_.ToString() << "]\ntype[";
300   switch (type_) {
301     case kOriginal:
302       str << "Original";
303       break;
304     case kSubrectOriginal:
305       str << "SubrectOriginal";
306       break;
307     case kSubrectAndScale:
308       str << "SubrectAndScale";
309       break;
310   }
311   str << "]\nis_nearest_neightbor[" << is_nearest_neighbor_ << "]\nsrc_rect["
312       << src_rect_.ToString() << "]\ntarget_size[" << target_size_.ToString()
313       << "]\ntarget_color_params[" << target_color_params_.ToString()
314       << "]\nhash[" << hash_ << "]";
315   return str.str();
316 }
317
318 // CacheEntry ------------------------------------------------------------------
319 SoftwareImageDecodeCacheUtils::CacheEntry::CacheEntry()
320     : tracing_id_(g_next_tracing_id_.GetNext()) {}
321 SoftwareImageDecodeCacheUtils::CacheEntry::CacheEntry(
322     const SkImageInfo& info,
323     std::unique_ptr<base::DiscardableMemory> in_memory,
324     const SkSize& src_rect_offset)
325     : is_locked(true),
326       memory(std::move(in_memory)),
327       image_info_(info),
328       src_rect_offset_(src_rect_offset),
329       tracing_id_(g_next_tracing_id_.GetNext()) {
330   DCHECK(memory);
331   SkPixmap pixmap(image_info_, memory->data(), image_info_.minRowBytes());
332   image_ = SkImage::MakeFromRaster(
333       pixmap, [](const void* pixels, void* context) {}, nullptr);
334 }
335
336 SoftwareImageDecodeCacheUtils::CacheEntry::~CacheEntry() {
337   DCHECK(!is_locked);
338
339   // We create temporary CacheEntries as a part of decoding. However, we move
340   // the memory to cache entries that actually live in the cache. Destroying the
341   // temporaries should not cause any of the stats to be recorded. Specifically,
342   // if allowed to report, they would report every single temporary entry as
343   // wasted, which is misleading. As a fix, don't report on a cache entry that
344   // has never been in the cache.
345   if (!cached_)
346     return;
347
348   // lock_count | used  | last lock failed | result state
349   // ===========+=======+==================+==================
350   //  1         | false | false            | WASTED
351   //  1         | false | true             | WASTED
352   //  1         | true  | false            | USED
353   //  1         | true  | true             | USED_RELOCK_FAILED
354   //  >1        | false | false            | WASTED_RELOCKED
355   //  >1        | false | true             | WASTED_RELOCKED
356   //  >1        | true  | false            | USED_RELOCKED
357   //  >1        | true  | true             | USED_RELOCKED
358   // Note that it's important not to reorder the following enums, since the
359   // numerical values are used in the histogram code.
360   enum State : int {
361     DECODED_IMAGE_STATE_WASTED,
362     DECODED_IMAGE_STATE_USED,
363     DECODED_IMAGE_STATE_USED_RELOCK_FAILED,
364     DECODED_IMAGE_STATE_WASTED_RELOCKED,
365     DECODED_IMAGE_STATE_USED_RELOCKED,
366     DECODED_IMAGE_STATE_COUNT
367   } state = DECODED_IMAGE_STATE_WASTED;
368
369   if (usage_stats_.lock_count == 1) {
370     if (!usage_stats_.used)
371       state = DECODED_IMAGE_STATE_WASTED;
372     else if (usage_stats_.last_lock_failed)
373       state = DECODED_IMAGE_STATE_USED_RELOCK_FAILED;
374     else
375       state = DECODED_IMAGE_STATE_USED;
376   } else {
377     if (usage_stats_.used)
378       state = DECODED_IMAGE_STATE_USED_RELOCKED;
379     else
380       state = DECODED_IMAGE_STATE_WASTED_RELOCKED;
381   }
382
383   UMA_HISTOGRAM_ENUMERATION("Renderer4.SoftwareImageDecodeState", state,
384                             DECODED_IMAGE_STATE_COUNT);
385   UMA_HISTOGRAM_BOOLEAN("Renderer4.SoftwareImageDecodeState.FirstLockWasted",
386                         usage_stats_.first_lock_wasted);
387   if (usage_stats_.first_lock_out_of_raster)
388     UMA_HISTOGRAM_BOOLEAN(
389         "Renderer4.SoftwareImageDecodeState.FirstLockWasted.OutOfRaster",
390         usage_stats_.first_lock_wasted);
391 }
392
393 void SoftwareImageDecodeCacheUtils::CacheEntry::MoveImageMemoryTo(
394     CacheEntry* entry) {
395   DCHECK(!is_budgeted);
396   DCHECK_EQ(ref_count, 0);
397
398   // Copy/move most things except budgeted and ref counts.
399   entry->decode_failed = decode_failed;
400   entry->is_locked = is_locked;
401   is_locked = false;
402
403   entry->memory = std::move(memory);
404   entry->image_info_ = std::move(image_info_);
405   entry->src_rect_offset_ = std::move(src_rect_offset_);
406   entry->image_ = std::move(image_);
407 }
408
409 bool SoftwareImageDecodeCacheUtils::CacheEntry::Lock() {
410   if (!memory)
411     return false;
412
413   DCHECK(!is_locked);
414   bool success = memory->Lock();
415   if (!success) {
416     memory = nullptr;
417     usage_stats_.last_lock_failed = true;
418     return false;
419   }
420   is_locked = true;
421   ++usage_stats_.lock_count;
422   return true;
423 }
424
425 void SoftwareImageDecodeCacheUtils::CacheEntry::Unlock() {
426   if (!memory)
427     return;
428
429   DCHECK(is_locked);
430   memory->Unlock();
431   is_locked = false;
432   if (usage_stats_.lock_count == 1)
433     usage_stats_.first_lock_wasted = !usage_stats_.used;
434 }
435
436 }  // namespace cc