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