1 | //! Everything related to `EGLContext` management. |
2 | |
3 | use std::fmt; |
4 | use std::marker::PhantomData; |
5 | use std::ops::Deref; |
6 | |
7 | use glutin_egl_sys::egl::types::{EGLenum, EGLint}; |
8 | use glutin_egl_sys::{egl, EGLContext}; |
9 | |
10 | use crate::config::{Api, GetGlConfig}; |
11 | use crate::context::{ |
12 | self, AsRawContext, ContextApi, ContextAttributes, GlProfile, RawContext, Robustness, Version, |
13 | }; |
14 | use crate::display::{DisplayFeatures, GetGlDisplay}; |
15 | use crate::error::{ErrorKind, Result}; |
16 | use crate::prelude::*; |
17 | use crate::private::Sealed; |
18 | use crate::surface::SurfaceTypeTrait; |
19 | |
20 | use super::config::Config; |
21 | use super::display::Display; |
22 | use super::surface::Surface; |
23 | |
24 | impl 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)] |
170 | pub struct NotCurrentContext { |
171 | inner: ContextInner, |
172 | } |
173 | |
174 | impl 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 | |
187 | impl 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 | |
213 | impl GlContext for NotCurrentContext { |
214 | fn context_api(&self) -> ContextApi { |
215 | self.inner.context_api() |
216 | } |
217 | } |
218 | |
219 | impl GetGlConfig for NotCurrentContext { |
220 | type Target = Config; |
221 | |
222 | fn config(&self) -> Self::Target { |
223 | self.inner.config.clone() |
224 | } |
225 | } |
226 | |
227 | impl GetGlDisplay for NotCurrentContext { |
228 | type Target = Display; |
229 | |
230 | fn display(&self) -> Self::Target { |
231 | self.inner.display.clone() |
232 | } |
233 | } |
234 | |
235 | impl AsRawContext for NotCurrentContext { |
236 | fn raw_context(&self) -> RawContext { |
237 | RawContext::Egl(*self.inner.raw) |
238 | } |
239 | } |
240 | |
241 | impl Sealed for NotCurrentContext {} |
242 | |
243 | /// A wrapper around `EGLContext` that could be current for the current thread. |
244 | #[derive (Debug)] |
245 | pub struct PossiblyCurrentContext { |
246 | pub(crate) inner: ContextInner, |
247 | _nosendsync: PhantomData<EGLContext>, |
248 | } |
249 | |
250 | impl 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 | |
257 | impl 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 | |
286 | impl GlContext for PossiblyCurrentContext { |
287 | fn context_api(&self) -> ContextApi { |
288 | self.inner.context_api() |
289 | } |
290 | } |
291 | |
292 | impl GetGlConfig for PossiblyCurrentContext { |
293 | type Target = Config; |
294 | |
295 | fn config(&self) -> Self::Target { |
296 | self.inner.config.clone() |
297 | } |
298 | } |
299 | |
300 | impl GetGlDisplay for PossiblyCurrentContext { |
301 | type Target = Display; |
302 | |
303 | fn display(&self) -> Self::Target { |
304 | self.inner.display.clone() |
305 | } |
306 | } |
307 | |
308 | impl AsRawContext for PossiblyCurrentContext { |
309 | fn raw_context(&self) -> RawContext { |
310 | RawContext::Egl(*self.inner.raw) |
311 | } |
312 | } |
313 | |
314 | impl Sealed for PossiblyCurrentContext {} |
315 | |
316 | pub(crate) struct ContextInner { |
317 | display: Display, |
318 | config: Config, |
319 | raw: EglContext, |
320 | api: egl::types::EGLenum, |
321 | } |
322 | |
323 | impl 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 | |
421 | impl 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 | |
429 | impl 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)] |
440 | struct EglContext(EGLContext); |
441 | |
442 | // Impl only `Send` for EglContext. |
443 | unsafe impl Send for EglContext {} |
444 | |
445 | impl Deref for EglContext { |
446 | type Target = EGLContext; |
447 | |
448 | fn deref(&self) -> &Self::Target { |
449 | &self.0 |
450 | } |
451 | } |
452 | |