diff --git a/Makefile.am b/Makefile.am index 6a91a8f8..87204a68 100644 --- a/Makefile.am +++ b/Makefile.am @@ -842,6 +842,7 @@ weston_tests = \ event.weston \ button.weston \ text.weston \ + presentation.weston \ subsurface.weston @@ -944,6 +945,13 @@ subsurface_weston_SOURCES = tests/subsurface-test.c subsurface_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) subsurface_weston_LDADD = libtest-client.la +presentation_weston_SOURCES = tests/presentation-test.c +nodist_presentation_weston_SOURCES = \ + protocol/presentation_timing-protocol.c \ + protocol/presentation_timing-client-protocol.h +presentation_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS) +presentation_weston_LDADD = libtest-client.la + if ENABLE_EGL weston_tests += buffer-count.weston buffer_count_weston_SOURCES = tests/buffer-count-test.c diff --git a/tests/presentation-test.c b/tests/presentation-test.c new file mode 100644 index 00000000..44080822 --- /dev/null +++ b/tests/presentation-test.c @@ -0,0 +1,247 @@ +/* + * Copyright © 2014 Collabora, Ltd. + * + * 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 +#include +#include +#include + +#include "weston-test-client-helper.h" +#include "presentation_timing-client-protocol.h" + +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) + +static inline void * +xzalloc(size_t size) +{ + void *p; + + p = calloc(1, size); + assert(p); + + return p; +} + +static struct presentation * +get_presentation(struct client *client) +{ + struct global *g; + struct global *global_pres = NULL; + static struct presentation *pres; + + if (pres) + return pres; + + wl_list_for_each(g, &client->global_list, link) { + if (strcmp(g->interface, "presentation")) + continue; + + if (global_pres) + assert(0 && "multiple presentation objects"); + + global_pres = g; + } + + assert(global_pres && "no presentation found"); + + assert(global_pres->version == 1); + + pres = wl_registry_bind(client->wl_registry, global_pres->name, + &presentation_interface, 1); + assert(pres); + + return pres; +} + +struct feedback { + struct client *client; + struct presentation_feedback *obj; + + enum { + FB_PENDING = 0, + FB_PRESENTED, + FB_DISCARDED + } result; + + struct wl_output *sync_output; + uint64_t seq; + struct timespec time; + uint32_t refresh_nsec; + uint32_t flags; +}; + +static void +timespec_from_proto(struct timespec *tm, uint32_t tv_sec_hi, + uint32_t tv_sec_lo, uint32_t tv_nsec) +{ + tm->tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo; + tm->tv_nsec = tv_nsec; +} + +static void +feedback_sync_output(void *data, + struct presentation_feedback *presentation_feedback, + struct wl_output *output) +{ + struct feedback *fb = data; + + assert(fb->result == FB_PENDING); + + if (output) + fb->sync_output = output; +} + +static void +feedback_presented(void *data, + struct presentation_feedback *presentation_feedback, + uint32_t tv_sec_hi, + uint32_t tv_sec_lo, + uint32_t tv_nsec, + uint32_t refresh_nsec, + uint32_t seq_hi, + uint32_t seq_lo, + uint32_t flags) +{ + struct feedback *fb = data; + + assert(fb->result == FB_PENDING); + fb->result = FB_PRESENTED; + fb->seq = ((uint64_t)seq_hi << 32) + seq_lo; + timespec_from_proto(&fb->time, tv_sec_hi, tv_sec_lo, tv_nsec); + fb->refresh_nsec = refresh_nsec; + fb->flags = flags; +} + +static void +feedback_discarded(void *data, + struct presentation_feedback *presentation_feedback) +{ + struct feedback *fb = data; + + assert(fb->result == FB_PENDING); + fb->result = FB_DISCARDED; +} + +static const struct presentation_feedback_listener feedback_listener = { + feedback_sync_output, + feedback_presented, + feedback_discarded +}; + +static struct feedback * +feedback_create(struct client *client, struct wl_surface *surface) +{ + struct feedback *fb; + + fb = xzalloc(sizeof *fb); + fb->client = client; + fb->obj = presentation_feedback(get_presentation(client), surface); + presentation_feedback_add_listener(fb->obj, &feedback_listener, fb); + + return fb; +} + +static void +feedback_wait(struct feedback *fb) +{ + while (fb->result == FB_PENDING) { + assert(wl_display_dispatch(fb->client->wl_display) >= 0); + } +} + +static char * +pflags_to_str(uint32_t flags, char *str, unsigned len) +{ + static const struct { + uint32_t flag; + char sym; + } desc[] = { + { 1, '1' }, /* dummy placeholder */ + }; + unsigned i; + + *str = '\0'; + if (len < ARRAY_LENGTH(desc) + 1) + return str; + + for (i = 0; i < ARRAY_LENGTH(desc); i++) + str[i] = flags & desc[i].flag ? desc[i].sym : '_'; + str[ARRAY_LENGTH(desc)] = '\0'; + + return str; +} + +static void +feedback_print(struct feedback *fb) +{ + char str[10]; + + switch (fb->result) { + case FB_PENDING: + printf("pending"); + return; + case FB_DISCARDED: + printf("discarded"); + return; + case FB_PRESENTED: + break; + } + + pflags_to_str(fb->flags, str, sizeof str); + printf("presented %lld.%09lld, refresh %u us, [%s] seq %" PRIu64, + (long long)fb->time.tv_sec, (long long)fb->time.tv_nsec, + fb->refresh_nsec / 1000, str, fb->seq); +} + +static void +feedback_destroy(struct feedback *fb) +{ + presentation_feedback_destroy(fb->obj); + free(fb); +} + +TEST(test_presentation_feedback_simple) +{ + struct client *client; + struct feedback *fb; + + client = client_create(100, 50, 123, 77); + assert(client); + + wl_surface_attach(client->surface->wl_surface, + client->surface->wl_buffer, 0, 0); + fb = feedback_create(client, client->surface->wl_surface); + wl_surface_damage(client->surface->wl_surface, 0, 0, 100, 100); + wl_surface_commit(client->surface->wl_surface); + + client_roundtrip(client); + + feedback_wait(fb); + + printf("%s feedback:", __func__); + feedback_print(fb); + printf("\n"); + + feedback_destroy(fb); +}