Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2011 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 android.openglperf.cts;
     18 
     19 import android.content.Context;
     20 import android.cts.util.WatchDog;
     21 import android.graphics.Bitmap;
     22 import android.graphics.BitmapFactory;
     23 import android.opengl.GLES20;
     24 import android.opengl.GLUtils;
     25 import android.opengl.Matrix;
     26 import android.util.Log;
     27 
     28 import java.io.IOException;
     29 import java.io.InputStream;
     30 import java.lang.System;
     31 import java.nio.FloatBuffer;
     32 import java.nio.ShortBuffer;
     33 
     34 import javax.microedition.khronos.egl.EGLConfig;
     35 import javax.microedition.khronos.opengles.GL10;
     36 
     37 /**
     38  * OpenGl renderer rendering given number of planets with different GL configuration.
     39  */
     40 public class PlanetsRenderer implements GLSurfaceViewCustom.Renderer {
     41 
     42     private static final String TAG = "PlanetsRenderer";
     43     // texture is from
     44     // http://en.wikipedia.org/wiki/File:Mercator_projection_SW.jpg
     45     private static final String TEXTURE_FILE = "world_512_512.jpg";
     46     private static final long EGL_SWAP_BUFFERS_WAIT_TIME_IN_NS = 100 * 1000 * 1000 * 1000L;
     47 
     48     private final Context mContext;
     49     private final PlanetsRenderingParam mParam;
     50     private final RenderCompletionListener mListener;
     51     private final WatchDog mWatchDog;
     52 
     53     private final Sphere[] mSpheres;
     54     private final int mNumSpheres;
     55     private final int mNumIndices;
     56     private final int mVboVertices[];
     57     private final int mVboIndices[];
     58 
     59     // configurations for sun and planets
     60     private static final int SPHERE_SLICES = 180;
     61     private static final float RADIUS_SUN = 0.4f;
     62     private static final float RADIUS_PLANET = 0.08f;
     63     private static final float RADIUS_ORBIT = 0.9f;
     64 
     65     private int mWidth;
     66     private int mHeight;
     67 
     68     private int mFrameCount = 0;
     69     private static final int FPS_DISPLAY_INTERVAL = 50;
     70     private long mLastFPSTime;
     71     private long mLastRenderingTime;
     72     // for total FPS measurement
     73     private long mRenderingStartTime;
     74     private long mMeasurementStartTime;
     75     private int[] mFrameInterval = null;
     76 
     77     private int mProgram; // shader program
     78     private int mMVPMatrixHandle;
     79     private float[] mMVPMatrix = new float[16];
     80     private float[] mMMatrix = new float[16];
     81     private float[] mVMatrix = new float[16];
     82     private float[] mProjMatrix = new float[16];
     83 
     84     private int mOffsetHandle;
     85     private static final float[] mDefaultOffset = { 0f, 0f, 0f, 1f };
     86     private int mPositionHandle;
     87     private int mTexCoord0Handle;
     88     private int mTextureHandle;
     89     private int mTextureId;
     90 
     91     /**
     92      * @param numSlices
     93      *            complexity of sphere used. A sphere will have (numSlices + 1)
     94      *            x (numSlices x 1) much of vertices
     95      * @param useVbo
     96      *            whether to use Vertex Buffer Object in rendering or not
     97      * @param framesToGo
     98      *            number of frames to render before calling completion to
     99      *            listener
    100      * @param listener
    101      */
    102     public PlanetsRenderer(Context context, PlanetsRenderingParam param,
    103             RenderCompletionListener listener, WatchDog watchDog) {
    104         resetTimer();
    105         mContext = context;
    106         mParam = param;
    107         mWatchDog = watchDog;
    108         mNumSpheres = mParam.mNumPlanets + 1; // 1 for sun
    109         mNumIndices = mNumSpheres * mParam.mNumIndicesPerVertex;
    110         mSpheres = new Sphere[mNumSpheres];
    111 
    112         if (mParam.mNumFrames > 0) {
    113             mFrameInterval = new int[mParam.mNumFrames];
    114         }
    115         printParams();
    116 
    117         // for big model, this construction phase takes time...
    118         mSpheres[0] = new Sphere(SPHERE_SLICES, 0f, 0f, 0f, RADIUS_SUN,
    119                 mParam.mNumIndicesPerVertex);
    120         for (int i = 1; i < mNumSpheres; i++) {
    121             mSpheres[i] = new Sphere(SPHERE_SLICES,
    122                     RADIUS_ORBIT * (float) Math.sin(((float) i) / (mNumSpheres - 1) * 2 * Math.PI),
    123                     RADIUS_ORBIT * (float) Math.cos(((float) i) / (mNumSpheres - 1) * 2 * Math.PI),
    124                     0f, RADIUS_PLANET, mParam.mNumIndicesPerVertex);
    125         }
    126         mVboVertices = new int[mNumSpheres];
    127         mVboIndices = new int[mNumIndices];
    128         mListener = listener;
    129         measureTime("construction");
    130     }
    131 
    132     @Override
    133     public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
    134         mProgram = createProgram(getVertexShader(), getFragmentShader());
    135         if (mProgram == 0) {
    136             // error, cannot proceed
    137             throw new IllegalStateException("createProgram failed");
    138         }
    139         mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
    140         mOffsetHandle = GLES20.glGetUniformLocation(mProgram, "uOffset");
    141         mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
    142         mTexCoord0Handle = GLES20.glGetAttribLocation(mProgram, "vTexCoord0");
    143         mTextureHandle = GLES20.glGetUniformLocation(mProgram, "sTexture");
    144 
    145         // Load the texture
    146         mTextureId = createTexture2D();
    147     }
    148 
    149     @Override
    150     public void onDrawFrame(GL10 glUnused) {
    151         mWatchDog.reset();
    152         long currentTime = System.currentTimeMillis();
    153         mFrameCount++;
    154         mLastRenderingTime = currentTime;
    155 
    156         float angle = 0.090f * ((int) (currentTime % 4000L));
    157         Matrix.setRotateM(mMMatrix, 0, angle, 0, 0, 1.0f);
    158         Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
    159         Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
    160 
    161         GLES20.glUseProgram(mProgram);
    162         GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    163         GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
    164 
    165         // Apply a ModelView Projection transformation
    166         GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
    167         GLES20.glUniform4f(mOffsetHandle, mDefaultOffset[0], mDefaultOffset[1],
    168                 mDefaultOffset[2], mDefaultOffset[3]);
    169 
    170         GLES20.glEnableVertexAttribArray(mPositionHandle);
    171         GLES20.glEnableVertexAttribArray(mTexCoord0Handle);
    172 
    173         // Bind the texture
    174         GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    175         GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
    176         // Set the sampler texture unit to 0
    177         GLES20.glUniform1i(mTextureHandle, 0);
    178 
    179         for (int i = 0; i < mNumSpheres; i++) {
    180             if (mParam.mUseVboForVertices) {
    181                 // generating VBOs for each sphere is not efficient way for drawing
    182                 // multiple spheres
    183                 // But this is done for testing performance with big VBO buffers.
    184                 // So please do not copy this code as it is.
    185                 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVboVertices[i]);
    186                 // Load the vertex position
    187                 GLES20.glVertexAttribPointer(mPositionHandle, 3,
    188                         GLES20.GL_FLOAT, false, mSpheres[i].getVeticesStride(),
    189                         0);
    190                 // Load the texture coordinate
    191                 GLES20.glVertexAttribPointer(mTexCoord0Handle, 3,
    192                         GLES20.GL_FLOAT, false, mSpheres[i].getVeticesStride(),
    193                         3 * Sphere.FLOAT_SIZE);
    194             } else {
    195                 // Load the vertex position
    196                 GLES20.glVertexAttribPointer(mPositionHandle, 3,
    197                         GLES20.GL_FLOAT, false, mSpheres[i].getVeticesStride(),
    198                         mSpheres[i].getVertices());
    199                 // Load the texture coordinate
    200                 GLES20.glVertexAttribPointer(mTexCoord0Handle, 3,
    201                         GLES20.GL_FLOAT, false, mSpheres[i].getVeticesStride(),
    202                         mSpheres[i].getVertices().duplicate().position(3));
    203             }
    204             int[] numIndices = mSpheres[i].getNumIndices();
    205             ShortBuffer[] indices = mSpheres[i].getIndices();
    206             if (mParam.mUseVboForIndices) {
    207                 int indexVboBase = i * mParam.mNumIndicesPerVertex;
    208                 for (int j = 0; j < numIndices.length; j++) {
    209                     GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER,
    210                             mVboIndices[indexVboBase + j]);
    211                     GLES20.glDrawElements(GLES20.GL_TRIANGLES,
    212                             numIndices[j], GLES20.GL_UNSIGNED_SHORT,
    213                             0);
    214                 }
    215             } else {
    216                 for (int j = 0; j < numIndices.length; j++) {
    217                     GLES20.glDrawElements(GLES20.GL_TRIANGLES,
    218                             numIndices[j], GLES20.GL_UNSIGNED_SHORT,
    219                             indices[j]);
    220                 }
    221             }
    222         }
    223     }
    224 
    225     @Override
    226     public void onEglSwapBuffers() {
    227         if (!OpenGlPerfNative.waitForEglCompletion(EGL_SWAP_BUFFERS_WAIT_TIME_IN_NS)) {
    228             Log.w(TAG, "time-out or error while waiting for eglSwapBuffers completion");
    229         }
    230         long currentTime = System.currentTimeMillis();
    231         if (mFrameCount == 0) {
    232             mRenderingStartTime = currentTime;
    233         }
    234         if (mFrameCount < mParam.mNumFrames) {
    235             mFrameInterval[mFrameCount] = (int)(currentTime - mLastRenderingTime);
    236         }
    237 
    238         if ((mFrameCount == mParam.mNumFrames) && (mParam.mNumFrames > 0)) {
    239             long timePassed = currentTime - mRenderingStartTime;
    240             float fps = ((float) mParam.mNumFrames) / ((float) timePassed) * 1000.0f;
    241             printGlInfos();
    242             printParams();
    243             int numTriangles = mNumSpheres * mSpheres[0].getTotalIndices() / 3;
    244             Log.i(TAG, "Final FPS " + fps + " Num triangles " + numTriangles + " start time " +
    245                     mRenderingStartTime + " finish time " + currentTime);
    246             if (mListener != null) {
    247                 mListener.onRenderCompletion(fps, numTriangles, mFrameInterval);
    248                 mFrameCount++; // to prevent entering here again
    249                 return;
    250             }
    251         }
    252     }
    253 
    254     @Override
    255     public void onSurfaceChanged(GL10 glUnused, int width, int height) {
    256         mWidth = width;
    257         mHeight = height;
    258         GLES20.glViewport(0, 0, width, height);
    259         float ratio = (float) width / height;
    260         Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
    261         Matrix.setLookAtM(mVMatrix, 0, 0, 3, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
    262 
    263         createVbo();
    264 
    265         // reset timer to remove delays added for FPS calculation.
    266         mLastFPSTime = System.currentTimeMillis();
    267         mRenderingStartTime = System.currentTimeMillis();
    268     }
    269 
    270     protected final String getVertexShader() {
    271         // simple shader with MVP matrix and text coord
    272         final String vShaderStr =
    273                   "uniform mat4 uMVPMatrix;                               \n"
    274                 + "uniform vec4 uOffset;                                  \n"
    275                 + "attribute vec4 vPosition;                              \n"
    276                 + "attribute vec2 vTexCoord0;                             \n"
    277                 + "varying vec2 vTexCoord;                                \n"
    278                 + "void main()                                            \n"
    279                 + "{                                                      \n"
    280                 + "   gl_Position = uMVPMatrix * (vPosition + uOffset);   \n"
    281                 + "   vTexCoord = vTexCoord0;                             \n"
    282                 + "}                                                      \n";
    283         return vShaderStr;
    284     }
    285 
    286     protected final String getFragmentShader() {
    287         // simple shader with one texture for color
    288         final String fShaderStr =
    289                   "precision mediump float;                          \n"
    290                 + "varying vec2 vTexCoord;                           \n"
    291                 + "uniform sampler2D sTexture;                       \n"
    292                 + "void main()                                       \n"
    293                 + "{                                                 \n"
    294                 + "  gl_FragColor = texture2D( sTexture, vTexCoord );\n"
    295                 + "}                                                 \n";
    296         return fShaderStr;
    297     }
    298 
    299     private int loadShader(int shaderType, String source) {
    300         int shader = GLES20.glCreateShader(shaderType);
    301         if (shader != 0) {
    302             GLES20.glShaderSource(shader, source);
    303             GLES20.glCompileShader(shader);
    304             int[] compiled = new int[1];
    305             GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
    306             if (compiled[0] == 0) {
    307                 Log.e(TAG, "Could not compile shader " + shaderType + ":");
    308                 Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
    309                 GLES20.glDeleteShader(shader);
    310                 shader = 0;
    311             }
    312         }
    313         return shader;
    314     }
    315 
    316     private int createProgram(String vertexSource, String fragmentSource) {
    317         int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
    318         if (vertexShader == 0) {
    319             return 0;
    320         }
    321 
    322         int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
    323         if (pixelShader == 0) {
    324             return 0;
    325         }
    326 
    327         int program = GLES20.glCreateProgram();
    328         if (program != 0) {
    329             GLES20.glAttachShader(program, vertexShader);
    330             checkGlError("glAttachShader");
    331             GLES20.glAttachShader(program, pixelShader);
    332             checkGlError("glAttachShader");
    333             GLES20.glLinkProgram(program);
    334             int[] linkStatus = new int[1];
    335             GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
    336             if (linkStatus[0] != GLES20.GL_TRUE) {
    337                 Log.e(TAG, "Could not link program: ");
    338                 Log.e(TAG, GLES20.glGetProgramInfoLog(program));
    339                 GLES20.glDeleteProgram(program);
    340                 program = 0;
    341             }
    342         }
    343         return program;
    344     }
    345 
    346     private int createTexture2D() {
    347         // Texture object handle
    348         int[] textureId = new int[1];
    349 
    350         InputStream in = null;
    351         try {
    352             in = mContext.getAssets().open(TEXTURE_FILE);
    353             Bitmap bitmap = BitmapFactory.decodeStream(in);
    354 
    355             // Generate a texture object
    356             GLES20.glGenTextures(1, textureId, 0);
    357 
    358             // Bind the texture object
    359             GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[0]);
    360             GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
    361 
    362             // Set the filtering mode
    363             GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
    364                     GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    365             GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
    366                     GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    367 
    368         } catch (IOException e) {
    369             throw new IllegalStateException("Couldn't load texture '" + TEXTURE_FILE
    370                     + "'", e);
    371         } finally {
    372             if (in != null)
    373                 try {
    374                     in.close();
    375                 } catch (IOException e) {
    376                 }
    377         }
    378 
    379         return textureId[0];
    380     }
    381 
    382     private void checkGlError(String op) {
    383         int error;
    384         while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
    385             Log.e(TAG, op + ": glError " + error);
    386             throw new IllegalStateException(op + ": glError " + error);
    387         }
    388     }
    389 
    390     private void createVbo() {
    391         resetTimer();
    392         if (mParam.mUseVboForVertices) {
    393             GLES20.glGenBuffers(mNumSpheres, mVboVertices, 0);
    394             checkGlError("glGenBuffers Vertex");
    395             for (int i = 0; i < mNumSpheres; i++) {
    396                 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVboVertices[i]);
    397                 checkGlError("glBindBuffer Vertex");
    398                 FloatBuffer vertices = mSpheres[i].getVertices();
    399                 GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertices.limit()
    400                         * Sphere.FLOAT_SIZE, vertices, GLES20.GL_STATIC_DRAW);
    401                 checkGlError("glBufferData Vertex");
    402             }
    403         }
    404         if (mParam.mUseVboForIndices) {
    405             GLES20.glGenBuffers(mNumIndices, mVboIndices, 0);
    406             checkGlError("glGenBuffers Index");
    407             for (int i = 0; i < mNumSpheres; i++) {
    408                 int[] numIndices = mSpheres[i].getNumIndices();
    409                 ShortBuffer[] indices = mSpheres[i].getIndices();
    410                 int indexVboBase = i * mParam.mNumIndicesPerVertex;
    411                 for (int j = 0; j < numIndices.length; j++) {
    412                     GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER,
    413                             mVboIndices[indexVboBase + j]);
    414                     GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER,
    415                             indices[j].limit() * Sphere.SHORT_SIZE, indices[j],
    416                             GLES20.GL_STATIC_DRAW);
    417                     checkGlError("glBufferData Index");
    418 
    419                 }
    420             }
    421         }
    422         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    423         GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
    424         measureTime("VBO creation");
    425     }
    426 
    427     private void resetTimer() {
    428         mMeasurementStartTime = System.currentTimeMillis();
    429     }
    430 
    431     private void measureTime(String description) {
    432         long currentTime = System.currentTimeMillis();
    433         float timePassedInSecs = (float) (currentTime - mMeasurementStartTime) / 1000f;
    434         Log.i(TAG, description + " time in secs: " + timePassedInSecs);
    435     }
    436 
    437     private void printGlInfos() {
    438         Log.i(TAG, "Vendor " + GLES20.glGetString(GLES20.GL_VENDOR));
    439         Log.i(TAG, "Version " + GLES20.glGetString(GLES20.GL_VERSION));
    440         Log.i(TAG, "Renderer " + GLES20.glGetString(GLES20.GL_RENDERER));
    441         Log.i(TAG, "Extensions " + GLES20.glGetString(GLES20.GL_EXTENSIONS));
    442     }
    443     private void printParams() {
    444         Log.i(TAG, "UseVboForVertex " + mParam.mUseVboForVertices);
    445         Log.i(TAG, "UseVboForIndex " + mParam.mUseVboForIndices);
    446         Log.i(TAG, "No Spheres " + mNumSpheres);
    447         Log.i(TAG, "No indices buffer per vertex " + mParam.mNumIndicesPerVertex);
    448     }
    449 }
    450