use std::collections::HashMap;
use std::ffi::c_char;
use std::ffi::CStr;
use std::ffi::CString;
use std::os::fd::AsFd;
use std::os::fd::AsRawFd;
use std::panic::catch_unwind;
use std::process::abort;
use std::ptr::NonNull;
use std::rc::Rc;
use std::slice;
use std::sync::Arc;
use std::sync::RwLock;
use anyhow::bail;
use base::error;
use base::AsRawDescriptor;
use base::Event;
use base::RawDescriptor;
use base::VolatileSlice;
use rutabaga_gfx::AhbInfo;
use sync::Waitable;
use vm_control::gpu::DisplayParameters;
use crate::DisplayExternalResourceImport;
use crate::DisplayT;
use crate::FlipToExtraInfo;
use crate::GpuDisplayError;
use crate::GpuDisplayFramebuffer;
use crate::GpuDisplayResult;
use crate::GpuDisplaySurface;
use crate::SemaphoreTimepoint;
use crate::SurfaceType;
use crate::SysDisplayT;
#[repr(C)]
pub(crate) struct AndroidDisplayContext {
_data: [u8; 0],
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}
#[repr(C)]
pub(crate) struct AndroidDisplaySurface {
_data: [u8; 0],
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}
#[repr(C)]
pub(crate) struct ANativeWindow_Buffer {
width: i32,
height: i32,
stride: i32,
format: i32,
bits: *mut u8,
reserved: [u32; 6],
}
pub(crate) type ErrorCallback = unsafe extern "C" fn(message: *const c_char);
extern "C" {
fn create_android_display_context(
name: *const c_char,
error_callback: ErrorCallback,
) -> *mut AndroidDisplayContext;
fn destroy_android_display_context(self_: *mut AndroidDisplayContext);
fn create_android_surface(
ctx: *mut AndroidDisplayContext,
width: u32,
height: u32,
for_cursor: bool,
) -> *mut AndroidDisplaySurface;
#[allow(dead_code)]
fn destroy_android_surface(
ctx: *mut AndroidDisplayContext,
surface: *mut AndroidDisplaySurface,
);
fn get_android_surface_buffer(
ctx: *mut AndroidDisplayContext,
surface: *mut AndroidDisplaySurface,
out_buffer: *mut ANativeWindow_Buffer,
) -> bool;
fn set_android_surface_position(ctx: *mut AndroidDisplayContext, x: u32, y: u32);
fn post_android_surface_buffer(
ctx: *mut AndroidDisplayContext,
surface: *mut AndroidDisplaySurface,
);
fn android_display_flip_to(
ctx: *mut AndroidDisplayContext,
_surface: *mut AndroidDisplaySurface,
ahb_info: *const AHardwareBufferInfo,
);
}
unsafe extern "C" fn error_callback(message: *const c_char) {
catch_unwind(|| {
error!(
"{}",
unsafe { CStr::from_ptr(message) }.to_string_lossy()
)
})
.unwrap_or_else(|_| abort())
}
struct AndroidDisplayContextWrapper(NonNull<AndroidDisplayContext>);
impl Drop for AndroidDisplayContextWrapper {
fn drop(&mut self) {
unsafe { destroy_android_display_context(self.0.as_ptr()) };
}
}
impl Default for ANativeWindow_Buffer {
fn default() -> Self {
Self {
width: 0,
height: 0,
stride: 0,
format: 0,
bits: std::ptr::null_mut(),
reserved: [0u32; 6],
}
}
}
impl From<ANativeWindow_Buffer> for GpuDisplayFramebuffer<'_> {
fn from(anb: ANativeWindow_Buffer) -> Self {
const BYTES_PER_PIXEL: u32 = 4;
let stride_bytes = BYTES_PER_PIXEL * u32::try_from(anb.stride).unwrap();
let buffer_size = stride_bytes * u32::try_from(anb.height).unwrap();
let buffer =
unsafe { slice::from_raw_parts_mut(anb.bits, buffer_size.try_into().unwrap()) };
Self::new(VolatileSlice::new(buffer), stride_bytes, BYTES_PER_PIXEL)
}
}
#[repr(C)]
pub struct AHardwareBufferInfo {
pub num_fds: usize,
pub fds_ptr: *const i32,
pub metadata_len: usize,
pub metadata_ptr: *const u8,
}
type AHardwareBufferImportMap = HashMap<u32, AhbInfo>;
struct AndroidSurface {
context: Rc<AndroidDisplayContextWrapper>,
surface: NonNull<AndroidDisplaySurface>,
ahb_import_map: Arc<RwLock<AHardwareBufferImportMap>>,
}
impl GpuDisplaySurface for AndroidSurface {
fn framebuffer(&mut self) -> Option<GpuDisplayFramebuffer> {
let mut anb = ANativeWindow_Buffer::default();
let success = unsafe {
get_android_surface_buffer(
self.context.0.as_ptr(),
self.surface.as_ptr(),
&mut anb as *mut ANativeWindow_Buffer,
)
};
if success {
Some(anb.into())
} else {
None
}
}
fn flip(&mut self) {
unsafe { post_android_surface_buffer(self.context.0.as_ptr(), self.surface.as_ptr()) }
}
fn set_position(&mut self, x: u32, y: u32) {
unsafe { set_android_surface_position(self.context.0.as_ptr(), x, y) };
}
fn flip_to(
&mut self,
import_id: u32,
_acquire_timepoint: Option<SemaphoreTimepoint>,
_release_timepoint: Option<SemaphoreTimepoint>,
_extra_info: Option<FlipToExtraInfo>,
) -> anyhow::Result<Waitable> {
{
let ahb_import_map = self.ahb_import_map.read().expect("failed to get a lock");
let ahb = ahb_import_map
.get(&import_id)
.ok_or(GpuDisplayError::InvalidImportId)?;
let fds: Vec<i32> = ahb.fds.iter().map(|fd| fd.as_fd().as_raw_fd()).collect();
let info = AHardwareBufferInfo {
num_fds: fds.len(),
fds_ptr: fds.as_ptr(),
metadata_len: ahb.metadata.len(),
metadata_ptr: ahb.metadata.as_ptr(),
};
unsafe {
android_display_flip_to(
self.context.0.as_ptr(),
self.surface.as_ptr(),
&info as *const AHardwareBufferInfo,
)
};
}
Ok(Waitable::signaled())
}
}
pub struct DisplayAndroid {
context: Rc<AndroidDisplayContextWrapper>,
event: Event,
surface_ahbs_map: HashMap<u32, Arc<RwLock<AHardwareBufferImportMap>>>,
}
impl DisplayAndroid {
pub fn new(name: &str) -> GpuDisplayResult<DisplayAndroid> {
let name = CString::new(name).unwrap();
let context = NonNull::new(
unsafe { create_android_display_context(name.as_ptr(), error_callback) },
)
.ok_or(GpuDisplayError::Unsupported)?;
let context = AndroidDisplayContextWrapper(context);
let event = Event::new().map_err(|_| GpuDisplayError::CreateEvent)?;
Ok(DisplayAndroid {
context: context.into(),
event,
surface_ahbs_map: HashMap::new(),
})
}
}
impl DisplayT for DisplayAndroid {
fn create_surface(
&mut self,
parent_surface_id: Option<u32>,
surface_id: u32,
_scanout_id: Option<u32>,
display_params: &DisplayParameters,
_surf_type: SurfaceType,
) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
let (requested_width, requested_height) = display_params.get_virtual_display_size();
let surface = NonNull::new(unsafe {
create_android_surface(
self.context.0.as_ptr(),
requested_width,
requested_height,
parent_surface_id.is_some(),
)
})
.ok_or(GpuDisplayError::CreateSurface)?;
let ahb_import_map = self.surface_ahbs_map.entry(surface_id).or_default();
Ok(Box::new(AndroidSurface {
context: self.context.clone(),
surface,
ahb_import_map: Arc::clone(ahb_import_map),
}))
}
fn release_surface(&mut self, surface_id: u32) {
self.surface_ahbs_map.remove(&surface_id);
}
fn import_resource(
&mut self,
import_id: u32,
surface_id: u32,
external_display_resource: DisplayExternalResourceImport,
) -> anyhow::Result<()> {
let DisplayExternalResourceImport::AHardwareBuffer { info } = external_display_resource
else {
bail!("gpu_display_android only supports AHardwareBufferInfo imports");
};
{
let mut ahbs = self
.surface_ahbs_map
.entry(surface_id)
.or_default()
.write()
.expect("failed to get a lock");
ahbs.insert(import_id, info);
}
Ok(())
}
fn release_import(&mut self, surface_id: u32, import_id: u32) {
let mut ahbs = self
.surface_ahbs_map
.entry(surface_id)
.or_default()
.write()
.expect("failed to get a lock");
ahbs.remove(&import_id);
}
}
impl SysDisplayT for DisplayAndroid {}
impl AsRawDescriptor for DisplayAndroid {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.event.as_raw_descriptor()
}
}