openGL之多线程渲染
随着Vulkan的引入,我们的图形技术的发展到达了一个新的顶点,但是呢,我们的老干爹OpenGL作为落日余晖,他在一些Vulkan才有的新功能上,也提供了一些支持,现在我们来讨论一下OpenGL之多线程渲染。
这里要补一补课:
windows上调用openGL最原始的原始方式
大概流程是这样的详情请见:https://gitee.com/GProReat/codes/bjptwd3hglozmi25esn4v31
1.获取句柄, 在win32或者MFC里面可以直接获取HWND,但是在glut,glfw,sdl等可以通过GetActiveWindow来获取HWND。
2.GetDC,这个是获取窗口的DC, HDC ( Handle To Device Context)是图像的设备描述表,窗口显示上下文句柄,其中可以进行图形显示。
3.PixelFormat的支持,这里可以看刚才代码段里面PixelFormat的设置
4.创建OpenGL的上下文wglCreateContext, 以及切换到当前的上下文,wglMakeCurrent,传入HGLRC类型的object。
5.初始化操作 各种开关比如: depthTest,AlphaTest,DepthFunc,Texture2等操作。
6.在窗口的绘制的时候,也就是WM_PAINT相应的时候处理绘制。
这里关键一点是,调用OpenGL绘制之后使用SwapBuffers,还有glFlush。
7.资源释放。
note:windows上使用openGL来渲染,大家可以找MFC或者win32上的OpenGL绘制。
接下来开始我们的主题:openGL多线程渲染:
根据我的测试,HGLRC 的创建与线程无关,也就是说目前HGLRC可以在另外一个线程创建(需要在当前线程wglShareLists),也可以在当前线程创建,
下面为核心代码:
this->hGLRC2 = wglCreateContext(hDC); wglShareLists(hGLRC2, hGLRC); std::thread th([this]() { wglMakeCurrent(hDC, hGLRC2); texAnotherThread.LoadFromFile1("1.jpg"); }); th.join();
当然我们根据测试,也能发现,HGLRC在shared之前或者之后加载资源,都能做到共享
hGLRC = wglGetCurrentContext(); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); std::thread th([this]() { hGLRC2 = wglCreateContext(hDC); wglMakeCurrent(hDC, hGLRC2); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); texAnotherThread.LoadFromFile1("1.jpg"); } th.join(); wglShareLists(hGLRC, hGLRC2);
还有根据我的测试wglShareLists(hGLRC, hGLRC2); 他实际上这两个参数顺序是有讲究的,如果在创建完上下文之后,直接共享,那么这俩参数顺序没有影响,如果在hGLRC2里加载完tetxure,那么只能wglShareLists(hGLRC2,hGLRC)
代码如下:(顺序无关的)
void MultiThreadTexture_App::Init() { hWnd = GetActiveWindow(); hDC = GetDC(hWnd); hGLRC = wglGetCurrentContext(); hGLRC2 = wglCreateContext(hDC); wglShareLists(hGLRC, hGLRC2); //顺序无关 isRunning = true; std::thread th([this]() { wglMakeCurrent(hDC, hGLRC2); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); texAnotherThread.LoadFromFile1("1.jpg"); }); th.join(); }
void MultiThreadTexture_App::Init() { hWnd = GetActiveWindow(); hDC = GetDC(hWnd); hGLRC = wglGetCurrentContext(); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); hGLRC2 = wglCreateContext(hDC); isRunning = true; std::thread th([this]() { wglMakeCurrent(hDC, hGLRC2); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); texAnotherThread.LoadFromFile1("1.jpg"); }); th.join(); //这两个参数顺序是和渲染结果有关系的 wglShareLists(hGLRC2, hGLRC); //只能这样子。 }
使用线程2来渲染,代码如下: 结果是可以显示
void MultiThreadTexture_App::Init() { hWnd = GetActiveWindow(); hDC = GetDC(hWnd); hGLRC = wglGetCurrentContext(); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); hGLRC2 = wglCreateContext(hDC); wglShareLists(hGLRC, hGLRC2); isRunning = true; std::thread th([this]() { wglMakeCurrent(hDC, hGLRC2); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); texAnotherThread.LoadFromFile1("1.jpg");while (isRunning) //使用程序来控制 isRunning在线程运行之前为true,程序退出为false { if (isRenderBegin) //在调用Render的时候为true。 { Render(-10); AfterRender(); } } }); th.detach(); } void MultiThreadTexture_App::Render(int deltaMillionSeconds) { if (!isRenderBegin) isRenderBegin = true; if (deltaMillionSeconds != -10) return; glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, width / height, 0.1, 1000); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, 0, 100, 0, 0, 0, 0, 1, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClearColor(0, 0, 0, 1); glClearDepth(1000); glEnable(GL_FILL); glBindTexture(GL_TEXTURE_2D, texAnotherThread.texId); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex3f(0, 0, 0); glTexCoord2f(1, 0); glVertex3f(100, 0, 0); glTexCoord2f(1, 1); glVertex3f(100, 100, 0); glTexCoord2f(0, 1); glVertex3f(0, 100, 0); glEnd(); }
最后,做了一个交替渲染, 每1s为间隔,线程1,线程2交替渲染,而且他们使用对方的资源,比如第1s线程1渲染用的是线程2加载的纹理,第2s线程2渲染用的是线程1加载的纹理,如此循环...
代码如下:
void MultiThreadTexture_App::Init() { hWnd = GetActiveWindow(); hDC = GetDC(hWnd); hGLRC = wglGetCurrentContext(); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); hGLRC2 = wglCreateContext(hDC); wglShareLists(hGLRC, hGLRC2); tex.LoadFromFile1("2.png"); isRunning = true; lastRenderThreadID = -1;//默认值 std::thread th([this]() { wglMakeCurrent(hDC, hGLRC2); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); texAnotherThread.LoadFromFile1("1.jpg"); while (isRunning) //使用程序来控制 isRunning在线程运行之前为true,程序退出为false { if (isRenderBegin) //在调用Render的时候为true。 { Render(-10); AfterRender(); } } }); th.detach(); } //deltaMillionSeconds是根据时间变化的,每一帧的间隔 void MultiThreadTexture_App::Render(int deltaMillionSeconds) { mtx.lock(); if (!isRenderBegin) isRenderBegin = true; isRenderBegin = true; if (deltaMillionSeconds == -10) RenderThreadID = 2; else RenderThreadID = 1; if (lastRenderThreadID != -1 && lastRenderThreadID == RenderThreadID) {//和之前的线程一致 ::Sleep(1000); //保证1s调用1次 mtx.unlock(); return; } glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, width / height, 0.1, 1000); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, 0, 100, 0, 0, 0, 0, 1, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClearColor(0, 0, 0, 1); glClearDepth(1000); glEnable(GL_FILL); //这里互相使用对方的资源 if (RenderThreadID == 1) glBindTexture(GL_TEXTURE_2D, texAnotherThread.texId); else glBindTexture(GL_TEXTURE_2D, tex.texId); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex3f(0, 0, 0); glTexCoord2f(1, 0); glVertex3f(100, 0, 0); glTexCoord2f(1, 1); glVertex3f(100, 100, 0); glTexCoord2f(0, 1); glVertex3f(0, 100, 0); glEnd(); lastRenderThreadID = RenderThreadID; //休息1秒钟然后 ::Sleep(1000); mtx.unlock(); } void MultiThreadTexture_App::Release() { isRunning = false; }
线程1用的是游戏截图,线程2用的是狗头。而渲染使用对方的资源,效果图如下: