/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ #include #include #include #include #include #include #include "ContextSwitchRenderer.h" #include #define LOG_TAG "CTS_OPENGL" #define LOG_NDEBUG 0 #include #include static const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; static const int NUM_WORKER_CONTEXTS = 7; static const int CS_TEXTURE_SIZE = 64; static const int CS_NUM_VERTICES = 6; static const float CS_VERTICES[CS_NUM_VERTICES * 3] = { 0.1f, 0.1f, -0.1f, -0.1f, 0.1f, -0.1f, -0.1f, -0.1f, -0.1f, -0.1f, -0.1f, -0.1f, 0.1f, -0.1f, -0.1f, 0.1f, 0.1f, -0.1f }; static const float CS_TEX_COORDS[CS_NUM_VERTICES * 2] = { 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f }; static const char* CS_VERTEX = "attribute vec4 a_Position;" "attribute vec2 a_TexCoord;" "uniform float u_Translate;" "varying vec2 v_TexCoord;" "void main() {" " v_TexCoord = a_TexCoord;" " gl_Position.x = a_Position.x + u_Translate;" " gl_Position.yzw = a_Position.yzw;" "}"; static const char* CS_FRAGMENT = "precision mediump float;" "uniform sampler2D u_Texture;" "varying vec2 v_TexCoord;" "void main() {" " gl_FragColor = texture2D(u_Texture, v_TexCoord);" "}"; ContextSwitchRenderer::ContextSwitchRenderer(ANativeWindow* window, bool offscreen) : Renderer(window, offscreen), mContexts(NULL), mWorkload(0) { } bool ContextSwitchRenderer::setUp(int workload) { SCOPED_TRACE(); mWorkload = workload; if (!Renderer::setUp(workload)) { return false; } // Setup texture. mTextureId = GLUtils::genTexture(CS_TEXTURE_SIZE, CS_TEXTURE_SIZE, GLUtils::RANDOM_FILL); if (mTextureId == 0) { return false; } // Create program. mProgramId = GLUtils::createProgram(&CS_VERTEX, &CS_FRAGMENT); if (mProgramId == 0) { return false; } // Bind attributes. mTextureUniformHandle = glGetUniformLocation(mProgramId, "u_Texture"); mTranslateUniformHandle = glGetUniformLocation(mProgramId, "u_Translate"); mPositionHandle = glGetAttribLocation(mProgramId, "a_Position"); mTexCoordHandle = glGetAttribLocation(mProgramId, "a_TexCoord"); mContexts = new EGLContext[NUM_WORKER_CONTEXTS]; mFboIds = new GLuint[NUM_WORKER_CONTEXTS]; for (int i = 0; i < NUM_WORKER_CONTEXTS; i++) { // Create the contexts, they share data with the main one. mContexts[i] = eglCreateContext(mEglDisplay, mGlConfig, mEglContext, contextAttribs); if (EGL_NO_CONTEXT == mContexts[i] || EGL_SUCCESS != eglGetError()) { return false; } if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mContexts[i]) || EGL_SUCCESS != eglGetError()) { return false; } if (mOffscreen) { // FBOs are not shared across contexts, textures and renderbuffers are though. glGenFramebuffers(1, &mFboIds[i]); glBindFramebuffer(GL_FRAMEBUFFER, mFboIds[i]); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mFboDepthId); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFboTexId, 0); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Framebuffer not complete: %d", status); return false; } } } return true; } bool ContextSwitchRenderer::tearDown() { SCOPED_TRACE(); if (mContexts) { // Destroy the contexts, the main one will be handled by Renderer::eglTearDown(). for (int i = 0; i < NUM_WORKER_CONTEXTS; i++) { if (mOffscreen) { if (mFboIds[i] != 0) { eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mContexts[i]); glDeleteFramebuffers(1, &mFboIds[i]); mFboIds[i] = 0; } } eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(mEglDisplay, mContexts[i]); } delete[] mContexts; } eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); if (mTextureId != 0) { glDeleteTextures(1, &mTextureId); mTextureId = 0; } if (!Renderer::tearDown()) { return false; } return true; } void ContextSwitchRenderer::drawWorkload() { SCOPED_TRACE(); if (mWorkload > 8) { return; // This test does not support higher workloads. } // Set the background clear color to black. glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // No culling of back faces glDisable(GL_CULL_FACE); // No depth testing glDisable(GL_DEPTH_TEST); EGLSyncKHR fence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, NULL); const int TOTAL_NUM_CONTEXTS = NUM_WORKER_CONTEXTS + 1; const float TRANSLATION = 0.9f - (TOTAL_NUM_CONTEXTS * 0.2f); for (int i = 0; i < TOTAL_NUM_CONTEXTS; i++) { eglWaitSyncKHR(mEglDisplay, fence, 0); eglDestroySyncKHR(mEglDisplay, fence); glUseProgram(mProgramId); // Set the texture. glActiveTexture (GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, mTextureId); glUniform1i(mTextureUniformHandle, 0); // Set the x translate. glUniform1f(mTranslateUniformHandle, (i * 0.2f) + TRANSLATION); glEnableVertexAttribArray(mPositionHandle); glEnableVertexAttribArray(mTexCoordHandle); glVertexAttribPointer(mPositionHandle, 3, GL_FLOAT, false, 0, CS_VERTICES); glVertexAttribPointer(mTexCoordHandle, 2, GL_FLOAT, false, 0, CS_TEX_COORDS); glDrawArrays(GL_TRIANGLES, 0, CS_NUM_VERTICES); fence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, NULL); // Switch to next context. if (i < (mWorkload - 1)) { eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mContexts[i]); // Switch to FBO and re-attach. if (mOffscreen) { glBindFramebuffer(GL_FRAMEBUFFER, mFboIds[i]); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mFboDepthId); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFboTexId, 0); glViewport(0, 0, mFboWidth, mFboHeight); } } GLuint err = glGetError(); if (err != GL_NO_ERROR) { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "GLError %d in drawWorkload", err); break; } } eglWaitSyncKHR(mEglDisplay, fence, 0); eglDestroySyncKHR(mEglDisplay, fence); // Switch back to the main context. eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); if (mOffscreen) { glBindFramebuffer(GL_FRAMEBUFFER, mFboId); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mFboDepthId); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFboTexId, 0); glViewport(0, 0, mFboWidth, mFboHeight); } }