C++程序  |  240行  |  8.21 KB

/*
 * 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 <android/native_window.h>

#include <stdlib.h>

#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

#include "ContextSwitchRenderer.h"
#include <graphics/GLUtils.h>

#define LOG_TAG "CTS_OPENGL"
#define LOG_NDEBUG 0
#include <android/log.h>
#include <Trace.h>

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);
    }
}