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