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 <android-base/logging.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::sMockSurface = 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 
prepareGL()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         LOG(ERROR) << "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         LOG(ERROR) << "Failed to initialize EGL: " << getEGLError();
73         return false;
74     } else {
75         LOG(INFO) << "Intiialized EGL at " << 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         LOG(ERROR) << "eglChooseConfig() failed with error: " << getEGLError();
84         return false;
85     }
86 
87 
88     // Create a temporary 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     sMockSurface = eglCreatePbufferSurface(display, egl_config, surface_attribs);
92     if (sMockSurface == EGL_NO_SURFACE) {
93         LOG(ERROR) << "Failed to create OpenGL ES Mock surface: " << getEGLError();
94         return false;
95     } else {
96         LOG(INFO) << "Mock 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         LOG(ERROR) << "Failed to create OpenGL ES Context: " << getEGLError();
106         return false;
107     }
108 
109 
110     // Activate our render target for drawing
111     if (!eglMakeCurrent(display, sMockSurface, sMockSurface, context)) {
112         LOG(ERROR) << "Failed to make the OpenGL ES Context current: " << getEGLError();
113         return false;
114     } else {
115         LOG(INFO) << "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     LOG(INFO) << "GL EXTENSIONS:\n  " << 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 
attachRenderTarget(const BufferDesc & tgtBuffer)141 bool RenderBase::attachRenderTarget(const BufferDesc& tgtBuffer) {
142     const AHardwareBuffer_Desc* pDesc =
143         reinterpret_cast<const AHardwareBuffer_Desc *>(&tgtBuffer.buffer.description);
144     // Hardcoded to RGBx for now
145     if (pDesc->format != HAL_PIXEL_FORMAT_RGBA_8888) {
146         LOG(ERROR) << "Unsupported target buffer format";
147         return false;
148     }
149 
150     // create a GraphicBuffer from the existing handle
151     sp<GraphicBuffer> pGfxBuffer = new GraphicBuffer(tgtBuffer.buffer.nativeHandle,
152                                                      GraphicBuffer::CLONE_HANDLE,
153                                                      pDesc->width,
154                                                      pDesc->height,
155                                                      pDesc->format,
156                                                      pDesc->layers,
157                                                      GRALLOC_USAGE_HW_RENDER,
158                                                      pDesc->stride);
159     if (pGfxBuffer.get() == nullptr) {
160         LOG(ERROR) << "Failed to allocate GraphicBuffer to wrap image handle";
161         return false;
162     }
163 
164     // Get a GL compatible reference to the graphics buffer we've been given
165     EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
166     EGLClientBuffer clientBuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
167     sKHRimage = eglCreateImageKHR(sDisplay, EGL_NO_CONTEXT,
168                                   EGL_NATIVE_BUFFER_ANDROID, clientBuf,
169                                   eglImageAttributes);
170     if (sKHRimage == EGL_NO_IMAGE_KHR) {
171         LOG(ERROR) << "Error creating EGLImage for target buffer: " << getEGLError();
172         return false;
173     }
174 
175     // Construct a render buffer around the external buffer
176     glBindRenderbuffer(GL_RENDERBUFFER, sColorBuffer);
177     glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, static_cast<GLeglImageOES>(sKHRimage));
178     if (eglGetError() != EGL_SUCCESS) {
179         LOG(INFO) << "glEGLImageTargetRenderbufferStorageOES => " << getEGLError();
180         return false;
181     }
182 
183     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, sColorBuffer);
184     if (eglGetError() != EGL_SUCCESS) {
185         LOG(ERROR) << "glFramebufferRenderbuffer => " << getEGLError();
186         return false;
187     }
188 
189     GLenum checkResult = glCheckFramebufferStatus(GL_FRAMEBUFFER);
190     if (checkResult != GL_FRAMEBUFFER_COMPLETE) {
191         LOG(ERROR) << "Offscreen framebuffer not configured successfully ("
192                    << checkResult << ": " << getGLFramebufferError() << ")";
193         return false;
194     }
195 
196     // Store the size of our target buffer
197     sWidth = pDesc->width;
198     sHeight = pDesc->height;
199     sAspectRatio = (float)sWidth / sHeight;
200 
201     // Set the viewport
202     glViewport(0, 0, sWidth, sHeight);
203 
204     // We don't actually need the clear if we're going to cover the whole screen anyway
205     // Clear the color buffer
206     glClearColor(0.8f, 0.1f, 0.2f, 1.0f);
207     glClear(GL_COLOR_BUFFER_BIT);
208 
209     return true;
210 }
211 
212 
detachRenderTarget()213 void RenderBase::detachRenderTarget() {
214     // Drop our external render target
215     if (sKHRimage != EGL_NO_IMAGE_KHR) {
216         eglDestroyImageKHR(sDisplay, sKHRimage);
217         sKHRimage = EGL_NO_IMAGE_KHR;
218     }
219 }
220