2014-10-02 1 views
14

Ich zeichne eine Animation mit Double-buffered GDI auf einem Fenster, auf einem System, wo DWM-Komposition aktiviert ist, und deutlich sichtbar tearing auf dem Bildschirm zu sehen. Gibt es einen Weg dies zu verhindern?Ist es möglich, reißende Artefakte beim Zeichnen mit GDI in einem Fenster mit DWM-Komposition zu verhindern?

Einzelheiten

Die Animation nimmt das gleiche Bild, und bewegt es nach rechts über den Bildschirm nach links; Die Anzahl der Pixel wird durch die Differenz zwischen der aktuellen Zeit und dem Beginn der Animation und der Zeit bis zum Ende bestimmt, um einen vollständigen Bruch zu erhalten, der auf die gesamte Fensterbreite angewendet wird, unter Verwendung von timeGetTime mit einer 1ms resolution. Die Animation zeichnet eine Schleife ohne Verarbeitung von Anwendungsnachrichten; Es ruft die (VCL-Bibliotheks-) Methode Repaint auf, die intern ungültig wird und dann UpdateWindow für das fragliche Fenster aufruft und direkt in die Nachrichtenprozedur mit WM_PAINT aufruft. Die VCL-Implementierung des Mal-Handlers verwendet BeginBufferedPaint. Die Malerei ist selbst doppelt gepuffert.

Ziel ist eine möglichst hohe Framerate, um eine flüssige Animation über den Bildschirm zu erreichen. (Die Zeichnung verwendet Double-Buffering, um Flimmern zu entfernen und sicherzustellen, dass ein ganzes Bild oder ein Frame gleichzeitig auf dem Bildschirm angezeigt wird. Es wird ungültig und aktualisiert direkt durch Aufrufen der Nachrichtenprozedur, ohne dass andere Nachrichten verarbeitet werden. zB BeginBufferedPaint) für die Aero-Komposition.) Dabei wird in ein paar BitBlt-Aufrufen gemalt (eine für die linke Seite der Animation, dh was sich außerhalb des Bildschirms bewegt, und eine für die rechte Seite der Animation, also was sich auf dem Bildschirm bewegt).)

Beim Betrachten der Animation ist deutlich sichtbar tearing. Dies tritt unter Windows Vista, 7 und 8.1 auf mehreren Systemen mit unterschiedlichen Grafikkarten auf.

Mein Ansatz, um dies zu handhaben, war es, die Geschwindigkeit, mit der es zeichnet, zu reduzieren oder zu versuchen, auf VSync zu warten, bevor Sie erneut malen. Dies könnte der falsche Ansatz sein, daher könnte die Antwort auf diese Frage lauten: "Mach etwas anderes komplett: X". Wenn ja, toll :)

(Was ich wirklich möchte, ist eine Möglichkeit, die DWM zu bitten, zu/compose verwenden nur voll gemalt Rahmen für diesen speziellen Fenster.)

Ich habe folgende Ansätze versucht von denen keines alle sichtbaren Risse entfernt. Daher ist die Frage, Ist es möglich, zu vermeiden, reißen, wenn DWM-Komposition verwenden, und wenn ja wie?

Ansätze versucht:

  • den Monitor Bildwiederholfrequenz über GetDeviceCaps(Application.MainForm.Handle, VREFRESH) bekommen; Sleeping für 1/Aktualisierungsrate Millisekunden. Etwas verbessert über die Malerei so schnell wie möglich, kann aber Wunschdenken sein. Perzeptuell etwas weniger glatte Animationsrate. (Tweaks: normal Sleep und eine hochauflösende Spin warten timeGetTime verwenden.)

  • DwmSetPresentParameters zu versuchen, auf die gleiche Rate zu begrenzen, die Aktualisierung auf dem der Code zieht. (Variationen: viele Puffer (cBuffer = 8) (kein sichtbarer Effekt); Spezifizieren einer Quellrate der Monitoraktualisierungsrate/1 und Schlafen unter Verwendung des obigen Codes (das gleiche wie das Versuchen des Schlafens); Spezifizieren einer Aktualisierung pro Bild von 1, 10 usw. (kein sichtbarer Effekt); Ändern der Quellbildabdeckung (kein sichtbarer Effekt.

    )
  • Mit DwmGetCompositionTimingInfo in einer Vielzahl von Möglichkeiten:

      • Während cFramesPending> 0, Spin;
      • Get cFrame (Rahmen zusammengesetzt) ​​und Spin, während diese Zahl nicht ändert;
      • Get cFrameDisplayed und Spin, während sich das nicht ändert;
      • eine Zeit Berechnung zu qpcVBlank + qpcRefreshPeriod indem zu schlafen, und dann während QueryPerformanceCounter eine Zeit von weniger als diese gibt, Spin
  • diese haben alle Ansätze auch durch Streichen variiert worden dann drehend/schlafend, bevor wieder gemalt wird; oder umgekehrt: schlafen und dann malen.

Nur wenige scheinen sichtbare Wirkung und es welche Wirkung zu haben, ist schwer von einer niedrigeren Bildrate zu qualifizieren und kann nur ein Ergebnis sein. Keines verhindert das Zerreißen, dh keiner der DWM erstellt das Fenster mit einer "ganzen" Kopie des Inhalts des DC des Fensters.

Tipps geschätzt :)

+1

GDI ist alt und knusprig und nicht auf glatte Animation. Vielleicht Direct2D ausprobieren? –

+1

@ JonathanPotter Guter Punkt. Eine der Einschränkungen, unter denen ich mich befinde, besteht darin, DX jeglicher Art nicht zu verwenden - ein Grund ist die Anzahl der Plattformen und der Mangel an Anforderungen, der letzte Code muss ausgeführt werden. (Nicht alle sind moderne Windows.) Vielleicht Helfer DX-Code nur, wenn Aero existiert ...? Allerdings müsste es nahtlos und ohne visuelle Störungen zu und von GDI/DX wechseln, wie es die Animation ein-/ausschaltet. Ist das möglich? –

+0

Vielleicht könnten Sie eine Zeichenhilfsklasse schreiben, die DX verwendet, falls verfügbar, und andernfalls auf GDI zurückgreift. Es wäre ziemlich selten, ein System zu finden, das Direct3D nicht unterstützt, wenn auch nicht D2D. –

Antwort

2

Da Sie BitBlt verwenden, stellen Sie sicher, dass Ihre DIBs sind 4-Byte/Pixel. Mit 3 Bytes/Pixel ist GDI schrecklich langsam, während DWM läuft, das könnte die Quelle Ihres Reißens sein. Ein weiterer BitBlt Problem, das ich habe, wenn Ihre DIB ist etwas größer, als die BitBlt Anruf machen eine unerwartet lange Zeit dauern. Wenn Sie einen Anruf in kleinere Anrufe aufteilen, anstatt nur einen Teil der Daten zu zeichnen, kann dies hilfreich sein. Beides half mir für meinen Fall, nur weil BitBlt selbst zu langsam lief, was zu Videoartefakten führte.

+0

Nur für den Fall hinzufügen: nicht zu vergessen, nur Dirty Rect (GetUpdateRect) anstelle des ganzen Fensters zu aktualisieren ... – Jurlie

Verwandte Themen