vulkan: optimize shm handling
This commit is contained in:
parent
03c02be34c
commit
af80fada6c
14 changed files with 629 additions and 413 deletions
|
|
@ -4,7 +4,7 @@ use {
|
|||
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture, SyncFile},
|
||||
gfx_apis::vulkan::{
|
||||
allocator::VulkanAllocation, device::VulkanDevice, format::VulkanMaxExtents,
|
||||
renderer::VulkanRenderer, util::OnDrop, VulkanError,
|
||||
renderer::VulkanRenderer, shm_image::VulkanShmImage, util::OnDrop, VulkanError,
|
||||
},
|
||||
theme::Color,
|
||||
utils::clonecell::CloneCell,
|
||||
|
|
@ -12,19 +12,18 @@ use {
|
|||
},
|
||||
ash::vk::{
|
||||
BindImageMemoryInfo, BindImagePlaneMemoryInfo, ComponentMapping, ComponentSwizzle,
|
||||
DeviceMemory, DeviceSize, Extent3D, ExternalMemoryHandleTypeFlags,
|
||||
ExternalMemoryImageCreateInfo, FormatFeatureFlags, Image, ImageAspectFlags,
|
||||
ImageCreateFlags, ImageCreateInfo, ImageDrmFormatModifierExplicitCreateInfoEXT,
|
||||
ImageLayout, ImageMemoryRequirementsInfo2, ImagePlaneMemoryRequirementsInfo,
|
||||
ImageSubresourceRange, ImageTiling, ImageType, ImageUsageFlags, ImageView,
|
||||
ImageViewCreateInfo, ImageViewType, ImportMemoryFdInfoKHR, MemoryAllocateInfo,
|
||||
MemoryDedicatedAllocateInfo, MemoryPropertyFlags, MemoryRequirements2, SampleCountFlags,
|
||||
SharingMode, SubresourceLayout,
|
||||
DeviceMemory, Extent3D, ExternalMemoryHandleTypeFlags, ExternalMemoryImageCreateInfo,
|
||||
FormatFeatureFlags, Image, ImageAspectFlags, ImageCreateFlags, ImageCreateInfo,
|
||||
ImageDrmFormatModifierExplicitCreateInfoEXT, ImageLayout, ImageMemoryRequirementsInfo2,
|
||||
ImagePlaneMemoryRequirementsInfo, ImageSubresourceRange, ImageTiling, ImageType,
|
||||
ImageUsageFlags, ImageView, ImageViewCreateInfo, ImageViewType, ImportMemoryFdInfoKHR,
|
||||
MemoryAllocateInfo, MemoryDedicatedAllocateInfo, MemoryPropertyFlags, MemoryRequirements2,
|
||||
SampleCountFlags, SharingMode, SubresourceLayout,
|
||||
},
|
||||
gpu_alloc::UsageFlags,
|
||||
std::{
|
||||
any::Any,
|
||||
cell::{Cell, RefCell},
|
||||
cell::Cell,
|
||||
fmt::{Debug, Formatter},
|
||||
mem,
|
||||
rc::Rc,
|
||||
|
|
@ -67,13 +66,6 @@ pub struct VulkanDmaBufImage {
|
|||
pub(super) mems: PlaneVec<DeviceMemory>,
|
||||
}
|
||||
|
||||
pub struct VulkanShmImage {
|
||||
pub(super) to_flush: RefCell<Option<Vec<u8>>>,
|
||||
pub(super) size: DeviceSize,
|
||||
pub(super) stride: u32,
|
||||
pub(super) _allocation: VulkanAllocation,
|
||||
}
|
||||
|
||||
pub struct VulkanFramebufferBridge {
|
||||
pub(super) dmabuf_image: Image,
|
||||
pub(super) _allocation: VulkanAllocation,
|
||||
|
|
@ -113,124 +105,7 @@ impl Drop for VulkanImage {
|
|||
}
|
||||
}
|
||||
|
||||
impl VulkanShmImage {
|
||||
pub fn upload(&self, buffer: &[Cell<u8>]) -> Result<(), VulkanError> {
|
||||
let buffer = unsafe {
|
||||
std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len()).to_vec()
|
||||
};
|
||||
*self.to_flush.borrow_mut() = Some(buffer);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanRenderer {
|
||||
pub fn create_shm_texture(
|
||||
self: &Rc<Self>,
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
data: &[Cell<u8>],
|
||||
for_download: bool,
|
||||
) -> Result<Rc<VulkanImage>, VulkanError> {
|
||||
let Some(shm_info) = &format.shm_info else {
|
||||
return Err(VulkanError::UnsupportedShmFormat(format.name));
|
||||
};
|
||||
if width <= 0 || height <= 0 || stride <= 0 {
|
||||
return Err(VulkanError::NonPositiveImageSize);
|
||||
}
|
||||
let width = width as u32;
|
||||
let height = height as u32;
|
||||
let stride = stride as u32;
|
||||
if stride % shm_info.bpp != 0 || stride / shm_info.bpp < width {
|
||||
return Err(VulkanError::InvalidStride);
|
||||
}
|
||||
let vk_format = self
|
||||
.device
|
||||
.formats
|
||||
.get(&format.drm)
|
||||
.ok_or(VulkanError::FormatNotSupported)?;
|
||||
let shm = vk_format.shm.as_ref().ok_or(VulkanError::ShmNotSupported)?;
|
||||
if width > shm.max_extents.width || height > shm.max_extents.height {
|
||||
return Err(VulkanError::ImageTooLarge);
|
||||
}
|
||||
let size = stride.checked_mul(height).ok_or(VulkanError::ShmOverflow)?;
|
||||
let usage = ImageUsageFlags::TRANSFER_SRC
|
||||
| match for_download {
|
||||
true => ImageUsageFlags::COLOR_ATTACHMENT,
|
||||
false => ImageUsageFlags::SAMPLED | ImageUsageFlags::TRANSFER_DST,
|
||||
};
|
||||
let create_info = ImageCreateInfo::builder()
|
||||
.image_type(ImageType::TYPE_2D)
|
||||
.format(format.vk_format)
|
||||
.mip_levels(1)
|
||||
.array_layers(1)
|
||||
.tiling(ImageTiling::OPTIMAL)
|
||||
.samples(SampleCountFlags::TYPE_1)
|
||||
.sharing_mode(SharingMode::EXCLUSIVE)
|
||||
.initial_layout(ImageLayout::UNDEFINED)
|
||||
.extent(Extent3D {
|
||||
width,
|
||||
height,
|
||||
depth: 1,
|
||||
})
|
||||
.usage(usage)
|
||||
.build();
|
||||
let image = unsafe { self.device.device.create_image(&create_info, None) };
|
||||
let image = image.map_err(VulkanError::CreateImage)?;
|
||||
let destroy_image = OnDrop(|| unsafe { self.device.device.destroy_image(image, None) });
|
||||
let memory_requirements =
|
||||
unsafe { self.device.device.get_image_memory_requirements(image) };
|
||||
let allocation =
|
||||
self.allocator
|
||||
.alloc(&memory_requirements, UsageFlags::FAST_DEVICE_ACCESS, false)?;
|
||||
let res = unsafe {
|
||||
self.device
|
||||
.device
|
||||
.bind_image_memory(image, allocation.memory, allocation.offset)
|
||||
};
|
||||
res.map_err(VulkanError::BindImageMemory)?;
|
||||
let image_view_create_info = ImageViewCreateInfo::builder()
|
||||
.image(image)
|
||||
.format(format.vk_format)
|
||||
.view_type(ImageViewType::TYPE_2D)
|
||||
.subresource_range(ImageSubresourceRange {
|
||||
aspect_mask: ImageAspectFlags::COLOR,
|
||||
base_mip_level: 0,
|
||||
level_count: 1,
|
||||
base_array_layer: 0,
|
||||
layer_count: 1,
|
||||
});
|
||||
let view = unsafe {
|
||||
self.device
|
||||
.device
|
||||
.create_image_view(&image_view_create_info, None)
|
||||
};
|
||||
let view = view.map_err(VulkanError::CreateImageView)?;
|
||||
let shm = VulkanShmImage {
|
||||
to_flush: Default::default(),
|
||||
size: size as u64,
|
||||
stride,
|
||||
_allocation: allocation,
|
||||
};
|
||||
shm.upload(data)?;
|
||||
destroy_image.forget();
|
||||
Ok(Rc::new(VulkanImage {
|
||||
renderer: self.clone(),
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
stride,
|
||||
texture_view: view,
|
||||
render_view: None,
|
||||
image,
|
||||
is_undefined: Cell::new(true),
|
||||
ty: VulkanImageMemory::Internal(shm),
|
||||
render_ops: Default::default(),
|
||||
bridge: None,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn import_dmabuf(
|
||||
self: &Rc<Self>,
|
||||
dmabuf: &DmaBuf,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ use {
|
|||
TexVertPushConstants, VulkanShader, FILL_FRAG, FILL_VERT, TEX_FRAG,
|
||||
TEX_FRAG_MULT_ALPHA, TEX_FRAG_MULT_OPAQUE, TEX_VERT,
|
||||
},
|
||||
staging::VulkanStagingBuffer,
|
||||
VulkanError,
|
||||
},
|
||||
io_uring::IoUring,
|
||||
|
|
@ -30,16 +29,15 @@ use {
|
|||
ahash::AHashMap,
|
||||
ash::{
|
||||
vk::{
|
||||
AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, BufferImageCopy, BufferImageCopy2,
|
||||
AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, BufferImageCopy,
|
||||
BufferMemoryBarrier2, ClearColorValue, ClearValue, CommandBuffer,
|
||||
CommandBufferBeginInfo, CommandBufferSubmitInfo, CommandBufferUsageFlags,
|
||||
CopyBufferToImageInfo2, CopyImageInfo2, DependencyInfo, DependencyInfoKHR,
|
||||
DescriptorImageInfo, DescriptorType, Extent2D, Extent3D, Fence, ImageAspectFlags,
|
||||
ImageCopy2, ImageLayout, ImageMemoryBarrier2, ImageMemoryBarrier2Builder,
|
||||
ImageSubresourceLayers, ImageSubresourceRange, PipelineBindPoint, PipelineStageFlags2,
|
||||
Rect2D, RenderingAttachmentInfo, RenderingInfo, SemaphoreSubmitInfo,
|
||||
SemaphoreSubmitInfoKHR, ShaderStageFlags, SubmitInfo2, Viewport, WriteDescriptorSet,
|
||||
QUEUE_FAMILY_FOREIGN_EXT,
|
||||
CopyImageInfo2, DependencyInfo, DependencyInfoKHR, DescriptorImageInfo, DescriptorType,
|
||||
Extent2D, Extent3D, Fence, ImageAspectFlags, ImageCopy2, ImageLayout,
|
||||
ImageMemoryBarrier2, ImageMemoryBarrier2Builder, ImageSubresourceLayers,
|
||||
ImageSubresourceRange, PipelineBindPoint, PipelineStageFlags2, Rect2D,
|
||||
RenderingAttachmentInfo, RenderingInfo, SemaphoreSubmitInfo, SemaphoreSubmitInfoKHR,
|
||||
ShaderStageFlags, SubmitInfo2, Viewport, WriteDescriptorSet, QUEUE_FAMILY_FOREIGN_EXT,
|
||||
},
|
||||
Device,
|
||||
},
|
||||
|
|
@ -66,6 +64,7 @@ pub struct VulkanRenderer {
|
|||
pub(super) total_buffers: NumCell<usize>,
|
||||
pub(super) memory: RefCell<Memory>,
|
||||
pub(super) pending_frames: CopyHashMap<u64, Rc<PendingFrame>>,
|
||||
pub(super) pending_uploads: CopyHashMap<u64, SpawnedFuture<()>>,
|
||||
pub(super) allocator: Rc<VulkanAllocator>,
|
||||
pub(super) last_point: NumCell<u64>,
|
||||
pub(super) buffer_resv_user: BufferResvUser,
|
||||
|
|
@ -93,11 +92,8 @@ pub(super) enum TexSourceType {
|
|||
#[derive(Default)]
|
||||
pub(super) struct Memory {
|
||||
sample: Vec<Rc<VulkanImage>>,
|
||||
flush: Vec<Rc<VulkanImage>>,
|
||||
flush_staging: Vec<(Rc<VulkanImage>, VulkanStagingBuffer)>,
|
||||
textures: Vec<UsedTexture>,
|
||||
image_barriers: Vec<ImageMemoryBarrier2>,
|
||||
shm_barriers: Vec<BufferMemoryBarrier2>,
|
||||
wait_semaphores: Vec<Rc<VulkanSemaphore>>,
|
||||
wait_semaphore_infos: Vec<SemaphoreSubmitInfo>,
|
||||
release_fence: Option<Rc<VulkanFence>>,
|
||||
|
|
@ -109,7 +105,6 @@ pub(super) struct PendingFrame {
|
|||
renderer: Rc<VulkanRenderer>,
|
||||
cmd: Cell<Option<Rc<VulkanCommandBuffer>>>,
|
||||
_textures: Vec<UsedTexture>,
|
||||
_staging: Vec<(Rc<VulkanImage>, VulkanStagingBuffer)>,
|
||||
wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>,
|
||||
waiter: Cell<Option<SpawnedFuture<()>>>,
|
||||
_release_fence: Option<Rc<VulkanFence>>,
|
||||
|
|
@ -197,6 +192,7 @@ impl VulkanDevice {
|
|||
total_buffers: Default::default(),
|
||||
memory: Default::default(),
|
||||
pending_frames: Default::default(),
|
||||
pending_uploads: Default::default(),
|
||||
allocator,
|
||||
last_point: Default::default(),
|
||||
buffer_resv_user: Default::default(),
|
||||
|
|
@ -205,20 +201,18 @@ impl VulkanDevice {
|
|||
}
|
||||
|
||||
impl VulkanRenderer {
|
||||
pub(super) fn allocate_point(&self) -> u64 {
|
||||
self.last_point.fetch_add(1) + 1
|
||||
}
|
||||
|
||||
fn collect_memory(&self, opts: &[GfxApiOpt]) {
|
||||
let mut memory = self.memory.borrow_mut();
|
||||
memory.sample.clear();
|
||||
memory.flush.clear();
|
||||
for cmd in opts {
|
||||
if let GfxApiOpt::CopyTexture(c) = cmd {
|
||||
let tex = c.tex.clone().into_vk(&self.device.device);
|
||||
match &tex.ty {
|
||||
VulkanImageMemory::DmaBuf(_) => memory.sample.push(tex.clone()),
|
||||
VulkanImageMemory::Internal(shm) => {
|
||||
if shm.to_flush.borrow_mut().is_some() {
|
||||
memory.flush.push(tex.clone());
|
||||
}
|
||||
}
|
||||
if let VulkanImageMemory::DmaBuf(_) = &tex.ty {
|
||||
memory.sample.push(tex.clone())
|
||||
}
|
||||
memory.textures.push(UsedTexture {
|
||||
tex,
|
||||
|
|
@ -241,32 +235,10 @@ impl VulkanRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
fn write_shm_staging_buffers(self: &Rc<Self>) -> Result<(), VulkanError> {
|
||||
let mut memory = self.memory.borrow_mut();
|
||||
let memory = &mut *memory;
|
||||
memory.flush_staging.clear();
|
||||
for img in &memory.flush {
|
||||
let shm = match &img.ty {
|
||||
VulkanImageMemory::DmaBuf(_) => unreachable!(),
|
||||
VulkanImageMemory::Internal(s) => s,
|
||||
};
|
||||
let staging = self.create_staging_buffer(shm.size, true, false, true)?;
|
||||
let to_flush = shm.to_flush.borrow_mut();
|
||||
let to_flush = to_flush.as_ref().unwrap();
|
||||
staging.upload(|mem, size| unsafe {
|
||||
let size = size.min(to_flush.len());
|
||||
ptr::copy_nonoverlapping(to_flush.as_ptr(), mem, size);
|
||||
})?;
|
||||
memory.flush_staging.push((img.clone(), staging));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn initial_barriers(&self, buf: CommandBuffer, fb: &VulkanImage) {
|
||||
let mut memory = self.memory.borrow_mut();
|
||||
let memory = &mut *memory;
|
||||
memory.image_barriers.clear();
|
||||
memory.shm_barriers.clear();
|
||||
let mut fb_image_memory_barrier = image_barrier()
|
||||
.image(fb.image)
|
||||
.new_layout(ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
|
||||
|
|
@ -307,89 +279,6 @@ impl VulkanRenderer {
|
|||
.build();
|
||||
memory.image_barriers.push(image_memory_barrier);
|
||||
}
|
||||
for (img, staging) in &memory.flush_staging {
|
||||
let image_memory_barrier = image_barrier()
|
||||
.image(img.image)
|
||||
.old_layout(if img.is_undefined.get() {
|
||||
ImageLayout::UNDEFINED
|
||||
} else {
|
||||
ImageLayout::SHADER_READ_ONLY_OPTIMAL
|
||||
})
|
||||
.new_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
|
||||
.dst_access_mask(AccessFlags2::TRANSFER_WRITE)
|
||||
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.build();
|
||||
memory.image_barriers.push(image_memory_barrier);
|
||||
let buffer_memory_barrier = BufferMemoryBarrier2::builder()
|
||||
.buffer(staging.buffer)
|
||||
.offset(0)
|
||||
.size(staging.size)
|
||||
.src_access_mask(AccessFlags2::HOST_WRITE)
|
||||
.src_stage_mask(PipelineStageFlags2::HOST)
|
||||
.dst_access_mask(AccessFlags2::TRANSFER_READ)
|
||||
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.build();
|
||||
memory.shm_barriers.push(buffer_memory_barrier);
|
||||
}
|
||||
let dep_info = DependencyInfoKHR::builder()
|
||||
.buffer_memory_barriers(&memory.shm_barriers)
|
||||
.image_memory_barriers(&memory.image_barriers);
|
||||
unsafe {
|
||||
self.device.device.cmd_pipeline_barrier2(buf, &dep_info);
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_shm_to_image(&self, cmd: CommandBuffer) {
|
||||
let memory = self.memory.borrow_mut();
|
||||
for (img, staging) in &memory.flush_staging {
|
||||
let Some(shm_info) = &img.format.shm_info else {
|
||||
continue;
|
||||
};
|
||||
let cpy = BufferImageCopy2::builder()
|
||||
.buffer_image_height(img.height)
|
||||
.buffer_row_length(img.stride / shm_info.bpp)
|
||||
.image_extent(Extent3D {
|
||||
width: img.width,
|
||||
height: img.height,
|
||||
depth: 1,
|
||||
})
|
||||
.image_subresource(ImageSubresourceLayers {
|
||||
aspect_mask: ImageAspectFlags::COLOR,
|
||||
mip_level: 0,
|
||||
base_array_layer: 0,
|
||||
layer_count: 1,
|
||||
})
|
||||
.build();
|
||||
let info = CopyBufferToImageInfo2::builder()
|
||||
.src_buffer(staging.buffer)
|
||||
.dst_image(img.image)
|
||||
.dst_image_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
|
||||
.regions(slice::from_ref(&cpy));
|
||||
unsafe {
|
||||
self.device.device.cmd_copy_buffer_to_image2(cmd, &info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn secondary_barriers(&self, buf: CommandBuffer) {
|
||||
let mut memory = self.memory.borrow_mut();
|
||||
let memory = &mut *memory;
|
||||
if memory.flush.is_empty() {
|
||||
return;
|
||||
}
|
||||
memory.image_barriers.clear();
|
||||
for img in &memory.flush {
|
||||
let image_memory_barrier = image_barrier()
|
||||
.image(img.image)
|
||||
.old_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
|
||||
.new_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL)
|
||||
.src_access_mask(AccessFlags2::TRANSFER_WRITE)
|
||||
.src_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.dst_access_mask(AccessFlags2::SHADER_SAMPLED_READ)
|
||||
.dst_stage_mask(PipelineStageFlags2::FRAGMENT_SHADER)
|
||||
.build();
|
||||
memory.image_barriers.push(image_memory_barrier);
|
||||
}
|
||||
let dep_info = DependencyInfoKHR::builder().image_memory_barriers(&memory.image_barriers);
|
||||
unsafe {
|
||||
self.device.device.cmd_pipeline_barrier2(buf, &dep_info);
|
||||
|
|
@ -625,7 +514,6 @@ impl VulkanRenderer {
|
|||
let mut memory = self.memory.borrow_mut();
|
||||
let memory = &mut *memory;
|
||||
memory.image_barriers.clear();
|
||||
memory.shm_barriers.clear();
|
||||
let mut fb_image_memory_barrier = image_barrier()
|
||||
.src_queue_family_index(self.device.graphics_queue_idx)
|
||||
.dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
|
||||
|
|
@ -659,9 +547,7 @@ impl VulkanRenderer {
|
|||
.build();
|
||||
memory.image_barriers.push(image_memory_barrier);
|
||||
}
|
||||
let dep_info = DependencyInfoKHR::builder()
|
||||
.image_memory_barriers(&memory.image_barriers)
|
||||
.buffer_memory_barriers(&memory.shm_barriers);
|
||||
let dep_info = DependencyInfoKHR::builder().image_memory_barriers(&memory.image_barriers);
|
||||
unsafe {
|
||||
self.device.device.cmd_pipeline_barrier2(buf, &dep_info);
|
||||
}
|
||||
|
|
@ -804,26 +690,16 @@ impl VulkanRenderer {
|
|||
|
||||
fn store_layouts(&self, fb: &VulkanImage) {
|
||||
fb.is_undefined.set(false);
|
||||
let memory = self.memory.borrow_mut();
|
||||
for img in &memory.flush {
|
||||
img.is_undefined.set(false);
|
||||
let shm = match &img.ty {
|
||||
VulkanImageMemory::DmaBuf(_) => unreachable!(),
|
||||
VulkanImageMemory::Internal(s) => s,
|
||||
};
|
||||
shm.to_flush.take();
|
||||
}
|
||||
}
|
||||
|
||||
fn create_pending_frame(self: &Rc<Self>, buf: Rc<VulkanCommandBuffer>) {
|
||||
let point = self.last_point.fetch_add(1) + 1;
|
||||
let point = self.allocate_point();
|
||||
let mut memory = self.memory.borrow_mut();
|
||||
let frame = Rc::new(PendingFrame {
|
||||
point,
|
||||
renderer: self.clone(),
|
||||
cmd: Cell::new(Some(buf)),
|
||||
_textures: mem::take(&mut memory.textures),
|
||||
_staging: mem::take(&mut memory.flush_staging),
|
||||
wait_semaphores: Cell::new(mem::take(&mut memory.wait_semaphores)),
|
||||
waiter: Cell::new(None),
|
||||
_release_fence: memory.release_fence.take(),
|
||||
|
|
@ -1034,9 +910,7 @@ impl VulkanRenderer {
|
|||
let res = self.try_execute(fb, opts, clear);
|
||||
let sync_file = {
|
||||
let mut memory = self.memory.borrow_mut();
|
||||
memory.flush.clear();
|
||||
memory.textures.clear();
|
||||
memory.flush_staging.clear();
|
||||
memory.sample.clear();
|
||||
memory.wait_semaphores.clear();
|
||||
memory.release_fence.take();
|
||||
|
|
@ -1045,7 +919,7 @@ impl VulkanRenderer {
|
|||
res.map(|_| sync_file)
|
||||
}
|
||||
|
||||
fn allocate_command_buffer(&self) -> Result<Rc<VulkanCommandBuffer>, VulkanError> {
|
||||
pub(super) fn allocate_command_buffer(&self) -> Result<Rc<VulkanCommandBuffer>, VulkanError> {
|
||||
let buf = match self.command_buffers.pop() {
|
||||
Some(b) => b,
|
||||
_ => {
|
||||
|
|
@ -1073,10 +947,7 @@ impl VulkanRenderer {
|
|||
let buf = self.allocate_command_buffer()?;
|
||||
self.collect_memory(opts);
|
||||
self.begin_command_buffer(buf.buffer)?;
|
||||
self.write_shm_staging_buffers()?;
|
||||
self.initial_barriers(buf.buffer, fb);
|
||||
self.copy_shm_to_image(buf.buffer);
|
||||
self.secondary_barriers(buf.buffer);
|
||||
self.begin_rendering(buf.buffer, fb, clear);
|
||||
self.set_viewport(buf.buffer, fb);
|
||||
self.record_draws(buf.buffer, opts)?;
|
||||
|
|
@ -1092,7 +963,7 @@ impl VulkanRenderer {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn block(&self) {
|
||||
pub(super) fn block(&self) {
|
||||
log::warn!("Blocking.");
|
||||
unsafe {
|
||||
if let Err(e) = self.device.device.device_wait_idle() {
|
||||
|
|
@ -1103,7 +974,8 @@ impl VulkanRenderer {
|
|||
|
||||
pub fn on_drop(&self) {
|
||||
let mut pending_frames = self.pending_frames.lock();
|
||||
if pending_frames.is_not_empty() {
|
||||
let mut pending_uploads = self.pending_uploads.lock();
|
||||
if pending_frames.is_not_empty() || pending_uploads.is_not_empty() {
|
||||
log::warn!("Context dropped with pending frames.");
|
||||
self.block();
|
||||
}
|
||||
|
|
@ -1111,6 +983,7 @@ impl VulkanRenderer {
|
|||
f.waiter.take();
|
||||
});
|
||||
pending_frames.clear();
|
||||
pending_uploads.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1150,7 +1023,7 @@ impl dyn GfxTexture {
|
|||
}
|
||||
}
|
||||
|
||||
fn image_barrier() -> ImageMemoryBarrier2Builder<'static> {
|
||||
pub(super) fn image_barrier() -> ImageMemoryBarrier2Builder<'static> {
|
||||
ImageMemoryBarrier2::builder().subresource_range(
|
||||
ImageSubresourceRange::builder()
|
||||
.aspect_mask(ImageAspectFlags::COLOR)
|
||||
|
|
|
|||
374
src/gfx_apis/vulkan/shm_image.rs
Normal file
374
src/gfx_apis/vulkan/shm_image.rs
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
use {
|
||||
crate::{
|
||||
format::{Format, FormatShmInfo},
|
||||
gfx_api::SyncFile,
|
||||
gfx_apis::vulkan::{
|
||||
allocator::VulkanAllocation,
|
||||
command::VulkanCommandBuffer,
|
||||
fence::VulkanFence,
|
||||
image::{VulkanImage, VulkanImageMemory},
|
||||
renderer::{image_barrier, VulkanRenderer},
|
||||
staging::VulkanStagingBuffer,
|
||||
util::OnDrop,
|
||||
VulkanError,
|
||||
},
|
||||
rect::Rect,
|
||||
utils::errorfmt::ErrorFmt,
|
||||
},
|
||||
ash::vk::{
|
||||
AccessFlags2, BufferImageCopy2, BufferMemoryBarrier2, CommandBufferBeginInfo,
|
||||
CommandBufferSubmitInfo, CommandBufferUsageFlags, CopyBufferToImageInfo2,
|
||||
DependencyInfoKHR, DeviceSize, Extent3D, ImageAspectFlags, ImageCreateInfo, ImageLayout,
|
||||
ImageSubresourceLayers, ImageSubresourceRange, ImageTiling, ImageType, ImageUsageFlags,
|
||||
ImageViewCreateInfo, ImageViewType, Offset3D, PipelineStageFlags2, SampleCountFlags,
|
||||
SharingMode, SubmitInfo2,
|
||||
},
|
||||
gpu_alloc::UsageFlags,
|
||||
isnt::std_1::primitive::IsntSliceExt,
|
||||
std::{cell::Cell, ptr, rc::Rc, slice},
|
||||
};
|
||||
|
||||
pub struct VulkanShmImage {
|
||||
pub(super) _size: DeviceSize,
|
||||
pub(super) stride: u32,
|
||||
pub(super) _allocation: VulkanAllocation,
|
||||
pub(super) shm_info: &'static FormatShmInfo,
|
||||
}
|
||||
|
||||
impl VulkanShmImage {
|
||||
pub fn upload(
|
||||
&self,
|
||||
img: &Rc<VulkanImage>,
|
||||
buffer: &[Cell<u8>],
|
||||
damage: Option<&[Rect]>,
|
||||
) -> Result<(), VulkanError> {
|
||||
if let Some(damage) = damage {
|
||||
if damage.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let copy = |full: bool, off, x, y, width, height| {
|
||||
let mut builder = BufferImageCopy2::builder()
|
||||
.buffer_offset(off)
|
||||
.image_offset(Offset3D { x, y, z: 0 })
|
||||
.image_extent(Extent3D {
|
||||
width,
|
||||
height,
|
||||
depth: 1,
|
||||
})
|
||||
.image_subresource(ImageSubresourceLayers {
|
||||
aspect_mask: ImageAspectFlags::COLOR,
|
||||
mip_level: 0,
|
||||
base_array_layer: 0,
|
||||
layer_count: 1,
|
||||
});
|
||||
if full {
|
||||
builder = builder
|
||||
.buffer_image_height(img.height)
|
||||
.buffer_row_length(img.stride / self.shm_info.bpp);
|
||||
}
|
||||
builder.build()
|
||||
};
|
||||
let mut total_size;
|
||||
let cpy_one;
|
||||
let mut cpy_many;
|
||||
let cpy;
|
||||
if let Some(damage) = damage {
|
||||
total_size = 0;
|
||||
cpy_many = Vec::with_capacity(damage.len());
|
||||
for damage in damage {
|
||||
let Some(damage) = Rect::new(
|
||||
damage.x1().max(0),
|
||||
damage.y1().max(0),
|
||||
damage.x2().min(img.width as i32),
|
||||
damage.y2().min(img.height as i32),
|
||||
) else {
|
||||
continue;
|
||||
};
|
||||
if damage.is_empty() {
|
||||
continue;
|
||||
}
|
||||
cpy_many.push(copy(
|
||||
false,
|
||||
total_size as DeviceSize,
|
||||
damage.x1(),
|
||||
damage.y1(),
|
||||
damage.width() as u32,
|
||||
damage.height() as u32,
|
||||
));
|
||||
total_size += damage.width() as u32 * damage.height() as u32 * self.shm_info.bpp;
|
||||
}
|
||||
cpy = &cpy_many[..];
|
||||
} else {
|
||||
cpy_one = copy(true, 0, 0, 0, img.width, img.height);
|
||||
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)?;
|
||||
staging.upload(|mem, _| unsafe {
|
||||
let buf = buffer.as_ptr() as *const u8;
|
||||
if damage.is_some() {
|
||||
let mut off = 0;
|
||||
for cpy in cpy {
|
||||
let x = cpy.image_offset.x as usize;
|
||||
let y = cpy.image_offset.y as usize;
|
||||
let width = cpy.image_extent.width as usize;
|
||||
let height = cpy.image_extent.height as usize;
|
||||
let stride = self.stride as usize;
|
||||
let bpp = self.shm_info.bpp as usize;
|
||||
for dy in 0..height {
|
||||
let lo = (y + dy) * stride + x * bpp;
|
||||
let len = width * bpp;
|
||||
ptr::copy_nonoverlapping(buf.add(lo), mem.add(off), len);
|
||||
off += len;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ptr::copy_nonoverlapping(buf, mem, total_size as usize);
|
||||
}
|
||||
})?;
|
||||
let memory_barrier = |sam, ssm, dam, dsm| {
|
||||
BufferMemoryBarrier2::builder()
|
||||
.buffer(staging.buffer)
|
||||
.offset(0)
|
||||
.size(staging.size)
|
||||
.src_access_mask(sam)
|
||||
.src_stage_mask(ssm)
|
||||
.dst_access_mask(dam)
|
||||
.dst_stage_mask(dsm)
|
||||
.build()
|
||||
};
|
||||
let initial_image_barrier = image_barrier()
|
||||
.image(img.image)
|
||||
.src_access_mask(AccessFlags2::SHADER_SAMPLED_READ)
|
||||
.src_stage_mask(PipelineStageFlags2::FRAGMENT_SHADER)
|
||||
.old_layout(if img.is_undefined.get() {
|
||||
ImageLayout::UNDEFINED
|
||||
} else {
|
||||
ImageLayout::SHADER_READ_ONLY_OPTIMAL
|
||||
})
|
||||
.new_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
|
||||
.dst_access_mask(AccessFlags2::TRANSFER_WRITE)
|
||||
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.build();
|
||||
let initial_buffer_barrier = memory_barrier(
|
||||
AccessFlags2::HOST_WRITE,
|
||||
PipelineStageFlags2::HOST,
|
||||
AccessFlags2::TRANSFER_READ,
|
||||
PipelineStageFlags2::TRANSFER,
|
||||
);
|
||||
let initial_dep_info = DependencyInfoKHR::builder()
|
||||
.buffer_memory_barriers(slice::from_ref(&initial_buffer_barrier))
|
||||
.image_memory_barriers(slice::from_ref(&initial_image_barrier));
|
||||
let final_image_barrier = image_barrier()
|
||||
.image(img.image)
|
||||
.src_access_mask(AccessFlags2::TRANSFER_WRITE)
|
||||
.src_stage_mask(PipelineStageFlags2::TRANSFER)
|
||||
.old_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
|
||||
.new_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL)
|
||||
.dst_access_mask(AccessFlags2::SHADER_SAMPLED_READ)
|
||||
.dst_stage_mask(PipelineStageFlags2::FRAGMENT_SHADER)
|
||||
.build();
|
||||
let final_buffer_barrier = memory_barrier(
|
||||
AccessFlags2::TRANSFER_READ,
|
||||
PipelineStageFlags2::TRANSFER,
|
||||
AccessFlags2::HOST_WRITE,
|
||||
PipelineStageFlags2::HOST,
|
||||
);
|
||||
let final_dep_info = DependencyInfoKHR::builder()
|
||||
.buffer_memory_barriers(slice::from_ref(&final_buffer_barrier))
|
||||
.image_memory_barriers(slice::from_ref(&final_image_barrier));
|
||||
let cpy_info = CopyBufferToImageInfo2::builder()
|
||||
.src_buffer(staging.buffer)
|
||||
.dst_image(img.image)
|
||||
.dst_image_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
|
||||
.regions(cpy);
|
||||
let cmd = img.renderer.allocate_command_buffer()?;
|
||||
let dev = &img.renderer.device.device;
|
||||
let command_buffer_info = CommandBufferSubmitInfo::builder().command_buffer(cmd.buffer);
|
||||
let submit_info =
|
||||
SubmitInfo2::builder().command_buffer_infos(slice::from_ref(&command_buffer_info));
|
||||
let begin_info =
|
||||
CommandBufferBeginInfo::builder().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT);
|
||||
let release_fence = img.renderer.device.create_fence()?;
|
||||
unsafe {
|
||||
dev.begin_command_buffer(cmd.buffer, &begin_info)
|
||||
.map_err(VulkanError::BeginCommandBuffer)?;
|
||||
dev.cmd_pipeline_barrier2(cmd.buffer, &initial_dep_info);
|
||||
dev.cmd_copy_buffer_to_image2(cmd.buffer, &cpy_info);
|
||||
dev.cmd_pipeline_barrier2(cmd.buffer, &final_dep_info);
|
||||
dev.end_command_buffer(cmd.buffer)
|
||||
.map_err(VulkanError::EndCommandBuffer)?;
|
||||
dev.queue_submit2(
|
||||
img.renderer.device.graphics_queue,
|
||||
slice::from_ref(&submit_info),
|
||||
release_fence.fence,
|
||||
)
|
||||
.map_err(VulkanError::Submit)?;
|
||||
}
|
||||
img.is_undefined.set(false);
|
||||
let release_sync_file = match release_fence.export_sync_file() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log::error!("Could not export sync file from fence: {}", ErrorFmt(e));
|
||||
img.renderer.block();
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let point = img.renderer.allocate_point();
|
||||
let future = img.renderer.device.instance.eng.spawn(await_upload(
|
||||
point,
|
||||
img.clone(),
|
||||
cmd,
|
||||
release_sync_file,
|
||||
release_fence,
|
||||
staging,
|
||||
));
|
||||
img.renderer.pending_uploads.set(point, future);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn await_upload(
|
||||
id: u64,
|
||||
img: Rc<VulkanImage>,
|
||||
buf: Rc<VulkanCommandBuffer>,
|
||||
sync_file: SyncFile,
|
||||
_fence: Rc<VulkanFence>,
|
||||
_staging: VulkanStagingBuffer,
|
||||
) {
|
||||
let res = img
|
||||
.renderer
|
||||
.device
|
||||
.instance
|
||||
.ring
|
||||
.readable(&sync_file.0)
|
||||
.await;
|
||||
if let Err(e) = res {
|
||||
log::error!(
|
||||
"Could not wait for sync file to become readable: {}",
|
||||
ErrorFmt(e)
|
||||
);
|
||||
img.renderer.block();
|
||||
}
|
||||
img.renderer.command_buffers.push(buf);
|
||||
img.renderer.pending_uploads.remove(&id);
|
||||
}
|
||||
|
||||
impl VulkanRenderer {
|
||||
pub fn create_shm_texture(
|
||||
self: &Rc<Self>,
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
data: &[Cell<u8>],
|
||||
for_download: bool,
|
||||
) -> Result<Rc<VulkanImage>, VulkanError> {
|
||||
let Some(shm_info) = &format.shm_info else {
|
||||
return Err(VulkanError::UnsupportedShmFormat(format.name));
|
||||
};
|
||||
if width <= 0 || height <= 0 || stride <= 0 {
|
||||
return Err(VulkanError::NonPositiveImageSize);
|
||||
}
|
||||
let width = width as u32;
|
||||
let height = height as u32;
|
||||
let stride = stride as u32;
|
||||
if stride % shm_info.bpp != 0 || stride / shm_info.bpp < width {
|
||||
return Err(VulkanError::InvalidStride);
|
||||
}
|
||||
let vk_format = self
|
||||
.device
|
||||
.formats
|
||||
.get(&format.drm)
|
||||
.ok_or(VulkanError::FormatNotSupported)?;
|
||||
let shm = vk_format.shm.as_ref().ok_or(VulkanError::ShmNotSupported)?;
|
||||
if width > shm.max_extents.width || height > shm.max_extents.height {
|
||||
return Err(VulkanError::ImageTooLarge);
|
||||
}
|
||||
let size = stride.checked_mul(height).ok_or(VulkanError::ShmOverflow)?;
|
||||
let usage = ImageUsageFlags::TRANSFER_SRC
|
||||
| match for_download {
|
||||
true => ImageUsageFlags::COLOR_ATTACHMENT,
|
||||
false => ImageUsageFlags::SAMPLED | ImageUsageFlags::TRANSFER_DST,
|
||||
};
|
||||
let create_info = ImageCreateInfo::builder()
|
||||
.image_type(ImageType::TYPE_2D)
|
||||
.format(format.vk_format)
|
||||
.mip_levels(1)
|
||||
.array_layers(1)
|
||||
.tiling(ImageTiling::OPTIMAL)
|
||||
.samples(SampleCountFlags::TYPE_1)
|
||||
.sharing_mode(SharingMode::EXCLUSIVE)
|
||||
.initial_layout(ImageLayout::UNDEFINED)
|
||||
.extent(Extent3D {
|
||||
width,
|
||||
height,
|
||||
depth: 1,
|
||||
})
|
||||
.usage(usage)
|
||||
.build();
|
||||
let image = unsafe { self.device.device.create_image(&create_info, None) };
|
||||
let image = image.map_err(VulkanError::CreateImage)?;
|
||||
let destroy_image = OnDrop(|| unsafe { self.device.device.destroy_image(image, None) });
|
||||
let memory_requirements =
|
||||
unsafe { self.device.device.get_image_memory_requirements(image) };
|
||||
let allocation =
|
||||
self.allocator
|
||||
.alloc(&memory_requirements, UsageFlags::FAST_DEVICE_ACCESS, false)?;
|
||||
let res = unsafe {
|
||||
self.device
|
||||
.device
|
||||
.bind_image_memory(image, allocation.memory, allocation.offset)
|
||||
};
|
||||
res.map_err(VulkanError::BindImageMemory)?;
|
||||
let image_view_create_info = ImageViewCreateInfo::builder()
|
||||
.image(image)
|
||||
.format(format.vk_format)
|
||||
.view_type(ImageViewType::TYPE_2D)
|
||||
.subresource_range(ImageSubresourceRange {
|
||||
aspect_mask: ImageAspectFlags::COLOR,
|
||||
base_mip_level: 0,
|
||||
level_count: 1,
|
||||
base_array_layer: 0,
|
||||
layer_count: 1,
|
||||
});
|
||||
let view = unsafe {
|
||||
self.device
|
||||
.device
|
||||
.create_image_view(&image_view_create_info, None)
|
||||
};
|
||||
let view = view.map_err(VulkanError::CreateImageView)?;
|
||||
let shm = VulkanShmImage {
|
||||
_size: size as u64,
|
||||
stride,
|
||||
_allocation: allocation,
|
||||
shm_info,
|
||||
};
|
||||
destroy_image.forget();
|
||||
let img = Rc::new(VulkanImage {
|
||||
renderer: self.clone(),
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
stride,
|
||||
texture_view: view,
|
||||
render_view: None,
|
||||
image,
|
||||
is_undefined: Cell::new(true),
|
||||
ty: VulkanImageMemory::Internal(shm),
|
||||
render_ops: Default::default(),
|
||||
bridge: None,
|
||||
});
|
||||
let shm = match &img.ty {
|
||||
VulkanImageMemory::DmaBuf(_) => unreachable!(),
|
||||
VulkanImageMemory::Internal(s) => s,
|
||||
};
|
||||
if data.is_not_empty() {
|
||||
shm.upload(&img, data, None)?;
|
||||
}
|
||||
Ok(img)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue