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

Neil Roberts neil at linux.intel.com
Tue Feb 25 06:19:26 PST 2014


Here is a second version of the patch to match with the changes in v2
of the extension.

Regards,
- Neil

------- >8 --------------- (use git am --scissors to automatically chop here)

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.

In the subcompositor when it detects that the client has passed a
planar buffer it will use eglCreateWaylandBufferFromPlanarImagesWL
instead of the original function. The function pointer prototype in
eglext.h for this is not used so that Weston can still be compiled
with the old version of the extension. In that case weston-nested will
crash if you pass the -y option, but it will otherwise work.
---
 .gitignore                  |    1 +
 Makefile.am                 |   11 +
 clients/nested-client-yuv.c | 1035 +++++++++++++++++++++++++++++++++++++++++++
 clients/nested.c            |  125 ++++--
 configure.ac                |   21 +-
 5 files changed, 1151 insertions(+), 42 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 64d0743..788bcdf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -478,6 +478,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..c37b999
--- /dev/null
+++ b/clients/nested-client-yuv.c
@@ -0,0 +1,1035 @@
+/*
+ * 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>
+
+#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 3bdb961..c207c2a 100644
--- a/clients/nested.c
+++ b/clients/nested.c
@@ -47,7 +47,7 @@
 
 #define MIN(x,y) (((x) < (y)) ? (x) : (y))
 
-#ifndef EGL_WL_create_wayland_buffer_from_image
+#ifndef EGL_WL_createl_wayland_buffer_from_image
 #define EGL_WL_create_wayland_buffer_from_image 1
 
 #ifdef EGL_EGLEXT_PROTOTYPES
@@ -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;
@@ -167,7 +172,16 @@ static PFNEGLDESTROYIMAGEKHRPROC destroy_image;
 static PFNEGLBINDWAYLANDDISPLAYWL bind_display;
 static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
 static PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
-static PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL create_wayland_buffer_from_image;
+static PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL create_buffer_from_image;
+
+/* This doesn't use the PFN* typedef from the EGL headers because that
+ * is only available in version 3 of the extension and we want to
+ * build on version 2 as well */
+static struct wl_buffer *
+(EGLAPIENTRYP create_buffer_from_planar_images)(EGLDisplay dpy,
+						EGLenum format,
+						EGLImageKHR *images,
+						EGLint n_images);
 
 static void
 nested_buffer_destroy_handler(struct wl_listener *listener, void *data)
@@ -430,19 +444,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 +494,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);
@@ -779,12 +807,17 @@ nested_init_compositor(struct nested *nested)
 	}
 
 	if (display_has_subcompositor(nested->display)) {
-		const char *func = "eglCreateWaylandBufferFromImageWL";
+		const char *from_image =
+			"eglCreateWaylandBufferFromImageWL";
+		const char *from_planar =
+			"eglCreateWaylandBufferFromPlanarImagesWL";
 		const char *ext = "EGL_WL_create_wayland_buffer_from_image";
 
 		if (strstr(extensions, ext)) {
-			create_wayland_buffer_from_image =
-				(void *) eglGetProcAddress(func);
+			create_buffer_from_image =
+				(void *) eglGetProcAddress(from_image);
+			create_buffer_from_planar_images =
+				(void *) eglGetProcAddress(from_planar);
 			use_ss_renderer = 1;
 		}
 	}
@@ -902,7 +935,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);
@@ -1047,30 +1080,47 @@ 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;
+
+	if (surface->n_planes > 1) {
+		buffer->parent_buffer =
+			create_buffer_from_planar_images(edpy,
+							 surface->format,
+							 surface->images,
+							 surface->n_planes);
+	} else {
+		buffer->parent_buffer =
+			create_buffer_from_image(edpy, surface->images[0]);
+	}
+
+	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;
 
@@ -1131,7 +1181,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