1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206

cc / tiles / image_decode_cache.h [blame]

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CC_TILES_IMAGE_DECODE_CACHE_H_
#define CC_TILES_IMAGE_DECODE_CACHE_H_

#include <atomic>

#include "base/memory/ref_counted.h"
#include "base/notreached.h"
#include "cc/base/devtools_instrumentation.h"
#include "cc/cc_export.h"
#include "cc/paint/decoded_draw_image.h"
#include "cc/paint/draw_image.h"
#include "cc/raster/tile_task.h"
#include "cc/tiles/image_decode_cache_utils.h"
#include "cc/tiles/tile_priority.h"

namespace cc {

// ImageDecodeCache is responsible for generating decode tasks, decoding
// images, storing images in cache, and being able to return the decoded images
// when requested.

// ImageDecodeCache is responsible for the following things:
// 1. Given a DrawImage, it can return an TileTask which when run will
//    decode and cache the resulting image. If the image does not need a task to
//    be decoded, then nullptr will be returned. The return value of the
//    function indicates whether the image was or is going to be locked, so an
//    unlock will be required.
// 2. Given a cache key and a DrawImage, it can decode the image and store it in
//    the cache. Note that it is important that this function is only accessed
//    via an image decode task.
// 3. Given a DrawImage, it can return a DecodedDrawImage, which represented the
//    decoded version of the image. Note that if the image is not in the cache
//    and it needs to be scaled/decoded, then this decode will happen as part of
//    getting the image. As such, this should only be accessed from a raster
//    thread.
class CC_EXPORT ImageDecodeCache {
 public:
  enum class TaskType { kInRaster, kOutOfRaster };

  // See GenerateClientId.
  using ClientId = uint32_t;

  // This information should be used strictly in tracing, UMA, and any other
  // reporting systems.
  struct TracingInfo {
    TracingInfo(uint64_t prepare_tiles_id,
                TilePriority::PriorityBin requesting_tile_bin)
        : prepare_tiles_id(prepare_tiles_id),
          requesting_tile_bin(requesting_tile_bin) {}
    TracingInfo() = default;

    // ID for the current prepare tiles call.
    const uint64_t prepare_tiles_id = 0;

    // The bin of the tile that caused this image to be requested.
    const TilePriority::PriorityBin requesting_tile_bin = TilePriority::NOW;
  };

  static devtools_instrumentation::ScopedImageDecodeTask::TaskType
  ToScopedTaskType(TaskType task_type) {
    using ScopedTaskType =
        devtools_instrumentation::ScopedImageDecodeTask::TaskType;
    switch (task_type) {
      case TaskType::kInRaster:
        return ScopedTaskType::kInRaster;
      case TaskType::kOutOfRaster:
        return ScopedTaskType::kOutOfRaster;
    }
    NOTREACHED();
  }

  static devtools_instrumentation::ScopedImageDecodeTask::ImageType
  ToScopedImageType(ImageType image_type) {
    using ScopedImageType =
        devtools_instrumentation::ScopedImageDecodeTask::ImageType;
    switch (image_type) {
      case ImageType::kAVIF:
        return ScopedImageType::kAvif;
      case ImageType::kBMP:
        return ScopedImageType::kBmp;
      case ImageType::kGIF:
        return ScopedImageType::kGif;
      case ImageType::kICO:
        return ScopedImageType::kIco;
      case ImageType::kJPEG:
        return ScopedImageType::kJpeg;
      case ImageType::kPNG:
        return ScopedImageType::kPng;
      case ImageType::kWEBP:
        return ScopedImageType::kWebP;
      case ImageType::kInvalid:
        return ScopedImageType::kOther;
    }
  }

  virtual ~ImageDecodeCache() {}

  struct CC_EXPORT TaskResult {
    explicit TaskResult(bool need_unref,
                        bool is_at_raster_decode,
                        bool can_do_hardware_accelerated_decode);
    explicit TaskResult(scoped_refptr<TileTask> task,
                        bool can_do_hardware_accelerated_decode);
    TaskResult(const TaskResult& result);
    ~TaskResult();

    scoped_refptr<TileTask> task;
    bool need_unref = false;
    bool is_at_raster_decode = false;
    bool can_do_hardware_accelerated_decode = false;
  };
  // Fill in an TileTask which will decode the given image when run. In
  // case the image is already cached, fills in nullptr. Returns true if the
  // image needs to be unreffed when the caller is finished with it. |client_id|
  // helps to identify a client that requests a task if the cache is shared
  // between multiple clients. Returns the same task iff an image task for the
  // the |client_id| already exists. The way multiple image tasks for the same
  // image from different clients are managed is an implementation detail of the
  // cache instance. The clients shouldn't worry about the ordering between
  // these tasks. The |client_id| is an id that must be generated by calling
  // |GenerateClientId| to avoid any collisions.
  //
  // This is called by the tile manager (on the compositor thread) when creating
  // a raster task.
  virtual TaskResult GetTaskForImageAndRef(ClientId client_id,
                                           const DrawImage& image,
                                           const TracingInfo& tracing_info) = 0;
  // Similar to GetTaskForImageAndRef (including the |client_id|), except that
  // it returns tasks that are not meant to be run as part of raster. That is,
  // this is part of a predecode API. Note that this should only return a task
  // responsible for decoding (and not uploading), since it will be run on a
  // worker thread which may not have the right GPU context for upload.
  virtual TaskResult GetOutOfRasterDecodeTaskForImageAndRef(
      ClientId client_id,
      const DrawImage& image) = 0;

  // Unrefs an image. When the tile is finished, this should be called for every
  // GetTaskForImageAndRef call that returned true.
  virtual void UnrefImage(const DrawImage& image) = 0;

  // Returns a decoded draw image. This may cause a decode if the image was not
  // predecoded.
  //
  // This is called by a raster task (on a worker thread) when an image is
  // required.
  //
  // TODO(khushalsagar/vmpstr): Since the cache knows if it's a video frame, it
  // should discard any frames from the same source not in use in the
  // compositor.
  virtual DecodedDrawImage GetDecodedImageForDraw(const DrawImage& image) = 0;
  // Unrefs an image. This should be called for every GetDecodedImageForDraw
  // when the draw with the image is finished.
  virtual void DrawWithImageFinished(const DrawImage& image,
                                     const DecodedDrawImage& decoded_image) = 0;

  // This function informs the cache that now is a good time to clean up
  // memory. This is called periodically from the compositor thread.
  virtual void ReduceCacheUsage() = 0;

  // This function informs the cache that we are hidden and should not be
  // retaining cached resources longer than needed. If |context_lock_acquired|
  // is true, the caller has already acquired the context lock.
  virtual void SetShouldAggressivelyFreeResources(
      bool aggressively_free_resources,
      bool context_lock_acquired) = 0;

  // Clears all elements from the cache.
  virtual void ClearCache() = 0;

  // Returns the maximum amount of memory we would be able to lock. This ignores
  // any temporary states, such as throttled, and return the maximum possible
  // memory. It is used as an esimate of whether an image can fit into the
  // locked budget before creating a task.
  virtual size_t GetMaximumMemoryLimitBytes() const = 0;

  // Returns true if the cache should be used for |image|. In certain cases the
  // image can directly be used for raster (for instance bitmaps in a software
  // draw).
  virtual bool UseCacheForDrawImage(const DrawImage& image) const = 0;

  // Should be called periodically to record statistics about cache use and
  // performance.
  virtual void RecordStats() = 0;

  // Returns a unique id that can be used to request upload/decode tasks for
  // images' uploading/decoding. This can be optionally used by ImageDecodeCache
  // instance to manage decode/upload tasks. Implementations may override this
  // to add additional logic. However, they should eventually use this to have a
  // new client id generated.
  virtual ClientId GenerateClientId();

 protected:
  static const ClientId kDefaultClientId;

 private:
  // A next available id for a client identification.
  std::atomic<ClientId> next_available_id_ = 0;
};

}  // namespace cc

#endif  // CC_TILES_IMAGE_DECODE_CACHE_H_