You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
libepoxy/src/gen_dispatch.py

932 lines
39 KiB

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © 2013 Intel Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
import sys
import argparse
import xml.etree.ElementTree as ET
import re
import os
class GLProvider(object):
def __init__(self, condition, condition_name, loader, name):
# C code for determining if this function is available.
# (e.g. epoxy_is_desktop_gl() && epoxy_gl_version() >= 20
self.condition = condition
# A string (possibly with spaces) describing the condition.
self.condition_name = condition_name
# The loader for getting the symbol -- either dlsym or
# getprocaddress. This is a python format string to generate
# C code, given self.name.
self.loader = loader
# The name of the function to be loaded (possibly an
# ARB/EXT/whatever-decorated variant).
self.name = name
# This is the C enum name we'll use for referring to this provider.
self.enum = condition_name
self.enum = self.enum.replace(' ', '_')
self.enum = self.enum.replace('\\"', '')
self.enum = self.enum.replace('.', '_')
self.enum = "PROVIDER_" + self.enum
class GLFunction(object):
def __init__(self, ret_type, name):
self.name = name
self.ptr_type = 'PFN' + name.upper() + 'PROC'
self.ret_type = ret_type
self.providers = {}
self.args = []
# These are functions with hand-written wrapper code in
# dispatch_common.c. Their dispatch entries are replaced with
# non-public symbols with a "_unwrapped" suffix.
wrapped_functions = {
'glBegin',
'glEnd',
'wglMakeCurrent',
'wglMakeContextCurrentEXT',
'wglMakeContextCurrentARB',
'wglMakeAssociatedContextCurrentAMD',
}
if name in wrapped_functions:
self.wrapped_name = name + '_unwrapped'
self.public = ''
else:
self.wrapped_name = name
self.public = 'EPOXY_PUBLIC '
# This is the string of C code for passing through the
# arguments to the function.
self.args_list = ''
# This is the string of C code for declaring the arguments
# list.
self.args_decl = 'void'
# This is the string name of the function that this is an
# alias of, or self.name. This initially comes from the
# registry, and may get updated if it turns out our alias is
# itself an alias (for example glFramebufferTextureEXT ->
# glFramebufferTextureARB -> glFramebufferTexture)
self.alias_name = name
# After alias resolution, this is the function that this is an
# alias of.
self.alias_func = None
# For the root of an alias tree, this lists the functions that
# are marked as aliases of it, so that it can write a resolver
# for all of them.
self.alias_exts = []
def add_arg(self, arg_type, arg_name):
# Reword glDepthRange() arguments to avoid clashing with the
# "near" and "far" keywords on win32.
if arg_name == "near":
arg_name = "hither"
elif arg_name == "far":
arg_name = "yon"
# Mac screwed up GLhandleARB and made it a void * instead of
# uint32_t, despite it being specced as only necessarily 32
# bits wide, causing portability problems all over. There are
# prototype conflicts between things like
# glAttachShader(GLuint program, GLuint shader) and
# glAttachObjectARB(GLhandleARB container, GLhandleARB obj),
# even though they are marked as aliases in the XML (and being
# aliases in Mesa).
#
# We retain those aliases. In the x86_64 ABI, the first 6
# args are stored in 64-bit registers, so the calls end up
# being the same despite the different types. We just need to
# add a cast to uintptr_t to shut up the compiler.
if arg_type == 'GLhandleARB':
assert len(self.args) < 6
arg_list_name = '(uintptr_t)' + arg_name
else:
arg_list_name = arg_name
self.args.append((arg_type, arg_name))
if self.args_decl == 'void':
self.args_list = arg_list_name
self.args_decl = arg_type + ' ' + arg_name
else:
self.args_list += ', ' + arg_list_name
self.args_decl += ', ' + arg_type + ' ' + arg_name
def add_provider(self, condition, loader, condition_name):
self.providers[condition_name] = GLProvider(condition, condition_name,
loader, self.name)
def add_alias(self, ext):
assert self.alias_func is None
self.alias_exts.append(ext)
ext.alias_func = self
class Generator(object):
def __init__(self, target):
self.target = target
self.enums = {}
self.functions = {}
self.sorted_functions = []
self.enum_string_offset = {}
self.max_enum_name_len = 1
self.entrypoint_string_offset = {}
self.copyright_comment = None
self.typedefs = ''
self.out_file = None
# GL versions named in the registry, which we should generate
# #defines for.
self.supported_versions = set()
# Extensions named in the registry, which we should generate
# #defines for.
self.supported_extensions = set()
# Dictionary mapping human-readable names of providers to a C
# enum token that will be used to reference those names, to
# reduce generated binary size.
self.provider_enum = {}
# Dictionary mapping human-readable names of providers to C
# code to detect if it's present.
self.provider_condition = {}
# Dictionary mapping human-readable names of providers to
# format strings for fetching the function pointer when
# provided the name of the symbol to be requested.
self.provider_loader = {}
def all_text_until_element_name(self, element, element_name):
text = ''
if element.text is not None:
text += element.text
for child in element:
if child.tag == element_name:
break
if child.text:
text += child.text
if child.tail:
text += child.tail
return text
def out(self, text):
self.out_file.write(text)
def outln(self, text):
self.out_file.write(text + '\n')
def parse_typedefs(self, reg):
for t in reg.findall('types/type'):
if 'name' in t.attrib and t.attrib['name'] not in {'GLhandleARB'}:
continue
# The gles1/gles2-specific types are redundant
# declarations, and the different types used for them (int
# vs int32_t) caused problems on win32 builds.
api = t.get('api')
if api:
continue
if t.text is not None:
self.typedefs += t.text
for child in t:
if child.tag == 'apientry':
self.typedefs += 'APIENTRY'
if child.text:
self.typedefs += child.text
if child.tail:
self.typedefs += child.tail
self.typedefs += '\n'
def parse_enums(self, reg):
for enum in reg.findall('enums/enum'):
name = enum.get('name')
# wgl.xml's 0xwhatever definitions end up colliding with
# wingdi.h's decimal definitions of these.
if name in ['WGL_SWAP_OVERLAY', 'WGL_SWAP_UNDERLAY', 'WGL_SWAP_MAIN_PLANE']:
continue
self.max_enum_name_len = max(self.max_enum_name_len, len(name))
self.enums[name] = enum.get('value')
def get_function_return_type(self, proto):
# Everything up to the start of the name element is the return type.
return self.all_text_until_element_name(proto, 'name').strip()
def parse_function_definitions(self, reg):
for command in reg.findall('commands/command'):
proto = command.find('proto')
name = proto.find('name').text
ret_type = self.get_function_return_type(proto)
func = GLFunction(ret_type, name)
for arg in command.findall('param'):
func.add_arg(self.all_text_until_element_name(arg, 'name').strip(),
arg.find('name').text)
alias = command.find('alias')
if alias is not None:
# Note that some alias references appear before the
# target command is defined (glAttachObjectARB() ->
# glAttachShader(), for example).
func.alias_name = alias.get('name')
self.functions[name] = func
def drop_weird_glx_functions(self):
# Drop a few ancient SGIX GLX extensions that use types not defined
# anywhere in Xlib. In glxext.h, they're protected by #ifdefs for the
# headers that defined them.
weird_functions = [name for name, func in self.functions.items()
if 'VLServer' in func.args_decl
or 'DMparams' in func.args_decl]
for name in weird_functions:
del self.functions[name]
def resolve_aliases(self):
for func in self.functions.values():
# Find the root of the alias tree, and add ourselves to it.
if func.alias_name != func.name:
alias_func = func
while alias_func.alias_name != alias_func.name:
alias_func = self.functions[alias_func.alias_name]
func.alias_name = alias_func.name
func.alias_func = alias_func
alias_func.alias_exts.append(func)
def prepare_provider_enum(self):
self.provider_enum = {}
# We assume that for any given provider, all functions using
# it will have the same loader. This lets us generate a
# general C function for detecting conditions and calling the
# dlsym/getprocaddress, and have our many resolver stubs just
# call it with a table of values.
for func in self.functions.values():
for provider in func.providers.values():
if provider.condition_name in self.provider_enum:
assert self.provider_condition[provider.condition_name] == provider.condition
assert self.provider_loader[provider.condition_name] == provider.loader
continue
self.provider_enum[provider.condition_name] = provider.enum
self.provider_condition[provider.condition_name] = provider.condition
self.provider_loader[provider.condition_name] = provider.loader
def sort_functions(self):
self.sorted_functions = sorted(self.functions.values(), key=lambda func: func.name)
def process_require_statements(self, feature, condition, loader, human_name):
for command in feature.findall('require/command'):
name = command.get('name')
# wgl.xml describes 6 functions in WGL 1.0 that are in
# gdi32.dll instead of opengl32.dll, and we would need to
# change up our symbol loading to support that. Just
# don't wrap those functions.
if self.target == 'wgl' and 'wgl' not in name:
del self.functions[name]
continue
func = self.functions[name]
func.add_provider(condition, loader, human_name)
def parse_function_providers(self, reg):
for feature in reg.findall('feature'):
api = feature.get('api') # string gl, gles1, gles2, glx
m = re.match(r'([0-9])\.([0-9])', feature.get('number'))
version = int(m.group(1)) * 10 + int(m.group(2))
self.supported_versions.add(feature.get('name'))
if api == 'gl':
human_name = 'Desktop OpenGL {0}'.format(feature.get('number'))
condition = 'epoxy_is_desktop_gl()'
loader = 'epoxy_get_core_proc_address({0}, {1})'.format('{0}', version)
if version >= 11:
condition += ' && epoxy_conservative_gl_version() >= {0}'.format(version)
elif api == 'gles2':
human_name = 'OpenGL ES {0}'.format(feature.get('number'))
condition = '!epoxy_is_desktop_gl() && epoxy_gl_version() >= {0}'.format(version)
if version <= 20:
loader = 'epoxy_gles2_dlsym({0})'
else:
loader = 'epoxy_gles3_dlsym({0})'
elif api == 'gles1':
human_name = 'OpenGL ES 1.0'
condition = '!epoxy_is_desktop_gl() && epoxy_gl_version() >= 10 && epoxy_gl_version() < 20'
loader = 'epoxy_gles1_dlsym({0})'
elif api == 'glx':
human_name = 'GLX {0}'.format(version)
# We could just always use GPA for loading everything
# but glXGetProcAddress(), but dlsym() is a more
# efficient lookup.
if version > 13:
condition = 'epoxy_conservative_glx_version() >= {0}'.format(version)
loader = 'glXGetProcAddress((const GLubyte *){0})'
else:
condition = 'true'
loader = 'epoxy_glx_dlsym({0})'
elif api == 'egl':
human_name = 'EGL {0}'.format(version)
if version > 10:
condition = 'epoxy_conservative_egl_version() >= {0}'.format(version)
else:
condition = 'true'
# All EGL core entrypoints must be dlsym()ed out --
# eglGetProcAdddress() will return NULL.
loader = 'epoxy_egl_dlsym({0})'
elif api == 'wgl':
human_name = 'WGL {0}'.format(version)
condition = 'true'
loader = 'epoxy_gl_dlsym({0})'
elif api == 'glsc2':
continue
else:
sys.exit('unknown API: "{0}"'.format(api))
self.process_require_statements(feature, condition, loader, human_name)
for extension in reg.findall('extensions/extension'):
extname = extension.get('name')
cond_extname = "enum_string[enum_string_offsets[i]]"
self.supported_extensions.add(extname)
# 'supported' is a set of strings like gl, gles1, gles2,
# or glx, which are separated by '|'
apis = extension.get('supported').split('|')
if 'glx' in apis:
condition = 'epoxy_conservative_has_glx_extension(provider_name)'
loader = 'glXGetProcAddress((const GLubyte *){0})'
self.process_require_statements(extension, condition, loader, extname)
if 'egl' in apis:
condition = 'epoxy_conservative_has_egl_extension(provider_name)'
loader = 'eglGetProcAddress({0})'
self.process_require_statements(extension, condition, loader, extname)
if 'wgl' in apis:
condition = 'epoxy_conservative_has_wgl_extension(provider_name)'
loader = 'wglGetProcAddress({0})'
self.process_require_statements(extension, condition, loader, extname)
if {'gl', 'gles1', 'gles2'}.intersection(apis):
condition = 'epoxy_conservative_has_gl_extension(provider_name)'
loader = 'epoxy_get_proc_address({0})'
self.process_require_statements(extension, condition, loader, extname)
def fixup_bootstrap_function(self, name, loader):
# We handle glGetString(), glGetIntegerv(), and
# glXGetProcAddressARB() specially, because we need to use
# them in the process of deciding on loaders for resolving,
# and the naive code generation would result in their
# resolvers calling their own resolvers.
if name not in self.functions:
return
func = self.functions[name]
func.providers = {}
func.add_provider('true', loader, 'always present')
def parse(self, xml_file):
reg = ET.parse(xml_file)
comment = reg.find('comment')
if comment is not None:
self.copyright_comment = comment.text
else:
self.copyright_comment = ''
self.parse_typedefs(reg)
self.parse_enums(reg)
self.parse_function_definitions(reg)
self.parse_function_providers(reg)
def write_copyright_comment_body(self):
for line in self.copyright_comment.splitlines():
if '-----' in line:
break
self.outln(' * ' + line)
def write_enums(self):
for name in sorted(self.supported_versions):
self.outln('#define {0} 1'.format(name))
self.outln('')
for name in sorted(self.supported_extensions):
self.outln('#define {0} 1'.format(name))
self.outln('')
# We want to sort by enum number (which puts a bunch of things
# in a logical order), then by name after that, so we do those
# sorts in reverse. This is still way uglier than doing some
# sort based on what version/extensions things are introduced
# in, but we haven't paid any attention to those attributes
# for enums yet.
sorted_by_name = sorted(self.enums.keys())
sorted_by_number = sorted(sorted_by_name, key=lambda name: self.enums[name])
for name in sorted_by_number:
self.outln('#define ' + name.ljust(self.max_enum_name_len + 3) + self.enums[name] + '')
def write_function_ptr_typedefs(self):
for func in self.sorted_functions:
self.outln('typedef {0} (GLAPIENTRY *{1})({2});'.format(func.ret_type,
func.ptr_type,
func.args_decl))
def write_header_header(self, out_file):
self.close()
self.out_file = open(out_file, 'w')
self.outln('/* GL dispatch header.')
self.outln(' * This is code-generated from the GL API XML files from Khronos.')
self.write_copyright_comment_body()
self.outln(' */')
self.outln('')
self.outln('#pragma once')
self.outln('#include <inttypes.h>')
self.outln('#include <stddef.h>')
self.outln('')
def write_header(self, out_file):
self.write_header_header(out_file)
self.outln('#include "epoxy/common.h"')
if self.target != "gl":
self.outln('#include "epoxy/gl.h"')
if self.target == "egl":
self.outln('#include "EGL/eglplatform.h"')
# Account for older eglplatform.h, which doesn't define
# the EGL_CAST macro.
self.outln('#ifndef EGL_CAST')
self.outln('#if defined(__cplusplus)')
self.outln('#define EGL_CAST(type, value) (static_cast<type>(value))')
self.outln('#else')
self.outln('#define EGL_CAST(type, value) ((type) (value))')
self.outln('#endif')
self.outln('#endif')
else:
# Add some ridiculous inttypes.h redefinitions that are
# from khrplatform.h and not included in the XML. We
# don't directly include khrplatform.h because it's not
# present on many systems, and coming up with #ifdefs to
# decide when it's not present would be hard.
self.outln('#define __khrplatform_h_ 1')
self.outln('typedef int8_t khronos_int8_t;')
self.outln('typedef int16_t khronos_int16_t;')
self.outln('typedef int32_t khronos_int32_t;')
self.outln('typedef int64_t khronos_int64_t;')
self.outln('typedef uint8_t khronos_uint8_t;')
self.outln('typedef uint16_t khronos_uint16_t;')
self.outln('typedef uint32_t khronos_uint32_t;')
self.outln('typedef uint64_t khronos_uint64_t;')
self.outln('typedef float khronos_float_t;')
self.outln('#ifdef _WIN64')
self.outln('typedef signed long long int khronos_intptr_t;')
self.outln('typedef unsigned long long int khronos_uintptr_t;')
self.outln('typedef signed long long int khronos_ssize_t;')
self.outln('typedef unsigned long long int khronos_usize_t;')
self.outln('#else')
self.outln('typedef signed long int khronos_intptr_t;')
self.outln('typedef unsigned long int khronos_uintptr_t;')
self.outln('typedef signed long int khronos_ssize_t;')
self.outln('typedef unsigned long int khronos_usize_t;')
self.outln('#endif')
self.outln('typedef uint64_t khronos_utime_nanoseconds_t;')
self.outln('typedef int64_t khronos_stime_nanoseconds_t;')
self.outln('#define KHRONOS_MAX_ENUM 0x7FFFFFFF')
self.outln('typedef enum {')
self.outln(' KHRONOS_FALSE = 0,')
self.outln(' KHRONOS_TRUE = 1,')
self.outln(' KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM')
self.outln('} khronos_boolean_enum_t;')
if self.target == "glx":
self.outln('#include <X11/Xlib.h>')
self.outln('#include <X11/Xutil.h>')
self.out(self.typedefs)
self.outln('')
self.write_enums()
self.outln('')
self.write_function_ptr_typedefs()
for func in self.sorted_functions:
self.outln('EPOXY_PUBLIC {0} (EPOXY_CALLSPEC *epoxy_{1})({2});'.format(func.ret_type,
func.name,
func.args_decl))
self.outln('')
for func in self.sorted_functions:
self.outln('#define {0} epoxy_{0}'.format(func.name))
def write_function_ptr_resolver(self, func):
self.outln('static {0}'.format(func.ptr_type))
self.outln('epoxy_{0}_resolver(void)'.format(func.wrapped_name))
self.outln('{')
providers = []
# Make a local list of all the providers for this alias group
alias_root = func
if func.alias_func:
alias_root = func.alias_func
for provider in alias_root.providers.values():
providers.append(provider)
for alias_func in alias_root.alias_exts:
for provider in alias_func.providers.values():
providers.append(provider)
# Add some partial aliases of a few functions. These are ones
# that aren't quite aliases, because of some trivial behavior
# difference (like whether to produce an error for a
# non-Genned name), but where we'd like to fall back to the
# similar function if the proper one isn't present.
half_aliases = {
'glBindVertexArray' : 'glBindVertexArrayAPPLE',
'glBindVertexArrayAPPLE' : 'glBindVertexArray',
'glBindFramebuffer' : 'glBindFramebufferEXT',
'glBindFramebufferEXT' : 'glBindFramebuffer',
'glBindRenderbuffer' : 'glBindRenderbufferEXT',
'glBindRenderbufferEXT' : 'glBindRenderbuffer',
}
if func.name in half_aliases:
alias_func = self.functions[half_aliases[func.name]]
for provider in alias_func.providers.values():
providers.append(provider)
def provider_sort(provider):
return (provider.name != func.name, provider.name, provider.enum)
providers.sort(key=provider_sort)
if len(providers) != 1:
self.outln(' static const enum {0}_provider providers[] = {{'.format(self.target))
for provider in providers:
self.outln(' {0},'.format(provider.enum))
self.outln(' {0}_provider_terminator'.format(self.target))
self.outln(' };')
self.outln(' static const uint32_t entrypoints[] = {')
if len(providers) > 1:
for provider in providers:
self.outln(' {0} /* "{1}" */,'.format(self.entrypoint_string_offset[provider.name], provider.name))
else:
self.outln(' 0 /* None */,')
self.outln(' };')
self.outln(' return {0}_provider_resolver(entrypoint_strings + {1} /* "{2}" */,'.format(self.target,
self.entrypoint_string_offset[func.name],
func.name))
self.outln(' providers, entrypoints);')
else:
assert providers[0].name == func.name
self.outln(' return {0}_single_resolver({1}, {2} /* {3} */);'.format(self.target,
providers[0].enum,
self.entrypoint_string_offset[func.name],
func.name))
self.outln('}')
self.outln('')
def write_thunks(self, func):
# Writes out the function that's initially plugged into the
# global function pointer, which resolves, updates the global
# function pointer, and calls down to it.
#
# It also writes out the actual initialized global function
# pointer.
if func.ret_type == 'void':
self.outln('GEN_THUNKS({0}, ({1}), ({2}))'.format(func.wrapped_name,
func.args_decl,
func.args_list))
else:
self.outln('GEN_THUNKS_RET({0}, {1}, ({2}), ({3}))'.format(func.ret_type,
func.wrapped_name,
func.args_decl,
func.args_list))
def write_function_pointer(self, func):
self.outln('{0} epoxy_{1} = epoxy_{1}_global_rewrite_ptr;'.format(func.ptr_type, func.wrapped_name))
self.outln('')
def write_provider_enums(self):
# Writes the enum declaration for the list of providers
# supported by gl_provider_resolver()
self.outln('')
self.outln('enum {0}_provider {{'.format(self.target))
sorted_providers = sorted(self.provider_enum.keys())
# We always put a 0 enum first so that we can have a
# terminator in our arrays
self.outln(' {0}_provider_terminator = 0,'.format(self.target))
for human_name in sorted_providers:
enum = self.provider_enum[human_name]
self.outln(' {0},'.format(enum))
self.outln('} PACKED;')
self.outln('ENDPACKED')
self.outln('')
def write_provider_enum_strings(self):
# Writes the mapping from enums to the strings describing them
# for epoxy_print_failure_reasons().
sorted_providers = sorted(self.provider_enum.keys())
offset = 0
self.outln('static const char *enum_string =')
for human_name in sorted_providers:
self.outln(' "{0}\\0"'.format(human_name))
self.enum_string_offset[human_name] = offset
offset += len(human_name.replace('\\', '')) + 1
self.outln(' ;')
self.outln('')
# We're using uint16_t for the offsets.
assert offset < 65536
self.outln('static const uint16_t enum_string_offsets[] = {')
self.outln(' -1, /* {0}_provider_terminator, unused */'.format(self.target))
for human_name in sorted_providers:
enum = self.provider_enum[human_name]
self.outln(' {1}, /* {0} */'.format(human_name, self.enum_string_offset[human_name]))
self.outln('};')
self.outln('')
def write_entrypoint_strings(self):
self.outln('static const char entrypoint_strings[] = {')
offset = 0
for func in self.sorted_functions:
if func.name not in self.entrypoint_string_offset:
self.entrypoint_string_offset[func.name] = offset
offset += len(func.name) + 1
for c in func.name:
self.outln(" '{0}',".format(c))
self.outln(' 0, // {0}'.format(func.name))
self.outln(' 0 };')
# We're using uint16_t for the offsets.
#assert(offset < 65536)
self.outln('')
def write_provider_resolver(self):
self.outln('static void *{0}_provider_resolver(const char *name,'.format(self.target))
self.outln(' const enum {0}_provider *providers,'.format(self.target))
self.outln(' const uint32_t *entrypoints)')
self.outln('{')
self.outln(' int i;')
self.outln(' for (i = 0; providers[i] != {0}_provider_terminator; i++) {{'.format(self.target))
self.outln(' const char *provider_name = enum_string + enum_string_offsets[providers[i]];')
self.outln(' switch (providers[i]) {')
self.outln('')
for human_name in sorted(self.provider_enum.keys()):
enum = self.provider_enum[human_name]
self.outln(' case {0}:'.format(enum))
self.outln(' if ({0})'.format(self.provider_condition[human_name]))
self.outln(' return {0};'.format(self.provider_loader[human_name]).format("entrypoint_strings + entrypoints[i]"))
self.outln(' break;')
self.outln(' case {0}_provider_terminator:'.format(self.target))
self.outln(' abort(); /* Not reached */')
self.outln(' }')
self.outln(' }')
self.outln('')
self.outln(' if (epoxy_resolver_failure_handler)')
self.outln(' return epoxy_resolver_failure_handler(name);')
self.outln('')
# If the function isn't provided by any known extension, print
# something useful for the poor application developer before
# aborting. (In non-epoxy GL usage, the app developer would
# call into some blank stub function and segfault).
self.outln(' fprintf(stderr, "No provider of %s found. Requires one of:\\n", name);')
self.outln(' for (i = 0; providers[i] != {0}_provider_terminator; i++) {{'.format(self.target))
self.outln(' fprintf(stderr, " %s\\n", enum_string + enum_string_offsets[providers[i]]);')
self.outln(' }')
self.outln(' if (providers[0] == {0}_provider_terminator) {{'.format(self.target))
self.outln(' fprintf(stderr, " No known providers. This is likely a bug "')
self.outln(' "in libepoxy code generation\\n");')
self.outln(' }')
self.outln(' abort();')
self.outln('}')
self.outln('')
single_resolver_proto = '{0}_single_resolver(enum {0}_provider provider, uint32_t entrypoint_offset)'.format(self.target)
self.outln('EPOXY_NOINLINE static void *')
self.outln('{0};'.format(single_resolver_proto))
self.outln('')
self.outln('static void *')
self.outln('{0}'.format(single_resolver_proto))
self.outln('{')
self.outln(' enum {0}_provider providers[] = {{'.format(self.target))
self.outln(' provider,')
self.outln(' {0}_provider_terminator'.format(self.target))
self.outln(' };')
self.outln(' return {0}_provider_resolver(entrypoint_strings + entrypoint_offset,'.format(self.target))
self.outln(' providers, &entrypoint_offset);')
self.outln('}')
self.outln('')
def write_source(self, f):
self.close()
self.out_file = open(f, 'w')
self.outln('/* GL dispatch code.')
self.outln(' * This is code-generated from the GL API XML files from Khronos.')
self.write_copyright_comment_body()
self.outln(' */')
self.outln('')
self.outln('#include "config.h"')
self.outln('')
self.outln('#include <stdlib.h>')
self.outln('#include <string.h>')
self.outln('#include <stdio.h>')
self.outln('')
self.outln('#include "dispatch_common.h"')
self.outln('#include "epoxy/{0}.h"'.format(self.target))
self.outln('')
self.outln('#ifdef __GNUC__')
self.outln('#define EPOXY_NOINLINE __attribute__((noinline))')
self.outln('#elif defined (_MSC_VER)')
self.outln('#define EPOXY_NOINLINE __declspec(noinline)')
self.outln('#endif')
self.outln('struct dispatch_table {')
for func in self.sorted_functions:
self.outln(' {0} epoxy_{1};'.format(func.ptr_type, func.wrapped_name))
self.outln('};')
self.outln('')
# Early declaration, so we can declare the real thing at the
# bottom. (I want the function_ptr_resolver as the first
# per-GL-call code, since it's the most interesting to see
# when you search for the implementation of a call)
self.outln('#if USING_DISPATCH_TABLE')
self.outln('static inline struct dispatch_table *')
self.outln('get_dispatch_table(void);')
self.outln('')
self.outln('#endif')
self.write_provider_enums()
self.write_provider_enum_strings()
self.write_entrypoint_strings()
self.write_provider_resolver()
for func in self.sorted_functions:
self.write_function_ptr_resolver(func)
for func in self.sorted_functions:
self.write_thunks(func)
self.outln('')
self.outln('#if USING_DISPATCH_TABLE')
self.outln('static struct dispatch_table resolver_table = {')
for func in self.sorted_functions:
self.outln(' epoxy_{0}_dispatch_table_rewrite_ptr, /* {0} */'.format(func.wrapped_name))
self.outln('};')
self.outln('')
self.outln('uint32_t {0}_tls_index;'.format(self.target))
self.outln('uint32_t {0}_tls_size = sizeof(struct dispatch_table);'.format(self.target))
self.outln('')
self.outln('static inline struct dispatch_table *')
self.outln('get_dispatch_table(void)')
self.outln('{')
self.outln(' return TlsGetValue({0}_tls_index);'.format(self.target))
self.outln('}')
self.outln('')
self.outln('void')
self.outln('{0}_init_dispatch_table(void)'.format(self.target))
self.outln('{')
self.outln(' struct dispatch_table *dispatch_table = get_dispatch_table();')
self.outln(' memcpy(dispatch_table, &resolver_table, sizeof(resolver_table));')
self.outln('}')
self.outln('')
self.outln('void')
self.outln('{0}_switch_to_dispatch_table(void)'.format(self.target))
self.outln('{')
for func in self.sorted_functions:
self.outln(' epoxy_{0} = epoxy_{0}_dispatch_table_thunk;'.format(func.wrapped_name))
self.outln('}')
self.outln('')
self.outln('#endif /* !USING_DISPATCH_TABLE */')
for func in self.sorted_functions:
self.write_function_pointer(func)
def close(self):
if self.out_file:
self.out_file.close()
self.out_file = None
argparser = argparse.ArgumentParser(description='Generate GL dispatch wrappers.')
argparser.add_argument('files', metavar='file.xml', nargs='+', help='GL API XML files to be parsed')
argparser.add_argument('--outputdir', metavar='dir', required=False, help='Destination directory for files (default to current dir)')
argparser.add_argument('--includedir', metavar='dir', required=False, help='Destination directory for headers')
argparser.add_argument('--srcdir', metavar='dir', required=False, help='Destination directory for source')
argparser.add_argument('--source', dest='source', action='store_true', required=False, help='Generate the source file')
argparser.add_argument('--no-source', dest='source', action='store_false', required=False, help='Do not generate the source file')
argparser.add_argument('--header', dest='header', action='store_true', required=False, help='Generate the header file')
argparser.add_argument('--no-header', dest='header', action='store_false', required=False, help='Do not generate the header file')
args = argparser.parse_args()
if args.outputdir:
outputdir = args.outputdir
else:
outputdir = os.getcwd()
if args.includedir:
includedir = args.includedir
else:
includedir = outputdir
if args.srcdir:
srcdir = args.srcdir
else:
srcdir = outputdir
build_source = args.source
build_header = args.header
if not build_source and not build_header:
build_source = True
build_header = True
for f in args.files:
name = os.path.basename(f).split('.xml')[0]
generator = Generator(name)
generator.parse(f)
generator.drop_weird_glx_functions()
# This is an ANSI vs Unicode function, handled specially by
# include/epoxy/wgl.h
if 'wglUseFontBitmaps' in generator.functions:
del generator.functions['wglUseFontBitmaps']
generator.sort_functions()
generator.resolve_aliases()
generator.fixup_bootstrap_function('glGetString',
'epoxy_get_bootstrap_proc_address({0})')
generator.fixup_bootstrap_function('glGetIntegerv',
'epoxy_get_bootstrap_proc_address({0})')
# While this is technically exposed as a GLX extension, it's
# required to be present as a public symbol by the Linux OpenGL
# ABI.
generator.fixup_bootstrap_function('glXGetProcAddress',
'epoxy_glx_dlsym({0})')
generator.prepare_provider_enum()
if build_header:
generator.write_header(os.path.join(includedir, name + '_generated.h'))
if build_source:
generator.write_source(os.path.join(srcdir, name + '_generated_dispatch.c'))
generator.close()