new workspace

This commit is contained in:
galister
2025-06-18 01:14:04 +09:00
parent 95f2ae4296
commit f05d3a8251
252 changed files with 24618 additions and 184 deletions

View File

@@ -0,0 +1,666 @@
pub mod dds;
pub mod dmabuf;
use std::{
collections::HashMap,
sync::{Arc, OnceLock},
};
use glam::{vec2, Vec2};
use vulkano::{
buffer::{BufferCreateInfo, BufferUsage},
command_buffer::{PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract},
image::view::ImageView,
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter},
sync::GpuFuture,
};
use wgui::gfx::WGfx;
#[cfg(feature = "openvr")]
use vulkano::instance::InstanceCreateFlags;
use wlx_capture::frame::DrmFormat;
use crate::shaders::{frag_color, frag_grid, frag_screen, frag_srgb, vert_quad};
#[cfg(feature = "openxr")]
use {ash::vk, std::os::raw::c_void};
use vulkano::{
self,
buffer::{Buffer, BufferContents, IndexBuffer, Subbuffer},
device::{
physical::{PhysicalDevice, PhysicalDeviceType},
Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo,
QueueFlags,
},
format::Format,
instance::{Instance, InstanceCreateInfo, InstanceExtensions},
pipeline::graphics::{
color_blend::{AttachmentBlend, BlendFactor, BlendOp},
vertex_input::Vertex,
},
shader::ShaderModule,
VulkanObject,
};
use dmabuf::get_drm_formats;
pub type Vert2Buf = Subbuffer<[Vert2Uv]>;
pub type IndexBuf = IndexBuffer;
#[repr(C)]
#[derive(BufferContents, Vertex, Copy, Clone, Debug)]
pub struct Vert2Uv {
#[format(R32G32_SFLOAT)]
pub in_pos: [f32; 2],
#[format(R32G32_SFLOAT)]
pub in_uv: [f32; 2],
}
pub const INDICES: [u16; 6] = [2, 1, 0, 1, 2, 3];
pub const BLEND_ALPHA: AttachmentBlend = AttachmentBlend {
src_color_blend_factor: BlendFactor::SrcAlpha,
dst_color_blend_factor: BlendFactor::OneMinusSrcAlpha,
color_blend_op: BlendOp::Add,
src_alpha_blend_factor: BlendFactor::One,
dst_alpha_blend_factor: BlendFactor::One,
alpha_blend_op: BlendOp::Max,
};
pub struct WGfxExtras {
pub shaders: HashMap<&'static str, Arc<ShaderModule>>,
pub drm_formats: Vec<DrmFormat>,
pub queue_capture: Option<Arc<Queue>>,
pub quad_verts: Vert2Buf,
}
impl WGfxExtras {
pub fn new(gfx: &WGfx, queue_capture: Option<Arc<Queue>>) -> anyhow::Result<Self> {
let mut shaders = HashMap::new();
let shader = vert_quad::load(gfx.device.clone())?;
shaders.insert("vert_quad", shader);
let shader = frag_color::load(gfx.device.clone())?;
shaders.insert("frag_color", shader);
let shader = frag_srgb::load(gfx.device.clone())?;
shaders.insert("frag_srgb", shader);
let shader = frag_grid::load(gfx.device.clone())?;
shaders.insert("frag_grid", shader);
let shader = frag_screen::load(gfx.device.clone())?;
shaders.insert("frag_screen", shader);
let drm_formats = get_drm_formats(gfx.device.clone());
let vertices = [
Vert2Uv {
in_pos: [0., 0.],
in_uv: [0., 0.],
},
Vert2Uv {
in_pos: [1., 0.],
in_uv: [1., 0.],
},
Vert2Uv {
in_pos: [0., 1.],
in_uv: [0., 1.],
},
Vert2Uv {
in_pos: [1., 1.],
in_uv: [1., 1.],
},
];
let quad_verts = Buffer::from_iter(
gfx.memory_allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::VERTEX_BUFFER,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
vertices.into_iter(),
)?;
Ok(Self {
shaders,
drm_formats,
queue_capture,
quad_verts,
})
}
}
const fn get_dmabuf_extensions() -> DeviceExtensions {
DeviceExtensions {
khr_external_memory: true,
khr_external_memory_fd: true,
ext_external_memory_dma_buf: true,
..DeviceExtensions::empty()
}
}
static VULKAN_LIBRARY: OnceLock<Arc<vulkano::VulkanLibrary>> = OnceLock::new();
fn get_vulkan_library() -> &'static Arc<vulkano::VulkanLibrary> {
VULKAN_LIBRARY.get_or_init(|| vulkano::VulkanLibrary::new().unwrap()) // want panic
}
#[cfg(feature = "openxr")]
unsafe extern "system" fn get_instance_proc_addr(
instance: openxr::sys::platform::VkInstance,
name: *const std::ffi::c_char,
) -> Option<unsafe extern "system" fn()> {
use vulkano::Handle;
let instance = ash::vk::Instance::from_raw(instance as _);
let library = get_vulkan_library();
library.get_instance_proc_addr(instance, name)
}
#[cfg(feature = "openxr")]
#[allow(clippy::too_many_lines)]
pub fn init_openxr_graphics(
xr_instance: openxr::Instance,
system: openxr::SystemId,
) -> anyhow::Result<(Arc<WGfx>, WGfxExtras)> {
use std::ffi::{self, CString};
use vulkano::{Handle, Version};
let instance_extensions = InstanceExtensions {
khr_get_physical_device_properties2: true,
..InstanceExtensions::empty()
};
let instance_extensions_raw = instance_extensions
.into_iter()
.filter_map(|(name, enabled)| {
if enabled {
Some(ffi::CString::new(name).unwrap().into_raw().cast_const())
// want panic
} else {
None
}
})
.collect::<Vec<_>>();
let vk_target_version = vk::make_api_version(0, 1, 3, 0);
let target_version = vulkano::Version::V1_3;
let library = get_vulkan_library();
let vk_app_info_raw = vk::ApplicationInfo::default()
.application_version(0)
.engine_version(0)
.api_version(vk_target_version);
let instance = unsafe {
let vk_instance = xr_instance
.create_vulkan_instance(
system,
get_instance_proc_addr,
std::ptr::from_ref(
&vk::InstanceCreateInfo::default()
.application_info(&vk_app_info_raw)
.enabled_extension_names(&instance_extensions_raw),
)
.cast(),
)
.expect("XR error creating Vulkan instance")
.map_err(vk::Result::from_raw)
.expect("Vulkan error creating Vulkan instance");
Instance::from_handle(
library.clone(),
ash::vk::Instance::from_raw(vk_instance as _),
InstanceCreateInfo {
application_version: Version::major_minor(0, 0),
engine_version: Version::major_minor(0, 0),
max_api_version: Some(Version::V1_3),
enabled_extensions: instance_extensions,
..Default::default()
},
)
};
let physical_device = unsafe {
PhysicalDevice::from_handle(
instance.clone(),
vk::PhysicalDevice::from_raw(
xr_instance.vulkan_graphics_device(system, instance.handle().as_raw() as _)? as _,
),
)
}?;
let vk_device_properties = physical_device.properties();
assert!(
(vk_device_properties.api_version >= target_version),
"Vulkan physical device doesn't support Vulkan {target_version}"
);
log::info!(
"Using vkPhysicalDevice: {}",
physical_device.properties().device_name,
);
let queue_families = try_all_queue_families(physical_device.as_ref())
.expect("vkPhysicalDevice does not have a GRAPHICS / TRANSFER queue.");
let mut device_extensions = DeviceExtensions::empty();
let dmabuf_extensions = get_dmabuf_extensions();
if physical_device
.supported_extensions()
.contains(&dmabuf_extensions)
{
device_extensions = device_extensions.union(&dmabuf_extensions);
device_extensions.ext_image_drm_format_modifier = physical_device
.supported_extensions()
.ext_image_drm_format_modifier;
}
let device_extensions_raw = device_extensions
.into_iter()
.filter_map(|(name, enabled)| {
if enabled {
Some(ffi::CString::new(name).unwrap().into_raw().cast_const())
// want panic
} else {
None
}
})
.collect::<Vec<_>>();
let features = DeviceFeatures {
dynamic_rendering: true,
..Default::default()
};
let queue_create_infos = queue_families
.iter()
.map(|fam| {
vk::DeviceQueueCreateInfo::default()
.queue_family_index(fam.queue_family_index)
.queue_priorities(&fam.priorities)
})
.collect::<Vec<_>>();
let mut device_create_info = vk::DeviceCreateInfo::default()
.queue_create_infos(&queue_create_infos)
.enabled_extension_names(&device_extensions_raw);
let mut dynamic_rendering =
vk::PhysicalDeviceDynamicRenderingFeatures::default().dynamic_rendering(true);
dynamic_rendering.p_next = device_create_info.p_next.cast_mut();
device_create_info.p_next = &raw mut dynamic_rendering as *const c_void;
let (device, queues) = unsafe {
let vk_device = xr_instance
.create_vulkan_device(
system,
get_instance_proc_addr,
physical_device.handle().as_raw() as _,
(&raw const device_create_info).cast(),
)
.expect("XR error creating Vulkan device")
.map_err(vk::Result::from_raw)
.expect("Vulkan error creating Vulkan device");
vulkano::device::Device::from_handle(
physical_device,
vk::Device::from_raw(vk_device as _),
DeviceCreateInfo {
queue_create_infos: queue_families
.iter()
.map(|fam| QueueCreateInfo {
queue_family_index: fam.queue_family_index,
queues: fam.priorities.clone(),
..Default::default()
})
.collect::<Vec<_>>(),
enabled_extensions: device_extensions,
enabled_features: features,
..Default::default()
},
)
};
log::debug!(
" DMA-buf supported: {}",
device.enabled_extensions().ext_external_memory_dma_buf
);
log::debug!(
" DRM format modifiers supported: {}",
device.enabled_extensions().ext_image_drm_format_modifier
);
// Drop the CStrings
device_extensions_raw
.into_iter()
.for_each(|c_string| unsafe {
let _ = CString::from_raw(c_string.cast_mut());
});
let (queue_gfx, queue_xfer, queue_capture) = unwrap_queues(queues.collect());
let gfx = WGfx::new_from_raw(
instance,
device,
queue_gfx,
queue_xfer,
Format::R8G8B8A8_SRGB,
);
let extras = WGfxExtras::new(&gfx, queue_capture)?;
Ok((gfx, extras))
}
#[allow(clippy::too_many_lines)]
#[cfg(feature = "openvr")]
pub fn init_openvr_graphics(
mut vk_instance_extensions: InstanceExtensions,
mut vk_device_extensions_fn: impl FnMut(&PhysicalDevice) -> DeviceExtensions,
) -> anyhow::Result<(Arc<WGfx>, WGfxExtras)> {
//#[cfg(debug_assertions)]
//let layers = vec!["VK_LAYER_KHRONOS_validation".to_owned()];
//#[cfg(not(debug_assertions))]
let layers = vec![];
log::debug!("Instance exts for runtime: {:?}", &vk_instance_extensions);
vk_instance_extensions.khr_get_physical_device_properties2 = true;
let instance = Instance::new(
get_vulkan_library().clone(),
InstanceCreateInfo {
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
enabled_extensions: vk_instance_extensions,
enabled_layers: layers,
..Default::default()
},
)?;
let dmabuf_extensions = get_dmabuf_extensions();
let (physical_device, my_extensions, queue_families) = instance
.enumerate_physical_devices()?
.filter_map(|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;
}
log::debug!(
"Device exts for {}: {:?}",
p.properties().device_name,
&my_extensions
);
Some((p, my_extensions))
})
.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!(
"Using vkPhysicalDevice: {}",
physical_device.properties().device_name,
);
let (device, queues) = Device::new(
physical_device,
DeviceCreateInfo {
enabled_extensions: my_extensions,
enabled_features: DeviceFeatures {
dynamic_rendering: true,
..DeviceFeatures::empty()
},
queue_create_infos: queue_families
.iter()
.map(|fam| QueueCreateInfo {
queue_family_index: fam.queue_family_index,
queues: fam.priorities.clone(),
..Default::default()
})
.collect::<Vec<_>>(),
..Default::default()
},
)?;
log::debug!(
" DMA-buf supported: {}",
device.enabled_extensions().ext_external_memory_dma_buf
);
log::debug!(
" DRM format modifiers supported: {}",
device.enabled_extensions().ext_image_drm_format_modifier
);
let (queue_gfx, queue_xfer, queue_capture) = unwrap_queues(queues.collect());
let gfx = WGfx::new_from_raw(
instance,
device,
queue_gfx,
queue_xfer,
Format::R8G8B8A8_SRGB,
);
let extras = WGfxExtras::new(&gfx, queue_capture)?;
Ok((gfx, extras))
}
pub fn upload_quad_vertices(
buf: &mut Subbuffer<[Vert2Uv]>,
width: f32,
height: f32,
x: f32,
y: f32,
w: f32,
h: f32,
) -> anyhow::Result<()> {
let rw = width;
let rh = height;
let x0 = x / rw;
let y0 = y / rh;
let x1 = w / rw + x0;
let y1 = h / rh + y0;
let data = [
Vert2Uv {
in_pos: [x0, y0],
in_uv: [0.0, 0.0],
},
Vert2Uv {
in_pos: [x0, y1],
in_uv: [0.0, 1.0],
},
Vert2Uv {
in_pos: [x1, y0],
in_uv: [1.0, 0.0],
},
Vert2Uv {
in_pos: [x1, y1],
in_uv: [1.0, 1.0],
},
];
buf.write()?[0..4].copy_from_slice(&data);
Ok(())
}
#[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
}
}
#[derive(Default)]
pub struct CommandBuffers {
inner: Vec<Arc<PrimaryAutoCommandBuffer>>,
}
impl CommandBuffers {
pub fn push(&mut self, buffer: Arc<PrimaryAutoCommandBuffer>) {
self.inner.push(buffer);
}
pub fn execute_now(self, queue: Arc<Queue>) -> anyhow::Result<Option<Box<dyn GpuFuture>>> {
let mut buffers = self.inner.into_iter();
let Some(first) = buffers.next() else {
return Ok(None);
};
let future = first.execute(queue)?;
let mut future: Box<dyn GpuFuture> = Box::new(future);
for buf in buffers {
future = Box::new(future.then_execute_same_queue(buf)?);
}
Ok(Some(future))
}
}
pub trait ExtentExt {
fn extent_f32(&self) -> [f32; 2];
fn extent_vec2(&self) -> Vec2;
fn extent_u32arr(&self) -> [u32; 2];
}
impl ExtentExt for Arc<ImageView> {
fn extent_f32(&self) -> [f32; 2] {
let [w, h, _] = self.image().extent();
[w as _, h as _]
}
fn extent_vec2(&self) -> Vec2 {
let [w, h, _] = self.image().extent();
vec2(w as _, h as _)
}
fn extent_u32arr(&self) -> [u32; 2] {
let [w, h, _] = self.image().extent();
[w, h]
}
}