diff --git a/libweston/renderer-gl/fragment.glsl b/libweston/renderer-gl/fragment.glsl index 5d975fea..63a20cd6 100644 --- a/libweston/renderer-gl/fragment.glsl +++ b/libweston/renderer-gl/fragment.glsl @@ -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; diff --git a/libweston/renderer-gl/gl-renderer-internal.h b/libweston/renderer-gl/gl-renderer-internal.h index 0beb1dc6..326e5bb9 100644 --- a/libweston/renderer-gl/gl-renderer-internal.h +++ b/libweston/renderer-gl/gl-renderer-internal.h @@ -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 { diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 5f9b8c2d..fb486294 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -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 */ diff --git a/libweston/renderer-gl/gl-shaders.c b/libweston/renderer-gl/gl-shaders.c index 0b1c8cfb..97f288c0 100644 --- a/libweston/renderer-gl/gl-shaders.c +++ b/libweston/renderer-gl/gl-shaders.c @@ -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