Чому це умовне в моєму фрагменті шейдер настільки повільне?


19

Я встановив деякий FPS-вимірювальний код у WebGL (на основі цієї відповіді ТА ) і виявив деякі диваки з виконанням мого фрагмента шейдера. Код просто видає один квадратик (а точніше два трикутники) на полотні розміром 1024x1024, тому вся магія відбувається в шейдері фрагмента.

Розглянемо цей простий шейдер (GLSL; вершина шейдера - лише прохідний):

// some definitions

void main() {
    float seed = uSeed;
    float x = vPos.x;
    float y = vPos.y;

    float value = 1.0;

    // Nothing to see here...

    gl_FragColor = vec4(value, value, value, 1.0);
}

Тож це просто робить біле полотно. На моїй машині він складає в середньому близько 30 кадрів в секунду.

Тепер давайте збільшимо число хрускоту і обчислимо кожен фрагмент на основі декількох октав шуму, що залежить від положення:

void main() {
    float seed = uSeed;
    float x = vPos.x;
    float y = vPos.y;

    float value = 1.0;

      float noise;
      for ( int j=0; j<10; ++j)
      {
        noise = 0.0;
        for ( int i=4; i>0; i-- )
        {
            float oct = pow(2.0,float(i));
            noise += snoise(vec2(mod(seed,13.0)+x*oct,mod(seed*seed,11.0)+y*oct))/oct*4.0;
        }
      }

      value = noise/2.0+0.5;

    gl_FragColor = vec4(value, value, value, 1.0);
}

Якщо ви хочете запустити вищевказаний код, я використовував цю реалізаціюsnoise .

Це зводить fps до чогось типу 7. Це має сенс.

Тепер дивна частина ... давайте обчислимо лише один з кожні 16 фрагментів як шум, а інші залишимо білими, обернувши обчислення шуму в такі умови:

if (int(mod(x*512.0,4.0)) == 0 && int(mod(y*512.0,4.0)) == 0)) {
    // same noise computation
}

Ви б очікували, що це буде набагато швидше, але це все ще лише 7 кадрів в секунду.

Для ще одного тесту давайте замість цього фільтрувати пікселі за наступними умовними:

if (x > 0.5 && y > 0.5) {
    // same noise computation
}

Це дає точно таку ж кількість шумових пікселів, як і раніше, але зараз ми набрали майже 30 кадрів в секунду.

Що тут відбувається? Чи не повинні два способи відфільтрувати 16-ту пікселів дають стільки ж циклів? І чому повільніша така ж повільна, як і всі пікселі, як шум?

Питання про бонус: Що я можу зробити з цього приводу? Чи є спосіб , щоб працювати навколо жахливої продуктивності , якщо я на самому ділі дійсно хочу спекл мого полотна з допомогою всього кілька дорогих фрагментів?

(Просто для впевненості, я підтвердив, що фактичні обчислення модуля взагалі не впливають на частоту кадрів, рендерінг кожного 16-го пікселя замість білого.)

Відповіді:


22

Пікселі об'єднуються в невеликі квадрати (наскільки велика залежить від обладнання) і обчислюються разом в одному конвеєрі SIMD . (структура масивів типу SIMD)

Цей конвеєр (який має кілька різних назв залежно від постачальника: warps, wavefronts) буде виконувати операції для кожного пікселя / фрагмента в lockstep. Це означає, що якщо для 1 пікселя потрібно обчислення, то всі пікселі будуть обчислені, а ті, яким не потрібен результат, викинуть його.

Якщо всі фрагменти йдуть однаковим шляхом через шейдер, то інші гілки не будуть виконані.

Це означає, що ваш перший метод обчислення кожного 16-го пікселя буде найгіршим розгалуженням.

Якщо ви хочете все-таки зменшити розмір свого зображення, то просто візуалізуйте меншу текстуру, а потім покрасьте його.


5
Надання меншої текстури та покращення розміру - це хороший спосіб зробити це. Але якщо з якихось причин вам дійсно потрібно записати на кожен 16-й піксель великої текстури, використовуючи обчислювальний шейдер з одним викликом для кожного 16-го пікселя плюс завантаження / зберігання зображення, щоб розпорошити запис у ціль візуалізації, може бути хорошим варіантом.
Натан Рід
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.