Contrôle fréquentielle d'une boucle

Partagez votre expérience de PureBasic avec les autres utilisateurs.
Guimauve
Messages : 1015
Inscription : mer. 11/févr./2004 0:32
Localisation : Québec, Canada

Contrôle fréquentielle d'une boucle

Message par Guimauve »

Bonjour à tous,

Suite à l'étude du code suivant http://www.purebasic.fr/english/viewtop ... 12&t=43033 voici un système de contrôle fréquentiel d'une boucle. Le système s'adapte et ralenti la boucle de telle manière qu'elle tourne une fréquence très proche de celle demandée (On veut 50 Hz, elle tourne à 49.9 Hz). Puisque l'on travaille dans l'ordre des millisecondes, il difficile de réguler parfaitement la fréquence de la boucle. Le système agit comme un limitateur de vitesse, ce qui implique que si la charge de calcul augmente, la fréquence de la boucle va diminuer et on n'y peut rien mise à part de faire les calculs en parallèles (Threads) ou en massivement parallèles (CUDA + Carte NVIDIA C1060 Tesla).

Dans mon cas je l'utilise afin de réguler la boucle de gestion d'un jeu en 3D, boucle qui comprend la mise à jour du monde 3D, l'IA, le rendu 3D-2D et les évènements (Input Clavier, Souris, Joystick).

A+
Guimauve

Code : Tout sélectionner

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; CODE GÉNÉRÉ AUTOMATIQUEMENT, NE PAS MODIFIER À
; MOINS D'AVOIR UNE RAISON TRÈS TRÈS VALABLE !!!
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Code généré par : Dev-Type V2.0.1
; Nom du projet : Contrôle fréquentielle d'une boucle
; Nom du fichier : FrameRateControl.pb
; Version du fichier : 1.0.0
; Programmation : OK
; Programmé par : Guimauve
; Date : 15-09-2010
; Mise à jour : 15-09-2010
; Codé pour PureBasic V4.51 RC2
; Plateforme : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Constantes de dimensionnement <<<<<

Enumeration
  
  #FRAMERATECONTROL_TIMER_FPS
  #FRAMERATECONTROL_TIMER_FLIP
  #FRAMERATECONTROL_TIMER_MAX
  
EndEnumeration 

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Déclaration de la Structure <<<<<

Structure FrameRateControl
  
  FpsCounter.l
  FpsActual.d
  Frequency.l
  FrameTime.l
  DelayTime.l
  CurrentTick.l[#FRAMERATECONTROL_TIMER_MAX]
  LastTick.l[#FRAMERATECONTROL_TIMER_MAX]
  ElapsedTick.l[#FRAMERATECONTROL_TIMER_MAX]
  
EndStructure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Les observateurs <<<<<

Macro GetFrameRateControlFpsCounter(FrameRateControlA)
  
  FrameRateControlA\FpsCounter
  
EndMacro

Macro GetFrameRateControlFpsActual(FrameRateControlA)
  
  FrameRateControlA\FpsActual
  
EndMacro

Macro GetFrameRateControlFrequency(FrameRateControlA)
  
  FrameRateControlA\Frequency
  
EndMacro

Macro GetFrameRateControlFrameTime(FrameRateControlA)
  
  FrameRateControlA\FrameTime
  
EndMacro

Macro GetFrameRateControlDelayTime(FrameRateControlA)
  
  FrameRateControlA\DelayTime
  
EndMacro

Macro GetFrameRateControlCurrentTick(FrameRateControlA, TimerID)
  
  FrameRateControlA\CurrentTick[TimerID]
  
EndMacro

Macro GetFrameRateControlLastTick(FrameRateControlA, TimerID)
  
  FrameRateControlA\LastTick[TimerID]
  
EndMacro

Macro GetFrameRateControlElapsedTick(FrameRateControlA, TimerID)
  
  FrameRateControlA\ElapsedTick[TimerID]
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Les mutateurs <<<<<

Macro SetFrameRateControlFpsCounter(FrameRateControlA, P_FpsCounter)
  
  GetFrameRateControlFpsCounter(FrameRateControlA) = P_FpsCounter
  
EndMacro

Macro SetFrameRateControlFpsActual(FrameRateControlA, P_FpsActual)
  
  GetFrameRateControlFpsActual(FrameRateControlA) = P_FpsActual
  
EndMacro

Macro SetFrameRateControlFrequency(FrameRateControlA, P_Frequency)
  
  GetFrameRateControlFrequency(FrameRateControlA) = P_Frequency
  
EndMacro

Macro SetFrameRateControlFrameTime(FrameRateControlA, P_FrameTime)
  
  GetFrameRateControlFrameTime(FrameRateControlA) = P_FrameTime
  
EndMacro

Macro SetFrameRateControlDelayTime(FrameRateControlA, P_DelayTime)
  
  GetFrameRateControlDelayTime(FrameRateControlA) = P_DelayTime
  
EndMacro

Macro SetFrameRateControlCurrentTick(FrameRateControlA, TimerID, P_CurrentTick)
  
  GetFrameRateControlCurrentTick(FrameRateControlA, TimerID) = P_CurrentTick
  
EndMacro

Macro SetFrameRateControlLastTick(FrameRateControlA, TimerID, P_LastTick)
  
  GetFrameRateControlLastTick(FrameRateControlA, TimerID) = P_LastTick
  
EndMacro

Macro SetFrameRateControlElapsedTick(FrameRateControlA, TimerID, P_ElapsedTick)
  
  GetFrameRateControlElapsedTick(FrameRateControlA, TimerID) = P_ElapsedTick
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< L'opérateur Reset <<<<<

Macro ResetFrameRateControl(FrameRateControlA)
  
  SetFrameRateControlFpsCounter(FrameRateControlA, 0)
  SetFrameRateControlFpsActual(FrameRateControlA, 0)
  SetFrameRateControlFrequency(FrameRateControlA, 0)
  SetFrameRateControlFrameTime(FrameRateControlA, 0)
  SetFrameRateControlDelayTime(FrameRateControlA, 0)
  
  For TimerID = 0 To #FRAMERATECONTROL_TIMER_MAX - 1
    SetFrameRateControlCurrentTick(FrameRateControlA, TimerID, 0)
    SetFrameRateControlLastTick(FrameRateControlA, TimerID, 0)
    SetFrameRateControlElapsedTick(FrameRateControlA, TimerID, 0)
  Next
  
  ; ClearStructure(FrameRateControlA, FrameRateControl)
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< L'opérateur Initialize <<<<<

Procedure InitializeFrameRateControl(*FrameRateControlA.FrameRateControl, P_Frequency.l)
  
  SetFrameRateControlFpsCounter(*FrameRateControlA, 0)
  SetFrameRateControlFpsActual(*FrameRateControlA, 0)
  
  SetFrameRateControlFrequency(*FrameRateControlA, P_Frequency)
  SetFrameRateControlFrameTime(*FrameRateControlA, 1000/P_Frequency)
  SetFrameRateControlDelayTime(*FrameRateControlA, 0)
  
  For TimerID = 0 To #FRAMERATECONTROL_TIMER_MAX - 1
    SetFrameRateControlCurrentTick(*FrameRateControlA, TimerID, 0)
    SetFrameRateControlLastTick(*FrameRateControlA, TimerID, 0)
    SetFrameRateControlElapsedTick(*FrameRateControlA, TimerID, 0)
  Next
  
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Code généré en : 00.010 secondes (21000.00 lignes/seconde) <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Procedure StartFrameRateControlTimers(*FrameRateControlA.FrameRateControl)
  
  For TimerID = 0 To #FRAMERATECONTROL_TIMER_MAX - 1
    SetFrameRateControlCurrentTick(*FrameRateControlA, TimerID, ElapsedMilliseconds())
    SetFrameRateControlLastTick(*FrameRateControlA, TimerID, GetFrameRateControlCurrentTick(*FrameRateControlA, TimerID))
    SetFrameRateControlElapsedTick(*FrameRateControlA, TimerID, 0)
  Next
  
EndProcedure

Procedure UpdateFrameRateControlTimerFlip(*FrameRateControlA.FrameRateControl)
  
  SetFrameRateControlCurrentTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FLIP, ElapsedMilliseconds())
  SetFrameRateControlElapsedTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FLIP, GetFrameRateControlCurrentTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FLIP) - GetFrameRateControlLastTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FLIP))
  SetFrameRateControlLastTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FLIP, GetFrameRateControlCurrentTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FLIP))
  SetFrameRateControlFpsCounter(*FrameRateControlA, GetFrameRateControlFpsCounter(*FrameRateControlA) + 1)

EndProcedure

Procedure FrameRateControl_SlowDownIfNeeded(*FrameRateControlA.FrameRateControl)
  
  SetFrameRateControlCurrentTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FPS, ElapsedMilliseconds())
  SetFrameRateControlElapsedTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FPS, GetFrameRateControlCurrentTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FPS) - GetFrameRateControlLastTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FPS))
  
  If GetFrameRateControlElapsedTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FPS) >= 1000
    
    SetFrameRateControlFpsActual(*FrameRateControlA, (GetFrameRateControlFpsCounter(*FrameRateControlA) / GetFrameRateControlElapsedTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FPS)) * 1000)
    SetFrameRateControlCurrentTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FPS, ElapsedMilliseconds())
    SetFrameRateControlElapsedTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FPS, GetFrameRateControlCurrentTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FPS) - GetFrameRateControlLastTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FPS))
    SetFrameRateControlLastTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FPS, GetFrameRateControlCurrentTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FPS))
    SetFrameRateControlFpsCounter(*FrameRateControlA, 0)
    
  EndIf
  
  SetFrameRateControlCurrentTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FLIP, ElapsedMilliseconds())
  SetFrameRateControlElapsedTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FLIP, GetFrameRateControlCurrentTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FLIP) - GetFrameRateControlLastTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FLIP))
  SetFrameRateControlDelayTime(*FrameRateControlA, GetFrameRateControlFrameTime(*FrameRateControlA) - GetFrameRateControlElapsedTick(*FrameRateControlA, #FRAMERATECONTROL_TIMER_FLIP))
  
  If GetFrameRateControlDelayTime(*FrameRateControlA) >= 1
    Delay(GetFrameRateControlDelayTime(*FrameRateControlA))
  EndIf
  
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 
; <<<<< !!! WARNING - YOU ARE NOW IN A TESTING ZONE - WARNING !!! <<<<< 
; <<<<< !!! WARNING - THIS CODE SHOULD BE COMMENTED - WARNING !!! <<<<< 
; <<<<< !!! WARNING - BEFORE THE FINAL COMPILATION. - WARNING !!! <<<<< 
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 

InitializeFrameRateControl(FrameRateControl.FrameRateControl, 50)

StartFrameRateControlTimers(FrameRateControl)

Repeat
  
  UpdateFrameRateControlTimerFlip(FrameRateControl)
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Début de la section des instructions <<<<<
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; 1. Gestion Évenèments + Clavier, Souris, Joystick
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; 2. Mise à jour de l'espace 3D (Position, Collision)
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; 3. Affichage de l'espace 3D vue par la(les) caméra(s)
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; 4. Affichage des éléments 2D (Messages, menus, etc.) 
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Fin de la section des instructions <<<<<
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  
  FrameRateControl_SlowDownIfNeeded(FrameRateControl)
  
  Debug StrD(GetFrameRateControlFpsActual(FrameRateControl),1) + " FPS"
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; Les instructions suivantes servent uniquement pour terminer la boucle.
  ; En temps normal, la sortie de la boucle de dépend pas d'un nombre de 
  ; tours de boucle. Elle dépend plutôt d'un évènement particulier.
  
  If LoopCount >= 250
    Quit = #True
  Else 
    LoopCount + 1
    Debug LoopCount
  EndIf 
  
Until Quit = #True

ResetFrameRateControl(FrameRateControl)

; <<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< FIN DU FICHIER <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: Contrôle fréquentielle d'une boucle

Message par G-Rom »

Super Guimauve , comme d'habitude tes codes sont de très grande qualité !
pour ma part , adopté ! :D
Guimauve
Messages : 1015
Inscription : mer. 11/févr./2004 0:32
Localisation : Québec, Canada

Re: Contrôle fréquentielle d'une boucle

Message par Guimauve »

G-Rom a écrit :Super Guimauve , comme d'habitude tes codes sont de très grande qualité !
pour ma part , adopté ! :D
J'aime avoir des petits modules dans mes programmes de tel sorte que je peux les déboguer facilement et surtout séparément. Et s'il y un bogue il suffit simplement d'aller voir le module fautif. C'est un des avantage de la programmation Objet. (Dans mon cas c'est la méthode PBO ou Programmation Basée Objet qui était utilisée en C juste avant l'apparition de C++ Orienté Objet)

Et un autre point, je simplement fais la synthèse du code de moogle, je n'ai pas vraiment de mérite à recevoir pour ce code mais il faut dire que je l'ai légèrement clarifié.

A+
Guimauve
Répondre