光线追踪相交着色器读取SSBO始终为零的问题排查
我的相交着色器从SSBO中读取的值始终为0。尝试使用debugPrintEXT,但无法在相交着色器内输出内容。通过ray payload输出命中颜色时,得到的始终是纯黑色。起初以为是数据复制方式有问题,但对比NVIDIA示例代码,实现并无问题。验证层也没有抛出任何警告或错误。用NVIDIA NSight Graphics查看缓冲区,里面的数据和复制的完全一致,布局符合预期。检查SPIRV指令,所有指令都正确指向缓冲区的偏移量——NSight能看到正确的值,但着色器就是读不到!
着色器代码
// Hitting a triangle. #version 460 core #extension GL_EXT_ray_tracing : enable layout(location = 0) rayPayloadInEXT vec4 payload; hitAttributeEXT vec2 attributes; layout(push_constant) uniform rayParams { vec3 rayOrigin; vec3 rayDir; mat4 transformMatrix; float maxDistance; }; struct PhongMaterial { float ambient; float diffuse; float specular; float shininess; float reflective; float transparency; float refraction; vec4 color; float reserved_1; vec4 reserved_2; }; layout(set = 0, binding = 2) readonly buffer Materials { PhongMaterial m[]; } materials; void main() { PhongMaterial material = materials.m[0]; payload = material.color; }
Rust结构体布局
#[repr(C)] pub struct PhongMaterial { pub ambient: f32, pub diffuse: f32, pub specular: f32, pub shininess: f32, pub reflective: f32, pub transparency: f32, pub refraction: f32, pub color: ColorRGBA, reserved: [u8; 20], }
该结构体总字节大小为44字节,从临时CPU缓冲区复制到GPU缓冲区时会对齐到16的倍数,即复制48字节的数据。
NSight截图显示缓冲区数据与Rust中设置的完全一致,布局符合预期。
SPIRV代码片段
OpName %58 "PhongMaterial" OpMemberDecorate %58 0 Offset 0 OpMemberDecorate %58 1 Offset 4 OpMemberDecorate %58 2 Offset 8 OpMemberDecorate %58 3 Offset 12 OpMemberDecorate %58 4 Offset 16 OpMemberDecorate %58 5 Offset 20 OpMemberDecorate %58 6 Offset 24 OpMemberDecorate %58 7 Offset 32 OpMemberDecorate %58 8 Offset 48 OpMemberDecorate %58 9 Offset 64
尝试调整填充、内存对齐都没有效果,即便存在对齐问题,至少前几个值的布局是正确的,着色器应该能读取到才对。
更新:最近命中着色器读取缓冲区完全异常
测试代码:
void main() { PhongMaterial material = materials.m[0]; if (material.ambient <= 0.1) { payload = vec4(1.0, 0.0, 0.0, 1.0); } else if (material.ambient <= 0.5) { payload = vec4(0.0, 1.0, 0.0, 1.0); } else { payload = vec4(0.0, 0.0, 1.0, 1.0); } }
所有分支都没有执行,payload完全未被设置,因此显示纯黑色。但如果不尝试访问material缓冲区,或者在if-else之后强行赋值payload,设置的颜色就能正常显示:
if (material.ambient <= 0.1) { payload = vec4(1.0, 0.0, 0.0, 1.0); } else if (material.ambient <= 0.5) { payload = vec4(0.0, 1.0, 0.0, 1.0); } else { payload = vec4(0.0, 0.0, 1.0, 1.0); } payload = vec4(1.0, 1.0, 0.0, 1.0);
除此之外,如果在分支前先设置颜色,颜色又会变成黑色,仿佛payload没有被设置:
payload = vec4(1.0, 1.0, 0.0, 1.0); if (material.ambient <= 0.1) { payload = vec4(1.0, 0.0, 0.0, 1.0); } else if (material.ambient <= 0.5) { payload = vec4(0.0, 1.0, 0.0, 1.0); } else { payload = vec4(0.0, 0.0, 1.0, 1.0); }
如果只保留一个if语句并检查任意数值,它总是被判定为true。这是不是未定义行为(UB)?
另一张NSight截图显示缓冲区布局正确,且已正确绑定到命中着色器。
现在猜测是命中着色器的着色器绑定表(Shader Binding Table)设置不正确,但无法理解为什么着色器还能被调用。
致投票关闭此问题的人:
对于任何了解Vulkan、Rust和GLSL的人来说,复现步骤都很明确:使用所需类型的GPU缓冲区即可。NSight已证明这部分操作正确,因此无需提供最小可复现示例(MCVE)。唯一无法正常工作的是着色器,其代码已作为MCVE。预期行为很明显——着色器应读取缓冲区中的实际数据而非始终为0。如有疑问,请提问,除非你确定问题不符合要求,否则不要投票关闭。即便你确定,也请告知作者,不要默默投票。只要提问,我会提供任何所需信息,而且我已经提供了所有必要的信息。
内容的提问来源于stack exchange,提问作者VP.

