Skip to content

Commit

Permalink
egl: Fallback if EGL_KHR_display_reference not supported
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Molytho committed Oct 9, 2023
1 parent ecf0428 commit 49f9b96
Showing 1 changed file with 156 additions and 118 deletions.
274 changes: 156 additions & 118 deletions glutin/src/api/egl/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ impl Display {
/// and managed by other libraries. Use this function only when you're
/// bringing everything down.
pub unsafe fn terminate(self) {
if !CLIENT_EXTENSIONS.get().unwrap().contains("EGL_KHR_display_reference") {
if !self.inner.uses_display_reference() {
unsafe {
self.inner.egl.Terminate(*self.inner.raw);
}
Expand All @@ -217,50 +217,63 @@ impl Display {

let extensions = CLIENT_EXTENSIONS.get().unwrap();

let mut attrs = Vec::<EGLAttrib>::with_capacity(5);
let (platform, mut display) = match display {
#[cfg(wayland_platform)]
RawDisplayHandle::Wayland(handle)
if extensions.contains("EGL_KHR_platform_wayland") =>
{
(egl::PLATFORM_WAYLAND_KHR, handle.display)
},
#[cfg(x11_platform)]
RawDisplayHandle::Xlib(handle) if extensions.contains("EGL_KHR_platform_x11") => {
attrs.push(egl::PLATFORM_X11_SCREEN_KHR as EGLAttrib);
attrs.push(handle.screen as EGLAttrib);
(egl::PLATFORM_X11_KHR, handle.display)
},
RawDisplayHandle::Gbm(handle) if extensions.contains("EGL_KHR_platform_gbm") => {
(egl::PLATFORM_GBM_KHR, handle.gbm_device)
},
RawDisplayHandle::Android(_) if extensions.contains("EGL_KHR_platform_android") => {
(egl::PLATFORM_ANDROID_KHR, egl::DEFAULT_DISPLAY as *mut _)
},
_ => {
return Err(
ErrorKind::NotSupported("provided display handle is not supported").into()
)
},
};
let mut first_try = true;
loop {
let mut attrs = Vec::<EGLAttrib>::with_capacity(5);
let (platform, mut display) = match display {
#[cfg(wayland_platform)]
RawDisplayHandle::Wayland(handle)
if extensions.contains("EGL_KHR_platform_wayland") =>
{
(egl::PLATFORM_WAYLAND_KHR, handle.display)
},
#[cfg(x11_platform)]
RawDisplayHandle::Xlib(handle) if extensions.contains("EGL_KHR_platform_x11") => {
attrs.push(egl::PLATFORM_X11_SCREEN_KHR as EGLAttrib);
attrs.push(handle.screen as EGLAttrib);
(egl::PLATFORM_X11_KHR, handle.display)
},
RawDisplayHandle::Gbm(handle) if extensions.contains("EGL_KHR_platform_gbm") => {
(egl::PLATFORM_GBM_KHR, handle.gbm_device)
},
RawDisplayHandle::Android(_) if extensions.contains("EGL_KHR_platform_android") => {
(egl::PLATFORM_ANDROID_KHR, egl::DEFAULT_DISPLAY as *mut _)
},
_ => {
return Err(
ErrorKind::NotSupported("provided display handle is not supported").into()
)
},
};

if extensions.contains("EGL_KHR_display_reference") && first_try {
attrs.push(egl::TRACK_REFERENCES_KHR as _);
attrs.push(egl::TRUE as _);
} else {
first_try = false;
}

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 _;
}

// Be explicit here.
if display.is_null() {
display = egl::DEFAULT_DISPLAY as *mut _;
}
// Push `egl::NONE` to terminate the list.
attrs.push(egl::NONE as EGLAttrib);

// Push `egl::NONE` to terminate the list.
attrs.push(egl::NONE as EGLAttrib);
let display =
unsafe { egl.GetPlatformDisplay(platform, display as *mut _, attrs.as_ptr()) };
let result = Self::check_display_error(display).map(EglDisplay::Khr);

let display =
unsafe { egl.GetPlatformDisplay(platform, display as *mut _, attrs.as_ptr()) };
// We can't check for the type of error here because for some reason it doesn't
// always report BadAttribute even though EGL_KHR_display_reference
// was the reason of the failure
if result.is_ok() || result.is_err() && !first_try {
return result;
}

Self::check_display_error(display).map(EglDisplay::Khr)
first_try = false;
}
}

fn get_platform_display_ext(egl: &Egl, display: RawDisplayHandle) -> Result<EglDisplay> {
Expand All @@ -270,72 +283,88 @@ impl Display {

let extensions = CLIENT_EXTENSIONS.get().unwrap();

let mut attrs = Vec::<EGLint>::with_capacity(5);
let mut legacy = false;
let (platform, mut display) = match display {
#[cfg(wayland_platform)]
RawDisplayHandle::Wayland(handle)
if extensions.contains("EGL_EXT_platform_wayland") =>
{
(egl::PLATFORM_WAYLAND_EXT, handle.display)
},
#[cfg(x11_platform)]
RawDisplayHandle::Xlib(handle) if extensions.contains("EGL_EXT_platform_x11") => {
attrs.push(egl::PLATFORM_X11_SCREEN_EXT as EGLint);
attrs.push(handle.screen as EGLint);
(egl::PLATFORM_X11_EXT, handle.display)
},
#[cfg(x11_platform)]
RawDisplayHandle::Xcb(handle)
if extensions.contains("EGL_MESA_platform_xcb")
|| extensions.contains("EGL_EXT_platform_xcb") =>
{
attrs.push(egl::PLATFORM_XCB_SCREEN_EXT as EGLint);
attrs.push(handle.screen as EGLint);
(egl::PLATFORM_XCB_EXT, handle.connection)
},
RawDisplayHandle::Gbm(handle) if extensions.contains("EGL_MESA_platform_gbm") => {
(egl::PLATFORM_GBM_MESA, handle.gbm_device)
},
RawDisplayHandle::Windows(..) if extensions.contains("EGL_ANGLE_platform_angle") => {
// Only CreateWindowSurface appears to work with Angle.
legacy = true;
(egl::PLATFORM_ANGLE_ANGLE, egl::DEFAULT_DISPLAY as *mut _)
},
_ => {
return Err(
ErrorKind::NotSupported("provided display handle is not supported").into()
)
},
};
let mut first_try = true;
loop {
let mut attrs = Vec::<EGLint>::with_capacity(5);
let mut legacy = false;
let (platform, mut display) = match display {
#[cfg(wayland_platform)]
RawDisplayHandle::Wayland(handle)
if extensions.contains("EGL_EXT_platform_wayland") =>
{
(egl::PLATFORM_WAYLAND_EXT, handle.display)
},
#[cfg(x11_platform)]
RawDisplayHandle::Xlib(handle) if extensions.contains("EGL_EXT_platform_x11") => {
attrs.push(egl::PLATFORM_X11_SCREEN_EXT as EGLint);
attrs.push(handle.screen as EGLint);
(egl::PLATFORM_X11_EXT, handle.display)
},
#[cfg(x11_platform)]
RawDisplayHandle::Xcb(handle)
if extensions.contains("EGL_MESA_platform_xcb")
|| extensions.contains("EGL_EXT_platform_xcb") =>
{
attrs.push(egl::PLATFORM_XCB_SCREEN_EXT as EGLint);
attrs.push(handle.screen as EGLint);
(egl::PLATFORM_XCB_EXT, handle.connection)
},
RawDisplayHandle::Gbm(handle) if extensions.contains("EGL_MESA_platform_gbm") => {
(egl::PLATFORM_GBM_MESA, handle.gbm_device)
},
RawDisplayHandle::Windows(..)
if extensions.contains("EGL_ANGLE_platform_angle") =>
{
// Only CreateWindowSurface appears to work with Angle.
legacy = true;
(egl::PLATFORM_ANGLE_ANGLE, egl::DEFAULT_DISPLAY as *mut _)
},
_ => {
return Err(
ErrorKind::NotSupported("provided display handle is not supported").into()
)
},
};

if extensions.contains("EGL_KHR_display_reference") && first_try {
attrs.push(egl::TRACK_REFERENCES_KHR as _);
attrs.push(egl::TRUE as _);
} else {
first_try = false;
}

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 _;
}

// Be explicit here.
if display.is_null() {
display = egl::DEFAULT_DISPLAY as *mut _;
}
// Push `egl::NONE` to terminate the list.
attrs.push(egl::NONE as EGLint);

// Push `egl::NONE` to terminate the list.
attrs.push(egl::NONE as EGLint);
let display =
unsafe { egl.GetPlatformDisplayEXT(platform, display as *mut _, attrs.as_ptr()) };

let display =
unsafe { egl.GetPlatformDisplayEXT(platform, display as *mut _, attrs.as_ptr()) };
let result = 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(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)
// We can't check for the type of error here because for some reason it doesn't
// always report BadAttribute even though EGL_KHR_display_reference
// was the reason of the failure
if result.is_ok() || result.is_err() && !first_try {
return result;
}
})

first_try = false;
}
}

fn get_display(egl: &Egl, display: RawDisplayHandle) -> Result<EglDisplay> {
Expand Down Expand Up @@ -525,6 +554,31 @@ pub(crate) struct DisplayInner {
pub(crate) _native_display: Option<NativeDisplay>,
}

impl DisplayInner {
fn uses_display_reference(&self) -> bool {
if !CLIENT_EXTENSIONS.get().unwrap().contains("EGL_KHR_display_reference") {
return false;
}

let mut track_references = MaybeUninit::<EGLAttrib>::uninit();
unsafe {
return match self.raw {
EglDisplay::Khr(khr) => self.egl.QueryDisplayAttribKHR(
khr,
egl::TRACK_REFERENCES_KHR as _,
track_references.as_mut_ptr(),
),
EglDisplay::Ext(ext) => self.egl.QueryDisplayAttribEXT(
ext,
egl::TRACK_REFERENCES_KHR as _,
track_references.as_mut_ptr(),
),
EglDisplay::Legacy(_) => egl::FALSE,
} == egl::TRUE;
}
}
}

impl fmt::Debug for DisplayInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Display")
Expand All @@ -542,25 +596,9 @@ impl Drop for DisplayInner {
// reference count the display. If that is the case, glutin can
// terminate the display without worry for the instance being
// reused elsewhere.
if CLIENT_EXTENSIONS.get().unwrap().contains("EGL_KHR_display_reference") {
let mut track_references = MaybeUninit::<EGLAttrib>::uninit();
if self.uses_display_reference() {
unsafe {
if match self.raw {
EglDisplay::Khr(khr) => self.egl.QueryDisplayAttribKHR(
khr,
egl::TRACK_REFERENCES_KHR as _,
track_references.as_mut_ptr(),
),
EglDisplay::Ext(ext) => self.egl.QueryDisplayAttribEXT(
ext,
egl::TRACK_REFERENCES_KHR as _,
track_references.as_mut_ptr(),
),
EglDisplay::Legacy(_) => return,
} == egl::TRUE
{
self.egl.Terminate(*self.raw);
}
self.egl.Terminate(*self.raw);
}
}

Expand Down

0 comments on commit 49f9b96

Please sign in to comment.