1 | //! Everything related to finding and manipulating the `EGLConfig`. |
2 | #![allow (clippy::unnecessary_cast)] // needed for 32bit & 64bit support |
3 | |
4 | use std::ops::Deref; |
5 | use std::sync::Arc; |
6 | use std::{fmt, mem}; |
7 | |
8 | use raw_window_handle::RawWindowHandle; |
9 | |
10 | use glutin_egl_sys::egl; |
11 | use glutin_egl_sys::egl::types::{EGLConfig, EGLint}; |
12 | |
13 | use crate::config::{ |
14 | Api, AsRawConfig, ColorBufferType, ConfigSurfaceTypes, ConfigTemplate, RawConfig, |
15 | }; |
16 | use crate::display::{DisplayFeatures, GetGlDisplay}; |
17 | use crate::error::{ErrorKind, Result}; |
18 | use crate::prelude::*; |
19 | use crate::private::Sealed; |
20 | |
21 | #[cfg (x11_platform)] |
22 | use crate::platform::x11::{X11GlConfigExt, X11VisualInfo}; |
23 | |
24 | use super::display::Display; |
25 | |
26 | impl 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)) if xcb.visual_id > 0 => { |
200 | xcb.visual_id as u32 == 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)] |
227 | pub struct Config { |
228 | pub(crate) inner: Arc<ConfigInner>, |
229 | } |
230 | |
231 | impl 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 | |
257 | impl 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 | #[cfg (not(any(wayland_platform, x11_platform)))] |
331 | fn supports_transparency(&self) -> Option<bool> { |
332 | None |
333 | } |
334 | |
335 | #[cfg (any(wayland_platform, x11_platform))] |
336 | fn supports_transparency(&self) -> Option<bool> { |
337 | use raw_window_handle::RawDisplayHandle; |
338 | match *self.inner.display.inner._native_display? { |
339 | #[cfg (x11_platform)] |
340 | RawDisplayHandle::Xlib(_) | RawDisplayHandle::Xcb(_) => { |
341 | self.x11_visual().map(|visual| visual.supports_transparency()) |
342 | }, |
343 | #[cfg (wayland_platform)] |
344 | RawDisplayHandle::Wayland(_) => Some(self.alpha_size() != 0), |
345 | _ => None, |
346 | } |
347 | } |
348 | |
349 | fn api(&self) -> Api { |
350 | let mut api = Api::empty(); |
351 | let raw_api = unsafe { self.raw_attribute(egl::RENDERABLE_TYPE as EGLint) as u32 }; |
352 | if raw_api & egl::OPENGL_BIT as u32 != 0 { |
353 | api.insert(Api::OPENGL); |
354 | } |
355 | if raw_api & egl::OPENGL_ES_BIT as u32 != 0 { |
356 | api.insert(Api::GLES1); |
357 | } |
358 | if raw_api & egl::OPENGL_ES2_BIT as u32 != 0 { |
359 | api.insert(Api::GLES2); |
360 | } |
361 | if raw_api & egl::OPENGL_ES3_BIT as u32 != 0 { |
362 | api.insert(Api::GLES3); |
363 | } |
364 | |
365 | api |
366 | } |
367 | } |
368 | |
369 | impl GetGlDisplay for Config { |
370 | type Target = Display; |
371 | |
372 | fn display(&self) -> Self::Target { |
373 | self.inner.display.clone() |
374 | } |
375 | } |
376 | |
377 | impl AsRawConfig for Config { |
378 | fn raw_config(&self) -> RawConfig { |
379 | RawConfig::Egl(*self.inner.raw) |
380 | } |
381 | } |
382 | |
383 | #[cfg (x11_platform)] |
384 | impl X11GlConfigExt for Config { |
385 | fn x11_visual(&self) -> Option<X11VisualInfo> { |
386 | match *self.inner.display.inner._native_display? { |
387 | raw_window_handle::RawDisplayHandle::Xlib(display_handle: XlibDisplayHandle) => unsafe { |
388 | let xid: u32 = self.native_visual(); |
389 | X11VisualInfo::from_xid(display_handle.display as *mut _, xid as _) |
390 | }, |
391 | _ => None, |
392 | } |
393 | } |
394 | } |
395 | |
396 | impl Sealed for Config {} |
397 | |
398 | pub(crate) struct ConfigInner { |
399 | display: Display, |
400 | pub(crate) raw: EglConfig, |
401 | } |
402 | |
403 | impl PartialEq for ConfigInner { |
404 | fn eq(&self, other: &Self) -> bool { |
405 | self.raw == other.raw |
406 | } |
407 | } |
408 | |
409 | impl Eq for ConfigInner {} |
410 | |
411 | impl fmt::Debug for ConfigInner { |
412 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
413 | f&mut DebugStruct<'_, '_>.debug_struct("Config" ) |
414 | .field("raw" , &self.raw) |
415 | .field(name:"display" , &self.display.inner.raw) |
416 | .finish() |
417 | } |
418 | } |
419 | |
420 | #[derive (Debug, Clone, PartialEq, Eq)] |
421 | pub(crate) struct EglConfig(EGLConfig); |
422 | |
423 | unsafe impl Send for EglConfig {} |
424 | unsafe impl Sync for EglConfig {} |
425 | |
426 | impl Deref for EglConfig { |
427 | type Target = EGLConfig; |
428 | |
429 | fn deref(&self) -> &Self::Target { |
430 | &self.0 |
431 | } |
432 | } |
433 | |