Можно ли записать в выровненный по 4 байта адрес с помощью вычислительного шейдера HLSL?

Я пытаюсь преобразовать существующее ядро ​​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
);

адрес [в]

Тип: уинт

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

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

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

1 ответ

Решение

Насколько я знаю, в этом сценарии невозможно писать напрямую по неподключенному адресу. Вы можете, однако, использовать небольшую хитрость, чтобы достичь того, что вы хотите. Ниже вы можете увидеть код всего вычислительного шейдера, который делает именно то, что вы хотите. Функция 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 потому что несколько потоков пишут по одному и тому же адресу, мешая друг другу, что создает опасность записи после записи. Это, конечно, работает только в том случае, если вы инициализируете весь выходной буфер нулями перед выполнением вызова диспетчеризации.

Другие вопросы по тегам