1 // Copyright 2020 The Chromium OS Authors. All rights reserved. 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 7 use std::collections::BTreeMap as Map; 8 use std::sync::Arc; 9 10 use base::{ExternalMapping, SafeDescriptor}; 11 use data_model::VolatileSlice; 12 13 use crate::cross_domain::CrossDomain; 14 15 #[cfg(feature = "gfxstream")] 16 use crate::gfxstream::Gfxstream; 17 18 use crate::rutabaga_2d::Rutabaga2D; 19 use crate::rutabaga_utils::*; 20 21 #[cfg(feature = "virgl_renderer")] 22 use crate::virgl_renderer::VirglRenderer; 23 24 /// Information required for 2D functionality. 25 pub struct Rutabaga2DInfo { 26 pub width: u32, 27 pub height: u32, 28 pub host_mem: Vec<u8>, 29 } 30 31 /// A Rutabaga resource, supporting 2D and 3D rutabaga features. Assumes a single-threaded library. 32 pub struct RutabagaResource { 33 pub resource_id: u32, 34 pub handle: Option<Arc<RutabagaHandle>>, 35 pub blob: bool, 36 pub blob_mem: u32, 37 pub blob_flags: u32, 38 pub map_info: Option<u32>, 39 pub info_2d: Option<Rutabaga2DInfo>, 40 pub info_3d: Option<Resource3DInfo>, 41 pub vulkan_info: Option<VulkanInfo>, 42 pub backing_iovecs: Option<Vec<RutabagaIovec>>, 43 } 44 45 /// A RutabagaComponent is a building block of the Virtual Graphics Interface (VGI). Each component 46 /// on it's own is sufficient to virtualize graphics on many Google products. These components wrap 47 /// libraries like gfxstream or virglrenderer, and Rutabaga's own 2D and cross-domain prototype 48 /// functionality. 49 /// 50 /// Most methods return a `RutabagaResult` that indicate the success, failure, or requested data for 51 /// the given command. 52 pub trait RutabagaComponent { 53 /// Implementations should return the version and size of the given capset_id. (0, 0) is 54 /// returned by default. get_capset_info(&self, _capset_id: u32) -> (u32, u32)55 fn get_capset_info(&self, _capset_id: u32) -> (u32, u32) { 56 (0, 0) 57 } 58 59 /// Implementations should return the capabilites of given a `capset_id` and `version`. A 60 /// zero-sized array is returned by default. get_capset(&self, _capset_id: u32, _version: u32) -> Vec<u8>61 fn get_capset(&self, _capset_id: u32, _version: u32) -> Vec<u8> { 62 Vec::new() 63 } 64 65 /// Implementations should set their internal context to be the reserved context 0. force_ctx_0(&self)66 fn force_ctx_0(&self) {} 67 68 /// Implementations must create a fence that represents the completion of prior work. This is 69 /// required for synchronization with the guest kernel. create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()>70 fn create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()> { 71 Err(RutabagaError::Unsupported) 72 } 73 74 /// Used only by VirglRenderer to poll when its poll_descriptor is signaled. poll(&self)75 fn poll(&self) {} 76 77 /// Used only by VirglRenderer to return a poll_descriptor that is signaled when a poll() is 78 /// necessary. poll_descriptor(&self) -> Option<SafeDescriptor>79 fn poll_descriptor(&self) -> Option<SafeDescriptor> { 80 None 81 } 82 83 /// Implementations must create a resource with the given metadata. For 2D rutabaga components, 84 /// this a system memory allocation. For 3D components, this is typically a GL texture or 85 /// buffer. Vulkan components should use blob resources instead. create_3d( &self, _resource_id: u32, _resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<RutabagaResource>86 fn create_3d( 87 &self, 88 _resource_id: u32, 89 _resource_create_3d: ResourceCreate3D, 90 ) -> RutabagaResult<RutabagaResource> { 91 Err(RutabagaError::Unsupported) 92 } 93 94 /// Implementations must attach `vecs` to the resource. attach_backing( &self, _resource_id: u32, _vecs: &mut Vec<RutabagaIovec>, ) -> RutabagaResult<()>95 fn attach_backing( 96 &self, 97 _resource_id: u32, 98 _vecs: &mut Vec<RutabagaIovec>, 99 ) -> RutabagaResult<()> { 100 Ok(()) 101 } 102 103 /// Implementations must detach `vecs` from the resource. detach_backing(&self, _resource_id: u32)104 fn detach_backing(&self, _resource_id: u32) {} 105 106 /// Implementations must release the guest kernel reference on the resource. unref_resource(&self, _resource_id: u32)107 fn unref_resource(&self, _resource_id: u32) {} 108 109 /// Implementations must perform the transfer write operation. For 2D rutabaga components, this 110 /// done via memcpy(). For 3D components, this is typically done via glTexSubImage(..). transfer_write( &self, _ctx_id: u32, _resource: &mut RutabagaResource, _transfer: Transfer3D, ) -> RutabagaResult<()>111 fn transfer_write( 112 &self, 113 _ctx_id: u32, 114 _resource: &mut RutabagaResource, 115 _transfer: Transfer3D, 116 ) -> RutabagaResult<()> { 117 Err(RutabagaError::Unsupported) 118 } 119 120 /// Implementations must perform the transfer read operation. For 2D rutabaga components, this 121 /// 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<VolatileSlice>, ) -> RutabagaResult<()>122 fn transfer_read( 123 &self, 124 _ctx_id: u32, 125 _resource: &mut RutabagaResource, 126 _transfer: Transfer3D, 127 _buf: Option<VolatileSlice>, 128 ) -> RutabagaResult<()> { 129 Err(RutabagaError::Unsupported) 130 } 131 132 /// Implementations must create a blob resource on success. The memory parameters, size, and 133 /// 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>134 fn create_blob( 135 &mut self, 136 _ctx_id: u32, 137 _resource_id: u32, 138 _resource_create_blob: ResourceCreateBlob, 139 _iovec_opt: Option<Vec<RutabagaIovec>>, 140 _handle_opt: Option<RutabagaHandle>, 141 ) -> RutabagaResult<RutabagaResource> { 142 Err(RutabagaError::Unsupported) 143 } 144 145 /// Implementations must map the blob resource on success. This is typically done by 146 /// glMapBufferRange(...) or vkMapMemory. map(&self, _resource_id: u32) -> RutabagaResult<ExternalMapping>147 fn map(&self, _resource_id: u32) -> RutabagaResult<ExternalMapping> { 148 Err(RutabagaError::Unsupported) 149 } 150 151 /// Implementations must return a RutabagaHandle of the fence on success. export_fence(&self, _fence_id: u32) -> RutabagaResult<RutabagaHandle>152 fn export_fence(&self, _fence_id: u32) -> RutabagaResult<RutabagaHandle> { 153 Err(RutabagaError::Unsupported) 154 } 155 156 /// Implementations must create a context for submitting commands. The command stream of the 157 /// context is determined by `context_init`. For virgl contexts, it is a Gallium/TGSI command 158 /// stream. For gfxstream contexts, it's an autogenerated Vulkan or GLES streams. create_context( &self, _ctx_id: u32, _context_init: u32, _fence_handler: RutabagaFenceHandler, ) -> RutabagaResult<Box<dyn RutabagaContext>>159 fn create_context( 160 &self, 161 _ctx_id: u32, 162 _context_init: u32, 163 _fence_handler: RutabagaFenceHandler, 164 ) -> RutabagaResult<Box<dyn RutabagaContext>> { 165 Err(RutabagaError::Unsupported) 166 } 167 } 168 169 pub trait RutabagaContext { 170 /// 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>171 fn context_create_blob( 172 &mut self, 173 _resource_id: u32, 174 _resource_create_blob: ResourceCreateBlob, 175 _handle_opt: Option<RutabagaHandle>, 176 ) -> RutabagaResult<RutabagaResource> { 177 Err(RutabagaError::Unsupported) 178 } 179 180 /// Implementations must handle the context-specific command stream. submit_cmd(&mut self, _commands: &mut [u8]) -> RutabagaResult<()>181 fn submit_cmd(&mut self, _commands: &mut [u8]) -> RutabagaResult<()>; 182 183 /// Implementations may use `resource` in this context's command stream. attach(&mut self, _resource: &mut RutabagaResource)184 fn attach(&mut self, _resource: &mut RutabagaResource); 185 186 /// Implementations must stop using `resource` in this context's command stream. detach(&mut self, _resource: &RutabagaResource)187 fn detach(&mut self, _resource: &RutabagaResource); 188 189 /// Implementations must create a fence on specified `ring_idx` in `fence`. This 190 /// allows for multiple synchronizations timelines per RutabagaContext. context_create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()>191 fn context_create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()> { 192 Err(RutabagaError::Unsupported) 193 } 194 195 /// Implementations must return the component type associated with the context. component_type(&self) -> RutabagaComponentType196 fn component_type(&self) -> RutabagaComponentType; 197 } 198 199 #[derive(Copy, Clone)] 200 struct RutabagaCapsetInfo { 201 pub capset_id: u32, 202 pub component: RutabagaComponentType, 203 } 204 205 /// The global libary handle used to query capability sets, create resources and contexts. 206 /// 207 /// Currently, Rutabaga only supports one default component. Many components running at the 208 /// same time is a stretch goal of Rutabaga GFX. 209 /// 210 /// Not thread-safe, but can be made so easily. Making non-Rutabaga, C/C++ components 211 /// thread-safe is more difficult. 212 pub struct Rutabaga { 213 resources: Map<u32, RutabagaResource>, 214 contexts: Map<u32, Box<dyn RutabagaContext>>, 215 // Declare components after resources and contexts such that it is dropped last. 216 components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>>, 217 default_component: RutabagaComponentType, 218 capset_info: Vec<RutabagaCapsetInfo>, 219 fence_handler: RutabagaFenceHandler, 220 } 221 222 impl Rutabaga { capset_id_to_component_type(&self, capset_id: u32) -> RutabagaResult<RutabagaComponentType>223 fn capset_id_to_component_type(&self, capset_id: u32) -> RutabagaResult<RutabagaComponentType> { 224 let component = self 225 .capset_info 226 .iter() 227 .find(|capset_info| capset_info.capset_id == capset_id) 228 .ok_or(RutabagaError::InvalidCapset)? 229 .component; 230 231 Ok(component) 232 } 233 capset_index_to_component_info(&self, index: u32) -> RutabagaResult<RutabagaCapsetInfo>234 fn capset_index_to_component_info(&self, index: u32) -> RutabagaResult<RutabagaCapsetInfo> { 235 let idx = index as usize; 236 if idx >= self.capset_info.len() { 237 return Err(RutabagaError::InvalidCapset); 238 } 239 240 Ok(self.capset_info[idx]) 241 } 242 243 /// Gets the version and size for the capabilty set `index`. get_capset_info(&self, index: u32) -> RutabagaResult<(u32, u32, u32)>244 pub fn get_capset_info(&self, index: u32) -> RutabagaResult<(u32, u32, u32)> { 245 let capset_info = self.capset_index_to_component_info(index)?; 246 247 let component = self 248 .components 249 .get(&capset_info.component) 250 .ok_or(RutabagaError::InvalidComponent)?; 251 252 let (capset_version, capset_size) = component.get_capset_info(capset_info.capset_id); 253 Ok((capset_info.capset_id, capset_version, capset_size)) 254 } 255 256 /// Gets the capability set for the `capset_id` and `version`. 257 /// Each capability set is associated with a context type, which is associated 258 /// with a rutabaga component. get_capset(&self, capset_id: u32, version: u32) -> RutabagaResult<Vec<u8>>259 pub fn get_capset(&self, capset_id: u32, version: u32) -> RutabagaResult<Vec<u8>> { 260 // The default workaround is just until context types are fully supported in all 261 // Google kernels. 262 let component_type = self 263 .capset_id_to_component_type(capset_id) 264 .unwrap_or(self.default_component); 265 266 let component = self 267 .components 268 .get(&component_type) 269 .ok_or(RutabagaError::InvalidComponent)?; 270 271 Ok(component.get_capset(capset_id, version)) 272 } 273 274 /// Forces context zero for the default rutabaga component. force_ctx_0(&self)275 pub fn force_ctx_0(&self) { 276 if let Some(component) = self.components.get(&self.default_component) { 277 component.force_ctx_0(); 278 } 279 } 280 281 /// Creates a fence with the given `fence`. 282 /// If the flags include RUTABAGA_FLAG_INFO_RING_IDX, then the fence is created on a 283 /// specific timeline on the specific context. create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()>284 pub fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> { 285 if fence.flags & RUTABAGA_FLAG_INFO_RING_IDX != 0 { 286 let ctx = self 287 .contexts 288 .get_mut(&fence.ctx_id) 289 .ok_or(RutabagaError::InvalidContextId)?; 290 291 ctx.context_create_fence(fence)?; 292 } else { 293 let component = self 294 .components 295 .get_mut(&self.default_component) 296 .ok_or(RutabagaError::InvalidComponent)?; 297 298 component.create_fence(fence)?; 299 } 300 301 Ok(()) 302 } 303 304 /// Polls the default rutabaga component. In practice, only called if the default component is 305 /// virglrenderer. poll(&self)306 pub fn poll(&self) { 307 if let Some(component) = self.components.get(&self.default_component) { 308 component.poll(); 309 } 310 } 311 312 /// Returns a pollable descriptor for the default rutabaga component. In practice, it is only 313 /// not None if the default component is virglrenderer. poll_descriptor(&self) -> Option<SafeDescriptor>314 pub fn poll_descriptor(&self) -> Option<SafeDescriptor> { 315 let component = self.components.get(&self.default_component).or(None)?; 316 component.poll_descriptor() 317 } 318 319 /// Creates a resource with the `resource_create_3d` metadata. resource_create_3d( &mut self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<()>320 pub fn resource_create_3d( 321 &mut self, 322 resource_id: u32, 323 resource_create_3d: ResourceCreate3D, 324 ) -> RutabagaResult<()> { 325 let component = self 326 .components 327 .get_mut(&self.default_component) 328 .ok_or(RutabagaError::InvalidComponent)?; 329 330 if self.resources.contains_key(&resource_id) { 331 return Err(RutabagaError::InvalidResourceId); 332 } 333 334 let resource = component.create_3d(resource_id, resource_create_3d)?; 335 self.resources.insert(resource_id, resource); 336 Ok(()) 337 } 338 339 /// Attaches `vecs` to the resource. attach_backing( &mut self, resource_id: u32, mut vecs: Vec<RutabagaIovec>, ) -> RutabagaResult<()>340 pub fn attach_backing( 341 &mut self, 342 resource_id: u32, 343 mut vecs: Vec<RutabagaIovec>, 344 ) -> RutabagaResult<()> { 345 let component = self 346 .components 347 .get_mut(&self.default_component) 348 .ok_or(RutabagaError::InvalidComponent)?; 349 350 let mut resource = self 351 .resources 352 .get_mut(&resource_id) 353 .ok_or(RutabagaError::InvalidResourceId)?; 354 355 component.attach_backing(resource_id, &mut vecs)?; 356 resource.backing_iovecs = Some(vecs); 357 Ok(()) 358 } 359 360 /// Detaches any previously attached iovecs from the resource. detach_backing(&mut self, resource_id: u32) -> RutabagaResult<()>361 pub fn detach_backing(&mut self, resource_id: u32) -> RutabagaResult<()> { 362 let component = self 363 .components 364 .get_mut(&self.default_component) 365 .ok_or(RutabagaError::InvalidComponent)?; 366 367 let resource = self 368 .resources 369 .get_mut(&resource_id) 370 .ok_or(RutabagaError::InvalidResourceId)?; 371 372 component.detach_backing(resource_id); 373 resource.backing_iovecs = None; 374 Ok(()) 375 } 376 377 /// Releases guest kernel reference on the resource. unref_resource(&mut self, resource_id: u32) -> RutabagaResult<()>378 pub fn unref_resource(&mut self, resource_id: u32) -> RutabagaResult<()> { 379 let component = self 380 .components 381 .get_mut(&self.default_component) 382 .ok_or(RutabagaError::InvalidComponent)?; 383 384 self.resources 385 .remove(&resource_id) 386 .ok_or(RutabagaError::InvalidResourceId)?; 387 388 component.unref_resource(resource_id); 389 Ok(()) 390 } 391 392 /// For HOST3D_GUEST resources, copies from the attached iovecs to the host resource. For 393 /// 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<()>394 pub fn transfer_write( 395 &mut self, 396 ctx_id: u32, 397 resource_id: u32, 398 transfer: Transfer3D, 399 ) -> RutabagaResult<()> { 400 let component = self 401 .components 402 .get(&self.default_component) 403 .ok_or(RutabagaError::InvalidComponent)?; 404 405 let resource = self 406 .resources 407 .get_mut(&resource_id) 408 .ok_or(RutabagaError::InvalidResourceId)?; 409 410 component.transfer_write(ctx_id, resource, transfer) 411 } 412 413 /// 1) If specified, copies to `buf` from the host resource. 414 /// 2) Otherwise, for HOST3D_GUEST resources, copies to the attached iovecs from the host 415 /// resource. For HOST3D resources, this may invalidate caches, though this feature is 416 /// unused by guest userspace. transfer_read( &mut self, ctx_id: u32, resource_id: u32, transfer: Transfer3D, buf: Option<VolatileSlice>, ) -> RutabagaResult<()>417 pub fn transfer_read( 418 &mut self, 419 ctx_id: u32, 420 resource_id: u32, 421 transfer: Transfer3D, 422 buf: Option<VolatileSlice>, 423 ) -> RutabagaResult<()> { 424 let component = self 425 .components 426 .get(&self.default_component) 427 .ok_or(RutabagaError::InvalidComponent)?; 428 429 let resource = self 430 .resources 431 .get_mut(&resource_id) 432 .ok_or(RutabagaError::InvalidResourceId)?; 433 434 component.transfer_read(ctx_id, resource, transfer, buf) 435 } 436 437 /// Creates a blob resource with the `ctx_id` and `resource_create_blob` metadata. 438 /// Associates `iovecs` with the resource, if there are any. Associates externally 439 /// 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<()>440 pub fn resource_create_blob( 441 &mut self, 442 ctx_id: u32, 443 resource_id: u32, 444 resource_create_blob: ResourceCreateBlob, 445 iovecs: Option<Vec<RutabagaIovec>>, 446 handle: Option<RutabagaHandle>, 447 ) -> RutabagaResult<()> { 448 if self.resources.contains_key(&resource_id) { 449 return Err(RutabagaError::InvalidResourceId); 450 } 451 452 let component = self 453 .components 454 .get_mut(&self.default_component) 455 .ok_or(RutabagaError::InvalidComponent)?; 456 457 let mut context = None; 458 // For the cross-domain context, we'll need to create the blob resource via a home-grown 459 // rutabaga context rather than one from an external C/C++ component. Use `ctx_id` and 460 // the component type if it happens to be a cross-domain context. 461 if ctx_id > 0 { 462 let ctx = self 463 .contexts 464 .get_mut(&ctx_id) 465 .ok_or(RutabagaError::InvalidContextId)?; 466 467 if ctx.component_type() == RutabagaComponentType::CrossDomain { 468 context = Some(ctx); 469 } 470 } 471 472 let resource = match context { 473 Some(ctx) => ctx.context_create_blob(resource_id, resource_create_blob, handle)?, 474 None => { 475 component.create_blob(ctx_id, resource_id, resource_create_blob, iovecs, handle)? 476 } 477 }; 478 479 self.resources.insert(resource_id, resource); 480 Ok(()) 481 } 482 483 /// Returns a memory mapping of the blob resource. map(&self, resource_id: u32) -> RutabagaResult<ExternalMapping>484 pub fn map(&self, resource_id: u32) -> RutabagaResult<ExternalMapping> { 485 let component = self 486 .components 487 .get(&self.default_component) 488 .ok_or(RutabagaError::InvalidComponent)?; 489 490 if !self.resources.contains_key(&resource_id) { 491 return Err(RutabagaError::InvalidResourceId); 492 } 493 494 component.map(resource_id) 495 } 496 497 /// Returns the `map_info` of the blob resource. The valid values for `map_info` 498 /// are defined in the virtio-gpu spec. map_info(&self, resource_id: u32) -> RutabagaResult<u32>499 pub fn map_info(&self, resource_id: u32) -> RutabagaResult<u32> { 500 let resource = self 501 .resources 502 .get(&resource_id) 503 .ok_or(RutabagaError::InvalidResourceId)?; 504 505 resource 506 .map_info 507 .ok_or(RutabagaError::SpecViolation("no map info available")) 508 } 509 510 /// Returns the `vulkan_info` of the blob resource, which consists of the physical device 511 /// index and memory index associated with the resource. vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo>512 pub fn vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo> { 513 let resource = self 514 .resources 515 .get(&resource_id) 516 .ok_or(RutabagaError::InvalidResourceId)?; 517 518 resource.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo) 519 } 520 521 /// Returns the 3D info associated with the resource, if any. query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo>522 pub fn query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo> { 523 let resource = self 524 .resources 525 .get(&resource_id) 526 .ok_or(RutabagaError::InvalidResourceId)?; 527 528 resource 529 .info_3d 530 .ok_or(RutabagaError::SpecViolation("no 3d info available")) 531 } 532 533 /// Exports a blob resource. See virtio-gpu spec for blob flag use flags. export_blob(&mut self, resource_id: u32) -> RutabagaResult<RutabagaHandle>534 pub fn export_blob(&mut self, resource_id: u32) -> RutabagaResult<RutabagaHandle> { 535 let resource = self 536 .resources 537 .get_mut(&resource_id) 538 .ok_or(RutabagaError::InvalidResourceId)?; 539 540 // We can inspect blob flags only once guest minigbm is fully transitioned to blob. 541 let share_mask = RUTABAGA_BLOB_FLAG_USE_SHAREABLE | RUTABAGA_BLOB_FLAG_USE_CROSS_DEVICE; 542 let shareable = (resource.blob_flags & share_mask != 0) || !resource.blob; 543 544 let opt = resource.handle.take(); 545 546 match (opt, shareable) { 547 (Some(handle), true) => { 548 let clone = handle.try_clone()?; 549 resource.handle = Some(handle); 550 Ok(clone) 551 } 552 (Some(handle), false) => { 553 // Exactly one strong reference in this case. 554 let hnd = 555 Arc::try_unwrap(handle).map_err(|_| RutabagaError::InvalidRutabagaHandle)?; 556 Ok(hnd) 557 } 558 _ => Err(RutabagaError::InvalidRutabagaHandle), 559 } 560 } 561 562 /// Exports the given fence for import into other processes. export_fence(&self, fence_id: u32) -> RutabagaResult<RutabagaHandle>563 pub fn export_fence(&self, fence_id: u32) -> RutabagaResult<RutabagaHandle> { 564 let component = self 565 .components 566 .get(&self.default_component) 567 .ok_or(RutabagaError::InvalidComponent)?; 568 569 component.export_fence(fence_id) 570 } 571 572 /// Creates a context with the given `ctx_id` and `context_init` variable. 573 /// `context_init` is used to determine which rutabaga component creates the context. create_context(&mut self, ctx_id: u32, context_init: u32) -> RutabagaResult<()>574 pub fn create_context(&mut self, ctx_id: u32, context_init: u32) -> RutabagaResult<()> { 575 // The default workaround is just until context types are fully supported in all 576 // Google kernels. 577 let capset_id = context_init & RUTABAGA_CONTEXT_INIT_CAPSET_ID_MASK; 578 let component_type = self 579 .capset_id_to_component_type(capset_id) 580 .unwrap_or(self.default_component); 581 582 let component = self 583 .components 584 .get_mut(&component_type) 585 .ok_or(RutabagaError::InvalidComponent)?; 586 587 if self.contexts.contains_key(&ctx_id) { 588 return Err(RutabagaError::InvalidContextId); 589 } 590 591 let ctx = component.create_context(ctx_id, context_init, self.fence_handler.clone())?; 592 self.contexts.insert(ctx_id, ctx); 593 Ok(()) 594 } 595 596 /// Destroys the context given by `ctx_id`. destroy_context(&mut self, ctx_id: u32) -> RutabagaResult<()>597 pub fn destroy_context(&mut self, ctx_id: u32) -> RutabagaResult<()> { 598 self.contexts 599 .remove(&ctx_id) 600 .ok_or(RutabagaError::InvalidContextId)?; 601 Ok(()) 602 } 603 604 /// 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<()>605 pub fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> { 606 let ctx = self 607 .contexts 608 .get_mut(&ctx_id) 609 .ok_or(RutabagaError::InvalidContextId)?; 610 611 let resource = self 612 .resources 613 .get_mut(&resource_id) 614 .ok_or(RutabagaError::InvalidResourceId)?; 615 616 ctx.attach(resource); 617 Ok(()) 618 } 619 620 /// 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<()>621 pub fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> { 622 let ctx = self 623 .contexts 624 .get_mut(&ctx_id) 625 .ok_or(RutabagaError::InvalidContextId)?; 626 627 let resource = self 628 .resources 629 .get_mut(&resource_id) 630 .ok_or(RutabagaError::InvalidResourceId)?; 631 632 ctx.detach(resource); 633 Ok(()) 634 } 635 636 /// Submits `commands` to the context given by `ctx_id`. submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> RutabagaResult<()>637 pub fn submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> RutabagaResult<()> { 638 let ctx = self 639 .contexts 640 .get_mut(&ctx_id) 641 .ok_or(RutabagaError::InvalidContextId)?; 642 643 ctx.submit_cmd(commands) 644 } 645 } 646 647 /// Rutabaga Builder, following the Rust builder pattern. 648 pub struct RutabagaBuilder { 649 display_width: Option<u32>, 650 display_height: Option<u32>, 651 default_component: RutabagaComponentType, 652 virglrenderer_flags: Option<VirglRendererFlags>, 653 gfxstream_flags: Option<GfxstreamFlags>, 654 channels: Option<Vec<RutabagaChannel>>, 655 } 656 657 impl RutabagaBuilder { 658 /// Create new a RutabagaBuilder. new(default_component: RutabagaComponentType) -> RutabagaBuilder659 pub fn new(default_component: RutabagaComponentType) -> RutabagaBuilder { 660 RutabagaBuilder { 661 display_width: None, 662 display_height: None, 663 default_component, 664 virglrenderer_flags: None, 665 gfxstream_flags: None, 666 channels: None, 667 } 668 } 669 670 /// Set display width for the RutabagaBuilder set_display_width(mut self, display_width: u32) -> RutabagaBuilder671 pub fn set_display_width(mut self, display_width: u32) -> RutabagaBuilder { 672 self.display_width = Some(display_width); 673 self 674 } 675 676 /// Set display height for the RutabagaBuilder set_display_height(mut self, display_height: u32) -> RutabagaBuilder677 pub fn set_display_height(mut self, display_height: u32) -> RutabagaBuilder { 678 self.display_height = Some(display_height); 679 self 680 } 681 682 /// Set virglrenderer flags for the RutabagaBuilder set_virglrenderer_flags( mut self, virglrenderer_flags: VirglRendererFlags, ) -> RutabagaBuilder683 pub fn set_virglrenderer_flags( 684 mut self, 685 virglrenderer_flags: VirglRendererFlags, 686 ) -> RutabagaBuilder { 687 self.virglrenderer_flags = Some(virglrenderer_flags); 688 self 689 } 690 691 /// Set gfxstream flags for the RutabagaBuilder set_gfxstream_flags(mut self, gfxstream_flags: GfxstreamFlags) -> RutabagaBuilder692 pub fn set_gfxstream_flags(mut self, gfxstream_flags: GfxstreamFlags) -> RutabagaBuilder { 693 self.gfxstream_flags = Some(gfxstream_flags); 694 self 695 } 696 697 /// Set rutabaga channels for the RutabagaBuilder set_rutabaga_channels( mut self, channels: Option<Vec<RutabagaChannel>>, ) -> RutabagaBuilder698 pub fn set_rutabaga_channels( 699 mut self, 700 channels: Option<Vec<RutabagaChannel>>, 701 ) -> RutabagaBuilder { 702 self.channels = channels; 703 self 704 } 705 706 /// Builds Rutabaga and returns a handle to it. 707 /// 708 /// This should be only called once per every virtual machine instance. Rutabaga tries to 709 /// intialize all 3D components which have been built. In 2D mode, only the 2D component is 710 /// initialized. build( self, fence_handler: RutabagaFenceHandler, render_server_fd: Option<SafeDescriptor>, ) -> RutabagaResult<Rutabaga>711 pub fn build( 712 self, 713 fence_handler: RutabagaFenceHandler, 714 render_server_fd: Option<SafeDescriptor>, 715 ) -> RutabagaResult<Rutabaga> { 716 let mut rutabaga_components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>> = 717 Default::default(); 718 719 let mut rutabaga_capsets: Vec<RutabagaCapsetInfo> = Default::default(); 720 721 // Make sure that disabled components are not used as default. 722 #[cfg(not(feature = "virgl_renderer"))] 723 if self.default_component == RutabagaComponentType::VirglRenderer { 724 return Err(RutabagaError::InvalidRutabagaBuild( 725 "virgl renderer feature not enabled", 726 )); 727 } 728 #[cfg(not(feature = "gfxstream"))] 729 if self.default_component == RutabagaComponentType::Gfxstream { 730 return Err(RutabagaError::InvalidRutabagaBuild( 731 "gfxstream feature not enabled", 732 )); 733 } 734 735 #[cfg(not(feature = "virgl_renderer_next"))] 736 if render_server_fd.is_some() { 737 return Err(RutabagaError::InvalidRutabagaBuild( 738 "render server FD is not supported with virgl_renderer_next feature", 739 )); 740 } 741 742 if self.default_component == RutabagaComponentType::Rutabaga2D { 743 let rutabaga_2d = Rutabaga2D::init(fence_handler.clone())?; 744 rutabaga_components.insert(RutabagaComponentType::Rutabaga2D, rutabaga_2d); 745 } else { 746 #[cfg(feature = "virgl_renderer")] 747 if self.default_component == RutabagaComponentType::VirglRenderer { 748 let virglrenderer_flags = 749 self.virglrenderer_flags 750 .ok_or(RutabagaError::InvalidRutabagaBuild( 751 "missing virgl renderer flags", 752 ))?; 753 754 let virgl = VirglRenderer::init( 755 virglrenderer_flags, 756 fence_handler.clone(), 757 render_server_fd, 758 )?; 759 rutabaga_components.insert(RutabagaComponentType::VirglRenderer, virgl); 760 761 rutabaga_capsets.push(RutabagaCapsetInfo { 762 capset_id: RUTABAGA_CAPSET_VIRGL, 763 component: RutabagaComponentType::VirglRenderer, 764 }); 765 rutabaga_capsets.push(RutabagaCapsetInfo { 766 capset_id: RUTABAGA_CAPSET_VIRGL2, 767 component: RutabagaComponentType::VirglRenderer, 768 }); 769 rutabaga_capsets.push(RutabagaCapsetInfo { 770 capset_id: RUTABAGA_CAPSET_VENUS, 771 component: RutabagaComponentType::VirglRenderer, 772 }); 773 } 774 775 #[cfg(feature = "gfxstream")] 776 if self.default_component == RutabagaComponentType::Gfxstream { 777 let display_width = self 778 .display_width 779 .ok_or(RutabagaError::InvalidRutabagaBuild("missing display width"))?; 780 let display_height = 781 self.display_height 782 .ok_or(RutabagaError::InvalidRutabagaBuild( 783 "missing display height", 784 ))?; 785 786 let gfxstream_flags = 787 self.gfxstream_flags 788 .ok_or(RutabagaError::InvalidRutabagaBuild( 789 "missing gfxstream flags", 790 ))?; 791 792 let gfxstream = Gfxstream::init( 793 display_width, 794 display_height, 795 gfxstream_flags, 796 fence_handler.clone(), 797 )?; 798 rutabaga_components.insert(RutabagaComponentType::Gfxstream, gfxstream); 799 800 rutabaga_capsets.push(RutabagaCapsetInfo { 801 capset_id: RUTABAGA_CAPSET_GFXSTREAM, 802 component: RutabagaComponentType::Gfxstream, 803 }); 804 } 805 806 let cross_domain = CrossDomain::init(self.channels)?; 807 rutabaga_components.insert(RutabagaComponentType::CrossDomain, cross_domain); 808 809 rutabaga_capsets.push(RutabagaCapsetInfo { 810 capset_id: RUTABAGA_CAPSET_CROSS_DOMAIN, 811 component: RutabagaComponentType::CrossDomain, 812 }); 813 } 814 815 Ok(Rutabaga { 816 resources: Default::default(), 817 contexts: Default::default(), 818 components: rutabaga_components, 819 default_component: self.default_component, 820 capset_info: rutabaga_capsets, 821 fence_handler, 822 }) 823 } 824 } 825