Chromium硬件加速渲染的OpenGL上下文调度过程分析.doc_第1页
Chromium硬件加速渲染的OpenGL上下文调度过程分析.doc_第2页
Chromium硬件加速渲染的OpenGL上下文调度过程分析.doc_第3页
Chromium硬件加速渲染的OpenGL上下文调度过程分析.doc_第4页
Chromium硬件加速渲染的OpenGL上下文调度过程分析.doc_第5页
已阅读5页,还剩60页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

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通道有一个Preemption Flag。当它处于PREEMPTING状态时,就会将Preemption Flag设置为True。WebGL端和Render端GPU通道可以访问Browser端GPU通道的Preemption Flag。属于WebGL端和Render端GPU通道的OpenGL上下文在调度期间,会不断地检查Browser端GPU通道的Preemption Flag是否被设置为True。如果被设置为True,那么它就会中止执行,提前释放GPU线程。 WebGL端和Render端GPU通道和Browser端GPU通道是父子关系。其中, WebGL端和Render端GPU通道是儿子,Browser端GPU通道是父亲。Chromium规定,儿子GPU通道可以访问父亲GPU通道的Preemption Flag。有了前面这些背景知识之后,接下来我们就结合源码分析OpenGL上下文的调度过程。 从前面一文可以知道,GPU进程通过调用GpuChannel类的成员函数Init创建GPU通道,如下所示:cpp view plain copyvoid GpuChannel: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_ = new GpuChannelMessageFilter(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类的成员函数OnMessageReceived的实现。 GpuChannel类的成员函数OnMessageReceived的实现如下所示:cpp view plain copybool GpuChannel:OnMessageReceived(const IPC:Message& message) . if (message.type() = GpuCommandBufferMsg_WaitForTokenInRange:ID | message.type() = GpuCommandBufferMsg_WaitForGetOffsetInRange:ID) / Move Wait commands to the head of the queue, so the renderer / doesnt have to wait any longer than necessary. deferred_messages_.push_front(new IPC:Message(message); else deferred_messages_.push_back(new IPC:Message(message); OnScheduled(); return true; 这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel.cc中。 GpuChannel类将接收到的IPC消息保存在成员变量deferred_messages_描述的一个std:deque中。其中,类型为GpuCommandBufferMsg_WaitForTokenInRange和GpuCommandBufferMsg_WaitForGetOffsetInRange的GPU消息将会保存在上述std:deque的头部,而其它GPU消息则保存在上述std:deque的末部。 从前面和这篇文章可以知道,当GPU进程接收到类型为GpuCommandBufferMsg_WaitForTokenInRange和GpuCommandBufferMsg_WaitForGetOffsetInRange的IPC消息时,就表示它的Client端,即WebGL端、Render端和Browser端,正在检查其GPU命令缓冲区的执行情况,需要尽快得到结果,因此就需要将它们放在上述std:deque的头部,以便尽快得到处理。 GpuChannel类的成员函数OnMessageReceived将接收到的GPU消息保存在成员变量deferred_messages_描述的std:deque之后,接下来调用另外一个成员函数OnScheduled对它们进行调度处理,如下所示:cpp view plain copyvoid GpuChannel:OnScheduled() if (handle_messages_scheduled_) return; / Post a task to handle any deferred messages. The deferred message queue is / not emptied here, which ensures that OnMessageReceived will continue to / defer newly received messages until the ones in the queue have all been / handled by HandleMessage. HandleMessage is invoked as a / task to prevent reentrancy. base:MessageLoop:current()-PostTask( FROM_HERE, base:Bind(&GpuChannel:HandleMessage, weak_factory_.GetWeakPtr(); handle_messages_scheduled_ = true; 这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel.cc中。 GpuChannel类的成员函数OnScheduled通过向GPU线程的消息队列发送一个Task请求为当前接收到GPU消息的GPU通道执行一次调度。该Task绑定的函数为GpuChannel类的成员函数HandleMessage。 GpuChannel类的成员变量handle_messages_scheduled_的值等于true时,表示当前接收到GPU消息的GPU通道已经请求过调度了,并且请求的调度还没有被执行。在这种情况下,就不必再向GPU线程的消息队列发送Task。 GpuChannel类的成员函数HandleMessage的实现如下所示:cpp view plain copyvoid GpuChannel:HandleMessage() handle_messages_scheduled_ = false; if (deferred_messages_.empty() return; bool should_fast_track_ack = false; IPC:Message* m = deferred_messages_.front(); GpuCommandBufferStub* stub = stubs_.Lookup(m-routing_id(); do if (stub) if (!stub-IsScheduled() return; if (stub-IsPreempted() OnScheduled(); return; scoped_ptr message(m); deferred_messages_.pop_front(); bool message_processed = true; currently_processing_message_ = message.get(); bool result; if (message-routing_id() = MSG_ROUTING_CONTROL) result = OnControlMessageReceived(*message); else result = router_.RouteMessage(*message); currently_processing_message_ = NULL; if (!result) / Respond to sync messages even if router failed to route. if (message-is_sync() IPC:Message* reply = IPC:SyncMessage:GenerateReply(&*message); reply-set_reply_error(); Send(reply); else / If the command buffer becomes unscheduled as a result of handling the / message but still has more commands to process, synthesize an IPC / message to flush that command buffer. if (stub) if (stub-HasUnprocessedCommands() deferred_messages_.push_front(new GpuCommandBufferMsg_Rescheduled( stub-route_id(); message_processed = false; if (message_processed) MessageProcessed(); / We want the EchoACK following the SwapBuffers to be sent as close as / possible, avoiding scheduling other channels in the meantime. should_fast_track_ack = false; if (!deferred_messages_.empty() m = deferred_messages_.front(); stub = stubs_.Lookup(m-routing_id(); should_fast_track_ack = (m-type() = GpuCommandBufferMsg_Echo:ID) & stub & stub-IsScheduled(); while (should_fast_track_ack); if (!deferred_messages_.empty() OnScheduled(); 这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel.cc中。 GpuChannel类的成员函数HandleMessage首先检查成员变量deferred_messages_描述的一个std:deque是否为空。如果为空,那么就说明没有未处理的GPU消息,因此就直接返回。否则的话,就取出头部的GPU消息m,并且根据这个GPU消息的Routing ID找到负责接收的一个GpuCommandBufferStub对象stub。 GpuChannel类的成员函数HandleMessage将GPU消息m分发给GpuCommandBufferStub对象stub处理之前,首先检查GpuCommandBufferStub对象stub是否自行放弃了调度,以及是否被抢占调度。如果GpuCommandBufferStub对象stub自行放弃了调度,那么调用它的成员函数IsScheduled获得的返回值就为false。如果GpuCommandBufferStub对象stub被抢占了调度,那么调用它的成员函数IsPreempted获得的返回值就为true。在前一种情况下,GpuChannel类的成员函数HandleMessage什么也不做就返回。在后一种情况下,GpuChannel类的成员函数HandleMessage调用前面分析过的成员函数OnScheduled向GPU线程的消息队列的末尾发送一个调度用的Task,也就是先将GPU线程让出来,以便GPU线程执行排在GPU线程消息队列头部的Task。根据前面的分析,这时候排在GPU线程消息队列头部的Task可能就是用来调度Browser端OpenGL上下文的。 如果GpuCommandBufferStub对象stub既没有自行放弃调度,也没有被抢占调度,那么接下来GpuChannel类的成员函数HandleMessage就会对GPU消息m进行处理。如果GPU消息m的Routing ID等于MSG_ROUTING_CONTROL,那么就说明它是一个控制类型的GPU消息,这时候就将它分发给GpuChannel类的成员函数OnControlMessageReceived处理。否则的话,就调用成员变量router_描述的一个MessageRouter对象的成员函数RouteMessage将GPU消息m分发给GpuCommandBufferStub对象stub的成员函数OnMessageReceived处理。 如果GPU消息m描述的是一个认识的GPU操作,那么前面调用GpuChannel类的成员函数OnControlMessageReceived或者调用GpuChannel类的成员变量router_描述的MessageRouter对象的成员函数RouteMessage得到的返回值就为true。这时候GpuChannel类的成员函数HandleMessage会继续调用GpuCommandBufferStub对象stub的成员函数HasUnprocessedCommands判断它的GPU命令缓冲区是否还有命令未被处理。如果是的话,就向成员变量deferred_messages_描述的一个std:deque的头部插入一个类型为GpuCommandBufferMsg_Rescheduled的GPU消息,并且将本地变量message_processed的值设置为false。当上述GpuCommandBufferMsg_Rescheduled消息被处理时,GPU线程就会继续执行GpuCommandBufferStub对象stub的GPU命令缓冲区中的未处理命令。将本地变量message_processed设置为false,表示后面不要调用GpuChannel类的成员函数MessageProcessed。 另一方面,如果GPU消息m描述的是一个不认识的GPU操作,那么前面调用GpuChannel类的成员函数OnControlMessageReceived或者调用GpuChannel类的成员变量router_描述的MessageRouter对象的成员函数RouteMessage得到的返回值就为false。这时候GpuChannel类的成员函数HandleMessage就会继续检查GPU消息m是否是一个同步类型的GPU消息。如果是的话,那么就向发送该GPU消息的Client端发送一个回复消息,以便该Client端可以结束等待。如果GPU消息m描述的是一个认识的GPU操作,那么该回复消息就是由负责处理GPU消息m的函数发出的。现在既然没有函数处理GPU消息m,因此GpuChannel类的成员函数HandleMessage就要负责回复该GPU消息,以便不让发送该GPU消息的Client端一直等待下去。 GpuChannel类的成员函数HandleMessage接下来判断本地变量message_processed的值是否等于true。如果等于true,那么就代表前面完整地处理完成了一个GPU消息,这时候就调用另外一个成员函数MessageProcessed告知前面提到的成员变量filter_描述的一个GpuChannelMessageFilter对象,当前正在被调度的GPU通道处理完成了一个GPU消息,以便该GpuChannelMessageFilter对象可以更新当前正在被调度的GPU通道的状态。后面我们分析Browser端OpenGL上下文抢占GPU线程时,再分析GpuChannel类的成员函数MessageProcessed的实现。 从前面的分析可以知道,本地变量message_processed的值只有一种情况等于false,那就是GpuCommandBufferStub对象stub的GPU命令缓冲区还有未处理命令。这种情况出现在GpuCommandBufferStub对象stub接收到了一个类型为GpuCommandBufferMsg_AsyncFlush的GPU消息。从前面一文可以知道,当一个GpuCommandBufferStub对象接收到一个类型为GpuCommandBufferMsg_AsyncFlush的GPU消息时,就会调用GpuScheduler类的成员函数PutChanged处理该GpuCommandBufferStub对象的GPU命令缓冲区新提交的命令,如下所示:cpp view plain copyvoid GpuScheduler:PutChanged() . if (!IsScheduled() return; while (!parser_-IsEmpty() if (IsPreempted() break; . error = parser_-ProcessCommand(); . . 这个函数定义在文件external/chromium_org/gpu/command_bufferservice/gpu_scheduler.cc中。 然而,有可能接收到类型为GpuCommandBufferMsg_AsyncFlush的GPU消息的GpuCommandBufferStub对象自行放弃了调度,或者被抢占了调度。前者表现为调用GpuScheduler类的成员函数IsScheduled获得的返回值为false,而后者表现为调用GpuScheduler类的成员函数IsPreempted获得的返回值为true。在这两种情况下,上述GpuCommandBufferStub对象的GPU命令缓冲区新提交的命令没有全部得到处理。这相当于是说接收到的GpuCommandBufferMsg_AsyncFlush消息并没有得到完整处理。因此,GpuChannel类的成员函数HandleMessage就不会调用成员函数MessageProcessed告知前面提到的成员变量filter_描述的一个GpuChannelMessageFilter对象,当前正在被调度的GPU通道处理完成了一个GPU消息。 回到GpuChannel类的成员函数HandleMessage中,它接下来检查成员变量deferred_messages_描述的std:deque是否还有待处理GPU消息。如果有,并且下一个待处理的GPU消息是一个类型为GpuCommandBufferMsg_Echo的GPU消息,并且负责处理该GpuCommandBufferMsg_Echo消息的GpuCommandBufferStub对象没有自行放弃调度,那么就需要继续处理该GpuCommandBufferMsg_Echo消息后才返回。 当一个GPU进程的Client端,例如一个Render端,完成自已的网页UI的绘制后,就会向GPU进程发送一个GPU消息,请求对Browser端OpenGL上下文合成该网页UI,并且显示在屏幕中。紧跟在该GPU消息后面的是一个GpuCommandBufferMsg_Echo消息。一个GpuCommandBufferStub对象接收到一个GpuCommandBufferMsg_Echo消息之后,要马上对该消息进行回复。Client端接收到这个回复消息之后,就可以知道前面已经绘制完成的网页UI已经被合成显示在屏幕中了,于是就可以执行一些清理工作,例如回收资源。这些清理操作越快进行越好,因此就要求类型为GpuCommandBufferMsg_Echo的GPU消息要尽快处理。 如果GpuChannel类的成员函数HandleMessage处理完成GPU消息m后,如果还有待处理的GPU消息,并且这个待处理的GPU消息不是一个类型为GpuCommandBufferMsg_Echo的GPU消息,那么这个待处理的GPU消息将会在负责处理它的GpuCommandBufferStub对象下一次被调度时才会处理。由此可见,GpuChannel类的成员函数HandleMessage一般情况下只会处理一个GPU消息。这就是为了让其它GpuCommandBufferStub对象也有机会处理自己的GPU消息的。 前面提到,如果当前处理的GPU消息不是一个控制类型为的GPU消息,那么GpuChannel类的成员函数HandleMessage会将它分发给对应的GpuCommandBufferStub对象的成员函数OnMessageReceived处理,处理过程如下所示:cpp view plain copybool GpuCommandBufferStub:OnMessageReceived(const IPC:Message& message) . if (decoder_.get() & message.type() != GpuCommandBufferMsg_Echo:ID & message.type() != GpuCommandBufferMsg_WaitForTokenInRange:ID & message.type() != GpuCommandBufferMsg_WaitForGetOffsetInRange:ID & message.type() != GpuCommandBufferMsg_RetireSyncPoint:ID & message.type() != GpuCommandBufferMsg_SetLatencyInfo:ID) if (!MakeCurrent() return false; . bool handled = true; IPC_BEGIN_MESSAGE_MAP(GpuCommandBufferStub, message) . IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_AsyncFlush, OnAsyncFlush); . IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() . return handled; 这个函数定义在文件external/chromium_org/content/common/gpu/gpu_command_buffer_stub.cc中。 除了以下五个GPU消息,其余的GPU消息均要求调用成员GpuCommandBufferStub类的成员函数MakeCurrent将当前正在处理的GpuCommandBufferStub对象描述的OpenGL上下文设置为GPU线程当前激活的OpenGL上下文之后再处理: 1. GpuCommandBufferMsg_Echo 2. GpuCommandBufferMsg_WaitForTokenInRange 3. GpuCommandBufferMsg_WaitForGetOffsetInRange 4. GpuCommandBufferMsg_RetireSyncPoint 5. GpuCommandBufferMsg_SetLatencyInfo 第1个GPU消息的作用可以参考前面的分析。第2个GPU消息是GPU进程的Client端回收共享缓冲区时发送过来的,具体可以参考前面一文。第3个GPU消息是GPU进程的Client端等待GPU命令缓冲区的命令被处理时发送过来的,具体可以参考前面一文。第4个GPU消息与我们后面要分析的Sync Point机制相关。第5个GPU消息是GPU进程的Client端用来告诉GPU进程它的UI绘制完成后多长时间可以用来合成到浏览器窗口中显示出来。 以GpuCommandBufferMsg_AsyncFlush消息为例,GPU线程对它的处理就是执行GPU命令缓冲区新提交的GPU命令,也就是调用相应的OpenGL函数,而调用这些OpenGL函数就必须要在发送GpuCommandBufferMsg_AsyncFlush消息的Client端的OpenGL上下文中进行。因此,GpuCommandBufferStub类的成员函数OnMessageReceived就需要调用成员函数MakeCurrent将发送GpuCommandBufferMsg_AsyncFlush消息的Client端的OpenGL上下文设置为GPU线程当前激活的OpenGL上下文,即切换GPU线程的OpenGL上下文。 切换GPU线程的OpenGL上下文是OpenGL上下文调度过程的重要一环,因此接下来我们继续分析GpuCommandBufferStub类的成员函数MakeCurrent的实现,如下所示:cpp view plain copybool GpuCommandBufferStub:MakeCurrent() if (decoder_-MakeCurrent() return true; . return false; 这个函数定义在文件external/chromium_org/content/common/gpu/gpu_command_buffer_stub.cc中。 从前面一文可以知道,GpuCommandBufferStub类的成员变量decoder_描述的是一个GLES2CmdDecoderImpl对象,这里调用这个GLES2CmdDecoderImpl对象的成员函数MakeCurrent切换GPU线程的OpenGL上下文,切换过程如下所示:cpp view plain copybool GLES2DecoderImpl:MakeCurrent() . if (!context_-MakeCurrent(surface_.get() | WasContextLost() . return false; ProcessFinishedAsyncTransfers(); / Rebind the FBO if it was unbound by the context. if (workarounds().unbind_fbo_on_context_switch) RestoreFramebufferBindings(); . return true; 这个函数定义在文件external/chromium_org/gpu/command_buffer/service/gles2_cmd_decoder.cc中。 GLES2CmdDecoderImpl类的成员函数MakeCurrent主要做了以下三件事情: 1. 调用成员变量context_描述的一个gfx:GLContext对象的成员函数MakeCurrent切换GPU线程的OpenGL上下文,也就是将当前正在处理的GLES2CmdDecoderImpl对象对应的OpenGL上下文设置为GPU线程当前激活的OpenGL上下文。 2. 调用成员函数ProcessFinishedAsyncTransfers处理那些已经异步上传完成了数据的纹理,这个

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论