Trying to write simple EGL app with out Desptop running

Hello everyone

I just flushed my AGX Xavier with Jetpack 5.1.2. First thing I like to point out that after installing eglinfo (mesa-utils-extra) and running it I get core dump.

apex@agx-xavier:$ eglinfo 
EGL client extensions string:
    EGL_EXT_platform_base EGL_EXT_device_base EGL_EXT_device_enumeration
    EGL_EXT_device_query EGL_KHR_client_get_all_proc_addresses
    EGL_EXT_client_extensions EGL_KHR_debug EGL_KHR_platform_x11
    EGL_EXT_platform_x11 EGL_EXT_platform_device
    EGL_MESA_platform_surfaceless EGL_KHR_platform_gbm
    EGL_MESA_platform_gbm EGL_KHR_platform_wayland
    EGL_EXT_platform_wayland EGL_MESA_platform_xcb

Segmentation fault (core dumped)

Is anyone also getting core dump? There has been no system modifications since installation. Just installing some packages using apt.

Now to my main issue: I am trying to write a simple full screen app in C++.
Please note that I like to run without desktop running. For that reason, I did run:

sudo systemctl set-default multi-user
sudo reboot

Now that I am booted in level 3, I started writing a simple app to do double buffering that initializes EGL and displays background color red., just to keep it simple.

My user was added to video group and rebooted.

sudo usermod -a -G video $USER
sudo reboot

First thing I checked available devices in /dev folder. I see following:

crw-rw----  1 root       video    29,   0 Mar 27 12:54 fb0
crw-rw----  1 root       video    29,   1 Mar 27 12:54 fb1
crw-rw----  1 root       video    29,   2 Mar 27 12:54 fb2

Checking each /dev/fb0 device, I see the following:

cat /sys/class/graphics/fb0/mode
U:1920x1080p-60
cat /sys/class/graphics/fb0/name
tegra_fb
cat /sys/class/graphics/fb0/virtual_size
1920,1080

fb1 and fb2 in /sys/class/graphics/fbX/name shows tegra_fb.
cat /sys/class/graphics/fbX/mode for 1 and 2 is empty.
cat /sys/class/graphics/fbX/virtual_size for 1 and 2 is 4096,2160.

First I did create a simple FB app which is working without EGL. After compiling, executing it filled the screen with green color and finish without any errors.

#include <signal.h>
#include <iostream>
#include <vector>
#include <cstdio>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <sys/mman.h>

int main(int argc, char* argv[]) 
{
    if(argc < 2) {
        std::cout << "ERROR: Missing FB device" << std::endl;
        return -1;
    }

    std::cout << "Initializing " << argv[1] << " ..." << std::endl;

    int fb = open(argv[1], O_RDWR);
    
    if (fb == -1) {
        perror("Error opening framebuffer device");
        return 1;
    }

    struct fb_var_screeninfo vinfo;
    if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo)) {
        perror("Error reading variable screen info");
        close(fb);
        return 1;
    }

    int screensize = vinfo.yres_virtual * vinfo.xres_virtual * vinfo.bits_per_pixel / 8;
    char* framebuffer = (char*)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);

    if (framebuffer == MAP_FAILED) {
        perror("Error mapping framebuffer to memory");
        close(fb);
        return 1;
    }

    // Fill the screen with green
    for (int i = 0; i < screensize; i += 4) {
        framebuffer[i] = 0;        // Blue
        framebuffer[i + 1] = 255;  // Green
        framebuffer[i + 2] = 0;    // Red
        framebuffer[i + 3] = 0;    // Transparency (might not be used)
    }

    // Cleanup
    munmap(framebuffer, screensize);
    close(fb);

    return 0;
}

Next I did try to modify it and add EGL. Here is my test source.
g++ egl_sample.cpp -o egl_sample -lEGL -lGLESv2

#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <signal.h>
#include <iostream>
#include <vector>
#include <cstdio>
#include <fcntl.h>

EGLDisplay eglDisplay;
EGLContext eglContext;
EGLSurface eglSurface;
EGLConfig  eglConfig;
bool       keepRunning(true);

bool initEGL(const std::string& framebufferDevice);
void printInformation();

extern "C" {
    void    abSigHandler(int sig)
    {
        std::cout << "Signal: " << sig << " received" << std::endl;
        if(sig == SIGINT)
            keepRunning = false;
    }
}

void printInformation()
{
    std::cout << (const char*)glGetString(GL_VENDOR) << std::endl;
    std::cout << (const char*)glGetString(GL_RENDERER) << std::endl;
    std::cout << (const char*)glGetString(GL_VERSION) << std::endl;
}

bool initEGL() {
    // 1. Initialize EGL
    eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if (eglDisplay == EGL_NO_DISPLAY) {
        return false;
    }

    if (!eglInitialize(eglDisplay, nullptr, nullptr)) {
        return false;
    }

    // 2. Choose an appropriate EGL config
    EGLint numConfigs;
    EGLint configAttribs[] = {
        EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
        EGL_BLUE_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_RED_SIZE, 8,
        EGL_DEPTH_SIZE, 8,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_NONE
    };

    if (!eglChooseConfig(eglDisplay, configAttribs, &eglConfig, 1, &numConfigs)) {
        return false;
    }

    // 3. Create an EGL context
    EGLint contextAttribs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE
    };
    eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttribs);
    if (eglContext == EGL_NO_CONTEXT) {
        return false;
    }

    // 4. Create a PBuffer surface
    EGLint pbufferAttribs[] = {
        EGL_WIDTH, 640,
        EGL_HEIGHT, 480,
        EGL_NONE
    };
    eglSurface = eglCreatePbufferSurface(eglDisplay, eglConfig, pbufferAttribs);
    if (eglSurface == EGL_NO_SURFACE) {
        return false;
    }

    // 5. Make the context and surface current
    if (!eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
        return false;
    }

    return true;
}

int main() {
    if (!initEGL()) {
        std::cerr << "Failed to initialize EGL!" << std::endl;
        return 1;
    }

    // Main rendering loop
    bool running = true;
    while (running) {
        // Clear the buffer to green
        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Swap the front and back buffers
        eglSwapBuffers(eglDisplay, eglSurface);
        // std::cout << ".";
    }

    // Cleanup: destroy the EGL objects
    eglDestroySurface(eglDisplay, eglSurface);
    eglDestroyContext(eglDisplay, eglContext);
    eglTerminate(eglDisplay);

    return 0;
}

Interesting thing is that is running and no errors. I added a ** std::cout << “.”; ** in the loop which is commented out in above sample, but when uncommented out it prints “…” continuously.

But I do not see anything just my terminal vs. color like I did with above “fb” example.

I did try to modify last example initEGL() to pass /dev/fb0. But I get error running.

bool initEGL(const std::string& framebufferDevice)
{
    int fb = open(framebufferDevice.c_str(), O_RDWR);

    if (fb == -1)
        throw std::runtime_error(std::string("Failed to open ") + framebufferDevice + " device");

    // 1. Initialize EGL
    eglDisplay = eglGetDisplay((EGLNativeDisplayType)fb);
    if (eglDisplay == EGL_NO_DISPLAY) {
        close(fb);
        throw std::runtime_error("Failed calling eglGetDisplay");
    }

    if (!eglInitialize(eglDisplay, nullptr, nullptr)) {
        close(fb);
        throw std::runtime_error("Failed calling eglInitialize");
    }

    // 2. Choose an appropriate EGL config
    EGLint numConfigs;
    EGLint configAttribs[] = {
        EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, // Use PBuffer for off-screen rendering
        EGL_BLUE_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_RED_SIZE, 8,
        EGL_DEPTH_SIZE, 8,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Assuming ES2
        EGL_NONE
    };

    if (!eglChooseConfig(eglDisplay, configAttribs, &eglConfig, 1, &numConfigs)) {
        close(fb);
        throw std::runtime_error("Failed calling eglChooseConfig");
    }

    struct fb_var_screeninfo vinfo;

    if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo)) {
        close(fb);
        throw std::runtime_error("Failed reading variable screen info");
    }

    EGLint windowAttribs[] = {
        EGL_WIDTH, static_cast<EGLint>(vinfo.xres),
        EGL_HEIGHT, static_cast<EGLint>(vinfo.yres),
        EGL_NONE
    };

    eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, (EGLNativeWindowType)fb, windowAttribs);

    if (eglSurface == EGL_NO_SURFACE) {
        close(fb);
        throw std::runtime_error("Failed calling eglCreateWindowSurface");
    }

    // 3. Create an EGL context
    EGLint contextAttribs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2, // Assuming ES2
        EGL_NONE
    };

    eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttribs);

    if (eglContext == EGL_NO_CONTEXT) {
        close(fb);
        throw std::runtime_error("Failed calling eglCreateContext");
    }

    if (!eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
        eglDestroyContext(eglDisplay, eglContext);
        eglDestroySurface(eglDisplay, eglSurface);
        close(fb);
        throw std::runtime_error("Failed calling eglMakeCurrent");
    }

    // Close the fb device now, EGL maintains its own reference
    close(fb);
    return true;
}

Anyone knows what I am doing wrong or missing? I do not see /dev/dri on my device eather to use DRI.

There are also other nv devices in /dev like:

/dev/nvhost-as-gpu
/dev/nvhost-ctrl
/dev/nvhost-ctrl-gpu
/dev/nvhost-ctrl-isp
/dev/nvhost-ctrl-nvdec
/dev/nvhost-ctrl-nvdec1
/dev/nvhost-ctrl-nvdla0
/dev/nvhost-ctrl-nvdla1
/dev/nvhost-ctrl-pva0
/dev/nvhost-ctrl-pva1
/dev/nvhost-ctxsw-gpu
/dev/nvhost-dbg-gpu
/dev/nvhost-gpu
/dev/nvhost-isp
/dev/nvhost-isp-thi
/dev/nvhost-msenc
/dev/nvhost-nvcsi
/dev/nvhost-nvdec
/dev/nvhost-nvdec1
/dev/nvhost-nvdla0
/dev/nvhost-nvdla1
/dev/nvhost-nvenc1
/dev/nvhost-nvjpg
/dev/nvhost-nvsched-gpu
/dev/nvhost-power-gpu
/dev/nvhost-prof-ctx-gpu
/dev/nvhost-prof-dev-gpu
/dev/nvhost-prof-gpu
/dev/nvhost-pva0
/dev/nvhost-pva1
/dev/nvhost-sched-gpu
/dev/nvhost-slvs-ec
/dev/nvhost-tsec
/dev/nvhost-tsecb
/dev/nvhost-tsg-gpu
/dev/nvhost-vi
/dev/nvhost-vic
/dev/nvhost-vi-thi
/dev/nvmap
/dev/nvme0
/dev/nvme0n1
/dev/nvsciipc

Should I be using one of them?

Any help is greatly appriciated.

Hi,
Could you try Jetpack 4.6.4? This may be due to the deviation:
How to boot from text mode - #3 by WayneWWW

Thank you for your time to sanswer this.

I did read link you posted, and unline that user I am on 5.1.2 released couple of weeks, I and can boot to level 3 just fine and see console. So that issue is solved.

So I have a couple more questions. I am working on production prototype and my issue above is very important to us. I can try to switch to 4.x for some time see how it goes.

  1. Can we expect this to be fixed in future 5.x releases?
  2. If not 5.x, I am reading that 6.x based of 22.04 is in works and scheduled sometimes in Q4.
  3. My first sample in my initial post is working by accessing /dev/fb0 and draw into the buffer directly. I did not do any drawing to test the speed at 1920x1080. I am assuming this is all software and I should not expect any hardware acceleration. Correct?
  4. If above point 1 or 2 can be fixed, I can live for now which just software rendering during development phace untill fixes arrive. Can I assume such?
  5. Is thee a paid support to request this be fixed? Who do I contact.
  6. For points 1 and 2 above, is my source code correct how I am initializing EGL? Perhaps there is a similar example included in Jetpack 4.x or 5.x I can take a look at?

In addition to above, I did try Qt5/QPA over last weekend, which also did not work probobly do to the same issue. I did install following:

qt5-default qtdeclarative5-dev libegl1-mesa-dev libgles2-mesa-dev cmake build-essential pkg-config

After building the sample, I did try to export

export QT_QPA_PLATFORM=eglfs

I was getting permission denied when running from my account. Trying to run as sudo just to try it produced a different error. I do not remember now, but I can get it if you need it.

Then I did try

export QT_QPA_PLATFORM=linuxfb

This worked on first try. But strangely, all other attempts did lock terminal. Keyboard did not respond. When this happened, I was able to ssh to Xavier just fine. When I did chek ps -ef | grep qtTest I did not see it running. I did forgot to check in /var/crash to check if anything there was generated. I will try it again later today and update here.

Here is my Qt 5 sample I used to load and display png image of 1920x1080.

#include <QGuiApplication>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <QWindow>
#include <QImage>
#include <QPixmap>
#include <QLabel>
#include <QScreen>

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);
    QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
    QCommandLineParser parser;
    parser.addHelpOption();
    QCommandLineOption imageFileOption(QStringList() << "i" << "image", "Load <image>.", "image");
    parser.addOption(imageFileOption);
    parser.process(app);

    if (parser.isSet("help")) {
        return 0;
    }

    QString imageFile = parser.value(imageFileOption);

    if (imageFile.isEmpty()) {
        qWarning("Please provide an image path with -i option.");
        return -1;
    }

    QImage image(imageFile);
    if (image.isNull()) {
        qWarning("Failed to load image.");
        return -1;
    }

    QLabel label;
    label.setPixmap(QPixmap::fromImage(image));

    // Set full screen and size
    QScreen *screen = QGuiApplication::primaryScreen();
    if (const QWindow *window = label.windowHandle()) {
        screen = window->screen();
    }

    QRect screenGeometry = screen->geometry();
    label.setGeometry(QRect(screenGeometry.width() / 8, screenGeometry.height() / 8, screenGeometry.width() / 4 * 3, screenGeometry.height() / 4 * 3));
    label.showFullScreen();

    return app.exec();
}

Please note, that when I stated above I was able to run my qtTest on the first try, I did not have this line included.

    if (parser.isSet("help")) {
        return 0;
    }

Of course it was a bug, and since I did not specify image as arg it would not work. But I did see screen change (no image) like something was activated.

Now, after added the –help handling and try to run

$ ./qtTest --help

The terminal freezes every time. Reboot done not help.
Also trying to switch to another terminal Alt+{2,3,4} is also not working.
But I can still ssh to Xavier just fine.

Thank you