From 6a3feda5ab5c79656dbbdc30bca07c53c964aecd Mon Sep 17 00:00:00 2001 From: Jonathan De Wachter Date: Wed, 28 Sep 2016 18:43:10 +0700 Subject: [PATCH] Make EGL pixel format selection follow the same procedure as the other context types and make OpenGL context version parsing more tolerant of garbage data. Fixes #2395 --- src/SFML/Graphics/RenderTextureImplFBO.cpp | 12 +- src/SFML/Window/EglContext.cpp | 141 ++++++++++++++------- src/SFML/Window/GlContext.cpp | 30 +++-- 3 files changed, 118 insertions(+), 65 deletions(-) diff --git a/src/SFML/Graphics/RenderTextureImplFBO.cpp b/src/SFML/Graphics/RenderTextureImplFBO.cpp index fd4debe4..223a2349 100644 --- a/src/SFML/Graphics/RenderTextureImplFBO.cpp +++ b/src/SFML/Graphics/RenderTextureImplFBO.cpp @@ -263,15 +263,17 @@ bool RenderTextureImplFBO::create(const Vector2u& size, unsigned int textureId, static_cast(size.x), static_cast(size.y))); + m_stencil = true; + #else + m_stencil = false; + err() << "Impossible to create render texture (failed to create the attached depth/stencil buffer)" << std::endl; return false; #endif // SFML_OPENGL_ES - - m_stencil = true; } else if (settings.depthBits) { @@ -355,14 +357,16 @@ bool RenderTextureImplFBO::create(const Vector2u& size, unsigned int textureId, static_cast(size.y))); } + m_multisample = true; + #else + m_multisample = false; + err() << "Impossible to create render texture (failed to create the multisample render buffers)" << std::endl; return false; #endif // SFML_OPENGL_ES - - m_multisample = true; } } diff --git a/src/SFML/Window/EglContext.cpp b/src/SFML/Window/EglContext.cpp index dfe4d7ba..92592185 100644 --- a/src/SFML/Window/EglContext.cpp +++ b/src/SFML/Window/EglContext.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #ifdef SFML_SYSTEM_ANDROID @@ -80,18 +81,27 @@ EGLDisplay getInitializedDisplay() //////////////////////////////////////////////////////////// void ensureInit() { - static bool initialized = false; - if (!initialized) - { - initialized = true; + static std::once_flag flag; - // We don't check the return value since the extension - // flags are cleared even if loading fails - gladLoaderLoadEGL(EGL_NO_DISPLAY); + std::call_once(flag, + []() + { + if (!gladLoaderLoadEGL(EGL_NO_DISPLAY)) + { + // At this point, the failure is unrecoverable + // Dump a message to the console and let the application terminate + sf::err() << "Failed to load EGL entry points" << std::endl; - // Continue loading with a display - gladLoaderLoadEGL(getInitializedDisplay()); - } + assert(false); + + return false; + } + + // Continue loading with a display + gladLoaderLoadEGL(getInitializedDisplay()); + + return true; + }); } } // namespace EglContextImpl } // namespace @@ -287,67 +297,100 @@ EGLConfig EglContext::getBestConfig(EGLDisplay display, unsigned int bitsPerPixe { EglContextImpl::ensureInit(); - // Set our video settings constraint - const EGLint attributes[] = - {EGL_BUFFER_SIZE, - static_cast(bitsPerPixel), - EGL_DEPTH_SIZE, - static_cast(settings.depthBits), - EGL_STENCIL_SIZE, - static_cast(settings.stencilBits), - EGL_SAMPLE_BUFFERS, - settings.antialiasingLevel ? 1 : 0, - EGL_SAMPLES, - static_cast(settings.antialiasingLevel), - EGL_SURFACE_TYPE, - EGL_WINDOW_BIT | EGL_PBUFFER_BIT, - EGL_RENDERABLE_TYPE, - EGL_OPENGL_ES_BIT, - EGL_NONE}; + // Determine the number of available configs + EGLint configCount; + eglCheck(eglGetConfigs(display, nullptr, 0, &configCount)); - EGLint configCount; - EGLConfig configs[1]; + // Retrieve the list of available configs + auto configs = std::make_unique(static_cast(configCount)); - // Ask EGL for the best config matching our video settings - eglCheck(eglChooseConfig(display, attributes, configs, 1, &configCount)); + eglCheck(eglGetConfigs(display, configs.get(), configCount, &configCount)); - // TODO: This should check EGL_CONFORMANT and pick the first conformant configuration. + // Evaluate all the returned configs, and pick the best one + int bestScore = 0x7FFFFFFF; + EGLConfig bestConfig{}; - return configs[0]; + for (std::size_t i = 0; i < static_cast(configCount); ++i) + { + // Check mandatory attributes + int surfaceType; + int renderableType; + eglCheck(eglGetConfigAttrib(display, configs[i], EGL_SURFACE_TYPE, &surfaceType)); + eglCheck(eglGetConfigAttrib(display, configs[i], EGL_RENDERABLE_TYPE, &renderableType)); + if (!(surfaceType & (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) || !(renderableType & EGL_OPENGL_ES_BIT)) + continue; + + // Extract the components of the current config + int red; + int green; + int blue; + int alpha; + int depth; + int stencil; + int multiSampling; + int samples; + int caveat; + eglCheck(eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &red)); + eglCheck(eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &green)); + eglCheck(eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &blue)); + eglCheck(eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &alpha)); + eglCheck(eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &depth)); + eglCheck(eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, &stencil)); + eglCheck(eglGetConfigAttrib(display, configs[i], EGL_SAMPLE_BUFFERS, &multiSampling)); + eglCheck(eglGetConfigAttrib(display, configs[i], EGL_SAMPLES, &samples)); + eglCheck(eglGetConfigAttrib(display, configs[i], EGL_CONFIG_CAVEAT, &caveat)); + + // Evaluate the config + int color = red + green + blue + alpha; + int score = evaluateFormat(bitsPerPixel, settings, color, depth, stencil, multiSampling ? samples : 0, caveat == EGL_NONE, false); + + // If it's better than the current best, make it the new best + if (score < bestScore) + { + bestScore = score; + bestConfig = configs[i]; + } + } + + assert(bestScore < 0x7FFFFFFF); + + return bestConfig; } //////////////////////////////////////////////////////////// void EglContext::updateSettings() { + m_settings.majorVersion = 1; + m_settings.minorVersion = 1; + m_settings.attributeFlags = ContextSettings::Default; + m_settings.depthBits = 0; + m_settings.stencilBits = 0; + m_settings.antialiasingLevel = 0; + EGLBoolean result = EGL_FALSE; EGLint tmp = 0; // Update the internal context settings with the current config eglCheck(result = eglGetConfigAttrib(m_display, m_config, EGL_DEPTH_SIZE, &tmp)); - if (result == EGL_FALSE) - err() << "Failed to retrieve EGL_DEPTH_SIZE" << std::endl; - - m_settings.depthBits = static_cast(tmp); + if (result != EGL_FALSE) + m_settings.depthBits = static_cast(tmp); eglCheck(result = eglGetConfigAttrib(m_display, m_config, EGL_STENCIL_SIZE, &tmp)); - if (result == EGL_FALSE) - err() << "Failed to retrieve EGL_STENCIL_SIZE" << std::endl; + if (result != EGL_FALSE) + m_settings.stencilBits = static_cast(tmp); - m_settings.stencilBits = static_cast(tmp); + eglCheck(result = eglGetConfigAttrib(m_display, m_config, EGL_SAMPLE_BUFFERS, &tmp)); - eglCheck(result = eglGetConfigAttrib(m_display, m_config, EGL_SAMPLES, &tmp)); + if ((result != EGL_FALSE) && tmp) + { + eglCheck(result = eglGetConfigAttrib(m_display, m_config, EGL_SAMPLES, &tmp)); - if (result == EGL_FALSE) - err() << "Failed to retrieve EGL_SAMPLES" << std::endl; - - m_settings.antialiasingLevel = static_cast(tmp); - - m_settings.majorVersion = 1; - m_settings.minorVersion = 1; - m_settings.attributeFlags = ContextSettings::Default; + if (result != EGL_FALSE) + m_settings.antialiasingLevel = static_cast(tmp); + } } diff --git a/src/SFML/Window/GlContext.cpp b/src/SFML/Window/GlContext.cpp index 25d90aeb..ce7336ac 100644 --- a/src/SFML/Window/GlContext.cpp +++ b/src/SFML/Window/GlContext.cpp @@ -275,8 +275,6 @@ std::vector extensions; // Load our extensions vector with the supported extensions void loadExtensions() { - extensions.clear(); - auto glGetErrorFunc = reinterpret_cast(sf::priv::GlContext::getFunction("glGetError")); auto glGetIntegervFunc = reinterpret_cast(sf::priv::GlContext::getFunction("glGetIntegerv")); auto glGetStringFunc = reinterpret_cast(sf::priv::GlContext::getFunction("glGetString")); @@ -290,21 +288,25 @@ void loadExtensions() auto glGetStringiFunc = reinterpret_cast(sf::priv::GlContext::getFunction("glGetStringi")); - if (glGetErrorFunc() == GL_INVALID_ENUM || !glGetStringiFunc) + if (glGetErrorFunc() == GL_INVALID_ENUM || !majorVersion || !glGetStringiFunc) { // Try to load the < 3.0 way const char* extensionString = reinterpret_cast(glGetStringFunc(GL_EXTENSIONS)); - assert(extensionString); - do + if (extensionString) { - const char* extension = extensionString; + extensions.clear(); - while (*extensionString && (*extensionString != ' ')) - ++extensionString; + do + { + const char* extension = extensionString; - extensions.emplace_back(extension, extensionString); - } while (*extensionString++); + while (*extensionString && (*extensionString != ' ')) + ++extensionString; + + extensions.emplace_back(extension, extensionString); + } while (*extensionString++); + } } else { @@ -314,10 +316,14 @@ void loadExtensions() if (numExtensions) { + extensions.clear(); + for (unsigned int i = 0; i < static_cast(numExtensions); ++i) { const char* extensionString = reinterpret_cast(glGetStringiFunc(GL_EXTENSIONS, i)); - extensions.emplace_back(extensionString); + + if (extensionString) + extensions.emplace_back(extensionString); } } } @@ -807,7 +813,7 @@ void GlContext::initialize(const ContextSettings& requestedSettings) glGetIntegervFunc(GL_MAJOR_VERSION, &majorVersion); glGetIntegervFunc(GL_MINOR_VERSION, &minorVersion); - if (glGetErrorFunc() != GL_INVALID_ENUM) + if ((glGetErrorFunc() != GL_INVALID_ENUM) && (majorVersion != 0)) { m_settings.majorVersion = static_cast(majorVersion); m_settings.minorVersion = static_cast(minorVersion);