wgpu_hal/gles/
egl.rs

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