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

#ifndef CC_TILES_SOFTWARE_IMAGE_DECODE_CACHE_H_
#define CC_TILES_SOFTWARE_IMAGE_DECODE_CACHE_H_

#include <stdint.h>

#include <memory>
#include <unordered_map>

#include "base/containers/mru_cache.h"
#include "base/memory/memory_coordinator_client.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/ref_counted.h"
#include "base/numerics/safe_math.h"
#include "base/trace_event/memory_dump_provider.h"
#include "cc/cc_export.h"
#include "cc/paint/draw_image.h"
#include "cc/tiles/image_decode_cache.h"
#include "cc/tiles/software_image_decode_cache_utils.h"

namespace cc {

class CC_EXPORT SoftwareImageDecodeCache
    : public ImageDecodeCache,
      public base::trace_event::MemoryDumpProvider,
      public base::MemoryCoordinatorClient {
 public:
  using Utils = SoftwareImageDecodeCacheUtils;
  using CacheKey = Utils::CacheKey;
  using CacheKeyHash = Utils::CacheKeyHash;

  enum class DecodeTaskType { USE_IN_RASTER_TASKS, USE_OUT_OF_RASTER_TASKS };

  SoftwareImageDecodeCache(SkColorType color_type,
                           size_t locked_memory_limit_bytes);
  ~SoftwareImageDecodeCache() override;

  // ImageDecodeCache overrides.
  TaskResult GetTaskForImageAndRef(const DrawImage& image,
                                   const TracingInfo& tracing_info) override;
  TaskResult GetOutOfRasterDecodeTaskForImageAndRef(
      const DrawImage& image) override;
  void UnrefImage(const DrawImage& image) override;
  DecodedDrawImage GetDecodedImageForDraw(const DrawImage& image) override;
  void DrawWithImageFinished(const DrawImage& image,
                             const DecodedDrawImage& decoded_image) override;
  void ReduceCacheUsage() override;
  // Software doesn't keep outstanding images pinned, so this is a no-op.
  void SetShouldAggressivelyFreeResources(
      bool aggressively_free_resources) override {}
  void ClearCache() override;
  size_t GetMaximumMemoryLimitBytes() const override;
  bool UseCacheForDrawImage(const DrawImage& image) const override;

  // Decode the given image and store it in the cache. This is only called by an
  // image decode task from a worker thread.
  void DecodeImageInTask(const CacheKey& key,
                         const PaintImage& paint_image,
                         DecodeTaskType task_type);

  void OnImageDecodeTaskCompleted(const CacheKey& key,
                                  DecodeTaskType task_type);

  // MemoryDumpProvider overrides.
  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
                    base::trace_event::ProcessMemoryDump* pmd) override;

  size_t GetNumCacheEntriesForTesting() const { return decoded_images_.size(); }

 private:
  using CacheEntry = Utils::CacheEntry;

  // MemoryBudget is a convenience class for memory bookkeeping and ensuring
  // that we don't go over the limit when pre-decoding.
  class MemoryBudget {
   public:
    explicit MemoryBudget(size_t limit_bytes);

    size_t AvailableMemoryBytes() const;
    void AddUsage(size_t usage);
    void SubtractUsage(size_t usage);
    void ResetUsage();
    size_t total_limit_bytes() const { return limit_bytes_; }
    size_t GetCurrentUsageSafe() const;

   private:
    const size_t limit_bytes_;
    base::CheckedNumeric<size_t> current_usage_bytes_;
  };

  using ImageMRUCache = base::
      HashingMRUCache<CacheKey, std::unique_ptr<CacheEntry>, CacheKeyHash>;

  // Actually decode the image. Note that this function can (and should) be
  // called with no lock acquired, since it can do a lot of work. Note that it
  // can also return nullptr to indicate the decode failed.
  std::unique_ptr<CacheEntry> DecodeImageInternal(const CacheKey& key,
                                                  const DrawImage& draw_image);

  // Get the decoded draw image for the given key and paint_image. Note that
  // this function has to be called with no lock acquired, since it will acquire
  // its own locks and might call DecodeImageInternal above. Note that
  // when used internally, we still require that DrawWithImageFinished() is
  // called afterwards.
  DecodedDrawImage GetDecodedImageForDrawInternal(
      const CacheKey& key,
      const PaintImage& paint_image);

  // Removes unlocked decoded images until the number of decoded images is
  // reduced within the given limit.
  void ReduceCacheUsageUntilWithinLimit(size_t limit);

  // Overriden from base::MemoryCoordinatorClient.
  void OnMemoryStateChange(base::MemoryState state) override;
  void OnPurgeMemory() override;

  // TODO(gyuyoung): OnMemoryPressure is deprecated. So this should be removed
  // when the memory coordinator is enabled by default.
  void OnMemoryPressure(
      base::MemoryPressureListener::MemoryPressureLevel level);

  // Helper method to get the different tasks. Note that this should be used as
  // if it was public (ie, all of the locks need to be properly acquired).
  TaskResult GetTaskForImageAndRefInternal(const DrawImage& image,
                                           const TracingInfo& tracing_info,
                                           DecodeTaskType type);

  CacheEntry* AddCacheEntry(const CacheKey& key);

  void DecodeImageIfNecessary(const CacheKey& key,
                              const PaintImage& paint_image,
                              CacheEntry* cache_entry);
  void AddBudgetForImage(const CacheKey& key, CacheEntry* entry);
  void RemoveBudgetForImage(const CacheKey& key, CacheEntry* entry);
  base::Optional<CacheKey> FindCachedCandidate(const CacheKey& key);

  void UnrefImage(const CacheKey& key);

  // The members below this comment can only be accessed if the lock is held to
  // ensure that they are safe to access on multiple threads.
  // The exception is accessing |locked_images_budget_.total_limit_bytes()|,
  // which is const and thread safe.
  base::Lock lock_;

  // Decoded images and ref counts (predecode path).
  ImageMRUCache decoded_images_;

  std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;

  // A map of PaintImage::FrameKey to the ImageKeys for cached decodes of this
  // PaintImage.
  std::unordered_map<PaintImage::FrameKey,
                     std::vector<CacheKey>,
                     PaintImage::FrameKeyHash>
      frame_key_to_image_keys_;

  MemoryBudget locked_images_budget_;

  SkColorType color_type_;
  size_t max_items_in_cache_;
  // Records the maximum number of items in the cache over the lifetime of the
  // cache. This is updated anytime we are requested to reduce cache usage.
  size_t lifetime_max_items_in_cache_ = 0u;
};

}  // namespace cc

#endif  // CC_TILES_SOFTWARE_IMAGE_DECODE_CACHE_H_
