Lite regulation function to be integrated into user loop for FPS control
Controlling FPS (Frames Per Second) by using an in-loop lite-regulating procedure.
Preamble:
The main objective of this regulation function is to minimize the CPU load which is added to that of the user's own code.
Therefore, all dead times used by this function to control the user FPS (Frames per second) are generated using exclusively the SLEEP keyword (no CPU consuming waiting loops).
But the exclusive use of the SLEEP keyword induces a limitation on the maximum accessible FPS value because the delay generated by SLEEP cannot drop below a limit value depending on the OS cycle period.
A workaround is integrated into this regulation function to provide from values greater than this FPS limit value no longer a true FPS but an apparent FPS which seems to match the requested FPS.
In addition, as the SLEEP keyword does not have a good accuracy on the generated delay, the FPS obtained fluctuates around the requested value.
SLEEP also produces a temporal bias which varies over time, but whose average is corrected by the regulation function.
1. Principles to overcome the penalizing behavior of SLEEP keyword
To implement the best regulation but based only on the SLEEP keyword, some bad behaviors as described above must be overcome or minimized.
Principle for measuring the minimum delay accessible with SLEEP
To evaluate the minimum delay 'tos' accessible with SLEEP, the duration of a sequence of ten 'Sleep 1, 1' is measured using the keyword TIMER and the time found is divided by ten.
But this minimum value 'tos' is not directly usable because a margin must be added to it to preserve an almost linear regulation operation.
The minimum usable value is thus fixed at '3 * tos / 2'.
This calibration phase is executed automatically on the first call to the regulation function, or if the time measured is much different than the time ordered, or on request from the user thanks to an optional parameter of the function, or on opportunity measurements when the high limit of regulation is reached (when a heel value of delay with 'Sleep tos / 2, 1' is used).
Principle for accessing an apparent FPS higher than the maximum true FPS accessible with SLEEP
When for a required FPS value, the 't' value to be applied in 'Sleep t, 1' becomes less than '3 * tos / 2', an 'image skipping' feature is started to provide an apparent FPS which matches to the required FPS.
The basic principle of 'image skipping' is to remove all delays between these images (the regulation function returns immediately) so that the persistence of these images is low compared to that which is followed by a true delay.
The smaller the image tracing time, the less these skipped images are visible.
The temporal principle for skipping 'n - 1' images over 'n' images is to generate only one delay after viewing the last image, with a value such that the total period of the sequence (of 'n' images) is equal to 'n' times the period corresponding to the required FPS.
The number of images to skip is stopped as soon as the only delay to generate for the sequence becomes greater than or equal to '3 * tos / 2'.
When 'n - 1' images are skipped over 'n' images, the apparent FPS is equal to the inverse of the full display period of the 'n' images but multiplied by 'n'.
This functionality of scrolling skipped images can be inhibited by the user thanks to an optional parameter of the function.
Conversely, the user can reinforce this functionality by downright removing the skipped images by testing an optional parameter of the function which marks these skipped images.
For this to work, the user code must very well separate the part of the code that concerns only the graphical drawing (the only part that should not be executed when the 'skipped image' tag is true) from the part of the code that concerns only the progress of the movement (which must always be executed). Otherwise, if these two parts of code are skipped at the same time (or even only a small portion of the progress of the movement), the graphical drawing will on the contrary be slowed down (although the returned FPS value says otherwise).
This operating configuration ('image skipping' +'skipped image' user removing) is therefore the most efficient (from the point of view of apparent FPS) but imposes complete separation rules between two tasks (tracing only and calculating only) in the user animation code loop.
On the other hand, the 'image skipping' configuration alone (with 'skipped image' scrolled by default) does not require any coding rules for the user.
In case of too high requested FPS and to avoid having an image that is too jerky in the case of image skipping applied, the actual refresh rate of the screen cannot go below 10 Hz in case of skipped images (the apparent FPS can be very much higher), and the number of consecutive skipped images + 1 cannot exceed 20.
These two limits when 'image skipping' is active induce an apparent displacement quasi-fluid for an initial image moving by 1 pixel every time (a displacement of 20 pixels at a frequency of 10 Hz appears to the user as a fairly smooth movement, not too jerky).
Principle for compensating for the average temporal bias produced by SLEEP
The principle is to measure (using the TIMER keyword) the real delay provided by 'Sleep t, 1' in relation with 't'.
If the time measured is much different than the time ordered, this probably means that there is an error compared to the resolution measured on initialization, and the function then will automatically restart at the next call a new calibration phase (with ten 'Sleep 1, 1').
Example: requested time = 4 µs, but measured time = 16 µs, probably because the resolution was changed (lowered) while program running.
If the time measured is only a little different from the time ordered, the differences are averaged to try to correct the average bias of the SLEEP keyword.
2. Description of the function body to include
The 'regulateLite()' function is described here.
The code is not thread safe because it uses 'Static' variables.
The function is well suited to regulate the FPS of an image refresh by inserting a delay into the loop, without adding a notable CPU load to that of the user's own code.
Note: Like any waiting feature, it is strongly discouraged to use 'regulateLite()' when the screen is locked, so no call from inside a [ScreenLock...ScreenUnlock] block.
The function has a mandatory first parameter 'MyFps' through which the user passes their required FPS.
For debugging purposes, the function returns the FPS (true or apparent) value it applied. If the user does not wish to use this debug data, then they can call the function as a Sub quite simply.
If this returned FPS value is much lower than that required, this means that it becomes difficult to reach the FPS setpoint. Otherwise, the fluctuation of the FPS returned is mainly due the use of the only SLEEP keyword to generate the delay in the loop.
Otherwise, the function proposes 3 optional parameters:
- 'Restart' ('False' by default):
This input parameter allows the user to force a new calibration phase, because for example he knows that the resolution of the OS cycle has just changed.
Set this parameter to 'True' in the call, then to 'False' on the next call.
- 'SkipImage' ('True' by default):
This input parameter allows the user to inhibit the automatic 'image skipping' feature. Whatever happens, there will be no skipped images, but perhaps to the detriment of the FPS obtained which may not follow the requested FPS.
Set this parameter to 'False' to disable the 'image skipping' feature (can be reset to 'True' later).
- 'ImageSkipped' ('False' by default bur default value irrelevant) :
This output parameter is set to 'True' at each image skipped by the function (if the 'image skipping' feature is activated).
The user can test it to know if the image is skipped or not ("image skipping" feature working in scrolling mode).
He can also use this parameter to downright not draw this skipped image at all, allowing for example even higher FPS ("image skipping" feature working in removing mode).
File to be included: "regulateLite.bi"
`
' regulateLite.bi
Function regulateLite(ByVal MyFps As ULong, ByVal SkipImage As Boolean = True, ByVal Restart As Boolean = False, ByRef ImageSkipped As Boolean = False) As ULong
'' 'MyFps' : requested FPS value, in frames per second
'' 'SkipImage' : optional parameter to activate the image skipping (True by default)
'' 'Restart' : optional parameter to force the resolution acquisition, to reset to False on the next call (False by default)
'' 'ImageSkipped' : optional parameter to inform the user that the image has been skipped (if image skipping is activated)
'' function return : applied FPS value (true or apparent), in frames per second
Static As Single tos
Static As Single bias
Static As Long count
Static As Single sum
' initialization calibration
If tos = 0 Or Restart = True Then
Dim As Double t = Timer
For I As Integer = 1 To 10
Sleep 1, 1
Next I
Dim As Double tt = Timer
#if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
If tt < t Then t -= 24 * 60 * 60
#endif
tos = (tt - t) / 10 * 1000
bias = 0
count = 0
sum = 0
End If
Static As Double t1
Static As Long N = 1
Static As ULong fps
Static As Single tf
' delay generation
Dim As Double t2 = Timer
#if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
If t2 < t1 Then t1 -= 24 * 60 * 60
#endif
Dim As Double t3 = t2
Dim As Single dt = (N * tf - (t2 - t1)) * 1000 - bias
If (dt >= 3 * tos / 2) Or (SkipImage = False) Or (N >= 20) Or (fps / N <= 10) Then
If dt <= tos Then dt = tos / 2
Sleep dt, 1
t2 = Timer
#if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
If t2 < t1 Then t1 -= 24 * 60 * 60 : t3 -= 24 * 60 * 60
#endif
fps = N / (t2 - t1)
tf = 1 / MyFps
t1 = t2
' automatic test and regulation
Dim As Single delta = (t2 - t3) * 1000 - (dt + bias)
If Abs(delta) > 3 * tos Then
tos = 0
Else
bias += 0.1 * Sgn(delta)
End If
' automatic calibation
If dt < tos Then
If count = 100 Then
tos = sum / 100 * 1000
bias = 0
sum = 0
count = 0
Else
sum += (t2 - t3)
count += 1
End If
End If
ImageSkipped = False
N = 1
Else
ImageSkipped = True
N += 1
End If
Return fps
End Function
`
Maximum performance estimation
Simple modeling of the user loop and its environment:
- tt : execution time of the tracing task
- tc : execution time of the calculation task (+ other process)
- tm : time of the minimum delay generated by 'regulateLite()' (depending on the OS cycle period) = 16 ms (for Windows in normal resolution)
- fm = minimum frequency of the true FPS authorized by 'regulateLite()' in case of 'image skipping' = 10 Hz (fixed in the code)
- siM = maximum number of skipped images + 1 = 20 (fixed in the code)
This estimate neglects the execution time of 'regulateLite()' except obviously the delay ('tm') it generates.
Without 'image skipping':
FPSmax = 1 / (tt + tc + tm)
With 'image skipping' alone (skipped images scrolled):
k0 = Int((1 / fm - tm) / (tt + tc))
k = Iif(k0 < siM, k0, siM)
apparent FPSmax = Cint(1 / (k * (tt + tc) + tm) * k)
('k' value corresponds to the number of 'skipped images' + 1 : 'k - 1' skipped images over 'k')
With 'image skipping' + skipped images removed by user:
k0 = Int((1 / fm - tm - tt) / tc)
k = Iif(k0 < siM, k0, siM)
apparent FPSmax = Cint(1 / (k * tc + tt + tm) * k)
('k' value corresponds to the number of 'skipped images' + 1 : 'k - 1' skipped images over 'k')
To highlight the maximum accessible FPS values with 'regulateLite()', depending on the duration of the 2 tasks in the user loop (tracing task and calculation task (+ other process)), a complete table of estimated values is below provided (duration of the 2 tasks ranging from 1 ms to 9 ms each in steps of 1 ms, Windows in normal resolution):

    '' Constant tm = 0.016 : time of the minimum delay generated by 'regulateLite()' (depending on the OS cycle period)
    '' Constant fm = 10    : minimum frequence of the true FPS authorized by 'regulateLite()' in case of 'image skipping'
    '' Constant siM = 20   : maximum number of skipped images + 1

    '' Variable tt : execution time of the tracing task
    '' Variable tc : execution time of the calculation task (+ other process)

    '' For each couple (tt,tc) of values, 3 FPS max are estimated, and for each the quota of 'skipped images':
    ''    - FPS without 'image skipping'                                         , number of 'skipped images' (always '0')
    ''    - Apparent FPS with 'image skipping' ('skipped images' only scrolled)  , quota of 'skipped images' (only scrolled)
    ''    - Apparent FPS with 'image skipping' + 'skipped images' removed by user, quota of 'skipped images' removed by user

    ''           | tt = 1 ms | tt = 2 ms | tt = 3 ms | tt = 4 ms | tt = 5 ms | tt = 6 ms | tt = 7 ms | tt = 8 ms | tt = 9 ms |
    '' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
    ''           |  56 0     |  53 0     |  50 0     |  48 0     |  45 0     |  43 0     |  42 0     |  40 0     |  38 0     |
    '' tc = 1 ms | 357 19/20 | 263 19/20 | 208 19/20 | 167 15/16 | 140 13/14 | 120 11/12 | 104 9/10  |  93 8/9   |  83 7/8   |
    ''           | 541 19/20 | 526 19/20 | 513 19/20 | 500 19/20 | 488 19/20 | 476 19/20 | 465 19/20 | 455 19/20 | 444 19/20 |
    '' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
    ''           |  53 0     |  50 0     |  48 0     |  45 0     |  43 0     |  42 0     |  40 0     |  38 0     |  37 0     |
    '' tc = 2 ms | 263 19/20 | 208 19/20 | 167 15/16 | 140 13/14 | 120 11/12 | 104 9/10  |  93 8/9   |  83 7/8   |  75 6/7   |
    ''           | 351 19/20 | 345 19/20 | 339 19/20 | 333 19/20 | 328 19/20 | 323 19/20 | 317 19/20 | 312 19/20 | 308 19/20 |
    '' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
    ''           |  50 0     |  48 0     |  45 0     |  43 0     |  42 0     |  40 0     |  38 0     |  37 0     |  36 0     |
    '' tc = 3 ms | 208 19/20 | 167 15/16 | 140 13/14 | 120 11/12 | 104 9/10  |  93 8/9   |  83 6/7   |  75 6/7   |  70 6/7   |
    ''           | 260 19/20 | 256 19/20 | 253 19/20 | 250 19/20 | 247 19/20 | 244 19/20 | 241 19/20 | 238 19/20 | 235 19/20 |
    '' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
    ''           |  48 0     |  45 0     |  43 0     |  42 0     |  40 0     |  38 0     |  37 0     |  36 0     |  34 0     |
    '' tc = 4 ms | 167 15/16 | 140 13/14 | 120 11/12 | 104 9/10  |  93 8/9   |  83 7/8   |  75 6/7   |  70 6/7   |  64 5/6   |
    ''           | 206 19/20 | 204 19/20 | 202 19/20 | 200 19/20 | 196 18/19 | 194 18/19 | 192 18/19 | 190 18/19 | 186 17/18 |
    '' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
    ''           |  45 0     |  43 0     |  42 0     |  40 0     |  38 0     |  37 0     |  36 0     |  34 0     |  33 0     |
    '' tc = 5 ms | 140 13/14 | 120 11/12 | 104 9/10  |  93 8/9   |  83 7/8   |  75 6/7   |  70 6/7   |  64 5/6   |  60 5/6   |
    ''           | 165 15/16 | 163 15/16 | 162 15/16 | 160 15/16 | 156 14/15 | 155 14/15 | 153 14/15 | 152 14/15 | 150 14/15 |
    '' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
    ''           |  43 0     |  42 0     |  40 0     |  38 0     |  37 0     |  36 0     |  34 0     |  33 0     |  32 0     |
    '' tc = 6 ms | 120 11/12 | 104 9/10  |  93 8/9   |  83 7/8   |  75 6/7   |  70 6/7   |  64 5/6   |  60 5/6   |  55 4/5   |
    ''           | 137 12/13 | 135 12/13 | 134 12/13 | 133 12/13 | 131 12/13 | 130 12/13 | 126 11/12 | 125 11/12 | 124 11/12 |
    '' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
    ''           |  42 0     |  40 0     |  38 0     |  37 0     |  36 0     |  34 0     |  33 0     |  32 0     |  31 0     |
    '' tc = 7 ms | 104 9/10  |  98 8/9   |  83 7/8   |  75 6/7   |  70 6/7   |  64 5/6   |  60 5/6   |  55 4/5   |  52 4/5   |
    ''           | 117 10/11 | 116 10/11 | 115 10/11 | 113 10/11 | 112 10/11 | 111 10/11 | 110 10/11 | 106 9/10  | 105 9/10  |
    '' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
    ''           |  40 0     |  38 0     |  37 0     |  36 0     |  34 0     |  33 0     |  32 0     |  31 0     |  30 0     |
    '' tc = 8 ms |  93 8/9   |  83 7/8   |  75 6/7   |  70 6/7   |  64 5/6   |  60 5/6   |  55 4/5   |  52 4/5   |  48 3/4   |
    ''           | 103 9/10  | 102 9/10  | 101 9/10  | 100 9/10  |  97 8/9   |  96 8/9   |  95 8/9   |  94 8/9   |  93 8/9   |
    '' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
    ''           |  38 0     |  37 0     |  36 0     |  34 0     |  33 0     |  32 0     |  31 0     |  30 0     |  29 0     |
    '' tc = 9 ms |  83 7/8   |  75 6/7   |  70 6/7   |  64 5/6   |  55 5/6   |  55 4/5   |  52 4/5   |  48 3/4   |  45 3/4   |
    ''           |  92 8/9   |  91 8/9   |  90 8/9   |  87 7/8   |  86 7/8   |  85 7/8   |  84 7/8   |  83 7/8   |  82 7/8   |
    '' -----------------------------------------------------------------------------------------------------------------------

3. Examples of test code
4 code examples for testing the behavior of the 'regulateLite()' function.
Test code for simple use of 'regulateLite()'
The 'regulateLite()' function is called as a sub (ignoring the return value) and only the mandatory parameter ('MyFps') is used.
=> "image skipping" feature working in scrolling mode.
The dynamic part of the display consists of the value of the requested FPS and a graphic progress bar to judge the quality of the regulation.
The static part of the display is the list of available commands:
- <+/-> for increase/decrease FPS
- <escape> to Quit
Test code 1:
`
#include "regulateLite.bi"
Screen 12
Dim As ULong FPS = 100
Do
Static As ULongInt l
ScreenLock
Cls
Color 15
Print Using "Requested FPS : ###"; FPS
Print
Print
Print
Color 14
Print "<+> : Increase FPS"
Print "<-> : Decrease FPS"
Print
Print "<Escape> : Quit"
Line (0, 32)-(639, 48), 7, B
Line (0, 32)-(l, 48), 7, BF
ScreenUnlock
l = (l + 1) Mod 640
Dim As String s = Inkey
Select Case s
Case "+"
If FPS < 200 Then FPS += 1
Case "-"
If FPS > 10 Then FPS -= 1
Case Chr(27)
Exit Do
End Select
regulateLite(FPS)
Loop
`
Test code for improved use of 'regulateLite()'
Compared to 'Code for simple test', this code calls 'regulateLite()' as a function and displays the applied FPS (return value).
Additionally, this code uses the optional 'ImageSkipped' parameter to not display images tagged 'ImageSkipped=True' at all, and also specify the quantum of images displayed.
=> "image skipping" feature working in removing mode.
Test code 2:
`
#include "regulateLite.bi"
Screen 12
Dim As ULong FPS = 100
Do
Static As ULongInt l
Static As ULong MyFPS
Static As Boolean ImageSkipped
Static As Long nis
Static As Long tnis
If ImageSkipped = False Then
ScreenLock
Cls
Color 15
Print Using "Requested FPS : ###"; FPS
Print
Color 11
Print Using "Applied FPS : ###"; MyFPS
Print " Image displayed : 1/" & tnis + 1
Print
Print
Print
Color 14
Print "<+> : Increase FPS"
Print "<-> : Decrease FPS"
Print
Print "<Escape> : Quit"
Line (0, 80)-(639, 96), 7, B
Line (0, 80)-(l, 96), 7, BF
ScreenUnlock
End If
l = (l + 1) Mod 640
Dim As String s = Inkey
Select Case s
Case "+"
If FPS < 200 Then FPS += 1
Case "-"
If FPS > 10 Then FPS -= 1
Case Chr(27)
Exit Do
End Select
MyFPS = regulateLite(FPS, , , ImageSkipped)
If ImageSkipped = True Then
nis += 1
Else
tnis = nis
nis = 0
End If
Loop
`
Test code for intensive use of 'regulateLite()'
Compared to 'Code for improved test', this code additionally uses the other two optional parameters 'SkipImage' and 'Restart'.
This code is used to test all the combinations of the parameters of the function as well as the type of display of the skipped images (either scrolling of the skipped images, or removing of the skipped images).
("image skipping" feature activated and pre-selected in scrolling mode)
For the Windows platform only, this code allows to change the OS cycle resolution (normal or high) and commands a new calibration phase at each change of resolution.
Added commands:
- <T/F> for True/False for image skipping
- <S/R> for Scroll/Remove image skipped
- <C> for Calibration phase
- <N/H> for Normal/High resolution (for Windows platform only)
Test code 3:
`
#include "regulateLite.bi"
#if defined(__FB_WIN32__)
Declare Function _setTimer Lib "winmm" Alias "timeBeginPeriod"(ByVal As ULong = 1) As Long
Declare Function _resetTimer Lib "winmm" Alias "timeEndPeriod"(ByVal As ULong = 1) As Long
#endif
Screen 12, , 2
ScreenSet 1, 0
Dim As ULongInt MyFps = 100
Dim As String res = "N"
Dim As Boolean SkipImage = True
Dim As Boolean Restart = False
Dim As Boolean RemoveImageSkipped = False
Do
Static As ULongInt l
Static As Double dt
Static As ULong fps
Static As Double t
Static As ULong averageFps
Static As Double sumFps
Static As Double averageDelay
Static As Double sumDelay
Static As Long N
Static As Boolean ImageSkipped
Static As Long ist = 0
Static As Long mist = 0
Dim As Double t1
Dim As Double t2
If (RemoveImageSkipped = False) Or (ImageSkipped = False) Then
t = Timer
Cls
Print
Color 15
Select Case res
Case "N"
Print " NORMAL RESOLUTION"
Case "H"
Print " HIGH RESOLUTION (for Windows only)"
End Select
Print
Print " Procedure : regulateLite( "; MyFPS & " [, " & SkipImage & " ])";
If SkipImage = True Then
Select Case RemoveImageSkipped
Case True
Print " Images skipped : Removing"
Case False
Print " Images skipped : Scrolling"
End Select
Else
Print " No image skipping"
End If
Print
Color 11
If mist = 0 Then
Print Using " Applied true FPS : ### (average : ###)"; fps; averageFps
Print Using " Applied delay : ###.### ms (average : ###.### ms)"; dt; averageDelay;
Else
Print Using " Applied apparent FPS : ### (average : ###)"; fps; averageFps
Print Using " Applied delay : ###.### ms (average : ###.### ms)"; dt; averageDelay;
End If
If SkipImage = True Then
Print " (not skipped image)"
Else
Print
End If
If SkipImage = True Then
Select Case RemoveImageSkipped
Case True
Print " Images removed : " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0")
Case False
Print " Images scrolled : " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0")
End Select
Else
Print " No image skipped"
End If
Print
Print
Print
Color 14
#if defined(__FB_WIN32__)
Print " <n> or <N> : Normal resolution"
Print " <h> or <H> : High resolutiion"
Print
#endif
Print " <+> : Increase FPS"
Print " <-> : Decrease FPS"
Print
Print " Optional parameter :"
Print " <t> or <T> : True for image skipping"
If SkipImage = True Then
Print " <r> or <R> : Remove image skipped"
Print " <s> or <S> : Scroll image skipped"
End If
Print " <f> or <F> : False for image skipping"
Print " <c> or <C> : Calibration phase"
Print
Print " <escape> : Quit"
Line (8, 144)-(631, 160), 7, B
Line (8, 144)-(8 + l, 160), 7, BF
Do
#if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
t2 = Timer
If t2 < t Then t -= 24 * 60 * 60
Loop Until t2 >= t + 0.002
#else
Loop Until Timer >= t + 0.002
#endif
ScreenCopy
End If
l = (l + 1) Mod 624
Dim As String s = UCase(Inkey)
Select Case s
Case "+"
If MyFPS < 500 Then MyFPS += 1
Case "-"
If MyFPS > 10 Then MyFPS -= 1
Case "T"
SkipImage = True
Case "F"
SkipImage = False
#if defined(__FB_WIN32__)
Case "N"
If res = "H" Then
Restart = True
res = "N"
End If
Case "H"
If res = "N" Then
Restart = True
res = "H"
End If
#endif
Case "C"
Restart = True
Case "R"
If SkipImage = True Then RemoveImageSkipped = True
Case "S"
If SkipImage = True Then RemoveImageSkipped = False
Case Chr(27)
Exit Do
End Select
sumFps += fps
sumDelay += dt
N += 1
If N >= fps / 2 Then
averageFps = sumFps / N
averageDelay = sumDelay / N
N = 0
sumFps = 0
sumDelay = 0
End If
#if defined(__FB_WIN32__)
If res = "H" Then
_setTimer()
End If
#endif
t1 = Timer
fps = regulateLite(MyFPS, SkipImage, Restart, ImageSkipped)
If ImageSkipped = False Then
t2 = Timer
#if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
If t2 < t1 Then t1 -= 24 * 60 * 60
#endif
dt = (t2 - t1) * 1000
End If
#if defined(__FB_WIN32__)
If res = "H" Then
_resetTimer()
End If
#endif
Restart = False
If ImageSkipped = True Then
ist += 1
Else
mist = ist
ist = 0
End If
Loop
`
Test code for maximum performance emulation of 'regulateLite()'
Consider following notations:
- tt : execution time of the user tracing task
- tc : execution time of the user calculation task (+ other process)
An emulation code is written with two TIMER waiting loops (for 'tt' and 'tc'), and calling the real 'regulateLite()', which allows to estimate the magnitude order of the maximum accessible performance.
This emulation allows to validate the order of magnitude of the results from the 'Maximum performance estimation' paragraph above.
Use the runtime commands to adjust the 'tt' and 'tc' values and test the 3 configurations using the optional parameters.
This test code works with the resolution from the OS cycle.
Test code 4:
`
Dim As Integer tt = 10 ' (in milliseconds)
Dim As Integer tc = 5 ' (in milliseconds)
#include "regulateLite.bi"
Screen 12, , 2
ScreenSet 1, 0
Dim As ULongInt MyFps = 9999
Dim As String res = "N"
Dim As Boolean SkipImage = False
Dim As Boolean RemoveImageSkipped = False
Do
Static As ULong fps
Static As ULong averageFps
Static As Double sumFps
Static As Long N
Static As Boolean ImageSkipped
Static As Long ist = 0
Static As Long mist = 0
Static As ULongInt l
Dim As Double t1
Dim As Double t2
If (RemoveImageSkipped = False) Or (ImageSkipped = False) Then
t1 = Timer
Cls
Print
Color 15
Print " MAXIMUM PERFORMANCE EMULATION of 'regulateLite()' regulation"
Print
Print
Print Using " tt = ## ms (execution time of the tracing task)"; tt
Print Using " tc = ## ms (execution time of the calculation task + other process)"; tc
Print
Print " Procedure : regulateLite( "; MyFPS & " [, " & SkipImage & " ])";
If SkipImage = True Then
Select Case RemoveImageSkipped
Case True
Print " Images skipped : Removing"
Case False
Print " Images skipped : Scrolling"
End Select
Else
Print " No image skipping"
End If
Print
Print
Color 11
If mist = 0 Then
Print Using " Applied true FPS max :#### (average :####)"; fps; averageFps
Else
Print Using " Applied apparent FPS max :#### (average :####)"; fps; averageFps
End If
If SkipImage = True Then
Select Case RemoveImageSkipped
Case True
Print " Images removed : " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0")
Case False
Print " Images scrolled : " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0")
End Select
Else
Print " No image skipped"
End If
Print
Print
Color 14
Print " <+> : Increment tt"
Print " <-> : Decrement tt"
Print
Print " <i> or <I> : Increment tc"
Print " <d> or <D> : Decrement tc"
Print
Print " Optional parameter :"
Print " <t> or <T> : True for image skipping"
If SkipImage = True Then
Print " <r> or <R> : Remove image skipped"
Print " <s> or <S> : Scroll image skipped"
End If
Print " <f> or <F> : False for image skipping"
Print
Print " <escape> : Quit"
Line (0, 197)-(639, 199), 3, B
Line (0, 198)-(l, 198), 11
Do
#if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
t2 = Timer
If t2 < t Then t -= 24 * 60 * 60
Loop Until t2 >= t1 + tt / 1000
#else
Loop Until Timer >= t1 + tt / 1000
#endif
ScreenCopy
End If
t1 = Timer
Dim As String s = UCase(Inkey)
Select Case s
Case "+"
If tt < 30 Then tt += 1
Case "-"
If tt > 1 Then tt -= 1
Case "I"
If tc < 30 Then tc += 1
Case "D"
If tc > 1 Then tc -= 1
Case "T"
SkipImage = True
Case "F"
SkipImage = False
Case "R"
If SkipImage = True Then RemoveImageSkipped = True
Case "S"
If SkipImage = True Then RemoveImageSkipped = False
Case Chr(27)
Exit Do
End Select
sumFps += fps
N += 1
If N >= fps / 2 Then
averageFps = sumFps / N
N = 0
sumFps = 0
End If
If ImageSkipped = True Then
ist += 1
Else
mist = ist
ist = 0
End If
l = (l + 1) Mod 640
Do
#if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
t2 = Timer
If t2 < t Then t -= 24 * 60 * 60
Loop Until t2 >= t1 + tc / 1000
#else
Loop Until Timer >= t1 + tc / 1000
#endif
fps = regulateLite(MyFPS, SkipImage, , ImageSkipped)
Loop
`
Note: As the applied FPS is at maximum limit, 'regulateLite()' periodically refreshes the value of tm (calibration at runtime), so the FPS values found can shift at each new calibration (slow rate).
4. Examples of graphic animation code
These two graphic animations highlight the interest of the image skipping feature of 'regulateLite()', with scrolling and above all removing of skipped images.
Graphic animation 1
This graphic animation (heavy CPU load) comes from dodicat (https://www.freebasic.net/forum/viewtopic.php?p=184009#p184009).
Some initializations and code lines may have been modified from the author's original in order to better highlight the regulation with 'regulateLite()' and its different configurations of use.
This code allows to modify:
- the value of the requested FPS (and it visualizes the applied FPS),
- the image skipping activation (false or true),
- and in case of image skipping activated, the mode for skipping images (scrolling or removing).
(the removing skipped images adds only three lines to the user code)
The full status of the image skipping feature is also visualized.
Graphic animation code 1:
`
'' Graphic animation from dodicat (https://www.freebasic.net/forum/viewtopic.php?p=184009#p184009)
#include "regulateLite.bi"
Type vector3d
As Single x,y,z
End Type
'assignment macro
#define vct Type<vector3d>
'macros
#define map(a,b,x,c,d) ((d)-(c))*((x)-(a))/((b)-(a))+(c)
#macro combsort(array,begin,finish,dot)
Scope
Var size=(finish),switch=0,j=0
Dim As Single void=size
Do
void=void/1.3: If void<1 Then void=1
switch=0
For i As Integer =(begin) To size-void
j=i+void
If array(i)dot<array(j)dot Then
Swap array(i),array(j): switch=1
End If
Next
Loop Until switch =0 And void=1
End Scope
#endmacro
Operator -(ByRef v1 As vector3d,ByRef v2 As vector3d) As vector3d
Return Type<vector3d>(v1.x-v2.x,v1.y-v2.y,v1.z-v2.z)
End Operator
Operator + (ByRef v1 As vector3d,ByRef v2 As vector3d) As vector3d
Return Type<vector3d>(v1.x+v2.x,v1.y+v2.y,v1.z+v2.z)
End Operator
Function length(ByRef v1 As vector3d) As Single
Return Sqr(v1.x*v1.x+v1.y*v1.y+v1.z*v1.z)
End Function
Function rotate3d(ByVal pivot As vector3d,ByVal pt As vector3d,ByVal Angle As vector3d, ByVal scale As vector3d=Type<vector3d>(1,1,1)) As vector3d
#define cr 0.0174532925199433
Angle=Type<vector3d>(Angle.x*cr,Angle.y*cr,Angle.z*cr)
#macro Rotate(a1,a2,b1,b2,d)
temp=Type<vector3d>((a1)*Cos(Angle.d)+(a2)*Sin(Angle.d),(b1)*Cos(Angle.d)+(b2)*Sin(Angle.d))
#endmacro
Dim As vector3d p=Type<vector3d>(pt.x-pivot.x,pt.y-pivot.y,pt.z-pivot.z)
Dim As vector3d rot,temp
Rotate(p.y,-p.z,p.z,p.y,x)'X
rot.y=temp.x:rot.z=temp.y
p.y = rot.y:p.z = rot.z
Rotate(p.z,-p.x,p.x,p.z,y)'Y
rot.z=temp.x:rot.x=temp.y
p.x=rot.x
Rotate(p.x,-p.y,p.y,p.x,z)'Z
rot.x=temp.x:rot.y=temp.y
Return Type<vector3d>((scale.x*rot.x+pivot.x),(scale.y*rot.y+pivot.y),(scale.z*rot.z+pivot.z))
End Function
Function apply_perspective(ByRef p As vector3d,ByRef eyepoint As vector3d) As vector3d
Dim As Single w=1+(p.z/eyepoint.z)
If w=0 Then w=1e-20
Return Type<vector3d>((p.x-eyepoint.x)/w+eyepoint.x,(p.y-eyepoint.y)/w+eyepoint.y,(p.z-eyepoint.z)/w+eyepoint.z)
End Function
'====================== End of rotator and perspective getter ======================================
Dim Shared As Integer xres,yres
ScreenRes 800, 640, 8
Width 800 \ 8, 640 \ 16
ScreenInfo xres,yres
'two main arrays
Dim Shared As vector3d rotated()
ReDim Shared As vector3d array()
'extra subs to regulate speed
'four shapes
Function create1(ByVal number As Integer) As Integer
ReDim array(0)
Dim As Integer count,stepper=10
For x As Integer=xres/2-number To xres/2+number Step stepper
For y As Integer=yres/2-number To yres/2+number Step stepper
For z As Integer=-number To number Step stepper
count=count+1
ReDim Preserve array(1 To count)
array(UBound(array))=vct(x,y,z)
Next z
Next y
Next x
ReDim rotated(LBound(array) To UBound(array))
Return 0
End Function
'variables
Dim As vector3d centre=vct(xres/2,yres/2,0)
Dim As vector3d eyepoint=vct(xres/2,yres/2,600)
Dim As vector3d angle
Dim As Long fps=150,rfps
Dim As Boolean skipping=False,remove=False,skipped
Dim As Long ist,mist
Dim As ULong averageFps
Dim As Double sumFps
Dim As Long N
Dim As vector3d disp=vct(xres/2,yres/2,0)
Dim As Integer k,flag=1,border=.2*xres
Dim As Single sx=1,kx=2,ky=1.9,kz=1.5
create1(90)
Do
angle=angle+vct(.2,2,.1)
With angle
If .x>=360 Then .x-=360
If .y>=360 Then .y-=360
If .x>=360 Then .z-=360
End With
disp=disp+vct(kx,ky,0)
If disp.x<border Then kx=-kx
If disp.x>xres-border Then kx=-kx
If disp.y<border Then ky=-ky
If disp.y>yres-border Then ky=-ky
If (remove = False) Or (skipped = False) Then
ScreenLock
Cls
For n As Integer=1 To UBound(rotated)
rotated(n)=rotate3d(centre,(array(n)),angle,vct(sx,sx,sx))
Next n
combsort(rotated,1,UBound(rotated),.z)
For n As Integer=1 To UBound(rotated)
Var dist=length(rotated(n)-centre)
rotated(n)=apply_perspective(rotated(n),eyepoint)
rotated(n)=rotated(n)+(disp-centre)
Var col=map(0,200,dist,1,15)
Var radius=map(-400,400,rotated(n).z,10,1)
Circle (rotated(n).x,rotated(n).y),radius,col,,,,f
Next n
Draw String (16,16),"Requested FPS = " & Right(" " & fps, 3)
Draw String (16,32),"Applied FPS = " & Right(" " & rfps, 3) & " (average = " & Right(" " & averageFps, 3) & ")"
Draw String (16,48),"Status : " & _
IIf(skipping = True, "Image skipping activation = true, with " & _
IIf(remove = True, "removing images skipped = " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0"), _
"scrolling images skipped = " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0")), _
"Image skipping activation = false")
Draw String (16,80),"<+> : Increase FPS"
Draw String (16,96),"<-> : Decrease FPS"
Draw String (16,128),"<t> or <T> : True for image skipping activation"
If Skipping = True Then
Draw String (16,144)," <s> or <S> : Scroll image skipped"
Draw String (16,160)," <r> or <R> : Remove image skipped"
Draw String (16,176),"<f> or <F> : False for image skipping activation"
Draw String (16,208),"<escape> : Quit"
Else
Draw String (16,144),"<f> or <F> : False for image skipping activation"
Draw String (16,176),"<escape> : Quit"
End If
Draw String (544,608),"Graphic animation from dodicat"
ScreenUnlock
End If
rfps = regulateLite(fps,skipping, ,skipped)
If skipped = True Then
ist += 1
Else
mist = ist
ist = 0
End If
sumFps += rfps
N += 1
If N >= rfps / 2 Then
averageFps = sumFps / N
N = 0
sumFps = 0
End If
Dim As String s = UCase(Inkey)
Select Case s
Case "+"
If fps < 700 Then fps += 1
Case "-"
If fps > 10 Then fps -= 1
Case "T"
skipping = True
Case "F"
skipping = False
Case "S"
If skipping = True Then remove = False
Case "R"
If skipping = True Then remove = True
Case Chr(27)
Exit Do
End Select
Loop
`
Graphic animation 2
This graphic animation (heavy CPU load) comes from dodicat (https://www.freebasic.net/forum/viewtopic.php?p=195122#p195122).
Some initializations and code lines may have been modified from the author's original in order to better highlight the regulation with 'regulateLite()' and its different configurations of use.
This code allows to modify:
- the value of the requested FPS (and it visualizes the applied FPS),
- the image skipping activation (false or true),
- and in case of image skipping activated, the mode for skipping images (scrolling or removing).
(the removing skipped images adds only three lines to the user code)
The full status of the image skipping feature is also visualized.
Graphic animation code 2:
`
'' Graphic animation from dodicat (https://www.freebasic.net/forum/viewtopic.php?p=195122#p195122)
#include "regulateLite.bi"
Sub Thing(ByVal w As Integer=700, _
ByVal h As Integer=600, _
ByVal posx As Integer=400, _
ByVal posy As Integer=300, _
ByVal morph As Single=24, _
ByVal aspect As Single=3, _
ByVal grade As Single=6, _
ByVal col As UInteger=RGBA(255,255,255,0))
Dim As Single XStep = 1
Dim As Single YStep = 1
Dim As Single b=w*w
Dim As Single y,m,n
For x As Single = 0 To w Step XStep
Dim As Single s=x*x
Dim As Single p=Sqr(b-s)
For i As Single = -P To P Step grade*YStep
Dim As Single r = Sqr(s+i*i)/w
Dim As Single Q = (R - 1) * Sin(morph*r)
y=i/aspect+q*h
If i = -p Then m=y:n=y
If y > m Then m = y
If y < n Then n = y
If m=y OrElse n=y Then
PSet(x/2+posx,-Y/2+posy),col/i
PSet(-x/2+posx,-Y/2+posy),col/i
End If
Next
Next
End Sub
'===========================================================
ScreenRes 800, 640, 32, 2
Width 800 \ 8, 640 \ 16
Color,RGB(0,0,50)
ScreenSet 1,0
Dim As Single Morph=0,k=1
Dim As Single aspect,counter,pi2=8*Atn(1)
Dim As Long fps=80,rfps
Dim As Boolean skipping=False,remove=False,skipped
Dim As Long ist,mist
Dim As ULong averageFps
Dim As Double sumFps
Dim As Long N
Do
counter+=.1
If counter>=pi2 Then counter=0
aspect=3+Sin(counter)
Morph+=.1*k
If Morph>35 Then k=-k
If Morph<-35 Then k=-k
If (remove = False) Or (skipped = False) Then
Cls
Thing(800,500,400,300,Morph,aspect)
Draw String (16,16),"Requested FPS = " & Right(" " & fps, 3)
Draw String (16,32),"Applied FPS = " & Right(" " & rfps, 3) & " (average = " & Right(" " & averageFps, 3) & ")"
Draw String (16,48),"Status : " & _
IIf(skipping = True, "Image skipping activation = true, with " & _
IIf(remove = True, "removing images skipped = " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0"), _
"scrolling images skipped = " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0")), _
"Image skipping activation = false")
Draw String (16,80),"<+> : Increase FPS"
Draw String (16,96),"<-> : Decrease FPS"
Draw String (16,128),"<t> or <T> : True for image skipping activation"
If Skipping = True Then
Draw String (16,144)," <s> or <S> : Scroll image skipped"
Draw String (16,160)," <r> or <R> : Remove image skipped"
Draw String (16,176),"<f> or <F> : False for image skipping activation"
Draw String (16,208),"<escape> : Quit"
Else
Draw String (16,144),"<f> or <F> : False for image skipping activation"
Draw String (16,176),"<escape> : Quit"
End If
Draw String (544,608),"Graphic animation from dodicat"
Flip
End If
rfps = regulateLite(fps,skipping, ,skipped)
If skipped = True Then
ist += 1
Else
mist = ist
ist = 0
End If
sumFps += rfps
N += 1
If N >= rfps / 2 Then
averageFps = sumFps / N
N = 0
sumFps = 0
End If
Dim As String s = UCase(Inkey)
Select Case s
Case "+"
If fps < 600 Then fps += 1
Case "-"
If fps > 10 Then fps -= 1
Case "T"
skipping = True
Case "F"
skipping = False
Case "S"
If skipping = True Then remove = False
Case "R"
If skipping = True Then remove = True
Case Chr(27)
Exit Do
End Select
Loop
`
One can notice that for these 2 graphic animations, the mode of operation with 'image skipping' activated plus 'skipped image' removed by the user makes it possible to reach higher FPS, because in these two cases the user code has well separated the drawing part of the new image (the only part that should not be executed when the 'skipped image' tag is true) from the calculation part of the parameters for the new image (which must always be executed).
See also

- `Sleep`
- `Timer`
- Graphics Mode Refresh and Anti-Flickering
- Fine-grain procedure for waiting and in-loop procedure for fine-regulating FPS