FreeBASIC マニュアルのトップに戻る

FreeBASIC ProPgLiteRegulate

目次→教本→プログラマーのための案内Fine-grain procedure for waiting and in-loop procedure for fine-regulating FPS←オリジナル・サイト

FPS制御用ユーザーループに統合される軽量調整機能 左にメニュー・フレームが表示されていない場合は、ここをクリックして下さい

←リンク元に戻る プログラム開発関連に戻る

FPS (フレーム/秒)をループ内の軽量調整手続きで制御する。

前文:
この調整機能の主な目的は、ユーザー独自のコードに追加されるCPU負荷を最小限に抑えることです。
したがって、この関数がユーザーのFPS(フレーム/秒)を制御するために使うすべてのデッドタイムは、SLEEPキーワードのみを使って生成されます(CPUを消費する待機ループはありません)。

ただし、SLEEPキーワードを排他的に使うと、OSサイクル期間によってはSLEEPによって生成される遅延が制限値を下回れないため、アクセス可能な最大FPS値に制限が生じます。
この調整機能には回避策が統合されており、このFPS制限値よりも大きい値から、真のFPSではなく、要求されたFPSと一致するように見える見かけ上のFPSを提供します。

また、SLEEPキーワードでは発生する遅延の精度が良くないため、得られるFPSは要求値付近で変動します。
また、SLEEPは時間的に変化する時間的バイアスを生じますが、その平均は調整関数によって補正されます。

1. SLEEPキーワードの不利な行動を克服するための原則
SLEEPキーワードのみに基づいて最適な調整を実装するには、上記のようないくつかの悪い動作を克服または最小限に抑える必要があります。

SLEEPでアクセス可能な最小遅延を測定するための原理
SLEEPでアクセス可能な最小遅延 'tos' を評価するには、キーワードTIMERを使用して10個の「Sleep 1、1」のシーケンスの持続時間を測定し、見つかった時間を10で割ります。
しかし、この最小値 'tos' は、ほぼ直線的な調整動作を維持するためにマージンを追加する必要があるため、直接使用することはできません。 したがって、使用可能な最小値は「3 * tos / 2」に固定されます。

この校正フェーズは、調整機能の最初の呼び出し時、または測定された時間が注文された時間と大きく異なる場合、または機能のオプションのパラメータによるユーザーからの要求に応じて、または調整の上限( 'Sleep tos / 2, 1' による遅延のヒール値が使用された場合)に達したときの機会測定時に自動的に実行されます。

SLEEPでアクセス可能な真の最大のFPSよりも高い見かけのFPSにアクセスするための原理
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).

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. 含める関数本体の説明
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. テストコードの例
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. 描画アニメーションコードの例
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

この2つのグラフィック・アニメーションでは、「image skipping」が有効になっている操作モードと、ユーザーによって「skipped image」が削除されている操作モードが、より高いFPSに到達することを可能にしていることがわかります。なぜなら、この2つのケースでは、ユーザー・コードが、新しい画像の描画部分(「skipped image」タグがtrueのときに実行されるべきではない唯一の部分)と、新しい画像のパラメータの計算部分(常に実行されなければならない部分)をうまく分離しているからです。

参照
プログラマーのための案内に戻る
←リンク元に戻る プログラム開発関連に戻る
ページ歴史:2024-02-20 13:13:04
日本語翻訳:WATANABE Makoto、原文著作者:fxm

ホームページのトップに戻る

表示-非営利-継承