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 <robert.mader@collabora.com>
This commit is contained in:
Robert Mader 2022-05-24 01:31:48 +02:00 committed by Pekka Paalanen
parent 0b2369bb4a
commit 62ab6891db
2 changed files with 335 additions and 21 deletions

View file

@ -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' ]
},

View file

@ -46,9 +46,11 @@
#include <sys/types.h>
#include <unistd.h>
#include <libweston/matrix.h>
#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);