blob: d62bf17487a4fbe9888d3979360bd232b67f2768 [file] [log] [blame]
/*
* 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;
}
}