gen_dispatch.py revision e52adb7b
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Copyright © 2013 Intel Corporation
5#
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:
12#
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
15# Software.
16#
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
23# IN THE SOFTWARE.
24
25import sys
26import argparse
27import xml.etree.ElementTree as ET
28import re
29import os
30
31class 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
36
37        # A string (possibly with spaces) describing the condition.
38        self.condition_name = condition_name
39
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.
43        self.loader = loader
44
45        # The name of the function to be loaded (possibly an
46        # ARB/EXT/whatever-decorated variant).
47        self.name = name
48
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('.', '_')
54
55class GLFunction(object):
56    def __init__(self, ret_type, name):
57        self.name = name
58        self.ptr_type = 'PFN' + name.upper() + 'PROC'
59        self.ret_type = ret_type
60        self.providers = {}
61        self.args = []
62
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.
66        wrapped_functions = {
67            'glBegin',
68            'glEnd',
69            'wglMakeCurrent',
70            'wglMakeContextCurrentEXT',
71            'wglMakeContextCurrentARB',
72            'wglMakeAssociatedContextCurrentAMD',
73        }
74
75        if name in wrapped_functions:
76            self.wrapped_name = name + '_unwrapped'
77            self.public = ''
78        else:
79            self.wrapped_name = name
80            self.public = 'PUBLIC '
81
82        # This is the string of C code for passing through the
83        # arguments to the function.
84        self.args_list = ''
85
86        # This is the string of C code for declaring the arguments
87        # list.
88        self.args_decl = 'void'
89
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
96
97        # After alias resolution, this is the function that this is an
98        # alias of.
99        self.alias_func = None
100
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
103        # for all of them.
104        self.alias_exts = []
105
106    def add_arg(self, type, name):
107        # Reword glDepthRange() arguments to avoid clashing with the
108        # "near" and "far" keywords on win32.
109        if name == "near":
110            name = "hither"
111        elif name == "far":
112            name = "yon"
113
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
121        # aliases in Mesa).
122        #
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
130        else:
131            arg_list_name = name
132
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
137        else:
138            self.args_list += ', ' + arg_list_name
139            self.args_decl += ', ' + type + ' ' + name
140
141    def add_provider(self, condition, loader, condition_name):
142        self.providers[condition_name] = GLProvider(condition, condition_name,
143                                                    loader, self.name)
144
145    def add_alias(self, ext):
146        assert self.alias_func is None
147
148        self.alias_exts.append(ext)
149        ext.alias_func = self
150
151class Generator(object):
152    def __init__(self, target):
153        self.target = target
154        self.enums = {}
155        self.functions = {}
156        self.sorted_function = None
157        self.max_enum_name_len = 1
158        self.copyright_comment = None
159        self.typedefs = ''
160        self.out_file = None
161
162        # GL versions named in the registry, which we should generate
163        # #defines for.
164        self.supported_versions = set()
165
166        # Extensions named in the registry, which we should generate
167        # #defines for.
168        self.supported_extensions = set()
169
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 = {}
174
175        # Dictionary mapping human-readable names of providers to C
176        # code to detect if it's present.
177        self.provider_condition = {}
178
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 = {}
183
184    def all_text_until_element_name(self, element, element_name):
185        text = ''
186
187        if element.text is not None:
188            text += element.text
189
190        for child in element:
191            if child.tag == element_name:
192                break
193            if child.text:
194                text += child.text
195            if child.tail:
196                text += child.tail
197        return text
198
199    def out(self, text):
200        self.out_file.write(text)
201
202    def outln(self, text):
203        self.out_file.write(text + '\n')
204
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'}:
208                continue
209
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.
213            api = t.get('api')
214            if api:
215                continue
216
217            if t.text is not None:
218                self.typedefs += t.text
219
220            for child in t:
221                if child.tag == 'apientry':
222                    self.typedefs += 'APIENTRY'
223                if child.text:
224                    self.typedefs += child.text
225                if child.tail:
226                    self.typedefs += child.tail
227            self.typedefs += '\n'
228
229    def parse_enums(self, reg):
230        for enum in reg.findall('enums/enum'):
231            name = enum.get('name')
232
233            # wgl.xml's 0xwhatever definitions end up colliding with
234            # wingdi.h's decimal definitions of these.
235            if ('WGL_SWAP_OVERLAY' in name or
236                'WGL_SWAP_UNDERLAY' in name or
237                'WGL_SWAP_MAIN_PLANE' in name):
238                continue
239
240            self.max_enum_name_len = max(self.max_enum_name_len, len(name))
241            self.enums[name] = enum.get('value')
242
243    def get_function_return_type(self, proto):
244        # Everything up to the start of the name element is the return type.
245        return self.all_text_until_element_name(proto, 'name').strip()
246
247    def parse_function_definitions(self, reg):
248        for command in reg.findall('commands/command'):
249            proto = command.find('proto')
250            name = proto.find('name').text
251            ret_type = self.get_function_return_type(proto)
252
253            func = GLFunction(ret_type, name)
254
255            for arg in command.findall('param'):
256                func.add_arg(self.all_text_until_element_name(arg, 'name').strip(),
257                             arg.find('name').text)
258
259            alias = command.find('alias')
260            if alias is not None:
261                # Note that some alias references appear before the
262                # target command is defined (glAttachObjectARB() ->
263                # glAttachShader(), for example).
264                func.alias_name = alias.get('name')
265
266            self.functions[name] = func
267
268    def drop_weird_glx_functions(self):
269        # Drop a few ancient SGIX GLX extensions that use types not defined
270        # anywhere in Xlib.  In glxext.h, they're protected by #ifdefs for the
271        # headers that defined them.
272        weird_functions = [name for name, func in self.functions.items()
273                           if 'VLServer' in func.args_decl
274                           or 'DMparams' in func.args_decl]
275
276        for name in weird_functions:
277            del self.functions[name]
278
279    def resolve_aliases(self):
280        for func in self.functions.values():
281            # Find the root of the alias tree, and add ourselves to it.
282            if func.alias_name != func.name:
283                alias_func = func
284                while alias_func.alias_name != alias_func.name:
285                    alias_func = self.functions[alias_func.alias_name]
286                func.alias_name = alias_func.name
287                func.alias_func = alias_func
288                alias_func.alias_exts.append(func)
289
290    def prepare_provider_enum(self):
291        self.provider_enum = {}
292
293        # We assume that for any given provider, all functions using
294        # it will have the same loader.  This lets us generate a
295        # general C function for detecting conditions and calling the
296        # dlsym/getprocaddress, and have our many resolver stubs just
297        # call it with a table of values.
298        for func in self.functions.values():
299            for provider in func.providers.values():
300                if provider.condition_name in self.provider_enum:
301                    assert(self.provider_condition[provider.condition_name] == provider.condition)
302                    assert(self.provider_loader[provider.condition_name] == provider.loader)
303                    continue
304
305                self.provider_enum[provider.condition_name] = provider.enum;
306                self.provider_condition[provider.condition_name] = provider.condition;
307                self.provider_loader[provider.condition_name] = provider.loader;
308
309    def sort_functions(self):
310        self.sorted_functions = sorted(self.functions.values(), key=lambda func:func.name)
311
312    def process_require_statements(self, feature, condition, loader, human_name):
313        for command in feature.findall('require/command'):
314            name = command.get('name')
315
316            # wgl.xml describes 6 functions in WGL 1.0 that are in
317            # gdi32.dll instead of opengl32.dll, and we would need to
318            # change up our symbol loading to support that.  Just
319            # don't wrap those functions.
320            if self.target == 'wgl' and 'wgl' not in name:
321                del self.functions[name]
322                continue;
323
324            func = self.functions[name]
325            func.add_provider(condition, loader, human_name)
326
327    def parse_function_providers(self, reg):
328        for feature in reg.findall('feature'):
329            api = feature.get('api') # string gl, gles1, gles2, glx
330            m = re.match('([0-9])\.([0-9])', feature.get('number'))
331            version = int(m.group(1)) * 10 + int(m.group(2))
332
333            self.supported_versions.add(feature.get('name'))
334
335            if api == 'gl':
336                human_name = 'Desktop OpenGL {0}'.format(feature.get('number'))
337                condition = 'epoxy_is_desktop_gl()'
338
339                loader = 'epoxy_get_core_proc_address({0}, {1})'.format('{0}', version)
340                if version >= 11:
341                    condition += ' && epoxy_conservative_gl_version() >= {0}'.format(version)
342            elif api == 'gles2':
343                human_name = 'OpenGL ES {0}'.format(feature.get('number'))
344                condition = '!epoxy_is_desktop_gl() && epoxy_gl_version() >= {0}'.format(version)
345
346                if version <= 20:
347                    loader = 'epoxy_gles2_dlsym({0})'
348                else:
349                    loader = 'epoxy_gles3_dlsym({0})'
350            elif api == 'gles1':
351                human_name = 'OpenGL ES 1.0'
352                condition = '!epoxy_is_desktop_gl() && epoxy_gl_version() >= 10 && epoxy_gl_version() < 20'
353                loader = 'epoxy_gles1_dlsym({0})'
354            elif api == 'glx':
355                human_name = 'GLX {0}'.format(version)
356                # We could just always use GPA for loading everything
357                # but glXGetProcAddress(), but dlsym() is a more
358                # efficient lookup.
359                if version > 13:
360                    condition = 'epoxy_conservative_glx_version() >= {0}'.format(version)
361                    loader = 'glXGetProcAddress((const GLubyte *){0})'
362                else:
363                    condition = 'true'
364                    loader = 'epoxy_glx_dlsym({0})'
365            elif api == 'egl':
366                human_name = 'EGL {0}'.format(version)
367                if version > 10:
368                    condition = 'epoxy_conservative_egl_version() >= {0}'.format(version)
369                else:
370                    condition = 'true'
371                # All EGL core entrypoints must be dlsym()ed out --
372                # eglGetProcAdddress() will return NULL.
373                loader = 'epoxy_egl_dlsym({0})'
374            elif api == 'wgl':
375                human_name = 'WGL {0}'.format(version)
376                condition = 'true'
377                loader = 'epoxy_gl_dlsym({0})'
378            else:
379                sys.exit('unknown API: "{0}"'.format(api))
380
381            self.process_require_statements(feature, condition, loader, human_name)
382
383        for extension in reg.findall('extensions/extension'):
384            extname = extension.get('name')
385
386            self.supported_extensions.add(extname)
387
388            # 'supported' is a set of strings like gl, gles1, gles2,
389            # or glx, which are separated by '|'
390            apis = extension.get('supported').split('|')
391            if 'glx' in apis:
392                human_name = 'GLX extension \\"{0}\\"'.format(extname)
393                condition = 'epoxy_conservative_has_glx_extension("{0}")'.format(extname)
394                loader = 'glXGetProcAddress((const GLubyte *){0})'
395                self.process_require_statements(extension, condition, loader, human_name)
396            if 'egl' in apis:
397                human_name = 'EGL extension \\"{0}\\"'.format(extname)
398                condition = 'epoxy_conservative_has_egl_extension("{0}")'.format(extname)
399                loader = 'eglGetProcAddress({0})'
400                self.process_require_statements(extension, condition, loader, human_name)
401            if 'wgl' in apis:
402                human_name = 'WGL extension \\"{0}\\"'.format(extname)
403                condition = 'epoxy_conservative_has_wgl_extension("{0}")'.format(extname)
404                loader = 'wglGetProcAddress({0})'
405                self.process_require_statements(extension, condition, loader, human_name)
406            if {'gl', 'gles1', 'gles2'}.intersection(apis):
407                human_name = 'GL extension \\"{0}\\"'.format(extname)
408                condition = 'epoxy_conservative_has_gl_extension("{0}")'.format(extname)
409                loader = 'epoxy_get_proc_address({0})'
410                self.process_require_statements(extension, condition, loader, human_name)
411
412    def fixup_bootstrap_function(self, name, loader):
413        # We handle glGetString(), glGetIntegerv(), and
414        # glXGetProcAddressARB() specially, because we need to use
415        # them in the process of deciding on loaders for resolving,
416        # and the naive code generation would result in their
417        # resolvers calling their own resolvers.
418        if name not in self.functions:
419            return
420
421        func = self.functions[name]
422        func.providers = {}
423        func.add_provider('true', loader, 'always present')
424
425    def parse(self, file):
426        reg = ET.parse(file)
427        if reg.find('comment') != None:
428            self.copyright_comment = reg.find('comment').text
429        else:
430            self.copyright_comment = ''
431        self.parse_typedefs(reg)
432        self.parse_enums(reg)
433        self.parse_function_definitions(reg)
434        self.parse_function_providers(reg)
435
436    def write_copyright_comment_body(self):
437        for line in self.copyright_comment.splitlines():
438            if '-----' in line:
439                break
440            self.outln(' * ' + line)
441
442    def write_enums(self):
443        for name in sorted(self.supported_versions):
444            self.outln('#define {0} 1'.format(name))
445        self.outln('')
446
447        for name in sorted(self.supported_extensions):
448            self.outln('#define {0} 1'.format(name))
449        self.outln('')
450
451        # We want to sort by enum number (which puts a bunch of things
452        # in a logical order), then by name after that, so we do those
453        # sorts in reverse.  This is still way uglier than doing some
454        # sort based on what version/extensions things are introduced
455        # in, but we haven't paid any attention to those attributes
456        # for enums yet.
457        sorted_by_name = sorted(self.enums.keys())
458        sorted_by_number = sorted(sorted_by_name, key=lambda name: self.enums[name])
459        for name in sorted_by_number:
460            self.outln('#define ' + name.ljust(self.max_enum_name_len + 3) + self.enums[name] + '')
461
462    def write_function_ptr_typedefs(self):
463        for func in self.sorted_functions:
464            self.outln('typedef {0} (GLAPIENTRY *{1})({2});'.format(func.ret_type,
465                                                                    func.ptr_type,
466                                                                    func.args_decl))
467
468    def write_header_header(self, file):
469        self.out_file = open(file, 'w')
470
471        self.outln('/* GL dispatch header.')
472        self.outln(' * This is code-generated from the GL API XML files from Khronos.')
473        self.write_copyright_comment_body()
474        self.outln(' */')
475        self.outln('')
476
477        self.outln('#pragma once')
478
479        self.outln('#include <inttypes.h>')
480        self.outln('#include <stddef.h>')
481        self.outln('')
482
483    def write_header(self, file):
484        self.write_header_header(file)
485
486        if self.target != "gl":
487            self.outln('#include "epoxy/gl.h"')
488            if self.target == "egl":
489                self.outln('#include "EGL/eglplatform.h"')
490        else:
491            # Add some ridiculous inttypes.h redefinitions that are
492            # from khrplatform.h and not included in the XML.  We
493            # don't directly include khrplatform.h because it's not
494            # present on many systems, and coming up with #ifdefs to
495            # decide when it's not present would be hard.
496            self.outln('#define __khrplatform_h_ 1')
497            self.outln('typedef int8_t khronos_int8_t;')
498            self.outln('typedef int16_t khronos_int16_t;')
499            self.outln('typedef int32_t khronos_int32_t;')
500            self.outln('typedef int64_t khronos_int64_t;')
501            self.outln('typedef uint8_t khronos_uint8_t;')
502            self.outln('typedef uint16_t khronos_uint16_t;')
503            self.outln('typedef uint32_t khronos_uint32_t;')
504            self.outln('typedef uint64_t khronos_uint64_t;')
505            self.outln('typedef float khronos_float_t;')
506            self.outln('typedef long khronos_intptr_t;')
507            self.outln('typedef long khronos_ssize_t;')
508            self.outln('typedef unsigned long khronos_usize_t;')
509            self.outln('typedef uint64_t khronos_utime_nanoseconds_t;')
510            self.outln('typedef int64_t khronos_stime_nanoseconds_t;')
511            self.outln('#define KHRONOS_MAX_ENUM 0x7FFFFFFF')
512            self.outln('typedef enum {')
513            self.outln('    KHRONOS_FALSE = 0,')
514            self.outln('    KHRONOS_TRUE  = 1,')
515            self.outln('    KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM')
516            self.outln('} khronos_boolean_enum_t;')
517            self.outln('typedef uintptr_t khronos_uintptr_t;')
518
519        if self.target == "glx":
520            self.outln('#include <X11/Xlib.h>')
521            self.outln('#include <X11/Xutil.h>')
522
523        self.out(self.typedefs)
524        self.outln('')
525        self.write_enums()
526        self.outln('')
527        self.write_function_ptr_typedefs()
528
529        for func in self.sorted_functions:
530            self.outln('extern EPOXY_IMPORTEXPORT {0} (EPOXY_CALLSPEC *epoxy_{1})({2});'.format(func.ret_type,
531                                                                                     func.name,
532                                                                                     func.args_decl))
533            self.outln('')
534
535        for func in self.sorted_functions:
536            self.outln('#define {0} epoxy_{0}'.format(func.name))
537
538    def write_function_ptr_resolver(self, func):
539        self.outln('static {0}'.format(func.ptr_type))
540        self.outln('epoxy_{0}_resolver(void)'.format(func.wrapped_name))
541        self.outln('{')
542
543        providers = []
544        # Make a local list of all the providers for this alias group
545        alias_root = func;
546        if func.alias_func:
547            alias_root = func.alias_func
548        for provider in alias_root.providers.values():
549            providers.append(provider)
550        for alias_func in alias_root.alias_exts:
551            for provider in alias_func.providers.values():
552                providers.append(provider)
553
554        # Add some partial aliases of a few functions.  These are ones
555        # that aren't quite aliases, because of some trivial behavior
556        # difference (like whether to produce an error for a
557        # non-Genned name), but where we'd like to fall back to the
558        # similar function if the proper one isn't present.
559        half_aliases = {
560            'glBindVertexArray' : 'glBindVertexArrayAPPLE',
561            'glBindVertexArrayAPPLE' : 'glBindVertexArray',
562            'glBindFramebuffer' : 'glBindFramebufferEXT',
563            'glBindFramebufferEXT' : 'glBindFramebuffer',
564        }
565        if func.name in half_aliases:
566            alias_func = self.functions[half_aliases[func.name]]
567            for provider in alias_func.providers.values():
568                providers.append(provider)
569
570        def provider_sort(provider):
571            return (provider.name != func.name, provider.name, provider.enum)
572        providers.sort(key=provider_sort);
573
574        if len(providers) != 1:
575            self.outln('    static const enum {0}_provider providers[] = {{'.format(self.target))
576            for provider in providers:
577                self.outln('        {0},'.format(provider.enum))
578            self.outln('        {0}_provider_terminator'.format(self.target))
579            self.outln('    };')
580
581            self.outln('    static const uint16_t entrypoints[] = {')
582            if len(providers) > 1:
583                for provider in providers:
584                    self.outln('        {0} /* "{1}" */,'.format(self.entrypoint_string_offset[provider.name], provider.name))
585            else:
586                    self.outln('        0 /* None */,')
587            self.outln('    };')
588
589            self.outln('    return {0}_provider_resolver(entrypoint_strings + {1} /* "{2}" */,'.format(self.target,
590                                                                                                       self.entrypoint_string_offset[func.name],
591                                                                                                       func.name))
592            self.outln('                                providers, entrypoints);')
593        else:
594            assert(providers[0].name == func.name)
595            self.outln('    return {0}_single_resolver({1}, {2} /* {3} */);'.format(self.target,
596                                                                                    providers[0].enum,
597                                                                                    self.entrypoint_string_offset[func.name],
598                                                                                    func.name))
599        self.outln('}')
600        self.outln('')
601
602    def write_thunks(self, func):
603        # Writes out the function that's initially plugged into the
604        # global function pointer, which resolves, updates the global
605        # function pointer, and calls down to it.
606        #
607        # It also writes out the actual initialized global function
608        # pointer.
609        if func.ret_type == 'void':
610            self.outln('GEN_THUNKS({0}, ({1}), ({2}))'.format(func.wrapped_name,
611                                                              func.args_decl,
612                                                              func.args_list))
613        else:
614            self.outln('GEN_THUNKS_RET({0}, {1}, ({2}), ({3}))'.format(func.ret_type,
615                                                                       func.wrapped_name,
616                                                                       func.args_decl,
617                                                                       func.args_list))
618
619    def write_function_pointer(self, func):
620        self.outln('{0}{1} epoxy_{2} = epoxy_{2}_global_rewrite_ptr;'.format(func.public,
621                                                                             func.ptr_type,
622                                                                             func.wrapped_name))
623        self.outln('')
624
625    def write_provider_enums(self):
626        # Writes the enum declaration for the list of providers
627        # supported by gl_provider_resolver()
628
629        self.outln('enum {0}_provider {{'.format(self.target))
630
631        sorted_providers = sorted(self.provider_enum.keys())
632
633        # We always put a 0 enum first so that we can have a
634        # terminator in our arrays
635        self.outln('    {0}_provider_terminator = 0,'.format(self.target))
636
637        for human_name in sorted_providers:
638            enum = self.provider_enum[human_name]
639            self.outln('    {0},'.format(enum))
640        self.outln('} PACKED;')
641        self.outln('')
642
643    def write_provider_enum_strings(self):
644        # Writes the mapping from enums to the strings describing them
645        # for epoxy_print_failure_reasons().
646
647        sorted_providers = sorted(self.provider_enum.keys())
648
649        self.enum_string_offset = {}
650        offset = 0
651        self.outln('static const char *enum_string =')
652        for human_name in sorted_providers:
653            self.outln('    "{0}\\0"'.format(human_name));
654            self.enum_string_offset[human_name] = offset
655            offset += len(human_name.replace('\\', '')) + 1
656        self.outln('     ;')
657        self.outln('')
658        # We're using uint16_t for the offsets.
659        assert(offset < 65536)
660
661        self.outln('static const uint16_t enum_string_offsets[] = {')
662        for human_name in sorted_providers:
663            enum = self.provider_enum[human_name]
664            self.outln('    [{0}] = {1},'.format(enum, self.enum_string_offset[human_name]))
665        self.outln('};')
666        self.outln('')
667
668    def write_entrypoint_strings(self):
669        self.entrypoint_string_offset = {}
670
671        self.outln('static const char entrypoint_strings[] = ')
672        offset = 0
673        for func in self.sorted_functions:
674            if func.name not in self.entrypoint_string_offset:
675                self.entrypoint_string_offset[func.name] = offset
676                offset += len(func.name) + 1
677                self.outln('   "{0}\\0"'.format(func.name))
678        self.outln('    ;')
679        # We're using uint16_t for the offsets.
680        assert(offset < 65536)
681        self.outln('')
682
683    def write_provider_resolver(self):
684        self.outln('static void *{0}_provider_resolver(const char *name,'.format(self.target))
685        self.outln('                                   const enum {0}_provider *providers,'.format(self.target))
686        self.outln('                                   const uint16_t *entrypoints)')
687        self.outln('{')
688        self.outln('    int i;')
689
690        self.outln('    for (i = 0; providers[i] != {0}_provider_terminator; i++) {{'.format(self.target))
691        self.outln('        switch (providers[i]) {')
692
693        for human_name in sorted(self.provider_enum.keys()):
694            enum = self.provider_enum[human_name]
695            self.outln('        case {0}:'.format(enum))
696            self.outln('            if ({0})'.format(self.provider_condition[human_name]))
697            self.outln('                return {0};'.format(self.provider_loader[human_name]).format("entrypoint_strings + entrypoints[i]"))
698            self.outln('            break;')
699
700        self.outln('        case {0}_provider_terminator:'.format(self.target))
701        self.outln('            abort(); /* Not reached */')
702        self.outln('        }')
703        self.outln('    }')
704        self.outln('')
705
706        # If the function isn't provided by any known extension, print
707        # something useful for the poor application developer before
708        # aborting.  (In non-epoxy GL usage, the app developer would
709        # call into some blank stub function and segfault).
710        self.outln('    fprintf(stderr, "No provider of %s found.  Requires one of:\\n", name);')
711        self.outln('    for (i = 0; providers[i] != {0}_provider_terminator; i++) {{'.format(self.target))
712        self.outln('        fprintf(stderr, "    %s\\n", enum_string + enum_string_offsets[providers[i]]);')
713        self.outln('    }')
714        self.outln('    if (providers[0] == {0}_provider_terminator) {{'.format(self.target))
715        self.outln('        fprintf(stderr, "    No known providers.  This is likely a bug "')
716        self.outln('                        "in libepoxy code generation\\n");')
717        self.outln('    }')
718        self.outln('    abort();')
719
720        self.outln('}')
721        self.outln('')
722
723        single_resolver_proto = '{0}_single_resolver(enum {0}_provider provider, uint16_t entrypoint_offset)'.format(self.target)
724        self.outln('EPOXY_NOINLINE static void *')
725        self.outln('{0};'.format(single_resolver_proto))
726        self.outln('')
727        self.outln('static void *')
728        self.outln('{0}'.format(single_resolver_proto))
729        self.outln('{')
730        self.outln('    enum {0}_provider providers[] = {{'.format(self.target))
731        self.outln('        provider,')
732        self.outln('        {0}_provider_terminator'.format(self.target))
733        self.outln('    };')
734        self.outln('    return {0}_provider_resolver(entrypoint_strings + entrypoint_offset,'.format(self.target))
735        self.outln('                                providers, &entrypoint_offset);')
736        self.outln('}')
737        self.outln('')
738
739    def write_source(self, file):
740        self.out_file = open(file, 'w')
741
742        self.outln('/* GL dispatch code.')
743        self.outln(' * This is code-generated from the GL API XML files from Khronos.')
744        self.write_copyright_comment_body()
745        self.outln(' */')
746        self.outln('')
747        self.outln('#include <stdlib.h>')
748        self.outln('#include <string.h>')
749        self.outln('#include <stdio.h>')
750        self.outln('')
751        self.outln('#include "dispatch_common.h"')
752        self.outln('#include "epoxy/{0}.h"'.format(self.target))
753        self.outln('')
754        self.outln('#ifdef __GNUC__')
755        self.outln('#define EPOXY_NOINLINE __attribute__((noinline))')
756        self.outln('#elif defined (_MSC_VER)')
757        self.outln('#define EPOXY_NOINLINE __declspec(noinline)')
758        self.outln('#endif')
759
760        self.outln('struct dispatch_table {')
761        for func in self.sorted_functions:
762            self.outln('    {0} epoxy_{1};'.format(func.ptr_type, func.wrapped_name))
763        self.outln('};')
764        self.outln('')
765
766        # Early declaration, so we can declare the real thing at the
767        # bottom. (I want the function_ptr_resolver as the first
768        # per-GL-call code, since it's the most interesting to see
769        # when you search for the implementation of a call)
770        self.outln('#if USING_DISPATCH_TABLE')
771        self.outln('static inline struct dispatch_table *')
772        self.outln('get_dispatch_table(void);')
773        self.outln('')
774        self.outln('#endif')
775
776        self.write_provider_enums()
777        self.write_provider_enum_strings()
778        self.write_entrypoint_strings()
779        self.write_provider_resolver()
780
781        for func in self.sorted_functions:
782            self.write_function_ptr_resolver(func)
783
784        for func in self.sorted_functions:
785            self.write_thunks(func)
786        self.outln('')
787
788        self.outln('#if USING_DISPATCH_TABLE')
789
790        self.outln('static struct dispatch_table resolver_table = {')
791        for func in self.sorted_functions:
792            self.outln('    .{0} = epoxy_{0}_dispatch_table_rewrite_ptr,'.format(func.wrapped_name))
793        self.outln('};')
794        self.outln('')
795
796        self.outln('uint32_t {0}_tls_index;'.format(self.target))
797        self.outln('uint32_t {0}_tls_size = sizeof(struct dispatch_table);'.format(self.target))
798        self.outln('')
799
800        self.outln('static inline struct dispatch_table *')
801        self.outln('get_dispatch_table(void)')
802        self.outln('{')
803        self.outln('	return TlsGetValue({0}_tls_index);'.format(self.target))
804        self.outln('}')
805        self.outln('')
806
807        self.outln('void')
808        self.outln('{0}_init_dispatch_table(void)'.format(self.target))
809        self.outln('{')
810        self.outln('    struct dispatch_table *dispatch_table = get_dispatch_table();')
811        self.outln('    memcpy(dispatch_table, &resolver_table, sizeof(resolver_table));')
812        self.outln('}')
813        self.outln('')
814
815        self.outln('void')
816        self.outln('{0}_switch_to_dispatch_table(void)'.format(self.target))
817        self.outln('{')
818
819        for func in self.sorted_functions:
820            self.outln('    epoxy_{0} = epoxy_{0}_dispatch_table_thunk;'.format(func.wrapped_name))
821
822        self.outln('}')
823        self.outln('')
824
825        self.outln('#endif /* !USING_DISPATCH_TABLE */')
826
827        for func in self.sorted_functions:
828            self.write_function_pointer(func)
829
830argparser = argparse.ArgumentParser(description='Generate GL dispatch wrappers.')
831argparser.add_argument('files', metavar='file.xml', nargs='+', help='GL API XML files to be parsed')
832argparser.add_argument('--dir', metavar='dir', required=True, help='Destination directory')
833args = argparser.parse_args()
834
835srcdir = args.dir + '/src/'
836incdir = args.dir + '/include/epoxy/'
837
838for file in args.files:
839    name = os.path.basename(file).split('.xml')[0]
840    generator = Generator(name)
841    generator.parse(file)
842
843    generator.drop_weird_glx_functions()
844
845    # This is an ANSI vs Unicode function, handled specially by
846    # include/epoxy/wgl.h
847    if 'wglUseFontBitmaps' in generator.functions:
848        del generator.functions['wglUseFontBitmaps']
849
850    generator.sort_functions()
851    generator.resolve_aliases()
852    generator.fixup_bootstrap_function('glGetString',
853                                       'epoxy_get_bootstrap_proc_address({0})')
854    generator.fixup_bootstrap_function('glGetIntegerv',
855                                       'epoxy_get_bootstrap_proc_address({0})')
856
857    # While this is technically exposed as a GLX extension, it's
858    # required to be present as a public symbol by the Linux OpenGL
859    # ABI.
860    generator.fixup_bootstrap_function('glXGetProcAddress',
861                                       'epoxy_glx_dlsym({0})')
862
863    generator.prepare_provider_enum()
864
865    generator.write_header(incdir + name + '_generated.h')
866    generator.write_source(srcdir + name + '_generated_dispatch.c')
867