diff --git a/Makefile.am b/Makefile.am index 62719c9..a4fd066 100644 --- a/Makefile.am +++ b/Makefile.am @@ -328,7 +328,8 @@ endif if ENABLE_HEADLESS_COMPOSITOR module_LTLIBRARIES += headless-backend.la headless_backend_la_LDFLAGS = -module -avoid-version -headless_backend_la_LIBADD = $(COMPOSITOR_LIBS) libshared.la +# FIXME: -lgbm and -ludev set by configure +headless_backend_la_LIBADD = $(COMPOSITOR_LIBS) -lgbm -ludev libshared.la headless_backend_la_CFLAGS = $(COMPOSITOR_CFLAGS) $(AM_CFLAGS) headless_backend_la_SOURCES = \ src/compositor-headless.c \ diff --git a/src/compositor-headless.c b/src/compositor-headless.c index dba21a6..708f6ae 100644 --- a/src/compositor-headless.c +++ b/src/compositor-headless.c @@ -31,16 +31,34 @@ #include #include +/*XXX: ifdef? */ +//#include +#include +#include +#include +#include + #include "shared/helpers.h" #include "compositor.h" #include "pixman-renderer.h" +#include "gl-renderer.h" #include "presentation_timing-server-protocol.h" +enum headless_renderer { + RENDERER_NOOP, + RENDERER_PIXMAN, + RENDERER_RENDERNODE_GL, + /* TODO: to be implemented later + RENDERER_SOFTWARE_GL + */ +}; struct headless_backend { struct weston_backend base; struct weston_compositor *compositor; struct weston_seat fake_seat; - bool use_pixman; + enum headless_renderer renderer; + char *device; /* ownership shared with headless_parameters */ + struct gbm_device *gbm; }; struct headless_output { @@ -49,15 +67,40 @@ struct headless_output { struct wl_event_source *finish_frame_timer; uint32_t *image_buf; pixman_image_t *image; + struct gbm_surface *surface; }; struct headless_parameters { int width; int height; - int use_pixman; + enum headless_renderer renderer; + char *device; uint32_t transform; }; +struct gl_renderer_interface *gl_renderer; + +static int +headless_parse_renderer(const char *renderer, enum headless_renderer *out) +{ + if (renderer == NULL || (strcmp(renderer, "noop") == 0)) { + *out = RENDERER_NOOP; + } else if (strcmp(renderer, "pixman") == 0) { + *out = RENDERER_PIXMAN; + } else if (strcmp(renderer, "rendernode-gl") == 0) { + *out = RENDERER_RENDERNODE_GL; + /* TODO: to be implemented later + } else if (strcmp(renderer, "software-gl") == 0) { + *out = RENDERER_SOFTWARE_GL; + */ + } else { + *out = RENDERER_NOOP; + return -1; + } + + return 0; +} + static void headless_output_start_repaint_loop(struct weston_output *output) { @@ -85,9 +128,19 @@ headless_output_repaint(struct weston_output *output_base, { struct headless_output *output = (struct headless_output *) output_base; struct weston_compositor *ec = output->base.compositor; + struct headless_backend *b = (struct headless_backend *) ec->backend; + struct gbm_bo *bo; ec->renderer->repaint_output(&output->base, damage); + if (b->renderer == RENDERER_RENDERNODE_GL) { + bo = gbm_surface_lock_front_buffer(output->surface); + if (!bo) + return -1; + + gbm_surface_release_buffer(output->surface, bo); + } + pixman_region32_subtract(&ec->primary_plane.damage, &ec->primary_plane.damage, damage); @@ -105,10 +158,13 @@ headless_output_destroy(struct weston_output *output_base) wl_event_source_remove(output->finish_frame_timer); - if (b->use_pixman) { + if (b->renderer == RENDERER_PIXMAN) { pixman_renderer_output_destroy(&output->base); pixman_image_unref(output->image); free(output->image_buf); + } else if (b->renderer == RENDERER_RENDERNODE_GL) { + gl_renderer->output_destroy(output_base); + gbm_surface_destroy(output->surface); } weston_output_destroy(&output->base); @@ -119,6 +175,38 @@ headless_output_destroy(struct weston_output *output_base) } static int +headless_gl_renderer_output_create(struct headless_backend *b, + struct headless_output *output) +{ + EGLint format = GBM_FORMAT_XRGB8888; + + output->surface = gbm_surface_create(b->gbm, + output->mode.width, + output->mode.height, + format, + /* XXX: will it be useful in tests? + GBM_BO_USE_SCANOUT | + */ + GBM_BO_USE_RENDERING); + if (!output->surface) { + weston_log("failed to create gbm surface\n"); + return -1; + } + + if (gl_renderer->output_create(&output->base, + (EGLNativeWindowType)output->surface, + output->surface, + gl_renderer->opaque_attribs, + &format, 1) < 0) { + weston_log("gl_renderer_output_create failed.\n"); + gbm_surface_destroy(output->surface); + return -1; + } + + return 0; +} + +static int headless_backend_create_output(struct headless_backend *b, struct headless_parameters *param) { @@ -157,7 +245,8 @@ headless_backend_create_output(struct headless_backend *b, output->base.set_dpms = NULL; output->base.switch_mode = NULL; - if (b->use_pixman) { + switch (b->renderer) { + case RENDERER_PIXMAN: output->image_buf = malloc(param->width * param->height * 4); if (!output->image_buf) return -1; @@ -173,6 +262,13 @@ headless_backend_create_output(struct headless_backend *b, pixman_renderer_output_set_buffer(&output->base, output->image); + break; + case RENDERER_RENDERNODE_GL: + if (headless_gl_renderer_output_create(b, output) < 0) + return -1; + break; + default: + break; } weston_compositor_add_output(c, &output->base); @@ -199,6 +295,109 @@ headless_input_destroy(struct headless_backend *b) weston_seat_release(&b->fake_seat); } +static int +rendernode_get_default_fd(void) +{ + struct udev *udev; + struct udev_enumerate *e; + struct udev_list_entry *devices, *entry; + struct udev_device *device; + const char *path, *filename; + int fd; + + udev = udev_new(); + if (udev == NULL) { + weston_log("%s: failed to initialize udev context\n", + __func__); + goto fail; + } + + e = udev_enumerate_new(udev); + if (e == NULL) { + weston_log("%s: failed to initialize enumeration context\n", + __func__); + goto fail_unref; + } + + if (udev_enumerate_add_match_subsystem(e, "drm") < 0) { + weston_log("%s: failed to add match subsystem 'drm'\n", + __func__); + goto fail_enum_unref; + } + + if (udev_enumerate_add_match_sysname(e, "renderD[0-9]*") < 0) { + weston_log("%s: failed to add match sysname 'renderD[0-9]*'\n", + __func__); + goto fail_enum_unref; + } + + if (udev_enumerate_scan_devices(e) < 0) { + weston_log("%s: failed to scan devices\n", __func__); + goto fail_enum_unref; + } + + devices = udev_enumerate_get_list_entry(e); + if (devices == NULL) { + weston_log("%s: failed to get the first entry of the sorted" + " list of device paths\n", __func__); + goto fail_enum_unref; + } + + udev_list_entry_foreach(entry, devices) { + path = udev_list_entry_get_name(entry); + device = udev_device_new_from_syspath(udev, path); + filename = udev_device_get_devnode(device); + fd = open(filename, O_RDWR | O_CLOEXEC); + udev_device_unref(device); + if (fd >= 0) { + udev_enumerate_unref(e); + udev_unref(udev); + return fd; + } + } + +fail_enum_unref: + udev_enumerate_unref(e); +fail_unref: + udev_unref(udev); +fail: + return -1; +} + +static int +headless_gbm_create(struct headless_backend *b) +{ + int fd; + + if (b->device != NULL) + fd = open(b->device, O_RDWR | O_CLOEXEC); + else + fd = rendernode_get_default_fd(); + + if (fd < 0) { + weston_log("%s: failed to open render node\n", __func__); + return -1; + } + + b->gbm = gbm_create_device(fd); + if (b->gbm == NULL) { + close(fd); + return -1; + } + + return 0; +} + +static void +headless_gbm_destroy(struct gbm_device *gbm) +{ + int fd; + + fd = gbm_device_get_fd(gbm); + gbm_device_destroy(gbm); + close(fd); +} + static void headless_restore(struct weston_compositor *ec) { @@ -211,10 +410,47 @@ headless_destroy(struct weston_compositor *ec) headless_input_destroy(b); weston_compositor_shutdown(ec); + if (b->renderer == RENDERER_RENDERNODE_GL) + headless_gbm_destroy(b->gbm); + free(b->device); free(b); } +static int +headless_gl_renderer_init(struct headless_backend *b) +{ + EGLint format = GBM_FORMAT_XRGB8888; + + if (headless_gbm_create(b) < 0) { + weston_log("%s: failed to initialize gbm\n", __func__); + return -1; + } + + gl_renderer = weston_load_module("gl-renderer.so", + "gl_renderer_interface"); + if (!gl_renderer) { + weston_log("could not load gl renderer\n"); + goto err; + } + + if (gl_renderer->create(b->compositor, + EGL_PLATFORM_GBM_KHR, + (void *)b->gbm, + gl_renderer->opaque_attribs, + &format, 1) < 0) { + weston_log("gl_renderer_create failed\n" + STAMP_SPACE "run with EGL_LOG_LEVEL=debug environment" + " variable for more details\n"); + goto err; + } + + return 0; +err: + headless_gbm_destroy(b->gbm); + return -1; +} + static struct headless_backend * headless_backend_create(struct weston_compositor *compositor, struct headless_parameters *param, @@ -226,6 +462,7 @@ headless_backend_create(struct weston_compositor *compositor, if (b == NULL) return NULL; + b->device = param->device; b->compositor = compositor; if (weston_compositor_set_presentation_clock_software(compositor) < 0) goto err_free; @@ -236,14 +473,23 @@ headless_backend_create(struct weston_compositor *compositor, b->base.destroy = headless_destroy; b->base.restore = headless_restore; - b->use_pixman = param->use_pixman; - if (b->use_pixman) { - pixman_renderer_init(compositor); + b->renderer = param->renderer; + switch (b->renderer) { + case RENDERER_NOOP: + if (noop_renderer_init(compositor) < 0) + goto err_input; + break; + case RENDERER_PIXMAN: + if (pixman_renderer_init(compositor) < 0) + goto err_input; + break; + case RENDERER_RENDERNODE_GL: + if (headless_gl_renderer_init(b) < 0) + goto err_input; + break; } - if (headless_backend_create_output(b, param) < 0) - goto err_input; - if (!b->use_pixman && noop_renderer_init(compositor) < 0) + if (headless_backend_create_output(b, param) < 0) goto err_input; compositor->backend = &b->base; @@ -252,7 +498,12 @@ headless_backend_create(struct weston_compositor *compositor, err_input: weston_compositor_shutdown(compositor); headless_input_destroy(b); + + /* can't destroy before weston_compositor_shutdown */ + if (b->gbm != NULL) + headless_gbm_destroy(b->gbm); err_free: + free(b->device); free(b); return NULL; } @@ -262,24 +513,31 @@ backend_init(struct weston_compositor *compositor, int *argc, char *argv[], struct weston_config *config) { - int width = 1024, height = 640; char *display_name = NULL; - struct headless_parameters param = { 0, }; + struct headless_parameters param = { + .width = 1024, + .height = 640, + .device = NULL, + }; + char *renderer = NULL; const char *transform = "normal"; struct headless_backend *b; const struct weston_option headless_options[] = { - { WESTON_OPTION_INTEGER, "width", 0, &width }, - { WESTON_OPTION_INTEGER, "height", 0, &height }, - { WESTON_OPTION_BOOLEAN, "use-pixman", 0, ¶m.use_pixman }, + { WESTON_OPTION_INTEGER, "width", 0, ¶m.width }, + { WESTON_OPTION_INTEGER, "height", 0, ¶m.height }, + { WESTON_OPTION_STRING, "device", 0, ¶m.device }, + { WESTON_OPTION_STRING, "renderer", 0, &renderer }, { WESTON_OPTION_STRING, "transform", 0, &transform }, }; parse_options(headless_options, ARRAY_LENGTH(headless_options), argc, argv); - param.width = width; - param.height = height; + if (headless_parse_renderer(renderer, ¶m.renderer) < 0) + weston_log("invalid renderer \"%s\"\n" + STAMP_SPACE "falling back to no rendering\n", renderer); + free(renderer); if (weston_parse_transform(transform, ¶m.transform) < 0) weston_log("Invalid transform \"%s\"\n", transform); diff --git a/src/main.c b/src/main.c index a98570e..aefa58a 100644 --- a/src/main.c +++ b/src/main.c @@ -284,7 +284,12 @@ usage(int error_code) " --height=HEIGHT\tHeight of memory surface\n" " --transform=TR\tThe output transformation, TR is one of:\n" "\tnormal 90 180 270 flipped flipped-90 flipped-180 flipped-270\n" - " --use-pixman\t\tUse the pixman (CPU) renderer (default: no rendering)\n\n"); + " --renderer=RENDERER\tRenderer module, one of:\n" + "\t\t\tnoop\t\tno rendering (default)\n" + "\t\t\tpixman\t\tpixman (CPU) renderer\n" + "\t\t\trendernode-gl\thardware accelerated rendernode\n" + "\t\t\tsoftware-gl\tsoftware GL rendering\n" + " --device=DEVICE\tRender node to use by rendernode-gl renderer\n\n"); #endif #if defined(BUILD_RDP_COMPOSITOR) diff --git a/tests/buffer-count-test.c b/tests/buffer-count-test.c index 065249d..8714d06 100644 --- a/tests/buffer-count-test.c +++ b/tests/buffer-count-test.c @@ -36,6 +36,8 @@ #define fail(msg) { fprintf(stderr, "%s failed\n", msg); return -1; } +char *server_parameters="--renderer=rendernode-gl"; + struct test_data { struct client *client; diff --git a/tests/internal-screenshot-test.c b/tests/internal-screenshot-test.c index e72a695..ba2cccc 100644 --- a/tests/internal-screenshot-test.c +++ b/tests/internal-screenshot-test.c @@ -32,7 +32,7 @@ #include "weston-test-client-helper.h" -char *server_parameters="--use-pixman --width=320 --height=240"; +char *server_parameters="--renderer=rendernode-gl --width=320 --height=240"; /** write_surface_as_png() *