1
0
Fork 0
forked from wry/wry

render: add abstraction for async-upload storage

This commit is contained in:
Julian Orth 2024-09-28 16:02:20 +02:00
parent d99444bd3c
commit ca134e683b
8 changed files with 149 additions and 93 deletions

View file

@ -2,11 +2,14 @@ use {
crate::{ crate::{
client::Client, client::Client,
cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker}, cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker},
gfx_api::{ShmMemory, ShmMemoryBacking},
utils::vec_ext::VecExt, utils::vec_ext::VecExt,
}, },
std::{ std::{
cell::Cell, cell::Cell,
error::Error,
mem::{ManuallyDrop, MaybeUninit}, mem::{ManuallyDrop, MaybeUninit},
ops::Deref,
ptr, ptr,
rc::Rc, rc::Rc,
sync::atomic::{compiler_fence, Ordering}, sync::atomic::{compiler_fence, Ordering},
@ -105,6 +108,7 @@ impl ClientMem {
} }
} }
#[expect(dead_code)]
pub fn fd(&self) -> &Rc<OwnedFd> { pub fn fd(&self) -> &Rc<OwnedFd> {
&self.fd &self.fd
} }
@ -115,14 +119,17 @@ impl ClientMem {
} }
impl ClientMemOffset { impl ClientMemOffset {
#[expect(dead_code)]
pub fn pool(&self) -> &ClientMem { pub fn pool(&self) -> &ClientMem {
&self.mem &self.mem
} }
#[expect(dead_code)]
pub fn offset(&self) -> usize { pub fn offset(&self) -> usize {
self.offset self.offset
} }
#[expect(dead_code)]
pub fn ptr(&self) -> *const [Cell<u8>] { pub fn ptr(&self) -> *const [Cell<u8>] {
self.data self.data
} }
@ -263,3 +270,20 @@ impl CpuWork for CloseMemWork {
None None
} }
} }
impl ShmMemory for ClientMemOffset {
fn len(&self) -> usize {
self.data.len()
}
fn safe_access(&self) -> ShmMemoryBacking {
match self.mem.sigbus_impossible() {
true => ShmMemoryBacking::Ptr(self.data),
false => ShmMemoryBacking::Fd(self.mem.fd.deref().clone(), self.offset),
}
}
fn access(&self, f: &mut dyn FnMut(&[Cell<u8>])) -> Result<(), Box<dyn Error + Sync + Send>> {
self.access(f).map_err(|e| e.into())
}
}

View file

@ -1,7 +1,6 @@
use { use {
crate::{ crate::{
allocator::Allocator, allocator::Allocator,
clientmem::ClientMemOffset,
cpu_worker::CpuWorker, cpu_worker::CpuWorker,
cursor::Cursor, cursor::Cursor,
damage::DamageVisualizer, damage::DamageVisualizer,
@ -513,11 +512,37 @@ pub struct PendingShmUpload {
id: u64, id: u64,
} }
pub trait ShmMemory {
fn len(&self) -> usize;
fn safe_access(&self) -> ShmMemoryBacking;
fn access(&self, f: &mut dyn FnMut(&[Cell<u8>])) -> Result<(), Box<dyn Error + Sync + Send>>;
}
pub enum ShmMemoryBacking {
Ptr(*const [Cell<u8>]),
Fd(Rc<OwnedFd>, usize),
}
impl ShmMemory for Vec<Cell<u8>> {
fn len(&self) -> usize {
self.len()
}
fn safe_access(&self) -> ShmMemoryBacking {
ShmMemoryBacking::Ptr(&**self)
}
fn access(&self, f: &mut dyn FnMut(&[Cell<u8>])) -> Result<(), Box<dyn Error + Sync + Send>> {
f(self);
Ok(())
}
}
pub trait AsyncShmGfxTexture: GfxTexture { pub trait AsyncShmGfxTexture: GfxTexture {
fn async_upload( fn async_upload(
self: Rc<Self>, self: Rc<Self>,
callback: Rc<dyn AsyncShmGfxTextureCallback>, callback: Rc<dyn AsyncShmGfxTextureCallback>,
mem: &Rc<ClientMemOffset>, mem: Rc<dyn ShmMemory>,
damage: Region, damage: Region,
) -> Result<Option<PendingShmUpload>, GfxError>; ) -> Result<Option<PendingShmUpload>, GfxError>;

View file

@ -67,7 +67,6 @@ macro_rules! dynload {
use { use {
crate::{ crate::{
clientmem::ClientMemError,
gfx_api::{ gfx_api::{
AcquireSync, CopyTexture, FillRect, GfxApiOpt, GfxContext, GfxError, GfxTexture, AcquireSync, CopyTexture, FillRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
ReleaseSync, SyncFile, ReleaseSync, SyncFile,
@ -95,7 +94,7 @@ use {
}, },
isnt::std_1::vec::IsntVecExt, isnt::std_1::vec::IsntVecExt,
once_cell::sync::Lazy, once_cell::sync::Lazy,
std::{cell::RefCell, rc::Rc, sync::Arc}, std::{cell::RefCell, error::Error, rc::Rc, sync::Arc},
thiserror::Error, thiserror::Error,
}; };
@ -199,7 +198,7 @@ enum RenderError {
#[error("Buffer format {0} is not supported for shm buffers in OpenGL context")] #[error("Buffer format {0} is not supported for shm buffers in OpenGL context")]
UnsupportedShmFormat(&'static str), UnsupportedShmFormat(&'static str),
#[error("Could not access the client memory")] #[error("Could not access the client memory")]
AccessFailed(#[source] ClientMemError), AccessFailed(#[source] Box<dyn Error + Sync + Send>),
} }
#[derive(Default)] #[derive(Default)]

View file

@ -1,10 +1,9 @@
use { use {
crate::{ crate::{
clientmem::ClientMemOffset,
format::Format, format::Format,
gfx_api::{ gfx_api::{
AsyncShmGfxTexture, AsyncShmGfxTextureCallback, GfxError, GfxTexture, PendingShmUpload, AsyncShmGfxTexture, AsyncShmGfxTextureCallback, GfxError, GfxTexture, PendingShmUpload,
ShmGfxTexture, ShmGfxTexture, ShmMemory,
}, },
gfx_apis::gl::{ gfx_apis::gl::{
gl::texture::GlTexture, gl::texture::GlTexture,
@ -102,12 +101,15 @@ impl AsyncShmGfxTexture for Texture {
fn async_upload( fn async_upload(
self: Rc<Self>, self: Rc<Self>,
_callback: Rc<dyn AsyncShmGfxTextureCallback>, _callback: Rc<dyn AsyncShmGfxTextureCallback>,
mem: &Rc<ClientMemOffset>, mem: Rc<dyn ShmMemory>,
damage: Region, _damage: Region,
) -> Result<Option<PendingShmUpload>, GfxError> { ) -> Result<Option<PendingShmUpload>, GfxError> {
mem.access(|data| self.clone().sync_upload(data, damage)) let mut res = Ok(());
.map_err(RenderError::AccessFailed)??; mem.access(&mut |data| {
Ok(None) res = self.clone().sync_upload(data, Region::default());
})
.map_err(RenderError::AccessFailed)?;
res.map(|_| None)
} }
fn sync_upload(self: Rc<Self>, data: &[Cell<u8>], _damage: Region) -> Result<(), GfxError> { fn sync_upload(self: Rc<Self>, data: &[Cell<u8>], _damage: Region) -> Result<(), GfxError> {

View file

@ -1,11 +1,10 @@
use { use {
crate::{ crate::{
clientmem::ClientMemOffset,
format::Format, format::Format,
gfx_api::{ gfx_api::{
AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback, AcquireSync, AsyncShmGfxTexture, AsyncShmGfxTextureCallback,
AsyncShmGfxTextureUploadCancellable, GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, AsyncShmGfxTextureUploadCancellable, GfxApiOpt, GfxError, GfxFramebuffer, GfxImage,
GfxTexture, PendingShmUpload, ReleaseSync, ShmGfxTexture, SyncFile, GfxTexture, PendingShmUpload, ReleaseSync, ShmGfxTexture, ShmMemory, SyncFile,
}, },
gfx_apis::vulkan::{ gfx_apis::vulkan::{
allocator::VulkanAllocation, device::VulkanDevice, format::VulkanModifierLimits, allocator::VulkanAllocation, device::VulkanDevice, format::VulkanModifierLimits,
@ -579,13 +578,13 @@ impl AsyncShmGfxTexture for VulkanImage {
fn async_upload( fn async_upload(
self: Rc<Self>, self: Rc<Self>,
callback: Rc<dyn AsyncShmGfxTextureCallback>, callback: Rc<dyn AsyncShmGfxTextureCallback>,
mem: &Rc<ClientMemOffset>, mem: Rc<dyn ShmMemory>,
damage: Region, damage: Region,
) -> Result<Option<PendingShmUpload>, GfxError> { ) -> Result<Option<PendingShmUpload>, GfxError> {
let VulkanImageMemory::Internal(shm) = &self.ty else { let VulkanImageMemory::Internal(shm) = &self.ty else {
unreachable!(); unreachable!();
}; };
let pending = shm.async_upload(&self, mem, damage, callback)?; let pending = shm.async_upload(&self, &mem, damage, callback)?;
Ok(pending) Ok(pending)
} }

View file

@ -1,6 +1,5 @@
use { use {
crate::{ crate::{
clientmem::ClientMemOffset,
cpu_worker::{ cpu_worker::{
jobs::{ jobs::{
img_copy::ImgCopyWork, img_copy::ImgCopyWork,
@ -9,7 +8,9 @@ use {
CpuJob, CpuWork, CpuWorker, CpuJob, CpuWork, CpuWorker,
}, },
format::{Format, FormatShmInfo}, format::{Format, FormatShmInfo},
gfx_api::{AsyncShmGfxTextureCallback, PendingShmUpload, SyncFile}, gfx_api::{
AsyncShmGfxTextureCallback, PendingShmUpload, ShmMemory, ShmMemoryBacking, SyncFile,
},
gfx_apis::vulkan::{ gfx_apis::vulkan::{
allocator::VulkanAllocation, allocator::VulkanAllocation,
command::VulkanCommandBuffer, command::VulkanCommandBuffer,
@ -329,7 +330,7 @@ impl VulkanShmImage {
pub fn async_upload( pub fn async_upload(
&self, &self,
img: &Rc<VulkanImage>, img: &Rc<VulkanImage>,
client_mem: &Rc<ClientMemOffset>, client_mem: &Rc<dyn ShmMemory>,
damage: Region, damage: Region,
callback: Rc<dyn AsyncShmGfxTextureCallback>, callback: Rc<dyn AsyncShmGfxTextureCallback>,
) -> Result<Option<PendingShmUpload>, VulkanError> { ) -> Result<Option<PendingShmUpload>, VulkanError> {
@ -350,13 +351,13 @@ impl VulkanShmImage {
&self, &self,
img: &Rc<VulkanImage>, img: &Rc<VulkanImage>,
data: &VulkanShmImageAsyncData, data: &VulkanShmImageAsyncData,
client_mem: &Rc<ClientMemOffset>, client_mem: &Rc<dyn ShmMemory>,
mut damage: Region, mut damage: Region,
) -> Result<(), VulkanError> { ) -> Result<(), VulkanError> {
if data.busy.get() { if data.busy.get() {
return Err(VulkanError::AsyncCopyBusy); return Err(VulkanError::AsyncCopyBusy);
} }
if self.size > client_mem.ptr().len() as u64 { if self.size > client_mem.len() as u64 {
return Err(VulkanError::InvalidBufferSize); return Err(VulkanError::InvalidBufferSize);
} }
data.busy.set(true); data.busy.set(true);
@ -530,7 +531,7 @@ impl VulkanShmImage {
fn async_upload_after_allocation( fn async_upload_after_allocation(
&self, &self,
img: &Rc<VulkanImage>, img: &Rc<VulkanImage>,
client_mem: &Rc<ClientMemOffset>, client_mem: &Rc<dyn ShmMemory>,
res: Result<VulkanStagingBuffer, VulkanError>, res: Result<VulkanStagingBuffer, VulkanError>,
) -> Result<(), VulkanError> { ) -> Result<(), VulkanError> {
let staging = Rc::new(res?); let staging = Rc::new(res?);
@ -546,73 +547,76 @@ impl VulkanShmImage {
data: &VulkanShmImageAsyncData, data: &VulkanShmImageAsyncData,
staging: &VulkanStagingBuffer, staging: &VulkanStagingBuffer,
copies: &[BufferImageCopy2], copies: &[BufferImageCopy2],
client_mem: &Rc<ClientMemOffset>, client_mem: &Rc<dyn ShmMemory>,
) -> Result<(), VulkanError> { ) -> Result<(), VulkanError> {
img.renderer.check_defunct()?; img.renderer.check_defunct()?;
let id = img.renderer.allocate_point(); let id = img.renderer.allocate_point();
let pending; let pending;
if client_mem.pool().sigbus_impossible() { match client_mem.safe_access() {
let mut job = data.copy_job.take().unwrap_or_else(|| { ShmMemoryBacking::Ptr(ptr) => {
Box::new(CopyUploadJob { let mut job = data.copy_job.take().unwrap_or_else(|| {
img: None, Box::new(CopyUploadJob {
id, img: None,
_mem: None, id,
work: unsafe { ImgCopyWork::new() }, _mem: None,
}) work: unsafe { ImgCopyWork::new() },
}); })
job.id = id; });
job.img = Some(img.clone()); job.id = id;
job._mem = Some(client_mem.clone()); job.img = Some(img.clone());
job.work.src = client_mem.ptr() as _; job._mem = Some(client_mem.clone());
job.work.dst = staging.allocation.mem.unwrap(); job.work.src = ptr as _;
job.work.width = img.width as _; job.work.dst = staging.allocation.mem.unwrap();
job.work.stride = img.stride as _; job.work.width = img.width as _;
job.work.bpp = self.shm_info.bpp as _; job.work.stride = img.stride as _;
job.work.rects.clear(); job.work.bpp = self.shm_info.bpp as _;
for copy in copies { job.work.rects.clear();
job.work.rects.push( for copy in copies {
Rect::new_sized( job.work.rects.push(
copy.image_offset.x as _, Rect::new_sized(
copy.image_offset.y as _, copy.image_offset.x as _,
copy.image_extent.width as _, copy.image_offset.y as _,
copy.image_extent.height as _, copy.image_extent.width as _,
) copy.image_extent.height as _,
.unwrap(), )
); .unwrap(),
);
}
pending = data.cpu.submit(job);
} }
pending = data.cpu.submit(job); ShmMemoryBacking::Fd(fd, offset) => {
} else { let mut min_offset = client_mem.len() as u64;
let mut min_offset = client_mem.ptr().len() as u64; let mut max_offset = 0;
let mut max_offset = 0; for copy in copies {
for copy in copies { min_offset = min_offset.min(copy.buffer_offset);
min_offset = min_offset.min(copy.buffer_offset); let len = img.stride * (copy.image_extent.height - 1)
let len = img.stride * (copy.image_extent.height - 1) + copy.image_extent.width * self.shm_info.bpp;
+ copy.image_extent.width * self.shm_info.bpp; max_offset = max_offset.max(copy.buffer_offset + len as u64);
max_offset = max_offset.max(copy.buffer_offset + len as u64); }
let mut job = data.io_job.take().unwrap_or_else(|| {
Box::new(IoUploadJob {
img: None,
id,
_mem: None,
work: unsafe { ReadWriteWork::new() },
fd: None,
})
});
job.id = id;
job.img = Some(img.clone());
job._mem = Some(client_mem.clone());
job.fd = Some(fd.clone());
unsafe {
let config = job.work.config();
config.fd = fd.raw();
config.offset = offset + min_offset as usize;
config.ptr = staging.allocation.mem.unwrap().add(min_offset as _);
config.len = max_offset.saturating_sub(min_offset) as usize;
config.write = false;
}
pending = data.cpu.submit(job);
} }
let mut job = data.io_job.take().unwrap_or_else(|| {
Box::new(IoUploadJob {
img: None,
id,
_mem: None,
work: unsafe { ReadWriteWork::new() },
fd: None,
})
});
job.id = id;
job.img = Some(img.clone());
job._mem = Some(client_mem.clone());
job.fd = Some(client_mem.pool().fd().clone());
unsafe {
let config = job.work.config();
config.fd = client_mem.pool().fd().raw();
config.offset = client_mem.offset() + min_offset as usize;
config.ptr = staging.allocation.mem.unwrap().add(min_offset as _);
config.len = max_offset.saturating_sub(min_offset) as usize;
config.write = false;
}
pending = data.cpu.submit(job);
} }
img.renderer.pending_cpu_jobs.set(id, pending); img.renderer.pending_cpu_jobs.set(id, pending);
@ -653,7 +657,7 @@ impl VulkanShmImage {
pub(super) struct IoUploadJob { pub(super) struct IoUploadJob {
img: Option<Rc<VulkanImage>>, img: Option<Rc<VulkanImage>>,
id: u64, id: u64,
_mem: Option<Rc<ClientMemOffset>>, _mem: Option<Rc<dyn ShmMemory>>,
fd: Option<Rc<OwnedFd>>, fd: Option<Rc<OwnedFd>>,
work: ReadWriteWork, work: ReadWriteWork,
} }
@ -661,7 +665,7 @@ pub(super) struct IoUploadJob {
pub(super) struct CopyUploadJob { pub(super) struct CopyUploadJob {
img: Option<Rc<VulkanImage>>, img: Option<Rc<VulkanImage>>,
id: u64, id: u64,
_mem: Option<Rc<ClientMemOffset>>, _mem: Option<Rc<dyn ShmMemory>>,
work: ImgCopyWork, work: ImgCopyWork,
} }

View file

@ -460,7 +460,7 @@ fn schedule_async_upload(
} }
}; };
back_tex back_tex
.async_upload(node_ref.clone(), mem, back.damage.get()) .async_upload(node_ref.clone(), mem.clone(), back.damage.get())
.map_err(WlSurfaceError::PrepareAsyncUpload) .map_err(WlSurfaceError::PrepareAsyncUpload)
} }

View file

@ -1,14 +1,13 @@
use { use {
crate::{ crate::{
allocator::{Allocator, AllocatorError, BufferObject, BufferUsage}, allocator::{Allocator, AllocatorError, BufferObject, BufferUsage},
clientmem::{ClientMemError, ClientMemOffset},
cpu_worker::CpuWorker, cpu_worker::CpuWorker,
format::{Format, ARGB8888, XRGB8888}, format::{Format, ARGB8888, XRGB8888},
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,
GfxTexture, GfxWriteModifier, PendingShmUpload, ReleaseSync, ResetStatus, GfxTexture, GfxWriteModifier, PendingShmUpload, ReleaseSync, ResetStatus,
ShmGfxTexture, SyncFile, ShmGfxTexture, ShmMemory, SyncFile,
}, },
rect::{Rect, Region}, rect::{Rect, Region},
theme::Color, theme::Color,
@ -20,6 +19,7 @@ use {
std::{ std::{
any::Any, any::Any,
cell::{Cell, RefCell}, cell::{Cell, RefCell},
error::Error,
ffi::CString, ffi::CString,
fmt::{Debug, Formatter}, fmt::{Debug, Formatter},
ops::Deref, ops::Deref,
@ -36,7 +36,7 @@ enum TestGfxError {
#[error("Could not import dmabuf")] #[error("Could not import dmabuf")]
ImportDmaBuf(#[source] AllocatorError), ImportDmaBuf(#[source] AllocatorError),
#[error("Could not access the client memory")] #[error("Could not access the client memory")]
AccessFailed(#[source] ClientMemError), AccessFailed(#[source] Box<dyn Error + Sync + Send>),
} }
impl From<TestGfxError> for GfxError { impl From<TestGfxError> for GfxError {
@ -336,12 +336,15 @@ impl AsyncShmGfxTexture for TestGfxImage {
fn async_upload( fn async_upload(
self: Rc<Self>, self: Rc<Self>,
_callback: Rc<dyn AsyncShmGfxTextureCallback>, _callback: Rc<dyn AsyncShmGfxTextureCallback>,
mem: &Rc<ClientMemOffset>, mem: Rc<dyn ShmMemory>,
damage: Region, _damage: Region,
) -> Result<Option<PendingShmUpload>, GfxError> { ) -> Result<Option<PendingShmUpload>, GfxError> {
mem.access(|d| self.clone().sync_upload(d, damage)) let mut res = Ok(());
.map_err(TestGfxError::AccessFailed)??; mem.access(&mut |d| {
Ok(None) res = self.clone().sync_upload(d, Region::default());
})
.map_err(TestGfxError::AccessFailed)?;
res.map(|_| None)
} }
fn sync_upload(self: Rc<Self>, mem: &[Cell<u8>], _damage: Region) -> Result<(), GfxError> { fn sync_upload(self: Rc<Self>, mem: &[Cell<u8>], _damage: Region) -> Result<(), GfxError> {