diff --git a/src/cursor.rs b/src/cursor.rs index 85342742..2e8033e7 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -326,7 +326,7 @@ impl CursorImageScaled { extents: Rect::new_sized(-xhot, -yhot, width, height).unwrap(), tex: ctx .clone() - .shmem_texture(None, data, ARGB8888, width, height, width * 4)?, + .shmem_texture(None, data, ARGB8888, width, height, width * 4, None)?, })) } } diff --git a/src/gfx_api.rs b/src/gfx_api.rs index 51b45d8c..ec622241 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -528,6 +528,7 @@ pub trait GfxContext: Debug { width: i32, height: i32, stride: i32, + damage: Option<&[Rect]>, ) -> Result, GfxError>; fn gbm(&self) -> &GbmDevice; diff --git a/src/gfx_apis/gl/renderer/context.rs b/src/gfx_apis/gl/renderer/context.rs index 4b31edba..9fda33dc 100644 --- a/src/gfx_apis/gl/renderer/context.rs +++ b/src/gfx_apis/gl/renderer/context.rs @@ -14,6 +14,7 @@ use { renderer::{framebuffer::Framebuffer, image::Image}, GfxGlState, RenderError, Texture, }, + rect::Rect, video::{ dmabuf::DmaBuf, drm::{sync_obj::SyncObjCtx, Drm}, @@ -270,6 +271,7 @@ impl GfxContext for GlRenderContext { width: i32, height: i32, stride: i32, + _damage: Option<&[Rect]>, ) -> Result, GfxError> { (&self) .shmem_texture(data, format, width, height, stride) diff --git a/src/gfx_apis/vulkan.rs b/src/gfx_apis/vulkan.rs index 7468fc7b..dc91bf95 100644 --- a/src/gfx_apis/vulkan.rs +++ b/src/gfx_apis/vulkan.rs @@ -11,6 +11,7 @@ mod renderer; mod sampler; mod semaphore; mod shaders; +mod shm_image; mod staging; mod util; @@ -25,6 +26,7 @@ use { image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer, }, io_uring::IoUring, + rect::Rect, utils::oserror::OsError, video::{ dmabuf::DmaBuf, @@ -230,6 +232,7 @@ impl GfxContext for Context { width: i32, height: i32, stride: i32, + damage: Option<&[Rect]>, ) -> Result, GfxError> { if let Some(old) = old { let old = old.into_vk(&self.0.device.device); @@ -242,7 +245,7 @@ impl GfxContext for Context { && shm.stride as i32 == stride && old.format.vk_format == format.vk_format { - shm.upload(data)?; + shm.upload(&old, data, damage)?; return Ok(old); } } diff --git a/src/gfx_apis/vulkan/image.rs b/src/gfx_apis/vulkan/image.rs index d78fb413..7216498e 100644 --- a/src/gfx_apis/vulkan/image.rs +++ b/src/gfx_apis/vulkan/image.rs @@ -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, } -pub struct VulkanShmImage { - pub(super) to_flush: RefCell>>, - 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]) -> 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, - format: &'static Format, - width: i32, - height: i32, - stride: i32, - data: &[Cell], - for_download: bool, - ) -> Result, 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, dmabuf: &DmaBuf, diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index b751da4c..3a478ebe 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -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, pub(super) memory: RefCell, pub(super) pending_frames: CopyHashMap>, + pub(super) pending_uploads: CopyHashMap>, pub(super) allocator: Rc, pub(super) last_point: NumCell, pub(super) buffer_resv_user: BufferResvUser, @@ -93,11 +92,8 @@ pub(super) enum TexSourceType { #[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>, @@ -109,7 +105,6 @@ pub(super) struct PendingFrame { renderer: Rc, cmd: Cell>>, _textures: Vec, - _staging: Vec<(Rc, VulkanStagingBuffer)>, wait_semaphores: Cell>>, waiter: Cell>>, _release_fence: Option>, @@ -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) -> 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, buf: Rc) { - 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, VulkanError> { + pub(super) fn allocate_command_buffer(&self) -> Result, 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) diff --git a/src/gfx_apis/vulkan/shm_image.rs b/src/gfx_apis/vulkan/shm_image.rs new file mode 100644 index 00000000..c280d4ac --- /dev/null +++ b/src/gfx_apis/vulkan/shm_image.rs @@ -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, + buffer: &[Cell], + 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, + buf: Rc, + sync_file: SyncFile, + _fence: Rc, + _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, + format: &'static Format, + width: i32, + height: i32, + stride: i32, + data: &[Cell], + for_download: bool, + ) -> Result, 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) + } +} diff --git a/src/ifs/wl_buffer.rs b/src/ifs/wl_buffer.rs index 51feccc6..8f14f092 100644 --- a/src/ifs/wl_buffer.rs +++ b/src/ifs/wl_buffer.rs @@ -4,25 +4,32 @@ use { clientmem::{ClientMem, ClientMemError, ClientMemOffset}, format::{Format, ARGB8888}, gfx_api::{GfxError, GfxFramebuffer, GfxImage, GfxTexture}, + ifs::wl_surface::WlSurface, leaks::Tracker, object::{Object, Version}, rect::Rect, theme::Color, - utils::{clonecell::CloneCell, errorfmt::ErrorFmt}, + utils::errorfmt::ErrorFmt, video::dmabuf::DmaBuf, wire::{wl_buffer::*, WlBufferId}, }, std::{ cell::{Cell, RefCell}, - ops::Deref, rc::Rc, }, thiserror::Error, }; pub enum WlBufferStorage { - Shm { mem: ClientMemOffset, stride: i32 }, - Dmabuf(Rc), + Shm { + mem: ClientMemOffset, + stride: i32, + }, + Dmabuf { + img: Rc, + tex: Option>, + fb: Option>, + }, } pub struct WlBuffer { @@ -35,8 +42,6 @@ pub struct WlBuffer { render_ctx_version: Cell, pub storage: RefCell>, pub color: Option, - pub texture: CloneCell>>, - pub famebuffer: CloneCell>>, width: i32, height: i32, pub tracker: Tracker, @@ -65,11 +70,13 @@ impl WlBuffer { format, width, height, - texture: CloneCell::new(None), - famebuffer: Default::default(), dmabuf: Some(dmabuf), render_ctx_version: Cell::new(client.state.render_ctx_version.get()), - storage: RefCell::new(Some(WlBufferStorage::Dmabuf(img.clone()))), + storage: RefCell::new(Some(WlBufferStorage::Dmabuf { + img: img.clone(), + tex: None, + fb: None, + })), tracker: Default::default(), color: None, } @@ -110,9 +117,7 @@ impl WlBuffer { storage: RefCell::new(Some(WlBufferStorage::Shm { mem, stride })), width, height, - texture: CloneCell::new(None), tracker: Default::default(), - famebuffer: Default::default(), color: None, }) } @@ -136,78 +141,110 @@ impl WlBuffer { storage: RefCell::new(None), width: 1, height: 1, - texture: CloneCell::new(None), tracker: Default::default(), - famebuffer: Default::default(), color: Some(Color::from_u32_rgba_premultiplied(r, g, b, a)), } } - pub fn handle_gfx_context_change(&self) { + pub fn handle_gfx_context_change(&self, surface: Option<&WlSurface>) { let ctx_version = self.client.state.render_ctx_version.get(); if self.render_ctx_version.replace(ctx_version) == ctx_version { return; } - let had_texture = self.texture.set(None).is_some(); - self.famebuffer.set(None); - self.reset_storage_after_gfx_context_change(); + let had_texture = self.reset_gfx_objects(surface); if had_texture { - self.update_texture_or_log(); + if let Some(surface) = surface { + self.update_texture_or_log(surface, None); + } } } - fn reset_storage_after_gfx_context_change(&self) { + fn reset_gfx_objects(&self, surface: Option<&WlSurface>) -> bool { let mut storage = self.storage.borrow_mut(); - if let Some(storage) = &mut *storage { - if let WlBufferStorage::Shm { .. } = storage { - return; + let Some(s) = &mut *storage else { + return false; + }; + let had_texture = match s { + WlBufferStorage::Shm { .. } => { + return match surface { + Some(s) => s.shm_texture.take().is_some(), + None => false, + }; } - } + WlBufferStorage::Dmabuf { tex, .. } => tex.is_some(), + }; *storage = None; - let ctx = match self.client.state.render_ctx.get() { - Some(ctx) => ctx, - _ => return, + let Some(ctx) = self.client.state.render_ctx.get() else { + return false; }; - if let Some(dmabuf) = &self.dmabuf { - let image = match ctx.dmabuf_img(dmabuf) { - Ok(image) => image, - Err(e) => { - log::error!( - "Cannot re-import wl_buffer after graphics context change: {}", - ErrorFmt(e) - ); - return; - } - }; - *storage = Some(WlBufferStorage::Dmabuf(image)); + let Some(dmabuf) = &self.dmabuf else { + return false; + }; + let img = match ctx.dmabuf_img(dmabuf) { + Ok(image) => image, + Err(e) => { + log::error!( + "Cannot re-import wl_buffer after graphics context change: {}", + ErrorFmt(e) + ); + return false; + } + }; + *storage = Some(WlBufferStorage::Dmabuf { + img, + tex: None, + fb: None, + }); + had_texture + } + + pub fn get_texture(&self, surface: &WlSurface) -> Option> { + match &*self.storage.borrow() { + None => None, + Some(s) => match s { + WlBufferStorage::Shm { .. } => surface.shm_texture.get(), + WlBufferStorage::Dmabuf { tex, .. } => tex.clone(), + }, } } - pub fn update_texture_or_log(&self) { - if let Err(e) = self.update_texture() { + pub fn update_texture_or_log(&self, surface: &WlSurface, damage: Option<&[Rect]>) { + if let Err(e) = self.update_texture(surface, damage) { log::warn!("Could not update texture: {}", ErrorFmt(e)); } } - fn update_texture(&self) -> Result<(), WlBufferError> { - let storage = self.storage.borrow_mut(); - let storage = match storage.deref() { + fn update_texture( + &self, + surface: &WlSurface, + damage: Option<&[Rect]>, + ) -> Result<(), WlBufferError> { + let old_shm_texture = surface.shm_texture.take(); + let storage = &mut *self.storage.borrow_mut(); + let storage = match storage { Some(s) => s, _ => return Ok(()), }; match storage { WlBufferStorage::Shm { mem, stride } => { - let old = self.texture.take(); if let Some(ctx) = self.client.state.render_ctx.get() { let tex = mem.access(|mem| { - ctx.shmem_texture(old, mem, self.format, self.width, self.height, *stride) + ctx.shmem_texture( + old_shm_texture, + mem, + self.format, + self.width, + self.height, + *stride, + damage, + ) })??; - self.texture.set(Some(tex)); + surface.shm_texture.set(Some(tex)); } } - WlBufferStorage::Dmabuf(img) => { - if self.texture.is_none() { - self.texture.set(Some(img.clone().to_texture()?)); + WlBufferStorage::Dmabuf { img, tex, .. } => { + if tex.is_none() { + *tex = Some(img.clone().to_texture()?); } } } @@ -215,8 +252,8 @@ impl WlBuffer { } pub fn update_framebuffer(&self) -> Result<(), WlBufferError> { - let storage = self.storage.borrow_mut(); - let storage = match storage.deref() { + let storage = &mut *self.storage.borrow_mut(); + let storage = match storage { Some(s) => s, _ => return Ok(()), }; @@ -224,9 +261,9 @@ impl WlBuffer { WlBufferStorage::Shm { .. } => { // nothing } - WlBufferStorage::Dmabuf(img) => { - if self.famebuffer.is_none() { - self.famebuffer.set(Some(img.clone().to_framebuffer()?)); + WlBufferStorage::Dmabuf { img, fb, .. } => { + if fb.is_none() { + *fb = Some(img.clone().to_framebuffer()?); } } } diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 82b10908..954fdc7b 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -21,7 +21,9 @@ use { cursor_user::{CursorUser, CursorUserId}, drm_feedback::DrmFeedback, fixed::Fixed, - gfx_api::{AcquireSync, BufferResv, BufferResvUser, ReleaseSync, SampleRect, SyncFile}, + gfx_api::{ + AcquireSync, BufferResv, BufferResvUser, GfxTexture, ReleaseSync, SampleRect, SyncFile, + }, ifs::{ wl_buffer::WlBuffer, wl_callback::WlCallback, @@ -261,6 +263,7 @@ pub struct WlSurface { pub buffer_abs_pos: Cell, pub need_extents_update: Cell, pub buffer: CloneCell>>, + pub shm_texture: CloneCell>>, pub buf_x: NumCell, pub buf_y: NumCell, pub children: RefCell>>, @@ -393,7 +396,8 @@ struct PendingState { opaque_region: Option>>, input_region: Option>>, frame_request: Vec>, - damage: bool, + damage_full: bool, + damage: Vec, presentation_feedback: Vec>, src_rect: Option>, dst_size: Option>, @@ -465,7 +469,14 @@ impl PendingState { self.offset = (dx1 + dx2, dy1 + dy2); } self.frame_request.append(&mut next.frame_request); - self.damage |= mem::take(&mut next.damage); + if !self.damage_full { + if self.damage.len() + next.damage.len() > MAX_DAMAGE { + self.damage_full = true; + self.damage.clear(); + } else { + self.damage.append(&mut next.damage); + } + } mem::swap( &mut self.presentation_feedback, &mut next.presentation_feedback, @@ -542,6 +553,7 @@ impl WlSurface { buffer_abs_pos: Cell::new(Default::default()), need_extents_update: Default::default(), buffer: Default::default(), + shm_texture: Default::default(), buf_x: Default::default(), buf_y: Default::default(), children: Default::default(), @@ -800,6 +812,8 @@ impl WlSurface { } } +const MAX_DAMAGE: usize = 32; + impl WlSurfaceRequestHandler for WlSurface { type Error = WlSurfaceError; @@ -819,6 +833,7 @@ impl WlSurfaceRequestHandler for WlSurface { *children = None; } self.buffer.set(None); + self.shm_texture.take(); if let Some(xwayland_serial) = self.xwayland_serial.get() { self.client .surfaces_by_xwayland_serial @@ -852,7 +867,9 @@ impl WlSurfaceRequestHandler for WlSurface { } fn damage(&self, _req: Damage, _slf: &Rc) -> Result<(), Self::Error> { - self.pending.borrow_mut().damage = true; + let pending = &mut *self.pending.borrow_mut(); + pending.damage.clear(); + pending.damage_full = true; Ok(()) } @@ -918,8 +935,19 @@ impl WlSurfaceRequestHandler for WlSurface { Ok(()) } - fn damage_buffer(&self, _req: DamageBuffer, _slf: &Rc) -> Result<(), Self::Error> { - self.pending.borrow_mut().damage = true; + fn damage_buffer(&self, req: DamageBuffer, _slf: &Rc) -> Result<(), Self::Error> { + let pending = &mut *self.pending.borrow_mut(); + if !pending.damage_full { + if pending.damage.len() >= MAX_DAMAGE || self.shm_texture.is_none() { + pending.damage.clear(); + pending.damage_full = true; + } else { + let Some(rect) = Rect::new_sized(req.x, req.y, req.width, req.height) else { + return Err(WlSurfaceError::InvalidRect); + }; + pending.damage.push(rect); + } + } Ok(()) } @@ -980,7 +1008,13 @@ impl WlSurface { old_raw_size = Some(buffer.buffer.rect); } if let Some(buffer) = buffer_change { - buffer.update_texture_or_log(); + let damage = match pending.damage_full { + true => None, + false => Some(&pending.damage[..]), + }; + buffer.update_texture_or_log(self, damage); + pending.damage.clear(); + pending.damage_full = false; let (sync, release_sync) = match pending.explicit_sync { false => (AcquireSync::Implicit, ReleaseSync::Implicit), true => (AcquireSync::Unnecessary, ReleaseSync::Explicit), @@ -998,6 +1032,7 @@ impl WlSurface { }; self.buffer.set(Some(Rc::new(surface_buffer))); } else { + self.shm_texture.take(); self.buf_x.set(0); self.buf_y.set(0); for (_, cursor) in &self.cursors { @@ -1669,6 +1704,8 @@ pub enum WlSurfaceError { MissingSyncPoints, #[error("No buffer is attached but acquire or release point is set")] UnexpectedSyncPoints, + #[error("The supplied region is invalid")] + InvalidRect, } efrom!(WlSurfaceError, ClientError); efrom!(WlSurfaceError, XdgSurfaceError); diff --git a/src/renderer.rs b/src/renderer.rs index 911a9f39..12c2bfe7 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -427,10 +427,10 @@ impl Renderer<'_> { }; } render!(&children.below); - self.render_buffer(&buffer, alpha, x, y, *tpoints, size, bounds); + self.render_buffer(surface, &buffer, alpha, x, y, *tpoints, size, bounds); render!(&children.above); } else { - self.render_buffer(&buffer, alpha, x, y, *tpoints, size, bounds); + self.render_buffer(surface, &buffer, alpha, x, y, *tpoints, size, bounds); } if let Some(result) = self.result.as_deref_mut() { { @@ -446,6 +446,7 @@ impl Renderer<'_> { pub fn render_buffer( &mut self, + surface: &WlSurface, buffer: &Rc, alpha: Option, x: i32, @@ -454,7 +455,7 @@ impl Renderer<'_> { tsize: (i32, i32), bounds: Option<&Rect>, ) { - if let Some(tex) = buffer.buffer.texture.get() { + if let Some(tex) = buffer.buffer.get_texture(surface) { self.base.render_texture( &tex, alpha, diff --git a/src/state.rs b/src/state.rs index 0d817293..e7738daf 100644 --- a/src/state.rs +++ b/src/state.rs @@ -43,7 +43,7 @@ use { wl_subsurface::SubsurfaceIds, zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1}, zwp_input_popup_surface_v2::ZwpInputPopupSurfaceV2, - NoneSurfaceExt, WlSurface, + NoneSurfaceExt, }, wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, wp_drm_lease_device_v1::WpDrmLeaseDeviceV1Global, @@ -87,7 +87,7 @@ use { xkbcommon::{KeyboardStateIds, XkbContext, XkbKeymap, XkbState}, xwayland::{self, XWaylandEvent}, }, - ahash::AHashMap, + ahash::{AHashMap, AHashSet}, bstr::ByteSlice, jay_config::{ video::{GfxApi, Transform}, @@ -456,17 +456,22 @@ impl State { node.textures.clear(); node.node_visit_children(self); } - fn visit_surface(&mut self, node: &Rc) { - if let Some(buffer) = node.buffer.get() { - buffer.buffer.handle_gfx_context_change(); - } - node.node_visit_children(self); - } } Walker.visit_display(&self.root); for client in self.clients.clients.borrow_mut().values() { + let mut updated_buffers = AHashSet::new(); + for surface in client.data.objects.surfaces.lock().values() { + if let Some(buffer) = surface.buffer.get() { + updated_buffers.insert(buffer.buffer.id); + buffer.buffer.handle_gfx_context_change(Some(surface)); + } else { + surface.shm_texture.take(); + } + } for buffer in client.data.objects.buffers.lock().values() { - buffer.handle_gfx_context_change(); + if !updated_buffers.contains(&buffer.id) { + buffer.handle_gfx_context_change(None); + } } } } diff --git a/src/text.rs b/src/text.rs index c4cb221e..77dfbacf 100644 --- a/src/text.rs +++ b/src/text.rs @@ -211,10 +211,15 @@ fn render2( Err(e) => return Err(TextError::ImageData(e)), }; let old = old.map(|o| o.texture); - match ctx - .clone() - .shmem_texture(old, bytes, ARGB8888, width, height, data.image.stride()) - { + match ctx.clone().shmem_texture( + old, + bytes, + ARGB8888, + width, + height, + data.image.stride(), + None, + ) { Ok(t) => Ok(TextTexture { config: Rc::new(config.to_static()), texture: t, diff --git a/src/tree/output.rs b/src/tree/output.rs index 93dec03e..a9652984 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -178,49 +178,52 @@ impl OutputNode { capture.send_failed(); continue; } - if let Some(WlBufferStorage::Shm { mem, stride }) = - wl_buffer.storage.borrow_mut().deref() - { - let res = self.state.perform_shm_screencopy( - tex, - self.global.pos.get(), - x_off, - y_off, - size, - &capture, - mem, - *stride, - wl_buffer.format, - Transform::None, - ); - if let Err(e) = res { - log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e)); - capture.send_failed(); - continue; - } - } else { - let fb = match wl_buffer.famebuffer.get() { - Some(fb) => fb, - _ => { - log::warn!("Capture buffer has no framebuffer"); - capture.send_failed(); - continue; + if let Some(storage) = wl_buffer.storage.borrow_mut().deref() { + match storage { + WlBufferStorage::Shm { mem, stride } => { + let res = self.state.perform_shm_screencopy( + tex, + self.global.pos.get(), + x_off, + y_off, + size, + &capture, + mem, + *stride, + wl_buffer.format, + Transform::None, + ); + if let Err(e) = res { + log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e)); + capture.send_failed(); + continue; + } + } + WlBufferStorage::Dmabuf { fb, .. } => { + let fb = match fb { + Some(fb) => fb, + _ => { + log::warn!("Capture buffer has no framebuffer"); + capture.send_failed(); + continue; + } + }; + let res = self.state.perform_screencopy( + tex, + &fb, + self.global.pos.get(), + render_hardware_cursors, + x_off - capture.rect.x1(), + y_off - capture.rect.y1(), + size, + Transform::None, + ); + if let Err(e) = res { + log::warn!("Could not perform screencopy: {}", ErrorFmt(e)); + capture.send_failed(); + continue; + } } - }; - let res = self.state.perform_screencopy( - tex, - &fb, - self.global.pos.get(), - render_hardware_cursors, - x_off - capture.rect.x1(), - y_off - capture.rect.y1(), - size, - Transform::None, - ); - if let Err(e) = res { - log::warn!("Could not perform screencopy: {}", ErrorFmt(e)); - capture.send_failed(); - continue; } } if capture.with_damage.get() { diff --git a/src/utils/cell_ext.rs b/src/utils/cell_ext.rs index e386f4ba..074c5083 100644 --- a/src/utils/cell_ext.rs +++ b/src/utils/cell_ext.rs @@ -1,16 +1,16 @@ -use std::cell::Cell; +use {crate::utils::ptr_ext::PtrExt, std::cell::Cell}; pub trait CellExt { fn is_some(&self) -> bool; fn is_none(&self) -> bool; } -impl CellExt for Cell> { +impl CellExt for Cell> { fn is_some(&self) -> bool { - self.get().is_some() + unsafe { self.as_ptr().deref().is_some() } } fn is_none(&self) -> bool { - self.get().is_none() + !self.is_some() } }