| 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)) => { |
| 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)] |
| 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 | 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 | |
| 363 | impl GetGlDisplay for Config { |
| 364 | type Target = Display; |
| 365 | |
| 366 | fn display(&self) -> Self::Target { |
| 367 | self.inner.display.clone() |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | impl AsRawConfig for Config { |
| 372 | fn raw_config(&self) -> RawConfig { |
| 373 | RawConfig::Egl(*self.inner.raw) |
| 374 | } |
| 375 | } |
| 376 | |
| 377 | #[cfg (x11_platform)] |
| 378 | impl 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: XlibDisplayHandle) => unsafe { |
| 382 | let xid: u32 = self.native_visual(); |
| 383 | X11VisualInfo::from_xid(display_handle.display?.as_ptr() as *mut _, xid as _) |
| 384 | }, |
| 385 | _ => None, |
| 386 | } |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | impl Sealed for Config {} |
| 391 | |
| 392 | pub(crate) struct ConfigInner { |
| 393 | display: Display, |
| 394 | pub(crate) raw: EglConfig, |
| 395 | } |
| 396 | |
| 397 | impl PartialEq for ConfigInner { |
| 398 | fn eq(&self, other: &Self) -> bool { |
| 399 | self.raw == other.raw |
| 400 | } |
| 401 | } |
| 402 | |
| 403 | impl Eq for ConfigInner {} |
| 404 | |
| 405 | impl fmt::Debug for ConfigInner { |
| 406 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 407 | f&mut DebugStruct<'_, '_>.debug_struct("Config" ) |
| 408 | .field("raw" , &self.raw) |
| 409 | .field(name:"display" , &self.display.inner.raw) |
| 410 | .finish() |
| 411 | } |
| 412 | } |
| 413 | |
| 414 | #[derive (Debug, Clone, PartialEq, Eq)] |
| 415 | pub(crate) struct EglConfig(EGLConfig); |
| 416 | |
| 417 | unsafe impl Send for EglConfig {} |
| 418 | unsafe impl Sync for EglConfig {} |
| 419 | |
| 420 | impl Deref for EglConfig { |
| 421 | type Target = EGLConfig; |
| 422 | |
| 423 | fn deref(&self) -> &Self::Target { |
| 424 | &self.0 |
| 425 | } |
| 426 | } |
| 427 | |