1
0
Fork 0
forked from wry/wry

vulkan: optimize shm handling

This commit is contained in:
Julian Orth 2024-05-23 22:30:30 +02:00
parent 03c02be34c
commit af80fada6c
14 changed files with 629 additions and 413 deletions

View file

@ -326,7 +326,7 @@ impl CursorImageScaled {
extents: Rect::new_sized(-xhot, -yhot, width, height).unwrap(), extents: Rect::new_sized(-xhot, -yhot, width, height).unwrap(),
tex: ctx tex: ctx
.clone() .clone()
.shmem_texture(None, data, ARGB8888, width, height, width * 4)?, .shmem_texture(None, data, ARGB8888, width, height, width * 4, None)?,
})) }))
} }
} }

View file

@ -528,6 +528,7 @@ pub trait GfxContext: Debug {
width: i32, width: i32,
height: i32, height: i32,
stride: i32, stride: i32,
damage: Option<&[Rect]>,
) -> Result<Rc<dyn GfxTexture>, GfxError>; ) -> Result<Rc<dyn GfxTexture>, GfxError>;
fn gbm(&self) -> &GbmDevice; fn gbm(&self) -> &GbmDevice;

View file

@ -14,6 +14,7 @@ use {
renderer::{framebuffer::Framebuffer, image::Image}, renderer::{framebuffer::Framebuffer, image::Image},
GfxGlState, RenderError, Texture, GfxGlState, RenderError, Texture,
}, },
rect::Rect,
video::{ video::{
dmabuf::DmaBuf, dmabuf::DmaBuf,
drm::{sync_obj::SyncObjCtx, Drm}, drm::{sync_obj::SyncObjCtx, Drm},
@ -270,6 +271,7 @@ impl GfxContext for GlRenderContext {
width: i32, width: i32,
height: i32, height: i32,
stride: i32, stride: i32,
_damage: Option<&[Rect]>,
) -> Result<Rc<dyn GfxTexture>, GfxError> { ) -> Result<Rc<dyn GfxTexture>, GfxError> {
(&self) (&self)
.shmem_texture(data, format, width, height, stride) .shmem_texture(data, format, width, height, stride)

View file

@ -11,6 +11,7 @@ mod renderer;
mod sampler; mod sampler;
mod semaphore; mod semaphore;
mod shaders; mod shaders;
mod shm_image;
mod staging; mod staging;
mod util; mod util;
@ -25,6 +26,7 @@ use {
image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer, image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer,
}, },
io_uring::IoUring, io_uring::IoUring,
rect::Rect,
utils::oserror::OsError, utils::oserror::OsError,
video::{ video::{
dmabuf::DmaBuf, dmabuf::DmaBuf,
@ -230,6 +232,7 @@ impl GfxContext for Context {
width: i32, width: i32,
height: i32, height: i32,
stride: i32, stride: i32,
damage: Option<&[Rect]>,
) -> Result<Rc<dyn GfxTexture>, GfxError> { ) -> Result<Rc<dyn GfxTexture>, GfxError> {
if let Some(old) = old { if let Some(old) = old {
let old = old.into_vk(&self.0.device.device); let old = old.into_vk(&self.0.device.device);
@ -242,7 +245,7 @@ impl GfxContext for Context {
&& shm.stride as i32 == stride && shm.stride as i32 == stride
&& old.format.vk_format == format.vk_format && old.format.vk_format == format.vk_format
{ {
shm.upload(data)?; shm.upload(&old, data, damage)?;
return Ok(old); return Ok(old);
} }
} }

View file

@ -4,7 +4,7 @@ use {
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture, SyncFile}, gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture, SyncFile},
gfx_apis::vulkan::{ gfx_apis::vulkan::{
allocator::VulkanAllocation, device::VulkanDevice, format::VulkanMaxExtents, allocator::VulkanAllocation, device::VulkanDevice, format::VulkanMaxExtents,
renderer::VulkanRenderer, util::OnDrop, VulkanError, renderer::VulkanRenderer, shm_image::VulkanShmImage, util::OnDrop, VulkanError,
}, },
theme::Color, theme::Color,
utils::clonecell::CloneCell, utils::clonecell::CloneCell,
@ -12,19 +12,18 @@ use {
}, },
ash::vk::{ ash::vk::{
BindImageMemoryInfo, BindImagePlaneMemoryInfo, ComponentMapping, ComponentSwizzle, BindImageMemoryInfo, BindImagePlaneMemoryInfo, ComponentMapping, ComponentSwizzle,
DeviceMemory, DeviceSize, Extent3D, ExternalMemoryHandleTypeFlags, DeviceMemory, Extent3D, ExternalMemoryHandleTypeFlags, ExternalMemoryImageCreateInfo,
ExternalMemoryImageCreateInfo, FormatFeatureFlags, Image, ImageAspectFlags, FormatFeatureFlags, Image, ImageAspectFlags, ImageCreateFlags, ImageCreateInfo,
ImageCreateFlags, ImageCreateInfo, ImageDrmFormatModifierExplicitCreateInfoEXT, ImageDrmFormatModifierExplicitCreateInfoEXT, ImageLayout, ImageMemoryRequirementsInfo2,
ImageLayout, ImageMemoryRequirementsInfo2, ImagePlaneMemoryRequirementsInfo, ImagePlaneMemoryRequirementsInfo, ImageSubresourceRange, ImageTiling, ImageType,
ImageSubresourceRange, ImageTiling, ImageType, ImageUsageFlags, ImageView, ImageUsageFlags, ImageView, ImageViewCreateInfo, ImageViewType, ImportMemoryFdInfoKHR,
ImageViewCreateInfo, ImageViewType, ImportMemoryFdInfoKHR, MemoryAllocateInfo, MemoryAllocateInfo, MemoryDedicatedAllocateInfo, MemoryPropertyFlags, MemoryRequirements2,
MemoryDedicatedAllocateInfo, MemoryPropertyFlags, MemoryRequirements2, SampleCountFlags, SampleCountFlags, SharingMode, SubresourceLayout,
SharingMode, SubresourceLayout,
}, },
gpu_alloc::UsageFlags, gpu_alloc::UsageFlags,
std::{ std::{
any::Any, any::Any,
cell::{Cell, RefCell}, cell::Cell,
fmt::{Debug, Formatter}, fmt::{Debug, Formatter},
mem, mem,
rc::Rc, rc::Rc,
@ -67,13 +66,6 @@ pub struct VulkanDmaBufImage {
pub(super) mems: PlaneVec<DeviceMemory>, pub(super) mems: PlaneVec<DeviceMemory>,
} }
pub struct VulkanShmImage {
pub(super) to_flush: RefCell<Option<Vec<u8>>>,
pub(super) size: DeviceSize,
pub(super) stride: u32,
pub(super) _allocation: VulkanAllocation,
}
pub struct VulkanFramebufferBridge { pub struct VulkanFramebufferBridge {
pub(super) dmabuf_image: Image, pub(super) dmabuf_image: Image,
pub(super) _allocation: VulkanAllocation, pub(super) _allocation: VulkanAllocation,
@ -113,124 +105,7 @@ impl Drop for VulkanImage {
} }
} }
impl VulkanShmImage {
pub fn upload(&self, buffer: &[Cell<u8>]) -> Result<(), VulkanError> {
let buffer = unsafe {
std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len()).to_vec()
};
*self.to_flush.borrow_mut() = Some(buffer);
Ok(())
}
}
impl VulkanRenderer { impl VulkanRenderer {
pub fn create_shm_texture(
self: &Rc<Self>,
format: &'static Format,
width: i32,
height: i32,
stride: i32,
data: &[Cell<u8>],
for_download: bool,
) -> Result<Rc<VulkanImage>, VulkanError> {
let Some(shm_info) = &format.shm_info else {
return Err(VulkanError::UnsupportedShmFormat(format.name));
};
if width <= 0 || height <= 0 || stride <= 0 {
return Err(VulkanError::NonPositiveImageSize);
}
let width = width as u32;
let height = height as u32;
let stride = stride as u32;
if stride % shm_info.bpp != 0 || stride / shm_info.bpp < width {
return Err(VulkanError::InvalidStride);
}
let vk_format = self
.device
.formats
.get(&format.drm)
.ok_or(VulkanError::FormatNotSupported)?;
let shm = vk_format.shm.as_ref().ok_or(VulkanError::ShmNotSupported)?;
if width > shm.max_extents.width || height > shm.max_extents.height {
return Err(VulkanError::ImageTooLarge);
}
let size = stride.checked_mul(height).ok_or(VulkanError::ShmOverflow)?;
let usage = ImageUsageFlags::TRANSFER_SRC
| match for_download {
true => ImageUsageFlags::COLOR_ATTACHMENT,
false => ImageUsageFlags::SAMPLED | ImageUsageFlags::TRANSFER_DST,
};
let create_info = ImageCreateInfo::builder()
.image_type(ImageType::TYPE_2D)
.format(format.vk_format)
.mip_levels(1)
.array_layers(1)
.tiling(ImageTiling::OPTIMAL)
.samples(SampleCountFlags::TYPE_1)
.sharing_mode(SharingMode::EXCLUSIVE)
.initial_layout(ImageLayout::UNDEFINED)
.extent(Extent3D {
width,
height,
depth: 1,
})
.usage(usage)
.build();
let image = unsafe { self.device.device.create_image(&create_info, None) };
let image = image.map_err(VulkanError::CreateImage)?;
let destroy_image = OnDrop(|| unsafe { self.device.device.destroy_image(image, None) });
let memory_requirements =
unsafe { self.device.device.get_image_memory_requirements(image) };
let allocation =
self.allocator
.alloc(&memory_requirements, UsageFlags::FAST_DEVICE_ACCESS, false)?;
let res = unsafe {
self.device
.device
.bind_image_memory(image, allocation.memory, allocation.offset)
};
res.map_err(VulkanError::BindImageMemory)?;
let image_view_create_info = ImageViewCreateInfo::builder()
.image(image)
.format(format.vk_format)
.view_type(ImageViewType::TYPE_2D)
.subresource_range(ImageSubresourceRange {
aspect_mask: ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: 1,
base_array_layer: 0,
layer_count: 1,
});
let view = unsafe {
self.device
.device
.create_image_view(&image_view_create_info, None)
};
let view = view.map_err(VulkanError::CreateImageView)?;
let shm = VulkanShmImage {
to_flush: Default::default(),
size: size as u64,
stride,
_allocation: allocation,
};
shm.upload(data)?;
destroy_image.forget();
Ok(Rc::new(VulkanImage {
renderer: self.clone(),
format,
width,
height,
stride,
texture_view: view,
render_view: None,
image,
is_undefined: Cell::new(true),
ty: VulkanImageMemory::Internal(shm),
render_ops: Default::default(),
bridge: None,
}))
}
pub fn import_dmabuf( pub fn import_dmabuf(
self: &Rc<Self>, self: &Rc<Self>,
dmabuf: &DmaBuf, dmabuf: &DmaBuf,

View file

@ -19,7 +19,6 @@ use {
TexVertPushConstants, VulkanShader, FILL_FRAG, FILL_VERT, TEX_FRAG, TexVertPushConstants, VulkanShader, FILL_FRAG, FILL_VERT, TEX_FRAG,
TEX_FRAG_MULT_ALPHA, TEX_FRAG_MULT_OPAQUE, TEX_VERT, TEX_FRAG_MULT_ALPHA, TEX_FRAG_MULT_OPAQUE, TEX_VERT,
}, },
staging::VulkanStagingBuffer,
VulkanError, VulkanError,
}, },
io_uring::IoUring, io_uring::IoUring,
@ -30,16 +29,15 @@ use {
ahash::AHashMap, ahash::AHashMap,
ash::{ ash::{
vk::{ vk::{
AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, BufferImageCopy, BufferImageCopy2, AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, BufferImageCopy,
BufferMemoryBarrier2, ClearColorValue, ClearValue, CommandBuffer, BufferMemoryBarrier2, ClearColorValue, ClearValue, CommandBuffer,
CommandBufferBeginInfo, CommandBufferSubmitInfo, CommandBufferUsageFlags, CommandBufferBeginInfo, CommandBufferSubmitInfo, CommandBufferUsageFlags,
CopyBufferToImageInfo2, CopyImageInfo2, DependencyInfo, DependencyInfoKHR, CopyImageInfo2, DependencyInfo, DependencyInfoKHR, DescriptorImageInfo, DescriptorType,
DescriptorImageInfo, DescriptorType, Extent2D, Extent3D, Fence, ImageAspectFlags, Extent2D, Extent3D, Fence, ImageAspectFlags, ImageCopy2, ImageLayout,
ImageCopy2, ImageLayout, ImageMemoryBarrier2, ImageMemoryBarrier2Builder, ImageMemoryBarrier2, ImageMemoryBarrier2Builder, ImageSubresourceLayers,
ImageSubresourceLayers, ImageSubresourceRange, PipelineBindPoint, PipelineStageFlags2, ImageSubresourceRange, PipelineBindPoint, PipelineStageFlags2, Rect2D,
Rect2D, RenderingAttachmentInfo, RenderingInfo, SemaphoreSubmitInfo, RenderingAttachmentInfo, RenderingInfo, SemaphoreSubmitInfo, SemaphoreSubmitInfoKHR,
SemaphoreSubmitInfoKHR, ShaderStageFlags, SubmitInfo2, Viewport, WriteDescriptorSet, ShaderStageFlags, SubmitInfo2, Viewport, WriteDescriptorSet, QUEUE_FAMILY_FOREIGN_EXT,
QUEUE_FAMILY_FOREIGN_EXT,
}, },
Device, Device,
}, },
@ -66,6 +64,7 @@ pub struct VulkanRenderer {
pub(super) total_buffers: NumCell<usize>, pub(super) total_buffers: NumCell<usize>,
pub(super) memory: RefCell<Memory>, pub(super) memory: RefCell<Memory>,
pub(super) pending_frames: CopyHashMap<u64, Rc<PendingFrame>>, pub(super) pending_frames: CopyHashMap<u64, Rc<PendingFrame>>,
pub(super) pending_uploads: CopyHashMap<u64, SpawnedFuture<()>>,
pub(super) allocator: Rc<VulkanAllocator>, pub(super) allocator: Rc<VulkanAllocator>,
pub(super) last_point: NumCell<u64>, pub(super) last_point: NumCell<u64>,
pub(super) buffer_resv_user: BufferResvUser, pub(super) buffer_resv_user: BufferResvUser,
@ -93,11 +92,8 @@ pub(super) enum TexSourceType {
#[derive(Default)] #[derive(Default)]
pub(super) struct Memory { pub(super) struct Memory {
sample: Vec<Rc<VulkanImage>>, sample: Vec<Rc<VulkanImage>>,
flush: Vec<Rc<VulkanImage>>,
flush_staging: Vec<(Rc<VulkanImage>, VulkanStagingBuffer)>,
textures: Vec<UsedTexture>, textures: Vec<UsedTexture>,
image_barriers: Vec<ImageMemoryBarrier2>, image_barriers: Vec<ImageMemoryBarrier2>,
shm_barriers: Vec<BufferMemoryBarrier2>,
wait_semaphores: Vec<Rc<VulkanSemaphore>>, wait_semaphores: Vec<Rc<VulkanSemaphore>>,
wait_semaphore_infos: Vec<SemaphoreSubmitInfo>, wait_semaphore_infos: Vec<SemaphoreSubmitInfo>,
release_fence: Option<Rc<VulkanFence>>, release_fence: Option<Rc<VulkanFence>>,
@ -109,7 +105,6 @@ pub(super) struct PendingFrame {
renderer: Rc<VulkanRenderer>, renderer: Rc<VulkanRenderer>,
cmd: Cell<Option<Rc<VulkanCommandBuffer>>>, cmd: Cell<Option<Rc<VulkanCommandBuffer>>>,
_textures: Vec<UsedTexture>, _textures: Vec<UsedTexture>,
_staging: Vec<(Rc<VulkanImage>, VulkanStagingBuffer)>,
wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>, wait_semaphores: Cell<Vec<Rc<VulkanSemaphore>>>,
waiter: Cell<Option<SpawnedFuture<()>>>, waiter: Cell<Option<SpawnedFuture<()>>>,
_release_fence: Option<Rc<VulkanFence>>, _release_fence: Option<Rc<VulkanFence>>,
@ -197,6 +192,7 @@ impl VulkanDevice {
total_buffers: Default::default(), total_buffers: Default::default(),
memory: Default::default(), memory: Default::default(),
pending_frames: Default::default(), pending_frames: Default::default(),
pending_uploads: Default::default(),
allocator, allocator,
last_point: Default::default(), last_point: Default::default(),
buffer_resv_user: Default::default(), buffer_resv_user: Default::default(),
@ -205,20 +201,18 @@ impl VulkanDevice {
} }
impl VulkanRenderer { impl VulkanRenderer {
pub(super) fn allocate_point(&self) -> u64 {
self.last_point.fetch_add(1) + 1
}
fn collect_memory(&self, opts: &[GfxApiOpt]) { fn collect_memory(&self, opts: &[GfxApiOpt]) {
let mut memory = self.memory.borrow_mut(); let mut memory = self.memory.borrow_mut();
memory.sample.clear(); memory.sample.clear();
memory.flush.clear();
for cmd in opts { for cmd in opts {
if let GfxApiOpt::CopyTexture(c) = cmd { if let GfxApiOpt::CopyTexture(c) = cmd {
let tex = c.tex.clone().into_vk(&self.device.device); let tex = c.tex.clone().into_vk(&self.device.device);
match &tex.ty { if let VulkanImageMemory::DmaBuf(_) = &tex.ty {
VulkanImageMemory::DmaBuf(_) => memory.sample.push(tex.clone()), 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 { memory.textures.push(UsedTexture {
tex, tex,
@ -241,32 +235,10 @@ impl VulkanRenderer {
} }
} }
fn write_shm_staging_buffers(self: &Rc<Self>) -> Result<(), VulkanError> {
let mut memory = self.memory.borrow_mut();
let memory = &mut *memory;
memory.flush_staging.clear();
for img in &memory.flush {
let shm = match &img.ty {
VulkanImageMemory::DmaBuf(_) => unreachable!(),
VulkanImageMemory::Internal(s) => s,
};
let staging = self.create_staging_buffer(shm.size, true, false, true)?;
let to_flush = shm.to_flush.borrow_mut();
let to_flush = to_flush.as_ref().unwrap();
staging.upload(|mem, size| unsafe {
let size = size.min(to_flush.len());
ptr::copy_nonoverlapping(to_flush.as_ptr(), mem, size);
})?;
memory.flush_staging.push((img.clone(), staging));
}
Ok(())
}
fn initial_barriers(&self, buf: CommandBuffer, fb: &VulkanImage) { fn initial_barriers(&self, buf: CommandBuffer, fb: &VulkanImage) {
let mut memory = self.memory.borrow_mut(); let mut memory = self.memory.borrow_mut();
let memory = &mut *memory; let memory = &mut *memory;
memory.image_barriers.clear(); memory.image_barriers.clear();
memory.shm_barriers.clear();
let mut fb_image_memory_barrier = image_barrier() let mut fb_image_memory_barrier = image_barrier()
.image(fb.image) .image(fb.image)
.new_layout(ImageLayout::COLOR_ATTACHMENT_OPTIMAL) .new_layout(ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
@ -307,89 +279,6 @@ impl VulkanRenderer {
.build(); .build();
memory.image_barriers.push(image_memory_barrier); 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); let dep_info = DependencyInfoKHR::builder().image_memory_barriers(&memory.image_barriers);
unsafe { unsafe {
self.device.device.cmd_pipeline_barrier2(buf, &dep_info); self.device.device.cmd_pipeline_barrier2(buf, &dep_info);
@ -625,7 +514,6 @@ impl VulkanRenderer {
let mut memory = self.memory.borrow_mut(); let mut memory = self.memory.borrow_mut();
let memory = &mut *memory; let memory = &mut *memory;
memory.image_barriers.clear(); memory.image_barriers.clear();
memory.shm_barriers.clear();
let mut fb_image_memory_barrier = image_barrier() let mut fb_image_memory_barrier = image_barrier()
.src_queue_family_index(self.device.graphics_queue_idx) .src_queue_family_index(self.device.graphics_queue_idx)
.dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT) .dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
@ -659,9 +547,7 @@ impl VulkanRenderer {
.build(); .build();
memory.image_barriers.push(image_memory_barrier); memory.image_barriers.push(image_memory_barrier);
} }
let dep_info = DependencyInfoKHR::builder() let dep_info = DependencyInfoKHR::builder().image_memory_barriers(&memory.image_barriers);
.image_memory_barriers(&memory.image_barriers)
.buffer_memory_barriers(&memory.shm_barriers);
unsafe { unsafe {
self.device.device.cmd_pipeline_barrier2(buf, &dep_info); self.device.device.cmd_pipeline_barrier2(buf, &dep_info);
} }
@ -804,26 +690,16 @@ impl VulkanRenderer {
fn store_layouts(&self, fb: &VulkanImage) { fn store_layouts(&self, fb: &VulkanImage) {
fb.is_undefined.set(false); fb.is_undefined.set(false);
let memory = self.memory.borrow_mut();
for img in &memory.flush {
img.is_undefined.set(false);
let shm = match &img.ty {
VulkanImageMemory::DmaBuf(_) => unreachable!(),
VulkanImageMemory::Internal(s) => s,
};
shm.to_flush.take();
}
} }
fn create_pending_frame(self: &Rc<Self>, buf: Rc<VulkanCommandBuffer>) { fn create_pending_frame(self: &Rc<Self>, buf: Rc<VulkanCommandBuffer>) {
let point = self.last_point.fetch_add(1) + 1; let point = self.allocate_point();
let mut memory = self.memory.borrow_mut(); let mut memory = self.memory.borrow_mut();
let frame = Rc::new(PendingFrame { let frame = Rc::new(PendingFrame {
point, point,
renderer: self.clone(), renderer: self.clone(),
cmd: Cell::new(Some(buf)), cmd: Cell::new(Some(buf)),
_textures: mem::take(&mut memory.textures), _textures: mem::take(&mut memory.textures),
_staging: mem::take(&mut memory.flush_staging),
wait_semaphores: Cell::new(mem::take(&mut memory.wait_semaphores)), wait_semaphores: Cell::new(mem::take(&mut memory.wait_semaphores)),
waiter: Cell::new(None), waiter: Cell::new(None),
_release_fence: memory.release_fence.take(), _release_fence: memory.release_fence.take(),
@ -1034,9 +910,7 @@ impl VulkanRenderer {
let res = self.try_execute(fb, opts, clear); let res = self.try_execute(fb, opts, clear);
let sync_file = { let sync_file = {
let mut memory = self.memory.borrow_mut(); let mut memory = self.memory.borrow_mut();
memory.flush.clear();
memory.textures.clear(); memory.textures.clear();
memory.flush_staging.clear();
memory.sample.clear(); memory.sample.clear();
memory.wait_semaphores.clear(); memory.wait_semaphores.clear();
memory.release_fence.take(); memory.release_fence.take();
@ -1045,7 +919,7 @@ impl VulkanRenderer {
res.map(|_| sync_file) res.map(|_| sync_file)
} }
fn allocate_command_buffer(&self) -> Result<Rc<VulkanCommandBuffer>, VulkanError> { pub(super) fn allocate_command_buffer(&self) -> Result<Rc<VulkanCommandBuffer>, VulkanError> {
let buf = match self.command_buffers.pop() { let buf = match self.command_buffers.pop() {
Some(b) => b, Some(b) => b,
_ => { _ => {
@ -1073,10 +947,7 @@ impl VulkanRenderer {
let buf = self.allocate_command_buffer()?; let buf = self.allocate_command_buffer()?;
self.collect_memory(opts); self.collect_memory(opts);
self.begin_command_buffer(buf.buffer)?; self.begin_command_buffer(buf.buffer)?;
self.write_shm_staging_buffers()?;
self.initial_barriers(buf.buffer, fb); 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.begin_rendering(buf.buffer, fb, clear);
self.set_viewport(buf.buffer, fb); self.set_viewport(buf.buffer, fb);
self.record_draws(buf.buffer, opts)?; self.record_draws(buf.buffer, opts)?;
@ -1092,7 +963,7 @@ impl VulkanRenderer {
Ok(()) Ok(())
} }
fn block(&self) { pub(super) fn block(&self) {
log::warn!("Blocking."); log::warn!("Blocking.");
unsafe { unsafe {
if let Err(e) = self.device.device.device_wait_idle() { if let Err(e) = self.device.device.device_wait_idle() {
@ -1103,7 +974,8 @@ impl VulkanRenderer {
pub fn on_drop(&self) { pub fn on_drop(&self) {
let mut pending_frames = self.pending_frames.lock(); 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."); log::warn!("Context dropped with pending frames.");
self.block(); self.block();
} }
@ -1111,6 +983,7 @@ impl VulkanRenderer {
f.waiter.take(); f.waiter.take();
}); });
pending_frames.clear(); 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( ImageMemoryBarrier2::builder().subresource_range(
ImageSubresourceRange::builder() ImageSubresourceRange::builder()
.aspect_mask(ImageAspectFlags::COLOR) .aspect_mask(ImageAspectFlags::COLOR)

View file

@ -0,0 +1,374 @@
use {
crate::{
format::{Format, FormatShmInfo},
gfx_api::SyncFile,
gfx_apis::vulkan::{
allocator::VulkanAllocation,
command::VulkanCommandBuffer,
fence::VulkanFence,
image::{VulkanImage, VulkanImageMemory},
renderer::{image_barrier, VulkanRenderer},
staging::VulkanStagingBuffer,
util::OnDrop,
VulkanError,
},
rect::Rect,
utils::errorfmt::ErrorFmt,
},
ash::vk::{
AccessFlags2, BufferImageCopy2, BufferMemoryBarrier2, CommandBufferBeginInfo,
CommandBufferSubmitInfo, CommandBufferUsageFlags, CopyBufferToImageInfo2,
DependencyInfoKHR, DeviceSize, Extent3D, ImageAspectFlags, ImageCreateInfo, ImageLayout,
ImageSubresourceLayers, ImageSubresourceRange, ImageTiling, ImageType, ImageUsageFlags,
ImageViewCreateInfo, ImageViewType, Offset3D, PipelineStageFlags2, SampleCountFlags,
SharingMode, SubmitInfo2,
},
gpu_alloc::UsageFlags,
isnt::std_1::primitive::IsntSliceExt,
std::{cell::Cell, ptr, rc::Rc, slice},
};
pub struct VulkanShmImage {
pub(super) _size: DeviceSize,
pub(super) stride: u32,
pub(super) _allocation: VulkanAllocation,
pub(super) shm_info: &'static FormatShmInfo,
}
impl VulkanShmImage {
pub fn upload(
&self,
img: &Rc<VulkanImage>,
buffer: &[Cell<u8>],
damage: Option<&[Rect]>,
) -> Result<(), VulkanError> {
if let Some(damage) = damage {
if damage.is_empty() {
return Ok(());
}
}
let copy = |full: bool, off, x, y, width, height| {
let mut builder = BufferImageCopy2::builder()
.buffer_offset(off)
.image_offset(Offset3D { x, y, z: 0 })
.image_extent(Extent3D {
width,
height,
depth: 1,
})
.image_subresource(ImageSubresourceLayers {
aspect_mask: ImageAspectFlags::COLOR,
mip_level: 0,
base_array_layer: 0,
layer_count: 1,
});
if full {
builder = builder
.buffer_image_height(img.height)
.buffer_row_length(img.stride / self.shm_info.bpp);
}
builder.build()
};
let mut total_size;
let cpy_one;
let mut cpy_many;
let cpy;
if let Some(damage) = damage {
total_size = 0;
cpy_many = Vec::with_capacity(damage.len());
for damage in damage {
let Some(damage) = Rect::new(
damage.x1().max(0),
damage.y1().max(0),
damage.x2().min(img.width as i32),
damage.y2().min(img.height as i32),
) else {
continue;
};
if damage.is_empty() {
continue;
}
cpy_many.push(copy(
false,
total_size as DeviceSize,
damage.x1(),
damage.y1(),
damage.width() as u32,
damage.height() as u32,
));
total_size += damage.width() as u32 * damage.height() as u32 * self.shm_info.bpp;
}
cpy = &cpy_many[..];
} else {
cpy_one = copy(true, 0, 0, 0, img.width, img.height);
cpy = slice::from_ref(&cpy_one);
total_size = img.height * img.stride;
}
let staging = img
.renderer
.create_staging_buffer(total_size as u64, true, false, true)?;
staging.upload(|mem, _| unsafe {
let buf = buffer.as_ptr() as *const u8;
if damage.is_some() {
let mut off = 0;
for cpy in cpy {
let x = cpy.image_offset.x as usize;
let y = cpy.image_offset.y as usize;
let width = cpy.image_extent.width as usize;
let height = cpy.image_extent.height as usize;
let stride = self.stride as usize;
let bpp = self.shm_info.bpp as usize;
for dy in 0..height {
let lo = (y + dy) * stride + x * bpp;
let len = width * bpp;
ptr::copy_nonoverlapping(buf.add(lo), mem.add(off), len);
off += len;
}
}
} else {
ptr::copy_nonoverlapping(buf, mem, total_size as usize);
}
})?;
let memory_barrier = |sam, ssm, dam, dsm| {
BufferMemoryBarrier2::builder()
.buffer(staging.buffer)
.offset(0)
.size(staging.size)
.src_access_mask(sam)
.src_stage_mask(ssm)
.dst_access_mask(dam)
.dst_stage_mask(dsm)
.build()
};
let initial_image_barrier = image_barrier()
.image(img.image)
.src_access_mask(AccessFlags2::SHADER_SAMPLED_READ)
.src_stage_mask(PipelineStageFlags2::FRAGMENT_SHADER)
.old_layout(if img.is_undefined.get() {
ImageLayout::UNDEFINED
} else {
ImageLayout::SHADER_READ_ONLY_OPTIMAL
})
.new_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
.dst_access_mask(AccessFlags2::TRANSFER_WRITE)
.dst_stage_mask(PipelineStageFlags2::TRANSFER)
.build();
let initial_buffer_barrier = memory_barrier(
AccessFlags2::HOST_WRITE,
PipelineStageFlags2::HOST,
AccessFlags2::TRANSFER_READ,
PipelineStageFlags2::TRANSFER,
);
let initial_dep_info = DependencyInfoKHR::builder()
.buffer_memory_barriers(slice::from_ref(&initial_buffer_barrier))
.image_memory_barriers(slice::from_ref(&initial_image_barrier));
let final_image_barrier = image_barrier()
.image(img.image)
.src_access_mask(AccessFlags2::TRANSFER_WRITE)
.src_stage_mask(PipelineStageFlags2::TRANSFER)
.old_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
.new_layout(ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.dst_access_mask(AccessFlags2::SHADER_SAMPLED_READ)
.dst_stage_mask(PipelineStageFlags2::FRAGMENT_SHADER)
.build();
let final_buffer_barrier = memory_barrier(
AccessFlags2::TRANSFER_READ,
PipelineStageFlags2::TRANSFER,
AccessFlags2::HOST_WRITE,
PipelineStageFlags2::HOST,
);
let final_dep_info = DependencyInfoKHR::builder()
.buffer_memory_barriers(slice::from_ref(&final_buffer_barrier))
.image_memory_barriers(slice::from_ref(&final_image_barrier));
let cpy_info = CopyBufferToImageInfo2::builder()
.src_buffer(staging.buffer)
.dst_image(img.image)
.dst_image_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
.regions(cpy);
let cmd = img.renderer.allocate_command_buffer()?;
let dev = &img.renderer.device.device;
let command_buffer_info = CommandBufferSubmitInfo::builder().command_buffer(cmd.buffer);
let submit_info =
SubmitInfo2::builder().command_buffer_infos(slice::from_ref(&command_buffer_info));
let begin_info =
CommandBufferBeginInfo::builder().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT);
let release_fence = img.renderer.device.create_fence()?;
unsafe {
dev.begin_command_buffer(cmd.buffer, &begin_info)
.map_err(VulkanError::BeginCommandBuffer)?;
dev.cmd_pipeline_barrier2(cmd.buffer, &initial_dep_info);
dev.cmd_copy_buffer_to_image2(cmd.buffer, &cpy_info);
dev.cmd_pipeline_barrier2(cmd.buffer, &final_dep_info);
dev.end_command_buffer(cmd.buffer)
.map_err(VulkanError::EndCommandBuffer)?;
dev.queue_submit2(
img.renderer.device.graphics_queue,
slice::from_ref(&submit_info),
release_fence.fence,
)
.map_err(VulkanError::Submit)?;
}
img.is_undefined.set(false);
let release_sync_file = match release_fence.export_sync_file() {
Ok(s) => s,
Err(e) => {
log::error!("Could not export sync file from fence: {}", ErrorFmt(e));
img.renderer.block();
return Ok(());
}
};
let point = img.renderer.allocate_point();
let future = img.renderer.device.instance.eng.spawn(await_upload(
point,
img.clone(),
cmd,
release_sync_file,
release_fence,
staging,
));
img.renderer.pending_uploads.set(point, future);
Ok(())
}
}
async fn await_upload(
id: u64,
img: Rc<VulkanImage>,
buf: Rc<VulkanCommandBuffer>,
sync_file: SyncFile,
_fence: Rc<VulkanFence>,
_staging: VulkanStagingBuffer,
) {
let res = img
.renderer
.device
.instance
.ring
.readable(&sync_file.0)
.await;
if let Err(e) = res {
log::error!(
"Could not wait for sync file to become readable: {}",
ErrorFmt(e)
);
img.renderer.block();
}
img.renderer.command_buffers.push(buf);
img.renderer.pending_uploads.remove(&id);
}
impl VulkanRenderer {
pub fn create_shm_texture(
self: &Rc<Self>,
format: &'static Format,
width: i32,
height: i32,
stride: i32,
data: &[Cell<u8>],
for_download: bool,
) -> Result<Rc<VulkanImage>, VulkanError> {
let Some(shm_info) = &format.shm_info else {
return Err(VulkanError::UnsupportedShmFormat(format.name));
};
if width <= 0 || height <= 0 || stride <= 0 {
return Err(VulkanError::NonPositiveImageSize);
}
let width = width as u32;
let height = height as u32;
let stride = stride as u32;
if stride % shm_info.bpp != 0 || stride / shm_info.bpp < width {
return Err(VulkanError::InvalidStride);
}
let vk_format = self
.device
.formats
.get(&format.drm)
.ok_or(VulkanError::FormatNotSupported)?;
let shm = vk_format.shm.as_ref().ok_or(VulkanError::ShmNotSupported)?;
if width > shm.max_extents.width || height > shm.max_extents.height {
return Err(VulkanError::ImageTooLarge);
}
let size = stride.checked_mul(height).ok_or(VulkanError::ShmOverflow)?;
let usage = ImageUsageFlags::TRANSFER_SRC
| match for_download {
true => ImageUsageFlags::COLOR_ATTACHMENT,
false => ImageUsageFlags::SAMPLED | ImageUsageFlags::TRANSFER_DST,
};
let create_info = ImageCreateInfo::builder()
.image_type(ImageType::TYPE_2D)
.format(format.vk_format)
.mip_levels(1)
.array_layers(1)
.tiling(ImageTiling::OPTIMAL)
.samples(SampleCountFlags::TYPE_1)
.sharing_mode(SharingMode::EXCLUSIVE)
.initial_layout(ImageLayout::UNDEFINED)
.extent(Extent3D {
width,
height,
depth: 1,
})
.usage(usage)
.build();
let image = unsafe { self.device.device.create_image(&create_info, None) };
let image = image.map_err(VulkanError::CreateImage)?;
let destroy_image = OnDrop(|| unsafe { self.device.device.destroy_image(image, None) });
let memory_requirements =
unsafe { self.device.device.get_image_memory_requirements(image) };
let allocation =
self.allocator
.alloc(&memory_requirements, UsageFlags::FAST_DEVICE_ACCESS, false)?;
let res = unsafe {
self.device
.device
.bind_image_memory(image, allocation.memory, allocation.offset)
};
res.map_err(VulkanError::BindImageMemory)?;
let image_view_create_info = ImageViewCreateInfo::builder()
.image(image)
.format(format.vk_format)
.view_type(ImageViewType::TYPE_2D)
.subresource_range(ImageSubresourceRange {
aspect_mask: ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: 1,
base_array_layer: 0,
layer_count: 1,
});
let view = unsafe {
self.device
.device
.create_image_view(&image_view_create_info, None)
};
let view = view.map_err(VulkanError::CreateImageView)?;
let shm = VulkanShmImage {
_size: size as u64,
stride,
_allocation: allocation,
shm_info,
};
destroy_image.forget();
let img = Rc::new(VulkanImage {
renderer: self.clone(),
format,
width,
height,
stride,
texture_view: view,
render_view: None,
image,
is_undefined: Cell::new(true),
ty: VulkanImageMemory::Internal(shm),
render_ops: Default::default(),
bridge: None,
});
let shm = match &img.ty {
VulkanImageMemory::DmaBuf(_) => unreachable!(),
VulkanImageMemory::Internal(s) => s,
};
if data.is_not_empty() {
shm.upload(&img, data, None)?;
}
Ok(img)
}
}

View file

@ -4,25 +4,32 @@ use {
clientmem::{ClientMem, ClientMemError, ClientMemOffset}, clientmem::{ClientMem, ClientMemError, ClientMemOffset},
format::{Format, ARGB8888}, format::{Format, ARGB8888},
gfx_api::{GfxError, GfxFramebuffer, GfxImage, GfxTexture}, gfx_api::{GfxError, GfxFramebuffer, GfxImage, GfxTexture},
ifs::wl_surface::WlSurface,
leaks::Tracker, leaks::Tracker,
object::{Object, Version}, object::{Object, Version},
rect::Rect, rect::Rect,
theme::Color, theme::Color,
utils::{clonecell::CloneCell, errorfmt::ErrorFmt}, utils::errorfmt::ErrorFmt,
video::dmabuf::DmaBuf, video::dmabuf::DmaBuf,
wire::{wl_buffer::*, WlBufferId}, wire::{wl_buffer::*, WlBufferId},
}, },
std::{ std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
ops::Deref,
rc::Rc, rc::Rc,
}, },
thiserror::Error, thiserror::Error,
}; };
pub enum WlBufferStorage { pub enum WlBufferStorage {
Shm { mem: ClientMemOffset, stride: i32 }, Shm {
Dmabuf(Rc<dyn GfxImage>), mem: ClientMemOffset,
stride: i32,
},
Dmabuf {
img: Rc<dyn GfxImage>,
tex: Option<Rc<dyn GfxTexture>>,
fb: Option<Rc<dyn GfxFramebuffer>>,
},
} }
pub struct WlBuffer { pub struct WlBuffer {
@ -35,8 +42,6 @@ pub struct WlBuffer {
render_ctx_version: Cell<u32>, render_ctx_version: Cell<u32>,
pub storage: RefCell<Option<WlBufferStorage>>, pub storage: RefCell<Option<WlBufferStorage>>,
pub color: Option<Color>, pub color: Option<Color>,
pub texture: CloneCell<Option<Rc<dyn GfxTexture>>>,
pub famebuffer: CloneCell<Option<Rc<dyn GfxFramebuffer>>>,
width: i32, width: i32,
height: i32, height: i32,
pub tracker: Tracker<Self>, pub tracker: Tracker<Self>,
@ -65,11 +70,13 @@ impl WlBuffer {
format, format,
width, width,
height, height,
texture: CloneCell::new(None),
famebuffer: Default::default(),
dmabuf: Some(dmabuf), dmabuf: Some(dmabuf),
render_ctx_version: Cell::new(client.state.render_ctx_version.get()), 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(), tracker: Default::default(),
color: None, color: None,
} }
@ -110,9 +117,7 @@ impl WlBuffer {
storage: RefCell::new(Some(WlBufferStorage::Shm { mem, stride })), storage: RefCell::new(Some(WlBufferStorage::Shm { mem, stride })),
width, width,
height, height,
texture: CloneCell::new(None),
tracker: Default::default(), tracker: Default::default(),
famebuffer: Default::default(),
color: None, color: None,
}) })
} }
@ -136,78 +141,110 @@ impl WlBuffer {
storage: RefCell::new(None), storage: RefCell::new(None),
width: 1, width: 1,
height: 1, height: 1,
texture: CloneCell::new(None),
tracker: Default::default(), tracker: Default::default(),
famebuffer: Default::default(),
color: Some(Color::from_u32_rgba_premultiplied(r, g, b, a)), 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(); let ctx_version = self.client.state.render_ctx_version.get();
if self.render_ctx_version.replace(ctx_version) == ctx_version { if self.render_ctx_version.replace(ctx_version) == ctx_version {
return; return;
} }
let had_texture = self.texture.set(None).is_some(); let had_texture = self.reset_gfx_objects(surface);
self.famebuffer.set(None);
self.reset_storage_after_gfx_context_change();
if had_texture { 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) {
let mut storage = self.storage.borrow_mut();
if let Some(storage) = &mut *storage {
if let WlBufferStorage::Shm { .. } = storage {
return;
} }
} }
*storage = None; }
let ctx = match self.client.state.render_ctx.get() {
Some(ctx) => ctx, fn reset_gfx_objects(&self, surface: Option<&WlSurface>) -> bool {
_ => return, let mut storage = self.storage.borrow_mut();
let Some(s) = &mut *storage else {
return false;
}; };
if let Some(dmabuf) = &self.dmabuf { let had_texture = match s {
let image = match ctx.dmabuf_img(dmabuf) { WlBufferStorage::Shm { .. } => {
Ok(image) => image, return match surface {
Err(e) => { Some(s) => s.shm_texture.take().is_some(),
log::error!( None => false,
"Cannot re-import wl_buffer after graphics context change: {}", };
ErrorFmt(e) }
); WlBufferStorage::Dmabuf { tex, .. } => tex.is_some(),
return; };
} *storage = None;
}; let Some(ctx) = self.client.state.render_ctx.get() else {
*storage = Some(WlBufferStorage::Dmabuf(image)); return false;
};
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<Rc<dyn GfxTexture>> {
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) { pub fn update_texture_or_log(&self, surface: &WlSurface, damage: Option<&[Rect]>) {
if let Err(e) = self.update_texture() { if let Err(e) = self.update_texture(surface, damage) {
log::warn!("Could not update texture: {}", ErrorFmt(e)); log::warn!("Could not update texture: {}", ErrorFmt(e));
} }
} }
fn update_texture(&self) -> Result<(), WlBufferError> { fn update_texture(
let storage = self.storage.borrow_mut(); &self,
let storage = match storage.deref() { 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, Some(s) => s,
_ => return Ok(()), _ => return Ok(()),
}; };
match storage { match storage {
WlBufferStorage::Shm { mem, stride } => { WlBufferStorage::Shm { mem, stride } => {
let old = self.texture.take();
if let Some(ctx) = self.client.state.render_ctx.get() { if let Some(ctx) = self.client.state.render_ctx.get() {
let tex = mem.access(|mem| { 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) => { WlBufferStorage::Dmabuf { img, tex, .. } => {
if self.texture.is_none() { if tex.is_none() {
self.texture.set(Some(img.clone().to_texture()?)); *tex = Some(img.clone().to_texture()?);
} }
} }
} }
@ -215,8 +252,8 @@ impl WlBuffer {
} }
pub fn update_framebuffer(&self) -> Result<(), WlBufferError> { pub fn update_framebuffer(&self) -> Result<(), WlBufferError> {
let storage = self.storage.borrow_mut(); let storage = &mut *self.storage.borrow_mut();
let storage = match storage.deref() { let storage = match storage {
Some(s) => s, Some(s) => s,
_ => return Ok(()), _ => return Ok(()),
}; };
@ -224,9 +261,9 @@ impl WlBuffer {
WlBufferStorage::Shm { .. } => { WlBufferStorage::Shm { .. } => {
// nothing // nothing
} }
WlBufferStorage::Dmabuf(img) => { WlBufferStorage::Dmabuf { img, fb, .. } => {
if self.famebuffer.is_none() { if fb.is_none() {
self.famebuffer.set(Some(img.clone().to_framebuffer()?)); *fb = Some(img.clone().to_framebuffer()?);
} }
} }
} }

View file

@ -21,7 +21,9 @@ use {
cursor_user::{CursorUser, CursorUserId}, cursor_user::{CursorUser, CursorUserId},
drm_feedback::DrmFeedback, drm_feedback::DrmFeedback,
fixed::Fixed, fixed::Fixed,
gfx_api::{AcquireSync, BufferResv, BufferResvUser, ReleaseSync, SampleRect, SyncFile}, gfx_api::{
AcquireSync, BufferResv, BufferResvUser, GfxTexture, ReleaseSync, SampleRect, SyncFile,
},
ifs::{ ifs::{
wl_buffer::WlBuffer, wl_buffer::WlBuffer,
wl_callback::WlCallback, wl_callback::WlCallback,
@ -261,6 +263,7 @@ pub struct WlSurface {
pub buffer_abs_pos: Cell<Rect>, pub buffer_abs_pos: Cell<Rect>,
pub need_extents_update: Cell<bool>, pub need_extents_update: Cell<bool>,
pub buffer: CloneCell<Option<Rc<SurfaceBuffer>>>, pub buffer: CloneCell<Option<Rc<SurfaceBuffer>>>,
pub shm_texture: CloneCell<Option<Rc<dyn GfxTexture>>>,
pub buf_x: NumCell<i32>, pub buf_x: NumCell<i32>,
pub buf_y: NumCell<i32>, pub buf_y: NumCell<i32>,
pub children: RefCell<Option<Box<ParentData>>>, pub children: RefCell<Option<Box<ParentData>>>,
@ -393,7 +396,8 @@ struct PendingState {
opaque_region: Option<Option<Rc<Region>>>, opaque_region: Option<Option<Rc<Region>>>,
input_region: Option<Option<Rc<Region>>>, input_region: Option<Option<Rc<Region>>>,
frame_request: Vec<Rc<WlCallback>>, frame_request: Vec<Rc<WlCallback>>,
damage: bool, damage_full: bool,
damage: Vec<Rect>,
presentation_feedback: Vec<Rc<WpPresentationFeedback>>, presentation_feedback: Vec<Rc<WpPresentationFeedback>>,
src_rect: Option<Option<[Fixed; 4]>>, src_rect: Option<Option<[Fixed; 4]>>,
dst_size: Option<Option<(i32, i32)>>, dst_size: Option<Option<(i32, i32)>>,
@ -465,7 +469,14 @@ impl PendingState {
self.offset = (dx1 + dx2, dy1 + dy2); self.offset = (dx1 + dx2, dy1 + dy2);
} }
self.frame_request.append(&mut next.frame_request); 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( mem::swap(
&mut self.presentation_feedback, &mut self.presentation_feedback,
&mut next.presentation_feedback, &mut next.presentation_feedback,
@ -542,6 +553,7 @@ impl WlSurface {
buffer_abs_pos: Cell::new(Default::default()), buffer_abs_pos: Cell::new(Default::default()),
need_extents_update: Default::default(), need_extents_update: Default::default(),
buffer: Default::default(), buffer: Default::default(),
shm_texture: Default::default(),
buf_x: Default::default(), buf_x: Default::default(),
buf_y: Default::default(), buf_y: Default::default(),
children: Default::default(), children: Default::default(),
@ -800,6 +812,8 @@ impl WlSurface {
} }
} }
const MAX_DAMAGE: usize = 32;
impl WlSurfaceRequestHandler for WlSurface { impl WlSurfaceRequestHandler for WlSurface {
type Error = WlSurfaceError; type Error = WlSurfaceError;
@ -819,6 +833,7 @@ impl WlSurfaceRequestHandler for WlSurface {
*children = None; *children = None;
} }
self.buffer.set(None); self.buffer.set(None);
self.shm_texture.take();
if let Some(xwayland_serial) = self.xwayland_serial.get() { if let Some(xwayland_serial) = self.xwayland_serial.get() {
self.client self.client
.surfaces_by_xwayland_serial .surfaces_by_xwayland_serial
@ -852,7 +867,9 @@ impl WlSurfaceRequestHandler for WlSurface {
} }
fn damage(&self, _req: Damage, _slf: &Rc<Self>) -> Result<(), Self::Error> { fn damage(&self, _req: Damage, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.pending.borrow_mut().damage = true; let pending = &mut *self.pending.borrow_mut();
pending.damage.clear();
pending.damage_full = true;
Ok(()) Ok(())
} }
@ -918,8 +935,19 @@ impl WlSurfaceRequestHandler for WlSurface {
Ok(()) Ok(())
} }
fn damage_buffer(&self, _req: DamageBuffer, _slf: &Rc<Self>) -> Result<(), Self::Error> { fn damage_buffer(&self, req: DamageBuffer, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.pending.borrow_mut().damage = true; 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(()) Ok(())
} }
@ -980,7 +1008,13 @@ impl WlSurface {
old_raw_size = Some(buffer.buffer.rect); old_raw_size = Some(buffer.buffer.rect);
} }
if let Some(buffer) = buffer_change { 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 { let (sync, release_sync) = match pending.explicit_sync {
false => (AcquireSync::Implicit, ReleaseSync::Implicit), false => (AcquireSync::Implicit, ReleaseSync::Implicit),
true => (AcquireSync::Unnecessary, ReleaseSync::Explicit), true => (AcquireSync::Unnecessary, ReleaseSync::Explicit),
@ -998,6 +1032,7 @@ impl WlSurface {
}; };
self.buffer.set(Some(Rc::new(surface_buffer))); self.buffer.set(Some(Rc::new(surface_buffer)));
} else { } else {
self.shm_texture.take();
self.buf_x.set(0); self.buf_x.set(0);
self.buf_y.set(0); self.buf_y.set(0);
for (_, cursor) in &self.cursors { for (_, cursor) in &self.cursors {
@ -1669,6 +1704,8 @@ pub enum WlSurfaceError {
MissingSyncPoints, MissingSyncPoints,
#[error("No buffer is attached but acquire or release point is set")] #[error("No buffer is attached but acquire or release point is set")]
UnexpectedSyncPoints, UnexpectedSyncPoints,
#[error("The supplied region is invalid")]
InvalidRect,
} }
efrom!(WlSurfaceError, ClientError); efrom!(WlSurfaceError, ClientError);
efrom!(WlSurfaceError, XdgSurfaceError); efrom!(WlSurfaceError, XdgSurfaceError);

View file

@ -427,10 +427,10 @@ impl Renderer<'_> {
}; };
} }
render!(&children.below); 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); render!(&children.above);
} else { } 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() { if let Some(result) = self.result.as_deref_mut() {
{ {
@ -446,6 +446,7 @@ impl Renderer<'_> {
pub fn render_buffer( pub fn render_buffer(
&mut self, &mut self,
surface: &WlSurface,
buffer: &Rc<SurfaceBuffer>, buffer: &Rc<SurfaceBuffer>,
alpha: Option<f32>, alpha: Option<f32>,
x: i32, x: i32,
@ -454,7 +455,7 @@ impl Renderer<'_> {
tsize: (i32, i32), tsize: (i32, i32),
bounds: Option<&Rect>, bounds: Option<&Rect>,
) { ) {
if let Some(tex) = buffer.buffer.texture.get() { if let Some(tex) = buffer.buffer.get_texture(surface) {
self.base.render_texture( self.base.render_texture(
&tex, &tex,
alpha, alpha,

View file

@ -43,7 +43,7 @@ use {
wl_subsurface::SubsurfaceIds, wl_subsurface::SubsurfaceIds,
zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1}, zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1},
zwp_input_popup_surface_v2::ZwpInputPopupSurfaceV2, zwp_input_popup_surface_v2::ZwpInputPopupSurfaceV2,
NoneSurfaceExt, WlSurface, NoneSurfaceExt,
}, },
wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1,
wp_drm_lease_device_v1::WpDrmLeaseDeviceV1Global, wp_drm_lease_device_v1::WpDrmLeaseDeviceV1Global,
@ -87,7 +87,7 @@ use {
xkbcommon::{KeyboardStateIds, XkbContext, XkbKeymap, XkbState}, xkbcommon::{KeyboardStateIds, XkbContext, XkbKeymap, XkbState},
xwayland::{self, XWaylandEvent}, xwayland::{self, XWaylandEvent},
}, },
ahash::AHashMap, ahash::{AHashMap, AHashSet},
bstr::ByteSlice, bstr::ByteSlice,
jay_config::{ jay_config::{
video::{GfxApi, Transform}, video::{GfxApi, Transform},
@ -456,17 +456,22 @@ impl State {
node.textures.clear(); node.textures.clear();
node.node_visit_children(self); node.node_visit_children(self);
} }
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
if let Some(buffer) = node.buffer.get() {
buffer.buffer.handle_gfx_context_change();
}
node.node_visit_children(self);
}
} }
Walker.visit_display(&self.root); Walker.visit_display(&self.root);
for client in self.clients.clients.borrow_mut().values() { 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() { 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);
}
} }
} }
} }

View file

@ -211,10 +211,15 @@ fn render2(
Err(e) => return Err(TextError::ImageData(e)), Err(e) => return Err(TextError::ImageData(e)),
}; };
let old = old.map(|o| o.texture); let old = old.map(|o| o.texture);
match ctx match ctx.clone().shmem_texture(
.clone() old,
.shmem_texture(old, bytes, ARGB8888, width, height, data.image.stride()) bytes,
{ ARGB8888,
width,
height,
data.image.stride(),
None,
) {
Ok(t) => Ok(TextTexture { Ok(t) => Ok(TextTexture {
config: Rc::new(config.to_static()), config: Rc::new(config.to_static()),
texture: t, texture: t,

View file

@ -178,49 +178,52 @@ impl OutputNode {
capture.send_failed(); capture.send_failed();
continue; continue;
} }
if let Some(WlBufferStorage::Shm { mem, stride }) = if let Some(storage) = wl_buffer.storage.borrow_mut().deref() {
wl_buffer.storage.borrow_mut().deref() match storage {
{ WlBufferStorage::Shm { mem, stride } => {
let res = self.state.perform_shm_screencopy( let res = self.state.perform_shm_screencopy(
tex, tex,
self.global.pos.get(), self.global.pos.get(),
x_off, x_off,
y_off, y_off,
size, size,
&capture, &capture,
mem, mem,
*stride, *stride,
wl_buffer.format, wl_buffer.format,
Transform::None, Transform::None,
); );
if let Err(e) = res { if let Err(e) = res {
log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e)); log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e));
capture.send_failed(); capture.send_failed();
continue; continue;
} }
} else { }
let fb = match wl_buffer.famebuffer.get() { WlBufferStorage::Dmabuf { fb, .. } => {
Some(fb) => fb, let fb = match fb {
_ => { Some(fb) => fb,
log::warn!("Capture buffer has no framebuffer"); _ => {
capture.send_failed(); log::warn!("Capture buffer has no framebuffer");
continue; 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() { if capture.with_damage.get() {

View file

@ -1,16 +1,16 @@
use std::cell::Cell; use {crate::utils::ptr_ext::PtrExt, std::cell::Cell};
pub trait CellExt { pub trait CellExt {
fn is_some(&self) -> bool; fn is_some(&self) -> bool;
fn is_none(&self) -> bool; fn is_none(&self) -> bool;
} }
impl<T: Copy> CellExt for Cell<Option<T>> { impl<T> CellExt for Cell<Option<T>> {
fn is_some(&self) -> bool { fn is_some(&self) -> bool {
self.get().is_some() unsafe { self.as_ptr().deref().is_some() }
} }
fn is_none(&self) -> bool { fn is_none(&self) -> bool {
self.get().is_none() !self.is_some()
} }
} }