1
0
Fork 0
forked from wry/wry

Merge pull request #769 from mahkoh/jorth/fence

vulkan: use sync objects if possible
This commit is contained in:
mahkoh 2026-03-02 18:52:53 +01:00 committed by GitHub
commit 8e51c71224
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 1314 additions and 434 deletions

View file

@ -9,7 +9,7 @@ use {
drm_feedback::DrmFeedback, drm_feedback::DrmFeedback,
fixed::Fixed, fixed::Fixed,
format::Format, format::Format,
gfx_api::{GfxApi, GfxFramebuffer, SyncFile}, gfx_api::{FdSync, GfxApi, GfxFramebuffer},
ifs::{ ifs::{
wl_output::OutputId, wl_output::OutputId,
wl_seat::{ wl_seat::{
@ -186,7 +186,7 @@ pub trait HardwareCursorUpdate {
fn set_enabled(&mut self, enabled: bool); fn set_enabled(&mut self, enabled: bool);
fn get_buffer(&self) -> Rc<dyn GfxFramebuffer>; fn get_buffer(&self) -> Rc<dyn GfxFramebuffer>;
fn set_position(&mut self, x: i32, y: i32); fn set_position(&mut self, x: i32, y: i32);
fn swap_buffer(&mut self, sync_file: Option<SyncFile>); fn swap_buffer(&mut self, sync: Option<FdSync>);
fn size(&self) -> (i32, i32); fn size(&self) -> (i32, i32);
} }

View file

@ -11,8 +11,8 @@ use {
}, },
format::Format, format::Format,
gfx_api::{ gfx_api::{
AcquireSync, GfxBlendBuffer, GfxError, GfxFormat, GfxFramebuffer, GfxTexture, AcquireSync, FdSync, GfxBlendBuffer, GfxError, GfxFormat, GfxFramebuffer, GfxTexture,
GfxWriteModifier, ReleaseSync, SyncFile, needs_render_usage, GfxWriteModifier, ReleaseSync, needs_render_usage,
}, },
rect::{DamageQueue, Rect, Region}, rect::{DamageQueue, Rect, Region},
udmabuf::{Udmabuf, UdmabufError}, udmabuf::{Udmabuf, UdmabufError},
@ -120,15 +120,15 @@ pub enum RenderBufferError {
#[derive(Default)] #[derive(Default)]
pub struct RenderBufferCopy { pub struct RenderBufferCopy {
pub render_block: Option<SyncFile>, pub render_block: Option<FdSync>,
pub present_block: Option<SyncFile>, pub present_block: Option<FdSync>,
} }
impl RenderBufferCopy { impl RenderBufferCopy {
pub fn for_both(sf: Option<SyncFile>) -> Self { pub fn for_both(sync: Option<FdSync>) -> Self {
Self { Self {
render_block: sf.clone(), render_block: sync.clone(),
present_block: sf, present_block: sync,
} }
} }
} }
@ -138,12 +138,12 @@ impl RenderBuffer {
&self, &self,
cd: &Rc<ColorDescription>, cd: &Rc<ColorDescription>,
region: Option<&Region>, region: Option<&Region>,
sync_file: Option<SyncFile>, sync: Option<FdSync>,
) -> Result<RenderBufferCopy, RenderBufferError> { ) -> Result<RenderBufferCopy, RenderBufferError> {
match &self.prime { match &self.prime {
RenderBufferPrime::None => Ok(RenderBufferCopy { RenderBufferPrime::None => Ok(RenderBufferCopy {
render_block: None, render_block: None,
present_block: sync_file, present_block: sync,
}), }),
RenderBufferPrime::Sampling { RenderBufferPrime::Sampling {
dev_render_tex, dev_render_tex,
@ -157,7 +157,7 @@ impl RenderBuffer {
dev_render_tex, dev_render_tex,
cd, cd,
None, None,
AcquireSync::from_sync_file(sync_file), AcquireSync::from_fd_sync(sync),
ReleaseSync::None, ReleaseSync::None,
0, 0,
0, 0,
@ -175,7 +175,7 @@ impl RenderBuffer {
.. ..
} => { } => {
let render_block = render_copy let render_block = render_copy
.execute(sync_file.as_ref(), region) .execute(sync.as_ref(), region)
.map_err(RenderBufferError::CopyRenderToUdmabuf)?; .map_err(RenderBufferError::CopyRenderToUdmabuf)?;
let present_block = dev_copy let present_block = dev_copy
.execute(render_block.as_ref(), region) .execute(render_block.as_ref(), region)
@ -189,7 +189,7 @@ impl RenderBuffer {
| RenderBufferPrime::CopyDirectPush { | RenderBufferPrime::CopyDirectPush {
render_copy: copy, .. render_copy: copy, ..
} => copy } => copy
.execute(sync_file.as_ref(), region) .execute(sync.as_ref(), region)
.map_err(RenderBufferError::CopyRenderToDev) .map_err(RenderBufferError::CopyRenderToDev)
.map(RenderBufferCopy::for_both), .map(RenderBufferCopy::for_both),
} }
@ -201,8 +201,8 @@ impl RenderBuffer {
self.damage_queue.damage(&[rect]); self.damage_queue.damage(&[rect]);
} }
pub fn clear(&self, cd: &Rc<ColorDescription>) -> Result<Option<SyncFile>, RenderBufferError> { pub fn clear(&self, cd: &Rc<ColorDescription>) -> Result<Option<FdSync>, RenderBufferError> {
let sync_file = match &self.prime { let sync = match &self.prime {
RenderBufferPrime::None => { RenderBufferPrime::None => {
self.render self.render
.fb .fb
@ -222,14 +222,14 @@ impl RenderBuffer {
self.copy_to_dev(cd, None, sf)?.present_block self.copy_to_dev(cd, None, sf)?.present_block
} }
}; };
Ok(sync_file) Ok(sync)
} }
pub fn copy_to_new( pub fn copy_to_new(
&self, &self,
new: &Self, new: &Self,
cd: &Rc<ColorDescription>, cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, RenderBufferError> { ) -> Result<Option<FdSync>, RenderBufferError> {
let old = self; let old = self;
if (old.width, old.height) != (new.width, new.height) { if (old.width, old.height) != (new.width, new.height) {

View file

@ -240,13 +240,8 @@ impl MetalConnector {
// current PresentFb if present_fb is None, potentially mutating the fb that is // current PresentFb if present_fb is None, potentially mutating the fb that is
// currently being scanned out, which would render such a wait absurd. // currently being scanned out, which would render such a wait absurd.
self.perform_screencopies(&present_fb, &node, &cd); self.perform_screencopies(&present_fb, &node, &cd);
if let Some(sync_file) = self.cursor_sync_file.take() if let Some(sync) = self.cursor_sync.take() {
&& let Err(e) = self.state.ring.readable(&sync_file).await sync.signaled(&self.state.ring, "cursor").await;
{
log::error!(
"Could not wait for cursor sync file to complete: {}",
ErrorFmt(e)
);
} }
self.await_present_fb(present_fb.as_mut(), PresentFbWait::Scanout) self.await_present_fb(present_fb.as_mut(), PresentFbWait::Scanout)
.await; .await;
@ -353,19 +348,14 @@ impl MetalConnector {
W::Render => &mut fb.copy.render_block, W::Render => &mut fb.copy.render_block,
W::Scanout => &mut fb.copy.present_block, W::Scanout => &mut fb.copy.present_block,
}; };
let Some(sync_file) = field.take() else { let Some(sync) = field.take() else {
return; return;
}; };
if let Err(e) = self.state.ring.readable(&sync_file).await { let name = match wait {
let name = match wait { W::Render => "render",
W::Render => "render", W::Scanout => "scanout",
W::Scanout => "scanout", };
}; sync.signaled(&self.state.ring, name).await;
log::error!(
"Could not wait for primary {name} sync file to complete: {}",
ErrorFmt(e),
);
}
} }
fn try_async_flip(&self) -> bool { fn try_async_flip(&self) -> bool {
@ -554,13 +544,13 @@ impl MetalConnector {
self.state.present_hardware_cursor(node, &mut c); self.state.present_hardware_cursor(node, &mut c);
let swap_buffers = c.cursor_swap_buffer.is_some(); let swap_buffers = c.cursor_swap_buffer.is_some();
self.cursor_swap_buffer.set(swap_buffers); self.cursor_swap_buffer.set(swap_buffers);
if let Some(sf) = c.cursor_swap_buffer.take() { if let Some(sync) = c.cursor_swap_buffer.take() {
let sf = c let sync = c
.cursor_buffer .cursor_buffer
.copy_to_dev(cd, None, sf) .copy_to_dev(cd, None, sync)
.map_err(MetalError::CopyToDev)? .map_err(MetalError::CopyToDev)?
.present_block; .present_block;
self.cursor_sync_file.set(sf); self.cursor_sync.set(sync);
} }
let mut cursor_changed = false; let mut cursor_changed = false;
cursor_changed |= self.cursor_enabled.replace(c.cursor_enabled) != c.cursor_enabled; cursor_changed |= self.cursor_enabled.replace(c.cursor_enabled) != c.cursor_enabled;
@ -886,13 +876,13 @@ impl MetalConnector {
tex = buffer.render.tex.clone(); tex = buffer.render.tex.clone();
} }
Some(dsd) => { Some(dsd) => {
let sf = match &dsd.acquire_sync { let sync = match &dsd.acquire_sync {
AcquireSync::None => None, AcquireSync::None => None,
AcquireSync::Implicit => None, AcquireSync::Implicit => None,
AcquireSync::SyncFile { sync_file } => Some(sync_file.clone()), AcquireSync::FdSync(sync) => Some(sync.clone()),
AcquireSync::Unnecessary => None, AcquireSync::Unnecessary => None,
}; };
copy = RenderBufferCopy::for_both(sf); copy = RenderBufferCopy::for_both(sync);
fb = dsd.fb.clone(); fb = dsd.fb.clone();
tex = dsd.tex.clone(); tex = dsd.tex.clone();
} }

View file

@ -242,7 +242,7 @@ impl MetalDeviceTransaction {
let mut unused_crtcs = BinarySearchMap::<_, _, SIZE>::new(); let mut unused_crtcs = BinarySearchMap::<_, _, SIZE>::new();
let mut unused_planes = BinarySearchMap::<_, _, SIZE>::new(); let mut unused_planes = BinarySearchMap::<_, _, SIZE>::new();
let mut crtc_planes = BinarySearchMap::<_, _, SIZE>::new(); let mut crtc_planes = BinarySearchMap::<_, _, SIZE>::new();
let mut sync_files = vec![]; let mut syncs = vec![];
let slf = &mut self.common; let slf = &mut self.common;
for (_, crtc) in &mut slf.crtcs { for (_, crtc) in &mut slf.crtcs {
crtc_planes.insert(crtc.obj.id, CrtcPlanes::default()); crtc_planes.insert(crtc.obj.id, CrtcPlanes::default());
@ -615,7 +615,7 @@ impl MetalDeviceTransaction {
new_buffer.clear(&cd) new_buffer.clear(&cd)
}; };
match res { match res {
Ok(sf) => sync_files.extend(sf), Ok(sf) => syncs.extend(sf),
Err(e) => { Err(e) => {
log::warn!("Could not clear new buffer: {}", ErrorFmt(e)); log::warn!("Could not clear new buffer: {}", ErrorFmt(e));
} }
@ -653,7 +653,7 @@ impl MetalDeviceTransaction {
match res { match res {
Ok(sf) => { Ok(sf) => {
buffer.locked.set(true); buffer.locked.set(true);
sync_files.extend(sf); syncs.extend(sf);
} }
Err(e) => { Err(e) => {
log::error!( log::error!(
@ -753,19 +753,8 @@ impl MetalDeviceTransaction {
plane.new = DrmPlaneState::default(); plane.new = DrmPlaneState::default();
} }
} }
for sf in sync_files { for sync in syncs {
let mut pollfd = c::pollfd { sync.signaled_blocking("transaction");
fd: sf.0.raw(),
events: c::POLLIN,
revents: 0,
};
let res = uapi::poll(slice::from_mut(&mut pollfd), -1);
if let Err(e) = res {
log::warn!(
"Could not wait for sync file to become readable: {}",
ErrorFmt(e)
);
}
} }
Ok(MetalDeviceTransactionWithDrmState { Ok(MetalDeviceTransactionWithDrmState {
common: self.common, common: self.common,

View file

@ -26,7 +26,7 @@ use {
drm_feedback::DrmFeedback, drm_feedback::DrmFeedback,
edid::{CtaDataBlock, Descriptor, EdidExtension}, edid::{CtaDataBlock, Descriptor, EdidExtension},
format::{Format, XRGB8888}, format::{Format, XRGB8888},
gfx_api::{GfxApi, GfxContext, GfxFramebuffer, SyncFile}, gfx_api::{FdSync, GfxApi, GfxContext, GfxFramebuffer},
ifs::{ ifs::{
wl_output::OutputId, wl_output::OutputId,
wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC, KIND_ZERO_COPY}, wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC, KIND_ZERO_COPY},
@ -534,7 +534,7 @@ pub struct MetalConnector {
pub cursor_enabled: Cell<bool>, pub cursor_enabled: Cell<bool>,
pub cursor_buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>, pub cursor_buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
pub cursor_swap_buffer: Cell<bool>, pub cursor_swap_buffer: Cell<bool>,
pub cursor_sync_file: CloneCell<Option<SyncFile>>, pub cursor_sync: CloneCell<Option<FdSync>>,
pub drm_feedback: CloneCell<Option<Rc<DrmFeedback>>>, pub drm_feedback: CloneCell<Option<Rc<DrmFeedback>>>,
pub scanout_buffers: RefCell<AHashMap<DmaBufId, DirectScanoutCache>>, pub scanout_buffers: RefCell<AHashMap<DmaBufId, DirectScanoutCache>>,
@ -565,7 +565,7 @@ pub struct MetalHardwareCursor {
} }
pub struct MetalHardwareCursorChange<'a> { pub struct MetalHardwareCursorChange<'a> {
pub cursor_swap_buffer: Option<Option<SyncFile>>, pub cursor_swap_buffer: Option<Option<FdSync>>,
pub cursor_enabled: bool, pub cursor_enabled: bool,
pub cursor_x: i32, pub cursor_x: i32,
pub cursor_y: i32, pub cursor_y: i32,
@ -603,8 +603,8 @@ impl HardwareCursorUpdate for MetalHardwareCursorChange<'_> {
self.cursor_y = y; self.cursor_y = y;
} }
fn swap_buffer(&mut self, sync_file: Option<SyncFile>) { fn swap_buffer(&mut self, sync: Option<FdSync>) {
self.cursor_swap_buffer = Some(sync_file); self.cursor_swap_buffer = Some(sync);
} }
fn size(&self) -> (i32, i32) { fn size(&self) -> (i32, i32) {
@ -1127,7 +1127,7 @@ fn create_connector(
cursor_changed: Cell::new(false), cursor_changed: Cell::new(false),
cursor_damage: Cell::new(false), cursor_damage: Cell::new(false),
cursor_swap_buffer: Cell::new(false), cursor_swap_buffer: Cell::new(false),
cursor_sync_file: Default::default(), cursor_sync: Default::default(),
drm_feedback: Default::default(), drm_feedback: Default::default(),
scanout_buffers: Default::default(), scanout_buffers: Default::default(),
active_framebuffer: Default::default(), active_framebuffer: Default::default(),

View file

@ -2,6 +2,7 @@ use {
crate::{ crate::{
allocator::{Allocator, AllocatorError, BufferUsage, MappedBuffer}, allocator::{Allocator, AllocatorError, BufferUsage, MappedBuffer},
cli::{GlobalArgs, ScreenshotArgs, ScreenshotFormat}, cli::{GlobalArgs, ScreenshotArgs, ScreenshotFormat},
eventfd_cache::EventfdCache,
format::XRGB8888, format::XRGB8888,
gfx_apis, gfx_apis,
tools::tool_client::{Handle, ToolClient, with_tool_client}, tools::tool_client::{Handle, ToolClient, with_tool_client},
@ -109,7 +110,8 @@ async fn run(screenshot: Rc<Screenshot>) {
} }
}; };
let format = screenshot.args.format; 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, Ok(d) => d,
Err(e) => fatal!("{}", ErrorFmt(e)), Err(e) => fatal!("{}", ErrorFmt(e)),
}; };
@ -159,6 +161,7 @@ fn map(
} }
pub fn buf_to_bytes( pub fn buf_to_bytes(
eventfd_cache: &Rc<EventfdCache>,
drm_dev: Option<&Rc<OwnedFd>>, drm_dev: Option<&Rc<OwnedFd>>,
buf: &DmaBuf, buf: &DmaBuf,
format: ScreenshotFormat, format: ScreenshotFormat,
@ -174,7 +177,7 @@ pub fn buf_to_bytes(
.map_err(ScreenshotError::CreateGbmDevice) .map_err(ScreenshotError::CreateGbmDevice)
}); });
let vulkan = Box::new(move || { let vulkan = Box::new(move || {
gfx_apis::create_vulkan_allocator(&drm()?) gfx_apis::create_vulkan_allocator(&drm()?, eventfd_cache)
.map_err(ScreenshotError::CreateVulkanAllocator) .map_err(ScreenshotError::CreateVulkanAllocator)
}); });
allocators.push(vulkan); allocators.push(vulkan);

View file

@ -26,6 +26,7 @@ use {
damage::{DamageVisualizer, visualize_damage}, damage::{DamageVisualizer, visualize_damage},
dbus::Dbus, dbus::Dbus,
ei::ei_client::EiClients, ei::ei_client::EiClients,
eventfd_cache::EventfdCache,
forker, forker,
format::XRGB8888, format::XRGB8888,
gfx_api::GfxApi, gfx_api::GfxApi,
@ -213,6 +214,7 @@ fn start_compositor2(
let cpu_worker = Rc::new(CpuWorker::new(&ring, &engine)?); let cpu_worker = Rc::new(CpuWorker::new(&ring, &engine)?);
let color_manager = ColorManager::new(); let color_manager = ColorManager::new();
let crit_ids = Rc::new(CritMatcherIds::default()); let crit_ids = Rc::new(CritMatcherIds::default());
let eventfd_cache = EventfdCache::new(&ring, &engine);
let state = Rc::new(State { let state = Rc::new(State {
kb_ctx, kb_ctx,
backend: CloneCell::new(Rc::new(DummyBackend)), backend: CloneCell::new(Rc::new(DummyBackend)),
@ -372,8 +374,9 @@ fn start_compositor2(
outputs_without_hc: Default::default(), outputs_without_hc: Default::default(),
udmabuf: Default::default(), udmabuf: Default::default(),
gfx_ctx_changed: 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(), supports_presentation_feedback: Default::default(),
eventfd_cache,
}); });
state.tracker.register(ClientId::from_raw(0)); state.tracker.register(ClientId::from_raw(0));
create_dummy_output(&state); create_dummy_output(&state);

View file

@ -1,20 +1,24 @@
use { use {
crate::{ crate::{
async_engine::{AsyncEngine, SpawnedFuture}, async_engine::{AsyncEngine, SpawnedFuture},
eventfd_cache::EventfdCache,
format::{FORMATS, Format}, format::{FORMATS, Format},
gfx_api::SyncFile, gfx_api::FdSync,
io_uring::IoUring, io_uring::IoUring,
rect::{Rect, Region}, rect::{Rect, Region},
utils::{ utils::{
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, queue::AsyncQueue, clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell,
stack::Stack, oserror::OsError, queue::AsyncQueue, stack::Stack,
}, },
video::{ video::{
LINEAR_MODIFIER, LINEAR_STRIDE_ALIGN, Modifier, LINEAR_MODIFIER, LINEAR_STRIDE_ALIGN, Modifier,
dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec}, dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec},
drm::{NodeType, get_drm_nodes_from_dev, syncobj::SyncobjCtx},
}, },
vulkan_core::{ vulkan_core::{
VULKAN_API_VERSION, VulkanCoreError, VulkanCoreInstance, map_extension_properties, self, VULKAN_API_VERSION, VulkanCoreError, VulkanCoreInstance, device::VulkanDeviceInf,
map_extension_properties, sync::VulkanDeviceSyncExt,
timeline_semaphore::VulkanDeviceTimelineSemaphoreExt,
}, },
}, },
ahash::{AHashMap, AHashSet}, ahash::{AHashMap, AHashSet},
@ -34,14 +38,13 @@ use {
CommandPoolCreateInfo, CopyBufferInfo2, CopyBufferToImageInfo2, CopyImageInfo2, CommandPoolCreateInfo, CopyBufferInfo2, CopyBufferToImageInfo2, CopyImageInfo2,
CopyImageToBufferInfo2, DependencyInfo, DeviceCreateInfo, DeviceMemory, CopyImageToBufferInfo2, DependencyInfo, DeviceCreateInfo, DeviceMemory,
DeviceQueueCreateInfo, DrmFormatModifierPropertiesEXT, DeviceQueueCreateInfo, DrmFormatModifierPropertiesEXT,
DrmFormatModifierPropertiesListEXT, ExportFenceCreateInfo, ExportMemoryAllocateInfo, DrmFormatModifierPropertiesListEXT, ExportMemoryAllocateInfo, Extent3D,
Extent3D, ExternalBufferProperties, ExternalFenceFeatureFlags, ExternalBufferProperties, ExternalFenceFeatureFlags, ExternalFenceHandleTypeFlags,
ExternalFenceHandleTypeFlags, ExternalFenceProperties, ExternalFenceProperties, ExternalImageFormatPropertiesKHR,
ExternalImageFormatPropertiesKHR, ExternalMemoryBufferCreateInfo, ExternalMemoryBufferCreateInfo, ExternalMemoryBufferCreateInfoKHR,
ExternalMemoryBufferCreateInfoKHR, ExternalMemoryFeatureFlags, ExternalMemoryFeatureFlags, ExternalMemoryHandleTypeFlags,
ExternalMemoryHandleTypeFlags, ExternalMemoryImageCreateInfo, ExternalMemoryImageCreateInfo, ExternalSemaphoreFeatureFlags,
ExternalSemaphoreFeatureFlags, ExternalSemaphoreHandleTypeFlags, ExternalSemaphoreHandleTypeFlags, ExternalSemaphoreProperties, Filter,
ExternalSemaphoreProperties, Fence, FenceCreateInfo, FenceGetFdInfoKHR, Filter,
FormatFeatureFlags, FormatProperties2, ImageAspectFlags, ImageBlit2, ImageCopy2, FormatFeatureFlags, FormatProperties2, ImageAspectFlags, ImageBlit2, ImageCopy2,
ImageCreateFlags, ImageCreateInfo, ImageDrmFormatModifierExplicitCreateInfoEXT, ImageCreateFlags, ImageCreateInfo, ImageDrmFormatModifierExplicitCreateInfoEXT,
ImageFormatProperties2, ImageLayout, ImageMemoryBarrier2, ImageMemoryRequirementsInfo2, ImageFormatProperties2, ImageLayout, ImageMemoryBarrier2, ImageMemoryRequirementsInfo2,
@ -54,9 +57,10 @@ use {
PhysicalDeviceExternalImageFormatInfoKHR, PhysicalDeviceExternalSemaphoreInfo, PhysicalDeviceExternalImageFormatInfoKHR, PhysicalDeviceExternalSemaphoreInfo,
PhysicalDeviceFeatures2, PhysicalDeviceImageDrmFormatModifierInfoEXT, PhysicalDeviceFeatures2, PhysicalDeviceImageDrmFormatModifierInfoEXT,
PhysicalDeviceImageFormatInfo2, PhysicalDeviceProperties2, PhysicalDeviceImageFormatInfo2, PhysicalDeviceProperties2,
PhysicalDeviceSynchronization2Features, PipelineStageFlags2, QUEUE_FAMILY_FOREIGN_EXT, PhysicalDeviceSynchronization2Features, PhysicalDeviceTimelineSemaphoreFeatures,
Queue, QueueFlags, SampleCountFlags, SemaphoreCreateInfo, SemaphoreImportFlags, PipelineStageFlags2, QUEUE_FAMILY_FOREIGN_EXT, Queue, QueueFlags, SampleCountFlags,
SemaphoreSubmitInfo, SharingMode, SubmitInfo2, SubresourceLayout, WHOLE_SIZE, SemaphoreCreateInfo, SemaphoreImportFlags, SemaphoreSubmitInfo, SharingMode,
SubmitInfo2, SubresourceLayout, WHOLE_SIZE,
}, },
}, },
bstr::ByteSlice, bstr::ByteSlice,
@ -84,16 +88,12 @@ pub enum CopyDeviceError {
Core(#[from] VulkanCoreError), Core(#[from] VulkanCoreError),
#[error("Could not create a semaphore")] #[error("Could not create a semaphore")]
CreateSemaphore(#[source] vk::Result), CreateSemaphore(#[source] vk::Result),
#[error("Could not create a fence")]
CreateFence(#[source] vk::Result),
#[error("Could not dup a sync file")] #[error("Could not dup a sync file")]
DupSyncFile(#[source] io::Error), DupSyncFile(#[source] io::Error),
#[error("Could not dup a dma buf")] #[error("Could not dup a dma buf")]
DupDmaBuf(#[source] io::Error), DupDmaBuf(#[source] io::Error),
#[error("Could not import a sync file")] #[error("Could not import a sync file")]
ImportSyncFile(#[source] vk::Result), ImportSyncFile(#[source] vk::Result),
#[error("Could not export a sync file")]
ExportSyncFile(#[source] vk::Result),
#[error("Could not submit the copy")] #[error("Could not submit the copy")]
SubmitCopy(#[source] vk::Result), SubmitCopy(#[source] vk::Result),
#[error("Could not enumerate the physical devices")] #[error("Could not enumerate the physical devices")]
@ -166,6 +166,12 @@ pub enum CopyDeviceError {
BothOffDevice, BothOffDevice,
#[error("Cannot blit between these formats")] #[error("Cannot blit between these formats")]
BlitNotSupported, 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<T> = StaticMap<TransferType, T>; type Keyed<T> = StaticMap<TransferType, T>;
@ -174,12 +180,15 @@ type KeyedCopy<T> = StaticCopyMap<TransferType, T>;
pub struct PhysicalCopyDevice { pub struct PhysicalCopyDevice {
ring: Rc<IoUring>, ring: Rc<IoUring>,
eng: Rc<AsyncEngine>, eng: Rc<AsyncEngine>,
eventfd_cache: Rc<EventfdCache>,
sync_ctx: Rc<SyncobjCtx>,
instance: VulkanCoreInstance, instance: VulkanCoreInstance,
physical_device: PhysicalDevice, physical_device: PhysicalDevice,
support: AHashMap<u32, StaticMap<Dir, Vec<CopyDeviceSupport>>>, support: AHashMap<u32, StaticMap<Dir, Vec<CopyDeviceSupport>>>,
queues_to_allocate: Vec<QueueToAllocate>, queues_to_allocate: Vec<QueueToAllocate>,
queues: KeyedCopy<QueueIndex>, queues: KeyedCopy<QueueIndex>,
supports_dmabuf_export: bool, supports_dmabuf_export: bool,
supports_timeline_opaque_export: bool,
memory_types: Vec<MemoryType>, memory_types: Vec<MemoryType>,
rects: RefCell<Vec<(i32, i32, u32, u32)>>, rects: RefCell<Vec<(i32, i32, u32, u32)>>,
buffer_copy_2: RefCell<Vec<BufferCopy2<'static>>>, buffer_copy_2: RefCell<Vec<BufferCopy2<'static>>>,
@ -205,6 +214,7 @@ struct QueueIndex {
pub struct CopyDevice { pub struct CopyDevice {
_tasks: Vec<SpawnedFuture<()>>, _tasks: Vec<SpawnedFuture<()>>,
dev: Rc<CopyDeviceInner>, dev: Rc<CopyDeviceInner>,
timeline_semaphore: Option<Rc<VulkanTimelineSemaphore>>,
} }
struct CopyDeviceInner { struct CopyDeviceInner {
@ -217,7 +227,7 @@ struct CopyDeviceInner {
external_fence_fd: external_fence_fd::Device, external_fence_fd: external_fence_fd::Device,
external_memory_fd: external_memory_fd::Device, external_memory_fd: external_memory_fd::Device,
semaphores: Stack<VulkanSemaphore>, semaphores: Stack<VulkanSemaphore>,
fences: Stack<VulkanFence>, fences: Stack<Rc<VulkanFence>>,
submissions: Keyed<Rc<PendingSubmissions>>, submissions: Keyed<Rc<PendingSubmissions>>,
} }
@ -229,12 +239,13 @@ struct PendingSubmissions {
pub struct CopyDeviceCopy { pub struct CopyDeviceCopy {
inner: Rc<CopyDeviceCopyInner>, inner: Rc<CopyDeviceCopyInner>,
_dev: Rc<CopyDevice>, dev: Rc<CopyDevice>,
} }
struct CopyDeviceCopyInner { struct CopyDeviceCopyInner {
dev: Rc<CopyDeviceInner>, dev: Rc<CopyDeviceInner>,
busy: CloneCell<Option<SyncFile>>, busy_id: NumCell<u64>,
busy: CloneCell<Option<FdSync>>,
width: u32, width: u32,
height: u32, height: u32,
command_buffer: CommandBuffer, command_buffer: CommandBuffer,
@ -273,10 +284,11 @@ enum CopyDeviceCopyType {
struct Pending { struct Pending {
dev: Rc<CopyDeviceInner>, dev: Rc<CopyDeviceInner>,
sync_file: Option<SyncFile>, busy_id: u64,
sync: Option<FdSync>,
copy: Rc<CopyDeviceCopyInner>, copy: Rc<CopyDeviceCopyInner>,
semaphore: Option<VulkanSemaphore>, semaphore: Option<VulkanSemaphore>,
fence: Option<VulkanFence>, vulkan_sync: VulkanSync,
} }
struct VulkanSemaphore { struct VulkanSemaphore {
@ -284,10 +296,10 @@ struct VulkanSemaphore {
semaphore: Semaphore, semaphore: Semaphore,
} }
struct VulkanFence { type VulkanFence = vulkan_core::fence::VulkanFence<CopyDeviceInner>;
dev: Rc<CopyDeviceInner>, type VulkanTimelineSemaphore =
fence: Fence, vulkan_core::timeline_semaphore::VulkanTimelineSemaphore<CopyDeviceInner>;
} type VulkanSync = vulkan_core::sync::VulkanSync<CopyDeviceInner>;
struct VulkanBuffer { struct VulkanBuffer {
dev: Rc<CopyDeviceInner>, dev: Rc<CopyDeviceInner>,
@ -340,6 +352,7 @@ struct ClassifiedDmabuf<'a> {
pub struct CopyDeviceRegistry { pub struct CopyDeviceRegistry {
ring: Rc<IoUring>, ring: Rc<IoUring>,
eng: Rc<AsyncEngine>, eng: Rc<AsyncEngine>,
eventfd_cache: Rc<EventfdCache>,
devs: CopyHashMap<c::dev_t, Option<Rc<PhysicalCopyDevice>>>, devs: CopyHashMap<c::dev_t, Option<Rc<PhysicalCopyDevice>>>,
} }
@ -356,6 +369,7 @@ impl PhysicalCopyDevice {
fn new( fn new(
ring: &Rc<IoUring>, ring: &Rc<IoUring>,
eng: &Rc<AsyncEngine>, eng: &Rc<AsyncEngine>,
eventfd_cache: &Rc<EventfdCache>,
dev: c::dev_t, dev: c::dev_t,
) -> Result<Rc<Self>, CopyDeviceError> { ) -> Result<Rc<Self>, CopyDeviceError> {
let core_instance = VulkanCoreInstance::new(Level::Debug)?; let core_instance = VulkanCoreInstance::new(Level::Debug)?;
@ -408,6 +422,16 @@ impl PhysicalCopyDevice {
} }
return Err(CopyDeviceError::NoVulkanDevice); 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 { if device_properties.api_version < VULKAN_API_VERSION {
return Err(CopyDeviceError::NoVulkan13); return Err(CopyDeviceError::NoVulkan13);
} }
@ -610,15 +634,21 @@ impl PhysicalCopyDevice {
} }
let memory_info = let memory_info =
unsafe { instance.get_physical_device_memory_properties(physical_device) }; 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 { let dev = Rc::new(PhysicalCopyDevice {
ring: ring.clone(), ring: ring.clone(),
eng: eng.clone(), eng: eng.clone(),
eventfd_cache: eventfd_cache.clone(),
sync_ctx: Rc::new(SyncobjCtx::new(&device_fd)),
instance: core_instance, instance: core_instance,
physical_device, physical_device,
support, support,
queues_to_allocate, queues_to_allocate,
queues: queue_indices, queues: queue_indices,
supports_dmabuf_export, supports_dmabuf_export,
supports_timeline_opaque_export,
memory_types: memory_info.memory_types_as_slice().to_vec(), memory_types: memory_info.memory_types_as_slice().to_vec(),
rects: Default::default(), rects: Default::default(),
buffer_copy_2: Default::default(), buffer_copy_2: Default::default(),
@ -658,11 +688,14 @@ impl PhysicalCopyDevice {
}) })
.collect(); .collect();
let extensions = DEVICE_EXTENSIONS.map(|e| e.as_ptr()); 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 = let mut synchronization2_features =
PhysicalDeviceSynchronization2Features::default().synchronization2(true); PhysicalDeviceSynchronization2Features::default().synchronization2(true);
let info = DeviceCreateInfo::default() let info = DeviceCreateInfo::default()
.queue_create_infos(&queue_create_info) .queue_create_infos(&queue_create_info)
.enabled_extension_names(&extensions) .enabled_extension_names(&extensions)
.push_next(&mut semaphore_features)
.push_next(&mut synchronization2_features); .push_next(&mut synchronization2_features);
unsafe { unsafe {
instance instance
@ -726,7 +759,11 @@ impl PhysicalCopyDevice {
let task = self.eng.spawn("copy-device-await-pending", future); let task = self.eng.spawn("copy-device-await-pending", future);
tasks.push(task); 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) Ok(queue)
} }
} }
@ -740,8 +777,8 @@ async fn wait_for_submissions(
submissions.task_has_pending.set(false); submissions.task_has_pending.set(false);
let pending = submissions.pending.pop().await; let pending = submissions.pending.pop().await;
submissions.task_has_pending.set(true); submissions.task_has_pending.set(true);
if let Some(sync_file) = &pending.sync_file if let Some(sync) = &pending.sync
&& let Err(e) = ring.readable(sync_file).await && let Err(e) = sync.try_signaled(&ring).await
{ {
log::warn!( log::warn!(
"Could not wait for sync file to become readable: {}", "Could not wait for sync file to become readable: {}",
@ -749,6 +786,7 @@ async fn wait_for_submissions(
); );
dev.wait_idle(); dev.wait_idle();
} }
pending.vulkan_sync.handle_validation();
} }
} }
@ -1124,6 +1162,7 @@ impl CopyDevice {
Ok(CopyDeviceCopy { Ok(CopyDeviceCopy {
inner: Rc::new(CopyDeviceCopyInner { inner: Rc::new(CopyDeviceCopyInner {
dev: self.dev.clone(), dev: self.dev.clone(),
busy_id: Default::default(),
busy: Default::default(), busy: Default::default(),
width: src.width as _, width: src.width as _,
height: src.height as _, height: src.height as _,
@ -1131,7 +1170,7 @@ impl CopyDevice {
tt, tt,
ty, ty,
}), }),
_dev: self.clone(), dev: self.clone(),
}) })
} }
@ -1258,36 +1297,14 @@ impl CopyDeviceInner {
semaphore, semaphore,
}) })
} }
fn create_fence(self: &Rc<Self>) -> Result<VulkanFence, CopyDeviceError> {
let mut export_info =
ExportFenceCreateInfo::default().handle_types(ExternalFenceHandleTypeFlags::SYNC_FD);
let create_info = FenceCreateInfo::default().push_next(&mut export_info);
let fence = unsafe {
self.dev
.create_fence(&create_info, None)
.map_err(CopyDeviceError::CreateFence)?
};
Ok(VulkanFence {
dev: self.clone(),
fence,
})
}
} }
impl CopyDeviceCopy { impl CopyDeviceCopy {
fn ensure_not_busy(&self) -> Result<(), CopyDeviceError> { fn ensure_not_busy(&self) -> Result<(), CopyDeviceError> {
let slf = &*self.inner; let slf = &*self.inner;
let Some(busy) = slf.busy.get() else { if let Some(sync) = slf.busy.get()
return Ok(()); && sync.is_unsignaled()
}; {
let mut pollfd = c::pollfd {
fd: busy.raw(),
events: c::POLLIN,
revents: 0,
};
let res = uapi::poll(slice::from_mut(&mut pollfd), 0);
if res != Ok(1) {
return Err(CopyDeviceError::Busy); return Err(CopyDeviceError::Busy);
} }
slf.busy.take(); slf.busy.take();
@ -1296,9 +1313,9 @@ impl CopyDeviceCopy {
pub fn execute( pub fn execute(
&self, &self,
sync_file: Option<&SyncFile>, sync: Option<&FdSync>,
region: Option<&Region>, region: Option<&Region>,
) -> Result<Option<SyncFile>, CopyDeviceError> { ) -> Result<Option<FdSync>, CopyDeviceError> {
self.ensure_not_busy()?; self.ensure_not_busy()?;
let slf = &*self.inner; let slf = &*self.inner;
let tt = slf.tt; let tt = slf.tt;
@ -1656,7 +1673,9 @@ impl CopyDeviceCopy {
} }
let mut wait_semaphore = None; let mut wait_semaphore = None;
let mut wait_semaphores = ArrayVec::<_, 1>::new(); let mut wait_semaphores = ArrayVec::<_, 1>::new();
if let Some(sync_file) = sync_file { if let Some(sync) = sync
&& let Some(sync_file) = sync.get_sync_file()
{
let semaphore = match slf.dev.semaphores.pop() { let semaphore = match slf.dev.semaphores.pop() {
Some(s) => s, Some(s) => s,
_ => slf.dev.create_semaphore()?, _ => slf.dev.create_semaphore()?,
@ -1668,42 +1687,38 @@ impl CopyDeviceCopy {
wait_semaphores.push(info); wait_semaphores.push(info);
wait_semaphore = Some(semaphore); wait_semaphore = Some(semaphore);
} }
let signal_fence = match slf.dev.fences.pop() {
Some(s) => s,
_ => slf.dev.create_fence()?,
};
let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(cmd); 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)) .command_buffer_infos(slice::from_ref(&command_buffer_info))
.wait_semaphore_infos(&wait_semaphores); .wait_semaphore_infos(&wait_semaphores);
let vulkan_sync = slf.dev.create_sync(
self.dev.timeline_semaphore.as_ref(),
&mut semaphore_submit_info,
&mut submit_info,
)?;
unsafe { unsafe {
slf.dev slf.dev
.dev .dev
.queue_submit2( .queue_submit2(
slf.dev.queues[tt], slf.dev.queues[tt],
slice::from_ref(&submit_info), slice::from_ref(&submit_info),
signal_fence.fence, vulkan_sync.fence(),
) )
.map_err(CopyDeviceError::SubmitCopy)?; .map_err(CopyDeviceError::SubmitCopy)?;
} }
let sync_file = match signal_fence.export() { let sync = vulkan_sync.to_sync(|| slf.dev.wait_idle());
Ok(f) => f, slf.busy.set(sync.clone());
Err(e) => {
log::error!("Could not export signal fence: {}", ErrorFmt(e));
slf.dev.wait_idle();
None
}
};
slf.busy.set(sync_file.clone());
let pending = Pending { let pending = Pending {
dev: slf.dev.clone(), dev: slf.dev.clone(),
sync_file: sync_file.clone(), busy_id: slf.busy_id.add_fetch(1),
sync: sync.clone(),
copy: self.inner.clone(), copy: self.inner.clone(),
semaphore: wait_semaphore, semaphore: wait_semaphore,
fence: Some(signal_fence), vulkan_sync,
}; };
slf.dev.submissions[tt].pending.push(pending); slf.dev.submissions[tt].pending.push(pending);
Ok(sync_file) Ok(sync)
} }
} }
@ -1728,31 +1743,16 @@ impl VulkanSemaphore {
} }
} }
impl VulkanFence {
fn export(&self) -> Result<Option<SyncFile>, CopyDeviceError> {
let info = FenceGetFdInfoKHR::default()
.fence(self.fence)
.handle_type(ExternalFenceHandleTypeFlags::SYNC_FD);
let fd = unsafe {
self.dev
.external_fence_fd
.get_fence_fd(&info)
.map_err(CopyDeviceError::ExportSyncFile)?
};
let fd = if fd == -1 {
None
} else {
Some(SyncFile(Rc::new(OwnedFd::new(fd))))
};
Ok(fd)
}
}
impl CopyDeviceRegistry { impl CopyDeviceRegistry {
pub fn new(ring: &Rc<IoUring>, eng: &Rc<AsyncEngine>) -> Self { pub fn new(
ring: &Rc<IoUring>,
eng: &Rc<AsyncEngine>,
eventfd_cache: &Rc<EventfdCache>,
) -> Self {
Self { Self {
ring: ring.clone(), ring: ring.clone(),
eng: eng.clone(), eng: eng.clone(),
eventfd_cache: eventfd_cache.clone(),
devs: Default::default(), devs: Default::default(),
} }
} }
@ -1765,7 +1765,7 @@ impl CopyDeviceRegistry {
if let Some(dev) = self.devs.get(&dev) { if let Some(dev) = self.devs.get(&dev) {
return 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) => { Ok(cd) => {
self.devs.set(dev, cd.clone()); self.devs.set(dev, cd.clone());
cd cd
@ -1792,14 +1792,6 @@ impl Drop for VulkanSemaphore {
} }
} }
impl Drop for VulkanFence {
fn drop(&mut self) {
unsafe {
self.dev.dev.destroy_fence(self.fence, None);
}
}
}
impl Drop for CopyDeviceCopyInner { impl Drop for CopyDeviceCopyInner {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
@ -1842,10 +1834,7 @@ impl Drop for Pending {
if let Some(v) = self.semaphore.take() { if let Some(v) = self.semaphore.take() {
self.dev.semaphores.push(v); self.dev.semaphores.push(v);
} }
if let Some(v) = self.fence.take() { if self.copy.busy_id.get() == self.busy_id {
self.dev.fences.push(v);
}
if self.copy.busy.get() == self.sync_file {
self.copy.busy.take(); self.copy.busy.take();
} }
} }
@ -2037,3 +2026,33 @@ fn allocate_queues(
}; };
(queues_to_allocate, queue_indices) (queues_to_allocate, queue_indices)
} }
impl VulkanDeviceInf for CopyDeviceInner {
fn instance(&self) -> &VulkanCoreInstance {
&self.phy.instance
}
fn device(&self) -> &Device {
&self.dev
}
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<SyncobjCtx> {
&self.phy.sync_ctx
}
fn eventfd_cache(&self) -> &Rc<EventfdCache> {
&self.phy.eventfd_cache
}
}

View file

@ -514,8 +514,8 @@ impl CursorUser {
&cd, &cd,
); );
match res { match res {
Ok(sync_file) => { Ok(sync) => {
hc.swap_buffer(sync_file); hc.swap_buffer(sync);
} }
Err(e) => { Err(e) => {
log::error!("Could not render hardware cursor: {}", ErrorFmt(e)); log::error!("Could not render hardware cursor: {}", ErrorFmt(e));

154
src/eventfd_cache.rs Normal file
View file

@ -0,0 +1,154 @@
use {
crate::{
async_engine::{AsyncEngine, SpawnedFuture},
io_uring::{IoUring, IoUringError},
utils::{buf::Buf, errorfmt::ErrorFmt, oserror::OsError, queue::AsyncQueue, stack::Stack},
},
std::{cell::Cell, future::poll_fn, pin::Pin, rc::Rc, slice, task::Poll},
thiserror::Error,
uapi::{OwnedFd, c},
};
#[cfg(test)]
mod tests;
#[derive(Debug, Error)]
pub enum EventfdError {
#[error("Could not create an eventfd")]
CreateEventfd(#[source] OsError),
}
pub struct EventfdCache {
inner: Rc<Inner>,
_task: SpawnedFuture<()>,
}
struct Inner {
ring: Rc<IoUring>,
fds: Stack<Rc<OwnedFd>>,
recycle: AsyncQueue<Rc<OwnedFd>>,
}
pub struct Eventfd {
cache: Rc<Inner>,
pub fd: Rc<OwnedFd>,
signaled: Cell<bool>,
}
impl EventfdCache {
pub fn new(ring: &Rc<IoUring>, eng: &Rc<AsyncEngine>) -> Rc<Self> {
let inner = Rc::new(Inner {
ring: ring.clone(),
fds: Default::default(),
recycle: Default::default(),
});
let task = eng.spawn("eventfd-cache", inner.clone().recycle());
Rc::new(Self { inner, _task: task })
}
pub fn acquire(&self) -> Result<Eventfd, EventfdError> {
let fd = match self.inner.fds.pop() {
Some(fd) => fd,
_ => uapi::eventfd(0, c::EFD_CLOEXEC)
.map(Rc::new)
.map_err(Into::into)
.map_err(EventfdError::CreateEventfd)?,
};
Ok(Eventfd {
cache: self.inner.clone(),
fd,
signaled: Default::default(),
})
}
}
impl Eventfd {
pub fn is_signaled(&self) -> bool {
self.signaled.get()
}
pub async fn signaled(&self) -> Result<(), IoUringError> {
if self.signaled.get() {
return Ok(());
}
self.cache.ring.readable(&self.fd).await?;
self.signaled.set(true);
Ok(())
}
pub fn signaled_blocking(&self) -> Result<(), OsError> {
if self.signaled.get() {
return Ok(());
}
let mut pollfd = c::pollfd {
fd: self.fd.raw(),
events: c::POLLIN,
revents: 0,
};
uapi::poll(slice::from_mut(&mut pollfd), -1)?;
self.signaled.set(true);
Ok(())
}
}
impl Inner {
async fn recycle(self: Rc<Self>) {
let slf = &*self;
let mut fds = vec![];
let mut bufs = vec![];
let mut tasks = vec![];
let mut todo = vec![];
loop {
fds.clear();
tasks.clear();
todo.clear();
slf.recycle.non_empty().await;
while let Some(fd) = slf.recycle.try_pop() {
fds.push(fd);
}
for (idx, fd) in fds.iter().enumerate() {
if idx >= bufs.len() {
bufs.push(Buf::new(size_of::<u64>()));
}
let fd = fd.clone();
let buf = bufs[idx].clone();
tasks.push(async move { slf.ring.read(&fd, buf).await });
todo.push(idx);
}
poll_fn(|ctx| {
let mut i = 0;
while i < todo.len() {
let idx = todo[i];
let task = unsafe { Pin::new_unchecked(&mut tasks[idx]) };
if let Poll::Ready(res) = task.poll(ctx) {
todo.swap_remove(i);
match res {
Ok(_) => {
self.fds.push(fds[idx].clone());
}
Err(e) => {
log::error!("Could not read from eventfd: {}", ErrorFmt(e));
}
}
} else {
i += 1;
}
}
if todo.is_empty() {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
}
}
}
impl Drop for Eventfd {
fn drop(&mut self) {
if self.signaled.get() {
self.cache.recycle.push(self.fd.clone());
}
}
}

View file

@ -0,0 +1,66 @@
use {
crate::{
async_engine::AsyncEngine, eventfd_cache::EventfdCache, io_uring::IoUring, utils::array,
},
std::{rc::Rc, slice},
uapi::c,
};
#[test]
fn test() {
let eng = AsyncEngine::new();
let ring = IoUring::new(&eng, 32).unwrap();
let cache = Rc::new(EventfdCache::new(&ring, &eng));
const TOTAL: usize = 5;
let signaled = 3;
let fd1: [_; TOTAL] = array::from_fn(|_| cache.acquire().unwrap());
let fd2: [_; TOTAL] = array::from_fn(|_| cache.acquire().unwrap());
for fd in fd1.iter().chain(fd2.iter()) {
uapi::eventfd_write(fd.fd.raw(), 1).unwrap();
let mut poll = c::pollfd {
fd: fd.fd.raw(),
events: c::POLLIN,
revents: 0,
};
uapi::poll(slice::from_mut(&mut poll), 0).unwrap();
assert_eq!(poll.revents, c::POLLIN);
}
assert_eq!(cache.inner.fds.len(), 0);
let ring2 = ring.clone();
let cache2 = cache.clone();
let _fut1 = eng.spawn("", async move {
for i in 0..signaled {
fd1[i].signaled().await.unwrap();
}
drop(fd1);
let debouncer = ring2.debouncer(0);
while cache2.inner.fds.len() != signaled {
debouncer.debounce().await;
}
for i in 0..signaled {
fd2[i].signaled().await.unwrap();
}
drop(fd2);
while cache2.inner.fds.len() != 2 * signaled {
debouncer.debounce().await;
}
ring2.stop();
});
let now_nsec = eng.now().nsec();
let ring2 = ring.clone();
let _fut2 = eng.spawn("", async move {
ring2.timeout(now_nsec + 1_000_000_000).await.unwrap();
ring2.stop();
});
ring.run().unwrap();
assert_eq!(cache.inner.fds.len(), 2 * signaled);
for fd in cache.inner.fds.take() {
let mut poll = c::pollfd {
fd: fd.raw(),
events: c::POLLIN,
revents: 0,
};
uapi::poll(slice::from_mut(&mut poll), 0).unwrap();
assert_eq!(poll.revents, 0);
}
}

View file

@ -5,16 +5,22 @@ use {
cpu_worker::CpuWorker, cpu_worker::CpuWorker,
cursor::Cursor, cursor::Cursor,
damage::DamageVisualizer, damage::DamageVisualizer,
eventfd_cache::Eventfd,
fixed::Fixed, fixed::Fixed,
format::Format, format::Format,
io_uring::{IoUring, IoUringError},
rect::{Rect, Region}, rect::{Rect, Region},
renderer::{Renderer, renderer_base::RendererBase}, renderer::{Renderer, renderer_base::RendererBase},
scale::Scale, scale::Scale,
state::State, state::State,
theme::Color, theme::Color,
tree::{Node, OutputNode, Transform}, tree::{Node, OutputNode, Transform},
utils::clonecell::UnsafeCellCloneSafe, utils::{clonecell::UnsafeCellCloneSafe, errorfmt::ErrorFmt},
video::{Modifier, dmabuf::DmaBuf, drm::syncobj::SyncobjCtx}, video::{
Modifier,
dmabuf::DmaBuf,
drm::syncobj::{Syncobj, SyncobjCtx, SyncobjPoint},
},
}, },
ahash::AHashMap, ahash::AHashMap,
indexmap::{IndexMap, IndexSet}, indexmap::{IndexMap, IndexSet},
@ -22,16 +28,17 @@ use {
linearize::Linearize, linearize::Linearize,
std::{ std::{
any::Any, any::Any,
cell::Cell, cell::{Cell, OnceCell},
error::Error, error::Error,
ffi::CString, ffi::CString,
fmt::{Debug, Formatter}, fmt::{Debug, Formatter},
ops::Deref, ops::Deref,
rc::Rc, rc::Rc,
slice,
sync::atomic::{AtomicU64, Ordering::Relaxed}, sync::atomic::{AtomicU64, Ordering::Relaxed},
}, },
thiserror::Error, thiserror::Error,
uapi::OwnedFd, uapi::{OwnedFd, c},
}; };
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Linearize)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Linearize)]
@ -285,15 +292,24 @@ unsafe impl UnsafeCellCloneSafe for SyncFile {}
pub enum AcquireSync { pub enum AcquireSync {
None, None,
Implicit, Implicit,
SyncFile { sync_file: SyncFile }, FdSync(FdSync),
Unnecessary, Unnecessary,
} }
impl AcquireSync { impl AcquireSync {
pub fn from_sync_file(sync_file: Option<SyncFile>) -> Self { pub fn from_fd_sync(sync: Option<FdSync>) -> Self {
match sync_file { match sync {
None => Self::Unnecessary, None => Self::Unnecessary,
Some(sync_file) => Self::SyncFile { sync_file }, Some(sync) => Self::FdSync(sync),
}
}
pub fn get_sync_file(&self) -> Option<&SyncFile> {
match self {
Self::None => None,
Self::Implicit => None,
Self::FdSync(sync) => sync.get_sync_file(),
Self::Unnecessary => None,
} }
} }
} }
@ -310,7 +326,7 @@ impl Debug for AcquireSync {
let name = match self { let name = match self {
AcquireSync::None => "None", AcquireSync::None => "None",
AcquireSync::Implicit => "Implicit", AcquireSync::Implicit => "Implicit",
AcquireSync::SyncFile { .. } => "SyncFile", AcquireSync::FdSync(d) => return Debug::fmt(d, f),
AcquireSync::Unnecessary => "Unnecessary", AcquireSync::Unnecessary => "Unnecessary",
}; };
f.debug_struct(name).finish_non_exhaustive() f.debug_struct(name).finish_non_exhaustive()
@ -318,7 +334,7 @@ impl Debug for AcquireSync {
} }
pub trait BufferResv: Debug { pub trait BufferResv: Debug {
fn set_sync_file(&self, user: BufferResvUser, sync_file: &SyncFile); fn set_sync(&self, user: BufferResvUser, sync: &FdSync);
} }
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
@ -363,7 +379,7 @@ pub trait GfxFramebuffer: Debug {
region: &Region, region: &Region,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>, blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>, blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError>; ) -> Result<Option<FdSync>, GfxError>;
fn format(&self) -> &'static Format; fn format(&self) -> &'static Format;
@ -398,7 +414,7 @@ impl dyn GfxFramebuffer {
clear_cd: &Rc<LinearColorDescription>, clear_cd: &Rc<LinearColorDescription>,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>, blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>, blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> { ) -> Result<Option<FdSync>, GfxError> {
self.clone().render_with_region( self.clone().render_with_region(
acquire_sync, acquire_sync,
release_sync, release_sync,
@ -417,7 +433,7 @@ impl dyn GfxFramebuffer {
acquire_sync: AcquireSync, acquire_sync: AcquireSync,
release_sync: ReleaseSync, release_sync: ReleaseSync,
cd: &Rc<ColorDescription>, cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> { ) -> Result<Option<FdSync>, GfxError> {
self.clear_with( self.clear_with(
acquire_sync, acquire_sync,
release_sync, release_sync,
@ -434,7 +450,7 @@ impl dyn GfxFramebuffer {
cd: &Rc<ColorDescription>, cd: &Rc<ColorDescription>,
color: &Color, color: &Color,
color_cd: &Rc<LinearColorDescription>, color_cd: &Rc<LinearColorDescription>,
) -> Result<Option<SyncFile>, GfxError> { ) -> Result<Option<FdSync>, GfxError> {
self.render( self.render(
acquire_sync, acquire_sync,
release_sync, release_sync,
@ -472,7 +488,7 @@ impl dyn GfxFramebuffer {
release_sync: ReleaseSync, release_sync: ReleaseSync,
x: i32, x: i32,
y: i32, y: i32,
) -> Result<Option<SyncFile>, GfxError> { ) -> Result<Option<FdSync>, GfxError> {
let mut ops = vec![]; let mut ops = vec![];
let scale = Scale::from_int(1); let scale = Scale::from_int(1);
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None); let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
@ -516,7 +532,7 @@ impl dyn GfxFramebuffer {
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>, blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>, blend_cd: &Rc<ColorDescription>,
f: &mut dyn FnMut(&mut RendererBase), f: &mut dyn FnMut(&mut RendererBase),
) -> Result<Option<SyncFile>, GfxError> { ) -> Result<Option<FdSync>, GfxError> {
let mut ops = vec![]; let mut ops = vec![];
let mut renderer = self.renderer_base(&mut ops, scale, Transform::None); let mut renderer = self.renderer_base(&mut ops, scale, Transform::None);
f(&mut renderer); f(&mut renderer);
@ -569,7 +585,7 @@ impl dyn GfxFramebuffer {
region: &Region, region: &Region,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>, blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>, blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> { ) -> Result<Option<FdSync>, GfxError> {
self.clone().render_with_region( self.clone().render_with_region(
acquire_sync, acquire_sync,
release_sync, release_sync,
@ -596,7 +612,7 @@ impl dyn GfxFramebuffer {
fill_black_in_grace_period: bool, fill_black_in_grace_period: bool,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>, blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>, blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> { ) -> Result<Option<FdSync>, GfxError> {
self.render_node( self.render_node(
acquire_sync, acquire_sync,
release_sync, release_sync,
@ -631,7 +647,7 @@ impl dyn GfxFramebuffer {
transform: Transform, transform: Transform,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>, blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>, blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> { ) -> Result<Option<FdSync>, GfxError> {
let pass = self.create_render_pass( let pass = self.create_render_pass(
node, node,
state, state,
@ -664,7 +680,7 @@ impl dyn GfxFramebuffer {
scale: Scale, scale: Scale,
transform: Transform, transform: Transform,
cd: &Rc<ColorDescription>, cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> { ) -> Result<Option<FdSync>, GfxError> {
let mut ops = vec![]; let mut ops = vec![];
let mut renderer = Renderer { let mut renderer = Renderer {
base: self.renderer_base(&mut ops, scale, transform), base: self.renderer_base(&mut ops, scale, transform),
@ -1087,3 +1103,108 @@ pub fn renderer_base<'a>(
pub fn logical_size(physical_size: (i32, i32), transform: Transform) -> (i32, i32) { pub fn logical_size(physical_size: (i32, i32), transform: Transform) -> (i32, i32) {
transform.maybe_swap(physical_size) transform.maybe_swap(physical_size)
} }
pub struct ReservedSyncobjPoint {
pub ctx: Rc<SyncobjCtx>,
pub syncobj: Rc<Syncobj>,
pub point: SyncobjPoint,
pub sync_file: OnceCell<Option<SyncFile>>,
pub signaled: Eventfd,
}
impl Debug for ReservedSyncobjPoint {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ReservedSyncobjPoint")
.field("syncobj", &self.syncobj.id())
.field("point", &self.point)
.finish_non_exhaustive()
}
}
#[derive(Clone, Debug)]
pub enum FdSync {
SyncFile(SyncFile),
Syncobj(Rc<ReservedSyncobjPoint>),
}
unsafe impl UnsafeCellCloneSafe for FdSync {}
impl FdSync {
pub async fn try_signaled(&self, ring: &Rc<IoUring>) -> Result<(), IoUringError> {
match self {
FdSync::SyncFile(f) => ring.readable(&f.0).await.map(drop),
FdSync::Syncobj(obj) => obj.signaled.signaled().await,
}
}
pub async fn signaled(&self, ring: &Rc<IoUring>, name: &str) {
if let Err(e) = self.try_signaled(ring).await {
log::error!(
"Could not wait for {name} sync to become signaled: {}",
ErrorFmt(e),
);
}
}
pub fn signaled_blocking(&self, name: &str) {
let res = match self {
FdSync::Syncobj(obj) => obj.signaled.signaled_blocking(),
FdSync::SyncFile(f) => {
let mut pollfd = c::pollfd {
fd: f.raw(),
events: c::POLLIN,
revents: 0,
};
uapi::poll(slice::from_mut(&mut pollfd), -1)
.map(drop)
.map_err(Into::into)
}
};
if let Err(e) = res {
log::error!(
"Could not wait for {name} sync to become signaled: {}",
ErrorFmt(e),
);
}
}
pub fn is_unsignaled(&self) -> bool {
!self.is_signaled()
}
pub fn is_signaled(&self) -> bool {
match self {
FdSync::Syncobj(obj) => obj.signaled.is_signaled(),
FdSync::SyncFile(f) => {
let mut pollfd = c::pollfd {
fd: f.raw(),
events: c::POLLIN,
revents: 0,
};
uapi::poll(slice::from_mut(&mut pollfd), 0) == Ok(1)
}
}
}
pub fn get_sync_file(&self) -> Option<&SyncFile> {
match self {
FdSync::SyncFile(f) => Some(f),
FdSync::Syncobj(obj) => {
if obj.signaled.is_signaled() {
return None;
}
obj.sync_file
.get_or_init(|| {
match obj.ctx.export_sync_file_blocking(&obj.syncobj, obj.point) {
Ok(sf) => Some(sf),
Err(e) => {
log::error!("Could not export sync file: {}", ErrorFmt(e));
None
}
}
})
.as_ref()
}
}
}
}

View file

@ -2,6 +2,7 @@ pub use vulkan::create_vulkan_allocator;
use { use {
crate::{ crate::{
async_engine::AsyncEngine, async_engine::AsyncEngine,
eventfd_cache::EventfdCache,
gfx_api::{GfxApi, GfxContext, GfxError}, gfx_api::{GfxApi, GfxContext, GfxError},
io_uring::IoUring, io_uring::IoUring,
pr_caps::PrCapsThread, pr_caps::PrCapsThread,
@ -17,6 +18,7 @@ mod vulkan;
pub fn create_gfx_context( pub fn create_gfx_context(
eng: &Rc<AsyncEngine>, eng: &Rc<AsyncEngine>,
ring: &Rc<IoUring>, ring: &Rc<IoUring>,
eventfd_cache: &Rc<EventfdCache>,
drm: &Drm, drm: &Drm,
api: GfxApi, api: GfxApi,
caps_thread: Option<&PrCapsThread>, caps_thread: Option<&PrCapsThread>,
@ -26,7 +28,8 @@ pub fn create_gfx_context(
let mut last_err = None; let mut last_err = None;
for software in [false, true] { for software in [false, true] {
for api in apis { 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 { match res {
Ok(_) => { Ok(_) => {
log::info!("Created a {api:?} renderer"); log::info!("Created a {api:?} renderer");
@ -48,6 +51,7 @@ pub fn create_gfx_context(
fn create_gfx_context_( fn create_gfx_context_(
eng: &Rc<AsyncEngine>, eng: &Rc<AsyncEngine>,
ring: &Rc<IoUring>, ring: &Rc<IoUring>,
eventfd_cache: &Rc<EventfdCache>,
drm: &Drm, drm: &Drm,
api: GfxApi, api: GfxApi,
caps_thread: Option<&PrCapsThread>, caps_thread: Option<&PrCapsThread>,
@ -55,6 +59,8 @@ fn create_gfx_context_(
) -> Result<Rc<dyn GfxContext>, GfxError> { ) -> Result<Rc<dyn GfxContext>, GfxError> {
match api { match api {
GfxApi::OpenGl => gl::create_gfx_context(drm, software), 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)
}
} }
} }

View file

@ -69,8 +69,8 @@ use {
crate::{ crate::{
cmm::cmm_eotf::Eotf, cmm::cmm_eotf::Eotf,
gfx_api::{ gfx_api::{
AcquireSync, CopyTexture, FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxTexture, AcquireSync, CopyTexture, FdSync, FramebufferRect, GfxApiOpt, GfxContext, GfxError,
ReleaseSync, SyncFile, GfxTexture, ReleaseSync, SyncFile,
}, },
gfx_apis::gl::{ gfx_apis::gl::{
egl::image::EglImage, egl::image::EglImage,
@ -221,7 +221,7 @@ struct GlFillRect {
pub color: Color, pub color: Color,
} }
fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<SyncFile> { fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<FdSync> {
let mut state = fb.ctx.gl_state.borrow_mut(); let mut state = fb.ctx.gl_state.borrow_mut();
let state = &mut *state; let state = &mut *state;
let mut fill_rect = state.fill_rect.take(); let mut fill_rect = state.fill_rect.take();
@ -301,13 +301,14 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<SyncFile> {
return None; return None;
} }
}; };
let file = FdSync::SyncFile(file);
let user = fb.ctx.buffer_resv_user; let user = fb.ctx.buffer_resv_user;
for op in ops { for op in ops {
if let GfxApiOpt::CopyTexture(ct) = op if let GfxApiOpt::CopyTexture(ct) = op
&& ct.release_sync == ReleaseSync::Explicit && ct.release_sync == ReleaseSync::Explicit
&& let Some(resv) = &ct.buffer_resv && let Some(resv) = &ct.buffer_resv
{ {
resv.set_sync_file(user, &file); resv.set_sync(user, &file);
} }
} }
return Some(file); return Some(file);
@ -417,9 +418,8 @@ fn render_texture(ctx: &GlRenderContext, tex: &CopyTexture) {
} }
fn handle_explicit_sync(ctx: &GlRenderContext, img: Option<&Rc<EglImage>>, sync: &AcquireSync) { fn handle_explicit_sync(ctx: &GlRenderContext, img: Option<&Rc<EglImage>>, sync: &AcquireSync) {
let sync_file = match sync { let Some(sync_file) = sync.get_sync_file() else {
AcquireSync::None | AcquireSync::Implicit | AcquireSync::Unnecessary => return, return;
AcquireSync::SyncFile { sync_file } => sync_file,
}; };
let sync_file = match uapi::fcntl_dupfd_cloexec(sync_file.raw(), 0) { let sync_file = match uapi::fcntl_dupfd_cloexec(sync_file.raw(), 0) {
Ok(s) => s, Ok(s) => s,

View file

@ -6,9 +6,9 @@ use {
}, },
format::Format, format::Format,
gfx_api::{ gfx_api::{
AcquireSync, AsyncShmGfxTextureCallback, GfxApiOpt, GfxBlendBuffer, GfxError, AcquireSync, AsyncShmGfxTextureCallback, FdSync, GfxApiOpt, GfxBlendBuffer, GfxError,
GfxFramebuffer, GfxInternalFramebuffer, GfxStagingBuffer, PendingShmTransfer, GfxFramebuffer, GfxInternalFramebuffer, GfxStagingBuffer, PendingShmTransfer,
ReleaseSync, ShmMemory, SyncFile, ReleaseSync, ShmMemory,
}, },
gfx_apis::gl::{ gfx_apis::gl::{
RenderError, RenderError,
@ -74,7 +74,7 @@ impl Framebuffer {
acquire_sync: AcquireSync, acquire_sync: AcquireSync,
ops: &[GfxApiOpt], ops: &[GfxApiOpt],
clear: Option<&Color>, clear: Option<&Color>,
) -> Result<Option<SyncFile>, RenderError> { ) -> Result<Option<FdSync>, RenderError> {
let gles = self.ctx.ctx.dpy.gles; let gles = self.ctx.ctx.dpy.gles;
self.ctx.ctx.with_current(|| { self.ctx.ctx.with_current(|| {
handle_explicit_sync(&self.ctx, self.gl.rb._img.as_ref(), &acquire_sync); handle_explicit_sync(&self.ctx, self.gl.rb._img.as_ref(), &acquire_sync);
@ -88,13 +88,13 @@ impl Framebuffer {
} }
(gles.glBlendFunc)(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); (gles.glBlendFunc)(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
} }
let fd = run_ops(self, ops); let sync = run_ops(self, ops);
if fd.is_none() { if sync.is_none() {
unsafe { unsafe {
(gles.glFinish)(); (gles.glFinish)();
} }
} }
Ok(fd) Ok(sync)
}) })
} }
} }
@ -115,7 +115,7 @@ impl GfxFramebuffer for Framebuffer {
_region: &Region, _region: &Region,
_blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>, _blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
_blend_cd: &Rc<ColorDescription>, _blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> { ) -> Result<Option<FdSync>, GfxError> {
(*self) (*self)
.render(acquire_sync, ops, clear) .render(acquire_sync, ops, clear)
.map_err(|e| e.into()) .map_err(|e| e.into())

View file

@ -9,7 +9,6 @@ mod descriptor_buffer;
mod device; mod device;
mod dmabuf_buffer; mod dmabuf_buffer;
mod eotfs; mod eotfs;
mod fence;
mod format; mod format;
mod gpu_alloc_ash; mod gpu_alloc_ash;
mod image; mod image;
@ -28,6 +27,7 @@ use {
allocator::{Allocator, AllocatorError}, allocator::{Allocator, AllocatorError},
async_engine::AsyncEngine, async_engine::AsyncEngine,
cpu_worker::{CpuWorker, jobs::read_write::ReadWriteJobError}, cpu_worker::{CpuWorker, jobs::read_write::ReadWriteJobError},
eventfd_cache::EventfdCache,
format::Format, format::Format,
gfx_api::{ gfx_api::{
AsyncShmGfxTexture, GfxApi, GfxBlendBuffer, GfxBuffer, GfxContext, GfxError, GfxFormat, AsyncShmGfxTexture, GfxApi, GfxBlendBuffer, GfxBuffer, GfxContext, GfxError, GfxFormat,
@ -35,7 +35,8 @@ use {
STAGING_DOWNLOAD, STAGING_UPLOAD, ShmGfxTexture, StagingBufferUsecase, STAGING_DOWNLOAD, STAGING_UPLOAD, ShmGfxTexture, StagingBufferUsecase,
}, },
gfx_apis::vulkan::{ gfx_apis::vulkan::{
image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer, device::VulkanDevice, image::VulkanImageMemory, instance::VulkanInstance,
renderer::VulkanRenderer,
}, },
io_uring::IoUring, io_uring::IoUring,
pr_caps::PrCapsThread, pr_caps::PrCapsThread,
@ -46,7 +47,7 @@ use {
drm::{Drm, DrmError, syncobj::SyncobjCtx}, drm::{Drm, DrmError, syncobj::SyncobjCtx},
gbm::GbmError, gbm::GbmError,
}, },
vulkan_core::VulkanCoreError, vulkan_core::{self, VulkanCoreError},
}, },
ahash::AHashMap, ahash::AHashMap,
ash::vk, ash::vk,
@ -73,8 +74,6 @@ pub enum VulkanError {
CreateDevice(#[source] vk::Result), CreateDevice(#[source] vk::Result),
#[error("Could not create a semaphore")] #[error("Could not create a semaphore")]
CreateSemaphore(#[source] vk::Result), CreateSemaphore(#[source] vk::Result),
#[error("Could not create a fence")]
CreateFence(#[source] vk::Result),
#[error("Could not create the buffer")] #[error("Could not create the buffer")]
CreateBuffer(#[source] vk::Result), CreateBuffer(#[source] vk::Result),
#[error("Could not create a shader module")] #[error("Could not create a shader module")]
@ -157,8 +156,6 @@ pub enum VulkanError {
IoctlExportSyncFile(#[source] OsError), IoctlExportSyncFile(#[source] OsError),
#[error("Could not import a sync file into a semaphore")] #[error("Could not import a sync file into a semaphore")]
ImportSyncFile(#[source] vk::Result), ImportSyncFile(#[source] vk::Result),
#[error("Could not export a sync file from a semaphore")]
ExportSyncFile(#[source] vk::Result),
#[error("Could not fetch the render node of the device")] #[error("Could not fetch the render node of the device")]
FetchRenderNode(#[source] DrmError), FetchRenderNode(#[source] DrmError),
#[error("Device has no render node")] #[error("Device has no render node")]
@ -215,6 +212,10 @@ pub enum VulkanError {
DmaBufBufferOffsetAlignment, DmaBufBufferOffsetAlignment,
} }
type VulkanSync = vulkan_core::sync::VulkanSync<VulkanDevice>;
type VulkanTimelineSemaphore =
vulkan_core::timeline_semaphore::VulkanTimelineSemaphore<VulkanDevice>;
impl From<VulkanError> for GfxError { impl From<VulkanError> for GfxError {
fn from(value: VulkanError) -> Self { fn from(value: VulkanError) -> Self {
Self(Box::new(value)) Self(Box::new(value))
@ -224,6 +225,7 @@ impl From<VulkanError> for GfxError {
pub fn create_graphics_context( pub fn create_graphics_context(
eng: &Rc<AsyncEngine>, eng: &Rc<AsyncEngine>,
ring: &Rc<IoUring>, ring: &Rc<IoUring>,
eventfd_cache: &Rc<EventfdCache>,
drm: &Drm, drm: &Drm,
caps_thread: Option<&PrCapsThread>, caps_thread: Option<&PrCapsThread>,
software: bool, software: bool,
@ -231,22 +233,25 @@ pub fn create_graphics_context(
let instance = VulkanInstance::new(Level::Info)?; let instance = VulkanInstance::new(Level::Info)?;
let device = 'device: { let device = 'device: {
if let Some(t) = caps_thread { 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, Ok(d) => break 'device d,
Err(e) => { Err(e) => {
log::warn!("Could not create high-priority device: {}", ErrorFmt(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)?; let renderer = device.create_renderer(eng, ring)?;
Ok(Rc::new(Context(renderer))) Ok(Rc::new(Context(renderer)))
} }
pub fn create_vulkan_allocator(drm: &Drm) -> Result<Rc<dyn Allocator>, AllocatorError> { pub fn create_vulkan_allocator(
drm: &Drm,
eventfd_cache: &Rc<EventfdCache>,
) -> Result<Rc<dyn Allocator>, AllocatorError> {
let instance = VulkanInstance::new(Level::Debug)?; 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)?; let allocator = device.create_bo_allocator(drm)?;
Ok(Rc::new(allocator)) Ok(Rc::new(allocator))
} }

View file

@ -1,6 +1,7 @@
use { use {
crate::{ crate::{
allocator::BufferObject, allocator::BufferObject,
eventfd_cache::EventfdCache,
format::XRGB8888, format::XRGB8888,
gfx_apis::vulkan::{ gfx_apis::vulkan::{
VulkanError, VulkanError,
@ -14,7 +15,8 @@ use {
gbm::{GBM_BO_USE_RENDERING, GbmDevice}, gbm::{GBM_BO_USE_RENDERING, GbmDevice},
}, },
vulkan_core::{ vulkan_core::{
ApiVersionDisplay, Extensions, VULKAN_API_VERSION, map_extension_properties, ApiVersionDisplay, Extensions, VULKAN_API_VERSION, VulkanCoreInstance,
device::VulkanDeviceInf, map_extension_properties,
}, },
}, },
ahash::AHashMap, ahash::AHashMap,
@ -63,6 +65,7 @@ pub struct VulkanDevice {
pub(super) sync_ctx: Rc<SyncobjCtx>, pub(super) sync_ctx: Rc<SyncobjCtx>,
pub(super) instance: Rc<VulkanInstance>, pub(super) instance: Rc<VulkanInstance>,
pub(super) device: Arc<Device>, pub(super) device: Arc<Device>,
pub(super) eventfd_cache: Rc<EventfdCache>,
pub(super) external_memory_fd: external_memory_fd::Device, pub(super) external_memory_fd: external_memory_fd::Device,
pub(super) external_semaphore_fd: external_semaphore_fd::Device, pub(super) external_semaphore_fd: external_semaphore_fd::Device,
pub(super) external_fence_fd: external_fence_fd::Device, pub(super) external_fence_fd: external_fence_fd::Device,
@ -85,6 +88,7 @@ pub struct VulkanDevice {
pub(super) uniform_buffer_descriptor_size: usize, pub(super) uniform_buffer_descriptor_size: usize,
pub(super) lost: Cell<bool>, pub(super) lost: Cell<bool>,
pub(super) fast_ram_access: bool, pub(super) fast_ram_access: bool,
pub(super) supports_timeline_opaque_export: bool,
} }
impl Drop for VulkanDevice { impl Drop for VulkanDevice {
@ -337,6 +341,7 @@ impl VulkanInstance {
pub fn create_device( pub fn create_device(
self: &Rc<Self>, self: &Rc<Self>,
drm: &Drm, drm: &Drm,
eventfd_cache: &Rc<EventfdCache>,
mut high_priority: bool, mut high_priority: bool,
software: bool, software: bool,
) -> Result<Rc<VulkanDevice>, VulkanError> { ) -> Result<Rc<VulkanDevice>, VulkanError> {
@ -379,6 +384,9 @@ impl VulkanInstance {
} }
transfer_granularity_mask = (width_mask, height_mask); 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) { if !self.supports_semaphore_import(phy_dev) {
return Err(VulkanError::SyncFileImport); return Err(VulkanError::SyncFileImport);
} }
@ -389,8 +397,8 @@ impl VulkanInstance {
if supports_descriptor_buffer { if supports_descriptor_buffer {
enabled_extensions.push(descriptor_buffer::NAME.as_ptr()); enabled_extensions.push(descriptor_buffer::NAME.as_ptr());
} }
let mut semaphore_features = let mut semaphore_features = PhysicalDeviceTimelineSemaphoreFeatures::default()
PhysicalDeviceTimelineSemaphoreFeatures::default().timeline_semaphore(true); .timeline_semaphore(supports_timeline_opaque_export);
let mut synchronization2_features = let mut synchronization2_features =
PhysicalDeviceSynchronization2Features::default().synchronization2(true); PhysicalDeviceSynchronization2Features::default().synchronization2(true);
let mut dynamic_rendering_features = let mut dynamic_rendering_features =
@ -557,6 +565,7 @@ impl VulkanInstance {
gbm: Rc::new(gbm), gbm: Rc::new(gbm),
instance: self.clone(), instance: self.clone(),
device: Arc::new(device), device: Arc::new(device),
eventfd_cache: eventfd_cache.clone(),
external_memory_fd, external_memory_fd,
external_semaphore_fd, external_semaphore_fd,
external_fence_fd, external_fence_fd,
@ -580,6 +589,7 @@ impl VulkanInstance {
uniform_buffer_descriptor_size, uniform_buffer_descriptor_size,
lost: Cell::new(false), lost: Cell::new(false),
fast_ram_access, fast_ram_access,
supports_timeline_opaque_export,
})) }))
} }
} }
@ -639,3 +649,33 @@ fn log_device(
} }
} }
} }
impl VulkanDeviceInf for VulkanDevice {
fn instance(&self) -> &VulkanCoreInstance {
&self.instance
}
fn device(&self) -> &Device {
&self.device
}
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<SyncobjCtx> {
&self.sync_ctx
}
fn eventfd_cache(&self) -> &Rc<EventfdCache> {
&self.eventfd_cache
}
}

View file

@ -4,9 +4,9 @@ use {
format::Format, format::Format,
gfx_api::{ gfx_api::{
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback, AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback,
AsyncShmGfxTextureTransferCancellable, GfxApiOpt, GfxBlendBuffer, GfxBuffer, GfxError, AsyncShmGfxTextureTransferCancellable, FdSync, GfxApiOpt, GfxBlendBuffer, GfxBuffer,
GfxFramebuffer, GfxImage, GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture, GfxError, GfxFramebuffer, GfxImage, GfxInternalFramebuffer, GfxStagingBuffer,
PendingShmTransfer, ReleaseSync, ShmGfxTexture, ShmMemory, SyncFile, GfxTexture, PendingShmTransfer, ReleaseSync, ShmGfxTexture, ShmMemory,
}, },
gfx_apis::vulkan::{ gfx_apis::vulkan::{
VulkanError, allocator::VulkanAllocation, device::VulkanDevice, VulkanError, allocator::VulkanAllocation, device::VulkanDevice,
@ -563,7 +563,7 @@ impl GfxFramebuffer for VulkanImage {
region: &Region, region: &Region,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>, blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>, blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> { ) -> Result<Option<FdSync>, GfxError> {
let mut blend_buffer = blend_buffer let mut blend_buffer = blend_buffer
.map(|b| b.clone().into_vk(&self.renderer.device.device)) .map(|b| b.clone().into_vk(&self.renderer.device.device))
.transpose()?; .transpose()?;

View file

@ -8,11 +8,11 @@ use {
}, },
cpu_worker::PendingJob, cpu_worker::PendingJob,
gfx_api::{ gfx_api::{
AcquireSync, AlphaMode, BufferResv, BufferResvUser, GfxApiOpt, GfxBlendBuffer, AcquireSync, AlphaMode, BufferResv, BufferResvUser, FdSync, GfxApiOpt, GfxBlendBuffer,
GfxFormat, GfxTexture, GfxWriteModifier, ReleaseSync, SyncFile, GfxFormat, GfxTexture, GfxWriteModifier, ReleaseSync,
}, },
gfx_apis::vulkan::{ gfx_apis::vulkan::{
VulkanError, VulkanError, VulkanSync, VulkanTimelineSemaphore,
allocator::{VulkanAllocator, VulkanThreadedAllocator}, allocator::{VulkanAllocator, VulkanThreadedAllocator},
buffer_cache::{GenericBufferWriter, VulkanBuffer, VulkanBufferCache}, buffer_cache::{GenericBufferWriter, VulkanBuffer, VulkanBufferCache},
command::{VulkanCommandBuffer, VulkanCommandPool}, command::{VulkanCommandBuffer, VulkanCommandPool},
@ -20,7 +20,6 @@ use {
descriptor_buffer::VulkanDescriptorBufferWriter, descriptor_buffer::VulkanDescriptorBufferWriter,
device::VulkanDevice, device::VulkanDevice,
eotfs::{EOTF_LINEAR, EotfExt, VulkanEotf}, eotfs::{EOTF_LINEAR, EotfExt, VulkanEotf},
fence::VulkanFence,
image::{QueueFamily, QueueState, QueueTransfer, VulkanImage, VulkanImageMemory}, image::{QueueFamily, QueueState, QueueTransfer, VulkanImage, VulkanImageMemory},
pipeline::{PipelineCreateInfo, VulkanPipeline}, pipeline::{PipelineCreateInfo, VulkanPipeline},
sampler::VulkanSampler, sampler::VulkanSampler,
@ -40,6 +39,9 @@ use {
stack::Stack, stack::Stack,
}, },
video::dmabuf::{DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, dma_buf_export_sync_file}, video::dmabuf::{DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, dma_buf_export_sync_file},
vulkan_core::{
sync::VulkanDeviceSyncExt, timeline_semaphore::VulkanDeviceTimelineSemaphoreExt,
},
}, },
ahash::AHashMap, ahash::AHashMap,
arrayvec::ArrayVec, arrayvec::ArrayVec,
@ -65,7 +67,7 @@ use {
std::{ std::{
any::Any, any::Any,
borrow::Cow, borrow::Cow,
cell::{Cell, RefCell}, cell::{Cell, LazyCell, RefCell},
collections::hash_map::Entry, collections::hash_map::Entry,
fmt::{Debug, Formatter}, fmt::{Debug, Formatter},
mem, mem,
@ -113,6 +115,8 @@ pub struct VulkanRenderer {
pub(super) blend_buffers: RefCell<AHashMap<(u32, u32), Weak<VulkanImage>>>, pub(super) blend_buffers: RefCell<AHashMap<(u32, u32), Weak<VulkanImage>>>,
pub(super) shader_buffer_cache: Rc<VulkanBufferCache>, pub(super) shader_buffer_cache: Rc<VulkanBufferCache>,
pub(super) uniform_buffer_cache: Rc<VulkanBufferCache>, pub(super) uniform_buffer_cache: Rc<VulkanBufferCache>,
pub(super) render_tls: Option<Rc<VulkanTimelineSemaphore>>,
pub(super) transfer_tls: Option<Rc<VulkanTimelineSemaphore>>,
} }
pub(super) struct CachedCommandBuffers { pub(super) struct CachedCommandBuffers {
@ -162,8 +166,8 @@ pub(super) struct Memory {
image_barriers: Vec<ImageMemoryBarrier2<'static>>, image_barriers: Vec<ImageMemoryBarrier2<'static>>,
wait_semaphores: Vec<Rc<VulkanSemaphore>>, wait_semaphores: Vec<Rc<VulkanSemaphore>>,
wait_semaphore_infos: Vec<SemaphoreSubmitInfo<'static>>, wait_semaphore_infos: Vec<SemaphoreSubmitInfo<'static>>,
release_fence: Option<Rc<VulkanFence>>, release_vulkan_sync: Option<VulkanSync>,
release_sync_file: Option<SyncFile>, release_sync: Option<FdSync>,
used_buffers: ArrayVec<VulkanBuffer, 4>, used_buffers: ArrayVec<VulkanBuffer, 4>,
paint_bounds: StaticMap<RenderPass, Option<PaintRegion>>, paint_bounds: StaticMap<RenderPass, Option<PaintRegion>>,
paint_regions: StaticMap<RenderPass, Vec<PaintRegion>>, paint_regions: StaticMap<RenderPass, Vec<PaintRegion>>,
@ -249,7 +253,7 @@ pub(super) struct PendingFrame {
_textures: Vec<UsedTexture>, _textures: Vec<UsedTexture>,
wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>, wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>,
waiter: Cell<Option<SpawnedFuture<()>>>, waiter: Cell<Option<SpawnedFuture<()>>>,
_release_fence: Option<Rc<VulkanFence>>, vulkan_sync: Option<VulkanSync>,
_used_buffers: ArrayVec<VulkanBuffer, 4>, _used_buffers: ArrayVec<VulkanBuffer, 4>,
} }
@ -407,6 +411,8 @@ impl VulkanDevice {
blend_buffers: Default::default(), blend_buffers: Default::default(),
shader_buffer_cache, shader_buffer_cache,
uniform_buffer_cache, uniform_buffer_cache,
render_tls: self.create_timeline_semaphore_or_log(),
transfer_tls: self.create_timeline_semaphore_or_log(),
}); });
Ok(render) Ok(render)
} }
@ -1671,10 +1677,12 @@ impl VulkanRenderer {
} }
} }
} }
AcquireSync::SyncFile { sync_file } => { AcquireSync::FdSync(sync) => {
let fd = uapi::fcntl_dupfd_cloexec(sync_file.raw(), 0) if let Some(sync_file) = sync.get_sync_file() {
.map_err(|e| VulkanError::Dupfd(e.into()))?; let fd = uapi::fcntl_dupfd_cloexec(sync_file.raw(), 0)
import_sync_file(fd)?; .map_err(|e| VulkanError::Dupfd(e.into()))?;
import_sync_file(fd)?;
}
} }
AcquireSync::Unnecessary => {} AcquireSync::Unnecessary => {}
} }
@ -1703,20 +1711,22 @@ impl VulkanRenderer {
fn import_release_semaphore(&self, fb: &VulkanImage, fb_release_sync: ReleaseSync) { fn import_release_semaphore(&self, fb: &VulkanImage, fb_release_sync: ReleaseSync) {
zone!("import_release_semaphore"); zone!("import_release_semaphore");
let memory = &mut *self.memory.borrow_mut(); let memory = &mut *self.memory.borrow_mut();
let sync_file = match memory.release_sync_file.as_ref() { let fd_sync = match memory.release_sync.as_ref() {
Some(sync_file) => sync_file, Some(sync) => sync,
_ => return, _ => return,
}; };
let sync_file = LazyCell::new(|| fd_sync.get_sync_file());
let import = let import =
|img: &VulkanImage, sync: ReleaseSync, resv: Option<Rc<dyn BufferResv>>, flag: u32| { |img: &VulkanImage, sync: ReleaseSync, resv: Option<Rc<dyn BufferResv>>, flag: u32| {
if sync == ReleaseSync::None { if sync == ReleaseSync::None {
return; return;
} }
if let Some(resv) = resv { if let Some(resv) = resv {
resv.set_sync_file(self.buffer_resv_user, sync_file); resv.set_sync(self.buffer_resv_user, &fd_sync);
} else if sync == ReleaseSync::Implicit { } else if sync == ReleaseSync::Implicit {
if let VulkanImageMemory::DmaBuf(buf) = &img.ty if let VulkanImageMemory::DmaBuf(buf) = &img.ty
&& let Err(e) = buf.template.dmabuf.import_sync_file(flag, sync_file) && let Some(fd) = &*sync_file
&& let Err(e) = buf.template.dmabuf.import_sync_file(flag, fd)
{ {
log::error!("Could not import sync file into dmabuf: {}", ErrorFmt(e)); log::error!("Could not import sync file into dmabuf: {}", ErrorFmt(e));
log::warn!("Relying on implicit sync"); log::warn!("Relying on implicit sync");
@ -1736,14 +1746,14 @@ impl VulkanRenderer {
&& let VulkanImageMemory::Internal(shm) = &texture.tex.ty && let VulkanImageMemory::Internal(shm) = &texture.tex.ty
&& let Some(data) = &shm.async_data && let Some(data) = &shm.async_data
{ {
data.last_gfx_use.set(Some(sync_file.clone())); data.last_gfx_use.set(Some(fd_sync.clone()));
} }
} }
if attach_async_shm_sync_file if attach_async_shm_sync_file
&& let VulkanImageMemory::Internal(shm) = &fb.ty && let VulkanImageMemory::Internal(shm) = &fb.ty
&& let Some(data) = &shm.async_data && let Some(data) = &shm.async_data
{ {
data.last_gfx_use.set(Some(sync_file.clone())); data.last_gfx_use.set(Some(fd_sync.clone()));
} }
import(fb, fb_release_sync, None, DMA_BUF_SYNC_WRITE); import(fb, fb_release_sync, None, DMA_BUF_SYNC_WRITE);
} }
@ -1751,33 +1761,29 @@ impl VulkanRenderer {
fn submit(&self, buf: CommandBuffer) -> Result<(), VulkanError> { fn submit(&self, buf: CommandBuffer) -> Result<(), VulkanError> {
zone!("submit"); zone!("submit");
let mut memory = self.memory.borrow_mut(); let mut memory = self.memory.borrow_mut();
let release_fence = self.device.create_fence()?; let mut semaphore_submit_info = SemaphoreSubmitInfo::default();
let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(buf); 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) .wait_semaphore_infos(&memory.wait_semaphore_infos)
.command_buffer_infos(slice::from_ref(&command_buffer_info)); .command_buffer_infos(slice::from_ref(&command_buffer_info));
let vulkan_sync = self.device.create_sync(
self.render_tls.as_ref(),
&mut semaphore_submit_info,
&mut submit_info,
)?;
unsafe { unsafe {
self.device self.device
.device .device
.queue_submit2( .queue_submit2(
self.device.graphics_queue, self.device.graphics_queue,
slice::from_ref(&submit_info), slice::from_ref(&submit_info),
release_fence.fence, vulkan_sync.fence(),
) )
.inspect_err(self.device.idl()) .inspect_err(self.device.idl())
.map_err(VulkanError::Submit)?; .map_err(VulkanError::Submit)?;
} }
zone!("export_sync_file"); memory.release_sync = vulkan_sync.to_sync(|| self.block());
let release_sync_file = match release_fence.export_sync_file() { memory.release_vulkan_sync = Some(vulkan_sync);
Ok(s) => s,
Err(e) => {
log::error!("Could not export sync file from fence: {}", ErrorFmt(e));
self.block();
None
}
};
memory.release_fence = Some(release_fence);
memory.release_sync_file = release_sync_file;
Ok(()) Ok(())
} }
@ -1816,14 +1822,14 @@ impl VulkanRenderer {
_textures: mem::take(&mut memory.textures), _textures: mem::take(&mut memory.textures),
wait_semaphores: Cell::new(mem::take(&mut memory.wait_semaphores)), wait_semaphores: Cell::new(mem::take(&mut memory.wait_semaphores)),
waiter: Cell::new(None), waiter: Cell::new(None),
_release_fence: memory.release_fence.take(), vulkan_sync: memory.release_vulkan_sync.take(),
_used_buffers: mem::take(&mut memory.used_buffers), _used_buffers: mem::take(&mut memory.used_buffers),
}); });
self.pending_frames.set(frame.point, frame.clone()); self.pending_frames.set(frame.point, frame.clone());
let future = self.eng.spawn( let future = self.eng.spawn(
"await release", "await release",
await_release( await_release(
memory.release_sync_file.clone(), memory.release_sync.clone(),
self.ring.clone(), self.ring.clone(),
frame.clone(), frame.clone(),
self.clone(), self.clone(),
@ -1844,7 +1850,7 @@ impl VulkanRenderer {
region: &Region, region: &Region,
blend_buffer: Option<Rc<VulkanImage>>, blend_buffer: Option<Rc<VulkanImage>>,
blend_cd: &Rc<ColorDescription>, blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, VulkanError> { ) -> Result<Option<FdSync>, VulkanError> {
zone!("execute"); zone!("execute");
let res = self.try_execute( let res = self.try_execute(
fb, fb,
@ -1858,19 +1864,19 @@ impl VulkanRenderer {
blend_buffer, blend_buffer,
blend_cd, blend_cd,
); );
let sync_file = { let sync = {
let mut memory = self.memory.borrow_mut(); let mut memory = self.memory.borrow_mut();
memory.textures.clear(); memory.textures.clear();
memory.dmabuf_sample.clear(); memory.dmabuf_sample.clear();
memory.queue_transfer.clear(); memory.queue_transfer.clear();
memory.wait_semaphores.clear(); memory.wait_semaphores.clear();
memory.release_fence.take(); memory.release_vulkan_sync.take();
memory.used_buffers.clear(); memory.used_buffers.clear();
memory.ops.clear(); memory.ops.clear();
memory.ops_tmp.clear(); memory.ops_tmp.clear();
memory.release_sync_file.take() memory.release_sync.take()
}; };
res.map(|_| sync_file) res.map(|_| sync)
} }
fn allocate_semaphore(&self) -> Result<Rc<VulkanSemaphore>, VulkanError> { fn allocate_semaphore(&self) -> Result<Rc<VulkanSemaphore>, VulkanError> {
@ -2187,13 +2193,13 @@ pub(super) fn image_barrier() -> ImageMemoryBarrier2<'static> {
} }
async fn await_release( async fn await_release(
sync_file: Option<SyncFile>, sync: Option<FdSync>,
ring: Rc<IoUring>, ring: Rc<IoUring>,
frame: Rc<PendingFrame>, frame: Rc<PendingFrame>,
renderer: Rc<VulkanRenderer>, renderer: Rc<VulkanRenderer>,
) { ) {
if let Some(sync_file) = sync_file if let Some(sync) = sync
&& let Err(e) = ring.readable(&sync_file).await && let Err(e) = sync.try_signaled(&ring).await
{ {
log::error!( log::error!(
"Could not wait for release semaphore to be signaled: {}", "Could not wait for release semaphore to be signaled: {}",
@ -2201,6 +2207,9 @@ async fn await_release(
); );
frame.renderer.block(); frame.renderer.block();
} }
if let Some(vs) = &frame.vulkan_sync {
vs.handle_validation();
}
if let Some(buf) = frame.cmd.take() { if let Some(buf) = frame.cmd.take() {
frame.renderer.gfx_command_buffers.buffers.push(buf); frame.renderer.gfx_command_buffers.buffers.push(buf);
} }

View file

@ -2,12 +2,11 @@ use {
crate::{ crate::{
cpu_worker::CpuWorker, cpu_worker::CpuWorker,
format::Format, format::Format,
gfx_api::SyncFile, gfx_api::FdSync,
gfx_apis::vulkan::{ gfx_apis::vulkan::{
VulkanError, VulkanError, VulkanSync,
allocator::VulkanAllocation, allocator::VulkanAllocation,
command::VulkanCommandBuffer, command::VulkanCommandBuffer,
fence::VulkanFence,
image::{QueueFamily, QueueState, VulkanImage, VulkanImageMemory}, image::{QueueFamily, QueueState, VulkanImage, VulkanImageMemory},
renderer::{VulkanRenderer, image_barrier}, renderer::{VulkanRenderer, image_barrier},
staging::VulkanStagingBuffer, staging::VulkanStagingBuffer,
@ -15,6 +14,7 @@ use {
}, },
rect::Rect, rect::Rect,
utils::errorfmt::ErrorFmt, utils::errorfmt::ErrorFmt,
vulkan_core::sync::VulkanDeviceSyncExt,
}, },
ash::vk::{ ash::vk::{
AccessFlags2, Buffer, BufferImageCopy2, BufferMemoryBarrier2, CommandBufferBeginInfo, AccessFlags2, Buffer, BufferImageCopy2, BufferMemoryBarrier2, CommandBufferBeginInfo,
@ -22,7 +22,8 @@ use {
CopyImageToBufferInfo2, DependencyInfoKHR, DeviceSize, Extent3D, ImageAspectFlags, CopyImageToBufferInfo2, DependencyInfoKHR, DeviceSize, Extent3D, ImageAspectFlags,
ImageCreateInfo, ImageLayout, ImageSubresourceLayers, ImageSubresourceRange, ImageTiling, ImageCreateInfo, ImageLayout, ImageSubresourceLayers, ImageSubresourceRange, ImageTiling,
ImageType, ImageUsageFlags, ImageViewCreateInfo, ImageViewType, Offset3D, 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, gpu_alloc::UsageFlags,
isnt::std_1::primitive::IsntSliceExt, isnt::std_1::primitive::IsntSliceExt,
@ -136,7 +137,7 @@ impl VulkanShmImage {
ptr::copy_nonoverlapping(buf, mem, total_size as usize); ptr::copy_nonoverlapping(buf, mem, total_size as usize);
} }
})?; })?;
let (cmd, fence, sync_file, point) = self.submit_buffer_image_copy( let (cmd, vulkan_sync, sync, point) = self.submit_buffer_image_copy(
img, img,
staging.buffer, staging.buffer,
staging.size, staging.size,
@ -147,7 +148,7 @@ impl VulkanShmImage {
)?; )?;
let future = img.renderer.eng.spawn( let future = img.renderer.eng.spawn(
"await upload", "await upload",
await_upload(point, img.clone(), cmd, sync_file, fence, staging), await_upload(point, img.clone(), cmd, sync, vulkan_sync, staging),
); );
img.renderer.pending_submits.set(point, future); img.renderer.pending_submits.set(point, future);
Ok(()) Ok(())
@ -162,15 +163,7 @@ impl VulkanShmImage {
use_transfer_queue: bool, use_transfer_queue: bool,
tt: TransferType, tt: TransferType,
foreign_buffer: bool, foreign_buffer: bool,
) -> Result< ) -> Result<(Rc<VulkanCommandBuffer>, VulkanSync, Option<FdSync>, u64), VulkanError> {
(
Rc<VulkanCommandBuffer>,
Rc<VulkanFence>,
Option<SyncFile>,
u64,
),
VulkanError,
> {
let mut transfer_queue_family_idx = img.renderer.device.graphics_queue_idx; let mut transfer_queue_family_idx = img.renderer.device.graphics_queue_idx;
if use_transfer_queue if use_transfer_queue
&& let Some(idx) = img.renderer.device.distinct_transfer_queue_family_idx && let Some(idx) = img.renderer.device.distinct_transfer_queue_family_idx
@ -277,11 +270,20 @@ impl VulkanShmImage {
}; };
let dev = &img.renderer.device.device; let dev = &img.renderer.device.device;
let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(cmd.buffer); 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)); SubmitInfo2::default().command_buffer_infos(slice::from_ref(&command_buffer_info));
let begin_info = let begin_info =
CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT); CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT);
let release_fence = img.renderer.device.create_fence()?; 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 { unsafe {
dev.begin_command_buffer(cmd.buffer, &begin_info) dev.begin_command_buffer(cmd.buffer, &begin_info)
.map_err(VulkanError::BeginCommandBuffer)?; .map_err(VulkanError::BeginCommandBuffer)?;
@ -313,7 +315,7 @@ impl VulkanShmImage {
_ => img.renderer.device.graphics_queue, _ => img.renderer.device.graphics_queue,
}, },
slice::from_ref(&submit_info), slice::from_ref(&submit_info),
release_fence.fence, vulkan_sync.fence(),
) )
.inspect_err(img.renderer.device.idl()) .inspect_err(img.renderer.device.idl())
.map_err(VulkanError::Submit)?; .map_err(VulkanError::Submit)?;
@ -322,16 +324,9 @@ impl VulkanShmImage {
img.is_undefined.set(false); img.is_undefined.set(false);
img.contents_are_undefined.set(false); img.contents_are_undefined.set(false);
} }
let release_sync_file = match release_fence.export_sync_file() { let release_sync = vulkan_sync.to_sync(|| img.renderer.block());
Ok(s) => s,
Err(e) => {
log::error!("Could not export sync file from fence: {}", ErrorFmt(e));
img.renderer.block();
None
}
};
let point = img.renderer.allocate_point(); let point = img.renderer.allocate_point();
Ok((cmd, release_fence, release_sync_file, point)) Ok((cmd, vulkan_sync, release_sync, point))
} }
} }
@ -339,12 +334,12 @@ async fn await_upload(
id: u64, id: u64,
img: Rc<VulkanImage>, img: Rc<VulkanImage>,
buf: Rc<VulkanCommandBuffer>, buf: Rc<VulkanCommandBuffer>,
sync_file: Option<SyncFile>, sync: Option<FdSync>,
_fence: Rc<VulkanFence>, vulkan_sync: VulkanSync,
_staging: VulkanStagingBuffer, _staging: VulkanStagingBuffer,
) { ) {
if let Some(sync_file) = sync_file if let Some(sync) = &sync
&& let Err(e) = img.renderer.ring.readable(&sync_file.0).await && let Err(e) = sync.try_signaled(&img.renderer.ring).await
{ {
log::error!( log::error!(
"Could not wait for sync file to become readable: {}", "Could not wait for sync file to become readable: {}",
@ -352,6 +347,7 @@ async fn await_upload(
); );
img.renderer.block(); img.renderer.block();
} }
vulkan_sync.handle_validation();
img.renderer.gfx_command_buffers.buffers.push(buf); img.renderer.gfx_command_buffers.buffers.push(buf);
img.renderer.pending_submits.remove(&id); img.renderer.pending_submits.remove(&id);
} }

View file

@ -8,13 +8,12 @@ use {
}, },
}, },
gfx_api::{ gfx_api::{
AsyncShmGfxTextureCallback, PendingShmTransfer, ShmMemory, ShmMemoryBacking, SyncFile, AsyncShmGfxTextureCallback, FdSync, PendingShmTransfer, ShmMemory, ShmMemoryBacking,
}, },
gfx_apis::vulkan::{ gfx_apis::vulkan::{
VulkanError, VulkanError, VulkanSync,
command::VulkanCommandBuffer, command::VulkanCommandBuffer,
dmabuf_buffer::{TRANSFER_QUEUE_BUFFER_ALIGNMENT, VulkanDmabufBuffer}, dmabuf_buffer::{TRANSFER_QUEUE_BUFFER_ALIGNMENT, VulkanDmabufBuffer},
fence::VulkanFence,
image::{QueueFamily, QueueState, QueueTransfer, VulkanImage, VulkanImageMemory}, image::{QueueFamily, QueueState, QueueTransfer, VulkanImage, VulkanImageMemory},
renderer::image_barrier, renderer::image_barrier,
shm_image::VulkanShmImage, shm_image::VulkanShmImage,
@ -22,12 +21,13 @@ use {
}, },
rect::{Rect, Region}, rect::{Rect, Region},
utils::{clonecell::CloneCell, errorfmt::ErrorFmt}, utils::{clonecell::CloneCell, errorfmt::ErrorFmt},
vulkan_core::sync::VulkanDeviceSyncExt,
}, },
arrayvec::ArrayVec, arrayvec::ArrayVec,
ash::vk::{ ash::vk::{
AccessFlags2, BufferImageCopy2, CommandBufferBeginInfo, CommandBufferSubmitInfo, AccessFlags2, BufferImageCopy2, CommandBufferBeginInfo, CommandBufferSubmitInfo,
CommandBufferUsageFlags, DependencyInfo, Extent3D, ImageAspectFlags, ImageLayout, CommandBufferUsageFlags, DependencyInfo, Extent3D, ImageAspectFlags, ImageLayout,
ImageSubresourceLayers, Offset3D, PipelineStageFlags2, SubmitInfo2, ImageSubresourceLayers, Offset3D, PipelineStageFlags2, SemaphoreSubmitInfo, SubmitInfo2,
}, },
std::{ std::{
cell::{Cell, RefCell, RefMut}, cell::{Cell, RefCell, RefMut},
@ -48,7 +48,7 @@ pub struct VulkanShmImageAsyncData {
pub(super) callback_id: Cell<u64>, pub(super) callback_id: Cell<u64>,
pub(super) regions: RefCell<Vec<BufferImageCopy2<'static>>>, pub(super) regions: RefCell<Vec<BufferImageCopy2<'static>>>,
pub(super) cpu: Rc<CpuWorker>, pub(super) cpu: Rc<CpuWorker>,
pub(super) last_gfx_use: Cell<Option<SyncFile>>, pub(super) last_gfx_use: Cell<Option<FdSync>>,
pub(super) data_copied: Cell<bool>, pub(super) data_copied: Cell<bool>,
} }
@ -314,7 +314,7 @@ impl VulkanShmImage {
img.renderer.check_defunct()?; img.renderer.check_defunct()?;
let Some(transfer_queue_idx) = img.renderer.device.distinct_transfer_queue_family_idx let Some(transfer_queue_idx) = img.renderer.device.distinct_transfer_queue_family_idx
else { else {
let Some(sync_file) = data.last_gfx_use.take() else { let Some(sync) = data.last_gfx_use.take() else {
img.queue_state.set(QueueState::Released { img.queue_state.set(QueueState::Released {
to: QueueFamily::Transfer, to: QueueFamily::Transfer,
}); });
@ -323,7 +323,7 @@ impl VulkanShmImage {
let id = img.renderer.allocate_point(); let id = img.renderer.allocate_point();
let pending = img.renderer.eng.spawn( let pending = img.renderer.eng.spawn(
"await_transfer_to_transfer", "await_transfer_to_transfer",
await_gfx_queue_release(id, img.clone(), None, None, Some(sync_file), tt), await_gfx_queue_release(id, img.clone(), None, None, Some(sync), tt),
); );
img.renderer.pending_submits.set(id, pending); img.renderer.pending_submits.set(id, pending);
img.queue_state.set(QueueState::Releasing); img.queue_state.set(QueueState::Releasing);
@ -375,14 +375,19 @@ impl VulkanShmImage {
.new_layout(transfer_layout); .new_layout(transfer_layout);
barriers.push(barrier); barriers.push(barrier);
let dep_info = DependencyInfo::default().image_memory_barriers(&barriers); let dep_info = DependencyInfo::default().image_memory_barriers(&barriers);
let release_fence = img.renderer.device.create_fence()?;
let dev = &img.renderer.device.device; let dev = &img.renderer.device.device;
let begin_info = let begin_info =
CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT); CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT);
let cmd = img.renderer.gfx_command_buffers.allocate()?; let cmd = img.renderer.gfx_command_buffers.allocate()?;
let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(cmd.buffer); 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)); SubmitInfo2::default().command_buffer_infos(slice::from_ref(&command_buffer_info));
let vulkan_sync = img.renderer.device.create_sync(
img.renderer.render_tls.as_ref(),
&mut semaphore_submit_info,
&mut submit_info,
)?;
unsafe { unsafe {
dev.begin_command_buffer(cmd.buffer, &begin_info) dev.begin_command_buffer(cmd.buffer, &begin_info)
.map_err(VulkanError::BeginCommandBuffer)?; .map_err(VulkanError::BeginCommandBuffer)?;
@ -392,23 +397,16 @@ impl VulkanShmImage {
dev.queue_submit2( dev.queue_submit2(
img.renderer.device.graphics_queue, img.renderer.device.graphics_queue,
slice::from_ref(&submit_info), slice::from_ref(&submit_info),
release_fence.fence, vulkan_sync.fence(),
) )
.inspect_err(img.renderer.device.idl()) .inspect_err(img.renderer.device.idl())
.map_err(VulkanError::Submit)?; .map_err(VulkanError::Submit)?;
} }
let sync_file = release_fence.export_sync_file()?; let sync = vulkan_sync.to_sync(|| img.renderer.block());
let id = img.renderer.allocate_point(); let id = img.renderer.allocate_point();
let pending = img.renderer.eng.spawn( let pending = img.renderer.eng.spawn(
"await_transfer_to_transfer", "await_transfer_to_transfer",
await_gfx_queue_release( await_gfx_queue_release(id, img.clone(), Some(cmd), Some(vulkan_sync), sync, tt),
id,
img.clone(),
Some(cmd),
Some(release_fence),
sync_file,
tt,
),
); );
img.renderer.pending_submits.set(id, pending); img.renderer.pending_submits.set(id, pending);
img.queue_state.set(QueueState::Releasing); img.queue_state.set(QueueState::Releasing);
@ -555,7 +553,7 @@ impl VulkanShmImage {
(host_buffer.buffer, host_buffer.size, true) (host_buffer.buffer, host_buffer.size, true)
} }
}; };
let (cmd, fence, sync_file, point) = self.submit_buffer_image_copy( let (cmd, vulkan_sync, sync, point) = self.submit_buffer_image_copy(
img, img,
buffer, buffer,
size, size,
@ -571,8 +569,8 @@ impl VulkanShmImage {
point, point,
img.clone(), img.clone(),
cmd, cmd,
fence, vulkan_sync,
sync_file, sync,
TransferType::Upload, TransferType::Upload,
), ),
); );
@ -590,7 +588,7 @@ impl VulkanShmImage {
return Ok(()); return Ok(());
} }
img.renderer.check_defunct()?; img.renderer.check_defunct()?;
let (cmd, fence, sync_file, point) = self.submit_buffer_image_copy( let (cmd, vulkan_sync, sync, point) = self.submit_buffer_image_copy(
img, img,
staging.buffer, staging.buffer,
staging.size, staging.size,
@ -606,8 +604,8 @@ impl VulkanShmImage {
point, point,
img.clone(), img.clone(),
cmd, cmd,
fence, vulkan_sync,
sync_file, sync,
TransferType::Download, TransferType::Download,
), ),
); );
@ -695,12 +693,12 @@ async fn await_gfx_queue_release(
id: u64, id: u64,
img: Rc<VulkanImage>, img: Rc<VulkanImage>,
buf: Option<Rc<VulkanCommandBuffer>>, buf: Option<Rc<VulkanCommandBuffer>>,
_fence: Option<Rc<VulkanFence>>, vulkan_sync: Option<VulkanSync>,
sync_file: Option<SyncFile>, sync: Option<FdSync>,
tt: TransferType, tt: TransferType,
) { ) {
if let Some(sync_file) = sync_file if let Some(sync) = &sync
&& let Err(e) = img.renderer.ring.readable(&sync_file.0).await && let Err(e) = sync.try_signaled(&img.renderer.ring).await
{ {
log::error!( log::error!(
"Could not wait for sync file to become readable: {}", "Could not wait for sync file to become readable: {}",
@ -708,6 +706,9 @@ async fn await_gfx_queue_release(
); );
img.renderer.block(); img.renderer.block();
} }
if let Some(vs) = vulkan_sync {
vs.handle_validation();
}
if let Some(buf) = buf { if let Some(buf) = buf {
img.renderer.gfx_command_buffers.buffers.push(buf); img.renderer.gfx_command_buffers.buffers.push(buf);
} }
@ -738,12 +739,12 @@ pub async fn await_async_transfer_release_to_gfx(
id: u64, id: u64,
img: Rc<VulkanImage>, img: Rc<VulkanImage>,
buf: Rc<VulkanCommandBuffer>, buf: Rc<VulkanCommandBuffer>,
_fence: Rc<VulkanFence>, vulkan_sync: VulkanSync,
sync_file: Option<SyncFile>, sync: Option<FdSync>,
tt: TransferType, tt: TransferType,
) { ) {
if let Some(sync_file) = sync_file if let Some(sync) = &sync
&& let Err(e) = img.renderer.ring.readable(&sync_file.0).await && let Err(e) = sync.try_signaled(&img.renderer.ring).await
{ {
log::error!( log::error!(
"Could not wait for sync file to become readable: {}", "Could not wait for sync file to become readable: {}",
@ -751,6 +752,7 @@ pub async fn await_async_transfer_release_to_gfx(
); );
img.renderer.block(); img.renderer.block();
} }
vulkan_sync.handle_validation();
match &img.renderer.transfer_command_buffers { match &img.renderer.transfer_command_buffers {
Some(b) => b.buffers.push(buf), Some(b) => b.buffers.push(buf),
None => img.renderer.gfx_command_buffers.buffers.push(buf), None => img.renderer.gfx_command_buffers.buffers.push(buf),

View file

@ -3,8 +3,8 @@ use {
client::{Client, ClientError}, client::{Client, ClientError},
cmm::cmm_description::ColorDescription, cmm::cmm_description::ColorDescription,
gfx_api::{ gfx_api::{
AcquireSync, AsyncShmGfxTextureCallback, BufferResv, GfxError, GfxFramebuffer, AcquireSync, AsyncShmGfxTextureCallback, BufferResv, FdSync, GfxError, GfxFramebuffer,
GfxTexture, ReleaseSync, STAGING_DOWNLOAD, SyncFile, GfxTexture, ReleaseSync, STAGING_DOWNLOAD,
}, },
ifs::{ ifs::{
ext_image_capture_source_v1::ImageCaptureSource, ext_image_capture_source_v1::ImageCaptureSource,
@ -78,7 +78,7 @@ impl ExtImageCopyCaptureFrameV1 {
Rc<dyn GfxFramebuffer>, Rc<dyn GfxFramebuffer>,
AcquireSync, AcquireSync,
ReleaseSync, ReleaseSync,
) -> Result<Option<SyncFile>, GfxError>, ) -> Result<Option<FdSync>, GfxError>,
) -> Result<(), FrameFailureReason> { ) -> Result<(), FrameFailureReason> {
let Some(ctx) = self.client.state.render_ctx.get() else { let Some(ctx) = self.client.state.render_ctx.get() else {
return Err(FrameFailureReason::BufferConstraints); return Err(FrameFailureReason::BufferConstraints);
@ -183,7 +183,7 @@ impl ExtImageCopyCaptureFrameV1 {
Rc<dyn GfxFramebuffer>, Rc<dyn GfxFramebuffer>,
AcquireSync, AcquireSync,
ReleaseSync, ReleaseSync,
) -> Result<Option<SyncFile>, GfxError>, ) -> Result<Option<FdSync>, GfxError>,
) { ) {
match self.try_copy(on, size, f) { match self.try_copy(on, size, f) {
Ok(()) => self.session.status.set(FrameStatus::Captured), Ok(()) => self.session.status.set(FrameStatus::Captured),

View file

@ -30,8 +30,8 @@ use {
drm_feedback::DrmFeedback, drm_feedback::DrmFeedback,
fixed::Fixed, fixed::Fixed,
gfx_api::{ gfx_api::{
AlphaMode, AsyncShmGfxTexture, BufferResv, BufferResvUser, GfxError, GfxStagingBuffer, AlphaMode, AsyncShmGfxTexture, BufferResv, BufferResvUser, FdSync, GfxError,
ReleaseSync, SampleRect, SyncFile, GfxStagingBuffer, ReleaseSync, SampleRect,
}, },
ifs::{ ifs::{
color_management::wp_color_management_surface_feedback_v1::WpColorManagementSurfaceFeedbackV1, color_management::wp_color_management_surface_feedback_v1::WpColorManagementSurfaceFeedbackV1,
@ -215,7 +215,7 @@ impl NodeVisitorBase for SurfaceSendPreferredColorDescription {
pub struct SurfaceBuffer { pub struct SurfaceBuffer {
pub buffer: AttachedBuffer, pub buffer: AttachedBuffer,
sync_files: SmallMap<BufferResvUser, SyncFile, 1>, syncs: SmallMap<BufferResvUser, FdSync, 1>,
pub release_sync: ReleaseSync, pub release_sync: ReleaseSync,
release: Option<SyncobjRelease>, release: Option<SyncobjRelease>,
_surface_release: SmallVec<[SurfaceRelease; 1]>, _surface_release: SmallVec<[SurfaceRelease; 1]>,
@ -223,14 +223,16 @@ pub struct SurfaceBuffer {
impl Drop for SurfaceBuffer { impl Drop for SurfaceBuffer {
fn drop(&mut self) { fn drop(&mut self) {
let sync_files = self.sync_files.take(); let syncs = self.syncs.take();
if let Some(release) = &mut self.release { if let Some(release) = &mut self.release {
release.signal(Some(&sync_files)); release.signal(Some(&syncs));
return; return;
} }
if let Some(dmabuf) = &self.buffer.buf.client_dmabuf { if let Some(dmabuf) = &self.buffer.buf.client_dmabuf {
for (_, sync_file) in &sync_files { for (_, sync) in &syncs {
if let Err(e) = dmabuf.import_sync_file(DMA_BUF_SYNC_READ, sync_file) { if let Some(sf) = sync.get_sync_file()
&& let Err(e) = dmabuf.import_sync_file(DMA_BUF_SYNC_READ, &sf)
{
log::error!("Could not import sync file: {}", ErrorFmt(e)); log::error!("Could not import sync file: {}", ErrorFmt(e));
} }
} }
@ -245,8 +247,8 @@ impl Debug for SurfaceBuffer {
} }
impl BufferResv for SurfaceBuffer { impl BufferResv for SurfaceBuffer {
fn set_sync_file(&self, user: BufferResvUser, sync_file: &SyncFile) { fn set_sync(&self, user: BufferResvUser, sync: &FdSync) {
self.sync_files.insert(user, sync_file.clone()); self.syncs.insert(user, sync.clone());
} }
} }
@ -1231,7 +1233,7 @@ impl WlSurface {
}; };
let surface_buffer = SurfaceBuffer { let surface_buffer = SurfaceBuffer {
buffer, buffer,
sync_files: Default::default(), syncs: Default::default(),
release_sync, release_sync,
release: pending.release_point.take(), release: pending.release_point.take(),
_surface_release: mem::take(&mut pending.surface_release), _surface_release: mem::take(&mut pending.surface_release),
@ -2263,7 +2265,7 @@ pub struct SyncobjRelease {
} }
impl SyncobjRelease { impl SyncobjRelease {
fn signal(&mut self, sync_files: Option<&SmallVec<[(BufferResvUser, SyncFile); 1]>>) { fn signal(&mut self, syncs: Option<&SmallVec<[(BufferResvUser, FdSync); 1]>>) {
if !self.committed { if !self.committed {
return; return;
} }
@ -2278,10 +2280,14 @@ impl SyncobjRelease {
log::error!("Cannot signal release point because there is no syncobj context"); log::error!("Cannot signal release point because there is no syncobj context");
return; return;
}; };
if let Some(sync_files) = sync_files if let Some(syncs) = syncs
&& sync_files.is_not_empty() && syncs.is_not_empty()
{ {
let res = ctx.import_sync_files(&syncobj, self.point, sync_files.iter().map(|f| &f.1)); let res = ctx.import_sync_files(
&syncobj,
self.point,
syncs.iter().flat_map(|f| f.1.get_sync_file()),
);
match res { match res {
Ok(_) => return, Ok(_) => return,
Err(e) => { Err(e) => {

View file

@ -211,7 +211,10 @@ impl TestBackend {
}; };
} }
let udmabuf = ("udmabuf", constructor!(Udmabuf::new())); 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 gbm = ("GBM", constructor!(create_gbm_allocator()));
let allocators = match need_drm { let allocators = match need_drm {
true => [vulkan, gbm, udmabuf], true => [vulkan, gbm, udmabuf],
@ -243,9 +246,10 @@ fn create_gbm_allocator() -> Result<GbmDevice, TestBackendError> {
create_drm_allocator(|drm| GbmDevice::new(&drm).map_err(TestBackendError::CreateGbmDevice)) create_drm_allocator(|drm| GbmDevice::new(&drm).map_err(TestBackendError::CreateGbmDevice))
} }
fn create_vk_allocator() -> Result<Rc<dyn Allocator>, TestBackendError> { fn create_vk_allocator(state: &State) -> Result<Rc<dyn Allocator>, TestBackendError> {
create_drm_allocator(|drm| { create_drm_allocator(|drm| {
create_vulkan_allocator(&drm).map_err(TestBackendError::CreateVulkanAllocator) create_vulkan_allocator(&drm, &state.eventfd_cache)
.map_err(TestBackendError::CreateVulkanAllocator)
}) })
} }

View file

@ -96,7 +96,12 @@ impl TestClient {
pub async fn take_screenshot(&self, include_cursor: bool) -> Result<Vec<u8>, TestError> { pub async fn take_screenshot(&self, include_cursor: bool) -> Result<Vec<u8>, TestError> {
let (dmabuf, dev) = self.jc.take_screenshot(include_cursor).await?; 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) Ok(qoi)
} }

View file

@ -5,11 +5,11 @@ use {
cpu_worker::CpuWorker, cpu_worker::CpuWorker,
format::{ARGB8888, Format, XRGB8888}, format::{ARGB8888, Format, XRGB8888},
gfx_api::{ gfx_api::{
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback, CopyTexture, FillRect, AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback, CopyTexture, FdSync,
FramebufferRect, GfxApi, GfxApiOpt, GfxBlendBuffer, GfxContext, GfxError, GfxFormat, FillRect, FramebufferRect, GfxApi, GfxApiOpt, GfxBlendBuffer, GfxContext, GfxError,
GfxFramebuffer, GfxImage, GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture, GfxFormat, GfxFramebuffer, GfxImage, GfxInternalFramebuffer, GfxStagingBuffer,
GfxWriteModifier, PendingShmTransfer, ReleaseSync, ResetStatus, ShmGfxTexture, GfxTexture, GfxWriteModifier, PendingShmTransfer, ReleaseSync, ResetStatus,
ShmMemory, SyncFile, ShmGfxTexture, ShmMemory,
}, },
rect::{Rect, Region}, rect::{Rect, Region},
theme::Color, theme::Color,
@ -387,7 +387,7 @@ impl GfxFramebuffer for TestGfxFb {
_region: &Region, _region: &Region,
_blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>, _blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
_blend_cd: &Rc<ColorDescription>, _blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> { ) -> Result<Option<FdSync>, GfxError> {
let fb_points = |width: i32, height: i32, rect: &FramebufferRect| { let fb_points = |width: i32, height: i32, rect: &FramebufferRect| {
let points = rect.to_points(); let points = rect.to_points();
let x1 = points[1][0]; let x1 = points[1][0];

View file

@ -68,6 +68,7 @@ mod dbus;
mod drm_feedback; mod drm_feedback;
mod edid; mod edid;
mod ei; mod ei;
mod eventfd_cache;
mod fixed; mod fixed;
mod forker; mod forker;
mod format; mod format;

View file

@ -15,6 +15,7 @@ use {
BUS_DEST, BUS_PATH, DBUS_NAME_FLAG_DO_NOT_QUEUE, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER, BUS_DEST, BUS_PATH, DBUS_NAME_FLAG_DO_NOT_QUEUE, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER,
Dbus, DbusSocket, Dbus, DbusSocket,
}, },
eventfd_cache::EventfdCache,
forker::ForkerError, forker::ForkerError,
io_uring::IoUring, io_uring::IoUring,
logger::Logger, logger::Logger,
@ -242,9 +243,11 @@ async fn run_async(
None None
} }
}; };
let eventfd_cache = EventfdCache::new(&ring, &eng);
let state = Rc::new(PortalState { let state = Rc::new(PortalState {
xrd, xrd,
ring, ring,
eventfd_cache,
eng, eng,
wheel, wheel,
displays: Default::default(), displays: Default::default(),
@ -324,6 +327,7 @@ async fn init_dbus_session(dbus: &Dbus, logger: Arc<Logger>, path_sink: OwnedFd)
struct PortalState { struct PortalState {
xrd: String, xrd: String,
ring: Rc<IoUring>, ring: Rc<IoUring>,
eventfd_cache: Rc<EventfdCache>,
eng: Rc<AsyncEngine>, eng: Rc<AsyncEngine>,
wheel: Rc<Wheel>, wheel: Rc<Wheel>,
displays: CopyHashMap<PortalDisplayId, Rc<PortalDisplay>>, displays: CopyHashMap<PortalDisplayId, Rc<PortalDisplay>>,

View file

@ -187,6 +187,7 @@ impl UsrJayRenderCtxOwner for PortalDisplay {
let ctx = match create_gfx_context( let ctx = match create_gfx_context(
&self.state.eng, &self.state.eng,
&self.state.ring, &self.state.ring,
&self.state.eventfd_cache,
&drm, &drm,
GfxApi::OpenGl, GfxApi::OpenGl,
None, None,

View file

@ -27,13 +27,14 @@ use {
ei_acceptor::EiAcceptor, ei_acceptor::EiAcceptor,
ei_client::{EiClient, EiClients}, ei_client::{EiClient, EiClients},
}, },
eventfd_cache::EventfdCache,
fixed::Fixed, fixed::Fixed,
forker::ForkerProxy, forker::ForkerProxy,
format::Format, format::Format,
gfx_api::{ gfx_api::{
AcquireSync, AlphaMode, BufferResv, GfxApi, GfxBlendBuffer, GfxContext, GfxError, AcquireSync, AlphaMode, BufferResv, FdSync, GfxApi, GfxBlendBuffer, GfxContext,
GfxFramebuffer, GfxTexture, PendingShmTransfer, ReleaseSync, STAGING_DOWNLOAD, GfxError, GfxFramebuffer, GfxTexture, PendingShmTransfer, ReleaseSync,
SampleRect, SyncFile, STAGING_DOWNLOAD, SampleRect,
}, },
gfx_apis::create_gfx_context, gfx_apis::create_gfx_context,
globals::{Globals, GlobalsError, RemovableWaylandGlobal, WaylandGlobal}, globals::{Globals, GlobalsError, RemovableWaylandGlobal, WaylandGlobal},
@ -288,6 +289,7 @@ pub struct State {
pub gfx_ctx_changed: EventSource<WlBuffer>, pub gfx_ctx_changed: EventSource<WlBuffer>,
pub copy_device_registry: Rc<CopyDeviceRegistry>, pub copy_device_registry: Rc<CopyDeviceRegistry>,
pub supports_presentation_feedback: Cell<bool>, pub supports_presentation_feedback: Cell<bool>,
pub eventfd_cache: Rc<EventfdCache>,
} }
// impl Drop for State { // impl Drop for State {
@ -555,6 +557,7 @@ impl State {
create_gfx_context( create_gfx_context(
&self.eng, &self.eng,
&self.ring, &self.ring,
&self.eventfd_cache,
drm, drm,
api.unwrap_or(self.default_gfx_api.get()), api.unwrap_or(self.default_gfx_api.get()),
self.caps_thread.as_ref(), self.caps_thread.as_ref(),
@ -1187,8 +1190,8 @@ impl State {
render_hw_cursor: bool, render_hw_cursor: bool,
blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>, blend_buffer: Option<&Rc<dyn GfxBlendBuffer>>,
blend_cd: &Rc<ColorDescription>, blend_cd: &Rc<ColorDescription>,
) -> Result<Option<SyncFile>, GfxError> { ) -> Result<Option<FdSync>, GfxError> {
let sync_file = fb.render_output( let sync = fb.render_output(
acquire_sync, acquire_sync,
release_sync, release_sync,
cd, cd,
@ -1213,7 +1216,7 @@ impl State {
0, 0,
None, None,
); );
Ok(sync_file) Ok(sync)
} }
pub fn perform_screencopy( pub fn perform_screencopy(
@ -1235,7 +1238,7 @@ impl State {
size: Option<(i32, i32)>, size: Option<(i32, i32)>,
transform: Transform, transform: Transform,
scale: Scale, scale: Scale,
) -> Result<Option<SyncFile>, GfxError> { ) -> Result<Option<FdSync>, GfxError> {
let mut ops = vec![]; let mut ops = vec![];
let mut renderer = Renderer { let mut renderer = Renderer {
base: target.renderer_base(&mut ops, scale, target_transform), base: target.renderer_base(&mut ops, scale, target_transform),

View file

@ -42,4 +42,9 @@ impl<T> Stack<T> {
pub fn take(&self) -> Vec<T> { pub fn take(&self) -> Vec<T> {
unsafe { mem::take(self.vec.get().deref_mut()) } unsafe { mem::take(self.vec.get().deref_mut()) }
} }
#[cfg_attr(not(test), expect(dead_code))]
pub fn len(&self) -> usize {
unsafe { self.vec.get().deref().len() }
}
} }

View file

@ -1,21 +1,33 @@
pub mod syncobj; pub mod syncobj;
mod sys; mod sys;
pub mod wait_for_syncobj; pub mod wait_for_syncobj;
pub use consts::*;
use { use {
crate::{ crate::{
utils::oserror::OsError, backend,
video::drm::sys::{ format::Format,
DRM_DISPLAY_MODE_LEN, DRM_MODE_ATOMIC_TEST_ONLY, DRM_MODE_FB_MODIFIERS, io_uring::{IoUring, IoUringError},
DRM_MODE_OBJECT_BLOB, DRM_MODE_OBJECT_CONNECTOR, DRM_MODE_OBJECT_CRTC, utils::{
DRM_MODE_OBJECT_ENCODER, DRM_MODE_OBJECT_FB, DRM_MODE_OBJECT_PLANE, buf::Buf, errorfmt::ErrorFmt, oserror::OsError, stack::Stack, syncqueue::SyncQueue,
DRM_MODE_OBJECT_PROPERTY, create_lease, drm_event, drm_event_vblank, gem_close, vec_ext::VecExt,
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, video::{
mode_get_resources, mode_getconnector, mode_getencoder, mode_getplane, INVALID_MODIFIER, Modifier,
mode_getplaneresources, mode_getprobblob, mode_getproperty, mode_obj_getproperties, dmabuf::DmaBuf,
mode_rmfb, prime_fd_to_handle, set_client_cap, 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, ahash::AHashMap,
@ -32,26 +44,14 @@ use {
thiserror::Error, thiserror::Error,
uapi::{OwnedFd, Pod, Ustring, c}, uapi::{OwnedFd, Pod, Ustring, c},
}; };
pub use {
use crate::{ consts::*,
backend, sys::{
format::Format, DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET, DRM_MODE_ATOMIC_NONBLOCK,
io_uring::{IoUring, IoUringError}, DRM_MODE_PAGE_FLIP_ASYNC, DRM_MODE_PAGE_FLIP_EVENT, drm_mode_modeinfo,
utils::{buf::Buf, errorfmt::ErrorFmt, stack::Stack, syncqueue::SyncQueue, vec_ext::VecExt}, get_drm_nodes_from_dev,
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 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)] #[derive(Debug, Error)]
pub enum DrmError { pub enum DrmError {
@ -123,6 +123,8 @@ pub enum DrmError {
CreateSyncobj(#[source] OsError), CreateSyncobj(#[source] OsError),
#[error("Could not export a syncobj")] #[error("Could not export a syncobj")]
ExportSyncobj(#[source] OsError), ExportSyncobj(#[source] OsError),
#[error("Could not query a syncobj")]
QuerySyncobj(#[source] OsError),
#[error("Could not register an eventfd with a syncobj")] #[error("Could not register an eventfd with a syncobj")]
RegisterEventfd(#[source] OsError), RegisterEventfd(#[source] OsError),
#[error("Could not create an eventfd")] #[error("Could not create an eventfd")]
@ -139,6 +141,8 @@ pub enum DrmError {
Merge(#[source] OsError), Merge(#[source] OsError),
#[error("Could not import a sync file into a syncobj")] #[error("Could not import a sync file into a syncobj")]
ImportSyncFile(#[source] OsError), ImportSyncFile(#[source] OsError),
#[error("Could not export a sync file")]
ExportSyncFile(#[source] OsError),
#[error("Could not create a lease")] #[error("Could not create a lease")]
CreateLease(#[source] OsError), CreateLease(#[source] OsError),
#[error("Could not drop DRM master")] #[error("Could not drop DRM master")]

View file

@ -15,9 +15,10 @@ use {
DRM_SYNCOBJ_CREATE_SIGNALED, DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE, DRM_SYNCOBJ_CREATE_SIGNALED, DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE,
DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_TIMELINE, DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_TIMELINE,
DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE, DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE,
DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE, sync_ioc_merge, syncobj_create, 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_destroy, syncobj_eventfd, syncobj_fd_to_handle, syncobj_handle_to_fd,
syncobj_signal, syncobj_transfer, syncobj_query, syncobj_signal, syncobj_transfer,
}, },
}, },
}, },
@ -61,6 +62,10 @@ impl Syncobj {
pub fn fd(&self) -> &Rc<OwnedFd> { pub fn fd(&self) -> &Rc<OwnedFd> {
&self.fd &self.fd
} }
pub fn id(&self) -> SyncobjId {
self.id
}
} }
impl Drop for Syncobj { impl Drop for Syncobj {
@ -118,7 +123,7 @@ impl SyncobjCtx {
pub fn create_syncobj(&self) -> Result<Syncobj, DrmError> { pub fn create_syncobj(&self) -> Result<Syncobj, DrmError> {
let handle = syncobj_create(self.inner.drm.raw(), 0).map_err(DrmError::CreateSyncobj)?; let handle = syncobj_create(self.inner.drm.raw(), 0).map_err(DrmError::CreateSyncobj)?;
let handle = SyncobjHandle(handle); let handle = SyncobjHandle(handle);
let fd = syncobj_handle_to_fd(self.inner.drm.raw(), handle.0, 0); let fd = syncobj_handle_to_fd(self.inner.drm.raw(), handle.0, 0, 0);
if fd.is_err() { if fd.is_err() {
destroy(&self.inner.drm, handle); destroy(&self.inner.drm, handle);
} }
@ -138,6 +143,7 @@ impl SyncobjCtx {
self.inner.drm.raw(), self.inner.drm.raw(),
handle.0, handle.0,
DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE, DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE,
0,
); );
destroy(&self.inner.drm, handle); destroy(&self.inner.drm, handle);
fd.map_err(DrmError::ExportSyncobj) fd.map_err(DrmError::ExportSyncobj)
@ -250,7 +256,46 @@ impl SyncobjCtx {
} }
let dummy = self.get_dummy()?; let dummy = self.get_dummy()?;
import(0, self.get_handle(&dummy)?)?; import(0, self.get_handle(&dummy)?)?;
self.transfer(&dummy, SyncobjPoint(0), syncobj, point) self.transfer(&dummy, SyncobjPoint(0), syncobj, point, 0)
}
pub fn export_sync_file_blocking(
&self,
syncobj: &Syncobj,
point: SyncobjPoint,
) -> Result<SyncFile, DrmError> {
let export = |flags: u32, handle: SyncobjHandle, point: SyncobjPoint| {
syncobj_handle_to_fd(
self.inner.drm.raw(),
handle.0,
DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE | flags,
point.0,
)
.map(Rc::new)
.map(SyncFile)
};
if self.supports_timeline_import() {
let res = export(
DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_TIMELINE,
self.get_handle(syncobj)?,
point,
);
match res {
Ok(sf) => return Ok(sf),
Err(e) if e.0 == c::EINVAL => {}
Err(e) => return Err(DrmError::ExportSyncFile(e)),
}
}
let dummy = self.get_dummy()?;
let zero = SyncobjPoint(0);
self.transfer(
syncobj,
point,
&dummy,
zero,
DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT,
)?;
export(0, self.get_handle(&dummy)?, zero).map_err(DrmError::ExportSyncFile)
} }
fn transfer( fn transfer(
@ -259,6 +304,7 @@ impl SyncobjCtx {
src_point: SyncobjPoint, src_point: SyncobjPoint,
dst_syncobj: &Syncobj, dst_syncobj: &Syncobj,
dst_point: SyncobjPoint, dst_point: SyncobjPoint,
flags: u32,
) -> Result<(), DrmError> { ) -> Result<(), DrmError> {
let src_handle = self.get_handle(src_syncobj)?; let src_handle = self.get_handle(src_syncobj)?;
let dst_handle = self.get_handle(dst_syncobj)?; let dst_handle = self.get_handle(dst_syncobj)?;
@ -268,7 +314,7 @@ impl SyncobjCtx {
src_point.0, src_point.0,
dst_handle.0, dst_handle.0,
dst_point.0, dst_point.0,
0, flags,
) )
.map_err(DrmError::TransferPoint) .map_err(DrmError::TransferPoint)
} }
@ -283,6 +329,11 @@ impl SyncobjCtx {
} }
} }
} }
pub fn query_last_signaled(&self, syncobj: &Syncobj) -> Result<u64, DrmError> {
let handle = self.get_handle(syncobj)?;
syncobj_query(self.inner.drm.raw(), handle.0).map_err(DrmError::QuerySyncobj)
}
} }
impl Drop for SyncobjCtx { impl Drop for SyncobjCtx {

View file

@ -171,7 +171,10 @@ pub fn get_device_name_from_fd2(fd: c::c_int) -> Result<Ustring, OsError> {
pub fn get_nodes(fd: c::c_int) -> Result<AHashMap<NodeType, CString>, OsError> { pub fn get_nodes(fd: c::c_int) -> Result<AHashMap<NodeType, CString>, OsError> {
let (_, maj, min) = drm_stat(fd)?; 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<AHashMap<NodeType, CString>, OsError> {
let dir = device_dir(maj, min); let dir = device_dir(maj, min);
let mut dir = uapi::opendir(dir)?; let mut dir = uapi::opendir(dir)?;
@ -1223,6 +1226,7 @@ pub const DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE: u32 = 1 << 0;
pub const DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_TIMELINE: u32 = 1 << 1; pub const DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_TIMELINE: u32 = 1 << 1;
pub const DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE: u32 = 1 << 0; pub const DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE: u32 = 1 << 0;
pub const DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_TIMELINE: u32 = 1 << 1;
#[repr(C)] #[repr(C)]
struct drm_syncobj_handle { struct drm_syncobj_handle {
@ -1236,13 +1240,18 @@ struct drm_syncobj_handle {
const DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD: u64 = drm_iowr::<drm_syncobj_handle>(0xC1); const DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD: u64 = drm_iowr::<drm_syncobj_handle>(0xC1);
const DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE: u64 = drm_iowr::<drm_syncobj_handle>(0xC2); const DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE: u64 = drm_iowr::<drm_syncobj_handle>(0xC2);
pub fn syncobj_handle_to_fd(drm: c::c_int, handle: u32, flags: u32) -> Result<OwnedFd, OsError> { pub fn syncobj_handle_to_fd(
drm: c::c_int,
handle: u32,
flags: u32,
point: u64,
) -> Result<OwnedFd, OsError> {
let mut res = drm_syncobj_handle { let mut res = drm_syncobj_handle {
handle, handle,
flags, flags,
fd: 0, fd: 0,
pad: 0, pad: 0,
point: 0, point,
}; };
unsafe { unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &mut res)?; ioctl(drm, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &mut res)?;
@ -1271,7 +1280,7 @@ pub fn syncobj_fd_to_handle(
} }
// pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL: u32 = 1 << 0; // pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL: u32 = 1 << 0;
// pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT: u32 = 1 << 1; pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT: u32 = 1 << 1;
pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE: u32 = 1 << 2; pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE: u32 = 1 << 2;
#[repr(C)] #[repr(C)]
@ -1328,6 +1337,22 @@ pub fn syncobj_signal(drm: c::c_int, handle: u32, point: u64) -> Result<(), OsEr
Ok(()) Ok(())
} }
const DRM_IOCTL_SYNCOBJ_QUERY: u64 = drm_iowr::<drm_syncobj_timeline_array>(0xCB);
pub fn syncobj_query(drm: c::c_int, handle: u32) -> Result<u64, OsError> {
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)] #[repr(C)]
struct drm_syncobj_transfer { struct drm_syncobj_transfer {
src_handle: u32, src_handle: u32,

View file

@ -1,4 +1,5 @@
use { use {
crate::{eventfd_cache::EventfdError, video::drm::DrmError},
ahash::{AHashMap, AHashSet}, ahash::{AHashMap, AHashSet},
ash::{ ash::{
Entry, Instance, LoadingError, Entry, Instance, LoadingError,
@ -6,9 +7,13 @@ use {
vk::{ vk::{
self, API_VERSION_1_3, ApplicationInfo, Bool32, DebugUtilsMessageSeverityFlagsEXT, self, API_VERSION_1_3, ApplicationInfo, Bool32, DebugUtilsMessageSeverityFlagsEXT,
DebugUtilsMessageTypeFlagsEXT, DebugUtilsMessengerCallbackDataEXT, DebugUtilsMessageTypeFlagsEXT, DebugUtilsMessengerCallbackDataEXT,
DebugUtilsMessengerCreateInfoEXT, DebugUtilsMessengerEXT, ExtensionProperties, FALSE, DebugUtilsMessengerCreateInfoEXT, DebugUtilsMessengerEXT, ExtensionProperties,
InstanceCreateInfo, LayerProperties, ValidationFeaturesEXT, api_version_major, ExternalSemaphoreFeatureFlags, ExternalSemaphoreHandleTypeFlags,
api_version_minor, api_version_patch, api_version_variant, 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, isnt::std_1::collections::IsntHashMapExt,
@ -25,6 +30,11 @@ use {
uapi::{Ustr, ustr}, uapi::{Ustr, ustr},
}; };
pub mod device;
pub mod fence;
pub mod sync;
pub mod timeline_semaphore;
static VULKAN_ENTRY: Lazy<Result<Entry, Arc<LoadingError>>> = static VULKAN_ENTRY: Lazy<Result<Entry, Arc<LoadingError>>> =
Lazy::new(|| unsafe { Entry::load() }.map_err(Arc::new)); Lazy::new(|| unsafe { Entry::load() }.map_err(Arc::new));
@ -45,6 +55,26 @@ pub enum VulkanCoreError {
CreateInstance(#[source] vk::Result), CreateInstance(#[source] vk::Result),
#[error("Could not create a debug-utils messenger")] #[error("Could not create a debug-utils messenger")]
Messenger(#[source] vk::Result), Messenger(#[source] vk::Result),
#[error("Could not create a fence")]
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 { pub struct VulkanCoreInstance {
@ -53,6 +83,13 @@ pub struct VulkanCoreInstance {
debug_utils: debug_utils::Instance, debug_utils: debug_utils::Instance,
messenger: DebugUtilsMessengerEXT, messenger: DebugUtilsMessengerEXT,
pub log_level: Level, pub log_level: Level,
pub validation: bool,
}
pub struct VulkanDeviceFeatures {
#[expect(dead_code)]
pub features: PhysicalDeviceFeatures,
pub semaphore_features: PhysicalDeviceTimelineSemaphoreFeatures<'static>,
} }
impl VulkanCoreInstance { impl VulkanCoreInstance {
@ -78,7 +115,8 @@ impl VulkanCoreInstance {
let mut severity = DebugUtilsMessageSeverityFlagsEXT::empty() let mut severity = DebugUtilsMessageSeverityFlagsEXT::empty()
| DebugUtilsMessageSeverityFlagsEXT::ERROR | DebugUtilsMessageSeverityFlagsEXT::ERROR
| DebugUtilsMessageSeverityFlagsEXT::WARNING; | DebugUtilsMessageSeverityFlagsEXT::WARNING;
if *VULKAN_VALIDATION { let validation = *VULKAN_VALIDATION;
if validation {
severity |= DebugUtilsMessageSeverityFlagsEXT::INFO severity |= DebugUtilsMessageSeverityFlagsEXT::INFO
| DebugUtilsMessageSeverityFlagsEXT::VERBOSE; | DebugUtilsMessageSeverityFlagsEXT::VERBOSE;
} }
@ -102,7 +140,7 @@ impl VulkanCoreInstance {
.application_info(&app_info) .application_info(&app_info)
.push_next(&mut debug_info); .push_next(&mut debug_info);
let validation_layer_name = VALIDATION_LAYER.as_ptr(); let validation_layer_name = VALIDATION_LAYER.as_ptr();
if *VULKAN_VALIDATION { if validation {
if get_available_layers(entry)?.contains(VALIDATION_LAYER) { if get_available_layers(entry)?.contains(VALIDATION_LAYER) {
create_info = create_info =
create_info.enabled_layer_names(slice::from_ref(&validation_layer_name)); create_info.enabled_layer_names(slice::from_ref(&validation_layer_name));
@ -138,8 +176,49 @@ impl VulkanCoreInstance {
debug_utils, debug_utils,
messenger, messenger,
log_level, 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 { impl Drop for VulkanCoreInstance {

21
src/vulkan_core/device.rs Normal file
View file

@ -0,0 +1,21 @@
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<SyncobjCtx>;
fn eventfd_cache(&self) -> &Rc<EventfdCache>;
}

View file

@ -1,7 +1,7 @@
use { use {
crate::{ crate::{
gfx_api::SyncFile, gfx_api::SyncFile,
gfx_apis::vulkan::{VulkanError, device::VulkanDevice}, vulkan_core::{VulkanCoreError, device::VulkanDeviceInf},
}, },
ash::vk::{ ash::vk::{
ExportFenceCreateInfo, ExternalFenceHandleTypeFlags, Fence, FenceCreateInfo, ExportFenceCreateInfo, ExternalFenceHandleTypeFlags, Fence, FenceCreateInfo,
@ -11,27 +11,40 @@ use {
uapi::OwnedFd, uapi::OwnedFd,
}; };
pub struct VulkanFence { pub struct VulkanFence<D>
pub(super) device: Rc<VulkanDevice>, where
pub(super) fence: Fence, D: VulkanDeviceInf,
{
pub device: Rc<D>,
pub fence: Fence,
} }
impl Drop for VulkanFence { pub trait VulkanDeviceFenceExt: VulkanDeviceInf {
fn create_fence(self: &Rc<Self>) -> Result<Rc<VulkanFence<Self>>, VulkanCoreError>;
}
impl<D> Drop for VulkanFence<D>
where
D: VulkanDeviceInf,
{
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
self.device.device.destroy_fence(self.fence, None); self.device.device().destroy_fence(self.fence, None);
} }
} }
} }
impl VulkanDevice { impl<D> VulkanDeviceFenceExt for D
pub fn create_fence(self: &Rc<Self>) -> Result<Rc<VulkanFence>, VulkanError> { where
D: VulkanDeviceInf,
{
fn create_fence(self: &Rc<Self>) -> Result<Rc<VulkanFence<Self>>, VulkanCoreError> {
let fence = { let fence = {
let mut export_info = ExportFenceCreateInfo::default() let mut export_info = ExportFenceCreateInfo::default()
.handle_types(ExternalFenceHandleTypeFlags::SYNC_FD); .handle_types(ExternalFenceHandleTypeFlags::SYNC_FD);
let create_info = FenceCreateInfo::default().push_next(&mut export_info); let create_info = FenceCreateInfo::default().push_next(&mut export_info);
let fence = unsafe { self.device.create_fence(&create_info, None) }; let fence = unsafe { self.device().create_fence(&create_info, None) };
fence.map_err(VulkanError::CreateFence)? fence.map_err(VulkanCoreError::CreateFence)?
}; };
Ok(Rc::new(VulkanFence { Ok(Rc::new(VulkanFence {
device: self.clone(), device: self.clone(),
@ -40,13 +53,16 @@ impl VulkanDevice {
} }
} }
impl VulkanFence { impl<D> VulkanFence<D>
pub fn export_sync_file(&self) -> Result<Option<SyncFile>, VulkanError> { where
D: VulkanDeviceInf,
{
pub fn export_sync_file(&self) -> Result<Option<SyncFile>, VulkanCoreError> {
let info = FenceGetFdInfoKHR::default() let info = FenceGetFdInfoKHR::default()
.fence(self.fence) .fence(self.fence)
.handle_type(ExternalFenceHandleTypeFlags::SYNC_FD); .handle_type(ExternalFenceHandleTypeFlags::SYNC_FD);
let res = unsafe { self.device.external_fence_fd.get_fence_fd(&info) }; let res = unsafe { self.device.external_fence_fd().get_fence_fd(&info) };
let fd = res.map_err(VulkanError::ExportSyncFile)?; let fd = res.map_err(VulkanCoreError::ExportSyncFile)?;
if fd == -1 { if fd == -1 {
Ok(None) Ok(None)
} else { } else {

136
src/vulkan_core/sync.rs Normal file
View file

@ -0,0 +1,136 @@
use {
crate::{
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, PipelineStageFlags2, SemaphoreSubmitInfo, SemaphoreWaitInfo, SubmitInfo2},
std::{rc::Rc, slice},
};
pub enum VulkanSync<D>
where
D: VulkanDeviceInf,
{
Fence(Rc<VulkanFence<D>>),
TimelineSemaphore {
tls: Rc<VulkanTimelineSemaphore<D>>,
pending: Rc<ReservedSyncobjPoint>,
},
}
impl<D> VulkanSync<D>
where
D: VulkanDeviceInf,
{
pub fn handle_validation(&self) {
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(),
}
}
pub fn to_sync(&self, block: impl FnOnce()) -> Option<FdSync> {
match self {
VulkanSync::Fence(release_fence) => {
zone!("export_sync_file");
let release_sync_file = match release_fence.export_sync_file() {
Ok(s) => s,
Err(e) => {
log::error!("Could not export sync file from fence: {}", ErrorFmt(e));
block();
None
}
};
release_sync_file.map(FdSync::SyncFile)
}
VulkanSync::TimelineSemaphore { pending, .. } => Some(FdSync::Syncobj(pending.clone())),
}
}
}
pub trait VulkanDeviceSyncExt: VulkanDeviceInf {
fn create_sync<'a>(
self: &Rc<Self>,
tls: Option<&Rc<VulkanTimelineSemaphore<Self>>>,
semaphore_submit_info: &'a mut SemaphoreSubmitInfo,
submit_info: &mut SubmitInfo2<'a>,
) -> Result<VulkanSync<Self>, VulkanCoreError>;
}
impl<D> VulkanDeviceSyncExt for D
where
D: VulkanDeviceInf,
{
fn create_sync<'a>(
self: &Rc<Self>,
tls: Option<&Rc<VulkanTimelineSemaphore<Self>>>,
semaphore_submit_info: &'a mut SemaphoreSubmitInfo,
submit_info: &mut SubmitInfo2<'a>,
) -> Result<VulkanSync<Self>, 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<D>,
tls: &Rc<VulkanTimelineSemaphore<D>>,
semaphore_submit_info: &'a mut SemaphoreSubmitInfo,
submit_info: &mut SubmitInfo2<'a>,
) -> Result<VulkanSync<D>, 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,
})
}

View file

@ -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<D>
where
D: VulkanDeviceInf,
{
pub(super) device: Rc<D>,
pub(super) semaphore: Semaphore,
pub(super) syncobj: Rc<Syncobj>,
pub(super) next_point: NumCell<u64>,
}
impl<D> Drop for VulkanTimelineSemaphore<D>
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<Self>,
) -> Result<Rc<VulkanTimelineSemaphore<Self>>, VulkanCoreError>;
fn create_timeline_semaphore_or_log(
self: &Rc<Self>,
) -> Option<Rc<VulkanTimelineSemaphore<Self>>>;
}
impl<D> VulkanDeviceTimelineSemaphoreExt for D
where
D: VulkanDeviceInf,
{
fn create_timeline_semaphore(
self: &Rc<Self>,
) -> Result<Rc<VulkanTimelineSemaphore<Self>>, 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<Self>,
) -> Option<Rc<VulkanTimelineSemaphore<Self>>> {
self.create_timeline_semaphore()
.inspect_err(|e| {
log::warn!("Could not create timeline semaphore: {}", ErrorFmt(e));
})
.ok()
}
}