Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/gpu_display/src/gpu_display_android.rs
5392 views
1
// Copyright 2024 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
use std::collections::HashMap;
6
use std::ffi::c_char;
7
use std::ffi::CStr;
8
use std::ffi::CString;
9
use std::os::fd::AsFd;
10
use std::os::fd::AsRawFd;
11
use std::panic::catch_unwind;
12
use std::process::abort;
13
use std::ptr::NonNull;
14
use std::rc::Rc;
15
use std::slice;
16
use std::sync::Arc;
17
use std::sync::RwLock;
18
19
use anyhow::bail;
20
use base::error;
21
use base::AsRawDescriptor;
22
use base::Event;
23
use base::RawDescriptor;
24
use base::VolatileSlice;
25
use rutabaga_gfx::AhbInfo;
26
use sync::Waitable;
27
use vm_control::gpu::DisplayParameters;
28
29
use crate::DisplayExternalResourceImport;
30
use crate::DisplayT;
31
use crate::FlipToExtraInfo;
32
use crate::GpuDisplayError;
33
use crate::GpuDisplayFramebuffer;
34
use crate::GpuDisplayResult;
35
use crate::GpuDisplaySurface;
36
use crate::SemaphoreTimepoint;
37
use crate::SurfaceType;
38
use crate::SysDisplayT;
39
40
// Opaque blob
41
#[repr(C)]
42
pub(crate) struct AndroidDisplayContext {
43
_data: [u8; 0],
44
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
45
}
46
47
// Opaque blob
48
#[repr(C)]
49
pub(crate) struct AndroidDisplaySurface {
50
_data: [u8; 0],
51
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
52
}
53
54
// Should be the same as ANativeWindow_Buffer in android/native_window.h
55
// Note that this struct is part of NDK; guaranteed to be stable, so we use it directly across the
56
// FFI.
57
#[repr(C)]
58
pub(crate) struct ANativeWindow_Buffer {
59
width: i32,
60
height: i32,
61
stride: i32, // in number of pixels, NOT bytes
62
format: i32,
63
bits: *mut u8,
64
reserved: [u32; 6],
65
}
66
67
pub(crate) type ErrorCallback = unsafe extern "C" fn(message: *const c_char);
68
69
extern "C" {
70
/// Constructs an AndroidDisplayContext for this backend. This awlays returns a valid (ex:
71
/// non-null) handle to the context. The `name` parameter is from crosvm commandline and the
72
/// client of crosvm will use it to locate and communicate to the AndroidDisplayContext. For
73
/// example, this can be a path to UNIX domain socket where a RPC binder server listens on.
74
/// `error_callback` is a function pointer to an error reporting function, and will be used by
75
/// this and other functions below when something goes wrong. The returned context should be
76
/// destroyed by calling `destroy_android_display_context` if this backend is no longer in use.
77
fn create_android_display_context(
78
name: *const c_char,
79
error_callback: ErrorCallback,
80
) -> *mut AndroidDisplayContext;
81
82
/// Destroys the AndroidDisplayContext created from `create_android_display_context`.
83
fn destroy_android_display_context(self_: *mut AndroidDisplayContext);
84
85
/// Creates an Android Surface (which is also called as Window) of given size. If the surface
86
/// can't be created for whatever reason, null pointer is returned, in which case we shouldn't
87
/// proceed further.
88
fn create_android_surface(
89
ctx: *mut AndroidDisplayContext,
90
width: u32,
91
height: u32,
92
for_cursor: bool,
93
) -> *mut AndroidDisplaySurface;
94
95
/// Destroys the Android surface created from `create_android_surface`.
96
#[allow(dead_code)]
97
fn destroy_android_surface(
98
ctx: *mut AndroidDisplayContext,
99
surface: *mut AndroidDisplaySurface,
100
);
101
102
/// Obtains one buffer from the given Android Surface. The information about the buffer (buffer
103
/// address, size, stride, etc) is reported via the `ANativeWindow_Buffer` struct. It shouldn't
104
/// be null. The size of the buffer is guaranteed to be bigger than (width * stride * 4) bytes.
105
/// This function locks the buffer for the client, which means the caller has the exclusive
106
/// access to the buffer until it is returned back to Android display stack (surfaceflinger) by
107
/// calling `post_android_surface_buffer`. This function may fail (in which case false is
108
/// returned), then the caller shouldn't try to read `out_buffer` or use the buffer in any way.
109
fn get_android_surface_buffer(
110
ctx: *mut AndroidDisplayContext,
111
surface: *mut AndroidDisplaySurface,
112
out_buffer: *mut ANativeWindow_Buffer,
113
) -> bool;
114
115
fn set_android_surface_position(ctx: *mut AndroidDisplayContext, x: u32, y: u32);
116
117
/// Posts the buffer obtained from `get_android_surface_buffer` to the Android display system
118
/// so that it can be displayed on the screen. Once this is called, the caller shouldn't use
119
/// the buffer any more.
120
fn post_android_surface_buffer(
121
ctx: *mut AndroidDisplayContext,
122
surface: *mut AndroidDisplaySurface,
123
);
124
125
fn android_display_flip_to(
126
ctx: *mut AndroidDisplayContext,
127
_surface: *mut AndroidDisplaySurface,
128
ahb_info: *const AHardwareBufferInfo,
129
);
130
}
131
132
unsafe extern "C" fn error_callback(message: *const c_char) {
133
catch_unwind(|| {
134
error!(
135
"{}",
136
// SAFETY: message is null terminated
137
unsafe { CStr::from_ptr(message) }.to_string_lossy()
138
)
139
})
140
.unwrap_or_else(|_| abort())
141
}
142
143
struct AndroidDisplayContextWrapper(NonNull<AndroidDisplayContext>);
144
145
impl Drop for AndroidDisplayContextWrapper {
146
fn drop(&mut self) {
147
// SAFETY: this object is constructed from create_android_display_context
148
unsafe { destroy_android_display_context(self.0.as_ptr()) };
149
}
150
}
151
152
impl Default for ANativeWindow_Buffer {
153
fn default() -> Self {
154
Self {
155
width: 0,
156
height: 0,
157
stride: 0,
158
format: 0,
159
bits: std::ptr::null_mut(),
160
reserved: [0u32; 6],
161
}
162
}
163
}
164
165
impl From<ANativeWindow_Buffer> for GpuDisplayFramebuffer<'_> {
166
fn from(anb: ANativeWindow_Buffer) -> Self {
167
// TODO: check anb.format to see if it's ARGB8888?
168
// TODO: infer bpp from anb.format?
169
const BYTES_PER_PIXEL: u32 = 4;
170
let stride_bytes = BYTES_PER_PIXEL * u32::try_from(anb.stride).unwrap();
171
let buffer_size = stride_bytes * u32::try_from(anb.height).unwrap();
172
let buffer =
173
// SAFETY: get_android_surface_buffer guarantees that bits points to a valid buffer and
174
// the buffer remains available until post_android_surface_buffer is called.
175
unsafe { slice::from_raw_parts_mut(anb.bits, buffer_size.try_into().unwrap()) };
176
Self::new(VolatileSlice::new(buffer), stride_bytes, BYTES_PER_PIXEL)
177
}
178
}
179
180
#[repr(C)]
181
pub struct AHardwareBufferInfo {
182
pub num_fds: usize,
183
pub fds_ptr: *const i32,
184
pub metadata_len: usize,
185
pub metadata_ptr: *const u8,
186
}
187
188
// The key is an import id.
189
type AHardwareBufferImportMap = HashMap<u32, AhbInfo>;
190
191
struct AndroidSurface {
192
context: Rc<AndroidDisplayContextWrapper>,
193
surface: NonNull<AndroidDisplaySurface>,
194
ahb_import_map: Arc<RwLock<AHardwareBufferImportMap>>,
195
}
196
197
impl GpuDisplaySurface for AndroidSurface {
198
fn framebuffer(&mut self) -> Option<GpuDisplayFramebuffer> {
199
let mut anb = ANativeWindow_Buffer::default();
200
// SAFETY: context and surface are opaque handles and buf is used as the out parameter to
201
// hold the return values.
202
let success = unsafe {
203
get_android_surface_buffer(
204
self.context.0.as_ptr(),
205
self.surface.as_ptr(),
206
&mut anb as *mut ANativeWindow_Buffer,
207
)
208
};
209
if success {
210
Some(anb.into())
211
} else {
212
None
213
}
214
}
215
216
fn flip(&mut self) {
217
// SAFETY: context and surface are opaque handles.
218
unsafe { post_android_surface_buffer(self.context.0.as_ptr(), self.surface.as_ptr()) }
219
}
220
221
fn set_position(&mut self, x: u32, y: u32) {
222
// SAFETY: context is an opaque handle.
223
unsafe { set_android_surface_position(self.context.0.as_ptr(), x, y) };
224
}
225
226
fn flip_to(
227
&mut self,
228
import_id: u32,
229
_acquire_timepoint: Option<SemaphoreTimepoint>,
230
_release_timepoint: Option<SemaphoreTimepoint>,
231
_extra_info: Option<FlipToExtraInfo>,
232
) -> anyhow::Result<Waitable> {
233
{
234
let ahb_import_map = self.ahb_import_map.read().expect("failed to get a lock");
235
let ahb = ahb_import_map
236
.get(&import_id)
237
.ok_or(GpuDisplayError::InvalidImportId)?;
238
let fds: Vec<i32> = ahb.fds.iter().map(|fd| fd.as_fd().as_raw_fd()).collect();
239
240
let info = AHardwareBufferInfo {
241
num_fds: fds.len(),
242
fds_ptr: fds.as_ptr(),
243
metadata_len: ahb.metadata.len(),
244
metadata_ptr: ahb.metadata.as_ptr(),
245
};
246
// SAFETY:
247
// Safe because the `AHardwareBufferInfo` outlives the call, and
248
// `android_display_flip_to` does not retain the pointer.
249
unsafe {
250
android_display_flip_to(
251
self.context.0.as_ptr(),
252
self.surface.as_ptr(),
253
&info as *const AHardwareBufferInfo,
254
)
255
};
256
}
257
Ok(Waitable::signaled())
258
}
259
}
260
261
pub struct DisplayAndroid {
262
context: Rc<AndroidDisplayContextWrapper>,
263
/// This event is never triggered and is used solely to fulfill AsRawDescriptor.
264
event: Event,
265
// The key is a surface id.
266
surface_ahbs_map: HashMap<u32, Arc<RwLock<AHardwareBufferImportMap>>>,
267
}
268
269
impl DisplayAndroid {
270
pub fn new(name: &str) -> GpuDisplayResult<DisplayAndroid> {
271
let name = CString::new(name).unwrap();
272
let context = NonNull::new(
273
// SAFETY: service_name is not leaked outside of this function
274
unsafe { create_android_display_context(name.as_ptr(), error_callback) },
275
)
276
.ok_or(GpuDisplayError::Unsupported)?;
277
let context = AndroidDisplayContextWrapper(context);
278
let event = Event::new().map_err(|_| GpuDisplayError::CreateEvent)?;
279
Ok(DisplayAndroid {
280
context: context.into(),
281
event,
282
surface_ahbs_map: HashMap::new(),
283
})
284
}
285
}
286
287
impl DisplayT for DisplayAndroid {
288
fn create_surface(
289
&mut self,
290
parent_surface_id: Option<u32>,
291
surface_id: u32,
292
_scanout_id: Option<u32>,
293
display_params: &DisplayParameters,
294
_surf_type: SurfaceType,
295
) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
296
let (requested_width, requested_height) = display_params.get_virtual_display_size();
297
// SAFETY: context is an opaque handle.
298
let surface = NonNull::new(unsafe {
299
create_android_surface(
300
self.context.0.as_ptr(),
301
requested_width,
302
requested_height,
303
parent_surface_id.is_some(),
304
)
305
})
306
.ok_or(GpuDisplayError::CreateSurface)?;
307
let ahb_import_map = self.surface_ahbs_map.entry(surface_id).or_default();
308
309
Ok(Box::new(AndroidSurface {
310
context: self.context.clone(),
311
surface,
312
ahb_import_map: Arc::clone(ahb_import_map),
313
}))
314
}
315
316
fn release_surface(&mut self, surface_id: u32) {
317
self.surface_ahbs_map.remove(&surface_id);
318
}
319
320
fn import_resource(
321
&mut self,
322
import_id: u32,
323
surface_id: u32,
324
external_display_resource: DisplayExternalResourceImport,
325
) -> anyhow::Result<()> {
326
let DisplayExternalResourceImport::AHardwareBuffer { info } = external_display_resource
327
else {
328
bail!("gpu_display_android only supports AHardwareBufferInfo imports");
329
};
330
331
{
332
let mut ahbs = self
333
.surface_ahbs_map
334
.entry(surface_id)
335
.or_default()
336
.write()
337
.expect("failed to get a lock");
338
ahbs.insert(import_id, info);
339
}
340
Ok(())
341
}
342
343
fn release_import(&mut self, surface_id: u32, import_id: u32) {
344
let mut ahbs = self
345
.surface_ahbs_map
346
.entry(surface_id)
347
.or_default()
348
.write()
349
.expect("failed to get a lock");
350
ahbs.remove(&import_id);
351
}
352
}
353
354
impl SysDisplayT for DisplayAndroid {}
355
356
impl AsRawDescriptor for DisplayAndroid {
357
fn as_raw_descriptor(&self) -> RawDescriptor {
358
self.event.as_raw_descriptor()
359
}
360
}
361
362