From 45ca284eeee7790663a10cc3b8af5ccf00ebb059 Mon Sep 17 00:00:00 2001 From: Rose Hudson Date: Fri, 2 Jun 2023 11:10:35 +0100 Subject: [PATCH] render/gles2: implement timer API --- include/render/gles2.h | 28 +++++++++- include/wlr/render/gles2.h | 1 + render/gles2/pass.c | 19 ++++++- render/gles2/renderer.c | 105 ++++++++++++++++++++++++++++++++++++- 4 files changed, 150 insertions(+), 3 deletions(-) diff --git a/include/render/gles2.h b/include/render/gles2.h index 619566fb..930cf07d 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,11 @@ #include #include +// mesa ships old GL headers that don't include this type, so for distros that use headers from +// mesa we need to def it ourselves until they update. +// https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/23144 +typedef void (GL_APIENTRYP PFNGLGETINTEGER64VEXTPROC) (GLenum pname, GLint64 *data); + struct wlr_gles2_pixel_format { uint32_t drm_format; // optional field, if empty then internalformat = format @@ -47,6 +53,7 @@ struct wlr_gles2_renderer { bool EXT_texture_type_2_10_10_10_REV; bool OES_texture_half_float_linear; bool EXT_texture_norm16; + bool EXT_disjoint_timer_query; } exts; struct { @@ -57,6 +64,12 @@ struct wlr_gles2_renderer { PFNGLPUSHDEBUGGROUPKHRPROC glPushDebugGroupKHR; PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES; PFNGLGETGRAPHICSRESETSTATUSKHRPROC glGetGraphicsResetStatusKHR; + PFNGLGENQUERIESEXTPROC glGenQueriesEXT; + PFNGLDELETEQUERIESEXTPROC glDeleteQueriesEXT; + PFNGLQUERYCOUNTEREXTPROC glQueryCounterEXT; + PFNGLGETQUERYOBJECTIVEXTPROC glGetQueryObjectivEXT; + PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT; + PFNGLGETINTEGER64VEXTPROC glGetInteger64vEXT; } procs; struct { @@ -78,6 +91,15 @@ struct wlr_gles2_renderer { uint32_t viewport_width, viewport_height; }; +struct wlr_gles2_render_timer { + struct wlr_render_timer base; + struct wlr_gles2_renderer *renderer; + struct timespec cpu_start; + struct timespec cpu_end; + GLuint id; + GLint64 gl_cpu_end; +}; + struct wlr_gles2_buffer { struct wlr_buffer *buffer; struct wlr_gles2_renderer *renderer; @@ -116,6 +138,7 @@ struct wlr_gles2_render_pass { struct wlr_render_pass base; struct wlr_gles2_buffer *buffer; float projection_matrix[9]; + struct wlr_gles2_render_timer *timer; }; bool is_gles2_pixel_format_supported(const struct wlr_gles2_renderer *renderer, @@ -128,6 +151,8 @@ const uint32_t *get_gles2_shm_formats(const struct wlr_gles2_renderer *renderer, struct wlr_gles2_renderer *gles2_get_renderer( struct wlr_renderer *wlr_renderer); +struct wlr_gles2_render_timer *gles2_get_render_timer( + struct wlr_render_timer *timer); struct wlr_gles2_texture *gles2_get_texture( struct wlr_texture *wlr_texture); @@ -140,6 +165,7 @@ void push_gles2_debug_(struct wlr_gles2_renderer *renderer, #define push_gles2_debug(renderer) push_gles2_debug_(renderer, _WLR_FILENAME, __func__) void pop_gles2_debug(struct wlr_gles2_renderer *renderer); -struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *buffer); +struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *buffer, + struct wlr_gles2_render_timer *timer); #endif diff --git a/include/wlr/render/gles2.h b/include/wlr/render/gles2.h index f8db6195..daf6aab3 100644 --- a/include/wlr/render/gles2.h +++ b/include/wlr/render/gles2.h @@ -33,6 +33,7 @@ struct wlr_gles2_texture_attribs { }; bool wlr_renderer_is_gles2(struct wlr_renderer *wlr_renderer); +bool wlr_render_timer_is_gles2(struct wlr_render_timer *timer); bool wlr_texture_is_gles2(struct wlr_texture *texture); void wlr_gles2_texture_get_attribs(struct wlr_texture *texture, struct wlr_gles2_texture_attribs *attribs); diff --git a/render/gles2/pass.c b/render/gles2/pass.c index df61d8fd..89ca873e 100644 --- a/render/gles2/pass.c +++ b/render/gles2/pass.c @@ -1,6 +1,8 @@ +#define _POSIX_C_SOURCE 199309L #include #include #include +#include #include #include "render/gles2.h" #include "types/wlr_matrix.h" @@ -18,6 +20,19 @@ static struct wlr_gles2_render_pass *get_render_pass(struct wlr_render_pass *wlr static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { struct wlr_gles2_render_pass *pass = get_render_pass(wlr_pass); struct wlr_gles2_renderer *renderer = pass->buffer->renderer; + struct wlr_gles2_render_timer *timer = pass->timer; + + if (timer) { + // clear disjoint flag + GLint64 disjoint; + renderer->procs.glGetInteger64vEXT(GL_GPU_DISJOINT_EXT, &disjoint); + // set up the query + renderer->procs.glQueryCounterEXT(timer->id, GL_TIMESTAMP_EXT); + // get end-of-CPU-work time in GL time domain + renderer->procs.glGetInteger64vEXT(GL_TIMESTAMP_EXT, &timer->gl_cpu_end); + // get end-of-CPU-work time in CPU time domain + clock_gettime(CLOCK_MONOTONIC, &timer->cpu_end); + } push_gles2_debug(renderer); glFlush(); glBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -219,7 +234,8 @@ static const char *reset_status_str(GLenum status) { } } -struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *buffer) { +struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *buffer, + struct wlr_gles2_render_timer *timer) { struct wlr_gles2_renderer *renderer = buffer->renderer; struct wlr_buffer *wlr_buffer = buffer->buffer; @@ -240,6 +256,7 @@ struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *b wlr_render_pass_init(&pass->base, &render_pass_impl); wlr_buffer_lock(wlr_buffer); pass->buffer = buffer; + pass->timer = timer; matrix_projection(pass->projection_matrix, wlr_buffer->width, wlr_buffer->height, WL_OUTPUT_TRANSFORM_FLIPPED_180); diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index c330d7ad..13ed2af0 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 199309L #include #include #include @@ -5,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +20,7 @@ #include "render/gles2.h" #include "render/pixel_format.h" #include "types/wlr_matrix.h" +#include "util/time.h" #include "common_vert_src.h" #include "quad_frag_src.h" @@ -33,6 +36,7 @@ static const GLfloat verts[] = { }; static const struct wlr_renderer_impl renderer_impl; +static const struct wlr_render_timer_impl render_timer_impl; bool wlr_renderer_is_gles2(struct wlr_renderer *wlr_renderer) { return wlr_renderer->impl == &renderer_impl; @@ -52,6 +56,16 @@ static struct wlr_gles2_renderer *gles2_get_renderer_in_context( return renderer; } +bool wlr_render_timer_is_gles2(struct wlr_render_timer *timer) { + return timer->impl == &render_timer_impl; +} + +struct wlr_gles2_render_timer *gles2_get_render_timer(struct wlr_render_timer *wlr_timer) { + assert(wlr_render_timer_is_gles2(wlr_timer)); + struct wlr_gles2_render_timer *timer = wl_container_of(wlr_timer, timer, base); + return timer; +} + static void destroy_buffer(struct wlr_gles2_buffer *buffer) { wl_list_remove(&buffer->link); wlr_addon_finish(&buffer->addon); @@ -543,18 +557,91 @@ static struct wlr_render_pass *gles2_begin_buffer_pass(struct wlr_renderer *wlr_ return NULL; } + struct wlr_gles2_render_timer *timer = gles2_get_render_timer(options->timer); + clock_gettime(CLOCK_MONOTONIC, &timer->cpu_start); + struct wlr_gles2_buffer *buffer = get_or_create_buffer(renderer, wlr_buffer); if (!buffer) { return NULL; } - struct wlr_gles2_render_pass *pass = begin_gles2_buffer_pass(buffer); + struct wlr_gles2_render_pass *pass = begin_gles2_buffer_pass(buffer, timer); if (!pass) { return NULL; } return &pass->base; } +static struct wlr_render_timer *gles2_render_timer_create(struct wlr_renderer *wlr_renderer) { + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); + if (!renderer->exts.EXT_disjoint_timer_query) { + wlr_log(WLR_ERROR, "can't create timer, EXT_disjoint_timer_query not available"); + return NULL; + } + + struct wlr_gles2_render_timer *timer = calloc(1, sizeof(struct wlr_gles2_render_timer)); + if (!timer) { + return NULL; + } + timer->base.impl = &render_timer_impl; + timer->renderer = renderer; + + struct wlr_egl_context prev_ctx; + wlr_egl_save_context(&prev_ctx); + wlr_egl_make_current(renderer->egl); + renderer->procs.glGenQueriesEXT(1, &timer->id); + wlr_egl_restore_context(&prev_ctx); + + return (struct wlr_render_timer *)timer; +} + +static int gles2_get_render_time(struct wlr_render_timer *wlr_timer) { + struct wlr_gles2_render_timer *timer = gles2_get_render_timer(wlr_timer); + struct wlr_gles2_renderer *renderer = timer->renderer; + + struct wlr_egl_context prev_ctx; + wlr_egl_save_context(&prev_ctx); + wlr_egl_make_current(renderer->egl); + + GLint64 disjoint; + renderer->procs.glGetInteger64vEXT(GL_GPU_DISJOINT_EXT, &disjoint); + if (disjoint) { + wlr_log(WLR_ERROR, "a disjoint operation occurred and the render timer is invalid"); + wlr_egl_restore_context(&prev_ctx); + return -1; + } + + GLint available; + renderer->procs.glGetQueryObjectivEXT(timer->id, + GL_QUERY_RESULT_AVAILABLE_EXT, &available); + if (!available) { + wlr_log(WLR_ERROR, "timer was read too early, gpu isn't done!"); + wlr_egl_restore_context(&prev_ctx); + return -1; + } + + GLuint64 gl_render_end; + renderer->procs.glGetQueryObjectui64vEXT(timer->id, GL_QUERY_RESULT_EXT, + &gl_render_end); + + int64_t cpu_nsec_total = timespec_to_nsec(&timer->cpu_end) - timespec_to_nsec(&timer->cpu_start); + + wlr_egl_restore_context(&prev_ctx); + return gl_render_end - timer->gl_cpu_end + cpu_nsec_total; +} + +static void gles2_render_timer_destroy(struct wlr_render_timer *wlr_timer) { + struct wlr_gles2_render_timer *timer = (struct wlr_gles2_render_timer *)wlr_timer; + struct wlr_gles2_renderer *renderer = timer->renderer; + + struct wlr_egl_context prev_ctx; + wlr_egl_save_context(&prev_ctx); + wlr_egl_make_current(renderer->egl); + renderer->procs.glDeleteQueriesEXT(1, &timer->id); + wlr_egl_restore_context(&prev_ctx); + free(timer); +} + static const struct wlr_renderer_impl renderer_impl = { .destroy = gles2_destroy, .bind_buffer = gles2_bind_buffer, @@ -573,6 +660,12 @@ static const struct wlr_renderer_impl renderer_impl = { .get_render_buffer_caps = gles2_get_render_buffer_caps, .texture_from_buffer = gles2_texture_from_buffer, .begin_buffer_pass = gles2_begin_buffer_pass, + .render_timer_create = gles2_render_timer_create, +}; + +static const struct wlr_render_timer_impl render_timer_impl = { + .get_duration_ns = gles2_get_render_time, + .destroy = gles2_render_timer_destroy, }; void push_gles2_debug_(struct wlr_gles2_renderer *renderer, @@ -812,6 +905,16 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { } } + if (check_gl_ext(exts_str, "GL_EXT_disjoint_timer_query")) { + renderer->exts.EXT_disjoint_timer_query = true; + load_gl_proc(&renderer->procs.glGenQueriesEXT, "glGenQueriesEXT"); + load_gl_proc(&renderer->procs.glDeleteQueriesEXT, "glDeleteQueriesEXT"); + load_gl_proc(&renderer->procs.glQueryCounterEXT, "glQueryCounterEXT"); + load_gl_proc(&renderer->procs.glGetQueryObjectivEXT, "glGetQueryObjectivEXT"); + load_gl_proc(&renderer->procs.glGetQueryObjectui64vEXT, "glGetQueryObjectui64vEXT"); + load_gl_proc(&renderer->procs.glGetInteger64vEXT, "glGetInteger64vEXT"); + } + if (renderer->exts.KHR_debug) { glEnable(GL_DEBUG_OUTPUT_KHR); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);