xref: /aosp_15_r20/external/crosvm/rutabaga_gfx/src/gfxstream.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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