gpt4 book ai didi

opengl - 有什么更好的方法来同步计算着色器的所有调用?

转载 作者:行者123 更新时间:2023-12-02 11:54:39 32 4
gpt4 key购买 nike

我正在计算着色器中实现一个算法如下

  • 对于图像中的每个像素
  • 计算一些东西并将其存储到临时图像
  • 对于图像中的每个像素
  • 等到它附近的所有 8 个像素都完成第 1 步
  • 从临时图像中读取与其附近 8 个像素相对应的数据
  • 用它们来计算结果

  • 我的工作组大小设置是 layout (local_size_x = 256) in;glDispatchCompute(1, 256, 1);在第 2 步读取临时图像之前,每个像素都要求其所有 8 个相邻像素都完成第 1 步。所以我在第 1 步和第 2 步之间放置了一个 memoryBarrier(),因为 OpenGL Programming Guide, 8th Editionmemory barrier functions apply globally ,不仅仅是同一个本地工作组。
    但这并没有按预期工作。

    为了证明结果,考虑一个简化但相似的问题,
  • 在白色图像上绘制一个黑色矩形
  • 对于图像中的每个像素
  • 如果是黑色,则将 1 存储到临时图像
  • 否则将 0 存储到临时图像
  • 对于图像中的每个像素
  • 如果它是黑色的或者它附近的 8 个像素中至少有一个是黑色的,则将自身设置为黑色

  • 这应该会导致黑色矩形变得越来越大。
    但结果是,矩形变大时会变形。

    那么,memoryBarrier() 真的会等到由同一个 glDispatchCompute 调用触发的所有调用完成它们的内存访问吗?

    我实现锁后 在第 2 步和第 3 步之间 ,结果按预期工作。 (但后来我发现有时会因为超过Windows Time-Out限制而导致程序崩溃!http://nvidia.custhelp.com/app/answers/detail/a_id/3007)
    (p 是当前位置,p+e[i] 是它附近 8 个像素的位置。我使用着色器存储缓冲区对象而不是图像变量,所以我添加了一个函数 posi() 将 ivec2 转换为数组索引)
    bool finished;
    do
    {
    finished = true;
    for(int i = 1; i < 9; i++)
    {
    if(!outOfBound(p+e[i]) && lock[posi(p+e[i])] != 1)
    {
    finished = false;
    }
    }
    }while(!finished);

    如果我误解了 memoryBarrier() 并且它不能做我想做的事,有没有更好的方法来同步计算着色器的调用?

    更新以添加计算着色器代码

    这是我上面描述的黑色矩形示例的计算着色器代码:
    其实 标签 是用于判断像素颜色是黑色还是白色的图像,它被初始化为白色背景上的一个黑色小矩形。
    温度 在我运行这个计算着色器之前设置为零。
    注释代码是关于上述锁的。使用此锁,着色器将提供所需的输出。
    #version 430 core

    layout (local_size_x = 256) in;

    const ivec2 e[9] = {
    ivec2(0,0),
    ivec2(1,0), ivec2(0,1), ivec2(-1,0), ivec2(0,-1),
    ivec2(1,1), ivec2(-1,1), ivec2(-1,-1), ivec2(1,-1)
    };

    layout(std430, binding = 14) coherent buffer tag_buff
    {
    int tag[];
    };
    layout(std430, binding = 15) coherent buffer temp_buff
    {
    int temp[];
    };
    layout(std430, binding = 16) coherent buffer lock_buff
    {
    int lock[];
    };

    int posi(ivec2 point)
    {
    return point.y * 256 + point.x;
    }

    bool outOfBound(ivec2 p)
    {
    return p.x < 0 || p.x >= 256
    || p.y < 0 || p.y >= 256;
    }

    void main()
    {
    ivec2 p = ivec2(gl_GlobalInvocationID.xy);

    int x = tag[posi(p)];
    temp[posi(p)] = x;
    //lock[posi(p)] = 1;

    memoryBarrier();

    //bool finished;
    //do
    //{
    // finished = true;
    // for(int i = 1; i < 9; i++)
    // {
    // if(!outOfBound(p+e[i]) && lock[posi(p+e[i])] != 1)
    // {
    // finished = false;
    // }
    // }
    //}while(!finished);

    // if it's black or at least one of its 8 nearby pixel is black
    // set itself to black
    for(int i = 0; i < 9; i++)
    {
    if(!outOfBound(p+e[i]) && temp[posi(p+e[i])] == 1)
    {
    tag[posi(p)] = 1;
    }
    }
    }

    后来我尝试存储 lock将其元素设置为1并调用memoryBarrier()后进入另一个ssbo,然后在片段着色器中加载新的ssbo并将其打印到屏幕上,从中我发现 lock的某些元素未设置为 1。
    我还在片段着色器或计算着色器中使用图像变量而不是 ssbo,只是为了找到 memoryBarrier 和相干不能改变任何东西。似乎 memoryBarrier 或相干不起作用。

    看了几篇资料后,我似乎知道这里发生了什么,我在下面发表我的理解。如果不正确,请纠正我。
    memoryBarrier无法通过同步内存访问来同步调用。更具体地说,究竟是什么 memoryBarrier do 只是等待所有内存访问完成 调用中已经发生的情况 .即使它在 memoryBarrier 之前,它也不会等待尚未执行的内存访问代码完成。在源代码中。 Opengl 编程指南说 When memoryBarrier() is called, it ensures that any writes to memory that have been performed by the shader invocation have been committed to memory rather than lingering in caches or being scheduled after the call to memoryBarrier() .这意味着,例如,假设有三个调用,如果调用 A 和 B 都为 coherent 运行了 imageStore()图像变量,然后是以下 memoryBarrier A 或 B 将保证此 imageStore() 已更改主内存中的数据,而不仅仅是缓存。但是如果在 A 或 B 调用 memoryBarrier 时调用 C 没有运行 imageStore() ,那么这个 memoryBarrier调用不会等待 C 运行其 imageStore()。所以 memoryBarrier不能帮我实现算法。

    最佳答案

    我偶然发现了一个类似的问题。我不是专家,但我相信我找到了一个很好的解决方案。

    您正确识别 memoryBarrier 必要时确保先前写入的可见性。

    然而,就其本身而言 memoryBarrier几乎没用,因为它不能确保执行顺序。所以虽然你有一个 memoryBarrier可能有些调用在其他调用开始运行之前就已完全完成。memoryBarrier无法使尚未发生的写入可见。

    我们有 barrier 为了解决这个问题:

    For any given static instance of barrier in a compute shader, all invocations within a single work group must enter it before any are allowed to continue beyond it.



    注意重点: barrier 不是 帮助您同步一个内的跨工作组 glDispatchCompute调用,它只在工作组内同步。

    显然, barrier对你的问题没有帮助,
    所以你引入了自己的障碍,它有缺点:
  • 编译器/驱动程序/调度程序不知道这是一个障碍,因此无法对其进行优化。
  • 您的屏障使用自旋锁
    占用处理器。这会增加运行时间,直到看门狗定时器触发。

  • 如果驱动程序知道屏障,它可以安排那些尚未到达屏障的调用运行。在您的解决方案中,驱动程序盲目地安排所有调用,将资源浪费在已经等待的调用上,而不是运行尚未到达屏障的调用。

    该怎么办?

    解决方案

    要实现跨越所有调用的障碍,只需执行多个 glDispatchCompute与适当的交错 glMemoryBarrier调用。

    拆分成多个 glDispatchCompute调用在它们之间形成了障碍。 glMemoryBarrier使先前调用的写入对以后的调用可见。

    关于opengl - 有什么更好的方法来同步计算着色器的所有调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24094469/

    32 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com