glutin/api/egl/
context.rs

1//! Everything related to `EGLContext` management.
2
3use std::fmt;
4use std::marker::PhantomData;
5use std::ops::Deref;
6
7use glutin_egl_sys::egl::types::{EGLenum, EGLint};
8use glutin_egl_sys::{egl, EGLContext};
9
10use crate::config::{Api, GetGlConfig};
11use crate::context::{
12    self, AsRawContext, ContextApi, ContextAttributes, GlProfile, Priority, RawContext, Robustness,
13    Version,
14};
15use crate::display::{DisplayFeatures, GetGlDisplay};
16use crate::error::{ErrorKind, Result};
17use crate::prelude::*;
18use crate::private::Sealed;
19use crate::surface::SurfaceTypeTrait;
20
21use super::config::Config;
22use super::display::Display;
23use super::surface::Surface;
24
25impl Display {
26    pub(crate) unsafe fn create_context(
27        &self,
28        config: &Config,
29        context_attributes: &ContextAttributes,
30    ) -> Result<NotCurrentContext> {
31        let mut attrs = Vec::<EGLint>::new();
32
33        let supports_opengl = self.inner.version > Version::new(1, 3);
34        let config_api = config.api();
35
36        let (api, mut version) = match context_attributes.api {
37            api @ Some(ContextApi::OpenGl(_)) | api @ None
38                if supports_opengl && config_api.contains(Api::OPENGL) =>
39            {
40                (egl::OPENGL_API, api.and_then(|api| api.version()))
41            },
42            api @ Some(ContextApi::Gles(_)) | api @ None => {
43                let version = match api.and_then(|api| api.version()) {
44                    Some(version) => version,
45                    None if config_api.contains(Api::GLES3) => Version::new(3, 0),
46                    None if config_api.contains(Api::GLES2) => Version::new(2, 0),
47                    _ => Version::new(1, 0),
48                };
49                (egl::OPENGL_ES_API, Some(version))
50            },
51            _ => {
52                return Err(
53                    ErrorKind::NotSupported("the requested context Api isn't supported.").into()
54                )
55            },
56        };
57
58        let is_one_five = self.inner.version >= Version::new(1, 5);
59        if is_one_five || self.inner.display_extensions.contains("EGL_KHR_create_context") {
60            let mut flags = 0;
61
62            // Add profile for the OpenGL Api.
63            if api == egl::OPENGL_API {
64                let (profile, new_version) =
65                    context::pick_profile(context_attributes.profile, version);
66                version = Some(new_version);
67                let profile = match profile {
68                    GlProfile::Core => egl::CONTEXT_OPENGL_CORE_PROFILE_BIT,
69                    GlProfile::Compatibility => egl::CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT,
70                };
71
72                attrs.push(egl::CONTEXT_OPENGL_PROFILE_MASK as EGLint);
73                attrs.push(profile as EGLint);
74            }
75
76            if let Some(version) = version {
77                attrs.push(egl::CONTEXT_MAJOR_VERSION as EGLint);
78                attrs.push(version.major as EGLint);
79                attrs.push(egl::CONTEXT_MINOR_VERSION as EGLint);
80                attrs.push(version.minor as EGLint);
81            }
82
83            let has_robustsess = self.inner.features.contains(DisplayFeatures::CONTEXT_ROBUSTNESS);
84
85            let mut requested_no_error = false;
86            match context_attributes.robustness {
87                Robustness::NotRobust => (),
88                Robustness::NoError
89                    if self.inner.features.contains(DisplayFeatures::CONTEXT_NO_ERROR) =>
90                {
91                    attrs.push(egl::CONTEXT_OPENGL_NO_ERROR_KHR as EGLint);
92                    attrs.push(egl::TRUE as EGLint);
93                    requested_no_error = true;
94                },
95                Robustness::RobustLoseContextOnReset if has_robustsess => {
96                    attrs.push(egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as EGLint);
97                    attrs.push(egl::LOSE_CONTEXT_ON_RESET as EGLint);
98                    flags |= egl::CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR;
99                },
100                Robustness::RobustNoResetNotification if has_robustsess => {
101                    attrs.push(egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as EGLint);
102                    attrs.push(egl::NO_RESET_NOTIFICATION as EGLint);
103                    flags |= egl::CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR;
104                },
105                _ => {
106                    return Err(
107                        ErrorKind::NotSupported("context robustness is not supported").into()
108                    )
109                },
110            }
111
112            if context_attributes.debug && is_one_five && !requested_no_error {
113                attrs.push(egl::CONTEXT_OPENGL_DEBUG as EGLint);
114                attrs.push(egl::TRUE as EGLint);
115            }
116
117            if flags != 0 {
118                attrs.push(egl::CONTEXT_FLAGS_KHR as EGLint);
119                attrs.push(flags as EGLint);
120            }
121        } else if self.inner.version >= Version::new(1, 3) {
122            // EGL 1.3 uses that to indicate client version instead of major/minor. The
123            // constant is the same as `CONTEXT_MAJOR_VERSION`.
124            if let Some(version) = version {
125                attrs.push(egl::CONTEXT_CLIENT_VERSION as EGLint);
126                attrs.push(version.major as EGLint);
127            }
128        }
129
130        if let Some(priority) = context_attributes.priority.filter(|_| {
131            let extensions = &self.inner.display_extensions;
132
133            // Some android versions don't report support for this extension, even though
134            // it's supported.
135            //
136            // https://github.com/googlevr/gvr-android-sdk/issues/330
137            #[cfg(android_platform)]
138            let android = extensions.contains("EGL_ANDROID_front_buffer_auto_refresh")
139                && extensions.contains("EGL_ANDROID_create_native_client_buffer");
140            #[cfg(not(android_platform))]
141            let android = false;
142
143            extensions.contains("EGL_IMG_context_priority") || android
144        }) {
145            let priority = match priority {
146                Priority::Low => egl::CONTEXT_PRIORITY_LOW_IMG,
147                Priority::Medium => egl::CONTEXT_PRIORITY_MEDIUM_IMG,
148                Priority::High => egl::CONTEXT_PRIORITY_HIGH_IMG,
149                Priority::Realtime => {
150                    if self.inner.display_extensions.contains("EGL_NV_context_priority_realtime") {
151                        egl::CONTEXT_PRIORITY_REALTIME_NV
152                    } else {
153                        egl::CONTEXT_PRIORITY_HIGH_IMG
154                    }
155                },
156            };
157
158            attrs.push(egl::CONTEXT_PRIORITY_LEVEL_IMG as EGLint);
159            attrs.push(priority as EGLint);
160        }
161
162        attrs.push(egl::NONE as EGLint);
163
164        let shared_context = if let Some(shared_context) =
165            context_attributes.shared_context.as_ref()
166        {
167            match shared_context {
168                RawContext::Egl(shared_context) => *shared_context,
169                #[allow(unreachable_patterns)]
170                _ => return Err(ErrorKind::NotSupported("passed incompatible raw context").into()),
171            }
172        } else {
173            egl::NO_CONTEXT
174        };
175
176        // Bind the api.
177        unsafe {
178            if self.inner.egl.BindAPI(api) == egl::FALSE {
179                return Err(super::check_error().err().unwrap());
180            }
181
182            let config = config.clone();
183            let context = self.inner.egl.CreateContext(
184                *self.inner.raw,
185                *config.inner.raw,
186                shared_context,
187                attrs.as_ptr(),
188            );
189
190            if context == egl::NO_CONTEXT {
191                return Err(super::check_error().err().unwrap());
192            }
193
194            let inner =
195                ContextInner { display: self.clone(), config, raw: EglContext(context), api };
196            Ok(NotCurrentContext::new(inner))
197        }
198    }
199}
200
201/// A wrapper around `EGLContext` that is known to be not current.
202#[derive(Debug)]
203pub struct NotCurrentContext {
204    inner: ContextInner,
205}
206
207impl NotCurrentContext {
208    /// Make a [`Self::PossiblyCurrentContext`] indicating that the context
209    /// could be current on the thread.
210    pub fn make_current_surfaceless(self) -> Result<PossiblyCurrentContext> {
211        self.inner.make_current_surfaceless()?;
212        Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData })
213    }
214
215    fn new(inner: ContextInner) -> Self {
216        Self { inner }
217    }
218}
219
220impl NotCurrentGlContext for NotCurrentContext {
221    type PossiblyCurrentContext = PossiblyCurrentContext;
222    type Surface<T: SurfaceTypeTrait> = Surface<T>;
223
224    fn treat_as_possibly_current(self) -> Self::PossiblyCurrentContext {
225        PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }
226    }
227
228    fn make_current<T: SurfaceTypeTrait>(
229        self,
230        surface: &Surface<T>,
231    ) -> Result<PossiblyCurrentContext> {
232        self.inner.make_current_draw_read(surface, surface)?;
233        Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData })
234    }
235
236    fn make_current_draw_read<T: SurfaceTypeTrait>(
237        self,
238        surface_draw: &Surface<T>,
239        surface_read: &Surface<T>,
240    ) -> Result<PossiblyCurrentContext> {
241        self.inner.make_current_draw_read(surface_draw, surface_read)?;
242        Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData })
243    }
244}
245
246impl GlContext for NotCurrentContext {
247    fn context_api(&self) -> ContextApi {
248        self.inner.context_api()
249    }
250
251    fn priority(&self) -> Priority {
252        self.inner.priority()
253    }
254}
255
256impl GetGlConfig for NotCurrentContext {
257    type Target = Config;
258
259    fn config(&self) -> Self::Target {
260        self.inner.config.clone()
261    }
262}
263
264impl GetGlDisplay for NotCurrentContext {
265    type Target = Display;
266
267    fn display(&self) -> Self::Target {
268        self.inner.display.clone()
269    }
270}
271
272impl AsRawContext for NotCurrentContext {
273    fn raw_context(&self) -> RawContext {
274        RawContext::Egl(*self.inner.raw)
275    }
276}
277
278impl Sealed for NotCurrentContext {}
279
280/// A wrapper around `EGLContext` that could be current for the current thread.
281#[derive(Debug)]
282pub struct PossiblyCurrentContext {
283    pub(crate) inner: ContextInner,
284    _nosendsync: PhantomData<EGLContext>,
285}
286
287impl PossiblyCurrentContext {
288    /// Make this context current on the calling thread.
289    pub fn make_current_surfaceless(&self) -> Result<()> {
290        self.inner.make_current_surfaceless()
291    }
292}
293
294impl PossiblyCurrentGlContext for PossiblyCurrentContext {
295    type NotCurrentContext = NotCurrentContext;
296    type Surface<T: SurfaceTypeTrait> = Surface<T>;
297
298    fn make_not_current(self) -> Result<Self::NotCurrentContext> {
299        self.make_not_current_in_place()?;
300        Ok(NotCurrentContext::new(self.inner))
301    }
302
303    fn make_not_current_in_place(&self) -> Result<()> {
304        self.inner.make_not_current()
305    }
306
307    fn is_current(&self) -> bool {
308        unsafe {
309            self.inner.bind_api();
310            self.inner.display.inner.egl.GetCurrentContext() == *self.inner.raw
311        }
312    }
313
314    fn make_current<T: SurfaceTypeTrait>(&self, surface: &Self::Surface<T>) -> Result<()> {
315        self.inner.make_current_draw_read(surface, surface)
316    }
317
318    fn make_current_draw_read<T: SurfaceTypeTrait>(
319        &self,
320        surface_draw: &Self::Surface<T>,
321        surface_read: &Self::Surface<T>,
322    ) -> Result<()> {
323        self.inner.make_current_draw_read(surface_draw, surface_read)
324    }
325}
326
327impl GlContext for PossiblyCurrentContext {
328    fn context_api(&self) -> ContextApi {
329        self.inner.context_api()
330    }
331
332    fn priority(&self) -> Priority {
333        self.inner.priority()
334    }
335}
336
337impl GetGlConfig for PossiblyCurrentContext {
338    type Target = Config;
339
340    fn config(&self) -> Self::Target {
341        self.inner.config.clone()
342    }
343}
344
345impl GetGlDisplay for PossiblyCurrentContext {
346    type Target = Display;
347
348    fn display(&self) -> Self::Target {
349        self.inner.display.clone()
350    }
351}
352
353impl AsRawContext for PossiblyCurrentContext {
354    fn raw_context(&self) -> RawContext {
355        RawContext::Egl(*self.inner.raw)
356    }
357}
358
359impl Sealed for PossiblyCurrentContext {}
360
361pub(crate) struct ContextInner {
362    display: Display,
363    config: Config,
364    raw: EglContext,
365    api: egl::types::EGLenum,
366}
367
368impl ContextInner {
369    fn make_current_surfaceless(&self) -> Result<()> {
370        unsafe {
371            if self.display.inner.egl.MakeCurrent(
372                *self.display.inner.raw,
373                egl::NO_SURFACE,
374                egl::NO_SURFACE,
375                *self.raw,
376            ) == egl::FALSE
377            {
378                super::check_error()
379            } else {
380                Ok(())
381            }
382        }
383    }
384
385    fn make_current_draw_read<T: SurfaceTypeTrait>(
386        &self,
387        surface_draw: &Surface<T>,
388        surface_read: &Surface<T>,
389    ) -> Result<()> {
390        unsafe {
391            let draw = surface_draw.raw;
392            let read = surface_read.raw;
393            if self.display.inner.egl.MakeCurrent(*self.display.inner.raw, draw, read, *self.raw)
394                == egl::FALSE
395            {
396                super::check_error()
397            } else {
398                Ok(())
399            }
400        }
401    }
402
403    fn make_not_current(&self) -> Result<()> {
404        unsafe {
405            self.bind_api();
406
407            if self.display.inner.egl.MakeCurrent(
408                *self.display.inner.raw,
409                egl::NO_SURFACE,
410                egl::NO_SURFACE,
411                egl::NO_CONTEXT,
412            ) == egl::FALSE
413            {
414                super::check_error()
415            } else {
416                Ok(())
417            }
418        }
419    }
420
421    fn context_api(&self) -> ContextApi {
422        match self.query_attribute(egl::CONTEXT_CLIENT_TYPE as EGLint).map(|a| a as EGLenum) {
423            Some(egl::OPENGL_API) => ContextApi::OpenGl(None),
424            // Map the rest to the GLES.
425            _ => ContextApi::Gles(None),
426        }
427    }
428
429    fn priority(&self) -> Priority {
430        match self.query_attribute(egl::CONTEXT_PRIORITY_LEVEL_IMG as EGLint).map(|a| a as EGLenum)
431        {
432            Some(egl::CONTEXT_PRIORITY_LOW_IMG) => Priority::Low,
433            Some(egl::CONTEXT_PRIORITY_HIGH_IMG) => Priority::High,
434            Some(egl::CONTEXT_PRIORITY_REALTIME_NV) => Priority::Realtime,
435            _ => Priority::Medium,
436        }
437    }
438
439    /// Query the context attribute.
440    fn query_attribute(&self, attribute: EGLint) -> Option<EGLint> {
441        unsafe {
442            let mut attribute_value = 0;
443            if self.display.inner.egl.QueryContext(
444                self.display.inner.raw.cast(),
445                self.raw.cast(),
446                attribute,
447                &mut attribute_value,
448            ) == egl::FALSE
449            {
450                None
451            } else {
452                Some(attribute_value)
453            }
454        }
455    }
456
457    /// This function could panic, but it does that for sanity reasons.
458    ///
459    /// When we create context we bind api and then store it and rebind
460    /// on functions requiring it, so if it fails it means that it worked
461    /// before, but for some reason stopped working, which should not
462    /// happen according to the specification.
463    pub(crate) fn bind_api(&self) {
464        unsafe {
465            if self.display.inner.egl.QueryAPI() == self.api {
466                return;
467            }
468
469            if self.display.inner.egl.BindAPI(self.api) == egl::FALSE {
470                panic!("EGL Api couldn't be bound anymore.");
471            }
472        }
473    }
474}
475
476impl Drop for ContextInner {
477    fn drop(&mut self) {
478        unsafe {
479            self.display.inner.egl.DestroyContext(*self.display.inner.raw, *self.raw);
480        }
481    }
482}
483
484impl fmt::Debug for ContextInner {
485    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
486        f.debug_struct("Context")
487            .field("display", &self.display.inner.raw)
488            .field("config", &self.config.inner.raw)
489            .field("raw", &self.raw)
490            .finish()
491    }
492}
493
494#[derive(Debug)]
495struct EglContext(EGLContext);
496
497// Impl only `Send` for EglContext.
498unsafe impl Send for EglContext {}
499
500impl Deref for EglContext {
501    type Target = EGLContext;
502
503    fn deref(&self) -> &Self::Target {
504        &self.0
505    }
506}