Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Do Mär 28, 2024 19:53

Foren-Übersicht » DGL » Feedback
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 11 Beiträge ] 
Autor Nachricht
BeitragVerfasst: So Jul 01, 2012 22:26 
Offline
DGL Member

Registriert: Di Jun 12, 2012 21:26
Beiträge: 112
Programmiersprache: Delphi
Hallo,

Beim Durcharbeiten einiger der Tutorials und auch in der Beschreibung zum Timebased Movement auf DGL-Wiki ist mir aufgefallen, dass das Timebased-Movement oft ungenau implementiert ist. Ich weiss nicht, ob hier das Problem schon gepostet wurde. Hier die oft benutzte Implementierung:

Code:
  1.  
  2. Var TimeFactor:Single; //global
  3. ... 
  4. procedure TForm1.IdleHandler(Sender: TObject; var Done: Boolean);
  5. Var
  6.  QPCStart:Int64;
  7.  QPCEnd:Int64;
  8. Begin
  9.  QueryPerformanceCounter(QPCStart);
  10.  
  11.  Render;
  12.  
  13.  QueryPerformanceCounter(QPCEnd);
  14.  TimeFactor := (QPCEnd-QPCStart) / QPCFreq;
  15. End;
  16.  

In den meisten Fällen wird es mit dieser Implementierung zwar keine Probleme geben, es kann jedoch unter Umständen eine spürbare Ungenauigkeit bei der Zeitmessung auftreten. Eine, meiner Meinung nach, bessere Implementierung wäre z.B. folgende:
Code:
  1.  
  2. Var
  3.  TimeFactor:Single //global
  4.  QPCStart:Int64; //global
  5.  QPCEnd:Int64; //global
  6. ... 
  7. procedure TForm1.FormCreate(Sender: TObject);
  8. Begin
  9.  QPCStart:=-1;
  10. End;
  11.  
  12. procedure TForm1.IdleHandler(Sender: TObject; var Done: Boolean);
  13. Begin
  14.   IF QPCStart=-1 then
  15.      QueryPerformanceCounter(QPCStart) else
  16.      QPCStart:=QPCEnd;
  17.  
  18.  Render;
  19.  
  20.  QueryPerformanceCounter(QPCEnd);
  21.  TimeFactor := (QPCEnd-QPCStart) / QPCFreq;
  22.  
  23. End;
  24.  
  25.  

Viele Grüße


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: So Jul 01, 2012 22:47 
Offline
DGL Member
Benutzeravatar

Registriert: Di Apr 29, 2008 18:56
Beiträge: 1213
Programmiersprache: Delphi/FPC
Hey,

da hast du natürlich Recht. Ich hab mir damals für das ganze Timebased-Movement ne Art Timer-Klasse programmiert. Wenn jmd Interesse daran hat:
Code:
  1. type
  2.   //Event welches der Timer auslösen soll
  3.   TOnTimerEvent = procedure(Sender: TObject) of Object;
  4.   //QueryPerformanceCounter-basierender Timer
  5.   TgluTimer = class(TObject)
  6.   private
  7.     //Anzahl der Frames der aktuellen Sekunde
  8.     fFrameCount: Cardinal;
  9.     //Zeit unterscheid zwischen dem aktuellen un dem neuen Frame (in ms)
  10.     //|         aktuelle Zeit (zwischen 0 un 1000 ms)
  11.     //|         |           gibt an aller wieviel ms die FPS aktualisiert werden sollen
  12.     //|         |           |          Frames Per Second
  13.     fDeltaTime, fTimeCount, fInterval, fFPS: Single;
  14.     //Frequenz (zur errechnung der Zeit)
  15.     //|         Startpunkt der Messung (Zeit in ms)
  16.     //|         |           Endpunkt der Messung (Zeit in ms)
  17.     fFrequency, fStartTime, fStopTime: Int64;
  18.     //Event welches der Timer auslösen soll
  19.     fOnTimer: TOnTimerEvent;
  20.   public
  21.     property FPS      : Single        read fFPS;
  22.     property DeltaTime: Single        read fDeltaTime;
  23.     property Interval : Single        read fInterval  write fInterval;
  24.     property OnTimer  : TOnTimerEvent read fOnTimer   write fOnTimer;
  25.     procedure NewFrame;
  26.     constructor Create;
  27.   end;
  28.  
  29. ////////////////////////////////////////////////////////////////////////////////
  30. //berichtet dem Timer das ein neues Frame begonnen hat
  31. procedure TgluTimer.NewFrame;
  32. begin
  33.   QueryPerformanceCounter(fStopTime);
  34.   fDeltaTime := (fStopTime - fStartTime)/fFrequency*1000;
  35.   QueryPerformanceCounter(fStartTime);
  36.  
  37.   fTimeCount := fTimeCount + fDeltaTime;
  38.   Inc(fFrameCount);
  39.  
  40.   if fTimeCount >= fInterval then begin
  41.     //fFPS := fFrameCount * 1000/fInterval;
  42.     //fTimeCount := fTimeCount - fInterval;
  43.     fFPS := fFrameCount * 1000/fTimeCount;
  44.     fTimeCount := 0;
  45.     fFrameCount := 0;
  46.     if Assigned(fOnTimer) then
  47.       fOnTimer(self);
  48.   end;
  49. end;
  50.  
  51. ///////////////////////////////////////////////////////////////////////////////
  52. //erzeugt den Timer
  53. constructor TgluTimer.Create;
  54. begin
  55.   inherited Create;
  56.   fInterval  := 1000;
  57.   fTimeCount := 0;
  58.   QueryPerformanceFrequency(fFrequency);
  59.   QueryPerformanceCounter(fStartTime);
  60. end;


MfG Bergmann.

_________________
Aktuelle Projekte: BumpMapGenerator, Massive Universe Online
Auf meiner Homepage gibt auch noch paar Projekte und Infos von mir.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jul 02, 2012 02:48 
Offline
DGL Member

Registriert: Di Jun 12, 2012 21:26
Beiträge: 112
Programmiersprache: Delphi
Bergmann89 hat geschrieben:
Ich hab mir damals für das ganze Timebased-Movement ne Art Timer-Klasse programmiert.

Oder so wie Du es implementiert hast.

Wichtig ist, dass...
Code:
  1. QueryPerformanceCounter(QPCEnd);
  2. QueryPerformanceCounter(QPCStart); 
... direkt hintereinander stehen, also entweder beides am Anfang des OnIdle-Events oder beides am Ende des OnIdle-Events. Vermutlich entsteht beim erneuten Aufruf von OnIdle stets eine kleine Pause.

[Klugsch-Modus AN]Wobei ein einmaliges Aufrufen von QueryPerformanceCounter(QPCEnd) und ein Übergeben des Wertes von QPCEnd an QPCStart (sieh mein Beispiel) am genausten sein dürfte ;-) [Klugsch-Modus AUS].

In der Praxis spielt das jedoch keine Rolle. Dein Code funktioniert genauso gut :P

Gruß


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jul 02, 2012 05:53 
Offline
DGL Member
Benutzeravatar

Registriert: Do Sep 02, 2004 19:42
Beiträge: 4158
Programmiersprache: FreePascal, C++
subotai hat geschrieben:
[Klugsch-Modus AN]Wobei ein einmaliges Aufrufen von QueryPerformanceCounter(QPCEnd) und ein Übergeben des Wertes von QPCEnd an QPCStart (sieh mein Beispiel) am genausten sein dürfte ;-) [Klugsch-Modus AUS].

Das ist genau das vorgehen das ich hier schon mehrfach empfohlen habe… Und genau so steht es auch im Wiki-Artikel Timebased Movement. Weiß nich, wo du deine Fehlinformation her hast :)

grüße

_________________
If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung.
current projects: ManiacLab; aioxmpp
zombofant networkmy photostream
„Writing code is like writing poetry“ - source unknown


„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jul 02, 2012 07:48 
Offline
DGL Member

Registriert: Di Sep 07, 2010 14:28
Beiträge: 34
Wohnort: Frankfurt
Programmiersprache: C++,C#,VB(,Delphi)
Hallo allerseits,

ich habe in der msdn mal gelesen, das ihr, wenn ihr das benutzt, aufpassen müsst, mit Mehrkehrnprozessoren!
Dort kann es sein, das ihr, wenn der Thread auf einem anderen Kern ausgeführt wird, ein anderes Ergebnis bekommt, deshalb sollte man dafür sorgen, das der Aufruf immer vom selben Thread herkommt.

Stichwort:
SetThreadAffinityMask
MSDN http://msdn.microsoft.com/en-us/library/windows/desktop/ms686247%28v=vs.85%29.aspx

Und noch einmal den Text zu QueryPerformanceCounter
MSDN http://msdn.microsoft.com/en-us/library/windows/desktop/ms644904%28v=vs.85%29.aspx

Edit: Rechtschreibung :-)

Jonathan

_________________
Das mit dem Dx tut mir leid.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jul 02, 2012 08:57 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Das Thema kommt regelmäßig im Forum und regelmäßig kommen die gleichen Antworten.
Jetzt fehlt quasi nur noch eine Sache, dynamisches Takten.

Der Counter wird anhand der Frequenz Aufgelöst aber neben der Problematik, dass die Register der einzelnen Kerne angesprochen werden und somit schwankungen in der Messung auftreten(die Kerne starten quasi zur gleichen Zeit und haben nur minimale Abweichungen), kann es bei Mobile und aktuelleren CPU's auch noch viel schlimmer werden.
Die genannten Systeme können zur laufzeit die Kerne hoch und runter Takten, damit man Strom spart und entsprechend ändert sich dann auch die Genauigkeit und man bekommt Schwankungen.
Von daher sollte auch die Frequenz immer neu abgefragt werden und man sollte immer davon ausgehen, dass man schwankungen, FPS drops und so weiter hat.

Da kann man mit entsprechenden Techniken sehr soliden Code schreiben aber wie gesagt einfach mal im Forum suchen, dass Thema hatten wir schon einige male.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jul 02, 2012 13:20 
Offline
DGL Member
Benutzeravatar

Registriert: Do Sep 02, 2004 19:42
Beiträge: 4158
Programmiersprache: FreePascal, C++
Jonathan hat geschrieben:
Dort kann es sein, das ihr, wenn der Thread auf einem anderen Kern ausgeführt wird, ein anderes Ergebnis bekommt, deshalb sollte man dafür sorgen, das der Aufruf immer vom selben Thread herkommt.

Das war doch mal nen Problem auf AMD-CPUs, da haben die dann aber nen patch rausgebracht damit das nicht mehr passiert… Hat nämlich einige Spiele gebrochen ;).

TAK2004 hat geschrieben:
Die genannten Systeme können zur laufzeit die Kerne hoch und runter Takten, damit man Strom spart und entsprechend ändert sich dann auch die Genauigkeit und man bekommt Schwankungen.

Also meine CPU taktet wie wild, hab aber noch nie probleme mit den Countern gehabt. Weiß aber nicht wie identisch das linux clock_gettime zu QPC ist.

grüße

_________________
If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung.
current projects: ManiacLab; aioxmpp
zombofant networkmy photostream
„Writing code is like writing poetry“ - source unknown


„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jul 02, 2012 17:01 
Offline
DGL Member

Registriert: Di Jun 12, 2012 21:26
Beiträge: 112
Programmiersprache: Delphi
Lord Horazont hat geschrieben:
Weiß nich, wo du deine Fehlinformation her hast

Scrolle mal im DGL-Wiki Artikel zum Timebased Movement ganz nach unten (Stichwort Zeit0 und Zeit1). Oder habe ich etwas falsch verstanden?
Code:
  1.  
  2. procedure TForm1.IdleHandler(Sender: TObject; var Done: Boolean);
  3. begin
  4.  QueryPerformanceCounter(StartCount); //Zeit0
  5.  Form1.Render;
  6.  QueryPerformanceCounter(EndCount); //Zeit1  
  7.  Speedfactor := ((EndCount - StartCount) /Frequency) * Scalefactor //(Zeit1 - Zeit0) / Frequenz
  8.  [...]
  9. end;
  10.  


Außerdem habe ich diese Fehlinformation aus ein paar Tutorials auf DGL-Wiki. Gerade OpenGl-Anfänger wie ich übernehmen solche Dinge ganz gerne. Die Tutorials sind sehr gut, und dass dort nicht immer alles 100% perfekt sein kann, ist mir auch klar, und das erwarte ich auch nicht (ich bin kein Pedant). Ich dachte, dass es vielleicht nicht schlecht wäre, explizit auf dieses Problem hinzuweisen :-)

Viele Grüße


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jul 02, 2012 17:10 
Offline
DGL Member
Benutzeravatar

Registriert: Do Sep 02, 2004 19:42
Beiträge: 4158
Programmiersprache: FreePascal, C++
Lustig. Ein paar Abschnitte darüber steht es richtig. Magst das mal korrigieren?

grüße

_________________
If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung.
current projects: ManiacLab; aioxmpp
zombofant networkmy photostream
„Writing code is like writing poetry“ - source unknown


„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jul 02, 2012 17:34 
Offline
DGL Member

Registriert: Di Jun 12, 2012 21:26
Beiträge: 112
Programmiersprache: Delphi
Hm, ich versuch mal mein Glück. Dann aber bitte nochmal Korrektur lesen ;-) Hab's gerade geändert.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jul 02, 2012 18:31 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Lord Horazont hat geschrieben:
Jonathan hat geschrieben:
Dort kann es sein, das ihr, wenn der Thread auf einem anderen Kern ausgeführt wird, ein anderes Ergebnis bekommt, deshalb sollte man dafür sorgen, das der Aufruf immer vom selben Thread herkommt.

Das war doch mal nen Problem auf AMD-CPUs, da haben die dann aber nen patch rausgebracht damit das nicht mehr passiert… Hat nämlich einige Spiele gebrochen ;).

Da muss ich dich entäuschen, die Problematik gibt es immer noch, nur ne frage der syscalls.
Das Problem kann nur auftreten, wenn die prozesszeit überschritten wird oder ein interrupt durch eine hand voll syscalls erzeugt wird. In beiden fällen wird der Prozess pausiert und andere threads und prozesse dürfen nun drauf. Da in allen gängigen OS scheduler neue threads und prozesse mit thread affinity mask "all" gesetzt werden, kann auch mal passieren, dass der thread wo anders landet, wenn der beliebte kern gerade von ein anderen programmcode belegt wird und dies führt dazu, dass man mal die zeit von ein core und mal von einem anderem bekommt. Um dieses Problem zu beheben hilft kein AMD patch oder der kauf einer Intel CPU, sondern nur ein beschränken auf ein kern.

Es gab aber tatsächlich ein patch von AMD zu den counter aber der hat ein anderes Problem gekillt. Einige haben nicht die system funktion, sondern direkt auf rdtsc zugegriffen und so wurde vom os keine corezugehörigkeit erkannt und das problem auf dem AMD systemen war, dass jeder Core ein eigenes Powermanagment hatte und somit die counter unterschiedlich oft erhöht wurden. Der patch hat ein workaround geschaffen, der die abfrage der frequenz gleich mit gemacht hat und entsprechen ein korrekten wert errechnen kann. Eigentlich sollte man ja jedes mal, wenn man rdtsc verwendet den tsc prüfen. Quasi auf jeden queryperformancecounter die freq holen und dividieren.
So hatte ich das zumindestens aus den patch Infos gezogen.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 11 Beiträge ] 
Foren-Übersicht » DGL » Feedback


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 8 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
cron
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.101s | 17 Queries | GZIP : On ]