【发布时间】:2014-11-05 15:45:53
【问题描述】:
我正在为 Android 设备编写一个效果过滤器,它在片段着色器中有二维循环。对于大多数设备来说,shader可以在合理的时间内编译并运行,但有些设备第一次编译shader需要几分钟时间。
我的片段着色器有一个很重的二维内核卷积:
const lowp int KERNEL_RADIUS = 19;
....
for (int y = -KERNEL_RADIUS; y <= KERNEL_RADIUS; y++)
{
for (int x = -KERNEL_RADIUS; x <= KERNEL_RADIUS; x++)
{
....
}
}
实际上它是一个 39x39 的循环,由于内核设计的原因,它不能分成两遍一维滤波器。内核权重存储为着色器的另一个输入纹理以供查找。 显然这个shader直接应用到正常尺寸(800x600~1600x1200)的图片上是不能有合理的性能的,所以我把图片调整到200x200~400x400,然后在大多数设备上都可以实时响应。
我知道有些着色器编译器不能接受这么大的循环,将无法编译程序。我发现了一些具有这种行为的设备。编译时间在设备上仍然是合理的。它只是报告失败,让我禁用效果过滤器。但在其他一些设备上,编译成功,程序可以正常使用,但第一次编译大约需要2~3分钟。之后,编译器缓存程序,给我50~100ms的编译时间,等我再次创建效果滤镜时。
目前我无法修改我的算法来删除或缩小二维循环,但如果我让用户等待几分钟等待第一次启动也很有趣。我想在这些设备上禁用效果过滤器。问题是我使用 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 上下文的线程不同的线程中编译着色器代码是否可能或安全?有人告诉我它们应该是同一个线程,我想知道当我真的需要这样做时它们是否能够不同。
【问题讨论】:
-
可能你的着色器代码有问题导致编译时间过长,(如果你返回 0,你也应该销毁着色器)
标签: android asynchronous opengl-es glsl shader