пятница, 31 декабря 2010 г.

Облако тэгов

В процессе блуждания по интернету набрел на http://www.wordle.net/ - сервис, который позволяет генерировать облако тегов в соответствии с заданными данными.
Пришла в голову мысль, что с помощью облака тегов гораздо проще рассказывать о чем-либо: о частоте, продолжительности, силе.

Для примера составил облако своего профессионального опыта=)



воскресенье, 19 декабря 2010 г.

Подсветка синтаксиса материалов в Ogre3D

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

Я решил попрактиковаться и сделал подсветку для файлов материалов Ogre3D. Итоговый результат получился размером на 530 строк зато может подсвечивать некоторые ошибки!



Итоговый результат доступен здесь.

понедельник, 13 декабря 2010 г.

Autocomplete


На днях принял решение значительным образом сократить использование autocomplete. Вызвано это было следующими причинами:

  1. обнаружил, что после дня использования autocomplete-а серьезно сокращается скорость ввода английского текста и руки некоторое время приходится приучать заново. :)
  2. периодически "умный" механизм подсказки ошибается и подставляет на место уже практически набранного слова полный бред; ужасно бесит. Позвольте пользователям самим совершать ошибки!
  3. автоматически всплывающая подсказка переводит (да, это так) внимание с текущей мысли на выбор нужного варианта.
  4. код, на мой взгляд, запоминается куда как хуже, когда работаешь не своими руками, а пользуешься виртуальным помощником.
В разумных количествах autocomplete - несомненное добро, но то, к чему приводит VS2010 + VAssist с максимальным уровнем подсказок - точно перебор.

воскресенье, 12 декабря 2010 г.

Наследование в C!

Как реализовать наследование в C? Скажете, там и классов-то нет!

Разбираюсь с GTK+ - кроссплатформенным набором инструментов для создания GUI. Там достаточно часто встречается код подобного вида

GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_signal_connect( GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(delete_event), NULL );

где GTK_OBJECT - фактически макрос приведения указателя на структуру GtkWidget к указателю на структуру GtkObject.

Так вот. Идея заключается в том, что если GtkWidget имеет вид

struct GtkWidget
{
  GtkObject innerObject;
};

то такое приведение действительно будет работать в C, а это значит, что одиночное наследование реализуется достаточно просто.

P.S. Следует учитывать, что в самом GTK макрос кроме непосредственно приведения указателей выполняет еще и проверку типов, так что будьте внимательны!

пятница, 10 декабря 2010 г.

Paged Geometry или почему я не люблю STL streams, а также как их правильно готовить.

Небольшое вступление, не совсем в тему, зато весьма поучительное и отражающее основную суть вопроса.

File copy circa 1975:

#include <stdio.h>
int main() {
int c;
while ((c = getchar()) != EOF)
    putchar(c);
    return errno != 0;
}

Fast forward 20 years, and...

#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>
using namespace std;
int main() {
    // (forgot the try/catch around main)
    copy(istream_iterator<string>(cin),
    istream_iterator<string>(),
    ostream_iterator<string>(cout,"\n"));
}
Something, somewhere, went terribly wrong.

Andrei Alexandrescu.

Я помню, как еще шесть с половиной лет назад, начиная изучать C++ я читал о потоках в STL и думал "Как это круто! Они так неожиданно и здорово перегрузили оператор <<". Однако время идет, а ореола очарования постепенно сошел на нет.

Первая проблема. They are too verbose! (В них чересчур много буков).
Пример из реально работающего кода.

        std::cout << std::setw(2) << std::setfill('0') << (pTime->tm_mon + 1)
            << std::setw(2) << std::setfill('0') << pTime->tm_mday
            << std::setw(2) << std::setfill('0') << (pTime->tm_year + 1900)
            << "_" << std::setw(2) << std::setfill('0') << pTime->tm_hour
            << std::setw(2) << std::setfill('0') << pTime->tm_min
            << std::setw(2) << std::setfill('0') << pTime->tm_sec
            << std::setw(3) << std::setfill('0') << (mTimer->getMilliseconds() % 1000);

В C-стиле это будет выглядеть как
printf( "%02d%02d%02d_%02d%02d%02d%03d", 
        (pTime->tm_mon + 1),
        pTime->tm_mday,
        (pTime->tm_year + 1900),
        pTime->tm_hour,
        pTime->tm_min,
        pTime->tm_sec,
        (mTimer->getMilliseconds() % 1000 
);

Как по мне, так второй код читается лучше, а пишется короче. Так почему же "something has gone wrong?"

Вторая причина. Работаем мы с open-source движком Ogre3D, таким большим, толстым с очень длинными именами классов навроде Ogre::HardwareVertexBufferSharedPtr и достаточно интенсивным использованием строк.


Делаем в проекте рендеринг огромных массивов растительности (бескрайний лес:)), используя PagedGeometry - плагин к огру; как раз подошла необходимость заняться оптимизацией. Проблема заключается в том, что у нас единовременно подгружаются достаточно большие участки леса и при этом наблюдается заметный лаг.

Включаю профайлер и что же я вижу? (Обратите внимание на Forests::BatchedGeometry::SubBatch::build/)

Функция которая должна вызываться не чаще, чем раз в 10 кадров пятая по времени среди "горячих точек"!

Копаем дальше! Это результаты еще одной сессии профилирования, где BatchedGeometry::build заняло в целом около 1.4 с. времени.


Ничего себе. В коде генерации деревьев больше половины времени занимают операции со строками!

Как только код c
String BatchedGeometry::getFormatString(SubEntity *ent)
{
 StringUtil::StrStreamType str;
 
 str << ent->getMaterialName() << "|";
 str << ent->getSubMesh()->indexData->indexBuffer->getType() << "|";
 
 const VertexDeclaration::VertexElementList &elemList = ent->getSubMesh()->vertexData->vertexDeclaration->getElements();
 VertexDeclaration::VertexElementList::const_iterator i;
 for (i = elemList.begin(); i != elemList.end(); ++i)
 {
  const VertexElement &element = *i;
  str << element.getSource() << "|";
  str << element.getSemantic() << "|";
  str << element.getType() << "|";
 }
 
 return str.str();
}

был заменен на

String BatchedGeometry::getFormatString(SubEntity *ent)
{
   const int BufSize = 1024;
   char buf[BufSize];


   int countWritten = 
         _snprintf_s( buf, BufSize,
               "%s|%d",
                ent->getMaterialName().c_str(), 
                ent->getSubMesh()->indexData->indexBuffer->getType() 
         );

   const VertexDeclaration::VertexElementList &elemList = ent->getSubMesh()->vertexData->vertexDeclaration->getElements();
   VertexDeclaration::VertexElementList::const_iterator i;
   for (i = elemList.begin(); i != elemList.end(); ++i)
   {
      const VertexElement &element = *i;
      countWritten += _snprintf_s( buf + countWritten, BufSize - countWritten, BufSize - countWritten,
                         "|%d|%d|%d",
                          element.getSource(),
                          element.getSemantic(),
                          element.getType()
                        );
   }

   return buf;
}

Время генерации деревьев сократилась в 2-2.2(!!!!!!) раза: вместо средних 4.8-5.6 мс на один вызов стало 2.2-2.8. Не знаю, ругаться на разработчиков PagedGeometry или на реализацию стримов у Майкрософта?

Такая низкая скорость работы объясняется тем, что

  1. сами потоки - объекты, которые любят выделять память, в частности на создании и удалении;
  2. реализация STL от майкрософта по умолчанию вставляет локи на работу с потоками, если проект собирается, как multithreaded.


Итак, они медленные.

Так что я буду по-прежнему использовать printf или класс StringFormatter и пусть меня убеждают  и дальше о пользе некоторых абстракций STL. :)

P.S. Между прочим у Александреску есть отличная статья на тему типо-безопасного printf-а!


среда, 8 декабря 2010 г.

Dynamic vs. pregenerated content

Вот что странно. За всю свою историю путешествий по интернету я НИ РАЗУ не встречал рассказа о том, как организована система сборки в том или ином игровом проекте (мультимедиа проекте; проекте, связанном с 3D графикой - нужное подчеркнуть).
А интересных тем там будет немало:
  • как храним код и файлы ресурсов?
  • какую make систему использовать?
  • как копируем файлы?
  • как делаем препроцессинг ресурсов? много утилит или одна со множеством плагинов?
  • должны ли все утилиты быть отправлены в систему контроля версий или каждый разработчик самостоятельно ставит себе, все, что надо?
  • как, в конце концов, интегрируем эту систему с нашим любимым супер навороченным редактором/IDE?
Все эти вопросы начинают занимать голову, когда билд приходит в неуправляемое состояние.
В следующем посте я постараюсь рассказать о том, как это все сделано у нас, а пока что...

Я до сих пор очень люблю pre-generated content(PreGC), то есть все данные, которые генерируется до запуска приложения. Это, по сути дела, чем-то похоже на конфиг файлы, куда можно все записать заранее, проверить(что очень важно!) отдельно, а потом использовать. В результате получаем отдельный модуль, который не нужно доводить до 100% оптимального состояния: алгоритмы в офлайне можно использовать самые простые и быстрые для написания.

У динамического контента(DynGC)  те же недостатки, что и преимущества у заранее сгенерированного: 
  • код приложения усложняется, могут понадобиться различные изощренные оптимизации;
  • модульность куда как сложнее организовать;
  • данные проверить становится уже сложно, потому что не всякий разработчик DynGC озаботится модульностью (к сожалению, это так!).
Так что второй вариант выбирать логичнее в случае либо его излишней простоты, либо когда объем данных становится излишне большим.

Хотя вообще  спор на эту тему напоминает некий холивар, в котором побеждает тот, у кого больше звездочек на погонах. :)

суббота, 4 декабря 2010 г.

Vim. Чем же он так хорош?

Решил изучить Vim, в планах было давно, но все как-то откладывал.

Помню раньше, работая с сервером под Линухой пользовался вимом для редактирования конфигов - и не понимал всей его прелести. Поэтому меня очень удивило, когда в одной презентации от господ Инсомниаков(проекты - раздва; вики - три ),я увидел скриншот кода, в котором было окошко вима. Это резко изменило мое представление о виме, и я поставил зарубку на память.

Итак, с чего же я начал?

Для начала, я нашел несколько вариантов для работы с вимом под виндой.
  1. gvim - официально-родная графическая оболочка для vim, существует как под виндой, так и под линухой.
  2. VimEmu - плагин для студий версий 2002 и выше, в том числе и 2010. Однако, оказалось, что он платный и не лечится, а отдавать порядка 100$ на что-то, что я еще не изучил...=)
  3. VsVim - плагин для студии 2010. Open Source проект от одного из разработчиков Microsoft; начат в декабре прошлого года, так что находится уже в достаточно зрелом состоянии. Именно на нем я и остановил свой выбор.

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

Как же работает сам редактор? У него есть несколько режимов функционирования:
  • режим вставки - работает так же, как и ввод текста в "обычном" текстовом редакторе.
  • командный режим - в нем можно вводить команды: перемещения по тексту и редактирования.
  • визуальный режим - можно выделять текст и выполнять команды редактирования уже над выделенным текстом.
В командном режиме команда действия частенько может сочетаться с командой перемещения. Так, для того, чтобы удалить слово нужно набрать - <Удалить><Переместиться на слово вперед>, а на клавиатуре - это будет dw. Приятнее, чем Ctrl +Shift + Right -> Del, не так ли? :)

Для начала я выучил основные команды для работы с текстом:
  • перемещения (h, j, k, l - вниз, влево, вправо, вверх; w,b - на слово вперед и назад)
  • редактирования текста (d - удалить, p - вставить ) и проч.
И начал пробовать. Первые несколько дней мозг ломало по-страшному. У меня с трудом получалось думать о том, что же именно я пишу, а большую часть времени занимало обдумывание следующего действия, примерно так( "джей-джей-эйч-ди-ви"). До сих иногда начинаю разговаривать с собой, когда использую команду, которая еще не записалась на подкорку.

Через неделю такое чувство прошло, и уже сейчас руки иногда начинают срабатывать на автомате. С удивлением иногда замечаю, как совершенно машинально для удаления текущей линии нажимаю dd.

Скорость редактирования действительно увеличивается, но для этого желательно обладать навыками слепой печати. В других текстовых редакторах руки машинально тянутся к Esc и пытаются вставить новую строку над текущей с помощью Shift+o, замечаю, что некоторых функций в них мне уже не хватает - вводить в виме приходится куда как меньше буков. Признаюсь правда, что иногда все-таки использую мышку. На мой взгляд текст ею прокручивать все-таки удобнее)

Еще больше преимущество вима осознал, когда пришлось повозиться с вот этим фрагментом кода:

// Set up root attribute parsers
mRootAttribParsers.insert(AttribParserList::value_type("material", (ATTRIBUTE_PARSER)parseMaterial));
mRootAttribParsers.insert(AttribParserList::value_type("vertex_program", (ATTRIBUTE_PARSER)parseVertexProgram));
mRootAttribParsers.insert(AttribParserList::value_type("geometry_program", (ATTRIBUTE_PARSER)parseGeometryProgram));
mRootAttribParsers.insert(AttribParserList::value_type("fragment_program", (ATTRIBUTE_PARSER)parseFragmentProgram));

//////////////////////////
// a lot of lines of codes
//////////////////////////
mPassAttribParsers.insert(AttribParserList::value_type("polygon_mode", (ATTRIBUTE_PARSER)parsePolygonMode));
mPassAttribParsers.insert(AttribParserList::value_type("polygon_mode_overrideable", (ATTRIBUTE_PARSER)parsePolygonModeOverrideable));

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

Сейчас очень доволен тем, что решил его таки изучить и сделал его своим текстовым редактором по умолчанию. Все - на вим! Хотя бы попробовать, чтобы понять, как работают программисты в другом мире =)