diff --git a/README.rst b/README.rst index 0501672..91f756b 100644 --- a/README.rst +++ b/README.rst @@ -5,6 +5,9 @@ gl3w: Simple OpenGL core profile loading Introduction ------------ +This is a modified gl3w generator, used to create a minimal amalgamated OpenGL loader header file. It is specifically +created for use within `Dear ImGui`_ project. + gl3w_ is the easiest way to get your hands on the functionality offered by the OpenGL core profile specification. @@ -22,6 +25,9 @@ It is also compatible with Python 3.x. Example ------- +Header is generated by executing `./gl3w_gen.py ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt`. +Not specifying `--ref` parameter will skip file scan and generate a loader including all available OpenGL APIs. + Here is a simple example of using gl3w_ with glut. Note that GL/gl3w.h must be included before any other OpenGL related headers:: @@ -45,11 +51,11 @@ included before any other OpenGL related headers:: glutMouseFunc(mouse); glutMotionFunc(motion); - if (gl3wInit()) { + if (imgl3wInit()) { fprintf(stderr, "failed to initialize OpenGL\n"); return -1; } - if (!gl3wIsSupported(3, 2)) { + if (!imgl3wIsSupported(3, 2)) { fprintf(stderr, "OpenGL 3.2 not supported\n"); return -1; } @@ -67,18 +73,18 @@ API Reference The gl3w_ API consists of just three functions: -``int gl3wInit(void)`` +``int imgl3wInit(void)`` Initializes the library. Should be called once after an OpenGL context has been created. Returns ``0`` when gl3w_ was initialized successfully, ``non-zero`` if there was an error. -``int gl3wIsSupported(int major, int minor)`` +``int imgl3wIsSupported(int major, int minor)`` Returns ``1`` when OpenGL core profile version *major.minor* is available and ``0`` otherwise. -``GL3WglProc gl3wGetProcAddress(const char *proc)`` +``GL3WglProc imgl3wGetProcAddress(const char *proc)`` Returns the address of an OpenGL extension function. Generally, you won't need to use it since gl3w_ loads all functions defined in the OpenGL core @@ -133,3 +139,4 @@ OpenGL_ is a registered trademark of SGI_. .. _OpenGL: http://www.opengl.org/ .. _Khronos: http://www.khronos.org/ .. _SGI: http://www.sgi.com/ +.. _Dear ImGui: https://github.com/ocornut/imgui/ diff --git a/extra_symbols.txt b/extra_symbols.txt new file mode 100644 index 0000000..423d2a5 --- /dev/null +++ b/extra_symbols.txt @@ -0,0 +1,3 @@ +glReadPixels +GL_PACK_ALIGNMENT +GL_FRAMEBUFFER_SRGB diff --git a/gl3w_gen.py b/gl3w_gen.py index f51f108..bb56aa6 100755 --- a/gl3w_gen.py +++ b/gl3w_gen.py @@ -30,6 +30,7 @@ import argparse import os +import os.path import re # Try to import Python 3 library urllib.request @@ -61,11 +62,64 @@ def download(url, dst): with open(dst, 'wb') as f: f.writelines(web.readlines()) + +class IfDefNode(object): + def __init__(self, parent=None): + self.children = [] + self.parent = parent + if parent is not None: + parent.children.append(self) + + def __str__(self): + if len(self.children): + return str(self.children[0]) + return '' + + +def cull_empty(node): + new_children = [] + for c in node.children: + if isinstance(c, str) or cull_empty(c): + new_children.append(c) + node.children = new_children + return len(node.children) > 2 + + +def gather_children(node): + for c in node.children: + if isinstance(c, str): + yield c + else: + yield from gather_children(c) + + +script_dir = os.path.dirname(__file__) parser = argparse.ArgumentParser(description='gl3w generator script') parser.add_argument('--ext', action='store_true', help='Load extensions') parser.add_argument('--root', type=str, default='', help='Root directory') +parser.add_argument('--ref', nargs='+', default=[], help='Scan files or dirs and only include used APIs.') +parser.add_argument('--output', default='include/GL/imgui_impl_opengl3_loader.h', help='Output header.') args = parser.parse_args() +# Create symbol whitelist +re_fun = re.compile(r'\b(gl[A-Z][a-zA-Z0-9_]+)\b') +re_def = re.compile(r'\b(GL_[a-zA-Z0-9_]+)\b') +re_comments = re.compile(r'//.*?\n|/\*.*?\*/', re.MULTILINE | re.DOTALL) +whitelist = set() + +# Include extensions, they will be trimmed if unused anyway. +args.ext = args.ext or len(args.ref) +for api_ref in args.ref: + with open(api_ref) as fp: + source_code = fp.read() + # Avoid including unused symbols that may appear in comments. + source_code = re_comments.sub('', source_code) + for e in re_def.findall(source_code): + whitelist.add(e) + for e in re_fun.findall(source_code): + whitelist.add(e) + whitelist.add('PFN{}PROC'.format(e.upper())) + # Create directories touch_dir(os.path.join(args.root, 'include/GL')) touch_dir(os.path.join(args.root, 'include/KHR')) @@ -81,44 +135,77 @@ def download(url, dst): print('Parsing glcorearb.h header...') procs = [] p = re.compile(r'GLAPI.*APIENTRY\s+(\w+)') -with open(os.path.join(args.root, 'include/GL/glcorearb.h'), 'r') as f: - for line in f: +d = re.compile(r'#define\s+(GL_[a-zA-Z0-9_]+)\s+(0x)?[0-9A-F]+') +f = re.compile(r'\bAPIENTRYP (PFNGL[A-Z0-9]+PROC)\b') +glcorearb = IfDefNode() +with open(os.path.join(args.root, 'include/GL/glcorearb.h'), 'r') as fp: + for line in fp: + # Match API m = p.match(line) - if not m: + if m is not None: + proc = m.group(1) + if (args.ext or not is_ext(proc)) and len(whitelist) == 0 or proc in whitelist: + procs.append(proc) + else: + continue + + # Exclude non-whitelisted preprocessor definitions + m = d.match(line) + if m is not None and len(whitelist) and m.group(1) not in whitelist: continue - proc = m.group(1) - if args.ext or not is_ext(proc): - procs.append(proc) + + # Exclude non-whitelisted function pointer types + m = f.search(line) + if m is not None and len(whitelist) and m.group(1) not in whitelist: + continue + + line = line.rstrip('\r\n') + if line.startswith('#if'): + glcorearb = IfDefNode(glcorearb) + glcorearb.children.append(line) + elif line.startswith('#endif'): + glcorearb.children.append(line) + glcorearb = glcorearb.parent + elif line: + glcorearb.children.append(line) procs.sort() +assert glcorearb.parent is None +cull_empty(glcorearb) # Walk parsed glcorearb and cull empty ifdefs +glcorearb = '\n'.join(gather_children(glcorearb)) # Reassemble glcorearb.h # Generate gl3w.h -print('Generating {0}...'.format(os.path.join(args.root, 'include/GL/gl3w.h'))) -with open(os.path.join(args.root, 'include/GL/gl3w.h'), 'wb') as f: - gl3w_h = open(os.path.join(args.root, 'template/gl3w.h'), 'r', encoding='utf-8').read() +print('Generating {0}...'.format(args.output)) +with open(args.output, 'w+', encoding='utf-8') as fp: + h_template = open('{}/template/gl3w.h'.format(script_dir)).read() + strings = [ '/* gl3w internal state */', 'union GL3WProcs {', - '\tGL3WglProc ptr[{0}];'.format(len(procs)), - '\tstruct {' + ' GL3WglProc ptr[{0}];'.format(len(procs)), + ' struct {' ] + max_proc_len = max([len(p) for p in procs]) + 7 for proc in procs: - strings.append('\t\t{0: <55} {1};'.format('PFN{0}PROC'.format(proc.upper()), proc[2:])) - strings.append('\t} gl;') # struct + strings.append(' {0: <{2}} {1};'.format('PFN{0}PROC'.format(proc.upper()), proc[2:], max_proc_len)) + strings.append(' } gl;') # struct strings.append('};') # union GL3WProcs - gl3w_h = gl3w_h.replace(strings[0], '\n'.join(strings)) + h_template = h_template.replace(strings[0], '\n'.join(strings)) strings = ['/* OpenGL functions */'] for proc in procs: - strings.append('#define {0: <48} gl3wProcs.gl.{1}'.format(proc, proc[2:])) - gl3w_h = gl3w_h.replace(strings[0], '\n'.join(strings)) - write(f, gl3w_h) - -# Generate gl3w.c -print('Generating {0}...'.format(os.path.join(args.root, 'src/gl3w.c'))) -with open(os.path.join(args.root, 'src/gl3w.c'), 'wb') as f: - gl3w_c = open(os.path.join(args.root, 'template/gl3w.c'), 'r', encoding='utf-8').read() + strings.append('#define {0: <{2}} imgl3wProcs.gl.{1}'.format(proc, proc[2:], max_proc_len)) + h_template = h_template.replace(strings[0], '\n'.join(strings)) + + # Embed GL/glcorearb.h + h_template = h_template.replace('#include ', glcorearb) + + # Remove KHR/khrplatform.h include, we use our own minimal typedefs from it + h_template = h_template.replace('#include ', '') + + # Embed gl3w.c strings = ['static const char *proc_names[] = {'] for proc in procs: - strings.append('\t"{0}",'.format(proc)) - gl3w_c = gl3w_c.replace(strings[0], '\n'.join(strings)) - write(f, gl3w_c) + strings.append(' "{0}",'.format(proc)) + h_template = h_template.replace(strings[0], '\n'.join(strings)) + + fp.write(h_template) diff --git a/template/gl3w.c b/template/gl3w.c deleted file mode 100644 index 787b678..0000000 --- a/template/gl3w.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * This file was generated with gl3w_gen.py, part of gl3w - * (hosted at https://github.com/skaslev/gl3w) - * - * This is free and unencumbered software released into the public domain. - * - * Anyone is free to copy, modify, publish, use, compile, sell, or - * distribute this software, either in source code form or as a compiled - * binary, for any purpose, commercial or non-commercial, and by any - * means. - * - * In jurisdictions that recognize copyright laws, the author or authors - * of this software dedicate any and all copyright interest in the - * software to the public domain. We make this dedication for the benefit - * of the public at large and to the detriment of our heirs and - * successors. We intend this dedication to be an overt act of - * relinquishment in perpetuity of all present and future rights to this - * software under copyright law. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - -#if defined(_WIN32) -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN 1 -#endif -#include - -static HMODULE libgl; -typedef PROC(__stdcall* GL3WglGetProcAddr)(LPCSTR); -static GL3WglGetProcAddr wgl_get_proc_address; - -static int open_libgl(void) -{ - libgl = LoadLibraryA("opengl32.dll"); - if (!libgl) - return GL3W_ERROR_LIBRARY_OPEN; - - wgl_get_proc_address = (GL3WglGetProcAddr)GetProcAddress(libgl, "wglGetProcAddress"); - return GL3W_OK; -} - -static void close_libgl(void) -{ - FreeLibrary(libgl); -} - -static GL3WglProc get_proc(const char *proc) -{ - GL3WglProc res; - - res = (GL3WglProc)wgl_get_proc_address(proc); - if (!res) - res = (GL3WglProc)GetProcAddress(libgl, proc); - return res; -} -#elif defined(__APPLE__) -#include - -static void *libgl; - -static int open_libgl(void) -{ - libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY | RTLD_LOCAL); - if (!libgl) - return GL3W_ERROR_LIBRARY_OPEN; - - return GL3W_OK; -} - -static void close_libgl(void) -{ - dlclose(libgl); -} - -static GL3WglProc get_proc(const char *proc) -{ - GL3WglProc res; - - *(void **)(&res) = dlsym(libgl, proc); - return res; -} -#else -#include - -static void *libgl; -static GL3WglProc (*glx_get_proc_address)(const GLubyte *); - -static int open_libgl(void) -{ - libgl = dlopen("libGL.so.1", RTLD_LAZY | RTLD_LOCAL); - if (!libgl) - return GL3W_ERROR_LIBRARY_OPEN; - - *(void **)(&glx_get_proc_address) = dlsym(libgl, "glXGetProcAddressARB"); - return GL3W_OK; -} - -static void close_libgl(void) -{ - dlclose(libgl); -} - -static GL3WglProc get_proc(const char *proc) -{ - GL3WglProc res; - - res = glx_get_proc_address((const GLubyte *)proc); - if (!res) - *(void **)(&res) = dlsym(libgl, proc); - return res; -} -#endif - -static struct { - int major, minor; -} version; - -static int parse_version(void) -{ - if (!glGetIntegerv) - return GL3W_ERROR_INIT; - - glGetIntegerv(GL_MAJOR_VERSION, &version.major); - glGetIntegerv(GL_MINOR_VERSION, &version.minor); - - if (version.major < 3) - return GL3W_ERROR_OPENGL_VERSION; - return GL3W_OK; -} - -static void load_procs(GL3WGetProcAddressProc proc); - -int gl3wInit(void) -{ - int res; - - res = open_libgl(); - if (res) - return res; - - atexit(close_libgl); - return gl3wInit2(get_proc); -} - -int gl3wInit2(GL3WGetProcAddressProc proc) -{ - load_procs(proc); - return parse_version(); -} - -int gl3wIsSupported(int major, int minor) -{ - if (major < 3) - return 0; - if (version.major == major) - return version.minor >= minor; - return version.major >= major; -} - -GL3WglProc gl3wGetProcAddress(const char *proc) -{ - return get_proc(proc); -} - -static const char *proc_names[] = { -}; - -GL3W_API union GL3WProcs gl3wProcs; - -static void load_procs(GL3WGetProcAddressProc proc) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(proc_names); i++) - gl3wProcs.ptr[i] = proc(proc_names[i]); -} diff --git a/template/gl3w.h b/template/gl3w.h index 9ff1bda..7e35bb7 100644 --- a/template/gl3w.h +++ b/template/gl3w.h @@ -1,6 +1,6 @@ /* - * This file was generated with gl3w_gen.py, part of gl3w - * (hosted at https://github.com/skaslev/gl3w) + * This file was generated with gl3w_gen.py, part of imgl3w + * (hosted at https://github.com/dearimgui/gl3w_stripped) * * This is free and unencumbered software released into the public domain. * @@ -26,9 +26,39 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +// We embed our own OpenGL loader to not require user to provide their own or to have to use ours, which proved to be endless problems for users. +// Our loader is custom-generated, based on gl3w but automatically filtered to only include enums/functions that we use in this source file. +// Regenerate with: python gl3w_gen.py --imgui-dir /path/to/imgui/ +// see https://github.com/dearimgui/gl3w_stripped for more info. #ifndef __gl3w_h_ #define __gl3w_h_ +// Adapted from KHR/khrplatform.h to avoid including entire file. +typedef float khronos_float_t; +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; +#ifdef _WIN64 +typedef signed long long int khronos_intptr_t; +typedef signed long long int khronos_ssize_t; +#else +typedef signed long int khronos_intptr_t; +typedef signed long int khronos_ssize_t; +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +typedef signed __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#elif (defined(__clang__) || defined(__GNUC__)) && (__cplusplus < 201100) +#include +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#else +typedef signed long long khronos_int64_t; +typedef unsigned long long khronos_uint64_t; +#endif + #include #ifndef GL3W_API @@ -52,14 +82,14 @@ typedef void (*GL3WglProc)(void); typedef GL3WglProc (*GL3WGetProcAddressProc)(const char *proc); /* gl3w api */ -GL3W_API int gl3wInit(void); -GL3W_API int gl3wInit2(GL3WGetProcAddressProc proc); -GL3W_API int gl3wIsSupported(int major, int minor); -GL3W_API GL3WglProc gl3wGetProcAddress(const char *proc); +GL3W_API int imgl3wInit(void); +GL3W_API int imgl3wInit2(GL3WGetProcAddressProc proc); +GL3W_API int imgl3wIsSupported(int major, int minor); +GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc); /* gl3w internal state */ -GL3W_API extern union GL3WProcs gl3wProcs; +GL3W_API extern union GL3WProcs imgl3wProcs; /* OpenGL functions */ @@ -68,3 +98,145 @@ GL3W_API extern union GL3WProcs gl3wProcs; #endif #endif + +#ifdef IMGL3W_IMPL +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include + +static HMODULE libgl; +typedef PROC(__stdcall* GL3WglGetProcAddr)(LPCSTR); +static GL3WglGetProcAddr wgl_get_proc_address; + +static int open_libgl(void) +{ + libgl = LoadLibraryA("opengl32.dll"); + if (!libgl) + return GL3W_ERROR_LIBRARY_OPEN; + wgl_get_proc_address = (GL3WglGetProcAddr)GetProcAddress(libgl, "wglGetProcAddress"); + return GL3W_OK; +} + +static void close_libgl(void) { FreeLibrary(libgl); } +static GL3WglProc get_proc(const char *proc) +{ + GL3WglProc res; + res = (GL3WglProc)wgl_get_proc_address(proc); + if (!res) + res = (GL3WglProc)GetProcAddress(libgl, proc); + return res; +} +#elif defined(__APPLE__) +#include + +static void *libgl; +static int open_libgl(void) +{ + libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY | RTLD_LOCAL); + if (!libgl) + return GL3W_ERROR_LIBRARY_OPEN; + return GL3W_OK; +} + +static void close_libgl(void) { dlclose(libgl); } + +static GL3WglProc get_proc(const char *proc) +{ + GL3WglProc res; + *(void **)(&res) = dlsym(libgl, proc); + return res; +} +#else +#include + +static void *libgl; +static GL3WglProc (*glx_get_proc_address)(const GLubyte *); + +static int open_libgl(void) +{ + libgl = dlopen("libGL.so.1", RTLD_LAZY | RTLD_LOCAL); + if (!libgl) + return GL3W_ERROR_LIBRARY_OPEN; + *(void **)(&glx_get_proc_address) = dlsym(libgl, "glXGetProcAddressARB"); + return GL3W_OK; +} + +static void close_libgl(void) { dlclose(libgl); } + +static GL3WglProc get_proc(const char *proc) +{ + GL3WglProc res; + res = glx_get_proc_address((const GLubyte *)proc); + if (!res) + *(void **)(&res) = dlsym(libgl, proc); + return res; +} +#endif + +static struct { int major, minor; } version; + +static int parse_version(void) +{ + if (!glGetIntegerv) + return GL3W_ERROR_INIT; + glGetIntegerv(GL_MAJOR_VERSION, &version.major); + glGetIntegerv(GL_MINOR_VERSION, &version.minor); + if (version.major < 3) + return GL3W_ERROR_OPENGL_VERSION; + return GL3W_OK; +} + +static void load_procs(GL3WGetProcAddressProc proc); + +int imgl3wInit(void) +{ + int res = open_libgl(); + if (res) + return res; + atexit(close_libgl); + return imgl3wInit2(get_proc); +} + +int imgl3wInit2(GL3WGetProcAddressProc proc) +{ + load_procs(proc); + return parse_version(); +} + +int imgl3wIsSupported(int major, int minor) +{ + if (major < 3) + return 0; + if (version.major == major) + return version.minor >= minor; + return version.major >= major; +} + +GL3WglProc imgl3wGetProcAddress(const char *proc) { return get_proc(proc); } + +static const char *proc_names[] = { +}; + +GL3W_API union GL3WProcs imgl3wProcs; + +static void load_procs(GL3WGetProcAddressProc proc) +{ + size_t i; + for (i = 0; i < ARRAY_SIZE(proc_names); i++) + imgl3wProcs.ptr[i] = proc(proc_names[i]); +} + +#ifdef __cplusplus +} +#endif +#endif