diff --git a/include/libweston/backend-headless.h b/include/libweston/backend-headless.h index 08bd674b46fd79f059b2c1a37047b18d963306d8..1f53835e01f079c21c910f632ad32db371ec47bd 100644 --- a/include/libweston/backend-headless.h +++ b/include/libweston/backend-headless.h @@ -41,6 +41,9 @@ struct weston_headless_backend_config { /** Whether to use the pixman renderer, default is no-op */ bool use_pixman; + + /** Whether to use the GL renderer, conflicts with use_pixman */ + bool use_gl; }; #ifdef __cplusplus diff --git a/libweston/backend-headless/headless.c b/libweston/backend-headless/headless.c index e5630b3a6173894de5b4bbbe12ab3205705b68d1..d087b2b0f429332c7cb9607aeaa350821a1e3ba2 100644 --- a/libweston/backend-headless/headless.c +++ b/libweston/backend-headless/headless.c @@ -32,18 +32,22 @@ #include <string.h> #include <sys/time.h> #include <stdbool.h> +#include <dlfcn.h> +#include <drm_fourcc.h> #include <libweston/libweston.h> #include <libweston/backend-headless.h> #include "shared/helpers.h" #include "linux-explicit-synchronization.h" #include "pixman-renderer.h" +#include "renderer-gl/gl-renderer.h" #include "presentation-time-server-protocol.h" #include <libweston/windowed-output-api.h> enum headless_renderer_type { HEADLESS_NOOP, HEADLESS_PIXMAN, + HEADLESS_GL, }; struct headless_backend { @@ -52,6 +56,8 @@ struct headless_backend { struct weston_seat fake_seat; enum headless_renderer_type renderer_type; + + struct gl_renderer_interface *glri; }; struct headless_head { @@ -67,6 +73,11 @@ struct headless_output { pixman_image_t *image; }; +static const uint32_t headless_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, +}; + static inline struct headless_head * to_headless_head(struct weston_head *base) { @@ -126,6 +137,15 @@ headless_output_repaint(struct weston_output *output_base, return 0; } +static void +headless_output_disable_gl(struct headless_output *output) +{ + struct weston_compositor *compositor = output->base.compositor; + struct headless_backend *b = to_headless_backend(compositor); + + b->glri->output_destroy(&output->base); +} + static void headless_output_disable_pixman(struct headless_output *output) { @@ -146,6 +166,9 @@ headless_output_disable(struct weston_output *base) wl_event_source_remove(output->finish_frame_timer); switch (b->renderer_type) { + case HEADLESS_GL: + headless_output_disable_gl(output); + break; case HEADLESS_PIXMAN: headless_output_disable_pixman(output); break; @@ -167,6 +190,25 @@ headless_output_destroy(struct weston_output *base) free(output); } +static int +headless_output_enable_gl(struct headless_output *output) +{ + struct weston_compositor *compositor = output->base.compositor; + struct headless_backend *b = to_headless_backend(compositor); + + if (b->glri->output_pbuffer_create(&output->base, + output->base.current_mode->width, + output->base.current_mode->height, + GL_RENDERER_CONFIG_OPAQUE, + headless_formats, + ARRAY_LENGTH(headless_formats)) < 0) { + weston_log("failed to create gl renderer output state\n"); + return -1; + } + + return 0; +} + static int headless_output_enable_pixman(struct headless_output *output) { @@ -209,6 +251,9 @@ headless_output_enable(struct weston_output *base) wl_event_loop_add_timer(loop, finish_frame_handler, output); switch (b->renderer_type) { + case HEADLESS_GL: + ret = headless_output_enable_gl(output); + break; case HEADLESS_PIXMAN: ret = headless_output_enable_pixman(output); break; @@ -339,6 +384,33 @@ headless_destroy(struct weston_compositor *ec) free(b); } +static int +headless_gl_renderer_init(struct headless_backend *b) +{ + b->glri = weston_load_module("gl-renderer.so", "gl_renderer_interface"); + if (!b->glri) + return -1; + + /* 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); + + if (b->glri->display_create(b->compositor, + EGL_PLATFORM_SURFACELESS_MESA, + EGL_DEFAULT_DISPLAY, + GL_RENDERER_CONFIG_OPAQUE, + GL_RENDERER_CONFIG_SURFACE_PBUFFER, + headless_formats, + ARRAY_LENGTH(headless_formats)) < 0) { + return -1; + } + + return 0; +} + static const struct weston_windowed_output_api api = { headless_output_set_size, headless_head_create, @@ -364,12 +436,22 @@ headless_backend_create(struct weston_compositor *compositor, b->base.destroy = headless_destroy; b->base.create_output = headless_output_create; - if (config->use_pixman) + if (config->use_pixman && config->use_gl) { + weston_log("Error: cannot use both Pixman *and* GL renderers.\n"); + goto err_free; + } + + if (config->use_gl) + b->renderer_type = HEADLESS_GL; + else if (config->use_pixman) b->renderer_type = HEADLESS_PIXMAN; else b->renderer_type = HEADLESS_NOOP; switch (b->renderer_type) { + case HEADLESS_GL: + ret = headless_gl_renderer_init(b); + break; case HEADLESS_PIXMAN: ret = pixman_renderer_init(compositor); break; diff --git a/libweston/backend-headless/meson.build b/libweston/backend-headless/meson.build index 19b57605c113f7174f4a8c256bfb51d260d3ad79..790895383fe47e9448d9c8e9eaeaf46b64f223f7 100644 --- a/libweston/backend-headless/meson.build +++ b/libweston/backend-headless/meson.build @@ -12,7 +12,7 @@ plugin_headless = shared_library( 'headless-backend', srcs_headless, include_directories: include_directories('../..', '../../shared'), - dependencies: dep_libweston, + dependencies: [ dep_libweston, dep_libdrm_headers ], name_prefix: '', install: true, install_dir: dir_module_libweston, diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index f5c64b8e83b55ff7900120c2400847ded5f1e4b2..00b05f8f5e02fd998485ef9f43d4ec4d69e5ca88 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -3145,6 +3145,48 @@ gl_renderer_output_window_create(struct weston_output *output, return ret; } +static int +gl_renderer_output_pbuffer_create(struct weston_output *output, + int width, + int height, + enum gl_renderer_config_alpha config_alpha, + const uint32_t *drm_formats, + unsigned drm_formats_count) +{ + struct gl_renderer *gr = get_renderer(output->compositor); + EGLConfig pbuffer_config; + EGLSurface egl_surface; + int ret; + EGLint pbuffer_attribs[] = { + EGL_WIDTH, width, + EGL_HEIGHT, height, + EGL_NONE + }; + + pbuffer_config = gl_renderer_get_egl_config(gr, config_alpha, + EGL_PBUFFER_BIT, + drm_formats, + drm_formats_count); + if (pbuffer_config == EGL_NO_CONFIG_KHR) { + weston_log("failed to choose EGL config for PbufferSurface\n"); + return -1; + } + + egl_surface = eglCreatePbufferSurface(gr->egl_display, pbuffer_config, + pbuffer_attribs); + if (egl_surface == EGL_NO_SURFACE) { + weston_log("failed to create egl surface\n"); + gl_renderer_print_egl_error_state(); + return -1; + } + + ret = gl_renderer_output_create(output, egl_surface); + if (ret < 0) + eglDestroySurface(gr->egl_display, egl_surface); + + return ret; +} + static void gl_renderer_output_destroy(struct weston_output *output) { @@ -3750,6 +3792,7 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) WL_EXPORT struct gl_renderer_interface gl_renderer_interface = { .display_create = gl_renderer_display_create, .output_window_create = gl_renderer_output_window_create, + .output_pbuffer_create = gl_renderer_output_pbuffer_create, .output_destroy = gl_renderer_output_destroy, .output_set_border = gl_renderer_output_set_border, .create_fence_fd = gl_renderer_create_fence_fd, diff --git a/libweston/renderer-gl/gl-renderer.h b/libweston/renderer-gl/gl-renderer.h index 90ae0e8d2cda2ccfaa0a91c5f73b3f365f9e07c1..08204fa2028195e8df5f636101bc3d52fe3e9b75 100644 --- a/libweston/renderer-gl/gl-renderer.h +++ b/libweston/renderer-gl/gl-renderer.h @@ -148,6 +148,37 @@ struct gl_renderer_interface { const uint32_t *drm_formats, unsigned drm_formats_count); + /** + * Attach GL-renderer to the output with internal pixel storage + * + * \param output The output to create a rendering surface for. + * \param width Width of the rendering surface in pixels. + * \param height Height of the rendering surface in pixels. + * \param config_alpha Determines if the EGLConfig should have + * alpha channel or not. + * \param drm_formats Array of DRM pixel formats that are acceptable. + * \param drm_formats_count The drm_formats array length. + * \return 0 on success, -1 on failure. + * + * This function creates the renderer data structures needed to repaint + * the output. The repaint results will be kept internal and can only + * be accessed through e.g. screen capture. + * + * An EGLConfig is chosen for the output, meeting the requirements in + * config_alpha, drm_formats and drm_formats_count. If drm_formats_count + * is zero then drm_formats can be NULL and EGL_NATIVE_VISUAL_ID + * attribute is ignored on choosing an EGLConfig for the output. + * + * This function should be used only if \c display_create was called + * with GL_RENDERER_CONFIG_SURFACE_PBUFFER. + */ + int (*output_pbuffer_create)(struct weston_output *output, + int width, + int height, + enum gl_renderer_config_alpha config_alpha, + const uint32_t *drm_formats, + unsigned drm_formats_count); + void (*output_destroy)(struct weston_output *output); /* Sets the output border.