ich habe jetzt ewig die Suche gequält, aber niemand scheint exakt das gleiche Problem zu haben wie ich.
Daher stelle ich einfach meine Frage zu dem bekannten Thema "PerformanceCounter"
Der unten stehende Code ist ein Kernelausschnitt meiner kleinen Engine. Ich hatte vor ihr TBM einzuimpfen, nur leider bekomme ich auf 2 Rechnern unterschiedliche Ergebnisse.
Kurioserweise auf dem langsameren Rechner rast mir der LoopCounter davon, nach 15 Sekunden Laufzeit liegt mein Laptop vom LoopCount schon ca. 100ms vor meinem Desktop.
//Wieder Zeit holen, bewusst an dieser Stelle um Berechnungen mit in Dauer einzubeziehen.
QueryPerformanceCounter(rhEndTime);
//Testweise Ausgabe auf der Titelleiste des Fensters
rhGameForm.Caption:=IntToStr(rhGameLoops)+' '+
IntToStr(rhFPS)+' '+
IntToStr(rhStartTime)+' '+
FloatToStr(rhLoopCounter);
Done :=False;
End;
Tja es mag spät sein, vielleicht sehe ich auch den Wald vor lauter Bäumen nicht. Ich hoffe ihr könnt mir auf die Sprünge helfen.
Der LoopCounter in dieser Konstellation sollte doch auf jedwedem Rechner die gleiche Geschwindigkeit haben, oder irre ich?
Leider ist das nicht so, habe es mit einem Alpha-Fadeout eines Fullscreen-Polygons getestet. Auch mit der Rotation einer Linie.
Der langsamere Laptop ist dabei stets etwas schneller als mein Desktop.
Vielleicht sind ein paar technische Daten nützlich:
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Das holen der Zeit ist schon mal nicht sauber gelöst, du nimmst einen aktuellen Zeitwert und subtrahierst den von dem Wert, den du am Ende des letzten Durchgangs geholt hast, also tust du garnicht die Renderzeit sondern den Rest messen(was auch alles ins negative verlagert). Wenn du die Zeit vom Rendern haben willst, dann schiebe den End Counter nach SwapBuffers(). Die Berechnung von den Werten hast du völlig verkompliziert, durch den fall, dass du ständig negative werte hasst. die ganzen Abs können dann weg und CoreFrequency musst du vor dem Query(StartTime) machen, da auf notebooks der Prozessor ständig rauf und runter taktet und damit du die völlig falsche Frequenz hast, wenn du nicht jedes mal beim StartTime lesen diesen neu holst. Der Loopcounter ist auch ziemlich wakelig, ich empfehle dir definitiv für jedes Gebäude und co, welches eine bestimmte Zeit braucht jeweils den StartWert zu speichern,den benötigten Offset drauf zu addieren und dann mit dem diesen Wert einfach prüfen ob er kleiner als StartTime geworden ist. Dann hast du diesen Zeitpunkt erreicht und ist zumal wesentlich schonender für deine CPU(die ganzen Additionen und Divisionen von LoopCounter, sowie vergleiche fallen weg und bei Double ist dies wirklich sehr sehr teuer, wenn du es im loop hast).
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Okay, der Punkt des dynamischen CPU Taktes bei Notebooks leuchtet mir ein. Initial bei der Entwicklung meiner Engine hatte ich die Abfrage der Taktfrequenz auch noch in der Routine mit drin, allein mir schien es ein bisschen viel Overhead jedesmal diese Abfrage zumachen. Aber durch dein Argument ist es maximal sinnvoll und zwingend, dass ich es wieder reinhänge.
Zum LoopCounter:
Warum meinst du, dass der wackelig ist? Der HPC sollte doch so exakt sein, dass ich beim Inkrementieren eines Integers auf verschiedenen Rechnern synchrone Werte habe, oder nicht? Meine Idee war eigentlich von bekannten Strategiespielen adaptiert, wo man z.B. eine Artillerie-Unit anklickt und diese dann wegen mir 10sek braucht, um gebaut zu werden. Nun wollte ich aber nicht in groben Sekunden rechnen, sondern mir einen Gameloop erzeugen, quasi eine Timeline, an die ich Ereignisse und Aktionen anhängen kann. Das funktioniert alles auch super, nur dass eben das Notebook bisher schneller zählt als der Desktop
Ich werde aber gleich erst einmal deinen Hinweis mit der Taktfrequenz ändern, dann werde ich eine neue Messung machen und schauen ob dadurch beide Rechner nicht schon synchron laufen.
Man mag es mir bitte nachsehen, ich programmiere zwar leidenschaftlich gern, würde aber nie so weit gehen und mich einen guten Programmierer schimpfen
EDIT:
Also ich habe jetzt den Code nach deinen Hinweisen geändert. Resultat fatal.
Das Problem hierbei ist, dass die FPS absolut falsch sind. Die Schleife sollte nach exakt 1s immer wieder die FPS aktualisieren. Sie tut dies scheinbar aber erst nach 2-3s.
Hier ist der aktuelle Code:
Code:
Begin
// Startzeit holen
QueryPerformanceCounter(rhStartTime);
// OpenGL Zeugs
glClear(GL_COLOR_BUFFER_BIT Or GL_DEPTH_BUFFER_BIT);
Es MUSS an meinem Code liegen, denn ich habe den HPC in einer "leeren" OnIdle Methode getestet und dort bekomme ich korrekte Werte.
Nur wo liegt hier mein Fehler. Bitte helft, ich überseh es einfach seit Stunden.
Gruß, Killian
_________________ Die Antwort ist 17, aber wie lautet die Frage?
Registriert: Mo Sep 02, 2002 15:41 Beiträge: 867 Wohnort: nahe Stuttgart
Hallo,
ich glaube, das könnte damit zusammenhängen, dass andere Prozesse oder VSync(?) den Prozess "virtuell ausbremsen", er also fürs Zeichnen lediglich paar µs braucht, dann allerdings ungezählte Ewigkeiten außerhalb OnIdle abwartet.
Probier mal Folgendes, um die real ablaufende Zeit zwischen ein und demselben Punkt in einer Funktion zu messen:
Code:
// OpenGL Code
QueryPerformanceCounter(EndTime);
QueryPerformanceFrequency(Freq);
Delta :=(EndTime - StartTime)/ Freq;
// alle Berechnungen, die mit End/Start rechnen, bevor es verändert wird
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich habe mal ein bisschen gegoogelt und gefunden, dass ich 2 dinge verwechselt habe, der RDTSC hat das Problem mit der CPU Taktfrequenz und der Performance Counter hatte Probleme mit mehrkern Prozessoren. Das Problem wird üblicherweise durch Windowsupdates behoben bzw. kann dann mit einem Patch von AMD selber gelöst werden.
Die Frequenz bleibt das ganze Programm über immer gleich, laut MSDN also kann das doch wieder aus der Loop raus.
rhFrameDelta:double; mit aufnehmen, damit du auch timebase movement hast.
rhFramesCounter würde ich zu double machen.
rhFrameDelta=(double)(rhEndTime-rhStartTime)/rhCoreFrequency;
Bei der Division kommen Kommazahlen raus und mit der expliziten double konvertieren passiert das auch wirklich.
rhFramesCounter := rhFramesCounter + rhFrameDelta;
Entsprechend auch unten if rhFramesCounter>=1.0 then ändern
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Dieser explizite Typecast funktioniert nicht!
Auch ansonsten keine Änderung. Ich habe weiterhin das Problem, dass die FPS zu hoch sind, und der LoopCounter auf 2 Rechnern unterschiedlich schnell ist. Obwohl er Millisekunden genau gesteuert werden sollte.
Noch interessanter finde ich aber, dass die FPS manchmal gar nicht ausgegeben werden, da steht dann immer "0" da, oder sie funktioniert eine Weile, dann kommen Werte unter 100 raus usw usw.
Auch die Position des QueryPerformanceCounter(rhStartTime) innerhalb der Routine ist entscheidend. Steht sie am Anfang des Codes, dann kriege ich Werte für FPS.
Steht die Codezeile dagegen am Ende, kommt gar nichts bei raus. steht sie direkt vor der FrameDelta Berechnung, dann kommen wieder völlig utopische Werte raus.
Ich glaube fast, ich reiße das ganze Timing noch mal raus und setze es neu auf. Oder hat noch jemand eine Idee?
Wie gesagt, der HPC funktioniert in einer anderen Anwendung tadellos und auch exakt so wie ich es erwarte^^
Gruß, Killian
_________________ Die Antwort ist 17, aber wie lautet die Frage?
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2623 Wohnort: Berlin
Programmiersprache: Go, C/C++
Der aktuelle Code kann nur die Zeitdauer messen, die du für das Rendern eines Frames brauchst(nicht die gesammte Zeit eines Frames), du willst aber die gesammte Zeit für ein FPS counter also die Anzahl an Durchläufen pro Sekunde. Dazu brauchst du nur ein Read, wie es WhiteHunter gemacht hat.
Also einmal vor der schleife QueryPerformanceCounter(StartTime); und in der schleife nur noch QueryPerformanceCounter(EndTime); und darauf dann deine Berechnung sowie StartTime:=EndTime;. Das ganze machst du am Anfang der schleife, sonnst hast du ja auch Renderzeit und so von den aktuellen Frame mit drin und das ist ja nicht gewollt. Dann hast du einen FPS counter und Delta. Deine ZeitEvents kannst du nach EndTime holen alle notwendigen Objekte Informierst, dass der Wert sich verändert hat. Nun kann jedes Objekt entsprechend Reagieren oder einfach garnichts machen. Diese Objekte haben ja von Hause aus eine Zeitdauer, wielange etwas Dauert(z.B. Hausbau), du holst dir also beim auslösen von Hausbau die aktuelle EndTime, addierst deine Zeitdauer vom Objekt drauf und Speicherst diesen Wert im Objekt als SchlussZeit. Immer wenn das Update Event von der Loop kommt, mit der aktuellen EndTime, kannst du gucken ob es immer noch kleiner ist und wenn nicht, dann kannst du es aus der Liste entfernen, da du die z.B. 50sekunden Bauzeit erledigt hast. Das wäre die saubere OO variante aber Unperformant ohne Ende. Besser ist das verschieben dieser Prüfung in die Schleife, so dass z.B. eine Liste vor liegt, die ein Callback und eine Endzeit enthält, sobald diese überschritten ist ruft die schleife das Event auf und wirft den Eintrag aus der Liste. Dann fallen die ganzen Methodenaufrufe weg und das damit verbundene Registerarbeit.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
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.