Merge pull request #257 from mahkoh/jorth/transfer-thread
vulkan: implement async shm uploads
This commit is contained in:
commit
02cfdc4be1
57 changed files with 2800 additions and 318 deletions
|
|
@ -13,7 +13,7 @@ use {
|
|||
|
||||
#[derive(Debug, Error)]
|
||||
#[error(transparent)]
|
||||
pub struct AllocatorError(#[from] pub Box<dyn Error>);
|
||||
pub struct AllocatorError(#[from] pub Box<dyn Error + Send>);
|
||||
|
||||
bitflags! {
|
||||
BufferUsage: u32;
|
||||
|
|
|
|||
|
|
@ -353,8 +353,10 @@ impl Input {
|
|||
async fn handle_keymap(&self, input: JayInputId) -> Vec<u8> {
|
||||
let data = Rc::new(RefCell::new(Vec::new()));
|
||||
jay_input::Keymap::handle(&self.tc, input, data.clone(), |d, map| {
|
||||
let mem = Rc::new(ClientMem::new(map.keymap.raw(), map.keymap_len as _, true).unwrap())
|
||||
.offset(0);
|
||||
let mem = Rc::new(
|
||||
ClientMem::new(&map.keymap, map.keymap_len as _, true, None, None).unwrap(),
|
||||
)
|
||||
.offset(0);
|
||||
mem.read(d.borrow_mut().deref_mut()).unwrap();
|
||||
});
|
||||
self.tc.round_trip().await;
|
||||
|
|
|
|||
102
src/clientmem.rs
102
src/clientmem.rs
|
|
@ -1,14 +1,21 @@
|
|||
use {
|
||||
crate::utils::vec_ext::VecExt,
|
||||
crate::{
|
||||
client::Client,
|
||||
cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker},
|
||||
utils::vec_ext::VecExt,
|
||||
},
|
||||
std::{
|
||||
cell::Cell,
|
||||
mem::MaybeUninit,
|
||||
mem::{ManuallyDrop, MaybeUninit},
|
||||
ptr,
|
||||
rc::Rc,
|
||||
sync::atomic::{compiler_fence, Ordering},
|
||||
},
|
||||
thiserror::Error,
|
||||
uapi::{c, c::raise},
|
||||
uapi::{
|
||||
c::{self, raise},
|
||||
OwnedFd,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
@ -22,27 +29,45 @@ pub enum ClientMemError {
|
|||
}
|
||||
|
||||
pub struct ClientMem {
|
||||
fd: ManuallyDrop<Rc<OwnedFd>>,
|
||||
failed: Cell<bool>,
|
||||
sigbus_impossible: bool,
|
||||
data: *const [Cell<u8>],
|
||||
cpu: Option<Rc<CpuWorker>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ClientMemOffset {
|
||||
mem: Rc<ClientMem>,
|
||||
offset: usize,
|
||||
data: *const [Cell<u8>],
|
||||
}
|
||||
|
||||
impl ClientMem {
|
||||
pub fn new(fd: i32, len: usize, read_only: bool) -> Result<Self, ClientMemError> {
|
||||
pub fn new(
|
||||
fd: &Rc<OwnedFd>,
|
||||
len: usize,
|
||||
read_only: bool,
|
||||
client: Option<&Client>,
|
||||
cpu: Option<&Rc<CpuWorker>>,
|
||||
) -> Result<Self, ClientMemError> {
|
||||
let mut sigbus_impossible = false;
|
||||
if let Ok(seals) = uapi::fcntl_get_seals(fd) {
|
||||
if let Ok(seals) = uapi::fcntl_get_seals(fd.raw()) {
|
||||
if seals & c::F_SEAL_SHRINK != 0 {
|
||||
if let Ok(stat) = uapi::fstat(fd) {
|
||||
if let Ok(stat) = uapi::fstat(fd.raw()) {
|
||||
sigbus_impossible = stat.st_size as u64 >= len as u64;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !sigbus_impossible {
|
||||
if let Some(client) = client {
|
||||
log::debug!(
|
||||
"Client {} ({}) has created a shm buffer that might cause SIGBUS",
|
||||
client.pid_info.comm,
|
||||
client.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
let data = if len == 0 {
|
||||
&mut [][..]
|
||||
} else {
|
||||
|
|
@ -51,7 +76,7 @@ impl ClientMem {
|
|||
false => c::PROT_READ | c::PROT_WRITE,
|
||||
};
|
||||
unsafe {
|
||||
let data = c::mmap64(ptr::null_mut(), len, prot, c::MAP_SHARED, fd, 0);
|
||||
let data = c::mmap64(ptr::null_mut(), len, prot, c::MAP_SHARED, fd.raw(), 0);
|
||||
if data == c::MAP_FAILED {
|
||||
return Err(ClientMemError::MmapFailed(uapi::Errno::default().into()));
|
||||
}
|
||||
|
|
@ -59,9 +84,11 @@ impl ClientMem {
|
|||
}
|
||||
};
|
||||
Ok(Self {
|
||||
fd: ManuallyDrop::new(fd.clone()),
|
||||
failed: Cell::new(false),
|
||||
sigbus_impossible,
|
||||
data,
|
||||
cpu: cpu.cloned(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -73,12 +100,33 @@ impl ClientMem {
|
|||
let mem = unsafe { &*self.data };
|
||||
ClientMemOffset {
|
||||
mem: self.clone(),
|
||||
offset,
|
||||
data: &mem[offset..],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fd(&self) -> &Rc<OwnedFd> {
|
||||
&self.fd
|
||||
}
|
||||
|
||||
pub fn sigbus_impossible(&self) -> bool {
|
||||
self.sigbus_impossible
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientMemOffset {
|
||||
pub fn pool(&self) -> &ClientMem {
|
||||
&self.mem
|
||||
}
|
||||
|
||||
pub fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
|
||||
pub fn ptr(&self) -> *const [Cell<u8>] {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn access<T, F: FnOnce(&[Cell<u8>]) -> T>(&self, f: F) -> Result<T, ClientMemError> {
|
||||
unsafe {
|
||||
if self.mem.sigbus_impossible {
|
||||
|
|
@ -114,8 +162,17 @@ impl ClientMemOffset {
|
|||
|
||||
impl Drop for ClientMem {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
c::munmap(self.data as _, self.len());
|
||||
let fd = unsafe { ManuallyDrop::take(&mut self.fd) };
|
||||
if let Some(cpu) = &self.cpu {
|
||||
let pending = cpu.submit(Box::new(CloseMemWork {
|
||||
fd: Rc::try_unwrap(fd).ok(),
|
||||
data: self.data,
|
||||
}));
|
||||
pending.detach();
|
||||
} else {
|
||||
unsafe {
|
||||
c::munmap(self.data as _, self.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -178,3 +235,30 @@ pub fn init() -> Result<(), ClientMemError> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CloseMemWork {
|
||||
fd: Option<OwnedFd>,
|
||||
data: *const [Cell<u8>],
|
||||
}
|
||||
|
||||
unsafe impl Send for CloseMemWork {}
|
||||
|
||||
impl CpuJob for CloseMemWork {
|
||||
fn work(&mut self) -> &mut dyn CpuWork {
|
||||
self
|
||||
}
|
||||
|
||||
fn completed(self: Box<Self>) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuWork for CloseMemWork {
|
||||
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
||||
self.fd.take();
|
||||
unsafe {
|
||||
c::munmap(self.data as _, self.data.len());
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use {
|
|||
client::{ClientId, Clients},
|
||||
clientmem::{self, ClientMemError},
|
||||
config::ConfigProxy,
|
||||
cpu_worker::{CpuWorker, CpuWorkerError},
|
||||
damage::{visualize_damage, DamageVisualizer},
|
||||
dbus::Dbus,
|
||||
ei::ei_client::EiClients,
|
||||
|
|
@ -107,6 +108,8 @@ pub enum CompositorError {
|
|||
WheelError(#[from] WheelError),
|
||||
#[error("Could not create an io-uring")]
|
||||
IoUringError(#[from] IoUringError),
|
||||
#[error("Could not create cpu worker")]
|
||||
CpuWorkerError(#[from] CpuWorkerError),
|
||||
}
|
||||
|
||||
pub const WAYLAND_DISPLAY: &str = "WAYLAND_DISPLAY";
|
||||
|
|
@ -143,6 +146,7 @@ fn start_compositor2(
|
|||
let node_ids = NodeIds::default();
|
||||
let scales = RefCounted::default();
|
||||
scales.add(Scale::from_int(1));
|
||||
let cpu_worker = Rc::new(CpuWorker::new(&ring, &engine)?);
|
||||
let state = Rc::new(State {
|
||||
xkb_ctx,
|
||||
backend: CloneCell::new(Rc::new(DummyBackend)),
|
||||
|
|
@ -258,6 +262,7 @@ fn start_compositor2(
|
|||
enable_ei_acceptor: Default::default(),
|
||||
ei_clients: EiClients::new(),
|
||||
slow_ei_clients: Default::default(),
|
||||
cpu_worker,
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
|
|||
430
src/cpu_worker.rs
Normal file
430
src/cpu_worker.rs
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
pub mod jobs;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
io_uring::IoUring,
|
||||
utils::{
|
||||
buf::TypedBuf, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, oserror::OsError,
|
||||
ptr_ext::MutPtrExt, queue::AsyncQueue, stack::Stack,
|
||||
},
|
||||
},
|
||||
parking_lot::Mutex,
|
||||
std::{
|
||||
any::Any,
|
||||
cell::{Cell, RefCell},
|
||||
collections::VecDeque,
|
||||
mem,
|
||||
ptr::NonNull,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
thread,
|
||||
},
|
||||
thiserror::Error,
|
||||
uapi::{c, OwnedFd},
|
||||
};
|
||||
|
||||
pub trait CpuJob {
|
||||
fn work(&mut self) -> &mut dyn CpuWork;
|
||||
fn completed(self: Box<Self>);
|
||||
}
|
||||
|
||||
pub trait CpuWork: Send {
|
||||
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>>;
|
||||
|
||||
fn cancel_async(&mut self, ring: &Rc<IoUring>) {
|
||||
let _ = ring;
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn async_work_done(&mut self, work: Box<dyn AsyncCpuWork>) {
|
||||
let _ = work;
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AsyncCpuWork {
|
||||
fn run(
|
||||
self: Box<Self>,
|
||||
eng: &Rc<AsyncEngine>,
|
||||
ring: &Rc<IoUring>,
|
||||
completion: WorkCompletion,
|
||||
) -> SpawnedFuture<CompletedWork>;
|
||||
|
||||
fn into_any(self: Box<Self>) -> Box<dyn Any>;
|
||||
}
|
||||
|
||||
pub struct WorkCompletion {
|
||||
worker: Rc<Worker>,
|
||||
id: CpuJobId,
|
||||
}
|
||||
|
||||
pub struct CompletedWork(());
|
||||
|
||||
impl WorkCompletion {
|
||||
pub fn complete(self, work: Box<dyn AsyncCpuWork>) -> CompletedWork {
|
||||
let job = self.worker.async_jobs.remove(&self.id).unwrap();
|
||||
unsafe {
|
||||
job.work.deref_mut().async_work_done(work);
|
||||
}
|
||||
self.worker.send_completion(self.id);
|
||||
CompletedWork(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CpuWorker {
|
||||
data: Rc<CpuWorkerData>,
|
||||
_completions_listener: SpawnedFuture<()>,
|
||||
_job_enqueuer: SpawnedFuture<()>,
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct PendingJob {
|
||||
id: CpuJobId,
|
||||
thread_data: Rc<CpuWorkerData>,
|
||||
job_data: Rc<PendingJobData>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
|
||||
enum PendingJobState {
|
||||
#[default]
|
||||
Waiting,
|
||||
Abandoned,
|
||||
Completed,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PendingJobData {
|
||||
job: Cell<Option<NonNull<dyn CpuJob>>>,
|
||||
state: Cell<PendingJobState>,
|
||||
}
|
||||
|
||||
enum Job {
|
||||
New {
|
||||
id: CpuJobId,
|
||||
work: *mut dyn CpuWork,
|
||||
},
|
||||
Cancel {
|
||||
id: CpuJobId,
|
||||
},
|
||||
}
|
||||
|
||||
unsafe impl Send for Job {}
|
||||
|
||||
struct CpuWorkerData {
|
||||
next: CpuJobIds,
|
||||
jobs_to_enqueue: AsyncQueue<Job>,
|
||||
new_jobs: Arc<Mutex<VecDeque<Job>>>,
|
||||
have_new_jobs: Rc<OwnedFd>,
|
||||
completed_jobs_remote: Arc<Mutex<VecDeque<CpuJobId>>>,
|
||||
completed_jobs_local: RefCell<VecDeque<CpuJobId>>,
|
||||
have_completed_jobs: Rc<OwnedFd>,
|
||||
pending_jobs: CopyHashMap<CpuJobId, Rc<PendingJobData>>,
|
||||
ring: Rc<IoUring>,
|
||||
_stop: OwnedFd,
|
||||
pending_job_data_cache: Stack<Rc<PendingJobData>>,
|
||||
}
|
||||
|
||||
linear_ids!(CpuJobIds, CpuJobId, u64);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CpuWorkerError {
|
||||
#[error("Could not create a pipe")]
|
||||
Pipe(#[source] OsError),
|
||||
#[error("Could not create an eventfd")]
|
||||
EventFd(#[source] OsError),
|
||||
#[error("Could not dup an eventfd")]
|
||||
Dup(#[source] OsError),
|
||||
}
|
||||
|
||||
impl PendingJob {
|
||||
pub fn detach(self) {
|
||||
match self.job_data.state.get() {
|
||||
PendingJobState::Waiting => {
|
||||
self.job_data.state.set(PendingJobState::Abandoned);
|
||||
}
|
||||
PendingJobState::Abandoned => {
|
||||
unreachable!();
|
||||
}
|
||||
PendingJobState::Completed => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CpuWorker {
|
||||
fn drop(&mut self) {
|
||||
self.data.do_equeue_jobs();
|
||||
if self.data.pending_jobs.is_not_empty() {
|
||||
log::warn!("CpuWorker dropped with pending jobs. Completed jobs will not be triggered.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PendingJob {
|
||||
fn drop(&mut self) {
|
||||
match self.job_data.state.get() {
|
||||
PendingJobState::Waiting => {
|
||||
log::warn!("PendingJob dropped before completion. Blocking.");
|
||||
let data = &self.thread_data;
|
||||
let id = self.id;
|
||||
self.job_data.state.set(PendingJobState::Abandoned);
|
||||
data.jobs_to_enqueue.push(Job::Cancel { id });
|
||||
data.do_equeue_jobs();
|
||||
let mut buf = 0u64;
|
||||
while data.pending_jobs.contains(&id) {
|
||||
if let Err(e) = uapi::read(data.have_completed_jobs.raw(), &mut buf) {
|
||||
panic!("Could not wait for job completions: {}", ErrorFmt(e));
|
||||
}
|
||||
data.dispatch_completions();
|
||||
}
|
||||
}
|
||||
PendingJobState::Abandoned => {}
|
||||
PendingJobState::Completed => {
|
||||
self.thread_data
|
||||
.pending_job_data_cache
|
||||
.push(self.job_data.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuWorkerData {
|
||||
async fn wait_for_completions(self: Rc<Self>) {
|
||||
let mut buf = TypedBuf::<u64>::new();
|
||||
loop {
|
||||
if let Err(e) = self.ring.read(&self.have_completed_jobs, buf.buf()).await {
|
||||
log::error!("Could not wait for job completions: {}", ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
self.dispatch_completions();
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch_completions(&self) {
|
||||
let completions = &mut *self.completed_jobs_local.borrow_mut();
|
||||
mem::swap(completions, &mut *self.completed_jobs_remote.lock());
|
||||
while let Some(id) = completions.pop_front() {
|
||||
let job_data = self.pending_jobs.remove(&id).unwrap();
|
||||
let job = job_data.job.take().unwrap();
|
||||
let job = unsafe { Box::from_raw(job.as_ptr()) };
|
||||
match job_data.state.get() {
|
||||
PendingJobState::Waiting => {
|
||||
job_data.state.set(PendingJobState::Completed);
|
||||
job.completed();
|
||||
}
|
||||
PendingJobState::Abandoned => {
|
||||
self.pending_job_data_cache.push(job_data);
|
||||
}
|
||||
PendingJobState::Completed => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn equeue_jobs(self: Rc<Self>) {
|
||||
loop {
|
||||
self.jobs_to_enqueue.non_empty().await;
|
||||
self.do_equeue_jobs();
|
||||
}
|
||||
}
|
||||
|
||||
fn do_equeue_jobs(&self) {
|
||||
self.jobs_to_enqueue.move_to(&mut self.new_jobs.lock());
|
||||
if let Err(e) = uapi::eventfd_write(self.have_new_jobs.raw(), 1) {
|
||||
panic!("Could not signal eventfd: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuWorker {
|
||||
pub fn new(ring: &Rc<IoUring>, eng: &Rc<AsyncEngine>) -> Result<Self, CpuWorkerError> {
|
||||
let new_jobs: Arc<Mutex<VecDeque<Job>>> = Default::default();
|
||||
let completed_jobs: Arc<Mutex<VecDeque<CpuJobId>>> = Default::default();
|
||||
let (stop_read, stop_write) =
|
||||
uapi::pipe2(c::O_CLOEXEC).map_err(|e| CpuWorkerError::Pipe(e.into()))?;
|
||||
let have_new_jobs =
|
||||
uapi::eventfd(0, c::EFD_CLOEXEC).map_err(|e| CpuWorkerError::EventFd(e.into()))?;
|
||||
let have_completed_jobs =
|
||||
uapi::eventfd(0, c::EFD_CLOEXEC).map_err(|e| CpuWorkerError::EventFd(e.into()))?;
|
||||
thread::Builder::new()
|
||||
.name("cpu worker".to_string())
|
||||
.spawn({
|
||||
let new_jobs = new_jobs.clone();
|
||||
let completed_jobs = completed_jobs.clone();
|
||||
let have_new_jobs = uapi::fcntl_dupfd_cloexec(have_new_jobs.raw(), 0)
|
||||
.map_err(|e| CpuWorkerError::Dup(e.into()))?;
|
||||
let have_completed_jobs = uapi::fcntl_dupfd_cloexec(have_completed_jobs.raw(), 0)
|
||||
.map_err(|e| CpuWorkerError::Dup(e.into()))?;
|
||||
move || {
|
||||
work(
|
||||
new_jobs,
|
||||
completed_jobs,
|
||||
stop_write,
|
||||
have_new_jobs,
|
||||
have_completed_jobs,
|
||||
)
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
let data = Rc::new(CpuWorkerData {
|
||||
next: Default::default(),
|
||||
jobs_to_enqueue: Default::default(),
|
||||
new_jobs,
|
||||
have_new_jobs: Rc::new(have_new_jobs),
|
||||
completed_jobs_remote: completed_jobs,
|
||||
completed_jobs_local: Default::default(),
|
||||
have_completed_jobs: Rc::new(have_completed_jobs),
|
||||
pending_jobs: Default::default(),
|
||||
ring: ring.clone(),
|
||||
_stop: stop_read,
|
||||
pending_job_data_cache: Default::default(),
|
||||
});
|
||||
Ok(Self {
|
||||
_completions_listener: eng.spawn(data.clone().wait_for_completions()),
|
||||
_job_enqueuer: eng.spawn(data.clone().equeue_jobs()),
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn submit(&self, job: Box<dyn CpuJob>) -> PendingJob {
|
||||
let mut job = NonNull::from(Box::leak(job));
|
||||
let id = self.data.next.next();
|
||||
self.data.jobs_to_enqueue.push(Job::New {
|
||||
id,
|
||||
work: unsafe { job.as_mut().work() },
|
||||
});
|
||||
let job_data = self.data.pending_job_data_cache.pop().unwrap_or_default();
|
||||
job_data.job.set(Some(job));
|
||||
job_data.state.set(PendingJobState::Waiting);
|
||||
self.data.pending_jobs.set(id, job_data.clone());
|
||||
PendingJob {
|
||||
id,
|
||||
thread_data: self.data.clone(),
|
||||
job_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn work(
|
||||
new_jobs: Arc<Mutex<VecDeque<Job>>>,
|
||||
completed_jobs: Arc<Mutex<VecDeque<CpuJobId>>>,
|
||||
stop: OwnedFd,
|
||||
have_new_jobs: OwnedFd,
|
||||
have_completed_jobs: OwnedFd,
|
||||
) {
|
||||
let eng = AsyncEngine::new();
|
||||
let ring = IoUring::new(&eng, 32).unwrap();
|
||||
let worker = Rc::new(Worker {
|
||||
eng,
|
||||
ring,
|
||||
completed_jobs,
|
||||
have_completed_jobs,
|
||||
async_jobs: Default::default(),
|
||||
stopped: Cell::new(false),
|
||||
});
|
||||
let _stop_listener = worker.eng.spawn(worker.clone().handle_stop(stop));
|
||||
let _new_job_listener = worker
|
||||
.eng
|
||||
.spawn(worker.clone().handle_new_jobs(new_jobs, have_new_jobs));
|
||||
if let Err(e) = worker.ring.run() {
|
||||
panic!("io_uring failed: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
|
||||
struct Worker {
|
||||
eng: Rc<AsyncEngine>,
|
||||
ring: Rc<IoUring>,
|
||||
completed_jobs: Arc<Mutex<VecDeque<CpuJobId>>>,
|
||||
have_completed_jobs: OwnedFd,
|
||||
async_jobs: CopyHashMap<CpuJobId, AsyncJob>,
|
||||
stopped: Cell<bool>,
|
||||
}
|
||||
|
||||
struct AsyncJob {
|
||||
_future: SpawnedFuture<CompletedWork>,
|
||||
work: *mut dyn CpuWork,
|
||||
}
|
||||
|
||||
impl Worker {
|
||||
async fn handle_stop(self: Rc<Self>, stop: OwnedFd) {
|
||||
let stop = Rc::new(stop);
|
||||
if let Err(e) = self.ring.poll(&stop, 0).await {
|
||||
log::error!(
|
||||
"Could not wait for stop fd to become readable: {}",
|
||||
ErrorFmt(e)
|
||||
);
|
||||
} else {
|
||||
assert!(self.async_jobs.is_empty());
|
||||
self.stopped.set(true);
|
||||
self.ring.stop();
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_new_jobs(
|
||||
self: Rc<Self>,
|
||||
jobs_remote: Arc<Mutex<VecDeque<Job>>>,
|
||||
new_jobs: OwnedFd,
|
||||
) {
|
||||
let mut buf = TypedBuf::<u64>::new();
|
||||
let new_jobs = Rc::new(new_jobs);
|
||||
let mut jobs = VecDeque::new();
|
||||
loop {
|
||||
if let Err(e) = self.ring.read(&new_jobs, buf.buf()).await {
|
||||
if self.stopped.get() {
|
||||
return;
|
||||
}
|
||||
panic!(
|
||||
"Could not wait for new jobs fd to be signaled: {}",
|
||||
ErrorFmt(e),
|
||||
);
|
||||
}
|
||||
mem::swap(&mut jobs, &mut *jobs_remote.lock());
|
||||
while let Some(job) = jobs.pop_front() {
|
||||
self.handle_new_job(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_new_job(self: &Rc<Self>, job: Job) {
|
||||
match job {
|
||||
Job::Cancel { id } => {
|
||||
let mut jobs = self.async_jobs.lock();
|
||||
if let Some(job) = jobs.get_mut(&id) {
|
||||
unsafe {
|
||||
job.work.deref_mut().cancel_async(&self.ring);
|
||||
}
|
||||
}
|
||||
}
|
||||
Job::New { id, work } => match unsafe { work.deref_mut() }.run() {
|
||||
None => {
|
||||
self.send_completion(id);
|
||||
return;
|
||||
}
|
||||
Some(w) => {
|
||||
let completion = WorkCompletion {
|
||||
worker: self.clone(),
|
||||
id,
|
||||
};
|
||||
let future = w.run(&self.eng, &self.ring, completion);
|
||||
self.async_jobs.set(
|
||||
id,
|
||||
AsyncJob {
|
||||
_future: future,
|
||||
work,
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn send_completion(&self, id: CpuJobId) {
|
||||
self.completed_jobs.lock().push_back(id);
|
||||
if let Err(e) = uapi::eventfd_write(self.have_completed_jobs.raw(), 1) {
|
||||
panic!("Could not signal job completion: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
2
src/cpu_worker/jobs.rs
Normal file
2
src/cpu_worker/jobs.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
pub mod img_copy;
|
||||
pub mod read_write;
|
||||
62
src/cpu_worker/jobs/img_copy.rs
Normal file
62
src/cpu_worker/jobs/img_copy.rs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
use {
|
||||
crate::{
|
||||
cpu_worker::{AsyncCpuWork, CpuWork},
|
||||
rect::Rect,
|
||||
},
|
||||
std::ptr,
|
||||
};
|
||||
|
||||
#[expect(clippy::manual_non_exhaustive)]
|
||||
pub struct ImgCopyWork {
|
||||
pub src: *mut u8,
|
||||
pub dst: *mut u8,
|
||||
pub width: i32,
|
||||
pub stride: i32,
|
||||
pub bpp: i32,
|
||||
pub rects: Vec<Rect>,
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
unsafe impl Send for ImgCopyWork {}
|
||||
|
||||
impl ImgCopyWork {
|
||||
pub unsafe fn new() -> Self {
|
||||
Self {
|
||||
src: ptr::null_mut(),
|
||||
dst: ptr::null_mut(),
|
||||
width: 0,
|
||||
stride: 0,
|
||||
bpp: 0,
|
||||
rects: vec![],
|
||||
_priv: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuWork for ImgCopyWork {
|
||||
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
||||
for rect in &self.rects {
|
||||
let mut offset = rect.y1() * self.stride + rect.x1() * self.bpp;
|
||||
if rect.width() == self.width {
|
||||
let offset = offset as usize;
|
||||
let len = rect.height() * self.stride;
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(self.src.add(offset), self.dst.add(offset), len as _);
|
||||
}
|
||||
} else {
|
||||
let len = rect.width() * self.bpp;
|
||||
for _ in 0..rect.height() {
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(
|
||||
self.src.add(offset as _),
|
||||
self.dst.add(offset as _),
|
||||
len as _,
|
||||
);
|
||||
}
|
||||
offset += self.stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
151
src/cpu_worker/jobs/read_write.rs
Normal file
151
src/cpu_worker/jobs/read_write.rs
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
cpu_worker::{AsyncCpuWork, CompletedWork, CpuWork, WorkCompletion},
|
||||
io_uring::{IoUring, IoUringError, IoUringTaskId},
|
||||
},
|
||||
std::{
|
||||
any::Any,
|
||||
ptr,
|
||||
rc::Rc,
|
||||
slice,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicU64, Ordering::Relaxed},
|
||||
Arc,
|
||||
},
|
||||
},
|
||||
thiserror::Error,
|
||||
uapi::{c, Fd},
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ReadWriteJobError {
|
||||
#[error("An io_uring error occurred")]
|
||||
IoUring(#[source] IoUringError),
|
||||
#[error("The job was cancelled")]
|
||||
Cancelled,
|
||||
#[error("Tried to operate outside the bounds of the file descriptor")]
|
||||
OutOfBounds,
|
||||
}
|
||||
|
||||
pub struct ReadWriteWork {
|
||||
cancel: Arc<CancelState>,
|
||||
config: Option<Box<ReadWriteWorkConfig>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for ReadWriteWork {}
|
||||
|
||||
impl ReadWriteWork {
|
||||
pub unsafe fn new() -> Self {
|
||||
let cancel = Arc::new(CancelState::default());
|
||||
ReadWriteWork {
|
||||
cancel: cancel.clone(),
|
||||
config: Some(Box::new(ReadWriteWorkConfig {
|
||||
fd: -1,
|
||||
offset: 0,
|
||||
ptr: ptr::null_mut(),
|
||||
len: 0,
|
||||
write: false,
|
||||
cancel,
|
||||
result: None,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config(&mut self) -> &mut ReadWriteWorkConfig {
|
||||
self.config.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReadWriteWorkConfig {
|
||||
pub fd: c::c_int,
|
||||
pub offset: usize,
|
||||
pub ptr: *mut u8,
|
||||
pub len: usize,
|
||||
pub write: bool,
|
||||
pub result: Option<Result<(), ReadWriteJobError>>,
|
||||
cancel: Arc<CancelState>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CancelState {
|
||||
cancelled: AtomicBool,
|
||||
cancel_id: AtomicU64,
|
||||
}
|
||||
|
||||
impl CpuWork for ReadWriteWork {
|
||||
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
||||
self.cancel.cancelled.store(false, Relaxed);
|
||||
self.cancel.cancel_id.store(0, Relaxed);
|
||||
self.config.take().map(|b| b as _)
|
||||
}
|
||||
|
||||
fn cancel_async(&mut self, ring: &Rc<IoUring>) {
|
||||
self.cancel.cancelled.store(true, Relaxed);
|
||||
let id = self.cancel.cancel_id.load(Relaxed);
|
||||
if id != 0 {
|
||||
ring.cancel(IoUringTaskId::from_raw(id));
|
||||
}
|
||||
}
|
||||
|
||||
fn async_work_done(&mut self, work: Box<dyn AsyncCpuWork>) {
|
||||
let work = work.into_any().downcast().unwrap();
|
||||
self.config = Some(work);
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncCpuWork for ReadWriteWorkConfig {
|
||||
fn run(
|
||||
mut self: Box<Self>,
|
||||
eng: &Rc<AsyncEngine>,
|
||||
ring: &Rc<IoUring>,
|
||||
completion: WorkCompletion,
|
||||
) -> SpawnedFuture<CompletedWork> {
|
||||
let ring = ring.clone();
|
||||
eng.spawn(async move {
|
||||
let res = loop {
|
||||
if self.cancel.cancelled.load(Relaxed) {
|
||||
break Err(ReadWriteJobError::Cancelled);
|
||||
}
|
||||
if self.len == 0 {
|
||||
break Ok(());
|
||||
};
|
||||
let res = if self.write {
|
||||
ring.write_no_cancel(
|
||||
Fd::new(self.fd),
|
||||
self.offset,
|
||||
unsafe { slice::from_raw_parts(self.ptr, self.len) },
|
||||
None,
|
||||
|id| self.cancel.cancel_id.store(id.raw(), Relaxed),
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
ring.read_no_cancel(
|
||||
Fd::new(self.fd),
|
||||
self.offset,
|
||||
unsafe { slice::from_raw_parts_mut(self.ptr, self.len) },
|
||||
|id| self.cancel.cancel_id.store(id.raw(), Relaxed),
|
||||
)
|
||||
.await
|
||||
};
|
||||
match res {
|
||||
Ok(0) => break Err(ReadWriteJobError::OutOfBounds),
|
||||
Ok(n) => {
|
||||
self.len -= n;
|
||||
self.offset += n;
|
||||
unsafe {
|
||||
self.ptr = self.ptr.add(n);
|
||||
}
|
||||
}
|
||||
Err(e) => break Err(ReadWriteJobError::IoUring(e)),
|
||||
}
|
||||
};
|
||||
self.result = Some(res);
|
||||
completion.complete(self)
|
||||
})
|
||||
}
|
||||
|
||||
fn into_any(self: Box<Self>) -> Box<dyn Any> {
|
||||
self
|
||||
}
|
||||
}
|
||||
117
src/cpu_worker/tests.rs
Normal file
117
src/cpu_worker/tests.rs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
cpu_worker::{AsyncCpuWork, CompletedWork, CpuJob, CpuWork, CpuWorker, WorkCompletion},
|
||||
io_uring::IoUring,
|
||||
utils::asyncevent::AsyncEvent,
|
||||
wheel::Wheel,
|
||||
},
|
||||
std::{any::Any, future::pending, rc::Rc, sync::Arc},
|
||||
uapi::{c::EFD_CLOEXEC, OwnedFd},
|
||||
};
|
||||
|
||||
struct Job {
|
||||
ae: Rc<AsyncEvent>,
|
||||
work: Work,
|
||||
cancel: bool,
|
||||
}
|
||||
struct Work(Arc<OwnedFd>);
|
||||
struct AsyncWork(Arc<OwnedFd>);
|
||||
|
||||
impl CpuJob for Job {
|
||||
fn work(&mut self) -> &mut dyn CpuWork {
|
||||
&mut self.work
|
||||
}
|
||||
|
||||
fn completed(self: Box<Self>) {
|
||||
if self.cancel {
|
||||
unreachable!();
|
||||
} else {
|
||||
self.ae.trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Job {
|
||||
fn drop(&mut self) {
|
||||
if self.cancel {
|
||||
self.ae.trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuWork for Work {
|
||||
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
||||
Some(Box::new(AsyncWork(self.0.clone())))
|
||||
}
|
||||
|
||||
fn cancel_async(&mut self, _ring: &Rc<IoUring>) {
|
||||
uapi::eventfd_write(self.0.raw(), 1).unwrap();
|
||||
}
|
||||
|
||||
fn async_work_done(&mut self, work: Box<dyn AsyncCpuWork>) {
|
||||
let _ = work;
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncCpuWork for AsyncWork {
|
||||
fn run(
|
||||
self: Box<Self>,
|
||||
eng: &Rc<AsyncEngine>,
|
||||
ring: &Rc<IoUring>,
|
||||
completion: WorkCompletion,
|
||||
) -> SpawnedFuture<CompletedWork> {
|
||||
let ring = ring.clone();
|
||||
eng.spawn(async move {
|
||||
let mut buf = [0; 8];
|
||||
let res = ring
|
||||
.read_no_cancel(self.0.borrow(), 0, &mut buf, |_| ())
|
||||
.await;
|
||||
res.unwrap();
|
||||
completion.complete(self)
|
||||
})
|
||||
}
|
||||
|
||||
fn into_any(self: Box<Self>) -> Box<dyn Any> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn run(cancel: bool) {
|
||||
let eng = AsyncEngine::new();
|
||||
let ring = IoUring::new(&eng, 32).unwrap();
|
||||
let ring2 = ring.clone();
|
||||
let wheel = Wheel::new(&eng, &ring).unwrap();
|
||||
let cpu = Rc::new(CpuWorker::new(&ring, &eng).unwrap());
|
||||
let ae = Rc::new(AsyncEvent::default());
|
||||
let eventfd = Arc::new(uapi::eventfd(0, EFD_CLOEXEC).unwrap());
|
||||
let pending_job = cpu.submit(Box::new(Job {
|
||||
ae: ae.clone(),
|
||||
work: Work(eventfd.clone()),
|
||||
cancel,
|
||||
}));
|
||||
let _fut1 = eng.spawn(async move {
|
||||
wheel.timeout(1).await.unwrap();
|
||||
if cancel {
|
||||
drop(pending_job);
|
||||
} else {
|
||||
uapi::eventfd_write(eventfd.raw(), 1).unwrap();
|
||||
pending::<()>().await;
|
||||
}
|
||||
});
|
||||
let _fut2 = eng.spawn(async move {
|
||||
ae.triggered().await;
|
||||
ring2.stop();
|
||||
});
|
||||
ring.run().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel() {
|
||||
run(true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complete() {
|
||||
run(false);
|
||||
}
|
||||
|
|
@ -328,7 +328,8 @@ impl CursorImageScaled {
|
|||
extents: Rect::new_sized(-xhot, -yhot, width, height).unwrap(),
|
||||
tex: ctx
|
||||
.clone()
|
||||
.shmem_texture(None, data, ARGB8888, width, height, width * 4, None)?,
|
||||
.shmem_texture(None, data, ARGB8888, width, height, width * 4, None)?
|
||||
.into_texture(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::Allocator,
|
||||
clientmem::ClientMemOffset,
|
||||
cpu_worker::CpuWorker,
|
||||
cursor::Cursor,
|
||||
damage::DamageVisualizer,
|
||||
fixed::Fixed,
|
||||
format::Format,
|
||||
rect::Rect,
|
||||
rect::{Rect, Region},
|
||||
renderer::{renderer_base::RendererBase, RenderResult, Renderer},
|
||||
scale::Scale,
|
||||
state::State,
|
||||
|
|
@ -531,6 +533,44 @@ pub trait GfxTexture: Debug {
|
|||
fn format(&self) -> &'static Format;
|
||||
}
|
||||
|
||||
pub trait ShmGfxTexture: GfxTexture {
|
||||
fn into_texture(self: Rc<Self>) -> Rc<dyn GfxTexture>;
|
||||
}
|
||||
|
||||
pub trait AsyncShmGfxTextureCallback {
|
||||
fn completed(self: Rc<Self>, res: Result<(), GfxError>);
|
||||
}
|
||||
|
||||
pub trait AsyncShmGfxTextureUploadCancellable {
|
||||
fn cancel(&self, id: u64);
|
||||
}
|
||||
|
||||
pub struct PendingShmUpload {
|
||||
cancel: Rc<dyn AsyncShmGfxTextureUploadCancellable>,
|
||||
id: u64,
|
||||
}
|
||||
|
||||
pub trait AsyncShmGfxTexture: GfxTexture {
|
||||
fn async_upload(
|
||||
self: Rc<Self>,
|
||||
callback: Rc<dyn AsyncShmGfxTextureCallback>,
|
||||
mem: &Rc<ClientMemOffset>,
|
||||
damage: Region,
|
||||
) -> Result<Option<PendingShmUpload>, GfxError>;
|
||||
|
||||
fn sync_upload(self: Rc<Self>, shm: &[Cell<u8>], damage: Region) -> Result<(), GfxError>;
|
||||
|
||||
fn compatible_with(
|
||||
&self,
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
) -> bool;
|
||||
|
||||
fn into_texture(self: Rc<Self>) -> Rc<dyn GfxTexture>;
|
||||
}
|
||||
|
||||
pub trait GfxContext: Debug {
|
||||
fn reset_status(&self) -> Option<ResetStatus>;
|
||||
|
||||
|
|
@ -546,14 +586,23 @@ pub trait GfxContext: Debug {
|
|||
|
||||
fn shmem_texture(
|
||||
self: Rc<Self>,
|
||||
old: Option<Rc<dyn GfxTexture>>,
|
||||
old: Option<Rc<dyn ShmGfxTexture>>,
|
||||
data: &[Cell<u8>],
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
damage: Option<&[Rect]>,
|
||||
) -> Result<Rc<dyn GfxTexture>, GfxError>;
|
||||
) -> Result<Rc<dyn ShmGfxTexture>, GfxError>;
|
||||
|
||||
fn async_shmem_texture(
|
||||
self: Rc<Self>,
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
cpu_worker: &Rc<CpuWorker>,
|
||||
) -> Result<Rc<dyn AsyncShmGfxTexture>, GfxError>;
|
||||
|
||||
fn allocator(&self) -> Rc<dyn Allocator>;
|
||||
|
||||
|
|
@ -588,7 +637,7 @@ pub struct GfxFormat {
|
|||
|
||||
#[derive(Error)]
|
||||
#[error(transparent)]
|
||||
pub struct GfxError(pub Box<dyn Error>);
|
||||
pub struct GfxError(pub Box<dyn Error + Send>);
|
||||
|
||||
impl Debug for GfxError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
|
|
@ -633,3 +682,15 @@ pub fn cross_intersect_formats(
|
|||
}
|
||||
res
|
||||
}
|
||||
|
||||
impl PendingShmUpload {
|
||||
pub fn new(cancel: Rc<dyn AsyncShmGfxTextureUploadCancellable>, id: u64) -> Self {
|
||||
Self { cancel, id }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PendingShmUpload {
|
||||
fn drop(&mut self) {
|
||||
self.cancel.cancel(self.id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ macro_rules! dynload {
|
|||
|
||||
use {
|
||||
crate::{
|
||||
clientmem::ClientMemError,
|
||||
gfx_api::{
|
||||
AcquireSync, CopyTexture, FillRect, GfxApiOpt, GfxContext, GfxError, GfxTexture,
|
||||
ReleaseSync, SyncFile,
|
||||
|
|
@ -196,6 +197,8 @@ enum RenderError {
|
|||
WaitSync,
|
||||
#[error("Buffer format {0} is not supported for shm buffers in OpenGL context")]
|
||||
UnsupportedShmFormat(&'static str),
|
||||
#[error("Could not access the client memory")]
|
||||
AccessFailed(#[source] ClientMemError),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -318,6 +321,10 @@ fn fill_boxes3(ctx: &GlRenderContext, boxes: &[[f32; 2]], color: &Color) {
|
|||
|
||||
fn render_texture(ctx: &GlRenderContext, tex: &CopyTexture) {
|
||||
let texture = tex.tex.as_gl();
|
||||
if !texture.gl.contents_valid.get() {
|
||||
log::error!("Ignoring texture with invalid contents");
|
||||
return;
|
||||
}
|
||||
assert!(rc_eq(&ctx.ctx, &texture.ctx.ctx));
|
||||
let gles = ctx.ctx.dpy.gles;
|
||||
unsafe {
|
||||
|
|
|
|||
|
|
@ -21,8 +21,10 @@ pub struct GlTexture {
|
|||
pub tex: GLuint,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub stride: i32,
|
||||
pub external_only: bool,
|
||||
pub format: &'static Format,
|
||||
pub contents_valid: Cell<bool>,
|
||||
}
|
||||
|
||||
pub fn image_target(external_only: bool) -> GLenum {
|
||||
|
|
@ -60,8 +62,10 @@ impl GlTexture {
|
|||
tex,
|
||||
width: img.dmabuf.width,
|
||||
height: img.dmabuf.height,
|
||||
stride: 0,
|
||||
external_only: img.external_only,
|
||||
format: img.dmabuf.format,
|
||||
contents_valid: Cell::new(true),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -108,8 +112,10 @@ impl GlTexture {
|
|||
tex,
|
||||
width,
|
||||
height,
|
||||
stride,
|
||||
external_only: false,
|
||||
format,
|
||||
contents_valid: Cell::new(true),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::Allocator,
|
||||
cpu_worker::CpuWorker,
|
||||
format::{Format, XRGB8888},
|
||||
gfx_api::{
|
||||
BufferResvUser, GfxApiOpt, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage,
|
||||
GfxTexture, ResetStatus,
|
||||
AsyncShmGfxTexture, BufferResvUser, GfxApiOpt, GfxContext, GfxError, GfxFormat,
|
||||
GfxFramebuffer, GfxImage, ResetStatus, ShmGfxTexture,
|
||||
},
|
||||
gfx_apis::gl::{
|
||||
egl::{context::EglContext, display::EglDisplay, image::EglImage},
|
||||
|
|
@ -265,20 +266,50 @@ impl GfxContext for GlRenderContext {
|
|||
|
||||
fn shmem_texture(
|
||||
self: Rc<Self>,
|
||||
_old: Option<Rc<dyn GfxTexture>>,
|
||||
_old: Option<Rc<dyn ShmGfxTexture>>,
|
||||
data: &[Cell<u8>],
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
_damage: Option<&[Rect]>,
|
||||
) -> Result<Rc<dyn GfxTexture>, GfxError> {
|
||||
) -> Result<Rc<dyn ShmGfxTexture>, GfxError> {
|
||||
(&self)
|
||||
.shmem_texture(data, format, width, height, stride)
|
||||
.map(|w| w as Rc<dyn GfxTexture>)
|
||||
.map(|w| w as Rc<dyn ShmGfxTexture>)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn async_shmem_texture(
|
||||
self: Rc<Self>,
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
_cpu_worker: &Rc<CpuWorker>,
|
||||
) -> Result<Rc<dyn AsyncShmGfxTexture>, GfxError> {
|
||||
let tex = self.ctx.with_current(|| unsafe {
|
||||
let mut tex = 0;
|
||||
(self.ctx.dpy.gles.glGenTextures)(1, &mut tex);
|
||||
Ok(tex)
|
||||
})?;
|
||||
Ok(Rc::new(Texture {
|
||||
gl: GlTexture {
|
||||
ctx: self.ctx.clone(),
|
||||
img: None,
|
||||
tex,
|
||||
width,
|
||||
height,
|
||||
stride,
|
||||
external_only: false,
|
||||
format,
|
||||
contents_valid: Cell::new(false),
|
||||
},
|
||||
ctx: self,
|
||||
format,
|
||||
}))
|
||||
}
|
||||
|
||||
fn allocator(&self) -> Rc<dyn Allocator> {
|
||||
self.gbm.clone()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,21 @@
|
|||
use {
|
||||
crate::{
|
||||
clientmem::ClientMemOffset,
|
||||
format::Format,
|
||||
gfx_api::{GfxError, GfxTexture},
|
||||
gfx_api::{
|
||||
AsyncShmGfxTexture, AsyncShmGfxTextureCallback, GfxError, GfxTexture, PendingShmUpload,
|
||||
ShmGfxTexture,
|
||||
},
|
||||
gfx_apis::gl::{
|
||||
gl::texture::GlTexture,
|
||||
renderer::{context::GlRenderContext, framebuffer::Framebuffer},
|
||||
sys::{
|
||||
GLint, GL_CLAMP_TO_EDGE, GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T,
|
||||
GL_UNPACK_ROW_LENGTH_EXT,
|
||||
},
|
||||
RenderError,
|
||||
},
|
||||
rect::Region,
|
||||
video::dmabuf::DmaBuf,
|
||||
},
|
||||
std::{
|
||||
|
|
@ -82,3 +91,72 @@ impl GfxTexture for Texture {
|
|||
self.format
|
||||
}
|
||||
}
|
||||
|
||||
impl ShmGfxTexture for Texture {
|
||||
fn into_texture(self: Rc<Self>) -> Rc<dyn GfxTexture> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncShmGfxTexture for Texture {
|
||||
fn async_upload(
|
||||
self: Rc<Self>,
|
||||
_callback: Rc<dyn AsyncShmGfxTextureCallback>,
|
||||
mem: &Rc<ClientMemOffset>,
|
||||
damage: Region,
|
||||
) -> Result<Option<PendingShmUpload>, GfxError> {
|
||||
mem.access(|data| self.clone().sync_upload(data, damage))
|
||||
.map_err(RenderError::AccessFailed)??;
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn sync_upload(self: Rc<Self>, data: &[Cell<u8>], _damage: Region) -> Result<(), GfxError> {
|
||||
let shm_info = self.format.shm_info.as_ref().unwrap();
|
||||
if (self.gl.stride * self.gl.height) as usize > data.len() {
|
||||
return Err(RenderError::SmallImageBuffer.into());
|
||||
}
|
||||
let gles = self.ctx.ctx.dpy.gles;
|
||||
self.ctx.ctx.with_current(|| unsafe {
|
||||
(gles.glBindTexture)(GL_TEXTURE_2D, self.gl.tex);
|
||||
(gles.glTexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
(gles.glTexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
(gles.glPixelStorei)(
|
||||
GL_UNPACK_ROW_LENGTH_EXT,
|
||||
self.gl.stride / shm_info.bpp as GLint,
|
||||
);
|
||||
(gles.glTexImage2D)(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
shm_info.gl_format,
|
||||
self.gl.width,
|
||||
self.gl.height,
|
||||
0,
|
||||
shm_info.gl_format as _,
|
||||
shm_info.gl_type as _,
|
||||
data.as_ptr() as _,
|
||||
);
|
||||
(gles.glPixelStorei)(GL_UNPACK_ROW_LENGTH_EXT, 0);
|
||||
(gles.glBindTexture)(GL_TEXTURE_2D, 0);
|
||||
Ok(())
|
||||
})?;
|
||||
self.gl.contents_valid.set(true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compatible_with(
|
||||
&self,
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
) -> bool {
|
||||
format == self.gl.format
|
||||
&& width == self.gl.width
|
||||
&& height == self.gl.height
|
||||
&& stride == self.gl.stride
|
||||
}
|
||||
|
||||
fn into_texture(self: Rc<Self>) -> Rc<dyn GfxTexture> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,15 +14,16 @@ mod semaphore;
|
|||
mod shaders;
|
||||
mod shm_image;
|
||||
mod staging;
|
||||
mod util;
|
||||
|
||||
use {
|
||||
crate::{
|
||||
allocator::{Allocator, AllocatorError},
|
||||
async_engine::AsyncEngine,
|
||||
cpu_worker::{jobs::read_write::ReadWriteJobError, CpuWorker},
|
||||
format::Format,
|
||||
gfx_api::{
|
||||
GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage, GfxTexture, ResetStatus,
|
||||
AsyncShmGfxTexture, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage,
|
||||
ResetStatus, ShmGfxTexture,
|
||||
},
|
||||
gfx_apis::vulkan::{
|
||||
image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer,
|
||||
|
|
@ -196,6 +197,12 @@ pub enum VulkanError {
|
|||
WaitIdle(#[source] vk::Result),
|
||||
#[error("Could not dup a DRM device")]
|
||||
DupDrm(#[source] DrmError),
|
||||
#[error("Graphics context has already been dropped")]
|
||||
Defunct,
|
||||
#[error("Could not perform an async copy to the staging buffer")]
|
||||
AsyncCopyToStaging(#[source] ReadWriteJobError),
|
||||
#[error("The async shm texture is busy")]
|
||||
AsyncCopyBusy,
|
||||
}
|
||||
|
||||
impl From<VulkanError> for GfxError {
|
||||
|
|
@ -250,16 +257,16 @@ impl GfxContext for Context {
|
|||
|
||||
fn shmem_texture(
|
||||
self: Rc<Self>,
|
||||
old: Option<Rc<dyn GfxTexture>>,
|
||||
old: Option<Rc<dyn ShmGfxTexture>>,
|
||||
data: &[Cell<u8>],
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
damage: Option<&[Rect]>,
|
||||
) -> Result<Rc<dyn GfxTexture>, GfxError> {
|
||||
) -> Result<Rc<dyn ShmGfxTexture>, GfxError> {
|
||||
if let Some(old) = old {
|
||||
let old = old.into_vk(&self.0.device.device);
|
||||
let old = old.into_texture().into_vk(&self.0.device.device);
|
||||
let shm = match &old.ty {
|
||||
VulkanImageMemory::DmaBuf(_) => unreachable!(),
|
||||
VulkanImageMemory::Internal(shm) => shm,
|
||||
|
|
@ -275,10 +282,30 @@ impl GfxContext for Context {
|
|||
}
|
||||
let tex = self
|
||||
.0
|
||||
.create_shm_texture(format, width, height, stride, data, false)?;
|
||||
.create_shm_texture(format, width, height, stride, data, false, None)?;
|
||||
Ok(tex as _)
|
||||
}
|
||||
|
||||
fn async_shmem_texture(
|
||||
self: Rc<Self>,
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
cpu_worker: &Rc<CpuWorker>,
|
||||
) -> Result<Rc<dyn AsyncShmGfxTexture>, GfxError> {
|
||||
let tex = self.0.create_shm_texture(
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
stride,
|
||||
&[],
|
||||
false,
|
||||
Some(cpu_worker),
|
||||
)?;
|
||||
Ok(tex)
|
||||
}
|
||||
|
||||
fn allocator(&self) -> Rc<dyn Allocator> {
|
||||
self.0.device.gbm.clone()
|
||||
}
|
||||
|
|
@ -296,7 +323,7 @@ impl GfxContext for Context {
|
|||
) -> Result<Rc<dyn GfxFramebuffer>, GfxError> {
|
||||
let fb = self
|
||||
.0
|
||||
.create_shm_texture(format, width, height, stride, &[], true)?;
|
||||
.create_shm_texture(format, width, height, stride, &[], true, None)?;
|
||||
Ok(fb)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,53 +1,122 @@
|
|||
use {
|
||||
crate::{
|
||||
gfx_apis::vulkan::{device::VulkanDevice, instance::API_VERSION, VulkanError},
|
||||
cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker},
|
||||
gfx_apis::vulkan::{
|
||||
device::VulkanDevice, instance::API_VERSION, renderer::VulkanRenderer, VulkanError,
|
||||
},
|
||||
utils::{numcell::NumCell, ptr_ext::MutPtrExt},
|
||||
},
|
||||
ash::vk::{DeviceMemory, DeviceSize, MemoryRequirements},
|
||||
gpu_alloc::{Config, GpuAllocator, MemoryBlock, Request, UsageFlags},
|
||||
ash::{
|
||||
vk::{DeviceMemory, DeviceSize, MemoryRequirements},
|
||||
Device,
|
||||
},
|
||||
gpu_alloc::{Config, GpuAllocator, MemoryBlock, MemoryPropertyFlags, Request, UsageFlags},
|
||||
gpu_alloc_ash::AshMemoryDevice,
|
||||
parking_lot::Mutex,
|
||||
std::{
|
||||
cell::{Cell, UnsafeCell},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct VulkanAllocator {
|
||||
pub(super) device: Rc<VulkanDevice>,
|
||||
pub(super) non_coherent_atom_mask: u64,
|
||||
pub struct SyncAllocatorStorage {
|
||||
allocator: Arc<Mutex<GpuAllocator<DeviceMemory>>>,
|
||||
device: Rc<VulkanDevice>,
|
||||
}
|
||||
|
||||
pub struct UnsyncAllocatorStorage {
|
||||
allocator: UnsafeCell<GpuAllocator<DeviceMemory>>,
|
||||
device: Rc<VulkanDevice>,
|
||||
}
|
||||
|
||||
pub struct VulkanAllocatorType<T> {
|
||||
storage: T,
|
||||
non_coherent_atom_mask: u64,
|
||||
total: NumCell<u64>,
|
||||
}
|
||||
|
||||
pub type VulkanAllocator = VulkanAllocatorType<UnsyncAllocatorStorage>;
|
||||
pub type VulkanThreadedAllocator = VulkanAllocatorType<SyncAllocatorStorage>;
|
||||
|
||||
enum AllocatorType {
|
||||
Local(Rc<VulkanAllocator>),
|
||||
Threaded {
|
||||
allocator: Rc<VulkanThreadedAllocator>,
|
||||
renderer: Rc<VulkanRenderer>,
|
||||
cpu: Rc<CpuWorker>,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct VulkanAllocation {
|
||||
pub(super) allocator: Rc<VulkanAllocator>,
|
||||
allocator: AllocatorType,
|
||||
pub(super) memory: DeviceMemory,
|
||||
pub(super) offset: DeviceSize,
|
||||
pub(super) mem: Option<*mut u8>,
|
||||
pub(super) size: DeviceSize,
|
||||
pub(super) coherency_mask: Option<u64>,
|
||||
block: Cell<Option<MemoryBlock<DeviceMemory>>>,
|
||||
}
|
||||
|
||||
impl VulkanAllocation {
|
||||
unsafe fn free_locally<T>(
|
||||
&self,
|
||||
allocator: &VulkanAllocatorType<T>,
|
||||
device: &VulkanDevice,
|
||||
gpu: &mut GpuAllocator<DeviceMemory>,
|
||||
) {
|
||||
allocator.total.fetch_sub(self.size);
|
||||
let block = self.block.take().unwrap();
|
||||
do_free(gpu, &device.device, block, self.mem);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VulkanAllocation {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.allocator.total.fetch_sub(self.size);
|
||||
let mut block = self.block.take().unwrap();
|
||||
if let Some(_ptr) = self.mem {
|
||||
// log::info!("free = {:?} - {:?} ({})", ptr, ptr.add(block.size() as usize), block.size());
|
||||
block.unmap(AshMemoryDevice::wrap(&self.allocator.device.device));
|
||||
match &self.allocator {
|
||||
AllocatorType::Local(a) => self.free_locally(a, &a.storage.device, a.allocator()),
|
||||
AllocatorType::Threaded {
|
||||
allocator,
|
||||
renderer,
|
||||
cpu,
|
||||
} => {
|
||||
if renderer.defunct.get() {
|
||||
self.free_locally(
|
||||
allocator,
|
||||
&allocator.storage.device,
|
||||
&mut allocator.storage.allocator.lock(),
|
||||
);
|
||||
} else {
|
||||
let id = renderer.allocate_point();
|
||||
let job = FreeJob {
|
||||
id,
|
||||
renderer: renderer.clone(),
|
||||
allocator: allocator.clone(),
|
||||
size: self.size,
|
||||
work: FreeWork {
|
||||
device: allocator.storage.device.device.clone(),
|
||||
allocator: allocator.storage.allocator.clone(),
|
||||
allocation: Some(UnsafeAllocation {
|
||||
block: self.block.take().unwrap(),
|
||||
ptr: self.mem,
|
||||
}),
|
||||
},
|
||||
};
|
||||
let pending = cpu.submit(Box::new(job));
|
||||
renderer.pending_cpu_jobs.set(id, pending);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.allocator
|
||||
.allocator
|
||||
.get()
|
||||
.deref_mut()
|
||||
.dealloc(AshMemoryDevice::wrap(&self.allocator.device.device), block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanDevice {
|
||||
pub fn create_allocator(self: &Rc<Self>) -> Result<Rc<VulkanAllocator>, VulkanError> {
|
||||
fn create_allocator_<T>(
|
||||
self: &Rc<Self>,
|
||||
map: impl FnOnce(GpuAllocator<DeviceMemory>) -> T,
|
||||
) -> Result<Rc<VulkanAllocatorType<T>>, VulkanError> {
|
||||
let config = Config::i_am_prototyping();
|
||||
let props = unsafe {
|
||||
gpu_alloc_ash::device_properties(
|
||||
|
|
@ -60,18 +129,56 @@ impl VulkanDevice {
|
|||
props.buffer_device_address = false;
|
||||
let non_coherent_atom_size = props.non_coherent_atom_size;
|
||||
let allocator = GpuAllocator::new(config, props);
|
||||
Ok(Rc::new(VulkanAllocator {
|
||||
device: self.clone(),
|
||||
Ok(Rc::new(VulkanAllocatorType {
|
||||
non_coherent_atom_mask: non_coherent_atom_size - 1,
|
||||
allocator: UnsafeCell::new(allocator),
|
||||
storage: map(allocator),
|
||||
total: Default::default(),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn create_allocator(self: &Rc<Self>) -> Result<Rc<VulkanAllocator>, VulkanError> {
|
||||
self.create_allocator_(|a| UnsyncAllocatorStorage {
|
||||
allocator: UnsafeCell::new(a),
|
||||
device: self.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_threaded_allocator(
|
||||
self: &Rc<Self>,
|
||||
) -> Result<Rc<VulkanThreadedAllocator>, VulkanError> {
|
||||
self.create_allocator_(|a| SyncAllocatorStorage {
|
||||
allocator: Arc::new(Mutex::new(a)),
|
||||
device: self.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> VulkanAllocatorType<T> {
|
||||
fn commit_allocation(
|
||||
self: &Rc<Self>,
|
||||
ua: UnsafeAllocation,
|
||||
allocator: AllocatorType,
|
||||
) -> VulkanAllocation {
|
||||
let UnsafeAllocation { block, ptr } = ua;
|
||||
self.total.fetch_add(block.size());
|
||||
VulkanAllocation {
|
||||
allocator,
|
||||
memory: *block.memory(),
|
||||
offset: block.offset(),
|
||||
mem: ptr,
|
||||
size: block.size(),
|
||||
coherency_mask: match block.props().contains(MemoryPropertyFlags::HOST_COHERENT) {
|
||||
true => None,
|
||||
false => Some(self.non_coherent_atom_mask),
|
||||
},
|
||||
block: Cell::new(Some(block)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanAllocator {
|
||||
fn allocator(&self) -> &mut GpuAllocator<DeviceMemory> {
|
||||
unsafe { self.allocator.get().deref_mut() }
|
||||
unsafe { self.storage.allocator.get().deref_mut() }
|
||||
}
|
||||
|
||||
pub fn alloc(
|
||||
|
|
@ -80,49 +187,202 @@ impl VulkanAllocator {
|
|||
usage: UsageFlags,
|
||||
map: bool,
|
||||
) -> Result<VulkanAllocation, VulkanError> {
|
||||
let request = Request {
|
||||
size: req.size,
|
||||
align_mask: req.alignment - 1,
|
||||
let ua = do_alloc(
|
||||
self.allocator(),
|
||||
&self.storage.device.device,
|
||||
req,
|
||||
usage,
|
||||
memory_types: req.memory_type_bits,
|
||||
};
|
||||
let block = unsafe {
|
||||
self.allocator()
|
||||
.alloc(AshMemoryDevice::wrap(&self.device.device), request)
|
||||
};
|
||||
let mut block = block.map_err(VulkanError::AllocateMemory2)?;
|
||||
let ptr = match map {
|
||||
true => {
|
||||
let ptr = unsafe {
|
||||
block.map(
|
||||
AshMemoryDevice::wrap(&self.device.device),
|
||||
0,
|
||||
block.size() as usize,
|
||||
)
|
||||
};
|
||||
Some(ptr.map_err(VulkanError::MapMemory)?.as_ptr())
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
self.total.fetch_add(block.size());
|
||||
Ok(VulkanAllocation {
|
||||
allocator: self.clone(),
|
||||
memory: *block.memory(),
|
||||
offset: block.offset(),
|
||||
mem: ptr,
|
||||
size: block.size(),
|
||||
block: Cell::new(Some(block)),
|
||||
})
|
||||
map,
|
||||
)?;
|
||||
Ok(self.commit_allocation(ua, AllocatorType::Local(self.clone())))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VulkanAllocator {
|
||||
fn drop(&mut self) {
|
||||
impl VulkanThreadedAllocator {
|
||||
pub fn async_alloc(
|
||||
self: &Rc<Self>,
|
||||
renderer: &Rc<VulkanRenderer>,
|
||||
cpu: &Rc<CpuWorker>,
|
||||
req: MemoryRequirements,
|
||||
usage: UsageFlags,
|
||||
map: bool,
|
||||
cb: impl FnOnce(Result<VulkanAllocation, VulkanError>) + 'static,
|
||||
) -> Result<(), VulkanError> {
|
||||
renderer.check_defunct()?;
|
||||
let id = renderer.allocate_point();
|
||||
let job = AllocJob {
|
||||
id,
|
||||
renderer: renderer.clone(),
|
||||
cpu: cpu.clone(),
|
||||
allocator: self.clone(),
|
||||
cb: Some(cb),
|
||||
work: AllocWork {
|
||||
req,
|
||||
usage,
|
||||
map,
|
||||
device: self.storage.device.device.clone(),
|
||||
allocator: self.storage.allocator.clone(),
|
||||
res: None,
|
||||
},
|
||||
};
|
||||
let pending = cpu.submit(Box::new(job));
|
||||
renderer.pending_cpu_jobs.set(id, pending);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct AllocJob<T> {
|
||||
id: u64,
|
||||
renderer: Rc<VulkanRenderer>,
|
||||
cpu: Rc<CpuWorker>,
|
||||
allocator: Rc<VulkanThreadedAllocator>,
|
||||
cb: Option<T>,
|
||||
work: AllocWork,
|
||||
}
|
||||
|
||||
struct AllocWork {
|
||||
req: MemoryRequirements,
|
||||
usage: UsageFlags,
|
||||
map: bool,
|
||||
device: Arc<Device>,
|
||||
allocator: Arc<Mutex<GpuAllocator<DeviceMemory>>>,
|
||||
res: Option<Result<UnsafeAllocation, VulkanError>>,
|
||||
}
|
||||
|
||||
impl CpuWork for AllocWork {
|
||||
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
||||
let r = do_alloc(
|
||||
&mut self.allocator.lock(),
|
||||
&self.device,
|
||||
&self.req,
|
||||
self.usage,
|
||||
self.map,
|
||||
);
|
||||
self.res = Some(r);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> CpuJob for AllocJob<T>
|
||||
where
|
||||
T: FnOnce(Result<VulkanAllocation, VulkanError>),
|
||||
{
|
||||
fn work(&mut self) -> &mut dyn CpuWork {
|
||||
&mut self.work
|
||||
}
|
||||
|
||||
fn completed(mut self: Box<Self>) {
|
||||
self.renderer.pending_cpu_jobs.remove(&self.id);
|
||||
let res = self.work.res.take().unwrap().map(|ua| {
|
||||
self.allocator.commit_allocation(
|
||||
ua,
|
||||
AllocatorType::Threaded {
|
||||
allocator: self.allocator.clone(),
|
||||
renderer: self.renderer.clone(),
|
||||
cpu: self.cpu.clone(),
|
||||
},
|
||||
)
|
||||
});
|
||||
self.cb.take().unwrap()(res);
|
||||
}
|
||||
}
|
||||
|
||||
struct FreeJob {
|
||||
id: u64,
|
||||
renderer: Rc<VulkanRenderer>,
|
||||
allocator: Rc<VulkanThreadedAllocator>,
|
||||
size: u64,
|
||||
work: FreeWork,
|
||||
}
|
||||
|
||||
struct FreeWork {
|
||||
device: Arc<Device>,
|
||||
allocator: Arc<Mutex<GpuAllocator<DeviceMemory>>>,
|
||||
allocation: Option<UnsafeAllocation>,
|
||||
}
|
||||
|
||||
impl CpuWork for FreeWork {
|
||||
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
||||
let ua = self.allocation.take().unwrap();
|
||||
unsafe {
|
||||
self.allocator
|
||||
.get()
|
||||
.deref_mut()
|
||||
.cleanup(AshMemoryDevice::wrap(&self.device.device));
|
||||
do_free(&mut self.allocator.lock(), &self.device, ua.block, ua.ptr);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuJob for FreeJob {
|
||||
fn work(&mut self) -> &mut dyn CpuWork {
|
||||
&mut self.work
|
||||
}
|
||||
|
||||
fn completed(self: Box<Self>) {
|
||||
self.renderer.pending_cpu_jobs.remove(&self.id);
|
||||
self.allocator.total.fetch_sub(self.size);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UnsafeAllocation {
|
||||
block: MemoryBlock<DeviceMemory>,
|
||||
ptr: Option<*mut u8>,
|
||||
}
|
||||
|
||||
unsafe impl Send for UnsafeAllocation {}
|
||||
|
||||
fn do_alloc(
|
||||
allocator: &mut GpuAllocator<DeviceMemory>,
|
||||
device: &Device,
|
||||
req: &MemoryRequirements,
|
||||
usage: UsageFlags,
|
||||
map: bool,
|
||||
) -> Result<UnsafeAllocation, VulkanError> {
|
||||
let request = Request {
|
||||
size: req.size,
|
||||
align_mask: req.alignment - 1,
|
||||
usage,
|
||||
memory_types: req.memory_type_bits,
|
||||
};
|
||||
let device = AshMemoryDevice::wrap(device);
|
||||
let block = unsafe { allocator.alloc(device, request) };
|
||||
let mut block = block.map_err(VulkanError::AllocateMemory2)?;
|
||||
let ptr = match map {
|
||||
true => {
|
||||
let ptr = unsafe { block.map(device, 0, block.size() as usize) };
|
||||
Some(ptr.map_err(VulkanError::MapMemory)?.as_ptr())
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
Ok(UnsafeAllocation { block, ptr })
|
||||
}
|
||||
|
||||
unsafe fn do_free(
|
||||
gpu: &mut GpuAllocator<DeviceMemory>,
|
||||
device: &Device,
|
||||
mut block: MemoryBlock<DeviceMemory>,
|
||||
ptr: Option<*mut u8>,
|
||||
) {
|
||||
let device = AshMemoryDevice::wrap(device);
|
||||
if let Some(_ptr) = ptr {
|
||||
// log::info!("free = {:?} - {:?} ({})", ptr, ptr.add(block.size() as usize), block.size());
|
||||
block.unmap(device);
|
||||
}
|
||||
gpu.dealloc(device, block);
|
||||
}
|
||||
|
||||
impl Drop for UnsyncAllocatorStorage {
|
||||
fn drop(&mut self) {
|
||||
let device = AshMemoryDevice::wrap(&self.device.device);
|
||||
unsafe {
|
||||
self.allocator.get_mut().cleanup(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SyncAllocatorStorage {
|
||||
fn drop(&mut self) {
|
||||
let device = AshMemoryDevice::wrap(&self.device.device);
|
||||
unsafe {
|
||||
self.allocator.lock().cleanup(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ use {
|
|||
gfx_apis::vulkan::{
|
||||
allocator::VulkanAllocator, command::VulkanCommandBuffer, device::VulkanDevice,
|
||||
format::VulkanFormat, renderer::image_barrier, staging::VulkanStagingBuffer,
|
||||
util::OnDrop, VulkanError,
|
||||
VulkanError,
|
||||
},
|
||||
utils::errorfmt::ErrorFmt,
|
||||
utils::{errorfmt::ErrorFmt, on_drop::OnDrop},
|
||||
video::{
|
||||
dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec},
|
||||
drm::Drm,
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ use {
|
|||
map_extension_properties, ApiVersionDisplay, Extensions, VulkanInstance,
|
||||
API_VERSION,
|
||||
},
|
||||
util::OnDrop,
|
||||
VulkanError,
|
||||
},
|
||||
utils::on_drop::OnDrop,
|
||||
video::{
|
||||
drm::{sync_obj::SyncObjCtx, Drm},
|
||||
gbm::GbmDevice,
|
||||
|
|
@ -42,6 +42,7 @@ use {
|
|||
std::{
|
||||
ffi::{CStr, CString},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
},
|
||||
uapi::Ustr,
|
||||
};
|
||||
|
|
@ -52,7 +53,7 @@ pub struct VulkanDevice {
|
|||
pub(super) gbm: Rc<GbmDevice>,
|
||||
pub(super) sync_ctx: Rc<SyncObjCtx>,
|
||||
pub(super) instance: Rc<VulkanInstance>,
|
||||
pub(super) device: Device,
|
||||
pub(super) device: Arc<Device>,
|
||||
pub(super) external_memory_fd: external_memory_fd::Device,
|
||||
pub(super) external_semaphore_fd: external_semaphore_fd::Device,
|
||||
pub(super) external_fence_fd: external_fence_fd::Device,
|
||||
|
|
@ -292,7 +293,7 @@ impl VulkanInstance {
|
|||
sync_ctx: Rc::new(SyncObjCtx::new(gbm.drm.fd())),
|
||||
gbm: Rc::new(gbm),
|
||||
instance: self.clone(),
|
||||
device,
|
||||
device: Arc::new(device),
|
||||
external_memory_fd,
|
||||
external_semaphore_fd,
|
||||
external_fence_fd,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,19 @@
|
|||
use {
|
||||
crate::{
|
||||
clientmem::ClientMemOffset,
|
||||
format::Format,
|
||||
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture, SyncFile},
|
||||
gfx_api::{
|
||||
AsyncShmGfxTexture, AsyncShmGfxTextureCallback, AsyncShmGfxTextureUploadCancellable,
|
||||
GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture, PendingShmUpload,
|
||||
ShmGfxTexture, SyncFile,
|
||||
},
|
||||
gfx_apis::vulkan::{
|
||||
allocator::VulkanAllocation, device::VulkanDevice, format::VulkanModifierLimits,
|
||||
renderer::VulkanRenderer, shm_image::VulkanShmImage, util::OnDrop, VulkanError,
|
||||
renderer::VulkanRenderer, shm_image::VulkanShmImage, VulkanError,
|
||||
},
|
||||
rect::Region,
|
||||
theme::Color,
|
||||
utils::clonecell::CloneCell,
|
||||
utils::{clonecell::CloneCell, on_drop::OnDrop},
|
||||
video::dmabuf::{DmaBuf, PlaneVec},
|
||||
},
|
||||
ash::vk::{
|
||||
|
|
@ -51,6 +57,7 @@ pub struct VulkanImage {
|
|||
pub(super) render_view: Option<ImageView>,
|
||||
pub(super) image: Image,
|
||||
pub(super) is_undefined: Cell<bool>,
|
||||
pub(super) contents_are_undefined: Cell<bool>,
|
||||
pub(super) ty: VulkanImageMemory,
|
||||
pub(super) render_ops: CloneCell<Vec<GfxApiOpt>>,
|
||||
pub(super) bridge: Option<VulkanFramebufferBridge>,
|
||||
|
|
@ -378,6 +385,7 @@ impl VulkanDmaBufImageTemplate {
|
|||
}),
|
||||
format: self.dmabuf.format,
|
||||
is_undefined: Cell::new(true),
|
||||
contents_are_undefined: Cell::new(false),
|
||||
bridge,
|
||||
}))
|
||||
}
|
||||
|
|
@ -530,3 +538,64 @@ impl GfxTexture for VulkanImage {
|
|||
self.format
|
||||
}
|
||||
}
|
||||
|
||||
impl ShmGfxTexture for VulkanImage {
|
||||
fn into_texture(self: Rc<Self>) -> Rc<dyn GfxTexture> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncShmGfxTexture for VulkanImage {
|
||||
fn async_upload(
|
||||
self: Rc<Self>,
|
||||
callback: Rc<dyn AsyncShmGfxTextureCallback>,
|
||||
mem: &Rc<ClientMemOffset>,
|
||||
damage: Region,
|
||||
) -> Result<Option<PendingShmUpload>, GfxError> {
|
||||
let VulkanImageMemory::Internal(shm) = &self.ty else {
|
||||
unreachable!();
|
||||
};
|
||||
let pending = shm.async_upload(&self, mem, damage, callback)?;
|
||||
Ok(pending)
|
||||
}
|
||||
|
||||
fn sync_upload(self: Rc<Self>, mem: &[Cell<u8>], damage: Region) -> Result<(), GfxError> {
|
||||
let VulkanImageMemory::Internal(shm) = &self.ty else {
|
||||
unreachable!();
|
||||
};
|
||||
if shm.async_data.as_ref().unwrap().busy.get() {
|
||||
return Err(VulkanError::AsyncCopyBusy.into());
|
||||
}
|
||||
shm.upload(&self, mem, Some(damage.rects()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compatible_with(
|
||||
&self,
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
) -> bool {
|
||||
self.format == format
|
||||
&& self.width == width as u32
|
||||
&& self.height == height as u32
|
||||
&& self.stride == stride as u32
|
||||
}
|
||||
|
||||
fn into_texture(self: Rc<Self>) -> Rc<dyn GfxTexture> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncShmGfxTextureUploadCancellable for VulkanImage {
|
||||
fn cancel(&self, id: u64) {
|
||||
let VulkanImageMemory::Internal(shm) = &self.ty else {
|
||||
unreachable!();
|
||||
};
|
||||
let data = shm.async_data.as_ref().unwrap();
|
||||
if data.callback_id.get() == id {
|
||||
data.callback.take();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
use {
|
||||
crate::gfx_apis::vulkan::{util::OnDrop, VulkanError, VULKAN_VALIDATION},
|
||||
crate::{
|
||||
gfx_apis::vulkan::{VulkanError, VULKAN_VALIDATION},
|
||||
utils::on_drop::OnDrop,
|
||||
},
|
||||
ahash::{AHashMap, AHashSet},
|
||||
ash::{
|
||||
ext::{debug_utils, validation_features},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
use {
|
||||
crate::gfx_apis::vulkan::{
|
||||
descriptor::VulkanDescriptorSetLayout, device::VulkanDevice, shaders::VulkanShader,
|
||||
util::OnDrop, VulkanError,
|
||||
crate::{
|
||||
gfx_apis::vulkan::{
|
||||
descriptor::VulkanDescriptorSetLayout, device::VulkanDevice, shaders::VulkanShader,
|
||||
VulkanError,
|
||||
},
|
||||
utils::on_drop::OnDrop,
|
||||
},
|
||||
arrayvec::ArrayVec,
|
||||
ash::{
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
cpu_worker::PendingJob,
|
||||
format::{Format, XRGB8888},
|
||||
gfx_api::{
|
||||
AcquireSync, BufferResv, BufferResvUser, GfxApiOpt, GfxFormat, GfxFramebuffer,
|
||||
GfxTexture, GfxWriteModifier, ReleaseSync, SyncFile,
|
||||
},
|
||||
gfx_apis::vulkan::{
|
||||
allocator::VulkanAllocator,
|
||||
allocator::{VulkanAllocator, VulkanThreadedAllocator},
|
||||
command::{VulkanCommandBuffer, VulkanCommandPool},
|
||||
descriptor::VulkanDescriptorSetLayout,
|
||||
device::VulkanDevice,
|
||||
|
|
@ -78,6 +79,9 @@ pub struct VulkanRenderer {
|
|||
pub(super) tex_frag_mult_opaque_shader: Rc<VulkanShader>,
|
||||
pub(super) tex_frag_mult_alpha_shader: Rc<VulkanShader>,
|
||||
pub(super) tex_descriptor_set_layout: Rc<VulkanDescriptorSetLayout>,
|
||||
pub(super) defunct: Cell<bool>,
|
||||
pub(super) pending_cpu_jobs: CopyHashMap<u64, PendingJob>,
|
||||
pub(super) shm_allocator: Rc<VulkanThreadedAllocator>,
|
||||
}
|
||||
|
||||
pub(super) struct UsedTexture {
|
||||
|
|
@ -172,6 +176,7 @@ impl VulkanDevice {
|
|||
})
|
||||
.collect();
|
||||
let allocator = self.create_allocator()?;
|
||||
let shm_allocator = self.create_threaded_allocator()?;
|
||||
let render = Rc::new(VulkanRenderer {
|
||||
formats: Rc::new(formats),
|
||||
device: self.clone(),
|
||||
|
|
@ -195,6 +200,9 @@ impl VulkanDevice {
|
|||
tex_frag_mult_opaque_shader,
|
||||
tex_frag_mult_alpha_shader,
|
||||
tex_descriptor_set_layout,
|
||||
defunct: Cell::new(false),
|
||||
pending_cpu_jobs: Default::default(),
|
||||
shm_allocator,
|
||||
});
|
||||
render.get_or_create_pipelines(XRGB8888.vk_format)?;
|
||||
Ok(render)
|
||||
|
|
@ -271,6 +279,9 @@ impl VulkanRenderer {
|
|||
for cmd in opts {
|
||||
if let GfxApiOpt::CopyTexture(c) = cmd {
|
||||
let tex = c.tex.clone().into_vk(&self.device.device);
|
||||
if tex.contents_are_undefined.get() {
|
||||
continue;
|
||||
}
|
||||
if let VulkanImageMemory::DmaBuf(_) = &tex.ty {
|
||||
memory.sample.push(tex.clone())
|
||||
}
|
||||
|
|
@ -450,6 +461,10 @@ impl VulkanRenderer {
|
|||
}
|
||||
GfxApiOpt::CopyTexture(c) => {
|
||||
let tex = c.tex.as_vk(&self.device.device);
|
||||
if tex.contents_are_undefined.get() {
|
||||
log::warn!("Ignoring undefined texture");
|
||||
continue;
|
||||
}
|
||||
let copy_type = match c.alpha.is_some() {
|
||||
true => TexCopyType::Multiply,
|
||||
false => TexCopyType::Identity,
|
||||
|
|
@ -799,6 +814,7 @@ impl VulkanRenderer {
|
|||
stride as i32,
|
||||
&[],
|
||||
true,
|
||||
None,
|
||||
)?;
|
||||
(&*tmp_tex as &dyn GfxFramebuffer)
|
||||
.copy_texture(
|
||||
|
|
@ -996,6 +1012,7 @@ impl VulkanRenderer {
|
|||
opts: &[GfxApiOpt],
|
||||
clear: Option<&Color>,
|
||||
) -> Result<(), VulkanError> {
|
||||
self.check_defunct()?;
|
||||
let buf = self.allocate_command_buffer()?;
|
||||
self.collect_memory(opts);
|
||||
self.begin_command_buffer(buf.buffer)?;
|
||||
|
|
@ -1025,6 +1042,7 @@ impl VulkanRenderer {
|
|||
}
|
||||
|
||||
pub fn on_drop(&self) {
|
||||
self.defunct.set(true);
|
||||
let mut pending_frames = self.pending_frames.lock();
|
||||
let mut pending_uploads = self.pending_uploads.lock();
|
||||
if pending_frames.is_not_empty() || pending_uploads.is_not_empty() {
|
||||
|
|
@ -1037,6 +1055,13 @@ impl VulkanRenderer {
|
|||
pending_frames.clear();
|
||||
pending_uploads.clear();
|
||||
}
|
||||
|
||||
pub(super) fn check_defunct(&self) -> Result<(), VulkanError> {
|
||||
match self.defunct.get() {
|
||||
true => Err(VulkanError::Defunct),
|
||||
false => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for VulkanRenderer {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,15 @@
|
|||
use {
|
||||
crate::{
|
||||
clientmem::ClientMemOffset,
|
||||
cpu_worker::{
|
||||
jobs::{
|
||||
img_copy::ImgCopyWork,
|
||||
read_write::{ReadWriteJobError, ReadWriteWork},
|
||||
},
|
||||
CpuJob, CpuWork, CpuWorker,
|
||||
},
|
||||
format::{Format, FormatShmInfo},
|
||||
gfx_api::SyncFile,
|
||||
gfx_api::{AsyncShmGfxTextureCallback, PendingShmUpload, SyncFile},
|
||||
gfx_apis::vulkan::{
|
||||
allocator::VulkanAllocation,
|
||||
command::VulkanCommandBuffer,
|
||||
|
|
@ -9,11 +17,10 @@ use {
|
|||
image::{VulkanImage, VulkanImageMemory},
|
||||
renderer::{image_barrier, VulkanRenderer},
|
||||
staging::VulkanStagingBuffer,
|
||||
util::OnDrop,
|
||||
VulkanError,
|
||||
},
|
||||
rect::Rect,
|
||||
utils::errorfmt::ErrorFmt,
|
||||
rect::{Rect, Region},
|
||||
utils::{clonecell::CloneCell, errorfmt::ErrorFmt, on_drop::OnDrop},
|
||||
},
|
||||
ash::vk::{
|
||||
AccessFlags2, BufferImageCopy2, BufferMemoryBarrier2, CommandBufferBeginInfo,
|
||||
|
|
@ -25,14 +32,32 @@ use {
|
|||
},
|
||||
gpu_alloc::UsageFlags,
|
||||
isnt::std_1::primitive::IsntSliceExt,
|
||||
std::{cell::Cell, ptr, rc::Rc, slice},
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
ptr,
|
||||
rc::Rc,
|
||||
slice,
|
||||
},
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
pub struct VulkanShmImage {
|
||||
pub(super) _size: DeviceSize,
|
||||
pub(super) size: DeviceSize,
|
||||
pub(super) stride: u32,
|
||||
pub(super) _allocation: VulkanAllocation,
|
||||
pub(super) shm_info: &'static FormatShmInfo,
|
||||
pub(super) async_data: Option<VulkanShmImageAsyncData>,
|
||||
}
|
||||
|
||||
pub struct VulkanShmImageAsyncData {
|
||||
pub(super) busy: Cell<bool>,
|
||||
pub(super) io_job: Cell<Option<Box<IoUploadJob>>>,
|
||||
pub(super) copy_job: Cell<Option<Box<CopyUploadJob>>>,
|
||||
pub(super) staging: CloneCell<Option<Rc<VulkanStagingBuffer>>>,
|
||||
pub(super) callback: Cell<Option<Rc<dyn AsyncShmGfxTextureCallback>>>,
|
||||
pub(super) callback_id: Cell<u64>,
|
||||
pub(super) regions: RefCell<Vec<BufferImageCopy2<'static>>>,
|
||||
pub(super) cpu: Rc<CpuWorker>,
|
||||
}
|
||||
|
||||
impl VulkanShmImage {
|
||||
|
|
@ -42,6 +67,7 @@ impl VulkanShmImage {
|
|||
buffer: &[Cell<u8>],
|
||||
damage: Option<&[Rect]>,
|
||||
) -> Result<(), VulkanError> {
|
||||
img.renderer.check_defunct()?;
|
||||
if let Some(damage) = damage {
|
||||
if damage.is_empty() {
|
||||
return Ok(());
|
||||
|
|
@ -133,6 +159,30 @@ impl VulkanShmImage {
|
|||
ptr::copy_nonoverlapping(buf, mem, total_size as usize);
|
||||
}
|
||||
})?;
|
||||
let Some((cmd, fence, sync_file, point)) =
|
||||
self.submit_buffer_to_image_copy(img, &staging, cpy)?
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
let future = img.renderer.eng.spawn(await_upload(
|
||||
point,
|
||||
img.clone(),
|
||||
cmd,
|
||||
sync_file,
|
||||
fence,
|
||||
staging,
|
||||
));
|
||||
img.renderer.pending_uploads.set(point, future);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn submit_buffer_to_image_copy(
|
||||
&self,
|
||||
img: &Rc<VulkanImage>,
|
||||
staging: &VulkanStagingBuffer,
|
||||
regions: &[BufferImageCopy2],
|
||||
) -> Result<Option<(Rc<VulkanCommandBuffer>, Rc<VulkanFence>, SyncFile, u64)>, VulkanError>
|
||||
{
|
||||
let memory_barrier = |sam, ssm, dam, dsm| {
|
||||
BufferMemoryBarrier2::default()
|
||||
.buffer(staging.buffer)
|
||||
|
|
@ -185,7 +235,7 @@ impl VulkanShmImage {
|
|||
.src_buffer(staging.buffer)
|
||||
.dst_image(img.image)
|
||||
.dst_image_layout(ImageLayout::TRANSFER_DST_OPTIMAL)
|
||||
.regions(cpy);
|
||||
.regions(regions);
|
||||
let cmd = img.renderer.allocate_command_buffer()?;
|
||||
let dev = &img.renderer.device.device;
|
||||
let command_buffer_info = CommandBufferSubmitInfo::default().command_buffer(cmd.buffer);
|
||||
|
|
@ -210,25 +260,17 @@ impl VulkanShmImage {
|
|||
.map_err(VulkanError::Submit)?;
|
||||
}
|
||||
img.is_undefined.set(false);
|
||||
img.contents_are_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(());
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
let point = img.renderer.allocate_point();
|
||||
let future = img.renderer.eng.spawn(await_upload(
|
||||
point,
|
||||
img.clone(),
|
||||
cmd,
|
||||
release_sync_file,
|
||||
release_fence,
|
||||
staging,
|
||||
));
|
||||
img.renderer.pending_uploads.set(point, future);
|
||||
Ok(())
|
||||
Ok(Some((cmd, release_fence, release_sync_file, point)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -252,6 +294,325 @@ async fn await_upload(
|
|||
img.renderer.pending_uploads.remove(&id);
|
||||
}
|
||||
|
||||
impl VulkanShmImageAsyncData {
|
||||
fn complete(&self, result: Result<(), VulkanError>) {
|
||||
self.busy.set(false);
|
||||
if let Some(cb) = self.callback.take() {
|
||||
cb.completed(result.map_err(|e| e.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanShmImage {
|
||||
pub fn async_upload(
|
||||
&self,
|
||||
img: &Rc<VulkanImage>,
|
||||
client_mem: &Rc<ClientMemOffset>,
|
||||
damage: Region,
|
||||
callback: Rc<dyn AsyncShmGfxTextureCallback>,
|
||||
) -> Result<Option<PendingShmUpload>, VulkanError> {
|
||||
let data = self.async_data.as_ref().unwrap();
|
||||
let res = self.try_async_upload(img, data, client_mem, damage);
|
||||
match res {
|
||||
Ok(()) => {
|
||||
let id = img.renderer.allocate_point();
|
||||
data.callback_id.set(id);
|
||||
data.callback.set(Some(callback));
|
||||
Ok(Some(PendingShmUpload::new(img.clone(), id)))
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_async_upload(
|
||||
&self,
|
||||
img: &Rc<VulkanImage>,
|
||||
data: &VulkanShmImageAsyncData,
|
||||
client_mem: &Rc<ClientMemOffset>,
|
||||
mut damage: Region,
|
||||
) -> Result<(), VulkanError> {
|
||||
if data.busy.get() {
|
||||
return Err(VulkanError::AsyncCopyBusy);
|
||||
}
|
||||
if self.size > client_mem.ptr().len() as u64 {
|
||||
return Err(VulkanError::InvalidBufferSize);
|
||||
}
|
||||
data.busy.set(true);
|
||||
if img.contents_are_undefined.get() {
|
||||
damage = Region::new2(Rect::new_sized(0, 0, img.width as _, img.height as _).unwrap())
|
||||
}
|
||||
|
||||
let copies = &mut *data.regions.borrow_mut();
|
||||
copies.clear();
|
||||
|
||||
let mut copy = |x, y, width, height| {
|
||||
let buffer_offset = (y as u32 * img.stride + x as u32 * self.shm_info.bpp) as u64;
|
||||
let copy = BufferImageCopy2::default()
|
||||
.buffer_offset(buffer_offset)
|
||||
.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,
|
||||
})
|
||||
.buffer_image_height(img.height)
|
||||
.buffer_row_length(img.stride / self.shm_info.bpp);
|
||||
copies.push(copy);
|
||||
};
|
||||
for damage in damage.rects() {
|
||||
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;
|
||||
}
|
||||
copy(
|
||||
damage.x1(),
|
||||
damage.y1(),
|
||||
damage.width() as u32,
|
||||
damage.height() as u32,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(staging) = data.staging.get() {
|
||||
return self.async_upload_initiate_copy(img, data, &staging, copies, client_mem);
|
||||
}
|
||||
|
||||
let img2 = img.clone();
|
||||
let client_mem = client_mem.clone();
|
||||
img.renderer.device.create_shm_staging(
|
||||
&img.renderer,
|
||||
&data.cpu,
|
||||
self.size,
|
||||
true,
|
||||
false,
|
||||
move |res| {
|
||||
let VulkanImageMemory::Internal(shm) = &img2.ty else {
|
||||
unreachable!();
|
||||
};
|
||||
if let Err(e) = shm.async_upload_after_allocation(&img2, &client_mem, res) {
|
||||
shm.async_data.as_ref().unwrap().complete(Err(e));
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn async_upload_after_allocation(
|
||||
&self,
|
||||
img: &Rc<VulkanImage>,
|
||||
client_mem: &Rc<ClientMemOffset>,
|
||||
res: Result<VulkanStagingBuffer, VulkanError>,
|
||||
) -> Result<(), VulkanError> {
|
||||
let staging = Rc::new(res?);
|
||||
let data = self.async_data.as_ref().unwrap();
|
||||
data.staging.set(Some(staging.clone()));
|
||||
let copies = &*data.regions.borrow();
|
||||
self.async_upload_initiate_copy(img, data, &staging, copies, client_mem)
|
||||
}
|
||||
|
||||
fn async_upload_initiate_copy(
|
||||
&self,
|
||||
img: &Rc<VulkanImage>,
|
||||
data: &VulkanShmImageAsyncData,
|
||||
staging: &VulkanStagingBuffer,
|
||||
copies: &[BufferImageCopy2],
|
||||
client_mem: &Rc<ClientMemOffset>,
|
||||
) -> Result<(), VulkanError> {
|
||||
img.renderer.check_defunct()?;
|
||||
|
||||
let id = img.renderer.allocate_point();
|
||||
let pending;
|
||||
if client_mem.pool().sigbus_impossible() {
|
||||
let mut job = data.copy_job.take().unwrap_or_else(|| {
|
||||
Box::new(CopyUploadJob {
|
||||
img: None,
|
||||
id,
|
||||
_mem: None,
|
||||
work: unsafe { ImgCopyWork::new() },
|
||||
})
|
||||
});
|
||||
job.id = id;
|
||||
job.img = Some(img.clone());
|
||||
job._mem = Some(client_mem.clone());
|
||||
job.work.src = client_mem.ptr() as _;
|
||||
job.work.dst = staging.allocation.mem.unwrap();
|
||||
job.work.width = img.width as _;
|
||||
job.work.stride = img.stride as _;
|
||||
job.work.bpp = self.shm_info.bpp as _;
|
||||
job.work.rects.clear();
|
||||
for copy in copies {
|
||||
job.work.rects.push(
|
||||
Rect::new_sized(
|
||||
copy.image_offset.x as _,
|
||||
copy.image_offset.y as _,
|
||||
copy.image_extent.width as _,
|
||||
copy.image_extent.height as _,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
pending = data.cpu.submit(job);
|
||||
} else {
|
||||
let mut min_offset = client_mem.ptr().len() as u64;
|
||||
let mut max_offset = 0;
|
||||
for copy in copies {
|
||||
min_offset = min_offset.min(copy.buffer_offset);
|
||||
let len = img.stride * (copy.image_extent.height - 1)
|
||||
+ copy.image_extent.width * self.shm_info.bpp;
|
||||
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(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);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn async_upload_copy_buffer_to_image(
|
||||
&self,
|
||||
img: &Rc<VulkanImage>,
|
||||
data: &VulkanShmImageAsyncData,
|
||||
res: Result<(), ReadWriteJobError>,
|
||||
) -> Result<(), VulkanError> {
|
||||
if let Err(e) = res {
|
||||
return Err(VulkanError::AsyncCopyToStaging(e));
|
||||
}
|
||||
img.renderer.check_defunct()?;
|
||||
let regions = &*data.regions.borrow();
|
||||
let staging = data.staging.get().unwrap();
|
||||
staging.upload(|_, _| ())?;
|
||||
let Some((cmd, fence, sync_file, point)) =
|
||||
self.submit_buffer_to_image_copy(img, &staging, regions)?
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
let future = img.renderer.eng.spawn(await_async_upload(
|
||||
point,
|
||||
img.clone(),
|
||||
cmd,
|
||||
fence,
|
||||
sync_file,
|
||||
));
|
||||
img.renderer.pending_uploads.set(point, future);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct IoUploadJob {
|
||||
img: Option<Rc<VulkanImage>>,
|
||||
id: u64,
|
||||
_mem: Option<Rc<ClientMemOffset>>,
|
||||
fd: Option<Rc<OwnedFd>>,
|
||||
work: ReadWriteWork,
|
||||
}
|
||||
|
||||
pub(super) struct CopyUploadJob {
|
||||
img: Option<Rc<VulkanImage>>,
|
||||
id: u64,
|
||||
_mem: Option<Rc<ClientMemOffset>>,
|
||||
work: ImgCopyWork,
|
||||
}
|
||||
|
||||
impl CpuJob for IoUploadJob {
|
||||
fn work(&mut self) -> &mut dyn CpuWork {
|
||||
&mut self.work
|
||||
}
|
||||
|
||||
fn completed(mut self: Box<Self>) {
|
||||
self._mem = None;
|
||||
self.fd = None;
|
||||
let img = self.img.take().unwrap();
|
||||
let res = self.work.config().result.take().unwrap();
|
||||
complete_async_upload(&img, self.id, res, |data| data.io_job.set(Some(self)));
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuJob for CopyUploadJob {
|
||||
fn work(&mut self) -> &mut dyn CpuWork {
|
||||
&mut self.work
|
||||
}
|
||||
|
||||
fn completed(mut self: Box<Self>) {
|
||||
self._mem = None;
|
||||
let img = self.img.take().unwrap();
|
||||
complete_async_upload(&img, self.id, Ok(()), |data| data.copy_job.set(Some(self)));
|
||||
}
|
||||
}
|
||||
|
||||
fn complete_async_upload(
|
||||
img: &Rc<VulkanImage>,
|
||||
id: u64,
|
||||
res: Result<(), ReadWriteJobError>,
|
||||
store: impl FnOnce(&VulkanShmImageAsyncData),
|
||||
) {
|
||||
img.renderer.pending_cpu_jobs.remove(&id);
|
||||
let VulkanImageMemory::Internal(shm) = &img.ty else {
|
||||
unreachable!();
|
||||
};
|
||||
let data = shm.async_data.as_ref().unwrap();
|
||||
store(data);
|
||||
if let Err(e) = shm.async_upload_copy_buffer_to_image(img, data, res) {
|
||||
data.complete(Err(e));
|
||||
}
|
||||
}
|
||||
|
||||
async fn await_async_upload(
|
||||
id: u64,
|
||||
img: Rc<VulkanImage>,
|
||||
buf: Rc<VulkanCommandBuffer>,
|
||||
_fence: Rc<VulkanFence>,
|
||||
sync_file: SyncFile,
|
||||
) {
|
||||
let res = img.renderer.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);
|
||||
let VulkanImageMemory::Internal(shm) = &img.ty else {
|
||||
unreachable!();
|
||||
};
|
||||
let data = shm.async_data.as_ref().unwrap();
|
||||
data.complete(Ok(()));
|
||||
}
|
||||
|
||||
impl VulkanRenderer {
|
||||
pub fn create_shm_texture(
|
||||
self: &Rc<Self>,
|
||||
|
|
@ -261,6 +622,7 @@ impl VulkanRenderer {
|
|||
stride: i32,
|
||||
data: &[Cell<u8>],
|
||||
for_download: bool,
|
||||
cpu_worker: Option<&Rc<CpuWorker>>,
|
||||
) -> Result<Rc<VulkanImage>, VulkanError> {
|
||||
let Some(shm_info) = &format.shm_info else {
|
||||
return Err(VulkanError::UnsupportedShmFormat(format.name));
|
||||
|
|
@ -283,7 +645,7 @@ impl VulkanRenderer {
|
|||
if width > shm.limits.max_width || height > shm.limits.max_height {
|
||||
return Err(VulkanError::ImageTooLarge);
|
||||
}
|
||||
let size = stride.checked_mul(height).ok_or(VulkanError::ShmOverflow)?;
|
||||
let size = stride.checked_mul(height).ok_or(VulkanError::ShmOverflow)? as u64;
|
||||
let usage = ImageUsageFlags::TRANSFER_SRC
|
||||
| match for_download {
|
||||
true => ImageUsageFlags::COLOR_ATTACHMENT,
|
||||
|
|
@ -335,11 +697,25 @@ impl VulkanRenderer {
|
|||
.create_image_view(&image_view_create_info, None)
|
||||
};
|
||||
let view = view.map_err(VulkanError::CreateImageView)?;
|
||||
let mut async_data = None;
|
||||
if let Some(cpu) = cpu_worker {
|
||||
async_data = Some(VulkanShmImageAsyncData {
|
||||
busy: Cell::new(false),
|
||||
io_job: Default::default(),
|
||||
copy_job: Default::default(),
|
||||
staging: Default::default(),
|
||||
callback: Default::default(),
|
||||
callback_id: Cell::new(0),
|
||||
regions: Default::default(),
|
||||
cpu: cpu.clone(),
|
||||
});
|
||||
}
|
||||
let shm = VulkanShmImage {
|
||||
_size: size as u64,
|
||||
size,
|
||||
stride,
|
||||
_allocation: allocation,
|
||||
shm_info,
|
||||
async_data,
|
||||
};
|
||||
destroy_image.forget();
|
||||
let img = Rc::new(VulkanImage {
|
||||
|
|
@ -352,6 +728,7 @@ impl VulkanRenderer {
|
|||
render_view: None,
|
||||
image,
|
||||
is_undefined: Cell::new(true),
|
||||
contents_are_undefined: Cell::new(true),
|
||||
ty: VulkanImageMemory::Internal(shm),
|
||||
render_ops: Default::default(),
|
||||
bridge: None,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
use {
|
||||
crate::gfx_apis::vulkan::{
|
||||
allocator::{VulkanAllocation, VulkanAllocator},
|
||||
device::VulkanDevice,
|
||||
util::OnDrop,
|
||||
VulkanError,
|
||||
crate::{
|
||||
cpu_worker::CpuWorker,
|
||||
gfx_apis::vulkan::{
|
||||
allocator::{VulkanAllocation, VulkanAllocator},
|
||||
device::VulkanDevice,
|
||||
renderer::VulkanRenderer,
|
||||
VulkanError,
|
||||
},
|
||||
utils::on_drop::{OnDrop, OnDrop2},
|
||||
},
|
||||
ash::vk::{Buffer, BufferCreateInfo, BufferUsageFlags, MappedMemoryRange},
|
||||
gpu_alloc::UsageFlags,
|
||||
|
|
@ -26,24 +30,8 @@ impl VulkanDevice {
|
|||
download: bool,
|
||||
transient: bool,
|
||||
) -> Result<VulkanStagingBuffer, VulkanError> {
|
||||
let mut vk_usage = BufferUsageFlags::empty();
|
||||
let mut usage = UsageFlags::empty();
|
||||
if upload {
|
||||
vk_usage |= BufferUsageFlags::TRANSFER_SRC;
|
||||
usage |= UsageFlags::UPLOAD;
|
||||
}
|
||||
if download {
|
||||
vk_usage |= BufferUsageFlags::TRANSFER_DST;
|
||||
usage |= UsageFlags::DOWNLOAD;
|
||||
}
|
||||
if transient {
|
||||
usage |= UsageFlags::TRANSIENT;
|
||||
}
|
||||
let buffer = {
|
||||
let create_info = BufferCreateInfo::default().size(size).usage(vk_usage);
|
||||
let buffer = unsafe { self.device.create_buffer(&create_info, None) };
|
||||
buffer.map_err(VulkanError::CreateBuffer)?
|
||||
};
|
||||
let (vk_usage, usage) = get_usage(upload, download, transient);
|
||||
let buffer = self.create_buffer(size, vk_usage)?;
|
||||
let destroy_buffer = OnDrop(|| unsafe { self.device.destroy_buffer(buffer, None) });
|
||||
let memory_requirements = unsafe { self.device.get_buffer_memory_requirements(buffer) };
|
||||
let allocation = allocator.alloc(&memory_requirements, usage, true)?;
|
||||
|
|
@ -62,6 +50,72 @@ impl VulkanDevice {
|
|||
size,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn create_shm_staging(
|
||||
self: &Rc<Self>,
|
||||
renderer: &Rc<VulkanRenderer>,
|
||||
cpu: &Rc<CpuWorker>,
|
||||
size: u64,
|
||||
upload: bool,
|
||||
download: bool,
|
||||
cb: impl FnOnce(Result<VulkanStagingBuffer, VulkanError>) + 'static,
|
||||
) -> Result<(), VulkanError> {
|
||||
let (vk_usage, usage) = get_usage(upload, download, false);
|
||||
let buffer = self.create_buffer(size, vk_usage)?;
|
||||
let memory_requirements = unsafe { self.device.get_buffer_memory_requirements(buffer) };
|
||||
let slf = self.clone();
|
||||
let destroy_buffer =
|
||||
OnDrop2::new(move || unsafe { slf.device.destroy_buffer(buffer, None) });
|
||||
let slf = self.clone();
|
||||
let finish_allocation = move |res| {
|
||||
let allocation: VulkanAllocation = res?;
|
||||
{
|
||||
let res = unsafe {
|
||||
slf.device
|
||||
.bind_buffer_memory(buffer, allocation.memory, allocation.offset)
|
||||
};
|
||||
res.map_err(VulkanError::BindBufferMemory)?;
|
||||
}
|
||||
destroy_buffer.forget();
|
||||
Ok(VulkanStagingBuffer {
|
||||
device: slf.clone(),
|
||||
allocation,
|
||||
buffer,
|
||||
size,
|
||||
})
|
||||
};
|
||||
renderer.shm_allocator.async_alloc(
|
||||
renderer,
|
||||
cpu,
|
||||
memory_requirements,
|
||||
usage,
|
||||
true,
|
||||
move |res| cb(finish_allocation(res)),
|
||||
)
|
||||
}
|
||||
|
||||
fn create_buffer(&self, size: u64, usage: BufferUsageFlags) -> Result<Buffer, VulkanError> {
|
||||
let create_info = BufferCreateInfo::default().size(size).usage(usage);
|
||||
let buffer = unsafe { self.device.create_buffer(&create_info, None) };
|
||||
buffer.map_err(VulkanError::CreateBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_usage(upload: bool, download: bool, transient: bool) -> (BufferUsageFlags, UsageFlags) {
|
||||
let mut vk_usage = BufferUsageFlags::empty();
|
||||
let mut usage = UsageFlags::empty();
|
||||
if upload {
|
||||
vk_usage |= BufferUsageFlags::TRANSFER_SRC;
|
||||
usage |= UsageFlags::UPLOAD;
|
||||
}
|
||||
if download {
|
||||
vk_usage |= BufferUsageFlags::TRANSFER_DST;
|
||||
usage |= UsageFlags::DOWNLOAD;
|
||||
}
|
||||
if transient {
|
||||
usage |= UsageFlags::TRANSIENT;
|
||||
}
|
||||
(vk_usage, usage)
|
||||
}
|
||||
|
||||
impl VulkanStagingBuffer {
|
||||
|
|
@ -70,27 +124,31 @@ impl VulkanStagingBuffer {
|
|||
F: FnOnce(*mut u8, usize) -> T,
|
||||
{
|
||||
let t = f(self.allocation.mem.unwrap(), self.size as usize);
|
||||
let range = self.range();
|
||||
let res = unsafe { self.device.device.flush_mapped_memory_ranges(&[range]) };
|
||||
res.map_err(VulkanError::FlushMemory).map(|_| t)
|
||||
if let Some(mask) = self.allocation.coherency_mask {
|
||||
let range = self.incoherent_range(mask);
|
||||
let res = unsafe { self.device.device.flush_mapped_memory_ranges(&[range]) };
|
||||
res.map_err(VulkanError::FlushMemory)?;
|
||||
}
|
||||
Ok(t)
|
||||
}
|
||||
|
||||
pub fn download<T, F>(&self, f: F) -> Result<T, VulkanError>
|
||||
where
|
||||
F: FnOnce(*const u8, usize) -> T,
|
||||
{
|
||||
let range = self.range();
|
||||
let res = unsafe { self.device.device.invalidate_mapped_memory_ranges(&[range]) };
|
||||
res.map_err(VulkanError::FlushMemory)?;
|
||||
if let Some(mask) = self.allocation.coherency_mask {
|
||||
let range = self.incoherent_range(mask);
|
||||
let res = unsafe { self.device.device.invalidate_mapped_memory_ranges(&[range]) };
|
||||
res.map_err(VulkanError::FlushMemory)?;
|
||||
}
|
||||
Ok(f(self.allocation.mem.unwrap(), self.size as usize))
|
||||
}
|
||||
|
||||
fn range(&self) -> MappedMemoryRange {
|
||||
let atom_mask = self.allocation.allocator.non_coherent_atom_mask;
|
||||
fn incoherent_range(&self, mask: u64) -> MappedMemoryRange {
|
||||
MappedMemoryRange::default()
|
||||
.memory(self.allocation.memory)
|
||||
.offset(self.allocation.offset & !atom_mask)
|
||||
.size((self.allocation.size + atom_mask) & !atom_mask)
|
||||
.offset(self.allocation.offset & !mask)
|
||||
.size((self.allocation.size + mask) & !mask)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
use std::mem;
|
||||
|
||||
pub struct OnDrop<F>(pub F)
|
||||
where
|
||||
F: FnMut() + Copy;
|
||||
|
||||
impl<F: FnMut() + Copy> OnDrop<F> {
|
||||
pub fn forget(self) {
|
||||
mem::forget(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnMut() + Copy> Drop for OnDrop<F> {
|
||||
fn drop(&mut self) {
|
||||
(self.0)();
|
||||
}
|
||||
}
|
||||
|
|
@ -165,11 +165,18 @@ impl JayInput {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_keymap_impl<F>(&self, keymap: &OwnedFd, len: u32, f: F) -> Result<(), JayInputError>
|
||||
fn set_keymap_impl<F>(&self, keymap: &Rc<OwnedFd>, len: u32, f: F) -> Result<(), JayInputError>
|
||||
where
|
||||
F: FnOnce(&Rc<XkbKeymap>) -> Result<(), JayInputError>,
|
||||
{
|
||||
let cm = Rc::new(ClientMem::new(keymap.raw(), len as _, true)?).offset(0);
|
||||
let cm = Rc::new(ClientMem::new(
|
||||
keymap,
|
||||
len as _,
|
||||
true,
|
||||
Some(&self.client),
|
||||
None,
|
||||
)?)
|
||||
.offset(0);
|
||||
let mut map = vec![];
|
||||
cm.read(&mut map)?;
|
||||
self.or_error(|| {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use {
|
|||
ifs::wl_surface::WlSurface,
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
rect::Rect,
|
||||
rect::{Rect, Region},
|
||||
theme::Color,
|
||||
utils::errorfmt::ErrorFmt,
|
||||
video::dmabuf::DmaBuf,
|
||||
|
|
@ -22,7 +22,7 @@ use {
|
|||
|
||||
pub enum WlBufferStorage {
|
||||
Shm {
|
||||
mem: ClientMemOffset,
|
||||
mem: Rc<ClientMemOffset>,
|
||||
stride: i32,
|
||||
},
|
||||
Dmabuf {
|
||||
|
|
@ -41,6 +41,7 @@ pub struct WlBuffer {
|
|||
pub dmabuf: Option<DmaBuf>,
|
||||
render_ctx_version: Cell<u32>,
|
||||
pub storage: RefCell<Option<WlBufferStorage>>,
|
||||
shm: bool,
|
||||
pub color: Option<Color>,
|
||||
width: i32,
|
||||
height: i32,
|
||||
|
|
@ -52,6 +53,10 @@ impl WlBuffer {
|
|||
self.destroyed.get()
|
||||
}
|
||||
|
||||
pub fn is_shm(&self) -> bool {
|
||||
self.shm
|
||||
}
|
||||
|
||||
pub fn new_dmabuf(
|
||||
id: WlBufferId,
|
||||
client: &Rc<Client>,
|
||||
|
|
@ -76,6 +81,7 @@ impl WlBuffer {
|
|||
tex: None,
|
||||
fb: None,
|
||||
})),
|
||||
shm: false,
|
||||
tracker: Default::default(),
|
||||
color: None,
|
||||
}
|
||||
|
|
@ -100,7 +106,7 @@ impl WlBuffer {
|
|||
if required > mem.len() as u64 {
|
||||
return Err(WlBufferError::OutOfBounds);
|
||||
}
|
||||
let mem = mem.offset(offset);
|
||||
let mem = Rc::new(mem.offset(offset));
|
||||
let min_row_size = width as u64 * shm_info.bpp as u64;
|
||||
if (stride as u64) < min_row_size {
|
||||
return Err(WlBufferError::StrideTooSmall);
|
||||
|
|
@ -114,6 +120,7 @@ impl WlBuffer {
|
|||
dmabuf: None,
|
||||
render_ctx_version: Cell::new(client.state.render_ctx_version.get()),
|
||||
storage: RefCell::new(Some(WlBufferStorage::Shm { mem, stride })),
|
||||
shm: true,
|
||||
width,
|
||||
height,
|
||||
tracker: Default::default(),
|
||||
|
|
@ -138,6 +145,7 @@ impl WlBuffer {
|
|||
dmabuf: None,
|
||||
render_ctx_version: Cell::new(client.state.render_ctx_version.get()),
|
||||
storage: RefCell::new(None),
|
||||
shm: false,
|
||||
width: 1,
|
||||
height: 1,
|
||||
tracker: Default::default(),
|
||||
|
|
@ -153,7 +161,7 @@ impl WlBuffer {
|
|||
let had_texture = self.reset_gfx_objects(surface);
|
||||
if had_texture {
|
||||
if let Some(surface) = surface {
|
||||
self.update_texture_or_log(surface, None);
|
||||
self.update_texture_or_log(surface, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -166,7 +174,10 @@ impl WlBuffer {
|
|||
let had_texture = match s {
|
||||
WlBufferStorage::Shm { .. } => {
|
||||
return match surface {
|
||||
Some(s) => s.shm_texture.take().is_some(),
|
||||
Some(s) => {
|
||||
s.shm_textures.back().tex.take();
|
||||
s.shm_textures.front().tex.take().is_some()
|
||||
}
|
||||
None => false,
|
||||
};
|
||||
}
|
||||
|
|
@ -201,24 +212,24 @@ impl WlBuffer {
|
|||
match &*self.storage.borrow() {
|
||||
None => None,
|
||||
Some(s) => match s {
|
||||
WlBufferStorage::Shm { .. } => surface.shm_texture.get(),
|
||||
WlBufferStorage::Shm { .. } => surface
|
||||
.shm_textures
|
||||
.front()
|
||||
.tex
|
||||
.get()
|
||||
.map(|t| t.into_texture()),
|
||||
WlBufferStorage::Dmabuf { tex, .. } => tex.clone(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_texture_or_log(&self, surface: &WlSurface, damage: Option<&[Rect]>) {
|
||||
if let Err(e) = self.update_texture(surface, damage) {
|
||||
pub fn update_texture_or_log(&self, surface: &WlSurface, sync_shm: bool) {
|
||||
if let Err(e) = self.update_texture(surface, sync_shm) {
|
||||
log::warn!("Could not update texture: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
|
||||
fn update_texture(
|
||||
&self,
|
||||
surface: &WlSurface,
|
||||
damage: Option<&[Rect]>,
|
||||
) -> Result<(), WlBufferError> {
|
||||
let old_shm_texture = surface.shm_texture.take();
|
||||
fn update_texture(&self, surface: &WlSurface, sync_shm: bool) -> Result<(), WlBufferError> {
|
||||
let storage = &mut *self.storage.borrow_mut();
|
||||
let storage = match storage {
|
||||
Some(s) => s,
|
||||
|
|
@ -226,19 +237,19 @@ impl WlBuffer {
|
|||
};
|
||||
match storage {
|
||||
WlBufferStorage::Shm { mem, stride } => {
|
||||
if let Some(ctx) = self.client.state.render_ctx.get() {
|
||||
let tex = mem.access(|mem| {
|
||||
ctx.shmem_texture(
|
||||
old_shm_texture,
|
||||
mem,
|
||||
if sync_shm {
|
||||
if let Some(ctx) = self.client.state.render_ctx.get() {
|
||||
let tex = ctx.async_shmem_texture(
|
||||
self.format,
|
||||
self.width,
|
||||
self.height,
|
||||
*stride,
|
||||
damage,
|
||||
)
|
||||
})??;
|
||||
surface.shm_texture.set(Some(tex));
|
||||
&self.client.state.cpu_worker,
|
||||
)?;
|
||||
mem.access(|mem| tex.clone().sync_upload(mem, Region::new2(self.rect)))??;
|
||||
surface.shm_textures.front().tex.set(Some(tex));
|
||||
surface.shm_textures.front().damage.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
WlBufferStorage::Dmabuf { img, tex, .. } => {
|
||||
|
|
|
|||
|
|
@ -56,9 +56,15 @@ impl ZwpVirtualKeyboardV1RequestHandler for ZwpVirtualKeyboardV1 {
|
|||
if req.size > MAX_SIZE {
|
||||
return Err(ZwpVirtualKeyboardV1Error::OversizedKeymap);
|
||||
}
|
||||
let client_mem = ClientMem::new(req.fd.raw(), req.size as usize - 1, true)
|
||||
.map(Rc::new)
|
||||
.map_err(ZwpVirtualKeyboardV1Error::MapKeymap)?;
|
||||
let client_mem = ClientMem::new(
|
||||
&req.fd,
|
||||
req.size as usize - 1,
|
||||
true,
|
||||
Some(&self.client),
|
||||
None,
|
||||
)
|
||||
.map(Rc::new)
|
||||
.map_err(ZwpVirtualKeyboardV1Error::MapKeymap)?;
|
||||
let mut map = vec![];
|
||||
client_mem
|
||||
.offset(0)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,13 @@ impl WlShmPool {
|
|||
Ok(Self {
|
||||
id,
|
||||
client: client.clone(),
|
||||
mem: CloneCell::new(Rc::new(ClientMem::new(fd.raw(), len, false)?)),
|
||||
mem: CloneCell::new(Rc::new(ClientMem::new(
|
||||
&fd,
|
||||
len,
|
||||
false,
|
||||
Some(client),
|
||||
Some(&client.state.cpu_worker),
|
||||
)?)),
|
||||
fd,
|
||||
tracker: Default::default(),
|
||||
version,
|
||||
|
|
@ -82,9 +88,11 @@ impl WlShmPoolRequestHandler for WlShmPool {
|
|||
return Err(WlShmPoolError::CannotShrink);
|
||||
}
|
||||
self.mem.set(Rc::new(ClientMem::new(
|
||||
self.fd.raw(),
|
||||
&self.fd,
|
||||
req.size as usize,
|
||||
false,
|
||||
Some(&self.client),
|
||||
Some(&self.client.state.cpu_worker),
|
||||
)?));
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ use {
|
|||
drm_feedback::DrmFeedback,
|
||||
fixed::Fixed,
|
||||
gfx_api::{
|
||||
AcquireSync, BufferResv, BufferResvUser, GfxTexture, ReleaseSync, SampleRect, SyncFile,
|
||||
AcquireSync, AsyncShmGfxTexture, BufferResv, BufferResvUser, GfxError, ReleaseSync,
|
||||
SampleRect, SyncFile,
|
||||
},
|
||||
ifs::{
|
||||
wl_buffer::WlBuffer,
|
||||
|
|
@ -59,16 +60,16 @@ use {
|
|||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
rect::{Rect, Region},
|
||||
rect::{DamageQueue, Rect, Region},
|
||||
renderer::Renderer,
|
||||
tree::{
|
||||
ContainerNode, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, NodeVisitorBase,
|
||||
OutputNode, OutputNodeId, PlaceholderNode, ToplevelNode,
|
||||
},
|
||||
utils::{
|
||||
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
|
||||
linkedlist::LinkedList, numcell::NumCell, smallmap::SmallMap,
|
||||
transform_ext::TransformExt,
|
||||
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||
double_buffered::DoubleBuffered, errorfmt::ErrorFmt, linkedlist::LinkedList,
|
||||
numcell::NumCell, smallmap::SmallMap, transform_ext::TransformExt,
|
||||
},
|
||||
video::{
|
||||
dmabuf::DMA_BUF_SYNC_READ,
|
||||
|
|
@ -249,6 +250,11 @@ impl BufferResv for SurfaceBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct SurfaceShmTexture {
|
||||
pub tex: CloneCell<Option<Rc<dyn AsyncShmGfxTexture>>>,
|
||||
pub damage: DamageQueue,
|
||||
}
|
||||
|
||||
pub struct WlSurface {
|
||||
pub id: WlSurfaceId,
|
||||
pub node_id: SurfaceNodeId,
|
||||
|
|
@ -271,7 +277,7 @@ pub struct WlSurface {
|
|||
pub buffer: CloneCell<Option<Rc<SurfaceBuffer>>>,
|
||||
buffer_presented: Cell<bool>,
|
||||
buffer_had_frame_request: Cell<bool>,
|
||||
pub shm_texture: CloneCell<Option<Rc<dyn GfxTexture>>>,
|
||||
pub shm_textures: DoubleBuffered<SurfaceShmTexture>,
|
||||
pub buf_x: NumCell<i32>,
|
||||
pub buf_y: NumCell<i32>,
|
||||
pub children: RefCell<Option<Box<ParentData>>>,
|
||||
|
|
@ -584,7 +590,10 @@ impl WlSurface {
|
|||
buffer: Default::default(),
|
||||
buffer_presented: Default::default(),
|
||||
buffer_had_frame_request: Default::default(),
|
||||
shm_texture: Default::default(),
|
||||
shm_textures: DoubleBuffered::new(DamageQueue::new().map(|damage| SurfaceShmTexture {
|
||||
tex: Default::default(),
|
||||
damage,
|
||||
})),
|
||||
buf_x: Default::default(),
|
||||
buf_y: Default::default(),
|
||||
children: Default::default(),
|
||||
|
|
@ -909,7 +918,7 @@ impl WlSurfaceRequestHandler for WlSurface {
|
|||
*children = None;
|
||||
}
|
||||
self.buffer.set(None);
|
||||
self.shm_texture.take();
|
||||
self.reset_shm_textures();
|
||||
if let Some(xwayland_serial) = self.xwayland_serial.get() {
|
||||
self.client
|
||||
.surfaces_by_xwayland_serial
|
||||
|
|
@ -1077,11 +1086,13 @@ impl WlSurface {
|
|||
old_raw_size = Some(buffer.buffer.rect);
|
||||
}
|
||||
if let Some(buffer) = buffer_change {
|
||||
let damage = match pending.damage_full || pending.surface_damage.is_not_empty() {
|
||||
true => None,
|
||||
false => Some(&pending.buffer_damage[..]),
|
||||
};
|
||||
buffer.update_texture_or_log(self, damage);
|
||||
if buffer.is_shm() {
|
||||
self.shm_textures.flip();
|
||||
self.shm_textures.front().damage.clear();
|
||||
} else {
|
||||
self.reset_shm_textures();
|
||||
}
|
||||
buffer.update_texture_or_log(self, false);
|
||||
let (sync, release_sync) = match pending.explicit_sync {
|
||||
false => (AcquireSync::Implicit, ReleaseSync::Implicit),
|
||||
true => (AcquireSync::Unnecessary, ReleaseSync::Explicit),
|
||||
|
|
@ -1103,7 +1114,7 @@ impl WlSurface {
|
|||
self.buffer_had_frame_request.set(false);
|
||||
}
|
||||
} else {
|
||||
self.shm_texture.take();
|
||||
self.reset_shm_textures();
|
||||
self.buf_x.set(0);
|
||||
self.buf_y.set(0);
|
||||
for (_, cursor) in &self.cursors {
|
||||
|
|
@ -1321,6 +1332,13 @@ impl WlSurface {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn reset_shm_textures(&self) {
|
||||
for tex in &*self.shm_textures {
|
||||
tex.tex.take();
|
||||
tex.damage.clear();
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_damage(&self, pending: &PendingState) {
|
||||
let bounds = self.toplevel.get().map(|tl| tl.node_absolute_position());
|
||||
let pos = self.buffer_abs_pos.get();
|
||||
|
|
@ -1915,6 +1933,12 @@ pub enum WlSurfaceError {
|
|||
UnexpectedSyncPoints,
|
||||
#[error("The supplied region is invalid")]
|
||||
InvalidRect,
|
||||
#[error("There is no render context")]
|
||||
NoRenderContext,
|
||||
#[error("Could not create a shm texture")]
|
||||
CreateAsyncShmTexture(#[source] GfxError),
|
||||
#[error("Could not prepare upload to a shm texture")]
|
||||
PrepareAsyncUpload(#[source] GfxError),
|
||||
}
|
||||
efrom!(WlSurfaceError, ClientError);
|
||||
efrom!(WlSurfaceError, XdgSurfaceError);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
use {
|
||||
crate::{
|
||||
ifs::wl_surface::{PendingState, WlSurface, WlSurfaceError},
|
||||
gfx_api::{AsyncShmGfxTextureCallback, GfxError, PendingShmUpload},
|
||||
ifs::{
|
||||
wl_buffer::WlBufferStorage,
|
||||
wl_surface::{PendingState, WlSurface, WlSurfaceError},
|
||||
},
|
||||
utils::{
|
||||
clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap,
|
||||
|
|
@ -14,13 +18,14 @@ use {
|
|||
DrmError,
|
||||
},
|
||||
},
|
||||
isnt::std_1::primitive::IsntSliceExt,
|
||||
isnt::std_1::{primitive::IsntSliceExt, vec::IsntVecExt},
|
||||
smallvec::SmallVec,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
ops::DerefMut,
|
||||
rc::Rc,
|
||||
slice,
|
||||
},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
|
@ -76,6 +81,8 @@ pub enum CommitTimelineError {
|
|||
Wait(#[source] DrmError),
|
||||
#[error("The client has too many pending commits")]
|
||||
Depth,
|
||||
#[error("Could not upload a shm texture")]
|
||||
ShmUpload(#[source] GfxError),
|
||||
}
|
||||
|
||||
impl CommitTimelines {
|
||||
|
|
@ -119,6 +126,7 @@ fn break_loops(list: &LinkedList<Entry>) {
|
|||
entry.link.take();
|
||||
if let EntryKind::Commit(c) = &entry.kind {
|
||||
c.wait_handles.take();
|
||||
*c.shm_upload.borrow_mut() = ShmUploadState::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -145,7 +153,9 @@ impl CommitTimeline {
|
|||
) -> Result<(), CommitTimelineError> {
|
||||
let mut points = SmallVec::new();
|
||||
consume_acquire_points(pending, &mut points);
|
||||
if points.is_empty() && self.own_timeline.entries.is_empty() {
|
||||
let mut pending_uploads = 0;
|
||||
count_shm_uploads(pending, &mut pending_uploads);
|
||||
if points.is_empty() && pending_uploads == 0 && self.own_timeline.entries.is_empty() {
|
||||
return surface
|
||||
.apply_state(pending)
|
||||
.map_err(CommitTimelineError::ImmediateCommit);
|
||||
|
|
@ -162,23 +172,35 @@ impl CommitTimeline {
|
|||
pending: RefCell::new(mem::take(pending)),
|
||||
sync_obj: NumCell::new(points.len()),
|
||||
wait_handles: Cell::new(Default::default()),
|
||||
pending_uploads: NumCell::new(pending_uploads),
|
||||
shm_upload: RefCell::new(ShmUploadState::None),
|
||||
}),
|
||||
);
|
||||
if points.is_not_empty() {
|
||||
let mut wait_handles = SmallVec::new();
|
||||
let noderef = Rc::new(noderef);
|
||||
for (sync_obj, point) in points {
|
||||
let handle = self
|
||||
.shared
|
||||
.wfs
|
||||
.wait(&sync_obj, point, true, noderef.clone())
|
||||
.map_err(CommitTimelineError::RegisterWait)?;
|
||||
wait_handles.push(handle);
|
||||
}
|
||||
let mut needs_flush = false;
|
||||
if points.is_not_empty() || pending_uploads > 0 {
|
||||
let noderef = Rc::new(noderef.clone());
|
||||
let EntryKind::Commit(commit) = &noderef.kind else {
|
||||
unreachable!();
|
||||
};
|
||||
commit.wait_handles.set(wait_handles);
|
||||
if points.is_not_empty() {
|
||||
let mut wait_handles = SmallVec::new();
|
||||
for (sync_obj, point) in points {
|
||||
let handle = self
|
||||
.shared
|
||||
.wfs
|
||||
.wait(&sync_obj, point, true, noderef.clone())
|
||||
.map_err(CommitTimelineError::RegisterWait)?;
|
||||
wait_handles.push(handle);
|
||||
}
|
||||
commit.wait_handles.set(wait_handles);
|
||||
}
|
||||
if pending_uploads > 0 {
|
||||
*commit.shm_upload.borrow_mut() = ShmUploadState::Todo(noderef.clone());
|
||||
needs_flush = true;
|
||||
}
|
||||
}
|
||||
if needs_flush && noderef.prev().is_none() {
|
||||
flush_from(noderef.clone()).map_err(CommitTimelineError::DelayedCommit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -194,12 +216,33 @@ impl SyncObjWaiter for NodeRef<Entry> {
|
|||
return;
|
||||
}
|
||||
commit.sync_obj.fetch_sub(1);
|
||||
if let Err(e) = flush_from(self.deref().clone()) {
|
||||
flush_commit(&self, commit);
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_commit(node_ref: &NodeRef<Entry>, commit: &Commit) {
|
||||
if let Err(e) = flush_from(node_ref.clone()) {
|
||||
commit
|
||||
.surface
|
||||
.client
|
||||
.error(CommitTimelineError::DelayedCommit(e));
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncShmGfxTextureCallback for NodeRef<Entry> {
|
||||
fn completed(self: Rc<Self>, res: Result<(), GfxError>) {
|
||||
let EntryKind::Commit(commit) = &self.kind else {
|
||||
unreachable!();
|
||||
};
|
||||
if let Err(e) = res {
|
||||
commit
|
||||
.surface
|
||||
.client
|
||||
.error(CommitTimelineError::DelayedCommit(e));
|
||||
.error(CommitTimelineError::ShmUpload(e));
|
||||
return;
|
||||
}
|
||||
commit.pending_uploads.fetch_sub(1);
|
||||
flush_commit(&self, commit);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -216,11 +259,19 @@ enum EntryKind {
|
|||
Gc(CommitTimelineId),
|
||||
}
|
||||
|
||||
enum ShmUploadState {
|
||||
None,
|
||||
Todo(Rc<NodeRef<Entry>>),
|
||||
Scheduled(#[expect(dead_code)] SmallVec<[PendingShmUpload; 1]>),
|
||||
}
|
||||
|
||||
struct Commit {
|
||||
surface: Rc<WlSurface>,
|
||||
pending: RefCell<Box<PendingState>>,
|
||||
sync_obj: NumCell<usize>,
|
||||
wait_handles: Cell<SmallVec<[WaitForSyncObjHandle; 1]>>,
|
||||
pending_uploads: NumCell<usize>,
|
||||
shm_upload: RefCell<ShmUploadState>,
|
||||
}
|
||||
|
||||
fn flush_from(mut point: NodeRef<Entry>) -> Result<(), WlSurfaceError> {
|
||||
|
|
@ -243,7 +294,17 @@ impl NodeRef<Entry> {
|
|||
}
|
||||
match &self.kind {
|
||||
EntryKind::Commit(c) => {
|
||||
let mut has_unmet_dependencies = false;
|
||||
if c.sync_obj.get() > 0 {
|
||||
has_unmet_dependencies = true;
|
||||
}
|
||||
if c.pending_uploads.get() > 0 {
|
||||
check_shm_uploads(c)?;
|
||||
if c.pending_uploads.get() > 0 {
|
||||
has_unmet_dependencies = true;
|
||||
}
|
||||
}
|
||||
if has_unmet_dependencies {
|
||||
return Ok(false);
|
||||
}
|
||||
c.surface.apply_state(c.pending.borrow_mut().deref_mut())?;
|
||||
|
|
@ -266,6 +327,90 @@ impl NodeRef<Entry> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_shm_uploads(c: &Commit) -> Result<(), WlSurfaceError> {
|
||||
let state = &mut *c.shm_upload.borrow_mut();
|
||||
if let ShmUploadState::Todo(node_ref) = state {
|
||||
let mut pending = SmallVec::new();
|
||||
schedule_async_uploads(node_ref, &c.surface, &c.pending.borrow(), &mut pending)?;
|
||||
c.pending_uploads.set(pending.len());
|
||||
*state = ShmUploadState::Scheduled(pending);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn schedule_async_uploads(
|
||||
node_ref: &Rc<NodeRef<Entry>>,
|
||||
surface: &WlSurface,
|
||||
pending: &PendingState,
|
||||
uploads: &mut SmallVec<[PendingShmUpload; 1]>,
|
||||
) -> Result<(), WlSurfaceError> {
|
||||
if let Some(pending) = schedule_async_upload(node_ref, surface, pending)? {
|
||||
uploads.push(pending);
|
||||
}
|
||||
for ss in pending.subsurfaces.values() {
|
||||
if let Some(state) = &ss.pending.state {
|
||||
schedule_async_uploads(node_ref, &ss.subsurface.surface, state, uploads)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn schedule_async_upload(
|
||||
node_ref: &Rc<NodeRef<Entry>>,
|
||||
surface: &WlSurface,
|
||||
pending: &PendingState,
|
||||
) -> Result<Option<PendingShmUpload>, WlSurfaceError> {
|
||||
let Some(Some(buf)) = &pending.buffer else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(WlBufferStorage::Shm { mem, stride, .. }) = &*buf.storage.borrow() else {
|
||||
return Ok(None);
|
||||
};
|
||||
let back = surface.shm_textures.back();
|
||||
let mut back_tex_opt = back.tex.get();
|
||||
if let Some(back_tex) = &back_tex_opt {
|
||||
if !back_tex.compatible_with(buf.format, buf.rect.width(), buf.rect.height(), *stride) {
|
||||
back_tex_opt = None;
|
||||
}
|
||||
}
|
||||
let damage_full = || {
|
||||
back.damage.clear();
|
||||
back.damage.damage(slice::from_ref(&buf.rect));
|
||||
};
|
||||
let back_tex = match back_tex_opt {
|
||||
Some(b) => {
|
||||
if pending.damage_full || pending.surface_damage.is_not_empty() {
|
||||
damage_full();
|
||||
} else {
|
||||
back.damage.damage(&pending.buffer_damage);
|
||||
}
|
||||
b
|
||||
}
|
||||
None => {
|
||||
damage_full();
|
||||
let state = &surface.client.state;
|
||||
let ctx = state
|
||||
.render_ctx
|
||||
.get()
|
||||
.ok_or(WlSurfaceError::NoRenderContext)?;
|
||||
let back_tex = ctx
|
||||
.async_shmem_texture(
|
||||
buf.format,
|
||||
buf.rect.width(),
|
||||
buf.rect.height(),
|
||||
*stride,
|
||||
&state.cpu_worker,
|
||||
)
|
||||
.map_err(WlSurfaceError::CreateAsyncShmTexture)?;
|
||||
back.tex.set(Some(back_tex.clone()));
|
||||
back_tex
|
||||
}
|
||||
};
|
||||
back_tex
|
||||
.async_upload(node_ref.clone(), mem, back.damage.get())
|
||||
.map_err(WlSurfaceError::PrepareAsyncUpload)
|
||||
}
|
||||
|
||||
type Point = (Rc<SyncObj>, SyncObjPoint);
|
||||
|
||||
fn consume_acquire_points(pending: &mut PendingState, points: &mut SmallVec<[Point; 1]>) {
|
||||
|
|
@ -301,3 +446,16 @@ fn set_effective_timeline(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn count_shm_uploads(pending: &PendingState, count: &mut usize) {
|
||||
if let Some(Some(buffer)) = &pending.buffer {
|
||||
if buffer.is_shm() {
|
||||
*count += 1;
|
||||
}
|
||||
}
|
||||
for ss in pending.subsurfaces.values() {
|
||||
if let Some(state) = &ss.pending.state {
|
||||
count_shm_uploads(state, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use {
|
|||
io_uring::{
|
||||
ops::{
|
||||
accept::AcceptTask, async_cancel::AsyncCancelTask, connect::ConnectTask,
|
||||
poll::PollTask, read_write::ReadWriteTask, recvmsg::RecvmsgTask,
|
||||
poll::PollTask, read_write::ReadWriteTask,
|
||||
read_write_no_cancel::ReadWriteNoCancelTask, recvmsg::RecvmsgTask,
|
||||
sendmsg::SendmsgTask, timeout::TimeoutTask, timeout_link::TimeoutLinkTask,
|
||||
},
|
||||
pending_result::PendingResults,
|
||||
|
|
@ -22,7 +23,6 @@ use {
|
|||
copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt,
|
||||
mmap::{mmap, Mmapped},
|
||||
numcell::NumCell,
|
||||
oserror::OsError,
|
||||
ptr_ext::{MutPtrExt, PtrExt},
|
||||
stack::Stack,
|
||||
|
|
@ -206,6 +206,7 @@ impl IoUring {
|
|||
tasks: Default::default(),
|
||||
pending_results: Default::default(),
|
||||
cached_read_writes: Default::default(),
|
||||
cached_read_writes_no_cancel: Default::default(),
|
||||
cached_cancels: Default::default(),
|
||||
cached_polls: Default::default(),
|
||||
cached_sendmsg: Default::default(),
|
||||
|
|
@ -229,6 +230,10 @@ impl IoUring {
|
|||
self.ring.kill();
|
||||
res
|
||||
}
|
||||
|
||||
pub fn cancel(&self, id: IoUringTaskId) {
|
||||
self.ring.cancel_task(id);
|
||||
}
|
||||
}
|
||||
|
||||
struct IoUringData {
|
||||
|
|
@ -254,14 +259,15 @@ struct IoUringData {
|
|||
|
||||
cqes_consumed: AsyncEvent,
|
||||
|
||||
next: NumCell<u64>,
|
||||
to_encode: SyncQueue<u64>,
|
||||
pending_in_kernel: CopyHashMap<u64, ()>,
|
||||
tasks: CopyHashMap<u64, Box<dyn Task>>,
|
||||
next: IoUringTaskIds,
|
||||
to_encode: SyncQueue<IoUringTaskId>,
|
||||
pending_in_kernel: CopyHashMap<IoUringTaskId, ()>,
|
||||
tasks: CopyHashMap<IoUringTaskId, Box<dyn Task>>,
|
||||
|
||||
pending_results: PendingResults,
|
||||
|
||||
cached_read_writes: Stack<Box<ReadWriteTask>>,
|
||||
cached_read_writes_no_cancel: Stack<Box<ReadWriteNoCancelTask>>,
|
||||
cached_cancels: Stack<Box<AsyncCancelTask>>,
|
||||
cached_polls: Stack<Box<PollTask>>,
|
||||
cached_sendmsg: Stack<Box<SendmsgTask>>,
|
||||
|
|
@ -276,7 +282,7 @@ struct IoUringData {
|
|||
}
|
||||
|
||||
unsafe trait Task {
|
||||
fn id(&self) -> u64;
|
||||
fn id(&self) -> IoUringTaskId;
|
||||
fn complete(self: Box<Self>, ring: &IoUringData, res: i32);
|
||||
fn encode(&self, sqe: &mut io_uring_sqe);
|
||||
|
||||
|
|
@ -347,8 +353,9 @@ impl IoUringData {
|
|||
let entry = self.cqmap.deref()[idx].get();
|
||||
head = head.wrapping_add(1);
|
||||
self.cqhead.deref().store(head, Release);
|
||||
if let Some(pending) = self.tasks.remove(&entry.user_data) {
|
||||
self.pending_in_kernel.remove(&entry.user_data);
|
||||
let id = IoUringTaskId(entry.user_data);
|
||||
if let Some(pending) = self.tasks.remove(&id) {
|
||||
self.pending_in_kernel.remove(&id);
|
||||
pending.complete(self, entry.res);
|
||||
}
|
||||
}
|
||||
|
|
@ -384,7 +391,7 @@ impl IoUringData {
|
|||
let sqe = self.sqesmap.deref()[idx].get().deref_mut();
|
||||
self.sqmap.deref()[idx].set(idx as _);
|
||||
*sqe = Default::default();
|
||||
sqe.user_data = id;
|
||||
sqe.user_data = id.raw();
|
||||
task.encode(sqe);
|
||||
if has_timeout {
|
||||
sqe.flags |= IOSQE_IO_LINK;
|
||||
|
|
@ -404,11 +411,11 @@ impl IoUringData {
|
|||
}
|
||||
}
|
||||
|
||||
fn id_raw(&self) -> u64 {
|
||||
self.next.fetch_add(1)
|
||||
fn id_raw(&self) -> IoUringTaskId {
|
||||
self.next.next()
|
||||
}
|
||||
|
||||
fn cancel_task(&self, id: u64) {
|
||||
fn cancel_task(&self, id: IoUringTaskId) {
|
||||
if !self.tasks.contains(&id) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -466,8 +473,17 @@ impl IoUringData {
|
|||
}
|
||||
}
|
||||
|
||||
linear_ids!(IoUringTaskIds, IoUringTaskId, u64);
|
||||
|
||||
#[expect(clippy::derivable_impls)]
|
||||
impl Default for IoUringTaskId {
|
||||
fn default() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
|
||||
struct Cancellable<'a> {
|
||||
id: u64,
|
||||
id: IoUringTaskId,
|
||||
data: &'a IoUringData,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ pub mod async_cancel;
|
|||
pub mod connect;
|
||||
pub mod poll;
|
||||
pub mod read_write;
|
||||
pub mod read_write_no_cancel;
|
||||
pub mod recvmsg;
|
||||
pub mod sendmsg;
|
||||
pub mod timeout;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use {
|
|||
crate::io_uring::{
|
||||
pending_result::PendingResult,
|
||||
sys::{io_uring_sqe, IORING_OP_ACCEPT},
|
||||
IoUring, IoUringData, IoUringError, Task, TaskResultExt,
|
||||
IoUring, IoUringData, IoUringError, IoUringTaskId, Task, TaskResultExt,
|
||||
},
|
||||
std::rc::Rc,
|
||||
uapi::{c, OwnedFd},
|
||||
|
|
@ -39,14 +39,14 @@ struct Data {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct AcceptTask {
|
||||
id: u64,
|
||||
id: IoUringTaskId,
|
||||
fd: i32,
|
||||
flags: u32,
|
||||
data: Option<Data>,
|
||||
}
|
||||
|
||||
unsafe impl Task for AcceptTask {
|
||||
fn id(&self) -> u64 {
|
||||
fn id(&self) -> IoUringTaskId {
|
||||
self.id
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use {
|
|||
crate::{
|
||||
io_uring::{
|
||||
sys::{io_uring_sqe, IORING_OP_ASYNC_CANCEL},
|
||||
IoUringData, Task,
|
||||
IoUringData, IoUringTaskId, Task,
|
||||
},
|
||||
utils::errorfmt::ErrorFmt,
|
||||
},
|
||||
|
|
@ -11,12 +11,12 @@ use {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct AsyncCancelTask {
|
||||
id: u64,
|
||||
target: u64,
|
||||
id: IoUringTaskId,
|
||||
target: IoUringTaskId,
|
||||
}
|
||||
|
||||
impl IoUringData {
|
||||
pub fn cancel_task_in_kernel(&self, target: u64) {
|
||||
pub fn cancel_task_in_kernel(&self, target: IoUringTaskId) {
|
||||
let id = self.id_raw();
|
||||
let mut task = self.cached_cancels.pop().unwrap_or_default();
|
||||
task.id = id;
|
||||
|
|
@ -26,7 +26,7 @@ impl IoUringData {
|
|||
}
|
||||
|
||||
unsafe impl Task for AsyncCancelTask {
|
||||
fn id(&self) -> u64 {
|
||||
fn id(&self) -> IoUringTaskId {
|
||||
self.id
|
||||
}
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ unsafe impl Task for AsyncCancelTask {
|
|||
|
||||
fn encode(&self, sqe: &mut io_uring_sqe) {
|
||||
sqe.opcode = IORING_OP_ASYNC_CANCEL;
|
||||
sqe.u2.addr = self.target;
|
||||
sqe.u2.addr = self.target.raw();
|
||||
}
|
||||
|
||||
fn is_cancel(&self) -> bool {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use {
|
|||
crate::io_uring::{
|
||||
pending_result::PendingResult,
|
||||
sys::{io_uring_sqe, IORING_OP_CONNECT},
|
||||
IoUring, IoUringData, IoUringError, Task, TaskResultExt,
|
||||
IoUring, IoUringData, IoUringError, IoUringTaskId, Task, TaskResultExt,
|
||||
},
|
||||
std::{mem, ptr, rc::Rc},
|
||||
uapi::{c, OwnedFd, SockAddr},
|
||||
|
|
@ -37,7 +37,7 @@ struct Data {
|
|||
}
|
||||
|
||||
pub struct ConnectTask {
|
||||
id: u64,
|
||||
id: IoUringTaskId,
|
||||
fd: i32,
|
||||
sockaddr: c::sockaddr_storage,
|
||||
addrlen: u64,
|
||||
|
|
@ -47,7 +47,7 @@ pub struct ConnectTask {
|
|||
impl Default for ConnectTask {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
id: 0,
|
||||
id: Default::default(),
|
||||
fd: 0,
|
||||
sockaddr: uapi::pod_zeroed(),
|
||||
addrlen: 0,
|
||||
|
|
@ -57,7 +57,7 @@ impl Default for ConnectTask {
|
|||
}
|
||||
|
||||
unsafe impl Task for ConnectTask {
|
||||
fn id(&self) -> u64 {
|
||||
fn id(&self) -> IoUringTaskId {
|
||||
self.id
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use {
|
|||
ops::TaskResult,
|
||||
pending_result::PendingResult,
|
||||
sys::{io_uring_sqe, IORING_OP_POLL_ADD},
|
||||
IoUring, IoUringData, IoUringError, Task, TaskResultExt,
|
||||
IoUring, IoUringData, IoUringError, IoUringTaskId, Task, TaskResultExt,
|
||||
},
|
||||
std::rc::Rc,
|
||||
uapi::{c, OwnedFd},
|
||||
|
|
@ -45,14 +45,14 @@ struct Data {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct PollTask {
|
||||
id: u64,
|
||||
id: IoUringTaskId,
|
||||
events: u16,
|
||||
fd: i32,
|
||||
data: Option<Data>,
|
||||
}
|
||||
|
||||
unsafe impl Task for PollTask {
|
||||
fn id(&self) -> u64 {
|
||||
fn id(&self) -> IoUringTaskId {
|
||||
self.id
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use {
|
|||
io_uring::{
|
||||
pending_result::PendingResult,
|
||||
sys::{io_uring_sqe, IORING_OP_READ, IORING_OP_WRITE},
|
||||
IoUring, IoUringData, IoUringError, Task, TaskResultExt,
|
||||
IoUring, IoUringData, IoUringError, IoUringTaskId, Task, TaskResultExt,
|
||||
},
|
||||
time::Time,
|
||||
utils::buf::Buf,
|
||||
|
|
@ -66,7 +66,7 @@ struct ReadWriteTaskData {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct ReadWriteTask {
|
||||
id: u64,
|
||||
id: IoUringTaskId,
|
||||
has_timeout: bool,
|
||||
fd: c::c_int,
|
||||
buf: usize,
|
||||
|
|
@ -76,7 +76,7 @@ pub struct ReadWriteTask {
|
|||
}
|
||||
|
||||
unsafe impl Task for ReadWriteTask {
|
||||
fn id(&self) -> u64 {
|
||||
fn id(&self) -> IoUringTaskId {
|
||||
self.id
|
||||
}
|
||||
|
||||
|
|
|
|||
137
src/io_uring/ops/read_write_no_cancel.rs
Normal file
137
src/io_uring/ops/read_write_no_cancel.rs
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use {
|
||||
crate::{
|
||||
io_uring::{
|
||||
pending_result::PendingResult,
|
||||
sys::{io_uring_sqe, IORING_OP_READ, IORING_OP_WRITE},
|
||||
IoUring, IoUringData, IoUringError, IoUringTaskId, Task, TaskResultExt,
|
||||
},
|
||||
time::Time,
|
||||
utils::on_drop::OnDrop,
|
||||
},
|
||||
uapi::{c, Fd},
|
||||
};
|
||||
|
||||
impl IoUring {
|
||||
pub async fn read_no_cancel(
|
||||
&self,
|
||||
fd: Fd,
|
||||
offset: usize,
|
||||
buf: &mut [u8],
|
||||
cancel: impl FnOnce(IoUringTaskId),
|
||||
) -> Result<usize, IoUringError> {
|
||||
self.perform_no_cancel(
|
||||
fd,
|
||||
offset,
|
||||
buf.as_mut_ptr(),
|
||||
buf.len(),
|
||||
None,
|
||||
IORING_OP_READ,
|
||||
cancel,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn write_no_cancel(
|
||||
&self,
|
||||
fd: Fd,
|
||||
offset: usize,
|
||||
buf: &[u8],
|
||||
timeout: Option<Time>,
|
||||
cancel: impl FnOnce(IoUringTaskId),
|
||||
) -> Result<usize, IoUringError> {
|
||||
self.perform_no_cancel(
|
||||
fd,
|
||||
offset,
|
||||
buf.as_ptr() as _,
|
||||
buf.len(),
|
||||
timeout,
|
||||
IORING_OP_WRITE,
|
||||
cancel,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn perform_no_cancel(
|
||||
&self,
|
||||
fd: Fd,
|
||||
offset: usize,
|
||||
buf: *mut u8,
|
||||
len: usize,
|
||||
timeout: Option<Time>,
|
||||
opcode: u8,
|
||||
cancel: impl FnOnce(IoUringTaskId),
|
||||
) -> Result<usize, IoUringError> {
|
||||
self.ring.check_destroyed()?;
|
||||
let id = self.ring.id();
|
||||
let pr = self.ring.pending_results.acquire();
|
||||
{
|
||||
let mut pw = self
|
||||
.ring
|
||||
.cached_read_writes_no_cancel
|
||||
.pop()
|
||||
.unwrap_or_default();
|
||||
pw.opcode = opcode;
|
||||
pw.id = id.id;
|
||||
pw.has_timeout = timeout.is_some();
|
||||
pw.fd = fd.raw();
|
||||
pw.offset = offset;
|
||||
pw.buf = buf as _;
|
||||
pw.len = len;
|
||||
pw.data = Some(ReadWriteTaskData { res: pr.clone() });
|
||||
self.ring.schedule(pw);
|
||||
if let Some(time) = timeout {
|
||||
self.schedule_timeout_link(time);
|
||||
}
|
||||
}
|
||||
let panic = OnDrop(|| panic!("Operation cannot be cancelled from userspace"));
|
||||
cancel(id.id);
|
||||
let res = Ok(pr.await.map(|v| v as usize)).merge();
|
||||
panic.forget();
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
struct ReadWriteTaskData {
|
||||
res: PendingResult,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ReadWriteNoCancelTask {
|
||||
id: IoUringTaskId,
|
||||
has_timeout: bool,
|
||||
fd: c::c_int,
|
||||
offset: usize,
|
||||
buf: usize,
|
||||
len: usize,
|
||||
data: Option<ReadWriteTaskData>,
|
||||
opcode: u8,
|
||||
}
|
||||
|
||||
unsafe impl Task for ReadWriteNoCancelTask {
|
||||
fn id(&self) -> IoUringTaskId {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn complete(mut self: Box<Self>, ring: &IoUringData, res: i32) {
|
||||
if let Some(data) = self.data.take() {
|
||||
data.res.complete(res);
|
||||
}
|
||||
ring.cached_read_writes_no_cancel.push(self);
|
||||
}
|
||||
|
||||
fn encode(&self, sqe: &mut io_uring_sqe) {
|
||||
sqe.opcode = self.opcode;
|
||||
sqe.fd = self.fd as _;
|
||||
sqe.u1.off = self.offset as _;
|
||||
sqe.u2.addr = self.buf as _;
|
||||
sqe.u3.rw_flags = 0;
|
||||
sqe.len = self.len as _;
|
||||
}
|
||||
|
||||
fn has_timeout(&self) -> bool {
|
||||
self.has_timeout
|
||||
}
|
||||
}
|
||||
50
src/io_uring/ops/read_write_no_cancel/tests.rs
Normal file
50
src/io_uring/ops/read_write_no_cancel/tests.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::AsyncEngine,
|
||||
io_uring::{IoUring, IoUringError},
|
||||
utils::{oserror::OsError, queue::AsyncQueue},
|
||||
wheel::Wheel,
|
||||
},
|
||||
std::rc::Rc,
|
||||
uapi::c::ECANCELED,
|
||||
};
|
||||
|
||||
fn cancel(timeout: bool) {
|
||||
let eng = AsyncEngine::new();
|
||||
let ring = IoUring::new(&eng, 32).unwrap();
|
||||
let ring2 = ring.clone();
|
||||
let ring3 = ring.clone();
|
||||
let wheel = Wheel::new(&eng, &ring).unwrap();
|
||||
let queue = Rc::new(AsyncQueue::new());
|
||||
let queue2 = queue.clone();
|
||||
let _fut1 = eng.spawn(async move {
|
||||
let (read, _write) = uapi::pipe().unwrap();
|
||||
let mut buf = [10];
|
||||
let res = ring
|
||||
.read_no_cancel(read.borrow(), !0, &mut buf, |id| queue.push(id))
|
||||
.await;
|
||||
assert!(matches!(
|
||||
res.unwrap_err(),
|
||||
IoUringError::OsError(OsError(ECANCELED))
|
||||
));
|
||||
ring.stop();
|
||||
});
|
||||
let _fut2 = eng.spawn(async move {
|
||||
let id = queue2.pop().await;
|
||||
if timeout {
|
||||
wheel.timeout(1).await.unwrap();
|
||||
}
|
||||
ring2.cancel(id);
|
||||
});
|
||||
ring3.run().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_in_kernel() {
|
||||
cancel(true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_in_userspace() {
|
||||
cancel(true);
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ use {
|
|||
io_uring::{
|
||||
pending_result::PendingResult,
|
||||
sys::{io_uring_sqe, IORING_OP_RECVMSG},
|
||||
IoUring, IoUringData, IoUringError, Task,
|
||||
IoUring, IoUringData, IoUringError, IoUringTaskId, Task,
|
||||
},
|
||||
utils::buf::Buf,
|
||||
},
|
||||
|
|
@ -85,7 +85,7 @@ struct Data {
|
|||
}
|
||||
|
||||
pub struct RecvmsgTask {
|
||||
id: u64,
|
||||
id: IoUringTaskId,
|
||||
fd: c::c_int,
|
||||
bufs: Vec<Buf>,
|
||||
iovecs: Vec<c::iovec>,
|
||||
|
|
@ -97,7 +97,7 @@ pub struct RecvmsgTask {
|
|||
impl Default for RecvmsgTask {
|
||||
fn default() -> Self {
|
||||
RecvmsgTask {
|
||||
id: 0,
|
||||
id: Default::default(),
|
||||
fd: 0,
|
||||
bufs: vec![],
|
||||
iovecs: vec![],
|
||||
|
|
@ -109,7 +109,7 @@ impl Default for RecvmsgTask {
|
|||
}
|
||||
|
||||
unsafe impl Task for RecvmsgTask {
|
||||
fn id(&self) -> u64 {
|
||||
fn id(&self) -> IoUringTaskId {
|
||||
self.id
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use {
|
|||
io_uring::{
|
||||
pending_result::PendingResult,
|
||||
sys::{io_uring_sqe, IORING_OP_SENDMSG},
|
||||
IoUring, IoUringData, IoUringError, Task,
|
||||
IoUring, IoUringData, IoUringError, IoUringTaskId, Task,
|
||||
},
|
||||
time::Time,
|
||||
utils::{buf::Buf, vec_ext::UninitVecExt},
|
||||
|
|
@ -91,7 +91,7 @@ struct SendmsgTaskData {
|
|||
}
|
||||
|
||||
pub struct SendmsgTask {
|
||||
id: u64,
|
||||
id: IoUringTaskId,
|
||||
iovecs: Vec<c::iovec>,
|
||||
msghdr: c::msghdr,
|
||||
bufs: Vec<Buf>,
|
||||
|
|
@ -106,7 +106,7 @@ impl Default for SendmsgTask {
|
|||
fn default() -> Self {
|
||||
unsafe {
|
||||
SendmsgTask {
|
||||
id: 0,
|
||||
id: Default::default(),
|
||||
iovecs: vec![],
|
||||
msghdr: MaybeUninit::zeroed().assume_init(),
|
||||
bufs: vec![],
|
||||
|
|
@ -121,7 +121,7 @@ impl Default for SendmsgTask {
|
|||
}
|
||||
|
||||
unsafe impl Task for SendmsgTask {
|
||||
fn id(&self) -> u64 {
|
||||
fn id(&self) -> IoUringTaskId {
|
||||
self.id
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use {
|
|||
crate::io_uring::{
|
||||
pending_result::PendingResult,
|
||||
sys::{io_uring_sqe, IORING_OP_TIMEOUT, IORING_TIMEOUT_ABS},
|
||||
IoUring, IoUringData, IoUringError, Task,
|
||||
IoUring, IoUringData, IoUringError, IoUringTaskId, Task,
|
||||
},
|
||||
uapi::c,
|
||||
};
|
||||
|
|
@ -16,7 +16,7 @@ pub(super) struct timespec64 {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct TimeoutTask {
|
||||
id: u64,
|
||||
id: IoUringTaskId,
|
||||
timespec: timespec64,
|
||||
pr: Option<PendingResult>,
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ impl IoUring {
|
|||
}
|
||||
|
||||
unsafe impl Task for TimeoutTask {
|
||||
fn id(&self) -> u64 {
|
||||
fn id(&self) -> IoUringTaskId {
|
||||
self.id
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ use crate::{
|
|||
io_uring::{
|
||||
ops::timeout::timespec64,
|
||||
sys::{io_uring_sqe, IORING_OP_LINK_TIMEOUT, IORING_TIMEOUT_ABS},
|
||||
IoUring, IoUringData, Task,
|
||||
IoUring, IoUringData, IoUringTaskId, Task,
|
||||
},
|
||||
time::Time,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TimeoutLinkTask {
|
||||
id: u64,
|
||||
id: IoUringTaskId,
|
||||
timespec: timespec64,
|
||||
}
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ impl IoUring {
|
|||
}
|
||||
|
||||
unsafe impl Task for TimeoutLinkTask {
|
||||
fn id(&self) -> u64 {
|
||||
fn id(&self) -> IoUringTaskId {
|
||||
self.id
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
use {
|
||||
crate::{
|
||||
allocator::{Allocator, AllocatorError, BufferObject, BufferUsage},
|
||||
clientmem::{ClientMemError, ClientMemOffset},
|
||||
cpu_worker::CpuWorker,
|
||||
format::{Format, ARGB8888, XRGB8888},
|
||||
gfx_api::{
|
||||
CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxContext, GfxError, GfxFormat,
|
||||
GfxFramebuffer, GfxImage, GfxTexture, GfxWriteModifier, ResetStatus, SyncFile,
|
||||
AsyncShmGfxTexture, AsyncShmGfxTextureCallback, CopyTexture, FillRect, FramebufferRect,
|
||||
GfxApiOpt, GfxContext, GfxError, GfxFormat, GfxFramebuffer, GfxImage, GfxTexture,
|
||||
GfxWriteModifier, PendingShmUpload, ResetStatus, ShmGfxTexture, SyncFile,
|
||||
},
|
||||
rect::Rect,
|
||||
rect::{Rect, Region},
|
||||
theme::Color,
|
||||
video::{dmabuf::DmaBuf, drm::sync_obj::SyncObjCtx, LINEAR_MODIFIER},
|
||||
},
|
||||
|
|
@ -31,6 +34,8 @@ enum TestGfxError {
|
|||
MapDmaBuf(#[source] AllocatorError),
|
||||
#[error("Could not import dmabuf")]
|
||||
ImportDmaBuf(#[source] AllocatorError),
|
||||
#[error("Could not access the client memory")]
|
||||
AccessFailed(#[source] ClientMemError),
|
||||
}
|
||||
|
||||
impl From<TestGfxError> for GfxError {
|
||||
|
|
@ -108,14 +113,14 @@ impl GfxContext for TestGfxCtx {
|
|||
|
||||
fn shmem_texture(
|
||||
self: Rc<Self>,
|
||||
_old: Option<Rc<dyn GfxTexture>>,
|
||||
_old: Option<Rc<dyn ShmGfxTexture>>,
|
||||
data: &[Cell<u8>],
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
_damage: Option<&[Rect]>,
|
||||
) -> Result<Rc<dyn GfxTexture>, GfxError> {
|
||||
) -> Result<Rc<dyn ShmGfxTexture>, GfxError> {
|
||||
assert!(stride >= width * 4);
|
||||
let size = (stride * height) as usize;
|
||||
assert!(data.len() >= size);
|
||||
|
|
@ -132,6 +137,25 @@ impl GfxContext for TestGfxCtx {
|
|||
})))
|
||||
}
|
||||
|
||||
fn async_shmem_texture(
|
||||
self: Rc<Self>,
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
_cpu_worker: &Rc<CpuWorker>,
|
||||
) -> Result<Rc<dyn AsyncShmGfxTexture>, GfxError> {
|
||||
assert!(stride >= width * 4);
|
||||
let size = (stride * height) as usize;
|
||||
Ok(Rc::new(TestGfxImage::Shm(TestShmGfxImage {
|
||||
data: RefCell::new(vec![0; size]),
|
||||
width,
|
||||
height,
|
||||
stride,
|
||||
format,
|
||||
})))
|
||||
}
|
||||
|
||||
fn allocator(&self) -> Rc<dyn Allocator> {
|
||||
self.allocator.clone()
|
||||
}
|
||||
|
|
@ -301,6 +325,54 @@ impl GfxTexture for TestGfxImage {
|
|||
}
|
||||
}
|
||||
|
||||
impl ShmGfxTexture for TestGfxImage {
|
||||
fn into_texture(self: Rc<Self>) -> Rc<dyn GfxTexture> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncShmGfxTexture for TestGfxImage {
|
||||
fn async_upload(
|
||||
self: Rc<Self>,
|
||||
_callback: Rc<dyn AsyncShmGfxTextureCallback>,
|
||||
mem: &Rc<ClientMemOffset>,
|
||||
damage: Region,
|
||||
) -> Result<Option<PendingShmUpload>, GfxError> {
|
||||
mem.access(|d| self.clone().sync_upload(d, damage))
|
||||
.map_err(TestGfxError::AccessFailed)??;
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn sync_upload(self: Rc<Self>, mem: &[Cell<u8>], _damage: Region) -> Result<(), GfxError> {
|
||||
let TestGfxImage::Shm(shm) = &*self else {
|
||||
unreachable!();
|
||||
};
|
||||
let data = &mut *shm.data.borrow_mut();
|
||||
assert!(mem.len() >= data.len());
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(mem.as_ptr() as _, data.as_mut_ptr(), data.len());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compatible_with(
|
||||
&self,
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
) -> bool {
|
||||
let TestGfxImage::Shm(shm) = &self else {
|
||||
unreachable!();
|
||||
};
|
||||
shm.format == format && shm.width == width && shm.height == height && shm.stride == stride
|
||||
}
|
||||
|
||||
fn into_texture(self: Rc<Self>) -> Rc<dyn GfxTexture> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl GfxImage for TestGfxImage {
|
||||
fn to_framebuffer(self: Rc<Self>) -> Result<Rc<dyn GfxFramebuffer>, GfxError> {
|
||||
Ok(Rc::new(TestGfxFb {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use {
|
|||
},
|
||||
bstr::ByteSlice,
|
||||
std::rc::Rc,
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
testcase!();
|
||||
|
|
@ -15,7 +16,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
let virtual_keymap_str = {
|
||||
let xkb = XkbContext::new()?;
|
||||
let map = xkb.keymap_from_str(VIRTUAL_KEYMAP).unwrap();
|
||||
read_keymap(map.map.raw(), map.map_len)
|
||||
read_keymap(&map.map, map.map_len)
|
||||
};
|
||||
|
||||
let ds = run.create_default_setup().await?;
|
||||
|
|
@ -51,7 +52,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
s_client.sync().await;
|
||||
let (start, keymap) = s_keymap.next().expect("virtual keymap");
|
||||
tassert_eq!(
|
||||
&read_keymap(keymap.fd.raw(), keymap.size as _),
|
||||
&read_keymap(&keymap.fd, keymap.size as _),
|
||||
&virtual_keymap_str
|
||||
);
|
||||
{
|
||||
|
|
@ -119,7 +120,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
s_client.sync().await;
|
||||
let (pos, keymap) = s_keymap.next().expect("seat keymap");
|
||||
tassert_eq!(pos, start + 8);
|
||||
tassert!(read_keymap(keymap.fd.raw(), keymap.size as _) != virtual_keymap_str);
|
||||
tassert!(read_keymap(&keymap.fd, keymap.size as _) != virtual_keymap_str);
|
||||
{
|
||||
let (pos, mods) = s_modifiers.next().expect("mods 0");
|
||||
tassert_eq!(pos, start + 9);
|
||||
|
|
@ -147,8 +148,8 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn read_keymap(fd: i32, size: usize) -> String {
|
||||
let client_mem = ClientMem::new(fd, size - 1, true).unwrap();
|
||||
fn read_keymap(fd: &Rc<OwnedFd>, size: usize) -> String {
|
||||
let client_mem = ClientMem::new(fd, size - 1, true, None, None).unwrap();
|
||||
let client_mem = Rc::new(client_mem).offset(0);
|
||||
let mut v = vec![];
|
||||
client_mem.read(&mut v).unwrap();
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ mod client;
|
|||
mod clientmem;
|
||||
mod compositor;
|
||||
mod config;
|
||||
mod cpu_worker;
|
||||
mod cursor;
|
||||
mod cursor_user;
|
||||
mod damage;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ mod region;
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use region::RegionBuilder;
|
||||
pub use region::{DamageQueue, RegionBuilder};
|
||||
use {
|
||||
jay_algorithms::rect::RectRaw,
|
||||
smallvec::SmallVec,
|
||||
|
|
@ -16,7 +16,7 @@ pub struct Rect {
|
|||
raw: RectRaw,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Default)]
|
||||
pub struct Region {
|
||||
rects: SmallVec<[RectRaw; 1]>,
|
||||
extents: Rect,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
use {
|
||||
crate::rect::{Rect, Region},
|
||||
crate::{
|
||||
rect::{Rect, Region},
|
||||
utils::{
|
||||
array,
|
||||
ptr_ext::{MutPtrExt, PtrExt},
|
||||
},
|
||||
},
|
||||
jay_algorithms::rect::{
|
||||
region::{extents, rects_to_bands, subtract, union},
|
||||
RectRaw,
|
||||
},
|
||||
smallvec::SmallVec,
|
||||
std::{mem, ops::Deref, rc::Rc},
|
||||
std::{cell::UnsafeCell, mem, ops::Deref, rc::Rc},
|
||||
};
|
||||
|
||||
thread_local! {
|
||||
|
|
@ -18,12 +24,16 @@ thread_local! {
|
|||
|
||||
impl Region {
|
||||
pub fn new(rect: Rect) -> Rc<Self> {
|
||||
Rc::new(Self::new2(rect))
|
||||
}
|
||||
|
||||
pub fn new2(rect: Rect) -> Self {
|
||||
let mut rects = SmallVec::new();
|
||||
rects.push(rect.raw);
|
||||
Rc::new(Self {
|
||||
Self {
|
||||
rects,
|
||||
extents: rect,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty() -> Rc<Self> {
|
||||
|
|
@ -34,16 +44,23 @@ impl Region {
|
|||
if rects.is_empty() {
|
||||
return Self::empty();
|
||||
}
|
||||
Rc::new(Self::from_rects2(rects))
|
||||
}
|
||||
|
||||
pub fn from_rects2(rects: &[Rect]) -> Self {
|
||||
if rects.is_empty() {
|
||||
return Self::default();
|
||||
}
|
||||
if rects.len() == 1 {
|
||||
return Self::new(rects[0]);
|
||||
return Self::new2(rects[0]);
|
||||
}
|
||||
let rects = rects_to_bands(unsafe { mem::transmute::<&[Rect], &[RectRaw]>(rects) });
|
||||
Rc::new(Self {
|
||||
Self {
|
||||
extents: Rect {
|
||||
raw: extents(&rects),
|
||||
},
|
||||
rects,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn union(self: &Rc<Self>, other: &Rc<Self>) -> Rc<Self> {
|
||||
|
|
@ -173,3 +190,35 @@ impl RegionBuilder {
|
|||
self.pending.clear();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DamageQueue {
|
||||
this: usize,
|
||||
datas: Rc<UnsafeCell<Vec<Vec<Rect>>>>,
|
||||
}
|
||||
|
||||
impl DamageQueue {
|
||||
pub fn new<const N: usize>() -> [DamageQueue; N] {
|
||||
let datas = Rc::new(UnsafeCell::new(vec![vec!(); N]));
|
||||
array::from_fn(|this| DamageQueue {
|
||||
this,
|
||||
datas: datas.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn damage(&self, rects: &[Rect]) {
|
||||
let datas = unsafe { self.datas.get().deref_mut() };
|
||||
for data in datas {
|
||||
data.extend(rects);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
let data = unsafe { &mut self.datas.get().deref_mut()[self.this] };
|
||||
data.clear();
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Region {
|
||||
let data = unsafe { &self.datas.get().deref()[self.this] };
|
||||
Region::from_rects2(data)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use {
|
|||
clientmem::ClientMemOffset,
|
||||
compositor::LIBEI_SOCKET,
|
||||
config::ConfigProxy,
|
||||
cpu_worker::CpuWorker,
|
||||
cursor::{Cursor, ServerCursors},
|
||||
cursor_user::{CursorUserGroup, CursorUserGroupId, CursorUserGroupIds, CursorUserIds},
|
||||
damage::DamageVisualizer,
|
||||
|
|
@ -214,6 +215,7 @@ pub struct State {
|
|||
pub enable_ei_acceptor: Cell<bool>,
|
||||
pub ei_clients: EiClients,
|
||||
pub slow_ei_clients: AsyncQueue<Rc<EiClient>>,
|
||||
pub cpu_worker: Rc<CpuWorker>,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
@ -482,7 +484,7 @@ impl State {
|
|||
updated_buffers.insert(buffer.buffer.id);
|
||||
buffer.buffer.handle_gfx_context_change(Some(surface));
|
||||
} else {
|
||||
surface.shm_texture.take();
|
||||
surface.reset_shm_textures();
|
||||
}
|
||||
}
|
||||
for buffer in client.data.objects.buffers.lock().values() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
format::ARGB8888,
|
||||
gfx_api::{GfxContext, GfxError, GfxTexture},
|
||||
gfx_api::{GfxContext, GfxError, GfxTexture, ShmGfxTexture},
|
||||
pango::{
|
||||
consts::{
|
||||
CAIRO_FORMAT_ARGB32, CAIRO_OPERATOR_SOURCE, PANGO_ELLIPSIZE_END, PANGO_SCALE,
|
||||
|
|
@ -73,6 +73,7 @@ impl<'a> Config<'a> {
|
|||
#[derive(Clone)]
|
||||
pub struct TextTexture {
|
||||
config: Rc<Config<'static>>,
|
||||
shm_texture: Rc<dyn ShmGfxTexture>,
|
||||
pub texture: Rc<dyn GfxTexture>,
|
||||
}
|
||||
|
||||
|
|
@ -169,6 +170,7 @@ fn render2(
|
|||
markup: bool,
|
||||
scale: Option<f64>,
|
||||
) -> Result<TextTexture, TextError> {
|
||||
let width = width.min(3840);
|
||||
let config = Config {
|
||||
x,
|
||||
y,
|
||||
|
|
@ -210,7 +212,7 @@ fn render2(
|
|||
Ok(d) => d,
|
||||
Err(e) => return Err(TextError::ImageData(e)),
|
||||
};
|
||||
let old = old.map(|o| o.texture);
|
||||
let old = old.map(|o| o.shm_texture);
|
||||
match ctx.clone().shmem_texture(
|
||||
old,
|
||||
bytes,
|
||||
|
|
@ -222,7 +224,8 @@ fn render2(
|
|||
) {
|
||||
Ok(t) => Ok(TextTexture {
|
||||
config: Rc::new(config.to_static()),
|
||||
texture: t,
|
||||
texture: t.clone().into_texture(),
|
||||
shm_texture: t,
|
||||
}),
|
||||
Err(e) => Err(TextError::RenderError(e)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ pub mod clone3;
|
|||
pub mod clonecell;
|
||||
pub mod copyhashmap;
|
||||
pub mod debug_fn;
|
||||
pub mod double_buffered;
|
||||
pub mod double_click_state;
|
||||
pub mod errorfmt;
|
||||
pub mod event_listener;
|
||||
|
|
@ -27,6 +28,7 @@ pub mod nonblock;
|
|||
pub mod num_cpus;
|
||||
pub mod numcell;
|
||||
pub mod on_change;
|
||||
pub mod on_drop;
|
||||
pub mod once;
|
||||
pub mod opaque;
|
||||
pub mod opaque_cell;
|
||||
|
|
|
|||
36
src/utils/double_buffered.rs
Normal file
36
src/utils/double_buffered.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
use std::{cell::Cell, ops::Deref};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DoubleBuffered<T> {
|
||||
bufs: [T; 2],
|
||||
front: Cell<usize>,
|
||||
}
|
||||
|
||||
impl<T> DoubleBuffered<T> {
|
||||
pub fn new(bufs: [T; 2]) -> Self {
|
||||
Self {
|
||||
bufs,
|
||||
front: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn front(&self) -> &T {
|
||||
unsafe { self.bufs.get_unchecked(self.front.get()) }
|
||||
}
|
||||
|
||||
pub fn back(&self) -> &T {
|
||||
unsafe { self.bufs.get_unchecked(1 - self.front.get()) }
|
||||
}
|
||||
|
||||
pub fn flip(&self) {
|
||||
self.front.set(1 - self.front.get());
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for DoubleBuffered<T> {
|
||||
type Target = [T; 2];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.bufs
|
||||
}
|
||||
}
|
||||
|
|
@ -16,20 +16,23 @@ impl FdCloser {
|
|||
cv: Condvar::new(),
|
||||
});
|
||||
let slf2 = slf.clone();
|
||||
std::thread::spawn(move || {
|
||||
let mut fds = vec![];
|
||||
let mut lock = slf2.fds.lock();
|
||||
loop {
|
||||
mem::swap(&mut *lock, &mut fds);
|
||||
if fds.len() > 0 {
|
||||
drop(lock);
|
||||
fds.clear();
|
||||
lock = slf2.fds.lock();
|
||||
} else {
|
||||
slf2.cv.wait(&mut lock);
|
||||
std::thread::Builder::new()
|
||||
.name("fd closer".to_string())
|
||||
.spawn(move || {
|
||||
let mut fds = vec![];
|
||||
let mut lock = slf2.fds.lock();
|
||||
loop {
|
||||
mem::swap(&mut *lock, &mut fds);
|
||||
if fds.len() > 0 {
|
||||
drop(lock);
|
||||
fds.clear();
|
||||
lock = slf2.fds.lock();
|
||||
} else {
|
||||
slf2.cv.wait(&mut lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
slf
|
||||
}
|
||||
|
||||
|
|
|
|||
46
src/utils/on_drop.rs
Normal file
46
src/utils/on_drop.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
use std::{mem, mem::ManuallyDrop};
|
||||
|
||||
pub struct OnDrop<F>(pub F)
|
||||
where
|
||||
F: FnMut() + Copy;
|
||||
|
||||
impl<F: FnMut() + Copy> OnDrop<F> {
|
||||
pub fn forget(self) {
|
||||
mem::forget(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnMut() + Copy> Drop for OnDrop<F> {
|
||||
fn drop(&mut self) {
|
||||
(self.0)();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OnDrop2<F>
|
||||
where
|
||||
F: FnOnce(),
|
||||
{
|
||||
f: ManuallyDrop<F>,
|
||||
}
|
||||
|
||||
impl<F: FnOnce()> OnDrop2<F> {
|
||||
pub fn new(f: F) -> Self {
|
||||
Self {
|
||||
f: ManuallyDrop::new(f),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn forget(mut self) {
|
||||
unsafe {
|
||||
ManuallyDrop::drop(&mut self.f);
|
||||
}
|
||||
mem::forget(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnOnce()> Drop for OnDrop2<F> {
|
||||
fn drop(&mut self) {
|
||||
let f = unsafe { ManuallyDrop::take(&mut self.f) };
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
|
@ -64,6 +64,12 @@ impl<T> AsyncQueue<T> {
|
|||
}
|
||||
self.waiter.take();
|
||||
}
|
||||
|
||||
pub fn move_to(&self, other: &mut VecDeque<T>) {
|
||||
unsafe {
|
||||
other.append(self.data.get().deref_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AsyncQueuePop<'a, T> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue