1use glow::HasContext;
2use parking_lot::{Mutex, MutexGuard};
3
4use std::{ffi, os::raw, ptr, rc::Rc, sync::Arc, time::Duration};
5
6const CONTEXT_LOCK_TIMEOUT_SECS: u64 = 1;
8
9const EGL_CONTEXT_FLAGS_KHR: i32 = 0x30FC;
10const EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR: i32 = 0x0001;
11const EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT: i32 = 0x30BF;
12const EGL_PLATFORM_WAYLAND_KHR: u32 = 0x31D8;
13const EGL_PLATFORM_X11_KHR: u32 = 0x31D5;
14const EGL_PLATFORM_ANGLE_ANGLE: u32 = 0x3202;
15const EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE: u32 = 0x348F;
16const EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED: u32 = 0x3451;
17const EGL_PLATFORM_SURFACELESS_MESA: u32 = 0x31DD;
18const EGL_GL_COLORSPACE_KHR: u32 = 0x309D;
19const EGL_GL_COLORSPACE_SRGB_KHR: u32 = 0x3089;
20
21type XOpenDisplayFun =
22 unsafe extern "system" fn(display_name: *const raw::c_char) -> *mut raw::c_void;
23
24type XCloseDisplayFun = unsafe extern "system" fn(display: *mut raw::c_void) -> raw::c_int;
25
26type WlDisplayConnectFun =
27 unsafe extern "system" fn(display_name: *const raw::c_char) -> *mut raw::c_void;
28
29type WlDisplayDisconnectFun = unsafe extern "system" fn(display: *const raw::c_void);
30
31#[cfg(not(target_os = "emscripten"))]
32type EglInstance = khronos_egl::DynamicInstance<khronos_egl::EGL1_4>;
33
34#[cfg(target_os = "emscripten")]
35type EglInstance = khronos_egl::Instance<khronos_egl::Static>;
36
37type WlEglWindowCreateFun = unsafe extern "system" fn(
38 surface: *const raw::c_void,
39 width: raw::c_int,
40 height: raw::c_int,
41) -> *mut raw::c_void;
42
43type WlEglWindowResizeFun = unsafe extern "system" fn(
44 window: *const raw::c_void,
45 width: raw::c_int,
46 height: raw::c_int,
47 dx: raw::c_int,
48 dy: raw::c_int,
49);
50
51type WlEglWindowDestroyFun = unsafe extern "system" fn(window: *const raw::c_void);
52
53#[cfg(target_os = "android")]
54extern "C" {
55 pub fn ANativeWindow_setBuffersGeometry(
56 window: *mut raw::c_void,
57 width: i32,
58 height: i32,
59 format: i32,
60 ) -> i32;
61}
62
63type EglLabel = *const raw::c_void;
64
65#[allow(clippy::upper_case_acronyms)]
66type EGLDEBUGPROCKHR = Option<
67 unsafe extern "system" fn(
68 error: khronos_egl::Enum,
69 command: *const raw::c_char,
70 message_type: u32,
71 thread_label: EglLabel,
72 object_label: EglLabel,
73 message: *const raw::c_char,
74 ),
75>;
76
77const EGL_DEBUG_MSG_CRITICAL_KHR: u32 = 0x33B9;
78const EGL_DEBUG_MSG_ERROR_KHR: u32 = 0x33BA;
79const EGL_DEBUG_MSG_WARN_KHR: u32 = 0x33BB;
80const EGL_DEBUG_MSG_INFO_KHR: u32 = 0x33BC;
81
82type EglDebugMessageControlFun = unsafe extern "system" fn(
83 proc: EGLDEBUGPROCKHR,
84 attrib_list: *const khronos_egl::Attrib,
85) -> raw::c_int;
86
87unsafe extern "system" fn egl_debug_proc(
88 error: khronos_egl::Enum,
89 command_raw: *const raw::c_char,
90 message_type: u32,
91 _thread_label: EglLabel,
92 _object_label: EglLabel,
93 message_raw: *const raw::c_char,
94) {
95 let log_severity = match message_type {
96 EGL_DEBUG_MSG_CRITICAL_KHR | EGL_DEBUG_MSG_ERROR_KHR => log::Level::Error,
97 EGL_DEBUG_MSG_WARN_KHR => log::Level::Warn,
98 EGL_DEBUG_MSG_INFO_KHR => log::Level::Info,
99 _ => log::Level::Debug,
100 };
101 let command = unsafe { ffi::CStr::from_ptr(command_raw) }.to_string_lossy();
102 let message = if message_raw.is_null() {
103 "".into()
104 } else {
105 unsafe { ffi::CStr::from_ptr(message_raw) }.to_string_lossy()
106 };
107
108 log::log!(
109 log_severity,
110 "EGL '{}' code 0x{:x}: {}",
111 command,
112 error,
113 message,
114 );
115}
116
117#[derive(Debug)]
122enum DisplayRef {
123 X11(ptr::NonNull<raw::c_void>),
124 Wayland,
125}
126
127impl DisplayRef {
128 fn as_ptr(&self) -> *mut raw::c_void {
130 match *self {
131 Self::X11(ptr) => ptr.as_ptr(),
132 Self::Wayland => unreachable!(),
133 }
134 }
135}
136
137#[derive(Debug)]
143struct DisplayOwner {
144 library: libloading::Library,
145 display: DisplayRef,
146}
147
148impl Drop for DisplayOwner {
149 fn drop(&mut self) {
150 match self.display {
151 DisplayRef::X11(ptr) => unsafe {
152 let func: libloading::Symbol<XCloseDisplayFun> =
153 self.library.get(b"XCloseDisplay").unwrap();
154 func(ptr.as_ptr());
155 },
156 DisplayRef::Wayland => {}
157 }
158 }
159}
160
161fn open_x_display() -> Option<DisplayOwner> {
162 log::info!("Loading X11 library to get the current display");
163 unsafe {
164 let library = libloading::Library::new("libX11.so").ok()?;
165 let func: libloading::Symbol<XOpenDisplayFun> = library.get(b"XOpenDisplay").unwrap();
166 let result = func(ptr::null());
167 ptr::NonNull::new(result).map(|ptr| DisplayOwner {
168 display: DisplayRef::X11(ptr),
169 library,
170 })
171 }
172}
173
174unsafe fn find_library(paths: &[&str]) -> Option<libloading::Library> {
175 for path in paths {
176 match unsafe { libloading::Library::new(path) } {
177 Ok(lib) => return Some(lib),
178 _ => continue,
179 };
180 }
181 None
182}
183
184fn test_wayland_display() -> Option<DisplayOwner> {
185 log::info!("Loading Wayland library to get the current display");
189 let library = unsafe {
190 let client_library = find_library(&["libwayland-client.so.0", "libwayland-client.so"])?;
191 let wl_display_connect: libloading::Symbol<WlDisplayConnectFun> =
192 client_library.get(b"wl_display_connect").unwrap();
193 let wl_display_disconnect: libloading::Symbol<WlDisplayDisconnectFun> =
194 client_library.get(b"wl_display_disconnect").unwrap();
195 let display = ptr::NonNull::new(wl_display_connect(ptr::null()))?;
196 wl_display_disconnect(display.as_ptr());
197 find_library(&["libwayland-egl.so.1", "libwayland-egl.so"])?
198 };
199 Some(DisplayOwner {
200 library,
201 display: DisplayRef::Wayland,
202 })
203}
204
205#[derive(Clone, Copy, Debug)]
206enum SrgbFrameBufferKind {
207 None,
209 Core,
211 Khr,
213}
214
215fn choose_config(
217 egl: &EglInstance,
218 display: khronos_egl::Display,
219 srgb_kind: SrgbFrameBufferKind,
220) -> Result<(khronos_egl::Config, bool), crate::InstanceError> {
221 let tiers = [
223 (
224 "off-screen",
225 &[
226 khronos_egl::SURFACE_TYPE,
227 khronos_egl::PBUFFER_BIT,
228 khronos_egl::RENDERABLE_TYPE,
229 khronos_egl::OPENGL_ES2_BIT,
230 ][..],
231 ),
232 (
233 "presentation",
234 &[khronos_egl::SURFACE_TYPE, khronos_egl::WINDOW_BIT][..],
235 ),
236 #[cfg(not(target_os = "android"))]
237 (
238 "native-render",
239 &[khronos_egl::NATIVE_RENDERABLE, khronos_egl::TRUE as _][..],
240 ),
241 ];
242
243 let mut attributes = Vec::with_capacity(9);
244 for tier_max in (0..tiers.len()).rev() {
245 let name = tiers[tier_max].0;
246 log::info!("\tTrying {}", name);
247
248 attributes.clear();
249 for &(_, tier_attr) in tiers[..=tier_max].iter() {
250 attributes.extend_from_slice(tier_attr);
251 }
252 match srgb_kind {
254 SrgbFrameBufferKind::None => {}
255 _ => {
256 attributes.push(khronos_egl::ALPHA_SIZE);
257 attributes.push(8);
258 }
259 }
260 attributes.push(khronos_egl::NONE);
261
262 match egl.choose_first_config(display, &attributes) {
263 Ok(Some(config)) => {
264 if tier_max == 1 {
265 log::warn!("EGL says it can present to the window but not natively",);
268 }
269 let tier_threshold = if cfg!(target_os = "android") || cfg!(windows) {
271 1
272 } else {
273 2
274 };
275 return Ok((config, tier_max >= tier_threshold));
276 }
277 Ok(None) => {
278 log::warn!("No config found!");
279 }
280 Err(e) => {
281 log::error!("error in choose_first_config: {:?}", e);
282 }
283 }
284 }
285
286 Err(crate::InstanceError::new(String::from(
288 "unable to find an acceptable EGL framebuffer configuration",
289 )))
290}
291
292#[derive(Clone, Debug)]
293struct EglContext {
294 instance: Arc<EglInstance>,
295 version: (i32, i32),
296 display: khronos_egl::Display,
297 raw: khronos_egl::Context,
298 pbuffer: Option<khronos_egl::Surface>,
299}
300
301impl EglContext {
302 fn make_current(&self) {
303 self.instance
304 .make_current(self.display, self.pbuffer, self.pbuffer, Some(self.raw))
305 .unwrap();
306 }
307 fn unmake_current(&self) {
308 self.instance
309 .make_current(self.display, None, None, None)
310 .unwrap();
311 }
312}
313
314pub struct AdapterContext {
317 glow: Mutex<glow::Context>,
318 egl: Option<EglContext>,
319}
320
321unsafe impl Sync for AdapterContext {}
322unsafe impl Send for AdapterContext {}
323
324impl AdapterContext {
325 pub fn is_owned(&self) -> bool {
326 self.egl.is_some()
327 }
328
329 pub fn egl_instance(&self) -> Option<&EglInstance> {
333 self.egl.as_ref().map(|egl| &*egl.instance)
334 }
335
336 pub fn raw_display(&self) -> Option<&khronos_egl::Display> {
340 self.egl.as_ref().map(|egl| &egl.display)
341 }
342
343 pub fn egl_version(&self) -> Option<(i32, i32)> {
347 self.egl.as_ref().map(|egl| egl.version)
348 }
349
350 pub fn raw_context(&self) -> *mut raw::c_void {
351 match self.egl {
352 Some(ref egl) => egl.raw.as_ptr(),
353 None => ptr::null_mut(),
354 }
355 }
356}
357
358struct EglContextLock<'a> {
359 instance: &'a Arc<EglInstance>,
360 display: khronos_egl::Display,
361}
362
363pub struct AdapterContextLock<'a> {
365 glow: MutexGuard<'a, glow::Context>,
366 egl: Option<EglContextLock<'a>>,
367}
368
369impl<'a> std::ops::Deref for AdapterContextLock<'a> {
370 type Target = glow::Context;
371
372 fn deref(&self) -> &Self::Target {
373 &self.glow
374 }
375}
376
377impl<'a> Drop for AdapterContextLock<'a> {
378 fn drop(&mut self) {
379 if let Some(egl) = self.egl.take() {
380 egl.instance
381 .make_current(egl.display, None, None, None)
382 .unwrap();
383 }
384 }
385}
386
387impl AdapterContext {
388 pub unsafe fn get_without_egl_lock(&self) -> MutexGuard<glow::Context> {
400 self.glow
401 .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
402 .expect("Could not lock adapter context. This is most-likely a deadlock.")
403 }
404
405 #[track_caller]
408 pub fn lock<'a>(&'a self) -> AdapterContextLock<'a> {
409 let glow = self
410 .glow
411 .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
414 .expect("Could not lock adapter context. This is most-likely a deadlock.");
415
416 let egl = self.egl.as_ref().map(|egl| {
417 egl.make_current();
418 EglContextLock {
419 instance: &egl.instance,
420 display: egl.display,
421 }
422 });
423
424 AdapterContextLock { glow, egl }
425 }
426}
427
428#[derive(Debug)]
429struct Inner {
430 egl: EglContext,
433 #[allow(unused)]
434 version: (i32, i32),
435 supports_native_window: bool,
436 config: khronos_egl::Config,
437 #[cfg_attr(target_os = "emscripten", allow(dead_code))]
438 wl_display: Option<*mut raw::c_void>,
439 #[cfg_attr(target_os = "emscripten", allow(dead_code))]
440 force_gles_minor_version: wgt::Gles3MinorVersion,
441 srgb_kind: SrgbFrameBufferKind,
443}
444
445impl Inner {
446 fn create(
447 flags: wgt::InstanceFlags,
448 egl: Arc<EglInstance>,
449 display: khronos_egl::Display,
450 force_gles_minor_version: wgt::Gles3MinorVersion,
451 ) -> Result<Self, crate::InstanceError> {
452 let version = egl.initialize(display).map_err(|e| {
453 crate::InstanceError::with_source(
454 String::from("failed to initialize EGL display connection"),
455 e,
456 )
457 })?;
458 let vendor = egl
459 .query_string(Some(display), khronos_egl::VENDOR)
460 .unwrap();
461 let display_extensions = egl
462 .query_string(Some(display), khronos_egl::EXTENSIONS)
463 .unwrap()
464 .to_string_lossy();
465 log::info!("Display vendor {:?}, version {:?}", vendor, version,);
466 log::debug!(
467 "Display extensions: {:#?}",
468 display_extensions.split_whitespace().collect::<Vec<_>>()
469 );
470
471 let srgb_kind = if version >= (1, 5) {
472 log::info!("\tEGL surface: +srgb");
473 SrgbFrameBufferKind::Core
474 } else if display_extensions.contains("EGL_KHR_gl_colorspace") {
475 log::info!("\tEGL surface: +srgb khr");
476 SrgbFrameBufferKind::Khr
477 } else {
478 log::warn!("\tEGL surface: -srgb");
479 SrgbFrameBufferKind::None
480 };
481
482 if log::max_level() >= log::LevelFilter::Trace {
483 log::trace!("Configurations:");
484 let config_count = egl.get_config_count(display).unwrap();
485 let mut configurations = Vec::with_capacity(config_count);
486 egl.get_configs(display, &mut configurations).unwrap();
487 for &config in configurations.iter() {
488 log::trace!("\tCONFORMANT=0x{:X}, RENDERABLE=0x{:X}, NATIVE_RENDERABLE=0x{:X}, SURFACE_TYPE=0x{:X}, ALPHA_SIZE={}",
489 egl.get_config_attrib(display, config, khronos_egl::CONFORMANT).unwrap(),
490 egl.get_config_attrib(display, config, khronos_egl::RENDERABLE_TYPE).unwrap(),
491 egl.get_config_attrib(display, config, khronos_egl::NATIVE_RENDERABLE).unwrap(),
492 egl.get_config_attrib(display, config, khronos_egl::SURFACE_TYPE).unwrap(),
493 egl.get_config_attrib(display, config, khronos_egl::ALPHA_SIZE).unwrap(),
494 );
495 }
496 }
497
498 let (config, supports_native_window) = choose_config(&egl, display, srgb_kind)?;
499 egl.bind_api(khronos_egl::OPENGL_ES_API).unwrap();
500
501 let needs_robustness = true;
502 let mut khr_context_flags = 0;
503 let supports_khr_context = display_extensions.contains("EGL_KHR_create_context");
504
505 let mut context_attributes = vec![
507 khronos_egl::CONTEXT_MAJOR_VERSION,
508 3, ];
510
511 if force_gles_minor_version != wgt::Gles3MinorVersion::Automatic {
512 context_attributes.push(khronos_egl::CONTEXT_MINOR_VERSION);
513 context_attributes.push(match force_gles_minor_version {
514 wgt::Gles3MinorVersion::Version0 => 0,
515 wgt::Gles3MinorVersion::Version1 => 1,
516 wgt::Gles3MinorVersion::Version2 => 2,
517 _ => unreachable!(),
518 });
519 }
520
521 if flags.contains(wgt::InstanceFlags::DEBUG) {
522 if version >= (1, 5) {
523 log::info!("\tEGL context: +debug");
524 context_attributes.push(khronos_egl::CONTEXT_OPENGL_DEBUG);
525 context_attributes.push(khronos_egl::TRUE as _);
526 } else if supports_khr_context {
527 log::info!("\tEGL context: +debug KHR");
528 khr_context_flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
529 } else {
530 log::info!("\tEGL context: -debug");
531 }
532 }
533 if needs_robustness {
534 if version >= (1, 5) && !display_extensions.contains("EGL_ANGLE_") {
538 log::info!("\tEGL context: +robust access");
539 context_attributes.push(khronos_egl::CONTEXT_OPENGL_ROBUST_ACCESS);
540 context_attributes.push(khronos_egl::TRUE as _);
541 } else if display_extensions.contains("EGL_EXT_create_context_robustness") {
542 log::info!("\tEGL context: +robust access EXT");
543 context_attributes.push(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT);
544 context_attributes.push(khronos_egl::TRUE as _);
545 } else {
546 log::warn!("\tEGL context: -robust access");
549 }
550
551 }
553 if khr_context_flags != 0 {
554 context_attributes.push(EGL_CONTEXT_FLAGS_KHR);
555 context_attributes.push(khr_context_flags);
556 }
557 context_attributes.push(khronos_egl::NONE);
558 let context = match egl.create_context(display, config, None, &context_attributes) {
559 Ok(context) => context,
560 Err(e) => {
561 return Err(crate::InstanceError::with_source(
562 String::from("unable to create GLES 3.x context"),
563 e,
564 ));
565 }
566 };
567
568 let pbuffer = if version >= (1, 5)
571 || display_extensions.contains("EGL_KHR_surfaceless_context")
572 || cfg!(target_os = "emscripten")
573 {
574 log::info!("\tEGL context: +surfaceless");
575 None
576 } else {
577 let attributes = [
578 khronos_egl::WIDTH,
579 1,
580 khronos_egl::HEIGHT,
581 1,
582 khronos_egl::NONE,
583 ];
584 egl.create_pbuffer_surface(display, config, &attributes)
585 .map(Some)
586 .map_err(|e| {
587 crate::InstanceError::with_source(
588 String::from("error in create_pbuffer_surface"),
589 e,
590 )
591 })?
592 };
593
594 Ok(Self {
595 egl: EglContext {
596 instance: egl,
597 display,
598 raw: context,
599 pbuffer,
600 version,
601 },
602 version,
603 supports_native_window,
604 config,
605 wl_display: None,
606 srgb_kind,
607 force_gles_minor_version,
608 })
609 }
610}
611
612impl Drop for Inner {
613 fn drop(&mut self) {
614 if let Err(e) = self
615 .egl
616 .instance
617 .destroy_context(self.egl.display, self.egl.raw)
618 {
619 log::warn!("Error in destroy_context: {:?}", e);
620 }
621 if let Err(e) = self.egl.instance.terminate(self.egl.display) {
622 log::warn!("Error in terminate: {:?}", e);
623 }
624 }
625}
626
627#[derive(Clone, Copy, Debug, PartialEq)]
628enum WindowKind {
629 Wayland,
630 X11,
631 AngleX11,
632 Unknown,
633}
634
635#[derive(Clone, Debug)]
636struct WindowSystemInterface {
637 display_owner: Option<Rc<DisplayOwner>>,
638 kind: WindowKind,
639}
640
641pub struct Instance {
642 wsi: WindowSystemInterface,
643 flags: wgt::InstanceFlags,
644 inner: Mutex<Inner>,
645}
646
647impl Instance {
648 pub fn raw_display(&self) -> khronos_egl::Display {
649 self.inner
650 .try_lock()
651 .expect("Could not lock instance. This is most-likely a deadlock.")
652 .egl
653 .display
654 }
655
656 pub fn egl_version(&self) -> (i32, i32) {
658 self.inner
659 .try_lock()
660 .expect("Could not lock instance. This is most-likely a deadlock.")
661 .version
662 }
663
664 pub fn egl_config(&self) -> khronos_egl::Config {
665 self.inner
666 .try_lock()
667 .expect("Could not lock instance. This is most-likely a deadlock.")
668 .config
669 }
670}
671
672unsafe impl Send for Instance {}
673unsafe impl Sync for Instance {}
674
675impl crate::Instance<super::Api> for Instance {
676 unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
677 #[cfg(target_os = "emscripten")]
678 let egl_result: Result<EglInstance, khronos_egl::Error> =
679 Ok(khronos_egl::Instance::new(khronos_egl::Static));
680
681 #[cfg(not(target_os = "emscripten"))]
682 let egl_result = if cfg!(windows) {
683 unsafe {
684 khronos_egl::DynamicInstance::<khronos_egl::EGL1_4>::load_required_from_filename(
685 "libEGL.dll",
686 )
687 }
688 } else if cfg!(any(target_os = "macos", target_os = "ios")) {
689 unsafe {
690 khronos_egl::DynamicInstance::<khronos_egl::EGL1_4>::load_required_from_filename(
691 "libEGL.dylib",
692 )
693 }
694 } else {
695 unsafe { khronos_egl::DynamicInstance::<khronos_egl::EGL1_4>::load_required() }
696 };
697 let egl = match egl_result {
698 Ok(egl) => Arc::new(egl),
699 Err(e) => {
700 return Err(crate::InstanceError::with_source(
701 String::from("unable to open libEGL"),
702 e,
703 ));
704 }
705 };
706
707 let client_extensions = egl.query_string(None, khronos_egl::EXTENSIONS);
708
709 let client_ext_str = match client_extensions {
710 Ok(ext) => ext.to_string_lossy().into_owned(),
711 Err(_) => String::new(),
712 };
713 log::debug!(
714 "Client extensions: {:#?}",
715 client_ext_str.split_whitespace().collect::<Vec<_>>()
716 );
717
718 let wayland_library = if client_ext_str.contains("EGL_EXT_platform_wayland") {
719 test_wayland_display()
720 } else {
721 None
722 };
723 let x11_display_library = if client_ext_str.contains("EGL_EXT_platform_x11") {
724 open_x_display()
725 } else {
726 None
727 };
728 let angle_x11_display_library = if client_ext_str.contains("EGL_ANGLE_platform_angle") {
729 open_x_display()
730 } else {
731 None
732 };
733
734 #[cfg(not(target_os = "emscripten"))]
735 let egl1_5 = egl.upcast::<khronos_egl::EGL1_5>();
736
737 #[cfg(target_os = "emscripten")]
738 let egl1_5: Option<&Arc<EglInstance>> = Some(&egl);
739
740 let (display, display_owner, wsi_kind) =
741 if let (Some(library), Some(egl)) = (wayland_library, egl1_5) {
742 log::info!("Using Wayland platform");
743 let display_attributes = [khronos_egl::ATTRIB_NONE];
744 let display = unsafe {
745 egl.get_platform_display(
746 EGL_PLATFORM_WAYLAND_KHR,
747 khronos_egl::DEFAULT_DISPLAY,
748 &display_attributes,
749 )
750 }
751 .unwrap();
752 (display, Some(Rc::new(library)), WindowKind::Wayland)
753 } else if let (Some(display_owner), Some(egl)) = (x11_display_library, egl1_5) {
754 log::info!("Using X11 platform");
755 let display_attributes = [khronos_egl::ATTRIB_NONE];
756 let display = unsafe {
757 egl.get_platform_display(
758 EGL_PLATFORM_X11_KHR,
759 display_owner.display.as_ptr(),
760 &display_attributes,
761 )
762 }
763 .unwrap();
764 (display, Some(Rc::new(display_owner)), WindowKind::X11)
765 } else if let (Some(display_owner), Some(egl)) = (angle_x11_display_library, egl1_5) {
766 log::info!("Using Angle platform with X11");
767 let display_attributes = [
768 EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE as khronos_egl::Attrib,
769 EGL_PLATFORM_X11_KHR as khronos_egl::Attrib,
770 EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED as khronos_egl::Attrib,
771 usize::from(desc.flags.contains(wgt::InstanceFlags::VALIDATION)),
772 khronos_egl::ATTRIB_NONE,
773 ];
774 let display = unsafe {
775 egl.get_platform_display(
776 EGL_PLATFORM_ANGLE_ANGLE,
777 display_owner.display.as_ptr(),
778 &display_attributes,
779 )
780 }
781 .unwrap();
782 (display, Some(Rc::new(display_owner)), WindowKind::AngleX11)
783 } else if client_ext_str.contains("EGL_MESA_platform_surfaceless") {
784 log::info!("No windowing system present. Using surfaceless platform");
785 let egl = egl1_5.expect("Failed to get EGL 1.5 for surfaceless");
786 let display = unsafe {
787 egl.get_platform_display(
788 EGL_PLATFORM_SURFACELESS_MESA,
789 std::ptr::null_mut(),
790 &[khronos_egl::ATTRIB_NONE],
791 )
792 }
793 .unwrap();
794
795 (display, None, WindowKind::Unknown)
796 } else {
797 log::info!("EGL_MESA_platform_surfaceless not available. Using default platform");
798 let display = unsafe { egl.get_display(khronos_egl::DEFAULT_DISPLAY) }.unwrap();
799 (display, None, WindowKind::Unknown)
800 };
801
802 if desc.flags.contains(wgt::InstanceFlags::VALIDATION)
803 && client_ext_str.contains("EGL_KHR_debug")
804 {
805 log::info!("Enabling EGL debug output");
806 let function: EglDebugMessageControlFun = {
807 let addr = egl.get_proc_address("eglDebugMessageControlKHR").unwrap();
808 unsafe { std::mem::transmute(addr) }
809 };
810 let attributes = [
811 EGL_DEBUG_MSG_CRITICAL_KHR as khronos_egl::Attrib,
812 1,
813 EGL_DEBUG_MSG_ERROR_KHR as khronos_egl::Attrib,
814 1,
815 EGL_DEBUG_MSG_WARN_KHR as khronos_egl::Attrib,
816 1,
817 EGL_DEBUG_MSG_INFO_KHR as khronos_egl::Attrib,
818 1,
819 khronos_egl::ATTRIB_NONE,
820 ];
821 unsafe { (function)(Some(egl_debug_proc), attributes.as_ptr()) };
822 }
823
824 let inner = Inner::create(desc.flags, egl, display, desc.gles_minor_version)?;
825
826 Ok(Instance {
827 wsi: WindowSystemInterface {
828 display_owner,
829 kind: wsi_kind,
830 },
831 flags: desc.flags,
832 inner: Mutex::new(inner),
833 })
834 }
835
836 #[cfg_attr(target_os = "macos", allow(unused, unused_mut, unreachable_code))]
837 unsafe fn create_surface(
838 &self,
839 display_handle: raw_window_handle::RawDisplayHandle,
840 window_handle: raw_window_handle::RawWindowHandle,
841 ) -> Result<Surface, crate::InstanceError> {
842 use raw_window_handle::RawWindowHandle as Rwh;
843
844 #[cfg_attr(
845 any(target_os = "android", target_os = "emscripten"),
846 allow(unused_mut)
847 )]
848 let mut inner = self.inner.lock();
849
850 match (window_handle, display_handle) {
851 (Rwh::Xlib(_), _) => {}
852 (Rwh::Xcb(_), _) => {}
853 (Rwh::Win32(_), _) => {}
854 (Rwh::AppKit(_), _) => {}
855 #[cfg(target_os = "android")]
856 (Rwh::AndroidNdk(handle), _) => {
857 let format = inner
858 .egl
859 .instance
860 .get_config_attrib(
861 inner.egl.display,
862 inner.config,
863 khronos_egl::NATIVE_VISUAL_ID,
864 )
865 .unwrap();
866
867 let ret = unsafe {
868 ANativeWindow_setBuffersGeometry(handle.a_native_window, 0, 0, format)
869 };
870
871 if ret != 0 {
872 return Err(crate::InstanceError::new(format!(
873 "error {ret} returned from ANativeWindow_setBuffersGeometry",
874 )));
875 }
876 }
877 #[cfg(not(target_os = "emscripten"))]
878 (Rwh::Wayland(_), raw_window_handle::RawDisplayHandle::Wayland(display_handle)) => {
879 if inner
880 .wl_display
881 .map(|ptr| ptr != display_handle.display)
882 .unwrap_or(true)
883 {
884 log::warn!("Re-initializing Gles context due to Wayland window");
891
892 use std::ops::DerefMut;
893 let display_attributes = [khronos_egl::ATTRIB_NONE];
894
895 let display = unsafe {
896 inner
897 .egl
898 .instance
899 .upcast::<khronos_egl::EGL1_5>()
900 .unwrap()
901 .get_platform_display(
902 EGL_PLATFORM_WAYLAND_KHR,
903 display_handle.display,
904 &display_attributes,
905 )
906 }
907 .unwrap();
908
909 let new_inner = Inner::create(
910 self.flags,
911 Arc::clone(&inner.egl.instance),
912 display,
913 inner.force_gles_minor_version,
914 )?;
915
916 let old_inner = std::mem::replace(inner.deref_mut(), new_inner);
917 inner.wl_display = Some(display_handle.display);
918
919 drop(old_inner);
920 }
921 }
922 #[cfg(target_os = "emscripten")]
923 (Rwh::Web(_), _) => {}
924 other => {
925 return Err(crate::InstanceError::new(format!(
926 "unsupported window: {other:?}"
927 )));
928 }
929 };
930
931 inner.egl.unmake_current();
932
933 Ok(Surface {
934 egl: inner.egl.clone(),
935 wsi: self.wsi.clone(),
936 config: inner.config,
937 presentable: inner.supports_native_window,
938 raw_window_handle: window_handle,
939 swapchain: None,
940 srgb_kind: inner.srgb_kind,
941 })
942 }
943 unsafe fn destroy_surface(&self, _surface: Surface) {}
944
945 unsafe fn enumerate_adapters(&self) -> Vec<crate::ExposedAdapter<super::Api>> {
946 let inner = self.inner.lock();
947 inner.egl.make_current();
948
949 let mut gl = unsafe {
950 glow::Context::from_loader_function(|name| {
951 inner
952 .egl
953 .instance
954 .get_proc_address(name)
955 .map_or(ptr::null(), |p| p as *const _)
956 })
957 };
958
959 if self.flags.contains(wgt::InstanceFlags::DEBUG) && gl.supports_debug() {
960 log::info!("Max label length: {}", unsafe {
961 gl.get_parameter_i32(glow::MAX_LABEL_LENGTH)
962 });
963 }
964
965 if self.flags.contains(wgt::InstanceFlags::VALIDATION) && gl.supports_debug() {
966 log::info!("Enabling GLES debug output");
967 unsafe { gl.enable(glow::DEBUG_OUTPUT) };
968 unsafe { gl.debug_message_callback(super::gl_debug_message_callback) };
969 }
970
971 inner.egl.unmake_current();
972
973 unsafe {
974 super::Adapter::expose(AdapterContext {
975 glow: Mutex::new(gl),
976 egl: Some(inner.egl.clone()),
977 })
978 }
979 .into_iter()
980 .collect()
981 }
982}
983
984impl super::Adapter {
985 pub unsafe fn new_external(
993 fun: impl FnMut(&str) -> *const ffi::c_void,
994 ) -> Option<crate::ExposedAdapter<super::Api>> {
995 let context = unsafe { glow::Context::from_loader_function(fun) };
996 unsafe {
997 Self::expose(AdapterContext {
998 glow: Mutex::new(context),
999 egl: None,
1000 })
1001 }
1002 }
1003
1004 pub fn adapter_context(&self) -> &AdapterContext {
1005 &self.shared.context
1006 }
1007}
1008
1009impl super::Device {
1010 pub fn context(&self) -> &AdapterContext {
1012 &self.shared.context
1013 }
1014}
1015
1016#[derive(Debug)]
1017pub struct Swapchain {
1018 surface: khronos_egl::Surface,
1019 wl_window: Option<*mut raw::c_void>,
1020 framebuffer: glow::Framebuffer,
1021 renderbuffer: glow::Renderbuffer,
1022 extent: wgt::Extent3d,
1024 format: wgt::TextureFormat,
1025 format_desc: super::TextureFormatDesc,
1026 #[allow(unused)]
1027 sample_type: wgt::TextureSampleType,
1028}
1029
1030#[derive(Debug)]
1031pub struct Surface {
1032 egl: EglContext,
1033 wsi: WindowSystemInterface,
1034 config: khronos_egl::Config,
1035 pub(super) presentable: bool,
1036 raw_window_handle: raw_window_handle::RawWindowHandle,
1037 swapchain: Option<Swapchain>,
1038 srgb_kind: SrgbFrameBufferKind,
1039}
1040
1041unsafe impl Send for Surface {}
1042unsafe impl Sync for Surface {}
1043
1044impl Surface {
1045 pub(super) unsafe fn present(
1046 &mut self,
1047 _suf_texture: super::Texture,
1048 context: &AdapterContext,
1049 ) -> Result<(), crate::SurfaceError> {
1050 let gl = unsafe { context.get_without_egl_lock() };
1051 let sc = self.swapchain.as_ref().unwrap();
1052
1053 self.egl
1054 .instance
1055 .make_current(
1056 self.egl.display,
1057 Some(sc.surface),
1058 Some(sc.surface),
1059 Some(self.egl.raw),
1060 )
1061 .map_err(|e| {
1062 log::error!("make_current(surface) failed: {}", e);
1063 crate::SurfaceError::Lost
1064 })?;
1065
1066 unsafe { gl.disable(glow::SCISSOR_TEST) };
1067 unsafe { gl.color_mask(true, true, true, true) };
1068
1069 unsafe { gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None) };
1070 unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(sc.framebuffer)) };
1071 unsafe {
1075 gl.blit_framebuffer(
1076 0,
1077 sc.extent.height as i32,
1078 sc.extent.width as i32,
1079 0,
1080 0,
1081 0,
1082 sc.extent.width as i32,
1083 sc.extent.height as i32,
1084 glow::COLOR_BUFFER_BIT,
1085 glow::NEAREST,
1086 )
1087 };
1088 unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
1089
1090 self.egl
1091 .instance
1092 .swap_buffers(self.egl.display, sc.surface)
1093 .map_err(|e| {
1094 log::error!("swap_buffers failed: {}", e);
1095 crate::SurfaceError::Lost
1096 })?;
1097 self.egl
1098 .instance
1099 .make_current(self.egl.display, None, None, None)
1100 .map_err(|e| {
1101 log::error!("make_current(null) failed: {}", e);
1102 crate::SurfaceError::Lost
1103 })?;
1104
1105 Ok(())
1106 }
1107
1108 unsafe fn unconfigure_impl(
1109 &mut self,
1110 device: &super::Device,
1111 ) -> Option<(khronos_egl::Surface, Option<*mut raw::c_void>)> {
1112 let gl = &device.shared.context.lock();
1113 match self.swapchain.take() {
1114 Some(sc) => {
1115 unsafe { gl.delete_renderbuffer(sc.renderbuffer) };
1116 unsafe { gl.delete_framebuffer(sc.framebuffer) };
1117 Some((sc.surface, sc.wl_window))
1118 }
1119 None => None,
1120 }
1121 }
1122
1123 pub fn supports_srgb(&self) -> bool {
1124 match self.srgb_kind {
1125 SrgbFrameBufferKind::None => false,
1126 _ => true,
1127 }
1128 }
1129}
1130
1131impl crate::Surface<super::Api> for Surface {
1132 unsafe fn configure(
1133 &mut self,
1134 device: &super::Device,
1135 config: &crate::SurfaceConfiguration,
1136 ) -> Result<(), crate::SurfaceError> {
1137 use raw_window_handle::RawWindowHandle as Rwh;
1138
1139 let (surface, wl_window) = match unsafe { self.unconfigure_impl(device) } {
1140 Some(pair) => pair,
1141 None => {
1142 let mut wl_window = None;
1143 let (mut temp_xlib_handle, mut temp_xcb_handle);
1144 #[allow(trivial_casts)]
1145 let native_window_ptr = match (self.wsi.kind, self.raw_window_handle) {
1146 (WindowKind::Unknown | WindowKind::X11, Rwh::Xlib(handle)) => {
1147 temp_xlib_handle = handle.window;
1148 &mut temp_xlib_handle as *mut _ as *mut std::ffi::c_void
1149 }
1150 (WindowKind::AngleX11, Rwh::Xlib(handle)) => {
1151 handle.window as *mut std::ffi::c_void
1152 }
1153 (WindowKind::Unknown | WindowKind::X11, Rwh::Xcb(handle)) => {
1154 temp_xcb_handle = handle.window;
1155 &mut temp_xcb_handle as *mut _ as *mut std::ffi::c_void
1156 }
1157 (WindowKind::AngleX11, Rwh::Xcb(handle)) => {
1158 handle.window as *mut std::ffi::c_void
1159 }
1160 (WindowKind::Unknown, Rwh::AndroidNdk(handle)) => handle.a_native_window,
1161 (WindowKind::Wayland, Rwh::Wayland(handle)) => {
1162 let library = &self.wsi.display_owner.as_ref().unwrap().library;
1163 let wl_egl_window_create: libloading::Symbol<WlEglWindowCreateFun> =
1164 unsafe { library.get(b"wl_egl_window_create") }.unwrap();
1165 let window = unsafe { wl_egl_window_create(handle.surface, 640, 480) };
1166 wl_window = Some(window);
1167 window
1168 }
1169 #[cfg(target_os = "emscripten")]
1170 (WindowKind::Unknown, Rwh::Web(handle)) => handle.id as *mut std::ffi::c_void,
1171 (WindowKind::Unknown, Rwh::Win32(handle)) => handle.hwnd,
1172 (WindowKind::Unknown, Rwh::AppKit(handle)) => {
1173 #[cfg(not(target_os = "macos"))]
1174 let window_ptr = handle.ns_view;
1175 #[cfg(target_os = "macos")]
1176 let window_ptr = {
1177 use objc::{msg_send, runtime::Object, sel, sel_impl};
1178 let layer: *mut Object =
1180 msg_send![handle.ns_view as *mut Object, layer];
1181 layer as *mut ffi::c_void
1182 };
1183 window_ptr
1184 }
1185 _ => {
1186 log::warn!(
1187 "Initialized platform {:?} doesn't work with window {:?}",
1188 self.wsi.kind,
1189 self.raw_window_handle
1190 );
1191 return Err(crate::SurfaceError::Other("incompatible window kind"));
1192 }
1193 };
1194
1195 let mut attributes = vec![
1196 khronos_egl::RENDER_BUFFER,
1197 if cfg!(any(target_os = "android", target_os = "macos"))
1201 || cfg!(windows)
1202 || self.wsi.kind == WindowKind::AngleX11
1203 {
1204 khronos_egl::BACK_BUFFER
1205 } else {
1206 khronos_egl::SINGLE_BUFFER
1207 },
1208 ];
1209 if config.format.is_srgb() {
1210 match self.srgb_kind {
1211 SrgbFrameBufferKind::None => {}
1212 SrgbFrameBufferKind::Core => {
1213 attributes.push(khronos_egl::GL_COLORSPACE);
1214 attributes.push(khronos_egl::GL_COLORSPACE_SRGB);
1215 }
1216 SrgbFrameBufferKind::Khr => {
1217 attributes.push(EGL_GL_COLORSPACE_KHR as i32);
1218 attributes.push(EGL_GL_COLORSPACE_SRGB_KHR as i32);
1219 }
1220 }
1221 }
1222 attributes.push(khronos_egl::ATTRIB_NONE as i32);
1223
1224 #[cfg(not(target_os = "emscripten"))]
1225 let egl1_5 = self.egl.instance.upcast::<khronos_egl::EGL1_5>();
1226
1227 #[cfg(target_os = "emscripten")]
1228 let egl1_5: Option<&Arc<EglInstance>> = Some(&self.egl.instance);
1229
1230 let raw_result = match egl1_5 {
1232 Some(egl) if self.wsi.kind != WindowKind::Unknown => {
1233 let attributes_usize = attributes
1234 .into_iter()
1235 .map(|v| v as usize)
1236 .collect::<Vec<_>>();
1237 unsafe {
1238 egl.create_platform_window_surface(
1239 self.egl.display,
1240 self.config,
1241 native_window_ptr,
1242 &attributes_usize,
1243 )
1244 }
1245 }
1246 _ => unsafe {
1247 self.egl.instance.create_window_surface(
1248 self.egl.display,
1249 self.config,
1250 native_window_ptr,
1251 Some(&attributes),
1252 )
1253 },
1254 };
1255
1256 match raw_result {
1257 Ok(raw) => (raw, wl_window),
1258 Err(e) => {
1259 log::warn!("Error in create_window_surface: {:?}", e);
1260 return Err(crate::SurfaceError::Lost);
1261 }
1262 }
1263 }
1264 };
1265
1266 if let Some(window) = wl_window {
1267 let library = &self.wsi.display_owner.as_ref().unwrap().library;
1268 let wl_egl_window_resize: libloading::Symbol<WlEglWindowResizeFun> =
1269 unsafe { library.get(b"wl_egl_window_resize") }.unwrap();
1270 unsafe {
1271 wl_egl_window_resize(
1272 window,
1273 config.extent.width as i32,
1274 config.extent.height as i32,
1275 0,
1276 0,
1277 )
1278 };
1279 }
1280
1281 let format_desc = device.shared.describe_texture_format(config.format);
1282 let gl = &device.shared.context.lock();
1283 let renderbuffer = unsafe { gl.create_renderbuffer() }.map_err(|error| {
1284 log::error!("Internal swapchain renderbuffer creation failed: {error}");
1285 crate::DeviceError::OutOfMemory
1286 })?;
1287 unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, Some(renderbuffer)) };
1288 unsafe {
1289 gl.renderbuffer_storage(
1290 glow::RENDERBUFFER,
1291 format_desc.internal,
1292 config.extent.width as _,
1293 config.extent.height as _,
1294 )
1295 };
1296 let framebuffer = unsafe { gl.create_framebuffer() }.map_err(|error| {
1297 log::error!("Internal swapchain framebuffer creation failed: {error}");
1298 crate::DeviceError::OutOfMemory
1299 })?;
1300 unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(framebuffer)) };
1301 unsafe {
1302 gl.framebuffer_renderbuffer(
1303 glow::READ_FRAMEBUFFER,
1304 glow::COLOR_ATTACHMENT0,
1305 glow::RENDERBUFFER,
1306 Some(renderbuffer),
1307 )
1308 };
1309 unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, None) };
1310 unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
1311
1312 self.swapchain = Some(Swapchain {
1313 surface,
1314 wl_window,
1315 renderbuffer,
1316 framebuffer,
1317 extent: config.extent,
1318 format: config.format,
1319 format_desc,
1320 sample_type: wgt::TextureSampleType::Float { filterable: false },
1321 });
1322
1323 Ok(())
1324 }
1325
1326 unsafe fn unconfigure(&mut self, device: &super::Device) {
1327 if let Some((surface, wl_window)) = unsafe { self.unconfigure_impl(device) } {
1328 self.egl
1329 .instance
1330 .destroy_surface(self.egl.display, surface)
1331 .unwrap();
1332 if let Some(window) = wl_window {
1333 let library = &self
1334 .wsi
1335 .display_owner
1336 .as_ref()
1337 .expect("unsupported window")
1338 .library;
1339 let wl_egl_window_destroy: libloading::Symbol<WlEglWindowDestroyFun> =
1340 unsafe { library.get(b"wl_egl_window_destroy") }.unwrap();
1341 unsafe { wl_egl_window_destroy(window) };
1342 }
1343 }
1344 }
1345
1346 unsafe fn acquire_texture(
1347 &mut self,
1348 _timeout_ms: Option<Duration>, ) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
1350 let sc = self.swapchain.as_ref().unwrap();
1351 let texture = super::Texture {
1352 inner: super::TextureInner::Renderbuffer {
1353 raw: sc.renderbuffer,
1354 },
1355 drop_guard: None,
1356 array_layer_count: 1,
1357 mip_level_count: 1,
1358 format: sc.format,
1359 format_desc: sc.format_desc.clone(),
1360 copy_size: crate::CopyExtent {
1361 width: sc.extent.width,
1362 height: sc.extent.height,
1363 depth: 1,
1364 },
1365 };
1366 Ok(Some(crate::AcquiredSurfaceTexture {
1367 texture,
1368 suboptimal: false,
1369 }))
1370 }
1371 unsafe fn discard_texture(&mut self, _texture: super::Texture) {}
1372}