Как стать автором
Обновить

Комментарии 85

Но это не id Tech 5. А всeго лишь обновленный код id Tech 4.
Собственно это и написано в оригинале. Какой-то у вас странный перевод.
Я не совсем точно передал суть — DOOM III BFG базируется на обновленном IDTech 4 с элементами IDTech 5, вы правы, hacenator.
Как увидел «во 3ей части» и «во 4ой части», подумал что автор поста — Мицгол, но оказалось что нет. Редко где подобное встретишь.
Вот так начало пути на хабре — сравнили с Мицголом. Только хорошо это или плохо? :)
(а вообще копипаст зло, исправил, спасибо)
> в коде не используются смарт-указатели или Boost. Какое облегчение (ведь это то, что обычно делает код нечитаемым).
Неплохой такой наезд на языки со сборкой мусора. Или просто к тому, что при правильном проектировании есть чёткое место создания и уничтожения объекта.
О чем вы? Boost и умные указатели действительно внешне захламляют код, если не пользоваться typedef'ами. Никакого наезда на языки с GC.
Приятно, что GNU Compiler Collection не стал наезжать на языки с GC. ))
auto тоже очень помогает. Да и тайпдефы таки удобная вещь, многие фичи из C++11 на деле сильно улучшают читабельность, хотя могут и во вред использоваться.
Вы правы, стоит добавить в пост.
Ещё стоит перевести заголовок "C++ подмножество:" на русский.
Я слышал прекрасный термин для этого — C+ :)
А что плохого во фразе «кирпич дом»?

(Автор тот заголовок уже исправил, спасибо ему.)
Doom III — отличный движок, на нем и сейчас можно делать великолепные игры.
Что же, ждем остальные 3 части перевода.
Не знаю, как вы, а я был сильно удивлен, узнав, что у них внутри Flash для рендеринга GUI.
Flash, а точнее Scaleform GFx, почти что стандарт для GUI в больших игровых проектах.
вот только у них не Scaleform, а свой парсер/рендер, как ни удивительно
По-моему, сделать меню было бы проще, чем сделать флеш.
Подозреваю, что эту часть кода они тоже из idTech 5 портировали.
А в RAGE он зачем? Тоже меню?
Для меню и для всех интерфейсов, думаю.
Не понимаю смысла. Анимация?
Производительность, скорость разработки и практически бесплатная масштабируемость.
Производительность и флеш ну совсем не вяжутся.
Речь ведь про UI идет, все нормально.
Речь идёт о тяжёлой виртуальной машине, поедающей довольно много памяти и тактов процессора, которые могли бы пойти на саму игру.

Эх, как жалко, что из id давно ушёл Майкл Эбраш, который выполняет целочисленные вычисления параллельно с дробным делением ради трёх десятков циклов.
Почитайте про ScaleForm GFx, который работает тоже как флеш. Практически во всех современных ААА-тайтлах (по крайней мере, в шутерах) используется именно он. Вручную делать то, что можно сделать с помощью скейлформа — на порядок более сложная задача.
Соббственно, тут id извернулись и соорудили свой велосипед в духе скейлформа.
Не «как флеш», это он и есть с аппаратным ускорением и парой фич типа z глубины.
UI по-вашему рисуется независимо от основного цикла рендеринга?
Не важно, где оно рисуется, всё равно, можно было ресурсы, потраченные на лишнюю виртуальную машину, использовать в более важном для игрового процесса месте.
Я и не спорю, скорее наоборот. На UI точно так же тратится часть драгоценных draw call'ов, тактов процессора, и т.д. Сомнительный профит использовать для него жирный движок рендеринга векторной графики. Впрочем, Кармаку виднее.
Судя по ужасно глючному стримингу (мыло при повороте головы) в RAGE, Кармак стал Иисусом или Магометом, но он уже уж точно не бог геймдева. Поэтому данный поступок вполне объясним. Да и уже во времена Doom 3 он допускал себе такие вещи, как модели в текстовом формате. Не мне судить, конечно, но по сравнению с первым Doom или Quake это так.
Настоящий бог графического программирования — это Тим Суини.

Кармаку работать нужно совместно с Абрашем, они взаимодополняют друг друга. Движок Quake тормозил бы как без разделения полигонов моделей пополам Кармака, так и без параллельной коррекции перспективы Абраша.
Это как с квейком, до того, как там появился сlient prediction — у Кармака был быстрый интернет T1 и он все делал с расчетом на маленькие пинги до 200 мс. Так и тут, наверняка у Кармака стоял SSD, с которого все замечательно стримится.
Да, и у него был OpenGL, который тогда не поддерживала ни одна коммерческая видеокарта, и он для разработки Quake одно время хотел использовать суперкомпьютер Cray, но не успел его купить. При этом странно, что несмотря на огромный широкоформатный монитор у Кармака, меню в Quake 3 нормально работает только на 4:3.
Я думаю, что причина отсутствия умных указатаелей, исключений, ссылк и boost кроется в том, что код тянется с тех времен, когда этого всего еще не было (ну или оно было нестандартизовано и работело не пойми как). Так что перед тем, как использовать такой подход в новом проекте, все же лучше хорошо подумать.
Связано с тем, что это игра. А при написании игр важнейшим фактором является производительность. Соответственно, дорожат каждой миллисекундой.

Также, как и везде, большое значение имеет то, насколько код прост. Чем крупнее проект, тем важнее этот фактор.
Например, smartptr добавляет видимую простоту, но в долгосрочной перспективе он добавляет большое количество неявных мест, что в итоге не идет на пользу проекту.

Если бы это было, предположим, офисное приложение, то это было бы не так страшно, но в условиях когда идет борьба за каждый кадр — чем меньше таких штук тем лучше, тем проще оптимизировать, тем проще предсказать поведение программы и так далее.
Большая часть стандартной библиотеки С++ реализована через шаблоны. Несмотря на то, что выглядить они могут очень громоздоко, они эффективно раскручиваются, иналйнаятся и оптимизируются.

Конечно те же умные указатели нужно знать, как применять, а не лепить везде «универсальный» shred_ptr. Тот же unique_ptr развернется в пару new и delete, от всех геттеров не останется вообще ничего после инлайна.

Что вы понимаете под «неявными местами», мне непонятно. Использование сырых указателей как раз дает горзадо бОльший простор для ошибок.
В вашем комментарии я вижу уже как минимум два неявных места:

> они эффективно раскручиваются, иналйнаятся и оптимизируются
> от всех геттеров не останется вообще ничего после инлайна.

это зависит не только от компилятора, но даже и от его версии и ключей компиляции, вот вам первое место, в котором вы не можете быть уверены. уверенность здесь может быть достигнута только путем накопления большого количества опыта и глубокого знания своего инструмента. поменялся компилятор/платформа — думаю понятно.

> Тот же unique_ptr развернется в пару new и delete

здесь мы, например, не всегда сможем быстро сказать где конкретно (и когда) будет удален объект. придется шерстить код не только на предмет reset, но и на предмет out of scope / operator=. Если же мы используем сырые указетели, то место удаления можно предсказать с точностью 100%.

> Что вы понимаете под «неявными местами», мне непонятно.

Надеюсь я добавил понимания.

Ещё раз обращаю ваше внимание, что здесь я говорю именно про игровые проекты уровня Doom 3 (собственно про это и топик). Если мы пишем другие проекты, то применение технологий стоит рассматривать в их контексте отдельно.
здесь мы, например, не всегда сможем быстро сказать где конкретно (и когда) будет удален объект.

Во-первых, мы точно знаем, что он будет удален. При out-of-scope.

В 99% процентов случаев только там. На это можно рассчитываеть по умолчанию. И не надо ничего шеристить: scoped_pointer — удален при out-of-scope. Правило такое хорошего тона. Можно ведь еще и гайдлайн на это дело ввести.

Вообще, нормальные люди очень редко использют reset посередине использования указателя.

Если же мы используем сырые указетели, то место удаления можно предсказать с точностью 100%.

Прошерстив код на предмет delete.

В общем, если изуить стандартную библиотеку, то все у вас будет хорошо, а если вам лень этим знаиматься — можно и дальше придумывать велосипеды и думать, что они по умолчанию работают лучше.
> Во-первых, мы точно знаем, что он будет удален. При out-of-scope.

Но вы пропустили ещё одну часть моей мысли. Этот out-of-scope ещё надо найти. Думаю, не надо объяснять что он очевиден только при использовании в пределах функции.

Кстати, немного отклонюсь от темы чтобы сообщить вам, что в играх стараются не использовать (а в некоторых не используют вообще) аллокации непосредственно во время игры. Что делает проблему указателей и выделения памяти чуть менее тяжелой и делает необходимость в «умных» указателях не такой важной как может показаться.

> В 99% процентов случаев только там.

Оставшийся процент как раз и даст о себе знать. Никого не интересуют простые паттерны использования той или иной технологии.

> Прошерстив код на предмет delete.

Да, но ТОЛЬКО на предмет delete и ничего более.

> Правило такое хорошего тона.
> нормальные люди очень редко использют reset посередине использования

Я бы не стал полагаться на это при написании больших проектов.

> если изуить стандартную библиотеку

Здесь вы, кажется, пропустили мой самый первый абзац. Стандартных библиотек, кстати, несколько. Каждую изучать?
Этот out-of-scope ещё надо найти

Что значит найти? Если указатель член класса — то заврешение времени жизни объекта класса. То есть после вызова деструктора до вызова деструктора родительского класса. delele вы тоже найдете в какой-то функции и вам также нужно будет определить, когда она вызвалась.

Основная беда с указателями ведь в том, что их забывают инициализировать и удалять. И тут умные указатели как раз очень ускоряют разработку, позволяют быстро находить места ошибок.

Стандартных библиотек, кстати, несколько. Каждую изучать?

Стандартная библиотека, она одна. Она на то и стандартная. Исходя из сущестующих компилляторов, можно ориентироваться на MSVC как на минимально поддерживающего новые фичи.

По поводу одного процента исключений — такие и в С-стиле бывают. Знатете, вызвали вы функцию printArray(ptr), а она ptr и удалила. Тоже долго искать будете.
Я заметил тенденцию восприятия моего текста фрагментарно. Не забывайте про комплексность проблемы. Про то, что у нас здесь большой проект, которому нужна производительность, портируемость, возможность работы над ним нескольким программистам и наверное что нибудь ещё. В случае написания каких-нибудь десктопных приложений ваш подход вполне может иметь место.

Или вот вы схватились за «умные» указатели, но кроме них предлагали использовать буст и исключения. Я же привел указатели как самый очевидный и легкий пример для обсуждения, подразумевая, конечно, что проблема не только в них, но целиком в ошибочности (в конкретном случае) подхода, который вы предложили.

> Что значит найти?

Предположим, что объект в unique_ptr у нас удаляется только по завершении жизни некоего класса, жизнь которого зависит от другого класса, а того от третьего. И таких классов в проекте, ну, пусть даже сотня. А у некоторых зависимость не от одного, а, предположим, от жизни двух или трех. Это что же, везде сидеть искать где заканчивается scope? Сами в своем коде мы может это всё и сможем проверуть. А коллеги?

В случае с простым delete мы опять же можем это сделать быстрее и точнее. Всегда можно быстро тыкнуть в код и сказать — вот, класс удаляется здесь, и он ещё тянет за собой вот эт и то. Явно.

Но, тут я повторюсь, это уже скорее не проблема игр, где памятью стараются управлять немного иначе, что делает использование «умных» указателей не оправданным ещё в большей степени.

> Стандартная библиотека, она одна. Она на то и стандартная

Я имел в виду реализации стандартных библиотек. У которых местами немного разное поведение.

> вызвали вы функцию printArray(ptr), а она ptr и удалила

Боюсь что этот пример как раз показывает отсутствие преимуществ у «умных» указателей в данном случае, т. к. неумелый программист может и reset сделать где не надо, что полностью нивелирует профит. И если явный delete мы найдем быстро, то неявный out of scope можем найти быстро не всегда. Как минимум придется посмотреть где что умирает и от чего зависит.

> можно ориентироваться на MSVC как на
> минимально поддерживающего новые фичи

Мы всё ещё говорим про игры уровня Doom 3? Вы программируете только для Windows?
Не то, чтобы фрагментарно, просто стараюсь отвечать на конкретные предложения, иначе можно совсем запутаться что и к чему сказано.

Это что же, везде сидеть искать где заканчивается scope?

Я же написал, что в случае delete, вам придется точно так же искать, кто вызыввает функцию, которая удаляет объект. просто примем define out-of-scope деструктор. Т.к. деструктор — то же функция, то можно сказать что проблема нахождения out-of-scope эквивавлента проблеме поиска мест вызова функции, в который объект удаляется.

Боюсь что этот пример как раз показывает отсутствие преимуществ у «умных» указателей в данном случае, т. к. неумелый программист может и reset сделать где не надо, что полностью нивелирует профит.


Тут весь смысл в том, что в отладочной версии умный указатель будет контроллировать доступ к соедржимому, и вместо неочевидного segmantation fault получим более очевидный assert: deref null ptr. Конечно, от дурака это не спасает, а от невыспавшегося программиста — вполне.

Я имел в виду реализации стандартных библиотек. У которых местами немного разное поведение.

Поведение описано в стандарте. Оно не может быть разным. Различаться может только поведение, не описанное в стандрате, но если вы на него полагаетесь, то тут уж сами виноваты. UB есть UB.

Ну, по большому счету, у нас сейчас есть 3 семейства компилляторов — gcc, clang, cl(MSVS). Последние наиболее лениво добавляют поддержку новых фич из стандарта.
А что такого плохого в играх уровня Doom 3?

За 9 лет изменились только размер текстур, количество полигонов и добавились шейдеры геометрии. Ну ещё многопоточность усилилась.
Плохого в них ничего нет, я немного не понял вопрос.

Здесь я имел в виду, что код игры достаточно сложный и большой. Соответственно, обсуждать его нужно с этих позиций и написанные мной мысли применимы именно в таком контексте.
Не такой уж он и большой, кстати

[/g/projects/DOOM-3-BFG]$ cat `find . -name \*.cpp -or -name \*.h` | wc -l
563595


Даже ваши нелюбимые офисные приложения разрастаются гораздо сильнее.

Вот, для примера, Webkit — большой

[/g/qt-everywhere-opensource-src-5.0.0/qtwebkit/Source]$ cat `find . -name \*.cpp -or -name \*.h` | wc -l
2147287
> нелюбимые офисные приложения

Я нигде не упоминал люблю я офисные приложения или не люблю. Это ваши выдумки.

> Не такой уж он и большой, кстати

Какой же он? ) С какого количества строк начинается большой?
Ну про нелюбимые, это было в шутку.

Просто я работаю в команаде над примерно таким же по размерам проектом, в общем-то, никаких особых сложностей, вызванных объемом, не испытываю.

Большой — это когда один человек не в состоянии охватить весь проект хотя бы на поверхностном уровне. Это где-то от миллиона, как мне кажется.
я работаю в команаде

Хотел бы и я работать в КоМонаде…
В играх boost — никому не нужный жилец.
Не говоря уже о том что на консолях он вряд ли взлетит.

А за каждую аллокацию в хипе внутри игрового фрейма, хорошо бы выписывать автору удар плетьми.

Аллокаторы типа dl-malloc спасают от хаоса мелких обьектов, выделяя их в пулах, но неумелая работа с крупными обьектами приведёт к фрагментации хипы, что в условия бедных на память консолей — прямой путь к неприятностям.
Вы так говорите, как будто указатели не умеют работать с собственными deleter-ами.

А чем плох буст? Некоторые части может и не взлетят, но как минимум половина буста — не привязана к ОС, а если взлетит та, что привязана, то избавит разработчиков от проблем с портированием.

Бонус: развелкуха с умными указателями
void* pool_malloc(size_t sz)
{
	std::cout << "creating in pool\n";
	return malloc(sz); /*fallback*/
}

void pool_free(void*ptr)
{
	std::cout << "free in pool\n";
	free(ptr);
}

template <typename T>
struct my_deleter
{
	void operator()(T*ptr) const
	{
		ptr->~T();
		pool_free(ptr);/*fallback*/
	}
};

template <typename T>
using pool_ptr = std::unique_ptr<T, my_deleter<T>>;


template <typename T>
struct my_alllocator
{
	template <typename... Args>
	T* operator()(Args... args) const
	{
		void* t = pool_malloc(sizeof(T));
		assert(t);
		new (t) T(args...);
		return static_cast<T*>(t);
	}
};

template <typename T, typename...Args>
pool_ptr<T> pool_alloc(Args... args)
{
	T* ptr = my_alllocator<T>()(args...);
	return pool_ptr<T>(ptr);
}


/*usage*/

int main()
{
	pool_ptr<int> ptr = pool_alloc<int>(42);
}

Он тяжёлый, непортируемый и медленно компилирующийся. И какой с него толк в играх?

Чтобы код быстро работал, необходимо заботиться о данных, консолидировать их в линейные куски, дабы уменьшить количество кеш/TLB промахов. Требуется индивидуальный подход, а не «one size fits all».

«Развлекуха» с указателями не нужна. Нужно чтобы блоки выделялись и удалялись в строго определённых местах в правильной последовательности.

Что значит, непортируемый? Если на вашей платформе есть нормальный компилятор С++ и POSIX, то особых проблем быть не должно. Долгая компиляция — это вообще не серьезно. Какая разница, сколько он компилироваться будет?

Относительно памяти — одно другому не мешает. Использовании RAII может только упростить управление ресурсами.
>> от которой ваши глаза будут кровоточить
Я, если честно, просто офигел от этого кода, но ровно до того самого момента, пока не решил сохранить его, чтобы показать друзьям. Как только я вставил его в редактор ( Sublime Text 2), все стало великолепно читаемо)
Гугловый c++-код очень профессионален и отлично читается, хоть обычно и не тривиален.
лазерный прицел теперь не может быть использован вместе с оружием.


Странно. Вроде и проходил игру неоднократно, а никакого «лазерного прицела» вспомнить не могу. Он встречается в аддоне или мультиплейере? Правда интересно стало.
Потому что в оригинале «Flash light». Прошу прощения. Просто изначально мне показался странным отказ от фонарика, и написал прицел, а потом просто забыл об этом.
А разве раньше фонарик не был отдельным оружием?
Был, а сейчас его на плечо нацепили из-за барона ада у игроков, которых убивали с фонариком в руках.
Я поправил текст, меня смутил в оригинале «duck taped».
Вы правы, duck tape вместо duct tape — очень популярная описка.
Это именно ошибка или языковая норма американского английского?
По данным википедии, изначально (с 1900-х) лента называлась «duck tape» (от duck в значении «парусина»), с 1950-х её стали называть «duct tape», в настоящее время название «duct tape» встречается чаще.
Интересно весьма, на счёт SWF-движка — насколько он полноценный? Скажем, сделать из него плагин для тех же браузеров интересно реально?
Он далеко не полноценный — не поддерживается SWF дальше девятой версии, плюс никакого AS3; только AS2 с командами от SWF седьмой версии.

Если Вас интересуют открытые альтернативы флешу, то самыми перспективными мне кажутся Lightspark и Mozilla Shumway.
У Lightspark-а нет поддержки AS2, ну а Shumway… Медленный, очень. На демке Box2D отлично видно. В любом случае спасибо за информацию.
Открыть Visual Studio 2010 Express и нажать F8 для компиляции. Готово!


Не так просто. Там в проекте Doom3BFG часто используется #include <atlbase.h>, который в express-е не водится. Похоже нужен или Windows 2003 SDK или Professional edition.
В Windows Driver Kit 7.1.0 он точно есть, в Windows Driver Kit 8 тоже может быть, но не знаю.
Интересно было бы почитать про сетевую архитектуру этого движка.
UDP с самодельным слоем для надёжной передачи.
Интересно не столько что это, а то как это устроено изнутри, как идёт синхронизация, какие данные перелаются и прочее.
ИМХО, очень маловероятно, что сетевой код там лучше, чем в той же ET: QW, которая изначально была мультиплеерная. Даже в (уже старой) Quake III сетевой код очень хорош и есть, чему поучиться :).
Подисываюсь.
Не Doom 3, но близко и тоже занятно.
Спасибо!
Движок idTech 4, которому уже почти 10 лет был обновлен до idTech 5 (Rage — первая игра на этом движке), и с его исходным кодом ознакомиться было очень интересно.

Я бы назвал движок «idTech4 улучшенный», т.к. по сути это idTech4, но с использованием элементов idTech5:

Не понял. Так «обновлен до idTech 5» или ««idTech4 улучшенный», т.к. по сути это idTech4, но с использованием элементов idTech5»?
Начало интригующее, буду ждать вторую часть.
Умные указатели делают код нечитаемым.
А вот если в функции восемь раз написано «delete p» — это, разумеется, способствует повышению читаемости и надежности кода.

cdriper.blogspot.com/2011/11/blog-post_25.html
Да. Потому что на 100% знаешь, когда освободится память.

Но судя по ссылке, комментатор говорит с глупым сарказмом.
На самом деле, да простит меня SiPlus, у меня примерно такое же ощущение от кода doom3 возникло. Экстаза по поводу кода Кармака никогда не испытывал. Думал, может я слишком избалован языками с фокусом на семантике и dsl? Ан нет, другие тоже возмущаются. Идеи в коде встречаются интересные, но толковым C++ (таким, каким его видел создатель) там и не пахнет. С моей точки зрения поддерживать такие проекты довольно сложно.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории