wgpu_hal/gles/
egl.rs

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