Optimierung von Texture-Switches im FBO



  • Hallo,

    Ich muss für eine Berechnung wahnsinnig oft Shader-Programme aufrufen. Dazu habe ich einen FBO, in den ich das Ergebnis jeweils rendere. Dabei muss der Shader mehrmals aufgerufen werden. Ich habe zwei Texturen, die ich ständig wechsle.

    Beide Texturen haben die gleiche Grösse und das gleiche Format, nämlich GL_RGBA16UI . Es handelt sich also um Integer-Texturen.

    GLuint sourceTexture = ...;
    GLuint destTexture = ...;
    GLuint fbo = ...;
    GLuint shaderProgram = ...;
    
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
    glUseProgramObjectARB(shaderProgram);
    
    for (/* mehrere Durchläufe */)
    {
       glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, destTexture, 0); // <-
       glBindTexture(GL_TEXTURE_2D, sourceTexture);
    
       glClear(GL_COLOR_BUFFER_BIT) );
       glBegin(GL_QUADS);
       ...
       glEnd();
    
       std::swap(sourceTexture, destTexture);
    }
    

    Ein Profiling hat ergeben, dass die meiste Zeit im Aufruf glFramebufferTexture2DEXT() verbraten wird. Warum ist der Textur-Wechsel so teuer? Wie könnte man hier optimieren?


  • Mod

    Pixie schrieb:

    Ein Profiling hat ergeben, dass die meiste Zeit im Aufruf glFramebufferTexture2DEXT() verbraten wird. Warum ist der Textur-Wechsel so teuer? Wie könnte man hier optimieren?

    weil dann vermutlich alle aufgestauten commandos abgearbeitet werden. versuch mal ein glFinish davor, ob das finish teuer ist und dafuer das framebuffer attachen billig.



  • Ah, in der Tat! Danke für den Tipp.

    Das selbe Problem hatte ich schon übrigens bei einem glReadPixels. Ich hatte mich damals auch gewundert, zumal ich in einen PBO geschrieben hatte. Ein glFinish davor hat dann ebenfalls die ganze Performance beansprucht.

    Das heisst in diesem Fall, das Rendern selbst braucht all die Zeit? Da kann man wahrscheinlich nicht viel optimieren, oder? Ich nehme an, für ein einzelnes Quadrat ist man mit Vertex Arrays oder VBOs auch nicht schneller als Immediate Mode...



  • Ich werde jedenfalls versuchen, die Anzahl der Shader-Aufrufe zu reduzieren, soweit das möglich ist. Weitere Ideen?



  • Pixie schrieb:

    Ein Profiling hat ergeben, dass die meiste Zeit im Aufruf glFramebufferTexture2DEXT() verbraten wird. Warum ist der Textur-Wechsel so teuer? Wie könnte man hier optimieren?

    Ich würde mal testen 2 Framebuffer jeweils mit einer der Texturen zu erstellen und diese zu wechseln, statt jeweils den Framebuffer zu modifizieren. Müsste man aber nachmessen.

    Wenn der Fragmentshader deine Flaschenhals ist, wird dir das allerdings auch nicht helfen. Kannst das ganze ja einfach mal mit leerem Fragmentshader testen der immer nur eine Konstante "Farbe" ausgibt und schauen wie es dann mit der Performance aussieht und ob die FBO's wirklich das Problem sind.



  • Vielen Dank für die Antworten!

    Habs soeben mit weniger Shader-Aufrufen (dafür komplexeren Shadern) getestet, war in den bisherigen Tests schneller. Bin mir auch nicht sicher, was genau vorher langsam war, aber glClear hat definitiv auch dazu beigetragen (trotz Scissor). Hat Rendern mit dem Immediate Mode einen grossen Overhead für ein einzelnes Rechteck?


  • Mod

    Pixie schrieb:

    Das heisst in diesem Fall, das Rendern selbst braucht all die Zeit?

    wer weiss

    Da kann man wahrscheinlich nicht viel optimieren, oder?

    um das zu beantworten fehlen ausreichend informationen. wir wissen nicht was du machst, somit nicht was das bottleneck ist, somit koennte jeder vorschlag hier was bringen aber auch einfach nur deine zeit verschwenden.

    Ich nehme an, für ein einzelnes Quadrat ist man mit Vertex Arrays oder VBOs auch nicht schneller als Immediate Mode...

    die chancen stehen gut dass du in diesem fall recht hast, dennoch besteht eine chance dass diese aufrufe das bottleneck sind, deswegen zeichnet niemand einzelne dreiecke. unter 100dreiecken ist oft der aufruf an sich das problem.


  • Mod

    Pixie schrieb:

    ... aber glClear hat definitiv auch dazu beigetragen (trotz Scissor).

    es kann sein dass scissors die sache langsam machen manchmal. ohne scissor kann die hardware einen 'fast clear' machen, sie setzt quasi nur ein bit pro tile vom framebufer dass dieser leer ist. wenn du einen scissor machst, muss sie eventuell wirklich den ganzen bereich leeren, weil die tiles nicht die exakte groesse deines scissors entsprechen.
    muss nicht sein, kann aber 😉

    Hat Rendern mit dem Immediate Mode einen grossen Overhead für ein einzelnes Rechteck?

    jup. viele spiele heutzutage sind oft drawcall bound. zu kleine 'arbeitspackete' kosten manchmal mehr setup zeit als sie am ende arbeitszeit benoetigen, somit ist die gpu idle, die cpu voll ausgelastet.

    aber das ist nicht generell zu sagen, wenn dein eines quad 1sekunde lang gerendert wird, weil du einen raytracing shader drauf hast, dann waere natuerlich der aufruf an sich total irrelevant.



  • rapso schrieb:

    die chancen stehen gut dass du in diesem fall recht hast, dennoch besteht eine chance dass diese aufrufe das bottleneck sind, deswegen zeichnet niemand einzelne dreiecke. unter 100dreiecken ist oft der aufruf an sich das problem.

    Ich kann leider nicht mehr als ein Quadrat aufs Mal zeichnen.

    Es handelt sich nicht um eine typsiche Grafikanwendung, ich führe Berechnungen mit OpenGL und GLSL durch. Das Quadrat wird auf eine Textur in einem FBO mittels Shader gerendert, und im Ping-Pong werden dann mehrere Durchgänge ausgeführt (wobei ich das soweit optimieren konnte, dass meist nur ein Durchgang nötig ist). Da jedes Quadrat grosse Teile der Textur benötigt, kann ich nicht gleichzeitig mehrere Quadrate zeichnen. Man könnte höchstens versuchen, sich nicht überlappende Quadrate gleichzeitig zu rendern, aber das würde in eine extreme Frickelei für möglicherweise wenig Gewinn ausarten.

    rapso schrieb:

    es kann sein dass scissors die sache langsam machen manchmal. ohne scissor kann die hardware einen 'fast clear' machen, sie setzt quasi nur ein bit pro tile vom framebufer dass dieser leer ist. wenn du einen scissor machst, muss sie eventuell wirklich den ganzen bereich leeren, weil die tiles nicht die exakte groesse deines scissors entsprechen.
    muss nicht sein, kann aber 😉

    Okay, danke für den Tipp!

    rapso schrieb:

    jup. viele spiele heutzutage sind oft drawcall bound. zu kleine 'arbeitspackete' kosten manchmal mehr setup zeit als sie am ende arbeitszeit benoetigen, somit ist die gpu idle, die cpu voll ausgelastet.

    Das könnte in meinem Fall auch sein. Dafür würde jedenfalls sprechen, dass weniger Shader-Aufrufe bei komplexeren Shadern schneller war, das Rendern selbst scheint also nicht das Bottleneck zu sein.

    Ich habe mich in OpenGL-Fences eingelesen und diese in meinem Programm eingesetzt. Ist zwar einiges schneller als glFinish(), aber ich verbrate trotzdem die meiste Zeit in glClientWaitSync(). Wenn ich das richtig interpretiere, scheint also die GPU voll ausgelastet zu sein. Falls ich keinen Weg finde, mehrere Objekte gleichzeitig zu rendern, kann ich diesbezüglich wohl nicht mehr viel optimieren.

    Momentan sieht das Profiling so aus:

    glClientWaitSync         39.2%
    glClear                  17.8%
    glFramebufferTexture2D    9.7%
    glBegin                   9.6%
    glDisable                 7.4%
    

    Ich schau mir mal glClear an. Scheinbar hat glBegin doch recht grossen Overhead, und erstaunlich scheint mir auch glDisable (für Scissor). Ich probiere mal die Schere wegzulassen.


  • Mod

    Wie wäre es mit opencl.opengl scheint ja nicht gerade optimal für deinen anwendungsfall zu wein.



  • Ja, dann beim nächsten Mal 🙂

    Bin eigentlich fast fertig. Ich wollte versuchen, wie fähig OpenGL hier ist, und dabei vieles lernen, was mir für die Spiele-/Grafikprogrammierung mal nützlich sein könnte.

    Jedenfalls vielen Dank für die Unterstützung soweit!


Anmelden zum Antworten