blob: fe781bd771f9fb4b413d85ce01796ea207813922 [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 Reck19b6bcf2014-02-14 20:03:38 -080027#include "../DeferredLayerUpdater.h"
28#include "../LayerRenderer.h"
John Reck4f02bf42014-01-03 18:09:17 -080029#include "../OpenGLRenderer.h"
John Reck23b797a2014-01-03 18:08:34 -080030#include "../Stencil.h"
31
32#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
33#define GLES_VERSION 2
34
John Reck4f02bf42014-01-03 18:09:17 -080035#ifdef USE_OPENGL_RENDERER
36// Android-specific addition that is used to show when frames began in systrace
37EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
38#endif
39
John Reck23b797a2014-01-03 18:08:34 -080040namespace android {
41namespace uirenderer {
42namespace renderthread {
43
44#define ERROR_CASE(x) case x: return #x;
45static const char* egl_error_str(EGLint error) {
46 switch (error) {
47 ERROR_CASE(EGL_SUCCESS)
48 ERROR_CASE(EGL_NOT_INITIALIZED)
49 ERROR_CASE(EGL_BAD_ACCESS)
50 ERROR_CASE(EGL_BAD_ALLOC)
51 ERROR_CASE(EGL_BAD_ATTRIBUTE)
52 ERROR_CASE(EGL_BAD_CONFIG)
53 ERROR_CASE(EGL_BAD_CONTEXT)
54 ERROR_CASE(EGL_BAD_CURRENT_SURFACE)
55 ERROR_CASE(EGL_BAD_DISPLAY)
56 ERROR_CASE(EGL_BAD_MATCH)
57 ERROR_CASE(EGL_BAD_NATIVE_PIXMAP)
58 ERROR_CASE(EGL_BAD_NATIVE_WINDOW)
59 ERROR_CASE(EGL_BAD_PARAMETER)
60 ERROR_CASE(EGL_BAD_SURFACE)
61 ERROR_CASE(EGL_CONTEXT_LOST)
62 default:
63 return "Unknown error";
64 }
65}
66static const char* egl_error_str() {
67 return egl_error_str(eglGetError());
68}
69
70static bool load_dirty_regions_property() {
71 char buf[PROPERTY_VALUE_MAX];
72 int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true");
73 return !strncasecmp("true", buf, len);
74}
75
76// This class contains the shared global EGL objects, such as EGLDisplay
77// and EGLConfig, which are re-used by CanvasContext
78class GlobalContext {
79public:
80 static GlobalContext* get();
81
John Reck4f02bf42014-01-03 18:09:17 -080082 // Returns true on success, false on failure
83 void initialize();
John Reck23b797a2014-01-03 18:08:34 -080084
John Reck4f02bf42014-01-03 18:09:17 -080085 void usePBufferSurface();
John Reck23b797a2014-01-03 18:08:34 -080086 EGLSurface createSurface(EGLNativeWindowType window);
87 void destroySurface(EGLSurface surface);
88
89 void destroy();
90
91 bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
John Reck4f02bf42014-01-03 18:09:17 -080092 void makeCurrent(EGLSurface surface);
93 void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
94 void swapBuffers(EGLSurface surface);
John Reck23b797a2014-01-03 18:08:34 -080095
96 bool enableDirtyRegions(EGLSurface surface);
97
98private:
99 GlobalContext();
100 // GlobalContext is never destroyed, method is purposely not implemented
101 ~GlobalContext();
102
John Reck4f02bf42014-01-03 18:09:17 -0800103 void loadConfig();
104 void createContext();
105 void initAtlas();
John Reck23b797a2014-01-03 18:08:34 -0800106
107 static GlobalContext* sContext;
108
109 EGLDisplay mEglDisplay;
110 EGLConfig mEglConfig;
111 EGLContext mEglContext;
112 EGLSurface mPBufferSurface;
113
114 const bool mRequestDirtyRegions;
115 bool mCanSetDirtyRegions;
116
117 EGLSurface mCurrentSurface;
118};
119
120GlobalContext* GlobalContext::sContext = 0;
121
122GlobalContext* GlobalContext::get() {
123 if (!sContext) {
124 sContext = new GlobalContext();
125 }
126 return sContext;
127}
128
129GlobalContext::GlobalContext()
130 : mEglDisplay(EGL_NO_DISPLAY)
131 , mEglConfig(0)
132 , mEglContext(EGL_NO_CONTEXT)
133 , mPBufferSurface(EGL_NO_SURFACE)
134 , mRequestDirtyRegions(load_dirty_regions_property())
135 , mCurrentSurface(EGL_NO_SURFACE) {
136 mCanSetDirtyRegions = mRequestDirtyRegions;
137 ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false");
138}
139
John Reck4f02bf42014-01-03 18:09:17 -0800140void GlobalContext::initialize() {
141 if (mEglDisplay != EGL_NO_DISPLAY) return;
John Reck23b797a2014-01-03 18:08:34 -0800142
143 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
John Reck4f02bf42014-01-03 18:09:17 -0800144 LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
145 "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
John Reck23b797a2014-01-03 18:08:34 -0800146
147 EGLint major, minor;
John Reck4f02bf42014-01-03 18:09:17 -0800148 LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
149 "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
150
John Reck23b797a2014-01-03 18:08:34 -0800151 ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
152
John Reck4f02bf42014-01-03 18:09:17 -0800153 loadConfig();
154 createContext();
155 usePBufferSurface();
156 Caches::getInstance().init();
157 initAtlas();
John Reck23b797a2014-01-03 18:08:34 -0800158}
159
John Reck4f02bf42014-01-03 18:09:17 -0800160void GlobalContext::loadConfig() {
John Reck23b797a2014-01-03 18:08:34 -0800161 EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
162 EGLint attribs[] = {
163 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
164 EGL_RED_SIZE, 8,
165 EGL_GREEN_SIZE, 8,
166 EGL_BLUE_SIZE, 8,
167 EGL_ALPHA_SIZE, 8,
168 EGL_DEPTH_SIZE, 0,
169 EGL_CONFIG_CAVEAT, EGL_NONE,
170 EGL_STENCIL_SIZE, Stencil::getStencilSize(),
171 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
172 EGL_NONE
173 };
174
175 EGLint num_configs = 1;
176 if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
177 || num_configs != 1) {
178 // Failed to get a valid config
179 if (mCanSetDirtyRegions) {
180 ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
181 // Try again without dirty regions enabled
182 mCanSetDirtyRegions = false;
183 loadConfig();
184 } else {
John Reck4f02bf42014-01-03 18:09:17 -0800185 LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
John Reck23b797a2014-01-03 18:08:34 -0800186 }
187 }
John Reck23b797a2014-01-03 18:08:34 -0800188}
189
John Reck4f02bf42014-01-03 18:09:17 -0800190void GlobalContext::createContext() {
John Reck23b797a2014-01-03 18:08:34 -0800191 EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE };
192 mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
John Reck4f02bf42014-01-03 18:09:17 -0800193 LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
194 "Failed to create context, error = %s", egl_error_str());
John Reck23b797a2014-01-03 18:08:34 -0800195}
196
John Reck4f02bf42014-01-03 18:09:17 -0800197void GlobalContext::initAtlas() {
198 // TODO implement
199 // For now just run without an atlas
200}
201
202void GlobalContext::usePBufferSurface() {
203 LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
204 "usePBufferSurface() called on uninitialized GlobalContext!");
John Reck23b797a2014-01-03 18:08:34 -0800205
206 if (mPBufferSurface == EGL_NO_SURFACE) {
207 EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
208 mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
209 }
John Reck4f02bf42014-01-03 18:09:17 -0800210 makeCurrent(mPBufferSurface);
John Reck23b797a2014-01-03 18:08:34 -0800211}
212
213EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) {
214 initialize();
215 return eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL);
216}
217
218void GlobalContext::destroySurface(EGLSurface surface) {
219 if (isCurrent(surface)) {
220 makeCurrent(EGL_NO_SURFACE);
221 }
222 if (!eglDestroySurface(mEglDisplay, surface)) {
223 ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str());
224 }
225}
226
227void GlobalContext::destroy() {
228 if (mEglDisplay == EGL_NO_DISPLAY) return;
229
230 usePBufferSurface();
231 if (Caches::hasInstance()) {
232 Caches::getInstance().terminate();
233 }
234
235 eglDestroyContext(mEglDisplay, mEglContext);
236 eglDestroySurface(mEglDisplay, mPBufferSurface);
237 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
238 eglTerminate(mEglDisplay);
239 eglReleaseThread();
240
241 mEglDisplay = EGL_NO_DISPLAY;
242 mEglContext = EGL_NO_CONTEXT;
243 mPBufferSurface = EGL_NO_SURFACE;
244 mCurrentSurface = EGL_NO_SURFACE;
245}
246
John Reck4f02bf42014-01-03 18:09:17 -0800247void GlobalContext::makeCurrent(EGLSurface surface) {
248 if (isCurrent(surface)) return;
John Reck23b797a2014-01-03 18:08:34 -0800249
250 if (surface == EGL_NO_SURFACE) {
251 // If we are setting EGL_NO_SURFACE we don't care about any of the potential
252 // return errors, which would only happen if mEglDisplay had already been
253 // destroyed in which case the current context is already NO_CONTEXT
254 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
255 } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
John Reck4f02bf42014-01-03 18:09:17 -0800256 LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
257 (void*)surface, egl_error_str());
John Reck23b797a2014-01-03 18:08:34 -0800258 }
259 mCurrentSurface = surface;
John Reck23b797a2014-01-03 18:08:34 -0800260}
261
John Reck4f02bf42014-01-03 18:09:17 -0800262void GlobalContext::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
263 LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
264 "Tried to beginFrame on EGL_NO_SURFACE!");
265 makeCurrent(surface);
266 if (width) {
267 eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
John Reck23b797a2014-01-03 18:08:34 -0800268 }
John Reck4f02bf42014-01-03 18:09:17 -0800269 if (height) {
270 eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
271 }
272 eglBeginFrame(mEglDisplay, surface);
273}
274
275void GlobalContext::swapBuffers(EGLSurface surface) {
276 eglSwapBuffers(mEglDisplay, surface);
277 EGLint err = eglGetError();
278 // TODO: Check whether we need to special case EGL_CONTEXT_LOST
279 LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS,
280 "Encountered EGL error %d %s during rendering", err, egl_error_str(err));
John Reck23b797a2014-01-03 18:08:34 -0800281}
282
283bool GlobalContext::enableDirtyRegions(EGLSurface surface) {
284 if (!mRequestDirtyRegions) return false;
285
286 if (mCanSetDirtyRegions) {
287 if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) {
288 ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
289 (void*) surface, egl_error_str());
290 return false;
291 }
292 return true;
293 }
294 // Perhaps it is already enabled?
295 EGLint value;
296 if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) {
297 ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
298 (void*) surface, egl_error_str());
299 return false;
300 }
301 return value == EGL_BUFFER_PRESERVED;
302}
303
John Reck4f02bf42014-01-03 18:09:17 -0800304CanvasContext::CanvasContext(bool translucent)
305 : mRenderThread(RenderThread::getInstance())
306 , mEglSurface(EGL_NO_SURFACE)
307 , mDirtyRegionsEnabled(false)
308 , mOpaque(!translucent)
309 , mCanvas(0)
310 , mHaveNewSurface(false)
311 , mInvokeFunctorsPending(false)
312 , mInvokeFunctorsTask(this) {
John Reck23b797a2014-01-03 18:08:34 -0800313 mGlobalContext = GlobalContext::get();
314}
315
316CanvasContext::~CanvasContext() {
John Reck4f02bf42014-01-03 18:09:17 -0800317 removeFunctorsTask();
318 destroyCanvas();
319}
320
321void CanvasContext::destroyCanvas() {
322 if (mCanvas) {
323 delete mCanvas;
324 mCanvas = 0;
325 }
John Reck23b797a2014-01-03 18:08:34 -0800326 setSurface(NULL);
327}
328
John Reck4f02bf42014-01-03 18:09:17 -0800329void CanvasContext::setSurface(EGLNativeWindowType window) {
John Reck23b797a2014-01-03 18:08:34 -0800330 if (mEglSurface != EGL_NO_SURFACE) {
331 mGlobalContext->destroySurface(mEglSurface);
332 mEglSurface = EGL_NO_SURFACE;
333 }
334
335 if (window) {
336 mEglSurface = mGlobalContext->createSurface(window);
John Reck4f02bf42014-01-03 18:09:17 -0800337 LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
338 "Failed to create EGLSurface for window %p, eglErr = %s",
339 (void*) window, egl_error_str());
John Reck23b797a2014-01-03 18:08:34 -0800340 }
341
342 if (mEglSurface != EGL_NO_SURFACE) {
343 mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface);
John Reck4f02bf42014-01-03 18:09:17 -0800344 mHaveNewSurface = true;
John Reck23b797a2014-01-03 18:08:34 -0800345 }
John Reck23b797a2014-01-03 18:08:34 -0800346}
347
John Reck4f02bf42014-01-03 18:09:17 -0800348void CanvasContext::swapBuffers() {
349 mGlobalContext->swapBuffers(mEglSurface);
350 mHaveNewSurface = false;
John Reck23b797a2014-01-03 18:08:34 -0800351}
352
John Reck4f02bf42014-01-03 18:09:17 -0800353void CanvasContext::makeCurrent() {
354 mGlobalContext->makeCurrent(mEglSurface);
John Reck23b797a2014-01-03 18:08:34 -0800355}
356
John Reck4f02bf42014-01-03 18:09:17 -0800357bool CanvasContext::initialize(EGLNativeWindowType window) {
358 if (mCanvas) return false;
359 setSurface(window);
360 makeCurrent();
361 mCanvas = new OpenGLRenderer();
362 mCanvas->initProperties();
363 return true;
364}
365
366void CanvasContext::updateSurface(EGLNativeWindowType window) {
367 setSurface(window);
368 makeCurrent();
369}
370
371void CanvasContext::setup(int width, int height) {
372 if (!mCanvas) return;
373 mCanvas->setViewport(width, height);
374}
375
John Reck19b6bcf2014-02-14 20:03:38 -0800376void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters) {
377 mGlobalContext->makeCurrent(mEglSurface);
378 for (size_t i = 0; i < layerUpdaters->size(); i++) {
379 DeferredLayerUpdater* update = layerUpdaters->itemAt(i);
380 LOG_ALWAYS_FATAL_IF(!update->apply(), "Failed to update layer!");
381 if (update->backingLayer()->deferredUpdateScheduled) {
382 mCanvas->pushLayerUpdate(update->backingLayer());
383 }
384 }
385}
386
John Reck4f02bf42014-01-03 18:09:17 -0800387void CanvasContext::drawDisplayList(DisplayList* displayList, Rect* dirty) {
388 LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
389 "drawDisplayList called on a context with no canvas or surface!");
390
391 EGLint width, height;
392 mGlobalContext->beginFrame(mEglSurface, &width, &height);
393 if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
394 mCanvas->setViewport(width, height);
395 dirty = NULL;
396 } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
397 dirty = NULL;
398 }
399
400 status_t status;
401 if (dirty) {
402 status = mCanvas->prepareDirty(dirty->left, dirty->top,
403 dirty->right, dirty->bottom, mOpaque);
404 } else {
405 status = mCanvas->prepare(mOpaque);
406 }
407
408 Rect outBounds;
409 status |= mCanvas->drawDisplayList(displayList, outBounds);
410 handleFunctorStatus(status, outBounds);
411
412 // TODO: Draw debug info
413 // TODO: Performance tracking
414
415 mCanvas->finish();
416
417 if (status & DrawGlInfo::kStatusDrew) {
418 swapBuffers();
419 }
420}
421
422void InvokeFunctorsTask::run() {
423 mContext->invokeFunctors();
424}
425
426void CanvasContext::attachFunctor(Functor* functor) {
427 if (!mCanvas) return;
428
429 mCanvas->attachFunctor(functor);
430 removeFunctorsTask();
431 queueFunctorsTask(0);
432}
433
434void CanvasContext::detachFunctor(Functor* functor) {
435 if (!mCanvas) return;
436
437 mCanvas->detachFunctor(functor);
438}
439
440void CanvasContext::invokeFunctors() {
441 mInvokeFunctorsPending = false;
442
443 if (!mCanvas) return;
444
445 makeCurrent();
446 Rect dirty;
447 int status = mCanvas->invokeFunctors(dirty);
448 handleFunctorStatus(status, dirty);
449}
450
451void CanvasContext::handleFunctorStatus(int status, const Rect& redrawClip) {
452 if (status & DrawGlInfo::kStatusDraw) {
453 // TODO: Invalidate the redrawClip
454 // Do we need to post to ViewRootImpl like the current renderer?
455 // Can we just enqueue ourselves to re-invoke the same display list?
456 // Something else entirely? Does ChromiumView still want this in a
457 // RenderThread world?
458 }
459
460 if (status & DrawGlInfo::kStatusInvoke) {
461 queueFunctorsTask();
462 }
463}
464
465void CanvasContext::removeFunctorsTask() {
466 if (!mInvokeFunctorsPending) return;
467
468 mRenderThread.remove(&mInvokeFunctorsTask);
469}
470
471void CanvasContext::queueFunctorsTask(int delayMs) {
472 if (mInvokeFunctorsPending) return;
473
474 mInvokeFunctorsPending = true;
475 mRenderThread.queueDelayed(&mInvokeFunctorsTask, delayMs);
John Reck23b797a2014-01-03 18:08:34 -0800476}
477
John Reck19b6bcf2014-02-14 20:03:38 -0800478bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
479 requireGlContext();
480 layer->apply();
481 return LayerRenderer::copyLayer(layer->backingLayer(), bitmap);
482}
483
John Reckfc53ef272014-02-11 10:40:25 -0800484void CanvasContext::runWithGlContext(RenderTask* task) {
John Reck19b6bcf2014-02-14 20:03:38 -0800485 requireGlContext();
486 task->run();
487}
488
489void CanvasContext::requireGlContext() {
John Reckfc53ef272014-02-11 10:40:25 -0800490 if (mEglSurface != EGL_NO_SURFACE) {
491 mGlobalContext->makeCurrent(mEglSurface);
492 } else {
493 mGlobalContext->usePBufferSurface();
494 }
John Reckfc53ef272014-02-11 10:40:25 -0800495}
496
John Reck23b797a2014-01-03 18:08:34 -0800497} /* namespace renderthread */
498} /* namespace uirenderer */
499} /* namespace android */