Re: [PATCH v4 03/11] drm/vkms: Add KUnit test for YCbCr to RGB conversion matrices

Robert Mader <[email protected]> Tue, 28 Jul 2026 17:17:34 +0200
Newsgroups org.freedesktop.lists.amd-gfx,org.freedesktop.lists.dri-devel
Message-ID <[email protected]>
TBH this test feels a bit redundant to me, given that it just repeats 
the exact values, which again just get memcpy'd. I suggest to drop it.

On 22.07.26 15:45, Harry Wentland wrote:
> The existing yuv_u16_to_argb_u16 test performs a full RGB->YCbCr->RGB
> round trip with a tolerance of 0x1ff. That tolerance is required because
> fully saturated primaries clamp their chroma at encode time and cannot
> round-trip exactly, but it also makes the test unable to detect small
> scaling errors in the conversion matrices (such as using a 2^n instead
> of a 2^n - 1 full-range maximum for limited range, an error of ~129 at
> gray - below the tolerance).
>
> Add a parameterised test that checks the exact S31.32 fixed-point
> coefficients and luma offset returned by
> get_conversion_matrix_to_argb_u16() for each encoding and range against
> reference values. This directly pins the matrices and catches
> sub-tolerance regressions that the round-trip test cannot.
>
> Assisted-by: Copilot:claude-opus-4.8
> Signed-off-by: Harry Wentland <[email protected]>
> ---
>   drivers/gpu/drm/vkms/tests/vkms_format_test.c | 122 ++++++++++++++++++
>   1 file changed, 122 insertions(+)
>
> diff --git a/drivers/gpu/drm/vkms/tests/vkms_format_test.c b/drivers/gpu/drm/vkms/tests/vkms_format_test.c
> index 789c59d07ffb..e746b18bf37b 100644
> --- a/drivers/gpu/drm/vkms/tests/vkms_format_test.c
> +++ b/drivers/gpu/drm/vkms/tests/vkms_format_test.c
> @@ -263,8 +263,130 @@ KUNIT_ARRAY_PARAM(yuv_u16_to_argb_u16, yuv_u16_to_argb_u16_cases,
>   		  vkms_format_test_yuv_u16_to_argb_u16_case_desc
>   );
>   
> +/*
> + * struct conversion_matrix_case - Reference matrix to test the YUV to RGB
> + * conversion matrices returned by get_conversion_matrix_to_argb_u16()
> + *
> + * @encoding: Encoding of the conversion matrix under test
> + * @range: Range of the conversion matrix under test
> + * @expected: Expected S31.32 fixed-point matrix and luma offset
> + *
> + * The limited-range coefficients use the studio-range scaling mandated by the
> + * DRM UAPI and IGT's igt_ycbcr_to_rgb_matrix(): the narrow range is expanded
> + * relative to a full-range maximum of 2^n - 1 (255 for 8-bit), i.e. luma by
> + * 255/(235 - 16) and chroma by 255/(240 - 128). Generating them with the
> + * common 2^n normalisation (as colour.matrix_YCbCr(is_legal=True) does) is off
> + * by a factor of 256/255. See the DRM_COLOROP_FM_YCBCR*_LIMITED_RGB
> + * documentation in <drm/drm_colorop.h>.
> + */
> +struct conversion_matrix_case {
> +	enum drm_color_encoding encoding;
> +	enum drm_color_range range;
> +	struct conversion_matrix expected;
> +};
> +
> +static struct conversion_matrix_case conversion_matrix_cases[] = {
> +	{
> +		.encoding = DRM_COLOR_YCBCR_BT601,
> +		.range = DRM_COLOR_YCBCR_FULL_RANGE,
> +		.expected = { .matrix = {
> +			{ 4294967296, 0,           6021544149 },
> +			{ 4294967296, -1478054095, -3067191994 },
> +			{ 4294967296, 7610682049,  0 },
> +		}, .y_offset = 0 },
> +	},
> +	{
> +		.encoding = DRM_COLOR_YCBCR_BT601,
> +		.range = DRM_COLOR_YCBCR_LIMITED_RANGE,
> +		.expected = { .matrix = {
> +			{ 5000989317, 0,           6854882848 },
> +			{ 5000989317, -1682606224, -3491669458 },
> +			{ 5000989317, 8663946082,  0 },
> +		}, .y_offset = 16 },
> +	},
> +	{
> +		.encoding = DRM_COLOR_YCBCR_BT709,
> +		.range = DRM_COLOR_YCBCR_FULL_RANGE,
> +		.expected = { .matrix = {
> +			{ 4294967296, 0,          6763714498 },
> +			{ 4294967296, -804551626, -2010578443 },
> +			{ 4294967296, 7969741314, 0 },
> +		}, .y_offset = 0 },
> +	},
> +	{
> +		.encoding = DRM_COLOR_YCBCR_BT709,
> +		.range = DRM_COLOR_YCBCR_LIMITED_RANGE,
> +		.expected = { .matrix = {
> +			{ 5000989317, 0,          7699764272 },
> +			{ 5000989317, -915895824, -2288828138 },
> +			{ 5000989317, 9072696586, 0 },
> +		}, .y_offset = 16 },
> +	},
> +	{
> +		.encoding = DRM_COLOR_YCBCR_BT2020,
> +		.range = DRM_COLOR_YCBCR_FULL_RANGE,
> +		.expected = { .matrix = {
> +			{ 4294967296, 0,          6333358775 },
> +			{ 4294967296, -706750298, -2453942994 },
> +			{ 4294967296, 8080551471, 0 },
> +		}, .y_offset = 0 },
> +	},
> +	{
> +		.encoding = DRM_COLOR_YCBCR_BT2020,
> +		.range = DRM_COLOR_YCBCR_LIMITED_RANGE,
> +		.expected = { .matrix = {
> +			{ 5000989317, 0,          7209850391 },
> +			{ 5000989317, -804559491, -2793551177 },
> +			{ 5000989317, 9198842076, 0 },
> +		}, .y_offset = 16 },
> +	},
> +};
> +
> +/*
> + * vkms_format_test_conversion_matrix - Verify the YUV to RGB conversion matrices
> + *
> + * This test checks that get_conversion_matrix_to_argb_u16() returns the exact
> + * fixed-point coefficients expected for each encoding and range. Unlike the
> + * round-trip test above, it is sensitive to small (sub-tolerance) scaling
> + * errors such as using a 2^n instead of a 2^n - 1 full-range maximum for the
> + * limited-range matrices.
> + */
> +static void vkms_format_test_conversion_matrix(struct kunit *test)
> +{
> +	const struct conversion_matrix_case *param = test->param_value;
> +	struct conversion_matrix matrix;
> +
> +	get_conversion_matrix_to_argb_u16(DRM_FORMAT_NV12, param->encoding,
> +					  param->range, false, &matrix);
> +
> +	for (size_t i = 0; i < 3; i++) {
> +		for (size_t j = 0; j < 3; j++) {
> +			KUNIT_EXPECT_EQ_MSG(test, matrix.matrix[i][j],
> +					    param->expected.matrix[i][j],
> +					    "matrix[%zu][%zu] mismatch for %s - %s",
> +					    i, j,
> +					    drm_get_color_encoding_name(param->encoding),
> +					    drm_get_color_range_name(param->range));
> +		}
> +	}
> +
> +	KUNIT_EXPECT_EQ(test, matrix.y_offset, param->expected.y_offset);
> +}
> +
> +static void vkms_format_test_conversion_matrix_case_desc(struct conversion_matrix_case *t,
> +							 char *desc)
> +{
> +	snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s - %s",
> +		 drm_get_color_encoding_name(t->encoding), drm_get_color_range_name(t->range));
> +}
> +
> +KUNIT_ARRAY_PARAM(conversion_matrix, conversion_matrix_cases,
> +		  vkms_format_test_conversion_matrix_case_desc
> +);
> +
>   static struct kunit_case vkms_format_test_cases[] = {
>   	KUNIT_CASE_PARAM(vkms_format_test_yuv_u16_to_argb_u16, yuv_u16_to_argb_u16_gen_params),
> +	KUNIT_CASE_PARAM(vkms_format_test_conversion_matrix, conversion_matrix_gen_params),
>   	{}
>   };
>   

-- 
Robert Mader
Consultant Software Developer

Collabora Ltd.
Platinum Building, St John's Innovation Park, Cambridge CB4 0DS, UK
Registered in England & Wales, no. 5513718