| // 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, |
| } |
| } |
| } |