tgoop.com/unsafecsharp/238
Create:
Last Update:
Last Update:
Новости BECS
Как мне тут сказали в чатике по ецс: "У тебя фреймворк не многопоточный, т.к. при использовании компонентов в двух джобах у тебя не будет исключения о race condition". Ну сказано - сделано. Теперь есть.
А теперь подробнее с какими трудностями пришлось столкнуться при реализации.
У меня есть 2 интерфейса:
IJobParallelForAspect<T..n>
IJobParallelForComponents<T..n>
Первый фильтрует по аспектам, второй - по компонентам. Для реализации исключений на самом деле нужно реализовать NativeContainer + m_Safety поле. При ините джобы юнити сама ищет все поля m_Safety в структуре джобы и использует их для понимания что от чего зависит.
В BECS реализация джоб выглядит примерно так:
public struct Job : IJobParallelForComponents<C1, C2> {
public void Execute(in JobInfo jobInfo, in Ent ent, ref C1 c1, ref C2 c2) {...}
}
Т.е. не нужно никаких создавать дополнительных полей. И тут внимательные читатели спросят "у тебя же ref для компонента, а как же права RO/WO/RW?". На самом деле ref тут исключительно для удобства, магия происходит на уровне кодогена.
А именно: когда вы написали код метода Execute, я его разбираю и нахожу все обращения ко всем компонентам и соотвественно могу выяснить что вы с ним делаете: например, только читаете или только пишите или и то и другое.
В этом разборе я составляю список используемых компонентов для конкретной джобы.
Если с компонетами можно было так не заморачиваться, то с аспектами так не выйдет, т.к. внутри аспекта по сути может быть 10 компонентов, а джобе вы используете только 1 или 2, например. Таким образом 2 параллельно запущенные джобы не дали бы обращаться к одному аспекту в параллель, т.к. внутри был бы лок на все компоненты. Поэтому и пришла идея парсить код метода Execute на предмет фактического использования компонентов.
В итоге кодоген создает вот такие данные для каждой джобы
public struct JobDebugDataXXX {
[NativeDisableUnsafePtrRestriction] public MyJob jobData;
[NativeDisableUnsafePtrRestriction] public CommandBuffer* buffer;
public RefRW<C1> c0;
public SafetyComponentContainerRO<C1> C1;
public SafetyComponentContainerWO<C2> C2;
public SafetyComponentContainerRO<ParentComponent> ParentComponent;
}
Если код Execute будет таким:
void Execute(in JobInfo jobInfo, in Ent ent, ref C1 c1) {
ent.GetParent().Get<C2>().data = c1.data;
}
Естественно, можно использовать любые вызовы внутри Execute и рекрусивно по ним BECS пройдет и увидит любые обращения.
Пришлось, конечно, скармливать JobDebugDataXXX вместо обычной джобы, что занимает чуть больше времени, чем обычно, но для этого есть ENABLE_UNITY_COLLECTIONS_CHECKS и ENABLE_BECS_COLLECTIONS_CHECKS дефайны, чтобы отключать всю эту штуку.
#becs #ecs #IL #codegenerator
BY Unity: Всё, что вы не знали о разработке
Share with your friend now:
tgoop.com/unsafecsharp/238