Help Center
Help CenterFreeBASICTutorials

Lite regulation function to be integrated into user loop for FPS control

Documentation

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