From c712efcd35baeb74b4c4dac7c087b48b258612bb Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sat, 7 Sep 2024 17:48:08 +0200 Subject: [PATCH] vulkan: implement async shm textures --- src/clientmem.rs | 5 - src/cpu_worker/jobs.rs | 1 + src/cpu_worker/jobs/img_copy.rs | 62 +++++ src/cpu_worker/jobs/read_write.rs | 2 - src/gfx_api.rs | 2 - src/gfx_apis/vulkan.rs | 31 ++- src/gfx_apis/vulkan/image.rs | 63 ++++- src/gfx_apis/vulkan/renderer.rs | 8 + src/gfx_apis/vulkan/shm_image.rs | 415 ++++++++++++++++++++++++++++-- src/gfx_apis/vulkan/staging.rs | 1 - 10 files changed, 551 insertions(+), 39 deletions(-) create mode 100644 src/cpu_worker/jobs/img_copy.rs diff --git a/src/clientmem.rs b/src/clientmem.rs index 1c8e9c98..8b7d506f 100644 --- a/src/clientmem.rs +++ b/src/clientmem.rs @@ -98,29 +98,24 @@ impl ClientMem { } } - #[expect(dead_code)] pub fn fd(&self) -> &Rc { &self.fd } - #[expect(dead_code)] pub fn sigbus_impossible(&self) -> bool { self.sigbus_impossible } } impl ClientMemOffset { - #[expect(dead_code)] pub fn pool(&self) -> &ClientMem { &self.mem } - #[expect(dead_code)] pub fn offset(&self) -> usize { self.offset } - #[expect(dead_code)] pub fn ptr(&self) -> *const [Cell] { self.data } diff --git a/src/cpu_worker/jobs.rs b/src/cpu_worker/jobs.rs index aadb998f..63b5b257 100644 --- a/src/cpu_worker/jobs.rs +++ b/src/cpu_worker/jobs.rs @@ -1 +1,2 @@ +pub mod img_copy; pub mod read_write; diff --git a/src/cpu_worker/jobs/img_copy.rs b/src/cpu_worker/jobs/img_copy.rs new file mode 100644 index 00000000..414c1afe --- /dev/null +++ b/src/cpu_worker/jobs/img_copy.rs @@ -0,0 +1,62 @@ +use { + crate::{ + cpu_worker::{AsyncCpuWork, CpuWork}, + rect::Rect, + }, + std::ptr, +}; + +#[expect(clippy::manual_non_exhaustive)] +pub struct ImgCopyWork { + pub src: *mut u8, + pub dst: *mut u8, + pub width: i32, + pub stride: i32, + pub bpp: i32, + pub rects: Vec, + _priv: (), +} + +unsafe impl Send for ImgCopyWork {} + +impl ImgCopyWork { + pub unsafe fn new() -> Self { + Self { + src: ptr::null_mut(), + dst: ptr::null_mut(), + width: 0, + stride: 0, + bpp: 0, + rects: vec![], + _priv: (), + } + } +} + +impl CpuWork for ImgCopyWork { + fn run(&mut self) -> Option> { + for rect in &self.rects { + let mut offset = rect.y1() * self.stride + rect.x1() * self.bpp; + if rect.width() == self.width { + let offset = offset as usize; + let len = rect.height() * self.stride; + unsafe { + ptr::copy_nonoverlapping(self.src.add(offset), self.dst.add(offset), len as _); + } + } else { + let len = rect.width() * self.bpp; + for _ in 0..rect.height() { + unsafe { + ptr::copy_nonoverlapping( + self.src.add(offset as _), + self.dst.add(offset as _), + len as _, + ); + } + offset += self.stride; + } + } + } + None + } +} diff --git a/src/cpu_worker/jobs/read_write.rs b/src/cpu_worker/jobs/read_write.rs index c8790529..791ee22f 100644 --- a/src/cpu_worker/jobs/read_write.rs +++ b/src/cpu_worker/jobs/read_write.rs @@ -36,7 +36,6 @@ pub struct ReadWriteWork { unsafe impl Send for ReadWriteWork {} impl ReadWriteWork { - #[expect(dead_code)] pub unsafe fn new() -> Self { let cancel = Arc::new(CancelState::default()); ReadWriteWork { @@ -53,7 +52,6 @@ impl ReadWriteWork { } } - #[expect(dead_code)] pub fn config(&mut self) -> &mut ReadWriteWorkConfig { self.config.as_mut().unwrap() } diff --git a/src/gfx_api.rs b/src/gfx_api.rs index 5ca68519..23ac5bab 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -538,7 +538,6 @@ pub trait ShmGfxTexture: GfxTexture { } pub trait AsyncShmGfxTextureCallback { - #[expect(dead_code)] fn completed(self: Rc, res: Result<(), GfxError>); } @@ -689,7 +688,6 @@ pub fn cross_intersect_formats( } impl PendingShmUpload { - #[expect(dead_code)] pub fn new(cancel: Rc, id: u64) -> Self { Self { cancel, id } } diff --git a/src/gfx_apis/vulkan.rs b/src/gfx_apis/vulkan.rs index 59923d50..4562377b 100644 --- a/src/gfx_apis/vulkan.rs +++ b/src/gfx_apis/vulkan.rs @@ -19,7 +19,7 @@ use { crate::{ allocator::{Allocator, AllocatorError}, async_engine::AsyncEngine, - cpu_worker::CpuWorker, + cpu_worker::{jobs::read_write::ReadWriteJobError, CpuWorker}, format::Format, gfx_api::{ AsyncShmGfxTexture, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage, @@ -199,6 +199,10 @@ pub enum VulkanError { DupDrm(#[source] DrmError), #[error("Graphics context has already been dropped")] Defunct, + #[error("Could not perform an async copy to the staging buffer")] + AsyncCopyToStaging(#[source] ReadWriteJobError), + #[error("The async shm texture is busy")] + AsyncCopyBusy, } impl From for GfxError { @@ -278,19 +282,28 @@ impl GfxContext for Context { } let tex = self .0 - .create_shm_texture(format, width, height, stride, data, false)?; + .create_shm_texture(format, width, height, stride, data, false, None)?; Ok(tex as _) } fn async_shmem_texture( self: Rc, - _format: &'static Format, - _width: i32, - _height: i32, - _stride: i32, - _cpu_worker: &Rc, + format: &'static Format, + width: i32, + height: i32, + stride: i32, + cpu_worker: &Rc, ) -> Result, GfxError> { - todo!() + let tex = self.0.create_shm_texture( + format, + width, + height, + stride, + &[], + false, + Some(cpu_worker), + )?; + Ok(tex) } fn allocator(&self) -> Rc { @@ -310,7 +323,7 @@ impl GfxContext for Context { ) -> Result, GfxError> { let fb = self .0 - .create_shm_texture(format, width, height, stride, &[], true)?; + .create_shm_texture(format, width, height, stride, &[], true, None)?; Ok(fb) } diff --git a/src/gfx_apis/vulkan/image.rs b/src/gfx_apis/vulkan/image.rs index 29469a79..53c26af4 100644 --- a/src/gfx_apis/vulkan/image.rs +++ b/src/gfx_apis/vulkan/image.rs @@ -1,13 +1,17 @@ use { crate::{ + clientmem::ClientMemOffset, format::Format, gfx_api::{ - GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture, ShmGfxTexture, SyncFile, + AsyncShmGfxTexture, AsyncShmGfxTextureCallback, AsyncShmGfxTextureUploadCancellable, + GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture, PendingShmUpload, + ShmGfxTexture, SyncFile, }, gfx_apis::vulkan::{ allocator::VulkanAllocation, device::VulkanDevice, format::VulkanModifierLimits, renderer::VulkanRenderer, shm_image::VulkanShmImage, VulkanError, }, + rect::Region, theme::Color, utils::{clonecell::CloneCell, on_drop::OnDrop}, video::dmabuf::{DmaBuf, PlaneVec}, @@ -53,6 +57,7 @@ pub struct VulkanImage { pub(super) render_view: Option, pub(super) image: Image, pub(super) is_undefined: Cell, + pub(super) contents_are_undefined: Cell, pub(super) ty: VulkanImageMemory, pub(super) render_ops: CloneCell>, pub(super) bridge: Option, @@ -380,6 +385,7 @@ impl VulkanDmaBufImageTemplate { }), format: self.dmabuf.format, is_undefined: Cell::new(true), + contents_are_undefined: Cell::new(false), bridge, })) } @@ -538,3 +544,58 @@ impl ShmGfxTexture for VulkanImage { self } } + +impl AsyncShmGfxTexture for VulkanImage { + fn async_upload( + self: Rc, + callback: Rc, + mem: &Rc, + damage: Region, + ) -> Result, GfxError> { + let VulkanImageMemory::Internal(shm) = &self.ty else { + unreachable!(); + }; + let pending = shm.async_upload(&self, mem, damage, callback)?; + Ok(pending) + } + + fn sync_upload(self: Rc, mem: &[Cell], damage: Region) -> Result<(), GfxError> { + let VulkanImageMemory::Internal(shm) = &self.ty else { + unreachable!(); + }; + if shm.async_data.as_ref().unwrap().busy.get() { + return Err(VulkanError::AsyncCopyBusy.into()); + } + shm.upload(&self, mem, Some(damage.rects()))?; + Ok(()) + } + + fn compatible_with( + &self, + format: &'static Format, + width: i32, + height: i32, + stride: i32, + ) -> bool { + self.format == format + && self.width == width as u32 + && self.height == height as u32 + && self.stride == stride as u32 + } + + fn into_texture(self: Rc) -> Rc { + self + } +} + +impl AsyncShmGfxTextureUploadCancellable for VulkanImage { + fn cancel(&self, id: u64) { + let VulkanImageMemory::Internal(shm) = &self.ty else { + unreachable!(); + }; + let data = shm.async_data.as_ref().unwrap(); + if data.callback_id.get() == id { + data.callback.take(); + } + } +} diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 71877a7f..0018700b 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -279,6 +279,9 @@ impl VulkanRenderer { for cmd in opts { if let GfxApiOpt::CopyTexture(c) = cmd { let tex = c.tex.clone().into_vk(&self.device.device); + if tex.contents_are_undefined.get() { + continue; + } if let VulkanImageMemory::DmaBuf(_) = &tex.ty { memory.sample.push(tex.clone()) } @@ -458,6 +461,10 @@ impl VulkanRenderer { } GfxApiOpt::CopyTexture(c) => { let tex = c.tex.as_vk(&self.device.device); + if tex.contents_are_undefined.get() { + log::warn!("Ignoring undefined texture"); + continue; + } let copy_type = match c.alpha.is_some() { true => TexCopyType::Multiply, false => TexCopyType::Identity, @@ -807,6 +814,7 @@ impl VulkanRenderer { stride as i32, &[], true, + None, )?; (&*tmp_tex as &dyn GfxFramebuffer) .copy_texture( diff --git a/src/gfx_apis/vulkan/shm_image.rs b/src/gfx_apis/vulkan/shm_image.rs index a21698a3..24ef74e5 100644 --- a/src/gfx_apis/vulkan/shm_image.rs +++ b/src/gfx_apis/vulkan/shm_image.rs @@ -1,7 +1,15 @@ use { crate::{ + clientmem::ClientMemOffset, + cpu_worker::{ + jobs::{ + img_copy::ImgCopyWork, + read_write::{ReadWriteJobError, ReadWriteWork}, + }, + CpuJob, CpuWork, CpuWorker, + }, format::{Format, FormatShmInfo}, - gfx_api::SyncFile, + gfx_api::{AsyncShmGfxTextureCallback, PendingShmUpload, SyncFile}, gfx_apis::vulkan::{ allocator::VulkanAllocation, command::VulkanCommandBuffer, @@ -11,8 +19,8 @@ use { staging::VulkanStagingBuffer, VulkanError, }, - rect::Rect, - utils::{errorfmt::ErrorFmt, on_drop::OnDrop}, + rect::{Rect, Region}, + utils::{clonecell::CloneCell, errorfmt::ErrorFmt, on_drop::OnDrop}, }, ash::vk::{ AccessFlags2, BufferImageCopy2, BufferMemoryBarrier2, CommandBufferBeginInfo, @@ -24,14 +32,32 @@ use { }, gpu_alloc::UsageFlags, isnt::std_1::primitive::IsntSliceExt, - std::{cell::Cell, ptr, rc::Rc, slice}, + std::{ + cell::{Cell, RefCell}, + ptr, + rc::Rc, + slice, + }, + uapi::OwnedFd, }; pub struct VulkanShmImage { - pub(super) _size: DeviceSize, + pub(super) size: DeviceSize, pub(super) stride: u32, pub(super) _allocation: VulkanAllocation, pub(super) shm_info: &'static FormatShmInfo, + pub(super) async_data: Option, +} + +pub struct VulkanShmImageAsyncData { + pub(super) busy: Cell, + pub(super) io_job: Cell>>, + pub(super) copy_job: Cell>>, + pub(super) staging: CloneCell>>, + pub(super) callback: Cell>>, + pub(super) callback_id: Cell, + pub(super) regions: RefCell>>, + pub(super) cpu: Rc, } impl VulkanShmImage { @@ -133,6 +159,30 @@ impl VulkanShmImage { ptr::copy_nonoverlapping(buf, mem, total_size as usize); } })?; + let Some((cmd, fence, sync_file, point)) = + self.submit_buffer_to_image_copy(img, &staging, cpy)? + else { + return Ok(()); + }; + let future = img.renderer.eng.spawn(await_upload( + point, + img.clone(), + cmd, + sync_file, + fence, + staging, + )); + img.renderer.pending_uploads.set(point, future); + Ok(()) + } + + fn submit_buffer_to_image_copy( + &self, + img: &Rc, + staging: &VulkanStagingBuffer, + regions: &[BufferImageCopy2], + ) -> Result, Rc, SyncFile, u64)>, VulkanError> + { let memory_barrier = |sam, ssm, dam, dsm| { BufferMemoryBarrier2::default() .buffer(staging.buffer) @@ -185,7 +235,7 @@ impl VulkanShmImage { .src_buffer(staging.buffer) .dst_image(img.image) .dst_image_layout(ImageLayout::TRANSFER_DST_OPTIMAL) - .regions(cpy); + .regions(regions); let cmd = img.renderer.allocate_command_buffer()?; let dev = &img.renderer.device.device; let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(cmd.buffer); @@ -210,25 +260,17 @@ impl VulkanShmImage { .map_err(VulkanError::Submit)?; } img.is_undefined.set(false); + img.contents_are_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(()); + return Ok(None); } }; let point = img.renderer.allocate_point(); - let future = img.renderer.eng.spawn(await_upload( - point, - img.clone(), - cmd, - release_sync_file, - release_fence, - staging, - )); - img.renderer.pending_uploads.set(point, future); - Ok(()) + Ok(Some((cmd, release_fence, release_sync_file, point))) } } @@ -252,6 +294,325 @@ async fn await_upload( img.renderer.pending_uploads.remove(&id); } +impl VulkanShmImageAsyncData { + fn complete(&self, result: Result<(), VulkanError>) { + self.busy.set(false); + if let Some(cb) = self.callback.take() { + cb.completed(result.map_err(|e| e.into())); + } + } +} + +impl VulkanShmImage { + pub fn async_upload( + &self, + img: &Rc, + client_mem: &Rc, + damage: Region, + callback: Rc, + ) -> Result, VulkanError> { + let data = self.async_data.as_ref().unwrap(); + let res = self.try_async_upload(img, data, client_mem, damage); + match res { + Ok(()) => { + let id = img.renderer.allocate_point(); + data.callback_id.set(id); + data.callback.set(Some(callback)); + Ok(Some(PendingShmUpload::new(img.clone(), id))) + } + Err(e) => Err(e), + } + } + + fn try_async_upload( + &self, + img: &Rc, + data: &VulkanShmImageAsyncData, + client_mem: &Rc, + mut damage: Region, + ) -> Result<(), VulkanError> { + if data.busy.get() { + return Err(VulkanError::AsyncCopyBusy); + } + if self.size > client_mem.ptr().len() as u64 { + return Err(VulkanError::InvalidBufferSize); + } + data.busy.set(true); + if img.contents_are_undefined.get() { + damage = Region::new2(Rect::new_sized(0, 0, img.width as _, img.height as _).unwrap()) + } + + let copies = &mut *data.regions.borrow_mut(); + copies.clear(); + + let mut copy = |x, y, width, height| { + let buffer_offset = (y as u32 * img.stride + x as u32 * self.shm_info.bpp) as u64; + let copy = BufferImageCopy2::default() + .buffer_offset(buffer_offset) + .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, + }) + .buffer_image_height(img.height) + .buffer_row_length(img.stride / self.shm_info.bpp); + copies.push(copy); + }; + for damage in damage.rects() { + 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; + } + copy( + damage.x1(), + damage.y1(), + damage.width() as u32, + damage.height() as u32, + ); + } + + if let Some(staging) = data.staging.get() { + return self.async_upload_initiate_copy(img, data, &staging, copies, client_mem); + } + + let img2 = img.clone(); + let client_mem = client_mem.clone(); + img.renderer.device.create_shm_staging( + &img.renderer, + &data.cpu, + self.size, + true, + false, + move |res| { + let VulkanImageMemory::Internal(shm) = &img2.ty else { + unreachable!(); + }; + if let Err(e) = shm.async_upload_after_allocation(&img2, &client_mem, res) { + shm.async_data.as_ref().unwrap().complete(Err(e)); + } + }, + ) + } + + fn async_upload_after_allocation( + &self, + img: &Rc, + client_mem: &Rc, + res: Result, + ) -> Result<(), VulkanError> { + let staging = Rc::new(res?); + let data = self.async_data.as_ref().unwrap(); + data.staging.set(Some(staging.clone())); + let copies = &*data.regions.borrow(); + self.async_upload_initiate_copy(img, data, &staging, copies, client_mem) + } + + fn async_upload_initiate_copy( + &self, + img: &Rc, + data: &VulkanShmImageAsyncData, + staging: &VulkanStagingBuffer, + copies: &[BufferImageCopy2], + client_mem: &Rc, + ) -> Result<(), VulkanError> { + img.renderer.check_defunct()?; + + let id = img.renderer.allocate_point(); + let pending; + if client_mem.pool().sigbus_impossible() { + let mut job = data.copy_job.take().unwrap_or_else(|| { + Box::new(CopyUploadJob { + img: None, + id, + _mem: None, + work: unsafe { ImgCopyWork::new() }, + }) + }); + job.id = id; + job.img = Some(img.clone()); + job._mem = Some(client_mem.clone()); + job.work.src = client_mem.ptr() as _; + job.work.dst = staging.allocation.mem.unwrap(); + job.work.width = img.width as _; + job.work.stride = img.stride as _; + job.work.bpp = self.shm_info.bpp as _; + job.work.rects.clear(); + for copy in copies { + job.work.rects.push( + Rect::new_sized( + copy.image_offset.x as _, + copy.image_offset.y as _, + copy.image_extent.width as _, + copy.image_extent.height as _, + ) + .unwrap(), + ); + } + pending = data.cpu.submit(job); + } else { + let mut min_offset = client_mem.ptr().len() as u64; + let mut max_offset = 0; + for copy in copies { + min_offset = min_offset.min(copy.buffer_offset); + let len = img.stride * (copy.image_extent.height - 1) + + copy.image_extent.width * self.shm_info.bpp; + max_offset = max_offset.max(copy.buffer_offset + len as u64); + } + let mut job = data.io_job.take().unwrap_or_else(|| { + Box::new(IoUploadJob { + img: None, + id, + _mem: None, + work: unsafe { ReadWriteWork::new() }, + fd: None, + }) + }); + job.id = id; + job.img = Some(img.clone()); + job._mem = Some(client_mem.clone()); + job.fd = Some(client_mem.pool().fd().clone()); + unsafe { + let config = job.work.config(); + config.fd = client_mem.pool().fd().raw(); + config.offset = client_mem.offset() + min_offset as usize; + config.ptr = staging.allocation.mem.unwrap().add(min_offset as _); + config.len = max_offset.saturating_sub(min_offset) as usize; + config.write = false; + } + pending = data.cpu.submit(job); + } + + img.renderer.pending_cpu_jobs.set(id, pending); + + Ok(()) + } + + fn async_upload_copy_buffer_to_image( + &self, + img: &Rc, + data: &VulkanShmImageAsyncData, + res: Result<(), ReadWriteJobError>, + ) -> Result<(), VulkanError> { + if let Err(e) = res { + return Err(VulkanError::AsyncCopyToStaging(e)); + } + img.renderer.check_defunct()?; + let regions = &*data.regions.borrow(); + let staging = data.staging.get().unwrap(); + staging.upload(|_, _| ())?; + let Some((cmd, fence, sync_file, point)) = + self.submit_buffer_to_image_copy(img, &staging, regions)? + else { + return Ok(()); + }; + let future = img.renderer.eng.spawn(await_async_upload( + point, + img.clone(), + cmd, + fence, + sync_file, + )); + img.renderer.pending_uploads.set(point, future); + Ok(()) + } +} + +pub(super) struct IoUploadJob { + img: Option>, + id: u64, + _mem: Option>, + fd: Option>, + work: ReadWriteWork, +} + +pub(super) struct CopyUploadJob { + img: Option>, + id: u64, + _mem: Option>, + work: ImgCopyWork, +} + +impl CpuJob for IoUploadJob { + fn work(&mut self) -> &mut dyn CpuWork { + &mut self.work + } + + fn completed(mut self: Box) { + self._mem = None; + self.fd = None; + let img = self.img.take().unwrap(); + let res = self.work.config().result.take().unwrap(); + complete_async_upload(&img, self.id, res, |data| data.io_job.set(Some(self))); + } +} + +impl CpuJob for CopyUploadJob { + fn work(&mut self) -> &mut dyn CpuWork { + &mut self.work + } + + fn completed(mut self: Box) { + self._mem = None; + let img = self.img.take().unwrap(); + complete_async_upload(&img, self.id, Ok(()), |data| data.copy_job.set(Some(self))); + } +} + +fn complete_async_upload( + img: &Rc, + id: u64, + res: Result<(), ReadWriteJobError>, + store: impl FnOnce(&VulkanShmImageAsyncData), +) { + img.renderer.pending_cpu_jobs.remove(&id); + let VulkanImageMemory::Internal(shm) = &img.ty else { + unreachable!(); + }; + let data = shm.async_data.as_ref().unwrap(); + store(data); + if let Err(e) = shm.async_upload_copy_buffer_to_image(img, data, res) { + data.complete(Err(e)); + } +} + +async fn await_async_upload( + id: u64, + img: Rc, + buf: Rc, + _fence: Rc, + sync_file: SyncFile, +) { + 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: {}", + ErrorFmt(e) + ); + img.renderer.block(); + } + img.renderer.command_buffers.push(buf); + img.renderer.pending_uploads.remove(&id); + let VulkanImageMemory::Internal(shm) = &img.ty else { + unreachable!(); + }; + let data = shm.async_data.as_ref().unwrap(); + data.complete(Ok(())); +} + impl VulkanRenderer { pub fn create_shm_texture( self: &Rc, @@ -261,6 +622,7 @@ impl VulkanRenderer { stride: i32, data: &[Cell], for_download: bool, + cpu_worker: Option<&Rc>, ) -> Result, VulkanError> { let Some(shm_info) = &format.shm_info else { return Err(VulkanError::UnsupportedShmFormat(format.name)); @@ -283,7 +645,7 @@ impl VulkanRenderer { if width > shm.limits.max_width || height > shm.limits.max_height { return Err(VulkanError::ImageTooLarge); } - let size = stride.checked_mul(height).ok_or(VulkanError::ShmOverflow)?; + let size = stride.checked_mul(height).ok_or(VulkanError::ShmOverflow)? as u64; let usage = ImageUsageFlags::TRANSFER_SRC | match for_download { true => ImageUsageFlags::COLOR_ATTACHMENT, @@ -335,11 +697,25 @@ impl VulkanRenderer { .create_image_view(&image_view_create_info, None) }; let view = view.map_err(VulkanError::CreateImageView)?; + let mut async_data = None; + if let Some(cpu) = cpu_worker { + async_data = Some(VulkanShmImageAsyncData { + busy: Cell::new(false), + io_job: Default::default(), + copy_job: Default::default(), + staging: Default::default(), + callback: Default::default(), + callback_id: Cell::new(0), + regions: Default::default(), + cpu: cpu.clone(), + }); + } let shm = VulkanShmImage { - _size: size as u64, + size, stride, _allocation: allocation, shm_info, + async_data, }; destroy_image.forget(); let img = Rc::new(VulkanImage { @@ -352,6 +728,7 @@ impl VulkanRenderer { render_view: None, image, is_undefined: Cell::new(true), + contents_are_undefined: Cell::new(true), ty: VulkanImageMemory::Internal(shm), render_ops: Default::default(), bridge: None, diff --git a/src/gfx_apis/vulkan/staging.rs b/src/gfx_apis/vulkan/staging.rs index e2d6b39f..5a3faeac 100644 --- a/src/gfx_apis/vulkan/staging.rs +++ b/src/gfx_apis/vulkan/staging.rs @@ -51,7 +51,6 @@ impl VulkanDevice { }) } - #[expect(dead_code)] pub(super) fn create_shm_staging( self: &Rc, renderer: &Rc,