Remote display with 3D acceleration using Wayland/Weston
DRC
dcommander at users.sourceforge.net
Wed Mar 1 05:09:17 UTC 2017
On 2/28/17 8:12 AM, Hardening wrote:
> This has been done a quite long time ago, here =>
> https://gitorious.org/weston/jonseverinsson-weston/?p=weston:jonseverinsson-weston.git;a=commit;h=9e26d9356255f4af1723700272805f6d356c7d7a
>
> It's clearly outdated, and IIRC people here didn't like the way it was
> implemented, but you have the idea. It's using DRI render nodes to do
> the rendering.
Thanks for the hint. I was able to integrate your patch with the latest
Weston code by borrowing some of the code from the DRM backend, but it
doesn't seem to display anything (my patch is attached. Maybe someone
can point out what I'm doing wrong, so I could at least get this working
for experimental purposes.)
The following GitHub comment summarizes my general feelings at the moment:
https://github.com/TurboVNC/turbovnc/issues/18#issuecomment-282827192
To the best of my knowledge (someone please correct me if I got any part
of this wrong), I think it would be much easier and more flexible to
pursue the interposer approach for now, mostly because of the nVidia
proprietary driver issue (that's a show-stopper for VirtualGL and
TurboVNC) but also because implementing a headless hardware-accelerated
remote display backend "the right way" would likely require some changes
to gl_renderer and the compositor code, including implementing PBO
readback in order to prevent the readback in the compositor from
blocking other OpenGL applications. I anticipate that further tuning
will likely be required as well, and it may still not perform as well as
an interposer, because VirtualGL reads back the pixels in the
application's rendering thread but uses a separate thread for displaying
the pixels. Thus, if the compositor was blocking on encoding a pixel
region for remote display, the Wayland client could still render the
next frame in the background.
I am not in a good position to develop or maintain changes to Weston,
and since interest has been expressed in "officially" supporting a
headless hardware-accelerated remote display backend in the long term,
it makes sense for me to develop an interposer as a stopgap measure.
The interposer could also provide a springboard for other developers who
are interested in making their own Weston remote display backends, since
they would not have to deal with the problem of OpenGL hardware
acceleration. In the long term, I anticipate that this interposer will
be rendered obsolete, but that's fine, because a lot of the effort
necessary to build an EGL interposer for Wayland would benefit the
existing GLX interposer in VirtualGL as well, so the only thing that
might be "thrown away" in the long term would be the Wayland-specific
parts. Barring any further information, that seems to be the best path
forward at the moment, but if there is any movement on the nVidia front
or on the headless hardware-accelerated remote display backend front,
please keep me in the loop.
DRC
-------------- next part --------------
From f331db279a138295701806de0c8bd71f385d2796 Mon Sep 17 00:00:00 2001
From: DRC <information at virtualgl.org>
Date: Tue, 28 Feb 2017 22:49:22 -0600
Subject: [PATCH] rdp-backend.so: OpenGL hardware acceleration
---
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 72c3cd1..7f4b8db 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
@@ -1329,11 +1330,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);
@@ -1342,6 +1346,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;
@@ -1351,6 +1357,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);
@@ -1373,6 +1385,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
@@ -1380,6 +1393,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);
@@ -1397,11 +1411,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);
@@ -1415,6 +1435,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 9df85d2..3d9a2be 100644
--- a/configure.ac
+++ b/configure.ac
@@ -257,9 +257,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 d9668e8..ff4d7df 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;
@@ -159,6 +175,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)
{
@@ -359,12 +377,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))
@@ -421,9 +451,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;
@@ -443,8 +667,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);
@@ -512,6 +749,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)
{
@@ -525,11 +773,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;
}
@@ -552,7 +806,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;
@@ -603,6 +861,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]);
@@ -1273,6 +1538,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 *
@@ -1293,6 +1559,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) {
@@ -1305,13 +1587,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;
@@ -1356,6 +1647,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:
@@ -1376,6 +1669,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 bd0a6a9..f3d77c8 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
--
2.5.4 (Apple Git-61)
More information about the wayland-devel
mailing list