mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-01-18 12:06:08 +00:00
Revent: Added support for Vsync timings.
Updated revent to use timing information provided from VSync service running on android to try and improve timing accuracy. Revent now takes `-V` parameter for both the record and replay command to indicate that the VSync service should be used.
This commit is contained in:
parent
d6eb8b6faf
commit
a81fe5555a
Binary file not shown.
Binary file not shown.
3
wlauto/external/revent/Makefile
vendored
3
wlauto/external/revent/Makefile
vendored
@ -1,6 +1,7 @@
|
||||
# CROSS_COMPILE=aarch64-linux-gnu- make
|
||||
#
|
||||
CC=gcc
|
||||
LIBFLAGS= -lm
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS=-static -lc -g
|
||||
@ -9,7 +10,7 @@ else
|
||||
endif
|
||||
|
||||
revent: revent.c
|
||||
$(CROSS_COMPILE)$(CC) $(CFLAGS) revent.c -o revent
|
||||
$(CROSS_COMPILE)$(CC) $(CFLAGS) revent.c -o revent $(LIBFLAGS)
|
||||
|
||||
clean:
|
||||
rm -rf revent
|
||||
|
399
wlauto/external/revent/revent.c
vendored
399
wlauto/external/revent/revent.c
vendored
@ -28,6 +28,11 @@
|
||||
#include <linux/input.h>
|
||||
#include <linux/uinput.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#define die(args...) do { \
|
||||
fprintf(stderr, "ERROR: "); \
|
||||
fprintf(stderr, args); \
|
||||
@ -120,8 +125,15 @@ typedef struct {
|
||||
replay_event_t *events;
|
||||
} revent_recording_t;
|
||||
|
||||
static void read_nbytes(int fd, void* buf, size_t nbytes);
|
||||
static int open_vsync_fd();
|
||||
static void drain_vsync_fd(int vfd);
|
||||
void handle_vsync_event(int fd, struct timeval* delta);
|
||||
static void close_vsync_fd(int fd);
|
||||
|
||||
bool_t verbose = FALSE;
|
||||
bool_t wait_for_stdin = TRUE;
|
||||
bool_t vsync_enabled = FALSE;
|
||||
|
||||
bool_t is_numeric(char *string)
|
||||
{
|
||||
@ -474,8 +486,8 @@ int write_device_info(FILE *fout, const device_info_t *info)
|
||||
ret += fwrite(info->key_bits, sizeof(char), KEY_BITS_SIZE, fout);
|
||||
if (ret < (EV_BITS_SIZE + KEY_BITS_SIZE * 3))
|
||||
return EIO;
|
||||
printf("EV_BITS_SIZE: %d\n", EV_BITS_SIZE);
|
||||
printf("KEY_BITS_SIZE: %d\n", KEY_BITS_SIZE);
|
||||
printf("EV_BITS_SIZE: %d\n", EV_BITS_SIZE);
|
||||
printf("KEY_BITS_SIZE: %d\n", KEY_BITS_SIZE);
|
||||
|
||||
ret = fwrite(&info->num_absinfo, sizeof(uint32_t), 1, fout);
|
||||
if (ret < 1)
|
||||
@ -532,7 +544,7 @@ void print_device_info(device_info_t *info)
|
||||
{
|
||||
printf("device name: %s\n", info->name);
|
||||
printf("bustype: 0x%x vendor: 0x%x product: 0x%x version: 0x%x\n",
|
||||
info->id.bustype, info->id.vendor, info->id.product, info->id.version);
|
||||
info->id.bustype, info->id.vendor, info->id.product, info->id.version);
|
||||
printf("abs_bits: %d\n", count_bits(info->abs_bits));
|
||||
printf("rel_bits: %d\n", count_bits(info->rel_bits));
|
||||
printf("key_bits: %d\n", count_bits(info->key_bits));
|
||||
@ -561,6 +573,7 @@ void print_device_info(device_info_t *info)
|
||||
|
||||
int write_replay_event(FILE *fout, const replay_event_t *ev)
|
||||
{
|
||||
|
||||
size_t ret;
|
||||
uint64_t time;
|
||||
|
||||
@ -711,7 +724,7 @@ int init_general_input_devices(input_devices_t *devices)
|
||||
uint32_t num, i, path_len;
|
||||
char paths[INPDEV_MAX_DEVICES][INPDEV_MAX_PATH];
|
||||
int fds[INPDEV_MAX_DEVICES];
|
||||
int max_fd;
|
||||
int max_fd = 0;
|
||||
|
||||
num = 0;
|
||||
for(i = 0; i < INPDEV_MAX_DEVICES; ++i) {
|
||||
@ -935,8 +948,8 @@ int create_replay_device_or_die(const device_info_t *info)
|
||||
if(ioctl(fd, UI_DEV_CREATE) < 0)
|
||||
die("Could not create replay device:", strerror(errno));
|
||||
|
||||
// wait for the new device to be recognised by the system
|
||||
sleep(3);
|
||||
// wait for the new device to be recognised by the system
|
||||
sleep(3);
|
||||
|
||||
return fd;
|
||||
}
|
||||
@ -1033,7 +1046,7 @@ void open_gamepad_input_devices_for_playback_or_die(input_devices_t *devices, co
|
||||
//Used to exit program properly on termination
|
||||
static volatile int EXIT = 0;
|
||||
void exitHandler(int z) {
|
||||
EXIT = 1;
|
||||
EXIT = 1;
|
||||
}
|
||||
|
||||
void record(const char *filepath, int delay, recording_mode_t mode)
|
||||
@ -1046,6 +1059,16 @@ void record(const char *filepath, int delay, recording_mode_t mode)
|
||||
input_devices_t devices;
|
||||
init_input_devices(&devices);
|
||||
|
||||
// Get vsync source.
|
||||
int vfd;
|
||||
struct timeval vsync_correction_tv = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = 0
|
||||
};
|
||||
if (vsync_enabled){
|
||||
vfd = open_vsync_fd();
|
||||
}
|
||||
|
||||
if (mode == GENERAL_MODE) {
|
||||
ret = init_general_input_devices(&devices);
|
||||
if (ret)
|
||||
@ -1099,11 +1122,12 @@ void record(const char *filepath, int delay, recording_mode_t mode)
|
||||
while(1)
|
||||
{
|
||||
FD_ZERO(&readfds);
|
||||
if (wait_for_stdin)
|
||||
FD_SET(STDIN_FILENO, &readfds);
|
||||
FD_SET(STDIN_FILENO, &readfds);
|
||||
for (i=0; i < devices.num; i++)
|
||||
FD_SET(devices.fds[i], &readfds);
|
||||
|
||||
if (vsync_enabled){
|
||||
FD_SET(vfd, &readfds);
|
||||
}
|
||||
/* wait for input */
|
||||
tout.tv_sec = delay;
|
||||
tout.tv_nsec = 0;
|
||||
@ -1129,6 +1153,8 @@ void record(const char *filepath, int delay, recording_mode_t mode)
|
||||
rev.event.code = KEY_ENTER;
|
||||
rev.event.value = 0;
|
||||
gettimeofday(&rev.event.time, NULL);
|
||||
if (vsync_enabled)
|
||||
timersub(&rev.event.time, &vsync_correction_tv, &rev.event.time); // vsync calibrate
|
||||
write_replay_event(fout, &rev);
|
||||
|
||||
// syn
|
||||
@ -1138,6 +1164,8 @@ void record(const char *filepath, int delay, recording_mode_t mode)
|
||||
rev.event.code = 0;
|
||||
rev.event.value = 0;
|
||||
gettimeofday(&rev.event.time, NULL);
|
||||
if (vsync_enabled)
|
||||
timersub(&rev.event.time, &vsync_correction_tv, &rev.event.time); // vsync calibrate
|
||||
write_replay_event(fout, &rev);
|
||||
|
||||
dprintf("added fake return exiting...\n");
|
||||
@ -1152,17 +1180,29 @@ void record(const char *filepath, int delay, recording_mode_t mode)
|
||||
memset(&rev, 0, sizeof(rev));
|
||||
rev.dev_idx = i;
|
||||
ret = read(devices.fds[i], (void *)&rev.event, sizeof(rev.event));
|
||||
if (vsync_enabled)
|
||||
timersub(&rev.event.time, &vsync_correction_tv, &rev.event.time); // vsync calibrate
|
||||
dprintf("%d event: type %d code %d value %d\n",
|
||||
(unsigned int)ret, rev.event.type, rev.event.code, rev.event.value);
|
||||
if (rev.event.type == EV_KEY && rev.event.code == KEY_ENTER && rev.event.value == 1)
|
||||
keydev = i;
|
||||
write_replay_event(fout, &rev);
|
||||
|
||||
event_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (vsync_enabled && FD_ISSET(vfd, &readfds)) {
|
||||
handle_vsync_event(vfd, &vsync_correction_tv);
|
||||
}
|
||||
}
|
||||
|
||||
if (vsync_enabled){
|
||||
close_vsync_fd(vfd);
|
||||
}
|
||||
|
||||
dprintf("Writing event count...");
|
||||
|
||||
if ((ret = fseek(fout, size_pos, SEEK_SET)) == -1)
|
||||
die("Could not write event count: %s", strerror(errno));
|
||||
ret = fwrite(&event_count, sizeof(uint64_t), 1, fout);
|
||||
@ -1213,7 +1253,7 @@ void dump(const char *filepath)
|
||||
recording.events[i].event.type,
|
||||
recording.events[i].event.code,
|
||||
recording.events[i].event.value
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
fini_revent_recording(&recording);
|
||||
@ -1221,21 +1261,30 @@ void dump(const char *filepath)
|
||||
|
||||
void replay(const char *filepath)
|
||||
{
|
||||
// Get vsync source.
|
||||
int vfd;
|
||||
struct timeval vsync_correction_tv = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = 0
|
||||
};
|
||||
if (vsync_enabled)
|
||||
vfd = open_vsync_fd();
|
||||
|
||||
revent_recording_t recording;
|
||||
init_revent_recording(&recording);
|
||||
|
||||
read_revent_recording_or_die(filepath, &recording);
|
||||
switch (recording.desc.mode) {
|
||||
case GENERAL_MODE:
|
||||
dprintf("Opening input devices for playback\n");
|
||||
open_general_input_devices_for_playback_or_die(&recording.devices);
|
||||
break;
|
||||
case GAMEPAD_MODE:
|
||||
dprintf("Creating gamepad playback device\n");
|
||||
open_gamepad_input_devices_for_playback_or_die(&recording.devices, recording.gamepad_info);
|
||||
break;
|
||||
default:
|
||||
die("Unexpected recording mod: %d", recording.desc.mode);
|
||||
case GENERAL_MODE:
|
||||
dprintf("Opening input devices for playback\n");
|
||||
open_general_input_devices_for_playback_or_die(&recording.devices);
|
||||
break;
|
||||
case GAMEPAD_MODE:
|
||||
dprintf("Creating gamepad playback device\n");
|
||||
open_gamepad_input_devices_for_playback_or_die(&recording.devices, recording.gamepad_info);
|
||||
break;
|
||||
default:
|
||||
die("Unexpected recording mod: %d", recording.desc.mode);
|
||||
}
|
||||
dprintf("Adjusting event timestamps\n");
|
||||
adjust_event_times(&recording);
|
||||
@ -1247,40 +1296,109 @@ void replay(const char *filepath)
|
||||
int ret;
|
||||
uint64_t i = 0;
|
||||
dprintf("Starting payback\n");
|
||||
while (i < recording.num_events) {
|
||||
gettimeofday(&now, NULL);
|
||||
timeradd(&start_time, &last_event_delta, &desired_time);
|
||||
if (vsync_enabled){
|
||||
dprintf("Using VSync\n");
|
||||
while (i < recording.num_events) {
|
||||
// Wait until the next event time, or until the next vsync event, whichever is sooner.
|
||||
while (1) {
|
||||
// Adjust system time by delta derived from vsyncs.
|
||||
gettimeofday(&now, NULL);
|
||||
timersub(&now, &vsync_correction_tv, &now); // vsync calibrate
|
||||
|
||||
if (timercmp(&desired_time, &now, >)) {
|
||||
timersub(&desired_time, &now, &delta);
|
||||
useconds_t d = (useconds_t)delta.tv_sec * 1000000 + delta.tv_usec;
|
||||
dprintf("now %u.%u desiredtime %u.%u sleeping %u uS\n",
|
||||
(unsigned int)now.tv_sec,
|
||||
(unsigned int)now.tv_usec,
|
||||
(unsigned int)desired_time.tv_sec,
|
||||
(unsigned int)desired_time.tv_usec,
|
||||
d);
|
||||
usleep(d);
|
||||
timeradd(&start_time, &last_event_delta, &desired_time);
|
||||
timersub(&desired_time, &now, &delta);
|
||||
|
||||
if (timercmp(&desired_time, &now, <=)) {
|
||||
break;
|
||||
}
|
||||
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(vfd, &rfds);
|
||||
int r = select(vfd + 1, &rfds, /* writefds */ NULL, /* errorfds */ NULL, &delta);
|
||||
|
||||
// I/O failure. Fall back.
|
||||
if (r < 0) {
|
||||
dprintf("Vsync Failure, falling back...\n");
|
||||
gettimeofday(&now, NULL);
|
||||
timeradd(&start_time, &last_event_delta, &desired_time);
|
||||
if (timercmp(&desired_time, &now, >)) {
|
||||
timersub(&desired_time, &now, &delta);
|
||||
useconds_t d = (useconds_t)delta.tv_sec * 1000000 + delta.tv_usec;
|
||||
dprintf("now %u.%u desiredtime %u.%u sleeping %u uS\n",
|
||||
(unsigned int)now.tv_sec,
|
||||
(unsigned int)now.tv_usec,
|
||||
(unsigned int)desired_time.tv_sec,
|
||||
(unsigned int)desired_time.tv_usec,
|
||||
d);
|
||||
usleep(d);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// VSync occurred, so update delta and wait again for the event.
|
||||
} else if (r == 1) {
|
||||
handle_vsync_event(vfd, &vsync_correction_tv);
|
||||
|
||||
// An actual event occurred so execute it.
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t idx = (recording.events[i]).dev_idx;
|
||||
struct input_event ev = (recording.events[i]).event;
|
||||
while((i < recording.num_events) && !timercmp(&ev.time, &last_event_delta, !=)) {
|
||||
ret = write(recording.devices.fds[idx], &ev, sizeof(ev));
|
||||
if (ret != sizeof(ev))
|
||||
die("Could not replay event");
|
||||
dprintf("replayed event: type %d code %d value %d\n", ev.type, ev.code, ev.value);
|
||||
|
||||
i++;
|
||||
idx = recording.events[i].dev_idx;
|
||||
ev = recording.events[i].event;
|
||||
}
|
||||
last_event_delta = ev.time;
|
||||
}
|
||||
}
|
||||
else {
|
||||
while (i < recording.num_events) {
|
||||
gettimeofday(&now, NULL);
|
||||
timeradd(&start_time, &last_event_delta, &desired_time);
|
||||
|
||||
int32_t idx = (recording.events[i]).dev_idx;
|
||||
struct input_event ev = (recording.events[i]).event;
|
||||
while((i < recording.num_events) && !timercmp(&ev.time, &last_event_delta, !=)) {
|
||||
ret = write(recording.devices.fds[idx], &ev, sizeof(ev));
|
||||
if (ret != sizeof(ev))
|
||||
die("Could not replay event");
|
||||
dprintf("replayed event: type %d code %d value %d\n", ev.type, ev.code, ev.value);
|
||||
if (timercmp(&desired_time, &now, >)) {
|
||||
timersub(&desired_time, &now, &delta);
|
||||
useconds_t d = (useconds_t)delta.tv_sec * 1000000 + delta.tv_usec;
|
||||
dprintf("now %u.%u desiredtime %u.%u sleeping %u uS\n",
|
||||
(unsigned int)now.tv_sec,
|
||||
(unsigned int)now.tv_usec,
|
||||
(unsigned int)desired_time.tv_sec,
|
||||
(unsigned int)desired_time.tv_usec,
|
||||
d);
|
||||
usleep(d);
|
||||
}
|
||||
|
||||
i++;
|
||||
idx = recording.events[i].dev_idx;
|
||||
ev = recording.events[i].event;
|
||||
int32_t idx = (recording.events[i]).dev_idx;
|
||||
struct input_event ev = (recording.events[i]).event;
|
||||
while((i < recording.num_events) && !timercmp(&ev.time, &last_event_delta, !=)) {
|
||||
ret = write(recording.devices.fds[idx], &ev, sizeof(ev));
|
||||
if (ret != sizeof(ev))
|
||||
die("Could not replay event");
|
||||
dprintf("replayed event: type %d code %d value %d\n", ev.type, ev.code, ev.value);
|
||||
|
||||
i++;
|
||||
idx = recording.events[i].dev_idx;
|
||||
ev = recording.events[i].event;
|
||||
}
|
||||
last_event_delta = ev.time;
|
||||
}
|
||||
last_event_delta = ev.time;
|
||||
}
|
||||
|
||||
if (recording.desc.mode == GAMEPAD_MODE)
|
||||
if (recording.desc.mode == GAMEPAD_MODE)
|
||||
destroy_replay_device(recording.devices.fds[0]);
|
||||
fini_revent_recording(&recording);
|
||||
if (vsync_enabled){
|
||||
close_vsync_fd(vfd);
|
||||
}
|
||||
}
|
||||
|
||||
void info(void)
|
||||
@ -1374,7 +1492,7 @@ void revent_args_init(revent_args_t **rargs, int argc, char** argv)
|
||||
revent_args->file = NULL;
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "hgt:d:vs")) != -1)
|
||||
while ((opt = getopt(argc, argv, "hgt:d:vsV")) != -1)
|
||||
{
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
@ -1406,6 +1524,9 @@ void revent_args_init(revent_args_t **rargs, int argc, char** argv)
|
||||
case 's':
|
||||
wait_for_stdin = FALSE;
|
||||
break;
|
||||
case 'V':
|
||||
vsync_enabled = TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
die("Unexpected option: %c", opt);
|
||||
@ -1489,3 +1610,187 @@ int main(int argc, char** argv)
|
||||
revent_args_close(rargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---- vsync event handler.-----
|
||||
|
||||
const int64_t us_per_sec = 1000 * 1000;
|
||||
const int64_t ns_per_us = 1000;
|
||||
|
||||
static int64_t first_frame_nanos;
|
||||
static struct timeval first_time_val;
|
||||
|
||||
static size_t vsync_count;
|
||||
static int64_t latest_frame_nanos;
|
||||
static struct timeval latest_time_val;
|
||||
static int64_t total_drift_ns;
|
||||
static int64_t total_abs_drift_ns;
|
||||
|
||||
// Circular buffer used to calculate std dev of drift.
|
||||
static const size_t max_drift_records = 1024;
|
||||
static int64_t drifts_ns[1024];//max_drift_records]; ##CHANGE
|
||||
static size_t db_cur;
|
||||
|
||||
/**
|
||||
The read function can return before reading all of the requested bytes,
|
||||
This function ensures that exactly nbytes are read (or a failure occurs,
|
||||
or the remote end is closed) before it returns the caller.
|
||||
*/
|
||||
static void read_nbytes(int fd, void* buf, size_t nbytes) {
|
||||
size_t bytes_read = 0;
|
||||
unsigned char* bufc = (unsigned char*)(buf);
|
||||
|
||||
while (bytes_read < nbytes) {
|
||||
size_t remaining_bytes = nbytes - bytes_read;
|
||||
int r = read(fd, &bufc[bytes_read], remaining_bytes);
|
||||
|
||||
// Zero means remote end has been closed.
|
||||
if (r < 0)
|
||||
die("%s: unexpected read value (%d, %s)", __func__, r, strerror(r));
|
||||
if (r == 0)
|
||||
die("%s: remote end unexpectedly closed.", __func__);
|
||||
|
||||
bytes_read += r;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Opens a Unix domain socket. The other end is connected to ChoreoService.
|
||||
|
||||
@see handle_vsync_event
|
||||
*/
|
||||
static int open_vsync_fd() {
|
||||
// Create a blank socket which will be connected to ChoreoService.
|
||||
int vfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (vfd == -1)
|
||||
die("open_vsync_fd: could not create socket");
|
||||
|
||||
// The Android class LocalServerSocket creates sockets in the abstract namespace.
|
||||
// Therefore, the name must be prefixed with a '\0' character.
|
||||
const char name[] = "vsync-uds";
|
||||
size_t namelen = strlen(name);
|
||||
|
||||
struct sockaddr_un sau;
|
||||
memset(&sau, 0, sizeof(sau));
|
||||
sau.sun_family = AF_LOCAL;
|
||||
sau.sun_path[0] = '\0';
|
||||
memcpy(&sau.sun_path[1], name, namelen); // no null terminator
|
||||
|
||||
socklen_t slen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
|
||||
int r = connect(vfd, (const struct sockaddr*) &sau, slen);
|
||||
if (r == -1)
|
||||
die("Vysnc Service not available\n");
|
||||
|
||||
drain_vsync_fd(vfd);
|
||||
|
||||
read_nbytes(vfd, &first_frame_nanos, sizeof(first_frame_nanos));
|
||||
|
||||
gettimeofday(&first_time_val, /* tzp */ NULL);
|
||||
|
||||
vsync_count = 0;
|
||||
total_drift_ns = 0;
|
||||
total_abs_drift_ns = 0;
|
||||
db_cur = 0;
|
||||
|
||||
return vfd;
|
||||
}
|
||||
|
||||
/**
|
||||
Drain any stale timestamps in the socket.
|
||||
*/
|
||||
static void drain_vsync_fd(int vfd) {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(vfd, &fds);
|
||||
|
||||
// Zero-length timeout for the select function. This is not related to the value of
|
||||
// the timestamps used for callibration.
|
||||
struct timeval tvNoWait = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = 0
|
||||
};
|
||||
|
||||
for (;;) {
|
||||
// Returns immediately if no reads are available.
|
||||
// Advanced Programming in the Unix Environment 2ed, S14.5.1.
|
||||
int r = select(/* maxfd */ vfd, /* readfds */ &fds, /* writefds */ NULL, /* exceptfds */ NULL, &tvNoWait);
|
||||
|
||||
// No events, so socket has been exhausted.
|
||||
if (r == 0) {
|
||||
break;
|
||||
// Read event, so get another frame timestamp.
|
||||
} else if (r == 1) {
|
||||
uint64_t frame_time_nanos;
|
||||
read_nbytes(vfd, &frame_time_nanos, sizeof(frame_time_nanos));
|
||||
} else {
|
||||
die("%s: unexpected select value %d", __func__, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_vsync_event(int fd, struct timeval* delta) {
|
||||
++vsync_count;
|
||||
|
||||
// Get nanoseconds since first frame, as recorded via frame times.
|
||||
read_nbytes(fd, &latest_frame_nanos, sizeof(latest_frame_nanos));
|
||||
int64_t frame_elapsed_ns = latest_frame_nanos - first_frame_nanos;
|
||||
|
||||
// Get time since first frame, as recorded via gettimeofday.
|
||||
gettimeofday(&latest_time_val, /* tzp */ NULL);
|
||||
struct timeval tv_since_start;
|
||||
timersub(&latest_time_val, &first_time_val, &tv_since_start);
|
||||
|
||||
// Convert both above measurements to nanoseconds.
|
||||
int64_t tv_elapsed_us = (tv_since_start.tv_sec * us_per_sec) + tv_since_start.tv_usec;
|
||||
int64_t tv_elapsed_ns = tv_elapsed_us * ns_per_us;
|
||||
|
||||
// Drift is how far time, as recorded by gtod has diverged from frame times.
|
||||
// This is returned as an instance of timeval so it can be used by the main loop.
|
||||
int64_t drift_ns = tv_elapsed_ns -frame_elapsed_ns;
|
||||
int64_t drift_us = drift_ns / ns_per_us;
|
||||
|
||||
total_drift_ns += drift_ns;
|
||||
total_abs_drift_ns += llabs(drift_ns);
|
||||
|
||||
delta->tv_sec = drift_us / us_per_sec;
|
||||
delta->tv_usec = drift_us - (delta->tv_sec * us_per_sec);
|
||||
|
||||
drifts_ns[db_cur] = drift_ns;
|
||||
db_cur = (db_cur + 1) % max_drift_records;
|
||||
}
|
||||
|
||||
void close_vsync_fd(int fd) {
|
||||
close(fd);
|
||||
|
||||
int64_t frame_elapsed_ns = latest_frame_nanos - first_frame_nanos;
|
||||
|
||||
struct timeval tv_since_start;
|
||||
timersub(&latest_time_val, &first_time_val, &tv_since_start);
|
||||
int64_t gtod_elapsed_us = (tv_since_start.tv_sec * us_per_sec) + tv_since_start.tv_usec;
|
||||
int64_t gtod_elapsed_ns = gtod_elapsed_us * ns_per_us;
|
||||
|
||||
int64_t delta_ns = gtod_elapsed_ns - frame_elapsed_ns;
|
||||
|
||||
// Calculate standard deviation of drifts.
|
||||
// The math here isn't quite true because it only considers those drifts
|
||||
// which are in the circular buffer.
|
||||
size_t ad_count = (vsync_count > max_drift_records) ? max_drift_records : db_cur;
|
||||
|
||||
int64_t ad_sum = 0;
|
||||
int64_t ad_abs_sum = 0;
|
||||
size_t i;
|
||||
for (i = 0; i < ad_count; ++i) {
|
||||
ad_sum += drifts_ns[i];
|
||||
ad_abs_sum += llabs(drifts_ns[i]);
|
||||
}
|
||||
int64_t ad_avg = ad_sum / ad_count;
|
||||
int64_t ad_abs_avg = ad_abs_sum / ad_count;
|
||||
|
||||
int64_t ad_dev_ns = 0;
|
||||
for (i = 0; i < ad_count; ++i) {
|
||||
int64_t dev_ns = drifts_ns[i] - ad_avg;
|
||||
ad_dev_ns += dev_ns * dev_ns;
|
||||
}
|
||||
ad_dev_ns /= (ad_count - 1); // sample, not population std dev
|
||||
long double ad_smp_dev_ld_ns = __builtin_sqrtl((long double)(ad_dev_ns));
|
||||
int64_t ad_smp_dev_ns = (int64_t)(ad_smp_dev_ld_ns);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user