[osg-users] Changing DrawBuffer for FBO

144 views
Skip to first unread message

Emmanuel Roche

unread,
Sep 22, 2011, 8:28:43 AM9/22/11
to OSG Users
Hi everyone,

I have a question regarding FBO usage and Draw/Read buffer changes:

- I have one pre render camera using FBO and with 2 textures attached (to COLOR_BUFFER0 and COLOR_BUFFER1)
- Under that camera I would like to add multiple screen aligned quads that would use the attached textures this way: (call them tex0 and tex1):
  - quad0 would using the tex0 as input and draw on tex1
  - quad1 would then use tex1 as input and draw on tex0
  - quad2 would using the tex0 as input and draw on tex1
  - quad3 would then use tex1 as input and draw on tex0
  - etc.

This is possible in pure OpenGL using something like that:

    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fftFbo2);
    glUseProgram(fftx->program);
    glUniform1i(glGetUniformLocation(fftx->program, "nLayers"), choppy ? 5 : 3);
    for (int i = 0; i < PASSES; ++i) {
        glUniform1f(glGetUniformLocation(fftx->program, "pass"), float(i + 0.5) / PASSES);
        if (i%2 == 0) {
            glUniform1i(glGetUniformLocation(fftx->program, "imgSampler"), FFT_A_UNIT);
            glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
        } else {
            glUniform1i(glGetUniformLocation(fftx->program, "imgSampler"), FFT_B_UNIT);
            glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
        }
        drawQuad();
    }
    glUseProgram(ffty->program);
    glUniform1i(glGetUniformLocation(ffty->program, "nLayers"), choppy ? 5 : 3);
    for (int i = PASSES; i < 2 * PASSES; ++i) {
        glUniform1f(glGetUniformLocation(ffty->program, "pass"), float(i - PASSES + 0.5) / PASSES);
        if (i%2 == 0) {
            glUniform1i(glGetUniformLocation(ffty->program, "imgSampler"), FFT_A_UNIT);
            glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
        } else {
            glUniform1i(glGetUniformLocation(ffty->program, "imgSampler"), FFT_B_UNIT);
            glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
        }
        drawQuad();
    }

    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);


.. but I can't figure out how to do something equivalent to the internal calls to glDrawBuffer in the previous snippet when I have a single camera. (as calling setDrawBuffer() is done once and for all before rendering anything... Any idea what could be worth trying here ?

Cheers,
Manu.

Sergey Polischuk

unread,
Sep 23, 2011, 4:42:26 AM9/23/11
to OpenSceneGraph Users
Hi, Manu
 
There are no convenient support for renderbuffer ping-ponging. You can either use pure gl, graph with lots of cameras setup with correct render order and output textures, or osgppu graph with chain of units
 
Cheers,
Sergey.
22.09.2011, 16:28, "Emmanuel Roche" <roche.e...@gmail.com>:

_______________________________________________
osg-users mailing list
osg-...@lists.openscenegraph.org
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

Emmanuel Roche

unread,
Sep 23, 2011, 7:50:01 AM9/23/11
to OpenSceneGraph Users
Thanks Sergey,

Right now I'm using multiple cameras rendered one after another... but I have the feeling the performances are not too good (with 17 pre render cameras...). Do you think I'm right to assume I could  really improve the performances by using pure GL code instead  and create only one FBO ?

as a matter of fact, my second idea would be to encapsulate all this using approximately the code snippet of the previous mail in a special drawable. But i'm wondering if this will not lead to other issues (I'm not that familiar with pure GL code and creating special drawables):  is it possible to allocate by FBO  and everything I could need from within the renderImplementation of a drawable ? Could somebody predict a big issue doing this ?

Cheers,
Manu.


2011/9/23 Sergey Polischuk <pol...@yandex.ru>

Sergey Polischuk

unread,
Sep 23, 2011, 9:02:53 AM9/23/11
to OpenSceneGraph Users
Hi, Manu
 
Cant comment on performance gain with pure gl, but you can write small test to see possible gain. Osg dont share fbos with cameras and dont keep track of vbo's binded so most of gain would be from reducing number of glBindFramebuffer...(..) calls and geometry setup (vertex\index\texcoord arrays\pointers).
 
It is possible to create whatever objects you need inside renderImplementation, also you need to info osg::State about all gl state that osg::State keep track of, that you change with opengl calls (look at osg::State::haveApplied...(...) ), or use osg::State functionality instead of opengl calls (look at applyMode(..)/applyAttribute(..)), though not all gl calls can be done through osg::State interface. Instead you can use pure gl and call osg::State::dirtyAll...() afterwards. Dont forget to restore fbo used by osg if you change fbo binding inside drawImplementation(). Also there could be issues with multiple opengl contexts handling if you use multiple screens, so you may need to track current contextID and create\store opengl handles for each contextID used, and pick right handles for current contextID.
 
Also with pure gl approach you can get rid of fbo and drawBuffers switch if you can use NV_texture_barrier extension - you can create texture array with two textures, reading from one layer and writing to another based on uniform value or instanceID if you use instanced drawing for sqreen aligned quads.
 
Cheers,
Sergey.
23.09.2011, 15:50, "Emmanuel Roche" <roche.e...@gmail.com>:

Emmanuel Roche

unread,
Sep 25, 2011, 6:06:52 PM9/25/11
to OpenSceneGraph Users
Hi Sergey and everyone,

I've been trying to setup a "ping pong rendering" with multiple cameras as previously mentioned... unfortunately this is not really working :-) : I guess I just can't render to one texture / use it as input / re-render again as easily as I expected.

So now I have to switch to another option. I have some experience with osgPPU already, but I only used it in a "regular" wait (with Texture2D, rendering  a couple of gauss passes in X and Y...) : here the situation is much more tricky: I have to ping pong between texture2DArrays with Texture3D samplers and with much more passes : it might be possible with osgPPU, but I have the feeling I could spend a lot of time to get everything properly set up first... (I already spent a log of time trying to setup something with regular OSG cameras, so I would like to use that work if possible).

So my idea is:  how about if I create a simple new StateAttribute class to call glDrawBuffer() ?? This stateAttribute would be as simple as the AlphaFunc StateAttribute for instance...: Would this make sense ? Or do you think I would be waiting a few additional hours here ? :-)

Then the plan would be to build a graph such as:

  Camera (PRE-Render, attachment color0=first Texture2DArray, attachment color1=second texture2D Array, rendering shader program)
     | -- Screen Quad 0 with stateset ( stateattribute glDrawBuffer=GL_COLOR_ATTACHMENT1_EXT)
     | -- Screen Quad 1 with stateset ( stateattribute glDrawBuffer=GL_COLOR_ATTACHMENT0_EXT)
     | -- Screen Quad 2 with stateset ( stateattribute glDrawBuffer=GL_COLOR_ATTACHMENT1_EXT)
     | -- etc

... I might have the ensure the quads are rendered in that exact order, but I think a simple bin number settings should be enough to do that no ?


If this is really working as expected, this would also mean I would not have to use more that one camera to perform all the needed passes... So I really think this is the next option I should try, except is someone already knows why this won't work :-)

Emmanuel Roche

unread,
Sep 26, 2011, 8:36:07 AM9/26/11
to OpenSceneGraph Users
Hmmm... okay.. some additional info on this topic:

- I tried with a new StateAttribute "osg::DrawBuffer", but this doen'st work :-( the situation is:

I have the top pre_render camera, I attach both texture2dArrays on slot 1 and slot 2 (slot 0 is already used by a needed sampler, also, those too textures are attached on COLOR_BUFFER0 and COLOR_BUFFER1); then I add child quads with the osg::DrawBuffer state attribute properly configured, and an uniform properly configured to access the "source texture" (as opposed to the texture being rendered on that quad, which is supposed to be determined by the DrawBuffer state attribute...)

=> If I perform a "single pass" (rendering texture B from texture A) then both texture A and B are properly rendered.

=> As soon as I try to render two passes (rendering texture B from texture A then rendering texture A from Texture B) then texture B is still OK but texture A is completely black :-(

- So I assumed the osg::DrawBuffer" stateAttribute was not working as expected (but here I really don't understand why...) and started creating a special PingPongDrawable.

The idea with this ping pong drawable is to place is just under the camera previously defined (so I assume the FBO is applied, the textures are applied, the shader program is applied and the color buffers are attached: I attached the source of that "PingPongDrawable if someone wants to have a look... it's main part is to render all the passes in one cycle just as in the pure OpenGL code snipped I'm using as template:

So the main part of the code is:

            // just call the OpenGL code.
            for (int i = 0; i < _numPasses; ++i) { //
                extensions->glUniform1f(passLoc, float(i + 0.5) / _numPasses);
                if (i%2 == 0) {
                    extensions->glUniform1i(imgLoc, 1); // use the first texture.
                    glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
                } else {
                    extensions->glUniform1i(imgLoc, 2); // Use the second texture.
                    glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
                }
                drawQuad();
            }

... but here again... everything is fine with a single pass but the texture A becomes completely black as soon as I try with 2 passes...

I'm really starting to feel desperated so if somebody as a clue why my first texture2D array gets cleared /destroyed/ sent into another dimension ? that would be very helpful.

Buy the way, here is how the FBO camera is setup if you want to know that (this is some lua code, but I guess everyone can understand the osg calls):

    local PASSES = options.PASSES or 8;
    local FFT_SIZE = options.FFT_SIZE or 256
   
    -- Create a single camera holding the attachments:
    local cam = osg.Camera();
    cam:setRenderOrder(osg.Camera.RenderOrder.PRE_RENDER,11);
    cam:setClearColor(osg.Vec4(1.0,0.0,0.0,1.0));
    --cam:setClearMask(GL.Mode.GL_COLOR_BUFFER_BIT + GL.Mode.GL_DEPTH_BUFFER_BIT)
    cam:setClearMask(0)
    cam:setViewport(osg.Viewport(0,0,FFT_SIZE,FFT_SIZE))
    --cam:setReferenceFrame(osg.Transform.ReferenceFrame.ABSOLUTE_RF);
    --cam:setGraphicsContext(context);
    cam:setRenderTargetImplementation(osg.Camera.RenderTargetImplementation.FRAME_BUFFER_OBJECT)
   
    -- attach the buffers to the init camera:
    --cam:setReadBuffer(GLValues.GL_COLOR_ATTACHMENT0_EXT);
    local ss = cam:getOrCreateStateSet()
   
    ss:setMode(GL.Mode.GL_BLEND,osg.StateAttribute.Values.OFF+osg.StateAttribute.Values.OVERRIDE+osg.StateAttribute.Values.PROTECTED)
    ss:setMode(GL.Mode.GL_DEPTH_TEST,osg.StateAttribute.Values.OFF+osg.StateAttribute.Values.OVERRIDE+osg.StateAttribute.Values.PROTECTED)

    cam:attach(osg.Camera.BufferComponent.COLOR_BUFFER0,result.ffta,0) --,0,true); -- last true here means: generate mipmaps (is it needed ??)
    cam:attach(osg.Camera.BufferComponent.COLOR_BUFFER1,result.fftb,0) --,0,true); -- last true here means: generate mipmaps (is it needed ??)
 
    ss:setTextureAttribute(1,result.ffta,osg.StateAttribute.Values.ON);
    ss:setTextureAttribute(2,result.fftb,osg.StateAttribute.Values.ON);


    local ss = cam:getOrCreateStateSet();
    cam:setReadBuffer(GLValues.GL_COLOR_ATTACHMENT0_EXT)
    cam:setDrawBuffer(GLValues.GL_COLOR_ATTACHMENT0_EXT)
   
    local proj = giProject.ProjectManager.getProject("ocean2");
    assignTexture{ss=ss,name="butterfly",project=proj,slot=0}
    release(proj)

    --- Save uniform here:
    giScene.SceneTools.setIntUniform(ss:getOrCreateUniform("nLayers",osg.Uniform.Type.INT), options.choppy and 5 or 3)   
   
    local fftx_passes = osg.Group()
    cam:addChild(fftx_passes)
       
    local ss = fftx_passes:getOrCreateStateSet()
   
    -- Add the program:
    local proj = giProject.ProjectManager.getProject("ocean2");
    local prog = createProgram(proj,"fftx")
    ss:setAttributeAndModes(prog)
    release(proj)
   
    -- Add passes:
    local pass = setupFFTPass(options,result,prog,8)  -- this is where the "pingPongDrawable" is created and added (taking the program and numPasses as arguments)
    fftx_passes:addChild(pass)

Thanks for your help guys!

cheers,
Manu.



2011/9/26 Emmanuel Roche <roche.e...@gmail.com>
PingPongDrawable.h
Reply all
Reply to author
Forward
0 new messages