Search code examples
c++qtopenglqt5pbo

How to use QOpenGLBuffer:: PixelUnpackBuffer


I can't figure it out how to properly write and read from a QOpenGLBuffer:: PixelUnpackBuffer.

  1. What is the proper setup before writing into a PBO?
  2. QOpenGLBuffer::write will not work using with a simple QImage.bits(), or glReadPixels() to pass the FBO render into the PBO. It has to be a specific type of data?
  3. How do you use a written PBO with Texture::setData()? A simple Texture.setData(*format*, *pixel_type*, pubo, nullptr) will suffice?

Here some code to exemplify what I'm doing:

QImage ScribbleArea::proImg(const QImage& image,
                         const QImage& tip,
                         const QString& vertexPosVar,
                         const QString& textureCoordVar){

    QOpenGLContext context;

    QOffscreenSurface offSurface;
    offSurface.setFormat(context.format());
    offSurface.create();

    // I'm sharing the context to show the offscreen Render into a
    // QOpenGLTextureBlitter under PaintGL()
    context.setShareContext(ScribbleArea::context()); 
    context.create();
    context.makeCurrent(&offSurface);

    m_fbo = new QOpenGLFramebufferObject(image.size());
    m_fbo->bind();

    context.functions()->glViewport(0, 0, image.width(), image.height());

    QOpenGLShaderProgram program(&context);
    program.addShaderFromSourceFile(QOpenGLShader::Vertex, "path to vertex shader");
    program.addShaderFromSourceFile(QOpenGLShader::Fragment, "path to fragment shader");
    program.link();
    program.bind();

    // The next block is basically what I understood how to setup a PBO using
    // Qt's OpenGL wrapper.
    QOpenGLBuffer *pubo = new QOpenGLBuffer(QOpenGLBuffer::PixelUnpackBuffer);
    pubo->setUsagePattern(QOpenGLBuffer::DynamicCopy);
    pubo->create();
    pubo->bind();
    pubo->map(QOpenGLBuffer::ReadWrite);
    pubo->allocate(image.bits(),image.sizeInBytes());
    pubo->write(0,image.bits(),image.sizeInBytes());
    pubo->unmap();
    pubo->release();

    // Testing how to use texture::setData() using straight bytes instead of the
    // baked method of setData(QImage).
    // I believe this is how to use the PBO's content to set the texture using QOpenGL.
    QOpenGLTexture textu(QOpenGLTexture::Target2D);
    textu.setSize(image.width(),image.height());
    textu.setFormat(QOpenGLTexture::RGBA8_UNorm);
    textu.allocateStorage();
    textu.setData(QOpenGLTexture::BGRA,QOpenGLTexture::UInt8,image.bits(),nullptr);

    // The texture bellow is a test to see if I was able to set up two textures and
    // change which one the shader should use.
    QOpenGLTexture brush(QOpenGLTexture::Target2D);
    brush.setData(tip);


    // Using native OpenGL snippets never work. The texture remain black.
//    GLuint tex;
//    glGenTextures(1, &tex);
//    glBindTexture(GL_TEXTURE_2D, tex);

//        glTexImage2D(GL_TEXTURE_2D, 0, 3, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.bits());
//        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

//    glBindTexture( GL_TEXTURE_2D, 0);

    GLint brTip = 1;
    brush.bind(brTip);
    GLint oriTxt = 0;
    textu.bind(oriTxt);

//Rest of the code. Following Amin Ahmadi tutorial.

Solution

  • You have some misunderstandings, and most of these are not related to Qt's abstraction classes, but to how these objects work in the GL itself:

    // The next block is basically what I understood how to setup a PBO using
    // Qt's OpenGL wrapper.
    QOpenGLBuffer *pubo = new QOpenGLBuffer(QOpenGLBuffer::PixelUnpackBuffer);
    pubo->setUsagePattern(QOpenGLBuffer::DynamicCopy);
    pubo->create();
    pubo->bind();
    

    So far, so good...

    pubo->map(QOpenGLBuffer::ReadWrite);
    

    Now you try to map the buffer to memory. THis can't work, because the buffer object does not have any storage allocated to it, so this call should fail with a GL error. Furthermore, map will return the memory address to where the memory would have been mapped, which you never care about here, so even if the mapping would work, you could not use it. Even worse, while a buffer is mapped, you must not do any GL operations with it (unless you create a persistent mapping).

    pubo->allocate(image.bits(),image.sizeInBytes());
    

    OK, now you create some storage. But only because the mapping operation failed earlier.

    pubo->write(0,image.bits(),image.sizeInBytes());
    

    This also should work. Also only because you don't have a memory mapping active here.

    pubo->unmap();
    

    This will fail because the buffer isn't mapped. But doesn't matter. You never needed nor intended to use the buffer mapping anyway.

    1. What is the proper setup before writing into a PBO?

    Create the buffer object, and allocate (enough) storage for it.

    1. QOpenGLBuffer::write will not work using with a simple QImage.bits(), or glReadPixels() to pass the FBO render into the PBO. It has to be a specific type of data?

    No idea what you're talking about. Writing the data from an QImage to it will work. What you mean by glReadPixels here is unclear. If you want to read pixels into a buffer object, you need to bind it as GL_PIXEL_PACK_BUFFER, not _UNPACK_BUFFER.

    Why do you say "it doesn't work"?

    1. How do you use a written PBO with Texture::setData()? A simple Texture.setData(*format*, *pixel_type*, pubo, nullptr)

    The is no overload of QOpenGLTexture::setData() that I'm aware of (i.e. can find in the docs) which would ever take any QtOpenGLBuffer parameter.

    In OpenGL, when a GL_PIXEL_UNPACK_BUFFER is bound, the data pointer of glTexImage...() functions will be treated as byte offset into that buffer object. Hence, you just need to bind your pobo, and call setData with the proper values, but you never pass pobo as a parameter to setData obviously.

    Btw., those Qt OpenGL abstractions are creating more issues than they are solving (for example, QtOpenGLBuffer abstraction makes it seem like GL buffer objects were typed, which is completely wrong - the same buffer object can be bound to multiple binding targets simultaneously). However, no matter what your opinion on those abstractions is, they in no way spare you from learning how the actual underlying GL objects are working - actually, with them you have to learn both the GL and Qt's sometimes quite arbitrary semantics of the abstractions of those.