From 62ab6891db9763dd44228228aea43c116f5718e1 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Tue, 24 May 2022 01:31:48 +0200 Subject: [PATCH] clients/simple-egl: Handle buffer scale and transform Buffer scale is common enough in the modern desktop space to expect average GL clients to handle it. Thus lets include it into our main example client. While on it, also handle buffer transforms. It's essentially free for GL clients in terms of computing power but may increase the chance that Wayland compositors are able to hit scanout fast paths. Thus having an example client for it is likely valueabel for client and compositor developers. Signed-off-by: Robert Mader --- clients/meson.build | 7 +- clients/simple-egl.c | 349 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 335 insertions(+), 21 deletions(-) diff --git a/clients/meson.build b/clients/meson.build index 6056cae5..8e6446a8 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -131,7 +131,12 @@ simple_clients = [ ivi_application_client_protocol_h, ivi_application_protocol_c, ], - 'dep_objs': [ dep_wayland_client, dep_libshared, dep_libm ], + 'dep_objs': [ + dep_libm, + dep_libshared, + dep_matrix_c, + dep_wayland_client, + ], 'deps': [ 'egl', 'wayland-egl', 'glesv2', 'wayland-cursor' ], 'options': [ 'renderer-gl' ] }, diff --git a/clients/simple-egl.c b/clients/simple-egl.c index c2b8b7c8..05a0ffcb 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -46,9 +46,11 @@ #include #include +#include #include "shared/helpers.h" #include "shared/platform.h" #include "shared/weston-egl-ext.h" +#include "shared/xalloc.h" struct window; struct seat; @@ -73,6 +75,8 @@ struct display { } egl; struct window *window; + struct wl_list output_list; /* struct output::link */ + PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage; }; @@ -82,7 +86,13 @@ struct geometry { struct window { struct display *display; - struct geometry buffer_size, window_size; + struct geometry window_size; + struct geometry logical_size; + struct geometry buffer_size; + int32_t buffer_scale; + enum wl_output_transform buffer_transform; + bool needs_buffer_geometry_update; + struct { GLuint rotation_uniform; GLuint pos; @@ -97,6 +107,22 @@ struct window { EGLSurface egl_surface; int fullscreen, maximized, opaque, buffer_bpp, frame_sync, delay; bool wait_for_configure; + + struct wl_list window_output_list; /* struct window_output::link */ +}; + +struct output { + struct display *display; + struct wl_output *wl_output; + uint32_t name; + struct wl_list link; /* struct display::output_list */ + enum wl_output_transform transform; + int32_t scale; +}; + +struct window_output { + struct output *output; + struct wl_list link; /* struct window::window_output_list */ }; static const char *vert_shader_text = @@ -347,16 +373,13 @@ handle_toplevel_configure(void *data, struct xdg_toplevel *toplevel, window->window_size.width = width; window->window_size.height = height; } - window->buffer_size.width = width; - window->buffer_size.height = height; + window->logical_size.width = width; + window->logical_size.height = height; } else if (!window->fullscreen && !window->maximized) { - window->buffer_size = window->window_size; + window->logical_size = window->window_size; } - if (window->native) - wl_egl_window_resize(window->native, - window->buffer_size.width, - window->buffer_size.height, 0, 0); + window->needs_buffer_geometry_update = true; } static void @@ -370,12 +393,80 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = { handle_toplevel_close, }; +static void +add_window_output(struct window *window, struct wl_output *wl_output) +{ + struct output *output; + struct output *output_found = NULL; + struct window_output *window_output; + + wl_list_for_each(output, &window->display->output_list, link) { + if (output->wl_output == wl_output) { + output_found = output; + break; + } + } + + if (!output_found) + return; + + window_output = xmalloc(sizeof *window_output); + window_output->output = output_found; + + wl_list_insert(window->window_output_list.prev, &window_output->link); + window->needs_buffer_geometry_update = true; +} + +static void +destroy_window_output(struct window *window, struct wl_output *wl_output) +{ + struct window_output *window_output; + struct window_output *window_output_found = NULL; + + wl_list_for_each(window_output, &window->window_output_list, link) { + if (window_output->output->wl_output == wl_output) { + window_output_found = window_output; + break; + } + } + + if (window_output_found) { + wl_list_remove(&window_output_found->link); + free(window_output_found); + window->needs_buffer_geometry_update = true; + } +} + +static void +surface_enter(void *data, + struct wl_surface *wl_surface, struct wl_output *wl_output) +{ + struct window *window = data; + + add_window_output(window, wl_output); +} + +static void +surface_leave(void *data, + struct wl_surface *wl_surface, struct wl_output *wl_output) +{ + struct window *window = data; + + destroy_window_output(window, wl_output); +} + +static const struct wl_surface_listener surface_listener = { + surface_enter, + surface_leave +}; + static void create_surface(struct window *window) { struct display *display = window->display; window->surface = wl_compositor_create_surface(display->compositor); + wl_surface_add_listener(window->surface, &surface_listener, window); window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base, window->surface); @@ -422,6 +513,90 @@ destroy_surface(struct window *window) wl_surface_destroy(window->surface); } +static int32_t +compute_buffer_scale(struct window *window) +{ + struct window_output *window_output; + int32_t scale = 1; + + wl_list_for_each(window_output, &window->window_output_list, link) { + if (window_output->output->scale > scale) + scale = window_output->output->scale; + } + + return scale; +} + +static enum wl_output_transform +compute_buffer_transform(struct window *window) +{ + struct window_output *window_output; + enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + + wl_list_for_each(window_output, &window->window_output_list, link) { + /* If the surface spans over multiple outputs the optimal + * transform value can be ambiguous. Thus just return the value + * from the oldest entered output. + */ + transform = window_output->output->transform; + break; + } + + return transform; +} + +static void +update_buffer_geometry(struct window *window) +{ + enum wl_output_transform new_buffer_transform; + int32_t new_buffer_scale; + struct geometry new_buffer_size; + + new_buffer_transform = compute_buffer_transform(window); + if (window->buffer_transform != new_buffer_transform) { + window->buffer_transform = new_buffer_transform; + wl_surface_set_buffer_transform(window->surface, + window->buffer_transform); + } + + new_buffer_scale = compute_buffer_scale(window); + if (window->buffer_scale != new_buffer_scale) { + window->buffer_scale = new_buffer_scale; + wl_surface_set_buffer_scale(window->surface, + window->buffer_scale); + } + + switch (window->buffer_transform) { + case WL_OUTPUT_TRANSFORM_NORMAL: + case WL_OUTPUT_TRANSFORM_180: + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + new_buffer_size.width = window->logical_size.width; + new_buffer_size.height = window->logical_size.height; + break; + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_270: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + new_buffer_size.width = window->logical_size.height; + new_buffer_size.height = window->logical_size.width; + break; + } + + new_buffer_size.width *= window->buffer_scale; + new_buffer_size.height *= window->buffer_scale; + + if (window->buffer_size.width != new_buffer_size.width || + window->buffer_size.height != new_buffer_size.height) { + window->buffer_size = new_buffer_size; + wl_egl_window_resize(window->native, + window->buffer_size.width, + window->buffer_size.height, 0, 0); + } + + window->needs_buffer_geometry_update = false; +} + static void redraw(struct window *window) { @@ -437,18 +612,16 @@ redraw(struct window *window) { 0, 0, 1 } }; GLfloat angle; - GLfloat rotation[4][4] = { - { 1, 0, 0, 0 }, - { 0, 1, 0, 0 }, - { 0, 0, 1, 0 }, - { 0, 0, 0, 1 } - }; + struct weston_matrix rotation; static const uint32_t speed_div = 5, benchmark_interval = 5; struct wl_region *region; EGLint rect[4]; EGLint buffer_age = 0; struct timeval tv; + if (window->needs_buffer_geometry_update) + update_buffer_geometry(window); + gettimeofday(&tv, NULL); uint32_t time = tv.tv_sec * 1000 + tv.tv_usec / 1000; if (window->frames == 0) @@ -462,11 +635,42 @@ redraw(struct window *window) window->frames = 0; } - angle = (time / speed_div) % 360 * M_PI / 180.0; - rotation[0][0] = cos(angle); - rotation[0][2] = sin(angle); - rotation[2][0] = -sin(angle); - rotation[2][2] = cos(angle); + weston_matrix_init(&rotation); + angle = ((time - window->benchmark_time) / speed_div) % 360 * M_PI / 180.0; + rotation.d[0] = cos(angle); + rotation.d[2] = sin(angle); + rotation.d[8] = -sin(angle); + rotation.d[10] = cos(angle); + + switch (window->buffer_transform) { + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + weston_matrix_scale(&rotation, -1, 1, 1); + break; + default: + break; + } + + switch (window->buffer_transform) { + default: + case WL_OUTPUT_TRANSFORM_NORMAL: + case WL_OUTPUT_TRANSFORM_FLIPPED: + break; + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + weston_matrix_rotate_xy(&rotation, 0, 1); + break; + case WL_OUTPUT_TRANSFORM_180: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + weston_matrix_rotate_xy(&rotation, -1, 0); + break; + case WL_OUTPUT_TRANSFORM_270: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + weston_matrix_rotate_xy(&rotation, 0, -1); + break; + } if (display->swap_buffers_with_damage) eglQuerySurface(display->egl.dpy, window->egl_surface, @@ -475,7 +679,7 @@ redraw(struct window *window) glViewport(0, 0, window->buffer_size.width, window->buffer_size.height); glUniformMatrix4fv(window->gl.rotation_uniform, 1, GL_FALSE, - (GLfloat *) rotation); + (GLfloat *) rotation.d); if (window->opaque || window->fullscreen) glClearColor(0.0, 0.0, 0.0, 1); @@ -732,6 +936,92 @@ static const struct xdg_wm_base_listener wm_base_listener = { xdg_wm_base_ping, }; +static void +display_handle_geometry(void *data, + struct wl_output *wl_output, + int32_t x, int32_t y, + int32_t physical_width, + int32_t physical_height, + int32_t subpixel, + const char *make, + const char *model, + int32_t transform) +{ + struct output *output = data; + + output->transform = transform; + output->display->window->needs_buffer_geometry_update = true; +} + +static void +display_handle_mode(void *data, + struct wl_output *wl_output, + uint32_t flags, + int32_t width, + int32_t height, + int32_t refresh) +{ +} + +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) +{ + struct output *output = data; + + output->scale = scale; + output->display->window->needs_buffer_geometry_update = true; +} + +static const struct wl_output_listener output_listener = { + display_handle_geometry, + display_handle_mode, + display_handle_done, + display_handle_scale +}; + +static void +display_add_output(struct display *d, uint32_t name) +{ + struct output *output; + + output = xzalloc(sizeof *output); + output->display = d; + output->scale = 1; + output->wl_output = + wl_registry_bind(d->registry, name, &wl_output_interface, 2); + output->name = name; + wl_list_insert(d->output_list.prev, &output->link); + + wl_output_add_listener(output->wl_output, &output_listener, output); +} + +static void +display_destroy_output(struct display *d, struct output *output) +{ + destroy_window_output(d->window, output->wl_output); + wl_output_destroy(output->wl_output); + wl_list_remove(&output->link); + free(output); +} + +static void +display_destroy_outputs(struct display *d) +{ + struct output *tmp; + struct output *output; + + wl_list_for_each_safe(output, tmp, &d->output_list, link) + display_destroy_output(d, output); +} + static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) @@ -765,6 +1055,8 @@ registry_handle_global(void *data, struct wl_registry *registry, fprintf(stderr, "unable to load default left pointer\n"); // TODO: abort ? } + } else if (strcmp(interface, "wl_output") == 0 && version >= 2) { + display_add_output(d, name); } } @@ -772,6 +1064,15 @@ static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + struct display *d = data; + struct output *output; + + wl_list_for_each(output, &d->output_list, link) { + if (output->name == name) { + display_destroy_output(d, output); + break; + } + } } static const struct wl_registry_listener registry_listener = { @@ -813,10 +1114,16 @@ main(int argc, char **argv) window.buffer_size.width = 250; window.buffer_size.height = 250; window.window_size = window.buffer_size; + window.buffer_scale = 1; + window.buffer_transform = WL_OUTPUT_TRANSFORM_NORMAL; + window.needs_buffer_geometry_update = false; window.buffer_bpp = 0; window.frame_sync = 1; window.delay = 0; + wl_list_init(&display.output_list); + wl_list_init(&window.window_output_list); + for (i = 1; i < argc; i++) { if (strcmp("-d", argv[i]) == 0 && i+1 < argc) window.delay = atoi(argv[++i]); @@ -884,6 +1191,8 @@ main(int argc, char **argv) wl_surface_destroy(display.cursor_surface); out_no_xdg_shell: + display_destroy_outputs(&display); + if (display.cursor_theme) wl_cursor_theme_destroy(display.cursor_theme);