集成到用户循环中用于 FPS 控制的轻量调节函数
- 来源: https://www.freebasic.net/wiki/wikka.php?wakka=ProPgLiteRegulate
- 最后更新: 2024-02-20
通过使用循环内轻量调节过程来控制 FPS(每秒帧数)。
前言:
该调节函数的主要目标是将其对 CPU 负载的增加降到最低,以配合用户自身代码的 CPU 消耗。
因此,该函数用于控制用户 FPS(每秒帧数)的所有死区时间,完全使用 SLEEP 关键字生成(不使用消耗 CPU 的等待循环)。
但仅使用 SLEEP 关键字会对最大可访问 FPS 值产生限制,因为 SLEEP 生成的延迟不能低于取决于操作系统周期的限制值。
该调节函数集成了一种变通方法,当请求的 FPS 值超过此限制时,不再提供真实的 FPS,而是提供一个与请求 FPS 相匹配的"表观 FPS"。
此外,由于 SLEEP 关键字生成的延迟精度不高,实际获得的 FPS 会在请求值附近波动。
SLEEP 还会产生随时间变化的时间偏差,但其平均值会被调节函数修正。
1. 克服 SLEEP 关键字不利行为的原理
为了实现仅基于 SLEEP 关键字的最佳调节,必须克服或最小化上述不良行为。
测量 SLEEP 可访问最小延迟的原理
为了评估 SLEEP 可访问的最小延迟 'tos',使用 TIMER 关键字测量连续十次 'Sleep 1, 1' 的持续时间,然后将测得的时间除以十。
但这个最小值 'tos' 不能直接使用,因为必须在其基础上加上一个余量,以保持近乎线性的调节操作。
因此,可用的最小值固定为 '3 * tos / 2'。
此校准阶段在首次调用调节函数时自动执行,或在测量时间与下达时间差异过大时执行,或根据用户通过函数的可选参数请求执行,或在达到调节上限时趁机进行测量(此时使用 'Sleep tos / 2, 1' 的延迟跟踪值)。
访问高于 SLEEP 可访问最大真实 FPS 的表观 FPS 的原理
当对于所需的 FPS 值,需要在 'Sleep t, 1' 中应用的 't' 值小于 '3 * tos / 2' 时,启动"跳帧"功能,以提供与所需 FPS 相匹配的表观 FPS。
"跳帧"的基本原理是移除这些图像之间的所有延迟(调节函数立即返回),以便这些图像的持续时间相对于后跟真实延迟的图像来说较低。
图像绘制时间越短,这些跳过的图像越不明显。
跳过 'n' 张图像中的 'n - 1' 张的时间原理是:仅在查看最后一张图像后生成一个延迟,其值使得整个序列('n' 张图像)的总周期等于 'n' 倍对应所需 FPS 的周期。
当需要为序列生成的唯一延迟大于或等于 '3 * tos / 2' 时,跳过的图像数量停止增加。
当 'n' 张图像中跳过 'n - 1' 张时,表观 FPS 等于 'n' 张图像完整显示周期的倒数乘以 'n'。
用户可以通过函数的可选参数禁用跳帧功能。
相反,用户可以通过测试函数的可选参数来完全移除跳过的图像(该参数标记这些跳过的图像),从而增强此功能。
要使此功能正常工作,用户代码必须很好地将仅涉及图形绘制的代码部分(当"跳帧"标记为 true 时唯一不应执行的部分)与仅涉及运动进度的代码部分(必须始终执行)分离开来。否则,如果这两部分代码同时被跳过(甚至只是运动进度的一小部分),图形绘制反而会被减慢(尽管返回的 FPS 值另有显示)。
这种操作配置("跳帧"+"用户移除跳过图像")是最高效的(从表观 FPS 角度来看),但对用户动画代码循环中的两个任务(仅绘制和仅计算)施加了完全分离的规则。
另一方面,单纯的"跳帧"配置(默认情况下跳过的图像仅滚动显示)不需要用户遵循任何编码规则。
如果请求的 FPS 过高,为避免应用跳帧时图像过于抖动,在使用跳帧时屏幕的实际刷新率不能低于 10 Hz(表观 FPS 可以更高),且连续跳过的图像数量 + 1 不能超过 20。
当"跳帧"激活时,这两个限制使得初始图像每次移动 1 像素时,位移看起来几乎流畅(以 10 Hz 频率移动 20 像素,对用户来说看起来是相当平滑的运动,不会过于抖动)。
补偿 SLEEP 产生的平均时间偏差的原理
原理是使用 TIMER 关键字测量 'Sleep t, 1' 相对于 't' 的实际延迟。
如果测量时间与下达时间差异过大,这可能意味着与初始化时测量的分辨率相比存在误差,函数将在下次调用时自动重新启动新的校准阶段(十次 'Sleep 1, 1')。
示例:请求时间 = 4 µs,但测量时间 = 16 µs,可能是因为程序运行时分辨率被更改(降低)了。
如果测量时间与下达时间差异较小,则对差异进行平均,以尝试修正 SLEEP 关键字的平均偏差。
2. 要包含的函数体说明
这里描述了 'regulateLite()' 函数。
该代码不是线程安全的,因为它使用了 'Static' 变量。
该函数非常适合通过在循环中插入延迟来调节图像刷新的 FPS,而不会向用户自身代码增加明显的 CPU 负载。
注意:与任何等待功能一样,强烈建议不要在屏幕锁定时使用 'regulateLite()',因此不要在 [ScreenLock...ScreenUnlock] 块内调用。
该函数有一个必需的第一个参数 'MyFps',用户通过该参数传递所需的 FPS。
出于调试目的,该函数返回其应用的 FPS(真实或表观)值。如果用户不希望使用此调试数据,则可以简单地将函数作为子程序调用。
如果返回的 FPS 值远低于所需值,则意味着很难达到 FPS 设定值。否则,返回的 FPS 波动主要是由于仅使用 SLEEP 关键字在循环中生成延迟造成的。
此外,该函数提供 3 个可选参数:
- 'Restart'(默认为 'False'):
此输入参数允许用户强制进行新的校准阶段,例如当用户知道操作系统周期分辨率刚刚更改时。
在调用中将此参数设置为 'True',然后在下次调用时设置为 'False'。
- 'SkipImage'(默认为 'True'):
此输入参数允许用户禁用自动"跳帧"功能。无论如何,都不会有跳过的图像,但这可能会以牺牲获得的 FPS 为代价,导致其无法跟随请求的 FPS。
将此参数设置为 'False' 以禁用"跳帧"功能(以后可以重置为 'True')。
- 'ImageSkipped'(默认为 'False',但默认值无关紧要):
此输出参数在函数跳过的每个图像处被设置为 'True'(如果"跳帧"功能已激活)。
用户可以测试它来判断图像是否被跳过("跳帧"功能在滚动模式下工作)。
用户还可以使用此参数完全不绘制跳过的图像,例如允许更高的 FPS("跳帧"功能在移除模式下工作)。
要包含的文件:"regulateLite.bi"
start GeSHi
' 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 Functionend GeSHi
最大性能估算
用户循环及其环境的简单建模:
tt:绘制任务的执行时间
tc:计算任务的执行时间(+ 其他进程)
tm:'regulateLite()' 生成的最小延迟时间(取决于操作系统周期)= 16 ms(Windows 正常分辨率)
fm:'regulateLite()' 在"跳帧"情况下允许的真实 FPS 最小频率 = 10 Hz(代码中固定)
siM:跳过图像的最大数量 + 1 = 20(代码中固定)
此估算忽略了 'regulateLite()' 的执行时间,但显然不包括其生成的延迟('tm')。
不使用"跳帧":
FPSmax = 1 / (tt + tc + tm)
仅使用"跳帧"(跳过的图像滚动显示):
k0 = Int((1 / fm - tm) / (tt + tc))
k = Iif(k0 < siM, k0, siM)
表观 FPSmax = Cint(1 / (k _ (tt + tc) + tm) _ k)
('k' 值对应"跳过图像"数量 + 1:'n' 张中跳过 'n - 1' 张)
使用"跳帧"+ 用户移除跳过图像:
k0 = Int((1 / fm - tm - tt) / tc)
k = Iif(k0 < siM, k0, siM)
表观 FPSmax = Cint(1 / (k _ tc + tt + tm) _ k)
('k' 值对应"跳过图像"数量 + 1:'n' 张中跳过 'n - 1' 张)
为了突出显示使用 'regulateLite()' 可访问的最大 FPS 值(取决于用户循环中两个任务的持续时间:绘制任务和计算任务(+ 其他进程)),下面提供了一个完整的估算值表(两个任务的持续时间从 1 ms 到 9 ms,步长为 1 ms,Windows 正常分辨率):
'' 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 个用于测试 'regulateLite()' 函数行为的代码示例。
'regulateLite()' 简单使用测试代码
将 'regulateLite()' 函数作为子程序调用(忽略返回值),只使用必需参数('MyFps')。
=> "跳帧"功能在滚动模式下工作。
显示的动态部分包括请求的 FPS 值和一个图形进度条,用于判断调节质量。
显示的静态部分是可用命令列表:
<+/->增加/减少 FPS<escape>退出
测试代码 1:
start GeSHi
#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)
Loopend GeSHi
'regulateLite()' 改进使用测试代码
与"简单测试代码"相比,此代码将 'regulateLite()' 作为函数调用并显示应用的 FPS(返回值)。
此外,此代码使用可选的 'ImageSkipped' 参数,完全不显示标记为 'ImageSkipped=True' 的图像,并指定显示图像的数量比例。
=> "跳帧"功能在移除模式下工作。
测试代码 2:
start GeSHi
#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
Loopend GeSHi
'regulateLite()' 密集使用测试代码
与"改进测试代码"相比,此代码还使用了另外两个可选参数 'SkipImage' 和 'Restart'。
此代码用于测试函数参数的所有组合以及跳过图像的显示类型(跳过图像的滚动或移除)。
("跳帧"功能已激活,预选为滚动模式)
对于 Windows 平台,此代码允许更改操作系统周期分辨率(正常或高),并在每次分辨率更改时命令新的校准阶段。
新增命令:
<T/F>对应跳帧的 True/False<S/R>对应滚动/移除跳过图像<C>对应校准阶段<N/H>对应正常/高分辨率(仅适用于 Windows 平台)
测试代码 3:
start GeSHi
#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
Loopend GeSHi
'regulateLite()' 最大性能模拟测试代码
考虑以下符号:
tt:用户绘制任务的执行时间
tc:用户计算任务的执行时间(+ 其他进程)
编写了一个带有两个 TIMER 等待循环(用于 'tt' 和 'tc')的模拟代码,并调用真实的 'regulateLite()',从而可以估算最大可访问性能的数量级。
此模拟用于验证上面"最大性能估算"段落中结果的数量级。
使用运行时命令调整 'tt' 和 'tc' 值,并使用可选参数测试 3 种配置。
此测试代码使用操作系统周期的分辨率。
测试代码 4:
start GeSHi
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)
Loopend GeSHi
注意:由于应用的 FPS 处于最大限制,'regulateLite()' 会定期刷新 tm 值(运行时校准),因此每次新校准后找到的 FPS 值可能会发生偏移(速率较慢)。
4. 图形动画代码示例
这两个图形动画突出了 'regulateLite()' 跳帧功能的优势,包括滚动和特别是移除跳过图像的效果。
图形动画 1
此图形动画(CPU 负载较重)来自 dodicat(https://www.freebasic.net/forum/viewtopic.php?p=184009#p184009)。
为了更好地突出使用 'regulateLite()' 的调节效果及其不同配置,对作者原始代码中的某些初始化和代码行进行了修改。
此代码允许修改:
请求的 FPS 值(并可视化应用的 FPS),
跳帧激活(false 或 true),
以及在跳帧激活的情况下,跳过图像的模式(滚动或移除)。
(移除跳过图像仅向用户代码添加三行)
跳帧功能的完整状态也会可视化显示。
图形动画代码 1:
start GeSHi
'' 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
Loopend GeSHi
图形动画 2
此图形动画(CPU 负载较重)来自 dodicat(https://www.freebasic.net/forum/viewtopic.php?p=195122#p195122)。
为了更好地突出使用 'regulateLite()' 的调节效果及其不同配置,对作者原始代码中的某些初始化和代码行进行了修改。
此代码允许修改:
请求的 FPS 值(并可视化应用的 FPS),
跳帧激活(false 或 true),
以及在跳帧激活的情况下,跳过图像的模式(滚动或移除)。
(移除跳过图像仅向用户代码添加三行)
跳帧功能的完整状态也会可视化显示。
图形动画代码 2:
start GeSHi
'' 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
Loopend GeSHi
可以注意到,对于这两个图形动画,激活"跳帧"模式并由用户移除跳过图像的操作方式能够实现更高的 FPS,因为在这两种情况下,用户代码已将绘制新图像的部分(当"跳帧"标记为 true 时唯一不应执行的部分)与计算新图像参数的部分(必须始终执行)完全分离。
另请参阅
SleepTimer- 图形模式刷新与防闪烁
- 精细等待过程和循环内精细 FPS 调节过程
返回 目录