Direct3D9 & Fenster verdeckt = PresentationInterval funktioniert nicht?



  • Ich hab' ein Programm dass Direct3D9 verwendet, und zwar im Windowed Mode.
    PresentationInterval ist auf 1 gesetzt.

    So lange das Fenster sichtbar ist geht auch alles wunderbar, allerdings wenn das Fenster entweder hidden ist oder ganz verdeckt, dann scheint das PresentationInterval nicht zu greifen. D.h. die Framerate steigt auf etliche hundert Frames/sec.

    Gleichzeitig beginnt das Programm dann das restliche System stark auszubremsen, und zwar lustigerweise mehr als mit PresentationInterval=0 und sichtbarem Fenster.

    Ich nehme an dieses Phänomen wird bekannt sein.
    Frage: Wie geht man damit um, wie verhindert man dass die D3D Applikation das restliche System so ausbremst.

    Im Moment prüfe ich jetzt mit timeGetTime() die Abstände zwischen den einzelnen Presen() Aufrufen, und rufe Sleep() auf falls die Zeit < 8 msec liegt. Das funktioniert einigermassen, allerdings is es ein Workaround den ich eigentlich gerne durch was besseres ersetzen würde.

    ----

    Eine andere Sache die mir gerade einfällt: sobald irgendwelche GUI Effekte mit Alpha-Blending von anderen Programmen dargestellt werden, fängt alles gröber zu ruckeln an. Also z.B. wenn so eine "Balloon Help" Blase in einem Windows-Dialog eingefadet wird.

    Ein paar Infos zum System die vielleicht relevant sein könnten:
    API = Direct3D 9, wie schon erwähnt im Windowed Mode
    OS = Windows XP SP2/SP3
    CPU = Intel Cure2 Duo/AMD Athlon X2
    Grafik = Radeon HD 4650

    Die beschriebenen Effekte treten sowohl auf dem Core2/XP-SP3 als auch auf dem Athlon/XP-SP2 System auf.



  • hustbaer schrieb:

    So lange das Fenster sichtbar ist geht auch alles wunderbar, allerdings wenn das Fenster entweder hidden ist oder ganz verdeckt, dann scheint das PresentationInterval nicht zu greifen. D.h. die Framerate steigt auf etliche hundert Frames/sec.

    Auch wenn ich das auf den ersten Blick nirgendwo explizit dokumentiert gesehen hab kann man wohl annehmen dass Present() eben nur wartet wenn es auch was zu presenten gibt. Spätestens in DXGI hat Present() einen eigenen Returnwert der angibt dass alles verdeckt ist (dort wird laut Doku gewartet auf den VSYNC des Displays auf dem der größte Anteil des sichtbaren Client-Bereichs des Fensters liegt). Es ist zwar auch dort auf den ersten Blick nicht dokumentiert ob die Funktion trotzdem wartet, aber wenn du mich fragst macht es keinen Sinn auf die VSYNC zu warten wenn es gar nichts zu presenten gibt, also würd ich auch nicht davon ausgehen dass die Runtime das tut. Imo könnte man da sogar ziemlich drüber diskutieren ob es überhaupt eine sinnvolle Antwort auf die Frage was genau der VSYNC für ein unsichtbares Fenster sein soll geben kann...

    hustbaer schrieb:

    Im Moment prüfe ich jetzt mit timeGetTime() die Abstände zwischen den einzelnen Presen() Aufrufen, und rufe Sleep() auf falls die Zeit < 8 msec liegt. Das funktioniert einigermassen, allerdings is es ein Workaround den ich eigentlich gerne durch was besseres ersetzen würde.

    Naja, eigentlich würd ich das weniger als Workaround sehen, denn wenn man nicht ständig 100% CPU Auslastung haben will (und wann will man das schon, erst recht in ner Fensteranwendung) wird man seine Renderloop genau so aufbauen!? Wenn deine Anwendung nicht gerade irgendwas extrem interaktives is (Spiel oder so) dann würd ich mir überlegen ob ich überhaupt eine Renderloop brauch oder nicht einfach nur render wenn auch gerendert werden muss (WM_PAINT).



  • Die Anwendung ist die Oberfläche einer Video-Jukebox (Kastl zum im Pub an die Wand hängen, *keine* Endkunden Software), und ist vollständig mit D3D aufgebaut (abgesehen vom DirectShow Teil der die Videos spielt und natürlich diversen Libs die intern verwendet werden wie GDI+ zum Bilder laden etc.).

    D.h. die GUI bedeckt im Produktivbetrieb immer den gesamten Schirm. Ist also kein Spiel, aber nicht weit entfernt. Im Produktivbetrieb gibt's auch kein grosses Problem, nur beim Entwickeln/Testen nervt es leicht.

    Das Video selbst wird zwar (derzeit noch) vom VMR9 im Windowless Mode selbst gezeichnet, von daher wäre es nicht nötig permanent alles upzudaten, wenn sich am Schirm sonst nichts tut. (Das ist auch der Hauptgrund warum wir nicht D3D Fullscreen fahren, denn damit kommt der VMR9 Windowless soweit ich weiss nicht klar. Wenn's die Zeit erlaubt stellen wir vielleicht mal auf Renderless um - mal sehen.)

    Allerdings gibt es schon etliche Stellen die wirklich extrem flüssig laufen sollen, z.B. das Scrollen von diversen Listen wie man es vom iPhone oder auch von Windows-Phone 7 kennt.

    Aus dem Grund, und weil die Software sowieso den ganzen PC "für sich alleine" hat, wurde die GUI auch von Anfang an auf "immer neu zeichnen" ausgelegt. (Und natürlich weil es die Entwicklung des GUI Frameworks etwas vereinfacht hat.)

    Naja, eigentlich würd ich das weniger als Workaround sehen, denn wenn man nicht ständig 100% CPU Auslastung haben will (und wann will man das schon, erst recht in ner Fensteranwendung) wird man seine Renderloop genau so aufbauen!?

    Naja... ein Workaround ist es für mich deswegen, weil ich selbst über einen Umweg (timeGetTime()) "raten" muss, ob das PresentationInterval eingehalten wurde oder nicht.
    Wenn mir Present() ein Flag in einem Output-Parameter setzen würde, wäre mir das viel lieber.

    ----

    Hast du vielleicht eine Vermutung warum der von mir beschriebene Effekt bei Balloon-Help Bubbles auftritt? Das Ruckeln ist dann wirklich extrem, von 60fps runter auf 3-4...und z.T. stockt sogar die Sound-Ausgabe.
    Ist zwar auch kein Problem im Produktivbetrieb, aber mich würde einfach interessieren was da so abgeht.



  • hustbaer schrieb:

    Naja... ein Workaround ist es für mich deswegen, weil ich selbst über einen Umweg (timeGetTime()) "raten" muss, ob das PresentationInterval eingehalten wurde oder nicht.

    Nunja, du kannst ja testen ob das Fenster sichtbar ist oder im Vordergrund oder so. Present() selbst liefert in D3D9 jedenfalls offenbar leider noch keinen entsprechenden Returncode (gibts erst in D3D9ex, aber du kannst ja mal schaun, vielleicht gibts S_D3DPRESENTATIONOCCLUDED ja doch auch schon in D3D9). Aber eigentlich tust du da ja nicht raten ob das PresentationInterval eingehalten wurde sondern du limitierst deine FPS auf einen Maximalwert und gibst für etwaige übrige Zeit die CPU ab, was eine imo vernünftige Sache und afaik gängige Praxis ist...

    hustbaer schrieb:

    Hast du vielleicht eine Vermutung warum der von mir beschriebene Effekt bei Balloon-Help Bubbles auftritt? Das Ruckeln ist dann wirklich extrem, von 60fps runter auf 3-4...und z.T. stockt sogar die Sound-Ausgabe.
    Ist zwar auch kein Problem im Produktivbetrieb, aber mich würde einfach interessieren was da so abgeht.

    Sry da kann ich nur raten, vielleicht liegts an der zu hohen CPU Auslastung!? :p
    Klingt jedenfalls komisch.



  • dot schrieb:

    (gibts erst in D3D9ex, aber du kannst ja mal schaun, vielleicht gibts S_D3DPRESENTATIONOCCLUDED ja doch auch schon in D3D9).

    Hab nach deinem ersten Beitrag schon geguckt ob irgendwas ausser 0 (S_OK) zurückkommt: nope, immer schön 0, auch wenn das Fenster komplett verdeckt ist. Trotzdem danke, war ein guter Tip (wenn auch daneben *g*) 🙂

    Dabei bin ich auch draufgekommt dass das Problem bei einem "hidden" Fenster selbstgebaut war, denn da ruf' ich Present() gleich gar nicht auf (weil das Destination-Rect "degenerated-or-empty" wird, und das check' ich vor dem Present()).

    Das Verhalten bei "occluded" ist allerdings wie beschrieben, und ... naja, ich lass dann man den Frame-Rate-Limiter drinnen.

    Aber eigentlich tust du da ja nicht raten ob das PresentationInterval eingehalten wurde sondern du limitierst deine FPS auf einen Maximalwert und gibst für etwaige übrige Zeit die CPU ab, was eine imo vernünftige Sache und afaik gängige Praxis ist...

    Naja... kann man so oder so sehen.
    Ich limitier das derzeit jetzt mit 8 msec. Was aber irgendwie doof ist, da je nach Bildschirminhalt mehr als 8 msec gebraucht werden. Der Wert ist zugegebenermassen sehr konservativ gewählt, bei ~16.66 msec pro Frame (@60Hz). Ich könnte mal 12 einstellen, aber viel weiter trau ich mich nicht raufgehen, denn ich will nicht riskieren auf Grund von Ungenauigkeiten bei der Zeitmessung (timeGetTime() ist ja nicht wirklich auf die Millisekunde genau) die Software künstlich zum "Stottern" zu bringen.

    hustbaer schrieb:

    Hast du vielleicht eine Vermutung warum der von mir beschriebene Effekt bei Balloon-Help Bubbles auftritt? Das Ruckeln ist dann wirklich extrem, von 60fps runter auf 3-4...und z.T. stockt sogar die Sound-Ausgabe.
    Ist zwar auch kein Problem im Produktivbetrieb, aber mich würde einfach interessieren was da so abgeht.

    Sry da kann ich nur raten, vielleicht liegts an der zu hohen CPU Auslastung!? :p
    Klingt jedenfalls komisch.

    Welche hohe CPU-Auslastung? 🙂 Die GUI braucht ca. 20% (40% eines Cores) und dann läuft noch ein Prozess der nochmal ca. 13% (26% eines Cores) braucht.
    Wobei mir noch nicht so ganz klar ist warum der 2. Prozess so viel CPU Zeit weglutscht, aber das ist mir erstmal egal.



  • hustbaer schrieb:

    [...] denn ich will nicht riskieren auf Grund von Ungenauigkeiten bei der Zeitmessung (timeGetTime() ist ja nicht wirklich auf die Millisekunde genau) die Software künstlich zum "Stottern" zu bringen.

    Du kannst ja einen besseren Timer verwenden 😉



  • MSDN geht bei mir grad nicht - vermutlich irgend ein Problem mit den MS Servern für Österreich?

    Aber ich schätze der Link wird auf QueryPerformanceCounter verweisen, und davon halte ich mich fern so lange es geht.

    p.S.: einen wirklich guten Timer gibt's ja erst ab Windows 7. Obwohl ausprobiert hab' ich den noch nicht, aber was man in der Doku so liest klingt zumindest nicht schlecht...



  • hustbaer schrieb:

    MSDN geht bei mir grad nicht - vermutlich irgend ein Problem mit den MS Servern für Österreich?

    Also ich sitz im schönen Graz und hab kein Problem mit MSDN 😉

    hustbaer schrieb:

    Aber ich schätze der Link wird auf QueryPerformanceCounter verweisen, und davon halte ich mich fern so lange es geht.

    Richtig geschätzt, warum willst du dich denn unbedingt davon fernhalten!?

    hustbaer schrieb:

    p.S.: einen wirklich guten Timer gibt's ja erst ab Windows 7. Obwohl ausprobiert hab' ich den noch nicht, aber was man in der Doku so liest klingt zumindest nicht schlecht...

    Ich bin ganz Ohr, wie heißt denn das Gute Stück? 🙂

    Meinst du vielleicht dieses Animation Zeug: http://msdn.microsoft.com/en-us/library/dd756787.aspx!?
    Klingt auf jeden Fall interessant, schaut aber auf den ersten Blick irgendwie 1:1 nach dem System aus was WPF verwendet. Wenn dem so ist dann ist das ja wohl eher eine ziemliche Highlevel Geschichte und nicht nur ein einfacher Timer!?



  • @dot
    Google-Cache sei Dank: http://msdn.microsoft.com/en-us/library/ee662307(v=vs.85).aspx
    Hatte das allerdings falsch in Erinnerung - die Auflösung ist auch nur so gut wie über timeBeginPeriod() eingestellt. Dafür bekommt man endlich mal nen 64 Bit Wert.

    p.S.: im Kernel gibt's den Timer mit 64 Bit eh schon ewigkeiten, aber MS hat es bisher anscheinend nicht für nötig gehalten den mal im Usermode zugänglich zu machen.

    Richtig geschätzt, warum willst du dich denn unbedingt davon fernhalten!?

    Weil ich gerne Timer verwende die ich problemlos verwenden kann. Um GetTickCount() oder timeGetTime() kann man sich einen Wrapper schreiben der aus den etwas mageren 32 Bit 64 Bit macht. Ist zwar lästig, und wie gesagt könnte die Auflösung besser sein, aber dafür hat men einen Timer der wirklich sehr stabil läuft.

    QPC() dagegen ist ... eigen. Es driftet, und je nach Hardware springt es auch mal - auch mal gerne zurück. Oder bleibt kurz stehen.
    (Mit driften meine ich, die Langzeit-Genauigkeit ist schlecht)

    Mit aktuellen Chipsets/CPUs vermutlich kein schlimmes Problem mehr, aber naja, ich hab einfach irgend wann mal beschlossen dass es besser ist sich davon fernzuhalten, und daran halte ich mich bisher.

    Was man machen kann, ist QPC() und timeGetTime() zu kombinieren, aber das ist mir zu viel Aufwand. (Ich hab' noch keine fertige Implementierung, und bisher hat die Auflösung von timeGetTime() noch immer gereicht)

    Gibt auch einige Artikel zu dem Thema, u.a. aus der Spiel-Programmierer-Szene, wo mehr oder weniger genau das beschrieben wird. Also mit timeGetTime() + QPC() einen Timer basteln der sozusagen das Beste aus beiden Welten vereint: gute Auflösung ohne Sprünge und mit gleichzeitig guter Langzeit-Genauigkeit.



  • Ok der Timer war mir neu. Von timeBeginPeriod() würd ich mich dagegen wirklich auf jeden Fall fernhalten 😉

    hustbaer schrieb:

    QPC() dagegen ist ... eigen. Es driftet, und je nach Hardware springt es auch mal - auch mal gerne zurück. Oder bleibt kurz stehen.
    (Mit driften meine ich, die Langzeit-Genauigkeit ist schlecht)

    Natürlich, das sind alles bekannte Verhaltensweisen (die Sprünge hängen afaik normal mit hohem PCI Bus Traffic zusammen, ich hatte noch nie wirklich ein Problem damit und wär mir nicht so sicher ob die Sache im aktuellen Jahrtausend überhaupt noch Relevanz hat). Und nichts davon spielt hier wirklich eine Rolle denn du willst nur kurze Zeitdifferenzen möglichst genau messen, also genau das wofür QPC gedacht ist!? Wer QPC als absolute Uhr verwendet ist selber schuld^^

    Hier gibts nen guten Artikel dazu (Google Cache ftw da gamedev.net grad umbaut)



  • dot schrieb:

    Ok der Timer war mir neu. Von timeBeginPeriod() würd ich mich dagegen wirklich auf jeden Fall fernhalten 😉

    Äh. Hast du mal ausprobiert wie hoch der Impact ist?
    Ich würde sagen: im aktuellen Jahrtausend hat das keine Relevanz mehr.
    Davon abgesehen macht D3D9 im Windowed Mode mit PRESENTATION_INTERVAL_ONE - laut Doku zumindest - intern sowieso ein timeBeginPeriod(), vermutlich mit period = 1. Von daher ... egal 🤡

    hustbaer schrieb:

    QPC() dagegen ist ... eigen. Es driftet, und je nach Hardware springt es auch mal - auch mal gerne zurück. Oder bleibt kurz stehen.
    (Mit driften meine ich, die Langzeit-Genauigkeit ist schlecht)

    Natürlich, das sind alles bekannte Verhaltensweisen (die Sprünge hängen afaik normal mit hohem PCI Bus Traffic zusammen, ich hatte noch nie wirklich ein Problem damit und wär mir nicht so sicher ob die Sache im aktuellen Jahrtausend überhaupt noch Relevanz hat). Und nichts davon spielt hier wirklich eine Rolle denn du willst nur kurze Zeitdifferenzen möglichst genau messen, also genau das wofür QPC gedacht ist!? Wer QPC als absolute Uhr verwendet ist selber schuld^^

    Vielleicht hab' ich das noch nicht ausreichend klar rübergebracht: ich verwende ungern Dinge, wo ich mir etwas Gedanken machen muss, was ich für "unnötig" halte. Und bei einem Timer halte ich es für unnötig sich über eventuelle Sprünge Gedanken machen zu müssen, bzw. bei einem "general-purpose" Timer über schlechte Langzeit-Genauigkeit.

    Die Langzeit-Genauigkeit wäre im Fall Frame-Zeiten messen zwar egal, aber die Sprünge wären doof. Von daher lieber timeGetTime().



  • hustbaer schrieb:

    Äh. Hast du mal ausprobiert wie hoch der Impact ist?

    Um ehrlich zu sein nicht weil ich timeGetTime() praktisch nie verwende 🤡
    Afaik verändert timeBeginPeriod() den Heartbeat des Schedulers, wenn du mich fragst ist es im aktuellen Jahrtausend sehr viel eher relevant wenn ich generell die Anzahl der Context Switches pro Sekunde drastisch erhöhe (wär das vielleicht ein möglicher Grund für dein ruckelndes Tooltip!?) als wenn ich auf einigen wenigen älteren Rechnern vielleicht jumps aufgrund von Hardware bugs habe mit denen man noch dazu relativ leicht umgehen kann (Zeitdifferenz begrenzen).

    hustbaer schrieb:

    Davon abgesehen macht D3D9 im Windowed Mode mit PRESENTATION_INTERVAL_ONE - laut Doku zumindest - intern sowieso ein timeBeginPeriod(), vermutlich mit period = 1. Von daher ... egal 🤡

    Richtig, dafür gibts D3DPRESENT_INTERVAL_DEFAULT 😉



  • wär das vielleicht ein möglicher Grund für dein ruckelndes Tooltip!?

    Wenn du das ernst meinst, dann ... eueueu.
    Nein, das ist ganz sicher nicht der Grund.
    Glaub mir, ich habe ausreichend Erfahrung mit timeBeginPeriod(), und es spielt überhaupt keinen Walzer.
    Ich kann dir keine genauen Zahlen sagen, aber ich weiss aus Erfahrung dass die Systemleistung dadurch nicht wesentlich sinkt.
    Mag sein dass Berechnungen in engen Schleifen dadurch um 2-3% langsamer werden, aber für unsere Anwendungen spielt das wirklich keine Rolle.

    Richtig, dafür gibts D3DPRESENT_INTERVAL_DEFAULT 😉

    Weiss ich. Was meinst du warum ich das nicht verwende?



  • hustbaer schrieb:

    Wenn du das ernst meinst, dann ... eueueu.

    Naja was nur so ein spontaner Gedanke, was besseres ist uns immerhin noch nicht eingefallen...

    hustbaer schrieb:

    Glaub mir, ich habe ausreichend Erfahrung mit timeBeginPeriod(), und es spielt überhaupt keinen Walzer.

    Nachdem ich selber keine Erfahrung damit hab glaub ichs dir, ich versuch ja auch nicht dir QPC() zu verkaufen, ich fand nur die Diskussion interessant weil bei diesen timing Geschichten ist am Ende irgendwie immer ein wenig Schwarze Magie im Spiel hab ich den Eindruck 😉


Anmelden zum Antworten