checked/unchecked
Встречали ли вы эти слова в коде? Думаю, что вряд ли. А все потому что в основном мы работаем с числами и относимся к ним как к хранилищу. Большинство вообще используют int и float, а если будут хранить большие числа - ну long и double. Для каких-нибудь ГК игр используют BigInteger при необходимости. И на самом деле большинство никогда не задумывается «а что будет если».
Но вернемся к теме. В шарпе по-умолчанию число int.MaxValue + 1 вернет результат без исключения и будет равно int.MinValue. То есть по сути будет ходить по кругу. Иногда это критично для некоторых кейсов, поэтому используют блок checked:
Тем самым можно поймать исключение, которое потом правильно обработать, ну или получить исключение в правильном месте. Ведь где-нибудь мы считали индекс массива, он стал минусовым, а потом где-нибудь совершенно в другом месте упал с ошибкой «чет индекс не попадает в рамки массива». И хорошо, если это индекс массива, а если просто число, которое влияет на логику выполнения и в какой-то момент у вас что-нибудь начнет крутиться в другую сторону?
Аналогично checked существует и unchecked:
На самом деле хоть unchecked - это поведение по-умолчанию, все равно его можно переопределить параметром компилятора.
Также существуют checked/unchecked операторы для более котороткой записи:
Для контекста переполнения важен контекст (странно даже), то есть если вы будете вызывать метод внутри checked, внутри которого будет переполнение, то исключения не будет, это важно не забыть.
#basics #overflow
Встречали ли вы эти слова в коде? Думаю, что вряд ли. А все потому что в основном мы работаем с числами и относимся к ним как к хранилищу. Большинство вообще используют int и float, а если будут хранить большие числа - ну long и double. Для каких-нибудь ГК игр используют BigInteger при необходимости. И на самом деле большинство никогда не задумывается «а что будет если».
Но вернемся к теме. В шарпе по-умолчанию число int.MaxValue + 1 вернет результат без исключения и будет равно int.MinValue. То есть по сути будет ходить по кругу. Иногда это критично для некоторых кейсов, поэтому используют блок checked:
value = int.MaxValue;
checked {
value += 1; // тут мы получим исключение
}
Тем самым можно поймать исключение, которое потом правильно обработать, ну или получить исключение в правильном месте. Ведь где-нибудь мы считали индекс массива, он стал минусовым, а потом где-нибудь совершенно в другом месте упал с ошибкой «чет индекс не попадает в рамки массива». И хорошо, если это индекс массива, а если просто число, которое влияет на логику выполнения и в какой-то момент у вас что-нибудь начнет крутиться в другую сторону?
Аналогично checked существует и unchecked:
value = int.MaxValue;
unchecked {
value += 1; // тут исключения не будет, число будет равно минимальному значению
}
На самом деле хоть unchecked - это поведение по-умолчанию, все равно его можно переопределить параметром компилятора.
Также существуют checked/unchecked операторы для более котороткой записи:
return checked(value + 1);
Для контекста переполнения важен контекст (странно даже), то есть если вы будете вызывать метод внутри checked, внутри которого будет переполнение, то исключения не будет, это важно не забыть.
#basics #overflow
👍42🔥11❤2🦄2
Статические лямбды
Я уже писал пост о том, что лямбды - это плохо, т.к. в основном лямбды используют ради замыканий, которые в свою очередь приводят к аллокациям, а аллокации - этоплохо медленно 🙂
Видимо, разработчики шарпа тоже решили исправить ситуацию и добавили слово static к определению лямбды:
Теперь можно не бояться, что мы случайно используем переменную извне и получим замыкание.
В целом это синтаксический сахар, который ровным счетом ничего не делает, кроме как запрещает передавать переменные в лямбду через замыкание. Если вы использовали лямбды правильно, то вы можете смело добавить в свои лямбды static.
#basics #staticlambda
Я уже писал пост о том, что лямбды - это плохо, т.к. в основном лямбды используют ради замыканий, которые в свою очередь приводят к аллокациям, а аллокации - это
Видимо, разработчики шарпа тоже решили исправить ситуацию и добавили слово static к определению лямбды:
Method(static () => …);
Теперь можно не бояться, что мы случайно используем переменную извне и получим замыкание.
В целом это синтаксический сахар, который ровным счетом ничего не делает, кроме как запрещает передавать переменные в лямбду через замыкание. Если вы использовали лямбды правильно, то вы можете смело добавить в свои лямбды static.
#basics #staticlambda
Telegram
Unity: Всё, что вы не знали о разработке
Немного про аллокации и кэш.
Самый простой пример аллокаций - это замыкание.
Тут остановимся подробнее. Где могут скрываться аллокации, например, в такой конструкции:
list.Where(x => x > 10).ToArray()
Очевидно, что при вызове ToArray будет создан массив.…
Самый простой пример аллокаций - это замыкание.
Тут остановимся подробнее. Где могут скрываться аллокации, например, в такой конструкции:
list.Where(x => x > 10).ToArray()
Очевидно, что при вызове ToArray будет создан массив.…
🔥29👍15❤2
UI Toolkit
Это некое подобие html/css. Почему подобие? Потому что многих стандартных вещей нет, а текущий стандарт css ушел довольно далеко от uss. То есть это примерно как верстать сейчас сайтики под Internet Explorer 6.0, оно вроде называется похоже, но большую часть просто не поддерживает.
Где это использовать?
Я бы рекомендовал это использовать только для editor-tools, еще может быть в каких-нибудь рантайм штук в билде типа дев консоли. Для продакшена еще далеко, да и есть много подводных камней.
Для редактора:
PropertyDrawer. Если в IMGUI нужно использовать два метода, если высота элемента больше стандартной линии, то при использовании UI Toolkit можно использовать только один метод:
EditorWindow. Для отрисовке в окне существует стандартный метод:
CustomEditor. Для отрисовки редактора для всего скрипта, нужно использовать метод:
Как это работает?
По сути UIToolkit - это контейнеры, которые можно заполнять объектами, которые сами по себе тоже являются контейнерами. Если знакомы с HTML, то контейнер в UITK - это тег.
В любом варианте из перечисленных выше, у нас есть VisualElement - это рутовый контейнер, в который мы можем вкладывать свои:
А в MyStyle.uss:
Что хорошего?
1. Многопоточность. Я не уверен, что прям все апи многопоточное, но большая часть точно, во всяком случае с тем, что я сталкивался. Это значит, что обновлять данные у элемента можно из потоков.
2. Быстро работает. На самом деле IMGUI работает тоже быстро, если не использовать GUILayout, просто там схема работы "Перемешали логику и визуалку", поэтому чтобы отрисовать элементы - нужно пройтись по всей логике.
3. Визуалка зависит от стилей, а не от настроек элементов в коде.
4. Не нужно тратить CPU на подготовку отрисовки каждый кадр, т.к. структурно меняется все очень редко.
Что плохого?
1. Если структурных изменений очень много, то нужно будет написать пулинг объектов, чтобы переиспользовать элементы, а не создавать их заново.
2. Нет поддержки партиклов и похоже что это будет еще не скоро. На самом деле одна из основных проблем почему UITK не нужно использовать в рантайме.
3. Нет поддержки анимаций. Есть стандартные анимации типа transition, но боюсь что этого недостаточно, чтобы сделать красивые штуки.
4. Нет возможности указать слой, т.е. рисуется все в том порядке, в котором было объявлено.
Резюмирую
Могу сказать, что я использую UITK во всех своих тулзах, т.к. это работает намного быстрее IMGUI и можно добиться красивого результата с меньшими затратами. Но именно для продакшена он не готов.
#uitoolkit #ui
Это некое подобие html/css. Почему подобие? Потому что многих стандартных вещей нет, а текущий стандарт css ушел довольно далеко от uss. То есть это примерно как верстать сейчас сайтики под Internet Explorer 6.0, оно вроде называется похоже, но большую часть просто не поддерживает.
Где это использовать?
Я бы рекомендовал это использовать только для editor-tools, еще может быть в каких-нибудь рантайм штук в билде типа дев консоли. Для продакшена еще далеко, да и есть много подводных камней.
Для редактора:
PropertyDrawer. Если в IMGUI нужно использовать два метода, если высота элемента больше стандартной линии, то при использовании UI Toolkit можно использовать только один метод:
VisualElement CreatePropertyGUI(SerializedProperty property) {
...
}
EditorWindow. Для отрисовке в окне существует стандартный метод:
private void CreateGUI() {
...
}
CustomEditor. Для отрисовки редактора для всего скрипта, нужно использовать метод:
VisualElement CreateInspectorGUI() {
...
}
Как это работает?
По сути UIToolkit - это контейнеры, которые можно заполнять объектами, которые сами по себе тоже являются контейнерами. Если знакомы с HTML, то контейнер в UITK - это тег.
В любом варианте из перечисленных выше, у нас есть VisualElement - это рутовый контейнер, в который мы можем вкладывать свои:
Label myLabel;
VisualElement CreateInspectorGUI() {
// Создаем свой контейнер
var myRoot = new VisualElement();
// Добавляем файл со стилями
myRoot.styleSheets.Add(Resources.Load<StyleSheet>("MyStyle"));
// Создаем label
var label = new Label("Hello World");
label.AddToClassList("my-label");
this.myLabel = label;
// Добавляем в иерархию
myRoot.Add(label);
// Возвращаем корневой объект
return myRoot;
}
void UpdateLabel() {
// Просто обновляем текст у объекта
this.myLabel.text = "New text";
}
А в MyStyle.uss:
// Наводим всякие красивости
.my-label {
font-size: 20px;
border-radius: 5px;
background-color: red;
}
Что хорошего?
1. Многопоточность. Я не уверен, что прям все апи многопоточное, но большая часть точно, во всяком случае с тем, что я сталкивался. Это значит, что обновлять данные у элемента можно из потоков.
2. Быстро работает. На самом деле IMGUI работает тоже быстро, если не использовать GUILayout, просто там схема работы "Перемешали логику и визуалку", поэтому чтобы отрисовать элементы - нужно пройтись по всей логике.
3. Визуалка зависит от стилей, а не от настроек элементов в коде.
4. Не нужно тратить CPU на подготовку отрисовки каждый кадр, т.к. структурно меняется все очень редко.
Что плохого?
1. Если структурных изменений очень много, то нужно будет написать пулинг объектов, чтобы переиспользовать элементы, а не создавать их заново.
2. Нет поддержки партиклов и похоже что это будет еще не скоро. На самом деле одна из основных проблем почему UITK не нужно использовать в рантайме.
3. Нет поддержки анимаций. Есть стандартные анимации типа transition, но боюсь что этого недостаточно, чтобы сделать красивые штуки.
4. Нет возможности указать слой, т.е. рисуется все в том порядке, в котором было объявлено.
Резюмирую
Могу сказать, что я использую UITK во всех своих тулзах, т.к. это работает намного быстрее IMGUI и можно добиться красивого результата с меньшими затратами. Но именно для продакшена он не готов.
#uitoolkit #ui
👍36🔥10🥰1
Quaternion vs Euler
На самом деле довольно интересная тема. Многие (особенно начинающие) не особо понимают в чем разница, т.к. кватернион хрен знает что это, там если умножить на вектор вроде как вектор повернет (но это не точно), а вот euler ваще классная штука (ведь не даром поворот в трансформе 3мя флотами записан (нет)).
Но давайте вспомним (или узнаем) про gimbal lock. Это такое положение осей, когда две оси сложились, а третья не в состоянии повлиять на вращение в нужную нам сторону (на рисунке зеленая и синия ось сложились в одну плоскость, а красная в состоянии вращать только в одном направлении).
Вообще euler angles - это понятное представление осей для человека, где есть понятный поворот x, y, z. Но в играх предпочтительно использовать quaternion, т.к. он не подвержен проблеме gimbal lock.
Другое дело quaternion. В реальности это гиперкомплексные числа.
Если не вдаваться в подробности математики (может стоит?), а остановиться на том, что мы используем в юнити, то вот несколько моментов:
1. Quaternion не может содержать 4 значения 0;
2. Существуют проблемы с точностью float, которые могут приводить к невалидному состоянию;
3. Умножение кватерниона на вектор - повернут вектор;
4. Умножение кватерниона на кватернион - сложение углов;
5. Кватернион можно представить в виде матрицы;
6. Порядок умножения кватернионов важен;
7. Умножение quaternion на inverse(quaternion) вычитает повороты;
На самом деле пока писал этот пост несколько раз подумал о том, что хорошо бы рассказать было о матрицах для начала. Но надеюсь, что и эта информация для кого-то окажется полезной.
#quaternion #euler #math
На самом деле довольно интересная тема. Многие (особенно начинающие) не особо понимают в чем разница, т.к. кватернион хрен знает что это, там если умножить на вектор вроде как вектор повернет (но это не точно), а вот euler ваще классная штука (ведь не даром поворот в трансформе 3мя флотами записан (нет)).
Но давайте вспомним (или узнаем) про gimbal lock. Это такое положение осей, когда две оси сложились, а третья не в состоянии повлиять на вращение в нужную нам сторону (на рисунке зеленая и синия ось сложились в одну плоскость, а красная в состоянии вращать только в одном направлении).
Вообще euler angles - это понятное представление осей для человека, где есть понятный поворот x, y, z. Но в играх предпочтительно использовать quaternion, т.к. он не подвержен проблеме gimbal lock.
Другое дело quaternion. В реальности это гиперкомплексные числа.
Если не вдаваться в подробности математики (может стоит?), а остановиться на том, что мы используем в юнити, то вот несколько моментов:
1. Quaternion не может содержать 4 значения 0;
2. Существуют проблемы с точностью float, которые могут приводить к невалидному состоянию;
3. Умножение кватерниона на вектор - повернут вектор;
4. Умножение кватерниона на кватернион - сложение углов;
5. Кватернион можно представить в виде матрицы;
6. Порядок умножения кватернионов важен;
7. Умножение quaternion на inverse(quaternion) вычитает повороты;
На самом деле пока писал этот пост несколько раз подумал о том, что хорошо бы рассказать было о матрицах для начала. Но надеюсь, что и эта информация для кого-то окажется полезной.
#quaternion #euler #math
👍47🔥20🥰2🤯2❤1
Как свапнуть значение
Представим, что у нас есть 2 переменные a и b, нам нужно поменять их значения местами: a => b, b => a.
Обычно, мы пишем так:
Но есть возможность записать это проще:
По производительности записи идентичны, т.к. второй вариант превращается в первый.
#tips #basics
Представим, что у нас есть 2 переменные a и b, нам нужно поменять их значения местами: a => b, b => a.
Обычно, мы пишем так:
var temp = a;
a = b;
b = temp;
Но есть возможность записать это проще:
(a, b) = (b, a);
По производительности записи идентичны, т.к. второй вариант превращается в первый.
#tips #basics
🔥56👍27😐7🤯6🥰3💯1
where T
Давайте представим ситуацию, нам нужно реализовать 2 метода с одинаковой сигнатурой, но фильтровать разные интерфейсы:
Такой код не скомпилится, т.к. сигнатуры методов одинаковые, разные только условия выбора.
Как можно это исправить? Самый простой вариант - объявить методы с разными именами:
Но с ростом количества вариантов придется каждый раз придумывать имена, а тем, кто используют эти методы, придется подбирать варианты имен.
Что делать?
Объявляем наши интерфейсы и класс:
Пишем два экстеншена:
И вуаля 🙂 На самом деле в большинстве случаев такой хак будет работать, т.к. компилятор по сути будет считать, что это 2 разных класса (MyClassExt1, MyClassExt2) и в них объявлены свои методы. А при компиляции они все равно развернутся в Method_I1, Method_I2 и не будут никаким образом мешать друг другу.
#lifehack #csharp
Давайте представим ситуацию, нам нужно реализовать 2 метода с одинаковой сигнатурой, но фильтровать разные интерфейсы:
public interface I1 {}
public interface I2 {}
public class MyClass {
public void Method<T>() where T : struct, I1 {}
public void Method<T>() where T : struct, I2 {}
}
Такой код не скомпилится, т.к. сигнатуры методов одинаковые, разные только условия выбора.
Как можно это исправить? Самый простой вариант - объявить методы с разными именами:
Method1<T>
Method2<T>
Но с ростом количества вариантов придется каждый раз придумывать имена, а тем, кто используют эти методы, придется подбирать варианты имен.
Что делать?
Объявляем наши интерфейсы и класс:
public interface I1 {}
public interface I2 {}
public class MyClass {}
Пишем два экстеншена:
public static class MyClassExt1 {
public static void Method<T>(this MyClass obj) where T : struct, I1 {}
}
public static class MyClassExt2 {
public static void Method<T>(this MyClass obj) where T : struct, I2 {}
}
И вуаля 🙂 На самом деле в большинстве случаев такой хак будет работать, т.к. компилятор по сути будет считать, что это 2 разных класса (MyClassExt1, MyClassExt2) и в них объявлены свои методы. А при компиляции они все равно развернутся в Method_I1, Method_I2 и не будут никаким образом мешать друг другу.
#lifehack #csharp
🔥70👍26🤡1🥴1🫡1
Новости BECS
Как мне тут сказали в чатике по ецс: "У тебя фреймворк не многопоточный, т.к. при использовании компонентов в двух джобах у тебя не будет исключения о race condition". Ну сказано - сделано. Теперь есть.
А теперь подробнее с какими трудностями пришлось столкнуться при реализации.
У меня есть 2 интерфейса:
Первый фильтрует по аспектам, второй - по компонентам. Для реализации исключений на самом деле нужно реализовать NativeContainer + m_Safety поле. При ините джобы юнити сама ищет все поля m_Safety в структуре джобы и использует их для понимания что от чего зависит.
В BECS реализация джоб выглядит примерно так:
Т.е. не нужно никаких создавать дополнительных полей. И тут внимательные читатели спросят "у тебя же ref для компонента, а как же права RO/WO/RW?". На самом деле ref тут исключительно для удобства, магия происходит на уровне кодогена.
А именно: когда вы написали код метода Execute, я его разбираю и нахожу все обращения ко всем компонентам и соотвественно могу выяснить что вы с ним делаете: например, только читаете или только пишите или и то и другое.
В этом разборе я составляю список используемых компонентов для конкретной джобы.
Если с компонетами можно было так не заморачиваться, то с аспектами так не выйдет, т.к. внутри аспекта по сути может быть 10 компонентов, а джобе вы используете только 1 или 2, например. Таким образом 2 параллельно запущенные джобы не дали бы обращаться к одному аспекту в параллель, т.к. внутри был бы лок на все компоненты. Поэтому и пришла идея парсить код метода Execute на предмет фактического использования компонентов.
В итоге кодоген создает вот такие данные для каждой джобы
Если код Execute будет таким:
Естественно, можно использовать любые вызовы внутри Execute и рекрусивно по ним BECS пройдет и увидит любые обращения.
Пришлось, конечно, скармливать JobDebugDataXXX вместо обычной джобы, что занимает чуть больше времени, чем обычно, но для этого есть ENABLE_UNITY_COLLECTIONS_CHECKS и ENABLE_BECS_COLLECTIONS_CHECKS дефайны, чтобы отключать всю эту штуку.
#becs #ecs #IL #codegenerator
Как мне тут сказали в чатике по ецс: "У тебя фреймворк не многопоточный, т.к. при использовании компонентов в двух джобах у тебя не будет исключения о 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
👍12🥰11🤯9❤3🔥3
if lock if
У меня в BECS очень много всяких вариантов локов, например, для ресайзов вида:
Но поскольку мне нужно многопоточность, самый простой вариант обернуть это в лок:
Но поскольку операция создания объекта происходит крайне редко (тут даже скорее лучше бы подошел пример с ресайзом массива), то какой смысл тратить перф на блокировку? Никакого.
Получается такая матрешка, но давайте резберемся что будет для нескольких потоков:
1. Поток №1 входит в условие if (obj.isCreated == false);
2. Поток №2 тоже может успеть войти в это условие;
3. Поток №1 блокирует объект;
4. Поток №2 ожидает снятия блокировки;
5. Поток №1 создает объект и записывает его;
6. В этом месте любое количество других потоков может войти в метод и уже использовать наш объект;
7. Поток №1 выходит из блокировки, освобождая поток №2
8. Поток №2 проверяет еще раз if (obj.isCreated == false) и оказывается, что ничего делать уже не нужно.
Таким образом если какая-то операция происходит довольно редко, то такая реализация ленивой инициализации в многопоточной среде увеличит производительность.
Естественно, я не использую конструкцию lock, заменяя ее CompareExchange, но для примера привожу именно lock.
Важное уточнение: isCreated = true вашего объекта в конструкторе должен идти последним, т.е. когда мы уже можем использовать объект.
#multithreading #csharp #lock
У меня в BECS очень много всяких вариантов локов, например, для ресайзов вида:
if (obj.isCreated == false) {
obj = new Obj();
}
Но поскольку мне нужно многопоточность, самый простой вариант обернуть это в лок:
lock (lockObj) {
if (obj.isCreated == false) {
obj = new Obj();
}
}
Но поскольку операция создания объекта происходит крайне редко (тут даже скорее лучше бы подошел пример с ресайзом массива), то какой смысл тратить перф на блокировку? Никакого.
if (obj.isCreated == false) {
lock (lockObj) {
if (obj.isCreated == false) {
obj = new Obj();
}
}
}
Получается такая матрешка, но давайте резберемся что будет для нескольких потоков:
1. Поток №1 входит в условие if (obj.isCreated == false);
2. Поток №2 тоже может успеть войти в это условие;
3. Поток №1 блокирует объект;
4. Поток №2 ожидает снятия блокировки;
5. Поток №1 создает объект и записывает его;
6. В этом месте любое количество других потоков может войти в метод и уже использовать наш объект;
7. Поток №1 выходит из блокировки, освобождая поток №2
8. Поток №2 проверяет еще раз if (obj.isCreated == false) и оказывается, что ничего делать уже не нужно.
Таким образом если какая-то операция происходит довольно редко, то такая реализация ленивой инициализации в многопоточной среде увеличит производительность.
Естественно, я не использую конструкцию lock, заменяя ее CompareExchange, но для примера привожу именно lock.
Важное уточнение: isCreated = true вашего объекта в конструкторе должен идти последним, т.е. когда мы уже можем использовать объект.
#multithreading #csharp #lock
🔥21👍8🤔4🥰3
https://www.youtube.com/watch?v=eQODSTApGrU
Поговорили тут немного про использование unsafe.
#record #stream
Поговорили тут немного про использование unsafe.
#record #stream
YouTube
unsafecsharp: Как и для чего использовать Unsafe в C#
Разберем примеры unsafe кода, примеры использования
Телеграм канал: @unsafecsharp
Телеграм канал: @unsafecsharp
🔥36👍10💋3🥰2
Unity Collections
Тут что-то на днях я заглянул в UnsafeQueue и заметил, что юнитехи ее переработали, и зачем-то добавили пул. Ну вот я не знаю что у них в головах когда они так делают, но в итоге если делать new UnsafeQueue(Allocator.Temp), то эту коллекцию обязательно нужно освобождать через Dispose, иначе эти самые пулы не будут возвращены.
Тут я подумал, может я че не заметил в изменении доки?
Да нет, все как и раньше: если создал Temp, то освобождать ее нет необходимости.
Короч, я очень надеюсь, что они исправят эту ошибку и вообще уберут пул, который зачем-то запихнули в Queue, при этом только туда, т.е. UnsafeList, например, остался как и был.
#unity #unsafe
Тут что-то на днях я заглянул в UnsafeQueue и заметил, что юнитехи ее переработали, и зачем-то добавили пул. Ну вот я не знаю что у них в головах когда они так делают, но в итоге если делать new UnsafeQueue(Allocator.Temp), то эту коллекцию обязательно нужно освобождать через Dispose, иначе эти самые пулы не будут возвращены.
Тут я подумал, может я че не заметил в изменении доки?
Because a Temp allocator gets discarded as a whole, you don't need to manually deallocate Temp allocations, and doing so does nothing.
Да нет, все как и раньше: если создал Temp, то освобождать ее нет необходимости.
Короч, я очень надеюсь, что они исправят эту ошибку и вообще уберут пул, который зачем-то запихнули в Queue, при этом только туда, т.е. UnsafeList, например, остался как и был.
#unity #unsafe
🤔29👍9🔥4
https://www.youtube.com/watch?v=s0ABebPu4Vg
Поговорили немного про многопоточность, блокировки и прочие штуки 🙂
#record #stream
Поговорили немного про многопоточность, блокировки и прочие штуки 🙂
#record #stream
YouTube
unsafecsharp: Многопоточность и как ее использовать
Разберем что же такое lock, как избежать блокировок, поговорим немного о cache line и как же работать с несколькими потоками.
Телеграм канал: @unsafecsharp
Телеграм канал: @unsafecsharp
🔥25❤3👍3💅3🍓1💋1
SerializeReference
Я уже рассказывал про этот аттрибут, который позволяет серилизовать любую структуру. Но недавно я столкнулся с интересным багом, т.к. по мне так это именно баг:
Если добавить поле с именем
На всякий случай я заглянул в доку и не нашел там ничего про то, что нельзя использовать поле с этим именем.
#unity #bug #serializereference
Я уже рассказывал про этот аттрибут, который позволяет серилизовать любую структуру. Но недавно я столкнулся с интересным багом, т.к. по мне так это именно баг:
public interface IMyData {}
[System.Serializable]
public struct MyStruct {
[SerializeReference]
public IMyData data;
public int references;
}
public MyStruct[] items;
Если добавить поле с именем
references в эту же структуру где у вас будет SerializeReference, то при компиляции будет ошибка The same field name is serialized multiple times с указанием именно на поле references.На всякий случай я заглянул в доку и не нашел там ничего про то, что нельзя использовать поле с этим именем.
#unity #bug #serializereference
🤔24🤣12🔥6👍5😁2
Assembly Definition и Assembly Definition ReferenceЯ уже затрагивал эту штуку несколько раз, но совсем забыл рассказать про
Assembly Definition Reference.Если
Assembly Definition - это непосредственно либа, которая собирается из папки.То
Assembly Definition Reference - это продолжение этой же либы, но уже в другой папке.Итого можно сделать несколько папок, которые будут собираться в одну библиотеку.
Это на самом деле дает возможность использовать internal классы/структуры/методы из пакетов, которые вы используете в проекте. Т.е. просто сделать указание на либу из пакета и можно спокойно использовать нужные internal методы.
#unity #asmdef #asmref
👍29🔥12🤔4❤🔥1
Буду на gdc, если кто-нибудь хочет встретиться - буду рад поболтать, обсудить проекты или еще что-нибудь :)
Если хотите что-нибудь конкретное узнать со стендов/докладов, могу сделать несколько постов.
#gdc2025
Если хотите что-нибудь конкретное узнать со стендов/докладов, могу сделать несколько постов.
#gdc2025
🔥43👍13🥰2
Unity 2d
Sprite Mask
Теперь можно использовать любой 2d renderer в качестве маски через mask source, включая tilemap.
Sprite Libraries
Выше уровнем, чем SpriteAtlas, по сути дает возможность нормально контролировать использование ресурсов.
Через Sprite Resolver можно подставлять части.
Pixel art
Поддержка aseprite, новый importer, включая анимации.
Performance
Завезли SRP Batcher.
2D light batching debugger по слоям.
Physics
Появились Layer overrides.
Simulation layers позволяют отключить слои симуляции.
Delaunay tessellation для генерации коллайдеров.
Rigidbody slide functions.
Advanced
Sprite shape geometry creator позволяет делать сложные формы.
ScriptablePacker позволяет интегрировать свой алгоритм паковки.
2D Renderer поддерживает Render Graph.
WIP
Render as 2D - позволяет рендерить 3д объекты как 2д с сортировкой, тенями, светом и пр.
#gdc2025
Sprite Mask
Теперь можно использовать любой 2d renderer в качестве маски через mask source, включая tilemap.
Sprite Libraries
Выше уровнем, чем SpriteAtlas, по сути дает возможность нормально контролировать использование ресурсов.
Через Sprite Resolver можно подставлять части.
Pixel art
Поддержка aseprite, новый importer, включая анимации.
Performance
Завезли SRP Batcher.
2D light batching debugger по слоям.
Physics
Появились Layer overrides.
Simulation layers позволяют отключить слои симуляции.
Delaunay tessellation для генерации коллайдеров.
Rigidbody slide functions.
Advanced
Sprite shape geometry creator позволяет делать сложные формы.
ScriptablePacker позволяет интегрировать свой алгоритм паковки.
2D Renderer поддерживает Render Graph.
WIP
Render as 2D - позволяет рендерить 3д объекты как 2д с сортировкой, тенями, светом и пр.
#gdc2025
👍37🔥19❤3🥰1🎉1🤗1🫡1
Performance tips & tricks
Advanced profiling
CPU Native profilers (ios Instruments, android studio, etc)
Или
Use Superluminal (подключается к юнити, показывает методы включая cpp)
CPU issues
Shader variants (их может быть тысячи)
Используйте shader prewarming
SVC имеют метод WarmUp
GraphicsStateCollections заменяют SVC в unity 6
MPB не используйте в URP /HDRP
MeshRenderer в некоторых случаях будет лучше: если рисуем одну мешку больше 1к раз, используем одинаковые свойства
Используйте RenderMeshInstanced или BRG
Draw calls: используйте HLOD
Используйте меньше камер, каждая камера имеет большой оверхед
Используйте разные SRP для разных камер
Resident Drawer
Split jobs
GPU issues
Overdraw: прозрачные объекты перекрывают друг друга, исключите полностью прозрачные объекты
Используйте renderdoc
Quad Overdraw: используйте LOD/HLOD
Shaders: исключайте if (например shader keywords), используйте half, int16
Memory
Используйте native profilers
Избегайте коротких (по времени) и длинных аллокаций вместе
Используйте unmanaged аллокации
Используйте Memory profiler memory map
Используйте GC.Alloc call stacks feature
Asset duplication - ресурсы будут загружены в память несколько раз (при использовании бандлов)
Shaders
Используйте shader_feature вместо multi_compile
Используйте IPreprocessShaders API
#gdc2025
Advanced profiling
CPU Native profilers (ios Instruments, android studio, etc)
Или
Use Superluminal (подключается к юнити, показывает методы включая cpp)
CPU issues
Shader variants (их может быть тысячи)
Используйте shader prewarming
SVC имеют метод WarmUp
GraphicsStateCollections заменяют SVC в unity 6
MPB не используйте в URP /HDRP
MeshRenderer в некоторых случаях будет лучше: если рисуем одну мешку больше 1к раз, используем одинаковые свойства
Используйте RenderMeshInstanced или BRG
Draw calls: используйте HLOD
Используйте меньше камер, каждая камера имеет большой оверхед
Используйте разные SRP для разных камер
Resident Drawer
Split jobs
GPU issues
Overdraw: прозрачные объекты перекрывают друг друга, исключите полностью прозрачные объекты
Используйте renderdoc
Quad Overdraw: используйте LOD/HLOD
Shaders: исключайте if (например shader keywords), используйте half, int16
Memory
Используйте native profilers
Избегайте коротких (по времени) и длинных аллокаций вместе
Используйте unmanaged аллокации
Используйте Memory profiler memory map
Используйте GC.Alloc call stacks feature
Asset duplication - ресурсы будут загружены в память несколько раз (при использовании бандлов)
Shaders
Используйте shader_feature вместо multi_compile
Используйте IPreprocessShaders API
#gdc2025
🔥22👍10🥰1
Тут один хороший человек расписал работу моего аллокатора, который я использую в ME.ECS и ME.BECS. За что ему отдельное спасибо :)
P.S: Для чего нужен аллокатор и сорсы в этом посте: https://news.1rj.ru/str/unsafecsharp/49
#allocator #memory
P.S: Для чего нужен аллокатор и сорсы в этом посте: https://news.1rj.ru/str/unsafecsharp/49
#allocator #memory
🔥23👍10🤯7❤3
Animator
На самом деле тут есть 2 момента, которые я бы хотел обозначить с точки зрения кода:
1. У аниматора есть
Например: при клике по кнопке нужно проиграть анимацию, которая висит на рут объекте, но будет затрагивать объект, который мы создаем в иерархии. Тогда перед тем как "заказывать" стейт для проигрывания у аниматора, нужно сделать animator.Rebind().
2.
#animator #unity #animations
На самом деле тут есть 2 момента, которые я бы хотел обозначить с точки зрения кода:
1. У аниматора есть
Rebind. Используется в том случае, если анимация затрагивает объекты, которые создаются динамически, но на момент запуска аниматора их еще не было. Например: при клике по кнопке нужно проиграть анимацию, которая висит на рут объекте, но будет затрагивать объект, который мы создаем в иерархии. Тогда перед тем как "заказывать" стейт для проигрывания у аниматора, нужно сделать animator.Rebind().
2.
keepAnimatorStateOnDisable. Это такая штука, которая позволяет "сохранить" (точнее не сбрасывать) состояние аниматора при выключении и последующем включении объекта, на котором есть аниматор. На самом деле если перейти в debug-режим инспектора, то эту галочку можно включить там и без кода.#animator #unity #animations
👍27🔥13❤1🫡1
ME.BECS #1: Создание и инициализация проекта
@heavyfront сделал то, до чего у меня не доходили руки;) За что ему огромное спасибо!
https://youtu.be/PCdhnXEjRQI
#becs #tutorials
@heavyfront сделал то, до чего у меня не доходили руки;) За что ему огромное спасибо!
https://youtu.be/PCdhnXEjRQI
#becs #tutorials
🔥15👍11🥰2😱2