Seite 2 von 2

Re: [GameLoop] DeltaTime & FPS ? (kein V-Sync/Framelimit)

Verfasst: 06.05.2018 18:55
von Mijikai
mk-soft hat geschrieben:Es ist besser mit Frames zu arbeiten um nicht die CPU Last zu hoch zu schrauben...

...
Danke, verwendet aber leider AddWindowTimer() was ich (später) nicht nutzen kann.
Der Code soll auch bei Fenstern funktionieren die nicht mit PureBasic erstellt wurden.

Hier mal mein Ansatz, leider ist noch was falsch es wird auf ~100 FPS :? gedrosselt anstatt auf 60!

Code:

Code: Alles auswählen

DeclareModule FPS
  Declare.i Init()
  Declare.i Count()
  Declare.f Delta()
  Declare.i Update()
EndDeclareModule

Module FPS
  
  #FPS_60 = 16
  #FPS_30 = 33
  #FPS_25 = 40
  #FPS_80 = 12
  #FPS_75 = 13
  #FPS_24 = 41
  #FPS_20 = 50
  #FPS_10 = 100
    
  Structure PERFORMANCE_STRUCT
    FrameTarget.q
    FrameStart.q
    FrameEnd.q
    FrameTime.f   
    Timing.f
    FrameCount.i
    FPS.i
    DeltaTime.f
  EndStructure
  
  Global Performance.PERFORMANCE_STRUCT
  
  Procedure.i Init()
    With Performance
      \FrameTarget = #FPS_60;GEWÜNSCHTE FPS!
      \FrameStart = ElapsedMilliseconds()
    EndWith
  EndProcedure
  
  Procedure.i Count()
    ProcedureReturn Performance\FPS
  EndProcedure
  
  Procedure.f Delta()
    ProcedureReturn Performance\DeltaTime
  EndProcedure
  
  Procedure.i Update() 
    With Performance
      If \FrameTime < \FrameTarget
        Delay(\FrameTarget - \FrameTime);NICHT KORREKT !?
      EndIf
      \FrameEnd = ElapsedMilliseconds()
      \FrameTime = \FrameEnd - \FrameStart
      \DeltaTime = \FrameTime / \FrameTarget
      \FrameStart = \FrameEnd
      If \Timing > 999
        \FPS = \FrameCount
        \Timing = 0
        \FrameCount = 1
      Else
        \Timing + \FrameTime
        \FrameCount + 1
      EndIf 
    EndWith
  EndProcedure

EndModule

If InitSprite()
  If OpenWindow(0,0,0,400,400,"TestWindow",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
    If OpenWindowedScreen(WindowID(0),0,0,400,400,#False,0,0,#PB_Screen_NoSynchronization);<- KEIN SYNCH!
      FPS::Init()
      Repeat
        ClearScreen($EB8724)
        If StartDrawing(ScreenOutput())
          DrawingMode(#PB_2DDrawing_Transparent)
          DrawText(10,10,"FPS: " + Str(FPS::Count()))
          DrawText(10,30,"DLT: " + StrF(FPS::Delta(),2))
          StopDrawing()
        Else
          PostEvent(#PB_Event_CloseWindow)
        EndIf
        FlipBuffers()
        FPS::Update()
      Until WindowEvent() = #PB_Event_CloseWindow
    EndIf
  EndIf 
EndIf

Re: [GameLoop] DeltaTime & FPS ? (kein V-Sync/Framelimit)

Verfasst: 06.05.2018 19:00
von RSBasic
Achso, du verwendest OpenWindowedScreen. Dann ist ein Delay schon sinnvoll, um den Prozessor zu entlasten.
In der PB-Hilfe unter OpenWindowedScreen gibt es ein Beispielcode mit einem Delay(1) und die Screen-Anwendung verbraucht nur 1 %-Prozessorauslastung.

Re: [GameLoop] DeltaTime & FPS ? (kein V-Sync/Framelimit)

Verfasst: 06.05.2018 19:52
von ccode_new
Huhu!!!!

Es ist eigentlich recht simpel.

Wichtig ist eine konstante Framerate festzulegen und die Zeit zwischen dem Aktualisieren zu messen (DeltaT).

Das sind eigentlich die Standards und alles ist locker flockig handelbar.

Man sollte aber beachten das trotz NOSync meist Treibertechnisch trotzdem bei 60 FPS abgeriegelt wird.

Re: [GameLoop] DeltaTime & FPS ? (kein V-Sync/Framelimit)

Verfasst: 06.05.2018 21:38
von Jan125
Hm, keine Ahnung ob das weiterhelfen könnte, aber mal 'ne kleine Entkoppelung via Threads.
Vermutlich müssen sich die erfahreneren Coder hier gleich übergeben. ;P

Code: Alles auswählen

Procedure.l GetTime()
  Static TimeLast.l
  Protected TimeNow.l
  Protected TimeReturn.l
  If TimeLast = 0
    TimeLast = ElapsedMilliseconds()
    ProcedureReturn 0
  EndIf
  TimeNow = ElapsedMilliseconds()
  TimeReturn = TimeNow-TimeLast
  TimeLast = TimeNow
  ProcedureReturn TimeReturn
EndProcedure
Procedure.l ThreadDelay(Delay)
  Delay(Delay)
  ProcedureReturn 0
EndProcedure

Structure ObjectStructure
  
EndStructure


Global Mutex.l
Global NewList svObjectList.ObjectStructure()
Global NewList clObjectList.ObjectStructure()



Procedure GameInit()
  UsePNGImageDecoder()
  
  InitSprite()
  InitSound()
  InitKeyboard()
  
  PrintN("Opening window.")
  OpenWindow(1, #PB_Ignore, #PB_Ignore, 800, 600, "asCL")
  PrintN("Opening screen.")
  OpenWindowedScreen(WindowID(1), 0, 0, 400, 300, 1, 0, 0, #PB_Screen_NoSynchronization)
  TransparentSpriteColor(#PB_Default, $ff00ff)
  
  
  For i = 0 To 0
  AddElement(svObjectList())
Next
EndProcedure

Procedure GameClient()
  Protected Event.l
  ;SetFrameRate(1000)
  UnlockMutex(Mutex)
  
  Repeat
    Repeat
      Event = WindowEvent()
      Select Event
        Case #PB_Event_CloseWindow
          Quit = 1
      EndSelect
    Until Event = 0
    ClearScreen($606060)
    
    
    
    
    
    LockMutex(Mutex)
    CopyList(svObjectList(), clObjectList())
    UnlockMutex(Mutex)
    
    
    
    
    ForEach clObjectList()
      ;;Display Objects
    Next
    
    
    
    
    
    
    
    FlipBuffers()
    
  Until Quit = 1
  
  ProcedureReturn 0
EndProcedure
Procedure GameServer(Rate_TickRate = 100) ;low overhead asynch rate server
  Protected ThreadDelayID.l
  Protected Correction.l
  Protected Time.l
  Protected ThreadCreationCorrection.l
  For i = 1 To 10
    Time = ElapsedMilliseconds()
    ServerDelay = CreateThread(@ThreadDelay(), 1)
    WaitThread(ServerDelay)
    ThreadCreationCorrection+(Time-ElapsedMilliseconds())
  Next
  ThreadCreationCorrection/10
  PrintN("Thread creation delay: "+Str(ThreadCreationCorrection))
  Time = ElapsedMilliseconds()
  Rate_Elapsed = Rate_TickRate
  PrintN("Resuming server loop.")
  Repeat
    Correction = Correction+(Rate_TickRate-Rate_Elapsed)
    If Rate_TickRate+Correction+ThreadCreationCorrection > 0
      ServerDelay = CreateThread(@ThreadDelay(), Rate_TickRate+Correction+ThreadCreationCorrection)
    EndIf
    
    ;;Debugger has to be disabled when calling Keyboard functions in a thread.
    ;;Else it will raise an exception
    DisableDebugger
    ExamineKeyboard()
    LockMutex(Mutex)
    FirstElement(svObjectList())
    ;;Keyboard Queries
    
    EnableDebugger
    
    ForEach svObjectList()
      ;;Object Actions
    Next
    UnlockMutex(Mutex)
    
    WaitThread(ServerDelay)
    Rate_Elapsed = ElapsedMilliseconds()-Time
    Time+Rate_Elapsed
    PrintN(Str(Rate_Elapsed))
    Debug Correction
    
  ForEver
  
  ProcedureReturn 0
EndProcedure



If OpenLibrary(0, "Kernel32.dll")
  Prototype GetConsoleWindow()
  Define GetConsoleWindow.GetConsoleWindow = GetFunction(0, "GetConsoleWindow")
  CloseLibrary(0)
EndIf



OpenConsole()
ConsoleHandle = GetConsoleWindow()
EnableMenuItem_(GetSystemMenu_(ConsoleHandle, #False), #SC_CLOSE, #MF_DISABLED)






Mutex = CreateMutex()

GameInit()
LockMutex(Mutex)
Server = CreateThread(@GameServer(), 20)
GameClient()
PrintN("Locking server thread.")
LockMutex(Mutex)
KillThread(Server)
PrintN("Killed server thread.")
UnlockMutex(Mutex)
PrintN("Shutting down.")
End
Müsstest eben was in die Struktur einfügen und auch Aktionen dafür festlegen...

Re: [GameLoop] DeltaTime & FPS ? (kein V-Sync/Framelimit)

Verfasst: 06.05.2018 21:49
von ccode_new
Ah!

"ThreadCreationCorrection" :mrgreen:
Vermutlich müssen sich die erfahreneren Coder hier gleich übergeben. ;P
:mrgreen:

Keine Ahnung!

Re: [GameLoop] DeltaTime & FPS ? (kein V-Sync/Framelimit)

Verfasst: 06.05.2018 22:46
von Mijikai
Jan125 hat geschrieben:Hm, keine Ahnung ob das weiterhelfen könnte, aber mal 'ne kleine Entkoppelung via Threads.
Vermutlich müssen sich die erfahreneren Coder hier gleich übergeben. ;P Müsstest eben was in die Struktur einfügen und auch Aktionen dafür festlegen...
:shock:
Was passiert hier ?
Ist zu komplex/durcheinander für mich.

Re: [GameLoop] DeltaTime & FPS ? (kein V-Sync/Framelimit)

Verfasst: 07.05.2018 06:33
von Jan125
Ups, stimmt ja... :D

Ich entkoppele die Logik von der Anzeige via Threads.
Der Großteil des Codes besteht aus Zeitstabilisierung und Korrektur (dazu eben noch das Thread-Erzeugungs-Delay, denn der Delay wird asynchron über einen weiteren Thread bewerkstelligt).
Im Endeffekt kümmert sich der "Server" um Eingaben und alles andere, packt darzustellende Objekte in eine Liste, der "Client" kopiert diese Liste und stellt sie dann dar.

Somit muss man sich um Frameratediskrepanzen keine Sorgen mehr machen. ^w^


(Das Ganze könnte dennoch nicht ganz Save sein, siehe Anmerkung zu Tastaturevents im Server.)

Re: [GameLoop] DeltaTime & FPS ? (kein V-Sync/Framelimit)

Verfasst: 08.05.2018 22:05
von Mijikai
Jan125 hat geschrieben:
Würde den Code gerne besser verstehen :)

- Wo ist die Procedure -> ThreadDelay() ?

Code: Alles auswählen

CreateThread(@ThreadDelay(), 1)
;...
CreateThread(@ThreadDelay(), Rate_TickRate+Correction+ThreadCreationCorrection)
;...
- Wird WindowEvent() nicht geblockt solange bis es ein Signal gibt ?

- Der Code scheint den Prozessor nicht zu entlasten hängt bei mir bei 5 - 8% !

Re: [GameLoop] DeltaTime & FPS ? (kein V-Sync/Framelimit)

Verfasst: 09.05.2018 05:17
von Jan125
ThreadDelay ist ganz oben, zweite Prozedur.
Soll einfach nur als Zeitgeber fungieren.

Nö, das wäre WaitWindowEvent, und wenn man das benutzen würde, dann würde die Darstellung blockieren. :D (Oder man macht einen weiteren Thread...)

Entlastung ist hier nicht das Ziel, die Performance wird sogar eher stark abnehmen. Hier soll nur die Tickrate korrigiert werden (wenn in einem Tick zu viel bearbeitet werden muss kann das durch den nächsten Tick u. U. ausgeglichen werden), sowie eine generelle Unabhängigkeit von der britischen Kron-... Äh, von der Bildwiederholfrequenz des Monitors (ältere CRTs haben z. B. meist 75Hz statt 60Hz, manche neueren Monitore 144Hz).

Da ich morgen frei habe setz' ich mich heute Abend mal hin und schreib 'n kleines Beispiel. :>

Re: [GameLoop] DeltaTime & FPS ? (kein V-Sync/Framelimit)

Verfasst: 09.05.2018 23:30
von Mijikai
Jan125 hat geschrieben:ThreadDelay ist ganz oben, zweite Prozedur.
Soll einfach nur als Zeitgeber fungieren.
Ah, das habe ich tatsächlich nicht gesehen, sah wie eine Funktion aus.
Jan125 hat geschrieben:Nö, das wäre WaitWindowEvent, und wenn man das benutzen würde, dann würde die Darstellung blockieren. :D (Oder man macht einen weiteren Thread...)
Ok das ist gut, ich steig bei Threads noch nicht wirklich durch.

_________________________________________________________________________________________


Ich hab bei meinem Code jetzt eine 'Clock' hinzugefügt um ein Render-Event auszulösen.

Code: Alles auswählen

If \Clock >= \FrameTarget
;...
Damit funktioniert es jetzt.
Momentan wird der Prozessor mit Delay(1) entlasted, besser
wäre ein Nano-Sleep (möglich aber nur mit OS-API).

Bild