Форум Игромании
 
Регистрация
Справка

Программирование Создание, разработка и доработка различных игр и программного обеспечения

Ответ
 
Опции темы
Старый 18.02.2008, 19:28   #1
Пугатель
 
Аватар для [CCCP] Monster

 
Регистрация: 26.06.2005
Адрес: Москва, СССР
Сообщений: 6,102
Репутация: 1085 [+/-]
Копилка опыта для программистов

В этой теме будем публиковать различные полезные данные, алгоритмы и приемы программирования, которые могут помочь в игростроении

Инструкция по добавлению статей:

1. Необходимо указать в теме сообщения, собственно, тему статьи, при этом должна соблюдаться нумерация тем, начиная с "1" и до победного конца (уточняю, ибо программеры любят начинать с "0")

2. Участки текста, которые содержат примеры кода должны быть обрамлены служебным форумным тэгом [ CODE ]

3. Статьи модерируются. Информация в статье должна быть интересна не только автору, но и остальным посетителям нашего раздела. Тексты, содержащие явно неверные сведения, либо спорные сведения, либо сведения, разжигающие "холивары", такие статьи будут удаляться без предупреждения.

4. Автором статьи должны либо являться Вы сами, либо автор должен быть указан и должна прилагаться ссылка на источник информации.

5. Утвержденные статьи будут иметь дублирующие ссылки в этом (первом) посте темы для удобства навигации.

6. При утверждении статье выдается рейтинг (по возрастанию) - "новичку", "опытному", "знатоку" и "профессионалу". Соответственно, сложность изложенного материала возрастает сообразно шкале.

СОДЕРЖАНИЕ

1. Подводные камни преобразования типов. Знатоку.
__________________
Служу Советскому Союзу!

Хорошо смеется тот, кто стреляет первым! (танкистская мудрость)

Последний раз редактировалось [CCCP] Monster; 18.02.2008 в 21:40. Причина: Добавлено сообщение
[CCCP] Monster вне форума  
Отправить сообщение для [CCCP] Monster с помощью Skype™ Ответить с цитированием
Старый 18.02.2008, 21:20   #2
Пугатель
 
Аватар для [CCCP] Monster

 
Регистрация: 26.06.2005
Адрес: Москва, СССР
Сообщений: 6,102
Репутация: 1085 [+/-]
1. Подводные камни преобразования типов.

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

По сути своей, тип данных - это инструкция для компилятора, как трактовать массив байтов, находящийся в памяти по заданному адресу.

И из-за этого начинаются пляски с бубнами. Потому что есть такие типы, как строка, целое числои с плавающей почкой.

Строка в памяти хранится, как массив байт, где один байт кодирует один символ (в случае с ASCII, если это UNICODE, тогда 2 байта). Т.е. если, например, строка хранит число "10000", то она будет состоять из 5 байт, каждый из которых кодирует свой символ, и плюс еще один, нулевой символ в конце строки, показывающий, что строка кончилась и дальше значащих символов нет. Чтобы сконвертировать эту строку в целое, надо провести серию операций, которые реализуются стандартной библиотечной функцией atoi. Естественно, что такая конвертация отнимает время. Именно это и происходит, когда вы пишете:

Код:
int a;
AnsiString b = "10000";

a = (int)b;
Теперь число типа float. Дело в том, что дробные числа - очень важная штука в математике, и чтобы их считать, придумали целый формат данных, а в АЛУ процессоров x86 был внедрен математический сопроцессор, который занимается обработкой таких чисел. А представлены они в памяти в формате IEEE, который бывает 32, 64 и 80 разрядным. Сейчас правда еще и 128 разрядный есть. Суть там в том, что биты слева направо в числе задают свойства числа, т.е. (слева направо) первый бит - знак степени (0 - "+", 1 - "-"), далее 7 бит - степень (0-128), потом еще бит - знак самого числа, и все оставшиеся биты - это само число.

Подробнее см. в описании процессоров интел и ассемблера, нет смысла сейчас все это подробно расписывать, разговор-то не об этом.

Так вот, чтобы, например, провести такую операцию:

Код:
int a;
float b = 10.0f;
a = b;
Процессору необходимо перевести число из формата IEEE в формат целого 32-разрядного числа, и только после этого выполнить присваивание (команду MOV). Поэтому такой код тоже выполняется с задержками.

Есть еще один нюанс. Бывает, что вам необходимо считать из памяти по заданному адресу какие-то данные, которые, составляют, например, целое 32 разрядное число.


Код:
unsigned char array[8];
int count;

count = *((int*)(&array[1]));  //Случай А
count = *((int*)(&array[0]));  //Случай Б
На первый взгляд, случаи ничем не отличаются и должны работать одинаково. Мы всего лишь указали компилятору, что адрес нужного байта в массиве – это на самом деле указатель на целое число и присвоили значение этого целого переменной count. По идее, обычный зашколенный высокоуровневый программер должен сейчас сломать себе мозг, сказать что код работать не будет, либо, если он продвинутый и опытный дядька, сказать, что код будет работать одинаково быстро. Но на самом деле все далеко не так однозначно.

Руководство по процессорам Интел имеет информацию о такой вещи, как выравнивание данных в памяти по 4 байта. Это значит, что данные, читаемы процессором из памяти должны иметь указатель, кратный 4-м. Если это не так, срабатывает прерывание по ошибке. Однако ОС отслеживает такие указатели и генерирует «на лету» исполняемый код потока так, чтобы исключить этот конфликт и заставить систему работать правильно. В самом лучшем случае это делается путем замены одной команды MOV двумя, с разными адресами источников данных, так, чтобы считать только то, что нужно. Компилятор – тоже не дурак, и старается данные выравнивать, чтобы избежать нежелательных маневров с указателями, которые проводит ОС. Но вот в случае, приведенном выше, никакие маневры не помогут – ну память просто не удается выровнять.

И вот тут как раз и начинается интересное. Именно из-за выравнивания в памяти, код в случае А работает вдвое медленнее, чем код в случае Б.

Однако, мои эксперименты с быстродействием показали, что это не всегда так. В каком именно случае именно так, я пока не выяснил, но советую избегать в тех участках кода, где необходимо максимальное быстродействие, работы с данными, которые не могут быть выровнены в памяти.

Практически во всех прочих случаях преобразование типов происходит без потери производительности. В особенности это относится к преобразованияю типов указателей.

Например:

Код:
int main(void)
{
  MyClass* newptr = new MyClass();
  GetObj(newptr);
{

void GetObj(void* ptr)
{
  MyClass* var=(MyClass*)ptr;
  //some code
}
Подобная передача указателей вполне безопасна, если не обращать внимания на то, что void* сам по себе не типизирован и вы не имеете страховки, предоставляемой синтаксическим анализатором компилятора, которая может найти ошибку. Однако код будет работать без изменения производительности. Ведь мы всего лишь указали компилятору, что указатель ptr на самом деле является указателем на объект класса MyClass и теперь компилятор знает, как расположены данные по адресу указателя и какие функции могут работать с этими данными. И более ничего с этими данными не происходило.
__________________
Служу Советскому Союзу!

Хорошо смеется тот, кто стреляет первым! (танкистская мудрость)

Последний раз редактировалось [CCCP] Monster; 11.08.2010 в 03:07.
[CCCP] Monster вне форума  
Отправить сообщение для [CCCP] Monster с помощью Skype™ Ответить с цитированием
Старый 11.08.2010, 03:10   #3
Пугатель
 
Аватар для [CCCP] Monster

 
Регистрация: 26.06.2005
Адрес: Москва, СССР
Сообщений: 6,102
Репутация: 1085 [+/-]
OpenGL vs DirecX

ВОобще данный текст был написан мной в другой теме и под другому поводу, но тем не менее, думаю будет полезно. Речь пойдет о разнице между DX и OGL, откуда она берется и в каких случаях что лучше.

Теперь немного о том, почему игры с использованием D3D делают все, а с OGL - единицы, а хорошо получается вообще только у Кармака и то после выхода DX8 даже он начал сливать по технологичности, хотя и успел сотворить интегрированный в основное освещение алгоритм управления затенением по картам нормалей. OpenGL - это графический API, который был создан в те времена, когда нас с вами вообще в проекте не было, и с тех пор его спецификация постоянно дополняется специальным комитетом ARB. Этот комитет решает, какие фишки войдут в новую спецификацию, и как все это будет называться в деталях. Иногда они никак не могут договориться. Иногда отдают работу по детальной проработке спецификации и ее первичной реализации сторонним компаниям, например Khronos Group. Дык вот.

По основной задумке этот ваш OpenGL - это подсистема рендеринга, единая вообще для всей системы, которая призвана делать одну вещь - обеспечивать простой и удобный вывод векторной графики. За эту свою особенность бешено любим разработчиками всяких 3D-редакторов и CAD-комплексов, поскольку там кадры в секунду не решают, а простота и легкость создания эффективного и мощного вспомогательно GUI на окне рендеринга того, для чего разработка предназначена, очень даже приветствуется. Хотя в последнее время наметилась тенденция и тут класть на OpenGL большой и жирный пиксель. OGL работает в системе как единая машина состояний и существует в одном экземпляре для одной программы. Точнее, из-за специфики использования подлючаемых динамически библиотек, драйвер в оконной проге сам может решить, сколько свопчейнов наклепать в своей любимой видеопамяти. Чтобы два параллельно запущенных OGL-приложения передавали друг другу команды рендера - так конечно не выйдет, но вот если у вас в одной проге два окна с этой вещью - вы получите незабываемые впечатления. Самые незабываемые получаются тогда, когда у вас один из потоков использует эту вот машину состояний для отрисовки чего-нибудь в память для каких-то ваших нужд. Поскольку системе в общем все равно, какие команды и от кого в рамках одного процесса использовать, в окна и в буферную область могут выводиться совершенно непредсказуемые данные.

Но одна из главных причин, почему OGL разработчики пользуют мало - это то, что из-за своей архитектуры и спецификаций драйвера для его поддержки писать сложно. Т.е. получается, что железяка использует свою архитектуру, организацию памяти, алгоритмы, шейдерные конвейеры и прочие радости для максимально эффективного вывода геометрии. Чтобы это все использовать было можно, надо написать драйвера, которые будут управлять возможностями видеокарты. И все бы ничего, да вот спецификация OpenGL прекрасно учитывает запросы программистов, но совершенно забывает о счастье разработчиков железа и драйверов, что приводит к неоптимальности его архитектуры - спецификация-то требует абсолютно четкой и конкретной работы абсолютно конкретных функций. Совершенно никак не учитывая объективные реалии внутренней логики устройств. Для кроссплатформенности - идеально. Для скорости работы - чудовищно. Причем, что интересно, в Windows, согласно документации, библиотека OpenGL реализована посредством работы с DirectX, т.е. он физически не может быть быстрее.

DX приближен к архитектуре железа, драйверы писать под него - сущее удовольствие, поскольку он по сути предоставляет универсальный интерфейс через HAL к самому железу. Работы руками для написания быстрых алгоритмов работы с аппаратной частью там требуется больше, но результаты достигаются более эффективно, именно за счет того, что у себя на высоком уровне архитектуру системы можно спроектировать так, чтобы она учитывала именно твои нужды. Это примерно тоже самое, что строить Храм Христа Спасителя из стандартных панелей от 18-этажек (OpenGL) или из обычных красных кирпичей (DX). Т.е.

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glVertexPointer(...);
glNormalPointer(...);

glDrawElements(...);

Это на уровне устройства не обязательно тоже самое, что

pDevice->SetStreamSource(...f);
pDevice->SetIndices(...);

pDevice->DrawIndexedPrimitive(...);

У DX в связи с этим есть один неудобный минус - приходится самому заботиться о восстановлении данных, когда устройство переходит в режим lost - т.е. фактически нерабочий режим. Дело в том, что при перехвате управления устройством Direct3D каким-либо приложением, D3D предоставляет ему полный доступ к вообще всем имеющимся ресурсам видеоадаптера, в том числе ко всей видеопамяти. Точнее, это по идее так, более конкретно решает драйвер, но суть не в этом. Суть в том, что если какое-то приложение (например, ваша игра) перехватило управление видеокартой, то сделать с памятью она может что угодно, и в каком угодно месте - это решает драйвер и немножко - программист. При возврате управления системе или другой программе, нет никакой гарантии, что данные в видеопамяти, а также состояние устройства, небыли затерты в течение того времени, когда эти ресурсы были во власти перехватившего их приложения. А значит - надо все это вернуть обратно, загрузить снова или восстановить из системной памяти. Обычно сброс устройства в состояние lost можно наблюдать при выходе из игры клавишами alt+tab. При возврате обратно она имеет тенденцию как-то долго возвращаться к тому моменту, где вы ее покинули. Это связано как раз с восстановлением ресурсов. В связи с этой особенностью программисты, использующие D3D вынуждены писать руками особые подсистемы для движка - менеджеры ресурсов, которые берут на себя управление вершинными/индексными буферами, состоянием системы, поверхностями рендеринга и т.п. Но результат того стоит.

Еще одна причина, почему все любят DX - это его первоклассная документированность, справочные данные, и общая забота MS о том, чтобы его хотели использовать. Проявляется она также в очень плотной работе с производителями видеокарт. На практике это выглядит так. Когда-то давно все бенчмарки в СДК от производителей видеокарт создавались под OpenGL, просто потому, что он, в отличие от DX, открытый, и свое расширения создать к нему - не проблема. А ДХ был слаб, не перехватил еще инициативу в развитии и единственный вариант был, чтобы показать миру свои крутые наработки - это создать OGL-расширение. Но есть одна проблема. Это вот нам сейчас хорошо - основных производителей видеокарт всего две штуки. И каждый предусматривает СВОИ расширения, которые по-разному называются и разных параметров в функции требуют. А пока дождешься, когда ARB там родит новый стандарт - это поседеть можно. Вот где-то в этот момент MS просекла баг в такой организации производства графических апи (напомню, захоти кто-нибудь в той ситуации поддержать своим движком все новомодные штуки от обоих производителей видеокарт - пришлось бы писать фактически два куска когда, одинаковых по назначению, и нужных только для реализации одной и той же функции для разных видеокарт), решилась на коварный шаг. MicroSoft (кстати, она вышла из ARB еще в 2003) стала заглядывать к производителям видеокарт, консультироваться с ними о новых технологиях и о том, как бы они хотели, чтобы выглядел интерфейс управления их видеокартами, и тщательно и со рвением реализовывали пожелания. Причем настолько тщательно и с таким рвением, что зачастую новая версия DX с новыми фишками появлялись раньше, чем видеокарты нового поколения на полках магазинов, которые поддерживали бы эти новые штуки. По понятным причинам, используя DX, народ совершенно лишается геморроя с написанием дубликатов одних и тех же функций, может не ждать, когда наконец ARB что-нибудь родит, а могут действовать.

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

DirectX базируется на основе COM-технологии, которая фактически убирает ситуацию DLL-Hell, обеспечивает полную независимость новых интерфейсов от старых (не говоря о реализации) и при этом обеспечивает обратную совместимость за счет включения старых версий библиотек, основанных на той же технологии и могущих быть использованными при запросе, в новый дистрибутив DirectX Redist. Это позволяет идти в ногу со временем и в новых версиях интерфейс разработать так, чтобы его было максимально удобно использовать как прикладным программистам, пишущим с использованием DX свои программы, так и системным, пишущим драйвера видеокарт.

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

Правда разработчикам кроссплатформенного ПО приходится мириться с тем, что есть - OpenGL, ARB, Khronos Group.
__________________
Служу Советскому Союзу!

Хорошо смеется тот, кто стреляет первым! (танкистская мудрость)
[CCCP] Monster вне форума  
Отправить сообщение для [CCCP] Monster с помощью Skype™ Ответить с цитированием
Ответ

Метки
делимся опытом

Опции темы

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход


Часовой пояс GMT +4, время: 01:19.


Powered by vBulletin® Version 3.8.0
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Rambler's Top100 Яндекс цитирования