skottie_serialization_history.cc 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // Copyright 2022 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "cc/paint/skottie_serialization_history.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/check.h"
  8. #include "base/location.h"
  9. #include "base/logging.h"
  10. #include "cc/paint/skottie_wrapper.h"
  11. namespace cc {
  12. SkottieSerializationHistory::SkottieFrameDataId::SkottieFrameDataId(
  13. const SkottieFrameData& frame_data)
  14. : paint_image_id(frame_data.image ? frame_data.image.stable_id()
  15. : PaintImage::kInvalidId),
  16. quality(frame_data.quality) {}
  17. bool SkottieSerializationHistory::SkottieFrameDataId::operator==(
  18. const SkottieFrameDataId& other) const {
  19. return paint_image_id == other.paint_image_id && quality == other.quality;
  20. }
  21. bool SkottieSerializationHistory::SkottieFrameDataId::operator!=(
  22. const SkottieFrameDataId& other) const {
  23. return !(*this == other);
  24. }
  25. SkottieSerializationHistory::SkottieWrapperHistory::SkottieWrapperHistory(
  26. const SkottieFrameDataMap& initial_images,
  27. const SkottieTextPropertyValueMap& initial_text_map)
  28. : accumulated_text_map_(initial_text_map) {
  29. for (const auto& image_asset_pair : initial_images) {
  30. DVLOG(1) << "Received initial image for asset " << image_asset_pair.first;
  31. last_frame_data_per_asset_.emplace(
  32. /*asset id*/ image_asset_pair.first,
  33. SkottieFrameDataId(image_asset_pair.second));
  34. }
  35. }
  36. SkottieSerializationHistory::SkottieWrapperHistory::SkottieWrapperHistory(
  37. const SkottieWrapperHistory& other) = default;
  38. SkottieSerializationHistory::SkottieWrapperHistory&
  39. SkottieSerializationHistory::SkottieWrapperHistory::operator=(
  40. const SkottieWrapperHistory& other) = default;
  41. SkottieSerializationHistory::SkottieWrapperHistory::~SkottieWrapperHistory() =
  42. default;
  43. void SkottieSerializationHistory::SkottieWrapperHistory::FilterNewState(
  44. SkottieFrameDataMap& images,
  45. SkottieTextPropertyValueMap& text_map) {
  46. ++current_sequence_id_;
  47. FilterNewFrameImages(images);
  48. FilterNewTextPropertyValues(text_map);
  49. }
  50. void SkottieSerializationHistory::SkottieWrapperHistory::FilterNewFrameImages(
  51. SkottieFrameDataMap& images) {
  52. auto images_iter = images.begin();
  53. while (images_iter != images.end()) {
  54. const SkottieResourceIdHash& asset_id = images_iter->first;
  55. const SkottieFrameData& frame_data = images_iter->second;
  56. SkottieFrameDataId new_frame_data_id(frame_data);
  57. auto [result_iterator, is_new_insertion] =
  58. last_frame_data_per_asset_.emplace(asset_id, new_frame_data_id);
  59. SkottieFrameDataId& existing_frame_data_id = result_iterator->second;
  60. bool asset_has_updated_frame_data =
  61. is_new_insertion || existing_frame_data_id != new_frame_data_id;
  62. if (asset_has_updated_frame_data) {
  63. DVLOG(1) << "New image available for asset " << asset_id;
  64. existing_frame_data_id = std::move(new_frame_data_id);
  65. ++images_iter;
  66. } else {
  67. DVLOG(4) << "No update to image for asset" << asset_id;
  68. images_iter = images.erase(images_iter);
  69. }
  70. }
  71. }
  72. void SkottieSerializationHistory::SkottieWrapperHistory::
  73. FilterNewTextPropertyValues(SkottieTextPropertyValueMap& text_map_in) {
  74. auto text_map_in_iter = text_map_in.begin();
  75. while (text_map_in_iter != text_map_in.end()) {
  76. const SkottieResourceIdHash& node = text_map_in_iter->first;
  77. const SkottieTextPropertyValue& new_text_property_val =
  78. text_map_in_iter->second;
  79. auto [accumulated_iter, is_new_insertion] =
  80. accumulated_text_map_.insert(*text_map_in_iter);
  81. SkottieTextPropertyValue& old_text_property_val = accumulated_iter->second;
  82. if (!is_new_insertion && old_text_property_val == new_text_property_val) {
  83. DVLOG(4) << "No update to text property value for node" << node;
  84. text_map_in_iter = text_map_in.erase(text_map_in_iter);
  85. } else {
  86. DVLOG(1) << "New text available for node " << node;
  87. old_text_property_val = new_text_property_val;
  88. ++text_map_in_iter;
  89. }
  90. }
  91. }
  92. SkottieSerializationHistory::SkottieSerializationHistory(int purge_period)
  93. : purge_period_(purge_period) {}
  94. SkottieSerializationHistory::~SkottieSerializationHistory() = default;
  95. void SkottieSerializationHistory::FilterNewSkottieFrameState(
  96. const SkottieWrapper& skottie,
  97. SkottieFrameDataMap& images,
  98. SkottieTextPropertyValueMap& text_map) {
  99. DCHECK(skottie.is_valid());
  100. base::AutoLock lock(mutex_);
  101. auto [result_iterator, is_new_insertion] =
  102. history_per_animation_.try_emplace(skottie.id(), images, text_map);
  103. if (is_new_insertion) {
  104. DVLOG(1) << "Encountered new SkottieWrapper with id " << skottie.id()
  105. << " and " << images.size() << " images";
  106. } else {
  107. SkottieWrapperHistory& skottie_history_found = result_iterator->second;
  108. skottie_history_found.FilterNewState(images, text_map);
  109. }
  110. }
  111. void SkottieSerializationHistory::RequestInactiveAnimationsPurge() {
  112. base::AutoLock lock(mutex_);
  113. // Since RequestInactiveAnimationsPurge() is called frequently in a
  114. // time-sensitive part of the code and purging stale history is not an urgent
  115. // operation, only do a purge check once in a while. (Even then, a purge check
  116. // actually isn't that expensive)
  117. //
  118. // If there is some odd corner case where a Skottie animation's history gets
  119. // purged while it is still active somehow, user functionality will not be
  120. // broken. The animation's history will just be recreated with a clean slate
  121. // the next time its state is registered with this class.
  122. ++purge_period_counter_;
  123. if (purge_period_counter_ < purge_period_)
  124. return;
  125. purge_period_counter_ = 0;
  126. auto animation_history_iter = history_per_animation_.begin();
  127. while (animation_history_iter != history_per_animation_.end()) {
  128. SkottieWrapperHistory& skottie_wrapper_history =
  129. animation_history_iter->second;
  130. if (skottie_wrapper_history.current_sequence_id() ==
  131. skottie_wrapper_history.sequence_id_at_last_purge_check()) {
  132. DVLOG(1) << "Purging Skottie animation with id "
  133. << animation_history_iter->first
  134. << ". No update to animation's state since last purge check.";
  135. animation_history_iter =
  136. history_per_animation_.erase(animation_history_iter);
  137. } else {
  138. skottie_wrapper_history.update_sequence_id_at_last_purge_check();
  139. ++animation_history_iter;
  140. }
  141. }
  142. }
  143. } // namespace cc