1
0
Fork 0
forked from wry/wry

Merge pull request #731 from mahkoh/jorth/fix-ctx-change

gfx: handle context change when buffer is attached to multiple surfaces
This commit is contained in:
mahkoh 2026-02-10 19:10:09 +01:00 committed by GitHub
commit 75e56bb8c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 119 additions and 102 deletions

View file

@ -7,6 +7,7 @@ use {
clm::{CL_CHANGED_DESTROYED, CL_CHANGED_NEW, ClMatcherChange}, clm::{CL_CHANGED_DESTROYED, CL_CHANGED_NEW, ClMatcherChange},
}, },
ifs::{ ifs::{
wl_buffer::WlBuffer,
wl_display::WlDisplay, wl_display::WlDisplay,
wl_registry::WlRegistry, wl_registry::WlRegistry,
wl_surface::{WlSurface, commit_timeline::CommitTimelines}, wl_surface::{WlSurface, commit_timeline::CommitTimelines},
@ -21,6 +22,7 @@ use {
buffd::{MsgFormatter, MsgParser, MsgParserError, OutBufferSwapchain}, buffd::{MsgFormatter, MsgParser, MsgParserError, OutBufferSwapchain},
copyhashmap::{CopyHashMap, Locked}, copyhashmap::{CopyHashMap, Locked},
errorfmt::ErrorFmt, errorfmt::ErrorFmt,
event_listener::EventSource,
numcell::NumCell, numcell::NumCell,
pending_serial::PendingSerial, pending_serial::PendingSerial,
pid_info::{PidInfo, get_pid_info, get_socket_creds}, pid_info::{PidInfo, get_pid_info, get_socket_creds},
@ -194,6 +196,7 @@ impl Clients {
changed_properties: Default::default(), changed_properties: Default::default(),
destroyed: Default::default(), destroyed: Default::default(),
acceptor: acceptor.clone(), acceptor: acceptor.clone(),
gfx_ctx_changed: Default::default(),
}); });
track!(data, data); track!(data, data);
global.update_capabilities(&data, bounding_caps, set_bounding_caps_for_children); global.update_capabilities(&data, bounding_caps, set_bounding_caps_for_children);
@ -321,6 +324,7 @@ pub struct Client {
pub changed_properties: Cell<ClMatcherChange>, pub changed_properties: Cell<ClMatcherChange>,
pub destroyed: CopyHashMap<CritMatcherId, Weak<dyn CritDestroyListener<Rc<Self>>>>, pub destroyed: CopyHashMap<CritMatcherId, Weak<dyn CritDestroyListener<Rc<Self>>>>,
pub acceptor: Rc<AcceptorMetadata>, pub acceptor: Rc<AcceptorMetadata>,
pub gfx_ctx_changed: EventSource<WlBuffer>,
} }
pub const NUM_CACHED_SERIAL_RANGES: usize = 64; pub const NUM_CACHED_SERIAL_RANGES: usize = 64;

View file

@ -101,7 +101,7 @@ pub enum QueueTransfer {
pub enum VulkanImageMemory { pub enum VulkanImageMemory {
DmaBuf(VulkanDmaBufImage), DmaBuf(VulkanDmaBufImage),
Internal(VulkanShmImage), Internal(VulkanShmImage),
Blend(#[expect(dead_code)] VulkanAllocation), Blend(VulkanAllocation),
} }
pub struct VulkanDmaBufImage { pub struct VulkanDmaBufImage {

View file

@ -2117,6 +2117,18 @@ impl Debug for VulkanRenderer {
impl VulkanImage { impl VulkanImage {
fn assert_device(&self, device: &Device) -> Result<(), VulkanError> { fn assert_device(&self, device: &Device) -> Result<(), VulkanError> {
if self.renderer.device.device.handle() != device.handle() { if self.renderer.device.device.handle() != device.handle() {
match &self.ty {
VulkanImageMemory::DmaBuf(v) => {
log::warn!("Mixed device use with dmabuf {}", v.template.dmabuf.id);
}
VulkanImageMemory::Internal(_v) => {
log::warn!("Mixed device use with internal image");
}
VulkanImageMemory::Blend(_v) => {
log::warn!("Mixed device use with blend buffer");
}
}
log::info!("Image address {:?}", ptr::from_ref(self));
return Err(VulkanError::MixedVulkanDeviceUse); return Err(VulkanError::MixedVulkanDeviceUse);
} }
Ok(()) Ok(())

View file

@ -8,7 +8,7 @@ use {
leaks::Tracker, leaks::Tracker,
object::{Object, Version}, object::{Object, Version},
rect::{Rect, Region}, rect::{Rect, Region},
utils::{errorfmt::ErrorFmt, page_size::page_size}, utils::{errorfmt::ErrorFmt, event_listener::EventListener, page_size::page_size},
video::{ video::{
LINEAR_MODIFIER, LINEAR_MODIFIER,
dmabuf::{DmaBuf, DmaBufPlane}, dmabuf::{DmaBuf, DmaBufPlane},
@ -61,6 +61,7 @@ pub struct WlBuffer {
pub color: Option<[u32; 4]>, pub color: Option<[u32; 4]>,
width: i32, width: i32,
height: i32, height: i32,
gfx_ctx_changed: EventListener<WlBuffer>,
pub tracker: Tracker<Self>, pub tracker: Tracker<Self>,
} }
@ -73,16 +74,18 @@ impl WlBuffer {
self.shm self.shm
} }
pub fn new_dmabuf( fn new(
id: WlBufferId, id: WlBufferId,
client: &Rc<Client>, client: &Rc<Client>,
format: &'static Format, format: &'static Format,
dmabuf: DmaBuf, width: i32,
img: &Rc<dyn GfxImage>, height: i32,
) -> Self { dmabuf: Option<DmaBuf>,
let width = img.width(); storage: Option<WlBufferStorage>,
let height = img.height(); shm: bool,
Self { color: Option<[u32; 4]>,
) -> Rc<Self> {
let slf = Rc::new_cyclic(|slf| Self {
id, id,
destroyed: Cell::new(false), destroyed: Cell::new(false),
client: client.clone(), client: client.clone(),
@ -90,17 +93,40 @@ impl WlBuffer {
format, format,
width, width,
height, height,
dmabuf: Some(dmabuf), dmabuf,
render_ctx_version: Cell::new(client.state.render_ctx_version.get()), render_ctx_version: Cell::new(client.state.render_ctx_version.get()),
storage: RefCell::new(Some(WlBufferStorage::Dmabuf { storage: RefCell::new(storage),
shm,
tracker: Default::default(),
color,
gfx_ctx_changed: EventListener::new(slf.clone()),
});
slf.gfx_ctx_changed.attach(&client.gfx_ctx_changed);
slf
}
pub fn new_dmabuf(
id: WlBufferId,
client: &Rc<Client>,
format: &'static Format,
dmabuf: DmaBuf,
img: &Rc<dyn GfxImage>,
) -> Rc<Self> {
Self::new(
id,
client,
format,
img.width(),
img.height(),
Some(dmabuf),
Some(WlBufferStorage::Dmabuf {
img: img.clone(), img: img.clone(),
tex: None, tex: None,
fb: None, fb: None,
})), }),
shm: false, false,
tracker: Default::default(), None,
color: None, )
}
} }
#[expect(clippy::too_many_arguments)] #[expect(clippy::too_many_arguments)]
@ -114,7 +140,7 @@ impl WlBuffer {
format: &'static Format, format: &'static Format,
mem: &Rc<ClientMem>, mem: &Rc<ClientMem>,
udmabuf: Option<(&Rc<OwnedFd>, usize)>, udmabuf: Option<(&Rc<OwnedFd>, usize)>,
) -> Result<Self, WlBufferError> { ) -> Result<Rc<Self>, WlBufferError> {
let bytes = stride as u64 * height as u64; let bytes = stride as u64 * height as u64;
let required = bytes + offset as u64; let required = bytes + offset as u64;
if required > mem.len() as u64 { if required > mem.len() as u64 {
@ -150,25 +176,21 @@ impl WlBuffer {
tex_impossible: false, tex_impossible: false,
}, },
}; };
Ok(Self { Ok(Self::new(
id, id,
destroyed: Cell::new(false), client,
client: client.clone(),
rect: Rect::new_sized_saturating(0, 0, width, height),
format, format,
dmabuf: None, width,
render_ctx_version: Cell::new(client.state.render_ctx_version.get()), height,
storage: RefCell::new(Some(WlBufferStorage::Shm { None,
Some(WlBufferStorage::Shm {
dmabuf_buffer_params, dmabuf_buffer_params,
mem, mem,
stride, stride,
})), }),
shm: true, true,
width, None,
height, ))
tracker: Default::default(),
color: None,
})
} }
pub fn new_single_pixel( pub fn new_single_pixel(
@ -178,40 +200,37 @@ impl WlBuffer {
g: u32, g: u32,
b: u32, b: u32,
a: u32, a: u32,
) -> Self { ) -> Rc<Self> {
Self { Self::new(
id, id,
destroyed: Cell::new(false), client,
client: client.clone(), ARGB8888,
rect: Rect::new_sized_saturating(0, 0, 1, 1), 1,
format: ARGB8888, 1,
dmabuf: None, None,
render_ctx_version: Cell::new(client.state.render_ctx_version.get()), None,
storage: RefCell::new(None), false,
shm: false, Some([r, g, b, a]),
width: 1, )
height: 1,
tracker: Default::default(),
color: Some([r, g, b, a]),
}
} }
pub fn handle_gfx_context_change(&self, surface: Option<&WlSurface>) { pub fn handle_gfx_context_change(&self) -> bool {
let ctx_version = self.client.state.render_ctx_version.get(); let ctx_version = self.client.state.render_ctx_version.get();
if self.render_ctx_version.replace(ctx_version) == ctx_version { let up_to_date = self.render_ctx_version.replace(ctx_version) == ctx_version;
return;
}
let had_texture = self.reset_gfx_objects(surface);
if had_texture && let Some(surface) = surface {
self.update_texture_or_log(surface, true);
}
}
fn reset_gfx_objects(&self, surface: Option<&WlSurface>) -> bool {
let mut storage = self.storage.borrow_mut(); let mut storage = self.storage.borrow_mut();
let Some(s) = &mut *storage else { let Some(s) = &mut *storage else {
return false; return false;
}; };
if up_to_date {
let tex = match s {
WlBufferStorage::Shm {
dmabuf_buffer_params: DmabufBufferParams { tex, .. },
..
} => tex,
WlBufferStorage::Dmabuf { tex, .. } => tex,
};
return tex.is_some();
}
let had_texture = match s { let had_texture = match s {
WlBufferStorage::Shm { WlBufferStorage::Shm {
dmabuf_buffer_params: dmabuf_buffer_params:
@ -227,13 +246,8 @@ impl WlBuffer {
} => { } => {
host_buffer.take(); host_buffer.take();
*host_buffer_impossible = *udmabuf_impossible; *host_buffer_impossible = *udmabuf_impossible;
let mut had_texture = tex.take().is_some(); let had_texture = tex.take().is_some();
*tex_impossible = *udmabuf_impossible; *tex_impossible = *udmabuf_impossible;
if let Some(s) = surface {
s.shm_staging.take();
s.shm_textures.back().tex.take();
had_texture |= s.shm_textures.front().tex.take().is_some();
}
return had_texture; return had_texture;
} }
WlBufferStorage::Dmabuf { tex, .. } => tex.is_some(), WlBufferStorage::Dmabuf { tex, .. } => tex.is_some(),

View file

@ -166,13 +166,7 @@ impl WlDrmRequestHandler for WlDrm {
} }
} }
let img = ctx.dmabuf_img(&dmabuf)?; let img = ctx.dmabuf_img(&dmabuf)?;
let buffer = Rc::new(WlBuffer::new_dmabuf( let buffer = WlBuffer::new_dmabuf(req.id, &self.client, format, dmabuf, &img);
req.id,
&self.client,
format,
dmabuf,
&img,
));
track!(self.client, buffer); track!(self.client, buffer);
self.client.add_client_obj(&buffer)?; self.client.add_client_obj(&buffer)?;
Ok(()) Ok(())

View file

@ -62,7 +62,7 @@ impl WlShmPoolRequestHandler for WlShmPool {
if req.height < 0 || req.width < 0 || req.stride < 0 || req.offset < 0 { if req.height < 0 || req.width < 0 || req.stride < 0 || req.offset < 0 {
return Err(WlShmPoolError::NegativeParameters); return Err(WlShmPoolError::NegativeParameters);
} }
let buffer = Rc::new(WlBuffer::new_shm( let buffer = WlBuffer::new_shm(
req.id, req.id,
&self.client, &self.client,
req.offset as usize, req.offset as usize,
@ -72,7 +72,7 @@ impl WlShmPoolRequestHandler for WlShmPool {
format, format,
&self.mem.get(), &self.mem.get(),
None, None,
)?); )?;
track!(self.client, buffer); track!(self.client, buffer);
self.client.add_client_obj(&buffer)?; self.client.add_client_obj(&buffer)?;
Ok(()) Ok(())

View file

@ -1481,12 +1481,14 @@ impl WlSurface {
Ok(()) Ok(())
} }
pub fn reset_shm_textures(&self) { pub fn reset_shm_textures(&self) -> bool {
let had_texture = self.shm_textures.front().tex.is_some();
self.shm_staging.take(); self.shm_staging.take();
for tex in &*self.shm_textures { for tex in &*self.shm_textures {
tex.tex.take(); tex.tex.take();
tex.damage.clear(); tex.damage.clear();
} }
had_texture
} }
fn apply_damage(&self, pending: &PendingState) { fn apply_damage(&self, pending: &PendingState) {

View file

@ -76,14 +76,7 @@ impl WpSinglePixelBufferManagerV1RequestHandler for WpSinglePixelBufferManagerV1
req: CreateU32RgbaBuffer, req: CreateU32RgbaBuffer,
_slf: &Rc<Self>, _slf: &Rc<Self>,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
let buffer = Rc::new(WlBuffer::new_single_pixel( let buffer = WlBuffer::new_single_pixel(req.id, &self.client, req.r, req.g, req.b, req.a);
req.id,
&self.client,
req.r,
req.g,
req.b,
req.a,
));
track!(self.client, buffer); track!(self.client, buffer);
self.client.add_client_obj(&buffer)?; self.client.add_client_obj(&buffer)?;
Ok(()) Ok(())

View file

@ -125,7 +125,7 @@ impl ZwpLinuxBufferParamsV1 {
) )
.map(Rc::new) .map(Rc::new)
.map_err(ZwpLinuxBufferParamsV1Error::CreateClientMem)?; .map_err(ZwpLinuxBufferParamsV1Error::CreateClientMem)?;
Rc::new(WlBuffer::new_shm( WlBuffer::new_shm(
get_id()?, get_id()?,
&self.parent.client, &self.parent.client,
p.offset as usize, p.offset as usize,
@ -135,16 +135,10 @@ impl ZwpLinuxBufferParamsV1 {
format.format, format.format,
&client_mem, &client_mem,
Some((&p.fd, size)), Some((&p.fd, size)),
)?) )?
} else { } else {
let img = ctx.dmabuf_img(&dmabuf)?; let img = ctx.dmabuf_img(&dmabuf)?;
Rc::new(WlBuffer::new_dmabuf( WlBuffer::new_dmabuf(get_id()?, &self.parent.client, format.format, dmabuf, &img)
get_id()?,
&self.parent.client,
format.format,
dmabuf,
&img,
))
}; };
track!(self.parent.client, buffer); track!(self.parent.client, buffer);
if buffer_id.is_some() { if buffer_id.is_some() {

View file

@ -182,6 +182,9 @@ impl RendererBase<'_> {
opaque: bool, opaque: bool,
cd: &Rc<ColorDescription>, cd: &Rc<ColorDescription>,
) { ) {
// log::info!("rendering texture {:?}", std::ptr::from_ref(&**texture) as *const u8);
// log::info!("{:?}", backtrace::Backtrace::new());
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity); let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
let (twidth, theight) = if let Some(size) = tsize { let (twidth, theight) = if let Some(size) = tsize {

View file

@ -133,7 +133,7 @@ use {
}, },
xwayland::{self, XWaylandEvent}, xwayland::{self, XWaylandEvent},
}, },
ahash::{AHashMap, AHashSet}, ahash::AHashMap,
bstr::ByteSlice, bstr::ByteSlice,
jay_config::{ jay_config::{
PciId, PciId,
@ -676,20 +676,21 @@ impl State {
} }
} }
Walker.visit_display(&self.root); Walker.visit_display(&self.root);
let mut updated_buffers = AHashMap::new();
for client in self.clients.clients.borrow_mut().values() { for client in self.clients.clients.borrow_mut().values() {
let mut updated_buffers = AHashSet::new(); updated_buffers.clear();
for buffer in client.data.gfx_ctx_changed.iter() {
let had_buffer_texture = buffer.handle_gfx_context_change();
updated_buffers.insert(buffer.id, had_buffer_texture);
}
for surface in client.data.objects.surfaces.lock().values() { for surface in client.data.objects.surfaces.lock().values() {
let had_shm_texture = surface.reset_shm_textures();
if let Some(buffer) = surface.buffer.get() { if let Some(buffer) = surface.buffer.get() {
updated_buffers.insert(buffer.buffer.id); let had_buffer_texture = *updated_buffers.get(&buffer.buffer.id).unwrap();
buffer.buffer.handle_gfx_context_change(Some(surface)); if had_shm_texture || had_buffer_texture {
} else { buffer.buffer.update_texture_or_log(surface, true);
surface.reset_shm_textures();
} }
} }
for buffer in client.data.objects.buffers.lock().values() {
if !updated_buffers.contains(&buffer.id) {
buffer.handle_gfx_context_change(None);
}
} }
} }
} }