3

I have accurately tested my app before release it, on Emulator set with different screen size (and with different Android SDK and CPU emulations), and many real devices. No problems, everything works fine. Now an user has reported a bug with his tablet.

I'm testing the app on tons of devices and the issue happens only if the devices use some kind of soc ARM with PowerVR SGX544 and Android 4.x.

The app doesn't use any texture, only GL11, GL10 and GLView to plot some graph, and runs smooth also on old cheap smartphones with at least Gingerbread... but with this Power VR the result of the plot is an unreadable and laggy graphic glitch

No error in the Eclipse logs, No crash or code deprecation warning

Must I assume that the bug is in the GPU driver?

The code of the section with the bug (I cannot be more syntetic because I get no error)

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);
    }
}

The code where I setup 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);
    }
}
AndreaF
  • 11,975
  • 27
  • 102
  • 168
  • @AndonM.Coleman I have tried different Architectures ad GPUs... only soc ARM v7 with PowerVR SGX544 have this issue. – AndreaF Sep 10 '13 at 22:05
  • @AndonM.Coleman I have edited my question and included the code where I setup FloatBuffers – AndreaF Sep 10 '13 at 22:36
  • This probably is not related, but it looks like you are over-allocating your `ByteBuffer` in `private static ByteBuffer buildBuffer(byte[] b)`, you are allocating twice the length of the input array (even though they both have the same size data type). – Andon M. Coleman Sep 10 '13 at 22:48
  • @AndonM.Coleman I'm not sure that I have unerstand... why twice the length? – AndreaF Sep 10 '13 at 23:03
  • Because `ByteBuffer bb = ByteBuffer.allocateDirect(b.length << 1);` is the same as `ByteBuffer bb = ByteBuffer.allocateDirect(b.length * 2);`. But since the buffer `bb` is holding bytes and the input array `b` is also bytes, you are allocating twice as much storage as you need. – Andon M. Coleman Sep 10 '13 at 23:05
  • Ok,I have tried to use only 'b.length' but still same issue – AndreaF Sep 10 '13 at 23:12
  • Testing on so many devices is an interesting experiment, especially if you can identify the specific drivers and give a better description of the problem? Maybe some pictures? – ClayMontgomery Sep 10 '13 at 23:24

0 Answers0