SteamVR/Frame-Timing

So zeigen Sie das erweiterte Frame-Timing-Diagramm an:

  1. Klicken Sie im SteamVR-Desktop-Widget auf das Hamburger-Menü
  2. Wählen Sie „Entwickler“
  3. Klicken Sie auf „Erweitertes Frame-Timing“

SteamVR Advanced Frame Timing.png

Die Standardansicht unterteilt die CPU- und GPU-Leistung in zwei gestapelte Diagramme:

FrameTiming Stacked2.PNG

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.

FrameTiming InHeadset.png

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.

FrameTiming Details2.PNG

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.

FrameTiming Bars2.PNG

  • Die im CPU-Diagramm gezeichnete gelbe Linie stellt die Anzahl zusätzlicher Vsync-Intervalle dar, bevor die der Anwendung bereitgestellten Posen vorhergesagt wurden, um das Rendern zu ermöglichen, das mehr als ein einzelnes Vsync-Intervall zum Abschluss benötigt. Da diese Posen der Anwendung bereitgestellt werden, bevor der Frame gerendert wird, kann dies nur eine Vermutung sein, die auf vorherigem Verhalten basiert. Hinweis: Ein Frame wird niemals angezeigt, bevor das Vsync-Intervall vorhergesagt wurde, selbst wenn das Rendern rechtzeitig abgeschlossen wurde. Dadurch wird eine „umgekehrte Neuprojektion“ des Bildes vermieden, die bei animierten Objekten in der Szene zu Ruckeln führen kann.
  • Die im CPU-Diagramm gezeichnete cyanfarbene Linie zeigt den Drosselungspegel zum Zeitpunkt der Aufzeichnung dieses Frames an. Anwendungen, deren Bereitstellung von Frames ständig länger als ein einzelnes Vsync-Intervall dauert, werden möglicherweise gedrosselt, um das Erlebnis zu verbessern.
  • Die im GPU-Diagramm gezeichnete cyanfarbene Linie gibt die Anzahl der Vsync-Intervalle an, bevor der Frame zur Verwendung bereit war. Für eine Anwendung, die die Framerate erstellt, sollte diese konstant bei 1 bleiben.
  • Die im GPU-Diagramm gezeichnete dunkelgrüne Linie gibt die Anzahl der Vsync-Intervalle an, bevor der Frame tatsächlich angezeigt wurde. Ein bestimmter Frame könnte rechtzeitig fertig sein, aber da ein zusätzlicher Frame vorhergesagt wurde, wurde er erst später angezeigt. Dieser Wert kann auch Null sein, wenn der Frame nie angezeigt wurde. Dies kann passieren, wenn ein neuerer Frame rechtzeitig fertig wurde (z. B. wenn das Vorhersageniveau sinkt).
  • Die im GPU-Diagramm gezeichnete weiße Linie zeigt an, wie oft ein bestimmter Frame präsentiert wurde. Ein Bild kann nur für einen bestimmten Zeitpunkt vorhergesagt werden, daher wird bei allen weiteren Verwendungen eine Neuprojektion genutzt, um Änderungen in der Kopfhaltung Rechnung zu tragen. Dieser Wert kann Null sein, wenn der Frame nie angezeigt wurde. Dies kann passieren, wenn der Frame nicht rechtzeitig fertig wurde, der nächste Frame jedoch schon.
  • Die im GPU-Diagramm gezeichnete rote Linie zeigt an, wie oft ein bestimmter Frame falsch präsentiert wurde (d. h. er wurde in einem anderen Vsync-Intervall als dem vorhergesagten präsentiert). Dies weist darauf hin, dass eine Neuprojektion angewendet wurde, um die Anzeige zu einem anderen Zeitpunkt als ursprünglich vorgesehen durchzuführen.

„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.

FrameTiming LateStart.PNG

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.