Сегодня был мой последний день в Ubisoft Kiev.
:(
пятница, 10 августа 2012 г.
понедельник, 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
Как быстрое решение, можно выводить текст, используя старый добрый 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 г.
среда, 1 августа 2012 г.
понедельник, 9 июля 2012 г.
суббота, 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
В 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 г.
Подписаться на:
Сообщения (Atom)
