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 //! virgl_renderer: Handles 3D virtio-gpu hypercalls using virglrenderer.
6 //! External code found at <https://gitlab.freedesktop.org/virgl/virglrenderer/>.
7
8 #![cfg(feature = "virgl_renderer")]
9
10 use std::cmp::min;
11 use std::convert::TryFrom;
12 use std::io::Error as SysError;
13 use std::io::IoSliceMut;
14 use std::mem::size_of;
15 use std::mem::transmute;
16 use std::os::raw::c_char;
17 use std::os::raw::c_int;
18 use std::os::raw::c_void;
19 use std::os::unix::io::AsRawFd;
20 use std::panic::catch_unwind;
21 use std::process::abort;
22 use std::ptr::null_mut;
23 use std::sync::atomic::AtomicBool;
24 use std::sync::atomic::Ordering;
25 use std::sync::Arc;
26
27 use log::debug;
28 use log::error;
29 use log::warn;
30
31 use crate::generated::virgl_debug_callback_bindings::*;
32 use crate::generated::virgl_renderer_bindings::*;
33 use crate::renderer_utils::*;
34 use crate::rutabaga_core::RutabagaComponent;
35 use crate::rutabaga_core::RutabagaContext;
36 use crate::rutabaga_core::RutabagaResource;
37 use crate::rutabaga_os::FromRawDescriptor;
38 use crate::rutabaga_os::IntoRawDescriptor;
39 use crate::rutabaga_os::SafeDescriptor;
40 use crate::rutabaga_utils::*;
41
42 type Query = virgl_renderer_export_query;
43
44 /// The virtio-gpu backend state tracker which supports accelerated rendering.
45 pub struct VirglRenderer {}
46
47 struct VirglRendererContext {
48 ctx_id: u32,
49 }
50
import_resource(resource: &mut RutabagaResource) -> RutabagaResult<()>51 fn import_resource(resource: &mut RutabagaResource) -> RutabagaResult<()> {
52 if (resource.component_mask & (1 << (RutabagaComponentType::VirglRenderer as u8))) != 0 {
53 return Ok(());
54 }
55
56 if let Some(handle) = &resource.handle {
57 if handle.handle_type == RUTABAGA_MEM_HANDLE_TYPE_DMABUF {
58 let dmabuf_fd = handle.os_handle.try_clone()?.into_raw_descriptor();
59 // SAFETY:
60 // Safe because we are being passed a valid fd
61 unsafe {
62 let dmabuf_size = libc::lseek64(dmabuf_fd, 0, libc::SEEK_END);
63 libc::lseek64(dmabuf_fd, 0, libc::SEEK_SET);
64 let args = virgl_renderer_resource_import_blob_args {
65 res_handle: resource.resource_id,
66 blob_mem: resource.blob_mem,
67 fd_type: VIRGL_RENDERER_BLOB_FD_TYPE_DMABUF,
68 fd: dmabuf_fd,
69 size: dmabuf_size as u64,
70 };
71 let ret = virgl_renderer_resource_import_blob(&args);
72 if ret != 0 {
73 // import_blob can fail if we've previously imported this resource,
74 // but in any case virglrenderer does not take ownership of the fd
75 // in error paths
76 //
77 // Because of the re-import case we must still fall through to the
78 // virgl_renderer_ctx_attach_resource() call.
79 libc::close(dmabuf_fd);
80 return Ok(());
81 }
82 resource.component_mask |= 1 << (RutabagaComponentType::VirglRenderer as u8);
83 }
84 }
85 }
86
87 Ok(())
88 }
89
90 impl RutabagaContext for VirglRendererContext {
submit_cmd(&mut self, commands: &mut [u8], fence_ids: &[u64]) -> RutabagaResult<()>91 fn submit_cmd(&mut self, commands: &mut [u8], fence_ids: &[u64]) -> RutabagaResult<()> {
92 #[cfg(not(virgl_renderer_unstable))]
93 if !fence_ids.is_empty() {
94 return Err(RutabagaError::Unsupported);
95 }
96 if commands.len() % size_of::<u32>() != 0 {
97 return Err(RutabagaError::InvalidCommandSize(commands.len()));
98 }
99 let dword_count = (commands.len() / size_of::<u32>()) as i32;
100 #[cfg(not(virgl_renderer_unstable))]
101 // SAFETY:
102 // Safe because the context and buffer are valid and virglrenderer will have been
103 // initialized if there are Context instances.
104 let ret = unsafe {
105 virgl_renderer_submit_cmd(
106 commands.as_mut_ptr() as *mut c_void,
107 self.ctx_id as i32,
108 dword_count,
109 )
110 };
111 #[cfg(virgl_renderer_unstable)]
112 // SAFETY:
113 // Safe because the context and buffers are valid and virglrenderer will have been
114 // initialized if there are Context instances.
115 let ret = unsafe {
116 virgl_renderer_submit_cmd2(
117 commands.as_mut_ptr() as *mut c_void,
118 self.ctx_id as i32,
119 dword_count,
120 fence_ids.as_ptr() as *mut u64,
121 fence_ids.len() as u32,
122 )
123 };
124 ret_to_res(ret)
125 }
126
attach(&mut self, resource: &mut RutabagaResource)127 fn attach(&mut self, resource: &mut RutabagaResource) {
128 match import_resource(resource) {
129 Ok(()) => (),
130 Err(e) => error!("importing resource failing with {}", e),
131 }
132
133 // SAFETY:
134 // The context id and resource id must be valid because the respective instances ensure
135 // their lifetime.
136 unsafe {
137 virgl_renderer_ctx_attach_resource(self.ctx_id as i32, resource.resource_id as i32);
138 }
139 }
140
detach(&mut self, resource: &RutabagaResource)141 fn detach(&mut self, resource: &RutabagaResource) {
142 // SAFETY:
143 // The context id and resource id must be valid because the respective instances ensure
144 // their lifetime.
145 unsafe {
146 virgl_renderer_ctx_detach_resource(self.ctx_id as i32, resource.resource_id as i32);
147 }
148 }
149
component_type(&self) -> RutabagaComponentType150 fn component_type(&self) -> RutabagaComponentType {
151 RutabagaComponentType::VirglRenderer
152 }
153
context_create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()>154 fn context_create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
155 // RutabagaFence::flags are not compatible with virglrenderer's fencing API and currently
156 // virglrenderer context's assume all fences on a single timeline are MERGEABLE, and enforce
157 // this assumption.
158 let flags: u32 = VIRGL_RENDERER_FENCE_FLAG_MERGEABLE;
159
160 // TODO(b/315870313): Add safety comment
161 #[allow(clippy::undocumented_unsafe_blocks)]
162 let ret = unsafe {
163 virgl_renderer_context_create_fence(
164 fence.ctx_id,
165 flags,
166 fence.ring_idx as u32,
167 fence.fence_id,
168 )
169 };
170 ret_to_res(ret)
171 }
172 }
173
174 impl Drop for VirglRendererContext {
drop(&mut self)175 fn drop(&mut self) {
176 // SAFETY:
177 // The context is safe to destroy because nothing else can be referencing it.
178 unsafe {
179 virgl_renderer_context_destroy(self.ctx_id);
180 }
181 }
182 }
183
debug_callback(fmt: *const ::std::os::raw::c_char, ap: stdio::va_list)184 extern "C" fn debug_callback(fmt: *const ::std::os::raw::c_char, ap: stdio::va_list) {
185 const BUF_LEN: usize = 256;
186 let mut v = [b' '; BUF_LEN];
187
188 // TODO(b/315870313): Add safety comment
189 #[allow(clippy::undocumented_unsafe_blocks)]
190 let printed_len = unsafe {
191 let ptr = v.as_mut_ptr() as *mut ::std::os::raw::c_char;
192 #[cfg(any(
193 target_arch = "x86",
194 target_arch = "x86_64",
195 target_arch = "aarch64",
196 target_arch = "riscv64"
197 ))]
198 let size = BUF_LEN as ::std::os::raw::c_ulong;
199 #[cfg(target_arch = "arm")]
200 let size = BUF_LEN as ::std::os::raw::c_uint;
201
202 stdio::vsnprintf(ptr, size, fmt, ap)
203 };
204
205 if printed_len < 0 {
206 debug!(
207 "rutabaga_gfx::virgl_renderer::debug_callback: vsnprintf returned {}",
208 printed_len
209 );
210 } else {
211 // vsnprintf returns the number of chars that *would* have been printed
212 let len = min(printed_len as usize, BUF_LEN - 1);
213 debug!("{}", String::from_utf8_lossy(&v[..len]));
214 }
215 }
216
write_context_fence(cookie: *mut c_void, ctx_id: u32, ring_idx: u32, fence_id: u64)217 extern "C" fn write_context_fence(cookie: *mut c_void, ctx_id: u32, ring_idx: u32, fence_id: u64) {
218 catch_unwind(|| {
219 assert!(!cookie.is_null());
220 // TODO(b/315870313): Add safety comment
221 #[allow(clippy::undocumented_unsafe_blocks)]
222 let cookie = unsafe { &*(cookie as *mut RutabagaCookie) };
223
224 // Call fence completion callback
225 if let Some(handler) = &cookie.fence_handler {
226 handler.call(RutabagaFence {
227 flags: RUTABAGA_FLAG_FENCE | RUTABAGA_FLAG_INFO_RING_IDX,
228 fence_id,
229 ctx_id,
230 ring_idx: ring_idx as u8,
231 });
232 }
233 })
234 .unwrap_or_else(|_| abort())
235 }
236
237 // TODO(b/315870313): Add safety comment
238 #[allow(clippy::undocumented_unsafe_blocks)]
write_fence(cookie: *mut c_void, fence: u32)239 unsafe extern "C" fn write_fence(cookie: *mut c_void, fence: u32) {
240 catch_unwind(|| {
241 assert!(!cookie.is_null());
242 let cookie = &*(cookie as *mut RutabagaCookie);
243
244 // Call fence completion callback
245 if let Some(handler) = &cookie.fence_handler {
246 handler.call(RutabagaFence {
247 flags: RUTABAGA_FLAG_FENCE,
248 fence_id: fence as u64,
249 ctx_id: 0,
250 ring_idx: 0,
251 });
252 }
253 })
254 .unwrap_or_else(|_| abort())
255 }
256
257 // TODO(b/315870313): Add safety comment
258 #[allow(clippy::undocumented_unsafe_blocks)]
get_server_fd(cookie: *mut c_void, version: u32) -> c_int259 unsafe extern "C" fn get_server_fd(cookie: *mut c_void, version: u32) -> c_int {
260 catch_unwind(|| {
261 assert!(!cookie.is_null());
262 let cookie = &mut *(cookie as *mut RutabagaCookie);
263
264 if version != 0 {
265 return -1;
266 }
267
268 // Transfer the fd ownership to virglrenderer.
269 cookie
270 .render_server_fd
271 .take()
272 .map(SafeDescriptor::into_raw_descriptor)
273 .unwrap_or(-1)
274 })
275 .unwrap_or_else(|_| abort())
276 }
277
278 const VIRGL_RENDERER_CALLBACKS: &virgl_renderer_callbacks = &virgl_renderer_callbacks {
279 version: 3,
280 write_fence: Some(write_fence),
281 create_gl_context: None,
282 destroy_gl_context: None,
283 make_current: None,
284 get_drm_fd: None,
285 write_context_fence: Some(write_context_fence),
286 get_server_fd: Some(get_server_fd),
287 get_egl_display: None,
288 };
289
290 /// Retrieves metadata suitable for export about this resource. If "export_fd" is true,
291 /// performs an export of this resource so that it may be imported by other processes.
export_query(resource_id: u32) -> RutabagaResult<Query>292 fn export_query(resource_id: u32) -> RutabagaResult<Query> {
293 let mut query: Query = Default::default();
294 query.hdr.stype = VIRGL_RENDERER_STRUCTURE_TYPE_EXPORT_QUERY;
295 query.hdr.stype_version = 0;
296 query.hdr.size = size_of::<Query>() as u32;
297 query.in_resource_id = resource_id;
298 query.in_export_fds = 0;
299
300 let ret =
301 // SAFETY:
302 // Safe because the image parameters are stack variables of the correct type.
303 unsafe { virgl_renderer_execute(&mut query as *mut _ as *mut c_void, query.hdr.size) };
304
305 ret_to_res(ret)?;
306 Ok(query)
307 }
308
309 impl VirglRenderer {
init( virglrenderer_flags: VirglRendererFlags, fence_handler: RutabagaFenceHandler, render_server_fd: Option<SafeDescriptor>, ) -> RutabagaResult<Box<dyn RutabagaComponent>>310 pub fn init(
311 virglrenderer_flags: VirglRendererFlags,
312 fence_handler: RutabagaFenceHandler,
313 render_server_fd: Option<SafeDescriptor>,
314 ) -> RutabagaResult<Box<dyn RutabagaComponent>> {
315 if cfg!(debug_assertions) {
316 // TODO(b/315870313): Add safety comment
317 #[allow(clippy::undocumented_unsafe_blocks)]
318 let ret = unsafe { libc::dup2(libc::STDOUT_FILENO, libc::STDERR_FILENO) };
319 if ret == -1 {
320 warn!(
321 "unable to dup2 stdout to stderr: {}",
322 SysError::last_os_error()
323 );
324 }
325 }
326
327 // virglrenderer is a global state backed library that uses thread bound OpenGL contexts.
328 // Initialize it only once and use the non-send/non-sync Renderer struct to keep things tied
329 // to whichever thread called this function first.
330 static INIT_ONCE: AtomicBool = AtomicBool::new(false);
331 if INIT_ONCE
332 .compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire)
333 .is_err()
334 {
335 return Err(RutabagaError::AlreadyInUse);
336 }
337
338 // TODO(b/315870313): Add safety comment
339 #[allow(clippy::undocumented_unsafe_blocks)]
340 unsafe {
341 virgl_set_debug_callback(Some(debug_callback))
342 };
343
344 // Cookie is intentionally never freed because virglrenderer never gets uninitialized.
345 // Otherwise, Resource and Context would become invalid because their lifetime is not tied
346 // to the Renderer instance. Doing so greatly simplifies the ownership for users of this
347 // library.
348 let cookie = Box::into_raw(Box::new(RutabagaCookie {
349 render_server_fd,
350 fence_handler: Some(fence_handler),
351 debug_handler: None,
352 }));
353
354 // SAFETY:
355 // Safe because a valid cookie and set of callbacks is used and the result is checked for
356 // error.
357 let ret = unsafe {
358 virgl_renderer_init(
359 cookie as *mut c_void,
360 virglrenderer_flags.into(),
361 transmute(VIRGL_RENDERER_CALLBACKS),
362 )
363 };
364
365 ret_to_res(ret)?;
366 Ok(Box::new(VirglRenderer {}))
367 }
368
map_info(&self, resource_id: u32) -> RutabagaResult<u32>369 fn map_info(&self, resource_id: u32) -> RutabagaResult<u32> {
370 let mut map_info = 0;
371 // TODO(b/315870313): Add safety comment
372 #[allow(clippy::undocumented_unsafe_blocks)]
373 let ret = unsafe { virgl_renderer_resource_get_map_info(resource_id, &mut map_info) };
374 ret_to_res(ret)?;
375
376 Ok(map_info | RUTABAGA_MAP_ACCESS_RW)
377 }
378
query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo>379 fn query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo> {
380 let query = export_query(resource_id)?;
381 if query.out_num_fds == 0 {
382 return Err(RutabagaError::Unsupported);
383 }
384
385 // virglrenderer unfortunately doesn't return the width or height, so map to zero.
386 Ok(Resource3DInfo {
387 width: 0,
388 height: 0,
389 drm_fourcc: query.out_fourcc,
390 strides: query.out_strides,
391 offsets: query.out_offsets,
392 modifier: query.out_modifier,
393 guest_cpu_mappable: false,
394 })
395 }
396
export_blob(&self, resource_id: u32) -> RutabagaResult<Arc<RutabagaHandle>>397 fn export_blob(&self, resource_id: u32) -> RutabagaResult<Arc<RutabagaHandle>> {
398 let mut fd_type = 0;
399 let mut fd = 0;
400 // TODO(b/315870313): Add safety comment
401 #[allow(clippy::undocumented_unsafe_blocks)]
402 let ret =
403 unsafe { virgl_renderer_resource_export_blob(resource_id, &mut fd_type, &mut fd) };
404 ret_to_res(ret)?;
405
406 // SAFETY:
407 // Safe because the FD was just returned by a successful virglrenderer
408 // call so it must be valid and owned by us.
409 let handle = unsafe { SafeDescriptor::from_raw_descriptor(fd) };
410
411 let handle_type = match fd_type {
412 VIRGL_RENDERER_BLOB_FD_TYPE_DMABUF => RUTABAGA_MEM_HANDLE_TYPE_DMABUF,
413 VIRGL_RENDERER_BLOB_FD_TYPE_SHM => RUTABAGA_MEM_HANDLE_TYPE_SHM,
414 VIRGL_RENDERER_BLOB_FD_TYPE_OPAQUE => RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD,
415 _ => {
416 return Err(RutabagaError::Unsupported);
417 }
418 };
419
420 Ok(Arc::new(RutabagaHandle {
421 os_handle: handle,
422 handle_type,
423 }))
424 }
425 }
426
427 impl Drop for VirglRenderer {
drop(&mut self)428 fn drop(&mut self) {
429 // SAFETY:
430 // Safe because virglrenderer is initialized.
431 //
432 // This invalidates all context ids and resource ids. It is fine because struct Rutabaga
433 // makes sure contexts and resources are dropped before this is reached. Even if it did
434 // not, virglrenderer is designed to deal with invalid ids safely.
435 unsafe {
436 virgl_renderer_cleanup(null_mut());
437 }
438 }
439 }
440
441 impl RutabagaComponent for VirglRenderer {
get_capset_info(&self, capset_id: u32) -> (u32, u32)442 fn get_capset_info(&self, capset_id: u32) -> (u32, u32) {
443 let mut version = 0;
444 let mut size = 0;
445 // SAFETY:
446 // Safe because virglrenderer is initialized by now and properly size stack variables are
447 // used for the pointers.
448 unsafe {
449 virgl_renderer_get_cap_set(capset_id, &mut version, &mut size);
450 }
451 (version, size)
452 }
453
get_capset(&self, capset_id: u32, version: u32) -> Vec<u8>454 fn get_capset(&self, capset_id: u32, version: u32) -> Vec<u8> {
455 let (_, max_size) = self.get_capset_info(capset_id);
456 let mut buf = vec![0u8; max_size as usize];
457 // SAFETY:
458 // Safe because virglrenderer is initialized by now and the given buffer is sized properly
459 // for the given cap id/version.
460 unsafe {
461 virgl_renderer_fill_caps(capset_id, version, buf.as_mut_ptr() as *mut c_void);
462 }
463 buf
464 }
465
force_ctx_0(&self)466 fn force_ctx_0(&self) {
467 // TODO(b/315870313): Add safety comment
468 #[allow(clippy::undocumented_unsafe_blocks)]
469 unsafe {
470 virgl_renderer_force_ctx_0()
471 };
472 }
473
create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()>474 fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
475 // TODO(b/315870313): Add safety comment
476 #[allow(clippy::undocumented_unsafe_blocks)]
477 let ret = unsafe { virgl_renderer_create_fence(fence.fence_id as i32, fence.ctx_id) };
478 ret_to_res(ret)
479 }
480
event_poll(&self)481 fn event_poll(&self) {
482 // TODO(b/315870313): Add safety comment
483 #[allow(clippy::undocumented_unsafe_blocks)]
484 unsafe {
485 virgl_renderer_poll()
486 };
487 }
488
poll_descriptor(&self) -> Option<SafeDescriptor>489 fn poll_descriptor(&self) -> Option<SafeDescriptor> {
490 // SAFETY:
491 // Safe because it can be called anytime and returns -1 in the event of an error.
492 let fd = unsafe { virgl_renderer_get_poll_fd() };
493 if fd >= 0 {
494 if let Ok(dup_fd) = SafeDescriptor::try_from(&fd as &dyn AsRawFd) {
495 return Some(dup_fd);
496 }
497 }
498 None
499 }
500
create_3d( &self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<RutabagaResource>501 fn create_3d(
502 &self,
503 resource_id: u32,
504 resource_create_3d: ResourceCreate3D,
505 ) -> RutabagaResult<RutabagaResource> {
506 let mut args = virgl_renderer_resource_create_args {
507 handle: resource_id,
508 target: resource_create_3d.target,
509 format: resource_create_3d.format,
510 bind: resource_create_3d.bind,
511 width: resource_create_3d.width,
512 height: resource_create_3d.height,
513 depth: resource_create_3d.depth,
514 array_size: resource_create_3d.array_size,
515 last_level: resource_create_3d.last_level,
516 nr_samples: resource_create_3d.nr_samples,
517 flags: resource_create_3d.flags,
518 };
519
520 // SAFETY:
521 // Safe because virglrenderer is initialized by now, and the return value is checked before
522 // returning a new resource. The backing buffers are not supplied with this call.
523 let ret = unsafe { virgl_renderer_resource_create(&mut args, null_mut(), 0) };
524 ret_to_res(ret)?;
525
526 Ok(RutabagaResource {
527 resource_id,
528 handle: self.export_blob(resource_id).ok(),
529 blob: false,
530 blob_mem: 0,
531 blob_flags: 0,
532 map_info: None,
533 info_2d: None,
534 info_3d: self.query(resource_id).ok(),
535 vulkan_info: None,
536 backing_iovecs: None,
537 component_mask: 1 << (RutabagaComponentType::VirglRenderer as u8),
538 size: 0,
539 mapping: None,
540 })
541 }
542
attach_backing( &self, resource_id: u32, vecs: &mut Vec<RutabagaIovec>, ) -> RutabagaResult<()>543 fn attach_backing(
544 &self,
545 resource_id: u32,
546 vecs: &mut Vec<RutabagaIovec>,
547 ) -> RutabagaResult<()> {
548 // SAFETY:
549 // Safe because the backing is into guest memory that we store a reference count for.
550 let ret = unsafe {
551 virgl_renderer_resource_attach_iov(
552 resource_id as i32,
553 vecs.as_mut_ptr() as *mut iovec,
554 vecs.len() as i32,
555 )
556 };
557 ret_to_res(ret)
558 }
559
detach_backing(&self, resource_id: u32)560 fn detach_backing(&self, resource_id: u32) {
561 // SAFETY:
562 // Safe as we don't need the old backing iovecs returned and the reference to the guest
563 // memory can be dropped as it will no longer be needed for this resource.
564 unsafe {
565 virgl_renderer_resource_detach_iov(resource_id as i32, null_mut(), null_mut());
566 }
567 }
568
unref_resource(&self, resource_id: u32)569 fn unref_resource(&self, resource_id: u32) {
570 // SAFETY:
571 // The resource is safe to unreference destroy because no user of these bindings can still
572 // be holding a reference.
573 unsafe {
574 virgl_renderer_resource_unref(resource_id);
575 }
576 }
577
transfer_write( &self, ctx_id: u32, resource: &mut RutabagaResource, transfer: Transfer3D, ) -> RutabagaResult<()>578 fn transfer_write(
579 &self,
580 ctx_id: u32,
581 resource: &mut RutabagaResource,
582 transfer: Transfer3D,
583 ) -> RutabagaResult<()> {
584 if transfer.is_empty() {
585 return Ok(());
586 }
587
588 let mut transfer_box = VirglBox {
589 x: transfer.x,
590 y: transfer.y,
591 z: transfer.z,
592 w: transfer.w,
593 h: transfer.h,
594 d: transfer.d,
595 };
596
597 // SAFETY:
598 // Safe because only stack variables of the appropriate type are used.
599 let ret = unsafe {
600 virgl_renderer_transfer_write_iov(
601 resource.resource_id,
602 ctx_id,
603 transfer.level as i32,
604 transfer.stride,
605 transfer.layer_stride,
606 &mut transfer_box as *mut VirglBox as *mut virgl_box,
607 transfer.offset,
608 null_mut(),
609 0,
610 )
611 };
612 ret_to_res(ret)
613 }
614
transfer_read( &self, ctx_id: u32, resource: &mut RutabagaResource, transfer: Transfer3D, buf: Option<IoSliceMut>, ) -> RutabagaResult<()>615 fn transfer_read(
616 &self,
617 ctx_id: u32,
618 resource: &mut RutabagaResource,
619 transfer: Transfer3D,
620 buf: Option<IoSliceMut>,
621 ) -> RutabagaResult<()> {
622 if transfer.is_empty() {
623 return Ok(());
624 }
625
626 let mut transfer_box = VirglBox {
627 x: transfer.x,
628 y: transfer.y,
629 z: transfer.z,
630 w: transfer.w,
631 h: transfer.h,
632 d: transfer.d,
633 };
634
635 let mut iov = RutabagaIovec {
636 base: null_mut(),
637 len: 0,
638 };
639
640 let (iovecs, num_iovecs) = match buf {
641 Some(mut buf) => {
642 iov.base = buf.as_mut_ptr() as *mut c_void;
643 iov.len = buf.len();
644 (&mut iov as *mut RutabagaIovec as *mut iovec, 1)
645 }
646 None => (null_mut(), 0),
647 };
648
649 // SAFETY:
650 // Safe because only stack variables of the appropriate type are used.
651 let ret = unsafe {
652 virgl_renderer_transfer_read_iov(
653 resource.resource_id,
654 ctx_id,
655 transfer.level,
656 transfer.stride,
657 transfer.layer_stride,
658 &mut transfer_box as *mut VirglBox as *mut virgl_box,
659 transfer.offset,
660 iovecs,
661 num_iovecs,
662 )
663 };
664 ret_to_res(ret)
665 }
666
667 #[allow(unused_variables)]
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>668 fn create_blob(
669 &mut self,
670 ctx_id: u32,
671 resource_id: u32,
672 resource_create_blob: ResourceCreateBlob,
673 mut iovec_opt: Option<Vec<RutabagaIovec>>,
674 _handle_opt: Option<RutabagaHandle>,
675 ) -> RutabagaResult<RutabagaResource> {
676 let mut iovec_ptr = null_mut();
677 let mut num_iovecs = 0;
678 if let Some(ref mut iovecs) = iovec_opt {
679 iovec_ptr = iovecs.as_mut_ptr();
680 num_iovecs = iovecs.len();
681 }
682
683 let resource_create_args = virgl_renderer_resource_create_blob_args {
684 res_handle: resource_id,
685 ctx_id,
686 blob_mem: resource_create_blob.blob_mem,
687 blob_flags: resource_create_blob.blob_flags,
688 blob_id: resource_create_blob.blob_id,
689 size: resource_create_blob.size,
690 iovecs: iovec_ptr as *const iovec,
691 num_iovs: num_iovecs as u32,
692 };
693
694 // TODO(b/315870313): Add safety comment
695 #[allow(clippy::undocumented_unsafe_blocks)]
696 let ret = unsafe { virgl_renderer_resource_create_blob(&resource_create_args) };
697 ret_to_res(ret)?;
698
699 // TODO(b/244591751): assign vulkan_info to support opaque_fd mapping via Vulkano when
700 // sandboxing (hence external_blob) is enabled.
701 Ok(RutabagaResource {
702 resource_id,
703 handle: self.export_blob(resource_id).ok(),
704 blob: true,
705 blob_mem: resource_create_blob.blob_mem,
706 blob_flags: resource_create_blob.blob_flags,
707 map_info: self.map_info(resource_id).ok(),
708 info_2d: None,
709 info_3d: self.query(resource_id).ok(),
710 vulkan_info: None,
711 backing_iovecs: iovec_opt,
712 component_mask: 1 << (RutabagaComponentType::VirglRenderer as u8),
713 size: resource_create_blob.size,
714 mapping: None,
715 })
716 }
717
map(&self, resource_id: u32) -> RutabagaResult<RutabagaMapping>718 fn map(&self, resource_id: u32) -> RutabagaResult<RutabagaMapping> {
719 let mut map: *mut c_void = null_mut();
720 let mut size: u64 = 0;
721 // SAFETY:
722 // Safe because virglrenderer wraps and validates use of GL/VK.
723 let ret = unsafe { virgl_renderer_resource_map(resource_id, &mut map, &mut size) };
724 if ret != 0 {
725 return Err(RutabagaError::MappingFailed(ret));
726 }
727
728 Ok(RutabagaMapping {
729 ptr: map as u64,
730 size,
731 })
732 }
733
unmap(&self, resource_id: u32) -> RutabagaResult<()>734 fn unmap(&self, resource_id: u32) -> RutabagaResult<()> {
735 // SAFETY:
736 // Safe because virglrenderer is initialized by now.
737 let ret = unsafe { virgl_renderer_resource_unmap(resource_id) };
738 ret_to_res(ret)
739 }
740
741 #[allow(unused_variables)]
export_fence(&self, fence_id: u64) -> RutabagaResult<RutabagaHandle>742 fn export_fence(&self, fence_id: u64) -> RutabagaResult<RutabagaHandle> {
743 #[cfg(virgl_renderer_unstable)]
744 {
745 let mut fd: i32 = 0;
746 // SAFETY:
747 // Safe because the parameters are stack variables of the correct type.
748 let ret = unsafe { virgl_renderer_export_fence(fence_id, &mut fd) };
749 ret_to_res(ret)?;
750
751 // SAFETY:
752 // Safe because the FD was just returned by a successful virglrenderer call so it must
753 // be valid and owned by us.
754 let fence = unsafe { SafeDescriptor::from_raw_descriptor(fd) };
755 Ok(RutabagaHandle {
756 os_handle: fence,
757 handle_type: RUTABAGA_FENCE_HANDLE_TYPE_SYNC_FD,
758 })
759 }
760 #[cfg(not(virgl_renderer_unstable))]
761 Err(RutabagaError::Unsupported)
762 }
763
764 #[allow(unused_variables)]
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 virglrenderer 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 match context_init {
782 0 => virgl_renderer_context_create(
783 ctx_id,
784 name.len() as u32,
785 name.as_ptr() as *const c_char,
786 ),
787 _ => virgl_renderer_context_create_with_flags(
788 ctx_id,
789 context_init,
790 name.len() as u32,
791 name.as_ptr() as *const c_char,
792 ),
793 }
794 };
795 ret_to_res(ret)?;
796 Ok(Box::new(VirglRendererContext { ctx_id }))
797 }
798 }
799