5

I have a strange problem at the moment, I have this sample application using GTK2, OpenGL and EGL. The strange behaviour I describe only happens on nVidia GPUs on Linux with the official nVidia driver. In the program you see a comment, if you dont use glFlush OR glGetError at this point in the code, the code will not draw the triangle, it will only display red (the clear color). If you call glGetError or glFlush, it works.

Can somebody explain to me why this is happening? Here is the code:

#include <stdio.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <EGL/egl.h>
#include <GL/gl.h>

static EGLDisplay egl_display;
static EGLSurface egl_surface;
static EGLContext egl_context;

static void realize_cb (GtkWidget *widget)
{
    EGLConfig egl_config;
    EGLint n_config;
    EGLint attributes[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
                            EGL_NONE };
    EGLint surf_attrs[] = {
        EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
        EGL_NONE
    };

    egl_display = eglGetDisplay ((EGLNativeDisplayType) gdk_x11_display_get_xdisplay (gtk_widget_get_display (widget)));
    eglInitialize (egl_display, NULL, NULL);
    eglChooseConfig (egl_display, attributes, &egl_config, 1, &n_config);
    egl_surface = eglCreateWindowSurface (egl_display, egl_config, GDK_WINDOW_XID (gtk_widget_get_window (widget)), NULL);
    eglBindAPI (EGL_OPENGL_API);   
    egl_context = eglCreateContext (egl_display, egl_config, EGL_NO_CONTEXT, NULL);
    eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
    printf("GL Version: %s\n", glGetString(GL_VERSION));
}

static gboolean on_configure (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
    gtk_widget_queue_draw(widget);
    return FALSE;
}

static gboolean draw_cb (GtkWidget *widget, GdkEventExpose *expose, gpointer userdata)
{
    eglMakeCurrent (egl_display, egl_surface, egl_surface, egl_context);

    GtkAllocation alloc;
    gtk_widget_get_allocation(widget, &alloc);
    glViewport (0, 0, alloc.width, alloc.height);

    glClearColor (1, 0, 0, 1);
    glClearDepth(1.0);
    glClear (GL_COLOR_BUFFER_BIT);

    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    glOrtho (0, 100, 0, 100, 0, 1);

    glBegin (GL_TRIANGLES);
    glColor3f (1, 0, 0);
    glVertex2f (50, 10);
    glColor3f (0, 1, 0);
    glVertex2f (90, 90);
    glColor3f (0, 0, 1);
    glVertex2f (10, 90);
    glEnd ();

    /****************************
     * It does not matter if you call glGetError or glFlush here, if you do, it
     * works, if not, it only displays a red surface
     ****************************/
    //glGetError();
    //glFlush();
    eglSwapBuffers (egl_display, egl_surface);

    return TRUE;
}

int main (int argc, char **argv)
{
    GtkWidget *w;

    gtk_init (&argc, &argv);

    w = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_double_buffered (GTK_WIDGET (w), FALSE);
    g_signal_connect_after (G_OBJECT (w), "realize", G_CALLBACK (realize_cb), NULL);
    g_signal_connect (G_OBJECT (w), "expose-event", G_CALLBACK (draw_cb), NULL);
    g_signal_connect (G_OBJECT (w), "configure-event", G_CALLBACK (on_configure), NULL);
    gtk_widget_show (w);

    gtk_main ();

    return 0;
}

Compile with:

g++ -o gtkegl gtkegl.cc -lEGL -lGL $(pkg-config --libs --cflags gtk+-2.0) -lX11
Nidhoegger
  • 4,973
  • 4
  • 36
  • 81
  • 3
    The [documentation for eglSwapBuffers](https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglSwapBuffers.xhtml) says "eglSwapBuffers performs an implicit flush operation on the context (glFlush for an OpenGL ES or OpenGL context bound to surface before swapping." Looks like an Nvidia bug. – Botje May 15 '19 at 10:58
  • Well, even if Swap issues an implicit Flush, calling it explicitly does not do much harm: being issued when a command buffer has already been flushed, Flush just returns and does nothing. On a side note, luckily I\`ve got the proper hardware, so I\`ll give this code a try. – hidefromkgb May 29 '19 at 10:43
  • Confirmed. GL Version: 4.6.0 NVIDIA 418.56, GTX 980Ti. – hidefromkgb May 29 '19 at 20:06
  • Glad it does not only happen to me :). Ive also posted this in the nvidia forums, but got no reaction at all yet. – Nidhoegger May 30 '19 at 11:04
  • @Nidhoegger …Got a question. Is GTK+ a requirement? Chances are the culprit is somewhere between GDK and EGL, for my Xlib-only EGL sample seems not to exhibit the issue. – hidefromkgb May 30 '19 at 19:46
  • GTK is a definitive requirement. i have written two xlib examples, one that works and one that does not. – Nidhoegger May 31 '19 at 07:52
  • I guess its some kind of race condition – Nidhoegger Jun 01 '19 at 19:53

1 Answers1

0

eglSwapBuffers is thread-context specific. If you somehow altered the rendering context from another thread then it fails.

I see couple possible errors:

  1. You don't check eglMakeCurrent returning value. It may fail.
  2. You don't synchronise draw_cb. It may be called from different threads simultaneously.
  3. You call eglMakeCurrent from different places. They might conflict between each other.
Tania Chistyakova
  • 3,928
  • 6
  • 13
  • It is ensured in my code (and also in the example above) that the drawing is only done in the same thread. Also before every draw the context is made current via `eglMakeCurrent`. – Nidhoegger Jun 04 '19 at 13:50
  • I see, but you don't check any return. – Tania Chistyakova Jun 04 '19 at 14:06
  • This is a minimal example which normaly does not contain error checking. I got a error checking version here and in my production code errors are checked – Nidhoegger Jun 04 '19 at 14:26