1
0
Fork 0
forked from wry/wry

gfx: implement async shm downloads

This commit is contained in:
Julian Orth 2024-10-06 15:11:00 +02:00
parent aca14d48dd
commit 028d0ed44c
11 changed files with 194 additions and 198 deletions

View file

@ -265,11 +265,23 @@ pub trait GfxFramebuffer: Debug {
clear: Option<&Color>, clear: Option<&Color>,
) -> Result<Option<SyncFile>, GfxError>; ) -> Result<Option<SyncFile>, GfxError>;
fn copy_to_shm(self: Rc<Self>, shm: &[Cell<u8>]) -> Result<(), GfxError>;
fn format(&self) -> &'static Format; fn format(&self) -> &'static Format;
} }
pub trait GfxInternalFramebuffer: GfxFramebuffer {
fn into_fb(self: Rc<Self>) -> Rc<dyn GfxFramebuffer>;
fn staging_size(&self) -> usize;
fn download(
self: Rc<Self>,
staging: &Rc<dyn GfxStagingBuffer>,
callback: Rc<dyn AsyncShmGfxTextureCallback>,
mem: Rc<dyn ShmMemory>,
damage: Region,
) -> Result<Option<PendingShmTransfer>, GfxError>;
}
impl dyn GfxFramebuffer { impl dyn GfxFramebuffer {
pub fn clear( pub fn clear(
&self, &self,
@ -593,13 +605,14 @@ pub trait GfxContext: Debug {
fn gfx_api(&self) -> GfxApi; fn gfx_api(&self) -> GfxApi;
fn create_fb( fn create_internal_fb(
self: Rc<Self>, self: Rc<Self>,
cpu_worker: &Rc<CpuWorker>,
width: i32, width: i32,
height: i32, height: i32,
stride: i32, stride: i32,
format: &'static Format, format: &'static Format,
) -> Result<Rc<dyn GfxFramebuffer>, GfxError>; ) -> Result<Rc<dyn GfxInternalFramebuffer>, GfxError>;
fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>>; fn sync_obj_ctx(&self) -> Option<&Rc<SyncObjCtx>>;

View file

@ -5,7 +5,7 @@ use {
format::{Format, XRGB8888}, format::{Format, XRGB8888},
gfx_api::{ gfx_api::{
AsyncShmGfxTexture, BufferResvUser, GfxContext, GfxError, GfxFormat, GfxFramebuffer, AsyncShmGfxTexture, BufferResvUser, GfxContext, GfxError, GfxFormat, GfxFramebuffer,
GfxImage, ResetStatus, ShmGfxTexture, GfxImage, GfxInternalFramebuffer, ResetStatus, ShmGfxTexture,
}, },
gfx_apis::gl::{ gfx_apis::gl::{
egl::{context::EglContext, display::EglDisplay, image::EglImage}, egl::{context::EglContext, display::EglDisplay, image::EglImage},
@ -316,13 +316,14 @@ impl GfxContext for GlRenderContext {
GfxApi::OpenGl GfxApi::OpenGl
} }
fn create_fb( fn create_internal_fb(
self: Rc<Self>, self: Rc<Self>,
_cpu_worker: &Rc<CpuWorker>,
width: i32, width: i32,
height: i32, height: i32,
_stride: i32, _stride: i32,
format: &'static Format, format: &'static Format,
) -> Result<Rc<dyn GfxFramebuffer>, GfxError> { ) -> Result<Rc<dyn GfxInternalFramebuffer>, GfxError> {
let fb = self.ctx.with_current(|| unsafe { let fb = self.ctx.with_current(|| unsafe {
GlRenderBuffer::new(&self.ctx, width, height, format)?.create_framebuffer() GlRenderBuffer::new(&self.ctx, width, height, format)?.create_framebuffer()
})?; })?;

View file

@ -1,7 +1,11 @@
use { use {
crate::{ crate::{
format::Format, format::Format,
gfx_api::{AcquireSync, GfxApiOpt, GfxError, GfxFramebuffer, ReleaseSync, SyncFile}, gfx_api::{
AcquireSync, AsyncShmGfxTextureCallback, GfxApiOpt, GfxError, GfxFramebuffer,
GfxInternalFramebuffer, GfxStagingBuffer, PendingShmTransfer, ReleaseSync, ShmMemory,
SyncFile,
},
gfx_apis::gl::{ gfx_apis::gl::{
gl::{ gl::{
frame_buffer::GlFrameBuffer, frame_buffer::GlFrameBuffer,
@ -13,6 +17,7 @@ use {
sys::{GL_ONE, GL_ONE_MINUS_SRC_ALPHA}, sys::{GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
RenderError, RenderError,
}, },
rect::Region,
theme::Color, theme::Color,
}, },
std::{ std::{
@ -104,11 +109,31 @@ impl GfxFramebuffer for Framebuffer {
self.render(acquire_sync, ops, clear).map_err(|e| e.into()) self.render(acquire_sync, ops, clear).map_err(|e| e.into())
} }
fn copy_to_shm(self: Rc<Self>, shm: &[Cell<u8>]) -> Result<(), GfxError> {
(*self).copy_to_shm(shm).map_err(|e| e.into())
}
fn format(&self) -> &'static Format { fn format(&self) -> &'static Format {
self.gl.rb.format self.gl.rb.format
} }
} }
impl GfxInternalFramebuffer for Framebuffer {
fn into_fb(self: Rc<Self>) -> Rc<dyn GfxFramebuffer> {
self
}
fn staging_size(&self) -> usize {
0
}
fn download(
self: Rc<Self>,
_staging: &Rc<dyn GfxStagingBuffer>,
_callback: Rc<dyn AsyncShmGfxTextureCallback>,
mem: Rc<dyn ShmMemory>,
_damage: Region,
) -> Result<Option<PendingShmTransfer>, GfxError> {
let mut res = Ok(());
mem.access(&mut |mem| res = self.copy_to_shm(mem))
.map_err(RenderError::AccessFailed)?;
res?;
Ok(None)
}
}

View file

@ -23,7 +23,7 @@ use {
cpu_worker::{jobs::read_write::ReadWriteJobError, CpuWorker}, cpu_worker::{jobs::read_write::ReadWriteJobError, CpuWorker},
format::Format, format::Format,
gfx_api::{ gfx_api::{
AsyncShmGfxTexture, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage, AsyncShmGfxTexture, GfxContext, GfxError, GfxFormat, GfxImage, GfxInternalFramebuffer,
GfxStagingBuffer, ResetStatus, ShmGfxTexture, StagingBufferUsecase, STAGING_DOWNLOAD, GfxStagingBuffer, ResetStatus, ShmGfxTexture, StagingBufferUsecase, STAGING_DOWNLOAD,
STAGING_UPLOAD, STAGING_UPLOAD,
}, },
@ -316,16 +316,23 @@ impl GfxContext for Context {
GfxApi::Vulkan GfxApi::Vulkan
} }
fn create_fb( fn create_internal_fb(
self: Rc<Self>, self: Rc<Self>,
cpu_worker: &Rc<CpuWorker>,
width: i32, width: i32,
height: i32, height: i32,
stride: i32, stride: i32,
format: &'static Format, format: &'static Format,
) -> Result<Rc<dyn GfxFramebuffer>, GfxError> { ) -> Result<Rc<dyn GfxInternalFramebuffer>, GfxError> {
let fb = self let fb = self.0.create_shm_texture(
.0 format,
.create_shm_texture(format, width, height, stride, &[], true, None)?; width,
height,
stride,
&[],
true,
Some(cpu_worker),
)?;
Ok(fb) Ok(fb)
} }

View file

@ -4,8 +4,8 @@ use {
gfx_api::{ gfx_api::{
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback, AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback,
AsyncShmGfxTextureTransferCancellable, GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, AsyncShmGfxTextureTransferCancellable, GfxApiOpt, GfxError, GfxFramebuffer, GfxImage,
GfxStagingBuffer, GfxTexture, PendingShmTransfer, ReleaseSync, ShmGfxTexture, GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture, PendingShmTransfer, ReleaseSync,
ShmMemory, SyncFile, ShmGfxTexture, ShmMemory, SyncFile,
}, },
gfx_apis::vulkan::{ gfx_apis::vulkan::{
allocator::VulkanAllocation, device::VulkanDevice, format::VulkanModifierLimits, allocator::VulkanAllocation, device::VulkanDevice, format::VulkanModifierLimits,
@ -510,17 +510,46 @@ impl GfxFramebuffer for VulkanImage {
.map_err(|e| e.into()) .map_err(|e| e.into())
} }
fn copy_to_shm(self: Rc<Self>, shm: &[Cell<u8>]) -> Result<(), GfxError> {
self.renderer
.read_all_pixels(&self, shm)
.map_err(|e| e.into())
}
fn format(&self) -> &'static Format { fn format(&self) -> &'static Format {
self.format self.format
} }
} }
impl GfxInternalFramebuffer for VulkanImage {
fn into_fb(self: Rc<Self>) -> Rc<dyn GfxFramebuffer> {
self
}
fn staging_size(&self) -> usize {
let VulkanImageMemory::Internal(shm) = &self.ty else {
unreachable!();
};
shm.size as _
}
fn download(
self: Rc<Self>,
staging: &Rc<dyn GfxStagingBuffer>,
callback: Rc<dyn AsyncShmGfxTextureCallback>,
mem: Rc<dyn ShmMemory>,
damage: Region,
) -> Result<Option<PendingShmTransfer>, GfxError> {
let VulkanImageMemory::Internal(shm) = &self.ty else {
unreachable!();
};
let staging = staging.clone().into_vk(&self.renderer.device.device);
let pending = shm.async_transfer(
&self,
staging,
&mem,
damage,
callback,
TransferType::Download,
)?;
Ok(pending)
}
}
impl GfxTexture for VulkanImage { impl GfxTexture for VulkanImage {
fn size(&self) -> (i32, i32) { fn size(&self) -> (i32, i32) {
(self.width as _, self.height as _) (self.width as _, self.height as _)

View file

@ -32,11 +32,10 @@ use {
ash::{ ash::{
vk, vk,
vk::{ vk::{
AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, BufferImageCopy, AccessFlags2, AttachmentLoadOp, AttachmentStoreOp, ClearColorValue, ClearValue,
BufferMemoryBarrier2, ClearColorValue, ClearValue, CommandBuffer, CommandBuffer, CommandBufferBeginInfo, CommandBufferSubmitInfo,
CommandBufferBeginInfo, CommandBufferSubmitInfo, CommandBufferUsageFlags, CommandBufferUsageFlags, CopyImageInfo2, DependencyInfoKHR, DescriptorImageInfo,
CopyImageInfo2, DependencyInfo, DependencyInfoKHR, DescriptorImageInfo, DescriptorType, DescriptorType, Extent2D, Extent3D, ImageAspectFlags, ImageCopy2, ImageLayout,
Extent2D, Extent3D, Fence, ImageAspectFlags, ImageCopy2, ImageLayout,
ImageMemoryBarrier2, ImageSubresourceLayers, ImageSubresourceRange, PipelineBindPoint, ImageMemoryBarrier2, ImageSubresourceLayers, ImageSubresourceRange, PipelineBindPoint,
PipelineStageFlags2, Rect2D, RenderingAttachmentInfo, RenderingInfo, PipelineStageFlags2, Rect2D, RenderingAttachmentInfo, RenderingInfo,
SemaphoreSubmitInfo, SemaphoreSubmitInfoKHR, ShaderStageFlags, SubmitInfo2, Viewport, SemaphoreSubmitInfo, SemaphoreSubmitInfoKHR, ShaderStageFlags, SubmitInfo2, Viewport,
@ -49,7 +48,7 @@ use {
std::{ std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
fmt::{Debug, Formatter}, fmt::{Debug, Formatter},
mem, ptr, mem,
rc::Rc, rc::Rc,
slice, slice,
}, },
@ -905,143 +904,6 @@ impl VulkanRenderer {
frame.waiter.set(Some(future)); frame.waiter.set(Some(future));
} }
pub(super) fn read_all_pixels(
self: &Rc<Self>,
tex: &VulkanImage,
dst: &[Cell<u8>],
) -> Result<(), VulkanError> {
let Some(shm_info) = &tex.format.shm_info else {
return Err(VulkanError::UnsupportedShmFormat(tex.format.name));
};
let size = tex.stride as u64 * tex.height as u64;
if size != dst.len() as u64 {
return Err(VulkanError::InvalidBufferSize);
}
let region = BufferImageCopy::default()
.buffer_row_length(tex.stride / shm_info.bpp)
.buffer_image_height(tex.height)
.image_subresource(ImageSubresourceLayers {
aspect_mask: ImageAspectFlags::COLOR,
mip_level: 0,
base_array_layer: 0,
layer_count: 1,
})
.image_extent(Extent3D {
width: tex.width,
height: tex.height,
depth: 1,
});
let staging =
self.device
.create_staging_buffer(&self.allocator, size, false, true, true)?;
let initial_tex_barrier;
let initial_buffer_barrier = BufferMemoryBarrier2::default()
.buffer(staging.buffer)
.offset(0)
.size(staging.size)
.dst_access_mask(AccessFlags2::TRANSFER_WRITE)
.dst_stage_mask(PipelineStageFlags2::TRANSFER);
let mut initial_barriers = DependencyInfo::default()
.buffer_memory_barriers(slice::from_ref(&initial_buffer_barrier));
if tex.bridge.is_none() {
initial_tex_barrier = image_barrier()
.src_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
.dst_queue_family_index(self.device.graphics_queue_idx)
.image(tex.image)
.old_layout(ImageLayout::GENERAL)
.new_layout(ImageLayout::TRANSFER_SRC_OPTIMAL)
.dst_access_mask(AccessFlags2::TRANSFER_READ)
.dst_stage_mask(PipelineStageFlags2::TRANSFER);
initial_barriers =
initial_barriers.image_memory_barriers(slice::from_ref(&initial_tex_barrier));
}
let final_tex_barrier;
let final_buffer_barrier = BufferMemoryBarrier2::default()
.buffer(staging.buffer)
.offset(0)
.size(staging.size)
.src_access_mask(AccessFlags2::TRANSFER_WRITE)
.src_stage_mask(PipelineStageFlags2::TRANSFER)
.dst_access_mask(AccessFlags2::HOST_READ)
.dst_stage_mask(PipelineStageFlags2::HOST);
let mut final_barriers = DependencyInfo::default()
.buffer_memory_barriers(slice::from_ref(&final_buffer_barrier));
if tex.bridge.is_none() {
final_tex_barrier = image_barrier()
.src_queue_family_index(self.device.graphics_queue_idx)
.dst_queue_family_index(QUEUE_FAMILY_FOREIGN_EXT)
.image(tex.image)
.old_layout(ImageLayout::TRANSFER_SRC_OPTIMAL)
.new_layout(ImageLayout::GENERAL)
.src_access_mask(AccessFlags2::TRANSFER_READ)
.src_stage_mask(PipelineStageFlags2::TRANSFER);
final_barriers =
final_barriers.image_memory_barriers(slice::from_ref(&final_tex_barrier));
}
let buf = self.gfx_command_buffers.allocate()?;
let mut semaphores = vec![];
let mut semaphore_infos = vec![];
if let VulkanImageMemory::DmaBuf(buf) = &tex.ty {
for plane in &buf.template.dmabuf.planes {
let fd = dma_buf_export_sync_file(&plane.fd, DMA_BUF_SYNC_READ)
.map_err(VulkanError::IoctlExportSyncFile)?;
let semaphore = self.allocate_semaphore()?;
semaphore.import_sync_file(fd)?;
let semaphore_info = SemaphoreSubmitInfo::default()
.semaphore(semaphore.semaphore)
.stage_mask(PipelineStageFlags2::TOP_OF_PIPE);
semaphores.push(semaphore);
semaphore_infos.push(semaphore_info);
}
}
let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(buf.buffer);
let submit_info = SubmitInfo2::default()
.wait_semaphore_infos(&semaphore_infos)
.command_buffer_infos(slice::from_ref(&command_buffer_info));
let begin_info =
CommandBufferBeginInfo::default().flags(CommandBufferUsageFlags::ONE_TIME_SUBMIT);
unsafe {
self.device
.device
.begin_command_buffer(buf.buffer, &begin_info)
.map_err(VulkanError::BeginCommandBuffer)?;
self.device
.device
.cmd_pipeline_barrier2(buf.buffer, &initial_barriers);
self.device.device.cmd_copy_image_to_buffer(
buf.buffer,
tex.image,
ImageLayout::TRANSFER_SRC_OPTIMAL,
staging.buffer,
&[region],
);
self.device
.device
.cmd_pipeline_barrier2(buf.buffer, &final_barriers);
self.device
.device
.end_command_buffer(buf.buffer)
.map_err(VulkanError::EndCommandBuffer)?;
self.device
.device
.queue_submit2(
self.device.graphics_queue,
slice::from_ref(&submit_info),
Fence::null(),
)
.map_err(VulkanError::Submit)?;
}
self.block();
self.gfx_command_buffers.buffers.push(buf);
for semaphore in semaphores {
self.wait_semaphores.push(semaphore);
}
staging.download(|mem, size| unsafe {
ptr::copy_nonoverlapping(mem, dst.as_ptr() as _, size);
})?;
Ok(())
}
pub fn execute( pub fn execute(
self: &Rc<Self>, self: &Rc<Self>,
fb: &VulkanImage, fb: &VulkanImage,

View file

@ -2,6 +2,7 @@ use {
crate::{ crate::{
client::{Client, ClientError}, client::{Client, ClientError},
format::XRGB8888, format::XRGB8888,
gfx_api::{AsyncShmGfxTextureCallback, GfxError, PendingShmTransfer},
ifs::{ ifs::{
wl_buffer::{WlBuffer, WlBufferError, WlBufferStorage}, wl_buffer::{WlBuffer, WlBufferError, WlBufferStorage},
wl_output::OutputGlobalOpt, wl_output::OutputGlobalOpt,
@ -9,6 +10,7 @@ use {
leaks::Tracker, leaks::Tracker,
object::{Object, Version}, object::{Object, Version},
rect::Rect, rect::Rect,
utils::errorfmt::ErrorFmt,
wire::{zwlr_screencopy_frame_v1::*, WlBufferId, ZwlrScreencopyFrameV1Id}, wire::{zwlr_screencopy_frame_v1::*, WlBufferId, ZwlrScreencopyFrameV1Id},
}, },
std::{cell::Cell, ops::Deref, rc::Rc}, std::{cell::Cell, ops::Deref, rc::Rc},
@ -29,6 +31,7 @@ pub struct ZwlrScreencopyFrameV1 {
pub with_damage: Cell<bool>, pub with_damage: Cell<bool>,
pub buffer: Cell<Option<Rc<WlBuffer>>>, pub buffer: Cell<Option<Rc<WlBuffer>>>,
pub version: Version, pub version: Version,
pub pending: Cell<Option<PendingShmTransfer>>,
} }
impl ZwlrScreencopyFrameV1 { impl ZwlrScreencopyFrameV1 {
@ -132,6 +135,7 @@ impl ZwlrScreencopyFrameV1 {
node.screencopies.remove(&(self.client.id, self.id)); node.screencopies.remove(&(self.client.id, self.id));
node.screencast_changed(); node.screencast_changed();
} }
self.pending.take();
} }
} }
@ -153,6 +157,22 @@ impl ZwlrScreencopyFrameV1RequestHandler for ZwlrScreencopyFrameV1 {
} }
} }
impl AsyncShmGfxTextureCallback for ZwlrScreencopyFrameV1 {
fn completed(self: Rc<Self>, res: Result<(), GfxError>) {
self.pending.take();
match res {
Ok(_) => {
let now = self.client.state.now();
self.send_ready(now.0.tv_sec as _, now.0.tv_nsec as _);
}
Err(e) => {
log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e));
self.send_failed();
}
}
}
}
object_base! { object_base! {
self = ZwlrScreencopyFrameV1; self = ZwlrScreencopyFrameV1;
version = self.version; version = self.version;

View file

@ -130,6 +130,7 @@ impl ZwlrScreencopyManagerV1 {
with_damage: Cell::new(false), with_damage: Cell::new(false),
buffer: Cell::new(None), buffer: Cell::new(None),
version: self.version, version: self.version,
pending: Default::default(),
}); });
track!(self.client, frame); track!(self.client, frame);
self.client.add_client_obj(&frame)?; self.client.add_client_obj(&frame)?;

View file

@ -6,8 +6,8 @@ use {
gfx_api::{ gfx_api::{
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback, CopyTexture, FillRect, AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback, CopyTexture, FillRect,
FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage, FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage,
GfxStagingBuffer, GfxTexture, GfxWriteModifier, PendingShmTransfer, ReleaseSync, GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture, GfxWriteModifier,
ResetStatus, ShmGfxTexture, ShmMemory, SyncFile, PendingShmTransfer, ReleaseSync, ResetStatus, ShmGfxTexture, ShmMemory, SyncFile,
}, },
rect::{Rect, Region}, rect::{Rect, Region},
theme::Color, theme::Color,
@ -165,13 +165,14 @@ impl GfxContext for TestGfxCtx {
GfxApi::OpenGl GfxApi::OpenGl
} }
fn create_fb( fn create_internal_fb(
self: Rc<Self>, self: Rc<Self>,
_cpu_worker: &Rc<CpuWorker>,
width: i32, width: i32,
height: i32, height: i32,
stride: i32, stride: i32,
format: &'static Format, format: &'static Format,
) -> Result<Rc<dyn GfxFramebuffer>, GfxError> { ) -> Result<Rc<dyn GfxInternalFramebuffer>, GfxError> {
assert!(stride >= width * 4); assert!(stride >= width * 4);
Ok(Rc::new(TestGfxFb { Ok(Rc::new(TestGfxFb {
img: Rc::new(TestGfxImage::Shm(TestShmGfxImage { img: Rc::new(TestGfxImage::Shm(TestShmGfxImage {
@ -548,15 +549,34 @@ impl GfxFramebuffer for TestGfxFb {
Ok(None) Ok(None)
} }
fn copy_to_shm(self: Rc<Self>, shm: &[Cell<u8>]) -> Result<(), GfxError> {
self.img.deref().read_pixels(shm)
}
fn format(&self) -> &'static Format { fn format(&self) -> &'static Format {
&ARGB8888 &ARGB8888
} }
} }
impl GfxInternalFramebuffer for TestGfxFb {
fn into_fb(self: Rc<Self>) -> Rc<dyn GfxFramebuffer> {
self
}
fn staging_size(&self) -> usize {
0
}
fn download(
self: Rc<Self>,
_staging: &Rc<dyn GfxStagingBuffer>,
_callback: Rc<dyn AsyncShmGfxTextureCallback>,
mem: Rc<dyn ShmMemory>,
_damage: Region,
) -> Result<Option<PendingShmTransfer>, GfxError> {
let mut res = Ok(());
mem.access(&mut |mem| res = self.img.deref().read_pixels(mem))
.map_err(TestGfxError::AccessFailed)?;
res.map(|_| None)
}
}
impl dyn GfxTexture { impl dyn GfxTexture {
fn as_native(&self) -> &TestGfxImage { fn as_native(&self) -> &TestGfxImage {
self.as_any() self.as_any()

View file

@ -27,8 +27,8 @@ use {
forker::ForkerProxy, forker::ForkerProxy,
format::Format, format::Format,
gfx_api::{ gfx_api::{
AcquireSync, BufferResv, GfxContext, GfxError, GfxFramebuffer, GfxTexture, ReleaseSync, AcquireSync, BufferResv, GfxContext, GfxError, GfxFramebuffer, GfxTexture,
SampleRect, SyncFile, PendingShmTransfer, ReleaseSync, SampleRect, SyncFile, STAGING_DOWNLOAD,
}, },
gfx_apis::create_gfx_context, gfx_apis::create_gfx_context,
globals::{Globals, GlobalsError, RemovableWaylandGlobal, WaylandGlobal}, globals::{Globals, GlobalsError, RemovableWaylandGlobal, WaylandGlobal},
@ -62,7 +62,7 @@ use {
io_uring::IoUring, io_uring::IoUring,
leaks::Tracker, leaks::Tracker,
logger::Logger, logger::Logger,
rect::Rect, rect::{Rect, Region},
renderer::Renderer, renderer::Renderer,
scale::Scale, scale::Scale,
security_context_acceptor::SecurityContextAcceptors, security_context_acceptor::SecurityContextAcceptors,
@ -1014,25 +1014,32 @@ impl State {
x_off: i32, x_off: i32,
y_off: i32, y_off: i32,
size: Option<(i32, i32)>, size: Option<(i32, i32)>,
capture: &ZwlrScreencopyFrameV1, capture: &Rc<ZwlrScreencopyFrameV1>,
mem: &ClientMemOffset, mem: &Rc<ClientMemOffset>,
stride: i32, stride: i32,
format: &'static Format, format: &'static Format,
transform: Transform, transform: Transform,
scale: Scale, scale: Scale,
) -> Result<(), ShmScreencopyError> { ) -> Result<Option<PendingShmTransfer>, ShmScreencopyError> {
let Some(ctx) = self.render_ctx.get() else { let Some(ctx) = self.render_ctx.get() else {
return Err(ShmScreencopyError::NoRenderContext); return Err(ShmScreencopyError::NoRenderContext);
}; };
let fb = ctx let fb = ctx
.create_fb(capture.rect.width(), capture.rect.height(), stride, format) .clone()
.create_internal_fb(
&self.cpu_worker,
capture.rect.width(),
capture.rect.height(),
stride,
format,
)
.map_err(ShmScreencopyError::CreateTemporaryFb)?; .map_err(ShmScreencopyError::CreateTemporaryFb)?;
self.perform_screencopy( self.perform_screencopy(
src, src,
None, None,
acquire_sync, acquire_sync,
ReleaseSync::None, ReleaseSync::None,
&fb, &fb.clone().into_fb(),
AcquireSync::Unnecessary, AcquireSync::Unnecessary,
ReleaseSync::None, ReleaseSync::None,
transform, transform,
@ -1045,14 +1052,16 @@ impl State {
scale, scale,
) )
.map_err(ShmScreencopyError::CopyToTemporary)?; .map_err(ShmScreencopyError::CopyToTemporary)?;
let acc = mem.access(|mem| fb.copy_to_shm(mem)); let staging = ctx.create_staging_buffer(fb.staging_size(), STAGING_DOWNLOAD);
match acc { let pending = fb
Ok(res) => res.map_err(ShmScreencopyError::ReadPixels), .download(
Err(e) => { &staging,
capture.client.error(e); capture.clone(),
Ok(()) mem.clone(),
} Region::new2(capture.rect.at_point(0, 0)),
} )
.map_err(ShmScreencopyError::ReadPixels)?;
Ok(pending)
} }
pub fn create_seat(self: &Rc<Self>, name: &str) -> Rc<WlSeatGlobal> { pub fn create_seat(self: &Rc<Self>, name: &str) -> Rc<WlSeatGlobal> {

View file

@ -267,6 +267,7 @@ impl OutputNode {
capture.send_failed(); capture.send_failed();
continue; continue;
} }
let mut ready = true;
if let Some(storage) = wl_buffer.storage.borrow_mut().deref() { if let Some(storage) = wl_buffer.storage.borrow_mut().deref() {
match storage { match storage {
WlBufferStorage::Shm { mem, stride } => { WlBufferStorage::Shm { mem, stride } => {
@ -284,10 +285,16 @@ impl OutputNode {
self.global.persistent.transform.get(), self.global.persistent.transform.get(),
self.global.persistent.scale.get(), self.global.persistent.scale.get(),
); );
if let Err(e) = res { match res {
log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e)); Ok(p) => {
capture.send_failed(); ready = p.is_none();
continue; capture.pending.set(p);
}
Err(e) => {
log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e));
capture.send_failed();
continue;
}
} }
} }
WlBufferStorage::Dmabuf { fb, .. } => { WlBufferStorage::Dmabuf { fb, .. } => {
@ -327,7 +334,9 @@ impl OutputNode {
if capture.with_damage.get() { if capture.with_damage.get() {
capture.send_damage(); capture.send_damage();
} }
capture.send_ready(now.0.tv_sec as _, now.0.tv_nsec as _); if ready {
capture.send_ready(now.0.tv_sec as _, now.0.tv_nsec as _);
}
} }
self.screencast_changed(); self.screencast_changed();
} }