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