From 37f6934e863de13926975ff5c4e60b9ee9fa79cc Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Sun, 5 Feb 2012 22:25:32 -0800 Subject: a test to measure the touch latency Change-Id: I01782274563fc9d6e4c1ba48e5aa371c164ed589 --- tests/touchlag/Android.mk | 14 +++ tests/touchlag/touchlag.cpp | 294 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 308 insertions(+) create mode 100644 tests/touchlag/Android.mk create mode 100644 tests/touchlag/touchlag.cpp (limited to 'tests') diff --git a/tests/touchlag/Android.mk b/tests/touchlag/Android.mk new file mode 100644 index 0000000..4f8aa1e --- /dev/null +++ b/tests/touchlag/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + touchlag.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils libutils \ + +LOCAL_MODULE:= test-touchlag + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/tests/touchlag/touchlag.cpp b/tests/touchlag/touchlag.cpp new file mode 100644 index 0000000..df4befb --- /dev/null +++ b/tests/touchlag/touchlag.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace android; + +#ifndef FBIO_WAITFORVSYNC +#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) +#endif + +struct Buffer { + size_t w; + size_t h; + size_t s; + union { + void* addr; + uint32_t* pixels; + }; +}; + +void clearBuffer(Buffer* buf, uint32_t pixel) { + android_memset32(buf->pixels, pixel, buf->s * buf->h * 4); +} + +void drawTwoPixels(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w) { + if (y>0 && yh)) { + uint32_t* bits = buf->pixels + y * buf->s; + if (x>=0 && xw) { + bits[x] = pixel; + } + ssize_t W(w); + if ((x+W)>=0 && (x+W)w) { + bits[x+W] = pixel; + } + } +} + +void drawHLine(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w) { + if (y>0 && yh)) { + ssize_t W(w); + if (x<0) { + W += x; + x = 0; + } + if (x+w > buf->w) { + W = buf->w - x; + } + if (W>0) { + uint32_t* bits = buf->pixels + y * buf->s + x; + android_memset32(bits, pixel, W*4); + } + } +} + +void drawRect(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w, size_t h) { + ssize_t W(w), H(h); + if (x<0) { + w += x; + x = 0; + } + if (y<0) { + h += y; + y = 0; + } + if (x+w > buf->w) W = buf->w - x; + if (y+h > buf->h) H = buf->h - y; + if (W>0 && H>0) { + uint32_t* bits = buf->pixels + y * buf->s + x; + for (ssize_t i=0 ; is; + } + } +} + +void drawCircle(Buffer* buf, uint32_t pixel, + size_t x0, size_t y0, size_t radius, bool filled = false) { + ssize_t f = 1 - radius; + ssize_t ddF_x = 1; + ssize_t ddF_y = -2 * radius; + ssize_t x = 0; + ssize_t y = radius; + if (filled) { + drawHLine(buf, pixel, x0-radius, y0, 2*radius); + } else { + drawTwoPixels(buf, pixel, x0-radius, y0, 2*radius); + } + while (x < y) { + if (f >= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + if (filled) { + drawHLine(buf, pixel, x0-x, y0+y, 2*x); + drawHLine(buf, pixel, x0-x, y0-y, 2*x); + drawHLine(buf, pixel, x0-y, y0+x, 2*y); + drawHLine(buf, pixel, x0-y, y0-x, 2*y); + } else { + drawTwoPixels(buf, pixel, x0-x, y0+y, 2*x); + drawTwoPixels(buf, pixel, x0-x, y0-y, 2*x); + drawTwoPixels(buf, pixel, x0-y, y0+x, 2*y); + drawTwoPixels(buf, pixel, x0-y, y0-x, 2*y); + } + } +} + +class TouchEvents { + class EventThread : public Thread { + int fd; + + virtual bool threadLoop() { + input_event event; + int first_down = 0; + do { + read(fd, &event, sizeof(event)); + if (event.type == EV_ABS) { + if (event.code == ABS_MT_TRACKING_ID) { + down = event.value == -1 ? 0 : 1; + first_down = down; + } + if (event.code == ABS_MT_POSITION_X) { + x = event.value; + } + if (event.code == ABS_MT_POSITION_Y) { + y = event.value; + } + } + } while (event.type == EV_SYN); + return true; + } + + public: + int x, y, down; + EventThread() : Thread(false), + x(0), y(0), down(0) + { + fd = open("/dev/input/event1", O_RDONLY); + } +}; + sp thread; + +public: + TouchEvents() { + thread = new EventThread(); + thread->run("EventThread", PRIORITY_URGENT_DISPLAY); + } + + int getMostRecentPosition(int* x, int* y) { + *x = thread->x; + *y = thread->y; + return thread->down; + } +}; + + +struct Queue { + struct position { + int x, y; + }; + int index; + position q[16]; + Queue() : index(0) { } + void push(int x, int y) { + index++; + index &= 0xF; + q[index].x = x; + q[index].y = y; + } + void get(int lag, int* x, int* y) { + const int i = (index - lag) & 0xF; + *x = q[i].x; + *y = q[i].y; + } +}; + +extern char *optarg; +extern int optind; +extern int optopt; +extern int opterr; +extern int optreset; + +void usage(const char* name) { + printf("\nusage: %s [-h] [-l lag]\n", name); +} + +int main(int argc, char** argv) { + fb_var_screeninfo vi; + fb_fix_screeninfo fi; + + int lag = 0; + int fd = open("/dev/graphics/fb0", O_RDWR); + ioctl(fd, FBIOGET_VSCREENINFO, &vi); + ioctl(fd, FBIOGET_FSCREENINFO, &fi); + void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + Buffer framebuffer; + framebuffer.w = vi.xres; + framebuffer.h = vi.yres; + framebuffer.s = fi.line_length / (vi.bits_per_pixel >> 3); + framebuffer.addr = bits; + + int ch; + while ((ch = getopt(argc, argv, "hl:")) != -1) { + switch (ch) { + case 'l': + lag = atoi(optarg); + break; + case 'h': + default: + usage(argv[0]); + exit(0); + } + } + argc -= optind; + argv += optind; + + + TouchEvents touch; + Queue queue; + + + int x=0, y=0, down=0; + int lag_x=0, lag_y=0; + + clearBuffer(&framebuffer, 0); + while (true) { + uint32_t crt = 0; + int err = ioctl(fd, FBIO_WAITFORVSYNC, &crt); + + // draw beam marker + drawRect(&framebuffer, 0x400000, framebuffer.w-2, 0, 2, framebuffer.h); + // erase screen + if (lag) { + drawCircle(&framebuffer, 0, lag_x, lag_y, 100); + drawHLine(&framebuffer, 0, 0, lag_y, 32); + } + drawCircle(&framebuffer, 0, x, y, 100, true); + drawHLine(&framebuffer, 0, 0, y, 32); + + // draw a line at y=1000 + drawHLine(&framebuffer, 0x808080, 0, 1000, framebuffer.w); + + // get touch events + touch.getMostRecentPosition(&x, &y); + queue.push(x, y); + queue.get(lag, &lag_x, &lag_y); + + if (lag) { + drawCircle(&framebuffer, 0x00FF00, lag_x, lag_y, 100); + drawHLine(&framebuffer, 0x00FF00, 0, lag_y, 32); + } + + drawCircle(&framebuffer, 0xFFFFFF, x, y, 100, true); + drawHLine(&framebuffer, 0xFFFFFF, 0, y, 32); + + // draw end of frame beam marker + drawRect(&framebuffer, 0x004000, framebuffer.w-2, 0, 2, framebuffer.h); + } + + close(fd); + return 0; +} -- cgit v1.1