Skip to main content

rutabaga_gfx/
rutabaga_core.rs

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