пятница, 10 августа 2012 г.

I leave Ubisoft

Сегодня был мой последний день в Ubisoft Kiev.
:(

понедельник, 6 августа 2012 г.

Direct2D interop

Всё началось с того, что из Direct3D 11 исключили классы наподобии ID3DXFont для работы с текстом. Это очень неудобно для маленьких приложений, когда некогда писать собственный фреймворк для текстурных шрифтов, а вывести простейший текст надо.

Как быстрое решение, можно выводить текст, используя старый добрый software GDI API, указав флаг DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE при создании swap chain-а, но это решение серьёзно роняет fps - чем больше размеры бэк-буфера, тем дольше выполняются методы GetDC/ReleaseDC, т. к. происходит трансфер буфера из видеопамяти.

С выходом DirectX 11 Microsoft представила новые API на замену GDI/GDI+ - это Direct2D и DirectWrite, которые подразумевалось использовать в том числе и для вывода текста. Эти API используют преимущества аппаратного ускорения, но беда в том, что сами они были написаны на базе Direct3D 10.x, и поэтому несовместимы с интерфейсами Direct3D 11. Подробнее об этом можно прочитать здесь: Hacking Direct2D to use directly Direct3D 11 instead of Direct3D 10.1 API.

Решение этой проблемы, представленное там, мне понравилось, но я был не удовлетворён его реализацией - она написана на скорую руку, как proof of concept. Решение построено на оборачивании интерфейса IDXGISurface в промежуточную оболочку, которая подставляется вместо реального интерфейса. Я решил займствовать идею, и создал свою реализацию proxy-интерфейсов, заключив всё в отдельную dll. Чтобы понять, какие вызовы Direct3D делаются из Direct2D, каждый метод логируется и проверяются коды возврата (HRESULT).

Это решение отлично работает в независимом приложении, но при попытке отладить его в PIX происходит крэш (на вызове D3D10CreateStateBlocks). Сложно сказать, из-за чего именно - возможно, на уровне d3d10.dll используются какие-то недокументированные особенности COM-интерфейсов Direct3D 10, которые не учитываются в proxy-интерфейсах. Также грядущий DirectX 11.1 содержит обновление Direct2D 1.1, которое работает с последней версией Direct3D API, делая хак несколько устаревшим. Впрочем, если приложение должно работать на чистой Windows 7 (которая будет жить ещё долго и счастливо), решение может оказаться очень полезным.

Update Aug 21, 2012

Обнаружил, что использование библиотеки приводит к утечке памяти - не все созданные объекты корректно удаляются, поэтому Direct3D debug layer рапортует о "live objects" после удаления устройства. Проанализировал код и дописал ->Release() к некоторым объектам. Также от меня ускользнуло то, что Direct2D пытается запросить интерфейс ID3D10InfoQueue, поэтому добавил обёртку и для него (хотя это не обязательно). В конце упростил функцию D2DXCreateInteroperableSurface() - теперь она принимает всего два параметра, а интерфейсы устройства и контекста получает через методы API. Поправил инкапсуляцию в классе-обёртке устройства Direct3D.

Ссылки на исходный код на Google Code:
d2dx.zip
d2d_interop.zip

пятница, 3 августа 2012 г.

Drive

Один из трэков к фильму "Drive", который мне ну очень понравился.

понедельник, 9 июля 2012 г.

Brave



Идти. Однозначно. В ТРИДЭ.

суббота, 7 июля 2012 г.

Render Target Compression

Некоторое время назад я заинтересовался компрессией текстур на лету. Типично в DXT (BC в D3D 10/11) формат преобразуются только статические текстуры, подготовленные художниками. Однако повсеместно в кишках графического движка семплируются и текстуры, которые являются динамическими render target-ами, и если кол-во выборок в пиксельном шейдере значительно, имеет смысл попробовать после отрисовки сжать такую текстуру (закрыв глаза на деградацию качества), и семплировать в сжатом формате.

В DirectX 10.0 такой возможности ещё не было, но до неё был буквально один шаг! Direct3D 10.0 позволял копировать в памяти только ресуры с одинаковым typeless форматом, например текстуру формата R32G32B32_FLOAT в текстуру формата R32G32B32_UINT. Direct3D 10.1 снял это ограничение и стало возможным копировать ресурсы с разными форматами при условии, что их размеры в памяти совпадают. Подробнее описано в разделе MSDN Format Conversion Using Direct3D 10.1.

Базой для моей работы послужило демо Хумуса GPU Texture Compression. Основной механизм преобразования формата прост: изображение из render target перебрасывается в текстуру формата R32G32_UINT, пиксельный шейдер используется для упаковки данных таким образом, чтобы после копирования в block compressed формат они интерпретировались как блоки 4x4, готовые к декомпрессии. Мне необходимо было сжать одноканальную текстуру формата R8_UNORM, поэтому в качестве целевого целесообразнее всего использовать формат BC4 (появился в DirectX 10.0). Разобравшись с демкой Хумуса, я немного оптимизировал её: например, он вычисляет текстурные координаты для gather выборок в вершинном шейдере, хотя для этого можно использовать опциональные целые смещения. Для ремаппинга в индексы блока 4x4 можно использовать индексируемый массив вместо замысловатого тернарного оператора (кол-во инструкций в шейдере уменьшается). Наконец используется статическая картинка большого размера, когда отличить сжатое изображение от оригинала невозможно - я же использовал динамический рендер в текстуру и последующее её сжатие.


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

Конечно, можно сжимать и цветные изображения, Хумус написал подобное демо. Для моих же экспериментов пока достаточно одного канала.

Ссылка на демо с исходным кодом на Google Code:
renderbc.zip

пятница, 6 июля 2012 г.