• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::RawDescriptor;
35 use crate::rutabaga_os::SafeDescriptor;
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;
stream_renderer_ctx_attach_resource(ctx_id: c_int, res_handle: c_int)151     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)152     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)153     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)154     fn stream_renderer_fill_caps(set: u32, version: u32, caps: *mut c_void);
155 
stream_renderer_flush(res_handle: u32)156     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_int157     fn stream_renderer_create_blob(
158         ctx_id: u32,
159         res_handle: u32,
160         create_blob: *const stream_renderer_create_blob,
161         iovecs: *const iovec,
162         num_iovs: u32,
163         handle: *const stream_renderer_handle,
164     ) -> c_int;
165 
stream_renderer_export_blob(res_handle: u32, handle: *mut stream_renderer_handle) -> c_int166     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_int167     fn stream_renderer_resource_map(
168         res_handle: u32,
169         map: *mut *mut c_void,
170         out_size: *mut u64,
171     ) -> c_int;
stream_renderer_resource_unmap(res_handle: u32) -> c_int172     fn stream_renderer_resource_unmap(res_handle: u32) -> c_int;
stream_renderer_resource_map_info(res_handle: u32, map_info: *mut u32) -> c_int173     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_int174     fn stream_renderer_vulkan_info(
175         res_handle: u32,
176         vulkan_info: *mut stream_renderer_vulkan_info,
177     ) -> c_int;
stream_renderer_context_create( handle: u32, nlen: u32, name: *const c_char, context_init: u32, ) -> c_int178     fn stream_renderer_context_create(
179         handle: u32,
180         nlen: u32,
181         name: *const c_char,
182         context_init: u32,
183     ) -> c_int;
184 
185     #[cfg(gfxstream_unstable)]
stream_renderer_snapshot(dir: *const c_char) -> c_int186     fn stream_renderer_snapshot(dir: *const c_char) -> c_int;
187 
188     #[cfg(gfxstream_unstable)]
stream_renderer_restore(dir: *const c_char) -> c_int189     fn stream_renderer_restore(dir: *const c_char) -> c_int;
190 }
191 
192 /// The virtio-gpu backend state tracker which supports accelerated rendering.
193 pub struct Gfxstream {
194     /// Cookie used by Gfxstream, should be held as long as the renderer is alive.
195     _cookie: Box<RutabagaCookie>,
196 }
197 
198 struct GfxstreamContext {
199     ctx_id: u32,
200     fence_handler: RutabagaFenceHandler,
201 }
202 
203 impl RutabagaContext for GfxstreamContext {
submit_cmd(&mut self, commands: &mut [u8], fence_ids: &[u64]) -> RutabagaResult<()>204     fn submit_cmd(&mut self, commands: &mut [u8], fence_ids: &[u64]) -> RutabagaResult<()> {
205         if !fence_ids.is_empty() {
206             return Err(RutabagaError::Unsupported);
207         }
208 
209         if commands.len() % size_of::<u32>() != 0 {
210             return Err(RutabagaError::InvalidCommandSize(commands.len()));
211         }
212 
213         // TODO(b/315870313): Add safety comment
214         #[allow(clippy::undocumented_unsafe_blocks)]
215         let ret = unsafe {
216             let cmd = stream_renderer_command {
217                 ctx_id: self.ctx_id,
218                 cmd_size: commands.len().try_into()?,
219                 cmd: commands.as_mut_ptr(),
220                 num_in_fences: 0,
221                 in_fence_descriptors: null(),
222             };
223 
224             stream_renderer_submit_cmd(&cmd as *const stream_renderer_command)
225         };
226         ret_to_res(ret)
227     }
228 
attach(&mut self, resource: &mut RutabagaResource)229     fn attach(&mut self, resource: &mut RutabagaResource) {
230         // SAFETY:
231         // The context id and resource id must be valid because the respective instances ensure
232         // their lifetime.
233         unsafe {
234             stream_renderer_ctx_attach_resource(self.ctx_id as i32, resource.resource_id as i32);
235         }
236     }
237 
detach(&mut self, resource: &RutabagaResource)238     fn detach(&mut self, resource: &RutabagaResource) {
239         // SAFETY:
240         // The context id and resource id must be valid because the respective instances ensure
241         // their lifetime.
242         unsafe {
243             stream_renderer_ctx_detach_resource(self.ctx_id as i32, resource.resource_id as i32);
244         }
245     }
246 
component_type(&self) -> RutabagaComponentType247     fn component_type(&self) -> RutabagaComponentType {
248         RutabagaComponentType::Gfxstream
249     }
250 
context_create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()>251     fn context_create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
252         if fence.ring_idx as u32 == 1 {
253             self.fence_handler.call(fence);
254             return Ok(());
255         }
256 
257         // SAFETY:
258         // Safe because RutabagaFences and stream_renderer_fence are ABI identical
259         let ret = unsafe { stream_renderer_create_fence(&fence as *const stream_renderer_fence) };
260 
261         ret_to_res(ret)
262     }
263 }
264 
265 impl Drop for GfxstreamContext {
drop(&mut self)266     fn drop(&mut self) {
267         // SAFETY:
268         // The context is safe to destroy because nothing else can be referencing it.
269         unsafe {
270             stream_renderer_context_destroy(self.ctx_id);
271         }
272     }
273 }
274 
write_context_fence(cookie: *mut c_void, fence: *const RutabagaFence)275 extern "C" fn write_context_fence(cookie: *mut c_void, fence: *const RutabagaFence) {
276     catch_unwind(|| {
277         assert!(!cookie.is_null());
278         // SAFETY:
279         // We trust gfxstream not give a dangling pointer
280         let cookie = unsafe { &*(cookie as *mut RutabagaCookie) };
281         if let Some(handler) = &cookie.fence_handler {
282             // SAFETY:
283             // We trust gfxstream not give a dangling pointer
284             unsafe { handler.call(*fence) };
285         }
286     })
287     .unwrap_or_else(|_| abort())
288 }
289 
gfxstream_debug_callback(cookie: *mut c_void, debug: *const stream_renderer_debug)290 extern "C" fn gfxstream_debug_callback(cookie: *mut c_void, debug: *const stream_renderer_debug) {
291     catch_unwind(|| {
292         assert!(!cookie.is_null());
293         // SAFETY:
294         // We trust gfxstream not give a dangling pointer
295         let cookie = unsafe { &*(cookie as *mut RutabagaCookie) };
296         if let Some(handler) = &cookie.debug_handler {
297             // SAFETY:
298             // We trust gfxstream not give a dangling pointer
299             unsafe { handler.call(*debug) };
300         }
301     })
302     .unwrap_or_else(|_| abort())
303 }
304 
305 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>>306     pub fn init(
307         display_width: u32,
308         display_height: u32,
309         gfxstream_flags: GfxstreamFlags,
310         gfxstream_features: Option<String>,
311         fence_handler: RutabagaFenceHandler,
312         debug_handler: Option<RutabagaDebugHandler>,
313     ) -> RutabagaResult<Box<dyn RutabagaComponent>> {
314         let use_debug = debug_handler.is_some();
315         let mut cookie = Box::new(RutabagaCookie {
316             render_server_fd: None,
317             fence_handler: Some(fence_handler),
318             debug_handler,
319         });
320 
321         let mut stream_renderer_params = Vec::from([
322             stream_renderer_param {
323                 key: STREAM_RENDERER_PARAM_USER_DATA,
324                 // Safe as cookie outlives the stream renderer (stream_renderer_teardown called
325                 // at Gfxstream Drop)
326                 value: &mut *cookie as *mut RutabagaCookie as u64,
327             },
328             stream_renderer_param {
329                 key: STREAM_RENDERER_PARAM_RENDERER_FLAGS,
330                 value: gfxstream_flags.into(),
331             },
332             stream_renderer_param {
333                 key: STREAM_RENDERER_PARAM_FENCE_CALLBACK,
334                 value: write_context_fence as usize as u64,
335             },
336             stream_renderer_param {
337                 key: STREAM_RENDERER_PARAM_WIN0_WIDTH,
338                 value: display_width as u64,
339             },
340             stream_renderer_param {
341                 key: STREAM_RENDERER_PARAM_WIN0_HEIGHT,
342                 value: display_height as u64,
343             },
344         ]);
345 
346         if use_debug {
347             stream_renderer_params.push(stream_renderer_param {
348                 key: STREAM_RENDERER_PARAM_DEBUG_CALLBACK,
349                 value: gfxstream_debug_callback as usize as u64,
350             });
351         }
352 
353         let features_cstr = gfxstream_features.map(|f| CString::new(f).unwrap());
354         if let Some(features_cstr) = &features_cstr {
355             stream_renderer_params.push(stream_renderer_param {
356                 key: STREAM_RENDERER_PARAM_RENDERER_FEATURES,
357                 value: features_cstr.as_ptr() as u64,
358             });
359         }
360 
361         // TODO(b/315870313): Add safety comment
362         #[allow(clippy::undocumented_unsafe_blocks)]
363         unsafe {
364             ret_to_res(stream_renderer_init(
365                 stream_renderer_params.as_mut_ptr(),
366                 stream_renderer_params.len() as u64,
367             ))?;
368         }
369 
370         Ok(Box::new(Gfxstream { _cookie: cookie }))
371     }
372 
map_info(&self, resource_id: u32) -> RutabagaResult<u32>373     fn map_info(&self, resource_id: u32) -> RutabagaResult<u32> {
374         let mut map_info = 0;
375         // SAFETY:
376         // Safe because `map_info` is a local stack variable owned by us.
377         let ret = unsafe { stream_renderer_resource_map_info(resource_id, &mut map_info) };
378         ret_to_res(ret)?;
379 
380         Ok(map_info | RUTABAGA_MAP_ACCESS_RW)
381     }
382 
vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo>383     fn vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo> {
384         let mut vulkan_info: stream_renderer_vulkan_info = Default::default();
385         // SAFETY:
386         // Safe because `vulkan_info` is a local stack variable owned by us.
387         let ret = unsafe { stream_renderer_vulkan_info(resource_id, &mut vulkan_info) };
388         ret_to_res(ret)?;
389 
390         Ok(VulkanInfo {
391             memory_idx: vulkan_info.memory_index,
392             device_id: DeviceId {
393                 device_uuid: vulkan_info.device_uuid,
394                 driver_uuid: vulkan_info.driver_uuid,
395             },
396         })
397     }
398 
export_blob(&self, resource_id: u32) -> RutabagaResult<Arc<RutabagaHandle>>399     fn export_blob(&self, resource_id: u32) -> RutabagaResult<Arc<RutabagaHandle>> {
400         let mut stream_handle: stream_renderer_handle = Default::default();
401         // TODO(b/315870313): Add safety comment
402         #[allow(clippy::undocumented_unsafe_blocks)]
403         let ret = unsafe { stream_renderer_export_blob(resource_id, &mut stream_handle) };
404         ret_to_res(ret)?;
405 
406         let raw_descriptor = stream_handle.os_handle as RawDescriptor;
407         // SAFETY:
408         // Safe because the handle was just returned by a successful gfxstream call so it must be
409         // valid and owned by us.
410         let handle = unsafe { SafeDescriptor::from_raw_descriptor(raw_descriptor) };
411 
412         Ok(Arc::new(RutabagaHandle {
413             os_handle: handle,
414             handle_type: stream_handle.handle_type,
415         }))
416     }
417 }
418 
419 impl Drop for Gfxstream {
drop(&mut self)420     fn drop(&mut self) {
421         // SAFETY: Safe because Gfxstream was successfully initialized.
422         unsafe {
423             stream_renderer_teardown();
424         }
425     }
426 }
427 
428 impl RutabagaComponent for Gfxstream {
get_capset_info(&self, capset_id: u32) -> (u32, u32)429     fn get_capset_info(&self, capset_id: u32) -> (u32, u32) {
430         let mut version = 0;
431         let mut size = 0;
432         // SAFETY:
433         // Safe because gfxstream is initialized by now and properly size stack variables are
434         // used for the pointers.
435         unsafe {
436             stream_renderer_get_cap_set(capset_id, &mut version, &mut size);
437         }
438         (version, size)
439     }
440 
get_capset(&self, capset_id: u32, version: u32) -> Vec<u8>441     fn get_capset(&self, capset_id: u32, version: u32) -> Vec<u8> {
442         let (_, max_size) = self.get_capset_info(capset_id);
443         let mut buf = vec![0u8; max_size as usize];
444         // SAFETY:
445         // Safe because gfxstream is initialized by now and the given buffer is sized properly
446         // for the given cap id/version.
447         unsafe {
448             stream_renderer_fill_caps(capset_id, version, buf.as_mut_ptr() as *mut c_void);
449         }
450 
451         buf
452     }
453 
create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()>454     fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
455         // SAFETY:
456         // Safe because RutabagaFences and stream_renderer_fence are ABI identical
457         let ret = unsafe { stream_renderer_create_fence(&fence as *const stream_renderer_fence) };
458         ret_to_res(ret)
459     }
460 
create_3d( &self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<RutabagaResource>461     fn create_3d(
462         &self,
463         resource_id: u32,
464         resource_create_3d: ResourceCreate3D,
465     ) -> RutabagaResult<RutabagaResource> {
466         let mut args = virgl_renderer_resource_create_args {
467             handle: resource_id,
468             target: resource_create_3d.target,
469             format: resource_create_3d.format,
470             bind: resource_create_3d.bind,
471             width: resource_create_3d.width,
472             height: resource_create_3d.height,
473             depth: resource_create_3d.depth,
474             array_size: resource_create_3d.array_size,
475             last_level: resource_create_3d.last_level,
476             nr_samples: resource_create_3d.nr_samples,
477             flags: resource_create_3d.flags,
478         };
479 
480         // SAFETY:
481         // Safe because gfxstream is initialized by now, and the return value is checked before
482         // returning a new resource. The backing buffers are not supplied with this call.
483         let ret = unsafe { stream_renderer_resource_create(&mut args, null_mut(), 0) };
484         ret_to_res(ret)?;
485 
486         Ok(RutabagaResource {
487             resource_id,
488             handle: None,
489             blob: false,
490             blob_mem: 0,
491             blob_flags: 0,
492             map_info: None,
493             info_2d: None,
494             info_3d: None,
495             vulkan_info: None,
496             backing_iovecs: None,
497             component_mask: 1 << (RutabagaComponentType::Gfxstream as u8),
498             size: 0,
499             mapping: None,
500         })
501     }
502 
attach_backing( &self, resource_id: u32, vecs: &mut Vec<RutabagaIovec>, ) -> RutabagaResult<()>503     fn attach_backing(
504         &self,
505         resource_id: u32,
506         vecs: &mut Vec<RutabagaIovec>,
507     ) -> RutabagaResult<()> {
508         // TODO(b/315870313): Add safety comment
509         #[allow(clippy::undocumented_unsafe_blocks)]
510         let ret = unsafe {
511             stream_renderer_resource_attach_iov(
512                 resource_id as i32,
513                 vecs.as_mut_ptr() as *mut iovec,
514                 vecs.len() as i32,
515             )
516         };
517         ret_to_res(ret)
518     }
519 
detach_backing(&self, resource_id: u32)520     fn detach_backing(&self, resource_id: u32) {
521         // TODO(b/315870313): Add safety comment
522         #[allow(clippy::undocumented_unsafe_blocks)]
523         unsafe {
524             stream_renderer_resource_detach_iov(
525                 resource_id as i32,
526                 std::ptr::null_mut(),
527                 std::ptr::null_mut(),
528             );
529         }
530     }
531 
unref_resource(&self, resource_id: u32)532     fn unref_resource(&self, resource_id: u32) {
533         // SAFETY:
534         // The resource is safe to unreference destroy because no user of these bindings can still
535         // be holding a reference.
536         unsafe {
537             stream_renderer_resource_unref(resource_id);
538         }
539     }
540 
transfer_write( &self, ctx_id: u32, resource: &mut RutabagaResource, transfer: Transfer3D, ) -> RutabagaResult<()>541     fn transfer_write(
542         &self,
543         ctx_id: u32,
544         resource: &mut RutabagaResource,
545         transfer: Transfer3D,
546     ) -> RutabagaResult<()> {
547         if transfer.is_empty() {
548             return Ok(());
549         }
550 
551         let mut transfer_box = VirglBox {
552             x: transfer.x,
553             y: transfer.y,
554             z: transfer.z,
555             w: transfer.w,
556             h: transfer.h,
557             d: transfer.d,
558         };
559 
560         // SAFETY:
561         // Safe because only stack variables of the appropriate type are used.
562         let ret = unsafe {
563             stream_renderer_transfer_write_iov(
564                 resource.resource_id,
565                 ctx_id,
566                 transfer.level as i32,
567                 transfer.stride,
568                 transfer.layer_stride,
569                 &mut transfer_box as *mut VirglBox as *mut stream_renderer_box,
570                 transfer.offset,
571                 null_mut(),
572                 0,
573             )
574         };
575         ret_to_res(ret)
576     }
577 
transfer_read( &self, ctx_id: u32, resource: &mut RutabagaResource, transfer: Transfer3D, buf: Option<IoSliceMut>, ) -> RutabagaResult<()>578     fn transfer_read(
579         &self,
580         ctx_id: u32,
581         resource: &mut RutabagaResource,
582         transfer: Transfer3D,
583         buf: Option<IoSliceMut>,
584     ) -> RutabagaResult<()> {
585         if transfer.is_empty() {
586             return Ok(());
587         }
588 
589         let mut transfer_box = VirglBox {
590             x: transfer.x,
591             y: transfer.y,
592             z: transfer.z,
593             w: transfer.w,
594             h: transfer.h,
595             d: transfer.d,
596         };
597 
598         let mut iov = RutabagaIovec {
599             base: null_mut(),
600             len: 0,
601         };
602 
603         let (iovecs, num_iovecs) = match buf {
604             Some(mut buf) => {
605                 iov.base = buf.as_mut_ptr() as *mut c_void;
606                 iov.len = buf.len();
607                 (&mut iov as *mut RutabagaIovec as *mut iovec, 1)
608             }
609             None => (null_mut(), 0),
610         };
611 
612         // SAFETY:
613         // Safe because only stack variables of the appropriate type are used.
614         let ret = unsafe {
615             stream_renderer_transfer_read_iov(
616                 resource.resource_id,
617                 ctx_id,
618                 transfer.level,
619                 transfer.stride,
620                 transfer.layer_stride,
621                 &mut transfer_box as *mut VirglBox as *mut stream_renderer_box,
622                 transfer.offset,
623                 iovecs,
624                 num_iovecs,
625             )
626         };
627         ret_to_res(ret)
628     }
629 
resource_flush(&self, resource: &mut RutabagaResource) -> RutabagaResult<()>630     fn resource_flush(&self, resource: &mut RutabagaResource) -> RutabagaResult<()> {
631         // TODO(b/315870313): Add safety comment
632         #[allow(clippy::undocumented_unsafe_blocks)]
633         unsafe {
634             stream_renderer_flush(resource.resource_id);
635         }
636         Ok(())
637     }
638 
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>639     fn create_blob(
640         &mut self,
641         ctx_id: u32,
642         resource_id: u32,
643         resource_create_blob: ResourceCreateBlob,
644         mut iovec_opt: Option<Vec<RutabagaIovec>>,
645         handle_opt: Option<RutabagaHandle>,
646     ) -> RutabagaResult<RutabagaResource> {
647         let mut iovec_ptr = null_mut();
648         let mut num_iovecs = 0;
649         if let Some(ref mut iovecs) = iovec_opt {
650             iovec_ptr = iovecs.as_mut_ptr();
651             num_iovecs = iovecs.len() as u32;
652         }
653 
654         let mut handle_ptr = null();
655         let mut stream_handle: stream_renderer_handle = Default::default();
656         if let Some(handle) = handle_opt {
657             stream_handle.handle_type = handle.handle_type;
658             stream_handle.os_handle = handle.os_handle.into_raw_descriptor() as i64;
659             handle_ptr = &stream_handle;
660         }
661 
662         // TODO(b/315870313): Add safety comment
663         #[allow(clippy::undocumented_unsafe_blocks)]
664         let ret = unsafe {
665             stream_renderer_create_blob(
666                 ctx_id,
667                 resource_id,
668                 &resource_create_blob as *const stream_renderer_create_blob,
669                 iovec_ptr as *const iovec,
670                 num_iovecs,
671                 handle_ptr,
672             )
673         };
674 
675         ret_to_res(ret)?;
676 
677         Ok(RutabagaResource {
678             resource_id,
679             handle: self.export_blob(resource_id).ok(),
680             blob: true,
681             blob_mem: resource_create_blob.blob_mem,
682             blob_flags: resource_create_blob.blob_flags,
683             map_info: self.map_info(resource_id).ok(),
684             info_2d: None,
685             info_3d: None,
686             vulkan_info: self.vulkan_info(resource_id).ok(),
687             backing_iovecs: iovec_opt,
688             component_mask: 1 << (RutabagaComponentType::Gfxstream as u8),
689             size: resource_create_blob.size,
690             mapping: None,
691         })
692     }
693 
map(&self, resource_id: u32) -> RutabagaResult<RutabagaMapping>694     fn map(&self, resource_id: u32) -> RutabagaResult<RutabagaMapping> {
695         let mut map: *mut c_void = null_mut();
696         let mut size: u64 = 0;
697 
698         // SAFETY:
699         // Safe because the Stream renderer wraps and validates use of vkMapMemory.
700         let ret = unsafe { stream_renderer_resource_map(resource_id, &mut map, &mut size) };
701         if ret != 0 {
702             return Err(RutabagaError::MappingFailed(ret));
703         }
704         Ok(RutabagaMapping {
705             ptr: map as u64,
706             size,
707         })
708     }
709 
unmap(&self, resource_id: u32) -> RutabagaResult<()>710     fn unmap(&self, resource_id: u32) -> RutabagaResult<()> {
711         // SAFETY:
712         // Safe because the Stream renderer wraps and validates use of vkMapMemory.
713         let ret = unsafe { stream_renderer_resource_unmap(resource_id) };
714         ret_to_res(ret)
715     }
716 
create_context( &self, ctx_id: u32, context_init: u32, context_name: Option<&str>, fence_handler: RutabagaFenceHandler, ) -> RutabagaResult<Box<dyn RutabagaContext>>717     fn create_context(
718         &self,
719         ctx_id: u32,
720         context_init: u32,
721         context_name: Option<&str>,
722         fence_handler: RutabagaFenceHandler,
723     ) -> RutabagaResult<Box<dyn RutabagaContext>> {
724         let mut name: &str = "gpu_renderer";
725         if let Some(name_string) = context_name.filter(|s| !s.is_empty()) {
726             name = name_string;
727         }
728 
729         // SAFETY:
730         // Safe because gfxstream is initialized by now and the context name is statically
731         // allocated. The return value is checked before returning a new context.
732         let ret = unsafe {
733             stream_renderer_context_create(
734                 ctx_id,
735                 name.len() as u32,
736                 name.as_ptr() as *const c_char,
737                 context_init,
738             )
739         };
740         ret_to_res(ret)?;
741         Ok(Box::new(GfxstreamContext {
742             ctx_id,
743             fence_handler,
744         }))
745     }
746 
747     #[cfg(gfxstream_unstable)]
snapshot(&self, directory: &str) -> RutabagaResult<()>748     fn snapshot(&self, directory: &str) -> RutabagaResult<()> {
749         let cstring = CString::new(directory)?;
750 
751         // SAFETY:
752         // Safe because directory string is valid
753         let ret = unsafe { stream_renderer_snapshot(cstring.as_ptr() as *const c_char) };
754         ret_to_res(ret)?;
755 
756         Ok(())
757     }
758 
759     #[cfg(gfxstream_unstable)]
restore(&self, directory: &str) -> RutabagaResult<()>760     fn restore(&self, directory: &str) -> RutabagaResult<()> {
761         let cstring = CString::new(directory)?;
762 
763         // SAFETY:
764         // Safe because directory string is valid
765         let ret = unsafe { stream_renderer_restore(cstring.as_ptr() as *const c_char) };
766         ret_to_res(ret)?;
767         Ok(())
768     }
769 }
770