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