Tutorial: Objetos ausentes debido al sombreado de vértices

Este tutorial muestra cómo utilizar las herramientas de diagnóstico de gráficos de Visual Studio para investigar un objeto que falta debido a un error que se produce durante la fase del sombreador de vértices.

Este tutorial muestra estas tareas:

  • Usar la Lista de eventos de gráficos para buscar los orígenes posibles del problema.

  • Utilizar la ventana Etapas de canalización de gráficos para comprobar el efecto de las llamadas API de Direct3D de DrawIndexed.

  • Utilizar el depurador de HLSL para examinar el sombreador de vértices.

  • Utilizar la Pila de llamadas de eventos gráficos para ayudar a encontrar el origen de una constante de HLSL incorrecta.

Escenario

Una de las causas comunes de que falte un objeto en una aplicación 3D se produce cuando el sombreador de vértices transforma los vértices del objeto de forma incorrecta o inesperada, por ejemplo, el objeto se puede escalar un tamaño muy pequeño o transformarse de forma que aparece detrás de la cámara, en lugar de delante.

En este escenario, cuando la aplicación se ejecuta para probarlo, el fondo se representa como se esperaba, pero uno de los objetos no aparece.Al usar el diagnóstico de gráficos, puede capturar el problema en un registro de gráficos de modo que pueda depurar la aplicación.El problema tiene el siguiente aspecto en la aplicación:

No se puede ver el objeto.

Investigación

Al usar el diagnóstico de gráficos, puede cargar el archivo de registro de gráficos para inspeccionar los marcos que se capturaron durante la prueba.

Para examinar un fotograma en un registro de gráficos

  1. En Visual Studio, cargue un registro de gráficos que contenga un marco que tenga el objeto que falta.Una nueva pestaña de registro de gráficos aparece en Visual Studio.En la parte superior de esta pestaña está la salida del destino de representación del marco seleccionado.En la parte inferior está Lista de fotogramas, que muestra cada cuadro capturado como imagen en miniatura.

  2. En la Lista de fotogramas, seleccione un marco que muestre que el objeto no se presenta.El destino de presentación se actualiza para reflejar el fotograma seleccionado.En este escenario, los gráficos registrar aspectos de la pestaña como el siguiente:

    Documento de registro de gráficos en Visual Studio

Después de haber seleccionado un marco que muestra el problema, puede comenzar a diagnosticarlo mediante la Lista de eventos gráficos.La Lista de eventos de gráficos contiene cada llamada API de Direct3D para presentar el fotograma activo, por ejemplo, las llamadas API para configurar el estado del dispositivo, crear y actualizar los búferes, y dibujar los objetos que aparecen en el fotograma.Muchos tipos de llamadas son interesantes porque muchas veces (pero no siempre) hay un cambio correspondiente en destino de representación cuando la aplicación funciona según lo previsto, por ejemplo, las llamadas a Draw, Dispatch, Copy o Clear.Las llamadas a draw son especialmente interesantes porque cada una representa la geometría que la aplicación representó (las llamadas a Dispatch también pueden representar la geometría).

Dado que sabe que el objeto que falta no se dibuja en el destino de representación (en este caso) pero que el resto de la escena se dibuja como se esperaba, puede usar la Lista de eventos gráficos junto con la herramienta de Etapas de canalización de gráficos para determinar qué llamada a draw se corresponde con la geometría del objeto que falta.La ventana Etapas de canalización de gráficos muestra la geometría que se envió a cada llamada a draw, sin importar su efecto en el destino de presentación.Mientras se desplaza por las llamadas de dibujo, las fases de la canalización se actualizan para mostrar la geometría asociada a esa llamada y se actualiza la salida del destino de representación para mostrar el estado del destino de representación después de completar la llamada.

Para buscar la llamada a draw para la geometría que falta

  1. Abra la ventana Lista de eventos gráficos.En la barra de herramientas Diagnóstico de gráfico, elija Lista de eventos.

  2. Abra la ventana Etapas de canalización de gráficos.En la barra de herramientas Diagnósticos de gráficos, elija Etapas de canalización.

  3. Al desplazarse con cada llamada de dibujo en la ventana de Lista de eventos gráficos, observe la ventana Etapas de canalización de gráficos para el objeto que falta.Para que le resulte más fácil, escriba “Draw” en el cuadro Buscar de la esquina superior derecha de la ventana Lista de eventos gráficos.Esto filtra la lista de modo que solo contenga los eventos que contienen “Draw” en sus títulos.

    En la ventana Etapas de canalización de gráficos, la fase Ensamblador de entrada muestra la geometría del objeto antes de que se transforme y la fase Sombreador de vértices muestra el mismo objeto una vez transformado.En este escenario, sabe que ha encontrado el objeto que falta cuando se muestra en la fase del Ensamblador de entrada y no aparece nada en la fase del Sombreador de vértices.

    [!NOTA]

    Si otras fases de geometría, por ejemplo el Sombreador de casco, Sombreador de dominios o Sombreador de geometría, procesa el objeto, pueden ser la causa del problema.Normalmente, el problema está relacionado con la fase más temprana en la que el resultado no se muestra o se muestra de forma inesperada.

  4. Deténgase al alcanzar la llamada a draw que corresponde al objeto que falta.En este escenario, la ventana Etapas de canalización de gráficos indica que la geometría se emitió para la GPU (indicada por la miniatura del Ensamblador de entrada), pero no aparece en el destino de representación porque algo fue mal durante la fase del sombreador de vértices (indicado por la miniatura de Sombreador de vértices):

    Un evento DrawIndexed y su efecto en la canalización

Después de que se confirme que la aplicación generó una llamada de dibujo para la geometría del objeto que falta y detecta que el problema ocurre durante la fase del sombreador de vértices, puede usar el depurador de HLSL para examinar el sombreador de vértices y para averiguar qué sucedió con la geometría del objeto.Puede utilizar el depurador de HLSL para examinar el estado de las variables de HLSL durante la ejecución, ejecutar paso a paso el código HLSL, y establecer puntos de interrupción que le ayudarán a diagnosticar el problema.

Para examinar el sombreador de vértices

  1. Empiece a depurar la fase del sombreador de vértices.En la ventana Etapas de canalización de gráficos, en la fase Sombreador de vértices, elija el botón Iniciar depuración.

  2. Dado que parece que la fase de Ensamblador de entrada proporciona buenos datos para el sombreador de vértices y la fase de Sombreador de vértices parece que no genera resultados, debe examinar la estructura de salida del sombreador de vértices, output.Mientras recorre el código de HLSL, tendrá una visión más detallada cuando se modifica output.

  3. La primera vez que se modifica ese miembro output, se escribe el miembro worldPos.

    El valor de "output.worldPos" parece razonable

    Dado que su valor parece ser razonable, continúa recorriendo el código hasta la siguiente línea que modifica output.

  4. La próxima vez que se modifique ese miembro output, se escribirá el miembro pos.

    El valor de "output.pos" se ha establecido en cero

    Esta vez, el valor del miembro pos, todo ceros, es sospechoso.A continuación, debe determinar qué ocurrió para que output.pos tuviera un valor de todo ceros.

  5. Observará que output.pos toma el valor de una variable que se denomina temp.En la línea anterior, verá que el valor de temp es el resultado de multiplicar su valor anterior por una constante que se denomina projection.Sospecha que el valor sospechoso de temp es el resultado de esta multiplicación.Cuando sitúa el puntero sobre projection, observa que su valor también es todo ceros.

    La matriz de proyección incluye una transformación errónea

    En este escenario, el examen revela que el valor sospechoso de temp probablemente esté producido por la multiplicación por projection y, como projection es una constante concebida para contener una matriz de proyección, sabe que no debe contener todos los ceros.

Después de determinar que la projection de la constante de HLSL, que pasó la aplicación al sombreador, es el origen probable del problema, el paso siguiente es encontrar la ubicación en el código fuente de la aplicación donde se rellena el búfer constante.Puede utilizar Pila de llamadas de eventos gráficos para encontrar esta ubicación.

Para averiguar dónde se establece la constante en el código fuente de la aplicación

  1. Abra la ventana Pila de llamadas de eventos gráficos.En la barra de herramientas Diagnóstico de gráficos, elija Pila de llamadas de eventos gráficos.

  2. Navegue por la pila de llamadas hasta el código fuente de la aplicación.En la ventana Pila de llamadas de eventos gráficos, elija la llamada superior para ver si el búfer constante se rellena allí.Si no es así, continúe por la parte superior de la pila de llamadas hasta que encuentre dónde se rellena.En este escenario, detecta que el búfer constante se está rellenando (al usar la API de Direct3D UpdateSubresource) en un punto más al inicio de la pila en una función denominada MarbleMaze::Render y que su valor procede de un objeto del búfer constante que se llama m_marbleConstantBufferData:

    Código que establece el búfer de constantes del objeto

    SugerenciaSugerencia

    Si está depurando simultáneamente su aplicación, puede establecer un punto de interrupción en esta ubicación y se alcanzará cuando se represente el siguiente marco.Después puede inspeccionar los miembros de m_marbleConstantBufferData para confirmar que el valor del miembro projection está establecido en todo ceros cuando el búfer de constantes se llena.

Después de encontrar la ubicación donde se rellena el búfer constante y la detectar que sus valores provienen del m_marbleConstantBufferData de la variable, el paso siguiente es averiguar dónde se establece el miembro de m_marbleConstantBufferData.projection para todos los ceros.Puede utilizar Buscar todas las referencias para buscar rápidamente el código que cambia el valor de m_marbleConstantBufferData.projection.

Para averiguar dónde se establece el miembro de proyección en el código fuente de la aplicación

  1. Buscar referencias para m_marbleConstantBufferData.projection.Abra el menú contextual para la variable m_marbleConstantBufferData y, a continuación, elija Buscar todas las referencias.

  2. Para navegar a la ubicación de la línea en el código fuente de la aplicación donde se modifica el miembro projection, elija esa línea en la ventana Resultados de la búsqueda de símbolos.Dado que el primer resultado que modifica el miembro de proyección es posible que no sea la causa del problema, puede que tenga que examinar varias áreas del código fuente de la aplicación.

Después de encontrar la ubicación donde se establece m_marbleConstantBufferData.projection, puede examinar el código fuente circundante para determinar el origen del valor incorrecto.En este escenario, detecta que el valor de m_marbleConstantBufferData.projection está establecido en una variable local denominada projection antes de que se haya inicializado en un valor que es especificado por el código m_camera->GetProjection(&projection); en la línea siguiente.

La proyección de Marble se establece antes de la inicialización

Para corregir el problema, mueva la línea de código que establece el valor de m_marbleConstantBufferData.projection detrás de la línea que inicializa el valor de la variable local projection.

Código fuente de C++ corregido

Después de corregir el código, puede recompilarlo y ejecute de nuevo la aplicación para detectar que el problema de representación se ha resuelto:

Ahora se muestra el objeto.