| // Copyright 2013 The Flutter 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 FLUTTER_FLOW_DIFF_CONTEXT_H_ |
| #define FLUTTER_FLOW_DIFF_CONTEXT_H_ |
| |
| #include <functional> |
| #include <map> |
| #include <optional> |
| #include <vector> |
| #include "display_list/utils/dl_matrix_clip_tracker.h" |
| #include "flutter/flow/paint_region.h" |
| #include "flutter/fml/macros.h" |
| #include "third_party/skia/include/core/SkMatrix.h" |
| #include "third_party/skia/include/core/SkRect.h" |
| |
| namespace flutter { |
| |
| class Layer; |
| |
| // Represents area that needs to be updated in front buffer (frame_damage) and |
| // area that is going to be painted to in back buffer (buffer_damage). |
| struct Damage { |
| // This is the damage between current and previous frame; |
| // If embedder supports partial update, this is the region that needs to be |
| // repainted. |
| // Corresponds to "surface damage" from EGL_KHR_partial_update. |
| SkIRect frame_damage; |
| |
| // Reflects actual change to target framebuffer; This is frame_damage + |
| // damage previously acumulated for target framebuffer. |
| // All drawing will be clipped to this region. Knowing the affected area |
| // upfront may be useful for tile based GPUs. |
| // Corresponds to "buffer damage" from EGL_KHR_partial_update. |
| SkIRect buffer_damage; |
| }; |
| |
| // Layer Unique Id to PaintRegion |
| using PaintRegionMap = std::map<uint64_t, PaintRegion>; |
| |
| // Tracks state during tree diffing process and computes resulting damage |
| class DiffContext { |
| public: |
| explicit DiffContext(SkISize frame_size, |
| double device_pixel_aspect_ratio, |
| PaintRegionMap& this_frame_paint_region_map, |
| const PaintRegionMap& last_frame_paint_region_map, |
| bool has_raster_cache); |
| |
| // Starts a new subtree. |
| void BeginSubtree(); |
| |
| // Ends current subtree; All modifications to state (transform, cullrect, |
| // dirty) will be restored |
| void EndSubtree(); |
| |
| // Creates subtree in current scope and closes it on scope exit |
| class AutoSubtreeRestore { |
| FML_DISALLOW_COPY_ASSIGN_AND_MOVE(AutoSubtreeRestore); |
| |
| public: |
| explicit AutoSubtreeRestore(DiffContext* context) : context_(context) { |
| context->BeginSubtree(); |
| } |
| ~AutoSubtreeRestore() { context_->EndSubtree(); } |
| |
| private: |
| DiffContext* context_; |
| }; |
| |
| // Pushes additional transform for current subtree |
| void PushTransform(const SkMatrix& transform); |
| |
| // Pushes cull rect for current subtree |
| bool PushCullRect(const SkRect& clip); |
| |
| // Function that adjusts layer bounds (in device coordinates) depending |
| // on filter. |
| using FilterBoundsAdjustment = std::function<SkRect(SkRect)>; |
| |
| // Pushes filter bounds adjustment to current subtree. Every layer in this |
| // subtree will have bounds adjusted by this function. |
| void PushFilterBoundsAdjustment(const FilterBoundsAdjustment& filter); |
| |
| // Instruct DiffContext that current layer will paint with integral transform. |
| void WillPaintWithIntegralTransform() { state_.integral_transform = true; } |
| |
| // Returns current transform as SkMatrix. |
| SkMatrix GetTransform3x3() const; |
| |
| // Return cull rect for current subtree (in local coordinates). |
| SkRect GetCullRect() const; |
| |
| // Sets the dirty flag on current subtree. |
| // |
| // previous_paint_region, which should represent region of previous subtree |
| // at this level will be added to damage area. |
| // |
| // Each paint region added to dirty subtree (through AddPaintRegion) is also |
| // added to damage. |
| void MarkSubtreeDirty( |
| const PaintRegion& previous_paint_region = PaintRegion()); |
| void MarkSubtreeDirty(const SkRect& previous_paint_region); |
| |
| bool IsSubtreeDirty() const { return state_.dirty; } |
| |
| // Marks that current subtree contains a TextureLayer. This is needed to |
| // ensure that we'll Diff the TextureLayer even if inside retained layer. |
| void MarkSubtreeHasTextureLayer(); |
| |
| // Add layer bounds to current paint region; rect is in "local" (layer) |
| // coordinates. |
| void AddLayerBounds(const SkRect& rect); |
| |
| // Add entire paint region of retained layer for current subtree. This can |
| // only be used in subtrees that are not dirty, otherwise ancestor transforms |
| // or clips may result in different paint region. |
| void AddExistingPaintRegion(const PaintRegion& region); |
| |
| // The idea of readback region is that if any part of the readback region |
| // needs to be repainted, then the whole readback region must be repainted; |
| // |
| // Readback rect is in screen coordinates. |
| void AddReadbackRegion(const SkIRect& rect); |
| |
| // Returns the paint region for current subtree; Each rect in paint region is |
| // in screen coordinates; Once a layer accumulates the paint regions of its |
| // children, this PaintRegion value can be associated with the current layer |
| // using DiffContext::SetLayerPaintRegion. |
| PaintRegion CurrentSubtreeRegion() const; |
| |
| // Computes final damage |
| // |
| // additional_damage is the previously accumulated frame_damage for |
| // current framebuffer |
| // |
| // clip_alignment controls the alignment of resulting frame and surface |
| // damage. |
| Damage ComputeDamage(const SkIRect& additional_damage, |
| int horizontal_clip_alignment = 0, |
| int vertical_clip_alignment = 0) const; |
| |
| double frame_device_pixel_ratio() const { return frame_device_pixel_ratio_; }; |
| |
| // Adds the region to current damage. Used for removed layers, where instead |
| // of diffing the layer its paint region is direcly added to damage. |
| void AddDamage(const PaintRegion& damage); |
| |
| // Associates the paint region with specified layer and current layer tree. |
| // The paint region can not be stored directly in layer itself, because same |
| // retained layer instance can possibly paint in different locations depending |
| // on ancestor layers. |
| void SetLayerPaintRegion(const Layer* layer, const PaintRegion& region); |
| |
| // Retrieves the paint region associated with specified layer and previous |
| // frame layer tree. |
| PaintRegion GetOldLayerPaintRegion(const Layer* layer) const; |
| |
| // Whether or not a raster cache is being used. If so, we must snap |
| // all transformations to physical pixels if the layer may be raster |
| // cached. |
| bool has_raster_cache() const { return has_raster_cache_; } |
| |
| class Statistics { |
| public: |
| // Picture replaced by different picture |
| void AddNewPicture() { ++new_pictures_; } |
| |
| // Picture that would require deep comparison but was considered too complex |
| // to serialize and thus was treated as new picture |
| void AddPictureTooComplexToCompare() { ++pictures_too_complex_to_compare_; } |
| |
| // Picture that has identical instance between frames |
| void AddSameInstancePicture() { ++same_instance_pictures_; }; |
| |
| // Picture that had to be serialized to compare for equality |
| void AddDeepComparePicture() { ++deep_compare_pictures_; } |
| |
| // Picture that had to be serialized to compare (different instances), |
| // but were equal |
| void AddDifferentInstanceButEqualPicture() { |
| ++different_instance_but_equal_pictures_; |
| }; |
| |
| // Logs the statistics to trace counter |
| void LogStatistics(); |
| |
| private: |
| int new_pictures_ = 0; |
| int pictures_too_complex_to_compare_ = 0; |
| int same_instance_pictures_ = 0; |
| int deep_compare_pictures_ = 0; |
| int different_instance_but_equal_pictures_ = 0; |
| }; |
| |
| Statistics& statistics() { return statistics_; } |
| |
| SkRect MapRect(const SkRect& rect); |
| |
| private: |
| struct State { |
| State(); |
| |
| bool dirty; |
| |
| size_t rect_index; |
| |
| // In order to replicate paint process closely, DiffContext needs to take |
| // into account that some layers are painted with transform translation |
| // snapped to integral coordinates. |
| // |
| // It's not possible to simply snap the transform itself, because culling |
| // needs to happen with original (unsnapped) transform, just like it does |
| // during paint. This means the integral coordinates must be applied after |
| // culling before painting the layer content (either the layer itself, or |
| // when starting subtree to paint layer children). |
| bool integral_transform; |
| |
| // Used to restoring clip tracker when popping state. |
| int clip_tracker_save_count; |
| |
| // Whether this subtree has filter bounds adjustment function. If so, |
| // it will need to be removed from stack when subtree is closed. |
| bool has_filter_bounds_adjustment; |
| |
| // Whether there is a texture layer in this subtree. |
| bool has_texture; |
| }; |
| |
| void MakeCurrentTransformIntegral(); |
| |
| DisplayListMatrixClipTracker clip_tracker_; |
| std::shared_ptr<std::vector<SkRect>> rects_; |
| State state_; |
| SkISize frame_size_; |
| double frame_device_pixel_ratio_; |
| std::vector<State> state_stack_; |
| std::vector<FilterBoundsAdjustment> filter_bounds_adjustment_stack_; |
| |
| // Applies the filter bounds adjustment stack on provided rect. |
| // Rect must be in device coordinates. |
| SkRect ApplyFilterBoundsAdjustment(SkRect rect) const; |
| |
| SkRect damage_ = SkRect::MakeEmpty(); |
| |
| PaintRegionMap& this_frame_paint_region_map_; |
| const PaintRegionMap& last_frame_paint_region_map_; |
| bool has_raster_cache_; |
| |
| void AddDamage(const SkRect& rect); |
| |
| void AlignRect(SkIRect& rect, |
| int horizontal_alignment, |
| int vertical_clip_alignment) const; |
| |
| struct Readback { |
| // Index of rects_ entry that this readback belongs to. Used to |
| // determine if subtree has any readback |
| size_t position; |
| |
| // readback area, in screen coordinates |
| SkIRect rect; |
| }; |
| |
| std::vector<Readback> readbacks_; |
| Statistics statistics_; |
| }; |
| |
| } // namespace flutter |
| |
| #endif // FLUTTER_FLOW_DIFF_CONTEXT_H_ |