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