1
0
Fork 0
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:
Yuxuan Shui 2022-12-14 08:07:36 +00:00
parent 035dac41de
commit 1536f51a91
No known key found for this signature in database
GPG key ID: D3A4405BE6CC17F4
7 changed files with 63 additions and 0 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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