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