Gestion du FPS

Partagez votre expérience de PureBasic avec les autres utilisateurs.
Avatar de l’utilisateur
flaith
Messages : 1487
Inscription : jeu. 07/avr./2005 1:06
Localisation : Rennes
Contact :

Gestion du FPS

Message par flaith »

Salut tout le monde

voici un code tiré de la lib SDL_gfx par ferzkopp
Le code original provient du source SDL_framerate.c
Il permet de fixer le FPS

Fichier include
FPS_Manager.pbi :

Code : Tout sélectionner

; From SDL_gfx by ferzkopp
; SDL_framerate.c
; http://www.ferzkopp.net/Software/SDL_gfx-2.0/

;-CONSTANTS
; Highest possible rate supported by framerate controller in Hz (1/s).
#FPS_UPPER_LIMIT    = 200
; Lowest possible rate supported by framerate controller in Hz (1/s).
#FPS_LOWER_LIMIT    = 1
; Default rate of framerate controller in Hz (1/s).
#FPS_DEFAULT        = 30

;-METHOD
Interface FPSObject
  FPS_setFramerate.i(__rate)
  FPS_getFramerate.i()
  FPS_getFramecount.i()
  FPS_framerateDelay.i()
EndInterface

;-TEMPLATE
; Structure holding the state and timing information of the framerate controller. 
Structure FPSmanager
  *vTable
  obj.FPSObject                      ; To be able to call class inside class
  ; Private
  framecount.i
  rateticks.f
  baseticks.i
  lastticks.i
  rate.i
EndStructure

;-DECLARE
Declare.i FPS_initFramerate()
Declare.i CLASS_setFramerate(*manager.FPSmanager, __rate.i)
Declare.i CLASS_getFramerate(*manager.FPSmanager)
Declare.i CLASS_getFramecount(*manager.FPSmanager)
Declare.i CLASS_framerateDelay(*manager.FPSmanager)

;-VIRTUAL TABLES
DataSection 
  VTable_FPSClass: 
    Data.i @CLASS_setFramerate()
    Data.i @CLASS_getFramerate()
    Data.i @CLASS_getFramecount()
    Data.i @CLASS_framerateDelay()
EndDataSection

;-INTERNAL PROCEDURE
Procedure.i getTicks()
  Protected.i _ticks = ElapsedMilliseconds()

  If _ticks = 0
    ProcedureReturn 1
  Else
    ProcedureReturn _ticks
  EndIf
EndProcedure

;-*************************************************************
;-Initialize framerate manager
; set Default framerate of 30Hz and reset delay interpolation.
; \param manager : Pointer to the framerate manager.
; \return        : Pointer for sucess and 0 for error.
Procedure.i FPS_initFramerate()
  Protected *manager.FPSManager

  *manager = AllocateMemory(SizeOf(FPSmanager))
  If *manager
    ;Need to initialize structure for the call of class inside another class
    InitializeStructure(*manager, FPSmanager)
    ;Make sure the *vTable field points to our virtual table.
    *manager\vTable     = ?VTable_FPSClass
    ;now the call of class inside class can be done
    *manager\obj        = *manager
    ;Initialise the variables if any
    *manager\framecount = 0
    *manager\rate       = #FPS_DEFAULT
    *manager\rateticks  = 1000.0 / #FPS_DEFAULT
    *manager\baseticks  = getTicks()
    *manager\lastticks  = *manager\baseticks
    ;Return a pointer to our new object.
    ProcedureReturn *manager
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

;-Set the framerate in Hz 
; Sets a new framerate For the manager And reset delay interpolation.
; Rate values must be between FPS_LOWER_LIMIT And FPS_UPPER_LIMIT inclusive To be accepted.
; \param manager : Pointer to the framerate manager.
; \param rate    : The new framerate in Hz (frames per second).
; \return        : 0 For sucess and -1 for error.
Procedure.i CLASS_setFramerate(*manager.FPSmanager, __rate.i)
  If (__rate >= #FPS_LOWER_LIMIT) And (__rate <= #FPS_UPPER_LIMIT)
    *manager\framecount = 0
    *manager\rate       = __rate
    *manager\rateticks  = 1000.0 / __rate
    ProcedureReturn 0
  Else
    ProcedureReturn -1
  EndIf
EndProcedure

;-Return current target framerate 
; Get the currently set framerate of the manager in Hz.
; \param manager : Pointer To the framerate manager.
; \return        : Current framerate in Hz or -1 for error.
Procedure.i CLASS_getFramerate(*manager.FPSmanager)
  If *manager = #Null
    ProcedureReturn -1
  Else
    ProcedureReturn *manager\rate
  EndIf
EndProcedure

;-Return current framecount
; Get the current framecount from the framerate manager. 
; A frame is counted each time SDL_framerateDelay is called.
; \param manager : Pointer To the framerate manager.
; \return        : Current frame count or -1 for error.
Procedure.i CLASS_getFramecount(*manager.FPSmanager)
  If *manager = #Null
    ProcedureReturn -1
  Else
    ProcedureReturn *manager\framecount
  EndIf
EndProcedure

;-Delay execution
; Maintain a constant framerate and calculate fps.
; Generate a delay to accomodate currently set framerate. Call once in the
; graphics/rendering loop. If the computer cannot keep up with the rate (i.e.
; drawing too slow), the delay is zero And the delay interpolation is reset.
; \param manager : Pointer To the framerate manager.
; \return        : The time that passed since the last call to the function in ms. May return 0.
Procedure.i CLASS_framerateDelay(*manager.FPSmanager)
  Protected.i _current_ticks
  Protected.i _target_ticks
  Protected.i _the_delay
  Protected.i _time_passed = 0

  ; No manager, no delay
  If *manager = #Null : ProcedureReturn 0 : EndIf
 
  ; Initialize uninitialized manager 
  If *manager\baseticks = 0 : *manager = FPS_initFramerate() : EndIf

  ; Next frame
  *manager\framecount + 1

  ; Get/calc ticks 
  _current_ticks      = getTicks()
  _time_passed        = _current_ticks - *manager\lastticks
  *manager\lastticks  = _current_ticks
  _target_ticks       = *manager\baseticks + (*manager\framecount * *manager\rateticks)

  If _current_ticks <= _target_ticks
    _the_delay = _target_ticks - _current_ticks
    Delay(_the_delay)
  Else
    *manager\framecount = 0
    *manager\baseticks  = getTicks()
  EndIf

  ProcedureReturn _time_passed
EndProcedure
Test code :

Code : Tout sélectionner

XIncludeFile "FPS_Manager.pbi"

; from the sample : TestFramerate.c of SDL_gfx
#scr_WIDTH  = 640
#scr_HEIGHT = 480
i=0
x=#scr_WIDTH/2
y=#scr_HEIGHT/2
larg_circle = 60
dx=7
dy=5
time_passed = 0
message.s = ""
message2.s = ""

InitSprite():InitKeyboard()
If OpenWindow(0, 0, 0, #scr_WIDTH, #scr_HEIGHT, "FPS Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  OpenWindowedScreen(WindowID(0), 0, 0, #scr_WIDTH, #scr_HEIGHT, #True, 0, 0)
  If CreateSprite(0, larg_circle, larg_circle) And StartDrawing(SpriteOutput(0))

    For Radius = larg_circle/2 To 1 Step -5
      Circle(larg_circle/2, larg_circle/2, Radius, RGB(Random(255), Random(255), Random(255)))
    Next

    StopDrawing()
  EndIf
  TransparentSpriteColor(0, 0)

  ; Set our FPS Manager, default:30
  fpsm.FPSObject
  fpsm = FPS_initFramerate()
  rate = fpsm\FPS_getFramerate()
  message = "Framerate set to "+Str(rate)+" Hz ..."

  Repeat
    Repeat
      Event = WindowEvent()

      Select Event 
        Case #PB_Event_Gadget
          If EventGadget() = 0
            quit = 1
          EndIf
        
        Case #PB_Event_CloseWindow
          quit = 1 
      EndSelect
    Until Event = 0

    ExamineKeyboard()
    If KeyboardPushed(#PB_Key_Escape) : quit = 1 : EndIf

    i - 1
    If i<0
      ; Set new rate
      rate=5+5*(Random(100) % 10)
      fpsm\FPS_setFramerate(rate)
      message = "Framerate set to "+Str(rate)+" Hz ..."
      ; New timeout
      i=2*rate
    EndIf
  
    ClearScreen(0)
    StartDrawing(ScreenOutput())  
    DrawText(#scr_WIDTH/2-4*Len(message),#scr_HEIGHT-15,message)
    If time_passed > 0
      message2 = "Delay is "+Str(time_passed)+" ms / Measured framerate "+Str(1000/time_passed)+" Hz ..." 
      DrawText(#scr_WIDTH/2-4*Len(message2),#scr_HEIGHT-30,message2)
    EndIf
    StopDrawing()
   
    ; Move
    x + dx
    y + dy

    ; Reflect
    If x<0 Or x>#scr_WIDTH-larg_circle  : dx = -dx : EndIf
    If y<0 Or y>#scr_HEIGHT-larg_circle : dy = -dy : EndIf
    DisplayTransparentSprite(0, x, y)

    FlipBuffers()
    time_passed = fpsm\FPS_framerateDelay()
  Until Quit = 1
EndIf
Avatar de l’utilisateur
blendman
Messages : 2017
Inscription : sam. 19/févr./2011 12:46

Re: Gestion du FPS

Message par blendman »

salut

Sur mon ordinateur, ce code a un résultat assez ... horrible ^^.
Soit ça accélère, soit ça ralentit, sans raison.

G-rom m'avait filé cette technique, plus courte et qui fonctionne pas mal :

Code : Tout sélectionner

    #FPS = 60
    If ElapsedMilliseconds() > CheckTime + 1000/#FPS
      CheckTime = ElapsedMilliseconds() 
      
      ; affichage      
      ClearScreen(0)
      FlipBuffers()

      ; evenement d'affichage (update UI)
      
    Else   
      Delay(1)        
    EndIf 
Dans un code complet, ça donne :

Code : Tout sélectionner

#scr_WIDTH  = 640
#scr_HEIGHT = 480

x=#scr_WIDTH/2
y=#scr_HEIGHT/2
larg_circle = 60
dx=7
dy=5

#FPS = 60

InitSprite():InitKeyboard()

If OpenWindow(0, 0, 0, #scr_WIDTH, #scr_HEIGHT, "FPS Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  OpenWindowedScreen(WindowID(0), 0, 0, #scr_WIDTH, #scr_HEIGHT, #True, 0, 0)
  
  If CreateSprite(0, larg_circle, larg_circle) And StartDrawing(SpriteOutput(0))
    For Radius = larg_circle/2 To 1 Step -5
      Circle(larg_circle/2, larg_circle/2, Radius, RGB(Random(255), Random(255), Random(255)))
    Next
    StopDrawing()
  EndIf
  TransparentSpriteColor(0, 0)

  Repeat
        
    If ElapsedMilliseconds() > CheckTime + 1000/#FPS
      CheckTime = ElapsedMilliseconds() 
      
      ; affichage      
      ClearScreen(0)
      
      x + dx 
      y + dy 
      
      If x<0 Or x>#scr_WIDTH-larg_circle  : dx = -dx : EndIf
      If y<0 Or y>#scr_HEIGHT-larg_circle : dy = -dy : EndIf
      DisplayTransparentSprite(0, x, y)
      
      FlipBuffers()
      
      ; evenement d'affichage (update UI)
            
    Else   
      Delay(1)        
    EndIf 
    
    ; Autres events
    Repeat
      Event = WindowEvent()

      Select Event
        Case #PB_Event_Gadget
          If EventGadget() = 0
            quit = 1
          EndIf
       
        Case #PB_Event_CloseWindow
          quit = 1
      EndSelect
    Until Event = 0

    ExamineKeyboard()
    If KeyboardPushed(#PB_Key_Escape) : quit = 1 : EndIf

  Until Quit = 1
EndIf
On règle le FPS avec #FPS, mais on peut passer par un fichier ini et une variable si on veut.
Avatar de l’utilisateur
flaith
Messages : 1487
Inscription : jeu. 07/avr./2005 1:06
Localisation : Rennes
Contact :

Re: Gestion du FPS

Message par flaith »

blendman a écrit :salut

Sur mon ordinateur, ce code a un résultat assez ... horrible ^^.
Soit ça accélère, soit ça ralentit, sans raison.
Salut blendman

En fait c'est pas sans raison, regarde la ligne :

Code : Tout sélectionner

    If i<0
      ; Set new rate
      rate=5+5*(Random(100) % 10)
      fpsm\FPS_setFramerate(rate)
      message = "Framerate set to "+Str(rate)+" Hz ..."
      ; New timeout
      i=2*rate
    EndIf
C'est pour les tests, il fait des modif de framerate aleatoirement :mrgreen:
Avatar de l’utilisateur
flaith
Messages : 1487
Inscription : jeu. 07/avr./2005 1:06
Localisation : Rennes
Contact :

Re: Gestion du FPS

Message par flaith »

Je l'utilise pour mes tests de 2D Sprite Engine OldSkool dont voici le lien du programme+images : GLMFS2D

Si ca interesse quelqu'un :wink:

Image
Avatar de l’utilisateur
blendman
Messages : 2017
Inscription : sam. 19/févr./2011 12:46

Re: Gestion du FPS

Message par blendman »

flaith a écrit :En fait c'est pas sans raison, regarde la ligne :

Code : Tout sélectionner

    If i<0
      ; Set new rate
      rate=5+5*(Random(100) % 10)
      fpsm\FPS_setFramerate(rate)
      message = "Framerate set to "+Str(rate)+" Hz ..."
      ; New timeout
      i=2*rate
    EndIf
C'est pour les tests, il fait des modif de framerate aleatoirement :mrgreen:
ah oui, j'avais pas vu le random() désolé ^^'
Effectivement, je comprends mieux pourquoi ça donnait ce résultat.

Mais si on compare au code que G-rom m'avait filé, ça ne fait pas sensiblement la même chose (en plus court) ?
Avatar de l’utilisateur
flaith
Messages : 1487
Inscription : jeu. 07/avr./2005 1:06
Localisation : Rennes
Contact :

Re: Gestion du FPS

Message par flaith »

J'utilisais aussi cette methode, mais j'ai vu celle utilise par SDL et l'ai adapte a PB.

De plus tu n'a plus besoin de gerer le delai, le Manager le fait pour toi et recalcule tout le temps le nombre a indiquer pour le delai afin d'etre le plus proche possible du framerate.

Et puis j'aime bien cette approche facon objet :D
Avatar de l’utilisateur
blendman
Messages : 2017
Inscription : sam. 19/févr./2011 12:46

Re: Gestion du FPS

Message par blendman »

J'ai testé les deux méthodes, et j'ai remarqué que celle adaptée de la SDL prenait environ 7% de CPU sur mon ordinateur, alors que celle de G-rom prend 0% de CPU.

Toutes les deux prennent sur mon ordinateur environ 6,6Mo en ram.

Donc, celle de G-rom permet de gagner 7% de CPU avec ce simple exemple (un cercle qui bouge), je ferai des tests avec une scène plus complexe, je me demande si du coup la méthode de G-rom ne permet pas de gagner du CPU, ce qui ne serait pas négligeable sur un jeu ;).
Avatar de l’utilisateur
flaith
Messages : 1487
Inscription : jeu. 07/avr./2005 1:06
Localisation : Rennes
Contact :

Re: Gestion du FPS

Message par flaith »

Et bien essaye avec la demo du Sprite Engine citée plus haut :wink:
Avatar de l’utilisateur
Ar-S
Messages : 9539
Inscription : dim. 09/oct./2005 16:51
Contact :

Re: Gestion du FPS

Message par Ar-S »

Salut Flaith ;)
flaith a écrit :Et bien essaye avec la demo du Sprite Engine citée plus haut :wink:
Chez moi en 5.20beta18 sans debug
- conso proce oscille entre 0.4 et 1.8% pour 12mo de Ram

Si ça peut donner une base.
~~~~Règles du forum ~~~~
⋅.˳˳.⋅ॱ˙˙ॱ⋅.˳Ar-S ˳.⋅ॱ˙˙ॱ⋅.˳˳.⋅
W11x64 PB 6.x
Section HORS SUJET : ICI
LDV MULTIMEDIA : Dépannage informatique & mes Logiciels PB
UPLOAD D'IMAGES : Uploader des images de vos logiciels
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Re: Gestion du FPS

Message par Backup »

la methode G-rom est forcement plus veloce,
pas de procedure donc
pas de passage par la pile (empilage/dépilage) qui prennent du temps ... :)

apres le resultat... je ne saurai dire

dans une boucle , un appel a une procedure ralentie la boucle

il vaudrai mieux pour le coup passer par des Gosub ...
vu que ça s'execute a chaque boucle :)
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: Gestion du FPS

Message par G-Rom »

Donc, celle de G-rom permet de gagner 7% de CPU avec ce simple exemple (un cercle qui bouge), je ferai des tests avec une scène plus complexe, je me demande si du coup la méthode de G-rom ne permet pas de gagner du CPU, ce qui ne serait pas négligeable sur un jeu
C'est le delay(1) qui fait cela, mais en contre partie on perd un peu en précision quand au FPS réel ( 5 ou 7 de décalage si je me rappelle bien , si ce n'est pas plus...) la méthode de flaith doit forcement être plus fiable , rajoute un delay(1) hors affichage , tu auras 0% aussi dans ton CPU.
mais au fur & mesure que ton jeu grossi , le CPU va monté , c'est inévitable , peu être pas pour un pong par contre ;)
Avatar de l’utilisateur
flaith
Messages : 1487
Inscription : jeu. 07/avr./2005 1:06
Localisation : Rennes
Contact :

Re: Gestion du FPS

Message par flaith »

@Blendman: Merci d'avoir testé 8) :D
@Ar-s: Salut et merci du retour :D
@Dobro: Salut et merci pour l'info :wink: :D
@G-Rom: Salut et merci aussi pour les explications :D :mrgreen:

@tous : De retour sur le forum, sympa de voir de nouveaux venus et de revoir les anciens :mrgreen:
Avatar de l’utilisateur
Micoute
Messages : 2583
Inscription : dim. 02/oct./2011 16:17
Localisation : 35520 La Mézière

Re: Gestion du FPS

Message par Micoute »

Bonjour flaith et heureux de ton retour sur ce forum !
Microsoft Windows 10 Famille 64 bits : Carte mère : ASRock 970 Extreme3 R2.0 : Carte Graphique NVIDIA GeForce RTX 3080 : Processeur AMD FX 6300 6 cœurs 12 threads 3,50 GHz PB 6.20 LTS (x64)
Un homme doit être poli, mais il doit aussi être libre !
Avatar de l’utilisateur
flaith
Messages : 1487
Inscription : jeu. 07/avr./2005 1:06
Localisation : Rennes
Contact :

Re: Gestion du FPS

Message par flaith »

Micoute a écrit :Bonjour flaith et heureux de ton retour sur ce forum !
Bonjour Micoute, merci, ce forum m'a manqué :wink:
Répondre