linebender/druid · error
after rendering, no pixmap to present
Error message
after rendering, no pixmap to present
What it means
After rendering completes, `Window::render` pops an idle pixmap from the buffer pool to present to the X server; this error fires when the pool is empty, so there is no free pixmap to present. This indicates the double/triple-buffering invariant was violated — more frames are in flight than the pool has buffers, or presentation bookkeeping failed to return buffers to the idle list.
Solutions
- Verify the X server supports DRI3/Present and that present events are being delivered (test with a compositor disabled/enabled).
- Check that every present path returns its pixmap to idle_pixmaps (or pops it consistently) — a missed event leaves the pool empty.
- Reduce redraw pressure or investigate frame pacing; rendering more frames than buffers can deadlock the pool.
- Update druid-shell; the X11 present buffer logic has had fixes around lost idle pixmaps.
Example fix
// before
let pixmap = *buffers
.idle_pixmaps
.last()
.ok_or_else(|| anyhow!("after rendering, no pixmap to present"))?;
// after
if buffers.idle_pixmaps.is_empty() {
log::warn!("no idle pixmap to present; skipping frame");
return Ok(());
}
let pixmap = *buffers.idle_pixmaps.last().unwrap(); Defensive patterns
Strategy: try-catch
Validate before calling
// app-level: avoid issuing more redraws than frames presented
if window.needs_present() { return; } // a frame is already in flight
window.request_redraw(); Type guard
fn has_idle_pixmap(buffers: &SurfaceBuffers) -> bool {
!buffers.idle_pixmaps.is_empty()
} Try / catch
match window.render() {
Err(e) if e.to_string().contains("no pixmap to present") => {
log::warn!("present pool exhausted; waiting for PresentComplete before next frame");
window.request_redraw(); // retry once buffers return
},
other => other,
} Prevention
- Throttle redraw requests to the display refresh rate
- Verify DRI3/Present support in the target X environment
- Investigate missing PresentCompleteNotify events if the error repeats
- Keep druid-shell updated for buffer-pool fixes
When it happens
Trigger: `render` (from `redraw_now`/`handle_complete_notify`) calls `set_needs_present(false)`, borrows `buffers`, and finds `idle_pixmaps` empty because all pixmaps are still in flight (present pending, no PresentComplete processed) or `present_data` was None so earlier frames never consumed/popped correctly.
Common situations: X server not sending PresentCompleteNotify events (compositor issues, broken DRI3/Present extension), rendering faster than the display refresh/present completion, or a prior present error that left buffers un-returned.
Related errors
- Failed to update cairo drawable
- Window::render - piet finish failed
- called request_paint during painting
- attaching an already in-use surface
- unexpected mouse wheel button
AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10).
Data as JSON: /api/errors/c5c1c2c38bf8be8c.
Report an issue: GitHub.
Appendix: source
Thrown at druid-shell/src/backend/x11/window.rs:827
.map_err(|e| anyhow!("Window::render - piet finish failed: {}", e))
}
Some(e) => {
// Finish might have errored, in which case we want to propagate it.
e
}
};
cairo_ctx.reset_clip();
err?;
}
self.set_needs_present(false)?;
let mut buffers = borrow_mut!(self.buffers)?;
let pixmap = *buffers
.idle_pixmaps
.last()
.ok_or_else(|| anyhow!("after rendering, no pixmap to present"))?;
let scale = self.scale.get();
if let Some(present) = borrow_mut!(self.present_data)?.as_mut() {
present.present(self.app.connection(), pixmap, self.id, &invalid, scale)?;
buffers.idle_pixmaps.pop();
} else {
for rect in invalid.rects() {
let rect = rect.to_px(scale).round();
let (x, y) = (rect.x0 as i16, rect.y0 as i16);
let (w, h) = (rect.width() as u16, rect.height() as u16);
self.app
.connection()
.copy_area(pixmap, self.id, self.gc, x, y, x, y, w, h)?;
}
}
Ok(())
}
fn show(&self) {View on GitHub (pinned to 0f8b1195e4)