blob: 781ededdef99d8bce0ec2d07aac9811f2469faff [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_PAINT_ARTIFACT_COMPOSITOR_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_PAINT_ARTIFACT_COMPOSITOR_H_
#include <memory>
#include "base/dcheck_is_on.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "cc/layers/content_layer_client.h"
#include "cc/layers/layer_collections.h"
#include "cc/layers/picture_layer.h"
#include "cc/trees/property_tree.h"
#include "third_party/blink/renderer/platform/graphics/compositing/layers_as_json.h"
#include "third_party/blink/renderer/platform/graphics/compositing/pending_layer.h"
#include "third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h"
#include "third_party/blink/renderer/platform/graphics/compositing_reasons.h"
#include "third_party/blink/renderer/platform/graphics/lcd_text_preference.h"
#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace cc {
class ViewTransitionRequest;
}
namespace blink {
class ContentLayerClientImpl;
class JSONObject;
class SynthesizedClip;
using CompositorScrollCallbacks = cc::ScrollCallbacks;
class LayerListBuilder {
public:
void Add(scoped_refptr<cc::Layer>);
cc::LayerList Finalize();
private:
// The list becomes invalid once |Finalize| is called.
bool list_valid_ = true;
cc::LayerList list_;
HashSet<int> layer_ids_;
};
// This class maintains unique stable cc effect IDs (and optionally a persistent
// mask layer) for reuse across compositing cycles. The mask layer paints a
// rounded rect, which is an updatable parameter of the class. The caller is
// responsible for inserting the mask layer into layer list and associating with
// property nodes. The mask layer may be omitted if the caller determines it is
// not necessary (e.g. because there is no content to mask).
//
// The typical application of the mask layer is to create an isolating effect
// node to paint the clipped contents, and at the end draw the mask layer with
// a kDstIn blend effect. This is why two stable cc effect IDs are provided.
// Even if the mask layer is not present, it's important for the isolation
// effect node to be stable, to minimize render surface damage.
class SynthesizedClip : private cc::ContentLayerClient {
public:
SynthesizedClip() : layer_(nullptr) {
mask_isolation_id_ =
CompositorElementIdFromUniqueObjectId(NewUniqueObjectId());
mask_effect_id_ =
CompositorElementIdFromUniqueObjectId(NewUniqueObjectId());
}
~SynthesizedClip() override {
if (layer_)
layer_->ClearClient();
}
void UpdateLayer(const ClipPaintPropertyNode&,
const TransformPaintPropertyNode&);
cc::PictureLayer* Layer() { return layer_.get(); }
CompositorElementId GetMaskIsolationId() const { return mask_isolation_id_; }
CompositorElementId GetMaskEffectId() const { return mask_effect_id_; }
private:
// ContentLayerClient implementation.
scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList() final;
bool FillsBoundsCompletely() const final { return false; }
private:
scoped_refptr<cc::PictureLayer> layer_;
gfx::Transform projection_;
bool rrect_is_local_ = false;
SkRRect rrect_;
std::optional<Path> path_;
CompositorElementId mask_isolation_id_;
CompositorElementId mask_effect_id_;
};
// Responsible for managing compositing in terms of a PaintArtifact.
//
// Owns a subtree of the compositor layer tree, and updates it in response to
// changes in the paint artifact.
class PLATFORM_EXPORT PaintArtifactCompositor final
: public GarbageCollected<PaintArtifactCompositor>,
private PropertyTreeManagerClient {
public:
PaintArtifactCompositor(
base::WeakPtr<CompositorScrollCallbacks> scroll_callbacks);
PaintArtifactCompositor(const PaintArtifactCompositor&) = delete;
PaintArtifactCompositor& operator=(const PaintArtifactCompositor&) = delete;
~PaintArtifactCompositor() override;
void Trace(Visitor* visitor) const { visitor->Trace(pending_layers_); }
struct ViewportProperties {
STACK_ALLOCATED();
public:
const TransformPaintPropertyNode* overscroll_elasticity_transform = nullptr;
const TransformPaintPropertyNode* page_scale = nullptr;
const TransformPaintPropertyNode* inner_scroll_translation = nullptr;
const ClipPaintPropertyNode* outer_clip = nullptr;
const TransformPaintPropertyNode* outer_scroll_translation = nullptr;
};
// Updates the cc layer list and property trees to match those provided in
// |paint_chunks|.
//
// |scroll_translation_nodes| is the complete set of scroll nodes, including
// noncomposited nodes, and is used for Scroll Unification to generate scroll
// nodes for noncomposited scrollers to complete the compositor's scroll
// property tree.
void Update(
const PaintArtifact& artifact,
const ViewportProperties& viewport_properties,
const Vector<const TransformPaintPropertyNode*>& scroll_translation_nodes,
Vector<std::unique_ptr<cc::ViewTransitionRequest>> requests);
// Fast-path update where the painting of existing composited layers changed,
// but property trees and compositing decisions remain the same. See:
// |Update| for full updates.
//
// When this update can be used is tightly coupled with |Update|, see
// |SetNeedsFullUpdateAfterPaintIfNeeded| for details. For example, this
// update can be used when the color of a display item is updated. This update
// can not be used if the size of a display item increases because that could
// require different cc::layers due to changes in overlap. This update also
// can not be used if property trees change (with the exception of fast-path
// direct updates that do not change compositing such as
// |DirectlyUpdateCompositedOpacityValue|) because property tree values in
// effect and clip nodes create cc::layers (e.g., clip mask layers).
//
// This copies over the newly-painted PaintChunks to existing
// |pending_layers_|, issues raster invalidations, and updates the existing
// cc::Layer properties such as background color.
void UpdateRepaintedLayers(const PaintArtifact&);
bool DirectlyUpdateCompositedOpacityValue(const EffectPaintPropertyNode&);
bool DirectlyUpdateScrollOffsetTransform(const TransformPaintPropertyNode&);
bool DirectlyUpdateTransform(const TransformPaintPropertyNode&);
bool DirectlyUpdatePageScaleTransform(const TransformPaintPropertyNode&);
// Directly sets cc::ScrollTree::current_scroll_offset. This doesn't affect
// cc::TransformNode::scroll_offset (which will be synched with blink
// transform node in DirectlyUpdateScrollOffsetTransform() or Update()).
bool DirectlySetScrollOffset(CompositorElementId,
const gfx::PointF& scroll_offset);
uint32_t GetMainThreadScrollingReasons(const ScrollPaintPropertyNode&) const;
// Returns true if the scroll node is currently composited in cc.
bool UsesCompositedScrolling(const ScrollPaintPropertyNode&) const;
// The root layer of the tree managed by this object.
cc::Layer* RootLayer() const { return root_layer_.get(); }
void SetTracksRasterInvalidations(bool);
// Called when the local frame view that owns this compositor is
// going to be removed from its frame.
void WillBeRemovedFromFrame();
std::unique_ptr<JSONArray> GetPendingLayersAsJSON() const;
std::unique_ptr<JSONObject> GetLayersAsJSON(LayerTreeFlags) const;
#if DCHECK_IS_ON()
void ShowDebugData();
#endif
// Returns the ith ContentLayerClientImpl for testing.
ContentLayerClientImpl* ContentLayerClientForTesting(wtf_size_t i) const;
// Mark this as needing a full compositing update. Repaint-only updates that
// do not affect compositing can use a fast-path in |UpdateRepaintedLayers|
// (see comment above that function for more information), and should not call
// SetNeedsUpdate.
void SetNeedsUpdate() { needs_update_ = true; }
bool NeedsUpdate() const { return needs_update_; }
void ClearNeedsUpdateForTesting() { needs_update_ = false; }
void SetLCDTextPreference(LCDTextPreference);
// There is no mechanism for doing a paint lifecycle phase without running
// PaintArtifactCompositor::Update so this is exposed so tests can check the
// last update type.
enum class PreviousUpdateType { kNone, kRepaint, kFull };
PreviousUpdateType PreviousUpdateForTesting() const {
return previous_update_for_testing_;
}
void ClearPreviousUpdateForTesting() {
previous_update_for_testing_ = PreviousUpdateType::kNone;
}
void SetNeedsFullUpdateAfterPaintIfNeeded(const PaintArtifact& previous,
const PaintArtifact& repainted);
// Returns true if a property tree node associated with |element_id| exists
// on any of the PropertyTrees constructed by |Update|.
bool HasComposited(CompositorElementId element_id) const;
void SetLayerDebugInfoEnabled(bool);
Vector<cc::Layer*> SynthesizedClipLayersForTesting() const;
size_t ApproximateUnsharedMemoryUsage() const;
// Invalidates the scrollbar layer. Returns true if the scrollbar layer is
// found by `element_id`.
bool SetScrollbarNeedsDisplay(CompositorElementId element_id);
// Sets color for solid color scrollbar layer. Returns true if the scrollbar
// layer is found by `element_id`.
bool SetScrollbarSolidColor(CompositorElementId element_id, SkColor4f color);
bool ShouldAlwaysUpdateOnScroll() const {
return should_always_update_on_scroll_;
}
private:
void UpdateCompositorViewportProperties(const ViewportProperties&,
PropertyTreeManager&,
cc::LayerTreeHost*);
// Collects the PaintChunks into groups which will end up in the same
// cc layer. This is the entry point of the layerization algorithm.
void CollectPendingLayers(const PaintArtifact&);
// This is the internal recursion of CollectPendingLayers. This function
// loops over the list of paint chunks, scoped by an isolated group
// (i.e. effect node). Inside of the loop, chunks are tested for overlap
// and merge compatibility. Subgroups are handled by recursion, and will
// be tested for "decompositing" upon return.
// Merge compatibility means consecutive chunks may be layerized into the
// same backing (i.e. merged) if their property states don't cross
// direct-compositing boundary.
// Non-consecutive chunks that are nevertheless compatible may still be
// merged, if reordering of the chunks won't affect the ultimate result.
// This is determined by overlap testing such that chunks can be safely
// reordered if their effective bounds in screen space can't overlap.
// The recursion only tests merge & overlap for chunks scoped by the same
// group. This is where "decompositing" came in. Upon returning from a
// recursion, the layerization of the subgroup may be tested for merge &
// overlap with other chunks in the parent group, if grouping requirement
// can be satisfied (and the effect node has no direct reason).
// |directly_composited_transforms| is used internally to optimize the first
// time a paint property tree node is encountered that has direct compositing
// reasons. This case will always start a new layer and can skip merge tests.
// New values are added when transform nodes are first encountered.
void LayerizeGroup(const PaintArtifact&,
const EffectPaintPropertyNode&,
PaintChunks::const_iterator& chunk_cursor,
HashSet<const TransformPaintPropertyNode*>&
directly_composited_transforms,
bool force_draws_content);
bool DecompositeEffect(const EffectPaintPropertyNode& parent_effect,
wtf_size_t first_layer_in_parent_group_index,
const EffectPaintPropertyNode& effect,
wtf_size_t layer_index);
const TransformPaintPropertyNode& ScrollTranslationStateForLayer(
const PendingLayer&);
// if |needs_layer| is false, no cc::Layer is created, |mask_effect_id| is
// not set, and the Layer() method on the returned SynthesizedClip returns
// nullptr.
// However, |mask_isolation_id| is always set.
SynthesizedClip& CreateOrReuseSynthesizedClipLayer(
const ClipPaintPropertyNode&,
const TransformPaintPropertyNode&,
bool needs_layer,
CompositorElementId& mask_isolation_id,
CompositorElementId& mask_effect_id) final;
bool NeedsCompositedScrolling(
const TransformPaintPropertyNode& scroll_translation) const final;
bool ComputeNeedsCompositedScrolling(
const PaintArtifact&,
PaintChunks::const_iterator chunk_cursor) const;
PendingLayer::CompositingType ChunkCompositingType(const PaintArtifact&,
const PaintChunk&) const;
static void UpdateRenderSurfaceForEffects(
cc::EffectTree&,
const cc::LayerList&,
const Vector<const EffectPaintPropertyNode*>&);
bool CanDirectlyUpdateProperties() const;
CompositingReasons GetCompositingReasons(
const PendingLayer& layer,
const PropertyTreeState& previous_layer_state) const;
void UpdateDebugInfo() const;
// For notifying blink of composited scrolling.
base::WeakPtr<CompositorScrollCallbacks> scroll_callbacks_;
bool tracks_raster_invalidations_;
bool needs_update_ = true;
bool layer_debug_info_enabled_ = false;
bool should_always_update_on_scroll_ = false;
PreviousUpdateType previous_update_for_testing_ = PreviousUpdateType::kNone;
LCDTextPreference lcd_text_preference_ = LCDTextPreference::kIgnored;
scoped_refptr<cc::Layer> root_layer_;
struct SynthesizedClipEntry {
raw_ptr<const ClipPaintPropertyNode, DanglingUntriaged> key;
std::unique_ptr<SynthesizedClip> synthesized_clip;
bool in_use;
};
Vector<SynthesizedClipEntry> synthesized_clip_cache_;
class OldPendingLayerMatcher;
PendingLayers pending_layers_;
// Scroll translation nodes of the PaintArtifact that are painted.
// This member variable is only used in PaintArtifactCompositor::Update.
// The value indicates if the scroll should be composited.
HashMap<const TransformPaintPropertyNode*, bool> painted_scroll_translations_;
friend class StubChromeClientForCAP;
friend class PaintArtifactCompositorTest;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_PAINT_ARTIFACT_COMPOSITOR_H_