原文地址 http://www.cnblogs.com/mazhenyu/archive/2010/04/29/1724190.html
關於這個問題以前只知道多個線程不能同時使用一個RC,結果為了能動態加載,當初還做了一個通過拆分主線程的工作來模擬多線程加載的偽多線程程序,今天突然很想把這個問題徹底搞明白,結果從百度到google.com最后才在終於找到這么一篇講解詳細的英文文章,可憐我4級都沒過的英語啊...
這是英文原文地址:http://veelck.wordpress.com/2008/11/28/multithread-texture-loading-in-opengl/
老外寫東西一般廢話會比較多,為了節省技術人員的寶貴時間就不逐句翻譯了,提取一下要點吧,如下:
首先使用同一個DC創建兩個RC,並且使用wglShareLists共享兩個RC的資源。建議不要給這三個函數之間加其他向RC中添加東西的GL函數。
HGLRC hRC1 = wglCreateContext(hDC);
HGLRC hRC2 = wglCreateContext(hDC);
wglShareLists(hRC2, hRC1);
注意:wglShareLists的第一個參數中的RC是分享別人資源的,第二個參數中的RC是奉獻資源供別人分享的。
//這是一個用來渲染場景的線程(也可用主線程來渲染)
renderingThread()
{
//...
wglMakeCurrent(hDC, hRC1);
//...
}
理論上來講,可以在多創建幾個RC,然后用多線程同時渲染。但是據說這樣做是可行但卻無益的。因為OpenGL會進行一些頻繁的切換,導致產生高昂的開銷。
//這是一個用來加載資源的線程,可以加載圖片,並使用創建紋理,設置紋理參數等。
loadingThread()
{
//...
wglMakeCurrent(hDC, hRC2);
//...
}
注意,加載工作也可以寫在多個線程中,例如使用多個線程從硬盤中讀取圖片文件(在多核機器上用雙核讀圖片會比單核快一些),然后使用一個專門的線程調用GL的函數來創建紋理。但是,如果使用一些開源的圖像庫來讀取圖片的話就要注意了,有些開源的圖像庫不支持多線程,如DevIL。(FreeImage記得也存在這個問題)
最后別忘了在各個線程結束時調用wglMakeCurrent(NULL, NULL);取消DC與RC的關聯。
還有要刪除RC
wglDeleteContext(hRC2);
wglDeleteContext(hRC1);
最后要發一下牢騷。網上三維圖形方面的資料相比其他計算機技術而言實在是不多,而且大多數還沒有用。國內更是少的可憐,可能高人們都在忙着為買房子而奮斗導致沒時間上網了。以下是該英文文章作者的感言,深有同感啊,向這種有國際主義精神的高人致敬。
It was quite a struggle to find anything useful in the web, only some minor things at few forums and mailing groups. But using that with some thinking and reading between lines I finally found the solution I’d like to share, as it can save (in my opinion) much time of searching and experimenting for others.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
我的測試:
測試程序由nehe的lesson7項目修改完成
#include<process.h>
HDC hDC=NULL; // Private GDI Device Context
HGLRC hRC=NULL; // Permanent Rendering Context
HGLRC hRCShareing=NULL;// 用於分享hRC的資源
if (!(hRC=wglCreateContext(hDC))) // Are We Able To Get A Rendering Context?
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
hRCShareing = wglCreateContext(hDC);
wglShareLists(hRCShareing, hRC);
// 啟動加載線程
_beginthread(TextureLoadingThread, NULL, NULL); //開啟加載紋理線程
void TextureLoadingThread(void* p)
{
//...
wglMakeCurrent(hDC, hRCShareing);
if (!LoadGLTextures()) // Jump To Texture Loading Routine
{
return; // If Texture Didn't Load Return FALSE
}
//...
wglMakeCurrent(NULL, NULL);
// 結束線程
_endthread();
}
測試結果:
通過OK,紋理顯示正常
如果注釋掉下面共享hrc的代碼,如下所示
//wglShareLists(hRCShareing, hRC);
則紋理無法顯示,看到的是個白塊。
GLFW文檔對Current context的注釋:
Before you can make OpenGL or OpenGL ES calls, you need to have a current context of the correct type. A context can only be current for a single thread at a time, and a thread can only have a single context current at a time.