用C++理解OpenGLES_多线程编译shader
下面是前几天开发过程中的记录: 问题: 当前游戏Loading时间过慢,经过检查PreWarmShader消耗过多。其中定点到glCompileShader函数阻塞。
解决方案:
首先想到的就是将shader进行简化,但是效果实在甚微。 于是考虑将shader分线程进行compile
1.创建新线程CSThread(容易)
2.为CSThread创建一个环状队列(容易)
3.将render线程中的shaderCompile部分进行拆分,立刻需要使用的shader,依然保留在render线程中,需要预编译的shader丢到CSThread等待队列中(顺利)
4.CSThread创建新的context(不顺利) 首先考虑的是两个线程公用一个context,使用glMakeCurrent进行线程绑定,但是引发的问题是一旦glMakeCurrent(null,null)将context从render线程解绑,就会导致render失效,这是openGLES机制引发的,glMakeCurrent(null,null)会导致驱动丢弃所有未完成的操作,结果就是渲染失效。 于是创建了两个context,想共用同一个surface,结果发现屏幕黑掉了,因为CSThread没有任何东西需要提交到surface,但是当surface提交到前台时候,会是黑屏。 于是每个线程单独一个context,每个context创建一个自己的surface,结果crash,因为eglCreateWindowSurface限制每个dispaly只允许有一个有效surface。 于是第二个context创建一个eglCreatePbufferSurface,永不提交该surface,结果运行时crash,因为两个context中数据不可交互,包括VBO这些。 于是使用sharedContext,终于成功…… 所以这里的核心流程如下,重心就两块:
-eglGetDisplay(EGL_DEFAULT_DISPLAY);
-eglInitialize( display, 0, 0 );
-eglChooseConfig( display, configs );
-eglGetConfigAttrib( selectConfig );
-pSurface = eglCreatePbufferSurface( display,selectConfig);// 注意,这里不再是eglCreateWindowSurface。
-csContext = eglCreateContext( display,selectConfig, renderContext,EGL_CONTEXT_CLIENT_VERSION);// 注意,第三个参数使用renderContext,而非null,这样就启动了shareContext。
-eglMakeCurrent(display,pSurface,pSurface,csContext);
... your code here
-eglDestroyContext( display, csContext ); // 这里建议在第二个线程销毁后调用。
- 然后我把所有的shader都丢给CSThread处理,结果有很轻度的Loading加速。
- 我将shader分为两组,一组继续render线程处理,一组给新的CSThread处理,最终得到了良好效果。
举例:render线程原本有两个任务 - 实际的render到屏幕,假设消耗2s - 编译shader,假设消耗20s。 我进行第5步骤后,CSThread编译shader 20s,render线程只做一件事就是render到屏幕2s,因为是多线程并行,所以累计消耗为20s。
我进行第6步骤后,CSThread编译一半的shader 10s,render线程两个任务 -实际的render到屏幕2s+编译一半的shader 10s,实际消耗12s。如果shader分配的更合理的话,则最佳可能优化到11秒。