Chromium硬件加速渲染的OpenGL上下文调度过程分析资料.docx
- 文档编号:1101787
- 上传时间:2022-10-17
- 格式:DOCX
- 页数:76
- 大小:372.46KB
Chromium硬件加速渲染的OpenGL上下文调度过程分析资料.docx
《Chromium硬件加速渲染的OpenGL上下文调度过程分析资料.docx》由会员分享,可在线阅读,更多相关《Chromium硬件加速渲染的OpenGL上下文调度过程分析资料.docx(76页珍藏版)》请在冰豆网上搜索。
Chromium硬件加速渲染的OpenGL上下文调度过程分析资料
Chromium硬件加速渲染的OpenGL上下文调度过程分析
Chromium的每一个WebGL端、Render端和Browser端实例在GPU进程中都有一个OpenGL上下文。
这些OpenGL上下文运行在相同线程中,因此同一时刻只有一个OpenGL上下文处于运行状态。
这就引发出一个OpenGL上下文调度问题。
此外,事情有轻重缓急,OpenGL上下文也有优先级高低之分,优先级高的要保证它的运行时间。
本文接下来就分析GPU进程调度运行OpenGL上下文的过程。
在前面一文中提到,GPU进程中的所有OpenGL上下文不仅运行在相同线程中,即运行在GPU进程的GPU线程中,它们还处于同一个共享组中,如图1所示:
在Chromium中,每一个OpenGL上下文使用一个GLContextEGL对象描述。
每一个OpenGL上下文都关联有一个绘图表面。
对于WebGL端和Render端的OpenGL上下文来说,它关联的绘图表面是一个离屏表面。
这个离屏表面一般就是用一个Pbuffer描述。
在Android平台上,Browser端的OpenGL上下文关联的绘图表面是一个SurfaceView。
当一个OpenGL上下文被调度时,它以及它关联的绘图表面就会通过调用EGL函数eglMakeCurrent设置为GPU线程当前使用的OpenGL上下文和绘图表面。
从前面一文又可以知道,Chromium为WebGL端、Render端和Browser端创建的OpenGL上下文可能是虚拟的,如图2所示:
在Chromium中,每一个虚拟OpenGL上下文都使用一个GLContextVirtual对象描述。
每一个虚拟OpenGL上下文都对应有一个真实OpenGL上下文,即一个GLContextEGL对象,并且所有的虚拟OpenGL上下文对应的真实OpenGL上下文都是相同的。
虚拟OpenGL上下文也像真实OpenGL上下文一样,关联有绘图表面。
对于WebGL端和Render端的虚拟OpenGL上下文来说,它关联的绘图表面也是一个使用Pbuffer描述的离屏表面。
在Android平台上,Browser端的OpenGL上下文关联的绘图表面同样也是一个SurfaceView。
当一个虚拟OpenGL上下文被调度时,它对应的真实OpenGL上下文以及它关联的绘图表面就会通过调用EGL函数eglMakeCurrent设置为GPU线程当前使用的OpenGL上下文和绘图表面。
由于所有的虚拟OpenGL上下文对应的真实OpenGL上下文都是相同的,因此当一个虚拟OpenGL上下文被调度时,只需要通过调用EGL函数eglMakeCurrent将其关联的绘图表面设置为GPU线程当前使用的绘图表面即可。
前面提到,OpenGL上下文有优先级高低之分,具体表现为Browser端的OpenGL上下文优先级比WebGL端和Render端的高。
这是因为前者负责合成后者的UI显示在屏幕中,因此就要保证它的运行时间。
在Browser端的OpenGL上下文需要调度运行而GPU线程又被其它OpenGL上下文占有时,Browser端的OpenGL上下文就可以抢占GPU线程。
为达到这一目的,Chromium给Browser端与GPU进程建立的GPU通道设置IDLE、WAITING、CHECKING、WOULD_PREEMPT_DESCHEDULED和PREEMPTING五个状态。
这五个状态的变迁关系如图3所示:
当Browser端的GPU通道处于PREEMPTING状态时,Browser端的OpenGL上下文就可以要求其它OpenGL上下文停止执行手头上的任务,以便将GPU线程交出来运行Browser端的OpenGL上下文。
Browser端的GPU通道开始时处于IDLE状态。
当有未处理IPC消息时,就从IDLE状态进入WAITING状态。
进入WAITING状态kPreemptWaitTimeMs毫秒之后,就自动进入CHECKING状态。
kPreemptWaitTimeMs毫秒等于2个kVsyncIntervalMs毫秒,kVsyncIntervalMs定义为17。
假设屏幕的刷新速度是60fps,那么kVsyncIntervalMs毫秒刚好就是一个Vsync时间,即一次屏幕刷新时间间隔。
处于CHECKING状态期间时,Browser端的GPU通道会不断检查最早接收到的未处理IPC消息流逝的时间是否小于2次屏幕刷新时间间隔。
如果小于,那么就继续停留在CHECKING状态。
否则的话,就进入WOULD_PREEMPT_DESCHEDULED状态或者PREEMPTING状态。
图1所示的stub指的是一个GpuCommandBufferStub对象。
从前面Chromium硬件加速渲染的OpenGL上下文创建过程分析一文可以知道,在GPU进程中,一个GpuCommandBufferStub对象描述的就是一个OpenGL上下文。
因此,图1所示的stub指的是一个Browser端OpenGL上下文。
处于CHECKING状态期间时,如果最早接收到的未处理IPC消息流逝的时间大于等于2次屏幕刷新时间间隔,并且没有任何的Browser端OpenGL上下文自行放弃调度,那么Browser端的GPU通道就会进入PREEMPTING状态,表示它要抢占GPU线程,也表示要求当前正在调度的OpenGL上下文放弃占有GPU线程。
另一方面,如果这时候至少有一个Browser端OpenGL上下文自行放弃调度,那么Browser端的GPU通道就会进入WOULD_PREEMPT_DESCHEDULED状态,表示它现在不急于抢占GPU线程,因为这时候有OpenGL上下文自行放弃了调度,从而使得最早接收到的未处理消息所属的OpenGL上下文得到调度处理。
处于WOULD_PREEMPT_DESCHEDULED状态时,Browser端的GPU通道会继续检查是否有Browser端OpenGL上下文自行放弃调度。
如果没有,那么就进入PREEMPTING状态,表示要抢占GPU线程。
如果有,并且这时候Browser端的GPU通道接收到的IPC消息均已被处理,或者最早接收到的未处理IPC消息的流逝时间小于kStopPreemptThresholdMs毫秒,那么就进入IDLE状态。
否则的话,就继续维持WOULD_PREEMPT_DESCHEDULED状态。
kStopPreemptThresholdMs也定义为17,意思是Browser端的GPU通道处于WOULD_PREEMPT_DESCHEDULED状态时,允许最早接收到的未处理IPC消息延迟一次屏幕刷新时间间隔再进行处理。
Browser端的GPU通道处于PREEMPTING状态的最长时间为kMaxPreemptTimeMs毫秒。
kMaxPreemptTimeMs也定义为17,意思是Browser端的GPU通道抢占GPU线程的时间不能超过一个屏幕刷新时间间隔。
如果超过了一个屏幕刷新时间间隔,那么就会进入IDLE状态。
在处于PREEMPTING状态期间,如果Browser端的GPU通道接收到的IPC消息均已被处理,或者最早接收到的未处理IPC消息的流逝时间小于kStopPreemptThresholdMs毫秒,那么Browser端的GPU通道也会进入IDLE状态。
此外,在处于PREEMPTING状态期间,如果至少有一个Browser端OpenGL上下文自行放弃调度,那么Browser端的GPU通道就会进入WOULD_PREEMPT_DESCHEDULED状态。
注意,在图3所示的状态变迁图中,只有处于PREEMPTING状态时,Browser端的GPU通道才会强行抢占GPU线程。
这是为了保证Browser端的OpenGL上下文,至少应该需要在两个屏幕刷新时间间隔之内,得到一次调度,从而保证网页UI得到刷新和及时显示。
WebGL端和Render端的OpenGL上下文就没有这种待遇,毕竟它们的优先级没有Browser端的OpenGL上下文高。
Browser端GPU通道是以什么方式强行抢占GPU线程的呢?
我们通过图4说明,如下所示:
Browser端GPU通道有一个PreemptionFlag。
当它处于PREEMPTING状态时,就会将PreemptionFlag设置为True。
WebGL端和Render端GPU通道可以访问Browser端GPU通道的PreemptionFlag。
属于WebGL端和Render端GPU通道的OpenGL上下文在调度期间,会不断地检查Browser端GPU通道的PreemptionFlag是否被设置为True。
如果被设置为True,那么它就会中止执行,提前释放GPU线程。
WebGL端和Render端GPU通道和Browser端GPU通道是父子关系。
其中,WebGL端和Render端GPU通道是儿子,Browser端GPU通道是父亲。
Chromium规定,儿子GPU通道可以访问父亲GPU通道的PreemptionFlag。
有了前面这些背景知识之后,接下来我们就结合源码分析OpenGL上下文的调度过程。
从前面一文可以知道,GPU进程通过调用GpuChannel类的成员函数Init创建GPU通道,如下所示:
[cpp]viewplaincopy
voidGpuChannel:
:
Init(base:
:
MessageLoopProxy*io_message_loop,
base:
:
WaitableEvent*shutdown_event){
......
channel_=IPC:
:
SyncChannel:
:
Create(channel_id_,
IPC:
:
Channel:
:
MODE_SERVER,
this,
io_message_loop,
false,
shutdown_event);
filter_=
newGpuChannelMessageFilter(weak_factory_.GetWeakPtr(),
gpu_channel_manager_->sync_point_manager(),
base:
:
MessageLoopProxy:
:
current());
......
channel_->AddFilter(filter_.get());
......
}
这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel.cc中。
结合前面一文可以知道,WebGL端、Render端和Browser端发送过来的GPU消息由GpuChannel类的成员函数OnMessageReceived负责接收。
在接收之前,这些GPU消息首先会被GpuChannel类的成员变量filter_指向的一个GpuChannelMessageFilter对象的成员函数OnMessageReceived过滤。
注意,GpuChannel类的成员函数Init是在GPU线程中执行的,这意味着GpuChannel类的成员函数OnMessageReceived也将会在GPU线程中执行,但是它的成员变量filter_指向的GpuChannelMessageFilter对象的成员函数OnMessageReceived不是在GPU线程执行的,而是在负责接收IPC消息的IO线程中执行的。
接下来我们先分析GpuChannel类的成员函数OnMessageReceived的实现,后面分析Browser端GPU通道抢占GPU线程的过程时,再分析GpuChannelMessageFilter类的成员函数OnMessageRe
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Chromium 硬件加速 渲染 OpenGL 上下文 调度 过程 分析 资料