From 92f2367e58f317a96ccefbd9eeba3edf1325aba0 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 9 Mar 2021 16:40:25 +0200 Subject: [PATCH] gl-renderer: implement 3 x 1D LUT color pre-curve This makes weston_color_transform object be able to express three-channel one-dimensional look-up table transformations. They are useful for applying EOTF and EOTF^-1 mapping, or, gamma curves. They will also be useful in optimizing a following 3D LUT tap distribution once support for 3D LUT is added. The code added here translates from the lut_3x1d fill_in() interface to a GL texture to be used with SHADER_COLOR_CURVE_LUT_3x1D for weston_surfaces. It demonstrates how renderer data is attached to weston_color_transform and cached. GL_OES_texture_float_linear is required to be able to use bilinear texture filtering with 32-bit floating-point textures, used for the LUT. As the size of the LUT depends on what implements it, lut_3x1d fill_in() interface is a callback to the color management component to ask for an arbitrary size. For GL-renderer this is not important as it can easily realize any LUT size, but when DRM-backend wants to offload the EOTF^-1 mapping to KMS (GAMMA_LUT), the LUT size comes from KMS. Nothing actually implements lut_3x1d fill_in() yet, that will come in a later patch. Signed-off-by: Pekka Paalanen --- libweston/color.h | 23 +- libweston/renderer-gl/gl-renderer-internal.h | 5 + libweston/renderer-gl/gl-renderer.c | 7 +- .../gl-shader-config-color-transformation.c | 217 ++++++++++++++++++ libweston/renderer-gl/meson.build | 1 + 5 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 libweston/renderer-gl/gl-shader-config-color-transformation.c diff --git a/libweston/color.h b/libweston/color.h index 970483e8..30a83924 100644 --- a/libweston/color.h +++ b/libweston/color.h @@ -40,7 +40,28 @@ enum weston_color_curve_type { /** LUT_3x1D parameters */ struct weston_color_curve_lut_3x1d { - /* To be defined */ + /** + * Approximate a color curve with three 1D LUTs + * + * A 1D LUT is a mapping from [0.0, 1.0] to arbitrary values. The first + * element in the LUT corresponds to input value 0.0, and the last + * element corresponds to input value 1.0. The step from one element + * to the next in input space is 1.0 / (len - 1). When input value is + * between two elements, linear interpolation should be used. + * + * This function fills in the given array with the LUT values. + * + * \param xform This color transformation object. + * \param len The number of elements in each 1D LUT. + * \param values Array of 3 x len elements. First R channel + * LUT, immediately followed by G channel LUT, and then B channel LUT. + */ + void + (*fill_in)(struct weston_color_transform *xform, + float *values, unsigned len); + + /** Optimal 1D LUT length for storage vs. precision */ + unsigned optimal_len; }; /** diff --git a/libweston/renderer-gl/gl-renderer-internal.h b/libweston/renderer-gl/gl-renderer-internal.h index 326e5bb9..03a2179b 100644 --- a/libweston/renderer-gl/gl-renderer-internal.h +++ b/libweston/renderer-gl/gl-renderer-internal.h @@ -83,6 +83,7 @@ static_assert(sizeof(struct gl_shader_requirements) == "struct gl_shader_requirements must not contain implicit padding"); struct gl_shader; +struct weston_color_transform; #define GL_SHADER_INPUT_TEX_MAX 3 struct gl_shader_config { @@ -234,4 +235,8 @@ gl_renderer_use_program(struct gl_renderer *gr, struct weston_log_scope * gl_shader_scope_create(struct gl_renderer *gr); +bool +gl_shader_config_set_color_transform(struct gl_shader_config *sconf, + struct weston_color_transform *xform); + #endif /* GL_RENDERER_INTERNAL_H */ diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index fb486294..3de0b946 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -987,7 +987,6 @@ gl_shader_config_init_for_paint_node(struct gl_shader_config *sconf, if (!pnode->surf_xform_valid) return false; - assert(pnode->surf_xform.transform == NULL); *sconf = (struct gl_shader_config) { .projection = go->output_matrix, @@ -997,6 +996,11 @@ gl_shader_config_init_for_paint_node(struct gl_shader_config *sconf, gl_shader_config_set_input_textures(sconf, gs); + if (!gl_shader_config_set_color_transform(sconf, pnode->surf_xform.transform)) { + weston_log("GL-renderer: failed to generate a color transformation.\n"); + return false; + } + return true; } @@ -3831,6 +3835,7 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) gr->has_egl_image_external = true; if (gr->gl_version >= gr_gl_version(3, 0) && + weston_check_egl_extension(extensions, "GL_OES_texture_float_linear") && weston_check_egl_extension(extensions, "GL_EXT_color_buffer_half_float")) { gr->gl_supports_color_transforms = true; } diff --git a/libweston/renderer-gl/gl-shader-config-color-transformation.c b/libweston/renderer-gl/gl-shader-config-color-transformation.c new file mode 100644 index 00000000..21a45653 --- /dev/null +++ b/libweston/renderer-gl/gl-shader-config-color-transformation.c @@ -0,0 +1,217 @@ +/* + * Copyright 2021 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include + +#include + +#include +#include "color.h" +#include "gl-renderer.h" +#include "gl-renderer-internal.h" + +#include "shared/weston-egl-ext.h" + +struct gl_renderer_color_curve { + enum gl_shader_color_curve type; + GLuint tex; + float scale; + float offset; +}; + +struct gl_renderer_color_transform { + struct weston_color_transform *owner; + struct wl_listener destroy_listener; + + struct gl_renderer_color_curve pre_curve; +}; + +static void +gl_renderer_color_curve_fini(struct gl_renderer_color_curve *gl_curve) +{ + if (gl_curve->tex) + glDeleteTextures(1, &gl_curve->tex); +} + +static void +gl_renderer_color_transform_destroy(struct gl_renderer_color_transform *gl_xform) +{ + gl_renderer_color_curve_fini(&gl_xform->pre_curve); + wl_list_remove(&gl_xform->destroy_listener.link); + free(gl_xform); +} + +static void +color_transform_destroy_handler(struct wl_listener *l, void *data) +{ + struct gl_renderer_color_transform *gl_xform; + + gl_xform = wl_container_of(l, gl_xform, destroy_listener); + assert(gl_xform->owner == data); + + gl_renderer_color_transform_destroy(gl_xform); +} + +static struct gl_renderer_color_transform * +gl_renderer_color_transform_create(struct weston_color_transform *xform) +{ + struct gl_renderer_color_transform *gl_xform; + + gl_xform = zalloc(sizeof *gl_xform); + if (!gl_xform) + return NULL; + + gl_xform->owner = xform; + gl_xform->destroy_listener.notify = color_transform_destroy_handler; + wl_signal_add(&xform->destroy_signal, &gl_xform->destroy_listener); + + return gl_xform; +} + +static struct gl_renderer_color_transform * +gl_renderer_color_transform_get(struct weston_color_transform *xform) +{ + struct wl_listener *l; + + l = wl_signal_get(&xform->destroy_signal, + color_transform_destroy_handler); + if (!l) + return NULL; + + return container_of(l, struct gl_renderer_color_transform, + destroy_listener); +} + +static bool +gl_color_curve_lut_3x1d(struct gl_renderer_color_curve *gl_curve, + const struct weston_color_curve *curve, + struct weston_color_transform *xform) +{ + const unsigned lut_len = curve->u.lut_3x1d.optimal_len; + const unsigned nr_rows = 4; + GLuint tex; + float *lut; + + /* + * Four rows, see fragment.glsl sample_color_pre_curve_lut_2d(). + * The fourth row is unused in fragment.glsl color_pre_curve(). + */ + lut = calloc(lut_len * nr_rows, sizeof *lut); + if (!lut) + return false; + + curve->u.lut_3x1d.fill_in(xform, lut, lut_len); + + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, 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); + glPixelStorei(GL_UNPACK_ALIGNMENT, sizeof (float)); + glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, lut_len, nr_rows, 0, + GL_RED_EXT, GL_FLOAT, lut); + + glBindTexture(GL_TEXTURE_2D, 0); + free(lut); + + gl_curve->type = SHADER_COLOR_CURVE_LUT_3x1D; + gl_curve->tex = tex; + gl_curve->scale = (float)(lut_len - 1) / lut_len; + gl_curve->offset = 0.5f / lut_len; + + return true; +} + +static const struct gl_renderer_color_transform * +gl_renderer_color_transform_from(struct weston_color_transform *xform) +{ + static const struct gl_renderer_color_transform no_op_gl_xform = { + .pre_curve.type = SHADER_COLOR_CURVE_IDENTITY, + .pre_curve.tex = 0, + .pre_curve.scale = 0.0f, + .pre_curve.offset = 0.0f, + }; + struct gl_renderer_color_transform *gl_xform; + bool ok = false; + + /* Identity transformation */ + if (!xform) + return &no_op_gl_xform; + + /* Cached transformation */ + gl_xform = gl_renderer_color_transform_get(xform); + if (gl_xform) + return gl_xform; + + /* New transformation */ + + gl_xform = gl_renderer_color_transform_create(xform); + if (!gl_xform) + return NULL; + + switch (xform->pre_curve.type) { + case WESTON_COLOR_CURVE_TYPE_IDENTITY: + gl_xform->pre_curve = no_op_gl_xform.pre_curve; + ok = true; + break; + case WESTON_COLOR_CURVE_TYPE_LUT_3x1D: + ok = gl_color_curve_lut_3x1d(&gl_xform->pre_curve, + &xform->pre_curve, xform); + break; + } + + if (!ok) { + gl_renderer_color_transform_destroy(gl_xform); + return NULL; + } + + return gl_xform; +} + +bool +gl_shader_config_set_color_transform(struct gl_shader_config *sconf, + struct weston_color_transform *xform) +{ + const struct gl_renderer_color_transform *gl_xform; + + gl_xform = gl_renderer_color_transform_from(xform); + if (!gl_xform) + return false; + + sconf->req.color_pre_curve = gl_xform->pre_curve.type; + sconf->color_pre_curve_lut_tex = gl_xform->pre_curve.tex; + sconf->color_pre_curve_lut_scale_offset[0] = gl_xform->pre_curve.scale; + sconf->color_pre_curve_lut_scale_offset[1] = gl_xform->pre_curve.offset; + + return true; +} diff --git a/libweston/renderer-gl/meson.build b/libweston/renderer-gl/meson.build index 476e26ce..4d9f64df 100644 --- a/libweston/renderer-gl/meson.build +++ b/libweston/renderer-gl/meson.build @@ -23,6 +23,7 @@ srcs_renderer_gl = [ fragment_glsl, 'gl-renderer.c', 'gl-shaders.c', + 'gl-shader-config-color-transformation.c', linux_dmabuf_unstable_v1_protocol_c, linux_dmabuf_unstable_v1_server_protocol_h, vertex_glsl,