2
# -*- coding: utf-8 -*-
4
# Copyright © 2013 Intel Corporation
6
# Permission is hereby granted, free of charge, to any person obtaining a
7
# copy of this software and associated documentation files (the "Software"),
8
# to deal in the Software without restriction, including without limitation
9
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
# and/or sell copies of the Software, and to permit persons to whom the
11
# Software is furnished to do so, subject to the following conditions:
13
# The above copyright notice and this permission notice (including the next
14
# paragraph) shall be included in all copies or substantial portions of the
17
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
27
import xml.etree.ElementTree as ET
31
class GLProvider(object):
32
def __init__(self, condition, condition_name, loader, name):
33
# C code for determining if this function is available.
34
# (e.g. epoxy_is_desktop_gl() && epoxy_gl_version() >= 20
35
self.condition = condition
37
# A string (possibly with spaces) describing the condition.
38
self.condition_name = condition_name
40
# The loader for getting the symbol -- either dlsym or
41
# getprocaddress. This is a python format string to generate
42
# C code, given self.name.
45
# The name of the function to be loaded (possibly an
46
# ARB/EXT/whatever-decorated variant).
49
# This is the C enum name we'll use for referring to this provider.
50
self.enum = condition_name
51
self.enum = self.enum.replace(' ', '_')
52
self.enum = self.enum.replace('\\"', '')
53
self.enum = self.enum.replace('.', '_')
55
class GLFunction(object):
56
def __init__(self, ret_type, name):
58
self.ptr_type = 'PFN' + name.upper() + 'PROC'
59
self.ret_type = ret_type
63
# These are functions with hand-written wrapper code in
64
# dispatch_common.c. Their dispatch entries are replaced with
65
# non-public symbols with a "_unwrapped" suffix.
70
'wglMakeContextCurrentEXT',
71
'wglMakeContextCurrentARB',
72
'wglMakeAssociatedContextCurrentAMD',
75
if name in wrapped_functions:
76
self.wrapped_name = name + '_unwrapped'
79
self.wrapped_name = name
80
self.public = 'PUBLIC '
82
# This is the string of C code for passing through the
83
# arguments to the function.
86
# This is the string of C code for declaring the arguments
88
self.args_decl = 'void'
90
# This is the string name of the function that this is an
91
# alias of, or self.name. This initially comes from the
92
# registry, and may get updated if it turns out our alias is
93
# itself an alias (for example glFramebufferTextureEXT ->
94
# glFramebufferTextureARB -> glFramebufferTexture)
95
self.alias_name = name
97
# After alias resolution, this is the function that this is an
99
self.alias_func = None
101
# For the root of an alias tree, this lists the functions that
102
# are marked as aliases of it, so that it can write a resolver
106
def add_arg(self, type, name):
107
# Reword glDepthRange() arguments to avoid clashing with the
108
# "near" and "far" keywords on win32.
114
# Mac screwed up GLhandleARB and made it a void * instead of
115
# uint32_t, despite it being specced as only necessarily 32
116
# bits wide, causing portability problems all over. There are
117
# prototype conflicts between things like
118
# glAttachShader(GLuint program, GLuint shader) and
119
# glAttachObjectARB(GLhandleARB container, GLhandleARB obj),
120
# even though they are marked as aliases in the XML (and being
123
# We retain those aliases. In the x86_64 ABI, the first 6
124
# args are stored in 64-bit registers, so the calls end up
125
# being the same despite the different types. We just need to
126
# add a cast to uintptr_t to shut up the compiler.
127
if type == 'GLhandleARB':
128
assert(len(self.args) < 6)
129
arg_list_name = '(uintptr_t)' + name
133
self.args.append((type, name))
134
if self.args_decl == 'void':
135
self.args_list = arg_list_name
136
self.args_decl = type + ' ' + name
138
self.args_list += ', ' + arg_list_name
139
self.args_decl += ', ' + type + ' ' + name
141
def add_provider(self, condition, loader, condition_name):
142
self.providers[condition_name] = GLProvider(condition, condition_name,
145
def add_alias(self, ext):
146
assert self.alias_func is None
148
self.alias_exts.append(ext)
149
ext.alias_func = self
151
class Generator(object):
152
def __init__(self, target):
156
self.sorted_function = None
157
self.max_enum_name_len = 1
158
self.copyright_comment = None
162
# GL versions named in the registry, which we should generate
164
self.supported_versions = set()
166
# Extensions named in the registry, which we should generate
168
self.supported_extensions = set()
170
# Dictionary mapping human-readable names of providers to a C
171
# enum token that will be used to reference those names, to
172
# reduce generated binary size.
173
self.provider_enum = {}
175
# Dictionary mapping human-readable names of providers to C
176
# code to detect if it's present.
177
self.provider_condition = {}
179
# Dictionary mapping human-readable names of providers to
180
# format strings for fetching the function pointer when
181
# provided the name of the symbol to be requested.
182
self.provider_loader = {}
184
def all_text_until_element_name(self, element, element_name):
187
if element.text is not None:
190
for child in element:
191
if child.tag == element_name:
200
self.out_file.write(text)
202
def outln(self, text):
203
self.out_file.write(text + '\n')
205
def parse_typedefs(self, reg):
206
for t in reg.findall('types/type'):
207
if 'name' in t.attrib and t.attrib['name'] not in {'GLhandleARB'}:
210
# The gles1/gles2-specific types are redundant
211
# declarations, and the different types used for them (int
212
# vs int32_t) caused problems on win32 builds.
217
if t.text is not None:
218
self.typedefs += t.text
222
self.typedefs += child.text
224
self.typedefs += child.tail
225
self.typedefs += '\n'
227
def parse_enums(self, reg):
228
for enum in reg.findall('enums/enum'):
229
name = enum.get('name')
231
# wgl.xml's 0xwhatever definitions end up colliding with
232
# wingdi.h's decimal definitions of these.
233
if ('WGL_SWAP_OVERLAY' in name or
234
'WGL_SWAP_UNDERLAY' in name or
235
'WGL_SWAP_MAIN_PLANE' in name):
238
self.max_enum_name_len = max(self.max_enum_name_len, len(name))
239
self.enums[name] = enum.get('value')
241
def get_function_return_type(self, proto):
242
# Everything up to the start of the name element is the return type.
243
return self.all_text_until_element_name(proto, 'name').strip()
245
def parse_function_definitions(self, reg):
246
for command in reg.findall('commands/command'):
247
proto = command.find('proto')
248
name = proto.find('name').text
249
ret_type = self.get_function_return_type(proto)
251
func = GLFunction(ret_type, name)
253
for arg in command.findall('param'):
254
func.add_arg(self.all_text_until_element_name(arg, 'name').strip(),
255
arg.find('name').text)
257
alias = command.find('alias')
258
if alias is not None:
259
# Note that some alias references appear before the
260
# target command is defined (glAttachObjectARB() ->
261
# glAttachShader(), for example).
262
func.alias_name = alias.get('name')
264
self.functions[name] = func
266
def drop_weird_glx_functions(self):
267
# Drop a few ancient SGIX GLX extensions that use types not defined
268
# anywhere in Xlib. In glxext.h, they're protected by #ifdefs for the
269
# headers that defined them.
270
weird_functions = [name for name, func in self.functions.items()
271
if 'VLServer' in func.args_decl
272
or 'DMparams' in func.args_decl]
274
for name in weird_functions:
275
del self.functions[name]
277
def resolve_aliases(self):
278
for func in self.functions.values():
279
# Find the root of the alias tree, and add ourselves to it.
280
if func.alias_name != func.name:
282
while alias_func.alias_name != alias_func.name:
283
alias_func = self.functions[alias_func.alias_name]
284
func.alias_name = alias_func.name
285
func.alias_func = alias_func
286
alias_func.alias_exts.append(func)
288
def prepare_provider_enum(self):
289
self.provider_enum = {}
291
# We assume that for any given provider, all functions using
292
# it will have the same loader. This lets us generate a
293
# general C function for detecting conditions and calling the
294
# dlsym/getprocaddress, and have our many resolver stubs just
295
# call it with a table of values.
296
for func in self.functions.values():
297
for provider in func.providers.values():
298
if provider.condition_name in self.provider_enum:
299
assert(self.provider_condition[provider.condition_name] == provider.condition)
300
assert(self.provider_loader[provider.condition_name] == provider.loader)
303
self.provider_enum[provider.condition_name] = provider.enum;
304
self.provider_condition[provider.condition_name] = provider.condition;
305
self.provider_loader[provider.condition_name] = provider.loader;
307
def sort_functions(self):
308
self.sorted_functions = sorted(self.functions.values(), key=lambda func:func.name)
310
def process_require_statements(self, feature, condition, loader, human_name):
311
for command in feature.findall('require/command'):
312
name = command.get('name')
314
# wgl.xml describes 6 functions in WGL 1.0 that are in
315
# gdi32.dll instead of opengl32.dll, and we would need to
316
# change up our symbol loading to support that. Just
317
# don't wrap those functions.
318
if self.target == 'wgl' and 'wgl' not in name:
319
del self.functions[name]
322
func = self.functions[name]
323
func.add_provider(condition, loader, human_name)
325
def parse_function_providers(self, reg):
326
for feature in reg.findall('feature'):
327
api = feature.get('api') # string gl, gles1, gles2, glx
328
m = re.match('([0-9])\.([0-9])', feature.get('number'))
329
version = int(m.group(1)) * 10 + int(m.group(2))
331
self.supported_versions.add(feature.get('name'))
334
human_name = 'Desktop OpenGL {0}'.format(feature.get('number'))
335
condition = 'epoxy_is_desktop_gl()'
337
loader = 'epoxy_get_core_proc_address({0}, {1})'.format('{0}', version)
339
condition += ' && epoxy_conservative_gl_version() >= {0}'.format(version)
341
human_name = 'OpenGL ES {0}'.format(feature.get('number'))
342
condition = '!epoxy_is_desktop_gl() && epoxy_gl_version() >= {0}'.format(version)
345
loader = 'epoxy_gles2_dlsym({0})'
347
loader = 'epoxy_get_proc_address({0})'
349
human_name = 'OpenGL ES 1.0'
350
condition = '!epoxy_is_desktop_gl() && epoxy_gl_version() >= 10 && epoxy_gl_version() < 20'
351
loader = 'epoxy_gles1_dlsym({0})'
353
human_name = 'GLX {0}'.format(version)
354
# We could just always use GPA for loading everything
355
# but glXGetProcAddress(), but dlsym() is a more
358
condition = 'epoxy_conservative_glx_version() >= {0}'.format(version)
359
loader = 'glXGetProcAddress((const GLubyte *){0})'
362
loader = 'epoxy_glx_dlsym({0})'
364
human_name = 'EGL {0}'.format(version)
366
condition = 'epoxy_conservative_egl_version() >= {0}'.format(version)
369
# All EGL core entrypoints must be dlsym()ed out --
370
# eglGetProcAdddress() will return NULL.
371
loader = 'epoxy_egl_dlsym({0})'
373
human_name = 'WGL {0}'.format(version)
375
loader = 'epoxy_gl_dlsym({0})'
377
sys.exit('unknown API: "{0}"'.format(api))
379
self.process_require_statements(feature, condition, loader, human_name)
381
for extension in reg.findall('extensions/extension'):
382
extname = extension.get('name')
384
self.supported_extensions.add(extname)
386
# 'supported' is a set of strings like gl, gles1, gles2,
387
# or glx, which are separated by '|'
388
apis = extension.get('supported').split('|')
390
human_name = 'GLX extension \\"{0}\\"'.format(extname)
391
condition = 'epoxy_conservative_has_glx_extension("{0}")'.format(extname)
392
loader = 'glXGetProcAddress((const GLubyte *){0})'
393
self.process_require_statements(extension, condition, loader, human_name)
395
human_name = 'EGL extension \\"{0}\\"'.format(extname)
396
condition = 'epoxy_conservative_has_egl_extension("{0}")'.format(extname)
397
loader = 'eglGetProcAddress({0})'
398
self.process_require_statements(extension, condition, loader, human_name)
400
human_name = 'WGL extension \\"{0}\\"'.format(extname)
401
condition = 'epoxy_conservative_has_wgl_extension("{0}")'.format(extname)
402
loader = 'wglGetProcAddress({0})'
403
self.process_require_statements(extension, condition, loader, human_name)
404
if {'gl', 'gles1', 'gles2'}.intersection(apis):
405
human_name = 'GL extension \\"{0}\\"'.format(extname)
406
condition = 'epoxy_conservative_has_gl_extension("{0}")'.format(extname)
407
loader = 'epoxy_get_proc_address({0})'
408
self.process_require_statements(extension, condition, loader, human_name)
410
def fixup_bootstrap_function(self, name, loader):
411
# We handle glGetString(), glGetIntegerv(), and
412
# glXGetProcAddressARB() specially, because we need to use
413
# them in the process of deciding on loaders for resolving,
414
# and the naive code generation would result in their
415
# resolvers calling their own resolvers.
416
if name not in self.functions:
419
func = self.functions[name]
421
func.add_provider('true', loader, 'always present')
423
def parse(self, file):
425
if reg.find('comment') != None:
426
self.copyright_comment = reg.find('comment').text
428
self.copyright_comment = ''
429
self.parse_typedefs(reg)
430
self.parse_enums(reg)
431
self.parse_function_definitions(reg)
432
self.parse_function_providers(reg)
434
def write_copyright_comment_body(self):
435
for line in self.copyright_comment.splitlines():
438
self.outln(' * ' + line)
440
def write_enums(self):
441
for name in sorted(self.supported_versions):
442
self.outln('#define {0} 1'.format(name))
445
for name in sorted(self.supported_extensions):
446
self.outln('#define {0} 1'.format(name))
449
# We want to sort by enum number (which puts a bunch of things
450
# in a logical order), then by name after that, so we do those
451
# sorts in reverse. This is still way uglier than doing some
452
# sort based on what version/extensions things are introduced
453
# in, but we haven't paid any attention to those attributes
455
sorted_by_name = sorted(self.enums.keys())
456
sorted_by_number = sorted(sorted_by_name, key=lambda name: self.enums[name])
457
for name in sorted_by_number:
458
self.outln('#define ' + name.ljust(self.max_enum_name_len + 3) + self.enums[name] + '')
460
def write_function_ptr_typedefs(self):
461
for func in self.sorted_functions:
462
self.outln('typedef {0} (GLAPIENTRY *{1})({2});'.format(func.ret_type,
466
def write_header_header(self, file):
467
self.out_file = open(file, 'w')
469
self.outln('/* GL dispatch header.')
470
self.outln(' * This is code-generated from the GL API XML files from Khronos.')
471
self.write_copyright_comment_body()
475
self.outln('#pragma once')
477
self.outln('#include <inttypes.h>')
478
self.outln('#include <stddef.h>')
481
def write_header(self, file):
482
self.write_header_header(file)
484
if self.target != "gl":
485
self.outln('#include "epoxy/gl.h"')
486
if self.target == "egl":
487
self.outln('#include "EGL/eglplatform.h"')
489
# Add some ridiculous inttypes.h redefinitions that are
490
# from khrplatform.h and not included in the XML. We
491
# don't directly include khrplatform.h because it's not
492
# present on many systems, and coming up with #ifdefs to
493
# decide when it's not present would be hard.
494
self.outln('#define __khrplatform_h_ 1')
495
self.outln('typedef int8_t khronos_int8_t;')
496
self.outln('typedef int16_t khronos_int16_t;')
497
self.outln('typedef int32_t khronos_int32_t;')
498
self.outln('typedef int64_t khronos_int64_t;')
499
self.outln('typedef uint8_t khronos_uint8_t;')
500
self.outln('typedef uint16_t khronos_uint16_t;')
501
self.outln('typedef uint32_t khronos_uint32_t;')
502
self.outln('typedef uint64_t khronos_uint64_t;')
503
self.outln('typedef float khronos_float_t;')
504
self.outln('typedef long khronos_intptr_t;')
505
self.outln('typedef long khronos_ssize_t;')
506
self.outln('typedef unsigned long khronos_usize_t;')
507
self.outln('typedef uint64_t khronos_utime_nanoseconds_t;')
508
self.outln('typedef int64_t khronos_stime_nanoseconds_t;')
509
self.outln('#define KHRONOS_MAX_ENUM 0x7FFFFFFF')
510
self.outln('typedef enum {')
511
self.outln(' KHRONOS_FALSE = 0,')
512
self.outln(' KHRONOS_TRUE = 1,')
513
self.outln(' KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM')
514
self.outln('} khronos_boolean_enum_t;')
515
self.outln('typedef uintptr_t khronos_uintptr_t;')
517
if self.target == "glx":
518
self.outln('#include <X11/Xlib.h>')
519
self.outln('#include <X11/Xutil.h>')
521
self.out(self.typedefs)
525
self.write_function_ptr_typedefs()
527
for func in self.sorted_functions:
528
self.outln('extern EPOXYAPIENTRY {0} (*epoxy_{1})({2});'.format(func.ret_type,
533
for func in self.sorted_functions:
534
self.outln('#define {0} epoxy_{0}'.format(func.name))
536
def write_function_ptr_resolver(self, func):
537
self.outln('static {0}'.format(func.ptr_type))
538
self.outln('epoxy_{0}_resolver(void)'.format(func.name))
542
# Make a local list of all the providers for this alias group
543
for provider in func.providers.values():
544
providers.append(provider)
545
for alias_func in func.alias_exts:
546
for provider in alias_func.providers.values():
547
providers.append(provider)
549
# Add some partial aliases of a few functions. These are ones
550
# that aren't quite aliases, because of some trivial behavior
551
# difference (like whether to produce an error for a
552
# non-Genned name), but where we'd like to fall back to the
553
# similar function if the proper one isn't present.
555
'glBindVertexArray' : 'glBindVertexArrayAPPLE',
556
'glBindVertexArrayAPPLE' : 'glBindVertexArray',
557
'glBindFramebuffer' : 'glBindFramebufferEXT',
558
'glBindFramebufferEXT' : 'glBindFramebuffer',
560
if func.name in half_aliases:
561
alias_func = self.functions[half_aliases[func.name]]
562
for provider in alias_func.providers.values():
563
providers.append(provider)
565
if len(providers) != 1:
566
self.outln(' static const enum {0}_provider providers[] = {{'.format(self.target))
567
for provider in providers:
568
self.outln(' {0},'.format(provider.enum))
569
self.outln(' {0}_provider_terminator'.format(self.target))
572
self.outln(' static const uint16_t entrypoints[] = {')
573
for provider in providers:
574
self.outln(' {0} /* "{1}" */,'.format(self.entrypoint_string_offset[provider.name], provider.name))
577
self.outln(' return {0}_provider_resolver(entrypoint_strings + {1} /* "{2}" */,'.format(self.target,
578
self.entrypoint_string_offset[func.name],
580
self.outln(' providers, entrypoints);')
582
assert(providers[0].name == func.name)
583
self.outln(' return {0}_single_resolver({1}, {2} /* {3} */);'.format(self.target,
585
self.entrypoint_string_offset[func.name],
590
def write_dispatch_table_thunk(self, func):
591
# Writes out the thunk that fetches the (win32) dispatch table
592
# and calls through its entrypoint.
594
dispatch_table_entry = 'dispatch_table->p{0}'.format(func.alias_name)
596
self.outln('static __stdcall {0}'.format(func.ret_type))
597
self.outln('epoxy_{0}_dispatch_table_thunk({1})'.format(func.wrapped_name, func.args_decl))
599
self.outln(' struct dispatch_table *dispatch_table = get_dispatch_table();')
601
if func.ret_type == 'void':
602
self.outln(' {0}({1});'.format(dispatch_table_entry, func.args_list))
604
self.outln(' return {0}({1});'.format(dispatch_table_entry, func.args_list))
608
def write_dispatch_table_rewrite_stub(self, func):
609
# Writes out the initial dispatch table function pointer value
610
# that that resolves, writes the resolved value into the
611
# dispatch table, and calls down to it.
613
dispatch_table_entry = 'dispatch_table->p{0}'.format(func.name)
615
self.outln('static GLAPIENTRY {0}'.format(func.ret_type))
616
self.outln('epoxy_{0}_rewrite_stub({1})'.format(func.name, func.args_decl))
618
self.outln(' struct dispatch_table *dispatch_table = get_dispatch_table();')
620
self.outln(' dispatch_table->p{0} = epoxy_{0}_resolver();'.format(func.name))
623
if func.ret_type == 'void':
624
self.outln(' dispatch_table->p{0}({1});'.format(func.name, func.args_list))
626
self.outln(' return dispatch_table->p{0}({1});'.format(func.name, func.args_list))
630
def write_linux_function_pointer(self, func):
631
# Writes out the function for resolving and updating the
632
# global function pointer, plus the actual global function
633
# pointer initializer.
635
self.outln('static {0}'.format(func.ret_type))
636
self.outln('epoxy_{0}_rewrite_ptr({1})'.format(func.wrapped_name,
639
self.outln(' epoxy_{0} = (void *)epoxy_{1}_resolver();'.format(func.wrapped_name,
642
if func.ret_type == 'void':
643
self.outln(' epoxy_{0}({1});'.format(func.wrapped_name,
646
self.outln(' return epoxy_{0}({1});'.format(func.wrapped_name,
651
self.outln('{0}{1} epoxy_{2} = epoxy_{2}_rewrite_ptr;'.format(func.public,
656
def write_win32_function_pointer(self, func):
657
self.outln('{0}{1} epoxy_{2} = epoxy_{2}_dispatch_table_thunk;'.format(func.public,
662
def write_provider_enums(self):
663
# Writes the enum declaration for the list of providers
664
# supported by gl_provider_resolver()
666
self.outln('enum {0}_provider {{'.format(self.target))
668
sorted_providers = sorted(self.provider_enum.keys())
670
# We always put a 0 enum first so that we can have a
671
# terminator in our arrays
672
self.outln(' {0}_provider_terminator = 0,'.format(self.target))
674
for human_name in sorted_providers:
675
enum = self.provider_enum[human_name]
676
self.outln(' {0},'.format(enum))
680
def write_provider_enum_strings(self):
681
# Writes the mapping from enums to the strings describing them
682
# for epoxy_print_failure_reasons().
684
self.outln('static const char *enum_strings[] = {')
686
sorted_providers = sorted(self.provider_enum.keys())
688
for human_name in sorted_providers:
689
enum = self.provider_enum[human_name]
690
self.outln(' [{0}] = "{1}",'.format(enum, human_name))
694
def write_entrypoint_strings(self):
695
self.entrypoint_string_offset = {}
697
self.outln('static const char entrypoint_strings[] = ')
699
for func in self.sorted_functions:
700
if func.name not in self.entrypoint_string_offset:
701
self.entrypoint_string_offset[func.name] = offset
702
offset += len(func.name) + 1
703
self.outln(' "{0}\\0"'.format(func.name))
705
# We're using uint16_t for the offsets.
706
assert(offset < 65536)
709
def write_provider_resolver(self):
710
self.outln('static void *{0}_provider_resolver(const char *name,'.format(self.target))
711
self.outln(' const enum {0}_provider *providers,'.format(self.target))
712
self.outln(' const uint16_t *entrypoints)')
714
self.outln(' int i;')
716
self.outln(' for (i = 0; providers[i] != {0}_provider_terminator; i++) {{'.format(self.target))
717
self.outln(' switch (providers[i]) {')
719
for human_name in sorted(self.provider_enum.keys()):
720
enum = self.provider_enum[human_name]
721
self.outln(' case {0}:'.format(enum))
722
self.outln(' if ({0})'.format(self.provider_condition[human_name]))
723
self.outln(' return {0};'.format(self.provider_loader[human_name]).format("entrypoint_strings + entrypoints[i]"))
724
self.outln(' break;')
726
self.outln(' case {0}_provider_terminator:'.format(self.target))
727
self.outln(' abort(); /* Not reached */')
732
# If the function isn't provided by any known extension, print
733
# something useful for the poor application developer before
734
# aborting. (In non-epoxy GL usage, the app developer would
735
# call into some blank stub function and segfault).
736
self.outln(' epoxy_print_failure_reasons(name, enum_strings, (const int *)providers);')
737
self.outln(' abort();')
742
single_resolver_proto = '{0}_single_resolver(enum {0}_provider provider, uint16_t entrypoint_offset)'.format(self.target)
743
self.outln('static void *')
744
self.outln('{0} __attribute__((noinline));'.format(single_resolver_proto))
746
self.outln('static void *')
747
self.outln('{0}'.format(single_resolver_proto))
749
self.outln(' enum {0}_provider providers[] = {{'.format(self.target))
750
self.outln(' provider,')
751
self.outln(' {0}_provider_terminator'.format(self.target))
753
self.outln(' return {0}_provider_resolver(entrypoint_strings + entrypoint_offset,'.format(self.target))
754
self.outln(' providers, &entrypoint_offset);')
758
def write_source(self, file):
759
self.out_file = open(file, 'w')
761
self.outln('/* GL dispatch code.')
762
self.outln(' * This is code-generated from the GL API XML files from Khronos.')
763
self.write_copyright_comment_body()
766
self.outln('#include <stdlib.h>')
767
self.outln('#include <stdio.h>')
769
self.outln('#include "dispatch_common.h"')
770
self.outln('#include "epoxy/{0}.h"'.format(self.target))
773
self.outln('struct dispatch_table {')
774
for func in self.sorted_functions:
775
# Aliases don't get their own slot, since they use a shared resolver.
776
if func.alias_name == func.name:
777
self.outln(' {0} p{1};'.format(func.ptr_type, func.name))
781
# Early declaration, so we can declare the real thing at the
782
# bottom. (I want the function_ptr_resolver as the first
783
# per-GL-call code, since it's the most interesting to see
784
# when you search for the implementation of a call)
785
self.outln('#if USING_DISPATCH_TABLE')
786
self.outln('static inline struct dispatch_table *')
787
self.outln('get_dispatch_table(void);')
791
self.write_provider_enums()
792
self.write_provider_enum_strings()
793
self.write_entrypoint_strings()
794
self.write_provider_resolver()
796
for func in self.sorted_functions:
797
if not func.alias_func:
798
self.write_function_ptr_resolver(func)
800
self.outln('#if USING_DISPATCH_TABLE')
802
for func in self.sorted_functions:
803
if not func.alias_func:
804
self.write_dispatch_table_rewrite_stub(func)
806
for func in self.sorted_functions:
807
self.write_dispatch_table_thunk(func)
809
self.outln('static struct dispatch_table resolver_table = {')
810
for func in self.sorted_functions:
811
# Aliases don't get their own slot, since they use a shared resolver.
812
if func.alias_name == func.name:
813
self.outln(' .p{0} = epoxy_{0}_rewrite_stub,'.format(func.name))
817
self.outln('uint32_t {0}_tls_index;'.format(self.target))
818
self.outln('uint32_t {0}_tls_size = sizeof(struct dispatch_table);'.format(self.target))
821
self.outln('static inline struct dispatch_table *')
822
self.outln('get_dispatch_table(void)')
824
self.outln(' return TlsGetValue({0}_tls_index);'.format(self.target))
829
self.outln('{0}_init_dispatch_table(void)'.format(self.target))
831
self.outln(' struct dispatch_table *dispatch_table = get_dispatch_table();')
832
self.outln(' memcpy(dispatch_table, &resolver_table, sizeof(resolver_table));')
836
for func in self.sorted_functions:
837
self.write_win32_function_pointer(func)
839
self.outln('#else /* !USING_DISPATCH_TABLE */')
841
for func in self.sorted_functions:
842
self.write_linux_function_pointer(func)
844
self.outln('#endif /* !USING_DISPATCH_TABLE */')
846
argparser = argparse.ArgumentParser(description='Generate GL dispatch wrappers.')
847
argparser.add_argument('files', metavar='file.xml', nargs='+', help='GL API XML files to be parsed')
848
argparser.add_argument('--dir', metavar='dir', required=True, help='Destination directory')
849
args = argparser.parse_args()
851
srcdir = args.dir + '/src/'
852
incdir = args.dir + '/include/epoxy/'
854
for file in args.files:
855
name = os.path.basename(file).split('.xml')[0]
856
generator = Generator(name)
857
generator.parse(file)
858
generator.drop_weird_glx_functions()
859
generator.sort_functions()
860
generator.resolve_aliases()
861
generator.fixup_bootstrap_function('glGetString',
862
'epoxy_get_bootstrap_proc_address({0})')
863
generator.fixup_bootstrap_function('glGetIntegerv',
864
'epoxy_get_bootstrap_proc_address({0})')
866
# While this is technically exposed as a GLX extension, it's
867
# required to be present as a public symbol by the Linux OpenGL
869
generator.fixup_bootstrap_function('glXGetProcAddress',
870
'epoxy_glx_dlsym({0})')
872
generator.prepare_provider_enum()
874
generator.write_header(incdir + name + '_generated.h')
875
generator.write_source(srcdir + name + '_generated_dispatch.c')