最近APP计划适配小米Pad,直接拿线上的code build,发现启动时CRASH,而且是必现的。线上code我们都发布了很多个版本了,怎么会有这种问题呢?
CRASH问题
Eclipse看logcat输出,最终定位到OpenGL的一次draw call函数上:
gl.glColor4f(bodyColor4f[0], bodyColor4f[1], bodyColor4f[2], bodyColor4f[3]);
// set vertices
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVerticesBuffer);
gl.glDisable(GL10.GL_TEXTURE_2D);
// draw
gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0, CIRCLE_VERTEX_SIZE);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glColor4f(1, 1, 1, 1);
问题发生在glDrawArrays函数处。
这段code是绘制一处半透明的圆圈。
问题分析
所有手机上运行都没问题,唯独小米Pad,因此猜想:这段代码一定没有问题,只不过不够严谨。进一步怀疑是OpenGL状态管理的不严谨。
进一步分析:这段代码使用颜色数组模式绘制一个圆,因此glDrawArrays前禁掉了GL_TEXTURE_2D,绘制完成后开启GL_TEXTURE_2D。因此将怀疑的范围进一步缩小到顶点数组的模式状态。
glDrawArrays时候,虽然禁掉了GL_TEXTURE_2D,但是GL_TEXTURE_COORD_ARRAY 还处于激活状态。这段代码通过颜色模式绘制圆,glVertexPointer只上传了顶点的位置和颜色数据,但是GL_COLOR_ARRAY和GL_VERTEX_ARRAY,GL_TEXTURE_COORD_ARRAY都处于激活状态,GL 如果检查不够严格(没有考虑当前GL_TEXTURE_2D是否激活),仍然按三个状态去取每个顶点的位置、颜色、纹理数据,此时就导致显存Buffer的越界,从而导致CRASH。
验证分析
在glDrawArrays前显示禁掉顶点纹理坐标数组,绘制完成后开启。
gl.glColor4f(bodyColor4f[0], bodyColor4f[1], bodyColor4f[2], bodyColor4f[3]);
// set vertices
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVerticesBuffer);
gl.glDisable(GL10.GL_TEXTURE_2D);
gl.glDisable(GL_TEXTURE_COORD_ARRAY); // dizuo open
// draw
gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0, CIRCLE_VERTEX_SIZE);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glEnable(GL_TEXTURE_COORD_ARRAY); // dizuo close
gl.glColor4f(1, 1, 1, 1);
再次Build,运行OK。 跟之前分析的完全一致。。
进一步反思:
手机上为何没有CRASH?
手机上对这种不严谨行为做法:OpenGL取顶点数据的时候,结合了当前GL_TEXTURE_2D的状态,纹理没有激活就不取纹理顶点数据,不管glVertexPointer到底实际是否传了纹理数据。。。
因此,OpenGL开发还是要显式管理状态。否则一些莫名其妙地CRASH就不请自来了。。。
更多OpenGL常见错误总结:http://blog.csdn.net/ryfdizuo/article/details/9961143