Работа с GLX

@KIA-it говоря Linux - я разработчик модуля OpenGL. И в данный момент я выключил из него glx, содержащий функции для подключения OpenGL к окнам XWindow. Это потому, что у меня нет ОС кроме Windows, поэтому я не могу даже протестировать работает ли мой код glx. Но если вы поможете - можно разобраться и добавить glx назад.

В частности - в wgl инициализация происходит так:

  1. Получить поверхность рисования функцией GetDC из user32.dll;
  2. Создать контекст OpenGL функцией wglCreateContext.
  3. И только после этого вызывать wglGetProcAddress для каждого имени функции, которую хотите использовать (потому что теоретически разные контексты OpenGL могут дать разные реализации одной и той же функции).

В первую очередь мне надо знать как аналогичное происходит в glx? Желательно с примером рабочего кода. (и скриншотами его работы, ибо опять же, у себя не проверю)

  1. Какая требуется поддержка со стороны видеодрайвера и железа? На интеловской интегрированной плате должно работать или нет?

У меня в наличии есть несколько дискретных плат от NVidia и AMD, но не моложе GT710 и AMD 5ххх.

  1. Сскопипащено отсюда https://www.khronos.org/opengl/wiki/Tutorial:OpenGL_3.0_Context_Creation(GLX):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <GL/gl.h>
#include <GL/glx.h>

#define GLX_CONTEXT_MAJOR_VERSION_ARB       0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB       0x2092
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);

// Helper to check for extension string presence.  Adapted from:
//   http://www.opengl.org/resources/features/OGLextensions/
static bool isExtensionSupported(const char *extList, const char *extension)
{
  const char *start;
  const char *where, *terminator;
  
  /* Extension names should not have spaces. */
  where = strchr(extension, ' ');
  if (where || *extension == '\0')
    return false;

  /* It takes a bit of care to be fool-proof about parsing the
     OpenGL extensions string. Don't be fooled by sub-strings,
     etc. */
  for (start=extList;;) {
    where = strstr(start, extension);

    if (!where)
      break;

    terminator = where + strlen(extension);

    if ( where == start || *(where - 1) == ' ' )
      if ( *terminator == ' ' || *terminator == '\0' )
        return true;

    start = terminator;
  }

  return false;
}

static bool ctxErrorOccurred = false;
static int ctxErrorHandler( Display *dpy, XErrorEvent *ev )
{
    ctxErrorOccurred = true;
    return 0;
}

int main(int argc, char* argv[])
{
  Display *display = XOpenDisplay(NULL);

  if (!display)
  {
    printf("Failed to open X display\n");
    exit(1);
  }

  // Get a matching FB config
  static int visual_attribs[] =
    {
      GLX_X_RENDERABLE    , True,
      GLX_DRAWABLE_TYPE   , GLX_WINDOW_BIT,
      GLX_RENDER_TYPE     , GLX_RGBA_BIT,
      GLX_X_VISUAL_TYPE   , GLX_TRUE_COLOR,
      GLX_RED_SIZE        , 8,
      GLX_GREEN_SIZE      , 8,
      GLX_BLUE_SIZE       , 8,
      GLX_ALPHA_SIZE      , 8,
      GLX_DEPTH_SIZE      , 24,
      GLX_STENCIL_SIZE    , 8,
      GLX_DOUBLEBUFFER    , True,
      //GLX_SAMPLE_BUFFERS  , 1,
      //GLX_SAMPLES         , 4,
      None
    };

  int glx_major, glx_minor;
 
  // FBConfigs were added in GLX version 1.3.
  if ( !glXQueryVersion( display, &glx_major, &glx_minor ) || 
       ( ( glx_major == 1 ) && ( glx_minor < 3 ) ) || ( glx_major < 1 ) )
  {
    printf("Invalid GLX version");
    exit(1);
  }

  printf( "Getting matching framebuffer configs\n" );
  int fbcount;
  GLXFBConfig* fbc = glXChooseFBConfig(display, DefaultScreen(display), visual_attribs, &fbcount);
  if (!fbc)
  {
    printf( "Failed to retrieve a framebuffer config\n" );
    exit(1);
  }
  printf( "Found %d matching FB configs.\n", fbcount );

  // Pick the FB config/visual with the most samples per pixel
  printf( "Getting XVisualInfos\n" );
  int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;

  int i;
  for (i=0; i<fbcount; ++i)
  {
    XVisualInfo *vi = glXGetVisualFromFBConfig( display, fbc[i] );
    if ( vi )
    {
      int samp_buf, samples;
      glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf );
      glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLES       , &samples  );
      
      printf( "  Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
              " SAMPLES = %d\n", 
              i, vi -> visualid, samp_buf, samples );

      if ( best_fbc < 0 || samp_buf && samples > best_num_samp )
        best_fbc = i, best_num_samp = samples;
      if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp )
        worst_fbc = i, worst_num_samp = samples;
    }
    XFree( vi );
  }

  GLXFBConfig bestFbc = fbc[ best_fbc ];

  // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
  XFree( fbc );

  // Get a visual
  XVisualInfo *vi = glXGetVisualFromFBConfig( display, bestFbc );
  printf( "Chosen visual ID = 0x%x\n", vi->visualid );

  printf( "Creating colormap\n" );
  XSetWindowAttributes swa;
  Colormap cmap;
  swa.colormap = cmap = XCreateColormap( display,
                                         RootWindow( display, vi->screen ), 
                                         vi->visual, AllocNone );
  swa.background_pixmap = None ;
  swa.border_pixel      = 0;
  swa.event_mask        = StructureNotifyMask;

  printf( "Creating window\n" );
  Window win = XCreateWindow( display, RootWindow( display, vi->screen ), 
                              0, 0, 100, 100, 0, vi->depth, InputOutput, 
                              vi->visual, 
                              CWBorderPixel|CWColormap|CWEventMask, &swa );
  if ( !win )
  {
    printf( "Failed to create window.\n" );
    exit(1);
  }

  // Done with the visual info data
  XFree( vi );

  XStoreName( display, win, "GL 3.0 Window" );

  printf( "Mapping window\n" );
  XMapWindow( display, win );

  // Get the default screen's GLX extension list
  const char *glxExts = glXQueryExtensionsString( display,
                                                  DefaultScreen( display ) );

  // NOTE: It is not necessary to create or make current to a context before
  // calling glXGetProcAddressARB
  glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
  glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
           glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" );

  GLXContext ctx = 0;

  // Install an X error handler so the application won't exit if GL 3.0
  // context allocation fails.
  //
  // Note this error handler is global.  All display connections in all threads
  // of a process use the same error handler, so be sure to guard against other
  // threads issuing X commands while this code is running.
  ctxErrorOccurred = false;
  int (*oldHandler)(Display*, XErrorEvent*) =
      XSetErrorHandler(&ctxErrorHandler);

  // Check for the GLX_ARB_create_context extension string and the function.
  // If either is not present, use GLX 1.3 context creation method.
  if ( !isExtensionSupported( glxExts, "GLX_ARB_create_context" ) ||
       !glXCreateContextAttribsARB )
  {
    printf( "glXCreateContextAttribsARB() not found"
            " ... using old-style GLX context\n" );
    ctx = glXCreateNewContext( display, bestFbc, GLX_RGBA_TYPE, 0, True );
  }

  // If it does, try to get a GL 3.0 context!
  else
  {
    int context_attribs[] =
      {
        GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
        GLX_CONTEXT_MINOR_VERSION_ARB, 0,
        //GLX_CONTEXT_FLAGS_ARB        , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
        None
      };

    printf( "Creating context\n" );
    ctx = glXCreateContextAttribsARB( display, bestFbc, 0,
                                      True, context_attribs );

    // Sync to ensure any errors generated are processed.
    XSync( display, False );
    if ( !ctxErrorOccurred && ctx )
      printf( "Created GL 3.0 context\n" );
    else
    {
      // Couldn't create GL 3.0 context.  Fall back to old-style 2.x context.
      // When a context version below 3.0 is requested, implementations will
      // return the newest context version compatible with OpenGL versions less
      // than version 3.0.
      // GLX_CONTEXT_MAJOR_VERSION_ARB = 1
      context_attribs[1] = 1;
      // GLX_CONTEXT_MINOR_VERSION_ARB = 0
      context_attribs[3] = 0;

      ctxErrorOccurred = false;

      printf( "Failed to create GL 3.0 context"
              " ... using old-style GLX context\n" );
      ctx = glXCreateContextAttribsARB( display, bestFbc, 0, 
                                        True, context_attribs );
    }
  }

  // Sync to ensure any errors generated are processed.
  XSync( display, False );

  // Restore the original error handler
  XSetErrorHandler( oldHandler );

  if ( ctxErrorOccurred || !ctx )
  {
    printf( "Failed to create an OpenGL context\n" );
    exit(1);
  }

  // Verifying that context is a direct context
  if ( ! glXIsDirect ( display, ctx ) )
  {
    printf( "Indirect GLX rendering context obtained\n" );
  }
  else
  {
    printf( "Direct GLX rendering context obtained\n" );
  }

  printf( "Making context current\n" );
  glXMakeCurrent( display, win, ctx );

  glClearColor( 0, 0.5, 1, 1 );
  glClear( GL_COLOR_BUFFER_BIT );
  glXSwapBuffers ( display, win );

  sleep( 1 );

  glClearColor ( 1, 0.5, 0, 1 );
  glClear ( GL_COLOR_BUFFER_BIT );
  glXSwapBuffers ( display, win );

  sleep( 1 );

  glXMakeCurrent( display, 0, 0 );
  glXDestroyContext( display, ctx );

  XDestroyWindow( display, win );
  XFreeColormap( display, cmap );
  XCloseDisplay( display );

  return 0;
}

Результат работы у меня на хубунте 18.04.5:

user@pabctest:~/workspace/glx$ gcc -o gl3 gl3.cxx -lGL -lX11
gl3.cxx: In function ‘int main(int, char**)’:
gl3.cxx:119:52: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 3 has type ‘VisualID {aka long unsigned int}’ [-Wformat=]
               i, vi -> visualid, samp_buf, samples );
                  ~~~~~~~~~~~~~~                    ^
gl3.cxx:136:53: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘VisualID {aka long unsigned int}’ [-Wformat=]
   printf( "Chosen visual ID = 0x%x\n", vi->visualid );
                                        ~~~~~~~~~~~~ ^
user@pabctest:~/workspace/glx$ ./gl3
Getting matching framebuffer configs
Found 10 matching FB configs.
Getting XVisualInfos
  Matching fbconfig 0, visual ID 0x24: SAMPLE_BUFFERS = 0, SAMPLES = 0
  Matching fbconfig 1, visual ID 0xba: SAMPLE_BUFFERS = 0, SAMPLES = 0
  Matching fbconfig 2, visual ID 0x38: SAMPLE_BUFFERS = 1, SAMPLES = 2
  Matching fbconfig 3, visual ID 0xce: SAMPLE_BUFFERS = 1, SAMPLES = 2
  Matching fbconfig 4, visual ID 0x3a: SAMPLE_BUFFERS = 1, SAMPLES = 4
  Matching fbconfig 5, visual ID 0xd0: SAMPLE_BUFFERS = 1, SAMPLES = 4
  Matching fbconfig 6, visual ID 0x44: SAMPLE_BUFFERS = 1, SAMPLES = 8
  Matching fbconfig 7, visual ID 0xda: SAMPLE_BUFFERS = 1, SAMPLES = 8
  Matching fbconfig 8, visual ID 0x4c: SAMPLE_BUFFERS = 1, SAMPLES = 16
  Matching fbconfig 9, visual ID 0xe2: SAMPLE_BUFFERS = 1, SAMPLES = 16
Chosen visual ID = 0x4c
Creating colormap
Creating window
Mapping window
Creating context
Created GL 3.0 context
Direct GLX rendering context obtained
Making context current
user@pabctest:~/workspace/glx$ ^C
user@pabctest:~/workspace/glx$ 

Думаю да. По крайней мере под виндой OpenGL практически с любым железом, подходящим для вывода картинки на экран.

Правда, под линуксом для особо редких вариантов может не быть драйверов… Но NVidia и AMD это вряд ли затронет.


Ну, я что то слишком расслабился… У меня

Рабочие примеры на C++ сложно Не найти. И с самим C++ у меня нет проблем.

Проблема в переводе. Когда я переводил код с wgl - вроде всё понятно, а вроде там и сям мелкие заковырки. С возможностью протестировать они исправляются за пол минуты, но у для glx такой возможности не было.


Ну, я что то слишком расслабился… Надо бы хотя бы добавить glx назад в модуль и исправить всю несовместимость, накопившуюся с момента когда я его убрал… Теперь исправился:

OpenGL.rar (301.1 КБ)

Но, что я ещё забыл - я так понимаю, у вас всё ещё не получается запустить компилятор паскаля на линуксе? Тогда давайте начнём с более простого и в C#:

Как правильно подключать функцию glXChooseFBConfig? Поидее с ней должен работать [DllImport], даже на линуксе. Но что указывать в качестве бинарника? OpenGL.so? OpenGL32.so? Попробуйте ещё какие то варианты, напишите что сработает…

P.S. Ещё вариант - libGL.so.1

 Институт математики, механики и компьютерных наук ЮФУ, 2005–2018
Администрация форума: В.Н. Брагилевский, С.С. Михалкович, А.М. Пеленицын
Yandex.Metrica