gl-renderer: Add texture parameters utilities

Cache and update texture parameters only when changed by storing
minification/magnification filters, wrap modes and their target into a
dedicated gl_texture_parameters structure and by calling new utility
functions to create and flush parameters.

This structure is itself stored into the buffer state for each texture
of a surface and into the output state for border and shadow textures.

The shader config filled before emitting a draw call now just takes a
pointer to the array of texture ids, a pointer to the array of
parameters and the number of textures.

This allows to simplify the logic in gl-renderer.c, to minimise GL
state changes and to let the utility wrapper validate the parameters.

Signed-off-by: Loïc Molinari <loic.molinari@collabora.com>
This commit is contained in:
Loïc Molinari 2024-11-08 15:18:10 +01:00 committed by Daniel Stone
parent acc40ce2bb
commit 5342945bb8
5 changed files with 237 additions and 77 deletions

View file

@ -299,6 +299,26 @@ static_assert(sizeof(struct gl_shader_requirements) ==
4 /* total bitfield size in bytes */,
"struct gl_shader_requirements must not contain implicit padding");
enum gl_texture_flag {
TEXTURE_FILTERS_DIRTY = 1 << 0,
TEXTURE_WRAP_MODES_DIRTY = 1 << 1,
TEXTURE_ALL_DIRTY = (TEXTURE_FILTERS_DIRTY |
TEXTURE_WRAP_MODES_DIRTY),
};
struct gl_texture_parameters {
GLenum target;
union {
struct { GLint min, mag; };
GLint array[2];
} filters;
union {
struct { GLint s, t, r; };
GLint array[3];
} wrap_modes;
uint32_t flags;
};
struct gl_shader;
struct weston_color_transform;
struct dmabuf_allocator;
@ -311,8 +331,11 @@ struct gl_shader_config {
float view_alpha;
GLfloat unicolor[4];
GLfloat tint[4];
GLint input_tex_filter; /* GL_NEAREST or GL_LINEAR */
GLuint input_tex[SHADER_INPUT_TEX_MAX];
struct gl_texture_parameters *input_param;
GLuint *input_tex;
int input_num;
GLuint wireframe_tex;
union {
@ -589,6 +612,18 @@ gl_texture_3d_store(struct gl_renderer *gr,
void
gl_texture_fini(GLuint *tex);
void
gl_texture_parameters_init(struct gl_renderer *gr,
struct gl_texture_parameters *parameters,
GLenum target,
const GLint *filters,
const GLint *wrap_modes,
bool flush);
void
gl_texture_parameters_flush(struct gl_renderer *gr,
struct gl_texture_parameters *parameters);
bool
gl_fbo_is_format_supported(struct gl_renderer *gr,
GLenum format);

View file

@ -145,6 +145,7 @@ struct gl_output_state {
struct gl_border_image borders_pending[4];
struct gl_border_image borders_current[4];
enum gl_border_status border_status;
struct gl_texture_parameters borders_param[4];
GLuint borders_tex[4];
struct weston_matrix output_matrix;
@ -156,6 +157,7 @@ struct gl_output_state {
struct wl_list timeline_render_point_list;
const struct pixel_format_info *shadow_format;
struct gl_texture_parameters shadow_param;
GLuint shadow_tex;
GLuint shadow_fb;
@ -243,6 +245,7 @@ struct gl_buffer_state {
enum gl_shader_texture_variant shader_variant;
struct gl_format_info texture_format[3];
struct gl_texture_parameters parameters[3];
GLuint textures[3];
int num_textures;
@ -1338,13 +1341,11 @@ prepare_placeholder(struct gl_shader_config *sconf,
*sconf = alt;
}
static void
gl_shader_config_set_input_textures(struct gl_shader_config *sconf,
struct gl_surface_state *gs)
struct gl_buffer_state *gb)
{
struct gl_buffer_state *gb = gs->buffer;
int i;
sconf->req.variant = gb->shader_variant;
sconf->req.color_channel_order = gb->gl_channel_order;
sconf->req.input_is_premult =
@ -1352,22 +1353,22 @@ gl_shader_config_set_input_textures(struct gl_shader_config *sconf,
copy_uniform4f(sconf->unicolor, gb->color);
assert(gb->num_textures <= SHADER_INPUT_TEX_MAX);
for (i = 0; i < gb->num_textures; i++)
sconf->input_tex[i] = gb->textures[i];
for (; i < SHADER_INPUT_TEX_MAX; i++)
sconf->input_tex[i] = 0;
sconf->input_param = gb->parameters;
sconf->input_tex = gb->textures;
sconf->input_num = gb->num_textures;
}
static bool
gl_shader_config_init_for_paint_node(struct gl_shader_config *sconf,
struct weston_paint_node *pnode,
GLint filter)
struct weston_paint_node *pnode)
{
struct gl_renderer *gr = get_renderer(pnode->surface->compositor);
struct gl_surface_state *gs = get_surface_state(pnode->surface);
struct gl_buffer_state *gb = gs->buffer;
struct gl_output_state *go = get_output_state(pnode->output);
struct weston_buffer *buffer = gs->buffer_ref.buffer;
GLint filter;
int i;
if (!pnode->surf_xform_valid)
return false;
@ -1378,7 +1379,6 @@ gl_shader_config_init_for_paint_node(struct gl_shader_config *sconf,
.surface_to_buffer =
pnode->view->surface->surface_to_buffer_matrix,
.view_alpha = pnode->view->alpha,
.input_tex_filter = filter,
};
weston_matrix_multiply(&sconf->projection, &go->output_matrix);
@ -1394,7 +1394,16 @@ gl_shader_config_init_for_paint_node(struct gl_shader_config *sconf,
weston_matrix_translate(&sconf->surface_to_buffer, 0, 1, 0);
}
gl_shader_config_set_input_textures(sconf, gs);
gl_shader_config_set_input_textures(sconf, gb);
filter = pnode->needs_filtering ? GL_LINEAR : GL_NEAREST;
for (i = 0; i < gb->num_textures; i++) {
if (filter != gb->parameters[i].filters.min) {
gb->parameters[i].filters.min = filter;
gb->parameters[i].filters.mag = filter;
gb->parameters[i].flags |= TEXTURE_FILTERS_DIRTY;
}
}
if (!gl_shader_config_set_color_transform(gr, sconf, pnode->surf_xform.transform)) {
weston_log("GL-renderer: failed to generate a color transformation.\n");
@ -1754,7 +1763,6 @@ draw_paint_node(struct weston_paint_node *pnode,
pixman_region32_t surface_opaque;
/* non-opaque region in surface coordinates: */
pixman_region32_t surface_blend;
GLint filter;
struct gl_shader_config sconf;
struct clipper_quad *quads = NULL;
int nquads;
@ -1772,12 +1780,7 @@ draw_paint_node(struct weston_paint_node *pnode,
if (!pnode->draw_solid && ensure_surface_buffer_is_ready(gr, gs) < 0)
goto out;
if (pnode->needs_filtering)
filter = GL_LINEAR;
else
filter = GL_NEAREST;
if (!gl_shader_config_init_for_paint_node(&sconf, pnode, filter))
if (!gl_shader_config_init_for_paint_node(&sconf, pnode))
goto out;
/* XXX: Should we be using ev->transform.opaque here? */
@ -1947,6 +1950,8 @@ static void
update_wireframe_tex(struct gl_renderer *gr,
const struct weston_geometry *area)
{
GLint filters[] = { GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR };
struct gl_texture_parameters params;
int new_size, i;
uint8_t *buffer;
@ -1972,9 +1977,8 @@ update_wireframe_tex(struct gl_renderer *gr,
glActiveTexture(GL_TEXTURE0 + TEX_UNIT_WIREFRAME);
gl_texture_2d_init(gr, (int) log2(new_size) + 1, GL_R8, new_size, 1,
&gr->wireframe_tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
gl_texture_parameters_init(gr, &params, GL_TEXTURE_2D, filters, NULL,
true);
gr->wireframe_size = new_size;
/* Store mip chain with a wireframe thickness of 1.0. */
@ -2010,12 +2014,9 @@ update_borders_tex(struct gl_renderer *gr,
gl_texture_2d_init(
gr, 1, GL_BGRA8_EXT, pending->tex_width,
pending->height, &go->borders_tex[i]);
glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
gl_texture_parameters_init(
gr, &go->borders_param[i],
GL_TEXTURE_2D, NULL, NULL, false);
}
}
@ -2043,8 +2044,9 @@ draw_output_border_texture(struct gl_renderer *gr,
/* An empty border image (as allowed by output_set_borders) would use
* the default (incomplete) OpenGL ES texture which, per spec, returns
* (0, 0, 0, 1) in the fragment shader. */
sconf->input_tex_filter = GL_NEAREST;
sconf->input_tex[0] = go->borders_tex[side];
sconf->input_tex = &go->borders_tex[side];
sconf->input_param = &go->borders_param[side];
sconf->input_num = 1;
gl_renderer_use_program(gr, sconf);
GLfloat texcoord[] = {
@ -2278,8 +2280,9 @@ blit_shadow_to_output(struct weston_output *output,
WESTON_MATRIX_TRANSFORM_TRANSLATE,
},
.view_alpha = 1.0f,
.input_tex_filter = GL_NEAREST,
.input_tex[0] = go->shadow_tex,
.input_tex = &go->shadow_tex,
.input_param = &go->shadow_param,
.input_num = 1,
};
struct gl_renderer *gr = get_renderer(output->compositor);
double width = go->area.width;
@ -2736,14 +2739,12 @@ ensure_textures(struct gl_buffer_state *gb, GLenum target, int num_textures)
assert(gb->num_textures == 0);
for (i = 0; i < num_textures; i++) {
glGenTextures(1, &gb->textures[i]);
glBindTexture(target, gb->textures[i]);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
glGenTextures(num_textures, gb->textures);
gb->num_textures = num_textures;
glBindTexture(target, 0);
for (i = 0; i < num_textures; i++)
gl_texture_parameters_init(gb->gr, &gb->parameters[i], target,
NULL, NULL, false);
}
static void
@ -2874,10 +2875,8 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer)
gl_texture_2d_init(gr, 1, texture_format[i].internal,
buffer->width / hsub, buffer->height / vsub,
&gb->textures[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
gl_texture_parameters_init(gr, &gb->parameters[i],
GL_TEXTURE_2D, NULL, NULL, false);
}
}
@ -3524,7 +3523,6 @@ gl_renderer_attach_buffer(struct weston_surface *surface,
struct gl_renderer *gr = get_renderer(surface->compositor);
struct gl_surface_state *gs = get_surface_state(surface);
struct gl_buffer_state *gb;
GLenum target;
int i;
assert(buffer->renderer_private);
@ -3535,14 +3533,14 @@ gl_renderer_attach_buffer(struct weston_surface *surface,
if (gb->specified)
return;
target = gl_shader_texture_variant_get_target(gb->shader_variant);
for (i = 0; i < gb->num_images; ++i) {
glBindTexture(target, gb->textures[i]);
glBindTexture(gb->parameters[i].target, gb->textures[i]);
if (gl_extensions_has(gr, EXTENSION_EXT_EGL_IMAGE_STORAGE))
gr->image_target_tex_storage(target, gb->images[i],
NULL);
gr->image_target_tex_storage(gb->parameters[i].target,
gb->images[i], NULL);
else
gr->image_target_texture_2d(target, gb->images[i]);
gr->image_target_texture_2d(gb->parameters[i].target,
gb->images[i]);
}
gb->specified = true;
@ -3763,7 +3761,6 @@ gl_renderer_surface_copy_content(struct weston_surface *surface,
};
struct gl_shader_config sconf = {
.view_alpha = 1.0f,
.input_tex_filter = GL_NEAREST,
};
const pixman_format_code_t format = PIXMAN_a8b8g8r8;
struct gl_renderer *gr = get_renderer(surface->compositor);
@ -3794,7 +3791,7 @@ gl_renderer_surface_copy_content(struct weston_surface *surface,
break;
}
gl_shader_config_set_input_textures(&sconf, gs);
gl_shader_config_set_input_textures(&sconf, gb);
if (!gl_fbo_init(gr, GL_RGBA8, cw, ch, &fbo, &rb)) {
weston_log("Failed to init FBO\n");
@ -4037,6 +4034,8 @@ gl_renderer_resize_output(struct weston_output *output,
ret = gl_fbo_texture_init(gr, shfmt->gl.internal, area->width,
area->height, &go->shadow_fb,
&go->shadow_tex);
gl_texture_parameters_init(gr, &go->shadow_param, GL_TEXTURE_2D, NULL,
NULL, false);
return ret;
}

View file

@ -181,8 +181,10 @@ gl_color_curve_lut_3x1d(struct gl_renderer *gr,
const struct weston_color_curve *curve,
struct weston_color_transform *xform)
{
GLint filters[] = { GL_LINEAR, GL_LINEAR };
const unsigned lut_len = curve->u.lut_3x1d.optimal_len;
const unsigned nr_rows = 4;
struct gl_texture_parameters params;
GLuint tex;
float *lut;
@ -199,12 +201,10 @@ gl_color_curve_lut_3x1d(struct gl_renderer *gr,
curve->u.lut_3x1d.fill_in(xform, lut, lut_len);
gl_texture_2d_init(gr, 1, GL_R32F, lut_len, nr_rows, &tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl_texture_2d_store(gr, 0, 0, 0, lut_len, nr_rows, GL_RED, GL_FLOAT,
lut);
gl_texture_parameters_init(gr, &params, GL_TEXTURE_2D, filters, NULL,
true);
free(lut);
@ -222,7 +222,8 @@ gl_3d_lut(struct gl_renderer *gr,
struct gl_renderer_color_transform *gl_xform,
struct weston_color_transform *xform)
{
GLint filters[] = { GL_LINEAR, GL_LINEAR };
struct gl_texture_parameters params;
GLuint tex3d;
float *lut;
const unsigned dim_size = xform->mapping.u.lut3d.optimal_len;
@ -235,13 +236,10 @@ gl_3d_lut(struct gl_renderer *gr,
gl_texture_3d_init(gr, 1, GL_RGB32F, dim_size, dim_size, dim_size,
&tex3d);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
gl_texture_3d_store(gr, 0, 0, 0, 0, dim_size, dim_size, dim_size,
GL_RGB, GL_FLOAT, lut);
gl_texture_parameters_init(gr, &params, GL_TEXTURE_3D, filters, NULL,
true);
glBindTexture(GL_TEXTURE_3D, 0);
gl_xform->mapping.type = SHADER_COLOR_MAPPING_3DLUT;

View file

@ -657,11 +657,10 @@ gl_shader_texture_variant_get_target(enum gl_shader_texture_variant v)
}
static void
gl_shader_load_config(struct gl_shader *shader,
gl_shader_load_config(struct gl_renderer *gr,
struct gl_shader *shader,
const struct gl_shader_config *sconf)
{
GLint in_filter = sconf->input_tex_filter;
GLenum in_tgt;
GLsizei n_params;
int i;
@ -679,18 +678,15 @@ gl_shader_load_config(struct gl_shader *shader,
glUniform1f(shader->view_alpha_uniform, sconf->view_alpha);
in_tgt = gl_shader_texture_variant_get_target(sconf->req.variant);
for (i = 0; i < SHADER_INPUT_TEX_MAX; i++) {
if (sconf->input_tex[i] == 0)
continue;
assert(sconf->input_num <= SHADER_INPUT_TEX_MAX);
for (i = 0; i < sconf->input_num; i++) {
assert(shader->tex_uniforms[i] != -1);
glUniform1i(shader->tex_uniforms[i], TEX_UNIT_IMAGES + i);
glActiveTexture(GL_TEXTURE0 + TEX_UNIT_IMAGES + i);
glBindTexture(in_tgt, sconf->input_tex[i]);
glTexParameteri(in_tgt, GL_TEXTURE_MIN_FILTER, in_filter);
glTexParameteri(in_tgt, GL_TEXTURE_MAG_FILTER, in_filter);
glBindTexture(sconf->input_param[i].target,
sconf->input_tex[i]);
if (sconf->input_tex[i])
gl_texture_parameters_flush(gr, &sconf->input_param[i]);
}
/* Fixed texture unit for color_pre_curve LUT if it is available */
@ -809,7 +805,7 @@ gl_renderer_use_program(struct gl_renderer *gr,
gr->current_shader = shader;
}
gl_shader_load_config(shader, sconf);
gl_shader_load_config(gr, shader, sconf);
return true;
}

View file

@ -579,6 +579,63 @@ is_valid_combination_es2(struct gl_renderer *gr,
}
}
/* Validate texture parameters.
*/
static bool
are_valid_texture_parameters(struct gl_renderer *gr,
struct gl_texture_parameters *parameters)
{
GLint tex = 0;
int i;
if (parameters->target == GL_TEXTURE_2D)
glGetIntegerv(GL_TEXTURE_BINDING_2D, &tex);
else if (parameters->target == GL_TEXTURE_3D)
glGetIntegerv(GL_TEXTURE_BINDING_3D, &tex);
else if (parameters->target == GL_TEXTURE_EXTERNAL_OES)
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &tex);
if (tex == 0)
return false;
/* Filters. */
for (i = 0; i < 2; i++) {
switch (parameters->filters.array[i]) {
case GL_NEAREST:
case GL_LINEAR:
break;
case GL_NEAREST_MIPMAP_NEAREST:
case GL_NEAREST_MIPMAP_LINEAR:
case GL_LINEAR_MIPMAP_NEAREST:
case GL_LINEAR_MIPMAP_LINEAR:
/* Minification filter only. */
if (parameters->target == GL_TEXTURE_EXTERNAL_OES ||
&parameters->filters.array[i] == &parameters->filters.mag)
return false;
break;
default:
return false;
};
}
/* Wrap modes. OpenGL ES 3.2 (and extensions) has GL_CLAMP_TO_BORDER but
* Weston doesn't need it. */
for (i = 0; i < 3; i++) {
switch (parameters->wrap_modes.array[i]) {
case GL_CLAMP_TO_EDGE:
case GL_REPEAT:
case GL_MIRRORED_REPEAT:
break;
default:
return false;
};
}
return true;
}
#endif /* !defined(NDEBUG) */
/* Get the supported BGRA8 texture creation method. This is needed to correctly
@ -1214,6 +1271,81 @@ gl_texture_fini(GLuint *tex)
*tex = 0;
}
/* Initialise texture parameters. 'target' is either a 2D, a 3D or an external
* texture target. 'filters' points to an array of 2 values for respectively the
* texture minification and magnification filters. 'wrap_modes' points to an
* array of 3 values for the S, T and R texture wrap modes. The texture object
* bound to the given texture target (of the active texture) is updated if
* 'flush' is true, make sure it's properly bound in that case. The parameters
* and the flags bitfield can then directly be set and flushed when needed.
*
* filters are set to GL_NEAREST if 'filters' is NULL and wrap modes are set to
* GL_CLAMP_TO_EDGE if 'wrap_modes' is NULL.
*
* See gl_texture_parameters_flush().
*/
void
gl_texture_parameters_init(struct gl_renderer *gr,
struct gl_texture_parameters *parameters,
GLenum target,
const GLint *filters,
const GLint *wrap_modes,
bool flush)
{
GLint default_filters[] = { GL_NEAREST, GL_NEAREST };
GLint default_wrap_modes[] = { GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE,
GL_CLAMP_TO_EDGE };
assert(target == GL_TEXTURE_2D ||
target == GL_TEXTURE_3D ||
target == GL_TEXTURE_EXTERNAL_OES);
assert(target != GL_TEXTURE_3D ||
gl_features_has(gr, FEATURE_TEXTURE_3D));
assert(target != GL_TEXTURE_EXTERNAL_OES ||
gl_extensions_has(gr, EXTENSION_OES_EGL_IMAGE_EXTERNAL));
parameters->target = target;
memcpy(&parameters->filters, filters ? filters : default_filters,
sizeof default_filters);
memcpy(&parameters->wrap_modes, wrap_modes ? wrap_modes :
default_wrap_modes, sizeof default_wrap_modes);
parameters->flags = TEXTURE_ALL_DIRTY;
if (flush)
gl_texture_parameters_flush(gr, parameters);
}
/* Flush texture parameters to the texture object currently bound to the texture
* target (of the active texture) set at initialisation.
*
* See gl_texture_parameters_init().
*/
void
gl_texture_parameters_flush(struct gl_renderer *gr,
struct gl_texture_parameters *parameters)
{
assert(are_valid_texture_parameters(gr, parameters));
if (parameters->flags & TEXTURE_FILTERS_DIRTY) {
glTexParameteri(parameters->target, GL_TEXTURE_MIN_FILTER,
parameters->filters.min);
glTexParameteri(parameters->target, GL_TEXTURE_MAG_FILTER,
parameters->filters.mag);
}
if (parameters->flags & TEXTURE_WRAP_MODES_DIRTY) {
glTexParameteri(parameters->target, GL_TEXTURE_WRAP_S,
parameters->wrap_modes.s);
glTexParameteri(parameters->target, GL_TEXTURE_WRAP_T,
parameters->wrap_modes.t);
if (parameters->target == GL_TEXTURE_3D)
glTexParameteri(parameters->target, GL_TEXTURE_WRAP_R,
parameters->wrap_modes.r);
}
parameters->flags = 0;
}
/* Check whether gl_fbo_init() supports FBO creation for a given
* colour-renderable sized internal 'format' or not.
*/