~ubuntu-branches/ubuntu/trusty/libepoxy/trusty-security

« back to all changes in this revision

Viewing changes to src/gen_dispatch.py

  • Committer: Package Import Robot
  • Author(s): Eric Anholt
  • Date: 2014-01-31 16:52:20 UTC
  • Revision ID: package-import@ubuntu.com-20140131165220-667dryh9rgibah2g
Tags: upstream-1.1
Import upstream version 1.1

Show diffs side-by-side

added added

removed removed

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