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 bdd9844
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,62 @@ 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()) };

Check failure on line 265 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Check formatting

Diff in /home/runner/work/glutin/glutin/glutin/src/api/egl/display.rs
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 +282,85 @@ 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") => {

Check failure on line 311 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Check formatting

Diff in /home/runner/work/glutin/glutin/glutin/src/api/egl/display.rs
(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)
}

Check failure on line 353 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Check formatting

Diff in /home/runner/work/glutin/glutin/glutin/src/api/egl/display.rs
});

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 +550,35 @@ 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 {
if match self.raw {

Check failure on line 561 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, x86_64-unknown-linux-gnu, ubuntu-latest)

this if-then-else expression returns a bool literal

Check failure on line 561 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, x86_64-pc-windows-msvc, windows-latest)

this if-then-else expression returns a bool literal

Check failure on line 561 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, i686-pc-windows-msvc, windows-latest)

this if-then-else expression returns a bool literal

Check failure on line 561 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, i686-unknown-linux-gnu, ubuntu-latest)

this if-then-else expression returns a bool literal

Check failure on line 561 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, x86_64-pc-windows-gnu, windows-latest, -x86_64-pc-windows-gnu)

this if-then-else expression returns a bool literal

Check failure on line 561 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, i686-pc-windows-gnu, windows-latest, -i686-pc-windows-gnu)

this if-then-else expression returns a bool literal
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(),

Check failure on line 570 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Check formatting

Diff in /home/runner/work/glutin/glutin/glutin/src/api/egl/display.rs
),
EglDisplay::Legacy(_) => egl::FALSE,
} == egl::TRUE {
return true;

Check failure on line 574 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, x86_64-unknown-linux-gnu, ubuntu-latest)

unneeded `return` statement

Check failure on line 574 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, x86_64-pc-windows-msvc, windows-latest)

unneeded `return` statement

Check failure on line 574 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, i686-pc-windows-msvc, windows-latest)

unneeded `return` statement

Check failure on line 574 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, i686-unknown-linux-gnu, ubuntu-latest)

unneeded `return` statement

Check failure on line 574 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, x86_64-pc-windows-gnu, windows-latest, -x86_64-pc-windows-gnu)

unneeded `return` statement

Check failure on line 574 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, i686-pc-windows-gnu, windows-latest, -i686-pc-windows-gnu)

unneeded `return` statement
} else {
return false;

Check failure on line 576 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, x86_64-unknown-linux-gnu, ubuntu-latest)

unneeded `return` statement

Check failure on line 576 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, x86_64-pc-windows-msvc, windows-latest)

unneeded `return` statement

Check failure on line 576 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, i686-pc-windows-msvc, windows-latest)

unneeded `return` statement

Check failure on line 576 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, i686-unknown-linux-gnu, ubuntu-latest)

unneeded `return` statement

Check failure on line 576 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, x86_64-pc-windows-gnu, windows-latest, -x86_64-pc-windows-gnu)

unneeded `return` statement

Check failure on line 576 in glutin/src/api/egl/display.rs

View workflow job for this annotation

GitHub Actions / Tests (1.65.0, i686-pc-windows-gnu, windows-latest, -i686-pc-windows-gnu)

unneeded `return` statement
}
}
}
}

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 bdd9844

Please sign in to comment.