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