new workspace
This commit is contained in:
101
wlx-overlay-s/src/graphics/dds.rs
Normal file
101
wlx-overlay-s/src/graphics/dds.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use image_dds::{ImageFormat, Surface};
|
||||
use std::{io::Read, sync::Arc};
|
||||
use vulkano::{
|
||||
buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer},
|
||||
command_buffer::CopyBufferToImageInfo,
|
||||
format::Format,
|
||||
image::{Image, ImageCreateInfo, ImageType, ImageUsage},
|
||||
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter},
|
||||
DeviceSize,
|
||||
};
|
||||
use wgui::gfx::cmd::XferCommandBuffer;
|
||||
|
||||
pub trait WlxCommandBufferDds {
|
||||
fn upload_image_dds<R>(&mut self, r: R) -> anyhow::Result<Arc<Image>>
|
||||
where
|
||||
R: Read;
|
||||
}
|
||||
|
||||
impl WlxCommandBufferDds for XferCommandBuffer {
|
||||
fn upload_image_dds<R>(&mut self, r: R) -> anyhow::Result<Arc<Image>>
|
||||
where
|
||||
R: Read,
|
||||
{
|
||||
let Ok(dds) = image_dds::ddsfile::Dds::read(r) else {
|
||||
anyhow::bail!("Not a valid DDS file.\nSee: https://github.com/galister/wlx-overlay-s/wiki/Custom-Textures");
|
||||
};
|
||||
|
||||
let surface = Surface::from_dds(&dds)?;
|
||||
|
||||
if surface.depth != 1 {
|
||||
anyhow::bail!("Not a 2D texture.")
|
||||
}
|
||||
|
||||
let image = Image::new(
|
||||
self.graphics.memory_allocator.clone(),
|
||||
ImageCreateInfo {
|
||||
image_type: ImageType::Dim2d,
|
||||
format: dds_to_vk(surface.image_format)?,
|
||||
extent: [surface.width, surface.height, surface.depth],
|
||||
usage: ImageUsage::TRANSFER_DST | ImageUsage::TRANSFER_SRC | ImageUsage::SAMPLED,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo::default(),
|
||||
)?;
|
||||
|
||||
let buffer: Subbuffer<[u8]> = Buffer::new_slice(
|
||||
self.graphics.memory_allocator.clone(),
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::TRANSFER_SRC,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
surface.data.len() as DeviceSize,
|
||||
)?;
|
||||
|
||||
buffer.write()?.copy_from_slice(surface.data);
|
||||
|
||||
self.command_buffer
|
||||
.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(buffer, image.clone()))?;
|
||||
|
||||
Ok(image)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dds_to_vk(dds_fmt: ImageFormat) -> anyhow::Result<Format> {
|
||||
match dds_fmt {
|
||||
ImageFormat::R8Unorm => Ok(Format::R8_UNORM),
|
||||
ImageFormat::Rgba8Unorm => Ok(Format::R8G8B8A8_UNORM),
|
||||
ImageFormat::Rgba8UnormSrgb => Ok(Format::R8G8B8A8_SRGB),
|
||||
ImageFormat::Rgba16Float => Ok(Format::R16G16B16A16_SFLOAT),
|
||||
ImageFormat::Rgba32Float => Ok(Format::R32G32B32A32_SFLOAT),
|
||||
ImageFormat::Bgra8Unorm => Ok(Format::B8G8R8A8_UNORM),
|
||||
ImageFormat::Bgra8UnormSrgb => Ok(Format::B8G8R8A8_SRGB),
|
||||
// DXT1
|
||||
ImageFormat::BC1RgbaUnorm => Ok(Format::BC1_RGBA_UNORM_BLOCK),
|
||||
ImageFormat::BC1RgbaUnormSrgb => Ok(Format::BC1_RGBA_SRGB_BLOCK),
|
||||
// DXT3
|
||||
ImageFormat::BC2RgbaUnorm => Ok(Format::BC2_UNORM_BLOCK),
|
||||
ImageFormat::BC2RgbaUnormSrgb => Ok(Format::BC2_SRGB_BLOCK),
|
||||
// DXT5
|
||||
ImageFormat::BC3RgbaUnorm => Ok(Format::BC3_UNORM_BLOCK),
|
||||
ImageFormat::BC3RgbaUnormSrgb => Ok(Format::BC3_SRGB_BLOCK),
|
||||
// RGTC1
|
||||
ImageFormat::BC4RUnorm => Ok(Format::BC4_UNORM_BLOCK),
|
||||
ImageFormat::BC4RSnorm => Ok(Format::BC4_SNORM_BLOCK),
|
||||
// RGTC2
|
||||
ImageFormat::BC5RgUnorm => Ok(Format::BC5_UNORM_BLOCK),
|
||||
ImageFormat::BC5RgSnorm => Ok(Format::BC5_SNORM_BLOCK),
|
||||
// BPTC
|
||||
ImageFormat::BC6hRgbUfloat => Ok(Format::BC6H_UFLOAT_BLOCK),
|
||||
ImageFormat::BC6hRgbSfloat => Ok(Format::BC6H_SFLOAT_BLOCK),
|
||||
// BPTC
|
||||
ImageFormat::BC7RgbaUnorm => Ok(Format::BC7_UNORM_BLOCK),
|
||||
ImageFormat::BC7RgbaUnormSrgb => Ok(Format::BC7_SRGB_BLOCK),
|
||||
_ => anyhow::bail!("Unsupported format {:?}", dds_fmt),
|
||||
}
|
||||
}
|
||||
350
wlx-overlay-s/src/graphics/dmabuf.rs
Normal file
350
wlx-overlay-s/src/graphics/dmabuf.rs
Normal file
@@ -0,0 +1,350 @@
|
||||
use std::{
|
||||
mem::MaybeUninit,
|
||||
os::fd::{FromRawFd, IntoRawFd},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
use vulkano::{
|
||||
device::Device,
|
||||
format::Format,
|
||||
image::{sys::RawImage, Image, ImageCreateInfo, ImageTiling, ImageUsage, SubresourceLayout},
|
||||
memory::{
|
||||
allocator::{MemoryAllocator, MemoryTypeFilter},
|
||||
DedicatedAllocation, DeviceMemory, ExternalMemoryHandleType, ExternalMemoryHandleTypes,
|
||||
MemoryAllocateInfo, MemoryImportInfo, MemoryPropertyFlags, ResourceMemory,
|
||||
},
|
||||
sync::Sharing,
|
||||
VulkanError, VulkanObject,
|
||||
};
|
||||
use wgui::gfx::WGfx;
|
||||
use wlx_capture::frame::{
|
||||
DmabufFrame, DrmFormat, FourCC, DRM_FORMAT_ABGR2101010, DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_ARGB8888, DRM_FORMAT_XBGR2101010, DRM_FORMAT_XBGR8888, DRM_FORMAT_XRGB8888,
|
||||
};
|
||||
|
||||
pub const DRM_FORMAT_MOD_INVALID: u64 = 0xff_ffff_ffff_ffff;
|
||||
|
||||
pub trait WGfxDmabuf {
|
||||
fn dmabuf_texture_ex(
|
||||
&self,
|
||||
frame: DmabufFrame,
|
||||
tiling: ImageTiling,
|
||||
layouts: Vec<SubresourceLayout>,
|
||||
modifiers: &[u64],
|
||||
) -> anyhow::Result<Arc<Image>>;
|
||||
|
||||
fn dmabuf_texture(&self, frame: DmabufFrame) -> anyhow::Result<Arc<Image>>;
|
||||
}
|
||||
|
||||
impl WGfxDmabuf for WGfx {
|
||||
fn dmabuf_texture_ex(
|
||||
&self,
|
||||
frame: DmabufFrame,
|
||||
tiling: ImageTiling,
|
||||
layouts: Vec<SubresourceLayout>,
|
||||
modifiers: &[u64],
|
||||
) -> anyhow::Result<Arc<Image>> {
|
||||
let extent = [frame.format.width, frame.format.height, 1];
|
||||
let format = fourcc_to_vk(frame.format.fourcc)?;
|
||||
|
||||
let image = unsafe {
|
||||
create_dmabuf_image(
|
||||
self.device.clone(),
|
||||
ImageCreateInfo {
|
||||
format,
|
||||
extent,
|
||||
usage: ImageUsage::SAMPLED,
|
||||
external_memory_handle_types: ExternalMemoryHandleTypes::DMA_BUF,
|
||||
tiling,
|
||||
drm_format_modifiers: modifiers.to_owned(),
|
||||
drm_format_modifier_plane_layouts: layouts,
|
||||
..Default::default()
|
||||
},
|
||||
)?
|
||||
};
|
||||
|
||||
let requirements = image.memory_requirements()[0];
|
||||
let memory_type_index = self
|
||||
.memory_allocator
|
||||
.find_memory_type_index(
|
||||
requirements.memory_type_bits,
|
||||
MemoryTypeFilter {
|
||||
required_flags: MemoryPropertyFlags::DEVICE_LOCAL,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.ok_or_else(|| anyhow::anyhow!("failed to get memory type index"))?;
|
||||
|
||||
debug_assert!(self.device.enabled_extensions().khr_external_memory_fd);
|
||||
debug_assert!(self.device.enabled_extensions().khr_external_memory);
|
||||
debug_assert!(self.device.enabled_extensions().ext_external_memory_dma_buf);
|
||||
|
||||
// only do the 1st
|
||||
unsafe {
|
||||
let Some(fd) = frame.planes[0].fd else {
|
||||
anyhow::bail!("DMA-buf plane has no FD");
|
||||
};
|
||||
|
||||
let file = std::fs::File::from_raw_fd(fd);
|
||||
let new_file = file.try_clone()?;
|
||||
let _ = file.into_raw_fd();
|
||||
|
||||
let memory = DeviceMemory::allocate_unchecked(
|
||||
self.device.clone(),
|
||||
MemoryAllocateInfo {
|
||||
allocation_size: requirements.layout.size(),
|
||||
memory_type_index,
|
||||
dedicated_allocation: Some(DedicatedAllocation::Image(&image)),
|
||||
..Default::default()
|
||||
},
|
||||
Some(MemoryImportInfo::Fd {
|
||||
file: new_file,
|
||||
handle_type: ExternalMemoryHandleType::DmaBuf,
|
||||
}),
|
||||
)?;
|
||||
|
||||
let mem_alloc = ResourceMemory::new_dedicated(memory);
|
||||
match image.bind_memory_unchecked([mem_alloc]) {
|
||||
Ok(image) => Ok(Arc::new(image)),
|
||||
Err(e) => {
|
||||
anyhow::bail!("Failed to bind memory to image: {}", e.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dmabuf_texture(&self, frame: DmabufFrame) -> anyhow::Result<Arc<Image>> {
|
||||
let mut modifiers: Vec<u64> = vec![];
|
||||
let mut tiling: ImageTiling = ImageTiling::Optimal;
|
||||
let mut layouts: Vec<SubresourceLayout> = vec![];
|
||||
|
||||
if frame.format.modifier != DRM_FORMAT_MOD_INVALID {
|
||||
(0..frame.num_planes).for_each(|i| {
|
||||
let plane = &frame.planes[i];
|
||||
layouts.push(SubresourceLayout {
|
||||
offset: plane.offset.into(),
|
||||
size: 0,
|
||||
row_pitch: plane.stride as _,
|
||||
array_pitch: None,
|
||||
depth_pitch: None,
|
||||
});
|
||||
modifiers.push(frame.format.modifier);
|
||||
});
|
||||
tiling = ImageTiling::DrmFormatModifier;
|
||||
}
|
||||
|
||||
self.dmabuf_texture_ex(frame, tiling, layouts, &modifiers)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::all, clippy::pedantic)]
|
||||
pub(super) unsafe fn create_dmabuf_image(
|
||||
device: Arc<Device>,
|
||||
create_info: ImageCreateInfo,
|
||||
) -> Result<RawImage, VulkanError> {
|
||||
let &ImageCreateInfo {
|
||||
flags,
|
||||
image_type,
|
||||
format,
|
||||
ref view_formats,
|
||||
extent,
|
||||
array_layers,
|
||||
mip_levels,
|
||||
samples,
|
||||
tiling,
|
||||
usage,
|
||||
stencil_usage,
|
||||
ref sharing,
|
||||
initial_layout,
|
||||
ref drm_format_modifiers,
|
||||
ref drm_format_modifier_plane_layouts,
|
||||
external_memory_handle_types,
|
||||
_ne: _,
|
||||
} = &create_info;
|
||||
|
||||
let (sharing_mode, queue_family_index_count, p_queue_family_indices) = match sharing {
|
||||
Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, 0, &[] as _),
|
||||
Sharing::Concurrent(queue_family_indices) => (
|
||||
ash::vk::SharingMode::CONCURRENT,
|
||||
queue_family_indices.len() as u32,
|
||||
queue_family_indices.as_ptr(),
|
||||
),
|
||||
};
|
||||
|
||||
let mut create_info_vk = ash::vk::ImageCreateInfo {
|
||||
flags: flags.into(),
|
||||
image_type: image_type.into(),
|
||||
format: format.into(),
|
||||
extent: ash::vk::Extent3D {
|
||||
width: extent[0],
|
||||
height: extent[1],
|
||||
depth: extent[2],
|
||||
},
|
||||
mip_levels,
|
||||
array_layers,
|
||||
samples: samples.into(),
|
||||
tiling: tiling.into(),
|
||||
usage: usage.into(),
|
||||
sharing_mode,
|
||||
queue_family_index_count,
|
||||
p_queue_family_indices,
|
||||
initial_layout: initial_layout.into(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut drm_format_modifier_explicit_info_vk = None;
|
||||
let drm_format_modifier_plane_layouts_vk: SmallVec<[_; 4]>;
|
||||
let mut drm_format_modifier_list_info_vk = None;
|
||||
let mut external_memory_info_vk = None;
|
||||
let mut format_list_info_vk = None;
|
||||
let format_list_view_formats_vk: Vec<_>;
|
||||
let mut stencil_usage_info_vk = None;
|
||||
|
||||
if drm_format_modifiers.len() == 1 {
|
||||
drm_format_modifier_plane_layouts_vk = drm_format_modifier_plane_layouts
|
||||
.iter()
|
||||
.map(|subresource_layout| {
|
||||
let &SubresourceLayout {
|
||||
offset,
|
||||
size,
|
||||
row_pitch,
|
||||
array_pitch,
|
||||
depth_pitch,
|
||||
} = subresource_layout;
|
||||
|
||||
ash::vk::SubresourceLayout {
|
||||
offset,
|
||||
size,
|
||||
row_pitch,
|
||||
array_pitch: array_pitch.unwrap_or(0),
|
||||
depth_pitch: depth_pitch.unwrap_or(0),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let next = drm_format_modifier_explicit_info_vk.insert(
|
||||
ash::vk::ImageDrmFormatModifierExplicitCreateInfoEXT {
|
||||
drm_format_modifier: drm_format_modifiers[0],
|
||||
drm_format_modifier_plane_count: drm_format_modifier_plane_layouts_vk.len() as u32,
|
||||
p_plane_layouts: drm_format_modifier_plane_layouts_vk.as_ptr(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
next.p_next = create_info_vk.p_next;
|
||||
create_info_vk.p_next = next as *const _ as *const _;
|
||||
} else if drm_format_modifiers.len() > 1 {
|
||||
let next = drm_format_modifier_list_info_vk.insert(
|
||||
ash::vk::ImageDrmFormatModifierListCreateInfoEXT {
|
||||
drm_format_modifier_count: drm_format_modifiers.len() as u32,
|
||||
p_drm_format_modifiers: drm_format_modifiers.as_ptr(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
next.p_next = create_info_vk.p_next;
|
||||
create_info_vk.p_next = next as *const _ as *const _;
|
||||
}
|
||||
|
||||
if !external_memory_handle_types.is_empty() {
|
||||
let next = external_memory_info_vk.insert(ash::vk::ExternalMemoryImageCreateInfo {
|
||||
handle_types: external_memory_handle_types.into(),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
next.p_next = create_info_vk.p_next;
|
||||
create_info_vk.p_next = next as *const _ as *const _;
|
||||
}
|
||||
|
||||
if !view_formats.is_empty() {
|
||||
format_list_view_formats_vk = view_formats
|
||||
.iter()
|
||||
.copied()
|
||||
.map(ash::vk::Format::from)
|
||||
.collect();
|
||||
|
||||
let next = format_list_info_vk.insert(ash::vk::ImageFormatListCreateInfo {
|
||||
view_format_count: format_list_view_formats_vk.len() as u32,
|
||||
p_view_formats: format_list_view_formats_vk.as_ptr(),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
next.p_next = create_info_vk.p_next;
|
||||
create_info_vk.p_next = next as *const _ as *const _;
|
||||
}
|
||||
|
||||
if let Some(stencil_usage) = stencil_usage {
|
||||
let next = stencil_usage_info_vk.insert(ash::vk::ImageStencilUsageCreateInfo {
|
||||
stencil_usage: stencil_usage.into(),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
next.p_next = create_info_vk.p_next;
|
||||
create_info_vk.p_next = next as *const _ as *const _;
|
||||
}
|
||||
|
||||
let handle = {
|
||||
let fns = device.fns();
|
||||
let mut output = MaybeUninit::uninit();
|
||||
(fns.v1_0.create_image)(
|
||||
device.handle(),
|
||||
&create_info_vk,
|
||||
std::ptr::null(),
|
||||
output.as_mut_ptr(),
|
||||
)
|
||||
.result()
|
||||
.map_err(VulkanError::from)?;
|
||||
output.assume_init()
|
||||
};
|
||||
|
||||
RawImage::from_handle(device, handle, create_info)
|
||||
}
|
||||
|
||||
pub fn get_drm_formats(device: Arc<Device>) -> Vec<DrmFormat> {
|
||||
let possible_formats = [
|
||||
DRM_FORMAT_ABGR8888.into(),
|
||||
DRM_FORMAT_XBGR8888.into(),
|
||||
DRM_FORMAT_ARGB8888.into(),
|
||||
DRM_FORMAT_XRGB8888.into(),
|
||||
DRM_FORMAT_ABGR2101010.into(),
|
||||
DRM_FORMAT_XBGR2101010.into(),
|
||||
];
|
||||
|
||||
let mut final_formats = vec![];
|
||||
|
||||
for &f in &possible_formats {
|
||||
let Ok(vk_fmt) = fourcc_to_vk(f) else {
|
||||
continue;
|
||||
};
|
||||
let Ok(props) = device.physical_device().format_properties(vk_fmt) else {
|
||||
continue;
|
||||
};
|
||||
let mut fmt = DrmFormat {
|
||||
fourcc: f,
|
||||
modifiers: props
|
||||
.drm_format_modifier_properties
|
||||
.iter()
|
||||
// important bit: only allow single-plane
|
||||
.filter(|m| m.drm_format_modifier_plane_count == 1)
|
||||
.map(|m| m.drm_format_modifier)
|
||||
.collect(),
|
||||
};
|
||||
fmt.modifiers.push(DRM_FORMAT_MOD_INVALID); // implicit modifiers support
|
||||
final_formats.push(fmt);
|
||||
}
|
||||
log::debug!("Supported DRM formats:");
|
||||
for f in &final_formats {
|
||||
log::debug!(" {} {:?}", f.fourcc, f.modifiers);
|
||||
}
|
||||
final_formats
|
||||
}
|
||||
|
||||
pub fn fourcc_to_vk(fourcc: FourCC) -> anyhow::Result<Format> {
|
||||
match fourcc.value {
|
||||
DRM_FORMAT_ABGR8888 | DRM_FORMAT_XBGR8888 => Ok(Format::R8G8B8A8_UNORM),
|
||||
DRM_FORMAT_ARGB8888 | DRM_FORMAT_XRGB8888 => Ok(Format::B8G8R8A8_UNORM),
|
||||
DRM_FORMAT_ABGR2101010 | DRM_FORMAT_XBGR2101010 => Ok(Format::A2B10G10R10_UNORM_PACK32),
|
||||
_ => anyhow::bail!("Unsupported format {}", fourcc),
|
||||
}
|
||||
}
|
||||
666
wlx-overlay-s/src/graphics/mod.rs
Normal file
666
wlx-overlay-s/src/graphics/mod.rs
Normal 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]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user