1use 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 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 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 #[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 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#[derive(Debug)]
203pub struct NotCurrentContext {
204 inner: ContextInner,
205}
206
207impl NotCurrentContext {
208 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#[derive(Debug)]
282pub struct PossiblyCurrentContext {
283 pub(crate) inner: ContextInner,
284 _nosendsync: PhantomData<EGLContext>,
285}
286
287impl PossiblyCurrentContext {
288 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 _ => 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 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 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
497unsafe 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}