1use super::device::EGL_FUNCTIONS;
4use super::error::ToWindowingApiError;
5use super::ffi::EGL_CONTEXT_OPENGL_PROFILE_MASK;
6use super::ffi::{EGL_CONTEXT_MINOR_VERSION_KHR, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT};
7use super::surface::{EGLBackedSurface, ExternalEGLSurfaces};
8use crate::context::{self, CREATE_CONTEXT_MUTEX};
9use crate::egl;
10use crate::egl::types::{EGLConfig, EGLContext, EGLDisplay, EGLSurface, EGLint};
11use crate::surface::Framebuffer;
12use crate::{ContextAttributeFlags, ContextAttributes, ContextID, Error, GLApi, GLVersion};
13use crate::{Gl, SurfaceInfo};
14use euclid::default::Size2D;
15use glow::HasContext;
16
17use std::ffi::CString;
18use std::mem;
19use std::os::raw::c_void;
20use std::ptr;
21use std::thread;
22
23#[allow(dead_code)]
24const DUMMY_PBUFFER_SIZE: EGLint = 16;
25const RGB_CHANNEL_BIT_DEPTH: EGLint = 8;
26
27pub(crate) struct EGLBackedContext {
28 pub(crate) egl_context: EGLContext,
29 pub(crate) id: ContextID,
30 pbuffer: EGLSurface,
31 pub(crate) framebuffer: Framebuffer<EGLBackedSurface, ExternalEGLSurfaces>,
32 context_is_owned: bool,
33}
34
35#[derive(Clone, Copy)]
37pub struct NativeContext {
38 pub egl_context: EGLContext,
40 pub egl_read_surface: EGLSurface,
42 pub egl_draw_surface: EGLSurface,
44}
45
46#[derive(Clone)]
50pub struct ContextDescriptor {
51 pub(crate) egl_config_id: EGLint,
52 pub(crate) gl_version: GLVersion,
53 pub(crate) compatibility_profile: bool,
54}
55
56#[must_use]
57pub(crate) struct CurrentContextGuard {
58 egl_display: EGLDisplay,
59 old_egl_draw_surface: EGLSurface,
60 old_egl_read_surface: EGLSurface,
61 old_egl_context: EGLContext,
62}
63
64impl CurrentContextGuard {
65 pub(crate) fn new() -> CurrentContextGuard {
66 EGL_FUNCTIONS.with(|egl| unsafe {
67 CurrentContextGuard {
68 egl_display: egl.GetCurrentDisplay(),
69 old_egl_draw_surface: egl.GetCurrentSurface(egl::DRAW as EGLint),
70 old_egl_read_surface: egl.GetCurrentSurface(egl::READ as EGLint),
71 old_egl_context: egl.GetCurrentContext(),
72 }
73 })
74 }
75}
76
77impl Drop for CurrentContextGuard {
78 fn drop(&mut self) {
79 EGL_FUNCTIONS.with(|egl| unsafe {
80 if self.egl_display == egl::NO_DISPLAY {
81 return;
82 }
83
84 if egl.GetCurrentDisplay() == self.egl_display
86 && egl.GetCurrentSurface(egl::DRAW as EGLint) == self.old_egl_draw_surface
87 && egl.GetCurrentSurface(egl::READ as EGLint) == self.old_egl_read_surface
88 && egl.GetCurrentContext() == self.old_egl_context
89 {
90 return;
91 }
92
93 egl.MakeCurrent(
94 self.egl_display,
95 self.old_egl_draw_surface,
96 self.old_egl_read_surface,
97 self.old_egl_context,
98 );
99 })
100 }
101}
102
103impl Drop for EGLBackedContext {
104 #[inline]
105 fn drop(&mut self) {
106 if self.egl_context != egl::NO_CONTEXT && !thread::panicking() {
107 panic!("Contexts must be destroyed explicitly with `destroy_context`!")
108 }
109 }
110}
111
112impl EGLBackedContext {
113 pub(crate) unsafe fn new(
114 egl_display: EGLDisplay,
115 descriptor: &ContextDescriptor,
116 share_with: Option<&EGLBackedContext>,
117 gl_api: GLApi,
118 ) -> Result<EGLBackedContext, Error> {
119 let mut next_context_id = CREATE_CONTEXT_MUTEX.lock().unwrap();
120
121 let egl_context = create_context(
123 egl_display,
124 descriptor,
125 share_with.map_or(egl::NO_CONTEXT, |ctx| ctx.egl_context),
126 gl_api,
127 )?;
128
129 let pbuffer = create_dummy_pbuffer(egl_display, egl_context).unwrap_or(egl::NO_SURFACE);
131
132 let context = EGLBackedContext {
134 egl_context,
135 id: *next_context_id,
136 framebuffer: Framebuffer::None,
137 context_is_owned: true,
138 pbuffer,
139 };
140 next_context_id.0 += 1;
141 Ok(context)
142 }
143
144 pub(crate) unsafe fn from_native_context(native_context: NativeContext) -> EGLBackedContext {
145 let mut next_context_id = CREATE_CONTEXT_MUTEX.lock().unwrap();
146 let context = EGLBackedContext {
147 egl_context: native_context.egl_context,
148 id: *next_context_id,
149 framebuffer: Framebuffer::External(ExternalEGLSurfaces {
150 draw: native_context.egl_draw_surface,
151 read: native_context.egl_read_surface,
152 }),
153 context_is_owned: false,
154 pbuffer: egl::NO_SURFACE,
155 };
156 next_context_id.0 += 1;
157 context
158 }
159
160 pub(crate) unsafe fn destroy(&mut self, egl_display: EGLDisplay) {
161 EGL_FUNCTIONS.with(|egl| {
162 if self.pbuffer != egl::NO_SURFACE {
163 let result = egl.DestroySurface(egl_display, self.pbuffer);
164 assert_ne!(result, egl::FALSE);
165 self.pbuffer = egl::NO_SURFACE;
166 }
167
168 egl.MakeCurrent(
169 egl_display,
170 egl::NO_SURFACE,
171 egl::NO_SURFACE,
172 egl::NO_CONTEXT,
173 );
174
175 if self.context_is_owned {
176 let result = egl.DestroyContext(egl_display, self.egl_context);
177 assert_ne!(result, egl::FALSE);
178 }
179
180 self.egl_context = egl::NO_CONTEXT;
181 });
182 }
183
184 pub(crate) fn native_context(&self) -> NativeContext {
185 let egl_surfaces = match self.framebuffer {
186 Framebuffer::Surface(ref surface) => surface.egl_surfaces(),
187 Framebuffer::External(ref surfaces) => (*surfaces).clone(),
188 Framebuffer::None => ExternalEGLSurfaces::default(),
189 };
190
191 NativeContext {
192 egl_context: self.egl_context,
193 egl_draw_surface: egl_surfaces.draw,
194 egl_read_surface: egl_surfaces.read,
195 }
196 }
197
198 pub(crate) unsafe fn make_current(&self, egl_display: EGLDisplay) -> Result<(), Error> {
199 let egl_surfaces = match self.framebuffer {
200 Framebuffer::Surface(ref surface) => surface.egl_surfaces(),
201 Framebuffer::External(ref surfaces) => (*surfaces).clone(),
202 Framebuffer::None => ExternalEGLSurfaces {
203 draw: self.pbuffer,
204 read: self.pbuffer,
205 },
206 };
207
208 EGL_FUNCTIONS.with(|egl| {
209 let result = egl.MakeCurrent(
210 egl_display,
211 egl_surfaces.draw,
212 egl_surfaces.read,
213 self.egl_context,
214 );
215 if result == egl::FALSE {
216 let err = egl.GetError().to_windowing_api_error();
217 return Err(Error::MakeCurrentFailed(err));
218 }
219 Ok(())
220 })
221 }
222
223 #[inline]
224 pub(crate) fn is_current(&self) -> bool {
225 unsafe { EGL_FUNCTIONS.with(|egl| egl.GetCurrentContext() == self.egl_context) }
226 }
227
228 pub(crate) unsafe fn bind_surface(
229 &mut self,
230 egl_display: EGLDisplay,
231 surface: EGLBackedSurface,
232 ) -> Result<(), (Error, EGLBackedSurface)> {
233 if self.id != surface.context_id {
234 return Err((Error::IncompatibleSurface, surface));
235 }
236
237 match self.framebuffer {
238 Framebuffer::None => self.framebuffer = Framebuffer::Surface(surface),
239 Framebuffer::External(_) => return Err((Error::ExternalRenderTarget, surface)),
240 Framebuffer::Surface(_) => return Err((Error::SurfaceAlreadyBound, surface)),
241 }
242
243 if self.is_current() {
245 drop(self.make_current(egl_display))
246 }
247
248 Ok(())
249 }
250
251 pub(crate) unsafe fn unbind_surface(
252 &mut self,
253 gl: &Gl,
254 egl_display: EGLDisplay,
255 ) -> Result<Option<EGLBackedSurface>, Error> {
256 gl.flush();
258
259 match self.framebuffer {
260 Framebuffer::None => return Ok(None),
261 Framebuffer::Surface(_) => {}
262 Framebuffer::External(_) => return Err(Error::ExternalRenderTarget),
263 }
264
265 let surface = match mem::replace(&mut self.framebuffer, Framebuffer::None) {
266 Framebuffer::Surface(surface) => surface,
267 Framebuffer::None | Framebuffer::External(_) => unreachable!(),
268 };
269
270 surface.unbind(gl, egl_display, self.egl_context);
272
273 Ok(Some(surface))
274 }
275
276 pub(crate) fn present_bound_surface(&self, egl_display: EGLDisplay) -> Result<(), Error> {
277 match &self.framebuffer {
278 Framebuffer::Surface(surface) => surface.present(egl_display, self.egl_context),
279 Framebuffer::None | Framebuffer::External(_) => Ok(()),
280 }
281 }
282
283 pub(crate) fn resize_bound_surface(&mut self, size: Size2D<i32>) -> Result<(), Error> {
284 if let Framebuffer::Surface(surface) = &mut self.framebuffer {
285 surface.resize(size);
286 }
287 Ok(())
288 }
289
290 pub(crate) fn surface_info(&self) -> Result<Option<SurfaceInfo>, Error> {
291 match self.framebuffer {
292 Framebuffer::None => Ok(None),
293 Framebuffer::External(_) => Err(Error::ExternalRenderTarget),
294 Framebuffer::Surface(ref surface) => Ok(Some(surface.info())),
295 }
296 }
297}
298
299impl NativeContext {
300 pub fn current() -> Result<NativeContext, Error> {
304 EGL_FUNCTIONS.with(|egl| unsafe {
305 let egl_context = egl.GetCurrentContext();
306 if egl_context == egl::NO_CONTEXT {
307 Err(Error::NoCurrentContext)
308 } else {
309 Ok(NativeContext {
310 egl_context,
311 egl_read_surface: egl.GetCurrentSurface(egl::READ as EGLint),
312 egl_draw_surface: egl.GetCurrentSurface(egl::DRAW as EGLint),
313 })
314 }
315 })
316 }
317}
318
319impl ContextDescriptor {
320 pub(crate) unsafe fn new(
321 egl_display: EGLDisplay,
322 attributes: &ContextAttributes,
323 extra_config_attributes: &[EGLint],
324 ) -> Result<ContextDescriptor, Error> {
325 let flags = attributes.flags;
326
327 let alpha_size = if flags.contains(ContextAttributeFlags::ALPHA) {
328 8
329 } else {
330 0
331 };
332 let depth_size = if flags.contains(ContextAttributeFlags::DEPTH) {
333 24
334 } else {
335 0
336 };
337 let stencil_size = if flags.contains(ContextAttributeFlags::STENCIL) {
338 8
339 } else {
340 0
341 };
342
343 let compatibility_profile = flags.contains(ContextAttributeFlags::COMPATIBILITY_PROFILE);
344
345 if compatibility_profile
348 && (attributes.version.major > 3
349 || attributes.version.major == 3 && attributes.version.minor > 0)
350 {
351 return Err(Error::UnsupportedGLProfile);
352 }
353
354 if cfg!(any(android_platform, ohos_platform)) && compatibility_profile {
356 return Err(Error::UnsupportedGLProfile);
357 }
358
359 let required_config_attributes = [
364 egl::RED_SIZE as EGLint,
365 RGB_CHANNEL_BIT_DEPTH,
366 egl::GREEN_SIZE as EGLint,
367 RGB_CHANNEL_BIT_DEPTH,
368 egl::BLUE_SIZE as EGLint,
369 RGB_CHANNEL_BIT_DEPTH,
370 ];
371
372 let mut requested_config_attributes = required_config_attributes.to_vec();
374 requested_config_attributes.extend_from_slice(&[
375 egl::ALPHA_SIZE as EGLint,
376 alpha_size,
377 egl::DEPTH_SIZE as EGLint,
378 depth_size,
379 egl::STENCIL_SIZE as EGLint,
380 stencil_size,
381 ]);
382 requested_config_attributes.extend_from_slice(extra_config_attributes);
383 requested_config_attributes.extend_from_slice(&[egl::NONE as EGLint, 0, 0, 0]);
384
385 EGL_FUNCTIONS.with(|egl| {
386 let mut config_count = 0;
388 let result = egl.ChooseConfig(
389 egl_display,
390 requested_config_attributes.as_ptr(),
391 ptr::null_mut(),
392 0,
393 &mut config_count,
394 );
395 if result == egl::FALSE {
396 let err = egl.GetError().to_windowing_api_error();
397 return Err(Error::PixelFormatSelectionFailed(err));
398 }
399 if config_count == 0 {
400 return Err(Error::NoPixelFormatFound);
401 }
402
403 let mut configs = vec![ptr::null(); config_count as usize];
405 let mut real_config_count = config_count;
406 let result = egl.ChooseConfig(
407 egl_display,
408 requested_config_attributes.as_ptr(),
409 configs.as_mut_ptr(),
410 config_count,
411 &mut real_config_count,
412 );
413 if result == egl::FALSE {
414 let err = egl.GetError().to_windowing_api_error();
415 return Err(Error::PixelFormatSelectionFailed(err));
416 }
417
418 let egl_config = configs.into_iter().find(|&egl_config| {
420 required_config_attributes
421 .chunks(2)
422 .all(|pair| get_config_attr(egl_display, egl_config, pair[0]) == pair[1])
423 });
424 let egl_config = match egl_config {
425 None => return Err(Error::NoPixelFormatFound),
426 Some(egl_config) => egl_config,
427 };
428
429 let egl_config_id = get_config_attr(egl_display, egl_config, egl::CONFIG_ID as EGLint);
431 let gl_version = attributes.version;
432
433 Ok(ContextDescriptor {
434 egl_config_id,
435 gl_version,
436 compatibility_profile,
437 })
438 })
439 }
440
441 pub(crate) unsafe fn from_egl_context(
442 gl: &Gl,
443 egl_display: EGLDisplay,
444 egl_context: EGLContext,
445 ) -> ContextDescriptor {
446 let egl_config_id = get_context_attr(egl_display, egl_context, egl::CONFIG_ID as EGLint);
447 let gl_version = GLVersion::current(&gl);
448 let compatibility_profile = context::current_context_uses_compatibility_profile(&gl);
449
450 ContextDescriptor {
451 egl_config_id,
452 gl_version,
453 compatibility_profile,
454 }
455 }
456
457 #[allow(dead_code)]
458 pub(crate) unsafe fn to_egl_config(&self, egl_display: EGLDisplay) -> EGLConfig {
459 let config_attributes = [
460 egl::CONFIG_ID as EGLint,
461 self.egl_config_id,
462 egl::NONE as EGLint,
463 0,
464 0,
465 0,
466 ];
467
468 EGL_FUNCTIONS.with(|egl| {
469 let (mut config, mut config_count) = (ptr::null(), 0);
470 let result = egl.ChooseConfig(
471 egl_display,
472 config_attributes.as_ptr(),
473 &mut config,
474 1,
475 &mut config_count,
476 );
477 assert_ne!(result, egl::FALSE);
478 assert!(config_count > 0);
479 config
480 })
481 }
482
483 pub(crate) unsafe fn attributes(&self, egl_display: EGLDisplay) -> ContextAttributes {
484 let egl_config = egl_config_from_id(egl_display, self.egl_config_id);
485
486 let alpha_size = get_config_attr(egl_display, egl_config, egl::ALPHA_SIZE as EGLint);
487 let depth_size = get_config_attr(egl_display, egl_config, egl::DEPTH_SIZE as EGLint);
488 let stencil_size = get_config_attr(egl_display, egl_config, egl::STENCIL_SIZE as EGLint);
489
490 let mut attribute_flags = ContextAttributeFlags::empty();
492 attribute_flags.set(ContextAttributeFlags::ALPHA, alpha_size != 0);
493 attribute_flags.set(ContextAttributeFlags::DEPTH, depth_size != 0);
494 attribute_flags.set(ContextAttributeFlags::STENCIL, stencil_size != 0);
495
496 attribute_flags.set(
497 ContextAttributeFlags::COMPATIBILITY_PROFILE,
498 self.compatibility_profile,
499 );
500
501 ContextAttributes {
503 flags: attribute_flags,
504 version: self.gl_version,
505 }
506 }
507}
508
509pub(crate) unsafe fn create_context(
510 egl_display: EGLDisplay,
511 descriptor: &ContextDescriptor,
512 share_with: EGLContext,
513 gl_api: GLApi,
514) -> Result<EGLContext, Error> {
515 EGL_FUNCTIONS.with(|egl| {
516 let ok = egl.BindAPI(match gl_api {
517 GLApi::GL => egl::OPENGL_API,
518 GLApi::GLES => egl::OPENGL_ES_API,
519 });
520 assert_ne!(ok, egl::FALSE);
521 });
522
523 let egl_config = egl_config_from_id(egl_display, descriptor.egl_config_id);
524
525 let mut egl_context_attributes = vec![
526 egl::CONTEXT_CLIENT_VERSION as EGLint,
527 descriptor.gl_version.major as EGLint,
528 EGL_CONTEXT_MINOR_VERSION_KHR as EGLint,
529 descriptor.gl_version.minor as EGLint,
530 ];
531
532 if descriptor.compatibility_profile {
535 egl_context_attributes.extend(&[
536 EGL_CONTEXT_OPENGL_PROFILE_MASK as EGLint,
537 EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT,
538 ]);
539 }
540
541 egl_context_attributes.extend(&[egl::NONE as EGLint, 0, 0, 0]);
545
546 EGL_FUNCTIONS.with(|egl| {
547 let egl_context = egl.CreateContext(
548 egl_display,
549 egl_config,
550 share_with,
551 egl_context_attributes.as_ptr(),
552 );
553 if egl_context == egl::NO_CONTEXT {
554 let err = egl.GetError();
555 let err = err.to_windowing_api_error();
556 return Err(Error::ContextCreationFailed(err));
557 }
558
559 Ok(egl_context)
560 })
561}
562
563pub(crate) unsafe fn make_no_context_current(egl_display: EGLDisplay) -> Result<(), Error> {
564 EGL_FUNCTIONS.with(|egl| {
565 let result = egl.MakeCurrent(
566 egl_display,
567 egl::NO_SURFACE,
568 egl::NO_SURFACE,
569 egl::NO_CONTEXT,
570 );
571 if result == egl::FALSE {
572 let err = egl.GetError().to_windowing_api_error();
573 return Err(Error::MakeCurrentFailed(err));
574 }
575 Ok(())
576 })
577}
578
579pub(crate) unsafe fn get_config_attr(
580 egl_display: EGLDisplay,
581 egl_config: EGLConfig,
582 attr: EGLint,
583) -> EGLint {
584 EGL_FUNCTIONS.with(|egl| {
585 let mut value = 0;
586 let result = egl.GetConfigAttrib(egl_display, egl_config, attr, &mut value);
587 assert_ne!(result, egl::FALSE);
588 value
589 })
590}
591
592pub(crate) unsafe fn get_context_attr(
593 egl_display: EGLDisplay,
594 egl_context: EGLContext,
595 attr: EGLint,
596) -> EGLint {
597 EGL_FUNCTIONS.with(|egl| {
598 let mut value = 0;
599 let result = egl.QueryContext(egl_display, egl_context, attr, &mut value);
600 assert_ne!(result, egl::FALSE);
601 value
602 })
603}
604
605pub(crate) unsafe fn egl_config_from_id(
606 egl_display: EGLDisplay,
607 egl_config_id: EGLint,
608) -> EGLConfig {
609 let config_attributes = [
610 egl::CONFIG_ID as EGLint,
611 egl_config_id,
612 egl::NONE as EGLint,
613 0,
614 0,
615 0,
616 ];
617
618 EGL_FUNCTIONS.with(|egl| {
619 let (mut config, mut config_count) = (ptr::null(), 0);
620 let result = egl.ChooseConfig(
621 egl_display,
622 config_attributes.as_ptr(),
623 &mut config,
624 1,
625 &mut config_count,
626 );
627 assert_ne!(result, egl::FALSE);
628 assert!(config_count > 0);
629 config
630 })
631}
632
633pub(crate) fn get_proc_address(symbol_name: &str) -> *const c_void {
634 EGL_FUNCTIONS.with(|egl| unsafe {
635 let symbol_name: CString = CString::new(symbol_name).unwrap();
636 egl.GetProcAddress(symbol_name.as_ptr()).cast()
637 })
638}
639
640#[allow(dead_code)]
643pub(crate) unsafe fn create_dummy_pbuffer(
644 egl_display: EGLDisplay,
645 egl_context: EGLContext,
646) -> Option<EGLSurface> {
647 let egl_config_id = get_context_attr(egl_display, egl_context, egl::CONFIG_ID as EGLint);
648 let egl_config = egl_config_from_id(egl_display, egl_config_id);
649
650 let pbuffer_attributes = [
651 egl::WIDTH as EGLint,
652 DUMMY_PBUFFER_SIZE,
653 egl::HEIGHT as EGLint,
654 DUMMY_PBUFFER_SIZE,
655 egl::NONE as EGLint,
656 0,
657 0,
658 0,
659 ];
660
661 EGL_FUNCTIONS.with(|egl| {
662 let pbuffer =
663 egl.CreatePbufferSurface(egl_display, egl_config, pbuffer_attributes.as_ptr());
664 if pbuffer == egl::NO_SURFACE {
665 None
666 } else {
667 Some(pbuffer)
668 }
669 })
670}