Home | History | Annotate | Download | only in basic
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.dreams.basic;
     18 
     19 import android.graphics.Color;
     20 import android.graphics.SurfaceTexture;
     21 import android.util.Log;
     22 import android.view.Choreographer;
     23 import android.os.SystemClock;
     24 
     25 import javax.microedition.khronos.egl.EGL10;
     26 import javax.microedition.khronos.egl.EGLConfig;
     27 import javax.microedition.khronos.egl.EGLContext;
     28 import javax.microedition.khronos.egl.EGLDisplay;
     29 import javax.microedition.khronos.egl.EGLSurface;
     30 
     31 import android.opengl.EGL14;
     32 import android.opengl.GLUtils;
     33 
     34 import java.nio.ByteBuffer;
     35 import java.nio.ByteOrder;
     36 import java.nio.FloatBuffer;
     37 import java.nio.ShortBuffer;
     38 
     39 import static android.opengl.GLES20.*;
     40 
     41 /**
     42  * The OpenGL renderer for the {@link Colors} dream.
     43  * <p>
     44  * This class is single-threaded.  Its methods must only be called on the
     45  * rendering thread.
     46  * </p>
     47  */
     48 final class ColorsGLRenderer implements Choreographer.FrameCallback {
     49     static final String TAG = ColorsGLRenderer.class.getSimpleName();
     50     static final boolean DEBUG = false;
     51 
     52     private static void LOG(String fmt, Object... args) {
     53         if (!DEBUG) return;
     54         Log.v(TAG, String.format(fmt, args));
     55     }
     56 
     57     private final SurfaceTexture mSurface;
     58     private int mWidth;
     59     private int mHeight;
     60 
     61     private final Choreographer mChoreographer;
     62 
     63     private Square mSquare;
     64     private long mLastFrameTime;
     65     private int mFrameNum = 0;
     66 
     67     // It's so easy to use OpenGLES 2.0!
     68     private EGL10 mEgl;
     69     private EGLDisplay mEglDisplay;
     70     private EGLContext mEglContext;
     71     private EGLSurface mEglSurface;
     72 
     73     public ColorsGLRenderer(SurfaceTexture surface, int width, int height) {
     74         mSurface = surface;
     75         mWidth = width;
     76         mHeight = height;
     77 
     78         mChoreographer = Choreographer.getInstance();
     79     }
     80 
     81     public void start() {
     82         initGL();
     83         mSquare = new Square();
     84 
     85         mFrameNum = 0;
     86         mChoreographer.postFrameCallback(this);
     87     }
     88 
     89     public void stop() {
     90         mChoreographer.removeFrameCallback(this);
     91 
     92         mSquare = null;
     93         finishGL();
     94     }
     95 
     96     public void setSize(int width, int height) {
     97         mWidth = width;
     98         mHeight = height;
     99     }
    100 
    101     @Override
    102     public void doFrame(long frameTimeNanos) {
    103         mFrameNum += 1;
    104 
    105         // Clear on first frame.
    106         if (mFrameNum == 1) {
    107             glClearColor(1f, 0f, 0f, 1.0f);
    108             if (DEBUG) {
    109                 mLastFrameTime = frameTimeNanos;
    110             }
    111         }
    112 
    113         // Draw new frame.
    114         checkCurrent();
    115 
    116         glViewport(0, 0, mWidth, mHeight);
    117 
    118         if (DEBUG) {
    119             final long dt = frameTimeNanos - mLastFrameTime;
    120             final int fps = (int) (1e9f / dt);
    121             if (0 == (mFrameNum % 10)) {
    122                 LOG("frame %d fps=%d", mFrameNum, fps);
    123             }
    124             if (fps < 40) {
    125                 LOG("JANK! (%d ms)", dt);
    126             }
    127             mLastFrameTime = frameTimeNanos;
    128         }
    129 
    130         glClear(GL_COLOR_BUFFER_BIT);
    131         checkGlError();
    132 
    133         mSquare.draw();
    134 
    135         if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
    136             throw new RuntimeException("Cannot swap buffers");
    137         }
    138         checkEglError();
    139 
    140         // Animate.  Post callback to run on next vsync.
    141         mChoreographer.postFrameCallback(this);
    142     }
    143 
    144     private void checkCurrent() {
    145         if (!mEglContext.equals(mEgl.eglGetCurrentContext()) ||
    146                 !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
    147             if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
    148                 throw new RuntimeException("eglMakeCurrent failed "
    149                         + GLUtils.getEGLErrorString(mEgl.eglGetError()));
    150             }
    151         }
    152     }
    153 
    154     private void initGL() {
    155         mEgl = (EGL10) EGLContext.getEGL();
    156 
    157         mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
    158         if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
    159             throw new RuntimeException("eglGetDisplay failed "
    160                     + GLUtils.getEGLErrorString(mEgl.eglGetError()));
    161         }
    162 
    163         int[] version = new int[2];
    164         if (!mEgl.eglInitialize(mEglDisplay, version)) {
    165             throw new RuntimeException("eglInitialize failed " +
    166                     GLUtils.getEGLErrorString(mEgl.eglGetError()));
    167         }
    168 
    169         EGLConfig eglConfig = chooseEglConfig();
    170         if (eglConfig == null) {
    171             throw new RuntimeException("eglConfig not initialized");
    172         }
    173 
    174         mEglContext = createContext(mEgl, mEglDisplay, eglConfig);
    175 
    176         mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, eglConfig, mSurface, null);
    177 
    178         if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
    179             int error = mEgl.eglGetError();
    180             if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
    181                 Log.e(TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
    182                 return;
    183             }
    184             throw new RuntimeException("createWindowSurface failed "
    185                     + GLUtils.getEGLErrorString(error));
    186         }
    187 
    188         if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
    189             throw new RuntimeException("eglMakeCurrent failed "
    190                     + GLUtils.getEGLErrorString(mEgl.eglGetError()));
    191         }
    192     }
    193 
    194     private void finishGL() {
    195         mEgl.eglDestroyContext(mEglDisplay, mEglContext);
    196         mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
    197     }
    198 
    199     private static EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
    200         int[] attrib_list = { EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
    201         return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
    202     }
    203 
    204     private EGLConfig chooseEglConfig() {
    205         int[] configsCount = new int[1];
    206         EGLConfig[] configs = new EGLConfig[1];
    207         int[] configSpec = getConfig();
    208         if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
    209             throw new IllegalArgumentException("eglChooseConfig failed " +
    210                     GLUtils.getEGLErrorString(mEgl.eglGetError()));
    211         } else if (configsCount[0] > 0) {
    212             return configs[0];
    213         }
    214         return null;
    215     }
    216 
    217     private static int[] getConfig() {
    218         return new int[] {
    219                 EGL10.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
    220                 EGL10.EGL_RED_SIZE, 8,
    221                 EGL10.EGL_GREEN_SIZE, 8,
    222                 EGL10.EGL_BLUE_SIZE, 8,
    223                 EGL10.EGL_ALPHA_SIZE, 0,
    224                 EGL10.EGL_DEPTH_SIZE, 0,
    225                 EGL10.EGL_STENCIL_SIZE, 0,
    226                 EGL10.EGL_NONE
    227         };
    228     }
    229 
    230     private static int buildProgram(String vertex, String fragment) {
    231         int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
    232         if (vertexShader == 0) return 0;
    233 
    234         int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
    235         if (fragmentShader == 0) return 0;
    236 
    237         int program = glCreateProgram();
    238         glAttachShader(program, vertexShader);
    239         checkGlError();
    240 
    241         glAttachShader(program, fragmentShader);
    242         checkGlError();
    243 
    244         glLinkProgram(program);
    245         checkGlError();
    246 
    247         int[] status = new int[1];
    248         glGetProgramiv(program, GL_LINK_STATUS, status, 0);
    249         if (status[0] != GL_TRUE) {
    250             String error = glGetProgramInfoLog(program);
    251             Log.d(TAG, "Error while linking program:\n" + error);
    252             glDeleteShader(vertexShader);
    253             glDeleteShader(fragmentShader);
    254             glDeleteProgram(program);
    255             return 0;
    256         }
    257 
    258         return program;
    259     }
    260 
    261     private static int buildShader(String source, int type) {
    262         int shader = glCreateShader(type);
    263 
    264         glShaderSource(shader, source);
    265         checkGlError();
    266 
    267         glCompileShader(shader);
    268         checkGlError();
    269 
    270         int[] status = new int[1];
    271         glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
    272         if (status[0] != GL_TRUE) {
    273             String error = glGetShaderInfoLog(shader);
    274             Log.d(TAG, "Error while compiling shader:\n" + error);
    275             glDeleteShader(shader);
    276             return 0;
    277         }
    278 
    279         return shader;
    280     }
    281 
    282     private void checkEglError() {
    283         int error = mEgl.eglGetError();
    284         if (error != EGL10.EGL_SUCCESS) {
    285             Log.w(TAG, "EGL error = 0x" + Integer.toHexString(error));
    286         }
    287     }
    288 
    289     private static void checkGlError() {
    290         checkGlError("");
    291     }
    292 
    293     private static void checkGlError(String what) {
    294         int error = glGetError();
    295         if (error != GL_NO_ERROR) {
    296             Log.w(TAG, "GL error: (" + what + ") = 0x" + Integer.toHexString(error));
    297         }
    298     }
    299 
    300     private final static class Square {
    301         // Straight from the API guide
    302         private final String vertexShaderCode =
    303             "attribute vec4 a_position;" +
    304             "attribute vec4 a_color;" +
    305             "varying vec4 v_color;" +
    306             "void main() {" +
    307             "  gl_Position = a_position;" +
    308             "  v_color = a_color;" +
    309             "}";
    310 
    311         private final String fragmentShaderCode =
    312             "precision mediump float;" +
    313             "varying vec4 v_color;" +
    314             "void main() {" +
    315             "  gl_FragColor = v_color;" +
    316             "}";
    317 
    318         private final FloatBuffer vertexBuffer;
    319         private final FloatBuffer colorBuffer;
    320         private final int mProgram;
    321         private int mPositionHandle;
    322         private int mColorHandle;
    323 
    324         private ShortBuffer drawListBuffer;
    325 
    326 
    327         // number of coordinates per vertex in this array
    328         final int COORDS_PER_VERTEX = 3;
    329         float squareCoords[] = { -1f,  1f, 0f,   // top left
    330                                  -1f, -1f, 0f,   // bottom left
    331                                   1f, -1f, 0f,   // bottom right
    332                                   1f,  1f, 0f }; // top right
    333 
    334         private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices (CCW)
    335 
    336         private final float HUES[] = { // reverse order due to CCW winding
    337                 60,  // yellow
    338                 120, // green
    339                 343, // red
    340                 200, // blue
    341         };
    342 
    343         private final int vertexCount = squareCoords.length / COORDS_PER_VERTEX;
    344         private final int vertexStride = COORDS_PER_VERTEX * 4; // bytes per vertex
    345 
    346         private float cornerFrequencies[] = new float[vertexCount];
    347         private int cornerRotation;
    348 
    349         final int COLOR_PLANES_PER_VERTEX = 4;
    350         private final int colorStride = COLOR_PLANES_PER_VERTEX * 4; // bytes per vertex
    351 
    352         // Set color with red, green, blue and alpha (opacity) values
    353         // float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
    354 
    355         public Square() {
    356             for (int i=0; i<vertexCount; i++) {
    357                 cornerFrequencies[i] = 1f + (float)(Math.random() * 5);
    358             }
    359             cornerRotation = (int)(Math.random() * vertexCount);
    360             // initialize vertex byte buffer for shape coordinates
    361             ByteBuffer bb = ByteBuffer.allocateDirect(
    362             // (# of coordinate values * 4 bytes per float)
    363                     squareCoords.length * 4);
    364             bb.order(ByteOrder.nativeOrder());
    365             vertexBuffer = bb.asFloatBuffer();
    366             vertexBuffer.put(squareCoords);
    367             vertexBuffer.position(0);
    368 
    369             bb = ByteBuffer.allocateDirect(vertexCount * colorStride);
    370             bb.order(ByteOrder.nativeOrder());
    371             colorBuffer = bb.asFloatBuffer();
    372 
    373             // initialize byte buffer for the draw list
    374             ByteBuffer dlb = ByteBuffer.allocateDirect(
    375             // (# of coordinate values * 2 bytes per short)
    376                     drawOrder.length * 2);
    377             dlb.order(ByteOrder.nativeOrder());
    378             drawListBuffer = dlb.asShortBuffer();
    379             drawListBuffer.put(drawOrder);
    380             drawListBuffer.position(0);
    381 
    382             mProgram = buildProgram(vertexShaderCode, fragmentShaderCode);
    383 
    384             // Add program to OpenGL environment
    385             glUseProgram(mProgram);
    386             checkGlError("glUseProgram(" + mProgram + ")");
    387 
    388             // get handle to vertex shader's a_position member
    389             mPositionHandle = glGetAttribLocation(mProgram, "a_position");
    390             checkGlError("glGetAttribLocation(a_position)");
    391 
    392             // Enable a handle to the triangle vertices
    393             glEnableVertexAttribArray(mPositionHandle);
    394 
    395             // Prepare the triangle coordinate data
    396             glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
    397                     GL_FLOAT, false,
    398                     vertexStride, vertexBuffer);
    399 
    400             mColorHandle = glGetAttribLocation(mProgram, "a_color");
    401             checkGlError("glGetAttribLocation(a_color)");
    402             glEnableVertexAttribArray(mColorHandle);
    403             checkGlError("glEnableVertexAttribArray");
    404         }
    405 
    406         final float[] _tmphsv = new float[3];
    407         public void draw() {
    408             // same thing for colors
    409             long now = SystemClock.uptimeMillis();
    410             colorBuffer.clear();
    411             final float t = now / 4000f; // set the base period to 4sec
    412             for(int i=0; i<vertexCount; i++) {
    413                 final float freq = (float) Math.sin(2 * Math.PI * t / cornerFrequencies[i]);
    414                 _tmphsv[0] = HUES[(i + cornerRotation) % vertexCount];
    415                 _tmphsv[1] = 1f;
    416                 _tmphsv[2] = freq * 0.25f + 0.75f;
    417                 final int c = Color.HSVToColor(_tmphsv);
    418                 colorBuffer.put((float)((c & 0xFF0000) >> 16) / 0xFF);
    419                 colorBuffer.put((float)((c & 0x00FF00) >> 8) / 0xFF);
    420                 colorBuffer.put((float)(c & 0x0000FF) / 0xFF);
    421                 colorBuffer.put(/*a*/ 1f);
    422             }
    423             colorBuffer.position(0);
    424             glVertexAttribPointer(mColorHandle, COLOR_PLANES_PER_VERTEX,
    425                     GL_FLOAT, false,
    426                     colorStride, colorBuffer);
    427             checkGlError("glVertexAttribPointer");
    428 
    429             // Draw the triangle
    430             glDrawArrays(GL_TRIANGLE_FAN, 0, vertexCount);
    431         }
    432     }
    433 }
    434