use std::marker::PhantomData;
use std::os::raw::c_void;
use std::ptr;
use euclid::default::Size2D;
use glow::{HasContext, Texture};
use log::info;
use crate::egl;
use crate::egl::types::{EGLSurface, EGLint};
use crate::gl;
use crate::gl_utils;
use crate::platform::egl::ohos_ffi::{eglGetNativeClientBufferANDROID, EGL_NATIVE_BUFFER_OHOS};
use crate::platform::generic;
use crate::platform::generic::egl::device::EGL_FUNCTIONS;
use crate::platform::generic::egl::ffi::EGLImageKHR;
use crate::platform::generic::egl::ffi::EGL_EXTENSION_FUNCTIONS;
use crate::platform::generic::egl::ffi::EGL_IMAGE_PRESERVED_KHR;
use crate::platform::generic::egl::ffi::EGL_NO_IMAGE_KHR;
use crate::renderbuffers::Renderbuffers;
use crate::{Error, SurfaceAccess, SurfaceID, SurfaceInfo, SurfaceType};
use super::super::context::Context;
use super::super::device::Device;
use super::super::ohos_ffi::{
NativeWindowOperation, OHNativeWindow, OH_NativeBuffer, OH_NativeBuffer_Alloc,
OH_NativeBuffer_Config, OH_NativeBuffer_Format, OH_NativeBuffer_Unreference,
OH_NativeBuffer_Usage, OH_NativeWindow_NativeWindowHandleOpt,
};
use super::{Surface, SurfaceTexture};
const SURFACE_GL_TEXTURE_TARGET: u32 = gl::TEXTURE_2D;
pub(crate) enum SurfaceObjects {
HardwareBuffer {
hardware_buffer: *mut OH_NativeBuffer,
egl_image: EGLImageKHR,
framebuffer_object: Option<glow::Framebuffer>,
texture_object: Option<Texture>,
renderbuffers: Renderbuffers,
},
Window {
egl_surface: EGLSurface,
},
}
pub struct NativeWidget {
pub(crate) native_window: *mut OHNativeWindow,
}
impl Device {
pub fn create_surface(
&self,
context: &Context,
_: SurfaceAccess,
surface_type: SurfaceType<NativeWidget>,
) -> Result<Surface, Error> {
info!("Device create_surface with Context");
match surface_type {
SurfaceType::Generic { size } => self.create_generic_surface(context, &size),
SurfaceType::Widget { native_widget } => unsafe {
self.create_window_surface(context, native_widget)
},
}
}
fn create_generic_surface(
&self,
context: &Context,
size: &Size2D<i32>,
) -> Result<Surface, Error> {
let _guard = self.temporarily_make_context_current(context)?;
let usage = OH_NativeBuffer_Usage::HW_RENDER | OH_NativeBuffer_Usage::HW_TEXTURE;
let config = OH_NativeBuffer_Config {
width: size.width,
height: size.height,
format: OH_NativeBuffer_Format::RGBA_8888,
usage: usage,
stride: 10, };
let gl = &context.gl;
unsafe {
let hardware_buffer = OH_NativeBuffer_Alloc(&config as *const _);
assert!(!hardware_buffer.is_null(), "Failed to create native buffer");
let egl_image = self.create_egl_image(context, hardware_buffer);
let texture_object = generic::egl::surface::bind_egl_image_to_gl_texture(gl, egl_image);
let framebuffer_object = gl_utils::create_and_bind_framebuffer(
gl,
SURFACE_GL_TEXTURE_TARGET,
Some(texture_object),
);
let context_descriptor = self.context_descriptor(context);
let context_attributes = self.context_descriptor_attributes(&context_descriptor);
let renderbuffers = Renderbuffers::new(gl, size, &context_attributes);
renderbuffers.bind_to_current_framebuffer(gl);
debug_assert_eq!(
gl.check_framebuffer_status(gl::FRAMEBUFFER),
gl::FRAMEBUFFER_COMPLETE
);
Ok(Surface {
size: *size,
context_id: context.id,
objects: SurfaceObjects::HardwareBuffer {
hardware_buffer,
egl_image,
framebuffer_object: Some(framebuffer_object),
texture_object: Some(texture_object),
renderbuffers,
},
destroyed: false,
})
}
}
unsafe fn create_window_surface(
&self,
context: &Context,
native_widget: NativeWidget,
) -> Result<Surface, Error> {
let mut height: i32 = 0;
let mut width: i32 = 0;
let result = unsafe {
OH_NativeWindow_NativeWindowHandleOpt(
native_widget.native_window,
NativeWindowOperation::GET_BUFFER_GEOMETRY,
&mut height as *mut i32,
&mut width as *mut i32,
)
};
assert_eq!(result, 0, "Failed to determine size of native window");
EGL_FUNCTIONS.with(|egl| {
let egl_surface = egl.CreateWindowSurface(
self.egl_display,
self.context_to_egl_config(context),
native_widget.native_window as *const c_void,
ptr::null(),
);
assert_ne!(egl_surface, egl::NO_SURFACE);
Ok(Surface {
context_id: context.id,
size: Size2D::new(width, height),
objects: SurfaceObjects::Window { egl_surface },
destroyed: false,
})
})
}
pub fn create_surface_texture(
&self,
context: &mut Context,
surface: Surface,
) -> Result<SurfaceTexture, (Error, Surface)> {
unsafe {
match surface.objects {
SurfaceObjects::Window { .. } => return Err((Error::WidgetAttached, surface)),
SurfaceObjects::HardwareBuffer {
hardware_buffer, ..
} => {
let _guard = match self.temporarily_make_context_current(context) {
Ok(guard) => guard,
Err(err) => return Err((err, surface)),
};
let gl = &context.gl;
let local_egl_image = self.create_egl_image(context, hardware_buffer);
let texture_object =
generic::egl::surface::bind_egl_image_to_gl_texture(gl, local_egl_image);
Ok(SurfaceTexture {
surface,
local_egl_image,
texture_object: Some(texture_object),
phantom: PhantomData,
})
}
}
}
}
pub fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> {
if context.id != surface.context_id {
return Err(Error::IncompatibleSurface);
}
EGL_FUNCTIONS.with(|egl| unsafe {
match surface.objects {
SurfaceObjects::Window { egl_surface } => {
egl.SwapBuffers(self.egl_display, egl_surface);
Ok(())
}
SurfaceObjects::HardwareBuffer { .. } => Err(Error::NoWidgetAttached),
}
})
}
pub fn resize_surface(
&self,
_context: &Context,
surface: &mut Surface,
size: Size2D<i32>,
) -> Result<(), Error> {
surface.size = size;
Ok(())
}
#[allow(non_snake_case)]
unsafe fn create_egl_image(
&self,
_: &Context,
hardware_buffer: *mut OH_NativeBuffer,
) -> EGLImageKHR {
let client_buffer = eglGetNativeClientBufferANDROID(hardware_buffer as *const _);
assert!(!client_buffer.is_null());
let egl_image_attributes = [
EGL_IMAGE_PRESERVED_KHR as EGLint,
egl::TRUE as EGLint,
egl::NONE as EGLint,
0,
];
let egl_image = (EGL_EXTENSION_FUNCTIONS.CreateImageKHR)(
self.egl_display,
egl::NO_CONTEXT,
EGL_NATIVE_BUFFER_OHOS,
client_buffer.cast_mut().cast(),
egl_image_attributes.as_ptr(),
);
assert_ne!(egl_image, EGL_NO_IMAGE_KHR);
info!("surfman created an EGL image succesfully!");
egl_image
}
pub fn destroy_surface(
&self,
context: &mut Context,
surface: &mut Surface,
) -> Result<(), Error> {
if context.id != surface.context_id {
return Err(Error::IncompatibleSurface);
}
unsafe {
match surface.objects {
SurfaceObjects::HardwareBuffer {
ref mut hardware_buffer,
ref mut egl_image,
ref mut framebuffer_object,
ref mut texture_object,
ref mut renderbuffers,
} => {
let gl = &context.gl;
gl.bind_framebuffer(gl::FRAMEBUFFER, None);
if let Some(framebuffer) = framebuffer_object.take() {
gl.delete_framebuffer(framebuffer);
}
renderbuffers.destroy(gl);
if let Some(texture) = texture_object.take() {
gl.delete_texture(texture);
}
let egl_display = self.egl_display;
let result = (EGL_EXTENSION_FUNCTIONS.DestroyImageKHR)(egl_display, *egl_image);
assert_ne!(result, egl::FALSE);
*egl_image = EGL_NO_IMAGE_KHR;
let res = OH_NativeBuffer_Unreference(*hardware_buffer);
assert_eq!(res, 0, "OH_NativeBuffer_Unreference failed");
*hardware_buffer = ptr::null_mut();
}
SurfaceObjects::Window {
ref mut egl_surface,
} => EGL_FUNCTIONS.with(|egl| {
egl.DestroySurface(self.egl_display, *egl_surface);
*egl_surface = egl::NO_SURFACE;
}),
}
}
surface.destroyed = true;
Ok(())
}
pub fn destroy_surface_texture(
&self,
context: &mut Context,
mut surface_texture: SurfaceTexture,
) -> Result<Surface, (Error, SurfaceTexture)> {
let _guard = self.temporarily_make_context_current(context);
let gl = &context.gl;
unsafe {
if let Some(texture) = surface_texture.texture_object.take() {
gl.delete_texture(texture);
}
let egl_display = self.egl_display;
let result = (EGL_EXTENSION_FUNCTIONS.DestroyImageKHR)(
egl_display,
surface_texture.local_egl_image,
);
assert_ne!(result, egl::FALSE);
surface_texture.local_egl_image = EGL_NO_IMAGE_KHR;
}
Ok(surface_texture.surface)
}
#[inline]
pub fn lock_surface_data<'s>(&self, _: &'s mut Surface) -> Result<SurfaceDataGuard<'s>, Error> {
error!("lock_surface_data not implemented yet for OHOS");
Err(Error::Unimplemented)
}
#[inline]
pub fn surface_gl_texture_target(&self) -> u32 {
SURFACE_GL_TEXTURE_TARGET
}
pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo {
SurfaceInfo {
size: surface.size,
id: surface.id(),
context_id: surface.context_id,
framebuffer_object: match surface.objects {
SurfaceObjects::HardwareBuffer {
framebuffer_object, ..
} => framebuffer_object,
SurfaceObjects::Window { .. } => None,
},
}
}
#[inline]
pub fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> Option<Texture> {
surface_texture.texture_object
}
}
impl NativeWidget {
#[inline]
pub unsafe fn from_native_window(native_window: *mut OHNativeWindow) -> NativeWidget {
NativeWidget { native_window }
}
}
impl Surface {
pub(super) fn id(&self) -> SurfaceID {
match self.objects {
SurfaceObjects::HardwareBuffer { egl_image, .. } => SurfaceID(egl_image as usize),
SurfaceObjects::Window { egl_surface } => SurfaceID(egl_surface as usize),
}
}
}
pub struct SurfaceDataGuard<'a> {
phantom: PhantomData<&'a ()>,
}