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