воскресенье, 31 января 2010 г.

Fast Prefiltered Lines

Наткнулся на эту технику в GPU Gems 2. Суть её в том, что она позволяет рисовать линии с лучшим сглаживанием, чем это позволяет стандартный антиализинг.

Chapter 22. Fast Prefiltered Lines
Fast Antialiasing Using Prefiltered Lines on Graphics Hardware

В основе лежит уравнение расстояния от точки до отрезка (луча). Растеризатор генерирует фрагмент "широкой" линии, рассчитывается расстояние фрагмента до бесконечно тонкого отрезка, затем делается выборка из предрассчитанной текстуры. Что побудило меня остановиться и реализовать этот алгоритм:

1) В оригинальном варианте вершины линии-квада и уравнения для его рёбер предлагается рассчитывать на CPU. Сегодня их можно перенести в geometry shader.
2) В Direct3D 10/11 нет прямой возможности рисовать сглаженные линии произвольной толщины. Единственное, что доступно - это флажок AntialiasedLineEnable структуры D3D1x_RASTERIZER_DESC.

Я думал по-быстрому реализовать алгоритм и двинуться дальше, но не тут-то было. Дело в том, что в GPU Gems не объясняется, почему уравнения рёбер рассчитываются именно таким образом. Например, неясно, откуда выводится 1 + ..., почему в числителе двойка, а в выражении под корнем, которое, как нетрудно догадаться, вычисляет длину линии, стоит минус вместо плюса. Особенно странно выглядит единица, которой нет в уравнениях, приведённых на странице MIT (вторая ссылка).

Я решил более детально разобраться в этих вещах, а пока реализовал простейший вариант, где рассчитывается расстояние до идеальной линии и используется его обратное значение в виде альфы фрагмента. Линии подаются на вход как LINELIST, копируются вершинным шейдером в геометрический, там линия "раздувается" в стрип из двух треугольников, рассчитывается уравнение прямой (в window space) и всё это передаётся в фрагментный шейдер. Ну а там уже рассчитывается расстояние до сгенерированного фрагмента. Смысл выборки из текстуры в том, что расстояние (и альфа) изменяются линейно, а с выборкой можно заставить их лечь на некую плавную кривую, сделать границы резче или размытее. Подготовка различных фильтров - это отдельная тема.

Скриншоты:


Верхний треугольник: антиалиазинг средствами Direct3D (AntialiasedLineEnable = true + blending).
Первый ряд: линии с шириной 1.3, 1.6, 2.0.
Второй ряд: линии с шириной 4.0, 6.0, 10.0.

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

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

English

Подумываю, не начать ли мне писать "технические" посты на английском? Надо совершенствоваться, да и блог будет более популярным...

пятница, 22 января 2010 г.

Calikusu

Когда-то в детстве я смотрел прекрасный 7-ми серийный турецкий фильм "Королёк - птичка певчая". Так вот, это он и есть - Calikusu (читается как Чалыкушу), снятый в 1980 году. Почему-то вспомнил я о нём, и решил найти, где в Сети его можно пересмотреть. Например, здесь:


Для души :)

"...Возьми чурек, а варенье называется "Гюльбешекер".
Кямран механически откусил от чурека.
- Нравится?
- Нравится.
- Будешь его любить?
- Уже люблю.
- Ну-ка, скажи ещё разок!
- Я люблю, очень люблю.
- Скажи, Кямран: "Я люблю Гюльбешекер".
- Я очень люблю Гюльбешекер.
- Я очень, очень, очень люблю Гюльбешекер!
- Я очень, очень, очень люблю Гюльбешекер! Ты и представить не можешь, как я люблю Гюльбешекер.

GPU memory test

Дописал демку с блендингом плоскостей, которая использует различные методы: блендинг через ROP и используя atomic операции над различными UAV. Про результаты я уже писал, поэтому привожу только ссылку:


Интересно, конечно, было бы потестировать это на GF100. Если у кого-то руки дойдут до этого, дайте знать! Также, если хочется посмотреть на артефакты read/write contention, откройте файл "MEDIA/shaders/source/incr.psh" и закомментируйте строчку USE_ATOMICS.

вторник, 19 января 2010 г.

Buzz and Woody Add a Dimension

21 у нас начинается показ Toy Story 2 в 3D. Надо обязательно сходить.


А этим летом, если кто ещё не знает, выйдёт третья (и скорее всего, последняя) часть Истории игрушек.

P.S. Кстати, "Toy Story" был первый 3D-мультфильм, который я посмотрел тогда, ещё в детстве, в 1995 году. Мне было 13. А крутили тогда в основном разные "Утиные истории" и "Трансформеров". Но "Тоy Story" я полюбил больше всего.


OIT/A-buffer demo: improvements

После различных твиков разогнал демку OIT.

Ссылка: oit_dx11.zip
Скриншот:



Как видно, fps удалось поднять c 300 до 450 на той же сцене. Главным образом за счёт того, что:
a) Первый проход, в котором подсчитывается overdraw, заменён на fixed-function блендинг;
б) При сортировке алгоритм bitonic sort заменён на inline реализацию odd-even merge sort.

Также я приложил два .exe, второй использует structured buffer для хранения color и depth, но по каким-то причинам этот способ медленнее (хотя и более удобен для программирования).

P.S. Кстати, в прошлом варианте был баг (он обнаружился в Windows 7 64-bit), из-за чего программа не запускалась. Почему баг "прокатил" в моей 32-bit OS, непонятно.

And for english speaking guys:

BTW, there was a bug in the previous demo (it was detected on Windows 7 64-bit), because of this bug the program simply crashes on start. I don't know why it works on my 32-bit OS. The link to the new, unbuggy demo, is provided.

Update

Иду по улице, двигаю байтики в голове. Думаю над down-sweep: вот мы запускаем в массив нолик и он начинает путешествовать со сдвигом... Вдруг - бац! Там же ноль в начало буфера попадает, у меня же exclusive prefix sum! А значит, из пиксельного шейдера можно выкинуть movc и ещё пару инструкций, которые по недосмотру остались от демки из SDK!

Пришлось подправить шейдер и перезалить архив, а то глупо получилось....

Кстати, наткнулся на таблицу различий между Compute Shader 4.0 и 5.0:

http://www.legitreviews.com/article/1001/2/

Большая часть этой информации содержится в SDK, но в таблице есть одно уместное замечание:
Unordered Access Views Bound to Pixel Shader
Shader Model 4.0: Not supported
Shader Model 5.0: 8
Allows pixel shaders to access compute shader data for interoperability with rendering pipeline
В SDK прямо об этом не говорится. В разделе Using Compute Shader on DX10 написано:
Only one unordered access view can be bound to the shader.
Only RWStructuredBuffers and RWByteAddressBuffers are available as unordered access views.
А в каком именно типе шейдеров доступен UAV - не сказано. Но если зайти в HLSL/Reference/Shader Models/Shader Model 5.0/Objects и прошерстить все объекты, то можно выяснить, что для RWByteAddressBuffer и RWStructuredBuffer сделана пометка Available for compute shader in Direct3D 11 on some Direct3D 10 devices.

Т. к. scattered write абсолютно необходим при аккумуляции фрагментов в пиксельном шейдере, можно сделать вывод, что A-buffer нереализуем в SM 4.0 (4.1). Уверен, это ограничение продиктовано недостатками архитектуры предыдущих чипов ATI.

воскресенье, 17 января 2010 г.

Третья ходка Януковича

Ну вот, приехали! Проффесор и Ко таки празднуют победу над "дерьмократическими" силами.

Держите шапки крепче, товарисчи украинцы! У руля становится кукла олигархов, ЗэКа, злостный рецидивист и просто бугай Бандюкович, а значит, в планах дальнейшего дерибана страны намечаются кардинальные перемены (Межигорье). С чем вас всех и поздравляю!

P.S. За Петровича всё-таки проголосовал ощутимый процент - 7-8%. Среди них и я. Это радует, что ещё остались в народе трезвовыслящие люди, верящие в молодых, перспективных политиков. Их немало. А теперь слова Яценюка для меня будут проходить проверку действием - действительно ли он, как и обещал, не будет объединяться ни с кем из "дерьмократических" сил и двигаться дальше во власть, возглавляя свою собственною партию, или же продастся одной из политических "горилл" в обмен на привилегии и должности?

P.P.S. А за базар Янык ещё ответит!

суббота, 16 января 2010 г.

Канал на Youtube

Завёл сабж, буду время от времени выкладывать туда разное видео. Адрес канала:

пятница, 15 января 2010 г.

Depth/Stencil: Render vs CopyResource

Провёл ещё одно тестирование, которое на этот раз касается stencil-routed K-buffer.

Как известно, в D3D 10.0 нельзя копировать depth/stencil и MSAA ресурсы. В D3D 10.1 это ограничение было снято, но т. к. у меня не было доступа к этому железу, было неясно, насколько копирование быстрее инициализации стенсель-буфера. Я решил это выяснить и провёл несколько простых тестов. Я тестировал в двух разрешениях: 512x512 и 1024x1024, при 8xMSAA:
512x512:

Copy stencil: ~1390 fps
Init stencil: ~1800 fps
1024x1024:

Copy stencil: ~390 fps
Init stencil: ~512 fps
Как видно, копирование примерно на 25% медленнее инициализации. Т. е. несмотря на многократную смену stencil ref и sample mask, это быстрее, чем один вызов ::CopyResource(). Возможно, отставание в скорости происходит из-за того, что кроме stencil копируется и depth буфер, тогда как при инициализации запись происходит только в stencil (DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO). Т. е. в инициализации всего один шаг:
1) Write stencil.
При копировании:
1) Read depth.
2) Read stencil.
3) Write depth.
4) Write stencil.

четверг, 14 января 2010 г.

UAV vs RT

Сегодня решил померить точно, во сколько обходится unordered read/write на HD 5xxx. Для теста взял десять перекрывающих друг друга плоскостей, которые формируют различный overdraw в разных частях буфера кадра:



В первом варианте для подсчёта overdraw использовалась unordered текстура (флаги D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE, формат R32_UINT) и инкремент в пиксельном шейдере, во втором - простая render-target текстура (флаги D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE, формат R8_UNORM) и аддитивный блендинг в буфере кадра. Для записи в UAV нельзя использовать форматы, отличные от R32_UINT и R32_FLOAT (их можно только кастовать через механизм view, например, R32_UINT -> R8G8B8A8_UNORM, при чтении, что я и делал). Если попробовать использовать форматы вроде R_UNORM, R8_UINT или R16_UINT, то DX Debug вываливает в Output сообщение такого вида:
D3D11: ERROR: ID3D11DeviceContext::DrawIndexed: The Unordered Access View (UAV) in slot 0 of the Pixel Shader unit has the Format (R8_UINT). This format does not support being read from a shader as as UAV. This mismatch is invalid if the shader actually uses the view (e.g. it is not skipped due to shader code branching). It was unfortunately not possible to have all hardware implementations support reading this format as a UAV, despite that the format can written to as a UAV. If the shader only needs to perform reads but not writes to this resource, consider using a Shader Resource View instead of a UAV.
Т. к. мне необходим инкремент, приходится использовать R32_UINT, хотя в RT-текстуре для экономии используется R8_UNORM (R8_UINT не blendable).

Я тестировал инкремент в двух разрешениях, это был отдельный render path, без всего остального кода. Только инкремент/блендинг и вывод значения через чтение в пиксельном шейдере из SRV. И вот что получилось:
512x512:

R/W texture: ~1040(1080) fps (0.962(0.925) ms)
RT texture: ~3130 fps (0.319 ms)
1024x1024:

R/W texture: ~300(305) fps (3.33(3.28) ms)
RT texture: ~1220 fps (0.819 ms)
(Для R/W текстуры значение в скобках указано для случая, когда UAV очищается в Compute Shader, а не через ID3D11DeviceContext::ClearUnorderedAccessViewUint()).

Из тестов видно, что старый добрый RT + fixed function blending серьёзно опережает unordered access - в 3-4 раза. В более высоких разрешениях отставание будет только увеличиваться, возможно, составит порядок.

Исходя из этого, можно сделать следующий вывод: на HD 5xxx везде, где только можно, следует избегать использования unordered access, и использовать старые добрые методы обработки данных. Использовать UAV следует только там, где он абсолютно необходим.

P.S. Не думаю, что на Fermi эта ситуация изменится. Неважно, насколько быстр GPU - это проблема памяти.

GPU ShaderAnalyzer

Новая джиза теперь поддерживает Shader Model 5.0:
GPU ShaderAnalyzer

Упс, а у меня на семёрке инсталлятор не работает :(

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

OIT/A-buffer demo

Наконец-то написал демку.

Ссылки:
Rapidshare.de: Ссылка: oit_dx11.zip
Cкриншот:



Пришлось повозиться с некоторыми вещами, а всё из-за невнимательности :)

В дополнение к A-buffer добавил stencil-routed K-buffer, для полноты подхода. Нужно отметить, что с K-буфером сейчас связаны некоторые артефакты, например, семплирование бортиков текстуры и проблемы в местах пересечения геометрии. Это не мой фейл, а скорее драйверов, т. к. демка с зелёным драконом их NV SDK 10 показывает артефакты ещё похлеще. Забавные, в общем, артефакты.

Update

Сделал fork и начал играться с оптимизациями. Сперва заменил color + depth буферы на один structured буфер. Дело в том, что с точки зрения памяти лучше всего читать/писать элементы, расположенные локально, чем, например, обращаться к элементам с большим страйдом (частично из-за этого MRT и тормозит). Но оказалось, что structured буфер медленнее (в этой программе на 7-10%). Пробовал разный размер структуры, т. е. кратный 16, 32 байтам - ничего. Кстати, в SDK сказано, что при создании structured buffer нужно указывать формат UAV как DXGI_FORMAT_R32_UNKNOWN. Но это ошибка, т. к. такого формата нет, а правильный "формат" - DXGI_FORMAT_UNKNOWN.

Задумался, как оптимизировать scan буфера.

Вообще, по идее, на этом этапе должен происходить read/write contention между различными пассами (d = 0, 1, 2...), но не происходит. Я не эксперт в CUDA или архитектуре Cypress-а, поэтому сложно сказать, почему нет вроде бы обязательных артефактов. С одной стороны, это упрощает жизнь - демка работает, ничего не глючит. С другой стороны, могла бы и быстрее, и не хорошо, когда что-то идёт не так как ожидается (даже если ожидания негативные). Есть догадка, что между двумя вызовами ::Dispatch() может происходить stall CPU-GPU, хотя тоже неясно, отчего именно так? За log(N) * 2 вызовов производительность хорошенько просаживается.

С другой стороны, за падение может отвечать unordered read/write access. Даром это штука наверняка не даётся, да и вообще - это первая его открытая реализация в DirectX. Возможно, что "стройная, как Кипарис" архитектура HD 5000 в этом месте всё же кривовата, и нужно ждать следующего поколения карт. Возможно (и вероятно) драйвер все ещё не отлажен как следует. В общем, одни предположения. Надо курить CUDA и пейпры от AMD, и в виду необкатанных драйверов (да ещё от ATI!), я воздержусь пока от окончательных выводов.

И все же мне удалось немного оптимизировать scan буфера. Я сделал небольшой trick - первые пассы (d = 0..2) я заставил выполняться в одном. Тред теперь оперирует не с двумя индексами, а с вектором из 2^3 = 8 элементов, т. е. данные из UAV загружаются в регистры, там происходят операции суммирования/обмена, а затем данные из регистров записываются в UAV. Я пробовал вектор из 8 и 16 элементов, из 8 быстрее. Старый scan вместе с предыдущими операциями (overdraw, copy) давал ~840 fps, улучшенный - ~1100.

Также на форуме разработчегов игр меня спрашивали, можно ли это заставить работать на DX 10.1. Сейчас, ознакомившись со спецификациями, мне кажется, что да, но со своими затыками. Например, к шейдеру можно подключит только один UAV, а нужно минимум три (color + depth + counter), придётся выкручиваться. Не поддерживается RWBuffer, но можно использовать RWStructuredBuffer. Не поддерживаются атомические операции, но я их пока и не использую.

И последнее. Mecha работает быстрее, при том что depth complexity у неё несоизмеримо больше. Там используются атомические операции для сериализации чтения/записи, что наводит на мысль о том, что у AMD другое понимание, как правильно использовать железо. Ну что ж, буду пробовать :)

вторник, 5 января 2010 г.

Compute Shader Application

Сегодня вдруг в голове сложилась картина, что CS + R/W buffers можно использовать для очень многих вещей, помимо A-buffer или DOF/фильтрации:

1) Тесселяция (Fermi?), примеры есть в SDK.
2) Т. н. Blend Shaders.
3) Silhouette generation (в обход GS).
4) LIDR - можно использовать битовые операции для хранения индексов источников света.
5) Deferred Shading вообще, похоже в этой области намечаются большие перемены.

Также несколько слов о противостоянии DirectCompute/CUDA/OpenCL.

Я несколько раз натыкался на статьи, где рассказывается, как применять DirectCompute для GPGPU. Лично я считаю, что Microsoft изначально планировала DirectCompute как узкий инструмент исключительно для графических вычислений (там, где связка VS/PS неээфективна), и использовать его в составе DirectX 11 (Vista/7) для GPGPU по крайней мере странно.
  
OpenCL vs CUDA. Неочевидно, что организации,, занимающиеся GPGPU, нуждаются остро в переносимости своих программ на железо другого производителя. Т. е., скажем, просто покупается кластер NVIDIA Tesla, и под него затачивается CUDA-программа, служащая целям организации. Где тут OpenCL - непонятно. Даже если нужна переносимость, то с развитием GPGPU перспективы OpenCL программ через 5-10 лет весьма туманны. Или вот выйдёт скоро Fermi. Под него - новая CUDA, учитывающая все тонкости железа. Логично, что скорее всего и использоваться будет именно она.

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

Во Львове на Проффесора напал настоящий зомби :)



P.S. Кстати, у Проффесора появилось новое прозвище - Ипполит (из "Иронии судьбы") :)

ЧП

Для Вогстера пришлось оформлять ЧП (уже второй раз, в прошлом году пять месяцев бегал по всем инстанциям, закрывал первое оформление). Сегодня день сдачи годового отчёта, поехал в нашу налоговую. Зашёл - и вышел :) Столько ЧэПэшников, что не видно конца очереди. Долбаный Гондурас, ну что за порядки? Стоять бревном до обеда, потом у них перерыв на час, потом стоять до 4 дня... Надо было мне заранее переслать отчёт, по почте, мне просто не думалось, что будет такой дурдом.

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

суббота, 2 января 2010 г.

A-buffer

Добил проблему ошибок в A-буфере. Всё дело в том, что правильная индексация в алгоритме возможна при размерностях буфера, кратного степени двойки. Например, 515x256, 1024x1024. Если это не так, получается выход за пределы массива (я проверял "на бумажке"). Хотя в современных GPU можно читать/писать за пределами ресурса (returns 0 on reads, writes are no-ops), prefix sum считается неправильно, и часть изображения портится. При размерностях, кратных степени двойки, всё Ok.

Теперь вот задумался. Простой метод решения проблемы - создавать буфер, кратный степени двойки, не эффективно в случае мониторов 16:9 - лишняя память впустую, а A-buffer и так немаленький. Вычисления, вероятно, можно оптимизировать, чтобы правильный prefix sum строился только в видимой части буфера. Но всё равно, интересно, можно ли написать алгоритм для параллельного сканирования буфера произвольных размеров?

Теперь думаю написать с нуля собственное демо по использованию A-буфера, т. к. смотреть на коды SDK уже тошно.

Update

Пока фарширую демку кодом и подумываю, какую модель использовать для прозрачности. Что-то простенькое или поэффектнее?

Оптимизируя по ходу OIT11, я решил убрать копирование RWTexture2D в RWBuffer. Но получилось медленнее, главным образом из-за того, что ClearUnorderedAccessViewUint() RWBuffer-а с Catalyst 9.12 очень медленный. Вместо очистки я решил использовать Compute Shader. Выбираем оптимальный размер блока, подключаем UAV, вызываем Dispatch() и пишем в шейдере 0. Получается быстрее очистки, что говорит о проблеме в драйвере, а не железе. Одна беда - всё равно получается медленнее, чем копирование! Не то, чтобы сильно медленнее, но всё же. По-видимому, тут проблема с паттерном доступа к памяти. При копировании блоками получается последовательное чтение/записть, RWBuffer это любит. А вот при unordered записи из пиксельного шейдера выгоднее, получается, использовать RWTexture2D. А может быть, доступ к RWBuffer не кэшируется, в отличие от RWTexture*, поэтому конструкция ++buffer[pos.xy] при overdraw для RW-текстуры получается быстрее.

Ещё надо посмотреть, как ведёт себя RWTexture1D.