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