From 766a093780f4011e94bf0e76ed4e339273143bb7 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Mon, 2 Sep 2024 15:56:09 +0200 Subject: [PATCH] vulkan: create a vulkan allocator --- src/cli/screenshot.rs | 76 +- src/gfx_apis.rs | 1 + src/gfx_apis/vulkan.rs | 29 +- src/gfx_apis/vulkan/bo_allocator.rs | 761 +++++++++++++++++++++ src/gfx_apis/vulkan/device.rs | 45 +- src/gfx_apis/vulkan/format.rs | 29 +- src/gfx_apis/vulkan/instance.rs | 18 +- src/gfx_apis/vulkan/renderer.rs | 20 +- src/gfx_apis/vulkan/shm_image.rs | 20 +- src/gfx_apis/vulkan/staging.rs | 19 +- src/it/test_backend.rs | 52 +- src/it/testrun.rs | 9 +- src/it/tests/t0013_graphics_initialized.rs | 2 +- src/it/tests/t0031_syncobj.rs | 2 +- src/it/tests/t0035_scanout_feedback.rs | 2 +- 15 files changed, 984 insertions(+), 101 deletions(-) create mode 100644 src/gfx_apis/vulkan/bo_allocator.rs diff --git a/src/cli/screenshot.rs b/src/cli/screenshot.rs index 994628f5..c3c2dec1 100644 --- a/src/cli/screenshot.rs +++ b/src/cli/screenshot.rs @@ -1,8 +1,9 @@ use { crate::{ - allocator::{Allocator, AllocatorError, BO_USE_LINEAR, BO_USE_RENDERING}, + allocator::{Allocator, AllocatorError, BufferUsage, MappedBuffer}, cli::{GlobalArgs, ScreenshotArgs, ScreenshotFormat}, format::XRGB8888, + gfx_apis, tools::tool_client::{with_tool_client, Handle, ToolClient}, udmabuf::{Udmabuf, UdmabufError}, utils::{errorfmt::ErrorFmt, queue::AsyncQueue, windows::WindowsExt}, @@ -138,6 +139,21 @@ pub enum ScreenshotError { ImportDmabuf(#[source] AllocatorError), #[error("Could not map a dmabuf")] MapDmabuf(#[source] AllocatorError), + #[error("Could not create a vulkan allocator")] + CreateVulkanAllocator(#[source] AllocatorError), + #[error("Could not map the dmabuf with any allocator")] + MapDmabufAny, +} + +fn map( + allocator: Rc, + buf: &DmaBuf, +) -> Result, ScreenshotError> { + let bo = allocator + .import_dmabuf(buf, BufferUsage::none()) + .map_err(ScreenshotError::ImportDmabuf)?; + let bo_map = bo.map_read().map_err(ScreenshotError::MapDmabuf)?; + Ok(bo_map) } pub fn buf_to_bytes( @@ -145,21 +161,55 @@ pub fn buf_to_bytes( buf: &DmaBuf, format: ScreenshotFormat, ) -> Result, ScreenshotError> { - let allocator: Rc = match drm_dev { + match drm_dev { + None => {} + Some(_) => {} + } + let mut allocators = + Vec:: Result, ScreenshotError>>>::new(); + match drm_dev { Some(drm_dev) => { - let drm = Drm::reopen(drm_dev.raw(), false).map_err(ScreenshotError::OpenDrmDevice)?; - GbmDevice::new(&drm) - .map(Rc::new) - .map_err(ScreenshotError::CreateGbmDevice)? + let drm = || Drm::reopen(drm_dev.raw(), false).map_err(ScreenshotError::OpenDrmDevice); + let gbm = Box::new(move || { + GbmDevice::new(&drm()?) + .map(|d| Rc::new(d) as _) + .map_err(ScreenshotError::CreateGbmDevice) + }); + let vulkan = Box::new(move || { + gfx_apis::create_vulkan_allocator(&drm()?) + .map_err(ScreenshotError::CreateVulkanAllocator) + }); + allocators.push(vulkan); + allocators.push(gbm); + } + None => { + let udmabuf = Box::new(|| { + Udmabuf::new() + .map(|u| Rc::new(u) as _) + .map_err(ScreenshotError::CreateUdmabuf) + }); + allocators.push(udmabuf); } - None => Udmabuf::new() - .map(Rc::new) - .map_err(ScreenshotError::CreateUdmabuf)?, + } + let bo_map = 'create_bo_map: { + for allocator in allocators { + let allocator = match allocator() { + Ok(a) => a, + Err(e) => { + log::error!("Could not create allocator: {}", ErrorFmt(e)); + continue; + } + }; + match map(allocator, buf) { + Ok(m) => break 'create_bo_map m, + Err(e) => { + log::error!("Could not map dmabuf: {}", ErrorFmt(e)); + continue; + } + }; + } + return Err(ScreenshotError::MapDmabufAny); }; - let bo = allocator - .import_dmabuf(buf, BO_USE_LINEAR | BO_USE_RENDERING) - .map_err(ScreenshotError::ImportDmabuf)?; - let bo_map = bo.map_read().map_err(ScreenshotError::MapDmabuf)?; let data = unsafe { bo_map.data() }; if format == ScreenshotFormat::Qoi { return Ok(xrgb8888_encode_qoi( diff --git a/src/gfx_apis.rs b/src/gfx_apis.rs index 5cc1dd3a..6226e25c 100644 --- a/src/gfx_apis.rs +++ b/src/gfx_apis.rs @@ -1,3 +1,4 @@ +pub use vulkan::create_vulkan_allocator; use { crate::{ async_engine::AsyncEngine, diff --git a/src/gfx_apis/vulkan.rs b/src/gfx_apis/vulkan.rs index 56d81677..8b1cae35 100644 --- a/src/gfx_apis/vulkan.rs +++ b/src/gfx_apis/vulkan.rs @@ -1,4 +1,5 @@ mod allocator; +mod bo_allocator; mod command; mod descriptor; mod device; @@ -17,7 +18,7 @@ mod util; use { crate::{ - allocator::Allocator, + allocator::{Allocator, AllocatorError}, async_engine::AsyncEngine, format::Format, gfx_api::{ @@ -39,6 +40,7 @@ use { ash::{vk, LoadingError}, gpu_alloc::{AllocationError, MapError}, jay_config::video::GfxApi, + log::Level, once_cell::sync::Lazy, std::{ cell::Cell, @@ -180,6 +182,20 @@ pub enum VulkanError { GfxError(GfxError), #[error("Buffer format {0} is not supported for shm buffers in Vulkan context")] UnsupportedShmFormat(&'static str), + #[error("Only BO_USE_RENDERING and BO_USE_WRITE are supported")] + UnsupportedBufferUsage, + #[error("None of the supplied modifiers are supported")] + NoSupportedModifiers, + #[error("Could not retrieve the image modifier")] + GetModifier(#[source] vk::Result), + #[error("Vulkan allocated the image with an invalid modifier")] + InvalidModifier, + #[error("Could not export the DmaBuf")] + GetDmaBuf(#[source] vk::Result), + #[error("Could not wait for the device to become idle")] + WaitIdle(#[source] vk::Result), + #[error("Could not dup a DRM device")] + DupDrm(#[source] DrmError), } impl From for GfxError { @@ -196,12 +212,19 @@ pub fn create_graphics_context( ring: &Rc, drm: &Drm, ) -> Result, GfxError> { - let instance = VulkanInstance::new(eng, ring, *VULKAN_VALIDATION)?; + let instance = VulkanInstance::new(Level::Info, *VULKAN_VALIDATION)?; let device = instance.create_device(drm)?; - let renderer = device.create_renderer()?; + let renderer = device.create_renderer(eng, ring)?; Ok(Rc::new(Context(renderer))) } +pub fn create_vulkan_allocator(drm: &Drm) -> Result, AllocatorError> { + let instance = VulkanInstance::new(Level::Debug, *VULKAN_VALIDATION)?; + let device = instance.create_device(drm)?; + let allocator = device.create_bo_allocator(drm)?; + Ok(Rc::new(allocator)) +} + #[derive(Debug)] struct Context(Rc); diff --git a/src/gfx_apis/vulkan/bo_allocator.rs b/src/gfx_apis/vulkan/bo_allocator.rs new file mode 100644 index 00000000..0ac35f9a --- /dev/null +++ b/src/gfx_apis/vulkan/bo_allocator.rs @@ -0,0 +1,761 @@ +use { + crate::{ + allocator::{ + Allocator, AllocatorError, BufferObject, BufferUsage, MappedBuffer, BO_USE_RENDERING, + BO_USE_WRITE, + }, + format::Format, + gfx_apis::vulkan::{ + allocator::VulkanAllocator, command::VulkanCommandBuffer, device::VulkanDevice, + format::VulkanFormat, renderer::image_barrier, staging::VulkanStagingBuffer, + util::OnDrop, VulkanError, + }, + utils::errorfmt::ErrorFmt, + video::{ + dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec}, + drm::Drm, + Modifier, + }, + }, + arrayvec::ArrayVec, + ash::vk::{ + AccessFlags2, BindImageMemoryInfo, BindImagePlaneMemoryInfo, BufferImageCopy2, + BufferMemoryBarrier2, CommandBuffer, CommandBufferBeginInfo, CommandBufferSubmitInfo, + CommandBufferUsageFlags, CopyBufferToImageInfo2, CopyImageToBufferInfo2, DependencyInfo, + DeviceMemory, ExportMemoryAllocateInfo, Extent3D, ExternalMemoryHandleTypeFlags, + ExternalMemoryImageCreateInfo, Fence, FormatFeatureFlags, Image, ImageAspectFlags, + ImageCreateInfo, ImageDrmFormatModifierExplicitCreateInfoEXT, + ImageDrmFormatModifierListCreateInfoEXT, ImageDrmFormatModifierPropertiesEXT, ImageLayout, + ImageMemoryBarrier2, ImageMemoryRequirementsInfo2, ImagePlaneMemoryRequirementsInfo, + ImageSubresource, ImageSubresourceLayers, ImageTiling, ImageType, ImageUsageFlags, + ImportMemoryFdInfoKHR, MemoryAllocateInfo, MemoryDedicatedAllocateInfo, + MemoryFdPropertiesKHR, MemoryGetFdInfoKHR, MemoryPropertyFlags, MemoryRequirements2, + PipelineStageFlags2, SampleCountFlags, SharingMode, SubmitInfo2, SubresourceLayout, + QUEUE_FAMILY_FOREIGN_EXT, + }, + std::{rc::Rc, slice}, + uapi::OwnedFd, +}; + +impl From for AllocatorError { + fn from(value: VulkanError) -> Self { + Self(Box::new(value)) + } +} + +pub(super) struct VulkanBoAllocator { + data: Rc, +} + +struct VulkanBoAllocatorData { + drm: Drm, + device: Rc, + allocator: Rc, + command_buffer: Rc, +} + +struct VulkanBo { + allocator: Rc, + image: Image, + memory: PlaneVec, + buf: DmaBuf, +} + +struct VulkanBoMapping { + bo: Rc, + upload: bool, + stride: i32, + staging: VulkanStagingBuffer, + data: *mut [u8], +} + +impl Drop for VulkanBo { + fn drop(&mut self) { + unsafe { + self.allocator.device.device.destroy_image(self.image, None); + for &memory in &self.memory { + self.allocator.device.device.free_memory(memory, None); + } + } + } +} + +impl VulkanDevice { + pub(super) fn create_bo_allocator( + self: &Rc, + drm: &Drm, + ) -> Result { + let allocator = self.create_allocator()?; + let pool = self.create_command_pool()?; + let command_buffer = pool.allocate_buffer()?; + let drm = drm.dup_render().map_err(VulkanError::DupDrm)?; + Ok(VulkanBoAllocator { + data: Rc::new(VulkanBoAllocatorData { + drm, + device: self.clone(), + allocator, + command_buffer, + }), + }) + } +} + +impl VulkanBoAllocator { + fn create_bo( + &self, + dma_buf_ids: &DmaBufIds, + width: i32, + height: i32, + format: &'static Format, + modifiers: &[Modifier], + usage: BufferUsage, + ) -> Result, VulkanError> { + validate_usage(usage)?; + let data = &self.data; + let Some(format) = data.device.formats.get(&format.drm) else { + return Err(VulkanError::FormatNotSupported); + }; + if width < 0 || height < 0 { + return Err(VulkanError::NonPositiveImageSize); + } + let width = width as u32; + let height = height as u32; + let image = { + let mut mods = vec![]; + for &modifier in modifiers { + if validate_modifier(width, height, usage, false, None, format, modifier) { + mods.push(modifier); + } + } + if mods.is_empty() { + return Err(VulkanError::NoSupportedModifiers); + } + let mut mod_list = + ImageDrmFormatModifierListCreateInfoEXT::default().drm_format_modifiers(&mods); + let mut memory_image_create_info = ExternalMemoryImageCreateInfo::default() + .handle_types(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT); + let create_info = image_create_info(width, height, format.format, usage) + .push_next(&mut memory_image_create_info) + .push_next(&mut mod_list); + let res = unsafe { data.device.device.create_image(&create_info, None) }; + res.map_err(VulkanError::CreateImage)? + }; + let destroy_image = OnDrop(|| unsafe { data.device.device.destroy_image(image, None) }); + let modifier = { + let mut props = ImageDrmFormatModifierPropertiesEXT::default(); + unsafe { + data.device + .image_drm_format_modifier + .get_image_drm_format_modifier_properties(image, &mut props) + .map_err(VulkanError::GetModifier)? + } + props.drm_format_modifier + }; + let Some(modifier) = format.modifiers.get(&modifier) else { + return Err(VulkanError::InvalidModifier)?; + }; + let memory = { + let image_memory_requirements_info = + ImageMemoryRequirementsInfo2::default().image(image); + let mut memory_requirements = MemoryRequirements2::default(); + unsafe { + data.device.device.get_image_memory_requirements2( + &image_memory_requirements_info, + &mut memory_requirements, + ); + } + let memory_type_index = data + .device + .find_memory_type( + MemoryPropertyFlags::DEVICE_LOCAL, + memory_requirements.memory_requirements.memory_type_bits, + ) + .ok_or(VulkanError::MemoryType)?; + let mut memory_dedicated_allocate_info = + MemoryDedicatedAllocateInfo::default().image(image); + let mut export_info = ExportMemoryAllocateInfo::default() + .handle_types(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT); + let memory_allocate_info = MemoryAllocateInfo::default() + .allocation_size(memory_requirements.memory_requirements.size) + .memory_type_index(memory_type_index) + .push_next(&mut memory_dedicated_allocate_info) + .push_next(&mut export_info); + let memory = unsafe { + data.device + .device + .allocate_memory(&memory_allocate_info, None) + }; + memory.map_err(VulkanError::AllocateMemory)? + }; + let destroy_memory = OnDrop(|| unsafe { data.device.device.free_memory(memory, None) }); + unsafe { + data.device + .device + .bind_image_memory(image, memory, 0) + .map_err(VulkanError::BindImageMemory)?; + } + let fd = { + let get_info = MemoryGetFdInfoKHR::default() + .handle_type(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT) + .memory(memory); + let fd = unsafe { data.device.external_memory_fd.get_memory_fd(&get_info) }; + fd.map_err(VulkanError::GetDmaBuf) + .map(OwnedFd::new) + .map(Rc::new)? + }; + let mut planes = PlaneVec::new(); + for i in 0..modifier.planes { + let flag = [ + ImageAspectFlags::MEMORY_PLANE_0_EXT, + ImageAspectFlags::MEMORY_PLANE_1_EXT, + ImageAspectFlags::MEMORY_PLANE_2_EXT, + ImageAspectFlags::MEMORY_PLANE_3_EXT, + ][i]; + let layout = unsafe { + data.device.device.get_image_subresource_layout( + image, + ImageSubresource::default().aspect_mask(flag), + ) + }; + planes.push(DmaBufPlane { + offset: layout.offset as _, + stride: layout.row_pitch as _, + fd: fd.clone(), + }); + } + let buf = DmaBuf { + id: dma_buf_ids.next(), + width: width as _, + height: height as _, + format: format.format, + modifier: modifier.modifier, + planes, + }; + unsafe { + let cmd = data.command_buffer.buffer; + let device = &data.device.device; + let begin = + CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT); + let barrier = image_barrier() + .src_queue_family_index(data.device.graphics_queue_idx) + .dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT) + .old_layout(ImageLayout::UNDEFINED) + .new_layout(ImageLayout::GENERAL) + .image(image); + let dependency_info = + DependencyInfo::default().image_memory_barriers(slice::from_ref(&barrier)); + let cmd_buffer_submit_info = CommandBufferSubmitInfo::default().command_buffer(cmd); + let submit_info = SubmitInfo2::default() + .command_buffer_infos(slice::from_ref(&cmd_buffer_submit_info)); + device + .begin_command_buffer(cmd, &begin) + .map_err(VulkanError::BeginCommandBuffer)?; + device.cmd_pipeline_barrier2(cmd, &dependency_info); + device + .end_command_buffer(cmd) + .map_err(VulkanError::EndCommandBuffer)?; + device + .queue_submit2( + data.device.graphics_queue, + slice::from_ref(&submit_info), + Fence::null(), + ) + .map_err(VulkanError::Submit)?; + device.device_wait_idle().map_err(VulkanError::WaitIdle)?; + } + destroy_image.forget(); + destroy_memory.forget(); + Ok(Rc::new(VulkanBo { + allocator: self.data.clone(), + image, + memory: [memory].into_iter().collect(), + buf, + })) + } + + fn import_dmabuf( + &self, + dmabuf: &DmaBuf, + usage: BufferUsage, + ) -> Result, VulkanError> { + validate_usage(usage)?; + let data = &self.data; + let Some(format) = data.device.formats.get(&dmabuf.format.drm) else { + return Err(VulkanError::FormatNotSupported); + }; + if dmabuf.width < 0 || dmabuf.height < 0 { + return Err(VulkanError::NonPositiveImageSize); + } + let width = dmabuf.width as u32; + let height = dmabuf.height as u32; + let disjoint = dmabuf.is_disjoint(); + let image = { + if !validate_modifier( + width, + height, + usage, + disjoint, + Some(dmabuf.planes.len()), + format, + dmabuf.modifier, + ) { + return Err(VulkanError::ModifierNotSupported); + } + let plane_layouts: PlaneVec<_> = dmabuf + .planes + .iter() + .map(|p| { + SubresourceLayout::default() + .offset(p.offset as _) + .row_pitch(p.stride as _) + }) + .collect(); + let mut modifier_info = ImageDrmFormatModifierExplicitCreateInfoEXT::default() + .plane_layouts(&plane_layouts) + .drm_format_modifier(dmabuf.modifier); + let mut memory_image_create_info = ExternalMemoryImageCreateInfo::default() + .handle_types(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT); + let create_info = image_create_info(width, height, format.format, usage) + .push_next(&mut memory_image_create_info) + .push_next(&mut modifier_info); + let res = unsafe { data.device.device.create_image(&create_info, None) }; + res.map_err(VulkanError::CreateImage)? + }; + let destroy_image = OnDrop(|| unsafe { data.device.device.destroy_image(image, None) }); + let num_device_memories = match disjoint { + true => dmabuf.planes.len(), + false => 1, + }; + let mut device_memories = PlaneVec::new(); + let mut free_device_memories = PlaneVec::new(); + let mut bind_image_plane_memory_infos = PlaneVec::new(); + for plane_idx in 0..num_device_memories { + let dma_buf_plane = &dmabuf.planes[plane_idx]; + let mut memory_fd_properties = MemoryFdPropertiesKHR::default(); + unsafe { + data.device + .external_memory_fd + .get_memory_fd_properties( + ExternalMemoryHandleTypeFlags::DMA_BUF_EXT, + dma_buf_plane.fd.raw(), + &mut memory_fd_properties, + ) + .map_err(VulkanError::MemoryFdProperties)?; + } + let mut image_memory_requirements_info = + ImageMemoryRequirementsInfo2::default().image(image); + let mut image_plane_memory_requirements_info; + if disjoint { + let plane_aspect = [ + ImageAspectFlags::MEMORY_PLANE_0_EXT, + ImageAspectFlags::MEMORY_PLANE_1_EXT, + ImageAspectFlags::MEMORY_PLANE_2_EXT, + ImageAspectFlags::MEMORY_PLANE_3_EXT, + ][plane_idx]; + image_plane_memory_requirements_info = + ImagePlaneMemoryRequirementsInfo::default().plane_aspect(plane_aspect); + image_memory_requirements_info = image_memory_requirements_info + .push_next(&mut image_plane_memory_requirements_info); + bind_image_plane_memory_infos + .push(BindImagePlaneMemoryInfo::default().plane_aspect(plane_aspect)); + } + let mut memory_requirements = MemoryRequirements2::default(); + unsafe { + data.device.device.get_image_memory_requirements2( + &image_memory_requirements_info, + &mut memory_requirements, + ); + } + let memory_type_bits = memory_requirements.memory_requirements.memory_type_bits + & memory_fd_properties.memory_type_bits; + let memory_type_index = data + .device + .find_memory_type(MemoryPropertyFlags::empty(), memory_type_bits) + .ok_or(VulkanError::MemoryType)?; + let fd = uapi::fcntl_dupfd_cloexec(dma_buf_plane.fd.raw(), 0) + .map_err(|e| VulkanError::Dupfd(e.into()))?; + let mut memory_dedicated_allocate_info = + MemoryDedicatedAllocateInfo::default().image(image); + let mut import_memory_fd_info = ImportMemoryFdInfoKHR::default() + .fd(fd.raw()) + .handle_type(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT); + let memory_allocate_info = MemoryAllocateInfo::default() + .allocation_size(memory_requirements.memory_requirements.size) + .memory_type_index(memory_type_index) + .push_next(&mut import_memory_fd_info) + .push_next(&mut memory_dedicated_allocate_info); + let device_memory = unsafe { + data.device + .device + .allocate_memory(&memory_allocate_info, None) + }; + let device_memory = device_memory.map_err(VulkanError::AllocateMemory)?; + fd.unwrap(); + device_memories.push(device_memory); + free_device_memories.push(OnDrop(move || unsafe { + data.device.device.free_memory(device_memory, None) + })); + } + let mut bind_image_memory_infos = Vec::with_capacity(num_device_memories); + let mut bind_image_plane_memory_infos = bind_image_plane_memory_infos.iter_mut(); + for mem in device_memories.iter().copied() { + let mut info = BindImageMemoryInfo::default().image(image).memory(mem); + if disjoint { + info = info.push_next(bind_image_plane_memory_infos.next().unwrap()); + } + bind_image_memory_infos.push(info); + } + let res = unsafe { + data.device + .device + .bind_image_memory2(&bind_image_memory_infos) + }; + res.map_err(VulkanError::BindImageMemory)?; + destroy_image.forget(); + free_device_memories.drain(..).for_each(|m| m.forget()); + Ok(Rc::new(VulkanBo { + allocator: data.clone(), + image, + memory: device_memories, + buf: dmabuf.clone(), + })) + } +} + +impl Allocator for VulkanBoAllocator { + fn drm(&self) -> Option<&Drm> { + Some(&self.data.drm) + } + + fn create_bo( + &self, + dma_buf_ids: &DmaBufIds, + width: i32, + height: i32, + format: &'static Format, + modifiers: &[Modifier], + usage: BufferUsage, + ) -> Result, AllocatorError> { + let bo = self.create_bo(dma_buf_ids, width, height, format, modifiers, usage)?; + Ok(bo) + } + + fn import_dmabuf( + &self, + dmabuf: &DmaBuf, + usage: BufferUsage, + ) -> Result, AllocatorError> { + let bo = self.import_dmabuf(dmabuf, usage)?; + Ok(bo) + } +} + +impl VulkanBo { + fn map(self: &Rc, write: bool) -> Result { + let format = self.buf.format; + let Some(shm_info) = &format.shm_info else { + return Err(VulkanError::ShmNotSupported); + }; + let stride = self.buf.width as u32 * shm_info.bpp; + let size = self.buf.height as u32 * stride; + let data = &self.allocator; + let staging = + data.device + .create_staging_buffer(&data.allocator, size as _, write, true, true)?; + self.transfer(&staging, false, |cmd| { + let region = BufferImageCopy2::default() + .image_subresource( + ImageSubresourceLayers::default() + .aspect_mask(ImageAspectFlags::COLOR) + .layer_count(1), + ) + .image_extent(Extent3D { + width: self.buf.width as _, + height: self.buf.height as _, + depth: 1, + }); + let copy_info = CopyImageToBufferInfo2::default() + .src_image(self.image) + .src_image_layout(ImageLayout::TRANSFER_SRC_OPTIMAL) + .regions(slice::from_ref(®ion)) + .dst_buffer(staging.buffer); + unsafe { + data.device + .device + .cmd_copy_image_to_buffer2(cmd, ©_info); + } + })?; + staging.download(|_, _| ())?; + let data = unsafe { + slice::from_raw_parts_mut( + staging.allocation.mem.unwrap(), + staging.allocation.size as _, + ) + }; + Ok(VulkanBoMapping { + bo: self.clone(), + upload: write, + stride: stride as _, + staging, + data, + }) + } + + fn transfer( + &self, + staging: &VulkanStagingBuffer, + write: bool, + f: F, + ) -> Result<(), VulkanError> + where + F: FnOnce(CommandBuffer), + { + let data = &self.allocator; + let cmd = data.command_buffer.buffer; + let device = &data.device.device; + let begin = + CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT); + let initial_image_barrier = self.initial_image_barrier(write); + let final_image_barrier = self.final_image_barrier(write); + let mut initial_buffer_barrier = ArrayVec::<_, 1>::new(); + let mut final_buffer_barrier = ArrayVec::<_, 1>::new(); + if write { + initial_buffer_barrier.push( + BufferMemoryBarrier2::default() + .src_access_mask(AccessFlags2::HOST_WRITE) + .src_stage_mask(PipelineStageFlags2::HOST) + .dst_access_mask(AccessFlags2::TRANSFER_READ) + .dst_stage_mask(PipelineStageFlags2::TRANSFER) + .buffer(staging.buffer) + .size(staging.size), + ); + } else { + final_buffer_barrier.push( + BufferMemoryBarrier2::default() + .src_access_mask(AccessFlags2::TRANSFER_WRITE) + .src_stage_mask(PipelineStageFlags2::TRANSFER) + .dst_access_mask(AccessFlags2::HOST_READ | AccessFlags2::HOST_WRITE) + .dst_stage_mask(PipelineStageFlags2::HOST) + .buffer(staging.buffer) + .size(staging.size), + ); + } + let initial_dependency_info = DependencyInfo::default() + .image_memory_barriers(slice::from_ref(&initial_image_barrier)) + .buffer_memory_barriers(&initial_buffer_barrier); + let final_dependency_info = DependencyInfo::default() + .image_memory_barriers(slice::from_ref(&final_image_barrier)) + .buffer_memory_barriers(&final_buffer_barrier); + let cmd_buffer_submit_info = CommandBufferSubmitInfo::default().command_buffer(cmd); + let submit_info = + SubmitInfo2::default().command_buffer_infos(slice::from_ref(&cmd_buffer_submit_info)); + unsafe { + device + .begin_command_buffer(cmd, &begin) + .map_err(VulkanError::BeginCommandBuffer)?; + device.cmd_pipeline_barrier2(cmd, &initial_dependency_info); + f(cmd); + device.cmd_pipeline_barrier2(cmd, &final_dependency_info); + device + .end_command_buffer(cmd) + .map_err(VulkanError::EndCommandBuffer)?; + device + .queue_submit2( + data.device.graphics_queue, + slice::from_ref(&submit_info), + Fence::null(), + ) + .map_err(VulkanError::Submit)?; + device.device_wait_idle().map_err(VulkanError::WaitIdle)?; + } + Ok(()) + } + + fn get_image_barrier_flags(&self, write: bool) -> (ImageLayout, AccessFlags2) { + let layout; + let access_mask; + match write { + false => { + layout = ImageLayout::TRANSFER_SRC_OPTIMAL; + access_mask = AccessFlags2::TRANSFER_READ; + } + true => { + layout = ImageLayout::TRANSFER_DST_OPTIMAL; + access_mask = AccessFlags2::TRANSFER_WRITE; + } + } + (layout, access_mask) + } + + fn initial_image_barrier(&self, write: bool) -> ImageMemoryBarrier2<'static> { + let (new_layout, dst_access_mask) = self.get_image_barrier_flags(write); + image_barrier() + .src_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT) + .dst_queue_family_index(self.allocator.device.graphics_queue_idx) + .old_layout(ImageLayout::GENERAL) + .new_layout(new_layout) + .dst_access_mask(dst_access_mask) + .dst_stage_mask(PipelineStageFlags2::TRANSFER) + .image(self.image) + } + + fn final_image_barrier(&self, write: bool) -> ImageMemoryBarrier2<'static> { + let (old_layout, src_access_mask) = self.get_image_barrier_flags(write); + image_barrier() + .src_queue_family_index(self.allocator.device.graphics_queue_idx) + .dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT) + .old_layout(old_layout) + .new_layout(ImageLayout::GENERAL) + .src_access_mask(src_access_mask) + .src_stage_mask(PipelineStageFlags2::TRANSFER) + .image(self.image) + } +} + +impl BufferObject for VulkanBo { + fn dmabuf(&self) -> &DmaBuf { + &self.buf + } + + fn map_read(self: Rc) -> Result, AllocatorError> { + let m = self.map(false)?; + Ok(Box::new(m)) + } + + fn map_write(self: Rc) -> Result, AllocatorError> { + let m = self.map(true)?; + Ok(Box::new(m)) + } +} + +impl VulkanBoMapping { + fn upload(&self) -> Result<(), VulkanError> { + let data = &self.bo.allocator; + self.staging.upload(|_, _| ())?; + self.bo.transfer(&self.staging, true, |cmd| { + let region = BufferImageCopy2::default() + .image_subresource( + ImageSubresourceLayers::default() + .aspect_mask(ImageAspectFlags::COLOR) + .layer_count(1), + ) + .image_extent(Extent3D { + width: self.bo.buf.width as _, + height: self.bo.buf.height as _, + depth: 1, + }); + let copy_info = CopyBufferToImageInfo2::default() + .dst_image(self.bo.image) + .dst_image_layout(ImageLayout::TRANSFER_DST_OPTIMAL) + .regions(slice::from_ref(®ion)) + .src_buffer(self.staging.buffer); + unsafe { + data.device + .device + .cmd_copy_buffer_to_image2(cmd, ©_info); + } + })?; + Ok(()) + } +} + +impl Drop for VulkanBoMapping { + fn drop(&mut self) { + if self.upload { + if let Err(e) = self.upload() { + log::error!("Could not upload to image: {}", ErrorFmt(e)); + } + } + } +} + +impl MappedBuffer for VulkanBoMapping { + unsafe fn data(&self) -> &[u8] { + &*self.data + } + + fn data_ptr(&self) -> *mut u8 { + self.data as _ + } + + fn stride(&self) -> i32 { + self.stride + } +} + +fn validate_usage(usage: BufferUsage) -> Result<(), VulkanError> { + if usage.contains(!(BO_USE_WRITE | BO_USE_RENDERING)) { + return Err(VulkanError::UnsupportedBufferUsage); + } + Ok(()) +} + +fn map_usage(usage: BufferUsage) -> ImageUsageFlags { + let mut vk_usage = ImageUsageFlags::TRANSFER_SRC | ImageUsageFlags::TRANSFER_DST; + if usage.contains(BO_USE_RENDERING) { + vk_usage |= ImageUsageFlags::COLOR_ATTACHMENT; + } + vk_usage +} + +fn validate_modifier( + width: u32, + height: u32, + usage: BufferUsage, + disjoint: bool, + plane_count: Option, + format: &VulkanFormat, + modifier: Modifier, +) -> bool { + let Some(modifier) = format.modifiers.get(&modifier) else { + return false; + }; + if disjoint && !modifier.features.contains(FormatFeatureFlags::DISJOINT) { + return false; + } + if let Some(plane_count) = plane_count { + if plane_count != modifier.planes { + return false; + } + } + let Some(max_extents) = modifier.transfer_max_extents else { + return false; + }; + let mut max_width = max_extents.width; + let mut max_height = max_extents.height; + if usage.contains(BO_USE_RENDERING) { + let Some(max_extents) = modifier.render_max_extents else { + return false; + }; + max_width = max_width.min(max_extents.width); + max_height = max_height.min(max_extents.height); + } + if width > max_width || height > max_height { + return false; + } + true +} + +fn image_create_info( + width: u32, + height: u32, + format: &Format, + usage: BufferUsage, +) -> ImageCreateInfo { + let usage = map_usage(usage); + ImageCreateInfo::default() + .image_type(ImageType::TYPE_2D) + .format(format.vk_format) + .mip_levels(1) + .array_layers(1) + .tiling(ImageTiling::DRM_FORMAT_MODIFIER_EXT) + .samples(SampleCountFlags::TYPE_1) + .sharing_mode(SharingMode::EXCLUSIVE) + .initial_layout(ImageLayout::UNDEFINED) + .extent(Extent3D { + width, + height, + depth: 1, + }) + .usage(usage) +} diff --git a/src/gfx_apis/vulkan/device.rs b/src/gfx_apis/vulkan/device.rs index 8209d200..33706e7b 100644 --- a/src/gfx_apis/vulkan/device.rs +++ b/src/gfx_apis/vulkan/device.rs @@ -57,6 +57,7 @@ pub struct VulkanDevice { pub(super) external_semaphore_fd: external_semaphore_fd::Device, pub(super) external_fence_fd: external_fence_fd::Device, pub(super) push_descriptor: push_descriptor::Device, + pub(super) image_drm_format_modifier: image_drm_format_modifier::Device, pub(super) formats: AHashMap, pub(super) memory_types: ArrayVec, pub(super) graphics_queue: Queue, @@ -104,7 +105,8 @@ impl VulkanInstance { Err(e) => return Err(VulkanError::Fstat(e.into())), }; let dev = stat.st_rdev; - log::info!( + log::log!( + self.log_level, "Searching for vulkan device with devnum {}:{}", uapi::major(dev), uapi::minor(dev) @@ -153,8 +155,13 @@ impl VulkanInstance { let render_dev = uapi::makedev(drm_props.render_major as _, drm_props.render_minor as _); if primary_dev == dev || render_dev == dev { - log::info!("Device with id {} matches", props.device_id); - log_device(&props, Some(&extensions), Some(&driver_props)); + log::log!(self.log_level, "Device with id {} matches", props.device_id); + log_device( + self.log_level, + &props, + Some(&extensions), + Some(&driver_props), + ); return Ok(phy_dev); } devices.push((props, Some(extensions), Some(driver_props))); @@ -166,7 +173,12 @@ impl VulkanInstance { for (props, extensions, driver_props) in devices.iter() { log::warn!("Found the following devices but none matches:"); log::warn!("-----"); - log_device(props, extensions.as_ref(), driver_props.as_ref()); + log_device( + self.log_level, + props, + extensions.as_ref(), + driver_props.as_ref(), + ); } } Err(VulkanError::NoDeviceFound(dev)) @@ -264,6 +276,8 @@ impl VulkanInstance { let external_semaphore_fd = external_semaphore_fd::Device::new(&self.instance, &device); let external_fence_fd = external_fence_fd::Device::new(&self.instance, &device); let push_descriptor = push_descriptor::Device::new(&self.instance, &device); + let image_drm_format_modifier = + image_drm_format_modifier::Device::new(&self.instance, &device); let memory_properties = unsafe { self.instance.get_physical_device_memory_properties(phy_dev) }; let memory_types = memory_properties.memory_types @@ -283,6 +297,7 @@ impl VulkanInstance { external_semaphore_fd, external_fence_fd, push_descriptor, + image_drm_format_modifier, formats, memory_types, graphics_queue, @@ -302,20 +317,27 @@ const REQUIRED_DEVICE_EXTENSIONS: &[&CStr] = &[ ]; fn log_device( + level: log::Level, props: &PhysicalDeviceProperties, extensions: Option<&Extensions>, driver_props: Option<&PhysicalDeviceDriverProperties>, ) { - log::info!(" api version: {}", ApiVersionDisplay(props.api_version)); - log::info!( + log::log!( + level, + " api version: {}", + ApiVersionDisplay(props.api_version) + ); + log::log!( + level, " driver version: {}", ApiVersionDisplay(props.driver_version) ); - log::info!(" vendor id: {}", props.vendor_id); - log::info!(" device id: {}", props.device_id); - log::info!(" device type: {:?}", props.device_type); + log::log!(level, " vendor id: {}", props.vendor_id); + log::log!(level, " device id: {}", props.device_id); + log::log!(level, " device type: {:?}", props.device_type); unsafe { - log::info!( + log::log!( + level, " device name: {}", Ustr::from_ptr(props.device_name.as_ptr()).display() ); @@ -330,7 +352,8 @@ fn log_device( } if let Some(driver_props) = driver_props { unsafe { - log::info!( + log::log!( + level, " driver: {} ({})", Ustr::from_ptr(driver_props.driver_name.as_ptr()).display(), Ustr::from_ptr(driver_props.driver_info.as_ptr()).display() diff --git a/src/gfx_apis/vulkan/format.rs b/src/gfx_apis/vulkan/format.rs index 7ad829e6..03cda4f5 100644 --- a/src/gfx_apis/vulkan/format.rs +++ b/src/gfx_apis/vulkan/format.rs @@ -34,6 +34,7 @@ pub struct VulkanModifier { pub features: FormatFeatureFlags, pub render_max_extents: Option, pub texture_max_extents: Option, + pub transfer_max_extents: Option, pub render_needs_bridge: bool, } @@ -58,24 +59,24 @@ const TEX_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw( | FormatFeatureFlags::TRANSFER_SRC.as_raw() | FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR.as_raw(), ); -const SHM_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw( - 0 | FormatFeatureFlags::TRANSFER_SRC.as_raw() - | FormatFeatureFlags::TRANSFER_DST.as_raw() - | TEX_FEATURES.as_raw(), +const TRANSFER_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw( + FormatFeatureFlags::TRANSFER_SRC.as_raw() | FormatFeatureFlags::TRANSFER_DST.as_raw(), ); +const SHM_FEATURES: FormatFeatureFlags = + FormatFeatureFlags::from_raw(TRANSFER_FEATURES.as_raw() | TEX_FEATURES.as_raw()); const FRAMEBUFFER_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw( - 0 | ImageUsageFlags::COLOR_ATTACHMENT.as_raw() | ImageUsageFlags::TRANSFER_SRC.as_raw(), + ImageUsageFlags::COLOR_ATTACHMENT.as_raw() | ImageUsageFlags::TRANSFER_SRC.as_raw(), ); const FRAMEBUFFER_BRIDGED_USAGE: ImageUsageFlags = ImageUsageFlags::TRANSFER_DST; const TEX_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw( - 0 | ImageUsageFlags::SAMPLED.as_raw() | ImageUsageFlags::TRANSFER_SRC.as_raw(), + ImageUsageFlags::SAMPLED.as_raw() | ImageUsageFlags::TRANSFER_SRC.as_raw(), ); -const SHM_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw( - 0 | ImageUsageFlags::TRANSFER_SRC.as_raw() - | ImageUsageFlags::TRANSFER_DST.as_raw() - | TEX_USAGE.as_raw(), +const TRANSFER_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw( + ImageUsageFlags::TRANSFER_SRC.as_raw() | ImageUsageFlags::TRANSFER_DST.as_raw(), ); +const SHM_USAGE: ImageUsageFlags = + ImageUsageFlags::from_raw(TRANSFER_USAGE.as_raw() | TEX_USAGE.as_raw()); impl VulkanInstance { pub(super) fn load_formats( @@ -209,6 +210,13 @@ impl VulkanInstance { )?; let texture_max_extents = self.get_max_extents(phy_dev, format, TEX_FEATURES, TEX_USAGE, &modifier)?; + let transfer_max_extents = self.get_max_extents( + phy_dev, + format, + TRANSFER_FEATURES, + TRANSFER_USAGE, + &modifier, + )?; let mut render_needs_bridge = false; if render_max_extents.is_none() && modifier.drm_format_modifier == LINEAR_MODIFIER { render_max_extents = self.get_fb_bridged_max_extents( @@ -229,6 +237,7 @@ impl VulkanInstance { features: modifier.drm_format_modifier_tiling_features, render_max_extents, texture_max_extents, + transfer_max_extents, render_needs_bridge, }, ); diff --git a/src/gfx_apis/vulkan/instance.rs b/src/gfx_apis/vulkan/instance.rs index 66dde3b9..aba36851 100644 --- a/src/gfx_apis/vulkan/instance.rs +++ b/src/gfx_apis/vulkan/instance.rs @@ -1,9 +1,5 @@ use { - crate::{ - async_engine::AsyncEngine, - gfx_apis::vulkan::{util::OnDrop, VulkanError, VULKAN_VALIDATION}, - io_uring::IoUring, - }, + crate::gfx_apis::vulkan::{util::OnDrop, VulkanError, VULKAN_VALIDATION}, ahash::{AHashMap, AHashSet}, ash::{ ext::{debug_utils, validation_features}, @@ -35,16 +31,11 @@ pub struct VulkanInstance { pub(super) instance: Instance, pub(super) debug_utils: debug_utils::Instance, pub(super) messenger: DebugUtilsMessengerEXT, - pub(super) eng: Rc, - pub(super) ring: Rc, + pub(super) log_level: Level, } impl VulkanInstance { - pub fn new( - eng: &Rc, - ring: &Rc, - validation: bool, - ) -> Result, VulkanError> { + pub fn new(log_level: Level, validation: bool) -> Result, VulkanError> { static ENTRY: Lazy>> = Lazy::new(|| unsafe { Entry::load() }.map_err(Arc::new)); let entry = match &*ENTRY { @@ -127,8 +118,7 @@ impl VulkanInstance { instance, debug_utils, messenger, - eng: eng.clone(), - ring: ring.clone(), + log_level, })) } } diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index df7c9fa8..36716a36 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -1,6 +1,6 @@ use { crate::{ - async_engine::SpawnedFuture, + async_engine::{AsyncEngine, SpawnedFuture}, format::Format, gfx_api::{ AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxFormat, GfxFramebuffer, @@ -68,6 +68,8 @@ pub struct VulkanRenderer { pub(super) allocator: Rc, pub(super) last_point: NumCell, pub(super) buffer_resv_user: BufferResvUser, + pub(super) eng: Rc, + pub(super) ring: Rc, } pub(super) struct UsedTexture { @@ -111,7 +113,11 @@ pub(super) struct PendingFrame { } impl VulkanDevice { - pub fn create_renderer(self: &Rc) -> Result, VulkanError> { + pub fn create_renderer( + self: &Rc, + eng: &Rc, + ring: &Rc, + ) -> Result, VulkanError> { let fill_pipeline = self.create_pipeline::( PipelineCreateInfo { vert: self.create_shader(FILL_VERT)?, @@ -196,6 +202,8 @@ impl VulkanDevice { allocator, last_point: Default::default(), buffer_resv_user: Default::default(), + eng: eng.clone(), + ring: ring.clone(), })) } } @@ -691,9 +699,9 @@ impl VulkanRenderer { _release_fence: memory.release_fence.take(), }); self.pending_frames.set(frame.point, frame.clone()); - let future = self.device.instance.eng.spawn(await_release( + let future = self.eng.spawn(await_release( memory.release_sync_file.clone(), - self.device.instance.ring.clone(), + self.ring.clone(), frame.clone(), self.clone(), )); @@ -776,7 +784,9 @@ impl VulkanRenderer { height: tex.height, depth: 1, }); - let staging = self.create_staging_buffer(size, false, true, true)?; + let staging = + self.device + .create_staging_buffer(&self.allocator, size, false, true, true)?; let initial_tex_barrier; let initial_buffer_barrier = BufferMemoryBarrier2::default() .buffer(staging.buffer) diff --git a/src/gfx_apis/vulkan/shm_image.rs b/src/gfx_apis/vulkan/shm_image.rs index d2a8666c..b80cf390 100644 --- a/src/gfx_apis/vulkan/shm_image.rs +++ b/src/gfx_apis/vulkan/shm_image.rs @@ -104,9 +104,13 @@ impl VulkanShmImage { cpy = slice::from_ref(&cpy_one); total_size = img.height * img.stride; } - let staging = img - .renderer - .create_staging_buffer(total_size as u64, true, false, true)?; + let staging = img.renderer.device.create_staging_buffer( + &img.renderer.allocator, + total_size as u64, + true, + false, + true, + )?; staging.upload(|mem, _| unsafe { let buf = buffer.as_ptr() as *const u8; if damage.is_some() { @@ -215,7 +219,7 @@ impl VulkanShmImage { } }; let point = img.renderer.allocate_point(); - let future = img.renderer.device.instance.eng.spawn(await_upload( + let future = img.renderer.eng.spawn(await_upload( point, img.clone(), cmd, @@ -236,13 +240,7 @@ async fn await_upload( _fence: Rc, _staging: VulkanStagingBuffer, ) { - let res = img - .renderer - .device - .instance - .ring - .readable(&sync_file.0) - .await; + let res = img.renderer.ring.readable(&sync_file.0).await; if let Err(e) = res { log::error!( "Could not wait for sync file to become readable: {}", diff --git a/src/gfx_apis/vulkan/staging.rs b/src/gfx_apis/vulkan/staging.rs index d74d0c19..7e205a81 100644 --- a/src/gfx_apis/vulkan/staging.rs +++ b/src/gfx_apis/vulkan/staging.rs @@ -1,6 +1,8 @@ use { crate::gfx_apis::vulkan::{ - allocator::VulkanAllocation, device::VulkanDevice, renderer::VulkanRenderer, util::OnDrop, + allocator::{VulkanAllocation, VulkanAllocator}, + device::VulkanDevice, + util::OnDrop, VulkanError, }, ash::vk::{Buffer, BufferCreateInfo, BufferUsageFlags, MappedMemoryRange}, @@ -15,9 +17,10 @@ pub struct VulkanStagingBuffer { pub(super) size: u64, } -impl VulkanRenderer { +impl VulkanDevice { pub(super) fn create_staging_buffer( self: &Rc, + allocator: &Rc, size: u64, upload: bool, download: bool, @@ -38,24 +41,22 @@ impl VulkanRenderer { } let buffer = { let create_info = BufferCreateInfo::default().size(size).usage(vk_usage); - let buffer = unsafe { self.device.device.create_buffer(&create_info, None) }; + let buffer = unsafe { self.device.create_buffer(&create_info, None) }; buffer.map_err(VulkanError::CreateBuffer)? }; - let destroy_buffer = OnDrop(|| unsafe { self.device.device.destroy_buffer(buffer, None) }); - let memory_requirements = - unsafe { self.device.device.get_buffer_memory_requirements(buffer) }; - let allocation = self.allocator.alloc(&memory_requirements, usage, true)?; + let destroy_buffer = OnDrop(|| unsafe { self.device.destroy_buffer(buffer, None) }); + let memory_requirements = unsafe { self.device.get_buffer_memory_requirements(buffer) }; + let allocation = allocator.alloc(&memory_requirements, usage, true)?; { let res = unsafe { self.device - .device .bind_buffer_memory(buffer, allocation.memory, allocation.offset) }; res.map_err(VulkanError::BindBufferMemory)?; } destroy_buffer.forget(); Ok(VulkanStagingBuffer { - device: self.device.clone(), + device: self.clone(), allocation, buffer, size, diff --git a/src/it/test_backend.rs b/src/it/test_backend.rs index 489ca9b6..ded416bb 100644 --- a/src/it/test_backend.rs +++ b/src/it/test_backend.rs @@ -1,6 +1,6 @@ use { crate::{ - allocator::Allocator, + allocator::{Allocator, AllocatorError}, async_engine::SpawnedFuture, backend::{ AxisSource, Backend, BackendEvent, Connector, ConnectorEvent, ConnectorId, @@ -12,6 +12,7 @@ use { drm_feedback::DrmFeedback, fixed::Fixed, gfx_api::GfxError, + gfx_apis::create_vulkan_allocator, it::{ test_error::TestResult, test_gfx_api::TestGfxCtx, test_utils::test_expected_event::TEEH, }, @@ -46,6 +47,8 @@ pub enum TestBackendError { CreateGbmDevice(#[source] GbmError), #[error("Could not create any allocator")] CreateAllocator, + #[error("Could not create a vulkan allocator")] + CreateVulkanAllocator(#[source] AllocatorError), } pub struct TestBackend { @@ -133,21 +136,21 @@ impl TestBackend { } } - pub fn install_render_context(&self, prefer_udmabuf: bool) -> TestResult { + pub fn install_render_context(&self, need_drm: bool) -> TestResult { if self.render_context_installed.get() { return Ok(()); } - self.create_render_context(prefer_udmabuf)?; + self.create_render_context(need_drm)?; self.render_context_installed.set(true); Ok(()) } pub fn install_default(&self) -> TestResult { - self.install_default2(true) + self.install_default2(false) } - pub fn install_default2(&self, prefer_udmabuf: bool) -> TestResult { - self.install_render_context(prefer_udmabuf)?; + pub fn install_default2(&self, need_drm: bool) -> TestResult { + self.install_render_context(need_drm)?; self.state .backend_events .push(BackendEvent::NewConnector(self.default_connector.clone())); @@ -163,20 +166,25 @@ impl TestBackend { Ok(()) } - fn create_render_context(&self, prefer_udmabuf: bool) -> Result<(), TestBackendError> { + fn create_render_context(&self, need_drm: bool) -> Result<(), TestBackendError> { macro_rules! constructor { ($c:expr) => { - (&|| { - $c.map(|a| Rc::new(a) as Rc) - .map_err(|e| Box::new(e) as Box) - }) as &dyn Fn() -> Result, Box> + constructor!($c, |a| Rc::new(a) as Rc) + }; + ($c:expr, nomap) => { + constructor!($c, |a| a) + }; + ($c:expr, $map:expr) => { + (&|| $c.map($map).map_err(|e| Box::new(e) as Box)) + as &dyn Fn() -> Result, Box> }; } let udmabuf = ("udmabuf", constructor!(Udmabuf::new())); + let vulkan = ("Vulkan", constructor!(create_vk_allocator(), nomap)); let gbm = ("GBM", constructor!(create_gbm_allocator())); - let allocators = match prefer_udmabuf { - true => [udmabuf, gbm], - false => [gbm, udmabuf], + let allocators = match need_drm { + true => [vulkan, gbm, udmabuf], + false => [udmabuf, vulkan, gbm], }; let mut allocator = None::>; for (name, f) in allocators { @@ -201,6 +209,19 @@ impl TestBackend { } fn create_gbm_allocator() -> Result { + create_drm_allocator(|drm| GbmDevice::new(&drm).map_err(TestBackendError::CreateGbmDevice)) +} + +fn create_vk_allocator() -> Result, TestBackendError> { + create_drm_allocator(|drm| { + create_vulkan_allocator(&drm).map_err(TestBackendError::CreateVulkanAllocator) + }) +} + +fn create_drm_allocator(f: F) -> Result +where + F: FnOnce(Drm) -> Result, +{ let dri = match std::fs::read_dir("/dev/dri") { Ok(d) => d, Err(e) => return Err(TestBackendError::ReadDri(e)), @@ -240,8 +261,7 @@ fn create_gbm_allocator() -> Result { } }; let drm = Drm::open_existing(file); - let gbm = GbmDevice::new(&drm).map_err(TestBackendError::CreateGbmDevice)?; - Ok(gbm) + f(drm) } impl Backend for TestBackend { diff --git a/src/it/testrun.rs b/src/it/testrun.rs index d9b99ff0..13d03a70 100644 --- a/src/it/testrun.rs +++ b/src/it/testrun.rs @@ -106,14 +106,11 @@ impl TestRun { } pub async fn create_default_setup(&self) -> Result { - self.create_default_setup2(true).await + self.create_default_setup2(false).await } - pub async fn create_default_setup2( - &self, - prefer_udmabuf: bool, - ) -> Result { - self.backend.install_default2(prefer_udmabuf)?; + pub async fn create_default_setup2(&self, need_drm: bool) -> Result { + self.backend.install_default2(need_drm)?; let seat = self.get_seat("default")?; self.state.eng.yield_now().await; let output = match self.state.root.outputs.lock().values().next() { diff --git a/src/it/tests/t0013_graphics_initialized.rs b/src/it/tests/t0013_graphics_initialized.rs index 5fcc7f4f..769ad132 100644 --- a/src/it/tests/t0013_graphics_initialized.rs +++ b/src/it/tests/t0013_graphics_initialized.rs @@ -10,7 +10,7 @@ async fn test(run: Rc) -> TestResult { tassert!(!run.cfg.graphics_initialized.get()); - run.backend.install_render_context(true)?; + run.backend.install_render_context(false)?; tassert!(run.cfg.graphics_initialized.get()); diff --git a/src/it/tests/t0031_syncobj.rs b/src/it/tests/t0031_syncobj.rs index 9a15c4ee..451d049b 100644 --- a/src/it/tests/t0031_syncobj.rs +++ b/src/it/tests/t0031_syncobj.rs @@ -11,7 +11,7 @@ use { testcase!(); async fn test(run: Rc) -> TestResult { - let _ds = run.create_default_setup2(false).await?; + let _ds = run.create_default_setup2(true).await?; struct Waiter(Cell); impl SyncObjWaiter for Waiter { diff --git a/src/it/tests/t0035_scanout_feedback.rs b/src/it/tests/t0035_scanout_feedback.rs index 80e74841..bd770db0 100644 --- a/src/it/tests/t0035_scanout_feedback.rs +++ b/src/it/tests/t0035_scanout_feedback.rs @@ -12,7 +12,7 @@ use { testcase!(); async fn test(run: Rc) -> TestResult { - let ds = run.create_default_setup2(false).await?; + let ds = run.create_default_setup2(true).await?; let scanout_feedback = { let Some(base_fb) = run.state.drm_feedback.get() else {