You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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相同)时,同样的问题再次出现。似乎只有当本地调用数等于设备指定的最大值时,才会触发该问题。

技术问询

  1. 这是否是MoltenVK的问题(仅在Mac设备上出现)?
  2. 这是否是缓冲区访问问题(硬编码值时无问题)?
  3. 这是否与Present Mode有关(macOS版本使用FIFO,Windows版本使用Mailbox)?

内容的提问来源于stack exchange,提问作者Chribit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 12:01:08