Генерация и ECS
Наверное многие задумывались: "она говорит про ECS, DOTS, но показывает ООП код, что-то тут не так"?👍 И ведь правда, причем тут ECS?
Вся генерация, как я писала в прошлых постах, реализована на самом обычном ООП по множеству причин. Но есть также и ECS часть в моем процессе генерации - представление.
Все, что генерируется и видимо игроком, должно каким-либо образом рисоваться. Для этого я разбила представление окружающих объектов на две части:
- окружающие объекты без физики;
- окружающие объекты с физикой.
Почему именно такое распределение? Потому что объекты без физики я могу рисовать самостоятельно без создания самих объектов (GameObject или Entity). Мне нужно знать только что рисовать и где рисовать.
А вот объекты с физикой (или любые другие объекты, с которыми игрок может взаимодействовать) необходимо уже собирать из кусочков. Тут и компоненты отображения, и компоненты физики, и компоненты взаимодействия вместе с состоянием. А еще они должны быть представлены и на сервере!
По этой причине, на этапе процессинга сгенерированных данных я подготавливаю их в требуемом виде (например, меши ландшафта уже представлены в виде UnityEngine.Mesh) и отправляю в соответствующие ECS-системы. Не могу сказать, что именно такая связь правильная, возможно было бы лучше, если бы ECS-системы сами собирали информацию.🤷
ECS-системы, в свою очередь, создают из этих данных сущности с компонентами. Например, есть система, которая создает сущности чанков. Другая система - создает уже отображение этих чанков. Третья - коллайдеры, с ними кстати все не так просто оказалось в UECS, надеюсь когда-нибудь расскажу. И вот на выходе у меня уже не просто меши, а самые настоящие ентити с набором компонентов.
Тоже самое происходит и на серверной стороне, только в существенно меньшем масштабе, потому что на сервере визуализация не нужна. Поэтому там например создаются чанки только с коллайдерами, да и то не все, а только необходимый минимум для симуляции.
Наверное многие задумывались: "она говорит про ECS, DOTS, но показывает ООП код, что-то тут не так"?
Вся генерация, как я писала в прошлых постах, реализована на самом обычном ООП по множеству причин. Но есть также и ECS часть в моем процессе генерации - представление.
Все, что генерируется и видимо игроком, должно каким-либо образом рисоваться. Для этого я разбила представление окружающих объектов на две части:
- окружающие объекты без физики;
- окружающие объекты с физикой.
Почему именно такое распределение? Потому что объекты без физики я могу рисовать самостоятельно без создания самих объектов (GameObject или Entity). Мне нужно знать только что рисовать и где рисовать.
А вот объекты с физикой (или любые другие объекты, с которыми игрок может взаимодействовать) необходимо уже собирать из кусочков. Тут и компоненты отображения, и компоненты физики, и компоненты взаимодействия вместе с состоянием. А еще они должны быть представлены и на сервере!
По этой причине, на этапе процессинга сгенерированных данных я подготавливаю их в требуемом виде (например, меши ландшафта уже представлены в виде UnityEngine.Mesh) и отправляю в соответствующие ECS-системы. Не могу сказать, что именно такая связь правильная, возможно было бы лучше, если бы ECS-системы сами собирали информацию.
ECS-системы, в свою очередь, создают из этих данных сущности с компонентами. Например, есть система, которая создает сущности чанков. Другая система - создает уже отображение этих чанков. Третья - коллайдеры, с ними кстати все не так просто оказалось в UECS, надеюсь когда-нибудь расскажу. И вот на выходе у меня уже не просто меши, а самые настоящие ентити с набором компонентов.
Тоже самое происходит и на серверной стороне, только в существенно меньшем масштабе, потому что на сервере визуализация не нужна. Поэтому там например создаются чанки только с коллайдерами, да и то не все, а только необходимый минимум для симуляции.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤1
Добавляю поддержку лодов в BRG, алгоритм уже работает (но еще не протестировала до конца), начала интегрировать в проект, скоро будет видео)
Редактор конечно кривоватый, но и так сойдет пока что🫥
Редактор конечно кривоватый, но и так сойдет пока что
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15❤1
This media is not supported in your browser
VIEW IN TELEGRAM
Обещанное видео) Конечно тут кубики, с разными цветами, обозначающие уровень детализации 🤷
С LOD оказалось все чуть сложнее, чем я думала. Но все же победа вот-вот будет за мной! Осталось откинуть те кубики, которые слишком далеко от камеры👏
С LOD оказалось все чуть сложнее, чем я думала. Но все же победа вот-вот будет за мной! Осталось откинуть те кубики, которые слишком далеко от камеры
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17👍3
This media is not supported in your browser
VIEW IN TELEGRAM
А вот теперь есть и поддержка "пустых" лодов, которые не рендерят ничего 💃
Стоит заметить, что LOD выбирается не просто по дистанции до камеры, а по эвристике, определяющей, сколько объект занимает в процентном соотношении места на экране! Это позволяет более точно выбирать LOD и рисовать только то, что реально видно и имеет шанс быть замеченным (поэтому маленькие объекты даже в близи могут быть еще скрыты, тогда как большие - уже рисоваться).
Следующий шаг - добавление cross fade, чтобы лоды плавно смешивались.
Стоит заметить, что LOD выбирается не просто по дистанции до камеры, а по эвристике, определяющей, сколько объект занимает в процентном соотношении места на экране! Это позволяет более точно выбирать LOD и рисовать только то, что реально видно и имеет шанс быть замеченным (поэтому маленькие объекты даже в близи могут быть еще скрыты, тогда как большие - уже рисоваться).
Следующий шаг - добавление cross fade, чтобы лоды плавно смешивались.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥19❤3
Хочу немного рассказать, как я выбираю LOD. Это не очень сложно на самом деле, как бы не звучало в моем предыдущем посте 🤭
Самый простой способ выбирать LOD - зная позицию камеры и для каждого инстанса матрицу трансформации, можно взять дистанцию между ними. Потом сравнить дистанцию с теми значениями диапазонов, которые мы указали в редакторе. Получается линейная выборка, которая никак не учитывает размер объекта.
А размер объекта на самом деле очень важен. Цель LOD - уменьшить не только кол-во треугольников, но и кол-во инстансов. Маленькие объекты вдалеке человеку чаще не заметны, тогда зачем их рисовать?
Диапазоны расстояний в итоге заменяются на процентное соотношение относительно экрана. То есть при настройке LOD мы указываем уже не дистанцию, а процентное соотношение. Например, 100% - виден когда занимает весь экран, 60% - виден, когда занимает примерно 60% площади экрана, 15% - когда объект занимает совсем небольшую площадь экрана, то есть он или маленький или находится далеко от нас.
В BRG у нас на руках есть матрица трансформации (с поворотом и скейлом в том числе) для каждого инстанса, а также размер меша per LOD в world space. То есть вычислить фактический размер в world space - уже не проблема, для удобства берем самое большее значение по одной из осей.
Зная размер меша и зная процентное соотношение, можно вычислить для него диапазон уже дистанций:
Но дистанция от объекта до камеры тоже не просто
Самый простой способ выбирать LOD - зная позицию камеры и для каждого инстанса матрицу трансформации, можно взять дистанцию между ними. Потом сравнить дистанцию с теми значениями диапазонов, которые мы указали в редакторе. Получается линейная выборка, которая никак не учитывает размер объекта.
А размер объекта на самом деле очень важен. Цель LOD - уменьшить не только кол-во треугольников, но и кол-во инстансов. Маленькие объекты вдалеке человеку чаще не заметны, тогда зачем их рисовать?
Диапазоны расстояний в итоге заменяются на процентное соотношение относительно экрана. То есть при настройке LOD мы указываем уже не дистанцию, а процентное соотношение. Например, 100% - виден когда занимает весь экран, 60% - виден, когда занимает примерно 60% площади экрана, 15% - когда объект занимает совсем небольшую площадь экрана, то есть он или маленький или находится далеко от нас.
В BRG у нас на руках есть матрица трансформации (с поворотом и скейлом в том числе) для каждого инстанса, а также размер меша per LOD в world space. То есть вычислить фактический размер в world space - уже не проблема, для удобства берем самое большее значение по одной из осей.
Зная размер меша и зная процентное соотношение, можно вычислить для него диапазон уже дистанций:
worldSpaceSize / screenRelativeTransitionHeight[i]. С этими дистанциями уже можно сравнивать дистанцию от объекта до камеры.Но дистанция от объекта до камеры тоже не просто
math.distance, так как для перспективной камеры у нас есть искажение, а для ортогональной его нет. Также необходимо еще учитывать LOD Global Bias из QualitySettings,Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤3🔥2🦄1
Вот и LOD Cross Fade получился! 🙌
Есть о чем рассказать, есть интересные моменты😧 Завтра соберу все мысли и опишу в виде поста 💃
Хотя может даже в виде статьи?🚪
Есть о чем рассказать, есть интересные моменты
Хотя может даже в виде статьи?
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11🔥9
https://telegra.ph/Level-of-Detail-v-Unity-BRG-04-10
Написала статью про LOD в BRG🚪 Весь код можно найти в моем репозитории по BRG (MIT-лицензия) 😶 Документацию там тоже скоро обновлю.
Написала статью про LOD в BRG
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegraph
Level of Detail в Unity BRG
Что такое BRG Для того, чтобы описать что же такое Batch Renderer Group (BRG) нужна отдельная статья, поэтому тут я только кратко расскажу про основные его принципы и больше расскажу про интеграцию поддержки Level of Detail (LOD).
🔥28❤6👏3😍3❤🔥1
Media is too big
VIEW IN TELEGRAM
Видео с камушками! 🤩 (и с LOD вместе с CrossFade). 🙌
Как по мне, выглядит неплохо, очень нравится, что камушки плавненько появляются и исчезают, ради этого я и добавила поддержку LOD (а не ради оптимизации, как вы могли подумать☺️ )
Чтобы не делать много постов, скриншот из Frame Debugger приложу в комментариях🚪
Как по мне, выглядит неплохо, очень нравится, что камушки плавненько появляются и исчезают, ради этого я и добавила поддержку LOD (а не ради оптимизации, как вы могли подумать
Чтобы не делать много постов, скриншот из Frame Debugger приложу в комментариях
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17❤12
Media is too big
VIEW IN TELEGRAM
Протестировала на траве! 🥔
К сожалению не нашла в HDRP ассетах от Unity лоды для травы, они ее просто скрывают плавно по дистанции (ага, и десятки и сотни млн треугольников в итоге в кадре, хоть и не видно их). Пришлось также взять просто Lit шейдер, чтобы не писать сейчас поддержку для Cross Fade для их шейдера. В итоге трава не шевелится :(
Но ничего, то, что мы не видим, то не рисуется, а это главное!🙌 Хотя при этом трава сгенерирована и находится в памяти.
P.S.: Я думала, думала, и решила немного уточнить момент с графикой. Это не стиль будущей игры, и она не будет в реализме, поэтому я не работаю сейчас над визуальной составляющей (некоторые подвижки в эту сторону скоро начнутся). Это пока что просто технические решения и некоторые стресс тесты для них (и для меня😄 ).
К сожалению не нашла в HDRP ассетах от Unity лоды для травы, они ее просто скрывают плавно по дистанции (ага, и десятки и сотни млн треугольников в итоге в кадре, хоть и не видно их). Пришлось также взять просто Lit шейдер, чтобы не писать сейчас поддержку для Cross Fade для их шейдера. В итоге трава не шевелится :(
Но ничего, то, что мы не видим, то не рисуется, а это главное!
P.S.: Я думала, думала, и решила немного уточнить момент с графикой. Это не стиль будущей игры, и она не будет в реализме, поэтому я не работаю сейчас над визуальной составляющей (некоторые подвижки в эту сторону скоро начнутся). Это пока что просто технические решения и некоторые стресс тесты для них (и для меня
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14❤3👍2
https://telegra.ph/Shading-landshafta-04-13
Решила сегодня попробовать формат не просто поста, а поста-статьи, так как в телеграмме не получится написать такой длинный пост😢 Но это не статья как в прошлый раз! Просто чуть длинненький пост)
Зато там получилось выложить побольше информации и побольше скриншотов🙌
Решила сегодня попробовать формат не просто поста, а поста-статьи, так как в телеграмме не получится написать такой длинный пост
Зато там получилось выложить побольше информации и побольше скриншотов
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegraph
Shading ландшафта
Как же я рисую ландшафт?
🔥16❤4👍3
Небольшой опрос по формату постов в канале)
И спасибо за то, что читаете и даже комментируете ^^
И спасибо за то, что читаете и даже комментируете ^^
Final Results
10%
Маленькие посты, но чаще.
10%
Большие посты через телеграф, но реже.
80%
Вперемешку - иногда маленькие, иногда большие, и иногда полноценные статьи.
🔥2
Всем спасибо за голосование! 🥰
10% проголосовало за маленькие частые посты, 10% за большие, но редкие, и 80% за смешанный тип. Тогда буду стараться поразнообразней публиковать посты)
10% проголосовало за маленькие частые посты, 10% за большие, но редкие, и 80% за смешанный тип. Тогда буду стараться поразнообразней публиковать посты)
Please open Telegram to view this post
VIEW IN TELEGRAM
🎉5👍3❤1🥰1
Биомы
А сейчас я хотела бы подняться на уровень выше в генерации. Биомы!
Что такое биом? Это какой-то набор флоры и фауны, определенный вид ландшафта с уникальными материалами, а еще с уникальными монстрами, условиями игры и многого другого. Например биом гор, полей, леса, океана и т.п.
Но что такое биом в рамках генерации? Как ни странно, это просто набор вокселей и правила их генерации! Потому что только воксели определяют весь окружающий мир.
При генерации каждого вокселя я для него определяю и биом. Это происходит все также по графу шума (на скриншоте), правда уже в 2D.
Для каждого биома я в дальнейшем генерирую полноценный воксель - SDF значение, его градиент. А потом по blend значению смешиваю и получаю уже окончательный результат. Мне это позволяет смешивать биомы: создавать плавный переход от одного к другому. Например горы плавно переходят в поля.
Материалы я также выбираю с двух биомов, и выбираю те, которые имеют наибольший вес. Но это требует повышенного количества ресурсов, так как в одном чанке может быть сразу несколько биомов, у каждого из которых свои настройки и текстуры.
А сейчас я хотела бы подняться на уровень выше в генерации. Биомы!
Что такое биом? Это какой-то набор флоры и фауны, определенный вид ландшафта с уникальными материалами, а еще с уникальными монстрами, условиями игры и многого другого. Например биом гор, полей, леса, океана и т.п.
Но что такое биом в рамках генерации? Как ни странно, это просто набор вокселей и правила их генерации! Потому что только воксели определяют весь окружающий мир.
При генерации каждого вокселя я для него определяю и биом. Это происходит все также по графу шума (на скриншоте), правда уже в 2D.
var biomeSelectionResult = BiomeFlow.SelectBiome(worldPosition.x, worldPosition.z, InputData.WorldSeed);
BiomeFlow.SelectBiome возвращает индексы двух биомов в указанной точке, которые имеют наибольший вес. Также я возвращаю и blend значение, чтобы потом можно было смешивать результаты.Для каждого биома я в дальнейшем генерирую полноценный воксель - SDF значение, его градиент. А потом по blend значению смешиваю и получаю уже окончательный результат. Мне это позволяет смешивать биомы: создавать плавный переход от одного к другому. Например горы плавно переходят в поля.
Материалы я также выбираю с двух биомов, и выбираю те, которые имеют наибольший вес. Но это требует повышенного количества ресурсов, так как в одном чанке может быть сразу несколько биомов, у каждого из которых свои настройки и текстуры.
🔥12❤4🥰1
Burst и делегаты
Иногда в задачах никак не обойтись без делегатов, но что делать, если код на Jobs + Burst? Delegate - это managed тип, под Burst не вызовешь, а от преимуществ отказываться не хочется.
В C# есть unmanaged делегаты, которые также поддерживаются и Burst если правильно приготовить🧂
Все, что нужно сделать, так это:
Шаг 1. Определить самый обыкновенный
Шаг 2. Определить метод/методы, которые хотим вызывать с помощью делегата и пометить атрибутом с ним
Шаг 3. Пометить вызываемые методы из п.2 и типы, которые их содержат атрибутом
Шаг 4. Скомпилить каждый делегат через
Или по старинке вот так:
А что по производительности?
Например, у меня так реализованы операции над ландшафтом.🚪 Я использую указатели на функции, чтобы все типы операций (копание/возведение, по сфере, по кубу и т.п.) можно было описать абстрактной структурой
Все эти операции складываются в массив, по которому пробегаюсь при вычислении SDF вокселя.🥔
Иногда в задачах никак не обойтись без делегатов, но что делать, если код на Jobs + Burst? Delegate - это managed тип, под Burst не вызовешь, а от преимуществ отказываться не хочется.
В C# есть unmanaged делегаты, которые также поддерживаются и Burst если правильно приготовить
Все, что нужно сделать, так это:
Шаг 1. Определить самый обыкновенный
delegate и пометить его как CallingConvention.Cdecl если собираем IL2CPP:[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void OperationPerformDelegate(ref OperationExecutor.Parameters parameters);
Шаг 2. Определить метод/методы, которые хотим вызывать с помощью делегата и пометить атрибутом с ним
[MonoPInvokeCallback(typeof(OperationPerformDelegate))]:[BurstCompile]
[MonoPInvokeCallback(typeof(OperationPerformDelegate))]
private static void InvokeExecute(ref OperationExecutor.Parameters parameters)
{
OperationExecutor.Execute<CapsuleOperation, CapsuleOperation>(ref parameters);
}
Шаг 3. Пометить вызываемые методы из п.2 и типы, которые их содержат атрибутом
[BurstCompile].Шаг 4. Скомпилить каждый делегат через
BurstCompiler.CompileFunctionPointer:private static readonly FunctionPointer<OperationPerformDelegate> m_InvokeExecuteFunctionPointer =
BurstCompiler.CompileFunctionPointer<OperationPerformDelegate>(InvokeExecute);
FunctionPointer можно передать любым удобным образом в Job и вызывать или с помощью обычного Invoke:m_ExecutePtr.Invoke.Invoke(ref parameters);
Или по старинке вот так:
((delegate * unmanaged[Cdecl] <ref OperationExecutor.Parameters, void>)m_ExecutePtr.Value)(ref parameters);
А что по производительности?
FunctionPointer существенно медленнее, чем прямой вызов, поэтому использовать лучше в тех моментах, где совсем не обойтись. Вот тут можно посмотреть сравнение.Например, у меня так реализованы операции над ландшафтом.
NativeOperation, которая содержит только упакованные данные операции + указатель на метод, который по этим данным может произвести вычисления.public struct NativeOperation : IDisposable
{
private readonly FunctionPointer<OperationPerformDelegate> m_ExecutePtr;
[NativeDisableUnsafePtrRestriction]
internal unsafe void* m_Data;
public readonly int3 Min;
public readonly int3 Max;
}
Все эти операции складываются в массив, по которому пробегаюсь при вычислении SDF вокселя.
Please open Telegram to view this post
VIEW IN TELEGRAM
Docs
Function pointers - C# feature specifications
This feature specification describes function pointers, which are unmanaged delegates. They are typically used to avoid the allocations necessary to instantiate a delegate object.
🔥12❤4👍2🤝1🤗1
Media is too big
VIEW IN TELEGRAM
Добавляю генерацию объектов, с которыми игрок может взаимодействовать тем или иным образом (например, физика). Это все большие камни, деревья, постройки и т.п.
Также учитываю и тот момент, что ландшафт может меняться☺️ Пока что реализовала самый простой вариант, когда при изменении ландшафта мы просто опускаем объект вниз (понадобится для кустов и подобных небольших объектов). Но в планах конечно в такие моменты включать физику, и пусть булыжник упадет кому-нибудь на голову 😕 🤭
Так как это объекты с коллайдером, то они симулируются и на серверной стороне, а не только на клиентской😶
Также учитываю и тот момент, что ландшафт может меняться
Так как это объекты с коллайдером, то они симулируются и на серверной стороне, а не только на клиентской
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8
Media is too big
VIEW IN TELEGRAM
Вот еще одно небольшое видео, на котором видно, что объект с коллайдером 🤩
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9
Media is too big
VIEW IN TELEGRAM
Тест с большими камушками 🤩
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11
Media is too big
VIEW IN TELEGRAM
Деревья!😧 🙌
Стало намного интереснее) Причем деревья, которые поблизости, являются "призраками" и синхронизируются с копией на сервере. А вот деревья вдалеке - существуют только на клиенте) При приближении они конечно же заменяются на призраки🥔
В комментариях еще скрин🫥
Стало намного интереснее) Причем деревья, которые поблизости, являются "призраками" и синхронизируются с копией на сервере. А вот деревья вдалеке - существуют только на клиенте) При приближении они конечно же заменяются на призраки
В комментариях еще скрин
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥18👍1
Сегодня расскажу про то, что крайне редко кастомизируется - аллокаторы в DOTS! ☺️
Все, кто использует нативные коллекции из Unity.Collections, знакомы с аллокаторами, которые Unity предоставляет по умолчанию. Это Persistent - для объектов с долгим времени жизни, Temp - для однокадровых объектов, TempJob - для объектов, передаваемых в Job. Но мало кто знает, что можно написать свой аллокатор и использовать также, как и дефолтные! (Правда с некоторыми оговорками😔 )
На самом деле Unity предоставляет все необходимое API, чтобы можно было реализовать свой аллокатор и использовать в нативных коллекциях. Все, что нужно сделать, так это реализовать интерфейс
Где
Самое интересное - метод🤭 ) выяснять некоторые детали.
В качестве входного параметра нам приходит
Правила тут такие:
- если
- если
- в ином случае, мы должны освободить память.
А вот что возвращает метод?🤔 Указан возвращаемый тип как
- код
- код
После того, как написали свой аллокатор, его можно использовать, но как? Каждый аллокатор должен возвращать свой🔍
Но в случае с кастомным аллокатором есть некоторые ограничения:
1. Нельзя использовать
2.😒 Чтобы создать NativeArray<> надо использовать
Но это сильно ограничивает, в Job уже не получится задиспоузить массив, созданный таким образом😢
3. Нет поддержки многопоточности! То есть нельзя создать инстанс аллокатора в главном потоке, а потом использовать в других потоках. Причина в том, что на каждый аллокатор также создаются свои safety checks - проверки "от дурака". И🤦
Зачем вообще использовать свой аллокатор? В большинстве задач это не требуется, но иногда, все же может быть лучше свое решение, чем то, что предоставляет Unity. Я использую кастомный аллокатор на базе smmalloc по той причине, что мне в какой-то момент понадобились аллокации, которые быстры как и TempJob/Temp, но и живут больше 4 и 1 кадра соответственно.
Все, кто использует нативные коллекции из Unity.Collections, знакомы с аллокаторами, которые Unity предоставляет по умолчанию. Это Persistent - для объектов с долгим времени жизни, Temp - для однокадровых объектов, TempJob - для объектов, передаваемых в Job. Но мало кто знает, что можно написать свой аллокатор и использовать также, как и дефолтные! (Правда с некоторыми оговорками
На самом деле Unity предоставляет все необходимое API, чтобы можно было реализовать свой аллокатор и использовать в нативных коллекциях. Все, что нужно сделать, так это реализовать интерфейс
AllocatorManager.IAllocator:public struct TestAllocator : AllocatorManager.IAllocator
{
public AllocatorManager.TryFunction Function { get; }
public AllocatorManager.AllocatorHandle Handle { get; set; }
public Allocator ToAllocator { get; }
public bool IsCustomAllocator { get; }
public int Try(ref AllocatorManager.Block block) { }
public void Dispose() { }
}
Где
AllocatorManager.TryFunction Function { get; } - делегат функции int Try(ref AllocatorManager.Block block) { }, которую AllocatorManager вызывает при попытке аллоцировать/реаллоцировать/освободить память.Самое интересное - метод
Try, тут мне пришлось опытным путем (ну как всегда в Unity В качестве входного параметра нам приходит
ref AllocatorManager.Block block, который содержит некоторые детали запроса. А именно сколько элементов и с каким размером и выравниванием надо аллоцировать. Также тут хранится и указатель на память, этот указатель нам и надо заполнить. Правила тут такие:
- если
block.Range.Pointer равен IntPtr.Zero, то мы должны аллоцировать память;- если
block.Range.Pointer не равен IntPtr.Zero и block.Range.Items больше 0, то есть уже раньше аллоцировали этот блок памяти и он валиден, то мы его должны реаллоцировать;- в ином случае, мы должны освободить память.
А вот что возвращает метод?
int, надо же что-то вернуть. А возвращается тут код ошибки. Из того, что я выяснила:- код
0 - все успешно;- код
-1 - операция прошла неуспешно.После того, как написали свой аллокатор, его можно использовать, но как? Каждый аллокатор должен возвращать свой
AllocatorHandle - это всего лишь указатель на запись во внутренней таблице аллокаторов, по которой Unity ищет указатель на функцию аллокатора. Кстати, Allocator.Persistent/Temp/TempJob - также неявно конвертируются в AllocatorHandle. Но в случае с кастомным аллокатором есть некоторые ограничения:
1. Нельзя использовать
UnsafeUtility.Malloc/Free, вместо этого необходимо использовать AllocatorManager.Allocator/Free.(T*)AllocatorManager.Allocate(handle, itemSizeInBytes, alignmentInBytes, length);
AllocatorManager.Free(handle, pointer, length);
2.
NativeArray<> не поддерживает кастомный аллокатор! Причина в том, что он написан для использования только enum Allocator CollectionHelper.CollectionHelper.CreateNativeArray<T, TestAllocator>(length, ref allocator, options);
CollectionHelper.Dispose(array);
Но это сильно ограничивает, в Job уже не получится задиспоузить массив, созданный таким образом
3. Нет поддержки многопоточности! То есть нельзя создать инстанс аллокатора в главном потоке, а потом использовать в других потоках. Причина в том, что на каждый аллокатор также создаются свои safety checks - проверки "от дурака". И
SafetyHandle внутри AllocatorManager хранятся в UnsafeList, который не является потокобезопасным. Опять же недоработка от Unity.Зачем вообще использовать свой аллокатор? В большинстве задач это не требуется, но иногда, все же может быть лучше свое решение, чем то, что предоставляет Unity. Я использую кастомный аллокатор на базе smmalloc по той причине, что мне в какой-то момент понадобились аллокации, которые быстры как и TempJob/Temp, но и живут больше 4 и 1 кадра соответственно.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤6🤯2❤🔥1👍1
Разбираюсь сейчас со спайками, и немного углубляюсь в unsafe мир ☺️
Вот что интересного выяснила: память желательно выравнивать! Вот да, недостаточно просто выделить кусок памяти, желательно выровнять его по размеру кэшлайна. Разница - огромная!😧
Размер кэшлайна в Job System можно узнать через константу
А вот выровнять можно через CollectionHelper:
Где T - ваш тип данных. Достаточно просто, а разница - на скриншоте)
Вот что интересного выяснила: память желательно выравнивать! Вот да, недостаточно просто выделить кусок памяти, желательно выровнять его по размеру кэшлайна. Разница - огромная!
Размер кэшлайна в Job System можно узнать через константу
JobsUtility.CacheLineSize (64 байта).А вот выровнять можно через CollectionHelper:
// Считаем сколько памяти (в байтах) нам нужно, заодно выравниваем по кэшлайну, чтобы было кратко CacheLineSize
var someDataSize = CollectionHelper.Align(UnsafeUtility.SizeOf<T> * length, JobsUtility.CacheLineSize);
// Выделяем (аллоцируем) память по посчитанному размеру и указываем, что выравниваем по CacheLineSize
var someData = (byte*)AllocatorManager.Allocate(allocatorHandle, someDataSize, JobsUtility.CacheLineSize);
Где T - ваш тип данных. Достаточно просто, а разница - на скриншоте)
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥20❤🔥2❤2👌2🦄2