[3/3] rdp-backend.so: OpenGL hardware acceleration

Submitted by Olivier Blin on July 6, 2017, 10:06 a.m.

Details

Message ID 20170706100615.24264-4-olivier.blin@softathome.com
State New
Headers show
Series "Remote display with 3D acceleration using Wayland/Weston" ( rev: 4 ) in Wayland

Not browsing as part of any series.

Commit Message

Olivier Blin July 6, 2017, 10:06 a.m.
From: DRC <information@virtualgl.org>

---
 compositor/main.c          |  23 +++-
 configure.ac               |   4 +-
 libweston/compositor-rdp.c | 314 +++++++++++++++++++++++++++++++++++++++++++--
 libweston/compositor-rdp.h |  24 ++++
 4 files changed, 352 insertions(+), 13 deletions(-)

Patch hide | download patch | download mbox

diff --git a/compositor/main.c b/compositor/main.c
index f8a60e97..125cc0f8 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -601,6 +601,7 @@  usage(int error_code)
 		"  --rdp4-key=FILE\tThe file containing the key for RDP4 encryption\n"
 		"  --rdp-tls-cert=FILE\tThe file containing the certificate for TLS encryption\n"
 		"  --rdp-tls-key=FILE\tThe file containing the private key for TLS encryption\n"
+		"  --use-pixman\t\tUse the pixman (CPU) renderer\n"
 		"\n");
 #endif
 
@@ -1331,11 +1332,14 @@  static void
 rdp_backend_output_configure(struct wl_listener *listener, void *data)
 {
 	struct weston_output *output = data;
+	struct weston_config *wc = wet_get_config(output->compositor);
 	struct wet_compositor *compositor = to_wet_compositor(output->compositor);
 	struct wet_output_config *parsed_options = compositor->parsed_options;
+	struct weston_config_section *section;
 	const struct weston_rdp_output_api *api = weston_rdp_output_get_api(output->compositor);
 	int width = 640;
 	int height = 480;
+	char *gbm_format = NULL;
 
 	assert(parsed_options);
 
@@ -1344,6 +1348,8 @@  rdp_backend_output_configure(struct wl_listener *listener, void *data)
 		return;
 	}
 
+	section = weston_config_get_section(wc, "output", "name", output->name);
+
 	if (parsed_options->width)
 		width = parsed_options->width;
 
@@ -1353,6 +1359,12 @@  rdp_backend_output_configure(struct wl_listener *listener, void *data)
 	weston_output_set_scale(output, 1);
 	weston_output_set_transform(output, WL_OUTPUT_TRANSFORM_NORMAL);
 
+	weston_config_section_get_string(section,
+					 "gbm-format", &gbm_format, NULL);
+
+	api->set_gbm_format(output, gbm_format);
+	free(gbm_format);
+
 	if (api->output_set_size(output, width, height) < 0) {
 		weston_log("Cannot configure output \"%s\" using weston_rdp_output_api.\n",
 			   output->name);
@@ -1375,6 +1387,7 @@  weston_rdp_backend_config_init(struct weston_rdp_backend_config *config)
 	config->server_key = NULL;
 	config->env_socket = 0;
 	config->no_clients_resize = 0;
+	config->use_pixman = false;
 }
 
 static int
@@ -1382,6 +1395,7 @@  load_rdp_backend(struct weston_compositor *c,
 		int *argc, char *argv[], struct weston_config *wc)
 {
 	struct weston_rdp_backend_config config  = {{ 0, }};
+	struct weston_config_section *section;
 	int ret = 0;
 
 	struct wet_output_config *parsed_options = wet_init_parsed_options(c);
@@ -1399,11 +1413,17 @@  load_rdp_backend(struct weston_compositor *c,
 		{ WESTON_OPTION_BOOLEAN, "no-clients-resize", 0, &config.no_clients_resize },
 		{ WESTON_OPTION_STRING,  "rdp4-key", 0, &config.rdp_key },
 		{ WESTON_OPTION_STRING,  "rdp-tls-cert", 0, &config.server_cert },
-		{ WESTON_OPTION_STRING,  "rdp-tls-key", 0, &config.server_key }
+		{ WESTON_OPTION_STRING,  "rdp-tls-key", 0, &config.server_key },
+		{ WESTON_OPTION_BOOLEAN, "use-pixman", 0, &config.use_pixman }
 	};
 
 	parse_options(rdp_options, ARRAY_LENGTH(rdp_options), argc, argv);
 
+	section = weston_config_get_section(wc, "core", NULL, NULL);
+	weston_config_section_get_string(section,
+					 "gbm-format", &config.gbm_format,
+					 NULL);
+
 	ret = weston_compositor_load_backend(c, WESTON_BACKEND_RDP,
 					     &config.base);
 
@@ -1417,6 +1437,7 @@  out:
 	free(config.rdp_key);
 	free(config.server_cert);
 	free(config.server_key);
+	free(config.gbm_format);
 
 	return ret;
 }
diff --git a/configure.ac b/configure.ac
index 53faee34..a7b2d517 100644
--- a/configure.ac
+++ b/configure.ac
@@ -250,9 +250,9 @@  AM_CONDITIONAL([ENABLE_RDP_COMPOSITOR],
                [test x$enable_rdp_compositor = xyes])
 if test x$enable_rdp_compositor = xyes; then
   AC_DEFINE([BUILD_RDP_COMPOSITOR], [1], [Build the RDP compositor])
-  PKG_CHECK_MODULES(RDP_COMPOSITOR, [freerdp2 >= 2.0.0],
+  PKG_CHECK_MODULES(RDP_COMPOSITOR, [freerdp2 >= 2.0.0 libudev >= 136 gbm],
     [],
-    [PKG_CHECK_MODULES(RDP_COMPOSITOR, [freerdp >= 1.1.0],[])]
+    [PKG_CHECK_MODULES(RDP_COMPOSITOR, [freerdp >= 1.1.0 libudev >= 136 gbm],[])]
   )
 
   SAVED_CPPFLAGS="$CPPFLAGS"
diff --git a/libweston/compositor-rdp.c b/libweston/compositor-rdp.c
index 7b1ab06d..b8c390d0 100644
--- a/libweston/compositor-rdp.c
+++ b/libweston/compositor-rdp.c
@@ -30,8 +30,14 @@ 
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <unistd.h>
 #include <linux/input.h>
 
+#include <gbm.h>
+#include <libudev.h>
+
 #if HAVE_FREERDP_VERSION_H
 #include <freerdp/version.h>
 #else
@@ -79,6 +85,7 @@ 
 #include "shared/helpers.h"
 #include "compositor.h"
 #include "compositor-rdp.h"
+#include "gl-renderer.h"
 #include "pixman-renderer.h"
 
 #define MAX_FREERDP_FDS 32
@@ -110,6 +117,13 @@  struct rdp_backend {
 	char *rdp_key;
 	int tls_enabled;
 	int no_clients_resize;
+
+	/* For GL rendering */
+	struct udev *udev;
+	int drm_fd;
+	struct gbm_device *gbm;
+	uint32_t gbm_format;
+	int use_pixman;
 };
 
 enum peer_item_flags {
@@ -128,6 +142,8 @@  struct rdp_peers_item {
 struct rdp_output {
 	struct weston_output base;
 	struct wl_event_source *finish_frame_timer;
+	uint32_t gbm_format;
+	struct gbm_surface *gbm_surface;
 	pixman_image_t *shadow_surface;
 
 	struct wl_list peers;
@@ -160,6 +176,8 @@  to_rdp_backend(struct weston_compositor *base)
 	return container_of(base->backend, struct rdp_backend, base);
 }
 
+static struct gl_renderer_interface *gl_renderer;
+
 static void
 rdp_peer_refresh_rfx(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer)
 {
@@ -364,12 +382,24 @@  rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage,
 {
 	struct rdp_output *output = container_of(output_base, struct rdp_output, base);
 	struct weston_compositor *ec = output->base.compositor;
+	struct rdp_backend *b = to_rdp_backend(ec);
 	struct rdp_peers_item *outputPeer;
 
-	pixman_renderer_output_set_buffer(output_base, output->shadow_surface);
+	if (b->use_pixman)
+		pixman_renderer_output_set_buffer(output_base, output->shadow_surface);
 	ec->renderer->repaint_output(&output->base, damage);
 
 	if (pixman_region32_not_empty(damage)) {
+		if (!b->use_pixman) {
+			/* TODO: Performance: Only read pixels that was actually repained by renderer->repaint_output. */
+			ec->renderer->read_pixels(output_base,
+					pixman_image_get_format(output->shadow_surface),
+					pixman_image_get_data(output->shadow_surface),
+					0, 0,
+					pixman_image_get_width(output->shadow_surface),
+					pixman_image_get_height(output->shadow_surface));
+		}
+
 		wl_list_for_each(outputPeer, &output->peers, link) {
 			if ((outputPeer->flags & RDP_PEER_ACTIVATED) &&
 					(outputPeer->flags & RDP_PEER_OUTPUT_ENABLED))
@@ -426,9 +456,203 @@  ensure_matching_mode(struct weston_output *output, struct weston_mode *target)
 }
 
 static int
+open_drm_device(struct udev *udev)
+{
+	struct udev_enumerate *e;
+	struct udev_list_entry *entry;
+	struct udev_device *device;
+	const char *path, *filename;
+	int fd;
+
+	e = udev_enumerate_new(udev);
+	udev_enumerate_add_match_subsystem(e, "drm");
+	udev_enumerate_add_match_sysname(e, "renderD[0-9]*");
+
+	udev_enumerate_scan_devices(e);
+	device = NULL;
+	udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
+		path = udev_list_entry_get_name(entry);
+		device = udev_device_new_from_syspath(udev, path);
+		if (device) {
+			filename = udev_device_get_devnode(device);
+			fd = open(filename, O_RDWR | O_CLOEXEC);
+			if (fd < 0) {
+				udev_device_unref(device);
+				continue;
+			}
+
+			weston_log("using %s\n", filename);
+			udev_device_unref(device);
+			udev_enumerate_unref(e);
+			return fd;
+		}
+	}
+
+	udev_enumerate_unref(e);
+	return -1;
+}
+
+static struct gbm_device *
+create_gbm_device(int fd)
+{
+	struct gbm_device *gbm;
+
+	gl_renderer = weston_load_module("gl-renderer.so",
+					 "gl_renderer_interface");
+	if (!gl_renderer)
+		return NULL;
+
+	/* GBM will load a dri driver, but even though they need symbols from
+	 * libglapi, in some version of Mesa they are not linked to it. Since
+	 * only the gl-renderer module links to it, the call above won't make
+	 * these symbols globally available, and loading the DRI driver fails.
+	 * Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL. */
+	dlopen("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL);
+
+	gbm = gbm_create_device(fd);
+
+	return gbm;
+}
+
+/* When initializing EGL, if the preferred buffer format isn't available
+ * we may be able to substitute an ARGB format for an XRGB one.
+ *
+ * This returns 0 if substitution isn't possible, but 0 might be a
+ * legitimate format for other EGL platforms, so the caller is
+ * responsible for checking for 0 before calling gl_renderer->create().
+ *
+ * This works around https://bugs.freedesktop.org/show_bug.cgi?id=89689
+ * but it's entirely possible we'll see this again on other implementations.
+ */
+static int
+fallback_format_for(uint32_t format)
+{
+	switch (format) {
+	case GBM_FORMAT_XRGB8888:
+		return GBM_FORMAT_ARGB8888;
+	case GBM_FORMAT_XRGB2101010:
+		return GBM_FORMAT_ARGB2101010;
+	default:
+		return 0;
+	}
+}
+
+static int
+rdp_backend_create_gl_renderer(struct rdp_backend *b)
+{
+	EGLint format[3] = {
+		b->gbm_format,
+		fallback_format_for(b->gbm_format),
+		0,
+	};
+	int n_formats = 2;
+
+	if (format[1])
+		n_formats = 3;
+	if (gl_renderer->display_create(b->compositor,
+					EGL_PLATFORM_GBM_KHR,
+					(void *)b->gbm,
+					NULL,
+					gl_renderer->opaque_attribs,
+					format,
+					n_formats) < 0) {
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+init_egl(struct rdp_backend *b)
+{
+	b->gbm = create_gbm_device(b->drm_fd);
+
+	if (!b->gbm)
+		return -1;
+
+	if (rdp_backend_create_gl_renderer(b) < 0) {
+		gbm_device_destroy(b->gbm);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+init_pixman(struct rdp_backend *b)
+{
+	return pixman_renderer_init(b->compositor);
+}
+
+static int
+parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format)
+{
+	int ret = 0;
+
+	if (s == NULL)
+		*gbm_format = default_value;
+	else if (strcmp(s, "xrgb8888") == 0)
+		*gbm_format = GBM_FORMAT_XRGB8888;
+	else if (strcmp(s, "rgb565") == 0)
+		*gbm_format = GBM_FORMAT_RGB565;
+	else if (strcmp(s, "xrgb2101010") == 0)
+		*gbm_format = GBM_FORMAT_XRGB2101010;
+	else {
+		weston_log("fatal: unrecognized pixel format: %s\n", s);
+		ret = -1;
+	}
+
+	return ret;
+}
+/* Init output state that depends on gl or gbm */
+static int
+rdp_output_init_egl(struct rdp_output *output, struct rdp_backend *b)
+{
+	EGLint format[2] = {
+		output->gbm_format,
+		fallback_format_for(output->gbm_format),
+	};
+	int n_formats = 1;
+
+	output->gbm_surface = gbm_surface_create(b->gbm,
+					     output->base.current_mode->width,
+					     output->base.current_mode->height,
+					     format[0],
+					     GBM_BO_USE_SCANOUT |
+					     GBM_BO_USE_RENDERING);
+	if (!output->gbm_surface) {
+		weston_log("failed to create gbm surface\n");
+		return -1;
+	}
+
+	if (format[1])
+		n_formats = 2;
+	if (gl_renderer->output_window_create(&output->base,
+					      (EGLNativeWindowType)output->gbm_surface,
+					      output->gbm_surface,
+					      gl_renderer->opaque_attribs,
+					      format,
+					      n_formats) < 0) {
+		weston_log("failed to create gl renderer output state\n");
+		gbm_surface_destroy(output->gbm_surface);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void
+rdp_output_fini_egl(struct rdp_output *output)
+{
+	gl_renderer->output_destroy(&output->base);
+	gbm_surface_destroy(output->gbm_surface);
+}
+
+static int
 rdp_switch_mode(struct weston_output *output, struct weston_mode *target_mode)
 {
 	struct rdp_output *rdpOutput = container_of(output, struct rdp_output, base);
+	struct rdp_backend *b = to_rdp_backend(output->compositor);
 	struct rdp_peers_item *rdpPeer;
 	rdpSettings *settings;
 	pixman_image_t *new_shadow_buffer;
@@ -448,8 +672,21 @@  rdp_switch_mode(struct weston_output *output, struct weston_mode *target_mode)
 	output->current_mode = local_mode;
 	output->current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
 
-	pixman_renderer_output_destroy(output);
-	pixman_renderer_output_create(output);
+	if (b->use_pixman) {
+		pixman_renderer_output_destroy(output);
+		if (pixman_renderer_output_create(output) < 0) {
+			weston_log("failed to init output pixman state with "
+				   "new mode\n");
+			return -1;
+		}
+	} else {
+		rdp_output_fini_egl(rdpOutput);
+		if (rdp_output_init_egl(rdpOutput, b) < 0) {
+			weston_log("failed to init output egl state with "
+				   "new mode");
+			return -1;
+		}
+	}
 
 	new_shadow_buffer = pixman_image_create_bits(PIXMAN_x8r8g8b8, target_mode->width,
 			target_mode->height, 0, target_mode->width * 4);
@@ -517,6 +754,17 @@  rdp_output_set_size(struct weston_output *base,
 	return 0;
 }
 
+static void
+rdp_output_set_gbm_format(struct weston_output *base,
+			  const char *gbm_format)
+{
+	struct rdp_output *output = to_rdp_output(base);
+	struct rdp_backend *b = to_rdp_backend(base->compositor);
+
+	if (parse_gbm_format(gbm_format, b->gbm_format, &output->gbm_format) == -1)
+		output->gbm_format = b->gbm_format;
+}
+
 static int
 rdp_output_enable(struct weston_output *base)
 {
@@ -530,11 +778,17 @@  rdp_output_enable(struct weston_output *base)
 							  NULL,
 							  output->base.current_mode->width * 4);
 	if (output->shadow_surface == NULL) {
-		weston_log("Failed to create surface for frame buffer.\n");
+		weston_log("Failed to create SW surface for frame buffer.\n");
 		return -1;
 	}
 
-	if (pixman_renderer_output_create(&output->base) < 0) {
+	if (b->use_pixman) {
+		if (pixman_renderer_output_create(&output->base) < 0) {
+			pixman_image_unref(output->shadow_surface);
+			return -1;
+		}
+	} else if (rdp_output_init_egl(output, b) < 0) {
+		weston_log("Failed to init output gl state\n");
 		pixman_image_unref(output->shadow_surface);
 		return -1;
 	}
@@ -557,7 +811,11 @@  rdp_output_disable(struct weston_output *base)
 		return 0;
 
 	pixman_image_unref(output->shadow_surface);
-	pixman_renderer_output_destroy(&output->base);
+
+	if (b->use_pixman)
+		pixman_renderer_output_destroy(&output->base);
+	else
+		rdp_output_fini_egl(output);
 
 	wl_event_source_remove(output->finish_frame_timer);
 	b->output = NULL;
@@ -608,6 +866,13 @@  rdp_destroy(struct weston_compositor *ec)
 	int i;
 
 	weston_compositor_shutdown(ec);
+
+	if (!b->use_pixman) {
+		gbm_device_destroy(b->gbm);
+		close(b->drm_fd);
+		udev_unref(b->udev);
+	}
+
 	for (i = 0; i < MAX_FREERDP_FDS; i++)
 		if (b->listener_events[i])
 			wl_event_source_remove(b->listener_events[i]);
@@ -1281,6 +1546,7 @@  rdp_incoming_peer(freerdp_listener *instance, freerdp_peer *client)
 
 static const struct weston_rdp_output_api api = {
 	rdp_output_set_size,
+	rdp_output_set_gbm_format
 };
 
 static struct rdp_backend *
@@ -1301,6 +1567,22 @@  rdp_backend_create(struct weston_compositor *compositor,
 	b->base.restore = rdp_restore;
 	b->rdp_key = config->rdp_key ? strdup(config->rdp_key) : NULL;
 	b->no_clients_resize = config->no_clients_resize;
+	b->use_pixman = config->use_pixman;
+
+	if (parse_gbm_format(config->gbm_format, GBM_FORMAT_XRGB8888, &b->gbm_format) < 0)
+		goto err_compositor;
+
+	b->udev = udev_new();
+	if (b->udev == NULL) {
+		weston_log("failed to initialize udev context\n");
+		goto err_compositor;
+	}
+
+	b->drm_fd = open_drm_device(b->udev);
+	if (b->drm_fd < 0) {
+		weston_log("failed to find a suitable drm render node\n");
+		goto err_udev;
+	}
 
 	/* activate TLS only if certificate/key are available */
 	if (config->server_cert && config->server_key) {
@@ -1313,13 +1595,22 @@  rdp_backend_create(struct weston_compositor *compositor,
 	}
 
 	if (weston_compositor_set_presentation_clock_software(compositor) < 0)
-		goto err_compositor;
+		goto err_udev;
 
-	if (pixman_renderer_init(compositor) < 0)
-		goto err_compositor;
+	if (b->use_pixman) {
+		if (init_pixman(b) < 0) {
+			weston_log("failed to initialize pixman renderer\n");
+			goto err_udev;
+		}
+	} else {
+		if (init_egl(b) < 0) {
+			weston_log("failed to initialize egl\n");
+			goto err_udev;
+		}
+	}
 
 	if (rdp_backend_create_output(compositor) < 0)
-		goto err_compositor;
+		goto err_udev;
 
 	compositor->capabilities |= WESTON_CAP_ARBITRARY_MODES;
 
@@ -1364,6 +1655,8 @@  err_listener:
 	freerdp_listener_free(b->listener);
 err_output:
 	weston_output_destroy(&b->output->base);
+err_udev:
+	udev_unref(b->udev);
 err_compositor:
 	weston_compositor_shutdown(compositor);
 err_free_strings:
@@ -1384,6 +1677,7 @@  config_init_to_defaults(struct weston_rdp_backend_config *config)
 	config->server_key = NULL;
 	config->env_socket = 0;
 	config->no_clients_resize = 0;
+	config->use_pixman = 0;
 }
 
 WL_EXPORT int
diff --git a/libweston/compositor-rdp.h b/libweston/compositor-rdp.h
index bd0a6a90..f3d77c88 100644
--- a/libweston/compositor-rdp.h
+++ b/libweston/compositor-rdp.h
@@ -42,6 +42,15 @@  struct weston_rdp_output_api {
 	 */
 	int (*output_set_size)(struct weston_output *output,
 			       int width, int height);
+
+	/** The pixel format to be used by the output. Valid values are:
+	 * - NULL - The format set at backend creation time will be used;
+	 * - "xrgb8888";
+	 * - "rgb565"
+	 * - "xrgb2101010"
+	 */
+	void (*set_gbm_format)(struct weston_output *output,
+			       const char *gbm_format);
 };
 
 static inline const struct weston_rdp_output_api *
@@ -65,6 +74,21 @@  struct weston_rdp_backend_config {
 	char *server_key;
 	int env_socket;
 	int no_clients_resize;
+
+	/** Whether to use the pixman renderer instead of the OpenGL ES renderer. */
+	bool use_pixman;
+
+	/** The pixel format of the framebuffer to be used.
+	 *
+	 * Valid values are:
+	 * - NULL - The default format ("xrgb8888") will be used;
+	 * - "xrgb8888";
+	 * - "rgb565"
+	 * - "xrgb2101010"
+	 * The backend will take ownership of the format pointer and will free
+	 * it on backend destruction.
+	 */
+	char *gbm_format;
 };
 
 #ifdef  __cplusplus

Comments

Le 06/07/2017 à 12:06, Olivier Blin a écrit :
> From: DRC <information@virtualgl.org>
> 
> ---
>  compositor/main.c          |  23 +++-
>  configure.ac               |   4 +-
>  libweston/compositor-rdp.c | 314 +++++++++++++++++++++++++++++++++++++++++++--
>  libweston/compositor-rdp.h |  24 ++++
>  4 files changed, 352 insertions(+), 13 deletions(-)
> 
> diff --git a/compositor/main.c b/compositor/main.c
> index f8a60e97..125cc0f8 100644
> --- a/compositor/main.c
> +++ b/compositor/main.c
> @@ -601,6 +601,7 @@ usage(int error_code)
>  		"  --rdp4-key=FILE\tThe file containing the key for RDP4 encryption\n"
>  		"  --rdp-tls-cert=FILE\tThe file containing the certificate for TLS encryption\n"
>  		"  --rdp-tls-key=FILE\tThe file containing the private key for TLS encryption\n"
> +		"  --use-pixman\t\tUse the pixman (CPU) renderer\n"
>  		"\n");
>  #endif
>  
> @@ -1331,11 +1332,14 @@ static void
>  rdp_backend_output_configure(struct wl_listener *listener, void *data)
>  {
>  	struct weston_output *output = data;
> +	struct weston_config *wc = wet_get_config(output->compositor);
>  	struct wet_compositor *compositor = to_wet_compositor(output->compositor);
>  	struct wet_output_config *parsed_options = compositor->parsed_options;
> +	struct weston_config_section *section;
>  	const struct weston_rdp_output_api *api = weston_rdp_output_get_api(output->compositor);
>  	int width = 640;
>  	int height = 480;
> +	char *gbm_format = NULL;
>  
>  	assert(parsed_options);
>  
> @@ -1344,6 +1348,8 @@ rdp_backend_output_configure(struct wl_listener *listener, void *data)
>  		return;
>  	}
>  
> +	section = weston_config_get_section(wc, "output", "name", output->name);
> +
>  	if (parsed_options->width)
>  		width = parsed_options->width;
>  
> @@ -1353,6 +1359,12 @@ rdp_backend_output_configure(struct wl_listener *listener, void *data)
>  	weston_output_set_scale(output, 1);
>  	weston_output_set_transform(output, WL_OUTPUT_TRANSFORM_NORMAL);
>  
> +	weston_config_section_get_string(section,
> +					 "gbm-format", &gbm_format, NULL);
> +
> +	api->set_gbm_format(output, gbm_format);
> +	free(gbm_format);
> +
>  	if (api->output_set_size(output, width, height) < 0) {
>  		weston_log("Cannot configure output \"%s\" using weston_rdp_output_api.\n",
>  			   output->name);
> @@ -1375,6 +1387,7 @@ weston_rdp_backend_config_init(struct weston_rdp_backend_config *config)
>  	config->server_key = NULL;
>  	config->env_socket = 0;
>  	config->no_clients_resize = 0;
> +	config->use_pixman = false;
>  }
>  
>  static int
> @@ -1382,6 +1395,7 @@ load_rdp_backend(struct weston_compositor *c,
>  		int *argc, char *argv[], struct weston_config *wc)
>  {
>  	struct weston_rdp_backend_config config  = {{ 0, }};
> +	struct weston_config_section *section;
>  	int ret = 0;
>  
>  	struct wet_output_config *parsed_options = wet_init_parsed_options(c);
> @@ -1399,11 +1413,17 @@ load_rdp_backend(struct weston_compositor *c,
>  		{ WESTON_OPTION_BOOLEAN, "no-clients-resize", 0, &config.no_clients_resize },
>  		{ WESTON_OPTION_STRING,  "rdp4-key", 0, &config.rdp_key },
>  		{ WESTON_OPTION_STRING,  "rdp-tls-cert", 0, &config.server_cert },
> -		{ WESTON_OPTION_STRING,  "rdp-tls-key", 0, &config.server_key }
> +		{ WESTON_OPTION_STRING,  "rdp-tls-key", 0, &config.server_key },
> +		{ WESTON_OPTION_BOOLEAN, "use-pixman", 0, &config.use_pixman }
>  	};
>  
>  	parse_options(rdp_options, ARRAY_LENGTH(rdp_options), argc, argv);
>  
> +	section = weston_config_get_section(wc, "core", NULL, NULL);
> +	weston_config_section_get_string(section,
> +					 "gbm-format", &config.gbm_format,
> +					 NULL);
> +
>  	ret = weston_compositor_load_backend(c, WESTON_BACKEND_RDP,
>  					     &config.base);
>  
> @@ -1417,6 +1437,7 @@ out:
>  	free(config.rdp_key);
>  	free(config.server_cert);
>  	free(config.server_key);
> +	free(config.gbm_format);
>  
>  	return ret;
>  }
> diff --git a/configure.ac b/configure.ac
> index 53faee34..a7b2d517 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -250,9 +250,9 @@ AM_CONDITIONAL([ENABLE_RDP_COMPOSITOR],
>                 [test x$enable_rdp_compositor = xyes])
>  if test x$enable_rdp_compositor = xyes; then
>    AC_DEFINE([BUILD_RDP_COMPOSITOR], [1], [Build the RDP compositor])
> -  PKG_CHECK_MODULES(RDP_COMPOSITOR, [freerdp2 >= 2.0.0],
> +  PKG_CHECK_MODULES(RDP_COMPOSITOR, [freerdp2 >= 2.0.0 libudev >= 136 gbm],
>      [],
> -    [PKG_CHECK_MODULES(RDP_COMPOSITOR, [freerdp >= 1.1.0],[])]
> +    [PKG_CHECK_MODULES(RDP_COMPOSITOR, [freerdp >= 1.1.0 libudev >= 136 gbm],[])]
>    )
>  
>    SAVED_CPPFLAGS="$CPPFLAGS"
> diff --git a/libweston/compositor-rdp.c b/libweston/compositor-rdp.c
> index 7b1ab06d..b8c390d0 100644
> --- a/libweston/compositor-rdp.c
> +++ b/libweston/compositor-rdp.c
> @@ -30,8 +30,14 @@
>  #include <stdlib.h>
>  #include <string.h>
>  #include <errno.h>
> +#include <dlfcn.h>
> +#include <fcntl.h>
> +#include <unistd.h>
>  #include <linux/input.h>
>  
> +#include <gbm.h>
> +#include <libudev.h>
> +
>  #if HAVE_FREERDP_VERSION_H
>  #include <freerdp/version.h>
>  #else
> @@ -79,6 +85,7 @@
>  #include "shared/helpers.h"
>  #include "compositor.h"
>  #include "compositor-rdp.h"
> +#include "gl-renderer.h"
>  #include "pixman-renderer.h"
>  
>  #define MAX_FREERDP_FDS 32
> @@ -110,6 +117,13 @@ struct rdp_backend {
>  	char *rdp_key;
>  	int tls_enabled;
>  	int no_clients_resize;
> +
> +	/* For GL rendering */
> +	struct udev *udev;
> +	int drm_fd;
> +	struct gbm_device *gbm;
> +	uint32_t gbm_format;
> +	int use_pixman;
>  };
>  
>  enum peer_item_flags {
> @@ -128,6 +142,8 @@ struct rdp_peers_item {
>  struct rdp_output {
>  	struct weston_output base;
>  	struct wl_event_source *finish_frame_timer;
> +	uint32_t gbm_format;
> +	struct gbm_surface *gbm_surface;
>  	pixman_image_t *shadow_surface;
>  
>  	struct wl_list peers;
> @@ -160,6 +176,8 @@ to_rdp_backend(struct weston_compositor *base)
>  	return container_of(base->backend, struct rdp_backend, base);
>  }
>  
> +static struct gl_renderer_interface *gl_renderer;
> +
>  static void
>  rdp_peer_refresh_rfx(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer)
>  {
> @@ -364,12 +382,24 @@ rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage,
>  {
>  	struct rdp_output *output = container_of(output_base, struct rdp_output, base);
>  	struct weston_compositor *ec = output->base.compositor;
> +	struct rdp_backend *b = to_rdp_backend(ec);
>  	struct rdp_peers_item *outputPeer;
>  
> -	pixman_renderer_output_set_buffer(output_base, output->shadow_surface);
> +	if (b->use_pixman)
> +		pixman_renderer_output_set_buffer(output_base, output->shadow_surface);
>  	ec->renderer->repaint_output(&output->base, damage);
>  
>  	if (pixman_region32_not_empty(damage)) {
> +		if (!b->use_pixman) {
> +			/* TODO: Performance: Only read pixels that was actually repained by renderer->repaint_output. */
> +			ec->renderer->read_pixels(output_base,
> +					pixman_image_get_format(output->shadow_surface),
> +					pixman_image_get_data(output->shadow_surface),
> +					0, 0,
> +					pixman_image_get_width(output->shadow_surface),
> +					pixman_image_get_height(output->shadow_surface));
> +		}
> +
>  		wl_list_for_each(outputPeer, &output->peers, link) {
>  			if ((outputPeer->flags & RDP_PEER_ACTIVATED) &&
>  					(outputPeer->flags & RDP_PEER_OUTPUT_ENABLED))
> @@ -426,9 +456,203 @@ ensure_matching_mode(struct weston_output *output, struct weston_mode *target)
>  }
>  
>  static int
> +open_drm_device(struct udev *udev)
> +{
> +	struct udev_enumerate *e;
> +	struct udev_list_entry *entry;
> +	struct udev_device *device;
> +	const char *path, *filename;
> +	int fd;
> +
> +	e = udev_enumerate_new(udev);
> +	udev_enumerate_add_match_subsystem(e, "drm");
> +	udev_enumerate_add_match_sysname(e, "renderD[0-9]*");
> +
> +	udev_enumerate_scan_devices(e);
> +	device = NULL;
> +	udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
> +		path = udev_list_entry_get_name(entry);
> +		device = udev_device_new_from_syspath(udev, path);
> +		if (device) {
> +			filename = udev_device_get_devnode(device);
> +			fd = open(filename, O_RDWR | O_CLOEXEC);
> +			if (fd < 0) {
> +				udev_device_unref(device);
> +				continue;
> +			}
> +
> +			weston_log("using %s\n", filename);
> +			udev_device_unref(device);
> +			udev_enumerate_unref(e);
> +			return fd;
> +		}
> +	}
> +
> +	udev_enumerate_unref(e);
> +	return -1;
> +}
> +
> +static struct gbm_device *
> +create_gbm_device(int fd)
> +{
> +	struct gbm_device *gbm;
> +
> +	gl_renderer = weston_load_module("gl-renderer.so",
> +					 "gl_renderer_interface");
> +	if (!gl_renderer)
> +		return NULL;
> +
> +	/* GBM will load a dri driver, but even though they need symbols from
> +	 * libglapi, in some version of Mesa they are not linked to it. Since
> +	 * only the gl-renderer module links to it, the call above won't make
> +	 * these symbols globally available, and loading the DRI driver fails.
> +	 * Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL. */
> +	dlopen("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL);
> +
> +	gbm = gbm_create_device(fd);
> +
> +	return gbm;
> +}
> +
> +/* When initializing EGL, if the preferred buffer format isn't available
> + * we may be able to substitute an ARGB format for an XRGB one.
> + *
> + * This returns 0 if substitution isn't possible, but 0 might be a
> + * legitimate format for other EGL platforms, so the caller is
> + * responsible for checking for 0 before calling gl_renderer->create().
> + *
> + * This works around https://bugs.freedesktop.org/show_bug.cgi?id=89689
> + * but it's entirely possible we'll see this again on other implementations.
> + */
> +static int
> +fallback_format_for(uint32_t format)
> +{
> +	switch (format) {
> +	case GBM_FORMAT_XRGB8888:
> +		return GBM_FORMAT_ARGB8888;
> +	case GBM_FORMAT_XRGB2101010:
> +		return GBM_FORMAT_ARGB2101010;
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static int
> +rdp_backend_create_gl_renderer(struct rdp_backend *b)
> +{
> +	EGLint format[3] = {
> +		b->gbm_format,
> +		fallback_format_for(b->gbm_format),
> +		0,
> +	};
> +	int n_formats = 2;
> +
> +	if (format[1])
> +		n_formats = 3;
> +	if (gl_renderer->display_create(b->compositor,
> +					EGL_PLATFORM_GBM_KHR,
> +					(void *)b->gbm,
> +					NULL,
> +					gl_renderer->opaque_attribs,
> +					format,
> +					n_formats) < 0) {
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +init_egl(struct rdp_backend *b)
> +{
> +	b->gbm = create_gbm_device(b->drm_fd);
> +
> +	if (!b->gbm)
> +		return -1;
> +
> +	if (rdp_backend_create_gl_renderer(b) < 0) {
> +		gbm_device_destroy(b->gbm);
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +init_pixman(struct rdp_backend *b)
> +{
> +	return pixman_renderer_init(b->compositor);
> +}
> +
> +static int
> +parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format)
> +{
> +	int ret = 0;
> +
> +	if (s == NULL)
> +		*gbm_format = default_value;
> +	else if (strcmp(s, "xrgb8888") == 0)
> +		*gbm_format = GBM_FORMAT_XRGB8888;
> +	else if (strcmp(s, "rgb565") == 0)
> +		*gbm_format = GBM_FORMAT_RGB565;
> +	else if (strcmp(s, "xrgb2101010") == 0)
> +		*gbm_format = GBM_FORMAT_XRGB2101010;
> +	else {
> +		weston_log("fatal: unrecognized pixel format: %s\n", s);
> +		ret = -1;
> +	}
> +
> +	return ret;
> +}
> +/* Init output state that depends on gl or gbm */
> +static int
> +rdp_output_init_egl(struct rdp_output *output, struct rdp_backend *b)
> +{
> +	EGLint format[2] = {
> +		output->gbm_format,
> +		fallback_format_for(output->gbm_format),
> +	};
> +	int n_formats = 1;
> +
> +	output->gbm_surface = gbm_surface_create(b->gbm,
> +					     output->base.current_mode->width,
> +					     output->base.current_mode->height,
> +					     format[0],
> +					     GBM_BO_USE_SCANOUT |
> +					     GBM_BO_USE_RENDERING);
> +	if (!output->gbm_surface) {
> +		weston_log("failed to create gbm surface\n");
> +		return -1;
> +	}
> +
> +	if (format[1])
> +		n_formats = 2;
> +	if (gl_renderer->output_window_create(&output->base,
> +					      (EGLNativeWindowType)output->gbm_surface,
> +					      output->gbm_surface,
> +					      gl_renderer->opaque_attribs,
> +					      format,
> +					      n_formats) < 0) {
> +		weston_log("failed to create gl renderer output state\n");
> +		gbm_surface_destroy(output->gbm_surface);
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +static void
> +rdp_output_fini_egl(struct rdp_output *output)
> +{
> +	gl_renderer->output_destroy(&output->base);
> +	gbm_surface_destroy(output->gbm_surface);
> +}
> +
> +static int
>  rdp_switch_mode(struct weston_output *output, struct weston_mode *target_mode)
>  {
>  	struct rdp_output *rdpOutput = container_of(output, struct rdp_output, base);
> +	struct rdp_backend *b = to_rdp_backend(output->compositor);
>  	struct rdp_peers_item *rdpPeer;
>  	rdpSettings *settings;
>  	pixman_image_t *new_shadow_buffer;
> @@ -448,8 +672,21 @@ rdp_switch_mode(struct weston_output *output, struct weston_mode *target_mode)
>  	output->current_mode = local_mode;
>  	output->current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
>  
> -	pixman_renderer_output_destroy(output);
> -	pixman_renderer_output_create(output);
> +	if (b->use_pixman) {
> +		pixman_renderer_output_destroy(output);
> +		if (pixman_renderer_output_create(output) < 0) {
> +			weston_log("failed to init output pixman state with "
> +				   "new mode\n");
> +			return -1;
> +		}
> +	} else {
> +		rdp_output_fini_egl(rdpOutput);
> +		if (rdp_output_init_egl(rdpOutput, b) < 0) {
> +			weston_log("failed to init output egl state with "
> +				   "new mode");
> +			return -1;
> +		}
> +	}
>  
>  	new_shadow_buffer = pixman_image_create_bits(PIXMAN_x8r8g8b8, target_mode->width,
>  			target_mode->height, 0, target_mode->width * 4);
> @@ -517,6 +754,17 @@ rdp_output_set_size(struct weston_output *base,
>  	return 0;
>  }
>  
> +static void
> +rdp_output_set_gbm_format(struct weston_output *base,
> +			  const char *gbm_format)
> +{
> +	struct rdp_output *output = to_rdp_output(base);
> +	struct rdp_backend *b = to_rdp_backend(base->compositor);
> +
> +	if (parse_gbm_format(gbm_format, b->gbm_format, &output->gbm_format) == -1)
> +		output->gbm_format = b->gbm_format;
> +}
> +
>  static int
>  rdp_output_enable(struct weston_output *base)
>  {
> @@ -530,11 +778,17 @@ rdp_output_enable(struct weston_output *base)
>  							  NULL,
>  							  output->base.current_mode->width * 4);
>  	if (output->shadow_surface == NULL) {
> -		weston_log("Failed to create surface for frame buffer.\n");
> +		weston_log("Failed to create SW surface for frame buffer.\n");
>  		return -1;
>  	}
>  
> -	if (pixman_renderer_output_create(&output->base) < 0) {
> +	if (b->use_pixman) {
> +		if (pixman_renderer_output_create(&output->base) < 0) {
> +			pixman_image_unref(output->shadow_surface);
> +			return -1;
> +		}
> +	} else if (rdp_output_init_egl(output, b) < 0) {
> +		weston_log("Failed to init output gl state\n");
>  		pixman_image_unref(output->shadow_surface);
>  		return -1;
>  	}
> @@ -557,7 +811,11 @@ rdp_output_disable(struct weston_output *base)
>  		return 0;
>  
>  	pixman_image_unref(output->shadow_surface);
> -	pixman_renderer_output_destroy(&output->base);
> +
> +	if (b->use_pixman)
> +		pixman_renderer_output_destroy(&output->base);
> +	else
> +		rdp_output_fini_egl(output);
>  
>  	wl_event_source_remove(output->finish_frame_timer);
>  	b->output = NULL;
> @@ -608,6 +866,13 @@ rdp_destroy(struct weston_compositor *ec)
>  	int i;
>  
>  	weston_compositor_shutdown(ec);
> +
> +	if (!b->use_pixman) {
> +		gbm_device_destroy(b->gbm);
> +		close(b->drm_fd);
> +		udev_unref(b->udev);
> +	}
> +
>  	for (i = 0; i < MAX_FREERDP_FDS; i++)
>  		if (b->listener_events[i])
>  			wl_event_source_remove(b->listener_events[i]);
> @@ -1281,6 +1546,7 @@ rdp_incoming_peer(freerdp_listener *instance, freerdp_peer *client)
>  
>  static const struct weston_rdp_output_api api = {
>  	rdp_output_set_size,
> +	rdp_output_set_gbm_format
>  };
>  
>  static struct rdp_backend *
> @@ -1301,6 +1567,22 @@ rdp_backend_create(struct weston_compositor *compositor,
>  	b->base.restore = rdp_restore;
>  	b->rdp_key = config->rdp_key ? strdup(config->rdp_key) : NULL;
>  	b->no_clients_resize = config->no_clients_resize;
> +	b->use_pixman = config->use_pixman;
> +
> +	if (parse_gbm_format(config->gbm_format, GBM_FORMAT_XRGB8888, &b->gbm_format) < 0)
> +		goto err_compositor;
> +
> +	b->udev = udev_new();
> +	if (b->udev == NULL) {
> +		weston_log("failed to initialize udev context\n");
> +		goto err_compositor;
> +	}
> +
> +	b->drm_fd = open_drm_device(b->udev);
> +	if (b->drm_fd < 0) {
> +		weston_log("failed to find a suitable drm render node\n");
> +		goto err_udev;
> +	}
>  
>  	/* activate TLS only if certificate/key are available */
>  	if (config->server_cert && config->server_key) {
> @@ -1313,13 +1595,22 @@ rdp_backend_create(struct weston_compositor *compositor,
>  	}
>  
>  	if (weston_compositor_set_presentation_clock_software(compositor) < 0)
> -		goto err_compositor;
> +		goto err_udev;
>  
> -	if (pixman_renderer_init(compositor) < 0)
> -		goto err_compositor;
> +	if (b->use_pixman) {
> +		if (init_pixman(b) < 0) {
> +			weston_log("failed to initialize pixman renderer\n");
> +			goto err_udev;
> +		}
> +	} else {
> +		if (init_egl(b) < 0) {
> +			weston_log("failed to initialize egl\n");
> +			goto err_udev;
> +		}
> +	}
>  
>  	if (rdp_backend_create_output(compositor) < 0)
> -		goto err_compositor;
> +		goto err_udev;
>  
>  	compositor->capabilities |= WESTON_CAP_ARBITRARY_MODES;
>  
> @@ -1364,6 +1655,8 @@ err_listener:
>  	freerdp_listener_free(b->listener);
>  err_output:
>  	weston_output_destroy(&b->output->base);
> +err_udev:
> +	udev_unref(b->udev);
>  err_compositor:
>  	weston_compositor_shutdown(compositor);
>  err_free_strings:
> @@ -1384,6 +1677,7 @@ config_init_to_defaults(struct weston_rdp_backend_config *config)
>  	config->server_key = NULL;
>  	config->env_socket = 0;
>  	config->no_clients_resize = 0;
> +	config->use_pixman = 0;
>  }
>  
>  WL_EXPORT int
> diff --git a/libweston/compositor-rdp.h b/libweston/compositor-rdp.h
> index bd0a6a90..f3d77c88 100644
> --- a/libweston/compositor-rdp.h
> +++ b/libweston/compositor-rdp.h
> @@ -42,6 +42,15 @@ struct weston_rdp_output_api {
>  	 */
>  	int (*output_set_size)(struct weston_output *output,
>  			       int width, int height);
> +
> +	/** The pixel format to be used by the output. Valid values are:
> +	 * - NULL - The format set at backend creation time will be used;
> +	 * - "xrgb8888";
> +	 * - "rgb565"
> +	 * - "xrgb2101010"
> +	 */
> +	void (*set_gbm_format)(struct weston_output *output,
> +			       const char *gbm_format);
>  };
>  
>  static inline const struct weston_rdp_output_api *
> @@ -65,6 +74,21 @@ struct weston_rdp_backend_config {
>  	char *server_key;
>  	int env_socket;
>  	int no_clients_resize;
> +
> +	/** Whether to use the pixman renderer instead of the OpenGL ES renderer. */
> +	bool use_pixman;
> +
> +	/** The pixel format of the framebuffer to be used.
> +	 *
> +	 * Valid values are:
> +	 * - NULL - The default format ("xrgb8888") will be used;
> +	 * - "xrgb8888";
> +	 * - "rgb565"
> +	 * - "xrgb2101010"
> +	 * The backend will take ownership of the format pointer and will free
> +	 * it on backend destruction.
> +	 */
> +	char *gbm_format;
>  };
>  
>  #ifdef  __cplusplus
> 

Reviewed-by: David Fort <contact@hardening-consulting.com>