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