站在老罗的肩膀上: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中去。
联系客服
微信登录中...
请勿关闭此页面