glutin/api/egl/
surface.rs

1//! Everything related to `EGLSurface`.
2
3use std::marker::PhantomData;
4use std::num::NonZeroU32;
5use std::{ffi, fmt};
6
7use glutin_egl_sys::egl;
8use glutin_egl_sys::egl::types::{EGLAttrib, EGLSurface, EGLint};
9use raw_window_handle::RawWindowHandle;
10#[cfg(wayland_platform)]
11use wayland_sys::{egl::*, ffi_dispatch};
12
13use crate::api::egl::display::EglDisplay;
14use crate::config::GetGlConfig;
15use crate::display::GetGlDisplay;
16use crate::error::{ErrorKind, Result};
17use crate::prelude::*;
18use crate::private::Sealed;
19use crate::surface::{
20    AsRawSurface, NativePixmap, PbufferSurface, PixmapSurface, RawSurface, Rect, SurfaceAttributes,
21    SurfaceTypeTrait, SwapInterval, WindowSurface,
22};
23
24use super::config::Config;
25use super::context::PossiblyCurrentContext;
26use super::display::Display;
27
28/// Hint for the attribute list size.
29const ATTR_SIZE_HINT: usize = 8;
30
31impl Display {
32    pub(crate) unsafe fn create_pbuffer_surface(
33        &self,
34        config: &Config,
35        surface_attributes: &SurfaceAttributes<PbufferSurface>,
36    ) -> Result<Surface<PbufferSurface>> {
37        let width = surface_attributes.width.unwrap();
38        let height = surface_attributes.height.unwrap();
39
40        // XXX Window surface is using `EGLAttrib` and not `EGLint`.
41        let mut attrs = Vec::<EGLint>::with_capacity(ATTR_SIZE_HINT);
42
43        // Add dimensions.
44        attrs.push(egl::WIDTH as EGLint);
45        attrs.push(width.get() as EGLint);
46
47        attrs.push(egl::HEIGHT as EGLint);
48        attrs.push(height.get() as EGLint);
49
50        // Push `egl::NONE` to terminate the list.
51        attrs.push(egl::NONE as EGLint);
52
53        let config = config.clone();
54        let surface = unsafe {
55            Self::check_surface_error(self.inner.egl.CreatePbufferSurface(
56                *self.inner.raw,
57                *config.inner.raw,
58                attrs.as_ptr(),
59            ))?
60        };
61
62        Ok(Surface {
63            display: self.clone(),
64            native_window: None,
65            config,
66            raw: surface,
67            _ty: PhantomData,
68        })
69    }
70
71    pub(crate) unsafe fn create_pixmap_surface(
72        &self,
73        config: &Config,
74        surface_attributes: &SurfaceAttributes<PixmapSurface>,
75    ) -> Result<Surface<PixmapSurface>> {
76        let native_pixmap = surface_attributes.native_pixmap.as_ref().unwrap();
77
78        let mut attrs = Vec::<EGLAttrib>::with_capacity(ATTR_SIZE_HINT);
79
80        if surface_attributes.srgb.is_some() && config.srgb_capable() {
81            attrs.push(egl::GL_COLORSPACE as EGLAttrib);
82            let colorspace = match surface_attributes.srgb {
83                Some(true) => egl::GL_COLORSPACE_SRGB as EGLAttrib,
84                _ => egl::GL_COLORSPACE_LINEAR as EGLAttrib,
85            };
86            attrs.push(colorspace);
87        }
88
89        // Push `egl::NONE` to terminate the list.
90        attrs.push(egl::NONE as EGLAttrib);
91
92        let config = config.clone();
93        let surface = match self.inner.raw {
94            EglDisplay::Khr(display) => {
95                let platform_pixmap = native_pixmap.as_platform_pixmap();
96                if platform_pixmap.is_null() {
97                    return Err(ErrorKind::BadNativePixmap.into());
98                }
99                unsafe {
100                    self.inner.egl.CreatePlatformPixmapSurface(
101                        display,
102                        *config.inner.raw,
103                        platform_pixmap,
104                        attrs.as_ptr(),
105                    )
106                }
107            },
108            EglDisplay::Ext(display) => {
109                let platform_pixmap = native_pixmap.as_platform_pixmap();
110                if platform_pixmap.is_null() {
111                    return Err(ErrorKind::BadNativePixmap.into());
112                }
113                unsafe {
114                    let attrs: Vec<EGLint> = attrs.into_iter().map(|attr| attr as EGLint).collect();
115                    self.inner.egl.CreatePlatformPixmapSurfaceEXT(
116                        display,
117                        *config.inner.raw,
118                        platform_pixmap,
119                        attrs.as_ptr(),
120                    )
121                }
122            },
123            EglDisplay::Legacy(display) => {
124                let native_pixmap = native_pixmap.as_native_pixmap();
125
126                #[cfg(not(windows))]
127                if native_pixmap.is_null() {
128                    return Err(ErrorKind::BadNativePixmap.into());
129                }
130
131                #[cfg(windows)]
132                if native_pixmap == 0 {
133                    return Err(ErrorKind::BadNativePixmap.into());
134                }
135
136                unsafe {
137                    // This call accepts raw value, instead of pointer.
138                    let attrs: Vec<EGLint> = attrs.into_iter().map(|attr| attr as EGLint).collect();
139                    self.inner.egl.CreatePixmapSurface(
140                        display,
141                        *config.inner.raw,
142                        native_pixmap,
143                        attrs.as_ptr(),
144                    )
145                }
146            },
147        };
148
149        let surface = Self::check_surface_error(surface)?;
150
151        Ok(Surface {
152            display: self.clone(),
153            config,
154            native_window: None,
155            raw: surface,
156            _ty: PhantomData,
157        })
158    }
159
160    pub(crate) unsafe fn create_window_surface(
161        &self,
162        config: &Config,
163        surface_attributes: &SurfaceAttributes<WindowSurface>,
164    ) -> Result<Surface<WindowSurface>> {
165        // Create native window.
166        let native_window = NativeWindow::new(
167            surface_attributes.width.unwrap(),
168            surface_attributes.height.unwrap(),
169            surface_attributes.raw_window_handle.as_ref().unwrap(),
170        )?;
171
172        // XXX Window surface is using `EGLAttrib` and not `EGLint`.
173        let mut attrs = Vec::<EGLAttrib>::with_capacity(ATTR_SIZE_HINT);
174
175        // Add information about render buffer.
176        attrs.push(egl::RENDER_BUFFER as EGLAttrib);
177        let buffer =
178            if surface_attributes.single_buffer { egl::SINGLE_BUFFER } else { egl::BACK_BUFFER }
179                as EGLAttrib;
180        attrs.push(buffer);
181
182        // // Add colorspace if the extension is present.
183        if surface_attributes.srgb.is_some() && config.srgb_capable() {
184            attrs.push(egl::GL_COLORSPACE as EGLAttrib);
185            let colorspace = match surface_attributes.srgb {
186                Some(true) => egl::GL_COLORSPACE_SRGB as EGLAttrib,
187                _ => egl::GL_COLORSPACE_LINEAR as EGLAttrib,
188            };
189            attrs.push(colorspace);
190        }
191
192        // Push `egl::NONE` to terminate the list.
193        attrs.push(egl::NONE as EGLAttrib);
194
195        let config = config.clone();
196
197        let surface = match self.inner.raw {
198            EglDisplay::Khr(display) => unsafe {
199                self.inner.egl.CreatePlatformWindowSurface(
200                    display,
201                    *config.inner.raw,
202                    native_window.as_platform_window(),
203                    attrs.as_ptr(),
204                )
205            },
206            EglDisplay::Ext(display) => unsafe {
207                let attrs: Vec<EGLint> = attrs.into_iter().map(|attr| attr as EGLint).collect();
208                self.inner.egl.CreatePlatformWindowSurfaceEXT(
209                    display,
210                    *config.inner.raw,
211                    native_window.as_platform_window(),
212                    attrs.as_ptr(),
213                )
214            },
215            EglDisplay::Legacy(display) => unsafe {
216                let attrs: Vec<EGLint> = attrs.into_iter().map(|attr| attr as EGLint).collect();
217                self.inner.egl.CreateWindowSurface(
218                    display,
219                    *config.inner.raw,
220                    native_window.as_native_window(),
221                    attrs.as_ptr(),
222                )
223            },
224        };
225
226        let surface = Self::check_surface_error(surface)?;
227
228        Ok(Surface {
229            display: self.clone(),
230            config,
231            native_window: Some(native_window),
232            raw: surface,
233            _ty: PhantomData,
234        })
235    }
236
237    fn check_surface_error(surface: EGLSurface) -> Result<EGLSurface> {
238        if surface == egl::NO_SURFACE {
239            Err(super::check_error().err().unwrap())
240        } else {
241            Ok(surface)
242        }
243    }
244}
245
246/// A wrapper around `EGLSurface`.
247pub struct Surface<T: SurfaceTypeTrait> {
248    display: Display,
249    config: Config,
250    pub(crate) raw: EGLSurface,
251    native_window: Option<NativeWindow>,
252    _ty: PhantomData<T>,
253}
254
255// Impl only `Send` for Surface.
256unsafe impl<T: SurfaceTypeTrait> Send for Surface<T> {}
257
258impl<T: SurfaceTypeTrait> Surface<T> {
259    /// Swaps the underlying back buffers when the surface is not single
260    /// buffered and pass the [`Rect`] information to the system
261    /// compositor. Providing empty slice will damage the entire surface.
262    ///
263    /// When the underlying extensions are not supported the function acts like
264    /// [`Self::swap_buffers`].
265    ///
266    /// This Api doesn't do any partial rendering, it just provides hints for
267    /// the system compositor.
268    pub fn swap_buffers_with_damage(
269        &self,
270        context: &PossiblyCurrentContext,
271        rects: &[Rect],
272    ) -> Result<()> {
273        context.inner.bind_api();
274
275        let res = unsafe {
276            if self.display.inner.display_extensions.contains("EGL_KHR_swap_buffers_with_damage") {
277                self.display.inner.egl.SwapBuffersWithDamageKHR(
278                    *self.display.inner.raw,
279                    self.raw,
280                    rects.as_ptr() as *mut _,
281                    rects.len() as _,
282                )
283            } else if self
284                .display
285                .inner
286                .display_extensions
287                .contains("EGL_EXT_swap_buffers_with_damage")
288            {
289                self.display.inner.egl.SwapBuffersWithDamageEXT(
290                    *self.display.inner.raw,
291                    self.raw,
292                    rects.as_ptr() as *mut _,
293                    rects.len() as _,
294                )
295            } else {
296                self.display.inner.egl.SwapBuffers(*self.display.inner.raw, self.raw)
297            }
298        };
299
300        if res == egl::FALSE {
301            super::check_error()
302        } else {
303            Ok(())
304        }
305    }
306
307    /// # Safety
308    ///
309    /// The caller must ensure that the attribute could be present.
310    unsafe fn raw_attribute(&self, attr: EGLint) -> EGLint {
311        unsafe {
312            let mut value = 0;
313            self.display.inner.egl.QuerySurface(
314                *self.display.inner.raw,
315                self.raw,
316                attr,
317                &mut value,
318            );
319            value
320        }
321    }
322}
323
324impl<T: SurfaceTypeTrait> Drop for Surface<T> {
325    fn drop(&mut self) {
326        unsafe {
327            self.display.inner.egl.DestroySurface(*self.display.inner.raw, self.raw);
328        }
329    }
330}
331
332impl<T: SurfaceTypeTrait> GlSurface<T> for Surface<T> {
333    type Context = PossiblyCurrentContext;
334    type SurfaceType = T;
335
336    fn buffer_age(&self) -> u32 {
337        self.display
338            .inner
339            .display_extensions
340            .contains("EGL_EXT_buffer_age")
341            .then(|| unsafe { self.raw_attribute(egl::BUFFER_AGE_EXT as EGLint) })
342            .unwrap_or(0) as u32
343    }
344
345    fn width(&self) -> Option<u32> {
346        unsafe { Some(self.raw_attribute(egl::WIDTH as EGLint) as u32) }
347    }
348
349    fn height(&self) -> Option<u32> {
350        unsafe { Some(self.raw_attribute(egl::HEIGHT as EGLint) as u32) }
351    }
352
353    fn is_single_buffered(&self) -> bool {
354        unsafe { self.raw_attribute(egl::RENDER_BUFFER as EGLint) == egl::SINGLE_BUFFER as i32 }
355    }
356
357    fn swap_buffers(&self, context: &Self::Context) -> Result<()> {
358        unsafe {
359            context.inner.bind_api();
360
361            if self.display.inner.egl.SwapBuffers(*self.display.inner.raw, self.raw) == egl::FALSE {
362                super::check_error()
363            } else {
364                Ok(())
365            }
366        }
367    }
368
369    fn set_swap_interval(&self, context: &Self::Context, interval: SwapInterval) -> Result<()> {
370        unsafe {
371            context.inner.bind_api();
372
373            let interval = match interval {
374                SwapInterval::DontWait => 0,
375                SwapInterval::Wait(interval) => interval.get() as EGLint,
376            };
377            if self.display.inner.egl.SwapInterval(*self.display.inner.raw, interval) == egl::FALSE
378            {
379                super::check_error()
380            } else {
381                Ok(())
382            }
383        }
384    }
385
386    fn is_current(&self, context: &Self::Context) -> bool {
387        self.is_current_draw(context) && self.is_current_read(context)
388    }
389
390    fn is_current_draw(&self, context: &Self::Context) -> bool {
391        unsafe {
392            context.inner.bind_api();
393            self.display.inner.egl.GetCurrentSurface(egl::DRAW as EGLint) == self.raw
394        }
395    }
396
397    fn is_current_read(&self, context: &Self::Context) -> bool {
398        unsafe {
399            context.inner.bind_api();
400            self.display.inner.egl.GetCurrentSurface(egl::READ as EGLint) == self.raw
401        }
402    }
403
404    fn resize(&self, _context: &Self::Context, width: NonZeroU32, height: NonZeroU32) {
405        self.native_window.as_ref().unwrap().resize(width, height)
406    }
407}
408
409impl<T: SurfaceTypeTrait> GetGlConfig for Surface<T> {
410    type Target = Config;
411
412    fn config(&self) -> Self::Target {
413        self.config.clone()
414    }
415}
416
417impl<T: SurfaceTypeTrait> GetGlDisplay for Surface<T> {
418    type Target = Display;
419
420    fn display(&self) -> Self::Target {
421        self.display.clone()
422    }
423}
424
425impl<T: SurfaceTypeTrait> AsRawSurface for Surface<T> {
426    fn raw_surface(&self) -> RawSurface {
427        RawSurface::Egl(self.raw)
428    }
429}
430
431impl<T: SurfaceTypeTrait> fmt::Debug for Surface<T> {
432    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
433        f.debug_struct("Surface")
434            .field("display", &self.display.inner.raw)
435            .field("config", &self.config.inner.raw)
436            .field("raw", &self.raw)
437            .field("native_window", &self.native_window)
438            .field("type", &T::surface_type())
439            .finish()
440    }
441}
442
443impl<T: SurfaceTypeTrait> Sealed for Surface<T> {}
444
445#[derive(Debug)]
446enum NativeWindow {
447    #[allow(dead_code)]
448    Wayland(*mut ffi::c_void),
449    Xlib(std::os::raw::c_ulong),
450    Xcb(u32),
451    Android(*mut ffi::c_void),
452    Ohos(*mut ffi::c_void),
453    Win32(isize),
454    Gbm(*mut ffi::c_void),
455}
456
457impl NativeWindow {
458    fn new(
459        _width: NonZeroU32,
460        _height: NonZeroU32,
461        raw_window_handle: &RawWindowHandle,
462    ) -> Result<Self> {
463        let native_window = match raw_window_handle {
464            #[cfg(wayland_platform)]
465            RawWindowHandle::Wayland(window_handle) => unsafe {
466                let ptr = ffi_dispatch!(
467                    wayland_egl_handle(),
468                    wl_egl_window_create,
469                    window_handle.surface.as_ptr().cast(),
470                    _width.get() as _,
471                    _height.get() as _
472                );
473                if ptr.is_null() {
474                    return Err(ErrorKind::OutOfMemory.into());
475                }
476                Self::Wayland(ptr.cast())
477            },
478            RawWindowHandle::Xlib(window_handle) => {
479                if window_handle.window == 0 {
480                    return Err(ErrorKind::BadNativeWindow.into());
481                }
482
483                Self::Xlib(window_handle.window as _)
484            },
485            RawWindowHandle::Xcb(window_handle) => Self::Xcb(window_handle.window.get() as _),
486            RawWindowHandle::AndroidNdk(window_handle) => {
487                Self::Android(window_handle.a_native_window.as_ptr())
488            },
489            RawWindowHandle::OhosNdk(window_handle) => {
490                Self::Ohos(window_handle.native_window.as_ptr())
491            },
492            RawWindowHandle::Win32(window_handle) => Self::Win32(window_handle.hwnd.get() as _),
493            RawWindowHandle::Gbm(window_handle) => Self::Gbm(window_handle.gbm_surface.as_ptr()),
494            _ => {
495                return Err(
496                    ErrorKind::NotSupported("provided native window is not supported").into()
497                )
498            },
499        };
500
501        Ok(native_window)
502    }
503
504    fn resize(&self, _width: NonZeroU32, _height: NonZeroU32) {
505        #[cfg(wayland_platform)]
506        if let Self::Wayland(wl_egl_surface) = self {
507            unsafe {
508                ffi_dispatch!(
509                    wayland_egl_handle(),
510                    wl_egl_window_resize,
511                    *wl_egl_surface as _,
512                    _width.get() as _,
513                    _height.get() as _,
514                    0,
515                    0
516                )
517            }
518        }
519    }
520
521    /// Returns the underlying handle value.
522    fn as_native_window(&self) -> egl::NativeWindowType {
523        match *self {
524            Self::Wayland(wl_egl_surface) => wl_egl_surface as egl::NativeWindowType,
525            Self::Xlib(window_id) => window_id as egl::NativeWindowType,
526            Self::Xcb(window_id) => window_id as egl::NativeWindowType,
527            Self::Win32(hwnd) => hwnd as egl::NativeWindowType,
528            Self::Android(a_native_window) => a_native_window as egl::NativeWindowType,
529            Self::Ohos(native_window) => native_window as egl::NativeWindowType,
530            Self::Gbm(gbm_surface) => gbm_surface as egl::NativeWindowType,
531        }
532    }
533
534    /// Returns a pointer to the underlying handle value on X11,
535    /// the raw underlying handle value on all other platforms.
536    ///
537    /// This exists because of a discrepancy in the new
538    /// `eglCreatePlatformWindowSurface*` functions which take a pointer to the
539    /// `window_id` on X11 and Xlib, in contrast to the legacy
540    /// `eglCreateWindowSurface` which always takes the raw value.
541    ///
542    /// See also:
543    /// <https://gitlab.freedesktop.org/mesa/mesa/-/blob/4de9a4b2b8c41864aadae89be705ef125a745a0a/src/egl/main/eglapi.c#L1102-1127>
544    ///
545    /// # Safety
546    ///
547    /// On X11 the returned pointer is a cast of the `&self` borrow.
548    fn as_platform_window(&self) -> *mut ffi::c_void {
549        match self {
550            Self::Wayland(wl_egl_surface) => *wl_egl_surface,
551            Self::Xlib(window_id) => window_id as *const _ as *mut ffi::c_void,
552            Self::Xcb(window_id) => window_id as *const _ as *mut ffi::c_void,
553            Self::Win32(hwnd) => *hwnd as *const ffi::c_void as *mut _,
554            Self::Android(a_native_window) => *a_native_window,
555            Self::Ohos(native_window) => *native_window,
556            Self::Gbm(gbm_surface) => *gbm_surface,
557        }
558    }
559}
560
561#[cfg(wayland_platform)]
562impl Drop for NativeWindow {
563    fn drop(&mut self) {
564        unsafe {
565            if let Self::Wayland(wl_egl_window) = self {
566                ffi_dispatch!(wayland_egl_handle(), wl_egl_window_destroy, wl_egl_window.cast());
567            }
568        }
569    }
570}
571
572impl NativePixmap {
573    /// Returns the underlying handle value.
574    fn as_native_pixmap(&self) -> egl::NativePixmapType {
575        match *self {
576            Self::XlibPixmap(xid) => xid as egl::NativePixmapType,
577            Self::XcbPixmap(xid) => xid as egl::NativePixmapType,
578            Self::WindowsPixmap(hbitmap) => hbitmap as egl::NativePixmapType,
579        }
580    }
581
582    /// Returns a pointer to the underlying handle value on X11,
583    /// the raw underlying handle value on all other platforms.
584    ///
585    /// This exists because of a discrepancy in the new
586    /// `eglCreatePlatformPixmapSurface*` functions which take a pointer to the
587    /// `xid` on X11 and Xlib, in contrast to the legacy
588    /// `eglCreatePixmapSurface` which always takes the raw value.
589    ///
590    /// See also:
591    /// <https://gitlab.freedesktop.org/mesa/mesa/-/blob/4de9a4b2b8c41864aadae89be705ef125a745a0a/src/egl/main/eglapi.c#L1166-1190>
592    ///
593    /// # Safety
594    ///
595    /// On X11 the returned pointer is a cast of the `&self` borrow.
596    fn as_platform_pixmap(&self) -> *mut ffi::c_void {
597        match self {
598            Self::XlibPixmap(xid) => xid as *const _ as *mut _,
599            Self::XcbPixmap(xid) => xid as *const _ as *mut _,
600            Self::WindowsPixmap(hbitmap) => *hbitmap as *const ffi::c_void as *mut _,
601        }
602    }
603}