diff --git a/include/libweston/backend-headless.h b/include/libweston/backend-headless.h index 08bd674b..1f53835e 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 e5630b3a..95b390a8 100644 --- a/libweston/backend-headless/headless.c +++ b/libweston/backend-headless/headless.c @@ -32,18 +32,22 @@ #include #include #include +#include #include #include #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 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) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index cc77a345..fa472e03 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -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, diff --git a/libweston/renderer-gl/gl-renderer.h b/libweston/renderer-gl/gl-renderer.h index 9aac993a..2bca2d00 100644 --- a/libweston/renderer-gl/gl-renderer.h +++ b/libweston/renderer-gl/gl-renderer.h @@ -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.