воскресенье, 17 сентября 2017 г.

TBO vs SSBO

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

В древние времена вариантов у нас было немного, фактически было всего два подхода - "Vertex Texture Fetch" (VTF) от NVidia и "Render to Vertex Buffer" (R2VB) от ATI. Каждый из подходов имел свои достоинства, недостатки и ограничения. С тех пор прошло 10 лет, на замену устаревшим техникам пришли UBO, TBO и наконец - SSBO. Для больших объемов данных UBO не пригоден, потому я решил сравнить как обстоят дела у TBO и SSBO.

Предыдущие тесты показали что скорость выборки как из TBO так и из SSBO достаточно высокая, чтоб как-то сравнить эти результаты я решил вывести кусок ландшафта по карте высот, размерность 1280х1280 вершин:


Как сгенерировать буфер TBO/SSBO я оставляю за кадром, так как на эту тему существует множество статей, желающие могут найти пример этого кода здесь.

Остановлюсь немного на вершинном шейдере, вот весь его код:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const char vShader[] = SHADER_SOURCE(

    layout(location = 0) in vec3 in_Position;
    layout(location = 1) in vec3 in_Normal;
    layout(location = 2) in vec3 in_TexCoord;

    uniform mat4 Proj;
    uniform mat4 View;

    uniform samplerBuffer tbo_tex;

    layout(std430, binding = 3) readonly buffer hm {
        float heights[];
    };

    subroutine float SSBO_TBO(in int offset);

    subroutine (SSBO_TBO) float ssboValue(in int offset) {
      return heights[offset];
    }

    subroutine (SSBO_TBO) float tboValue(in int offset) {
      return texelFetch(tbo_tex, offset).r;
    }

    subroutine (SSBO_TBO) float zeroValue(in int offset) {
      return 0.0;
    }

    subroutine uniform SSBO_TBO ssbo_tboSelector;

    out vec2 tc;
    void main(){
        tc = in_TexCoord.st;
        vec4 p = vec4(in_Position.xyz,1.0);
        int offs = int(tc.x*1280.0+(tc.y*1280.0*1280.0));

        p.y = ssbo_tboSelector(offs)*3.0-1.5;
        gl_Position = Proj*View*p;
    }

);

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

Данные для TBO у нас передаются как samplerBuffer tbo_tex (строка 10), SSBO объявляется в строках 12-14. В обоих случаях шейдер ничего не знает о размерности буферов, так что их нам придется указать вручную. Я их указал явно в расчете смещения (строка 36).

Для чтения данных я объявил три подпрограммы - ssboValue,  tboValue и  zeroValue, между которыми мы и будем переключаться. Модификация высоты происходит в строке 38 за счет вызова соответствующей функции.

Прогнав тесты (исходные коды вы можете найти здесь) я обнаружил что отличия минимальны:
SSBO - 418 fps
TBO - 420 fps
Zero - 420 fps 

Есть не существенная просадка фпс (целых 2 кадра) при использовании SSBO, но ею можно пренебречь. За счет использования TMU и prefetch выборка с использованием TBO получилась практически бесплатной. На этом можно было бы и завершить статью (или вообще не выкладывать), со словами "используйте что хотите", но в процессе разработки демо я слегка изменил код, нарушив порядок следования данных, каково же было мое удивление когда я увидел просадку по фпс у SSBO в 2.5 раза относительно TBO!  Придется и с этим разобраться :)

Чтоб понять на сколько влияет случайная выборка я прикрутил к демо случайный выбор смещения, получилось примерно так:


Это изменение никак не повлияло на смену текстурных координат для zero-варианта, но одинаково снизило фпс до ~62 кадров, для TBO - 62.05 fps и SSBO 61.66 fps, что в 7 раз меньше предыдущих результатов, так что с произвольным доступом плохо в обоих случаях...

Но я на этом не остановился, решил провести еще пару тестов:


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

Второй эксперимент был более простым, я попытался добавить некоторый шаг к текстурным координатам, в начале х3, потом х10:


Тут я получил очень интересный результат, как видите со скриншота - для TBO фпс практически не изменился (-1,5 кадра), а вот для SSBO результат оказался плачевным - 57 фпс:

Так что не смотря на всю мою любовь к SSBO приходится признать что для произвольного последовательного доступа TBO все же предпочтительнее, возможно  из-за того что эта нагрузка ложится на TMU, возможно из-за размера prefetch-буфера, он явно имеет предел, что показал тест с совсем случайным доступом, но при последовательном обращении оно отрабатывает на отлично.

Вот такие вот получились результаты...

Исходные коды проекта и фреймворка можно найти здесь:
https://github.com/karpenyuk/GL_Demos/tree/master/VTFvsSSBO
Непосредственно код примера здесь:
https://github.com/karpenyuk/GL_Demos/blob/master/VTFvsSSBO/demo0.inl


Комментариев нет:

Отправить комментарий