Ermitteln der Anzahl der Fragmente, die den Tiefentest bestanden haben

8

In "modernen" Umgebungen bietet die Erweiterung "NV Occlusion Query" eine Methode zum Abrufen der Anzahl der Fragmente, die den Tiefentest bestanden haben. Auf dem iPad / iPhone mit OpenGL ES ist die Erweiterung jedoch nicht verfügbar.

Was ist der performanteste Ansatz, um ein ähnliches Verhalten im Fragment-Shader zu implementieren?

Einige meiner Ideen:

  • Rendern Sie das Objekt vollständig in Weiß und zählen Sie dann alle Farben mit einem Shader mit zwei Durchgängen zusammen, wobei zuerst eine vertikale Linie gerendert wird und der Shader für jedes Fragment die Summe über die gesamte Zeile berechnet. Dann wird ein einzelner Scheitelpunkt gerendert, dessen Fragment alle Teilsummen des ersten Durchgangs summiert. Scheint nicht sehr effizient zu sein.

  • Rendern Sie das Objekt vollständig in Weiß über einem schwarzen Hintergrund. Rekursives Downsample, Missbrauch der linearen Hardware-Interpolation zwischen Texturen bis zu einer relativ kleinen Auflösung. Dies führt zu Fragmenten, die abhängig von der Anzahl der weißen Pixel in ihrem entsprechenden Bereich einen Graustufenpegel aufweisen. Ist das überhaupt genau genug?

  • Verwenden Sie Mipmaps und lesen Sie einfach das Pixel auf der 1x1-Ebene. Wieder die Frage nach der Genauigkeit und ob es überhaupt möglich ist, Texturen ohne Zweierpotenz zu verwenden.

Das Problem bei diesen Ansätzen ist, dass die Pipeline blockiert wird, was zu erheblichen Leistungsproblemen führt. Daher suche ich nach einem performanteren Weg, um mein Ziel zu erreichen.

Verwenden der Erweiterung EXT_OCCLUSION_QUERY_BOOLEAN

Apple hat EXT_OCCLUSION_QUERY_BOOLEAN in iOS 5.0 für iPad 2 eingeführt.

"4.1.6  Occlusion Queries

Occlusion queries use query objects to track the number of fragments or 
samples that pass the depth test. An occlusion query can be started and 
finished by calling BeginQueryEXT and EndQueryEXT, respectively, with a 
target of ANY_SAMPLES_PASSED_EXT or ANY_SAMPLES_PASSED_CONSERVATIVE_EXT.

When an occlusion query is started with the target 
ANY_SAMPLES_PASSED_EXT, the samples-boolean state maintained by the GL is
set to FALSE. While that occlusion query is active, the samples-boolean 
state is set to TRUE if any fragment or sample passes the depth test. When 
the occlusion query finishes, the samples-boolean state of FALSE or TRUE is
written to the corresponding query object as the query result value, and 
the query result for that object is marked as available. If the target of 
the query is ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, an implementation may 
choose to use a less precise version of the test which can additionally set
the samples-boolean state to TRUE in some other implementation dependent 
cases."

Der erste Satz weist auf ein Verhalten hin, nach dem ich genau suche: Die Anzahl der Pixel, die den Tiefentest bestanden haben, wird asynchron ohne großen Leistungsverlust ermittelt. Der Rest des Dokuments beschreibt jedoch nur, wie boolesche Ergebnisse erzielt werden.

Ist es möglich, diese Erweiterung zu nutzen, um die Pixelanzahl zu ermitteln? Unterstützt die Hardware dies, sodass möglicherweise eine versteckte API vorhanden ist, um Zugriff auf die Pixelanzahl zu erhalten?

Andere Erweiterungen, die ausgenutzt werden könnten, wären Debugging-Funktionen wie die Häufigkeit, mit der der Fragment-Shader aufgerufen wurde (PSInvocations in DirectX - nicht sicher, ob in OpenGL ES etwas Ähnliches verfügbar ist). Dies würde jedoch auch zu einem Pipeline-Stillstand führen.

Etan
quelle
Was ist der Endeffekt, den Sie erreichen möchten?
Tetrad
Ich brauche die Pixelanzahl nicht für einen Rendering-Effekt, sondern für die Berechnung. Ich möchte es verwenden, um das Gewicht von Partikeln in einem Partikelfilter zu berechnen.
Etan
Unterstützt Apple AMD_performance_monitor? Wenn ja, können Sie möglicherweise den Zähler finden, der die übergebenen Pixel anzeigt. Beachten Sie, dass dies sehr, sehr gerätespezifisch ist, auch wenn Sie es zum Laufen bringen.
Jari Komppa
Das Problem mit AMD_performance_monitor wäre, dass es nicht asynchron ist, so dass der Leistungsverlust auch vorhanden wäre, da ich zwischen den Frames warten muss, um zu wissen, dass die Aufrufe aus dem nächsten Frame keine Auswirkungen auf die Leistungsindikatoren haben.
Etan
Außerdem ist AMD_performance_monitor nicht verfügbar.
Etan

Antworten:

1

Ist es möglich, diese Erweiterung zu nutzen, um die Pixelanzahl zu ermitteln? Unterstützt die Hardware dies, sodass möglicherweise eine versteckte API vorhanden ist, um Zugriff auf die Pixelanzahl zu erhalten?

Nein und nein. Nun, ich nahm an, wenn Sie eine Reihe von Dreiecken mit einer Pixelgröße im Fensterraum zeichnen, können Sie zählen, wie viele Boolesche Werte Sie erhalten. Dies würde jedoch eine separate Abfrage für jedes einzelne Pixel erfordern . Wahrscheinlich nicht die schnellste Sache der Welt.

Wenn es eine "versteckte API" gibt, haben Sie keinen Zugriff darauf (da sie versteckt ist), es spielt also keine Rolle. Darüber hinaus deutet die Art der Erweiterung bereits darauf hin, dass dies nicht der Fall ist. Wenn die Hardware die tatsächliche Fragmentanzahl hatte, warum sollte sie dann nicht einfach direkt verfügbar gemacht werden, wie es Desktop OpenGL tut? Wenn die Hardware dies unterstützt hätte, hätten sie einfach ARB_occlusion_query nehmen und diese verwenden können.

Aber sie haben es nicht getan. Was stark darauf hindeutet, dass sie es nicht konnten.

Nicol Bolas
quelle
Danke für die Antwort! Der Ansatz mit Dreiecken mit einer Pixelgröße kann durch einfaches Rendern von Punkten oder kleinen Linien verbessert werden, und er kann sogar durch eine logarithmische Skalierung verbessert werden, bei der eine Linie, die "true" zurückgibt, einfach in der Mitte geteilt und dann beide Teile erneut überprüft werden. Ich bin mir über die Leistung noch nicht sicher, da die Modelle nicht mehr als 100-200 Eckpunkte haben. Die Begründung für das Nichtvorhandensein der "versteckten API" ist ebenfalls gültig. Gibt es andere Möglichkeiten, um Daten von der GPU asynchron ohne Pipeline-Stillstand zur CPU zurückzusenden (bitte auch auf hackige Weise)?
Etan
0

Die Ergebnisse werden in einem GLuint zurückgegeben (kann auch GLint für einen anderen Aufruf sein). Leider ist das Ergebnis immer eine 0 oder eine 1, möglicherweise wird es in Zukunft geändert, um Frags zu registrieren.

Es sieht auch so aus, als hätte keine einzige Person im gesamten Internet über diese neuen Erweiterungen geschrieben ... also sind sie hier richtig eingerichtet ... was, soweit ich das beurteilen kann, nirgendwo dokumentiert ist ... Sie können sich vorstellen, wie sie sind würde in Ihren Code von diesem kleinen Sudo-Code hier gehen:

import UIKit/UIKit.h

import GLKit/GLKit.h

import "GLProgram.h"

GLuint testBox,hasBeenTested,theParams;

//...

glGenQueriesEXT(1, &testBox);

glBeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, testBox);


//... draw an object .......

glEndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT);

glGetQueryObjectuivEXT(testBox, GL_QUERY_RESULT_AVAILABLE_EXT, &hasBeenTested);

if (hasBeenTested) glGetQueryObjectuivEXT(testBox, GL_QUERY_RESULT_EXT, &theParams);

glDeleteQueriesEXT(1, &testBox);

if (!theParams)  object is hidden;  don't draw next time;
honjj
quelle
Nur weil der Typ sagt, GLintbedeutet dies nicht, dass es sich um eine Ganzzahl der Anzahl der übergebenen Fragmente handelt. Die Spezifikation ist ziemlich klar, dass der QUERY_RESULT_EXTZustand ein boolescher Wert ist; Sie fragen es einfach als Ganzzahl ab. Es ist, GL_FALSEwenn es fehlgeschlagen ist und nicht, GL_FALSEwenn es bestanden hat.
Nicol Bolas
Ich hatte gerade den Code implementiert und festgestellt, dass er nur wahr oder falsch ist, und meine Antwort geändert, bevor ich Ihren Kommentar sah ... da es sich um eine Gluint handelt, werden sie hoffentlich in Zukunft Zustände hinzufügen.
Honjj
Übrigens scheint es einen Fehler in GKKit zu geben, wenn Sie self.effect2 = [[GLKBaseEffect alloc] init] verwenden; Typ des GLKit-Codetyps zum Rendern eines Objekts anstelle einer normalen GLES2.0-Pipeline. Die Abfragen geben Ihnen nicht die richtige Antwort ... (nie ausgeblendet), obwohl ich ein Pipeline-Objekt getestet habe, das ein GLKBaseEffect-Objekt versteckt, also könnte es einen Fehler geben, der nur die beiden mischt, ich habe noch nicht weiter getestet ...
honjj
Die Erweiterungen werden anhand von Beispielen in den WWDC-Videos erläutert, die auf der Apples-Website mit einem aktiven Entwicklerkonto zu finden sind.
Etan