glutin/api/egl/
config.rs

1//! Everything related to finding and manipulating the `EGLConfig`.
2#![allow(clippy::unnecessary_cast)] // needed for 32bit & 64bit support
3
4use std::ops::Deref;
5use std::sync::Arc;
6use std::{fmt, mem};
7
8use raw_window_handle::RawWindowHandle;
9
10use glutin_egl_sys::egl;
11use glutin_egl_sys::egl::types::{EGLConfig, EGLint};
12
13use crate::config::{
14    Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, RawConfig,
15};
16use crate::display::{DisplayFeatures, GetGlDisplay};
17use crate::error::{ErrorKind, Result};
18use crate::prelude::*;
19use crate::private::Sealed;
20
21#[cfg(x11_platform)]
22use crate::platform::x11::{X11GlConfigExt, X11VisualInfo};
23
24use super::display::Display;
25
26impl Display {
27    pub(crate) unsafe fn find_configs(
28        &self,
29        template: ConfigTemplate,
30    ) -> Result<Box<dyn Iterator<Item = Config> + '_>> {
31        let mut config_attributes = Vec::<EGLint>::new();
32
33        // Add color buffer type.
34        match template.color_buffer_type {
35            ColorBufferType::Rgb { r_size, g_size, b_size } => {
36                // Type.
37                config_attributes.push(egl::COLOR_BUFFER_TYPE as EGLint);
38                config_attributes.push(egl::RGB_BUFFER as EGLint);
39
40                // R.
41                config_attributes.push(egl::RED_SIZE as EGLint);
42                config_attributes.push(r_size as EGLint);
43
44                // G.
45                config_attributes.push(egl::GREEN_SIZE as EGLint);
46                config_attributes.push(g_size as EGLint);
47
48                // B.
49                config_attributes.push(egl::BLUE_SIZE as EGLint);
50                config_attributes.push(b_size as EGLint);
51            },
52            ColorBufferType::Luminance(luminance) => {
53                // Type.
54                config_attributes.push(egl::COLOR_BUFFER_TYPE as EGLint);
55                config_attributes.push(egl::LUMINANCE_BUFFER as EGLint);
56
57                // L.
58                config_attributes.push(egl::LUMINANCE_SIZE as EGLint);
59                config_attributes.push(luminance as EGLint);
60            },
61        };
62
63        if template.float_pixels
64            && self.inner.features.contains(DisplayFeatures::FLOAT_PIXEL_FORMAT)
65        {
66            config_attributes.push(egl::COLOR_COMPONENT_TYPE_EXT as EGLint);
67            config_attributes.push(egl::COLOR_COMPONENT_TYPE_FLOAT_EXT as EGLint);
68        } else if template.float_pixels {
69            return Err(ErrorKind::NotSupported("float pixels not supported").into());
70        }
71
72        // Add alpha.
73        config_attributes.push(egl::ALPHA_SIZE as EGLint);
74        config_attributes.push(template.alpha_size as EGLint);
75
76        // Add depth.
77        config_attributes.push(egl::DEPTH_SIZE as EGLint);
78        config_attributes.push(template.depth_size as EGLint);
79
80        // Add stencil.
81        config_attributes.push(egl::STENCIL_SIZE as EGLint);
82        config_attributes.push(template.stencil_size as EGLint);
83
84        // Add surface type.
85        config_attributes.push(egl::SURFACE_TYPE as EGLint);
86        let mut surface_type = 0;
87        if template.config_surface_types.contains(ConfigSurfaceTypes::WINDOW) {
88            surface_type |= egl::WINDOW_BIT;
89        }
90        if template.config_surface_types.contains(ConfigSurfaceTypes::PBUFFER) {
91            surface_type |= egl::PBUFFER_BIT;
92        }
93        if template.config_surface_types.contains(ConfigSurfaceTypes::PIXMAP) {
94            surface_type |= egl::PIXMAP_BIT;
95        }
96        config_attributes.push(surface_type as EGLint);
97
98        // Add caveat.
99        if let Some(hardware_accelerated) = template.hardware_accelerated {
100            config_attributes.push(egl::CONFIG_CAVEAT as EGLint);
101            if hardware_accelerated {
102                config_attributes.push(egl::NONE as EGLint);
103            } else {
104                config_attributes.push(egl::SLOW_CONFIG as EGLint);
105            }
106        }
107
108        // Add minimum swap interval.
109        if let Some(min_swap_interval) = template.min_swap_interval {
110            config_attributes.push(egl::MIN_SWAP_INTERVAL as EGLint);
111            config_attributes.push(min_swap_interval as EGLint)
112        }
113
114        // Add maximum swap interval.
115        if let Some(max_swap_interval) = template.max_swap_interval {
116            config_attributes.push(egl::MAX_SWAP_INTERVAL as EGLint);
117            config_attributes.push(max_swap_interval as EGLint)
118        }
119
120        // Add multisampling.
121        if let Some(num_samples) = template.num_samples {
122            config_attributes.push(egl::SAMPLE_BUFFERS as EGLint);
123            config_attributes.push(1);
124            config_attributes.push(egl::SAMPLES as EGLint);
125            config_attributes.push(num_samples as EGLint);
126        }
127
128        config_attributes.push(egl::RENDERABLE_TYPE as EGLint);
129        let api = if let Some(requested_api) = template.api {
130            let mut api = 0;
131            if requested_api.contains(Api::GLES1) {
132                api |= egl::OPENGL_ES_BIT;
133            }
134            if requested_api.contains(Api::GLES2) {
135                api |= egl::OPENGL_ES2_BIT;
136            }
137            if requested_api.contains(Api::GLES3) {
138                api |= egl::OPENGL_ES3_BIT;
139            }
140            if requested_api.contains(Api::OPENGL) {
141                api |= egl::OPENGL_BIT;
142            }
143            api
144        } else {
145            // NOTE: use ES2 by default to avoid matching pure ES1 configs,
146            // for more see https://github.com/rust-windowing/glutin/issues/1586.
147            egl::OPENGL_ES2_BIT
148        };
149        config_attributes.push(api as EGLint);
150
151        // Add maximum height of pbuffer.
152        if let Some(pbuffer_width) = template.max_pbuffer_width {
153            config_attributes.push(egl::MAX_PBUFFER_WIDTH as EGLint);
154            config_attributes.push(pbuffer_width as EGLint);
155        }
156
157        // Add maximum width of pbuffer.
158        if let Some(pbuffer_height) = template.max_pbuffer_height {
159            config_attributes.push(egl::MAX_PBUFFER_HEIGHT as EGLint);
160            config_attributes.push(pbuffer_height as EGLint);
161        }
162
163        // Push `egl::NONE` to terminate the list.
164        config_attributes.push(egl::NONE as EGLint);
165
166        let mut configs_number = self.configs_number() as EGLint;
167        let mut found_configs: Vec<EGLConfig> =
168            unsafe { vec![mem::zeroed(); configs_number as usize] };
169
170        unsafe {
171            let result = self.inner.egl.ChooseConfig(
172                *self.inner.raw,
173                config_attributes.as_ptr(),
174                found_configs.as_mut_ptr(),
175                configs_number as EGLint,
176                &mut configs_number,
177            );
178
179            if result == egl::FALSE {
180                return Err(ErrorKind::BadConfig.into());
181            }
182
183            found_configs.set_len(configs_number as usize);
184        }
185
186        let configs = found_configs
187            .into_iter()
188            .map(move |raw| {
189                let raw = EglConfig(raw);
190                let inner = Arc::new(ConfigInner { display: self.clone(), raw });
191                Config { inner }
192            })
193            .filter(move |config| {
194                // Filter configs not compatible with the native window.
195                //
196                // XXX This can't be done by passing visual in the EGL attributes
197                // when calling `eglChooseConfig` since the visual is ignored.
198                match template.native_window {
199                    Some(RawWindowHandle::Xcb(xcb)) => {
200                        xcb.visual_id.map_or(false, |id| id.get() == config.native_visual())
201                    },
202                    Some(RawWindowHandle::Xlib(xlib)) if xlib.visual_id > 0 => {
203                        xlib.visual_id as u32 == config.native_visual()
204                    },
205                    _ => true,
206                }
207            })
208            .filter(move |config| {
209                !template.transparency || config.supports_transparency().unwrap_or(true)
210            });
211
212        Ok(Box::new(configs))
213    }
214
215    fn configs_number(&self) -> usize {
216        unsafe {
217            let mut num_configs = 0;
218            self.inner.egl.GetConfigs(*self.inner.raw, std::ptr::null_mut(), 0, &mut num_configs);
219            num_configs as usize
220        }
221    }
222}
223
224/// A simple wrapper around `EGLConfig` that could be used with `EGLContext`
225/// and `EGLSurface`.
226#[derive(Debug, Clone, PartialEq, Eq)]
227pub struct Config {
228    pub(crate) inner: Arc<ConfigInner>,
229}
230
231impl Config {
232    /// The native visual identifier.
233    ///
234    /// The interpretation of this value is platform dependant. Consult
235    /// `platform` extension you're ended up using.
236    pub fn native_visual(&self) -> u32 {
237        unsafe { self.raw_attribute(egl::NATIVE_VISUAL_ID as EGLint) as u32 }
238    }
239
240    /// # Safety
241    ///
242    /// The caller must ensure that the attribute could be present.
243    unsafe fn raw_attribute(&self, attr: EGLint) -> EGLint {
244        unsafe {
245            let mut val = 0;
246            self.inner.display.inner.egl.GetConfigAttrib(
247                *self.inner.display.inner.raw,
248                *self.inner.raw,
249                attr,
250                &mut val,
251            );
252            val as EGLint
253        }
254    }
255}
256
257impl GlConfig for Config {
258    fn color_buffer_type(&self) -> Option<ColorBufferType> {
259        unsafe {
260            match self.raw_attribute(egl::COLOR_BUFFER_TYPE as EGLint) as _ {
261                egl::LUMINANCE_BUFFER => {
262                    let luma = self.raw_attribute(egl::LUMINANCE_SIZE as EGLint);
263                    Some(ColorBufferType::Luminance(luma as u8))
264                },
265                egl::RGB_BUFFER => {
266                    let r_size = self.raw_attribute(egl::RED_SIZE as EGLint) as u8;
267                    let g_size = self.raw_attribute(egl::GREEN_SIZE as EGLint) as u8;
268                    let b_size = self.raw_attribute(egl::BLUE_SIZE as EGLint) as u8;
269                    Some(ColorBufferType::Rgb { r_size, g_size, b_size })
270                },
271                _ => None,
272            }
273        }
274    }
275
276    fn float_pixels(&self) -> bool {
277        unsafe {
278            if self.inner.display.inner.features.contains(DisplayFeatures::FLOAT_PIXEL_FORMAT) {
279                matches!(
280                    self.raw_attribute(egl::COLOR_COMPONENT_TYPE_EXT as EGLint) as _,
281                    egl::COLOR_COMPONENT_TYPE_FLOAT_EXT
282                )
283            } else {
284                false
285            }
286        }
287    }
288
289    fn alpha_size(&self) -> u8 {
290        unsafe { self.raw_attribute(egl::ALPHA_SIZE as EGLint) as u8 }
291    }
292
293    fn srgb_capable(&self) -> bool {
294        self.inner.display.inner.features.contains(DisplayFeatures::SRGB_FRAMEBUFFERS)
295    }
296
297    fn depth_size(&self) -> u8 {
298        unsafe { self.raw_attribute(egl::DEPTH_SIZE as EGLint) as u8 }
299    }
300
301    fn stencil_size(&self) -> u8 {
302        unsafe { self.raw_attribute(egl::STENCIL_SIZE as EGLint) as u8 }
303    }
304
305    fn num_samples(&self) -> u8 {
306        unsafe { self.raw_attribute(egl::SAMPLES as EGLint) as u8 }
307    }
308
309    fn config_surface_types(&self) -> ConfigSurfaceTypes {
310        let mut ty = ConfigSurfaceTypes::empty();
311
312        let raw_ty = unsafe { self.raw_attribute(egl::SURFACE_TYPE as EGLint) as u32 };
313        if raw_ty & egl::WINDOW_BIT as u32 != 0 {
314            ty.insert(ConfigSurfaceTypes::WINDOW);
315        }
316        if raw_ty & egl::PBUFFER_BIT as u32 != 0 {
317            ty.insert(ConfigSurfaceTypes::PBUFFER);
318        }
319        if raw_ty & egl::PIXMAP_BIT as u32 != 0 {
320            ty.insert(ConfigSurfaceTypes::PIXMAP);
321        }
322
323        ty
324    }
325
326    fn hardware_accelerated(&self) -> bool {
327        unsafe { self.raw_attribute(egl::CONFIG_CAVEAT as EGLint) != egl::SLOW_CONFIG as EGLint }
328    }
329
330    fn supports_transparency(&self) -> Option<bool> {
331        match *self.inner.display.inner._native_display? {
332            #[cfg(x11_platform)]
333            raw_window_handle::RawDisplayHandle::Xlib(_)
334            | raw_window_handle::RawDisplayHandle::Xcb(_) => {
335                self.x11_visual().map(|visual| visual.supports_transparency())
336            },
337            #[cfg(wayland_platform)]
338            raw_window_handle::RawDisplayHandle::Wayland(_) => Some(self.alpha_size() != 0),
339            _ => None,
340        }
341    }
342
343    fn api(&self) -> Api {
344        let mut api = Api::empty();
345        let raw_api = unsafe { self.raw_attribute(egl::RENDERABLE_TYPE as EGLint) as u32 };
346        if raw_api & egl::OPENGL_BIT as u32 != 0 {
347            api.insert(Api::OPENGL);
348        }
349        if raw_api & egl::OPENGL_ES_BIT as u32 != 0 {
350            api.insert(Api::GLES1);
351        }
352        if raw_api & egl::OPENGL_ES2_BIT as u32 != 0 {
353            api.insert(Api::GLES2);
354        }
355        if raw_api & egl::OPENGL_ES3_BIT as u32 != 0 {
356            api.insert(Api::GLES3);
357        }
358
359        api
360    }
361}
362
363impl GetGlDisplay for Config {
364    type Target = Display;
365
366    fn display(&self) -> Self::Target {
367        self.inner.display.clone()
368    }
369}
370
371impl AsRawConfig for Config {
372    fn raw_config(&self) -> RawConfig {
373        RawConfig::Egl(*self.inner.raw)
374    }
375}
376
377#[cfg(x11_platform)]
378impl X11GlConfigExt for Config {
379    fn x11_visual(&self) -> Option<X11VisualInfo> {
380        match *self.inner.display.inner._native_display? {
381            raw_window_handle::RawDisplayHandle::Xlib(display_handle) => unsafe {
382                let xid = self.native_visual();
383                X11VisualInfo::from_xid(display_handle.display?.as_ptr() as *mut _, xid as _)
384            },
385            _ => None,
386        }
387    }
388}
389
390impl Sealed for Config {}
391
392pub(crate) struct ConfigInner {
393    display: Display,
394    pub(crate) raw: EglConfig,
395}
396
397impl PartialEq for ConfigInner {
398    fn eq(&self, other: &Self) -> bool {
399        self.raw == other.raw
400    }
401}
402
403impl Eq for ConfigInner {}
404
405impl fmt::Debug for ConfigInner {
406    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407        f.debug_struct("Config")
408            .field("raw", &self.raw)
409            .field("display", &self.display.inner.raw)
410            .finish()
411    }
412}
413
414#[derive(Debug, Clone, PartialEq, Eq)]
415pub(crate) struct EglConfig(EGLConfig);
416
417unsafe impl Send for EglConfig {}
418unsafe impl Sync for EglConfig {}
419
420impl Deref for EglConfig {
421    type Target = EGLConfig;
422
423    fn deref(&self) -> &Self::Target {
424        &self.0
425    }
426}