我正在使用Brad Larson的GPUImage库,我想我发现了一个有趣的问题。
下面的着色器程序执行得很好:
NSString *const kDilationFragmentShaderString = SHADER_STRING
(
precision highp float;
uniform int height;
uniform int width;
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform int radius;
void main (void)
{
vec2 uv = textureCoordinate;
vec2 theSize = vec2(width, height);
vec3 theMax = texture2D(inputImageTexture, uv).rgb;
gl_FragColor = vec4(theMax, 1.0);
}
);
这个版本,然而,崩溃的大图像(即,4 × 3的图像从相机调整到2560的最长边)。在我看来,唯一明显不同的是texture2D调用集:
NSString *const kDilationFragmentShaderString = SHADER_STRING
(
precision highp float;
uniform int height;
uniform int width;
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform int radius;
void main (void)
{
vec2 uv = textureCoordinate;
vec2 theSize = vec2(width, height);
vec3 theMax = texture2D(inputImageTexture, uv).rgb;
int i;
int j;
int radsqr = radius*radius;
for (j = -radius; j <= radius; ++j) {
for (i = -radius; i <= radius; ++i) {
if (i * i + j * j > radsqr) continue;
theMax = max(theMax, texture2D(inputImageTexture, uv + vec2(i,j)/theSize).rgb);
}
}
gl_FragColor = vec4(theMax, 1.0);
}
);
我正在运行这个过滤器,然后是最小值的第二个过滤器(即形态膨胀,然后是侵蚀,或形态闭合操作符)。
我确实意识到实现这一点的更优方法是尝试通过顶点着色器将所有texture2D调用到自己的位置;然而,如果半径为10,则需要314个顶点,这超出了允许的位置数量。如果我在模拟器中运行这些代码,并且所有其他东西都是相同的,那么第一个代码完成得很好,但是第二个代码会增加内存,并且内存会因为侵蚀过滤器的调用而急剧上升。在iPhone 4s上运行,第一个代码片段完成得很好(当然,非常快),但第二个代码片段在膨胀后崩溃,并且没有运行侵蚀调用。
最初,它看起来像texture2D泄漏;然而,这些函数是在线程中调用的。当线程退出时,模拟器中的所有内存都会被清除。因此,如果函数第一次工作正常,则可以运行多次而没有问题。
所以我的问题是:什么是texture2D调用做那里可能导致这种行为?是否有一种方法来冲洗任何缓冲区,一旦过滤器已经完成,独立于结束调用之间的线程?
编辑:自从发布这个问题以来,我在一周中学到了一些东西:问题在于for循环本身。删除for循环,内存问题就消失了。也就是
NSString *const kDilationFragmentShaderString = SHADER_STRING
(
precision highp float;
uniform int height;
uniform int width;
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform int radius;
void main (void)
{
vec2 uv = textureCoordinate;
vec2 theSize = vec2(width, height);
vec3 theMax = texture2D(inputImageTexture, uv).rgb;
int i;
int j;
int radsqr = radius*radius;
for (j = -radius; j <= radius; ++j) {
for (i = -radius; i <= radius; ++i) {
}
}
gl_FragColor = vec4(theMax, 1.0);
}
);
将分配尽可能多的内存,就好像在循环内部发生了什么事情一样。我通过模拟器上的检查器来确定这种行为。当我在1280x1280的图像上运行一个没有for循环的shader时,我总共分配了202 mb,当我运行for循环时,我分配了230 mb,不管for循环内部发生了什么。同样的行为也发生在while循环中。
如果你想刷新东西,你可以调用glFlush()
,它将刷新当前上下文的OpenGL命令队列。你可以做的另一件事是平铺你的图像,一次处理更小的部分。这就是像Photoshop, Final Cut Pro和其他应用程序的工作原理,它可以非常有效地节省内存。