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