Поделиться через


Пошаговое руководство. Отсутствие объектов вследствие заливки вершин

В этом пошаговом руководстве показано, как использовать средства диагностики графики Visual Studio для изучения объекта, который отсутствует из-за ошибки, происходящей за время стадии шейдера вершины.

В данном пошаговом руководстве рассмотрены следующие задачи:

  • Использование Список событий графики, чтобы обнаружить потенциальные источники проблемы.

  • Использование окно Этапы графического конвейера для проверки эффекта вызовов API Direct3D DrawIndexed.

  • Использование Отладчик HLSL для изучения шейдера вершин.

  • Использование Стек вызовов событий графики, чтобы помочь обнаружить источник неправильной константы HLSL.

Сценарий

Одна из распространенных причин отсутствия объекта в приложении трехмерной графики связана с тем, что шейдер вершин преобразовывает вершины объекта неправильно или непредвиденным способом — например, объект может быть очень сильно уменьшен или преобразован так, что находится за камерой, а не перед ней.

В этом сценарии при запуске приложения для тестирования фон отрисован как ожидалось, но один из объектов не появляется.Используя диагностику графики, вы записываете проблему в журнал графики, позволяя отлаживать приложение.В приложении проблема выглядит следующим образом:

Объект не виден.

Расследование

С помощью инструментов диагностики графики можно загрузить файл журнала графики для проверки кадров, зарегистрированных во время теста.

Как Просмотреть кадр в журнале графики

  1. В Visual Studio загрузите журнал графики, содержащий фрейм, который указывает на отсутствующий объект.Новая вкладка журнала графики отображается в Visual Studio.В верхней части этой вкладки находится отображенный целевой вывод выбранного кадра.В нижней части раздела Список кадров, которая отображает каждый схваченный кадр как эскиз.

  2. В поле Список кадров выберите кадр, который демонстрирует, что объект не отображается.целевой объект прорисовки обновляется, отражая выбранный кадр.В этом сценарии вкладка журнала графики выглядит следующим образом:

    Документ журнала графики в Visual Studio

После выбора кадра, который демонстрирует проблему, можно начать диагностику с использованием Списка событий графики.Список событий графики содержит все вызовы API Direct3D, которое были выполнены для отрисовки активного кадра, например вызовы API для настройки состояния устройства, создания и обновления буферов и рисования объектов, которые присутствуют в кадре.Существует много типов вызовов, которые интересны поскольку часто (но не всегда) происходит соответствующее изменение в целевом объекте отрисовки, когда приложение работает, как ожидалось. Например, вызовы Draw, Dispatch, Copy или Clear.Вызовы рисования особенно интересны, поскольку каждый из них представляет геометрию, отрисованную приложением (вызовы диспетчера также могут отрисовывать геометрию).

Так как известно, что отсутствующий объект не рисуется в в целевом объекте прорисовки (в данном случае) — но остальная часть сцены отрисовываются как предполагалось—можно использовать Список событий графики совместно с средством Этапы графического конвейера, чтобы определить, какой вызов рисования соответствует геометрии отсутствующего объекта.В окне Этапы графического конвейера отображается геометрия, отправляемая каждому вызову рисования, независимо от оказываемого им воздействия на целевой буфер отрисовки.По мере продвижения через вызовы рисования этапы конвейера обновляются для указания геометрии, связанной с этим вызовом, и целевой результат отрисовки обновляется для отображения состояния целевого объекта отрисовки после выполнения вызова.

Поиск вызова рисования для отсутствующей геометрии

  1. При необходимости откройте окно Список событий графики.На панели инструментов Диагностика графики выберите Список событий.

  2. Откройте окно Этапы графического конвейера.На панели инструментов Диагностика графики выберите Этапы конвейера.

  3. По мере движения через каждый вызов рисования в окне Список событий графики, следите за отсутствующим объектом в окне Этапы графического конвейера.Чтобы сделать это проще, введите «Рисование» в окне Поиск в правом верхнем углу окна Список событий графики.Это фильтрует список, чтобы он будет содержать только события с «Draw» в своих именах.

    В окне Этапы графического конвейера этап Сборщик входных данных показывает геометрию объекта перед тем, как он будет преобразован, а этап Шейдер вершин показывает тот же объект после преобразования.В этом сценарии известно, что найден отсутствующий объект при отображении на этапе Сборщик входных данных и ничего не отображается на этапе Шейдер вершин.

    ПримечаниеПримечание

    Если другие стадии геометрии — например стадии шейдера поверхности, шейдера доменов или шейдера геометрии — обрабатывают объект, они могут быть причиной проблемы.Как правило, проблема связана с самой ранней стадией, в которой результат не отображается или отображается непредвиденным образом.

  4. Дойдя до вызова рисования, соответствующего отсутствующему объекту, остановитесь.В этом сценарии окно Этапы графического конвейера указывает, что геометрия выдана в GPU (указывает эскиз сборщика входных данных), но не отображается в целевом объекте отрисовки, поскольку что-то пошло неправильно на этапе шейдера вершин (указывает эскиз шейдера вершины):

    Событие DrawIndexed и его результат в конвейере

После подтверждения, что приложение выдало вызовы рисования для геометрии отсутствующего объекта и обнаружения, что проблема возникает на этапе вершинного построителя текстуры, можно использовать отладчик HLSL для проверки вершинного построителя текстуры и установления, что произошло с объектом геометрии.Можно использовать отладчик HLSL, чтобы проверить состояние переменных HLSL во время выполнения, пройти по коду HLSL и задать точки останова, чтобы помочь диагностировать проблему.

Как Просмотреть Шейдер вершин

  1. Запустите отладку этапа шейдера вершин.В окне Этапы графического конвейера под этапом Шейдер вершин нажмите кнопку Начать отладку.

  2. Так как кажется, что этап Сборщик входных данных предоставляет правильные данные шейдеру вершин, а этап Шейдер вершин не предоставляет вывода,пользователь хочет изучить структуру вывода шейдера вершин, output.При выполнении кода HLSL вы Более внимательно ознакомитесь, когда output будет изменен.

  3. При первом изменении output производится запись в член worldPos.

    Значение "output.worldPos" оказывается допустимым

    Так как значение кажется приемлемым, вы продолжаете пошаговое выполнение кода до следующей строки, которая изменяет output.

  4. При следующем изменении output производится запись в член pos.

    Значение "output.pos" было обнулено

    В этот раз, значение члена pos — все нули — кажется подозрительным.Далее необходимо определить, как получилось, что output.pos имеет значение, состоящее из одних нулей.

  5. Заметьте, что output.pos принимает его значение из переменной с именем temp.В предыдущей строке можно увидеть, что значение temp — это результат умножения его предыдущего значения на константу, которая называется projection.Возникают подозрения, что подозрительное значение temp — результат умножения.При наведении указателя на projection вы заметите, что его значение также все нули.

    Матрица проекций содержит недопустимое преобразование

    В этом сценарии анализ указывает, что подозрительное значение temp с наибольшей вероятностью вызвано умножением на projection, и поскольку projection — константа, которая должна содержать матрицу проекции, известно, что это значение не должно содержать только нули.

После определения, что константа HLSL projection— переданная в шейдер приложением— была источником проблемы, на следующем этапе необходимо найти место в исходном коде приложения, где заполнен постоянный буфер.Можно использовать Стек вызовов событий графики чтобы найти это расположение.

Поиск, где константа установлена в исходном коде приложения

  1. При необходимости откройте окно Стек вызовов событий графики.На панели инструментов Диагностика графики выберите Стек вызовов событий графики.

  2. Перейдите вверх по стеку вызовов в исходный код своего приложения.В окне Стек вызовов событий графики выберите самый верхний вызов, чтобы узнать, будет ли там заполняться постоянный буфер.Если это не так, продолжайте двигаться вверх по стеку вызовов до тех пор, пока не найдете место заполнения.В этом сценарии будет обнаружено, что постоянный буфер заполняется (с использованием API Direct3D UpdateSubresource) вверх по стеку вызовов в функции с именем MarbleMaze::Render, а его значение берется из объекта постоянного буфера с именем m_marbleConstantBufferData:

    Код, устанавливающий постоянный буфер объекта

    СоветСовет

    Если одновременно выполняется отладка приложения, можно установить точку останова в этом месте, и она сработает при отрисовке следующего кадра.Затем можно проверить элементы m_marbleConstantBufferData, чтобы подтвердить, что значение элемента projection установлено на все нули, когда буфер констант заполнен.

После того, как вы найдете место, где заполняется постоянный буфер, и обнаружите, что его значения поступают из переменной m_marbleConstantBufferData, следующим шагом является определение, где член m_marbleConstantBufferData.projection устанавливается на все нули.Можно использовать Найти все ссылки, чтобы быстро просмотреть код, который изменяет значение m_marbleConstantBufferData.projection.

Поиск, где член проекции установлен в исходном коде приложения

  1. Найти ссылки на m_marbleConstantBufferData.projection.Откройте контекстное меню для переменной m_marbleConstantBufferData и выберите Найти все ссылки.

  2. Чтобы перейти к расположению линии в исходном коде приложения, в которой элемент projection изменен, выберите эту линию в окне Результаты поиска символа.Поскольку первый результат, изменяющий член проекции, может не являться причиной проблемы, может потребоваться изучить несколько областей исходного кода приложения.

После того, как вы найдете место, где установлено m_marbleConstantBufferData.projection, можно просмотреть окружающий исходный код, чтобы определить причину неверного значения.В этом сценарии будет обнаружено, что значение m_marbleConstantBufferData.projection установлено на локальную переменную с именем projection до того, как она была инициализирована со значением, которое задается кодом m_camera->GetProjection(&projection); на следующей линии.

Проекция шарика задается до инициализации

Чтобы устранить проблему, необходимо переместить строку кода, которая задает значение m_marbleConstantBufferData.projection, после строки, которая инициализирует значение локальной переменной projection.

Исправленный исходный код C++

После фиксирования кода можно перестроить его и снова запустите приложение, чтобы определить, что проблема отрисовки разрешена:

Объект появился.