wgpu_hal/gles/
egl.rs

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