- ArrayMemoryManager - класс, что является базовой абстрактной системой управляющей SoA(Structure Of Arrays)-памятью. Все производные классы (NodeArrayMemoryManager и ObjecDataArrayMemoryManager) предоставляют список с байтами, необходимыми для каждого SoA указателя, что мы будем использовать.
В целом, данная абстрактная система создаёт более дружелюбную SIMD SoA-память. Она используется менеджерами Node и MovableObject.
Может обрабатывать ситуации очистки и нехватки памяти. Однако требуется слушатель для расширенной обработки (к примеру, NodeArrayMemoryManager) в противном же случае, если это была нехватка памяти, то будет вызвано исключение.
- NodeMemoryManager - основной публичный интерфейс, что управляет всем в NodeArrayMemoryManagers.
- ObjectDataMemoryManager - основной публичный интерфейс, что управляет всем в ObjectDataArrayMemoryManager.
Немного о *ArrayMemoryManager
У стандартного SceneManager может быть лишь по одному NodeArrayMemoryManager на каждый родительский Node-уровень.
- Корневой Node - это 0 родительский уровень.
- Дочерние Node от корневого - это 1 родительский уровень.
- Дочерние Node от дочернего - это 2 родительский уровень.
- И так далее.
И может быть лишь по одному ObjectDataArrayMemoryManager на каждую RenderQueue (рендер-очередь).
MovableObjects группируются по RenderQueue ID (начиная с 0) вместо родительских уровней.
SIMD согласованность
Используя одинарную точность SSE2, Ogre3d, обычно, обрабатывает 4 Node / объекта одновременно. Однако, если есть только 3 узла, то нам необходимо выделить память всё равно для 4 (ArrayMemoryManager этим сам займётся) и инициализировать значения по умолчанию, даже если они не используются. Это необходимо чтобы предотвратить получение NaN во время вычислений. Некоторые архитектуры замедляются, если один из элементов в регистре xmm содержит NaN.
Более того, null указатели являются невалидными, так что вместо них используются фиктивные (dummy) указатели.
SIMD согласованность очень важна как для стабильности, так и для производительности, и составляет 99% времени от всех задач менеджеров памяти.
Шаблоны проектирования Менеджеров Памяти
ArrayMemoryManagers работают с понятием слотов.
Когда Node запрашивает Transform - запрашивается слот. Когда MovableObject запрашивает ObjectData - запрашивается слот.
В SSE2 сборке 4 слота образуют блок. Количество слотов, необходимых для создания блока, зависит от значения макроса ARRAY_PACKED_REALS.
Слоты наиболее эффективны, когда они запрашиваются и освобождаются в порядке LIFO.
Когда порядок LIFO не соблюдается, выполнение освобождения (т. е. уничтожение Node) помещает слот в список. При запросе нового слота используются те, были помещены в список, и удаляются из него. Когда этот список становится слишком большим, выполняется очистка. Порог для очистки может быть изменен.
Очистка
Очистка происходит тогда, когда количество слотов "освобождённых" вне очереди LIFO становится слишком большим. Процесс очистки сместит память так, чтобы она снова была непрерывной.
Зачем? Если просто, то - производительность. Представьте себе следующий пример (предполагая, что ARRAY_PACKED_REALS = 4): пользователь создал 20 Node с именами от A до T:
ABCD EFGH IJKL MNOP QRST
Затем пользователь решает удалить все Node, кроме A K M; конечный слой памяти будет следующим:
A*** **** **K* M*** ****
Там, где * - пустой слот. При разборе массивов SoA (т. е. при обновлении Nodes сцены, обновлении MovableObjects AABB, frustum culling) все выполняется последовательно.
Код повторится 4 раза: обработаем A, затем ничего, затем K, затем M - двигаемся по блокам. Если ARRAY_PACKED_REALS будет 1, то код повторится 13 раз.
Это, очевидно, неэффективно. В процессе работы реального приложения эта проблема не повлияет особо на производительность, если есть только 4 Node, но если такая "фрагментация" происходит с тысячами Node - падение производительности будет ощутимым.
Очистка сместит все Node, чтобы сделать их непрерывными:
АКМ* **** **** **** ****
Следовательно, код будет циклически выполняться только 1 раз для сборок SSE (или 3 раза, если ARRAY_PACKED_REALS = 1).
Предварительное распределение памяти
Класс ArrayMemoryManagers предварительно выделяет фиксированное количество слотов (всегда округленных до кратного ARRAY_PACKED_REALS значения), указанных во время инициализации.
При достижении предела может произойти следующее:
- Размер буфера изменяется. Любой указатель на слот будет признан невалидным (т. е. удерживающий внешнюю ссылку на Transform::mPosition). Внутренние указатели Nodes и MovableObjects обновляются автоматически.
- Жесткий предел был достигнут (жесткий предел устанавливается во время инициализации, по умолчанию, он игнорируется). Когда это случится - менеджер памяти сгенерирует исключение.
Настройка менеджеров памяти
Примечание: на момент написания статьи менеджеры памяти не имеют прямого способа по настройки preallocations или частоты выполнения очистки.
Где RenderTarget::update? Почему получаю ошибки во Viewport?
Продвинутые пользователи, вероятно, привыкли к низкоуровневым манипуляциям с RenderTargets. Они их используют для настройки своих собственных Viewports и RenderTarget::update.
Это слишком низкий уровень. Вместо этого теперь предлагается настроить "Compositor nodes" и несколько рабочих пространств для выполнения рендеринга в несколько RT (RenderTarget), даже если это делается для собственных кастом-материалов. Новый Compositor гораздо более гибкий, чем старый (который был удален).
Viewports больше не связаны с камерами, поскольку они теперь не имеют состояния (они применяются для кэширования используемой в настоящее время камеры), и многие настройки, которые они использовали для хранения (цвет фона, настройки очистки и т. д.), были перемещены в Nodes. Смотрите CompositorPassClearDef и CompositorPassClear.
RenderTarget:: update исчез, поскольку обновление сцены рендеринга было разделено на два этапа: cull и render.
Если вы все еще хотите работать на низком уровне, то смотрите код в CompositorPassScene::execute, чтобы понять, как подготовить RenderTarget и отобразить его вручную. Но опять же, авторы настаивают на том, чтобы вы работали через Compositor.
Прошлые записи:
Буду благодарен за любую поддержку каналу:
https://money.yandex.ru/to/4100110976771904