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