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