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