vulkan: optimize shm handling
This commit is contained in:
parent
03c02be34c
commit
af80fada6c
14 changed files with 629 additions and 413 deletions
|
|
@ -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)?,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
374
src/gfx_apis/vulkan/shm_image.rs
Normal file
374
src/gfx_apis/vulkan/shm_image.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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()?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
23
src/state.rs
23
src/state.rs
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
src/text.rs
13
src/text.rs
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue