[PATCH weston] Add a nested YUV client to the weston-nested example

Neil Roberts neil at linux.intel.com
Mon Feb 10 09:05:53 PST 2014


This adds a separate nested client to the weston-nested example which
will create YUV buffers using libva. The code is based on the
putsurface test in libva. The client can be used by passing the -y
option to weston-nested.
---
 .gitignore                  |    1 +
 Makefile.am                 |   11 +
 clients/nested-client-yuv.c | 1058 +++++++++++++++++++++++++++++++++++++++++++
 clients/nested.c            |   96 ++--
 configure.ac                |   21 +-
 5 files changed, 1150 insertions(+), 37 deletions(-)
 create mode 100644 clients/nested-client-yuv.c

diff --git a/.gitignore b/.gitignore
index e0a73c0..3728f36 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,6 +42,7 @@ weston-gears
 weston-image
 weston-nested
 weston-nested-client
+weston-nested-client-yuv
 weston-resizor
 weston-scaler
 weston-simple-egl
diff --git a/Makefile.am b/Makefile.am
index dc50da3..9309b91 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -491,6 +491,17 @@ weston_nested_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS)
 weston_nested_client_SOURCES = clients/nested-client.c
 weston_nested_client_LDADD = $(SIMPLE_EGL_CLIENT_LIBS) -lm
 weston_nested_client_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS)
+
+if HAVE_LIBVA_WAYLAND
+demo_clients += weston-nested-client-yuv
+weston_nested_client_yuv_SOURCES = clients/nested-client-yuv.c
+weston_nested_client_yuv_LDADD = $(LIBVA_WAYLAND_LIBS) $(SIMPLE_CLIENT_LIBS)
+weston_nested_client_yuv_CFLAGS = \
+	$(CLIENT_CFLAGS) \
+	$(AM_CFLAGS) \
+	$(LIBVA_WAYLAND_CFLAGS)
+endif
+
 endif
 
 weston_eventdemo_SOURCES = clients/eventdemo.c
diff --git a/clients/nested-client-yuv.c b/clients/nested-client-yuv.c
new file mode 100644
index 0000000..ba6ee0d
--- /dev/null
+++ b/clients/nested-client-yuv.c
@@ -0,0 +1,1058 @@
+/*
+ * Copyright (c) 2008, 2009, 2014 Intel Corporation. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <sys/time.h>
+
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include <va/va_wayland.h>
+
+/*currently, if XCheckWindowEvent was called in more than one thread,
+ * it would ucause
+ * XIO:	 fatal IO error 11 (Resource temporarily unavailable) on X server ":0.0"
+ *	 after 87 requests (83 known processed) with 0 events remaining.
+ *
+ *	 X Error of failed request:  BadGC (invalid GC parameter)
+ *	 Major opcode of failed request:  60 (X_FreeGC)
+ *	 Resource id in failed request:	 0x600034
+ *	 Serial number of failed request:  398
+ *	 Current serial number in output stream:  399
+ * The root cause is unknown. */
+
+#define CHECK_VASTATUS(va_status,func)				\
+	if (va_status != VA_STATUS_SUCCESS) {			\
+		fprintf(stderr,"%s:%s (%d) failed,exit\n",	\
+			__func__,				\
+			func,					\
+			__LINE__);				\
+		exit(1);					\
+	}
+
+static void *open_display(void);
+static void close_display(void *win_display);
+static int create_window(void *win_display,
+             int x, int y, int width, int height);
+static int check_window_event(void *win_display, void *drawable,
+                  int *width, int *height, int *quit);
+
+struct display;
+struct drawable;
+
+static VAStatus
+va_put_surface(
+    VADisplay           dpy,
+    struct drawable    *wl_drawable,
+    VASurfaceID         va_surface,
+    const VARectangle  *src_rect,
+    const VARectangle  *dst_rect,
+    const VARectangle  *cliprects,
+    unsigned int        num_cliprects,
+    unsigned int        flags
+);
+
+/* Glue code for the current PutSurface test design */
+#define CAST_DRAWABLE(a)  (struct drawable *)(a)
+
+static inline VADisplay
+vaGetDisplay(VANativeDisplay native_dpy)
+{
+    return vaGetDisplayWl(native_dpy);
+}
+
+static VAStatus
+vaPutSurface(
+    VADisplay           dpy,
+    VASurfaceID         surface,
+    struct drawable    *wl_drawable,
+    short               src_x,
+    short               src_y,
+    unsigned short      src_w,
+    unsigned short      src_h,
+    short               dst_x,
+    short               dst_y,
+    unsigned short      dst_w,
+    unsigned short      dst_h,
+    const VARectangle  *cliprects,
+    unsigned int        num_cliprects,
+    unsigned int        flags
+)
+{
+    VARectangle src_rect, dst_rect;
+
+    src_rect.x      = src_x;
+    src_rect.y      = src_y;
+    src_rect.width  = src_w;
+    src_rect.height = src_h;
+
+    dst_rect.x      = src_x;
+    dst_rect.y      = src_y;
+    dst_rect.width  = src_w;
+    dst_rect.height = src_h;
+    return va_put_surface(dpy, wl_drawable, surface, &src_rect, &dst_rect,
+                          cliprects, num_cliprects, flags);
+}
+
+static const struct {
+	unsigned char y, u, v;
+} color_bars[7] = {
+	{ 180, 128, 128 },
+	{ 168, 44, 136 },
+	{ 145, 147, 44 },
+	{ 133, 63, 52 },
+	{ 63, 193, 204 },
+	{ 51, 109, 212 },
+	{ 28, 212, 120 }
+};
+
+static int
+yuvgen_planar(int width, int height,
+	      unsigned char *Y_start, int Y_pitch,
+	      unsigned char *U_start, int U_pitch,
+	      unsigned char *V_start, int V_pitch,
+	      unsigned int fourcc, int box_width, int row_shift, int field)
+{
+	int row, jj;
+	unsigned char u_value, v_value;
+	int y_factor, uv_factor = 1;
+
+	/* copy Y plane */
+	y_factor = 1;
+	if (fourcc == VA_FOURCC_YUY2)
+		y_factor = 2;
+
+	switch (fourcc) {
+	case VA_FOURCC_NV12:
+		uv_factor = 2;
+		break;
+	case VA_FOURCC_YV12:
+		uv_factor = 1;
+		break;
+	case VA_FOURCC_YUY2:
+		uv_factor = 4;
+		break;
+	default:
+		printf("unsupported fourcc\n");
+		assert(0);
+	}
+
+	for (row = 0; row < height; row++) {
+		unsigned char *Y_row = Y_start + row * Y_pitch;
+		int xpos, ypos;
+
+		ypos = (row / box_width) & 0x1;
+
+		/* fill garbage data into the other field */
+		if (((field == VA_TOP_FIELD) && (row & 1))
+		    || ((field == VA_BOTTOM_FIELD) && ((row & 1) == 0))) {
+			memset(Y_row, 0xff, width);
+			continue;
+		}
+
+		for (jj = 0; jj < width; jj++) {
+			xpos = ((row_shift + jj) / box_width) & 0x1;
+			if (row < height / 2)
+				Y_row[jj * y_factor] =
+					color_bars[jj * 7 / width].y;
+			else if (xpos == ypos)
+				Y_row[jj * y_factor] = 0xeb;
+			else
+				Y_row[jj * y_factor] = 0x10;
+		}
+	}
+
+	/* copy UV data */
+	for (row = 0; row < height / 2; row++) {
+		unsigned char *U_row = U_start + row * U_pitch;
+		unsigned char *V_row = V_start + row * V_pitch;
+
+		for (jj = 0; jj < width / 2; jj++) {
+			/* fill garbage data into the other field */
+			if (((field == VA_TOP_FIELD) && (row & 1))
+			    || ((field == VA_BOTTOM_FIELD) &&
+				((row & 1) == 0))) {
+				u_value = 0xff;
+				v_value = 0xff;
+			} else if (row >= height / 4) {
+				u_value = 0x80;
+				v_value = 0x80;
+			} else {
+				u_value = color_bars[jj * 7 * 2 / width].u;
+				v_value = color_bars[jj * 7 * 2 / width].v;
+			}
+
+			U_row[jj * uv_factor] = u_value;
+			V_row[jj * uv_factor] = v_value;
+		}
+	}
+
+	return 0;
+}
+
+static int
+upload_surface(VADisplay va_dpy, VASurfaceID surface_id,
+	       int box_width, int row_shift, int field)
+{
+	VAImage surface_image;
+	void *surface_p = NULL, *U_start = NULL, *V_start = NULL;
+	VAStatus va_status;
+	unsigned int pitches[3] = { 0, 0, 0 };
+
+	va_status = vaDeriveImage(va_dpy, surface_id, &surface_image);
+	CHECK_VASTATUS(va_status, "vaDeriveImage");
+
+	vaMapBuffer(va_dpy, surface_image.buf, &surface_p);
+	assert(VA_STATUS_SUCCESS == va_status);
+
+	pitches[0] = surface_image.pitches[0];
+	switch (surface_image.format.fourcc) {
+	case VA_FOURCC_NV12:
+		U_start = (char *)surface_p + surface_image.offsets[1];
+		V_start = (char *)U_start + 1;
+		pitches[1] = surface_image.pitches[1];
+		pitches[2] = surface_image.pitches[1];
+		break;
+	case VA_FOURCC_IYUV:
+		U_start = (char *)surface_p + surface_image.offsets[1];
+		V_start = (char *)surface_p + surface_image.offsets[2];
+		pitches[1] = surface_image.pitches[1];
+		pitches[2] = surface_image.pitches[2];
+		break;
+	case VA_FOURCC_YV12:
+		U_start = (char *)surface_p + surface_image.offsets[2];
+		V_start = (char *)surface_p + surface_image.offsets[1];
+		pitches[1] = surface_image.pitches[2];
+		pitches[2] = surface_image.pitches[1];
+		break;
+	case VA_FOURCC_YUY2:
+		U_start = (char *)surface_p + 1;
+		V_start = (char *)surface_p + 3;
+		pitches[1] = surface_image.pitches[0];
+		pitches[2] = surface_image.pitches[0];
+		break;
+	default:
+		assert(0);
+	}
+
+	/* assume surface is planar format */
+	yuvgen_planar(surface_image.width, surface_image.height,
+		      (unsigned char *)surface_p, pitches[0],
+		      (unsigned char *)U_start, pitches[1],
+		      (unsigned char *)V_start, pitches[2],
+		      surface_image.format.fourcc, box_width, row_shift, field);
+
+	vaUnmapBuffer(va_dpy, surface_image.buf);
+
+	vaDestroyImage(va_dpy, surface_image.image_id);
+
+	return 0;
+}
+
+#define SURFACE_NUM 16
+
+static void *win_display;
+static VADisplay va_dpy;
+static VAImageFormat *va_image_formats;
+static int va_num_image_formats = -1;
+static VAConfigID vpp_config_id = VA_INVALID_ID;
+static VASurfaceAttrib *va_surface_attribs;
+static int va_num_surface_attribs = -1;
+static VASurfaceID surface_id[SURFACE_NUM];
+static pthread_mutex_t surface_mutex[SURFACE_NUM];
+
+static void *drawable_thread0, *drawable_thread1;
+static int surface_width = 250, surface_height = 250;
+static int win_x = 0, win_y = 0;
+static int win_width = 250, win_height = 250;
+static int frame_rate = 0;
+static unsigned long long frame_num_total = ~0;
+static int check_event = 1;
+static int test_clip = 0;
+static int display_field = VA_FRAME_PICTURE;
+static pthread_mutex_t gmutex;
+static int box_width = 32;
+static int multi_thread = 0;
+static int verbose = 0;
+static int test_color_conversion = 0;
+static int csc_src_fourcc = 0, csc_dst_fourcc = 0;
+static VAImage csc_dst_fourcc_image;
+static VASurfaceID csc_render_surface;
+
+typedef struct {
+	char *fmt_str;
+	unsigned int fourcc;
+} fourcc_map;
+fourcc_map va_fourcc_map[] = {
+	{"YUYV", VA_FOURCC_YUY2},
+	{"YUY2", VA_FOURCC_YUY2},
+	{"NV12", VA_FOURCC_NV12},
+	{"YV12", VA_FOURCC_YV12},
+	{"BGRA", VA_FOURCC_BGRA},
+	{"RGBA", VA_FOURCC_RGBA},
+	{"BGRX", VA_FOURCC_BGRX},
+	{"RGBX", VA_FOURCC_RGBX},
+};
+
+static char *
+map_vafourcc_to_str(unsigned int format)
+{
+	static char unknown_format[] = "unknown-format";
+	size_t i;
+	for (i = 0; i < sizeof(va_fourcc_map) / sizeof(fourcc_map); i++) {
+		if (va_fourcc_map[i].fourcc == format) {
+			return va_fourcc_map[i].fmt_str;
+		}
+	}
+
+	return unknown_format;
+
+}
+
+static int
+va_value_equals(const VAGenericValue * v1, const VAGenericValue * v2)
+{
+	if (v1->type != v2->type)
+		return 0;
+
+	switch (v1->type) {
+	case VAGenericValueTypeInteger:
+		return v1->value.i == v2->value.i;
+	case VAGenericValueTypeFloat:
+		return v1->value.f == v2->value.f;
+	case VAGenericValueTypePointer:
+		return v1->value.p == v2->value.p;
+	case VAGenericValueTypeFunc:
+		return v1->value.fn == v2->value.fn;
+	}
+	return 0;
+}
+
+static int
+ensure_image_formats(void)
+{
+	VAStatus va_status;
+	VAImageFormat *image_formats;
+	int num_image_formats;
+
+	if (va_num_image_formats >= 0)
+		return va_num_image_formats;
+
+	num_image_formats = vaMaxNumImageFormats(va_dpy);
+	if (num_image_formats == 0)
+		return 0;
+
+	image_formats = malloc(num_image_formats * sizeof(*image_formats));
+	if (!image_formats)
+		return 0;
+
+	va_status =
+	    vaQueryImageFormats(va_dpy, image_formats, &num_image_formats);
+	CHECK_VASTATUS(va_status, "vaQuerySurfaceAttributes()");
+
+	va_image_formats = image_formats;
+	va_num_image_formats = num_image_formats;
+	return num_image_formats;
+}
+
+static const VAImageFormat *
+lookup_image_format(uint32_t fourcc)
+{
+	int i;
+
+	if (!ensure_image_formats())
+		return NULL;
+
+	for (i = 0; i < va_num_image_formats; i++) {
+		const VAImageFormat *const image_format = &va_image_formats[i];
+		if (image_format->fourcc == fourcc)
+			return image_format;
+	}
+	return NULL;
+}
+
+static int
+ensure_surface_attribs(void)
+{
+	VAStatus va_status;
+	VASurfaceAttrib *surface_attribs;
+	unsigned int num_image_formats, num_surface_attribs;
+
+	if (va_num_surface_attribs >= 0)
+		return va_num_surface_attribs;
+
+	num_image_formats = vaMaxNumImageFormats(va_dpy);
+	if (num_image_formats == 0)
+		return 0;
+
+	va_status = vaCreateConfig(va_dpy, VAProfileNone, VAEntrypointVideoProc,
+				   NULL, 0, &vpp_config_id);
+	CHECK_VASTATUS(va_status, "vaCreateConfig()");
+
+	/* Guess the number of surface attributes, thus including any
+	   pixel-format supported by the VA driver */
+	num_surface_attribs = VASurfaceAttribCount + num_image_formats;
+	surface_attribs =
+	    malloc(num_surface_attribs * sizeof(*surface_attribs));
+	if (!surface_attribs)
+		return 0;
+
+	va_status = vaQuerySurfaceAttributes(va_dpy, vpp_config_id,
+					     surface_attribs,
+					     &num_surface_attribs);
+	if (va_status == VA_STATUS_SUCCESS)
+		va_surface_attribs = surface_attribs;
+	else if (va_status == VA_STATUS_ERROR_MAX_NUM_EXCEEDED) {
+		va_surface_attribs = realloc(surface_attribs,
+					     num_surface_attribs *
+					     sizeof(*va_surface_attribs));
+		if (!va_surface_attribs) {
+			free(surface_attribs);
+			return 0;
+		}
+		va_status = vaQuerySurfaceAttributes(va_dpy, vpp_config_id,
+						     va_surface_attribs,
+						     &num_surface_attribs);
+	}
+	CHECK_VASTATUS(va_status, "vaQuerySurfaceAttributes()");
+	va_num_surface_attribs = num_surface_attribs;
+	return num_surface_attribs;
+}
+
+static const VASurfaceAttrib *
+lookup_surface_attrib(VASurfaceAttribType type, const VAGenericValue * value)
+{
+	int i;
+
+	if (!ensure_surface_attribs())
+		return NULL;
+
+	for (i = 0; i < va_num_surface_attribs; i++) {
+		const VASurfaceAttrib *const surface_attrib =
+		    &va_surface_attribs[i];
+		if (surface_attrib->type != type)
+			continue;
+		if (!(surface_attrib->flags & VA_SURFACE_ATTRIB_SETTABLE))
+			continue;
+		if (va_value_equals(&surface_attrib->value, value))
+			return surface_attrib;
+	}
+	return NULL;
+}
+
+static int
+csc_preparation(void)
+{
+	VAStatus va_status;
+
+	// 1. make sure dst fourcc is supported for vaImage
+	if (!lookup_image_format(csc_dst_fourcc)) {
+		test_color_conversion = 0;
+		printf
+		    ("VA driver doesn't support %s image, skip additional color conversion\n",
+		     map_vafourcc_to_str(csc_dst_fourcc));
+		goto cleanup;
+	}
+	// 2. make sure src_fourcc is supported for vaSurface
+	VASurfaceAttrib surface_attribs[1], *const s_attrib =
+	    &surface_attribs[0];
+	s_attrib->type = VASurfaceAttribPixelFormat;
+	s_attrib->flags = VA_SURFACE_ATTRIB_SETTABLE;
+	s_attrib->value.type = VAGenericValueTypeInteger;
+	s_attrib->value.value.i = csc_src_fourcc;
+
+	if (!lookup_surface_attrib
+	    (VASurfaceAttribPixelFormat, &s_attrib->value)) {
+		printf("VA driver doesn't support %s surface, "
+		       "skip additional color conversion\n",
+		       map_vafourcc_to_str(csc_src_fourcc));
+		test_color_conversion = 0;
+		goto cleanup;
+	}
+	// 3 create all objs required by csc
+	// 3.1 vaSurface with src fourcc
+	va_status = vaCreateSurfaces(va_dpy,
+				     VA_RT_FORMAT_YUV420, surface_width,
+				     surface_height, &surface_id[0],
+				     SURFACE_NUM, surface_attribs, 1);
+	CHECK_VASTATUS(va_status, "vaCreateSurfaces");
+
+	// 3.2 vaImage with dst fourcc
+	VAImageFormat image_format;
+	image_format.fourcc = csc_dst_fourcc;
+	image_format.byte_order = VA_LSB_FIRST;
+	image_format.bits_per_pixel = 16;
+
+	va_status = vaCreateImage(va_dpy, &image_format,
+				  surface_width, surface_height,
+				  &csc_dst_fourcc_image);
+	CHECK_VASTATUS(va_status, "vaCreateImage");
+
+	// 3.3 create a temp VASurface for final rendering(vaPutSurface)
+	s_attrib->value.value.i = VA_FOURCC_NV12;
+	va_status = vaCreateSurfaces(va_dpy, VA_RT_FORMAT_YUV420,
+				     surface_width, surface_height,
+				     &csc_render_surface, 1,
+				     surface_attribs, 1);
+	CHECK_VASTATUS(va_status, "vaCreateSurfaces");
+
+ cleanup:
+	return test_color_conversion;
+}
+
+static VASurfaceID
+get_next_free_surface(int *index)
+{
+	VASurfaceStatus surface_status;
+	int i;
+
+	assert(index);
+
+	if (multi_thread == 0) {
+		i = *index;
+		i++;
+		if (i == SURFACE_NUM)
+			i = 0;
+		*index = i;
+
+		return surface_id[i];
+	}
+
+	for (i = 0; i < SURFACE_NUM; i++) {
+		surface_status = (VASurfaceStatus) 0;
+		vaQuerySurfaceStatus(va_dpy, surface_id[i], &surface_status);
+		if (surface_status == VASurfaceReady) {
+			if (0 == pthread_mutex_trylock(&surface_mutex[i])) {
+				*index = i;
+				break;
+			}
+		}
+	}
+
+	if (i == SURFACE_NUM)
+		return VA_INVALID_SURFACE;
+	else
+		return surface_id[i];
+}
+
+static int
+upload_source_YUV_once_for_all(void)
+{
+	int box_width_loc = 8;
+	int row_shift_loc = 0;
+	int i;
+
+	for (i = 0; i < SURFACE_NUM; i++) {
+		printf("\rLoading data into surface %d.....", i);
+		upload_surface(va_dpy, surface_id[i], box_width_loc,
+			       row_shift_loc, 0);
+
+		row_shift_loc++;
+		if (row_shift_loc == (2 * box_width_loc))
+			row_shift_loc = 0;
+	}
+	printf("\n");
+
+	return 0;
+}
+
+/*
+ * Helper function for profiling purposes
+ */
+static unsigned long
+get_tick_count(void)
+{
+	struct timeval tv;
+	if (gettimeofday(&tv, NULL))
+		return 0;
+	return tv.tv_usec / 1000 + tv.tv_sec * 1000;
+}
+
+static void
+update_clipbox(VARectangle * cliprects, int width, int height)
+{
+	if (test_clip == 0)
+		return;
+
+	srand((unsigned)time(NULL));
+
+	cliprects[0].x = (rand() % width);
+	cliprects[0].y = (rand() % height);
+	cliprects[0].width = (rand() % (width - cliprects[0].x));
+	cliprects[0].height = (rand() % (height - cliprects[0].y));
+
+	cliprects[1].x = (rand() % width);
+	cliprects[1].y = (rand() % height);
+	cliprects[1].width = (rand() % (width - cliprects[1].x));
+	cliprects[1].height = (rand() % (height - cliprects[1].y));
+	printf("\nTest clip (%d,%d, %d x %d) and (%d,%d, %d x %d) \n",
+	       cliprects[0].x, cliprects[0].y, cliprects[0].width,
+	       cliprects[0].height, cliprects[1].x, cliprects[1].y,
+	       cliprects[1].width, cliprects[1].height);
+}
+
+static void *
+putsurface_thread(void *data)
+{
+	int width = win_width, height = win_height;
+	void *drawable = data;
+	int quit = 0;
+	VAStatus vaStatus;
+	int row_shift = 0;
+	int index = 0;
+	unsigned int frame_num = 0, start_time, putsurface_time;
+	VARectangle cliprects[2];	/* client supplied clip list */
+	int continue_display = 0;
+
+	if (drawable == drawable_thread0)
+		printf("Enter into thread0\n\n");
+	if (drawable == drawable_thread1)
+		printf("Enter into thread1\n\n");
+
+	putsurface_time = 0;
+	while (!quit) {
+		VASurfaceID surface_id = VA_INVALID_SURFACE;
+
+		while (surface_id == VA_INVALID_SURFACE)
+			surface_id = get_next_free_surface(&index);
+
+		if (verbose)
+			printf("Thread: %p Display surface 0x%x,\n", drawable,
+			       surface_id);
+
+		if (multi_thread)
+			upload_surface(va_dpy, surface_id, box_width, row_shift,
+				       display_field);
+
+		if (check_event)
+			pthread_mutex_lock(&gmutex);
+
+		start_time = get_tick_count();
+		if ((continue_display == 0) && getenv("FRAME_STOP")) {
+			char c;
+			printf("Press any key to display frame %d..."
+			       "(c/C to continue)\n",
+			       frame_num);
+			c = getchar();
+			if (c == 'c' || c == 'C')
+				continue_display = 1;
+		}
+		if (test_color_conversion) {
+			static int _put_surface_count = 0;
+			if (_put_surface_count++ % 50 == 0) {
+				printf("do additional colorcoversion from "
+				       "%s to %s\n",
+				       map_vafourcc_to_str(csc_src_fourcc),
+				       map_vafourcc_to_str(csc_dst_fourcc));
+			}
+			// get image from surface, csc_src_fourcc to
+			// csc_dst_fourcc conversion happens
+			vaStatus = vaGetImage(va_dpy, surface_id, 0, 0,
+					      surface_width, surface_height,
+					      csc_dst_fourcc_image.image_id);
+			CHECK_VASTATUS(vaStatus, "vaGetImage");
+
+			// render csc_dst_fourcc image to temp surface
+			vaStatus =
+			    vaPutImage(va_dpy, csc_render_surface,
+				       csc_dst_fourcc_image.image_id, 0, 0,
+				       surface_width, surface_height, 0, 0,
+				       surface_width, surface_height);
+			CHECK_VASTATUS(vaStatus, "vaPutImage");
+
+			// render the temp surface, it should be same
+			// with original surface without color
+			// conversion test
+			vaStatus =
+			    vaPutSurface(va_dpy, csc_render_surface,
+					 CAST_DRAWABLE(drawable), 0, 0,
+					 surface_width, surface_height, 0, 0,
+					 width, height,
+					 (test_clip ==
+					  0) ? NULL : &cliprects[0],
+					 (test_clip == 0) ? 0 : 2,
+					 display_field);
+			CHECK_VASTATUS(vaStatus, "vaPutSurface");
+
+		} else {
+			vaStatus =
+			    vaPutSurface(va_dpy, surface_id,
+					 CAST_DRAWABLE(drawable), 0, 0,
+					 surface_width, surface_height, 0, 0,
+					 width, height,
+					 (test_clip ==
+					  0) ? NULL : &cliprects[0],
+					 (test_clip == 0) ? 0 : 2,
+					 display_field);
+			CHECK_VASTATUS(vaStatus, "vaPutSurface");
+		}
+
+		putsurface_time += (get_tick_count() - start_time);
+
+		if (check_event)
+			pthread_mutex_unlock(&gmutex);
+
+		/* locked in get_next_free_surface */
+		pthread_mutex_unlock(&surface_mutex[index]);
+
+		if ((frame_num % 0xff) == 0) {
+			fprintf(stderr, "%.2f FPS                 \r",
+				256000.0 / (float)putsurface_time);
+			putsurface_time = 0;
+			update_clipbox(cliprects, width, height);
+		}
+
+		if (check_event)
+			check_window_event(win_display, drawable, &width,
+					   &height, &quit);
+
+		if (multi_thread) {	/* reload surface content */
+			row_shift++;
+			if (row_shift == (2 * box_width))
+				row_shift = 0;
+		}
+
+		if (frame_rate != 0)	/* rough framerate control */
+			usleep(1000 / frame_rate * 1000);
+
+		frame_num++;
+		if (frame_num >= frame_num_total)
+			quit = 1;
+	}
+
+	if (drawable == drawable_thread1)
+		pthread_exit(NULL);
+
+	return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+	int major_ver, minor_ver;
+	VAStatus va_status;
+	pthread_t thread1;
+	int ret;
+	int i;
+
+	if (csc_src_fourcc && csc_dst_fourcc) {
+		test_color_conversion = 1;
+	}
+
+	win_display = (void *)open_display();
+	if (win_display == NULL) {
+		fprintf(stderr, "Can't open the connection of display!\n");
+		exit(-1);
+	}
+	create_window(win_display, win_x, win_y, win_width, win_height);
+
+	va_dpy = vaGetDisplay(win_display);
+	va_status = vaInitialize(va_dpy, &major_ver, &minor_ver);
+	CHECK_VASTATUS(va_status, "vaInitialize");
+
+	if (test_color_conversion) {
+		ret = csc_preparation();
+	}
+	if (!test_color_conversion || !ret) {
+		va_status = vaCreateSurfaces(va_dpy,
+					     VA_RT_FORMAT_YUV420, surface_width,
+					     surface_height, &surface_id[0],
+					     SURFACE_NUM, NULL, 0);
+	}
+	CHECK_VASTATUS(va_status, "vaCreateSurfaces");
+	if (multi_thread == 0)	/* upload the content for all surfaces */
+		upload_source_YUV_once_for_all();
+
+	if (check_event)
+		pthread_mutex_init(&gmutex, NULL);
+
+	for (i = 0; i < SURFACE_NUM; i++)
+		pthread_mutex_init(&surface_mutex[i], NULL);
+
+	if (multi_thread == 1)
+		ret =
+		    pthread_create(&thread1, NULL, putsurface_thread,
+				   (void *)drawable_thread1);
+
+	putsurface_thread((void *)drawable_thread0);
+
+	if (multi_thread == 1)
+		pthread_join(thread1, (void **)&ret);
+	printf("thread1 is free\n");
+
+	if (test_color_conversion) {
+		// destroy temp surface/image
+		va_status = vaDestroySurfaces(va_dpy, &csc_render_surface, 1);
+		CHECK_VASTATUS(va_status, "vaDestroySurfaces");
+
+		va_status =
+		    vaDestroyImage(va_dpy, csc_dst_fourcc_image.image_id);
+		CHECK_VASTATUS(va_status, "vaDestroyImage");
+	}
+
+	if (vpp_config_id != VA_INVALID_ID) {
+		vaDestroyConfig(va_dpy, vpp_config_id);
+		vpp_config_id = VA_INVALID_ID;
+	}
+
+	vaDestroySurfaces(va_dpy, &surface_id[0], SURFACE_NUM);
+	vaTerminate(va_dpy);
+
+	free(va_image_formats);
+	free(va_surface_attribs);
+	close_display(win_display);
+
+	return 0;
+}
+
+struct display {
+    struct wl_display  *display;
+    struct wl_compositor *compositor;
+    struct wl_registry *registry;
+    int                 event_fd;
+};
+
+struct drawable {
+    struct wl_display  *display;
+    struct wl_surface  *surface;
+    unsigned int        redraw_pending  : 1;
+};
+
+static void
+frame_redraw_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+    struct drawable * const drawable = data;
+
+    drawable->redraw_pending = 0;
+    wl_callback_destroy(callback);
+}
+
+static const struct wl_callback_listener frame_callback_listener = {
+    frame_redraw_callback
+};
+
+static VAStatus
+va_put_surface(
+    VADisplay           dpy,
+    struct drawable    *wl_drawable,
+    VASurfaceID         va_surface,
+    const VARectangle  *src_rect,
+    const VARectangle  *dst_rect,
+    const VARectangle  *cliprects,
+    unsigned int        num_cliprects,
+    unsigned int        flags
+)
+{
+    struct display *d;
+    struct wl_callback *callback;
+    VAStatus va_status;
+    struct wl_buffer *buffer;
+
+    if (!wl_drawable)
+        return VA_STATUS_ERROR_INVALID_SURFACE;
+
+    d = wl_display_get_user_data(wl_drawable->display);
+    if (!d)
+        return VA_STATUS_ERROR_INVALID_DISPLAY;
+
+    /* Wait for the previous frame to complete redraw */
+    if (wl_drawable->redraw_pending) {
+        wl_display_flush(d->display);
+        while (wl_drawable->redraw_pending)
+            wl_display_dispatch(wl_drawable->display);
+    }
+
+    va_status = vaGetSurfaceBufferWl(va_dpy, va_surface, VA_FRAME_PICTURE, &buffer);
+    if (va_status != VA_STATUS_SUCCESS)
+        return va_status;
+
+    wl_surface_attach(wl_drawable->surface, buffer, 0, 0);
+    wl_surface_damage(
+        wl_drawable->surface,
+        dst_rect->x, dst_rect->y, dst_rect->width, dst_rect->height
+    );
+
+    wl_display_flush(d->display);
+    wl_drawable->redraw_pending = 1;
+    callback = wl_surface_frame(wl_drawable->surface);
+    wl_surface_commit(wl_drawable->surface);
+    wl_callback_add_listener(callback, &frame_callback_listener, wl_drawable);
+    return VA_STATUS_SUCCESS;
+}
+
+static void
+registry_handle_global(
+    void               *data,
+    struct wl_registry *registry,
+    uint32_t            id,
+    const char         *interface,
+    uint32_t            version
+)
+{
+    struct display * const d = data;
+
+    if (strcmp(interface, "wl_compositor") == 0)
+        d->compositor =
+            wl_registry_bind(registry, id, &wl_compositor_interface, 1);
+}
+
+static const struct wl_registry_listener registry_listener = {
+    registry_handle_global,
+    NULL,
+};
+
+static void *
+open_display(void)
+{
+    struct display *d;
+
+    d = calloc(1, sizeof *d);
+    if (!d)
+        return NULL;
+
+    d->display = wl_display_connect(NULL);
+    if (!d->display)
+        return NULL;
+
+    wl_display_set_user_data(d->display, d);
+    d->registry = wl_display_get_registry(d->display);
+    wl_registry_add_listener(d->registry, &registry_listener, d);
+    d->event_fd = wl_display_get_fd(d->display);
+    wl_display_dispatch(d->display);
+    return d->display;
+}
+
+static void
+close_display(void *win_display)
+{
+    struct display * const d = wl_display_get_user_data(win_display);
+
+    if (d->compositor) {
+        wl_compositor_destroy(d->compositor);
+        d->compositor = NULL;
+    }
+
+    if (d->display) {
+        wl_display_disconnect(d->display);
+        d->display = NULL;
+    }
+    free(d);
+}
+
+static int
+create_window(void *win_display, int x, int y, int width, int height)
+{
+    struct wl_display * const display = win_display;
+    struct display * const d = wl_display_get_user_data(display);
+    struct wl_surface *surface1, *surface2;
+    struct drawable *drawable1, *drawable2;
+
+    surface1 = wl_compositor_create_surface(d->compositor);
+
+    drawable1 = malloc(sizeof(*drawable1));
+    drawable1->display          = display;
+    drawable1->surface          = surface1;
+    drawable1->redraw_pending   = 0;
+
+    /* global out */
+    drawable_thread0 = drawable1;
+
+    if (multi_thread == 0)
+        return 0;
+
+    surface2 = wl_compositor_create_surface(d->compositor);
+
+    drawable2 = malloc(sizeof(*drawable2));
+    drawable2->display          = display;
+    drawable1->surface          = surface2;
+    drawable2->redraw_pending   = 0;
+
+    /* global out */
+    drawable_thread1 = drawable2;
+    return 0;
+}
+
+static int
+check_window_event(
+    void *win_display,
+    void *drawable,
+    int  *width,
+    int  *height,
+    int  *quit
+)
+{
+    struct wl_display * const display = win_display;
+    struct display * const d = wl_display_get_user_data(display);
+    struct timeval tv;
+    fd_set rfds;
+    int retval;
+
+    if (check_event == 0)
+        return 0;
+
+    tv.tv_sec  = 0;
+    tv.tv_usec = 0;
+    do {
+        FD_ZERO(&rfds);
+        FD_SET(d->event_fd, &rfds);
+
+        retval = select(d->event_fd + 1, &rfds, NULL, NULL, &tv);
+        if (retval < 0) {
+            perror("select");
+            break;
+        }
+        if (retval == 1) {
+		if (wl_display_dispatch(d->display) == -1)
+			break;
+	}
+    } while (retval > 0);
+
+#if 0
+    /* bail on any focused key press */
+    if(event.type == KeyPress) {  
+        *quit = 1;
+        return 0;
+    }
+#endif
+
+#if 0
+    /* rescale the video to fit the window */
+    if(event.type == ConfigureNotify) { 
+        *width = event.xconfigure.width;
+        *height = event.xconfigure.height;
+        printf("Scale window to %dx%d\n", width, height);
+    }
+#endif
+    return 0;
+}
+
diff --git a/clients/nested.c b/clients/nested.c
index d75e953..1d754d0 100644
--- a/clients/nested.c
+++ b/clients/nested.c
@@ -58,6 +58,7 @@ typedef struct wl_buffer * (EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL) (
 #endif
 
 static int option_blit;
+static int option_yuv;
 
 struct nested {
 	struct display *display;
@@ -105,9 +106,12 @@ struct nested_buffer {
 struct nested_surface {
 	struct wl_resource *resource;
 	struct nested *nested;
-	EGLImageKHR *image;
+	EGLImageKHR images[3];
 	struct wl_list link;
 
+	EGLint format;
+	int n_planes;
+
 	struct wl_list frame_callback_list;
 
 	struct {
@@ -156,6 +160,7 @@ struct nested_renderer {
 
 static const struct weston_option nested_options[] = {
 	{ WESTON_OPTION_BOOLEAN, "blit", 'b', &option_blit },
+	{ WESTON_OPTION_BOOLEAN, "yuv", 'y', &option_yuv }
 };
 
 static const struct nested_renderer nested_blit_renderer;
@@ -430,19 +435,25 @@ surface_attach(struct wl_client *client,
 	struct nested_buffer *buffer = NULL;
 
 	if (buffer_resource) {
-		int format;
-
 		if (!query_buffer(nested->egl_display, (void *) buffer_resource,
-				  EGL_TEXTURE_FORMAT, &format)) {
+				  EGL_TEXTURE_FORMAT, &surface->format)) {
 			wl_resource_post_error(buffer_resource,
 					       WL_DISPLAY_ERROR_INVALID_OBJECT,
 					       "attaching non-egl wl_buffer");
 			return;
 		}
 
-		switch (format) {
+		switch (surface->format) {
 		case EGL_TEXTURE_RGB:
 		case EGL_TEXTURE_RGBA:
+			surface->n_planes = 1;
+			break;
+		case EGL_TEXTURE_Y_U_V_WL:
+			surface->n_planes = 3;
+			break;
+		case EGL_TEXTURE_Y_UV_WL:
+		case EGL_TEXTURE_Y_XUXV_WL:
+			surface->n_planes = 2;
 			break;
 		default:
 			wl_resource_post_error(buffer_resource,
@@ -474,16 +485,24 @@ nested_surface_attach(struct nested_surface *surface,
 		      struct nested_buffer *buffer)
 {
 	struct nested *nested = surface->nested;
-
-	if (surface->image != EGL_NO_IMAGE_KHR)
-		destroy_image(nested->egl_display, surface->image);
-
-	surface->image = create_image(nested->egl_display, NULL,
-				      EGL_WAYLAND_BUFFER_WL, buffer->resource,
-				      NULL);
-	if (surface->image == EGL_NO_IMAGE_KHR) {
-		fprintf(stderr, "failed to create img\n");
-		return;
+	EGLint attribs[] = { EGL_WAYLAND_PLANE_WL, 0, EGL_NONE };
+	int i;
+
+	for (i = 0; i < 3; i++)
+		if (surface->images[i] != EGL_NO_IMAGE_KHR)
+			destroy_image(nested->egl_display, surface->images[i]);
+
+	for (i = 0; i < surface->n_planes; i++) {
+		attribs[1] = i;
+		surface->images[i] =
+			create_image(nested->egl_display, NULL,
+				     EGL_WAYLAND_BUFFER_WL,
+				     buffer->resource,
+				     attribs);
+		if (surface->images[i] == EGL_NO_IMAGE_KHR) {
+			fprintf(stderr, "failed to create img\n");
+			return;
+		}
 	}
 
 	nested->renderer->surface_attach(surface, buffer);
@@ -902,7 +921,7 @@ blit_render_clients(struct nested *nested,
 					       nested->window, NULL);
 
 		glBindTexture(GL_TEXTURE_2D, blit_surface->texture);
-		image_target_texture_2d(GL_TEXTURE_2D, s->image);
+		image_target_texture_2d(GL_TEXTURE_2D, s->images[0]);
 
 		display_release_window_surface(nested->display,
 					       nested->window);
@@ -1045,30 +1064,42 @@ static const struct wl_callback_listener ss_frame_listener = {
 	ss_frame_callback
 };
 
+static struct wl_buffer *
+get_parent_buffer(struct nested_surface *surface,
+		  struct nested_buffer *buffer)
+{
+	struct nested *nested = surface->nested;
+	EGLDisplay *edpy = nested->egl_display;
+
+	/* Create a representation of the buffer in the parent
+	 * compositor if we haven't already */
+	if (buffer->parent_buffer)
+		return buffer->parent_buffer;
+
+	buffer->parent_buffer =
+		create_wayland_buffer_from_image(edpy,
+						 surface->format,
+						 surface->images,
+						 surface->n_planes);
+
+	wl_buffer_add_listener(buffer->parent_buffer,
+			       &ss_buffer_listener,
+			       buffer);
+
+	return buffer->parent_buffer;
+}
+
 static void
 ss_surface_attach(struct nested_surface *surface,
 		  struct nested_buffer *buffer)
 {
-	struct nested *nested = surface->nested;
 	struct nested_ss_surface *ss_surface = surface->renderer_data;
 	struct wl_buffer *parent_buffer;
 	const pixman_box32_t *rects;
 	int n_rects, i;
 
 	if (buffer) {
-		/* Create a representation of the buffer in the parent
-		 * compositor if we haven't already */
-		if (buffer->parent_buffer == NULL) {
-			EGLDisplay *edpy = nested->egl_display;
-			EGLImageKHR image = surface->image;
-
-			buffer->parent_buffer =
-				create_wayland_buffer_from_image(edpy, image);
-
-			wl_buffer_add_listener(buffer->parent_buffer,
-					       &ss_buffer_listener,
-					       buffer);
-		}
+		parent_buffer = get_parent_buffer(surface, buffer);
 
 		parent_buffer = buffer->parent_buffer;
 
@@ -1129,7 +1160,10 @@ main(int argc, char *argv[])
 
 	nested = nested_create(display);
 
-	launch_client(nested, "weston-nested-client");
+	launch_client(nested,
+		      option_yuv ?
+		      "weston-nested-client-yuv" :
+		      "weston-nested-client");
 
 	display_run(display);
 
diff --git a/configure.ac b/configure.ac
index 00d7123..63b59a2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -250,19 +250,28 @@ PKG_CHECK_MODULES(WEBP, [libwebp], [have_webp=yes], [have_webp=no])
 AS_IF([test "x$have_webp" = "xyes"],
       [AC_DEFINE([HAVE_WEBP], [1], [Have webp])])
 
+PKG_CHECK_MODULES(LIBVA, [libva >= 0.34.0 libva-drm >= 0.34.0],
+                  [have_libva=yes], [have_libva=no])
+
+have_libva_wayland=no
+AS_IF([test "x$have_libva" = "xyes"],
+      [PKG_CHECK_MODULES(LIBVA_WAYLAND,
+                         [libva >= 0.34.0 libva-drm >= 0.34.0
+                          libva-wayland >= 0.34.0],
+                         [have_libva_wayland=yes], [have_libva_wayland=no])])
+AM_CONDITIONAL(HAVE_LIBVA_WAYLAND, test "x$have_libva_wayland" = xyes)
+
 AC_ARG_ENABLE(vaapi-recorder, [  --enable-vaapi-recorder],,
 	      enable_vaapi_recorder=auto)
 if test x$enable_vaapi_recorder != xno; then
-  PKG_CHECK_MODULES(LIBVA, [libva >= 0.34.0 libva-drm >= 0.34.0],
-                    [have_libva=yes], [have_libva=no])
   if test "x$have_libva" = "xno" -a "x$enable_vaapi_recorder" = "xyes"; then
     AC_MSG_ERROR([vaapi-recorder explicitly enabled, but libva couldn't be found])
   fi
-  AS_IF([test "x$have_libva" = "xyes"],
+  enable_vaapi_recorder="$have_libva"
+  AS_IF([test "x$enable_vaapi_recorder" = "xyes"],
         [AC_DEFINE([BUILD_VAAPI_RECORDER], [1], [Build the vaapi recorder])])
 fi
-AM_CONDITIONAL(ENABLE_VAAPI_RECORDER, test "x$have_libva" = xyes)
-
+AM_CONDITIONAL(ENABLE_VAAPI_RECORDER, test "x$enable_vaapi_recorder" = xyes)
 
 AC_CHECK_LIB([jpeg], [jpeg_CreateDecompress], have_jpeglib=yes)
 if test x$have_jpeglib = xyes; then
@@ -520,5 +529,5 @@ AC_MSG_RESULT([
 	LCMS2 Support			${have_lcms}
 	libwebp Support			${have_webp}
 	libunwind Support		${have_libunwind}
-	VA H.264 encoding Support	${have_libva}
+	VA H.264 encoding Support	${enable_vaapi_recorder}
 ])
-- 
1.8.5.3



More information about the wayland-devel mailing list