headless, gl-renderer: support pbuffer outputs
authorPekka Paalanen <pekka.paalanen@collabora.com>
Fri, 20 Sep 2019 11:23:12 +0000 (14:23 +0300)
committerPekka Paalanen <pq@iki.fi>
Fri, 11 Oct 2019 12:16:35 +0000 (12:16 +0000)
Use the surfaceless platform in the headless backend to initialize the
GL-renderer and create pbuffer outputs. This allows headless backend to use
GL-renderer, even hardware accelerated.

This paves way for exercising GL-renderer in CI and using the Weston test suite
to test hardware GL ES implementations.

Relates to: https://gitlab.freedesktop.org/wayland/weston/issues/278

Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
include/libweston/backend-headless.h
libweston/backend-headless/headless.c
libweston/renderer-gl/gl-renderer.c
libweston/renderer-gl/gl-renderer.h

index 08bd674b46fd79f059b2c1a37047b18d963306d8..1f53835e01f079c21c910f632ad32db371ec47bd 100644 (file)
@@ -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
index e5630b3a6173894de5b4bbbe12ab3205705b68d1..95b390a85d1a967be625a56e1b741328398f1800 100644 (file)
 #include <string.h>
 #include <sys/time.h>
 #include <stdbool.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 "linux-dmabuf.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,24 @@ 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,
+                                          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 +250,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 +383,25 @@ 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;
+
+       if (b->glri->display_create(b->compositor,
+                                   EGL_PLATFORM_SURFACELESS_MESA,
+                                   EGL_DEFAULT_DISPLAY,
+                                   EGL_PBUFFER_BIT,
+                                   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 +427,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;
@@ -381,6 +454,13 @@ headless_backend_create(struct weston_compositor *compositor,
        if (ret < 0)
                goto err_input;
 
+       if (compositor->renderer->import_dmabuf) {
+               if (linux_dmabuf_setup(compositor) < 0) {
+                       weston_log("Error: dmabuf protocol setup failed.\n");
+                       goto err_input;
+               }
+       }
+
        /* Support zwp_linux_explicit_synchronization_unstable_v1 to enable
         * testing. */
        if (linux_explicit_synchronization_setup(compositor) < 0)
index cc77a3451b2e0fb3378fff4ba466a48f20390301..fa472e03e5ccf784d5fa374f82d1fbf9f0d0646b 100644 (file)
@@ -3142,6 +3142,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,
+                                 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, 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;
+       }
+
+       log_egl_config_info(gr->egl_display, pbuffer_config);
+
+       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)
 {
@@ -3728,6 +3770,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,
index 9aac993a8696b09f0cacc6be78c28f3323892de6..2bca2d00c5320b2293d55a15fd4b53e4f41e6304 100644 (file)
@@ -135,6 +135,33 @@ 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 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.
+        *
+        * The first format in drm_formats that matches any EGLConfig
+        * determines which EGLConfig is chosen. See \c display_create about
+        * how the matching works and the possible limitations.
+        *
+        * This function should be used only if \c display_create was called
+        * with \c EGL_PBUFFER_BIT in \c egl_surface_type.
+        */
+       int (*output_pbuffer_create)(struct weston_output *output,
+                                    int width,
+                                    int height,
+                                    const uint32_t *drm_formats,
+                                    unsigned drm_formats_count);
+
        void (*output_destroy)(struct weston_output *output);
 
        /* Sets the output border.