use std::collections::BTreeMap as Map;
use std::convert::TryInto;
use std::io::IoSliceMut;
use std::io::Read;
use std::io::Write;
use std::sync::Arc;
use serde::Deserialize;
use serde::Serialize;
use crate::cross_domain::CrossDomain;
#[cfg(feature = "gfxstream")]
use crate::gfxstream::Gfxstream;
use crate::rutabaga_2d::Rutabaga2D;
use crate::rutabaga_os::MemoryMapping;
use crate::rutabaga_os::OwnedDescriptor;
use crate::rutabaga_utils::*;
#[cfg(feature = "virgl_renderer")]
use crate::virgl_renderer::VirglRenderer;
const RUTABAGA_DEFAULT_WIDTH: u32 = 1280;
const RUTABAGA_DEFAULT_HEIGHT: u32 = 1024;
pub struct Rutabaga2DInfo {
pub width: u32,
pub height: u32,
pub host_mem: Vec<u8>,
}
pub struct RutabagaResource {
pub resource_id: u32,
pub handle: Option<Arc<RutabagaHandle>>,
pub blob: bool,
pub blob_mem: u32,
pub blob_flags: u32,
pub map_info: Option<u32>,
pub info_2d: Option<Rutabaga2DInfo>,
pub info_3d: Option<Resource3DInfo>,
pub vulkan_info: Option<VulkanInfo>,
pub backing_iovecs: Option<Vec<RutabagaIovec>>,
pub component_mask: u8,
pub size: u64,
pub mapping: Option<MemoryMapping>,
}
#[derive(Deserialize, Serialize)]
pub struct RutabagaResourceSnapshot {
pub resource_id: u32,
pub width: u32,
pub height: u32,
}
impl TryFrom<&RutabagaResource> for RutabagaResourceSnapshot {
type Error = RutabagaError;
fn try_from(resource: &RutabagaResource) -> Result<Self, Self::Error> {
let info = resource
.info_2d
.as_ref()
.ok_or(RutabagaError::Unsupported)?;
assert_eq!(
usize::try_from(info.width * info.height * 4).unwrap(),
info.host_mem.len()
);
assert_eq!(usize::try_from(resource.size).unwrap(), info.host_mem.len());
Ok(RutabagaResourceSnapshot {
resource_id: resource.resource_id,
width: info.width,
height: info.height,
})
}
}
impl TryFrom<RutabagaResourceSnapshot> for RutabagaResource {
type Error = RutabagaError;
fn try_from(snapshot: RutabagaResourceSnapshot) -> Result<Self, Self::Error> {
let size = u64::from(snapshot.width * snapshot.height * 4);
Ok(RutabagaResource {
resource_id: snapshot.resource_id,
handle: None,
blob: false,
blob_mem: 0,
blob_flags: 0,
map_info: None,
info_2d: Some(Rutabaga2DInfo {
width: snapshot.width,
height: snapshot.height,
host_mem: vec![0; usize::try_from(size).unwrap()],
}),
info_3d: None,
vulkan_info: None,
backing_iovecs: None,
component_mask: 1 << (RutabagaComponentType::Rutabaga2D as u8),
size,
mapping: None,
})
}
}
pub trait RutabagaComponent {
fn get_capset_info(&self, _capset_id: u32) -> (u32, u32) {
(0, 0)
}
fn get_capset(&self, _capset_id: u32, _version: u32) -> Vec<u8> {
Vec::new()
}
fn force_ctx_0(&self) {}
fn create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()> {
Ok(())
}
fn event_poll(&self) {}
fn poll_descriptor(&self) -> Option<OwnedDescriptor> {
None
}
fn create_3d(
&self,
resource_id: u32,
_resource_create_3d: ResourceCreate3D,
) -> RutabagaResult<RutabagaResource> {
Ok(RutabagaResource {
resource_id,
handle: None,
blob: false,
blob_mem: 0,
blob_flags: 0,
map_info: None,
info_2d: None,
info_3d: None,
vulkan_info: None,
backing_iovecs: None,
component_mask: 0,
size: 0,
mapping: None,
})
}
fn attach_backing(
&self,
_resource_id: u32,
_vecs: &mut Vec<RutabagaIovec>,
) -> RutabagaResult<()> {
Ok(())
}
fn detach_backing(&self, _resource_id: u32) {}
fn unref_resource(&self, _resource_id: u32) {}
fn transfer_write(
&self,
_ctx_id: u32,
_resource: &mut RutabagaResource,
_transfer: Transfer3D,
) -> RutabagaResult<()> {
Ok(())
}
fn transfer_read(
&self,
_ctx_id: u32,
_resource: &mut RutabagaResource,
_transfer: Transfer3D,
_buf: Option<IoSliceMut>,
) -> RutabagaResult<()> {
Ok(())
}
fn resource_flush(&self, _resource_id: &mut RutabagaResource) -> RutabagaResult<()> {
Err(RutabagaError::Unsupported)
}
fn 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> {
Err(RutabagaError::Unsupported)
}
fn map(&self, _resource_id: u32) -> RutabagaResult<RutabagaMapping> {
Err(RutabagaError::Unsupported)
}
fn unmap(&self, _resource_id: u32) -> RutabagaResult<()> {
Err(RutabagaError::Unsupported)
}
fn export_fence(&self, _fence_id: u64) -> RutabagaResult<RutabagaHandle> {
Err(RutabagaError::Unsupported)
}
fn create_context(
&self,
_ctx_id: u32,
_context_init: u32,
_context_name: Option<&str>,
_fence_handler: RutabagaFenceHandler,
) -> RutabagaResult<Box<dyn RutabagaContext>> {
Err(RutabagaError::Unsupported)
}
fn suspend(&self) -> RutabagaResult<()> {
Ok(())
}
fn snapshot(&self, _directory: &str) -> RutabagaResult<()> {
Err(RutabagaError::Unsupported)
}
fn restore(&self, _directory: &str) -> RutabagaResult<()> {
Err(RutabagaError::Unsupported)
}
fn resume(&self) -> RutabagaResult<()> {
Ok(())
}
fn wait_sync(&self, _resource: &RutabagaResource) -> RutabagaResult<()> {
Err(RutabagaError::Unsupported)
}
}
pub trait RutabagaContext {
fn context_create_blob(
&mut self,
_resource_id: u32,
_resource_create_blob: ResourceCreateBlob,
_handle_opt: Option<RutabagaHandle>,
) -> RutabagaResult<RutabagaResource> {
Err(RutabagaError::Unsupported)
}
fn submit_cmd(
&mut self,
_commands: &mut [u8],
_fence_ids: &[u64],
shareable_fences: Vec<RutabagaHandle>,
) -> RutabagaResult<()>;
fn attach(&mut self, _resource: &mut RutabagaResource);
fn detach(&mut self, _resource: &RutabagaResource);
fn context_create_fence(
&mut self,
_fence: RutabagaFence,
) -> RutabagaResult<Option<RutabagaHandle>> {
Err(RutabagaError::Unsupported)
}
fn component_type(&self) -> RutabagaComponentType;
}
#[derive(Copy, Clone)]
struct RutabagaCapsetInfo {
pub capset_id: u32,
pub component: RutabagaComponentType,
pub name: &'static str,
}
const RUTABAGA_CAPSETS: [RutabagaCapsetInfo; 9] = [
RutabagaCapsetInfo {
capset_id: RUTABAGA_CAPSET_VIRGL,
component: RutabagaComponentType::VirglRenderer,
name: "virgl",
},
RutabagaCapsetInfo {
capset_id: RUTABAGA_CAPSET_VIRGL2,
component: RutabagaComponentType::VirglRenderer,
name: "virgl2",
},
RutabagaCapsetInfo {
capset_id: RUTABAGA_CAPSET_GFXSTREAM_VULKAN,
component: RutabagaComponentType::Gfxstream,
name: "gfxstream-vulkan",
},
RutabagaCapsetInfo {
capset_id: RUTABAGA_CAPSET_VENUS,
component: RutabagaComponentType::VirglRenderer,
name: "venus",
},
RutabagaCapsetInfo {
capset_id: RUTABAGA_CAPSET_CROSS_DOMAIN,
component: RutabagaComponentType::CrossDomain,
name: "cross-domain",
},
RutabagaCapsetInfo {
capset_id: RUTABAGA_CAPSET_DRM,
component: RutabagaComponentType::VirglRenderer,
name: "drm",
},
RutabagaCapsetInfo {
capset_id: RUTABAGA_CAPSET_GFXSTREAM_MAGMA,
component: RutabagaComponentType::Gfxstream,
name: "gfxstream-magma",
},
RutabagaCapsetInfo {
capset_id: RUTABAGA_CAPSET_GFXSTREAM_GLES,
component: RutabagaComponentType::Gfxstream,
name: "gfxstream-gles",
},
RutabagaCapsetInfo {
capset_id: RUTABAGA_CAPSET_GFXSTREAM_COMPOSER,
component: RutabagaComponentType::Gfxstream,
name: "gfxstream-composer",
},
];
pub fn calculate_capset_mask<'a, I: Iterator<Item = &'a str>>(context_names: I) -> u64 {
let mut capset_mask = 0;
for name in context_names {
if let Some(capset) = RUTABAGA_CAPSETS.iter().find(|capset| capset.name == name) {
capset_mask |= 1 << capset.capset_id;
};
}
capset_mask
}
pub fn calculate_capset_names(capset_mask: u64) -> Vec<String> {
RUTABAGA_CAPSETS
.iter()
.filter(|capset| capset_mask & (1 << capset.capset_id) != 0)
.map(|capset| capset.name.to_string())
.collect()
}
fn calculate_component(component_mask: u8) -> RutabagaResult<RutabagaComponentType> {
if component_mask.count_ones() != 1 {
return Err(RutabagaError::SpecViolation("can't infer single component"));
}
match component_mask.trailing_zeros() {
0 => Ok(RutabagaComponentType::Rutabaga2D),
1 => Ok(RutabagaComponentType::VirglRenderer),
2 => Ok(RutabagaComponentType::Gfxstream),
3 => Ok(RutabagaComponentType::CrossDomain),
_ => Err(RutabagaError::InvalidComponent),
}
}
pub struct Rutabaga {
resources: Map<u32, RutabagaResource>,
#[cfg(fence_passing_option1)]
shareable_fences: Map<u64, RutabagaHandle>,
contexts: Map<u32, Box<dyn RutabagaContext>>,
components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>>,
default_component: RutabagaComponentType,
capset_info: Vec<RutabagaCapsetInfo>,
fence_handler: RutabagaFenceHandler,
}
#[derive(Deserialize, Serialize)]
pub struct RutabagaSnapshot {
pub resources: Map<u32, RutabagaResourceSnapshot>,
}
impl Rutabaga {
pub fn suspend(&self) -> RutabagaResult<()> {
let component = self
.components
.get(&self.default_component)
.ok_or(RutabagaError::InvalidComponent)?;
component.suspend()
}
pub fn snapshot(&self, w: &mut impl Write, directory: &str) -> RutabagaResult<()> {
if self.default_component == RutabagaComponentType::Gfxstream {
let component = self
.components
.get(&self.default_component)
.ok_or(RutabagaError::InvalidComponent)?;
component.snapshot(directory)
} else if self.default_component == RutabagaComponentType::Rutabaga2D {
let snapshot = RutabagaSnapshot {
resources: self
.resources
.iter()
.map(|(i, r)| Ok((*i, RutabagaResourceSnapshot::try_from(r)?)))
.collect::<RutabagaResult<_>>()?,
};
serde_json::to_writer(w, &snapshot).map_err(|e| RutabagaError::IoError(e.into()))
} else {
Err(RutabagaError::Unsupported)
}
}
pub fn restore(&mut self, r: &mut impl Read, directory: &str) -> RutabagaResult<()> {
if self.default_component == RutabagaComponentType::Gfxstream {
let component = self
.components
.get_mut(&self.default_component)
.ok_or(RutabagaError::InvalidComponent)?;
component.restore(directory)
} else if self.default_component == RutabagaComponentType::Rutabaga2D {
let snapshot: RutabagaSnapshot =
serde_json::from_reader(r).map_err(|e| RutabagaError::IoError(e.into()))?;
self.resources = snapshot
.resources
.into_iter()
.map(|(i, s)| Ok((i, RutabagaResource::try_from(s)?)))
.collect::<RutabagaResult<_>>()?;
return Ok(());
} else {
Err(RutabagaError::Unsupported)
}
}
pub fn resume(&self) -> RutabagaResult<()> {
let component = self
.components
.get(&self.default_component)
.ok_or(RutabagaError::InvalidComponent)?;
component.resume()
}
fn capset_id_to_component_type(&self, capset_id: u32) -> RutabagaResult<RutabagaComponentType> {
let component = self
.capset_info
.iter()
.find(|capset_info| capset_info.capset_id == capset_id)
.ok_or(RutabagaError::InvalidCapset)?
.component;
Ok(component)
}
fn capset_index_to_component_info(&self, index: u32) -> RutabagaResult<RutabagaCapsetInfo> {
let idx = index as usize;
if idx >= self.capset_info.len() {
return Err(RutabagaError::InvalidCapset);
}
Ok(self.capset_info[idx])
}
pub fn get_capset_info(&self, index: u32) -> RutabagaResult<(u32, u32, u32)> {
let capset_info = self.capset_index_to_component_info(index)?;
let component = self
.components
.get(&capset_info.component)
.ok_or(RutabagaError::InvalidComponent)?;
let (capset_version, capset_size) = component.get_capset_info(capset_info.capset_id);
Ok((capset_info.capset_id, capset_version, capset_size))
}
pub fn get_capset(&self, capset_id: u32, version: u32) -> RutabagaResult<Vec<u8>> {
let component_type = self
.capset_id_to_component_type(capset_id)
.unwrap_or(self.default_component);
let component = self
.components
.get(&component_type)
.ok_or(RutabagaError::InvalidComponent)?;
Ok(component.get_capset(capset_id, version))
}
pub fn get_num_capsets(&self) -> u32 {
self.capset_info.len() as u32
}
pub fn force_ctx_0(&self) {
if let Some(component) = self.components.get(&self.default_component) {
component.force_ctx_0();
}
}
pub fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
if fence.flags & RUTABAGA_FLAG_INFO_RING_IDX != 0 {
let ctx = self
.contexts
.get_mut(&fence.ctx_id)
.ok_or(RutabagaError::InvalidContextId)?;
#[allow(unused_variables)]
let handle_opt = ctx.context_create_fence(fence)?;
#[cfg(fence_passing_option1)]
if fence.flags & RUTABAGA_FLAG_FENCE_HOST_SHAREABLE != 0 {
let handle = handle_opt.unwrap();
self.shareable_fences.insert(fence.fence_id, handle);
}
} else {
let component = self
.components
.get_mut(&self.default_component)
.ok_or(RutabagaError::InvalidComponent)?;
component.create_fence(fence)?;
}
Ok(())
}
pub fn event_poll(&self) {
if let Some(component) = self.components.get(&self.default_component) {
component.event_poll();
}
}
pub fn poll_descriptor(&self) -> Option<OwnedDescriptor> {
let component = self.components.get(&self.default_component).or(None)?;
component.poll_descriptor()
}
pub fn resource_create_3d(
&mut self,
resource_id: u32,
resource_create_3d: ResourceCreate3D,
) -> RutabagaResult<()> {
let component = self
.components
.get_mut(&self.default_component)
.ok_or(RutabagaError::InvalidComponent)?;
if self.resources.contains_key(&resource_id) {
return Err(RutabagaError::InvalidResourceId);
}
let resource = component.create_3d(resource_id, resource_create_3d)?;
self.resources.insert(resource_id, resource);
Ok(())
}
pub fn attach_backing(
&mut self,
resource_id: u32,
mut vecs: Vec<RutabagaIovec>,
) -> RutabagaResult<()> {
let component = self
.components
.get_mut(&self.default_component)
.ok_or(RutabagaError::InvalidComponent)?;
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
component.attach_backing(resource_id, &mut vecs)?;
resource.backing_iovecs = Some(vecs);
Ok(())
}
pub fn detach_backing(&mut self, resource_id: u32) -> RutabagaResult<()> {
let component = self
.components
.get_mut(&self.default_component)
.ok_or(RutabagaError::InvalidComponent)?;
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
component.detach_backing(resource_id);
resource.backing_iovecs = None;
Ok(())
}
pub fn unref_resource(&mut self, resource_id: u32) -> RutabagaResult<()> {
let component = self
.components
.get_mut(&self.default_component)
.ok_or(RutabagaError::InvalidComponent)?;
self.resources
.remove(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
component.unref_resource(resource_id);
Ok(())
}
pub fn transfer_write(
&mut self,
ctx_id: u32,
resource_id: u32,
transfer: Transfer3D,
) -> RutabagaResult<()> {
let component = self
.components
.get(&self.default_component)
.ok_or(RutabagaError::InvalidComponent)?;
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
component.transfer_write(ctx_id, resource, transfer)
}
pub fn transfer_read(
&mut self,
ctx_id: u32,
resource_id: u32,
transfer: Transfer3D,
buf: Option<IoSliceMut>,
) -> RutabagaResult<()> {
let component = self
.components
.get(&self.default_component)
.ok_or(RutabagaError::InvalidComponent)?;
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
component.transfer_read(ctx_id, resource, transfer, buf)
}
pub fn resource_flush(&mut self, resource_id: u32) -> RutabagaResult<()> {
let component = self
.components
.get(&self.default_component)
.ok_or(RutabagaError::Unsupported)?;
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
component.resource_flush(resource)
}
pub fn resource_create_blob(
&mut self,
ctx_id: u32,
resource_id: u32,
resource_create_blob: ResourceCreateBlob,
iovecs: Option<Vec<RutabagaIovec>>,
handle: Option<RutabagaHandle>,
) -> RutabagaResult<()> {
if self.resources.contains_key(&resource_id) {
return Err(RutabagaError::InvalidResourceId);
}
let component = self
.components
.get_mut(&self.default_component)
.ok_or(RutabagaError::InvalidComponent)?;
let mut context = None;
if ctx_id > 0 {
let ctx = self
.contexts
.get_mut(&ctx_id)
.ok_or(RutabagaError::InvalidContextId)?;
if ctx.component_type() == RutabagaComponentType::CrossDomain {
context = Some(ctx);
}
}
let resource = match context {
Some(ctx) => ctx.context_create_blob(resource_id, resource_create_blob, handle)?,
None => {
component.create_blob(ctx_id, resource_id, resource_create_blob, iovecs, handle)?
}
};
self.resources.insert(resource_id, resource);
Ok(())
}
pub fn map(&mut self, resource_id: u32) -> RutabagaResult<RutabagaMapping> {
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
let component_type = calculate_component(resource.component_mask)?;
if component_type == RutabagaComponentType::CrossDomain {
let handle_opt = resource.handle.take();
match handle_opt {
Some(handle) => {
if handle.handle_type != RUTABAGA_MEM_HANDLE_TYPE_SHM {
return Err(RutabagaError::SpecViolation(
"expected a shared memory handle",
));
}
let clone = handle.try_clone()?;
let resource_size: usize = resource.size.try_into()?;
let map_info = resource
.map_info
.ok_or(RutabagaError::SpecViolation("no map info available"))?;
let mapping = MemoryMapping::from_safe_descriptor(
clone.os_handle,
resource_size,
map_info,
)?;
let rutabaga_mapping = mapping.as_rutabaga_mapping();
resource.handle = Some(handle);
resource.mapping = Some(mapping);
return Ok(rutabaga_mapping);
}
None => return Err(RutabagaError::SpecViolation("expected a handle to map")),
}
}
let component = self
.components
.get(&component_type)
.ok_or(RutabagaError::InvalidComponent)?;
component.map(resource_id)
}
pub fn unmap(&mut self, resource_id: u32) -> RutabagaResult<()> {
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
let component_type = calculate_component(resource.component_mask)?;
if component_type == RutabagaComponentType::CrossDomain {
resource.mapping = None;
return Ok(());
}
let component = self
.components
.get(&component_type)
.ok_or(RutabagaError::InvalidComponent)?;
component.unmap(resource_id)
}
pub fn map_info(&self, resource_id: u32) -> RutabagaResult<u32> {
let resource = self
.resources
.get(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
resource
.map_info
.ok_or(RutabagaError::SpecViolation("no map info available"))
}
pub fn vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo> {
let resource = self
.resources
.get(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
resource.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo)
}
pub fn query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo> {
let resource = self
.resources
.get(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
resource
.info_3d
.ok_or(RutabagaError::SpecViolation("no 3d info available"))
}
pub fn export_blob(&mut self, resource_id: u32) -> RutabagaResult<RutabagaHandle> {
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
let share_mask = RUTABAGA_BLOB_FLAG_USE_SHAREABLE | RUTABAGA_BLOB_FLAG_USE_CROSS_DEVICE;
let shareable = (resource.blob_flags & share_mask != 0) || !resource.blob;
let opt = resource.handle.take();
match (opt, shareable) {
(Some(handle), true) => {
let clone = handle.try_clone()?;
resource.handle = Some(handle);
Ok(clone)
}
(Some(handle), false) => {
let hnd =
Arc::try_unwrap(handle).map_err(|_| RutabagaError::InvalidRutabagaHandle)?;
Ok(hnd)
}
_ => Err(RutabagaError::InvalidRutabagaHandle),
}
}
pub fn export_fence(&mut self, fence_id: u64) -> RutabagaResult<RutabagaHandle> {
#[cfg(fence_passing_option1)]
if let Some(handle) = self.shareable_fences.get_mut(&fence_id) {
return handle.try_clone();
}
let component = self
.components
.get(&self.default_component)
.ok_or(RutabagaError::InvalidComponent)?;
component.export_fence(fence_id)
}
pub fn create_context(
&mut self,
ctx_id: u32,
context_init: u32,
context_name: Option<&str>,
) -> RutabagaResult<()> {
let capset_id = context_init & RUTABAGA_CONTEXT_INIT_CAPSET_ID_MASK;
let component_type = self
.capset_id_to_component_type(capset_id)
.unwrap_or(self.default_component);
let component = self
.components
.get_mut(&component_type)
.ok_or(RutabagaError::InvalidComponent)?;
if self.contexts.contains_key(&ctx_id) {
return Err(RutabagaError::InvalidContextId);
}
let ctx = component.create_context(
ctx_id,
context_init,
context_name,
self.fence_handler.clone(),
)?;
self.contexts.insert(ctx_id, ctx);
Ok(())
}
pub fn destroy_context(&mut self, ctx_id: u32) -> RutabagaResult<()> {
self.contexts
.remove(&ctx_id)
.ok_or(RutabagaError::InvalidContextId)?;
Ok(())
}
pub fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> {
let ctx = self
.contexts
.get_mut(&ctx_id)
.ok_or(RutabagaError::InvalidContextId)?;
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
ctx.attach(resource);
Ok(())
}
pub fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> {
let ctx = self
.contexts
.get_mut(&ctx_id)
.ok_or(RutabagaError::InvalidContextId)?;
let resource = self
.resources
.get_mut(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
ctx.detach(resource);
Ok(())
}
pub fn submit_command(
&mut self,
ctx_id: u32,
commands: &mut [u8],
fence_ids: &[u64],
) -> RutabagaResult<()> {
let ctx = self
.contexts
.get_mut(&ctx_id)
.ok_or(RutabagaError::InvalidContextId)?;
#[allow(unused_mut)]
let mut shareable_fences: Vec<RutabagaHandle> = Vec::with_capacity(fence_ids.len());
#[cfg(fence_passing_option1)]
for (i, fence_id) in fence_ids.iter().enumerate() {
let handle = self
.shareable_fences
.get_mut(fence_id)
.ok_or(RutabagaError::InvalidRutabagaHandle)?;
let clone = handle.try_clone()?;
shareable_fences.insert(i, clone);
}
ctx.submit_cmd(commands, fence_ids, shareable_fences)
}
#[cfg(fence_passing_option1)]
pub fn destroy_fences(&mut self, fence_ids: &[u64]) -> RutabagaResult<()> {
for fence_id in fence_ids {
self.shareable_fences
.remove(fence_id)
.ok_or(RutabagaError::InvalidRutabagaHandle)?;
}
Ok(())
}
pub fn wait_sync(&mut self, resource_id: u32) -> RutabagaResult<()> {
let component = self
.components
.get_mut(&self.default_component)
.ok_or(RutabagaError::InvalidComponent)?;
let resource = self
.resources
.get(&resource_id)
.ok_or(RutabagaError::InvalidResourceId)?;
component.wait_sync(resource)?;
Ok(())
}
}
#[derive(Clone)]
pub struct RutabagaBuilder {
display_width: u32,
display_height: u32,
default_component: RutabagaComponentType,
gfxstream_flags: GfxstreamFlags,
virglrenderer_flags: VirglRendererFlags,
capset_mask: u64,
channels: Option<Vec<RutabagaChannel>>,
debug_handler: Option<RutabagaDebugHandler>,
renderer_features: Option<String>,
}
impl RutabagaBuilder {
pub fn new(default_component: RutabagaComponentType, capset_mask: u64) -> RutabagaBuilder {
let virglrenderer_flags = VirglRendererFlags::new()
.use_thread_sync(true)
.use_async_fence_cb(true);
let gfxstream_flags = GfxstreamFlags::new();
RutabagaBuilder {
display_width: RUTABAGA_DEFAULT_WIDTH,
display_height: RUTABAGA_DEFAULT_HEIGHT,
default_component,
gfxstream_flags,
virglrenderer_flags,
capset_mask,
channels: None,
debug_handler: None,
renderer_features: None,
}
}
pub fn set_display_width(mut self, display_width: u32) -> RutabagaBuilder {
self.display_width = display_width;
self
}
pub fn set_display_height(mut self, display_height: u32) -> RutabagaBuilder {
self.display_height = display_height;
self
}
pub fn set_use_egl(mut self, v: bool) -> RutabagaBuilder {
self.gfxstream_flags = self.gfxstream_flags.use_egl(v);
self.virglrenderer_flags = self.virglrenderer_flags.use_egl(v);
self
}
pub fn set_use_gles(mut self, v: bool) -> RutabagaBuilder {
self.gfxstream_flags = self.gfxstream_flags.use_gles(v);
self.virglrenderer_flags = self.virglrenderer_flags.use_gles(v);
self
}
pub fn set_use_glx(mut self, v: bool) -> RutabagaBuilder {
self.gfxstream_flags = self.gfxstream_flags.use_glx(v);
self.virglrenderer_flags = self.virglrenderer_flags.use_glx(v);
self
}
pub fn set_use_surfaceless(mut self, v: bool) -> RutabagaBuilder {
self.gfxstream_flags = self.gfxstream_flags.use_surfaceless(v);
self.virglrenderer_flags = self.virglrenderer_flags.use_surfaceless(v);
self
}
pub fn set_use_vulkan(mut self, v: bool) -> RutabagaBuilder {
self.gfxstream_flags = self.gfxstream_flags.use_vulkan(v);
self.virglrenderer_flags = self.virglrenderer_flags.use_venus(v);
self
}
pub fn set_use_external_blob(mut self, v: bool) -> RutabagaBuilder {
self.gfxstream_flags = self.gfxstream_flags.use_external_blob(v);
self.virglrenderer_flags = self.virglrenderer_flags.use_external_blob(v);
self
}
pub fn set_use_system_blob(mut self, v: bool) -> RutabagaBuilder {
self.gfxstream_flags = self.gfxstream_flags.use_system_blob(v);
self
}
pub fn set_use_render_server(mut self, v: bool) -> RutabagaBuilder {
self.virglrenderer_flags = self.virglrenderer_flags.use_render_server(v);
self
}
pub fn set_wsi(mut self, v: RutabagaWsi) -> RutabagaBuilder {
self.gfxstream_flags = self.gfxstream_flags.set_wsi(v);
self
}
pub fn set_rutabaga_channels(
mut self,
channels: Option<Vec<RutabagaChannel>>,
) -> RutabagaBuilder {
self.channels = channels;
self
}
pub fn set_debug_handler(
mut self,
debug_handler: Option<RutabagaDebugHandler>,
) -> RutabagaBuilder {
self.debug_handler = debug_handler;
self
}
pub fn set_renderer_features(mut self, renderer_features: Option<String>) -> RutabagaBuilder {
self.renderer_features = renderer_features;
self
}
pub fn build(
mut self,
fence_handler: RutabagaFenceHandler,
#[allow(unused_variables)] rutabaga_server_descriptor: Option<OwnedDescriptor>,
) -> RutabagaResult<Rutabaga> {
let mut rutabaga_components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>> =
Default::default();
#[allow(unused_mut)]
let mut rutabaga_capsets: Vec<RutabagaCapsetInfo> = Default::default();
let capset_enabled =
|capset_id: u32| -> bool { (self.capset_mask & (1 << capset_id)) != 0 };
let mut push_capset = |capset_id: u32| {
if let Some(capset) = RUTABAGA_CAPSETS
.iter()
.find(|capset| capset_id == capset.capset_id)
{
if self.capset_mask != 0 {
if capset_enabled(capset.capset_id) {
rutabaga_capsets.push(*capset);
}
} else {
rutabaga_capsets.push(*capset);
}
};
};
if self.capset_mask != 0 {
let supports_gfxstream = capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_VULKAN)
| capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_MAGMA)
| capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_GLES)
| capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_COMPOSER);
let supports_virglrenderer = capset_enabled(RUTABAGA_CAPSET_VIRGL2)
| capset_enabled(RUTABAGA_CAPSET_VENUS)
| capset_enabled(RUTABAGA_CAPSET_DRM);
if supports_gfxstream {
self.default_component = RutabagaComponentType::Gfxstream;
} else if supports_virglrenderer {
self.default_component = RutabagaComponentType::VirglRenderer;
} else {
self.default_component = RutabagaComponentType::CrossDomain;
}
self.virglrenderer_flags = self
.virglrenderer_flags
.use_virgl(capset_enabled(RUTABAGA_CAPSET_VIRGL2))
.use_venus(capset_enabled(RUTABAGA_CAPSET_VENUS))
.use_drm(capset_enabled(RUTABAGA_CAPSET_DRM));
self.gfxstream_flags = self
.gfxstream_flags
.use_gles(capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_GLES))
.use_vulkan(capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_VULKAN))
}
#[cfg(not(feature = "virgl_renderer"))]
if self.default_component == RutabagaComponentType::VirglRenderer {
return Err(RutabagaError::InvalidRutabagaBuild(
"virgl renderer feature not enabled",
));
}
#[cfg(not(feature = "gfxstream"))]
if self.default_component == RutabagaComponentType::Gfxstream {
return Err(RutabagaError::InvalidRutabagaBuild(
"gfxstream feature not enabled",
));
}
if self.default_component != RutabagaComponentType::Rutabaga2D {
#[cfg(feature = "virgl_renderer")]
if self.default_component == RutabagaComponentType::VirglRenderer {
if let Ok(virgl) = VirglRenderer::init(
self.virglrenderer_flags,
fence_handler.clone(),
rutabaga_server_descriptor,
) {
rutabaga_components.insert(RutabagaComponentType::VirglRenderer, virgl);
push_capset(RUTABAGA_CAPSET_VIRGL);
push_capset(RUTABAGA_CAPSET_VIRGL2);
push_capset(RUTABAGA_CAPSET_VENUS);
push_capset(RUTABAGA_CAPSET_DRM);
} else {
log::warn!("error initializing gpu backend=virglrenderer, falling back to 2d.");
self.default_component = RutabagaComponentType::Rutabaga2D;
};
}
#[cfg(feature = "gfxstream")]
if self.default_component == RutabagaComponentType::Gfxstream {
let gfxstream = Gfxstream::init(
self.display_width,
self.display_height,
self.gfxstream_flags,
self.renderer_features,
fence_handler.clone(),
self.debug_handler.clone(),
)?;
rutabaga_components.insert(RutabagaComponentType::Gfxstream, gfxstream);
push_capset(RUTABAGA_CAPSET_GFXSTREAM_VULKAN);
push_capset(RUTABAGA_CAPSET_GFXSTREAM_MAGMA);
push_capset(RUTABAGA_CAPSET_GFXSTREAM_GLES);
push_capset(RUTABAGA_CAPSET_GFXSTREAM_COMPOSER);
}
let cross_domain = CrossDomain::init(self.channels, fence_handler.clone())?;
rutabaga_components.insert(RutabagaComponentType::CrossDomain, cross_domain);
push_capset(RUTABAGA_CAPSET_CROSS_DOMAIN);
}
if self.default_component == RutabagaComponentType::Rutabaga2D {
let rutabaga_2d = Rutabaga2D::init(fence_handler.clone())?;
rutabaga_components.insert(RutabagaComponentType::Rutabaga2D, rutabaga_2d);
}
Ok(Rutabaga {
resources: Default::default(),
#[cfg(fence_passing_option1)]
shareable_fences: Default::default(),
contexts: Default::default(),
components: rutabaga_components,
default_component: self.default_component,
capset_info: rutabaga_capsets,
fence_handler,
})
}
}
#[cfg(test)]
mod tests {
use crate::*;
fn new_2d() -> Rutabaga {
RutabagaBuilder::new(RutabagaComponentType::Rutabaga2D, 0)
.build(RutabagaHandler::new(|_| {}), None)
.unwrap()
}
#[test]
fn snapshot_restore_2d_no_resources() {
let mut buffer = std::io::Cursor::new(Vec::new());
let rutabaga1 = new_2d();
rutabaga1.snapshot(&mut buffer, "").unwrap();
let mut rutabaga1 = new_2d();
rutabaga1.restore(&mut &buffer.get_ref()[..], "").unwrap();
}
#[test]
fn snapshot_restore_2d_one_resource() {
let resource_id = 123;
let resource_create_3d = ResourceCreate3D {
target: RUTABAGA_PIPE_TEXTURE_2D,
format: 1,
bind: RUTABAGA_PIPE_BIND_RENDER_TARGET,
width: 100,
height: 200,
depth: 1,
array_size: 1,
last_level: 0,
nr_samples: 0,
flags: 0,
};
let mut buffer = std::io::Cursor::new(Vec::new());
let mut rutabaga1 = new_2d();
rutabaga1
.resource_create_3d(resource_id, resource_create_3d)
.unwrap();
rutabaga1
.attach_backing(
resource_id,
vec![RutabagaIovec {
base: std::ptr::null_mut(),
len: 456,
}],
)
.unwrap();
rutabaga1.snapshot(&mut buffer, "").unwrap();
let mut rutabaga2 = new_2d();
rutabaga2.restore(&mut &buffer.get_ref()[..], "").unwrap();
assert_eq!(rutabaga2.resources.len(), 1);
let rutabaga_resource = rutabaga2.resources.get(&resource_id).unwrap();
assert_eq!(rutabaga_resource.resource_id, resource_id);
assert_eq!(
rutabaga_resource.info_2d.as_ref().unwrap().width,
resource_create_3d.width
);
assert_eq!(
rutabaga_resource.info_2d.as_ref().unwrap().height,
resource_create_3d.height
);
assert!(rutabaga_resource.backing_iovecs.is_none());
}
}