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