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