Freigeben über


Exemplarische Vorgehensweise: Fehlende Objekte durch Vertexschattierung

Diese exemplarische Vorgehensweise veranschaulicht, wie die Grafikdiagnose-Tools aus Visual Studio verwendet werden, um ein Objekt zu untersuchen, das aufgrund eines Fehlers fehlt, der in der Vertex-Shader-Stufe auftritt.

In dieser exemplarischen Vorgehensweise werden die folgenden Aufgaben beschrieben:

  • Verwenden der Grafikereignisliste, um mögliche Quellen des Problems zu suchen.

  • Verwenden des Fensters Grafikpipelinestufen, um die Auswirkung der Direct3D-API-Aufrufe DrawIndexed zu überprüfen.

  • Verwenden von HLSL Debugger, um den Vertex-Shader zu überprüfen.

  • Verwenden der Aufrufliste des Grafikereignisses, um die Quelle einer falschen HLSL-Konstanten zu suchen.

Szenario

Oft ist die Ursache für ein fehlendes Objekts in einer 3D-App, dass der Vertex-Shader die Vertices des Objekts in falscher oder unerwarteter Weise transformiert. So kann das Objekt beispielsweise zu klein skaliert oder so transformiert werden, dass es statt vor der Kamera dahinter liegt.

In diesem Szenario wird bei der Ausführung der App der Hintergrund wie erwartet gerendert, jedoch eines der Objekte nicht angezeigt. Mit der Grafikdiagnose können Sie den Fehler in einer Grafikprotokolldatei erfassen, um die App zu debuggen. Das Problem sieht in der App wie folgt aus:

Das Objekt ist nicht sichtbar.

Untersuchung

Mithilfe des Grafikdiagnose-Tools können Sie die Grafikprotokolldatei laden, um die Frames zu überprüfen, die während des Tests aufgezeichnet wurden.

So überprüfen Sie einen Frame in einem Grafikprotokoll

  1. Laden Sie in Visual Studio ein Grafikprotokoll, das einen Frame mit dem fehlenden Objekt enthält. Eine neue Grafikprotokoll-Registerkarte wird in Visual Studio angezeigt. Ganz oben auf dieser Registerkarte befindet sich die Renderingzielausgabe des ausgewählten Frames. Im unteren Teil befindet sich die Frameliste, in der alle aufgezeichneten Frames als Miniaturansicht angezeigt werden.

  2. Wählen Sie in der Frameliste einen Frame aus, in dem das Objekt nicht angezeigt wird. Das Renderingziel wird aktualisiert, um den ausgewählten Frame wiederzugeben. In diesem Szenario sieht die Grafikprotokoll-Registerkarte wie folgt aus:

    Das Dokument mit dem Diagrammprotokoll in Visual Studio

Nachdem Sie einen Frame ausgewählt haben, der das Problem veranschaulicht, können Sie ihn mithilfe der Grafikereignisliste untersuchen. Die Grafikereignisliste enthält alle Direct3D-API-Aufrufe, die zum Rendern des aktiven Frames erzeugt wurden, z. B. API-Aufrufe, zum Einrichten des Gerätezustands, zum Erstellen und Aktualisieren des Puffers und zum Zeichnen von Objekten, die im Frame angezeigt werden. Viele Arten von Aufrufen sind interessant, weil es häufig (jedoch nicht immer) eine entsprechende Änderung im Renderziel gibt, wenn die App wie gewünscht funktioniert, z. B. Aufrufe zum Zeichnen, Verteilen, Kopieren oder Löschen. Zeichnen-Aufrufe sind besonders interessant, da jeder einzelne Geometrie darstellt, die die App gerendert hat (Dispatch-Aufrufe können ebenfalls Geometrie rendern).

Da Sie (in diesem Fall) wissen, dass das fehlende Objekt nicht auf dem Renderziel gezeichnet wird, während die restliche Szene wie erwartet dargestellt wird, können Sie die Grafikereignisliste zusammen mit dem Tool Grafikpipelinestufen verwenden, um zu ermitteln, welcher Draw-Aufruf der Geometrie des fehlenden Objekts entspricht. Im Fenster Grafikpipelinestufen wird die Geometrie angezeigt, die an die einzelnen Zeichnen-Aufrufe gesendet wurde, unabhängig von der Auswirkung auf das Renderziel. Wenn Sie sich durch die Zeichnen-Aufrufe bewegen, werden die Pipelinestufen aktualisiert und zeigen die Geometrie an, die dem jeweiligen Aufruf zugeordnet ist. Die Ausgabe des Renderziels wird aktualisiert und zeigt den Zustand des Renderziels nach Ausführung des Aufrufs an.

So finden Sie den Zeichnen-Aufruf für die fehlende Geometrie

  1. Öffnen Sie das Fenster Grafikereignisliste. Wählen Sie auf der Symbolleiste Grafikdiagnose die Option Ereignisliste aus.

  2. Öffnen Sie das Fenster Grafikpipelinestufen. Wählen Sie auf der Symbolleiste Grafikdiagnose die Option Pipelinestufen aus.

  3. Durchlaufen Sie alle Draw-Aufrufe im Fenster Grafikereignisliste und achten Sie dabei im Fenster Grafikpipelinestufen auf das fehlende Objekt. Zur Erleichterung können Sie im Feld Suche in der oberen rechten Ecke des Fensters Grafikereignisliste "Zeichnen" eingeben. Damit wird die Liste gefiltert und enthält nur Ereignisse, die "Zeichnen" im Titel haben.

    Im Fenster Grafikpipelinestufen zeigt die Eingabe-Assembler-Stufe die Geometrie des Objekts vor der Transformation, und die Vertex-Shader-Stufe zeigt dasselbe Objekt, nachdem es transformiert wurde. In diesem Szenario wissen Sie, dass Sie das fehlende Objekt gefunden haben, wenn es in der Eingabe-Assembler-Stufe angezeigt wird, während die Vertex-Shader-Stufe nichts anzeigt.

    Hinweis

    Wenn andere Geometriestufen, beispielsweise der Hull-Shader, Domain-Shader oder Geometrie-Shader, das Objekt verarbeiten, sind sie möglicherweise die Ursache des Problems.In der Regel steht das Problem in Zusammenhang mit der untersten Stufe, auf der das Ergebnis nicht oder nicht wie erwartet dargestellt wird

  4. Beenden Sie die Suche, wenn Sie den Draw-Aufruf erreicht haben, der dem fehlenden Objekt entspricht. In diesem Szenario gibt das Fenster Grafikpipelinestufen an, dass die Geometrie zwar der GPU übergeben wurde (wird durch das Symbol des Eingabe-Assemblers angezeigt), jedoch nicht im Renderziel erscheint, da auf der Vertex-Shader-Stufe ein Problem aufgetreten ist (wird durch das Symbol für den Vertex-Shader angezeigt):

    Ein DrawIndexed-Ereignis und seine Auswirkungen auf die Pipeline

Nachdem Sie sich davon überzeugt haben, dass die App einen Draw-Aufruf für die Geometrie des fehlenden Objekts ausgeführt hat und dass das Problem auf der Vertex-Shader-Stufe aufgetreten ist, können Sie den HLSL-Debugger verwenden, um den Vertex-Shader zu überprüfen und festzustellen, was mit der Objektgeometrie geschehen ist. Sie können den HLSL-Debugger verwenden, um den Zustand von HLSL-Variablen während der Ausführung zu untersuchen, den HLSL-Code schrittweise zu überprüfen und Haltepunkte festzulegen, die Ihnen bei der Problemdiagnose helfen.

So überprüfen Sie den Vertex-Shader

  1. Debugging der Vertex-Shader-Stufe starten. Wählen Sie im Fenster Grafikpipelinestufen unter der Stufe Vertex-Shader die Schaltfläche Debuggen starten.

  2. Da die Eingabe-Assembler-Stufe korrekte Daten für den Vertex-Shader bereitzustellen scheint, die Vertex-Shader-Stufe aber keine Ausgabe erzeugt, müssen Sie die Ausgabestruktur output des Vertex-Shader überprüfen. Während Sie den HLSL-Code durchlaufen, achten Sie darauf, ob output geändert wird.

  3. Bei der erstmaligen Änderung von output wird der Member worldPos geschrieben.

    Der Wert von „output.worldPos“ erscheint angemessen.

    Da sein Wert korrekt zu sein scheint, durchlaufen Sie den Code weiter bis zur nächsten Zeile, in der output geändert wird.

  4. Bei der nächsten Änderung von output wird der Member pos geschrieben.

    Der Wert von „output.pos“ wurde eliminiert.

    Dieses Mal erscheint der Wert des pos-Members – nur Nullen – verdächtig. Als Nächstes müssen Sie herausfinden, warum der Wert von output.pos nur aus Nullen besteht.

  5. Beachten Sie, dass output.pos seinen Wert von der Variablen temp übernimmt. Auf der vorherigen Zeile sehen Sie, dass der Wert von temp das Ergebnis der Multiplikation des vorherigen Werts mit der Konstanten projection ist. Sie vermuten, dass der verdächtige Wert von temp das Ergebnis dieser Multiplikation ist. Wenn Sie auf projection zeigen, sehen Sie, dass der Wert auch nur aus Nullen besteht.

    Die Projektionsmatrix enthält eine ungültige Transformation.

    In diesem Szenario ergibt die Prüfung, dass der verdächtige Wert von temp wahrscheinlich aus der Multiplikation mit projection resultiert, und da projection eine Konstante ist, die eine Projektionsmatrix darstellen soll, wissen Sie, dass sie nicht nur Nullen enthalten darf.

Nachdem Sie ermittelt haben, dass die HLSL-Konstante projection (die von Ihrer App an den Shader übergeben wurde) wahrscheinlich die Quelle des Problems ist, besteht der nächste Schritt darin, die Stelle im Quellcode der App zu suchen, wo der Konstantenpuffer gefüllt wird. Sie können die Aufrufliste des Grafikereignisses verwenden, um diese Position zu suchen.

So ermitteln Sie, wo die Konstante im Quellcode der App festgelegt wird

  1. Öffnen Sie das Fenster Aufrufliste des Grafikereignisses. Wählen Sie auf der Symbolleiste Grafikdiagnose die Aufrufliste des Grafikereignisses.

  2. Navigieren Sie in der Aufrufliste in den Quellcode der Anwendung. Im Fenster Aufrufliste des Grafikereignisses wählen Sie den obersten Aufruf, um festzustellen, ob der Konstantenpuffer dort gefüllt wird. Wenn nicht, suchen Sie weiter in der Aufrufliste, bis Sie finden, wo er gefüllt wird. In diesem Szenario ermitteln Sie, dass der Konstantenpuffer durch die Anwendung der UpdateSubresource-Direct3D-API weiter oben in der Aufrufliste in einer Funktion mit dem Namen MarbleMaze::Render gefüllt wird, und dass der Wert von einem Konstantenpufferobjekt mit dem Namen m_marbleConstantBufferData stammt:

    Der Code, der den Konstantenpuffer festlegt

    Tipp

    Wenn Sie gleichzeitig die App debuggen, können Sie einen Haltepunkt an dieser Position festlegen, der erreicht wird, wenn der nächste Frame gerendert wird.Sie können dann die Member von m_marbleConstantBufferData überprüfen, um zu bestätigen, dass der Wert des projection-Members nur aus Nullen besteht, wenn der konstante Puffer gefüllt wird.

Nachdem Sie die Position gefunden haben, an welcher der Konstantenpuffer gefüllt wird, und festgestellt haben, dass seine Werte aus der Variablen m_marbleConstantBufferData stammen, besteht der nächste Schritt darin, herauszufinden, wo der m_marbleConstantBufferData.projection-Member mit Nullen gefüllt wird. Sie können Alle Verweise suchen verwenden, um schnell festzustellen, welcher Code den Wert von m_marbleConstantBufferData.projection ändert.

So ermitteln Sie, wo der Projektionsmember im Quellcode der App festgelegt wird

  1. Suchen Sie Verweise auf m_marbleConstantBufferData.projection. Öffnen Sie das Kontextmenü für die Variable m_marbleConstantBufferData und wählen Sie dann Alle Verweise suchen.

  2. Um zu der Quellcodezeile in der App zu navigieren, in welcher der projection-Member geändert wird, wählen Sie diese Zeile im Fenster Ergebnisse der Symbolsuche. Da das erste Ergebnis, das den Projektionsmember ändert, nicht unbedingt die Ursache des Problems ist, müssen Sie möglicherweise mehrere Bereiche des Quellcodes der App überprüfen.

Nachdem Sie die Position gefunden haben, an der m_marbleConstantBufferData.projection festgelegt wird, können Sie den umgebenden Quellcode überprüfen, um den Ursprung des falschen Werts zu bestimmen. In diesem Szenario ermitteln Sie, dass der Wert von m_marbleConstantBufferData.projection auf eine lokale Variable mit dem Namen projection festgelegt wird, bevor er mit einem Wert initialisiert wurde, der durch den Code m_camera->GetProjection(&projection); auf der nächsten Zeile angegeben wird.

Die Marmorprojektion wird vor der Initialisierung festgelegt.

Um das Problem zu beheben, verschieben Sie die Codezeile, die den Wert von m_marbleConstantBufferData.projection festlegt, hinter die Zeile, die den Wert der lokalen Variablen projection initialisiert.

Der korrigierte C++-Quellcode

Nachdem Sie den Code korrigiert haben, können Sie ihn neu erstellen und die Anwendung ausführen. Sie werden festzustellen, dass das Renderproblem gelöst ist:

Das Objekt wird jetzt angezeigt.