glutin/api/glx/
context.rs

1//! Everything related to `GLXContext`.
2
3use std::fmt;
4use std::marker::PhantomData;
5use std::ops::Deref;
6use std::os::raw::c_int;
7
8use glutin_glx_sys::glx::types::GLXContext;
9use glutin_glx_sys::{glx, glx_extra};
10
11use crate::config::GetGlConfig;
12use crate::context::{
13    self, AsRawContext, ContextApi, ContextAttributes, GlProfile, Priority, RawContext,
14    ReleaseBehavior, Robustness, Version,
15};
16use crate::display::{DisplayFeatures, GetGlDisplay};
17use crate::error::{ErrorKind, Result};
18use crate::prelude::*;
19use crate::private::Sealed;
20use crate::surface::SurfaceTypeTrait;
21
22use super::config::Config;
23use super::display::Display;
24use super::surface::Surface;
25
26impl Display {
27    pub(crate) unsafe fn create_context(
28        &self,
29        config: &Config,
30        context_attributes: &ContextAttributes,
31    ) -> Result<NotCurrentContext> {
32        let shared_context = if let Some(shared_context) =
33            context_attributes.shared_context.as_ref()
34        {
35            match shared_context {
36                RawContext::Glx(shared_context) => *shared_context,
37                #[allow(unreachable_patterns)]
38                _ => return Err(ErrorKind::NotSupported("incompatible context was passed").into()),
39            }
40        } else {
41            std::ptr::null()
42        };
43
44        let (context, supports_surfaceless) =
45            if self.inner.client_extensions.contains("GLX_ARB_create_context")
46                && self.inner.glx_extra.is_some()
47            {
48                self.create_context_arb(config, context_attributes, shared_context)?
49            } else {
50                (self.create_context_legacy(config, shared_context)?, false)
51            };
52
53        // Failed to create the context.
54        if context.is_null() {
55            return Err(ErrorKind::BadContext.into());
56        }
57
58        let config = config.clone();
59        let is_gles = matches!(context_attributes.api, Some(ContextApi::Gles(_)));
60        let inner = ContextInner {
61            display: self.clone(),
62            config,
63            raw: GlxContext(context),
64            is_gles,
65            supports_surfaceless,
66        };
67
68        Ok(NotCurrentContext::new(inner))
69    }
70
71    fn create_context_arb(
72        &self,
73        config: &Config,
74        context_attributes: &ContextAttributes,
75        shared_context: GLXContext,
76    ) -> Result<(GLXContext, bool)> {
77        let extra = self.inner.glx_extra.as_ref().unwrap();
78        let mut attrs = Vec::<c_int>::with_capacity(16);
79
80        // Check whether the ES context creation is supported.
81        let supports_es = self.inner.features.contains(DisplayFeatures::CREATE_ES_CONTEXT);
82
83        let (profile, version, supports_surfaceless) = match context_attributes.api {
84            api @ Some(ContextApi::OpenGl(_)) | api @ None => {
85                let version = api.and_then(|api| api.version());
86                let (profile, version) = context::pick_profile(context_attributes.profile, version);
87                let profile = match profile {
88                    GlProfile::Core => glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB,
89                    GlProfile::Compatibility => glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
90                };
91
92                // Surfaceless contexts are supported with the GLX_ARB_create_context extension
93                // when using OpenGL 3.0 or greater.
94                let supports_surfaceless = version >= Version::new(3, 0);
95
96                (Some(profile), Some(version), supports_surfaceless)
97            },
98            Some(ContextApi::Gles(version)) if supports_es => (
99                Some(glx_extra::CONTEXT_ES2_PROFILE_BIT_EXT),
100                Some(version.unwrap_or(Version::new(2, 0))),
101                false,
102            ),
103            _ => {
104                return Err(ErrorKind::NotSupported(
105                    "extension to create ES context with glx is not present.",
106                )
107                .into())
108            },
109        };
110
111        // Set the profile.
112        if let Some(profile) = profile {
113            attrs.push(glx_extra::CONTEXT_PROFILE_MASK_ARB as c_int);
114            attrs.push(profile as c_int);
115        }
116
117        // Add version.
118        if let Some(version) = version {
119            attrs.push(glx_extra::CONTEXT_MAJOR_VERSION_ARB as c_int);
120            attrs.push(version.major as c_int);
121            attrs.push(glx_extra::CONTEXT_MINOR_VERSION_ARB as c_int);
122            attrs.push(version.minor as c_int);
123        }
124
125        if let Some(profile) = context_attributes.profile {
126            let profile = match profile {
127                GlProfile::Core => glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB,
128                GlProfile::Compatibility => glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
129            };
130
131            attrs.push(glx_extra::CONTEXT_PROFILE_MASK_ARB as c_int);
132            attrs.push(profile as c_int);
133        }
134
135        let mut flags: c_int = 0;
136        let mut requested_no_error = false;
137        if self.inner.features.contains(DisplayFeatures::CONTEXT_ROBUSTNESS) {
138            match context_attributes.robustness {
139                Robustness::NotRobust => (),
140                Robustness::RobustNoResetNotification => {
141                    attrs.push(glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int);
142                    attrs.push(glx_extra::NO_RESET_NOTIFICATION_ARB as c_int);
143                    flags |= glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int;
144                },
145                Robustness::RobustLoseContextOnReset => {
146                    attrs.push(glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int);
147                    attrs.push(glx_extra::LOSE_CONTEXT_ON_RESET_ARB as c_int);
148                    flags |= glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int;
149                },
150                Robustness::NoError => {
151                    if !self.inner.features.contains(DisplayFeatures::CONTEXT_NO_ERROR) {
152                        return Err(ErrorKind::NotSupported(
153                            "GLX_ARB_create_context_no_error not supported",
154                        )
155                        .into());
156                    }
157
158                    attrs.push(glx_extra::CONTEXT_OPENGL_NO_ERROR_ARB as c_int);
159                    attrs.push(1);
160                    requested_no_error = true;
161                },
162            }
163        } else if context_attributes.robustness != Robustness::NotRobust {
164            return Err(ErrorKind::NotSupported(
165                "GLX_ARB_create_context_robustness is not supported",
166            )
167            .into());
168        }
169
170        // Debug flag.
171        if context_attributes.debug && !requested_no_error {
172            flags |= glx_extra::CONTEXT_DEBUG_BIT_ARB as c_int;
173        }
174
175        if flags != 0 {
176            attrs.push(glx_extra::CONTEXT_FLAGS_ARB as c_int);
177            attrs.push(flags as c_int);
178        }
179
180        // Flush control.
181        if self.inner.features.contains(DisplayFeatures::CONTEXT_RELEASE_BEHAVIOR) {
182            match context_attributes.release_behavior {
183                // This is the default behavior in specification.
184                //
185                // XXX passing it explicitly causing issues with older mesa versions.
186                ReleaseBehavior::Flush => (),
187                ReleaseBehavior::None => {
188                    attrs.push(glx_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int);
189                    attrs.push(glx_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as c_int);
190                },
191            }
192        } else if context_attributes.release_behavior != ReleaseBehavior::Flush {
193            return Err(ErrorKind::NotSupported(
194                "flush control behavior GLX_ARB_context_flush_control",
195            )
196            .into());
197        }
198
199        // Terminate list with zero.
200        attrs.push(0);
201
202        let context = super::last_glx_error(|| unsafe {
203            extra.CreateContextAttribsARB(
204                self.inner.raw.cast(),
205                *config.inner.raw,
206                shared_context,
207                // Direct context
208                1,
209                attrs.as_ptr(),
210            )
211        })?;
212
213        Ok((context, supports_surfaceless))
214    }
215
216    fn create_context_legacy(
217        &self,
218        config: &Config,
219        shared_context: GLXContext,
220    ) -> Result<GLXContext> {
221        let render_type =
222            if config.float_pixels() { glx_extra::RGBA_FLOAT_TYPE_ARB } else { glx::RGBA_TYPE };
223
224        super::last_glx_error(|| unsafe {
225            self.inner.glx.CreateNewContext(
226                self.inner.raw.cast(),
227                *config.inner.raw,
228                render_type as c_int,
229                shared_context,
230                // Direct context.
231                1,
232            )
233        })
234    }
235}
236
237/// A wrapper around `GLXContext` that is known to be not current.
238#[derive(Debug)]
239pub struct NotCurrentContext {
240    inner: ContextInner,
241}
242
243impl NotCurrentContext {
244    /// Make a [`Self::PossiblyCurrentContext`] indicating that the context
245    /// could be current on the thread.
246    ///
247    /// Requires the GLX_ARB_create_context extension and OpenGL 3.0 or greater.
248    pub fn make_current_surfaceless(self) -> Result<PossiblyCurrentContext> {
249        self.inner.make_current_surfaceless()?;
250        Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData })
251    }
252
253    fn new(inner: ContextInner) -> Self {
254        Self { inner }
255    }
256}
257
258impl NotCurrentGlContext for NotCurrentContext {
259    type PossiblyCurrentContext = PossiblyCurrentContext;
260    type Surface<T: SurfaceTypeTrait> = Surface<T>;
261
262    fn treat_as_possibly_current(self) -> PossiblyCurrentContext {
263        PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }
264    }
265
266    fn make_current<T: SurfaceTypeTrait>(
267        self,
268        surface: &Self::Surface<T>,
269    ) -> Result<Self::PossiblyCurrentContext> {
270        self.inner.make_current_draw_read(surface, surface)?;
271        Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData })
272    }
273
274    fn make_current_draw_read<T: SurfaceTypeTrait>(
275        self,
276        surface_draw: &Self::Surface<T>,
277        surface_read: &Self::Surface<T>,
278    ) -> Result<Self::PossiblyCurrentContext> {
279        self.inner.make_current_draw_read(surface_draw, surface_read)?;
280        Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData })
281    }
282}
283
284impl GlContext for NotCurrentContext {
285    fn context_api(&self) -> ContextApi {
286        self.inner.context_api()
287    }
288
289    fn priority(&self) -> Priority {
290        Priority::Medium
291    }
292}
293
294impl GetGlConfig for NotCurrentContext {
295    type Target = Config;
296
297    fn config(&self) -> Self::Target {
298        self.inner.config.clone()
299    }
300}
301
302impl GetGlDisplay for NotCurrentContext {
303    type Target = Display;
304
305    fn display(&self) -> Self::Target {
306        self.inner.display.clone()
307    }
308}
309
310impl AsRawContext for NotCurrentContext {
311    fn raw_context(&self) -> RawContext {
312        RawContext::Glx(*self.inner.raw)
313    }
314}
315
316impl Sealed for NotCurrentContext {}
317
318/// A wrapper around `GLXContext` that could be current for the current thread.
319#[derive(Debug)]
320pub struct PossiblyCurrentContext {
321    inner: ContextInner,
322    // The context could be current only on the one thread.
323    _nosendsync: PhantomData<GLXContext>,
324}
325
326impl PossiblyCurrentContext {
327    /// Make this context current on the calling thread.
328    ///
329    /// Requires the GLX_ARB_create_context extension and OpenGL 3.0 or greater.
330    pub fn make_current_surfaceless(&self) -> Result<()> {
331        self.inner.make_current_surfaceless()
332    }
333}
334
335impl PossiblyCurrentGlContext for PossiblyCurrentContext {
336    type NotCurrentContext = NotCurrentContext;
337    type Surface<T: SurfaceTypeTrait> = Surface<T>;
338
339    fn make_not_current(self) -> Result<Self::NotCurrentContext> {
340        self.make_not_current_in_place()?;
341        Ok(NotCurrentContext::new(self.inner))
342    }
343
344    fn make_not_current_in_place(&self) -> Result<()> {
345        self.inner.make_not_current()
346    }
347
348    fn is_current(&self) -> bool {
349        unsafe { self.inner.display.inner.glx.GetCurrentContext() == *self.inner.raw }
350    }
351
352    fn make_current<T: SurfaceTypeTrait>(&self, surface: &Self::Surface<T>) -> Result<()> {
353        self.inner.make_current_draw_read(surface, surface)
354    }
355
356    fn make_current_draw_read<T: SurfaceTypeTrait>(
357        &self,
358        surface_draw: &Self::Surface<T>,
359        surface_read: &Self::Surface<T>,
360    ) -> Result<()> {
361        self.inner.make_current_draw_read(surface_draw, surface_read)
362    }
363}
364
365impl GlContext for PossiblyCurrentContext {
366    fn context_api(&self) -> ContextApi {
367        self.inner.context_api()
368    }
369
370    fn priority(&self) -> Priority {
371        Priority::Medium
372    }
373}
374
375impl GetGlConfig for PossiblyCurrentContext {
376    type Target = Config;
377
378    fn config(&self) -> Self::Target {
379        self.inner.config.clone()
380    }
381}
382
383impl GetGlDisplay for PossiblyCurrentContext {
384    type Target = Display;
385
386    fn display(&self) -> Self::Target {
387        self.inner.display.clone()
388    }
389}
390
391impl AsRawContext for PossiblyCurrentContext {
392    fn raw_context(&self) -> RawContext {
393        RawContext::Glx(*self.inner.raw)
394    }
395}
396
397impl Sealed for PossiblyCurrentContext {}
398
399struct ContextInner {
400    display: Display,
401    config: Config,
402    raw: GlxContext,
403    is_gles: bool,
404    supports_surfaceless: bool,
405}
406
407impl ContextInner {
408    fn make_current_surfaceless(&self) -> Result<()> {
409        if !self.supports_surfaceless {
410            return Err(
411                ErrorKind::NotSupported("the surfaceless context Api isn't supported").into()
412            );
413        }
414
415        // Passing zero arguments for both `draw` and `read` parameters makes
416        // the context current without a default framebuffer.
417        super::last_glx_error(|| unsafe {
418            self.display.inner.glx.MakeContextCurrent(
419                self.display.inner.raw.cast(),
420                0,
421                0,
422                *self.raw,
423            );
424        })
425    }
426
427    fn make_current_draw_read<T: SurfaceTypeTrait>(
428        &self,
429        surface_draw: &Surface<T>,
430        surface_read: &Surface<T>,
431    ) -> Result<()> {
432        super::last_glx_error(|| unsafe {
433            self.display.inner.glx.MakeContextCurrent(
434                self.display.inner.raw.cast(),
435                surface_draw.raw,
436                surface_read.raw,
437                *self.raw,
438            );
439        })
440    }
441
442    fn make_not_current(&self) -> Result<()> {
443        super::last_glx_error(|| unsafe {
444            self.display.inner.glx.MakeContextCurrent(
445                self.display.inner.raw.cast(),
446                0,
447                0,
448                std::ptr::null(),
449            );
450        })
451    }
452
453    fn context_api(&self) -> ContextApi {
454        if self.is_gles {
455            ContextApi::Gles(None)
456        } else {
457            ContextApi::OpenGl(None)
458        }
459    }
460}
461
462impl Drop for ContextInner {
463    fn drop(&mut self) {
464        let _ = super::last_glx_error(|| unsafe {
465            self.display.inner.glx.DestroyContext(self.display.inner.raw.cast(), *self.raw);
466        });
467    }
468}
469
470impl fmt::Debug for ContextInner {
471    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
472        f.debug_struct("Context")
473            .field("display", &self.display.inner.raw)
474            .field("config", &self.config.inner.raw)
475            .field("raw", &self.raw)
476            .finish()
477    }
478}
479
480#[derive(Debug)]
481struct GlxContext(GLXContext);
482
483unsafe impl Send for GlxContext {}
484
485impl Deref for GlxContext {
486    type Target = GLXContext;
487
488    fn deref(&self) -> &Self::Target {
489        &self.0
490    }
491}