blob: db2f311652d0221c9db595bd1c3bd762f7b6e09c [file] [log] [blame]
/*
* Copyright (C) 2009 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
#include <algorithm>
#include <cmath>
#include <memory>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/trace_event/traced_value.h"
#include "cc/layers/layer.h"
#include "cc/layers/picture_layer.h"
#include "cc/paint/display_item_list.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_size.h"
#include "third_party/blink/renderer/platform/geometry/float_rect.h"
#include "third_party/blink/renderer/platform/geometry/geometry_as_json.h"
#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
#include "third_party/blink/renderer/platform/geometry/region.h"
#include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
#include "third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h"
#include "third_party/blink/renderer/platform/graphics/compositor_filter_operations.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
#include "third_party/blink/renderer/platform/graphics/graphics_layer_tree_as_text.h"
#include "third_party/blink/renderer/platform/graphics/image.h"
#include "third_party/blink/renderer/platform/graphics/logging_canvas.h"
#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
#include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset_recorder.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
#include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h"
#include "third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h"
#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
#include "third_party/blink/renderer/platform/wtf/text/text_stream.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
GraphicsLayer::GraphicsLayer(GraphicsLayerClient& client)
: client_(client),
draws_content_(false),
paints_hit_test_(false),
contents_visible_(true),
hit_testable_(false),
needs_check_raster_invalidation_(false),
raster_invalidated_(false),
should_create_layers_after_paint_(false),
repainted_(false),
painting_phase_(kGraphicsLayerPaintAllWithOverflowClip),
parent_(nullptr),
raster_invalidation_function_(
base::BindRepeating(&GraphicsLayer::InvalidateRaster,
base::Unretained(this))) {
// TODO(crbug.com/1033240): Debugging information for the referenced bug.
// Remove when it is fixed.
CHECK(&client_);
#if DCHECK_IS_ON()
DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
client.VerifyNotPainting();
#endif
layer_ = cc::PictureLayer::Create(this);
layer_->SetIsDrawable(draws_content_ && contents_visible_);
layer_->SetHitTestable(hit_testable_);
UpdateTrackingRasterInvalidations();
}
GraphicsLayer::~GraphicsLayer() {
CcLayer().ClearClient();
contents_layer_ = nullptr;
#if DCHECK_IS_ON()
client_.VerifyNotPainting();
#endif
RemoveAllChildren();
RemoveFromParent();
DCHECK(!parent_);
// This ensures we clean-up the ElementId to cc::Layer mapping in
// LayerTreeHost before a new layer with the same ElementId is added. See
// https://crbug.com/979002 for more information.
SetElementId(CompositorElementId());
}
void GraphicsLayer::AppendAdditionalInfoAsJSON(LayerTreeFlags flags,
const cc::Layer& layer,
JSONObject& json) const {
// Only the primary layer associated with GraphicsLayer adds additional
// information. Other layer state, such as raster invalidations, don't
// disambiguate between specific layers.
if (&layer != layer_.get())
return;
if ((flags & (kLayerTreeIncludesInvalidations |
kLayerTreeIncludesDetailedInvalidations)) &&
Client().IsTrackingRasterInvalidations() &&
GetRasterInvalidationTracking()) {
GetRasterInvalidationTracking()->AsJSON(
&json, flags & kLayerTreeIncludesDetailedInvalidations);
}
GraphicsLayerPaintingPhase painting_phase = PaintingPhase();
if ((flags & kLayerTreeIncludesPaintingPhases) && painting_phase) {
auto painting_phases_json = std::make_unique<JSONArray>();
if (painting_phase & kGraphicsLayerPaintBackground)
painting_phases_json->PushString("GraphicsLayerPaintBackground");
if (painting_phase & kGraphicsLayerPaintForeground)
painting_phases_json->PushString("GraphicsLayerPaintForeground");
if (painting_phase & kGraphicsLayerPaintMask)
painting_phases_json->PushString("GraphicsLayerPaintMask");
if (painting_phase & kGraphicsLayerPaintOverflowContents)
painting_phases_json->PushString("GraphicsLayerPaintOverflowContents");
if (painting_phase & kGraphicsLayerPaintCompositedScroll)
painting_phases_json->PushString("GraphicsLayerPaintCompositedScroll");
if (painting_phase & kGraphicsLayerPaintDecoration)
painting_phases_json->PushString("GraphicsLayerPaintDecoration");
json.SetArray("paintingPhases", std::move(painting_phases_json));
}
if (flags &
(kLayerTreeIncludesDebugInfo | kLayerTreeIncludesCompositingReasons)) {
bool debug = flags & kLayerTreeIncludesDebugInfo;
{
auto squashing_disallowed_reasons_json = std::make_unique<JSONArray>();
SquashingDisallowedReasons squashing_disallowed_reasons =
GetSquashingDisallowedReasons();
auto names = debug ? SquashingDisallowedReason::Descriptions(
squashing_disallowed_reasons)
: SquashingDisallowedReason::ShortNames(
squashing_disallowed_reasons);
for (const char* name : names)
squashing_disallowed_reasons_json->PushString(name);
json.SetArray("squashingDisallowedReasons",
std::move(squashing_disallowed_reasons_json));
}
}
if (ShouldCreateLayersAfterPaint()) {
json.SetBoolean("shouldCreateLayersAfterPaint", true);
} else {
#if DCHECK_IS_ON()
if (HasLayerState() && cc_display_item_list_ &&
(flags & kLayerTreeIncludesPaintRecords)) {
LoggingCanvas canvas;
cc_display_item_list_->Raster(&canvas);
json.SetValue("paintRecord", canvas.Log());
}
#endif
}
}
void GraphicsLayer::SetParent(GraphicsLayer* layer) {
#if DCHECK_IS_ON()
DCHECK(!layer || !layer->HasAncestor(this));
#endif
parent_ = layer;
}
#if DCHECK_IS_ON()
bool GraphicsLayer::HasAncestor(GraphicsLayer* ancestor) const {
for (GraphicsLayer* curr = Parent(); curr; curr = curr->Parent()) {
if (curr == ancestor)
return true;
}
return false;
}
#endif
bool GraphicsLayer::SetChildren(const GraphicsLayerVector& new_children) {
// If the contents of the arrays are the same, nothing to do.
if (new_children == children_)
return false;
RemoveAllChildren();
for (auto* new_child : new_children)
AddChildInternal(new_child);
NotifyChildListChange();
return true;
}
void GraphicsLayer::AddChildInternal(GraphicsLayer* child_layer) {
// TODO(szager): Remove CHECK after diagnosing crbug.com/1092673
CHECK(child_layer);
DCHECK_NE(child_layer, this);
if (child_layer->Parent())
child_layer->RemoveFromParent();
child_layer->SetParent(this);
children_.push_back(child_layer);
// Don't call NotifyChildListChange here, this function is used in cases where
// it should not be called until all children are processed.
}
void GraphicsLayer::AddChild(GraphicsLayer* child_layer) {
AddChildInternal(child_layer);
NotifyChildListChange();
}
void GraphicsLayer::RemoveAllChildren() {
while (!children_.IsEmpty()) {
GraphicsLayer* cur_layer = children_.back();
DCHECK(cur_layer->Parent());
cur_layer->RemoveFromParent();
}
}
void GraphicsLayer::RemoveFromParent() {
if (parent_) {
// We use reverseFind so that removeAllChildren() isn't n^2.
parent_->children_.EraseAt(parent_->children_.ReverseFind(this));
SetParent(nullptr);
}
// cc::Layers are created and removed in PaintArtifactCompositor so ensure it
// is notified that something has changed.
client_.GraphicsLayersDidChange();
}
void GraphicsLayer::SetOffsetFromLayoutObject(const IntSize& offset) {
if (offset == offset_from_layout_object_)
return;
offset_from_layout_object_ = offset;
Invalidate(PaintInvalidationReason::kFullLayer); // As DisplayItemClient.
}
void GraphicsLayer::ClearPaintStateRecursively() {
ForAllGraphicsLayers(
*this,
[](GraphicsLayer& layer) -> bool {
layer.paint_controller_ = nullptr;
layer.raster_invalidator_ = nullptr;
return true;
},
[](GraphicsLayer&, const cc::Layer&) {});
}
bool GraphicsLayer::PaintRecursively(
GraphicsContext& context,
Vector<PreCompositedLayerInfo>& pre_composited_layers,
PaintBenchmarkMode benchmark_mode) {
bool repainted = false;
ForAllGraphicsLayers(
*this,
[&](GraphicsLayer& layer) -> bool {
if (layer.Client().ShouldSkipPaintingSubtree()) {
layer.ClearPaintStateRecursively();
return false;
}
layer.Paint(pre_composited_layers, benchmark_mode);
repainted |= layer.repainted_;
return true;
},
[&](const GraphicsLayer& layer, cc::Layer& contents_layer) {
PaintChunkSubsetRecorder subset_recorder(context.GetPaintController());
auto contents_state = layer.GetContentsPropertyTreeState();
RecordForeignLayer(
context, layer, DisplayItem::kForeignLayerContentsWrapper,
&contents_layer, layer.GetContentsOffsetFromTransformNode(),
&contents_state);
pre_composited_layers.push_back(
PreCompositedLayerInfo{subset_recorder.Get()});
});
#if DCHECK_IS_ON()
if (repainted) {
VLOG(2) << "GraphicsLayer tree:\n"
<< GraphicsLayerTreeAsTextForTesting(
this, VLOG_IS_ON(3) ? 0xffffffff : kOutputAsLayerTree)
.Utf8();
}
#endif
return repainted;
}
void GraphicsLayer::PaintForTesting(const IntRect& interest_rect) {
Vector<PreCompositedLayerInfo> pre_composited_layers;
Paint(pre_composited_layers, PaintBenchmarkMode::kNormal, &interest_rect);
}
void GraphicsLayer::Paint(Vector<PreCompositedLayerInfo>& pre_composited_layers,
PaintBenchmarkMode benchmark_mode,
const IntRect* interest_rect) {
repainted_ = false;
DCHECK(!client_.ShouldSkipPaintingSubtree());
if (!PaintsContentOrHitTest()) {
if (IsHitTestable()) {
pre_composited_layers.push_back(
PreCompositedLayerInfo{PaintChunkSubset(), this});
}
return;
}
#if !DCHECK_IS_ON()
// TODO(crbug.com/853096): Investigate why we can ever reach here without
// a valid layer state. Seems to only happen on Android builds.
// TODO(chrishtr): I think this might have been due to iframe throttling.
// The comment above and the code here was written before the early
// out if client_.ShouldThrottleRendering() was true was added. Throttled
// layers can of course have a stale or missing layer_state_.
if (!layer_state_)
return;
#endif
DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName();
IntRect new_interest_rect;
if (!RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
new_interest_rect = interest_rect ? *interest_rect
: client_.ComputeInterestRect(
this, previous_interest_rect_);
}
auto& paint_controller = GetPaintController();
PaintController::ScopedBenchmarkMode scoped_benchmark_mode(paint_controller,
benchmark_mode);
bool cached = !paint_controller.ShouldForcePaintForBenchmark() &&
!client_.NeedsRepaint(*this) &&
// TODO(wangxianzhu): This will be replaced by subsequence
// caching when unifying PaintController.
paint_controller.ClientCacheIsValid(*this) &&
previous_interest_rect_ == new_interest_rect;
if (!cached) {
GraphicsContext context(paint_controller);
DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName();
paint_controller.UpdateCurrentPaintChunkProperties(
nullptr, layer_state_->state.GetPropertyTreeState());
// If this uses pre-CAP compositing, contents_opaque will be calculated by
// CompositedLayerMapping; otherwise, it is calculated by PaintChunker.
paint_controller.SetShouldComputeContentsOpaque(
ShouldCreateLayersAfterPaint());
previous_interest_rect_ = new_interest_rect;
client_.PaintContents(this, context, painting_phase_, new_interest_rect);
paint_controller.CommitNewDisplayItems();
// TODO(wangxianzhu): Remove this and friend class in DisplayItemClient
// when unifying PaintController.
Validate();
DVLOG(2) << "Painted GraphicsLayer: " << DebugName()
<< " paintable region: " << PaintableRegion().ToString();
}
PaintChunkSubset chunks(paint_controller.GetPaintArtifactShared());
pre_composited_layers.push_back(PreCompositedLayerInfo{chunks, this});
if (cached && !needs_check_raster_invalidation_ &&
paint_controller.GetBenchmarkMode() !=
PaintBenchmarkMode::kForceRasterInvalidationAndConvert) {
return;
}
if (!ShouldCreateLayersAfterPaint()) {
auto& raster_invalidator = EnsureRasterInvalidator();
gfx::Size old_layer_size = raster_invalidator.LayerBounds().size();
gfx::Rect layer_bounds(layer_state_->offset, Size());
PropertyTreeState property_tree_state = GetPropertyTreeState().Unalias();
EnsureRasterInvalidator().Generate(raster_invalidation_function_, chunks,
layer_bounds, property_tree_state, this);
base::Optional<RasterUnderInvalidationCheckingParams>
raster_under_invalidation_params;
if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() &&
PaintsContentOrHitTest()) {
raster_under_invalidation_params.emplace(
EnsureRasterInvalidator().EnsureTracking(),
IntRect(PaintableRegion()), DebugName());
}
// If nothing changed in the layer, keep the original display item list.
// Here check layer_bounds because RasterInvalidator doesn't issue raster
// invalidation when only layer_bounds change.
if (raster_invalidated_ || !cc_display_item_list_ ||
old_layer_size != Size() || raster_under_invalidation_params) {
cc_display_item_list_ = PaintChunksToCcLayer::Convert(
chunks, property_tree_state,
gfx::Vector2dF(layer_state_->offset.X(), layer_state_->offset.Y()),
cc::DisplayItemList::kTopLevelDisplayItemList,
base::OptionalOrNullptr(raster_under_invalidation_params));
raster_invalidated_ = false;
}
}
needs_check_raster_invalidation_ = false;
repainted_ = true;
}
void GraphicsLayer::SetShouldCreateLayersAfterPaint(
bool should_create_layers_after_paint) {
DCHECK(RuntimeEnabledFeatures::CompositeSVGEnabled());
if (should_create_layers_after_paint != should_create_layers_after_paint_) {
should_create_layers_after_paint_ = should_create_layers_after_paint;
// Depending on |should_create_layers_after_paint_|, raster invalidation
// will happen in via two different code paths. When it changes we need to
// fully invalidate because the incremental raster invalidations of these
// code paths will not work.
if (raster_invalidator_)
raster_invalidator_->ClearOldStates();
}
}
void GraphicsLayer::NotifyChildListChange() {
client_.GraphicsLayersDidChange();
}
void GraphicsLayer::UpdateLayerIsDrawable() {
// For the rest of the accelerated compositor code, there is no reason to make
// a distinction between drawsContent and contentsVisible. So, for
// m_layer->layer(), these two flags are combined here. |m_contentsLayer|
// shouldn't receive the drawsContent flag, so it is only given
// contentsVisible.
CcLayer().SetIsDrawable(draws_content_ && contents_visible_);
if (contents_layer_)
contents_layer_->SetIsDrawable(contents_visible_);
if (draws_content_)
CcLayer().SetNeedsDisplay();
}
void GraphicsLayer::UpdateContentsLayerBounds() {
if (!contents_layer_)
return;
IntSize contents_size = contents_rect_.Size();
contents_layer_->SetBounds(gfx::Size(contents_size));
}
void GraphicsLayer::SetContentsToCcLayer(
scoped_refptr<cc::Layer> contents_layer) {
DCHECK_NE(contents_layer, layer_);
SetContentsTo(std::move(contents_layer));
}
void GraphicsLayer::SetContentsTo(scoped_refptr<cc::Layer> layer) {
if (layer) {
if (contents_layer_ != layer) {
contents_layer_ = std::move(layer);
// It is necessary to call SetDrawsContent() as soon as we receive the new
// contents_layer, for the correctness of early exit conditions in
// SetDrawsContent() and SetContentsVisible().
contents_layer_->SetIsDrawable(contents_visible_);
contents_layer_->SetHitTestable(contents_visible_);
NotifyChildListChange();
}
UpdateContentsLayerBounds();
} else if (contents_layer_) {
contents_layer_ = nullptr;
NotifyChildListChange();
}
}
RasterInvalidator& GraphicsLayer::EnsureRasterInvalidator() {
if (!raster_invalidator_) {
raster_invalidator_ = std::make_unique<RasterInvalidator>();
raster_invalidator_->SetTracksRasterInvalidations(
client_.IsTrackingRasterInvalidations());
}
return *raster_invalidator_;
}
void GraphicsLayer::UpdateTrackingRasterInvalidations() {
bool should_track = client_.IsTrackingRasterInvalidations();
if (should_track)
EnsureRasterInvalidator().SetTracksRasterInvalidations(true);
else if (raster_invalidator_)
raster_invalidator_->SetTracksRasterInvalidations(false);
}
void GraphicsLayer::ResetTrackedRasterInvalidations() {
if (auto* tracking = GetRasterInvalidationTracking())
tracking->ClearInvalidations();
}
bool GraphicsLayer::HasTrackedRasterInvalidations() const {
if (auto* tracking = GetRasterInvalidationTracking())
return tracking->HasInvalidations();
return false;
}
RasterInvalidationTracking* GraphicsLayer::GetRasterInvalidationTracking()
const {
return raster_invalidator_ ? raster_invalidator_->GetTracking() : nullptr;
}
void GraphicsLayer::TrackRasterInvalidation(const DisplayItemClient& client,
const IntRect& rect,
PaintInvalidationReason reason) {
if (RasterInvalidationTracking::ShouldAlwaysTrack())
EnsureRasterInvalidator().EnsureTracking();
// This only tracks invalidations that the cc::Layer is fully invalidated
// directly, e.g. from SetContentsNeedsDisplay(), etc. Other raster
// invalidations are tracked in RasterInvalidator.
if (auto* tracking = GetRasterInvalidationTracking())
tracking->AddInvalidation(&client, client.DebugName(), rect, reason);
}
String GraphicsLayer::DebugName(const cc::Layer* layer) const {
if (layer == contents_layer_.get())
return "ContentsLayer for " + client_.DebugName(this);
if (layer == layer_.get())
return client_.DebugName(this);
NOTREACHED();
return "";
}
const gfx::Size& GraphicsLayer::Size() const {
return CcLayer().bounds();
}
void GraphicsLayer::SetSize(const gfx::Size& size) {
DCHECK(size.width() >= 0 && size.height() >= 0);
if (size == CcLayer().bounds())
return;
Invalidate(PaintInvalidationReason::kIncremental); // as DisplayItemClient.
CcLayer().SetBounds(size);
SetNeedsCheckRasterInvalidation();
// Note that we don't resize contents_layer_. It's up the caller to do that.
}
void GraphicsLayer::SetDrawsContent(bool draws_content) {
// NOTE: This early-exit is only correct because we also properly call
// cc::Layer::SetIsDrawable() whenever |contents_layer_| is set to a new
// layer in SetupContentsLayer().
if (draws_content == draws_content_)
return;
// This may affect which layers the client collects.
client_.GraphicsLayersDidChange();
// This flag will be updated when the layer is repainted.
should_create_layers_after_paint_ = false;
draws_content_ = draws_content;
UpdateLayerIsDrawable();
if (!draws_content) {
paint_controller_.reset();
raster_invalidator_.reset();
}
}
void GraphicsLayer::SetContentsVisible(bool contents_visible) {
// NOTE: This early-exit is only correct because we also properly call
// cc::Layer::SetIsDrawable() whenever |contents_layer_| is set to a new
// layer in SetupContentsLayer().
if (contents_visible == contents_visible_)
return;
contents_visible_ = contents_visible;
UpdateLayerIsDrawable();
}
void GraphicsLayer::SetPaintsHitTest(bool paints_hit_test) {
if (paints_hit_test_ == paints_hit_test)
return;
// This may affect which layers the client collects.
client_.GraphicsLayersDidChange();
// This flag will be updated when the layer is repainted.
should_create_layers_after_paint_ = false;
paints_hit_test_ = paints_hit_test;
}
void GraphicsLayer::SetHitTestable(bool should_hit_test) {
if (hit_testable_ == should_hit_test)
return;
// This may affect which layers the client collects.
client_.GraphicsLayersDidChange();
hit_testable_ = should_hit_test;
CcLayer().SetHitTestable(should_hit_test);
}
void GraphicsLayer::InvalidateContents() {
if (contents_layer_) {
contents_layer_->SetNeedsDisplay();
TrackRasterInvalidation(*this, contents_rect_,
PaintInvalidationReason::kFullLayer);
}
}
void GraphicsLayer::InvalidateRaster(const IntRect& rect) {
DCHECK(PaintsContentOrHitTest());
raster_invalidated_ = true;
CcLayer().SetNeedsDisplayRect(rect);
}
void GraphicsLayer::SetContentsRect(const IntRect& rect) {
if (rect == contents_rect_)
return;
contents_rect_ = rect;
UpdateContentsLayerBounds();
client_.GraphicsLayersDidChange();
}
void GraphicsLayer::SetPaintingPhase(GraphicsLayerPaintingPhase phase) {
if (painting_phase_ == phase)
return;
painting_phase_ = phase;
Invalidate(PaintInvalidationReason::kFullLayer); // As DisplayItemClient.
}
PaintController& GraphicsLayer::GetPaintController() const {
CHECK(PaintsContentOrHitTest());
if (!paint_controller_)
paint_controller_ = std::make_unique<PaintController>();
return *paint_controller_;
}
void GraphicsLayer::SetElementId(const CompositorElementId& id) {
CcLayer().SetElementId(id);
}
void GraphicsLayer::SetLayerState(const PropertyTreeStateOrAlias& layer_state,
const IntPoint& layer_offset) {
if (layer_state_) {
if (layer_state_->state == layer_state &&
layer_state_->offset == layer_offset)
return;
layer_state_->state = layer_state;
layer_state_->offset = layer_offset;
} else {
layer_state_ = std::make_unique<LayerState>(
LayerState{RefCountedPropertyTreeState(layer_state), layer_offset});
}
CcLayer().SetSubtreePropertyChanged();
client_.GraphicsLayersDidChange();
}
void GraphicsLayer::SetContentsLayerState(
const PropertyTreeStateOrAlias& layer_state,
const IntPoint& layer_offset) {
DCHECK(ContentsLayer());
if (contents_layer_state_) {
if (contents_layer_state_->state == layer_state &&
contents_layer_state_->offset == layer_offset)
return;
contents_layer_state_->state = layer_state;
contents_layer_state_->offset = layer_offset;
} else {
contents_layer_state_ = std::make_unique<LayerState>(
LayerState{RefCountedPropertyTreeState(layer_state), layer_offset});
}
ContentsLayer()->SetSubtreePropertyChanged();
client_.GraphicsLayersDidChange();
}
gfx::Rect GraphicsLayer::PaintableRegion() const {
return RuntimeEnabledFeatures::CullRectUpdateEnabled()
? client_.PaintableRegion(this)
: previous_interest_rect_;
}
scoped_refptr<cc::DisplayItemList> GraphicsLayer::PaintContentsToDisplayList() {
DCHECK(!ShouldCreateLayersAfterPaint());
return cc_display_item_list_;
}
size_t GraphicsLayer::ApproximateUnsharedMemoryUsageRecursive() const {
size_t result = sizeof(*this);
if (paint_controller_)
result += paint_controller_->ApproximateUnsharedMemoryUsage();
if (raster_invalidator_)
result += raster_invalidator_->ApproximateUnsharedMemoryUsage();
for (auto* child : Children())
result += child->ApproximateUnsharedMemoryUsageRecursive();
return result;
}
} // namespace blink
#if DCHECK_IS_ON()
void showGraphicsLayerTree(const blink::GraphicsLayer* layer) {
if (!layer) {
LOG(ERROR) << "Cannot showGraphicsLayerTree for (nil).";
return;
}
String output = blink::GraphicsLayerTreeAsTextForTesting(layer, 0xffffffff);
LOG(INFO) << output.Utf8();
}
#endif