使用X11和nVidia驱动程序时的奇怪EGL行为

5
我目前遇到一个奇怪的问题,我有一个样例应用程序,使用GTK2、OpenGL和EGL。我所描述的奇怪行为仅在Linux上的nVidia GPU以及官方的nVidia驱动程序上发生。在程序中,您会看到一条注释,如果在代码的这个点不使用glFlushglGetError,代码将不会绘制三角形,它只会显示红色(清除颜色)。如果您调用glGetErrorglFlush,它会正常工作。
有人能解释一下这是为什么吗?以下是代码:
#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;
}

使用以下命令编译:

g++ -o gtkegl gtkegl.cc -lEGL -lGL $(pkg-config --libs --cflags gtk+-2.0) -lX11

3
eglSwapBuffers的文档中写道:"eglSwapBuffers在交换之前对上下文执行隐式刷新操作(对于绑定到表面的OpenGL ES或OpenGL上下文为glFlush)"。看起来是Nvidia的一个bug。 - Botje
即使Swap发出了隐式Flush,显式调用它也不会造成太大的伤害:当命令缓冲区已经被刷新时,Flush只是返回并且不执行任何操作。顺便说一句,幸运的是我有适当的硬件,所以我会尝试这段代码。 - hidefromkgb
确认。GL版本:4.6.0 NVIDIA 418.56,GTX 980Ti。 - hidefromkgb
很高兴这不仅发生在我身上:)。我也在NVIDIA论坛上发布了这个问题,但还没有得到任何反应。 - Nidhoegger
@Nidhoegger…我有一个问题。GTK+是必须的吗?很可能罪魁祸首在GDK和EGL之间,因为我的Xlib-only EGL示例似乎没有出现这个问题。 - hidefromkgb
显示剩余2条评论
1个回答

0

eglSwapBuffers是线程上下文特定的。如果您以某种方式从另一个线程更改了渲染上下文,则会失败。

我看到了几个可能的错误:

  1. 您没有检查eglMakeCurrent返回值。它可能会失败。
  2. 您没有同步draw_cb。它可能会同时从不同的线程调用。
  3. 您从不同的位置调用eglMakeCurrent。它们可能会相互冲突。

在我的代码中(以及上面的示例中),确保绘图仅在同一线程中完成。此外,在每次绘制之前,通过eglMakeCurrent使上下文成为当前状态。 - Nidhoegger
我明白了,但你没有检查任何返回。 - Andrey Chistyakov
这是一个最简示例,通常不包含错误检查。我在这里提供了一个带有错误检查的版本,在我的生产代码中会进行错误检查。 - Nidhoegger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接