blob: a848c8f4e2e11858689946bff0d828577002cb72 [file] [log] [blame]
John Reck23b797a2014-01-03 18:08:34 -08001/*
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
17#define LOG_TAG "CanvasContext"
18
19#include "CanvasContext.h"
20
21#include <cutils/properties.h>
John Reck4f02bf42014-01-03 18:09:17 -080022#include <private/hwui/DrawGlInfo.h>
John Reck23b797a2014-01-03 18:08:34 -080023#include <strings.h>
24
John Reck4f02bf42014-01-03 18:09:17 -080025#include "RenderThread.h"
John Reck23b797a2014-01-03 18:08:34 -080026#include "../Caches.h"
John Reck4f02bf42014-01-03 18:09:17 -080027#include "../OpenGLRenderer.h"
John Reck23b797a2014-01-03 18:08:34 -080028#include "../Stencil.h"
29
30#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
31#define GLES_VERSION 2
32
John Reck4f02bf42014-01-03 18:09:17 -080033#ifdef USE_OPENGL_RENDERER
34// Android-specific addition that is used to show when frames began in systrace
35EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
36#endif
37
John Reck23b797a2014-01-03 18:08:34 -080038namespace android {
39namespace uirenderer {
40namespace renderthread {
41
42#define ERROR_CASE(x) case x: return #x;
43static const char* egl_error_str(EGLint error) {
44 switch (error) {
45 ERROR_CASE(EGL_SUCCESS)
46 ERROR_CASE(EGL_NOT_INITIALIZED)
47 ERROR_CASE(EGL_BAD_ACCESS)
48 ERROR_CASE(EGL_BAD_ALLOC)
49 ERROR_CASE(EGL_BAD_ATTRIBUTE)
50 ERROR_CASE(EGL_BAD_CONFIG)
51 ERROR_CASE(EGL_BAD_CONTEXT)
52 ERROR_CASE(EGL_BAD_CURRENT_SURFACE)
53 ERROR_CASE(EGL_BAD_DISPLAY)
54 ERROR_CASE(EGL_BAD_MATCH)
55 ERROR_CASE(EGL_BAD_NATIVE_PIXMAP)
56 ERROR_CASE(EGL_BAD_NATIVE_WINDOW)
57 ERROR_CASE(EGL_BAD_PARAMETER)
58 ERROR_CASE(EGL_BAD_SURFACE)
59 ERROR_CASE(EGL_CONTEXT_LOST)
60 default:
61 return "Unknown error";
62 }
63}
64static const char* egl_error_str() {
65 return egl_error_str(eglGetError());
66}
67
68static bool load_dirty_regions_property() {
69 char buf[PROPERTY_VALUE_MAX];
70 int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true");
71 return !strncasecmp("true", buf, len);
72}
73
74// This class contains the shared global EGL objects, such as EGLDisplay
75// and EGLConfig, which are re-used by CanvasContext
76class GlobalContext {
77public:
78 static GlobalContext* get();
79
John Reck4f02bf42014-01-03 18:09:17 -080080 // Returns true on success, false on failure
81 void initialize();
John Reck23b797a2014-01-03 18:08:34 -080082
John Reck4f02bf42014-01-03 18:09:17 -080083 void usePBufferSurface();
John Reck23b797a2014-01-03 18:08:34 -080084 EGLSurface createSurface(EGLNativeWindowType window);
85 void destroySurface(EGLSurface surface);
86
87 void destroy();
88
89 bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
John Reck4f02bf42014-01-03 18:09:17 -080090 void makeCurrent(EGLSurface surface);
91 void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
92 void swapBuffers(EGLSurface surface);
John Reck23b797a2014-01-03 18:08:34 -080093
94 bool enableDirtyRegions(EGLSurface surface);
95
96private:
97 GlobalContext();
98 // GlobalContext is never destroyed, method is purposely not implemented
99 ~GlobalContext();
100
John Reck4f02bf42014-01-03 18:09:17 -0800101 void loadConfig();
102 void createContext();
103 void initAtlas();
John Reck23b797a2014-01-03 18:08:34 -0800104
105 static GlobalContext* sContext;
106
107 EGLDisplay mEglDisplay;
108 EGLConfig mEglConfig;
109 EGLContext mEglContext;
110 EGLSurface mPBufferSurface;
111
112 const bool mRequestDirtyRegions;
113 bool mCanSetDirtyRegions;
114
115 EGLSurface mCurrentSurface;
116};
117
118GlobalContext* GlobalContext::sContext = 0;
119
120GlobalContext* GlobalContext::get() {
121 if (!sContext) {
122 sContext = new GlobalContext();
123 }
124 return sContext;
125}
126
127GlobalContext::GlobalContext()
128 : mEglDisplay(EGL_NO_DISPLAY)
129 , mEglConfig(0)
130 , mEglContext(EGL_NO_CONTEXT)
131 , mPBufferSurface(EGL_NO_SURFACE)
132 , mRequestDirtyRegions(load_dirty_regions_property())
133 , mCurrentSurface(EGL_NO_SURFACE) {
134 mCanSetDirtyRegions = mRequestDirtyRegions;
135 ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false");
136}
137
John Reck4f02bf42014-01-03 18:09:17 -0800138void GlobalContext::initialize() {
139 if (mEglDisplay != EGL_NO_DISPLAY) return;
John Reck23b797a2014-01-03 18:08:34 -0800140
141 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
John Reck4f02bf42014-01-03 18:09:17 -0800142 LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
143 "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
John Reck23b797a2014-01-03 18:08:34 -0800144
145 EGLint major, minor;
John Reck4f02bf42014-01-03 18:09:17 -0800146 LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
147 "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
148
John Reck23b797a2014-01-03 18:08:34 -0800149 ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
150
John Reck4f02bf42014-01-03 18:09:17 -0800151 loadConfig();
152 createContext();
153 usePBufferSurface();
154 Caches::getInstance().init();
155 initAtlas();
John Reck23b797a2014-01-03 18:08:34 -0800156}
157
John Reck4f02bf42014-01-03 18:09:17 -0800158void GlobalContext::loadConfig() {
John Reck23b797a2014-01-03 18:08:34 -0800159 EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
160 EGLint attribs[] = {
161 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
162 EGL_RED_SIZE, 8,
163 EGL_GREEN_SIZE, 8,
164 EGL_BLUE_SIZE, 8,
165 EGL_ALPHA_SIZE, 8,
166 EGL_DEPTH_SIZE, 0,
167 EGL_CONFIG_CAVEAT, EGL_NONE,
168 EGL_STENCIL_SIZE, Stencil::getStencilSize(),
169 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
170 EGL_NONE
171 };
172
173 EGLint num_configs = 1;
174 if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
175 || num_configs != 1) {
176 // Failed to get a valid config
177 if (mCanSetDirtyRegions) {
178 ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
179 // Try again without dirty regions enabled
180 mCanSetDirtyRegions = false;
181 loadConfig();
182 } else {
John Reck4f02bf42014-01-03 18:09:17 -0800183 LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
John Reck23b797a2014-01-03 18:08:34 -0800184 }
185 }
John Reck23b797a2014-01-03 18:08:34 -0800186}
187
John Reck4f02bf42014-01-03 18:09:17 -0800188void GlobalContext::createContext() {
John Reck23b797a2014-01-03 18:08:34 -0800189 EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE };
190 mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
John Reck4f02bf42014-01-03 18:09:17 -0800191 LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
192 "Failed to create context, error = %s", egl_error_str());
John Reck23b797a2014-01-03 18:08:34 -0800193}
194
John Reck4f02bf42014-01-03 18:09:17 -0800195void GlobalContext::initAtlas() {
196 // TODO implement
197 // For now just run without an atlas
198}
199
200void GlobalContext::usePBufferSurface() {
201 LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
202 "usePBufferSurface() called on uninitialized GlobalContext!");
John Reck23b797a2014-01-03 18:08:34 -0800203
204 if (mPBufferSurface == EGL_NO_SURFACE) {
205 EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
206 mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
207 }
John Reck4f02bf42014-01-03 18:09:17 -0800208 makeCurrent(mPBufferSurface);
John Reck23b797a2014-01-03 18:08:34 -0800209}
210
211EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) {
212 initialize();
213 return eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL);
214}
215
216void GlobalContext::destroySurface(EGLSurface surface) {
217 if (isCurrent(surface)) {
218 makeCurrent(EGL_NO_SURFACE);
219 }
220 if (!eglDestroySurface(mEglDisplay, surface)) {
221 ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str());
222 }
223}
224
225void GlobalContext::destroy() {
226 if (mEglDisplay == EGL_NO_DISPLAY) return;
227
228 usePBufferSurface();
229 if (Caches::hasInstance()) {
230 Caches::getInstance().terminate();
231 }
232
233 eglDestroyContext(mEglDisplay, mEglContext);
234 eglDestroySurface(mEglDisplay, mPBufferSurface);
235 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
236 eglTerminate(mEglDisplay);
237 eglReleaseThread();
238
239 mEglDisplay = EGL_NO_DISPLAY;
240 mEglContext = EGL_NO_CONTEXT;
241 mPBufferSurface = EGL_NO_SURFACE;
242 mCurrentSurface = EGL_NO_SURFACE;
243}
244
John Reck4f02bf42014-01-03 18:09:17 -0800245void GlobalContext::makeCurrent(EGLSurface surface) {
246 if (isCurrent(surface)) return;
John Reck23b797a2014-01-03 18:08:34 -0800247
248 if (surface == EGL_NO_SURFACE) {
249 // If we are setting EGL_NO_SURFACE we don't care about any of the potential
250 // return errors, which would only happen if mEglDisplay had already been
251 // destroyed in which case the current context is already NO_CONTEXT
252 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
253 } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
John Reck4f02bf42014-01-03 18:09:17 -0800254 LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
255 (void*)surface, egl_error_str());
John Reck23b797a2014-01-03 18:08:34 -0800256 }
257 mCurrentSurface = surface;
John Reck23b797a2014-01-03 18:08:34 -0800258}
259
John Reck4f02bf42014-01-03 18:09:17 -0800260void GlobalContext::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
261 LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
262 "Tried to beginFrame on EGL_NO_SURFACE!");
263 makeCurrent(surface);
264 if (width) {
265 eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
John Reck23b797a2014-01-03 18:08:34 -0800266 }
John Reck4f02bf42014-01-03 18:09:17 -0800267 if (height) {
268 eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
269 }
270 eglBeginFrame(mEglDisplay, surface);
271}
272
273void GlobalContext::swapBuffers(EGLSurface surface) {
274 eglSwapBuffers(mEglDisplay, surface);
275 EGLint err = eglGetError();
276 // TODO: Check whether we need to special case EGL_CONTEXT_LOST
277 LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS,
278 "Encountered EGL error %d %s during rendering", err, egl_error_str(err));
John Reck23b797a2014-01-03 18:08:34 -0800279}
280
281bool GlobalContext::enableDirtyRegions(EGLSurface surface) {
282 if (!mRequestDirtyRegions) return false;
283
284 if (mCanSetDirtyRegions) {
285 if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) {
286 ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
287 (void*) surface, egl_error_str());
288 return false;
289 }
290 return true;
291 }
292 // Perhaps it is already enabled?
293 EGLint value;
294 if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) {
295 ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
296 (void*) surface, egl_error_str());
297 return false;
298 }
299 return value == EGL_BUFFER_PRESERVED;
300}
301
John Reck4f02bf42014-01-03 18:09:17 -0800302CanvasContext::CanvasContext(bool translucent)
303 : mRenderThread(RenderThread::getInstance())
304 , mEglSurface(EGL_NO_SURFACE)
305 , mDirtyRegionsEnabled(false)
306 , mOpaque(!translucent)
307 , mCanvas(0)
308 , mHaveNewSurface(false)
309 , mInvokeFunctorsPending(false)
310 , mInvokeFunctorsTask(this) {
John Reck23b797a2014-01-03 18:08:34 -0800311 mGlobalContext = GlobalContext::get();
312}
313
314CanvasContext::~CanvasContext() {
John Reck4f02bf42014-01-03 18:09:17 -0800315 removeFunctorsTask();
316 destroyCanvas();
317}
318
319void CanvasContext::destroyCanvas() {
320 if (mCanvas) {
321 delete mCanvas;
322 mCanvas = 0;
323 }
John Reck23b797a2014-01-03 18:08:34 -0800324 setSurface(NULL);
325}
326
John Reck4f02bf42014-01-03 18:09:17 -0800327void CanvasContext::setSurface(EGLNativeWindowType window) {
John Reck23b797a2014-01-03 18:08:34 -0800328 if (mEglSurface != EGL_NO_SURFACE) {
329 mGlobalContext->destroySurface(mEglSurface);
330 mEglSurface = EGL_NO_SURFACE;
331 }
332
333 if (window) {
334 mEglSurface = mGlobalContext->createSurface(window);
John Reck4f02bf42014-01-03 18:09:17 -0800335 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
336 "Failed to create EGLSurface for window %p, eglErr = %s",
337 (void*) window, egl_error_str());
John Reck23b797a2014-01-03 18:08:34 -0800338 }
339
340 if (mEglSurface != EGL_NO_SURFACE) {
341 mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface);
John Reck4f02bf42014-01-03 18:09:17 -0800342 mHaveNewSurface = true;
John Reck23b797a2014-01-03 18:08:34 -0800343 }
John Reck23b797a2014-01-03 18:08:34 -0800344}
345
John Reck4f02bf42014-01-03 18:09:17 -0800346void CanvasContext::swapBuffers() {
347 mGlobalContext->swapBuffers(mEglSurface);
348 mHaveNewSurface = false;
John Reck23b797a2014-01-03 18:08:34 -0800349}
350
John Reck4f02bf42014-01-03 18:09:17 -0800351void CanvasContext::makeCurrent() {
352 mGlobalContext->makeCurrent(mEglSurface);
John Reck23b797a2014-01-03 18:08:34 -0800353}
354
John Reck4f02bf42014-01-03 18:09:17 -0800355bool CanvasContext::initialize(EGLNativeWindowType window) {
356 if (mCanvas) return false;
357 setSurface(window);
358 makeCurrent();
359 mCanvas = new OpenGLRenderer();
360 mCanvas->initProperties();
361 return true;
362}
363
364void CanvasContext::updateSurface(EGLNativeWindowType window) {
365 setSurface(window);
366 makeCurrent();
367}
368
369void CanvasContext::setup(int width, int height) {
370 if (!mCanvas) return;
371 mCanvas->setViewport(width, height);
372}
373
374void CanvasContext::drawDisplayList(DisplayList* displayList, Rect* dirty) {
375 LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
376 "drawDisplayList called on a context with no canvas or surface!");
377
378 EGLint width, height;
379 mGlobalContext->beginFrame(mEglSurface, &width, &height);
380 if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
381 mCanvas->setViewport(width, height);
382 dirty = NULL;
383 } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
384 dirty = NULL;
385 }
386
387 status_t status;
388 if (dirty) {
389 status = mCanvas->prepareDirty(dirty->left, dirty->top,
390 dirty->right, dirty->bottom, mOpaque);
391 } else {
392 status = mCanvas->prepare(mOpaque);
393 }
394
395 Rect outBounds;
396 status |= mCanvas->drawDisplayList(displayList, outBounds);
397 handleFunctorStatus(status, outBounds);
398
399 // TODO: Draw debug info
400 // TODO: Performance tracking
401
402 mCanvas->finish();
403
404 if (status & DrawGlInfo::kStatusDrew) {
405 swapBuffers();
406 }
407}
408
409void InvokeFunctorsTask::run() {
410 mContext->invokeFunctors();
411}
412
413void CanvasContext::attachFunctor(Functor* functor) {
414 if (!mCanvas) return;
415
416 mCanvas->attachFunctor(functor);
417 removeFunctorsTask();
418 queueFunctorsTask(0);
419}
420
421void CanvasContext::detachFunctor(Functor* functor) {
422 if (!mCanvas) return;
423
424 mCanvas->detachFunctor(functor);
425}
426
427void CanvasContext::invokeFunctors() {
428 mInvokeFunctorsPending = false;
429
430 if (!mCanvas) return;
431
432 makeCurrent();
433 Rect dirty;
434 int status = mCanvas->invokeFunctors(dirty);
435 handleFunctorStatus(status, dirty);
436}
437
438void CanvasContext::handleFunctorStatus(int status, const Rect& redrawClip) {
439 if (status & DrawGlInfo::kStatusDraw) {
440 // TODO: Invalidate the redrawClip
441 // Do we need to post to ViewRootImpl like the current renderer?
442 // Can we just enqueue ourselves to re-invoke the same display list?
443 // Something else entirely? Does ChromiumView still want this in a
444 // RenderThread world?
445 }
446
447 if (status & DrawGlInfo::kStatusInvoke) {
448 queueFunctorsTask();
449 }
450}
451
452void CanvasContext::removeFunctorsTask() {
453 if (!mInvokeFunctorsPending) return;
454
455 mRenderThread.remove(&mInvokeFunctorsTask);
456}
457
458void CanvasContext::queueFunctorsTask(int delayMs) {
459 if (mInvokeFunctorsPending) return;
460
461 mInvokeFunctorsPending = true;
462 mRenderThread.queueDelayed(&mInvokeFunctorsTask, delayMs);
John Reck23b797a2014-01-03 18:08:34 -0800463}
464
465} /* namespace renderthread */
466} /* namespace uirenderer */
467} /* namespace android */