Re: [PATCH v4 07/10] lib/igt_color: Refactor transform_pixels for input/output FBs and CSC

Alex Hung <[email protected]>
Newsgroups org.freedesktop.lists.igt-dev
Message-ID <[email protected]>
Reviewed-by: Alex Hung <[email protected]>

On 7/31/26 12:01, Harry Wentland wrote:
> Refactor igt_color_transform_pixels() to accept separate input and
> output framebuffers, plus optional YUV encoding/range parameters for
> CSC conversion. This enables:
> - Reading from YUV input (NV12/P010) and writing to RGB output
> - Inline YUV-to-RGB CSC using the igt_color_encoding matrix library
> - Skipping the first transform when CSC is applied (since it's done
>    inline via the matrix)
> 
> Update apply_transforms() in kms_colorop.c to extract encoding/range
> from the first colorop when it's a FIXED_MATRIX type, and pass both input
> and output FBs to the refactored function.
> 
> Also update colorop_plane_test() to create sw_transform_fb with the
> output format rather than copying the input FB.
> 
> Assisted-by: Claude:claude-sonnet-4-5
> Signed-off-by: Harry Wentland <[email protected]>
> ---
>   lib/igt_color.c     | 170 +++++++++++++++++++++++++++++++++++---------
>   lib/igt_color.h     |   6 +-
>   tests/kms_colorop.c |  34 +++++----
>   3 files changed, 160 insertions(+), 50 deletions(-)
> 
> diff --git a/lib/igt_color.c b/lib/igt_color.c
> index a451f5642bac..582fc6c0c3bc 100644
> --- a/lib/igt_color.c
> +++ b/lib/igt_color.c
> @@ -13,6 +13,7 @@
>   
>   #include "drmtest.h"
>   #include "igt_color.h"
> +#include "igt_color_encoding.h"
>   #include "igt_core.h"
>   #include "igt_x86.h"
>   
> @@ -578,58 +579,159 @@ igt_color_pixel_to_fourcc(uint32_t drm_format, igt_pixel_t *pixel)
>   	return raw_pixel;
>   }
>   
> -int igt_color_transform_pixels(igt_fb_t *fb, igt_pixel_transform transforms[], int num_transforms)
> +int igt_color_transform_pixels(igt_fb_t *input_fb, igt_fb_t *output_fb,
> +				  igt_pixel_transform transforms[],
> +				  int num_transforms,
> +				  int yuv_encoding,
> +				  enum igt_color_range yuv_range)
>   {
> -	uint32_t *line = NULL;
> -	void *map;
> -	char *ptr;
> -	int x, y, cpp = igt_drm_format_to_bpp(fb->drm_format) / 8;
> -	uint32_t stride = igt_fb_calc_plane_stride(fb, 0);
> +	uint32_t *input_line = NULL;
> +	uint32_t *output_line = NULL;
> +	void *input_map, *output_map;
> +	char *input_ptr, *output_ptr;
> +	int x, y;
> +	int input_cpp = 0;
> +	int output_cpp = igt_drm_format_to_bpp(output_fb->drm_format) / 8;
> +	uint32_t input_stride = 0, output_stride = igt_fb_calc_plane_stride(output_fb, 0);
> +	uint8_t *y_plane = NULL, *uv_plane = NULL;
> +	int y_stride = 0, uv_stride = 0;
> +	bool input_is_yuv = (input_fb->drm_format == DRM_FORMAT_NV12 ||
> +			     input_fb->drm_format == DRM_FORMAT_P010);
> +	bool output_is_yuv = (output_fb->drm_format == DRM_FORMAT_NV12 ||
> +			      output_fb->drm_format == DRM_FORMAT_P010);
> +	struct igt_mat4 csc_matrix;
> +	bool apply_csc = false;
>   
> -	if (fb->num_planes != 1)
> +	/* Validate framebuffer dimensions match */
> +	igt_assert(input_fb->width == output_fb->width);
> +	igt_assert(input_fb->height == output_fb->height);
> +
> +	if (input_is_yuv && yuv_encoding >= 0) {
> +		apply_csc = true;
> +
> +		csc_matrix = igt_ycbcr_to_rgb_matrix(input_fb->drm_format,
> +						     output_fb->drm_format,
> +						     yuv_encoding, yuv_range);
> +	}
> +
> +	/* Validate plane counts */
> +	if (!input_is_yuv && input_fb->num_planes != 1)
>   		return -EINVAL;
>   
> -	ptr = igt_fb_map_buffer(fb->fd, fb);
> -	igt_assert(ptr);
> -	map = ptr;
> +	if (input_is_yuv && input_fb->num_planes != 2)
> +		return -EINVAL;
>   
> -	/*
> -	 * Framebuffers are often uncached, which can make byte-wise accesses
> -	 * very slow. We copy each line of the FB into a local buffer to speed
> -	 * up the hashing.
> -	 */
> -	line = malloc(stride);
> -	if (!line) {
> -		munmap(map, fb->size);
> +	if (!output_is_yuv && output_fb->num_planes != 1)
> +		return -EINVAL;
> +
> +	if (output_is_yuv && output_fb->num_planes != 2)
> +		return -EINVAL;
> +
> +	/* Map input buffer */
> +	input_ptr = igt_fb_map_buffer(input_fb->fd, input_fb);
> +	igt_assert(input_ptr);
> +	input_map = input_ptr;
> +
> +	/* For YUV input, set up plane pointers */
> +	if (input_is_yuv) {
> +		y_plane = input_map;
> +		y_stride = input_fb->strides[0];
> +		uv_plane = y_plane + input_fb->offsets[1];
> +		uv_stride = input_fb->strides[1];
> +	} else {
> +		input_cpp = igt_drm_format_to_bpp(input_fb->drm_format) / 8;
> +		input_stride = igt_fb_calc_plane_stride(input_fb, 0);
> +	}
> +
> +	/* Map output buffer */
> +	output_ptr = igt_fb_map_buffer(output_fb->fd, output_fb);
> +	igt_assert(output_ptr);
> +	output_map = output_ptr;
> +
> +	/* Allocate line buffers for speed */
> +	if (!input_is_yuv) {
> +		input_line = malloc(input_stride);
> +		if (!input_line) {
> +			igt_fb_unmap_buffer(output_fb, output_map);
> +			igt_fb_unmap_buffer(input_fb, input_map);
> +			return -ENOMEM;
> +		}
> +	}
> +
> +	output_line = malloc(output_stride);
> +	if (!output_line) {
> +		free(input_line);
> +		igt_fb_unmap_buffer(output_fb, output_map);
> +		igt_fb_unmap_buffer(input_fb, input_map);
>   		return -ENOMEM;
>   	}
>   
> -	for (y = 0; y < fb->height; y++, ptr += stride) {
> +	for (y = 0; y < input_fb->height; y++) {
> +		/* For RGB input, read line from input buffer */
> +		if (!input_is_yuv)
> +			igt_memcpy_from_wc(input_line, input_ptr + y * input_stride,
> +					   input_fb->width * input_cpp);
>   
> -		/* get line from buffer */
> -		igt_memcpy_from_wc(line, ptr, fb->width * cpp);
> -
> -		for (x = 0; x < fb->width; x++) {
> -			uint32_t raw_pixel = le32_to_cpu(line[x]);
> +		for (x = 0; x < input_fb->width; x++) {
>   			igt_pixel_t pixel;
>   			int i;
> +			int start_transform;
>   
> -			igt_color_fourcc_to_pixel(raw_pixel, fb->drm_format, &pixel);
> +			/* READ from input buffer */
> +			if (input_is_yuv) {
> +				uint32_t raw_y, raw_u, raw_v;
> +				struct igt_vec4 yuv, rgb;
>   
> -			/* run transform on pixel */
> -			for (i = 0; i < num_transforms; i++)
> -				transforms[i](&pixel);
> +				/* Extract raw Y, U, V from YUV input buffer */
> +				igt_color_extract_yuv_pixel(input_fb, x, y, y_plane, uv_plane,
> +							    y_stride, uv_stride,
> +							    &raw_y, &raw_u, &raw_v);
>   
> -			/* write back to line */
> -			line[x] = cpu_to_le32(igt_color_pixel_to_fourcc(fb->drm_format, &pixel));
> +				/* Convert YUV to RGB using pre-computed CSC matrix */
> +				if (apply_csc) {
> +					float rgb_max;
> +
> +					/* Matrix expects raw format values */
> +					yuv.d[0] = raw_y;
> +					yuv.d[1] = raw_u;
> +					yuv.d[2] = raw_v;
> +					yuv.d[3] = 1.0f;
> +
> +					rgb = igt_matrix_transform(&csc_matrix, &yuv);
> +
> +					rgb_max = (output_fb->drm_format == DRM_FORMAT_XRGB2101010) ? 1023.0f : 255.0f;
> +
> +					pixel.r = rgb.d[0] / rgb_max;
> +					pixel.g = rgb.d[1] / rgb_max;
> +					pixel.b = rgb.d[2] / rgb_max;
> +				} else {
> +					igt_color_yuv_to_pixel(input_fb->drm_format, raw_y, raw_u, raw_v, &pixel);
> +				}
> +			} else {
> +				uint32_t raw_pixel = le32_to_cpu(input_line[x]);
> +				igt_color_fourcc_to_pixel(raw_pixel, input_fb->drm_format, &pixel);
> +			}
> +
> +			/* Transform pixel through remaining transforms */
> +			/* Skip first transform if it was a CSC that we already applied */
> +			start_transform = apply_csc ? 1 : 0;
> +			for (i = start_transform; i < num_transforms; i++)
> +				if (transforms[i])
> +					transforms[i](&pixel);
> +
> +			/* write to output buffer */
> +			output_line[x] = cpu_to_le32(igt_color_pixel_to_fourcc(output_fb->drm_format, &pixel));
>   		}
>   
> -		/* copy line back to fb buffer */
> -		igt_memcpy_from_wc(ptr, line, fb->width * cpp);
> +		/* Copy output line to output buffer */
> +		igt_memcpy_from_wc(output_ptr + y * output_stride, output_line,
> +			      output_fb->width * output_cpp);
>   	}
>   
> -	free(line);
> -	igt_fb_unmap_buffer(fb, map);
> +	free(output_line);
> +	free(input_line);
> +	igt_fb_unmap_buffer(output_fb, output_map);
> +	igt_fb_unmap_buffer(input_fb, input_map);
>   
>   	return 0;
>   }
> diff --git a/lib/igt_color.h b/lib/igt_color.h
> index 722446400f01..d66cfebc4191 100644
> --- a/lib/igt_color.h
> +++ b/lib/igt_color.h
> @@ -72,7 +72,11 @@ void igt_dump_fb(igt_display_t *display, igt_fb_t *fb, const char *path_name, co
>   
>   typedef void (*igt_pixel_transform)(igt_pixel_t *pixel);
>   
> -int igt_color_transform_pixels(igt_fb_t *fb, igt_pixel_transform transforms[], int num_transforms);
> +int igt_color_transform_pixels(igt_fb_t *input_fb, igt_fb_t *output_fb,
> +			       igt_pixel_transform transforms[],
> +			       int num_transforms,
> +			       int yuv_encoding,
> +			       enum igt_color_range yuv_range);
>   
>   /* colorop helpers */
>   
> diff --git a/tests/kms_colorop.c b/tests/kms_colorop.c
> index 16db0b8ad37f..bdfe312b8124 100644
> --- a/tests/kms_colorop.c
> +++ b/tests/kms_colorop.c
> @@ -195,15 +195,25 @@ static bool compare_with_bracket(igt_fb_t *in, igt_fb_t *out)
>   
>   #define MAX_COLOROPS 5
>   
> -static void apply_transforms(kms_colorop_t *colorops[], igt_fb_t *sw_transform_fb)
> +static void apply_transforms(kms_colorop_t *colorops[], igt_fb_t *input_fb,
> +			     igt_fb_t *sw_transform_fb)
>   {
>   	int i;
> +	int yuv_encoding = -1;
> +	enum igt_color_range yuv_range = IGT_COLOR_YCBCR_LIMITED_RANGE;
>   	igt_pixel_transform transforms[MAX_COLOROPS];
>   
>   	for (i = 0; colorops[i]; i++)
>   		transforms[i] = colorops[i]->transform;
>   
> -	igt_color_transform_pixels(sw_transform_fb, transforms, i);
> +	/* If first colorop is CSC FF, extract encoding/range for sw reference */
> +	if (colorops[0] && colorops[0]->type == KMS_COLOROP_FIXED_MATRIX) {
> +		yuv_encoding = colorops[0]->fixed_matrix_info.encoding;
> +		yuv_range = colorops[0]->fixed_matrix_info.range;
> +	}
> +
> +	igt_color_transform_pixels(input_fb, sw_transform_fb, transforms, i,
> +				   yuv_encoding, yuv_range);
>   }
>   
>   static void colorop_plane_test(igt_display_t *display,
> @@ -217,11 +227,8 @@ static void colorop_plane_test(igt_display_t *display,
>   {
>   	igt_colorop_t *color_pipeline = NULL;
>   	igt_fb_t sw_transform_fb;
> -	igt_crc_t input_crc, output_crc;
>   	int res;
>   
> -	igt_fb_get_fnv1a_crc(input_fb, &input_crc);
> -
>   	/* reset color pipeline*/
>   
>   	set_color_pipeline_bypass(plane);
> @@ -235,18 +242,15 @@ static void colorop_plane_test(igt_display_t *display,
>   				NULL);
>   	igt_get_and_wait_out_fence(output);
>   
> -	/* Compare input and output buffers. They should be equal here. */
> -	igt_fb_get_fnv1a_crc(output_fb, &output_crc);
> -
> -	igt_assert_crc_equal(&input_crc, &output_crc);
> -
> -	/* create sw transformed buffer */
> -	res = igt_copy_fb(display->drm_fd, input_fb, &sw_transform_fb);
> +	/* create sw transform buffer with output format */
> +	res = igt_create_fb(display->drm_fd,
> +			    input_fb->width, input_fb->height,
> +			    output_fb->drm_format,
> +			    DRM_FORMAT_MOD_LINEAR,
> +			    &sw_transform_fb);
>   	igt_assert_lte(0, res);
>   
> -	igt_assert(igt_cmp_fb_pixels(input_fb, &sw_transform_fb, 0, 0));
> -
> -	apply_transforms(colorops, &sw_transform_fb);
> +	apply_transforms(colorops, input_fb, &sw_transform_fb);
>   
>   	if (data.dump_check)
>   		igt_dump_fb(display, &sw_transform_fb, ".", "sw_transform");
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.