| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.testlatency; |
| |
| import android.content.Context; |
| import android.opengl.GLSurfaceView; |
| import android.util.AttributeSet; |
| import android.util.Log; |
| import android.view.KeyEvent; |
| import android.view.MotionEvent; |
| |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.nio.FloatBuffer; |
| |
| import javax.microedition.khronos.egl.EGL10; |
| import javax.microedition.khronos.egl.EGLConfig; |
| import javax.microedition.khronos.egl.EGLContext; |
| import javax.microedition.khronos.egl.EGLDisplay; |
| import javax.microedition.khronos.opengles.GL10; |
| |
| import android.opengl.GLES20; |
| |
| /** |
| * An implementation of SurfaceView that uses the dedicated surface for |
| * displaying an OpenGL animation. This allows the animation to run in a |
| * separate thread, without requiring that it be driven by the update mechanism |
| * of the view hierarchy. |
| * |
| * The application-specific rendering code is delegated to a GLView.Renderer |
| * instance. |
| */ |
| class TestLatencyView extends GLSurfaceView { |
| private static String TAG = "TestLatencyiew"; |
| private float mX; |
| private float mY; |
| private float mDX; |
| private float mDY; |
| private long mT; |
| private long mDT; |
| |
| public TestLatencyView(Context context) { |
| super(context); |
| setEGLContextClientVersion(2); |
| setRenderer(new Renderer()); |
| } |
| |
| @Override |
| public boolean onTouchEvent(MotionEvent event) { |
| switch (event.getAction()) { |
| case MotionEvent.ACTION_MOVE: |
| float x = event.getX(); |
| float y = event.getY(); |
| long t = event.getEventTime(); |
| synchronized(this) { |
| mDT = t - mT; |
| mT = t; |
| mDX = x - mX; |
| mX = x; |
| mDY = y - mY; |
| mY = y; |
| } |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| private class Renderer implements GLSurfaceView.Renderer { |
| private float mScaleX, mScaleY, mOffsetX, mOffsetY; |
| private final float MS_PER_FRAME = 1000 / 60; |
| public Renderer() { |
| mTriangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length * 4) |
| .order(ByteOrder.nativeOrder()).asFloatBuffer(); |
| } |
| |
| |
| public void onDrawFrame(GL10 gl) { |
| GLES20.glClearColor(0.4f, 0.4f, 0.4f, 1.0f); |
| GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); |
| GLES20.glUseProgram(mProgram); |
| checkGlError("glUseProgram"); |
| |
| float x, y, dx, dy; |
| long t, dt; |
| synchronized(TestLatencyView.this) { |
| x = mX; |
| y = mY; |
| dx = mDX; |
| dy = mDY; |
| dt = mDT; |
| } |
| |
| if (dt > 0) { |
| dx = dx * MS_PER_FRAME / dt; |
| dy = dy * MS_PER_FRAME / dt; |
| } |
| |
| GLES20.glEnableVertexAttribArray(mvPositionHandle); |
| checkGlError("glEnableVertexAttribArray"); |
| GLES20.glEnableVertexAttribArray(mvColorHandle); |
| checkGlError("glEnableVertexAttribArray"); |
| for(int step = 0; step < 8; step++) { |
| float sx = (x + dx * step) * mScaleX + mOffsetX; |
| float sy = (y + dy * step) * mScaleY + mOffsetY; |
| int cbase = step * 4; |
| |
| for (int i = 0; i < mTriangleVerticesData.length; i += 6) { |
| mTriangleVerticesData2[i] = sx + mTriangleVerticesData[i]; |
| mTriangleVerticesData2[i+1] = -sy + mTriangleVerticesData[i+1]; |
| mTriangleVerticesData2[i+2] = mColors[cbase]; |
| mTriangleVerticesData2[i+3] = mColors[cbase+1]; |
| mTriangleVerticesData2[i+4] = mColors[cbase+2]; |
| mTriangleVerticesData2[i+5] = mColors[cbase+3]; |
| } |
| mTriangleVertices.position(0); |
| mTriangleVertices.put(mTriangleVerticesData2).position(0); |
| |
| GLES20.glVertexAttribPointer(mvPositionHandle, 2, GLES20.GL_FLOAT, false, 6*4, mTriangleVertices); |
| checkGlError("glVertexAttribPointer mvPosition"); |
| mTriangleVertices.put(mTriangleVerticesData2).position(2); |
| GLES20.glVertexAttribPointer(mvColorHandle, 4, GLES20.GL_FLOAT, false, 6*4, mTriangleVertices); |
| checkGlError("glVertexAttribPointer mvColor"); |
| GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3); |
| checkGlError("glDrawArrays"); |
| } |
| } |
| |
| public void onSurfaceChanged(GL10 gl, int width, int height) { |
| GLES20.glViewport(0, 0, width, height); |
| mScaleX = 2.0f / width; |
| mScaleY = 2.0f / height; |
| mOffsetX = -1f; |
| mOffsetY = -1f; |
| } |
| |
| public void onSurfaceCreated(GL10 gl, EGLConfig config) { |
| mProgram = createProgram(mVertexShader, mFragmentShader); |
| if (mProgram == 0) { |
| return; |
| } |
| mvPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); |
| checkGlError("glGetAttribLocation"); |
| if (mvPositionHandle == -1) { |
| throw new RuntimeException("Could not get attrib location for vPosition"); |
| } |
| mvColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor"); |
| checkGlError("glGetAttribLocation"); |
| if (mvColorHandle == -1) { |
| throw new RuntimeException("Could not get attrib location for vColor"); |
| } |
| } |
| |
| private int loadShader(int shaderType, String source) { |
| int shader = GLES20.glCreateShader(shaderType); |
| if (shader != 0) { |
| GLES20.glShaderSource(shader, source); |
| GLES20.glCompileShader(shader); |
| int[] compiled = new int[1]; |
| GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); |
| if (compiled[0] == 0) { |
| Log.e(TAG, "Could not compile shader " + shaderType + ":"); |
| Log.e(TAG, GLES20.glGetShaderInfoLog(shader)); |
| GLES20.glDeleteShader(shader); |
| shader = 0; |
| } |
| } |
| return shader; |
| } |
| |
| private int createProgram(String vertexSource, String fragmentSource) { |
| int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); |
| if (vertexShader == 0) { |
| return 0; |
| } |
| |
| int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); |
| if (pixelShader == 0) { |
| return 0; |
| } |
| |
| int program = GLES20.glCreateProgram(); |
| if (program != 0) { |
| GLES20.glAttachShader(program, vertexShader); |
| checkGlError("glAttachShader vertexShader"); |
| GLES20.glAttachShader(program, pixelShader); |
| checkGlError("glAttachShader pixelShader"); |
| GLES20.glLinkProgram(program); |
| int[] linkStatus = new int[1]; |
| GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); |
| if (linkStatus[0] != GLES20.GL_TRUE) { |
| Log.e(TAG, "Could not link program: "); |
| Log.e(TAG, GLES20.glGetProgramInfoLog(program)); |
| GLES20.glDeleteProgram(program); |
| program = 0; |
| } |
| } |
| return program; |
| } |
| |
| private void checkGlError(String op) { |
| int error; |
| while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { |
| Log.e(TAG, op + ": glError " + error); |
| throw new RuntimeException(op + ": glError " + error); |
| } |
| } |
| |
| // X, Y, R G B A |
| private final float[] mTriangleVerticesData = { |
| -0.025f, 0.3f, 0.0f, 1.0f, 0.0f, 1.0f, |
| 0.0f , 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, |
| 0.025f, 0.3f, 1.0f, 1.0f, 255.0f, 1.0f |
| }; |
| |
| // Color cascade: |
| private final float[] mColors = { |
| 0.0f, 0.0f, 0.0f, 1.0f, |
| 0.5f, 0.0f, 0.0f, 1.0f, |
| 0.0f, 0.5f, 0.0f, 1.0f, |
| 0.5f, 0.5f, 0.0f, 1.0f, |
| |
| 0.0f, 0.0f, 0.5f, 1.0f, |
| 1.0f, 0.0f, 0.0f, 1.0f, |
| 1.0f, 1.0f, 1.0f, 1.0f, |
| 0.0f, 1.0f, 0.0f, 1.0f |
| }; |
| |
| private float[] mTriangleVerticesData2 = new float[mTriangleVerticesData.length]; |
| private FloatBuffer mTriangleVertices; |
| |
| private final String mVertexShader = "attribute vec4 aPosition;\n" |
| + "attribute vec4 aColor;\n" |
| + "varying vec4 vColor;\n" |
| + "void main() {\n" |
| + " gl_Position = aPosition;\n" |
| + " vColor = aColor;\n" |
| + "}\n"; |
| |
| private final String mFragmentShader = "precision mediump float;\n" |
| + "varying vec4 vColor;\n" |
| + "void main() {\n" |
| + " gl_FragColor = vColor;\n" |
| + "}\n"; |
| |
| private int mProgram; |
| private int mvPositionHandle; |
| private int mvColorHandle; |
| |
| } |
| } |
| |