| // |
| // Copyright (c) 2013 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| #include "util/EGLWindow.h" |
| |
| #include <cassert> |
| #include <iostream> |
| #include <vector> |
| |
| #include <string.h> |
| |
| #include "platform/Platform.h" |
| #include "util/OSWindow.h" |
| #include "util/system_utils.h" |
| |
| EGLPlatformParameters::EGLPlatformParameters() |
| : renderer(EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE), |
| majorVersion(EGL_DONT_CARE), |
| minorVersion(EGL_DONT_CARE), |
| deviceType(EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE), |
| presentPath(EGL_DONT_CARE) |
| {} |
| |
| EGLPlatformParameters::EGLPlatformParameters(EGLint renderer) |
| : renderer(renderer), |
| majorVersion(EGL_DONT_CARE), |
| minorVersion(EGL_DONT_CARE), |
| deviceType(EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE), |
| presentPath(EGL_DONT_CARE) |
| {} |
| |
| EGLPlatformParameters::EGLPlatformParameters(EGLint renderer, |
| EGLint majorVersion, |
| EGLint minorVersion, |
| EGLint deviceType) |
| : renderer(renderer), |
| majorVersion(majorVersion), |
| minorVersion(minorVersion), |
| deviceType(deviceType), |
| presentPath(EGL_DONT_CARE) |
| {} |
| |
| EGLPlatformParameters::EGLPlatformParameters(EGLint renderer, |
| EGLint majorVersion, |
| EGLint minorVersion, |
| EGLint deviceType, |
| EGLint presentPath) |
| : renderer(renderer), |
| majorVersion(majorVersion), |
| minorVersion(minorVersion), |
| deviceType(deviceType), |
| presentPath(presentPath) |
| {} |
| |
| bool operator<(const EGLPlatformParameters &a, const EGLPlatformParameters &b) |
| { |
| if (a.renderer != b.renderer) |
| { |
| return a.renderer < b.renderer; |
| } |
| |
| if (a.majorVersion != b.majorVersion) |
| { |
| return a.majorVersion < b.majorVersion; |
| } |
| |
| if (a.minorVersion != b.minorVersion) |
| { |
| return a.minorVersion < b.minorVersion; |
| } |
| |
| if (a.deviceType != b.deviceType) |
| { |
| return a.deviceType < b.deviceType; |
| } |
| |
| return a.presentPath < b.presentPath; |
| } |
| |
| bool operator==(const EGLPlatformParameters &a, const EGLPlatformParameters &b) |
| { |
| return (a.renderer == b.renderer) && (a.majorVersion == b.majorVersion) && |
| (a.minorVersion == b.minorVersion) && (a.deviceType == b.deviceType) && |
| (a.presentPath == b.presentPath); |
| } |
| |
| // GLWindowBase implementation. |
| GLWindowBase::GLWindowBase(EGLint glesMajorVersion, EGLint glesMinorVersion) |
| : mClientMajorVersion(glesMajorVersion), |
| mClientMinorVersion(glesMinorVersion), |
| mRedBits(-1), |
| mGreenBits(-1), |
| mBlueBits(-1), |
| mAlphaBits(-1), |
| mDepthBits(-1), |
| mStencilBits(-1), |
| mSwapInterval(-1), |
| mPlatformMethods(nullptr) |
| {} |
| |
| GLWindowBase::~GLWindowBase() = default; |
| |
| // EGLWindow implementation. |
| EGLWindow::EGLWindow(EGLint glesMajorVersion, |
| EGLint glesMinorVersion, |
| const EGLPlatformParameters &platform) |
| : GLWindowBase(glesMajorVersion, glesMinorVersion), |
| mDisplay(EGL_NO_DISPLAY), |
| mSurface(EGL_NO_SURFACE), |
| mContext(EGL_NO_CONTEXT), |
| mEGLMajorVersion(0), |
| mEGLMinorVersion(0), |
| mPlatform(platform), |
| mComponentType(EGL_COLOR_COMPONENT_TYPE_FIXED_EXT), |
| mMultisample(false), |
| mDebug(false), |
| mNoError(false), |
| mExtensionsEnabled(), |
| mBindGeneratesResource(true), |
| mClientArraysEnabled(true), |
| mRobustAccess(false), |
| mSamples(-1), |
| mDebugLayersEnabled(), |
| mContextProgramCacheEnabled(), |
| mContextVirtualization() |
| {} |
| |
| EGLWindow::~EGLWindow() |
| { |
| destroyGL(); |
| } |
| |
| void EGLWindow::swap() |
| { |
| eglSwapBuffers(mDisplay, mSurface); |
| } |
| |
| EGLConfig EGLWindow::getConfig() const |
| { |
| return mConfig; |
| } |
| |
| EGLDisplay EGLWindow::getDisplay() const |
| { |
| return mDisplay; |
| } |
| |
| EGLSurface EGLWindow::getSurface() const |
| { |
| return mSurface; |
| } |
| |
| EGLContext EGLWindow::getContext() const |
| { |
| return mContext; |
| } |
| |
| bool EGLWindow::initializeGL(OSWindow *osWindow, angle::Library *glWindowingLibrary) |
| { |
| if (!initializeDisplayAndSurface(osWindow, glWindowingLibrary)) |
| return false; |
| return initializeContext(); |
| } |
| |
| bool EGLWindow::initializeDisplayAndSurface(OSWindow *osWindow, angle::Library *glWindowingLibrary) |
| { |
| #if defined(ANGLE_USE_UTIL_LOADER) |
| PFNEGLGETPROCADDRESSPROC getProcAddress; |
| glWindowingLibrary->getAs("eglGetProcAddress", &getProcAddress); |
| if (!getProcAddress) |
| { |
| return false; |
| } |
| |
| // Likely we will need to use a fallback to Library::getAs on non-ANGLE platforms. |
| angle::LoadEGL(getProcAddress); |
| #endif // defined(ANGLE_USE_UTIL_LOADER) |
| |
| std::vector<EGLAttrib> displayAttributes; |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); |
| displayAttributes.push_back(mPlatform.renderer); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE); |
| displayAttributes.push_back(mPlatform.majorVersion); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE); |
| displayAttributes.push_back(mPlatform.minorVersion); |
| |
| if (mPlatform.deviceType != EGL_DONT_CARE) |
| { |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE); |
| displayAttributes.push_back(mPlatform.deviceType); |
| } |
| |
| if (mPlatform.presentPath != EGL_DONT_CARE) |
| { |
| const char *extensionString = |
| static_cast<const char *>(eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS)); |
| if (strstr(extensionString, "EGL_ANGLE_experimental_present_path") == nullptr) |
| { |
| destroyGL(); |
| return false; |
| } |
| |
| displayAttributes.push_back(EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE); |
| displayAttributes.push_back(mPlatform.presentPath); |
| } |
| |
| // Set debug layer settings if requested. |
| if (mDebugLayersEnabled.valid()) |
| { |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE); |
| displayAttributes.push_back(mDebugLayersEnabled.value() ? EGL_TRUE : EGL_FALSE); |
| } |
| |
| if (mContextVirtualization.valid()) |
| { |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE); |
| displayAttributes.push_back(mContextVirtualization.value() ? EGL_TRUE : EGL_FALSE); |
| } |
| |
| if (mPlatformMethods) |
| { |
| static_assert(sizeof(EGLAttrib) == sizeof(mPlatformMethods), "Unexpected pointer size"); |
| displayAttributes.push_back(EGL_PLATFORM_ANGLE_PLATFORM_METHODS_ANGLEX); |
| displayAttributes.push_back(reinterpret_cast<EGLAttrib>(mPlatformMethods)); |
| } |
| |
| displayAttributes.push_back(EGL_NONE); |
| |
| mDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE, |
| reinterpret_cast<void *>(osWindow->getNativeDisplay()), |
| &displayAttributes[0]); |
| if (mDisplay == EGL_NO_DISPLAY) |
| { |
| destroyGL(); |
| return false; |
| } |
| |
| if (eglInitialize(mDisplay, &mEGLMajorVersion, &mEGLMinorVersion) == EGL_FALSE) |
| { |
| destroyGL(); |
| return false; |
| } |
| |
| const char *displayExtensions = eglQueryString(mDisplay, EGL_EXTENSIONS); |
| |
| std::vector<EGLint> configAttributes = { |
| EGL_RED_SIZE, (mRedBits >= 0) ? mRedBits : EGL_DONT_CARE, |
| EGL_GREEN_SIZE, (mGreenBits >= 0) ? mGreenBits : EGL_DONT_CARE, |
| EGL_BLUE_SIZE, (mBlueBits >= 0) ? mBlueBits : EGL_DONT_CARE, |
| EGL_ALPHA_SIZE, (mAlphaBits >= 0) ? mAlphaBits : EGL_DONT_CARE, |
| EGL_DEPTH_SIZE, (mDepthBits >= 0) ? mDepthBits : EGL_DONT_CARE, |
| EGL_STENCIL_SIZE, (mStencilBits >= 0) ? mStencilBits : EGL_DONT_CARE, |
| EGL_SAMPLE_BUFFERS, mMultisample ? 1 : 0, |
| EGL_SAMPLES, (mSamples >= 0) ? mSamples : EGL_DONT_CARE, |
| }; |
| |
| // Add dynamic attributes |
| bool hasPixelFormatFloat = strstr(displayExtensions, "EGL_EXT_pixel_format_float") != nullptr; |
| if (!hasPixelFormatFloat && mComponentType != EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) |
| { |
| destroyGL(); |
| return false; |
| } |
| if (hasPixelFormatFloat) |
| { |
| configAttributes.push_back(EGL_COLOR_COMPONENT_TYPE_EXT); |
| configAttributes.push_back(mComponentType); |
| } |
| |
| // Finish the attribute list |
| configAttributes.push_back(EGL_NONE); |
| |
| if (!FindEGLConfig(mDisplay, configAttributes.data(), &mConfig)) |
| { |
| std::cout << "Could not find a suitable EGL config!" << std::endl; |
| destroyGL(); |
| return false; |
| } |
| |
| eglGetConfigAttrib(mDisplay, mConfig, EGL_RED_SIZE, &mRedBits); |
| eglGetConfigAttrib(mDisplay, mConfig, EGL_GREEN_SIZE, &mGreenBits); |
| eglGetConfigAttrib(mDisplay, mConfig, EGL_BLUE_SIZE, &mBlueBits); |
| eglGetConfigAttrib(mDisplay, mConfig, EGL_ALPHA_SIZE, &mAlphaBits); |
| eglGetConfigAttrib(mDisplay, mConfig, EGL_DEPTH_SIZE, &mDepthBits); |
| eglGetConfigAttrib(mDisplay, mConfig, EGL_STENCIL_SIZE, &mStencilBits); |
| eglGetConfigAttrib(mDisplay, mConfig, EGL_SAMPLES, &mSamples); |
| |
| std::vector<EGLint> surfaceAttributes; |
| if (strstr(displayExtensions, "EGL_NV_post_sub_buffer") != nullptr) |
| { |
| surfaceAttributes.push_back(EGL_POST_SUB_BUFFER_SUPPORTED_NV); |
| surfaceAttributes.push_back(EGL_TRUE); |
| } |
| |
| bool hasRobustResourceInit = |
| strstr(displayExtensions, "EGL_ANGLE_robust_resource_initialization") != nullptr; |
| if (hasRobustResourceInit && mRobustResourceInit.valid()) |
| { |
| surfaceAttributes.push_back(EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE); |
| surfaceAttributes.push_back(mRobustResourceInit.value() ? EGL_TRUE : EGL_FALSE); |
| } |
| |
| surfaceAttributes.push_back(EGL_NONE); |
| |
| mSurface = eglCreateWindowSurface(mDisplay, mConfig, osWindow->getNativeWindow(), |
| &surfaceAttributes[0]); |
| if (eglGetError() != EGL_SUCCESS || (mSurface == EGL_NO_SURFACE)) |
| { |
| destroyGL(); |
| return false; |
| } |
| |
| #if defined(ANGLE_USE_UTIL_LOADER) |
| angle::LoadGLES(eglGetProcAddress); |
| #endif // defined(ANGLE_USE_UTIL_LOADER) |
| |
| return true; |
| } |
| |
| EGLContext EGLWindow::createContext(EGLContext share) const |
| { |
| const char *displayExtensions = eglQueryString(mDisplay, EGL_EXTENSIONS); |
| |
| // EGL_KHR_create_context is required to request a ES3+ context. |
| bool hasKHRCreateContext = strstr(displayExtensions, "EGL_KHR_create_context") != nullptr; |
| if (mClientMajorVersion > 2 && !(mEGLMajorVersion > 1 || mEGLMinorVersion >= 5) && |
| !hasKHRCreateContext) |
| { |
| return EGL_NO_CONTEXT; |
| } |
| |
| bool hasWebGLCompatibility = |
| strstr(displayExtensions, "EGL_ANGLE_create_context_webgl_compatibility") != nullptr; |
| if (mWebGLCompatibility.valid() && !hasWebGLCompatibility) |
| { |
| return EGL_NO_CONTEXT; |
| } |
| |
| bool hasCreateContextExtensionsEnabled = |
| strstr(displayExtensions, "EGL_ANGLE_create_context_extensions_enabled") != nullptr; |
| if (mExtensionsEnabled.valid() && !hasCreateContextExtensionsEnabled) |
| { |
| return EGL_NO_CONTEXT; |
| } |
| |
| bool hasRobustness = strstr(displayExtensions, "EGL_EXT_create_context_robustness") != nullptr; |
| if (mRobustAccess && !hasRobustness) |
| { |
| return EGL_NO_CONTEXT; |
| } |
| |
| bool hasBindGeneratesResource = |
| strstr(displayExtensions, "EGL_CHROMIUM_create_context_bind_generates_resource") != nullptr; |
| if (!mBindGeneratesResource && !hasBindGeneratesResource) |
| { |
| return EGL_NO_CONTEXT; |
| } |
| |
| bool hasClientArraysExtension = |
| strstr(displayExtensions, "EGL_ANGLE_create_context_client_arrays") != nullptr; |
| if (!mClientArraysEnabled && !hasClientArraysExtension) |
| { |
| // Non-default state requested without the extension present |
| return EGL_NO_CONTEXT; |
| } |
| |
| bool hasProgramCacheControlExtension = |
| strstr(displayExtensions, "EGL_ANGLE_program_cache_control ") != nullptr; |
| if (mContextProgramCacheEnabled.valid() && !hasProgramCacheControlExtension) |
| { |
| return EGL_NO_CONTEXT; |
| } |
| |
| eglBindAPI(EGL_OPENGL_ES_API); |
| if (eglGetError() != EGL_SUCCESS) |
| { |
| return EGL_NO_CONTEXT; |
| } |
| |
| std::vector<EGLint> contextAttributes; |
| if (hasKHRCreateContext) |
| { |
| contextAttributes.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR); |
| contextAttributes.push_back(mClientMajorVersion); |
| |
| contextAttributes.push_back(EGL_CONTEXT_MINOR_VERSION_KHR); |
| contextAttributes.push_back(mClientMinorVersion); |
| |
| contextAttributes.push_back(EGL_CONTEXT_OPENGL_DEBUG); |
| contextAttributes.push_back(mDebug ? EGL_TRUE : EGL_FALSE); |
| |
| // TODO(jmadill): Check for the extension string. |
| // bool hasKHRCreateContextNoError = strstr(displayExtensions, |
| // "EGL_KHR_create_context_no_error") != nullptr; |
| |
| contextAttributes.push_back(EGL_CONTEXT_OPENGL_NO_ERROR_KHR); |
| contextAttributes.push_back(mNoError ? EGL_TRUE : EGL_FALSE); |
| |
| if (mWebGLCompatibility.valid()) |
| { |
| contextAttributes.push_back(EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE); |
| contextAttributes.push_back(mWebGLCompatibility.value() ? EGL_TRUE : EGL_FALSE); |
| } |
| |
| if (mExtensionsEnabled.valid()) |
| { |
| contextAttributes.push_back(EGL_EXTENSIONS_ENABLED_ANGLE); |
| contextAttributes.push_back(mExtensionsEnabled.value() ? EGL_TRUE : EGL_FALSE); |
| } |
| |
| if (hasRobustness) |
| { |
| contextAttributes.push_back(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT); |
| contextAttributes.push_back(mRobustAccess ? EGL_TRUE : EGL_FALSE); |
| } |
| |
| if (hasBindGeneratesResource) |
| { |
| contextAttributes.push_back(EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM); |
| contextAttributes.push_back(mBindGeneratesResource ? EGL_TRUE : EGL_FALSE); |
| } |
| |
| if (hasClientArraysExtension) |
| { |
| contextAttributes.push_back(EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE); |
| contextAttributes.push_back(mClientArraysEnabled ? EGL_TRUE : EGL_FALSE); |
| } |
| |
| if (mContextProgramCacheEnabled.valid()) |
| { |
| contextAttributes.push_back(EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE); |
| contextAttributes.push_back(mContextProgramCacheEnabled.value() ? EGL_TRUE : EGL_FALSE); |
| } |
| |
| bool hasRobustResourceInit = |
| strstr(displayExtensions, "EGL_ANGLE_robust_resource_initialization") != nullptr; |
| if (hasRobustResourceInit && mRobustResourceInit.valid()) |
| { |
| contextAttributes.push_back(EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE); |
| contextAttributes.push_back(mRobustResourceInit.value() ? EGL_TRUE : EGL_FALSE); |
| } |
| } |
| contextAttributes.push_back(EGL_NONE); |
| |
| EGLContext context = eglCreateContext(mDisplay, mConfig, nullptr, &contextAttributes[0]); |
| if (eglGetError() != EGL_SUCCESS) |
| { |
| return EGL_NO_CONTEXT; |
| } |
| |
| return context; |
| } |
| |
| bool EGLWindow::initializeContext() |
| { |
| mContext = createContext(EGL_NO_CONTEXT); |
| if (mContext == EGL_NO_CONTEXT) |
| { |
| destroyGL(); |
| return false; |
| } |
| |
| eglMakeCurrent(mDisplay, mSurface, mSurface, mContext); |
| if (eglGetError() != EGL_SUCCESS) |
| { |
| destroyGL(); |
| return false; |
| } |
| |
| if (mSwapInterval != -1) |
| { |
| eglSwapInterval(mDisplay, mSwapInterval); |
| } |
| |
| return true; |
| } |
| |
| void EGLWindow::destroyGL() |
| { |
| if (mSurface != EGL_NO_SURFACE) |
| { |
| assert(mDisplay != EGL_NO_DISPLAY); |
| eglDestroySurface(mDisplay, mSurface); |
| mSurface = EGL_NO_SURFACE; |
| } |
| |
| if (mContext != EGL_NO_CONTEXT) |
| { |
| assert(mDisplay != EGL_NO_DISPLAY); |
| eglDestroyContext(mDisplay, mContext); |
| mContext = EGL_NO_CONTEXT; |
| } |
| |
| if (mDisplay != EGL_NO_DISPLAY) |
| { |
| eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| eglTerminate(mDisplay); |
| mDisplay = EGL_NO_DISPLAY; |
| } |
| } |
| |
| bool EGLWindow::isGLInitialized() const |
| { |
| return mSurface != EGL_NO_SURFACE && mContext != EGL_NO_CONTEXT && mDisplay != EGL_NO_DISPLAY; |
| } |
| |
| // Find an EGLConfig that is an exact match for the specified attributes. EGL_FALSE is returned if |
| // the EGLConfig is found. This indicates that the EGLConfig is not supported. |
| EGLBoolean EGLWindow::FindEGLConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *config) |
| { |
| EGLint numConfigs = 0; |
| eglGetConfigs(dpy, nullptr, 0, &numConfigs); |
| std::vector<EGLConfig> allConfigs(numConfigs); |
| eglGetConfigs(dpy, allConfigs.data(), static_cast<EGLint>(allConfigs.size()), &numConfigs); |
| |
| for (size_t i = 0; i < allConfigs.size(); i++) |
| { |
| bool matchFound = true; |
| for (const EGLint *curAttrib = attrib_list; curAttrib[0] != EGL_NONE; curAttrib += 2) |
| { |
| if (curAttrib[1] == EGL_DONT_CARE) |
| { |
| continue; |
| } |
| |
| EGLint actualValue = EGL_DONT_CARE; |
| eglGetConfigAttrib(dpy, allConfigs[i], curAttrib[0], &actualValue); |
| if (curAttrib[1] != actualValue) |
| { |
| matchFound = false; |
| break; |
| } |
| } |
| |
| if (matchFound) |
| { |
| *config = allConfigs[i]; |
| return EGL_TRUE; |
| } |
| } |
| |
| return EGL_FALSE; |
| } |
| |
| void EGLWindow::makeCurrent() |
| { |
| eglMakeCurrent(mDisplay, mSurface, mSurface, mContext); |
| } |
| |
| bool EGLWindow::hasError() const |
| { |
| return eglGetError() != EGL_SUCCESS; |
| } |
| |
| // static |
| bool EGLWindow::ClientExtensionEnabled(const std::string &extName) |
| { |
| return CheckExtensionExists(eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS), extName); |
| } |
| |
| bool CheckExtensionExists(const char *allExtensions, const std::string &extName) |
| { |
| const std::string paddedExtensions = std::string(" ") + allExtensions + std::string(" "); |
| return paddedExtensions.find(std::string(" ") + extName + std::string(" ")) != |
| std::string::npos; |
| } |
| |
| // static |
| void GLWindowBase::Delete(GLWindowBase **window) |
| { |
| delete *window; |
| *window = nullptr; |
| } |
| |
| // static |
| EGLWindow *EGLWindow::New(EGLint glesMajorVersion, |
| EGLint glesMinorVersion, |
| const EGLPlatformParameters &platform) |
| { |
| return new EGLWindow(glesMajorVersion, glesMinorVersion, platform); |
| } |
| |
| // static |
| void EGLWindow::Delete(EGLWindow **window) |
| { |
| delete *window; |
| *window = nullptr; |
| } |