1 /*
2  * Copyright (C) 2015 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 package com.android.test.uibench.opengl;
17 
18 import android.content.res.Resources;
19 import android.graphics.Bitmap;
20 import android.graphics.BitmapFactory;
21 import android.graphics.SurfaceTexture;
22 import android.opengl.GLUtils;
23 import android.util.Log;
24 
25 import com.android.test.uibench.R;
26 
27 import java.nio.ByteBuffer;
28 import java.nio.ByteOrder;
29 import java.nio.FloatBuffer;
30 
31 import javax.microedition.khronos.egl.EGL10;
32 import javax.microedition.khronos.egl.EGLConfig;
33 import javax.microedition.khronos.egl.EGLContext;
34 import javax.microedition.khronos.egl.EGLDisplay;
35 import javax.microedition.khronos.egl.EGLSurface;
36 
37 import static android.opengl.GLES20.GL_CLAMP_TO_EDGE;
38 import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
39 import static android.opengl.GLES20.GL_COMPILE_STATUS;
40 import static android.opengl.GLES20.GL_FLOAT;
41 import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
42 import static android.opengl.GLES20.GL_LINEAR;
43 import static android.opengl.GLES20.GL_LINK_STATUS;
44 import static android.opengl.GLES20.GL_NO_ERROR;
45 import static android.opengl.GLES20.GL_RGBA;
46 import static android.opengl.GLES20.GL_TEXTURE0;
47 import static android.opengl.GLES20.GL_TEXTURE_2D;
48 import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER;
49 import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER;
50 import static android.opengl.GLES20.GL_TEXTURE_WRAP_S;
51 import static android.opengl.GLES20.GL_TEXTURE_WRAP_T;
52 import static android.opengl.GLES20.GL_TRIANGLE_STRIP;
53 import static android.opengl.GLES20.GL_TRUE;
54 import static android.opengl.GLES20.GL_UNSIGNED_BYTE;
55 import static android.opengl.GLES20.GL_VERTEX_SHADER;
56 import static android.opengl.GLES20.glActiveTexture;
57 import static android.opengl.GLES20.glAttachShader;
58 import static android.opengl.GLES20.glBindTexture;
59 import static android.opengl.GLES20.glClear;
60 import static android.opengl.GLES20.glClearColor;
61 import static android.opengl.GLES20.glCompileShader;
62 import static android.opengl.GLES20.glCreateProgram;
63 import static android.opengl.GLES20.glCreateShader;
64 import static android.opengl.GLES20.glDeleteProgram;
65 import static android.opengl.GLES20.glDeleteShader;
66 import static android.opengl.GLES20.glDrawArrays;
67 import static android.opengl.GLES20.glEnableVertexAttribArray;
68 import static android.opengl.GLES20.glGenTextures;
69 import static android.opengl.GLES20.glGetAttribLocation;
70 import static android.opengl.GLES20.glGetError;
71 import static android.opengl.GLES20.glGetProgramInfoLog;
72 import static android.opengl.GLES20.glGetProgramiv;
73 import static android.opengl.GLES20.glGetShaderInfoLog;
74 import static android.opengl.GLES20.glGetShaderiv;
75 import static android.opengl.GLES20.glGetUniformLocation;
76 import static android.opengl.GLES20.glLinkProgram;
77 import static android.opengl.GLES20.glShaderSource;
78 import static android.opengl.GLES20.glTexParameteri;
79 import static android.opengl.GLES20.glUniform1i;
80 import static android.opengl.GLES20.glUseProgram;
81 import static android.opengl.GLES20.glVertexAttribPointer;
82 
83 public class ImageFlipRenderThread extends Thread {
84     public static final String LOG_TAG = "GLTextureView";
85 
86     static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
87     static final int EGL_OPENGL_ES2_BIT = 4;
88 
89     private volatile boolean mFinished;
90 
91     private final Resources mResources;
92     private final SurfaceTexture mSurface;
93 
94     private EGL10 mEgl;
95     private EGLDisplay mEglDisplay;
96     private EGLConfig mEglConfig;
97     private EGLContext mEglContext;
98     private EGLSurface mEglSurface;
99 
ImageFlipRenderThread(Resources resources, SurfaceTexture surface)100     public ImageFlipRenderThread(Resources resources, SurfaceTexture surface) {
101         mResources = resources;
102         mSurface = surface;
103     }
104 
105     private static final String sSimpleVS =
106             "attribute vec4 position;\n" +
107                     "attribute vec2 texCoords;\n" +
108                     "varying vec2 outTexCoords;\n" +
109                     "\nvoid main(void) {\n" +
110                     "    outTexCoords = texCoords;\n" +
111                     "    gl_Position = position;\n" +
112                     "}\n\n";
113     private static final String sSimpleFS =
114             "precision mediump float;\n\n" +
115                     "varying vec2 outTexCoords;\n" +
116                     "uniform sampler2D texture;\n" +
117                     "\nvoid main(void) {\n" +
118                     "    gl_FragColor = texture2D(texture, outTexCoords);\n" +
119                     "}\n\n";
120 
121     private static final int FLOAT_SIZE_BYTES = 4;
122     private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
123     private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
124     private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
125     private final float[] mTriangleVerticesData = {
126             // X, Y, Z, U, V
127             -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
128             1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
129             -1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
130             1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
131     };
132 
133     @Override
run()134     public void run() {
135         initGL();
136 
137         FloatBuffer triangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length
138                 * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
139         triangleVertices.put(mTriangleVerticesData).position(0);
140 
141         int texture = loadTexture(R.drawable.large_photo);
142         int program = buildProgram(sSimpleVS, sSimpleFS);
143 
144         int attribPosition = glGetAttribLocation(program, "position");
145         checkGlError();
146 
147         int attribTexCoords = glGetAttribLocation(program, "texCoords");
148         checkGlError();
149 
150         int uniformTexture = glGetUniformLocation(program, "texture");
151         checkGlError();
152 
153         glBindTexture(GL_TEXTURE_2D, texture);
154         checkGlError();
155 
156         glUseProgram(program);
157         checkGlError();
158 
159         glEnableVertexAttribArray(attribPosition);
160         checkGlError();
161 
162         glEnableVertexAttribArray(attribTexCoords);
163         checkGlError();
164 
165         glUniform1i(uniformTexture, 0);
166         checkGlError();
167 
168         // drawQuad
169         triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
170         glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
171                 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
172         checkGlError();
173 
174         triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
175         glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
176                 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
177         checkGlError();
178 
179         glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
180         checkGlError();
181 
182         while (!mFinished) {
183             checkCurrent();
184 
185             glClear(GL_COLOR_BUFFER_BIT);
186             checkGlError();
187 
188             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
189             checkGlError();
190 
191             if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
192                 throw new RuntimeException("Cannot swap buffers");
193             }
194             checkEglError();
195 
196             try {
197                 Thread.sleep(2000);
198             } catch (InterruptedException e) {
199                 // Ignore
200             }
201         }
202 
203         finishGL();
204     }
205 
loadTexture(int resource)206     private int loadTexture(int resource) {
207         int[] textures = new int[1];
208 
209         glActiveTexture(GL_TEXTURE0);
210         glGenTextures(1, textures, 0);
211         checkGlError();
212 
213         int texture = textures[0];
214         glBindTexture(GL_TEXTURE_2D, texture);
215         checkGlError();
216 
217         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
218         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
219 
220         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
221         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
222 
223         Bitmap bitmap = BitmapFactory.decodeResource(mResources, resource);
224 
225         GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
226         checkGlError();
227 
228         bitmap.recycle();
229 
230         return texture;
231     }
232 
buildProgram(String vertex, String fragment)233     private static int buildProgram(String vertex, String fragment) {
234         int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
235         if (vertexShader == 0) return 0;
236 
237         int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
238         if (fragmentShader == 0) return 0;
239 
240         int program = glCreateProgram();
241         glAttachShader(program, vertexShader);
242         checkGlError();
243 
244         glAttachShader(program, fragmentShader);
245         checkGlError();
246 
247         glLinkProgram(program);
248         checkGlError();
249 
250         int[] status = new int[1];
251         glGetProgramiv(program, GL_LINK_STATUS, status, 0);
252         if (status[0] != GL_TRUE) {
253             String error = glGetProgramInfoLog(program);
254             Log.d(LOG_TAG, "Error while linking program:\n" + error);
255             glDeleteShader(vertexShader);
256             glDeleteShader(fragmentShader);
257             glDeleteProgram(program);
258             return 0;
259         }
260 
261         return program;
262     }
263 
buildShader(String source, int type)264     private static int buildShader(String source, int type) {
265         int shader = glCreateShader(type);
266 
267         glShaderSource(shader, source);
268         checkGlError();
269 
270         glCompileShader(shader);
271         checkGlError();
272 
273         int[] status = new int[1];
274         glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
275         if (status[0] != GL_TRUE) {
276             String error = glGetShaderInfoLog(shader);
277             Log.d(LOG_TAG, "Error while compiling shader:\n" + error);
278             glDeleteShader(shader);
279             return 0;
280         }
281 
282         return shader;
283     }
284 
checkEglError()285     private void checkEglError() {
286         int error = mEgl.eglGetError();
287         if (error != EGL10.EGL_SUCCESS) {
288             Log.w(LOG_TAG, "EGL error = 0x" + Integer.toHexString(error));
289         }
290     }
291 
checkGlError()292     private static void checkGlError() {
293         int error = glGetError();
294         if (error != GL_NO_ERROR) {
295             Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
296         }
297     }
298 
finishGL()299     private void finishGL() {
300         mEgl.eglDestroyContext(mEglDisplay, mEglContext);
301         mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
302     }
303 
checkCurrent()304     private void checkCurrent() {
305         if (!mEglContext.equals(mEgl.eglGetCurrentContext()) ||
306                 !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
307             if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
308                 throw new RuntimeException("eglMakeCurrent failed "
309                         + GLUtils.getEGLErrorString(mEgl.eglGetError()));
310             }
311         }
312     }
313 
initGL()314     private void initGL() {
315         mEgl = (EGL10) EGLContext.getEGL();
316 
317         mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
318         if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
319             throw new RuntimeException("eglGetDisplay failed "
320                     + GLUtils.getEGLErrorString(mEgl.eglGetError()));
321         }
322 
323         int[] version = new int[2];
324         if (!mEgl.eglInitialize(mEglDisplay, version)) {
325             throw new RuntimeException("eglInitialize failed " +
326                     GLUtils.getEGLErrorString(mEgl.eglGetError()));
327         }
328 
329         mEglConfig = chooseEglConfig();
330         if (mEglConfig == null) {
331             throw new RuntimeException("eglConfig not initialized");
332         }
333 
334         mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
335 
336         mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null);
337 
338         if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
339             int error = mEgl.eglGetError();
340             if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
341                 Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
342                 return;
343             }
344             throw new RuntimeException("createWindowSurface failed "
345                     + GLUtils.getEGLErrorString(error));
346         }
347 
348         if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
349             throw new RuntimeException("eglMakeCurrent failed "
350                     + GLUtils.getEGLErrorString(mEgl.eglGetError()));
351         }
352     }
353 
354 
createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig)355     EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
356         int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
357         return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
358     }
359 
chooseEglConfig()360     private EGLConfig chooseEglConfig() {
361         int[] configsCount = new int[1];
362         EGLConfig[] configs = new EGLConfig[1];
363         int[] configSpec = getConfig();
364         if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
365             throw new IllegalArgumentException("eglChooseConfig failed " +
366                     GLUtils.getEGLErrorString(mEgl.eglGetError()));
367         } else if (configsCount[0] > 0) {
368             return configs[0];
369         }
370         return null;
371     }
372 
getConfig()373     private static int[] getConfig() {
374         return new int[]{
375                 EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
376                 EGL10.EGL_RED_SIZE, 8,
377                 EGL10.EGL_GREEN_SIZE, 8,
378                 EGL10.EGL_BLUE_SIZE, 8,
379                 EGL10.EGL_ALPHA_SIZE, 8,
380                 EGL10.EGL_DEPTH_SIZE, 0,
381                 EGL10.EGL_STENCIL_SIZE, 0,
382                 EGL10.EGL_NONE
383         };
384     }
385 
finish()386     public void finish() {
387         mFinished = true;
388     }
389 }
390