1 | //! A cross platform OpenGL surface representation. |
2 | #![allow (unreachable_patterns)] |
3 | |
4 | use std::marker::PhantomData; |
5 | use std::num::NonZeroU32; |
6 | |
7 | use raw_window_handle::RawWindowHandle; |
8 | |
9 | use crate::context::{PossiblyCurrentContext, PossiblyCurrentGlContext}; |
10 | use crate::display::{Display, GetGlDisplay}; |
11 | use crate::error::Result; |
12 | use crate::private::{gl_api_dispatch, Sealed}; |
13 | |
14 | #[cfg (cgl_backend)] |
15 | use crate::api::cgl::surface::Surface as CglSurface; |
16 | #[cfg (egl_backend)] |
17 | use crate::api::egl::surface::Surface as EglSurface; |
18 | #[cfg (glx_backend)] |
19 | use crate::api::glx::surface::Surface as GlxSurface; |
20 | #[cfg (wgl_backend)] |
21 | use crate::api::wgl::surface::Surface as WglSurface; |
22 | |
23 | /// A trait to group common operations on the surface. |
24 | pub trait GlSurface<T: SurfaceTypeTrait>: Sealed { |
25 | /// The type of the surface. |
26 | type SurfaceType: SurfaceTypeTrait; |
27 | /// The context to access surface data. |
28 | type Context: PossiblyCurrentGlContext; |
29 | |
30 | /// The age of the back buffer of that surface. The `0` indicates that the |
31 | /// buffer is either a new one or we failed to get the information about |
32 | /// its age. In both cases you must redraw the entire buffer. |
33 | /// |
34 | /// # Platform-specific |
35 | /// |
36 | /// - **Wayland:** this call will latch the underlying back buffer, meaning |
37 | /// that all resize operations will apply after the next |
38 | /// [`GlSurface::swap_buffers`]. |
39 | fn buffer_age(&self) -> u32; |
40 | |
41 | /// The **physical** width of the underlying surface. |
42 | fn width(&self) -> Option<u32>; |
43 | |
44 | /// The **physical** height of the underlying surface. |
45 | /// |
46 | /// # Platform specific |
47 | /// |
48 | /// - **macOS: this will block if your main thread is blocked.** |
49 | fn height(&self) -> Option<u32>; |
50 | |
51 | /// Check whether the surface is single buffered. |
52 | /// |
53 | /// # Platform specific |
54 | /// |
55 | /// - **macOS: this will block if your main thread is blocked.** |
56 | fn is_single_buffered(&self) -> bool; |
57 | |
58 | /// Swaps the underlying back buffers when the surface is not single |
59 | /// buffered. |
60 | fn swap_buffers(&self, context: &Self::Context) -> Result<()>; |
61 | |
62 | /// Check whether the surface is current on to the current thread. |
63 | fn is_current(&self, context: &Self::Context) -> bool; |
64 | |
65 | /// Check whether the surface is the current draw surface to the current |
66 | /// thread. |
67 | fn is_current_draw(&self, context: &Self::Context) -> bool; |
68 | |
69 | /// Check whether the surface is the current read surface to the current |
70 | /// thread. |
71 | fn is_current_read(&self, context: &Self::Context) -> bool; |
72 | |
73 | /// Set swap interval for the surface. |
74 | /// |
75 | /// See [`crate::surface::SwapInterval`] for details. |
76 | fn set_swap_interval(&self, context: &Self::Context, interval: SwapInterval) -> Result<()>; |
77 | |
78 | /// Resize the surface to a new size. |
79 | /// |
80 | /// This call is for compatibility reasons, on most platforms it's a no-op. |
81 | /// It's recommended to call this function before doing any rendering and |
82 | /// performing [`PossiblyCurrentGlContext::make_current`], and |
83 | /// [`GlSurface::buffer_age`]. |
84 | /// |
85 | /// # Platform specific |
86 | /// |
87 | /// - **Wayland:** resizes the surface; |
88 | /// - **macOS: this will block if your main thread is blocked;** |
89 | /// - **Other:** no op. |
90 | fn resize(&self, context: &Self::Context, width: NonZeroU32, height: NonZeroU32) |
91 | where |
92 | Self::SurfaceType: ResizeableSurface; |
93 | } |
94 | |
95 | /// The marker trait to indicate the type of the surface. |
96 | pub trait SurfaceTypeTrait: Sealed { |
97 | /// Get the type of the surface. |
98 | fn surface_type() -> SurfaceType; |
99 | } |
100 | |
101 | /// Marker indicating that the surface could be resized. |
102 | pub trait ResizeableSurface: Sealed {} |
103 | |
104 | /// Trait for accessing the raw GL surface. |
105 | pub trait AsRawSurface { |
106 | /// Get the raw handle to the surface. |
107 | fn raw_surface(&self) -> RawSurface; |
108 | } |
109 | |
110 | /// Builder to get the required set of attributes initialized before hand. |
111 | #[derive (Default, Debug, Clone)] |
112 | pub struct SurfaceAttributesBuilder<T: SurfaceTypeTrait + Default> { |
113 | attributes: SurfaceAttributes<T>, |
114 | } |
115 | |
116 | impl<T: SurfaceTypeTrait + Default> SurfaceAttributesBuilder<T> { |
117 | /// Get new surface attributes. |
118 | pub fn new() -> Self { |
119 | Default::default() |
120 | } |
121 | |
122 | /// Specify whether the surface should support srgb or not. Passing `None` |
123 | /// means you don't care. |
124 | /// |
125 | /// # Api-specific. |
126 | /// |
127 | /// This only controls EGL surfaces, other platforms use the context for |
128 | /// that. |
129 | pub fn with_srgb(mut self, srgb: Option<bool>) -> Self { |
130 | self.attributes.srgb = srgb; |
131 | self |
132 | } |
133 | } |
134 | |
135 | impl SurfaceAttributesBuilder<WindowSurface> { |
136 | /// Specify whether the single buffer should be used instead of double |
137 | /// buffering. This doesn't guarantee that the resulted buffer will have |
138 | /// only single buffer, to know that the single buffer is actually used |
139 | /// query the created surface with [`Surface::is_single_buffered`]. |
140 | /// |
141 | /// The surface is requested as double buffered by default. |
142 | /// |
143 | /// # Api-specific. |
144 | /// |
145 | /// This is EGL specific, other platforms use the context for that. |
146 | pub fn with_single_buffer(mut self, single_buffer: bool) -> Self { |
147 | self.attributes.single_buffer = single_buffer; |
148 | self |
149 | } |
150 | |
151 | /// Build the surface attributes suitable to create a window surface. |
152 | pub fn build( |
153 | mut self, |
154 | raw_window_handle: RawWindowHandle, |
155 | width: NonZeroU32, |
156 | height: NonZeroU32, |
157 | ) -> SurfaceAttributes<WindowSurface> { |
158 | self.attributes.raw_window_handle = Some(raw_window_handle); |
159 | self.attributes.width = Some(width); |
160 | self.attributes.height = Some(height); |
161 | self.attributes |
162 | } |
163 | } |
164 | |
165 | impl SurfaceAttributesBuilder<PbufferSurface> { |
166 | /// Request the largest pbuffer. |
167 | pub fn with_largest_pbuffer(mut self, largest_pbuffer: bool) -> Self { |
168 | self.attributes.largest_pbuffer = largest_pbuffer; |
169 | self |
170 | } |
171 | |
172 | /// The same as in |
173 | /// [`SurfaceAttributesBuilder::<WindowSurface>::with_single_buffer`]. |
174 | pub fn with_single_buffer(mut self, single_buffer: bool) -> Self { |
175 | self.attributes.single_buffer = single_buffer; |
176 | self |
177 | } |
178 | |
179 | /// Build the surface attributes suitable to create a pbuffer surface. |
180 | pub fn build( |
181 | mut self, |
182 | width: NonZeroU32, |
183 | height: NonZeroU32, |
184 | ) -> SurfaceAttributes<PbufferSurface> { |
185 | self.attributes.width = Some(width); |
186 | self.attributes.height = Some(height); |
187 | self.attributes |
188 | } |
189 | } |
190 | |
191 | impl SurfaceAttributesBuilder<PixmapSurface> { |
192 | /// Build the surface attributes suitable to create a pixmap surface. |
193 | pub fn build(mut self, native_pixmap: NativePixmap) -> SurfaceAttributes<PixmapSurface> { |
194 | self.attributes.native_pixmap = Some(native_pixmap); |
195 | self.attributes |
196 | } |
197 | } |
198 | |
199 | /// Attributes which are used for creating a particular surface. |
200 | #[derive (Default, Debug, Clone)] |
201 | pub struct SurfaceAttributes<T: SurfaceTypeTrait> { |
202 | pub(crate) srgb: Option<bool>, |
203 | pub(crate) single_buffer: bool, |
204 | pub(crate) width: Option<NonZeroU32>, |
205 | pub(crate) height: Option<NonZeroU32>, |
206 | pub(crate) largest_pbuffer: bool, |
207 | pub(crate) raw_window_handle: Option<RawWindowHandle>, |
208 | pub(crate) native_pixmap: Option<NativePixmap>, |
209 | _ty: PhantomData<T>, |
210 | } |
211 | |
212 | /// Marker that used to type-gate methods for window. |
213 | #[derive (Default, Debug, Clone, Copy, PartialEq, Eq)] |
214 | pub struct WindowSurface; |
215 | |
216 | impl SurfaceTypeTrait for WindowSurface { |
217 | fn surface_type() -> SurfaceType { |
218 | SurfaceType::Window |
219 | } |
220 | } |
221 | |
222 | impl ResizeableSurface for WindowSurface {} |
223 | |
224 | impl Sealed for WindowSurface {} |
225 | |
226 | /// Marker that used to type-gate methods for pbuffer. |
227 | #[derive (Default, Debug, Clone, Copy, PartialEq, Eq)] |
228 | pub struct PbufferSurface; |
229 | |
230 | impl SurfaceTypeTrait for PbufferSurface { |
231 | fn surface_type() -> SurfaceType { |
232 | SurfaceType::Pbuffer |
233 | } |
234 | } |
235 | |
236 | impl Sealed for PbufferSurface {} |
237 | |
238 | /// Marker that used to type-gate methods for pixmap. |
239 | #[derive (Default, Debug, Clone, Copy, PartialEq, Eq)] |
240 | pub struct PixmapSurface; |
241 | |
242 | impl SurfaceTypeTrait for PixmapSurface { |
243 | fn surface_type() -> SurfaceType { |
244 | SurfaceType::Pixmap |
245 | } |
246 | } |
247 | |
248 | impl Sealed for PixmapSurface {} |
249 | |
250 | /// The underlying type of the surface. |
251 | #[derive (Debug, Clone, Copy)] |
252 | pub enum SurfaceType { |
253 | /// The window surface. |
254 | Window, |
255 | |
256 | /// Pixmap surface. |
257 | Pixmap, |
258 | |
259 | /// Pbuffer surface. |
260 | Pbuffer, |
261 | } |
262 | |
263 | /// The GL surface that is used for rendering. |
264 | /// |
265 | /// Similar to the context, the GL surface is [`Send`] but not [`Sync`]. This |
266 | /// means it could be sent to a different thread as long as it is not current on |
267 | /// another thread. |
268 | /// |
269 | /// ```no_run |
270 | /// fn test_send<T: Send>() {} |
271 | /// test_send::<glutin::surface::Surface<glutin::surface::WindowSurface>>(); |
272 | /// ``` |
273 | /// ```compile_fail |
274 | /// fn test_sync<T: Sync>() {} |
275 | /// test_sync::<glutin::surface::Surface<glutin::surface::WindowSurface>>(); |
276 | /// ``` |
277 | #[derive (Debug)] |
278 | pub enum Surface<T: SurfaceTypeTrait> { |
279 | /// The EGL surface. |
280 | #[cfg (egl_backend)] |
281 | Egl(EglSurface<T>), |
282 | |
283 | /// The GLX surface. |
284 | #[cfg (glx_backend)] |
285 | Glx(GlxSurface<T>), |
286 | |
287 | /// The WGL surface. |
288 | #[cfg (wgl_backend)] |
289 | Wgl(WglSurface<T>), |
290 | |
291 | /// The CGL surface. |
292 | #[cfg (cgl_backend)] |
293 | Cgl(CglSurface<T>), |
294 | } |
295 | |
296 | impl<T: SurfaceTypeTrait> GlSurface<T> for Surface<T> { |
297 | type Context = PossiblyCurrentContext; |
298 | type SurfaceType = T; |
299 | |
300 | fn buffer_age(&self) -> u32 { |
301 | gl_api_dispatch!(self; Self(surface) => surface.buffer_age()) |
302 | } |
303 | |
304 | fn width(&self) -> Option<u32> { |
305 | gl_api_dispatch!(self; Self(surface) => surface.width()) |
306 | } |
307 | |
308 | fn height(&self) -> Option<u32> { |
309 | gl_api_dispatch!(self; Self(surface) => surface.height()) |
310 | } |
311 | |
312 | fn is_single_buffered(&self) -> bool { |
313 | gl_api_dispatch!(self; Self(surface) => surface.is_single_buffered()) |
314 | } |
315 | |
316 | fn swap_buffers(&self, context: &Self::Context) -> Result<()> { |
317 | match (self, context) { |
318 | #[cfg (egl_backend)] |
319 | (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => { |
320 | surface.swap_buffers(context) |
321 | }, |
322 | #[cfg (glx_backend)] |
323 | (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => { |
324 | surface.swap_buffers(context) |
325 | }, |
326 | #[cfg (cgl_backend)] |
327 | (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => { |
328 | surface.swap_buffers(context) |
329 | }, |
330 | #[cfg (wgl_backend)] |
331 | (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => { |
332 | surface.swap_buffers(context) |
333 | }, |
334 | _ => unreachable!(), |
335 | } |
336 | } |
337 | |
338 | fn set_swap_interval(&self, context: &Self::Context, interval: SwapInterval) -> Result<()> { |
339 | match (self, context) { |
340 | #[cfg (egl_backend)] |
341 | (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => { |
342 | surface.set_swap_interval(context, interval) |
343 | }, |
344 | #[cfg (glx_backend)] |
345 | (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => { |
346 | surface.set_swap_interval(context, interval) |
347 | }, |
348 | #[cfg (cgl_backend)] |
349 | (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => { |
350 | surface.set_swap_interval(context, interval) |
351 | }, |
352 | #[cfg (wgl_backend)] |
353 | (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => { |
354 | surface.set_swap_interval(context, interval) |
355 | }, |
356 | _ => unreachable!(), |
357 | } |
358 | } |
359 | |
360 | fn is_current(&self, context: &Self::Context) -> bool { |
361 | match (self, context) { |
362 | #[cfg (egl_backend)] |
363 | (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => { |
364 | surface.is_current(context) |
365 | }, |
366 | #[cfg (glx_backend)] |
367 | (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => { |
368 | surface.is_current(context) |
369 | }, |
370 | #[cfg (cgl_backend)] |
371 | (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => { |
372 | surface.is_current(context) |
373 | }, |
374 | #[cfg (wgl_backend)] |
375 | (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => { |
376 | surface.is_current(context) |
377 | }, |
378 | _ => unreachable!(), |
379 | } |
380 | } |
381 | |
382 | fn is_current_draw(&self, context: &Self::Context) -> bool { |
383 | match (self, context) { |
384 | #[cfg (egl_backend)] |
385 | (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => { |
386 | surface.is_current_draw(context) |
387 | }, |
388 | #[cfg (glx_backend)] |
389 | (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => { |
390 | surface.is_current_draw(context) |
391 | }, |
392 | #[cfg (cgl_backend)] |
393 | (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => { |
394 | surface.is_current_draw(context) |
395 | }, |
396 | #[cfg (wgl_backend)] |
397 | (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => { |
398 | surface.is_current_draw(context) |
399 | }, |
400 | _ => unreachable!(), |
401 | } |
402 | } |
403 | |
404 | fn is_current_read(&self, context: &Self::Context) -> bool { |
405 | match (self, context) { |
406 | #[cfg (egl_backend)] |
407 | (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => { |
408 | surface.is_current_read(context) |
409 | }, |
410 | #[cfg (glx_backend)] |
411 | (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => { |
412 | surface.is_current_read(context) |
413 | }, |
414 | #[cfg (cgl_backend)] |
415 | (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => { |
416 | surface.is_current_read(context) |
417 | }, |
418 | #[cfg (wgl_backend)] |
419 | (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => { |
420 | surface.is_current_read(context) |
421 | }, |
422 | _ => unreachable!(), |
423 | } |
424 | } |
425 | |
426 | fn resize(&self, context: &Self::Context, width: NonZeroU32, height: NonZeroU32) |
427 | where |
428 | Self::SurfaceType: ResizeableSurface, |
429 | { |
430 | match (self, context) { |
431 | #[cfg (egl_backend)] |
432 | (Self::Egl(surface), PossiblyCurrentContext::Egl(context)) => { |
433 | surface.resize(context, width, height) |
434 | }, |
435 | #[cfg (glx_backend)] |
436 | (Self::Glx(surface), PossiblyCurrentContext::Glx(context)) => { |
437 | surface.resize(context, width, height) |
438 | }, |
439 | #[cfg (cgl_backend)] |
440 | (Self::Cgl(surface), PossiblyCurrentContext::Cgl(context)) => { |
441 | surface.resize(context, width, height) |
442 | }, |
443 | #[cfg (wgl_backend)] |
444 | (Self::Wgl(surface), PossiblyCurrentContext::Wgl(context)) => { |
445 | surface.resize(context, width, height) |
446 | }, |
447 | _ => unreachable!(), |
448 | } |
449 | } |
450 | } |
451 | |
452 | impl<T: SurfaceTypeTrait> GetGlDisplay for Surface<T> { |
453 | type Target = Display; |
454 | |
455 | fn display(&self) -> Self::Target { |
456 | gl_api_dispatch!(self; Self(surface) => surface.display(); as Display) |
457 | } |
458 | } |
459 | |
460 | impl<T: SurfaceTypeTrait> AsRawSurface for Surface<T> { |
461 | fn raw_surface(&self) -> RawSurface { |
462 | gl_api_dispatch!(self; Self(surface) => surface.raw_surface()) |
463 | } |
464 | } |
465 | |
466 | impl<T: SurfaceTypeTrait> Sealed for Surface<T> {} |
467 | |
468 | /// A swap interval. |
469 | /// |
470 | /// The default swap interval for your [`Surface`] is platform-dependent. For |
471 | /// example, on EGL it is `1` by default, but on GLX it is `0` by default. |
472 | /// |
473 | /// Please note that your application's desired swap interval may be overridden |
474 | /// by external, driver-specific configuration, which means that you can't know |
475 | /// in advance whether [`crate::surface::GlSurface::swap_buffers`] will block |
476 | /// or not. |
477 | /// |
478 | /// # Platform specific |
479 | /// |
480 | /// - **Wayland:** when the window is hidden and [`SwapInterval::Wait`] is used |
481 | /// [`GlSurface::swap_buffers`] and any functions based on it may block until |
482 | /// the window is visible again. Using this variant is not recommended on |
483 | /// Wayland and instead the throttling should be performed by [`frame |
484 | /// callbacks`]. |
485 | /// |
486 | /// [`frame callbacks`]: https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_surface-request-frame |
487 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
488 | pub enum SwapInterval { |
489 | /// When this variant is used calling |
490 | /// [`crate::surface::GlSurface::swap_buffers()`] will not block. |
491 | DontWait, |
492 | |
493 | /// The swap is synchronized to the `n`'th video frame. This is typically |
494 | /// set to `1` to enable vsync and prevent screen tearing. |
495 | Wait(NonZeroU32), |
496 | } |
497 | |
498 | /// A platform native pixmap. |
499 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
500 | pub enum NativePixmap { |
501 | /// XID of X11 pixmap. |
502 | XlibPixmap(std::os::raw::c_ulong), |
503 | |
504 | /// XID of X11 pixmap from xcb. |
505 | XcbPixmap(u32), |
506 | |
507 | /// HBITMAP handle for windows bitmap. |
508 | WindowsPixmap(isize), |
509 | } |
510 | |
511 | /// Handle to the raw OpenGL surface. |
512 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
513 | pub enum RawSurface { |
514 | /// A pointer to EGLSurface. |
515 | #[cfg (egl_backend)] |
516 | Egl(*const std::ffi::c_void), |
517 | |
518 | /// GLXDrawable. |
519 | #[cfg (glx_backend)] |
520 | Glx(u64), |
521 | |
522 | /// Either a `HWND` or `HPBUFFEREXT` depending on [`SurfaceType`]. |
523 | #[cfg (wgl_backend)] |
524 | Wgl(*const std::ffi::c_void), |
525 | |
526 | /// Pointer to `NSView`. |
527 | #[cfg (cgl_backend)] |
528 | Cgl(*const std::ffi::c_void), |
529 | } |
530 | |
531 | /// The rect that is being used in various surface operations. |
532 | /// |
533 | /// The origin is in the bottom left of the surface. |
534 | #[repr (C)] |
535 | #[derive (Debug, Clone, Copy, Default, PartialEq, Eq, Hash)] |
536 | pub struct Rect { |
537 | /// `X` of the origin. |
538 | pub x: i32, |
539 | /// `Y` of the origin. |
540 | pub y: i32, |
541 | /// Rect width. |
542 | pub width: i32, |
543 | /// Rect height. |
544 | pub height: i32, |
545 | } |
546 | |
547 | impl Rect { |
548 | /// Helper to simplify rectangle creation. |
549 | pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self { |
550 | Self { x, y, width, height } |
551 | } |
552 | } |
553 | |