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