- 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.
For a fine and accurate regulation but consuming much more CPU load, see:
Fine-grain procedure for waiting and in-loop procedure for fine-regulating FPS topic of the "Programmer's Guide".
- 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).
- 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 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).
- 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.
- 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.
- 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.
- 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).
- 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).
- 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.
Code: Select all
' 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- See below in the Maximum performance estimation and emulation of it with 'regulateLite()' post
- - 'Restart' ('False' by default):
- 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
Test1:Code: Select all
#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
- 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.
Test2:Code: Select all
#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
- 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)
Test3:Code: Select all
#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
- See below in the Maximum performance estimation and emulation of it with 'regulateLite()' post
- The 'regulateLite()' function is called as a sub (ignoring the return value) and only the mandatory parameter ('MyFps') is used.
- See for example below the tenth graphic animation and the twelfth graphic animation, both highlighting the interest of the image skipping feature of 'regulateLite()', with scrolling and above all removing of skipped images.