Как расширить возможности вершинных шейдеров для GPGPU

Я пытаюсь реализовать хешер Scrypt (для майнера LTC) на GLSL (не спрашивайте меня, почему).

И на самом деле я застрял в алгоритме HMAC SHA-256. Несмотря на то, что я правильно реализовал SHA-256 (он возвращает коррент-хеш для ввода), фрагментный шейдер перестает компилироваться, когда я добавляю последний шаг (хеширование предыдущего хеша, объединенного с oKey).

Шейдер не может выполнять более трех раундов SHA-256. Он просто перестает компилироваться. Какие ограничения? Он не использует много памяти, всего 174 объекта vec2. Похоже, это не имеет отношения к памяти, потому что любой дополнительный раунд SHA256 не требует новой памяти. И, похоже, это не имеет отношения к размеру области просмотра. Он перестает работать как с окнами просмотра 1x1, так и с 1x128.

Я начал делать майнер на WebGL, но после появления лимита попытался запустить тот же шейдер в Qt на полнофункциональном OpenGL. В результате настольный OpenGL позволяет на один раунд SHA256 меньше, чем OpenGL ES в WebGL (почему?).

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

Я не использую никаких текстур, каких-либо расширений, медленных вещей и т. Д. Просто простой квадрат (4 вершины vec2) и несколько форм для фрагментного шейдера. Входные данные составляют всего 80 байт, результат фрагментного шейдера является двоичным (черный или белый), поэтому задача идеально соответствует принципам GLSL.

Моя видеокарта - Radeon HD7970 с большим количеством видеопамяти, которая способна уместить сотни потоков scrypt (scrypt использует 128 КБ на хэш, но я не могу достичь только HMAC-SHA-256). Моя карта поддерживает OpenGL 4.4.

Я новичок в OpenGL и могу что-то не так понять. Я понимаю, что фрагментный шейдер запускается для каждого пикселя отдельно, но если у меня есть область просмотра 1x128, используются только 128x348 байтов. Где предел фрагментного шейдера.

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

uniform vec2 base_nonce[2];
uniform vec2 header[20];    /* Header of the block */
uniform vec2 H[8];
uniform vec2 K[64];

void sha256_round(inout vec2 w[64], inout vec2 t[8], inout vec2 hash[8]) {
    for (int i = 0; i < 64; i++) {
        if( i > 15 ) {
            w[i] = blend(w[i-16], w[i-15], w[i-7], w[i-2]);
        }

        _s0 = e0(t[0]);
        _maj = maj(t[0],t[1],t[2]);
        _t2 = safe_add(_s0, _maj);
        _s1 = e1(t[4]);
        _ch = ch(t[4], t[5], t[6]);
        _t1 = safe_add(safe_add(safe_add(safe_add(t[7], _s1), _ch), K[i]), w[i]);

        t[7] = t[6]; t[6] = t[5]; t[5] = t[4];
        t[4] = safe_add(t[3], _t1);
        t[3] = t[2]; t[2] = t[1]; t[1] = t[0];
        t[0] = safe_add(_t1, _t2);
    }
    for (int i = 0; i < 8; i++) {
        hash[i] = safe_add(t[i], hash[i]);
        t[i] = hash[i];
    }
}

void main () {
    vec2 key_hash[8]; /* Our SHA-256 hash */
    vec2 i_key[16];
    vec2 i_key_hash[8];
    vec2 o_key[16];

    vec2 nonced_header[20]; /* Header with nonce */
    set_nonce_to_header(nonced_header);

    vec2 P[32]; /* Padded SHA-256 message */
    pad_the_header(P, nonced_header);

    /* Hash HMAC secret key */
    sha256(P, key_hash);

    /* Make iKey and oKey */
    for(int i = 0; i < 16; i++) {
        if (i < 8) {
            i_key[i] = xor(key_hash[i], vec2(Ox3636, Ox3636));
            o_key[i] = xor(key_hash[i], vec2(Ox5c5c, Ox5c5c));
        } else {
            i_key[i] = vec2(Ox3636, Ox3636);
            o_key[i] = vec2(Ox5c5c, Ox5c5c);
        }
    }

    /* SHA256 hash of iKey */

    for (int i = 0; i < 8; i++) {
        i_key_hash[i] = H[i];
        t[i] = i_key_hash[i];
    }

    for (int i = 0; i < 16; i++) { w[i] = i_key[i]; }
    sha256_round(w, t, i_key_hash);

    gl_FragColor = toRGBA(i_key_hash[0]);
}

Какие решения я могу использовать для улучшения ситуации? Есть ли что-нибудь крутое в OpenGL 4.4, в OpenGL ES 3.1? Можно ли вообще делать такие вычисления и хранить столько (128кБ) во фрагментном шейдере? Каковы ограничения для вершинного шейдера? Могу ли я сделать то же самое с вершинным шейдером вместо фрагмента?


person Kukunin    schedule 30.07.2014    source источник
comment
Существуют реализации как WebGL, так и Qt, которые используют ANGLE для преобразования вызовов OpenGL в DirectX. Так что этот перевод может усложнить ситуацию (если это произойдет). Я предполагаю, что развертывание цикла может увеличить размер исполняемого файла за пределы возможностей вашего графического процессора (кажется маловероятным, но я не знаю полной сложности шейдера)   -  person PeterT    schedule 30.07.2014
comment
Вы предлагаете мне попробовать в Linux?   -  person Kukunin    schedule 30.07.2014
comment
Я не знаю, как обстоят дела с драйвером AMD, но я думаю, вы могли бы проверить двоичный размер шейдера, иначе у меня нет никаких идей, я сам не делал тонны OpenGL.   -  person PeterT    schedule 30.07.2014
comment
Некоторое время назад я перевел шейдер HLSL (из WebGL). Кажется, он переводит 1 на 1. pastebin.com/uY7V8uHC. Цикл for в GLSL соответствует тому же циклу в HLSL   -  person Kukunin    schedule 30.07.2014
comment
Вы успешно завершили работу с glsl-майнером или в конце концов застряли?   -  person mixalbl4    schedule 22.04.2018
comment
Вы проверили мое репо github.com/Kukunin/webgl-scrypt? Он реализовал весь алгоритм scrypt с использованием WebGL, но он в 10 раз медленнее, чем простой JS. Это должно быть быстрее с WebGL2, но я не пробовал   -  person Kukunin    schedule 23.04.2018
comment
@Kukunin, нет, не видел. У меня результат 6747 мс с webgl 2.0, где я могу найти версию с простым JS для сравнения с тем же хешем?   -  person mixalbl4    schedule 30.04.2018
comment
Взгляните на github.com/ricmoo/scrypt-js. Это чистый JS, без ASM.js или WebAssembly. Этот быстрее github.com/tonyg/js-scrypt   -  person Kukunin    schedule 01.05.2018


Ответы (1)


Я пытаюсь ответить на свой вопрос.

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

С другой стороны, вы можете менять свои шейдерные программы во время рендеринга десятки или сотни раз. Это нормальная практика.

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

Из-за статистики webgl 96,5% клиентов имеют MAX_TEXTURE_SIZE eq 4096. Это дает вам 32 мегабайта памяти. Он может содержать черновые данные для 256 потоков вычислений scrypt.

person Kukunin    schedule 31.07.2014
comment
Как избежать длительных операций с памятью с текстурой (GPU ‹-› CPU)? Можно ли привязать результирующую текстуру одного рендера к входу другого без копирования в память ЦП и обратно в ГП? - person Kukunin; 31.07.2014
comment
Глупый вопрос. Конечно вы можете. Просто привяжите свою текстуру к буферу кадра, выполните рендеринг в буфер кадра, и данные будут в вашей текстуре. Просто прикрепите эту текстуру к вашей следующей сцене и все. Дополнительная информация здесь - person Kukunin; 31.07.2014