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 
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         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 placeholder 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 
attachRenderTarget(const BufferDesc & tgtBuffer)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 
detachRenderTarget()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 }