0%

glad VS glew 之 glGenbuffers

这里不是比较 GLADGLEW 优劣问题,而是简单地说一下其实现流程。

GLAD

  1. 因为 OpenGL 只是一个 标准/规范,具体的实现是由驱动开发商针对特定显卡实现的。由于 OpenGL 驱动版本众多,大多数函数的位置(内存地址)都无法在 编译 时确定下来,需要在 运行时 查询。所以开发者在开发使用 OpenGL时,需要在 运行时 获取函数的内存地址并将其保存在一个函数指针中供以后使用。取得地址的方法因平台而异,(Windows)大致流程 如下:

    1
    2
    3
    4
    5
    6
    7
    // 定义函数原型
    typedef void (*GL_GENBUFFERS) (GLsizei, GLuint*);
    // 找到正确的函数并赋值给函数指针
    GL_GENBUFFERS glGenBuffers = (GL_GENBUFFERS)wglGetProcAddress("glGenBuffers");
    // 现在函数可以被正常调用了
    GLuint buffer;
    glGenBuffers(1, &buffer);
  2. 然而写这些代码非常复杂,而且很繁琐,需要对每个可能使用的函数都要重复这个过程。不过 GLAD 的做过就是将上面的过程进行简化供开发者使用。

  3. 根据 Load OpenGL Functions WiKiwglGetProcAddress 在失败时返回 NULL,但一些实现将返回其他值。1,2和3,以及-1过程如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void *GetAnyGLFuncAddress(const char *name)
    {
    void *p = (void *)wglGetProcAddress(name);
    if(p == 0 ||
    (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) ||
    (p == (void*)-1) )
    {
    HMODULE module = LoadLibraryA("opengl32.dll");
    p = (void *)GetProcAddress(module, name);
    }

    return p;
    }
  4. MacOSX 平台,在 OSX 10.2 后 GL Fuctionweak 链接,也就意味着可以直接调用它们,未实现的扩展将解析为 NULLApple 建议需要 getProcAddress 功能的程序使用 NSSymbol 直接查找函数指针。如下:

    • Listing C-1 :

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      #import <mach-o/dyld.h>
      #import <stdlib.h>
      #import <string.h>
      void * MyNSGLGetProcAddress (const char *name)
      {
      NSSymbol symbol;
      char *symbolName;
      symbolName = malloc (strlen (name) + 2);
      strcpy(symbolName + 1, name);
      symbolName[0] = '_';
      symbol = NULL;
      if (NSIsSymbolNameDefined (symbolName))
      symbol = NSLookupAndBindSymbol (symbolName);
      free (symbolName);
      return symbol ? NSAddressOfSymbol (symbol) : NULL;
      }
    • Listing C-2:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      #import "MyNSGLGetProcAddress.h" 
      static void InitEntryPoints (void);
      static void DeallocEntryPoints (void);

      // Function pointer type definitions
      typedef void (*glBlendColorProcPtr)(GLclampf red,GLclampf green,
      GLclampf blue,GLclampf alpha);
      typedef void (*glBlendEquationProcPtr)(GLenum mode);
      typedef void (*glDrawRangeElementsProcPtr)(GLenum mode, GLuint start,
      GLuint end,GLsizei count,GLenum type,const GLvoid *indices);

      glBlendColorProcPtr pfglBlendColor = NULL;
      glBlendEquationProcPtr pfglBlendEquation = NULL;
      glDrawRangeElementsProcPtr pfglDrawRangeElements = NULL;

      static void InitEntryPoints (void)
      {
      pfglBlendColor = (glBlendColorProcPtr) MyNSGLGetProcAddress
      ("glBlendColor");
      pfglBlendEquation = (glBlendEquationProcPtr)MyNSGLGetProcAddress
      ("glBlendEquation");
      pfglDrawRangeElements = (glDrawRangeElementsProcPtr)MyNSGLGetProcAddress
      ("glDrawRangeElements");
      }
      // -------------------------
      static void DeallocEntryPoints (void)
      {
      pfglBlendColor = NULL;
      pfglBlendEquation = NULL;
      pfglDrawRangeElements = NULL;;
      }
    • 关于以上代码的解释,可以到这里查看。

  5. 再回到 GLAD ,

    • glad.h 文件可以看到 glGenBuffers 的定义:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      #ifndef APIENTRYP
      #define APIENTRYP APIENTRY *
      #endif

      define GLAPI extern

      typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers);
      GLAPI PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers;
      #define glDeleteBuffers glad_glDeleteBuffers

    • glad.c 文件中可以看到 gladGetProcAddressPtr 的定义:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      #ifndef __APPLE__
      typedef void* (APIENTRYP PFNGLXGETPROCADDRESSPROC_PRIVATE)(const char*);
      static PFNGLXGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr;
      #endif

      static
      int open_gl(void) {
      #ifdef __APPLE__
      static const char *NAMES[] = {
      "../Frameworks/OpenGL.framework/OpenGL",
      "/Library/Frameworks/OpenGL.framework/OpenGL",
      "/System/Library/Frameworks/OpenGL.framework/OpenGL",
      "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL"
      };
      #else
      static const char *NAMES[] = {"libGL.so.1", "libGL.so"};
      #endif

      unsigned int index = 0;
      for(index = 0; index < (sizeof(NAMES) / sizeof(NAMES[0])); index++) {
      libGL = dlopen(NAMES[index], RTLD_NOW | RTLD_GLOBAL);

      if(libGL != NULL) {
      #ifdef __APPLE__
      return 1;
      #else
      gladGetProcAddressPtr = (PFNGLXGETPROCADDRESSPROC_PRIVATE)dlsym(libGL,
      "glXGetProcAddressARB");
      return gladGetProcAddressPtr != NULL;
      #endif
      }
      }

      return 0;
      }

GLEW

  1. GLEW 的出现也是为了在开发时,能够针对不能显卡制造商的显卡的 OpenGL 的不同版本,实现在 运行时 获取函数的内存地址并将其保存在一个函数指针中供以使用。

  2. glew.h 文件中可以看懂 glGenBuffers 的定义:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #ifndef GLEW_GET_FUN
    #define GLEW_GET_FUN(x) x
    #endif

    #ifndef GLAPIENTRY
    #define GLAPIENTRY
    #endif

    typedef void (GLAPIENTRY * PFNGLGENBUFFERSPROC) (GLsizei n, GLuint* buffers);

    GLEW_FUN_EXPORT PFNGLGENBUFFERSPROC __glewGenBuffers;

    #define glGenBuffers GLEW_GET_FUN(__glewGenBuffers)

    小结

    GLADGLEW 的作用都是帮助在开发 OpenGL 时简化其在 运行时 获取函数的内存地址并将其保存在一个函数指针中供以使用的流程。

参考资料