Skip to content

Commit

Permalink
gl3w_gen.py generates amalgamated and trimmed header.
Browse files Browse the repository at this point in the history
(reworked 2024-05-07 to try unfucking this project)

Clean up generator arguments and ignore symbols mentioned in comments, but unused in source code.

(reworked 2024-05-07 to try unfucking this project)
# Conflicts:
#	gl3w_gen.py

Change name of gl3wProcs to avoid symbol collisions.

ocornut/imgui#4445 (comment)

# Conflicts:
#	gl3w_gen.py

Simplify API scan and add extra_symbols.txt for including additional symbols.

# Conflicts:
#	gl3w_gen.py
  • Loading branch information
rokups authored and ocornut committed May 7, 2024
1 parent fcd3715 commit 9b4deac
Show file tree
Hide file tree
Showing 5 changed files with 306 additions and 225 deletions.
17 changes: 12 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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::

Expand All @@ -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;
}
Expand All @@ -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
Expand Down Expand Up @@ -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/
3 changes: 3 additions & 0 deletions extra_symbols.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
glReadPixels
GL_PACK_ALIGNMENT
GL_FRAMEBUFFER_SRGB
137 changes: 112 additions & 25 deletions gl3w_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import argparse
import os
import os.path
import re

# Try to import Python 3 library urllib.request
Expand Down Expand Up @@ -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 '<empty>'


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'))
Expand All @@ -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 <GL/glcorearb.h>', glcorearb)

# Remove KHR/khrplatform.h include, we use our own minimal typedefs from it
h_template = h_template.replace('#include <KHR/khrplatform.h>', '')

# 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)
Loading

0 comments on commit 9b4deac

Please sign in to comment.