1d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich/*
2d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * Copyright (C) 2009 The Android Open Source Project
3d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich *
4d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * Licensed under the Apache License, Version 2.0 (the "License");
5d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * you may not use this file except in compliance with the License.
6d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * You may obtain a copy of the License at
7d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich *
8d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich *      http://www.apache.org/licenses/LICENSE-2.0
9d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich *
10d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * Unless required by applicable law or agreed to in writing, software
11d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * distributed under the License is distributed on an "AS IS" BASIS,
12d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * See the License for the specific language governing permissions and
14d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * limitations under the License.
15d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich */
16d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
17d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichpackage com.android.testlatency;
18d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
19d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport android.content.Context;
20d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport android.opengl.GLSurfaceView;
21d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport android.util.AttributeSet;
22d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport android.util.Log;
23d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport android.view.KeyEvent;
24d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport android.view.MotionEvent;
25d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
26d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport java.nio.ByteBuffer;
27d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport java.nio.ByteOrder;
28d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport java.nio.FloatBuffer;
29d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
30d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport javax.microedition.khronos.egl.EGL10;
31d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport javax.microedition.khronos.egl.EGLConfig;
32d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport javax.microedition.khronos.egl.EGLContext;
33d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport javax.microedition.khronos.egl.EGLDisplay;
34d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport javax.microedition.khronos.opengles.GL10;
35d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
36d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichimport android.opengl.GLES20;
37d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
38d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich/**
39d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * An implementation of SurfaceView that uses the dedicated surface for
40d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * displaying an OpenGL animation.  This allows the animation to run in a
41d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * separate thread, without requiring that it be driven by the update mechanism
42d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * of the view hierarchy.
43d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich *
44d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * The application-specific rendering code is delegated to a GLView.Renderer
45d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich * instance.
46d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich */
47d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevichclass TestLatencyView extends GLSurfaceView {
48d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich    private static String TAG = "TestLatencyiew";
49d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich    private float mX;
50d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich    private float mY;
51d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich    private float mDX;
52d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich    private float mDY;
53d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich    private long  mT;
54d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich    private long  mDT;
55d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
56d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich    public TestLatencyView(Context context) {
57d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        super(context);
58d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        setEGLContextClientVersion(2);
59d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        setRenderer(new Renderer());
60d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich    }
61d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
62d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich    @Override
63d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich    public boolean onTouchEvent(MotionEvent event) {
64d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        switch (event.getAction()) {
65d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        case MotionEvent.ACTION_MOVE:
66d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            float x = event.getX();
67d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            float y = event.getY();
68d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            long  t = event.getEventTime();
69d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            synchronized(this) {
70d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                mDT = t - mT;
71d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                mT = t;
72d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                mDX = x - mX;
73d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                mX = x;
74d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                mDY = y - mY;
75d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                mY = y;
76d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            }
77d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            break;
78d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        default:
79d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            break;
80d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        }
81d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        return true;
82d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich    }
83d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
84d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich    private class Renderer implements GLSurfaceView.Renderer {
85d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        private float mScaleX, mScaleY, mOffsetX, mOffsetY;
86d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        private final float MS_PER_FRAME = 1000 / 60;
87d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        public Renderer() {
88d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            mTriangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length * 4)
89d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                .order(ByteOrder.nativeOrder()).asFloatBuffer();
90d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        }
91d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
92d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
93d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        public void onDrawFrame(GL10 gl) {
94d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            GLES20.glClearColor(0.4f, 0.4f, 0.4f, 1.0f);
95d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
96d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            GLES20.glUseProgram(mProgram);
97d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            checkGlError("glUseProgram");
98d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
99d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            float x, y, dx, dy;
100d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            long t, dt;
101d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            synchronized(TestLatencyView.this) {
102d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                x = mX;
103d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                y = mY;
104d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                dx = mDX;
105d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                dy = mDY;
106d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                dt = mDT;
107d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            }
108d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
109d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            if (dt > 0) {
110d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                dx = dx * MS_PER_FRAME / dt;
111d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                dy = dy * MS_PER_FRAME / dt;
112d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            }
113d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
114d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            GLES20.glEnableVertexAttribArray(mvPositionHandle);
115d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            checkGlError("glEnableVertexAttribArray");
116d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            GLES20.glEnableVertexAttribArray(mvColorHandle);
117d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            checkGlError("glEnableVertexAttribArray");
118d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            for(int step = 0; step < 8; step++) {
119d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                float sx = (x + dx * step) * mScaleX + mOffsetX;
120d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                float sy = (y + dy * step) * mScaleY + mOffsetY;
121d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                int cbase = step * 4;
122d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
123d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                for (int i = 0; i < mTriangleVerticesData.length; i += 6) {
124d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                    mTriangleVerticesData2[i] = sx + mTriangleVerticesData[i];
125d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                    mTriangleVerticesData2[i+1] = -sy + mTriangleVerticesData[i+1];
126d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                    mTriangleVerticesData2[i+2] = mColors[cbase];
127d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                    mTriangleVerticesData2[i+3] = mColors[cbase+1];
128d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                    mTriangleVerticesData2[i+4] = mColors[cbase+2];
129d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                    mTriangleVerticesData2[i+5] = mColors[cbase+3];
130d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                }
131d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                mTriangleVertices.position(0);
132d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                mTriangleVertices.put(mTriangleVerticesData2).position(0);
133d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
134d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                GLES20.glVertexAttribPointer(mvPositionHandle, 2, GLES20.GL_FLOAT, false, 6*4, mTriangleVertices);
135d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                checkGlError("glVertexAttribPointer mvPosition");
136d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                mTriangleVertices.put(mTriangleVerticesData2).position(2);
137d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                GLES20.glVertexAttribPointer(mvColorHandle, 4, GLES20.GL_FLOAT, false, 6*4, mTriangleVertices);
138d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                checkGlError("glVertexAttribPointer mvColor");
139d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
140d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                checkGlError("glDrawArrays");
141d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            }
142d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        }
143d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
144d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        public void onSurfaceChanged(GL10 gl, int width, int height) {
145d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            GLES20.glViewport(0, 0, width, height);
146d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            mScaleX = 2.0f / width;
147d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            mScaleY = 2.0f / height;
148d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            mOffsetX = -1f;
149d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            mOffsetY = -1f;
150d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        }
151d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
152d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
153d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            mProgram = createProgram(mVertexShader, mFragmentShader);
154d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            if (mProgram == 0) {
155d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                return;
156d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            }
157d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            mvPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
158d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            checkGlError("glGetAttribLocation");
159d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            if (mvPositionHandle == -1) {
160d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                throw new RuntimeException("Could not get attrib location for vPosition");
161d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            }
162d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            mvColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
163d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            checkGlError("glGetAttribLocation");
164d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            if (mvColorHandle == -1) {
165d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                throw new RuntimeException("Could not get attrib location for vColor");
166d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            }
167d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        }
168d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
169d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        private int loadShader(int shaderType, String source) {
170d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            int shader = GLES20.glCreateShader(shaderType);
171d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            if (shader != 0) {
172d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                GLES20.glShaderSource(shader, source);
173d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                GLES20.glCompileShader(shader);
174d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                int[] compiled = new int[1];
175d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
176d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                if (compiled[0] == 0) {
177d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                    Log.e(TAG, "Could not compile shader " + shaderType + ":");
178d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                    Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
179d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                    GLES20.glDeleteShader(shader);
180d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                    shader = 0;
181d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                }
182d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            }
183d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            return shader;
184d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        }
185d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
186d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        private int createProgram(String vertexSource, String fragmentSource) {
187d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
188d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            if (vertexShader == 0) {
189d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                return 0;
190d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            }
191d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
192d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
193d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            if (pixelShader == 0) {
194d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                return 0;
195d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            }
196d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
197d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            int program = GLES20.glCreateProgram();
198d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            if (program != 0) {
199d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                GLES20.glAttachShader(program, vertexShader);
200d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                checkGlError("glAttachShader vertexShader");
201d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                GLES20.glAttachShader(program, pixelShader);
202d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                checkGlError("glAttachShader pixelShader");
203d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                GLES20.glLinkProgram(program);
204d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                int[] linkStatus = new int[1];
205d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
206d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                if (linkStatus[0] != GLES20.GL_TRUE) {
207d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                    Log.e(TAG, "Could not link program: ");
208d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                    Log.e(TAG, GLES20.glGetProgramInfoLog(program));
209d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                    GLES20.glDeleteProgram(program);
210d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                    program = 0;
211d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                }
212d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            }
213d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            return program;
214d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        }
215d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
216d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        private void checkGlError(String op) {
217d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            int error;
218d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
219d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                Log.e(TAG, op + ": glError " + error);
220d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                throw new RuntimeException(op + ": glError " + error);
221d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            }
222d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        }
223d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
224d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        // X, Y, R G B A
225d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        private final float[] mTriangleVerticesData = {
226d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                -0.025f, 0.3f, 0.0f, 1.0f, 0.0f, 1.0f,
227d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                 0.0f  , 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
228d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                 0.025f, 0.3f, 1.0f, 1.0f, 255.0f, 1.0f
229d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                };
230d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
231d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        // Color cascade:
232d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        private final float[] mColors = {
233d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                0.0f, 0.0f, 0.0f, 1.0f,
234d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                0.5f, 0.0f, 0.0f, 1.0f,
235d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                0.0f, 0.5f, 0.0f, 1.0f,
236d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                0.5f, 0.5f, 0.0f, 1.0f,
237d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
238d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                0.0f, 0.0f, 0.5f, 1.0f,
239d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                1.0f, 0.0f, 0.0f, 1.0f,
240d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                1.0f, 1.0f, 1.0f, 1.0f,
241d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich                0.0f, 1.0f, 0.0f, 1.0f
242d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        };
243d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
244d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        private float[] mTriangleVerticesData2 = new float[mTriangleVerticesData.length];
245d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        private FloatBuffer mTriangleVertices;
246d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
247d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        private final String mVertexShader = "attribute vec4 aPosition;\n"
248d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            + "attribute vec4 aColor;\n"
249d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            + "varying vec4 vColor;\n"
250d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            + "void main() {\n"
251d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            + "  gl_Position = aPosition;\n"
252d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            + "  vColor = aColor;\n"
253d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            + "}\n";
254d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
255d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        private final String mFragmentShader = "precision mediump float;\n"
256d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            + "varying vec4 vColor;\n"
257d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            + "void main() {\n"
258d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            + "  gl_FragColor = vColor;\n"
259d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich            + "}\n";
260d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
261d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        private int mProgram;
262d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        private int mvPositionHandle;
263d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich        private int mvColorHandle;
264d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
265d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich    }
266d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich}
267d0259a47fc90a0a3c69724a686580b29b4c36e69Jack Palevich
268