Simple Viewer GL

Simple Viewer GL Некоторые пользователи моего вьювера жаловались на медленное проигрывание GIF-анимации во вьювере. И чем больше по размеру GIF-файл, тем медленнее происходит загрузка следующего фрейма. И проблема была явно не в медленном декодировании фрейма и его загрузке в GL-текстуру. Все оказалось проще. Каждый раз, когда вьювер запрашивал следующий фрейм, он заново инициализировал giflib, скармливал ему заново путь к файлу, выбирал нужный фрейм и только потом загружал его GL-текстуру.

Фикс получился прост как валенок. Хотя нет, из шерсти делать войлок я не умею, а вот исправить способ работы с GIF-анимацией удалось за пару минут. Теперь и большие анимации проигрываются без тормозов.

А еще я добавил экспериментальную поддержку JPEG 2000, основанную на библиотеке OpenJPEG. А так же написал загрузчик Apple Icon Image. Заодно сделал небольшой рефакторинг PNG, дабы унифицировать код загрузки, т.к. он используется и для загрузки PNG, и для ICO, а теперь и для ICNS.

Подробнее о Simpel Viewer GL.

История поиска одного бага

Debugging Довольно много времени потратил на поиск “плавающего” бага. За это время успел отрефакторить кучу кода, до которого руки не доходили ранее. Но баг не ловился. И в его поимке не смогли помочь ни Xcode Instruments, ни Xcode Analyzer (фронтэнд к статическому анализатору llvm).

Баг проявлялся редко и далеко не на всех девайсах. Я никак не мог понять условия его проявления. Пробовал записывать направление и температуру ветра, положение звезд на небе и длительность соседского ора. Но это никак не помогало в поиске проблемы.

Падения игры под отладчиком и backtrace давали BAD EXC, что как бы намекало на испорченную где-то ранее память. Но где именно? Статический анализатор, по идее, должен на раз находить access out of bounds. Но анализатор молчал, типа вообще все пучком.

От безысходности и обиды на самого себя, решил синхронизировать линуксовую версию с main branch, т.к. в процессе поиска бага был сделан серьезный рефакторинг кода. Ну и очевидно, что погонять движок под valgrind никогда лишним не будет.
И valgrind сразу же нашел memory corruption (тот самый “плавающий” баг, на поиски которого я убил много времени. И мелкий ничего не значащий memory leak.

Linux и valgrind рулят!

Emscripten: события клавиатуры в iframe

Столкнулся с проблемой на itch.io, когда приложение запущенной внутри iframe не получает события клавиатуры. Придумал вот такой workaround:

postRun: (function() {
                window.addEventListener('mousedown', function(evt) {
                    window.focus();
                    // evt.preventDefault();
                    evt.stopPropagation();
                    evt.target.style.cursor = 'default';
                }, false);
            })(),

К событию postRun добавляем установку листенера на событие mousedown. Теперь при клике мышью на канавасе с игрой события от клавиатуры будут передаваться в наше приложение.

Texture Packer

Texture Packer для Linux и macOS – утилита, которая упаковывает набор входных изображений в один большой атлас. Утилита консольная, что удобно для автоматизации.

Из возможностей:

  • Достаточно шустрая. Сравнивал с “обычным платным” (с) пакером.
  • Умеет создавать Power of Two атлас.
  • Можно ограничить максимальный размер атласа.
  • Умеет отрезать “лишние” пиксели (trim) у входных изображений.
  • Может добавить бордюр нужного размера вокруг изображений при размещении в атласе.

Утилита делалась для себя, а теперь доступна на Bitbucket – Texture Packer.

C++11 countof

Наверняка многие используют макрос, подобный нижеприведенному:

#define countof(array) (sizeof(array)/sizeof(array[0]))

Он хорош до тех пор, пока в качестве array в него не будет передан указатель на array. Компилятор проглотит и ничего не скажет, а результат будет совсем не тот, которого ожидает разработчик.

Благодаря constexpr появившемуся в C++11 есть возможность сделать безопасный countof.

template <class T, size_t N>
constexpr size_t countof(const T (&)[N]) noexcept
{
    return N;
}

Теперь на этапе компиляции произойдет ошибка:

const int values[] = { 42, 76, 16, 11, 31 };

void foo(const int v[])
{
   for (size_t i = 0; i < countof(v); ++i) // <-- compilation error!
   {
      bar(v[i]);
   }
}

Версия для C++98 может быть такой:

template <typename T, size_t N>
char (&CountOfHelper(T(&)[N]))[N];
#define countof(x) sizeof(CountOfHelper(x))

Emscripten + OS X El Capitan

Emscripten Emscripten is an LLVM-based project that compiles C and C++ into highly-optimizable JavaScript in asm.js format. This lets you run C and C++ on the web at near-native speed, without plugins.

Решил еще раз установить Emscripten с помощью brew, на сей раз все оказалось гораздо проще. Возможно это работало и раньше, но я только сегодня обратил на это внимание.

Правильный способ установки Emscripten с помощью brew:

# устанавливаем emscripten как обычно:
$ brew install emscripten

# запускаем emcc, что бы он создал файл ~/.emscripten
$ emcc

# исправляем LLVM_ROOT:
$ vim ~/.emscripten

Должно получиться как-то так:

LLVM_ROOT = '/usr/local/opt/emscripten/libexec/llvm/bin'

К сожалению с помощью brew правильно установить emscripten под OS X El Capitan мне не удалось.
Я не смог найти какую-либо информацию по правильной установке emscripten с помощью brew, поэтому получилась вот такая магическая инструкция.

Список шагов для установки emscripten из-под OS X:

  • С официального сайта качаем портабельную версию emscripten – http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html#sdk-download-and-install
  • Распаковываем и запускаем ./emsdk update.
  • С помощью команды ./emsdk list можно посмотреть список доступных пакетов, утилит и sdk.
  • С помощью комады ./emsdk install latest устанавливаем самые последние версии.
  • Командой ./emsdk activate активируем переменные окружения emscripten.
  • Теперь при необходимости сборки проекта инициализируем переменные окружения с помощью команды source ./emsdk_env.sh.
  • По желанию можно прописать путь к директории с emsdk.

Gradle, Lint: [MissingTranslation]

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

Если нужно полностью подавить сообщение об ошибке, то в build.gradle достаточно добавить такие параметры:

lintOptions
{
   disable 'MissingTranslation'
}

Если нужно подавить сообщение об ошибке только для определенных файлов, то добавляются такие атрибуты в каждый файл:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">

CPU time

#include <sys/resource.h>
#include <sys/time.h>

unsigned GetSystemTime()
{
    rusage ru;
    if(::getrusage(RUSAGE_SELF, &ru) != -1)
    {
        return (unsigned)(ru.ru_utime.tv_sec * 1000000 + ru.ru_utime.tv_usec);
    }

    // fallback
    timeval time;
    ::gettimeofday(&time, 0);

    return (unsigned)(time.tv_sec * 1000000 + time.tv_usec);
}
1 2 3 4 11