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