FBO with GLCanvas

classic Classic list List threaded Threaded
6 messages Options
Bob
Reply | Threaded
Open this post in threaded view
|

FBO with GLCanvas

Bob
Hello,

I am currently facing two problems using AWT GLCanvas and FBO:
- when I bind a FBO and call display on the GLCanvas, drawings are done in the FBO. But GLCanvas swaps buffers (GLDrawableImpl.swapBuffers) leading to a change in the screen: the old back buffer is put in front.
- when I try to take a screenshot with AWTGLReadBufferUtil.readPixelsToBufferedImage, the read buffer is changed to the default read buffer of the default framebuffer, not the currently bound FBO. This is done in GLReadBufferUtil.readPixelsImpl (l.227): gl2es3.glReadBuffer(gl2es3.getDefaultReadBuffer());. There is an error (visible with DebugGL) because the read buffer should be GL_COLOR_ATTACHMENT0.

The goal is to take a screenshot of the screen with some customisation:
1. bind a FBO
2. perform screenshot specific customisation
3. perform default drawings
4. take screenshot
5. bind default framebuffer

I am using jogamp 2.3.2 on Windows with an Intel HD Graphics.

Is there something I do wrong ?

Thanks
Bob
Reply | Threaded
Open this post in threaded view
|

Re: FBO with GLCanvas

Bob
Here is the full code I used to make tests:

public class TestsCanvasFBO implements GLEventListener
{
    private static int fboId = -1;

    private static boolean doScreenshot = false;

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(
            () ->
            {
                GLProfile glp = GLProfile.getDefault();
                GLCapabilities caps = new GLCapabilities(glp);
                GLCanvas canvas = new GLCanvas(caps);
                canvas.addGLEventListener(new TestsCanvasFBO());

                Button screenshotButton = new Button("Screenshot");
                screenshotButton.addActionListener(e ->
                                                   {
                                                       doScreenshot = true;
                                                       canvas.display();
                                                   });

                Frame frame = new Frame("FBO Test");
                frame.setSize(1000, 1000);
                frame.add(canvas, BorderLayout.CENTER);
                frame.add(screenshotButton, BorderLayout.PAGE_END);
                frame.setVisible(true);
            });
    }

    @Override
    public void init(GLAutoDrawable drawable)
    {
        //        drawable.setGL(new DebugGL2(drawable.getGL().getGL2()));
        drawable.getGL().glClearColor(0, 0, 0, 1);
        initialiseFBO(drawable.getGL(), drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
    }

    private static void initialiseFBO(GL gl, int width, int height)
    {
        // Framebuffer creation
        // ----------------------
        int[] ids = new int[1];
        gl.glGenFramebuffers(1, ids, 0);
        fboId = ids[0];

        gl.glGenRenderbuffers(1, ids, 0);
        int rbId = ids[0];

        gl.glBindFramebuffer(GL_FRAMEBUFFER, fboId);
        gl.glBindRenderbuffer(GL_RENDERBUFFER, rbId);
        gl.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);

        // Texture creation
        // ------------------
        gl.glGenTextures(1, ids, 0);
        int tId = ids[0];
        gl.glBindTexture(GL_TEXTURE_2D, tId);
        gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);

        // Attach texture and render buffer to FBO
        // ----------------------------------------
        gl.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tId, 0);
        gl.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbId);
        gl.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbId);

        gl.glBindTexture(GL_TEXTURE_2D, 0);
        gl.glBindFramebuffer(GL_FRAMEBUFFER, 0);
    }

    @Override
    public void display(GLAutoDrawable drawable)
    {
        GL2 gl = drawable.getGL().getGL2();

        if (doScreenshot)
        {
            gl.glBindFramebuffer(GL_FRAMEBUFFER, fboId);
            gl.glClearColor(1, 1, 1, 1);
        }
        else
        {
            gl.glClearColor(0, 0, 0, 1);
        }

        gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        gl.glColor3f(1, 0, 0);
        gl.glBegin(GL2.GL_POLYGON);
        {
            gl.glVertex2f(0.5f, 0.5f);
            gl.glVertex2f(-0.5f, 0.5f);
            gl.glVertex2f(-0.5f, -0.5f);
            gl.glVertex2f(0.5f, -0.5f);
            gl.glVertex2f(0f, 0f);
        }
        gl.glEnd();

        if (doScreenshot)
        {
            gl.glFinish();

            capture(gl);

            gl.glBindFramebuffer(GL_FRAMEBUFFER, 0);
            doScreenshot = false;
        }
    }

    private static void capture(GL gl)
    {
        // Create image
        BufferedImage bi = new AWTGLReadBufferUtil(gl.getGLProfile(), false).readPixelsToBufferedImage(gl, true);
        try
        {
            ImageIO.write(bi, "PNG", new File("./capture.png"));
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    @Override
    public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h)
    {
    }

    @Override
    public void dispose(GLAutoDrawable drawable)
    {
        deleteFBO(drawable);
    }

    private void deleteFBO(GLAutoDrawable drawable)
    {
        GL gl = drawable.getGL();

        // Dispose resources
        int[] ids = new int[1];
        // Texture
        gl.glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, ids, 0);
        gl.glDeleteTextures(1, ids, 0);

        // Render buffer
        gl.glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, ids, 0);
        gl.glBindRenderbuffer(GL_RENDERBUFFER, 0);
        gl.glDeleteRenderbuffers(1, ids, 0);

        // Frame buffer
        gl.glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ids, 0);
        gl.glBindFramebuffer(GL_FRAMEBUFFER, 0);
        gl.glDeleteFramebuffers(1, ids, 0);
    }
}
Reply | Threaded
Open this post in threaded view
|

Re: FBO with GLCanvas

gouessej
Administrator
In reply to this post by Bob
Hello

You can disable the automatic swap buffer mode by calling GLCanvas.setAutoSwapBufferMode(false).
Julien Gouesse | Personal blog | Website
Bob
Reply | Threaded
Open this post in threaded view
|

Re: FBO with GLCanvas

Bob
Hello Julien,

Thank you for this quick answer.

I added setAutoSwapBufferMode(false) and setAutoSwapBufferMode(true) between calling display() to draw the screenshot.
This solved my first problem.

Do you have an idea for the second problem ?

I currently see two options:
- do not take into account the error. glReadBuffer ends with an error because getDefaultReadBuffer() is GL_BACK but when a FBO is bound, only GL_COLOR_ATTACHMENTi is allowed. However at this point, glGet(GL_READ_BUFFER) is GL_COLOR_ATTACHMENT0 so the following glReadPixels reads the correct buffer.
  It is not really clean but this works.
- do not use AWTGLReadBufferUtil to create the BufferedImage. But I will have to copy most of the code to change only the line with glReadBuffer.

Thanks again
Reply | Threaded
Open this post in threaded view
|

Re: FBO with GLCanvas

Sven Gothel
Administrator
I was just glancing over this post
and wanted to add some thoughts here.

On 11/28/19 12:47 PM, Bob [via jogamp] wrote:
> - do not take into account the error. glReadBuffer ends with an error because
> getDefaultReadBuffer() is GL_BACK but when a FBO is bound, only
> GL_COLOR_ATTACHMENTi is allowed. However at this point, glGet(GL_READ_BUFFER)
> is GL_COLOR_ATTACHMENT0 so the following glReadPixels reads the correct buffer.
>   It is not really clean but this works.

That is the normal expected behavior,
using the proper read buffer.

For example GLFBODrawable's implementation
overrides GLDrawable methods used in GLContext (and GL)
to retrieve the proper values of the buffers - see below.

This is part of our 'managed code' convenience
to have same behavior exposed whether you use onscreen
or offscreen GLDrawable instances.

<<-------------
abstract int getDefaultDrawFramebuffer()
Return the default draw framebuffer name.

abstract int getDefaultPixelDataFormat()
Get the default pixel data format, as required by e.g.

abstract int getDefaultPixelDataType()
Get the default pixel data type, as required by e.g.

abstract int getDefaultReadBuffer()
Returns the default color buffer within the current bound
getDefaultReadFramebuffer(), i.e.

abstract int getDefaultReadFramebuffer()
Return the default read framebuffer name.
----------------->>>

GLFBODrawable is the offscreen case using
default construction of drawable/context and operating
with our GLEventListener model, like via GLWindow.

It then just uses FBObject's buffer name getter
to get the right read/write buffer names:
<<<-------
    /** Returns the framebuffer name to render to. */
    public final int getWriteFramebuffer() { return fbName; }
    /** Returns the framebuffer name to read from. Depending on multisampling,
this may be a different framebuffer. */
    public final int getReadFramebuffer() {
        return 0 < samples ? ( null != samplingSink ?
samplingSink.getReadFramebuffer() : 0 ) : fbName;
    }
    public final int getDefaultReadBuffer() { return GL.GL_COLOR_ATTACHMENT0; }
-------------->>>>

and so on and so forth.

Cheers, Sven
Bob
Reply | Threaded
Open this post in threaded view
|

Re: FBO with GLCanvas

Bob
Hello,

Yes, there is no problem with getDefaultReadBuffer() which returns the correct default read buffer of the drawable (GL_BACK or GL_FRONT for GLDrawableImpl, GL_COLOR_ATTACHMENTi for GLFBODrawableImpl).
Sorry if I was not clear.

There is an error in my case because I bind an FBO in my GLCanvas to draw offscreen for one display.
So I have a GLCanvas containing the default WindowsOnscreenWGLDrawable but with an FBO as read and write buffer.
That is why glReadBuffer(getDefaultReadBuffer=GL_BACK) ends in error.

Is it possible to bind an FBO with glBindFramebuffer when the drawable is not a GLFBODrawable ?
Should I use another drawable (GLFBODrawable) ?
Maybe AWTGLReadBufferUtil/GLReadBufferUtil cannot be used when the current read buffer is different from the default read buffer ?


Beside that, I saw that the 2.4.0 release was almost here.
Congratulations and thanks for this good work.