blob: e34d59adaf5359bacd0b3a2c10555a8579737efc [file] [log] [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_