Skip to content

Commit 1679584

Browse files
authored
api/egl: Add EGLDevice wrappers
This allows querying the available EGL devices on the system and getting the device used by the display. A device can provide information such as the name of the device the vendor and in the future, other info such as the DRM device node on Linux. Mesa does not implement the extensions at the moment to allow getting the name and vendor of the device. A headless Display can be created using a Device. Along with this addition, an EGL specific api to make a context current without a surface is also available.
1 parent a460e99 commit 1679584

File tree

12 files changed

+518
-36
lines changed

12 files changed

+518
-36
lines changed

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,3 @@ target/
33
.DS_Store
44
*~
55
#*#
6-
headless.png

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Unreleased
22

3+
- Added `EGLDevice` wrappers for EGL.
4+
- Added EGL dependent api to make a context current without a surface.
35
- Added `supports_transparency` on `GlConfig`.
46
- On GLX, try all extensions when setting vsync.
57
- On WGL, fixed that `Surface::swap_buffers` takes longer with every call caused by frequent calls of the win32 function `HDC GetDC(HWND hWnd)`.

glutin/src/api/egl/config.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ impl GlConfig for Config {
308308
#[cfg(any(wayland_platform, x11_platform))]
309309
fn supports_transparency(&self) -> Option<bool> {
310310
use raw_window_handle::RawDisplayHandle;
311-
match *self.inner.display.inner._native_display {
311+
match *self.inner.display.inner._native_display? {
312312
#[cfg(x11_platform)]
313313
RawDisplayHandle::Xlib(_) | RawDisplayHandle::Xcb(_) => {
314314
self.x11_visual().map(|visual| visual.supports_transparency())
@@ -356,7 +356,7 @@ impl AsRawConfig for Config {
356356
#[cfg(x11_platform)]
357357
impl X11GlConfigExt for Config {
358358
fn x11_visual(&self) -> Option<X11VisualInfo> {
359-
match *self.inner.display.inner._native_display {
359+
match *self.inner.display.inner._native_display? {
360360
raw_window_handle::RawDisplayHandle::Xlib(display_handle) => unsafe {
361361
let xid = self.native_visual();
362362
X11VisualInfo::from_xid(display_handle.display as *mut _, xid as _)

glutin/src/api/egl/context.rs

+30
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@ pub struct NotCurrentContext {
162162
}
163163

164164
impl NotCurrentContext {
165+
/// Make a [`Self::PossiblyCurrentContext`] indicating that the context
166+
/// could be current on the thread.
167+
pub fn make_current_surfaceless(self) -> Result<PossiblyCurrentContext> {
168+
self.inner.make_current_surfaceless()?;
169+
Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData })
170+
}
171+
165172
fn new(inner: ContextInner) -> Self {
166173
Self { inner }
167174
}
@@ -225,6 +232,13 @@ pub struct PossiblyCurrentContext {
225232
_nosendsync: PhantomData<EGLContext>,
226233
}
227234

235+
impl PossiblyCurrentContext {
236+
/// Make this context current on the calling thread.
237+
pub fn make_current_surfaceless(&self) -> Result<()> {
238+
self.inner.make_current_surfaceless()
239+
}
240+
}
241+
228242
impl PossiblyCurrentGlContext for PossiblyCurrentContext {
229243
type NotCurrentContext = NotCurrentContext;
230244

@@ -289,6 +303,22 @@ pub(crate) struct ContextInner {
289303
}
290304

291305
impl ContextInner {
306+
fn make_current_surfaceless(&self) -> Result<()> {
307+
unsafe {
308+
if self.display.inner.egl.MakeCurrent(
309+
*self.display.inner.raw,
310+
egl::NO_SURFACE,
311+
egl::NO_SURFACE,
312+
*self.raw,
313+
) == egl::FALSE
314+
{
315+
super::check_error()
316+
} else {
317+
Ok(())
318+
}
319+
}
320+
}
321+
292322
fn make_current_draw_read<T: SurfaceTypeTrait>(
293323
&self,
294324
surface_draw: &Surface<T>,

glutin/src/api/egl/device.rs

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
//! Everything related to `EGLDevice`.
2+
3+
use std::collections::HashSet;
4+
use std::ffi::{c_void, CStr};
5+
use std::ptr;
6+
7+
use glutin_egl_sys::egl;
8+
use glutin_egl_sys::egl::types::EGLDeviceEXT;
9+
10+
use crate::error::{ErrorKind, Result};
11+
12+
use super::display::{extensions_from_ptr, get_extensions, NO_DISPLAY_EXTENSIONS};
13+
use super::{Egl, EGL};
14+
15+
/// Wrapper for `EGLDevice`.
16+
#[derive(Debug, Clone, PartialEq, Eq)]
17+
pub struct Device {
18+
inner: EGLDeviceEXT,
19+
extensions: HashSet<&'static str>,
20+
name: Option<String>,
21+
vendor: Option<String>,
22+
}
23+
24+
impl Device {
25+
/// Query the available devices.
26+
///
27+
/// This function returns [`Err`] if the `EGL_EXT_device_query` and
28+
/// `EGL_EXT_device_enumeration` or `EGL_EXT_device_base` extensions are
29+
/// not available.
30+
pub fn query_devices() -> Result<impl Iterator<Item = Device>> {
31+
let egl = match EGL.as_ref() {
32+
Some(egl) => egl,
33+
None => return Err(ErrorKind::NotFound.into()),
34+
};
35+
36+
let no_display_extensions =
37+
NO_DISPLAY_EXTENSIONS.get_or_init(|| get_extensions(egl, egl::NO_DISPLAY));
38+
39+
// Querying devices requires EGL_EXT_device_enumeration and
40+
// EGL_EXT_device_query.
41+
//
42+
// Or we can check for the EGL_EXT_device_base extension since it contains both
43+
// extensions.
44+
if (!no_display_extensions.contains("EGL_EXT_device_enumeration")
45+
&& !no_display_extensions.contains("EGL_EXT_device_query"))
46+
|| !no_display_extensions.contains("EGL_EXT_device_base")
47+
{
48+
return Err(ErrorKind::NotSupported("EGL does not support EGL_EXT_device_base").into());
49+
}
50+
51+
let mut device_count = 0;
52+
53+
if unsafe {
54+
// The specification states:
55+
// > An EGL_BAD_PARAMETER error is generated if <max_devices> is
56+
// > less than or equal to zero unless <devices> is NULL, or if
57+
// > <num_devices> is NULL.
58+
//
59+
// The error will never be generated since num_devices is a pointer
60+
// to the count being queried. Therefore there is no need to check
61+
// the error.
62+
egl.QueryDevicesEXT(0, ptr::null_mut(), &mut device_count) == egl::FALSE
63+
} {
64+
super::check_error()?;
65+
// On failure, EGL_FALSE is returned.
66+
return Err(ErrorKind::NotSupported("Querying device count failed").into());
67+
}
68+
69+
let mut devices = Vec::with_capacity(device_count as usize);
70+
71+
unsafe {
72+
let mut count = device_count;
73+
if egl.QueryDevicesEXT(device_count, devices.as_mut_ptr(), &mut count) == egl::FALSE {
74+
super::check_error()?;
75+
// On failure, EGL_FALSE is returned.
76+
return Err(ErrorKind::NotSupported("Querying devices failed").into());
77+
}
78+
79+
// SAFETY: EGL has initialized the vector for the number of devices.
80+
devices.set_len(device_count as usize);
81+
}
82+
83+
Ok(devices.into_iter().flat_map(|ptr| Device::from_ptr(egl, ptr)))
84+
}
85+
86+
/// Get the device extensions supported by this device.
87+
///
88+
/// These extensions are distinct from the display extensions and should not
89+
/// be used interchangeably.
90+
pub fn extensions(&self) -> &HashSet<&str> {
91+
&self.extensions
92+
}
93+
94+
/// Get the name of the device.
95+
///
96+
/// This function will return [`None`] if the `EGL_EXT_device_query_name`
97+
/// device extension is not available.
98+
pub fn name(&self) -> Option<&str> {
99+
self.name.as_deref()
100+
}
101+
102+
/// Get the vendor of the device.
103+
///
104+
/// This function will return [`None`] if the `EGL_EXT_device_query_name`
105+
/// device extension is not available.
106+
pub fn vendor(&self) -> Option<&str> {
107+
self.vendor.as_deref()
108+
}
109+
110+
/// Get a raw handle to the `EGLDevice`.
111+
pub fn raw_device(&self) -> *const c_void {
112+
self.inner
113+
}
114+
}
115+
116+
// SAFETY: An EGLDevice is immutable and valid for the lifetime of the EGL
117+
// library.
118+
unsafe impl Send for Device {}
119+
unsafe impl Sync for Device {}
120+
121+
impl Device {
122+
unsafe fn query_string(egl_device: *const c_void, name: egl::types::EGLenum) -> Option<String> {
123+
let egl = super::EGL.as_ref().unwrap();
124+
125+
// SAFETY: The caller has ensured the name is valid.
126+
let ptr = unsafe { egl.QueryDeviceStringEXT(egl_device, name as _) };
127+
128+
if ptr.is_null() {
129+
return None;
130+
}
131+
132+
unsafe { CStr::from_ptr(ptr) }.to_str().ok().map(String::from)
133+
}
134+
135+
pub(crate) fn from_ptr(egl: &Egl, ptr: *const c_void) -> Result<Self> {
136+
// SAFETY: The EGL specification guarantees the returned string is
137+
// static and null terminated:
138+
//
139+
// > eglQueryDeviceStringEXT returns a pointer to a static,
140+
// > zero-terminated string describing some aspect of the specified
141+
// > EGLDeviceEXT. <name> must be EGL_EXTENSIONS.
142+
let extensions =
143+
unsafe { extensions_from_ptr(egl.QueryDeviceStringEXT(ptr, egl::EXTENSIONS as _)) };
144+
145+
let (name, vendor) = if extensions.contains("EGL_EXT_device_query_name") {
146+
// SAFETY: RENDERER_EXT and VENDOR are valid strings for device string queries
147+
// if EGL_EXT_device_query_name.
148+
unsafe {
149+
(Self::query_string(ptr, egl::RENDERER_EXT), Self::query_string(ptr, egl::VENDOR))
150+
}
151+
} else {
152+
(None, None)
153+
};
154+
155+
Ok(Self { inner: ptr, extensions, name, vendor })
156+
}
157+
}

0 commit comments

Comments
 (0)