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 }