From 25a4bb5327a8058e20c0466c5d6673523945b9fa Mon Sep 17 00:00:00 2001
From: Pekka Paalanen <pekka.paalanen@collabora.com>
Date: Fri, 12 Mar 2021 14:06:34 +0200
Subject: [PATCH] gl-renderer: add shader bit input_is_premult

Add a new shader requirements bit input_is_premult which says whether
the texture sampling results in premultiplied alpha or not. Currently
this can be deduced fully from the shader texture variant, but in the
future there might a protocol extension to explicitly control it. Hence
the need for a new bit.

yuva2rgba() is changed to produce straight alpha always. This makes
sample_input_texture() sometimes produce straight or premultiplied
alpha. The input_is_premult bit needs to match sample_input_texture()
behavior. Doing this should save three multiplications in the shader for
straight alpha formats.

pipeline_premult() function is added, because in the future another
pipeline working on straight alpha will be needed.

Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
---
 libweston/renderer-gl/fragment.glsl          | 23 ++++++++++++++----
 libweston/renderer-gl/gl-renderer-internal.h |  6 ++++-
 libweston/renderer-gl/gl-renderer.c          |  6 +++++
 libweston/renderer-gl/gl-shaders.c           | 25 +++++++++++++++++++-
 4 files changed, 54 insertions(+), 6 deletions(-)

diff --git a/libweston/renderer-gl/fragment.glsl b/libweston/renderer-gl/fragment.glsl
index f56d1bc1f9..b5143edce4 100644
--- a/libweston/renderer-gl/fragment.glsl
+++ b/libweston/renderer-gl/fragment.glsl
@@ -53,6 +53,7 @@ precision mediump float;
  * snippet.
  */
 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;
 
 vec4
@@ -79,7 +80,6 @@ yuva2rgba(vec4 yuva)
 	color_out.g = Y - 0.39176229 * su - 0.81296764 * sv;
 	color_out.b = Y + 2.01723214 * su;
 
-	color_out.rgb *= yuva.w;
 	color_out.a = yuva.w;
 
 	return color_out;
@@ -141,16 +141,31 @@ sample_input_texture()
 	return yuva2rgba(yuva);
 }
 
+vec4
+pipeline_premult(vec4 color, compile_const bool input_is_premult)
+{
+	/* Ensure premultiplied alpha, apply view alpha (opacity) */
+	if (input_is_premult) {
+		color *= alpha;
+	} else {
+		color.a *= alpha;
+		color.rgb *= color.a;
+	}
+
+	return color;
+}
+
 void
 main()
 {
 	vec4 color;
 
-	/* Electrical (non-linear) RGBA values, pre-multiplied */
+	/* Electrical (non-linear) RGBA values, may be premult or not */
 	color = sample_input_texture();
 
-	/* View alpha (opacity) */
-	color *= alpha;
+	color = pipeline_premult(color, c_input_is_premult);
+
+	/* color is guaranteed premult here */
 
 	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 cf2476dba6..0beb1dc64c 100644
--- a/libweston/renderer-gl/gl-renderer-internal.h
+++ b/libweston/renderer-gl/gl-renderer-internal.h
@@ -62,13 +62,14 @@ enum gl_shader_texture_variant {
 struct gl_shader_requirements
 {
 	unsigned variant:4; /* enum gl_shader_texture_variant */
+	bool input_is_premult:1;
 	bool green_tint:1;
 
 	/*
 	 * 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_:27;
+	unsigned pad_bits_:26;
 };
 static_assert(sizeof(struct gl_shader_requirements) ==
 	      4 /* total bitfield size in bytes */,
@@ -202,6 +203,9 @@ gl_renderer_setup_egl_extensions(struct weston_compositor *ec);
 GLenum
 gl_shader_texture_variant_get_target(enum gl_shader_texture_variant v);
 
+bool
+gl_shader_texture_variant_can_be_premult(enum gl_shader_texture_variant v);
+
 void
 gl_shader_destroy(struct gl_renderer *gr, struct gl_shader *shader);
 
diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c
index f4ee68916d..5f9b8c2d28 100644
--- a/libweston/renderer-gl/gl-renderer.c
+++ b/libweston/renderer-gl/gl-renderer.c
@@ -738,6 +738,7 @@ triangle_fan_debug(struct gl_renderer *gr,
 	alt = (struct gl_shader_config) {
 		.req = {
 			.variant = SHADER_VARIANT_SOLID,
+			.input_is_premult = true,
 		},
 		.projection = sconf->projection,
 		.view_alpha = 1.0f,
@@ -925,6 +926,7 @@ maybe_censor_override(struct gl_shader_config *sconf,
 	const struct gl_shader_config alt = {
 		.req = {
 			.variant = SHADER_VARIANT_SOLID,
+			.input_is_premult = true,
 		},
 		.projection = sconf->projection,
 		.view_alpha = sconf->view_alpha,
@@ -960,6 +962,8 @@ gl_shader_config_set_input_textures(struct gl_shader_config *sconf,
 	int i;
 
 	sconf->req.variant = gs->shader_variant;
+	sconf->req.input_is_premult =
+		gl_shader_texture_variant_can_be_premult(gs->shader_variant);
 
 	for (i = 0; i < 4; i++)
 		sconf->unicolor[i] = gs->color[i];
@@ -1264,6 +1268,7 @@ draw_output_borders(struct weston_output *output,
 	struct gl_shader_config sconf = {
 		.req = {
 			.variant = SHADER_VARIANT_RGBA,
+			.input_is_premult = true,
 		},
 		.view_alpha = 1.0f,
 	};
@@ -1488,6 +1493,7 @@ blit_shadow_to_output(struct weston_output *output,
 	const struct gl_shader_config sconf = {
 		.req = {
 			.variant = SHADER_VARIANT_RGBA,
+			.input_is_premult = true,
 		},
 		.projection = {
 			.d = { /* transpose */
diff --git a/libweston/renderer-gl/gl-shaders.c b/libweston/renderer-gl/gl-shaders.c
index 4a47ca986c..0b1c8cfb1f 100644
--- a/libweston/renderer-gl/gl-shaders.c
+++ b/libweston/renderer-gl/gl-shaders.c
@@ -147,8 +147,9 @@ create_shader_description_string(const struct gl_shader_requirements *req)
 	int size;
 	char *str;
 
-	size = asprintf(&str, "%s %cgreen",
+	size = asprintf(&str, "%s %cinput_is_premult %cgreen",
 			gl_shader_texture_variant_to_string(req->variant),
+			req->input_is_premult ? '+' : '-',
 			req->green_tint ? '+' : '-');
 	if (size < 0)
 		return NULL;
@@ -163,8 +164,10 @@ 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_VARIANT %s\n",
 			req->green_tint ? "true" : "false",
+			req->input_is_premult ? "true" : "false",
 			gl_shader_texture_variant_to_string(req->variant));
 	if (size < 0)
 		return NULL;
@@ -349,6 +352,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,
 	};
 	struct gl_shader *shader;
 
@@ -415,6 +419,25 @@ gl_renderer_garbage_collect_programs(struct gl_renderer *gr)
 	}
 }
 
+bool
+gl_shader_texture_variant_can_be_premult(enum gl_shader_texture_variant v)
+{
+	switch (v) {
+	case SHADER_VARIANT_SOLID:
+	case SHADER_VARIANT_RGBA:
+	case SHADER_VARIANT_EXTERNAL:
+		return true;
+	case SHADER_VARIANT_NONE:
+	case SHADER_VARIANT_RGBX:
+	case SHADER_VARIANT_Y_U_V:
+	case SHADER_VARIANT_Y_UV:
+	case SHADER_VARIANT_Y_XUXV:
+	case SHADER_VARIANT_XYUV:
+		return false;
+	}
+	return true;
+}
+
 GLenum
 gl_shader_texture_variant_get_target(enum gl_shader_texture_variant v)
 {
-- 
GitLab