




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、Chromium硬件加速渲染的OpenGL上下文調(diào)度過程分析Chromium的每一個(gè)WebGL端、Render端和Browser端實(shí)例在GPU進(jìn)程中都有一個(gè)OpenGL上下文。這些OpenGL上下文運(yùn)行在相同線程中,因此同一時(shí)刻只有一個(gè)OpenGL上下文處于運(yùn)行狀態(tài)。這就引發(fā)出一個(gè)OpenGL上下文調(diào)度問題。此外,事情有輕重緩急,OpenGL上下文也有優(yōu)先級高低之分,優(yōu)先級高的要保證它的運(yùn)行時(shí)間。本文接下來就分析GPU進(jìn)程調(diào)度運(yùn)行OpenGL上下文的過程。在前面一文中提到,GPU進(jìn)程中的所有OpenGL上下文不僅運(yùn)行在相同線程中,即運(yùn)行在GPU進(jìn)程的GPU線程中,它們還處于同一個(gè)共享組中,如圖
2、1所示:在Chromium中,每一個(gè)OpenGL上下文使用一個(gè)GLContextEGL對象描述。每一個(gè)OpenGL上下文都關(guān)聯(lián)有一個(gè)繪圖表面。對于WebGL端和Render端的OpenGL上下文來說,它關(guān)聯(lián)的繪圖表面是一個(gè)離屏表面。這個(gè)離屏表面一般就是用一個(gè)Pbuffer描述。在Android平臺(tái)上,Browser端的OpenGL上下文關(guān)聯(lián)的繪圖表面是一個(gè)SurfaceView。 當(dāng)一個(gè)OpenGL上下文被調(diào)度時(shí),它以及它關(guān)聯(lián)的繪圖表面就會(huì)通過調(diào)用EGL函數(shù)eglMakeCurrent設(shè)置為GPU線程當(dāng)前使用的OpenGL上下文和繪圖表面。 從前面一文又可以知道,Chromium為WebGL端
3、、Render端和Browser端創(chuàng)建的OpenGL上下文可能是虛擬的,如圖2所示:在Chromium中,每一個(gè)虛擬OpenGL上下文都使用一個(gè)GLContextVirtual對象描述。每一個(gè)虛擬OpenGL上下文都對應(yīng)有一個(gè)真實(shí)OpenGL上下文,即一個(gè)GLContextEGL對象,并且所有的虛擬OpenGL上下文對應(yīng)的真實(shí)OpenGL上下文都是相同的。虛擬OpenGL上下文也像真實(shí)OpenGL上下文一樣,關(guān)聯(lián)有繪圖表面。對于WebGL端和Render端的虛擬OpenGL上下文來說,它關(guān)聯(lián)的繪圖表面也是一個(gè)使用Pbuffer描述的離屏表面。在Android平臺(tái)上,Browser端的OpenG
4、L上下文關(guān)聯(lián)的繪圖表面同樣也是一個(gè)SurfaceView。 當(dāng)一個(gè)虛擬OpenGL上下文被調(diào)度時(shí),它對應(yīng)的真實(shí)OpenGL上下文以及它關(guān)聯(lián)的繪圖表面就會(huì)通過調(diào)用EGL函數(shù)eglMakeCurrent設(shè)置為GPU線程當(dāng)前使用的OpenGL上下文和繪圖表面。由于所有的虛擬OpenGL上下文對應(yīng)的真實(shí)OpenGL上下文都是相同的,因此當(dāng)一個(gè)虛擬OpenGL上下文被調(diào)度時(shí),只需要通過調(diào)用EGL函數(shù)eglMakeCurrent將其關(guān)聯(lián)的繪圖表面設(shè)置為GPU線程當(dāng)前使用的繪圖表面即可。 前面提到,OpenGL上下文有優(yōu)先級高低之分,具體表現(xiàn)為Browser端的OpenGL上下文優(yōu)先級比WebGL端和Ren
5、der端的高。這是因?yàn)榍罢哓?fù)責(zé)合成后者的UI顯示在屏幕中,因此就要保證它的運(yùn)行時(shí)間。在Browser端的OpenGL上下文需要調(diào)度運(yùn)行而GPU線程又被其它OpenGL上下文占有時(shí),Browser端的OpenGL上下文就可以搶占GPU線程。為達(dá)到這一目的,Chromium給Browser端與GPU進(jìn)程建立的GPU通道設(shè)置IDLE、WAITING、CHECKING、WOULD_PREEMPT_DESCHEDULED和PREEMPTING五個(gè)狀態(tài)。這五個(gè)狀態(tài)的變遷關(guān)系如圖3所示:當(dāng)Browser端的GPU通道處于PREEMPTING狀態(tài)時(shí),Browser端的OpenGL上下文就可以要求其它OpenG
6、L上下文停止執(zhí)行手頭上的任務(wù),以便將GPU線程交出來運(yùn)行Browser端的OpenGL上下文。 Browser端的GPU通道開始時(shí)處于IDLE狀態(tài)。當(dāng)有未處理IPC消息時(shí),就從IDLE狀態(tài)進(jìn)入WAITING狀態(tài)。進(jìn)入WAITING狀態(tài)kPreemptWaitTimeMs毫秒之后,就自動(dòng)進(jìn)入CHECKING狀態(tài)。kPreemptWaitTimeMs毫秒等于2個(gè)kVsyncIntervalMs毫秒,kVsyncIntervalMs定義為17。假設(shè)屏幕的刷新速度是60fps,那么kVsyncIntervalMs毫秒剛好就是一個(gè)Vsync時(shí)間,即一次屏幕刷新時(shí)間間隔。 處于CHECKING狀態(tài)期間時(shí),
7、Browser端的GPU通道會(huì)不斷檢查最早接收到的未處理IPC消息流逝的時(shí)間是否小于2次屏幕刷新時(shí)間間隔。如果小于,那么就繼續(xù)停留在CHECKING狀態(tài)。否則的話,就進(jìn)入WOULD_PREEMPT_DESCHEDULED狀態(tài)或者PREEMPTING狀態(tài)。圖1所示的stub指的是一個(gè)GpuCommandBufferStub對象。從前面Chromium硬件加速渲染的OpenGL上下文創(chuàng)建過程分析一文可以知道,在GPU進(jìn)程中,一個(gè)GpuCommandBufferStub對象描述的就是一個(gè)OpenGL上下文。因此,圖1所示的stub指的是一個(gè)Browser端OpenGL上下文。 處于CHECKING狀
8、態(tài)期間時(shí),如果最早接收到的未處理IPC消息流逝的時(shí)間大于等于2次屏幕刷新時(shí)間間隔,并且沒有任何的Browser端OpenGL上下文自行放棄調(diào)度,那么Browser端的GPU通道就會(huì)進(jìn)入PREEMPTING狀態(tài),表示它要搶占GPU線程,也表示要求當(dāng)前正在調(diào)度的OpenGL上下文放棄占有GPU線程。另一方面,如果這時(shí)候至少有一個(gè)Browser端OpenGL上下文自行放棄調(diào)度,那么Browser端的GPU通道就會(huì)進(jìn)入WOULD_PREEMPT_DESCHEDULED狀態(tài),表示它現(xiàn)在不急于搶占GPU線程,因?yàn)檫@時(shí)候有OpenGL上下文自行放棄了調(diào)度,從而使得最早接收到的未處理消息所屬的OpenGL上下
9、文得到調(diào)度處理。 處于WOULD_PREEMPT_DESCHEDULED狀態(tài)時(shí),Browser端的GPU通道會(huì)繼續(xù)檢查是否有Browser端OpenGL上下文自行放棄調(diào)度。如果沒有,那么就進(jìn)入PREEMPTING狀態(tài),表示要搶占GPU線程。如果有,并且這時(shí)候Browser端的GPU通道接收到的IPC消息均已被處理,或者最早接收到的未處理IPC消息的流逝時(shí)間小于kStopPreemptThresholdMs毫秒,那么就進(jìn)入IDLE狀態(tài)。否則的話,就繼續(xù)維持WOULD_PREEMPT_DESCHEDULED狀態(tài)。kStopPreemptThresholdMs也定義為17,意思是Browser端的G
10、PU通道處于WOULD_PREEMPT_DESCHEDULED狀態(tài)時(shí),允許最早接收到的未處理IPC消息延遲一次屏幕刷新時(shí)間間隔再進(jìn)行處理。 Browser端的GPU通道處于PREEMPTING狀態(tài)的最長時(shí)間為kMaxPreemptTimeMs毫秒。kMaxPreemptTimeMs也定義為17,意思是Browser端的GPU通道搶占GPU線程的時(shí)間不能超過一個(gè)屏幕刷新時(shí)間間隔。如果超過了一個(gè)屏幕刷新時(shí)間間隔,那么就會(huì)進(jìn)入IDLE狀態(tài)。 在處于PREEMPTING狀態(tài)期間,如果Browser端的GPU通道接收到的IPC消息均已被處理,或者最早接收到的未處理IPC消息的流逝時(shí)間小于kStopPre
11、emptThresholdMs毫秒,那么Browser端的GPU通道也會(huì)進(jìn)入IDLE狀態(tài)。此外,在處于PREEMPTING狀態(tài)期間,如果至少有一個(gè)Browser端OpenGL上下文自行放棄調(diào)度,那么Browser端的GPU通道就會(huì)進(jìn)入WOULD_PREEMPT_DESCHEDULED狀態(tài)。 注意,在圖3所示的狀態(tài)變遷圖中,只有處于PREEMPTING狀態(tài)時(shí),Browser端的GPU通道才會(huì)強(qiáng)行搶占GPU線程。這是為了保證Browser端的OpenGL上下文,至少應(yīng)該需要在兩個(gè)屏幕刷新時(shí)間間隔之內(nèi),得到一次調(diào)度,從而保證網(wǎng)頁UI得到刷新和及時(shí)顯示。WebGL端和Render端的OpenGL上下文
12、就沒有這種待遇,畢竟它們的優(yōu)先級沒有Browser端的OpenGL上下文高。 Browser端GPU通道是以什么方式強(qiáng)行搶占GPU線程的呢?我們通過圖4說明,如下所示:Browser端GPU通道有一個(gè)Preemption Flag。當(dāng)它處于PREEMPTING狀態(tài)時(shí),就會(huì)將Preemption Flag設(shè)置為True。WebGL端和Render端GPU通道可以訪問Browser端GPU通道的Preemption Flag。屬于WebGL端和Render端GPU通道的OpenGL上下文在調(diào)度期間,會(huì)不斷地檢查Browser端GPU通道的Preemption Flag是否被設(shè)置為True。如果被設(shè)
13、置為True,那么它就會(huì)中止執(zhí)行,提前釋放GPU線程。 WebGL端和Render端GPU通道和Browser端GPU通道是父子關(guān)系。其中, WebGL端和Render端GPU通道是兒子,Browser端GPU通道是父親。Chromium規(guī)定,兒子GPU通道可以訪問父親GPU通道的Preemption Flag。有了前面這些背景知識之后,接下來我們就結(jié)合源碼分析OpenGL上下文的調(diào)度過程。 從前面一文可以知道,GPU進(jìn)程通過調(diào)用GpuChannel類的成員函數(shù)Init創(chuàng)建GPU通道,如下所示:cpp view plain copyvoid GpuChannel:Init(base:Messa
14、geLoopProxy* 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(),
15、base:MessageLoopProxy:current(); . channel_->AddFilter(filter_.get(); . 這個(gè)函數(shù)定義在文件external/chromium_org/content/common/gpu/gpu_channel.cc中。 結(jié)合前面一文可以知道,WebGL端、Render端和Browser端發(fā)送過來的GPU消息由GpuChannel類的成員函數(shù)OnMessageReceived負(fù)責(zé)接收。在接收之前,這些GPU消息首先會(huì)被GpuChannel類的成員變量filter_指向的一個(gè)GpuChannelMessageFilter對象的成員函數(shù)
16、OnMessageReceived過濾。 注意,GpuChannel類的成員函數(shù)Init是在GPU線程中執(zhí)行的,這意味著GpuChannel類的成員函數(shù)OnMessageReceived也將會(huì)在GPU線程中執(zhí)行,但是它的成員變量filter_指向的GpuChannelMessageFilter對象的成員函數(shù)OnMessageReceived不是在GPU線程執(zhí)行的,而是在負(fù)責(zé)接收IPC消息的IO線程中執(zhí)行的。 接下來我們先分析GpuChannel類的成員函數(shù)OnMessageReceived的實(shí)現(xiàn),后面分析Browser端GPU通道搶占GPU線程的過程時(shí),再分析GpuChannelMessage
17、Filter類的成員函數(shù)OnMessageReceived的實(shí)現(xiàn)。 GpuChannel類的成員函數(shù)OnMessageReceived的實(shí)現(xiàn)如下所示:cpp view plain copybool GpuChannel:OnMessageReceived(const IPC:Message& message) . if (message.type() = GpuCommandBufferMsg_WaitForTokenInRange:ID | message.type() = GpuCommandBufferMsg_WaitForGetOffsetInRange:ID) / Move
18、Wait commands to the head of the queue, so the renderer / doesn't 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; 這個(gè)函數(shù)定義在文件external/chromium_org/content/common/
19、gpu/gpu_channel.cc中。 GpuChannel類將接收到的IPC消息保存在成員變量deferred_messages_描述的一個(gè)std:deque中。其中,類型為GpuCommandBufferMsg_WaitForTokenInRange和GpuCommandBufferMsg_WaitForGetOffsetInRange的GPU消息將會(huì)保存在上述std:deque的頭部,而其它GPU消息則保存在上述std:deque的末部。 從前面和這篇文章可以知道,當(dāng)GPU進(jìn)程接收到類型為GpuCommandBufferMsg_WaitForTokenInRange和GpuComman
20、dBufferMsg_WaitForGetOffsetInRange的IPC消息時(shí),就表示它的Client端,即WebGL端、Render端和Browser端,正在檢查其GPU命令緩沖區(qū)的執(zhí)行情況,需要盡快得到結(jié)果,因此就需要將它們放在上述std:deque的頭部,以便盡快得到處理。 GpuChannel類的成員函數(shù)OnMessageReceived將接收到的GPU消息保存在成員變量deferred_messages_描述的std:deque之后,接下來調(diào)用另外一個(gè)成員函數(shù)OnScheduled對它們進(jìn)行調(diào)度處理,如下所示:cpp view plain copyvoid GpuChannel:
21、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 HandleMe
22、ssage. 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; 這個(gè)函數(shù)定義在文件external/chromium_org/content/common/gpu/gpu_channel.cc中。 GpuChannel
23、類的成員函數(shù)OnScheduled通過向GPU線程的消息隊(duì)列發(fā)送一個(gè)Task請求為當(dāng)前接收到GPU消息的GPU通道執(zhí)行一次調(diào)度。該Task綁定的函數(shù)為GpuChannel類的成員函數(shù)HandleMessage。 GpuChannel類的成員變量handle_messages_scheduled_的值等于true時(shí),表示當(dāng)前接收到GPU消息的GPU通道已經(jīng)請求過調(diào)度了,并且請求的調(diào)度還沒有被執(zhí)行。在這種情況下,就不必再向GPU線程的消息隊(duì)列發(fā)送Task。 GpuChannel類的成員函數(shù)HandleMessage的實(shí)現(xiàn)如下所示:cpp view plain copyvoid GpuChannel
24、: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->Is
25、Preempted() OnScheduled(); return; scoped_ptr<IPC:Message> 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); els
26、e 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 th
27、e 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->rou
28、te_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_.
29、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(); 這個(gè)函數(shù)定義在文件external/chromium_org/content/common/gpu/gp
30、u_channel.cc中。 GpuChannel類的成員函數(shù)HandleMessage首先檢查成員變量deferred_messages_描述的一個(gè)std:deque是否為空。如果為空,那么就說明沒有未處理的GPU消息,因此就直接返回。否則的話,就取出頭部的GPU消息m,并且根據(jù)這個(gè)GPU消息的Routing ID找到負(fù)責(zé)接收的一個(gè)GpuCommandBufferStub對象stub。 GpuChannel類的成員函數(shù)HandleMessage將GPU消息m分發(fā)給GpuCommandBufferStub對象stub處理之前,首先檢查GpuCommandBufferStub對象stub是否自行
31、放棄了調(diào)度,以及是否被搶占調(diào)度。如果GpuCommandBufferStub對象stub自行放棄了調(diào)度,那么調(diào)用它的成員函數(shù)IsScheduled獲得的返回值就為false。如果GpuCommandBufferStub對象stub被搶占了調(diào)度,那么調(diào)用它的成員函數(shù)IsPreempted獲得的返回值就為true。在前一種情況下,GpuChannel類的成員函數(shù)HandleMessage什么也不做就返回。在后一種情況下,GpuChannel類的成員函數(shù)HandleMessage調(diào)用前面分析過的成員函數(shù)OnScheduled向GPU線程的消息隊(duì)列的末尾發(fā)送一個(gè)調(diào)度用的Task,也就是先將GPU線程讓
32、出來,以便GPU線程執(zhí)行排在GPU線程消息隊(duì)列頭部的Task。根據(jù)前面的分析,這時(shí)候排在GPU線程消息隊(duì)列頭部的Task可能就是用來調(diào)度Browser端OpenGL上下文的。 如果GpuCommandBufferStub對象stub既沒有自行放棄調(diào)度,也沒有被搶占調(diào)度,那么接下來GpuChannel類的成員函數(shù)HandleMessage就會(huì)對GPU消息m進(jìn)行處理。如果GPU消息m的Routing ID等于MSG_ROUTING_CONTROL,那么就說明它是一個(gè)控制類型的GPU消息,這時(shí)候就將它分發(fā)給GpuChannel類的成員函數(shù)OnControlMessageReceived處理。否則的話
33、,就調(diào)用成員變量router_描述的一個(gè)MessageRouter對象的成員函數(shù)RouteMessage將GPU消息m分發(fā)給GpuCommandBufferStub對象stub的成員函數(shù)OnMessageReceived處理。 如果GPU消息m描述的是一個(gè)認(rèn)識的GPU操作,那么前面調(diào)用GpuChannel類的成員函數(shù)OnControlMessageReceived或者調(diào)用GpuChannel類的成員變量router_描述的MessageRouter對象的成員函數(shù)RouteMessage得到的返回值就為true。這時(shí)候GpuChannel類的成員函數(shù)HandleMessage會(huì)繼續(xù)調(diào)用GpuCo
34、mmandBufferStub對象stub的成員函數(shù)HasUnprocessedCommands判斷它的GPU命令緩沖區(qū)是否還有命令未被處理。如果是的話,就向成員變量deferred_messages_描述的一個(gè)std:deque的頭部插入一個(gè)類型為GpuCommandBufferMsg_Rescheduled的GPU消息,并且將本地變量message_processed的值設(shè)置為false。當(dāng)上述GpuCommandBufferMsg_Rescheduled消息被處理時(shí),GPU線程就會(huì)繼續(xù)執(zhí)行GpuCommandBufferStub對象stub的GPU命令緩沖區(qū)中的未處理命令。將本地變量me
35、ssage_processed設(shè)置為false,表示后面不要調(diào)用GpuChannel類的成員函數(shù)MessageProcessed。 另一方面,如果GPU消息m描述的是一個(gè)不認(rèn)識的GPU操作,那么前面調(diào)用GpuChannel類的成員函數(shù)OnControlMessageReceived或者調(diào)用GpuChannel類的成員變量router_描述的MessageRouter對象的成員函數(shù)RouteMessage得到的返回值就為false。這時(shí)候GpuChannel類的成員函數(shù)HandleMessage就會(huì)繼續(xù)檢查GPU消息m是否是一個(gè)同步類型的GPU消息。如果是的話,那么就向發(fā)送該GPU消息的Clie
36、nt端發(fā)送一個(gè)回復(fù)消息,以便該Client端可以結(jié)束等待。如果GPU消息m描述的是一個(gè)認(rèn)識的GPU操作,那么該回復(fù)消息就是由負(fù)責(zé)處理GPU消息m的函數(shù)發(fā)出的?,F(xiàn)在既然沒有函數(shù)處理GPU消息m,因此GpuChannel類的成員函數(shù)HandleMessage就要負(fù)責(zé)回復(fù)該GPU消息,以便不讓發(fā)送該GPU消息的Client端一直等待下去。 GpuChannel類的成員函數(shù)HandleMessage接下來判斷本地變量message_processed的值是否等于true。如果等于true,那么就代表前面完整地處理完成了一個(gè)GPU消息,這時(shí)候就調(diào)用另外一個(gè)成員函數(shù)MessageProcessed告知前面
37、提到的成員變量filter_描述的一個(gè)GpuChannelMessageFilter對象,當(dāng)前正在被調(diào)度的GPU通道處理完成了一個(gè)GPU消息,以便該GpuChannelMessageFilter對象可以更新當(dāng)前正在被調(diào)度的GPU通道的狀態(tài)。后面我們分析Browser端OpenGL上下文搶占GPU線程時(shí),再分析GpuChannel類的成員函數(shù)MessageProcessed的實(shí)現(xiàn)。 從前面的分析可以知道,本地變量message_processed的值只有一種情況等于false,那就是GpuCommandBufferStub對象stub的GPU命令緩沖區(qū)還有未處理命令。這種情況出現(xiàn)在GpuComm
38、andBufferStub對象stub接收到了一個(gè)類型為GpuCommandBufferMsg_AsyncFlush的GPU消息。從前面一文可以知道,當(dāng)一個(gè)GpuCommandBufferStub對象接收到一個(gè)類型為GpuCommandBufferMsg_AsyncFlush的GPU消息時(shí),就會(huì)調(diào)用GpuScheduler類的成員函數(shù)PutChanged處理該GpuCommandBufferStub對象的GPU命令緩沖區(qū)新提交的命令,如下所示:cpp view plain copyvoid GpuScheduler:PutChanged() . if (!IsScheduled() retur
39、n; while (!parser_->IsEmpty() if (IsPreempted() break; . error = parser_->ProcessCommand(); . . 這個(gè)函數(shù)定義在文件external/chromium_org/gpu/command_bufferservice/gpu_scheduler.cc中。 然而,有可能接收到類型為GpuCommandBufferMsg_AsyncFlush的GPU消息的GpuCommandBufferStub對象自行放棄了調(diào)度,或者被搶占了調(diào)度。前者表現(xiàn)為調(diào)用GpuScheduler類的成員函數(shù)IsSchedul
40、ed獲得的返回值為false,而后者表現(xiàn)為調(diào)用GpuScheduler類的成員函數(shù)IsPreempted獲得的返回值為true。在這兩種情況下,上述GpuCommandBufferStub對象的GPU命令緩沖區(qū)新提交的命令沒有全部得到處理。這相當(dāng)于是說接收到的GpuCommandBufferMsg_AsyncFlush消息并沒有得到完整處理。因此,GpuChannel類的成員函數(shù)HandleMessage就不會(huì)調(diào)用成員函數(shù)MessageProcessed告知前面提到的成員變量filter_描述的一個(gè)GpuChannelMessageFilter對象,當(dāng)前正在被調(diào)度的GPU通道處理完成了一個(gè)GP
41、U消息。 回到GpuChannel類的成員函數(shù)HandleMessage中,它接下來檢查成員變量deferred_messages_描述的std:deque是否還有待處理GPU消息。如果有,并且下一個(gè)待處理的GPU消息是一個(gè)類型為GpuCommandBufferMsg_Echo的GPU消息,并且負(fù)責(zé)處理該GpuCommandBufferMsg_Echo消息的GpuCommandBufferStub對象沒有自行放棄調(diào)度,那么就需要繼續(xù)處理該GpuCommandBufferMsg_Echo消息后才返回。 當(dāng)一個(gè)GPU進(jìn)程的Client端,例如一個(gè)Render端,完成自已的網(wǎng)頁UI的繪制后,就會(huì)向G
42、PU進(jìn)程發(fā)送一個(gè)GPU消息,請求對Browser端OpenGL上下文合成該網(wǎng)頁UI,并且顯示在屏幕中。緊跟在該GPU消息后面的是一個(gè)GpuCommandBufferMsg_Echo消息。一個(gè)GpuCommandBufferStub對象接收到一個(gè)GpuCommandBufferMsg_Echo消息之后,要馬上對該消息進(jìn)行回復(fù)。Client端接收到這個(gè)回復(fù)消息之后,就可以知道前面已經(jīng)繪制完成的網(wǎng)頁UI已經(jīng)被合成顯示在屏幕中了,于是就可以執(zhí)行一些清理工作,例如回收資源。這些清理操作越快進(jìn)行越好,因此就要求類型為GpuCommandBufferMsg_Echo的GPU消息要盡快處理。 如果GpuCha
43、nnel類的成員函數(shù)HandleMessage處理完成GPU消息m后,如果還有待處理的GPU消息,并且這個(gè)待處理的GPU消息不是一個(gè)類型為GpuCommandBufferMsg_Echo的GPU消息,那么這個(gè)待處理的GPU消息將會(huì)在負(fù)責(zé)處理它的GpuCommandBufferStub對象下一次被調(diào)度時(shí)才會(huì)處理。由此可見,GpuChannel類的成員函數(shù)HandleMessage一般情況下只會(huì)處理一個(gè)GPU消息。這就是為了讓其它GpuCommandBufferStub對象也有機(jī)會(huì)處理自己的GPU消息的。 前面提到,如果當(dāng)前處理的GPU消息不是一個(gè)控制類型為的GPU消息,那么GpuChannel類
44、的成員函數(shù)HandleMessage會(huì)將它分發(fā)給對應(yīng)的GpuCommandBufferStub對象的成員函數(shù)OnMessageReceived處理,處理過程如下所示:cpp view plain copybool GpuCommandBufferStub:OnMessageReceived(const IPC:Message& message) . if (decoder_.get() && message.type() != GpuCommandBufferMsg_Echo:ID && message.type() != GpuCommandBuffe
45、rMsg_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_
46、BEGIN_MESSAGE_MAP(GpuCommandBufferStub, message) . IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_AsyncFlush, OnAsyncFlush); . IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() . return handled; 這個(gè)函數(shù)定義在文件external/chromium_org/content/common/gpu/gpu_command_buffer_stub.cc中。 除了以下五個(gè)GPU消息,其余的GPU消息均要
47、求調(diào)用成員GpuCommandBufferStub類的成員函數(shù)MakeCurrent將當(dāng)前正在處理的GpuCommandBufferStub對象描述的OpenGL上下文設(shè)置為GPU線程當(dāng)前激活的OpenGL上下文之后再處理: 1. GpuCommandBufferMsg_Echo 2. GpuCommandBufferMsg_WaitForTokenInRange 3. GpuCommandBufferMsg_WaitForGetOffsetInRange 4. GpuCommandBufferMsg_RetireSyncPoint 5. GpuCommandBufferMsg_SetLate
48、ncyInfo 第1個(gè)GPU消息的作用可以參考前面的分析。第2個(gè)GPU消息是GPU進(jìn)程的Client端回收共享緩沖區(qū)時(shí)發(fā)送過來的,具體可以參考前面一文。第3個(gè)GPU消息是GPU進(jìn)程的Client端等待GPU命令緩沖區(qū)的命令被處理時(shí)發(fā)送過來的,具體可以參考前面一文。第4個(gè)GPU消息與我們后面要分析的Sync Point機(jī)制相關(guān)。第5個(gè)GPU消息是GPU進(jìn)程的Client端用來告訴GPU進(jìn)程它的UI繪制完成后多長時(shí)間可以用來合成到瀏覽器窗口中顯示出來。 以GpuCommandBufferMsg_AsyncFlush消息為例,GPU線程對它的處理就是執(zhí)行GPU命令緩沖區(qū)新提交的GPU命令,也就是調(diào)用
49、相應(yīng)的OpenGL函數(shù),而調(diào)用這些OpenGL函數(shù)就必須要在發(fā)送GpuCommandBufferMsg_AsyncFlush消息的Client端的OpenGL上下文中進(jìn)行。因此,GpuCommandBufferStub類的成員函數(shù)OnMessageReceived就需要調(diào)用成員函數(shù)MakeCurrent將發(fā)送GpuCommandBufferMsg_AsyncFlush消息的Client端的OpenGL上下文設(shè)置為GPU線程當(dāng)前激活的OpenGL上下文,即切換GPU線程的OpenGL上下文。 切換GPU線程的OpenGL上下文是OpenGL上下文調(diào)度過程的重要一環(huán),因此接下來我們繼續(xù)分析GpuC
50、ommandBufferStub類的成員函數(shù)MakeCurrent的實(shí)現(xiàn),如下所示:cpp view plain copybool GpuCommandBufferStub:MakeCurrent() if (decoder_->MakeCurrent() return true; . return false; 這個(gè)函數(shù)定義在文件external/chromium_org/content/common/gpu/gpu_command_buffer_stub.cc中。 從前面一文可以知道,GpuCommandBufferStub類的成員變量decoder_描述的是一個(gè)GLES2CmdDe
51、coderImpl對象,這里調(diào)用這個(gè)GLES2CmdDecoderImpl對象的成員函數(shù)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 contex
52、t. if (workarounds().unbind_fbo_on_context_switch) RestoreFramebufferBindings(); . return true; 這個(gè)函數(shù)定義在文件external/chromium_org/gpu/command_buffer/service/gles2_cmd_decoder.cc中。 GLES2CmdDecoderImpl類的成員函數(shù)MakeCurrent主要做了以下三件事情: 1. 調(diào)用成員變量context_描述的一個(gè)gfx:GLContext對象的成員函數(shù)MakeCurrent切換GPU線程的OpenGL上下文,也就是將當(dāng)前正在處理的GLES2CmdDecoderImpl對象對應(yīng)的OpenGL上下文設(shè)置為GPU線程當(dāng)前激活的OpenGL上下文。 2. 調(diào)用成員函數(shù)ProcessFinishedAsyncTransfers處理那些已經(jīng)異步上傳完成了數(shù)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 家庭電工實(shí)戰(zhàn)施工方案
- 槽鋼施工方案
- TSHAEPI 012-2024 低碳實(shí)踐區(qū)近零碳排放實(shí)踐區(qū)建設(shè)和評價(jià)指南
- 幼兒園環(huán)境創(chuàng)設(shè)家長參與2025年度合作協(xié)議
- 二零二五年度劇院包場合同-電影院租賃年度文化合作協(xié)議
- 2025年度跨境電商平臺(tái)國際人才招聘與派遣合同
- 二零二五年度茶山租賃及茶葉種植與農(nóng)業(yè)觀光旅游開發(fā)合同
- 二零二五年度商業(yè)街房地產(chǎn)招商代理執(zhí)行協(xié)議
- 2025年度金融科技股權(quán)分紅與風(fēng)險(xiǎn)防范協(xié)議
- 二零二五年度健身房浴室共享租賃合同范本
- DB32T 4400-2022《飲用水次氯酸鈉消毒技術(shù)規(guī)程》
- 古詩惠崇春江晚景課件市公開課一等獎(jiǎng)省賽課微課金獎(jiǎng)?wù)n件
- 化學(xué)品(氬氣+二氧化碳混合氣)安全技術(shù)使用說明書
- 煤層氣開發(fā)-第2章-煤層氣地質(zhì)
- 黑龍江商業(yè)職業(yè)學(xué)院單招《職業(yè)技能測試》參考試題庫(含答案)
- 美羅華(利妥昔單抗)課件
- 稅務(wù)簡易注銷課件
- 人教版五年級數(shù)學(xué)下冊第六單元分層作業(yè)設(shè)計(jì)
- 肺葉切除術(shù)和全肺切除術(shù)的麻醉課件
- 智能制造在食品加工業(yè)的應(yīng)用
- 珍愛生命遠(yuǎn)離毒品禁毒教育宣傳
評論
0/150
提交評論