wgpu_hal/gles/
egl.rs

1use glow::HasContext;
2use parking_lot::{Mutex, MutexGuard};
3
4use std::{ffi, os::raw, ptr, 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    Err(crate::InstanceError)
287}
288
289fn gl_debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, message: &str) {
290    let source_str = match source {
291        glow::DEBUG_SOURCE_API => "API",
292        glow::DEBUG_SOURCE_WINDOW_SYSTEM => "Window System",
293        glow::DEBUG_SOURCE_SHADER_COMPILER => "ShaderCompiler",
294        glow::DEBUG_SOURCE_THIRD_PARTY => "Third Party",
295        glow::DEBUG_SOURCE_APPLICATION => "Application",
296        glow::DEBUG_SOURCE_OTHER => "Other",
297        _ => unreachable!(),
298    };
299
300    let log_severity = match severity {
301        glow::DEBUG_SEVERITY_HIGH => log::Level::Error,
302        glow::DEBUG_SEVERITY_MEDIUM => log::Level::Warn,
303        glow::DEBUG_SEVERITY_LOW => log::Level::Info,
304        glow::DEBUG_SEVERITY_NOTIFICATION => log::Level::Trace,
305        _ => unreachable!(),
306    };
307
308    let type_str = match gltype {
309        glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR => "Deprecated Behavior",
310        glow::DEBUG_TYPE_ERROR => "Error",
311        glow::DEBUG_TYPE_MARKER => "Marker",
312        glow::DEBUG_TYPE_OTHER => "Other",
313        glow::DEBUG_TYPE_PERFORMANCE => "Performance",
314        glow::DEBUG_TYPE_POP_GROUP => "Pop Group",
315        glow::DEBUG_TYPE_PORTABILITY => "Portability",
316        glow::DEBUG_TYPE_PUSH_GROUP => "Push Group",
317        glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR => "Undefined Behavior",
318        _ => unreachable!(),
319    };
320
321    let _ = std::panic::catch_unwind(|| {
322        log::log!(
323            log_severity,
324            "GLES: [{}/{}] ID {} : {}",
325            source_str,
326            type_str,
327            id,
328            message
329        );
330    });
331
332    if cfg!(debug_assertions) && log_severity == log::Level::Error {
333        // Set canary and continue
334        crate::VALIDATION_CANARY.set();
335    }
336}
337
338#[derive(Clone, Debug)]
339struct EglContext {
340    instance: Arc<EglInstance>,
341    version: (i32, i32),
342    display: khronos_egl::Display,
343    raw: khronos_egl::Context,
344    pbuffer: Option<khronos_egl::Surface>,
345}
346
347impl EglContext {
348    fn make_current(&self) {
349        self.instance
350            .make_current(self.display, self.pbuffer, self.pbuffer, Some(self.raw))
351            .unwrap();
352    }
353    fn unmake_current(&self) {
354        self.instance
355            .make_current(self.display, None, None, None)
356            .unwrap();
357    }
358}
359
360/// A wrapper around a [`glow::Context`] and the required EGL context that uses locking to guarantee
361/// exclusive access when shared with multiple threads.
362pub struct AdapterContext {
363    glow: Mutex<glow::Context>,
364    egl: Option<EglContext>,
365}
366
367unsafe impl Sync for AdapterContext {}
368unsafe impl Send for AdapterContext {}
369
370impl AdapterContext {
371    pub fn is_owned(&self) -> bool {
372        self.egl.is_some()
373    }
374
375    /// Returns the EGL instance.
376    ///
377    /// This provides access to EGL functions and the ability to load GL and EGL extension functions.
378    pub fn egl_instance(&self) -> Option<&EglInstance> {
379        self.egl.as_ref().map(|egl| &*egl.instance)
380    }
381
382    /// Returns the EGLDisplay corresponding to the adapter context.
383    ///
384    /// Returns [`None`] if the adapter was externally created.
385    pub fn raw_display(&self) -> Option<&khronos_egl::Display> {
386        self.egl.as_ref().map(|egl| &egl.display)
387    }
388
389    /// Returns the EGL version the adapter context was created with.
390    ///
391    /// Returns [`None`] if the adapter was externally created.
392    pub fn egl_version(&self) -> Option<(i32, i32)> {
393        self.egl.as_ref().map(|egl| egl.version)
394    }
395
396    pub fn raw_context(&self) -> *mut raw::c_void {
397        match self.egl {
398            Some(ref egl) => egl.raw.as_ptr(),
399            None => ptr::null_mut(),
400        }
401    }
402}
403
404struct EglContextLock<'a> {
405    instance: &'a Arc<EglInstance>,
406    display: khronos_egl::Display,
407}
408
409/// A guard containing a lock to an [`AdapterContext`]
410pub struct AdapterContextLock<'a> {
411    glow: MutexGuard<'a, glow::Context>,
412    egl: Option<EglContextLock<'a>>,
413}
414
415impl<'a> std::ops::Deref for AdapterContextLock<'a> {
416    type Target = glow::Context;
417
418    fn deref(&self) -> &Self::Target {
419        &self.glow
420    }
421}
422
423impl<'a> Drop for AdapterContextLock<'a> {
424    fn drop(&mut self) {
425        if let Some(egl) = self.egl.take() {
426            egl.instance
427                .make_current(egl.display, None, None, None)
428                .unwrap();
429        }
430    }
431}
432
433impl AdapterContext {
434    /// Get's the [`glow::Context`] without waiting for a lock
435    ///
436    /// # Safety
437    ///
438    /// This should only be called when you have manually made sure that the current thread has made
439    /// the EGL context current and that no other thread also has the EGL context current.
440    /// Additionally, you must manually make the EGL context **not** current after you are done with
441    /// it, so that future calls to `lock()` will not fail.
442    ///
443    /// > **Note:** Calling this function **will** still lock the [`glow::Context`] which adds an
444    /// > extra safe-guard against accidental concurrent access to the context.
445    pub unsafe fn get_without_egl_lock(&self) -> MutexGuard<glow::Context> {
446        self.glow
447            .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
448            .expect("Could not lock adapter context. This is most-likely a deadlock.")
449    }
450
451    /// Obtain a lock to the EGL context and get handle to the [`glow::Context`] that can be used to
452    /// do rendering.
453    #[track_caller]
454    pub fn lock<'a>(&'a self) -> AdapterContextLock<'a> {
455        let glow = self
456            .glow
457            // Don't lock forever. If it takes longer than 1 second to get the lock we've got a
458            // deadlock and should panic to show where we got stuck
459            .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
460            .expect("Could not lock adapter context. This is most-likely a deadlock.");
461
462        let egl = self.egl.as_ref().map(|egl| {
463            egl.make_current();
464            EglContextLock {
465                instance: &egl.instance,
466                display: egl.display,
467            }
468        });
469
470        AdapterContextLock { glow, egl }
471    }
472}
473
474#[derive(Debug)]
475struct Inner {
476    /// Note: the context contains a dummy pbuffer (1x1).
477    /// Required for `eglMakeCurrent` on platforms that doesn't supports `EGL_KHR_surfaceless_context`.
478    egl: EglContext,
479    #[allow(unused)]
480    version: (i32, i32),
481    supports_native_window: bool,
482    config: khronos_egl::Config,
483    #[cfg_attr(target_os = "emscripten", allow(dead_code))]
484    wl_display: Option<*mut raw::c_void>,
485    /// Method by which the framebuffer should support srgb
486    srgb_kind: SrgbFrameBufferKind,
487}
488
489impl Inner {
490    fn create(
491        flags: crate::InstanceFlags,
492        egl: Arc<EglInstance>,
493        display: khronos_egl::Display,
494    ) -> Result<Self, crate::InstanceError> {
495        let version = egl.initialize(display).map_err(|_| crate::InstanceError)?;
496        let vendor = egl
497            .query_string(Some(display), khronos_egl::VENDOR)
498            .unwrap();
499        let display_extensions = egl
500            .query_string(Some(display), khronos_egl::EXTENSIONS)
501            .unwrap()
502            .to_string_lossy();
503        log::info!("Display vendor {:?}, version {:?}", vendor, version,);
504        log::debug!(
505            "Display extensions: {:#?}",
506            display_extensions.split_whitespace().collect::<Vec<_>>()
507        );
508
509        let srgb_kind = if version >= (1, 5) {
510            log::info!("\tEGL surface: +srgb");
511            SrgbFrameBufferKind::Core
512        } else if display_extensions.contains("EGL_KHR_gl_colorspace") {
513            log::info!("\tEGL surface: +srgb khr");
514            SrgbFrameBufferKind::Khr
515        } else {
516            log::warn!("\tEGL surface: -srgb");
517            SrgbFrameBufferKind::None
518        };
519
520        if log::max_level() >= log::LevelFilter::Trace {
521            log::trace!("Configurations:");
522            let config_count = egl.get_config_count(display).unwrap();
523            let mut configurations = Vec::with_capacity(config_count);
524            egl.get_configs(display, &mut configurations).unwrap();
525            for &config in configurations.iter() {
526                log::trace!("\tCONFORMANT=0x{:X}, RENDERABLE=0x{:X}, NATIVE_RENDERABLE=0x{:X}, SURFACE_TYPE=0x{:X}, ALPHA_SIZE={}",
527                    egl.get_config_attrib(display, config, khronos_egl::CONFORMANT).unwrap(),
528                    egl.get_config_attrib(display, config, khronos_egl::RENDERABLE_TYPE).unwrap(),
529                    egl.get_config_attrib(display, config, khronos_egl::NATIVE_RENDERABLE).unwrap(),
530                    egl.get_config_attrib(display, config, khronos_egl::SURFACE_TYPE).unwrap(),
531                    egl.get_config_attrib(display, config, khronos_egl::ALPHA_SIZE).unwrap(),
532                );
533            }
534        }
535
536        let (config, supports_native_window) = choose_config(&egl, display, srgb_kind)?;
537        egl.bind_api(khronos_egl::OPENGL_ES_API).unwrap();
538
539        let needs_robustness = true;
540        let mut khr_context_flags = 0;
541        let supports_khr_context = display_extensions.contains("EGL_KHR_create_context");
542
543        //TODO: make it so `Device` == EGL Context
544        let mut context_attributes = vec![
545            khronos_egl::CONTEXT_CLIENT_VERSION,
546            3, // Request GLES 3.0 or higher
547        ];
548        if flags.contains(crate::InstanceFlags::DEBUG) {
549            if version >= (1, 5) {
550                log::info!("\tEGL context: +debug");
551                context_attributes.push(khronos_egl::CONTEXT_OPENGL_DEBUG);
552                context_attributes.push(khronos_egl::TRUE as _);
553            } else if supports_khr_context {
554                log::info!("\tEGL context: +debug KHR");
555                khr_context_flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
556            } else {
557                log::info!("\tEGL context: -debug");
558            }
559        }
560        if needs_robustness {
561            //Note: the core version can fail if robustness is not supported
562            // (regardless of whether the extension is supported!).
563            // In fact, Angle does precisely that awful behavior, so we don't try it there.
564            if version >= (1, 5) && !display_extensions.contains("EGL_ANGLE_") {
565                log::info!("\tEGL context: +robust access");
566                context_attributes.push(khronos_egl::CONTEXT_OPENGL_ROBUST_ACCESS);
567                context_attributes.push(khronos_egl::TRUE as _);
568            } else if display_extensions.contains("EGL_EXT_create_context_robustness") {
569                log::info!("\tEGL context: +robust access EXT");
570                context_attributes.push(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT);
571                context_attributes.push(khronos_egl::TRUE as _);
572            } else {
573                //Note: we aren't trying `EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR`
574                // because it's for desktop GL only, not GLES.
575                log::warn!("\tEGL context: -robust access");
576            }
577
578            //TODO do we need `khronos_egl::CONTEXT_OPENGL_NOTIFICATION_STRATEGY_EXT`?
579        }
580        if khr_context_flags != 0 {
581            context_attributes.push(EGL_CONTEXT_FLAGS_KHR);
582            context_attributes.push(khr_context_flags);
583        }
584        context_attributes.push(khronos_egl::NONE);
585        let context = match egl.create_context(display, config, None, &context_attributes) {
586            Ok(context) => context,
587            Err(e) => {
588                log::warn!("unable to create GLES 3.x context: {:?}", e);
589                return Err(crate::InstanceError);
590            }
591        };
592
593        // Testing if context can be binded without surface
594        // and creating dummy pbuffer surface if not.
595        let pbuffer = if version >= (1, 5)
596            || display_extensions.contains("EGL_KHR_surfaceless_context")
597            || cfg!(target_os = "emscripten")
598        {
599            log::info!("\tEGL context: +surfaceless");
600            None
601        } else {
602            let attributes = [
603                khronos_egl::WIDTH,
604                1,
605                khronos_egl::HEIGHT,
606                1,
607                khronos_egl::NONE,
608            ];
609            egl.create_pbuffer_surface(display, config, &attributes)
610                .map(Some)
611                .map_err(|e| {
612                    log::warn!("Error in create_pbuffer_surface: {:?}", e);
613                    crate::InstanceError
614                })?
615        };
616
617        Ok(Self {
618            egl: EglContext {
619                instance: egl,
620                display,
621                raw: context,
622                pbuffer,
623                version,
624            },
625            version,
626            supports_native_window,
627            config,
628            wl_display: None,
629            srgb_kind,
630        })
631    }
632}
633
634impl Drop for Inner {
635    fn drop(&mut self) {
636        if let Err(e) = self
637            .egl
638            .instance
639            .destroy_context(self.egl.display, self.egl.raw)
640        {
641            log::warn!("Error in destroy_context: {:?}", e);
642        }
643        if let Err(e) = self.egl.instance.terminate(self.egl.display) {
644            log::warn!("Error in terminate: {:?}", e);
645        }
646    }
647}
648
649#[derive(Clone, Copy, Debug, PartialEq)]
650enum WindowKind {
651    Wayland,
652    X11,
653    AngleX11,
654    Unknown,
655}
656
657#[derive(Clone, Debug)]
658struct WindowSystemInterface {
659    display_owner: Option<Arc<DisplayOwner>>,
660    kind: WindowKind,
661}
662
663pub struct Instance {
664    wsi: WindowSystemInterface,
665    flags: crate::InstanceFlags,
666    inner: Mutex<Inner>,
667}
668
669impl Instance {
670    pub fn raw_display(&self) -> khronos_egl::Display {
671        self.inner
672            .try_lock()
673            .expect("Could not lock instance. This is most-likely a deadlock.")
674            .egl
675            .display
676    }
677
678    /// Returns the version of the EGL display.
679    pub fn egl_version(&self) -> (i32, i32) {
680        self.inner
681            .try_lock()
682            .expect("Could not lock instance. This is most-likely a deadlock.")
683            .version
684    }
685
686    pub fn egl_config(&self) -> khronos_egl::Config {
687        self.inner
688            .try_lock()
689            .expect("Could not lock instance. This is most-likely a deadlock.")
690            .config
691    }
692}
693
694unsafe impl Send for Instance {}
695unsafe impl Sync for Instance {}
696
697impl crate::Instance<super::Api> for Instance {
698    unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
699        #[cfg(target_os = "emscripten")]
700        let egl_result: Result<EglInstance, khronos_egl::Error> =
701            Ok(khronos_egl::Instance::new(khronos_egl::Static));
702
703        #[cfg(not(target_os = "emscripten"))]
704        let egl_result = if cfg!(windows) {
705            unsafe {
706                khronos_egl::DynamicInstance::<khronos_egl::EGL1_4>::load_required_from_filename(
707                    "libEGL.dll",
708                )
709            }
710        } else if cfg!(any(target_os = "macos", target_os = "ios")) {
711            unsafe {
712                khronos_egl::DynamicInstance::<khronos_egl::EGL1_4>::load_required_from_filename(
713                    "libEGL.dylib",
714                )
715            }
716        } else {
717            unsafe { khronos_egl::DynamicInstance::<khronos_egl::EGL1_4>::load_required() }
718        };
719        let egl = match egl_result {
720            Ok(egl) => Arc::new(egl),
721            Err(e) => {
722                log::info!("Unable to open libEGL: {:?}", e);
723                return Err(crate::InstanceError);
724            }
725        };
726
727        let client_extensions = egl.query_string(None, khronos_egl::EXTENSIONS);
728
729        let client_ext_str = match client_extensions {
730            Ok(ext) => ext.to_string_lossy().into_owned(),
731            Err(_) => String::new(),
732        };
733        log::debug!(
734            "Client extensions: {:#?}",
735            client_ext_str.split_whitespace().collect::<Vec<_>>()
736        );
737
738        let wayland_library = if client_ext_str.contains("EGL_EXT_platform_wayland") {
739            test_wayland_display()
740        } else {
741            None
742        };
743        let x11_display_library = if client_ext_str.contains("EGL_EXT_platform_x11") {
744            open_x_display()
745        } else {
746            None
747        };
748        let angle_x11_display_library = if client_ext_str.contains("EGL_ANGLE_platform_angle") {
749            open_x_display()
750        } else {
751            None
752        };
753
754        #[cfg(not(target_os = "emscripten"))]
755        let egl1_5 = egl.upcast::<khronos_egl::EGL1_5>();
756
757        #[cfg(target_os = "emscripten")]
758        let egl1_5: Option<&Arc<EglInstance>> = Some(&egl);
759
760        let (display, display_owner, wsi_kind) =
761            if let (Some(library), Some(egl)) = (wayland_library, egl1_5) {
762                log::info!("Using Wayland platform");
763                let display_attributes = [khronos_egl::ATTRIB_NONE];
764                let display = egl
765                    .get_platform_display(
766                        EGL_PLATFORM_WAYLAND_KHR,
767                        khronos_egl::DEFAULT_DISPLAY,
768                        &display_attributes,
769                    )
770                    .unwrap();
771                (display, Some(Arc::new(library)), WindowKind::Wayland)
772            } else if let (Some(display_owner), Some(egl)) = (x11_display_library, egl1_5) {
773                log::info!("Using X11 platform");
774                let display_attributes = [khronos_egl::ATTRIB_NONE];
775                let display = egl
776                    .get_platform_display(
777                        EGL_PLATFORM_X11_KHR,
778                        display_owner.display.as_ptr(),
779                        &display_attributes,
780                    )
781                    .unwrap();
782                (display, Some(Arc::new(display_owner)), WindowKind::X11)
783            } else if let (Some(display_owner), Some(egl)) = (angle_x11_display_library, egl1_5) {
784                log::info!("Using Angle platform with X11");
785                let display_attributes = [
786                    EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE as khronos_egl::Attrib,
787                    EGL_PLATFORM_X11_KHR as khronos_egl::Attrib,
788                    EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED as khronos_egl::Attrib,
789                    usize::from(desc.flags.contains(crate::InstanceFlags::VALIDATION)),
790                    khronos_egl::ATTRIB_NONE,
791                ];
792                let display = egl
793                    .get_platform_display(
794                        EGL_PLATFORM_ANGLE_ANGLE,
795                        display_owner.display.as_ptr(),
796                        &display_attributes,
797                    )
798                    .unwrap();
799                (display, Some(Arc::new(display_owner)), WindowKind::AngleX11)
800            } else if client_ext_str.contains("EGL_MESA_platform_surfaceless") {
801                log::info!("No windowing system present. Using surfaceless platform");
802                let egl = egl1_5.expect("Failed to get EGL 1.5 for surfaceless");
803                let display = egl
804                    .get_platform_display(
805                        EGL_PLATFORM_SURFACELESS_MESA,
806                        std::ptr::null_mut(),
807                        &[khronos_egl::ATTRIB_NONE],
808                    )
809                    .unwrap();
810                (display, None, WindowKind::Unknown)
811            } else {
812                log::info!("EGL_MESA_platform_surfaceless not available. Using default platform");
813                let display = egl.get_display(khronos_egl::DEFAULT_DISPLAY).unwrap();
814                (display, None, WindowKind::Unknown)
815            };
816
817        if desc.flags.contains(crate::InstanceFlags::VALIDATION)
818            && client_ext_str.contains("EGL_KHR_debug")
819        {
820            log::info!("Enabling EGL debug output");
821            let function: EglDebugMessageControlFun = {
822                let addr = egl.get_proc_address("eglDebugMessageControlKHR").unwrap();
823                unsafe { std::mem::transmute(addr) }
824            };
825            let attributes = [
826                EGL_DEBUG_MSG_CRITICAL_KHR as khronos_egl::Attrib,
827                1,
828                EGL_DEBUG_MSG_ERROR_KHR as khronos_egl::Attrib,
829                1,
830                EGL_DEBUG_MSG_WARN_KHR as khronos_egl::Attrib,
831                1,
832                EGL_DEBUG_MSG_INFO_KHR as khronos_egl::Attrib,
833                1,
834                khronos_egl::ATTRIB_NONE,
835            ];
836            unsafe { (function)(Some(egl_debug_proc), attributes.as_ptr()) };
837        }
838
839        let inner = Inner::create(desc.flags, egl, display)?;
840
841        Ok(Instance {
842            wsi: WindowSystemInterface {
843                display_owner,
844                kind: wsi_kind,
845            },
846            flags: desc.flags,
847            inner: Mutex::new(inner),
848        })
849    }
850
851    #[cfg_attr(target_os = "macos", allow(unused, unused_mut, unreachable_code))]
852    unsafe fn create_surface(
853        &self,
854        display_handle: raw_window_handle::RawDisplayHandle,
855        window_handle: raw_window_handle::RawWindowHandle,
856    ) -> Result<Surface, crate::InstanceError> {
857        use raw_window_handle::RawWindowHandle as Rwh;
858
859        #[cfg_attr(
860            any(target_os = "android", target_os = "emscripten"),
861            allow(unused_mut)
862        )]
863        let mut inner = self.inner.lock();
864
865        match (window_handle, display_handle) {
866            (Rwh::Xlib(_), _) => {}
867            (Rwh::Xcb(_), _) => {}
868            (Rwh::Win32(_), _) => {}
869            (Rwh::AppKit(_), _) => {}
870            #[cfg(target_os = "android")]
871            (Rwh::AndroidNdk(handle), _) => {
872                let format = inner
873                    .egl
874                    .instance
875                    .get_config_attrib(
876                        inner.egl.display,
877                        inner.config,
878                        khronos_egl::NATIVE_VISUAL_ID,
879                    )
880                    .unwrap();
881
882                let ret = unsafe {
883                    ANativeWindow_setBuffersGeometry(handle.a_native_window, 0, 0, format)
884                };
885
886                if ret != 0 {
887                    log::error!("Error returned from ANativeWindow_setBuffersGeometry");
888                    return Err(crate::InstanceError);
889                }
890            }
891            #[cfg(not(target_os = "emscripten"))]
892            (Rwh::Wayland(_), raw_window_handle::RawDisplayHandle::Wayland(display_handle)) => {
893                if inner
894                    .wl_display
895                    .map(|ptr| ptr != display_handle.display)
896                    .unwrap_or(true)
897                {
898                    /* Wayland displays are not sharable between surfaces so if the
899                     * surface we receive from this handle is from a different
900                     * display, we must re-initialize the context.
901                     *
902                     * See gfx-rs/gfx#3545
903                     */
904                    log::warn!("Re-initializing Gles context due to Wayland window");
905
906                    use std::ops::DerefMut;
907                    let display_attributes = [khronos_egl::ATTRIB_NONE];
908
909                    let display = inner
910                        .egl
911                        .instance
912                        .upcast::<khronos_egl::EGL1_5>()
913                        .unwrap()
914                        .get_platform_display(
915                            EGL_PLATFORM_WAYLAND_KHR,
916                            display_handle.display,
917                            &display_attributes,
918                        )
919                        .unwrap();
920
921                    let new_inner =
922                        Inner::create(self.flags, Arc::clone(&inner.egl.instance), display)
923                            .map_err(|_| crate::InstanceError)?;
924
925                    let old_inner = std::mem::replace(inner.deref_mut(), new_inner);
926                    inner.wl_display = Some(display_handle.display);
927
928                    drop(old_inner);
929                }
930            }
931            #[cfg(target_os = "emscripten")]
932            (Rwh::Web(_), _) => {}
933            other => {
934                log::error!("Unsupported window: {:?}", other);
935                return Err(crate::InstanceError);
936            }
937        };
938
939        inner.egl.unmake_current();
940
941        Ok(Surface {
942            egl: inner.egl.clone(),
943            wsi: self.wsi.clone(),
944            config: inner.config,
945            presentable: inner.supports_native_window,
946            raw_window_handle: window_handle,
947            swapchain: None,
948            srgb_kind: inner.srgb_kind,
949        })
950    }
951    unsafe fn destroy_surface(&self, _surface: Surface) {}
952
953    unsafe fn enumerate_adapters(&self) -> Vec<crate::ExposedAdapter<super::Api>> {
954        let inner = self.inner.lock();
955        inner.egl.make_current();
956
957        let gl = unsafe {
958            glow::Context::from_loader_function(|name| {
959                inner
960                    .egl
961                    .instance
962                    .get_proc_address(name)
963                    .map_or(ptr::null(), |p| p as *const _)
964            })
965        };
966
967        if self.flags.contains(crate::InstanceFlags::DEBUG) && gl.supports_debug() {
968            log::info!("Max label length: {}", unsafe {
969                gl.get_parameter_i32(glow::MAX_LABEL_LENGTH)
970            });
971        }
972
973        if self.flags.contains(crate::InstanceFlags::VALIDATION) && gl.supports_debug() {
974            log::info!("Enabling GLES debug output");
975            unsafe { gl.enable(glow::DEBUG_OUTPUT) };
976            unsafe { gl.debug_message_callback(gl_debug_message_callback) };
977        }
978
979        inner.egl.unmake_current();
980
981        unsafe {
982            super::Adapter::expose(AdapterContext {
983                glow: Mutex::new(gl),
984                egl: Some(inner.egl.clone()),
985            })
986        }
987        .into_iter()
988        .collect()
989    }
990}
991
992impl super::Adapter {
993    /// Creates a new external adapter using the specified loader function.
994    ///
995    /// # Safety
996    ///
997    /// - The underlying OpenGL ES context must be current.
998    /// - The underlying OpenGL ES context must be current when interfacing with any objects returned by
999    ///   wgpu-hal from this adapter.
1000    pub unsafe fn new_external(
1001        fun: impl FnMut(&str) -> *const ffi::c_void,
1002    ) -> Option<crate::ExposedAdapter<super::Api>> {
1003        let context = unsafe { glow::Context::from_loader_function(fun) };
1004        unsafe {
1005            Self::expose(AdapterContext {
1006                glow: Mutex::new(context),
1007                egl: None,
1008            })
1009        }
1010    }
1011
1012    pub fn adapter_context(&self) -> &AdapterContext {
1013        &self.shared.context
1014    }
1015}
1016
1017impl super::Device {
1018    /// Returns the underlying EGL context.
1019    pub fn context(&self) -> &AdapterContext {
1020        &self.shared.context
1021    }
1022}
1023
1024#[derive(Debug)]
1025pub struct Swapchain {
1026    surface: khronos_egl::Surface,
1027    wl_window: Option<*mut raw::c_void>,
1028    framebuffer: glow::Framebuffer,
1029    renderbuffer: glow::Renderbuffer,
1030    /// Extent because the window lies
1031    extent: wgt::Extent3d,
1032    format: wgt::TextureFormat,
1033    format_desc: super::TextureFormatDesc,
1034    #[allow(unused)]
1035    sample_type: wgt::TextureSampleType,
1036}
1037
1038#[derive(Debug)]
1039pub struct Surface {
1040    egl: EglContext,
1041    wsi: WindowSystemInterface,
1042    config: khronos_egl::Config,
1043    pub(super) presentable: bool,
1044    raw_window_handle: raw_window_handle::RawWindowHandle,
1045    swapchain: Option<Swapchain>,
1046    srgb_kind: SrgbFrameBufferKind,
1047}
1048
1049unsafe impl Send for Surface {}
1050unsafe impl Sync for Surface {}
1051
1052impl Surface {
1053    pub(super) unsafe fn present(
1054        &mut self,
1055        _suf_texture: super::Texture,
1056        gl: &glow::Context,
1057    ) -> Result<(), crate::SurfaceError> {
1058        let sc = self.swapchain.as_ref().unwrap();
1059
1060        self.egl
1061            .instance
1062            .make_current(
1063                self.egl.display,
1064                Some(sc.surface),
1065                Some(sc.surface),
1066                Some(self.egl.raw),
1067            )
1068            .map_err(|e| {
1069                log::error!("make_current(surface) failed: {}", e);
1070                crate::SurfaceError::Lost
1071            })?;
1072
1073        unsafe { gl.disable(glow::SCISSOR_TEST) };
1074        unsafe { gl.color_mask(true, true, true, true) };
1075
1076        unsafe { gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None) };
1077        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(sc.framebuffer)) };
1078        // Note the Y-flipping here. GL's presentation is not flipped,
1079        // but main rendering is. Therefore, we Y-flip the output positions
1080        // in the shader, and also this blit.
1081        unsafe {
1082            gl.blit_framebuffer(
1083                0,
1084                sc.extent.height as i32,
1085                sc.extent.width as i32,
1086                0,
1087                0,
1088                0,
1089                sc.extent.width as i32,
1090                sc.extent.height as i32,
1091                glow::COLOR_BUFFER_BIT,
1092                glow::NEAREST,
1093            )
1094        };
1095        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
1096
1097        self.egl
1098            .instance
1099            .swap_buffers(self.egl.display, sc.surface)
1100            .map_err(|e| {
1101                log::error!("swap_buffers failed: {}", e);
1102                crate::SurfaceError::Lost
1103            })?;
1104        self.egl
1105            .instance
1106            .make_current(self.egl.display, None, None, None)
1107            .map_err(|e| {
1108                log::error!("make_current(null) failed: {}", e);
1109                crate::SurfaceError::Lost
1110            })?;
1111
1112        Ok(())
1113    }
1114
1115    unsafe fn unconfigure_impl(
1116        &mut self,
1117        device: &super::Device,
1118    ) -> Option<(khronos_egl::Surface, Option<*mut raw::c_void>)> {
1119        let gl = &device.shared.context.lock();
1120        match self.swapchain.take() {
1121            Some(sc) => {
1122                unsafe { gl.delete_renderbuffer(sc.renderbuffer) };
1123                unsafe { gl.delete_framebuffer(sc.framebuffer) };
1124                Some((sc.surface, sc.wl_window))
1125            }
1126            None => None,
1127        }
1128    }
1129
1130    pub fn supports_srgb(&self) -> bool {
1131        match self.srgb_kind {
1132            SrgbFrameBufferKind::None => false,
1133            _ => true,
1134        }
1135    }
1136}
1137
1138impl crate::Surface<super::Api> for Surface {
1139    unsafe fn configure(
1140        &mut self,
1141        device: &super::Device,
1142        config: &crate::SurfaceConfiguration,
1143    ) -> Result<(), crate::SurfaceError> {
1144        use raw_window_handle::RawWindowHandle as Rwh;
1145
1146        let (surface, wl_window) = match unsafe { self.unconfigure_impl(device) } {
1147            Some(pair) => pair,
1148            None => {
1149                let mut wl_window = None;
1150                let (mut temp_xlib_handle, mut temp_xcb_handle);
1151                #[allow(trivial_casts)]
1152                let native_window_ptr = match (self.wsi.kind, self.raw_window_handle) {
1153                    (WindowKind::Unknown | WindowKind::X11, Rwh::Xlib(handle)) => {
1154                        temp_xlib_handle = handle.window;
1155                        &mut temp_xlib_handle as *mut _ as *mut std::ffi::c_void
1156                    }
1157                    (WindowKind::AngleX11, Rwh::Xlib(handle)) => {
1158                        handle.window as *mut std::ffi::c_void
1159                    }
1160                    (WindowKind::Unknown | WindowKind::X11, Rwh::Xcb(handle)) => {
1161                        temp_xcb_handle = handle.window;
1162                        &mut temp_xcb_handle as *mut _ as *mut std::ffi::c_void
1163                    }
1164                    (WindowKind::AngleX11, Rwh::Xcb(handle)) => {
1165                        handle.window as *mut std::ffi::c_void
1166                    }
1167                    (WindowKind::Unknown, Rwh::AndroidNdk(handle)) => handle.a_native_window,
1168                    (WindowKind::Wayland, Rwh::Wayland(handle)) => {
1169                        let library = &self.wsi.display_owner.as_ref().unwrap().library;
1170                        let wl_egl_window_create: libloading::Symbol<WlEglWindowCreateFun> =
1171                            unsafe { library.get(b"wl_egl_window_create") }.unwrap();
1172                        let window = unsafe { wl_egl_window_create(handle.surface, 640, 480) }
1173                            as *mut _ as *mut std::ffi::c_void;
1174                        wl_window = Some(window);
1175                        window
1176                    }
1177                    #[cfg(target_os = "emscripten")]
1178                    (WindowKind::Unknown, Rwh::Web(handle)) => handle.id as *mut std::ffi::c_void,
1179                    (WindowKind::Unknown, Rwh::Win32(handle)) => handle.hwnd,
1180                    (WindowKind::Unknown, Rwh::AppKit(handle)) => {
1181                        #[cfg(not(target_os = "macos"))]
1182                        let window_ptr = handle.ns_view;
1183                        #[cfg(target_os = "macos")]
1184                        let window_ptr = {
1185                            use objc::{msg_send, runtime::Object, sel, sel_impl};
1186                            // ns_view always have a layer and don't need to verify that it exists.
1187                            let layer: *mut Object =
1188                                msg_send![handle.ns_view as *mut Object, layer];
1189                            layer as *mut ffi::c_void
1190                        };
1191                        window_ptr
1192                    }
1193                    _ => {
1194                        log::warn!(
1195                            "Initialized platform {:?} doesn't work with window {:?}",
1196                            self.wsi.kind,
1197                            self.raw_window_handle
1198                        );
1199                        return Err(crate::SurfaceError::Other("incompatible window kind"));
1200                    }
1201                };
1202
1203                let mut attributes = vec![
1204                    khronos_egl::RENDER_BUFFER,
1205                    // We don't want any of the buffering done by the driver, because we
1206                    // manage a swapchain on our side.
1207                    // Some drivers just fail on surface creation seeing `EGL_SINGLE_BUFFER`.
1208                    if cfg!(any(target_os = "android", target_os = "macos"))
1209                        || cfg!(windows)
1210                        || self.wsi.kind == WindowKind::AngleX11
1211                    {
1212                        khronos_egl::BACK_BUFFER
1213                    } else {
1214                        khronos_egl::SINGLE_BUFFER
1215                    },
1216                ];
1217                if config.format.is_srgb() {
1218                    match self.srgb_kind {
1219                        SrgbFrameBufferKind::None => {}
1220                        SrgbFrameBufferKind::Core => {
1221                            attributes.push(khronos_egl::GL_COLORSPACE);
1222                            attributes.push(khronos_egl::GL_COLORSPACE_SRGB);
1223                        }
1224                        SrgbFrameBufferKind::Khr => {
1225                            attributes.push(EGL_GL_COLORSPACE_KHR as i32);
1226                            attributes.push(EGL_GL_COLORSPACE_SRGB_KHR as i32);
1227                        }
1228                    }
1229                }
1230                attributes.push(khronos_egl::ATTRIB_NONE as i32);
1231
1232                #[cfg(not(target_os = "emscripten"))]
1233                let egl1_5 = self.egl.instance.upcast::<khronos_egl::EGL1_5>();
1234
1235                #[cfg(target_os = "emscripten")]
1236                let egl1_5: Option<&Arc<EglInstance>> = Some(&self.egl.instance);
1237
1238                // Careful, we can still be in 1.4 version even if `upcast` succeeds
1239                let raw_result = match egl1_5 {
1240                    Some(egl) if self.wsi.kind != WindowKind::Unknown => {
1241                        let attributes_usize = attributes
1242                            .into_iter()
1243                            .map(|v| v as usize)
1244                            .collect::<Vec<_>>();
1245                        egl.create_platform_window_surface(
1246                            self.egl.display,
1247                            self.config,
1248                            native_window_ptr,
1249                            &attributes_usize,
1250                        )
1251                    }
1252                    _ => unsafe {
1253                        self.egl.instance.create_window_surface(
1254                            self.egl.display,
1255                            self.config,
1256                            native_window_ptr,
1257                            Some(&attributes),
1258                        )
1259                    },
1260                };
1261
1262                match raw_result {
1263                    Ok(raw) => (raw, wl_window),
1264                    Err(e) => {
1265                        log::warn!("Error in create_window_surface: {:?}", e);
1266                        return Err(crate::SurfaceError::Lost);
1267                    }
1268                }
1269            }
1270        };
1271
1272        if let Some(window) = wl_window {
1273            let library = &self.wsi.display_owner.as_ref().unwrap().library;
1274            let wl_egl_window_resize: libloading::Symbol<WlEglWindowResizeFun> =
1275                unsafe { library.get(b"wl_egl_window_resize") }.unwrap();
1276            unsafe {
1277                wl_egl_window_resize(
1278                    window,
1279                    config.extent.width as i32,
1280                    config.extent.height as i32,
1281                    0,
1282                    0,
1283                )
1284            };
1285        }
1286
1287        let format_desc = device.shared.describe_texture_format(config.format);
1288        let gl = &device.shared.context.lock();
1289        let renderbuffer = unsafe { gl.create_renderbuffer() }.map_err(|error| {
1290            log::error!("Internal swapchain renderbuffer creation failed: {error}");
1291            crate::DeviceError::OutOfMemory
1292        })?;
1293        unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, Some(renderbuffer)) };
1294        unsafe {
1295            gl.renderbuffer_storage(
1296                glow::RENDERBUFFER,
1297                format_desc.internal,
1298                config.extent.width as _,
1299                config.extent.height as _,
1300            )
1301        };
1302        let framebuffer = unsafe { gl.create_framebuffer() }.map_err(|error| {
1303            log::error!("Internal swapchain framebuffer creation failed: {error}");
1304            crate::DeviceError::OutOfMemory
1305        })?;
1306        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(framebuffer)) };
1307        unsafe {
1308            gl.framebuffer_renderbuffer(
1309                glow::READ_FRAMEBUFFER,
1310                glow::COLOR_ATTACHMENT0,
1311                glow::RENDERBUFFER,
1312                Some(renderbuffer),
1313            )
1314        };
1315        unsafe { gl.bind_renderbuffer(glow::RENDERBUFFER, None) };
1316        unsafe { gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None) };
1317
1318        self.swapchain = Some(Swapchain {
1319            surface,
1320            wl_window,
1321            renderbuffer,
1322            framebuffer,
1323            extent: config.extent,
1324            format: config.format,
1325            format_desc,
1326            sample_type: wgt::TextureSampleType::Float { filterable: false },
1327        });
1328
1329        Ok(())
1330    }
1331
1332    unsafe fn unconfigure(&mut self, device: &super::Device) {
1333        if let Some((surface, wl_window)) = unsafe { self.unconfigure_impl(device) } {
1334            self.egl
1335                .instance
1336                .destroy_surface(self.egl.display, surface)
1337                .unwrap();
1338            if let Some(window) = wl_window {
1339                let library = &self
1340                    .wsi
1341                    .display_owner
1342                    .as_ref()
1343                    .expect("unsupported window")
1344                    .library;
1345                let wl_egl_window_destroy: libloading::Symbol<WlEglWindowDestroyFun> =
1346                    unsafe { library.get(b"wl_egl_window_destroy") }.unwrap();
1347                unsafe { wl_egl_window_destroy(window) };
1348            }
1349        }
1350    }
1351
1352    unsafe fn acquire_texture(
1353        &mut self,
1354        _timeout_ms: Option<Duration>, //TODO
1355    ) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
1356        let sc = self.swapchain.as_ref().unwrap();
1357        let texture = super::Texture {
1358            inner: super::TextureInner::Renderbuffer {
1359                raw: sc.renderbuffer,
1360            },
1361            drop_guard: None,
1362            array_layer_count: 1,
1363            mip_level_count: 1,
1364            format: sc.format,
1365            format_desc: sc.format_desc.clone(),
1366            copy_size: crate::CopyExtent {
1367                width: sc.extent.width,
1368                height: sc.extent.height,
1369                depth: 1,
1370            },
1371            is_cubemap: false,
1372        };
1373        Ok(Some(crate::AcquiredSurfaceTexture {
1374            texture,
1375            suboptimal: false,
1376        }))
1377    }
1378    unsafe fn discard_texture(&mut self, _texture: super::Texture) {}
1379}