Skip to content

Commit 37f6934

Browse files
committed
a test to measure the touch latency
Change-Id: I01782274563fc9d6e4c1ba48e5aa371c164ed589
1 parent 587ae01 commit 37f6934

File tree

2 files changed

+308
-0
lines changed

2 files changed

+308
-0
lines changed

tests/touchlag/Android.mk

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
LOCAL_PATH:= $(call my-dir)
2+
include $(CLEAR_VARS)
3+
4+
LOCAL_SRC_FILES:= \
5+
touchlag.cpp
6+
7+
LOCAL_SHARED_LIBRARIES := \
8+
libcutils libutils \
9+
10+
LOCAL_MODULE:= test-touchlag
11+
12+
LOCAL_MODULE_TAGS := tests
13+
14+
include $(BUILD_EXECUTABLE)

tests/touchlag/touchlag.cpp

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
/*
2+
* Copyright (C) 2012 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <stdint.h>
18+
#include <sys/types.h>
19+
20+
#include <fcntl.h>
21+
#include <sys/ioctl.h>
22+
#include <linux/fb.h>
23+
#include <linux/input.h>
24+
#include <errno.h>
25+
#include <string.h>
26+
#include <stdio.h>
27+
#include <cutils/memory.h>
28+
#include <asm-generic/mman.h>
29+
#include <sys/mman.h>
30+
#include <utils/threads.h>
31+
#include <unistd.h>
32+
#include <math.h>
33+
34+
using namespace android;
35+
36+
#ifndef FBIO_WAITFORVSYNC
37+
#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
38+
#endif
39+
40+
struct Buffer {
41+
size_t w;
42+
size_t h;
43+
size_t s;
44+
union {
45+
void* addr;
46+
uint32_t* pixels;
47+
};
48+
};
49+
50+
void clearBuffer(Buffer* buf, uint32_t pixel) {
51+
android_memset32(buf->pixels, pixel, buf->s * buf->h * 4);
52+
}
53+
54+
void drawTwoPixels(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w) {
55+
if (y>0 && y<ssize_t(buf->h)) {
56+
uint32_t* bits = buf->pixels + y * buf->s;
57+
if (x>=0 && x<buf->w) {
58+
bits[x] = pixel;
59+
}
60+
ssize_t W(w);
61+
if ((x+W)>=0 && (x+W)<buf->w) {
62+
bits[x+W] = pixel;
63+
}
64+
}
65+
}
66+
67+
void drawHLine(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w) {
68+
if (y>0 && y<ssize_t(buf->h)) {
69+
ssize_t W(w);
70+
if (x<0) {
71+
W += x;
72+
x = 0;
73+
}
74+
if (x+w > buf->w) {
75+
W = buf->w - x;
76+
}
77+
if (W>0) {
78+
uint32_t* bits = buf->pixels + y * buf->s + x;
79+
android_memset32(bits, pixel, W*4);
80+
}
81+
}
82+
}
83+
84+
void drawRect(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w, size_t h) {
85+
ssize_t W(w), H(h);
86+
if (x<0) {
87+
w += x;
88+
x = 0;
89+
}
90+
if (y<0) {
91+
h += y;
92+
y = 0;
93+
}
94+
if (x+w > buf->w) W = buf->w - x;
95+
if (y+h > buf->h) H = buf->h - y;
96+
if (W>0 && H>0) {
97+
uint32_t* bits = buf->pixels + y * buf->s + x;
98+
for (ssize_t i=0 ; i<H ; i++) {
99+
android_memset32(bits, pixel, W*4);
100+
bits += buf->s;
101+
}
102+
}
103+
}
104+
105+
void drawCircle(Buffer* buf, uint32_t pixel,
106+
size_t x0, size_t y0, size_t radius, bool filled = false) {
107+
ssize_t f = 1 - radius;
108+
ssize_t ddF_x = 1;
109+
ssize_t ddF_y = -2 * radius;
110+
ssize_t x = 0;
111+
ssize_t y = radius;
112+
if (filled) {
113+
drawHLine(buf, pixel, x0-radius, y0, 2*radius);
114+
} else {
115+
drawTwoPixels(buf, pixel, x0-radius, y0, 2*radius);
116+
}
117+
while (x < y) {
118+
if (f >= 0) {
119+
y--;
120+
ddF_y += 2;
121+
f += ddF_y;
122+
}
123+
x++;
124+
ddF_x += 2;
125+
f += ddF_x;
126+
if (filled) {
127+
drawHLine(buf, pixel, x0-x, y0+y, 2*x);
128+
drawHLine(buf, pixel, x0-x, y0-y, 2*x);
129+
drawHLine(buf, pixel, x0-y, y0+x, 2*y);
130+
drawHLine(buf, pixel, x0-y, y0-x, 2*y);
131+
} else {
132+
drawTwoPixels(buf, pixel, x0-x, y0+y, 2*x);
133+
drawTwoPixels(buf, pixel, x0-x, y0-y, 2*x);
134+
drawTwoPixels(buf, pixel, x0-y, y0+x, 2*y);
135+
drawTwoPixels(buf, pixel, x0-y, y0-x, 2*y);
136+
}
137+
}
138+
}
139+
140+
class TouchEvents {
141+
class EventThread : public Thread {
142+
int fd;
143+
144+
virtual bool threadLoop() {
145+
input_event event;
146+
int first_down = 0;
147+
do {
148+
read(fd, &event, sizeof(event));
149+
if (event.type == EV_ABS) {
150+
if (event.code == ABS_MT_TRACKING_ID) {
151+
down = event.value == -1 ? 0 : 1;
152+
first_down = down;
153+
}
154+
if (event.code == ABS_MT_POSITION_X) {
155+
x = event.value;
156+
}
157+
if (event.code == ABS_MT_POSITION_Y) {
158+
y = event.value;
159+
}
160+
}
161+
} while (event.type == EV_SYN);
162+
return true;
163+
}
164+
165+
public:
166+
int x, y, down;
167+
EventThread() : Thread(false),
168+
x(0), y(0), down(0)
169+
{
170+
fd = open("/dev/input/event1", O_RDONLY);
171+
}
172+
};
173+
sp<EventThread> thread;
174+
175+
public:
176+
TouchEvents() {
177+
thread = new EventThread();
178+
thread->run("EventThread", PRIORITY_URGENT_DISPLAY);
179+
}
180+
181+
int getMostRecentPosition(int* x, int* y) {
182+
*x = thread->x;
183+
*y = thread->y;
184+
return thread->down;
185+
}
186+
};
187+
188+
189+
struct Queue {
190+
struct position {
191+
int x, y;
192+
};
193+
int index;
194+
position q[16];
195+
Queue() : index(0) { }
196+
void push(int x, int y) {
197+
index++;
198+
index &= 0xF;
199+
q[index].x = x;
200+
q[index].y = y;
201+
}
202+
void get(int lag, int* x, int* y) {
203+
const int i = (index - lag) & 0xF;
204+
*x = q[i].x;
205+
*y = q[i].y;
206+
}
207+
};
208+
209+
extern char *optarg;
210+
extern int optind;
211+
extern int optopt;
212+
extern int opterr;
213+
extern int optreset;
214+
215+
void usage(const char* name) {
216+
printf("\nusage: %s [-h] [-l lag]\n", name);
217+
}
218+
219+
int main(int argc, char** argv) {
220+
fb_var_screeninfo vi;
221+
fb_fix_screeninfo fi;
222+
223+
int lag = 0;
224+
int fd = open("/dev/graphics/fb0", O_RDWR);
225+
ioctl(fd, FBIOGET_VSCREENINFO, &vi);
226+
ioctl(fd, FBIOGET_FSCREENINFO, &fi);
227+
void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
228+
Buffer framebuffer;
229+
framebuffer.w = vi.xres;
230+
framebuffer.h = vi.yres;
231+
framebuffer.s = fi.line_length / (vi.bits_per_pixel >> 3);
232+
framebuffer.addr = bits;
233+
234+
int ch;
235+
while ((ch = getopt(argc, argv, "hl:")) != -1) {
236+
switch (ch) {
237+
case 'l':
238+
lag = atoi(optarg);
239+
break;
240+
case 'h':
241+
default:
242+
usage(argv[0]);
243+
exit(0);
244+
}
245+
}
246+
argc -= optind;
247+
argv += optind;
248+
249+
250+
TouchEvents touch;
251+
Queue queue;
252+
253+
254+
int x=0, y=0, down=0;
255+
int lag_x=0, lag_y=0;
256+
257+
clearBuffer(&framebuffer, 0);
258+
while (true) {
259+
uint32_t crt = 0;
260+
int err = ioctl(fd, FBIO_WAITFORVSYNC, &crt);
261+
262+
// draw beam marker
263+
drawRect(&framebuffer, 0x400000, framebuffer.w-2, 0, 2, framebuffer.h);
264+
// erase screen
265+
if (lag) {
266+
drawCircle(&framebuffer, 0, lag_x, lag_y, 100);
267+
drawHLine(&framebuffer, 0, 0, lag_y, 32);
268+
}
269+
drawCircle(&framebuffer, 0, x, y, 100, true);
270+
drawHLine(&framebuffer, 0, 0, y, 32);
271+
272+
// draw a line at y=1000
273+
drawHLine(&framebuffer, 0x808080, 0, 1000, framebuffer.w);
274+
275+
// get touch events
276+
touch.getMostRecentPosition(&x, &y);
277+
queue.push(x, y);
278+
queue.get(lag, &lag_x, &lag_y);
279+
280+
if (lag) {
281+
drawCircle(&framebuffer, 0x00FF00, lag_x, lag_y, 100);
282+
drawHLine(&framebuffer, 0x00FF00, 0, lag_y, 32);
283+
}
284+
285+
drawCircle(&framebuffer, 0xFFFFFF, x, y, 100, true);
286+
drawHLine(&framebuffer, 0xFFFFFF, 0, y, 32);
287+
288+
// draw end of frame beam marker
289+
drawRect(&framebuffer, 0x004000, framebuffer.w-2, 0, 2, framebuffer.h);
290+
}
291+
292+
close(fd);
293+
return 0;
294+
}

0 commit comments

Comments
 (0)