blob: 2863ee5ad9d8820b5805c91c7e493427b696aaeb [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![allow(non_upper_case_globals)]
use fidl_fuchsia_sysmem as fsysmem;
use fidl_fuchsia_ui_composition as fuicomp;
use fuchsia_component::client::connect_channel_to_protocol;
use fuchsia_image_format::*;
use fuchsia_zircon as zx;
use magma::*;
use vk_sys as vk;
use zerocopy::{AsBytes, FromBytes};
use crate::device::wayland::vulkan::*;
use crate::task::CurrentTask;
use crate::types::*;
/// Reads a magma command and its type from user space.
///
/// # Parameters
/// - `current_task`: The task to which the command memory belongs.
/// - `command_address`: The address of the `virtmagma_ioctl_args_magma_command`.
pub fn read_magma_command_and_type(
current_task: &CurrentTask,
command_address: UserAddress,
) -> Result<(virtmagma_ioctl_args_magma_command, virtio_magma_ctrl_type), Errno> {
let command_ref = UserRef::new(command_address);
let mut command = virtmagma_ioctl_args_magma_command::default();
current_task.mm.read_object(command_ref, &mut command)?;
let request_address = UserAddress::from(command.request_address);
let mut header = virtio_magma_ctrl_hdr_t::default();
current_task.mm.read_object(UserRef::new(request_address), &mut header)?;
Ok((command, header.type_ as u16))
}
/// Reads the control and response structs from the given magma command struct.
///
/// # Parameters
/// - `current_task`: The task to which the memory belongs.
/// - `command`: The command struct that contains the pointers to the control and response structs.
pub fn read_control_and_response<C: Default + AsBytes + FromBytes, R: Default>(
current_task: &CurrentTask,
command: &virtmagma_ioctl_args_magma_command,
) -> Result<(C, R), Errno> {
let request_address = UserAddress::from(command.request_address);
let mut ctrl = C::default();
current_task.mm.read_object(UserRef::new(request_address), &mut ctrl)?;
Ok((ctrl, R::default()))
}
/// Creates an image in a buffer collection.
///
/// # Parameters
/// - `physical_device_index`: The index of the physical device to use when initializing the Vulkan
/// loader.
/// - `create_info`: The magma info used to create the image.
///
/// Returns the image vmo, an import token for the collection, and the image info for the created
/// image.
pub fn create_drm_image(
physical_device_index: u32,
create_info: &magma_image_create_info_t,
) -> Result<(zx::Vmo, fuicomp::BufferCollectionImportToken, magma_image_info_t), magma_status_t> {
let flags = create_info.flags as u32;
if flags & !MAGMA_IMAGE_CREATE_FLAGS_PRESENTABLE != 0 {
return Err(MAGMA_STATUS_INVALID_ARGS);
}
let vk_format = drm_format_to_vulkan_format(create_info.drm_format as u32)
.map_err(|_| MAGMA_STATUS_INVALID_ARGS)?;
let sysmem_format = drm_format_to_sysmem_format(create_info.drm_format as u32)
.map_err(|_| MAGMA_STATUS_INVALID_ARGS)?;
let mut sysmem_modifiers = vec![];
let mut terminator_found = false;
for modifier in create_info.drm_format_modifiers {
if modifier == DRM_FORMAT_MOD_INVALID {
terminator_found = true;
break;
}
let modifier =
drm_modifier_to_sysmem_modifier(modifier).map_err(|_| MAGMA_STATUS_INVALID_ARGS)?;
sysmem_modifiers.push(modifier);
}
if !terminator_found {
return Err(MAGMA_STATUS_INVALID_ARGS);
}
let loader = Loader::new(physical_device_index).map_err(|_| MAGMA_STATUS_INVALID_ARGS)?;
// TODO: verify physical device limits
// TODO: Handle the case when MAGMA_IMAGE_CREATE_FLAGS_PRESENTABLE is not set and the scenic
// allocator is not intended to be used.
let scenic_allocator = init_scenic().map_err(|_| MAGMA_STATUS_INVALID_ARGS)?;
let image_create_info = vk::ImageCreateInfo {
sType: vk::STRUCTURE_TYPE_IMAGE_CREATE_INFO,
pNext: std::ptr::null(),
flags: 0,
imageType: vk::IMAGE_TYPE_2D,
format: vk_format,
extent: vk::Extent3D { width: create_info.width, height: create_info.height, depth: 1 },
mipLevels: 1,
arrayLayers: 1,
samples: vk::SAMPLE_COUNT_1_BIT,
tiling: vk::IMAGE_TILING_OPTIMAL,
usage: vk::IMAGE_USAGE_TRANSFER_SRC_BIT
| vk::IMAGE_USAGE_TRANSFER_DST_BIT
| vk::IMAGE_USAGE_SAMPLED_BIT
| vk::IMAGE_USAGE_STORAGE_BIT
| vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT
| vk::IMAGE_USAGE_INPUT_ATTACHMENT_BIT,
sharingMode: vk::SHARING_MODE_EXCLUSIVE,
queueFamilyIndexCount: 0,
pQueueFamilyIndices: std::ptr::null(),
initialLayout: vk::IMAGE_LAYOUT_UNDEFINED,
};
let (tokens, sysmem_allocator) = init_sysmem().map_err(|_| MAGMA_STATUS_INVALID_ARGS)?;
let (scenic_import_token, buffer_collection) = loader
.create_collection(
&image_create_info,
sysmem_format,
&sysmem_modifiers,
tokens,
&scenic_allocator,
&sysmem_allocator,
)
.map_err(|_| MAGMA_STATUS_INVALID_ARGS)?;
let (vmo, image_info) =
get_image_info(buffer_collection, create_info.width, create_info.height)
.map_err(|_| MAGMA_STATUS_INVALID_ARGS)?;
Ok((vmo, scenic_import_token, image_info))
}
/// Initializes and returns Scenic allocator proxy.
pub fn init_scenic() -> Result<fuicomp::AllocatorSynchronousProxy, Errno> {
let (server_end, client_end) = zx::Channel::create().map_err(|_| errno!(ENOENT))?;
connect_channel_to_protocol::<fuicomp::AllocatorMarker>(server_end)
.map_err(|_| errno!(ENOENT))?;
let composition_proxy = fuicomp::AllocatorSynchronousProxy::new(client_end);
Ok(composition_proxy)
}
/// Allocates a shared sysmem collection.
///
/// The returned `BufferCollectionTokens` contains a proxy to the shared collection, as well as a
/// duplicate token to use for both Scenic and Vulkan.
pub fn init_sysmem() -> Result<(BufferCollectionTokens, fsysmem::AllocatorSynchronousProxy), Errno>
{
let (server_end, client_end) = zx::Channel::create().map_err(|_| errno!(ENOENT))?;
connect_channel_to_protocol::<fsysmem::AllocatorMarker>(server_end)
.map_err(|_| errno!(ENOENT))?;
let sysmem_allocator = fsysmem::AllocatorSynchronousProxy::new(client_end);
let (client, remote) =
fidl::endpoints::create_endpoints::<fsysmem::BufferCollectionTokenMarker>()
.map_err(|_| errno!(EINVAL))?;
sysmem_allocator.allocate_shared_collection(remote).map_err(|_| errno!(EINVAL))?;
let buffer_token_proxy =
fsysmem::BufferCollectionTokenSynchronousProxy::new(client.into_channel());
let (scenic_token, remote) =
fidl::endpoints::create_endpoints::<fsysmem::BufferCollectionTokenMarker>()
.map_err(|_| errno!(EINVAL))?;
buffer_token_proxy.duplicate(!0, remote).map_err(|_| errno!(EINVAL))?;
let (vulkan_token, remote) =
fidl::endpoints::create_endpoints::<fsysmem::BufferCollectionTokenMarker>()
.map_err(|_| errno!(EINVAL))?;
buffer_token_proxy.duplicate(!0, remote).map_err(|_| errno!(EINVAL))?;
buffer_token_proxy.sync(zx::Time::INFINITE).map_err(|_| errno!(EINVAL))?;
Ok((
BufferCollectionTokens { buffer_token_proxy, scenic_token, vulkan_token },
sysmem_allocator,
))
}
/// Waits for buffers to be allocated in the provided buffer collection and returns the first buffer
/// in the collection, as well as the image info for the buffer.
///
/// # Parameters
/// - `buffer_collection`: The collection to fetch the image and info from.
/// - `width`: The width to use when creating the image format.
/// - `height`: The height to use when creating the image format.
pub fn get_image_info(
buffer_collection: fsysmem::BufferCollectionSynchronousProxy,
width: u32,
height: u32,
) -> Result<(zx::Vmo, magma_image_info_t), Errno> {
let (_, mut collection_info) = buffer_collection
.wait_for_buffers_allocated(zx::Time::INFINITE)
.map_err(|_| errno!(EINVAL))?;
let _ = buffer_collection.close();
let image_format =
constraints_to_format(&collection_info.settings.image_format_constraints, width, height)
.map_err(|_| errno!(EINVAL))?;
let mut image_info = magma_image_info_t::default();
for plane in 0..MAGMA_MAX_IMAGE_PLANES {
image_info.plane_offsets[plane as usize] =
image_format_plane_byte_offset(&image_format, plane).unwrap_or(0);
image_info.plane_strides[plane as usize] =
get_plane_row_bytes(&image_format, plane).unwrap_or(0) as u64;
}
image_info.drm_format_modifier =
sysmem_modifier_to_drm_modifier(image_format.pixel_format.format_modifier.value)
.unwrap_or(0);
image_info.coherency_domain = match collection_info.settings.buffer_settings.coherency_domain {
fsysmem::CoherencyDomain::Cpu => MAGMA_COHERENCY_DOMAIN_CPU,
fsysmem::CoherencyDomain::Ram => MAGMA_COHERENCY_DOMAIN_RAM,
fsysmem::CoherencyDomain::Inaccessible => MAGMA_COHERENCY_DOMAIN_INACCESSIBLE,
};
let vmo = collection_info.buffers[0].vmo.take().ok_or(errno!(EINVAL))?;
Ok((vmo, image_info))
}
#[repr(C)]
#[derive(AsBytes, FromBytes, Copy, Clone, Default, Debug)]
/// `StarnixPollItem` exists to be able to `AsBytes` and `FromBytes` the union that exists in
/// `magma_poll_item_t`.
pub struct StarnixPollItem {
pub semaphore_or_handle: u64,
pub type_: u32,
pub condition: u32,
pub result: u32,
pub unused: [u8; 4usize],
}
impl StarnixPollItem {
pub fn new(poll_item: &magma_poll_item_t) -> StarnixPollItem {
let semaphore_or_handle = unsafe {
if poll_item.type_ == MAGMA_POLL_TYPE_SEMAPHORE {
poll_item.__bindgen_anon_1.semaphore as u64
} else {
poll_item.__bindgen_anon_1.handle as u64
}
};
StarnixPollItem {
semaphore_or_handle,
type_: poll_item.type_,
condition: poll_item.condition,
result: poll_item.result,
unused: [0; 4],
}
}
pub fn into_poll_item(&self) -> magma_poll_item_t {
let handle = if self.type_ == MAGMA_POLL_TYPE_SEMAPHORE {
magma_poll_item__bindgen_ty_1 {
semaphore: self.semaphore_or_handle as magma_semaphore_t,
}
} else {
magma_poll_item__bindgen_ty_1 { handle: self.semaphore_or_handle as magma_handle_t }
};
magma_poll_item_t {
__bindgen_anon_1: handle,
type_: self.type_,
condition: self.condition,
result: self.result,
unused: 0,
}
}
}