Быстропост про загрузку классов и CDO в Unreal Engine.
Стандартное предупреждение: этот пост — не истина в последней инстанции. Я всё ещё изучаю связанную с темой часть кода, и по мере разбора либо обновлю пост, либо напишу новый. Сейчас цель — развлечение и небольшие размышления по интересной, но редко обсуждаемой теме: загрузка классов и CDO (Class Default Object) в Unreal Engine.
В Unreal есть понятие Soft Reference (мягкая ссылка) — это ссылка на объект, которая не приводит к его немедленной загрузке. Система загрузки в UE работает так: движок сначала подгружает корневые объекты (например, всё, что на сцене), затем рекурсивно загружает всё, на что они ссылаются напрямую. Это делается, чтобы сократить количество подгрузок во время симуляции — что логично. Soft Ref позволяет избежать этой каскадной загрузки и вручную управлять, когда объект появится в памяти. Обычно Soft Ref используют для тяжелых ассетов — текстур, моделей, и т.п., которые не нужно держать в памяти всё время. Справедливости ради, сами Ref в виде переменной являются лишь частью системы зависимостей Unreal Engine, однако это тема для следующей статьи.
Но вот интересный момент: Soft Ref может быть не только на объект, но и на класс. И это, на мой взгляд, создаёт интересное логическое противоречие. Смотрите: класс — по сути, статическая единица. Он компилируется заранее, загружается вместе с проектом, и на его основе создаются объекты. Более того, каждый класс в UE имеет свой CDO — экземпляр по умолчанию, с которого копируются значения при спавне. Классы C++ вообще живут с момента запуска и висят в памяти всегда. Так зачем вообще их загружать? Что именно считается «загрузкой» в таком случае? Я проверял это экспериментально — и да, загрузка классов действительно "срабатывает", даже если класс уже есть. Но в чём суть?
Кратко - не совсем. И вот почему.
Ответ — в нюансах. Во-первых, в UE класс — не всегда статичен. C++-классы действительно загружаются на этапе компиляции и не выгружаются, но вот Blueprint-классы работают иначе. Они загружаются во время исполнения, и именно для них soft-ссылки реально влияют на процесс. Когда вы загружаете Blueprint-класс по SoftRef, в этот момент происходит его активация: создаётся CDO, запускается логика конструктора и подключается вся графическая начинка. Причём CDO создаётся именно в момент загрузки, а не заранее. Из любопытного - ам же любой объект подключается к Gabrage Collector с помощью NonObjectGabrageCollector, который, в отличие от написанного в документации, используется самим UObject, и именно он выступает основой для сборки.
Важно и то, что при загрузке класса зачастую загружаются и его ресурсы — текстуры, меши и т.д. Это и есть основная причина, по которой загрузка классов может занимать время. Сам по себе класс — лёгкий, но его CDO может потянуть за собой кучу тяжелого контента. Так что да, даже загрузка класса может оказаться «тяжёлой» — просто не из-за кода, а из-за того, что за ним стоит.
Поэтому вопрос — стоит ли вообще использовать Soft-загрузку классов? Ответ: редко, но иногда нужно. Как правило, это касается систем, которые изолированы от основного кода, но при этом содержат полезную информацию, которую нужно подгружать по требованию. Типичный пример — игровые магазины, где вы не хотите сразу грузить все товары, но хотите быстро показать карточку при открытии. В остальных случаях код слишком взаимосвязан, и проще использовать Soft-загрузку инстансов (а не классов), или вообще держать всё в памяти, если это критичный геймплейный элемент.