mirror of
https://github.com/yshui/picom.git
synced 2024-11-11 13:51:02 -05:00
core: collect frame timing information.
Needed to estimate refresh rate, and for us to stay in phase with vblank. Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
parent
e00686b1e6
commit
035dac41de
3 changed files with 89 additions and 2 deletions
|
@ -240,6 +240,15 @@ typedef struct session {
|
|||
bool first_frame;
|
||||
/// Whether screen has been turned off
|
||||
bool screen_is_off;
|
||||
/// Event context for X Present extension.
|
||||
uint32_t present_event_id;
|
||||
xcb_special_event_t *present_event;
|
||||
/// Last MSC event, in useconds.
|
||||
uint64_t last_msc;
|
||||
/// When did we render our last frame.
|
||||
uint64_t last_render;
|
||||
|
||||
struct rolling_avg *frame_time;
|
||||
|
||||
// === Operation related ===
|
||||
/// Flags related to the root window
|
||||
|
|
78
src/picom.c
78
src/picom.c
|
@ -1342,6 +1342,24 @@ static bool redirect_start(session_t *ps) {
|
|||
pixman_region32_init(&ps->damage_ring[i]);
|
||||
}
|
||||
|
||||
if (ps->present_exists) {
|
||||
ps->present_event_id = x_new_id(ps->c);
|
||||
auto select_input = xcb_present_select_input(
|
||||
ps->c, ps->present_event_id, session_get_target_window(ps),
|
||||
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
|
||||
auto notify_msc =
|
||||
xcb_present_notify_msc(ps->c, session_get_target_window(ps), 0, 0, 1, 0);
|
||||
set_cant_fail_cookie(ps, select_input);
|
||||
set_cant_fail_cookie(ps, notify_msc);
|
||||
ps->present_event = xcb_register_for_special_xge(
|
||||
ps->c, &xcb_present_id, ps->present_event_id, NULL);
|
||||
|
||||
// Initialize rendering and frame timing statistics.
|
||||
ps->last_msc = 0;
|
||||
ps->last_render = 0;
|
||||
rolling_avg_reset(ps->frame_time);
|
||||
}
|
||||
|
||||
// Must call XSync() here
|
||||
x_sync(ps->c);
|
||||
|
||||
|
@ -1383,6 +1401,14 @@ static void unredirect(session_t *ps) {
|
|||
free(ps->damage_ring);
|
||||
ps->damage_ring = ps->damage = NULL;
|
||||
|
||||
if (ps->present_event_id) {
|
||||
xcb_present_select_input(ps->c, ps->present_event_id,
|
||||
session_get_target_window(ps), 0);
|
||||
ps->present_event_id = XCB_NONE;
|
||||
xcb_unregister_for_special_event(ps->c, ps->present_event);
|
||||
ps->present_event = NULL;
|
||||
}
|
||||
|
||||
// Must call XSync() here
|
||||
x_sync(ps->c);
|
||||
|
||||
|
@ -1390,9 +1416,53 @@ static void unredirect(session_t *ps) {
|
|||
log_debug("Screen unredirected.");
|
||||
}
|
||||
|
||||
static void
|
||||
handle_present_complete_notify(session_t *ps, xcb_present_complete_notify_event_t *cne) {
|
||||
if (cne->kind != XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC) {
|
||||
return;
|
||||
}
|
||||
if (cne->ust <= ps->last_msc) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto cookie = xcb_present_notify_msc(ps->c, session_get_target_window(ps), 0,
|
||||
cne->msc + 1, 0, 0);
|
||||
set_cant_fail_cookie(ps, cookie);
|
||||
|
||||
if (ps->last_msc != 0) {
|
||||
int frame_time = (int)(cne->ust - ps->last_msc);
|
||||
rolling_avg_push(ps->frame_time, frame_time);
|
||||
log_trace("Frame time: %d us, rolling average: %lf us, msc: %" PRIu64,
|
||||
frame_time, rolling_avg_get_avg(ps->frame_time), cne->ust);
|
||||
}
|
||||
ps->last_msc = cne->ust;
|
||||
}
|
||||
|
||||
static void handle_present_events(session_t *ps) {
|
||||
if (!ps->present_event) {
|
||||
return;
|
||||
}
|
||||
xcb_present_generic_event_t *ev;
|
||||
while ((ev = (void *)xcb_poll_for_special_event(ps->c, ps->present_event))) {
|
||||
if (ev->event != ps->present_event_id) {
|
||||
// This event doesn't have the right event context, it's not meant
|
||||
// for us.
|
||||
goto next;
|
||||
}
|
||||
|
||||
// We only subscribed to the complete notify event.
|
||||
assert(ev->evtype == XCB_PRESENT_EVENT_COMPLETE_NOTIFY);
|
||||
handle_present_complete_notify(ps, (void *)ev);
|
||||
next:
|
||||
free(ev);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle queued events before we go to sleep
|
||||
static void handle_queued_x_events(EV_P attr_unused, ev_prepare *w, int revents attr_unused) {
|
||||
session_t *ps = session_ptr(w, event_check);
|
||||
handle_present_events(ps);
|
||||
|
||||
xcb_generic_event_t *ev;
|
||||
while ((ev = xcb_poll_for_queued_event(ps->c))) {
|
||||
ev_handle(ps, ev);
|
||||
|
@ -1748,6 +1818,8 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
|||
.white_picture = XCB_NONE,
|
||||
.shadow_context = NULL,
|
||||
|
||||
.last_msc = 0,
|
||||
|
||||
#ifdef CONFIG_VSYNC_DRM
|
||||
.drm_fd = -1,
|
||||
#endif
|
||||
|
@ -1767,6 +1839,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
|||
.randr_exists = 0,
|
||||
.randr_event = 0,
|
||||
.randr_error = 0,
|
||||
.present_event_id = XCB_NONE,
|
||||
.glx_exists = false,
|
||||
.glx_event = 0,
|
||||
.glx_error = 0,
|
||||
|
@ -1794,6 +1867,9 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
|||
ps->loop = EV_DEFAULT;
|
||||
pixman_region32_init(&ps->screen_reg);
|
||||
|
||||
// TODO(yshui) investigate what's the best window size
|
||||
ps->frame_time = rolling_avg_new(16);
|
||||
|
||||
ps->pending_reply_tail = &ps->pending_reply_head;
|
||||
|
||||
ps->o.show_all_xerrors = all_xerrors;
|
||||
|
@ -2438,6 +2514,8 @@ static void session_destroy(session_t *ps) {
|
|||
free(ps->o.glx_fshader_win_str);
|
||||
free_xinerama_info(ps);
|
||||
|
||||
rolling_avg_destroy(ps->frame_time);
|
||||
|
||||
// Release custom window shaders
|
||||
free(ps->o.window_shader_fg);
|
||||
struct shader_info *shader, *tmp;
|
||||
|
|
|
@ -292,14 +292,14 @@ int next_power_of_two(int n);
|
|||
struct rolling_max;
|
||||
|
||||
struct rolling_max *rolling_max_new(int window_size);
|
||||
void rolling_max_free(struct rolling_max *rm);
|
||||
void rolling_max_destroy(struct rolling_max *rm);
|
||||
void rolling_max_reset(struct rolling_max *rm);
|
||||
void rolling_max_push(struct rolling_max *rm, int val);
|
||||
int rolling_max_get_max(struct rolling_max *rm);
|
||||
|
||||
struct rolling_avg;
|
||||
struct rolling_avg *rolling_avg_new(int window_size);
|
||||
void rolling_avg_free(struct rolling_avg *ra);
|
||||
void rolling_avg_destroy(struct rolling_avg *ra);
|
||||
void rolling_avg_reset(struct rolling_avg *ra);
|
||||
void rolling_avg_push(struct rolling_avg *ra, int val);
|
||||
double rolling_avg_get_avg(struct rolling_avg *ra);
|
||||
|
|
Loading…
Reference in a new issue