Direct3D11, WPF und der Energiesparmodus

Direct3D 11 direkt in ein WPF D3DImage zu integrieren ist nicht ganz so einfach – das ist jedem bekannt, der das schon einmal probiert hat. Im Fall meiner 3D-Engine SeeingSharp gehe ich sogar noch etwas weiter: Das Rendering erfolgt komplett verteilt auf dem ThreadPool. Weiterhin werden so Kleinigkeiten wie Kantenglättung unterstützt, die die Integration in WPF auch noch etwas schwieriger machen. Nun habe ich es schon vor einer ganzen Weile geschafft, dass das auf allen getesteten Desktop-Rechnern gut funktioniert – aber irgendwie trifft man immer wieder auf neue Fälle. Neulich habe ich mir das neue Surface Pro 3 gekauft und siehe da… dort gibt es Probleme.

Das Problem besteht darin, dass scheinbar öfters das Programm im Hintergrund weiterläuft, aber an der Darstellung nichts mehr vom Direct3D Inhalt aktualisiert wird. Glücklicherweise kann ich am Surface-Tablet Visual Studio installieren und damit debuggen. Dabei kam raus, das seltsamerweise das Komplette Rendering inkl. Aktualisierung des D3DImage im Hintergrund eigentlich super funktioniert und nirgends Fehler oder Warnungen auftauchen – auf der Oberfläche ist aber nichts zu sehen. Rein von der Hardware her habe ich ein Surface Pro 3 mit 8 GB Arbeitsspeicher und einem Core I5, ist also eigentlich relativ gut ausgestattet.

Nach etwas rumspielen habe ich herausgefunden, dass das Problem von den Energie-Einstellungen her kommt. Das Surface ist standardmäßig so eingestellt, dass wenn möglich div. Komponenten auf Sparmodus geschaltet werden. Die Auswirkungen auf WPF sind mir noch nicht ganz klar, aber scheinbar werden dort im Hintergrund öfters die Ressourcen neu geladen. Letzten Endes lässt sich im Debugger aber erkennen, dass das D3DImage im Fehlerfall ganz normal ansprechbar ist, nur das private Flag _isDirty bleibt auf true. Nachfolgend im Screenshot die Stelle, wo man dieses Flag im Code von WPF finden kann.

isDirty Flag im D3DImage

Nach dem Kommentar bedeutet der true-Wert auf diesem Flag, dass das aktuelle Bild noch nicht gerendert wurde. Im Fehlerfall, den ich hier untersucht habe, bleibt dieses Flag auf true und springt nie wieder auf false, egal wie oft die Bilder meiner 3D-Engine in das D3DImage übertragen werden.

Die Lösung war letzten Endes vor jedem Rendering dieses Flag auszulesen und die eigenen Ressourcen bezogen auf das D3DImage neu zu erstellen, falls das Flag länger als eine Sekunde auf true bleibt. Es ist schon eine kleine „Hack“-Lösung, da man somit direkt das private Feld _isDirty auslesen muss, aber anders scheint es nicht zu funktionieren. Zumal gibt es keinen öffentlichen Member, über den man auf das _isDirty Flag zugreifen kann.

Nachtrag 22.11.2014: Nach weiteren Tests musste ich noch einen weiteren Member des D3DImage auslesen, und zwar _pUserSurfaceUnsafe. Hierbei handelt es sich um einen Pointer auf die von WPF verwendete Textur. Scheinbar gibt es auch hier Situationen, zu denen dieser Pointer auf Null steht, der Rendering-Prozess aus Sicht der Anwendung aber trotzdem erfolgreich durchläuft. Seit dieser zusätzlichen Anpassungen sind von dieser Seite keine Probleme mehr zu beobachten.

Für mich bleibt am Ende zu hoffen, das Microsoft in den nächsten Versionen diese Schnittstelle zwischen WPF und Direct3D allgemein verbessert. Die WinRT Plattform macht schon länger vor, wir es besser geht. Die aktuelle Roadmap von WPF macht glücklicherweise Hoffnung, da hier die Interoperabilität mit Direct3D an zweiter Stelle direkt nach der Performance steht.

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.