From: Benjamin Gaignard benjamin.gaignard@linaro.org
It is a simple example of how use wl_dmabuf protocol with weston
Signed-off-by: Benjamin Gaignard benjamin.gaignard@linaro.org --- clients/Makefile.am | 11 ++ clients/simple-dmabuf.c | 469 +++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 5 + 3 files changed, 485 insertions(+) create mode 100644 clients/simple-dmabuf.c
diff --git a/clients/Makefile.am b/clients/Makefile.am index 032d900..3840079 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -52,6 +52,17 @@ weston_multi_resource_SOURCES = multi-resource.c \ ../shared/os-compatibility.h weston_multi_resource_CPPFLAGS = $(SIMPLE_CLIENT_CFLAGS) weston_multi_resource_LDADD = $(SIMPLE_CLIENT_LIBS) -lm + +if BUILD_SIMPLE_CLIENT_DMABUF +simple_clients_programs += \ + weston-simple-dmabuf + +weston_simple_dmabuf_SOURCES = simple-dmabuf.c \ + ../shared/os-compatibility.c \ + ../shared/os-compatibility.h +weston_simple_dmabuf_CPPFLAGS = $(SIMPLE_CLIENT_CFLAGS) $(SIMPLE_CLIENT_DMABUF_CFLAGS) +weston_simple_dmabuf_LDADD = $(SIMPLE_CLIENT_LIBS) $(SIMPLE_CLIENT_DMABUF_LIBS) +endif endif
if BUILD_SIMPLE_EGL_CLIENTS diff --git a/clients/simple-dmabuf.c b/clients/simple-dmabuf.c new file mode 100644 index 0000000..7f381ae --- /dev/null +++ b/clients/simple-dmabuf.c @@ -0,0 +1,469 @@ +/* + * Copyright 2014 Benjamin Gaignard for Linaro + * Copyright © 2011 Benjamin Franzke + * Copyright © 2010 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <assert.h> +#include <unistd.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <signal.h> + +#include <wayland-client.h> +#include <wayland-dmabuf-client-protocol.h> +#include <xf86drm.h> + +#include "../shared/os-compatibility.h" + +struct display { + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_shell *shell; + struct wl_dmabuf *dmabuf; + uint32_t format; + int drm_fd; +}; + +struct buffer { + struct wl_buffer *buffer; + int fd; + int busy; + uint32_t size; + struct display *display; + void *dmabuf_data; +}; + +struct window { + struct display *display; + int width, height; + struct wl_surface *surface; + struct wl_shell_surface *shell_surface; + struct buffer buffers[2]; + struct buffer *prev_buffer; + struct wl_callback *callback; +}; + +static void +buffer_release(void *data, struct wl_buffer *buffer) +{ + struct buffer *mybuf = data; + + mybuf->busy = 0; +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_release +}; + +static int +create_dmabuf_buffer(struct display *display, struct buffer *buffer, + int width, int height, uint32_t format) +{ + struct drm_mode_create_dumb create_arg; + int ret; + int prime_fd; + uint32_t stride; + + memset (&create_arg, 0, sizeof (create_arg)); + create_arg.bpp = 32; + create_arg.width = width; + create_arg.height = height; + + ret = drmIoctl (display->drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg); + if (ret) + return ret; + + stride = create_arg.pitch; + + ret = drmPrimeHandleToFD (display->drm_fd, create_arg.handle, DRM_CLOEXEC, &prime_fd); + if (ret) + return ret; + + buffer->buffer = wl_dmabuf_create_prime_buffer (display->dmabuf, prime_fd, + width, height, format, stride); + + wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer); + + buffer->fd = prime_fd; + buffer->display = display; + + buffer->size = width * height * 4; + + buffer->dmabuf_data = mmap (0, buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, buffer->fd, 0); + + return 0; +} + +static void +destroy_dmabuf_buffer(struct buffer *buffer) +{ + struct display *display = buffer->display; + struct drm_mode_destroy_dumb destroy_arg; + + munmap (buffer->dmabuf_data, buffer->size); + + memset (&destroy_arg, 0, sizeof destroy_arg); + drmPrimeFDToHandle (display->drm_fd, buffer->fd, &destroy_arg.handle); + drmIoctl (display->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg); + close (buffer->fd); +} + +static void +handle_ping(void *data, struct wl_shell_surface *shell_surface, + uint32_t serial) +{ + wl_shell_surface_pong(shell_surface, serial); +} + +static void +handle_configure(void *data, struct wl_shell_surface *shell_surface, + uint32_t edges, int32_t width, int32_t height) +{ +} + +static void +handle_popup_done(void *data, struct wl_shell_surface *shell_surface) +{ +} + +static const struct wl_shell_surface_listener shell_surface_listener = { + handle_ping, + handle_configure, + handle_popup_done +}; + +static struct window * +create_window(struct display *display, int width, int height) +{ + struct window *window; + + window = calloc(1, sizeof *window); + if (!window) + return NULL; + + window->callback = NULL; + window->display = display; + window->width = width; + window->height = height; + window->surface = wl_compositor_create_surface(display->compositor); + window->shell_surface = wl_shell_get_shell_surface(display->shell, + window->surface); + + if (window->shell_surface) + wl_shell_surface_add_listener(window->shell_surface, + &shell_surface_listener, window); + + wl_shell_surface_set_title(window->shell_surface, "simple-dmabuf"); + + wl_shell_surface_set_toplevel(window->shell_surface); + + return window; +} + +static void +destroy_window(struct window *window) +{ + if (window->callback) + wl_callback_destroy(window->callback); + + if (window->buffers[0].buffer) + wl_buffer_destroy(window->buffers[0].buffer); + if (window->buffers[1].buffer) + wl_buffer_destroy(window->buffers[1].buffer); + + destroy_dmabuf_buffer(&window->buffers[0]); + destroy_dmabuf_buffer(&window->buffers[1]); + + wl_shell_surface_destroy(window->shell_surface); + wl_surface_destroy(window->surface); + free(window); +} + +static struct buffer * +window_next_buffer(struct window *window) +{ + struct buffer *buffer; + int ret = 0; + + if (!window->buffers[0].busy) + buffer = &window->buffers[0]; + else if (!window->buffers[1].busy) + buffer = &window->buffers[1]; + else + return NULL; + + if (!buffer->buffer) { + ret = create_dmabuf_buffer(window->display, buffer, + window->width, window->height, + WL_DMABUF_FORMAT_XRGB8888); + + if (ret) + return NULL; + + /* paint the padding */ + memset(buffer->dmabuf_data, 0xff, window->width * window->height * 4); + } + + return buffer; +} + +static void +paint_pixels(void *image, int padding, int width, int height, uint32_t time) +{ + const int halfh = padding + (height - padding * 2) / 2; + const int halfw = padding + (width - padding * 2) / 2; + int ir, or; + uint32_t *pixel = image; + int y; + + /* squared radii thresholds */ + or = (halfw < halfh ? halfw : halfh) - 8; + ir = or - 32; + or *= or; + ir *= ir; + + pixel += padding * width; + for (y = padding; y < height - padding; y++) { + int x; + int y2 = (y - halfh) * (y - halfh); + + pixel += padding; + for (x = padding; x < width - padding; x++) { + uint32_t v; + + /* squared distance from center */ + int r2 = (x - halfw) * (x - halfw) + y2; + + if (r2 < ir) + v = (r2 / 32 + time / 64) * 0x0080401; + else if (r2 < or) + v = (y + time / 32) * 0x0080401; + else + v = (x + time / 16) * 0x0080401; + v &= 0x00ffffff; + + /* cross if compositor uses X from XRGB as alpha */ + if (abs(x - y) > 6 && abs(x + y - height) > 6) + v |= 0xff000000; + + *pixel++ = v; + } + + pixel += padding; + } +} + +static const struct wl_callback_listener frame_listener; + +static void +redraw(void *data, struct wl_callback *callback, uint32_t time) +{ + struct window *window = data; + struct buffer *buffer; + + buffer = window_next_buffer(window); + if (!buffer) { + fprintf(stderr, + !callback ? "Failed to create the first buffer.\n" : + "Both buffers busy at redraw(). Server bug?\n"); + abort(); + } + + paint_pixels(buffer->dmabuf_data, 20, window->width, window->height, time); + + wl_surface_attach(window->surface, buffer->buffer, 0, 0); + wl_surface_damage(window->surface, + 20, 20, window->width - 40, window->height - 40); + + if (callback) + wl_callback_destroy(callback); + + window->callback = wl_surface_frame(window->surface); + wl_callback_add_listener(window->callback, &frame_listener, window); + wl_surface_commit(window->surface); + buffer->busy = 1; +} + +static const struct wl_callback_listener frame_listener = { + redraw +}; + +static void +dmabuf_handle_format(void *data, struct wl_dmabuf *wl_dmabuf, uint32_t format) +{ + struct display *d = data; + + fprintf(stderr, "server supported format %.4s\n", (char *) &format); + + if (format == WL_DMABUF_FORMAT_XRGB8888) + d->format = format; +} + +static void +dmabuf_handle_device (void *data, struct wl_dmabuf *wl_dmabuf, char *name) +{ + struct display *d = data; + + fprintf(stderr, "server %s\n", name); + d->drm_fd = open (name, O_RDWR, 0); +} + +struct wl_dmabuf_listener dmabuf_listener = { + dmabuf_handle_format, + dmabuf_handle_device +}; + +static void +registry_handle_global(void *data, struct wl_registry *registry, + uint32_t id, const char *interface, uint32_t version) +{ + struct display *d = data; + + if (strcmp(interface, "wl_compositor") == 0) { + d->compositor = + wl_registry_bind(registry, + id, &wl_compositor_interface, 1); + } else if (strcmp(interface, "wl_shell") == 0) { + d->shell = wl_registry_bind(registry, + id, &wl_shell_interface, 1); + } else if (strcmp(interface, "wl_dmabuf") == 0) { + d->dmabuf = wl_registry_bind(registry, + id, &wl_dmabuf_interface, 1); + wl_dmabuf_add_listener(d->dmabuf, &dmabuf_listener, d); + } +} + +static void +registry_handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global, + registry_handle_global_remove +}; + +static struct display * +create_display(void) +{ + struct display *display; + + display = malloc(sizeof *display); + if (display == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + display->display = wl_display_connect(NULL); + assert(display->display); + + display->format = 0; + display->drm_fd = -1; + display->registry = wl_display_get_registry(display->display); + wl_registry_add_listener(display->registry, + ®istry_listener, display); + wl_display_roundtrip(display->display); + if (display->dmabuf == NULL) { + fprintf(stderr, "No wl_dmabuf global\n"); + exit(1); + } + + wl_display_roundtrip(display->display); + + if (display->format != WL_DMABUF_FORMAT_XRGB8888) { + fprintf(stderr, "WL_DMABUF_FORMAT_XRGB8888 not available\n"); + exit(1); + } + + wl_display_get_fd(display->display); + + return display; +} + +static void +destroy_display(struct display *display) +{ + if (display->dmabuf) + wl_dmabuf_destroy(display->dmabuf); + + if (display->shell) + wl_shell_destroy(display->shell); + + if (display->compositor) + wl_compositor_destroy(display->compositor); + + wl_registry_destroy(display->registry); + wl_display_flush(display->display); + wl_display_disconnect(display->display); + close(display->drm_fd); + free(display); +} + +static int running = 1; + +static void +signal_int(int signum) +{ + running = 0; +} + +int +main(int argc, char **argv) +{ + struct sigaction sigint; + struct display *display; + struct window *window; + int ret = 0; + + display = create_display(); + window = create_window(display, 250, 250); + if (!window) + return 1; + + sigint.sa_handler = signal_int; + sigemptyset(&sigint.sa_mask); + sigint.sa_flags = SA_RESETHAND; + sigaction(SIGINT, &sigint, NULL); + + /* Initialise damage to full surface, so the padding gets painted */ + wl_surface_damage(window->surface, 0, 0, + window->width, window->height); + + redraw(window, NULL, 0); + + while (running && ret != -1) + ret = wl_display_dispatch(display->display); + + fprintf(stderr, "simple-dmabuf exiting\n"); + destroy_window(window); + destroy_display(display); + + return 0; +} diff --git a/configure.ac b/configure.ac index 50bce23..da445f3 100644 --- a/configure.ac +++ b/configure.ac @@ -271,6 +271,11 @@ AC_ARG_ENABLE(simple-clients, AM_CONDITIONAL(BUILD_SIMPLE_CLIENTS, test "x$enable_simple_clients" = "xyes") if test x$enable_simple_clients = xyes; then PKG_CHECK_MODULES(SIMPLE_CLIENT, [wayland-client]) + AC_CHECK_HEADER([wayland-dmabuf.h], + [ PKG_CHECK_MODULES(SIMPLE_CLIENT_DMABUF, [libdrm libkms] , + [have_dmabuf=yes], [have_dmabuf=no])], + [have_dmabuf=no]) + AM_CONDITIONAL(BUILD_SIMPLE_CLIENT_DMABUF, test "x$have_dmabuf" = "xyes") fi
AC_ARG_ENABLE(simple-egl-clients,