The .spec files which were previously used are no longer maintained. Khronos advises to migrate to the XML Registry.
Note: This breaks the arb_multisample-pushpop test as it expects GL_ALL_ATTRIB_BITS not to include GL_MULTISAMPLE_BIT as the extension spec mandates. gl.xml defines GL_ALL_ATTRIB_BITS to include GL_MULTISAMPLE_BIT as GL 1.3 and later specs say. Signed-off-by: Fabian Bieler <[email protected]> --- CMakeLists.txt | 1 - cmake/piglit_dispatch.cmake | 2 +- cmake/piglit_glapi.cmake | 45 ---- glapi/parse_glspec.py | 555 ------------------------------------------- tests/util/gen_dispatch.py | 234 +++++++++++------- tests/util/piglit-dispatch.h | 1 + 6 files changed, 155 insertions(+), 683 deletions(-) delete mode 100644 cmake/piglit_glapi.cmake delete mode 100644 glapi/parse_glspec.py diff --git a/CMakeLists.txt b/CMakeLists.txt index bef9c35..68a19b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -376,7 +376,6 @@ configure_file( ) include(cmake/piglit_util.cmake) -include(cmake/piglit_glapi.cmake) include(cmake/piglit_dispatch.cmake) include_directories(src) diff --git a/cmake/piglit_dispatch.cmake b/cmake/piglit_dispatch.cmake index 0b0a2eb..78e9782 100644 --- a/cmake/piglit_dispatch.cmake +++ b/cmake/piglit_dispatch.cmake @@ -30,7 +30,7 @@ set(piglit_dispatch_gen_outputs set(piglit_dispatch_gen_inputs ${CMAKE_SOURCE_DIR}/tests/util/gen_dispatch.py - ${CMAKE_BINARY_DIR}/glapi/glapi.json + ${CMAKE_SOURCE_DIR}/glapi/gl.xml ) add_custom_command( diff --git a/cmake/piglit_glapi.cmake b/cmake/piglit_glapi.cmake deleted file mode 100644 index 59d4d42..0000000 --- a/cmake/piglit_glapi.cmake +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2012 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. - -# Note: we're outputting the generated file to a subdirectory of -# ${CMAKE_SOURCE_DIR} so that we can check it back in to source -# control. -set(piglit_glapi_src_dir ${CMAKE_SOURCE_DIR}/glapi) - -file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/glapi) - -set(piglit_glapi_output ${CMAKE_BINARY_DIR}/glapi/glapi.json) - -set(piglit_glapi_inputs - ${piglit_glapi_src_dir}/parse_glspec.py - ${piglit_glapi_src_dir}/gl.tm - ${piglit_glapi_src_dir}/gl.spec - ${piglit_glapi_src_dir}/enum.spec - ${piglit_glapi_src_dir}/enumext.spec - ${piglit_glapi_src_dir}/GLES3/gl3.h - ${piglit_glapi_src_dir}/GLES2/gl2ext.h - ) - -add_custom_command( - OUTPUT ${piglit_glapi_output} - DEPENDS ${piglit_glapi_inputs} - COMMAND ${python} ${piglit_glapi_inputs} ${piglit_glapi_output} - ) diff --git a/glapi/parse_glspec.py b/glapi/parse_glspec.py deleted file mode 100644 index bab4996..0000000 --- a/glapi/parse_glspec.py +++ /dev/null @@ -1,555 +0,0 @@ -# Copyright 2012 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. - -# This script generates a JSON description of the GL API based on -# source files published by www.opengl.org. -# -# The source file "gl.tm" is a CSV file mapping from abstract type -# names (in column 0) to C types (in column 3). All other columns are -# ignored. -# -# -# The source file "gl.spec" consists of a record for each API -# function, which looks like this: -# -# # Function name (excluding the "gl" prefix), followed by the names -# # of all function parameters: -# GetVertexAttribdvARB(index, pname, params) -# -# # Property/value pairs follow. Order is irrelevant. -# -# # "return" specifies the return type (as a reference to -# # column 0 of gl.tm). -# return void -# -# # "param" specifies the type of a single function parameter -# # (as a reference to column 0 of gl.tm). In addition, it -# # specifies whether the parameter is passed as an input -# # value, as an input or output array, or an input or output -# # reference. Input arrays and references get translated -# # into const pointers, and Output arrays and references get -# # translated into non-const pointers. Note that for arrays, -# # the size of the array appears in brackets after the word -# # "array". This value is ignored. -# param index UInt32 in value -# param pname VertexAttribPropertyARB in value -# param params Float64 out array [4] -# -# # "category" specifies which extension (or GL version) this -# # function was introduced in. For extensions, the category -# # name is the extension name (without the "GL_" prefix). -# # For GL versions, the category name looks like -# # e.g. "VERSION_1_0" or "VERSION_1_0_DEPRECATED" (for -# # deprecated features). -# category ARB_vertex_program -# -# # "alias" specifies the name of a function that is -# # behaviorally indistinguishable from this function (if -# # any). -# alias GetVertexAttribdv -# -# # Other property/value pairs are ignored. -# -# # Lines in any other format are ignored. -# -# -# The source file "enumext.spec" consists of lines of the form <enum -# name> = <value>, e.g.: -# -# FRONT = 0x0404 -# -# The enum name is the name without the "GL_" prefix. -# -# It is also permissible for the value to be a reference to an enum -# that appeared earlier, e.g.: -# -# DRAW_FRAMEBUFFER_BINDING = GL_FRAMEBUFFER_BINDING -# -# Note that when this happens the value *does* include the "GL_" -# prefix. -# -# -# The JSON output format is as follows: -# { -# "categories": { -# <category name>: { -# "kind": <"GL" or "GLES" for a GL version, "extension" for an extension> -# "gl_10x_version": <For a GL version, version number times 10>, -# "extension_name" <For an extension, name of the extension> -# }, ... -# }, -# "enums": { -# <enum name, without "GL_" prefix>: { -# "value_int": <value integer> -# "value_str": <value string> -# }, ... -# }, -# "functions": { -# <function name, without "gl" prefix>: { -# "categories": <list of categories in which this function appears>, -# "param_names": <list of param names>, -# "param_types": <list of param types>, -# "return_type": <type, or "void" if no return> -# }, ... -# }, -# "function_alias_sets": { -# <list of synonymous function names>, ... -# }, -# } - - -import collections -import csv -import re -import sys -try: - import simplejson as json -except ImportError: - import json - - -GLSPEC_HEADER_REGEXP = re.compile(r'^(\w+)\((.*)\)$') -GLSPEC_ATTRIBUTE_REGEXP = re.compile(r'^\s+(\w+)\s+(.*)$') -GL_VERSION_REGEXP = re.compile('^VERSION_([0-9])_([0-9])(_DEPRECATED)?$') -GLES_VERSION_REGEXP = re.compile('^GL_ES_VERSION_([0-9])_([0-9])(_DEPRECATED)?$') -ENUM_REGEXP = re.compile(r'^\s+(\w+)\s+=\s+(\w+)$') -EXTENSION_SUFFIX_REGEXP = re.compile(r'(ARB|EXT|KHR|OES|NV|AMD|IMG|QCOM|INTEL)$') - - -# Convert a type into a canonical form that is consistent about its -# use of spaces. -# -# Example input: 'const void**' -# Example output: 'const void * *' -def normalize_type(typ): - tokens = [token for token in typ.replace('*', ' * ').split(' ') - if token != ''] - return ' '.join(tokens) - - -# Interpret an enumerated value, which may be in hex or decimal, and -# may include a type suffix such as "ull". -# -# Example input: '0xFFFFFFFFul' -# Example output: 4294967295 -def decode_enum_value(value_str): - for suffix in ('u', 'ul', 'ull'): - if value_str.endswith(suffix): - value_str = value_str[:-len(suffix)] - break - return int(value_str, 0) - - -# Convert an object to a form that can be serialized to JSON. Python -# "set" objects are converted to lists. -def jsonize(obj): - if type(obj) in (set, frozenset): - return sorted(obj) - else: - raise Exception('jsonize failed for {0}'.format(type(obj))) - - -# Iterate through the lines of a file, discarding end-of-line comments -# delimited by "#". Blank lines are discarded, as well as any -# whitespace at the end of a line. -def filter_comments(f): - for line in f: - if '#' in line: - line = line[:line.find('#')] - line = line.rstrip() - if line != '': - yield line.rstrip() - - -# Convert a category name from the form used in the gl.spec file to -# the form we want to output in JSON. E.g.: -# -# - "VERSION_2_1" is converted into { 'kind': 'GL', 'gl_10x_version': 21 } -# -# - "ARB_foo" is converted into: -# { 'kind': 'extension', 'extension_name': 'GL_ARB_foo' } -# -# - "GL_ES_VERSION_2_0" is converted into: -# { 'kind': 'GLES', 'gl_10x_version': 20 } -# (this category is a piglit extension for local-gl.spec) -def translate_category(category_name): - m = GL_VERSION_REGEXP.match(category_name) - if m: - ones = int(m.group(1)) - tenths = int(m.group(2)) - return ('{0}.{1}'.format(ones, tenths), - {'kind': 'GL', 'gl_10x_version': 10 * ones + tenths}) - - m = GLES_VERSION_REGEXP.match(category_name) - if m: - ones = int(m.group(1)) - tenths = int(m.group(2)) - return ('GLES{0}.{1}'.format(ones, tenths), - {'kind': 'GLES', 'gl_10x_version': 10 * ones + tenths}) - - extension_name = 'GL_' + category_name - return (extension_name, - {'kind': 'extension', 'extension_name': extension_name}) - - -# Data structure keeping track of which function names are known, and -# which names are synonymous with which other names. -class SynonymMap(object): - def __init__(self): - # __name_to_synonyms maps from a function name to the set of - # all names that are synonymous with it (including itself). - self.__name_to_synonyms = {} - - # Add a single function name which is not (yet) known to be - # synonymous with any other name. No effect if the function name - # is already known. - def add_singleton(self, name): - if name not in self.__name_to_synonyms: - self.__name_to_synonyms[name] = frozenset([name]) - return self.__name_to_synonyms[name] - - # Add a pair of function names, and note that they are synonymous. - # Synonymity is transitive, so if either of the two function names - # previously had known synonyms, all synonyms are combined into a - # single set. - def add_alias(self, name, alias): - name_ss = self.add_singleton(name) - alias_ss = self.add_singleton(alias) - combined_set = name_ss | alias_ss - for n in combined_set: - self.__name_to_synonyms[n] = combined_set - - # Get a set of sets of synonymous functions. - def get_synonym_sets(self): - return frozenset(self.__name_to_synonyms.values()) - - -# In-memory representation of the GL API. -class Api(object): - def __init__(self): - # Api.type_translation is a dict mapping abstract type names - # to C types. It is based on the data in the gl.tm file. For - # example, the dict entry for String is: - # - # 'String': 'const GLubyte *' - self.type_translation = {} - - # Api.enums is a dict mapping enum names (without the 'GL_' - # prefix) to a dict containing (a) the enum value expressed as - # an integer, and (b) the enum value expressed as a C literal. - # It is based on the data in the gl.spec file. For example, - # the dict entry for GL_CLIENT_ALL_ATTRIB_BITS is: - # - # 'CLIENT_ALL_ATTRIB_BITS': { 'value_int': 4294967295, - # 'value_str': "0xFFFFFFFF" } - self.enums = {} - - # Api.functions is a dict mapping function names (without the - # 'gl' prefix) to a dict containing (a) the name of the - # category the function is in, (b) the function call parameter - # names, (c) the function call parameter types, and (d) the - # function return type. It is based on the data in the - # gl.spec file, cross-referenced against the type translations - # from the gl.tm file. For example, the dict entry for - # glAreTexturesResident is: - # - # 'AreTexturesResident': { - # 'category': '1.1', - # 'param_names': ['n', 'textures', 'residences'], - # 'param_types': ['GLsizei', 'const GLuint *', 'GLboolean *'], - # 'return_type': ['GLboolean'] } - self.functions = {} - - # Api.synonyms is a SynonymMap object which records which - # function names are aliases of each other. It is based on - # the "alias" declarations from the gl.spec file. - self.synonyms = SynonymMap() - - # Api.categories is a dict mapping category names to a dict - # describing the category. For categories representing a GL - # version, the dict entry looks like this: - # - # '2.1': { 'kind': 'GL', 'gl_10x_version': 21 } - # - # For categories representing a GLES version, the dict entry looks - # like this: - # - # 'GLES2.0': { 'kind': 'GLES', 'gl_10x_version': 20 } - # - # For categories representing an extension, the dict entry - # looks like this: - # - # 'GL_ARB_sync': { 'kind': 'extension', - # 'extension_name': 'GL_ARB_sync' } - self.categories = {} - - # Convert each line in the gl.tm file into a key/value pair in - # self.type_translation, mapping an abstract type name to a C - # type. - def read_gl_tm(self, f): - for line in csv.reader(filter_comments(f)): - name = line[0].strip() - typ = line[3].strip() - if typ == '*': - # gl.tm uses "*" to represent void (for void used as a - # return value). - typ = 'void' - self.type_translation[name] = normalize_type(typ) - - # Group the lines in the gl.spec file into triples (function_name, - # param_names, attributes). For example, the following gl.spec - # input: - # - # Foo(bar, baz): - # x value1 - # y value2 other_info - # y value3 more_info - # - # Produces this output triple: - # - # ('Foo', ['bar', 'baz'], - # {'x': ['value1'], 'y': ['value2 other_info', 'value3 more_info']}) - @staticmethod - def group_gl_spec_functions(f): - function_name = None - param_names = None - attributes = None - for line in filter_comments(f): - m = GLSPEC_HEADER_REGEXP.match(line) - if m: - if function_name: - yield function_name, param_names, attributes - function_name = m.group(1) - if m.group(2) == '': - param_names = [] - else: - param_names = [n.strip() for n in m.group(2).split(',')] - attributes = collections.defaultdict(list) - continue - m = GLSPEC_ATTRIBUTE_REGEXP.match(line) - if m: - attribute_type, attribute_data = m.groups() - attributes[attribute_type].append(attribute_data) - continue - continue - if function_name: - yield function_name, param_names, attributes - - def add_function(self, name, return_type, param_names, param_types, - category_string): - category, additional_data = translate_category(category_string) - if category not in self.categories: - self.categories[category] = additional_data - - if name not in self.functions: - self.functions[name] = { - 'return_type': return_type, - 'param_names': param_names, - 'param_types': param_types, - 'categories': [category] - } - else: - if category not in self.functions[name]['categories']: - self.functions[name]['categories'].append(category) - - self.synonyms.add_singleton(name) - - # Process the data in gl.spec, and populate self.functions, - # self.synonyms, and self.categories based on it. - def read_gl_spec(self, f): - for name, param_names, attributes in self.group_gl_spec_functions(f): - if name in self.functions: - raise Exception( - 'Function {0!r} appears more than once'.format(name)) - param_name_to_index = dict( - (param_name, index) - for index, param_name in enumerate(param_names)) - param_types = [None] * len(param_names) - if len(attributes['param']) != len(param_names): - raise Exception( - 'Function {0!r} has a different number of parameters and ' - 'param declarations'.format(name)) - for param_datum in attributes['param']: - param_datum_tokens = param_datum.split() - param_name = param_datum_tokens[0] - param_index = param_name_to_index[param_name] - param_base_type = self.type_translation[param_datum_tokens[1]] - param_dir = param_datum_tokens[2] - param_multiplicity = param_datum_tokens[3] - if param_types[param_index] is not None: - raise Exception( - 'Function {0!r} contains more than one param ' - 'declaration for parameter {1!r}'.format( - name, param_name)) - if param_multiplicity == 'value': - assert param_dir == 'in' - param_type = param_base_type - elif param_multiplicity in ('array', 'reference'): - if param_dir == 'in': - # Note: technically this is not correct if - # param_base_type is a pointer type (e.g. to - # make an "in array" of "void *" we should - # produce "void * const *", not "const void * - # *"). However, the scripts used by the GL - # consortium to produce glext.h from gl.spec - # produce "const void * *", and fortunately - # the only ill effect of this is that clients - # have to do a little more typecasting than - # they should. So to avoid confusing people, - # we're going to make the same mistake, so - # that the resulting function signatures match - # those in glext.h. - param_type = normalize_type( - 'const {0} *'.format(param_base_type)) - elif param_dir == 'out': - param_type = normalize_type( - '{0} *'.format(param_base_type)) - else: - raise Exception( - 'Function {0!r} parameter {1!r} uses unrecognized ' - 'direction {2!r}'.format(name, param_name, - param_dir)) - else: - raise Exception( - 'Function {0!r} parameter {1!r} uses unrecognized ' - 'multiplicity {2!r}'.format(name, param_name, - param_multiplicity)) - param_types[param_index] = param_type - if len(attributes['return']) != 1: - raise Exception( - 'Function {0!r} contains {1} return attributes'.format( - name, len(attributes['return']))) - return_type = self.type_translation[attributes['return'][0]] - if len(attributes['category']) != 1: - raise Exception( - 'Function {0!r} contains {1} category attributes'.format( - name, len(attributes['category']))) - category = attributes['category'][0] - self.add_function(name, return_type, param_names, param_types, - category) - for alias in attributes['alias']: - self.synonyms.add_alias(name, alias) - - def read_gles_header(self, f): - category = 'GL_ES_VERSION_2_0' - for line in f: - # The GLES gl3.h has typedefs, tokens, and prototypes, - # each listed after a comment indicating whether they're - # part of 2.0 core or 3.0 core. - # - # The gl2ext.h is split into groups of functions prefixed - # by the extension name in a comment. - if re.match(r'/\* OpenGL ES 2.0 \*/', line): - category = 'GL_ES_VERSION_2_0' - elif re.match(r'/\* OpenGL ES 3.0 \*/', line): - category = 'GL_ES_VERSION_3_0' - else: - m = re.match(r'/\* (GL_.*) \*/', line) - if m: - # replace only the first occurence of 'GL_' - category = m.group(1).replace('GL_', '', 1) - - m = re.match(r'GL_APICALL', line) - if m: - # We do the regexp in two parts to make sure that we - # actually do catch all the GL_APICALLs. - m = re.match(r'^GL_APICALL\s*(.*)\s*GL_APIENTRY' - '\s*gl(\w*)\s\((.*)\).*$', line) - return_type, name, args = m.groups() - - return_type = return_type.strip() - args = args.split(', ') - - if args == ['void']: - args = [] - param_names = [] - param_types = [] - for arg in args: - splitloc = max(arg.rfind(' '), arg.rfind('*')) - param_types.append(arg[:splitloc + 1]) - param_names.append(arg[splitloc + 1:]) - - self.add_function(name, return_type, param_names, param_types, - category) - - # Since we don't have alias information for - # extensions, assume that pretty much anything - # with the same base name as a core function is - # aliased with it. - # - # glTexImage3DOES is an exception because it - # doesn't have the border argument. - if name != 'TexImage3DOES': - corename = EXTENSION_SUFFIX_REGEXP.sub('', name) - if corename in self.functions: - self.synonyms.add_alias(corename, name) - - # Convert each line in the enumext.spec file into a key/value pair - # in self.enums, mapping an enum name to a dict. For example, the - # following enumext.spec input: - # - # CLIENT_ALL_ATTRIB_BITS = 0xFFFFFFFF # ClientAttribMask - # - # Produces the dict entry: - # - # 'CLIENT_ALL_ATTRIB_BITS': { 'value_int': 4294967295, - # 'value_str': "0xFFFFFFFF" } - def read_enumext_spec(self, f): - for line in filter_comments(f): - m = ENUM_REGEXP.match(line) - if m: - name, value = m.groups() - if value.startswith('GL_'): - value_rhs = value[3:] - value_int = self.enums[value_rhs]['value_int'] - else: - value_int = decode_enum_value(value) - self.enums[name] = {'value_str': value, - 'value_int': value_int} - - # Convert the stored API into JSON. To make diffing easier, all - # dictionaries are sorted by key, and all sets are sorted by set - # element. - def to_json(self): - return json.dumps({'categories': self.categories, - 'enums': self.enums, - 'functions': self.functions, - 'function_alias_sets': - self.synonyms.get_synonym_sets()}, - indent=2, sort_keys=True, default=jsonize) - - -if __name__ == '__main__': - api = Api() - with open(sys.argv[1]) as f: - api.read_gl_tm(f) - with open(sys.argv[2]) as f: - api.read_gl_spec(f) - with open(sys.argv[3]) as f: - api.read_enumext_spec(f) - with open(sys.argv[4]) as f: - api.read_enumext_spec(f) - with open(sys.argv[5]) as f: - api.read_gles_header(f) - with open(sys.argv[6]) as f: - api.read_gles_header(f) - with open(sys.argv[7], 'w') as f: - f.write(api.to_json()) diff --git a/tests/util/gen_dispatch.py b/tests/util/gen_dispatch.py index c97af94..d983513 100644 --- a/tests/util/gen_dispatch.py +++ b/tests/util/gen_dispatch.py @@ -20,41 +20,10 @@ # IN THE SOFTWARE. # This script generates a C file (and corresponding header) allowing -# Piglit to dispatch calls to OpenGL based on a JSON description of -# the GL API (and extensions). -# -# Invoke this script with 3 command line arguments: the JSON input -# filename, the C output filename, and the header outpit filename. -# -# -# The input looks like this: -# -# { -# "categories": { -# <category name>: { -# "kind": <"GL" or "GLES" for a GL spec API, "extension" for an extension>, -# "gl_10x_version": <For a GL version, version number times 10>, -# "extension_name" <For an extension, name of the extension> -# }, ... -# }, -# "enums": { -# <enum name, without "GL_" prefix>: { -# "value_int": <value integer> -# "value_str": <value string> -# }, ... -# }, -# "functions": { -# <function name, without "gl" prefix>: { -# "categories": <list of categories in which this function appears>, -# "param_names": <list of param names>, -# "param_types": <list of param types>, -# "return_type": <type, or "void" if no return> -# }, ... -# }, -# "function_alias_sets": { -# <list of synonymous function names>, ... -# }, -# } +# Piglit to dispatch calls to OpenGL based on the OpenGL API Registry gl.xml. +# +# Invoke this script with 3 command line arguments: the path to gl.xml, +# the C output filename, and the header output filename. # # # The generated header consists of the following: @@ -145,10 +114,7 @@ import collections import os.path import sys -try: - import simplejson as json -except: - import json +from xml.etree import ElementTree # Generate a top-of-file comment cautioning that the file is @@ -197,7 +163,6 @@ def fixup_param_name(name): else: return name - # Internal representation of a category. # # - For a category representing a GL version, Category.kind is 'GL' @@ -208,12 +173,15 @@ def fixup_param_name(name): # 'extension' and Category.extension_name is the extension name # (including the 'GL_' prefix). class Category(object): - def __init__(self, json_data): - self.kind = json_data['kind'] - if 'gl_10x_version' in json_data: - self.gl_10x_version = json_data['gl_10x_version'] - if 'extension_name' in json_data: - self.extension_name = json_data['extension_name'] + def __init__(self, xml_element): + if xml_element.tag == 'feature': + api = xml_element.attrib['api'] + number = xml_element.attrib['number'] + self.kind = 'GL' if api == 'gl' else 'GLES' + self.gl_10x_version = int(number.replace('.', '')) + else: + self.kind = 'extension' + self.extension_name = xml_element.attrib['name'] # Generate a human-readable representation of the category (for # use in generated comments). @@ -245,21 +213,41 @@ class Category(object): # - Function.return_type is the return type of the function, or 'void' # if the function has no return. # -# - Function.category is a Category object describing the extension or -# GL version the function is defined in. +# - Function.categories is a list of Category objects describing the extension +# or GL version the function is defined in. class Function(object): - def __init__(self, name, json_data): - self.name = name + def __init__(self, xml_function): + proto = xml_function.find('proto') + self.gl_name = proto.find('name').text self.param_names = [ - fixup_param_name(x) for x in json_data['param_names']] - self.param_types = json_data['param_types'] - self.return_type = json_data['return_type'] - self.categories = json_data['categories'] + fixup_param_name(x.find('name').text) + for x in xml_function.findall('param')] + self.param_types = [ + self.get_type(x) + for x in xml_function.findall('param')] + self.return_type = self.get_type(proto) + + # Categories are added later. + self.categories = [] + + # Helper to extract type information from gl.xml data. + @staticmethod + def get_type(xml_elem): + type_name = "" + if xml_elem.text: type_name += xml_elem.text + for child in xml_elem: + if child.tag == 'ptype': + if child.text: type_name += child.text + if child.tail: type_name += child.tail + if child.tag == 'name': + break + return type_name.strip() + - # Name of the function, with the 'gl' prefix. + # Name of the function, without the 'gl' prefix. @property - def gl_name(self): - return 'gl' + self.name + def name(self): + return self.gl_name.partition('gl')[2] # Name of the function signature typedef corresponding to this # function. E.g. for the glGetString function, this is @@ -297,9 +285,10 @@ class Function(object): # - Enum.value_str is the value of the enum, as a string suitable for # emitting as C code. class Enum(object): - def __init__(self, json_data): - self.value_int = json_data['value_int'] - self.value_str = json_data['value_str'] + def __init__(self, xml_enum): + value = xml_enum.attrib['value'] + self.value_int = int(value, base=0) + self.value_str = value # Data structure keeping track of a set of synonymous functions. Such @@ -379,17 +368,102 @@ class DispatchSet(object): # - Api.categories is a dict mapping category name to a Category # object. class Api(object): - def __init__(self, json_data): + def __init__(self, xml_root): + # Parse enums. self.enums = dict( - (key, Enum(value)) - for key, value in json_data['enums'].items()) + (xml_enum.attrib['name'], Enum(xml_enum)) + for xml_enums in xml_root.findall('enums') + for xml_enum in xml_enums.findall('enum')) + + # Parse function prototypes ("commands" in gl.xml lingo). + xml_functions = xml_root.find('commands').findall('command') self.functions = dict( - (key, Function(key, value)) - for key, value in json_data['functions'].items()) - self.function_alias_sets = json_data['function_alias_sets'] - self.categories = dict( - (key, Category(value)) - for key, value in json_data['categories'].items()) + (self.function_name(xml_function), Function(xml_function)) + for xml_function in xml_functions) + + self.fix_aliases(xml_functions) + + # Parse function alias data. + self.function_alias_sets = [[self.function_name(xml_function)] + for xml_function in xml_functions + if not self.function_is_alias(xml_function)] + aliases = [(self.function_name(xml_function), + self.function_alias(xml_function)) + for xml_function in xml_functions + if self.function_is_alias(xml_function)] + self.merge_aliases(self.function_alias_sets, aliases) + + self.categories = dict() + # Parse core api categories ("features" in gl.xml lingo). + for xml_feature in xml_root.findall('feature'): + api = xml_feature.attrib['api'] + number = xml_feature.attrib['number'] + name = number if (api == 'gl') else 'GLES' + number + self.categories[name] = Category(xml_feature) + self.add_category_to_functions(name, xml_feature) + # Parse GL extension categories. + for xml_extension in xml_root.find('extensions').findall('extension'): + name = xml_extension.attrib['name'] + self.categories[name] = Category(xml_extension) + self.add_category_to_functions(name, xml_extension) + + @staticmethod + def function_name(xml_function): + return xml_function.find('proto').find('name').text + + @staticmethod + def function_alias(xml_function): + return xml_function.find('alias').attrib['name'] + + @staticmethod + def function_is_alias(xml_function): + return xml_function.find('alias') is not None + + # glDebugMessageInsertARB and glDebugMessageControlARB are marked as + # aliases of the corresponding 4.3 core functions in gl.xml. This is not + # correct as the core functions accept additional enums for their type + # and severity parameters. Remove the aliases. + @classmethod + def fix_aliases(cls, xml_functions): + for xml_function in xml_functions: + if cls.function_name(xml_function) == 'glDebugMessageInsertARB': + xml_function.remove(xml_function.find('alias')) + if cls.function_name(xml_function) == 'glDebugMessageControlARB': + xml_function.remove(xml_function.find('alias')) + + # Helper function to build list of aliases lists. + # + # merged -- list of n-tuples of function names that are aliases of each + # other. + # to_merge -- list of 2-tuples to merge into merged. + # + # recursively try to merge aliases from to_merge into merged until to_merge + # is empty. + @staticmethod + def merge_aliases(merged, to_merge): + if not to_merge: + return + still_to_merge = [] + for alias in to_merge: + i = next((i + for i, f in enumerate(merged) + if alias[1] in f), None) + if i != None: + merged[i].append(alias[0]) + else: + still_to_merge.append(to_merge) + + Api.merge_aliases(merged, still_to_merge) + + # Add given category to all functions it requires. + # + # name -- category name e.g.: "1.5", "GLES2.0" or "GL_ARB_multitexture" + # xml_element -- xml element of category + def add_category_to_functions(self, name, xml_element): + for function in [cmd.attrib['name'] + for req in xml_element.findall('require') + for cmd in req.findall('command')]: + self.functions[function].categories.append(name) # Generate a list of (name, value) pairs representing all enums in # the API. The resulting list is sorted by enum value. @@ -436,8 +510,8 @@ class Api(object): # Read the given input file and return an Api object containing the # data in it. def read_api(filename): - with open(filename, 'r') as f: - return Api(json.load(f)) + tree = ElementTree.parse(filename) + return Api(tree.getroot()) # Generate the resolve function for a given DispatchSet. @@ -486,6 +560,11 @@ def generate_resolve_function(ds): # glTexImage3DEXT, cast the result to PFNGLTEXIMAGE3DPROC # to avoid a warning. typedef_name = 'PFNGLTEXIMAGE3DPROC' + elif f.name == 'ShaderSourceARB': + # As above: glShaderSourceARB defines the first argument as + # GLhandleARB, glShaderSource uses a GLuint. Both are + # typedefs of unsigned int. + typedef_name = 'PFNGLSHADERSOURCEPROC' else: typedef_name = f.typedef_name @@ -495,9 +574,8 @@ def generate_resolve_function(ds): condition_code_pairs.append((condition, code)) # XXX: glDraw{Arrays,Elements}InstancedARB are exposed by - # ARB_instanced_arrays in addition to ARB_draw_instanced, but neither - # gl.spec nor gl.json can accomodate an extension with two categories, so - # insert these cases here. + # ARB_instanced_arrays in addition to ARB_draw_instanced, but gl.xml + # ignores the former, so insert these cases here. if f.gl_name in ('glDrawArraysInstancedARB', 'glDrawElementsInstancedARB'): condition = 'check_extension("GL_ARB_instanced_arrays")' @@ -649,20 +727,14 @@ def generate_code(api): # Emit enum #defines for name, value in api.compute_unique_enums(): - h_contents.append('#define GL_{0} {1}\n'.format(name, value)) + h_contents.append('#define {0} {1}\n'.format(name, value)) # Emit extension #defines - # - # While enum.ext lists some old extension names (defined to 1), it - # doesn't contain the full set that appears in glext.h. h_contents.append('\n') for ext in api.extensions: h_contents.append('#define {0} 1\n'.format(ext)) # Emit GL version #defines - # - # While enum.ext lists GL versions up to 3.2, it didn't continue - # adding them for later GL versions. h_contents.append('\n') for ver in api.gl_versions: h_contents.append('#define GL_VERSION_{0}_{1} 1\n'.format( diff --git a/tests/util/piglit-dispatch.h b/tests/util/piglit-dispatch.h index 669aecc..cb2dbad 100644 --- a/tests/util/piglit-dispatch.h +++ b/tests/util/piglit-dispatch.h @@ -96,6 +96,7 @@ typedef unsigned short GLushort; typedef unsigned long GLulong; typedef float GLfloat; typedef float GLclampf; +typedef int GLclampx; typedef double GLdouble; typedef double GLclampd; typedef void GLvoid; -- 1.8.3.2 _______________________________________________ Piglit mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/piglit
