blob: c9656dae18cecd9a5ae1b48af31a2afd718ea8be [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/layers/picture_layer.h"
#include <memory>
#include <utility>
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/trace_event.h"
#include "cc/base/features.h"
#include "cc/layers/content_layer_client.h"
#include "cc/layers/picture_layer_impl.h"
#include "cc/layers/recording_source.h"
#include "cc/paint/paint_record.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/transform_node.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace cc {
scoped_refptr<PictureLayer> PictureLayer::Create(ContentLayerClient* client) {
return base::WrapRefCounted(new PictureLayer(client));
}
PictureLayer::PictureLayer(ContentLayerClient* client)
: client_(client),
instrumentation_object_tracker_(id()),
update_source_frame_number_(-1) {}
PictureLayer::~PictureLayer() = default;
std::unique_ptr<LayerImpl> PictureLayer::CreateLayerImpl(
LayerTreeImpl* tree_impl) const {
return PictureLayerImpl::Create(tree_impl, id());
}
void PictureLayer::PushDirtyPropertiesTo(
LayerImpl* base_layer,
uint8_t dirty_flag,
const CommitState& commit_state,
const ThreadUnsafeCommitState& unsafe_state) {
Layer::PushDirtyPropertiesTo(base_layer, dirty_flag, commit_state,
unsafe_state);
if (dirty_flag & kChangedGeneralProperty) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"PictureLayer::PushPropertiesTo");
DropRecordingSourceContentIfInvalid(
base_layer->layer_tree_impl()->source_frame_number());
PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer);
if (!update_rect().IsEmpty()) {
layer_impl->set_has_non_animated_image_update_rect();
}
layer_impl->set_gpu_raster_max_texture_size(
commit_state.device_viewport_rect.size());
layer_impl->SetIsBackdropFilterMask(is_backdrop_filter_mask());
layer_impl->UpdateRasterSource(CreateRasterSource(),
&last_updated_invalidation_.Write(*this));
}
DCHECK(last_updated_invalidation_.Read(*this).IsEmpty());
}
scoped_refptr<RasterSource> PictureLayer::CreateRasterSource() const {
return recording_source_.Read(*this).CreateRasterSource();
}
void PictureLayer::SetLayerTreeHost(LayerTreeHost* host) {
Layer::SetLayerTreeHost(host);
if (!host)
return;
recording_source_.Write(*this).SetSlowdownRasterScaleFactor(
host->GetDebugState().slow_down_raster_scale_factor);
// Source frame numbers are relative the LayerTreeHost, so this needs
// to be reset.
update_source_frame_number_.Write(*this) = -1;
}
void PictureLayer::SetNeedsDisplayRect(const gfx::Rect& layer_rect) {
DCHECK(IsPropertyChangeAllowed());
recording_source_.Write(*this).SetNeedsDisplayRect(layer_rect);
Layer::SetNeedsDisplayRect(layer_rect);
}
void PictureLayer::SetForceUpdateRecordingSource() {
DCHECK(IsPropertyChangeAllowed());
recording_source_.Write(*this).set_force_update();
SetNeedsPushProperties();
if (draws_content() && IsAttached()) {
layer_tree_host()->SetNeedsUpdateLayers();
}
}
bool PictureLayer::RequiresSetNeedsDisplayOnHdrHeadroomChange() const {
if (const DisplayItemList* display_list = GetDisplayItemList()) {
return display_list->content_color_usage() == gfx::ContentColorUsage::kHDR;
}
return false;
}
bool PictureLayer::Update() {
update_source_frame_number_.Write(*this) =
layer_tree_host()->SourceFrameNumber();
bool updated = Layer::Update();
auto& recording_source = recording_source_.Write(*this);
recording_source.SetBackgroundColor(SafeOpaqueBackgroundColor());
recording_source.SetRequiresClear(!contents_opaque() &&
!client_->FillsBoundsCompletely());
recording_source.SetCanUseRecordedBounds(
layer_tree_host()->GetSettings().enable_hit_test_opaqueness);
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "PictureLayer::Update",
"source_frame_number", layer_tree_host()->SourceFrameNumber());
devtools_instrumentation::ScopedLayerTreeTask update_layer(
devtools_instrumentation::kUpdateLayer, id(), layer_tree_host()->GetId());
// UpdateAndExpandInvalidation will give us an invalidation that covers
// anything not explicitly recorded in this frame. We give this region
// to the impl side so that it drops tiles that may not have a recording
// for them.
DCHECK(client_);
updated |= recording_source.Update(
bounds(), layer_tree_host()->recording_scale_factor(), *client_,
last_updated_invalidation_.Write(*this));
if (!updated) {
return false;
}
SetNeedsPushProperties();
IncreasePaintCount();
return true;
}
sk_sp<const SkPicture> PictureLayer::GetPicture() const {
if (!draws_content() || bounds().IsEmpty())
return nullptr;
scoped_refptr<DisplayItemList> display_list =
client_->PaintContentsToDisplayList();
SkPictureRecorder recorder;
SkCanvas* canvas =
recorder.beginRecording(bounds().width(), bounds().height());
canvas->clear(SK_ColorTRANSPARENT);
ScrollOffsetMap raster_inducing_scroll_offsets;
const ScrollTree& scroll_tree =
layer_tree_host()->property_trees()->scroll_tree();
for (auto [element_id, _] : display_list->raster_inducing_scrolls()) {
raster_inducing_scroll_offsets[element_id] =
scroll_tree.current_scroll_offset(element_id);
}
display_list->Raster(canvas, /*image_provider=*/nullptr,
&raster_inducing_scroll_offsets);
return recorder.finishRecordingAsPicture();
}
void PictureLayer::ClearClient() {
client_ = nullptr;
UpdateDrawsContent();
}
bool PictureLayer::HasDrawableContent() const {
return client_ && Layer::HasDrawableContent();
}
void PictureLayer::SetIsBackdropFilterMask(bool is_backdrop_filter_mask) {
if (is_backdrop_filter_mask_ == is_backdrop_filter_mask) {
return;
}
is_backdrop_filter_mask_ = is_backdrop_filter_mask;
SetNeedsCommit();
}
void PictureLayer::RunMicroBenchmark(MicroBenchmark* benchmark) {
benchmark->RunOnLayer(this);
}
void PictureLayer::CaptureContent(const gfx::Rect& rect,
std::vector<NodeInfo>* content) const {
if (!draws_content())
return;
const DisplayItemList* display_item_list = GetDisplayItemList();
if (!display_item_list)
return;
// We could run into this situation as CaptureContent could start at any time.
if (transform_tree_index() == kInvalidPropertyNodeId)
return;
gfx::Transform inverse_screen_space_transform;
if (!ScreenSpaceTransform().GetInverse(&inverse_screen_space_transform))
return;
gfx::Rect transformed = MathUtil::ProjectEnclosingClippedRect(
inverse_screen_space_transform, rect);
transformed.Intersect(gfx::Rect(bounds()));
if (transformed.IsEmpty())
return;
display_item_list->CaptureContent(transformed, content);
if (auto* outer_viewport_layer = layer_tree_host()->LayerByElementId(
layer_tree_host()->OuterViewportScrollElementId())) {
if (transform_tree_index() == outer_viewport_layer->transform_tree_index())
return;
gfx::Transform inverse_outer_screen_space_transform;
if (!outer_viewport_layer->ScreenSpaceTransform().GetInverse(
&inverse_outer_screen_space_transform)) {
return;
}
gfx::Transform combined_transform =
ScreenSpaceTransform() * inverse_outer_screen_space_transform;
for (auto& i : *content) {
i.visual_rect = MathUtil::ProjectEnclosingClippedRect(combined_transform,
i.visual_rect);
}
}
}
void PictureLayer::DropRecordingSourceContentIfInvalid(
int source_frame_number) {
gfx::Size recording_source_size = recording_source_.Read(*this).size();
gfx::Size layer_bounds = bounds();
// If update called, then recording source size must match bounds pushed to
// impl layer.
DCHECK(update_source_frame_number_.Read(*this) != source_frame_number ||
layer_bounds == recording_source_size)
<< " bounds " << layer_bounds.ToString() << " recording source "
<< recording_source_size.ToString();
if (update_source_frame_number_.Read(*this) != source_frame_number &&
recording_source_size != layer_bounds) {
// Update may not get called for the layer (if it's not in the viewport
// for example), even though it has resized making the recording source no
// longer valid. In this case just destroy the recording source.
recording_source_.Write(*this).SetEmptyBounds();
}
}
const DisplayItemList* PictureLayer::GetDisplayItemList() const {
return recording_source_.Read(*this).display_list();
}
} // namespace cc