1//! OpenGL context creation and initialization.
2
3#![allow(unreachable_patterns)]
4use std::ffi;
5
6use raw_window_handle::RawWindowHandle;
7
8use crate::config::{Config, GetGlConfig};
9use crate::display::{Display, GetGlDisplay};
10use crate::error::Result;
11use crate::private::{gl_api_dispatch, Sealed};
12use crate::surface::{GlSurface, Surface, SurfaceTypeTrait};
13
14#[cfg(cgl_backend)]
15use crate::api::cgl::context::{
16 NotCurrentContext as NotCurrentCglContext, PossiblyCurrentContext as PossiblyCurrentCglContext,
17};
18#[cfg(egl_backend)]
19use crate::api::egl::context::{
20 NotCurrentContext as NotCurrentEglContext, PossiblyCurrentContext as PossiblyCurrentEglContext,
21};
22#[cfg(glx_backend)]
23use crate::api::glx::context::{
24 NotCurrentContext as NotCurrentGlxContext, PossiblyCurrentContext as PossiblyCurrentGlxContext,
25};
26#[cfg(wgl_backend)]
27use crate::api::wgl::context::{
28 NotCurrentContext as NotCurrentWglContext, PossiblyCurrentContext as PossiblyCurrentWglContext,
29};
30
31/// A trait to group common context operations.
32pub trait GlContext: Sealed {
33 /// Get the [`ContextApi`] used by the context.
34 ///
35 /// The returned value's [`Version`] will always be `None`.
36 fn context_api(&self) -> ContextApi;
37}
38
39/// A trait to group common not current operations.
40pub trait NotCurrentGlContext: Sealed {
41 /// The type of possibly current context.
42 type PossiblyCurrentContext: PossiblyCurrentGlContext;
43
44 /// The surface supported by the context.
45 type Surface<T: SurfaceTypeTrait>: GlSurface<T>;
46
47 /// Treat the not current context as possibly current. The operation is safe
48 /// because the possibly current context is more restricted and not
49 /// guaranteed to be current.
50 fn treat_as_possibly_current(self) -> Self::PossiblyCurrentContext;
51
52 /// Make [`Self::Surface`] on the calling thread producing the
53 /// [`Self::PossiblyCurrentContext`] indicating that the context could
54 /// be current on the theard.
55 ///
56 /// # Platform specific
57 ///
58 /// - **macOS: this will block if your main thread is blocked**;
59 /// - **Wayland:** this call may latch the underlying back buffer (will do
60 /// with mesa drivers), meaning that all resize operations will apply
61 /// after the next [`GlSurface::swap_buffers`].
62 fn make_current<T: SurfaceTypeTrait>(
63 self,
64 surface: &Self::Surface<T>,
65 ) -> Result<Self::PossiblyCurrentContext>;
66
67 /// The same as [`Self::make_current`], but provides a way to set read and
68 /// draw surfaces.
69 ///
70 /// # Api-specific:
71 ///
72 /// - **WGL/CGL:** not supported.
73 fn make_current_draw_read<T: SurfaceTypeTrait>(
74 self,
75 surface_draw: &Self::Surface<T>,
76 surface_read: &Self::Surface<T>,
77 ) -> Result<Self::PossiblyCurrentContext>;
78}
79
80/// A trait to group common context operations.
81pub trait PossiblyCurrentGlContext: Sealed {
82 /// The not current context type.
83 type NotCurrentContext: NotCurrentGlContext;
84
85 /// The surface supported by the context.
86 type Surface<T: SurfaceTypeTrait>: GlSurface<T>;
87
88 /// Returns `true` if this context is the current one in this thread.
89 fn is_current(&self) -> bool;
90
91 /// Make the context not current to the current thread and returns a
92 /// [`Self::NotCurrentContext`] to indicate that the context is a not
93 /// current to allow sending it to the different thread.
94 ///
95 /// # Platform specific
96 ///
97 /// - **macOS: this will block if your main thread is blocked.**
98 fn make_not_current(self) -> Result<Self::NotCurrentContext>;
99
100 /// Make [`Self::Surface`] current on the calling thread.
101 ///
102 /// # Platform specific
103 ///
104 /// - **macOS: this will block if your main thread is blocked.**
105 fn make_current<T: SurfaceTypeTrait>(&self, surface: &Self::Surface<T>) -> Result<()>;
106
107 /// The same as [`Self::make_current`] but provides a way to set read and
108 /// draw surfaces explicitly.
109 ///
110 /// # Api-specific:
111 ///
112 /// - **CGL/WGL:** not supported.
113 fn make_current_draw_read<T: SurfaceTypeTrait>(
114 &self,
115 surface_draw: &Self::Surface<T>,
116 surface_read: &Self::Surface<T>,
117 ) -> Result<()>;
118}
119
120/// A trait that provides raw context.
121pub trait AsRawContext {
122 /// Get the raw context handle.
123 fn raw_context(&self) -> RawContext;
124}
125
126/// The builder to help customizing context
127#[derive(Default, Debug, Clone)]
128pub struct ContextAttributesBuilder {
129 attributes: ContextAttributes,
130}
131
132impl ContextAttributesBuilder {
133 /// Create new builder.
134 pub fn new() -> Self {
135 Default::default()
136 }
137
138 /// Sets the *debug* flag for the OpenGL context.
139 ///
140 /// Debug contexts are usually slower, but give better error reporting.
141 /// This option is ignored when using [`Robustness::NoError`].
142 ///
143 /// The default value for this flag is `false`.
144 pub fn with_debug(mut self, debug: bool) -> Self {
145 self.attributes.debug = debug;
146 self
147 }
148
149 /// Share the display lists with the given context.
150 ///
151 /// To get sharing working it's recommended to use the same [`Config`] when
152 /// creating contexts that are going to be shared.
153 ///
154 /// # Platform-specific
155 ///
156 /// - **Wayland:** both contexts must use the same Wayland connection.
157 ///
158 /// [`Config`]: crate::config::Config
159 pub fn with_sharing(mut self, context: &impl AsRawContext) -> Self {
160 self.attributes.shared_context = Some(context.raw_context());
161 self
162 }
163
164 /// Sets the robustness of the OpenGL context. See the docs of
165 /// [`Robustness`].
166 ///
167 /// The default is [`Robustness::NotRobust`], because this is what typically
168 /// expected when you create an OpenGL context. However for safety you
169 /// should consider [`Robustness::RobustLoseContextOnReset`].
170 pub fn with_robustness(mut self, robustness: Robustness) -> Self {
171 self.attributes.robustness = robustness;
172 self
173 }
174
175 /// The behavior when changing the current context. See the docs of
176 /// [`ReleaseBehavior`].
177 ///
178 /// The default is [`ReleaseBehavior::Flush`].
179 pub fn with_release_behavior(mut self, release_behavior: ReleaseBehavior) -> Self {
180 self.attributes.release_behavior = release_behavior;
181 self
182 }
183
184 /// Set the desired OpenGL context profile. See the docs of [`GlProfile`].
185 ///
186 /// By default the profile is unspecified.
187 ///
188 /// # Api-specific
189 ///
190 /// - **macOS:** not supported, the latest is picked automatically.
191 pub fn with_profile(mut self, profile: GlProfile) -> Self {
192 self.attributes.profile = Some(profile);
193 self
194 }
195
196 /// Set the desired OpenGL context api. See the docs of [`ContextApi`].
197 ///
198 /// By default the supported api will be picked.
199 pub fn with_context_api(mut self, api: ContextApi) -> Self {
200 self.attributes.api = Some(api);
201 self
202 }
203
204 /// Build the context attributes.
205 ///
206 /// The `raw_window_handle` isn't required and here for WGL compatibility.
207 ///
208 /// # Api-specific
209 ///
210 /// - **WGL:** you **must** pass a `raw_window_handle` if you plan to use
211 /// this context with that window.
212 pub fn build(mut self, raw_window_handle: Option<RawWindowHandle>) -> ContextAttributes {
213 self.attributes.raw_window_handle = raw_window_handle;
214 self.attributes
215 }
216}
217
218/// The attributes that are used to create a graphics context.
219#[derive(Default, Debug, Clone)]
220pub struct ContextAttributes {
221 pub(crate) release_behavior: ReleaseBehavior,
222
223 pub(crate) debug: bool,
224
225 pub(crate) robustness: Robustness,
226
227 pub(crate) profile: Option<GlProfile>,
228
229 pub(crate) api: Option<ContextApi>,
230
231 pub(crate) shared_context: Option<RawContext>,
232
233 pub(crate) raw_window_handle: Option<RawWindowHandle>,
234}
235
236/// Specifies the tolerance of the OpenGL context to faults. If you accept
237/// raw OpenGL commands and/or raw shader code from an untrusted source, you
238/// should definitely care about this.
239#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
240pub enum Robustness {
241 /// Not everything is checked. Your application can crash if you do
242 /// something wrong with your shaders.
243 #[default]
244 NotRobust,
245
246 /// The driver doesn't check anything. This option is very dangerous.
247 /// Please know what you're doing before using it. See the
248 /// `GL_KHR_no_error` extension.
249 ///
250 /// Since this option is purely an optimization, no error will be returned
251 /// if the backend doesn't support it. Instead it will automatically
252 /// fall back to [`Robustness::NotRobust`].
253 NoError,
254
255 /// Everything is checked to avoid any crash. The driver will attempt to
256 /// avoid any problem, but if a problem occurs the behavior is
257 /// implementation-defined. You are just guaranteed not to get a crash.
258 RobustNoResetNotification,
259
260 /// Everything is checked to avoid any crash. If a problem occurs, the
261 /// context will enter a "context lost" state. It must then be
262 /// recreated.
263 RobustLoseContextOnReset,
264}
265
266/// Describes the requested OpenGL context profiles.
267#[derive(Debug, Clone, Copy, PartialEq, Eq)]
268pub enum GlProfile {
269 /// Include all the future-compatible functions and definitions.
270 ///
271 /// The requested OpenGL version with [`ContextApi`] should be at least 3.3.
272 Core,
273 /// Include all the immediate more functions and definitions.
274 ///
275 /// Use it only when it's really needed, otherwise use [`Self::Core`].
276 Compatibility,
277}
278
279/// The rendering Api context should support.
280#[derive(Debug, Clone, Copy, PartialEq, Eq)]
281pub enum ContextApi {
282 /// OpenGL Api version that should be used by the context.
283 ///
284 /// When using `None` as `Version` any OpenGL context will be picked,
285 /// however when the [`GlProfile::Core`] is used at least 3.3 will be
286 /// requested.
287 OpenGl(Option<Version>),
288
289 /// OpenGL Api version that should be used by the context.
290 ///
291 /// When using `None` as `Version` the latest **known** major version is
292 /// picked. Versions that are higher than what was picked automatically
293 /// could still be supported.
294 Gles(Option<Version>),
295}
296
297#[cfg(any(egl_backend, glx_backend, wgl_backend))]
298impl ContextApi {
299 pub(crate) fn version(&self) -> Option<Version> {
300 match self {
301 Self::OpenGl(version: &Option) => *version,
302 Self::Gles(version: &Option) => *version,
303 _ => None,
304 }
305 }
306}
307
308impl Default for ContextApi {
309 fn default() -> Self {
310 Self::OpenGl(None)
311 }
312}
313
314/// The version used to index the Api.
315#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
316pub struct Version {
317 /// Major version of the Api.
318 pub major: u8,
319 /// Minor version of the Api.
320 pub minor: u8,
321}
322
323impl Version {
324 /// Create new version with the given `major` and `minor` values.
325 pub const fn new(major: u8, minor: u8) -> Self {
326 Self { major, minor }
327 }
328}
329
330/// The behavior of the driver when you change the current context.
331#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
332pub enum ReleaseBehavior {
333 /// Doesn't do anything. Most notably doesn't flush. Not supported by all
334 /// drivers.
335 ///
336 /// # Api-specific
337 ///
338 /// - **macOS:** not supported, [`Self::Flush`] is always used.
339 None,
340
341 /// Flushes the context that was previously current as if `glFlush` was
342 /// called. This is the default behavior.
343 #[default]
344 Flush,
345}
346
347/// A context that is known to be not current on the current thread.
348///
349/// This type is a safe wrapper around the context to indicate that it could be
350/// `Send` to the different thread, since the context must be not current before
351/// doing so.
352///
353/// ```no_run
354/// fn test_send<T: Send>() {}
355/// test_send::<glutin::context::NotCurrentContext>();
356/// ```
357/// However it's not `Sync`.
358/// ```compile_fail
359/// fn test_sync<T: Sync>() {}
360/// test_sync::<glutin::context::NotCurrentContext>();
361/// ```
362#[derive(Debug)]
363pub enum NotCurrentContext {
364 /// The EGL context.
365 #[cfg(egl_backend)]
366 Egl(NotCurrentEglContext),
367
368 /// The GLX context.
369 #[cfg(glx_backend)]
370 Glx(NotCurrentGlxContext),
371
372 /// The WGL context.
373 #[cfg(wgl_backend)]
374 Wgl(NotCurrentWglContext),
375
376 /// The CGL context.
377 #[cfg(cgl_backend)]
378 Cgl(NotCurrentCglContext),
379}
380
381impl NotCurrentGlContext for NotCurrentContext {
382 type PossiblyCurrentContext = PossiblyCurrentContext;
383 type Surface<T: SurfaceTypeTrait> = Surface<T>;
384
385 fn treat_as_possibly_current(self) -> Self::PossiblyCurrentContext {
386 gl_api_dispatch!(self; Self(context) => context.treat_as_possibly_current(); as PossiblyCurrentContext)
387 }
388
389 fn make_current<T: SurfaceTypeTrait>(
390 self,
391 surface: &Self::Surface<T>,
392 ) -> Result<Self::PossiblyCurrentContext> {
393 match (self, surface) {
394 #[cfg(egl_backend)]
395 (Self::Egl(context), Surface::Egl(surface)) => {
396 Ok(PossiblyCurrentContext::Egl(context.make_current(surface)?))
397 },
398 #[cfg(glx_backend)]
399 (Self::Glx(context), Surface::Glx(surface)) => {
400 Ok(PossiblyCurrentContext::Glx(context.make_current(surface)?))
401 },
402 #[cfg(wgl_backend)]
403 (Self::Wgl(context), Surface::Wgl(surface)) => {
404 Ok(PossiblyCurrentContext::Wgl(context.make_current(surface)?))
405 },
406 #[cfg(cgl_backend)]
407 (Self::Cgl(context), Surface::Cgl(surface)) => {
408 Ok(PossiblyCurrentContext::Cgl(context.make_current(surface)?))
409 },
410 _ => unreachable!(),
411 }
412 }
413
414 fn make_current_draw_read<T: SurfaceTypeTrait>(
415 self,
416 surface_draw: &Self::Surface<T>,
417 surface_read: &Self::Surface<T>,
418 ) -> Result<Self::PossiblyCurrentContext> {
419 match (self, surface_draw, surface_read) {
420 #[cfg(egl_backend)]
421 (Self::Egl(context), Surface::Egl(draw), Surface::Egl(read)) => {
422 Ok(PossiblyCurrentContext::Egl(context.make_current_draw_read(draw, read)?))
423 },
424 #[cfg(glx_backend)]
425 (Self::Glx(context), Surface::Glx(draw), Surface::Glx(read)) => {
426 Ok(PossiblyCurrentContext::Glx(context.make_current_draw_read(draw, read)?))
427 },
428 #[cfg(wgl_backend)]
429 (Self::Wgl(context), Surface::Wgl(draw), Surface::Wgl(read)) => {
430 Ok(PossiblyCurrentContext::Wgl(context.make_current_draw_read(draw, read)?))
431 },
432 #[cfg(cgl_backend)]
433 (Self::Cgl(context), Surface::Cgl(draw), Surface::Cgl(read)) => {
434 Ok(PossiblyCurrentContext::Cgl(context.make_current_draw_read(draw, read)?))
435 },
436 _ => unreachable!(),
437 }
438 }
439}
440
441impl GlContext for NotCurrentContext {
442 fn context_api(&self) -> ContextApi {
443 gl_api_dispatch!(self; Self(context) => context.context_api())
444 }
445}
446
447impl GetGlConfig for NotCurrentContext {
448 type Target = Config;
449
450 fn config(&self) -> Self::Target {
451 gl_api_dispatch!(self; Self(context) => context.config(); as Config)
452 }
453}
454
455impl GetGlDisplay for NotCurrentContext {
456 type Target = Display;
457
458 fn display(&self) -> Self::Target {
459 gl_api_dispatch!(self; Self(context) => context.display(); as Display)
460 }
461}
462
463impl AsRawContext for NotCurrentContext {
464 fn raw_context(&self) -> RawContext {
465 gl_api_dispatch!(self; Self(context) => context.raw_context())
466 }
467}
468
469impl Sealed for NotCurrentContext {}
470
471/// A context that is possibly current on the current thread.
472///
473/// The context that could be current on the current thread can neither be
474/// [`Send`] nor [`Sync`]. In case you need to use it on a different thread
475/// [make it not current].
476/// ```compile_fail
477/// fn test_send<T: Send>() {}
478/// test_send::<glutin::context::PossiblyCurrentContext>();
479/// ```
480///
481/// ```compile_fail
482/// fn test_sync<T: Sync>() {}
483/// test_sync::<glutin::context::PossiblyCurrentContext>();
484/// ```
485///
486/// [make it not current]: crate::context::PossiblyCurrentGlContext::make_not_current
487#[derive(Debug)]
488pub enum PossiblyCurrentContext {
489 /// The EGL context.
490 #[cfg(egl_backend)]
491 Egl(PossiblyCurrentEglContext),
492
493 /// The GLX context.
494 #[cfg(glx_backend)]
495 Glx(PossiblyCurrentGlxContext),
496
497 /// The WGL context.
498 #[cfg(wgl_backend)]
499 Wgl(PossiblyCurrentWglContext),
500
501 /// The CGL context.
502 #[cfg(cgl_backend)]
503 Cgl(PossiblyCurrentCglContext),
504}
505
506impl PossiblyCurrentGlContext for PossiblyCurrentContext {
507 type NotCurrentContext = NotCurrentContext;
508 type Surface<T: SurfaceTypeTrait> = Surface<T>;
509
510 fn is_current(&self) -> bool {
511 gl_api_dispatch!(self; Self(context) => context.is_current())
512 }
513
514 fn make_not_current(self) -> Result<Self::NotCurrentContext> {
515 Ok(
516 gl_api_dispatch!(self; Self(context) => context.make_not_current()?; as NotCurrentContext),
517 )
518 }
519
520 fn make_current<T: SurfaceTypeTrait>(&self, surface: &Self::Surface<T>) -> Result<()> {
521 match (self, surface) {
522 #[cfg(egl_backend)]
523 (Self::Egl(context), Surface::Egl(surface)) => context.make_current(surface),
524 #[cfg(glx_backend)]
525 (Self::Glx(context), Surface::Glx(surface)) => context.make_current(surface),
526 #[cfg(wgl_backend)]
527 (Self::Wgl(context), Surface::Wgl(surface)) => context.make_current(surface),
528 #[cfg(cgl_backend)]
529 (Self::Cgl(context), Surface::Cgl(surface)) => context.make_current(surface),
530 _ => unreachable!(),
531 }
532 }
533
534 fn make_current_draw_read<T: SurfaceTypeTrait>(
535 &self,
536 surface_draw: &Self::Surface<T>,
537 surface_read: &Self::Surface<T>,
538 ) -> Result<()> {
539 match (self, surface_draw, surface_read) {
540 #[cfg(egl_backend)]
541 (Self::Egl(context), Surface::Egl(draw), Surface::Egl(read)) => {
542 context.make_current_draw_read(draw, read)
543 },
544 #[cfg(glx_backend)]
545 (Self::Glx(context), Surface::Glx(draw), Surface::Glx(read)) => {
546 context.make_current_draw_read(draw, read)
547 },
548 #[cfg(wgl_backend)]
549 (Self::Wgl(context), Surface::Wgl(draw), Surface::Wgl(read)) => {
550 context.make_current_draw_read(draw, read)
551 },
552 #[cfg(cgl_backend)]
553 (Self::Cgl(context), Surface::Cgl(draw), Surface::Cgl(read)) => {
554 context.make_current_draw_read(draw, read)
555 },
556 _ => unreachable!(),
557 }
558 }
559}
560
561impl GlContext for PossiblyCurrentContext {
562 fn context_api(&self) -> ContextApi {
563 gl_api_dispatch!(self; Self(context) => context.context_api())
564 }
565}
566
567impl GetGlConfig for PossiblyCurrentContext {
568 type Target = Config;
569
570 fn config(&self) -> Self::Target {
571 gl_api_dispatch!(self; Self(context) => context.config(); as Config)
572 }
573}
574
575impl GetGlDisplay for PossiblyCurrentContext {
576 type Target = Display;
577
578 fn display(&self) -> Self::Target {
579 gl_api_dispatch!(self; Self(context) => context.display(); as Display)
580 }
581}
582
583impl AsRawContext for PossiblyCurrentContext {
584 fn raw_context(&self) -> RawContext {
585 gl_api_dispatch!(self; Self(context) => context.raw_context())
586 }
587}
588
589impl Sealed for PossiblyCurrentContext {}
590
591/// Raw context.
592#[derive(Debug, Clone, Copy, PartialEq, Eq)]
593pub enum RawContext {
594 /// Raw EGL context.
595 #[cfg(egl_backend)]
596 Egl(*const ffi::c_void),
597
598 /// Raw GLX context.
599 #[cfg(glx_backend)]
600 Glx(*const ffi::c_void),
601
602 /// HGLRC pointer.
603 #[cfg(wgl_backend)]
604 Wgl(*const ffi::c_void),
605
606 /// Pointer to NSOpenGLContext.
607 #[cfg(cgl_backend)]
608 Cgl(*const ffi::c_void),
609}
610
611/// Pick `GlProfile` and `Version` based on the provided params.
612#[cfg(any(egl_backend, glx_backend, wgl_backend))]
613pub(crate) fn pick_profile(
614 profile: Option<GlProfile>,
615 version: Option<Version>,
616) -> (GlProfile, Version) {
617 match (profile, version) {
618 (Some(GlProfile::Core), Some(version: Version)) => (GlProfile::Core, version),
619 (Some(GlProfile::Compatibility), Some(version: Version)) => (GlProfile::Compatibility, version),
620 (None, Some(version: Version)) if version >= Version::new(major:3, minor:3) => (GlProfile::Core, version),
621 (None, Some(version: Version)) => (GlProfile::Compatibility, version),
622 (Some(GlProfile::Core), None) => (GlProfile::Core, Version::new(major:3, minor:3)),
623 (Some(GlProfile::Compatibility), None) => (GlProfile::Compatibility, Version::new(major:2, minor:1)),
624 (None, None) => (GlProfile::Core, Version::new(major:3, minor:3)),
625 }
626}
627