blob: d7f81729b52920e3c9bc0454d611fad61fe1b204 [file]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
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;
// Opaque blob
#[repr(C)]
pub(crate) struct AndroidDisplayContext {
_data: [u8; 0],
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}
// Opaque blob
#[repr(C)]
pub(crate) struct AndroidDisplaySurface {
_data: [u8; 0],
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}
// Should be the same as ANativeWindow_Buffer in android/native_window.h
// Note that this struct is part of NDK; guaranteed to be stable, so we use it directly across the
// FFI.
#[repr(C)]
pub(crate) struct ANativeWindow_Buffer {
width: i32,
height: i32,
stride: i32, // in number of pixels, NOT bytes
format: i32,
bits: *mut u8,
reserved: [u32; 6],
}
pub(crate) type ErrorCallback = unsafe extern "C" fn(message: *const c_char);
extern "C" {
/// Constructs an AndroidDisplayContext for this backend. This awlays returns a valid (ex:
/// non-null) handle to the context. The `name` parameter is from crosvm commandline and the
/// client of crosvm will use it to locate and communicate to the AndroidDisplayContext. For
/// example, this can be a path to UNIX domain socket where a RPC binder server listens on.
/// `error_callback` is a function pointer to an error reporting function, and will be used by
/// this and other functions below when something goes wrong. The returned context should be
/// destroyed by calling `destroy_android_display_context` if this backend is no longer in use.
fn create_android_display_context(
name: *const c_char,
error_callback: ErrorCallback,
) -> *mut AndroidDisplayContext;
/// Destroys the AndroidDisplayContext created from `create_android_display_context`.
fn destroy_android_display_context(self_: *mut AndroidDisplayContext);
/// Creates an Android Surface (which is also called as Window) of given size. If the surface
/// can't be created for whatever reason, null pointer is returned, in which case we shouldn't
/// proceed further.
fn create_android_surface(
ctx: *mut AndroidDisplayContext,
width: u32,
height: u32,
for_cursor: bool,
) -> *mut AndroidDisplaySurface;
/// Destroys the Android surface created from `create_android_surface`.
#[allow(dead_code)]
fn destroy_android_surface(
ctx: *mut AndroidDisplayContext,
surface: *mut AndroidDisplaySurface,
);
/// Obtains one buffer from the given Android Surface. The information about the buffer (buffer
/// address, size, stride, etc) is reported via the `ANativeWindow_Buffer` struct. It shouldn't
/// be null. The size of the buffer is guaranteed to be bigger than (width * stride * 4) bytes.
/// This function locks the buffer for the client, which means the caller has the exclusive
/// access to the buffer until it is returned back to Android display stack (surfaceflinger) by
/// calling `post_android_surface_buffer`. This function may fail (in which case false is
/// returned), then the caller shouldn't try to read `out_buffer` or use the buffer in any way.
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);
/// Posts the buffer obtained from `get_android_surface_buffer` to the Android display system
/// so that it can be displayed on the screen. Once this is called, the caller shouldn't use
/// the buffer any more.
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!(
"{}",
// SAFETY: message is null terminated
unsafe { CStr::from_ptr(message) }.to_string_lossy()
)
})
.unwrap_or_else(|_| abort())
}
struct AndroidDisplayContextWrapper(NonNull<AndroidDisplayContext>);
impl Drop for AndroidDisplayContextWrapper {
fn drop(&mut self) {
// SAFETY: this object is constructed from create_android_display_context
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 {
// TODO: check anb.format to see if it's ARGB8888?
// TODO: infer bpp from anb.format?
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 =
// SAFETY: get_android_surface_buffer guarantees that bits points to a valid buffer and
// the buffer remains available until post_android_surface_buffer is called.
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,
}
// The key is an import id.
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();
// SAFETY: context and surface are opaque handles and buf is used as the out parameter to
// hold the return values.
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) {
// SAFETY: context and surface are opaque handles.
unsafe { post_android_surface_buffer(self.context.0.as_ptr(), self.surface.as_ptr()) }
}
fn set_position(&mut self, x: u32, y: u32) {
// SAFETY: context is an opaque handle.
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(),
};
// SAFETY:
// Safe because the `AHardwareBufferInfo` outlives the call, and
// `android_display_flip_to` does not retain the pointer.
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>,
/// This event is never triggered and is used solely to fulfill AsRawDescriptor.
event: Event,
// The key is a surface id.
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(
// SAFETY: service_name is not leaked outside of this function
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();
// SAFETY: context is an opaque handle.
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()
}
}