在发布之前,我已经在具有不同屏幕尺寸(以及不同的 Android SDK 和 CPU 仿真)的模拟器上以及许多真实设备上准确地测试了我的应用程序。没问题,一切正常。现在,一位用户报告了他的平板电脑的错误。
我正在大量设备上测试该应用程序,只有当设备使用某种带有PowerVR SGX544和 Android 4.x的 soc ARM 时才会出现问题。
该应用程序不使用任何纹理,仅使用 GL11、GL10 和 GLView 来绘制一些图形,并且在具有至少 Gingerbread 的旧廉价智能手机上也能流畅运行......但是使用这个 Power VR,绘图的结果是不可读和滞后图形故障
Eclipse 日志中没有错误,没有崩溃或代码弃用警告
我必须假设错误存在于 GPU 驱动程序中吗?
有错误的部分的代码(我不能更综合,因为我没有收到错误)
public class My3dView extends GLView implements
Grapher,
TouchHandler.TouchHandlerInterface
{
private float lastTouchX, lastTouchY;
private TouchHandler touchHandler;
private float zoomLevel = 1, targetZoom, zoomStep = 0, currentZoom;
private FPS fps = new FPS();
private Graph3d graph;
public My3dView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public My3dView(Context context) {
super(context);
touchHandler = new TouchHandler(this);
init();
}
private void init() {
startLooping();
zoomController.setOnZoomListener(this);
Matrix.setIdentityM(matrix1, 0);
Matrix.rotateM(matrix1, 0, -75, 1, 0, 0);
}
public void onVisibilityChanged(boolean visible) {
}
@Override
protected void glDraw() {
if ((zoomStep < 0 && zoomLevel > targetZoom) ||
(zoomStep > 0 && zoomLevel < targetZoom)) {
zoomLevel += zoomStep;
} else if (zoomStep != 0) {
zoomStep = 0;
zoomLevel = targetZoom;
isDirty = true;
if (!shouldRotate()) {
stopLooping();
}
}
super.glDraw();
}
@Override
public void onDetachedFromWindow() {
zoomController.setVisible(false);
super.onDetachedFromWindow();
}
public void onTouchDown(float x, float y) {
zoomController.setVisible(true);
stopLooping();
lastTouchX = x;
lastTouchY = y;
}
public void onTouchMove(float x, float y) {
float deltaX = x - lastTouchX;
float deltaY = y - lastTouchY;
if (deltaX > 1 || deltaX < -1 || deltaY > 1 || deltaY < -1) {
setRotation(deltaX, deltaY);
glDraw();
lastTouchX = x;
lastTouchY = y;
}
}
public void onTouchUp(float x, float y) {
float vx = touchHandler.velocityTracker.getXVelocity();
float vy = touchHandler.velocityTracker.getYVelocity();
setRotation(vx/100, vy/100);
if (shouldRotate()) {
startLooping();
}
}
public void onTouchZoomDown(float x1, float y1, float x2, float y2) {
}
public void onTouchZoomMove(float x1, float y1, float x2, float y2) {
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return touchHandler != null ? touchHandler.onTouchEvent(event) : super.onTouchEvent(event);
}
private float[] matrix1 = new float[16], matrix2 = new float[16], matrix3 = new float[16];
private float angleX, angleY;
private boolean isDirty;
private Function function;
private static final float DISTANCE = 15f;
void setRotation(float x, float y) {
angleX = x;
angleY = y;
}
boolean shouldRotate() {
final float limit = .5f;
return angleX < -limit || angleX > limit || angleY < -limit || angleY > limit;
}
public void setFunction(Function f) {
function = f;
zoomLevel = 1;
isDirty = true;
}
@Override
public void onSurfaceCreated(GL10 gl, int width, int height) {
gl.glDisable(GL10.GL_DITHER);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
gl.glClearColor(0, 0, 0, 1);
gl.glShadeModel(GL10.GL_SMOOTH);
gl.glDisable(GL10.GL_LIGHTING);
graph = new Graph3d((GL11) gl);
isDirty = true;
angleX = .5f;
angleY = 0;
gl.glViewport(0, 0, width, height);
initFrustum(gl, DISTANCE * zoomLevel);
currentZoom = zoomLevel;
}
@Override
public void onDrawFrame(GL10 gl10) {
GL11 gl = (GL11) gl10;
if (currentZoom != zoomLevel) {
initFrustum(gl, DISTANCE * zoomLevel);
currentZoom = zoomLevel;
}
if (isDirty) {
graph.update(gl, function, zoomLevel);
isDirty = false;
}
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glTranslatef(0, 0, -DISTANCE*zoomLevel);
Matrix.setIdentityM(matrix2, 0);
float ax = Math.abs(angleX);
float ay = Math.abs(angleY);
if (ay * 3 < ax) {
Matrix.rotateM(matrix2, 0, angleX, 0, 1, 0);
} else if (ax * 3 < ay) {
Matrix.rotateM(matrix2, 0, angleY, 1, 0, 0);
} else {
if (ax > ay) {
Matrix.rotateM(matrix2, 0, angleX, 0, 1, 0);
Matrix.rotateM(matrix2, 0, angleY, 1, 0, 0);
} else {
Matrix.rotateM(matrix2, 0, angleY, 1, 0, 0);
Matrix.rotateM(matrix2, 0, angleX, 0, 1, 0);
}
}
Matrix.multiplyMM(matrix3, 0, matrix2, 0, matrix1, 0);
gl.glMultMatrixf(matrix3, 0);
System.arraycopy(matrix3, 0, matrix1, 0, 16);
graph.draw(gl);
}
private void initFrustum(GL10 gl, float distance) {
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
float near = distance * (1/3f);
float far = distance * 3f;
float dimen = near/5f;
float h = dimen * height / width;
gl.glFrustumf(-dimen, dimen, -h, h, near, far);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
}
}
我设置 FloatBuffers 的代码:
class Graph3d {
private final int N = 48;
private ShortBuffer verticeIdx;
private FloatBuffer vertexBuf;
private ByteBuffer colorBuf;
private int vertexVbo, colorVbo, vertexElementVbo;
private boolean useVBO;
private int nVertex;
Graph3d(GL11 gl) {
short[] b = new short[N*N];
int p = 0;
for (int i = 0; i < N; i++) {
short v = 0;
for (int j = 0; j < N; v += N+N, j+=2) {
b[p++] = (short)(v+i);
b[p++] = (short)(v+N+N-1-i);
}
v = (short) (N*(N-2));
i++;
for (int j = N-1; j >= 0; v -= N+N, j-=2) {
b[p++] = (short)(v+N+N-1-i);
b[p++] = (short)(v+i);
}
}
verticeIdx = buildBuffer(b);
String extensions = gl.glGetString(GL10.GL_EXTENSIONS);
useVBO = extensions.indexOf("vertex_buffer_object") != -1;
Calculator.log("VBOs support: " + useVBO + " version " + gl.glGetString(GL10.GL_VERSION));
if (useVBO) {
int[] out = new int[3];
gl.glGenBuffers(3, out, 0);
vertexVbo = out[0];
colorVbo = out[1];
vertexElementVbo = out[2];
}
}
private static FloatBuffer buildBuffer(float[] b) {
ByteBuffer bb = ByteBuffer.allocateDirect(b.length << 2);
bb.order(ByteOrder.nativeOrder());
FloatBuffer sb = bb.asFloatBuffer();
sb.put(b);
sb.position(0);
return sb;
}
private static ShortBuffer buildBuffer(short[] b) {
ByteBuffer bb = ByteBuffer.allocateDirect(b.length << 1);
bb.order(ByteOrder.nativeOrder());
ShortBuffer sb = bb.asShortBuffer();
sb.put(b);
sb.position(0);
return sb;
}
private static ByteBuffer buildBuffer(byte[] b) {
ByteBuffer bb = ByteBuffer.allocateDirect(b.length << 1);
bb.order(ByteOrder.nativeOrder());
bb.put(b);
bb.position(0);
return bb;
}
public void update(GL11 gl, Function f, float zoom) {
final int NTICK = Calculator.useHighQuality3d ? 5 : 0;
final float size = 4*zoom;
final float minX = -size, maxX = size, minY = -size, maxY = size;
Calculator.log("update VBOs " + vertexVbo + ' ' + colorVbo + ' ' + vertexElementVbo);
nVertex = N*N+6+8 + NTICK*6;
int nFloats = nVertex * 3;
float vertices[] = new float[nFloats];
byte colors[] = new byte[nVertex << 2];
if (f != null) {
Calculator.log("Graph3d update");
float sizeX = maxX - minX;
float sizeY = maxY - minY;
float stepX = sizeX / (N-1);
float stepY = sizeY / (N-1);
int pos = 0;
double sum = 0;
float y = minY;
float x = minX - stepX;
int nRealPoints = 0;
for (int i = 0; i < N; i++, y+=stepY) {
float xinc = (i & 1) == 0 ? stepX : -stepX;
x += xinc;
for (int j = 0; j < N; ++j, x+=xinc, pos+=3) {
float z = (float) f.eval(x, y);
vertices[pos] = x;
vertices[pos+1] = y;
vertices[pos+2] = z;
if (z == z) { // not NAN
sum += z * z;
++nRealPoints;
}
}
}
float maxAbs = (float) Math.sqrt(sum / nRealPoints);
maxAbs *= .9f;
maxAbs = Math.min(maxAbs, 15);
maxAbs = Math.max(maxAbs, .001f);
final int limitColor = N*N*4;
for (int i = 0, j = 2; i < limitColor; i+=4, j+=3) {
float z = vertices[j];
if (z == z) {
final float a = z / maxAbs;
final float abs = a < 0 ? -a : a;
colors[i] = floatToByte(a);
colors[i+1] = floatToByte(1-abs*.3f);
colors[i+2] = floatToByte(-a);
colors[i+3] = (byte) 255;
} else {
vertices[j] = 0;
z = 0;
colors[i] = 0;
colors[i+1] = 0;
colors[i+2] = 0;
colors[i+3] = 0;
}
}
}
int base = N*N*3;
int colorBase = N*N*4;
int p = base;
final int baseSize = 2;
for (int i = -baseSize; i <= baseSize; i+=2*baseSize) {
vertices[p] = i; vertices[p+1] = -baseSize; vertices[p+2] = 0;
p += 3;
vertices[p] = i; vertices[p+1] = baseSize; vertices[p+2] = 0;
p += 3;
vertices[p] = -baseSize; vertices[p+1] = i; vertices[p+2] = 0;
p += 3;
vertices[p] = baseSize; vertices[p+1] = i; vertices[p+2] = 0;
p += 3;
}
for (int i = colorBase; i < colorBase+8*4; i += 4) {
colors[i] = 0;
colors[i+1] = 0;
colors[i+2] = (byte) 255;
colors[i+3] = (byte) 255;
}
base += 8*3;
colorBase += 8*4;
final float unit = 2;
final float axis[] = {
0, 0, 0,
unit, 0, 0,
0, 0, 0,
0, unit, 0,
0, 0, 0,
0, 0, unit,
};
System.arraycopy(axis, 0, vertices, base, 6*3);
for (int i = colorBase; i < colorBase+6*4; i+=4) {
colors[i] = (byte) 255;
colors[i+1] = (byte) 255;
colors[i+2] = (byte) 255;
colors[i+3] = (byte) 255;
}
base += 6*3;
colorBase += 6*4;
p = base;
final float tick = .03f;
final float offset = .01f;
for (int i = 1; i <= NTICK; ++i) {
vertices[p] = i-tick;
vertices[p+1] = -offset;
vertices[p+2] = -offset;
vertices[p+3] = i+tick;
vertices[p+4] = offset;
vertices[p+5] = offset;
p += 6;
vertices[p] = -offset;
vertices[p+1] = i-tick;
vertices[p+2] = -offset;
vertices[p+3] = offset;
vertices[p+4] = i+tick;
vertices[p+5] = offset;
p += 6;
vertices[p] = -offset;
vertices[p+1] = -offset;
vertices[p+2] = i-tick;
vertices[p+3] = offset;
vertices[p+4] = offset;
vertices[p+5] = i+tick;
p += 6;
}
for (int i = colorBase+NTICK*6*4-1; i >= colorBase; --i) {
colors[i] = (byte) 255;
}
vertexBuf = buildBuffer(vertices);
colorBuf = buildBuffer(colors);
if (useVBO) {
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vertexVbo);
gl.glBufferData(GL11.GL_ARRAY_BUFFER, vertexBuf.capacity()*4, vertexBuf, GL11.GL_STATIC_DRAW);
vertexBuf = null;
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, colorVbo);
gl.glBufferData(GL11.GL_ARRAY_BUFFER, colorBuf.capacity(), colorBuf, GL11.GL_STATIC_DRAW);
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
colorBuf = null;
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, vertexElementVbo);
gl.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, verticeIdx.capacity()*2, verticeIdx, GL11.GL_STATIC_DRAW);
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
private byte floatToByte(float v) {
return (byte) (v <= 0 ? 0 : v >= 1 ? 255 : (int)(v*255));
}
public void draw(GL11 gl) {
if (useVBO) {
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vertexVbo);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, colorVbo);
gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, 0);
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
// gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, N*N);
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, vertexElementVbo);
gl.glDrawElements(GL10.GL_LINE_STRIP, N*N, GL10.GL_UNSIGNED_SHORT, 0);
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
} else {
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuf);
gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, colorBuf);
gl.glDrawElements(GL10.GL_LINE_STRIP, N*N, GL10.GL_UNSIGNED_SHORT, verticeIdx);
}
final int N2 = N*N;
gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, N2);
gl.glDrawArrays(GL10.GL_LINES, N2, nVertex - N2);
}
}