#![cfg(target_os = "android")]
use super::Egl;
use crate::{Error, Result};
use edgefirst_egl as egl;
use std::ffi::c_void;
use std::sync::OnceLock;
const EGL_NATIVE_BUFFER_ANDROID: u32 = 0x3140;
type FnGetNativeClientBuffer = unsafe extern "C" fn(buffer: *mut c_void) -> egl::EGLClientBuffer;
type FnCreateImageKHR = unsafe extern "C" fn(
dpy: egl::EGLDisplay,
ctx: egl::EGLContext,
target: u32,
buffer: egl::EGLClientBuffer,
attribs: *const egl::Int,
) -> egl::EGLImage;
type FnDestroyImageKHR = unsafe extern "C" fn(dpy: egl::EGLDisplay, image: egl::EGLImage) -> u32;
struct AhbEglFns {
get_native_client_buffer: FnGetNativeClientBuffer,
create_image_khr: FnCreateImageKHR,
destroy_image_khr: FnDestroyImageKHR,
}
static AHB_EGL_FNS: OnceLock<std::result::Result<AhbEglFns, String>> = OnceLock::new();
fn fns(egl: &Egl) -> Result<&'static AhbEglFns> {
AHB_EGL_FNS
.get_or_init(|| {
let get_native_client_buffer = egl
.get_proc_address("eglGetNativeClientBufferANDROID")
.ok_or_else(|| {
"eglGetNativeClientBufferANDROID not exported \
(EGL_ANDROID_get_native_client_buffer missing)"
.to_string()
})?;
let create_image_khr = egl.get_proc_address("eglCreateImageKHR").ok_or_else(|| {
"eglCreateImageKHR not exported (EGL_KHR_image_base missing)".to_string()
})?;
let destroy_image_khr = egl.get_proc_address("eglDestroyImageKHR").ok_or_else(|| {
"eglDestroyImageKHR not exported (EGL_KHR_image_base missing)".to_string()
})?;
Ok(unsafe {
AhbEglFns {
get_native_client_buffer: std::mem::transmute::<
extern "system" fn(),
FnGetNativeClientBuffer,
>(get_native_client_buffer),
create_image_khr: std::mem::transmute::<extern "system" fn(), FnCreateImageKHR>(
create_image_khr,
),
destroy_image_khr: std::mem::transmute::<
extern "system" fn(),
FnDestroyImageKHR,
>(destroy_image_khr),
}
})
})
.as_ref()
.map_err(|s| Error::NotSupported(s.clone()))
}
pub(super) unsafe fn create_ahardwarebuffer_eglimage(
egl: &Egl,
display: egl::Display,
buffer_ptr: *mut c_void,
) -> Result<egl::Image> {
let _span =
tracing::trace_span!("image.convert.gl.egl_import", target = "ahardwarebuffer").entered();
let fns = fns(egl)?;
let client_buffer_raw = (fns.get_native_client_buffer)(buffer_ptr);
if client_buffer_raw.is_null() {
return Err(Error::Io(std::io::Error::other(format!(
"eglGetNativeClientBufferANDROID returned NULL (EGL error {:?})",
egl.get_error()
))));
}
let attribs: [egl::Int; 3] = [egl::IMAGE_PRESERVED, egl::TRUE as egl::Int, egl::NONE];
let image_raw = (fns.create_image_khr)(
display.as_ptr(),
egl::NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID,
client_buffer_raw,
attribs.as_ptr(),
);
if image_raw == egl::NO_IMAGE {
return Err(Error::Io(std::io::Error::other(format!(
"eglCreateImageKHR(EGL_NATIVE_BUFFER_ANDROID) returned NO_IMAGE (EGL error {:?})",
egl.get_error()
))));
}
Ok(egl::Image::from_ptr(image_raw))
}
pub(super) fn destroy_ahardwarebuffer_eglimage(
egl: &Egl,
display: egl::Display,
image: egl::Image,
) {
if image.as_ptr() == egl::NO_IMAGE {
return;
}
match fns(egl) {
Ok(f) => {
let ok = unsafe { (f.destroy_image_khr)(display.as_ptr(), image.as_ptr()) };
if ok == 0 {
log::error!(
"eglDestroyImageKHR failed (EGL error {:?})",
egl.get_error()
);
}
}
Err(e) => log::error!("destroy_ahardwarebuffer_eglimage: {e:?}"),
}
}