mirror of
https://github.com/yshui/picom.git
synced 2024-11-11 13:51:02 -05:00
core: make sure unredirection happens when screen is off
So when the screen is off, we calls queue_redraw, hoping draw_callback will be called and unredirects the screen. However, queue_redraw doesn't queue another redraw when one is already queued. Redraws can be queued in two ways: one is timer based, which is fine, because it will be triggered no matter what; the other is frame based, which is triggered by Present events. When the screen is off, X server, depends on the driver, could send abnormal Present events, which we ignore. But that also means queued frame based redraw will stop being triggered. Those two factors combined means sometimes unredirection does not happen when the screen is off. Which means we aren't going to free the GL context, which are still receiving Present events, but can't handle them, because we are not rendering anything with GL. In the end all these causes memory usage to balloon, until the screen is turned on and we start rendering again. And all these is not caught by leak checkers because this technically is not a leak, as everything is eventually freed. Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
parent
edeb17ad46
commit
fcb9dc8cfd
1 changed files with 24 additions and 3 deletions
27
src/picom.c
27
src/picom.c
|
@ -133,6 +133,7 @@ void check_dpms_status(EV_P attr_unused, ev_timer *w, int revents attr_unused) {
|
||||||
}
|
}
|
||||||
auto now_screen_is_off = dpms_screen_is_off(r);
|
auto now_screen_is_off = dpms_screen_is_off(r);
|
||||||
if (ps->screen_is_off != now_screen_is_off) {
|
if (ps->screen_is_off != now_screen_is_off) {
|
||||||
|
log_debug("Screen is now %s", now_screen_is_off ? "off" : "on");
|
||||||
ps->screen_is_off = now_screen_is_off;
|
ps->screen_is_off = now_screen_is_off;
|
||||||
queue_redraw(ps);
|
queue_redraw(ps);
|
||||||
}
|
}
|
||||||
|
@ -145,14 +146,17 @@ void check_dpms_status(EV_P attr_unused, ev_timer *w, int revents attr_unused) {
|
||||||
* XXX move to win.c
|
* XXX move to win.c
|
||||||
*/
|
*/
|
||||||
static inline struct managed_win *find_win_all(session_t *ps, const xcb_window_t wid) {
|
static inline struct managed_win *find_win_all(session_t *ps, const xcb_window_t wid) {
|
||||||
if (!wid || PointerRoot == wid || wid == ps->root || wid == ps->overlay)
|
if (!wid || PointerRoot == wid || wid == ps->root || wid == ps->overlay) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
auto w = find_managed_win(ps, wid);
|
auto w = find_managed_win(ps, wid);
|
||||||
if (!w)
|
if (!w) {
|
||||||
w = find_toplevel(ps, wid);
|
w = find_toplevel(ps, wid);
|
||||||
if (!w)
|
}
|
||||||
|
if (!w) {
|
||||||
w = find_managed_window_or_parent(ps, wid);
|
w = find_managed_window_or_parent(ps, wid);
|
||||||
|
}
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,6 +248,11 @@ void schedule_render(session_t *ps) {
|
||||||
ps->target_msc = ps->last_msc + frame_delay;
|
ps->target_msc = ps->last_msc + frame_delay;
|
||||||
auto delay = deadline - (uint64_t)render_time_estimate - now_us;
|
auto delay = deadline - (uint64_t)render_time_estimate - now_us;
|
||||||
delay_s = (double)delay / 1000000.0;
|
delay_s = (double)delay / 1000000.0;
|
||||||
|
if (delay_s > 1) {
|
||||||
|
log_warn("Delay too long: %f s, render_time: %d us, frame_time: %" PRIu64
|
||||||
|
" us, now_us: %" PRIu64 " us, next_msc: %" PRIu64 " us",
|
||||||
|
delay_s, render_time_estimate, frame_time, now_us, deadline);
|
||||||
|
}
|
||||||
|
|
||||||
log_trace("Delay: %lu us, last_msc: %" PRIu64 ", render_time: %d, frame_time: "
|
log_trace("Delay: %lu us, last_msc: %" PRIu64 ", render_time: %d, frame_time: "
|
||||||
"%" PRIu64 ", now_us: %" PRIu64 ", next_msc: %" PRIu64,
|
"%" PRIu64 ", now_us: %" PRIu64 ", next_msc: %" PRIu64,
|
||||||
|
@ -257,6 +266,18 @@ schedule:
|
||||||
}
|
}
|
||||||
|
|
||||||
void queue_redraw(session_t *ps) {
|
void queue_redraw(session_t *ps) {
|
||||||
|
if (ps->screen_is_off) {
|
||||||
|
// The screen is off, if there is a draw queued for the next frame (i.e.
|
||||||
|
// ps->redraw_needed == true), it won't be triggered until the screen is
|
||||||
|
// on again, because the abnormal Present events we will receive from the
|
||||||
|
// X server when the screen is off. Yet we need the draw_callback to be
|
||||||
|
// called as soon as possible so the screen can be unredirected.
|
||||||
|
// So here we unconditionally start the draw timer.
|
||||||
|
ev_timer_stop(ps->loop, &ps->draw_timer);
|
||||||
|
ev_timer_set(&ps->draw_timer, 0, 0);
|
||||||
|
ev_timer_start(ps->loop, &ps->draw_timer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Whether we have already rendered for the current frame.
|
// Whether we have already rendered for the current frame.
|
||||||
// If frame pacing is not enabled, pretend this is false.
|
// If frame pacing is not enabled, pretend this is false.
|
||||||
// If --benchmark is used, redraw is always queued
|
// If --benchmark is used, redraw is always queued
|
||||||
|
|
Loading…
Reference in a new issue