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