use multi-threaded capture if vk queues ≥ 2

This commit is contained in:
galister
2025-04-08 00:49:34 +09:00
parent b2a7d3c4cb
commit f13a473048
8 changed files with 342 additions and 269 deletions

View File

@@ -229,7 +229,7 @@ where
has_wlr_dmabuf, has_wlr_dmabuf,
has_wlr_screencopy, has_wlr_screencopy,
pw_token_store, pw_token_store,
&app.session, &app,
) { ) {
overlay.backend.set_renderer(Box::new(renderer)); overlay.backend.set_renderer(Box::new(renderer));
} }

View File

@@ -30,8 +30,10 @@ pub(super) struct LinePool {
impl LinePool { impl LinePool {
pub fn new(graphics: Arc<WlxGraphics>) -> anyhow::Result<Self> { pub fn new(graphics: Arc<WlxGraphics>) -> anyhow::Result<Self> {
let mut command_buffer = let mut command_buffer = graphics.create_uploads_command_buffer(
graphics.create_uploads_command_buffer(CommandBufferUsage::OneTimeSubmit)?; graphics.transfer_queue.clone(),
CommandBufferUsage::OneTimeSubmit,
)?;
let buf = vec![255; 16]; let buf = vec![255; 16];

View File

@@ -31,9 +31,10 @@ pub(super) struct Skybox {
impl Skybox { impl Skybox {
pub fn new(app: &AppState) -> anyhow::Result<Self> { pub fn new(app: &AppState) -> anyhow::Result<Self> {
let mut command_buffer = app let mut command_buffer = app.graphics.create_uploads_command_buffer(
.graphics app.graphics.transfer_queue.clone(),
.create_uploads_command_buffer(CommandBufferUsage::OneTimeSubmit)?; CommandBufferUsage::OneTimeSubmit,
)?;
let mut maybe_image = None; let mut maybe_image = None;

View File

@@ -117,7 +117,7 @@ pub struct WlxGraphics {
pub device: Arc<Device>, pub device: Arc<Device>,
pub graphics_queue: Arc<Queue>, pub graphics_queue: Arc<Queue>,
pub transfer_queue: Arc<Queue>, pub transfer_queue: Arc<Queue>,
pub capture_queue: Arc<Queue>, pub capture_queue: Option<Arc<Queue>>,
pub native_format: Format, pub native_format: Format,
pub texture_filtering: Filter, pub texture_filtering: Filter,
@@ -247,21 +247,8 @@ impl WlxGraphics {
physical_device.properties().device_name, physical_device.properties().device_name,
); );
let queue_family_index_gfx = physical_device let queue_families = try_all_queue_families(physical_device.as_ref())
.queue_family_properties() .expect("vkPhysicalDevice does not have a GRAPHICS / TRANSFER queue.");
.iter()
.enumerate()
.position(|(_, q)| q.queue_flags.intersects(QueueFlags::GRAPHICS))
.expect("Vulkan device has no graphics queue")
as u32;
let queue_family_index_xfer = physical_device
.queue_family_properties()
.iter()
.enumerate()
.position(|(_, q)| q.queue_flags.intersects(QueueFlags::TRANSFER))
.expect("Vulkan device has no transfer queue")
as u32;
let mut device_extensions = DeviceExtensions::empty(); let mut device_extensions = DeviceExtensions::empty();
let dmabuf_extensions = get_dmabuf_extensions(); let dmabuf_extensions = get_dmabuf_extensions();
@@ -276,9 +263,12 @@ impl WlxGraphics {
.ext_image_drm_format_modifier; .ext_image_drm_format_modifier;
} }
if physical_device.supported_extensions().ext_filter_cubic { let texture_filtering = if physical_device.supported_extensions().ext_filter_cubic {
device_extensions.ext_filter_cubic = true; device_extensions.ext_filter_cubic = true;
} Filter::Cubic
} else {
Filter::Linear
};
let device_extensions_raw = device_extensions let device_extensions_raw = device_extensions
.into_iter() .into_iter()
@@ -297,19 +287,14 @@ impl WlxGraphics {
..Default::default() ..Default::default()
}; };
let queue_priorities = vec![1.0]; let queue_create_infos = queue_families
.iter()
let queue_create_infos = [ .map(|fam| {
vk::DeviceQueueCreateInfo::default() vk::DeviceQueueCreateInfo::default()
.queue_family_index(queue_family_index_gfx) .queue_family_index(fam.queue_family_index)
.queue_priorities(&queue_priorities), .queue_priorities(&fam.priorities)
vk::DeviceQueueCreateInfo::default() })
.queue_family_index(queue_family_index_xfer) .collect::<Vec<_>>();
.queue_priorities(&queue_priorities),
vk::DeviceQueueCreateInfo::default()
.queue_family_index(queue_family_index_xfer)
.queue_priorities(&queue_priorities),
];
let mut device_create_info = vk::DeviceCreateInfo::default() let mut device_create_info = vk::DeviceCreateInfo::default()
.queue_create_infos(&queue_create_infos) .queue_create_infos(&queue_create_infos)
@@ -321,13 +306,7 @@ impl WlxGraphics {
dynamic_rendering.p_next = device_create_info.p_next.cast_mut(); dynamic_rendering.p_next = device_create_info.p_next.cast_mut();
device_create_info.p_next = &raw mut dynamic_rendering as *const c_void; device_create_info.p_next = &raw mut dynamic_rendering as *const c_void;
let texture_filtering = if physical_device.supported_extensions().ext_filter_cubic { let (device, queues) = unsafe {
Filter::Cubic
} else {
Filter::Linear
};
let (device, mut queues) = unsafe {
let vk_device = xr_instance let vk_device = xr_instance
.create_vulkan_device( .create_vulkan_device(
system, system,
@@ -343,23 +322,14 @@ impl WlxGraphics {
physical_device, physical_device,
vk::Device::from_raw(vk_device as _), vk::Device::from_raw(vk_device as _),
DeviceCreateInfo { DeviceCreateInfo {
queue_create_infos: vec![ queue_create_infos: queue_families
QueueCreateInfo { .iter()
queue_family_index: queue_family_index_gfx, .map(|fam| QueueCreateInfo {
queues: queue_priorities.clone(), queue_family_index: fam.queue_family_index,
queues: fam.priorities.clone(),
..Default::default() ..Default::default()
}, })
QueueCreateInfo { .collect::<Vec<_>>(),
queue_family_index: queue_family_index_xfer,
queues: queue_priorities.clone(),
..Default::default()
},
QueueCreateInfo {
queue_family_index: queue_family_index_xfer,
queues: queue_priorities,
..Default::default()
},
],
enabled_extensions: device_extensions, enabled_extensions: device_extensions,
enabled_features: features, enabled_features: features,
..Default::default() ..Default::default()
@@ -383,15 +353,7 @@ impl WlxGraphics {
let _ = CString::from_raw(c_string.cast_mut()); let _ = CString::from_raw(c_string.cast_mut());
}); });
let graphics_queue = queues let (graphics_queue, transfer_queue, capture_queue) = unwrap_queues(queues.collect());
.next()
.ok_or_else(|| anyhow::anyhow!("no graphics queues available"))?;
let transfer_queue = queues
.next()
.ok_or_else(|| anyhow::anyhow!("no transfer queues available"))?;
let capture_queue = queues
.next()
.ok_or_else(|| anyhow::anyhow!("not enough transfer queues available"))?;
let memory_allocator = memory_allocator(device.clone()); let memory_allocator = memory_allocator(device.clone());
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
@@ -459,65 +421,48 @@ impl WlxGraphics {
let dmabuf_extensions = get_dmabuf_extensions(); let dmabuf_extensions = get_dmabuf_extensions();
let (physical_device, my_extensions, queue_family_index_gfx, queue_family_index_xfer) = let (physical_device, my_extensions, queue_families) = instance
instance .enumerate_physical_devices()?
.enumerate_physical_devices()? .filter_map(|p| {
.filter_map(|p| { let mut my_extensions = vk_device_extensions_fn(&p);
let mut my_extensions = vk_device_extensions_fn(&p);
if !p.supported_extensions().contains(&my_extensions) {
log::debug!(
"Not using {} due to missing extensions:",
p.properties().device_name,
);
for (ext, missing) in p.supported_extensions().difference(&my_extensions) {
if missing {
log::debug!(" {ext}");
}
}
return None;
}
if p.supported_extensions().contains(&dmabuf_extensions) {
my_extensions = my_extensions.union(&dmabuf_extensions);
my_extensions.ext_image_drm_format_modifier =
p.supported_extensions().ext_image_drm_format_modifier;
}
if p.supported_extensions().ext_filter_cubic {
my_extensions.ext_filter_cubic = true;
}
if !p.supported_extensions().contains(&my_extensions) {
log::debug!( log::debug!(
"Device exts for {}: {:?}", "Not using {} due to missing extensions:",
p.properties().device_name, p.properties().device_name,
&my_extensions
); );
Some((p, my_extensions)) for (ext, missing) in p.supported_extensions().difference(&my_extensions) {
}) if missing {
.filter_map(|(p, my_extensions)| { log::debug!(" {ext}");
p.queue_family_properties() }
.iter() }
.enumerate() return None;
.position(|(_, q)| q.queue_flags.intersects(QueueFlags::GRAPHICS)) }
.map(|i| (p, my_extensions, i as u32))
}) if p.supported_extensions().contains(&dmabuf_extensions) {
.filter_map(|(p, my_extensions, queue_family_index)| { my_extensions = my_extensions.union(&dmabuf_extensions);
p.queue_family_properties() my_extensions.ext_image_drm_format_modifier =
.iter() p.supported_extensions().ext_image_drm_format_modifier;
.enumerate() }
.position(|(_, q)| q.queue_flags.intersects(QueueFlags::TRANSFER))
.map(|i| (p, my_extensions, queue_family_index, i as u32)) if p.supported_extensions().ext_filter_cubic {
}) my_extensions.ext_filter_cubic = true;
.min_by_key(|(p, _, _, _)| match p.properties().device_type { }
PhysicalDeviceType::DiscreteGpu => 0,
PhysicalDeviceType::IntegratedGpu => 1, log::debug!(
PhysicalDeviceType::VirtualGpu => 2, "Device exts for {}: {:?}",
PhysicalDeviceType::Cpu => 3, p.properties().device_name,
PhysicalDeviceType::Other => 4, &my_extensions
_ => 5, );
}) Some((p, my_extensions))
.expect("no suitable physical device found"); })
.filter_map(|(p, my_extensions)| {
try_all_queue_families(p.as_ref()).map(|families| (p, my_extensions, families))
})
.min_by_key(|(p, _, families)| {
prio_from_device_type(p) * 10 + prio_from_families(families)
})
.expect("no suitable physical device found");
log::info!( log::info!(
"Using vkPhysicalDevice: {}", "Using vkPhysicalDevice: {}",
@@ -530,7 +475,7 @@ impl WlxGraphics {
Filter::Linear Filter::Linear
}; };
let (device, mut queues) = Device::new( let (device, queues) = Device::new(
physical_device, physical_device,
DeviceCreateInfo { DeviceCreateInfo {
enabled_extensions: my_extensions, enabled_extensions: my_extensions,
@@ -538,23 +483,14 @@ impl WlxGraphics {
dynamic_rendering: true, dynamic_rendering: true,
..DeviceFeatures::empty() ..DeviceFeatures::empty()
}, },
queue_create_infos: vec![ queue_create_infos: queue_families
QueueCreateInfo { .iter()
queue_family_index: queue_family_index_gfx, .map(|fam| QueueCreateInfo {
queues: vec![1.0], queue_family_index: fam.queue_family_index,
queues: fam.priorities.clone(),
..Default::default() ..Default::default()
}, })
QueueCreateInfo { .collect::<Vec<_>>(),
queue_family_index: queue_family_index_xfer,
queues: vec![1.0],
..Default::default()
},
QueueCreateInfo {
queue_family_index: queue_family_index_xfer,
queues: vec![1.0],
..Default::default()
},
],
..Default::default() ..Default::default()
}, },
)?; )?;
@@ -568,15 +504,7 @@ impl WlxGraphics {
device.enabled_extensions().ext_image_drm_format_modifier device.enabled_extensions().ext_image_drm_format_modifier
); );
let graphics_queue = queues let (graphics_queue, transfer_queue, capture_queue) = unwrap_queues(queues.collect());
.next()
.ok_or_else(|| anyhow::anyhow!("no graphics queues available"))?;
let transfer_queue = queues
.next()
.ok_or_else(|| anyhow::anyhow!("no transfer queues available"))?;
let capture_queue = queues
.next()
.ok_or_else(|| anyhow::anyhow!("no transfer queues available"))?;
let memory_allocator = memory_allocator(device.clone()); let memory_allocator = memory_allocator(device.clone());
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
@@ -624,7 +552,6 @@ impl WlxGraphics {
)> { )> {
use vulkano::{ use vulkano::{
descriptor_set::allocator::StandardDescriptorSetAllocatorCreateInfo, descriptor_set::allocator::StandardDescriptorSetAllocatorCreateInfo,
device::physical::PhysicalDeviceType,
instance::InstanceCreateFlags, instance::InstanceCreateFlags,
swapchain::{Surface, SurfaceInfo}, swapchain::{Surface, SurfaceInfo},
}; };
@@ -657,7 +584,7 @@ impl WlxGraphics {
log::debug!("Device exts for app: {:?}", &device_extensions); log::debug!("Device exts for app: {:?}", &device_extensions);
let (physical_device, my_extensions, queue_family_index) = instance let (physical_device, my_extensions, queue_families) = instance
.enumerate_physical_devices()? .enumerate_physical_devices()?
.filter_map(|p| { .filter_map(|p| {
if p.supported_extensions().contains(&device_extensions) { if p.supported_extensions().contains(&device_extensions) {
@@ -675,22 +602,11 @@ impl WlxGraphics {
None None
} }
}) })
.filter_map(|(p, my_extensions)| { .filter_map(|(p, my_extensions)|
p.queue_family_properties() try_all_queue_families(p.as_ref()).map(|families| (p, my_extensions, families))
.iter() )
.enumerate() .min_by_key(|(p, _, _)| prio_from_device_type(p)
.position(|(i, q)| q.queue_flags.intersects(QueueFlags::GRAPHICS) )
&& p.surface_support(i as u32, &surface).unwrap_or(false))
.map(|i| (p, my_extensions, i as u32))
})
.min_by_key(|(p, _, _)| match p.properties().device_type {
PhysicalDeviceType::DiscreteGpu => 0,
PhysicalDeviceType::IntegratedGpu => 1,
PhysicalDeviceType::VirtualGpu => 2,
PhysicalDeviceType::Cpu => 3,
PhysicalDeviceType::Other => 4,
_ => 5,
})
.expect("no suitable physical device found"); .expect("no suitable physical device found");
log::info!( log::info!(
@@ -698,7 +614,7 @@ impl WlxGraphics {
physical_device.properties().device_name, physical_device.properties().device_name,
); );
let (device, mut queues) = Device::new( let (device, queues) = Device::new(
physical_device, physical_device,
DeviceCreateInfo { DeviceCreateInfo {
enabled_extensions: my_extensions, enabled_extensions: my_extensions,
@@ -706,14 +622,20 @@ impl WlxGraphics {
dynamic_rendering: true, dynamic_rendering: true,
..DeviceFeatures::empty() ..DeviceFeatures::empty()
}, },
queue_create_infos: vec![QueueCreateInfo { queue_create_infos: queue_families
queue_family_index, .iter()
..Default::default() .map(|fam| QueueCreateInfo {
}], queue_family_index: fam.queue_family_index,
queues: fam.priorities.clone(),
..Default::default()
})
.collect::<Vec<_>>(),
..Default::default() ..Default::default()
}, },
)?; )?;
let (graphics_queue, transfer_queue, capture_queue) = unwrap_queues(queues.collect());
let native_format = device let native_format = device
.physical_device() .physical_device()
.surface_formats(&surface, SurfaceInfo::default()) .surface_formats(&surface, SurfaceInfo::default())
@@ -721,10 +643,6 @@ impl WlxGraphics {
.0; .0;
log::info!("Using surface format: {native_format:?}"); log::info!("Using surface format: {native_format:?}");
let queue = queues
.next()
.ok_or_else(|| anyhow::anyhow!("no GPU queues available"))?;
let memory_allocator = memory_allocator(device.clone()); let memory_allocator = memory_allocator(device.clone());
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
device.clone(), device.clone(),
@@ -744,9 +662,9 @@ impl WlxGraphics {
let me = Self { let me = Self {
instance, instance,
device, device,
graphics_queue: queue.clone(), graphics_queue,
transfer_queue: queue.clone(), // what could go wrong! transfer_queue,
capture_queue: queue, capture_queue,
memory_allocator, memory_allocator,
native_format, native_format,
texture_filtering: Filter::Linear, texture_filtering: Filter::Linear,
@@ -1076,34 +994,17 @@ impl WlxGraphics {
/// Creates a CommandBuffer to be used for texture uploads on the main thread. /// Creates a CommandBuffer to be used for texture uploads on the main thread.
pub fn create_uploads_command_buffer( pub fn create_uploads_command_buffer(
self: &Arc<Self>, self: &Arc<Self>,
queue: Arc<Queue>,
usage: CommandBufferUsage, usage: CommandBufferUsage,
) -> anyhow::Result<WlxUploadsBuffer> { ) -> anyhow::Result<WlxUploadsBuffer> {
let command_buffer = AutoCommandBufferBuilder::primary( let command_buffer = AutoCommandBufferBuilder::primary(
self.command_buffer_allocator.clone(), self.command_buffer_allocator.clone(),
self.transfer_queue.queue_family_index(), queue.queue_family_index(),
usage, usage,
)?; )?;
Ok(WlxUploadsBuffer { Ok(WlxUploadsBuffer {
graphics: self.clone(), graphics: self.clone(),
queue: self.transfer_queue.clone(), queue,
command_buffer,
dummy: None,
})
}
/// Creates a CommandBuffer to be used for texture uploads on the capture thread.
pub fn create_capture_command_buffer(
self: &Arc<Self>,
usage: CommandBufferUsage,
) -> anyhow::Result<WlxUploadsBuffer> {
let command_buffer = AutoCommandBufferBuilder::primary(
self.command_buffer_allocator.clone(),
self.capture_queue.queue_family_index(),
usage,
)?;
Ok(WlxUploadsBuffer {
graphics: self.clone(),
queue: self.capture_queue.clone(),
command_buffer, command_buffer,
dummy: None, dummy: None,
}) })
@@ -1577,3 +1478,103 @@ fn memory_allocator(device: Arc<Device>) -> Arc<StandardMemoryAllocator> {
Arc::new(StandardMemoryAllocator::new(device, create_info)) Arc::new(StandardMemoryAllocator::new(device, create_info))
} }
#[derive(Debug)]
struct QueueFamilyLayout {
queue_family_index: u32,
priorities: Vec<f32>,
}
fn prio_from_device_type(physical_device: &PhysicalDevice) -> u32 {
match physical_device.properties().device_type {
PhysicalDeviceType::DiscreteGpu => 0,
PhysicalDeviceType::IntegratedGpu => 1,
PhysicalDeviceType::VirtualGpu => 2,
PhysicalDeviceType::Cpu => 3,
_ => 4,
}
}
const fn prio_from_families(families: &[QueueFamilyLayout]) -> u32 {
match families.len() {
2 | 3 => 0,
_ => 1,
}
}
fn unwrap_queues(queues: Vec<Arc<Queue>>) -> (Arc<Queue>, Arc<Queue>, Option<Arc<Queue>>) {
match queues[..] {
[ref g, ref t, ref c] => (g.clone(), t.clone(), Some(c.clone())),
[ref gt, ref c] => (gt.clone(), gt.clone(), Some(c.clone())),
[ref gt] => (gt.clone(), gt.clone(), None),
_ => unreachable!(),
}
}
fn try_all_queue_families(physical_device: &PhysicalDevice) -> Option<Vec<QueueFamilyLayout>> {
queue_families_priorities(
physical_device,
vec![
// main-thread graphics + uploads
QueueFlags::GRAPHICS | QueueFlags::TRANSFER,
// capture-thread uploads
QueueFlags::TRANSFER,
],
)
.or_else(|| {
queue_families_priorities(
physical_device,
vec![
// main thread graphics
QueueFlags::GRAPHICS,
// main thread uploads
QueueFlags::TRANSFER,
// capture thread uploads
QueueFlags::TRANSFER,
],
)
})
.or_else(|| {
queue_families_priorities(
physical_device,
// main thread-only. software capture not supported.
vec![QueueFlags::GRAPHICS | QueueFlags::TRANSFER],
)
})
}
fn queue_families_priorities(
physical_device: &PhysicalDevice,
mut requested_queues: Vec<QueueFlags>,
) -> Option<Vec<QueueFamilyLayout>> {
let mut result = Vec::with_capacity(3);
for (idx, props) in physical_device.queue_family_properties().iter().enumerate() {
let mut remaining = props.queue_count;
let mut want = 0usize;
requested_queues.retain(|requested| {
if props.queue_flags.intersects(*requested) && remaining > 0 {
remaining -= 1;
want += 1;
false
} else {
true
}
});
if want > 0 {
result.push(QueueFamilyLayout {
queue_family_index: idx as u32,
priorities: std::iter::repeat_n(1.0, want).collect(),
});
}
}
if requested_queues.is_empty() {
log::debug!("Selected GPU queue families: {result:?}");
Some(result)
} else {
None
}
}

View File

@@ -240,8 +240,10 @@ impl FontCache {
}; };
if cmd_buffer.is_none() { if cmd_buffer.is_none() {
*cmd_buffer = *cmd_buffer = Some(graphics.create_uploads_command_buffer(
Some(graphics.create_uploads_command_buffer(CommandBufferUsage::OneTimeSubmit)?); graphics.transfer_queue.clone(),
CommandBufferUsage::OneTimeSubmit,
)?);
} }
let texture = cmd_buffer.as_mut().unwrap().texture2d_raw( let texture = cmd_buffer.as_mut().unwrap().texture2d_raw(

View File

@@ -552,9 +552,10 @@ fn sprite_from_path(path: Arc<str>, app: &mut AppState) -> anyhow::Result<Arc<Im
anyhow::bail!("Could not open custom sprite at: {}", path); anyhow::bail!("Could not open custom sprite at: {}", path);
}; };
let mut command_buffer = app let mut command_buffer = app.graphics.create_uploads_command_buffer(
.graphics app.graphics.transfer_queue.clone(),
.create_uploads_command_buffer(CommandBufferUsage::OneTimeSubmit)?; CommandBufferUsage::OneTimeSubmit,
)?;
match command_buffer.texture2d_dds(f) { match command_buffer.texture2d_dds(f) {
Ok(image) => { Ok(image) => {

View File

@@ -8,6 +8,7 @@ use std::{
}; };
use vulkano::{ use vulkano::{
command_buffer::CommandBufferUsage, command_buffer::CommandBufferUsage,
device::Queue,
format::Format, format::Format,
image::{sampler::Filter, view::ImageView, Image}, image::{sampler::Filter, view::ImageView, Image},
pipeline::graphics::color_blend::AttachmentBlend, pipeline::graphics::color_blend::AttachmentBlend,
@@ -258,6 +259,16 @@ impl ScreenPipeline {
} }
} }
macro_rules! new_wlx_capture {
($capture_queue:expr, $capture:expr) => {
if $capture_queue.is_none() {
Box::new(MainThreadWlxCapture::new($capture)) as Box<dyn WlxCapture<_, _>>
} else {
Box::new($capture) as Box<dyn WlxCapture<_, _>>
}
};
}
pub struct ScreenRenderer { pub struct ScreenRenderer {
name: Arc<str>, name: Arc<str>,
capture: Box<dyn WlxCapture<WlxCaptureIn, WlxCaptureOut>>, capture: Box<dyn WlxCapture<WlxCaptureIn, WlxCaptureOut>>,
@@ -282,41 +293,33 @@ impl ScreenRenderer {
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
pub fn new_wlr_dmabuf(output: &WlxOutput) -> Option<Self> { pub fn new_wlr_dmabuf(output: &WlxOutput, app: &AppState) -> Option<Self> {
let client = WlxClient::new()?; let client = WlxClient::new()?;
let capture = WlrDmabufCapture::new(client, output.id); let capture = new_wlx_capture!(
app.graphics.capture_queue,
Some(Self { WlrDmabufCapture::new(client, output.id)
name: output.name.clone(), );
capture: Box::new(capture), Some(Self::new_raw(output.name.clone(), capture))
pipeline: None,
cur_frame: None,
meta: None,
})
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
pub fn new_wlr_screencopy(output: &WlxOutput) -> Option<Self> { pub fn new_wlr_screencopy(output: &WlxOutput, app: &AppState) -> Option<Self> {
let client = WlxClient::new()?; let client = WlxClient::new()?;
let capture = WlrScreencopyCapture::new(client, output.id); let capture = new_wlx_capture!(
app.graphics.capture_queue,
Some(Self { WlrScreencopyCapture::new(client, output.id)
name: output.name.clone(), );
capture: Box::new(capture), Some(Self::new_raw(output.name.clone(), capture))
pipeline: None,
cur_frame: None,
meta: None,
})
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
pub fn new_pw( pub fn new_pw(
output: &WlxOutput, output: &WlxOutput,
token: Option<&str>, token: Option<&str>,
session: &AppSession, app: &AppState,
) -> anyhow::Result<(Self, Option<String> /* pipewire restore token */)> { ) -> anyhow::Result<(Self, Option<String> /* pipewire restore token */)> {
let name = output.name.clone(); let name = output.name.clone();
let embed_mouse = !session.config.double_cursor_fix; let embed_mouse = !app.session.config.double_cursor_fix;
let select_screen_result = select_pw_screen( let select_screen_result = select_pw_screen(
&format!( &format!(
@@ -336,31 +339,21 @@ impl ScreenRenderer {
let node_id = select_screen_result.streams.first().unwrap().node_id; // streams guaranteed to have at least one element let node_id = select_screen_result.streams.first().unwrap().node_id; // streams guaranteed to have at least one element
let capture = PipewireCapture::new(name, node_id); let capture = new_wlx_capture!(
app.graphics.capture_queue,
PipewireCapture::new(name, node_id)
);
Ok(( Ok((
Self { Self::new_raw(output.name.clone(), capture),
name: output.name.clone(),
capture: Box::new(capture),
pipeline: None,
cur_frame: None,
meta: None,
},
select_screen_result.restore_token, select_screen_result.restore_token,
)) ))
} }
#[cfg(feature = "x11")] #[cfg(feature = "x11")]
pub fn new_xshm(screen: Arc<XshmScreen>) -> Self { pub fn new_xshm(screen: Arc<XshmScreen>, app: &AppState) -> Self {
let capture = XshmCapture::new(screen.clone()); let capture =
new_wlx_capture!(app.graphics.capture_queue, XshmCapture::new(screen.clone()));
Self { Self::new_raw(screen.name.clone(), capture)
name: screen.name.clone(),
capture: Box::new(capture),
pipeline: None,
cur_frame: None,
meta: None,
}
} }
} }
@@ -368,6 +361,7 @@ impl ScreenRenderer {
pub struct WlxCaptureIn { pub struct WlxCaptureIn {
name: Arc<str>, name: Arc<str>,
graphics: Arc<WlxGraphics>, graphics: Arc<WlxGraphics>,
queue: Arc<Queue>,
} }
#[derive(Clone)] #[derive(Clone)]
@@ -386,7 +380,7 @@ fn upload_image(
) -> Option<Arc<Image>> { ) -> Option<Arc<Image>> {
let mut upload = match me let mut upload = match me
.graphics .graphics
.create_capture_command_buffer(CommandBufferUsage::OneTimeSubmit) .create_uploads_command_buffer(me.queue.clone(), CommandBufferUsage::OneTimeSubmit)
{ {
Ok(x) => x, Ok(x) => x,
Err(e) => { Err(e) => {
@@ -518,9 +512,15 @@ impl OverlayRenderer for ScreenRenderer {
let dmabuf_formats = if !supports_dmabuf { let dmabuf_formats = if !supports_dmabuf {
log::info!("Capture method does not support DMA-buf"); log::info!("Capture method does not support DMA-buf");
if app.graphics.capture_queue.is_none() {
log::warn!("Current GPU does not support multiple queues. Software capture will take place on the main thread. Expect degraded performance.");
}
&Vec::new() &Vec::new()
} else if !allow_dmabuf { } else if !allow_dmabuf {
log::info!("Not using DMA-buf capture due to {capture_method}"); log::info!("Not using DMA-buf capture due to {capture_method}");
if app.graphics.capture_queue.is_none() {
log::warn!("Current GPU does not support multiple queues. Software capture will take place on the main thread. Expect degraded performance.");
}
&Vec::new() &Vec::new()
} else { } else {
log::warn!( log::warn!(
@@ -534,6 +534,12 @@ impl OverlayRenderer for ScreenRenderer {
let user_data = WlxCaptureIn { let user_data = WlxCaptureIn {
name: self.name.clone(), name: self.name.clone(),
graphics: app.graphics.clone(), graphics: app.graphics.clone(),
queue: app
.graphics
.capture_queue
.as_ref()
.unwrap_or_else(|| &app.graphics.transfer_queue)
.clone(),
}; };
self.capture self.capture
@@ -554,9 +560,10 @@ impl OverlayRenderer for ScreenRenderer {
}); });
self.pipeline = Some({ self.pipeline = Some({
let mut pipeline = ScreenPipeline::new(&capture.image.extent(), app)?; let mut pipeline = ScreenPipeline::new(&capture.image.extent(), app)?;
let mut upload = app let mut upload = app.graphics.create_uploads_command_buffer(
.graphics app.graphics.transfer_queue.clone(),
.create_uploads_command_buffer(CommandBufferUsage::OneTimeSubmit)?; CommandBufferUsage::OneTimeSubmit,
)?;
pipeline.ensure_mouse_initialized(&mut upload)?; pipeline.ensure_mouse_initialized(&mut upload)?;
upload.build_and_execute_now()?; upload.build_and_execute_now()?;
pipeline pipeline
@@ -613,17 +620,17 @@ pub fn create_screen_renderer_wl(
has_wlr_dmabuf: bool, has_wlr_dmabuf: bool,
has_wlr_screencopy: bool, has_wlr_screencopy: bool,
pw_token_store: &mut PwTokenMap, pw_token_store: &mut PwTokenMap,
session: &AppSession, app: &AppState,
) -> Option<ScreenRenderer> { ) -> Option<ScreenRenderer> {
let mut capture: Option<ScreenRenderer> = None; let mut capture: Option<ScreenRenderer> = None;
if (&*session.config.capture_method == "wlr-dmabuf") && has_wlr_dmabuf { if (&*app.session.config.capture_method == "wlr-dmabuf") && has_wlr_dmabuf {
log::info!("{}: Using Wlr DMA-Buf", &output.name); log::info!("{}: Using Wlr DMA-Buf", &output.name);
capture = ScreenRenderer::new_wlr_dmabuf(output); capture = ScreenRenderer::new_wlr_dmabuf(output, app);
} }
if &*session.config.capture_method == "screencopy" && has_wlr_screencopy { if &*app.session.config.capture_method == "screencopy" && has_wlr_screencopy {
log::info!("{}: Using Wlr Screencopy Wl-SHM", &output.name); log::info!("{}: Using Wlr Screencopy Wl-SHM", &output.name);
capture = ScreenRenderer::new_wlr_screencopy(output); capture = ScreenRenderer::new_wlr_screencopy(output, app);
} }
if capture.is_none() { if capture.is_none() {
@@ -640,7 +647,7 @@ pub fn create_screen_renderer_wl(
log::info!("Found existing Pipewire token for display {display_name}: {t}"); log::info!("Found existing Pipewire token for display {display_name}: {t}");
} }
match ScreenRenderer::new_pw(output, token, session) { match ScreenRenderer::new_pw(output, token, app) {
Ok((renderer, restore_token)) => { Ok((renderer, restore_token)) => {
capture = Some(renderer); capture = Some(renderer);
@@ -793,7 +800,7 @@ pub fn create_screens_wayland(wl: &mut WlxClientAlias, app: &mut AppState) -> Sc
has_wlr_dmabuf, has_wlr_dmabuf,
has_wlr_screencopy, has_wlr_screencopy,
&mut pw_tokens, &mut pw_tokens,
&app.session, app,
) { ) {
let logical_pos = vec2(output.logical_pos.0 as f32, output.logical_pos.1 as f32); let logical_pos = vec2(output.logical_pos.0 as f32, output.logical_pos.1 as f32);
let logical_size = vec2(output.logical_size.0 as f32, output.logical_size.1 as f32); let logical_size = vec2(output.logical_size.0 as f32, output.logical_size.1 as f32);
@@ -910,13 +917,13 @@ pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateDa
native_handle: 0, native_handle: 0,
}; };
let renderer = ScreenRenderer { let renderer = ScreenRenderer::new_raw(
name: m.name.clone(), m.name.clone(),
capture: Box::new(PipewireCapture::new(m.name.clone(), s.node_id)), new_wlx_capture!(
pipeline: None, app.graphics.capture_queue,
cur_frame: None, PipewireCapture::new(m.name.clone(), s.node_id)
meta: None, ),
}; );
let backend = Box::new(SplitOverlayBackend { let backend = Box::new(SplitOverlayBackend {
renderer: Box::new(renderer), renderer: Box::new(renderer),
@@ -953,7 +960,7 @@ pub fn create_screens_xshm(app: &mut AppState) -> anyhow::Result<ScreenCreateDat
let size = (s.monitor.width(), s.monitor.height()); let size = (s.monitor.width(), s.monitor.height());
let pos = (s.monitor.x(), s.monitor.y()); let pos = (s.monitor.x(), s.monitor.y());
let renderer = ScreenRenderer::new_xshm(s.clone()); let renderer = ScreenRenderer::new_xshm(s.clone(), app);
log::info!( log::info!(
"{}: Init X11 screen of res {:?} at {:?}", "{}: Init X11 screen of res {:?} at {:?}",
@@ -1139,3 +1146,62 @@ fn select_pw_screen(
futures::executor::block_on(future) futures::executor::block_on(future)
} }
// Used when a separate GPU queue is not available
// In this case, receive_callback needs to run on the main thread
struct MainThreadWlxCapture<T>
where
T: WlxCapture<(), WlxFrame>,
{
inner: T,
data: Option<WlxCaptureIn>,
}
impl<T> MainThreadWlxCapture<T>
where
T: WlxCapture<(), WlxFrame>,
{
pub const fn new(inner: T) -> Self {
Self { inner, data: None }
}
}
impl<T> WlxCapture<WlxCaptureIn, WlxCaptureOut> for MainThreadWlxCapture<T>
where
T: WlxCapture<(), WlxFrame>,
{
fn init(
&mut self,
dmabuf_formats: &[wlx_frame::DrmFormat],
user_data: WlxCaptureIn,
_: fn(&WlxCaptureIn, WlxFrame) -> Option<WlxCaptureOut>,
) {
self.data = Some(user_data);
self.inner.init(dmabuf_formats, (), receive_callback_dummy);
}
fn is_ready(&self) -> bool {
self.inner.is_ready()
}
fn request_new_frame(&mut self) {
self.inner.request_new_frame();
}
fn pause(&mut self) {
self.inner.pause();
}
fn resume(&mut self) {
self.inner.resume();
}
fn receive(&mut self) -> Option<WlxCaptureOut> {
self.inner
.receive()
.and_then(|frame| receive_callback(self.data.as_ref().unwrap(), frame))
}
fn supports_dmbuf(&self) -> bool {
self.inner.supports_dmbuf()
}
}
#[allow(clippy::trivially_copy_pass_by_ref, clippy::unnecessary_wraps)]
const fn receive_callback_dummy(_: &(), frame: wlx_frame::WlxFrame) -> Option<wlx_frame::WlxFrame> {
Some(frame)
}

View File

@@ -565,9 +565,10 @@ impl WayVRRenderer {
&mut self, &mut self,
data: &wayvr::egl_data::RenderSoftwarePixelsData, data: &wayvr::egl_data::RenderSoftwarePixelsData,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut upload = self let mut upload = self.graphics.create_uploads_command_buffer(
.graphics self.graphics.transfer_queue.clone(),
.create_uploads_command_buffer(CommandBufferUsage::OneTimeSubmit)?; CommandBufferUsage::OneTimeSubmit,
)?;
let tex = upload.texture2d_raw( let tex = upload.texture2d_raw(
u32::from(data.width), u32::from(data.width),
@@ -702,7 +703,6 @@ impl OverlayRenderer for WayVRRenderer {
drop(ctx); drop(ctx);
match data { match data {
//TODO: render to _tgt_
wayvr::egl_data::RenderData::Dmabuf(data) => { wayvr::egl_data::RenderData::Dmabuf(data) => {
self.ensure_dmabuf_data(&data)?; self.ensure_dmabuf_data(&data)?;
} }