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