GLSL SpinLock只有大部分工作 [英] GLSL SpinLock only Mostly Works

查看:240
本文介绍了GLSL SpinLock只有大部分工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用GLSL自旋锁实现了深度剥离算法(灵感来自,我发现程序必须是并行的,因为如果两个线程在同一个像素,warp不能继续(一个必须等​​待,而另一个继续,并且GPU线程warp中的所有线程必须同时执行)。



我的片段程序看起来像这样(添加注释和间距):

  #version 420核心

//锁定纹理
layout(r32ui)coherent uniform uimage2D img2D_0;
//数据纹理,也渲染目标
布局(RGBA32F)相干统一image2D img2D_1;

//在data中插入new_data,一个排序的列表
vec4 insert(vec4 data,float new_data){
if(new_data< data.x)return vec4 (new_data,data.xyz);
else if(new_data< data.y)return vec4(data.x,new_data,data.yz);
else if(new_data< data.z)return vec4(data.xy,new_data,data.z);
else if(new_data< data.w)return vec4(data.xyz,new_data);
else return data;
}

void main(){
ivec2 coord = ivec2(gl_FragCoord.xy);

//这里的想法是保持循环遍历一个像素,直到写入一个值。
//通过循环遍历整个逻辑,同一warp中的线程不会被其他等待线程阻塞
//。第一个imageAtomicExchange调用将
//锁定值设置为1.如果锁定值已经是1,那么有人
// else具有锁定,并且can_write为false。如果锁定值为0,
//那么锁定是自由的,并且can_write是true。然后读取深度,
//插入的新值,但只有在can_write为true时才写入(
//锁定纹理是免费的)。第二个imageAtomicExchange调用将
//锁定重置为0.

bool have_written = false;
while(!have_written){
bool can_write =(imageAtomicExchange(img2D_0,coord,1u)!= 1u);

memoryBarrier();

vec4 depths = imageLoad(img2D_1,coord);
depths = insert(depths,gl_FragCoord.z);

if(can_write){
imageStore(img2D_1,coord,depths);
have_written = true;
}

memoryBarrier();

imageAtomicExchange(img2D_0,coord,0);

memoryBarrier();
}
discard; //已经使用imageStore写了渲染目标
}

我的问题是为什么这个斑点行为发生?我希望螺旋锁100%的时间工作!

解决方案

imageAtomicExchange(img2D_0,coord,0);需要在if语句内部,因为它重置锁定变量,即使对于没有它的线程!更改此操作会修复此问题。


I have implemented a depth peeling algorithm using a GLSL spinlock (inspired by this). In the following visualization, notice how overall the depth peeling algorithm functions correctly (first layer top left, second layer top right, third layer bottom left, fourth layer bottom right). The four depth layers are stored into a single RGBA texture.

Unfortunately, the spinlock sometimes fails to prevent errors--you can see little white speckles, particularly in the fourth layer. There's also one on the wing of the spaceship in the second layer. These speckles vary each frame.

In my GLSL spinlock, when a fragment is to be drawn, the fragment program reads and write a locking value into a separate locking texture atomically, waiting until a 0 shows up, indicating that the lock is open. In practice, I found that the program must be parallel, because if two threads are on the same pixel, the warp cannot continue (one must wait, while the other continues, and all threads in a GPU thread warp must execute simultaneously).

My fragment program looks like this (comments and spacing added):

#version 420 core

//locking texture
layout(r32ui) coherent uniform uimage2D img2D_0;
//data texture, also render target
layout(RGBA32F) coherent uniform image2D img2D_1;

//Inserts "new_data" into "data", a sorted list
vec4 insert(vec4 data, float new_data) {
    if      (new_data<data.x) return vec4(      new_data,data.xyz);
    else if (new_data<data.y) return vec4(data.x,new_data,data.yz);
    else if (new_data<data.z) return vec4(data.xy,new_data,data.z);
    else if (new_data<data.w) return vec4(data.xyz,new_data      );
    else                      return data;
}

void main() {
    ivec2 coord = ivec2(gl_FragCoord.xy);

    //The idea here is to keep looping over a pixel until a value is written.
    //By looping over the entire logic, threads in the same warp aren't stalled
    //by other waiting threads.  The first imageAtomicExchange call sets the
    //locking value to 1.  If the locking value was already 1, then someone
    //else has the lock, and can_write is false.   If the locking value was 0,
    //then the lock is free, and can_write is true.  The depth is then read,
    //the new value inserted, but only written if can_write is true (the
    //locking texture was free).  The second imageAtomicExchange call resets
    //the lock back to 0.

    bool have_written = false;
    while (!have_written) {
        bool can_write = (imageAtomicExchange(img2D_0,coord,1u) != 1u);

        memoryBarrier();

        vec4 depths = imageLoad(img2D_1,coord);
        depths = insert(depths,gl_FragCoord.z);

        if (can_write) {
            imageStore(img2D_1,coord,depths);
            have_written = true;
        }

        memoryBarrier();

        imageAtomicExchange(img2D_0,coord,0);

        memoryBarrier();
    }
    discard; //Already wrote to render target with imageStore
}

My question is why this speckling behavior occurs? I want the spinlock to work 100% of the time! Could it relate to my placement of memoryBarrier()?

解决方案

The "imageAtomicExchange(img2D_0,coord,0);" needs to be inside the if statement, since it is resetting the lock variable even for threads that didn't have it! Changing this fixes it.

这篇关于GLSL SpinLock只有大部分工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆