站在老罗的肩膀上: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 whatPerformPreLayoutTasks();...bool in_subtree_layout = IsSubtreeLayout();...PerformLayout(in_subtree_layout);...
}
void LocalFrameView::PerformLayout(bool in_subtree_layout) {...{...if (in_subtree_layout) {// subtree...} else {// whole treeif (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 Objectswhile (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 Layerif (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 LayerPaintLayerPaintingInfo 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 blockbool 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 backgroundif (ShouldPaintSelfBlockBackground(paint_phase)) {...layout_block_.PaintBoxDecorationBackground(paint_info, paint_offset);...}// paint maskif (paint_phase == PaintPhase::kMask &&layout_block_.Style()->Visibility() == EVisibility::kVisible) {layout_block_.PaintMask(paint_info, paint_offset);return;}// paint foregroundif (paint_phase == PaintPhase::kForeground && paint_info.IsPrinting())ObjectPainter(layout_block_).AddPDFURLRectIfNeeded(paint_info, paint_offset);// paint contentsif (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 outlineif (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中去。

Layer Tree 绘制相关推荐

  1. Chromium网页Render Layer Tree创建过程分析

    在前面一文中,我们分析了网页Render Object Tree的创建过程.在创建Render Object Tree的同时,WebKit还会创建Render Layer Tree,但不是每一个Ren ...

  2. Graphics Layer Tree创建

    站在老罗的肩膀上:https://blog.csdn.net/luoshengyang/article/details/50661553 Graphics Layer形成一个Graphics Laye ...

  3. Chromium网页Layer Tree创建过程分析

    在Chromium中,WebKit会创建一个Graphics Layer Tree描述网页.Graphics Layer Tree是和网页渲染相关的一个Tree.网页渲染最终由Chromium的CC模 ...

  4. ios:Layer 异步绘制

    1:layoutSubviews调用时机 init不会触发layoutSubviews 调用addSubview会触发layoutSubviews UIView的Frame改变时(frame的值设置前 ...

  5. 决策树(Decision Tree) | 绘制决策树

    01 起 在这篇文章中,我们讲解了如何训练决策树,然后我们得到了一个字典嵌套格式的决策树结果,这个结果不太直观,不能一眼看着这颗"树"的形状.分支.属性值等,怎么办呢? 本文就上文 ...

  6. 《老罗的Android之旅》专栏目录

    Android学习启动篇 在Ubuntu上下载.编译和安装Android最新源代码 在Ubuntu上下载.编译和安装Android最新内核源代码(Linux Kernel) 如何单独编译Android ...

  7. Chromium网页GPU光栅化原理分析

    在前面一篇文章中,我们分析了网页分块的光栅化过程.根据Chromium的启动选项,网页分块有可能使用GPU来执行光栅化操作,也有可能使用CPU来执行光栅化操作.不管是使用GPU,还是CPU,光栅化操作 ...

  8. Chromium分发输入事件给WebKit处理的过程分析

    Chromium的Render进程接收到Browser进程分发过来的输入事件之后,会在Compoistor线程中处理掉滑动和捏合手势这两种特殊的输入事件,其它类型的输入事件则交给Main线程处理.Ma ...

  9. Chromium网页光栅化过程分析

    在前面一篇文章中,我们分析了网页CC Layer Tree同步为CC Pending Layer Tree的过程.同步操作完成后,CC Pending Layer Tree中的每一个Layer都会被划 ...

最新文章

  1. 华盛顿大学《生成模型》2020秋季课程完结,课件、讲义全部放出
  2. 关于算法中的并查集,写的很有意思,转过来看看~
  3. mysql各种错误提示码和解决方法
  4. 你真的会写二分查找吗?
  5. 设置ComboBox控件的边框颜色.
  6. cocos2dx实现经典飞机大战
  7. org.hibernate.HibernateException: 'hibernate.dialect' must be set when no Connection avalable
  8. oledb驱动Oracle,Oracle学习笔记:手工注册oracle的oledb驱动 | 学步园
  9. python在线编程免费课程-吐血整理!程序员最爱的13个免费Python课程
  10. 第五篇、常用的SQL语句和函数介绍
  11. hadoop安装报错
  12. concat效率 mysql_MYSQL数据库mysql中or效率高还是in效率高
  13. CCF CSP 201512-02 消除类游戏
  14. 五分钟学会安装电脑操作系统
  15. JavaWeb 狂神
  16. 【C语言入门】数字中英翻译
  17. 基于Token的身份验证的原理
  18. poj 1655 树的重心
  19. December, 14
  20. 服务器系统里面怎么查看有没有做raid,windows如何查看服务器raid信息

热门文章

  1. IPhone、Windows Mobil、Symbian、Android移动开发前景分析
  2. 导出富文本格式word
  3. Android实例-手机安全卫士(四十一)-选择自定义Toast背景
  4. 中国人民银行面试题目(经典题目2)
  5. 启动gazebo失败报错[gazebo-1] process has died [pid 10999, exit code 255
  6. 给你一个项目,你会如何开展性能测试工作
  7. 卡路里与脂肪重量的换算
  8. epub 免费转换网站
  9. TypeScript 初学者指南
  10. AI时代——人工智能技术图谱,它来啦(机器学习+深度学习学习路线)