Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "RenderBase.h"
     18 #include "glError.h"
     19 
     20 #include <log/log.h>
     21 #include <ui/GraphicBuffer.h>
     22 
     23 // Eventually we shouldn't need this dependency, but for now the
     24 // graphics allocator interface isn't fully supported on all platforms
     25 // and this is our work around.
     26 using ::android::GraphicBuffer;
     27 
     28 
     29 // OpenGL state shared among all renderers
     30 EGLDisplay   RenderBase::sDisplay = EGL_NO_DISPLAY;
     31 EGLContext   RenderBase::sContext = EGL_NO_CONTEXT;
     32 EGLSurface   RenderBase::sDummySurface = EGL_NO_SURFACE;
     33 GLuint       RenderBase::sFrameBuffer = -1;
     34 GLuint       RenderBase::sColorBuffer = -1;
     35 GLuint       RenderBase::sDepthBuffer = -1;
     36 EGLImageKHR  RenderBase::sKHRimage = EGL_NO_IMAGE_KHR;
     37 unsigned     RenderBase::sWidth  = 0;
     38 unsigned     RenderBase::sHeight = 0;
     39 float        RenderBase::sAspectRatio = 0.0f;
     40 
     41 
     42 bool RenderBase::prepareGL() {
     43     // Just trivially return success if we're already prepared
     44     if (sDisplay != EGL_NO_DISPLAY) {
     45         return true;
     46     }
     47 
     48     // Hardcoded to RGBx output display
     49     const EGLint config_attribs[] = {
     50         // Tag                  Value
     51         EGL_RENDERABLE_TYPE,    EGL_OPENGL_ES2_BIT,
     52         EGL_RED_SIZE,           8,
     53         EGL_GREEN_SIZE,         8,
     54         EGL_BLUE_SIZE,          8,
     55         EGL_NONE
     56     };
     57 
     58     // Select OpenGL ES v 3
     59     const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
     60 
     61 
     62     // Set up our OpenGL ES context associated with the default display (though we won't be visible)
     63     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     64     if (display == EGL_NO_DISPLAY) {
     65         ALOGE("Failed to get egl display");
     66         return false;
     67     }
     68 
     69     EGLint major = 0;
     70     EGLint minor = 0;
     71     if (!eglInitialize(display, &major, &minor)) {
     72         ALOGE("Failed to initialize EGL: %s", getEGLError());
     73         return false;
     74     } else {
     75         ALOGI("Intiialized EGL at %d.%d", major, minor);
     76     }
     77 
     78 
     79     // Select the configuration that "best" matches our desired characteristics
     80     EGLConfig egl_config;
     81     EGLint num_configs;
     82     if (!eglChooseConfig(display, config_attribs, &egl_config, 1, &num_configs)) {
     83         ALOGE("eglChooseConfig() failed with error: %s", getEGLError());
     84         return false;
     85     }
     86 
     87 
     88     // Create a dummy pbuffer so we have a surface to bind -- we never intend to draw to this
     89     // because attachRenderTarget will be called first.
     90     EGLint surface_attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
     91     sDummySurface = eglCreatePbufferSurface(display, egl_config, surface_attribs);
     92     if (sDummySurface == EGL_NO_SURFACE) {
     93         ALOGE("Failed to create OpenGL ES Dummy surface: %s", getEGLError());
     94         return false;
     95     } else {
     96         ALOGI("Dummy surface looks good!  :)");
     97     }
     98 
     99 
    100     //
    101     // Create the EGL context
    102     //
    103     EGLContext context = eglCreateContext(display, egl_config, EGL_NO_CONTEXT, context_attribs);
    104     if (context == EGL_NO_CONTEXT) {
    105         ALOGE("Failed to create OpenGL ES Context: %s", getEGLError());
    106         return false;
    107     }
    108 
    109 
    110     // Activate our render target for drawing
    111     if (!eglMakeCurrent(display, sDummySurface, sDummySurface, context)) {
    112         ALOGE("Failed to make the OpenGL ES Context current: %s", getEGLError());
    113         return false;
    114     } else {
    115         ALOGI("We made our context current!  :)");
    116     }
    117 
    118 
    119     // Report the extensions available on this implementation
    120     const char* gl_extensions = (const char*) glGetString(GL_EXTENSIONS);
    121     ALOGI("GL EXTENSIONS:\n  %s", gl_extensions);
    122 
    123 
    124     // Reserve handles for the color and depth targets we'll be setting up
    125     glGenRenderbuffers(1, &sColorBuffer);
    126     glGenRenderbuffers(1, &sDepthBuffer);
    127 
    128     // Set up the frame buffer object we can modify and use for off screen rendering
    129     glGenFramebuffers(1, &sFrameBuffer);
    130     glBindFramebuffer(GL_FRAMEBUFFER, sFrameBuffer);
    131 
    132 
    133     // Now that we're assured success, store object handles we constructed
    134     sDisplay = display;
    135     sContext = context;
    136 
    137     return true;
    138 }
    139 
    140 
    141 bool RenderBase::attachRenderTarget(const BufferDesc& tgtBuffer) {
    142     // Hardcoded to RGBx for now
    143     if (tgtBuffer.format != HAL_PIXEL_FORMAT_RGBA_8888) {
    144         ALOGE("Unsupported target buffer format");
    145         return false;
    146     }
    147 
    148     // create a GraphicBuffer from the existing handle
    149     sp<GraphicBuffer> pGfxBuffer = new GraphicBuffer(tgtBuffer.memHandle,
    150                                                      GraphicBuffer::CLONE_HANDLE,
    151                                                      tgtBuffer.width, tgtBuffer.height,
    152                                                      tgtBuffer.format, 1, // layer count
    153                                                      GRALLOC_USAGE_HW_RENDER,
    154                                                      tgtBuffer.stride);
    155     if (pGfxBuffer.get() == nullptr) {
    156         ALOGE("Failed to allocate GraphicBuffer to wrap image handle");
    157         return false;
    158     }
    159 
    160     // Get a GL compatible reference to the graphics buffer we've been given
    161     EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
    162     EGLClientBuffer clientBuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
    163     sKHRimage = eglCreateImageKHR(sDisplay, EGL_NO_CONTEXT,
    164                                   EGL_NATIVE_BUFFER_ANDROID, clientBuf,
    165                                   eglImageAttributes);
    166     if (sKHRimage == EGL_NO_IMAGE_KHR) {
    167         ALOGE("error creating EGLImage for target buffer: %s", getEGLError());
    168         return false;
    169     }
    170 
    171     // Construct a render buffer around the external buffer
    172     glBindRenderbuffer(GL_RENDERBUFFER, sColorBuffer);
    173     glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, static_cast<GLeglImageOES>(sKHRimage));
    174     if (eglGetError() != EGL_SUCCESS) {
    175         ALOGI("glEGLImageTargetRenderbufferStorageOES => %s", getEGLError());
    176         return false;
    177     }
    178 
    179     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, sColorBuffer);
    180     if (eglGetError() != EGL_SUCCESS) {
    181         ALOGE("glFramebufferRenderbuffer => %s", getEGLError());
    182         return false;
    183     }
    184 
    185     GLenum checkResult = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    186     if (checkResult != GL_FRAMEBUFFER_COMPLETE) {
    187         ALOGE("Offscreen framebuffer not configured successfully (%d: %s)",
    188               checkResult, getGLFramebufferError());
    189         return false;
    190     }
    191 
    192     // Store the size of our target buffer
    193     sWidth = tgtBuffer.width;
    194     sHeight = tgtBuffer.height;
    195     sAspectRatio = (float)sWidth / sHeight;
    196 
    197     // Set the viewport
    198     glViewport(0, 0, sWidth, sHeight);
    199 
    200 #if 1   // We don't actually need the clear if we're going to cover the whole screen anyway
    201     // Clear the color buffer
    202     glClearColor(0.8f, 0.1f, 0.2f, 1.0f);
    203     glClear(GL_COLOR_BUFFER_BIT);
    204 #endif
    205 
    206 
    207     return true;
    208 }
    209 
    210 
    211 void RenderBase::detachRenderTarget() {
    212     // Drop our external render target
    213     if (sKHRimage != EGL_NO_IMAGE_KHR) {
    214         eglDestroyImageKHR(sDisplay, sKHRimage);
    215         sKHRimage = EGL_NO_IMAGE_KHR;
    216     }
    217 }