ПрограммированиеФорумГрафика

Магический глюк в шейдере (HLSL)

#0
14:57, 1 ноя 2011

Всем привет!
При реализации размытия столкнулся с эффектом, который я локализовал, но понять природу его возникновения не получается.
Суть такова:
Размываем круг, совпадающий с бОльшим кругом на данном скриншоте
Изображение

// Весовой массив
const float weights[25] =
{
  1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 
  1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 
  1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 
  1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
  1.0f, 1.0f, 1.0f, 1.0f, 1.0f
};

float4 PS_VertBlur2(VS_OUT In): COLOR
{
  float4 col = 0.0f;
  float texStepY = BlurScale * InvTexSize.y;  
  for (int i = 0; i < 25; i++)
    col.rgb += tex2D(SceneTex, float2(In.Tex.x, In.Tex.y + texStepY * (i - 12))).rgb * weights[i];        
  col.rgb /= 25;
  col.a = 1.0f;
  return col;
}

Весовой массив weights здесь заполнен единицами, чтобы лучше показать абсурдность ситуации.
BlurScale и InvTexSize задается из приложения и в ходе эксперимента не меняется. Точнее даже так - вообще ничего в окружении не меняется.
Компилирую его, как ps_3_0.
С данным шейдером получаем следующую картинку:
Изображение
Как видим, круг почему-то съехал вверх.

Если закомментировать

 * weights[i]

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

Напомню, что веса в массиве все единичные, то есть само по себе умножение на

weights[i]

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

У кого-нибудь есть идеи, что это за Voodoo Magic, и как с ней бороться?

#1
15:30, 1 ноя 2011

Cannibal
Ты грузишь шейдера напрямую? Или успользуешь какой-нибудь FXShader. Посмотри на асм после компиляции (можно быстро посмотреть в PIX'e).
Или сделай быстрый тест - перенеси объявление весов внутрь функции и убери слово const.

#2
15:51, 1 ноя 2011

VirT
> Ты грузишь шейдера напрямую?
D3DXCreateEffectFromFile.
> Или сделай быстрый тест - перенеси объявление весов внутрь функции и убери слово const.
Не помогло.

PIX посмотрю.

#3
18:13, 1 ноя 2011

Посмотрел рендер черех PIX.
Получившиеся после компиляции asm тут: [file=72619] и [file=72620]

Я в них понял только:
1) если совсем не использовать массив, то он и не создается
2) в версии с массивом (которая Wrong) он почему-то развернул цикл.

Возникли вопросы по прешейдеру с массивом:

     preshader
    mul r0.x, c0.y, c1.x
    mul c0.x, r0.x, (12)
    mul c1.x, r0.x, (11)
    mul c2.x, r0.x, (10)
    mul c3.x, r0.x, (9)
    mul c4.x, r0.x, (8)
    mul c5.x, r0.x, (7)
    mul c6.x, r0.x, (6)
    mul c7.x, r0.x, (5)
    mul c8.x, r0.x, (4)
    mul c9.x, r0.x, (3)
    mul c10.x, r0.x, (2)
    add c12.x, r0.x, r0.x
    mul c13.x, r0.x, (-3)
    mul c14.x, r0.x, (4)
    mul c15.x, r0.x, (5)
    mul c16.x, r0.x, (6)
    mul c17.x, r0.x, (7)
    mul c18.x, r0.x, (8)
    mul c19.x, r0.x, (9)
    mul c20.x, r0.x, (10)
    mul c21.x, r0.x, (11)
    mul c22.x, r0.x, (12)
    mov c11.x, r0.x

Что это за числа в скобочках?
Если это индексы заполняемых элементов массива, то почему у них такой странный порядок? А (-3) совсем выбивается из колеи...

#4
19:03, 1 ноя 2011

Какая версия SDK, последняя?
Попробуй отключить прешейдеры.

#5
22:21, 1 ноя 2011

Попробуй для начала static const float weights.

#6
22:23, 1 ноя 2011

Проблема решена заменой строки

col.rgb += tex2D(SceneTex, float2(In.Tex.x, In.Tex.y + texStepY * (i - 12))).rgb * weights[i];    

на код вида

col.rgb += tex2D(SceneTex, In.Tex + texStepY * offsets[i]).rgb * weights[i];    

где offsets это:

const float2 offsets[25] =
{
  {0, -12},
  {0, -11},
  {0, -10},
...и так далее ....
  {0, 11},
  {0, 12}
};

Теперь все, вроде, работает правильно. Но я не понимаю, почему был глюк и почему он исчез после такой замены :(
Магия - не иначе.

arabesc
> Какая версия SDK, последняя?
Да.
>Попробуй отключить прешейдеры.
Как? Хоть проблема и решена, интересно узнать побольше.

#7
22:24, 1 ноя 2011

Wraith
> Попробуй для начала static const float weights.
Пробовал. Не помогло.

#8
23:04, 1 ноя 2011

> Как? Хоть проблема и решена, интересно узнать побольше.

HRESULT D3DXCreateEffectFromFile(
  __in   LPDIRECT3DDEVICE9 pDevice,
  __in   LPCTSTR pSrcFile,
  __in   const D3DXMACRO *pDefines,
  __in   LPD3DXINCLUDE pInclude,
  __in   DWORD Flags, // <-- D3DXSHADER_NO_PRESHADER
  __in   LPD3DXEFFECTPOOL pPool,
  __out  LPD3DXEFFECT *ppEffect,
  __out  LPD3DXBUFFER *ppCompilationErrors
);
#9
23:41, 1 ноя 2011

Ну тут действительно есть подлянка.
Вот тестовый шойдыр, очень похожий: http://www.everfall.com/paste/id.php?th2z5zremsbb
А вот результат: http://www.everfall.com/paste/id.php?uofcfr7g3jfx

А разгадка, я чую, вот в чем: компилятор облажался по вине программиста.
Дело все в том, что в ps_3_0 нету адресного регистра: http://msdn.microsoft.com/en-us/library/windows/desktop/bb172920%… VS.85%29.aspx
Поэтому ему просто нечем индексировать массив весов. Для коротких фильтров компилятор иногда умеет использовать dot product trick.
Если скомпилить с /Gfp (prefer flow control), он выдает прекрасное полотно из 50 25 cmp и 1 texld внутри цикла: http://www.everfall.com/paste/id.php?1bt059cp35lr
Вот и.

Upd: поставил [unroll] перед циклом, все равно неправильно.

#10
23:46, 1 ноя 2011

Wraith
> А разгадка, я чую, вот в чем: компилятор облажался по вине программиста.
> Дело все в том, что в ps_3_0 нету адресного регистра:
> http://msdn.microsoft.com/en-us/library/windows/desktop/bb172920%…
> VS.85%29.aspx
> Поэтому ему просто нечем индексировать массив весов. Для коротких фильтров
> компилятор иногда умеет использовать dot product trick.
> Если скомпилить с /Gfp (prefer flow control), он выдает прекрасное полотно из
> 50 cmp и 1 texld внутри цикла

странно, обычно делается unroll с жесткой привязкой к индексу констант
ой, там ещё и rep ... жесть просто

пардон, конечно, нужно массив float2, а не массив float :)

#11
0:04, 2 ноя 2011

Так, это я пофейлился.
Там надо c += tex2D(..., а у меня c = tex2D( ...
Поправив ошибку, получаются интересные вещи: http://www.everfall.com/paste/id.php?wnsed0bzvrcs  - шейдер.
Результат с /Gfp: http://www.everfall.com/paste/id.php?2plgi5oyau1h
Результат с /Gfp, если сделать static const float: http://www.everfall.com/paste/id.php?r1d3cqzuzj50  - намного приятнее.
Add:
Результат с /Gfa, если сделать static const float: http://www.everfall.com/paste/id.php?1nbjnl95tyq3

#12
12:06, 2 ноя 2011

Попробовал скомпилировать глючный вариант шейдера в офлайне. Использовал и /Gfp и /Gfa. В первом случае был получен варнинг:
Изображение
В обоих случаях скомпилированный шейдер работает неверно.

Получается, что нужно обойти проблему введением массива сдвигов (offsets) и забыть о

float2(In.Tex.x, In.Tex.y + texStepY * (i - 12))

как о страшном сне.

Блин, но так ведь в другой ситуации всплывет что-нибудь подобное :(

UPD. Варнинг оказался про другое.

ПрограммированиеФорумГрафика

Тема в архиве.