diff --git a/src/cli/screenshot.rs b/src/cli/screenshot.rs index 7402453b..8e8894e6 100644 --- a/src/cli/screenshot.rs +++ b/src/cli/screenshot.rs @@ -2,6 +2,7 @@ use { crate::{ allocator::{Allocator, AllocatorError, BufferUsage, MappedBuffer}, cli::{GlobalArgs, ScreenshotArgs, ScreenshotFormat}, + eventfd_cache::EventfdCache, format::XRGB8888, gfx_apis, tools::tool_client::{Handle, ToolClient, with_tool_client}, @@ -109,7 +110,8 @@ async fn run(screenshot: Rc) { } }; let format = screenshot.args.format; - let data = match buf_to_bytes(drm_dev.as_ref(), &buf, format) { + let eventfd_cache = EventfdCache::new(&tc.ring, &tc.eng); + let data = match buf_to_bytes(&eventfd_cache, drm_dev.as_ref(), &buf, format) { Ok(d) => d, Err(e) => fatal!("{}", ErrorFmt(e)), }; @@ -159,6 +161,7 @@ fn map( } pub fn buf_to_bytes( + eventfd_cache: &Rc, drm_dev: Option<&Rc>, buf: &DmaBuf, format: ScreenshotFormat, @@ -174,7 +177,7 @@ pub fn buf_to_bytes( .map_err(ScreenshotError::CreateGbmDevice) }); let vulkan = Box::new(move || { - gfx_apis::create_vulkan_allocator(&drm()?) + gfx_apis::create_vulkan_allocator(&drm()?, eventfd_cache) .map_err(ScreenshotError::CreateVulkanAllocator) }); allocators.push(vulkan); diff --git a/src/compositor.rs b/src/compositor.rs index 3a2957ff..fa6e9835 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -214,6 +214,7 @@ fn start_compositor2( let cpu_worker = Rc::new(CpuWorker::new(&ring, &engine)?); let color_manager = ColorManager::new(); let crit_ids = Rc::new(CritMatcherIds::default()); + let eventfd_cache = EventfdCache::new(&ring, &engine); let state = Rc::new(State { kb_ctx, backend: CloneCell::new(Rc::new(DummyBackend)), @@ -373,9 +374,9 @@ fn start_compositor2( outputs_without_hc: Default::default(), udmabuf: Default::default(), gfx_ctx_changed: Default::default(), - copy_device_registry: Rc::new(CopyDeviceRegistry::new(&ring, &engine)), + copy_device_registry: Rc::new(CopyDeviceRegistry::new(&ring, &engine, &eventfd_cache)), supports_presentation_feedback: Default::default(), - eventfd_cache: EventfdCache::new(&ring, &engine), + eventfd_cache, }); state.tracker.register(ClientId::from_raw(0)); create_dummy_output(&state); diff --git a/src/copy_device.rs b/src/copy_device.rs index 84fa9fe6..9313ed03 100644 --- a/src/copy_device.rs +++ b/src/copy_device.rs @@ -1,21 +1,24 @@ use { crate::{ async_engine::{AsyncEngine, SpawnedFuture}, + eventfd_cache::EventfdCache, format::{FORMATS, Format}, gfx_api::FdSync, io_uring::IoUring, rect::{Rect, Region}, utils::{ clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, - queue::AsyncQueue, stack::Stack, + oserror::OsError, queue::AsyncQueue, stack::Stack, }, video::{ LINEAR_MODIFIER, LINEAR_STRIDE_ALIGN, Modifier, dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec}, + drm::{NodeType, get_drm_nodes_from_dev, syncobj::SyncobjCtx}, }, vulkan_core::{ self, VULKAN_API_VERSION, VulkanCoreError, VulkanCoreInstance, device::VulkanDeviceInf, map_extension_properties, sync::VulkanDeviceSyncExt, + timeline_semaphore::VulkanDeviceTimelineSemaphoreExt, }, }, ahash::{AHashMap, AHashSet}, @@ -54,9 +57,10 @@ use { PhysicalDeviceExternalImageFormatInfoKHR, PhysicalDeviceExternalSemaphoreInfo, PhysicalDeviceFeatures2, PhysicalDeviceImageDrmFormatModifierInfoEXT, PhysicalDeviceImageFormatInfo2, PhysicalDeviceProperties2, - PhysicalDeviceSynchronization2Features, PipelineStageFlags2, QUEUE_FAMILY_FOREIGN_EXT, - Queue, QueueFlags, SampleCountFlags, SemaphoreCreateInfo, SemaphoreImportFlags, - SemaphoreSubmitInfo, SharingMode, SubmitInfo2, SubresourceLayout, WHOLE_SIZE, + PhysicalDeviceSynchronization2Features, PhysicalDeviceTimelineSemaphoreFeatures, + PipelineStageFlags2, QUEUE_FAMILY_FOREIGN_EXT, Queue, QueueFlags, SampleCountFlags, + SemaphoreCreateInfo, SemaphoreImportFlags, SemaphoreSubmitInfo, SharingMode, + SubmitInfo2, SubresourceLayout, WHOLE_SIZE, }, }, bstr::ByteSlice, @@ -162,6 +166,12 @@ pub enum CopyDeviceError { BothOffDevice, #[error("Cannot blit between these formats")] BlitNotSupported, + #[error("Could not get DRM nodes")] + GetDrmNodes(#[source] OsError), + #[error("Device has no device nodes")] + NoDeviceNodes, + #[error("Could not open device node")] + OpenDeviceNode(#[source] OsError), } type Keyed = StaticMap; @@ -170,12 +180,15 @@ type KeyedCopy = StaticCopyMap; pub struct PhysicalCopyDevice { ring: Rc, eng: Rc, + eventfd_cache: Rc, + sync_ctx: Rc, instance: VulkanCoreInstance, physical_device: PhysicalDevice, support: AHashMap>>, queues_to_allocate: Vec, queues: KeyedCopy, supports_dmabuf_export: bool, + supports_timeline_opaque_export: bool, memory_types: Vec, rects: RefCell>, buffer_copy_2: RefCell>>, @@ -201,6 +214,7 @@ struct QueueIndex { pub struct CopyDevice { _tasks: Vec>, dev: Rc, + timeline_semaphore: Option>, } struct CopyDeviceInner { @@ -225,7 +239,7 @@ struct PendingSubmissions { pub struct CopyDeviceCopy { inner: Rc, - _dev: Rc, + dev: Rc, } struct CopyDeviceCopyInner { @@ -283,6 +297,8 @@ struct VulkanSemaphore { } type VulkanFence = vulkan_core::fence::VulkanFence; +type VulkanTimelineSemaphore = + vulkan_core::timeline_semaphore::VulkanTimelineSemaphore; type VulkanSync = vulkan_core::sync::VulkanSync; struct VulkanBuffer { @@ -336,6 +352,7 @@ struct ClassifiedDmabuf<'a> { pub struct CopyDeviceRegistry { ring: Rc, eng: Rc, + eventfd_cache: Rc, devs: CopyHashMap>>, } @@ -352,6 +369,7 @@ impl PhysicalCopyDevice { fn new( ring: &Rc, eng: &Rc, + eventfd_cache: &Rc, dev: c::dev_t, ) -> Result, CopyDeviceError> { let core_instance = VulkanCoreInstance::new(Level::Debug)?; @@ -404,6 +422,16 @@ impl PhysicalCopyDevice { } return Err(CopyDeviceError::NoVulkanDevice); } + let nodes = get_drm_nodes_from_dev(uapi::major(dev), uapi::minor(dev)) + .map_err(CopyDeviceError::GetDrmNodes)?; + let path = nodes + .get(&NodeType::Render) + .or_else(|| nodes.get(&NodeType::Primary)) + .ok_or(CopyDeviceError::NoDeviceNodes)?; + let device_fd = uapi::open(path.as_c_str(), c::O_RDWR, 0) + .map(Rc::new) + .map_err(Into::into) + .map_err(CopyDeviceError::OpenDeviceNode)?; if device_properties.api_version < VULKAN_API_VERSION { return Err(CopyDeviceError::NoVulkan13); } @@ -606,15 +634,21 @@ impl PhysicalCopyDevice { } let memory_info = unsafe { instance.get_physical_device_memory_properties(physical_device) }; + let features = core_instance.get_features(physical_device); + let supports_timeline_opaque_export = + core_instance.supports_timeline_opaque_export(physical_device, &features); let dev = Rc::new(PhysicalCopyDevice { ring: ring.clone(), eng: eng.clone(), + eventfd_cache: eventfd_cache.clone(), + sync_ctx: Rc::new(SyncobjCtx::new(&device_fd)), instance: core_instance, physical_device, support, queues_to_allocate, queues: queue_indices, supports_dmabuf_export, + supports_timeline_opaque_export, memory_types: memory_info.memory_types_as_slice().to_vec(), rects: Default::default(), buffer_copy_2: Default::default(), @@ -654,11 +688,14 @@ impl PhysicalCopyDevice { }) .collect(); let extensions = DEVICE_EXTENSIONS.map(|e| e.as_ptr()); + let mut semaphore_features = PhysicalDeviceTimelineSemaphoreFeatures::default() + .timeline_semaphore(self.supports_timeline_opaque_export); let mut synchronization2_features = PhysicalDeviceSynchronization2Features::default().synchronization2(true); let info = DeviceCreateInfo::default() .queue_create_infos(&queue_create_info) .enabled_extension_names(&extensions) + .push_next(&mut semaphore_features) .push_next(&mut synchronization2_features); unsafe { instance @@ -722,7 +759,11 @@ impl PhysicalCopyDevice { let task = self.eng.spawn("copy-device-await-pending", future); tasks.push(task); } - let queue = Rc::new(CopyDevice { dev, _tasks: tasks }); + let queue = Rc::new(CopyDevice { + timeline_semaphore: dev.create_timeline_semaphore_or_log(), + dev, + _tasks: tasks, + }); Ok(queue) } } @@ -1129,7 +1170,7 @@ impl CopyDevice { tt, ty, }), - _dev: self.clone(), + dev: self.clone(), }) } @@ -1647,10 +1688,15 @@ impl CopyDeviceCopy { wait_semaphore = Some(semaphore); } let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(cmd); - let submit_info = SubmitInfo2::default() + let mut semaphore_submit_info = SemaphoreSubmitInfo::default(); + let mut submit_info = SubmitInfo2::default() .command_buffer_infos(slice::from_ref(&command_buffer_info)) .wait_semaphore_infos(&wait_semaphores); - let vulkan_sync = slf.dev.create_sync()?; + let vulkan_sync = slf.dev.create_sync( + self.dev.timeline_semaphore.as_ref(), + &mut semaphore_submit_info, + &mut submit_info, + )?; unsafe { slf.dev .dev @@ -1698,10 +1744,15 @@ impl VulkanSemaphore { } impl CopyDeviceRegistry { - pub fn new(ring: &Rc, eng: &Rc) -> Self { + pub fn new( + ring: &Rc, + eng: &Rc, + eventfd_cache: &Rc, + ) -> Self { Self { ring: ring.clone(), eng: eng.clone(), + eventfd_cache: eventfd_cache.clone(), devs: Default::default(), } } @@ -1714,7 +1765,7 @@ impl CopyDeviceRegistry { if let Some(dev) = self.devs.get(&dev) { return dev; } - match PhysicalCopyDevice::new(&self.ring, &self.eng, dev).map(Some) { + match PhysicalCopyDevice::new(&self.ring, &self.eng, &self.eventfd_cache, dev).map(Some) { Ok(cd) => { self.devs.set(dev, cd.clone()); cd @@ -1977,6 +2028,10 @@ fn allocate_queues( } impl VulkanDeviceInf for CopyDeviceInner { + fn instance(&self) -> &VulkanCoreInstance { + &self.phy.instance + } + fn device(&self) -> &Device { &self.dev } @@ -1984,4 +2039,20 @@ impl VulkanDeviceInf for CopyDeviceInner { fn external_fence_fd(&self) -> &external_fence_fd::Device { &self.external_fence_fd } + + fn external_semaphore_fd(&self) -> &external_semaphore_fd::Device { + &self.external_semaphore_fd + } + + fn supports_timeline_opaque_export(&self) -> bool { + self.phy.supports_timeline_opaque_export + } + + fn sync_ctx(&self) -> &Rc { + &self.phy.sync_ctx + } + + fn eventfd_cache(&self) -> &Rc { + &self.phy.eventfd_cache + } } diff --git a/src/eventfd_cache.rs b/src/eventfd_cache.rs index e2905bce..de9f2000 100644 --- a/src/eventfd_cache.rs +++ b/src/eventfd_cache.rs @@ -46,7 +46,6 @@ impl EventfdCache { Rc::new(Self { inner, _task: task }) } - #[cfg_attr(not(test), expect(dead_code))] pub fn acquire(&self) -> Result { let fd = match self.inner.fds.pop() { Some(fd) => fd, diff --git a/src/gfx_api.rs b/src/gfx_api.rs index 43a61c7f..f178b753 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -1124,7 +1124,6 @@ impl Debug for ReservedSyncobjPoint { #[derive(Clone, Debug)] pub enum FdSync { SyncFile(SyncFile), - #[expect(dead_code)] Syncobj(Rc), } diff --git a/src/gfx_apis.rs b/src/gfx_apis.rs index f0d1aaa2..bb9b48a9 100644 --- a/src/gfx_apis.rs +++ b/src/gfx_apis.rs @@ -2,6 +2,7 @@ pub use vulkan::create_vulkan_allocator; use { crate::{ async_engine::AsyncEngine, + eventfd_cache::EventfdCache, gfx_api::{GfxApi, GfxContext, GfxError}, io_uring::IoUring, pr_caps::PrCapsThread, @@ -17,6 +18,7 @@ mod vulkan; pub fn create_gfx_context( eng: &Rc, ring: &Rc, + eventfd_cache: &Rc, drm: &Drm, api: GfxApi, caps_thread: Option<&PrCapsThread>, @@ -26,7 +28,8 @@ pub fn create_gfx_context( let mut last_err = None; for software in [false, true] { for api in apis { - let res = create_gfx_context_(eng, ring, drm, api, caps_thread, software); + let res = + create_gfx_context_(eng, ring, eventfd_cache, drm, api, caps_thread, software); match res { Ok(_) => { log::info!("Created a {api:?} renderer"); @@ -48,6 +51,7 @@ pub fn create_gfx_context( fn create_gfx_context_( eng: &Rc, ring: &Rc, + eventfd_cache: &Rc, drm: &Drm, api: GfxApi, caps_thread: Option<&PrCapsThread>, @@ -55,6 +59,8 @@ fn create_gfx_context_( ) -> Result, GfxError> { match api { GfxApi::OpenGl => gl::create_gfx_context(drm, software), - GfxApi::Vulkan => vulkan::create_graphics_context(eng, ring, drm, caps_thread, software), + GfxApi::Vulkan => { + vulkan::create_graphics_context(eng, ring, eventfd_cache, drm, caps_thread, software) + } } } diff --git a/src/gfx_apis/vulkan.rs b/src/gfx_apis/vulkan.rs index 0297f0ca..78a1f396 100644 --- a/src/gfx_apis/vulkan.rs +++ b/src/gfx_apis/vulkan.rs @@ -27,6 +27,7 @@ use { allocator::{Allocator, AllocatorError}, async_engine::AsyncEngine, cpu_worker::{CpuWorker, jobs::read_write::ReadWriteJobError}, + eventfd_cache::EventfdCache, format::Format, gfx_api::{ AsyncShmGfxTexture, GfxApi, GfxBlendBuffer, GfxBuffer, GfxContext, GfxError, GfxFormat, @@ -212,6 +213,8 @@ pub enum VulkanError { } type VulkanSync = vulkan_core::sync::VulkanSync; +type VulkanTimelineSemaphore = + vulkan_core::timeline_semaphore::VulkanTimelineSemaphore; impl From for GfxError { fn from(value: VulkanError) -> Self { @@ -222,6 +225,7 @@ impl From for GfxError { pub fn create_graphics_context( eng: &Rc, ring: &Rc, + eventfd_cache: &Rc, drm: &Drm, caps_thread: Option<&PrCapsThread>, software: bool, @@ -229,22 +233,25 @@ pub fn create_graphics_context( let instance = VulkanInstance::new(Level::Info)?; let device = 'device: { if let Some(t) = caps_thread { - match unsafe { t.run(|| instance.create_device(drm, true, software)) } { + match unsafe { t.run(|| instance.create_device(drm, eventfd_cache, true, software)) } { Ok(d) => break 'device d, Err(e) => { log::warn!("Could not create high-priority device: {}", ErrorFmt(e)); } } } - instance.create_device(drm, false, software)? + instance.create_device(drm, eventfd_cache, false, software)? }; let renderer = device.create_renderer(eng, ring)?; Ok(Rc::new(Context(renderer))) } -pub fn create_vulkan_allocator(drm: &Drm) -> Result, AllocatorError> { +pub fn create_vulkan_allocator( + drm: &Drm, + eventfd_cache: &Rc, +) -> Result, AllocatorError> { let instance = VulkanInstance::new(Level::Debug)?; - let device = instance.create_device(drm, false, false)?; + let device = instance.create_device(drm, eventfd_cache, false, false)?; let allocator = device.create_bo_allocator(drm)?; Ok(Rc::new(allocator)) } diff --git a/src/gfx_apis/vulkan/device.rs b/src/gfx_apis/vulkan/device.rs index 3d1927e1..f65e8e06 100644 --- a/src/gfx_apis/vulkan/device.rs +++ b/src/gfx_apis/vulkan/device.rs @@ -1,6 +1,7 @@ use { crate::{ allocator::BufferObject, + eventfd_cache::EventfdCache, format::XRGB8888, gfx_apis::vulkan::{ VulkanError, @@ -14,8 +15,8 @@ use { gbm::{GBM_BO_USE_RENDERING, GbmDevice}, }, vulkan_core::{ - ApiVersionDisplay, Extensions, VULKAN_API_VERSION, device::VulkanDeviceInf, - map_extension_properties, + ApiVersionDisplay, Extensions, VULKAN_API_VERSION, VulkanCoreInstance, + device::VulkanDeviceInf, map_extension_properties, }, }, ahash::AHashMap, @@ -64,6 +65,7 @@ pub struct VulkanDevice { pub(super) sync_ctx: Rc, pub(super) instance: Rc, pub(super) device: Arc, + pub(super) eventfd_cache: Rc, pub(super) external_memory_fd: external_memory_fd::Device, pub(super) external_semaphore_fd: external_semaphore_fd::Device, pub(super) external_fence_fd: external_fence_fd::Device, @@ -86,6 +88,7 @@ pub struct VulkanDevice { pub(super) uniform_buffer_descriptor_size: usize, pub(super) lost: Cell, pub(super) fast_ram_access: bool, + pub(super) supports_timeline_opaque_export: bool, } impl Drop for VulkanDevice { @@ -338,6 +341,7 @@ impl VulkanInstance { pub fn create_device( self: &Rc, drm: &Drm, + eventfd_cache: &Rc, mut high_priority: bool, software: bool, ) -> Result, VulkanError> { @@ -380,6 +384,9 @@ impl VulkanInstance { } transfer_granularity_mask = (width_mask, height_mask); } + let features = self.get_features(phy_dev); + let supports_timeline_opaque_export = + self.supports_timeline_opaque_export(phy_dev, &features); if !self.supports_semaphore_import(phy_dev) { return Err(VulkanError::SyncFileImport); } @@ -390,8 +397,8 @@ impl VulkanInstance { if supports_descriptor_buffer { enabled_extensions.push(descriptor_buffer::NAME.as_ptr()); } - let mut semaphore_features = - PhysicalDeviceTimelineSemaphoreFeatures::default().timeline_semaphore(true); + let mut semaphore_features = PhysicalDeviceTimelineSemaphoreFeatures::default() + .timeline_semaphore(supports_timeline_opaque_export); let mut synchronization2_features = PhysicalDeviceSynchronization2Features::default().synchronization2(true); let mut dynamic_rendering_features = @@ -558,6 +565,7 @@ impl VulkanInstance { gbm: Rc::new(gbm), instance: self.clone(), device: Arc::new(device), + eventfd_cache: eventfd_cache.clone(), external_memory_fd, external_semaphore_fd, external_fence_fd, @@ -581,6 +589,7 @@ impl VulkanInstance { uniform_buffer_descriptor_size, lost: Cell::new(false), fast_ram_access, + supports_timeline_opaque_export, })) } } @@ -642,6 +651,10 @@ fn log_device( } impl VulkanDeviceInf for VulkanDevice { + fn instance(&self) -> &VulkanCoreInstance { + &self.instance + } + fn device(&self) -> &Device { &self.device } @@ -649,4 +662,20 @@ impl VulkanDeviceInf for VulkanDevice { fn external_fence_fd(&self) -> &external_fence_fd::Device { &self.external_fence_fd } + + fn external_semaphore_fd(&self) -> &external_semaphore_fd::Device { + &self.external_semaphore_fd + } + + fn supports_timeline_opaque_export(&self) -> bool { + self.supports_timeline_opaque_export + } + + fn sync_ctx(&self) -> &Rc { + &self.sync_ctx + } + + fn eventfd_cache(&self) -> &Rc { + &self.eventfd_cache + } } diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 4841674a..911fa6a8 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -12,7 +12,7 @@ use { GfxFormat, GfxTexture, GfxWriteModifier, ReleaseSync, }, gfx_apis::vulkan::{ - VulkanError, VulkanSync, + VulkanError, VulkanSync, VulkanTimelineSemaphore, allocator::{VulkanAllocator, VulkanThreadedAllocator}, buffer_cache::{GenericBufferWriter, VulkanBuffer, VulkanBufferCache}, command::{VulkanCommandBuffer, VulkanCommandPool}, @@ -39,7 +39,9 @@ use { stack::Stack, }, video::dmabuf::{DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, dma_buf_export_sync_file}, - vulkan_core::sync::VulkanDeviceSyncExt, + vulkan_core::{ + sync::VulkanDeviceSyncExt, timeline_semaphore::VulkanDeviceTimelineSemaphoreExt, + }, }, ahash::AHashMap, arrayvec::ArrayVec, @@ -113,6 +115,8 @@ pub struct VulkanRenderer { pub(super) blend_buffers: RefCell>>, pub(super) shader_buffer_cache: Rc, pub(super) uniform_buffer_cache: Rc, + pub(super) render_tls: Option>, + pub(super) transfer_tls: Option>, } pub(super) struct CachedCommandBuffers { @@ -407,6 +411,8 @@ impl VulkanDevice { blend_buffers: Default::default(), shader_buffer_cache, uniform_buffer_cache, + render_tls: self.create_timeline_semaphore_or_log(), + transfer_tls: self.create_timeline_semaphore_or_log(), }); Ok(render) } @@ -1755,11 +1761,16 @@ impl VulkanRenderer { fn submit(&self, buf: CommandBuffer) -> Result<(), VulkanError> { zone!("submit"); let mut memory = self.memory.borrow_mut(); + let mut semaphore_submit_info = SemaphoreSubmitInfo::default(); let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(buf); - let submit_info = SubmitInfo2::default() + let mut submit_info = SubmitInfo2::default() .wait_semaphore_infos(&memory.wait_semaphore_infos) .command_buffer_infos(slice::from_ref(&command_buffer_info)); - let vulkan_sync = self.device.create_sync()?; + let vulkan_sync = self.device.create_sync( + self.render_tls.as_ref(), + &mut semaphore_submit_info, + &mut submit_info, + )?; unsafe { self.device .device diff --git a/src/gfx_apis/vulkan/shm_image.rs b/src/gfx_apis/vulkan/shm_image.rs index f2694118..06e347d7 100644 --- a/src/gfx_apis/vulkan/shm_image.rs +++ b/src/gfx_apis/vulkan/shm_image.rs @@ -22,7 +22,8 @@ use { CopyImageToBufferInfo2, DependencyInfoKHR, DeviceSize, Extent3D, ImageAspectFlags, ImageCreateInfo, ImageLayout, ImageSubresourceLayers, ImageSubresourceRange, ImageTiling, ImageType, ImageUsageFlags, ImageViewCreateInfo, ImageViewType, Offset3D, - PipelineStageFlags2, QUEUE_FAMILY_FOREIGN_EXT, SampleCountFlags, SharingMode, SubmitInfo2, + PipelineStageFlags2, QUEUE_FAMILY_FOREIGN_EXT, SampleCountFlags, SemaphoreSubmitInfo, + SharingMode, SubmitInfo2, }, gpu_alloc::UsageFlags, isnt::std_1::primitive::IsntSliceExt, @@ -269,11 +270,20 @@ impl VulkanShmImage { }; let dev = &img.renderer.device.device; let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(cmd.buffer); - let submit_info = + let mut semaphore_submit_info = SemaphoreSubmitInfo::default(); + let mut submit_info = SubmitInfo2::default().command_buffer_infos(slice::from_ref(&command_buffer_info)); let begin_info = CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT); - let vulkan_sync = img.renderer.device.create_sync()?; + let tls = match use_transfer_queue { + true => &img.renderer.transfer_tls, + false => &img.renderer.render_tls, + }; + let vulkan_sync = img.renderer.device.create_sync( + tls.as_ref(), + &mut semaphore_submit_info, + &mut submit_info, + )?; unsafe { dev.begin_command_buffer(cmd.buffer, &begin_info) .map_err(VulkanError::BeginCommandBuffer)?; diff --git a/src/gfx_apis/vulkan/transfer.rs b/src/gfx_apis/vulkan/transfer.rs index eefb396e..13d8bd93 100644 --- a/src/gfx_apis/vulkan/transfer.rs +++ b/src/gfx_apis/vulkan/transfer.rs @@ -27,7 +27,7 @@ use { ash::vk::{ AccessFlags2, BufferImageCopy2, CommandBufferBeginInfo, CommandBufferSubmitInfo, CommandBufferUsageFlags, DependencyInfo, Extent3D, ImageAspectFlags, ImageLayout, - ImageSubresourceLayers, Offset3D, PipelineStageFlags2, SubmitInfo2, + ImageSubresourceLayers, Offset3D, PipelineStageFlags2, SemaphoreSubmitInfo, SubmitInfo2, }, std::{ cell::{Cell, RefCell, RefMut}, @@ -380,9 +380,14 @@ impl VulkanShmImage { CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT); let cmd = img.renderer.gfx_command_buffers.allocate()?; let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(cmd.buffer); - let submit_info = + let mut semaphore_submit_info = SemaphoreSubmitInfo::default(); + let mut submit_info = SubmitInfo2::default().command_buffer_infos(slice::from_ref(&command_buffer_info)); - let vulkan_sync = img.renderer.device.create_sync()?; + let vulkan_sync = img.renderer.device.create_sync( + img.renderer.render_tls.as_ref(), + &mut semaphore_submit_info, + &mut submit_info, + )?; unsafe { dev.begin_command_buffer(cmd.buffer, &begin_info) .map_err(VulkanError::BeginCommandBuffer)?; diff --git a/src/it/test_backend.rs b/src/it/test_backend.rs index a14527f9..42748ae0 100644 --- a/src/it/test_backend.rs +++ b/src/it/test_backend.rs @@ -211,7 +211,10 @@ impl TestBackend { }; } let udmabuf = ("udmabuf", constructor!(Udmabuf::new())); - let vulkan = ("Vulkan", constructor!(create_vk_allocator(), nomap)); + let vulkan = ( + "Vulkan", + constructor!(create_vk_allocator(&self.state), nomap), + ); let gbm = ("GBM", constructor!(create_gbm_allocator())); let allocators = match need_drm { true => [vulkan, gbm, udmabuf], @@ -243,9 +246,10 @@ fn create_gbm_allocator() -> Result { create_drm_allocator(|drm| GbmDevice::new(&drm).map_err(TestBackendError::CreateGbmDevice)) } -fn create_vk_allocator() -> Result, TestBackendError> { +fn create_vk_allocator(state: &State) -> Result, TestBackendError> { create_drm_allocator(|drm| { - create_vulkan_allocator(&drm).map_err(TestBackendError::CreateVulkanAllocator) + create_vulkan_allocator(&drm, &state.eventfd_cache) + .map_err(TestBackendError::CreateVulkanAllocator) }) } diff --git a/src/it/test_client.rs b/src/it/test_client.rs index bf6e544e..5252bda4 100644 --- a/src/it/test_client.rs +++ b/src/it/test_client.rs @@ -96,7 +96,12 @@ impl TestClient { pub async fn take_screenshot(&self, include_cursor: bool) -> Result, TestError> { let (dmabuf, dev) = self.jc.take_screenshot(include_cursor).await?; - let qoi = buf_to_bytes(dev.as_ref(), &dmabuf, ScreenshotFormat::Qoi)?; + let qoi = buf_to_bytes( + &self.run.state.eventfd_cache, + dev.as_ref(), + &dmabuf, + ScreenshotFormat::Qoi, + )?; Ok(qoi) } diff --git a/src/portal.rs b/src/portal.rs index 6ba3ceae..b2b9f62e 100644 --- a/src/portal.rs +++ b/src/portal.rs @@ -327,7 +327,6 @@ async fn init_dbus_session(dbus: &Dbus, logger: Arc, path_sink: OwnedFd) struct PortalState { xrd: String, ring: Rc, - #[expect(dead_code)] eventfd_cache: Rc, eng: Rc, wheel: Rc, diff --git a/src/portal/ptl_display.rs b/src/portal/ptl_display.rs index 2c59eeb4..a0223a40 100644 --- a/src/portal/ptl_display.rs +++ b/src/portal/ptl_display.rs @@ -187,6 +187,7 @@ impl UsrJayRenderCtxOwner for PortalDisplay { let ctx = match create_gfx_context( &self.state.eng, &self.state.ring, + &self.state.eventfd_cache, &drm, GfxApi::OpenGl, None, diff --git a/src/state.rs b/src/state.rs index aa57a3f9..49c703e9 100644 --- a/src/state.rs +++ b/src/state.rs @@ -289,7 +289,6 @@ pub struct State { pub gfx_ctx_changed: EventSource, pub copy_device_registry: Rc, pub supports_presentation_feedback: Cell, - #[expect(dead_code)] pub eventfd_cache: Rc, } @@ -558,6 +557,7 @@ impl State { create_gfx_context( &self.eng, &self.ring, + &self.eventfd_cache, drm, api.unwrap_or(self.default_gfx_api.get()), self.caps_thread.as_ref(), diff --git a/src/video/drm.rs b/src/video/drm.rs index 8ff83a59..f70b5497 100644 --- a/src/video/drm.rs +++ b/src/video/drm.rs @@ -1,21 +1,33 @@ pub mod syncobj; mod sys; pub mod wait_for_syncobj; -pub use consts::*; use { crate::{ - utils::oserror::OsError, - video::drm::sys::{ - DRM_DISPLAY_MODE_LEN, DRM_MODE_ATOMIC_TEST_ONLY, DRM_MODE_FB_MODIFIERS, - DRM_MODE_OBJECT_BLOB, DRM_MODE_OBJECT_CONNECTOR, DRM_MODE_OBJECT_CRTC, - DRM_MODE_OBJECT_ENCODER, DRM_MODE_OBJECT_FB, DRM_MODE_OBJECT_PLANE, - DRM_MODE_OBJECT_PROPERTY, create_lease, drm_event, drm_event_vblank, gem_close, - get_cap, get_device_name_from_fd2, get_minor_name_from_fd, get_node_type_from_fd, - get_nodes, mode_addfb2, mode_atomic, mode_create_blob, mode_destroy_blob, - mode_get_resources, mode_getconnector, mode_getencoder, mode_getplane, - mode_getplaneresources, mode_getprobblob, mode_getproperty, mode_obj_getproperties, - mode_rmfb, prime_fd_to_handle, set_client_cap, + backend, + format::Format, + io_uring::{IoUring, IoUringError}, + utils::{ + buf::Buf, errorfmt::ErrorFmt, oserror::OsError, stack::Stack, syncqueue::SyncQueue, + vec_ext::VecExt, + }, + video::{ + INVALID_MODIFIER, Modifier, + dmabuf::DmaBuf, + drm::sys::{ + DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, DRM_CAP_CURSOR_HEIGHT, DRM_CAP_CURSOR_WIDTH, + DRM_DISPLAY_MODE_LEN, DRM_MODE_ATOMIC_TEST_ONLY, DRM_MODE_FB_MODIFIERS, + DRM_MODE_OBJECT_BLOB, DRM_MODE_OBJECT_CONNECTOR, DRM_MODE_OBJECT_CRTC, + DRM_MODE_OBJECT_ENCODER, DRM_MODE_OBJECT_FB, DRM_MODE_OBJECT_PLANE, + DRM_MODE_OBJECT_PROPERTY, FORMAT_BLOB_CURRENT, auth_magic, create_lease, drm_event, + drm_event_crtc_sequence, drm_event_vblank, drm_format_modifier, + drm_format_modifier_blob, drop_master, gem_close, get_cap, + get_device_name_from_fd2, get_minor_name_from_fd, get_node_type_from_fd, get_nodes, + get_version, mode_addfb2, mode_atomic, mode_create_blob, mode_destroy_blob, + mode_get_resources, mode_getconnector, mode_getencoder, mode_getplane, + mode_getplaneresources, mode_getprobblob, mode_getproperty, mode_obj_getproperties, + mode_rmfb, prime_fd_to_handle, queue_sequence, revoke_lease, set_client_cap, + }, }, }, ahash::AHashMap, @@ -32,26 +44,14 @@ use { thiserror::Error, uapi::{OwnedFd, Pod, Ustring, c}, }; - -use crate::{ - backend, - format::Format, - io_uring::{IoUring, IoUringError}, - utils::{buf::Buf, errorfmt::ErrorFmt, stack::Stack, syncqueue::SyncQueue, vec_ext::VecExt}, - video::{ - INVALID_MODIFIER, Modifier, - dmabuf::DmaBuf, - drm::sys::{ - DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, DRM_CAP_CURSOR_HEIGHT, DRM_CAP_CURSOR_WIDTH, - FORMAT_BLOB_CURRENT, auth_magic, drm_event_crtc_sequence, drm_format_modifier, - drm_format_modifier_blob, drop_master, get_version, queue_sequence, revoke_lease, - }, +pub use { + consts::*, + sys::{ + DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET, DRM_MODE_ATOMIC_NONBLOCK, + DRM_MODE_PAGE_FLIP_ASYNC, DRM_MODE_PAGE_FLIP_EVENT, drm_mode_modeinfo, + get_drm_nodes_from_dev, }, }; -pub use sys::{ - DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET, DRM_MODE_ATOMIC_NONBLOCK, - DRM_MODE_PAGE_FLIP_ASYNC, DRM_MODE_PAGE_FLIP_EVENT, drm_mode_modeinfo, -}; #[derive(Debug, Error)] pub enum DrmError { @@ -123,6 +123,8 @@ pub enum DrmError { CreateSyncobj(#[source] OsError), #[error("Could not export a syncobj")] ExportSyncobj(#[source] OsError), + #[error("Could not query a syncobj")] + QuerySyncobj(#[source] OsError), #[error("Could not register an eventfd with a syncobj")] RegisterEventfd(#[source] OsError), #[error("Could not create an eventfd")] diff --git a/src/video/drm/syncobj.rs b/src/video/drm/syncobj.rs index 4450721f..160528fa 100644 --- a/src/video/drm/syncobj.rs +++ b/src/video/drm/syncobj.rs @@ -18,7 +18,7 @@ use { DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_TIMELINE, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, sync_ioc_merge, syncobj_create, syncobj_destroy, syncobj_eventfd, syncobj_fd_to_handle, syncobj_handle_to_fd, - syncobj_signal, syncobj_transfer, + syncobj_query, syncobj_signal, syncobj_transfer, }, }, }, @@ -329,6 +329,11 @@ impl SyncobjCtx { } } } + + pub fn query_last_signaled(&self, syncobj: &Syncobj) -> Result { + let handle = self.get_handle(syncobj)?; + syncobj_query(self.inner.drm.raw(), handle.0).map_err(DrmError::QuerySyncobj) + } } impl Drop for SyncobjCtx { diff --git a/src/video/drm/sys.rs b/src/video/drm/sys.rs index ba61b919..e0a55ccf 100644 --- a/src/video/drm/sys.rs +++ b/src/video/drm/sys.rs @@ -171,7 +171,10 @@ pub fn get_device_name_from_fd2(fd: c::c_int) -> Result { pub fn get_nodes(fd: c::c_int) -> Result, OsError> { let (_, maj, min) = drm_stat(fd)?; + get_drm_nodes_from_dev(maj, min) +} +pub fn get_drm_nodes_from_dev(maj: u64, min: u64) -> Result, OsError> { let dir = device_dir(maj, min); let mut dir = uapi::opendir(dir)?; @@ -1334,6 +1337,22 @@ pub fn syncobj_signal(drm: c::c_int, handle: u32, point: u64) -> Result<(), OsEr Ok(()) } +const DRM_IOCTL_SYNCOBJ_QUERY: u64 = drm_iowr::(0xCB); + +pub fn syncobj_query(drm: c::c_int, handle: u32) -> Result { + let mut point = 0u64; + let mut res = drm_syncobj_timeline_array { + handles: &handle as *const u32 as u64, + points: &mut point as *mut u64 as u64, + count_handles: 1, + flags: 0, + }; + unsafe { + ioctl(drm, DRM_IOCTL_SYNCOBJ_QUERY, &mut res)?; + } + Ok(point) +} + #[repr(C)] struct drm_syncobj_transfer { src_handle: u32, diff --git a/src/vulkan_core.rs b/src/vulkan_core.rs index b4c4264b..397a5356 100644 --- a/src/vulkan_core.rs +++ b/src/vulkan_core.rs @@ -1,4 +1,5 @@ use { + crate::{eventfd_cache::EventfdError, video::drm::DrmError}, ahash::{AHashMap, AHashSet}, ash::{ Entry, Instance, LoadingError, @@ -6,9 +7,13 @@ use { vk::{ self, API_VERSION_1_3, ApplicationInfo, Bool32, DebugUtilsMessageSeverityFlagsEXT, DebugUtilsMessageTypeFlagsEXT, DebugUtilsMessengerCallbackDataEXT, - DebugUtilsMessengerCreateInfoEXT, DebugUtilsMessengerEXT, ExtensionProperties, FALSE, - InstanceCreateInfo, LayerProperties, ValidationFeaturesEXT, api_version_major, - api_version_minor, api_version_patch, api_version_variant, + DebugUtilsMessengerCreateInfoEXT, DebugUtilsMessengerEXT, ExtensionProperties, + ExternalSemaphoreFeatureFlags, ExternalSemaphoreHandleTypeFlags, + ExternalSemaphoreProperties, FALSE, InstanceCreateInfo, LayerProperties, + PhysicalDevice, PhysicalDeviceExternalSemaphoreInfo, PhysicalDeviceFeatures, + PhysicalDeviceFeatures2, PhysicalDeviceTimelineSemaphoreFeatures, SemaphoreType, + SemaphoreTypeCreateInfo, ValidationFeaturesEXT, api_version_major, api_version_minor, + api_version_patch, api_version_variant, }, }, isnt::std_1::collections::IsntHashMapExt, @@ -28,6 +33,7 @@ use { pub mod device; pub mod fence; pub mod sync; +pub mod timeline_semaphore; static VULKAN_ENTRY: Lazy>> = Lazy::new(|| unsafe { Entry::load() }.map_err(Arc::new)); @@ -53,6 +59,22 @@ pub enum VulkanCoreError { CreateFence(#[source] vk::Result), #[error("Could not export a sync file from a semaphore")] ExportSyncFile(#[source] vk::Result), + #[error("Could not create a semaphore")] + CreateSemaphore(#[source] vk::Result), + #[error("Device does not support timeline semaphore export")] + TimelineExportNotSupported, + #[error("Could not export an opaque fd from a semaphore")] + ExportTimelineSemaphore(#[source] vk::Result), + #[error("Could not signal the timeline semaphore")] + SignalSemaphore(#[source] vk::Result), + #[error("Could not query last signaled sync obj point")] + QueryLastSignaled(#[source] DrmError), + #[error("Mapping between syncobj points and timeline semaphore points is unexpected")] + UnsupportedPointMapping, + #[error("Could not acquire an eventfd")] + AcquireEventfd(#[source] EventfdError), + #[error("Could not create a sync obj eventfd wait")] + CreateSyncobjWait(#[source] DrmError), } pub struct VulkanCoreInstance { @@ -61,6 +83,13 @@ pub struct VulkanCoreInstance { debug_utils: debug_utils::Instance, messenger: DebugUtilsMessengerEXT, pub log_level: Level, + pub validation: bool, +} + +pub struct VulkanDeviceFeatures { + #[expect(dead_code)] + pub features: PhysicalDeviceFeatures, + pub semaphore_features: PhysicalDeviceTimelineSemaphoreFeatures<'static>, } impl VulkanCoreInstance { @@ -86,7 +115,8 @@ impl VulkanCoreInstance { let mut severity = DebugUtilsMessageSeverityFlagsEXT::empty() | DebugUtilsMessageSeverityFlagsEXT::ERROR | DebugUtilsMessageSeverityFlagsEXT::WARNING; - if *VULKAN_VALIDATION { + let validation = *VULKAN_VALIDATION; + if validation { severity |= DebugUtilsMessageSeverityFlagsEXT::INFO | DebugUtilsMessageSeverityFlagsEXT::VERBOSE; } @@ -110,7 +140,7 @@ impl VulkanCoreInstance { .application_info(&app_info) .push_next(&mut debug_info); let validation_layer_name = VALIDATION_LAYER.as_ptr(); - if *VULKAN_VALIDATION { + if validation { if get_available_layers(entry)?.contains(VALIDATION_LAYER) { create_info = create_info.enabled_layer_names(slice::from_ref(&validation_layer_name)); @@ -146,8 +176,49 @@ impl VulkanCoreInstance { debug_utils, messenger, log_level, + validation, }) } + + pub fn get_features(&self, phy_dev: PhysicalDevice) -> VulkanDeviceFeatures { + let mut semaphore_features = PhysicalDeviceTimelineSemaphoreFeatures::default(); + let mut features = PhysicalDeviceFeatures2::default().push_next(&mut semaphore_features); + unsafe { + self.instance + .get_physical_device_features2(phy_dev, &mut features); + } + VulkanDeviceFeatures { + features: features.features, + semaphore_features, + } + } + + pub fn supports_timeline_opaque_export( + &self, + phy_dev: PhysicalDevice, + features: &VulkanDeviceFeatures, + ) -> bool { + if features.semaphore_features.timeline_semaphore == vk::TRUE { + return self.supports_semaphore_opaque_export(phy_dev); + } + false + } + + fn supports_semaphore_opaque_export(&self, phy_dev: PhysicalDevice) -> bool { + let mut props = ExternalSemaphoreProperties::default(); + let mut type_info = + SemaphoreTypeCreateInfo::default().semaphore_type(SemaphoreType::TIMELINE); + let info = PhysicalDeviceExternalSemaphoreInfo::default() + .handle_type(ExternalSemaphoreHandleTypeFlags::OPAQUE_FD) + .push_next(&mut type_info); + unsafe { + self.instance + .get_physical_device_external_semaphore_properties(phy_dev, &info, &mut props); + } + props + .external_semaphore_features + .contains(ExternalSemaphoreFeatureFlags::EXPORTABLE) + } } impl Drop for VulkanCoreInstance { diff --git a/src/vulkan_core/device.rs b/src/vulkan_core/device.rs index 69fed856..ab4ceb1a 100644 --- a/src/vulkan_core/device.rs +++ b/src/vulkan_core/device.rs @@ -1,6 +1,21 @@ -use ash::{Device, khr::external_fence_fd}; +use { + crate::{ + eventfd_cache::EventfdCache, video::drm::syncobj::SyncobjCtx, + vulkan_core::VulkanCoreInstance, + }, + ash::{ + Device, + khr::{external_fence_fd, external_semaphore_fd}, + }, + std::rc::Rc, +}; pub trait VulkanDeviceInf: Sized { + fn instance(&self) -> &VulkanCoreInstance; fn device(&self) -> &Device; fn external_fence_fd(&self) -> &external_fence_fd::Device; + fn external_semaphore_fd(&self) -> &external_semaphore_fd::Device; + fn supports_timeline_opaque_export(&self) -> bool; + fn sync_ctx(&self) -> &Rc; + fn eventfd_cache(&self) -> &Rc; } diff --git a/src/vulkan_core/sync.rs b/src/vulkan_core/sync.rs index eca2df83..aaa7e317 100644 --- a/src/vulkan_core/sync.rs +++ b/src/vulkan_core/sync.rs @@ -1,15 +1,17 @@ use { crate::{ - gfx_api::FdSync, + gfx_api::{FdSync, ReservedSyncobjPoint}, utils::errorfmt::ErrorFmt, + video::drm::syncobj::SyncobjPoint, vulkan_core::{ VulkanCoreError, device::VulkanDeviceInf, fence::{VulkanDeviceFenceExt, VulkanFence}, + timeline_semaphore::VulkanTimelineSemaphore, }, }, - ash::vk::Fence, - std::rc::Rc, + ash::vk::{Fence, PipelineStageFlags2, SemaphoreSubmitInfo, SemaphoreWaitInfo, SubmitInfo2}, + std::{rc::Rc, slice}, }; pub enum VulkanSync @@ -17,6 +19,10 @@ where D: VulkanDeviceInf, { Fence(Rc>), + TimelineSemaphore { + tls: Rc>, + pending: Rc, + }, } impl VulkanSync @@ -24,12 +30,22 @@ where D: VulkanDeviceInf, { pub fn handle_validation(&self) { - // nothing + if let VulkanSync::TimelineSemaphore { tls, pending } = self + && tls.device.instance().validation + { + let info = SemaphoreWaitInfo::default() + .semaphores(slice::from_ref(&tls.semaphore)) + .values(slice::from_ref(&pending.point.0)); + unsafe { + let _ = tls.device.device().wait_semaphores(&info, 0); + } + } } pub fn fence(&self) -> Fence { match self { VulkanSync::Fence(f) => f.fence, + VulkanSync::TimelineSemaphore { .. } => Fence::null(), } } @@ -47,19 +63,74 @@ where }; release_sync_file.map(FdSync::SyncFile) } + VulkanSync::TimelineSemaphore { pending, .. } => Some(FdSync::Syncobj(pending.clone())), } } } pub trait VulkanDeviceSyncExt: VulkanDeviceInf { - fn create_sync(self: &Rc) -> Result, VulkanCoreError>; + fn create_sync<'a>( + self: &Rc, + tls: Option<&Rc>>, + semaphore_submit_info: &'a mut SemaphoreSubmitInfo, + submit_info: &mut SubmitInfo2<'a>, + ) -> Result, VulkanCoreError>; } impl VulkanDeviceSyncExt for D where D: VulkanDeviceInf, { - fn create_sync(self: &Rc) -> Result, VulkanCoreError> { + fn create_sync<'a>( + self: &Rc, + tls: Option<&Rc>>, + semaphore_submit_info: &'a mut SemaphoreSubmitInfo, + submit_info: &mut SubmitInfo2<'a>, + ) -> Result, VulkanCoreError> { + if let Some(tls) = tls { + match create_tls_sync(self, tls, semaphore_submit_info, submit_info) { + Ok(s) => return Ok(s), + Err(e) => { + log::warn!("Could not create sync obj sync: {}", ErrorFmt(e)); + } + } + } self.create_fence().map(VulkanSync::Fence) } } + +fn create_tls_sync<'a, D>( + device: &Rc, + tls: &Rc>, + semaphore_submit_info: &'a mut SemaphoreSubmitInfo, + submit_info: &mut SubmitInfo2<'a>, +) -> Result, VulkanCoreError> +where + D: VulkanDeviceInf, +{ + let point = SyncobjPoint(tls.next_point.fetch_add(1)); + let eventfd = device + .eventfd_cache() + .acquire() + .map_err(VulkanCoreError::AcquireEventfd)?; + device + .sync_ctx() + .wait_for_point(&eventfd.fd, &tls.syncobj, point, true) + .map_err(VulkanCoreError::CreateSyncobjWait)?; + let pending = Rc::new(ReservedSyncobjPoint { + ctx: device.sync_ctx().clone(), + syncobj: tls.syncobj.clone(), + point, + sync_file: Default::default(), + signaled: eventfd, + }); + *semaphore_submit_info = SemaphoreSubmitInfo::default() + .semaphore(tls.semaphore) + .value(point.0) + .stage_mask(PipelineStageFlags2::ALL_COMMANDS); + *submit_info = submit_info.signal_semaphore_infos(slice::from_ref(semaphore_submit_info)); + Ok(VulkanSync::TimelineSemaphore { + tls: tls.clone(), + pending, + }) +} diff --git a/src/vulkan_core/timeline_semaphore.rs b/src/vulkan_core/timeline_semaphore.rs new file mode 100644 index 00000000..7f46c689 --- /dev/null +++ b/src/vulkan_core/timeline_semaphore.rs @@ -0,0 +1,116 @@ +use { + crate::{ + utils::{errorfmt::ErrorFmt, numcell::NumCell}, + video::drm::syncobj::Syncobj, + vulkan_core::{VulkanCoreError, device::VulkanDeviceInf}, + }, + ash::vk::{ + ExportSemaphoreCreateInfo, ExternalSemaphoreHandleTypeFlags, Semaphore, + SemaphoreCreateInfo, SemaphoreGetFdInfoKHR, SemaphoreSignalInfo, SemaphoreType, + SemaphoreTypeCreateInfo, + }, + run_on_drop::on_drop, + std::rc::Rc, + uapi::OwnedFd, +}; + +pub struct VulkanTimelineSemaphore +where + D: VulkanDeviceInf, +{ + pub(super) device: Rc, + pub(super) semaphore: Semaphore, + pub(super) syncobj: Rc, + pub(super) next_point: NumCell, +} + +impl Drop for VulkanTimelineSemaphore +where + D: VulkanDeviceInf, +{ + fn drop(&mut self) { + unsafe { + self.device.device().destroy_semaphore(self.semaphore, None); + } + } +} + +pub trait VulkanDeviceTimelineSemaphoreExt: VulkanDeviceInf { + fn create_timeline_semaphore( + self: &Rc, + ) -> Result>, VulkanCoreError>; + + fn create_timeline_semaphore_or_log( + self: &Rc, + ) -> Option>>; +} + +impl VulkanDeviceTimelineSemaphoreExt for D +where + D: VulkanDeviceInf, +{ + fn create_timeline_semaphore( + self: &Rc, + ) -> Result>, VulkanCoreError> { + if !self.supports_timeline_opaque_export() { + return Err(VulkanCoreError::TimelineExportNotSupported); + } + let sem = { + let mut export_info = ExportSemaphoreCreateInfo::default() + .handle_types(ExternalSemaphoreHandleTypeFlags::OPAQUE_FD); + let mut type_info = + SemaphoreTypeCreateInfo::default().semaphore_type(SemaphoreType::TIMELINE); + let info = SemaphoreCreateInfo::default() + .push_next(&mut export_info) + .push_next(&mut type_info); + let sem = unsafe { self.device().create_semaphore(&info, None) }; + sem.map_err(VulkanCoreError::CreateSemaphore)? + }; + let destroy_sem = on_drop(|| unsafe { self.device().destroy_semaphore(sem, None) }); + let syncobj = { + let info = SemaphoreGetFdInfoKHR::default() + .semaphore(sem) + .handle_type(ExternalSemaphoreHandleTypeFlags::OPAQUE_FD); + let res = unsafe { self.external_semaphore_fd().get_semaphore_fd(&info) }; + let fd = res.map_err(VulkanCoreError::ExportTimelineSemaphore)?; + Syncobj::new(&Rc::new(OwnedFd::new(fd))) + }; + let signal = |p| { + let info = SemaphoreSignalInfo::default().semaphore(sem).value(p); + unsafe { + self.device() + .signal_semaphore(&info) + .map_err(VulkanCoreError::SignalSemaphore) + } + }; + let next_point = NumCell::new(1); + for _ in 0..2 { + let n = next_point.fetch_add(1); + signal(n)?; + let signaled = self + .sync_ctx() + .query_last_signaled(&syncobj) + .map_err(VulkanCoreError::QueryLastSignaled)?; + if signaled != n { + return Err(VulkanCoreError::UnsupportedPointMapping); + } + } + destroy_sem.forget(); + Ok(Rc::new(VulkanTimelineSemaphore { + device: self.clone(), + semaphore: sem, + syncobj: Rc::new(syncobj), + next_point, + })) + } + + fn create_timeline_semaphore_or_log( + self: &Rc, + ) -> Option>> { + self.create_timeline_semaphore() + .inspect_err(|e| { + log::warn!("Could not create timeline semaphore: {}", ErrorFmt(e)); + }) + .ok() + } +}