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