其他分享
首页 > 其他分享> > 异步或在Android上的其他线程中编译GLSL着色器

异步或在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