1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 #include "Renderer.h"
15 #include <graphics/GLUtils.h>
16 
17 #define LOG_TAG "CTS_OPENGL"
18 #define LOG_NDEBUG 0
19 #include <android/log.h>
20 
21 #include <Trace.h>
22 
23 // Used to center the grid on the screen.
24 #define CENTER_GRID(x) ((((x) * 2.0 + 1) - OFFSCREEN_GRID_SIZE) / OFFSCREEN_GRID_SIZE)
25 // Leave a good error message if something fails.
26 #define EGL_RESULT_CHECK(X) do { \
27                                    EGLint error = eglGetError(); \
28                                    if (!(X) || error != EGL_SUCCESS) { \
29                                        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, \
30                                           "EGL error '%d' at %s:%d", error, __FILE__, __LINE__);\
31                                        return false; \
32                                     } \
33                             } while (0)
34 
35 static const int FBO_NUM_VERTICES = 6;
36 
37 static const float FBO_VERTICES[FBO_NUM_VERTICES * 3] = {
38         0.1f, 0.1f, -0.1f,
39         -0.1f, 0.1f, -0.1f,
40         -0.1f, -0.1f, -0.1f,
41         -0.1f, -0.1f, -0.1f,
42         0.1f, -0.1f, -0.1f,
43         0.1f, 0.1f, -0.1f };
44 static const float FBO_TEX_COORDS[FBO_NUM_VERTICES * 2] = {
45         1.0f, 1.0f,
46         0.0f, 1.0f,
47         0.0f, 0.0f,
48         0.0f, 0.0f,
49         1.0f, 0.0f,
50         1.0f, 1.0f };
51 
52 static const char* FBO_VERTEX =
53         "attribute vec4 a_Position;"
54         "attribute vec2 a_TexCoord;"
55         "uniform float u_XOffset;"
56         "uniform float u_YOffset;"
57         "varying vec2 v_TexCoord;"
58         "void main() {"
59         "  v_TexCoord = a_TexCoord;"
60         "  gl_Position.x = a_Position.x + u_XOffset;"
61         "  gl_Position.y = a_Position.y + u_YOffset;"
62         "  gl_Position.zw = a_Position.zw;"
63         "}";
64 
65 static const char* FBO_FRAGMENT =
66         "precision mediump float;"
67         "uniform sampler2D u_Texture;"
68         "varying vec2 v_TexCoord;"
69         "void main() {"
70         "  gl_FragColor = texture2D(u_Texture, v_TexCoord);"
71         "}";
72 
73 static const EGLint contextAttribs[] = {
74         EGL_CONTEXT_CLIENT_VERSION, 2,
75         EGL_NONE };
76 
77 static const EGLint configAttribs[] = {
78         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
79         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
80         EGL_RED_SIZE, 8,
81         EGL_GREEN_SIZE, 8,
82         EGL_BLUE_SIZE, 8,
83         EGL_ALPHA_SIZE, 8,
84         EGL_DEPTH_SIZE, 16,
85         EGL_STENCIL_SIZE, 8,
86         EGL_NONE };
87 
88 static const int FBO_SIZE = 128;
89 
Renderer(EGLNativeWindowType window,bool offscreen)90 Renderer::Renderer(EGLNativeWindowType window, bool offscreen) :
91         mOffscreen(offscreen), mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE),
92         mEglContext(EGL_NO_CONTEXT), mWindow(window)  {
93 }
94 
eglSetUp()95 bool Renderer::eglSetUp() {
96     SCOPED_TRACE();
97     mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
98     EGL_RESULT_CHECK(mEglDisplay != EGL_NO_DISPLAY);
99 
100     EGLint major;
101     EGLint minor;
102     EGL_RESULT_CHECK(eglInitialize(mEglDisplay, &major, &minor));
103 
104     EGLint numConfigs = 0;
105     EGL_RESULT_CHECK(eglChooseConfig(mEglDisplay, configAttribs, &mGlConfig, 1, &numConfigs)
106                      && (numConfigs > 0));
107 
108     mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, mWindow, NULL);
109     EGL_RESULT_CHECK(mEglSurface != EGL_NO_SURFACE);
110 
111     mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, contextAttribs);
112     EGL_RESULT_CHECK(mEglContext != EGL_NO_CONTEXT);
113 
114     EGL_RESULT_CHECK(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));
115     EGL_RESULT_CHECK(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &mWidth));
116     EGL_RESULT_CHECK(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &mHeight));
117 
118     return true;
119 }
120 
eglTearDown()121 void Renderer::eglTearDown() {
122     SCOPED_TRACE();
123     eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
124 
125     if (mEglContext != EGL_NO_CONTEXT) {
126         eglDestroyContext(mEglDisplay, mEglContext);
127         mEglContext = EGL_NO_CONTEXT;
128     }
129 
130     if (mEglSurface != EGL_NO_SURFACE) {
131         mEglSurface = EGL_NO_SURFACE;
132     }
133 
134     if (mEglDisplay != EGL_NO_DISPLAY) {
135         eglTerminate(mEglDisplay);
136         mEglDisplay = EGL_NO_DISPLAY;
137     }
138 }
139 
setUp(int)140 bool Renderer::setUp(int /*workload*/) {
141     SCOPED_TRACE();
142 
143     EGL_RESULT_CHECK(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));
144 
145     if (mOffscreen) {
146         mFboWidth = FBO_SIZE;
147         mFboHeight = FBO_SIZE;
148 
149         glGenFramebuffers(1, &mFboId);
150         glBindFramebuffer(GL_FRAMEBUFFER, mFboId);
151 
152         glGenRenderbuffers(1, &mFboDepthId);
153         glBindRenderbuffer(GL_RENDERBUFFER, mFboDepthId);
154         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, mFboWidth, mFboHeight);
155         glBindRenderbuffer(GL_RENDERBUFFER, 0);
156         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
157                                   GL_RENDERBUFFER, mFboDepthId);
158 
159         mFboTexId = GLUtils::genTexture(mFboWidth, mFboHeight, 0);
160         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFboTexId, 0);
161 
162         GLuint err = glGetError();
163         if (err != GL_NO_ERROR) {
164             __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "GLError %d", err);
165             return false;
166         }
167 
168         GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
169         if (status != GL_FRAMEBUFFER_COMPLETE) {
170            __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Framebuffer not complete: %d", status);
171             return false;
172         }
173         // Create fbo program.
174         mFboProgId = GLUtils::createProgram(&FBO_VERTEX, &FBO_FRAGMENT);
175         if (mFboProgId == 0) {
176             return false;
177         }
178         // Bind attributes.
179         mFboTexUniformHandle = glGetUniformLocation(mFboProgId, "u_Texture");
180         mFboXOffsetUniformHandle = glGetUniformLocation(mFboProgId, "u_XOffset");
181         mFboYOffsetUniformHandle = glGetUniformLocation(mFboProgId, "u_YOffset");
182         mFboPositionHandle = glGetAttribLocation(mFboProgId, "a_Position");
183         mFboTexCoordHandle = glGetAttribLocation(mFboProgId, "a_TexCoord");
184     } else {
185         mFboWidth = 0;
186         mFboHeight = 0;
187         mFboId = 0;
188         mFboDepthId = 0;
189         mFboTexId = 0;
190     }
191 
192     GLuint err = glGetError();
193     if (err != GL_NO_ERROR) {
194         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "GLError %d in setUp", err);
195         return false;
196     }
197 
198     return true;
199 }
200 
tearDown()201 bool Renderer::tearDown() {
202     SCOPED_TRACE();
203     if (mOffscreen) {
204         if (mFboId != 0) {
205             glDeleteFramebuffers(1, &mFboId);
206             mFboId = 0;
207         }
208         if (mFboDepthId != 0) {
209             glDeleteRenderbuffers(1, &mFboDepthId);
210             mFboDepthId = 0;
211         }
212         if (mFboTexId != 0) {
213             glDeleteTextures(1, &mFboTexId);
214             mFboTexId = 0;
215         }
216     }
217     GLuint err = glGetError();
218     if (err != GL_NO_ERROR) {
219         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "GLError %d in tearDown", err);
220         return false;
221     }
222 
223     return true;
224 }
225 
draw()226 bool Renderer::draw() {
227     SCOPED_TRACE();
228 
229     EGL_RESULT_CHECK(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));
230 
231     glBindFramebuffer(GL_FRAMEBUFFER, 0);
232     glViewport(0, 0, mWidth, mHeight);
233 
234     if (mOffscreen) {
235         // Set the background clear color to black.
236         glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
237         glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
238         for (int i = 0; i < OFFSCREEN_INNER_FRAMES; i++) {
239             // Switch to FBO and re-attach.
240             glBindFramebuffer(GL_FRAMEBUFFER, mFboId);
241             glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
242                                     GL_RENDERBUFFER, mFboDepthId);
243             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
244                                  GL_TEXTURE_2D, mFboTexId, 0);
245             glViewport(0, 0, mFboWidth, mFboHeight);
246 
247             // Render workload.
248             drawWorkload();
249             glFlush();
250 
251             // Switch back to display.
252             glBindFramebuffer(GL_FRAMEBUFFER, 0);
253             glViewport(0, 0, mWidth, mHeight);
254 
255             // No culling of back faces
256             glDisable (GL_CULL_FACE);
257             // No depth testing
258             glDisable (GL_DEPTH_TEST);
259             // No blending
260             glDisable (GL_BLEND);
261 
262             glUseProgram(mFboProgId);
263 
264             // Set the texture.
265             glActiveTexture (GL_TEXTURE0);
266             glBindTexture(GL_TEXTURE_2D, mFboTexId);
267             glUniform1i(mFboTexUniformHandle, 0);
268 
269             // Set the offsets
270             glUniform1f(mFboXOffsetUniformHandle, CENTER_GRID(i / OFFSCREEN_GRID_SIZE));
271             glUniform1f(mFboYOffsetUniformHandle, CENTER_GRID(i % OFFSCREEN_GRID_SIZE));
272 
273             glEnableVertexAttribArray(mFboPositionHandle);
274             glEnableVertexAttribArray(mFboTexCoordHandle);
275             glVertexAttribPointer(mFboPositionHandle, 3, GL_FLOAT, false, 0, FBO_VERTICES);
276             glVertexAttribPointer(mFboTexCoordHandle, 2, GL_FLOAT, false, 0, FBO_TEX_COORDS);
277 
278             // Render FBO to display.
279             glDrawArrays(GL_TRIANGLES, 0, FBO_NUM_VERTICES);
280         }
281     } else {
282         // Render workload.
283         drawWorkload();
284     }
285 
286     GLuint err = glGetError();
287     if (err != GL_NO_ERROR) {
288         __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "GLError %d in draw", err);
289         return false;
290     }
291 
292     EGL_RESULT_CHECK(eglSwapBuffers(mEglDisplay, mEglSurface));
293     return true;
294 }
295