这一篇,我们来研究一下GPU进程的实现细节,首先是GPU对外暴露的接口形式:
GPU Command Buffer
在GPU独立进程的架构中,Command Buffer作为GPU Server 与 Client的通信协议扮演着重要角色。它将对GPU图形接口(OpenGL ES 2.0)转化成一条条命令,用来支持跨进程调用。提高了GPU调用的安全性、通用性、和并发性。其定义的协议与直接调用GPU图形接口相比,裁剪掉了一些不通用的特性,例如GLSL的高级特性。同步改为异步,这种改造对glUniform 、glDrawArrays这种比较费的接口效果尤为明显。这样,就大大提高了GPU的并行程度,而这恰好比较适合GPU的特性——善于处理并行任务。
Command Buffer本质上是一块共享内存,用于跨进程传输数据,根据OpenGL ES2接口的不同,有三种方式:
1、直接通过Command Buffer传输,这种直接将调用参数写到Command Buffer,由于Command Buffer有固定的长度,因此,参数的数据量不能太大,像glBlendColor这种调用可以采用。
2、通过共享内存,当调用需要传输大数据时,例如传送图片。就会先将图片数据拷贝如Shared memory,然后用Command传送shared memory id和便宜即可。
3、抽象的bucket,这个实际上是对共享内存的封装,它要解决的问题是如果需要传输大量数据,而shared memory不能一次传输时,bucket可以用多次传输的方式完成。而对我暴露的接口时一次调用完成的。使用起来比较方便。
下图是一个典型的跨进程调用结构:
在Client进程中,所有的渲染请求会被转化为OpenGL ES 2.0的接口调用,通过GLES2Implementation将调用发送给CommandBufferHelper,将调用翻译成Command,CommandBufferProxy发送Command到GPU进程。
GPU进程收到Command Buffer后,会交给CommandBufferService,通过GpuScheduler调度,解析,解码变成了OpenGL ES 2.0调用。Windows系统下,Chrome使用了Angle库,提供了一个libGLESv2.dll导出OpenGL ES 2.0相同的接口,替换掉了系统自带的Dll,而这个DLL又将OpenGL的调用转化成了DirectX调用,渲染结束。
这里释一下Chrome在Windows上选择用Angle转换接口的原因。OpenGL与DirectX的争斗由来已久,OpenGL以其开放性和在专业领域的权威性对抗微软的商业化。但市场是残酷的,微软凭着Windows的市场占有率和商业化运作得到了大部分游戏生产厂商和硬件厂商的支持(曾经一度SGI和微软有合并图形接口的意向,但后来失败了,微软借此也给了OpenGL一定的打击。见Farenheit门)。在DirectX7.0以后,微软已经成为图形接口的实际制定者。而对不同版本DirectX的支持,也成了判别一款显卡级别的重要参考。在这种大趋势下,各大显卡厂商(Nvidia ATI)把在Windows上支持DirectX的驱动作为高优先级,因此,就出现了在Windows上有很多显卡驱动对OpenGL支持的并不好。Chrome不惜通过一次转接去调用DirectX也就不足为怪了。
Command Buffer使用了OpenGL的100多个函数,并对这些函数做了适合Command Buffer体系的封装。这些函数被定义在:
src/gpu/command_buffer/cmd_buffer_functions.txt
通过src/gpu/command_buffer/build_gles2_cmd_buffer.py 脚本会生成很多以_autogen结尾的文件,这些文件在不同的层次定义了opengl es的函数:
src/gpu/command_buffer/client/gles2_c_lib_autogen.h 是OpenGL ES 的C接口
src/gpu/command_buffer/common/gles2_cmd_format_autogen.h 是定义了Command的格式
src/gpu/command_buffer/client/gles2_cmd_helper_autogen.h 定义了用于生成Command的辅助类
src/gpu/command_buffer/client/gles2_implementation_autogen.h 定义了Command Buffer的实现函数定义,在.cc文件里,实现了Command写入到buffer的操作。
src/gpu/command_buffer/service/gles2_cmd_decoder_autogen.cc 定义了GPU进程在接受Command之后的解析代码
在这里需要赞一下chromer的行事风格,为了能够通过python脚本自动生成这些代码,要对OpenGL函数做详尽的分类,制定规则,然后写一个很复杂的python脚本。这样做的复杂程度要比简单的copy paste代码高。而这样做的好处也是显而易见的——好维护,易扩展。这个对一个像chrome这样庞大的工程来说,意义极其重要。OpenGL ES 3.0已经出来了,在可预见的未来,这部分接口一定会扩充。这样的做法体现着一个工程师对未来的责任感。