/* scivi - visualization plugin for XMMS * Copyright (C) 2003 Vitaly V. Bursov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #include "fpsctl.h" #define C(x) (dyn_data-> x) /* copied from "info libc" */ static int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y) { /* Perform the carry for the later subtraction by updating Y. */ if (x->tv_usec < y->tv_usec) { int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; y->tv_usec -= 1000000 * nsec; y->tv_sec += nsec; } if (x->tv_usec - y->tv_usec > 1000000) { int nsec = (x->tv_usec - y->tv_usec) / 1000000; y->tv_usec += 1000000 * nsec; y->tv_sec -= nsec; } /* Compute the time remaining to wait. `tv_usec' is certainly positive. */ result->tv_sec = x->tv_sec - y->tv_sec; result->tv_usec = x->tv_usec - y->tv_usec; /* Return 1 if result is negative. */ return x->tv_sec < y->tv_sec; } #define TIME_TYPE struct timeval #define TIME_SUBTRACT(__res, __x, __y) \ timeval_subtract(&(__res), &(__x), &(__y)); #define TIME_GET(__time__) gettimeofday(&(__time__), NULL) #define TIME_TO_FLOAT(__time__) \ ((float)(__time__).tv_usec*0.000001f + (__time__).tv_sec) #ifdef HAVE_NANOSLEEP static void time_nanosleep(int nsec) { struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = nsec; nanosleep(&ts, NULL); } #define TIME_SLEEP(t) time_nanosleep(t*1000) #else # if HAVE_USLEEP # define TIME_SLEEP(t) usleep(t) # else # define TIME_SLEEP(t) { } # endif #endif typedef struct { TIME_TYPE tv_start, tv_rendertime, tv_fps_prev; TIME_TYPE tv_frstrt, tv_frendp; float oversleep; int frames; int prev_frames; int decay_fix_frame; } FPSControl_TOD; static FPSControl fps_control_tod_loop_init(Scivi *scivi, DynamicsData *dyn_data, Gui gui) { FPSControl_TOD *fc; #if 0 TIME_TYPE ct1, ct2, ctd; int i; float slpr; dprintf("calibrating sleep()...\n"); /* warmup... */ TIME_GET(ct1); TIME_SLEEP(1); TIME_GET(ct2); TIME_SUBTRACT(ctd, ct2, ct1); slpr = 0.0f; TIME_GET(ct1); for (i=1;i<=16;i++){ TIME_SLEEP(1); } TIME_GET(ct2); TIME_SUBTRACT(ctd, ct2, ct1); slpr += TIME_TO_FLOAT(ctd); slpr /= 16.0f; dprintf("done. precision: %f sec\n", slpr); #endif fc = (FPSControl_TOD *)malloc(sizeof(FPSControl_TOD)); if (!fc){ eprintf("Failed to allocate %d bytes.\n", sizeof(FPSControl_TOD)); return NULL; } fc->frames = 0; fc->prev_frames = 0; fc->decay_fix_frame = 0; fc->oversleep = 0.0f; TIME_GET(fc->tv_start); memcpy(&fc->tv_frstrt, &fc->tv_start, sizeof(TIME_TYPE)); memcpy(&fc->tv_frendp, &fc->tv_start, sizeof(TIME_TYPE)); memset(&fc->tv_rendertime, 0, sizeof(TIME_TYPE)); /* warning: probably precision loss. doesn`t important */ C(starttime) = TIME_TO_FLOAT(fc->tv_start); return fc; } static void fps_control_tod_loop_finit(Scivi *scivi, FPSControl _f) { if (_f) free(_f); } static int fps_control_tod_loop_frame_start(Scivi *scivi, FPSControl f, DynamicsData *dyn_data, Gui gui) { FPSControl_TOD *fc = (FPSControl_TOD*)f; TIME_TYPE tv, tv2, tw; TIME_TYPE tv_tmp, tv_tmp2; if (!fc) return 0; TIME_GET(tv); TIME_SUBTRACT(tv2, tv, fc->tv_frstrt); if (scivi->max_fps > 0){ tw.tv_sec = 0; tw.tv_usec = 1000000/scivi->max_fps; TIME_SUBTRACT(tv, tw, tv2); if ((tv.tv_sec >= 0) && (tv.tv_usec > 0)){ pthread_yield(); return 1; } } TIME_GET(fc->tv_frstrt); TIME_SUBTRACT(tv_tmp, fc->tv_frstrt, fc->tv_start); C(time) = TIME_TO_FLOAT(tv_tmp); fc->frames++; C(frame) = fc->frames; return 0; } static void fps_control_tod_loop_frame_end(Scivi *scivi, FPSControl _f, DynamicsData *dyn_data, Gui gui) { FPSControl_TOD *fc = (FPSControl_TOD*)_f; float f; struct timeval tv_tmp, tv_tmp2; if (!fc) return; TIME_GET(tv_tmp2); TIME_SUBTRACT(fc->tv_rendertime, tv_tmp2, fc->tv_frstrt); TIME_SUBTRACT(tv_tmp, tv_tmp2, fc->tv_fps_prev); f = TIME_TO_FLOAT(tv_tmp); if (f >= 1.0f){ gui_set_fps(gui, (float)(fc->frames-fc->prev_frames)/f); printf("fps: %f\n",(float)(fc->frames-fc->prev_frames)/f); fc->prev_frames = fc->frames; memcpy(&fc->tv_fps_prev, &tv_tmp2, sizeof(TIME_TYPE)); } } static int fps_control_tod_loop_post_decay_frames_cnt(Scivi *scivi, FPSControl f) { FPSControl_TOD *fc = (FPSControl_TOD*)f; if (!fc) return 0; return fc->frames - fc->decay_fix_frame; } static void fps_control_tod_loop_decay_point(Scivi *scivi, FPSControl f) { FPSControl_TOD *fc = (FPSControl_TOD*)f; if (!fc) return; fc->decay_fix_frame = fc->frames; } static int fps_control_sleep_loop_frame_start(Scivi *scivi, FPSControl f, DynamicsData *dyn_data, Gui gui) { FPSControl_TOD *fc = (FPSControl_TOD*)f; TIME_TYPE tv, tv2, tw; TIME_TYPE tv_tmp, tv_tmp2; if (!fc) return 0; TIME_GET(tv); TIME_SUBTRACT(tv2, tv, fc->tv_frstrt); TIME_SLEEP(1); TIME_GET(fc->tv_frstrt); TIME_SUBTRACT(tv_tmp, fc->tv_frstrt, fc->tv_start); C(time) = TIME_TO_FLOAT(tv_tmp); fc->frames++; C(frame) = fc->frames; return 0; } static int fps_control_sleep26_loop_frame_start(Scivi *scivi, FPSControl f, DynamicsData *dyn_data, Gui gui) { FPSControl_TOD *fc = (FPSControl_TOD*)f; TIME_TYPE tv, tv2, tw; TIME_TYPE tv_tmp, tv_tmp2; if (!fc) return 0; TIME_GET(tv); TIME_SUBTRACT(tv2, tv, fc->tv_frstrt); if ((scivi->max_fps > 0) && (fc->frames > 0)){ float tosleep; tosleep = (1.0/scivi->max_fps) - TIME_TO_FLOAT(tv2); if (tosleep < TIME_TO_FLOAT(fc->tv_rendertime)){ tosleep = TIME_TO_FLOAT(fc->tv_rendertime); } if (tosleep > 0.001){ float t = tosleep-fc->oversleep; if (t>0) TIME_SLEEP(1000000.0*t); } /* how long we were sleeping? */ TIME_GET(tv2); TIME_SUBTRACT(tv_tmp, tv2, tv); fc->oversleep += TIME_TO_FLOAT(tv_tmp) - tosleep; /* todo: can it happen? */ if (fc->oversleep > TIME_TO_FLOAT(fc->tv_rendertime)) fc->oversleep = 0; } TIME_GET(fc->tv_frstrt); TIME_SUBTRACT(tv_tmp, fc->tv_frstrt, fc->tv_start); C(time) = TIME_TO_FLOAT(tv_tmp); fc->frames++; C(frame) = fc->frames; return 0; } static FPSController fps_limiters[] = { { "Precise FPS control", 0, fps_control_tod_loop_init, fps_control_tod_loop_finit, fps_control_tod_loop_frame_start, fps_control_tod_loop_frame_end, fps_control_tod_loop_post_decay_frames_cnt, fps_control_tod_loop_decay_point }, { "Sleep()-driven FPS control", 1, fps_control_tod_loop_init, fps_control_tod_loop_finit, fps_control_sleep_loop_frame_start, fps_control_tod_loop_frame_end, fps_control_tod_loop_post_decay_frames_cnt, fps_control_tod_loop_decay_point }, { "Sleep()-driven FPS control. Linux 2.6", 2, fps_control_tod_loop_init, fps_control_tod_loop_finit, fps_control_sleep26_loop_frame_start, fps_control_tod_loop_frame_end, fps_control_tod_loop_post_decay_frames_cnt, fps_control_tod_loop_decay_point } }; #define fps_limiters_count (sizeof(fps_limiters)/sizeof(fps_limiters[0])) FPSController *scivi_get_fps_ctl(int idx) { if (idx < 0) idx = 0; if (idx >= fps_limiters_count) idx = fps_limiters_count-1; return &fps_limiters[idx]; } FPSController *scivi_get_fps_ctl_by_uid(int uid) { int i; for (i=0; i