[PATCH 3/3] rdp-backend.so: OpenGL hardware acceleration
Olivier Blin
olivier.blin at softathome.com
Thu Jul 6 10:06:15 UTC 2017
From: DRC <information at 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
--
2.13.0
More information about the wayland-devel
mailing list