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
19 #include "Utils.h"
20 #include "glError.h"
21
22 #include <aidl/android/hardware/automotive/evs/BufferDesc.h>
23 #include <aidlcommonsupport/NativeHandle.h>
24 #include <android-base/logging.h>
25 #include <android-base/scopeguard.h>
26 #include <ui/GraphicBuffer.h>
27
28 namespace {
29
30 using aidl::android::hardware::automotive::evs::BufferDesc;
31
32 // Eventually we shouldn't need this dependency, but for now the
33 // graphics allocator interface isn't fully supported on all platforms
34 // and this is our work around.
35 using ::android::GraphicBuffer;
36
37 } // namespace
38
39 // OpenGL state shared among all renderers
40 EGLDisplay RenderBase::sDisplay = EGL_NO_DISPLAY;
41 EGLContext RenderBase::sContext = EGL_NO_CONTEXT;
42 EGLSurface RenderBase::sMockSurface = EGL_NO_SURFACE;
43 GLuint RenderBase::sFrameBuffer = -1;
44 GLuint RenderBase::sColorBuffer = -1;
45 GLuint RenderBase::sDepthBuffer = -1;
46 EGLImageKHR RenderBase::sKHRimage = EGL_NO_IMAGE_KHR;
47 unsigned RenderBase::sWidth = 0;
48 unsigned RenderBase::sHeight = 0;
49 float RenderBase::sAspectRatio = 0.0f;
50
prepareGL()51 bool RenderBase::prepareGL() {
52 // Just trivially return success if we're already prepared
53 if (sDisplay != EGL_NO_DISPLAY) {
54 return true;
55 }
56
57 // Hardcoded to RGBx output display
58 const EGLint config_attribs[] = {// Tag Value
59 EGL_RENDERABLE_TYPE,
60 EGL_OPENGL_ES2_BIT,
61 EGL_RED_SIZE,
62 8,
63 EGL_GREEN_SIZE,
64 8,
65 EGL_BLUE_SIZE,
66 8,
67 EGL_NONE};
68
69 // Select OpenGL ES v 3
70 const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
71
72 // Set up our OpenGL ES context associated with the default display (though we won't be visible)
73 EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
74 if (display == EGL_NO_DISPLAY) {
75 LOG(ERROR) << "Failed to get egl display";
76 return false;
77 }
78
79 EGLint major = 0;
80 EGLint minor = 0;
81 if (!eglInitialize(display, &major, &minor)) {
82 LOG(ERROR) << "Failed to initialize EGL: " << getEGLError();
83 return false;
84 } else {
85 LOG(INFO) << "Intiialized EGL at " << major << "." << minor;
86 }
87
88 // Select the configuration that "best" matches our desired characteristics
89 EGLConfig egl_config;
90 EGLint num_configs;
91 if (!eglChooseConfig(display, config_attribs, &egl_config, 1, &num_configs)) {
92 LOG(ERROR) << "eglChooseConfig() failed with error: " << getEGLError();
93 return false;
94 }
95
96 // Create a temporary pbuffer so we have a surface to bind -- we never intend to draw to this
97 // because attachRenderTarget will be called first.
98 EGLint surface_attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
99 sMockSurface = eglCreatePbufferSurface(display, egl_config, surface_attribs);
100 if (sMockSurface == EGL_NO_SURFACE) {
101 LOG(ERROR) << "Failed to create OpenGL ES Mock surface: " << getEGLError();
102 return false;
103 } else {
104 LOG(INFO) << "Mock surface looks good! :)";
105 }
106
107 //
108 // Create the EGL context
109 //
110 EGLContext context = eglCreateContext(display, egl_config, EGL_NO_CONTEXT, context_attribs);
111 if (context == EGL_NO_CONTEXT) {
112 LOG(ERROR) << "Failed to create OpenGL ES Context: " << getEGLError();
113 return false;
114 }
115
116 // Activate our render target for drawing
117 if (!eglMakeCurrent(display, sMockSurface, sMockSurface, context)) {
118 LOG(ERROR) << "Failed to make the OpenGL ES Context current: " << getEGLError();
119 return false;
120 } else {
121 LOG(INFO) << "We made our context current! :)";
122 }
123
124 // Report the extensions available on this implementation
125 const char* gl_extensions = (const char*)glGetString(GL_EXTENSIONS);
126 LOG(INFO) << "GL EXTENSIONS:\n " << gl_extensions;
127
128 // Reserve handles for the color and depth targets we'll be setting up
129 glGenRenderbuffers(1, &sColorBuffer);
130 glGenRenderbuffers(1, &sDepthBuffer);
131
132 // Set up the frame buffer object we can modify and use for off screen rendering
133 glGenFramebuffers(1, &sFrameBuffer);
134 glBindFramebuffer(GL_FRAMEBUFFER, sFrameBuffer);
135
136 // Now that we're assured success, store object handles we constructed
137 sDisplay = display;
138 sContext = context;
139
140 return true;
141 }
142
attachRenderTarget(const BufferDesc & tgtBuffer)143 bool RenderBase::attachRenderTarget(const BufferDesc& tgtBuffer) {
144 native_handle_t* nativeHandle = getNativeHandle(tgtBuffer);
145 if (nativeHandle == nullptr) {
146 LOG(ERROR) << "Target buffer is invalid.";
147 return false;
148 }
149
150 const auto handleGuard =
151 android::base::make_scope_guard([nativeHandle] { free(nativeHandle); });
152 const AHardwareBuffer_Desc* pDesc =
153 reinterpret_cast<const AHardwareBuffer_Desc*>(&tgtBuffer.buffer.description);
154 // Hardcoded to RGBx for now
155 if (pDesc->format != HAL_PIXEL_FORMAT_RGBA_8888) {
156 LOG(ERROR) << "Unsupported target buffer format";
157 return false;
158 }
159
160 // create a GraphicBuffer from the existing handle
161 android::sp<GraphicBuffer> pGfxBuffer =
162 new GraphicBuffer(nativeHandle, GraphicBuffer::CLONE_HANDLE, pDesc->width,
163 pDesc->height, pDesc->format, pDesc->layers, pDesc->usage,
164 pDesc->stride);
165 if (!pGfxBuffer) {
166 LOG(ERROR) << "Failed to allocate GraphicBuffer to wrap image handle";
167 return false;
168 }
169
170 if (auto status = pGfxBuffer->initCheck(); status != android::OK) {
171 LOG(ERROR) << "Failed to initialize the graphic buffer, error = "
172 << android::statusToString(status);
173 return false;
174 }
175
176 // Get a GL compatible reference to the graphics buffer we've been given
177 EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
178 EGLClientBuffer clientBuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
179 sKHRimage = eglCreateImageKHR(sDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuf,
180 eglImageAttributes);
181 if (sKHRimage == EGL_NO_IMAGE_KHR) {
182 LOG(ERROR) << "Error creating EGLImage for target buffer: " << getEGLError();
183 return false;
184 }
185
186 // Construct a render buffer around the external buffer
187 glBindRenderbuffer(GL_RENDERBUFFER, sColorBuffer);
188 glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, static_cast<GLeglImageOES>(sKHRimage));
189 if (eglGetError() != EGL_SUCCESS) {
190 LOG(INFO) << "glEGLImageTargetRenderbufferStorageOES => " << getEGLError();
191 return false;
192 }
193
194 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, sColorBuffer);
195 if (eglGetError() != EGL_SUCCESS) {
196 LOG(ERROR) << "glFramebufferRenderbuffer => " << getEGLError();
197 return false;
198 }
199
200 GLenum checkResult = glCheckFramebufferStatus(GL_FRAMEBUFFER);
201 if (checkResult != GL_FRAMEBUFFER_COMPLETE) {
202 LOG(ERROR) << "Offscreen framebuffer not configured successfully (" << checkResult << ": "
203 << getGLFramebufferError() << ")";
204 return false;
205 }
206
207 // Store the size of our target buffer
208 sWidth = pDesc->width;
209 sHeight = pDesc->height;
210 sAspectRatio = (float)sWidth / sHeight;
211
212 // Set the viewport
213 glViewport(0, 0, sWidth, sHeight);
214
215 // We don't actually need the clear if we're going to cover the whole screen anyway
216 // Clear the color buffer
217 glClearColor(0.8f, 0.1f, 0.2f, 1.0f);
218 glClear(GL_COLOR_BUFFER_BIT);
219
220 return true;
221 }
222
detachRenderTarget()223 void RenderBase::detachRenderTarget() {
224 // Drop our external render target
225 if (sKHRimage != EGL_NO_IMAGE_KHR) {
226 eglDestroyImageKHR(sDisplay, sKHRimage);
227 sKHRimage = EGL_NO_IMAGE_KHR;
228 }
229 }
230