wgpu_hal/gles/
egl.rs

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