From a42bd8f78d4a8325263a2e4a96ebe40e3b7ffe66 Mon Sep 17 00:00:00 2001 From: thead_admin Date: Tue, 13 Sep 2022 14:38:03 +0800 Subject: [PATCH] Linux_SDK_V0.9.5 --- COPYING | 19 ++ README.txt | 9 + gtk01/gtk01.c | 65 ++++++ gtk01/meson.build | 9 + meson.build | 15 ++ wl-tester/buffers.c | 400 +++++++++++++++++++++++++++++++++ wl-tester/buffers.h | 66 ++++++ wl-tester/common.c | 506 ++++++++++++++++++++++++++++++++++++++++++ wl-tester/common.h | 47 ++++ wl-tester/meson.build | 41 ++++ wl-tester/tester.c | 107 +++++++++ 11 files changed, 1284 insertions(+) create mode 100644 COPYING create mode 100644 README.txt create mode 100644 gtk01/gtk01.c create mode 100644 gtk01/meson.build create mode 100644 meson.build create mode 100644 wl-tester/buffers.c create mode 100644 wl-tester/buffers.h create mode 100644 wl-tester/common.c create mode 100644 wl-tester/common.h create mode 100644 wl-tester/meson.build create mode 100644 wl-tester/tester.c diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..c03e089 --- /dev/null +++ b/COPYING @@ -0,0 +1,19 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..eeb5848 --- /dev/null +++ b/README.txt @@ -0,0 +1,9 @@ +== Build +TBD + +== Run "gtk01" Tests +TBD + +== Run "wl-tester" Tests +1. Prepare a YUV raw file, you can convert it by ffmpeg. +2. Run wl-tester in target device and check Wayland Compositor's UI. diff --git a/gtk01/gtk01.c b/gtk01/gtk01.c new file mode 100644 index 0000000..968e390 --- /dev/null +++ b/gtk01/gtk01.c @@ -0,0 +1,65 @@ +/* +* Copyright © Jan Newmarch, jan@newmarch.name +* https://jan.newmarch.name/Wayland/GTK/ +*/ + +#include + +gint count = 0; +char buf[5]; + +void increase(GtkWidget *widget, gpointer label) { + count++; + + sprintf(buf, "%d", count); + gtk_label_set_text(GTK_LABEL(label), buf); +} + +void decrease(GtkWidget *widget, gpointer label) { + count--; + + sprintf(buf, "%d", count); + gtk_label_set_text(GTK_LABEL(label), buf); +} + +int main(int argc, char **argv) { + + GtkWidget *label; + GtkWidget *window; + GtkWidget *frame; + GtkWidget *plus; + GtkWidget *minus; + + gtk_init(&argc, &argv); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS); + gtk_window_set_default_size(GTK_WINDOW(window), 250, 180); + gtk_window_set_title(GTK_WINDOW(window), "+-"); + + frame = gtk_fixed_new(); + gtk_container_add(GTK_CONTAINER(window), frame); + + plus = gtk_button_new_with_label("+"); + gtk_widget_set_size_request(plus, 80, 35); + gtk_fixed_put(GTK_FIXED(frame), plus, 50, 20); + + minus = gtk_button_new_with_label("-"); + gtk_widget_set_size_request(minus, 80, 35); + gtk_fixed_put(GTK_FIXED(frame), minus, 50, 80); + + label = gtk_label_new("0"); + gtk_fixed_put(GTK_FIXED(frame), label, 190, 58); + + gtk_widget_show_all(window); + + g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); + + g_signal_connect(plus, "clicked", G_CALLBACK(increase), label); + + g_signal_connect(minus, "clicked", G_CALLBACK(decrease), label); + + gtk_main(); + + return 0; +} diff --git a/gtk01/meson.build b/gtk01/meson.build new file mode 100644 index 0000000..27bcfb4 --- /dev/null +++ b/gtk01/meson.build @@ -0,0 +1,9 @@ +gtk01_sources = files( + 'gtk01.c', +) + +cc = meson.get_compiler('c') +dep_gtk3 = dependency('gtk+-3.0', version : '>=3.20') +dep_common = [dep_gtk3] + +executable('gtk01', gtk01_sources, dependencies : dep_common, install : true) diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..95eb94f --- /dev/null +++ b/meson.build @@ -0,0 +1,15 @@ +project( + 'gfx-examples', + 'c', + version : '0.0.1', + license : 'MIT', + meson_version : '>= 0.47', + default_options : ['c_std=gnu99', 'warning_level=2'] +) + +if get_option('c_std') != 'gnu99' + error('c_std must be gnu99') +endif + +subdir('gtk01') +subdir('wl-tester') diff --git a/wl-tester/buffers.c b/wl-tester/buffers.c new file mode 100644 index 0000000..691b9e2 --- /dev/null +++ b/wl-tester/buffers.c @@ -0,0 +1,400 @@ +/* + * Source updates from: + * - https://wayland-book.com/xdg-shell-basics/example-code.html + * - libdrm's "modetest" + */ + + /* + * DRM based mode setting test program + * Copyright 2008 Tungsten Graphics + * Jakob Bornecrantz + * Copyright 2008 Intel Corporation + * Jesse Barnes + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drm/drm.h" +#include "drm/drm_fourcc.h" +#include "xf86drm.h" + +#include "buffers.h" + +long int raw_offset = 0; + +static void randname(char *buf) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + long r = ts.tv_nsec; + for (int i = 0; i < 6; ++i) { + buf[i] = 'A' + (r & 15) + (r & 16) * 2; + r >>= 5; + } +} + +static int create_shm_file(void) { + int retries = 100; + do { + char name[] = "/wl_shm-XXXXXX"; + randname(name + sizeof(name) - 7); + --retries; + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + shm_unlink(name); + return fd; + } + } while (retries > 0 && errno == EEXIST); + return -1; +} + +int allocate_shm_file(size_t size) { + int fd = create_shm_file(); + if (fd < 0) + return -1; + int ret; + do { + ret = ftruncate(fd, size); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + close(fd); + return -1; + } + return fd; +} + +int fill_with_raw(FILE *fp, uint32_t width, uint32_t height, + uint8_t *data) { + fseek(fp, 0, SEEK_END); + uint32_t file_size = ftell(fp); + uint32_t frame_size = (width * height + 2 * ((width + 1) / 2) * ((height + 1) / 2)); + fseek(fp, raw_offset, SEEK_SET); + + size_t result = fread(data, 1, frame_size, fp); + if (result != frame_size) { + fprintf(stderr, "Error reading yuv image\n"); + return -1; + } + + raw_offset = raw_offset + frame_size; + if (raw_offset >= file_size) + raw_offset = 0; + return 0; +} + +struct bo *bo_create_dumb(int fd, unsigned int width, + unsigned int height, unsigned int bpp) { + struct drm_mode_create_dumb arg; + struct bo *bo; + int ret; + + bo = calloc(1, sizeof(*bo)); + if (bo == NULL) { + fprintf(stderr, "failed to allocate buffer object\n"); + return NULL; + } + + memset(&arg, 0, sizeof(arg)); + arg.bpp = bpp; + arg.width = width; + arg.height = height; + + ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &arg); + if (ret) { + fprintf(stderr, "failed to create dumb buffer: %s\n", strerror(errno)); + free(bo); + return NULL; + } + + bo->fd = fd; + bo->handle = arg.handle; + bo->size = arg.size; + bo->pitch = arg.pitch; + + return bo; +} + +int bo_map(struct bo *bo, void **out) { + struct drm_mode_map_dumb arg; + void *map; + int ret; + + memset(&arg, 0, sizeof(arg)); + arg.handle = bo->handle; + + ret = drmIoctl(bo->fd, DRM_IOCTL_MODE_MAP_DUMB, &arg); + if (ret) + return ret; + + map = + mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED, bo->fd, arg.offset); + + bo->ptr = map; + *out = map; + + return 0; +} + +void bo_unmap(struct bo *bo) { + if (!bo->ptr) + return; + + munmap(bo->ptr, bo->size); + bo->ptr = NULL; +} + +struct bo *bo_create(int fd, unsigned int format, unsigned int width, + unsigned int height, unsigned int handles[4], + unsigned int pitches[4], unsigned int offsets[4]) { + unsigned int virtual_height; + struct bo *bo; + unsigned int bpp; + void *planes[3] = { + 0, + }; + void *virtual; + int ret; + + switch (format) { + case DRM_FORMAT_C8: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + bpp = 8; + break; + + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_BGRX4444: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_BGRA5551: + case DRM_FORMAT_BGRX5551: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + bpp = 16; + break; + + case DRM_FORMAT_BGR888: + case DRM_FORMAT_RGB888: + bpp = 24; + break; + + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_RGBX1010102: + case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_BGRX1010102: + bpp = 32; + break; + + case DRM_FORMAT_XRGB16161616F: + case DRM_FORMAT_XBGR16161616F: + case DRM_FORMAT_ARGB16161616F: + case DRM_FORMAT_ABGR16161616F: + bpp = 64; + break; + + default: + fprintf(stderr, "unsupported format 0x%08x\n", format); + return NULL; + } + + switch (format) { + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + virtual_height = height * 3 / 2; + break; + + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + virtual_height = height * 2; + break; + + default: + virtual_height = height; + break; + } + + bo = bo_create_dumb(fd, width, virtual_height, bpp); + if (!bo) + return NULL; + + ret = bo_map(bo, &virtual); + if (ret) { + fprintf(stderr, "failed to map buffer: %s\n", strerror(-errno)); + bo_destroy(bo); + return NULL; + } + + /* just testing a limited # of formats to test single + * and multi-planar path.. would be nice to add more.. + */ + switch (format) { + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVYU: + offsets[0] = 0; + handles[0] = bo->handle; + pitches[0] = bo->pitch; + + planes[0] = virtual; + break; + + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV61: + offsets[0] = 0; + handles[0] = bo->handle; + pitches[0] = bo->pitch; + pitches[1] = pitches[0]; + offsets[1] = pitches[0] * height; + handles[1] = bo->handle; + + planes[0] = virtual; + planes[1] = virtual + offsets[1]; + break; + + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YVU420: + offsets[0] = 0; + handles[0] = bo->handle; + pitches[0] = bo->pitch; + pitches[1] = pitches[0] / 2; + offsets[1] = pitches[0] * height; + handles[1] = bo->handle; + pitches[2] = pitches[1]; + offsets[2] = offsets[1] + pitches[1] * height / 2; + handles[2] = bo->handle; + + planes[0] = virtual; + planes[1] = virtual + offsets[1]; + planes[2] = virtual + offsets[2]; + break; + + case DRM_FORMAT_C8: + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_BGRX4444: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_BGRA5551: + case DRM_FORMAT_BGRX5551: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + case DRM_FORMAT_BGR888: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_RGBX1010102: + case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_BGRX1010102: + case DRM_FORMAT_XRGB16161616F: + case DRM_FORMAT_XBGR16161616F: + case DRM_FORMAT_ARGB16161616F: + case DRM_FORMAT_ABGR16161616F: + offsets[0] = 0; + handles[0] = bo->handle; + pitches[0] = bo->pitch; + + planes[0] = virtual; + break; + } + + bo_unmap(bo); + + return bo; +} + +void bo_destroy(struct bo *bo) { + struct drm_mode_destroy_dumb arg; + int ret; + + memset(&arg, 0, sizeof(arg)); + arg.handle = bo->handle; + + ret = drmIoctl(bo->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &arg); + if (ret) + fprintf(stderr, "failed to destroy dumb buffer: %s\n", strerror(errno)); + + free(bo); +} diff --git a/wl-tester/buffers.h b/wl-tester/buffers.h new file mode 100644 index 0000000..8e53771 --- /dev/null +++ b/wl-tester/buffers.h @@ -0,0 +1,66 @@ +/* + * DRM based mode setting test program + * Copyright 2008 Tungsten Graphics + * Jakob Bornecrantz + * Copyright 2008 Intel Corporation + * Jesse Barnes + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __BUFFERS_H__ +#define __BUFFERS_H__ + +#define FORMAT_MAX_PLANES 4 + +struct fb_buffer { + struct wl_buffer *buffer; + struct bo *bo; + void *buf_vma; + bool busy; +}; + +struct shm_buffer { + struct wl_buffer *buffer; + struct wl_shm *wl_shm; + void *buf_vma; + int fd; + bool busy; +}; + +struct bo { + int fd; + void *ptr; + size_t size; + size_t offset; + size_t pitch; + unsigned handle; +}; + +int allocate_shm_file(size_t size); +int fill_with_raw(FILE *fp, uint32_t width, uint32_t height, uint8_t *data); + +int bo_map(struct bo *bo, void **out); +void bo_unmap(struct bo *bo); +struct bo *bo_create(int fd, unsigned int format, unsigned int width, + unsigned int height, unsigned int handles[4], + unsigned int pitches[4], unsigned int offsets[4]); +void bo_destroy(struct bo *bo); + +#endif diff --git a/wl-tester/common.c b/wl-tester/common.c new file mode 100644 index 0000000..7c2b72f --- /dev/null +++ b/wl-tester/common.c @@ -0,0 +1,506 @@ +/* + * Source updates from: + * - https://wayland-book.com/xdg-shell-basics/example-code.html + * - weston's clients examples + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drm/drm.h" +#include "drm/drm_fourcc.h" +#include "xf86drm.h" + +#include "linux-dmabuf-unstable-v1-client-protocol.h" +#include "xdg-shell-client-protocol.h" +#include +#include "viewporter-client-protocol.h" +#include "xdg-output-unstable-v1-client-protocol.h" + +#include "buffers.h" +#include "common.h" + +int ms_buf_index = 0; +int raw_frame_index = 0; +static const int benchmark_interval = 5; + +static void ms_flip(void *data, struct wl_callback *callback, uint32_t time); + +static void wl_buffer_release(void *data, struct wl_buffer *wl_buffer) { +} + +const struct wl_buffer_listener wl_buffer_listener = { + .release = wl_buffer_release, +}; + +static void fb_buffer_release(void *data, struct wl_buffer *wl_buffer) { + struct fb_buffer *buffer = data; + + buffer->busy = false; +} + +const struct wl_buffer_listener fb_buffer_listener = { + .release = fb_buffer_release, +}; + +static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, + uint32_t serial) { + + struct example_window *window = data; + if ( !window->wait_for_configure) + return; + + xdg_surface_ack_configure(xdg_surface, serial); + + if (window->initialized && window->wait_for_configure) { + ms_flip(data, NULL, 0); + } + + window->wait_for_configure = false; +} + +const struct xdg_surface_listener xdg_surface_listener = { + .configure = xdg_surface_configure, +}; + +static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, + uint32_t serial) { + xdg_wm_base_pong(xdg_wm_base, serial); +} + +const struct xdg_wm_base_listener xdg_wm_base_listener = { + .ping = xdg_wm_base_ping, +}; + +static void dmabuf_modifier(void *data, + struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, + uint32_t format, uint32_t modifier_hi, + uint32_t modifier_lo) {} + +static void dmabuf_format(void *data, + struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, + uint32_t format) {} + +const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = { + .format = dmabuf_format, + .modifier = dmabuf_modifier, +}; + +/* Start of XDG Output */ +/* Can got output infos to future usages */ + +static void +display_handle_geometry(void *data, + struct wl_output *wl_output, + int x, int y, + int physical_width, + int physical_height, + int subpixel, + const char *make, + const char *model, + int transform) +{ + //printf("YG: x=%d, y=%d, p-width=%d, p-height=%d, subpixel=%d, transform=%d, model=\"%s\"\n", x, y, + // physical_width, physical_height, subpixel, transform, model); +} + +static void +display_handle_done(void *data, + struct wl_output *wl_output) +{ +} + +static void +display_handle_scale(void *data, + struct wl_output *wl_output, + int32_t scale) +{ +} + +static void +display_handle_mode(void *data, + struct wl_output *wl_output, + uint32_t flags, + int width, + int height, + int refresh) +{ + //printf("YG:width=%d, height=%d, flags=%d, refresh=%d\n", width, height, flags, refresh); +} + +static const struct wl_output_listener output_listener = { + display_handle_geometry, + display_handle_mode, + display_handle_done, + display_handle_scale +}; + +static void +window_add_output(struct example_window *window, uint32_t id) +{ + struct output *output; + + output = malloc(sizeof *output); + memset(output, 0, sizeof *output); + output->output = wl_registry_bind(window->wl_registry, id, &wl_output_interface, 2); + //wl_list_insert(window->output_list.prev, &output->link); + + wl_output_add_listener(output->output, &output_listener, output); +} + +static void +handle_xdg_output_v1_logical_position(void *data, struct zxdg_output_v1 *output, + int32_t x, int32_t y) +{ +} + +static void +handle_xdg_output_v1_logical_size(void *data, struct zxdg_output_v1 *output, + int32_t width, int32_t height) +{ +} + +static void +handle_xdg_output_v1_done(void *data, struct zxdg_output_v1 *output) +{ +} + +static void +handle_xdg_output_v1_name(void *data, struct zxdg_output_v1 *output, + const char *name) +{ +} + +static void +handle_xdg_output_v1_description(void *data, struct zxdg_output_v1 *output, + const char *description) +{ +} + +static const struct zxdg_output_v1_listener xdg_output_v1_listener = { + .logical_position = handle_xdg_output_v1_logical_position, + .logical_size = handle_xdg_output_v1_logical_size, + .done = handle_xdg_output_v1_done, + .name = handle_xdg_output_v1_name, + .description = handle_xdg_output_v1_description, +}; + +static void +add_xdg_output_manager_v1_info(struct example_window *window, uint32_t id, uint32_t version) +{ + window->xdg_output_manager = wl_registry_bind(window->wl_registry, id, + &zxdg_output_manager_v1_interface, version > 2 ? 2 : version); +} +/* End of XDG Output */ + +static void registry_global(void *data, struct wl_registry *wl_registry, + uint32_t name, const char *interface, + uint32_t version) { + struct example_window *window = data; + if (strcmp(interface, wl_shm_interface.name) == 0) { + window->ss_buffer.wl_shm = wl_registry_bind(wl_registry, name, &wl_shm_interface, 1); + } else if (strcmp(interface, wl_compositor_interface.name) == 0) { + window->wl_compositor = + wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4); + } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + window->xdg_wm_base = + wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(window->xdg_wm_base, &xdg_wm_base_listener, window); + } else if (strcmp(interface, wl_subcompositor_interface.name) == 0) { + window->wl_subcompositor = + wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, 1); + } else if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) { + window->dmabuf = + wl_registry_bind(wl_registry, name, &zwp_linux_dmabuf_v1_interface, 3); + zwp_linux_dmabuf_v1_add_listener(window->dmabuf, &dmabuf_listener, window); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + window_add_output(window, name); + } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) + add_xdg_output_manager_v1_info(window, name, version); +} + +static void registry_global_remove(void *data, struct wl_registry *wl_registry, + uint32_t name) {} + +const struct wl_registry_listener wl_registry_listener = { + .global = registry_global, + .global_remove = registry_global_remove, +}; + +static const struct wl_callback_listener frame_listener = { + ms_flip +}; + +static void ms_flip(void *data, struct wl_callback *callback, uint32_t time) { + struct example_window *window = data; + + while(1){ + if (ms_buf_index >= FB_BUFFER_NUM) + ms_buf_index = 0; + + if (!window->ms_buffers[ms_buf_index].busy){ + break; + } + ms_buf_index++; + } + + if (!window->noread){ + int ret = fill_with_raw(window->raw_fp, window->width, window->height, (uint8_t *)(window->ms_buffers[ms_buf_index].buf_vma)); + if (ret != 0) { + fprintf(stderr, "failed to fill the buf\n"); + } + } + + wl_surface_attach(window->wl_surface, window->ms_buffers[ms_buf_index].buffer, 0, 0); + wl_surface_damage(window->wl_surface, 0, 0, window->width, window->height); + + if (callback) + wl_callback_destroy(callback); + + window->callback = wl_surface_frame(window->wl_surface); + wl_callback_add_listener(window->callback, &frame_listener, window); + wl_surface_commit(window->wl_surface); + + window->ms_buffers[ms_buf_index].busy = true; + + struct timeval tv; + gettimeofday(&tv, NULL); + time = tv.tv_sec * 1000 + tv.tv_usec / 1000; + if (window->frames_num == 0) + window->benchmark_time = time; + + if (time - window->benchmark_time > (benchmark_interval * 1000)) { + printf("%d frames in %d seconds: %f fps\n", + window->frames_num, + benchmark_interval, + (float) window->frames_num / benchmark_interval); + window->benchmark_time = time; + window->frames_num = 0; + } + window->frames_num++; + return; +} + +static void create_succeeded(void *data, + struct zwp_linux_buffer_params_v1 *params, + struct wl_buffer *new_buffer) { + zwp_linux_buffer_params_v1_destroy(params); +} + +static void create_failed(void *data, + struct zwp_linux_buffer_params_v1 *params) { + zwp_linux_buffer_params_v1_destroy(params); + fprintf(stderr, "Error: zwp_linux_buffer_params.create failed.\n"); +} + +static const struct zwp_linux_buffer_params_v1_listener params_listener = { + create_succeeded, create_failed}; + + +int init_buffers(struct example_window *window) { + int ret = 0; + + window->drm_dev_fd = open(window->drm_node, O_RDWR); + if (window->drm_dev_fd < 0) { + fprintf(stderr, "failed to open device %s: %s\n", window->drm_node, strerror(errno)); + return -1; + } + + //Init main surface buffers + for(int i_buf = 0; i_buf < FB_BUFFER_NUM; i_buf++){ + uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; + + window->ms_buffers[i_buf].bo = bo_create(window->drm_dev_fd, DRM_FORMAT_NV12, window->width, window->height, handles, pitches, offsets); + window->ms_buffers[i_buf].busy = false; + if (!window->ms_buffers[i_buf].bo) { + fprintf(stderr, "failed to create bo\n"); + close(window->drm_dev_fd); + return -1; + } + + //TODO: need unmap in handling of exception or exit + ret = bo_map(window->ms_buffers[i_buf].bo, &(window->ms_buffers[i_buf].buf_vma)); + if (ret) { + fprintf(stderr, "failed to map buffer: %s\n", strerror(-errno)); + bo_destroy(window->ms_buffers[i_buf].bo); + return -1; + } + + struct zwp_linux_buffer_params_v1 *params; + params = zwp_linux_dmabuf_v1_create_params(window->dmabuf); + + int fd_plane = 0; + for (int i = 0; i < FORMAT_MAX_PLANES; ++i) { + if (!handles[i]) + continue; + + int ret = drmPrimeHandleToFD(window->drm_dev_fd, handles[i], 0, &fd_plane); + if (ret < 0 || fd_plane < 0) { + fprintf(stderr, "error: failed to get dmabuf_fd\n"); + bo_destroy(window->ms_buffers[i_buf].bo); + close(window->drm_dev_fd); + return -1; + } + + zwp_linux_buffer_params_v1_add(params, fd_plane, i, offsets[i], pitches[i], + DRM_FORMAT_MOD_INVALID >> 32, + DRM_FORMAT_MOD_INVALID & 0xffffffff); + } + + uint32_t flags = 0; + zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, window); + window->ms_buffers[i_buf].buffer = zwp_linux_buffer_params_v1_create_immed(params, window->width, window->height, WL_SHM_FORMAT_NV12, flags); + wl_buffer_add_listener(window->ms_buffers[i_buf].buffer, &fb_buffer_listener, &(window->ms_buffers[i_buf])); + } + + //Init sub surface buffers + if (window->show_bar) { + int ss_stride = window->width * 4; + int ss_height = window->width/5; + int ss_size = ss_stride * (ss_height); + + if (window->ss_usedma){ + uint32_t ss_handles[4] = {0}, ss_pitches[4] = {0}, ss_offsets[4] = {0}; + //Using BGR as DMA Buf in Light can't support RGB + struct bo *ss_bo = bo_create(window->drm_dev_fd, DRM_FORMAT_ARGB8888, window->width, ss_height, ss_handles, ss_pitches, ss_offsets); + if (!ss_bo) { + fprintf(stderr, "failed to create ss_bo\n"); + close(window->drm_dev_fd); + return -1; + } + + ret = bo_map(ss_bo, &(window->ss_buffer.buf_vma)); + if (ret) { + fprintf(stderr, "failed to map buffer: %s\n", strerror(-errno)); + bo_destroy(ss_bo); + return -1; + } + + struct zwp_linux_buffer_params_v1 *ss_params; + ss_params = zwp_linux_dmabuf_v1_create_params(window->dmabuf); + + int ss_fd_plane = 0; + for (int i = 0; i < FORMAT_MAX_PLANES; ++i) { + if (!ss_handles[i]) + continue; + + ret = drmPrimeHandleToFD(window->drm_dev_fd, ss_handles[i], 0, &ss_fd_plane); + if (ret < 0 || ss_fd_plane < 0) { + fprintf(stderr, "error: failed to get ss dmabuf fd\n"); + bo_destroy(ss_bo); + close(window->drm_dev_fd); + return -1; + } + + zwp_linux_buffer_params_v1_add(ss_params, ss_fd_plane, i, ss_offsets[i], ss_pitches[i], + DRM_FORMAT_MOD_INVALID >> 32, + DRM_FORMAT_MOD_INVALID & 0xffffffff); + } + + uint32_t ss_flags = 0; + zwp_linux_buffer_params_v1_add_listener(ss_params, ¶ms_listener, window); + window->ss_buffer.buffer = zwp_linux_buffer_params_v1_create_immed(ss_params, window->width, + ss_height, 0x34325241, ss_flags); + } else { + window->ss_buffer.fd = allocate_shm_file(ss_size); + if (window->ss_buffer.fd == -1) { + return -1; + } + + struct wl_shm_pool *pool = wl_shm_create_pool(window->ss_buffer.wl_shm, window->ss_buffer.fd, ss_size); + window->ss_buffer.buffer = wl_shm_pool_create_buffer(pool, 0, window->width, ss_height, ss_stride, WL_SHM_FORMAT_XRGB8888); + wl_shm_pool_destroy(pool); + + window->ss_buffer.buf_vma = mmap(NULL, ss_size, PROT_READ | PROT_WRITE, MAP_SHARED, window->ss_buffer.fd, 0); + if (window->ss_buffer.buf_vma == MAP_FAILED) { + close(window->ss_buffer.fd); + return -1; + } + } + + //Start fill sub surface buffer + for (int y = 0; y < ss_height; ++y) { + for (int x = 0; x < window->width; ++x) { + if ((x + y / 8 * 8) % 16 < 8) + ((uint32_t *)window->ss_buffer.buf_vma)[y * window->width + x] = 0xFF666666; + else + ((uint32_t *)window->ss_buffer.buf_vma)[y * window->width + x] = 0xFFEEEEEE; + } + } + + munmap(window->ss_buffer.buf_vma, ss_size); + + wl_surface_attach(window->wl_subsurface, window->ss_buffer.buffer, 0, 0); + wl_surface_damage(window->wl_subsurface, 0, 0, window->width, ss_height); + wl_surface_commit(window->wl_subsurface); + wl_buffer_add_listener(window->ss_buffer.buffer, &wl_buffer_listener, NULL); + } + + return 0; +} + + +int init_window(struct example_window *window) { + + window->frames_num = 0; + window->wl_display = wl_display_connect(NULL); + window->wl_registry = wl_display_get_registry(window->wl_display); + wl_registry_add_listener(window->wl_registry, &wl_registry_listener, window); + wl_display_roundtrip(window->wl_display); + + window->wl_surface = wl_compositor_create_surface(window->wl_compositor); + window->wl_subsurface = + wl_compositor_create_surface(window->wl_compositor); + window->subsurface = wl_subcompositor_get_subsurface( + window->wl_subcompositor, window->wl_subsurface, window->wl_surface); + + //we are using xdg_surface_set_window_geometry() to set window position now. + //wl_subsurface_set_position(window->subsurface, window->x, window->y); + + window->xdg_surface = + xdg_wm_base_get_xdg_surface(window->xdg_wm_base, window->wl_surface); + xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window); + window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface); + xdg_toplevel_set_title(window->xdg_toplevel, + "Graphics Wayland Buf Display Example"); + xdg_toplevel_set_app_id(window->xdg_toplevel, "wl-tester"); + xdg_surface_set_window_geometry(window->xdg_surface, window->x, window->y, window->width, window->height); + + //Not sure if it is workable? + //xdg_toplevel_set_fullscreen(); + + window->wait_for_configure = true; + if (window->sync) + wl_subsurface_set_sync(window->subsurface); + else + wl_subsurface_set_desync(window->subsurface); + + wl_surface_commit(window->wl_surface); + + int ret = init_buffers(window); + if (ret != 0){ + fprintf(stderr, "Error init buffers\n"); + return -1; + } + wl_display_roundtrip(window->wl_display); + + if (!window->wait_for_configure){ + ms_flip(window, NULL, 0); + } + window->initialized = true; + + while (wl_display_dispatch(window->wl_display)) { + } + + return 0; +} diff --git a/wl-tester/common.h b/wl-tester/common.h new file mode 100644 index 0000000..0dfc83e --- /dev/null +++ b/wl-tester/common.h @@ -0,0 +1,47 @@ +#ifndef TESTER_COMMON_H +#define TESTER_COMMON_H + +#define FB_BUFFER_NUM 3 + +struct output { + struct wl_output *output; + struct wl_list link; +}; + +struct example_window { + struct wl_display *wl_display; + char *drm_node; + int drm_dev_fd; + int width; + int height; + struct fb_buffer ms_buffers[FB_BUFFER_NUM]; + char *raw_file; + FILE *raw_fp; + struct shm_buffer ss_buffer; + int x; + int y; + struct wl_registry *wl_registry; + struct wl_compositor *wl_compositor; + struct wl_subcompositor *wl_subcompositor; + struct xdg_wm_base *xdg_wm_base; + struct wl_surface *wl_surface; + struct wl_surface *wl_subsurface; + struct wl_subsurface *subsurface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + struct zwp_linux_dmabuf_v1 *dmabuf; + struct wl_callback *callback; + bool wait_for_configure; + bool initialized; + uint32_t frames_num; + uint32_t benchmark_time; + bool show_bar; + bool sync; + bool noread; + bool ss_usedma; + struct wl_list output_list; + struct zxdg_output_manager_v1 *xdg_output_manager; +}; + +int init_window(struct example_window *window); +#endif diff --git a/wl-tester/meson.build b/wl-tester/meson.build new file mode 100644 index 0000000..c0dbab1 --- /dev/null +++ b/wl-tester/meson.build @@ -0,0 +1,41 @@ +tester_sources = files( + 'tester.c', + 'common.c', + 'buffers.c', +) + +cc = meson.get_compiler('c') +wl_req = '>= 1.15' +wl_scanner = find_program('wayland-scanner') +drm_dep = dependency('libdrm') +rt_dep = cc.find_library('rt', required: false) +wl_client_dep = dependency('wayland-client', version: wl_req) +wl_protocol_dep = dependency('wayland-protocols', version: wl_req) + +protocols_datadir = wl_protocol_dep.get_pkgconfig_variable('pkgdatadir') +protocol_defs = [ + ['/stable/viewporter/viewporter.xml', 'viewporter-protocol.c', 'viewporter-client-protocol.h'], + ['/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', + 'linux-dmabuf-unstable-v1-protocol.c', 'linux-dmabuf-unstable-v1-client-protocol.h'], + ['/unstable/xdg-output/xdg-output-unstable-v1.xml', + 'xdg-output-unstable-v1-protocol.c', 'xdg-output-unstable-v1-client-protocol.h'], + ['/stable/xdg-shell/xdg-shell.xml', 'xdg-shell-protocol.c', 'xdg-shell-client-protocol.h'], +] +protocols_files = [] + +foreach protodef: protocol_defs + xmlfile = protocols_datadir + protodef.get(0) + + protocols_files += [custom_target(protodef.get(1), + output : protodef.get(1), + input : xmlfile, + command : [wl_scanner, 'code', '@INPUT@', '@OUTPUT@'])] + + protocols_files += [custom_target(protodef.get(2), + output : protodef.get(2), + input : xmlfile, + command : [wl_scanner, 'client-header', '@INPUT@', '@OUTPUT@'])] +endforeach + +dep_common = [drm_dep, rt_dep, wl_client_dep, wl_protocol_dep] +wl_tester = executable('wl-tester', tester_sources + protocols_files, dependencies : dep_common, install : true) diff --git a/wl-tester/tester.c b/wl-tester/tester.c new file mode 100644 index 0000000..0f707a7 --- /dev/null +++ b/wl-tester/tester.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "buffers.h" +#include "common.h" + +static void print_usage_and_exit(void) { + printf("Usages:\n\n" + "wl-tester -d /dev/dri/card0 -w 1280 -h 720 -f /path-to/test.yuv -x 100 -y 200 -s -b\n\n" + "\t-d: DRM device node path\n" + "\t-w: RAW YUV file's width\n" + "\t-h: RAW YUV file's height\n" + "\t-f: RAW YUV file's path\n" + "\t-x: Window Surface's x position if compositor supports window position\n" + "\t-y: Window Surface's y position if compositor supports window position\n" + "\t-b: Show Sub Surface/bar\n" + "\t-s: sync mode of sub surface\n" + "\t-n: not read YUV file in loop, for perf debug purpose\n" + "\t-u: Sub Surface use DMA Buf also, default is using Share Memory\n" + ); + exit(0); +} + +int main(int argc, char *argv[]) { + struct example_window window = {0}; + int c, option_index; + int ret = 0; + char default_drm_node[32] = "/dev/dri/card0"; + + window.drm_node = default_drm_node; + + static struct option long_options[] = { + {"drm-node", required_argument, 0, 'd'}, + {"raw-file", required_argument, 0, 'f'}, + {"width", required_argument, 0, 'w'}, + {"height", required_argument, 0, 'h'}, + {"sx", required_argument, 0, 'x'}, + {"sy", required_argument, 0, 'y'}, + {"show-bar", no_argument, NULL, 'b'}, + {"sync", no_argument, NULL, 's'}, + {"noread", no_argument, NULL, 'n'}, + {"ss-usedma", no_argument, NULL, 'u'}, + {"help", no_argument, 0, 0}, + {0, 0, 0, 0}}; + + while ((c = getopt_long(argc, argv, "bsnud:f:w:h:x:y:", long_options, &option_index)) != + -1) { + switch (c) { + case 'w': + window.width = atoi(optarg); + break; + case 'h': + window.height = atoi(optarg); + break; + case 'x': + window.x = atoi(optarg); + break; + case 'y': + window.y = atoi(optarg); + break; + case 'd': + window.drm_node = optarg; + break; + case 'f': + window.raw_file = optarg; + break; + case 's': + window.sync = true; + break; + case 'n': + window.noread = true; + break; + case 'u': + window.ss_usedma = true; + break; + case 'b': + window.show_bar = true; + break; + default: + print_usage_and_exit(); + } + } + + window.raw_fp = fopen(window.raw_file, "rb"); + if (!window.raw_fp) { + fprintf(stderr, "Error opening yuv image for read\n"); + return -1; + } + + ret = init_window(&window); + if (ret){ + return 1; + } + + return 0; +}