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

cc / paint / skottie_serialization_history.h [blame]

// Copyright 2022 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_PAINT_SKOTTIE_SERIALIZATION_HISTORY_H_
#define CC_PAINT_SKOTTIE_SERIALIZATION_HISTORY_H_

#include "base/containers/flat_map.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "cc/paint/paint_export.h"
#include "cc/paint/paint_flags.h"
#include "cc/paint/paint_image.h"
#include "cc/paint/skottie_frame_data.h"
#include "cc/paint/skottie_resource_metadata.h"
#include "cc/paint/skottie_text_property_value.h"

namespace cc {

class SkottieWrapper;

// For each frame of a Skottie animation that uses out-of-process rasterization:
// 1) The animation's state is captured in a DrawSkottieOp object.
// 2) The object is serialized, transmitted over IPC, then deserialized.
// 3) The animation's state is updated in the target raster process.
// 4) The new frame is rendered/rasterized.
//
// SkottieSerializationHistory keeps track of past animation state that has
// been serialized and transmitted for rasterization. This allows the pipeline
// to be optimized such that only "new"/"updated" animation state is transmitted
// for each frame rather than re-transmitting everything. In other words, step
// 2) can use this history to filter out existing animation state that is
// already reflected in the target process from a previous frame in step 3).
//
// This class is thread-safe.
class CC_PAINT_EXPORT SkottieSerializationHistory {
 public:
  // Number of RequestInactiveAnimationsPurge() calls that must be made before
  // trying to purge stale skottie animations from history (referred to as
  // doing a "purge check"). If an animation has had no activity since the last
  // purge check, it gets deleted.
  static constexpr int kDefaultPurgePeriod = 1000;

  explicit SkottieSerializationHistory(int purge_period = kDefaultPurgePeriod);
  SkottieSerializationHistory(const SkottieSerializationHistory&) = delete;
  SkottieSerializationHistory& operator=(const SkottieSerializationHistory&) =
      delete;
  ~SkottieSerializationHistory();

  // Given the set of |images| and the |text_map| in the |skottie| animation's
  // new frame, filter out the entries whose contents have not changed since the
  // last frame. If an entry *has* changed, it is kept in its corresponding
  // output argument and the history is updated internally.
  void FilterNewSkottieFrameState(const SkottieWrapper& skottie,
                                  SkottieFrameDataMap& images,
                                  SkottieTextPropertyValueMap& text_map);

  // Purges the history of any Skottie animations that have been inactive for
  // a while. Although an animation's history is rather light-weight, this is
  // done as a safeguard to prevent accumulating stale data in the long run.
  //
  // Note this method is intentionally designed to be called frequently; it's
  // cheap and does not trigger an actual purge most of the time. Ideally, an
  // animation's history would be automatically purged when the animation
  // finishes, but that is difficult to observe at this layer of the code.
  void RequestInactiveAnimationsPurge();

 private:
  // Uniquely identifies the contents of a SkottieFrameData instance (used to
  // tell when an image asset's contents change from one animation frame to
  // another).
  struct SkottieFrameDataId {
    explicit SkottieFrameDataId(const SkottieFrameData& frame_data);

    bool operator==(const SkottieFrameDataId& other) const;
    bool operator!=(const SkottieFrameDataId& other) const;

    // Only store the id. Storing the whole PaintImage is doable but can
    // potentially keep some ref-counted members alive longer than necessary. So
    // just store the bare minimum to identify the image.
    PaintImage::Id paint_image_id;
    PaintFlags::FilterQuality quality;
  };

  // History for an individual SkottieWrapper (animation instance).
  class SkottieWrapperHistory {
   public:
    static constexpr int kInitialSequenceId = 1;

    SkottieWrapperHistory(const SkottieFrameDataMap& initial_images,
                          const SkottieTextPropertyValueMap& initial_text_map);
    SkottieWrapperHistory(const SkottieWrapperHistory& other);
    SkottieWrapperHistory& operator=(const SkottieWrapperHistory& other);
    ~SkottieWrapperHistory();

    void FilterNewState(SkottieFrameDataMap& images,
                        SkottieTextPropertyValueMap& text_map);

    // The "sequence_id" is incremented each time the caller tries to update an
    // animation's history, regardless of whether its history is ultimately
    // mutated. This is just used an indicator that the animation is still alive
    // and playing, not for judging when its state has changed.
    int current_sequence_id() const { return current_sequence_id_; }
    int sequence_id_at_last_purge_check() const {
      return sequence_id_at_last_purge_check_;
    }
    void update_sequence_id_at_last_purge_check() {
      sequence_id_at_last_purge_check_ = current_sequence_id_;
    }

   private:
    void FilterNewFrameImages(SkottieFrameDataMap& images);
    void FilterNewTextPropertyValues(SkottieTextPropertyValueMap& text_map);

    int current_sequence_id_ = kInitialSequenceId;
    int sequence_id_at_last_purge_check_ = kInitialSequenceId;
    base::flat_map<SkottieResourceIdHash, SkottieFrameDataId>
        last_frame_data_per_asset_;
    SkottieTextPropertyValueMap accumulated_text_map_;
  };

  base::Lock mutex_;
  base::flat_map</*SkottieWrapper id*/ uint32_t, SkottieWrapperHistory>
      history_per_animation_ GUARDED_BY(mutex_);
  int GUARDED_BY(mutex_) purge_period_counter_ = 0;
  const int purge_period_;
};

}  // namespace cc

#endif  // CC_PAINT_SKOTTIE_SERIALIZATION_HISTORY_H_