• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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        self.enum = "PROVIDER_" + self.enum
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 = 'EPOXY_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, arg_type, arg_name):
107        # Reword glDepthRange() arguments to avoid clashing with the
108        # "near" and "far" keywords on win32.
109        if arg_name == "near":
110            arg_name = "hither"
111        elif arg_name == "far":
112            arg_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 arg_type == 'GLhandleARB':
128            assert len(self.args) < 6
129            arg_list_name = '(uintptr_t)' + arg_name
130        else:
131            arg_list_name = arg_name
132
133        self.args.append((arg_type, arg_name))
134        if self.args_decl == 'void':
135            self.args_list = arg_list_name
136            self.args_decl = arg_type + ' ' + arg_name
137        else:
138            self.args_list += ', ' + arg_list_name
139            self.args_decl += ', ' + arg_type + ' ' + arg_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_functions = []
157        self.enum_string_offset = {}
158        self.max_enum_name_len = 1
159        self.entrypoint_string_offset = {}
160        self.copyright_comment = None
161        self.typedefs = ''
162        self.out_file = None
163
164        # GL versions named in the registry, which we should generate
165        # #defines for.
166        self.supported_versions = set()
167
168        # Extensions named in the registry, which we should generate
169        # #defines for.
170        self.supported_extensions = set()
171
172        # Dictionary mapping human-readable names of providers to a C
173        # enum token that will be used to reference those names, to
174        # reduce generated binary size.
175        self.provider_enum = {}
176
177        # Dictionary mapping human-readable names of providers to C
178        # code to detect if it's present.
179        self.provider_condition = {}
180
181        # Dictionary mapping human-readable names of providers to
182        # format strings for fetching the function pointer when
183        # provided the name of the symbol to be requested.
184        self.provider_loader = {}
185
186    def all_text_until_element_name(self, element, element_name):
187        text = ''
188
189        if element.text is not None:
190            text += element.text
191
192        for child in element:
193            if child.tag == element_name:
194                break
195            if child.text:
196                text += child.text
197            if child.tail:
198                text += child.tail
199        return text
200
201    def out(self, text):
202        self.out_file.write(text)
203
204    def outln(self, text):
205        self.out_file.write(text + '\n')
206
207    def parse_typedefs(self, reg):
208        for t in reg.findall('types/type'):
209            if 'name' in t.attrib and t.attrib['name'] not in {'GLhandleARB'}:
210                continue
211
212            # The gles1/gles2-specific types are redundant
213            # declarations, and the different types used for them (int
214            # vs int32_t) caused problems on win32 builds.
215            api = t.get('api')
216            if api:
217                continue
218
219            if t.text is not None:
220                self.typedefs += t.text
221
222            for child in t:
223                if child.tag == 'apientry':
224                    self.typedefs += 'APIENTRY'
225                if child.text:
226                    self.typedefs += child.text
227                if child.tail:
228                    self.typedefs += child.tail
229            self.typedefs += '\n'
230
231    def parse_enums(self, reg):
232        for enum in reg.findall('enums/enum'):
233            name = enum.get('name')
234
235            # wgl.xml's 0xwhatever definitions end up colliding with
236            # wingdi.h's decimal definitions of these.
237            if name in ['WGL_SWAP_OVERLAY', 'WGL_SWAP_UNDERLAY', 'WGL_SWAP_MAIN_PLANE']:
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(r'([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            elif api == 'glsc2':
379                continue
380            else:
381                sys.exit('unknown API: "{0}"'.format(api))
382
383            self.process_require_statements(feature, condition, loader, human_name)
384
385        for extension in reg.findall('extensions/extension'):
386            extname = extension.get('name')
387            cond_extname = "enum_string[enum_string_offsets[i]]"
388
389            self.supported_extensions.add(extname)
390
391            # 'supported' is a set of strings like gl, gles1, gles2,
392            # or glx, which are separated by '|'
393            apis = extension.get('supported').split('|')
394            if 'glx' in apis:
395                condition = 'epoxy_conservative_has_glx_extension(provider_name)'
396                loader = 'glXGetProcAddress((const GLubyte *){0})'
397                self.process_require_statements(extension, condition, loader, extname)
398            if 'egl' in apis:
399                condition = 'epoxy_conservative_has_egl_extension(provider_name)'
400                loader = 'eglGetProcAddress({0})'
401                self.process_require_statements(extension, condition, loader, extname)
402            if 'wgl' in apis:
403                condition = 'epoxy_conservative_has_wgl_extension(provider_name)'
404                loader = 'wglGetProcAddress({0})'
405                self.process_require_statements(extension, condition, loader, extname)
406            if {'gl', 'gles1', 'gles2'}.intersection(apis):
407                condition = 'epoxy_conservative_has_gl_extension(provider_name)'
408                loader = 'epoxy_get_proc_address({0})'
409                self.process_require_statements(extension, condition, loader, extname)
410
411    def fixup_bootstrap_function(self, name, loader):
412        # We handle glGetString(), glGetIntegerv(), and
413        # glXGetProcAddressARB() specially, because we need to use
414        # them in the process of deciding on loaders for resolving,
415        # and the naive code generation would result in their
416        # resolvers calling their own resolvers.
417        if name not in self.functions:
418            return
419
420        func = self.functions[name]
421        func.providers = {}
422        func.add_provider('true', loader, 'always present')
423
424    def parse(self, xml_file):
425        reg = ET.parse(xml_file)
426        comment = reg.find('comment')
427        if comment is not None:
428            self.copyright_comment = 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, out_file):
469        self.out_file = open(out_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, out_file):
484        self.write_header_header(out_file)
485
486        self.outln('#include "epoxy/common.h"')
487
488        if self.target != "gl":
489            self.outln('#include "epoxy/gl.h"')
490            if self.target == "egl":
491                self.outln('#include "EGL/eglplatform.h"')
492                # Account for older eglplatform.h, which doesn't define
493                # the EGL_CAST macro.
494                self.outln('#ifndef EGL_CAST')
495                self.outln('#if defined(__cplusplus)')
496                self.outln('#define EGL_CAST(type, value) (static_cast<type>(value))')
497                self.outln('#else')
498                self.outln('#define EGL_CAST(type, value) ((type) (value))')
499                self.outln('#endif')
500                self.outln('#endif')
501        else:
502            # Add some ridiculous inttypes.h redefinitions that are
503            # from khrplatform.h and not included in the XML.  We
504            # don't directly include khrplatform.h because it's not
505            # present on many systems, and coming up with #ifdefs to
506            # decide when it's not present would be hard.
507            self.outln('#define __khrplatform_h_ 1')
508            self.outln('typedef int8_t khronos_int8_t;')
509            self.outln('typedef int16_t khronos_int16_t;')
510            self.outln('typedef int32_t khronos_int32_t;')
511            self.outln('typedef int64_t khronos_int64_t;')
512            self.outln('typedef uint8_t khronos_uint8_t;')
513            self.outln('typedef uint16_t khronos_uint16_t;')
514            self.outln('typedef uint32_t khronos_uint32_t;')
515            self.outln('typedef uint64_t khronos_uint64_t;')
516            self.outln('typedef float khronos_float_t;')
517            self.outln('typedef long khronos_intptr_t;')
518            self.outln('typedef long khronos_ssize_t;')
519            self.outln('typedef unsigned long khronos_usize_t;')
520            self.outln('typedef uint64_t khronos_utime_nanoseconds_t;')
521            self.outln('typedef int64_t khronos_stime_nanoseconds_t;')
522            self.outln('#define KHRONOS_MAX_ENUM 0x7FFFFFFF')
523            self.outln('typedef enum {')
524            self.outln('    KHRONOS_FALSE = 0,')
525            self.outln('    KHRONOS_TRUE  = 1,')
526            self.outln('    KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM')
527            self.outln('} khronos_boolean_enum_t;')
528            self.outln('typedef uintptr_t khronos_uintptr_t;')
529
530        if self.target == "glx":
531            self.outln('#include <X11/Xlib.h>')
532            self.outln('#include <X11/Xutil.h>')
533
534        self.out(self.typedefs)
535        self.outln('')
536        self.write_enums()
537        self.outln('')
538        self.write_function_ptr_typedefs()
539
540        for func in self.sorted_functions:
541            self.outln('EPOXY_PUBLIC {0} (EPOXY_CALLSPEC *epoxy_{1})({2});'.format(func.ret_type,
542                                                                                   func.name,
543                                                                                   func.args_decl))
544            self.outln('')
545
546        for func in self.sorted_functions:
547            self.outln('#define {0} epoxy_{0}'.format(func.name))
548
549    def write_function_ptr_resolver(self, func):
550        self.outln('static {0}'.format(func.ptr_type))
551        self.outln('epoxy_{0}_resolver(void)'.format(func.wrapped_name))
552        self.outln('{')
553
554        providers = []
555        # Make a local list of all the providers for this alias group
556        alias_root = func
557        if func.alias_func:
558            alias_root = func.alias_func
559        for provider in alias_root.providers.values():
560            providers.append(provider)
561        for alias_func in alias_root.alias_exts:
562            for provider in alias_func.providers.values():
563                providers.append(provider)
564
565        # Add some partial aliases of a few functions.  These are ones
566        # that aren't quite aliases, because of some trivial behavior
567        # difference (like whether to produce an error for a
568        # non-Genned name), but where we'd like to fall back to the
569        # similar function if the proper one isn't present.
570        half_aliases = {
571            'glBindVertexArray' : 'glBindVertexArrayAPPLE',
572            'glBindVertexArrayAPPLE' : 'glBindVertexArray',
573            'glBindFramebuffer' : 'glBindFramebufferEXT',
574            'glBindFramebufferEXT' : 'glBindFramebuffer',
575            'glBindRenderbuffer' : 'glBindRenderbufferEXT',
576            'glBindRenderbufferEXT' : 'glBindRenderbuffer',
577        }
578        if func.name in half_aliases:
579            alias_func = self.functions[half_aliases[func.name]]
580            for provider in alias_func.providers.values():
581                providers.append(provider)
582
583        def provider_sort(provider):
584            return (provider.name != func.name, provider.name, provider.enum)
585        providers.sort(key=provider_sort)
586
587        if len(providers) != 1:
588            self.outln('    static const enum {0}_provider providers[] = {{'.format(self.target))
589            for provider in providers:
590                self.outln('        {0},'.format(provider.enum))
591            self.outln('        {0}_provider_terminator'.format(self.target))
592            self.outln('    };')
593
594            self.outln('    static const uint32_t entrypoints[] = {')
595            if len(providers) > 1:
596                for provider in providers:
597                    self.outln('        {0} /* "{1}" */,'.format(self.entrypoint_string_offset[provider.name], provider.name))
598            else:
599                self.outln('        0 /* None */,')
600            self.outln('    };')
601
602            self.outln('    return {0}_provider_resolver(entrypoint_strings + {1} /* "{2}" */,'.format(self.target,
603                                                                                                       self.entrypoint_string_offset[func.name],
604                                                                                                       func.name))
605            self.outln('                                providers, entrypoints);')
606        else:
607            assert providers[0].name == func.name
608            self.outln('    return {0}_single_resolver({1}, {2} /* {3} */);'.format(self.target,
609                                                                                    providers[0].enum,
610                                                                                    self.entrypoint_string_offset[func.name],
611                                                                                    func.name))
612        self.outln('}')
613        self.outln('')
614
615    def write_thunks(self, func):
616        # Writes out the function that's initially plugged into the
617        # global function pointer, which resolves, updates the global
618        # function pointer, and calls down to it.
619        #
620        # It also writes out the actual initialized global function
621        # pointer.
622        if func.ret_type == 'void':
623            self.outln('GEN_THUNKS({0}, ({1}), ({2}))'.format(func.wrapped_name,
624                                                              func.args_decl,
625                                                              func.args_list))
626        else:
627            self.outln('GEN_THUNKS_RET({0}, {1}, ({2}), ({3}))'.format(func.ret_type,
628                                                                       func.wrapped_name,
629                                                                       func.args_decl,
630                                                                       func.args_list))
631
632    def write_function_pointer(self, func):
633        self.outln('{0} epoxy_{1} = epoxy_{1}_global_rewrite_ptr;'.format(func.ptr_type, func.wrapped_name))
634        self.outln('')
635
636    def write_provider_enums(self):
637        # Writes the enum declaration for the list of providers
638        # supported by gl_provider_resolver()
639
640        self.outln('')
641        self.outln('enum {0}_provider {{'.format(self.target))
642
643        sorted_providers = sorted(self.provider_enum.keys())
644
645        # We always put a 0 enum first so that we can have a
646        # terminator in our arrays
647        self.outln('    {0}_provider_terminator = 0,'.format(self.target))
648
649        for human_name in sorted_providers:
650            enum = self.provider_enum[human_name]
651            self.outln('    {0},'.format(enum))
652        self.outln('} PACKED;')
653        self.outln('ENDPACKED')
654        self.outln('')
655
656    def write_provider_enum_strings(self):
657        # Writes the mapping from enums to the strings describing them
658        # for epoxy_print_failure_reasons().
659
660        sorted_providers = sorted(self.provider_enum.keys())
661
662        offset = 0
663        self.outln('static const char *enum_string =')
664        for human_name in sorted_providers:
665            self.outln('    "{0}\\0"'.format(human_name))
666            self.enum_string_offset[human_name] = offset
667            offset += len(human_name.replace('\\', '')) + 1
668        self.outln('     ;')
669        self.outln('')
670        # We're using uint16_t for the offsets.
671        assert offset < 65536
672
673        self.outln('static const uint16_t enum_string_offsets[] = {')
674        self.outln('    -1, /* {0}_provider_terminator, unused */'.format(self.target))
675        for human_name in sorted_providers:
676            enum = self.provider_enum[human_name]
677            self.outln('    {1}, /* {0} */'.format(human_name, self.enum_string_offset[human_name]))
678        self.outln('};')
679        self.outln('')
680
681    def write_entrypoint_strings(self):
682        self.outln('static const char entrypoint_strings[] = {')
683        offset = 0
684        for func in self.sorted_functions:
685            if func.name not in self.entrypoint_string_offset:
686                self.entrypoint_string_offset[func.name] = offset
687                offset += len(func.name) + 1
688                for c in func.name:
689                    self.outln("   '{0}',".format(c))
690                self.outln('   0, // {0}'.format(func.name))
691        self.outln('    0 };')
692        # We're using uint16_t for the offsets.
693        #assert(offset < 65536)
694        self.outln('')
695
696    def write_provider_resolver(self):
697        self.outln('static void *{0}_provider_resolver(const char *name,'.format(self.target))
698        self.outln('                                   const enum {0}_provider *providers,'.format(self.target))
699        self.outln('                                   const uint32_t *entrypoints)')
700        self.outln('{')
701        self.outln('    int i;')
702
703        self.outln('    for (i = 0; providers[i] != {0}_provider_terminator; i++) {{'.format(self.target))
704        self.outln('        const char *provider_name = enum_string + enum_string_offsets[providers[i]];')
705        self.outln('        switch (providers[i]) {')
706        self.outln('')
707
708        for human_name in sorted(self.provider_enum.keys()):
709            enum = self.provider_enum[human_name]
710            self.outln('        case {0}:'.format(enum))
711            self.outln('            if ({0})'.format(self.provider_condition[human_name]))
712            self.outln('                return {0};'.format(self.provider_loader[human_name]).format("entrypoint_strings + entrypoints[i]"))
713            self.outln('            break;')
714
715        self.outln('        case {0}_provider_terminator:'.format(self.target))
716        self.outln('            abort(); /* Not reached */')
717        self.outln('        }')
718        self.outln('    }')
719        self.outln('')
720
721        self.outln('    if (epoxy_resolver_failure_handler)')
722        self.outln('        return epoxy_resolver_failure_handler(name);')
723        self.outln('')
724
725        # If the function isn't provided by any known extension, print
726        # something useful for the poor application developer before
727        # aborting.  (In non-epoxy GL usage, the app developer would
728        # call into some blank stub function and segfault).
729        self.outln('    fprintf(stderr, "No provider of %s found.  Requires one of:\\n", name);')
730        self.outln('    for (i = 0; providers[i] != {0}_provider_terminator; i++) {{'.format(self.target))
731        self.outln('        fprintf(stderr, "    %s\\n", enum_string + enum_string_offsets[providers[i]]);')
732        self.outln('    }')
733        self.outln('    if (providers[0] == {0}_provider_terminator) {{'.format(self.target))
734        self.outln('        fprintf(stderr, "    No known providers.  This is likely a bug "')
735        self.outln('                        "in libepoxy code generation\\n");')
736        self.outln('    }')
737        self.outln('    abort();')
738
739        self.outln('}')
740        self.outln('')
741
742        single_resolver_proto = '{0}_single_resolver(enum {0}_provider provider, uint32_t entrypoint_offset)'.format(self.target)
743        self.outln('EPOXY_NOINLINE static void *')
744        self.outln('{0};'.format(single_resolver_proto))
745        self.outln('')
746        self.outln('static void *')
747        self.outln('{0}'.format(single_resolver_proto))
748        self.outln('{')
749        self.outln('    enum {0}_provider providers[] = {{'.format(self.target))
750        self.outln('        provider,')
751        self.outln('        {0}_provider_terminator'.format(self.target))
752        self.outln('    };')
753        self.outln('    return {0}_provider_resolver(entrypoint_strings + entrypoint_offset,'.format(self.target))
754        self.outln('                                providers, &entrypoint_offset);')
755        self.outln('}')
756        self.outln('')
757
758    def write_source(self, f):
759        self.out_file = open(f, 'w')
760
761        self.outln('/* GL dispatch code.')
762        self.outln(' * This is code-generated from the GL API XML files from Khronos.')
763        self.write_copyright_comment_body()
764        self.outln(' */')
765        self.outln('')
766        self.outln('#include "config.h"')
767        self.outln('')
768        self.outln('#include <stdlib.h>')
769        self.outln('#include <string.h>')
770        self.outln('#include <stdio.h>')
771        self.outln('')
772        self.outln('#include "dispatch_common.h"')
773        self.outln('#include "epoxy/{0}.h"'.format(self.target))
774        self.outln('')
775        self.outln('#ifdef __GNUC__')
776        self.outln('#define EPOXY_NOINLINE __attribute__((noinline))')
777        self.outln('#elif defined (_MSC_VER)')
778        self.outln('#define EPOXY_NOINLINE __declspec(noinline)')
779        self.outln('#endif')
780
781        self.outln('struct dispatch_table {')
782        for func in self.sorted_functions:
783            self.outln('    {0} epoxy_{1};'.format(func.ptr_type, func.wrapped_name))
784        self.outln('};')
785        self.outln('')
786
787        # Early declaration, so we can declare the real thing at the
788        # bottom. (I want the function_ptr_resolver as the first
789        # per-GL-call code, since it's the most interesting to see
790        # when you search for the implementation of a call)
791        self.outln('#if USING_DISPATCH_TABLE')
792        self.outln('static inline struct dispatch_table *')
793        self.outln('get_dispatch_table(void);')
794        self.outln('')
795        self.outln('#endif')
796
797        self.write_provider_enums()
798        self.write_provider_enum_strings()
799        self.write_entrypoint_strings()
800        self.write_provider_resolver()
801
802        for func in self.sorted_functions:
803            self.write_function_ptr_resolver(func)
804
805        for func in self.sorted_functions:
806            self.write_thunks(func)
807        self.outln('')
808
809        self.outln('#if USING_DISPATCH_TABLE')
810
811        self.outln('static struct dispatch_table resolver_table = {')
812        for func in self.sorted_functions:
813            self.outln('    epoxy_{0}_dispatch_table_rewrite_ptr, /* {0} */'.format(func.wrapped_name))
814        self.outln('};')
815        self.outln('')
816
817        self.outln('uint32_t {0}_tls_index;'.format(self.target))
818        self.outln('uint32_t {0}_tls_size = sizeof(struct dispatch_table);'.format(self.target))
819        self.outln('')
820
821        self.outln('static inline struct dispatch_table *')
822        self.outln('get_dispatch_table(void)')
823        self.outln('{')
824        self.outln('	return TlsGetValue({0}_tls_index);'.format(self.target))
825        self.outln('}')
826        self.outln('')
827
828        self.outln('void')
829        self.outln('{0}_init_dispatch_table(void)'.format(self.target))
830        self.outln('{')
831        self.outln('    struct dispatch_table *dispatch_table = get_dispatch_table();')
832        self.outln('    memcpy(dispatch_table, &resolver_table, sizeof(resolver_table));')
833        self.outln('}')
834        self.outln('')
835
836        self.outln('void')
837        self.outln('{0}_switch_to_dispatch_table(void)'.format(self.target))
838        self.outln('{')
839
840        for func in self.sorted_functions:
841            self.outln('    epoxy_{0} = epoxy_{0}_dispatch_table_thunk;'.format(func.wrapped_name))
842
843        self.outln('}')
844        self.outln('')
845
846        self.outln('#endif /* !USING_DISPATCH_TABLE */')
847
848        for func in self.sorted_functions:
849            self.write_function_pointer(func)
850
851argparser = argparse.ArgumentParser(description='Generate GL dispatch wrappers.')
852argparser.add_argument('files', metavar='file.xml', nargs='+', help='GL API XML files to be parsed')
853argparser.add_argument('--outputdir', metavar='dir', required=False, help='Destination directory for files (default to current dir)')
854argparser.add_argument('--includedir', metavar='dir', required=False, help='Destination directory for headers')
855argparser.add_argument('--srcdir', metavar='dir', required=False, help='Destination directory for source')
856argparser.add_argument('--source', dest='source', action='store_true', required=False, help='Generate the source file')
857argparser.add_argument('--no-source', dest='source', action='store_false', required=False, help='Do not generate the source file')
858argparser.add_argument('--header', dest='header', action='store_true', required=False, help='Generate the header file')
859argparser.add_argument('--no-header', dest='header', action='store_false', required=False, help='Do not generate the header file')
860args = argparser.parse_args()
861
862if args.outputdir:
863    outputdir = args.outputdir
864else:
865    outputdir = os.getcwd()
866
867if args.includedir:
868    includedir = args.includedir
869else:
870    includedir = outputdir
871
872if args.srcdir:
873    srcdir = args.srcdir
874else:
875    srcdir = outputdir
876
877build_source = args.source
878build_header = args.header
879
880if not build_source and not build_header:
881    build_source = True
882    build_header = True
883
884for f in args.files:
885    name = os.path.basename(f).split('.xml')[0]
886    generator = Generator(name)
887    generator.parse(f)
888
889    generator.drop_weird_glx_functions()
890
891    # This is an ANSI vs Unicode function, handled specially by
892    # include/epoxy/wgl.h
893    if 'wglUseFontBitmaps' in generator.functions:
894        del generator.functions['wglUseFontBitmaps']
895
896    generator.sort_functions()
897    generator.resolve_aliases()
898    generator.fixup_bootstrap_function('glGetString',
899                                       'epoxy_get_bootstrap_proc_address({0})')
900    generator.fixup_bootstrap_function('glGetIntegerv',
901                                       'epoxy_get_bootstrap_proc_address({0})')
902
903    # While this is technically exposed as a GLX extension, it's
904    # required to be present as a public symbol by the Linux OpenGL
905    # ABI.
906    generator.fixup_bootstrap_function('glXGetProcAddress',
907                                       'epoxy_glx_dlsym({0})')
908
909    generator.prepare_provider_enum()
910
911    if build_header:
912        generator.write_header(os.path.join(includedir, name + '_generated.h'))
913    if build_source:
914        generator.write_source(os.path.join(srcdir, name + '_generated_dispatch.c'))
915