2016-08-02 33 views
1

Ich rende einzelne Pixelpunkte in eine uint32-Textur mit einem Compute-Shader. Die Textur ist eine 3D-Textur, x und y sind Ansichtsfenster-Koordinaten, z hat Tiefeninformationen über die Koordinate 0 und zusätzliche Attribute auf 1. also zwei manuell erstellte Renderziele, wenn Sie so wollen. Code sieht wie folgt aus:Manuelles Tiefen-Rendering: Zufällige Ergebnisse trotz atomarer Operationen

layout (r32ui, binding = 0) coherent volatile uniform uimage3D renderBuffer; 
layout (rgba32f, binding = 1) restrict readonly uniform imageBuffer pointBuffer; 

for(int j = 0; j < numPoints/gl_WorkGroupSize.x + 1; j++) 
{ 
    vec4 point = imageLoad(pointBuffer, ...) 
    // ... transform point ... 
    uint originalDepth = imageAtomicMin(renderBuffer, ivec3(imageCoords, 0), point.depth); 
    if (originalDepth >= point.depth) 
    { 
     // write happened, store the attributes 
     imageStore(renderBuffer, ivec3(imageCoords, 1), point.attributes); 
    } 
} 

während die Tiefenwerte richtig sind, ich habe ein paar Pixel, bei denen die Attribute zwischen zwei Werten flackern.

Die Reihenfolge der Punkte im PointBuffer ist zufällig (aber ich habe überprüft, die Menge aller Punkte ist immer gleich), so mein erster Gedanke war, dass zwei gleiche Tiefenwerte die Ausgabe ändern können, je nachdem, welche kommt zuerst. also habe ich es so gemacht, dass if originalDepth == point.depthimageAtomicMax verwendet, um immer die gleichen der beiden alternativen Attribute geschrieben zu haben, aber das hat nichts verändert.

Ich verstreute und memoryBarrier() überall, aber das änderte nichts. ich entfernte auch alle divergierenden Kontrollflüsse dafür, änderte nichts.

Wenn Sie die lokale Arbeitsgröße auf 32 reduzieren, werden 90% des Flackerns entfernt, einige bleiben jedoch erhalten.

alle Ideen würden sehr geschätzt werden.

bearbeiten: Bevor Sie fragen, warum ich dieses Zeug manuell anstelle von normalen Rasterisierung und Fragment Shader, der Grund ist Leistung. Der Rasterizer hilft nicht, da ich Single-Pixel-Punkte rendere, Shared Memory beschleunigt Dinge sehr, und ich rendere jeden Punkt mehrere Male, was mich verlangte, einen Geometrie-Shader zu verwenden, der langsam war.

Antwort

1

Das Problem ist das: Sie haben eine Race Condition beim Schreiben auf renderBuffer. Wenn zwei verschiedene CS-Aufrufe demselben Pixel zugeordnet werden und beide sich dazu entschließen, den Wert zu schreiben, gibt es ein Rennen über Ihren imageStore-Aufruf. Man kann das andere überschreiben, es kann ein teilweises Überschreiben oder etwas ganz anderes sein. Aber auf jeden Fall funktioniert es nicht garantiert.

Dies ist am besten zu lösen, indem Sie tun, was Rasterizer tun: brechen Sie den Prozess in zwei separate Phasen. Die erste Phase erledigt den ... transform point ... Teil und schreibt diese Daten in einen Puffer. Die zweite Phase geht dann durch die Punkte und schreibt sie in das endgültige Bild.

In Phase 2 führt jeder CS-Aufruf alle der Verarbeitung für ein bestimmtes Ausgabe-Pixel. Auf diese Weise gibt es keine Wettlaufbedingungen. Natürlich erfordert dies, dass Phase 1 Daten in einer Weise erzeugt, die pro Pixel geordnet werden kann.

Es gibt mehrere Möglichkeiten, um Letzteres zu gehen. Sie können eine verknüpfte Liste mit einer Liste pro Pixel verwenden. Oder Sie können eine Liste pro Arbeitsgruppe verwenden, wobei eine Arbeitsgruppe eine X/Y-Region des Pixelraums darstellt. In diesem Fall würden Sie lokalen gemeinsamen Speicher als Ihren lokalen Tiefenpuffer verwenden, wobei alle CS-Aufrufe von dieser Region lesen/schreiben. Nachdem alle Pixel verarbeitet sind, schreiben Sie sie in den realen Speicher. Grundsätzlich würden Sie das Kachel-basierte Rendering manuell implementieren. Wenn Sie einen Lot dieser Punkte haben, würde eine Kachel-basierte Lösung Ihnen erlauben, Pipelining zu integrieren, so dass Sie nicht warten müssen, bis die gesamte Phase 1 fertig ist, bevor Sie mit einigen beginnen Phase 2. Sie könnten Phase 1 in Stücke zerlegen. Sie starten ein paar Phase-1-Chunks, dann einen Phase-2-Chunk, der aus der ersten Phase 1 liest, dann eine weitere Phase 1 und so weiter.

Vulkan verfügt mit seinem Event-System über bessere Werkzeuge zum Aufbau einer so effizienten Abhängigkeitskette als OpenGL.

+0

Ich selbst schrieb in einem verdammten Kommentar direkt über diesem BildStore mit der Aussage "mögliche Race Condition hier", aber aus irgendeinem Grund entschied, dass es nicht verantwortlich sein könnte. Wahrscheinlich habe ich mich zu sehr darauf konzentriert ... Egal danke :) Ich rendere in einen Atlas von 64x64 Shadowmaps und habe schon eine Arbeitsgruppe pro SM, aber das ist zu groß, um den Tiefenpuffer im Shared Memory zu haben. Zwei Fragen: Verschiebt die Workgroup-Liste das Problem nicht einfach vom globalen in den gemeinsamen Speicher und ich muss dort auch die Race Condition lösen? Zweitens würde das Pipelining mehrere Dispatch-Aufrufe erfordern, nicht wahr? – karyon

+0

"* verschiebt die pro-workgroup-Liste nicht nur das Problem von globalem zu geteiltem Speicher und ich müsste auch dort die Race-Bedingung lösen? *" Ja, aber es gibt Dinge, die Sie tun können, um es dort zu lösen , die relativ billig sind. –

+0

Ein Zeiger zum Starten wäre nett, da ich am Ende meines Wissens hier bin und dieses Zeug kaum Google-fähig ist ... – karyon