xref: /aosp_15_r20/external/crosvm/rutabaga_gfx/src/rutabaga_core.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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 //! rutabaga_core: Cross-platform, Rust-based, Wayland and Vulkan centric GPU virtualization.
6 use std::collections::BTreeMap as Map;
7 use std::convert::TryInto;
8 use std::io::IoSliceMut;
9 use std::io::Read;
10 use std::io::Write;
11 use std::sync::Arc;
12 
13 use serde::Deserialize;
14 use serde::Serialize;
15 
16 use crate::cross_domain::CrossDomain;
17 #[cfg(feature = "gfxstream")]
18 use crate::gfxstream::Gfxstream;
19 use crate::rutabaga_2d::Rutabaga2D;
20 use crate::rutabaga_os::MemoryMapping;
21 use crate::rutabaga_os::OwnedDescriptor;
22 use crate::rutabaga_utils::*;
23 #[cfg(feature = "virgl_renderer")]
24 use crate::virgl_renderer::VirglRenderer;
25 
26 const RUTABAGA_DEFAULT_WIDTH: u32 = 1280;
27 const RUTABAGA_DEFAULT_HEIGHT: u32 = 1024;
28 
29 /// Information required for 2D functionality.
30 pub struct Rutabaga2DInfo {
31     pub width: u32,
32     pub height: u32,
33     pub host_mem: Vec<u8>,
34 }
35 
36 /// A Rutabaga resource, supporting 2D and 3D rutabaga features.  Assumes a single-threaded library.
37 pub struct RutabagaResource {
38     pub resource_id: u32,
39     pub handle: Option<Arc<RutabagaHandle>>,
40     pub blob: bool,
41     pub blob_mem: u32,
42     pub blob_flags: u32,
43     pub map_info: Option<u32>,
44     pub info_2d: Option<Rutabaga2DInfo>,
45     pub info_3d: Option<Resource3DInfo>,
46     pub vulkan_info: Option<VulkanInfo>,
47     pub backing_iovecs: Option<Vec<RutabagaIovec>>,
48 
49     /// Bitmask of components that have already imported this resource
50     pub component_mask: u8,
51     pub size: u64,
52     pub mapping: Option<MemoryMapping>,
53 }
54 
55 #[derive(Deserialize, Serialize)]
56 pub struct RutabagaResourceSnapshot {
57     pub resource_id: u32,
58     pub width: u32,
59     pub height: u32,
60 }
61 
62 impl TryFrom<&RutabagaResource> for RutabagaResourceSnapshot {
63     type Error = RutabagaError;
try_from(resource: &RutabagaResource) -> Result<Self, Self::Error>64     fn try_from(resource: &RutabagaResource) -> Result<Self, Self::Error> {
65         let info = resource
66             .info_2d
67             .as_ref()
68             .ok_or(RutabagaError::Unsupported)?;
69         assert_eq!(
70             usize::try_from(info.width * info.height * 4).unwrap(),
71             info.host_mem.len()
72         );
73         assert_eq!(usize::try_from(resource.size).unwrap(), info.host_mem.len());
74         Ok(RutabagaResourceSnapshot {
75             resource_id: resource.resource_id,
76             width: info.width,
77             height: info.height,
78         })
79     }
80 }
81 
82 impl TryFrom<RutabagaResourceSnapshot> for RutabagaResource {
83     type Error = RutabagaError;
try_from(snapshot: RutabagaResourceSnapshot) -> Result<Self, Self::Error>84     fn try_from(snapshot: RutabagaResourceSnapshot) -> Result<Self, Self::Error> {
85         let size = u64::from(snapshot.width * snapshot.height * 4);
86         Ok(RutabagaResource {
87             resource_id: snapshot.resource_id,
88             handle: None,
89             blob: false,
90             blob_mem: 0,
91             blob_flags: 0,
92             map_info: None,
93             info_2d: Some(Rutabaga2DInfo {
94                 width: snapshot.width,
95                 height: snapshot.height,
96                 host_mem: vec![0; usize::try_from(size).unwrap()],
97             }),
98             info_3d: None,
99             vulkan_info: None,
100             // NOTE: `RutabagaResource::backing_iovecs` isn't snapshotted because the
101             // pointers won't be valid at restore time, see the `Rutabaga::restore` doc.
102             // If the client doesn't attach new iovecs, the restored resource will
103             // behave as if they had been detached (instead of segfaulting on the stale
104             // iovec pointers).
105             backing_iovecs: None,
106             component_mask: 1 << (RutabagaComponentType::Rutabaga2D as u8),
107             size,
108             mapping: None,
109         })
110     }
111 }
112 
113 /// A RutabagaComponent is a building block of the Virtual Graphics Interface (VGI).  Each component
114 /// on it's own is sufficient to virtualize graphics on many Google products.  These components wrap
115 /// libraries like gfxstream or virglrenderer, and Rutabaga's own 2D and cross-domain prototype
116 /// functionality.
117 ///
118 /// Most methods return a `RutabagaResult` that indicate the success, failure, or requested data for
119 /// the given command.
120 pub trait RutabagaComponent {
121     /// Implementations should return the version and size of the given capset_id.  (0, 0) is
122     /// returned by default.
get_capset_info(&self, _capset_id: u32) -> (u32, u32)123     fn get_capset_info(&self, _capset_id: u32) -> (u32, u32) {
124         (0, 0)
125     }
126 
127     /// Implementations should return the capabilites of given a `capset_id` and `version`.  A
128     /// zero-sized array is returned by default.
get_capset(&self, _capset_id: u32, _version: u32) -> Vec<u8>129     fn get_capset(&self, _capset_id: u32, _version: u32) -> Vec<u8> {
130         Vec::new()
131     }
132 
133     /// Implementations should set their internal context to be the reserved context 0.
force_ctx_0(&self)134     fn force_ctx_0(&self) {}
135 
136     /// Implementations must create a fence that represents the completion of prior work.  This is
137     /// required for synchronization with the guest kernel.
create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()>138     fn create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()> {
139         Ok(())
140     }
141 
142     /// Used only by VirglRenderer to poll when its poll_descriptor is signaled.
event_poll(&self)143     fn event_poll(&self) {}
144 
145     /// Used only by VirglRenderer to return a poll_descriptor that is signaled when a poll() is
146     /// necessary.
poll_descriptor(&self) -> Option<OwnedDescriptor>147     fn poll_descriptor(&self) -> Option<OwnedDescriptor> {
148         None
149     }
150 
151     /// Implementations must create a resource with the given metadata.  For 2D rutabaga components,
152     /// this a system memory allocation.  For 3D components, this is typically a GL texture or
153     /// buffer.  Vulkan components should use blob resources instead.
create_3d( &self, resource_id: u32, _resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<RutabagaResource>154     fn create_3d(
155         &self,
156         resource_id: u32,
157         _resource_create_3d: ResourceCreate3D,
158     ) -> RutabagaResult<RutabagaResource> {
159         Ok(RutabagaResource {
160             resource_id,
161             handle: None,
162             blob: false,
163             blob_mem: 0,
164             blob_flags: 0,
165             map_info: None,
166             info_2d: None,
167             info_3d: None,
168             vulkan_info: None,
169             backing_iovecs: None,
170             component_mask: 0,
171             size: 0,
172             mapping: None,
173         })
174     }
175 
176     /// Implementations must attach `vecs` to the resource.
attach_backing( &self, _resource_id: u32, _vecs: &mut Vec<RutabagaIovec>, ) -> RutabagaResult<()>177     fn attach_backing(
178         &self,
179         _resource_id: u32,
180         _vecs: &mut Vec<RutabagaIovec>,
181     ) -> RutabagaResult<()> {
182         Ok(())
183     }
184 
185     /// Implementations must detach `vecs` from the resource.
detach_backing(&self, _resource_id: u32)186     fn detach_backing(&self, _resource_id: u32) {}
187 
188     /// Implementations must release the guest kernel reference on the resource.
unref_resource(&self, _resource_id: u32)189     fn unref_resource(&self, _resource_id: u32) {}
190 
191     /// Implementations must perform the transfer write operation.  For 2D rutabaga components, this
192     /// done via memcpy().  For 3D components, this is typically done via glTexSubImage(..).
transfer_write( &self, _ctx_id: u32, _resource: &mut RutabagaResource, _transfer: Transfer3D, ) -> RutabagaResult<()>193     fn transfer_write(
194         &self,
195         _ctx_id: u32,
196         _resource: &mut RutabagaResource,
197         _transfer: Transfer3D,
198     ) -> RutabagaResult<()> {
199         Ok(())
200     }
201 
202     /// Implementations must perform the transfer read operation.  For 2D rutabaga components, this
203     /// done via memcpy().  For 3D components, this is typically done via glReadPixels(..).
transfer_read( &self, _ctx_id: u32, _resource: &mut RutabagaResource, _transfer: Transfer3D, _buf: Option<IoSliceMut>, ) -> RutabagaResult<()>204     fn transfer_read(
205         &self,
206         _ctx_id: u32,
207         _resource: &mut RutabagaResource,
208         _transfer: Transfer3D,
209         _buf: Option<IoSliceMut>,
210     ) -> RutabagaResult<()> {
211         Ok(())
212     }
213 
214     /// Implementations must flush the given resource to the display.
resource_flush(&self, _resource_id: &mut RutabagaResource) -> RutabagaResult<()>215     fn resource_flush(&self, _resource_id: &mut RutabagaResource) -> RutabagaResult<()> {
216         Err(RutabagaError::Unsupported)
217     }
218 
219     /// Implementations must create a blob resource on success.  The memory parameters, size, and
220     /// usage of the blob resource is given by `resource_create_blob`.
create_blob( &mut self, _ctx_id: u32, _resource_id: u32, _resource_create_blob: ResourceCreateBlob, _iovec_opt: Option<Vec<RutabagaIovec>>, _handle_opt: Option<RutabagaHandle>, ) -> RutabagaResult<RutabagaResource>221     fn create_blob(
222         &mut self,
223         _ctx_id: u32,
224         _resource_id: u32,
225         _resource_create_blob: ResourceCreateBlob,
226         _iovec_opt: Option<Vec<RutabagaIovec>>,
227         _handle_opt: Option<RutabagaHandle>,
228     ) -> RutabagaResult<RutabagaResource> {
229         Err(RutabagaError::Unsupported)
230     }
231 
232     /// Implementations must map the blob resource on success.  This is typically done by
233     /// glMapBufferRange(...) or vkMapMemory.
map(&self, _resource_id: u32) -> RutabagaResult<RutabagaMapping>234     fn map(&self, _resource_id: u32) -> RutabagaResult<RutabagaMapping> {
235         Err(RutabagaError::Unsupported)
236     }
237 
238     /// Implementations must unmap the blob resource on success.  This is typically done by
239     /// glUnmapBuffer(...) or vkUnmapMemory.
unmap(&self, _resource_id: u32) -> RutabagaResult<()>240     fn unmap(&self, _resource_id: u32) -> RutabagaResult<()> {
241         Err(RutabagaError::Unsupported)
242     }
243 
244     /// Implementations must return a RutabagaHandle of the fence on success.
export_fence(&self, _fence_id: u64) -> RutabagaResult<RutabagaHandle>245     fn export_fence(&self, _fence_id: u64) -> RutabagaResult<RutabagaHandle> {
246         Err(RutabagaError::Unsupported)
247     }
248 
249     /// Implementations must create a context for submitting commands.  The command stream of the
250     /// context is determined by `context_init`.  For virgl contexts, it is a Gallium/TGSI command
251     /// stream.  For gfxstream contexts, it's an autogenerated Vulkan or GLES streams.
create_context( &self, _ctx_id: u32, _context_init: u32, _context_name: Option<&str>, _fence_handler: RutabagaFenceHandler, ) -> RutabagaResult<Box<dyn RutabagaContext>>252     fn create_context(
253         &self,
254         _ctx_id: u32,
255         _context_init: u32,
256         _context_name: Option<&str>,
257         _fence_handler: RutabagaFenceHandler,
258     ) -> RutabagaResult<Box<dyn RutabagaContext>> {
259         Err(RutabagaError::Unsupported)
260     }
261 
262     /// Implementations should stop workers.
suspend(&self) -> RutabagaResult<()>263     fn suspend(&self) -> RutabagaResult<()> {
264         Ok(())
265     }
266 
267     /// Implementations must snapshot to the specified directory
snapshot(&self, _directory: &str) -> RutabagaResult<()>268     fn snapshot(&self, _directory: &str) -> RutabagaResult<()> {
269         Err(RutabagaError::Unsupported)
270     }
271 
272     /// Implementations must restore from the specified directory
restore(&self, _directory: &str) -> RutabagaResult<()>273     fn restore(&self, _directory: &str) -> RutabagaResult<()> {
274         Err(RutabagaError::Unsupported)
275     }
276 
277     /// Implementations should resume workers.
resume(&self) -> RutabagaResult<()>278     fn resume(&self) -> RutabagaResult<()> {
279         Ok(())
280     }
281 
282     /// Implementations must perform a blocking wait-sync on the resource identified by resource_id
wait_sync(&self, _resource: &RutabagaResource) -> RutabagaResult<()>283     fn wait_sync(&self, _resource: &RutabagaResource) -> RutabagaResult<()> {
284         Err(RutabagaError::Unsupported)
285     }
286 }
287 
288 pub trait RutabagaContext {
289     /// Implementations must return a RutabagaResource given the `resource_create_blob` parameters.
context_create_blob( &mut self, _resource_id: u32, _resource_create_blob: ResourceCreateBlob, _handle_opt: Option<RutabagaHandle>, ) -> RutabagaResult<RutabagaResource>290     fn context_create_blob(
291         &mut self,
292         _resource_id: u32,
293         _resource_create_blob: ResourceCreateBlob,
294         _handle_opt: Option<RutabagaHandle>,
295     ) -> RutabagaResult<RutabagaResource> {
296         Err(RutabagaError::Unsupported)
297     }
298 
299     /// Implementations must handle the context-specific command stream.
submit_cmd( &mut self, _commands: &mut [u8], _fence_ids: &[u64], shareable_fences: Vec<RutabagaHandle>, ) -> RutabagaResult<()>300     fn submit_cmd(
301         &mut self,
302         _commands: &mut [u8],
303         _fence_ids: &[u64],
304         shareable_fences: Vec<RutabagaHandle>,
305     ) -> RutabagaResult<()>;
306 
307     /// Implementations may use `resource` in this context's command stream.
attach(&mut self, _resource: &mut RutabagaResource)308     fn attach(&mut self, _resource: &mut RutabagaResource);
309 
310     /// Implementations must stop using `resource` in this context's command stream.
detach(&mut self, _resource: &RutabagaResource)311     fn detach(&mut self, _resource: &RutabagaResource);
312 
313     /// Implementations must create a fence on specified `ring_idx` in `fence`.  This
314     /// allows for multiple synchronizations timelines per RutabagaContext.
315     ///
316     /// If RUTABAGA_FLAG_FENCE_HOST_SHAREABLE is set, a rutabaga handle must be returned on
317     /// success.
context_create_fence( &mut self, _fence: RutabagaFence, ) -> RutabagaResult<Option<RutabagaHandle>>318     fn context_create_fence(
319         &mut self,
320         _fence: RutabagaFence,
321     ) -> RutabagaResult<Option<RutabagaHandle>> {
322         Err(RutabagaError::Unsupported)
323     }
324 
325     /// Implementations must return the component type associated with the context.
component_type(&self) -> RutabagaComponentType326     fn component_type(&self) -> RutabagaComponentType;
327 }
328 
329 #[derive(Copy, Clone)]
330 struct RutabagaCapsetInfo {
331     pub capset_id: u32,
332     pub component: RutabagaComponentType,
333     pub name: &'static str,
334 }
335 
336 const RUTABAGA_CAPSETS: [RutabagaCapsetInfo; 9] = [
337     RutabagaCapsetInfo {
338         capset_id: RUTABAGA_CAPSET_VIRGL,
339         component: RutabagaComponentType::VirglRenderer,
340         name: "virgl",
341     },
342     RutabagaCapsetInfo {
343         capset_id: RUTABAGA_CAPSET_VIRGL2,
344         component: RutabagaComponentType::VirglRenderer,
345         name: "virgl2",
346     },
347     RutabagaCapsetInfo {
348         capset_id: RUTABAGA_CAPSET_GFXSTREAM_VULKAN,
349         component: RutabagaComponentType::Gfxstream,
350         name: "gfxstream-vulkan",
351     },
352     RutabagaCapsetInfo {
353         capset_id: RUTABAGA_CAPSET_VENUS,
354         component: RutabagaComponentType::VirglRenderer,
355         name: "venus",
356     },
357     RutabagaCapsetInfo {
358         capset_id: RUTABAGA_CAPSET_CROSS_DOMAIN,
359         component: RutabagaComponentType::CrossDomain,
360         name: "cross-domain",
361     },
362     RutabagaCapsetInfo {
363         capset_id: RUTABAGA_CAPSET_DRM,
364         component: RutabagaComponentType::VirglRenderer,
365         name: "drm",
366     },
367     RutabagaCapsetInfo {
368         capset_id: RUTABAGA_CAPSET_GFXSTREAM_MAGMA,
369         component: RutabagaComponentType::Gfxstream,
370         name: "gfxstream-magma",
371     },
372     RutabagaCapsetInfo {
373         capset_id: RUTABAGA_CAPSET_GFXSTREAM_GLES,
374         component: RutabagaComponentType::Gfxstream,
375         name: "gfxstream-gles",
376     },
377     RutabagaCapsetInfo {
378         capset_id: RUTABAGA_CAPSET_GFXSTREAM_COMPOSER,
379         component: RutabagaComponentType::Gfxstream,
380         name: "gfxstream-composer",
381     },
382 ];
383 
calculate_capset_mask<'a, I: Iterator<Item = &'a str>>(context_names: I) -> u64384 pub fn calculate_capset_mask<'a, I: Iterator<Item = &'a str>>(context_names: I) -> u64 {
385     let mut capset_mask = 0;
386     for name in context_names {
387         if let Some(capset) = RUTABAGA_CAPSETS.iter().find(|capset| capset.name == name) {
388             capset_mask |= 1 << capset.capset_id;
389         };
390     }
391 
392     capset_mask
393 }
394 
calculate_capset_names(capset_mask: u64) -> Vec<String>395 pub fn calculate_capset_names(capset_mask: u64) -> Vec<String> {
396     RUTABAGA_CAPSETS
397         .iter()
398         .filter(|capset| capset_mask & (1 << capset.capset_id) != 0)
399         .map(|capset| capset.name.to_string())
400         .collect()
401 }
402 
calculate_component(component_mask: u8) -> RutabagaResult<RutabagaComponentType>403 fn calculate_component(component_mask: u8) -> RutabagaResult<RutabagaComponentType> {
404     if component_mask.count_ones() != 1 {
405         return Err(RutabagaError::SpecViolation("can't infer single component"));
406     }
407 
408     match component_mask.trailing_zeros() {
409         0 => Ok(RutabagaComponentType::Rutabaga2D),
410         1 => Ok(RutabagaComponentType::VirglRenderer),
411         2 => Ok(RutabagaComponentType::Gfxstream),
412         3 => Ok(RutabagaComponentType::CrossDomain),
413         _ => Err(RutabagaError::InvalidComponent),
414     }
415 }
416 
417 /// The global libary handle used to query capability sets, create resources and contexts.
418 ///
419 /// Currently, Rutabaga only supports one default component.  Many components running at the
420 /// same time is a stretch goal of Rutabaga GFX.
421 ///
422 /// Not thread-safe, but can be made so easily.  Making non-Rutabaga, C/C++ components
423 /// thread-safe is more difficult.
424 pub struct Rutabaga {
425     resources: Map<u32, RutabagaResource>,
426     #[cfg(fence_passing_option1)]
427     shareable_fences: Map<u64, RutabagaHandle>,
428     contexts: Map<u32, Box<dyn RutabagaContext>>,
429     // Declare components after resources and contexts such that it is dropped last.
430     components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>>,
431     default_component: RutabagaComponentType,
432     capset_info: Vec<RutabagaCapsetInfo>,
433     fence_handler: RutabagaFenceHandler,
434 }
435 
436 /// The serialized and deserialized parts of `Rutabaga` that are preserved across
437 /// snapshot() and restore().
438 #[derive(Deserialize, Serialize)]
439 pub struct RutabagaSnapshot {
440     pub resources: Map<u32, RutabagaResourceSnapshot>,
441 }
442 
443 impl Rutabaga {
suspend(&self) -> RutabagaResult<()>444     pub fn suspend(&self) -> RutabagaResult<()> {
445         let component = self
446             .components
447             .get(&self.default_component)
448             .ok_or(RutabagaError::InvalidComponent)?;
449 
450         component.suspend()
451     }
452 
453     /// Take a snapshot of Rutabaga's current state. The snapshot is serialized into an opaque byte
454     /// stream and written to `w`.
snapshot(&self, w: &mut impl Write, directory: &str) -> RutabagaResult<()>455     pub fn snapshot(&self, w: &mut impl Write, directory: &str) -> RutabagaResult<()> {
456         if self.default_component == RutabagaComponentType::Gfxstream {
457             let component = self
458                 .components
459                 .get(&self.default_component)
460                 .ok_or(RutabagaError::InvalidComponent)?;
461 
462             component.snapshot(directory)
463         } else if self.default_component == RutabagaComponentType::Rutabaga2D {
464             let snapshot = RutabagaSnapshot {
465                 resources: self
466                     .resources
467                     .iter()
468                     .map(|(i, r)| Ok((*i, RutabagaResourceSnapshot::try_from(r)?)))
469                     .collect::<RutabagaResult<_>>()?,
470             };
471 
472             serde_json::to_writer(w, &snapshot).map_err(|e| RutabagaError::IoError(e.into()))
473         } else {
474             Err(RutabagaError::Unsupported)
475         }
476     }
477 
478     /// Restore Rutabaga to a previously snapshot'd state.
479     ///
480     /// Snapshotting on one host machine and then restoring on another ("host migration") might
481     /// work for very similar machines but isn't explicitly supported yet.
482     ///
483     /// Rutabaga will recreate resources internally, but it's the VMM's responsibility to re-attach
484     /// backing iovecs and re-map the memory after re-creation. Specifically:
485     ///
486     /// * Mode2D
487     ///    * The VMM must call `Rutabaga::attach_backing` calls for all resources that had backing
488     ///      memory at the time of the snapshot.
489     /// * ModeVirglRenderer
490     ///    * Not supported.
491     /// * ModeGfxstream
492     ///    * WiP support.
493     ///
494     /// NOTES: This is required because the pointers to backing memory aren't stable, help from the
495     /// VMM is necessary. In an alternative approach, the VMM could supply Rutabaga with callbacks
496     /// to translate to/from stable guest physical addresses, but it is unclear how well that
497     /// approach would scale to support 3D modes, which have others problems that require VMM help,
498     /// like resource handles.
restore(&mut self, r: &mut impl Read, directory: &str) -> RutabagaResult<()>499     pub fn restore(&mut self, r: &mut impl Read, directory: &str) -> RutabagaResult<()> {
500         if self.default_component == RutabagaComponentType::Gfxstream {
501             let component = self
502                 .components
503                 .get_mut(&self.default_component)
504                 .ok_or(RutabagaError::InvalidComponent)?;
505 
506             component.restore(directory)
507         } else if self.default_component == RutabagaComponentType::Rutabaga2D {
508             let snapshot: RutabagaSnapshot =
509                 serde_json::from_reader(r).map_err(|e| RutabagaError::IoError(e.into()))?;
510 
511             self.resources = snapshot
512                 .resources
513                 .into_iter()
514                 .map(|(i, s)| Ok((i, RutabagaResource::try_from(s)?)))
515                 .collect::<RutabagaResult<_>>()?;
516 
517             return Ok(());
518         } else {
519             Err(RutabagaError::Unsupported)
520         }
521     }
522 
resume(&self) -> RutabagaResult<()>523     pub fn resume(&self) -> RutabagaResult<()> {
524         let component = self
525             .components
526             .get(&self.default_component)
527             .ok_or(RutabagaError::InvalidComponent)?;
528 
529         component.resume()
530     }
531 
capset_id_to_component_type(&self, capset_id: u32) -> RutabagaResult<RutabagaComponentType>532     fn capset_id_to_component_type(&self, capset_id: u32) -> RutabagaResult<RutabagaComponentType> {
533         let component = self
534             .capset_info
535             .iter()
536             .find(|capset_info| capset_info.capset_id == capset_id)
537             .ok_or(RutabagaError::InvalidCapset)?
538             .component;
539 
540         Ok(component)
541     }
542 
capset_index_to_component_info(&self, index: u32) -> RutabagaResult<RutabagaCapsetInfo>543     fn capset_index_to_component_info(&self, index: u32) -> RutabagaResult<RutabagaCapsetInfo> {
544         let idx = index as usize;
545         if idx >= self.capset_info.len() {
546             return Err(RutabagaError::InvalidCapset);
547         }
548 
549         Ok(self.capset_info[idx])
550     }
551 
552     /// Gets the version and size for the capabilty set `index`.
get_capset_info(&self, index: u32) -> RutabagaResult<(u32, u32, u32)>553     pub fn get_capset_info(&self, index: u32) -> RutabagaResult<(u32, u32, u32)> {
554         let capset_info = self.capset_index_to_component_info(index)?;
555 
556         let component = self
557             .components
558             .get(&capset_info.component)
559             .ok_or(RutabagaError::InvalidComponent)?;
560 
561         let (capset_version, capset_size) = component.get_capset_info(capset_info.capset_id);
562         Ok((capset_info.capset_id, capset_version, capset_size))
563     }
564 
565     /// Gets the capability set for the `capset_id` and `version`.
566     /// Each capability set is associated with a context type, which is associated
567     /// with a rutabaga component.
get_capset(&self, capset_id: u32, version: u32) -> RutabagaResult<Vec<u8>>568     pub fn get_capset(&self, capset_id: u32, version: u32) -> RutabagaResult<Vec<u8>> {
569         // The default workaround is just until context types are fully supported in all
570         // Google kernels.
571         let component_type = self
572             .capset_id_to_component_type(capset_id)
573             .unwrap_or(self.default_component);
574 
575         let component = self
576             .components
577             .get(&component_type)
578             .ok_or(RutabagaError::InvalidComponent)?;
579 
580         Ok(component.get_capset(capset_id, version))
581     }
582 
583     /// Gets the number of capsets
get_num_capsets(&self) -> u32584     pub fn get_num_capsets(&self) -> u32 {
585         self.capset_info.len() as u32
586     }
587 
588     /// Forces context zero for the default rutabaga component.
force_ctx_0(&self)589     pub fn force_ctx_0(&self) {
590         if let Some(component) = self.components.get(&self.default_component) {
591             component.force_ctx_0();
592         }
593     }
594 
595     /// Creates a fence with the given `fence`.
596     /// If the flags include RUTABAGA_FLAG_INFO_RING_IDX, then the fence is created on a
597     /// specific timeline on the specific context.
create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()>598     pub fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
599         if fence.flags & RUTABAGA_FLAG_INFO_RING_IDX != 0 {
600             let ctx = self
601                 .contexts
602                 .get_mut(&fence.ctx_id)
603                 .ok_or(RutabagaError::InvalidContextId)?;
604 
605             #[allow(unused_variables)]
606             let handle_opt = ctx.context_create_fence(fence)?;
607 
608             #[cfg(fence_passing_option1)]
609             if fence.flags & RUTABAGA_FLAG_FENCE_HOST_SHAREABLE != 0 {
610                 let handle = handle_opt.unwrap();
611                 self.shareable_fences.insert(fence.fence_id, handle);
612             }
613         } else {
614             let component = self
615                 .components
616                 .get_mut(&self.default_component)
617                 .ok_or(RutabagaError::InvalidComponent)?;
618 
619             component.create_fence(fence)?;
620         }
621 
622         Ok(())
623     }
624 
625     /// Polls the default rutabaga component.
event_poll(&self)626     pub fn event_poll(&self) {
627         if let Some(component) = self.components.get(&self.default_component) {
628             component.event_poll();
629         }
630     }
631 
632     /// Returns a pollable descriptor for the default rutabaga component. In practice, it is only
633     /// not None if the default component is virglrenderer.
poll_descriptor(&self) -> Option<OwnedDescriptor>634     pub fn poll_descriptor(&self) -> Option<OwnedDescriptor> {
635         let component = self.components.get(&self.default_component).or(None)?;
636         component.poll_descriptor()
637     }
638 
639     /// Creates a resource with the `resource_create_3d` metadata.
resource_create_3d( &mut self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<()>640     pub fn resource_create_3d(
641         &mut self,
642         resource_id: u32,
643         resource_create_3d: ResourceCreate3D,
644     ) -> RutabagaResult<()> {
645         let component = self
646             .components
647             .get_mut(&self.default_component)
648             .ok_or(RutabagaError::InvalidComponent)?;
649 
650         if self.resources.contains_key(&resource_id) {
651             return Err(RutabagaError::InvalidResourceId);
652         }
653 
654         let resource = component.create_3d(resource_id, resource_create_3d)?;
655         self.resources.insert(resource_id, resource);
656         Ok(())
657     }
658 
659     /// Attaches `vecs` to the resource.
attach_backing( &mut self, resource_id: u32, mut vecs: Vec<RutabagaIovec>, ) -> RutabagaResult<()>660     pub fn attach_backing(
661         &mut self,
662         resource_id: u32,
663         mut vecs: Vec<RutabagaIovec>,
664     ) -> RutabagaResult<()> {
665         let component = self
666             .components
667             .get_mut(&self.default_component)
668             .ok_or(RutabagaError::InvalidComponent)?;
669 
670         let resource = self
671             .resources
672             .get_mut(&resource_id)
673             .ok_or(RutabagaError::InvalidResourceId)?;
674 
675         component.attach_backing(resource_id, &mut vecs)?;
676         resource.backing_iovecs = Some(vecs);
677         Ok(())
678     }
679 
680     /// Detaches any previously attached iovecs from the resource.
detach_backing(&mut self, resource_id: u32) -> RutabagaResult<()>681     pub fn detach_backing(&mut self, resource_id: u32) -> RutabagaResult<()> {
682         let component = self
683             .components
684             .get_mut(&self.default_component)
685             .ok_or(RutabagaError::InvalidComponent)?;
686 
687         let resource = self
688             .resources
689             .get_mut(&resource_id)
690             .ok_or(RutabagaError::InvalidResourceId)?;
691 
692         component.detach_backing(resource_id);
693         resource.backing_iovecs = None;
694         Ok(())
695     }
696 
697     /// Releases guest kernel reference on the resource.
unref_resource(&mut self, resource_id: u32) -> RutabagaResult<()>698     pub fn unref_resource(&mut self, resource_id: u32) -> RutabagaResult<()> {
699         let component = self
700             .components
701             .get_mut(&self.default_component)
702             .ok_or(RutabagaError::InvalidComponent)?;
703 
704         self.resources
705             .remove(&resource_id)
706             .ok_or(RutabagaError::InvalidResourceId)?;
707 
708         component.unref_resource(resource_id);
709         Ok(())
710     }
711 
712     /// For HOST3D_GUEST resources, copies from the attached iovecs to the host resource.  For
713     /// HOST3D resources, this may flush caches, though this feature is unused by guest userspace.
transfer_write( &mut self, ctx_id: u32, resource_id: u32, transfer: Transfer3D, ) -> RutabagaResult<()>714     pub fn transfer_write(
715         &mut self,
716         ctx_id: u32,
717         resource_id: u32,
718         transfer: Transfer3D,
719     ) -> RutabagaResult<()> {
720         let component = self
721             .components
722             .get(&self.default_component)
723             .ok_or(RutabagaError::InvalidComponent)?;
724 
725         let resource = self
726             .resources
727             .get_mut(&resource_id)
728             .ok_or(RutabagaError::InvalidResourceId)?;
729 
730         component.transfer_write(ctx_id, resource, transfer)
731     }
732 
733     /// 1) If specified, copies to `buf` from the host resource.
734     /// 2) Otherwise, for HOST3D_GUEST resources, copies to the attached iovecs from the host
735     ///    resource.  For HOST3D resources, this may invalidate caches, though this feature is
736     ///    unused by guest userspace.
transfer_read( &mut self, ctx_id: u32, resource_id: u32, transfer: Transfer3D, buf: Option<IoSliceMut>, ) -> RutabagaResult<()>737     pub fn transfer_read(
738         &mut self,
739         ctx_id: u32,
740         resource_id: u32,
741         transfer: Transfer3D,
742         buf: Option<IoSliceMut>,
743     ) -> RutabagaResult<()> {
744         let component = self
745             .components
746             .get(&self.default_component)
747             .ok_or(RutabagaError::InvalidComponent)?;
748 
749         let resource = self
750             .resources
751             .get_mut(&resource_id)
752             .ok_or(RutabagaError::InvalidResourceId)?;
753 
754         component.transfer_read(ctx_id, resource, transfer, buf)
755     }
756 
resource_flush(&mut self, resource_id: u32) -> RutabagaResult<()>757     pub fn resource_flush(&mut self, resource_id: u32) -> RutabagaResult<()> {
758         let component = self
759             .components
760             .get(&self.default_component)
761             .ok_or(RutabagaError::Unsupported)?;
762 
763         let resource = self
764             .resources
765             .get_mut(&resource_id)
766             .ok_or(RutabagaError::InvalidResourceId)?;
767 
768         component.resource_flush(resource)
769     }
770 
771     /// Creates a blob resource with the `ctx_id` and `resource_create_blob` metadata.
772     /// Associates `iovecs` with the resource, if there are any.  Associates externally
773     /// created `handle` with the resource, if there is any.
resource_create_blob( &mut self, ctx_id: u32, resource_id: u32, resource_create_blob: ResourceCreateBlob, iovecs: Option<Vec<RutabagaIovec>>, handle: Option<RutabagaHandle>, ) -> RutabagaResult<()>774     pub fn resource_create_blob(
775         &mut self,
776         ctx_id: u32,
777         resource_id: u32,
778         resource_create_blob: ResourceCreateBlob,
779         iovecs: Option<Vec<RutabagaIovec>>,
780         handle: Option<RutabagaHandle>,
781     ) -> RutabagaResult<()> {
782         if self.resources.contains_key(&resource_id) {
783             return Err(RutabagaError::InvalidResourceId);
784         }
785 
786         let component = self
787             .components
788             .get_mut(&self.default_component)
789             .ok_or(RutabagaError::InvalidComponent)?;
790 
791         let mut context = None;
792         // For the cross-domain context, we'll need to create the blob resource via a home-grown
793         // rutabaga context rather than one from an external C/C++ component.  Use `ctx_id` and
794         // the component type if it happens to be a cross-domain context.
795         if ctx_id > 0 {
796             let ctx = self
797                 .contexts
798                 .get_mut(&ctx_id)
799                 .ok_or(RutabagaError::InvalidContextId)?;
800 
801             if ctx.component_type() == RutabagaComponentType::CrossDomain {
802                 context = Some(ctx);
803             }
804         }
805 
806         let resource = match context {
807             Some(ctx) => ctx.context_create_blob(resource_id, resource_create_blob, handle)?,
808             None => {
809                 component.create_blob(ctx_id, resource_id, resource_create_blob, iovecs, handle)?
810             }
811         };
812 
813         self.resources.insert(resource_id, resource);
814         Ok(())
815     }
816 
817     /// Returns a memory mapping of the blob resource.
map(&mut self, resource_id: u32) -> RutabagaResult<RutabagaMapping>818     pub fn map(&mut self, resource_id: u32) -> RutabagaResult<RutabagaMapping> {
819         let resource = self
820             .resources
821             .get_mut(&resource_id)
822             .ok_or(RutabagaError::InvalidResourceId)?;
823 
824         let component_type = calculate_component(resource.component_mask)?;
825         if component_type == RutabagaComponentType::CrossDomain {
826             let handle_opt = resource.handle.take();
827             match handle_opt {
828                 Some(handle) => {
829                     if handle.handle_type != RUTABAGA_MEM_HANDLE_TYPE_SHM {
830                         return Err(RutabagaError::SpecViolation(
831                             "expected a shared memory handle",
832                         ));
833                     }
834 
835                     let clone = handle.try_clone()?;
836                     let resource_size: usize = resource.size.try_into()?;
837                     let map_info = resource
838                         .map_info
839                         .ok_or(RutabagaError::SpecViolation("no map info available"))?;
840 
841                     // Creating the mapping closes the cloned descriptor.
842                     let mapping = MemoryMapping::from_safe_descriptor(
843                         clone.os_handle,
844                         resource_size,
845                         map_info,
846                     )?;
847                     let rutabaga_mapping = mapping.as_rutabaga_mapping();
848                     resource.handle = Some(handle);
849                     resource.mapping = Some(mapping);
850 
851                     return Ok(rutabaga_mapping);
852                 }
853                 None => return Err(RutabagaError::SpecViolation("expected a handle to map")),
854             }
855         }
856 
857         let component = self
858             .components
859             .get(&component_type)
860             .ok_or(RutabagaError::InvalidComponent)?;
861 
862         component.map(resource_id)
863     }
864 
865     /// Unmaps the blob resource from the default component
unmap(&mut self, resource_id: u32) -> RutabagaResult<()>866     pub fn unmap(&mut self, resource_id: u32) -> RutabagaResult<()> {
867         let resource = self
868             .resources
869             .get_mut(&resource_id)
870             .ok_or(RutabagaError::InvalidResourceId)?;
871 
872         let component_type = calculate_component(resource.component_mask)?;
873         if component_type == RutabagaComponentType::CrossDomain {
874             resource.mapping = None;
875             return Ok(());
876         }
877 
878         let component = self
879             .components
880             .get(&component_type)
881             .ok_or(RutabagaError::InvalidComponent)?;
882 
883         component.unmap(resource_id)
884     }
885 
886     /// Returns the `map_info` of the blob resource. The valid values for `map_info`
887     /// are defined in the virtio-gpu spec.
map_info(&self, resource_id: u32) -> RutabagaResult<u32>888     pub fn map_info(&self, resource_id: u32) -> RutabagaResult<u32> {
889         let resource = self
890             .resources
891             .get(&resource_id)
892             .ok_or(RutabagaError::InvalidResourceId)?;
893 
894         resource
895             .map_info
896             .ok_or(RutabagaError::SpecViolation("no map info available"))
897     }
898 
899     /// Returns the `vulkan_info` of the blob resource, which consists of the physical device
900     /// index and memory index associated with the resource.
vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo>901     pub fn vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo> {
902         let resource = self
903             .resources
904             .get(&resource_id)
905             .ok_or(RutabagaError::InvalidResourceId)?;
906 
907         resource.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo)
908     }
909 
910     /// Returns the 3D info associated with the resource, if any.
query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo>911     pub fn query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo> {
912         let resource = self
913             .resources
914             .get(&resource_id)
915             .ok_or(RutabagaError::InvalidResourceId)?;
916 
917         resource
918             .info_3d
919             .ok_or(RutabagaError::SpecViolation("no 3d info available"))
920     }
921 
922     /// Exports a blob resource.  See virtio-gpu spec for blob flag use flags.
export_blob(&mut self, resource_id: u32) -> RutabagaResult<RutabagaHandle>923     pub fn export_blob(&mut self, resource_id: u32) -> RutabagaResult<RutabagaHandle> {
924         let resource = self
925             .resources
926             .get_mut(&resource_id)
927             .ok_or(RutabagaError::InvalidResourceId)?;
928 
929         // We can inspect blob flags only once guest minigbm is fully transitioned to blob.
930         let share_mask = RUTABAGA_BLOB_FLAG_USE_SHAREABLE | RUTABAGA_BLOB_FLAG_USE_CROSS_DEVICE;
931         let shareable = (resource.blob_flags & share_mask != 0) || !resource.blob;
932 
933         let opt = resource.handle.take();
934 
935         match (opt, shareable) {
936             (Some(handle), true) => {
937                 let clone = handle.try_clone()?;
938                 resource.handle = Some(handle);
939                 Ok(clone)
940             }
941             (Some(handle), false) => {
942                 // Exactly one strong reference in this case.
943                 let hnd =
944                     Arc::try_unwrap(handle).map_err(|_| RutabagaError::InvalidRutabagaHandle)?;
945                 Ok(hnd)
946             }
947             _ => Err(RutabagaError::InvalidRutabagaHandle),
948         }
949     }
950 
951     /// Exports the given fence for import into other processes.
export_fence(&mut self, fence_id: u64) -> RutabagaResult<RutabagaHandle>952     pub fn export_fence(&mut self, fence_id: u64) -> RutabagaResult<RutabagaHandle> {
953         #[cfg(fence_passing_option1)]
954         if let Some(handle) = self.shareable_fences.get_mut(&fence_id) {
955             return handle.try_clone();
956         }
957 
958         let component = self
959             .components
960             .get(&self.default_component)
961             .ok_or(RutabagaError::InvalidComponent)?;
962 
963         component.export_fence(fence_id)
964     }
965 
966     /// Creates snapshotth the given `ctx_id` and `context_init` variable.
967     /// `context_init` is used to determine which rutabaga component creates the context.
create_context( &mut self, ctx_id: u32, context_init: u32, context_name: Option<&str>, ) -> RutabagaResult<()>968     pub fn create_context(
969         &mut self,
970         ctx_id: u32,
971         context_init: u32,
972         context_name: Option<&str>,
973     ) -> RutabagaResult<()> {
974         // The default workaround is just until context types are fully supported in all
975         // Google kernels.
976         let capset_id = context_init & RUTABAGA_CONTEXT_INIT_CAPSET_ID_MASK;
977         let component_type = self
978             .capset_id_to_component_type(capset_id)
979             .unwrap_or(self.default_component);
980 
981         let component = self
982             .components
983             .get_mut(&component_type)
984             .ok_or(RutabagaError::InvalidComponent)?;
985 
986         if self.contexts.contains_key(&ctx_id) {
987             return Err(RutabagaError::InvalidContextId);
988         }
989 
990         let ctx = component.create_context(
991             ctx_id,
992             context_init,
993             context_name,
994             self.fence_handler.clone(),
995         )?;
996         self.contexts.insert(ctx_id, ctx);
997         Ok(())
998     }
999 
1000     /// Destroys the context given by `ctx_id`.
destroy_context(&mut self, ctx_id: u32) -> RutabagaResult<()>1001     pub fn destroy_context(&mut self, ctx_id: u32) -> RutabagaResult<()> {
1002         self.contexts
1003             .remove(&ctx_id)
1004             .ok_or(RutabagaError::InvalidContextId)?;
1005         Ok(())
1006     }
1007 
1008     /// Attaches the resource given by `resource_id` to the context given by `ctx_id`.
context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()>1009     pub fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> {
1010         let ctx = self
1011             .contexts
1012             .get_mut(&ctx_id)
1013             .ok_or(RutabagaError::InvalidContextId)?;
1014 
1015         let resource = self
1016             .resources
1017             .get_mut(&resource_id)
1018             .ok_or(RutabagaError::InvalidResourceId)?;
1019 
1020         ctx.attach(resource);
1021         Ok(())
1022     }
1023 
1024     /// Detaches the resource given by `resource_id` from the context given by `ctx_id`.
context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()>1025     pub fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> {
1026         let ctx = self
1027             .contexts
1028             .get_mut(&ctx_id)
1029             .ok_or(RutabagaError::InvalidContextId)?;
1030 
1031         let resource = self
1032             .resources
1033             .get_mut(&resource_id)
1034             .ok_or(RutabagaError::InvalidResourceId)?;
1035 
1036         ctx.detach(resource);
1037         Ok(())
1038     }
1039 
1040     /// submits `commands` to the context given by `ctx_id`.
submit_command( &mut self, ctx_id: u32, commands: &mut [u8], fence_ids: &[u64], ) -> RutabagaResult<()>1041     pub fn submit_command(
1042         &mut self,
1043         ctx_id: u32,
1044         commands: &mut [u8],
1045         fence_ids: &[u64],
1046     ) -> RutabagaResult<()> {
1047         let ctx = self
1048             .contexts
1049             .get_mut(&ctx_id)
1050             .ok_or(RutabagaError::InvalidContextId)?;
1051 
1052         #[allow(unused_mut)]
1053         let mut shareable_fences: Vec<RutabagaHandle> = Vec::with_capacity(fence_ids.len());
1054 
1055         #[cfg(fence_passing_option1)]
1056         for (i, fence_id) in fence_ids.iter().enumerate() {
1057             let handle = self
1058                 .shareable_fences
1059                 .get_mut(fence_id)
1060                 .ok_or(RutabagaError::InvalidRutabagaHandle)?;
1061 
1062             let clone = handle.try_clone()?;
1063             shareable_fences.insert(i, clone);
1064         }
1065 
1066         ctx.submit_cmd(commands, fence_ids, shareable_fences)
1067     }
1068 
1069     /// destroy fences that are still outstanding
1070     #[cfg(fence_passing_option1)]
destroy_fences(&mut self, fence_ids: &[u64]) -> RutabagaResult<()>1071     pub fn destroy_fences(&mut self, fence_ids: &[u64]) -> RutabagaResult<()> {
1072         for fence_id in fence_ids {
1073             self.shareable_fences
1074                 .remove(fence_id)
1075                 .ok_or(RutabagaError::InvalidRutabagaHandle)?;
1076         }
1077 
1078         Ok(())
1079     }
1080 
1081     /// Performs a blocking wait-sync for all pending operations on the resource identified by
1082     /// resource_id
wait_sync(&mut self, resource_id: u32) -> RutabagaResult<()>1083     pub fn wait_sync(&mut self, resource_id: u32) -> RutabagaResult<()> {
1084         let component = self
1085             .components
1086             .get_mut(&self.default_component)
1087             .ok_or(RutabagaError::InvalidComponent)?;
1088 
1089         let resource = self
1090             .resources
1091             .get(&resource_id)
1092             .ok_or(RutabagaError::InvalidResourceId)?;
1093 
1094         component.wait_sync(resource)?;
1095         Ok(())
1096     }
1097 }
1098 
1099 /// Rutabaga Builder, following the Rust builder pattern.
1100 #[derive(Clone)]
1101 pub struct RutabagaBuilder {
1102     display_width: u32,
1103     display_height: u32,
1104     default_component: RutabagaComponentType,
1105     gfxstream_flags: GfxstreamFlags,
1106     virglrenderer_flags: VirglRendererFlags,
1107     capset_mask: u64,
1108     channels: Option<Vec<RutabagaChannel>>,
1109     debug_handler: Option<RutabagaDebugHandler>,
1110     renderer_features: Option<String>,
1111 }
1112 
1113 impl RutabagaBuilder {
1114     /// Create new a RutabagaBuilder.
new(default_component: RutabagaComponentType, capset_mask: u64) -> RutabagaBuilder1115     pub fn new(default_component: RutabagaComponentType, capset_mask: u64) -> RutabagaBuilder {
1116         let virglrenderer_flags = VirglRendererFlags::new()
1117             .use_thread_sync(true)
1118             .use_async_fence_cb(true);
1119         let gfxstream_flags = GfxstreamFlags::new();
1120         RutabagaBuilder {
1121             display_width: RUTABAGA_DEFAULT_WIDTH,
1122             display_height: RUTABAGA_DEFAULT_HEIGHT,
1123             default_component,
1124             gfxstream_flags,
1125             virglrenderer_flags,
1126             capset_mask,
1127             channels: None,
1128             debug_handler: None,
1129             renderer_features: None,
1130         }
1131     }
1132 
1133     /// Set display width for the RutabagaBuilder
set_display_width(mut self, display_width: u32) -> RutabagaBuilder1134     pub fn set_display_width(mut self, display_width: u32) -> RutabagaBuilder {
1135         self.display_width = display_width;
1136         self
1137     }
1138 
1139     /// Set display height for the RutabagaBuilder
set_display_height(mut self, display_height: u32) -> RutabagaBuilder1140     pub fn set_display_height(mut self, display_height: u32) -> RutabagaBuilder {
1141         self.display_height = display_height;
1142         self
1143     }
1144 
1145     /// Sets use EGL flags in gfxstream + virglrenderer.
set_use_egl(mut self, v: bool) -> RutabagaBuilder1146     pub fn set_use_egl(mut self, v: bool) -> RutabagaBuilder {
1147         self.gfxstream_flags = self.gfxstream_flags.use_egl(v);
1148         self.virglrenderer_flags = self.virglrenderer_flags.use_egl(v);
1149         self
1150     }
1151 
1152     /// Sets use GLES in gfxstream + virglrenderer.
set_use_gles(mut self, v: bool) -> RutabagaBuilder1153     pub fn set_use_gles(mut self, v: bool) -> RutabagaBuilder {
1154         self.gfxstream_flags = self.gfxstream_flags.use_gles(v);
1155         self.virglrenderer_flags = self.virglrenderer_flags.use_gles(v);
1156         self
1157     }
1158 
1159     /// Sets use GLX flags in gfxstream + virglrenderer.
set_use_glx(mut self, v: bool) -> RutabagaBuilder1160     pub fn set_use_glx(mut self, v: bool) -> RutabagaBuilder {
1161         self.gfxstream_flags = self.gfxstream_flags.use_glx(v);
1162         self.virglrenderer_flags = self.virglrenderer_flags.use_glx(v);
1163         self
1164     }
1165 
1166     /// Sets use surfaceless flags in gfxstream + virglrenderer.
set_use_surfaceless(mut self, v: bool) -> RutabagaBuilder1167     pub fn set_use_surfaceless(mut self, v: bool) -> RutabagaBuilder {
1168         self.gfxstream_flags = self.gfxstream_flags.use_surfaceless(v);
1169         self.virglrenderer_flags = self.virglrenderer_flags.use_surfaceless(v);
1170         self
1171     }
1172 
1173     /// Sets use Vulkan in gfxstream + virglrenderer.
set_use_vulkan(mut self, v: bool) -> RutabagaBuilder1174     pub fn set_use_vulkan(mut self, v: bool) -> RutabagaBuilder {
1175         self.gfxstream_flags = self.gfxstream_flags.use_vulkan(v);
1176         self.virglrenderer_flags = self.virglrenderer_flags.use_venus(v);
1177         self
1178     }
1179 
1180     /// Sets use external blob in gfxstream + virglrenderer.
set_use_external_blob(mut self, v: bool) -> RutabagaBuilder1181     pub fn set_use_external_blob(mut self, v: bool) -> RutabagaBuilder {
1182         self.gfxstream_flags = self.gfxstream_flags.use_external_blob(v);
1183         self.virglrenderer_flags = self.virglrenderer_flags.use_external_blob(v);
1184         self
1185     }
1186 
1187     /// Sets use system blob in gfxstream.
set_use_system_blob(mut self, v: bool) -> RutabagaBuilder1188     pub fn set_use_system_blob(mut self, v: bool) -> RutabagaBuilder {
1189         self.gfxstream_flags = self.gfxstream_flags.use_system_blob(v);
1190         self
1191     }
1192 
1193     /// Sets use render server in virglrenderer.
set_use_render_server(mut self, v: bool) -> RutabagaBuilder1194     pub fn set_use_render_server(mut self, v: bool) -> RutabagaBuilder {
1195         self.virglrenderer_flags = self.virglrenderer_flags.use_render_server(v);
1196         self
1197     }
1198 
1199     /// Use the Vulkan swapchain to draw on the host window for gfxstream.
set_wsi(mut self, v: RutabagaWsi) -> RutabagaBuilder1200     pub fn set_wsi(mut self, v: RutabagaWsi) -> RutabagaBuilder {
1201         self.gfxstream_flags = self.gfxstream_flags.set_wsi(v);
1202         self
1203     }
1204 
1205     /// Set rutabaga channels for the RutabagaBuilder
set_rutabaga_channels( mut self, channels: Option<Vec<RutabagaChannel>>, ) -> RutabagaBuilder1206     pub fn set_rutabaga_channels(
1207         mut self,
1208         channels: Option<Vec<RutabagaChannel>>,
1209     ) -> RutabagaBuilder {
1210         self.channels = channels;
1211         self
1212     }
1213 
1214     /// Set debug handler for the RutabagaBuilder
set_debug_handler( mut self, debug_handler: Option<RutabagaDebugHandler>, ) -> RutabagaBuilder1215     pub fn set_debug_handler(
1216         mut self,
1217         debug_handler: Option<RutabagaDebugHandler>,
1218     ) -> RutabagaBuilder {
1219         self.debug_handler = debug_handler;
1220         self
1221     }
1222 
1223     /// Set renderer features for the RutabagaBuilder
set_renderer_features(mut self, renderer_features: Option<String>) -> RutabagaBuilder1224     pub fn set_renderer_features(mut self, renderer_features: Option<String>) -> RutabagaBuilder {
1225         self.renderer_features = renderer_features;
1226         self
1227     }
1228 
1229     /// Builds Rutabaga and returns a handle to it.
1230     ///
1231     /// This should be only called once per every virtual machine instance.  Rutabaga tries to
1232     /// intialize all 3D components which have been built. In 2D mode, only the 2D component is
1233     /// initialized.
build( mut self, fence_handler: RutabagaFenceHandler, #[allow(unused_variables)] rutabaga_server_descriptor: Option<OwnedDescriptor>, ) -> RutabagaResult<Rutabaga>1234     pub fn build(
1235         mut self,
1236         fence_handler: RutabagaFenceHandler,
1237         #[allow(unused_variables)] rutabaga_server_descriptor: Option<OwnedDescriptor>,
1238     ) -> RutabagaResult<Rutabaga> {
1239         let mut rutabaga_components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>> =
1240             Default::default();
1241 
1242         #[allow(unused_mut)]
1243         let mut rutabaga_capsets: Vec<RutabagaCapsetInfo> = Default::default();
1244 
1245         let capset_enabled =
1246             |capset_id: u32| -> bool { (self.capset_mask & (1 << capset_id)) != 0 };
1247 
1248         let mut push_capset = |capset_id: u32| {
1249             if let Some(capset) = RUTABAGA_CAPSETS
1250                 .iter()
1251                 .find(|capset| capset_id == capset.capset_id)
1252             {
1253                 if self.capset_mask != 0 {
1254                     if capset_enabled(capset.capset_id) {
1255                         rutabaga_capsets.push(*capset);
1256                     }
1257                 } else {
1258                     // Unconditionally push capset -- this should eventually be deleted when context
1259                     // types are always specified by crosvm launchers.
1260                     rutabaga_capsets.push(*capset);
1261                 }
1262             };
1263         };
1264 
1265         if self.capset_mask != 0 {
1266             let supports_gfxstream = capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_VULKAN)
1267                 | capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_MAGMA)
1268                 | capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_GLES)
1269                 | capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_COMPOSER);
1270             let supports_virglrenderer = capset_enabled(RUTABAGA_CAPSET_VIRGL2)
1271                 | capset_enabled(RUTABAGA_CAPSET_VENUS)
1272                 | capset_enabled(RUTABAGA_CAPSET_DRM);
1273 
1274             if supports_gfxstream {
1275                 self.default_component = RutabagaComponentType::Gfxstream;
1276             } else if supports_virglrenderer {
1277                 self.default_component = RutabagaComponentType::VirglRenderer;
1278             } else {
1279                 self.default_component = RutabagaComponentType::CrossDomain;
1280             }
1281 
1282             self.virglrenderer_flags = self
1283                 .virglrenderer_flags
1284                 .use_virgl(capset_enabled(RUTABAGA_CAPSET_VIRGL2))
1285                 .use_venus(capset_enabled(RUTABAGA_CAPSET_VENUS))
1286                 .use_drm(capset_enabled(RUTABAGA_CAPSET_DRM));
1287 
1288             self.gfxstream_flags = self
1289                 .gfxstream_flags
1290                 .use_gles(capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_GLES))
1291                 .use_vulkan(capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_VULKAN))
1292         }
1293 
1294         // Make sure that disabled components are not used as default.
1295         #[cfg(not(feature = "virgl_renderer"))]
1296         if self.default_component == RutabagaComponentType::VirglRenderer {
1297             return Err(RutabagaError::InvalidRutabagaBuild(
1298                 "virgl renderer feature not enabled",
1299             ));
1300         }
1301         #[cfg(not(feature = "gfxstream"))]
1302         if self.default_component == RutabagaComponentType::Gfxstream {
1303             return Err(RutabagaError::InvalidRutabagaBuild(
1304                 "gfxstream feature not enabled",
1305             ));
1306         }
1307 
1308         if self.default_component != RutabagaComponentType::Rutabaga2D {
1309             #[cfg(feature = "virgl_renderer")]
1310             if self.default_component == RutabagaComponentType::VirglRenderer {
1311                 if let Ok(virgl) = VirglRenderer::init(
1312                     self.virglrenderer_flags,
1313                     fence_handler.clone(),
1314                     rutabaga_server_descriptor,
1315                 ) {
1316                     rutabaga_components.insert(RutabagaComponentType::VirglRenderer, virgl);
1317 
1318                     push_capset(RUTABAGA_CAPSET_VIRGL);
1319                     push_capset(RUTABAGA_CAPSET_VIRGL2);
1320                     push_capset(RUTABAGA_CAPSET_VENUS);
1321                     push_capset(RUTABAGA_CAPSET_DRM);
1322                 } else {
1323                     log::warn!("error initializing gpu backend=virglrenderer, falling back to 2d.");
1324                     self.default_component = RutabagaComponentType::Rutabaga2D;
1325                 };
1326             }
1327 
1328             #[cfg(feature = "gfxstream")]
1329             if self.default_component == RutabagaComponentType::Gfxstream {
1330                 let gfxstream = Gfxstream::init(
1331                     self.display_width,
1332                     self.display_height,
1333                     self.gfxstream_flags,
1334                     self.renderer_features,
1335                     fence_handler.clone(),
1336                     self.debug_handler.clone(),
1337                 )?;
1338 
1339                 rutabaga_components.insert(RutabagaComponentType::Gfxstream, gfxstream);
1340 
1341                 push_capset(RUTABAGA_CAPSET_GFXSTREAM_VULKAN);
1342                 push_capset(RUTABAGA_CAPSET_GFXSTREAM_MAGMA);
1343                 push_capset(RUTABAGA_CAPSET_GFXSTREAM_GLES);
1344                 push_capset(RUTABAGA_CAPSET_GFXSTREAM_COMPOSER);
1345             }
1346 
1347             let cross_domain = CrossDomain::init(self.channels, fence_handler.clone())?;
1348             rutabaga_components.insert(RutabagaComponentType::CrossDomain, cross_domain);
1349             push_capset(RUTABAGA_CAPSET_CROSS_DOMAIN);
1350         }
1351 
1352         if self.default_component == RutabagaComponentType::Rutabaga2D {
1353             let rutabaga_2d = Rutabaga2D::init(fence_handler.clone())?;
1354             rutabaga_components.insert(RutabagaComponentType::Rutabaga2D, rutabaga_2d);
1355         }
1356 
1357         Ok(Rutabaga {
1358             resources: Default::default(),
1359             #[cfg(fence_passing_option1)]
1360             shareable_fences: Default::default(),
1361             contexts: Default::default(),
1362             components: rutabaga_components,
1363             default_component: self.default_component,
1364             capset_info: rutabaga_capsets,
1365             fence_handler,
1366         })
1367     }
1368 }
1369 
1370 #[cfg(test)]
1371 mod tests {
1372     use crate::*;
1373 
new_2d() -> Rutabaga1374     fn new_2d() -> Rutabaga {
1375         RutabagaBuilder::new(RutabagaComponentType::Rutabaga2D, 0)
1376             .build(RutabagaHandler::new(|_| {}), None)
1377             .unwrap()
1378     }
1379 
1380     #[test]
snapshot_restore_2d_no_resources()1381     fn snapshot_restore_2d_no_resources() {
1382         let mut buffer = std::io::Cursor::new(Vec::new());
1383 
1384         let rutabaga1 = new_2d();
1385         rutabaga1.snapshot(&mut buffer, "").unwrap();
1386 
1387         let mut rutabaga1 = new_2d();
1388         rutabaga1.restore(&mut &buffer.get_ref()[..], "").unwrap();
1389     }
1390 
1391     #[test]
snapshot_restore_2d_one_resource()1392     fn snapshot_restore_2d_one_resource() {
1393         let resource_id = 123;
1394         let resource_create_3d = ResourceCreate3D {
1395             target: RUTABAGA_PIPE_TEXTURE_2D,
1396             format: 1,
1397             bind: RUTABAGA_PIPE_BIND_RENDER_TARGET,
1398             width: 100,
1399             height: 200,
1400             depth: 1,
1401             array_size: 1,
1402             last_level: 0,
1403             nr_samples: 0,
1404             flags: 0,
1405         };
1406 
1407         let mut buffer = std::io::Cursor::new(Vec::new());
1408 
1409         let mut rutabaga1 = new_2d();
1410         rutabaga1
1411             .resource_create_3d(resource_id, resource_create_3d)
1412             .unwrap();
1413         rutabaga1
1414             .attach_backing(
1415                 resource_id,
1416                 vec![RutabagaIovec {
1417                     base: std::ptr::null_mut(),
1418                     len: 456,
1419                 }],
1420             )
1421             .unwrap();
1422         rutabaga1.snapshot(&mut buffer, "").unwrap();
1423 
1424         let mut rutabaga2 = new_2d();
1425         rutabaga2.restore(&mut &buffer.get_ref()[..], "").unwrap();
1426 
1427         assert_eq!(rutabaga2.resources.len(), 1);
1428         let rutabaga_resource = rutabaga2.resources.get(&resource_id).unwrap();
1429         assert_eq!(rutabaga_resource.resource_id, resource_id);
1430         assert_eq!(
1431             rutabaga_resource.info_2d.as_ref().unwrap().width,
1432             resource_create_3d.width
1433         );
1434         assert_eq!(
1435             rutabaga_resource.info_2d.as_ref().unwrap().height,
1436             resource_create_3d.height
1437         );
1438         // NOTE: We attached an backing iovec, but it should be gone post-restore.
1439         assert!(rutabaga_resource.backing_iovecs.is_none());
1440     }
1441 }
1442