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.
196pub struct AdapterContext {
197    glow: Mutex<ManuallyDrop<glow::Context>>,
198    egl: Option<EglContext>,
199}
200
201unsafe impl Sync for AdapterContext {}
202unsafe impl Send for AdapterContext {}
203
204impl AdapterContext {
205    pub fn is_owned(&self) -> bool {
206        self.egl.is_some()
207    }
208
209    /// Returns the EGL instance.
210    ///
211    /// This provides access to EGL functions and the ability to load GL and EGL extension functions.
212    pub fn egl_instance(&self) -> Option<&EglInstance> {
213        self.egl.as_ref().map(|egl| &*egl.instance)
214    }
215
216    /// Returns the EGLDisplay corresponding to the adapter context.
217    ///
218    /// Returns [`None`] if the adapter was externally created.
219    pub fn raw_display(&self) -> Option<&khronos_egl::Display> {
220        self.egl.as_ref().map(|egl| &egl.display)
221    }
222
223    /// Returns the EGL version the adapter context was created with.
224    ///
225    /// Returns [`None`] if the adapter was externally created.
226    pub fn egl_version(&self) -> Option<(i32, i32)> {
227        self.egl.as_ref().map(|egl| egl.version)
228    }
229
230    pub fn raw_context(&self) -> *mut ffi::c_void {
231        match self.egl {
232            Some(ref egl) => egl.raw.as_ptr(),
233            None => ptr::null_mut(),
234        }
235    }
236}
237
238impl Drop for AdapterContext {
239    fn drop(&mut self) {
240        struct CurrentGuard<'a>(&'a EglContext);
241        impl Drop for CurrentGuard<'_> {
242            fn drop(&mut self) {
243                self.0.unmake_current();
244            }
245        }
246
247        // Context must be current when dropped. See safety docs on
248        // `glow::HasContext`.
249        //
250        // NOTE: This is only set to `None` by `Adapter::new_external` which
251        // requires the context to be current when anything that may be holding
252        // the `Arc<AdapterShared>` is dropped.
253        let _guard = self.egl.as_ref().map(|egl| {
254            egl.make_current();
255            CurrentGuard(egl)
256        });
257        let glow = self.glow.get_mut();
258        // SAFETY: Field not used after this.
259        unsafe { ManuallyDrop::drop(glow) };
260    }
261}
262
263struct EglContextLock<'a> {
264    instance: &'a Arc<EglInstance>,
265    display: khronos_egl::Display,
266}
267
268/// A guard containing a lock to an [`AdapterContext`], while the GL context is kept current.
269pub struct AdapterContextLock<'a> {
270    glow: MutexGuard<'a, ManuallyDrop<glow::Context>>,
271    egl: Option<EglContextLock<'a>>,
272}
273
274impl<'a> core::ops::Deref for AdapterContextLock<'a> {
275    type Target = glow::Context;
276
277    fn deref(&self) -> &Self::Target {
278        &self.glow
279    }
280}
281
282impl<'a> Drop for AdapterContextLock<'a> {
283    fn drop(&mut self) {
284        if let Some(egl) = self.egl.take() {
285            if let Err(err) = egl.instance.make_current(egl.display, None, None, None) {
286                log::error!("Failed to make EGL context current: {err:?}");
287            }
288        }
289    }
290}
291
292impl AdapterContext {
293    /// Get's the [`glow::Context`] without waiting for a lock
294    ///
295    /// # Safety
296    ///
297    /// This should only be called when you have manually made sure that the current thread has made
298    /// the EGL context current and that no other thread also has the EGL context current.
299    /// Additionally, you must manually make the EGL context **not** current after you are done with
300    /// it, so that future calls to `lock()` will not fail.
301    ///
302    /// > **Note:** Calling this function **will** still lock the [`glow::Context`] which adds an
303    /// > extra safe-guard against accidental concurrent access to the context.
304    pub unsafe fn get_without_egl_lock(&self) -> MappedMutexGuard<'_, glow::Context> {
305        let guard = self
306            .glow
307            .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
308            .expect("Could not lock adapter context. This is most-likely a deadlock.");
309        MutexGuard::map(guard, |glow| &mut **glow)
310    }
311
312    /// Obtain a lock to the EGL context and get handle to the [`glow::Context`] that can be used to
313    /// do rendering.
314    #[track_caller]
315    pub fn lock<'a>(&'a self) -> AdapterContextLock<'a> {
316        let glow = self
317            .glow
318            // Don't lock forever. If it takes longer than 1 second to get the lock we've got a
319            // deadlock and should panic to show where we got stuck
320            .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
321            .expect("Could not lock adapter context. This is most-likely a deadlock.");
322
323        let egl = self.egl.as_ref().map(|egl| {
324            egl.make_current();
325            EglContextLock {
326                instance: &egl.instance,
327                display: egl.display,
328            }
329        });
330
331        AdapterContextLock { glow, egl }
332    }
333}
334
335#[derive(Debug)]
336struct Inner {
337    /// Note: the context contains a dummy pbuffer (1x1).
338    /// Required for `eglMakeCurrent` on platforms that doesn't supports `EGL_KHR_surfaceless_context`.
339    egl: EglContext,
340    version: (i32, i32),
341    supports_native_window: bool,
342    config: khronos_egl::Config,
343    /// Method by which the framebuffer should support srgb
344    srgb_kind: SrgbFrameBufferKind,
345}
346
347// Different calls to `eglGetPlatformDisplay` may return the same `Display`, making it a global
348// state of all our `EglContext`s. This forces us to track the number of such context to prevent
349// terminating the display if it's currently used by another `EglContext`.
350static DISPLAYS_REFERENCE_COUNT: LazyLock<Mutex<HashMap<usize, usize>>> =
351    LazyLock::new(Default::default);
352
353fn initialize_display(
354    egl: &EglInstance,
355    display: khronos_egl::Display,
356) -> Result<(i32, i32), khronos_egl::Error> {
357    let mut guard = DISPLAYS_REFERENCE_COUNT.lock();
358    *guard.entry(display.as_ptr() as usize).or_default() += 1;
359
360    // We don't need to check the reference count here since according to the `eglInitialize`
361    // documentation, initializing an already initialized EGL display connection has no effect
362    // besides returning the version numbers.
363    egl.initialize(display)
364}
365
366fn terminate_display(
367    egl: &EglInstance,
368    display: khronos_egl::Display,
369) -> Result<(), khronos_egl::Error> {
370    let key = &(display.as_ptr() as usize);
371    let mut guard = DISPLAYS_REFERENCE_COUNT.lock();
372    let count_ref = guard
373        .get_mut(key)
374        .expect("Attempted to decref a display before incref was called");
375
376    if *count_ref > 1 {
377        *count_ref -= 1;
378
379        Ok(())
380    } else {
381        guard.remove(key);
382
383        egl.terminate(display)
384    }
385}
386
387fn instance_err<E: core::error::Error + Send + Sync + 'static>(
388    message: impl Into<String>,
389) -> impl FnOnce(E) -> crate::InstanceError {
390    move |e| crate::InstanceError::with_source(message.into(), e)
391}
392
393impl Inner {
394    fn create(
395        flags: wgt::InstanceFlags,
396        egl: Arc<EglInstance>,
397        display: khronos_egl::Display,
398        force_gles_minor_version: wgt::Gles3MinorVersion,
399    ) -> Result<Self, crate::InstanceError> {
400        let version = initialize_display(&egl, display)
401            .map_err(instance_err("failed to initialize EGL display connection"))?;
402        let vendor = egl
403            .query_string(Some(display), khronos_egl::VENDOR)
404            .map_err(instance_err("failed to query EGL vendor"))?;
405        let display_extensions = egl
406            .query_string(Some(display), khronos_egl::EXTENSIONS)
407            .map_err(instance_err("failed to query EGL display extensions"))?
408            .to_string_lossy();
409        log::debug!("Display vendor {vendor:?}, version {version:?}",);
410        log::debug!(
411            "Display extensions: {:#?}",
412            display_extensions.split_whitespace().collect::<Vec<_>>()
413        );
414
415        let srgb_kind = if version >= (1, 5) {
416            log::debug!("\tEGL surface: +srgb");
417            SrgbFrameBufferKind::Core
418        } else if display_extensions.contains("EGL_KHR_gl_colorspace") {
419            log::debug!("\tEGL surface: +srgb khr");
420            SrgbFrameBufferKind::Khr
421        } else {
422            log::debug!("\tEGL surface: -srgb");
423            SrgbFrameBufferKind::None
424        };
425
426        if log::max_level() >= log::LevelFilter::Trace {
427            log::trace!("Configurations:");
428            let config_count = egl
429                .get_config_count(display)
430                .map_err(instance_err("failed to get config count"))?;
431            let mut configurations = Vec::with_capacity(config_count);
432            egl.get_configs(display, &mut configurations)
433                .map_err(instance_err("failed to get configs"))?;
434            for &config in configurations.iter() {
435                log::trace!("\tCONFORMANT=0x{:X?}, RENDERABLE=0x{:X?}, NATIVE_RENDERABLE=0x{:X?}, SURFACE_TYPE=0x{:X?}, ALPHA_SIZE={:?}",
436                    egl.get_config_attrib(display, config, khronos_egl::CONFORMANT),
437                    egl.get_config_attrib(display, config, khronos_egl::RENDERABLE_TYPE),
438                    egl.get_config_attrib(display, config, khronos_egl::NATIVE_RENDERABLE),
439                    egl.get_config_attrib(display, config, khronos_egl::SURFACE_TYPE),
440                    egl.get_config_attrib(display, config, khronos_egl::ALPHA_SIZE),
441                );
442            }
443        }
444
445        let (config, supports_native_window) = choose_config(&egl, display, srgb_kind)?;
446
447        let supports_opengl = if version >= (1, 4) {
448            let client_apis = egl
449                .query_string(Some(display), khronos_egl::CLIENT_APIS)
450                .map_err(instance_err("failed to query EGL client APIs string"))?
451                .to_string_lossy();
452            client_apis
453                .split(' ')
454                .any(|client_api| client_api == "OpenGL")
455        } else {
456            false
457        };
458
459        let mut khr_context_flags = 0;
460        let supports_khr_context = display_extensions.contains("EGL_KHR_create_context");
461
462        let mut context_attributes = vec![];
463        let mut gl_context_attributes = vec![];
464        let mut gles_context_attributes = vec![];
465        gl_context_attributes.push(khronos_egl::CONTEXT_MAJOR_VERSION);
466        gl_context_attributes.push(3);
467        gl_context_attributes.push(khronos_egl::CONTEXT_MINOR_VERSION);
468        gl_context_attributes.push(3);
469        if supports_opengl && force_gles_minor_version != wgt::Gles3MinorVersion::Automatic {
470            log::warn!("Ignoring specified GLES minor version as OpenGL is used");
471        }
472        gles_context_attributes.push(khronos_egl::CONTEXT_MAJOR_VERSION);
473        gles_context_attributes.push(3); // Request GLES 3.0 or higher
474        if force_gles_minor_version != wgt::Gles3MinorVersion::Automatic {
475            gles_context_attributes.push(khronos_egl::CONTEXT_MINOR_VERSION);
476            gles_context_attributes.push(match force_gles_minor_version {
477                wgt::Gles3MinorVersion::Automatic => unreachable!(),
478                wgt::Gles3MinorVersion::Version0 => 0,
479                wgt::Gles3MinorVersion::Version1 => 1,
480                wgt::Gles3MinorVersion::Version2 => 2,
481            });
482        }
483        if flags.contains(wgt::InstanceFlags::DEBUG) {
484            if version >= (1, 5) {
485                log::debug!("\tEGL context: +debug");
486                context_attributes.push(khronos_egl::CONTEXT_OPENGL_DEBUG);
487                context_attributes.push(khronos_egl::TRUE as _);
488            } else if supports_khr_context {
489                log::debug!("\tEGL context: +debug KHR");
490                khr_context_flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
491            } else {
492                log::debug!("\tEGL context: -debug");
493            }
494        }
495
496        if khr_context_flags != 0 {
497            context_attributes.push(EGL_CONTEXT_FLAGS_KHR);
498            context_attributes.push(khr_context_flags);
499        }
500
501        gl_context_attributes.extend(&context_attributes);
502        gles_context_attributes.extend(&context_attributes);
503
504        let context = {
505            #[derive(Copy, Clone)]
506            enum Robustness {
507                Core,
508                Ext,
509            }
510
511            let robustness = if version >= (1, 5) {
512                Some(Robustness::Core)
513            } else if display_extensions.contains("EGL_EXT_create_context_robustness") {
514                Some(Robustness::Ext)
515            } else {
516                None
517            };
518
519            let create_context = |api, base_attributes: &[khronos_egl::Int]| {
520                egl.bind_api(api)?;
521
522                let mut robustness = robustness;
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 context_attributes = base_attributes.to_vec();
543                    context_attributes.extend(&robustness_attributes);
544
545                    match egl.create_context(display, config, None, &context_attributes) {
546                        Ok(context) => {
547                            match robustness {
548                                Some(Robustness::Core) => {
549                                    log::debug!("\tEGL context: +robust access");
550                                }
551                                Some(Robustness::Ext) => {
552                                    log::debug!("\tEGL context: +robust access EXT");
553                                }
554                                None => {
555                                    log::debug!("\tEGL context: -robust access");
556                                }
557                            }
558                            return Ok(context);
559                        }
560
561                        // Robust access context creation can fail with different error codes
562                        // depending on the EGL path. Retry with a lower robustness level.
563                        Err(
564                            khronos_egl::Error::BadAttribute
565                            | khronos_egl::Error::BadMatch
566                            | khronos_egl::Error::BadConfig,
567                        ) if robustness.is_some() => {
568                            robustness = match robustness {
569                                Some(Robustness::Core)
570                                    if display_extensions
571                                        .contains("EGL_EXT_create_context_robustness") =>
572                                {
573                                    Some(Robustness::Ext)
574                                }
575                                _ => None,
576                            };
577                            continue;
578                        }
579
580                        Err(e) => return Err(e),
581                    }
582                }
583            };
584
585            let result = if supports_opengl {
586                create_context(khronos_egl::OPENGL_API, &gl_context_attributes).or_else(
587                    |gl_error| {
588                        log::debug!("Failed to create desktop OpenGL context: {gl_error}, falling back to OpenGL ES");
589                        create_context(khronos_egl::OPENGL_ES_API, &gles_context_attributes)
590                    },
591                )
592            } else {
593                create_context(khronos_egl::OPENGL_ES_API, &gles_context_attributes)
594            };
595
596            result.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))
818                if client_ext_str.contains("EGL_EXT_platform_xcb") =>
819            {
820                log::debug!("Using XCB platform");
821                let display_attributes = [
822                    EGL_PLATFORM_XCB_SCREEN_EXT as khronos_egl::Attrib,
823                    xcb_display_handle.screen as khronos_egl::Attrib,
824                    khronos_egl::ATTRIB_NONE,
825                ];
826                let display = unsafe {
827                    egl.get_platform_display(
828                        EGL_PLATFORM_XCB_EXT,
829                        xcb_display_handle
830                            .connection
831                            .map_or(khronos_egl::DEFAULT_DISPLAY, ptr::NonNull::as_ptr),
832                        &display_attributes,
833                    )
834                }
835                .map_err(instance_err("failed to get XCB display"))?;
836                (display, WindowKind::X11)
837            }
838            x if client_ext_str.contains("EGL_MESA_platform_surfaceless") => {
839                log::debug!(
840                    "No (or unknown) windowing system ({x:?}) present. Using surfaceless platform"
841                );
842                #[allow(
843                    clippy::unnecessary_literal_unwrap,
844                    reason = "this is only a literal on Emscripten"
845                )]
846                // 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
847                let egl = egl1_5.expect("Failed to get EGL 1.5 for surfaceless");
848                let display = unsafe {
849                    egl.get_platform_display(
850                        EGL_PLATFORM_SURFACELESS_MESA,
851                        khronos_egl::DEFAULT_DISPLAY,
852                        &[khronos_egl::ATTRIB_NONE],
853                    )
854                }
855                .map_err(instance_err("failed to get MESA surfaceless display"))?;
856                (display, WindowKind::Unknown)
857            }
858            x => {
859                log::debug!(
860                    "No (or unknown) windowing system {x:?} and EGL_MESA_platform_surfaceless not available. Using default platform"
861                );
862                let display =
863                    unsafe { egl.get_display(khronos_egl::DEFAULT_DISPLAY) }.ok_or_else(|| {
864                        crate::InstanceError::new("Failed to get default display".into())
865                    })?;
866                (display, WindowKind::Unknown)
867            }
868        };
869
870        if desc.flags.contains(wgt::InstanceFlags::VALIDATION)
871            && client_ext_str.contains("EGL_KHR_debug")
872        {
873            log::debug!("Enabling EGL debug output");
874            let function: EglDebugMessageControlFun = {
875                let addr = egl
876                    .get_proc_address("eglDebugMessageControlKHR")
877                    .ok_or_else(|| {
878                        crate::InstanceError::new(
879                            "failed to get `eglDebugMessageControlKHR` proc address".into(),
880                        )
881                    })?;
882                unsafe { core::mem::transmute(addr) }
883            };
884            let attributes = [
885                EGL_DEBUG_MSG_CRITICAL_KHR as khronos_egl::Attrib,
886                1,
887                EGL_DEBUG_MSG_ERROR_KHR as khronos_egl::Attrib,
888                1,
889                EGL_DEBUG_MSG_WARN_KHR as khronos_egl::Attrib,
890                1,
891                EGL_DEBUG_MSG_INFO_KHR as khronos_egl::Attrib,
892                1,
893                khronos_egl::ATTRIB_NONE,
894            ];
895            unsafe { (function)(Some(egl_debug_proc), attributes.as_ptr()) };
896        }
897
898        let inner = Inner::create(
899            desc.flags,
900            egl,
901            display,
902            desc.backend_options.gl.gles_minor_version,
903        )?;
904
905        Ok(Instance {
906            wsi: WindowSystemInterface { kind: wsi_kind },
907            flags: desc.flags,
908            options: desc.backend_options.gl.clone(),
909            inner: Mutex::new(inner),
910        })
911    }
912
913    unsafe fn create_surface(
914        &self,
915        display_handle: raw_window_handle::RawDisplayHandle,
916        window_handle: raw_window_handle::RawWindowHandle,
917    ) -> Result<Surface, crate::InstanceError> {
918        use raw_window_handle::RawWindowHandle as Rwh;
919
920        let inner = self.inner.lock();
921
922        match (window_handle, display_handle) {
923            (Rwh::Xlib(_), _) => {}
924            (Rwh::Xcb(_), _) => {}
925            (Rwh::Win32(_), _) => {}
926            (Rwh::AppKit(_), _) => {}
927            (Rwh::OhosNdk(_), _) => {}
928            #[cfg(target_os = "android")]
929            (Rwh::AndroidNdk(handle), _) => {
930                let format = inner
931                    .egl
932                    .instance
933                    .get_config_attrib(
934                        inner.egl.display,
935                        inner.config,
936                        khronos_egl::NATIVE_VISUAL_ID,
937                    )
938                    .map_err(instance_err("failed to get config NATIVE_VISUAL_ID"))?;
939
940                let ret = unsafe {
941                    ndk_sys::ANativeWindow_setBuffersGeometry(
942                        handle
943                            .a_native_window
944                            .as_ptr()
945                            .cast::<ndk_sys::ANativeWindow>(),
946                        0,
947                        0,
948                        format,
949                    )
950                };
951
952                if ret != 0 {
953                    return Err(crate::InstanceError::new(format!(
954                        "error {ret} returned from ANativeWindow_setBuffersGeometry",
955                    )));
956                }
957            }
958            (Rwh::Wayland(_), _) => {}
959            #[cfg(Emscripten)]
960            (Rwh::Web(_), _) => {}
961            other => {
962                return Err(crate::InstanceError::new(format!(
963                    "unsupported window: {other:?}"
964                )));
965            }
966        };
967
968        inner.egl.unmake_current();
969
970        Ok(Surface {
971            egl: inner.egl.clone(),
972            wsi: self.wsi.clone(),
973            config: inner.config,
974            presentable: inner.supports_native_window,
975            raw_window_handle: window_handle,
976            swapchain: RwLock::new(None),
977            srgb_kind: inner.srgb_kind,
978        })
979    }
980
981    unsafe fn enumerate_adapters(
982        &self,
983        _surface_hint: Option<&Surface>,
984    ) -> Vec<crate::ExposedAdapter<super::Api>> {
985        let inner = self.inner.lock();
986        inner.egl.make_current();
987
988        let mut gl = unsafe {
989            glow::Context::from_loader_function(|name| {
990                inner
991                    .egl
992                    .instance
993                    .get_proc_address(name)
994                    .map_or(ptr::null(), |p| p as *const _)
995            })
996        };
997
998        // In contrast to OpenGL ES, OpenGL requires explicitly enabling sRGB conversions,
999        // as otherwise the user has to do the sRGB conversion.
1000        if !matches!(inner.srgb_kind, SrgbFrameBufferKind::None) {
1001            unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) };
1002        }
1003
1004        if self.flags.contains(wgt::InstanceFlags::DEBUG) && gl.supports_debug() {
1005            log::debug!("Max label length: {}", unsafe {
1006                gl.get_parameter_i32(glow::MAX_LABEL_LENGTH)
1007            });
1008        }
1009
1010        if self.flags.contains(wgt::InstanceFlags::VALIDATION) && gl.supports_debug() {
1011            log::debug!("Enabling GLES debug output");
1012            unsafe { gl.enable(glow::DEBUG_OUTPUT) };
1013            unsafe { gl.debug_message_callback(super::gl_debug_message_callback) };
1014        }
1015
1016        // Wrap in ManuallyDrop to make it easier to "current" the GL context before dropping this
1017        // GLOW context, which could also happen if a panic occurs after we uncurrent the context
1018        // below but before AdapterContext is constructed.
1019        let gl = ManuallyDrop::new(gl);
1020        inner.egl.unmake_current();
1021
1022        unsafe {
1023            super::Adapter::expose(
1024                AdapterContext {
1025                    glow: Mutex::new(gl),
1026                    // ERROR: Copying owned reference handles here, be careful to not drop them!
1027                    egl: Some(inner.egl.clone()),
1028                },
1029                self.options.clone(),
1030            )
1031        }
1032        .into_iter()
1033        .collect()
1034    }
1035}
1036
1037impl super::Adapter {
1038    /// Creates a new external adapter using the specified loader function.
1039    ///
1040    /// # Safety
1041    ///
1042    /// - The underlying OpenGL ES context must be current.
1043    /// - The underlying OpenGL ES context must be current when interfacing with any objects returned by
1044    ///   wgpu-hal from this adapter.
1045    /// - The underlying OpenGL ES context must be current when dropping this adapter and when
1046    ///   dropping any objects returned from this adapter.
1047    pub unsafe fn new_external(
1048        fun: impl FnMut(&str) -> *const ffi::c_void,
1049        options: wgt::GlBackendOptions,
1050    ) -> Option<crate::ExposedAdapter<super::Api>> {
1051        let context = unsafe { glow::Context::from_loader_function(fun) };
1052        unsafe {
1053            Self::expose(
1054                AdapterContext {
1055                    glow: Mutex::new(ManuallyDrop::new(context)),
1056                    egl: None,
1057                },
1058                options,
1059            )
1060        }
1061    }
1062
1063    pub fn adapter_context(&self) -> &AdapterContext {
1064        &self.shared.context
1065    }
1066}
1067
1068impl super::Device {
1069    /// Returns the underlying EGL context.
1070    pub fn context(&self) -> &AdapterContext {
1071        &self.shared.context
1072    }
1073}
1074
1075#[derive(Debug)]
1076pub struct Swapchain {
1077    surface: khronos_egl::Surface,
1078    wl_window: Option<*mut wayland_sys::egl::wl_egl_window>,
1079    framebuffer: glow::Framebuffer,
1080    renderbuffer: glow::Renderbuffer,
1081    /// Extent because the window lies
1082    extent: wgt::Extent3d,
1083    format: wgt::TextureFormat,
1084    format_desc: super::TextureFormatDesc,
1085    #[allow(unused)]
1086    sample_type: wgt::TextureSampleType,
1087}
1088
1089#[derive(Debug)]
1090pub struct Surface {
1091    egl: EglContext,
1092    wsi: WindowSystemInterface,
1093    config: khronos_egl::Config,
1094    pub(super) presentable: bool,
1095    raw_window_handle: raw_window_handle::RawWindowHandle,
1096    swapchain: RwLock<Option<Swapchain>>,
1097    srgb_kind: SrgbFrameBufferKind,
1098}
1099
1100unsafe impl Send for Surface {}
1101unsafe impl Sync for Surface {}
1102
1103impl Surface {
1104    pub(super) unsafe fn present(
1105        &self,
1106        _suf_texture: super::Texture,
1107        context: &AdapterContext,
1108    ) -> Result<(), crate::SurfaceError> {
1109        let gl = unsafe { context.get_without_egl_lock() };
1110        let swapchain = self.swapchain.read();
1111        let sc = swapchain.as_ref().ok_or(crate::SurfaceError::Other(
1112            "Surface has no swap-chain configured",
1113        ))?;
1114
1115        self.egl
1116            .instance
1117            .make_current(
1118                self.egl.display,
1119                Some(sc.surface),
1120                Some(sc.surface),
1121                Some(self.egl.raw),
1122            )
1123            .map_err(|e| {
1124                log::error!("make_current(surface) failed: {e}");
1125                crate::SurfaceError::Lost
1126            })?;
1127
1128        unsafe { gl.disable(glow::SCISSOR_TEST) };
1129        unsafe { gl.color_mask(true, true, true, true) };
1130
1131        unsafe { gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None) };
1132        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(sc.framebuffer)) };
1133
1134        if !matches!(self.srgb_kind, SrgbFrameBufferKind::None) {
1135            // Disable sRGB conversions for `glBlitFramebuffer` as behavior does diverge between
1136            // drivers and formats otherwise and we want to ensure no sRGB conversions happen.
1137            unsafe { gl.disable(glow::FRAMEBUFFER_SRGB) };
1138        }
1139
1140        // Note the Y-flipping here. GL's presentation is not flipped,
1141        // but main rendering is. Therefore, we Y-flip the output positions
1142        // in the shader, and also this blit.
1143        unsafe {
1144            gl.blit_framebuffer(
1145                0,
1146                sc.extent.height as i32,
1147                sc.extent.width as i32,
1148                0,
1149                0,
1150                0,
1151                sc.extent.width as i32,
1152                sc.extent.height as i32,
1153                glow::COLOR_BUFFER_BIT,
1154                glow::NEAREST,
1155            )
1156        };
1157
1158        if !matches!(self.srgb_kind, SrgbFrameBufferKind::None) {
1159            unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) };
1160        }
1161
1162        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
1163
1164        self.egl
1165            .instance
1166            .swap_buffers(self.egl.display, sc.surface)
1167            .map_err(|e| {
1168                log::error!("swap_buffers failed: {e}");
1169                crate::SurfaceError::Lost
1170                // TODO: should we unset the current context here?
1171            })?;
1172        self.egl
1173            .instance
1174            .make_current(self.egl.display, None, None, None)
1175            .map_err(|e| {
1176                log::error!("make_current(null) failed: {e}");
1177                crate::SurfaceError::Lost
1178            })?;
1179
1180        Ok(())
1181    }
1182
1183    unsafe fn unconfigure_impl(
1184        &self,
1185        device: &super::Device,
1186    ) -> Option<(
1187        khronos_egl::Surface,
1188        Option<*mut wayland_sys::egl::wl_egl_window>,
1189    )> {
1190        let gl = &device.shared.context.lock();
1191        match self.swapchain.write().take() {
1192            Some(sc) => {
1193                unsafe { gl.delete_renderbuffer(sc.renderbuffer) };
1194                unsafe { gl.delete_framebuffer(sc.framebuffer) };
1195                Some((sc.surface, sc.wl_window))
1196            }
1197            None => None,
1198        }
1199    }
1200
1201    pub fn supports_srgb(&self) -> bool {
1202        match self.srgb_kind {
1203            SrgbFrameBufferKind::None => false,
1204            _ => true,
1205        }
1206    }
1207}
1208
1209impl crate::Surface for Surface {
1210    type A = super::Api;
1211
1212    unsafe fn configure(
1213        &self,
1214        device: &super::Device,
1215        config: &crate::SurfaceConfiguration,
1216    ) -> Result<(), crate::SurfaceError> {
1217        use raw_window_handle::RawWindowHandle as Rwh;
1218
1219        let (surface, wl_window) = match unsafe { self.unconfigure_impl(device) } {
1220            Some((sc, wl_window)) => {
1221                if let Some(window) = wl_window {
1222                    wayland_sys::ffi_dispatch!(
1223                        wayland_sys::egl::wayland_egl_handle(),
1224                        wl_egl_window_resize,
1225                        window,
1226                        config.extent.width as i32,
1227                        config.extent.height as i32,
1228                        0,
1229                        0,
1230                    );
1231                }
1232
1233                (sc, wl_window)
1234            }
1235            None => {
1236                let mut wl_window = None;
1237                let (mut temp_xlib_handle, mut temp_xcb_handle);
1238                let native_window_ptr = match (self.wsi.kind, self.raw_window_handle) {
1239                    (WindowKind::Unknown | WindowKind::X11, Rwh::Xlib(handle)) => {
1240                        temp_xlib_handle = handle.window;
1241                        ptr::from_mut(&mut temp_xlib_handle).cast::<ffi::c_void>()
1242                    }
1243                    (WindowKind::AngleX11, Rwh::Xlib(handle)) => handle.window as *mut ffi::c_void,
1244                    (WindowKind::Unknown | WindowKind::X11, Rwh::Xcb(handle)) => {
1245                        temp_xcb_handle = handle.window;
1246                        ptr::from_mut(&mut temp_xcb_handle).cast::<ffi::c_void>()
1247                    }
1248                    (WindowKind::AngleX11, Rwh::Xcb(handle)) => {
1249                        handle.window.get() as *mut ffi::c_void
1250                    }
1251                    (WindowKind::Unknown, Rwh::AndroidNdk(handle)) => {
1252                        handle.a_native_window.as_ptr()
1253                    }
1254                    (WindowKind::Unknown, Rwh::OhosNdk(handle)) => handle.native_window.as_ptr(),
1255                    #[cfg(unix)]
1256                    (WindowKind::Wayland, Rwh::Wayland(handle)) => {
1257                        let window = wayland_sys::ffi_dispatch!(
1258                            wayland_sys::egl::wayland_egl_handle(),
1259                            wl_egl_window_create,
1260                            handle.surface.as_ptr().cast(),
1261                            config.extent.width as i32,
1262                            config.extent.height as i32,
1263                        );
1264                        wl_window = Some(window);
1265                        window.cast()
1266                    }
1267                    #[cfg(Emscripten)]
1268                    (WindowKind::Unknown, Rwh::Web(handle)) => handle.id as *mut ffi::c_void,
1269                    (WindowKind::Unknown, Rwh::Win32(handle)) => {
1270                        handle.hwnd.get() as *mut ffi::c_void
1271                    }
1272                    (WindowKind::Unknown, Rwh::AppKit(handle)) => {
1273                        #[cfg(not(target_os = "macos"))]
1274                        let window_ptr = handle.ns_view.as_ptr();
1275                        #[cfg(target_os = "macos")]
1276                        let window_ptr = {
1277                            use objc2::msg_send;
1278                            use objc2::runtime::AnyObject;
1279                            // ns_view always have a layer and don't need to verify that it exists.
1280                            let layer: *mut AnyObject =
1281                                msg_send![handle.ns_view.as_ptr().cast::<AnyObject>(), layer];
1282                            layer.cast::<ffi::c_void>()
1283                        };
1284                        window_ptr
1285                    }
1286                    _ => {
1287                        log::warn!(
1288                            "Initialized platform {:?} doesn't work with window {:?}",
1289                            self.wsi.kind,
1290                            self.raw_window_handle
1291                        );
1292                        return Err(crate::SurfaceError::Other("incompatible window kind"));
1293                    }
1294                };
1295
1296                let mut attributes = vec![
1297                    khronos_egl::RENDER_BUFFER,
1298                    // We don't want any of the buffering done by the driver, because we
1299                    // manage a swapchain on our side.
1300                    // Some drivers just fail on surface creation seeing `EGL_SINGLE_BUFFER`.
1301                    if cfg!(any(
1302                        target_os = "android",
1303                        target_os = "macos",
1304                        target_env = "ohos"
1305                    )) || cfg!(windows)
1306                        || self.wsi.kind == WindowKind::AngleX11
1307                    {
1308                        khronos_egl::BACK_BUFFER
1309                    } else {
1310                        khronos_egl::SINGLE_BUFFER
1311                    },
1312                ];
1313                if config.format.is_srgb() {
1314                    match self.srgb_kind {
1315                        SrgbFrameBufferKind::None => {}
1316                        SrgbFrameBufferKind::Core => {
1317                            attributes.push(khronos_egl::GL_COLORSPACE);
1318                            attributes.push(khronos_egl::GL_COLORSPACE_SRGB);
1319                        }
1320                        SrgbFrameBufferKind::Khr => {
1321                            attributes.push(EGL_GL_COLORSPACE_KHR as i32);
1322                            attributes.push(EGL_GL_COLORSPACE_SRGB_KHR as i32);
1323                        }
1324                    }
1325                }
1326                attributes.push(khronos_egl::ATTRIB_NONE as i32);
1327
1328                #[cfg(not(Emscripten))]
1329                let egl1_5 = self.egl.instance.upcast::<khronos_egl::EGL1_5>();
1330
1331                #[cfg(Emscripten)]
1332                let egl1_5: Option<&Arc<EglInstance>> = Some(&self.egl.instance);
1333
1334                // Careful, we can still be in 1.4 version even if `upcast` succeeds
1335                let raw_result = match egl1_5 {
1336                    Some(egl) if self.wsi.kind != WindowKind::Unknown => {
1337                        let attributes_usize = attributes
1338                            .into_iter()
1339                            .map(|v| v as usize)
1340                            .collect::<Vec<_>>();
1341                        unsafe {
1342                            egl.create_platform_window_surface(
1343                                self.egl.display,
1344                                self.config,
1345                                native_window_ptr,
1346                                &attributes_usize,
1347                            )
1348                        }
1349                    }
1350                    _ => unsafe {
1351                        self.egl.instance.create_window_surface(
1352                            self.egl.display,
1353                            self.config,
1354                            native_window_ptr,
1355                            Some(&attributes),
1356                        )
1357                    },
1358                };
1359
1360                match raw_result {
1361                    Ok(raw) => (raw, wl_window),
1362                    Err(e) => {
1363                        log::warn!("Error in create_window_surface: {e:?}");
1364                        return Err(crate::SurfaceError::Lost);
1365                    }
1366                }
1367            }
1368        };
1369
1370        let format_desc = device.shared.describe_texture_format(config.format);
1371        let gl = &device.shared.context.lock();
1372        let renderbuffer = unsafe { gl.create_renderbuffer() }.map_err(|error| {
1373            log::error!("Internal swapchain renderbuffer creation failed: {error}");
1374            crate::DeviceError::OutOfMemory
1375        })?;
1376        unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, Some(renderbuffer)) };
1377        unsafe {
1378            gl.renderbuffer_storage(
1379                glow::RENDERBUFFER,
1380                format_desc.internal,
1381                config.extent.width as _,
1382                config.extent.height as _,
1383            )
1384        };
1385        let framebuffer = unsafe { gl.create_framebuffer() }.map_err(|error| {
1386            log::error!("Internal swapchain framebuffer creation failed: {error}");
1387            crate::DeviceError::OutOfMemory
1388        })?;
1389        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(framebuffer)) };
1390        unsafe {
1391            gl.framebuffer_renderbuffer(
1392                glow::READ_FRAMEBUFFER,
1393                glow::COLOR_ATTACHMENT0,
1394                glow::RENDERBUFFER,
1395                Some(renderbuffer),
1396            )
1397        };
1398        unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, None) };
1399        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
1400
1401        let mut swapchain = self.swapchain.write();
1402        *swapchain = Some(Swapchain {
1403            surface,
1404            wl_window,
1405            renderbuffer,
1406            framebuffer,
1407            extent: config.extent,
1408            format: config.format,
1409            format_desc,
1410            sample_type: wgt::TextureSampleType::Float { filterable: false },
1411        });
1412
1413        Ok(())
1414    }
1415
1416    unsafe fn unconfigure(&self, device: &super::Device) {
1417        if let Some((surface, wl_window)) = unsafe { self.unconfigure_impl(device) } {
1418            self.egl
1419                .instance
1420                .destroy_surface(self.egl.display, surface)
1421                .unwrap();
1422            if let Some(window) = wl_window {
1423                wayland_sys::ffi_dispatch!(
1424                    wayland_sys::egl::wayland_egl_handle(),
1425                    wl_egl_window_destroy,
1426                    window,
1427                );
1428            }
1429        }
1430    }
1431
1432    unsafe fn acquire_texture(
1433        &self,
1434        _timeout_ms: Option<Duration>, //TODO
1435        _fence: &super::Fence,
1436    ) -> Result<crate::AcquiredSurfaceTexture<super::Api>, crate::SurfaceError> {
1437        let swapchain = self.swapchain.read();
1438        let sc = swapchain.as_ref().ok_or(crate::SurfaceError::Other(
1439            "Surface has no swap-chain configured",
1440        ))?;
1441        let texture = super::Texture {
1442            inner: super::TextureInner::Renderbuffer {
1443                raw: sc.renderbuffer,
1444            },
1445            drop_guard: None,
1446            array_layer_count: 1,
1447            mip_level_count: 1,
1448            format: sc.format,
1449            format_desc: sc.format_desc.clone(),
1450            copy_size: crate::CopyExtent {
1451                width: sc.extent.width,
1452                height: sc.extent.height,
1453                depth: 1,
1454            },
1455        };
1456        Ok(crate::AcquiredSurfaceTexture {
1457            texture,
1458            suboptimal: false,
1459        })
1460    }
1461    unsafe fn discard_texture(&self, _texture: super::Texture) {}
1462}