RenderDoc — графический отладчик для DirectX11 от Crytek

Как вы, возможно, знаете в мире Windows для рисования графики часто используется DirectX. В последних версиях (10, 11.x) библиотека серьёзно шагнула вперёд и именно на них построены движки многих современных игр. Кроме того, DirectX используется не только в играх — сам интерфейс ОС Windows тоже с непомню-какой версии (Vista?) рисуется через него, да и казалось-бы не сильно связанные с графикой программы, желая увеличить производительность и плавность зума\скрола переходят на последние версии DirectX. Так некоторое время назад на DirectX11 перешел рендер Google Chrome (вроде бы с версии 36).Когда-то во времена Windows 95 и Pentium II была такая шутка, что чем медленнее компьютер — тем лучше можно понять работу операционной системы — невооруженным глазом видно в каком порядке прорисовываются элементы окон, обрабатываются события. Сегодня для подобных целей относительно DirectX есть отдельные инструменты — графические отладчики, позволяющие понять, как именно рисуется каждый пиксель каждого кадра, какие операции выполняет движок DirectX, какие ресурсы он использует, насколько быстро и правильно всё работает. Один из таких инструментов — RenderDoc от компании Crytek мы сегодня и рассмотрим. А в качестве примера разберём уже упомянутый выше новый рендер Google Chrome.

e84e0e872fd84356a4c34fd09bc4683d.png

Прежде всего — почему я говорю о RenderDoc? Есть немало аналогичных инструментов:

Все они — очень хороши. Но:

PIX — уже устарел, не развивается и не работает на последних ОС инструменты от Intel\AMD\Nvidia — сделаны в духе «вот этими утилитами можно посмотреть, как офигительно классно и быстро работает DirectX на наших видеокартах», при этом некоторые прикладные, рутинные задачи отладки в них делать не очень удобно Visual Studio — заточена именно под разработку своего софта, а не анализ чужого Что касается RenderDoc, то это:

opensource, активно развивается заточен на последние версии Windows и DirectX11 продукт от профессионалов 3D-разработки — компании, создавшей движок CryEngine со всей сопутствующей обвязкой пропитан практическим подходом к разработке и отладке — не знаю, как это лучше объяснить, но вот к примеру когда только задумываешься о том, что «хм, было бы неплохо в этом вызове увидеть вот эту информацию в вот такой-вот форме» внезапно оказывается, что RenderDoc показывает именно эту информацию, именно в этом вызове и именно в нужной форме. Видно, что инструмент живёт и полируется в руках Crytek. вообще единственный продукт, который понял от меня, что я хочу отлаживать отрисовку графики в дочернем процессе Chrome, запущенного из родительского, в котором никакой графики нет вообще. Казалось бы, лаунчер — простейшая вещь, используется во многих играх, но вот только RenderDoc дошел до необходимости его поддержки в отладчике. Итак, качаем RenderDoc, устанавливаем. Запускаем, видим главное окно. Открываем в меню Tools→Options и указываем папку, в которую будут складываться временные файлы (дампы).

a929c9c5a14d4926920c9b01c66044c7.png

Теперь нам нужно запустить под отладчиком RenderDoc приложение, которое мы хотим отлаживать. Для этого на вкладке «Capture Executable» вводим путь к Chrome, его рабочую папку. Здесь есть несколько интересных моментов. Графика в Хроме рисуется в отдельном дочернем процессе, его можно определить запустив Process Hacker и найдя среди всех запущенных процессов chrome.exe тот, у которого среди параметров командной строки имеется флаг --type=gpu-process.

d108540360df477fb7dcefc2d05f713a.png

Мы не можем запустить этот процесс напрямую, поэтому мы должны запустить главный процесс Chrome, указав в RenderDoc, что хотим отслеживать вызовы DirectX-функций в том числе среди дочерних процессов (галочка Hook Into Children)

По-умолчанию дочерние процессы Chrome «живут» в песочнице — имеют низкий Integrity Level, который препятствует их взаимодействию с файловой системой, другими процессами и общими ресурсами ОС. Таким образом, если мы просто запустим Chrome, то RenderDoc не будет в состоянии взаимодействовать с процессом, в котором рисуется графика. Для этого есть хак — Chrome нужно запускать со специальным флагом --no-sandbox, который отключает «песочницу» Chrome.

Поскольку нам интересно вообще всё, что будет происходить по ходу рисования графики — включаем побольше разных полезных галочек. Также мы можем сразу указать, какой по счету фрейм мы хотим захватить (для этого есть галочка Queue Capture Of Frame #), а можем уже в приложении нажать кнопку PrintScreen, чтобы создать дамп для текущего кадра

В итоге вкладка Capture Executable у нас будет выглядеть вот так: fd84627558ea48d1a130eb33638324d3.png

Жмём кнопку Capture, запускается Chrome.436ac4786fbb42c3aa1736ab7883547b.png

В верхней части окна мы видим некую отладочную информацию, которая говорит нам как-минимум о нескольких вещах:

Chrome действительно использует DirectX11 для рисования своего окна (причём целого окна: заголовка, тулбаров и контента страницы) RenderDoc успешно «прицепился» к Chrome Теперь можно открыть в Chrome что-нибудь и нажать PrintScreen. В верхней части окна Chrome появится надпись о созданном скриншоте. Всё, Chrome можно закрывать, а в папке, которую вы указали в настройках должен появиться файл с расширением rdc. Это и есть наш дамп. Открываем его через «File→Open Log» и видим примерно вот такую картину: aabe69d69a9f4cab9468544666465f2e.png

В верхней части окна находится «таймлайн» — временная линия, на которой отмечены этапы рисования данного кадра. Их может быть до нескольких десятков (если вы их не видите — кликните по плюсику в левом верхнем углу окна «Timeline»). Те же этапы отмечены в панели «Event Browser» в левой части окна. Кликая по таймлайну или по событиям в «Event Browser» мы можем переходить к разным моментам по ходу рисования кадра.

Корневой узел дерева событий называется «Frame #N» и показывает, какой это кадр от момента инициализации DirectX в приложении. Далее следует мета-узел «Frame start» указывающий на момент начала рисования данного кадра (никаких реальных вызовов DirectX-методов к нему не привязано). Далее мы видим три узла: «Colour Pass #1 (1 Targets)», «Draw (4)» и «Present ()». Из этого мы можем понять, что всё рисование контента окна хрома проходит в несколько этапов:

Сначала всё рисуется в некую промежуточную текстуру (этап «Colour Pass #1 (1 Targets)») Затем вызов метода «Draw (4)» переносит содержание этой текстуры в Swap Chain Backbuffer И наконец метод Present вызывает отображение бэкбуфера на экран Как видите, этап рисование в промежуточную текстуру в дереве можно раскрыть и мы увидим моменты рисования отдельных элементов окна Хрома.de513799d12a4f679cd0dee55276124f.png

Первым делом текстура очищается (вызов ClearRenderTargetView). Затем следует много этапов под названием «DrawIndexed (6)». 6 — это количество точек, ограничивающих область, рисуемую на данном этапе. То, что их 6, наталкивает на мысль, что это 2 треугольника, составляющих прямоугольник. Давайте выберем один их этапов «DrawIndexed (6)» (не первый, но и не последний) и рассмотрим его повнимательнее.

Начнём с вкладки «Pipeline State»cf1c48c5d6f24e2fa7b447a4defaada1.png

Как вы, возможно, знаете, в DirectX11 используется понятие «Pipelene» — это набор из нескольких последовательных операций, предназначенных для формирования окончательного кадра. Начинается пайплайн с этапа Input Assembler — здесь мы предоставляем все необходимые входные данные по вершинным, индексным и константным буферам, которые могут в дальнейшем понадобиться для рассчёта что и куда нужно будет рисовать. Далее следуют этапы обработки входных данных различными типами шейдеров и последняя фаза — Output Merger, в которой графика компонуется и выводится туда, куда должна быть выведена.

В окне Pipeline State мы можем кликнуть по любой стадии пайплайна и увидеть:

Что пришло на вход стадии (буфер, шейдер, текстура), общую информацию об этом объекте Кликнув по зелёной стрелочке справа от входного параметра мы можем открыть окно с более полной информацией. Для буфера это его полное содержание, для текстуры — её картинка, для шейдера — его скомпилированный байткод. Для стадии Input Assembler мы также можем кликнуть по большой кнопке «Mesh» и посмотреть координаты точек, образующих рисуемую на данном этапе область. Здесь ещё раз можно убедиться, что рисуем мы именно прямоугольник. Перейдя в окно текстур, мы можем увидеть используемые при рисовании данного кадра текстуры: 13c2b7aa1b2d4344808e101573d86ec8.png

В тулбаре «PS Resources» мы видим текстуру (ы), которая будет рисоваться на данном этапе, а в тулбаре «OM Targets» — текстуру, куда будет происходить отрисовка. Оставаясь в окне текстур, можно покликать по этапам отрисовке сверху или слева — и мы увидим, что Хром рисует своё окно текстурами размером 256×256 пикселей. Начинает он с нижней части окна, потом рисует боковые края и затем — заголовок окна с тулбарами. После этого Хром приступает к рисованию контента вкладки (опять-таки кусочками по 256×256 пикселей). Потом рисуются лежащие «поверх» контента объекты — видео, флеш-баннеры, всплывающие подсказки. На последних этапах рисуются скроллбар и его ползунок. Теперь текстура готова к рисованию в бэкбуфер.

Что ещё интересного умеет RenderDocПоказывать API вызываемых DirectX-методов для каждого этапа отрисовки1831576ed2814b36b8fad6d05468e393.pngНевероятно полезная вещь. А учитывая, что видны не только названия методов, но и их параметры — это вообще красота.Показывать колстек (откуда в коде был вызван тот или иной DirectX-метод)Правда, для этого нужно подсунуть программе pdb-файл (который у вас есть только если вы сами — автор отлаживаемого кода). Весьма полезно для отладки своих программ, полностью бесполезно для анализа чужих.

Дебажить шейдерыДля вершинных шейдеров отладка начинается в окне Mesh Output с клика правой кнопкой мыши по интересующей вершине.5a784ae64f74455c94c7ac2dd50b1871.png

Для пикселей — в окне текстур, где на интересующем пикселе нужно кликнуть правой кнопкой мыши и кликнуть в тулбаре «Pixel Content» кнопку «Debug this Pixel»bbe3c7d2362b4c2b9a3d87ea944f108d.png

Вот такой полезный инструмент RenderDoc.Удачной вам отладки графики.

© Habrahabr.ru