Read pixels directly from pixman Image instead of ExportMem

ExportMem::copy_framebuffer may not capture composited client surfaces.
Read pixels directly from the pixman Image's CPU memory after rendering,
since PixmanRenderer composites in-place. This should fix missing
client windows in the remote display.
This commit is contained in:
Till Wegmueller 2026-04-07 18:16:05 +02:00
parent fa6bb53142
commit 8bd8f2490c

View file

@ -7,7 +7,7 @@ use smithay::{
backend::{ backend::{
allocator::Fourcc, allocator::Fourcc,
renderer::{ renderer::{
Bind, ExportMem, Offscreen, Bind, Offscreen,
damage::OutputDamageTracker, damage::OutputDamageTracker,
element::texture::TextureRenderElement, element::texture::TextureRenderElement,
pixman::{PixmanRenderer, PixmanTexture}, pixman::{PixmanRenderer, PixmanTexture},
@ -230,6 +230,11 @@ fn render_headless_frame(data: &mut CalloopData) {
} }
}; };
let element_count = data.state.space.elements().count();
if element_count > 0 {
tracing::trace!(element_count, "rendering space with elements");
}
let custom_elements: &[TextureRenderElement<PixmanTexture>] = &[]; let custom_elements: &[TextureRenderElement<PixmanTexture>] = &[];
let render_result = render_output::<_, _, Window, _>( let render_result = render_output::<_, _, Window, _>(
@ -248,35 +253,22 @@ fn render_headless_frame(data: &mut CalloopData) {
Ok(result) => { Ok(result) => {
let damage = result.damage.cloned(); let damage = result.damage.cloned();
// Read pixels from the CPU buffer for network transport. // Drop the render target to release the borrow on the buffer.
let output_size = data.state.output.current_mode().unwrap().size; drop(target);
let region: Rectangle<i32, BufferCoord> =
Rectangle::from_size(Size::from((output_size.w, output_size.h)));
match data // Read pixels directly from the pixman Image's CPU memory.
.renderer // PixmanRenderer composites into the buffer in-place, so after
.copy_framebuffer(&target, region, Fourcc::Argb8888) // rendering the data is already there — no GPU readback needed.
{ let output_size = data.state.output.current_mode().unwrap().size;
Ok(mapping) => match data.renderer.map_texture(&mapping) { let frame_bytes = (output_size.w * output_size.h * 4) as usize;
Ok(pixels) => { let pixels = unsafe {
// Send frame over network if a client is connected. let ptr = data.render_buffer.data() as *const u8;
if data.client_connected { std::slice::from_raw_parts(ptr, frame_bytes)
send_frame_to_network( };
data,
pixels, // Send frame over network if a client is connected.
&damage, if data.client_connected {
output_size.w, send_frame_to_network(data, pixels, &damage, output_size.w, output_size.h);
output_size.h,
);
}
}
Err(err) => {
tracing::warn!(?err, "failed to map headless framebuffer");
}
},
Err(err) => {
tracing::warn!(?err, "failed to copy headless framebuffer");
}
} }
// Send frame callbacks to all mapped surfaces. // Send frame callbacks to all mapped surfaces.