From 430627d8b0bcd5731853934e69dc585a9f163913 Mon Sep 17 00:00:00 2001 From: Robin Ebert Date: Mon, 9 Oct 2023 18:58:27 +0200 Subject: [PATCH] egl: Fallback if EGL_KHR_display_reference not supported libglvnd reports client extensions if at least one vendor supports them. This might lead to display creation failure when a vendor library reports support but fails to create a display for another reason (like when the nvidia egl implementation is installed but no nvidia card is available). To work around this issue retry creation without EGL_KHR_display_reference. --- glutin/src/api/egl/display.rs | 111 +++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 29 deletions(-) diff --git a/glutin/src/api/egl/display.rs b/glutin/src/api/egl/display.rs index 7d34df2727..f6c5f505b3 100644 --- a/glutin/src/api/egl/display.rs +++ b/glutin/src/api/egl/display.rs @@ -127,15 +127,17 @@ impl Display { let mut attrs = Vec::::with_capacity(3); - if extensions.contains("EGL_KHR_display_reference") { - attrs.push(egl::TRACK_REFERENCES_KHR as _); - attrs.push(egl::TRUE as _); - } - // TODO: Some extensions exist like EGL_EXT_device_drm which allow specifying // which DRM master fd to use under the hood by the implementation. This would // mean there would need to be an unsafe equivalent to this function. + // Push at the end so we can pop it on failure + let has_display_reference = extensions.contains("EGL_KHR_display_reference"); + if has_display_reference { + attrs.push(egl::TRACK_REFERENCES_KHR as _); + attrs.push(egl::TRUE as _); + } + // Push `egl::NONE` to terminate the list. attrs.push(egl::NONE as EGLint); @@ -146,6 +148,23 @@ impl Display { attrs.as_ptr(), ) }) + .or_else(|err| { + if has_display_reference { + attrs.pop(); + attrs.pop(); + attrs.pop(); + attrs.push(egl::NONE as EGLint); + Self::check_display_error(unsafe { + egl.GetPlatformDisplayEXT( + egl::PLATFORM_DEVICE_EXT, + device.raw_device() as *mut _, + attrs.as_ptr(), + ) + }) + } else { + Err(err) + } + }) .map(EglDisplay::Ext)?; Self::initialize_display(egl, display, None) @@ -244,23 +263,40 @@ impl Display { }, }; - if extensions.contains("EGL_KHR_display_reference") { - attrs.push(egl::TRACK_REFERENCES_KHR as _); - attrs.push(egl::TRUE as _); - } - // Be explicit here. if display.is_null() { display = egl::DEFAULT_DISPLAY as *mut _; } + // Push at the end so we can pop it on failure + let has_display_reference = extensions.contains("EGL_KHR_display_reference"); + if has_display_reference { + attrs.push(egl::TRACK_REFERENCES_KHR as _); + attrs.push(egl::TRUE as _); + } + // Push `egl::NONE` to terminate the list. attrs.push(egl::NONE as EGLAttrib); - let display = + let platform_display = unsafe { egl.GetPlatformDisplay(platform, display as *mut _, attrs.as_ptr()) }; - Self::check_display_error(display).map(EglDisplay::Khr) + Self::check_display_error(platform_display) + .or_else(|err| { + if has_display_reference { + attrs.pop(); + attrs.pop(); + attrs.pop(); + attrs.push(egl::NONE as EGLAttrib); + let platform_display = unsafe { + egl.GetPlatformDisplay(platform, display as *mut _, attrs.as_ptr()) + }; + Self::check_display_error(platform_display) + } else { + Err(err) + } + }) + .map(EglDisplay::Khr) } fn get_platform_display_ext(egl: &Egl, display: RawDisplayHandle) -> Result { @@ -309,33 +345,50 @@ impl Display { }, }; - if extensions.contains("EGL_KHR_display_reference") { - attrs.push(egl::TRACK_REFERENCES_KHR as _); - attrs.push(egl::TRUE as _); - } - // Be explicit here. if display.is_null() { display = egl::DEFAULT_DISPLAY as *mut _; } + // Push at the end so we can pop it on failure + let has_display_reference = extensions.contains("EGL_KHR_display_reference"); + if has_display_reference { + attrs.push(egl::TRACK_REFERENCES_KHR as _); + attrs.push(egl::TRUE as _); + } + // Push `egl::NONE` to terminate the list. attrs.push(egl::NONE as EGLint); - let display = + let platform_display = unsafe { egl.GetPlatformDisplayEXT(platform, display as *mut _, attrs.as_ptr()) }; - Self::check_display_error(display).map(|display| { - if legacy { - // NOTE: For angle we use the Legacy code path, as that uses CreateWindowSurface - // instead of CreatePlatformWindowSurface*. The latter somehow - // doesn't work, only the former does. But Angle's own example also use the - // former: https://github.com/google/angle/blob/main/util/EGLWindow.cpp#L424 - EglDisplay::Legacy(display) - } else { - EglDisplay::Ext(display) - } - }) + Self::check_display_error(platform_display) + .or_else(|err| { + if has_display_reference { + attrs.pop(); + attrs.pop(); + attrs.pop(); + attrs.push(egl::NONE as EGLint); + let platform_display = unsafe { + egl.GetPlatformDisplayEXT(platform, display as *mut _, attrs.as_ptr()) + }; + Self::check_display_error(platform_display) + } else { + Err(err) + } + }) + .map(|display| { + if legacy { + // NOTE: For angle we use the Legacy code path, as that uses CreateWindowSurface + // instead of CreatePlatformWindowSurface*. The latter somehow + // doesn't work, only the former does. But Angle's own example also use the + // former: https://github.com/google/angle/blob/main/util/EGLWindow.cpp#L424 + EglDisplay::Legacy(display) + } else { + EglDisplay::Ext(display) + } + }) } fn get_display(egl: &Egl, display: RawDisplayHandle) -> Result {