1 #![cfg(any(
2     target_os = "windows",
3     target_os = "linux",
4     target_os = "android",
5     target_os = "dragonfly",
6     target_os = "freebsd",
7     target_os = "netbsd",
8     target_os = "openbsd",
9 ))]
10 #![allow(unused_variables)]
11 
12 #[cfg(not(target_os = "android"))]
13 mod egl {
14     use super::ffi;
15     use crate::api::dlloader::{SymTrait, SymWrapper};
16 
17     #[derive(Clone)]
18     pub struct Egl(pub SymWrapper<ffi::egl::Egl>);
19 
20     /// Because `*const raw::c_void` doesn't implement `Sync`.
21     unsafe impl Sync for Egl {}
22 
23     impl SymTrait for ffi::egl::Egl {
load_with<F>(loadfn: F) -> Self where F: FnMut(&'static str) -> *const std::os::raw::c_void,24         fn load_with<F>(loadfn: F) -> Self
25         where
26             F: FnMut(&'static str) -> *const std::os::raw::c_void,
27         {
28             Self::load_with(loadfn)
29         }
30     }
31 
32     impl Egl {
new() -> Result<Self, ()>33         pub fn new() -> Result<Self, ()> {
34             #[cfg(target_os = "windows")]
35             let paths = vec!["libEGL.dll", "atioglxx.dll"];
36 
37             #[cfg(not(target_os = "windows"))]
38             let paths = vec!["libEGL.so.1", "libEGL.so"];
39 
40             SymWrapper::new(paths).map(|i| Egl(i))
41         }
42     }
43 }
44 
45 #[cfg(target_os = "android")]
46 mod egl {
47     use super::ffi;
48 
49     #[derive(Clone)]
50     pub struct Egl(pub ffi::egl::Egl);
51 
52     impl Egl {
new() -> Result<Self, ()>53         pub fn new() -> Result<Self, ()> {
54             Ok(Egl(ffi::egl::Egl))
55         }
56     }
57 }
58 
59 mod make_current_guard;
60 
61 pub use self::egl::Egl;
62 use self::make_current_guard::MakeCurrentGuard;
63 use crate::{
64     Api, ContextError, CreationError, GlAttributes, GlRequest, PixelFormat,
65     PixelFormatRequirements, ReleaseBehavior, Robustness,
66 };
67 
68 use glutin_egl_sys as ffi;
69 use parking_lot::Mutex;
70 #[cfg(any(
71     target_os = "android",
72     target_os = "windows",
73     target_os = "linux",
74     target_os = "dragonfly",
75     target_os = "freebsd",
76     target_os = "netbsd",
77     target_os = "openbsd",
78 ))]
79 use winit::dpi;
80 
81 use std::ffi::{CStr, CString};
82 use std::ops::{Deref, DerefMut};
83 use std::os::raw;
84 
85 impl Deref for Egl {
86     type Target = ffi::egl::Egl;
87 
deref(&self) -> &Self::Target88     fn deref(&self) -> &Self::Target {
89         &self.0
90     }
91 }
92 
93 impl DerefMut for Egl {
deref_mut(&mut self) -> &mut ffi::egl::Egl94     fn deref_mut(&mut self) -> &mut ffi::egl::Egl {
95         &mut self.0
96     }
97 }
98 
99 lazy_static! {
100     pub static ref EGL: Option<Egl> = Egl::new().ok();
101 }
102 
103 /// Specifies the type of display passed as `native_display`.
104 #[derive(Debug)]
105 #[allow(dead_code)]
106 pub enum NativeDisplay {
107     /// `None` means `EGL_DEFAULT_DISPLAY`.
108     X11(Option<ffi::EGLNativeDisplayType>),
109     /// `None` means `EGL_DEFAULT_DISPLAY`.
110     Gbm(Option<ffi::EGLNativeDisplayType>),
111     /// `None` means `EGL_DEFAULT_DISPLAY`.
112     Wayland(Option<ffi::EGLNativeDisplayType>),
113     /// `EGL_DEFAULT_DISPLAY` is mandatory for Android.
114     Android,
115     // TODO: should be `EGLDeviceEXT`
116     Device(ffi::EGLNativeDisplayType),
117     /// Don't specify any display type. Useful on windows. `None` means
118     /// `EGL_DEFAULT_DISPLAY`.
119     Other(Option<ffi::EGLNativeDisplayType>),
120 }
121 
122 #[derive(Debug)]
123 pub struct Context {
124     display: ffi::egl::types::EGLDisplay,
125     context: ffi::egl::types::EGLContext,
126     surface: Option<Mutex<ffi::egl::types::EGLSurface>>,
127     api: Api,
128     pixel_format: PixelFormat,
129     #[cfg(target_os = "android")]
130     config_id: ffi::egl::types::EGLConfig,
131 }
132 
133 #[cfg(target_os = "android")]
134 #[inline]
get_native_display(egl: &Egl, ndisp: NativeDisplay) -> *const raw::c_void135 fn get_native_display(egl: &Egl, ndisp: NativeDisplay) -> *const raw::c_void {
136     unsafe { egl.GetDisplay(ffi::egl::DEFAULT_DISPLAY as *mut _) }
137 }
138 
139 #[cfg(not(target_os = "android"))]
get_native_display(egl: &Egl, ndisp: NativeDisplay) -> *const raw::c_void140 fn get_native_display(egl: &Egl, ndisp: NativeDisplay) -> *const raw::c_void {
141     // the first step is to query the list of extensions without any display, if
142     // supported
143     let dp_extensions = unsafe {
144         let p =
145             egl.QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32);
146 
147         // this possibility is available only with EGL 1.5 or
148         // EGL_EXT_platform_base, otherwise `eglQueryString` returns an
149         // error
150         if p.is_null() {
151             vec![]
152         } else {
153             let p = CStr::from_ptr(p);
154             let list = String::from_utf8(p.to_bytes().to_vec())
155                 .unwrap_or_else(|_| format!(""));
156             list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
157         }
158     };
159 
160     let has_dp_extension =
161         |e: &str| dp_extensions.iter().find(|s| s == &e).is_some();
162 
163     match ndisp {
164         // Note: Some EGL implementations are missing the
165         // `eglGetPlatformDisplay(EXT)` symbol       despite reporting
166         // `EGL_EXT_platform_base`. I'm pretty sure this is a bug.
167         //       Therefore we detect whether the symbol is loaded in addition to
168         // checking for       extensions.
169         NativeDisplay::X11(display)
170             if has_dp_extension("EGL_KHR_platform_x11")
171                 && egl.GetPlatformDisplay.is_loaded() =>
172         {
173             let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
174             // TODO: `PLATFORM_X11_SCREEN_KHR`
175             unsafe {
176                 egl.GetPlatformDisplay(
177                     ffi::egl::PLATFORM_X11_KHR,
178                     d as *mut _,
179                     std::ptr::null(),
180                 )
181             }
182         }
183 
184         NativeDisplay::X11(display)
185             if has_dp_extension("EGL_EXT_platform_x11")
186                 && egl.GetPlatformDisplayEXT.is_loaded() =>
187         {
188             let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
189             // TODO: `PLATFORM_X11_SCREEN_EXT`
190             unsafe {
191                 egl.GetPlatformDisplayEXT(
192                     ffi::egl::PLATFORM_X11_EXT,
193                     d as *mut _,
194                     std::ptr::null(),
195                 )
196             }
197         }
198 
199         NativeDisplay::Gbm(display)
200             if has_dp_extension("EGL_KHR_platform_gbm")
201                 && egl.GetPlatformDisplay.is_loaded() =>
202         {
203             let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
204             unsafe {
205                 egl.GetPlatformDisplay(
206                     ffi::egl::PLATFORM_GBM_KHR,
207                     d as *mut _,
208                     std::ptr::null(),
209                 )
210             }
211         }
212 
213         NativeDisplay::Gbm(display)
214             if has_dp_extension("EGL_MESA_platform_gbm")
215                 && egl.GetPlatformDisplayEXT.is_loaded() =>
216         {
217             let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
218             unsafe {
219                 egl.GetPlatformDisplayEXT(
220                     ffi::egl::PLATFORM_GBM_KHR,
221                     d as *mut _,
222                     std::ptr::null(),
223                 )
224             }
225         }
226 
227         NativeDisplay::Wayland(display)
228             if has_dp_extension("EGL_KHR_platform_wayland")
229                 && egl.GetPlatformDisplay.is_loaded() =>
230         {
231             let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
232             unsafe {
233                 egl.GetPlatformDisplay(
234                     ffi::egl::PLATFORM_WAYLAND_KHR,
235                     d as *mut _,
236                     std::ptr::null(),
237                 )
238             }
239         }
240 
241         NativeDisplay::Wayland(display)
242             if has_dp_extension("EGL_EXT_platform_wayland")
243                 && egl.GetPlatformDisplayEXT.is_loaded() =>
244         {
245             let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _);
246             unsafe {
247                 egl.GetPlatformDisplayEXT(
248                     ffi::egl::PLATFORM_WAYLAND_EXT,
249                     d as *mut _,
250                     std::ptr::null(),
251                 )
252             }
253         }
254 
255         // TODO: This will never be reached right now, as the android egl
256         // bindings use the static generator, so can't rely on
257         // GetPlatformDisplay(EXT).
258         NativeDisplay::Android
259             if has_dp_extension("EGL_KHR_platform_android")
260                 && egl.GetPlatformDisplay.is_loaded() =>
261         unsafe {
262             egl.GetPlatformDisplay(
263                 ffi::egl::PLATFORM_ANDROID_KHR,
264                 ffi::egl::DEFAULT_DISPLAY as *mut _,
265                 std::ptr::null(),
266             )
267         }
268 
269         NativeDisplay::Device(display)
270             if has_dp_extension("EGL_EXT_platform_device")
271                 && egl.GetPlatformDisplay.is_loaded() =>
272         unsafe {
273             egl.GetPlatformDisplay(
274                 ffi::egl::PLATFORM_DEVICE_EXT,
275                 display as *mut _,
276                 std::ptr::null(),
277             )
278         }
279 
280         NativeDisplay::X11(Some(display))
281         | NativeDisplay::Gbm(Some(display))
282         | NativeDisplay::Wayland(Some(display))
283         | NativeDisplay::Device(display)
284         | NativeDisplay::Other(Some(display)) => unsafe {
285             egl.GetDisplay(display as *mut _)
286         },
287 
288         NativeDisplay::X11(None)
289         | NativeDisplay::Gbm(None)
290         | NativeDisplay::Wayland(None)
291         | NativeDisplay::Android
292         | NativeDisplay::Other(None) => unsafe {
293             egl.GetDisplay(ffi::egl::DEFAULT_DISPLAY as *mut _)
294         },
295     }
296 }
297 
298 #[derive(Copy, Clone, PartialEq)]
299 pub enum SurfaceType {
300     PBuffer,
301     Window,
302     Surfaceless,
303 }
304 
305 impl Context {
306     /// Start building an EGL context.
307     ///
308     /// This function initializes some things and chooses the pixel format.
309     ///
310     /// To finish the process, you must call `.finish(window)` on the
311     /// `ContextPrototype`.
new<'a>( pf_reqs: &PixelFormatRequirements, opengl: &'a GlAttributes<&'a Context>, ndisp: NativeDisplay, surface_type: SurfaceType, ) -> Result<ContextPrototype<'a>, CreationError>312     pub fn new<'a>(
313         pf_reqs: &PixelFormatRequirements,
314         opengl: &'a GlAttributes<&'a Context>,
315         ndisp: NativeDisplay,
316         surface_type: SurfaceType,
317     ) -> Result<ContextPrototype<'a>, CreationError> {
318         let egl = EGL.as_ref().unwrap();
319         // calling `eglGetDisplay` or equivalent
320         let display = get_native_display(egl, ndisp);
321 
322         if display.is_null() {
323             return Err(CreationError::OsError(
324                 "Could not create EGL display object".to_string(),
325             ));
326         }
327 
328         let egl_version = unsafe {
329             let mut major: ffi::egl::types::EGLint = std::mem::uninitialized();
330             let mut minor: ffi::egl::types::EGLint = std::mem::uninitialized();
331 
332             if egl.Initialize(display, &mut major, &mut minor) == 0 {
333                 return Err(CreationError::OsError(
334                     "eglInitialize failed".to_string(),
335                 ));
336             }
337 
338             (major, minor)
339         };
340 
341         // the list of extensions supported by the client once initialized is
342         // different from the list of extensions obtained earlier
343         let extensions = if egl_version >= (1, 2) {
344             let p = unsafe {
345                 CStr::from_ptr(
346                     egl.QueryString(display, ffi::egl::EXTENSIONS as i32),
347                 )
348             };
349             let list = String::from_utf8(p.to_bytes().to_vec())
350                 .unwrap_or_else(|_| format!(""));
351             list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
352         } else {
353             vec![]
354         };
355 
356         // binding the right API and choosing the version
357         let (version, api) = unsafe {
358             match opengl.version {
359                 GlRequest::Latest => {
360                     if egl_version >= (1, 4) {
361                         if egl.BindAPI(ffi::egl::OPENGL_API) != 0 {
362                             (None, Api::OpenGl)
363                         } else if egl.BindAPI(ffi::egl::OPENGL_ES_API) != 0 {
364                             (None, Api::OpenGlEs)
365                         } else {
366                             return Err(
367                                 CreationError::OpenGlVersionNotSupported,
368                             );
369                         }
370                     } else {
371                         (None, Api::OpenGlEs)
372                     }
373                 }
374                 GlRequest::Specific(Api::OpenGlEs, version) => {
375                     if egl_version >= (1, 2) {
376                         if egl.BindAPI(ffi::egl::OPENGL_ES_API) == 0 {
377                             return Err(
378                                 CreationError::OpenGlVersionNotSupported,
379                             );
380                         }
381                     }
382                     (Some(version), Api::OpenGlEs)
383                 }
384                 GlRequest::Specific(Api::OpenGl, version) => {
385                     if egl_version < (1, 4) {
386                         return Err(CreationError::OpenGlVersionNotSupported);
387                     }
388                     if egl.BindAPI(ffi::egl::OPENGL_API) == 0 {
389                         return Err(CreationError::OpenGlVersionNotSupported);
390                     }
391                     (Some(version), Api::OpenGl)
392                 }
393                 GlRequest::Specific(_, _) => {
394                     return Err(CreationError::OpenGlVersionNotSupported);
395                 }
396                 GlRequest::GlThenGles {
397                     opengles_version,
398                     opengl_version,
399                 } => {
400                     if egl_version >= (1, 4) {
401                         if egl.BindAPI(ffi::egl::OPENGL_API) != 0 {
402                             (Some(opengl_version), Api::OpenGl)
403                         } else if egl.BindAPI(ffi::egl::OPENGL_ES_API) != 0 {
404                             (Some(opengles_version), Api::OpenGlEs)
405                         } else {
406                             return Err(
407                                 CreationError::OpenGlVersionNotSupported,
408                             );
409                         }
410                     } else {
411                         (Some(opengles_version), Api::OpenGlEs)
412                     }
413                 }
414             }
415         };
416 
417         let (config_id, pixel_format) = unsafe {
418             choose_fbconfig(
419                 egl,
420                 display,
421                 &egl_version,
422                 api,
423                 version,
424                 pf_reqs,
425                 surface_type,
426             )?
427         };
428 
429         Ok(ContextPrototype {
430             opengl,
431             display,
432             egl_version,
433             extensions,
434             api,
435             version,
436             config_id,
437             pixel_format,
438         })
439     }
440 
check_make_current( &self, ret: Option<u32>, ) -> Result<(), ContextError>441     unsafe fn check_make_current(
442         &self,
443         ret: Option<u32>,
444     ) -> Result<(), ContextError> {
445         let egl = EGL.as_ref().unwrap();
446         if ret == Some(0) {
447             match egl.GetError() as u32 {
448                 ffi::egl::CONTEXT_LOST => Err(ContextError::ContextLost),
449                 err => panic!(
450                     "make_current: eglMakeCurrent failed (eglGetError returned 0x{:x})",
451                     err
452                 ),
453             }
454         } else {
455             Ok(())
456         }
457     }
458 
make_current(&self) -> Result<(), ContextError>459     pub unsafe fn make_current(&self) -> Result<(), ContextError> {
460         let egl = EGL.as_ref().unwrap();
461         let surface = self
462             .surface
463             .as_ref()
464             .map(|s| *s.lock())
465             .unwrap_or(ffi::egl::NO_SURFACE);
466         let ret = egl.MakeCurrent(self.display, surface, surface, self.context);
467 
468         self.check_make_current(Some(ret))
469     }
470 
make_not_current(&self) -> Result<(), ContextError>471     pub unsafe fn make_not_current(&self) -> Result<(), ContextError> {
472         let egl = EGL.as_ref().unwrap();
473 
474         let surface_eq =
475             if let Some(surface) = self.surface.as_ref().map(|s| *s.lock()) {
476                 egl.GetCurrentSurface(ffi::egl::DRAW as i32) == surface
477                     || egl.GetCurrentSurface(ffi::egl::READ as i32) == surface
478             } else {
479                 false
480             };
481 
482         if surface_eq || egl.GetCurrentContext() == self.context {
483             let ret = egl.MakeCurrent(
484                 self.display,
485                 ffi::egl::NO_SURFACE,
486                 ffi::egl::NO_SURFACE,
487                 ffi::egl::NO_CONTEXT,
488             );
489 
490             self.check_make_current(Some(ret))
491         } else {
492             self.check_make_current(None)
493         }
494     }
495 
496     #[inline]
is_current(&self) -> bool497     pub fn is_current(&self) -> bool {
498         let egl = EGL.as_ref().unwrap();
499         unsafe { egl.GetCurrentContext() == self.context }
500     }
501 
502     #[inline]
get_api(&self) -> Api503     pub fn get_api(&self) -> Api {
504         self.api
505     }
506 
507     #[inline]
raw_handle(&self) -> ffi::egl::types::EGLContext508     pub unsafe fn raw_handle(&self) -> ffi::egl::types::EGLContext {
509         self.context
510     }
511 
512     #[inline]
get_egl_display(&self) -> ffi::egl::types::EGLDisplay513     pub unsafe fn get_egl_display(&self) -> ffi::egl::types::EGLDisplay {
514         self.display
515     }
516 
517     // Handle Android Life Cycle.
518     // Android has started the activity or sent it to foreground.
519     // Create a new surface and attach it to the recreated ANativeWindow.
520     // Restore the EGLContext.
521     #[cfg(target_os = "android")]
on_surface_created(&self, nwin: ffi::EGLNativeWindowType)522     pub unsafe fn on_surface_created(&self, nwin: ffi::EGLNativeWindowType) {
523         let egl = EGL.as_ref().unwrap();
524         let mut surface = self.surface.as_ref().unwrap().lock();
525         if *surface != ffi::egl::NO_SURFACE {
526             return;
527         }
528         *surface = egl.CreateWindowSurface(
529             self.display,
530             self.config_id,
531             nwin,
532             std::ptr::null(),
533         );
534         if surface.is_null() {
535             panic!(
536                 "on_surface_created: eglCreateWindowSurface failed with 0x{:x}",
537                 egl.GetError()
538             )
539         }
540         let ret =
541             egl.MakeCurrent(self.display, *surface, *surface, self.context);
542         if ret == 0 {
543             panic!(
544                 "on_surface_created: eglMakeCurrent failed with 0x{:x}",
545                 egl.GetError()
546             )
547         }
548     }
549 
550     // Handle Android Life Cycle.
551     // Android has stopped the activity or sent it to background.
552     // Release the surface attached to the destroyed ANativeWindow.
553     // The EGLContext is not destroyed so it can be restored later.
554     #[cfg(target_os = "android")]
on_surface_destroyed(&self)555     pub unsafe fn on_surface_destroyed(&self) {
556         let egl = EGL.as_ref().unwrap();
557         let mut surface = self.surface.as_ref().unwrap().lock();
558         if *surface == ffi::egl::NO_SURFACE {
559             return;
560         }
561         let ret = egl.MakeCurrent(
562             self.display,
563             ffi::egl::NO_SURFACE,
564             ffi::egl::NO_SURFACE,
565             ffi::egl::NO_CONTEXT,
566         );
567         if ret == 0 {
568             panic!(
569                 "on_surface_destroyed: eglMakeCurrent failed with 0x{:x}",
570                 egl.GetError()
571             )
572         }
573 
574         egl.DestroySurface(self.display, *surface);
575         *surface = ffi::egl::NO_SURFACE;
576     }
577 
578     #[inline]
get_proc_address(&self, addr: &str) -> *const ()579     pub fn get_proc_address(&self, addr: &str) -> *const () {
580         let egl = EGL.as_ref().unwrap();
581         let addr = CString::new(addr.as_bytes()).unwrap();
582         let addr = addr.as_ptr();
583         unsafe { egl.GetProcAddress(addr) as *const _ }
584     }
585 
586     #[inline]
swap_buffers(&self) -> Result<(), ContextError>587     pub fn swap_buffers(&self) -> Result<(), ContextError> {
588         let egl = EGL.as_ref().unwrap();
589         let surface = self.surface.as_ref().unwrap().lock();
590         if *surface == ffi::egl::NO_SURFACE {
591             return Err(ContextError::ContextLost);
592         }
593 
594         let ret = unsafe { egl.SwapBuffers(self.display, *surface) };
595 
596         if ret == 0 {
597             match unsafe { egl.GetError() } as u32 {
598                 ffi::egl::CONTEXT_LOST => {
599                     return Err(ContextError::ContextLost)
600                 }
601                 err => panic!(
602                     "swap_buffers: eglSwapBuffers failed (eglGetError returned 0x{:x})",
603                     err
604                 ),
605             }
606         } else {
607             Ok(())
608         }
609     }
610 
611     #[inline]
get_pixel_format(&self) -> PixelFormat612     pub fn get_pixel_format(&self) -> PixelFormat {
613         self.pixel_format.clone()
614     }
615 }
616 
617 unsafe impl Send for Context {}
618 unsafe impl Sync for Context {}
619 
620 impl Drop for Context {
drop(&mut self)621     fn drop(&mut self) {
622         unsafe {
623             // https://stackoverflow.com/questions/54402688/recreate-eglcreatewindowsurface-with-same-native-window
624             let egl = EGL.as_ref().unwrap();
625             let surface = self
626                 .surface
627                 .as_ref()
628                 .map(|s| *s.lock())
629                 .unwrap_or(ffi::egl::NO_SURFACE);
630             // Ok, so we got to call `glFinish` before destroying the context
631             // to ensure it actually gets destroyed. This requires making the
632             // this context current.
633             let mut guard = MakeCurrentGuard::new(
634                 self.display,
635                 surface,
636                 surface,
637                 self.context,
638             )
639             .map_err(|err| ContextError::OsError(err))
640             .unwrap();
641 
642             guard.if_any_same_then_invalidate(surface, surface, self.context);
643 
644             let gl_finish_fn = self.get_proc_address("glFinish");
645             assert!(gl_finish_fn != std::ptr::null());
646             let gl_finish_fn =
647                 std::mem::transmute::<_, extern "system" fn()>(gl_finish_fn);
648             gl_finish_fn();
649 
650             egl.DestroyContext(self.display, self.context);
651             self.context = ffi::egl::NO_CONTEXT;
652             egl.DestroySurface(self.display, surface);
653             if let Some(ref surface) = self.surface {
654                 let mut surface = surface.lock();
655                 *surface = ffi::egl::NO_SURFACE;
656             }
657 
658             // In a reasonable world, we could uncomment the line bellow.
659             //
660             // This is no such world. Lets talk about something.
661             //
662             // You see, every call to `get_native_display` returns the exact
663             // same display, just look at the docs:
664             //
665             // "Multiple calls made to eglGetDisplay with the same display_id
666             // will return the same EGLDisplay handle."
667             //
668             // My EGL implementation does not do any ref counting, nor do the
669             // EGL docs mention ref counting anywhere. In fact, the docs state
670             // that there will be *no effect*, which, in a way, implies no ref
671             // counting:
672             //
673             // "Initializing an already initialized EGL display connection has
674             // no effect besides returning the version numbers."
675             //
676             // So, if we terminate the display, other people who are using it
677             // won't be so happy.
678             //
679             // Well, how did I stumble on this issue you might ask...
680             //
681             // In this case, the "other people" was us, for it appears my EGL
682             // implementation does not follow the docs,  or maybe I'm misreading
683             // them. You see, according to the egl docs:
684             //
685             // "To release the current context without assigning a new one, set
686             // context to EGL_NO_CONTEXT and set draw and read to
687             // EGL_NO_SURFACE.  [...] ******This is the only case where an
688             // uninitialized display may be passed to eglMakeCurrent.******"
689             // (Emphasis mine).
690             //
691             // Well, my computer returns EGL_BAD_DISPLAY if the display passed
692             // to eglMakeCurrent is uninitialized, which allowed to me to spot
693             // this issue.
694             //
695             // I would have assumed that if EGL was going to provide us with
696             // the same EGLDisplay that they'd at least do
697             // some ref counting, but they don't.
698             //
699             // FIXME: Technically we are leaking resources, not much we can do.
700             // Yeah, we could have a global static that does ref counting
701             // ourselves, but what if some other library is using the display.
702             //
703             // On Linux, we could preload a little lib that does ref
704             // counting on that level, but:
705             //      A) What about other platforms?
706             //      B) Do you *really* want all glutin programs to preload a
707             //      library?
708             //      C) Who the hell is going to maintain that?
709             //
710             // egl.Terminate(self.display);
711         }
712     }
713 }
714 
715 #[derive(Debug)]
716 pub struct ContextPrototype<'a> {
717     opengl: &'a GlAttributes<&'a Context>,
718     display: ffi::egl::types::EGLDisplay,
719     egl_version: (ffi::egl::types::EGLint, ffi::egl::types::EGLint),
720     extensions: Vec<String>,
721     api: Api,
722     version: Option<(u8, u8)>,
723     config_id: ffi::egl::types::EGLConfig,
724     pixel_format: PixelFormat,
725 }
726 
727 impl<'a> ContextPrototype<'a> {
728     #[cfg(any(
729         target_os = "linux",
730         target_os = "dragonfly",
731         target_os = "freebsd",
732         target_os = "netbsd",
733         target_os = "openbsd",
734     ))]
get_native_visual_id(&self) -> ffi::egl::types::EGLint735     pub fn get_native_visual_id(&self) -> ffi::egl::types::EGLint {
736         let egl = EGL.as_ref().unwrap();
737         let mut value = unsafe { std::mem::uninitialized() };
738         let ret = unsafe {
739             egl.GetConfigAttrib(
740                 self.display,
741                 self.config_id,
742                 ffi::egl::NATIVE_VISUAL_ID as ffi::egl::types::EGLint,
743                 &mut value,
744             )
745         };
746         if ret == 0 {
747             panic!(
748                 "get_native_visual_id: eglGetConfigAttrib failed with 0x{:x}",
749                 unsafe { egl.GetError() }
750             )
751         };
752         value
753     }
754 
finish( self, nwin: ffi::EGLNativeWindowType, ) -> Result<Context, CreationError>755     pub fn finish(
756         self,
757         nwin: ffi::EGLNativeWindowType,
758     ) -> Result<Context, CreationError> {
759         let egl = EGL.as_ref().unwrap();
760         let surface = unsafe {
761             let surface = egl.CreateWindowSurface(
762                 self.display,
763                 self.config_id,
764                 nwin,
765                 std::ptr::null(),
766             );
767             if surface.is_null() {
768                 return Err(CreationError::OsError(
769                     "eglCreateWindowSurface failed".to_string(),
770                 ));
771             }
772             surface
773         };
774 
775         self.finish_impl(Some(surface))
776     }
777 
778     #[cfg(any(
779         target_os = "linux",
780         target_os = "dragonfly",
781         target_os = "freebsd",
782         target_os = "netbsd",
783         target_os = "openbsd",
784     ))]
finish_surfaceless(self) -> Result<Context, CreationError>785     pub fn finish_surfaceless(self) -> Result<Context, CreationError> {
786         // FIXME: Also check for the GL_OES_surfaceless_context *CONTEXT*
787         // extension
788         if self
789             .extensions
790             .iter()
791             .find(|s| s == &"EGL_KHR_surfaceless_context")
792             .is_none()
793         {
794             Err(CreationError::NotSupported(
795                 "EGL surfaceless not supported".to_string(),
796             ))
797         } else {
798             self.finish_impl(None)
799         }
800     }
801 
802     #[cfg(any(
803         target_os = "android",
804         target_os = "windows",
805         target_os = "linux",
806         target_os = "dragonfly",
807         target_os = "freebsd",
808         target_os = "netbsd",
809         target_os = "openbsd",
810     ))]
finish_pbuffer( self, size: dpi::PhysicalSize, ) -> Result<Context, CreationError>811     pub fn finish_pbuffer(
812         self,
813         size: dpi::PhysicalSize,
814     ) -> Result<Context, CreationError> {
815         let size: (u32, u32) = size.into();
816 
817         let egl = EGL.as_ref().unwrap();
818         let tex_fmt = if self.pixel_format.alpha_bits > 0 {
819             ffi::egl::TEXTURE_RGBA
820         } else {
821             ffi::egl::TEXTURE_RGB
822         };
823         let attrs = &[
824             ffi::egl::WIDTH as raw::c_int,
825             size.0 as raw::c_int,
826             ffi::egl::HEIGHT as raw::c_int,
827             size.1 as raw::c_int,
828             ffi::egl::NONE as raw::c_int,
829         ];
830 
831         let surface = unsafe {
832             let surface = egl.CreatePbufferSurface(
833                 self.display,
834                 self.config_id,
835                 attrs.as_ptr(),
836             );
837             if surface.is_null() || surface == ffi::egl::NO_SURFACE {
838                 return Err(CreationError::OsError(
839                     "eglCreatePbufferSurface failed".to_string(),
840                 ));
841             }
842             surface
843         };
844 
845         self.finish_impl(Some(surface))
846     }
847 
finish_impl( self, surface: Option<ffi::egl::types::EGLSurface>, ) -> Result<Context, CreationError>848     fn finish_impl(
849         self,
850         surface: Option<ffi::egl::types::EGLSurface>,
851     ) -> Result<Context, CreationError> {
852         let share = match self.opengl.sharing {
853             Some(ctx) => ctx.context,
854             None => std::ptr::null(),
855         };
856 
857         let context = unsafe {
858             if let Some(version) = self.version {
859                 create_context(
860                     self.display,
861                     &self.egl_version,
862                     &self.extensions,
863                     self.api,
864                     version,
865                     self.config_id,
866                     self.opengl.debug,
867                     self.opengl.robustness,
868                     share,
869                 )?
870             } else if self.api == Api::OpenGlEs {
871                 if let Ok(ctx) = create_context(
872                     self.display,
873                     &self.egl_version,
874                     &self.extensions,
875                     self.api,
876                     (2, 0),
877                     self.config_id,
878                     self.opengl.debug,
879                     self.opengl.robustness,
880                     share,
881                 ) {
882                     ctx
883                 } else if let Ok(ctx) = create_context(
884                     self.display,
885                     &self.egl_version,
886                     &self.extensions,
887                     self.api,
888                     (1, 0),
889                     self.config_id,
890                     self.opengl.debug,
891                     self.opengl.robustness,
892                     share,
893                 ) {
894                     ctx
895                 } else {
896                     return Err(CreationError::OpenGlVersionNotSupported);
897                 }
898             } else {
899                 if let Ok(ctx) = create_context(
900                     self.display,
901                     &self.egl_version,
902                     &self.extensions,
903                     self.api,
904                     (3, 2),
905                     self.config_id,
906                     self.opengl.debug,
907                     self.opengl.robustness,
908                     share,
909                 ) {
910                     ctx
911                 } else if let Ok(ctx) = create_context(
912                     self.display,
913                     &self.egl_version,
914                     &self.extensions,
915                     self.api,
916                     (3, 1),
917                     self.config_id,
918                     self.opengl.debug,
919                     self.opengl.robustness,
920                     share,
921                 ) {
922                     ctx
923                 } else if let Ok(ctx) = create_context(
924                     self.display,
925                     &self.egl_version,
926                     &self.extensions,
927                     self.api,
928                     (1, 0),
929                     self.config_id,
930                     self.opengl.debug,
931                     self.opengl.robustness,
932                     share,
933                 ) {
934                     ctx
935                 } else {
936                     return Err(CreationError::OpenGlVersionNotSupported);
937                 }
938             }
939         };
940 
941         Ok(Context {
942             display: self.display,
943             context,
944             surface: surface.map(|s| Mutex::new(s)),
945             api: self.api,
946             pixel_format: self.pixel_format,
947             #[cfg(target_os = "android")]
948             config_id: self.config_id,
949         })
950     }
951 }
952 
choose_fbconfig( egl: &Egl, display: ffi::egl::types::EGLDisplay, egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), api: Api, version: Option<(u8, u8)>, reqs: &PixelFormatRequirements, surface_type: SurfaceType, ) -> Result<(ffi::egl::types::EGLConfig, PixelFormat), CreationError>953 unsafe fn choose_fbconfig(
954     egl: &Egl,
955     display: ffi::egl::types::EGLDisplay,
956     egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint),
957     api: Api,
958     version: Option<(u8, u8)>,
959     reqs: &PixelFormatRequirements,
960     surface_type: SurfaceType,
961 ) -> Result<(ffi::egl::types::EGLConfig, PixelFormat), CreationError> {
962     let descriptor = {
963         let mut out: Vec<raw::c_int> = Vec::with_capacity(37);
964 
965         if egl_version >= &(1, 2) {
966             out.push(ffi::egl::COLOR_BUFFER_TYPE as raw::c_int);
967             out.push(ffi::egl::RGB_BUFFER as raw::c_int);
968         }
969 
970         if surface_type != SurfaceType::Surfaceless {
971             out.push(ffi::egl::SURFACE_TYPE as raw::c_int);
972             let surface_type = match surface_type {
973                 SurfaceType::Window => ffi::egl::WINDOW_BIT,
974                 SurfaceType::PBuffer => ffi::egl::PBUFFER_BIT,
975                 _ => unreachable!(),
976             };
977             out.push(surface_type as raw::c_int);
978         }
979 
980         match (api, version) {
981             (Api::OpenGlEs, Some((3, _))) => {
982                 if egl_version < &(1, 3) {
983                     return Err(CreationError::NoAvailablePixelFormat);
984                 }
985                 out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int);
986                 out.push(ffi::egl::OPENGL_ES3_BIT as raw::c_int);
987                 out.push(ffi::egl::CONFORMANT as raw::c_int);
988                 out.push(ffi::egl::OPENGL_ES3_BIT as raw::c_int);
989             }
990             (Api::OpenGlEs, Some((2, _))) => {
991                 if egl_version < &(1, 3) {
992                     return Err(CreationError::NoAvailablePixelFormat);
993                 }
994                 out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int);
995                 out.push(ffi::egl::OPENGL_ES2_BIT as raw::c_int);
996                 out.push(ffi::egl::CONFORMANT as raw::c_int);
997                 out.push(ffi::egl::OPENGL_ES2_BIT as raw::c_int);
998             }
999             (Api::OpenGlEs, Some((1, _))) => {
1000                 if egl_version >= &(1, 3) {
1001                     out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int);
1002                     out.push(ffi::egl::OPENGL_ES_BIT as raw::c_int);
1003                     out.push(ffi::egl::CONFORMANT as raw::c_int);
1004                     out.push(ffi::egl::OPENGL_ES_BIT as raw::c_int);
1005                 }
1006             }
1007             (Api::OpenGlEs, _) => unimplemented!(),
1008             (Api::OpenGl, _) => {
1009                 if egl_version < &(1, 3) {
1010                     return Err(CreationError::NoAvailablePixelFormat);
1011                 }
1012                 out.push(ffi::egl::RENDERABLE_TYPE as raw::c_int);
1013                 out.push(ffi::egl::OPENGL_BIT as raw::c_int);
1014                 out.push(ffi::egl::CONFORMANT as raw::c_int);
1015                 out.push(ffi::egl::OPENGL_BIT as raw::c_int);
1016             }
1017             (_, _) => unimplemented!(),
1018         };
1019 
1020         if let Some(hardware_accelerated) = reqs.hardware_accelerated {
1021             out.push(ffi::egl::CONFIG_CAVEAT as raw::c_int);
1022             out.push(if hardware_accelerated {
1023                 ffi::egl::NONE as raw::c_int
1024             } else {
1025                 ffi::egl::SLOW_CONFIG as raw::c_int
1026             });
1027         }
1028 
1029         if let Some(color) = reqs.color_bits {
1030             out.push(ffi::egl::RED_SIZE as raw::c_int);
1031             out.push((color / 3) as raw::c_int);
1032             out.push(ffi::egl::GREEN_SIZE as raw::c_int);
1033             out.push(
1034                 (color / 3 + if color % 3 != 0 { 1 } else { 0 }) as raw::c_int,
1035             );
1036             out.push(ffi::egl::BLUE_SIZE as raw::c_int);
1037             out.push(
1038                 (color / 3 + if color % 3 == 2 { 1 } else { 0 }) as raw::c_int,
1039             );
1040         }
1041 
1042         if let Some(alpha) = reqs.alpha_bits {
1043             out.push(ffi::egl::ALPHA_SIZE as raw::c_int);
1044             out.push(alpha as raw::c_int);
1045         }
1046 
1047         if let Some(depth) = reqs.depth_bits {
1048             out.push(ffi::egl::DEPTH_SIZE as raw::c_int);
1049             out.push(depth as raw::c_int);
1050         }
1051 
1052         if let Some(stencil) = reqs.stencil_bits {
1053             out.push(ffi::egl::STENCIL_SIZE as raw::c_int);
1054             out.push(stencil as raw::c_int);
1055         }
1056 
1057         if let Some(true) = reqs.double_buffer {
1058             return Err(CreationError::NoAvailablePixelFormat);
1059         }
1060 
1061         if let Some(multisampling) = reqs.multisampling {
1062             out.push(ffi::egl::SAMPLES as raw::c_int);
1063             out.push(multisampling as raw::c_int);
1064         }
1065 
1066         if reqs.stereoscopy {
1067             return Err(CreationError::NoAvailablePixelFormat);
1068         }
1069 
1070         if let Some(xid) = reqs.x11_visual_xid {
1071             out.push(ffi::egl::NATIVE_VISUAL_ID as raw::c_int);
1072             out.push(xid as raw::c_int);
1073         }
1074 
1075         // FIXME: srgb is not taken into account
1076 
1077         match reqs.release_behavior {
1078             ReleaseBehavior::Flush => (),
1079             ReleaseBehavior::None => {
1080                 // TODO: with EGL you need to manually set the behavior
1081                 unimplemented!()
1082             }
1083         }
1084 
1085         out.push(ffi::egl::NONE as raw::c_int);
1086         out
1087     };
1088 
1089     // calling `eglChooseConfig`
1090     let mut config_id = std::mem::uninitialized();
1091     let mut num_configs = std::mem::uninitialized();
1092     if egl.ChooseConfig(
1093         display,
1094         descriptor.as_ptr(),
1095         &mut config_id,
1096         1,
1097         &mut num_configs,
1098     ) == 0
1099     {
1100         return Err(CreationError::OsError(
1101             "eglChooseConfig failed".to_string(),
1102         ));
1103     }
1104     if num_configs == 0 {
1105         return Err(CreationError::NoAvailablePixelFormat);
1106     }
1107 
1108     // analyzing each config
1109     macro_rules! attrib {
1110         ($egl:expr, $display:expr, $config:expr, $attr:expr) => {{
1111             let mut value = std::mem::uninitialized();
1112             let res = $egl.GetConfigAttrib(
1113                 $display,
1114                 $config,
1115                 $attr as ffi::egl::types::EGLint,
1116                 &mut value,
1117             );
1118             if res == 0 {
1119                 return Err(CreationError::OsError(
1120                     "eglGetConfigAttrib failed".to_string(),
1121                 ));
1122             }
1123             value
1124         }};
1125     };
1126 
1127     let desc = PixelFormat {
1128         hardware_accelerated: attrib!(
1129             egl,
1130             display,
1131             config_id,
1132             ffi::egl::CONFIG_CAVEAT
1133         ) != ffi::egl::SLOW_CONFIG as i32,
1134         color_bits: attrib!(egl, display, config_id, ffi::egl::RED_SIZE) as u8
1135             + attrib!(egl, display, config_id, ffi::egl::BLUE_SIZE) as u8
1136             + attrib!(egl, display, config_id, ffi::egl::GREEN_SIZE) as u8,
1137         alpha_bits: attrib!(egl, display, config_id, ffi::egl::ALPHA_SIZE)
1138             as u8,
1139         depth_bits: attrib!(egl, display, config_id, ffi::egl::DEPTH_SIZE)
1140             as u8,
1141         stencil_bits: attrib!(egl, display, config_id, ffi::egl::STENCIL_SIZE)
1142             as u8,
1143         stereoscopy: false,
1144         double_buffer: true,
1145         multisampling: match attrib!(egl, display, config_id, ffi::egl::SAMPLES)
1146         {
1147             0 | 1 => None,
1148             a => Some(a as u16),
1149         },
1150         srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that
1151     };
1152 
1153     Ok((config_id, desc))
1154 }
1155 
create_context( display: ffi::egl::types::EGLDisplay, egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), extensions: &[String], api: Api, version: (u8, u8), config_id: ffi::egl::types::EGLConfig, gl_debug: bool, gl_robustness: Robustness, share: ffi::EGLContext, ) -> Result<ffi::egl::types::EGLContext, CreationError>1156 unsafe fn create_context(
1157     display: ffi::egl::types::EGLDisplay,
1158     egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint),
1159     extensions: &[String],
1160     api: Api,
1161     version: (u8, u8),
1162     config_id: ffi::egl::types::EGLConfig,
1163     gl_debug: bool,
1164     gl_robustness: Robustness,
1165     share: ffi::EGLContext,
1166 ) -> Result<ffi::egl::types::EGLContext, CreationError> {
1167     let egl = EGL.as_ref().unwrap();
1168 
1169     let mut context_attributes = Vec::with_capacity(10);
1170     let mut flags = 0;
1171 
1172     if egl_version >= &(1, 5)
1173         || extensions
1174             .iter()
1175             .find(|s| s == &"EGL_KHR_create_context")
1176             .is_some()
1177     {
1178         context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32);
1179         context_attributes.push(version.0 as i32);
1180         context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
1181         context_attributes.push(version.1 as i32);
1182 
1183         // handling robustness
1184         let supports_robustness = egl_version >= &(1, 5)
1185             || extensions
1186                 .iter()
1187                 .find(|s| s == &"EGL_EXT_create_context_robustness")
1188                 .is_some();
1189 
1190         match gl_robustness {
1191             Robustness::NotRobust => (),
1192 
1193             Robustness::NoError => {
1194                 if extensions
1195                     .iter()
1196                     .find(|s| s == &"EGL_KHR_create_context_no_error")
1197                     .is_some()
1198                 {
1199                     context_attributes.push(
1200                         ffi::egl::CONTEXT_OPENGL_NO_ERROR_KHR as raw::c_int,
1201                     );
1202                     context_attributes.push(1);
1203                 }
1204             }
1205 
1206             Robustness::RobustNoResetNotification => {
1207                 if supports_robustness {
1208                     context_attributes.push(
1209                         ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
1210                             as raw::c_int,
1211                     );
1212                     context_attributes
1213                         .push(ffi::egl::NO_RESET_NOTIFICATION as raw::c_int);
1214                     flags = flags
1215                         | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int;
1216                 } else {
1217                     return Err(CreationError::RobustnessNotSupported);
1218                 }
1219             }
1220 
1221             Robustness::TryRobustNoResetNotification => {
1222                 if supports_robustness {
1223                     context_attributes.push(
1224                         ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
1225                             as raw::c_int,
1226                     );
1227                     context_attributes
1228                         .push(ffi::egl::NO_RESET_NOTIFICATION as raw::c_int);
1229                     flags = flags
1230                         | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int;
1231                 }
1232             }
1233 
1234             Robustness::RobustLoseContextOnReset => {
1235                 if supports_robustness {
1236                     context_attributes.push(
1237                         ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
1238                             as raw::c_int,
1239                     );
1240                     context_attributes
1241                         .push(ffi::egl::LOSE_CONTEXT_ON_RESET as raw::c_int);
1242                     flags = flags
1243                         | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int;
1244                 } else {
1245                     return Err(CreationError::RobustnessNotSupported);
1246                 }
1247             }
1248 
1249             Robustness::TryRobustLoseContextOnReset => {
1250                 if supports_robustness {
1251                     context_attributes.push(
1252                         ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY
1253                             as raw::c_int,
1254                     );
1255                     context_attributes
1256                         .push(ffi::egl::LOSE_CONTEXT_ON_RESET as raw::c_int);
1257                     flags = flags
1258                         | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as raw::c_int;
1259                 }
1260             }
1261         }
1262 
1263         if gl_debug {
1264             if egl_version >= &(1, 5) {
1265                 context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
1266                 context_attributes.push(ffi::egl::TRUE as i32);
1267             }
1268 
1269             // TODO: using this flag sometimes generates an error
1270             //       there was a change in the specs that added this flag, so it
1271             // may not be       supported everywhere ; however it is
1272             // not possible to know whether it is       supported or
1273             // not flags = flags |
1274             // ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32;
1275         }
1276 
1277         // In at least some configurations, the Android emulator’s GL
1278         // implementation advertises support for the
1279         // EGL_KHR_create_context extension but returns BAD_ATTRIBUTE
1280         // when CONTEXT_FLAGS_KHR is used.
1281         if flags != 0 {
1282             context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
1283             context_attributes.push(flags);
1284         }
1285     } else if egl_version >= &(1, 3) && api == Api::OpenGlEs {
1286         // robustness is not supported
1287         match gl_robustness {
1288             Robustness::RobustNoResetNotification
1289             | Robustness::RobustLoseContextOnReset => {
1290                 return Err(CreationError::RobustnessNotSupported);
1291             }
1292             _ => (),
1293         }
1294 
1295         context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
1296         context_attributes.push(version.0 as i32);
1297     }
1298 
1299     context_attributes.push(ffi::egl::NONE as i32);
1300 
1301     let context = egl.CreateContext(
1302         display,
1303         config_id,
1304         share,
1305         context_attributes.as_ptr(),
1306     );
1307 
1308     if context.is_null() {
1309         match egl.GetError() as u32 {
1310             ffi::egl::BAD_MATCH | ffi::egl::BAD_ATTRIBUTE => {
1311                 return Err(CreationError::OpenGlVersionNotSupported);
1312             }
1313             e => panic!("create_context: eglCreateContext failed: 0x{:x}", e),
1314         }
1315     }
1316 
1317     Ok(context)
1318 }
1319