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