1
0
Fork 0
forked from wry/wry

vulkan: create a vulkan allocator

This commit is contained in:
Julian Orth 2024-09-02 15:56:09 +02:00
parent 952bd31f48
commit 766a093780
15 changed files with 984 additions and 101 deletions

View file

@ -0,0 +1,761 @@
use {
crate::{
allocator::{
Allocator, AllocatorError, BufferObject, BufferUsage, MappedBuffer, BO_USE_RENDERING,
BO_USE_WRITE,
},
format::Format,
gfx_apis::vulkan::{
allocator::VulkanAllocator, command::VulkanCommandBuffer, device::VulkanDevice,
format::VulkanFormat, renderer::image_barrier, staging::VulkanStagingBuffer,
util::OnDrop, VulkanError,
},
utils::errorfmt::ErrorFmt,
video::{
dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec},
drm::Drm,
Modifier,
},
},
arrayvec::ArrayVec,
ash::vk::{
AccessFlags2, BindImageMemoryInfo, BindImagePlaneMemoryInfo, BufferImageCopy2,
BufferMemoryBarrier2, CommandBuffer, CommandBufferBeginInfo, CommandBufferSubmitInfo,
CommandBufferUsageFlags, CopyBufferToImageInfo2, CopyImageToBufferInfo2, DependencyInfo,
DeviceMemory, ExportMemoryAllocateInfo, Extent3D, ExternalMemoryHandleTypeFlags,
ExternalMemoryImageCreateInfo, Fence, FormatFeatureFlags, Image, ImageAspectFlags,
ImageCreateInfo, ImageDrmFormatModifierExplicitCreateInfoEXT,
ImageDrmFormatModifierListCreateInfoEXT, ImageDrmFormatModifierPropertiesEXT, ImageLayout,
ImageMemoryBarrier2, ImageMemoryRequirementsInfo2, ImagePlaneMemoryRequirementsInfo,
ImageSubresource, ImageSubresourceLayers, ImageTiling, ImageType, ImageUsageFlags,
ImportMemoryFdInfoKHR, MemoryAllocateInfo, MemoryDedicatedAllocateInfo,
MemoryFdPropertiesKHR, MemoryGetFdInfoKHR, MemoryPropertyFlags, MemoryRequirements2,
PipelineStageFlags2, SampleCountFlags, SharingMode, SubmitInfo2, SubresourceLayout,
QUEUE_FAMILY_FOREIGN_EXT,
},
std::{rc::Rc, slice},
uapi::OwnedFd,
};
impl From<VulkanError> for AllocatorError {
fn from(value: VulkanError) -> Self {
Self(Box::new(value))
}
}
pub(super) struct VulkanBoAllocator {
data: Rc<VulkanBoAllocatorData>,
}
struct VulkanBoAllocatorData {
drm: Drm,
device: Rc<VulkanDevice>,
allocator: Rc<VulkanAllocator>,
command_buffer: Rc<VulkanCommandBuffer>,
}
struct VulkanBo {
allocator: Rc<VulkanBoAllocatorData>,
image: Image,
memory: PlaneVec<DeviceMemory>,
buf: DmaBuf,
}
struct VulkanBoMapping {
bo: Rc<VulkanBo>,
upload: bool,
stride: i32,
staging: VulkanStagingBuffer,
data: *mut [u8],
}
impl Drop for VulkanBo {
fn drop(&mut self) {
unsafe {
self.allocator.device.device.destroy_image(self.image, None);
for &memory in &self.memory {
self.allocator.device.device.free_memory(memory, None);
}
}
}
}
impl VulkanDevice {
pub(super) fn create_bo_allocator(
self: &Rc<Self>,
drm: &Drm,
) -> Result<VulkanBoAllocator, VulkanError> {
let allocator = self.create_allocator()?;
let pool = self.create_command_pool()?;
let command_buffer = pool.allocate_buffer()?;
let drm = drm.dup_render().map_err(VulkanError::DupDrm)?;
Ok(VulkanBoAllocator {
data: Rc::new(VulkanBoAllocatorData {
drm,
device: self.clone(),
allocator,
command_buffer,
}),
})
}
}
impl VulkanBoAllocator {
fn create_bo(
&self,
dma_buf_ids: &DmaBufIds,
width: i32,
height: i32,
format: &'static Format,
modifiers: &[Modifier],
usage: BufferUsage,
) -> Result<Rc<VulkanBo>, VulkanError> {
validate_usage(usage)?;
let data = &self.data;
let Some(format) = data.device.formats.get(&format.drm) else {
return Err(VulkanError::FormatNotSupported);
};
if width < 0 || height < 0 {
return Err(VulkanError::NonPositiveImageSize);
}
let width = width as u32;
let height = height as u32;
let image = {
let mut mods = vec![];
for &modifier in modifiers {
if validate_modifier(width, height, usage, false, None, format, modifier) {
mods.push(modifier);
}
}
if mods.is_empty() {
return Err(VulkanError::NoSupportedModifiers);
}
let mut mod_list =
ImageDrmFormatModifierListCreateInfoEXT::default().drm_format_modifiers(&mods);
let mut memory_image_create_info = ExternalMemoryImageCreateInfo::default()
.handle_types(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
let create_info = image_create_info(width, height, format.format, usage)
.push_next(&mut memory_image_create_info)
.push_next(&mut mod_list);
let res = unsafe { data.device.device.create_image(&create_info, None) };
res.map_err(VulkanError::CreateImage)?
};
let destroy_image = OnDrop(|| unsafe { data.device.device.destroy_image(image, None) });
let modifier = {
let mut props = ImageDrmFormatModifierPropertiesEXT::default();
unsafe {
data.device
.image_drm_format_modifier
.get_image_drm_format_modifier_properties(image, &mut props)
.map_err(VulkanError::GetModifier)?
}
props.drm_format_modifier
};
let Some(modifier) = format.modifiers.get(&modifier) else {
return Err(VulkanError::InvalidModifier)?;
};
let memory = {
let image_memory_requirements_info =
ImageMemoryRequirementsInfo2::default().image(image);
let mut memory_requirements = MemoryRequirements2::default();
unsafe {
data.device.device.get_image_memory_requirements2(
&image_memory_requirements_info,
&mut memory_requirements,
);
}
let memory_type_index = data
.device
.find_memory_type(
MemoryPropertyFlags::DEVICE_LOCAL,
memory_requirements.memory_requirements.memory_type_bits,
)
.ok_or(VulkanError::MemoryType)?;
let mut memory_dedicated_allocate_info =
MemoryDedicatedAllocateInfo::default().image(image);
let mut export_info = ExportMemoryAllocateInfo::default()
.handle_types(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
let memory_allocate_info = MemoryAllocateInfo::default()
.allocation_size(memory_requirements.memory_requirements.size)
.memory_type_index(memory_type_index)
.push_next(&mut memory_dedicated_allocate_info)
.push_next(&mut export_info);
let memory = unsafe {
data.device
.device
.allocate_memory(&memory_allocate_info, None)
};
memory.map_err(VulkanError::AllocateMemory)?
};
let destroy_memory = OnDrop(|| unsafe { data.device.device.free_memory(memory, None) });
unsafe {
data.device
.device
.bind_image_memory(image, memory, 0)
.map_err(VulkanError::BindImageMemory)?;
}
let fd = {
let get_info = MemoryGetFdInfoKHR::default()
.handle_type(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
.memory(memory);
let fd = unsafe { data.device.external_memory_fd.get_memory_fd(&get_info) };
fd.map_err(VulkanError::GetDmaBuf)
.map(OwnedFd::new)
.map(Rc::new)?
};
let mut planes = PlaneVec::new();
for i in 0..modifier.planes {
let flag = [
ImageAspectFlags::MEMORY_PLANE_0_EXT,
ImageAspectFlags::MEMORY_PLANE_1_EXT,
ImageAspectFlags::MEMORY_PLANE_2_EXT,
ImageAspectFlags::MEMORY_PLANE_3_EXT,
][i];
let layout = unsafe {
data.device.device.get_image_subresource_layout(
image,
ImageSubresource::default().aspect_mask(flag),
)
};
planes.push(DmaBufPlane {
offset: layout.offset as _,
stride: layout.row_pitch as _,
fd: fd.clone(),
});
}
let buf = DmaBuf {
id: dma_buf_ids.next(),
width: width as _,
height: height as _,
format: format.format,
modifier: modifier.modifier,
planes,
};
unsafe {
let cmd = data.command_buffer.buffer;
let device = &data.device.device;
let begin =
CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT);
let barrier = image_barrier()
.src_queue_family_index(data.device.graphics_queue_idx)
.dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
.old_layout(ImageLayout::UNDEFINED)
.new_layout(ImageLayout::GENERAL)
.image(image);
let dependency_info =
DependencyInfo::default().image_memory_barriers(slice::from_ref(&barrier));
let cmd_buffer_submit_info = CommandBufferSubmitInfo::default().command_buffer(cmd);
let submit_info = SubmitInfo2::default()
.command_buffer_infos(slice::from_ref(&cmd_buffer_submit_info));
device
.begin_command_buffer(cmd, &begin)
.map_err(VulkanError::BeginCommandBuffer)?;
device.cmd_pipeline_barrier2(cmd, &dependency_info);
device
.end_command_buffer(cmd)
.map_err(VulkanError::EndCommandBuffer)?;
device
.queue_submit2(
data.device.graphics_queue,
slice::from_ref(&submit_info),
Fence::null(),
)
.map_err(VulkanError::Submit)?;
device.device_wait_idle().map_err(VulkanError::WaitIdle)?;
}
destroy_image.forget();
destroy_memory.forget();
Ok(Rc::new(VulkanBo {
allocator: self.data.clone(),
image,
memory: [memory].into_iter().collect(),
buf,
}))
}
fn import_dmabuf(
&self,
dmabuf: &DmaBuf,
usage: BufferUsage,
) -> Result<Rc<VulkanBo>, VulkanError> {
validate_usage(usage)?;
let data = &self.data;
let Some(format) = data.device.formats.get(&dmabuf.format.drm) else {
return Err(VulkanError::FormatNotSupported);
};
if dmabuf.width < 0 || dmabuf.height < 0 {
return Err(VulkanError::NonPositiveImageSize);
}
let width = dmabuf.width as u32;
let height = dmabuf.height as u32;
let disjoint = dmabuf.is_disjoint();
let image = {
if !validate_modifier(
width,
height,
usage,
disjoint,
Some(dmabuf.planes.len()),
format,
dmabuf.modifier,
) {
return Err(VulkanError::ModifierNotSupported);
}
let plane_layouts: PlaneVec<_> = dmabuf
.planes
.iter()
.map(|p| {
SubresourceLayout::default()
.offset(p.offset as _)
.row_pitch(p.stride as _)
})
.collect();
let mut modifier_info = ImageDrmFormatModifierExplicitCreateInfoEXT::default()
.plane_layouts(&plane_layouts)
.drm_format_modifier(dmabuf.modifier);
let mut memory_image_create_info = ExternalMemoryImageCreateInfo::default()
.handle_types(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
let create_info = image_create_info(width, height, format.format, usage)
.push_next(&mut memory_image_create_info)
.push_next(&mut modifier_info);
let res = unsafe { data.device.device.create_image(&create_info, None) };
res.map_err(VulkanError::CreateImage)?
};
let destroy_image = OnDrop(|| unsafe { data.device.device.destroy_image(image, None) });
let num_device_memories = match disjoint {
true => dmabuf.planes.len(),
false => 1,
};
let mut device_memories = PlaneVec::new();
let mut free_device_memories = PlaneVec::new();
let mut bind_image_plane_memory_infos = PlaneVec::new();
for plane_idx in 0..num_device_memories {
let dma_buf_plane = &dmabuf.planes[plane_idx];
let mut memory_fd_properties = MemoryFdPropertiesKHR::default();
unsafe {
data.device
.external_memory_fd
.get_memory_fd_properties(
ExternalMemoryHandleTypeFlags::DMA_BUF_EXT,
dma_buf_plane.fd.raw(),
&mut memory_fd_properties,
)
.map_err(VulkanError::MemoryFdProperties)?;
}
let mut image_memory_requirements_info =
ImageMemoryRequirementsInfo2::default().image(image);
let mut image_plane_memory_requirements_info;
if disjoint {
let plane_aspect = [
ImageAspectFlags::MEMORY_PLANE_0_EXT,
ImageAspectFlags::MEMORY_PLANE_1_EXT,
ImageAspectFlags::MEMORY_PLANE_2_EXT,
ImageAspectFlags::MEMORY_PLANE_3_EXT,
][plane_idx];
image_plane_memory_requirements_info =
ImagePlaneMemoryRequirementsInfo::default().plane_aspect(plane_aspect);
image_memory_requirements_info = image_memory_requirements_info
.push_next(&mut image_plane_memory_requirements_info);
bind_image_plane_memory_infos
.push(BindImagePlaneMemoryInfo::default().plane_aspect(plane_aspect));
}
let mut memory_requirements = MemoryRequirements2::default();
unsafe {
data.device.device.get_image_memory_requirements2(
&image_memory_requirements_info,
&mut memory_requirements,
);
}
let memory_type_bits = memory_requirements.memory_requirements.memory_type_bits
& memory_fd_properties.memory_type_bits;
let memory_type_index = data
.device
.find_memory_type(MemoryPropertyFlags::empty(), memory_type_bits)
.ok_or(VulkanError::MemoryType)?;
let fd = uapi::fcntl_dupfd_cloexec(dma_buf_plane.fd.raw(), 0)
.map_err(|e| VulkanError::Dupfd(e.into()))?;
let mut memory_dedicated_allocate_info =
MemoryDedicatedAllocateInfo::default().image(image);
let mut import_memory_fd_info = ImportMemoryFdInfoKHR::default()
.fd(fd.raw())
.handle_type(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
let memory_allocate_info = MemoryAllocateInfo::default()
.allocation_size(memory_requirements.memory_requirements.size)
.memory_type_index(memory_type_index)
.push_next(&mut import_memory_fd_info)
.push_next(&mut memory_dedicated_allocate_info);
let device_memory = unsafe {
data.device
.device
.allocate_memory(&memory_allocate_info, None)
};
let device_memory = device_memory.map_err(VulkanError::AllocateMemory)?;
fd.unwrap();
device_memories.push(device_memory);
free_device_memories.push(OnDrop(move || unsafe {
data.device.device.free_memory(device_memory, None)
}));
}
let mut bind_image_memory_infos = Vec::with_capacity(num_device_memories);
let mut bind_image_plane_memory_infos = bind_image_plane_memory_infos.iter_mut();
for mem in device_memories.iter().copied() {
let mut info = BindImageMemoryInfo::default().image(image).memory(mem);
if disjoint {
info = info.push_next(bind_image_plane_memory_infos.next().unwrap());
}
bind_image_memory_infos.push(info);
}
let res = unsafe {
data.device
.device
.bind_image_memory2(&bind_image_memory_infos)
};
res.map_err(VulkanError::BindImageMemory)?;
destroy_image.forget();
free_device_memories.drain(..).for_each(|m| m.forget());
Ok(Rc::new(VulkanBo {
allocator: data.clone(),
image,
memory: device_memories,
buf: dmabuf.clone(),
}))
}
}
impl Allocator for VulkanBoAllocator {
fn drm(&self) -> Option<&Drm> {
Some(&self.data.drm)
}
fn create_bo(
&self,
dma_buf_ids: &DmaBufIds,
width: i32,
height: i32,
format: &'static Format,
modifiers: &[Modifier],
usage: BufferUsage,
) -> Result<Rc<dyn BufferObject>, AllocatorError> {
let bo = self.create_bo(dma_buf_ids, width, height, format, modifiers, usage)?;
Ok(bo)
}
fn import_dmabuf(
&self,
dmabuf: &DmaBuf,
usage: BufferUsage,
) -> Result<Rc<dyn BufferObject>, AllocatorError> {
let bo = self.import_dmabuf(dmabuf, usage)?;
Ok(bo)
}
}
impl VulkanBo {
fn map(self: &Rc<Self>, write: bool) -> Result<VulkanBoMapping, VulkanError> {
let format = self.buf.format;
let Some(shm_info) = &format.shm_info else {
return Err(VulkanError::ShmNotSupported);
};
let stride = self.buf.width as u32 * shm_info.bpp;
let size = self.buf.height as u32 * stride;
let data = &self.allocator;
let staging =
data.device
.create_staging_buffer(&data.allocator, size as _, write, true, true)?;
self.transfer(&staging, false, |cmd| {
let region = BufferImageCopy2::default()
.image_subresource(
ImageSubresourceLayers::default()
.aspect_mask(ImageAspectFlags::COLOR)
.layer_count(1),
)
.image_extent(Extent3D {
width: self.buf.width as _,
height: self.buf.height as _,
depth: 1,
});
let copy_info = CopyImageToBufferInfo2::default()
.src_image(self.image)
.src_image_layout(ImageLayout::TRANSFER_SRC_OPTIMAL)
.regions(slice::from_ref(&region))
.dst_buffer(staging.buffer);
unsafe {
data.device
.device
.cmd_copy_image_to_buffer2(cmd, &copy_info);
}
})?;
staging.download(|_, _| ())?;
let data = unsafe {
slice::from_raw_parts_mut(
staging.allocation.mem.unwrap(),
staging.allocation.size as _,
)
};
Ok(VulkanBoMapping {
bo: self.clone(),
upload: write,
stride: stride as _,
staging,
data,
})
}
fn transfer<F>(
&self,
staging: &VulkanStagingBuffer,
write: bool,
f: F,
) -> Result<(), VulkanError>
where
F: FnOnce(CommandBuffer),
{
let data = &self.allocator;
let cmd = data.command_buffer.buffer;
let device = &data.device.device;
let begin =
CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT);
let initial_image_barrier = self.initial_image_barrier(write);
let final_image_barrier = self.final_image_barrier(write);
let mut initial_buffer_barrier = ArrayVec::<_, 1>::new();
let mut final_buffer_barrier = ArrayVec::<_, 1>::new();
if write {
initial_buffer_barrier.push(
BufferMemoryBarrier2::default()
.src_access_mask(AccessFlags2::HOST_WRITE)
.src_stage_mask(PipelineStageFlags2::HOST)
.dst_access_mask(AccessFlags2::TRANSFER_READ)
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
.buffer(staging.buffer)
.size(staging.size),
);
} else {
final_buffer_barrier.push(
BufferMemoryBarrier2::default()
.src_access_mask(AccessFlags2::TRANSFER_WRITE)
.src_stage_mask(PipelineStageFlags2::TRANSFER)
.dst_access_mask(AccessFlags2::HOST_READ | AccessFlags2::HOST_WRITE)
.dst_stage_mask(PipelineStageFlags2::HOST)
.buffer(staging.buffer)
.size(staging.size),
);
}
let initial_dependency_info = DependencyInfo::default()
.image_memory_barriers(slice::from_ref(&initial_image_barrier))
.buffer_memory_barriers(&initial_buffer_barrier);
let final_dependency_info = DependencyInfo::default()
.image_memory_barriers(slice::from_ref(&final_image_barrier))
.buffer_memory_barriers(&final_buffer_barrier);
let cmd_buffer_submit_info = CommandBufferSubmitInfo::default().command_buffer(cmd);
let submit_info =
SubmitInfo2::default().command_buffer_infos(slice::from_ref(&cmd_buffer_submit_info));
unsafe {
device
.begin_command_buffer(cmd, &begin)
.map_err(VulkanError::BeginCommandBuffer)?;
device.cmd_pipeline_barrier2(cmd, &initial_dependency_info);
f(cmd);
device.cmd_pipeline_barrier2(cmd, &final_dependency_info);
device
.end_command_buffer(cmd)
.map_err(VulkanError::EndCommandBuffer)?;
device
.queue_submit2(
data.device.graphics_queue,
slice::from_ref(&submit_info),
Fence::null(),
)
.map_err(VulkanError::Submit)?;
device.device_wait_idle().map_err(VulkanError::WaitIdle)?;
}
Ok(())
}
fn get_image_barrier_flags(&self, write: bool) -> (ImageLayout, AccessFlags2) {
let layout;
let access_mask;
match write {
false => {
layout = ImageLayout::TRANSFER_SRC_OPTIMAL;
access_mask = AccessFlags2::TRANSFER_READ;
}
true => {
layout = ImageLayout::TRANSFER_DST_OPTIMAL;
access_mask = AccessFlags2::TRANSFER_WRITE;
}
}
(layout, access_mask)
}
fn initial_image_barrier(&self, write: bool) -> ImageMemoryBarrier2<'static> {
let (new_layout, dst_access_mask) = self.get_image_barrier_flags(write);
image_barrier()
.src_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
.dst_queue_family_index(self.allocator.device.graphics_queue_idx)
.old_layout(ImageLayout::GENERAL)
.new_layout(new_layout)
.dst_access_mask(dst_access_mask)
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
.image(self.image)
}
fn final_image_barrier(&self, write: bool) -> ImageMemoryBarrier2<'static> {
let (old_layout, src_access_mask) = self.get_image_barrier_flags(write);
image_barrier()
.src_queue_family_index(self.allocator.device.graphics_queue_idx)
.dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
.old_layout(old_layout)
.new_layout(ImageLayout::GENERAL)
.src_access_mask(src_access_mask)
.src_stage_mask(PipelineStageFlags2::TRANSFER)
.image(self.image)
}
}
impl BufferObject for VulkanBo {
fn dmabuf(&self) -> &DmaBuf {
&self.buf
}
fn map_read(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError> {
let m = self.map(false)?;
Ok(Box::new(m))
}
fn map_write(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError> {
let m = self.map(true)?;
Ok(Box::new(m))
}
}
impl VulkanBoMapping {
fn upload(&self) -> Result<(), VulkanError> {
let data = &self.bo.allocator;
self.staging.upload(|_, _| ())?;
self.bo.transfer(&self.staging, true, |cmd| {
let region = BufferImageCopy2::default()
.image_subresource(
ImageSubresourceLayers::default()
.aspect_mask(ImageAspectFlags::COLOR)
.layer_count(1),
)
.image_extent(Extent3D {
width: self.bo.buf.width as _,
height: self.bo.buf.height as _,
depth: 1,
});
let copy_info = CopyBufferToImageInfo2::default()
.dst_image(self.bo.image)
.dst_image_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
.regions(slice::from_ref(&region))
.src_buffer(self.staging.buffer);
unsafe {
data.device
.device
.cmd_copy_buffer_to_image2(cmd, &copy_info);
}
})?;
Ok(())
}
}
impl Drop for VulkanBoMapping {
fn drop(&mut self) {
if self.upload {
if let Err(e) = self.upload() {
log::error!("Could not upload to image: {}", ErrorFmt(e));
}
}
}
}
impl MappedBuffer for VulkanBoMapping {
unsafe fn data(&self) -> &[u8] {
&*self.data
}
fn data_ptr(&self) -> *mut u8 {
self.data as _
}
fn stride(&self) -> i32 {
self.stride
}
}
fn validate_usage(usage: BufferUsage) -> Result<(), VulkanError> {
if usage.contains(!(BO_USE_WRITE | BO_USE_RENDERING)) {
return Err(VulkanError::UnsupportedBufferUsage);
}
Ok(())
}
fn map_usage(usage: BufferUsage) -> ImageUsageFlags {
let mut vk_usage = ImageUsageFlags::TRANSFER_SRC | ImageUsageFlags::TRANSFER_DST;
if usage.contains(BO_USE_RENDERING) {
vk_usage |= ImageUsageFlags::COLOR_ATTACHMENT;
}
vk_usage
}
fn validate_modifier(
width: u32,
height: u32,
usage: BufferUsage,
disjoint: bool,
plane_count: Option<usize>,
format: &VulkanFormat,
modifier: Modifier,
) -> bool {
let Some(modifier) = format.modifiers.get(&modifier) else {
return false;
};
if disjoint && !modifier.features.contains(FormatFeatureFlags::DISJOINT) {
return false;
}
if let Some(plane_count) = plane_count {
if plane_count != modifier.planes {
return false;
}
}
let Some(max_extents) = modifier.transfer_max_extents else {
return false;
};
let mut max_width = max_extents.width;
let mut max_height = max_extents.height;
if usage.contains(BO_USE_RENDERING) {
let Some(max_extents) = modifier.render_max_extents else {
return false;
};
max_width = max_width.min(max_extents.width);
max_height = max_height.min(max_extents.height);
}
if width > max_width || height > max_height {
return false;
}
true
}
fn image_create_info(
width: u32,
height: u32,
format: &Format,
usage: BufferUsage,
) -> ImageCreateInfo {
let usage = map_usage(usage);
ImageCreateInfo::default()
.image_type(ImageType::TYPE_2D)
.format(format.vk_format)
.mip_levels(1)
.array_layers(1)
.tiling(ImageTiling::DRM_FORMAT_MODIFIER_EXT)
.samples(SampleCountFlags::TYPE_1)
.sharing_mode(SharingMode::EXCLUSIVE)
.initial_layout(ImageLayout::UNDEFINED)
.extent(Extent3D {
width,
height,
depth: 1,
})
.usage(usage)
}

View file

@ -57,6 +57,7 @@ pub struct VulkanDevice {
pub(super) external_semaphore_fd: external_semaphore_fd::Device,
pub(super) external_fence_fd: external_fence_fd::Device,
pub(super) push_descriptor: push_descriptor::Device,
pub(super) image_drm_format_modifier: image_drm_format_modifier::Device,
pub(super) formats: AHashMap<u32, VulkanFormat>,
pub(super) memory_types: ArrayVec<MemoryType, MAX_MEMORY_TYPES>,
pub(super) graphics_queue: Queue,
@ -104,7 +105,8 @@ impl VulkanInstance {
Err(e) => return Err(VulkanError::Fstat(e.into())),
};
let dev = stat.st_rdev;
log::info!(
log::log!(
self.log_level,
"Searching for vulkan device with devnum {}:{}",
uapi::major(dev),
uapi::minor(dev)
@ -153,8 +155,13 @@ impl VulkanInstance {
let render_dev =
uapi::makedev(drm_props.render_major as _, drm_props.render_minor as _);
if primary_dev == dev || render_dev == dev {
log::info!("Device with id {} matches", props.device_id);
log_device(&props, Some(&extensions), Some(&driver_props));
log::log!(self.log_level, "Device with id {} matches", props.device_id);
log_device(
self.log_level,
&props,
Some(&extensions),
Some(&driver_props),
);
return Ok(phy_dev);
}
devices.push((props, Some(extensions), Some(driver_props)));
@ -166,7 +173,12 @@ impl VulkanInstance {
for (props, extensions, driver_props) in devices.iter() {
log::warn!("Found the following devices but none matches:");
log::warn!("-----");
log_device(props, extensions.as_ref(), driver_props.as_ref());
log_device(
self.log_level,
props,
extensions.as_ref(),
driver_props.as_ref(),
);
}
}
Err(VulkanError::NoDeviceFound(dev))
@ -264,6 +276,8 @@ impl VulkanInstance {
let external_semaphore_fd = external_semaphore_fd::Device::new(&self.instance, &device);
let external_fence_fd = external_fence_fd::Device::new(&self.instance, &device);
let push_descriptor = push_descriptor::Device::new(&self.instance, &device);
let image_drm_format_modifier =
image_drm_format_modifier::Device::new(&self.instance, &device);
let memory_properties =
unsafe { self.instance.get_physical_device_memory_properties(phy_dev) };
let memory_types = memory_properties.memory_types
@ -283,6 +297,7 @@ impl VulkanInstance {
external_semaphore_fd,
external_fence_fd,
push_descriptor,
image_drm_format_modifier,
formats,
memory_types,
graphics_queue,
@ -302,20 +317,27 @@ const REQUIRED_DEVICE_EXTENSIONS: &[&CStr] = &[
];
fn log_device(
level: log::Level,
props: &PhysicalDeviceProperties,
extensions: Option<&Extensions>,
driver_props: Option<&PhysicalDeviceDriverProperties>,
) {
log::info!(" api version: {}", ApiVersionDisplay(props.api_version));
log::info!(
log::log!(
level,
" api version: {}",
ApiVersionDisplay(props.api_version)
);
log::log!(
level,
" driver version: {}",
ApiVersionDisplay(props.driver_version)
);
log::info!(" vendor id: {}", props.vendor_id);
log::info!(" device id: {}", props.device_id);
log::info!(" device type: {:?}", props.device_type);
log::log!(level, " vendor id: {}", props.vendor_id);
log::log!(level, " device id: {}", props.device_id);
log::log!(level, " device type: {:?}", props.device_type);
unsafe {
log::info!(
log::log!(
level,
" device name: {}",
Ustr::from_ptr(props.device_name.as_ptr()).display()
);
@ -330,7 +352,8 @@ fn log_device(
}
if let Some(driver_props) = driver_props {
unsafe {
log::info!(
log::log!(
level,
" driver: {} ({})",
Ustr::from_ptr(driver_props.driver_name.as_ptr()).display(),
Ustr::from_ptr(driver_props.driver_info.as_ptr()).display()

View file

@ -34,6 +34,7 @@ pub struct VulkanModifier {
pub features: FormatFeatureFlags,
pub render_max_extents: Option<VulkanMaxExtents>,
pub texture_max_extents: Option<VulkanMaxExtents>,
pub transfer_max_extents: Option<VulkanMaxExtents>,
pub render_needs_bridge: bool,
}
@ -58,24 +59,24 @@ const TEX_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw(
| FormatFeatureFlags::TRANSFER_SRC.as_raw()
| FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR.as_raw(),
);
const SHM_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw(
0 | FormatFeatureFlags::TRANSFER_SRC.as_raw()
| FormatFeatureFlags::TRANSFER_DST.as_raw()
| TEX_FEATURES.as_raw(),
const TRANSFER_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw(
FormatFeatureFlags::TRANSFER_SRC.as_raw() | FormatFeatureFlags::TRANSFER_DST.as_raw(),
);
const SHM_FEATURES: FormatFeatureFlags =
FormatFeatureFlags::from_raw(TRANSFER_FEATURES.as_raw() | TEX_FEATURES.as_raw());
const FRAMEBUFFER_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw(
0 | ImageUsageFlags::COLOR_ATTACHMENT.as_raw() | ImageUsageFlags::TRANSFER_SRC.as_raw(),
ImageUsageFlags::COLOR_ATTACHMENT.as_raw() | ImageUsageFlags::TRANSFER_SRC.as_raw(),
);
const FRAMEBUFFER_BRIDGED_USAGE: ImageUsageFlags = ImageUsageFlags::TRANSFER_DST;
const TEX_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw(
0 | ImageUsageFlags::SAMPLED.as_raw() | ImageUsageFlags::TRANSFER_SRC.as_raw(),
ImageUsageFlags::SAMPLED.as_raw() | ImageUsageFlags::TRANSFER_SRC.as_raw(),
);
const SHM_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw(
0 | ImageUsageFlags::TRANSFER_SRC.as_raw()
| ImageUsageFlags::TRANSFER_DST.as_raw()
| TEX_USAGE.as_raw(),
const TRANSFER_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw(
ImageUsageFlags::TRANSFER_SRC.as_raw() | ImageUsageFlags::TRANSFER_DST.as_raw(),
);
const SHM_USAGE: ImageUsageFlags =
ImageUsageFlags::from_raw(TRANSFER_USAGE.as_raw() | TEX_USAGE.as_raw());
impl VulkanInstance {
pub(super) fn load_formats(
@ -209,6 +210,13 @@ impl VulkanInstance {
)?;
let texture_max_extents =
self.get_max_extents(phy_dev, format, TEX_FEATURES, TEX_USAGE, &modifier)?;
let transfer_max_extents = self.get_max_extents(
phy_dev,
format,
TRANSFER_FEATURES,
TRANSFER_USAGE,
&modifier,
)?;
let mut render_needs_bridge = false;
if render_max_extents.is_none() && modifier.drm_format_modifier == LINEAR_MODIFIER {
render_max_extents = self.get_fb_bridged_max_extents(
@ -229,6 +237,7 @@ impl VulkanInstance {
features: modifier.drm_format_modifier_tiling_features,
render_max_extents,
texture_max_extents,
transfer_max_extents,
render_needs_bridge,
},
);

View file

@ -1,9 +1,5 @@
use {
crate::{
async_engine::AsyncEngine,
gfx_apis::vulkan::{util::OnDrop, VulkanError, VULKAN_VALIDATION},
io_uring::IoUring,
},
crate::gfx_apis::vulkan::{util::OnDrop, VulkanError, VULKAN_VALIDATION},
ahash::{AHashMap, AHashSet},
ash::{
ext::{debug_utils, validation_features},
@ -35,16 +31,11 @@ pub struct VulkanInstance {
pub(super) instance: Instance,
pub(super) debug_utils: debug_utils::Instance,
pub(super) messenger: DebugUtilsMessengerEXT,
pub(super) eng: Rc<AsyncEngine>,
pub(super) ring: Rc<IoUring>,
pub(super) log_level: Level,
}
impl VulkanInstance {
pub fn new(
eng: &Rc<AsyncEngine>,
ring: &Rc<IoUring>,
validation: bool,
) -> Result<Rc<Self>, VulkanError> {
pub fn new(log_level: Level, validation: bool) -> Result<Rc<Self>, VulkanError> {
static ENTRY: Lazy<Result<Entry, Arc<LoadingError>>> =
Lazy::new(|| unsafe { Entry::load() }.map_err(Arc::new));
let entry = match &*ENTRY {
@ -127,8 +118,7 @@ impl VulkanInstance {
instance,
debug_utils,
messenger,
eng: eng.clone(),
ring: ring.clone(),
log_level,
}))
}
}

View file

@ -1,6 +1,6 @@
use {
crate::{
async_engine::SpawnedFuture,
async_engine::{AsyncEngine, SpawnedFuture},
format::Format,
gfx_api::{
AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxFormat, GfxFramebuffer,
@ -68,6 +68,8 @@ pub struct VulkanRenderer {
pub(super) allocator: Rc<VulkanAllocator>,
pub(super) last_point: NumCell<u64>,
pub(super) buffer_resv_user: BufferResvUser,
pub(super) eng: Rc<AsyncEngine>,
pub(super) ring: Rc<IoUring>,
}
pub(super) struct UsedTexture {
@ -111,7 +113,11 @@ pub(super) struct PendingFrame {
}
impl VulkanDevice {
pub fn create_renderer(self: &Rc<Self>) -> Result<Rc<VulkanRenderer>, VulkanError> {
pub fn create_renderer(
self: &Rc<Self>,
eng: &Rc<AsyncEngine>,
ring: &Rc<IoUring>,
) -> Result<Rc<VulkanRenderer>, VulkanError> {
let fill_pipeline = self.create_pipeline::<FillVertPushConstants, FillFragPushConstants>(
PipelineCreateInfo {
vert: self.create_shader(FILL_VERT)?,
@ -196,6 +202,8 @@ impl VulkanDevice {
allocator,
last_point: Default::default(),
buffer_resv_user: Default::default(),
eng: eng.clone(),
ring: ring.clone(),
}))
}
}
@ -691,9 +699,9 @@ impl VulkanRenderer {
_release_fence: memory.release_fence.take(),
});
self.pending_frames.set(frame.point, frame.clone());
let future = self.device.instance.eng.spawn(await_release(
let future = self.eng.spawn(await_release(
memory.release_sync_file.clone(),
self.device.instance.ring.clone(),
self.ring.clone(),
frame.clone(),
self.clone(),
));
@ -776,7 +784,9 @@ impl VulkanRenderer {
height: tex.height,
depth: 1,
});
let staging = self.create_staging_buffer(size, false, true, true)?;
let staging =
self.device
.create_staging_buffer(&self.allocator, size, false, true, true)?;
let initial_tex_barrier;
let initial_buffer_barrier = BufferMemoryBarrier2::default()
.buffer(staging.buffer)

View file

@ -104,9 +104,13 @@ impl VulkanShmImage {
cpy = slice::from_ref(&cpy_one);
total_size = img.height * img.stride;
}
let staging = img
.renderer
.create_staging_buffer(total_size as u64, true, false, true)?;
let staging = img.renderer.device.create_staging_buffer(
&img.renderer.allocator,
total_size as u64,
true,
false,
true,
)?;
staging.upload(|mem, _| unsafe {
let buf = buffer.as_ptr() as *const u8;
if damage.is_some() {
@ -215,7 +219,7 @@ impl VulkanShmImage {
}
};
let point = img.renderer.allocate_point();
let future = img.renderer.device.instance.eng.spawn(await_upload(
let future = img.renderer.eng.spawn(await_upload(
point,
img.clone(),
cmd,
@ -236,13 +240,7 @@ async fn await_upload(
_fence: Rc<VulkanFence>,
_staging: VulkanStagingBuffer,
) {
let res = img
.renderer
.device
.instance
.ring
.readable(&sync_file.0)
.await;
let res = img.renderer.ring.readable(&sync_file.0).await;
if let Err(e) = res {
log::error!(
"Could not wait for sync file to become readable: {}",

View file

@ -1,6 +1,8 @@
use {
crate::gfx_apis::vulkan::{
allocator::VulkanAllocation, device::VulkanDevice, renderer::VulkanRenderer, util::OnDrop,
allocator::{VulkanAllocation, VulkanAllocator},
device::VulkanDevice,
util::OnDrop,
VulkanError,
},
ash::vk::{Buffer, BufferCreateInfo, BufferUsageFlags, MappedMemoryRange},
@ -15,9 +17,10 @@ pub struct VulkanStagingBuffer {
pub(super) size: u64,
}
impl VulkanRenderer {
impl VulkanDevice {
pub(super) fn create_staging_buffer(
self: &Rc<Self>,
allocator: &Rc<VulkanAllocator>,
size: u64,
upload: bool,
download: bool,
@ -38,24 +41,22 @@ impl VulkanRenderer {
}
let buffer = {
let create_info = BufferCreateInfo::default().size(size).usage(vk_usage);
let buffer = unsafe { self.device.device.create_buffer(&create_info, None) };
let buffer = unsafe { self.device.create_buffer(&create_info, None) };
buffer.map_err(VulkanError::CreateBuffer)?
};
let destroy_buffer = OnDrop(|| unsafe { self.device.device.destroy_buffer(buffer, None) });
let memory_requirements =
unsafe { self.device.device.get_buffer_memory_requirements(buffer) };
let allocation = self.allocator.alloc(&memory_requirements, usage, true)?;
let destroy_buffer = OnDrop(|| unsafe { self.device.destroy_buffer(buffer, None) });
let memory_requirements = unsafe { self.device.get_buffer_memory_requirements(buffer) };
let allocation = allocator.alloc(&memory_requirements, usage, true)?;
{
let res = unsafe {
self.device
.device
.bind_buffer_memory(buffer, allocation.memory, allocation.offset)
};
res.map_err(VulkanError::BindBufferMemory)?;
}
destroy_buffer.forget();
Ok(VulkanStagingBuffer {
device: self.device.clone(),
device: self.clone(),
allocation,
buffer,
size,