gl-renderer: implement SHADER_COLOR_CURVE_LUT_3x1D

This adds shader support for using a three-channel one-dimensional
look-up table for de/encoding input colors. This operation will be useful
for applying EOTF or its inverse, in other words, gamma curves. It will
also be useful in optimizing a following 3D LUT tap distribution once
support for 3D LUT is added.

Even though called three-channel and one-dimensional, it is actually
implemented as a one-channel two-dimensional texture with four rows.
Each row corresponds to a source color channel except the fourth one is
unused. The reason for having the fourth row is to get texture
coordinates in 1/8 steps instead of 1/6 steps. 1/6 may would not be
exact in floating- or fixed-point arithmetic and might perhaps risk
unintended results from bilinear texture filtering when we want linear
filtering only in x but not in y texture coordinates. I may be paranoid.

The LUT is applied on source colors after they have been converted to
straight RGB. It cannot be applied with pre-multiplied alpha. A LUT can
be used for both applying EOTF to go from source color space to blending
color space, and EOTF^-1 to go from blending space to output
(electrical) space. However, this type of LUT cannot do color space
conversions.

For now, this feature is hardcoded to off everywhere, to be enabled in
following patches.

Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
dev
Pekka Paalanen 4 years ago committed by Pekka Paalanen
parent 391f513c36
commit 9a6a4e7032
  1. 78
      libweston/renderer-gl/fragment.glsl
  2. 11
      libweston/renderer-gl/gl-renderer-internal.h
  3. 4
      libweston/renderer-gl/gl-renderer.c
  4. 44
      libweston/renderer-gl/gl-shaders.c

@ -42,6 +42,10 @@
#define SHADER_VARIANT_SOLID 7
#define SHADER_VARIANT_EXTERNAL 8
/* enum gl_shader_color_curve */
#define SHADER_COLOR_CURVE_IDENTITY 0
#define SHADER_COLOR_CURVE_LUT_3x1D 1
#if DEF_VARIANT == SHADER_VARIANT_EXTERNAL
#extension GL_OES_EGL_image_external : require
#endif
@ -61,6 +65,7 @@ precision HIGHPRECISION float;
compile_const int c_variant = DEF_VARIANT;
compile_const bool c_input_is_premult = DEF_INPUT_IS_PREMULT;
compile_const bool c_green_tint = DEF_GREEN_TINT;
compile_const int c_color_pre_curve = DEF_COLOR_PRE_CURVE;
vec4
yuva2rgba(vec4 yuva)
@ -102,6 +107,8 @@ uniform sampler2D tex1;
uniform sampler2D tex2;
uniform float alpha;
uniform vec4 unicolor;
uniform HIGHPRECISION sampler2D color_pre_curve_lut_2d;
uniform HIGHPRECISION vec2 color_pre_curve_lut_scale_offset;
vec4
sample_input_texture()
@ -147,6 +154,62 @@ sample_input_texture()
return yuva2rgba(yuva);
}
/*
* Texture coordinates go from 0.0 to 1.0 corresponding to texture edges.
* When we do LUT look-ups with linear filtering, the correct range to sample
* from is not from edge to edge, but center of first texel to center of last
* texel. This follows because with LUTs, you have the exact end points given,
* you never extrapolate but only interpolate.
* The scale and offset are precomputed to achieve this mapping.
*/
float
lut_texcoord(float x, vec2 scale_offset)
{
return x * scale_offset.s + scale_offset.t;
}
/*
* Sample a 1D LUT which is a single row of a 2D texture. The 2D texture has
* four rows so that the centers of texels have precise y-coordinates.
*/
float
sample_color_pre_curve_lut_2d(float x, compile_const int row)
{
float tx = lut_texcoord(x, color_pre_curve_lut_scale_offset);
return texture2D(color_pre_curve_lut_2d,
vec2(tx, (float(row) + 0.5) / 4.0)).x;
}
vec3
color_pre_curve(vec3 color)
{
vec3 ret;
if (c_color_pre_curve == SHADER_COLOR_CURVE_IDENTITY) {
return color;
} else if (c_color_pre_curve == SHADER_COLOR_CURVE_LUT_3x1D) {
ret.r = sample_color_pre_curve_lut_2d(color.r, 0);
ret.g = sample_color_pre_curve_lut_2d(color.g, 1);
ret.b = sample_color_pre_curve_lut_2d(color.b, 2);
return ret;
} else {
/* Never reached, bad c_color_pre_curve. */
return vec3(1.0, 0.3, 1.0);
}
}
vec4
color_pipeline(vec4 color)
{
/* View alpha (opacity) */
color.a *= alpha;
color.rgb = color_pre_curve(color.rgb);
return color;
}
void
main()
{
@ -155,15 +218,18 @@ main()
/* Electrical (non-linear) RGBA values, may be premult or not */
color = sample_input_texture();
/* Ensure premultiplied alpha, apply view alpha (opacity) */
/* Ensure straight alpha */
if (c_input_is_premult) {
color *= alpha;
} else {
color.a *= alpha;
color.rgb *= color.a;
if (color.a == 0.0)
color.rgb = vec3(0, 0, 0);
else
color.rgb *= 1.0 / color.a;
}
/* color is guaranteed premult here */
color = color_pipeline(color);
/* pre-multiply for blending */
color.rgb *= color.a;
if (c_green_tint)
color = vec4(0.0, 0.3, 0.0, 0.2) + color * 0.8;

@ -50,6 +50,12 @@ enum gl_shader_texture_variant {
SHADER_VARIANT_EXTERNAL,
};
/* Keep the following in sync with fragment.glsl. */
enum gl_shader_color_curve {
SHADER_COLOR_CURVE_IDENTITY = 0,
SHADER_COLOR_CURVE_LUT_3x1D,
};
/** GL shader requirements key
*
* This structure is used as a binary blob key for building and searching
@ -64,12 +70,13 @@ struct gl_shader_requirements
unsigned variant:4; /* enum gl_shader_texture_variant */
bool input_is_premult:1;
bool green_tint:1;
unsigned color_pre_curve:1; /* enum gl_shader_color_curve */
/*
* The total size of all bitfields plus pad_bits_ must fill up exactly
* how many bytes the compiler allocates for them together.
*/
unsigned pad_bits_:26;
unsigned pad_bits_:25;
};
static_assert(sizeof(struct gl_shader_requirements) ==
4 /* total bitfield size in bytes */,
@ -86,6 +93,8 @@ struct gl_shader_config {
GLfloat unicolor[4];
GLint input_tex_filter; /* GL_NEAREST or GL_LINEAR */
GLuint input_tex[GL_SHADER_INPUT_TEX_MAX];
GLuint color_pre_curve_lut_tex;
GLfloat color_pre_curve_lut_scale_offset[2];
};
struct gl_renderer {

@ -739,6 +739,7 @@ triangle_fan_debug(struct gl_renderer *gr,
.req = {
.variant = SHADER_VARIANT_SOLID,
.input_is_premult = true,
.color_pre_curve = SHADER_COLOR_CURVE_IDENTITY,
},
.projection = sconf->projection,
.view_alpha = 1.0f,
@ -927,6 +928,7 @@ maybe_censor_override(struct gl_shader_config *sconf,
.req = {
.variant = SHADER_VARIANT_SOLID,
.input_is_premult = true,
.color_pre_curve = SHADER_COLOR_CURVE_IDENTITY,
},
.projection = sconf->projection,
.view_alpha = sconf->view_alpha,
@ -1269,6 +1271,7 @@ draw_output_borders(struct weston_output *output,
.req = {
.variant = SHADER_VARIANT_RGBA,
.input_is_premult = true,
.color_pre_curve = SHADER_COLOR_CURVE_IDENTITY,
},
.view_alpha = 1.0f,
};
@ -1494,6 +1497,7 @@ blit_shadow_to_output(struct weston_output *output,
.req = {
.variant = SHADER_VARIANT_RGBA,
.input_is_premult = true,
.color_pre_curve = SHADER_COLOR_CURVE_IDENTITY,
},
.projection = {
.d = { /* transpose */

@ -58,6 +58,8 @@ struct gl_shader {
GLint tex_uniforms[3];
GLint alpha_uniform;
GLint color_uniform;
GLint color_pre_curve_lut_2d_uniform;
GLint color_pre_curve_lut_scale_offset_uniform;
struct wl_list link; /* gl_renderer::shader_list */
struct timespec last_used;
};
@ -82,6 +84,19 @@ gl_shader_texture_variant_to_string(enum gl_shader_texture_variant v)
return "!?!?"; /* never reached */
}
static const char *
gl_shader_color_curve_to_string(enum gl_shader_color_curve kind)
{
switch (kind) {
#define CASERET(x) case x: return #x;
CASERET(SHADER_COLOR_CURVE_IDENTITY)
CASERET(SHADER_COLOR_CURVE_LUT_3x1D)
#undef CASERET
}
return "!?!?"; /* never reached */
}
static void
dump_program_with_line_numbers(int count, const char **sources)
{
@ -147,8 +162,9 @@ create_shader_description_string(const struct gl_shader_requirements *req)
int size;
char *str;
size = asprintf(&str, "%s %cinput_is_premult %cgreen",
size = asprintf(&str, "%s %s %cinput_is_premult %cgreen",
gl_shader_texture_variant_to_string(req->variant),
gl_shader_color_curve_to_string(req->color_pre_curve),
req->input_is_premult ? '+' : '-',
req->green_tint ? '+' : '-');
if (size < 0)
@ -165,9 +181,11 @@ create_shader_config_string(const struct gl_shader_requirements *req)
size = asprintf(&str,
"#define DEF_GREEN_TINT %s\n"
"#define DEF_INPUT_IS_PREMULT %s\n"
"#define DEF_COLOR_PRE_CURVE %s\n"
"#define DEF_VARIANT %s\n",
req->green_tint ? "true" : "false",
req->input_is_premult ? "true" : "false",
gl_shader_color_curve_to_string(req->color_pre_curve),
gl_shader_texture_variant_to_string(req->variant));
if (size < 0)
return NULL;
@ -245,6 +263,10 @@ gl_shader_create(struct gl_renderer *gr,
shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha");
shader->color_uniform = glGetUniformLocation(shader->program,
"unicolor");
shader->color_pre_curve_lut_2d_uniform =
glGetUniformLocation(shader->program, "color_pre_curve_lut_2d");
shader->color_pre_curve_lut_scale_offset_uniform =
glGetUniformLocation(shader->program, "color_pre_curve_lut_scale_offset");
free(conf);
@ -353,6 +375,7 @@ gl_renderer_create_fallback_shader(struct gl_renderer *gr)
static const struct gl_shader_requirements fallback_requirements = {
.variant = SHADER_VARIANT_SOLID,
.input_is_premult = true,
.color_pre_curve = SHADER_COLOR_CURVE_IDENTITY,
};
struct gl_shader *shader;
@ -473,6 +496,25 @@ gl_shader_load_config(struct gl_shader *shader,
glTexParameteri(in_tgt, GL_TEXTURE_MIN_FILTER, in_filter);
glTexParameteri(in_tgt, GL_TEXTURE_MAG_FILTER, in_filter);
}
/* Fixed texture unit for color_pre_curve LUT */
i = GL_SHADER_INPUT_TEX_MAX;
glActiveTexture(GL_TEXTURE0 + i);
switch (sconf->req.color_pre_curve) {
case SHADER_COLOR_CURVE_IDENTITY:
assert(sconf->color_pre_curve_lut_tex == 0);
break;
case SHADER_COLOR_CURVE_LUT_3x1D:
assert(sconf->color_pre_curve_lut_tex != 0);
assert(shader->color_pre_curve_lut_2d_uniform != -1);
assert(shader->color_pre_curve_lut_scale_offset_uniform != -1);
glBindTexture(GL_TEXTURE_2D, sconf->color_pre_curve_lut_tex);
glUniform1i(shader->color_pre_curve_lut_2d_uniform, i);
glUniform2fv(shader->color_pre_curve_lut_scale_offset_uniform,
1, sconf->color_pre_curve_lut_scale_offset);
break;
}
}
bool

Loading…
Cancel
Save