C++程序  |  295行  |  9.8 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 "Renderer.h"
#include <graphics/GLUtils.h>

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

#include <Trace.h>

// Used to center the grid on the screen.
#define CENTER_GRID(x) ((((x) * 2.0 + 1) - OFFSCREEN_GRID_SIZE) / OFFSCREEN_GRID_SIZE)
// Leave a good error message if something fails.
#define EGL_RESULT_CHECK(X) do { \
                                   EGLint error = eglGetError(); \
                                   if (!(X) || error != EGL_SUCCESS) { \
                                       __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, \
                                          "EGL error '%d' at %s:%d", error, __FILE__, __LINE__);\
                                       return false; \
                                    } \
                            } while (0)

static const int FBO_NUM_VERTICES = 6;

static const float FBO_VERTICES[FBO_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 FBO_TEX_COORDS[FBO_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* FBO_VERTEX =
        "attribute vec4 a_Position;"
        "attribute vec2 a_TexCoord;"
        "uniform float u_XOffset;"
        "uniform float u_YOffset;"
        "varying vec2 v_TexCoord;"
        "void main() {"
        "  v_TexCoord = a_TexCoord;"
        "  gl_Position.x = a_Position.x + u_XOffset;"
        "  gl_Position.y = a_Position.y + u_YOffset;"
        "  gl_Position.zw = a_Position.zw;"
        "}";

static const char* FBO_FRAGMENT =
        "precision mediump float;"
        "uniform sampler2D u_Texture;"
        "varying vec2 v_TexCoord;"
        "void main() {"
        "  gl_FragColor = texture2D(u_Texture, v_TexCoord);"
        "}";

static const EGLint contextAttribs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE };

static const EGLint configAttribs[] = {
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 8,
        EGL_DEPTH_SIZE, 16,
        EGL_STENCIL_SIZE, 8,
        EGL_NONE };

static const int FBO_SIZE = 128;

Renderer::Renderer(EGLNativeWindowType window, bool offscreen) :
        mOffscreen(offscreen), mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE),
        mEglContext(EGL_NO_CONTEXT), mWindow(window)  {
}

bool Renderer::eglSetUp() {
    SCOPED_TRACE();
    mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    EGL_RESULT_CHECK(mEglDisplay != EGL_NO_DISPLAY);

    EGLint major;
    EGLint minor;
    EGL_RESULT_CHECK(eglInitialize(mEglDisplay, &major, &minor));

    EGLint numConfigs = 0;
    EGL_RESULT_CHECK(eglChooseConfig(mEglDisplay, configAttribs, &mGlConfig, 1, &numConfigs)
                     && (numConfigs > 0));

    mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, mWindow, NULL);
    EGL_RESULT_CHECK(mEglSurface != EGL_NO_SURFACE);

    mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, contextAttribs);
    EGL_RESULT_CHECK(mEglContext != EGL_NO_CONTEXT);

    EGL_RESULT_CHECK(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));
    EGL_RESULT_CHECK(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &mWidth));
    EGL_RESULT_CHECK(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &mHeight));

    return true;
}

void Renderer::eglTearDown() {
    SCOPED_TRACE();
    eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);

    if (mEglContext != EGL_NO_CONTEXT) {
        eglDestroyContext(mEglDisplay, mEglContext);
        mEglContext = EGL_NO_CONTEXT;
    }

    if (mEglSurface != EGL_NO_SURFACE) {
        mEglSurface = EGL_NO_SURFACE;
    }

    if (mEglDisplay != EGL_NO_DISPLAY) {
        eglTerminate(mEglDisplay);
        mEglDisplay = EGL_NO_DISPLAY;
    }
}

bool Renderer::setUp(int /*workload*/) {
    SCOPED_TRACE();

    EGL_RESULT_CHECK(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));

    if (mOffscreen) {
        mFboWidth = FBO_SIZE;
        mFboHeight = FBO_SIZE;

        glGenFramebuffers(1, &mFboId);
        glBindFramebuffer(GL_FRAMEBUFFER, mFboId);

        glGenRenderbuffers(1, &mFboDepthId);
        glBindRenderbuffer(GL_RENDERBUFFER, mFboDepthId);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, mFboWidth, mFboHeight);
        glBindRenderbuffer(GL_RENDERBUFFER, 0);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                                  GL_RENDERBUFFER, mFboDepthId);

        mFboTexId = GLUtils::genTexture(mFboWidth, mFboHeight, 0);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFboTexId, 0);

        GLuint err = glGetError();
        if (err != GL_NO_ERROR) {
            __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "GLError %d", err);
            return false;
        }

        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;
        }
        // Create fbo program.
        mFboProgId = GLUtils::createProgram(&FBO_VERTEX, &FBO_FRAGMENT);
        if (mFboProgId == 0) {
            return false;
        }
        // Bind attributes.
        mFboTexUniformHandle = glGetUniformLocation(mFboProgId, "u_Texture");
        mFboXOffsetUniformHandle = glGetUniformLocation(mFboProgId, "u_XOffset");
        mFboYOffsetUniformHandle = glGetUniformLocation(mFboProgId, "u_YOffset");
        mFboPositionHandle = glGetAttribLocation(mFboProgId, "a_Position");
        mFboTexCoordHandle = glGetAttribLocation(mFboProgId, "a_TexCoord");
    } else {
        mFboWidth = 0;
        mFboHeight = 0;
        mFboId = 0;
        mFboDepthId = 0;
        mFboTexId = 0;
    }

    GLuint err = glGetError();
    if (err != GL_NO_ERROR) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "GLError %d in setUp", err);
        return false;
    }

    return true;
}

bool Renderer::tearDown() {
    SCOPED_TRACE();
    if (mOffscreen) {
        if (mFboId != 0) {
            glDeleteFramebuffers(1, &mFboId);
            mFboId = 0;
        }
        if (mFboDepthId != 0) {
            glDeleteRenderbuffers(1, &mFboDepthId);
            mFboDepthId = 0;
        }
        if (mFboTexId != 0) {
            glDeleteTextures(1, &mFboTexId);
            mFboTexId = 0;
        }
    }
    GLuint err = glGetError();
    if (err != GL_NO_ERROR) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "GLError %d in tearDown", err);
        return false;
    }

    return true;
}

bool Renderer::draw() {
    SCOPED_TRACE();

    EGL_RESULT_CHECK(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glViewport(0, 0, mWidth, mHeight);

    if (mOffscreen) {
        // 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);
        for (int i = 0; i < OFFSCREEN_INNER_FRAMES; i++) {
            // Switch to FBO and re-attach.
            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);

            // Render workload.
            drawWorkload();
            glFlush();

            // Switch back to display.
            glBindFramebuffer(GL_FRAMEBUFFER, 0);
            glViewport(0, 0, mWidth, mHeight);

            // No culling of back faces
            glDisable (GL_CULL_FACE);
            // No depth testing
            glDisable (GL_DEPTH_TEST);
            // No blending
            glDisable (GL_BLEND);

            glUseProgram(mFboProgId);

            // Set the texture.
            glActiveTexture (GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, mFboTexId);
            glUniform1i(mFboTexUniformHandle, 0);

            // Set the offsets
            glUniform1f(mFboXOffsetUniformHandle, CENTER_GRID(i / OFFSCREEN_GRID_SIZE));
            glUniform1f(mFboYOffsetUniformHandle, CENTER_GRID(i % OFFSCREEN_GRID_SIZE));

            glEnableVertexAttribArray(mFboPositionHandle);
            glEnableVertexAttribArray(mFboTexCoordHandle);
            glVertexAttribPointer(mFboPositionHandle, 3, GL_FLOAT, false, 0, FBO_VERTICES);
            glVertexAttribPointer(mFboTexCoordHandle, 2, GL_FLOAT, false, 0, FBO_TEX_COORDS);

            // Render FBO to display.
            glDrawArrays(GL_TRIANGLES, 0, FBO_NUM_VERTICES);
        }
    } else {
        // Render workload.
        drawWorkload();
    }

    GLuint err = glGetError();
    if (err != GL_NO_ERROR) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "GLError %d in draw", err);
        return false;
    }

    EGL_RESULT_CHECK(eglSwapBuffers(mEglDisplay, mEglSurface)); 
    return true;
}