wgpu_hal/gles/
egl.rs

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