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