summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@virtuousgeek.org>2012-05-07 12:36:28 -0700
committerJesse Barnes <jbarnes@virtuousgeek.org>2012-05-07 12:36:28 -0700
commit362599ac1042eeca52ebbfca646a975a989ff38f (patch)
tree5d2798d8cf8e9d5ce7504c4ff46e60ca31a9efa3
parentc6f42f4cca10cb644c7f91d1fd3e625dba505eb0 (diff)
tests: add simple flip test
Add a simple flip test to test flipping on a single output with the preferred mode. TODO: test each crtc for 3s and print pass/fail results
-rw-r--r--tests/Makefile.am1
-rw-r--r--tests/bird-thaumatrope.pngbin0 -> 39572 bytes
-rw-r--r--tests/cage-thaumatrope.pngbin0 -> 40130 bytes
-rw-r--r--tests/flip_test.c576
4 files changed, 577 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 108b55c6..18caf78e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -62,6 +62,7 @@ TESTS_progs = \
drm_vma_limiter_gtt \
drm_vma_limiter_cached \
sysfs_rc6_residency \
+ flip_test \
$(NULL)
# IMPORTANT: The ZZ_ tests need to be run last!
diff --git a/tests/bird-thaumatrope.png b/tests/bird-thaumatrope.png
new file mode 100644
index 00000000..81671b1c
--- /dev/null
+++ b/tests/bird-thaumatrope.png
Binary files differ
diff --git a/tests/cage-thaumatrope.png b/tests/cage-thaumatrope.png
new file mode 100644
index 00000000..98392981
--- /dev/null
+++ b/tests/cage-thaumatrope.png
Binary files differ
diff --git a/tests/flip_test.c b/tests/flip_test.c
new file mode 100644
index 00000000..131a1c3d
--- /dev/null
+++ b/tests/flip_test.c
@@ -0,0 +1,576 @@
+/*
+ * Copyright 2012 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * 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 "config.h"
+
+#include <assert.h>
+#include <cairo.h>
+#include <errno.h>
+#include <math.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+
+#include "xf86drm.h"
+#include "xf86drmMode.h"
+#include "i915_drm.h"
+#include "drmtest.h"
+#include "testdisplay.h"
+
+drmModeRes *resources;
+int drm_fd;
+int test_time = 3;
+
+uint32_t *fb_ptr;
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+struct type_name {
+ int type;
+ const char *name;
+};
+
+struct test_output {
+ uint32_t id;
+ int mode_valid;
+ drmModeModeInfo mode;
+ drmModeEncoder *encoder;
+ drmModeConnector *connector;
+ int crtc;
+ int pipe;
+ unsigned int current_fb_id;
+ unsigned int fb_ids[2];
+ unsigned long swap_count;
+ struct timeval start;
+};
+
+static void dump_mode(drmModeModeInfo *mode)
+{
+ printf(" %s %d %d %d %d %d %d %d %d %d 0x%x 0x%x %d\n",
+ mode->name,
+ mode->vrefresh,
+ mode->hdisplay,
+ mode->hsync_start,
+ mode->hsync_end,
+ mode->htotal,
+ mode->vdisplay,
+ mode->vsync_start,
+ mode->vsync_end,
+ mode->vtotal,
+ mode->flags,
+ mode->type,
+ mode->clock);
+}
+
+static void page_flip_handler(int fd, unsigned int frame, unsigned int sec,
+ unsigned int usec, void *data)
+{
+ struct test_output *o = data;
+ unsigned int new_fb_id;
+ struct timeval end;
+ double t;
+
+ if (o->current_fb_id == o->fb_ids[0])
+ new_fb_id = o->fb_ids[1];
+ else
+ new_fb_id = o->fb_ids[0];
+
+ drmModePageFlip(drm_fd, o->crtc, new_fb_id,
+ DRM_MODE_PAGE_FLIP_EVENT, o);
+ o->current_fb_id = new_fb_id;
+ o->swap_count++;
+ if (o->swap_count == 60) {
+ gettimeofday(&end, NULL);
+ t = end.tv_sec + end.tv_usec * 1e-6 -
+ (o->start.tv_sec + o->start.tv_usec * 1e-6);
+ fprintf(stderr, "freq: %.02fHz\n", o->swap_count / t);
+ o->swap_count = 0;
+ o->start = end;
+ }
+}
+
+static void connector_find_preferred_mode(struct test_output *o, int crtc_id)
+{
+ drmModeConnector *connector;
+ drmModeEncoder *encoder = NULL;
+ int i, j;
+
+ /* First, find the connector & mode */
+ o->mode_valid = 0;
+ o->crtc = 0;
+ connector = drmModeGetConnector(drm_fd, o->id);
+ if (!connector) {
+ fprintf(stderr, "could not get connector %d: %s\n",
+ o->id, strerror(errno));
+ drmModeFreeConnector(connector);
+ return;
+ }
+
+ if (connector->connection != DRM_MODE_CONNECTED) {
+ drmModeFreeConnector(connector);
+ return;
+ }
+
+ if (!connector->count_modes) {
+ fprintf(stderr, "connector %d has no modes\n", o->id);
+ drmModeFreeConnector(connector);
+ return;
+ }
+
+ if (connector->connector_id != o->id) {
+ fprintf(stderr, "connector id doesn't match (%d != %d)\n",
+ connector->connector_id, o->id);
+ drmModeFreeConnector(connector);
+ return;
+ }
+
+ for (j = 0; j < connector->count_modes; j++) {
+ o->mode = connector->modes[j];
+ if (o->mode.type & DRM_MODE_TYPE_PREFERRED) {
+ o->mode_valid = 1;
+ break;
+ }
+ }
+
+ if (!o->mode_valid) {
+ if (connector->count_modes > 0) {
+ /* use the first mode as test mode */
+ o->mode = connector->modes[0];
+ o->mode_valid = 1;
+ }
+ else {
+ fprintf(stderr, "failed to find any modes on connector %d\n",
+ o->id);
+ return;
+ }
+ }
+
+ /* Now get the encoder */
+ for (i = 0; i < connector->count_encoders; i++) {
+ encoder = drmModeGetEncoder(drm_fd, connector->encoders[i]);
+
+ if (!encoder) {
+ fprintf(stderr, "could not get encoder %i: %s\n",
+ resources->encoders[i], strerror(errno));
+ drmModeFreeEncoder(encoder);
+ continue;
+ }
+
+ break;
+ }
+
+ o->encoder = encoder;
+
+ if (i == resources->count_encoders) {
+ fprintf(stderr, "failed to find encoder\n");
+ o->mode_valid = 0;
+ return;
+ }
+
+ /* Find first CRTC not in use */
+ for (i = 0; i < resources->count_crtcs; i++) {
+ if (resources->crtcs[i] != crtc_id)
+ continue;
+ if (resources->crtcs[i] &&
+ (o->encoder->possible_crtcs & (1<<i))) {
+ o->crtc = resources->crtcs[i];
+ break;
+ }
+ }
+
+ if (!o->crtc) {
+ fprintf(stderr, "could not find requested crtc %d\n", crtc_id);
+ return;
+ }
+
+ o->pipe = i;
+
+ o->connector = connector;
+}
+
+static cairo_surface_t *
+allocate_surface(int fd, uint32_t *handle, int width, int height,
+ int bpp, int depth, int *_stride)
+{
+ cairo_format_t format;
+ struct drm_i915_gem_set_tiling set_tiling;
+ int size, v, stride;
+
+ /* Round the tiling up to the next power-of-two and the
+ * region up to the next pot fence size so that this works
+ * on all generations.
+ *
+ * This can still fail if the framebuffer is too large to
+ * be tiled. But then that failure is expected.
+ */
+ v = width * bpp / 8;
+ for (stride = 512; stride < v; stride *= 2)
+ ;
+
+ v = stride * height;
+ for (size = 1024*1024; size < v; size *= 2)
+ ;
+
+ *_stride = stride;
+
+ switch (depth) {
+ case 16:
+ format = CAIRO_FORMAT_RGB16_565;
+ break;
+ case 24:
+ format = CAIRO_FORMAT_RGB24;
+ break;
+#if 0
+ case 30:
+ format = CAIRO_FORMAT_RGB30;
+ break;
+#endif
+ case 32:
+ format = CAIRO_FORMAT_ARGB32;
+ break;
+ default:
+ fprintf(stderr, "bad depth %d\n", depth);
+ return NULL;
+ }
+
+ *handle = gem_create(fd, size);
+
+ set_tiling.handle = *handle;
+ set_tiling.tiling_mode = I915_TILING_X;
+ set_tiling.stride = stride;
+ if (ioctl(fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling)) {
+ fprintf(stderr, "set tiling failed: %s (stride=%d, size=%d)\n",
+ strerror(errno), stride, size);
+ return NULL;
+ }
+
+ fb_ptr = gem_mmap(fd, *handle, size, PROT_READ | PROT_WRITE);
+
+ return cairo_image_surface_create_for_data((unsigned char *)fb_ptr,
+ format, width, height,
+ stride);
+}
+
+enum corner {
+ topleft,
+ topright,
+ bottomleft,
+ bottomright,
+};
+
+static void paint_marker(cairo_t *cr, int x, int y, char *str,
+ enum corner text_location)
+{
+ cairo_text_extents_t extents;
+ int xoff, yoff;
+
+ cairo_set_font_size(cr, 18);
+ cairo_text_extents(cr, str, &extents);
+
+ switch (text_location) {
+ case topleft:
+ xoff = -20;
+ xoff -= extents.width;
+ yoff = -20;
+ break;
+ case topright:
+ xoff = 20;
+ yoff = -20;
+ break;
+ case bottomleft:
+ xoff = -20;
+ xoff -= extents.width;
+ yoff = 20;
+ break;
+ case bottomright:
+ xoff = 20;
+ yoff = 20;
+ break;
+ default:
+ xoff = 0;
+ yoff = 0;
+ }
+
+ cairo_move_to(cr, x, y - 20);
+ cairo_line_to(cr, x, y + 20);
+ cairo_move_to(cr, x - 20, y);
+ cairo_line_to(cr, x + 20, y);
+ cairo_new_sub_path(cr);
+ cairo_arc(cr, x, y, 10, 0, M_PI * 2);
+ cairo_set_line_width(cr, 4);
+ cairo_set_source_rgb(cr, 0, 0, 0);
+ cairo_stroke_preserve(cr);
+ cairo_set_source_rgb(cr, 1, 1, 1);
+ cairo_set_line_width(cr, 2);
+ cairo_stroke(cr);
+
+ cairo_move_to(cr, x + xoff, y + yoff);
+ cairo_text_path(cr, str);
+ cairo_set_source_rgb(cr, 0, 0, 0);
+ cairo_stroke_preserve(cr);
+ cairo_set_source_rgb(cr, 1, 1, 1);
+ cairo_fill(cr);
+}
+
+static void paint_image(cairo_t *cr, const char *file, int width, int height)
+{
+ cairo_surface_t *image;
+ int img_w, img_h;
+ int img_x, img_y;
+
+ image = cairo_image_surface_create_from_png(file);
+
+ img_w = cairo_image_surface_get_width(image);
+ img_h = cairo_image_surface_get_height(image);
+
+ if (img_w > width || img_h > height) {
+ cairo_scale(cr, width/img_w, height/img_h);
+ img_w = img_w * (width / img_w);
+ img_h = img_h * (height / img_h);
+ }
+
+ img_x = (width / 2) - (img_w / 2);
+ img_y = (height / 2) - (img_h / 2);
+
+ cairo_translate(cr, img_x, img_y);
+
+ fprintf(stderr, "drew %dx%d image at %d,%d\n", img_w, img_h,
+ img_x, img_y);
+
+ cairo_set_source_surface(cr, image, 0, 0);
+ cairo_paint(cr);
+ cairo_surface_destroy(image);
+}
+
+/*
+ * Images from:
+ * http://www.docstoc.com/docs/28101658/Make-a-Thaumatrope
+ * listed as public domain.
+ */
+static unsigned int initialize_fb(int fd, int width, int height, int bpp,
+ int depth, const char *file)
+{
+ cairo_surface_t *surface;
+ cairo_status_t status;
+ cairo_t *cr;
+ uint32_t handle;
+ unsigned int fb_id;
+ int ret, stride;
+ char buf[128];
+
+ surface = allocate_surface(drm_fd, &handle, width, height, bpp, depth,
+ &stride);
+ if (!surface) {
+ fprintf(stderr, "allocation failed %dx%d\n", width, height);
+ return 0;
+ }
+
+ cr = cairo_create(surface);
+
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
+
+ /* Paint corner markers */
+ snprintf(buf, sizeof buf, "(%d, %d)", 0, 0);
+ paint_marker(cr, 0, 0, buf, bottomright);
+ snprintf(buf, sizeof buf, "(%d, %d)", width, 0);
+ paint_marker(cr, width, 0, buf, bottomleft);
+ snprintf(buf, sizeof buf, "(%d, %d)", 0, height);
+ paint_marker(cr, 0, height, buf, topright);
+ snprintf(buf, sizeof buf, "(%d, %d)", width, height);
+ paint_marker(cr, width, height, buf, topleft);
+
+ paint_image(cr, file, width, height);
+
+ status = cairo_status(cr);
+ cairo_destroy(cr);
+ if (status)
+ fprintf(stderr, "failed to draw pretty picture %dx%d: %s\n",
+ width, height, cairo_status_to_string(status));
+
+ ret = drmModeAddFB(drm_fd, width, height, depth, bpp, stride,
+ handle, &fb_id);
+ cairo_surface_destroy(surface);
+ gem_close(drm_fd, handle);
+
+ if (ret) {
+ fprintf(stderr, "failed to add fb (%dx%d): %s\n",
+ width, height, strerror(errno));
+ return 0;
+ }
+
+ return fb_id;
+}
+
+static void set_mode(struct test_output *o)
+{
+ int ret;
+ int bpp = 32, depth = 24;
+ drmEventContext evctx;
+ int width, height;
+
+ connector_find_preferred_mode(o, resources->crtcs[0]);
+ if (!o->mode_valid)
+ return;
+
+ width = o->mode.hdisplay;
+ height = o->mode.vdisplay;
+
+ o->fb_ids[0] = initialize_fb(drm_fd, width, height, bpp, depth,
+ "bird-thaumatrope.png");
+ o->fb_ids[1] = initialize_fb(drm_fd, width, height, bpp, depth,
+ "cage-thaumatrope.png");
+ if (!o->fb_ids[0] || !o->fb_ids[1]) {
+ fprintf(stderr, "failed to create fbs\n");
+ return;
+ }
+
+ fprintf(stdout, "CRTS(%u):",o->crtc);
+ dump_mode(&o->mode);
+ if (drmModeSetCrtc(drm_fd, o->crtc, o->fb_ids[0], 0, 0,
+ &o->id, 1, &o->mode)) {
+ fprintf(stderr, "failed to set mode (%dx%d@%dHz): %s\n",
+ width, height, o->mode.vrefresh,
+ strerror(errno));
+ return;
+ }
+
+ ret = drmModePageFlip(drm_fd, o->crtc, o->fb_ids[1],
+ DRM_MODE_PAGE_FLIP_EVENT, o);
+ if (ret) {
+ fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
+ return;
+ }
+ gettimeofday(&o->start, NULL);
+ o->swap_count = 0;
+ o->current_fb_id = o->fb_ids[1];
+
+ memset(&evctx, 0, sizeof evctx);
+ evctx.version = DRM_EVENT_CONTEXT_VERSION;
+ evctx.vblank_handler = NULL;
+ evctx.page_flip_handler = page_flip_handler;
+
+ while (1) {
+ struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
+ fd_set fds;
+
+ FD_ZERO(&fds);
+ FD_SET(0, &fds);
+ FD_SET(drm_fd, &fds);
+ ret = select(drm_fd + 1, &fds, NULL, NULL, &timeout);
+
+ if (ret <= 0) {
+ fprintf(stderr, "select timed out or error (ret %d)\n",
+ ret);
+ continue;
+ } else if (FD_ISSET(0, &fds)) {
+ fprintf(stderr, "no fds active, breaking\n");
+ break;
+ }
+
+ drmHandleEvent(drm_fd, &evctx);
+ }
+
+ drmModeFreeEncoder(o->encoder);
+ drmModeFreeConnector(o->connector);
+}
+
+static int run_test(void)
+{
+ struct test_output *connectors;
+ int c;
+
+ resources = drmModeGetResources(drm_fd);
+ if (!resources) {
+ fprintf(stderr, "drmModeGetResources failed: %s\n",
+ strerror(errno));
+ return 0;
+ }
+
+ connectors = calloc(resources->count_connectors,
+ sizeof(struct test_output));
+ if (!connectors)
+ return 0;
+
+ /* Find any connected displays */
+ for (c = 0; c < resources->count_connectors; c++) {
+ connectors[c].id = resources->connectors[c];
+ set_mode(&connectors[c]);
+ }
+
+ drmModeFreeResources(resources);
+ return 1;
+}
+
+static char optstr[] = "h";
+
+static void usage(char *name)
+{
+ fprintf(stderr, "usage: %s [-h]\n", name);
+ fprintf(stderr, "\t-h: help\n");
+ exit(0);
+}
+
+int main(int argc, char **argv)
+{
+ int c;
+ const char *modules[] = { "i915" };
+ unsigned int i;
+ int ret = 0;
+
+ opterr = 0;
+ while ((c = getopt(argc, argv, optstr)) != -1) {
+ switch (c) {
+ default:
+ fprintf(stderr, "unknown option %c\n", c);
+ /* fall through */
+ case 'h':
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(modules); i++) {
+ drm_fd = drmOpen(modules[i], NULL);
+ if (drm_fd < 0) {
+ printf("failed to load %s driver.\n", modules[i]);
+ goto out;
+ } else
+ break;
+ }
+
+ if (i == ARRAY_SIZE(modules)) {
+ fprintf(stderr, "failed to load any modules, aborting.\n");
+ ret = -1;
+ goto out_close;
+ }
+
+ run_test();
+
+out_close:
+ drmClose(drm_fd);
+out:
+ return ret;
+}