#![cfg(target_os = "android")]
use super::Egl;
use crate::{Error, Result};
use edgefirst_egl as egl;
use std::ffi::c_void;
use std::os::fd::{FromRawFd, OwnedFd};
use std::sync::OnceLock;
const EGL_SYNC_NATIVE_FENCE_ANDROID: u32 = 0x3144;
type EglSyncKhr = *mut c_void;
type FnCreateSyncKhr =
unsafe extern "C" fn(dpy: egl::EGLDisplay, r#type: u32, attribs: *const egl::Int) -> EglSyncKhr;
type FnDestroySyncKhr = unsafe extern "C" fn(dpy: egl::EGLDisplay, sync: EglSyncKhr) -> u32;
type FnDupNativeFenceFd = unsafe extern "C" fn(dpy: egl::EGLDisplay, sync: EglSyncKhr) -> egl::Int;
struct FenceEglFns {
create_sync_khr: FnCreateSyncKhr,
destroy_sync_khr: FnDestroySyncKhr,
dup_native_fence_fd: FnDupNativeFenceFd,
}
static FENCE_EGL_FNS: OnceLock<std::result::Result<FenceEglFns, String>> = OnceLock::new();
fn fns(egl: &Egl) -> Result<&'static FenceEglFns> {
FENCE_EGL_FNS
.get_or_init(|| {
let create_sync_khr = egl.get_proc_address("eglCreateSyncKHR").ok_or_else(|| {
"eglCreateSyncKHR not exported (EGL_KHR_fence_sync missing)".to_string()
})?;
let destroy_sync_khr = egl.get_proc_address("eglDestroySyncKHR").ok_or_else(|| {
"eglDestroySyncKHR not exported (EGL_KHR_fence_sync missing)".to_string()
})?;
let dup_native_fence_fd = egl
.get_proc_address("eglDupNativeFenceFDANDROID")
.ok_or_else(|| {
"eglDupNativeFenceFDANDROID not exported \
(EGL_ANDROID_native_fence_sync missing)"
.to_string()
})?;
Ok(unsafe {
FenceEglFns {
create_sync_khr: std::mem::transmute::<extern "system" fn(), FnCreateSyncKhr>(
create_sync_khr,
),
destroy_sync_khr: std::mem::transmute::<extern "system" fn(), FnDestroySyncKhr>(
destroy_sync_khr,
),
dup_native_fence_fd: std::mem::transmute::<
extern "system" fn(),
FnDupNativeFenceFd,
>(dup_native_fence_fd),
}
})
})
.as_ref()
.map_err(|s| Error::NotSupported(s.clone()))
}
pub(super) fn export_native_fence_fd(egl: &Egl, display: egl::Display) -> Result<OwnedFd> {
let fns = fns(egl)?;
let attribs: [egl::Int; 1] = [egl::NONE];
let sync = unsafe {
(fns.create_sync_khr)(
display.as_ptr(),
EGL_SYNC_NATIVE_FENCE_ANDROID,
attribs.as_ptr(),
)
};
if sync.is_null() {
return Err(Error::OpenGl(format!(
"eglCreateSyncKHR(EGL_SYNC_NATIVE_FENCE_ANDROID) failed (EGL error {:?})",
egl.get_error()
)));
}
unsafe { edgefirst_gl::gl::Flush() };
let fd = unsafe { (fns.dup_native_fence_fd)(display.as_ptr(), sync) };
let destroy_ok = unsafe { (fns.destroy_sync_khr)(display.as_ptr(), sync) } != 0;
if !destroy_ok {
log::warn!("eglDestroySyncKHR failed after fence dup (leaking the sync object)");
}
if fd < 0 {
return Err(Error::OpenGl(format!(
"eglDupNativeFenceFDANDROID returned {fd} (EGL error {:?})",
egl.get_error()
)));
}
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}