// 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 #include #include #include #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/SkM44.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; // Tracks state during tree diffing process and computes resulting damage class DiffContext { public: explicit DiffContext(SkISize frame_size, PaintRegionMap& this_frame_paint_region_map, const PaintRegionMap& last_frame_paint_region_map, bool has_raster_cache, bool impeller_enabled); // 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); void PushTransform(const SkM44& 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; // 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; // // paint_rect - rectangle where the filter paints contents (in screen // coordinates) // readback_rect - rectangle where the filter samples from (in screen // coordinates) void AddReadbackRegion(const SkIRect& paint_rect, const SkIRect& readback_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; // 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_; } bool impeller_enabled() const { return impeller_enabled_; } 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 = false; size_t rect_index = 0; // 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 = false; // Current transform and clip for the layer DisplayListMatrixClipState matrix_clip; // 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 = false; // Whether there is a texture layer in this subtree. bool has_texture = false; }; void MakeTransformIntegral(DisplayListMatrixClipState& matrix_clip); std::shared_ptr> rects_; State state_; SkISize frame_size_; std::vector state_stack_; std::vector 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_; bool impeller_enabled_; 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; // Paint region of the filter performing readback, in screen coordinates. SkIRect paint_rect; // Readback area of the filter, in screen coordinates. SkIRect readback_rect; }; std::vector readbacks_; Statistics statistics_; }; } // namespace flutter #endif // FLUTTER_FLOW_DIFF_CONTEXT_H_