Можно ли записать в выровненный по 4 байта адрес с помощью вычислительного шейдера HLSL? - PullRequest
0 голосов
/ 30 декабря 2018

Я пытаюсь преобразовать существующее ядро ​​OpenCL в вычислительный шейдер HLSL.

Ядро OpenCL выбирает каждый пиксель в текстуре RGBA и записывает каждый цветовой канал в плотно упакованный массив.

В общем, мне нужно записать в плотно упакованный массив uchar в виде шаблона, который выглядит примерно так:

r r r ... r g g g ... g b b b ... b a a a ... a

, где каждая буква обозначает один байт (красный / зеленый / синий / альфа), который исходит из пиксельного канала.

, просматривая документацию по RWByteAddressBuffer методу хранилища, он четко гласит:

void Store(
  in uint address,
  in uint value
);

address [in]

Тип: uint

Адрес ввода в байтах, , который должен быть кратен 4 .

Для записи правильного шаблона в буфер,Я должен быть в состоянии записать один байт на невыровненный адрес.В OpenCL / CUDA это довольно тривиально.

  • Технически возможно ли добиться этого с помощью HLSL?
  • Это известное ограничение?возможные обходные пути?

1 Ответ

0 голосов
/ 06 января 2019

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

Texture2D<float4> Input;
RWByteAddressBuffer Output;

void StoreValueAtByte(in uint index_of_byte, in uint value) {

    // Calculate the address of the 4-byte-slot in which index_of_byte resides
    uint addr_align4 = floor(float(index_of_byte) / 4.0f) * 4;

    // Calculate which byte within the 4-byte-slot it is
    uint location = index_of_byte % 4;

    // Shift bits to their proper location within its 4-byte-slot
    value = value << ((3 - location) * 8);

    // Write value to buffer
    Output.InterlockedOr(addr_align4, value);
}

[numthreads(20, 20, 1)]
void CSMAIN(uint3 ID : SV_DispatchThreadID) {

    // Get width and height of texture
    uint tex_width, tex_height;
    Input.GetDimensions(tex_width, tex_height);

    // Make sure thread does not operate outside the texture
    if(tex_width > ID.x && tex_height > ID.y) {

        uint num_pixels = tex_width * tex_height;

        // Calculate address of where to write color channel data of pixel
        uint addr_red = 0 * num_pixels + ID.y * tex_width + ID.x;
        uint addr_green = 1 * num_pixels + ID.y * tex_width + ID.x;
        uint addr_blue = 2 * num_pixels + ID.y * tex_width + ID.x;
        uint addr_alpha = 3 * num_pixels + ID.y * tex_width + ID.x;

        // Get color of pixel and convert from [0,1] to [0,255]
        float4 color = Input[ID.xy];
        uint4 color_final = uint4(round(color.x * 255), round(color.y * 255), round(color.z * 255), round(color.w * 255));      

        // Store color channel values in output buffer
        StoreValueAtByte(addr_red, color_final.x);
        StoreValueAtByte(addr_green, color_final.y);
        StoreValueAtByte(addr_blue, color_final.z);
        StoreValueAtByte(addr_alpha, color_final.w);
    }
}

Я надеюсь, что код не требует пояснений, потому что это трудно объяснить, но я все равно попробую.
Первое, что делает функция StoreValueAtByte, это вычисление адреса 4-байт-слот, содержащий байт, в который вы хотите записать.После этого вычисляется позиция байта внутри 4-байтового слота (это первый, третий, третий или четвертый байт в слоте).Поскольку байт, который вы хотите записать, уже находится внутри 4-байтовой переменной (а именно value) и занимает самый правый байт, вам просто нужно переместить байт в его правильное положение внутри 4-байтовой переменной.После этого вам просто нужно записать переменную value в буфер по 4-байтовому адресу.Это делается с использованием bitwise OR, потому что несколько потоков записывают по одному и тому же адресу, мешая друг другу, что приводит к угрозе записи после записи.Это, конечно, работает только в том случае, если вы инициализируете весь выходной буфер нулями перед выполнением вызова отправки.

...