Ich habe schon diverse kleine Ausgaben mit OpenGl progammiert, sowohl 3D als auch 2D-Darstellung. Dieses Forum hat mir dabei auch sehr geholfen. Momentan habe ich aber ein Problem, bei dem ich alleine nicht weiter komme. Es handelt sich um ein Programm, für das ich eine konstante, gleichmäßige Bewegung in 2D-Darstellung benötige. Dies habe ich per VSync realisiert und erhalte dadurch eine butterweiche Bewegung mit 60 FPS, soweit so gut.
Mein Problem ist nun, dass ich regelmäßig ca. 1 mal pro Sekunde in der fließenden Bewegung einen „Ruckler“ drin habe. Das Auftreten ist anscheinend unabhängig von der Geschwindigkeit des Rechners, ich habe es auch auf anderen Rechnern getestet. Die Ruckler treten außerdem abhängig von der verwendeten Ausgabegröße auf, im Vollbild eher als bei kleinen Fenstergrößen.
Die Ruckler lassen sich nur abstellen, wenn ich zu Beginn jeder Renderschleife ActivateRenderingContext(DC, RC) anstelle einfach nur wglMakeCurrent(DC, RC) aufrufe. Hierbei werden zusätzlich die ImplementationProperties und Extensions eingelesen. Letzteres verlangsamt mein Programm aber so sehr, dass ich auf großen Auflösungen (4K) keine ruckelfreie Ausgabe (dann 24 FPS) mehr hinbekomme.
Ich habe schon versucht, die Prozessorwechsel mittels SetProcessAffinityMask zu vehindern, kein Erfolg. Das Einfügen von kleinen Sleeps oder weiteren wglMakeCurrent vor oder nach SwapBuffers bringt teilweise Erfolge aber nicht sicher für alle Fenstergrößen.
Also zusammengefasst lautete meine konkrete Frage: Wie kann ich Auflösungs-unabhängig eine VSync-Ausgabe ohne Ruckler in 2D erreichen?
PS: Ich verwende Delphi7 und nVidia Grafikkarten, ich habe 2 Bildschirme, die aber beide mit 60 Hz in derselben Auflösung angesteuert werden. Das Problem tritt auch auf anderen Systemen mit nur einem Bildschirm auf. Ich habe mir erlaubt, einen Beispielcode anzufügen…
Grüße, Delfit
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
Normal ist das sicher nicht und den Zusammenhang mit Extensions verstehe ich auch nicht. Auch wenn es möglicherweise keine wirkliche Lösung für das Problem ist, rufe doch einfach "wglMakeCurrent" nicht mehr auf. Einmal am Anfang reicht doch völlig. Dann ist der Context aktiviert und solange du nicht mit mehreren Contexts arbeitest und im selben Thread nochmal einen anderen Context aktivieren willst, ist der wiederholte Aufruf von "wglMakeCurrent" doch total überflüssig.
Meine eigentliche Vermutung wäre, dass etwas ganz anderes schief läuft.
Außerdem verwendest du noch deprecated OpenGL, das kann unter neueren Grafikkarten möglicherweise auch ein Performanceproblem darstellen und ist nicht Zukunftssicher.
Danke für die schnelle Antwort! Das ich den Renderkontext nicht jedesmal aktivieren muss ist mir im Prinzip klar. Mein Programm soll jedoch 2 unabhängige Darstellungen liefern, daher muss ich jedesmal zwischen den Kontexten wechseln. Ich habe die Frage daher erstmal für die Verwendung von nur einem formuliert, weil der Effekt hier schon auftritt.
Warum der Aufruf von ActivateRenderingContext(DC, RC) das Ruckeln in allen Auflösungen behebt ist mir auch unklar. Ich könnte mir hier höchstens Seiteneffekte vorstellen, weil der Aufruf Zeit beansprucht oder das Lesen einer bestimmten Extension irgendwas im Treiber zurücksetzt...
Vielleicht hast du Recht und es hängt mit dem veralteten Code auf einer neuen Graka zusammen. Das würde bedeuten ich müsste alles in OpenGL3 schreiben. Ich hatte bisher vermutet, dass die "alten" OpenGl Befehle noch unterstützt verden, also in diesem Sinne nur alt, aber nicht "deprecated" sind, ich werde mich genauer informieren.
Leider gibt es noch kein Tutorial für den Einsteig von 0 auf OpenGl3 und viele andere Tutorials/Beispiele verwenden Code wie ich... Naja vielleicht schreibe ich ja selber eines, wenn ich durchblicke
Aufgrund der bisherigen Antwort müsste ich einen Test mit OpenGl3 machen, das werde ich auch mittelfristig tun. Da mein eigentliches Programm (Nicht der oben übertragene Test) jedoch schon relativ viele Kommandos (glBegin/End etc) hat wäre es schön, vielleicht doch noch eine Lösung für OpenGl < 3 zu finden.
Hat noch irgend jemand eine Idee, was für ein VSync ohne Ruckeln mit OpenGl < 3 noch zu beachten sein könnte?
Registriert: Mo Nov 08, 2010 18:41 Beiträge: 769
Programmiersprache: Gestern
Hi, ich habe mal den Code in meinen eigenen Fenster unter C getestet und alles lief tadellose. Daher würde ich spontan mal behaupten das onIdle hier Probleme macht.
Registriert: Di Jun 12, 2012 21:26 Beiträge: 112
Programmiersprache: Delphi
Das OnIdle Event war auch mein erster Gedanke, da ich damit selbst schon Probleme hatte. Aus eigener Erfahrung bin ich mir recht sicher, dass das OnIdle Event nicht regelmäßig aufgerufen wird und dass dadurch Ruckler entstehen. Abhilfe kannst Du schaffen, in dem Du entweder ein Timebased Movement verwendest (siehe Link von damadmax) oder eben kein Idle-Event. Den QueryperformanceCounter nur einmal im OnIdle aufrufen! Ich zitiere mich mal selbst :
Zitat:
Beim getrennten Aufruf des QueryPerformanceCounter für den Startwert und den Endwert vor und nach dem Rendern können gerade im OnIdle-Event unter Umständen erhebliche Abweichungen in der Zeitmessung auftreten.
Registriert: Mi Aug 14, 2013 21:17 Beiträge: 588
Programmiersprache: C++
subotai hat geschrieben:
Aus eigener Erfahrung bin ich mir recht sicher, dass das OnIdle Event nicht regelmäßig aufgerufen wird und dass dadurch Ruckler entstehen. Abhilfe kannst Du schaffen, in dem Du entweder ein Timebased Movement verwendest
Timebased Movement ist kein Mittel, um Ruckler zu vermeiden, sondern um Vorgänge auf verschieden schnellen Rechnern gleich schnell auszuführen. An einer konstanten Framerate führt kein Weg vorbei, wenn es nicht ruckelig aussehen soll. Siehe auch Framerate#Variation_der_Framerate.
_________________ So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)
Registriert: Di Jun 12, 2012 21:26 Beiträge: 112
Programmiersprache: Delphi
glAwesome hat geschrieben:
Timebased Movement ist kein Mittel, um Ruckler zu vermeiden, sondern um Vorgänge auf verschieden schnellen Rechnern gleich schnell auszuführen. An einer konstanten Framerate führt kein Weg vorbei, wenn es nicht ruckelig aussehen soll.
Man muss nicht alles glauben, was im Wiki steht. Laut Deiner Aussage wäre eine ruckelfreie Darstellung über das OnIdle-Event gar nicht möglich, da das OnIdle-Event keine konstante Framerate gewährleistet.
Registriert: Mi Aug 14, 2013 21:17 Beiträge: 588
Programmiersprache: C++
In diesem Fall glaube ich es, weil ich es selbst geschrieben habe. Mit dem OnIdle-Event kenne ich mich nicht aus, aber Fakt ist, dass nur konstante (und hohe) Frameraten absolut flüssig aussehen. Die Begründung steht ja auch im Wiki-Artikel.
_________________ So aktivierst du Syntaxhighlighting im Forum: [code=pascal ][/code], [code=cpp ][/code], [code=java ][/code] oder [code=glsl ][/code] (ohne die Leerzeichen)
Registriert: Mo Sep 23, 2002 19:27 Beiträge: 5812
Programmiersprache: C++
Aus persönlicher Erfahrung würde ich die Finger vom OnIdle-Event weglassen, es gibt keine Garantie dass da nicht irgendein Prozess "reinpfuscht" und dafür sorgt dass man zwischen zwei OnIdle-Events unterschiedliche Zeitsprünge hat. Alles schon live und in Farbe selbst erlebt (auf eigenen und fremden Rechnern).
Von daher ganz klar eigene Renderschleife nach dem Erzeugen des Renderkontextes starten, in der gerendert wird bis eine Abbruchbedingung gegeben ist (z.B. Quit = True) und die am Ende eines jeden Durchganges Application.ProcessMessages aufruft :
Code:
repeat
QueryPerformanceCounter(QPCs);
Render;
UpdteEnvironment(dT);
UpdatePhysics(dT);
UpdateAudio(dT);
Application.ProcessMessages;
QueryPerformanceCounter(QPCe);
dT := ...
until Quit
Quit setzt man dann z.B. (wenn man die VCL nutzt) im CanClose des Formulars.
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Sascha ist vollkommen zuzustimmen. Das OnIdle ist hochgradig frickelig. Selbst mausbewegungen habe ich da schon einfluss nehmen sehen.
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 network • my 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
Bei meinem Tool handels es sich um eine art Monitor-Testprogramm. Die Grundlage des Programmes (also das Problem dieses Threads) ist die Aufgabe, eine Abfolge von exakt definierten Frames hintereinander ausgeben zu können. Das teste ich zur Zeit unter anderem mit dem angegebenen Beispielprogramm mit den Balken, bei denen man eine Abweichung vom sanften Scroll sofort durch "Ruckeln" erkennt. Das Ruckeln entsteht hier nicht durch eine zu niedrige Framerate, sondern falls die Bewegung nicht immer genau einen Pixel (das nächste Frame) ausgibt, sondern auch mal 0 oder 2 Pixel pro Frame, falls bei der Ausgabe Frames verdoppelt oder übersprungen werden.
Eine Ausgabensteuerung mittels „timebased movement“ fällt daher flach, da das Timing der Ausgabe nicht auf einen internen Hardware/Software-Timer, sondern wirklich auf die Bildausgabe selbst, also auf das VSync synchronisiert werden soll. Die Geschwindigkeit der Szenen-Änderung richtet sich dementsprechend rein nach der Bildwiederholfrequenz.
Ich habe beobachtet, dass die Ruckler unterschiedlich stark auftreten je nach Ausgabemonitor oder Fenster/Vollbild Darstellung: * Bei der Ausgabe auf nur einem 60-Hz Monitor im Vollbild läuft alles perfekt. * Im Fenstermodus dagegen ruckelt es gleichmäßig. * Bei Anschluss eines 2. (baugleichen) Monitores ist eine ruckelfreie Ausgabe auf keinem Bildschirm im Vollbild/Fenster zu realisieren. * Bei Anschluss eines 2. Monitores mit 120 Hz ruckelt die Ausgabe auf diesem extrem aber es werden nachweislich Frames unterschlagen. Trotzdem zeigt mein inzwischen implementierter Framecounter exakt 120 FPS an... * Bei Anschluss nur des 120 Hz Monitores ist alles wieder perfekt, aber nur im Vollbild. Hier kann ich exakt 120 definierte Bilder pro Sekunde übergeben, z.B. wird ein weiss/schwarz Wechsel als ruhiges, gleichmäßiges und stehendes grau wiedergegeben.
Bei allen Konfigurationenen außer einem einzelnen Monitor im Vollbild flackert ein derartiges Szenario unruhig und wechselt zwischen längeren Weiss- und Schwarzphasen, als ob nur jeder 2. Frame auch übertragen wird. Der Framecounter und der Monitor zeigen aber exakt die zu erwartenen Bildfrequenz.
Mein Eindruck ist, dass zum einen dem OpenGl Treiber nicht klar ist, auf welchen(!) Monitor er sich synchronisieren soll. Zum anderen scheinen auch bei regelmäßigen SwapBuffers mit korrekter Frequenz nicht alle Frames auch tatsächlich an der Grafikausgabe zum Monitor aktualisiert zu werden.
Zum Thema OnIdle: Meine Renderschleife ist extrem kurz und verbraucht sehr wenig Rechenzeit. Ich sehe aber die Problematik der Zuverlässigkeit des Auftretens von OnIdle auch abhängig von der Hintergrundaktivität (Files öffnen, Virenscanner, Netzwerk etc.). Ich habe daher bereits die Prozesspriorität erhöht, aber ohne Wirkung.
Den Vorschlag, eine Hauptschleife zu verwenden und die Renderschleife mit ProcessMessages zu kombinieren finde ich reizvoll, allerdings stellt sich mir hier eine Frage: Die Schleife wird ja nach Einsprung bis zum "Quit" nicht mehr verlassen... Wie springe ich sie also an? Wenn ich sie z.B. über einen Button starte, dann kommt ja die Behandlungsfunktion des Buttons nicht mehr zu Windows zurück... Führt der interne Aufruf von ProcessMessages dann nicht zu Verwirrung für das OS? Und wenn ich einen separaten Thread erstelle, kann ich in diesem dann ProcessMessages aufrufen? Ich denke an die Thread-Synchronisierung...
Ich hoffe, das war jetzt alles nicht zu technisch, letztlich möchte ich "nur" ein zuverlässiges vsync im 2.Modus...
Registriert: Mo Nov 08, 2010 18:41 Beiträge: 769
Programmiersprache: Gestern
Delfit hat geschrieben:
Den Vorschlag, eine Hauptschleife zu verwenden und die Renderschleife mit ProcessMessages zu kombinieren finde ich reizvoll, allerdings stellt sich mir hier eine Frage: Die Schleife wird ja nach Einsprung bis zum "Quit" nicht mehr verlassen... Wie springe ich sie also an? Wenn ich sie z.B. über einen Button starte, dann kommt ja die Behandlungsfunktion des Buttons nicht mehr zu Windows zurück... Führt der interne Aufruf von ProcessMessages dann nicht zu Verwirrung für das OS? Und wenn ich einen separaten Thread erstelle, kann ich in diesem dann ProcessMessages aufrufen? Ich denke an die Thread-Synchronisierung...
Ich hoffe, das war jetzt alles nicht zu technisch, letztlich möchte ich "nur" ein zuverlässiges vsync im 2.Modus...
Grüße Delfit
Hi,
Also da ja deine Frames exakt definiert sind kannst du ganz einfach folgendes machen:
Code:
int iframe = MAXIFRAME; //zähler für die frames
button_click... //click event
iframe = 0; //zähler auf "start" setzen
...
repeat... //deine Render Schleife
wenn iframe < MAXIFRAME dann //irgendwo in deiner Schleife
zeige_frame(iframe); //zeige den aktuellen frame
iframe = iframe + 1 //gehe zum nächsten frame
ende wenn
...
until...
Du könntest aber auch einfach den Button deaktivieren solange deine Schleife läuft. Oder nen Userthread erzeugen..... Ich lass deiner Kreativität mal freien Lauf
Ich habe mitlerweile einen mehr schlecht als rechten Workaround gefunden: Sobald man Aero ausschaltet (anderes Theme wählen) funktioniert alles sauber und ohne Ruckler ohne zusätzliche Tricks oder Kniffe. Das Problem liegt vielleicht nicht so sehr an OpenGl sondern an der Interaktion mit Windows...
Mitglieder in diesem Forum: 0 Mitglieder und 85 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.