1use 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#[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 }
83
84pub 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 pub component_mask: u8,
98 pub size: u64,
99 pub mapping: Option<MemoryMapping>,
100 pub guest_cpu_mappable: bool,
101}
102
103#[derive(Deserialize, Serialize)]
106struct RutabagaResourceSnapshot {
107 resource_id: u32,
108 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 component_mask: u8,
125 size: u64,
126 }
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
183pub trait RutabagaComponent {
191 fn get_capset_info(&self, _capset_id: u32) -> (u32, u32) {
194 (0, 0)
195 }
196
197 fn get_capset(&self, _capset_id: u32, _version: u32) -> Vec<u8> {
200 Vec::new()
201 }
202
203 fn force_ctx_0(&self) {}
205
206 fn create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()> {
209 Ok(())
210 }
211
212 fn event_poll(&self) {}
214
215 fn poll_descriptor(&self) -> Option<OwnedDescriptor> {
218 None
219 }
220
221 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 fn attach_backing(
258 &self,
259 _resource_id: u32,
260 _vecs: &mut Vec<RutabagaIovec>,
261 ) -> RutabagaResult<()> {
262 Ok(())
263 }
264
265 fn detach_backing(&self, _resource_id: u32) {}
267
268 fn unref_resource(&self, _resource_id: u32) {}
270
271 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 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 fn resource_flush(&self, _resource_id: &mut RutabagaResource) -> RutabagaResult<()> {
297 Err(MesaError::Unsupported.into())
298 }
299
300 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 fn map(&self, _resource_id: u32) -> RutabagaResult<MesaMapping> {
316 Err(MesaError::Unsupported.into())
317 }
318
319 fn unmap(&self, _resource_id: u32) -> RutabagaResult<()> {
322 Err(MesaError::Unsupported.into())
323 }
324
325 fn export_fence(&self, _fence_id: u64) -> RutabagaResult<MesaHandle> {
327 Err(MesaError::Unsupported.into())
328 }
329
330 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 fn suspend(&self) -> RutabagaResult<()> {
345 Ok(())
346 }
347
348 fn snapshot(&self, _writer: RutabagaSnapshotWriter) -> RutabagaResult<()> {
350 Err(MesaError::Unsupported.into())
351 }
352
353 fn restore(&self, _reader: RutabagaSnapshotReader) -> RutabagaResult<()> {
355 Err(MesaError::Unsupported.into())
356 }
357
358 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 fn resume(&self) -> RutabagaResult<()> {
369 Ok(())
370 }
371}
372
373pub trait RutabagaContext {
374 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 fn submit_cmd(
386 &mut self,
387 _commands: &mut [u8],
388 _fence_ids: &[u64],
389 shareable_fences: Vec<MesaHandle>,
390 ) -> RutabagaResult<()>;
391
392 fn attach(&mut self, _resource: &mut RutabagaResource);
394
395 fn detach(&mut self, _resource: &RutabagaResource);
397
398 fn context_create_fence(
404 &mut self,
405 _fence: RutabagaFence,
406 ) -> RutabagaResult<Option<MesaHandle>> {
407 Err(MesaError::Unsupported.into())
408 }
409
410 fn component_type(&self) -> RutabagaComponentType;
412
413 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
508pub 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 components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>>,
522 default_component: RutabagaComponentType,
523 capset_info: Vec<RutabagaCapsetInfo>,
524 fence_handler: RutabagaFenceHandler,
525}
526
527#[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 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 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 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 pub fn get_capset(&self, capset_id: u32, version: u32) -> RutabagaResult<Vec<u8>> {
682 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 pub fn get_num_capsets(&self) -> u32 {
698 self.capset_info.len() as u32
699 }
700
701 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 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 pub fn event_poll(&self) {
740 if let Some(component) = self.components.get(&self.default_component) {
741 component.event_poll();
742 }
743 }
744
745 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 let hnd = Arc::try_unwrap(handle).map_err(|_| MesaError::InvalidMesaHandle)?;
1121 Ok(hnd)
1122 }
1123 _ => Err(MesaError::InvalidMesaHandle.into()),
1124 }
1125 }
1126
1127 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 pub fn create_context(
1145 &mut self,
1146 ctx_id: u32,
1147 context_init: u32,
1148 context_name: Option<&str>,
1149 ) -> RutabagaResult<()> {
1150 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 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 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 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 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 #[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
1258pub 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 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 pub fn set_display_width(mut self, display_width: u32) -> RutabagaBuilder {
1297 self.display_width = display_width;
1298 self
1299 }
1300
1301 pub fn set_display_height(mut self, display_height: u32) -> RutabagaBuilder {
1303 self.display_height = display_height;
1304 self
1305 }
1306
1307 pub fn set_default_component(mut self, component: RutabagaComponentType) -> RutabagaBuilder {
1309 self.default_component = component;
1310 self
1311 }
1312
1313 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 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 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 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 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 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 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 pub fn set_wsi(mut self, v: RutabagaWsi) -> RutabagaBuilder {
1362 self.gfxstream_flags = self.gfxstream_flags.set_wsi(v);
1363 self
1364 }
1365
1366 pub fn set_rutabaga_paths(mut self, paths: Option<Vec<RutabagaPath>>) -> RutabagaBuilder {
1368 self.paths = paths;
1369 self
1370 }
1371
1372 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 pub fn set_renderer_features(mut self, renderer_features: Option<String>) -> RutabagaBuilder {
1383 self.renderer_features = renderer_features;
1384 self
1385 }
1386
1387 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 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 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 #[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 assert!(rutabaga_resource.backing_iovecs.is_none());
1612
1613 fs::remove_dir_all(&snapshot_dir).unwrap();
1614 }
1615}