摘要:Part I分析了GPU客户端之间存在的同步问题,以及Chromium的GL扩展同步点机制的基本原理。本文将源代码的角度剖析同步点(SyncPoint)机制的实现方式。同步点机制的实现主要涉及到是如何跨进程实现两个GL扩展接口InsertSyncPointCHROMIUM和WaitSyncPointCHROMIUM,以及GPU服务端的同步点等待。
GPU客户端
GPU客户端将所有的GL命令都封装在GLES2Implementation中,GLES2Implementation将客户端的GL命令序列化后存放在命令行缓冲区中,客户端的每个WebGraphicsContext3DImpl都会创建一个GLES2Implementation实例,调用GLES2Implementation的方法看上去就像直接调用OpenGL方法直接操作GPU设备一样。GPU客户端与GPU进程之间的交互封装在GpuCommandBufferProxy类中,这个代理类会向GPU服务端的GpuCommandBufferStub发送IPC消息请求执行某些GPU操作,如发送GpuCommandBufferMsg_AsyncFlush消息提交(Flush)命令缓冲区。
同步点机制首先需要在GLES2Implementation类中实现这两个GL扩展接口,以便允许客户端代码使用同步点机制。GLES2Implementation::InsertSyncPointCHROMIUM通过GpuCommandBufferProxy向GPU服务端发送一条同步IPC消息GpuCommandBufferMsg_InsertSyncPoint请求注册一个新的同步点,在调用CommandBufferProxyImpl::InsertSyncPoint之前,GLES2Implementation会将客户端的所有GL命令通过Flush命令行缓冲区都提交到服务端,如下代码:
GLuint GLES2Implementation::InsertSyncPointCHROMIUM() {
GPU_CLIENT_SINGLE_THREAD_CHECK();
GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glInsertSyncPointCHROMIUM");
helper_->CommandBufferHelper::Flush();
return gpu_control_->InsertSyncPoint();
}
...
uint32 CommandBufferProxyImpl::InsertSyncPoint() {
if (last_state_.error != gpu::error::kNoError)
return 0;
uint32 sync_point = 0;
Send(new GpuCommandBufferMsg_InsertSyncPoint(route_id_, true, &sync_point));
return sync_point;
}
同步IPC消息GpuCommandBufferMsg_InsertSyncPoint的参数sync_point返回由GPU服务端统一分配的唯一标识。
而与InsertSyncPointCHROMIUM不同的是,WaitSyncPointCHROMIUM不是通过发送专门的IPC消息给GPU服务端,而是作为一条GL扩展命令添加到CommandBuffer中,GPU服务端处理这条GL命令时再决定如何执行暂停后续命令的提交,等待同步点的完成。
GPU服务端
GPU服务端既可以是一个独立的进程,也可以是一个Browser进程的一个线程,不论是进程还是线程,所有GPU操作了都是在同一个线程上执行,