Tylko GLSL SpinLock Przeważnie działa

Zaimplementowałem algorytm peelingu głębokości z wykorzystaniem spinlocka GLSL (zainspirowany przezto). W poniższej wizualizacji zauważ, jak ogólnie algorytm peelingu głębokości działa poprawnie (pierwsza warstwa na górze z lewej, druga warstwa na górze z prawej, trzecia warstwa na dole z lewej, czwarta warstwa na dole z prawej). Cztery warstwy głębokości są przechowywane w pojedynczej teksturze RGBA.

Niestety spinlock czasami nie zapobiega błędom - widać małe białe plamki, szczególnie w czwartej warstwie. Jest też jeden na skrzydle statku kosmicznego w drugiej warstwie. Te plamki zmieniają każdą ramkę.

W moim spinlocku GLSL, gdy fragment ma być narysowany, program fragmentu odczytuje i zapisuje atomowo wartość blokującą w oddzielnej strukturze blokującej, czekając, aż pojawi się 0, wskazując, że zamek jest otwarty.W praktyce, Odkryłem, że program musi być równoległy, ponieważ jeśli dwa wątki są na tym samym pikselu, warp nie może być kontynuowany (trzeba czekać, podczas gdy drugi kontynuuje, a wszystkie wątki w warp wątku GPU muszą działać jednocześnie).

Mój program fragmentów wygląda tak (dodano komentarze i odstępy):

#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
}

Moje pytanie brzmi: dlaczego to zachowanie cętkowania występuje? Chcę, żeby spinlock działał przez 100% czasu! Czy może to dotyczyć mojego umieszczenia pamięci memoryBarrier ()?

questionAnswers(2)

yourAnswerToTheQuestion