mirror of
https://github.com/yshui/picom.git
synced 2024-11-11 13:51:02 -05:00
backend: add last_render_time interface.
Used for querying how long render takes to complete, used for frame pacing. Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
parent
035dac41de
commit
1536f51a91
7 changed files with 63 additions and 0 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
15
src/picom.c
15
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);
|
||||
|
|
Loading…
Reference in a new issue