use { crate::{ async_engine::SpawnedFuture, format::Format, gfx_api::{ AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxFormat, GfxFramebuffer, GfxTexture, ReleaseSync, SyncFile, }, gfx_apis::vulkan::{ allocator::VulkanAllocator, command::{VulkanCommandBuffer, VulkanCommandPool}, device::VulkanDevice, fence::VulkanFence, image::{VulkanImage, VulkanImageMemory}, pipeline::{PipelineCreateInfo, VulkanPipeline}, semaphore::VulkanSemaphore, shaders::{ FillFragPushConstants, FillVertPushConstants, TexFragPushConstants, TexVertPushConstants, VulkanShader, FILL_FRAG, FILL_VERT, TEX_FRAG, TEX_FRAG_MULT_ALPHA, TEX_FRAG_MULT_OPAQUE, 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_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, }, enum_map::{enum_map, Enum, EnumMap}, 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_pipelines: EnumMap>>, 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, pub(super) buffer_resv_user: BufferResvUser, } pub(super) struct UsedTexture { tex: Rc, resv: Option>, acquire_sync: AcquireSync, release_sync: ReleaseSync, } #[derive(Enum)] pub(super) enum TexCopyType { Identity, Multiply, } #[derive(Enum)] pub(super) enum TexSourceType { Opaque, HasAlpha, } #[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_sync_file: 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 tex_frag_mult_opaque_shader = self.create_shader(TEX_FRAG_MULT_OPAQUE)?; let tex_frag_mult_alpha_shader = self.create_shader(TEX_FRAG_MULT_ALPHA)?; 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 create_tex_mult_pipeline = |frag: &Rc| { self.create_pipeline::(PipelineCreateInfo { vert: tex_vert_shader.clone(), frag: frag.clone(), alpha: true, 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 tex_mult_opaque_pipeline = create_tex_mult_pipeline(&tex_frag_mult_opaque_shader)?; let tex_mult_alpha_pipeline = create_tex_mult_pipeline(&tex_frag_mult_alpha_shader)?; 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_pipelines: enum_map! { TexCopyType::Identity => enum_map! { TexSourceType::HasAlpha => tex_alpha_pipeline.clone(), TexSourceType::Opaque => tex_opaque_pipeline.clone(), }, TexCopyType::Multiply => enum_map! { TexSourceType::HasAlpha => tex_mult_alpha_pipeline.clone(), TexSourceType::Opaque => tex_mult_opaque_pipeline.clone(), }, }, 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(), buffer_resv_user: 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(UsedTexture { tex, resv: c.buffer_resv.clone(), acquire_sync: c.acquire_sync.clone(), release_sync: c.release_sync, }); } } } 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 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); } } 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 copy_type = match c.alpha.is_some() { true => TexCopyType::Multiply, false => TexCopyType::Identity, }; let source_type = match tex.format.has_alpha { true => TexSourceType::HasAlpha, false => TexSourceType::Opaque, }; let pipeline = &self.tex_pipelines[copy_type][source_type]; 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), ); if let Some(alpha) = c.alpha { let frag = TexFragPushConstants { alpha }; dev.cmd_push_constants( buf, pipeline.pipeline_layout, ShaderStageFlags::FRAGMENT, mem::size_of_val(&vert) as _, uapi::as_bytes(&frag), ); } 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, sync: &AcquireSync, flag: u32| -> Result<(), VulkanError> { if let VulkanImageMemory::DmaBuf(buf) = &img.ty { let mut import_sync_file = |fd: OwnedFd| -> Result<(), VulkanError> { let semaphore = self.allocate_semaphore()?; semaphore.import_sync_file(fd)?; infos.push( SemaphoreSubmitInfo::builder() .semaphore(semaphore.semaphore) .stage_mask(PipelineStageFlags2::TOP_OF_PIPE) .build(), ); semaphores.push(semaphore); Ok(()) }; match sync { AcquireSync::None => {} AcquireSync::Implicit { .. } => { for plane in &buf.template.dmabuf.planes { let fd = dma_buf_export_sync_file(&plane.fd, flag) .map_err(VulkanError::IoctlExportSyncFile)?; import_sync_file(fd)?; } } AcquireSync::SyncFile { sync_file } => { let fd = uapi::fcntl_dupfd_cloexec(sync_file.raw(), 0) .map_err(|e| VulkanError::Dupfd(e.into()))?; import_sync_file(fd)?; } AcquireSync::Unnecessary => {} } } Ok(()) }; for texture in &memory.textures { import( &mut memory.wait_semaphore_infos, &mut memory.wait_semaphores, &texture.tex, &texture.acquire_sync, DMA_BUF_SYNC_READ, )?; } import( &mut memory.wait_semaphore_infos, &mut memory.wait_semaphores, fb, &AcquireSync::Implicit, DMA_BUF_SYNC_WRITE, )?; Ok(()) } fn import_release_semaphore(&self, fb: &VulkanImage) { let memory = &mut *self.memory.borrow_mut(); let sync_file = match memory.release_sync_file.as_ref() { Some(sync_file) => sync_file, _ => return, }; let import = |img: &VulkanImage, sync: ReleaseSync, resv: Option>, flag: u32| { if sync == ReleaseSync::None { return; } if let Some(resv) = resv { resv.set_sync_file(self.buffer_resv_user, sync_file); } else if sync == ReleaseSync::Implicit { if let VulkanImageMemory::DmaBuf(buf) = &img.ty { if let Err(e) = buf.template.dmabuf.import_sync_file(flag, sync_file) { log::error!("Could not import sync file into dmabuf: {}", ErrorFmt(e)); log::warn!("Relying on implicit sync"); } } } }; for texture in &mut memory.textures { import( &texture.tex, texture.release_sync, texture.resv.take(), DMA_BUF_SYNC_READ, ); } import(fb, ReleaseSync::Implicit, None, 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_sync_file = match release_fence.export_sync_file() { Ok(s) => Some(s), Err(e) => { log::error!("Could not export sync file from fence: {}", ErrorFmt(e)); None } }; memory.release_fence = Some(release_fence); memory.release_sync_file = release_sync_file; 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_sync_file.clone(), 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 _), AcquireSync::None, ReleaseSync::None, x, y, ) .map_err(VulkanError::GfxError)?; self.read_all_pixels(&tmp_tex, stride, dst) } fn read_all_pixels( self: &Rc, tex: &VulkanImage, stride: u32, dst: &[Cell], ) -> Result<(), VulkanError> { let Some(shm_info) = &tex.format.shm_info else { return Err(VulkanError::UnsupportedShmFormat(tex.format.name)); }; if stride < tex.width * shm_info.bpp || stride % shm_info.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 / shm_info.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_sync_file(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 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(); memory.release_sync_file.take() }; res.map(|_| sync_file) } 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() } } 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( sync_file: Option, ring: Rc, frame: Rc, renderer: Rc, ) { let mut is_released = false; if let Some(sync_file) = sync_file { if let Err(e) = ring.readable(&sync_file).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); }