// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! gfxstream: Handles 3D virtio-gpu hypercalls using gfxstream.
//!
//! External code found at .
#![cfg(feature = "gfxstream")]
use std::convert::TryInto;
use std::ffi::CString;
use std::io::IoSliceMut;
use std::mem::size_of;
use std::os::raw::c_char;
use std::os::raw::c_int;
use std::os::raw::c_uint;
use std::os::raw::c_void;
use std::panic::catch_unwind;
use std::process::abort;
use std::ptr::null;
use std::ptr::null_mut;
use std::sync::Arc;
use crate::generated::virgl_renderer_bindings::iovec;
use crate::generated::virgl_renderer_bindings::virgl_box;
use crate::generated::virgl_renderer_bindings::virgl_renderer_resource_create_args;
use crate::renderer_utils::*;
use crate::rutabaga_core::RutabagaComponent;
use crate::rutabaga_core::RutabagaContext;
use crate::rutabaga_core::RutabagaResource;
use crate::rutabaga_os::FromRawDescriptor;
use crate::rutabaga_os::IntoRawDescriptor;
use crate::rutabaga_os::OwnedDescriptor;
use crate::rutabaga_os::RawDescriptor;
use crate::rutabaga_utils::*;
// See `virtgpu-gfxstream-renderer.h` for definitions
const STREAM_RENDERER_PARAM_USER_DATA: u64 = 1;
const STREAM_RENDERER_PARAM_RENDERER_FLAGS: u64 = 2;
const STREAM_RENDERER_PARAM_FENCE_CALLBACK: u64 = 3;
const STREAM_RENDERER_PARAM_WIN0_WIDTH: u64 = 4;
const STREAM_RENDERER_PARAM_WIN0_HEIGHT: u64 = 5;
const STREAM_RENDERER_PARAM_DEBUG_CALLBACK: u64 = 6;
const STREAM_RENDERER_PARAM_RENDERER_FEATURES: u64 = 11;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct stream_renderer_param {
key: u64,
value: u64,
}
#[repr(C)]
#[derive(Copy, Clone, Default)]
pub struct stream_renderer_handle {
pub os_handle: i64,
pub handle_type: u32,
}
#[repr(C)]
#[derive(Copy, Clone, Default)]
pub struct stream_renderer_vulkan_info {
pub memory_index: u32,
pub device_uuid: [u8; 16],
pub driver_uuid: [u8; 16],
}
#[repr(C)]
pub struct stream_renderer_command {
pub ctx_id: u32,
pub cmd_size: u32,
pub cmd: *const u8,
pub num_in_fences: u32,
pub in_fence_descriptors: *const u64,
}
#[allow(non_camel_case_types)]
pub type stream_renderer_create_blob = ResourceCreateBlob;
#[allow(non_camel_case_types)]
pub type stream_renderer_resource_create_args = virgl_renderer_resource_create_args;
#[allow(non_camel_case_types)]
pub type stream_renderer_box = virgl_box;
#[allow(non_camel_case_types)]
pub type stream_renderer_fence = RutabagaFence;
#[allow(non_camel_case_types)]
pub type stream_renderer_debug = RutabagaDebug;
extern "C" {
// Entry point for the stream renderer.
fn stream_renderer_init(
stream_renderer_params: *mut stream_renderer_param,
num_params: u64,
) -> c_int;
// Shutdown entry point for the renderer.
fn stream_renderer_teardown();
// virtio-gpu-3d ioctl functions (begin)
// In gfxstream, the resource create/transfer ioctls correspond to creating buffers for API
// forwarding and the notification of new API calls forwarded by the guest, unless they
// correspond to minigbm resource targets (PIPE_TEXTURE_2D), in which case they create globally
// visible shared GL textures to support gralloc.
fn stream_renderer_resource_create(
args: *mut stream_renderer_resource_create_args,
iov: *mut iovec,
num_iovs: u32,
) -> c_int;
fn stream_renderer_resource_unref(res_handle: u32);
fn stream_renderer_context_destroy(handle: u32);
fn stream_renderer_transfer_read_iov(
handle: u32,
ctx_id: u32,
level: u32,
stride: u32,
layer_stride: u32,
box_: *mut stream_renderer_box,
offset: u64,
iov: *mut iovec,
iovec_cnt: c_int,
) -> c_int;
fn stream_renderer_transfer_write_iov(
handle: u32,
ctx_id: u32,
level: c_int,
stride: u32,
layer_stride: u32,
box_: *mut stream_renderer_box,
offset: u64,
iovec: *mut iovec,
iovec_cnt: c_uint,
) -> c_int;
fn stream_renderer_submit_cmd(cmd: *const stream_renderer_command) -> c_int;
fn stream_renderer_resource_attach_iov(
res_handle: c_int,
iov: *mut iovec,
num_iovs: c_int,
) -> c_int;
fn stream_renderer_resource_detach_iov(
res_handle: c_int,
iov: *mut *mut iovec,
num_iovs: *mut c_int,
);
fn stream_renderer_create_fence(fence: *const stream_renderer_fence) -> c_int;
#[cfg(gfxstream_unstable)]
fn stream_renderer_export_fence(fence_id: u64, handle: *mut stream_renderer_handle) -> c_int;
fn stream_renderer_ctx_attach_resource(ctx_id: c_int, res_handle: c_int);
fn stream_renderer_ctx_detach_resource(ctx_id: c_int, res_handle: c_int);
fn stream_renderer_get_cap_set(set: u32, max_ver: *mut u32, max_size: *mut u32);
fn stream_renderer_fill_caps(set: u32, version: u32, caps: *mut c_void);
fn stream_renderer_flush(res_handle: u32);
fn stream_renderer_create_blob(
ctx_id: u32,
res_handle: u32,
create_blob: *const stream_renderer_create_blob,
iovecs: *const iovec,
num_iovs: u32,
handle: *const stream_renderer_handle,
) -> c_int;
fn stream_renderer_export_blob(res_handle: u32, handle: *mut stream_renderer_handle) -> c_int;
fn stream_renderer_resource_map(
res_handle: u32,
map: *mut *mut c_void,
out_size: *mut u64,
) -> c_int;
fn stream_renderer_resource_unmap(res_handle: u32) -> c_int;
fn stream_renderer_resource_map_info(res_handle: u32, map_info: *mut u32) -> c_int;
fn stream_renderer_vulkan_info(
res_handle: u32,
vulkan_info: *mut stream_renderer_vulkan_info,
) -> c_int;
fn stream_renderer_context_create(
handle: u32,
nlen: u32,
name: *const c_char,
context_init: u32,
) -> c_int;
#[cfg(gfxstream_unstable)]
fn stream_renderer_suspend() -> c_int;
#[cfg(gfxstream_unstable)]
fn stream_renderer_snapshot(dir: *const c_char) -> c_int;
#[cfg(gfxstream_unstable)]
fn stream_renderer_restore(dir: *const c_char) -> c_int;
#[cfg(gfxstream_unstable)]
fn stream_renderer_resume() -> c_int;
#[cfg(gfxstream_unstable)]
fn stream_renderer_wait_sync_resource(res_handle: u32) -> c_int;
}
/// The virtio-gpu backend state tracker which supports accelerated rendering.
pub struct Gfxstream {
/// Cookie used by Gfxstream, should be held as long as the renderer is alive.
_cookie: Box,
}
struct GfxstreamContext {
ctx_id: u32,
fence_handler: RutabagaFenceHandler,
}
impl GfxstreamContext {
#[cfg(gfxstream_unstable)]
fn export_fence(&self, fence_id: u64) -> RutabagaResult {
let mut stream_handle: stream_renderer_handle = Default::default();
// SAFETY:
// Safe because a correctly formatted stream_handle is given to gfxstream.
let ret = unsafe { stream_renderer_export_fence(fence_id, &mut stream_handle) };
ret_to_res(ret)?;
let raw_descriptor = stream_handle.os_handle as RawDescriptor;
// SAFETY:
// Safe because the handle was just returned by a successful gfxstream call so it must
// be valid and owned by us.
let handle = unsafe { OwnedDescriptor::from_raw_descriptor(raw_descriptor) };
Ok(RutabagaHandle {
os_handle: handle,
handle_type: stream_handle.handle_type,
})
}
#[cfg(not(gfxstream_unstable))]
fn export_fence(&self, _fence_id: u64) -> RutabagaResult {
Err(RutabagaError::Unsupported)
}
}
impl RutabagaContext for GfxstreamContext {
fn submit_cmd(
&mut self,
commands: &mut [u8],
_fence_ids: &[u64],
_shareable_fences: Vec,
) -> RutabagaResult<()> {
if commands.len() % size_of::() != 0 {
return Err(RutabagaError::InvalidCommandSize(commands.len()));
}
// TODO(b/315870313): Add safety comment
#[allow(clippy::undocumented_unsafe_blocks)]
let ret = unsafe {
let cmd = stream_renderer_command {
ctx_id: self.ctx_id,
cmd_size: commands.len().try_into()?,
cmd: commands.as_mut_ptr(),
num_in_fences: 0,
in_fence_descriptors: null(),
};
stream_renderer_submit_cmd(&cmd as *const stream_renderer_command)
};
ret_to_res(ret)
}
fn attach(&mut self, resource: &mut RutabagaResource) {
// SAFETY:
// The context id and resource id must be valid because the respective instances ensure
// their lifetime.
unsafe {
stream_renderer_ctx_attach_resource(self.ctx_id as i32, resource.resource_id as i32);
}
}
fn detach(&mut self, resource: &RutabagaResource) {
// SAFETY:
// The context id and resource id must be valid because the respective instances ensure
// their lifetime.
unsafe {
stream_renderer_ctx_detach_resource(self.ctx_id as i32, resource.resource_id as i32);
}
}
fn component_type(&self) -> RutabagaComponentType {
RutabagaComponentType::Gfxstream
}
fn context_create_fence(
&mut self,
fence: RutabagaFence,
) -> RutabagaResult