From 1ed2cad87ec6f04e8c20a5690c7a0fb5c55749f9 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 10 Feb 2021 12:33:03 +0200 Subject: [PATCH] gl-renderer: garbage-collect old shaders This adds a heuristic for freeing shader programs that have not been needed for a while. The intention is to stop Weston accumulating shader programs indefinitely, especially in the future when color management will explode the number of possible different shader programs. Shader programs that have not been used in the past minute are freed, except always keep the ten most recently used shader programs anyway. The former rule is to ensure we keep shader programs that are actively used regardless of how many. The latter rule is to prevent freeing too many shader programs after Weston has been idle for a long time and then repaints just a small area. Many of the shader programs could still be relevant even though not needed in the first repaint after idle. The numbers ten and one minute in the above are arbitrary and not based on anything. These heuristics are simpler to implement than e.g. views taking references on shader programs. Expiry by time allows shader programs to survive a while even after their last user is gone, with the hope of being re-used soon. Tracking actual use instead of references also adapts to what is actually visible rather than what merely exists. Keeping the shader list in most recently used order might also make gl_renderer_get_program() more efficient on average. last_repaint_start time is used for shader timestamp to avoid calling clock_gettime() more often. Adding that variable is an ABI break, but libweston major has already been bumped to 10 since last release. Signed-off-by: Pekka Paalanen --- include/libweston/libweston.h | 1 + libweston/compositor.c | 1 + libweston/renderer-gl/gl-renderer-internal.h | 6 ++-- libweston/renderer-gl/gl-renderer.c | 32 +++++++++++++++++++- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 4cdd2744..4daef0d3 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1130,6 +1130,7 @@ struct weston_compositor { clockid_t presentation_clock; int32_t repaint_msec; + struct timespec last_repaint_start; unsigned int activate_serial; diff --git a/libweston/compositor.c b/libweston/compositor.c index 22a3a897..6e965372 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2919,6 +2919,7 @@ output_repaint_timer_handler(void *data) int ret = 0; weston_compositor_read_presentation_clock(compositor, &now); + compositor->last_repaint_start = now; if (compositor->backend->repaint_begin) repaint_data = compositor->backend->repaint_begin(compositor); diff --git a/libweston/renderer-gl/gl-renderer-internal.h b/libweston/renderer-gl/gl-renderer-internal.h index 9e032ac7..b52c3c1d 100644 --- a/libweston/renderer-gl/gl-renderer-internal.h +++ b/libweston/renderer-gl/gl-renderer-internal.h @@ -29,6 +29,7 @@ #define GL_RENDERER_INTERNAL_H #include +#include #include #include @@ -82,6 +83,7 @@ struct gl_shader { GLint alpha_uniform; GLint color_uniform; struct wl_list link; /* gl_renderer::shader_list */ + struct timespec last_used; }; struct gl_renderer { @@ -158,9 +160,9 @@ struct gl_renderer { GLint gl_half_float_type; - /** struct gl_shader::link + /** Shader program cache in most recently used order * - * List constains cached shaders built from struct gl_shader_requirements + * Uses struct gl_shader::link. */ struct wl_list shader_list; struct weston_log_scope *shader_scope; diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index ed5632d2..81c59c2a 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1,6 +1,6 @@ /* * Copyright © 2012 Intel Corporation - * Copyright © 2015,2019 Collabora, Ltd. + * Copyright © 2015,2019,2021 Collabora, Ltd. * Copyright © 2016 NVIDIA Corporation * * Permission is hereby granted, free of charge, to any person obtaining @@ -774,11 +774,39 @@ gl_renderer_use_program(struct gl_renderer *gr, struct gl_shader **shaderp) if (gr->current_shader == shader) return true; + if (shader != gr->fallback_shader) { + /* Update list order for most recently used. */ + wl_list_remove(&shader->link); + wl_list_insert(&gr->shader_list, &shader->link); + } + shader->last_used = gr->compositor->last_repaint_start; + glUseProgram(shader->program); gr->current_shader = shader; return true; } +static void +gl_renderer_garbage_collect_programs(struct gl_renderer *gr) +{ + struct gl_shader *shader, *tmp; + unsigned count = 0; + + wl_list_for_each_safe(shader, tmp, &gr->shader_list, link) { + /* Keep the 10 most recently used always. */ + if (count++ < 10) + continue; + + /* Keep everything used in the past 1 minute. */ + if (timespec_sub_to_msec(&gr->compositor->last_repaint_start, + &shader->last_used) < 60000) + continue; + + /* The rest throw away. */ + gl_shader_destroy(gr, shader); + } +} + static const struct gl_shader_requirements requirements_triangle_fan = { .variant = SHADER_VARIANT_SOLID, }; @@ -1807,6 +1835,8 @@ gl_renderer_repaint_output(struct weston_output *output, TIMELINE_RENDER_POINT_TYPE_END); update_buffer_release_fences(compositor, output); + + gl_renderer_garbage_collect_programs(gr); } static int