blob: dd6211b63badd7568b4cd661abbea41a9b07743f [file] [log] [blame]
John Reck3b202512014-06-23 13:13:08 -07001/*
2 * Copyright (C) 2014 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
John Reck3b202512014-06-23 13:13:08 -070017#include "EglManager.h"
18
19#include <cutils/log.h>
20#include <cutils/properties.h>
21
Tom Hudson2dc236b2014-10-15 15:46:42 -040022#include "../Caches.h"
John Reck3b202512014-06-23 13:13:08 -070023#include "../RenderState.h"
24#include "RenderThread.h"
25
26#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
27#define GLES_VERSION 2
28
29// Android-specific addition that is used to show when frames began in systrace
30EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
31
32namespace android {
33namespace uirenderer {
34namespace renderthread {
35
36#define ERROR_CASE(x) case x: return #x;
37static const char* egl_error_str(EGLint error) {
38 switch (error) {
39 ERROR_CASE(EGL_SUCCESS)
40 ERROR_CASE(EGL_NOT_INITIALIZED)
41 ERROR_CASE(EGL_BAD_ACCESS)
42 ERROR_CASE(EGL_BAD_ALLOC)
43 ERROR_CASE(EGL_BAD_ATTRIBUTE)
44 ERROR_CASE(EGL_BAD_CONFIG)
45 ERROR_CASE(EGL_BAD_CONTEXT)
46 ERROR_CASE(EGL_BAD_CURRENT_SURFACE)
47 ERROR_CASE(EGL_BAD_DISPLAY)
48 ERROR_CASE(EGL_BAD_MATCH)
49 ERROR_CASE(EGL_BAD_NATIVE_PIXMAP)
50 ERROR_CASE(EGL_BAD_NATIVE_WINDOW)
51 ERROR_CASE(EGL_BAD_PARAMETER)
52 ERROR_CASE(EGL_BAD_SURFACE)
53 ERROR_CASE(EGL_CONTEXT_LOST)
54 default:
55 return "Unknown error";
56 }
57}
58static const char* egl_error_str() {
59 return egl_error_str(eglGetError());
60}
61
62static bool load_dirty_regions_property() {
63 char buf[PROPERTY_VALUE_MAX];
64 int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true");
65 return !strncasecmp("true", buf, len);
66}
67
68EglManager::EglManager(RenderThread& thread)
69 : mRenderThread(thread)
70 , mEglDisplay(EGL_NO_DISPLAY)
71 , mEglConfig(0)
72 , mEglContext(EGL_NO_CONTEXT)
73 , mPBufferSurface(EGL_NO_SURFACE)
74 , mRequestDirtyRegions(load_dirty_regions_property())
75 , mCurrentSurface(EGL_NO_SURFACE)
76 , mAtlasMap(NULL)
77 , mAtlasMapSize(0) {
78 mCanSetDirtyRegions = mRequestDirtyRegions;
79 ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false");
80}
81
82void EglManager::initialize() {
83 if (hasEglContext()) return;
84
85 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
86 LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
87 "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
88
89 EGLint major, minor;
90 LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
91 "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
92
93 ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
94
95 loadConfig();
96 createContext();
97 usePBufferSurface();
98 mRenderThread.renderState().onGLContextCreated();
99 initAtlas();
100}
101
102bool EglManager::hasEglContext() {
103 return mEglDisplay != EGL_NO_DISPLAY;
104}
105
106void EglManager::requireGlContext() {
107 LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "No EGL context");
108
109 // We don't care *WHAT* surface is active, just that one is active to give
110 // us access to the GL context
111 if (mCurrentSurface == EGL_NO_SURFACE) {
112 usePBufferSurface();
113 }
114}
115
116void EglManager::loadConfig() {
117 EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
118 EGLint attribs[] = {
119 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
120 EGL_RED_SIZE, 8,
121 EGL_GREEN_SIZE, 8,
122 EGL_BLUE_SIZE, 8,
123 EGL_ALPHA_SIZE, 8,
124 EGL_DEPTH_SIZE, 0,
125 EGL_CONFIG_CAVEAT, EGL_NONE,
126 EGL_STENCIL_SIZE, Stencil::getStencilSize(),
127 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
128 EGL_NONE
129 };
130
131 EGLint num_configs = 1;
132 if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
133 || num_configs != 1) {
134 // Failed to get a valid config
135 if (mCanSetDirtyRegions) {
136 ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
137 // Try again without dirty regions enabled
138 mCanSetDirtyRegions = false;
139 loadConfig();
140 } else {
141 LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
142 }
143 }
144}
145
146void EglManager::createContext() {
147 EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE };
148 mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
149 LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
150 "Failed to create context, error = %s", egl_error_str());
151}
152
153void EglManager::setTextureAtlas(const sp<GraphicBuffer>& buffer,
154 int64_t* map, size_t mapSize) {
155
156 // Already initialized
157 if (mAtlasBuffer.get()) {
158 ALOGW("Multiple calls to setTextureAtlas!");
159 delete map;
160 return;
161 }
162
163 mAtlasBuffer = buffer;
164 mAtlasMap = map;
165 mAtlasMapSize = mapSize;
166
167 if (hasEglContext()) {
168 usePBufferSurface();
169 initAtlas();
170 }
171}
172
173void EglManager::initAtlas() {
174 if (mAtlasBuffer.get()) {
175 Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize);
176 }
177}
178
179void EglManager::usePBufferSurface() {
180 LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
181 "usePBufferSurface() called on uninitialized GlobalContext!");
182
183 if (mPBufferSurface == EGL_NO_SURFACE) {
184 EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
185 mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
186 }
187 makeCurrent(mPBufferSurface);
188}
189
190EGLSurface EglManager::createSurface(EGLNativeWindowType window) {
191 initialize();
192 EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL);
193 LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
194 "Failed to create EGLSurface for window %p, eglErr = %s",
195 (void*) window, egl_error_str());
196 return surface;
197}
198
199void EglManager::destroySurface(EGLSurface surface) {
200 if (isCurrent(surface)) {
201 makeCurrent(EGL_NO_SURFACE);
202 }
203 if (!eglDestroySurface(mEglDisplay, surface)) {
204 ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str());
205 }
206}
207
208void EglManager::destroy() {
209 if (mEglDisplay == EGL_NO_DISPLAY) return;
210
211 usePBufferSurface();
212 if (Caches::hasInstance()) {
213 Caches::getInstance().terminate();
214 }
215
Chris Craik1d477422014-08-26 17:30:15 -0700216 mRenderThread.renderState().onGLContextDestroyed();
John Reck3b202512014-06-23 13:13:08 -0700217 eglDestroyContext(mEglDisplay, mEglContext);
218 eglDestroySurface(mEglDisplay, mPBufferSurface);
219 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
220 eglTerminate(mEglDisplay);
221 eglReleaseThread();
222
223 mEglDisplay = EGL_NO_DISPLAY;
224 mEglContext = EGL_NO_CONTEXT;
225 mPBufferSurface = EGL_NO_SURFACE;
226 mCurrentSurface = EGL_NO_SURFACE;
227}
228
229bool EglManager::makeCurrent(EGLSurface surface) {
230 if (isCurrent(surface)) return false;
231
232 if (surface == EGL_NO_SURFACE) {
233 // If we are setting EGL_NO_SURFACE we don't care about any of the potential
234 // return errors, which would only happen if mEglDisplay had already been
235 // destroyed in which case the current context is already NO_CONTEXT
236 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
237 } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
238 LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
239 (void*)surface, egl_error_str());
240 }
241 mCurrentSurface = surface;
242 return true;
243}
244
245void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
246 LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
247 "Tried to beginFrame on EGL_NO_SURFACE!");
248 makeCurrent(surface);
249 if (width) {
250 eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
251 }
252 if (height) {
253 eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
254 }
255 eglBeginFrame(mEglDisplay, surface);
256}
257
John Reck2cdbc7d2014-09-17 16:06:36 -0700258bool EglManager::swapBuffers(EGLSurface surface) {
John Reck3b202512014-06-23 13:13:08 -0700259 eglSwapBuffers(mEglDisplay, surface);
260 EGLint err = eglGetError();
John Reck2cdbc7d2014-09-17 16:06:36 -0700261 if (CC_LIKELY(err == EGL_SUCCESS)) {
262 return true;
263 }
264 if (err == EGL_BAD_SURFACE) {
265 // For some reason our surface was destroyed out from under us
266 // This really shouldn't happen, but if it does we can recover easily
267 // by just not trying to use the surface anymore
268 ALOGW("swapBuffers encountered EGL_BAD_SURFACE on %p, halting rendering...", surface);
269 return false;
270 }
271 LOG_ALWAYS_FATAL("Encountered EGL error %d %s during rendering",
272 err, egl_error_str(err));
273 // Impossible to hit this, but the compiler doesn't know that
274 return false;
John Reck3b202512014-06-23 13:13:08 -0700275}
276
277bool EglManager::enableDirtyRegions(EGLSurface surface) {
278 if (!mRequestDirtyRegions) return false;
279
280 if (mCanSetDirtyRegions) {
281 if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) {
282 ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
283 (void*) surface, egl_error_str());
284 return false;
285 }
286 return true;
287 }
288 // Perhaps it is already enabled?
289 EGLint value;
290 if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) {
291 ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
292 (void*) surface, egl_error_str());
293 return false;
294 }
295 return value == EGL_BUFFER_PRESERVED;
296}
297
298} /* namespace renderthread */
299} /* namespace uirenderer */
300} /* namespace android */