說明:在本節涉及的frame buffer是以顯示爲目的的frame buffer。
爲了將GPU繪製結果在窗口中顯示出來,必然需要將OpenGL和具體窗口系統相結合。如下圖所示,OpenGL spec定義了GL context< /span>的行爲,從具體窗口衍生出frame buffer,兩者結合,即可完成OpenGL應用程序的顯示輸出。
當然,GL context的輸出格式和Frame buffer的格式要一致才行,否則無法將GL context和frame buffer連接起來。比如,假如GL context將輸出depth值,則frame buffer中應該包括depth buffer;假如GL context輸出的顏色中的紅色分量佔8比特,則frame buffer中相應的color buffer的red分量也應該是8比特;假如GL context要求輸出到back buffer中,則frame buffer就應該既包括front buffer,也包括back buffer。
但是OpenGL spec並沒有定義如何和具體窗口系統相結合的問題,所以,需要引入新的spec來支持。具體到Windows操作系統,就是WGL;而在Linux/X下,就是GLX。
考察一個OpenGL應用程序在Windows平臺的剛開始運行時和WGL相關的流程
1. 調用函數CreateWindowEx創建窗口hWnd
2. 調用函數GetDC得到hDC,其函數參數是hWnd
3. 調用函數ChoosePixelFormat來查詢hDC支持的所有像素格式,應用程序同時選擇出其將採用哪種像素格式
4. 調用函數SetPixelFormat設置hDC的像素格式
5. 調用函數wglCreateContext創建hGLRC,其參數是hDC
6. 調用函數wglMakeCurrent,其參數是hDC和hGLRC
7. 調用glAPI函數繪製
粗略的,我們可以這樣理解,第4步完成後將根據hDC衍生出frame buffer;第5步得到的hGLRC本質上是上圖中的GL context;而第6步則將應用程序和GL context、GL context和frame buffer連接在一起。從第7步開始,所有的glAPI都將作用於和應用程序連接的GL context;而GL context的輸出目的地則是和其連接的frame buffer。
其中,第5步的hDC和第6步的hDC並不需要是同一個值,只需要這兩個hDC都設置了相同的像素格式即可,這樣就可以保證GL context的輸出格式和Frame buffer的格式的一致性。
考察一個OpenGL應用程序在Linux/X下的剛開始運行時和GLX1.4相關的流程
1. 調用函數XOpenDisplay(NULL)得到display,可以將其認爲是X server對顯卡的一個抽象
2. 查詢display,確定其連接的screen
3. 調用函數glxChooseFBConfig查詢將在其上繪製的screen所支持的fbconfigs(本質上和windows中的像素格式是相同的),應用程序同時選擇出其將採用哪種fbconfig
4. 調用函數XCreateWindow創建窗口xWin,其參數包括fbconfig的部分內容
5. 調用函數glXCreateWindow創建glxWin,參數是xWin和fbconfig
6. 調用函數glXCreateNewContext創建glxCtx,參數是fbconfig
7. 調用函數glxMakeContextCurrent,其參數包括glxWin和glxCtx。
8. 調用glAPI函數繪製
粗略的,我們可以這樣理解,第5步得到的glxWin將衍生出frame buffer;第6步得到的glxCtx本質上就是上圖中的GL context;而第7步則是將應用程序和GL context、GL context和frame buffer連接在一起。從第8步開始,所有的glAPI都將作用於和應用程序連接的GL context;而GL context的輸出目的地則是和其連接的frame buffer。
其中,第7步調用函數的參數glxWin和glxCtx要求他們在被創建時所傳入的fbconfig參數是相同的,這樣就可以保證GL context的輸出格式和Frame buffer的格式的一致性。
至此,應該可以理解到hGLRC/glxCtx和GL context的關係,它們在本質上是同一回事。hGLRC是在WGL下對GL context的封裝,glxCtx是在GLX下對GL context的封裝。hGLRC和glxCtx是相對應用程序的概念,而GL context則是OpenGL內部的概念。
不管是Windows系統還是Linux/X,一個應用程序可能會多線程的在多個窗口上用OpenGL繪製。通過討論這種複雜情況,來加深對wglMakeCurrent/glxMakeContextCurrent函數(以下將簡稱爲MakeCurrent)的理解。
如下圖所示,某多線程程序,且已創建多個GL contex和frame buffer。圖示狀態表示,線程甲已成功調用函數MakeCurrent(GL context 3, Frame buffer B),線程乙已成功調用函數MakeCurrent(GL context 1, Frame buffer A),線程丁已成功調用函數MakeCurrent(GL context 4, Frame buffer C)。
接下去,在線程甲中被執行的glAPI都將作用於GL context3,其繪製結果進入Frame buffer B;在線程乙和線程丁中調用的glAPI也將分別作用於GL context 1和GL context4,其繪製結果分別進入Frame buffer A和Frame buffer C。
假如某個線程不調用OpenGL繪製,那它可以不和任何GL context連接。每個線程同時只能最多和一個GL context連接,因爲,假如超過一個的話,該線程接下去的glAPI調用就將無法確定應該作用於哪個GL context連接了。當某個線程調用MakeCurrent函數重新設置和GL context、Frame buffer的連接關係時,原有的連接關係失效。
每個GL context最多可以和一個線程連接,因爲,假如超過一個的話,多個線程作用於同一個GL context,其繪製結果顯然不是所希望的。每個GL contxt只和一個Frame buffer連接,顯然是無法和多個Frame buffer連接的。
每個Frame buffer只和一個GL context連接,似乎spec並沒有禁止一個Frame buffer和多個GL context連接,但是,強烈不建議這樣做,很有可能驅動程序沒有針對這種情況編寫代碼的。
在默認情況下,不同GL context之間是相互獨立的。如果採用share context技術,可以使不同GL context之間共享部分信息,對一個GL context的改動,就會在在另一個GL context中體現出來。
在驅動程序中,爲節約顯存,frame buffer往往會被延遲到函數MakeCurrent被調用時才被真正創建。