Chromium网页渲染调度器Scheduler实现分析.docx
- 文档编号:29265811
- 上传时间:2023-07-21
- 格式:DOCX
- 页数:72
- 大小:380.65KB
Chromium网页渲染调度器Scheduler实现分析.docx
《Chromium网页渲染调度器Scheduler实现分析.docx》由会员分享,可在线阅读,更多相关《Chromium网页渲染调度器Scheduler实现分析.docx(72页珍藏版)》请在冰豆网上搜索。
Chromium网页渲染调度器Scheduler实现分析
Chromium网页渲染调度器(Scheduler)实现分析
在采用线程化渲染方式渲染网页时,Chromium依赖一个调度器协调Main线程和Compositor线程的执行,同时也通过这个调度器决定它们什么时候该执行什么操作。
调度器将Main线程和Compositor线程的当前状态记录在一个状态机中,然后通过这个状态机决定下一个要执行的操作。
这个操作在满足当前设置条件下是最优的,因此可以使网页渲染更快更流畅。
本文接下来就分析Chromium网页调度器的实现。
调度器实现在Chromium的CC模块中,并且运行在Compositor线程中。
Compositor线程的职责是将网页的内容渲染出来。
从这个角度看,调度器只不过是在调度Compositor线程的执行。
不过由于要渲染的网页内容是由Main线程提供给Compositor线程的,因此调度器也会在必要的时候调度Main线程执行,使得它可以提供最新的网页内容给Compositor线程渲染。
网页是一帧一帧地渲染出来的。
从前面这个系列的文章我们学习到,Android应用程序UI的每一帧的最佳渲染时机是下一个屏幕VSync信号到来时。
Chromium也不例外,它在渲染网页的时候,也是利用了屏幕的VSync信号。
这一点在调度器的时间轴中可以得到体现,如图1所示:
从图1可以看到,调度器并没有严格在VSync到来时就去渲染网页的下一帧,而是为网页的下一帧渲染时机设置了一个Deadline。
在Deadline到来前,调度器可以调度执行其它的渲染操作。
在继续分析上述的Deadline机制之前,我们要先搞清楚网页的一帧渲染涉及到哪些操作。
这些操作如图2所示:
图2的完整分析可以参考前面一文。
我们前面说的Deadline,是针对第6个操作ACTION_DRAW_AND_SWAP_FORCED而言的。
也就是说,当VSync信号到来时,ACTION_DRAW_AND_SWAP_FORCED操作最迟必须在设置的Deadline到来时执行。
这个Deadline是怎么计算出来的呢?
我们先来看网页的渲染过程。
首先是Render进程进行渲染,然后交给Browser进程进行合成。
因此,网页的渲染过程可以看作由两部分时间组成:
estimated_draw_duration+estimated_browser_composite_time。
其中,estimated_draw_duration表示Render进程的渲染时间,estimated_browser_composite_time表示Browser进程的合成时间。
假设下一个VSync到来的时间为frame_time,VSync信号时间周期为interval,那么就可以计算出Deadline=frame_time+(interval-estimated_draw_duration-estimated_browser_composite_time)。
剩下来的时间区间[frame_time,deadline)可以用做其它事情,例如执行图2所示的第2个操作ACTION_SEND_BEGIN_MAIN_FRAME,也就是通知Main线程对CCLayerTree进行绘制。
时间区间[frame_time,deadline)称为BEGIN_IMPL_FRAME时间。
在BEGIN_IMPL_FRAME时间内,存在四个BeginImplFrameState状态,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
classCC_EXPORTSchedulerStateMachine{
public:
......
//Note:
BeginImplFrameStatewillalwayscyclethroughallthestatesin
//order.Whetherornotitactuallywaitsordraws,itwillatleasttryto
//waitinBEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAMEandtrytodrawin
//BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE
enumBeginImplFrameState{
BEGIN_IMPL_FRAME_STATE_IDLE,
BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING,
BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME,
BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE,
};
......
protected:
......
BeginImplFrameStatebegin_impl_frame_state_;
......
};
这个状态定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.h中。
调度器通过类SchedulerStateMachine描述内部的状态机。
状态机的BeginImplFrameState状态记录在SchedulerStateMachine类的成员变量begin_impl_frame_state_中。
四个BeginImplFrameState状态分别为:
1.BEGIN_IMPL_FRAME_STATE_IDLE
2.BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING
3.BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME
4.BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE
它们的变迁关系如图3所示:
下一个VSync信号到来之前,状态机处于BEGIN_IMPL_FRAME_STATE_IDLE状态。
下一个VSync信号到来之时,调度器调用SchedulerStateMachine类的成员函数OnBeginImplFrame将状态机的BeginImplFrameState状态设置为BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING。
这时候调度器通过调用Scheduler类的成员函数ProcessScheduledActions调度计算网页中的动画,或者执行图2所示的第2个操作ACTION_SEND_BEGIN_MAIN_FRAME,也就是通知Main线程对网页内容进行绘制。
从Scheduler类的成员函数ProcessScheduledActions返回后,BeginImplFrameState状态就从BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING变为BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME。
这时候调度器等待Deadline的到来。
Deadline到来之时,调度器调用SchedulerStateMachine类的成员函数OnBeginImplFrameDeadline将BeginImplFrameState状态从BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME设置为BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE。
这时候调度器就会通过调用Scheduler类的成员函数ProcessScheduledActions调度执行网页的渲染操作,也就是图2所示的第6个操作ACTION_DRAW_AND_SWAP_FORCED。
从Scheduler类的成员函数ProcessScheduledActions返回后,BeginImplFrameState状态就从BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE重新变为BEGIN_IMPL_FRAME_STATE_IDLE。
这时候调度器等待再下一个VSync信号的到来。
状态机除了有BeginImplFrameState状态,还有其它三个状态,分别是OutputSurfaceState、CommitState和ForcedRedrawOnTimeoutState。
OutputSurfaceState描述的是网页绘图表面的状态,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
classCC_EXPORTSchedulerStateMachine{
public:
......
enumOutputSurfaceState{
OUTPUT_SURFACE_ACTIVE,
OUTPUT_SURFACE_LOST,
OUTPUT_SURFACE_CREATING,
OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT,
OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION,
};
......
protected:
......
OutputSurfaceStateoutput_surface_state_;
......
};
这个状态定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.h中。
网页绘图表面的状态记录在SchedulerStateMachine类的成员变量output_surface_state_中。
网页绘图表面的状态有五个状态,分别是:
1.OUTPUT_SURFACE_LOST
2.OUTPUT_SURFACE_CREATING
3.OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT
4.OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION
5.OUTPUT_SURFACE_ACTIVE
它们的变迁关系如图4所示:
网页绘图表面最初处于OUTPUT_SURFACE_LOST状态,等到CCLayerTree创建之后,调度器会调度图2所示的第2个操作ACTION_BEGIN_OUTPUT_SURFACE_CREATION,也就是请求Compositor线程为网页创建绘图表面,这时候网页绘图表面的状态变为OUTPUT_SURFACE_CREATING。
Compositor线程为网页创建好了绘图表面之后,就会调用SchedulerStateMachine类的成员函数DidCreateAndInitializeOutputSurface将绘图表面的状态设置为OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT。
这将会触发调度器尽快调度执行图2所示的第2个操作ACTION_SEND_BEGIN_MAIN_FRAME和第3个操作ACTION_COMMIT,也就是请求Main线程对CCLayerTree进行绘制,并且将其同步到CCPendingLayerTree中去。
这时候绘图表面的状态变为OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION,表示要尽快将CCPendingLayerTree激活为CCActiveLayerTree。
CCPendingLayerTree被激活之后,也就是图2所示的第5个操作ACTION_ACTIVATE_PENDING_TREE执行之后,绘图表面以后就会一直处于OUTPUT_SURFACE_ACTIVE状态。
不过,在绘图表面处于OUTPUT_SURFACE_ACTIVE状态期间,如果Render进程与GPU进程之间的GPU通道断开了连接,或者GPU进程在解析Render进程发送来的GPU命令时发生了错误,那么SchedulerStateMachine类的成员函数DidLoseOutputSurface会被调用。
这时候绘图表面的状态就会被设置为OUTPUT_SURFACE_LOST状态。
这将会触发调度器调度执行ACTION_BEGIN_OUTPUT_SURFACE_CREATION操作,以便为网页重新创建绘图表面。
CommitState描述的是CCLayerTree的提交状态,包括同步到CCPendingLayerTree,以及CCPendingLayerTree激活为CCActiveLayerTree的过程,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
classCC_EXPORTSchedulerStateMachine{
public:
......
enumCommitState{
COMMIT_STATE_IDLE,
COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,
COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED,
COMMIT_STATE_READY_TO_COMMIT,
COMMIT_STATE_WAITING_FOR_ACTIVATION,
COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
};
......
private:
......
CommitStatecommit_state_;
......
};
这个状态定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.h中。
CCLayerTree的提交状态记录在SchedulerStateMachine类的成员变量commit_state_中。
CCLayerTree有六个提交状态,分别是:
1.COMMIT_STATE_IDLE
2.COMMIT_STATE_BEGIN_MAIN_FRAME_SENT
3.COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED
4.COMMIT_STATE_READY_TO_COMMIT
5.COMMIT_STATE_WAITING_FOR_ACTIVATION
6.COMMIT_STATE_WAITING_FOR_FIRST_DRAW
它们的变迁关系如图5所示:
CCLayerTree的提交状态最开始时被设置为COMMIT_STATE_IDLE。
当调度器调度执行图2所示的第2个操作ACTION_BEGIN_MAIN_FRAME时,CCLayerTree的提交状态被设置为COMMIT_STATE_BEGIN_MAIN_FRAME_SENT,表示调度器已经请求Main线程对CCLayerTree进行绘制。
在Main线程执行ACTION_BEGIN_MAIN_FRAME操作期间,CCLayerTree有可能变为不可见,这时候调度器就会调用SchedulerStateMachine类的成员函数BeginMainFrameAborted重新设置为COMMIT_STATE_IDLE。
Main线程执行完成ACTION_BEGIN_MAIN_FRAME操作之后,调度器就会调用SchedulerStateMachine类的成员函数NotifyReadyToCommit将CCLayerTree的提交状态设置为COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED。
这时候Main线程会通知Compositor线程对网页中的图片资源以纹理方式上传到GPU去,以便后面进行渲染显示。
图片资源上传完毕,调度器就会调用SchedulerStateMachine类的成员函数NotifyReadyToCommit将CCLayerTree的提交状态设置为COMMIT_STATE_READY_TO_COMMIT,表示CCLayerTree需要同步到CCPendingLayerTree中去。
这将会触发调度器调度执行图2所示的第三个操作ACTION_COMMIT,也就是将CCLayerTree同步到CCPendingLayerTree中去。
ACTION_COMMIT操作执行完成之后,CCLayerTree的提交状态会从COMMIT_STATE_READY_TO_COMMIT变为以下三个状态之一:
1.在满足以下两个条件之一时,变为COMMIT_STATE_IDLE:
A.main_frame_before_activation_enabled被设置为true。
这表示在上一个CCPendingLayerTree被激活为CCActiveLayerTree之前,允许Main线程绘制网页的下一帧。
B.main_frame_before_draw_enabled被设置为true,但是impl_side_painting被设置为false。
main_frame_before_draw_enabled设置为true,表示在上一个CCActiveLayerTree被渲染之前,允许Main线程绘制网页的下一帧。
impl_side_painting设置为true表示Main线程在绘制网页时,实际上只是记录了网页的绘制命令。
只有在impl_side_painting设置为true的时候,才会有CCPendingLayerTree被激活为CCActiveLayerTree的环节。
因此,在impl_side_painting等于false的情况下,main_frame_before_draw_enabled被设置为true等同于main_frame_before_activation_enabled被设置为true的情况。
2.FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION。
需要满足的条件是main_frame_before_draw_enabled和impl_side_painting均被设置为true,并且main_frame_before_activation_enabled被设置为false。
这表示在上一个CCPendingLayerTree被激活为CCActiveLayerTree之后,才允许Main线程绘制网页的下一帧。
3.COMMIT_STATE_WAITING_FOR_FIRST_DRAW。
需要满足的条件是main_frame_before_activation_enabled和main_frame_before_draw_enabled均被设置为true。
这表示在上一个CCActiveLayerTree第一次渲染之后,才允许Main线程绘制网页的下一帧。
这实际上是给予CCActiveLayerTree更高的优先级,使得它一激活就马上进行渲染。
如果CCLayerTree的提交状态处于FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION,那么当CCPendingLayerTree被激活为CCActiveLayerTree之后,也就是图2所示的第5个操作ACTION_ACTIVATE_PENDING_TREE执行之后,CCLayerTree的提交状态就变为COMMIT_STATE_IDLE。
如果CCLayerTree的提交状态处于COMMIT_STATE_WAITING_FOR_FIRST_DRAW,那么当CCActiveLayerTree被渲染之后,也就是图2所示的第6个操作ACTION_DRAW_AND_SWAP_FORCED,或者另外一个操作ACTION_DRAW_AND_SWAP_IF_POSSIBLE执行之后,CCLayerTree的提交状态就变为COMMIT_STATE_IDLE。
注意,只有CCLayerTree的提交状态处于COMMIT_STATE_IDLE时,Main线程才可以绘制网页的下一帧。
ForcedRedrawOnTimeoutState描述的是网页的渲染状态,如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
classCC_EXPORTSchedulerStateMachine{
public:
......
enumForcedRedrawOnTimeoutState{
FORCED_REDRAW_STATE_IDLE,
FORCED_REDRAW_STATE_WAITING_FOR_COMMIT,
FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION,
FORCED_REDRAW_STATE_WAITING_FOR_DRAW,
};
......
private:
......
ForcedRedrawOnTimeoutStateforced_redraw_state_;
......
};
这个状态定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.h中。
网页的渲染状态记录在SchedulerStateMachine类的成员变量forced_redraw_state_中。
网页的渲染状态有四个,分别是:
1.FORCED_REDRAW_STATE_IDLE,
2.FORCED_REDRAW_STATE_WAITING_FOR_COMMIT
3.FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION
4.FORCED_REDRAW_STATE_WAITING_FOR_DRAW
它们的变迁关系如图6所示:
网页渲染状态最开始处于FORCED_REDRAW_STATE_IDLE状态。
在渲染动画的过程中,如果某些分块(Tile)还没有光栅化好,那么CC模块就会用棋盘(Checkboard)来代替这些缺失的分块。
这时候的网页渲染结果被视为DRAW_ABORTED_CHECKERBOARD_ANIMATIONS。
如果网页连续渲染结果都是DRAW_ABORTED_CHECKERBOARD_ANIMATIONS的次数超出预设值,那么网页渲染状态就会被设置为FORCED_REDRAW_STATE_WAITING_FOR_COMMIT,表示要尽快执行一次图2所示的第3个操作ACTION_COMMIT,以便补充缺失的分块。
ACTION_COMMIT操作执行完成之后,如果Main线程在绘制网页时,仅仅记录了CCLayerTree的绘制命令,也就是前面提到的impl_side_painting等于true,那么就意味着存在一个CCPendingLayerTree,这时候网页渲染状态会被设置为F
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Chromium 网页 渲染 调度 Scheduler 实现 分析