From df3517f19bf8bd29847316eb2b4f71e0e801719e Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 18 Feb 2024 17:16:46 +0900 Subject: [PATCH] GLContext: Use EGL 1.5 platform selection --- src/util/gl/context.cpp | 16 ++- src/util/gl/context_egl.cpp | 175 +++++++++++++++++++++++----- src/util/gl/context_egl.h | 10 +- src/util/gl/context_egl_wayland.cpp | 38 +++++- src/util/gl/context_egl_wayland.h | 3 +- src/util/gl/context_egl_x11.cpp | 14 ++- src/util/gl/context_egl_x11.h | 3 +- 7 files changed, 206 insertions(+), 53 deletions(-) diff --git a/src/util/gl/context.cpp b/src/util/gl/context.cpp index 48d6b151c..b84abde61 100644 --- a/src/util/gl/context.cpp +++ b/src/util/gl/context.cpp @@ -155,29 +155,33 @@ std::unique_ptr Context::Create(const WindowInfo& wi, Error* error) } std::unique_ptr context; + Error local_error; #if defined(_WIN32) && !defined(_M_ARM64) - context = ContextWGL::Create(wi, versions_to_try, error); + context = ContextWGL::Create(wi, versions_to_try, error ? error : &local_error); #elif defined(__APPLE__) context = ContextAGL::Create(wi, versions_to_try); #elif defined(__ANDROID__) - context = ContextEGLAndroid::Create(wi, versions_to_try, error); + context = ContextEGLAndroid::Create(wi, versions_to_try, error ? error : &local_error); #else #if defined(ENABLE_X11) if (wi.type == WindowInfo::Type::X11) - context = ContextEGLX11::Create(wi, versions_to_try, error); + context = ContextEGLX11::Create(wi, versions_to_try, error ? error : &local_error); #endif #if defined(ENABLE_WAYLAND) if (wi.type == WindowInfo::Type::Wayland) - context = ContextEGLWayland::Create(wi, versions_to_try, error); + context = ContextEGLWayland::Create(wi, versions_to_try, error ? error : &local_error); #endif if (wi.type == WindowInfo::Type::Surfaceless) - context = ContextEGL::Create(wi, versions_to_try, error); + context = ContextEGL::Create(wi, versions_to_try, error ? error : &local_error); #endif if (!context) + { + Log_ErrorFmt("Failed to create GL context: {}", (error ? error : &local_error)->GetDescription()); return nullptr; + } - Log_InfoPrintf("Created a %s context", context->IsGLES() ? "OpenGL ES" : "OpenGL"); + Log_InfoPrint(context->IsGLES() ? "Created an OpenGL ES context" : "Created an OpenGL context"); // TODO: Not thread-safe. static Context* context_being_created; diff --git a/src/util/gl/context_egl.cpp b/src/util/gl/context_egl.cpp index a6ff50c28..b86bff904 100644 --- a/src/util/gl/context_egl.cpp +++ b/src/util/gl/context_egl.cpp @@ -9,6 +9,7 @@ #include "common/log.h" #include +#include #include #include @@ -48,15 +49,34 @@ static void UnloadEGL() static bool LoadGLADEGL(EGLDisplay display, Error* error) { - if (!gladLoadEGL(display, [](const char* name) { return (GLADapiproc)s_egl_library.GetSymbolAddress(name); })) + const int version = + gladLoadEGL(display, [](const char* name) { return (GLADapiproc)s_egl_library.GetSymbolAddress(name); }); + if (version == 0) { Error::SetStringView(error, "Loading GLAD EGL functions failed"); return false; } + Log_DevFmt("GLAD EGL Version: {}.{}", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); return true; } +static std::vector EGLAttribToInt(const EGLAttrib* attribs) +{ + std::vector int_attribs; + if (attribs) + { + for (const EGLAttrib* attrib = attribs; *attrib != EGL_NONE;) + { + int_attribs.push_back(static_cast(*(attrib++))); // key + int_attribs.push_back(static_cast(*(attrib++))); // value + } + int_attribs.push_back(EGL_NONE); + int_attribs.push_back(0); + } + return int_attribs; +} + namespace GL { ContextEGL::ContextEGL(const WindowInfo& wi) : Context(wi) { @@ -85,11 +105,8 @@ bool ContextEGL::Initialize(std::span versions_to_try, Error* err if (!LoadGLADEGL(EGL_NO_DISPLAY, error)) return false; - if (!SetDisplay()) - return false; - - // Re-initialize GLAD. - if (!LoadGLADEGL(m_display, error)) + m_display = GetPlatformDisplay(nullptr, error); + if (m_display == EGL_NO_DISPLAY) return false; int egl_major, egl_minor; @@ -99,15 +116,14 @@ bool ContextEGL::Initialize(std::span versions_to_try, Error* err Error::SetStringFmt(error, "eglInitialize() failed: {} (0x{:X})", gerror, gerror); return false; } - Log_InfoFmt("EGL Version: {}.{}", egl_major, egl_minor); - const char* extensions = eglQueryString(m_display, EGL_EXTENSIONS); - if (extensions) - { - Log_DebugFmt("EGL Extensions: {}", extensions); - m_supports_surfaceless = std::strstr(extensions, "EGL_KHR_surfaceless_context") != nullptr; - } - if (!m_supports_surfaceless) + Log_DevFmt("eglInitialize() version: {}.{}", egl_major, egl_minor); + + // Re-initialize EGL/GLAD. + if (!LoadGLADEGL(m_display, error)) + return false; + + if (!GLAD_EGL_KHR_surfaceless_context) Log_WarningPrint("EGL implementation does not support surfaceless contexts, emulating with pbuffers"); for (const Version& cv : versions_to_try) @@ -120,16 +136,119 @@ bool ContextEGL::Initialize(std::span versions_to_try, Error* err return false; } -bool ContextEGL::SetDisplay() +EGLDisplay ContextEGL::GetPlatformDisplay(const EGLAttrib* attribs, Error* error) { - m_display = eglGetDisplay(static_cast(m_wi.display_connection)); - if (!m_display) + EGLDisplay dpy = TryGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, attribs); + if (dpy == EGL_NO_DISPLAY) + dpy = GetFallbackDisplay(error); + + return dpy; +} + +EGLDisplay ContextEGL::TryGetPlatformDisplay(EGLenum platform, const EGLAttrib* attribs) +{ + const char* extensions_str = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + if (!extensions_str) { - Log_ErrorPrintf("eglGetDisplay() failed: %d", eglGetError()); - return false; + Log_ErrorPrint("No extensions supported."); + return EGL_NO_DISPLAY; } - return true; + EGLDisplay dpy = EGL_NO_DISPLAY; + if (std::strstr(extensions_str, "EGL_KHR_platform_base")) + { + Log_DevPrint("Using EGL_KHR_platform_base."); + + PFNEGLGETPLATFORMDISPLAYPROC get_platform_display; + if (s_egl_library.GetSymbol("eglGetPlatformDisplay", &get_platform_display)) + { + dpy = get_platform_display(platform, m_wi.display_connection, attribs); + if (dpy == EGL_NO_DISPLAY) + { + const EGLint err = eglGetError(); + Log_ErrorFmt("eglGetPlatformDisplay() failed: {} (0x{:X})", err, err); + } + } + else + { + Log_WarningPrint("eglGetPlatformDisplay() was not found"); + } + } + else if (std::strstr(extensions_str, "EGL_EXT_platform_base")) + { + Log_DevPrint("Using EGL_EXT_platform_base."); + + PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display_ext = + (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + if (get_platform_display_ext) + { + const std::vector int_attribs = EGLAttribToInt(attribs); + dpy = get_platform_display_ext(platform, m_wi.display_connection, attribs ? int_attribs.data() : nullptr); + if (dpy == EGL_NO_DISPLAY) + { + const EGLint err = eglGetError(); + Log_ErrorFmt("eglGetPlatformDisplayEXT() failed: {} (0x{:X})", err, err); + } + } + else + { + Log_WarningPrint("eglGetPlatformDisplayEXT() was not found"); + } + } + else + { + Log_WarningPrint("EGL_EXT_platform_base nor EGL_KHR_platform_base is supported."); + } + + return dpy; +} + +EGLDisplay ContextEGL::GetFallbackDisplay(Error* error) +{ + Log_WarningPrint("Using fallback eglGetDisplay() path."); + EGLDisplay dpy = eglGetDisplay(m_wi.display_connection); + if (dpy == EGL_NO_DISPLAY) + { + const EGLint err = eglGetError(); + Error::SetStringFmt(error, "eglGetDisplay() failed: {} (0x{:X})", err, err); + } + + return dpy; +} + +EGLSurface ContextEGL::CreatePlatformSurface(EGLConfig config, const EGLAttrib* attribs, Error* error) +{ + EGLSurface surface = EGL_NO_SURFACE; + if (GLAD_EGL_VERSION_1_5) + { + surface = eglCreatePlatformWindowSurface(m_display, config, m_wi.window_handle, attribs); + if (surface == EGL_NO_SURFACE) + { + const EGLint err = eglGetError(); + Error::SetStringFmt(error, "eglCreatePlatformWindowSurface() failed: {} (0x{:X})", err, err); + } + } + if (surface == EGL_NO_SURFACE) + surface = CreateFallbackSurface(config, attribs, m_wi.window_handle, error); + + return surface; +} + +EGLSurface ContextEGL::CreateFallbackSurface(EGLConfig config, const EGLAttrib* attribs, void* win, Error* error) +{ + Log_WarningPrint("Using fallback eglCreateWindowSurface() path."); + + const std::vector int_attribs = EGLAttribToInt(attribs); + + EGLSurface surface = + eglCreateWindowSurface(m_display, config, (EGLNativeWindowType)win, attribs ? int_attribs.data() : nullptr); + if (surface == EGL_NO_SURFACE) + { + const EGLint err = eglGetError(); + Error::SetStringFmt(error, "eglCreateWindowSurface() failed: {} (0x{:X})", err, err); + } + + return surface; } void* ContextEGL::GetProcAddress(const char* name) @@ -219,7 +338,6 @@ std::unique_ptr ContextEGL::CreateSharedContext(const WindowInfo& wi, E { std::unique_ptr context = std::make_unique(wi); context->m_display = m_display; - context->m_supports_surfaceless = m_supports_surfaceless; if (!context->CreateContextAndSurface(m_version, m_context, false)) { @@ -230,26 +348,21 @@ std::unique_ptr ContextEGL::CreateSharedContext(const WindowInfo& wi, E return context; } -EGLNativeWindowType ContextEGL::GetNativeWindow(EGLConfig config) -{ - return {}; -} - bool ContextEGL::CreateSurface() { if (m_wi.type == WindowInfo::Type::Surfaceless) { - if (m_supports_surfaceless) + if (GLAD_EGL_KHR_surfaceless_context) return true; else return CreatePBufferSurface(); } - EGLNativeWindowType native_window = GetNativeWindow(m_config); - m_surface = eglCreateWindowSurface(m_display, m_config, native_window, nullptr); - if (!m_surface) + Error error; + m_surface = CreatePlatformSurface(m_config, nullptr, &error); + if (m_surface == EGL_NO_SURFACE) { - Log_ErrorPrintf("eglCreateWindowSurface() failed: %d", eglGetError()); + Log_ErrorFmt("Failed to create platform surface: {}", error.GetDescription()); return false; } diff --git a/src/util/gl/context_egl.h b/src/util/gl/context_egl.h index 8cd7a6a5f..41f9b75d4 100644 --- a/src/util/gl/context_egl.h +++ b/src/util/gl/context_egl.h @@ -28,8 +28,12 @@ public: virtual std::unique_ptr CreateSharedContext(const WindowInfo& wi, Error* error) override; protected: - virtual bool SetDisplay(); - virtual EGLNativeWindowType GetNativeWindow(EGLConfig config); + virtual EGLDisplay GetPlatformDisplay(const EGLAttrib* attribs, Error* error); + virtual EGLSurface CreatePlatformSurface(EGLConfig config, const EGLAttrib* attribs, Error* error); + + EGLDisplay TryGetPlatformDisplay(EGLenum platform, const EGLAttrib* attribs); + EGLDisplay GetFallbackDisplay(Error* error); + EGLSurface CreateFallbackSurface(EGLConfig config, const EGLAttrib* attribs, void* window, Error* error); bool Initialize(std::span versions_to_try, Error* error); bool CreateDisplay(); @@ -47,8 +51,6 @@ protected: EGLContext m_context = EGL_NO_CONTEXT; EGLConfig m_config = {}; - - bool m_supports_surfaceless = false; }; } // namespace GL diff --git a/src/util/gl/context_egl_wayland.cpp b/src/util/gl/context_egl_wayland.cpp index d4f60c719..9f70ef77f 100644 --- a/src/util/gl/context_egl_wayland.cpp +++ b/src/util/gl/context_egl_wayland.cpp @@ -3,6 +3,7 @@ #include "context_egl_wayland.h" +#include "common/error.h" #include "common/log.h" #include @@ -52,7 +53,16 @@ void ContextEGLWayland::ResizeSurface(u32 new_surface_width, u32 new_surface_hei ContextEGL::ResizeSurface(new_surface_width, new_surface_height); } -EGLNativeWindowType ContextEGLWayland::GetNativeWindow(EGLConfig config) +EGLDisplay ContextEGLWayland::GetPlatformDisplay(const EGLAttrib* attribs, Error* error) +{ + EGLDisplay dpy = TryGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, attribs); + if (dpy == EGL_NO_DISPLAY) + dpy = GetFallbackDisplay(error); + + return dpy; +} + +EGLSurface ContextEGLWayland::CreatePlatformSurface(EGLConfig config, const EGLAttrib* attribs, Error* error) { if (m_wl_window) { @@ -63,9 +73,31 @@ EGLNativeWindowType ContextEGLWayland::GetNativeWindow(EGLConfig config) m_wl_window = m_wl_egl_window_create(static_cast(m_wi.window_handle), m_wi.surface_width, m_wi.surface_height); if (!m_wl_window) - return {}; + { + Error::SetStringView(error, "wl_egl_window_create() failed"); + return EGL_NO_SURFACE; + } - return reinterpret_cast(m_wl_window); + EGLSurface surface = EGL_NO_SURFACE; + if (GLAD_EGL_VERSION_1_5) + { + surface = eglCreatePlatformWindowSurface(m_display, config, m_wl_window, attribs); + if (surface == EGL_NO_SURFACE) + { + const EGLint err = eglGetError(); + Error::SetStringFmt(error, "eglCreatePlatformWindowSurface() for Wayland failed: {} (0x{:X})", err, err); + } + } + if (surface == EGL_NO_SURFACE) + surface = CreateFallbackSurface(config, attribs, m_wl_window, error); + + if (surface == EGL_NO_SURFACE) + { + m_wl_egl_window_destroy(m_wl_window); + m_wl_window = nullptr; + } + + return surface; } bool ContextEGLWayland::LoadModule() diff --git a/src/util/gl/context_egl_wayland.h b/src/util/gl/context_egl_wayland.h index a9aad39c5..5180f23ca 100644 --- a/src/util/gl/context_egl_wayland.h +++ b/src/util/gl/context_egl_wayland.h @@ -19,7 +19,8 @@ public: void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override; protected: - EGLNativeWindowType GetNativeWindow(EGLConfig config) override; + EGLDisplay GetPlatformDisplay(const EGLAttrib* attribs, Error* error) override; + EGLSurface CreatePlatformSurface(EGLConfig config, const EGLAttrib* attribs, Error* error) override; private: bool LoadModule(); diff --git a/src/util/gl/context_egl_x11.cpp b/src/util/gl/context_egl_x11.cpp index 8d1c12d82..c3b467a0b 100644 --- a/src/util/gl/context_egl_x11.cpp +++ b/src/util/gl/context_egl_x11.cpp @@ -3,6 +3,8 @@ #include "context_egl_x11.h" +#include "common/error.h" + namespace GL { ContextEGLX11::ContextEGLX11(const WindowInfo& wi) : ContextEGL(wi) { @@ -30,13 +32,13 @@ std::unique_ptr ContextEGLX11::CreateSharedContext(const WindowInfo& wi return context; } -void ContextEGLX11::ResizeSurface(u32 new_surface_width, u32 new_surface_height) +EGLDisplay ContextEGLX11::GetPlatformDisplay(const EGLAttrib* attribs, Error* error) { - ContextEGL::ResizeSurface(new_surface_width, new_surface_height); + EGLDisplay dpy = TryGetPlatformDisplay(EGL_PLATFORM_X11_KHR, attribs); + if (dpy == EGL_NO_DISPLAY) + dpy = GetFallbackDisplay(error); + + return dpy; } -EGLNativeWindowType ContextEGLX11::GetNativeWindow(EGLConfig config) -{ - return (EGLNativeWindowType)m_wi.window_handle; -} } // namespace GL diff --git a/src/util/gl/context_egl_x11.h b/src/util/gl/context_egl_x11.h index f720520b8..7734d6281 100644 --- a/src/util/gl/context_egl_x11.h +++ b/src/util/gl/context_egl_x11.h @@ -15,10 +15,9 @@ public: static std::unique_ptr Create(const WindowInfo& wi, std::span versions_to_try, Error* error); std::unique_ptr CreateSharedContext(const WindowInfo& wi, Error* error) override; - void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override; protected: - EGLNativeWindowType GetNativeWindow(EGLConfig config) override; + EGLDisplay GetPlatformDisplay(const EGLAttrib* attribs, Error* error) override; }; } // namespace GL