1 // Copyright 2020 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 //! gfxstream: Handles 3D virtio-gpu hypercalls using gfxstream.
6 //!
7 //! External code found at <https://android.googlesource.com/device/generic/vulkan-cereal/>.
8
9 #![cfg(feature = "gfxstream")]
10
11 use std::convert::TryInto;
12 use std::ffi::CString;
13 use std::io::IoSliceMut;
14 use std::mem::size_of;
15 use std::os::raw::c_char;
16 use std::os::raw::c_int;
17 use std::os::raw::c_uint;
18 use std::os::raw::c_void;
19 use std::panic::catch_unwind;
20 use std::process::abort;
21 use std::ptr::null;
22 use std::ptr::null_mut;
23 use std::sync::Arc;
24
25 use crate::generated::virgl_renderer_bindings::iovec;
26 use crate::generated::virgl_renderer_bindings::virgl_box;
27 use crate::generated::virgl_renderer_bindings::virgl_renderer_resource_create_args;
28 use crate::renderer_utils::*;
29 use crate::rutabaga_core::RutabagaComponent;
30 use crate::rutabaga_core::RutabagaContext;
31 use crate::rutabaga_core::RutabagaResource;
32 use crate::rutabaga_os::FromRawDescriptor;
33 use crate::rutabaga_os::IntoRawDescriptor;
34 use crate::rutabaga_os::OwnedDescriptor;
35 use crate::rutabaga_os::RawDescriptor;
36 use crate::rutabaga_utils::*;
37
38 // See `virtgpu-gfxstream-renderer.h` for definitions
39 const STREAM_RENDERER_PARAM_USER_DATA: u64 = 1;
40 const STREAM_RENDERER_PARAM_RENDERER_FLAGS: u64 = 2;
41 const STREAM_RENDERER_PARAM_FENCE_CALLBACK: u64 = 3;
42 const STREAM_RENDERER_PARAM_WIN0_WIDTH: u64 = 4;
43 const STREAM_RENDERER_PARAM_WIN0_HEIGHT: u64 = 5;
44 const STREAM_RENDERER_PARAM_DEBUG_CALLBACK: u64 = 6;
45 const STREAM_RENDERER_PARAM_RENDERER_FEATURES: u64 = 11;
46
47 #[repr(C)]
48 #[derive(Clone, Copy, Debug)]
49 pub struct stream_renderer_param {
50 key: u64,
51 value: u64,
52 }
53
54 #[repr(C)]
55 #[derive(Copy, Clone, Default)]
56 pub struct stream_renderer_handle {
57 pub os_handle: i64,
58 pub handle_type: u32,
59 }
60
61 #[repr(C)]
62 #[derive(Copy, Clone, Default)]
63 pub struct stream_renderer_vulkan_info {
64 pub memory_index: u32,
65 pub device_uuid: [u8; 16],
66 pub driver_uuid: [u8; 16],
67 }
68
69 #[repr(C)]
70 pub struct stream_renderer_command {
71 pub ctx_id: u32,
72 pub cmd_size: u32,
73 pub cmd: *const u8,
74 pub num_in_fences: u32,
75 pub in_fence_descriptors: *const u64,
76 }
77
78 #[allow(non_camel_case_types)]
79 pub type stream_renderer_create_blob = ResourceCreateBlob;
80
81 #[allow(non_camel_case_types)]
82 pub type stream_renderer_resource_create_args = virgl_renderer_resource_create_args;
83
84 #[allow(non_camel_case_types)]
85 pub type stream_renderer_box = virgl_box;
86
87 #[allow(non_camel_case_types)]
88 pub type stream_renderer_fence = RutabagaFence;
89
90 #[allow(non_camel_case_types)]
91 pub type stream_renderer_debug = RutabagaDebug;
92
93 extern "C" {
94 // Entry point for the stream renderer.
stream_renderer_init( stream_renderer_params: *mut stream_renderer_param, num_params: u64, ) -> c_int95 fn stream_renderer_init(
96 stream_renderer_params: *mut stream_renderer_param,
97 num_params: u64,
98 ) -> c_int;
99
100 // Shutdown entry point for the renderer.
stream_renderer_teardown()101 fn stream_renderer_teardown();
102
103 // virtio-gpu-3d ioctl functions (begin)
104
105 // In gfxstream, the resource create/transfer ioctls correspond to creating buffers for API
106 // forwarding and the notification of new API calls forwarded by the guest, unless they
107 // correspond to minigbm resource targets (PIPE_TEXTURE_2D), in which case they create globally
108 // visible shared GL textures to support gralloc.
stream_renderer_resource_create( args: *mut stream_renderer_resource_create_args, iov: *mut iovec, num_iovs: u32, ) -> c_int109 fn stream_renderer_resource_create(
110 args: *mut stream_renderer_resource_create_args,
111 iov: *mut iovec,
112 num_iovs: u32,
113 ) -> c_int;
114
stream_renderer_resource_unref(res_handle: u32)115 fn stream_renderer_resource_unref(res_handle: u32);
stream_renderer_context_destroy(handle: u32)116 fn stream_renderer_context_destroy(handle: u32);
stream_renderer_transfer_read_iov( handle: u32, ctx_id: u32, level: u32, stride: u32, layer_stride: u32, box_: *mut stream_renderer_box, offset: u64, iov: *mut iovec, iovec_cnt: c_int, ) -> c_int117 fn stream_renderer_transfer_read_iov(
118 handle: u32,
119 ctx_id: u32,
120 level: u32,
121 stride: u32,
122 layer_stride: u32,
123 box_: *mut stream_renderer_box,
124 offset: u64,
125 iov: *mut iovec,
126 iovec_cnt: c_int,
127 ) -> c_int;
stream_renderer_transfer_write_iov( handle: u32, ctx_id: u32, level: c_int, stride: u32, layer_stride: u32, box_: *mut stream_renderer_box, offset: u64, iovec: *mut iovec, iovec_cnt: c_uint, ) -> c_int128 fn stream_renderer_transfer_write_iov(
129 handle: u32,
130 ctx_id: u32,
131 level: c_int,
132 stride: u32,
133 layer_stride: u32,
134 box_: *mut stream_renderer_box,
135 offset: u64,
136 iovec: *mut iovec,
137 iovec_cnt: c_uint,
138 ) -> c_int;
stream_renderer_submit_cmd(cmd: *const stream_renderer_command) -> c_int139 fn stream_renderer_submit_cmd(cmd: *const stream_renderer_command) -> c_int;
stream_renderer_resource_attach_iov( res_handle: c_int, iov: *mut iovec, num_iovs: c_int, ) -> c_int140 fn stream_renderer_resource_attach_iov(
141 res_handle: c_int,
142 iov: *mut iovec,
143 num_iovs: c_int,
144 ) -> c_int;
stream_renderer_resource_detach_iov( res_handle: c_int, iov: *mut *mut iovec, num_iovs: *mut c_int, )145 fn stream_renderer_resource_detach_iov(
146 res_handle: c_int,
147 iov: *mut *mut iovec,
148 num_iovs: *mut c_int,
149 );
stream_renderer_create_fence(fence: *const stream_renderer_fence) -> c_int150 fn stream_renderer_create_fence(fence: *const stream_renderer_fence) -> c_int;
151 #[cfg(gfxstream_unstable)]
stream_renderer_export_fence(fence_id: u64, handle: *mut stream_renderer_handle) -> c_int152 fn stream_renderer_export_fence(fence_id: u64, handle: *mut stream_renderer_handle) -> c_int;
stream_renderer_ctx_attach_resource(ctx_id: c_int, res_handle: c_int)153 fn stream_renderer_ctx_attach_resource(ctx_id: c_int, res_handle: c_int);
stream_renderer_ctx_detach_resource(ctx_id: c_int, res_handle: c_int)154 fn stream_renderer_ctx_detach_resource(ctx_id: c_int, res_handle: c_int);
stream_renderer_get_cap_set(set: u32, max_ver: *mut u32, max_size: *mut u32)155 fn stream_renderer_get_cap_set(set: u32, max_ver: *mut u32, max_size: *mut u32);
stream_renderer_fill_caps(set: u32, version: u32, caps: *mut c_void)156 fn stream_renderer_fill_caps(set: u32, version: u32, caps: *mut c_void);
157
stream_renderer_flush(res_handle: u32)158 fn stream_renderer_flush(res_handle: u32);
stream_renderer_create_blob( ctx_id: u32, res_handle: u32, create_blob: *const stream_renderer_create_blob, iovecs: *const iovec, num_iovs: u32, handle: *const stream_renderer_handle, ) -> c_int159 fn stream_renderer_create_blob(
160 ctx_id: u32,
161 res_handle: u32,
162 create_blob: *const stream_renderer_create_blob,
163 iovecs: *const iovec,
164 num_iovs: u32,
165 handle: *const stream_renderer_handle,
166 ) -> c_int;
167
stream_renderer_export_blob(res_handle: u32, handle: *mut stream_renderer_handle) -> c_int168 fn stream_renderer_export_blob(res_handle: u32, handle: *mut stream_renderer_handle) -> c_int;
stream_renderer_resource_map( res_handle: u32, map: *mut *mut c_void, out_size: *mut u64, ) -> c_int169 fn stream_renderer_resource_map(
170 res_handle: u32,
171 map: *mut *mut c_void,
172 out_size: *mut u64,
173 ) -> c_int;
stream_renderer_resource_unmap(res_handle: u32) -> c_int174 fn stream_renderer_resource_unmap(res_handle: u32) -> c_int;
stream_renderer_resource_map_info(res_handle: u32, map_info: *mut u32) -> c_int175 fn stream_renderer_resource_map_info(res_handle: u32, map_info: *mut u32) -> c_int;
stream_renderer_vulkan_info( res_handle: u32, vulkan_info: *mut stream_renderer_vulkan_info, ) -> c_int176 fn stream_renderer_vulkan_info(
177 res_handle: u32,
178 vulkan_info: *mut stream_renderer_vulkan_info,
179 ) -> c_int;
stream_renderer_context_create( handle: u32, nlen: u32, name: *const c_char, context_init: u32, ) -> c_int180 fn stream_renderer_context_create(
181 handle: u32,
182 nlen: u32,
183 name: *const c_char,
184 context_init: u32,
185 ) -> c_int;
186
187 #[cfg(gfxstream_unstable)]
stream_renderer_suspend() -> c_int188 fn stream_renderer_suspend() -> c_int;
189
190 #[cfg(gfxstream_unstable)]
stream_renderer_snapshot(dir: *const c_char) -> c_int191 fn stream_renderer_snapshot(dir: *const c_char) -> c_int;
192
193 #[cfg(gfxstream_unstable)]
stream_renderer_restore(dir: *const c_char) -> c_int194 fn stream_renderer_restore(dir: *const c_char) -> c_int;
195
196 #[cfg(gfxstream_unstable)]
stream_renderer_resume() -> c_int197 fn stream_renderer_resume() -> c_int;
198
199 #[cfg(gfxstream_unstable)]
stream_renderer_wait_sync_resource(res_handle: u32) -> c_int200 fn stream_renderer_wait_sync_resource(res_handle: u32) -> c_int;
201 }
202
203 /// The virtio-gpu backend state tracker which supports accelerated rendering.
204 pub struct Gfxstream {
205 /// Cookie used by Gfxstream, should be held as long as the renderer is alive.
206 _cookie: Box<RutabagaCookie>,
207 }
208
209 struct GfxstreamContext {
210 ctx_id: u32,
211 fence_handler: RutabagaFenceHandler,
212 }
213
214 impl GfxstreamContext {
215 #[cfg(gfxstream_unstable)]
export_fence(&self, fence_id: u64) -> RutabagaResult<RutabagaHandle>216 fn export_fence(&self, fence_id: u64) -> RutabagaResult<RutabagaHandle> {
217 let mut stream_handle: stream_renderer_handle = Default::default();
218 // SAFETY:
219 // Safe because a correctly formatted stream_handle is given to gfxstream.
220 let ret = unsafe { stream_renderer_export_fence(fence_id, &mut stream_handle) };
221 ret_to_res(ret)?;
222
223 let raw_descriptor = stream_handle.os_handle as RawDescriptor;
224 // SAFETY:
225 // Safe because the handle was just returned by a successful gfxstream call so it must
226 // be valid and owned by us.
227 let handle = unsafe { OwnedDescriptor::from_raw_descriptor(raw_descriptor) };
228
229 Ok(RutabagaHandle {
230 os_handle: handle,
231 handle_type: stream_handle.handle_type,
232 })
233 }
234
235 #[cfg(not(gfxstream_unstable))]
export_fence(&self, _fence_id: u64) -> RutabagaResult<RutabagaHandle>236 fn export_fence(&self, _fence_id: u64) -> RutabagaResult<RutabagaHandle> {
237 Err(RutabagaError::Unsupported)
238 }
239 }
240
241 impl RutabagaContext for GfxstreamContext {
submit_cmd( &mut self, commands: &mut [u8], _fence_ids: &[u64], _shareable_fences: Vec<RutabagaHandle>, ) -> RutabagaResult<()>242 fn submit_cmd(
243 &mut self,
244 commands: &mut [u8],
245 _fence_ids: &[u64],
246 _shareable_fences: Vec<RutabagaHandle>,
247 ) -> RutabagaResult<()> {
248 if commands.len() % size_of::<u32>() != 0 {
249 return Err(RutabagaError::InvalidCommandSize(commands.len()));
250 }
251
252 // TODO(b/315870313): Add safety comment
253 #[allow(clippy::undocumented_unsafe_blocks)]
254 let ret = unsafe {
255 let cmd = stream_renderer_command {
256 ctx_id: self.ctx_id,
257 cmd_size: commands.len().try_into()?,
258 cmd: commands.as_mut_ptr(),
259 num_in_fences: 0,
260 in_fence_descriptors: null(),
261 };
262
263 stream_renderer_submit_cmd(&cmd as *const stream_renderer_command)
264 };
265 ret_to_res(ret)
266 }
267
attach(&mut self, resource: &mut RutabagaResource)268 fn attach(&mut self, resource: &mut RutabagaResource) {
269 // SAFETY:
270 // The context id and resource id must be valid because the respective instances ensure
271 // their lifetime.
272 unsafe {
273 stream_renderer_ctx_attach_resource(self.ctx_id as i32, resource.resource_id as i32);
274 }
275 }
276
detach(&mut self, resource: &RutabagaResource)277 fn detach(&mut self, resource: &RutabagaResource) {
278 // SAFETY:
279 // The context id and resource id must be valid because the respective instances ensure
280 // their lifetime.
281 unsafe {
282 stream_renderer_ctx_detach_resource(self.ctx_id as i32, resource.resource_id as i32);
283 }
284 }
285
component_type(&self) -> RutabagaComponentType286 fn component_type(&self) -> RutabagaComponentType {
287 RutabagaComponentType::Gfxstream
288 }
289
context_create_fence( &mut self, fence: RutabagaFence, ) -> RutabagaResult<Option<RutabagaHandle>>290 fn context_create_fence(
291 &mut self,
292 fence: RutabagaFence,
293 ) -> RutabagaResult<Option<RutabagaHandle>> {
294 if fence.ring_idx as u32 == 1 {
295 self.fence_handler.call(fence);
296 return Ok(None);
297 }
298
299 // SAFETY:
300 // Safe because RutabagaFences and stream_renderer_fence are ABI identical
301 let ret = unsafe { stream_renderer_create_fence(&fence as *const stream_renderer_fence) };
302 ret_to_res(ret)?;
303
304 let mut hnd: Option<RutabagaHandle> = None;
305 if fence.flags & RUTABAGA_FLAG_FENCE_HOST_SHAREABLE != 0 {
306 hnd = Some(self.export_fence(fence.fence_id)?);
307 }
308
309 Ok(hnd)
310 }
311 }
312
313 impl Drop for GfxstreamContext {
drop(&mut self)314 fn drop(&mut self) {
315 // SAFETY:
316 // The context is safe to destroy because nothing else can be referencing it.
317 unsafe {
318 stream_renderer_context_destroy(self.ctx_id);
319 }
320 }
321 }
322
write_context_fence(cookie: *mut c_void, fence: *const RutabagaFence)323 extern "C" fn write_context_fence(cookie: *mut c_void, fence: *const RutabagaFence) {
324 catch_unwind(|| {
325 assert!(!cookie.is_null());
326 // SAFETY:
327 // We trust gfxstream not give a dangling pointer
328 let cookie = unsafe { &*(cookie as *mut RutabagaCookie) };
329 if let Some(handler) = &cookie.fence_handler {
330 // SAFETY:
331 // We trust gfxstream not give a dangling pointer
332 unsafe { handler.call(*fence) };
333 }
334 })
335 .unwrap_or_else(|_| abort())
336 }
337
gfxstream_debug_callback(cookie: *mut c_void, debug: *const stream_renderer_debug)338 extern "C" fn gfxstream_debug_callback(cookie: *mut c_void, debug: *const stream_renderer_debug) {
339 catch_unwind(|| {
340 assert!(!cookie.is_null());
341 // SAFETY:
342 // We trust gfxstream not give a dangling pointer
343 let cookie = unsafe { &*(cookie as *mut RutabagaCookie) };
344 if let Some(handler) = &cookie.debug_handler {
345 // SAFETY:
346 // We trust gfxstream not give a dangling pointer
347 unsafe { handler.call(*debug) };
348 }
349 })
350 .unwrap_or_else(|_| abort())
351 }
352
353 impl Gfxstream {
init( display_width: u32, display_height: u32, gfxstream_flags: GfxstreamFlags, gfxstream_features: Option<String>, fence_handler: RutabagaFenceHandler, debug_handler: Option<RutabagaDebugHandler>, ) -> RutabagaResult<Box<dyn RutabagaComponent>>354 pub fn init(
355 display_width: u32,
356 display_height: u32,
357 gfxstream_flags: GfxstreamFlags,
358 gfxstream_features: Option<String>,
359 fence_handler: RutabagaFenceHandler,
360 debug_handler: Option<RutabagaDebugHandler>,
361 ) -> RutabagaResult<Box<dyn RutabagaComponent>> {
362 let use_debug = debug_handler.is_some();
363 let mut cookie = Box::new(RutabagaCookie {
364 render_server_fd: None,
365 fence_handler: Some(fence_handler),
366 debug_handler,
367 });
368
369 let mut stream_renderer_params = Vec::from([
370 stream_renderer_param {
371 key: STREAM_RENDERER_PARAM_USER_DATA,
372 // Safe as cookie outlives the stream renderer (stream_renderer_teardown called
373 // at Gfxstream Drop)
374 value: &mut *cookie as *mut RutabagaCookie as u64,
375 },
376 stream_renderer_param {
377 key: STREAM_RENDERER_PARAM_RENDERER_FLAGS,
378 value: gfxstream_flags.into(),
379 },
380 stream_renderer_param {
381 key: STREAM_RENDERER_PARAM_FENCE_CALLBACK,
382 value: write_context_fence as usize as u64,
383 },
384 stream_renderer_param {
385 key: STREAM_RENDERER_PARAM_WIN0_WIDTH,
386 value: display_width as u64,
387 },
388 stream_renderer_param {
389 key: STREAM_RENDERER_PARAM_WIN0_HEIGHT,
390 value: display_height as u64,
391 },
392 ]);
393
394 if use_debug {
395 stream_renderer_params.push(stream_renderer_param {
396 key: STREAM_RENDERER_PARAM_DEBUG_CALLBACK,
397 value: gfxstream_debug_callback as usize as u64,
398 });
399 }
400
401 let features_cstr = gfxstream_features.map(|f| CString::new(f).unwrap());
402 if let Some(features_cstr) = &features_cstr {
403 stream_renderer_params.push(stream_renderer_param {
404 key: STREAM_RENDERER_PARAM_RENDERER_FEATURES,
405 value: features_cstr.as_ptr() as u64,
406 });
407 }
408
409 // TODO(b/315870313): Add safety comment
410 #[allow(clippy::undocumented_unsafe_blocks)]
411 unsafe {
412 ret_to_res(stream_renderer_init(
413 stream_renderer_params.as_mut_ptr(),
414 stream_renderer_params.len() as u64,
415 ))?;
416 }
417
418 Ok(Box::new(Gfxstream { _cookie: cookie }))
419 }
420
map_info(&self, resource_id: u32) -> RutabagaResult<u32>421 fn map_info(&self, resource_id: u32) -> RutabagaResult<u32> {
422 let mut map_info = 0;
423 // SAFETY:
424 // Safe because `map_info` is a local stack variable owned by us.
425 let ret = unsafe { stream_renderer_resource_map_info(resource_id, &mut map_info) };
426 ret_to_res(ret)?;
427
428 Ok(map_info | RUTABAGA_MAP_ACCESS_RW)
429 }
430
vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo>431 fn vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo> {
432 let mut vulkan_info: stream_renderer_vulkan_info = Default::default();
433 // SAFETY:
434 // Safe because `vulkan_info` is a local stack variable owned by us.
435 let ret = unsafe { stream_renderer_vulkan_info(resource_id, &mut vulkan_info) };
436 ret_to_res(ret)?;
437
438 Ok(VulkanInfo {
439 memory_idx: vulkan_info.memory_index,
440 device_id: DeviceId {
441 device_uuid: vulkan_info.device_uuid,
442 driver_uuid: vulkan_info.driver_uuid,
443 },
444 })
445 }
446
export_blob(&self, resource_id: u32) -> RutabagaResult<Arc<RutabagaHandle>>447 fn export_blob(&self, resource_id: u32) -> RutabagaResult<Arc<RutabagaHandle>> {
448 let mut stream_handle: stream_renderer_handle = Default::default();
449 // TODO(b/315870313): Add safety comment
450 #[allow(clippy::undocumented_unsafe_blocks)]
451 let ret = unsafe { stream_renderer_export_blob(resource_id, &mut stream_handle) };
452 ret_to_res(ret)?;
453
454 let raw_descriptor = stream_handle.os_handle as RawDescriptor;
455 // SAFETY:
456 // Safe because the handle was just returned by a successful gfxstream call so it must be
457 // valid and owned by us.
458 let handle = unsafe { OwnedDescriptor::from_raw_descriptor(raw_descriptor) };
459
460 Ok(Arc::new(RutabagaHandle {
461 os_handle: handle,
462 handle_type: stream_handle.handle_type,
463 }))
464 }
465 }
466
467 impl Drop for Gfxstream {
drop(&mut self)468 fn drop(&mut self) {
469 // SAFETY: Safe because Gfxstream was successfully initialized.
470 unsafe {
471 stream_renderer_teardown();
472 }
473 }
474 }
475
476 impl RutabagaComponent for Gfxstream {
get_capset_info(&self, capset_id: u32) -> (u32, u32)477 fn get_capset_info(&self, capset_id: u32) -> (u32, u32) {
478 let mut version = 0;
479 let mut size = 0;
480 // SAFETY:
481 // Safe because gfxstream is initialized by now and properly size stack variables are
482 // used for the pointers.
483 unsafe {
484 stream_renderer_get_cap_set(capset_id, &mut version, &mut size);
485 }
486 (version, size)
487 }
488
get_capset(&self, capset_id: u32, version: u32) -> Vec<u8>489 fn get_capset(&self, capset_id: u32, version: u32) -> Vec<u8> {
490 let (_, max_size) = self.get_capset_info(capset_id);
491 let mut buf = vec![0u8; max_size as usize];
492 // SAFETY:
493 // Safe because gfxstream is initialized by now and the given buffer is sized properly
494 // for the given cap id/version.
495 unsafe {
496 stream_renderer_fill_caps(capset_id, version, buf.as_mut_ptr() as *mut c_void);
497 }
498
499 buf
500 }
501
create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()>502 fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
503 // SAFETY:
504 // Safe because RutabagaFences and stream_renderer_fence are ABI identical
505 let ret = unsafe { stream_renderer_create_fence(&fence as *const stream_renderer_fence) };
506 ret_to_res(ret)
507 }
508
create_3d( &self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<RutabagaResource>509 fn create_3d(
510 &self,
511 resource_id: u32,
512 resource_create_3d: ResourceCreate3D,
513 ) -> RutabagaResult<RutabagaResource> {
514 let mut args = virgl_renderer_resource_create_args {
515 handle: resource_id,
516 target: resource_create_3d.target,
517 format: resource_create_3d.format,
518 bind: resource_create_3d.bind,
519 width: resource_create_3d.width,
520 height: resource_create_3d.height,
521 depth: resource_create_3d.depth,
522 array_size: resource_create_3d.array_size,
523 last_level: resource_create_3d.last_level,
524 nr_samples: resource_create_3d.nr_samples,
525 flags: resource_create_3d.flags,
526 };
527
528 // SAFETY:
529 // Safe because gfxstream is initialized by now, and the return value is checked before
530 // returning a new resource. The backing buffers are not supplied with this call.
531 let ret = unsafe { stream_renderer_resource_create(&mut args, null_mut(), 0) };
532 ret_to_res(ret)?;
533
534 Ok(RutabagaResource {
535 resource_id,
536 handle: None,
537 blob: false,
538 blob_mem: 0,
539 blob_flags: 0,
540 map_info: None,
541 info_2d: None,
542 info_3d: None,
543 vulkan_info: None,
544 backing_iovecs: None,
545 component_mask: 1 << (RutabagaComponentType::Gfxstream as u8),
546 size: 0,
547 mapping: None,
548 })
549 }
550
attach_backing( &self, resource_id: u32, vecs: &mut Vec<RutabagaIovec>, ) -> RutabagaResult<()>551 fn attach_backing(
552 &self,
553 resource_id: u32,
554 vecs: &mut Vec<RutabagaIovec>,
555 ) -> RutabagaResult<()> {
556 // TODO(b/315870313): Add safety comment
557 #[allow(clippy::undocumented_unsafe_blocks)]
558 let ret = unsafe {
559 stream_renderer_resource_attach_iov(
560 resource_id as i32,
561 vecs.as_mut_ptr() as *mut iovec,
562 vecs.len() as i32,
563 )
564 };
565 ret_to_res(ret)
566 }
567
detach_backing(&self, resource_id: u32)568 fn detach_backing(&self, resource_id: u32) {
569 // TODO(b/315870313): Add safety comment
570 #[allow(clippy::undocumented_unsafe_blocks)]
571 unsafe {
572 stream_renderer_resource_detach_iov(
573 resource_id as i32,
574 std::ptr::null_mut(),
575 std::ptr::null_mut(),
576 );
577 }
578 }
579
unref_resource(&self, resource_id: u32)580 fn unref_resource(&self, resource_id: u32) {
581 // SAFETY:
582 // The resource is safe to unreference destroy because no user of these bindings can still
583 // be holding a reference.
584 unsafe {
585 stream_renderer_resource_unref(resource_id);
586 }
587 }
588
transfer_write( &self, ctx_id: u32, resource: &mut RutabagaResource, transfer: Transfer3D, ) -> RutabagaResult<()>589 fn transfer_write(
590 &self,
591 ctx_id: u32,
592 resource: &mut RutabagaResource,
593 transfer: Transfer3D,
594 ) -> RutabagaResult<()> {
595 if transfer.is_empty() {
596 return Ok(());
597 }
598
599 let mut transfer_box = VirglBox {
600 x: transfer.x,
601 y: transfer.y,
602 z: transfer.z,
603 w: transfer.w,
604 h: transfer.h,
605 d: transfer.d,
606 };
607
608 // SAFETY:
609 // Safe because only stack variables of the appropriate type are used.
610 let ret = unsafe {
611 stream_renderer_transfer_write_iov(
612 resource.resource_id,
613 ctx_id,
614 transfer.level as i32,
615 transfer.stride,
616 transfer.layer_stride,
617 &mut transfer_box as *mut VirglBox as *mut stream_renderer_box,
618 transfer.offset,
619 null_mut(),
620 0,
621 )
622 };
623 ret_to_res(ret)
624 }
625
transfer_read( &self, ctx_id: u32, resource: &mut RutabagaResource, transfer: Transfer3D, buf: Option<IoSliceMut>, ) -> RutabagaResult<()>626 fn transfer_read(
627 &self,
628 ctx_id: u32,
629 resource: &mut RutabagaResource,
630 transfer: Transfer3D,
631 buf: Option<IoSliceMut>,
632 ) -> RutabagaResult<()> {
633 if transfer.is_empty() {
634 return Ok(());
635 }
636
637 let mut transfer_box = VirglBox {
638 x: transfer.x,
639 y: transfer.y,
640 z: transfer.z,
641 w: transfer.w,
642 h: transfer.h,
643 d: transfer.d,
644 };
645
646 let mut iov = RutabagaIovec {
647 base: null_mut(),
648 len: 0,
649 };
650
651 let (iovecs, num_iovecs) = match buf {
652 Some(mut buf) => {
653 iov.base = buf.as_mut_ptr() as *mut c_void;
654 iov.len = buf.len();
655 (&mut iov as *mut RutabagaIovec as *mut iovec, 1)
656 }
657 None => (null_mut(), 0),
658 };
659
660 // SAFETY:
661 // Safe because only stack variables of the appropriate type are used.
662 let ret = unsafe {
663 stream_renderer_transfer_read_iov(
664 resource.resource_id,
665 ctx_id,
666 transfer.level,
667 transfer.stride,
668 transfer.layer_stride,
669 &mut transfer_box as *mut VirglBox as *mut stream_renderer_box,
670 transfer.offset,
671 iovecs,
672 num_iovecs,
673 )
674 };
675 ret_to_res(ret)
676 }
677
resource_flush(&self, resource: &mut RutabagaResource) -> RutabagaResult<()>678 fn resource_flush(&self, resource: &mut RutabagaResource) -> RutabagaResult<()> {
679 // TODO(b/315870313): Add safety comment
680 #[allow(clippy::undocumented_unsafe_blocks)]
681 unsafe {
682 stream_renderer_flush(resource.resource_id);
683 }
684 Ok(())
685 }
686
create_blob( &mut self, ctx_id: u32, resource_id: u32, resource_create_blob: ResourceCreateBlob, mut iovec_opt: Option<Vec<RutabagaIovec>>, handle_opt: Option<RutabagaHandle>, ) -> RutabagaResult<RutabagaResource>687 fn create_blob(
688 &mut self,
689 ctx_id: u32,
690 resource_id: u32,
691 resource_create_blob: ResourceCreateBlob,
692 mut iovec_opt: Option<Vec<RutabagaIovec>>,
693 handle_opt: Option<RutabagaHandle>,
694 ) -> RutabagaResult<RutabagaResource> {
695 let mut iovec_ptr = null_mut();
696 let mut num_iovecs = 0;
697 if let Some(ref mut iovecs) = iovec_opt {
698 iovec_ptr = iovecs.as_mut_ptr();
699 num_iovecs = iovecs.len() as u32;
700 }
701
702 let mut handle_ptr = null();
703 let mut stream_handle: stream_renderer_handle = Default::default();
704 if let Some(handle) = handle_opt {
705 stream_handle.handle_type = handle.handle_type;
706 stream_handle.os_handle = handle.os_handle.into_raw_descriptor() as i64;
707 handle_ptr = &stream_handle;
708 }
709
710 // TODO(b/315870313): Add safety comment
711 #[allow(clippy::undocumented_unsafe_blocks)]
712 let ret = unsafe {
713 stream_renderer_create_blob(
714 ctx_id,
715 resource_id,
716 &resource_create_blob as *const stream_renderer_create_blob,
717 iovec_ptr as *const iovec,
718 num_iovecs,
719 handle_ptr,
720 )
721 };
722
723 ret_to_res(ret)?;
724
725 Ok(RutabagaResource {
726 resource_id,
727 handle: self.export_blob(resource_id).ok(),
728 blob: true,
729 blob_mem: resource_create_blob.blob_mem,
730 blob_flags: resource_create_blob.blob_flags,
731 map_info: self.map_info(resource_id).ok(),
732 info_2d: None,
733 info_3d: None,
734 vulkan_info: self.vulkan_info(resource_id).ok(),
735 backing_iovecs: iovec_opt,
736 component_mask: 1 << (RutabagaComponentType::Gfxstream as u8),
737 size: resource_create_blob.size,
738 mapping: None,
739 })
740 }
741
map(&self, resource_id: u32) -> RutabagaResult<RutabagaMapping>742 fn map(&self, resource_id: u32) -> RutabagaResult<RutabagaMapping> {
743 let mut map: *mut c_void = null_mut();
744 let mut size: u64 = 0;
745
746 // SAFETY:
747 // Safe because the Stream renderer wraps and validates use of vkMapMemory.
748 let ret = unsafe { stream_renderer_resource_map(resource_id, &mut map, &mut size) };
749 if ret != 0 {
750 return Err(RutabagaError::MappingFailed(ret));
751 }
752 Ok(RutabagaMapping {
753 ptr: map as u64,
754 size,
755 })
756 }
757
unmap(&self, resource_id: u32) -> RutabagaResult<()>758 fn unmap(&self, resource_id: u32) -> RutabagaResult<()> {
759 // SAFETY:
760 // Safe because the Stream renderer wraps and validates use of vkMapMemory.
761 let ret = unsafe { stream_renderer_resource_unmap(resource_id) };
762 ret_to_res(ret)
763 }
764
create_context( &self, ctx_id: u32, context_init: u32, context_name: Option<&str>, fence_handler: RutabagaFenceHandler, ) -> RutabagaResult<Box<dyn RutabagaContext>>765 fn create_context(
766 &self,
767 ctx_id: u32,
768 context_init: u32,
769 context_name: Option<&str>,
770 fence_handler: RutabagaFenceHandler,
771 ) -> RutabagaResult<Box<dyn RutabagaContext>> {
772 let mut name: &str = "gpu_renderer";
773 if let Some(name_string) = context_name.filter(|s| !s.is_empty()) {
774 name = name_string;
775 }
776
777 // SAFETY:
778 // Safe because gfxstream is initialized by now and the context name is statically
779 // allocated. The return value is checked before returning a new context.
780 let ret = unsafe {
781 stream_renderer_context_create(
782 ctx_id,
783 name.len() as u32,
784 name.as_ptr() as *const c_char,
785 context_init,
786 )
787 };
788 ret_to_res(ret)?;
789 Ok(Box::new(GfxstreamContext {
790 ctx_id,
791 fence_handler,
792 }))
793 }
794
795 #[cfg(gfxstream_unstable)]
suspend(&self) -> RutabagaResult<()>796 fn suspend(&self) -> RutabagaResult<()> {
797 // SAFETY:
798 // Safe because gfxstream is initialized by now.
799 let ret = unsafe { stream_renderer_suspend() };
800 ret_to_res(ret)?;
801 Ok(())
802 }
803
804 #[cfg(gfxstream_unstable)]
snapshot(&self, directory: &str) -> RutabagaResult<()>805 fn snapshot(&self, directory: &str) -> RutabagaResult<()> {
806 let cstring = CString::new(directory)?;
807
808 // SAFETY:
809 // Safe because directory string is valid
810 let ret = unsafe { stream_renderer_snapshot(cstring.as_ptr() as *const c_char) };
811 ret_to_res(ret)?;
812
813 Ok(())
814 }
815
816 #[cfg(gfxstream_unstable)]
restore(&self, directory: &str) -> RutabagaResult<()>817 fn restore(&self, directory: &str) -> RutabagaResult<()> {
818 let cstring = CString::new(directory)?;
819
820 // SAFETY:
821 // Safe because directory string is valid
822 let ret = unsafe { stream_renderer_restore(cstring.as_ptr() as *const c_char) };
823 ret_to_res(ret)?;
824 Ok(())
825 }
826
827 #[cfg(gfxstream_unstable)]
resume(&self) -> RutabagaResult<()>828 fn resume(&self) -> RutabagaResult<()> {
829 // SAFETY:
830 // Safe because gfxstream is initialized by now.
831 let ret = unsafe { stream_renderer_resume() };
832 ret_to_res(ret)?;
833 Ok(())
834 }
835
836 #[cfg(gfxstream_unstable)]
wait_sync(&self, resource: &RutabagaResource) -> RutabagaResult<()>837 fn wait_sync(&self, resource: &RutabagaResource) -> RutabagaResult<()> {
838 let ret = unsafe { stream_renderer_wait_sync_resource(resource.resource_id) };
839 ret_to_res(ret)?;
840 Ok(())
841 }
842 }
843