1use alloc::{rc::Rc, string::String, sync::Arc, vec::Vec};
2use core::{ffi, mem::ManuallyDrop, ptr, time::Duration};
3use std::sync::LazyLock;
4
5use glow::HasContext;
6use hashbrown::HashMap;
7use parking_lot::{MappedMutexGuard, Mutex, MutexGuard, RwLock};
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 ffi::c_char) -> *mut ffi::c_void;
26
27type XCloseDisplayFun = unsafe extern "system" fn(display: *mut ffi::c_void) -> ffi::c_int;
28
29type WlDisplayConnectFun =
30 unsafe extern "system" fn(display_name: *const ffi::c_char) -> *mut ffi::c_void;
31
32type WlDisplayDisconnectFun = unsafe extern "system" fn(display: *const ffi::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 ffi::c_void,
42 width: ffi::c_int,
43 height: ffi::c_int,
44) -> *mut ffi::c_void;
45
46type WlEglWindowResizeFun = unsafe extern "system" fn(
47 window: *const ffi::c_void,
48 width: ffi::c_int,
49 height: ffi::c_int,
50 dx: ffi::c_int,
51 dy: ffi::c_int,
52);
53
54type WlEglWindowDestroyFun = unsafe extern "system" fn(window: *const ffi::c_void);
55
56type EglLabel = *const ffi::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 ffi::c_char,
63 message_type: u32,
64 thread_label: EglLabel,
65 object_label: EglLabel,
66 message: *const ffi::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) -> ffi::c_int;
79
80unsafe extern "system" fn egl_debug_proc(
81 error: khronos_egl::Enum,
82 command_raw: *const ffi::c_char,
83 message_type: u32,
84 _thread_label: EglLabel,
85 _object_label: EglLabel,
86 message_raw: *const ffi::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!(log_severity, "EGL '{command}' code 0x{error:x}: {message}",);
102}
103
104#[derive(Debug)]
109enum DisplayRef {
110 X11(ptr::NonNull<ffi::c_void>),
111 Wayland,
112}
113
114impl DisplayRef {
115 fn as_ptr(&self) -> *mut ffi::c_void {
117 match *self {
118 Self::X11(ptr) => ptr.as_ptr(),
119 Self::Wayland => unreachable!(),
120 }
121 }
122}
123
124#[derive(Debug)]
130struct DisplayOwner {
131 library: libloading::Library,
132 display: DisplayRef,
133}
134
135impl Drop for DisplayOwner {
136 fn drop(&mut self) {
137 match self.display {
138 DisplayRef::X11(ptr) => unsafe {
139 let func: libloading::Symbol<XCloseDisplayFun> =
140 self.library.get(c"XCloseDisplay".to_bytes()).unwrap();
141 func(ptr.as_ptr());
142 },
143 DisplayRef::Wayland => {}
144 }
145 }
146}
147
148fn open_x_display() -> Option<DisplayOwner> {
149 log::debug!("Loading X11 library to get the current display");
150 unsafe {
151 let library = find_library(&["libX11.so.6", "libX11.so"])?;
152 let func: libloading::Symbol<XOpenDisplayFun> =
153 library.get(c"XOpenDisplay".to_bytes()).unwrap();
154 let result = func(ptr::null());
155 ptr::NonNull::new(result).map(|ptr| DisplayOwner {
156 display: DisplayRef::X11(ptr),
157 library,
158 })
159 }
160}
161
162unsafe fn find_library(paths: &[&str]) -> Option<libloading::Library> {
163 for path in paths {
164 match unsafe { libloading::Library::new(path) } {
165 Ok(lib) => return Some(lib),
166 _ => continue,
167 };
168 }
169 None
170}
171
172fn test_wayland_display() -> Option<DisplayOwner> {
173 log::debug!("Loading Wayland library to get the current display");
177 let library = unsafe {
178 let client_library = find_library(&["libwayland-client.so.0", "libwayland-client.so"])?;
179 let wl_display_connect: libloading::Symbol<WlDisplayConnectFun> = client_library
180 .get(c"wl_display_connect".to_bytes())
181 .unwrap();
182 let wl_display_disconnect: libloading::Symbol<WlDisplayDisconnectFun> = client_library
183 .get(c"wl_display_disconnect".to_bytes())
184 .unwrap();
185 let display = ptr::NonNull::new(wl_display_connect(ptr::null()))?;
186 wl_display_disconnect(display.as_ptr());
187 find_library(&["libwayland-egl.so.1", "libwayland-egl.so"])?
188 };
189 Some(DisplayOwner {
190 library,
191 display: DisplayRef::Wayland,
192 })
193}
194
195#[derive(Clone, Copy, Debug)]
196enum SrgbFrameBufferKind {
197 None,
199 Core,
201 Khr,
203}
204
205fn choose_config(
207 egl: &EglInstance,
208 display: khronos_egl::Display,
209 srgb_kind: SrgbFrameBufferKind,
210) -> Result<(khronos_egl::Config, bool), crate::InstanceError> {
211 let tiers = [
213 (
214 "off-screen",
215 &[
216 khronos_egl::SURFACE_TYPE,
217 khronos_egl::PBUFFER_BIT,
218 khronos_egl::RENDERABLE_TYPE,
219 khronos_egl::OPENGL_ES2_BIT,
220 ][..],
221 ),
222 (
223 "presentation",
224 &[khronos_egl::SURFACE_TYPE, khronos_egl::WINDOW_BIT][..],
225 ),
226 #[cfg(not(target_os = "android"))]
227 (
228 "native-render",
229 &[khronos_egl::NATIVE_RENDERABLE, khronos_egl::TRUE as _][..],
230 ),
231 ];
232
233 let mut attributes = Vec::with_capacity(9);
234 for tier_max in (0..tiers.len()).rev() {
235 let name = tiers[tier_max].0;
236 log::debug!("\tTrying {name}");
237
238 attributes.clear();
239 for &(_, tier_attr) in tiers[..=tier_max].iter() {
240 attributes.extend_from_slice(tier_attr);
241 }
242 match srgb_kind {
244 SrgbFrameBufferKind::None => {}
245 _ => {
246 attributes.push(khronos_egl::ALPHA_SIZE);
247 attributes.push(8);
248 }
249 }
250 attributes.push(khronos_egl::NONE);
251
252 match egl.choose_first_config(display, &attributes) {
253 Ok(Some(config)) => {
254 if tier_max == 1 {
255 log::warn!("EGL says it can present to the window but not natively",);
258 }
259 let tier_threshold =
261 if cfg!(target_os = "android") || cfg!(windows) || cfg!(target_env = "ohos") {
262 1
263 } else {
264 2
265 };
266 return Ok((config, tier_max >= tier_threshold));
267 }
268 Ok(None) => {
269 log::warn!("No config found!");
270 }
271 Err(e) => {
272 log::error!("error in choose_first_config: {e:?}");
273 }
274 }
275 }
276
277 Err(crate::InstanceError::new(String::from(
279 "unable to find an acceptable EGL framebuffer configuration",
280 )))
281}
282
283#[derive(Clone, Debug)]
284struct EglContext {
285 instance: Arc<EglInstance>,
286 version: (i32, i32),
287 display: khronos_egl::Display,
288 raw: khronos_egl::Context,
289 pbuffer: Option<khronos_egl::Surface>,
290}
291
292impl EglContext {
293 fn make_current(&self) {
294 self.instance
295 .make_current(self.display, self.pbuffer, self.pbuffer, Some(self.raw))
296 .unwrap();
297 }
298
299 fn unmake_current(&self) {
300 self.instance
301 .make_current(self.display, None, None, None)
302 .unwrap();
303 }
304}
305
306pub struct AdapterContext {
309 glow: Mutex<ManuallyDrop<glow::Context>>,
310 egl: Option<EglContext>,
311}
312
313unsafe impl Sync for AdapterContext {}
314unsafe impl Send for AdapterContext {}
315
316impl AdapterContext {
317 pub fn is_owned(&self) -> bool {
318 self.egl.is_some()
319 }
320
321 pub fn egl_instance(&self) -> Option<&EglInstance> {
325 self.egl.as_ref().map(|egl| &*egl.instance)
326 }
327
328 pub fn raw_display(&self) -> Option<&khronos_egl::Display> {
332 self.egl.as_ref().map(|egl| &egl.display)
333 }
334
335 pub fn egl_version(&self) -> Option<(i32, i32)> {
339 self.egl.as_ref().map(|egl| egl.version)
340 }
341
342 pub fn raw_context(&self) -> *mut ffi::c_void {
343 match self.egl {
344 Some(ref egl) => egl.raw.as_ptr(),
345 None => ptr::null_mut(),
346 }
347 }
348}
349
350impl Drop for AdapterContext {
351 fn drop(&mut self) {
352 struct CurrentGuard<'a>(&'a EglContext);
353 impl Drop for CurrentGuard<'_> {
354 fn drop(&mut self) {
355 self.0.unmake_current();
356 }
357 }
358
359 let _guard = self.egl.as_ref().map(|egl| {
366 egl.make_current();
367 CurrentGuard(egl)
368 });
369 let glow = self.glow.get_mut();
370 unsafe { ManuallyDrop::drop(glow) };
372 }
373}
374
375struct EglContextLock<'a> {
376 instance: &'a Arc<EglInstance>,
377 display: khronos_egl::Display,
378}
379
380pub struct AdapterContextLock<'a> {
382 glow: MutexGuard<'a, ManuallyDrop<glow::Context>>,
383 egl: Option<EglContextLock<'a>>,
384}
385
386impl<'a> core::ops::Deref for AdapterContextLock<'a> {
387 type Target = glow::Context;
388
389 fn deref(&self) -> &Self::Target {
390 &self.glow
391 }
392}
393
394impl<'a> Drop for AdapterContextLock<'a> {
395 fn drop(&mut self) {
396 if let Some(egl) = self.egl.take() {
397 egl.instance
398 .make_current(egl.display, None, None, None)
399 .unwrap();
400 }
401 }
402}
403
404impl AdapterContext {
405 pub unsafe fn get_without_egl_lock(&self) -> MappedMutexGuard<glow::Context> {
417 let guard = self
418 .glow
419 .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
420 .expect("Could not lock adapter context. This is most-likely a deadlock.");
421 MutexGuard::map(guard, |glow| &mut **glow)
422 }
423
424 #[track_caller]
427 pub fn lock<'a>(&'a self) -> AdapterContextLock<'a> {
428 let glow = self
429 .glow
430 .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
433 .expect("Could not lock adapter context. This is most-likely a deadlock.");
434
435 let egl = self.egl.as_ref().map(|egl| {
436 egl.make_current();
437 EglContextLock {
438 instance: &egl.instance,
439 display: egl.display,
440 }
441 });
442
443 AdapterContextLock { glow, egl }
444 }
445}
446
447#[derive(Debug)]
448struct Inner {
449 egl: EglContext,
452 #[allow(unused)]
453 version: (i32, i32),
454 supports_native_window: bool,
455 config: khronos_egl::Config,
456 #[cfg_attr(Emscripten, allow(dead_code))]
457 wl_display: Option<*mut ffi::c_void>,
458 #[cfg_attr(Emscripten, allow(dead_code))]
459 force_gles_minor_version: wgt::Gles3MinorVersion,
460 srgb_kind: SrgbFrameBufferKind,
462}
463
464static DISPLAYS_REFERENCE_COUNT: LazyLock<Mutex<HashMap<usize, usize>>> =
468 LazyLock::new(Default::default);
469
470fn initialize_display(
471 egl: &EglInstance,
472 display: khronos_egl::Display,
473) -> Result<(i32, i32), khronos_egl::Error> {
474 let mut guard = DISPLAYS_REFERENCE_COUNT.lock();
475 *guard.entry(display.as_ptr() as usize).or_default() += 1;
476
477 egl.initialize(display)
481}
482
483fn terminate_display(
484 egl: &EglInstance,
485 display: khronos_egl::Display,
486) -> Result<(), khronos_egl::Error> {
487 let key = &(display.as_ptr() as usize);
488 let mut guard = DISPLAYS_REFERENCE_COUNT.lock();
489 let count_ref = guard
490 .get_mut(key)
491 .expect("Attempted to decref a display before incref was called");
492
493 if *count_ref > 1 {
494 *count_ref -= 1;
495
496 Ok(())
497 } else {
498 guard.remove(key);
499
500 egl.terminate(display)
501 }
502}
503
504impl Inner {
505 fn create(
506 flags: wgt::InstanceFlags,
507 egl: Arc<EglInstance>,
508 display: khronos_egl::Display,
509 force_gles_minor_version: wgt::Gles3MinorVersion,
510 ) -> Result<Self, crate::InstanceError> {
511 let version = initialize_display(&egl, display).map_err(|e| {
512 crate::InstanceError::with_source(
513 String::from("failed to initialize EGL display connection"),
514 e,
515 )
516 })?;
517 let vendor = egl
518 .query_string(Some(display), khronos_egl::VENDOR)
519 .unwrap();
520 let display_extensions = egl
521 .query_string(Some(display), khronos_egl::EXTENSIONS)
522 .unwrap()
523 .to_string_lossy();
524 log::debug!("Display vendor {vendor:?}, version {version:?}",);
525 log::debug!(
526 "Display extensions: {:#?}",
527 display_extensions.split_whitespace().collect::<Vec<_>>()
528 );
529
530 let srgb_kind = if version >= (1, 5) {
531 log::debug!("\tEGL surface: +srgb");
532 SrgbFrameBufferKind::Core
533 } else if display_extensions.contains("EGL_KHR_gl_colorspace") {
534 log::debug!("\tEGL surface: +srgb khr");
535 SrgbFrameBufferKind::Khr
536 } else {
537 log::warn!("\tEGL surface: -srgb");
538 SrgbFrameBufferKind::None
539 };
540
541 if log::max_level() >= log::LevelFilter::Trace {
542 log::trace!("Configurations:");
543 let config_count = egl.get_config_count(display).unwrap();
544 let mut configurations = Vec::with_capacity(config_count);
545 egl.get_configs(display, &mut configurations).unwrap();
546 for &config in configurations.iter() {
547 log::trace!("\tCONFORMANT=0x{:X}, RENDERABLE=0x{:X}, NATIVE_RENDERABLE=0x{:X}, SURFACE_TYPE=0x{:X}, ALPHA_SIZE={}",
548 egl.get_config_attrib(display, config, khronos_egl::CONFORMANT).unwrap(),
549 egl.get_config_attrib(display, config, khronos_egl::RENDERABLE_TYPE).unwrap(),
550 egl.get_config_attrib(display, config, khronos_egl::NATIVE_RENDERABLE).unwrap(),
551 egl.get_config_attrib(display, config, khronos_egl::SURFACE_TYPE).unwrap(),
552 egl.get_config_attrib(display, config, khronos_egl::ALPHA_SIZE).unwrap(),
553 );
554 }
555 }
556
557 let (config, supports_native_window) = choose_config(&egl, display, srgb_kind)?;
558
559 let supports_opengl = if version >= (1, 4) {
560 let client_apis = egl
561 .query_string(Some(display), khronos_egl::CLIENT_APIS)
562 .unwrap()
563 .to_string_lossy();
564 client_apis
565 .split(' ')
566 .any(|client_api| client_api == "OpenGL")
567 } else {
568 false
569 };
570 egl.bind_api(if supports_opengl {
571 khronos_egl::OPENGL_API
572 } else {
573 khronos_egl::OPENGL_ES_API
574 })
575 .unwrap();
576
577 let needs_robustness = true;
578 let mut khr_context_flags = 0;
579 let supports_khr_context = display_extensions.contains("EGL_KHR_create_context");
580
581 let mut context_attributes = vec![];
582 let mut gl_context_attributes = vec![];
583 let mut gles_context_attributes = vec![];
584 gl_context_attributes.push(khronos_egl::CONTEXT_MAJOR_VERSION);
585 gl_context_attributes.push(3);
586 gl_context_attributes.push(khronos_egl::CONTEXT_MINOR_VERSION);
587 gl_context_attributes.push(3);
588 if supports_opengl && force_gles_minor_version != wgt::Gles3MinorVersion::Automatic {
589 log::warn!("Ignoring specified GLES minor version as OpenGL is used");
590 }
591 gles_context_attributes.push(khronos_egl::CONTEXT_MAJOR_VERSION);
592 gles_context_attributes.push(3); if force_gles_minor_version != wgt::Gles3MinorVersion::Automatic {
594 gles_context_attributes.push(khronos_egl::CONTEXT_MINOR_VERSION);
595 gles_context_attributes.push(match force_gles_minor_version {
596 wgt::Gles3MinorVersion::Automatic => unreachable!(),
597 wgt::Gles3MinorVersion::Version0 => 0,
598 wgt::Gles3MinorVersion::Version1 => 1,
599 wgt::Gles3MinorVersion::Version2 => 2,
600 });
601 }
602 if flags.contains(wgt::InstanceFlags::DEBUG) {
603 if version >= (1, 5) {
604 log::debug!("\tEGL context: +debug");
605 context_attributes.push(khronos_egl::CONTEXT_OPENGL_DEBUG);
606 context_attributes.push(khronos_egl::TRUE as _);
607 } else if supports_khr_context {
608 log::debug!("\tEGL context: +debug KHR");
609 khr_context_flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
610 } else {
611 log::debug!("\tEGL context: -debug");
612 }
613 }
614 if needs_robustness {
615 if version >= (1, 5) && !display_extensions.contains("EGL_ANGLE_") {
619 log::debug!("\tEGL context: +robust access");
620 context_attributes.push(khronos_egl::CONTEXT_OPENGL_ROBUST_ACCESS);
621 context_attributes.push(khronos_egl::TRUE as _);
622 } else if display_extensions.contains("EGL_EXT_create_context_robustness") {
623 log::debug!("\tEGL context: +robust access EXT");
624 context_attributes.push(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT);
625 context_attributes.push(khronos_egl::TRUE as _);
626 } else {
627 log::warn!("\tEGL context: -robust access");
630 }
631 }
632 if khr_context_flags != 0 {
633 context_attributes.push(EGL_CONTEXT_FLAGS_KHR);
634 context_attributes.push(khr_context_flags);
635 }
636 context_attributes.push(khronos_egl::NONE);
637
638 gl_context_attributes.extend(&context_attributes);
639 gles_context_attributes.extend(&context_attributes);
640
641 let context = if supports_opengl {
642 egl.create_context(display, config, None, &gl_context_attributes)
643 .or_else(|_| {
644 egl.bind_api(khronos_egl::OPENGL_ES_API).unwrap();
645 egl.create_context(display, config, None, &gles_context_attributes)
646 })
647 .map_err(|e| {
648 crate::InstanceError::with_source(
649 String::from("unable to create OpenGL or GLES 3.x context"),
650 e,
651 )
652 })
653 } else {
654 egl.create_context(display, config, None, &gles_context_attributes)
655 .map_err(|e| {
656 crate::InstanceError::with_source(
657 String::from("unable to create GLES 3.x context"),
658 e,
659 )
660 })
661 }?;
662
663 let pbuffer = if version >= (1, 5)
666 || display_extensions.contains("EGL_KHR_surfaceless_context")
667 || cfg!(Emscripten)
668 {
669 log::debug!("\tEGL context: +surfaceless");
670 None
671 } else {
672 let attributes = [
673 khronos_egl::WIDTH,
674 1,
675 khronos_egl::HEIGHT,
676 1,
677 khronos_egl::NONE,
678 ];
679 egl.create_pbuffer_surface(display, config, &attributes)
680 .map(Some)
681 .map_err(|e| {
682 crate::InstanceError::with_source(
683 String::from("error in create_pbuffer_surface"),
684 e,
685 )
686 })?
687 };
688
689 Ok(Self {
690 egl: EglContext {
691 instance: egl,
692 display,
693 raw: context,
694 pbuffer,
695 version,
696 },
697 version,
698 supports_native_window,
699 config,
700 wl_display: None,
701 srgb_kind,
702 force_gles_minor_version,
703 })
704 }
705}
706
707impl Drop for Inner {
708 fn drop(&mut self) {
709 if let Err(e) = self
710 .egl
711 .instance
712 .destroy_context(self.egl.display, self.egl.raw)
713 {
714 log::warn!("Error in destroy_context: {e:?}");
715 }
716
717 if let Err(e) = terminate_display(&self.egl.instance, self.egl.display) {
718 log::warn!("Error in terminate: {e:?}");
719 }
720 }
721}
722
723#[derive(Clone, Copy, Debug, PartialEq)]
724enum WindowKind {
725 Wayland,
726 X11,
727 AngleX11,
728 Unknown,
729}
730
731#[derive(Clone, Debug)]
732struct WindowSystemInterface {
733 display_owner: Option<Rc<DisplayOwner>>,
734 kind: WindowKind,
735}
736
737pub struct Instance {
738 wsi: WindowSystemInterface,
739 flags: wgt::InstanceFlags,
740 options: wgt::GlBackendOptions,
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 { core::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(
926 desc.flags,
927 egl,
928 display,
929 desc.backend_options.gl.gles_minor_version,
930 )?;
931
932 Ok(Instance {
933 wsi: WindowSystemInterface {
934 display_owner,
935 kind: wsi_kind,
936 },
937 flags: desc.flags,
938 options: desc.backend_options.gl.clone(),
939 inner: Mutex::new(inner),
940 })
941 }
942
943 #[cfg_attr(target_os = "macos", allow(unused, unused_mut, unreachable_code))]
944 unsafe fn create_surface(
945 &self,
946 display_handle: raw_window_handle::RawDisplayHandle,
947 window_handle: raw_window_handle::RawWindowHandle,
948 ) -> Result<Surface, crate::InstanceError> {
949 use raw_window_handle::RawWindowHandle as Rwh;
950
951 #[cfg_attr(any(target_os = "android", Emscripten), allow(unused_mut))]
952 let mut inner = self.inner.lock();
953
954 match (window_handle, display_handle) {
955 (Rwh::Xlib(_), _) => {}
956 (Rwh::Xcb(_), _) => {}
957 (Rwh::Win32(_), _) => {}
958 (Rwh::AppKit(_), _) => {}
959 (Rwh::OhosNdk(_), _) => {}
960 #[cfg(target_os = "android")]
961 (Rwh::AndroidNdk(handle), _) => {
962 let format = inner
963 .egl
964 .instance
965 .get_config_attrib(
966 inner.egl.display,
967 inner.config,
968 khronos_egl::NATIVE_VISUAL_ID,
969 )
970 .unwrap();
971
972 let ret = unsafe {
973 ndk_sys::ANativeWindow_setBuffersGeometry(
974 handle
975 .a_native_window
976 .as_ptr()
977 .cast::<ndk_sys::ANativeWindow>(),
978 0,
979 0,
980 format,
981 )
982 };
983
984 if ret != 0 {
985 return Err(crate::InstanceError::new(format!(
986 "error {ret} returned from ANativeWindow_setBuffersGeometry",
987 )));
988 }
989 }
990 #[cfg(not(Emscripten))]
991 (Rwh::Wayland(_), raw_window_handle::RawDisplayHandle::Wayland(display_handle)) => {
992 if inner
993 .wl_display
994 .map(|ptr| ptr != display_handle.display.as_ptr())
995 .unwrap_or(true)
996 {
997 log::warn!("Re-initializing Gles context due to Wayland window");
1004
1005 use core::ops::DerefMut;
1006 let display_attributes = [khronos_egl::ATTRIB_NONE];
1007
1008 let display = unsafe {
1009 inner
1010 .egl
1011 .instance
1012 .upcast::<khronos_egl::EGL1_5>()
1013 .unwrap()
1014 .get_platform_display(
1015 EGL_PLATFORM_WAYLAND_KHR,
1016 display_handle.display.as_ptr(),
1017 &display_attributes,
1018 )
1019 }
1020 .unwrap();
1021
1022 let new_inner = Inner::create(
1023 self.flags,
1024 Arc::clone(&inner.egl.instance),
1025 display,
1026 inner.force_gles_minor_version,
1027 )?;
1028
1029 let old_inner = core::mem::replace(inner.deref_mut(), new_inner);
1030 inner.wl_display = Some(display_handle.display.as_ptr());
1031
1032 drop(old_inner);
1033 }
1034 }
1035 #[cfg(Emscripten)]
1036 (Rwh::Web(_), _) => {}
1037 other => {
1038 return Err(crate::InstanceError::new(format!(
1039 "unsupported window: {other:?}"
1040 )));
1041 }
1042 };
1043
1044 inner.egl.unmake_current();
1045
1046 Ok(Surface {
1047 egl: inner.egl.clone(),
1048 wsi: self.wsi.clone(),
1049 config: inner.config,
1050 presentable: inner.supports_native_window,
1051 raw_window_handle: window_handle,
1052 swapchain: RwLock::new(None),
1053 srgb_kind: inner.srgb_kind,
1054 })
1055 }
1056
1057 unsafe fn enumerate_adapters(
1058 &self,
1059 _surface_hint: Option<&Surface>,
1060 ) -> Vec<crate::ExposedAdapter<super::Api>> {
1061 let inner = self.inner.lock();
1062 inner.egl.make_current();
1063
1064 let mut gl = unsafe {
1065 glow::Context::from_loader_function(|name| {
1066 inner
1067 .egl
1068 .instance
1069 .get_proc_address(name)
1070 .map_or(ptr::null(), |p| p as *const _)
1071 })
1072 };
1073
1074 if !matches!(inner.srgb_kind, SrgbFrameBufferKind::None) {
1077 unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) };
1078 }
1079
1080 if self.flags.contains(wgt::InstanceFlags::DEBUG) && gl.supports_debug() {
1081 log::debug!("Max label length: {}", unsafe {
1082 gl.get_parameter_i32(glow::MAX_LABEL_LENGTH)
1083 });
1084 }
1085
1086 if self.flags.contains(wgt::InstanceFlags::VALIDATION) && gl.supports_debug() {
1087 log::debug!("Enabling GLES debug output");
1088 unsafe { gl.enable(glow::DEBUG_OUTPUT) };
1089 unsafe { gl.debug_message_callback(super::gl_debug_message_callback) };
1090 }
1091
1092 let gl = ManuallyDrop::new(gl);
1096 inner.egl.unmake_current();
1097
1098 unsafe {
1099 super::Adapter::expose(
1100 AdapterContext {
1101 glow: Mutex::new(gl),
1102 egl: Some(inner.egl.clone()),
1103 },
1104 self.options.clone(),
1105 )
1106 }
1107 .into_iter()
1108 .collect()
1109 }
1110}
1111
1112impl super::Adapter {
1113 pub unsafe fn new_external(
1123 fun: impl FnMut(&str) -> *const ffi::c_void,
1124 options: wgt::GlBackendOptions,
1125 ) -> Option<crate::ExposedAdapter<super::Api>> {
1126 let context = unsafe { glow::Context::from_loader_function(fun) };
1127 unsafe {
1128 Self::expose(
1129 AdapterContext {
1130 glow: Mutex::new(ManuallyDrop::new(context)),
1131 egl: None,
1132 },
1133 options,
1134 )
1135 }
1136 }
1137
1138 pub fn adapter_context(&self) -> &AdapterContext {
1139 &self.shared.context
1140 }
1141}
1142
1143impl super::Device {
1144 pub fn context(&self) -> &AdapterContext {
1146 &self.shared.context
1147 }
1148}
1149
1150#[derive(Debug)]
1151pub struct Swapchain {
1152 surface: khronos_egl::Surface,
1153 wl_window: Option<*mut ffi::c_void>,
1154 framebuffer: glow::Framebuffer,
1155 renderbuffer: glow::Renderbuffer,
1156 extent: wgt::Extent3d,
1158 format: wgt::TextureFormat,
1159 format_desc: super::TextureFormatDesc,
1160 #[allow(unused)]
1161 sample_type: wgt::TextureSampleType,
1162}
1163
1164#[derive(Debug)]
1165pub struct Surface {
1166 egl: EglContext,
1167 wsi: WindowSystemInterface,
1168 config: khronos_egl::Config,
1169 pub(super) presentable: bool,
1170 raw_window_handle: raw_window_handle::RawWindowHandle,
1171 swapchain: RwLock<Option<Swapchain>>,
1172 srgb_kind: SrgbFrameBufferKind,
1173}
1174
1175unsafe impl Send for Surface {}
1176unsafe impl Sync for Surface {}
1177
1178impl Surface {
1179 pub(super) unsafe fn present(
1180 &self,
1181 _suf_texture: super::Texture,
1182 context: &AdapterContext,
1183 ) -> Result<(), crate::SurfaceError> {
1184 let gl = unsafe { context.get_without_egl_lock() };
1185 let swapchain = self.swapchain.read();
1186 let sc = swapchain.as_ref().unwrap();
1187
1188 self.egl
1189 .instance
1190 .make_current(
1191 self.egl.display,
1192 Some(sc.surface),
1193 Some(sc.surface),
1194 Some(self.egl.raw),
1195 )
1196 .map_err(|e| {
1197 log::error!("make_current(surface) failed: {e}");
1198 crate::SurfaceError::Lost
1199 })?;
1200
1201 unsafe { gl.disable(glow::SCISSOR_TEST) };
1202 unsafe { gl.color_mask(true, true, true, true) };
1203
1204 unsafe { gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None) };
1205 unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(sc.framebuffer)) };
1206
1207 if !matches!(self.srgb_kind, SrgbFrameBufferKind::None) {
1208 unsafe { gl.disable(glow::FRAMEBUFFER_SRGB) };
1211 }
1212
1213 unsafe {
1217 gl.blit_framebuffer(
1218 0,
1219 sc.extent.height as i32,
1220 sc.extent.width as i32,
1221 0,
1222 0,
1223 0,
1224 sc.extent.width as i32,
1225 sc.extent.height as i32,
1226 glow::COLOR_BUFFER_BIT,
1227 glow::NEAREST,
1228 )
1229 };
1230
1231 if !matches!(self.srgb_kind, SrgbFrameBufferKind::None) {
1232 unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) };
1233 }
1234
1235 unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
1236
1237 self.egl
1238 .instance
1239 .swap_buffers(self.egl.display, sc.surface)
1240 .map_err(|e| {
1241 log::error!("swap_buffers failed: {e}");
1242 crate::SurfaceError::Lost
1243 })?;
1245 self.egl
1246 .instance
1247 .make_current(self.egl.display, None, None, None)
1248 .map_err(|e| {
1249 log::error!("make_current(null) failed: {e}");
1250 crate::SurfaceError::Lost
1251 })?;
1252
1253 Ok(())
1254 }
1255
1256 unsafe fn unconfigure_impl(
1257 &self,
1258 device: &super::Device,
1259 ) -> Option<(khronos_egl::Surface, Option<*mut ffi::c_void>)> {
1260 let gl = &device.shared.context.lock();
1261 match self.swapchain.write().take() {
1262 Some(sc) => {
1263 unsafe { gl.delete_renderbuffer(sc.renderbuffer) };
1264 unsafe { gl.delete_framebuffer(sc.framebuffer) };
1265 Some((sc.surface, sc.wl_window))
1266 }
1267 None => None,
1268 }
1269 }
1270
1271 pub fn supports_srgb(&self) -> bool {
1272 match self.srgb_kind {
1273 SrgbFrameBufferKind::None => false,
1274 _ => true,
1275 }
1276 }
1277}
1278
1279impl crate::Surface for Surface {
1280 type A = super::Api;
1281
1282 unsafe fn configure(
1283 &self,
1284 device: &super::Device,
1285 config: &crate::SurfaceConfiguration,
1286 ) -> Result<(), crate::SurfaceError> {
1287 use raw_window_handle::RawWindowHandle as Rwh;
1288
1289 let (surface, wl_window) = match unsafe { self.unconfigure_impl(device) } {
1290 Some(pair) => pair,
1291 None => {
1292 let mut wl_window = None;
1293 let (mut temp_xlib_handle, mut temp_xcb_handle);
1294 let native_window_ptr = match (self.wsi.kind, self.raw_window_handle) {
1295 (WindowKind::Unknown | WindowKind::X11, Rwh::Xlib(handle)) => {
1296 temp_xlib_handle = handle.window;
1297 ptr::from_mut(&mut temp_xlib_handle).cast::<ffi::c_void>()
1298 }
1299 (WindowKind::AngleX11, Rwh::Xlib(handle)) => handle.window as *mut ffi::c_void,
1300 (WindowKind::Unknown | WindowKind::X11, Rwh::Xcb(handle)) => {
1301 temp_xcb_handle = handle.window;
1302 ptr::from_mut(&mut temp_xcb_handle).cast::<ffi::c_void>()
1303 }
1304 (WindowKind::AngleX11, Rwh::Xcb(handle)) => {
1305 handle.window.get() as *mut ffi::c_void
1306 }
1307 (WindowKind::Unknown, Rwh::AndroidNdk(handle)) => {
1308 handle.a_native_window.as_ptr()
1309 }
1310 (WindowKind::Unknown, Rwh::OhosNdk(handle)) => handle.native_window.as_ptr(),
1311 (WindowKind::Wayland, Rwh::Wayland(handle)) => {
1312 let library = &self.wsi.display_owner.as_ref().unwrap().library;
1313 let wl_egl_window_create: libloading::Symbol<WlEglWindowCreateFun> =
1314 unsafe { library.get(c"wl_egl_window_create".to_bytes()) }.unwrap();
1315 let window =
1316 unsafe { wl_egl_window_create(handle.surface.as_ptr(), 640, 480) }
1317 .cast();
1318 wl_window = Some(window);
1319 window
1320 }
1321 #[cfg(Emscripten)]
1322 (WindowKind::Unknown, Rwh::Web(handle)) => handle.id as *mut ffi::c_void,
1323 (WindowKind::Unknown, Rwh::Win32(handle)) => {
1324 handle.hwnd.get() as *mut ffi::c_void
1325 }
1326 (WindowKind::Unknown, Rwh::AppKit(handle)) => {
1327 #[cfg(not(target_os = "macos"))]
1328 let window_ptr = handle.ns_view.as_ptr();
1329 #[cfg(target_os = "macos")]
1330 let window_ptr = {
1331 use objc::{msg_send, runtime::Object, sel, sel_impl};
1332 let layer: *mut Object =
1334 msg_send![handle.ns_view.as_ptr().cast::<Object>(), layer];
1335 layer.cast::<ffi::c_void>()
1336 };
1337 window_ptr
1338 }
1339 _ => {
1340 log::warn!(
1341 "Initialized platform {:?} doesn't work with window {:?}",
1342 self.wsi.kind,
1343 self.raw_window_handle
1344 );
1345 return Err(crate::SurfaceError::Other("incompatible window kind"));
1346 }
1347 };
1348
1349 let mut attributes = vec![
1350 khronos_egl::RENDER_BUFFER,
1351 if cfg!(any(
1355 target_os = "android",
1356 target_os = "macos",
1357 target_env = "ohos"
1358 )) || cfg!(windows)
1359 || self.wsi.kind == WindowKind::AngleX11
1360 {
1361 khronos_egl::BACK_BUFFER
1362 } else {
1363 khronos_egl::SINGLE_BUFFER
1364 },
1365 ];
1366 if config.format.is_srgb() {
1367 match self.srgb_kind {
1368 SrgbFrameBufferKind::None => {}
1369 SrgbFrameBufferKind::Core => {
1370 attributes.push(khronos_egl::GL_COLORSPACE);
1371 attributes.push(khronos_egl::GL_COLORSPACE_SRGB);
1372 }
1373 SrgbFrameBufferKind::Khr => {
1374 attributes.push(EGL_GL_COLORSPACE_KHR as i32);
1375 attributes.push(EGL_GL_COLORSPACE_SRGB_KHR as i32);
1376 }
1377 }
1378 }
1379 attributes.push(khronos_egl::ATTRIB_NONE as i32);
1380
1381 #[cfg(not(Emscripten))]
1382 let egl1_5 = self.egl.instance.upcast::<khronos_egl::EGL1_5>();
1383
1384 #[cfg(Emscripten)]
1385 let egl1_5: Option<&Arc<EglInstance>> = Some(&self.egl.instance);
1386
1387 let raw_result = match egl1_5 {
1389 Some(egl) if self.wsi.kind != WindowKind::Unknown => {
1390 let attributes_usize = attributes
1391 .into_iter()
1392 .map(|v| v as usize)
1393 .collect::<Vec<_>>();
1394 unsafe {
1395 egl.create_platform_window_surface(
1396 self.egl.display,
1397 self.config,
1398 native_window_ptr,
1399 &attributes_usize,
1400 )
1401 }
1402 }
1403 _ => unsafe {
1404 self.egl.instance.create_window_surface(
1405 self.egl.display,
1406 self.config,
1407 native_window_ptr,
1408 Some(&attributes),
1409 )
1410 },
1411 };
1412
1413 match raw_result {
1414 Ok(raw) => (raw, wl_window),
1415 Err(e) => {
1416 log::warn!("Error in create_window_surface: {e:?}");
1417 return Err(crate::SurfaceError::Lost);
1418 }
1419 }
1420 }
1421 };
1422
1423 if let Some(window) = wl_window {
1424 let library = &self.wsi.display_owner.as_ref().unwrap().library;
1425 let wl_egl_window_resize: libloading::Symbol<WlEglWindowResizeFun> =
1426 unsafe { library.get(c"wl_egl_window_resize".to_bytes()) }.unwrap();
1427 unsafe {
1428 wl_egl_window_resize(
1429 window,
1430 config.extent.width as i32,
1431 config.extent.height as i32,
1432 0,
1433 0,
1434 )
1435 };
1436 }
1437
1438 let format_desc = device.shared.describe_texture_format(config.format);
1439 let gl = &device.shared.context.lock();
1440 let renderbuffer = unsafe { gl.create_renderbuffer() }.map_err(|error| {
1441 log::error!("Internal swapchain renderbuffer creation failed: {error}");
1442 crate::DeviceError::OutOfMemory
1443 })?;
1444 unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, Some(renderbuffer)) };
1445 unsafe {
1446 gl.renderbuffer_storage(
1447 glow::RENDERBUFFER,
1448 format_desc.internal,
1449 config.extent.width as _,
1450 config.extent.height as _,
1451 )
1452 };
1453 let framebuffer = unsafe { gl.create_framebuffer() }.map_err(|error| {
1454 log::error!("Internal swapchain framebuffer creation failed: {error}");
1455 crate::DeviceError::OutOfMemory
1456 })?;
1457 unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(framebuffer)) };
1458 unsafe {
1459 gl.framebuffer_renderbuffer(
1460 glow::READ_FRAMEBUFFER,
1461 glow::COLOR_ATTACHMENT0,
1462 glow::RENDERBUFFER,
1463 Some(renderbuffer),
1464 )
1465 };
1466 unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, None) };
1467 unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
1468
1469 let mut swapchain = self.swapchain.write();
1470 *swapchain = Some(Swapchain {
1471 surface,
1472 wl_window,
1473 renderbuffer,
1474 framebuffer,
1475 extent: config.extent,
1476 format: config.format,
1477 format_desc,
1478 sample_type: wgt::TextureSampleType::Float { filterable: false },
1479 });
1480
1481 Ok(())
1482 }
1483
1484 unsafe fn unconfigure(&self, device: &super::Device) {
1485 if let Some((surface, wl_window)) = unsafe { self.unconfigure_impl(device) } {
1486 self.egl
1487 .instance
1488 .destroy_surface(self.egl.display, surface)
1489 .unwrap();
1490 if let Some(window) = wl_window {
1491 let library = &self
1492 .wsi
1493 .display_owner
1494 .as_ref()
1495 .expect("unsupported window")
1496 .library;
1497 let wl_egl_window_destroy: libloading::Symbol<WlEglWindowDestroyFun> =
1498 unsafe { library.get(c"wl_egl_window_destroy".to_bytes()) }.unwrap();
1499 unsafe { wl_egl_window_destroy(window) };
1500 }
1501 }
1502 }
1503
1504 unsafe fn acquire_texture(
1505 &self,
1506 _timeout_ms: Option<Duration>, _fence: &super::Fence,
1508 ) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
1509 let swapchain = self.swapchain.read();
1510 let sc = swapchain.as_ref().unwrap();
1511 let texture = super::Texture {
1512 inner: super::TextureInner::Renderbuffer {
1513 raw: sc.renderbuffer,
1514 },
1515 drop_guard: None,
1516 array_layer_count: 1,
1517 mip_level_count: 1,
1518 format: sc.format,
1519 format_desc: sc.format_desc.clone(),
1520 copy_size: crate::CopyExtent {
1521 width: sc.extent.width,
1522 height: sc.extent.height,
1523 depth: 1,
1524 },
1525 };
1526 Ok(Some(crate::AcquiredSurfaceTexture {
1527 texture,
1528 suboptimal: false,
1529 }))
1530 }
1531 unsafe fn discard_texture(&self, _texture: super::Texture) {}
1532}