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:
Yuxuan Shui 2022-12-13 20:31:32 +00:00
parent e00686b1e6
commit 035dac41de
No known key found for this signature in database
GPG Key ID: D3A4405BE6CC17F4
3 changed files with 89 additions and 2 deletions

View File

@ -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

View File

@ -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;

View File

@ -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);