Pbuffer texture does not display

First, I want to thank all the posters and repliers who have helped me get this far. A apologize for the length of the post, but I want to provide as much information as possible. If this is the wrong place for the post, please identify the correct forum. Thanks.
I am writing code to run on an embedded device. (i.MX6 via Phytec SOC). The supported graphics recipes are for EGL and GLESv2. Using the many posts from this and similar forums, I now have a program that can change the cleared display to a solid color (albeit with an unwanted cursor blinking). However, when I try to follow instructions to display a bitmap via a texture, no display change occurs. I’m hoping for guidance.
My “display” is the HDMI cable, which runs to a projector. When I boot, 4 Linux penguins are displayed, followed by the login prompt (which appears on my console also). When I log in, no updates occur on the display. I run two program, the code for which is below.
The first uses a regular window surface and “clears” the display to coral pink:

#include <sys/time.h>
#include <stdlib.h>
#include <unistd.h>
#include <opencv/cv.h>
#include <opencv/highgui.h>

#include <GLES/egl.h>
#include <GLES2/gl2.h>
#include <EGL/eglplatform.h>
#include "mat4x4.hpp"

#include "CoretronicsProjector.h"
#include "DebugLogOutputThread.h"
#include "Log.h"

#include "Image.h"

//The name of the video interface to use on the target is card0-HDMI-A-1 (/sys/class/drm)

int retval = 0;

const char * printableError()
{
    EGLint error = eglGetError();
    if (error == EGL_SUCCESS) return "No error";
    if (error == EGL_BAD_DISPLAY) return "Bad display";
    if (error == EGL_NOT_INITIALIZED) return "Not initialized";
    if (error == EGL_BAD_CONFIG) return "Bad configuration";
    if (error == EGL_BAD_NATIVE_WINDOW) return "Bad native window";
    if (error == EGL_BAD_ATTRIBUTE) return "Bad attribute";
    if (error == EGL_BAD_ALLOC) return "Bad alloc";
    if (error == EGL_BAD_MATCH) return "Bad match";
    if (error == EGL_BAD_SURFACE) return "Bad surface";
    if (error == EGL_BAD_CONTEXT) return "Bad context";
    if (error == EGL_BAD_ACCESS) return "Bad access";
    if (error == EGL_BAD_NATIVE_PIXMAP) return "Bad native pixmap";
    if (error == EGL_BAD_NATIVE_WINDOW) return "Bad native window";
    if (error == EGL_BAD_CURRENT_SURFACE) return "Bad current surface";
    if (error == EGL_CONTEXT_LOST) return "Context lost";
    if (error == EGL_BAD_PARAMETER) return "Bad parameter";
    return "Something else";
}

cv::Mat GetCroppedMat(int originx, int originy, int width, int height)
{
    Image *testImage = new Image("/home/root/DuctWorkBlackWithText.png");
    cv::Mat *bitmap = testImage->GetImagePtr();
    std::cout << "Original bitmap = " << bitmap->cols << " x " << bitmap->rows << std::endl;

    cv::Rect myROI(originx, originy, width, height);
    return (*bitmap)(myROI);
}

int main(int argc, char *argv[])
{
    cv::Mat croppedImage = GetCroppedMat(0, 0, 1024, 768);

    //Setup to output to HDMI (default display)
    int fbnum = 0;
    EGLNativeDisplayType nativeDisplay = fbGetDisplayByIndex(fbnum);
    EGLNativeWindowType nativeWindow = fbCreateWindow(nativeDisplay, 0, 0, 0, 0);

    //Initialize display
    EGLint major;
    EGLint minor;
    EGLDisplay display = eglGetDisplay(nativeDisplay);
    if (display == EGL_NO_DISPLAY)
    {
        std::cout << "eglGetDisplay: " << printableError() << std::endl;
    }
    if (eglInitialize(display, &major, &minor))
    {
            std::cout  << "eglInitialize returned true"<<std::endl;
    }
    else
    {
        std::cout << "eglInitialize: " << printableError() << std::endl;
    }

    eglBindAPI(EGL_OPENGL_ES_API);
    std::cout << "eglBindAPI: " << printableError() << std::endl;

    //Configure the display
    EGLint num_config;

    EGLint configAttrib[] =
    {
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_NONE
    };
    EGLConfig config;
    eglChooseConfig(display, configAttrib, &config, 1, &num_config);
    std::cout << "eglChooseConfig: " << printableError() << std::endl;

    //Get the display context
    EGLint context_attrib[] =
    {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE
    };

    EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attrib);
    std::cout << "eglCreateContext: " << printableError() << std::endl;

    //Get the surface
    EGLSurface surface = eglCreateWindowSurface(display, config, nativeWindow, NULL);
    if (surface == EGL_NO_SURFACE)
    {
        std::cout << "eglCreatePbufferSurface: " << printableError() << std::endl;
    }

    //Make the surface current
    eglMakeCurrent(display, surface, surface, context);
    std::cout << "eglMakeCurrent: " << printableError() << std::endl;

    glClearColor((float)0xf8/0xff, (float)0x83/0xff, (float)0x79/0xff, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    eglSwapBuffers(display, surface);
    std::cout << "eglSwapBuffers: " << printableError() << std::endl;


    sleep(10);
    eglTerminate(display);

    return retval;
}

The output for this program is:
Original bitmap = 2560 x 1600
eglInitialize returned true
eglBindAPI: No error
eglChooseConfig: No error
eglCreateContext: No error
eglMakeCurrent: No error
eglSwapBuffers: No error

As I said, this program works.

The second is my attempt to display a bitmap via textures. It appears to do nothing, although it reports that all of the calls succeed:

#include <sys/time.h>
#include <stdlib.h>
#include <unistd.h>
#include <opencv/cv.h>
#include <opencv/highgui.h>

#include <GLES/egl.h>
#include <GLES2/gl2.h>
#include <EGL/eglplatform.h>
#include "mat4x4.hpp"

#include "CoretronicsProjector.h"
#include "DebugLogOutputThread.h"
#include "Log.h"

#include "Image.h"

//The name of the video interface to use on the target is card0-HDMI-A-1 (/sys/class/drm)

int retval = 0;

const char * printableError()
{
    EGLint error = eglGetError();
    if (error == EGL_SUCCESS) return "No error";
    if (error == EGL_BAD_DISPLAY) return "Bad display";
    if (error == EGL_NOT_INITIALIZED) return "Not initialized";
    if (error == EGL_BAD_CONFIG) return "Bad configuration";
    if (error == EGL_BAD_NATIVE_WINDOW) return "Bad native window";
    if (error == EGL_BAD_ATTRIBUTE) return "Bad attribute";
    if (error == EGL_BAD_ALLOC) return "Bad alloc";
    if (error == EGL_BAD_MATCH) return "Bad match";
    if (error == EGL_BAD_SURFACE) return "Bad surface";
    if (error == EGL_BAD_CONTEXT) return "Bad context";
    if (error == EGL_BAD_ACCESS) return "Bad access";
    if (error == EGL_BAD_NATIVE_PIXMAP) return "Bad native pixmap";
    if (error == EGL_BAD_NATIVE_WINDOW) return "Bad native window";
    if (error == EGL_BAD_CURRENT_SURFACE) return "Bad current surface";
    if (error == EGL_CONTEXT_LOST) return "Context lost";
    if (error == EGL_BAD_PARAMETER) return "Bad parameter";
    return "Something else";
}

cv::Mat GetCroppedMat(int originx, int originy, int width, int height)
{
    Image *testImage = new Image("/home/root/DuctWorkBlackWithText.png");
    cv::Mat *bitmap = testImage->GetImagePtr();
    std::cout << "Original bitmap = " << bitmap->cols << " x " << bitmap->rows << std::endl;

    cv::Rect myROI(originx, originy, width, height);
    return (*bitmap)(myROI);
}

int main(int argc, char *argv[])
{
    cv::Mat croppedImage = GetCroppedMat(0, 0, 1024, 768);

    //Setup to output to HDMI (default display)
    int fbnum = 0;
    EGLNativeDisplayType nativeDisplay = fbGetDisplayByIndex(fbnum);
    EGLNativeWindowType nativeWindow = fbCreateWindow(nativeDisplay, 0, 0, 0, 0);

    //Initialize display
    EGLint major;
    EGLint minor;
    EGLDisplay display = eglGetDisplay(nativeDisplay);
    std::cout << "eglGetDisplay: " << printableError() << std::endl;

    eglInitialize(display, &major, &minor);
    std::cout << "eglInitialize: " << printableError() << std::endl;

    eglBindAPI(EGL_OPENGL_ES_API);
    std::cout << "eglBindAPI: " << printableError() << std::endl;

    //Configure the display
    EGLint num_config;

    EGLint configPbufferAttrib[] =
    {
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_BIND_TO_TEXTURE_RGB, EGL_PBUFFER_BIT,
        EGL_NONE
    };
    EGLConfig config;
    eglChooseConfig(display, configPbufferAttrib, &config, 1, &num_config);
    std::cout << "eglChooseConfig: " << printableError() << std::endl;

    //Get the display context
    EGLint context_attrib[] =
    {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE
    };

    EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attrib);
    std::cout << "eglCreateContext: " << printableError() << std::endl;

    //Get the surface (Pbuffer)
    EGLint surfaceAttribs[] =
    {
        EGL_WIDTH, 1024,
        EGL_HEIGHT, 1024,
        EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB,
        EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
        EGL_NONE
    };

    EGLSurface surface = eglCreatePbufferSurface(display, config, surfaceAttribs);
    std::cout << "eglCreatePbufferSurface: " << printableError() << std::endl;

    //Make the surface current
    eglMakeCurrent(display, surface, surface, context);
    std::cout << "eglMakeCurrent: " << printableError() << std::endl;

    glClearColor((float)0x88/0xff, (float)0x83/0xff, (float)0x79/0xff, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    //TODO change from BGR to RGB

    //Make a texture and bind to it
    GLuint texture;
    glGenTextures(1, &texture);
    std::cout << "New texture = " <<  texture << std::endl;

    glBindTexture(GL_TEXTURE_2D, texture);
    if (glGetError() != GL_NO_ERROR)
    {
        std::cout << "glBindTexture generated error = " << (int)glGetError() << std::endl;
        retval = -1;
    }

    //Attach input data to texture?
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1024, 1024, 0, GL_RGB, GL_UNSIGNED_BYTE, croppedImage.data);
    if (glGetError() != GL_NO_ERROR)
    {
        std::cout << "glTexImage2D generated error = " << (int)glGetError() << std::endl;
        retval = -1;
    }

    GLsizei width = croppedImage.cols;
    GLsizei height = croppedImage.rows;
    std::cout << "Width/height = " << width << ", " << height << std::endl;

    //3rd and 4th parameters are offset into buffer. Use this for movement updates
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, croppedImage.data);
    if (glGetError() != GL_NO_ERROR)
    {
        std::cout << "glTexSubImage2D generated error = " << (int)glGetError() << std::endl;
        retval = -1;
    }

    //Set texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    if (glGetError() != GL_NO_ERROR)
    {
        std::cout << "glTexParameteri generated error = " << (int)glGetError() << std::endl;
        retval = -1;
    }
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    if (glGetError() != GL_NO_ERROR)
    {
        std::cout << "glTexParameteri generated error = " << (int)glGetError() << std::endl;
        retval = -1;
    }
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    if (glGetError() != GL_NO_ERROR)
    {
        std::cout << "glTexParameteri generated error = " << (int)glGetError() << std::endl;
    }
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    if (glGetError() != GL_NO_ERROR)
    {
        std::cout << "glTexParameteri generated error = " << (int)glGetError() << std::endl;
    }

    //Try to display
    eglBindTexImage(display, surface, EGL_BACK_BUFFER);
    std::cout << "eglBindTexImage: " << printableError() << std::endl;

    glEnable(GL_TEXTURE_2D);
    sleep(3);

    eglSwapBuffers(display, surface);
    std::cout << "eglSwapBuffers: " << printableError() << std::endl;

    sleep(10);
    eglTerminate(display);

    return retval;
}

The output for this program is
Original bitmap = 2560 x 1600
eglGetDisplay: No error
eglInitialize: No error
eglBindAPI: No error
eglChooseConfig: No error
eglCreateContext: No error
eglCreatePbufferSurface: No error
eglMakeCurrent: No error
New texture = 1
Width/height = 1024, 768
eglBindTexImage: No error
eglSwapBuffers: No error

This program runs to completion, but the display does not change. I assume I’m missing something, but I have no idea what.

Check the valid values of EGL_BIND_TO_TEXTURE_RGB here.

EGL_TRUE and EGL_PBUFFER_BIT both equal 1 in my implementation, I changed it but didn’t get different results.
I also added EGL_SURFACE_TYPE, EGL_PBUFFER_BIT to the attribute list. Still no change in behavior: if I see the login screen before running, I see it after running also.
New configPbufferAttrib:

   EGLint configPbufferAttrib[] =
    {
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE,
        EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
        EGL_NONE
    };

This seems to be more an EGL question than OpenGL. You might modify your 2nd example so that it builds with stock EGL/GLES. That way, at least folks with NVidia desktop GL drivers (which supports GLES) can try this and likely give you more feedback.

Your current example seems to require a number of things not present in standard EGL/GLES. If I hack it up so that it’ll compile and link, I find that eglChooseConfig always returns 0 configs here so long as EGL_BIND_TO_TEXTURE_RGB is specified, even if I add EGL_SURFACE_TYPE == EGL_PBUFFER_BIT. If I comment out EGL_BIND_TO_TEXTURE_RGB though, then I get a config.

Related:

I’m currently interacting with Phytec to see if the code to display a pbuffer/texture to the display works as I implemented it (on their hardware/build). However, if the technique is deprecated, then I’m curious how to display a bitmap. All examples I’ve found use this technique. Is there another technique that works? I’m shooting for rapid updates to the origin of the displayed image without reloading/rewriting the image.

PBuffers were used in “the old days” before Framebuffer Objects (FBOs) when you wanted to render to an off-screen framebuffer because you couldn’t render directly to a texture. FBOs provide that.

If you want to render to a texture, consider just using FBOs. This doesn’t require any interaction with EGL, just OpenGL ES. Then if you need to composite that onto the display, just render to an EGL window surface (either reading from the off-screen-rendered texture in a shader, or using glBlitFramebuffer).

Of course if you don’t really need to render to an off-screen pbuffer/texture, just render directly to the EGL window surface and forget about pbuffers and FBOs.

Using the tutorial at OpenGL - Drawing polygons, I am now able to display a white triangle.
This uses a vertex shader and fragment shader.
I’m hoping my next step is to convert the vertex shader into two triangles that cover the display and then to get the bitmap data into the fragment shader.

The entire fragment shader is
#version 100
void main()
{
gl_FragColor= vec4(1.0, 1.0, 1.0, 1.0);
}

I’m thinking that I can add an attribute to this that has the bitmap data and then set gl_FragColor based on that attribute.

That is what I’ll be trying next. (I’ll be continuing with the tutorial, hoping that the texture section gives me a clue how to push the bitmap data into the fragment shader.)

My final solution did not use Pbuffers and did use Shader Language. I’ll be continuing with efforts to get the code a little more flexible than it is, but I wanted to thank you for the support and help.