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


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

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

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

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

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

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

Моя видеокарта Radeon HD7970 с большим количеством VRAM, которая способна вместить сотни потоков 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 кб) в шейдере фрагментов? Каковы ограничения для вершинного шейдера? Могу ли я сделать то же самое с вершинным шейдером вместо фрагмента?

1 2

1 ответ:

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

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

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

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

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