站在老罗的肩膀上:https://blog.csdn.net/luoshengyang/article/details/51148299
网页绘图表面创建完成之后,调度器就会请求绘制CC Layer Tree,这样网页在加载完成之后就能快速显示出来。通过CC Layer Tree可以依次找到Graphics Layer Tree、Paint Layer Tree和Layout Object Tree,就可以执行具体的绘制工作了。
crRendererMain线程实际上并没有对CC Layer Tree执行真正的绘制,它只是将每一个Layer的绘制命令收集起来。这些绘制命令在对网页分块进行光栅化时才会被执行,也就是PREPARE_TILES中执行。
CC Layer Tree中的每一个Layer都是按照分块进行绘制的。每一个分块的绘制命令都收集在一个SkPicture中。这个SkPicture就类似于Android应用程序UI硬件加速渲染过程形成的Display List。Layer分块并不是简单的区域划分。简单的区域划分在网页缩放过程中会有问题。Layer划分成相互重叠的区域。重叠的区域多大才合适的呢?这与网页的最小缩放因子有关。假设网页最小可以缩小原来的1/16,那么重叠的区域就至少需要15个点。
当调度器调用SchedulerStateMachine类的成员函数NextAction询问状态机下一步要执行的操作时,SchedulerStateMachine类的成员函数NextAction会调用另外一个成员函数ShouldSendBeginMainFrame。当SchedulerStateMachine类的成员函数ShouldSendBeginMainFrame返回值等于true的时候,状态机就会提示调度器接下来需要执行SEND_BEGIN_MAIN_FRAME操作,也就是对CC Layer Tree进行绘制。
SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
...
if (ShouldSendBeginMainFrame())
return Action::SEND_BEGIN_MAIN_FRAME;
...
}
状态从BeginMainFrameState::IDLE转变成BeginMainFrameState::SENT
void SchedulerStateMachine::WillSendBeginMainFrame() {
...
begin_main_frame_state_ = BeginMainFrameState::SENT;
needs_begin_main_frame_ = false;
did_send_begin_main_frame_for_current_frame_ = true;
last_frame_number_begin_main_frame_sent_ = current_frame_number_;
}
void ProxyImpl::ScheduledActionSendBeginMainFrame(
const viz::BeginFrameArgs& args) {
...
std::unique_ptr<BeginMainFrameAndCommitState> begin_main_frame_state(
new BeginMainFrameAndCommitState);
...
MainThreadTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&ProxyMain::BeginMainFrame, proxy_main_weak_ptr_,
base::Passed(&begin_main_frame_state)));
host_impl_->DidSendBeginMainFrame();
devtools_instrumentation::DidRequestMainThreadFrame(layer_tree_host_id_);
}
向crRendererMain线程的消息队列发送一个Task,这个Task绑定的函数是ProxyMain::BeginMainFrame。因此,接下来会转入crRendererMain线程
void ProxyMain::BeginMainFrame(
std::unique_ptr<BeginMainFrameAndCommitState> begin_main_frame_state) {
...
if (!layer_tree_host_->IsVisible()) {
...
ImplThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&ProxyImpl::BeginMainFrameAbortedOnImpl,
base::Unretained(proxy_impl_.get()),
CommitEarlyOutReason::ABORTED_NOT_VISIBLE,
begin_main_frame_start_time,
base::Passed(&empty_swap_promises)));
return;
}
layer_tree_host_->ApplyScrollAndScale(
begin_main_frame_state->scroll_info.get());
layer_tree_host_->WillBeginMainFrame();
layer_tree_host_->BeginMainFrame(begin_main_frame_state->begin_frame_args);
layer_tree_host_->AnimateLayers(
begin_main_frame_state->begin_frame_args.frame_time);
if (begin_main_frame_state->evicted_ui_resources)
layer_tree_host_->GetUIResourceManager()->RecreateUIResources();
layer_tree_host_->RequestMainFrameUpdate(
skip_paint_and_commit ? LayerTreeHost::VisualStateUpdate::kPrePaint
: LayerTreeHost::VisualStateUpdate::kAll);
...
if (skip_paint_and_commit) {
...
ImplThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&ProxyImpl::BeginMainFrameAbortedOnImpl,
base::Unretained(proxy_impl_.get()),
CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT,
begin_main_frame_start_time,
base::Passed(&empty_swap_promises)));
...
return;
}
// If UI resources were evicted on the impl thread, we need a commit.
if (begin_main_frame_state->evicted_ui_resources)
final_pipeline_stage_ = COMMIT_PIPELINE_STAGE;
current_pipeline_stage_ = UPDATE_LAYERS_PIPELINE_STAGE;
bool should_update_layers =
final_pipeline_stage_ >= UPDATE_LAYERS_PIPELINE_STAGE;
...
bool updated = should_update_layers && layer_tree_host_->UpdateLayers();
// If updating the layers resulted in a content update, we need a commit.
if (updated)
final_pipeline_stage_ = COMMIT_PIPELINE_STAGE;
layer_tree_host_->WillCommit();
devtools_instrumentation::ScopedCommitTrace commit_task(
layer_tree_host_->GetId());
current_pipeline_stage_ = COMMIT_PIPELINE_STAGE;
if (final_pipeline_stage_ < COMMIT_PIPELINE_STAGE) {
...
ImplThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&ProxyImpl::BeginMainFrameAbortedOnImpl,
base::Unretained(proxy_impl_.get()),
CommitEarlyOutReason::FINISHED_NO_UPDATES,
begin_main_frame_start_time,
base::Passed(&swap_promises)));
...
return;
}
...
{
...
ImplThreadTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&ProxyImpl::NotifyReadyToCommitOnImpl,
base::Unretained(proxy_impl_.get()), &completion,
layer_tree_host_, begin_main_frame_start_time,
hold_commit_for_activation));
completion.Wait();
}
...
}
BeginMainFrame主要是做三件事情:
1. 计算CC Layer Tree的布局。这是通过调用LayerTreeHost类的成员函数BeginMainFrame实现的。
2. 计算CC Layer Tree的动画。使用网址:https://www.jianshu.com/p/7da4895b3693进行动画渲染调试
当网页的DOM Tree中的某一个Element需要创建动画时,调用Animation::create为其创建一个动画,运行在CrBrowserMain进程,如下所示:
scoped_refptr<Animation> Animation::CreateImplInstance() const {
return Animation::Create(id());
}
更新网页的Graphics Layer Tree的时候,就会将DOM Tree中的动画注册到CC模块中去,接着又会调用DocumentAnimations::UpdateAnimations执行网页的DOM Tree中的动画。
void PaintLayerCompositor::UpdateIfNeededRecursiveInternal(
DocumentLifecycle::LifecycleState target_state,
CompositingReasonsStats& compositing_reasons_stats) {
...
if (!layout_view_.GetDocument().Printing() ||
RuntimeEnabledFeatures::PrintBrowserEnabled()) {
...
if (!RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
base::Optional<CompositorElementIdSet> composited_element_ids;
DocumentAnimations::UpdateAnimations(layout_view_.GetDocument(),
DocumentLifecycle::kCompositingClean,
composited_element_ids);
}
...
}
void DocumentAnimations::UpdateAnimations(
Document& document,
DocumentLifecycle::LifecycleState required_lifecycle_state,
base::Optional<CompositorElementIdSet>& composited_element_ids) {
if (document.GetPendingAnimations().Update(composited_element_ids)) {
DCHECK(document.View());
document.View()->ScheduleAnimation();
}
...
}
LocalFrameView::ScheduleAnimation调度执行这些动画 。PendingAnimations::Update将动画是注册到CC模块
bool PendingAnimations::Update(
const base::Optional<CompositorElementIdSet>& composited_element_ids,
bool start_on_compositor) {
...
for (auto& animation : animations) {
bool had_compositor_animation =
animation->HasActiveAnimationsOnCompositor();
// Animations with a start time do not participate in compositor start-time
// grouping.
if (animation->PreCommit(animation->startTime() ? 1 : compositor_group,
composited_element_ids, start_on_compositor)) {
if (animation->HasActiveAnimationsOnCompositor() &&
!had_compositor_animation) {
started_synchronized_on_compositor = true;
}
if (animation->Playing() && !animation->startTime() &&
animation->TimelineInternal() &&
animation->TimelineInternal()->IsActive()) {
waiting_for_start_time.push_back(animation.Get());
}
} else {
deferred.push_back(animation);
}
}
...
}
其中的Animation::PreCommit如下:
bool Animation::PreCommit(
int compositor_group,
const base::Optional<CompositorElementIdSet>& composited_element_ids,
bool start_on_compositor) {
...
if (should_start) {
compositor_group_ = compositor_group;
if (start_on_compositor) {
...
if (failure_code.Ok()) {
CreateCompositorAnimation();
StartAnimationOnCompositor(composited_element_ids);
compositor_state_ = std::make_unique<CompositorState>(*this);
...
}
return true;
}
最终调用CompositorAnimations::StartAnimationOnCompositor执行该动画。
void CompositorAnimations::StartAnimationOnCompositor(
const Element& element,
...,
const EffectModel& effect,
...) {
...
GetAnimationOnCompositor(timing, group, start_time, time_offset,
keyframe_effect, keyframe_models,
animation_playback_rate);
for (auto& compositor_keyframe_model : keyframe_models) {
int id = compositor_keyframe_model->Id();
compositor_animation.AddKeyframeModel(std::move(compositor_keyframe_model));
started_keyframe_model_ids.push_back(id);
}
}
其中compositor_annimation类型为CompositorAnimation,具有类型为cc::SingleKeyframeEffectAnimation的成员变量animation_ 。
参数element描述的是要执行动画的一个Element。这个Element对应的是网页的DOM Tree中的一个节点。通过调用CompositorAnimations类的成员函数toRenderBoxModelObject可以获得它在网页的Render Layer Tree中对应的节点,也就是一个RenderLayer对象。
参数effect描述了上述Element要执行的动画,每一个动画会被重新封装成一个WebAnimation对象。这是通过调用CompositorAnimationsImpl类的静态成员函数CompositorAnimations::GetAnimationOnCompositor实现的,如下所示:
void CompositorAnimations::GetAnimationOnCompositor(
const Timing& timing,
...,
const KeyframeEffectModelBase& effect,
Vector<std::unique_ptr<CompositorKeyframeModel>>& keyframe_models,
double animation_playback_rate) {
...
for (const auto& property : properties) {
...
const PropertySpecificKeyframeVector& values =
effect.GetPropertySpecificKeyframes(property);
CompositorTargetProperty::Type target_property;
std::unique_ptr<CompositorAnimationCurve> curve;
switch (property.GetCSSProperty().PropertyID()) {
...
case CSSPropertyTransform: {
target_property = CompositorTargetProperty::TRANSFORM;
std::unique_ptr<CompositorTransformAnimationCurve> transform_curve =
CompositorTransformAnimationCurve::Create();
AddKeyframesToCurve(*transform_curve, values);
transform_curve->SetTimingFunction(*timing.timing_function);
transform_curve->SetScaledDuration(scale);
curve = std::move(transform_curve);
break;
}
...
}
std::unique_ptr<CompositorKeyframeModel> keyframe_model =
CompositorKeyframeModel::Create(*curve, target_property, group, 0);
...
keyframe_models.push_back(std::move(keyframe_model));
}
}
每一个动画都封装成CompositorKeyframeModel,保存在compositor_animation中,接下来要把它们关联到Dom节点对应都Graphics Layer中,回到CompositorAnimations::StartAnimationOnCompositor,通过compositor_animation.AddKeyframeModel,最终将动画添加到keyframe_models_
void KeyframeEffect::AddKeyframeModel(
std::unique_ptr<KeyframeModel> keyframe_model) {
...
keyframe_models_.push_back(std::move(keyframe_model));
...
}
而这个对象keyframe_models_就是CCLayer计算Animation所使用到到对象,通过调用LayerTreeHost类的成员函数AnimateLayers实现的。
void LayerTreeHost::AnimateLayers(base::TimeTicks monotonic_time) {
...
// slimming path and will do so in a follow up. (762717)
if (IsUsingLayerLists())
return;
std::unique_ptr<MutatorEvents> events = mutator_host_->CreateEvents();
if (mutator_host_->TickAnimations(monotonic_time,
property_trees()->scroll_tree, true))
mutator_host_->UpdateAnimationState(true, events.get());
if (!events->IsEmpty())
property_trees_.needs_rebuild = true;
}
mutator_host_负责管理动画,类型为AnimationHost.
注册动画
void KeyframeEffect::Tick(base::TimeTicks monotonic_time) {
DCHECK(has_bound_element_animations());
if (!element_animations_->has_element_in_any_list())
return;
if (needs_to_start_keyframe_models_)
StartKeyframeModels(monotonic_time);
for (auto& keyframe_model : keyframe_models_) {
TickKeyframeModel(monotonic_time, keyframe_model.get(),
element_animations_.get());
}
last_tick_time_ = monotonic_time;
element_animations_->UpdateClientAnimationState();
}
void KeyframeEffect::TickKeyframeModel(base::TimeTicks monotonic_time,
KeyframeModel* keyframe_model,
AnimationTarget* target) {
...
AnimationCurve* curve = keyframe_model->curve();
base::TimeDelta trimmed =
keyframe_model->TrimTimeToCurrentIteration(monotonic_time);
switch (curve->Type()) {
case AnimationCurve::TRANSFORM:
target->NotifyClientTransformOperationsAnimated(
curve->ToTransformAnimationCurve()->GetValue(trimmed),
keyframe_model->target_property_id(), keyframe_model);
break;
...
}
}
由此Animation的绘制就从Dom关联到了CCLayer 。
3. 绘制CC Layer Tree。
创建Layout Object Tree时有以下调用栈
从Document::UpdateStyleAndLayoutTree创建或更新Layout Object Tree,并在完成后通过LocalFrameView::UpdateLayout创建或更新Layout
void Document::UpdateStyleAndLayout() {
...
UpdateStyleAndLayoutTree();
...
if (frame_view && frame_view->NeedsLayout())
frame_view->UpdateLayout();
...
}
void LocalFrameView::UpdateLayout() {
// We should never layout a Document which is not in a LocalFrame.
...
// PreLayout is for what
PerformPreLayoutTasks();
...
bool in_subtree_layout = IsSubtreeLayout();
...
PerformLayout(in_subtree_layout);
...
}
void LocalFrameView::PerformLayout(bool in_subtree_layout) {
...
{
...
if (in_subtree_layout) {
// subtree
...
} else {
// whole tree
if (HasOrthogonalWritingModeRoots())
LayoutOrthogonalWritingModeRoots();
GetLayoutView()->UpdateLayout();
}
}
...
}
经历如下调用栈,
其中主要的递归逻辑在
void LayoutBlockFlow::LayoutBlockChildren(bool relayout_children,
SubtreeLayoutScope& layout_scope,
LayoutUnit before_edge,
LayoutUnit after_edge) {
...
// first do all the Layout of children Layout Objects
while (next) {
LayoutBox* child = next;
LayoutObject* next_sibling = child->NextSibling();
CHECK(!next_sibling || next_sibling->IsBox());
next = ToLayoutBox(next_sibling);
...
// Lay out the child.
LayoutBlockChild(*child, layout_info);
layout_info.ClearIsAtFirstInFlowChild();
last_normal_flow_child = child;
}
// Second do the layout for the current Layout Object
// Now do the handling of the bottom of the block, adding in our bottom
// border/padding and determining the correct collapsed bottom margin
// information.
HandleAfterSideOfBlock(last_normal_flow_child, before_edge, after_edge,
margin_info);
}
这样得到了完整的Layout,然后可以开始绘制CCLayer,通过调用LayerTreeHost类的成员函数UpdateLayers实现的。
CC Layer Tree与Graphics Layer Tree中的节点是一一对应关系,并且Graphics Layer Tree的每一个Layer代表的都是一个图层。这个图层在硬件加速渲染条件下,就是一个FBO。但是到底要不要为Graphics Layer Tree中的Layer分配一个图层最终是由CC模块决定的。如果CC模块决定要为一个Graphics Layer分配一个图层,那么就会为它创建一个Render Surface。Render Surface才是真正表示一个图层。
LayerTreeHostCommon类静态成员函数CalculateDrawProperties根据CC Layer Tree创建出来的Render Surface Tree虽然在结构上也是一个Tree,不过它的节点是以列表的形式储存的,也就是储存在本地变量render_surface_list_描述的一个RenderSurfaceList(std::vector<RenderSurfaceImpl*>)中。
(CrBrowserMain)
1.计算以参数root_layer指向的Layer对象为根节点的CC Layer Tree的每一个Layer的绘图属性。此外,LayerTreeHostCommon类静态成员函数CalculateDrawProperties还会根据上述CC Layer Tree创建一个Render Surface Tree。CC Layer Tree中的节点与Render Surface Tree中的节点是多对一的关系。也就是只有CC Layer Tree的某些节点在Render Surface Tree中才拥有Render Surface。
void CalculateDrawPropertiesInternal(
LayerTreeHostCommon::CalcDrawPropsImplInputs* inputs,
PropertyTreeOption property_tree_option) {
inputs->render_surface_list->clear();
const bool should_measure_property_tree_performance =
property_tree_option == BUILD_PROPERTY_TREES;
LayerImplList visible_layer_list;
switch (property_tree_option) {
case BUILD_PROPERTY_TREES: {
if (should_measure_property_tree_performance) {
TRACE_EVENT_BEGIN0(
TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"),
"LayerTreeHostCommon::ComputeVisibleRectsWithPropertyTrees");
}
PropertyTreeBuilder::BuildPropertyTrees(
inputs->root_layer, inputs->page_scale_layer,
inputs->inner_viewport_scroll_layer,
inputs->outer_viewport_scroll_layer,
inputs->elastic_overscroll_application_layer,
inputs->elastic_overscroll, inputs->page_scale_factor,
inputs->device_scale_factor, gfx::Rect(inputs->device_viewport_size),
inputs->device_transform, inputs->property_trees);
draw_property_utils::UpdatePropertyTreesAndRenderSurfaces(
inputs->root_layer, inputs->property_trees,
inputs->can_adjust_raster_scales);
inputs->property_trees->transform_tree
.set_source_to_parent_updates_allowed(false);
break;
}
...
}
draw_property_utils::FindLayersThatNeedUpdates(
inputs->root_layer->layer_tree_impl(), inputs->property_trees,
&visible_layer_list);
draw_property_utils::ComputeDrawPropertiesOfVisibleLayers(
&visible_layer_list, inputs->property_trees);
CalculateRenderSurfaceLayerList(
inputs->root_layer->layer_tree_impl(), inputs->property_trees,
inputs->render_surface_list, inputs->max_texture_size);
...
}
有了上述RenderSurfaceLayerList(CrBrowserMain计算)之后,LayerTreeHost类的成员函数UpdateLayers再调用另外一个成员函数PaintLayerContents对保存在RenderSurfaceLayerList中的Render Surface进行绘制,实际上就是对CC Layer Tree进行绘制。
(CrRendererMain)
bool LayerTreeHost::UpdateLayers() {
...
// LayerTreeHost::root_layer 获取cc layer的根节点。
bool result = DoUpdateLayers(root_layer());
...
return result;
}
bool LayerTreeHost::DoUpdateLayers(Layer* root_layer) {
...
gfx::Transform identity_transform;
LayerList update_layer_list;
if (!IsUsingLayerLists()) {
PropertyTreeBuilder::BuildPropertyTrees(
root_layer, page_scale_layer, inner_viewport_scroll_layer(),
outer_viewport_scroll_layer(), overscroll_elasticity_layer(),
elastic_overscroll_, page_scale_factor_, device_scale_factor_,
gfx::Rect(device_viewport_size_), identity_transform, &property_trees_);
} else {
...
}
draw_property_utils::UpdatePropertyTrees(this, &property_trees_);
draw_property_utils::FindLayersThatNeedUpdates(this, &property_trees_,
&update_layer_list);
...
bool did_paint_content =
PaintContent(update_layer_list, &painted_content_has_slow_paths,
&painted_content_has_non_aa_paint);
...
return did_paint_content;
}
bool LayerTreeHost::PaintContent(const LayerList& update_layer_list,
bool* content_has_slow_paths,
bool* content_has_non_aa_paint) {
base::AutoReset<bool> painting(&in_paint_layer_contents_, true);
bool did_paint_content = false;
for (const auto& layer : update_layer_list) {
did_paint_content |= layer->Update();
...
}
return did_paint_content;
}
CC Layer Tree中的每一个Layer都是通过一个PictureLayer对象描述的。因此,接下来我们分析PictureLayer::Update的实现,如下所示:
bool PictureLayer::Update() {
update_source_frame_number_ = layer_tree_host()->SourceFrameNumber();
bool updated = Layer::Update();
gfx::Size layer_size = bounds();
recording_source_->SetBackgroundColor(SafeOpaqueBackgroundColor());
recording_source_->SetRequiresClear(
!contents_opaque() &&
!picture_layer_inputs_.client->FillsBoundsCompletely());
...
picture_layer_inputs_.recorded_viewport =
picture_layer_inputs_.client->PaintableRegion();
updated |= recording_source_->UpdateAndExpandInvalidation(
&last_updated_invalidation_, layer_size,
picture_layer_inputs_.recorded_viewport);
if (updated) {
picture_layer_inputs_.display_list =
picture_layer_inputs_.client->PaintContentsToDisplayList(
ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
picture_layer_inputs_.painter_reported_memory_usage =
picture_layer_inputs_.client->GetApproximateUnsharedMemoryUsage();
recording_source_->UpdateDisplayItemList(
picture_layer_inputs_.display_list,
picture_layer_inputs_.painter_reported_memory_usage,
layer_tree_host()->recording_scale_factor());
SetNeedsPushProperties();
} else {
...
}
return updated;
}
PictureLayer类的成员函数Update首先调用父类Layer的成员函数Update让其有机会对当前正在处理的Layer执行一些更新工作(实际上什么也没有做)。PictureLayer类有个重要的成员变量last_updated_invalidation_指向的是一个Region对象。这个Region对象描述的是当前正在处理的Layer的待重绘区域。PictureLayer类的成员函数Update在重绘这个区域之前,会先将它的值设置到另外一个成员变量pile_invalidation_中去,以表示Layer的当前重绘区域,同时也会将待重绘区域清空。
其中GraphicsLayer::PaintContentsToDisplayList创建DisplayItemList,存储了ccLayer的绘制指令
scoped_refptr<cc::DisplayItemList> GraphicsLayer::PaintContentsToDisplayList(
PaintingControlSetting painting_control) {
TRACE_EVENT0("blink,benchmark", "GraphicsLayer::PaintContents");
PaintController& paint_controller = GetPaintController();
paint_controller.SetDisplayItemConstructionIsDisabled(
painting_control == DISPLAY_LIST_CONSTRUCTION_DISABLED);
paint_controller.SetSubsequenceCachingIsDisabled(
painting_control == SUBSEQUENCE_CACHING_DISABLED);
if (painting_control == PARTIAL_INVALIDATION)
client_.InvalidateTargetElementForTesting();
// We also disable caching when Painting or Construction are disabled. In both
// cases we would like to compare assuming the full cost of recording, not the
// cost of re-using cached content.
if (painting_control == DISPLAY_LIST_CACHING_DISABLED ||
painting_control == DISPLAY_LIST_PAINTING_DISABLED ||
painting_control == DISPLAY_LIST_CONSTRUCTION_DISABLED)
paint_controller.InvalidateAll();
GraphicsContext::DisabledMode disabled_mode =
GraphicsContext::kNothingDisabled;
if (painting_control == DISPLAY_LIST_PAINTING_DISABLED ||
painting_control == DISPLAY_LIST_CONSTRUCTION_DISABLED)
disabled_mode = GraphicsContext::kFullyDisabled;
// Anything other than PAINTING_BEHAVIOR_NORMAL is for testing. In non-testing
// scenarios, it is an error to call GraphicsLayer::Paint. Actual painting
// occurs in LocalFrameView::PaintTree() which calls GraphicsLayer::Paint();
// this method merely copies the painted output to the cc::DisplayItemList.
if (painting_control != PAINTING_BEHAVIOR_NORMAL)
Paint(nullptr, disabled_mode);
auto display_list = base::MakeRefCounted<cc::DisplayItemList>();
DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName();
PaintChunksToCcLayer::ConvertInto(
GetPaintController().PaintChunks(), layer_state_->state,
gfx::Vector2dF(layer_state_->offset.X(), layer_state_->offset.Y()),
VisualRectSubpixelOffset(),
paint_controller.GetPaintArtifact().GetDisplayItemList(), *display_list);
paint_controller.SetDisplayItemConstructionIsDisabled(false);
paint_controller.SetSubsequenceCachingIsDisabled(false);
display_list->Finalize();
return display_list;
}
回到ProxyMain::BeginMainFrame,
void ProxyMain::BeginMainFrame(
std::unique_ptr<BeginMainFrameAndCommitState> begin_main_frame_state) {
...
layer_tree_host_->BeginMainFrame(begin_main_frame_state->begin_frame_args);
layer_tree_host_->AnimateLayers(
begin_main_frame_state->begin_frame_args.frame_time);
layer_tree_host_->RequestMainFrameUpdate(
skip_paint_and_commit ? LayerTreeHost::VisualStateUpdate::kPrePaint
: LayerTreeHost::VisualStateUpdate::kAll);
}
对Graphics Layer进行绘制,最终调用CompositedLayerMapping::PaintContents对Normal Layer, Squashe Layer和ScrollableArea采用不同方式分别绘制
void CompositedLayerMapping::PaintContents(
const GraphicsLayer* graphics_layer,
GraphicsContext& context,
GraphicsLayerPaintingPhase graphics_layer_painting_phase,
const IntRect& interest_rect) const {
...
PaintLayerFlags paint_layer_flags = 0;
...
if (graphics_layer == graphics_layer_.get() ||
graphics_layer == foreground_layer_.get() ||
graphics_layer == mask_layer_.get() ||
graphics_layer == child_clipping_mask_layer_.get() ||
graphics_layer == scrolling_contents_layer_.get() ||
graphics_layer == decoration_outline_layer_.get() ||
graphics_layer == ancestor_clipping_mask_layer_.get()) {
...
DoPaintTask(paint_info, *graphics_layer, paint_layer_flags, context,
interest_rect);
} else if (graphics_layer == squashing_layer_.get()) {
for (size_t i = 0; i < squashed_layers_.size(); ++i) {
DoPaintTask(squashed_layers_[i], *graphics_layer, paint_layer_flags,
context, interest_rect);
}
} else if (IsScrollableAreaLayer(graphics_layer)) {
PaintScrollableArea(graphics_layer, context, interest_rect);
}
...
}
随后进入对Paint Layer Tree的绘制,如对于Normal调用DoPaintTask,
void CompositedLayerMapping::DoPaintTask(
const GraphicsLayerPaintInfo& paint_info,
const GraphicsLayer& graphics_layer,
PaintLayerFlags paint_layer_flags,
GraphicsContext& context,
const IntRect& clip /* In the coords of rootLayer */) const {
...
// Paint Layer owns Graphics Layer
if (paint_info.paint_layer->GetCompositingState() !=
kPaintsIntoGroupedBacking) {
// FIXME: GraphicsLayers need a way to split for multicol.
PaintLayerPaintingInfo painting_info(
paint_info.paint_layer, LayoutRect(dirty_rect), kGlobalPaintNormalPhase,
paint_info.paint_layer->SubpixelAccumulation());
PaintLayerPainter(*paint_info.paint_layer)
.PaintLayerContents(context, painting_info, paint_layer_flags);
if (paint_info.paint_layer->ContainsDirtyOverlayScrollbars()) {
PaintLayerPainter(*paint_info.paint_layer)
.PaintLayerContents(
context, painting_info,
paint_layer_flags | kPaintLayerPaintingOverlayScrollbars);
}
} else {
// Paint Layer don't own Graphics Layer
PaintLayerPaintingInfo painting_info(
paint_info.paint_layer, LayoutRect(dirty_rect), kGlobalPaintNormalPhase,
paint_info.paint_layer->SubpixelAccumulation());
PaintLayerPainter(*paint_info.paint_layer)
.Paint(context, painting_info, paint_layer_flags);
}
}
参数paintInfo描述的GraphicsLayerPaintInfo对象的成员变量paint_layer描述的是当前要绘制的Paint Layer。当一个其拥有自己的Graphics Layer时,它会绘制自己的Backing Store中,否则的话,它与其它的Paint Layer一起绘制在别的Backing Store中。
我们假设当前要绘制的拥有自己的Graphics Layer,这时候调用它的成员函数compositingState得到的返回值不等于PaintsIntoGroupedBacking,因此接下来CompositedLayerMapping类的成员函数doPaintTask就会调用PaintLayerPainter::PaintLayerContents实现如下所示:
PaintResult PaintLayerPainter::PaintLayerContents(
GraphicsContext& context,
const PaintLayerPaintingInfo& painting_info_arg,
PaintLayerFlags paint_flags_arg) {
...
PaintLayerFragments layer_fragments;
if (should_paint_content || should_paint_self_outline ||
is_painting_overlay_scrollbars) {
...
paint_layer_for_fragments->CollectFragments(
layer_fragments, local_painting_info.root_layer,
&local_painting_info.paint_dirty_rect,
kIgnorePlatformOverlayScrollbarSize, respect_overflow_clip,
&offset_from_root, local_painting_info.sub_pixel_accumulation);
...
bool selection_only =
local_painting_info.GetGlobalPaintFlags() & kGlobalPaintSelectionOnly;
{ // Begin block for the lifetime of any filter.
size_t display_item_list_size_before_painting =
context.GetPaintController().NewDisplayItemList().size();
bool is_painting_root_layer = (&paint_layer_) == painting_info.root_layer;
bool should_paint_background =
should_paint_content && !selection_only &&
(is_painting_composited_background ||
(is_painting_root_layer &&
!(paint_flags & kPaintLayerPaintingSkipRootBackground)));
bool should_paint_neg_z_order_list =
(is_painting_scrolling_content && is_painting_overflow_contents) ||
(!is_painting_scrolling_content && is_painting_composited_background);
bool should_paint_own_contents =
is_painting_composited_foreground && should_paint_content;
bool should_paint_normal_flow_and_pos_z_order_lists =
is_painting_composited_foreground;
bool should_paint_overlay_scrollbars = is_painting_overlay_scrollbars;
base::Optional<ScopedPaintChunkProperties>
subsequence_forced_chunk_properties;
if (subsequence_recorder && paint_layer_.HasSelfPaintingLayerDescendant()) {
...
subsequence_forced_chunk_properties.emplace(
context.GetPaintController(),
paint_layer_.GetLayoutObject()
.FirstFragment()
.LocalBorderBoxProperties(),
paint_layer_, DisplayItem::kUninitializedType);
}
if (should_paint_background) {
if (subsequence_forced_chunk_properties) {
context.GetPaintController().ForceNewChunk(
paint_layer_, DisplayItem::kLayerChunkBackground);
}
PaintBackgroundForFragments(layer_fragments, context,
local_painting_info, paint_flags);
}
if (should_paint_neg_z_order_list) {
if (subsequence_forced_chunk_properties) {
context.GetPaintController().ForceNewChunk(
paint_layer_, DisplayItem::kLayerChunkNegativeZOrderChildren);
}
if (PaintChildren(kNegativeZOrderChildren, context, painting_info,
paint_flags) == kMayBeClippedByPaintDirtyRect)
result = kMayBeClippedByPaintDirtyRect;
}
if (should_paint_own_contents) {
PaintForegroundForFragments(
layer_fragments, context, local_painting_info, selection_only,
!!subsequence_forced_chunk_properties, paint_flags);
}
if (should_paint_self_outline) {
PaintSelfOutlineForFragments(layer_fragments, context,
local_painting_info, paint_flags);
}
if (should_paint_normal_flow_and_pos_z_order_lists) {
if (subsequence_forced_chunk_properties) {
context.GetPaintController().ForceNewChunk(
paint_layer_,
DisplayItem::kLayerChunkNormalFlowAndPositiveZOrderChildren);
}
if (PaintChildren(kNormalFlowChildren | kPositiveZOrderChildren, context,
painting_info,
paint_flags) == kMayBeClippedByPaintDirtyRect)
result = kMayBeClippedByPaintDirtyRect;
}
if (should_paint_overlay_scrollbars) {
PaintOverflowControlsForFragments(layer_fragments, context,
local_painting_info, paint_flags);
}
if (!is_painting_overlay_scrollbars && paint_layer_.PaintsWithFilters() &&
display_item_list_size_before_painting ==
context.GetPaintController().NewDisplayItemList().size()) {
// If a layer with filters painted nothing, we need to issue a no-op
// display item to ensure the filters won't be ignored.
PaintEmptyContentForFilters(context);
}
} // FilterPainter block
bool should_paint_mask = is_painting_mask && should_paint_content &&
paint_layer_.GetLayoutObject().HasMask() &&
!selection_only;
if (should_paint_mask) {
PaintMaskForFragments(layer_fragments, context, local_painting_info,
paint_flags);
} else if ...
return result;
}
收集到了要绘制的Fragment之后,大概就按照以下顺序绘制自己的内容:
1. Background
2. Z-index为负的子Paintr Layer
3. Foreground
4. Outline
5. Z-index为0和正数的子Paint Layer
6. Scollbar
7. Mask
除了子Paint Layer Layer的内容是通过调用成员函数PaintChildren进行绘制的,其余的内容是通过调用成员函数PaintXXXForFragments进行绘制的。
继而进入Layout Object Tree的绘制。
void BlockPainter::PaintObject(const PaintInfo& paint_info,
const LayoutPoint& paint_offset) {
...
// paint background
if (ShouldPaintSelfBlockBackground(paint_phase)) {
...
layout_block_.PaintBoxDecorationBackground(paint_info, paint_offset);
...
}
// paint mask
if (paint_phase == PaintPhase::kMask &&
layout_block_.Style()->Visibility() == EVisibility::kVisible) {
layout_block_.PaintMask(paint_info, paint_offset);
return;
}
// paint foreground
if (paint_phase == PaintPhase::kForeground && paint_info.IsPrinting())
ObjectPainter(layout_block_)
.AddPDFURLRectIfNeeded(paint_info, paint_offset);
// paint contents
if (paint_phase != PaintPhase::kSelfOutlineOnly) {
...
const PaintInfo& contents_paint_info =
scrolled_paint_info ? *scrolled_paint_info : paint_info;
if (layout_block_.IsLayoutBlockFlow()) {
BlockFlowPainter block_flow_painter(ToLayoutBlockFlow(layout_block_));
block_flow_painter.PaintContents(contents_paint_info, paint_offset);
if (paint_phase == PaintPhase::kFloat ||
paint_phase == PaintPhase::kSelection ||
paint_phase == PaintPhase::kTextClip)
block_flow_painter.PaintFloats(contents_paint_info);
} else {
PaintContents(contents_paint_info, paint_offset);
}
}
// paint outline
if (ShouldPaintSelfOutline(paint_phase))
ObjectPainter(layout_block_).PaintOutline(paint_info, paint_offset);
// If the caret's node's layout object's containing block is this block, and
// the paint action is PaintPhaseForeground, then paint the caret.
if (paint_phase == PaintPhase::kForeground &&
layout_block_.ShouldPaintCarets())
PaintCarets(paint_info, paint_offset);
}
void BlockPainter::PaintChildren(const PaintInfo& paint_info) {
for (LayoutBox* child = layout_block_.FirstChildBox(); child;
child = child->NextSiblingBox())
PaintChild(*child, paint_info);
}
void BlockPainter::PaintChild(const LayoutBox& child,
const PaintInfo& paint_info) {
if (!child.HasSelfPaintingLayer() && !child.IsFloating() &&
!child.IsColumnSpanAll())
child.Paint(paint_info); // -----> recursively call LayoutBlock::Paint
}
当子Layout Object不拥有自己的Paint Layer,并且它也没有设置Float属性的时候,就会递归调用LayoutBlock::Paint进行绘制,直到中间某个Layout Object的所有子Render Object都具有自己的Paint Layer为止,或者都设置了float属性。
这样,一个Paint Layer的内容就绘制完成了。PaintResult PaintLayerPainter::PaintLayerContents除了绘制自身内容,还会绘制它的子Paint Layer的内容,
PaintResult PaintLayerPainter::PaintChildren(
unsigned children_to_visit,
GraphicsContext& context,
const PaintLayerPaintingInfo& painting_info,
PaintLayerFlags paint_flags) {
...
PaintLayer* child = iterator.Next();
if (!child)
return result;
for (; child; child = iterator.Next()) {
// If this Layer should paint into its own backing or a grouped backing,
// that will be done via CompositedLayerMapping::PaintContents() and
// CompositedLayerMapping::DoPaintTask().
if (child->PaintsIntoOwnOrGroupedBacking(
painting_info.GetGlobalPaintFlags()))
continue;
if (child->IsReplacedNormalFlowStacking())
continue;
if (PaintLayerPainter(*child).Paint(context, painting_info, paint_flags) ==
kMayBeClippedByPaintDirtyRect)
result = kMayBeClippedByPaintDirtyRect;
}
return result;
}
这一步执行完成之后,回到前面分析的LayerTreeHost::UpdateLayers中,这时候网页的CC Layer Tree的内容就绘制完成了,不过仅仅是将绘制命令记录在一系列的Render Surface,也得到了Property Tree。
然后返回到ProxyMain的成员函数BeginMainFrame中,这时候它就会向Compositor线程的消息队列发送一个Task。Compositor线程开始执行。期间,CrRendererMain线程进入等待状态。
void ProxyMain::BeginMainFrame(
std::unique_ptr<BeginMainFrameAndCommitState> begin_main_frame_state) {
...
{
...
ImplThreadTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&ProxyImpl::NotifyReadyToCommitOnImpl,
base::Unretained(proxy_impl_.get()), &completion,
layer_tree_host_, begin_main_frame_start_time,
hold_commit_for_activation));
completion.Wait();
}
...
}
void ProxyImpl::NotifyReadyToCommitOnImpl(
CompletionEvent* completion,
LayerTreeHost* layer_tree_host,
base::TimeTicks main_thread_start_time,
bool hold_commit_for_activation) {
...
scheduler_->NotifyBeginMainFrameStarted(main_thread_start_time);
host_impl_->ReadyToCommit();
...
scheduler_->NotifyReadyToCommit();
}
Scheduler::NotifyBeginMainFrameStarted把状态机改为BeginMainFrameState::STARTED,继续Scheduler::NotifyReadyToCommit中,它将状态机修改为READY_TO_COMMIT之后,再调用成员函数ProcessScheduledActions将刚刚绘制好的CC Layer Tree同步到一个新的CC Pending Layer Tree中去。