Files
frameworks_base/libs/hwui/renderthread/EglManager.cpp
Brian Osman da6ad83507 Change how distance field paths are disabled
The path renderer bitfield will soon be private (for Skia testing
only), so switch to using the public flag.

Test: Simple refactoring change, still builds
Change-Id: I6715ba8addd0e104a46842f2398320d1904bb187
2017-08-30 09:33:44 -04:00

475 lines
17 KiB
C++

/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "EglManager.h"
#include <string>
#include "utils/StringUtils.h"
#include <cutils/properties.h>
#include <log/log.h>
#include "Caches.h"
#include "DeviceInfo.h"
#include "Frame.h"
#include "Properties.h"
#include "RenderThread.h"
#include "renderstate/RenderState.h"
#include "Texture.h"
#include <EGL/eglext.h>
#include <GrContextOptions.h>
#include <gl/GrGLInterface.h>
#ifdef HWUI_GLES_WRAP_ENABLED
#include "debug/GlesDriver.h"
#endif
#define GLES_VERSION 2
// Android-specific addition that is used to show when frames began in systrace
EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
namespace android {
namespace uirenderer {
namespace renderthread {
#define ERROR_CASE(x) case x: return #x;
static const char* egl_error_str(EGLint error) {
switch (error) {
ERROR_CASE(EGL_SUCCESS)
ERROR_CASE(EGL_NOT_INITIALIZED)
ERROR_CASE(EGL_BAD_ACCESS)
ERROR_CASE(EGL_BAD_ALLOC)
ERROR_CASE(EGL_BAD_ATTRIBUTE)
ERROR_CASE(EGL_BAD_CONFIG)
ERROR_CASE(EGL_BAD_CONTEXT)
ERROR_CASE(EGL_BAD_CURRENT_SURFACE)
ERROR_CASE(EGL_BAD_DISPLAY)
ERROR_CASE(EGL_BAD_MATCH)
ERROR_CASE(EGL_BAD_NATIVE_PIXMAP)
ERROR_CASE(EGL_BAD_NATIVE_WINDOW)
ERROR_CASE(EGL_BAD_PARAMETER)
ERROR_CASE(EGL_BAD_SURFACE)
ERROR_CASE(EGL_CONTEXT_LOST)
default:
return "Unknown error";
}
}
const char* EglManager::eglErrorString() {
return egl_error_str(eglGetError());
}
static struct {
bool bufferAge = false;
bool setDamage = false;
bool noConfigContext = false;
bool pixelFormatFloat = false;
bool glColorSpace = false;
bool scRGB = false;
} EglExtensions;
EglManager::EglManager(RenderThread& thread)
: mRenderThread(thread)
, mEglDisplay(EGL_NO_DISPLAY)
, mEglConfig(nullptr)
, mEglConfigWideGamut(nullptr)
, mEglContext(EGL_NO_CONTEXT)
, mPBufferSurface(EGL_NO_SURFACE)
, mCurrentSurface(EGL_NO_SURFACE) {
}
void EglManager::initialize() {
if (hasEglContext()) return;
ATRACE_NAME("Creating EGLContext");
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
"Failed to get EGL_DEFAULT_DISPLAY! err=%s", eglErrorString());
EGLint major, minor;
LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
"Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString());
ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
initExtensions();
// Now that extensions are loaded, pick a swap behavior
if (Properties::enablePartialUpdates) {
// An Adreno driver bug is causing rendering problems for SkiaGL with
// buffer age swap behavior (b/31957043). To temporarily workaround,
// we will use preserved swap behavior.
if (Properties::useBufferAge && EglExtensions.bufferAge) {
mSwapBehavior = SwapBehavior::BufferAge;
} else {
mSwapBehavior = SwapBehavior::Preserved;
}
}
loadConfigs();
createContext();
createPBufferSurface();
makeCurrent(mPBufferSurface);
DeviceInfo::initialize();
mRenderThread.renderState().onGLContextCreated();
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
#ifdef HWUI_GLES_WRAP_ENABLED
debug::GlesDriver* driver = debug::GlesDriver::get();
sk_sp<const GrGLInterface> glInterface(driver->getSkiaInterface());
#else
sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
#endif
LOG_ALWAYS_FATAL_IF(!glInterface.get());
GrContextOptions options;
options.fDisableDistanceFieldPaths = true;
mRenderThread.cacheManager().configureContext(&options);
mRenderThread.setGrContext(GrContext::Create(GrBackend::kOpenGL_GrBackend,
(GrBackendContext)glInterface.get(), options));
}
}
void EglManager::initExtensions() {
auto extensions = StringUtils::split(
eglQueryString(mEglDisplay, EGL_EXTENSIONS));
// For our purposes we don't care if EGL_BUFFER_AGE is a result of
// EGL_EXT_buffer_age or EGL_KHR_partial_update as our usage is covered
// under EGL_KHR_partial_update and we don't need the expanded scope
// that EGL_EXT_buffer_age provides.
EglExtensions.bufferAge = extensions.has("EGL_EXT_buffer_age")
|| extensions.has("EGL_KHR_partial_update");
EglExtensions.setDamage = extensions.has("EGL_KHR_partial_update");
LOG_ALWAYS_FATAL_IF(!extensions.has("EGL_KHR_swap_buffers_with_damage"),
"Missing required extension EGL_KHR_swap_buffers_with_damage");
EglExtensions.glColorSpace = extensions.has("EGL_KHR_gl_colorspace");
EglExtensions.noConfigContext = extensions.has("EGL_KHR_no_config_context");
EglExtensions.pixelFormatFloat = extensions.has("EGL_EXT_pixel_format_float");
#ifdef ANDROID_ENABLE_LINEAR_BLENDING
EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb_linear");
#else
EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb");
#endif
}
bool EglManager::hasEglContext() {
return mEglDisplay != EGL_NO_DISPLAY;
}
void EglManager::loadConfigs() {
ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
EGLint swapBehavior = (mSwapBehavior == SwapBehavior::Preserved)
? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
EGLint attribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 0,
EGL_CONFIG_CAVEAT, EGL_NONE,
EGL_STENCIL_SIZE, Stencil::getStencilSize(),
EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
EGL_NONE
};
EGLint numConfigs = 1;
if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, numConfigs, &numConfigs)
|| numConfigs != 1) {
if (mSwapBehavior == SwapBehavior::Preserved) {
// Try again without dirty regions enabled
ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
mSwapBehavior = SwapBehavior::Discard;
loadConfigs();
return; // the call to loadConfigs() we just made picks the wide gamut config
} else {
// Failed to get a valid config
LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString());
}
}
if (EglExtensions.pixelFormatFloat) {
// If we reached this point, we have a valid swap behavior
EGLint attribs16F[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT,
EGL_RED_SIZE, 16,
EGL_GREEN_SIZE, 16,
EGL_BLUE_SIZE, 16,
EGL_ALPHA_SIZE, 16,
EGL_DEPTH_SIZE, 0,
EGL_STENCIL_SIZE, Stencil::getStencilSize(),
EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
EGL_NONE
};
numConfigs = 1;
if (!eglChooseConfig(mEglDisplay, attribs16F, &mEglConfigWideGamut, numConfigs, &numConfigs)
|| numConfigs != 1) {
LOG_ALWAYS_FATAL(
"Device claims wide gamut support, cannot find matching config, error = %s",
eglErrorString());
}
}
}
void EglManager::createContext() {
EGLint attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION,
EGL_NONE
};
mEglContext = eglCreateContext(mEglDisplay,
EglExtensions.noConfigContext ? ((EGLConfig) nullptr) : mEglConfig,
EGL_NO_CONTEXT, attribs);
LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
"Failed to create context, error = %s", eglErrorString());
}
void EglManager::createPBufferSurface() {
LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
"usePBufferSurface() called on uninitialized GlobalContext!");
if (mPBufferSurface == EGL_NO_SURFACE) {
EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
}
}
EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorGamut) {
initialize();
wideColorGamut = wideColorGamut && EglExtensions.glColorSpace && EglExtensions.scRGB
&& EglExtensions.pixelFormatFloat && EglExtensions.noConfigContext;
// The color space we want to use depends on whether linear blending is turned
// on and whether the app has requested wide color gamut rendering. When wide
// color gamut rendering is off, the app simply renders in the display's native
// color gamut.
//
// When wide gamut rendering is off:
// - Blending is done by default in gamma space, which requires using a
// linear EGL color space (the GPU uses the color values as is)
// - If linear blending is on, we must use the sRGB EGL color space (the
// GPU will perform sRGB to linear and linear to SRGB conversions before
// and after blending)
//
// When wide gamut rendering is on we cannot rely on the GPU performing
// linear blending for us. We use two different color spaces to tag the
// surface appropriately for SurfaceFlinger:
// - Gamma blending (default) requires the use of the scRGB-nl color space
// - Linear blending requires the use of the scRGB color space
// Not all Android targets support the EGL_GL_COLOR_SPACE_KHR extension
// We insert to placeholders to set EGL_GL_COLORSPACE_KHR and its value.
// According to section 3.4.1 of the EGL specification, the attributes
// list is considered empty if the first entry is EGL_NONE
EGLint attribs[] = {
EGL_NONE, EGL_NONE,
EGL_NONE
};
if (EglExtensions.glColorSpace) {
attribs[0] = EGL_GL_COLORSPACE_KHR;
#ifdef ANDROID_ENABLE_LINEAR_BLENDING
if (wideColorGamut) {
attribs[1] = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT;
} else {
attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR;
}
#else
if (wideColorGamut) {
attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
} else {
attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
}
#endif
}
EGLSurface surface = eglCreateWindowSurface(mEglDisplay,
wideColorGamut ? mEglConfigWideGamut : mEglConfig, window, attribs);
LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
"Failed to create EGLSurface for window %p, eglErr = %s",
(void*) window, eglErrorString());
if (mSwapBehavior != SwapBehavior::Preserved) {
LOG_ALWAYS_FATAL_IF(eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) == EGL_FALSE,
"Failed to set swap behavior to destroyed for window %p, eglErr = %s",
(void*) window, eglErrorString());
}
return surface;
}
void EglManager::destroySurface(EGLSurface surface) {
if (isCurrent(surface)) {
makeCurrent(EGL_NO_SURFACE);
}
if (!eglDestroySurface(mEglDisplay, surface)) {
ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, eglErrorString());
}
}
void EglManager::destroy() {
if (mEglDisplay == EGL_NO_DISPLAY) return;
mRenderThread.setGrContext(nullptr);
mRenderThread.renderState().onGLContextDestroyed();
eglDestroyContext(mEglDisplay, mEglContext);
eglDestroySurface(mEglDisplay, mPBufferSurface);
eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(mEglDisplay);
eglReleaseThread();
mEglDisplay = EGL_NO_DISPLAY;
mEglContext = EGL_NO_CONTEXT;
mPBufferSurface = EGL_NO_SURFACE;
mCurrentSurface = EGL_NO_SURFACE;
}
bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) {
if (isCurrent(surface)) return false;
if (surface == EGL_NO_SURFACE) {
// Ensure we always have a valid surface & context
surface = mPBufferSurface;
}
if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
if (errOut) {
*errOut = eglGetError();
ALOGW("Failed to make current on surface %p, error=%s",
(void*)surface, egl_error_str(*errOut));
} else {
LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
(void*)surface, eglErrorString());
}
}
mCurrentSurface = surface;
if (Properties::disableVsync) {
eglSwapInterval(mEglDisplay, 0);
}
return true;
}
EGLint EglManager::queryBufferAge(EGLSurface surface) {
switch (mSwapBehavior) {
case SwapBehavior::Discard:
return 0;
case SwapBehavior::Preserved:
return 1;
case SwapBehavior::BufferAge:
EGLint bufferAge;
eglQuerySurface(mEglDisplay, surface, EGL_BUFFER_AGE_EXT, &bufferAge);
return bufferAge;
}
return 0;
}
Frame EglManager::beginFrame(EGLSurface surface) {
LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
"Tried to beginFrame on EGL_NO_SURFACE!");
makeCurrent(surface);
Frame frame;
frame.mSurface = surface;
eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, &frame.mWidth);
eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, &frame.mHeight);
frame.mBufferAge = queryBufferAge(surface);
eglBeginFrame(mEglDisplay, surface);
return frame;
}
void EglManager::damageFrame(const Frame& frame, const SkRect& dirty) {
#ifdef EGL_KHR_partial_update
if (EglExtensions.setDamage && mSwapBehavior == SwapBehavior::BufferAge) {
EGLint rects[4];
frame.map(dirty, rects);
if (!eglSetDamageRegionKHR(mEglDisplay, frame.mSurface, rects, 1)) {
LOG_ALWAYS_FATAL("Failed to set damage region on surface %p, error=%s",
(void*)frame.mSurface, eglErrorString());
}
}
#endif
}
bool EglManager::damageRequiresSwap() {
return EglExtensions.setDamage && mSwapBehavior == SwapBehavior::BufferAge;
}
bool EglManager::swapBuffers(const Frame& frame, const SkRect& screenDirty) {
if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
ATRACE_NAME("Finishing GPU work");
fence();
}
EGLint rects[4];
frame.map(screenDirty, rects);
eglSwapBuffersWithDamageKHR(mEglDisplay, frame.mSurface, rects,
screenDirty.isEmpty() ? 0 : 1);
EGLint err = eglGetError();
if (CC_LIKELY(err == EGL_SUCCESS)) {
return true;
}
if (err == EGL_BAD_SURFACE || err == EGL_BAD_NATIVE_WINDOW) {
// For some reason our surface was destroyed out from under us
// This really shouldn't happen, but if it does we can recover easily
// by just not trying to use the surface anymore
ALOGW("swapBuffers encountered EGL error %d on %p, halting rendering...",
err, frame.mSurface);
return false;
}
LOG_ALWAYS_FATAL("Encountered EGL error %d %s during rendering",
err, egl_error_str(err));
// Impossible to hit this, but the compiler doesn't know that
return false;
}
void EglManager::fence() {
EGLSyncKHR fence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, NULL);
eglClientWaitSyncKHR(mEglDisplay, fence,
EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR);
eglDestroySyncKHR(mEglDisplay, fence);
}
bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) {
if (mSwapBehavior != SwapBehavior::Preserved) return false;
bool preserved = eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR,
preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED);
if (!preserved) {
ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
(void*) surface, eglErrorString());
// Maybe it's already set?
EGLint swapBehavior;
if (eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &swapBehavior)) {
preserved = (swapBehavior == EGL_BUFFER_PRESERVED);
} else {
ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
(void*) surface, eglErrorString());
}
}
return preserved;
}
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */