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