headless, gl-renderer: support pbuffer outputs
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>
This commit is contained in:
committed by
Pekka Paalanen
parent
209187491b
commit
5104d7b2af
@@ -41,6 +41,9 @@ struct weston_headless_backend_config {
|
|||||||
|
|
||||||
/** Whether to use the pixman renderer, default is no-op */
|
/** Whether to use the pixman renderer, default is no-op */
|
||||||
bool use_pixman;
|
bool use_pixman;
|
||||||
|
|
||||||
|
/** Whether to use the GL renderer, conflicts with use_pixman */
|
||||||
|
bool use_gl;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -32,18 +32,22 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <drm_fourcc.h>
|
||||||
|
|
||||||
#include <libweston/libweston.h>
|
#include <libweston/libweston.h>
|
||||||
#include <libweston/backend-headless.h>
|
#include <libweston/backend-headless.h>
|
||||||
#include "shared/helpers.h"
|
#include "shared/helpers.h"
|
||||||
#include "linux-explicit-synchronization.h"
|
#include "linux-explicit-synchronization.h"
|
||||||
#include "pixman-renderer.h"
|
#include "pixman-renderer.h"
|
||||||
|
#include "renderer-gl/gl-renderer.h"
|
||||||
|
#include "linux-dmabuf.h"
|
||||||
#include "presentation-time-server-protocol.h"
|
#include "presentation-time-server-protocol.h"
|
||||||
#include <libweston/windowed-output-api.h>
|
#include <libweston/windowed-output-api.h>
|
||||||
|
|
||||||
enum headless_renderer_type {
|
enum headless_renderer_type {
|
||||||
HEADLESS_NOOP,
|
HEADLESS_NOOP,
|
||||||
HEADLESS_PIXMAN,
|
HEADLESS_PIXMAN,
|
||||||
|
HEADLESS_GL,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct headless_backend {
|
struct headless_backend {
|
||||||
@@ -52,6 +56,8 @@ struct headless_backend {
|
|||||||
|
|
||||||
struct weston_seat fake_seat;
|
struct weston_seat fake_seat;
|
||||||
enum headless_renderer_type renderer_type;
|
enum headless_renderer_type renderer_type;
|
||||||
|
|
||||||
|
struct gl_renderer_interface *glri;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct headless_head {
|
struct headless_head {
|
||||||
@@ -67,6 +73,11 @@ struct headless_output {
|
|||||||
pixman_image_t *image;
|
pixman_image_t *image;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const uint32_t headless_formats[] = {
|
||||||
|
DRM_FORMAT_XRGB8888,
|
||||||
|
DRM_FORMAT_ARGB8888,
|
||||||
|
};
|
||||||
|
|
||||||
static inline struct headless_head *
|
static inline struct headless_head *
|
||||||
to_headless_head(struct weston_head *base)
|
to_headless_head(struct weston_head *base)
|
||||||
{
|
{
|
||||||
@@ -126,6 +137,15 @@ headless_output_repaint(struct weston_output *output_base,
|
|||||||
return 0;
|
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
|
static void
|
||||||
headless_output_disable_pixman(struct headless_output *output)
|
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);
|
wl_event_source_remove(output->finish_frame_timer);
|
||||||
|
|
||||||
switch (b->renderer_type) {
|
switch (b->renderer_type) {
|
||||||
|
case HEADLESS_GL:
|
||||||
|
headless_output_disable_gl(output);
|
||||||
|
break;
|
||||||
case HEADLESS_PIXMAN:
|
case HEADLESS_PIXMAN:
|
||||||
headless_output_disable_pixman(output);
|
headless_output_disable_pixman(output);
|
||||||
break;
|
break;
|
||||||
@@ -167,6 +190,24 @@ headless_output_destroy(struct weston_output *base)
|
|||||||
free(output);
|
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
|
static int
|
||||||
headless_output_enable_pixman(struct headless_output *output)
|
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);
|
wl_event_loop_add_timer(loop, finish_frame_handler, output);
|
||||||
|
|
||||||
switch (b->renderer_type) {
|
switch (b->renderer_type) {
|
||||||
|
case HEADLESS_GL:
|
||||||
|
ret = headless_output_enable_gl(output);
|
||||||
|
break;
|
||||||
case HEADLESS_PIXMAN:
|
case HEADLESS_PIXMAN:
|
||||||
ret = headless_output_enable_pixman(output);
|
ret = headless_output_enable_pixman(output);
|
||||||
break;
|
break;
|
||||||
@@ -339,6 +383,25 @@ headless_destroy(struct weston_compositor *ec)
|
|||||||
free(b);
|
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 = {
|
static const struct weston_windowed_output_api api = {
|
||||||
headless_output_set_size,
|
headless_output_set_size,
|
||||||
headless_head_create,
|
headless_head_create,
|
||||||
@@ -364,12 +427,22 @@ headless_backend_create(struct weston_compositor *compositor,
|
|||||||
b->base.destroy = headless_destroy;
|
b->base.destroy = headless_destroy;
|
||||||
b->base.create_output = headless_output_create;
|
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;
|
b->renderer_type = HEADLESS_PIXMAN;
|
||||||
else
|
else
|
||||||
b->renderer_type = HEADLESS_NOOP;
|
b->renderer_type = HEADLESS_NOOP;
|
||||||
|
|
||||||
switch (b->renderer_type) {
|
switch (b->renderer_type) {
|
||||||
|
case HEADLESS_GL:
|
||||||
|
ret = headless_gl_renderer_init(b);
|
||||||
|
break;
|
||||||
case HEADLESS_PIXMAN:
|
case HEADLESS_PIXMAN:
|
||||||
ret = pixman_renderer_init(compositor);
|
ret = pixman_renderer_init(compositor);
|
||||||
break;
|
break;
|
||||||
@@ -381,6 +454,13 @@ headless_backend_create(struct weston_compositor *compositor,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_input;
|
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
|
/* Support zwp_linux_explicit_synchronization_unstable_v1 to enable
|
||||||
* testing. */
|
* testing. */
|
||||||
if (linux_explicit_synchronization_setup(compositor) < 0)
|
if (linux_explicit_synchronization_setup(compositor) < 0)
|
||||||
|
|||||||
@@ -3142,6 +3142,48 @@ gl_renderer_output_window_create(struct weston_output *output,
|
|||||||
return ret;
|
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
|
static void
|
||||||
gl_renderer_output_destroy(struct weston_output *output)
|
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 = {
|
WL_EXPORT struct gl_renderer_interface gl_renderer_interface = {
|
||||||
.display_create = gl_renderer_display_create,
|
.display_create = gl_renderer_display_create,
|
||||||
.output_window_create = gl_renderer_output_window_create,
|
.output_window_create = gl_renderer_output_window_create,
|
||||||
|
.output_pbuffer_create = gl_renderer_output_pbuffer_create,
|
||||||
.output_destroy = gl_renderer_output_destroy,
|
.output_destroy = gl_renderer_output_destroy,
|
||||||
.output_set_border = gl_renderer_output_set_border,
|
.output_set_border = gl_renderer_output_set_border,
|
||||||
.create_fence_fd = gl_renderer_create_fence_fd,
|
.create_fence_fd = gl_renderer_create_fence_fd,
|
||||||
|
|||||||
@@ -135,6 +135,33 @@ struct gl_renderer_interface {
|
|||||||
const uint32_t *drm_formats,
|
const uint32_t *drm_formats,
|
||||||
unsigned drm_formats_count);
|
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);
|
void (*output_destroy)(struct weston_output *output);
|
||||||
|
|
||||||
/* Sets the output border.
|
/* Sets the output border.
|
||||||
|
|||||||
Reference in New Issue
Block a user