原文地址 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.