Re: [igt-dev] [PATCH i-g-t] lib/igt_fb: Add support for P01x formats, v2.

Sharma, Swati2
2019-02-04T14:09:30Z

On 04-Feb-19 6:16 PM, Maarten Lankhorst wrote:
> The P01x formats are planar 16 bits per component, with the unused lower bits set to 0.
> This means they can all be converted the same way. Only the range is slightly different,
> and this is handled in the color_encoding implementation.
>
> This requires cairo 1.17.2 and pixman 0.36. This works but doesn't give extra precision.
> For more than 8 bits precision a few more patches are required to pixman, pending review:
> https://lists.freedesktop.org/archives/pixman/2019-January/004815.html
> https://lists.freedesktop.org/archives/pixman/2019-January/004809.html
>
> Once those are merged, we will require the next pixman release for better precision.
>
> Changes since v1:
> - Add fallback color definitions when compiling on cairo version < 1.17.2.
> - Skip when FB creation fails on HDR formats, instead of failing.
>
> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> ---
> include/drm-uapi/drm_fourcc.h | 10 ++
> lib/igt_color_encoding.c | 4 +
> lib/igt_fb.c | 234 ++++++++++++++++++++++++++++++++--
> lib/igt_fb.h | 6 +
> 4 files changed, 243 insertions(+), 11 deletions(-)
>
> diff --git a/include/drm-uapi/drm_fourcc.h b/include/drm-uapi/drm_fourcc.h
> index 4ddf754bab09..c9a5e5787031 100644
> --- a/include/drm-uapi/drm_fourcc.h
> +++ b/include/drm-uapi/drm_fourcc.h
> @@ -195,6 +195,16 @@ extern "C" {
> #define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */
> #define DRM_FORMAT_NV42 fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */
>
> +/*
> + * 2 plane YCbCr
> + * index 0 = Y plane, [15:0] Y little endian where Pxxx indicate
> + * component xxx msb Y [xxx:16-xxx]
> + * index 1 = Cr:Cb plane, [31:0] Cr:Cb little endian [xxx:16-xxx:xxx:16-xxx]
> + */
> +#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') /* 2x2 subsampled Cr:Cb plane, 10 bit per channel */
> +#define DRM_FORMAT_P012 fourcc_code('P', '0', '1', '2') /* 2x2 subsampled Cr:Cb plane, 12 bit per channel */
> +#define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane, 16 bit per channel */
> +
> /*
> * 3 plane YCbCr
> * index 0: Y plane, [7:0] Y
> diff --git a/lib/igt_color_encoding.c b/lib/igt_color_encoding.c
> index 4cbe18e217e3..b7a12a1e07f7 100644
> --- a/lib/igt_color_encoding.c
> +++ b/lib/igt_color_encoding.c
> @@ -135,11 +135,15 @@ static const struct color_encoding_format {
> float ofs_y, max_y, ofs_cbcr, mid_cbcr, max_cbcr;
> } formats[] = {
> { DRM_FORMAT_XRGB8888, 255.f, },
> + { IGT_FORMAT_FLOAT, 1.f, },
> { DRM_FORMAT_NV12, 255.f, 16.f, 235.f, 16.f, 128.f, 240.f },
> { DRM_FORMAT_YUYV, 255.f, 16.f, 235.f, 16.f, 128.f, 240.f },
> { DRM_FORMAT_YVYU, 255.f, 16.f, 235.f, 16.f, 128.f, 240.f },
> { DRM_FORMAT_UYVY, 255.f, 16.f, 235.f, 16.f, 128.f, 240.f },
> { DRM_FORMAT_VYUY, 255.f, 16.f, 235.f, 16.f, 128.f, 240.f },
> + { DRM_FORMAT_P010, 65472.f, 4096.f, 60160.f, 4096.f, 32768.f, 61440.f },
> + { DRM_FORMAT_P012, 65520.f, 4096.f, 60160.f, 4096.f, 32768.f, 61440.f },
> + { DRM_FORMAT_P016, 65535.f, 4096.f, 60160.f, 4096.f, 32768.f, 61440.f },
> };
>
> static const struct color_encoding_format *lookup_fourcc(uint32_t fourcc)
> diff --git a/lib/igt_fb.c b/lib/igt_fb.c
> index a971aebb36b3..30e55eabdfa8 100644
> --- a/lib/igt_fb.c
> +++ b/lib/igt_fb.c
> @@ -62,6 +62,20 @@
>
> #define PIXMAN_invalid 0
>
> +#if CAIRO_VERSION < CAIRO_VERSION_ENCODE(1, 17, 2)
> +/*
> + * We need cairo 1.17.2 to use HDR formats, but the only thing added is a value
> + * to pixman_format_code_t.
> + *
> + * To prevent going outside the enum, make pixman_format_code_t an int and define
> + * ourselves.
> + */
> +
> +#define CAIRO_FORMAT_RGB96F (6)
> +#define CAIRO_FORMAT_RGBA128F (7)
> +#define pixman_format_code_t int
> +#endif
> +
> /* drm fourcc/cairo format maps */
> static const struct format_desc_struct {
> const char *name;
> @@ -151,6 +165,22 @@ static const struct format_desc_struct {
> .cairo_id = CAIRO_FORMAT_RGB24,
> .num_planes = 1, .plane_bpp = { 16, },
> },
> + { .name = "P010", .depth = -1, .drm_id = DRM_FORMAT_P010,
> + .cairo_id = CAIRO_FORMAT_RGB96F,
> + .num_planes = 2, .plane_bpp = { 16, 32 },
> + },
> + { .name = "P012", .depth = -1, .drm_id = DRM_FORMAT_P012,
> + .cairo_id = CAIRO_FORMAT_RGB96F,
> + .num_planes = 2, .plane_bpp = { 16, 32 },
> + },
> + { .name = "P016", .depth = -1, .drm_id = DRM_FORMAT_P016,
> + .cairo_id = CAIRO_FORMAT_RGB96F,
> + .num_planes = 2, .plane_bpp = { 16, 32 },
> + },
> + { .name = "IGT-FLOAT", .depth = -1, .drm_id = IGT_FORMAT_FLOAT,
> + .cairo_id = CAIRO_FORMAT_INVALID,
> + .num_planes = 1, .plane_bpp = { 128 },
> + },
> };
> #define for_each_format(f) \
> for (f = format_desc; f - format_desc < ARRAY_SIZE(format_desc); f++)
> @@ -239,10 +269,17 @@ void igt_get_fb_tile_size(int fd, uint64_t tiling, int fb_bpp,
>
> static unsigned fb_plane_width(const struct igt_fb *fb, int plane)
> {
> - if (fb->drm_format == DRM_FORMAT_NV12 && plane == 1)
> - return DIV_ROUND_UP(fb->width, 2);
> -
> - return fb->width;
> + switch (fb->drm_format) {
> + case DRM_FORMAT_P010:
> + case DRM_FORMAT_P012:
> + case DRM_FORMAT_P016:
> + case DRM_FORMAT_NV12:
> + if (plane == 1)
> + return DIV_ROUND_UP(fb->width, 2);
> + /* fall-thru */
> + default:
> + return fb->width;
> + }
> }
>
> static unsigned fb_plane_bpp(const struct igt_fb *fb, int plane)
> @@ -254,10 +291,17 @@ static unsigned fb_plane_bpp(const struct igt_fb *fb, int plane)
>
> static unsigned fb_plane_height(const struct igt_fb *fb, int plane)
> {
> - if (fb->drm_format == DRM_FORMAT_NV12 && plane == 1)
> - return DIV_ROUND_UP(fb->height, 2);
> -
> - return fb->height;
> + switch (fb->drm_format) {
> + case DRM_FORMAT_P010:
> + case DRM_FORMAT_P012:
> + case DRM_FORMAT_P016:
> + case DRM_FORMAT_NV12:
> + if (plane == 1)
> + return DIV_ROUND_UP(fb->height, 2);
> + /* fall-thru */
> + default:
> + return fb->height;
> + }
> }
>
> static int fb_num_planes(const struct igt_fb *fb)
> @@ -516,6 +560,13 @@ static int create_bo_for_fb(struct igt_fb *fb)
> full_range ? 0x00800080 : 0x10801080,
> fb->strides[0] * fb->plane_height[0] / sizeof(wchar_t));
> break;
> + case DRM_FORMAT_P010:
> + case DRM_FORMAT_P012:
> + case DRM_FORMAT_P016:
> + wmemset(ptr, full_range ? 0 : 0x10001000,
> + fb->offsets[1] / sizeof(wchar_t));
> + wmemset(ptr + fb->offsets[1], 0x80008000,
> + DIV_ROUND_UP(fb->height,2) * fb->strides[1] / sizeof(wchar_t));
> }
> gem_munmap(ptr, fb->size);
>
> @@ -1463,6 +1514,7 @@ struct fb_convert_blit_upload {
> };
>
> static void *igt_fb_create_cairo_shadow_buffer(int fd,
> + unsigned drm_format,
> unsigned int width,
> unsigned int height,
> struct igt_fb *shadow)
> @@ -1472,7 +1524,7 @@ static void *igt_fb_create_cairo_shadow_buffer(int fd,
> igt_assert(shadow);
>
> fb_init(shadow, fd, width, height,
> - DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE,
> + drm_format, LOCAL_DRM_FORMAT_MOD_NONE,
> IGT_COLOR_YCBCR_BT709, IGT_COLOR_YCBCR_LIMITED_RANGE);
>
> shadow->strides[0] = ALIGN(width * shadow->plane_bpp[0], 16);
> @@ -1983,6 +2035,124 @@ static void convert_rgb24_to_yuyv(struct fb_convert *cvt)
> }
> }
>
> +static void read_rgbf(struct igt_vec4 *rgb, const float *rgb24)
> +{
> + rgb->d[0] = rgb24[0];
> + rgb->d[1] = rgb24[1];
> + rgb->d[2] = rgb24[2];
> + rgb->d[3] = 1.0f;
> +}
> +
> +static void write_rgbf(float *rgb24, const struct igt_vec4 *rgb)
> +{
> + rgb24[0] = rgb->d[0];
> + rgb24[1] = rgb->d[1];
> + rgb24[2] = rgb->d[2];
> +}
> +
> +static void convert_p01X_to_float(struct fb_convert *cvt)
> +{
> + int i, j;
> + struct igt_fb *fb_p01x = cvt->src.fb;
> + struct igt_fb *fb_float = cvt->dst.fb;
> + const uint16_t *y, *uv;
> + void *buf = convert_src_get(cvt);
> + float *ptr = cvt->dst.ptr + fb_float->offsets[0];
> + unsigned float_stride = fb_float->strides[0] / sizeof(*ptr);
> + unsigned planar_stride = fb_p01x->strides[0] / sizeof(*y);
> + struct igt_mat4 m = igt_ycbcr_to_rgb_matrix(fb_p01x->drm_format,
> + fb_float->drm_format,
> + fb_p01x->color_encoding,
> + fb_p01x->color_range);
> +
> + y = buf + fb_p01x->offsets[0];
> + uv = buf + fb_p01x->offsets[1];
> +
> + for (i = 0; i < fb_p01x->height / 2; i++) {
> + for (j = 0; j < fb_p01x->width / 2; j++) {
> + /* Convert 2x2 pixel blocks */
> + struct igt_vec4 yuv[4];
> + struct igt_vec4 rgb[4];
> +
> + yuv[0].d[0] = y[j * 2 + 0];
> + yuv[1].d[0] = y[j * 2 + 1];
> + yuv[2].d[0] = y[j * 2 + 0 + planar_stride];
> + yuv[3].d[0] = y[j * 2 + 1 + planar_stride];
> +
> + yuv[0].d[1] = yuv[1].d[1] = yuv[2].d[1] = yuv[3].d[1] = uv[j * 2 + 0];
> + yuv[0].d[2] = yuv[1].d[2] = yuv[2].d[2] = yuv[3].d[2] = uv[j * 2 + 1];
> + yuv[0].d[3] = yuv[1].d[3] = yuv[2].d[3] = yuv[3].d[3] = 1.0f;
> +
> + rgb[0] = igt_matrix_transform(&m, &yuv[0]);
> + rgb[1] = igt_matrix_transform(&m, &yuv[1]);
> + rgb[2] = igt_matrix_transform(&m, &yuv[2]);
> + rgb[3] = igt_matrix_transform(&m, &yuv[3]);
> +
> + write_rgbf(&ptr[j * 6 + 0], &rgb[0]);
> + write_rgbf(&ptr[j * 6 + 3], &rgb[1]);
> + write_rgbf(&ptr[j * 6 + 0 + float_stride], &rgb[2]);
> + write_rgbf(&ptr[j * 6 + 3 + float_stride], &rgb[3]);
> + }
> +
> + ptr += 2 * float_stride;
> + y += 2 * planar_stride;
> + uv += planar_stride;
> + }
> +
> + convert_src_put(cvt, buf);
> +}
> +
> +static void convert_float_to_p01X(struct fb_convert *cvt)
> +{
> + int i, j;
> + struct igt_fb *fb_float = cvt->src.fb;
> + struct igt_fb *fb_p01x = cvt->dst.fb;
> + uint16_t *y = cvt->dst.ptr + fb_p01x->offsets[0];
> + uint16_t *uv = cvt->dst.ptr + fb_p01x->offsets[1];
> + const float *ptr = cvt->src.ptr + fb_float->offsets[0];
> + unsigned float_stride = fb_float->strides[0] / sizeof(*ptr);
> + unsigned planar_stride = fb_p01x->strides[0] / sizeof(*y);
> + struct igt_mat4 m = igt_rgb_to_ycbcr_matrix(fb_float->drm_format,
> + fb_p01x->drm_format,
> + fb_p01x->color_encoding,
> + fb_p01x->color_range);
> +
> + for (i = 0; i < fb_p01x->height / 2; i++) {
> + for (j = 0; j < fb_p01x->width / 2; j++) {
> + /* Convert 2x2 pixel blocks */
> + struct igt_vec4 rgb[4];
> + struct igt_vec4 yuv[4];
> +
> + read_rgbf(&rgb[0], &ptr[j * 6 + 0]);
> + read_rgbf(&rgb[1], &ptr[j * 6 + 3]);
> + read_rgbf(&rgb[2], &ptr[j * 6 + 0 + float_stride]);
> + read_rgbf(&rgb[3], &ptr[j * 6 + 3 + float_stride]);
> +
> + yuv[0] = igt_matrix_transform(&m, &rgb[0]);
> + yuv[1] = igt_matrix_transform(&m, &rgb[1]);
> + yuv[2] = igt_matrix_transform(&m, &rgb[2]);
> + yuv[3] = igt_matrix_transform(&m, &rgb[3]);
> +
> + y[j * 2 + 0] = yuv[0].d[0];
> + y[j * 2 + 1] = yuv[1].d[0];
> + y[j * 2 + 0 + planar_stride] = yuv[2].d[0];
> + y[j * 2 + 1 + planar_stride] = yuv[3].d[0];
> +
> + /*
> + * We assume the MPEG2 chroma siting convention, where
> + * pixel center for Cb'Cr' is between the left top and
> + * bottom pixel in a 2x2 block, so take the average.
> + */
> + uv[j * 2 + 0] = (yuv[0].d[1] + yuv[2].d[1]) / 2.0f;
> + uv[j * 2 + 1] = (yuv[0].d[2] + yuv[2].d[2]) / 2.0f;
> + }
> +
> + ptr += 2 * float_stride;
> + y += 2 * planar_stride;
> + uv += planar_stride;
> + }
> +}
> +
> static void convert_pixman(struct fb_convert *cvt)
> {
> pixman_format_code_t src_pixman = drm_format_to_pixman(cvt->src.fb->drm_format);
> @@ -2058,6 +2228,22 @@ static void fb_convert(struct fb_convert *cvt)
> convert_rgb24_to_yuyv(cvt);
> return;
> }
> + } else if (cvt->dst.fb->drm_format == IGT_FORMAT_FLOAT) {
> + switch (cvt->src.fb->drm_format) {
> + case DRM_FORMAT_P010:
> + case DRM_FORMAT_P012:
> + case DRM_FORMAT_P016:
> + convert_p01X_to_float(cvt);
> + return;
> + }
> + } else if (cvt->src.fb->drm_format == IGT_FORMAT_FLOAT) {
> + switch (cvt->dst.fb->drm_format) {
> + case DRM_FORMAT_P010:
> + case DRM_FORMAT_P012:
> + case DRM_FORMAT_P016:
> + convert_float_to_p01X(cvt);
> + return;
> + }
> }
>
> igt_assert_f(false,
> @@ -2098,11 +2284,19 @@ static void create_cairo_surface__convert(int fd, struct igt_fb *fb)
> {
> struct fb_convert_blit_upload *blit = malloc(sizeof(*blit));
> struct fb_convert cvt = { };
> + const struct format_desc_struct *f = lookup_drm_format(fb->drm_format);
> + unsigned drm_format;
> +
> + if (f->cairo_id == CAIRO_FORMAT_RGB96F ||
> + f->cairo_id == CAIRO_FORMAT_RGBA128F)
> + drm_format = IGT_FORMAT_FLOAT;
> + else
> + drm_format = DRM_FORMAT_XRGB8888;
>
> igt_assert(blit);
> blit->base.fd = fd;
> blit->base.fb = fb;
> - blit->shadow_ptr = igt_fb_create_cairo_shadow_buffer(fd,
> + blit->shadow_ptr = igt_fb_create_cairo_shadow_buffer(fd, drm_format,
> fb->width,
> fb->height,
> &blit->shadow_fb);
> @@ -2129,7 +2323,7 @@ static void create_cairo_surface__convert(int fd, struct igt_fb *fb)
>
> fb->cairo_surface =
> cairo_image_surface_create_for_data(blit->shadow_ptr,
> - CAIRO_FORMAT_RGB24,
> + f->cairo_id,
> fb->width, fb->height,
> blit->shadow_fb.strides[0]);
>
> @@ -2194,6 +2388,21 @@ cairo_surface_t *igt_get_cairo_surface(int fd, struct igt_fb *fb)
> create_cairo_surface__blit(fd, fb);
> else
> create_cairo_surface__gtt(fd, fb);
> +
> + if (f->cairo_id == CAIRO_FORMAT_RGB96F ||
> + f->cairo_id == CAIRO_FORMAT_RGBA128F) {
> + cairo_status_t status = cairo_surface_status(fb->cairo_surface);
> +
> + igt_skip_on_f(status == CAIRO_STATUS_INVALID_FORMAT &&
> + cairo_version() < CAIRO_VERSION_ENCODE(1, 17, 2),
> + "Cairo version too old, need 1.17.2, have %s\n",
> + cairo_version_string());
> +
> + igt_skip_on_f(status == CAIRO_STATUS_NO_MEMORY &&
> + pixman_version() < PIXMAN_VERSION_ENCODE(0, 36, 0),
> + "Pixman version too old, need 0.36.0, have %s\n",
> + pixman_version_string());
> + }
> }

Hi Maarten,

Though I have installed cairo and pixman versions 1.17.3 and 37.1
respectively as seen in /usr/include/cairo and /usr/include/pixman-1
versions file . Still cairo_version() and pixman_version() prints the
old 1.14.06 and 33.06 respectively in i-g-t. Are there some changes
required in i-g-t to link to new releases of cairo and pixman?

> igt_assert(cairo_surface_status(fb->cairo_surface) == CAIRO_STATUS_SUCCESS);
> @@ -2400,6 +2609,9 @@ bool igt_format_is_yuv(uint32_t drm_format)
> {
> switch (drm_format) {
> case DRM_FORMAT_NV12:
> + case DRM_FORMAT_P010:
> + case DRM_FORMAT_P012:
> + case DRM_FORMAT_P016:
> case DRM_FORMAT_YUYV:
> case DRM_FORMAT_YVYU:
> case DRM_FORMAT_UYVY:
> diff --git a/lib/igt_fb.h b/lib/igt_fb.h
> index 9f027deba842..8c683db5e9ec 100644
> --- a/lib/igt_fb.h
> +++ b/lib/igt_fb.h
> @@ -38,6 +38,12 @@
>
> #include "igt_color_encoding.h"
>
> +/*
> + * Internal format to denote a buffer compatible with pixman's
> + * floating point format. Range [0-1].
> + */
> +#define IGT_FORMAT_FLOAT fourcc_code('I', 'G', 'F', 'x')
> +
> /**
> * igt_fb_t:
> * @fb_id: KMS ID of the framebuffer

--
Thanks and Regards,
Swati