天天看點

Chromium Graphics: 3D上下文及其虛拟化(Virtualization) - Part II

摘要:Part I介紹了OpenGL上下文和繪制表面等基本概念,以及Chromium為什麼需要多個3D上下文。本文将繼續這個話題,探讨Chromium為什麼需要引入虛拟3D上下文,以及虛拟上下文之間切換有何不同。

多個3D上下文的限制

Chromium需要使用多個3D上下文,并且這些上下文能夠共享資源。然而,目前圖形驅動對多個3D上下文的支援存在不同程度的問題,尤其是在移動裝置上,例如(參見http://crbug.com/155557 ):

  • 有些驅動一旦使用多個上下文就會暴露這樣或那樣的問題;
  • 有些驅動在共享資源的多個上下文之間切換時很慢;
  • 有些驅動限制了多個上下文數量;
  • 有些驅動在資源共享方面存在問題,例如在一個上下文中更新了一個資源,但另一個上下文卻得不到這個更新。

虛拟的3D上下文

對存在上述問題的GPU裝置,Chromium引入了虛拟上下文的概念,其主要思路是盡可能避免建立真實的3D上下文,通過虛拟化3D上下文使得多個上下文可共享一個真實的上下文,而虛拟上下文的切換不一定不會導緻實際上下文的切換,如此不僅減少了真實的上下文數量,而且有效地避免了上下文切換的性能開銷等問題。

Chromium隻會對存在問題的GPU裝置采用建立虛拟上下文的方法,也就說,對于多上下文支援良好的GPU裝置,是采用虛拟上下文的。檔案gpu/config/gpu_driver_bug_list_json.cc列舉所有可能有問題的GPU驅動,包括上述多上下文支援的問題,Chromium運作時讀取這個清單并與目前系統的GPU驅動進行比對,如果比對成功并且包含特性use_virtualized_gl_contexts時,那麼Chromium則會使用虛拟化的上下文來規避GPU驅動可能存在的問題。

GPU線程收到來自用戶端(可能是Browser程序,也可能是Renderer程序)的上下文建立請求後,會請求GPU驅動建立上下文執行個體:

  1. 如果GPU驅動能夠支援多上下文時,GPU線程會為每個請求都建立一個真實的上下文;
  2. 如果GPU驅動不能支援多上下文時, GPU線程則會為每個請求建立一個虛拟的上下文;

對于情況1,GPU線程直接建立平台相關的上下文。需要特别說明的是,GPU線程中所有建立的上下文都屬于同一個共享組(sharegroup)。同一共享組中所有上下文之間資源是可以共享的,共享意味着它們使用的是同一個名字空間,任何一個上下文建立的Texture都可以被其他上下文所使用。可共享的資源包括Texture,Buffer對象和GLSL對象等。是以,Browser端的Compositor可直接通路WebGL上下文中的Texture,用來最終的螢幕合成。

對于情況2,GPU線程建立的是虛拟上下文,并且所有的虛拟上下文都将共享同一個真實的上下文。

不論是真實上下文,還是虛拟上下文,必須與一個與其相容的渲染表面(Drawing Surface)才能被切換成為“目前”上下文,啟動OpenGL的渲染流水線。

渲染表面和上下文的切換

GLX和EGL規範中都明确定義了,要讓GLXContext或EGLContext成為“目前”的上下文還需要指定渲染表面。Chromium定義了兩類渲染表面:

  • 屏上(onscreen)渲染表面:每個onscreen的渲染表面都是視窗系統的一個視窗,根據視窗ID或者句柄建立而來,具有前後雙緩沖區,通過交換緩沖區将渲染結果呈現在螢幕上的視窗中。Chromium隻會為Browser程序端的Compositor建立這個渲染表面作為合成表面(compositingsurface);
  • 離屏(offscreen)渲染表面:隻有後緩沖區,内容在螢幕不可見。以WebGL為例,Chromium通過Framebuffer對象将WebGL渲染到一個Texture,而WebGL上下文所使用的渲染表面正是一個建構在PBuffer上的offscreen渲染表面。

有了渲染表面,便可以将一個上下文切換成為“目前”上下文。真實上下文之間的切換,實際是OpenGL狀态機之間的切換,而虛拟上下文的切換,Chromium是不會觸發真實上下文的切換,盡管渲染表面是從onscreen類型切換到offscreen類型的。下圖展示的就是調用makeCurrent切換真實上下文的情形。

Chromium Graphics: 3D上下文及其虛拟化(Virtualization) - Part II

前面提到,GPU線程收到上下文建立請求後,建立虛拟上下文之前,會首先嘗試從共享組(share group)擷取一個真實上下文,如果沒有,則需要建立這樣一個上下文并添加到共享組中,再建立一個建構在這個真實上下文之上的虛拟上下文,然後以渲染表面初始化這個虛拟上下文,這裡強調一下,上下文需要一個與其相容的渲染表面才能成為能夠執行GL指令的“目前”上下文。

  • 如果GPU線程收來自Browser程序端的請求,要為Compositor建立虛拟上下文,那麼這個渲染表面是一個onscreen渲染表面(從視窗ID建立而來);
  • 如果GPU線程收到來自Render程序端的請求,要為WebGL建立虛拟上下文,那麼這個渲染表面是一個offscreen渲染表面(Pbuffer)。

建立上下文之後,GPU線程将交替執行不同上下文中的GL指令,這就意味着GPU線程能夠在虛拟上下文之間進行切換。那麼虛拟上下文的切換有什麼不同呢?

第一,因為隻有真實上下文才會擁有一個OpenGL狀态機,是以在使用虛拟上下文的情況下,GPU線程隻有一個OpenGL狀态機,而這個狀态機被不同目的的虛拟上下文(如Compositor,WebGL等)所複用。為了確定來回切換時能夠恢複上一次的OpenGL狀态,Chromium為每個虛拟上下文都提供了一個狀态恢複器用以OpenGL狀态的恢複。OpenGL的狀态包括,

  • Framebuffer對象的綁定
  • 所有Texture單元的綁定
  • 頂點屬性
  • Buffer對象和RenderBuffer的綁定
  • OpenGL全局狀态

第二,虛拟上下文的切換隻是邏輯上的上下文切換,并不會導緻真實上下文的切換(因為隻有一個真實上下文),盡管Compositor和WebGL上下文使用的是不同的渲染表面,但仍然不會因渲染表面的不同而導緻上下文的切換。具體的來說,假如目前GPU線程已經工作在Compositor的虛拟上下文中,使用的是onscreen渲染表面,此時GPU線程需要切換到WebGL上下文去執行GL指令,而WebGL虛拟上下文時使用的offscreen渲染表面(Pbuffer),即便是兩者具有不同類型的渲染表面,也不會導緻真實上下文的切換。因為對于WebGL的離屏渲染方式來說,它是通過Framebuffer對象将内容渲染到一個Texture上,是以指定的渲染表面既可以是onscreen,也可以是offscreen的,隻是對于onscreen的情況來說,使用的僅僅是它的後緩沖區,最終的内容還是在Texture中,是以不用關心用的是哪類渲染表面。下圖描述的就是虛拟上下文的情形:

Chromium Graphics: 3D上下文及其虛拟化(Virtualization) - Part II

那麼,在哪些情況下虛拟上下文的切換才會導緻真實上下文的切換呢?

  •  第一次切換到Browser程序端的Compositor虛拟上下文時,此時整個GPU線程還沒有綁定一個有效的“目前”上下文,需要執行真實上下文的切換,保證GPU線程有個共享的OpenGL狀态機;
  • 當兩個不在同一共享組的虛拟上下文切換時,表面這兩個虛拟上下文具有對應的真實上下文。
  • 當兩個虛拟上下文同時需要在兩個不同的onscreen表面切換時,不論是否在同一個共享組中。

目前Chromium的實作中,實際上隻有情況1才會發生,而情況2和3隻是從代碼邏輯中的推理而得出來的。

未完待續...下節将從Chromium源碼解讀上下文以及虛拟化是如何實作的。

繼續閱讀