macOS下Vulkan计算着色器达最大工作组规模时出现黑线问题
基于SDF的图形引擎计算着色器黑线问题排查与问询
我正在开发一款基于SDF的小型图形引擎,该引擎使用计算着色器从存储缓冲区提供的数据中计算像素颜色。目前遇到一个奇怪的问题:计算着色器渲染的帧中出现黑线(绿色像素为正确渲染结果):

问题出在遍历空间加速数据结构(Bounding Interval Hierarchy,包围区间层次结构)的过程中,具体是从bounding interval link整数的高位比特中提取布尔值的操作。以下是能复现问题的最小GLSL代码示例(已添加注释):
// relevant struct definition of a bounding interval struct bounding_interval { uint link; uint target; float coordinate; }; // compute work group dimensions layout(local_size_x = 32, local_size_y = 32) in; // push constants provide window size and scene bounding box layout(push_constant) uniform constants { uvec2 window_size; vec2 scene_lower_bound; vec2 scene_upper_bound; }; // relevant input buffer ... layout(set = 0, binding = 3) buffer _tlbih { bounding_interval tlbih[]; }; // output image layout(set = 1, binding = 0, rgba8_snorm) uniform writeonly image2D output_image; // checks if the query point is within the scene bounding box // the scene bounding box is provided by push constants bool point_is_within_scene (vec2 point) { return point.x > scene_lower_bound.x && point.x < scene_upper_bound.x && point.y > scene_lower_bound.y && point.y < scene_upper_bound.y; } // checks if the query point is to the left of the bounding interval border bool point_is_left_in_tlbi (vec2 point, uint index) { return point[ (tlbih[index].link & 0x40000000) >> 30 ] < tlbih[index].coordinate; } // checks if the query point is directly on the bounding interval border bool point_is_center_in_tlbi (vec2 point, uint index) { return point[ (tlbih[index].link & 0x40000000) >> 30 ] == tlbih[index].coordinate; } // checks if the current bounding interval is "left" or "right" bool current_tlbi_is_right (uint index) { return bool(tlbih[index].link & 0x20000000); } // checks if a query point intersects a bounding interval bool point_intersects_tlbi (vec2 point, uint index) { return ((point_is_left_in_tlbi(point, index) || point_is_center_in_tlbi(point, index)) && !current_tlbi_is_right(index)) || ((!point_is_left_in_tlbi(point, index) || point_is_center_in_tlbi(point, index)) && current_tlbi_is_right(index)); } // checks if the current bounding interval is a "leaf" and therefore points to a primitive shape bool current_tlbi_is_leaf (uint index) { return bool(tlbih[index].link & 0x80000000); } // supposed to iterate over bounding interval hierarchy and output a colour // removed most irrelevant code vec4 determine_pixel_colour (vec2 point) { vec3 colour = vec3(0.0); uint current_tlbi = 0; if ( !point_is_within_scene( point )) return vec4(colour, 1.0); while (true) { colour.x = 0.0; if ( point_intersects_tlbi( point, current_tlbi )) { // the issue only appears once this nested if statement is added if ( current_tlbi_is_leaf( current_tlbi )) { colour.x = 1.0; } return vec4(colour, 1.0); } else { return vec4(colour, 1.0); } } } // calculates the pixel coordinates based on the compute thread ids ivec2 determine_pixel_coordinates () { return ivec2( clamp(gl_LocalInvocationID.xy + gl_WorkGroupID.xy * 32, ivec2(0), window_size) ); } void main() { ivec2 coordinates = determine_pixel_coordinates(); // flips y coordinate so y+ is up, not down ivec2 y_inverted_coordinates = ivec2( coordinates.x, window_size.y - coordinates.y ); vec2 point = (2.0 * y_inverted_coordinates - window_size) / float(window_size.y); vec4 pixel_colour = determine_pixel_colour(point); // adds green colour to make black lines visible pixel_colour += vec4(0.0, 0.5, 0.0, 1.0); imageStore(output_image, coordinates, pixel_colour); }
已排查情况
- 完全相同的代码在Windows台式机(NVIDIA GTX 1080 Ti)上不会出现黑线问题,仅在运行macOS的MacBook Pro上出现(需通过MoltenVK运行Vulkan);
- 移除
determine_pixel_colour中标记的嵌套if语句后,问题消失; - 将
current_tlbi_is_leaf函数调用移至外层if语句中时,问题消失; - 黑线数量与垂直方向的工作组数量相等,每个32×32线程的计算工作组前半部分渲染正确,后半部分返回黑色;
- 使用硬编码值确定布尔值时(如
bool(2147483648 & 0x80000000)),问题不会出现——怀疑是缓冲区访问问题?
更新内容
更新1:当我将本地工作组规模设置为16×16时,问题不再出现。虽然这能解决问题,但我仍不理解为何32×32的规模会导致一半线程渲染失败:我的MacBook的最大工作组调用数为1024,我的调用数并未超过该上限。
更新2:当选择16×16×4作为本地工作组规模(总调用数与32×32相同)时,同样的问题再次出现。似乎只有当本地调用数等于设备指定的最大值时,才会触发该问题。
技术问询
- 这是否是MoltenVK的问题(仅在Mac设备上出现)?
- 这是否是缓冲区访问问题(硬编码值时无问题)?
- 这是否与Present Mode有关(macOS版本使用FIFO,Windows版本使用Mailbox)?
内容的提问来源于stack exchange,提问作者Chribit
相关产品推荐
相关产品推荐

