diff --git a/src/backend/backend.h b/src/backend/backend.h index ef1bcb88..7cd64a08 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -292,6 +292,14 @@ struct backend_operations { /// Optional int (*buffer_age)(backend_t *backend_data); + /// Get the render time of the last frame. If the render is still in progress, + /// returns false. The time is returned in `ts`. Frames are delimited by the + /// present() calls. i.e. after a present() call, last_render_time() should start + /// reporting the time of the just presen1ted frame. + /// + /// Optional, if not available, the most conservative estimation will be used. + bool (*last_render_time)(backend_t *backend_data, struct timespec *ts); + /// The maximum number buffer_age might return. int max_buffer_age; diff --git a/src/backend/gl/egl.c b/src/backend/gl/egl.c index dd1d4037..770bd5d4 100644 --- a/src/backend/gl/egl.c +++ b/src/backend/gl/egl.c @@ -372,6 +372,7 @@ struct backend_operations egl_ops = { .deinit = egl_deinit, .bind_pixmap = egl_bind_pixmap, .release_image = gl_release_image, + .prepare = gl_prepare, .compose = gl_compose, .image_op = gl_image_op, .set_image_property = gl_set_image_property, @@ -380,6 +381,7 @@ struct backend_operations egl_ops = { .is_image_transparent = default_is_image_transparent, .present = egl_present, .buffer_age = egl_buffer_age, + .last_render_time = gl_last_render_time, .create_shadow_context = gl_create_shadow_context, .destroy_shadow_context = gl_destroy_shadow_context, .render_shadow = backend_render_shadow_from_mask, diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index 9789eb51..b02572df 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -22,6 +22,11 @@ #include "backend/backend_common.h" #include "backend/gl/gl_common.h" +void gl_prepare(backend_t *base, const region_t *reg attr_unused) { + auto gd = (struct gl_data *)base; + glBeginQuery(GL_TIME_ELAPSED, gd->frame_timing[gd->current_frame_timing]); +} + GLuint gl_create_shader(GLenum shader_type, const char *shader_str) { log_trace("===\n%s\n===", shader_str); @@ -800,6 +805,9 @@ uint64_t gl_get_shader_attributes(backend_t *backend_data attr_unused, void *sha } bool gl_init(struct gl_data *gd, session_t *ps) { + glGenQueries(2, gd->frame_timing); + gd->current_frame_timing = 0; + // Initialize GLX data structure glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); @@ -1154,10 +1162,33 @@ void gl_present(backend_t *base, const region_t *region) { glDeleteBuffers(2, bo); glDeleteVertexArrays(1, &vao); + glEndQuery(GL_TIME_ELAPSED); + gd->current_frame_timing ^= 1; + + gl_check_err(); + free(coord); free(indices); } +bool gl_last_render_time(backend_t *base, struct timespec *ts) { + auto gd = (struct gl_data *)base; + GLint available = 0; + glGetQueryObjectiv(gd->frame_timing[gd->current_frame_timing ^ 1], + GL_QUERY_RESULT_AVAILABLE, &available); + if (!available) { + return false; + } + + GLuint64 time; + glGetQueryObjectui64v(gd->frame_timing[gd->current_frame_timing ^ 1], + GL_QUERY_RESULT, &time); + ts->tv_sec = (long)(time / 1000000000); + ts->tv_nsec = (long)(time % 1000000000); + gl_check_err(); + return true; +} + bool gl_image_op(backend_t *base, enum image_operations op, void *image_data, const region_t *reg_op, const region_t *reg_visible attr_unused, void *arg) { struct backend_image *tex = image_data; diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h index 4aad9c0d..f7e721c2 100644 --- a/src/backend/gl/gl_common.h +++ b/src/backend/gl/gl_common.h @@ -108,6 +108,8 @@ struct gl_data { gl_shadow_shader_t shadow_shader; GLuint back_texture, back_fbo; GLint back_format; + GLuint frame_timing[2]; + int current_frame_timing; GLuint present_prog; bool dithered_present; @@ -129,6 +131,7 @@ typedef struct session session_t; #define GL_PROG_MAIN_INIT \ { .prog = 0, .unifm_opacity = -1, .unifm_invert_color = -1, .unifm_tex = -1, } +void gl_prepare(backend_t *base, const region_t *reg); void x_rect_to_coords(int nrects, const rect_t *rects, coord_t image_dst, int extent_height, int texture_height, int root_height, bool y_inverted, GLint *coord, GLuint *indices); @@ -142,6 +145,7 @@ void gl_destroy_window_shader(backend_t *backend_data, void *shader); uint64_t gl_get_shader_attributes(backend_t *backend_data, void *shader); bool gl_set_image_property(backend_t *backend_data, enum image_properties prop, void *image_data, void *args); +bool gl_last_render_time(backend_t *backend_data, struct timespec *time); /** * @brief Render a region with texture data. diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c index 109bec94..b4ace133 100644 --- a/src/backend/gl/glx.c +++ b/src/backend/gl/glx.c @@ -528,6 +528,7 @@ struct backend_operations glx_ops = { .deinit = glx_deinit, .bind_pixmap = glx_bind_pixmap, .release_image = gl_release_image, + .prepare = gl_prepare, .compose = gl_compose, .image_op = gl_image_op, .set_image_property = gl_set_image_property, @@ -536,6 +537,7 @@ struct backend_operations glx_ops = { .is_image_transparent = default_is_image_transparent, .present = glx_present, .buffer_age = glx_buffer_age, + .last_render_time = gl_last_render_time, .create_shadow_context = gl_create_shadow_context, .destroy_shadow_context = gl_destroy_shadow_context, .render_shadow = backend_render_shadow_from_mask, diff --git a/src/common.h b/src/common.h index d604ab6b..9c23c2f8 100644 --- a/src/common.h +++ b/src/common.h @@ -249,6 +249,7 @@ typedef struct session { uint64_t last_render; struct rolling_avg *frame_time; + struct rolling_max *render_stats; // === Operation related === /// Flags related to the root window diff --git a/src/picom.c b/src/picom.c index 276e764b..e2ba911c 100644 --- a/src/picom.c +++ b/src/picom.c @@ -210,6 +210,19 @@ static inline struct managed_win *find_win_all(session_t *ps, const xcb_window_t void queue_redraw(session_t *ps) { // If --benchmark is used, redraw is always queued if (!ps->redraw_needed && !ps->o.benchmark) { + if (!ps->first_frame && ps->redirected && + ps->backend_data->ops->last_render_time) { + struct timespec render_time; + if (ps->backend_data->ops->last_render_time(ps->backend_data, + &render_time)) { + int render_time_us = (int)(render_time.tv_sec * 1000000 + + render_time.tv_nsec / 1000); + log_info( + "Last render call took: %d us, rolling_max: %d us", + render_time_us, rolling_max_get_max(ps->render_stats)); + rolling_max_push(ps->render_stats, render_time_us); + } + } ev_idle_start(ps->loop, &ps->draw_idle); } ps->redraw_needed = true; @@ -1868,6 +1881,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy, pixman_region32_init(&ps->screen_reg); // TODO(yshui) investigate what's the best window size + ps->render_stats = rolling_max_new(128); ps->frame_time = rolling_avg_new(16); ps->pending_reply_tail = &ps->pending_reply_head; @@ -2515,6 +2529,7 @@ static void session_destroy(session_t *ps) { free_xinerama_info(ps); rolling_avg_destroy(ps->frame_time); + rolling_max_destroy(ps->render_stats); // Release custom window shaders free(ps->o.window_shader_fg);