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