异步或在Android上的其他线程中编译GLSL着色器
作者:互联网
我正在为Android设备编写一个效果滤镜,它在片段着色器中具有二维循环.对于大多数设备,可以在合理的时间内编译和运行着色器,但是某些设备在第一次编译时需要花费几分钟.
我的片段着色器具有沉重的二维内核卷积:
const lowp int KERNEL_RADIUS = 19;
....
for (int y = -KERNEL_RADIUS; y <= KERNEL_RADIUS; y++)
{
for (int x = -KERNEL_RADIUS; x <= KERNEL_RADIUS; x++)
{
....
}
}
实际上,这是一个39×39的循环,由于内核设计的原因,它不能分为两维的一维滤波器.内核权重存储为着色器的另一个输入纹理以进行查找.
显然,当直接应用于具有正常大小(800×600〜1600×1200)的图像时,此着色器不能具有合理的性能,因此我将图像的大小调整为200×200〜400×400,然后我可以在大多数设备上获得实时响应.
我知道某些着色器编译器无法接受如此大的循环,并且将无法编译程序.我发现某些设备具有这种行为.设备上的编译时间仍然合理.它只是报告失败,让我禁用效果滤镜.但是,在其他一些设备上,编译成功并且程序可以正常使用,但是第一次编译大约需要2到3分钟.之后,当我再次创建效果滤镜时,编译器将缓存程序并给出50〜100 ms的编译时间.
目前,我无法修改算法来删除或缩小二维循环,但是如果让用户等待几分钟以进行首次启动,这也很有趣.我想在这些设备上禁用效果过滤器.问题是我使用GLES20.glCompileShader()来编译着色器:
public static int loadShader(final String strSource, final int iType)
{
int[] compiled = new int[1];
int iShader = GLES20.glCreateShader(iType);
GLES20.glShaderSource(iShader, strSource);
GLES20.glCompileShader(iShader);
GLES20.glGetShaderiv(iShader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.d("Load Shader Failed", "Compilation\n" + GLES20.glGetShaderInfoLog(iShader));
return 0;
}
return iShader;
}
这是一个阻止呼叫.我需要等待几分钟,然后才能决定在此类设备上禁用过滤器.
有没有一种方法可以异步或在有限的时间内编译着色器代码?
(例如,如果尚未完成,则在5秒钟内返回失败.)
如果只能同步调用glCompileShader(),则我想强制终止线程,以免阻塞AP.但这会引起严重的问题.编译着色器代码的线程与创建OpenGL上下文的线程相同.如果在线程阻塞时杀死该线程,则无法在同一线程中适当地破坏OpenGL上下文.
在不同于初始化OpenGL上下文的线程中编译着色器代码是否可能或安全?有人告诉我它们应该是同一线程,我想知道当我确实需要这样做时它们是否可以不同.
解决方法:
我不得不说我从未见过需要MINUTES在Android设备上进行编译的着色器,所以我怀疑那里存在问题,但是可以在单独的线程上创建着色器.为此,您必须创建一个第二EGLContext,该EGLContext设置为与主上下文共享资源.
大多数现代设备都支持这种功能,但是我遇到了一些不支持这种功能的较旧的Android设备.
创建这样的第二个EGLContext:
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
EGLContext2 = egl.eglCreateContext( display, eglConfig, EGLContext1, attrib_list);
if( EGLContext2!=null )
{
int pbufferAttribs[] = { EGL10.EGL_WIDTH, 1, EGL10.EGL_HEIGHT, 1, EGL10.EGL_NONE };
EGLSurface2 = egl.eglCreatePbufferSurface( display, eglConfig, pbufferAttribs );
}
然后创建您的线程,并从线程中调用它来设置上下文:
egl.eglMakeCurrent( EGLDisplay, EGLSurface2, EGLSurface2, EGLContext2 );
此后,您可以从该辅助线程创建任何GL资源,包括着色器,然后在主线程中使用着色器. (确保创建着色器后,在第二个线程上调用glFlush()).当线程退出时,您必须清理辅助EGLcontext:
egl.eglDestroyContext( EGLDisplay, EGLContext2 );
标签:opengl-es,asynchronous,shader,glsl,android 来源: https://codeday.me/bug/20191121/2049105.html