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 <pekka.paalanen@collabora.com>
This commit is contained in:
Pekka Paalanen 2021-02-10 12:33:03 +02:00
parent a8c4dfead5
commit 1ed2cad87e
4 changed files with 37 additions and 3 deletions

View File

@ -1130,6 +1130,7 @@ struct weston_compositor {
clockid_t presentation_clock; clockid_t presentation_clock;
int32_t repaint_msec; int32_t repaint_msec;
struct timespec last_repaint_start;
unsigned int activate_serial; unsigned int activate_serial;

View File

@ -2919,6 +2919,7 @@ output_repaint_timer_handler(void *data)
int ret = 0; int ret = 0;
weston_compositor_read_presentation_clock(compositor, &now); weston_compositor_read_presentation_clock(compositor, &now);
compositor->last_repaint_start = now;
if (compositor->backend->repaint_begin) if (compositor->backend->repaint_begin)
repaint_data = compositor->backend->repaint_begin(compositor); repaint_data = compositor->backend->repaint_begin(compositor);

View File

@ -29,6 +29,7 @@
#define GL_RENDERER_INTERNAL_H #define GL_RENDERER_INTERNAL_H
#include <stdbool.h> #include <stdbool.h>
#include <time.h>
#include <wayland-util.h> #include <wayland-util.h>
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
@ -82,6 +83,7 @@ struct gl_shader {
GLint alpha_uniform; GLint alpha_uniform;
GLint color_uniform; GLint color_uniform;
struct wl_list link; /* gl_renderer::shader_list */ struct wl_list link; /* gl_renderer::shader_list */
struct timespec last_used;
}; };
struct gl_renderer { struct gl_renderer {
@ -158,9 +160,9 @@ struct gl_renderer {
GLint gl_half_float_type; 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 wl_list shader_list;
struct weston_log_scope *shader_scope; struct weston_log_scope *shader_scope;

View File

@ -1,6 +1,6 @@
/* /*
* Copyright © 2012 Intel Corporation * Copyright © 2012 Intel Corporation
* Copyright © 2015,2019 Collabora, Ltd. * Copyright © 2015,2019,2021 Collabora, Ltd.
* Copyright © 2016 NVIDIA Corporation * Copyright © 2016 NVIDIA Corporation
* *
* Permission is hereby granted, free of charge, to any person obtaining * 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) if (gr->current_shader == shader)
return true; 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); glUseProgram(shader->program);
gr->current_shader = shader; gr->current_shader = shader;
return true; 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 = { static const struct gl_shader_requirements requirements_triangle_fan = {
.variant = SHADER_VARIANT_SOLID, .variant = SHADER_VARIANT_SOLID,
}; };
@ -1807,6 +1835,8 @@ gl_renderer_repaint_output(struct weston_output *output,
TIMELINE_RENDER_POINT_TYPE_END); TIMELINE_RENDER_POINT_TYPE_END);
update_buffer_release_fences(compositor, output); update_buffer_release_fences(compositor, output);
gl_renderer_garbage_collect_programs(gr);
} }
static int static int