wgpu_hal/gles/
egl.rs

1use alloc::{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
9/// The amount of time to wait while trying to obtain a lock to the adapter context
10const 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
24#[cfg(not(Emscripten))]
25type EglInstance = khronos_egl::DynamicInstance<khronos_egl::EGL1_4>;
26
27#[cfg(Emscripten)]
28type EglInstance = khronos_egl::Instance<khronos_egl::Static>;
29
30type EglLabel = *const ffi::c_void;
31
32#[allow(clippy::upper_case_acronyms)]
33type EGLDEBUGPROCKHR = Option<
34    unsafe extern "system" fn(
35        error: khronos_egl::Enum,
36        command: *const ffi::c_char,
37        message_type: u32,
38        thread_label: EglLabel,
39        object_label: EglLabel,
40        message: *const ffi::c_char,
41    ),
42>;
43
44const EGL_DEBUG_MSG_CRITICAL_KHR: u32 = 0x33B9;
45const EGL_DEBUG_MSG_ERROR_KHR: u32 = 0x33BA;
46const EGL_DEBUG_MSG_WARN_KHR: u32 = 0x33BB;
47const EGL_DEBUG_MSG_INFO_KHR: u32 = 0x33BC;
48
49type EglDebugMessageControlFun = unsafe extern "system" fn(
50    proc: EGLDEBUGPROCKHR,
51    attrib_list: *const khronos_egl::Attrib,
52) -> ffi::c_int;
53
54unsafe extern "system" fn egl_debug_proc(
55    error: khronos_egl::Enum,
56    command_raw: *const ffi::c_char,
57    message_type: u32,
58    _thread_label: EglLabel,
59    _object_label: EglLabel,
60    message_raw: *const ffi::c_char,
61) {
62    let log_severity = match message_type {
63        EGL_DEBUG_MSG_CRITICAL_KHR | EGL_DEBUG_MSG_ERROR_KHR => log::Level::Error,
64        EGL_DEBUG_MSG_WARN_KHR => log::Level::Warn,
65        // We intentionally suppress info messages down to debug
66        // so that users are not inundated with info messages from
67        // the runtime.
68        EGL_DEBUG_MSG_INFO_KHR => log::Level::Debug,
69        _ => log::Level::Trace,
70    };
71    let command = unsafe { ffi::CStr::from_ptr(command_raw) }.to_string_lossy();
72    let message = if message_raw.is_null() {
73        "".into()
74    } else {
75        unsafe { ffi::CStr::from_ptr(message_raw) }.to_string_lossy()
76    };
77
78    log::log!(log_severity, "EGL '{command}' code 0x{error:x}: {message}",);
79}
80
81#[derive(Clone, Copy, Debug)]
82enum SrgbFrameBufferKind {
83    /// No support for SRGB surface
84    None,
85    /// Using EGL 1.5's support for colorspaces
86    Core,
87    /// Using EGL_KHR_gl_colorspace
88    Khr,
89}
90
91/// Choose GLES framebuffer configuration.
92fn choose_config(
93    egl: &EglInstance,
94    display: khronos_egl::Display,
95    srgb_kind: SrgbFrameBufferKind,
96) -> Result<(khronos_egl::Config, bool), crate::InstanceError> {
97    //TODO: EGL_SLOW_CONFIG
98    let tiers = [
99        (
100            "off-screen",
101            &[
102                khronos_egl::SURFACE_TYPE,
103                khronos_egl::PBUFFER_BIT,
104                khronos_egl::RENDERABLE_TYPE,
105                khronos_egl::OPENGL_ES2_BIT,
106            ][..],
107        ),
108        (
109            "presentation",
110            &[khronos_egl::SURFACE_TYPE, khronos_egl::WINDOW_BIT][..],
111        ),
112        #[cfg(not(target_os = "android"))]
113        (
114            "native-render",
115            &[khronos_egl::NATIVE_RENDERABLE, khronos_egl::TRUE as _][..],
116        ),
117    ];
118
119    let mut attributes = Vec::with_capacity(9);
120    for tier_max in (0..tiers.len()).rev() {
121        let name = tiers[tier_max].0;
122        log::debug!("\tTrying {name}");
123
124        attributes.clear();
125        for &(_, tier_attr) in tiers[..=tier_max].iter() {
126            attributes.extend_from_slice(tier_attr);
127        }
128        // make sure the Alpha is enough to support sRGB
129        match srgb_kind {
130            SrgbFrameBufferKind::None => {}
131            _ => {
132                attributes.push(khronos_egl::ALPHA_SIZE);
133                attributes.push(8);
134            }
135        }
136        attributes.push(khronos_egl::NONE);
137
138        match egl.choose_first_config(display, &attributes) {
139            Ok(Some(config)) => {
140                if tier_max == 1 {
141                    //Note: this has been confirmed to malfunction on Intel+NV laptops,
142                    // but also on Angle.
143                    log::info!("EGL says it can present to the window but not natively",);
144                }
145                // Android emulator can't natively present either.
146                let tier_threshold =
147                    if cfg!(target_os = "android") || cfg!(windows) || cfg!(target_env = "ohos") {
148                        1
149                    } else {
150                        2
151                    };
152                return Ok((config, tier_max >= tier_threshold));
153            }
154            Ok(None) => {
155                log::debug!("No config found!");
156            }
157            Err(e) => {
158                log::error!("error in choose_first_config: {e:?}");
159            }
160        }
161    }
162
163    // TODO: include diagnostic details that are currently logged
164    Err(crate::InstanceError::new(String::from(
165        "unable to find an acceptable EGL framebuffer configuration",
166    )))
167}
168
169#[derive(Clone, Debug)]
170struct EglContext {
171    instance: Arc<EglInstance>,
172    version: (i32, i32),
173    display: khronos_egl::Display,
174    raw: khronos_egl::Context,
175    pbuffer: Option<khronos_egl::Surface>,
176}
177
178impl EglContext {
179    fn make_current(&self) {
180        self.instance
181            .make_current(self.display, self.pbuffer, self.pbuffer, Some(self.raw))
182            .unwrap();
183    }
184
185    fn unmake_current(&self) {
186        self.instance
187            .make_current(self.display, None, None, None)
188            .unwrap();
189    }
190}
191
192/// A wrapper around a [`glow::Context`] and the required EGL context that uses locking to guarantee
193/// exclusive access when shared with multiple threads.
194pub struct AdapterContext {
195    glow: Mutex<ManuallyDrop<glow::Context>>,
196    egl: Option<EglContext>,
197}
198
199unsafe impl Sync for AdapterContext {}
200unsafe impl Send for AdapterContext {}
201
202impl AdapterContext {
203    pub fn is_owned(&self) -> bool {
204        self.egl.is_some()
205    }
206
207    /// Returns the EGL instance.
208    ///
209    /// This provides access to EGL functions and the ability to load GL and EGL extension functions.
210    pub fn egl_instance(&self) -> Option<&EglInstance> {
211        self.egl.as_ref().map(|egl| &*egl.instance)
212    }
213
214    /// Returns the EGLDisplay corresponding to the adapter context.
215    ///
216    /// Returns [`None`] if the adapter was externally created.
217    pub fn raw_display(&self) -> Option<&khronos_egl::Display> {
218        self.egl.as_ref().map(|egl| &egl.display)
219    }
220
221    /// Returns the EGL version the adapter context was created with.
222    ///
223    /// Returns [`None`] if the adapter was externally created.
224    pub fn egl_version(&self) -> Option<(i32, i32)> {
225        self.egl.as_ref().map(|egl| egl.version)
226    }
227
228    pub fn raw_context(&self) -> *mut ffi::c_void {
229        match self.egl {
230            Some(ref egl) => egl.raw.as_ptr(),
231            None => ptr::null_mut(),
232        }
233    }
234}
235
236impl Drop for AdapterContext {
237    fn drop(&mut self) {
238        struct CurrentGuard<'a>(&'a EglContext);
239        impl Drop for CurrentGuard<'_> {
240            fn drop(&mut self) {
241                self.0.unmake_current();
242            }
243        }
244
245        // Context must be current when dropped. See safety docs on
246        // `glow::HasContext`.
247        //
248        // NOTE: This is only set to `None` by `Adapter::new_external` which
249        // requires the context to be current when anything that may be holding
250        // the `Arc<AdapterShared>` is dropped.
251        let _guard = self.egl.as_ref().map(|egl| {
252            egl.make_current();
253            CurrentGuard(egl)
254        });
255        let glow = self.glow.get_mut();
256        // SAFETY: Field not used after this.
257        unsafe { ManuallyDrop::drop(glow) };
258    }
259}
260
261struct EglContextLock<'a> {
262    instance: &'a Arc<EglInstance>,
263    display: khronos_egl::Display,
264}
265
266/// A guard containing a lock to an [`AdapterContext`], while the GL context is kept current.
267pub struct AdapterContextLock<'a> {
268    glow: MutexGuard<'a, ManuallyDrop<glow::Context>>,
269    egl: Option<EglContextLock<'a>>,
270}
271
272impl<'a> core::ops::Deref for AdapterContextLock<'a> {
273    type Target = glow::Context;
274
275    fn deref(&self) -> &Self::Target {
276        &self.glow
277    }
278}
279
280impl<'a> Drop for AdapterContextLock<'a> {
281    fn drop(&mut self) {
282        if let Some(egl) = self.egl.take() {
283            if let Err(err) = egl.instance.make_current(egl.display, None, None, None) {
284                log::error!("Failed to make EGL context current: {err:?}");
285            }
286        }
287    }
288}
289
290impl AdapterContext {
291    /// Get's the [`glow::Context`] without waiting for a lock
292    ///
293    /// # Safety
294    ///
295    /// This should only be called when you have manually made sure that the current thread has made
296    /// the EGL context current and that no other thread also has the EGL context current.
297    /// Additionally, you must manually make the EGL context **not** current after you are done with
298    /// it, so that future calls to `lock()` will not fail.
299    ///
300    /// > **Note:** Calling this function **will** still lock the [`glow::Context`] which adds an
301    /// > extra safe-guard against accidental concurrent access to the context.
302    pub unsafe fn get_without_egl_lock(&self) -> MappedMutexGuard<'_, glow::Context> {
303        let guard = self
304            .glow
305            .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
306            .expect("Could not lock adapter context. This is most-likely a deadlock.");
307        MutexGuard::map(guard, |glow| &mut **glow)
308    }
309
310    /// Obtain a lock to the EGL context and get handle to the [`glow::Context`] that can be used to
311    /// do rendering.
312    #[track_caller]
313    pub fn lock<'a>(&'a self) -> AdapterContextLock<'a> {
314        let glow = self
315            .glow
316            // Don't lock forever. If it takes longer than 1 second to get the lock we've got a
317            // deadlock and should panic to show where we got stuck
318            .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
319            .expect("Could not lock adapter context. This is most-likely a deadlock.");
320
321        let egl = self.egl.as_ref().map(|egl| {
322            egl.make_current();
323            EglContextLock {
324                instance: &egl.instance,
325                display: egl.display,
326            }
327        });
328
329        AdapterContextLock { glow, egl }
330    }
331}
332
333#[derive(Debug)]
334struct Inner {
335    /// Note: the context contains a dummy pbuffer (1x1).
336    /// Required for `eglMakeCurrent` on platforms that doesn't supports `EGL_KHR_surfaceless_context`.
337    egl: EglContext,
338    #[allow(unused)]
339    version: (i32, i32),
340    supports_native_window: bool,
341    config: khronos_egl::Config,
342    /// Method by which the framebuffer should support srgb
343    srgb_kind: SrgbFrameBufferKind,
344}
345
346// Different calls to `eglGetPlatformDisplay` may return the same `Display`, making it a global
347// state of all our `EglContext`s. This forces us to track the number of such context to prevent
348// terminating the display if it's currently used by another `EglContext`.
349static DISPLAYS_REFERENCE_COUNT: LazyLock<Mutex<HashMap<usize, usize>>> =
350    LazyLock::new(Default::default);
351
352fn initialize_display(
353    egl: &EglInstance,
354    display: khronos_egl::Display,
355) -> Result<(i32, i32), khronos_egl::Error> {
356    let mut guard = DISPLAYS_REFERENCE_COUNT.lock();
357    *guard.entry(display.as_ptr() as usize).or_default() += 1;
358
359    // We don't need to check the reference count here since according to the `eglInitialize`
360    // documentation, initializing an already initialized EGL display connection has no effect
361    // besides returning the version numbers.
362    egl.initialize(display)
363}
364
365fn terminate_display(
366    egl: &EglInstance,
367    display: khronos_egl::Display,
368) -> Result<(), khronos_egl::Error> {
369    let key = &(display.as_ptr() as usize);
370    let mut guard = DISPLAYS_REFERENCE_COUNT.lock();
371    let count_ref = guard
372        .get_mut(key)
373        .expect("Attempted to decref a display before incref was called");
374
375    if *count_ref > 1 {
376        *count_ref -= 1;
377
378        Ok(())
379    } else {
380        guard.remove(key);
381
382        egl.terminate(display)
383    }
384}
385
386fn instance_err<E: core::error::Error + Send + Sync + 'static>(
387    message: impl Into<String>,
388) -> impl FnOnce(E) -> crate::InstanceError {
389    move |e| crate::InstanceError::with_source(message.into(), e)
390}
391
392impl Inner {
393    fn create(
394        flags: wgt::InstanceFlags,
395        egl: Arc<EglInstance>,
396        display: khronos_egl::Display,
397        force_gles_minor_version: wgt::Gles3MinorVersion,
398    ) -> Result<Self, crate::InstanceError> {
399        let version = initialize_display(&egl, display)
400            .map_err(instance_err("failed to initialize EGL display connection"))?;
401        let vendor = egl
402            .query_string(Some(display), khronos_egl::VENDOR)
403            .map_err(instance_err("failed to query EGL vendor"))?;
404        let display_extensions = egl
405            .query_string(Some(display), khronos_egl::EXTENSIONS)
406            .map_err(instance_err("failed to query EGL display extensions"))?
407            .to_string_lossy();
408        log::debug!("Display vendor {vendor:?}, version {version:?}",);
409        log::debug!(
410            "Display extensions: {:#?}",
411            display_extensions.split_whitespace().collect::<Vec<_>>()
412        );
413
414        let srgb_kind = if version >= (1, 5) {
415            log::debug!("\tEGL surface: +srgb");
416            SrgbFrameBufferKind::Core
417        } else if display_extensions.contains("EGL_KHR_gl_colorspace") {
418            log::debug!("\tEGL surface: +srgb khr");
419            SrgbFrameBufferKind::Khr
420        } else {
421            log::debug!("\tEGL surface: -srgb");
422            SrgbFrameBufferKind::None
423        };
424
425        if log::max_level() >= log::LevelFilter::Trace {
426            log::trace!("Configurations:");
427            let config_count = egl
428                .get_config_count(display)
429                .map_err(instance_err("failed to get config count"))?;
430            let mut configurations = Vec::with_capacity(config_count);
431            egl.get_configs(display, &mut configurations)
432                .map_err(instance_err("failed to get configs"))?;
433            for &config in configurations.iter() {
434                log::trace!("\tCONFORMANT=0x{:X?}, RENDERABLE=0x{:X?}, NATIVE_RENDERABLE=0x{:X?}, SURFACE_TYPE=0x{:X?}, ALPHA_SIZE={:?}",
435                    egl.get_config_attrib(display, config, khronos_egl::CONFORMANT),
436                    egl.get_config_attrib(display, config, khronos_egl::RENDERABLE_TYPE),
437                    egl.get_config_attrib(display, config, khronos_egl::NATIVE_RENDERABLE),
438                    egl.get_config_attrib(display, config, khronos_egl::SURFACE_TYPE),
439                    egl.get_config_attrib(display, config, khronos_egl::ALPHA_SIZE),
440                );
441            }
442        }
443
444        let (config, supports_native_window) = choose_config(&egl, display, srgb_kind)?;
445
446        let supports_opengl = if version >= (1, 4) {
447            let client_apis = egl
448                .query_string(Some(display), khronos_egl::CLIENT_APIS)
449                .map_err(instance_err("failed to query EGL client APIs string"))?
450                .to_string_lossy();
451            client_apis
452                .split(' ')
453                .any(|client_api| client_api == "OpenGL")
454        } else {
455            false
456        };
457        egl.bind_api(if supports_opengl {
458            khronos_egl::OPENGL_API
459        } else {
460            khronos_egl::OPENGL_ES_API
461        })
462        .map_err(instance_err("failed to bind API"))?;
463
464        let mut khr_context_flags = 0;
465        let supports_khr_context = display_extensions.contains("EGL_KHR_create_context");
466
467        let mut context_attributes = vec![];
468        let mut gl_context_attributes = vec![];
469        let mut gles_context_attributes = vec![];
470        gl_context_attributes.push(khronos_egl::CONTEXT_MAJOR_VERSION);
471        gl_context_attributes.push(3);
472        gl_context_attributes.push(khronos_egl::CONTEXT_MINOR_VERSION);
473        gl_context_attributes.push(3);
474        if supports_opengl && force_gles_minor_version != wgt::Gles3MinorVersion::Automatic {
475            log::warn!("Ignoring specified GLES minor version as OpenGL is used");
476        }
477        gles_context_attributes.push(khronos_egl::CONTEXT_MAJOR_VERSION);
478        gles_context_attributes.push(3); // Request GLES 3.0 or higher
479        if force_gles_minor_version != wgt::Gles3MinorVersion::Automatic {
480            gles_context_attributes.push(khronos_egl::CONTEXT_MINOR_VERSION);
481            gles_context_attributes.push(match force_gles_minor_version {
482                wgt::Gles3MinorVersion::Automatic => unreachable!(),
483                wgt::Gles3MinorVersion::Version0 => 0,
484                wgt::Gles3MinorVersion::Version1 => 1,
485                wgt::Gles3MinorVersion::Version2 => 2,
486            });
487        }
488        if flags.contains(wgt::InstanceFlags::DEBUG) {
489            if version >= (1, 5) {
490                log::debug!("\tEGL context: +debug");
491                context_attributes.push(khronos_egl::CONTEXT_OPENGL_DEBUG);
492                context_attributes.push(khronos_egl::TRUE as _);
493            } else if supports_khr_context {
494                log::debug!("\tEGL context: +debug KHR");
495                khr_context_flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
496            } else {
497                log::debug!("\tEGL context: -debug");
498            }
499        }
500
501        if khr_context_flags != 0 {
502            context_attributes.push(EGL_CONTEXT_FLAGS_KHR);
503            context_attributes.push(khr_context_flags);
504        }
505
506        gl_context_attributes.extend(&context_attributes);
507        gles_context_attributes.extend(&context_attributes);
508
509        let context = {
510            enum Robustness {
511                Core,
512                Ext,
513            }
514
515            let mut robustness = if version >= (1, 5) {
516                Some(Robustness::Core)
517            } else if display_extensions.contains("EGL_EXT_create_context_robustness") {
518                Some(Robustness::Ext)
519            } else {
520                None
521            };
522
523            loop {
524                let robustness_attributes = match robustness {
525                    Some(Robustness::Core) => {
526                        vec![
527                            khronos_egl::CONTEXT_OPENGL_ROBUST_ACCESS,
528                            khronos_egl::TRUE as _,
529                            khronos_egl::NONE,
530                        ]
531                    }
532                    Some(Robustness::Ext) => {
533                        vec![
534                            EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT,
535                            khronos_egl::TRUE as _,
536                            khronos_egl::NONE,
537                        ]
538                    }
539                    None => vec![khronos_egl::NONE],
540                };
541
542                let mut gl_context_attributes = gl_context_attributes.clone();
543                gl_context_attributes.extend(&robustness_attributes);
544
545                let mut gles_context_attributes = gles_context_attributes.clone();
546                gles_context_attributes.extend(&robustness_attributes);
547
548                let result = if supports_opengl {
549                    egl.create_context(display, config, None, &gl_context_attributes)
550                        .or_else(|_| {
551                            egl.bind_api(khronos_egl::OPENGL_ES_API)?;
552                            egl.create_context(display, config, None, &gles_context_attributes)
553                        })
554                } else {
555                    egl.create_context(display, config, None, &gles_context_attributes)
556                };
557
558                match (result, robustness) {
559                    // We have a context at the requested robustness level
560                    (Ok(_), robustness) => {
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
573                        break result;
574                    }
575
576                    // BadAttribute could mean that context creation is not supported at the requested robustness level
577                    // We try the next robustness level.
578                    (Err(khronos_egl::Error::BadAttribute), Some(r)) => {
579                        // Trying EXT robustness if Core robustness is not working
580                        // and EXT robustness is supported.
581                        robustness = if matches!(r, Robustness::Core)
582                            && display_extensions.contains("EGL_EXT_create_context_robustness")
583                        {
584                            Some(Robustness::Ext)
585                        } else {
586                            None
587                        };
588
589                        continue;
590                    }
591
592                    // Any other error, or depleted robustness levels, we give up.
593                    _ => break result,
594                }
595            }
596            .map_err(|e| {
597                crate::InstanceError::with_source(
598                    String::from("unable to create OpenGL or GLES 3.x context"),
599                    e,
600                )
601            })
602        }?;
603
604        // Testing if context can be binded without surface
605        // and creating dummy pbuffer surface if not.
606        let pbuffer = if version >= (1, 5)
607            || display_extensions.contains("EGL_KHR_surfaceless_context")
608            || cfg!(Emscripten)
609        {
610            log::debug!("\tEGL context: +surfaceless");
611            None
612        } else {
613            let attributes = [
614                khronos_egl::WIDTH,
615                1,
616                khronos_egl::HEIGHT,
617                1,
618                khronos_egl::NONE,
619            ];
620            egl.create_pbuffer_surface(display, config, &attributes)
621                .map(Some)
622                .map_err(|e| {
623                    crate::InstanceError::with_source(
624                        String::from("error in create_pbuffer_surface"),
625                        e,
626                    )
627                })?
628        };
629
630        Ok(Self {
631            egl: EglContext {
632                instance: egl,
633                display,
634                raw: context,
635                pbuffer,
636                version,
637            },
638            version,
639            supports_native_window,
640            config,
641            srgb_kind,
642        })
643    }
644}
645
646impl Drop for Inner {
647    fn drop(&mut self) {
648        // ERROR: Since EglContext is erroneously Clone, these handles could be copied and
649        // accidentally used elsewhere outside of Inner, despite us assuming ownership and
650        // destroying the handles here.
651        if let Err(e) = self
652            .egl
653            .instance
654            .destroy_context(self.egl.display, self.egl.raw)
655        {
656            log::warn!("Error in destroy_context: {e:?}");
657        }
658
659        if let Err(e) = terminate_display(&self.egl.instance, self.egl.display) {
660            log::warn!("Error in terminate: {e:?}");
661        }
662    }
663}
664
665#[derive(Clone, Copy, Debug, PartialEq)]
666enum WindowKind {
667    Wayland,
668    X11,
669    AngleX11,
670    Unknown,
671}
672
673#[derive(Clone, Debug)]
674struct WindowSystemInterface {
675    kind: WindowKind,
676}
677
678pub struct Instance {
679    wsi: WindowSystemInterface,
680    flags: wgt::InstanceFlags,
681    options: wgt::GlBackendOptions,
682    inner: Mutex<Inner>,
683}
684
685impl Instance {
686    pub fn raw_display(&self) -> khronos_egl::Display {
687        self.inner
688            .try_lock()
689            .expect("Could not lock instance. This is most-likely a deadlock.")
690            .egl
691            .display
692    }
693
694    /// Returns the version of the EGL display.
695    pub fn egl_version(&self) -> (i32, i32) {
696        self.inner
697            .try_lock()
698            .expect("Could not lock instance. This is most-likely a deadlock.")
699            .version
700    }
701
702    pub fn egl_config(&self) -> khronos_egl::Config {
703        self.inner
704            .try_lock()
705            .expect("Could not lock instance. This is most-likely a deadlock.")
706            .config
707    }
708}
709
710unsafe impl Send for Instance {}
711unsafe impl Sync for Instance {}
712
713impl crate::Instance for Instance {
714    type A = super::Api;
715
716    unsafe fn init(desc: &crate::InstanceDescriptor<'_>) -> Result<Self, crate::InstanceError> {
717        use raw_window_handle::RawDisplayHandle as Rdh;
718
719        profiling::scope!("Init OpenGL (EGL) Backend");
720        #[cfg(Emscripten)]
721        let egl_result: Result<EglInstance, khronos_egl::Error> =
722            Ok(khronos_egl::Instance::new(khronos_egl::Static));
723
724        #[cfg(not(Emscripten))]
725        let egl_result = if cfg!(windows) {
726            unsafe {
727                khronos_egl::DynamicInstance::<khronos_egl::EGL1_4>::load_required_from_filename(
728                    "libEGL.dll",
729                )
730            }
731        } else if cfg!(target_vendor = "apple") {
732            unsafe {
733                khronos_egl::DynamicInstance::<khronos_egl::EGL1_4>::load_required_from_filename(
734                    "libEGL.dylib",
735                )
736            }
737        } else {
738            unsafe { khronos_egl::DynamicInstance::<khronos_egl::EGL1_4>::load_required() }
739        };
740        let egl = egl_result
741            .map(Arc::new)
742            .map_err(instance_err("unable to open libEGL"))?;
743
744        let client_extensions = egl.query_string(None, khronos_egl::EXTENSIONS);
745
746        let client_ext_str = match client_extensions {
747            Ok(ext) => ext.to_string_lossy().into_owned(),
748            Err(_) => String::new(),
749        };
750        log::debug!(
751            "Client extensions: {:#?}",
752            client_ext_str.split_whitespace().collect::<Vec<_>>()
753        );
754
755        #[cfg(not(Emscripten))]
756        let egl1_5 = egl.upcast::<khronos_egl::EGL1_5>();
757
758        #[cfg(Emscripten)]
759        let egl1_5: Option<&Arc<EglInstance>> = Some(&egl);
760
761        let (display, wsi_kind) = match (desc.display.map(|d| d.as_raw()), egl1_5) {
762            (Some(Rdh::Wayland(wayland_display_handle)), Some(egl))
763                if client_ext_str.contains("EGL_EXT_platform_wayland") =>
764            {
765                log::debug!("Using Wayland platform");
766                let display_attributes = [khronos_egl::ATTRIB_NONE];
767                let display = unsafe {
768                    egl.get_platform_display(
769                        EGL_PLATFORM_WAYLAND_KHR,
770                        wayland_display_handle.display.as_ptr(),
771                        &display_attributes,
772                    )
773                }
774                .map_err(instance_err("failed to get Wayland display"))?;
775                (display, WindowKind::Wayland)
776            }
777            (Some(Rdh::Xlib(xlib_display_handle)), Some(egl))
778                if client_ext_str.contains("EGL_EXT_platform_x11") =>
779            {
780                log::debug!("Using X11 platform");
781                let display_attributes = [khronos_egl::ATTRIB_NONE];
782                let display = unsafe {
783                    egl.get_platform_display(
784                        EGL_PLATFORM_X11_KHR,
785                        xlib_display_handle
786                            .display
787                            .map_or(khronos_egl::DEFAULT_DISPLAY, ptr::NonNull::as_ptr),
788                        &display_attributes,
789                    )
790                }
791                .map_err(instance_err("failed to get X11 display"))?;
792                (display, WindowKind::X11)
793            }
794            (Some(Rdh::Xlib(xlib_display_handle)), Some(egl))
795                if client_ext_str.contains("EGL_ANGLE_platform_angle") =>
796            {
797                log::debug!("Using Angle platform with X11");
798                let display_attributes = [
799                    EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE as khronos_egl::Attrib,
800                    EGL_PLATFORM_X11_KHR as khronos_egl::Attrib,
801                    EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED as khronos_egl::Attrib,
802                    usize::from(desc.flags.contains(wgt::InstanceFlags::VALIDATION)),
803                    khronos_egl::ATTRIB_NONE,
804                ];
805                let display = unsafe {
806                    egl.get_platform_display(
807                        EGL_PLATFORM_ANGLE_ANGLE,
808                        xlib_display_handle
809                            .display
810                            .map_or(khronos_egl::DEFAULT_DISPLAY, ptr::NonNull::as_ptr),
811                        &display_attributes,
812                    )
813                }
814                .map_err(instance_err("failed to get Angle display"))?;
815                (display, WindowKind::AngleX11)
816            }
817            (Some(Rdh::Xcb(_xcb_display_handle)), Some(_egl)) => todo!("xcb"),
818            x if client_ext_str.contains("EGL_MESA_platform_surfaceless") => {
819                log::debug!(
820                    "No (or unknown) windowing system ({x:?}) present. Using surfaceless platform"
821                );
822                #[allow(clippy::unnecessary_literal_unwrap)]
823                // This is only a literal on Emscripten
824                // TODO: This extension is also supported on EGL 1.4 with EGL_EXT_platform_base: https://registry.khronos.org/EGL/extensions/MESA/EGL_MESA_platform_surfaceless.txt
825                let egl = egl1_5.expect("Failed to get EGL 1.5 for surfaceless");
826                let display = unsafe {
827                    egl.get_platform_display(
828                        EGL_PLATFORM_SURFACELESS_MESA,
829                        khronos_egl::DEFAULT_DISPLAY,
830                        &[khronos_egl::ATTRIB_NONE],
831                    )
832                }
833                .map_err(instance_err("failed to get MESA surfaceless display"))?;
834                (display, WindowKind::Unknown)
835            }
836            x => {
837                log::debug!(
838                    "No (or unknown) windowing system {x:?} and EGL_MESA_platform_surfaceless not available. Using default platform"
839                );
840                let display =
841                    unsafe { egl.get_display(khronos_egl::DEFAULT_DISPLAY) }.ok_or_else(|| {
842                        crate::InstanceError::new("Failed to get default display".into())
843                    })?;
844                (display, WindowKind::Unknown)
845            }
846        };
847
848        if desc.flags.contains(wgt::InstanceFlags::VALIDATION)
849            && client_ext_str.contains("EGL_KHR_debug")
850        {
851            log::debug!("Enabling EGL debug output");
852            let function: EglDebugMessageControlFun = {
853                let addr = egl
854                    .get_proc_address("eglDebugMessageControlKHR")
855                    .ok_or_else(|| {
856                        crate::InstanceError::new(
857                            "failed to get `eglDebugMessageControlKHR` proc address".into(),
858                        )
859                    })?;
860                unsafe { core::mem::transmute(addr) }
861            };
862            let attributes = [
863                EGL_DEBUG_MSG_CRITICAL_KHR as khronos_egl::Attrib,
864                1,
865                EGL_DEBUG_MSG_ERROR_KHR as khronos_egl::Attrib,
866                1,
867                EGL_DEBUG_MSG_WARN_KHR as khronos_egl::Attrib,
868                1,
869                EGL_DEBUG_MSG_INFO_KHR as khronos_egl::Attrib,
870                1,
871                khronos_egl::ATTRIB_NONE,
872            ];
873            unsafe { (function)(Some(egl_debug_proc), attributes.as_ptr()) };
874        }
875
876        let inner = Inner::create(
877            desc.flags,
878            egl,
879            display,
880            desc.backend_options.gl.gles_minor_version,
881        )?;
882
883        Ok(Instance {
884            wsi: WindowSystemInterface { kind: wsi_kind },
885            flags: desc.flags,
886            options: desc.backend_options.gl.clone(),
887            inner: Mutex::new(inner),
888        })
889    }
890
891    #[cfg_attr(target_os = "macos", allow(unused, unused_mut, unreachable_code))]
892    unsafe fn create_surface(
893        &self,
894        display_handle: raw_window_handle::RawDisplayHandle,
895        window_handle: raw_window_handle::RawWindowHandle,
896    ) -> Result<Surface, crate::InstanceError> {
897        use raw_window_handle::RawWindowHandle as Rwh;
898
899        let inner = self.inner.lock();
900
901        match (window_handle, display_handle) {
902            (Rwh::Xlib(_), _) => {}
903            (Rwh::Xcb(_), _) => {}
904            (Rwh::Win32(_), _) => {}
905            (Rwh::AppKit(_), _) => {}
906            (Rwh::OhosNdk(_), _) => {}
907            #[cfg(target_os = "android")]
908            (Rwh::AndroidNdk(handle), _) => {
909                let format = inner
910                    .egl
911                    .instance
912                    .get_config_attrib(
913                        inner.egl.display,
914                        inner.config,
915                        khronos_egl::NATIVE_VISUAL_ID,
916                    )
917                    .map_err(instance_err("failed to get config NATIVE_VISUAL_ID"))?;
918
919                let ret = unsafe {
920                    ndk_sys::ANativeWindow_setBuffersGeometry(
921                        handle
922                            .a_native_window
923                            .as_ptr()
924                            .cast::<ndk_sys::ANativeWindow>(),
925                        0,
926                        0,
927                        format,
928                    )
929                };
930
931                if ret != 0 {
932                    return Err(crate::InstanceError::new(format!(
933                        "error {ret} returned from ANativeWindow_setBuffersGeometry",
934                    )));
935                }
936            }
937            (Rwh::Wayland(_), _) => {}
938            #[cfg(Emscripten)]
939            (Rwh::Web(_), _) => {}
940            other => {
941                return Err(crate::InstanceError::new(format!(
942                    "unsupported window: {other:?}"
943                )));
944            }
945        };
946
947        inner.egl.unmake_current();
948
949        Ok(Surface {
950            egl: inner.egl.clone(),
951            wsi: self.wsi.clone(),
952            config: inner.config,
953            presentable: inner.supports_native_window,
954            raw_window_handle: window_handle,
955            swapchain: RwLock::new(None),
956            srgb_kind: inner.srgb_kind,
957        })
958    }
959
960    unsafe fn enumerate_adapters(
961        &self,
962        _surface_hint: Option<&Surface>,
963    ) -> Vec<crate::ExposedAdapter<super::Api>> {
964        let inner = self.inner.lock();
965        inner.egl.make_current();
966
967        let mut gl = unsafe {
968            glow::Context::from_loader_function(|name| {
969                inner
970                    .egl
971                    .instance
972                    .get_proc_address(name)
973                    .map_or(ptr::null(), |p| p as *const _)
974            })
975        };
976
977        // In contrast to OpenGL ES, OpenGL requires explicitly enabling sRGB conversions,
978        // as otherwise the user has to do the sRGB conversion.
979        if !matches!(inner.srgb_kind, SrgbFrameBufferKind::None) {
980            unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) };
981        }
982
983        if self.flags.contains(wgt::InstanceFlags::DEBUG) && gl.supports_debug() {
984            log::debug!("Max label length: {}", unsafe {
985                gl.get_parameter_i32(glow::MAX_LABEL_LENGTH)
986            });
987        }
988
989        if self.flags.contains(wgt::InstanceFlags::VALIDATION) && gl.supports_debug() {
990            log::debug!("Enabling GLES debug output");
991            unsafe { gl.enable(glow::DEBUG_OUTPUT) };
992            unsafe { gl.debug_message_callback(super::gl_debug_message_callback) };
993        }
994
995        // Wrap in ManuallyDrop to make it easier to "current" the GL context before dropping this
996        // GLOW context, which could also happen if a panic occurs after we uncurrent the context
997        // below but before AdapterContext is constructed.
998        let gl = ManuallyDrop::new(gl);
999        inner.egl.unmake_current();
1000
1001        unsafe {
1002            super::Adapter::expose(
1003                AdapterContext {
1004                    glow: Mutex::new(gl),
1005                    // ERROR: Copying owned reference handles here, be careful to not drop them!
1006                    egl: Some(inner.egl.clone()),
1007                },
1008                self.options.clone(),
1009            )
1010        }
1011        .into_iter()
1012        .collect()
1013    }
1014}
1015
1016impl super::Adapter {
1017    /// Creates a new external adapter using the specified loader function.
1018    ///
1019    /// # Safety
1020    ///
1021    /// - The underlying OpenGL ES context must be current.
1022    /// - The underlying OpenGL ES context must be current when interfacing with any objects returned by
1023    ///   wgpu-hal from this adapter.
1024    /// - The underlying OpenGL ES context must be current when dropping this adapter and when
1025    ///   dropping any objects returned from this adapter.
1026    pub unsafe fn new_external(
1027        fun: impl FnMut(&str) -> *const ffi::c_void,
1028        options: wgt::GlBackendOptions,
1029    ) -> Option<crate::ExposedAdapter<super::Api>> {
1030        let context = unsafe { glow::Context::from_loader_function(fun) };
1031        unsafe {
1032            Self::expose(
1033                AdapterContext {
1034                    glow: Mutex::new(ManuallyDrop::new(context)),
1035                    egl: None,
1036                },
1037                options,
1038            )
1039        }
1040    }
1041
1042    pub fn adapter_context(&self) -> &AdapterContext {
1043        &self.shared.context
1044    }
1045}
1046
1047impl super::Device {
1048    /// Returns the underlying EGL context.
1049    pub fn context(&self) -> &AdapterContext {
1050        &self.shared.context
1051    }
1052}
1053
1054#[derive(Debug)]
1055pub struct Swapchain {
1056    surface: khronos_egl::Surface,
1057    wl_window: Option<*mut wayland_sys::egl::wl_egl_window>,
1058    framebuffer: glow::Framebuffer,
1059    renderbuffer: glow::Renderbuffer,
1060    /// Extent because the window lies
1061    extent: wgt::Extent3d,
1062    format: wgt::TextureFormat,
1063    format_desc: super::TextureFormatDesc,
1064    #[allow(unused)]
1065    sample_type: wgt::TextureSampleType,
1066}
1067
1068#[derive(Debug)]
1069pub struct Surface {
1070    egl: EglContext,
1071    wsi: WindowSystemInterface,
1072    config: khronos_egl::Config,
1073    pub(super) presentable: bool,
1074    raw_window_handle: raw_window_handle::RawWindowHandle,
1075    swapchain: RwLock<Option<Swapchain>>,
1076    srgb_kind: SrgbFrameBufferKind,
1077}
1078
1079unsafe impl Send for Surface {}
1080unsafe impl Sync for Surface {}
1081
1082impl Surface {
1083    pub(super) unsafe fn present(
1084        &self,
1085        _suf_texture: super::Texture,
1086        context: &AdapterContext,
1087    ) -> Result<(), crate::SurfaceError> {
1088        let gl = unsafe { context.get_without_egl_lock() };
1089        let swapchain = self.swapchain.read();
1090        let sc = swapchain.as_ref().ok_or(crate::SurfaceError::Other(
1091            "Surface has no swap-chain configured",
1092        ))?;
1093
1094        self.egl
1095            .instance
1096            .make_current(
1097                self.egl.display,
1098                Some(sc.surface),
1099                Some(sc.surface),
1100                Some(self.egl.raw),
1101            )
1102            .map_err(|e| {
1103                log::error!("make_current(surface) failed: {e}");
1104                crate::SurfaceError::Lost
1105            })?;
1106
1107        unsafe { gl.disable(glow::SCISSOR_TEST) };
1108        unsafe { gl.color_mask(true, true, true, true) };
1109
1110        unsafe { gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None) };
1111        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(sc.framebuffer)) };
1112
1113        if !matches!(self.srgb_kind, SrgbFrameBufferKind::None) {
1114            // Disable sRGB conversions for `glBlitFramebuffer` as behavior does diverge between
1115            // drivers and formats otherwise and we want to ensure no sRGB conversions happen.
1116            unsafe { gl.disable(glow::FRAMEBUFFER_SRGB) };
1117        }
1118
1119        // Note the Y-flipping here. GL's presentation is not flipped,
1120        // but main rendering is. Therefore, we Y-flip the output positions
1121        // in the shader, and also this blit.
1122        unsafe {
1123            gl.blit_framebuffer(
1124                0,
1125                sc.extent.height as i32,
1126                sc.extent.width as i32,
1127                0,
1128                0,
1129                0,
1130                sc.extent.width as i32,
1131                sc.extent.height as i32,
1132                glow::COLOR_BUFFER_BIT,
1133                glow::NEAREST,
1134            )
1135        };
1136
1137        if !matches!(self.srgb_kind, SrgbFrameBufferKind::None) {
1138            unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) };
1139        }
1140
1141        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
1142
1143        self.egl
1144            .instance
1145            .swap_buffers(self.egl.display, sc.surface)
1146            .map_err(|e| {
1147                log::error!("swap_buffers failed: {e}");
1148                crate::SurfaceError::Lost
1149                // TODO: should we unset the current context here?
1150            })?;
1151        self.egl
1152            .instance
1153            .make_current(self.egl.display, None, None, None)
1154            .map_err(|e| {
1155                log::error!("make_current(null) failed: {e}");
1156                crate::SurfaceError::Lost
1157            })?;
1158
1159        Ok(())
1160    }
1161
1162    unsafe fn unconfigure_impl(
1163        &self,
1164        device: &super::Device,
1165    ) -> Option<(
1166        khronos_egl::Surface,
1167        Option<*mut wayland_sys::egl::wl_egl_window>,
1168    )> {
1169        let gl = &device.shared.context.lock();
1170        match self.swapchain.write().take() {
1171            Some(sc) => {
1172                unsafe { gl.delete_renderbuffer(sc.renderbuffer) };
1173                unsafe { gl.delete_framebuffer(sc.framebuffer) };
1174                Some((sc.surface, sc.wl_window))
1175            }
1176            None => None,
1177        }
1178    }
1179
1180    pub fn supports_srgb(&self) -> bool {
1181        match self.srgb_kind {
1182            SrgbFrameBufferKind::None => false,
1183            _ => true,
1184        }
1185    }
1186}
1187
1188impl crate::Surface for Surface {
1189    type A = super::Api;
1190
1191    unsafe fn configure(
1192        &self,
1193        device: &super::Device,
1194        config: &crate::SurfaceConfiguration,
1195    ) -> Result<(), crate::SurfaceError> {
1196        use raw_window_handle::RawWindowHandle as Rwh;
1197
1198        let (surface, wl_window) = match unsafe { self.unconfigure_impl(device) } {
1199            Some((sc, wl_window)) => {
1200                if let Some(window) = wl_window {
1201                    wayland_sys::ffi_dispatch!(
1202                        wayland_sys::egl::wayland_egl_handle(),
1203                        wl_egl_window_resize,
1204                        window,
1205                        config.extent.width as i32,
1206                        config.extent.height as i32,
1207                        0,
1208                        0,
1209                    );
1210                }
1211
1212                (sc, wl_window)
1213            }
1214            None => {
1215                let mut wl_window = None;
1216                let (mut temp_xlib_handle, mut temp_xcb_handle);
1217                let native_window_ptr = match (self.wsi.kind, self.raw_window_handle) {
1218                    (WindowKind::Unknown | WindowKind::X11, Rwh::Xlib(handle)) => {
1219                        temp_xlib_handle = handle.window;
1220                        ptr::from_mut(&mut temp_xlib_handle).cast::<ffi::c_void>()
1221                    }
1222                    (WindowKind::AngleX11, Rwh::Xlib(handle)) => handle.window as *mut ffi::c_void,
1223                    (WindowKind::Unknown | WindowKind::X11, Rwh::Xcb(handle)) => {
1224                        temp_xcb_handle = handle.window;
1225                        ptr::from_mut(&mut temp_xcb_handle).cast::<ffi::c_void>()
1226                    }
1227                    (WindowKind::AngleX11, Rwh::Xcb(handle)) => {
1228                        handle.window.get() as *mut ffi::c_void
1229                    }
1230                    (WindowKind::Unknown, Rwh::AndroidNdk(handle)) => {
1231                        handle.a_native_window.as_ptr()
1232                    }
1233                    (WindowKind::Unknown, Rwh::OhosNdk(handle)) => handle.native_window.as_ptr(),
1234                    #[cfg(unix)]
1235                    (WindowKind::Wayland, Rwh::Wayland(handle)) => {
1236                        let window = wayland_sys::ffi_dispatch!(
1237                            wayland_sys::egl::wayland_egl_handle(),
1238                            wl_egl_window_create,
1239                            handle.surface.as_ptr().cast(),
1240                            config.extent.width as i32,
1241                            config.extent.height as i32,
1242                        );
1243                        wl_window = Some(window);
1244                        window.cast()
1245                    }
1246                    #[cfg(Emscripten)]
1247                    (WindowKind::Unknown, Rwh::Web(handle)) => handle.id as *mut ffi::c_void,
1248                    (WindowKind::Unknown, Rwh::Win32(handle)) => {
1249                        handle.hwnd.get() as *mut ffi::c_void
1250                    }
1251                    (WindowKind::Unknown, Rwh::AppKit(handle)) => {
1252                        #[cfg(not(target_os = "macos"))]
1253                        let window_ptr = handle.ns_view.as_ptr();
1254                        #[cfg(target_os = "macos")]
1255                        let window_ptr = {
1256                            use objc::{msg_send, runtime::Object, sel, sel_impl};
1257                            // ns_view always have a layer and don't need to verify that it exists.
1258                            let layer: *mut Object =
1259                                msg_send![handle.ns_view.as_ptr().cast::<Object>(), layer];
1260                            layer.cast::<ffi::c_void>()
1261                        };
1262                        window_ptr
1263                    }
1264                    _ => {
1265                        log::warn!(
1266                            "Initialized platform {:?} doesn't work with window {:?}",
1267                            self.wsi.kind,
1268                            self.raw_window_handle
1269                        );
1270                        return Err(crate::SurfaceError::Other("incompatible window kind"));
1271                    }
1272                };
1273
1274                let mut attributes = vec![
1275                    khronos_egl::RENDER_BUFFER,
1276                    // We don't want any of the buffering done by the driver, because we
1277                    // manage a swapchain on our side.
1278                    // Some drivers just fail on surface creation seeing `EGL_SINGLE_BUFFER`.
1279                    if cfg!(any(
1280                        target_os = "android",
1281                        target_os = "macos",
1282                        target_env = "ohos"
1283                    )) || cfg!(windows)
1284                        || self.wsi.kind == WindowKind::AngleX11
1285                    {
1286                        khronos_egl::BACK_BUFFER
1287                    } else {
1288                        khronos_egl::SINGLE_BUFFER
1289                    },
1290                ];
1291                if config.format.is_srgb() {
1292                    match self.srgb_kind {
1293                        SrgbFrameBufferKind::None => {}
1294                        SrgbFrameBufferKind::Core => {
1295                            attributes.push(khronos_egl::GL_COLORSPACE);
1296                            attributes.push(khronos_egl::GL_COLORSPACE_SRGB);
1297                        }
1298                        SrgbFrameBufferKind::Khr => {
1299                            attributes.push(EGL_GL_COLORSPACE_KHR as i32);
1300                            attributes.push(EGL_GL_COLORSPACE_SRGB_KHR as i32);
1301                        }
1302                    }
1303                }
1304                attributes.push(khronos_egl::ATTRIB_NONE as i32);
1305
1306                #[cfg(not(Emscripten))]
1307                let egl1_5 = self.egl.instance.upcast::<khronos_egl::EGL1_5>();
1308
1309                #[cfg(Emscripten)]
1310                let egl1_5: Option<&Arc<EglInstance>> = Some(&self.egl.instance);
1311
1312                // Careful, we can still be in 1.4 version even if `upcast` succeeds
1313                let raw_result = match egl1_5 {
1314                    Some(egl) if self.wsi.kind != WindowKind::Unknown => {
1315                        let attributes_usize = attributes
1316                            .into_iter()
1317                            .map(|v| v as usize)
1318                            .collect::<Vec<_>>();
1319                        unsafe {
1320                            egl.create_platform_window_surface(
1321                                self.egl.display,
1322                                self.config,
1323                                native_window_ptr,
1324                                &attributes_usize,
1325                            )
1326                        }
1327                    }
1328                    _ => unsafe {
1329                        self.egl.instance.create_window_surface(
1330                            self.egl.display,
1331                            self.config,
1332                            native_window_ptr,
1333                            Some(&attributes),
1334                        )
1335                    },
1336                };
1337
1338                match raw_result {
1339                    Ok(raw) => (raw, wl_window),
1340                    Err(e) => {
1341                        log::warn!("Error in create_window_surface: {e:?}");
1342                        return Err(crate::SurfaceError::Lost);
1343                    }
1344                }
1345            }
1346        };
1347
1348        let format_desc = device.shared.describe_texture_format(config.format);
1349        let gl = &device.shared.context.lock();
1350        let renderbuffer = unsafe { gl.create_renderbuffer() }.map_err(|error| {
1351            log::error!("Internal swapchain renderbuffer creation failed: {error}");
1352            crate::DeviceError::OutOfMemory
1353        })?;
1354        unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, Some(renderbuffer)) };
1355        unsafe {
1356            gl.renderbuffer_storage(
1357                glow::RENDERBUFFER,
1358                format_desc.internal,
1359                config.extent.width as _,
1360                config.extent.height as _,
1361            )
1362        };
1363        let framebuffer = unsafe { gl.create_framebuffer() }.map_err(|error| {
1364            log::error!("Internal swapchain framebuffer creation failed: {error}");
1365            crate::DeviceError::OutOfMemory
1366        })?;
1367        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(framebuffer)) };
1368        unsafe {
1369            gl.framebuffer_renderbuffer(
1370                glow::READ_FRAMEBUFFER,
1371                glow::COLOR_ATTACHMENT0,
1372                glow::RENDERBUFFER,
1373                Some(renderbuffer),
1374            )
1375        };
1376        unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, None) };
1377        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
1378
1379        let mut swapchain = self.swapchain.write();
1380        *swapchain = Some(Swapchain {
1381            surface,
1382            wl_window,
1383            renderbuffer,
1384            framebuffer,
1385            extent: config.extent,
1386            format: config.format,
1387            format_desc,
1388            sample_type: wgt::TextureSampleType::Float { filterable: false },
1389        });
1390
1391        Ok(())
1392    }
1393
1394    unsafe fn unconfigure(&self, device: &super::Device) {
1395        if let Some((surface, wl_window)) = unsafe { self.unconfigure_impl(device) } {
1396            self.egl
1397                .instance
1398                .destroy_surface(self.egl.display, surface)
1399                .unwrap();
1400            if let Some(window) = wl_window {
1401                wayland_sys::ffi_dispatch!(
1402                    wayland_sys::egl::wayland_egl_handle(),
1403                    wl_egl_window_destroy,
1404                    window,
1405                );
1406            }
1407        }
1408    }
1409
1410    unsafe fn acquire_texture(
1411        &self,
1412        _timeout_ms: Option<Duration>, //TODO
1413        _fence: &super::Fence,
1414    ) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
1415        let swapchain = self.swapchain.read();
1416        let sc = swapchain.as_ref().ok_or(crate::SurfaceError::Other(
1417            "Surface has no swap-chain configured",
1418        ))?;
1419        let texture = super::Texture {
1420            inner: super::TextureInner::Renderbuffer {
1421                raw: sc.renderbuffer,
1422            },
1423            drop_guard: None,
1424            array_layer_count: 1,
1425            mip_level_count: 1,
1426            format: sc.format,
1427            format_desc: sc.format_desc.clone(),
1428            copy_size: crate::CopyExtent {
1429                width: sc.extent.width,
1430                height: sc.extent.height,
1431                depth: 1,
1432            },
1433        };
1434        Ok(Some(crate::AcquiredSurfaceTexture {
1435            texture,
1436            suboptimal: false,
1437        }))
1438    }
1439    unsafe fn discard_texture(&self, _texture: super::Texture) {}
1440}