So zeigen Sie das erweiterte Frame-Timing-Diagramm an:
Die Standardansicht unterteilt die CPU- und GPU-Leistung in zwei gestapelte Diagramme:
Die blauen Abschnitte geben die von der Anwendung aufgewendete Zeit an; diese sind weiter in „Szene“ und „Sonstiges“ unterteilt. Der „Szene“-Teil ist die Menge an Arbeit, die zwischen der Rückkehr von WaitGetPoses und der Übermittlung der zweiten Augentextur ( https://github.com/ValveSoftware/openvr/wiki/IVRCompositor_Overview ) geleistet wurde, während „Sonstiges“ die Zeit ist, die danach aufgewendet wurde Dies dient zum Rendern des Begleitfensters der Anwendung usw. Das CPU-Timing hier erfasst keine parallel ausgeführten Arbeiten (z. B. im Hauptthread Ihrer Anwendung). Der braune Abschnitt „Andere“ im GPU-Timing spiegelt alle GPU-Blasen, Kontextwechsel-Overhead oder GPU-Arbeit von anderen Anwendungen wider, die zwischen anderen Arbeitssegmenten für diesen Frame eingeplant werden. Um dies genauer zu untersuchen, ist die Verwendung von gpuview ( https://developer.valvesoftware.com/wiki/SteamVR/Installing_GPUView ) erforderlich.
Wenn Sie auf „Im Headset anzeigen“ klicken, wird dieses Diagramm auch in VR angezeigt. Dies wird auf der Rückseite Ihres rechten Controllers angezeigt (oder am Ursprung, wenn keine Controller angeschlossen sind). Sie werden feststellen, dass das grüne GPU-Band etwas länger dauert, wenn dieses Diagramm in VR sichtbar ist, da das Rendern von Overlays zusätzliche Arbeit für den Compositor erfordert.
Wenn Sie auf „Details“ klicken, wird eine Aufschlüsselung aller erfassten Einzelzeiten angezeigt. Verwenden Sie das Kontrollkästchen neben jeder Beschriftung, um die Daten zu filtern.
Dies sind die gleichen Daten, die von IVRCompositor.GetFrameTiming zurückgegeben werden .
/** Stellt der App die Timing-Informationen eines einzelnen Frames zur Verfügung */
struct Compositor_FrameTiming
{
uint32_t m_nSize ; // Auf sizeof( Compositor_FrameTiming ) setzen
uint32_t m_nFrameIndex ;
uint32_t m_nNumFramePresents ; // Anzahl der Präsentationen dieses Frames
uint32_t m_nNumMisPresented ; // Häufigkeit, mit der dieser Frame auf einem anderen als dem ursprünglich vorhergesagten vsync präsentiert wurde
uint32_t m_nNumDroppedFrames ; // Anzahl der zusätzlichen Scans des vorherigen Frames
uint32_t m_nReprojectionFlags ;
/** Absolute Zeitreferenz zum Vergleichen von Frames. Dies stimmt mit dem Vsync überein, zu dem der Start relativ ist. */
double m_flSystemTimeInSeconds ;
/** Diese Zeiten können aufgrund der Betriebssystemplanung Arbeit von anderen Prozessen umfassen.
* Je weniger Arbeitspakete diese aufteilen, desto unwahrscheinlicher ist es, dass dies geschieht.
* GPU-Arbeit kann durch Aufrufen von Flush unterbrochen werden. Dies kann manchmal nützlich sein, um die GPU mit der Verarbeitung zu beginnen
, die früher im Frame funktioniert. */
float m_flPreSubmitGpuMs ; // Zeitaufwand für das Rendern der Szene (GPU-Arbeit, die zwischen WaitGetPoses und dem zweiten Submit übermittelt wurde)
float m_flPostSubmitGpuMs ; // zusätzlicher Zeitaufwand für das Rendern durch die Anwendung (z. B. Begleitfenster)
float m_flTotalRenderGpuMs ; // Zeit zwischen der unmittelbar nach der Gegenwart übermittelten Arbeit (idealerweise vsync) und dem Ende der vom Compositor übermittelten Arbeit
float m_flCompositorRenderGpuMs ; // Zeitaufwand für die Durchführung von Verzerrungskorrekturen, das Rendern von Begleitpersonen, Überlagerungen usw.
float m_flCompositorRenderCpuMs ; // Zeitaufwand für die CPU-Übermittlung der oben genannten Arbeit für diesen Frame
float m_flCompositorIdleCpuMs ; // Zeit, die mit dem Warten auf den Start verbracht wurde (die Anwendung hätte viel mehr Zeit gebrauchen können)
/** Verschiedene gemessene Intervalle. */
float m_flClientFrameIntervalMs ; // Zeit zwischen Aufrufen von WaitGetPoses
float m_flPresentCallCpuMs ; // Zeit, die beim Aufruf von present blockiert wird (normalerweise 0,0, kann aber auch lang sein)
float m_flWaitForPresentCpuMs ; // Zeit, die beim Spin-Warten auf die Änderung des Frame-Index aufgewendet wird (nicht nahe Null weist auf einen Warteobjektfehler hin)
float m_flSubmitFrameMs ; // in IVRCompositor::Submit verbrachte Zeit (nicht nahe Null weist auf ein Treiberproblem hin)
/** Das Folgende ist alles relativ zur SystemTimeInSeconds dieses Frames */
float m_flWaitGetPosesCalledMs ;
float m_flNewPosesReadyMs ;
float m_flNewFrameReadyMs ; // zweiter Aufruf von IVRCompositor::Submit
float m_flCompositorUpdateStartMs ;
float m_flCompositorUpdateEndMs ;
float m_flCompositorRenderStartMs ;
vr :: TrackedDevicePose_t m_HmdPose ; // Pose, die von der App zum Rendern dieses Frames verwendet wird
uint32_t m_nNumVSyncsReadyForUse ;
uint32_t m_nNumVSyncsToFirstView ;
};
Mit dem Schieberegler können Sie hinein- und herauszoomen. Wenn weit genug hineingezoomt wird, ändert sich die gestapelte Ansicht in ein Balkendiagramm, das nützlich ist, um das Timing einzelner Bilder anzuzeigen.
„Late Start“ ist ein Sonderfall, bei dem die Anwendung ihr CPU-Budget für den Frame überschritten hat, aus diesem Grund aber noch keinen Frame verloren hat. Dieser zusätzliche Spielraum entsteht durch das, was wir „anlaufenden Start“ nennen; Es stellt einen 3-ms-Puffer bereit, bevor der Frame beginnt (z. B. Vsync), damit die Anwendung mit der Eingabe der GPU-Renderbefehle für diesen Frame beginnen kann.
Mehr dazu hier: http://www.gdcvault.com/play/1021771/Advanced-VR .
In diesem Beispiel können Sie bei Frame 7964 sehen, dass die Anwendung erst gegen Ende des Frames an den Compositor übergeben wurde. Dadurch wurde die „laufende Startzeit“ für den nächsten Frame verbraucht, was durch das rote „Late Start“ angezeigt wird. Sie können auch sehen, wie sich dies auf die GPU auswirkt, indem Sie eine Blase (Leerlaufzeit) erzeugen, die durch das entsprechend wachsende bräunliche „Andere“ angezeigt wird. Auch hier sehen wir, dass die Interleaved-Reprojektion einsetzt, weil wir so nah dran sind, Frames für Frame 7966 zu löschen. Auch die gelbe Leerlaufzeit springt in die Höhe (und verlässt den oberen Bereich der Skala), was darauf hindeutet, dass die Anwendung nun über das doppelte Budget verfügt Arbeit mit und fällt nach drei weiteren Frames unter dem Budget wieder auf den Normalwert zurück. Dieser ganze Vorgang wiederholt sich in diesem Beispiel etwa 60 Frames später noch einmal.
Wenn wir zur Detailansicht wechseln, können wir sehen, dass in der Anwendung etwas passiert ist, das dazu geführt hat, dass WaitGetPoses in diesen beiden Fällen viel später als normal aufgerufen wird. An diesem Punkt müssten Sie den Profiler Ihrer Anwendung untersuchen, um die Ursache dafür zu ermitteln.