li_wgpu_hal/gles/
egl.rs

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