use { crate::{ async_engine::SpawnedFuture, format::Format, gfx_api::{GfxApiOpt, GfxFormat, GfxFramebuffer, GfxTexture}, gfx_apis::vulkan::{ allocator::VulkanAllocator, command::{VulkanCommandBuffer, VulkanCommandPool}, device::VulkanDevice, fence::VulkanFence, image::{VulkanImage, VulkanImageMemory}, pipeline::{PipelineCreateInfo, VulkanPipeline}, semaphore::VulkanSemaphore, shaders::{ FillFragPushConstants, FillVertPushConstants, TexVertPushConstants, FILL_FRAG, FILL_VERT, TEX_FRAG, TEX_VERT, }, staging::VulkanStagingBuffer, VulkanError, }, io_uring::IoUring, theme::Color, utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell, stack::Stack}, video::dmabuf::{ dma_buf_export_sync_file, dma_buf_import_sync_file, DMA_BUF_SYNC_READ, DMA_BUF_SYNC_WRITE, }, }, ahash::AHashMap, ash::{ vk::{ AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, BufferImageCopy, BufferImageCopy2, BufferMemoryBarrier2, ClearColorValue, ClearValue, CommandBuffer, CommandBufferBeginInfo, CommandBufferSubmitInfo, CommandBufferUsageFlags, CopyBufferToImageInfo2, DependencyInfo, DependencyInfoKHR, DescriptorImageInfo, DescriptorType, Extent2D, Extent3D, Fence, ImageAspectFlags, ImageLayout, ImageMemoryBarrier2, ImageMemoryBarrier2Builder, ImageSubresourceLayers, ImageSubresourceRange, PipelineBindPoint, PipelineStageFlags2, Rect2D, RenderingAttachmentInfo, RenderingInfo, SemaphoreSubmitInfo, SemaphoreSubmitInfoKHR, ShaderStageFlags, SubmitInfo2, Viewport, WriteDescriptorSet, QUEUE_FAMILY_FOREIGN_EXT, }, Device, }, isnt::std_1::collections::IsntHashMapExt, std::{ cell::{Cell, RefCell}, fmt::{Debug, Formatter}, mem, ptr, rc::Rc, slice, }, uapi::OwnedFd, }; pub struct VulkanRenderer { pub(super) formats: Rc>, pub(super) device: Rc, pub(super) fill_pipeline: Rc, pub(super) tex_opaque_pipeline: Rc, pub(super) tex_alpha_pipeline: Rc, pub(super) command_pool: Rc, pub(super) command_buffers: Stack>, pub(super) wait_semaphores: Stack>, pub(super) total_buffers: NumCell, pub(super) memory: RefCell, pub(super) pending_frames: CopyHashMap>, pub(super) allocator: Rc, pub(super) last_point: NumCell, } #[derive(Default)] pub(super) struct Memory { sample: Vec>, flush: Vec>, flush_staging: Vec<(Rc, VulkanStagingBuffer)>, textures: Vec>, image_barriers: Vec, shm_barriers: Vec, wait_semaphores: Vec>, wait_semaphore_infos: Vec, release_fence: Option>, release_syncfile: Option>, } pub(super) struct PendingFrame { point: u64, renderer: Rc, cmd: Cell>>, _textures: Vec>, _staging: Vec<(Rc, VulkanStagingBuffer)>, wait_semaphores: Cell>>, waiter: Cell>>, _release_fence: Option>, } impl VulkanDevice { pub fn create_renderer(self: &Rc) -> Result, VulkanError> { let fill_pipeline = self.create_pipeline::( PipelineCreateInfo { vert: self.create_shader(FILL_VERT)?, frag: self.create_shader(FILL_FRAG)?, alpha: true, frag_descriptor_set_layout: None, }, )?; let sampler = self.create_sampler()?; let tex_descriptor_set_layout = self.create_descriptor_set_layout(&sampler)?; let tex_vert_shader = self.create_shader(TEX_VERT)?; let tex_frag_shader = self.create_shader(TEX_FRAG)?; let create_tex_pipeline = |alpha| { self.create_pipeline::(PipelineCreateInfo { vert: tex_vert_shader.clone(), frag: tex_frag_shader.clone(), alpha, frag_descriptor_set_layout: Some(tex_descriptor_set_layout.clone()), }) }; let tex_opaque_pipeline = create_tex_pipeline(false)?; let tex_alpha_pipeline = create_tex_pipeline(true)?; let command_pool = self.create_command_pool()?; let formats: AHashMap = self .formats .iter() .map(|(drm, vk)| { ( *drm, GfxFormat { format: vk.format, read_modifiers: vk .modifiers .values() .filter(|m| m.texture_max_extents.is_some()) .map(|m| m.modifier) .collect(), write_modifiers: vk .modifiers .values() .filter(|m| m.render_max_extents.is_some()) .map(|m| m.modifier) .collect(), }, ) }) .collect(); let allocator = self.create_allocator()?; Ok(Rc::new(VulkanRenderer { formats: Rc::new(formats), device: self.clone(), fill_pipeline, tex_opaque_pipeline, tex_alpha_pipeline, command_pool, command_buffers: Default::default(), wait_semaphores: Default::default(), total_buffers: Default::default(), memory: Default::default(), pending_frames: Default::default(), allocator, last_point: Default::default(), })) } } impl VulkanRenderer { 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()); } } } memory.textures.push(tex); } } } fn begin_command_buffer(&self, buf: CommandBuffer) -> Result<(), VulkanError> { let begin_info = CommandBufferBeginInfo::builder().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT); unsafe { self.device .device .begin_command_buffer(buf, &begin_info) .map_err(VulkanError::BeginCommandBuffer) } } fn write_shm_staging_buffers(self: &Rc) -> 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 fb_image_memory_barrier = image_barrier() .src_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT) .dst_queue_family_index(self.device.graphics_queue_idx) .image(fb.image) .old_layout(if fb.is_undefined.get() { ImageLayout::UNDEFINED } else { ImageLayout::GENERAL }) .new_layout(ImageLayout::COLOR_ATTACHMENT_OPTIMAL) .dst_access_mask(AccessFlags2::COLOR_ATTACHMENT_WRITE) .dst_stage_mask(PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) .build(); memory.image_barriers.push(fb_image_memory_barrier); for img in &memory.sample { let image_memory_barrier = image_barrier() .src_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT) .dst_queue_family_index(self.device.graphics_queue_idx) .image(img.image) .old_layout(ImageLayout::GENERAL) .new_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL) .dst_access_mask(AccessFlags2::SHADER_SAMPLED_READ) .dst_stage_mask(PipelineStageFlags2::FRAGMENT_SHADER) .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 cpy = BufferImageCopy2::builder() .buffer_image_height(img.height) .buffer_row_length(img.stride / img.format.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); } } fn begin_rendering(&self, buf: CommandBuffer, fb: &VulkanImage, clear: Option<&Color>) { let rendering_attachment_info = { let mut rai = RenderingAttachmentInfo::builder() .image_view(fb.render_view.unwrap_or(fb.texture_view)) .image_layout(ImageLayout::GENERAL) .load_op(AttachmentLoadOp::LOAD) .store_op(AttachmentStoreOp::STORE); if let Some(clear) = clear { rai = rai .clear_value(ClearValue { color: ClearColorValue { float32: clear.to_array_srgb(), }, }) .load_op(AttachmentLoadOp::CLEAR); } rai }; let rendering_info = RenderingInfo::builder() .render_area(Rect2D { offset: Default::default(), extent: Extent2D { width: fb.width, height: fb.height, }, }) .layer_count(1) .color_attachments(slice::from_ref(&rendering_attachment_info)); unsafe { self.device.device.cmd_begin_rendering(buf, &rendering_info); } } fn set_viewport(&self, buf: CommandBuffer, fb: &VulkanImage) { let viewport = Viewport { x: 0.0, y: 0.0, width: fb.width as _, height: fb.height as _, min_depth: 0.0, max_depth: 1.0, }; let scissor = Rect2D { offset: Default::default(), extent: Extent2D { width: fb.width, height: fb.height, }, }; unsafe { self.device .device .cmd_set_viewport(buf, 0, slice::from_ref(&viewport)); self.device .device .cmd_set_scissor(buf, 0, slice::from_ref(&scissor)); } } fn record_draws(&self, buf: CommandBuffer, opts: &[GfxApiOpt]) -> Result<(), VulkanError> { let dev = &self.device.device; let mut current_pipeline = None; let mut bind = |pipeline: &VulkanPipeline| { if current_pipeline != Some(pipeline.pipeline) { current_pipeline = Some(pipeline.pipeline); unsafe { dev.cmd_bind_pipeline(buf, PipelineBindPoint::GRAPHICS, pipeline.pipeline); } } }; for opt in opts { match opt { GfxApiOpt::Sync => {} GfxApiOpt::FillRect(r) => { bind(&self.fill_pipeline); let vert = FillVertPushConstants { pos: r.rect.to_points(), }; let frag = FillFragPushConstants { color: r.color.to_array_srgb(), }; unsafe { dev.cmd_push_constants( buf, self.fill_pipeline.pipeline_layout, ShaderStageFlags::VERTEX, 0, uapi::as_bytes(&vert), ); dev.cmd_push_constants( buf, self.fill_pipeline.pipeline_layout, ShaderStageFlags::FRAGMENT, self.fill_pipeline.frag_push_offset, uapi::as_bytes(&frag), ); dev.cmd_draw(buf, 4, 1, 0, 0); } } GfxApiOpt::CopyTexture(c) => { let tex = c.tex.as_vk(&self.device.device); let pipeline = match tex.format.has_alpha { true => &self.tex_alpha_pipeline, false => &self.tex_opaque_pipeline, }; bind(pipeline); let vert = TexVertPushConstants { pos: c.target.to_points(), tex_pos: c.source.to_points(), }; let image_info = DescriptorImageInfo::builder() .image_view(tex.texture_view) .image_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL); let write_descriptor_set = WriteDescriptorSet::builder() .descriptor_type(DescriptorType::COMBINED_IMAGE_SAMPLER) .image_info(slice::from_ref(&image_info)) .build(); unsafe { self.device.push_descriptor.cmd_push_descriptor_set( buf, PipelineBindPoint::GRAPHICS, pipeline.pipeline_layout, 0, slice::from_ref(&write_descriptor_set), ); dev.cmd_push_constants( buf, pipeline.pipeline_layout, ShaderStageFlags::VERTEX, 0, uapi::as_bytes(&vert), ); dev.cmd_draw(buf, 4, 1, 0, 0); } } } } Ok(()) } fn end_rendering(&self, buf: CommandBuffer) { unsafe { self.device.device.cmd_end_rendering(buf); } } fn final_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 fb_image_memory_barrier = image_barrier() .src_queue_family_index(self.device.graphics_queue_idx) .dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT) .image(fb.image) .old_layout(ImageLayout::COLOR_ATTACHMENT_OPTIMAL) .new_layout(ImageLayout::GENERAL) .src_access_mask( AccessFlags2::COLOR_ATTACHMENT_WRITE | AccessFlags2::COLOR_ATTACHMENT_READ, ) .src_stage_mask(PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT) .build(); memory.image_barriers.push(fb_image_memory_barrier); for img in &memory.sample { let image_memory_barrier = image_barrier() .src_queue_family_index(self.device.graphics_queue_idx) .dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT) .old_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL) .new_layout(ImageLayout::GENERAL) .image(img.image) .src_access_mask(AccessFlags2::SHADER_SAMPLED_READ) .src_stage_mask(PipelineStageFlags2::FRAGMENT_SHADER) .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); unsafe { self.device.device.cmd_pipeline_barrier2(buf, &dep_info); } } fn end_command_buffer(&self, buf: CommandBuffer) -> Result<(), VulkanError> { unsafe { self.device .device .end_command_buffer(buf) .map_err(VulkanError::EndCommandBuffer) } } fn create_wait_semaphores(&self, fb: &VulkanImage) -> Result<(), VulkanError> { let mut memory = self.memory.borrow_mut(); let memory = &mut *memory; memory.wait_semaphore_infos.clear(); let import = |infos: &mut Vec, semaphores: &mut Vec>, img: &VulkanImage, flag: u32| -> Result<(), VulkanError> { if let VulkanImageMemory::DmaBuf(buf) = &img.ty { for plane in &buf.template.dmabuf.planes { let fd = dma_buf_export_sync_file(&plane.fd, flag) .map_err(VulkanError::IoctlExportSyncFile)?; let semaphore = self.allocate_semaphore()?; semaphore.import_syncfile(fd)?; infos.push( SemaphoreSubmitInfo::builder() .semaphore(semaphore.semaphore) .stage_mask(PipelineStageFlags2::TOP_OF_PIPE) .build(), ); semaphores.push(semaphore); } } Ok(()) }; for texture in &memory.textures { import( &mut memory.wait_semaphore_infos, &mut memory.wait_semaphores, texture, DMA_BUF_SYNC_READ, )?; } import( &mut memory.wait_semaphore_infos, &mut memory.wait_semaphores, fb, DMA_BUF_SYNC_WRITE, )?; Ok(()) } fn import_release_semaphore(&self, fb: &VulkanImage) { let memory = self.memory.borrow(); let syncfile = match memory.release_syncfile.as_ref() { Some(syncfile) => syncfile, _ => return, }; let import = |img: &VulkanImage, flag: u32| { if let VulkanImageMemory::DmaBuf(buf) = &img.ty { for plane in &buf.template.dmabuf.planes { let res = dma_buf_import_sync_file(&plane.fd, flag, &syncfile) .map_err(VulkanError::IoctlImportSyncFile); if let Err(e) = res { log::error!("Could not import syncfile into dmabuf: {}", ErrorFmt(e)); log::warn!("Relying on implicit sync"); } } } }; for texture in &memory.textures { import(texture, DMA_BUF_SYNC_WRITE); } import(fb, DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE); } fn submit(&self, buf: CommandBuffer) -> Result<(), VulkanError> { let mut memory = self.memory.borrow_mut(); let release_fence = self.device.create_fence()?; let command_buffer_info = CommandBufferSubmitInfo::builder() .command_buffer(buf) .build(); let submit_info = SubmitInfo2::builder() .wait_semaphore_infos(&memory.wait_semaphore_infos) .command_buffer_infos(slice::from_ref(&command_buffer_info)) .build(); unsafe { self.device .device .queue_submit2( self.device.graphics_queue, slice::from_ref(&submit_info), release_fence.fence, ) .map_err(VulkanError::Submit)?; } let release_syncfile = match release_fence.export_syncfile() { Ok(s) => Some(s), Err(e) => { log::error!("Could not export syncfile from fence: {}", ErrorFmt(e)); None } }; memory.release_fence = Some(release_fence); memory.release_syncfile = release_syncfile; Ok(()) } 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, buf: Rc) { let point = self.last_point.fetch_add(1) + 1; 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(), }); self.pending_frames.set(frame.point, frame.clone()); let future = self.device.instance.eng.spawn(await_release( memory.release_syncfile.take(), self.device.instance.ring.clone(), frame.clone(), self.clone(), )); frame.waiter.set(Some(future)); } pub fn read_pixels( self: &Rc, tex: &Rc, x: i32, y: i32, width: i32, height: i32, stride: i32, format: &'static Format, dst: &[Cell], ) -> Result<(), VulkanError> { if x < 0 || y < 0 || width <= 0 || height <= 0 || stride <= 0 { return Err(VulkanError::InvalidShmParameters { x, y, width, height, stride, }); } let width = width as u32; let height = height as u32; let stride = stride as u32; if x == 0 && y == 0 && width == tex.width && height == tex.height && format == tex.format { return self.read_all_pixels(tex, stride, dst); } let tmp_tex = self.create_shm_texture( format, width as i32, height as i32, stride as i32, &[], true, )?; (&*tmp_tex as &dyn GfxFramebuffer).copy_texture(&(tex.clone() as _), x, y); self.read_all_pixels(&tmp_tex, stride, dst) } fn read_all_pixels( self: &Rc, tex: &VulkanImage, stride: u32, dst: &[Cell], ) -> Result<(), VulkanError> { if stride < tex.width * tex.format.bpp || stride % tex.format.bpp != 0 { return Err(VulkanError::InvalidStride); } let size = stride as u64 * tex.height as u64; if size != dst.len() as u64 { return Err(VulkanError::InvalidBufferSize); } let region = BufferImageCopy::builder() .buffer_row_length(stride / tex.format.bpp) .buffer_image_height(tex.height) .image_subresource(ImageSubresourceLayers { aspect_mask: ImageAspectFlags::COLOR, mip_level: 0, base_array_layer: 0, layer_count: 1, }) .image_extent(Extent3D { width: tex.width, height: tex.height, depth: 1, }) .build(); let staging = self.create_staging_buffer(size, false, true, true)?; let initial_tex_barrier = image_barrier() .src_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT) .dst_queue_family_index(self.device.graphics_queue_idx) .image(tex.image) .old_layout(ImageLayout::GENERAL) .new_layout(ImageLayout::TRANSFER_SRC_OPTIMAL) .dst_access_mask(AccessFlags2::TRANSFER_READ) .dst_stage_mask(PipelineStageFlags2::TRANSFER); let initial_buffer_barrier = BufferMemoryBarrier2::builder() .buffer(staging.buffer) .offset(0) .size(staging.size) .dst_access_mask(AccessFlags2::TRANSFER_WRITE) .dst_stage_mask(PipelineStageFlags2::TRANSFER); let initial_barriers = DependencyInfo::builder() .buffer_memory_barriers(slice::from_ref(&initial_buffer_barrier)) .image_memory_barriers(slice::from_ref(&initial_tex_barrier)); let final_tex_barrier = image_barrier() .src_queue_family_index(self.device.graphics_queue_idx) .dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT) .image(tex.image) .old_layout(ImageLayout::TRANSFER_SRC_OPTIMAL) .new_layout(ImageLayout::GENERAL) .src_access_mask(AccessFlags2::TRANSFER_READ) .src_stage_mask(PipelineStageFlags2::TRANSFER); let final_buffer_barrier = BufferMemoryBarrier2::builder() .buffer(staging.buffer) .offset(0) .size(staging.size) .src_access_mask(AccessFlags2::TRANSFER_WRITE) .src_stage_mask(PipelineStageFlags2::TRANSFER) .dst_access_mask(AccessFlags2::HOST_READ) .dst_stage_mask(PipelineStageFlags2::HOST); let final_barriers = DependencyInfo::builder() .buffer_memory_barriers(slice::from_ref(&final_buffer_barrier)) .image_memory_barriers(slice::from_ref(&final_tex_barrier)); let buf = self.allocate_command_buffer()?; let mut semaphores = vec![]; let mut semaphore_infos = vec![]; if let VulkanImageMemory::DmaBuf(buf) = &tex.ty { for plane in &buf.template.dmabuf.planes { let fd = dma_buf_export_sync_file(&plane.fd, DMA_BUF_SYNC_READ) .map_err(VulkanError::IoctlExportSyncFile)?; let semaphore = self.allocate_semaphore()?; semaphore.import_syncfile(fd)?; let semaphore_info = SemaphoreSubmitInfo::builder() .semaphore(semaphore.semaphore) .stage_mask(PipelineStageFlags2::TOP_OF_PIPE) .build(); semaphores.push(semaphore); semaphore_infos.push(semaphore_info); } } let command_buffer_info = CommandBufferSubmitInfo::builder().command_buffer(buf.buffer); let submit_info = SubmitInfo2::builder() .wait_semaphore_infos(&semaphore_infos) .command_buffer_infos(slice::from_ref(&command_buffer_info)); let begin_info = CommandBufferBeginInfo::builder().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT); unsafe { self.device .device .begin_command_buffer(buf.buffer, &begin_info) .map_err(VulkanError::BeginCommandBuffer)?; self.device .device .cmd_pipeline_barrier2(buf.buffer, &initial_barriers); self.device.device.cmd_copy_image_to_buffer( buf.buffer, tex.image, ImageLayout::TRANSFER_SRC_OPTIMAL, staging.buffer, &[region], ); self.device .device .cmd_pipeline_barrier2(buf.buffer, &final_barriers); self.device .device .end_command_buffer(buf.buffer) .map_err(VulkanError::EndCommandBuffer)?; self.device .device .queue_submit2( self.device.graphics_queue, slice::from_ref(&submit_info), Fence::null(), ) .map_err(VulkanError::Submit)?; } self.block(); self.command_buffers.push(buf); for semaphore in semaphores { self.wait_semaphores.push(semaphore); } staging.download(|mem, size| unsafe { ptr::copy_nonoverlapping(mem, dst.as_ptr() as _, size); })?; Ok(()) } pub fn execute( self: &Rc, fb: &VulkanImage, opts: &[GfxApiOpt], clear: Option<&Color>, ) -> Result<(), VulkanError> { let res = self.try_execute(fb, opts, clear); { 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(); memory.release_syncfile.take(); } res } fn allocate_command_buffer(&self) -> Result, VulkanError> { let buf = match self.command_buffers.pop() { Some(b) => b, _ => { self.total_buffers.fetch_add(1); self.command_pool.allocate_buffer()? } }; Ok(buf) } fn allocate_semaphore(&self) -> Result, VulkanError> { let semaphore = match self.wait_semaphores.pop() { Some(s) => s, _ => self.device.create_semaphore()?, }; Ok(semaphore) } fn try_execute( self: &Rc, fb: &VulkanImage, opts: &[GfxApiOpt], clear: Option<&Color>, ) -> Result<(), VulkanError> { 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)?; self.end_rendering(buf.buffer); self.final_barriers(buf.buffer, fb); self.end_command_buffer(buf.buffer)?; self.create_wait_semaphores(fb)?; self.submit(buf.buffer)?; self.import_release_semaphore(fb); self.store_layouts(fb); self.create_pending_frame(buf); Ok(()) } fn block(&self) { log::warn!("Blocking."); unsafe { if let Err(e) = self.device.device.device_wait_idle() { log::error!("Could not wait for device idle: {}", ErrorFmt(e)); } } } pub fn on_drop(&self) { let mut pending_frames = self.pending_frames.lock(); if pending_frames.is_not_empty() { log::warn!("Context dropped with pending frames."); self.block(); } pending_frames.values().for_each(|f| { f.waiter.take(); }); pending_frames.clear(); } } impl Debug for VulkanRenderer { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("VulkanRenderer").finish_non_exhaustive() } } #[derive(Debug)] pub struct TmpShmTexture(pub i32, pub i32); impl VulkanImage { fn assert_device(&self, device: &Device) { assert_eq!( self.renderer.device.device.handle(), device.handle(), "Mixed vulkan device use" ); } } impl dyn GfxTexture { fn as_vk(&self, device: &Device) -> &VulkanImage { let img: &VulkanImage = self .as_any() .downcast_ref() .expect("Non-vulkan texture passed into vulkan"); img.assert_device(device); img } pub(super) fn into_vk(self: Rc, device: &Device) -> Rc { let img: Rc = self .into_any() .downcast() .expect("Non-vulkan texture passed into vulkan"); img.assert_device(device); img } } fn image_barrier() -> ImageMemoryBarrier2Builder<'static> { ImageMemoryBarrier2::builder().subresource_range( ImageSubresourceRange::builder() .aspect_mask(ImageAspectFlags::COLOR) .layer_count(1) .level_count(1) .build(), ) } async fn await_release( syncfile: Option>, ring: Rc, frame: Rc, renderer: Rc, ) { let mut is_released = false; if let Some(syncfile) = syncfile { if let Err(e) = ring.readable(&syncfile).await { log::error!( "Could not wait for release semaphore to be signaled: {}", ErrorFmt(e) ); } else { is_released = true; } } if !is_released { frame.renderer.block(); } if let Some(buf) = frame.cmd.take() { frame.renderer.command_buffers.push(buf); } for wait_semaphore in frame.wait_semaphores.take() { frame.renderer.wait_semaphores.push(wait_semaphore); } renderer.pending_frames.remove(&frame.point); }