31f91248302e476fa4ccf14dd530cc0bb3fc3035
[pcsx_rearmed.git] / fps.c
1 /***************************************************************************
2                           fps.c  -  description
3                              -------------------
4     begin                : Sun Oct 28 2001
5     copyright            : (C) 2001 by Pete Bernert
6     email                : BlackDove@addcom.de
7  ***************************************************************************/
8 /***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version. See also the license.txt file for *
14  *   additional informations.                                              *
15  *                                                                         *
16  ***************************************************************************/
17
18 // FPS stuff
19 float          fFrameRateHz=0;
20 DWORD          dwFrameRateTicks=16;
21 float          fFrameRate=200.0f;
22 int            iFrameLimit=2;
23 int            UseFrameLimit=0;
24 int            UseFrameSkip=0;
25
26 // FPS skipping / limit
27 BOOL   bInitCap = TRUE;
28 float  fps_skip = 0;
29 float  fps_cur  = 0;
30
31 #define TIMEBASE 100000
32 #define MAXLACE 16
33 #define MAXSKIP 120
34
35 static void calcfps(void);
36 static void FrameCap(void);
37
38 void CheckFrameRate(void)
39 {
40  if(UseFrameSkip)                                      // skipping mode?
41   {
42    if(!(dwActFixes&0x80))                              // not old skipping mode?
43     {
44      dwLaceCnt++;                                      // -> store cnt of vsync between frames
45      if(dwLaceCnt>=MAXLACE && UseFrameLimit)           // -> if there are many laces without screen toggling,
46       {                                                //    do std frame limitation
47        if(dwLaceCnt==MAXLACE) bInitCap=TRUE;
48        FrameCap();
49       }
50     }
51    else if(UseFrameLimit) FrameCap();
52    calcfps();                                          // -> calc fps display in skipping mode
53   }
54  else                                                  // non-skipping mode:
55   {
56    if(UseFrameLimit) FrameCap();                       // -> do it
57    calcfps();                                          // -> and calc fps display
58   }
59 }
60
61 static unsigned long timeGetTime(void)
62 {
63  struct timeval tv;
64  gettimeofday(&tv, 0);                                 // well, maybe there are better ways
65  return tv.tv_sec * 100000 + tv.tv_usec/10;            // to do that, but at least it works
66 }
67
68 static void FrameCap (void)
69 {
70  static unsigned long curticks, lastticks, _ticks_since_last_update;
71  static unsigned int TicksToWait = 0;
72  int overslept=0, tickstogo=0;
73  BOOL Waiting = TRUE;
74
75   {
76    curticks = timeGetTime();
77    _ticks_since_last_update = curticks - lastticks;
78
79     if((_ticks_since_last_update > TicksToWait) ||
80        (curticks <lastticks))
81     {
82      lastticks = curticks;
83      overslept = _ticks_since_last_update - TicksToWait;
84      if((_ticks_since_last_update-TicksToWait) > dwFrameRateTicks)
85           TicksToWait=0;
86      else
87           TicksToWait=dwFrameRateTicks - overslept;
88     }
89    else
90     {
91      while (Waiting)
92       {
93        curticks = timeGetTime();
94        _ticks_since_last_update = curticks - lastticks;
95        tickstogo = TicksToWait - _ticks_since_last_update;
96        if ((_ticks_since_last_update > TicksToWait) ||
97            (curticks < lastticks) || tickstogo < overslept)
98         {
99          Waiting = FALSE;
100          lastticks = curticks;
101          overslept = _ticks_since_last_update - TicksToWait;
102          TicksToWait = dwFrameRateTicks - overslept;
103          return;
104         }
105         if (tickstogo >= 200 && !(dwActFixes&16))
106                 usleep(tickstogo*10 - 200);
107       }
108     }
109   }
110 }
111
112 static void FrameSkip(void)
113 {
114  static int   iNumSkips=0,iAdditionalSkip=0;           // number of additional frames to skip
115  static DWORD dwLastLace=0;                            // helper var for frame limitation
116  static DWORD curticks, lastticks, _ticks_since_last_update;
117  int tickstogo=0;
118  static int overslept=0;
119
120  if(!dwLaceCnt) return;                                // important: if no updatelace happened, we ignore it completely
121
122  if(iNumSkips)                                         // we are in skipping mode?
123   {
124    dwLastLace+=dwLaceCnt;                              // -> calc frame limit helper (number of laces)
125    bSkipNextFrame = TRUE;                              // -> we skip next frame
126    iNumSkips--;                                        // -> ok, one done
127   }
128  else                                                  // ok, no additional skipping has to be done...
129   {                                                    // we check now, if some limitation is needed, or a new skipping has to get started
130    DWORD dwWaitTime;
131
132    if(bInitCap || bSkipNextFrame)                      // first time or we skipped before?
133     {
134      if(UseFrameLimit && !bInitCap)                    // frame limit wanted and not first time called?
135       {
136        DWORD dwT=_ticks_since_last_update;             // -> that's the time of the last drawn frame
137        dwLastLace+=dwLaceCnt;                          // -> and that's the number of updatelace since the start of the last drawn frame
138
139        curticks = timeGetTime();                       // -> now we calc the time of the last drawn frame + the time we spent skipping
140        _ticks_since_last_update= dwT+curticks - lastticks;
141
142        dwWaitTime=dwLastLace*dwFrameRateTicks;         // -> and now we calc the time the real psx would have needed
143
144        if(_ticks_since_last_update<dwWaitTime)         // -> we were too fast?
145         {
146          if((dwWaitTime-_ticks_since_last_update)>     // -> some more security, to prevent
147             (60*dwFrameRateTicks))                     //    wrong waiting times
148           _ticks_since_last_update=dwWaitTime;
149
150          while(_ticks_since_last_update<dwWaitTime)    // -> loop until we have reached the real psx time
151           {                                            //    (that's the additional limitation, yup)
152            curticks = timeGetTime();
153            _ticks_since_last_update = dwT+curticks - lastticks;
154           }
155         }
156        else                                            // we were still too slow ?!!?
157         {
158          if(iAdditionalSkip<MAXSKIP)                   // -> well, somewhen we really have to stop skipping on very slow systems
159           {
160            iAdditionalSkip++;                          // -> inc our watchdog var
161            dwLaceCnt=0;                                // -> reset lace count
162            lastticks = timeGetTime();
163            return;                                     // -> done, we will skip next frame to get more speed
164           } 
165         }
166       }
167
168      bInitCap=FALSE;                                   // -> ok, we have inited the frameskip func
169      iAdditionalSkip=0;                                // -> init additional skip
170      bSkipNextFrame=FALSE;                             // -> we don't skip the next frame
171      lastticks = timeGetTime();                        // -> we store the start time of the next frame
172      dwLaceCnt=0;                                      // -> and we start to count the laces 
173      dwLastLace=0;
174      _ticks_since_last_update=0;
175      return;                                           // -> done, the next frame will get drawn
176     }
177
178    bSkipNextFrame=FALSE;                               // init the frame skip signal to 'no skipping' first
179
180    curticks = timeGetTime();                           // get the current time (we are now at the end of one drawn frame)
181    _ticks_since_last_update = curticks - lastticks;
182
183    dwLastLace=dwLaceCnt;                               // store curr count (frame limitation helper)
184    dwWaitTime=dwLaceCnt*dwFrameRateTicks;              // calc the 'real psx lace time'
185    if (dwWaitTime >= overslept)
186         dwWaitTime-=overslept;
187
188    if(_ticks_since_last_update>dwWaitTime)             // hey, we needed way too long for that frame...
189     {
190      if(UseFrameLimit)                                 // if limitation, we skip just next frame,
191       {                                                // and decide after, if we need to do more
192        iNumSkips=0;
193       }
194      else
195       {
196        iNumSkips=_ticks_since_last_update/dwWaitTime;  // -> calc number of frames to skip to catch up
197        iNumSkips--;                                    // -> since we already skip next frame, one down
198        if(iNumSkips>MAXSKIP) iNumSkips=MAXSKIP;        // -> well, somewhere we have to draw a line
199       }
200      bSkipNextFrame = TRUE;                            // -> signal for skipping the next frame
201     }
202    else                                                // we were faster than real psx? fine :)
203    if(UseFrameLimit)                                   // frame limit used? so we wait til the 'real psx time' has been reached
204     {
205      if(dwLaceCnt>MAXLACE)                             // -> security check
206       _ticks_since_last_update=dwWaitTime;
207
208      while(_ticks_since_last_update<dwWaitTime)        // -> just do a waiting loop...
209       {
210        curticks = timeGetTime();
211        _ticks_since_last_update = curticks - lastticks;
212
213         tickstogo = dwWaitTime - _ticks_since_last_update;
214         if (tickstogo-overslept >= 200 && !(dwActFixes&16))
215                 usleep(tickstogo*10 - 200);
216       }
217     }
218    overslept = _ticks_since_last_update - dwWaitTime;
219    if (overslept < 0)
220         overslept = 0;
221    lastticks = timeGetTime();                          // ok, start time of the next frame
222   }
223
224  dwLaceCnt=0;                                          // init lace counter
225 }
226
227 static void calcfps(void)
228 {
229  static unsigned long curticks,_ticks_since_last_update,lastticks;
230  static long   fps_cnt = 0;
231  static unsigned long  fps_tck = 1;
232  static long          fpsskip_cnt = 0;
233  static unsigned long fpsskip_tck = 1;
234
235   {
236    curticks = timeGetTime();
237    _ticks_since_last_update=curticks-lastticks;
238
239    if(UseFrameSkip && !UseFrameLimit && _ticks_since_last_update)
240     fps_skip=min(fps_skip,((float)TIMEBASE/(float)_ticks_since_last_update+1.0f));
241
242    lastticks = curticks;
243   }
244
245  if(UseFrameSkip && UseFrameLimit)
246   {
247    fpsskip_tck += _ticks_since_last_update;
248
249    if(++fpsskip_cnt==2)
250     {
251      fps_skip = (float)2000/(float)fpsskip_tck;
252      fps_skip +=6.0f;
253      fpsskip_cnt = 0;
254      fpsskip_tck = 1;
255     }
256   }
257
258  fps_tck += _ticks_since_last_update;
259
260  if(++fps_cnt==20)
261   {
262    fps_cur = (float)(TIMEBASE*20)/(float)fps_tck;
263
264    fps_cnt = 0;
265    fps_tck = 1;
266   }
267
268 }
269
270 static void PCFrameCap (void)
271 {
272  static unsigned long curticks, lastticks, _ticks_since_last_update;
273  static unsigned long TicksToWait = 0;
274  BOOL Waiting = TRUE;
275
276  while (Waiting)
277   {
278    curticks = timeGetTime();
279    _ticks_since_last_update = curticks - lastticks;
280    if ((_ticks_since_last_update > TicksToWait) ||
281        (curticks < lastticks))
282     {
283      Waiting = FALSE;
284      lastticks = curticks;
285      TicksToWait = (TIMEBASE/ (unsigned long)fFrameRateHz);
286     }
287   }
288 }
289
290 static void PCcalcfps(void)
291 {
292  static unsigned long curticks,_ticks_since_last_update,lastticks;
293  static long  fps_cnt = 0;
294  static float fps_acc = 0;
295  float CurrentFPS=0;
296
297  curticks = timeGetTime();
298  _ticks_since_last_update=curticks-lastticks;
299  if(_ticks_since_last_update)
300       CurrentFPS=(float)TIMEBASE/(float)_ticks_since_last_update;
301  else CurrentFPS = 0;
302  lastticks = curticks;
303
304  fps_acc += CurrentFPS;
305
306  if(++fps_cnt==10)
307   {
308    fps_cur = fps_acc / 10;
309    fps_acc = 0;
310    fps_cnt = 0;
311   }
312
313  fps_skip=CurrentFPS+1.0f;
314 }
315
316 void SetAutoFrameCap(void)
317 {
318  if(iFrameLimit==1)
319   {
320    fFrameRateHz = fFrameRate;
321    dwFrameRateTicks=(TIMEBASE*100 / (unsigned long)(fFrameRateHz*100));
322    return;
323   }
324
325  if(dwActFixes&32)
326   {
327    if (PSXDisplay.Interlaced)
328         fFrameRateHz = PSXDisplay.PAL?50.0f:60.0f;
329    else fFrameRateHz = PSXDisplay.PAL?25.0f:30.0f;
330   }
331  else
332   {
333    fFrameRateHz = PSXDisplay.PAL?50.0f:59.94f;
334    dwFrameRateTicks=(TIMEBASE*100 / (unsigned long)(fFrameRateHz*100));
335   }
336 }
337
338 static void InitFPS(void)
339 {
340  if(!fFrameRate) fFrameRate=200.0f;
341  if(fFrameRateHz==0) fFrameRateHz=fFrameRate;          // set user framerate
342  dwFrameRateTicks=(TIMEBASE / (unsigned long)fFrameRateHz);
343 }