u/int7bh

I wrote a vulkan visualizer for my crate I probably shouldn't release
▲ 7 r/rust

I wrote a vulkan visualizer for my crate I probably shouldn't release

So, three months ago I started writing a Vulkan wrapper alongside my Super Hexagon clone because every existing one made me angry for different reasons. ash is too low level (although i love it), vulkano makes choices for you, wgpu is a different abstraction entirely. Fine, i'll do it myself, how hard can it be?

Turns out hard. But that's not the point... and I made it zero-dependency. It relies on ash only. Without egui, winit or etc. Pure FFI, pure ash.

The point is two weeks ago I was debugging a missing barrier between a compute write and a fragment read, and I sat in renderdoc for forty minutes scrolling through a frame capture trying to figure out which of my 60 dispatches was the one. and I thought, you know what, I have all this state in my own library already, I just need to render it somewhere.

So now I have an ipc (im tired pressing shift sorry) ring buffer between my lib and a separate viewer process, and the viewer draws:

  • A frame graph that updates live. Sugiyama layout because i tried force-directed first and it looked like spaghetti. Nodes for passes and resources, edges for dependencies, it animates when things change so you can see what got added.
  • A sync dag with queue lanes and submission bars. Detects deadlock cycles between queues. tints lanes red when it finds one. Found three real bugs in my own code with this, which is either good (it works) or bad (i had three deadlock bugs).
  • Memory layout. Horizontal strips per VkDeviceMemory with colored rectangles for allocations. Hover for call site and age. Heap budget bars on top. Nothing groundbreaking, but it's right there in the same window as everything else.
  • A pass profiler and a separate gpu timeline. CPU side shows how long recording took, gpu side shows actual gpu timestamps split by stage. They don't align in time because the clocks aren't correlated without vkGetCalibratedTimestamps which i didn't bother wiring up.
  • Validation log with filtering, search, click-to-pin. When a vl warning mentions an image, the node for that image gets a little red exclamation badge.
  • Canary monitor for buffer overflows when using my hardened allocator. shows hex diff of expected vs actual guard bytes with carets under the ones that differ. You can tell from the pattern what wrote there: 0xCD means uninit, 0xDD means freed memory, ASCII means a string ended up in your vertex buffer somehow.
  • Determinism checker. Runs the same workload N times, hashes outputs, table goes red if hashes diverge. opens a bmp showing diff pixels. caught one (1) actual atomic race in my code so i guess it works.
  • Tabs at the top, draggable, pinnable, persisted across runs. a scrubber for rewinding the last 16k events. ctrl+p freezes the stream so you can stare at a frame.
  • The renderer is a cpu rasterizer that copies into a swapchain image. no pipelines, no shaders. I did this partly because i didn't want to write shaders for my debug tool and partly because if my main vulkan code is broken i still want the debug tool to render.

here's the thing though!

It's coupled to my own vulkan wrapper. The ipc protocol is mine, the event kinds are mine. someone on vulkano (why tho lmao) would need to write a bridge, someone on wgpu wouldn't benefit because wgpu hides most of what this shows. It's also windows only - rendering is portable but the window code is win32. and the viewer is one binary where every view shares an Rc<RefCell<World>>, which works fine but probably wouldn't survive a code review.

if you've shipped a niche tool like this, was the maintenance worth it?

btw yes i know about gpuopen rdna profiler. yes i tried it. yes it's better at what it does. mine does different things.

youtube.com
u/int7bh — 2 days ago

Продолжаю шествие бесполезного навоза. В этот раз я понтуюсь своим функционалом отладки из движка, все так же на чистейший Vulkan + Rust.

Стоило давно это сделать, просто как-то не задумывался. Но в принципе, теперь у моего крейта есть отдельный движок, где можно все отлаживать. Зачем вам это нужно? Понятия не имею.

TL;DR: написал визуализатор для своего Vulkan-движка чтобы не сдохнуть от рендердока, потому что я конченный дебил.

Короче. Каждый раз когда у меня что-то ломалось в графике, я открывал рендердок, ждал пока он прожуёт 38^(10000мб) дамп, искал в нём один забытый барьер и закрывал рендердок. Потом это повторялось через час. В какой-то момент я просто сел и написал свой инструмент потому что всё равно быстрее.

Что есть сейчас.

  • Граф кадра. Все ноды (passes, resources, submits) и связи между ними. Лейаут через Sugiyama чтобы 200 нод не превращались в кашу. Когда что-то добавляется или удаляется, граф плавно перестраивается, а не моргает.
  • Sync DAG. Лайны очередей, бары сабмитов, детектор циклов и орфан-семафоров. Если compute ждёт graphics а graphics ждёт compute - я это вижу до того как получу смерть устройства.
  • Память. Полоски VkDeviceMemory с цветными квадратиками аллокаций. Наводишь - видно вызовы, размер, возраст. Сверху бары по хипам. Утечку видно сразу, не надо ждать пока прод упадёт по OOM.
  • Профилировщик пассов и жэпэу-таймлайном отдельно. Первый показывает сколько проц писал команды, второй настоящие гпу-таймстемпы. Раздельно потому что таймстемп-период это ад и без vkGetCalibratedTimestamps они не коррелируют, врать не буду.
  • Валидация. Лог с фильтрами по серьезности (severity), поиском, автопрыжком на ошибку. Связан с графом - если варнинг про image layout, нода с этим имеджем подсвечивается красной точкой.
  • Мониторинг канарейки (гуард бэндов). Интеграция с hardened allocator из моего крейта - читай hardened malloc из GrapheneOS, который я прикрутил к Vulkan. Когда что-то пишет за границы буфера, видно паттерн актуальных байт против ожидаемых, с подсветкой отличающихся. 0xCD - кто-то прочитал неинит, 0xDD - UAF, ASCII - строка улетела не туда. Полезно.
  • Детерминизм. Таблица ранов с агрегатным хешем. Если десятый ран отличается от первых девяти - строка красная, кнопка открывает диф-битмапу с покрашенными в красное пикселями которые разошлись. Невидимые гонки атомиков становятся видимыми.
  • Табы наверху, можно открывать несколько одинаковых, перетаскивать, пинить. Сохраняется между запусками. Скруббер для прокрутки назад на 16к последних событий, Ctrl+P замораживает поток. Снапшоттинг в файл - можно прислать кому-то и тот откроет, если не взорвется.

Архитектура примитивная до неприличия. Кольцевая память между моей либой и вьювером, 256 байт на запись, лоззи при переполнении. Продюсер пишет, вьювер читает. Падает один - другой ждёт.

  • Рендер вообще без пайплайнов. CPU растеризатор пишет в BGRA буфер, потом vkCmdCopyBufferToImage в свопчейны. Никаких шейдеров, никаких VE внутри инструмента валидации.

Чего нет.

  • Capture/replay отдельных кадров. Это рендердок, я туда не лезу.
  • ГПУ-сталлинг на уровне волн. Это Nsight и RGP.
  • Linux и macOS. Пока только винда, рендер платформонезависимый, но клей к отвратительным в реализации X11/Wayland не написан.

Хз, надо ли публиковать вообще, но если вдруг кому тут понадобится и кто точно такой же мазохист как и я - вылью на гх. А, нуида, все снова zero-dependency, кроме ash, разумеется, иначе с вулканом я там вообще не поработаю. А так... Ну, Win FFI)))

Если будете говорить что я переизобрёл <ээээээээнструмент> - я знаю, мне быстрее было написать чем учиться <эээээээээээнструменту>. Суп вилкой есть тоже можно.

*Да, Tracy я видел. Мне нужен был вулканоспецифичный аудит барьеров и трекинг дескрипторов, а не зоны таймлайна.

u/int7bh — 2 days ago