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