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