o2 dev
108 subscribers
49 photos
4 videos
25 files
54 links
About o2 engine development
Download Telegram
This media is not supported in your browser
VIEW IN TELEGRAM
навайбкодил. Питон, ни одной строчки руками не набрал. Но я знаю как должно работать внутри, все по моей инструкции. Страшно 🥲 или нет?
штош, что в итоге навайбкодил. Кстати, использовал новую-свежу модель GPT-5. Могу поделиться впечатлениями, они довольно неоднозначны
CodeGraphViewerTool.zip
166 KB
тулзу на питоне сгенерировал почти без ручных правок кода. Сгенеренный код довольно хорош. В целом llm с питоном и раньше справлялся лучше чем с С++, но тут я бы сам отревьюил код как "хороший"

помогать приходилось обильно, все классические грабли - что-то llm забывает, что-то делает не верно. Но в целом я действительно давал инструкции и у меня получилось слепить тулзу

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

приложу архивчик с сорцами, если интересно
большой проблемой стала проихводительность питона. Дело в том, что я пытался анализировать свой рабочий проект - игру homescapes. Она довольно большая )

и если тестовый пример с десятью сущностями работали прекрасно, то на 10 тысячах стало все прям печально.

хорошая новость в том, что через llm получилось ускорить на порядки. Местами конечно очевидные оптимизации, местами приходилось подсказывать, но например прикрутить jit компиляцию он предложил сам, а так же несколько либ ускоряющих перфоманс. Что порадовало

плохая новость в том, что кадр все равно обсчитывает очень долго - полторы секунды - и тулза совсем бесполезна

поэтому я пошел на следующую итерацию, сделать тулзу на С++
и здесь я сразу решил с ноги зайти и просто написать что за тулзу я хочу. Да да, одним промтом, вот прям из коробки.
За основу я взял демо-проект imgui со своим прикрученным профайлером

Вот первый промт:
я хочу сделать графическую утилиту, которая сканирует исходники С++ в нескольких папках, строит зависимости, и отображает с помощью imgui в графическом виде эти зависимости. Графически эти зависимости кластеризуют связанные сущности (исходники), с помощью физических законов. Сущности представлены в виде цветных кружков, зависимости (или связи) в виде линий.

Зависимости строятся на 2х уровнях:
- иерархия папок и подпапок: для каждой директории создается сущность директории, дочерняя директория зависит от родительской. Внутри директории есть список сущностей - файлов в ней
- зависимости между исходниками С++ через #include: из каждого исходника парсится список инклюдов, ищутся файлы-исходники по путям и между ними создается связь

Работа физики:
- для этого сущности расположены в 2д мире, у каждого есть позиция
- между ними есть зависимости- связи, которые задают некое расстояние между сущностями
- сущности стараются оттолкнуться от билзлижащих сущностей
- используется интеграция верле и разрешение связей как в научной работе якобсена из hitman codename 47
- при зажимании левой кнопки мыши умеет захватывать сущность чтобы ее перемещать вслед за курсором

Графика:
- используется imgui Для отображения. В текущем исходнике Main.cpp создается дополнительное окно, в котором отображается вся графика: сущности и связи
- сущности - это разноцветные круги
- связи - это линнии между ними
- существует камера, которой можно управлять: скролл (перемещение) с зажатой правой кнопкой мыши, зум с помощью колесика
- наведение курсора на сущность включает ее подсветку: отображается имя исходника, а так же связь с родителем и детьми
- для профилирования используется nano profiler: работа физики и графики

что нужно сделать:
- завести json конфиг с параметрами: исходные директории для сканирования, настройки графики и физики. Работает как отдельный класс
- сделать класс парсера исходников с многопоточностью: берет исходные директории из json конфига и рекурсивно сканирует их в поисках C++ исходников .h/.cpp, парсит #include, связывает сущности, создает сущности для всех директорий
- физический движок, оптимизированный для вычисления 10000 сущностей и 30000 связей. Инициализируется из парсера исходников, сохраняя ссылки на исходные сущности. ПРоводит симуляцию мира и физики: интегрирует позиции, разрешает связи, расталкивает близлижащие сущности
- графическое отображение. Содержит камеру, обрабатывает инпут пользователя, отображает сущности и связи
- главный класс утилиты: читает конфиг, парсит исходники, инициализирует физику и графику, запускает игровой цикл апдейта фрейма - обонвление физики, обработка инпута и отрисовка

Разделяй вышеописанное на классы в разных файлах, рядом с Main.cpp. Используй для всего стандартную библиотеку stl и imgui. Не используй ничего кастомного, никаких скриптов, только С++
cursor_c.md
767.4 KB
иии... тулза сгенерилась и скомпилилась сразу. Но естественно не совсем так, как хотелось.
Поэтому дальше допиливалось напильником, правился код стайл, в общем вот вся история общения с С++ версией

Проблемы были все те же: что-то забывал, что-то делал не так, но в код руками я почти не лез
This media is not supported in your browser
VIEW IN TELEGRAM
В результате получилась тулза, значительно быстрее по перфомансу, FPS получается в районе 20ти, что неплохо для 10 тыс сущностей, 30к связей и полумиллиона пар расталкивания

Здесь я практически не просил оптимизировать, некоторые алгоритмы llm выдал сразу оптимизированными. Например, для расталкивания сразу применил кластеризацию пространства, чтобы искать близко лежащие пары сущностей

Но сама тулза в итоге бесполезна. Физическая система не справляется с разрешением противоречий в системе зависимостей, в итоге она либо взрывается, либо уплывает куда-то в сторону. Думаю это все можно доработать, но уже не очень много смысла в этом, проще посмотреть структуру папок и директорий чтобы понять структуру.

Идея была в том, чтобы связать и кластеризовать зависящие друг от друга логические куски игры. Но, оказалось что все связано со всем, и все превращается в кашу
Ах да, что в итоге с gpt-5. Честно говоря я не увидел особых отличий в лучшую сторону от claude-4, который использовал все последнее время. Честно говоря даже местами получается хуже. Я пробовал и на других задачах, буквальо спрашивая одно и то же у двух моделей, и claude был понятнее, предсказуемее и... быстрее! gpt-5 очень долго думает, около минуты, чтобы предпринять действие. Честно говоря мне и самому минуты хватает чтобы разобраться, особенно в хорошо знакомом мне коде

Поэтому, пока что откатываюсь к более старой и проверенной claude
Удобства отладки: .natvis && lldb formatters

Привет! Немного коснемся темы удобства при отладке С++. А точнее о том, как удобно смотреть данные переменных и структур в IDE при дебаге
Порой в реальных проектах классы становятся сложными, в них много полей и даже бывает что они просто плохо написаны их сложно смотреть при отладке. Тебе нужно увидеть значение какой-нибудь переменной, а до нее листать и листать, или разворачивать кучу вложенностей

Поверх этого еще могут быть оптимизационные ухищрения - кастомные коллекции, union'ы и другие страшные штуки, которые в отладке просто не читаемы

Еще бывают довольно простые типы, например математические вектора (x, y, z), цвет (RGBA), UID и тд, в общем что-то простое, но чтобы узнать содержимое - нужно разворачивать. Хотя данные можно было бы уместить в одну строку удобным образом
Для всего этого есть специальные форматтеры, подключаемые к IDE, чтобы упростить вывод сложных объектов. Они говорят IDE в каком виде представлять ту или иную структуру, условное форматирование и форматирование превью объекта - то что может вывести XYZ или RGBA прямо в строке переменной.

Это и есть те самые natvis и lldb formatters. Есть и другие, но я пока использовал только их, поэтому расскажу про них 😁

- natvis - это инструмент чисто под msvs. Работает как конфиг, оперирующий полями объекта. Синтаксис довольно не простой, но возможности довольно обширные: условное форматирование, разворачивание сложных коллекций а-ля деревья/списки

- lldb formatters - это api для python, который позволяет писать скрипты для форматирования отображения в lldb. Тут все просто - пишешь скрипт под определенное API отладчика, внутри делаешь что хочешь. Но есть проблемы с перфомансом
Вывод краткого описания
Полезно для каких-то простых структур, содержимое которых можно уместить в одной строке: цвет RGBA, координаты вектора XYZ, интернированные строки и т.п.

в natvis это делается через тег DisplayString, внутри которого можно форматировать строку из доступных полей класса:
<Type Name="o2::Vertex">
<DisplayString>{{ x: { x } y: { y } z: { z } c: { color } u: { tu } v: { tv }}</DisplayString>
</Type>


Так же можно задавать условие отображения того или иного варианта, что весьма удобно. Вот классический пример для умного указателя: если поинтер не пустой, то показываем его, иначе "empty"
<DisplayString Condition="(mPtr != 0)">{ mPtr }</DisplayString>
<DisplayString Condition="(mPtr == 0)">empty</DisplayString>


в lldb formatter'ах чуть сложнее. Сначала нужно зарегать хендлер для типа:
debugger.HandleCommand('type summary add -F o2_lldb_formatters.vertex_summary "o2::Vertex"')


затем реализовать его:
def get_child_value(valobj, name):
"""Helper to safely get child value"""
try:
child = valobj.GetChildMemberWithName(name)
if child.IsValid():
return child.GetValue()
return "?"
except:
return "?"


def vertex_summary(valobj, internal_dict):
"""Format o2::Vertex"""
x = get_child_value(valobj, "x")
y = get_child_value(valobj, "y")
z = get_child_value(valobj, "z")
color = get_child_value(valobj, "color")
tu = get_child_value(valobj, "tu")
tv = get_child_value(valobj, "tv")
return f"{{ x: {x} y: {y} z: {z} c: {color} u: {tu} v: {tv} }}"

Относительно xml это выглядит понятнее и гибче
Построение дочерних нод и структуры

Когда нужно какие-то поля показать, а какие-то нет. Использовать более читаемые слова или изменить порядок

В natvis это делается с помощью тегов <Expand>, <Synthetic> и <Item>:
- <Expand> - список полей при разворачивании
- <Item> - конкретное поле класса
- <Synthetic> - сложное синтезированное поле класса, которое может быть коллекцией
- <ExpandedItem> - развернутые поля определенного дочернего поля класса

Рассмотри пару примеров. Здесь просто перечисляем некоторые поля класса. Обратите внимание на само описание типа - оно задается для шаблонного типа, через &lt;*&gt; (да, вот такой вот незамысловатый формат с кодированием угловых скобок в xml)
<Type Name="o2::FieldInfo&lt;*&gt;">
<DisplayString>{{ name={ mName } value={ mFieldRef } }}</DisplayString>
<Expand>
<Item Name="name">mName</Item>
<Item Name="value">mFieldRef</Item>
<Item Name="owner">mOwner</Item>
</Expand>
</Type>


Или вот пример посложнее с синтезированными полями. Здесь описан тип меша с кастомными коллекциями вершин и индексов. IDE передаются два параметра: Size - размер коллекции, и ValuePointer - откуда начинать отсчет
<Type Name="o2::Mesh">
<DisplayString>{{ vertx = { vertexCount }/{ mMaxVertexCount } poly = { polyCount }/{ mMaxPolyCount } tex = { mTexture.mTexture } }}</DisplayString>
<Expand>
<Item Name="texture" ExcludeView="simple">mTexture.mTexture</Item
<Synthetic Name="verticies">
<DisplayString>{ vertexCount }/{ mMaxVertexCount }</DisplayString>
<Expand>
<ArrayItems>
<Size>vertexCount</Size>
<ValuePointer>vertices</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic
<Synthetic Name="indexes">
<DisplayString>{ polyCount }/{ mMaxPolyCount }</DisplayString>
<Expand>
<ArrayItems>
<Size>polyCount*3</Size>
<ValuePointer>indexes</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic
</Expand>
</Type>


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

Здесь в моем движке лежит список .natvis описаний типов, там есть довольно сложные описания типа актора
https://github.com/o2-engine/o2/blob/master/Framework/Platforms/Windows/Framework.natvis
Для lldb все сильно проще - пишется скрипт, который формирует нужную структуру и отображение. Но у меня не получилось это хорошо завести из-за проблем перфоманса. Эти форматтеры вешали отладчик намертво, в итоге остались только простые summary обработчики

https://github.com/o2-engine/o2/blob/master/Framework/Platforms/o2_lldb_formatters.py