910e62b5创建于 1月15日历史提交
// 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));
    layer_impl->set_should_batch_updated_tiles();
  }

  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