surface: use async uploads for shm buffers
This commit is contained in:
parent
80310f4c0d
commit
d40e605f66
8 changed files with 250 additions and 73 deletions
|
|
@ -551,7 +551,6 @@ pub struct PendingShmUpload {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AsyncShmGfxTexture: GfxTexture {
|
pub trait AsyncShmGfxTexture: GfxTexture {
|
||||||
#[expect(dead_code)]
|
|
||||||
fn async_upload(
|
fn async_upload(
|
||||||
self: Rc<Self>,
|
self: Rc<Self>,
|
||||||
callback: Rc<dyn AsyncShmGfxTextureCallback>,
|
callback: Rc<dyn AsyncShmGfxTextureCallback>,
|
||||||
|
|
@ -561,7 +560,6 @@ pub trait AsyncShmGfxTexture: GfxTexture {
|
||||||
|
|
||||||
fn sync_upload(self: Rc<Self>, shm: &[Cell<u8>], damage: Region) -> Result<(), GfxError>;
|
fn sync_upload(self: Rc<Self>, shm: &[Cell<u8>], damage: Region) -> Result<(), GfxError>;
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
fn compatible_with(
|
fn compatible_with(
|
||||||
&self,
|
&self,
|
||||||
format: &'static Format,
|
format: &'static Format,
|
||||||
|
|
@ -570,7 +568,6 @@ pub trait AsyncShmGfxTexture: GfxTexture {
|
||||||
stride: i32,
|
stride: i32,
|
||||||
) -> bool;
|
) -> bool;
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
fn into_texture(self: Rc<Self>) -> Rc<dyn GfxTexture>;
|
fn into_texture(self: Rc<Self>) -> Rc<dyn GfxTexture>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -598,7 +595,6 @@ pub trait GfxContext: Debug {
|
||||||
damage: Option<&[Rect]>,
|
damage: Option<&[Rect]>,
|
||||||
) -> Result<Rc<dyn ShmGfxTexture>, GfxError>;
|
) -> Result<Rc<dyn ShmGfxTexture>, GfxError>;
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
fn async_shmem_texture(
|
fn async_shmem_texture(
|
||||||
self: Rc<Self>,
|
self: Rc<Self>,
|
||||||
format: &'static Format,
|
format: &'static Format,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use {
|
||||||
ifs::wl_surface::WlSurface,
|
ifs::wl_surface::WlSurface,
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
rect::Rect,
|
rect::{Rect, Region},
|
||||||
theme::Color,
|
theme::Color,
|
||||||
utils::errorfmt::ErrorFmt,
|
utils::errorfmt::ErrorFmt,
|
||||||
video::dmabuf::DmaBuf,
|
video::dmabuf::DmaBuf,
|
||||||
|
|
@ -22,7 +22,7 @@ use {
|
||||||
|
|
||||||
pub enum WlBufferStorage {
|
pub enum WlBufferStorage {
|
||||||
Shm {
|
Shm {
|
||||||
mem: ClientMemOffset,
|
mem: Rc<ClientMemOffset>,
|
||||||
stride: i32,
|
stride: i32,
|
||||||
},
|
},
|
||||||
Dmabuf {
|
Dmabuf {
|
||||||
|
|
@ -41,6 +41,7 @@ pub struct WlBuffer {
|
||||||
pub dmabuf: Option<DmaBuf>,
|
pub dmabuf: Option<DmaBuf>,
|
||||||
render_ctx_version: Cell<u32>,
|
render_ctx_version: Cell<u32>,
|
||||||
pub storage: RefCell<Option<WlBufferStorage>>,
|
pub storage: RefCell<Option<WlBufferStorage>>,
|
||||||
|
shm: bool,
|
||||||
pub color: Option<Color>,
|
pub color: Option<Color>,
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
|
|
@ -52,6 +53,10 @@ impl WlBuffer {
|
||||||
self.destroyed.get()
|
self.destroyed.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_shm(&self) -> bool {
|
||||||
|
self.shm
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_dmabuf(
|
pub fn new_dmabuf(
|
||||||
id: WlBufferId,
|
id: WlBufferId,
|
||||||
client: &Rc<Client>,
|
client: &Rc<Client>,
|
||||||
|
|
@ -76,6 +81,7 @@ impl WlBuffer {
|
||||||
tex: None,
|
tex: None,
|
||||||
fb: None,
|
fb: None,
|
||||||
})),
|
})),
|
||||||
|
shm: false,
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
color: None,
|
color: None,
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +106,7 @@ impl WlBuffer {
|
||||||
if required > mem.len() as u64 {
|
if required > mem.len() as u64 {
|
||||||
return Err(WlBufferError::OutOfBounds);
|
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;
|
let min_row_size = width as u64 * shm_info.bpp as u64;
|
||||||
if (stride as u64) < min_row_size {
|
if (stride as u64) < min_row_size {
|
||||||
return Err(WlBufferError::StrideTooSmall);
|
return Err(WlBufferError::StrideTooSmall);
|
||||||
|
|
@ -114,6 +120,7 @@ impl WlBuffer {
|
||||||
dmabuf: None,
|
dmabuf: None,
|
||||||
render_ctx_version: Cell::new(client.state.render_ctx_version.get()),
|
render_ctx_version: Cell::new(client.state.render_ctx_version.get()),
|
||||||
storage: RefCell::new(Some(WlBufferStorage::Shm { mem, stride })),
|
storage: RefCell::new(Some(WlBufferStorage::Shm { mem, stride })),
|
||||||
|
shm: true,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
|
|
@ -138,6 +145,7 @@ impl WlBuffer {
|
||||||
dmabuf: None,
|
dmabuf: None,
|
||||||
render_ctx_version: Cell::new(client.state.render_ctx_version.get()),
|
render_ctx_version: Cell::new(client.state.render_ctx_version.get()),
|
||||||
storage: RefCell::new(None),
|
storage: RefCell::new(None),
|
||||||
|
shm: false,
|
||||||
width: 1,
|
width: 1,
|
||||||
height: 1,
|
height: 1,
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
|
|
@ -153,7 +161,7 @@ impl WlBuffer {
|
||||||
let had_texture = self.reset_gfx_objects(surface);
|
let had_texture = self.reset_gfx_objects(surface);
|
||||||
if had_texture {
|
if had_texture {
|
||||||
if let Some(surface) = surface {
|
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 {
|
let had_texture = match s {
|
||||||
WlBufferStorage::Shm { .. } => {
|
WlBufferStorage::Shm { .. } => {
|
||||||
return match surface {
|
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,
|
None => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -201,24 +212,24 @@ impl WlBuffer {
|
||||||
match &*self.storage.borrow() {
|
match &*self.storage.borrow() {
|
||||||
None => None,
|
None => None,
|
||||||
Some(s) => match s {
|
Some(s) => match s {
|
||||||
WlBufferStorage::Shm { .. } => surface.shm_texture.get().map(|t| t.into_texture()),
|
WlBufferStorage::Shm { .. } => surface
|
||||||
|
.shm_textures
|
||||||
|
.front()
|
||||||
|
.tex
|
||||||
|
.get()
|
||||||
|
.map(|t| t.into_texture()),
|
||||||
WlBufferStorage::Dmabuf { tex, .. } => tex.clone(),
|
WlBufferStorage::Dmabuf { tex, .. } => tex.clone(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_texture_or_log(&self, surface: &WlSurface, damage: Option<&[Rect]>) {
|
pub fn update_texture_or_log(&self, surface: &WlSurface, sync_shm: bool) {
|
||||||
if let Err(e) = self.update_texture(surface, damage) {
|
if let Err(e) = self.update_texture(surface, sync_shm) {
|
||||||
log::warn!("Could not update texture: {}", ErrorFmt(e));
|
log::warn!("Could not update texture: {}", ErrorFmt(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_texture(
|
fn update_texture(&self, surface: &WlSurface, sync_shm: bool) -> Result<(), WlBufferError> {
|
||||||
&self,
|
|
||||||
surface: &WlSurface,
|
|
||||||
damage: Option<&[Rect]>,
|
|
||||||
) -> Result<(), WlBufferError> {
|
|
||||||
let old_shm_texture = surface.shm_texture.take();
|
|
||||||
let storage = &mut *self.storage.borrow_mut();
|
let storage = &mut *self.storage.borrow_mut();
|
||||||
let storage = match storage {
|
let storage = match storage {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
|
|
@ -226,19 +237,19 @@ impl WlBuffer {
|
||||||
};
|
};
|
||||||
match storage {
|
match storage {
|
||||||
WlBufferStorage::Shm { mem, stride } => {
|
WlBufferStorage::Shm { mem, stride } => {
|
||||||
if let Some(ctx) = self.client.state.render_ctx.get() {
|
if sync_shm {
|
||||||
let tex = mem.access(|mem| {
|
if let Some(ctx) = self.client.state.render_ctx.get() {
|
||||||
ctx.shmem_texture(
|
let tex = ctx.async_shmem_texture(
|
||||||
old_shm_texture,
|
|
||||||
mem,
|
|
||||||
self.format,
|
self.format,
|
||||||
self.width,
|
self.width,
|
||||||
self.height,
|
self.height,
|
||||||
*stride,
|
*stride,
|
||||||
damage,
|
&self.client.state.cpu_worker,
|
||||||
)
|
)?;
|
||||||
})??;
|
mem.access(|mem| tex.clone().sync_upload(mem, Region::new2(self.rect)))??;
|
||||||
surface.shm_texture.set(Some(tex));
|
surface.shm_textures.front().tex.set(Some(tex));
|
||||||
|
surface.shm_textures.front().damage.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WlBufferStorage::Dmabuf { img, tex, .. } => {
|
WlBufferStorage::Dmabuf { img, tex, .. } => {
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ use {
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::DrmFeedback,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::{
|
gfx_api::{
|
||||||
AcquireSync, BufferResv, BufferResvUser, ReleaseSync, SampleRect, ShmGfxTexture,
|
AcquireSync, AsyncShmGfxTexture, BufferResv, BufferResvUser, GfxError, ReleaseSync,
|
||||||
SyncFile,
|
SampleRect, SyncFile,
|
||||||
},
|
},
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_buffer::WlBuffer,
|
wl_buffer::WlBuffer,
|
||||||
|
|
@ -60,16 +60,16 @@ use {
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
rect::{Rect, Region},
|
rect::{DamageQueue, Rect, Region},
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
tree::{
|
tree::{
|
||||||
ContainerNode, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, NodeVisitorBase,
|
ContainerNode, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, NodeVisitorBase,
|
||||||
OutputNode, OutputNodeId, PlaceholderNode, ToplevelNode,
|
OutputNode, OutputNodeId, PlaceholderNode, ToplevelNode,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
|
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||||
linkedlist::LinkedList, numcell::NumCell, smallmap::SmallMap,
|
double_buffered::DoubleBuffered, errorfmt::ErrorFmt, linkedlist::LinkedList,
|
||||||
transform_ext::TransformExt,
|
numcell::NumCell, smallmap::SmallMap, transform_ext::TransformExt,
|
||||||
},
|
},
|
||||||
video::{
|
video::{
|
||||||
dmabuf::DMA_BUF_SYNC_READ,
|
dmabuf::DMA_BUF_SYNC_READ,
|
||||||
|
|
@ -250,6 +250,11 @@ impl BufferResv for SurfaceBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SurfaceShmTexture {
|
||||||
|
pub tex: CloneCell<Option<Rc<dyn AsyncShmGfxTexture>>>,
|
||||||
|
pub damage: DamageQueue,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct WlSurface {
|
pub struct WlSurface {
|
||||||
pub id: WlSurfaceId,
|
pub id: WlSurfaceId,
|
||||||
pub node_id: SurfaceNodeId,
|
pub node_id: SurfaceNodeId,
|
||||||
|
|
@ -272,7 +277,7 @@ pub struct WlSurface {
|
||||||
pub buffer: CloneCell<Option<Rc<SurfaceBuffer>>>,
|
pub buffer: CloneCell<Option<Rc<SurfaceBuffer>>>,
|
||||||
buffer_presented: Cell<bool>,
|
buffer_presented: Cell<bool>,
|
||||||
buffer_had_frame_request: Cell<bool>,
|
buffer_had_frame_request: Cell<bool>,
|
||||||
pub shm_texture: CloneCell<Option<Rc<dyn ShmGfxTexture>>>,
|
pub shm_textures: DoubleBuffered<SurfaceShmTexture>,
|
||||||
pub buf_x: NumCell<i32>,
|
pub buf_x: NumCell<i32>,
|
||||||
pub buf_y: NumCell<i32>,
|
pub buf_y: NumCell<i32>,
|
||||||
pub children: RefCell<Option<Box<ParentData>>>,
|
pub children: RefCell<Option<Box<ParentData>>>,
|
||||||
|
|
@ -585,7 +590,10 @@ impl WlSurface {
|
||||||
buffer: Default::default(),
|
buffer: Default::default(),
|
||||||
buffer_presented: Default::default(),
|
buffer_presented: Default::default(),
|
||||||
buffer_had_frame_request: 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_x: Default::default(),
|
||||||
buf_y: Default::default(),
|
buf_y: Default::default(),
|
||||||
children: Default::default(),
|
children: Default::default(),
|
||||||
|
|
@ -910,7 +918,7 @@ impl WlSurfaceRequestHandler for WlSurface {
|
||||||
*children = None;
|
*children = None;
|
||||||
}
|
}
|
||||||
self.buffer.set(None);
|
self.buffer.set(None);
|
||||||
self.shm_texture.take();
|
self.reset_shm_textures();
|
||||||
if let Some(xwayland_serial) = self.xwayland_serial.get() {
|
if let Some(xwayland_serial) = self.xwayland_serial.get() {
|
||||||
self.client
|
self.client
|
||||||
.surfaces_by_xwayland_serial
|
.surfaces_by_xwayland_serial
|
||||||
|
|
@ -1078,11 +1086,13 @@ impl WlSurface {
|
||||||
old_raw_size = Some(buffer.buffer.rect);
|
old_raw_size = Some(buffer.buffer.rect);
|
||||||
}
|
}
|
||||||
if let Some(buffer) = buffer_change {
|
if let Some(buffer) = buffer_change {
|
||||||
let damage = match pending.damage_full || pending.surface_damage.is_not_empty() {
|
if buffer.is_shm() {
|
||||||
true => None,
|
self.shm_textures.flip();
|
||||||
false => Some(&pending.buffer_damage[..]),
|
self.shm_textures.front().damage.clear();
|
||||||
};
|
} else {
|
||||||
buffer.update_texture_or_log(self, damage);
|
self.reset_shm_textures();
|
||||||
|
}
|
||||||
|
buffer.update_texture_or_log(self, false);
|
||||||
let (sync, release_sync) = match pending.explicit_sync {
|
let (sync, release_sync) = match pending.explicit_sync {
|
||||||
false => (AcquireSync::Implicit, ReleaseSync::Implicit),
|
false => (AcquireSync::Implicit, ReleaseSync::Implicit),
|
||||||
true => (AcquireSync::Unnecessary, ReleaseSync::Explicit),
|
true => (AcquireSync::Unnecessary, ReleaseSync::Explicit),
|
||||||
|
|
@ -1104,7 +1114,7 @@ impl WlSurface {
|
||||||
self.buffer_had_frame_request.set(false);
|
self.buffer_had_frame_request.set(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.shm_texture.take();
|
self.reset_shm_textures();
|
||||||
self.buf_x.set(0);
|
self.buf_x.set(0);
|
||||||
self.buf_y.set(0);
|
self.buf_y.set(0);
|
||||||
for (_, cursor) in &self.cursors {
|
for (_, cursor) in &self.cursors {
|
||||||
|
|
@ -1322,6 +1332,13 @@ impl WlSurface {
|
||||||
Ok(())
|
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) {
|
fn apply_damage(&self, pending: &PendingState) {
|
||||||
let bounds = self.toplevel.get().map(|tl| tl.node_absolute_position());
|
let bounds = self.toplevel.get().map(|tl| tl.node_absolute_position());
|
||||||
let pos = self.buffer_abs_pos.get();
|
let pos = self.buffer_abs_pos.get();
|
||||||
|
|
@ -1916,6 +1933,12 @@ pub enum WlSurfaceError {
|
||||||
UnexpectedSyncPoints,
|
UnexpectedSyncPoints,
|
||||||
#[error("The supplied region is invalid")]
|
#[error("The supplied region is invalid")]
|
||||||
InvalidRect,
|
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, ClientError);
|
||||||
efrom!(WlSurfaceError, XdgSurfaceError);
|
efrom!(WlSurfaceError, XdgSurfaceError);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
ifs::wl_surface::{PendingState, WlSurface, WlSurfaceError},
|
gfx_api::{AsyncShmGfxTextureCallback, GfxError, PendingShmUpload},
|
||||||
|
ifs::{
|
||||||
|
wl_buffer::WlBufferStorage,
|
||||||
|
wl_surface::{PendingState, WlSurface, WlSurfaceError},
|
||||||
|
},
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
copyhashmap::CopyHashMap,
|
copyhashmap::CopyHashMap,
|
||||||
|
|
@ -14,13 +18,14 @@ use {
|
||||||
DrmError,
|
DrmError,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isnt::std_1::primitive::IsntSliceExt,
|
isnt::std_1::{primitive::IsntSliceExt, vec::IsntVecExt},
|
||||||
smallvec::SmallVec,
|
smallvec::SmallVec,
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
mem,
|
mem,
|
||||||
ops::{Deref, DerefMut},
|
ops::DerefMut,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
slice,
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
|
@ -76,6 +81,8 @@ pub enum CommitTimelineError {
|
||||||
Wait(#[source] DrmError),
|
Wait(#[source] DrmError),
|
||||||
#[error("The client has too many pending commits")]
|
#[error("The client has too many pending commits")]
|
||||||
Depth,
|
Depth,
|
||||||
|
#[error("Could not upload a shm texture")]
|
||||||
|
ShmUpload(#[source] GfxError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommitTimelines {
|
impl CommitTimelines {
|
||||||
|
|
@ -119,6 +126,7 @@ fn break_loops(list: &LinkedList<Entry>) {
|
||||||
entry.link.take();
|
entry.link.take();
|
||||||
if let EntryKind::Commit(c) = &entry.kind {
|
if let EntryKind::Commit(c) = &entry.kind {
|
||||||
c.wait_handles.take();
|
c.wait_handles.take();
|
||||||
|
*c.shm_upload.borrow_mut() = ShmUploadState::None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -145,7 +153,9 @@ impl CommitTimeline {
|
||||||
) -> Result<(), CommitTimelineError> {
|
) -> Result<(), CommitTimelineError> {
|
||||||
let mut points = SmallVec::new();
|
let mut points = SmallVec::new();
|
||||||
consume_acquire_points(pending, &mut points);
|
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
|
return surface
|
||||||
.apply_state(pending)
|
.apply_state(pending)
|
||||||
.map_err(CommitTimelineError::ImmediateCommit);
|
.map_err(CommitTimelineError::ImmediateCommit);
|
||||||
|
|
@ -162,23 +172,35 @@ impl CommitTimeline {
|
||||||
pending: RefCell::new(mem::take(pending)),
|
pending: RefCell::new(mem::take(pending)),
|
||||||
sync_obj: NumCell::new(points.len()),
|
sync_obj: NumCell::new(points.len()),
|
||||||
wait_handles: Cell::new(Default::default()),
|
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 needs_flush = false;
|
||||||
let mut wait_handles = SmallVec::new();
|
if points.is_not_empty() || pending_uploads > 0 {
|
||||||
let noderef = Rc::new(noderef);
|
let noderef = Rc::new(noderef.clone());
|
||||||
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 EntryKind::Commit(commit) = &noderef.kind else {
|
let EntryKind::Commit(commit) = &noderef.kind else {
|
||||||
unreachable!();
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -194,12 +216,33 @@ impl SyncObjWaiter for NodeRef<Entry> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
commit.sync_obj.fetch_sub(1);
|
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
|
commit
|
||||||
.surface
|
.surface
|
||||||
.client
|
.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),
|
Gc(CommitTimelineId),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ShmUploadState {
|
||||||
|
None,
|
||||||
|
Todo(Rc<NodeRef<Entry>>),
|
||||||
|
Scheduled(#[expect(dead_code)] SmallVec<[PendingShmUpload; 1]>),
|
||||||
|
}
|
||||||
|
|
||||||
struct Commit {
|
struct Commit {
|
||||||
surface: Rc<WlSurface>,
|
surface: Rc<WlSurface>,
|
||||||
pending: RefCell<Box<PendingState>>,
|
pending: RefCell<Box<PendingState>>,
|
||||||
sync_obj: NumCell<usize>,
|
sync_obj: NumCell<usize>,
|
||||||
wait_handles: Cell<SmallVec<[WaitForSyncObjHandle; 1]>>,
|
wait_handles: Cell<SmallVec<[WaitForSyncObjHandle; 1]>>,
|
||||||
|
pending_uploads: NumCell<usize>,
|
||||||
|
shm_upload: RefCell<ShmUploadState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush_from(mut point: NodeRef<Entry>) -> Result<(), WlSurfaceError> {
|
fn flush_from(mut point: NodeRef<Entry>) -> Result<(), WlSurfaceError> {
|
||||||
|
|
@ -243,7 +294,17 @@ impl NodeRef<Entry> {
|
||||||
}
|
}
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
EntryKind::Commit(c) => {
|
EntryKind::Commit(c) => {
|
||||||
|
let mut has_unmet_dependencies = false;
|
||||||
if c.sync_obj.get() > 0 {
|
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);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
c.surface.apply_state(c.pending.borrow_mut().deref_mut())?;
|
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);
|
type Point = (Rc<SyncObj>, SyncObjPoint);
|
||||||
|
|
||||||
fn consume_acquire_points(pending: &mut PendingState, points: &mut SmallVec<[Point; 1]>) {
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,7 @@ mod region;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
#[expect(unused_imports)]
|
pub use region::{DamageQueue, RegionBuilder};
|
||||||
pub use region::DamageQueue;
|
|
||||||
pub use region::RegionBuilder;
|
|
||||||
use {
|
use {
|
||||||
jay_algorithms::rect::RectRaw,
|
jay_algorithms::rect::RectRaw,
|
||||||
smallvec::SmallVec,
|
smallvec::SmallVec,
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,6 @@ pub struct DamageQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DamageQueue {
|
impl DamageQueue {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn new<const N: usize>() -> [DamageQueue; N] {
|
pub fn new<const N: usize>() -> [DamageQueue; N] {
|
||||||
let datas = Rc::new(UnsafeCell::new(vec![vec!(); N]));
|
let datas = Rc::new(UnsafeCell::new(vec![vec!(); N]));
|
||||||
array::from_fn(|this| DamageQueue {
|
array::from_fn(|this| DamageQueue {
|
||||||
|
|
@ -206,7 +205,6 @@ impl DamageQueue {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn damage(&self, rects: &[Rect]) {
|
pub fn damage(&self, rects: &[Rect]) {
|
||||||
let datas = unsafe { self.datas.get().deref_mut() };
|
let datas = unsafe { self.datas.get().deref_mut() };
|
||||||
for data in datas {
|
for data in datas {
|
||||||
|
|
@ -214,13 +212,11 @@ impl DamageQueue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
let data = unsafe { &mut self.datas.get().deref_mut()[self.this] };
|
let data = unsafe { &mut self.datas.get().deref_mut()[self.this] };
|
||||||
data.clear();
|
data.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn get(&self) -> Region {
|
pub fn get(&self) -> Region {
|
||||||
let data = unsafe { &self.datas.get().deref()[self.this] };
|
let data = unsafe { &self.datas.get().deref()[self.this] };
|
||||||
Region::from_rects2(data)
|
Region::from_rects2(data)
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,6 @@ pub struct State {
|
||||||
pub enable_ei_acceptor: Cell<bool>,
|
pub enable_ei_acceptor: Cell<bool>,
|
||||||
pub ei_clients: EiClients,
|
pub ei_clients: EiClients,
|
||||||
pub slow_ei_clients: AsyncQueue<Rc<EiClient>>,
|
pub slow_ei_clients: AsyncQueue<Rc<EiClient>>,
|
||||||
#[expect(dead_code)]
|
|
||||||
pub cpu_worker: Rc<CpuWorker>,
|
pub cpu_worker: Rc<CpuWorker>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -485,7 +484,7 @@ impl State {
|
||||||
updated_buffers.insert(buffer.buffer.id);
|
updated_buffers.insert(buffer.buffer.id);
|
||||||
buffer.buffer.handle_gfx_context_change(Some(surface));
|
buffer.buffer.handle_gfx_context_change(Some(surface));
|
||||||
} else {
|
} else {
|
||||||
surface.shm_texture.take();
|
surface.reset_shm_textures();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for buffer in client.data.objects.buffers.lock().values() {
|
for buffer in client.data.objects.buffers.lock().values() {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ pub struct DoubleBuffered<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> DoubleBuffered<T> {
|
impl<T> DoubleBuffered<T> {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn new(bufs: [T; 2]) -> Self {
|
pub fn new(bufs: [T; 2]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bufs,
|
bufs,
|
||||||
|
|
@ -15,17 +14,14 @@ impl<T> DoubleBuffered<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn front(&self) -> &T {
|
pub fn front(&self) -> &T {
|
||||||
unsafe { self.bufs.get_unchecked(self.front.get()) }
|
unsafe { self.bufs.get_unchecked(self.front.get()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn back(&self) -> &T {
|
pub fn back(&self) -> &T {
|
||||||
unsafe { self.bufs.get_unchecked(1 - self.front.get()) }
|
unsafe { self.bufs.get_unchecked(1 - self.front.get()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn flip(&self) {
|
pub fn flip(&self) {
|
||||||
self.front.set(1 - self.front.get());
|
self.front.set(1 - self.front.get());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue