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