[PATCH] drm/vkms: add alpha-premultiplied color blending

1 view
Skip to first unread message

Melissa Wen

unread,
Aug 19, 2020, 4:53:48 PM8/19/20
to Rodrigo Siqueira, Haneen Mohammed, Daniel Vetter, David Airlie, Rodrigo Siqueira, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, kerne...@googlegroups.com, twoe...@gmail.com
The current VKMS blend function ignores alpha channel and just overwrites
vaddr_src with vaddr_dst. This XRGB approach triggers a warning when
running the kms_cursor_crc/cursor-alpha-transparent test case. In IGT
tests, cairo_format_argb32 uses premultiplied alpha (according to
documentation), so this patch considers premultiplied alpha colors to
compose vaddr_src with vaddr_dst.

This change removes the following cursor-alpha-transparent warning:
Suspicious CRC: All values are 0.

Cc: Daniel Vetter <dan...@ffwll.ch>
Cc: Rodrigo Siqueira <rodrigosi...@gmail.com>
Cc: Haneen Mohammed <hamoha...@gmail.com>

Signed-off-by: Melissa Wen <melis...@gmail.com>
---
drivers/gpu/drm/vkms/vkms_composer.c | 43 +++++++++++++++++++++-------
1 file changed, 33 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c
index 4f3b07a32b60..6aac962d3e2e 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -32,8 +32,6 @@ static uint32_t compute_crc(void *vaddr_out, struct vkms_composer *composer)
src_offset = composer->offset
+ (i * composer->pitch)
+ (j * composer->cpp);
- /* XRGB format ignores Alpha channel */
- bitmap_clear(vaddr_out + src_offset, 24, 8);
crc = crc32_le(crc, vaddr_out + src_offset,
sizeof(u32));
}
@@ -42,6 +40,32 @@ static uint32_t compute_crc(void *vaddr_out, struct vkms_composer *composer)
return crc;
}

+u8 blend_channel(u8 c_src, u8 c_dst, u8 a_src)
+{
+ u32 pre_blend;
+ u8 new_color;
+
+ /* Premultiplied alpha blending - IGT + cairo context */
+ pre_blend = (c_src * 255 + c_dst * (255 - a_src));
+
+ /* Faster div by 255 */
+ new_color = ((pre_blend + ((pre_blend + 257) >> 8)) >> 8);
+
+ return new_color;
+}
+
+void alpha_blending(u8 *argb_src, u8 *argb_dst)
+{
+ u8 a_src;
+
+ a_src = argb_src[3];
+ argb_dst[0] = blend_channel(argb_src[0], argb_dst[0], a_src);
+ argb_dst[1] = blend_channel(argb_src[1], argb_dst[1], a_src);
+ argb_dst[2] = blend_channel(argb_src[2], argb_dst[2], a_src);
+ /* Opaque primary */
+ argb_dst[3] = 0xFF;
+}
+
/**
* blend - blend value at vaddr_src with value at vaddr_dst
* @vaddr_dst: destination address
@@ -50,12 +74,9 @@ static uint32_t compute_crc(void *vaddr_out, struct vkms_composer *composer)
* @src_composer: source framebuffer's metadata
*
* Blend value at vaddr_src with value at vaddr_dst.
- * Currently, this function write value of vaddr_src on value
- * at vaddr_dst using buffer's metadata to locate the new values
- * from vaddr_src and their destination at vaddr_dst.
- *
- * TODO: Use the alpha value to blend vaddr_src with vaddr_dst
- * instead of overwriting it.
+ * Currently, this function considers premultiplied alpha for blending, as used
+ * by Cairo. It uses buffer's metadata to locate the new composite values at
+ * vaddr_dst.
*/
static void blend(void *vaddr_dst, void *vaddr_src,
struct vkms_composer *dest_composer,
@@ -63,6 +84,7 @@ static void blend(void *vaddr_dst, void *vaddr_src,
{
int i, j, j_dst, i_dst;
int offset_src, offset_dst;
+ u8 *p_dst, *p_src;

int x_src = src_composer->src.x1 >> 16;
int y_src = src_composer->src.y1 >> 16;
@@ -84,8 +106,9 @@ static void blend(void *vaddr_dst, void *vaddr_src,
+ (i * src_composer->pitch)
+ (j * src_composer->cpp);

- memcpy(vaddr_dst + offset_dst,
- vaddr_src + offset_src, sizeof(u32));
+ p_src = (u8 *)(vaddr_src + offset_src);
+ p_dst = (u8 *)(vaddr_dst + offset_dst);
+ alpha_blending(p_src, p_dst);
}
i_dst++;
}
--
2.28.0

Pekka Paalanen

unread,
Aug 20, 2020, 3:28:55 AM8/20/20
to Melissa Wen, Rodrigo Siqueira, Haneen Mohammed, Daniel Vetter, David Airlie, Rodrigo Siqueira, linux-...@vger.kernel.org, dri-...@lists.freedesktop.org, kerne...@googlegroups.com
Hi,

DRM pixel formats are often defined as "bits in a 32-bit word", but
here you are accessing it as an array of bytes. To me that looks
suspicious wrt. big-endian architectures.

Unfortunately I have again forgot how DRM pixel formats should be
interpreted on a big-endian machine, if I ever even understood it, so I
can't say if this is right or not.


Thanks,
pq

Rodrigo Siqueira

unread,
Aug 24, 2020, 11:04:12 PM8/24/20
to Melissa Wen, Haneen Mohammed, Daniel Vetter, David Airlie, Rodrigo Siqueira, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, kerne...@googlegroups.com, twoe...@gmail.com
Hi Melissa,

First of all, thanks a lot for your patch!

Follows my inline comments.
Use static here.

Also, replace c_src to src, c_dst to dst, and a_src to alpha.

> +{
> + u32 pre_blend;
> + u8 new_color;
> +
> + /* Premultiplied alpha blending - IGT + cairo context */

You can drop the part that says "IGT + cairo context", this explanation
better suit the commit message.

> + pre_blend = (c_src * 255 + c_dst * (255 - a_src));
> +
> + /* Faster div by 255 */
> + new_color = ((pre_blend + ((pre_blend + 257) >> 8)) >> 8);
> +
> + return new_color;
> +}
> +
> +void alpha_blending(u8 *argb_src, u8 *argb_dst)

Use static.

Looks like that argb_src is a read-only variable, in this sense add
const.

> +{
> + u8 a_src;
> +
> + a_src = argb_src[3];

change a_src to alpha.
I suppose that p stands for "pixel", right? In this case how about use
pixel?

Best Regards
Rodrigo Siqueira

>
> int x_src = src_composer->src.x1 >> 16;
> int y_src = src_composer->src.y1 >> 16;
> @@ -84,8 +106,9 @@ static void blend(void *vaddr_dst, void *vaddr_src,
> + (i * src_composer->pitch)
> + (j * src_composer->cpp);
>
> - memcpy(vaddr_dst + offset_dst,
> - vaddr_src + offset_src, sizeof(u32));
> + p_src = (u8 *)(vaddr_src + offset_src);
> + p_dst = (u8 *)(vaddr_dst + offset_dst);
> + alpha_blending(p_src, p_dst);
> }
> i_dst++;
> }
> --
> 2.28.0
>

--
Rodrigo Siqueira
https://siqueira.tech
signature.asc

Melissa Wen

unread,
Aug 25, 2020, 7:45:41 AM8/25/20
to Rodrigo Siqueira, Haneen Mohammed, Daniel Vetter, David Airlie, Rodrigo Siqueira, twoe...@gmail.com, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, kerne...@googlegroups.com
The VKMS blend function was ignoring the alpha channel and just
overwriting vaddr_src with vaddr_dst. This XRGB approach triggers a
warning when running the kms_cursor_crc/cursor-alpha-transparent test
case. In IGT, cairo_format_argb32 uses premultiplied alpha (according to
documentation). Also current DRM assumption is that alpha is
premultiplied. Therefore, this patch considers premultiplied alpha
blending eq to compose vaddr_src with vaddr_dst.

This change removes the following cursor-alpha-transparent warning:
"Suspicious CRC: All values are 0."

--

v2:
- static for local functions
- const for the read-only variable argb_src
- replaces variable names
- drops unnecessary comment

--

Cc: Daniel Vetter <dan...@ffwll.ch>
Cc: Rodrigo Siqueira <rodrigosi...@gmail.com>
Cc: Haneen Mohammed <hamoha...@gmail.com>

Signed-off-by: Melissa Wen <melis...@gmail.com>
---
drivers/gpu/drm/vkms/vkms_composer.c | 55 ++++++++++++++++++++--------
1 file changed, 39 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c
index 4f3b07a32b60..eaecc5a6c5db 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -32,8 +32,6 @@ static uint32_t compute_crc(void *vaddr_out, struct vkms_composer *composer)
src_offset = composer->offset
+ (i * composer->pitch)
+ (j * composer->cpp);
- /* XRGB format ignores Alpha channel */
- bitmap_clear(vaddr_out + src_offset, 24, 8);
crc = crc32_le(crc, vaddr_out + src_offset,
sizeof(u32));
}
@@ -42,27 +40,51 @@ static uint32_t compute_crc(void *vaddr_out, struct vkms_composer *composer)
return crc;
}

+static u8 blend_channel(u8 src, u8 dst, u8 alpha)
+{
+ u32 pre_blend;
+ u8 new_color;
+
+ pre_blend = (src * 255 + dst * (255 - alpha));
+
+ /* Faster div by 255 */
+ new_color = ((pre_blend + ((pre_blend + 257) >> 8)) >> 8);
+
+ return new_color;
+}
+
+static void alpha_blending(const u8 *argb_src, u8 *argb_dst)
+{
+ u8 alpha;
+
+ alpha = argb_src[3];
+ argb_dst[0] = blend_channel(argb_src[0], argb_dst[0], alpha);
+ argb_dst[1] = blend_channel(argb_src[1], argb_dst[1], alpha);
+ argb_dst[2] = blend_channel(argb_src[2], argb_dst[2], alpha);
+ /* Opaque primary */
+ argb_dst[3] = 0xFF;
+}
+
/**
* blend - blend value at vaddr_src with value at vaddr_dst
* @vaddr_dst: destination address
* @vaddr_src: source address
- * @dest_composer: destination framebuffer's metadata
+ * @dst_composer: destination framebuffer's metadata
* @src_composer: source framebuffer's metadata
*
- * Blend value at vaddr_src with value at vaddr_dst.
- * Currently, this function write value of vaddr_src on value
- * at vaddr_dst using buffer's metadata to locate the new values
- * from vaddr_src and their destination at vaddr_dst.
- *
- * TODO: Use the alpha value to blend vaddr_src with vaddr_dst
- * instead of overwriting it.
+ * Blend the vaddr_src value with the vaddr_dst value using the pre-multiplied
+ * alpha blending equation, since DRM currently assumes that the pixel color
+ * values have already been pre-multiplied with the alpha channel values. See
+ * more drm_plane_create_blend_mode_property(). This function uses buffer's
+ * metadata to locate the new composite values at vaddr_dst.
*/
static void blend(void *vaddr_dst, void *vaddr_src,
- struct vkms_composer *dest_composer,
+ struct vkms_composer *dst_composer,
struct vkms_composer *src_composer)
{
int i, j, j_dst, i_dst;
int offset_src, offset_dst;
+ u8 *pixel_dst, *pixel_src;

int x_src = src_composer->src.x1 >> 16;
int y_src = src_composer->src.y1 >> 16;
@@ -77,15 +99,16 @@ static void blend(void *vaddr_dst, void *vaddr_src,

for (i = y_src, i_dst = y_dst; i < y_limit; ++i) {
for (j = x_src, j_dst = x_dst; j < x_limit; ++j) {
- offset_dst = dest_composer->offset
- + (i_dst * dest_composer->pitch)
- + (j_dst++ * dest_composer->cpp);
+ offset_dst = dst_composer->offset
+ + (i_dst * dst_composer->pitch)
+ + (j_dst++ * dst_composer->cpp);
offset_src = src_composer->offset
+ (i * src_composer->pitch)
+ (j * src_composer->cpp);

- memcpy(vaddr_dst + offset_dst,
- vaddr_src + offset_src, sizeof(u32));
+ pixel_src = (u8 *)(vaddr_src + offset_src);
+ pixel_dst = (u8 *)(vaddr_dst + offset_dst);
+ alpha_blending(pixel_src, pixel_dst);
}
i_dst++;
}
--
2.28.0

Melissa Wen

unread,
Aug 25, 2020, 7:48:07 AM8/25/20
to Rodrigo Siqueira, Haneen Mohammed, Daniel Vetter, David Airlie, Rodrigo Siqueira, DRI Development, LKML, kerne...@googlegroups.com, Trevor Woerner
Hi Rodrigo,

Thanks for the review!
I just sent a v2 applying your suggestions.

Best Regards,

Melissa Wen

Melissa Wen

Rodrigo Siqueira

unread,
Aug 30, 2020, 9:16:15 AM8/30/20
to Melissa Wen, Haneen Mohammed, Daniel Vetter, David Airlie, Rodrigo Siqueira, twoe...@gmail.com, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, kerne...@googlegroups.com
Reviewed-by: Rodrigo Siqueira <rodrigosi...@gmail.com>
signature.asc
Reply all
Reply to author
Forward
0 new messages