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 
15 #include <android/native_window.h>
16 
17 #include <stdlib.h>
18 
19 #include <EGL/egl.h>
20 #include <EGL/eglext.h>
21 #include <GLES2/gl2.h>
22 #include <GLES2/gl2ext.h>
23 
24 #include "ContextSwitchRenderer.h"
25 #include <graphics/GLUtils.h>
26 
27 #define LOG_TAG "CTS_OPENGL"
28 #define LOG_NDEBUG 0
29 #include <android/log.h>
30 #include <Trace.h>
31 
32 static const EGLint contextAttribs[] = {
33         EGL_CONTEXT_CLIENT_VERSION, 2,
34         EGL_NONE };
35 
36 static const int NUM_WORKER_CONTEXTS = 7;
37 
38 static const int CS_TEXTURE_SIZE = 64;
39 
40 static const int CS_NUM_VERTICES = 6;
41 
42 static const float CS_VERTICES[CS_NUM_VERTICES * 3] = {
43         0.1f, 0.1f, -0.1f,
44         -0.1f, 0.1f, -0.1f,
45         -0.1f, -0.1f, -0.1f,
46         -0.1f, -0.1f, -0.1f,
47         0.1f, -0.1f, -0.1f,
48         0.1f, 0.1f, -0.1f };
49 
50 static const float CS_TEX_COORDS[CS_NUM_VERTICES * 2] = {
51         1.0f, 1.0f,
52         0.0f, 1.0f,
53         0.0f, 0.0f,
54         0.0f, 0.0f,
55         1.0f, 0.0f,
56         1.0f, 1.0f };
57 
58 static const char* CS_VERTEX =
59         "attribute vec4 a_Position;"
60         "attribute vec2 a_TexCoord;"
61         "uniform float u_Translate;"
62         "varying vec2 v_TexCoord;"
63         "void main() {"
64         "  v_TexCoord = a_TexCoord;"
65         "  gl_Position.x = a_Position.x + u_Translate;"
66         "  gl_Position.yzw = a_Position.yzw;"
67         "}";
68 
69 static const char* CS_FRAGMENT =
70         "precision mediump float;"
71         "uniform sampler2D u_Texture;"
72         "varying vec2 v_TexCoord;"
73         "void main() {"
74         "  gl_FragColor = texture2D(u_Texture, v_TexCoord);"
75         "}";
76 
ContextSwitchRenderer(ANativeWindow * window,bool offscreen)77 ContextSwitchRenderer::ContextSwitchRenderer(ANativeWindow* window, bool offscreen) :
78         Renderer(window, offscreen), mContexts(NULL), mWorkload(0) {
79 }
80 
setUp(int workload)81 bool ContextSwitchRenderer::setUp(int workload) {
82     SCOPED_TRACE();
83     mWorkload = workload;
84     if (!Renderer::setUp(workload)) {
85         return false;
86     }
87 
88     // Setup texture.
89     mTextureId = GLUtils::genTexture(CS_TEXTURE_SIZE, CS_TEXTURE_SIZE, GLUtils::RANDOM_FILL);
90     if (mTextureId == 0) {
91         return false;
92     }
93 
94     // Create program.
95     mProgramId = GLUtils::createProgram(&CS_VERTEX, &CS_FRAGMENT);
96     if (mProgramId == 0) {
97         return false;
98     }
99     // Bind attributes.
100     mTextureUniformHandle = glGetUniformLocation(mProgramId, "u_Texture");
101     mTranslateUniformHandle = glGetUniformLocation(mProgramId, "u_Translate");
102     mPositionHandle = glGetAttribLocation(mProgramId, "a_Position");
103     mTexCoordHandle = glGetAttribLocation(mProgramId, "a_TexCoord");
104 
105     mContexts = new EGLContext[NUM_WORKER_CONTEXTS];
106     mFboIds = new GLuint[NUM_WORKER_CONTEXTS];
107     for (int i = 0; i < NUM_WORKER_CONTEXTS; i++) {
108         // Create the contexts, they share data with the main one.
109         mContexts[i] = eglCreateContext(mEglDisplay, mGlConfig, mEglContext, contextAttribs);
110         if (EGL_NO_CONTEXT == mContexts[i] || EGL_SUCCESS != eglGetError()) {
111             return false;
112         }
113 
114         if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mContexts[i])
115                 || EGL_SUCCESS != eglGetError()) {
116             return false;
117         }
118         if (mOffscreen) {
119             // FBOs are not shared across contexts, textures and renderbuffers are though.
120             glGenFramebuffers(1, &mFboIds[i]);
121             glBindFramebuffer(GL_FRAMEBUFFER, mFboIds[i]);
122 
123             glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
124                                       GL_RENDERBUFFER, mFboDepthId);
125 
126             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
127                                    GL_TEXTURE_2D, mFboTexId, 0);
128             GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
129             if (status != GL_FRAMEBUFFER_COMPLETE) {
130                 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Framebuffer not complete: %d", status);
131                 return false;
132             }
133         }
134     }
135     return true;
136 }
137 
tearDown()138 bool ContextSwitchRenderer::tearDown() {
139     SCOPED_TRACE();
140     if (mContexts) {
141         // Destroy the contexts, the main one will be handled by Renderer::eglTearDown().
142         for (int i = 0; i < NUM_WORKER_CONTEXTS; i++) {
143             if (mOffscreen) {
144                 if (mFboIds[i] != 0) {
145                     eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mContexts[i]);
146                     glDeleteFramebuffers(1, &mFboIds[i]);
147                     mFboIds[i] = 0;
148                 }
149             }
150             eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
151             eglDestroyContext(mEglDisplay, mContexts[i]);
152         }
153         delete[] mContexts;
154     }
155     eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
156     if (mTextureId != 0) {
157         glDeleteTextures(1, &mTextureId);
158         mTextureId = 0;
159     }
160     if (!Renderer::tearDown()) {
161         return false;
162     }
163     return true;
164 }
165 
drawWorkload()166 void ContextSwitchRenderer::drawWorkload() {
167     SCOPED_TRACE();
168 
169     if (mWorkload > 8) {
170         return; // This test does not support higher workloads.
171     }
172 
173     // Set the background clear color to black.
174     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
175     glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
176     // No culling of back faces
177     glDisable(GL_CULL_FACE);
178     // No depth testing
179     glDisable(GL_DEPTH_TEST);
180 
181     EGLSyncKHR fence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, NULL);
182 
183     const int TOTAL_NUM_CONTEXTS = NUM_WORKER_CONTEXTS + 1;
184     const float TRANSLATION = 0.9f - (TOTAL_NUM_CONTEXTS * 0.2f);
185     for (int i = 0; i < TOTAL_NUM_CONTEXTS; i++) {
186         eglWaitSyncKHR(mEglDisplay, fence, 0);
187         eglDestroySyncKHR(mEglDisplay, fence);
188         glUseProgram(mProgramId);
189 
190         // Set the texture.
191         glActiveTexture (GL_TEXTURE0);
192         glBindTexture(GL_TEXTURE_2D, mTextureId);
193         glUniform1i(mTextureUniformHandle, 0);
194 
195         // Set the x translate.
196         glUniform1f(mTranslateUniformHandle, (i * 0.2f) + TRANSLATION);
197 
198         glEnableVertexAttribArray(mPositionHandle);
199         glEnableVertexAttribArray(mTexCoordHandle);
200         glVertexAttribPointer(mPositionHandle, 3, GL_FLOAT, false, 0, CS_VERTICES);
201         glVertexAttribPointer(mTexCoordHandle, 2, GL_FLOAT, false, 0, CS_TEX_COORDS);
202 
203         glDrawArrays(GL_TRIANGLES, 0, CS_NUM_VERTICES);
204         fence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, NULL);
205 
206         // Switch to next context.
207         if (i < (mWorkload - 1)) {
208             eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mContexts[i]);
209             // Switch to FBO and re-attach.
210             if (mOffscreen) {
211                 glBindFramebuffer(GL_FRAMEBUFFER, mFboIds[i]);
212                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
213                                         GL_RENDERBUFFER, mFboDepthId);
214                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
215                                      GL_TEXTURE_2D, mFboTexId, 0);
216                 glViewport(0, 0, mFboWidth, mFboHeight);
217             }
218         }
219         GLuint err = glGetError();
220         if (err != GL_NO_ERROR) {
221             __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "GLError %d in drawWorkload", err);
222             break;
223         }
224     }
225 
226     eglWaitSyncKHR(mEglDisplay, fence, 0);
227     eglDestroySyncKHR(mEglDisplay, fence);
228 
229     // Switch back to the main context.
230     eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
231     if (mOffscreen) {
232         glBindFramebuffer(GL_FRAMEBUFFER, mFboId);
233         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
234                                 GL_RENDERBUFFER, mFboDepthId);
235         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
236                              GL_TEXTURE_2D, mFboTexId, 0);
237         glViewport(0, 0, mFboWidth, mFboHeight);
238     }
239 }
240