| 1 | // Copyright 2013 The Flutter 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 | |
| 5 | #ifndef FLUTTER_FLOW_DIFF_CONTEXT_H_ |
| 6 | #define FLUTTER_FLOW_DIFF_CONTEXT_H_ |
| 7 | |
| 8 | #include <functional> |
| 9 | #include <map> |
| 10 | #include <optional> |
| 11 | #include <vector> |
| 12 | #include "display_list/utils/dl_matrix_clip_tracker.h" |
| 13 | #include "flutter/flow/paint_region.h" |
| 14 | #include "flutter/fml/macros.h" |
| 15 | #include "third_party/skia/include/core/SkM44.h" |
| 16 | #include "third_party/skia/include/core/SkMatrix.h" |
| 17 | #include "third_party/skia/include/core/SkRect.h" |
| 18 | |
| 19 | namespace flutter { |
| 20 | |
| 21 | class Layer; |
| 22 | |
| 23 | // Represents area that needs to be updated in front buffer (frame_damage) and |
| 24 | // area that is going to be painted to in back buffer (buffer_damage). |
| 25 | struct Damage { |
| 26 | // This is the damage between current and previous frame; |
| 27 | // If embedder supports partial update, this is the region that needs to be |
| 28 | // repainted. |
| 29 | // Corresponds to "surface damage" from EGL_KHR_partial_update. |
| 30 | SkIRect frame_damage; |
| 31 | |
| 32 | // Reflects actual change to target framebuffer; This is frame_damage + |
| 33 | // damage previously acumulated for target framebuffer. |
| 34 | // All drawing will be clipped to this region. Knowing the affected area |
| 35 | // upfront may be useful for tile based GPUs. |
| 36 | // Corresponds to "buffer damage" from EGL_KHR_partial_update. |
| 37 | SkIRect buffer_damage; |
| 38 | }; |
| 39 | |
| 40 | // Layer Unique Id to PaintRegion |
| 41 | using PaintRegionMap = std::map<uint64_t, PaintRegion>; |
| 42 | |
| 43 | // Tracks state during tree diffing process and computes resulting damage |
| 44 | class DiffContext { |
| 45 | public: |
| 46 | explicit DiffContext(SkISize frame_size, |
| 47 | PaintRegionMap& this_frame_paint_region_map, |
| 48 | const PaintRegionMap& last_frame_paint_region_map, |
| 49 | bool has_raster_cache, |
| 50 | bool impeller_enabled); |
| 51 | |
| 52 | // Starts a new subtree. |
| 53 | void BeginSubtree(); |
| 54 | |
| 55 | // Ends current subtree; All modifications to state (transform, cullrect, |
| 56 | // dirty) will be restored |
| 57 | void EndSubtree(); |
| 58 | |
| 59 | // Creates subtree in current scope and closes it on scope exit |
| 60 | class AutoSubtreeRestore { |
| 61 | FML_DISALLOW_COPY_ASSIGN_AND_MOVE(AutoSubtreeRestore); |
| 62 | |
| 63 | public: |
| 64 | explicit AutoSubtreeRestore(DiffContext* context) : context_(context) { |
| 65 | context->BeginSubtree(); |
| 66 | } |
| 67 | ~AutoSubtreeRestore() { context_->EndSubtree(); } |
| 68 | |
| 69 | private: |
| 70 | DiffContext* context_; |
| 71 | }; |
| 72 | |
| 73 | // Pushes additional transform for current subtree |
| 74 | void PushTransform(const SkMatrix& transform); |
| 75 | void PushTransform(const SkM44& transform); |
| 76 | |
| 77 | // Pushes cull rect for current subtree |
| 78 | bool PushCullRect(const SkRect& clip); |
| 79 | |
| 80 | // Function that adjusts layer bounds (in device coordinates) depending |
| 81 | // on filter. |
| 82 | using FilterBoundsAdjustment = std::function<SkRect(SkRect)>; |
| 83 | |
| 84 | // Pushes filter bounds adjustment to current subtree. Every layer in this |
| 85 | // subtree will have bounds adjusted by this function. |
| 86 | void PushFilterBoundsAdjustment(const FilterBoundsAdjustment& filter); |
| 87 | |
| 88 | // Instruct DiffContext that current layer will paint with integral transform. |
| 89 | void WillPaintWithIntegralTransform() { state_.integral_transform = true; } |
| 90 | |
| 91 | // Returns current transform as SkMatrix. |
| 92 | SkMatrix GetTransform3x3() const; |
| 93 | |
| 94 | // Return cull rect for current subtree (in local coordinates). |
| 95 | SkRect GetCullRect() const; |
| 96 | |
| 97 | // Sets the dirty flag on current subtree. |
| 98 | // |
| 99 | // previous_paint_region, which should represent region of previous subtree |
| 100 | // at this level will be added to damage area. |
| 101 | // |
| 102 | // Each paint region added to dirty subtree (through AddPaintRegion) is also |
| 103 | // added to damage. |
| 104 | void MarkSubtreeDirty( |
| 105 | const PaintRegion& previous_paint_region = PaintRegion()); |
| 106 | void MarkSubtreeDirty(const SkRect& previous_paint_region); |
| 107 | |
| 108 | bool IsSubtreeDirty() const { return state_.dirty; } |
| 109 | |
| 110 | // Marks that current subtree contains a TextureLayer. This is needed to |
| 111 | // ensure that we'll Diff the TextureLayer even if inside retained layer. |
| 112 | void MarkSubtreeHasTextureLayer(); |
| 113 | |
| 114 | // Add layer bounds to current paint region; rect is in "local" (layer) |
| 115 | // coordinates. |
| 116 | void AddLayerBounds(const SkRect& rect); |
| 117 | |
| 118 | // Add entire paint region of retained layer for current subtree. This can |
| 119 | // only be used in subtrees that are not dirty, otherwise ancestor transforms |
| 120 | // or clips may result in different paint region. |
| 121 | void AddExistingPaintRegion(const PaintRegion& region); |
| 122 | |
| 123 | // The idea of readback region is that if any part of the readback region |
| 124 | // needs to be repainted, then the whole readback region must be repainted; |
| 125 | // |
| 126 | // Readback rect is in screen coordinates. |
| 127 | void AddReadbackRegion(const SkIRect& rect); |
| 128 | |
| 129 | // Returns the paint region for current subtree; Each rect in paint region is |
| 130 | // in screen coordinates; Once a layer accumulates the paint regions of its |
| 131 | // children, this PaintRegion value can be associated with the current layer |
| 132 | // using DiffContext::SetLayerPaintRegion. |
| 133 | PaintRegion CurrentSubtreeRegion() const; |
| 134 | |
| 135 | // Computes final damage |
| 136 | // |
| 137 | // additional_damage is the previously accumulated frame_damage for |
| 138 | // current framebuffer |
| 139 | // |
| 140 | // clip_alignment controls the alignment of resulting frame and surface |
| 141 | // damage. |
| 142 | Damage ComputeDamage(const SkIRect& additional_damage, |
| 143 | int horizontal_clip_alignment = 0, |
| 144 | int vertical_clip_alignment = 0) const; |
| 145 | |
| 146 | // Adds the region to current damage. Used for removed layers, where instead |
| 147 | // of diffing the layer its paint region is direcly added to damage. |
| 148 | void AddDamage(const PaintRegion& damage); |
| 149 | |
| 150 | // Associates the paint region with specified layer and current layer tree. |
| 151 | // The paint region can not be stored directly in layer itself, because same |
| 152 | // retained layer instance can possibly paint in different locations depending |
| 153 | // on ancestor layers. |
| 154 | void SetLayerPaintRegion(const Layer* layer, const PaintRegion& region); |
| 155 | |
| 156 | // Retrieves the paint region associated with specified layer and previous |
| 157 | // frame layer tree. |
| 158 | PaintRegion GetOldLayerPaintRegion(const Layer* layer) const; |
| 159 | |
| 160 | // Whether or not a raster cache is being used. If so, we must snap |
| 161 | // all transformations to physical pixels if the layer may be raster |
| 162 | // cached. |
| 163 | bool has_raster_cache() const { return has_raster_cache_; } |
| 164 | |
| 165 | bool impeller_enabled() const { return impeller_enabled_; } |
| 166 | |
| 167 | class Statistics { |
| 168 | public: |
| 169 | // Picture replaced by different picture |
| 170 | void AddNewPicture() { ++new_pictures_; } |
| 171 | |
| 172 | // Picture that would require deep comparison but was considered too complex |
| 173 | // to serialize and thus was treated as new picture |
| 174 | void AddPictureTooComplexToCompare() { ++pictures_too_complex_to_compare_; } |
| 175 | |
| 176 | // Picture that has identical instance between frames |
| 177 | void AddSameInstancePicture() { ++same_instance_pictures_; }; |
| 178 | |
| 179 | // Picture that had to be serialized to compare for equality |
| 180 | void AddDeepComparePicture() { ++deep_compare_pictures_; } |
| 181 | |
| 182 | // Picture that had to be serialized to compare (different instances), |
| 183 | // but were equal |
| 184 | void AddDifferentInstanceButEqualPicture() { |
| 185 | ++different_instance_but_equal_pictures_; |
| 186 | }; |
| 187 | |
| 188 | // Logs the statistics to trace counter |
| 189 | void LogStatistics(); |
| 190 | |
| 191 | private: |
| 192 | int new_pictures_ = 0; |
| 193 | int pictures_too_complex_to_compare_ = 0; |
| 194 | int same_instance_pictures_ = 0; |
| 195 | int deep_compare_pictures_ = 0; |
| 196 | int different_instance_but_equal_pictures_ = 0; |
| 197 | }; |
| 198 | |
| 199 | Statistics& statistics() { return statistics_; } |
| 200 | |
| 201 | SkRect MapRect(const SkRect& rect); |
| 202 | |
| 203 | private: |
| 204 | struct State { |
| 205 | State(); |
| 206 | |
| 207 | bool dirty; |
| 208 | |
| 209 | size_t rect_index; |
| 210 | |
| 211 | // In order to replicate paint process closely, DiffContext needs to take |
| 212 | // into account that some layers are painted with transform translation |
| 213 | // snapped to integral coordinates. |
| 214 | // |
| 215 | // It's not possible to simply snap the transform itself, because culling |
| 216 | // needs to happen with original (unsnapped) transform, just like it does |
| 217 | // during paint. This means the integral coordinates must be applied after |
| 218 | // culling before painting the layer content (either the layer itself, or |
| 219 | // when starting subtree to paint layer children). |
| 220 | bool integral_transform; |
| 221 | |
| 222 | // Used to restoring clip tracker when popping state. |
| 223 | int clip_tracker_save_count; |
| 224 | |
| 225 | // Whether this subtree has filter bounds adjustment function. If so, |
| 226 | // it will need to be removed from stack when subtree is closed. |
| 227 | bool has_filter_bounds_adjustment; |
| 228 | |
| 229 | // Whether there is a texture layer in this subtree. |
| 230 | bool has_texture; |
| 231 | }; |
| 232 | |
| 233 | void MakeCurrentTransformIntegral(); |
| 234 | |
| 235 | DisplayListMatrixClipTracker clip_tracker_; |
| 236 | std::shared_ptr<std::vector<SkRect>> rects_; |
| 237 | State state_; |
| 238 | SkISize frame_size_; |
| 239 | std::vector<State> state_stack_; |
| 240 | std::vector<FilterBoundsAdjustment> filter_bounds_adjustment_stack_; |
| 241 | |
| 242 | // Applies the filter bounds adjustment stack on provided rect. |
| 243 | // Rect must be in device coordinates. |
| 244 | SkRect ApplyFilterBoundsAdjustment(SkRect rect) const; |
| 245 | |
| 246 | SkRect damage_ = SkRect::MakeEmpty(); |
| 247 | |
| 248 | PaintRegionMap& this_frame_paint_region_map_; |
| 249 | const PaintRegionMap& last_frame_paint_region_map_; |
| 250 | bool has_raster_cache_; |
| 251 | bool impeller_enabled_; |
| 252 | |
| 253 | void AddDamage(const SkRect& rect); |
| 254 | |
| 255 | void AlignRect(SkIRect& rect, |
| 256 | int horizontal_alignment, |
| 257 | int vertical_clip_alignment) const; |
| 258 | |
| 259 | struct Readback { |
| 260 | // Index of rects_ entry that this readback belongs to. Used to |
| 261 | // determine if subtree has any readback |
| 262 | size_t position; |
| 263 | |
| 264 | // readback area, in screen coordinates |
| 265 | SkIRect rect; |
| 266 | }; |
| 267 | |
| 268 | std::vector<Readback> readbacks_; |
| 269 | Statistics statistics_; |
| 270 | }; |
| 271 | |
| 272 | } // namespace flutter |
| 273 | |
| 274 | #endif // FLUTTER_FLOW_DIFF_CONTEXT_H_ |
| 275 | |