direct3d - 是否可以使用 HLSL 计算着色器写入非 4 字节对齐的地址?
问题描述
我正在尝试将现有的 OpenCL 内核转换为 HLSL 计算着色器。
OpenCL 内核对 RGBA 纹理中的每个像素进行采样,并将每个颜色通道写入一个紧密封装的数组。
uchar
所以基本上,我需要以类似于这样的模式写入一个紧凑的数组:
r r r ... r g g g ... g b b b ... b a a a ... a
其中每个字母代表源自像素通道的单个字节(红色/绿色/蓝色/阿尔法)。
RWByteAddressBuffer
浏览Store 方法的文档,它清楚地指出:
void Store(
in uint address,
in uint value
);
地址 [in]
类型:uint
以字节为单位的输入地址,必须是 4 的倍数。
为了将正确的模式写入缓冲区,我必须能够将单个字节写入非对齐地址。在 OpenCL / CUDA 中,这是非常微不足道的。
- 使用 HLSL 在技术上是否可以实现这一目标?
- 这是一个已知的限制吗?可能的解决方法?
解决方案
据我所知,在这种情况下不可能直接写入非对齐地址。但是,您可以使用一些小技巧来实现您想要的。下面你可以看到整个计算着色器的代码,它完全符合你的要求。特别是该功能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
因为多个线程写入同一地址会相互干扰,从而导致写后写危险。这当然只有在发出调度调用之前用零初始化整个输出缓冲区时才有效。
推荐阅读
- linux - 为什么 NOP 的数量似乎会影响 shellcode 是否成功执行?
- android - 在加载活动期间将嵌套滚动视图的顶部设置为总高度的一半时遇到问题
- java - 用 Regex 替换 Java 中的空格和分号
- javascript - 数据存在于剪贴板中,但我无法在 JavaScript 中获取它
- java - Spring中构造函数参数的歧义
- html - flexbox子元素内部div的高度
- javascript - ESLint - ReactJS 中没有未使用的表达式
- angular - 角日期选择器隐藏显示按钮单击
- jsf - @EJB 在 @ManagedBean 中为空
- linux - 更改配置后 statsinfo 扩展不活动