1
0
Fork 0
forked from wry/wry

vulkan: create high-priority queues if possible

This commit is contained in:
Julian Orth 2025-05-10 22:44:00 +02:00
parent 7a623006e2
commit 68713b2e39
9 changed files with 253 additions and 44 deletions

View file

@ -39,7 +39,7 @@ use {
logger::Logger,
output_schedule::OutputSchedule,
portal::{self, PortalStartup},
pr_caps::pr_caps,
pr_caps::{PrCapsThread, pr_caps},
scale::Scale,
sighand::{self, SighandError},
state::{ConnectorData, IdleState, ScreenlockState, State, XWaylandState},
@ -82,10 +82,13 @@ pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
sighand::reset_all();
let reaper_pid = ensure_reaper();
let caps = pr_caps().into_comp();
if caps.has_nice() {
let caps_thread = if caps.has_nice() {
elevate_scheduler();
}
drop(caps);
Some(caps.into_thread())
} else {
drop(caps);
None
};
let forker = create_forker(reaper_pid);
let portal = portal::run_from_compositor(global.log_level.into());
enable_profiler();
@ -97,7 +100,14 @@ pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
None
}
};
let res = start_compositor2(Some(forker), portal, Some(logger.clone()), args, None);
let res = start_compositor2(
Some(forker),
portal,
Some(logger.clone()),
args,
None,
caps_thread,
);
leaks::log_leaked();
if let Err(e) = res {
let e = ErrorFmt(e);
@ -111,7 +121,7 @@ pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
#[cfg(feature = "it")]
pub fn start_compositor_for_test(future: TestFuture) -> Result<(), CompositorError> {
let res = start_compositor2(None, None, None, RunArgs::default(), Some(future));
let res = start_compositor2(None, None, None, RunArgs::default(), Some(future), None);
leaks::log_leaked();
res
}
@ -157,6 +167,7 @@ fn start_compositor2(
logger: Option<Arc<Logger>>,
run_args: RunArgs,
test_future: Option<TestFuture>,
caps_thread: Option<PrCapsThread>,
) -> Result<(), CompositorError> {
log::info!("pid = {}", uapi::getpid());
log::info!("version = {VERSION}");
@ -321,6 +332,7 @@ fn start_compositor2(
show_pin_icon: Cell::new(false),
cl_matcher_manager: ClMatcherManager::new(&crit_ids),
tl_matcher_manager: TlMatcherManager::new(&crit_ids),
caps_thread,
});
state.tracker.register(ClientId::from_raw(0));
create_dummy_output(&state);

View file

@ -4,6 +4,7 @@ use {
async_engine::AsyncEngine,
gfx_api::{GfxContext, GfxError},
io_uring::IoUring,
pr_caps::PrCapsThread,
utils::errorfmt::ErrorFmt,
video::drm::Drm,
},
@ -19,12 +20,13 @@ pub fn create_gfx_context(
ring: &Rc<IoUring>,
drm: &Drm,
api: GfxApi,
caps_thread: Option<&PrCapsThread>,
) -> Result<Rc<dyn GfxContext>, GfxError> {
let mut apis = [GfxApi::OpenGl, GfxApi::Vulkan];
apis.sort_by_key(|&a| if a == api { -1 } else { a as i32 });
let mut last_err = None;
for api in apis {
let res = create_gfx_context_(eng, ring, drm, api);
let res = create_gfx_context_(eng, ring, drm, api, caps_thread);
match res {
Ok(_) => return res,
Err(e) => {
@ -41,10 +43,11 @@ fn create_gfx_context_(
ring: &Rc<IoUring>,
drm: &Drm,
api: GfxApi,
caps_thread: Option<&PrCapsThread>,
) -> Result<Rc<dyn GfxContext>, GfxError> {
match api {
GfxApi::OpenGl => gl::create_gfx_context(drm),
GfxApi::Vulkan => vulkan::create_graphics_context(eng, ring, drm),
GfxApi::Vulkan => vulkan::create_graphics_context(eng, ring, drm, caps_thread),
_ => unreachable!(),
}
}

View file

@ -35,8 +35,9 @@ use {
image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer,
},
io_uring::IoUring,
pr_caps::PrCapsThread,
rect::Rect,
utils::oserror::OsError,
utils::{errorfmt::ErrorFmt, oserror::OsError},
video::{
dmabuf::DmaBuf,
drm::{Drm, DrmError, sync_obj::SyncObjCtx},
@ -224,16 +225,27 @@ pub fn create_graphics_context(
eng: &Rc<AsyncEngine>,
ring: &Rc<IoUring>,
drm: &Drm,
caps_thread: Option<&PrCapsThread>,
) -> Result<Rc<dyn GfxContext>, GfxError> {
let instance = VulkanInstance::new(Level::Info, *VULKAN_VALIDATION)?;
let device = instance.create_device(drm)?;
let device = 'device: {
if let Some(t) = caps_thread {
match unsafe { t.run(|| instance.create_device(drm, true)) } {
Ok(d) => break 'device d,
Err(e) => {
log::warn!("Could not create high-priority device: {}", ErrorFmt(e));
}
}
}
instance.create_device(drm, false)?
};
let renderer = device.create_renderer(eng, ring)?;
Ok(Rc::new(Context(renderer)))
}
pub fn create_vulkan_allocator(drm: &Drm) -> Result<Rc<dyn Allocator>, AllocatorError> {
let instance = VulkanInstance::new(Level::Debug, *VULKAN_VALIDATION)?;
let device = instance.create_device(drm)?;
let device = instance.create_device(drm, false)?;
let allocator = device.create_bo_allocator(drm)?;
Ok(Rc::new(allocator))
}

View file

@ -24,12 +24,12 @@ use {
physical_device_drm, queue_family_foreign,
},
khr::{
driver_properties, external_fence_fd, external_memory_fd, external_semaphore_fd,
self, driver_properties, external_fence_fd, external_memory_fd, external_semaphore_fd,
push_descriptor,
},
vk::{
self, DeviceCreateInfo, DeviceQueueCreateInfo, DeviceSize, DriverId,
ExternalSemaphoreFeatureFlags, ExternalSemaphoreHandleTypeFlags,
self, DeviceCreateInfo, DeviceQueueCreateInfo, DeviceQueueGlobalPriorityCreateInfoKHR,
DeviceSize, DriverId, ExternalSemaphoreFeatureFlags, ExternalSemaphoreHandleTypeFlags,
ExternalSemaphoreProperties, MAX_MEMORY_TYPES, MemoryPropertyFlags, MemoryType,
PhysicalDevice, PhysicalDeviceBufferDeviceAddressFeatures,
PhysicalDeviceDescriptorBufferFeaturesEXT, PhysicalDeviceDescriptorBufferPropertiesEXT,
@ -39,7 +39,7 @@ use {
PhysicalDeviceProperties2, PhysicalDeviceSynchronization2Features,
PhysicalDeviceTimelineSemaphoreFeatures,
PhysicalDeviceUniformBufferStandardLayoutFeatures, PhysicalDeviceVulkan12Properties,
Queue, QueueFlags,
Queue, QueueFamilyProperties2, QueueFlags, QueueGlobalPriorityKHR,
},
},
isnt::std_1::collections::IsntHashMap2Ext,
@ -50,6 +50,7 @@ use {
sync::Arc,
},
uapi::Ustr,
vk::QueueFamilyGlobalPriorityPropertiesKHR,
};
pub struct VulkanDevice {
@ -210,20 +211,42 @@ impl VulkanInstance {
fn find_queues(
&self,
phy_dev: PhysicalDevice,
) -> Result<(u32, Option<(u32, u32, u32)>), VulkanError> {
let props = unsafe {
) -> Result<
(
u32,
Option<(u32, u32, u32)>,
QueueGlobalPriorityKHR,
QueueGlobalPriorityKHR,
),
VulkanError,
> {
let len = unsafe {
self.instance
.get_physical_device_queue_family_properties(phy_dev)
.get_physical_device_queue_family_properties2_len(phy_dev)
};
let mut priority_props = vec![QueueFamilyGlobalPriorityPropertiesKHR::default(); len];
let mut props: Vec<_> = priority_props
.iter_mut()
.map(|p| QueueFamilyProperties2::default().push_next(p))
.collect();
unsafe {
self.instance
.get_physical_device_queue_family_properties2(phy_dev, &mut props[..])
}
let gfx_queue = props
.iter()
.position(|p| p.queue_flags.contains(QueueFlags::GRAPHICS))
.position(|p| {
p.queue_family_properties
.queue_flags
.contains(QueueFlags::GRAPHICS)
})
.ok_or(VulkanError::NoGraphicsQueue)?;
let transfer_queue = 'transfer: {
let mut transfer_only = None;
let mut compute_only = None;
let mut separate_gfx = None;
for (idx, props) in props.iter().enumerate() {
let props = &props.queue_family_properties;
if idx == gfx_queue {
continue;
}
@ -244,7 +267,7 @@ impl VulkanInstance {
if let Some(idx) = transfer_only.or(compute_only).or(separate_gfx) {
break 'transfer Some(idx);
}
if props[gfx_queue].queue_count > 1 {
if props[gfx_queue].queue_family_properties.queue_count > 1 {
break 'transfer Some(gfx_queue);
}
None
@ -252,13 +275,27 @@ impl VulkanInstance {
let mut width_mask = 0;
let mut height_mask = 0;
if let Some(idx) = transfer_queue {
let g = &props[idx].min_image_transfer_granularity;
let g = &props[idx]
.queue_family_properties
.min_image_transfer_granularity;
width_mask = g.width.wrapping_sub(1);
height_mask = g.height.wrapping_sub(1);
}
let get_priority = |idx: usize| {
let props = &priority_props[idx];
if props.priority_count > 0 {
props.priorities[props.priority_count as usize - 1]
} else {
QueueGlobalPriorityKHR::MEDIUM
}
};
Ok((
gfx_queue as _,
transfer_queue.map(|v| (v as _, width_mask, height_mask)),
get_priority(gfx_queue),
transfer_queue
.map(get_priority)
.unwrap_or(QueueGlobalPriorityKHR::MEDIUM),
))
}
@ -275,7 +312,11 @@ impl VulkanInstance {
.contains(ExternalSemaphoreFeatureFlags::IMPORTABLE)
}
pub fn create_device(self: &Rc<Self>, drm: &Drm) -> Result<Rc<VulkanDevice>, VulkanError> {
pub fn create_device(
self: &Rc<Self>,
drm: &Drm,
mut high_priority: bool,
) -> Result<Rc<VulkanDevice>, VulkanError> {
let render_node = drm
.get_render_node()
.map_err(VulkanError::FetchRenderNode)?
@ -293,7 +334,17 @@ impl VulkanInstance {
if !supports_descriptor_buffer {
log::warn!("Vulkan device does not support descriptor buffers");
}
let (graphics_queue_family_idx, transfer_queue_family) = self.find_queues(phy_dev)?;
let supports_queue_priority = extensions.contains_key(khr::global_priority::NAME);
if !supports_queue_priority && high_priority {
high_priority = false;
log::warn!("Vulkan device does not support queue priorities");
}
let (
graphics_queue_family_idx,
transfer_queue_family,
max_graphics_priority,
max_transfer_priority,
) = self.find_queues(phy_dev)?;
let mut distinct_transfer_queue_family_idx = None;
let mut transfer_granularity_mask = (0, 0);
if let Some((idx, width_mask, height_mask)) = transfer_queue_family {
@ -325,18 +376,31 @@ impl VulkanInstance {
let mut uniform_buffer_standard_layout_features =
PhysicalDeviceUniformBufferStandardLayoutFeatures::default()
.uniform_buffer_standard_layout(true);
let mut gfx_queue_device_queue_global_priority_create_info =
DeviceQueueGlobalPriorityCreateInfoKHR::default()
.global_priority(max_graphics_priority);
let mut trn_queue_device_queue_global_priority_create_info =
DeviceQueueGlobalPriorityCreateInfoKHR::default()
.global_priority(max_transfer_priority);
let mut queue_create_infos = ArrayVec::<_, 2>::new();
queue_create_infos.push(
DeviceQueueCreateInfo::default()
.queue_family_index(graphics_queue_family_idx)
.queue_priorities(&[1.0]),
);
let queue_create_info = |idx, priority_info| {
let mut info = DeviceQueueCreateInfo::default()
.queue_family_index(idx)
.queue_priorities(&[1.0]);
if high_priority {
info = info.push_next(priority_info);
}
info
};
queue_create_infos.push(queue_create_info(
graphics_queue_family_idx,
&mut gfx_queue_device_queue_global_priority_create_info,
));
if let Some((tq, _, _)) = transfer_queue_family {
queue_create_infos.push(
DeviceQueueCreateInfo::default()
.queue_family_index(tq)
.queue_priorities(&[1.0]),
);
queue_create_infos.push(queue_create_info(
tq,
&mut trn_queue_device_queue_global_priority_create_info,
));
}
let mut device_create_info = DeviceCreateInfo::default()
.push_next(&mut semaphore_features)
@ -433,6 +497,11 @@ impl VulkanInstance {
};
unsafe { device.get_device_queue(family_idx, queue_idx) }
});
if high_priority {
log::info!(
"Created queues with priorities {max_graphics_priority:?}/{max_transfer_priority:?}",
);
}
Ok(Rc::new(VulkanDevice {
physical_device: phy_dev,
render_node,

View file

@ -185,17 +185,22 @@ impl UsrJayRenderCtxOwner for PortalDisplay {
}
}
if render_ctx.is_none() {
let ctx =
match create_gfx_context(&self.state.eng, &self.state.ring, &drm, GfxApi::OpenGl) {
Ok(c) => c,
Err(e) => {
log::error!(
"Could not create render context from drm device: {}",
ErrorFmt(e)
);
return;
}
};
let ctx = match create_gfx_context(
&self.state.eng,
&self.state.ring,
&drm,
GfxApi::OpenGl,
None,
) {
Ok(c) => c,
Err(e) => {
log::error!(
"Could not create render context from drm device: {}",
ErrorFmt(e)
);
return;
}
};
let ctx = Rc::new(PortalRenderCtx {
_dev_id: dev_id,
ctx,

View file

@ -6,6 +6,13 @@ use {
},
utils::{bitflags::BitflagsExt, errorfmt::ErrorFmt, oserror::OsError},
},
opera::PhantomNotSend,
parking_lot::{Condvar, Mutex},
std::{
mem,
sync::Arc,
thread::{self, JoinHandle},
},
uapi::{
c::{SYS_capget, SYS_capset, syscall},
map_err,
@ -22,6 +29,24 @@ pub struct PrCompCaps {
caps: PrCaps,
}
pub struct PrCapsThread {
thread: Option<JoinHandle<()>>,
data: Arc<ThreadData>,
_no_send: PhantomNotSend,
}
#[derive(Default)]
struct ThreadData {
cond: Condvar,
mutex: Mutex<MutData>,
}
#[derive(Default)]
struct MutData {
exit: bool,
fun: Option<Box<dyn FnOnce() + Send>>,
}
pub fn pr_caps() -> PrCaps {
let mut hdr = cap_user_header_t {
version: _LINUX_CAPABILITY_VERSION_3,
@ -103,6 +128,70 @@ impl PrCompCaps {
pub fn has_nice(&self) -> bool {
self.caps.effective.contains(1 << CAP_SYS_NICE)
}
pub fn into_thread(self) -> PrCapsThread {
let data = Arc::new(ThreadData::default());
let data2 = data.clone();
let jh = thread::Builder::new()
.name("SYS_nice thread".to_string())
.spawn(move || {
let data2 = data2;
let mut lock = data2.mutex.lock();
loop {
if lock.exit {
return;
}
if let Some(f) = lock.fun.take() {
f();
}
data2.cond.wait(&mut lock);
}
})
.expect("Could not spawn SYS_nice thread");
PrCapsThread {
thread: Some(jh),
data,
_no_send: Default::default(),
}
}
}
impl PrCapsThread {
pub unsafe fn run<T, F>(&self, f: F) -> T
where
F: FnOnce() -> T,
{
struct AssertSend<T>(T);
unsafe impl<T> Send for AssertSend<T> {}
struct Data<T> {
cond: Condvar,
mutex: Mutex<Option<AssertSend<T>>>,
}
let data = Arc::new(Data {
cond: Default::default(),
mutex: Default::default(),
});
let data2 = data.clone();
let f = AssertSend(f);
let fun = Box::new(move || {
let f = f;
let t = f.0();
*data2.mutex.lock() = Some(AssertSend(t));
data2.cond.notify_all();
});
let fun = unsafe {
mem::transmute::<Box<dyn FnOnce() + Send + '_>, Box<dyn FnOnce() + Send>>(fun)
};
self.data.mutex.lock().fun = Some(fun);
self.data.cond.notify_all();
let mut lock = data.mutex.lock();
loop {
if let Some(t) = lock.take() {
return t.0;
}
data.cond.wait(&mut lock);
}
}
}
impl Drop for PrCaps {
@ -111,6 +200,14 @@ impl Drop for PrCaps {
}
}
impl Drop for PrCapsThread {
fn drop(&mut self) {
self.data.mutex.lock().exit = true;
self.data.cond.notify_all();
let _ = self.thread.take().unwrap().join();
}
}
mod sys {
#![allow(dead_code)]

View file

@ -74,6 +74,7 @@ use {
keyboard::KeyboardStateIds,
leaks::Tracker,
logger::Logger,
pr_caps::PrCapsThread,
rect::{Rect, Region},
renderer::Renderer,
scale::Scale,
@ -246,6 +247,7 @@ pub struct State {
pub show_pin_icon: Cell<bool>,
pub cl_matcher_manager: ClMatcherManager,
pub tl_matcher_manager: TlMatcherManager,
pub caps_thread: Option<PrCapsThread>,
}
// impl Drop for State {
@ -446,6 +448,7 @@ impl State {
&self.ring,
drm,
api.unwrap_or(self.default_gfx_api.get()),
self.caps_thread.as_ref(),
)
}