1use std::collections::BTreeMap as Map;
7use std::convert::TryInto;
8use std::io::IoSliceMut;
9use std::io::Read;
10use std::io::Write;
11use std::sync::Arc;
12
13use crate::cross_domain::CrossDomain;
14#[cfg(feature = "gfxstream")]
15use crate::gfxstream::Gfxstream;
16use crate::rutabaga_2d::Rutabaga2D;
17use crate::rutabaga_os::MemoryMapping;
18use crate::rutabaga_os::SafeDescriptor;
19use crate::rutabaga_snapshot::RutabagaResourceSnapshot;
20use crate::rutabaga_snapshot::RutabagaSnapshot;
21use crate::rutabaga_utils::*;
22#[cfg(feature = "virgl_renderer")]
23use crate::virgl_renderer::VirglRenderer;
24
25const RUTABAGA_DEFAULT_WIDTH: u32 = 1280;
26const RUTABAGA_DEFAULT_HEIGHT: u32 = 1024;
27
28pub struct Rutabaga2DInfo {
30 pub width: u32,
31 pub height: u32,
32 pub host_mem: Vec<u8>,
33}
34
35pub struct RutabagaResource {
37 pub resource_id: u32,
38 pub handle: Option<Arc<RutabagaHandle>>,
39 pub blob: bool,
40 pub blob_mem: u32,
41 pub blob_flags: u32,
42 pub map_info: Option<u32>,
43 pub info_2d: Option<Rutabaga2DInfo>,
44 pub info_3d: Option<Resource3DInfo>,
45 pub vulkan_info: Option<VulkanInfo>,
46 pub backing_iovecs: Option<Vec<RutabagaIovec>>,
47
48 pub component_mask: u8,
50 pub size: u64,
51 pub mapping: Option<MemoryMapping>,
52}
53
54pub trait RutabagaComponent {
62 fn get_capset_info(&self, _capset_id: u32) -> (u32, u32) {
65 (0, 0)
66 }
67
68 fn get_capset(&self, _capset_id: u32, _version: u32) -> Vec<u8> {
71 Vec::new()
72 }
73
74 fn force_ctx_0(&self) {}
76
77 fn create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()> {
80 Ok(())
81 }
82
83 fn event_poll(&self) {}
85
86 fn poll_descriptor(&self) -> Option<SafeDescriptor> {
89 None
90 }
91
92 fn create_3d(
96 &self,
97 resource_id: u32,
98 _resource_create_3d: ResourceCreate3D,
99 ) -> RutabagaResult<RutabagaResource> {
100 Ok(RutabagaResource {
101 resource_id,
102 handle: None,
103 blob: false,
104 blob_mem: 0,
105 blob_flags: 0,
106 map_info: None,
107 info_2d: None,
108 info_3d: None,
109 vulkan_info: None,
110 backing_iovecs: None,
111 component_mask: 0,
112 size: 0,
113 mapping: None,
114 })
115 }
116
117 fn attach_backing(
119 &self,
120 _resource_id: u32,
121 _vecs: &mut Vec<RutabagaIovec>,
122 ) -> RutabagaResult<()> {
123 Ok(())
124 }
125
126 fn detach_backing(&self, _resource_id: u32) {}
128
129 fn unref_resource(&self, _resource_id: u32) {}
131
132 fn transfer_write(
135 &self,
136 _ctx_id: u32,
137 _resource: &mut RutabagaResource,
138 _transfer: Transfer3D,
139 ) -> RutabagaResult<()> {
140 Ok(())
141 }
142
143 fn transfer_read(
146 &self,
147 _ctx_id: u32,
148 _resource: &mut RutabagaResource,
149 _transfer: Transfer3D,
150 _buf: Option<IoSliceMut>,
151 ) -> RutabagaResult<()> {
152 Ok(())
153 }
154
155 fn resource_flush(&self, _resource_id: &mut RutabagaResource) -> RutabagaResult<()> {
157 Err(RutabagaError::Unsupported)
158 }
159
160 fn create_blob(
163 &mut self,
164 _ctx_id: u32,
165 _resource_id: u32,
166 _resource_create_blob: ResourceCreateBlob,
167 _iovec_opt: Option<Vec<RutabagaIovec>>,
168 _handle_opt: Option<RutabagaHandle>,
169 ) -> RutabagaResult<RutabagaResource> {
170 Err(RutabagaError::Unsupported)
171 }
172
173 fn map(&self, _resource_id: u32) -> RutabagaResult<RutabagaMapping> {
176 Err(RutabagaError::Unsupported)
177 }
178
179 fn unmap(&self, _resource_id: u32) -> RutabagaResult<()> {
182 Err(RutabagaError::Unsupported)
183 }
184
185 fn export_fence(&self, _fence_id: u64) -> RutabagaResult<RutabagaHandle> {
187 Err(RutabagaError::Unsupported)
188 }
189
190 fn create_context(
194 &self,
195 _ctx_id: u32,
196 _context_init: u32,
197 _context_name: Option<&str>,
198 _fence_handler: RutabagaFenceHandler,
199 ) -> RutabagaResult<Box<dyn RutabagaContext>> {
200 Err(RutabagaError::Unsupported)
201 }
202}
203
204pub trait RutabagaContext {
205 fn context_create_blob(
207 &mut self,
208 _resource_id: u32,
209 _resource_create_blob: ResourceCreateBlob,
210 _handle_opt: Option<RutabagaHandle>,
211 ) -> RutabagaResult<RutabagaResource> {
212 Err(RutabagaError::Unsupported)
213 }
214
215 fn submit_cmd(&mut self, _commands: &mut [u8], _fence_ids: &[u64]) -> RutabagaResult<()>;
217
218 fn attach(&mut self, _resource: &mut RutabagaResource);
220
221 fn detach(&mut self, _resource: &RutabagaResource);
223
224 fn context_create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()> {
227 Err(RutabagaError::Unsupported)
228 }
229
230 fn component_type(&self) -> RutabagaComponentType;
232}
233
234#[derive(Copy, Clone)]
235struct RutabagaCapsetInfo {
236 pub capset_id: u32,
237 pub component: RutabagaComponentType,
238 pub name: &'static str,
239}
240
241const RUTABAGA_CAPSETS: [RutabagaCapsetInfo; 9] = [
242 RutabagaCapsetInfo {
243 capset_id: RUTABAGA_CAPSET_VIRGL,
244 component: RutabagaComponentType::VirglRenderer,
245 name: "virgl",
246 },
247 RutabagaCapsetInfo {
248 capset_id: RUTABAGA_CAPSET_VIRGL2,
249 component: RutabagaComponentType::VirglRenderer,
250 name: "virgl2",
251 },
252 RutabagaCapsetInfo {
253 capset_id: RUTABAGA_CAPSET_GFXSTREAM_VULKAN,
254 component: RutabagaComponentType::Gfxstream,
255 name: "gfxstream-vulkan",
256 },
257 RutabagaCapsetInfo {
258 capset_id: RUTABAGA_CAPSET_VENUS,
259 component: RutabagaComponentType::VirglRenderer,
260 name: "venus",
261 },
262 RutabagaCapsetInfo {
263 capset_id: RUTABAGA_CAPSET_CROSS_DOMAIN,
264 component: RutabagaComponentType::CrossDomain,
265 name: "cross-domain",
266 },
267 RutabagaCapsetInfo {
268 capset_id: RUTABAGA_CAPSET_DRM,
269 component: RutabagaComponentType::VirglRenderer,
270 name: "drm",
271 },
272 RutabagaCapsetInfo {
273 capset_id: RUTABAGA_CAPSET_GFXSTREAM_MAGMA,
274 component: RutabagaComponentType::Gfxstream,
275 name: "gfxstream-magma",
276 },
277 RutabagaCapsetInfo {
278 capset_id: RUTABAGA_CAPSET_GFXSTREAM_GLES,
279 component: RutabagaComponentType::Gfxstream,
280 name: "gfxstream-gles",
281 },
282 RutabagaCapsetInfo {
283 capset_id: RUTABAGA_CAPSET_GFXSTREAM_COMPOSER,
284 component: RutabagaComponentType::Gfxstream,
285 name: "gfxstream-composer",
286 },
287];
288
289pub fn calculate_capset_mask<'a, I: Iterator<Item = &'a str>>(context_names: I) -> u64 {
290 let mut capset_mask = 0;
291 for name in context_names {
292 if let Some(capset) = RUTABAGA_CAPSETS.iter().find(|capset| capset.name == name) {
293 capset_mask |= 1 << capset.capset_id;
294 };
295 }
296
297 capset_mask
298}
299
300pub fn calculate_capset_names(capset_mask: u64) -> Vec<String> {
301 RUTABAGA_CAPSETS
302 .iter()
303 .filter(|capset| capset_mask & (1 << capset.capset_id) != 0)
304 .map(|capset| capset.name.to_string())
305 .collect()
306}
307
308fn calculate_component(component_mask: u8) -> RutabagaResult<RutabagaComponentType> {
309 if component_mask.count_ones() != 1 {
310 return Err(RutabagaError::SpecViolation("can't infer single component"));
311 }
312
313 match component_mask.trailing_zeros() {
314 0 => Ok(RutabagaComponentType::Rutabaga2D),
315 1 => Ok(RutabagaComponentType::VirglRenderer),
316 2 => Ok(RutabagaComponentType::Gfxstream),
317 3 => Ok(RutabagaComponentType::CrossDomain),
318 _ => Err(RutabagaError::InvalidComponent),
319 }
320}
321
322pub struct Rutabaga {
330 resources: Map<u32, RutabagaResource>,
331 contexts: Map<u32, Box<dyn RutabagaContext>>,
332 components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>>,
334 default_component: RutabagaComponentType,
335 capset_info: Vec<RutabagaCapsetInfo>,
336 fence_handler: RutabagaFenceHandler,
337}
338
339impl Rutabaga {
340 pub fn snapshot(&self, w: &mut impl Write) -> RutabagaResult<()> {
345 if !(self.contexts.is_empty()
347 && self
348 .components
349 .keys()
350 .all(|t| *t == RutabagaComponentType::Rutabaga2D)
351 && self.default_component == RutabagaComponentType::Rutabaga2D
352 && self.capset_info.is_empty())
353 {
354 return Err(RutabagaError::Unsupported);
355 }
356 let snapshot = RutabagaSnapshot {
357 resources: self
358 .resources
359 .iter()
360 .map(|(i, r)| {
361 if !(r.handle.is_none()
362 && !r.blob
363 && r.blob_mem == 0
364 && r.blob_flags == 0
365 && r.map_info.is_none()
366 && r.info_3d.is_none()
367 && r.vulkan_info.is_none()
368 && r.component_mask == 1 << (RutabagaComponentType::Rutabaga2D as u8)
369 && r.mapping.is_none())
370 {
371 return Err(RutabagaError::Unsupported);
372 }
373 let info = r.info_2d.as_ref().ok_or(RutabagaError::Unsupported)?;
374 assert_eq!(
375 usize::try_from(info.width * info.height * 4).unwrap(),
376 info.host_mem.len()
377 );
378 assert_eq!(usize::try_from(r.size).unwrap(), info.host_mem.len());
379 let s = RutabagaResourceSnapshot {
380 resource_id: r.resource_id,
381 width: info.width,
382 height: info.height,
383 };
384 Ok((*i, s))
385 })
386 .collect::<RutabagaResult<_>>()?,
387 };
388
389 snapshot.serialize_to(w).map_err(RutabagaError::IoError)
390 }
391
392 pub fn restore(&mut self, r: &mut impl Read) -> RutabagaResult<()> {
414 let snapshot = RutabagaSnapshot::deserialize_from(r).map_err(RutabagaError::IoError)?;
415
416 if !(self.resources.is_empty()
418 && self.contexts.is_empty()
419 && self
420 .components
421 .keys()
422 .all(|t| *t == RutabagaComponentType::Rutabaga2D)
423 && self.default_component == RutabagaComponentType::Rutabaga2D
424 && self.capset_info.is_empty())
425 {
426 return Err(RutabagaError::Unsupported);
427 }
428 self.resources = snapshot
429 .resources
430 .into_iter()
431 .map(|(i, s)| {
432 let size = u64::from(s.width * s.height * 4);
433 let r = RutabagaResource {
434 resource_id: s.resource_id,
435 handle: None,
436 blob: false,
437 blob_mem: 0,
438 blob_flags: 0,
439 map_info: None,
440 info_2d: Some(Rutabaga2DInfo {
441 width: s.width,
442 height: s.height,
443 host_mem: vec![0; usize::try_from(size).unwrap()],
444 }),
445 info_3d: None,
446 vulkan_info: None,
447 backing_iovecs: None,
453 component_mask: 1 << (RutabagaComponentType::Rutabaga2D as u8),
454 size,
455 mapping: None,
456 };
457 (i, r)
458 })
459 .collect();
460
461 Ok(())
462 }
463
464 fn capset_id_to_component_type(&self, capset_id: u32) -> RutabagaResult<RutabagaComponentType> {
465 let component = self
466 .capset_info
467 .iter()
468 .find(|capset_info| capset_info.capset_id == capset_id)
469 .ok_or(RutabagaError::InvalidCapset)?
470 .component;
471
472 Ok(component)
473 }
474
475 fn capset_index_to_component_info(&self, index: u32) -> RutabagaResult<RutabagaCapsetInfo> {
476 let idx = index as usize;
477 if idx >= self.capset_info.len() {
478 return Err(RutabagaError::InvalidCapset);
479 }
480
481 Ok(self.capset_info[idx])
482 }
483
484 pub fn get_capset_info(&self, index: u32) -> RutabagaResult<(u32, u32, u32)> {
486 let capset_info = self.capset_index_to_component_info(index)?;
487
488 let component = self
489 .components
490 .get(&capset_info.component)
491 .ok_or(RutabagaError::InvalidComponent)?;
492
493 let (capset_version, capset_size) = component.get_capset_info(capset_info.capset_id);
494 Ok((capset_info.capset_id, capset_version, capset_size))
495 }
496
497 pub fn get_capset(&self, capset_id: u32, version: u32) -> RutabagaResult<Vec<u8>> {
501 let component_type = self
504 .capset_id_to_component_type(capset_id)
505 .unwrap_or(self.default_component);
506
507 let component = self
508 .components
509 .get(&component_type)
510 .ok_or(RutabagaError::InvalidComponent)?;
511
512 Ok(component.get_capset(capset_id, version))
513 }
514
515 pub fn get_num_capsets(&self) -> u32 {
517 self.capset_info.len() as u32
518 }
519
520 pub fn force_ctx_0(&self) {
522 if let Some(component) = self.components.get(&self.default_component) {
523 component.force_ctx_0();
524 }
525 }
526
527 pub fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
531 if fence.flags & RUTABAGA_FLAG_INFO_RING_IDX != 0 {
532 let ctx = self
533 .contexts
534 .get_mut(&fence.ctx_id)
535 .ok_or(RutabagaError::InvalidContextId)?;
536
537 ctx.context_create_fence(fence)?;
538 } else {
539 let component = self
540 .components
541 .get_mut(&self.default_component)
542 .ok_or(RutabagaError::InvalidComponent)?;
543
544 component.create_fence(fence)?;
545 }
546
547 Ok(())
548 }
549
550 pub fn event_poll(&self) {
552 if let Some(component) = self.components.get(&self.default_component) {
553 component.event_poll();
554 }
555 }
556
557 pub fn poll_descriptor(&self) -> Option<SafeDescriptor> {
560 let component = self.components.get(&self.default_component).or(None)?;
561 component.poll_descriptor()
562 }
563
564 pub fn resource_create_3d(
566 &mut self,
567 resource_id: u32,
568 resource_create_3d: ResourceCreate3D,
569 ) -> RutabagaResult<()> {
570 let component = self
571 .components
572 .get_mut(&self.default_component)
573 .ok_or(RutabagaError::InvalidComponent)?;
574
575 if self.resources.contains_key(&resource_id) {
576 return Err(RutabagaError::InvalidResourceId);
577 }
578
579 let resource = component.create_3d(resource_id, resource_create_3d)?;
580 self.resources.insert(resource_id, resource);
581 Ok(())
582 }
583
584 pub fn attach_backing(
586 &mut self,
587 resource_id: u32,
588 mut vecs: Vec<RutabagaIovec>,
589 ) -> RutabagaResult<()> {
590 let component = self
591 .components
592 .get_mut(&self.default_component)
593 .ok_or(RutabagaError::InvalidComponent)?;
594
595 let resource = self
596 .resources
597 .get_mut(&resource_id)
598 .ok_or(RutabagaError::InvalidResourceId)?;
599
600 component.attach_backing(resource_id, &mut vecs)?;
601 resource.backing_iovecs = Some(vecs);
602 Ok(())
603 }
604
605 pub fn detach_backing(&mut self, resource_id: u32) -> RutabagaResult<()> {
607 let component = self
608 .components
609 .get_mut(&self.default_component)
610 .ok_or(RutabagaError::InvalidComponent)?;
611
612 let resource = self
613 .resources
614 .get_mut(&resource_id)
615 .ok_or(RutabagaError::InvalidResourceId)?;
616
617 component.detach_backing(resource_id);
618 resource.backing_iovecs = None;
619 Ok(())
620 }
621
622 pub fn unref_resource(&mut self, resource_id: u32) -> RutabagaResult<()> {
624 let component = self
625 .components
626 .get_mut(&self.default_component)
627 .ok_or(RutabagaError::InvalidComponent)?;
628
629 self.resources
630 .remove(&resource_id)
631 .ok_or(RutabagaError::InvalidResourceId)?;
632
633 component.unref_resource(resource_id);
634 Ok(())
635 }
636
637 pub fn transfer_write(
640 &mut self,
641 ctx_id: u32,
642 resource_id: u32,
643 transfer: Transfer3D,
644 ) -> RutabagaResult<()> {
645 let component = self
646 .components
647 .get(&self.default_component)
648 .ok_or(RutabagaError::InvalidComponent)?;
649
650 let resource = self
651 .resources
652 .get_mut(&resource_id)
653 .ok_or(RutabagaError::InvalidResourceId)?;
654
655 component.transfer_write(ctx_id, resource, transfer)
656 }
657
658 pub fn transfer_read(
663 &mut self,
664 ctx_id: u32,
665 resource_id: u32,
666 transfer: Transfer3D,
667 buf: Option<IoSliceMut>,
668 ) -> RutabagaResult<()> {
669 let component = self
670 .components
671 .get(&self.default_component)
672 .ok_or(RutabagaError::InvalidComponent)?;
673
674 let resource = self
675 .resources
676 .get_mut(&resource_id)
677 .ok_or(RutabagaError::InvalidResourceId)?;
678
679 component.transfer_read(ctx_id, resource, transfer, buf)
680 }
681
682 pub fn resource_flush(&mut self, resource_id: u32) -> RutabagaResult<()> {
683 let component = self
684 .components
685 .get(&self.default_component)
686 .ok_or(RutabagaError::Unsupported)?;
687
688 let resource = self
689 .resources
690 .get_mut(&resource_id)
691 .ok_or(RutabagaError::InvalidResourceId)?;
692
693 component.resource_flush(resource)
694 }
695
696 pub fn resource_create_blob(
700 &mut self,
701 ctx_id: u32,
702 resource_id: u32,
703 resource_create_blob: ResourceCreateBlob,
704 iovecs: Option<Vec<RutabagaIovec>>,
705 handle: Option<RutabagaHandle>,
706 ) -> RutabagaResult<()> {
707 if self.resources.contains_key(&resource_id) {
708 return Err(RutabagaError::InvalidResourceId);
709 }
710
711 let component = self
712 .components
713 .get_mut(&self.default_component)
714 .ok_or(RutabagaError::InvalidComponent)?;
715
716 let mut context = None;
717 if ctx_id > 0 {
721 let ctx = self
722 .contexts
723 .get_mut(&ctx_id)
724 .ok_or(RutabagaError::InvalidContextId)?;
725
726 if ctx.component_type() == RutabagaComponentType::CrossDomain {
727 context = Some(ctx);
728 }
729 }
730
731 let resource = match context {
732 Some(ctx) => ctx.context_create_blob(resource_id, resource_create_blob, handle)?,
733 None => {
734 component.create_blob(ctx_id, resource_id, resource_create_blob, iovecs, handle)?
735 }
736 };
737
738 self.resources.insert(resource_id, resource);
739 Ok(())
740 }
741
742 pub fn map(&mut self, resource_id: u32) -> RutabagaResult<RutabagaMapping> {
744 let resource = self
745 .resources
746 .get_mut(&resource_id)
747 .ok_or(RutabagaError::InvalidResourceId)?;
748
749 let component_type = calculate_component(resource.component_mask)?;
750 if component_type == RutabagaComponentType::CrossDomain {
751 let handle_opt = resource.handle.take();
752 match handle_opt {
753 Some(handle) => {
754 if handle.handle_type != RUTABAGA_MEM_HANDLE_TYPE_SHM {
755 return Err(RutabagaError::SpecViolation(
756 "expected a shared memory handle",
757 ));
758 }
759
760 let clone = handle.try_clone()?;
761 let resource_size: usize = resource.size.try_into()?;
762 let map_info = resource
763 .map_info
764 .ok_or(RutabagaError::SpecViolation("no map info available"))?;
765
766 let mapping = MemoryMapping::from_safe_descriptor(
768 clone.os_handle,
769 resource_size,
770 map_info,
771 )?;
772 let rutabaga_mapping = mapping.as_rutabaga_mapping();
773 resource.handle = Some(handle);
774 resource.mapping = Some(mapping);
775
776 return Ok(rutabaga_mapping);
777 }
778 None => return Err(RutabagaError::SpecViolation("expected a handle to map")),
779 }
780 }
781
782 let component = self
783 .components
784 .get(&component_type)
785 .ok_or(RutabagaError::InvalidComponent)?;
786
787 component.map(resource_id)
788 }
789
790 pub fn unmap(&mut self, resource_id: u32) -> RutabagaResult<()> {
792 let resource = self
793 .resources
794 .get_mut(&resource_id)
795 .ok_or(RutabagaError::InvalidResourceId)?;
796
797 let component_type = calculate_component(resource.component_mask)?;
798 if component_type == RutabagaComponentType::CrossDomain {
799 resource.mapping = None;
800 return Ok(());
801 }
802
803 let component = self
804 .components
805 .get(&component_type)
806 .ok_or(RutabagaError::InvalidComponent)?;
807
808 component.unmap(resource_id)
809 }
810
811 pub fn map_info(&self, resource_id: u32) -> RutabagaResult<u32> {
814 let resource = self
815 .resources
816 .get(&resource_id)
817 .ok_or(RutabagaError::InvalidResourceId)?;
818
819 resource
820 .map_info
821 .ok_or(RutabagaError::SpecViolation("no map info available"))
822 }
823
824 pub fn vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo> {
827 let resource = self
828 .resources
829 .get(&resource_id)
830 .ok_or(RutabagaError::InvalidResourceId)?;
831
832 resource.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo)
833 }
834
835 pub fn query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo> {
837 let resource = self
838 .resources
839 .get(&resource_id)
840 .ok_or(RutabagaError::InvalidResourceId)?;
841
842 resource
843 .info_3d
844 .ok_or(RutabagaError::SpecViolation("no 3d info available"))
845 }
846
847 pub fn export_blob(&mut self, resource_id: u32) -> RutabagaResult<RutabagaHandle> {
849 let resource = self
850 .resources
851 .get_mut(&resource_id)
852 .ok_or(RutabagaError::InvalidResourceId)?;
853
854 let share_mask = RUTABAGA_BLOB_FLAG_USE_SHAREABLE | RUTABAGA_BLOB_FLAG_USE_CROSS_DEVICE;
856 let shareable = (resource.blob_flags & share_mask != 0) || !resource.blob;
857
858 let opt = resource.handle.take();
859
860 match (opt, shareable) {
861 (Some(handle), true) => {
862 let clone = handle.try_clone()?;
863 resource.handle = Some(handle);
864 Ok(clone)
865 }
866 (Some(handle), false) => {
867 let hnd =
869 Arc::try_unwrap(handle).map_err(|_| RutabagaError::InvalidRutabagaHandle)?;
870 Ok(hnd)
871 }
872 _ => Err(RutabagaError::InvalidRutabagaHandle),
873 }
874 }
875
876 pub fn export_fence(&self, fence_id: u64) -> RutabagaResult<RutabagaHandle> {
878 let component = self
879 .components
880 .get(&self.default_component)
881 .ok_or(RutabagaError::InvalidComponent)?;
882
883 component.export_fence(fence_id)
884 }
885
886 pub fn create_context(
889 &mut self,
890 ctx_id: u32,
891 context_init: u32,
892 context_name: Option<&str>,
893 ) -> RutabagaResult<()> {
894 let capset_id = context_init & RUTABAGA_CONTEXT_INIT_CAPSET_ID_MASK;
897 let component_type = self
898 .capset_id_to_component_type(capset_id)
899 .unwrap_or(self.default_component);
900
901 let component = self
902 .components
903 .get_mut(&component_type)
904 .ok_or(RutabagaError::InvalidComponent)?;
905
906 if self.contexts.contains_key(&ctx_id) {
907 return Err(RutabagaError::InvalidContextId);
908 }
909
910 let ctx = component.create_context(
911 ctx_id,
912 context_init,
913 context_name,
914 self.fence_handler.clone(),
915 )?;
916 self.contexts.insert(ctx_id, ctx);
917 Ok(())
918 }
919
920 pub fn destroy_context(&mut self, ctx_id: u32) -> RutabagaResult<()> {
922 self.contexts
923 .remove(&ctx_id)
924 .ok_or(RutabagaError::InvalidContextId)?;
925 Ok(())
926 }
927
928 pub fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> {
930 let ctx = self
931 .contexts
932 .get_mut(&ctx_id)
933 .ok_or(RutabagaError::InvalidContextId)?;
934
935 let resource = self
936 .resources
937 .get_mut(&resource_id)
938 .ok_or(RutabagaError::InvalidResourceId)?;
939
940 ctx.attach(resource);
941 Ok(())
942 }
943
944 pub fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> {
946 let ctx = self
947 .contexts
948 .get_mut(&ctx_id)
949 .ok_or(RutabagaError::InvalidContextId)?;
950
951 let resource = self
952 .resources
953 .get_mut(&resource_id)
954 .ok_or(RutabagaError::InvalidResourceId)?;
955
956 ctx.detach(resource);
957 Ok(())
958 }
959
960 pub fn submit_command(
962 &mut self,
963 ctx_id: u32,
964 commands: &mut [u8],
965 fence_ids: &[u64],
966 ) -> RutabagaResult<()> {
967 let ctx = self
968 .contexts
969 .get_mut(&ctx_id)
970 .ok_or(RutabagaError::InvalidContextId)?;
971
972 ctx.submit_cmd(commands, fence_ids)
973 }
974}
975
976#[derive(Clone)]
978pub struct RutabagaBuilder {
979 display_width: u32,
980 display_height: u32,
981 default_component: RutabagaComponentType,
982 gfxstream_flags: GfxstreamFlags,
983 virglrenderer_flags: VirglRendererFlags,
984 capset_mask: u64,
985 channels: Option<Vec<RutabagaChannel>>,
986 debug_handler: Option<RutabagaDebugHandler>,
987}
988
989impl RutabagaBuilder {
990 pub fn new(default_component: RutabagaComponentType, capset_mask: u64) -> RutabagaBuilder {
992 let virglrenderer_flags = VirglRendererFlags::new()
993 .use_thread_sync(true)
994 .use_async_fence_cb(true);
995 let gfxstream_flags = GfxstreamFlags::new();
996 RutabagaBuilder {
997 display_width: RUTABAGA_DEFAULT_WIDTH,
998 display_height: RUTABAGA_DEFAULT_HEIGHT,
999 default_component,
1000 gfxstream_flags,
1001 virglrenderer_flags,
1002 capset_mask,
1003 channels: None,
1004 debug_handler: None,
1005 }
1006 }
1007
1008 pub fn set_display_width(mut self, display_width: u32) -> RutabagaBuilder {
1010 self.display_width = display_width;
1011 self
1012 }
1013
1014 pub fn set_display_height(mut self, display_height: u32) -> RutabagaBuilder {
1016 self.display_height = display_height;
1017 self
1018 }
1019
1020 pub fn set_use_egl(mut self, v: bool) -> RutabagaBuilder {
1022 self.gfxstream_flags = self.gfxstream_flags.use_egl(v);
1023 self.virglrenderer_flags = self.virglrenderer_flags.use_egl(v);
1024 self
1025 }
1026
1027 pub fn set_use_gles(mut self, v: bool) -> RutabagaBuilder {
1029 self.gfxstream_flags = self.gfxstream_flags.use_gles(v);
1030 self.virglrenderer_flags = self.virglrenderer_flags.use_gles(v);
1031 self
1032 }
1033
1034 pub fn set_use_glx(mut self, v: bool) -> RutabagaBuilder {
1036 self.gfxstream_flags = self.gfxstream_flags.use_glx(v);
1037 self.virglrenderer_flags = self.virglrenderer_flags.use_glx(v);
1038 self
1039 }
1040
1041 pub fn set_use_surfaceless(mut self, v: bool) -> RutabagaBuilder {
1043 self.gfxstream_flags = self.gfxstream_flags.use_surfaceless(v);
1044 self.virglrenderer_flags = self.virglrenderer_flags.use_surfaceless(v);
1045 self
1046 }
1047
1048 pub fn set_use_vulkan(mut self, v: bool) -> RutabagaBuilder {
1050 self.gfxstream_flags = self.gfxstream_flags.use_vulkan(v);
1051 self.virglrenderer_flags = self.virglrenderer_flags.use_venus(v);
1052 self
1053 }
1054
1055 pub fn set_use_external_blob(mut self, v: bool) -> RutabagaBuilder {
1057 self.gfxstream_flags = self.gfxstream_flags.use_external_blob(v);
1058 self.virglrenderer_flags = self.virglrenderer_flags.use_external_blob(v);
1059 self
1060 }
1061
1062 pub fn set_use_system_blob(mut self, v: bool) -> RutabagaBuilder {
1064 self.gfxstream_flags = self.gfxstream_flags.use_system_blob(v);
1065 self
1066 }
1067
1068 pub fn set_use_render_server(mut self, v: bool) -> RutabagaBuilder {
1070 self.virglrenderer_flags = self.virglrenderer_flags.use_render_server(v);
1071 self
1072 }
1073
1074 pub fn set_wsi(mut self, v: RutabagaWsi) -> RutabagaBuilder {
1076 self.gfxstream_flags = self.gfxstream_flags.set_wsi(v);
1077 self
1078 }
1079
1080 pub fn set_rutabaga_channels(
1082 mut self,
1083 channels: Option<Vec<RutabagaChannel>>,
1084 ) -> RutabagaBuilder {
1085 self.channels = channels;
1086 self
1087 }
1088
1089 pub fn set_debug_handler(
1091 mut self,
1092 debug_handler: Option<RutabagaDebugHandler>,
1093 ) -> RutabagaBuilder {
1094 self.debug_handler = debug_handler;
1095 self
1096 }
1097
1098 pub fn build(
1104 mut self,
1105 fence_handler: RutabagaFenceHandler,
1106 #[allow(unused_variables)] rutabaga_server_descriptor: Option<SafeDescriptor>,
1107 ) -> RutabagaResult<Rutabaga> {
1108 let mut rutabaga_components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>> =
1109 Default::default();
1110
1111 #[allow(unused_mut)]
1112 let mut rutabaga_capsets: Vec<RutabagaCapsetInfo> = Default::default();
1113
1114 let capset_enabled =
1115 |capset_id: u32| -> bool { (self.capset_mask & (1 << capset_id)) != 0 };
1116
1117 let mut push_capset = |capset_id: u32| {
1118 if let Some(capset) = RUTABAGA_CAPSETS
1119 .iter()
1120 .find(|capset| capset_id == capset.capset_id)
1121 {
1122 if self.capset_mask != 0 {
1123 if capset_enabled(capset.capset_id) {
1124 rutabaga_capsets.push(*capset);
1125 }
1126 } else {
1127 rutabaga_capsets.push(*capset);
1130 }
1131 };
1132 };
1133
1134 if self.capset_mask != 0 {
1135 let supports_gfxstream = capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_VULKAN)
1136 | capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_MAGMA)
1137 | capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_GLES)
1138 | capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_COMPOSER);
1139 let supports_virglrenderer = capset_enabled(RUTABAGA_CAPSET_VIRGL2)
1140 | capset_enabled(RUTABAGA_CAPSET_VENUS)
1141 | capset_enabled(RUTABAGA_CAPSET_DRM);
1142
1143 if supports_gfxstream {
1144 self.default_component = RutabagaComponentType::Gfxstream;
1145 } else if supports_virglrenderer {
1146 self.default_component = RutabagaComponentType::VirglRenderer;
1147 } else {
1148 self.default_component = RutabagaComponentType::CrossDomain;
1149 }
1150
1151 self.virglrenderer_flags = self
1152 .virglrenderer_flags
1153 .use_virgl(capset_enabled(RUTABAGA_CAPSET_VIRGL2))
1154 .use_venus(capset_enabled(RUTABAGA_CAPSET_VENUS))
1155 .use_drm(capset_enabled(RUTABAGA_CAPSET_DRM));
1156
1157 self.gfxstream_flags = self
1158 .gfxstream_flags
1159 .use_gles(capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_GLES))
1160 .use_vulkan(capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_VULKAN))
1161 }
1162
1163 #[cfg(not(feature = "virgl_renderer"))]
1165 if self.default_component == RutabagaComponentType::VirglRenderer {
1166 return Err(RutabagaError::InvalidRutabagaBuild(
1167 "virgl renderer feature not enabled",
1168 ));
1169 }
1170 #[cfg(not(feature = "gfxstream"))]
1171 if self.default_component == RutabagaComponentType::Gfxstream {
1172 return Err(RutabagaError::InvalidRutabagaBuild(
1173 "gfxstream feature not enabled",
1174 ));
1175 }
1176
1177 if self.default_component == RutabagaComponentType::Rutabaga2D {
1178 let rutabaga_2d = Rutabaga2D::init(fence_handler.clone())?;
1179 rutabaga_components.insert(RutabagaComponentType::Rutabaga2D, rutabaga_2d);
1180 } else {
1181 #[cfg(feature = "virgl_renderer")]
1182 if self.default_component == RutabagaComponentType::VirglRenderer {
1183 let virgl = VirglRenderer::init(
1184 self.virglrenderer_flags,
1185 fence_handler.clone(),
1186 rutabaga_server_descriptor,
1187 )?;
1188 rutabaga_components.insert(RutabagaComponentType::VirglRenderer, virgl);
1189
1190 push_capset(RUTABAGA_CAPSET_VIRGL);
1191 push_capset(RUTABAGA_CAPSET_VIRGL2);
1192 push_capset(RUTABAGA_CAPSET_VENUS);
1193 push_capset(RUTABAGA_CAPSET_DRM);
1194 }
1195
1196 #[cfg(feature = "gfxstream")]
1197 if self.default_component == RutabagaComponentType::Gfxstream {
1198 let gfxstream = Gfxstream::init(
1199 self.display_width,
1200 self.display_height,
1201 self.gfxstream_flags,
1202 fence_handler.clone(),
1203 self.debug_handler.clone(),
1204 )?;
1205
1206 rutabaga_components.insert(RutabagaComponentType::Gfxstream, gfxstream);
1207
1208 push_capset(RUTABAGA_CAPSET_GFXSTREAM_VULKAN);
1209 push_capset(RUTABAGA_CAPSET_GFXSTREAM_MAGMA);
1210 push_capset(RUTABAGA_CAPSET_GFXSTREAM_GLES);
1211 push_capset(RUTABAGA_CAPSET_GFXSTREAM_COMPOSER);
1212 }
1213
1214 let cross_domain = CrossDomain::init(self.channels, fence_handler.clone())?;
1215 rutabaga_components.insert(RutabagaComponentType::CrossDomain, cross_domain);
1216 push_capset(RUTABAGA_CAPSET_CROSS_DOMAIN);
1217 }
1218
1219 Ok(Rutabaga {
1220 resources: Default::default(),
1221 contexts: Default::default(),
1222 components: rutabaga_components,
1223 default_component: self.default_component,
1224 capset_info: rutabaga_capsets,
1225 fence_handler,
1226 })
1227 }
1228}
1229
1230#[cfg(test)]
1231mod tests {
1232 use crate::*;
1233
1234 fn new_2d() -> Rutabaga {
1235 RutabagaBuilder::new(RutabagaComponentType::Rutabaga2D, 0)
1236 .build(RutabagaHandler::new(|_| {}), None)
1237 .unwrap()
1238 }
1239
1240 #[test]
1241 fn snapshot_restore_2d_no_resources() {
1242 let mut buffer = std::io::Cursor::new(Vec::new());
1243
1244 let rutabaga1 = new_2d();
1245 rutabaga1.snapshot(&mut buffer).unwrap();
1246
1247 let mut rutabaga1 = new_2d();
1248 rutabaga1.restore(&mut &buffer.get_ref()[..]).unwrap();
1249 }
1250
1251 #[test]
1252 fn snapshot_restore_2d_one_resource() {
1253 let resource_id = 123;
1254 let resource_create_3d = ResourceCreate3D {
1255 target: RUTABAGA_PIPE_TEXTURE_2D,
1256 format: 1,
1257 bind: RUTABAGA_PIPE_BIND_RENDER_TARGET,
1258 width: 100,
1259 height: 200,
1260 depth: 1,
1261 array_size: 1,
1262 last_level: 0,
1263 nr_samples: 0,
1264 flags: 0,
1265 };
1266
1267 let mut buffer = std::io::Cursor::new(Vec::new());
1268
1269 let mut rutabaga1 = new_2d();
1270 rutabaga1
1271 .resource_create_3d(resource_id, resource_create_3d)
1272 .unwrap();
1273 rutabaga1
1274 .attach_backing(
1275 resource_id,
1276 vec![RutabagaIovec {
1277 base: std::ptr::null_mut(),
1278 len: 456,
1279 }],
1280 )
1281 .unwrap();
1282 rutabaga1.snapshot(&mut buffer).unwrap();
1283
1284 let mut rutabaga2 = new_2d();
1285 rutabaga2.restore(&mut &buffer.get_ref()[..]).unwrap();
1286
1287 assert_eq!(rutabaga2.resources.len(), 1);
1288 let rutabaga_resource = rutabaga2.resources.get(&resource_id).unwrap();
1289 assert_eq!(rutabaga_resource.resource_id, resource_id);
1290 assert_eq!(
1291 rutabaga_resource.info_2d.as_ref().unwrap().width,
1292 resource_create_3d.width
1293 );
1294 assert_eq!(
1295 rutabaga_resource.info_2d.as_ref().unwrap().height,
1296 resource_create_3d.height
1297 );
1298 assert!(rutabaga_resource.backing_iovecs.is_none());
1300 }
1301}