From: DRC <informat...@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 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/wayland-devel