diff options
author | Daniel Sandler <dsandler@android.com> | 2012-09-23 01:37:46 -0400 |
---|---|---|
committer | Daniel Sandler <dsandler@android.com> | 2012-09-24 11:39:54 -0400 |
commit | 0b4c919a23e239f516a0168a2e47e00cf228cd13 (patch) | |
tree | 6918fbcf1b25d5b9776942014b5bd1de8a26e5fc /src | |
parent | 74a589cd63d04369920bf7203d4d3262d06ab97b (diff) | |
download | android_packages_screensavers_Basic-0b4c919a23e239f516a0168a2e47e00cf228cd13.tar.gz android_packages_screensavers_Basic-0b4c919a23e239f516a0168a2e47e00cf228cd13.tar.bz2 android_packages_screensavers_Basic-0b4c919a23e239f516a0168a2e47e00cf228cd13.zip |
Convert Colors from GLSurfaceView to TextureView.
(At great personal cost.)
This fixes the super-janky startup for Colors.
Bug: 7171323
Change-Id: If9103eaccdb3202ff94bafea7874437d0f4ddcb4
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/dreams/basic/Colors.java | 436 |
1 files changed, 316 insertions, 120 deletions
diff --git a/src/com/android/dreams/basic/Colors.java b/src/com/android/dreams/basic/Colors.java index e4e22bd..021189f 100644 --- a/src/com/android/dreams/basic/Colors.java +++ b/src/com/android/dreams/basic/Colors.java @@ -16,53 +16,114 @@ package com.android.dreams.basic; -import android.animation.Animator; -import android.opengl.GLSurfaceView; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.TimeInterpolator; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Configuration; -import android.graphics.drawable.Drawable; import android.graphics.Color; -import android.graphics.PorterDuff; -import android.net.Uri; -import android.os.BatteryManager; -import android.os.Handler; -import android.provider.Settings; +import android.graphics.SurfaceTexture; import android.service.dreams.Dream; import android.util.Log; -import android.view.MotionEvent; -import android.view.View; -import android.view.WindowManager; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import android.widget.TextView; +import android.view.Choreographer; +import android.view.Choreographer.FrameCallback; +import android.view.TextureView; +import android.os.Looper; import android.os.SystemClock; +import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; - -import android.opengl.GLES20; -import android.opengl.GLSurfaceView; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; +import javax.microedition.khronos.opengles.GL; +import android.opengl.GLUtils; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; -public class Colors extends Dream { +import static android.opengl.GLES20.*; + +public class Colors extends Dream implements TextureView.SurfaceTextureListener { + static final String TAG = Colors.class.getSimpleName(); + static final boolean DEBUG = true; + public static final void LOG(String fmt, Object... args) { + if (!DEBUG) return; + Log.v(TAG, String.format(fmt, args)); + } + // It's so easy to use OpenGLES 2.0! - GLSurfaceView gl; + private EGL10 mEgl; + private EGLDisplay mEglDisplay; + private EGLConfig mEglConfig; + private EGLContext mEglContext; + private EGLSurface mEglSurface; + private SurfaceTexture mSurface; + private GL mGL; + + static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + static final int EGL_OPENGL_ES2_BIT = 4; + + volatile boolean mStop = false; + Square mSquare; + TextureView mTextureView; + + private long mLastFrameTime; + private int mFrameNum = 0; + + private FrameCallback mFrameCallback = new Choreographer.FrameCallback() { + @Override + public void doFrame(long frameTimeNanos) { + if (mStop) { + Looper.myLooper().quit(); + return; + } + + if (mSquare == null) { + initGL(); + + mSquare = new Square(); + glClearColor(1f, 0f, 0f, 1.0f); + + if (DEBUG) { + mLastFrameTime = frameTimeNanos; + } + } + + checkCurrent(); + + glViewport(0, 0, mWidth, mHeight); + + if (DEBUG) { + mFrameNum ++; + final long t2 = frameTimeNanos; + final long dt = t2-mLastFrameTime; + final int fps = (int) (1e9f/dt); + if (0 == (mFrameNum % 10)) { + LOG("frame %d fps=%d", mFrameNum, fps); + } + if (fps < 40) { + LOG("JANK! (%d ms)", dt); + } + mLastFrameTime = t2; + } + + glClear(GL_COLOR_BUFFER_BIT); + checkGlError(); + + mSquare.draw(); + + if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { + throw new RuntimeException("Cannot swap buffers"); + } + checkEglError(); + + mChoreographer.postFrameCallback(mFrameCallback); + } + }; + + private int mHeight; + + private int mWidth; + private Choreographer mChoreographer; class Square { // Straight from the API guide @@ -99,20 +160,20 @@ public class Colors extends Dream { 1f, 1f, 0f }; // top right private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices (CCW) - + private final float HUES[] = { // reverse order due to CCW winding 60, // yellow 120, // green 343, // red 200, // blue }; - + private final int vertexCount = squareCoords.length / COORDS_PER_VERTEX; private final int vertexStride = COORDS_PER_VERTEX * 4; // bytes per vertex private float cornerFrequencies[] = new float[vertexCount]; private int cornerRotation; - + final int COLOR_PLANES_PER_VERTEX = 4; private final int colorStride = COLOR_PLANES_PER_VERTEX * 4; // bytes per vertex @@ -121,7 +182,7 @@ public class Colors extends Dream { public Square() { for (int i=0; i<vertexCount; i++) { - cornerFrequencies[i] = 1f + (float)(Math.random() * 5); + cornerFrequencies[i] = 1f + (float)(Math.random() * 5); } cornerRotation = (int)(Math.random() * vertexCount); // initialize vertex byte buffer for shape coordinates @@ -132,7 +193,7 @@ public class Colors extends Dream { vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(squareCoords); vertexBuffer.position(0); - + bb = ByteBuffer.allocateDirect(vertexCount * colorStride); bb.order(ByteOrder.nativeOrder()); colorBuffer = bb.asFloatBuffer(); @@ -146,39 +207,36 @@ public class Colors extends Dream { drawListBuffer.put(drawOrder); drawListBuffer.position(0); - // prepare shaders and OpenGL program - int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, - vertexShaderCode); - int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, - fragmentShaderCode); - - mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program - GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program - GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program - GLES20.glLinkProgram(mProgram); // create OpenGL program executables - } + mProgram = buildProgram(vertexShaderCode, fragmentShaderCode); - final float[] _tmphsv = new float[3]; - public void draw() { // Add program to OpenGL environment - GLES20.glUseProgram(mProgram); + glUseProgram(mProgram); + checkGlError("glUseProgram(" + mProgram + ")"); // get handle to vertex shader's a_position member - mPositionHandle = GLES20.glGetAttribLocation(mProgram, "a_position"); + mPositionHandle = glGetAttribLocation(mProgram, "a_position"); + checkGlError("glGetAttribLocation(a_position)"); // Enable a handle to the triangle vertices - GLES20.glEnableVertexAttribArray(mPositionHandle); + glEnableVertexAttribArray(mPositionHandle); // Prepare the triangle coordinate data - GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, - GLES20.GL_FLOAT, false, - vertexStride, vertexBuffer); + glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, + GL_FLOAT, false, + vertexStride, vertexBuffer); + mColorHandle = glGetAttribLocation(mProgram, "a_color"); + checkGlError("glGetAttribLocation(a_color)"); + glEnableVertexAttribArray(mColorHandle); + checkGlError("glEnableVertexAttribArray"); + } + + final float[] _tmphsv = new float[3]; + public void draw() { // same thing for colors long now = SystemClock.uptimeMillis(); colorBuffer.clear(); - final float t = (float)now / 4000f; // set the base period to 4sec -// android.util.Slog.v("Colors", "t=" + t); + final float t = now / 4000f; // set the base period to 4sec for(int i=0; i<vertexCount; i++) { final float freq = (float) Math.sin(2 * Math.PI * t / cornerFrequencies[i]); _tmphsv[0] = HUES[(i + cornerRotation) % vertexCount]; @@ -191,102 +249,240 @@ public class Colors extends Dream { colorBuffer.put(/*a*/ 1f); } colorBuffer.position(0); - mColorHandle = GLES20.glGetAttribLocation(mProgram, "a_color"); - checkGlError("glGetAttribLocation"); - GLES20.glEnableVertexAttribArray(mColorHandle); - checkGlError("glEnableVertexAttribArray"); - GLES20.glVertexAttribPointer(mColorHandle, COLOR_PLANES_PER_VERTEX, - GLES20.GL_FLOAT, false, + glVertexAttribPointer(mColorHandle, COLOR_PLANES_PER_VERTEX, + GL_FLOAT, false, colorStride, colorBuffer); checkGlError("glVertexAttribPointer"); // Draw the triangle - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, vertexCount); + glDrawArrays(GL_TRIANGLE_FAN, 0, vertexCount); + } + } + + private static int buildProgram(String vertex, String fragment) { + int vertexShader = buildShader(vertex, GL_VERTEX_SHADER); + if (vertexShader == 0) return 0; + + int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER); + if (fragmentShader == 0) return 0; + + int program = glCreateProgram(); + glAttachShader(program, vertexShader); + checkGlError(); - // Disable vertex array - GLES20.glDisableVertexAttribArray(mPositionHandle); - GLES20.glDisableVertexAttribArray(mColorHandle); + glAttachShader(program, fragmentShader); + checkGlError(); + + glLinkProgram(program); + checkGlError(); + + int[] status = new int[1]; + glGetProgramiv(program, GL_LINK_STATUS, status, 0); + if (status[0] != GL_TRUE) { + String error = glGetProgramInfoLog(program); + Log.d(TAG, "Error while linking program:\n" + error); + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + glDeleteProgram(program); + return 0; } - public int loadShader(int type, String shaderCode){ + return program; + } - // create a vertex shader type (GLES20.GL_VERTEX_SHADER) - // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) - int shader = GLES20.glCreateShader(type); + private static int buildShader(String source, int type) { + int shader = glCreateShader(type); - // add the source code to the shader and compile it - GLES20.glShaderSource(shader, shaderCode); - GLES20.glCompileShader(shader); + glShaderSource(shader, source); + checkGlError(); - return shader; + glCompileShader(shader); + checkGlError(); + + int[] status = new int[1]; + glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0); + if (status[0] != GL_TRUE) { + String error = glGetShaderInfoLog(shader); + Log.d(TAG, "Error while compiling shader:\n" + error); + glDeleteShader(shader); + return 0; } - /** - * Utility method for debugging OpenGL calls. Provide the name of the call - * just after making it: - * - * <pre> - * mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); - * MyGLRenderer.checkGlError("glGetUniformLocation");</pre> - * - * If the operation is not successful, the check throws an error. - * - * @param glOperation - Name of the OpenGL call to check. - */ - public void checkGlError(String glOperation) { - int error; - while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { - Log.e("GL", glOperation + ": glError " + error); - throw new RuntimeException(String.format("%s: glError 0x%04x", glOperation, error)); + return shader; + } + @Override + public void onStart() { + super.onStart(); + } + + private void checkEglError() { + int error = mEgl.eglGetError(); + if (error != EGL10.EGL_SUCCESS) { + Log.w(TAG, "EGL error = 0x" + Integer.toHexString(error)); + } + } + + private static void checkGlError() { + checkGlError(""); + } + + private static void checkGlError(String what) { + int error = glGetError(); + if (error != GL_NO_ERROR) { + Log.w(TAG, "GL error: (" + what + ") = 0x" + Integer.toHexString(error)); + } + } + + private void finishGL() { + mEgl.eglDestroyContext(mEglDisplay, mEglContext); + mEgl.eglDestroySurface(mEglDisplay, mEglSurface); + } + + private void checkCurrent() { + if (!mEglContext.equals(mEgl.eglGetCurrentContext()) || + !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) { + if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { + throw new RuntimeException("eglMakeCurrent failed " + + GLUtils.getEGLErrorString(mEgl.eglGetError())); } } } - private class ColorsRenderer implements GLSurfaceView.Renderer { - public void onSurfaceCreated(GL10 unused, EGLConfig config) { - mSquare = new Square(); - GLES20.glClearColor(0f, 0f, 0f, 1.0f); + private void initGL() { + mEgl = (EGL10) EGLContext.getEGL(); + + mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { + throw new RuntimeException("eglGetDisplay failed " + + GLUtils.getEGLErrorString(mEgl.eglGetError())); + } + + int[] version = new int[2]; + if (!mEgl.eglInitialize(mEglDisplay, version)) { + throw new RuntimeException("eglInitialize failed " + + GLUtils.getEGLErrorString(mEgl.eglGetError())); + } + + mEglConfig = chooseEglConfig(); + if (mEglConfig == null) { + throw new RuntimeException("eglConfig not initialized"); } - public void onDrawFrame(GL10 unused) { - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + mEglContext = createContext(mEgl, mEglDisplay, mEglConfig); - mSquare.draw(); + mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null); + + if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { + int error = mEgl.eglGetError(); + if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { + Log.e(TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); + return; + } + throw new RuntimeException("createWindowSurface failed " + + GLUtils.getEGLErrorString(error)); } - public void onSurfaceChanged(GL10 unused, int width, int height) { - GLES20.glViewport(0, 0, width, height); + if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { + throw new RuntimeException("eglMakeCurrent failed " + + GLUtils.getEGLErrorString(mEgl.eglGetError())); } + + mGL = mEglContext.getGL(); } - @Override - public void onStart() { - super.onStart(); + + EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { + int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; + return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); + } + + private EGLConfig chooseEglConfig() { + int[] configsCount = new int[1]; + EGLConfig[] configs = new EGLConfig[1]; + int[] configSpec = getConfig(); + if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) { + throw new IllegalArgumentException("eglChooseConfig failed " + + GLUtils.getEGLErrorString(mEgl.eglGetError())); + } else if (configsCount[0] > 0) { + return configs[0]; + } + return null; } - class TheySaidIHadToHaveAGLSurfaceView extends GLSurfaceView { + private static int[] getConfig() { + return new int[] { + EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL10.EGL_RED_SIZE, 8, + EGL10.EGL_GREEN_SIZE, 8, + EGL10.EGL_BLUE_SIZE, 8, + EGL10.EGL_ALPHA_SIZE, 8, + EGL10.EGL_DEPTH_SIZE, 0, + EGL10.EGL_STENCIL_SIZE, 0, + EGL10.EGL_NONE + }; + } - public TheySaidIHadToHaveAGLSurfaceView(Context context){ - super(context); + @Override + public void onCreate() { + super.onCreate(); - setEGLContextClientVersion(2); + setInteractive(false); - setRenderer(new ColorsRenderer()); - } + mTextureView = new TextureView(this); + mTextureView.setSurfaceTextureListener(this); } @Override public void onAttachedToWindow() { - super.onAttachedToWindow(); - - setFullscreen(true); setInteractive(false); - - gl = new TheySaidIHadToHaveAGLSurfaceView(Colors.this); - gl.postDelayed(new Runnable() { + setLowProfile(true); + setFullscreen(true); + setContentView(mTextureView); + } + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + LOG("onSurfaceTextureAvailable(%s, %d, %d)", surface, width, height); + mSurface = surface; + + mWidth = width; + mHeight = height; + + new Thread() { + @Override public void run() { - Colors.this.setContentView(gl); + Looper.prepare(); + + mChoreographer = Choreographer.getInstance(); + mChoreographer.postFrameCallback(mFrameCallback); + + Looper.loop(); } - }, 1000); + }.start(); + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + LOG("onSurfaceTextureSizeChanged(%s, %d, %d)", surface, width, height); + mWidth = width; + mHeight = height; + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + LOG("onSurfaceTextureDestroyed(%s)", surface); + return false; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + LOG("onSurfaceTextureUpdated(%s)", surface); + } + + @Override + public void finish() { + mStop = true; + finishGL(); + super.finish(); } } |