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

Harry Wentland <[email protected]>
Newsgroups org.freedesktop.lists.amd-gfx,org.freedesktop.lists.dri-devel
Message-ID <[email protected]>
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),
 	{}
 };
 
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.