summaryrefslogtreecommitdiff
path: root/glwrangle.h
blob: 3da96b7a951955c99bf8b72bd49c295365e7ced9 (plain)
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#pragma once

static GLenum (*_glGetError)(void);
static void (*_glGetIntegerv)(GLenum, GLint*);
static void (*_glGetFloatv)(GLenum, GLfloat*);
static void (*_glGetBooleanv)(GLenum, GLboolean*);
static const char* (*_glGetString)(GLenum);
static GLboolean (*_glIsBuffer)(GLuint);
static void (*_glGenBuffers)(GLsizei, GLuint*);
static void (*_glDeleteBuffers)(GLsizei, GLuint*);
static void (*_glBindBuffer)(GLenum, GLuint);
static void (*_glBufferData)(GLenum, GLsizeiptr, const GLvoid*, GLenum);
static void* (*_glMapBufferRange)(GLenum, GLintptr, GLsizeiptr, GLbitfield);
static void (*_glUnmapBuffer)(GLenum);
static void (*_glPixelStorei)(GLenum, GLint);
static void (*_glReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid*);
static void (*_glEnable)(GLenum);
static void (*_glDisable)(GLenum);
static void (*_glScissor)(GLint, GLint, GLsizei, GLsizei);
static void (*_glClearColor)(GLclampf, GLclampf, GLclampf, GLclampf);
static void (*_glClear)(GLbitfield);
static void (*_glDebugMessageCallback)(GLDEBUGPROC, const void*);

enum gl_variant {
   OPENGL_ES,
   OPENGL,
};

struct gl_version {
   uint32_t major, minor;
};

static enum gl_variant OPENGL_VARIANT;
static struct gl_version OPENGL_VERSION;

#define glGetError _glGetError
#define glGetIntegerv _glGetIntegerv
#define glGetFloatv _glGetFloatv
#define glGetBooleanv _glGetBooleanv
#define glGetString _glGetString
#define glIsBuffer _glIsBuffer
#define glGenBuffers _glGenBuffers
#define glDeleteBuffers _glDeleteBuffers
#define glBindBuffer _glBindBuffer
#define glBufferData _glBufferData
#define glMapBufferRange _glMapBufferRange
#define glUnmapBuffer _glUnmapBuffer
#define glPixelStorei _glPixelStorei
#define glReadPixels _glReadPixels
#define glEnable _glEnable
#define glDisable _glDisable
#define glScissor _glScissor
#define glClearColor _glClearColor
#define glClear _glClear
#define glDebugMessageCallback _glDebugMessageCallback

static void
debug_cb(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *data)
{
   (void)source, (void)type, (void)id, (void)severity, (void)length,  (void)message, (void)data;
   WARNX("%s", message);
}

static void*
dlsym_proc(const char *procname)
{
   HOOK_DLSYM(dlsym);
   return _dlsym(RTLD_NEXT, procname);
}

static void
load_gl_function_pointers(void* (*procs[])(const char*), const size_t memb)
{
   static bool loaded;

   if (loaded)
      return;

   void* (*proc)(const char*);
   for (size_t i = 0; i < memb; ++i) {
      if ((proc = procs[i]))
         break;
   }

   if (!proc)
      proc = dlsym_proc;

   // We try to support wide range of OpenGL and variants as possible.
   // Thus avoid using functions that only work in certain OpenGL versions.
   // e.g. glPushAttrib, glPushClientAttrib, it's bit of shitty but such is life.
   // Alternatively if code starts getting too much saving/restoring, consider hooking
   // the gl state changes we care about and write our own push/pop around swap_buffer.
   //
   // Version / variant dependant code is still possible through GL_VARIANT and GL_VERSION variables.
   //
   // Note that we also rely on system GL/glx.h for typedefs / constants, which probably is plain wrong on ES
   // for example, but seems to work fine so far. Main interest is to work with mainly GLX / Wine games anyways.

#define GL_REQUIRED(x) do { if (!(_##x = proc(#x))) { ERRX(EXIT_FAILURE"Failed to load %s", #x); } } while (0)
#define GL_OPTIONAL(x) do { _##x = proc(#x); } while (0)
   GL_REQUIRED(glGetError);
   GL_REQUIRED(glGetIntegerv);
   GL_REQUIRED(glGetFloatv);
   GL_REQUIRED(glGetBooleanv);
   GL_REQUIRED(glGetString);
   GL_REQUIRED(glIsBuffer);
   GL_REQUIRED(glGenBuffers);
   GL_REQUIRED(glDeleteBuffers);
   GL_REQUIRED(glBindBuffer);
   GL_REQUIRED(glBufferData);
   GL_REQUIRED(glMapBufferRange);
   GL_REQUIRED(glUnmapBuffer);
   GL_REQUIRED(glPixelStorei);
   GL_REQUIRED(glReadPixels);
   GL_REQUIRED(glEnable);
   GL_REQUIRED(glDisable);
   GL_REQUIRED(glScissor);
   GL_REQUIRED(glClearColor);
   GL_REQUIRED(glClear);
   GL_OPTIONAL(glDebugMessageCallback);
#undef GL

   if (glDebugMessageCallback) {
      // GL_DEBUG_OUTPUT_SYNCHRONOUS for breakpoints (slower)
      glEnable(GL_DEBUG_OUTPUT);
      // glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
      glDebugMessageCallback(debug_cb, NULL);
   }

   const struct { const char *p; enum gl_variant v; } variants[] = {
      { .p = "OpenGL ES-CM ", .v = OPENGL_ES },
      { .p = "OpenGL ES-CL ", .v = OPENGL_ES },
      { .p = "OpenGL ES ", .v = OPENGL_ES },
      { .p = "OpenGL ", .v = OPENGL },
   };

   const char *version = glGetString(GL_VERSION);
   WARNX("%s", version);

   for (size_t i = 0; i < ARRAY_SIZE(variants); ++i) {
      const size_t len = strlen(variants[i].p);
      if (strncmp(version, variants[i].p, len))
         continue;

      OPENGL_VARIANT = variants[i].v;
      version += len;
      break;
   }

   sscanf(version, "%u.%u", &OPENGL_VERSION.major, &OPENGL_VERSION.minor);
   loaded = true;
}