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