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