1#![cfg(feature = "gfxstream")]
10
11use std::convert::TryInto;
12use std::io::IoSliceMut;
13use std::mem::size_of;
14use std::os::raw::c_char;
15use std::os::raw::c_int;
16use std::os::raw::c_uint;
17use std::os::raw::c_void;
18use std::panic::catch_unwind;
19use std::process::abort;
20use std::ptr::null;
21use std::ptr::null_mut;
22use std::sync::Arc;
23
24use crate::generated::virgl_renderer_bindings::iovec;
25use crate::generated::virgl_renderer_bindings::virgl_box;
26use crate::generated::virgl_renderer_bindings::virgl_renderer_resource_create_args;
27use crate::renderer_utils::*;
28use crate::rutabaga_core::RutabagaComponent;
29use crate::rutabaga_core::RutabagaContext;
30use crate::rutabaga_core::RutabagaResource;
31use crate::rutabaga_os::FromRawDescriptor;
32use crate::rutabaga_os::IntoRawDescriptor;
33use crate::rutabaga_os::RawDescriptor;
34use crate::rutabaga_os::SafeDescriptor;
35use crate::rutabaga_utils::*;
36
37const STREAM_RENDERER_PARAM_NULL: u64 = 0;
39const STREAM_RENDERER_PARAM_USER_DATA: u64 = 1;
40const STREAM_RENDERER_PARAM_RENDERER_FLAGS: u64 = 2;
41const STREAM_RENDERER_PARAM_FENCE_CALLBACK: u64 = 3;
42const STREAM_RENDERER_PARAM_WIN0_WIDTH: u64 = 4;
43const STREAM_RENDERER_PARAM_WIN0_HEIGHT: u64 = 5;
44const STREAM_RENDERER_PARAM_DEBUG_CALLBACK: u64 = 6;
45
46const STREAM_RENDERER_MAX_PARAMS: usize = 6;
47
48#[repr(C)]
49#[derive(Clone, Copy, Debug)]
50pub struct stream_renderer_param {
51 key: u64,
52 value: u64,
53}
54
55#[repr(C)]
56#[derive(Copy, Clone, Default)]
57pub struct stream_renderer_handle {
58 pub os_handle: i64,
59 pub handle_type: u32,
60}
61
62#[repr(C)]
63#[derive(Copy, Clone, Default)]
64pub struct stream_renderer_vulkan_info {
65 pub memory_index: u32,
66 pub device_uuid: [u8; 16],
67 pub driver_uuid: [u8; 16],
68}
69
70#[repr(C)]
71pub struct stream_renderer_command {
72 pub ctx_id: u32,
73 pub cmd_size: u32,
74 pub cmd: *const u8,
75 pub num_in_fences: u32,
76 pub in_fence_descriptors: *const u64,
77}
78
79#[allow(non_camel_case_types)]
80pub type stream_renderer_create_blob = ResourceCreateBlob;
81
82#[allow(non_camel_case_types)]
83pub type stream_renderer_resource_create_args = virgl_renderer_resource_create_args;
84
85#[allow(non_camel_case_types)]
86pub type stream_renderer_box = virgl_box;
87
88#[allow(non_camel_case_types)]
89pub type stream_renderer_fence = RutabagaFence;
90
91#[allow(non_camel_case_types)]
92pub type stream_renderer_debug = RutabagaDebug;
93
94extern "C" {
95 fn stream_renderer_init(
97 stream_renderer_params: *mut stream_renderer_param,
98 num_params: u64,
99 ) -> c_int;
100
101 fn stream_renderer_teardown();
103
104 fn stream_renderer_resource_create(
111 args: *mut stream_renderer_resource_create_args,
112 iov: *mut iovec,
113 num_iovs: u32,
114 ) -> c_int;
115
116 fn stream_renderer_resource_unref(res_handle: u32);
117 fn stream_renderer_context_destroy(handle: u32);
118 fn stream_renderer_transfer_read_iov(
119 handle: u32,
120 ctx_id: u32,
121 level: u32,
122 stride: u32,
123 layer_stride: u32,
124 box_: *mut stream_renderer_box,
125 offset: u64,
126 iov: *mut iovec,
127 iovec_cnt: c_int,
128 ) -> c_int;
129 fn stream_renderer_transfer_write_iov(
130 handle: u32,
131 ctx_id: u32,
132 level: c_int,
133 stride: u32,
134 layer_stride: u32,
135 box_: *mut stream_renderer_box,
136 offset: u64,
137 iovec: *mut iovec,
138 iovec_cnt: c_uint,
139 ) -> c_int;
140 fn stream_renderer_submit_cmd(cmd: *const stream_renderer_command) -> c_int;
141 fn stream_renderer_resource_attach_iov(
142 res_handle: c_int,
143 iov: *mut iovec,
144 num_iovs: c_int,
145 ) -> c_int;
146 fn stream_renderer_resource_detach_iov(
147 res_handle: c_int,
148 iov: *mut *mut iovec,
149 num_iovs: *mut c_int,
150 );
151 fn stream_renderer_create_fence(fence: *const stream_renderer_fence) -> c_int;
152 fn stream_renderer_ctx_attach_resource(ctx_id: c_int, res_handle: c_int);
153 fn stream_renderer_ctx_detach_resource(ctx_id: c_int, res_handle: c_int);
154 fn stream_renderer_get_cap_set(set: u32, max_ver: *mut u32, max_size: *mut u32);
155 fn stream_renderer_fill_caps(set: u32, version: u32, caps: *mut c_void);
156
157 fn stream_renderer_flush(res_handle: u32);
158 fn stream_renderer_create_blob(
159 ctx_id: u32,
160 res_handle: u32,
161 create_blob: *const stream_renderer_create_blob,
162 iovecs: *const iovec,
163 num_iovs: u32,
164 handle: *const stream_renderer_handle,
165 ) -> c_int;
166
167 fn stream_renderer_export_blob(res_handle: u32, handle: *mut stream_renderer_handle) -> c_int;
168 fn stream_renderer_resource_map(
169 res_handle: u32,
170 map: *mut *mut c_void,
171 out_size: *mut u64,
172 ) -> c_int;
173 fn stream_renderer_resource_unmap(res_handle: u32) -> c_int;
174 fn stream_renderer_resource_map_info(res_handle: u32, map_info: *mut u32) -> c_int;
175 fn stream_renderer_vulkan_info(
176 res_handle: u32,
177 vulkan_info: *mut stream_renderer_vulkan_info,
178 ) -> c_int;
179 fn stream_renderer_context_create(
180 handle: u32,
181 nlen: u32,
182 name: *const c_char,
183 context_init: u32,
184 ) -> c_int;
185}
186
187pub struct Gfxstream {
189 _cookie: Box<RutabagaCookie>,
191}
192
193struct GfxstreamContext {
194 ctx_id: u32,
195 fence_handler: RutabagaFenceHandler,
196}
197
198impl RutabagaContext for GfxstreamContext {
199 fn submit_cmd(&mut self, commands: &mut [u8], fence_ids: &[u64]) -> RutabagaResult<()> {
200 if !fence_ids.is_empty() {
201 return Err(RutabagaError::Unsupported);
202 }
203
204 if commands.len() % size_of::<u32>() != 0 {
205 return Err(RutabagaError::InvalidCommandSize(commands.len()));
206 }
207
208 #[allow(clippy::undocumented_unsafe_blocks)]
210 let ret = unsafe {
211 let cmd = stream_renderer_command {
212 ctx_id: self.ctx_id,
213 cmd_size: commands.len().try_into()?,
214 cmd: commands.as_mut_ptr(),
215 num_in_fences: 0,
216 in_fence_descriptors: null(),
217 };
218
219 stream_renderer_submit_cmd(&cmd as *const stream_renderer_command)
220 };
221 ret_to_res(ret)
222 }
223
224 fn attach(&mut self, resource: &mut RutabagaResource) {
225 unsafe {
229 stream_renderer_ctx_attach_resource(self.ctx_id as i32, resource.resource_id as i32);
230 }
231 }
232
233 fn detach(&mut self, resource: &RutabagaResource) {
234 unsafe {
238 stream_renderer_ctx_detach_resource(self.ctx_id as i32, resource.resource_id as i32);
239 }
240 }
241
242 fn component_type(&self) -> RutabagaComponentType {
243 RutabagaComponentType::Gfxstream
244 }
245
246 fn context_create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
247 if fence.ring_idx as u32 == 1 {
248 self.fence_handler.call(fence);
249 return Ok(());
250 }
251
252 let ret = unsafe { stream_renderer_create_fence(&fence as *const stream_renderer_fence) };
255
256 ret_to_res(ret)
257 }
258}
259
260impl Drop for GfxstreamContext {
261 fn drop(&mut self) {
262 unsafe {
265 stream_renderer_context_destroy(self.ctx_id);
266 }
267 }
268}
269
270extern "C" fn write_context_fence(cookie: *mut c_void, fence: *const RutabagaFence) {
271 catch_unwind(|| {
272 assert!(!cookie.is_null());
273 let cookie = unsafe { &*(cookie as *mut RutabagaCookie) };
276 if let Some(handler) = &cookie.fence_handler {
277 unsafe { handler.call(*fence) };
280 }
281 })
282 .unwrap_or_else(|_| abort())
283}
284
285extern "C" fn gfxstream_debug_callback(cookie: *mut c_void, debug: *const stream_renderer_debug) {
286 catch_unwind(|| {
287 assert!(!cookie.is_null());
288 let cookie = unsafe { &*(cookie as *mut RutabagaCookie) };
291 if let Some(handler) = &cookie.debug_handler {
292 unsafe { handler.call(*debug) };
295 }
296 })
297 .unwrap_or_else(|_| abort())
298}
299
300impl Gfxstream {
301 pub fn init(
302 display_width: u32,
303 display_height: u32,
304 gfxstream_flags: GfxstreamFlags,
305 fence_handler: RutabagaFenceHandler,
306 debug_handler: Option<RutabagaDebugHandler>,
307 ) -> RutabagaResult<Box<dyn RutabagaComponent>> {
308 let use_debug = debug_handler.is_some();
309 let mut cookie = Box::new(RutabagaCookie {
310 render_server_fd: None,
311 fence_handler: Some(fence_handler),
312 debug_handler,
313 });
314
315 let mut stream_renderer_params: [stream_renderer_param; STREAM_RENDERER_MAX_PARAMS] = [
316 stream_renderer_param {
317 key: STREAM_RENDERER_PARAM_USER_DATA,
318 value: &mut *cookie as *mut RutabagaCookie as u64,
321 },
322 stream_renderer_param {
323 key: STREAM_RENDERER_PARAM_RENDERER_FLAGS,
324 value: gfxstream_flags.into(),
325 },
326 stream_renderer_param {
327 key: STREAM_RENDERER_PARAM_FENCE_CALLBACK,
328 value: write_context_fence as usize as u64,
329 },
330 stream_renderer_param {
331 key: STREAM_RENDERER_PARAM_WIN0_WIDTH,
332 value: display_width as u64,
333 },
334 stream_renderer_param {
335 key: STREAM_RENDERER_PARAM_WIN0_HEIGHT,
336 value: display_height as u64,
337 },
338 if use_debug {
339 stream_renderer_param {
340 key: STREAM_RENDERER_PARAM_DEBUG_CALLBACK,
341 value: gfxstream_debug_callback as usize as u64,
342 }
343 } else {
344 stream_renderer_param {
345 key: STREAM_RENDERER_PARAM_NULL,
346 value: 0,
347 }
348 },
349 ];
350
351 #[allow(clippy::undocumented_unsafe_blocks)]
353 unsafe {
354 ret_to_res(stream_renderer_init(
355 stream_renderer_params.as_mut_ptr(),
356 stream_renderer_params.len() as u64,
357 ))?;
358 }
359
360 Ok(Box::new(Gfxstream { _cookie: cookie }))
361 }
362
363 fn map_info(&self, resource_id: u32) -> RutabagaResult<u32> {
364 let mut map_info = 0;
365 let ret = unsafe { stream_renderer_resource_map_info(resource_id, &mut map_info) };
368 ret_to_res(ret)?;
369
370 Ok(map_info | RUTABAGA_MAP_ACCESS_RW)
371 }
372
373 fn vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo> {
374 let mut vulkan_info: stream_renderer_vulkan_info = Default::default();
375 let ret = unsafe { stream_renderer_vulkan_info(resource_id, &mut vulkan_info) };
378 ret_to_res(ret)?;
379
380 Ok(VulkanInfo {
381 memory_idx: vulkan_info.memory_index,
382 device_id: DeviceId {
383 device_uuid: vulkan_info.device_uuid,
384 driver_uuid: vulkan_info.driver_uuid,
385 },
386 })
387 }
388
389 fn export_blob(&self, resource_id: u32) -> RutabagaResult<Arc<RutabagaHandle>> {
390 let mut stream_handle: stream_renderer_handle = Default::default();
391 #[allow(clippy::undocumented_unsafe_blocks)]
393 let ret = unsafe { stream_renderer_export_blob(resource_id, &mut stream_handle) };
394 ret_to_res(ret)?;
395
396 let raw_descriptor = stream_handle.os_handle as RawDescriptor;
397 let handle = unsafe { SafeDescriptor::from_raw_descriptor(raw_descriptor) };
401
402 Ok(Arc::new(RutabagaHandle {
403 os_handle: handle,
404 handle_type: stream_handle.handle_type,
405 }))
406 }
407}
408
409impl Drop for Gfxstream {
410 fn drop(&mut self) {
411 unsafe {
413 stream_renderer_teardown();
414 }
415 }
416}
417
418impl RutabagaComponent for Gfxstream {
419 fn get_capset_info(&self, capset_id: u32) -> (u32, u32) {
420 let mut version = 0;
421 let mut size = 0;
422 unsafe {
426 stream_renderer_get_cap_set(capset_id, &mut version, &mut size);
427 }
428 (version, size)
429 }
430
431 fn get_capset(&self, capset_id: u32, version: u32) -> Vec<u8> {
432 let (_, max_size) = self.get_capset_info(capset_id);
433 let mut buf = vec![0u8; max_size as usize];
434 unsafe {
438 stream_renderer_fill_caps(capset_id, version, buf.as_mut_ptr() as *mut c_void);
439 }
440
441 buf
442 }
443
444 fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
445 let ret = unsafe { stream_renderer_create_fence(&fence as *const stream_renderer_fence) };
448 ret_to_res(ret)
449 }
450
451 fn create_3d(
452 &self,
453 resource_id: u32,
454 resource_create_3d: ResourceCreate3D,
455 ) -> RutabagaResult<RutabagaResource> {
456 let mut args = virgl_renderer_resource_create_args {
457 handle: resource_id,
458 target: resource_create_3d.target,
459 format: resource_create_3d.format,
460 bind: resource_create_3d.bind,
461 width: resource_create_3d.width,
462 height: resource_create_3d.height,
463 depth: resource_create_3d.depth,
464 array_size: resource_create_3d.array_size,
465 last_level: resource_create_3d.last_level,
466 nr_samples: resource_create_3d.nr_samples,
467 flags: resource_create_3d.flags,
468 };
469
470 let ret = unsafe { stream_renderer_resource_create(&mut args, null_mut(), 0) };
474 ret_to_res(ret)?;
475
476 Ok(RutabagaResource {
477 resource_id,
478 handle: None,
479 blob: false,
480 blob_mem: 0,
481 blob_flags: 0,
482 map_info: None,
483 info_2d: None,
484 info_3d: None,
485 vulkan_info: None,
486 backing_iovecs: None,
487 component_mask: 1 << (RutabagaComponentType::Gfxstream as u8),
488 size: 0,
489 mapping: None,
490 })
491 }
492
493 fn attach_backing(
494 &self,
495 resource_id: u32,
496 vecs: &mut Vec<RutabagaIovec>,
497 ) -> RutabagaResult<()> {
498 #[allow(clippy::undocumented_unsafe_blocks)]
500 let ret = unsafe {
501 stream_renderer_resource_attach_iov(
502 resource_id as i32,
503 vecs.as_mut_ptr() as *mut iovec,
504 vecs.len() as i32,
505 )
506 };
507 ret_to_res(ret)
508 }
509
510 fn detach_backing(&self, resource_id: u32) {
511 #[allow(clippy::undocumented_unsafe_blocks)]
513 unsafe {
514 stream_renderer_resource_detach_iov(
515 resource_id as i32,
516 std::ptr::null_mut(),
517 std::ptr::null_mut(),
518 );
519 }
520 }
521
522 fn unref_resource(&self, resource_id: u32) {
523 unsafe {
527 stream_renderer_resource_unref(resource_id);
528 }
529 }
530
531 fn transfer_write(
532 &self,
533 ctx_id: u32,
534 resource: &mut RutabagaResource,
535 transfer: Transfer3D,
536 ) -> RutabagaResult<()> {
537 if transfer.is_empty() {
538 return Ok(());
539 }
540
541 let mut transfer_box = VirglBox {
542 x: transfer.x,
543 y: transfer.y,
544 z: transfer.z,
545 w: transfer.w,
546 h: transfer.h,
547 d: transfer.d,
548 };
549
550 let ret = unsafe {
553 stream_renderer_transfer_write_iov(
554 resource.resource_id,
555 ctx_id,
556 transfer.level as i32,
557 transfer.stride,
558 transfer.layer_stride,
559 &mut transfer_box as *mut VirglBox as *mut stream_renderer_box,
560 transfer.offset,
561 null_mut(),
562 0,
563 )
564 };
565 ret_to_res(ret)
566 }
567
568 fn transfer_read(
569 &self,
570 ctx_id: u32,
571 resource: &mut RutabagaResource,
572 transfer: Transfer3D,
573 buf: Option<IoSliceMut>,
574 ) -> RutabagaResult<()> {
575 if transfer.is_empty() {
576 return Ok(());
577 }
578
579 let mut transfer_box = VirglBox {
580 x: transfer.x,
581 y: transfer.y,
582 z: transfer.z,
583 w: transfer.w,
584 h: transfer.h,
585 d: transfer.d,
586 };
587
588 let mut iov = RutabagaIovec {
589 base: null_mut(),
590 len: 0,
591 };
592
593 let (iovecs, num_iovecs) = match buf {
594 Some(mut buf) => {
595 iov.base = buf.as_mut_ptr() as *mut c_void;
596 iov.len = buf.len();
597 (&mut iov as *mut RutabagaIovec as *mut iovec, 1)
598 }
599 None => (null_mut(), 0),
600 };
601
602 let ret = unsafe {
605 stream_renderer_transfer_read_iov(
606 resource.resource_id,
607 ctx_id,
608 transfer.level,
609 transfer.stride,
610 transfer.layer_stride,
611 &mut transfer_box as *mut VirglBox as *mut stream_renderer_box,
612 transfer.offset,
613 iovecs,
614 num_iovecs,
615 )
616 };
617 ret_to_res(ret)
618 }
619
620 fn resource_flush(&self, resource: &mut RutabagaResource) -> RutabagaResult<()> {
621 #[allow(clippy::undocumented_unsafe_blocks)]
623 unsafe {
624 stream_renderer_flush(resource.resource_id);
625 }
626 Ok(())
627 }
628
629 fn create_blob(
630 &mut self,
631 ctx_id: u32,
632 resource_id: u32,
633 resource_create_blob: ResourceCreateBlob,
634 mut iovec_opt: Option<Vec<RutabagaIovec>>,
635 handle_opt: Option<RutabagaHandle>,
636 ) -> RutabagaResult<RutabagaResource> {
637 let mut iovec_ptr = null_mut();
638 let mut num_iovecs = 0;
639 if let Some(ref mut iovecs) = iovec_opt {
640 iovec_ptr = iovecs.as_mut_ptr();
641 num_iovecs = iovecs.len() as u32;
642 }
643
644 let mut handle_ptr = null();
645 let mut stream_handle: stream_renderer_handle = Default::default();
646 if let Some(handle) = handle_opt {
647 stream_handle.handle_type = handle.handle_type;
648 stream_handle.os_handle = handle.os_handle.into_raw_descriptor() as i64;
649 handle_ptr = &stream_handle;
650 }
651
652 #[allow(clippy::undocumented_unsafe_blocks)]
654 let ret = unsafe {
655 stream_renderer_create_blob(
656 ctx_id,
657 resource_id,
658 &resource_create_blob as *const stream_renderer_create_blob,
659 iovec_ptr as *const iovec,
660 num_iovecs,
661 handle_ptr,
662 )
663 };
664
665 ret_to_res(ret)?;
666
667 Ok(RutabagaResource {
668 resource_id,
669 handle: self.export_blob(resource_id).ok(),
670 blob: true,
671 blob_mem: resource_create_blob.blob_mem,
672 blob_flags: resource_create_blob.blob_flags,
673 map_info: self.map_info(resource_id).ok(),
674 info_2d: None,
675 info_3d: None,
676 vulkan_info: self.vulkan_info(resource_id).ok(),
677 backing_iovecs: iovec_opt,
678 component_mask: 1 << (RutabagaComponentType::Gfxstream as u8),
679 size: resource_create_blob.size,
680 mapping: None,
681 })
682 }
683
684 fn map(&self, resource_id: u32) -> RutabagaResult<RutabagaMapping> {
685 let mut map: *mut c_void = null_mut();
686 let mut size: u64 = 0;
687
688 let ret = unsafe { stream_renderer_resource_map(resource_id, &mut map, &mut size) };
691 if ret != 0 {
692 return Err(RutabagaError::MappingFailed(ret));
693 }
694 Ok(RutabagaMapping {
695 ptr: map as u64,
696 size,
697 })
698 }
699
700 fn unmap(&self, resource_id: u32) -> RutabagaResult<()> {
701 let ret = unsafe { stream_renderer_resource_unmap(resource_id) };
704 ret_to_res(ret)
705 }
706
707 fn create_context(
708 &self,
709 ctx_id: u32,
710 context_init: u32,
711 context_name: Option<&str>,
712 fence_handler: RutabagaFenceHandler,
713 ) -> RutabagaResult<Box<dyn RutabagaContext>> {
714 let mut name: &str = "gpu_renderer";
715 if let Some(name_string) = context_name.filter(|s| !s.is_empty()) {
716 name = name_string;
717 }
718
719 let ret = unsafe {
723 stream_renderer_context_create(
724 ctx_id,
725 name.len() as u32,
726 name.as_ptr() as *const c_char,
727 context_init,
728 )
729 };
730 ret_to_res(ret)?;
731 Ok(Box::new(GfxstreamContext {
732 ctx_id,
733 fence_handler,
734 }))
735 }
736}