EglManager.cpp revision cd55852fcd840f7f4c4d7a0a7253a2995c77afa2
1/*
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#include "EglManager.h"
18
19#include "Texture.h"
20#include "Caches.h"
21#include "DeviceInfo.h"
22#include "Frame.h"
23#include "Properties.h"
24#include "RenderThread.h"
25#include "renderstate/RenderState.h"
26#include "utils/StringUtils.h"
27#include <cutils/log.h>
28#include <cutils/properties.h>
29#include <EGL/eglext.h>
30#include <GrContextOptions.h>
31#include <gl/GrGLInterface.h>
32#include <string>
33
34#ifdef HWUI_GLES_WRAP_ENABLED
35#include "debug/GlesDriver.h"
36#endif
37
38#define GLES_VERSION 2
39
40// Android-specific addition that is used to show when frames began in systrace
41EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
42
43namespace android {
44namespace uirenderer {
45namespace renderthread {
46
47#define ERROR_CASE(x) case x: return #x;
48static const char* egl_error_str(EGLint error) {
49    switch (error) {
50        ERROR_CASE(EGL_SUCCESS)
51        ERROR_CASE(EGL_NOT_INITIALIZED)
52        ERROR_CASE(EGL_BAD_ACCESS)
53        ERROR_CASE(EGL_BAD_ALLOC)
54        ERROR_CASE(EGL_BAD_ATTRIBUTE)
55        ERROR_CASE(EGL_BAD_CONFIG)
56        ERROR_CASE(EGL_BAD_CONTEXT)
57        ERROR_CASE(EGL_BAD_CURRENT_SURFACE)
58        ERROR_CASE(EGL_BAD_DISPLAY)
59        ERROR_CASE(EGL_BAD_MATCH)
60        ERROR_CASE(EGL_BAD_NATIVE_PIXMAP)
61        ERROR_CASE(EGL_BAD_NATIVE_WINDOW)
62        ERROR_CASE(EGL_BAD_PARAMETER)
63        ERROR_CASE(EGL_BAD_SURFACE)
64        ERROR_CASE(EGL_CONTEXT_LOST)
65    default:
66        return "Unknown error";
67    }
68}
69const char* EglManager::eglErrorString() {
70    return egl_error_str(eglGetError());
71}
72
73static struct {
74    bool bufferAge = false;
75    bool setDamage = false;
76} EglExtensions;
77
78EglManager::EglManager(RenderThread& thread)
79        : mRenderThread(thread)
80        , mEglDisplay(EGL_NO_DISPLAY)
81        , mEglConfig(nullptr)
82        , mEglContext(EGL_NO_CONTEXT)
83        , mPBufferSurface(EGL_NO_SURFACE)
84        , mCurrentSurface(EGL_NO_SURFACE) {
85}
86
87void EglManager::initialize() {
88    if (hasEglContext()) return;
89
90    ATRACE_NAME("Creating EGLContext");
91
92    mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
93    LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
94            "Failed to get EGL_DEFAULT_DISPLAY! err=%s", eglErrorString());
95
96    EGLint major, minor;
97    LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
98            "Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString());
99
100    ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
101
102    initExtensions();
103
104    // Now that extensions are loaded, pick a swap behavior
105    if (Properties::enablePartialUpdates) {
106        if (Properties::useBufferAge && EglExtensions.bufferAge) {
107            mSwapBehavior = SwapBehavior::BufferAge;
108        } else {
109            mSwapBehavior = SwapBehavior::Preserved;
110        }
111    }
112
113    loadConfig();
114    createContext();
115    createPBufferSurface();
116    makeCurrent(mPBufferSurface);
117    DeviceInfo::initialize();
118    mRenderThread.renderState().onGLContextCreated();
119
120    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
121#ifdef HWUI_GLES_WRAP_ENABLED
122        debug::GlesDriver* driver = debug::GlesDriver::get();
123        sk_sp<const GrGLInterface> glInterface(driver->getSkiaInterface());
124#else
125        sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
126#endif
127        LOG_ALWAYS_FATAL_IF(!glInterface.get());
128
129        GrContextOptions options;
130        options.fDisableDistanceFieldPaths = true;
131        options.fAllowPathMaskCaching = true;
132        mRenderThread.setGrContext(GrContext::Create(GrBackend::kOpenGL_GrBackend,
133                (GrBackendContext)glInterface.get(), options));
134    }
135}
136
137void EglManager::initExtensions() {
138    auto extensions = StringUtils::split(
139            eglQueryString(mEglDisplay, EGL_EXTENSIONS));
140    // For our purposes we don't care if EGL_BUFFER_AGE is a result of
141    // EGL_EXT_buffer_age or EGL_KHR_partial_update as our usage is covered
142    // under EGL_KHR_partial_update and we don't need the expanded scope
143    // that EGL_EXT_buffer_age provides.
144    EglExtensions.bufferAge = extensions.has("EGL_EXT_buffer_age")
145            || extensions.has("EGL_KHR_partial_update");
146    EglExtensions.setDamage = extensions.has("EGL_KHR_partial_update");
147    LOG_ALWAYS_FATAL_IF(!extensions.has("EGL_KHR_swap_buffers_with_damage"),
148            "Missing required extension EGL_KHR_swap_buffers_with_damage");
149}
150
151bool EglManager::hasEglContext() {
152    return mEglDisplay != EGL_NO_DISPLAY;
153}
154
155void EglManager::loadConfig() {
156    ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
157    EGLint swapBehavior = (mSwapBehavior == SwapBehavior::Preserved)
158            ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
159    EGLint attribs[] = {
160            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
161            EGL_RED_SIZE, 8,
162            EGL_GREEN_SIZE, 8,
163            EGL_BLUE_SIZE, 8,
164            EGL_ALPHA_SIZE, 8,
165            EGL_DEPTH_SIZE, 0,
166            EGL_CONFIG_CAVEAT, EGL_NONE,
167            EGL_STENCIL_SIZE, Stencil::getStencilSize(),
168            EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
169            EGL_NONE
170    };
171
172    EGLint num_configs = 1;
173    if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
174            || num_configs != 1) {
175        if (mSwapBehavior == SwapBehavior::Preserved) {
176            // Try again without dirty regions enabled
177            ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
178            mSwapBehavior = SwapBehavior::Discard;
179            loadConfig();
180        } else {
181            // Failed to get a valid config
182            LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString());
183        }
184    }
185}
186
187void EglManager::createContext() {
188    EGLint attribs[] = {
189            EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION,
190            EGL_NONE
191    };
192    mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
193    LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
194        "Failed to create context, error = %s", eglErrorString());
195}
196
197void EglManager::createPBufferSurface() {
198    LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
199            "usePBufferSurface() called on uninitialized GlobalContext!");
200
201    if (mPBufferSurface == EGL_NO_SURFACE) {
202        EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
203        mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
204    }
205}
206
207EGLSurface EglManager::createSurface(EGLNativeWindowType window) {
208    initialize();
209
210    EGLint attribs[] = {
211#ifdef ANDROID_ENABLE_LINEAR_BLENDING
212            EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR,
213            EGL_COLORSPACE, EGL_COLORSPACE_sRGB,
214#endif
215            EGL_NONE
216    };
217
218    EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, attribs);
219    LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
220            "Failed to create EGLSurface for window %p, eglErr = %s",
221            (void*) window, eglErrorString());
222
223    if (mSwapBehavior != SwapBehavior::Preserved) {
224        LOG_ALWAYS_FATAL_IF(eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) == EGL_FALSE,
225                            "Failed to set swap behavior to destroyed for window %p, eglErr = %s",
226                            (void*) window, eglErrorString());
227    }
228
229    return surface;
230}
231
232void EglManager::destroySurface(EGLSurface surface) {
233    if (isCurrent(surface)) {
234        makeCurrent(EGL_NO_SURFACE);
235    }
236    if (!eglDestroySurface(mEglDisplay, surface)) {
237        ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, eglErrorString());
238    }
239}
240
241void EglManager::destroy() {
242    if (mEglDisplay == EGL_NO_DISPLAY) return;
243
244    mRenderThread.setGrContext(nullptr);
245    mRenderThread.renderState().onGLContextDestroyed();
246    eglDestroyContext(mEglDisplay, mEglContext);
247    eglDestroySurface(mEglDisplay, mPBufferSurface);
248    eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
249    eglTerminate(mEglDisplay);
250    eglReleaseThread();
251
252    mEglDisplay = EGL_NO_DISPLAY;
253    mEglContext = EGL_NO_CONTEXT;
254    mPBufferSurface = EGL_NO_SURFACE;
255    mCurrentSurface = EGL_NO_SURFACE;
256}
257
258bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) {
259    if (isCurrent(surface)) return false;
260
261    if (surface == EGL_NO_SURFACE) {
262        // Ensure we always have a valid surface & context
263        surface = mPBufferSurface;
264    }
265    if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
266        if (errOut) {
267            *errOut = eglGetError();
268            ALOGW("Failed to make current on surface %p, error=%s",
269                    (void*)surface, egl_error_str(*errOut));
270        } else {
271            LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
272                    (void*)surface, eglErrorString());
273        }
274    }
275    mCurrentSurface = surface;
276    return true;
277}
278
279EGLint EglManager::queryBufferAge(EGLSurface surface) {
280    switch (mSwapBehavior) {
281    case SwapBehavior::Discard:
282        return 0;
283    case SwapBehavior::Preserved:
284        return 1;
285    case SwapBehavior::BufferAge:
286        EGLint bufferAge;
287        eglQuerySurface(mEglDisplay, surface, EGL_BUFFER_AGE_EXT, &bufferAge);
288        return bufferAge;
289    }
290    return 0;
291}
292
293Frame EglManager::beginFrame(EGLSurface surface) {
294    LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
295            "Tried to beginFrame on EGL_NO_SURFACE!");
296    makeCurrent(surface);
297    Frame frame;
298    frame.mSurface = surface;
299    eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, &frame.mWidth);
300    eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, &frame.mHeight);
301    frame.mBufferAge = queryBufferAge(surface);
302    eglBeginFrame(mEglDisplay, surface);
303    return frame;
304}
305
306void EglManager::damageFrame(const Frame& frame, const SkRect& dirty) {
307#ifdef EGL_KHR_partial_update
308    if (EglExtensions.setDamage && mSwapBehavior == SwapBehavior::BufferAge) {
309        EGLint rects[4];
310        frame.map(dirty, rects);
311        if (!eglSetDamageRegionKHR(mEglDisplay, frame.mSurface, rects, 1)) {
312            LOG_ALWAYS_FATAL("Failed to set damage region on surface %p, error=%s",
313                    (void*)frame.mSurface, eglErrorString());
314        }
315    }
316#endif
317}
318
319bool EglManager::damageRequiresSwap() {
320    return EglExtensions.setDamage && mSwapBehavior == SwapBehavior::BufferAge;
321}
322
323bool EglManager::swapBuffers(const Frame& frame, const SkRect& screenDirty) {
324
325    if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
326        ATRACE_NAME("Finishing GPU work");
327        fence();
328    }
329
330    EGLint rects[4];
331    frame.map(screenDirty, rects);
332    eglSwapBuffersWithDamageKHR(mEglDisplay, frame.mSurface, rects,
333            screenDirty.isEmpty() ? 0 : 1);
334
335    EGLint err = eglGetError();
336    if (CC_LIKELY(err == EGL_SUCCESS)) {
337        return true;
338    }
339    if (err == EGL_BAD_SURFACE || err == EGL_BAD_NATIVE_WINDOW) {
340        // For some reason our surface was destroyed out from under us
341        // This really shouldn't happen, but if it does we can recover easily
342        // by just not trying to use the surface anymore
343        ALOGW("swapBuffers encountered EGL error %d on %p, halting rendering...",
344                err, frame.mSurface);
345        return false;
346    }
347    LOG_ALWAYS_FATAL("Encountered EGL error %d %s during rendering",
348            err, egl_error_str(err));
349    // Impossible to hit this, but the compiler doesn't know that
350    return false;
351}
352
353void EglManager::fence() {
354    EGLSyncKHR fence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, NULL);
355    eglClientWaitSyncKHR(mEglDisplay, fence,
356            EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR);
357    eglDestroySyncKHR(mEglDisplay, fence);
358}
359
360bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) {
361    if (mSwapBehavior != SwapBehavior::Preserved) return false;
362
363    bool preserved = eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR,
364            preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED);
365    if (!preserved) {
366        ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
367                (void*) surface, eglErrorString());
368        // Maybe it's already set?
369        EGLint swapBehavior;
370        if (eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &swapBehavior)) {
371            preserved = (swapBehavior == EGL_BUFFER_PRESERVED);
372        } else {
373            ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
374                                (void*) surface, eglErrorString());
375        }
376    }
377
378    return preserved;
379}
380
381} /* namespace renderthread */
382} /* namespace uirenderer */
383} /* namespace android */
384