[PATCH V10 27/30] tests/kms_colorop: Add a 3D LUT subtest

Aurabindo Pillai aurabindo.pillai at amd.com
Fri Aug 22 21:35:19 UTC 2025



On 8/15/25 12:06 AM, Alex Hung wrote:
> This includes a subtests 3dlut_17_12_rgb.
> 
> Signed-off-by: Alex Hung <alex.hung at amd.com>
> ---
>   include/drm-uapi/drm_mode.h |   18 +
>   lib/igt_color.c             |  186 ++
>   lib/igt_color.h             |   10 +
>   lib/igt_color_lut.h         | 4946 +++++++++++++++++++++++++++++++++++
>   lib/igt_kms.c               |    2 +
>   lib/igt_kms.h               |    2 +
>   tests/kms_colorop.c         |   35 +-
>   tests/kms_colorop.h         |   19 +
>   8 files changed, 5217 insertions(+), 1 deletion(-)
>   create mode 100644 lib/igt_color_lut.h
> 
> diff --git a/include/drm-uapi/drm_mode.h b/include/drm-uapi/drm_mode.h
> index fe3e98e20..009bb3e7f 100644
> --- a/include/drm-uapi/drm_mode.h
> +++ b/include/drm-uapi/drm_mode.h
> @@ -891,6 +891,24 @@ enum drm_colorop_type {
>   	DRM_COLOROP_1D_LUT,
>   	DRM_COLOROP_CTM_3X4,
>   	DRM_COLOROP_MULTIPLIER,
> +	DRM_COLOROP_3D_LUT,
> +};
> +
> +enum drm_colorop_interpolation_type {
> +	DRM_COLOROP_TETRAHEDRAL,
> +};
> +
> +/**
> + * enum drm_colorop_lut3d_interpolation_type - type of 3DLUT interpolation
> + *
> + */
> +enum drm_colorop_lut3d_interpolation_type {
> +	/**
> +	 * @DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL:
> +	 *
> +	 * Tetrahedral 3DLUT interpolation
> +	 */
> +	DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL,
>   };
>   
>   /**
> diff --git a/lib/igt_color.c b/lib/igt_color.c
> index aa65eac20..6a306bda2 100644
> --- a/lib/igt_color.c
> +++ b/lib/igt_color.c
> @@ -213,6 +213,184 @@ void igt_color_multiply_inv_125(igt_pixel_t *pixel)
>   	igt_color_multiply(pixel, 1 / 125.0f);
>   }
>   
> +static int
> +igt_get_lut3d_index_blue_fast(int r, int g, int b, long dim, int components)
> +{
> +	return components * (b + (int)dim * (g + (int)dim * r));
> +}
> +
> +/* algorithm from https://github.com/AcademySoftwareFoundation/OpenColorIO/blob/main/src/OpenColorIO/ops/lut3d/Lut3DOpCPU.cpp#L422 */
> +static void igt_color_3dlut_tetrahedral(igt_pixel_t *pixel, igt_3dlut_t *lut3d, long m_dim)
> +{
> +	int n000, n100, n010, n001, n110, n101, n011, n111;
> +	float m_step = (float) m_dim - 1.0f;
> +	const float dimMinusOne = (float) m_dim - 1.f;
> +	float *m_optLut = (float *) lut3d->lut;
> +	float idx[3];
> +	float out[3];
> +	int indexLow[3];
> +	int indexHigh[3];
> +	float fx, fy, fz;
> +
> +	idx[0] = pixel->b * m_step;
> +	idx[1] = pixel->g * m_step;
> +	idx[2] = pixel->r * m_step;
> +
> +	// NaNs become 0.
> +	idx[0] = clamp(idx[0], 0.f, dimMinusOne);
> +	idx[1] = clamp(idx[1], 0.f, dimMinusOne);
> +	idx[2] = clamp(idx[2], 0.f, dimMinusOne);
> +
> +	indexLow[0] = floor(idx[0]);
> +	indexLow[1] = floor(idx[1]);
> +	indexLow[2] = floor(idx[2]);
> +
> +	// When the idx is exactly equal to an index (e.g. 0,1,2...)
> +	// then the computation of highIdx is wrong. However,
> +	// the delta is then equal to zero (e.g. idx-lowIdx),
> +	// so the highIdx has no impact.
> +	indexHigh[0] = ceil(idx[0]);
> +	indexHigh[1] = ceil(idx[1]);
> +	indexHigh[2] = ceil(idx[2]);
> +
> +	fx = idx[0] - (float) indexLow[0];
> +	fy = idx[1] - (float) indexLow[1];
> +	fz = idx[2] - (float) indexLow[2];
> +
> +	// Compute index into LUT for surrounding corners
> +	n000 = igt_get_lut3d_index_blue_fast(indexLow[0], indexLow[1], indexLow[2], m_dim, 3);
> +	n100 = igt_get_lut3d_index_blue_fast(indexHigh[0], indexLow[1], indexLow[2], m_dim, 3);
> +	n010 = igt_get_lut3d_index_blue_fast(indexLow[0], indexHigh[1], indexLow[2], m_dim, 3);
> +	n001 = igt_get_lut3d_index_blue_fast(indexLow[0], indexLow[1], indexHigh[2], m_dim, 3);
> +	n110 = igt_get_lut3d_index_blue_fast(indexHigh[0], indexHigh[1], indexLow[2], m_dim, 3);
> +	n101 = igt_get_lut3d_index_blue_fast(indexHigh[0], indexLow[1], indexHigh[2], m_dim, 3);
> +	n011 = igt_get_lut3d_index_blue_fast(indexLow[0], indexHigh[1], indexHigh[2], m_dim, 3);
> +	n111 = igt_get_lut3d_index_blue_fast(indexHigh[0], indexHigh[1], indexHigh[2], m_dim, 3);
> +
> +	if (fx > fy) {
> +		if (fy > fz) {
> +			out[0] =
> +				(1 - fx)  * m_optLut[n000] +
> +				(fx - fy) * m_optLut[n100] +
> +				(fy - fz) * m_optLut[n110] +
> +				(fz)      * m_optLut[n111];
> +
> +			out[1] =
> +				(1 - fx)  * m_optLut[n000 + 1] +
> +				(fx - fy) * m_optLut[n100 + 1] +
> +				(fy - fz) * m_optLut[n110 + 1] +
> +				(fz)      * m_optLut[n111 + 1];
> +
> +			out[2] =
> +				(1 - fx)  * m_optLut[n000 + 2] +
> +				(fx - fy) * m_optLut[n100 + 2] +
> +				(fy - fz) * m_optLut[n110 + 2] +
> +				(fz)      * m_optLut[n111 + 2];
> +		} else if (fx > fz) {
> +			out[0] =
> +				(1 - fx)  * m_optLut[n000] +
> +				(fx - fz) * m_optLut[n100] +
> +				(fz - fy) * m_optLut[n101] +
> +				(fy)      * m_optLut[n111];
> +
> +			out[1] =
> +				(1 - fx)  * m_optLut[n000 + 1] +
> +				(fx - fz) * m_optLut[n100 + 1] +
> +				(fz - fy) * m_optLut[n101 + 1] +
> +				(fy)      * m_optLut[n111 + 1];
> +
> +			out[2] =
> +				(1 - fx)  * m_optLut[n000 + 2] +
> +				(fx - fz) * m_optLut[n100 + 2] +
> +				(fz - fy) * m_optLut[n101 + 2] +
> +				(fy)      * m_optLut[n111 + 2];
> +		} else {
> +			out[0] =
> +				(1 - fz)  * m_optLut[n000] +
> +				(fz - fx) * m_optLut[n001] +
> +				(fx - fy) * m_optLut[n101] +
> +				(fy)      * m_optLut[n111];
> +
> +			out[1] =
> +				(1 - fz)  * m_optLut[n000 + 1] +
> +				(fz - fx) * m_optLut[n001 + 1] +
> +				(fx - fy) * m_optLut[n101 + 1] +
> +				(fy)      * m_optLut[n111 + 1];
> +
> +			out[2] =
> +				(1 - fz)  * m_optLut[n000 + 2] +
> +				(fz - fx) * m_optLut[n001 + 2] +
> +				(fx - fy) * m_optLut[n101 + 2] +
> +				(fy)      * m_optLut[n111 + 2];
> +		}
> +	} else {
> +		if (fz > fy) {
> +			out[0] =
> +				(1 - fz)  * m_optLut[n000] +
> +				(fz - fy) * m_optLut[n001] +
> +				(fy - fx) * m_optLut[n011] +
> +				(fx)      * m_optLut[n111];
> +
> +			out[1] =
> +				(1 - fz)  * m_optLut[n000 + 1] +
> +				(fz - fy) * m_optLut[n001 + 1] +
> +				(fy - fx) * m_optLut[n011 + 1] +
> +				(fx)      * m_optLut[n111 + 1];
> +
> +			out[2] =
> +				(1 - fz)  * m_optLut[n000 + 2] +
> +				(fz - fy) * m_optLut[n001 + 2] +
> +				(fy - fx) * m_optLut[n011 + 2] +
> +				(fx)      * m_optLut[n111 + 2];
> +		} else if (fz > fx) {
> +			out[0] =
> +				(1 - fy)  * m_optLut[n000] +
> +				(fy - fz) * m_optLut[n010] +
> +				(fz - fx) * m_optLut[n011] +
> +				(fx)      * m_optLut[n111];
> +
> +			out[1] =
> +				(1 - fy)  * m_optLut[n000 + 1] +
> +				(fy - fz) * m_optLut[n010 + 1] +
> +				(fz - fx) * m_optLut[n011 + 1] +
> +				(fx)      * m_optLut[n111 + 1];
> +
> +			out[2] =
> +				(1 - fy)  * m_optLut[n000 + 2] +
> +				(fy - fz) * m_optLut[n010 + 2] +
> +				(fz - fx) * m_optLut[n011 + 2] +
> +				(fx)      * m_optLut[n111 + 2];
> +		} else {
> +			out[0] =
> +				(1 - fy)  * m_optLut[n000] +
> +				(fy - fx) * m_optLut[n010] +
> +				(fx - fz) * m_optLut[n110] +
> +				(fz)      * m_optLut[n111];
> +
> +			out[1] =
> +				(1 - fy)  * m_optLut[n000 + 1] +
> +				(fy - fx) * m_optLut[n010 + 1] +
> +				(fx - fz) * m_optLut[n110 + 1] +
> +				(fz)      * m_optLut[n111 + 1];
> +
> +			out[2] =
> +				(1 - fy)  * m_optLut[n000 + 2] +
> +				(fy - fx) * m_optLut[n010 + 2] +
> +				(fx - fz) * m_optLut[n110 + 2] +
> +				(fz)      * m_optLut[n111 + 2];
> +		}
> +	}
> +
> +	pixel->r = out[0];
> +	pixel->g = out[1];
> +	pixel->b = out[2];
> +}
> +
> +void igt_color_3dlut_17_12_rgb(igt_pixel_t *pixel)
> +{
> +	igt_color_3dlut_tetrahedral(pixel, &igt_3dlut_17_rgb, 17);
> +}
> +
>   static void
>   igt_color_fourcc_to_pixel(uint32_t raw_pixel, uint32_t drm_format, igt_pixel_t *pixel)
>   {
> @@ -446,3 +624,11 @@ void igt_colorop_set_custom_1dlut(igt_display_t *display,
>   {
>   	igt_colorop_replace_prop_blob(colorop, IGT_COLOROP_DATA, lut1d, lut_size);
>   }
> +
> +void igt_colorop_set_3dlut(igt_display_t *display,
> +			   igt_colorop_t *colorop,
> +			   const igt_3dlut_norm_t *lut3d,
> +			   const size_t lut_size)
> +{
> +	igt_colorop_replace_prop_blob(colorop, IGT_COLOROP_DATA, lut3d, lut_size);
> +}
> diff --git a/lib/igt_color.h b/lib/igt_color.h
> index 761dd2f95..0e1dc84e2 100644
> --- a/lib/igt_color.h
> +++ b/lib/igt_color.h
> @@ -16,6 +16,7 @@
>   
>   #include "igt_fb.h"
>   #include "igt_kms.h"
> +#include "igt_color_lut.h"
>   
>   #define MAX_COLOR_LUT_ENTRIES 4096
>   
> @@ -112,6 +113,12 @@ void igt_colorop_set_custom_1dlut(igt_display_t *display,
>   				  const igt_1dlut_t *lut1d,
>   				  const size_t lut_size);
>   
> +void igt_colorop_set_3dlut(igt_display_t *display,
> +			   igt_colorop_t *colorop,
> +			   const igt_3dlut_norm_t *lut3d,
> +			   const size_t lut_size);
> +
> +
>   /* transformations */
>   
>   void igt_color_srgb_inv_eotf(igt_pixel_t *pixel);
> @@ -136,4 +143,7 @@ void igt_color_ctm_3x4_bt709_enc(igt_pixel_t *pixel);
>   
>   void igt_color_multiply_125(igt_pixel_t *pixel);
>   void igt_color_multiply_inv_125(igt_pixel_t *pixel);
> +void igt_color_3dlut_17_12_rgb(igt_pixel_t *pixel);
> +void igt_color_3dlut_17_12_bgr(igt_pixel_t *pixel);
> +
>   #endif
> diff --git a/lib/igt_color_lut.h b/lib/igt_color_lut.h
> new file mode 100644
> index 000000000..b5c149c3d
> --- /dev/null
> +++ b/lib/igt_color_lut.h
> @@ -0,0 +1,4946 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright 2024 Advanced Micro Devices, Inc.
> + *
> + */
> +
> +#ifndef __IGT_COLOR_LUT_H__
> +#define __IGT_COLOR_LUT_H__
> +
> +#include "drm_mode.h"
> +
> +typedef struct igt_3dlut_norm {
> +	struct drm_color_lut32 lut[4913];
> +} igt_3dlut_norm_t;
> +
> +struct igt_color_lut_float {
> +	float red;
> +	float green;
> +	float blue;
> +};
> +
> +typedef struct igt_3dlut {
> +	struct igt_color_lut_float lut[4913];
> +} igt_3dlut_t;
> +
> +/*
> + * A 3D LUT table with 17x17x17 entries for Traversal order RGB.
> + * It can be generated by scripts/convert_3dlut.py
> + */
> +igt_3dlut_t igt_3dlut_17_rgb = { {

This is pretty big, and assuming this doesn't need to be changed, 
perhaps make it const so that it goes to .rodata

> +	{ .red = 0.000000, .green = 0.000000, .blue = 0.000000 },
> +	{ .red = 0.175580, .green = 0.003419, .blue = 0.006838 },
> +	{ .red = 0.338706, .green = 0.015140, .blue = 0.012454 },
> +	{ .red = 0.492552, .green = 0.015629, .blue = 0.012454 },
> +	{ .red = 0.641026, .green = 0.023687, .blue = 0.013187 },
> +	{ .red = 0.734310, .green = 0.049817, .blue = 0.013675 },
> +	{ .red = 0.809035, .green = 0.063736, .blue = 0.006105 },
> +	{ .red = 0.884982, .green = 0.063492, .blue = 0.019048 },
> +	{ .red = 0.963370, .green = 0.056899, .blue = 0.028571 },
> +	{ .red = 0.993407, .green = 0.080586, .blue = 0.044689 },
> +	{ .red = 0.999267, .green = 0.099145, .blue = 0.054457 },
> +	{ .red = 0.999267, .green = 0.110623, .blue = 0.060317 },
> +	{ .red = 0.999267, .green = 0.116972, .blue = 0.064225 },
> +	{ .red = 0.999267, .green = 0.121612, .blue = 0.066422 },

< snip >


More information about the igt-dev mailing list