wgpu_hal/gles/
egl.rs

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