[Piglit] [PATCH 2/6] dispatch: Generate piglit-dispatch from Khronos XML
Chad Versace
chad.versace at linux.intel.com
Mon Jun 16 09:04:58 PDT 2014
Khronos now generates its headers from XML and no longer maintains the
old, crufty *.spec files. Piglit should do the same. Otherwise,
piglit-dispatch won't be able to pick up new extension functions.
As a really really big bonus, after Piglit starts generating its GL
dispatch code from gl.xml, it's a small step to start generating EGL and
GLX dispatch from egl.xml and glx.xml.
This patch imports 'gl.xml' into a new toplevel 'registry directory, to
follow the precedent of libepoxy.
I did *not* try to redesign piglit-dispatch in this patch. To the
contrary, I attempted to keep the newly generated dispatch code to be as
similar as possible as the old generated code. I did want to clean up
piglit-dispatch's design , but I refrained because "a patch should do
one thing, and do it well".
I strove to keep separate concerns in separate files. File
registry/gl.py parses registry/gl.xml. File tests/util/gen_dispatch.py
consumes the parsing result to generate the code. This decision kept
gen_dispatch.py small and focused.
I hope everyone finds the rewritten gen_dispatch.py more maintainable
and easy to read.
The generated code has changed as following:
- It now contains the GLES1 API, because gl.xml contains information
on all OpenGL APIs.
- The comment block for each function alias set now contains more
information. For each function in the set, it now lists the
complete set of providers.
For example:
/* glActiveTexture (GL_VERSION_1_3) (GL_VERSION_ES_CM_1_0) (GL_ES_VERSION_2_0) */
/* glActiveTextureARB (GL_ARB_multitexture) */
extern PFNGLACTIVETEXTUREPROC piglit_dispatch_glActiveTexture;
#define glActiveTexture piglit_dispatch_glActiveTexture
#define glActiveTextureARB piglit_dispatch_glActiveTexture
- Enums are sorted by group then by value. Old dispatch sorted only by
value.
For example:
/* Enum Group MapBufferUsageMask */
#define GL_MAP_READ_BIT 0x0001
#define GL_MAP_READ_BIT_EXT 0x0001
#define GL_MAP_WRITE_BIT 0x0002
#define GL_MAP_WRITE_BIT_EXT 0x0002
...
- After function resolve_glCheeseCake resolved the proc address for
glCheeseCake, old dispatch assigned the proc address to
piglit_dispatch_glCheeseCake. Function resolve_glCheeseCake returns
the proc address in new dispatch, and then function
stub_glCheeseCake performs the assignment to
piglit_dispatch_glCheeseCake. This difference resulted in no
behavioral change, and removed the need for superfluous typecasting.
Tested for regressions with:
piglit run -p x11_egl -x glx -x glean
Mesa 10.2.1
Intel Ivybridge
Fedora 20
Signed-off-by: Chad Versace <chad.versace at linux.intel.com>
---
cmake/piglit_dispatch.cmake | 7 +-
registry/__init__.py | 0
registry/gl.py | 832 +++++++++++++++++++++++++++++++++++++++
tests/util/gen_dispatch.py | 916 +++++++++++++------------------------------
tests/util/piglit-dispatch.h | 3 +-
5 files changed, 1109 insertions(+), 649 deletions(-)
create mode 100644 registry/__init__.py
create mode 100644 registry/gl.py
diff --git a/cmake/piglit_dispatch.cmake b/cmake/piglit_dispatch.cmake
index 0b0a2eb..b4ff2cf 100644
--- a/cmake/piglit_dispatch.cmake
+++ b/cmake/piglit_dispatch.cmake
@@ -19,6 +19,7 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
+set(piglit_dispatch_gen_script ${CMAKE_SOURCE_DIR}/tests/util/gen_dispatch.py)
set(piglit_dispatch_gen_output_dir ${CMAKE_BINARY_DIR}/tests/util)
file(MAKE_DIRECTORY ${piglit_dispatch_gen_output_dir})
@@ -29,14 +30,16 @@ set(piglit_dispatch_gen_outputs
)
set(piglit_dispatch_gen_inputs
+ ${CMAKE_SOURCE_DIR}/registry/gl.py
+ ${CMAKE_SOURCE_DIR}/registry/gl.xml
+ ${CMAKE_SOURCE_DIR}/registry/__init__.py
${CMAKE_SOURCE_DIR}/tests/util/gen_dispatch.py
- ${CMAKE_BINARY_DIR}/glapi/glapi.json
)
add_custom_command(
OUTPUT ${piglit_dispatch_gen_outputs}
DEPENDS ${piglit_dispatch_gen_inputs}
- COMMAND ${python} ${piglit_dispatch_gen_inputs} ${piglit_dispatch_gen_outputs}
+ COMMAND ${python} ${piglit_dispatch_gen_script} --out-dir ${piglit_dispatch_gen_output_dir}
)
add_custom_target(piglit_dispatch_gen
diff --git a/registry/__init__.py b/registry/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/registry/gl.py b/registry/gl.py
new file mode 100644
index 0000000..32adb10
--- /dev/null
+++ b/registry/gl.py
@@ -0,0 +1,832 @@
+# Copyright 2014 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.
+
+"""
+Parse gl.xml into Python objects.
+"""
+
+import os.path
+import re
+import sys
+import xml.etree.ElementTree as ElementTree
+from collections import namedtuple, OrderedDict
+from textwrap import dedent
+
+# Export 'debug' so other Piglit modules can easily enable it.
+debug = False
+
+def _log_debug(msg):
+ if debug:
+ sys.stderr.write('debug: {0}: {1}\n'.format(__name__, msg))
+
+def parse():
+ """Parse gl.xml and return a Registry object."""
+ filename = os.path.join(os.path.dirname(__file__), 'gl.xml')
+ elem = ElementTree.parse(filename)
+ xml_registry = elem.getroot()
+ return Registry(xml_registry)
+
+class OrderedKeyedSet(object):
+ """A set with keyed elements that preserves order of element insertion.
+
+ Why waste words? Let's document the class with example code.
+
+ Example:
+ Cheese = namedtuple('Cheese', ('name', 'flavor'))
+ cheeses = OrderedKeyedSet(key='name')
+ cheeses.add(Cheese(name='cheddar', flavor='good'))
+ cheeses.add(Cheese(name='gouda', flavor='smells like feet'))
+ cheeses.add(Cheese(name='romano', flavor='awesome'))
+
+ # Elements are retrievable by key.
+ assert(cheeses['gouda'].flavor == 'smells like feet')
+
+ # On key collision, the old element is removed.
+ cheeses.add(Cheese(name='gouda', flavor='ok i guess'))
+ assert(cheeses['gouda'].flavor == 'ok i guess')
+
+ # The set preserves order of insertion. Replacement does not alter
+ # order.
+ assert(list(cheeses)[2].name == 'romano')
+
+ # The set is iterable.
+ for cheese in cheeses:
+ print(cheese.name)
+
+ # Yet another example...
+ Bread = namedtuple('Bread', ('name', 'smell'))
+ breads = OrderedKeyedSet(key='name')
+ breads.add(Bread(name='como', smell='subtle')
+ breads.add(Bread(name='sourdough', smell='pleasant'))
+
+ # The set supports some common set operations, such as union.
+ breads_and_cheeses = breads | cheeses
+ assert(len(breads_and_cheeses) == len(breads) + len(cheeses))
+ """
+
+ def __init__(self, key, elems=()):
+ """Create a new set with the given key.
+
+ The given 'key' defines how to calculate each element's key value. If
+ 'key' is a string, then each key value is defined to be `getattr(elem, key)`.
+ If 'key' is a function, then the key value is `key(elem)`.
+ """
+ if isinstance(key, str):
+ self.__key_func = lambda elem: getattr(elem, key)
+ else:
+ self.__key_func = key
+
+ self.__dict = OrderedDict()
+ for i in elems:
+ self.add(i)
+
+ def __or__(x, y):
+ """Same as `union`."""
+ return x.union(y)
+
+ def __contains__(self, key):
+ return key in self.__dict
+
+ def __delitem__(self, key):
+ del self.__dict[key]
+
+ def __getitem__(self, key):
+ return self.__dict[key]
+
+ def __iter__(self):
+ return self.__dict.itervalues()
+
+ def __len__(self):
+ return len(self.__dict)
+
+ def __repr__(self):
+ return '{0}({1})'.format(self.__class__.__name__, list(self.keys()))
+
+ def copy(self):
+ """Return shallow copy."""
+ other = OrderedKeyedSet(key=self.__key_func)
+ other.__dict = self.__dict.copy()
+ return other
+
+ def add(self, x):
+ self.__dict[self.__key_func(x)] = x
+
+ def clear(self):
+ self.__dict.clear()
+
+ def extend(self, elems):
+ for e in elems:
+ self.add(e)
+
+ def get(self, key, default):
+ return self.__dict.get(key, default)
+
+ def get_key(self, x):
+ return self.__key_func(x)
+
+ def items(self, x):
+ return self.__dict.iteritems()
+
+ def keys(self):
+ return self.__dict.iterkeys()
+
+ def values(self):
+ return self.__dict.itervalues()
+
+ def pop(self, key):
+ return self.__dict.pop(key)
+
+ def sort_by_key(self):
+ self.__dict = OrderedDict(sorted(self.items(), key=lambda i: i[0]))
+
+ def sort_by_value(self):
+ self.__dict = OrderedDict(sorted(self.items(), key=lambda i: i[1]))
+
+ def union(self, other):
+ """Return the union of two sets as a new set.
+
+ The the new set is ordered so that all elements of self precede those
+ of other, and the order of elements within each origin set is
+ preserved.
+
+ The new set's key function is taken from self. On key collisions, set
+ y has precedence over x.
+ """
+ u = self.copy()
+ u.extend(other)
+ return u
+
+class Registry(object):
+ """The toplevel <registry> element.
+
+ Attributes:
+ features: An OrderedKeyedSet of class Feature, where each element
+ represents a <feature> element.
+
+ extensions: An OrderedKeyedSet of class Extension, where each element
+ represents a <extension> element.
+
+ commands: An OrderedKeyedSet of class Command, where each element
+ represents a <command> element.
+
+ enum_groups: An ordered collection of class EnumGroup, where each
+ element represents an <enums> element.
+
+ enums: An OrderedKeyedSet of class Enum, where each element
+ represents an <enum> element.
+
+ vendor_namespaces: A collection of all vendor prefixes and suffixes.
+ For example, "ARB", "EXT", "CHROMIUM", "NV".
+ """
+
+ def __init__(self, xml_registry):
+ """Parse the <registry> element."""
+
+ assert(xml_registry.tag == 'registry')
+
+ self.features = OrderedKeyedSet(
+ key='name', elems=(
+ Feature(xml_feature)
+ for xml_feature in xml_registry.iterfind('./feature')
+ )
+ )
+
+ self.extensions = OrderedKeyedSet(
+ key='name', elems=(
+ Extension(xml_extension)
+ for xml_extension in xml_registry.iterfind('./extensions/extension')
+ )
+ )
+
+ self.commands = OrderedKeyedSet(
+ key='name', elems=(
+ Command(xml_command)
+ for xml_command in xml_registry.iterfind('./commands/command')
+ )
+ )
+
+ self.enum_groups = [
+ EnumGroup(xml_enums)
+ for xml_enums in xml_registry.iterfind('./enums')
+ ]
+
+ self.__fix_enum_groups()
+
+ self.enums = OrderedKeyedSet(
+ key='name', elems=(
+ enum
+ for group in self.enum_groups
+ for enum in group.enums
+ if not enum.is_collider
+ )
+ )
+
+ self.vendor_namespaces = {
+ extension.vendor_namespace
+ for extension in self.extensions
+ if extension.vendor_namespace is not None
+ }
+
+ for feature in self.features:
+ feature._link_commands(self.commands)
+ feature._link_enums(self.enums)
+
+ for extension in self.extensions:
+ extension._link_commands(self.commands)
+ extension._link_enums(self.enums)
+
+ self.__set_command_name_parts()
+
+ def __fix_enum_groups(self):
+ for enum_group in self.enum_groups:
+ if (enum_group.name is None
+ and enum_group.vendor == 'SGI'
+ and enum_group.start == '0x8000'
+ and enum_group.end == '0x80BF'):
+ self.enum_groups.remove(enum_group)
+ elif (enum_group.name is None
+ and enum_group.vendor == 'ARB'
+ and enum_group.start == None
+ and enum_group.end == None):
+ enum_group.start = '0x8000'
+ enum_group.start = '0x80BF'
+
+ def __set_command_name_parts(self):
+ suffix_choices = '|'.join(self.vendor_namespaces)
+ regex = '^(?P<basename>[a-zA-Z0-9_]+?)(?P<vendor_suffix>{suffix_choices})?$'
+ regex = regex.format(suffix_choices=suffix_choices)
+ _log_debug('setting command name parts with regex {0!r}'.format(regex))
+ regex = re.compile(regex)
+ for command in self.commands:
+ command._set_name_parts(regex)
+
+class Feature(object):
+ """A <feature> XML element.
+
+ Attributes:
+ name: The XML element's 'name' attribute.
+
+ api: The XML element's 'api' attribute.
+
+ version_str: The XML element's 'number' attribute. For example, "3.1".
+
+ version_float: float(version_str)
+
+ version_int: int(10 * version_float)
+
+ commands: An OrderedKeyedSet of class Command that contains all
+ <command> subelements.
+
+ enums: An OrderedKeyedSet of class Enum that contains all <enum>
+ subelements.
+ """
+
+ def __init__(self, xml_feature):
+ """Parse a <feature> element."""
+
+ # Example <feature> element:
+ #
+ # <feature api="gles2" name="GL_ES_VERSION_3_1" number="3.1">
+ # <!-- arrays_of_arrays features -->
+ # <require/>
+ # <!-- compute_shader features -->
+ # <require>
+ # <command name="glDispatchCompute"/>
+ # <command name="glDispatchComputeIndirect"/>
+ # <enum name="GL_COMPUTE_SHADER"/>
+ # <enum name="GL_MAX_COMPUTE_UNIFORM_BLOCKS"/>
+ # ...
+ # </require>
+ # <!-- draw_indirect features -->
+ # <require>
+ # <command name="glDrawArraysIndirect"/>
+ # <command name="glDrawElementsIndirect"/>
+ # <enum name="GL_DRAW_INDIRECT_BUFFER"/>
+ # <enum name="GL_DRAW_INDIRECT_BUFFER_BINDING"/>
+ # </require>
+ # ...
+ # </feature>
+
+ assert(xml_feature.tag == 'feature')
+ self._xml_feature = xml_feature
+
+ # Parse the <feature> tag's attributes.
+ self.api = xml_feature.get('api')
+ self.name = xml_feature.get('name')
+
+ self.version_str = xml_feature.get('number')
+ self.version_float = float(self.version_str)
+ self.version_int = int(10 * self.version_float)
+
+ self.commands = OrderedKeyedSet(key='name')
+ self.enums = OrderedKeyedSet(key='name')
+
+ def _link_commands(self, commands):
+ """Parse <command> subelements and link them to the Command objects in
+ 'commands'.
+ """
+ for xml_command in self._xml_feature.iterfind('./require/command'):
+ command_name = xml_command.get('name')
+ command = commands[command_name]
+ assert(isinstance(command, Command))
+ _log_debug('link command {0!r} and feature {1!r})'.format(command.name, self.name))
+ self.commands.add(command)
+ command.features.add(self)
+
+ def _link_enums(self, enums):
+ """Parse <enum> subelements and link them to the Command objects in
+ 'enums'.
+ """
+ for xml_enum in self._xml_feature.iterfind('./require/enum'):
+ enum_name = xml_enum.get('name')
+ enum = enums[enum_name]
+ assert(isinstance(enum, Enum))
+ _log_debug('link command {0!r} and feature {1!r}'.format(enum.name, self.name))
+ self.enums.add(enum)
+ enum.features.add(self)
+
+class Extension(object):
+ """An <extension> XML element.
+
+ Attributes:
+ name: The XML element's 'name' attribute.
+
+ supported_apis: The collection of api strings in the XML element's
+ 'supported' attribute. For example, ['gl', 'glcore'].
+
+ vendor_namespace: For example, "AMD". May be None.
+
+ commands: An OrderedKeyedSet of class Command that contains all
+ <command> subelements.
+
+ enums: An OrderedKeyedSet of class Enum that contains all <enum>
+ subelements.
+ """
+
+ __vendor_regex = re.compile(r'^GL_(?P<vendor_namespace>[A-Z]+)_')
+
+ def __init__(self, xml_extension):
+ """Parse an <extension> element."""
+
+ # Example <extension> element:
+ # <extension name="GL_ARB_ES2_compatibility" supported="gl|glcore">
+ # <require>
+ # <enum name="GL_FIXED"/>
+ # <enum name="GL_IMPLEMENTATION_COLOR_READ_TYPE"/>
+ # ...
+ # <command name="glReleaseShaderCompiler"/>
+ # <command name="glShaderBinary"/>
+ # ...
+ # </require>
+ # </extension>
+
+ assert(xml_extension.tag == 'extension')
+ self._xml_extension = xml_extension
+
+ # Parse the <extension> tag's attributes.
+ self.name = xml_extension.get('name')
+ self.supported_apis = xml_extension.get('supported').split('|')
+
+ self.vendor_namespace = None
+ match = Extension.__vendor_regex.match(self.name)
+ if match is not None:
+ self.vendor_namespace = match.groupdict().get('vendor_namespace', None)
+
+ self.commands = OrderedKeyedSet(key='name')
+ self.enums = OrderedKeyedSet(key='name')
+
+ def __cmp__(x, y):
+ """Compare by name."""
+ return cmp(x.name, y.name)
+
+ def _link_commands(self, commands):
+ """Parse <command> subelements and link them to the Command objects in
+ 'commands'.
+ """
+ for xml_command in self._xml_extension.iterfind('./require/command'):
+ command_name = xml_command.get('name')
+ command = commands[command_name]
+ assert(isinstance(command, Command))
+ _log_debug('link command {0!r} and extension {1!r}'.format(command.name, self.name))
+ self.commands.add(command)
+ command.extensions.add(self)
+
+ def _link_enums(self, enums):
+ """Parse <enum> subelements and link them to the Command objects in
+ 'enums'.
+ """
+ for xml_enum in self._xml_extension.iterfind('./require/enum'):
+ enum_name = xml_enum.get('name')
+ enum = enums[enum_name]
+ assert(isinstance(enum, Enum))
+ _log_debug('link command {0!r} and extension {1!r}'.format(enum.name, self.name))
+ self.enums.add(enum)
+ enum.extensions.add(self)
+
+class CommandParam(object):
+ """A <param> XML element at path command/param.
+
+ Attributes:
+ name
+ c_type
+ """
+
+ __PARAM_NAME_FIXES = {'near': 'hither', 'far': 'yon'}
+
+ def __init__(self, xml_param, log=None):
+ """Parse a <param> element."""
+
+ # Example <param> elements:
+ #
+ # <param>const <ptype>GLchar</ptype> *<name>name</name></param>
+ # <param len="1"><ptype>GLsizei</ptype> *<name>length</name></param>
+ # <param len="bufSize"><ptype>GLint</ptype> *<name>values</name></param>
+ # <param><ptype>GLenum</ptype> <name>shadertype</name></param>
+ # <param group="sync"><ptype>GLsync</ptype> <name>sync</name></param>
+
+ assert(xml_param.tag == 'param')
+
+ self.name = xml_param.find('./name').text
+
+ # Rename the parameter if its name is a reserved keyword in MSVC.
+ self.name = self.__PARAM_NAME_FIXES.get(self.name, self.name)
+
+ # Pare the C type.
+ c_type_text = list(xml_param.itertext())
+ c_type_text.pop(-1) # Pop off the text from the <name> subelement.
+ c_type_text = (t.strip() for t in c_type_text)
+ self.c_type = ' '.join(c_type_text).strip()
+
+ _log_debug('parsed {0}'.format(self))
+
+ def __repr__(self):
+ templ = (
+ '{self.__class__.__name__}('
+ 'name={self.name!r}, '
+ 'type={self.c_type!r})')
+ return templ.format(self=self)
+
+class Command(object):
+ """A <command> XML element.
+
+ Attributes:
+ name: The XML element's 'name' attribute.
+
+ features: An OrderedKeyedSet of class Feature that enumerates all
+ features that require this command.
+
+ extensions: An OrderedKeyedSet of class Extensions that enumerates all
+ extensions that require this command.
+
+ c_return_type: For example, "void *".
+
+ alias: The XML element's 'alias' element. May be None.
+
+ param_list: List of class CommandParam that contains all <param>
+ subelements.
+ """
+
+ def __init__(self, xml_command):
+ """Parse a <command> element."""
+
+ # Example <command> element:
+ #
+ # <command>
+ # <proto>void <name>glTexSubImage2D</name></proto>
+ # <param group="TextureTarget"><ptype>GLenum</ptype> <name>target</name></param>
+ # <param group="CheckedInt32"><ptype>GLint</ptype> <name>level</name></param>
+ # <param group="CheckedInt32"><ptype>GLint</ptype> <name>xoffset</name></param>
+ # <param group="CheckedInt32"><ptype>GLint</ptype> <name>yoffset</name></param>
+ # <param><ptype>GLsizei</ptype> <name>width</name></param>
+ # <param><ptype>GLsizei</ptype> <name>height</name></param>
+ # <param group="PixelFormat"><ptype>GLenum</ptype> <name>format</name></param>
+ # <param group="PixelType"><ptype>GLenum</ptype> <name>type</name></param>
+ # <param len="COMPSIZE(format,type,width,height)">const void *<name>pixels</name></param>
+ # <glx type="render" opcode="4100"/>
+ # <glx type="render" opcode="332" name="glTexSubImage2DPBO" comment="PBO protocol"/>
+ # </command>
+ #
+
+ assert(xml_command.tag == 'command')
+ self.__xml_command = xml_command
+
+ xml_proto = xml_command.find('./proto')
+ self.name = xml_proto.find('./name').text
+ _log_debug('start parsing Command(name={0!r})'.format(self.name))
+
+ self.features = OrderedKeyedSet(key='name')
+ self.extensions = OrderedKeyedSet(key='name')
+
+ # Parse the return type from the <proto> element.
+ #
+ # Example of a difficult <proto> element:
+ # <proto group="String">const <ptype>GLubyte</ptype> *<name>glGetStringi</name></proto>
+ c_return_type_text = list(xml_proto.itertext())
+ c_return_type_text.pop(-1) # Pop off the text from the <name> subelement.
+ c_return_type_text = (t.strip() for t in c_return_type_text)
+ self.c_return_type = ' '.join(c_return_type_text).strip()
+
+ # Parse alias info, if any.
+ xml_alias = xml_command.find('./alias')
+ if xml_alias is None:
+ self.alias = None
+ else:
+ self.alias = xml_alias.get('name')
+
+ self.param_list = [
+ CommandParam(xml_param)
+ for xml_param in xml_command.iterfind('./param')
+ ]
+
+ _log_debug(('parsed {self.__class__.__name__}('
+ 'name={self.name!r}, '
+ 'alias={self.alias!r}, '
+ 'prototype={self.c_prototype!r}, '
+ 'features={self.features}, '
+ 'extensions={self.extensions})').format(self=self))
+
+ def __cmp__(x, y):
+ return cmp(x.name, y.name)
+
+ def __repr__(self):
+ cls = self.__class__
+ return '{cls.__name__}({self.name!r})'.format(**locals())
+
+ @property
+ def c_prototype(self):
+ """For example, "void glAccum(GLenum o, GLfloat value)"."""
+ return '{self.c_return_type} {self.name}({self.c_named_param_list})'.format(self=self)
+
+ @property
+ def c_funcptr_typedef(self):
+ """For example, "PFNGLACCUMROC" for glAccum."""
+ return 'PFN{0}PROC'.format(self.name).upper()
+
+ @property
+ def c_named_param_list(self):
+ """For example, "GLenum op, GLfloat value" for glAccum."""
+ return ', '.join([
+ '{param.c_type} {param.name}'.format(param=param)
+ for param in self.param_list
+ ])
+
+ @property
+ def c_unnamed_param_list(self):
+ """For example, "GLenum, GLfloat" for glAccum."""
+ return ', '.join([
+ param.c_type
+ for param in self.param_list
+ ])
+
+ @property
+ def c_untyped_param_list(self):
+ """For example, "op, value" for glAccum."""
+ return ', '.join([
+ param.name
+ for param in self.param_list
+ ])
+
+ @property
+ def requirements(self):
+ """The union of features and extensions that require this commnand."""
+ return self.features | self.extensions
+
+ def _set_name_parts(self, regex):
+ groups = regex.match(self.name).groupdict()
+ self.basename = groups['basename']
+ self.vendor_suffix = groups.get('vendor_suffix', None)
+
+class EnumGroup(object):
+ """An <enums> element at path registry/enums.
+
+ Attributes:
+ name: The XML element's 'group' attribute. If the XML does not define
+ 'group', then this class invents one.
+
+ type: The XML element's 'type' attribute. If the XML does not define
+ 'type', then this class invents one.
+
+ start, end: The XML element's 'start' and 'end' attributes. May be
+ None.
+
+ enums: An OrderedKeyedSet of class Enum that contains all <enum>
+ sublements.
+ """
+
+ # Each EnumGroup belongs to exactly one member of EnumGroup.TYPES.
+ #
+ # Some members in EnumGroup.TYPES are invented and not present in gl.xml.
+ # The only enum type defined explicitly in gl.xml is "bitmask", which
+ # occurs as <enums type="bitmask">. However, in gl.xml each block of
+ # non-bitmask enums is introduced by a comment that describes the block's
+ # "type", even if the <enums> tag lacks a 'type' attribute. (Thanks,
+ # Khronos, for encoding data in XML comments rather than the XML itself).
+ # EnumGroup.TYPES lists such implicit comment-only types, with invented
+ # names, alongside the types explicitly defined by <enums type=>.
+ TYPES = (
+ # Type 'default_namespace' is self-explanatory. It indicates the large
+ # set of enums from 0x0000 to 0xffff that includes, for example,
+ # GL_POINTS and GL_TEXTURE_2D.
+ 'default_namespace',
+
+ # Type 'bitmask' is self-explanatory.
+ 'bitmask',
+
+ # Type 'small_index' indicates a small namespace of non-bitmask enums.
+ # As of Khronos revision 26792, 'small_index' groups generally contain
+ # small numbers used for indexed access.
+ 'small_index',
+
+ # Type 'special' is used only for the group named "SpecialNumbers". The
+ # group contains enums such as GL_FALSE, GL_ZERO, and GL_INVALID_INDEX.
+ 'special',
+ )
+
+ @staticmethod
+ def __fix_xml(xml_elem):
+ """Add missing attributes and fix misnamed ones."""
+ if xml_elem.get('namespace') == 'OcclusionQueryEventMaskAMD':
+ # This tag's attributes are totally broken.
+ xml_elem.set('namespace', 'GL')
+ xml_elem.set('group', 'OcclusionQueryEventMaskAMD')
+ xml_elem.set('type', 'bitmask')
+ elif xml_elem.get('vendor', None) == 'ARB':
+ enums = xml_elem.findall('./enum')
+ if (len(enums) != 0
+ and enums[0].get('value') == '0x8000'
+ and enums[-1].get('value') == '0x80BF'):
+ # This tag lacks 'start' and 'end' attributes.
+ xml_elem.set('start','0x8000')
+ xml_elem.set('end', '0x80BF')
+
+ def __init__(self, xml_enums):
+ """Parse an <enums> element."""
+
+ # Example of a bitmask group:
+ #
+ # <enums namespace="GL" group="SyncObjectMask" type="bitmask">
+ # <enum value="0x00000001" name="GL_SYNC_FLUSH_COMMANDS_BIT"/>
+ # <enum value="0x00000001" name="GL_SYNC_FLUSH_COMMANDS_BIT_APPLE"/>
+ # </enums>
+ #
+ # Example of a group that resides in OpenGL's default enum namespace:
+ #
+ # <enums namespace="GL" start="0x0000" end="0x7FFF" vendor="ARB" comment="...">
+ # <enum value="0x0000" name="GL_POINTS"/>
+ # <enum value="0x0001" name="GL_LINES"/>
+ # <enum value="0x0002" name="GL_LINE_LOOP"/>
+ # ...
+ # </enums>
+ #
+ # Example of a non-bitmask group that resides outside OpenGL's default
+ # enum namespace:
+ #
+ # <enums namespace="GL" group="PathRenderingTokenNV" vendor="NV">
+ # <enum value="0x00" name="GL_CLOSE_PATH_NV"/>
+ # <enum value="0x02" name="GL_MOVE_TO_NV"/>
+ # <enum value="0x03" name="GL_RELATIVE_MOVE_TO_NV"/>
+ # ...
+ # </enums>
+ #
+
+ EnumGroup.__fix_xml(xml_enums)
+
+ self.name = xml_enums.get('group', None)
+ _log_debug(('start parsing {self.__class__.__name__}('
+ 'name={self.name!r}').format(self=self))
+ self.type = xml_enums.get('type', None)
+ self.start = xml_enums.get('start', None)
+ self.end = xml_enums.get('end', None)
+ self.enums = []
+
+ self.__invent_name_and_type()
+ assert(self.name is not None)
+ assert(self.type in self.TYPES)
+
+ # Parse the <enum> subelements.
+ _log_debug('start parsing subelements of {self}'.format(self=self))
+ self.enums = OrderedKeyedSet(
+ key='name', elems=(
+ Enum(self, xml_enum)
+ for xml_enum in xml_enums.iterfind('./enum')
+ )
+ )
+ _log_debug('parsed {0}'.format(self))
+
+ def __repr__(self):
+ return (
+ '{self.__class__.__name__}('
+ 'name={self.name!r}, '
+ 'type={self.type!r}, '
+ 'start={self.start}, '
+ 'end={self.end}, '
+ 'enums={enums})'
+ ).format(
+ self=self,
+ enums= ', '.join([repr(e) for e in self.enums]),
+ )
+
+ def __invent_name_and_type(self):
+ """If the XML didn't define a name or type, invent one."""
+ if self.name is None:
+ assert(self.type is None)
+ assert(self.start is not None)
+ assert(self.end is not None)
+ self.name = 'range_{self.start}_{self.end}'.format(self=self)
+ self.type = 'default_namespace'
+ elif self.type is None:
+ self.type = 'small_index'
+ elif self.name == 'SpecialNumbers':
+ assert(self.type is None)
+ self.type = 'special'
+
+ def __post_fix_xml(self):
+ if (self.vendor == "ARB"
+ and self.start is None
+ and self.end is None
+ and self.enums[0].num_value == 0x8000
+ and self.enums[-1].num_value == 0x80BF):
+ self.start = '0x8000'
+ self.end = '0x0BF'
+
+class Enum(object):
+ """An <enum> XML element.
+
+ Attributes:
+ name, api: The XML element's 'name' and 'api' attributes.
+
+ str_value, c_num_literal: Equivalent attributes. The XML element's
+ 'value' attribute.
+
+ num_value: The long integer for str_value.
+
+ features: An OrderedKeyedSet of class Feature that enumerates all
+ features that require this enum.
+
+ extensions: An OrderedKeyedSet of class Extensions that enumerates all
+ extensions that require this enum.
+ """
+
+ def __init__(self, enum_group, xml_enum):
+ """Parse an <enum> tag located at path registry/enums/enum."""
+
+ # Example <enum> element:
+ # <enum value="0x0000" name="GL_POINTS"/>
+
+ assert(isinstance(enum_group, EnumGroup))
+ assert(xml_enum.tag == 'enum')
+
+ self.group = enum_group
+ self.name = xml_enum.get('name')
+ self.api = xml_enum.get('api')
+ self.str_value = xml_enum.get('value')
+ self.features = OrderedKeyedSet(key='name')
+ self.extensions = OrderedKeyedSet(key='name')
+ self.c_num_literal = self.str_value
+
+ if '0x' in self.str_value.lower():
+ base = 16
+ else:
+ base = 10
+ self.num_value = long(self.str_value, base)
+
+ _log_debug('parsed {0}'.format(self))
+
+ def __repr__(self):
+ return ('{self.__class__.__name__}('
+ 'name={self.name!r}, '
+ 'value={self.str_value!r}, '
+ 'features={self.features}, '
+ 'extensions={self.extensions})').format(self=self)
+
+ @property
+ def is_collider(self):
+ """Does the enum's name collide with another enum's name?"""
+ if self.name == 'GL_ACTIVE_PROGRAM_EXT' and self.api == 'gles2':
+ # This enum has different numerical values in GL (0x8B8D) and
+ # in GLES (0x8259).
+ return True
+ else:
+ return False
+
+ @property
+ def requirements(self):
+ return self.features | self.extensions
diff --git a/tests/util/gen_dispatch.py b/tests/util/gen_dispatch.py
index c97af94..d445932 100644
--- a/tests/util/gen_dispatch.py
+++ b/tests/util/gen_dispatch.py
@@ -1,4 +1,4 @@
-# Copyright 2012 Intel Corporation
+# Copyright 2014 Intel Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@@ -19,664 +19,290 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# 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>, ...
-# },
-# }
-#
-#
-# The generated header consists of the following:
-#
-# - A typedef for each function, of the form that would normally
-# appear in gl.h or glext.h, e.g.:
-#
-# typedef GLvoid * (APIENTRY *PFNGLMAPBUFFERPROC)(GLenum, GLenum);
-# typedef GLvoid * (APIENTRY *PFNGLMAPBUFFERARBPROC)(GLenum, GLenum);
-#
-# - A set of extern declarations for "dispatch function pointers".
-# There is one dispatch function pointer for each set of synonymous
-# functions in the GL API, e.g.:
-#
-# extern PFNGLMAPBUFFERPROC piglit_dispatch_glMapBuffer;
-#
-# - A set of #defines mapping each function name to the corresponding
-# dispatch function pointer, e.g.:
-#
-# #define glMapBuffer piglit_dispatch_glMapBuffer
-# #define glMapBufferARB piglit_dispatch_glMapBuffer
-#
-# - A #define for each enum in the GL API, e.g.:
-#
-# #define GL_FRONT 0x0404
-#
-# - A #define for each extension, e.g.:
-#
-# #define GL_ARB_vertex_buffer_object 1
-#
-# - A #define for each known GL version, e.g.:
-#
-# #define GL_VERSION_1_5 1
-#
-#
-# The generated C file consists of the following:
-#
-# - A resolve function corresponding to each set of synonymous
-# functions in the GL API. The resolve function determines which of
-# the synonymous names the implementation supports (by consulting
-# the current GL version and/or the extension string), and calls
-# either get_core_proc() or get_ext_proc() to get the function
-# pointer. It stores the result in the dispatch function pointer,
-# and then returns it as a generic void(void) function pointer. If
-# the implementation does not support any of the synonymous names,
-# it calls unsupported(). E.g.:
-#
-# /* glMapBuffer (GL 1.5) */
-# /* glMapbufferARB (GL_ARB_vertex_buffer_object) */
-# static piglit_dispatch_function_ptr resolve_glMapBuffer()
-# {
-# if (check_version(15))
-# piglit_dispatch_glMapBuffer = (PFNGLMAPBUFFERPROC) get_core_proc("glMapBuffer", 15);
-# else if (check_extension("GL_ARB_vertex_buffer_object"))
-# piglit_dispatch_glMapBuffer = (PFNGLMAPBUFFERARBPROC) get_ext_proc("glMapBufferARB");
-# else
-# unsupported("MapBuffer");
-# return (piglit_dispatch_function_ptr) piglit_dispatch_glMapBuffer;
-# }
-#
-# - A stub function corresponding to each set of synonymous functions
-# in the GL API. The stub function first calls
-# check_initialized(). Then it calls the resolve function to
-# ensure that the dispatch function pointer is set. Finally, it
-# dispatches to the GL function through the dispatch function
-# pointer. E.g.:
-#
-# static GLvoid * APIENTRY stub_glMapBuffer(GLenum target, GLenum access)
-# {
-# check_initialized();
-# resolve_glMapBuffer();
-# return piglit_dispatch_glMapBuffer(target, access);
-# }
-#
-# - A declaration for each dispatch function pointer, e.g.:
-#
-# PFNGLMAPBUFFERPROC piglit_dispatch_glMapBuffer = stub_glMapBuffer;
-#
-# - An function, reset_dispatch_pointers(), which resets each dispatch
-# pointer to the corresponding stub function.
-#
-# - A table function_names, containing the name of each function in
-# alphabetical order (including the "gl" prefix).
-#
-# - A table function_resolvers, containing a pointer to the resolve
-# function corresponding to each entry in function_names.
-import collections
+"""
+Generate C source code from Khronos XML.
+"""
+
+import argparse
+import mako
import os.path
import sys
-try:
- import simplejson as json
-except:
- import json
-
-
-# Generate a top-of-file comment cautioning that the file is
-# auto-generated and should not be manually edited.
-def generated_boilerplate():
- return """\
-/**
- * This file was automatically generated by the script {0!r}.
- *
- * DO NOT EDIT!
- *
- * To regenerate, run "make piglit_dispatch_gen" from the toplevel directory.
- *
- * 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.
- */
-""".format(os.path.basename(sys.argv[0]))
-
-
-# Certain param names used in OpenGL are reserved by some compilers.
-# Rename them.
-PARAM_NAME_FIXUPS = {'near': 'hither', 'far': 'yon'}
-
-
-def fixup_param_name(name):
- if name in PARAM_NAME_FIXUPS:
- return PARAM_NAME_FIXUPS[name]
- else:
- return name
-
-
-# Internal representation of a category.
-#
-# - For a category representing a GL version, Category.kind is 'GL'
-# and Category.gl_10x_version is 10 times the GL version (e.g. 21
-# for OpenGL version 2.1).
-#
-# - For a category representing an extension, Category.kind is
-# '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']
-
- # Generate a human-readable representation of the category (for
- # use in generated comments).
- def __str__(self):
- if self.kind == 'GL':
- return 'GL {0}.{1}'.format(
- self.gl_10x_version // 10, self.gl_10x_version % 10)
- if self.kind == 'GLES':
- return 'GLES {0}.{1}'.format(
- self.gl_10x_version // 10, self.gl_10x_version % 10)
- elif self.kind == 'extension':
- return self.extension_name
- else:
- raise Exception(
- 'Unexpected category kind {0!r}'.format(self.kind))
-
-
-# Internal representation of a GL function.
-#
-# - Function.name is the name of the function, without the 'gl'
-# prefix.
-#
-# - Function.param_names is a list containing the name of each
-# function parameter.
-#
-# - Function.param_types is a list containing the type of each
-# function parameter.
-#
-# - 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.
-class Function(object):
- def __init__(self, name, json_data):
- self.name = name
- 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']
-
- # Name of the function, with the 'gl' prefix.
- @property
- def gl_name(self):
- return 'gl' + self.name
- # Name of the function signature typedef corresponding to this
- # function. E.g. for the glGetString function, this is
- # 'PFNGLGETSTRINGPROC'.
- @property
- def typedef_name(self):
- return 'pfn{0}proc'.format(self.gl_name).upper()
-
- # Generate a string representing the function signature in C.
- #
- # - name is inserted before the opening paren--use '' to generate
- # an anonymous function type signature.
- #
- # - If anonymous_args is True, then the signature contains only
- # the types of the arguments, not the names.
- def c_form(self, name, anonymous_args):
- if self.param_types:
- if anonymous_args:
- param_decls = ', '.join(self.param_types)
+from cStringIO import StringIO
+from collections import namedtuple
+from textwrap import dedent
+from mako.template import Template
+
+PIGLIT_TOP_DIR = os.path.join(os.path.dirname(__file__), '..', '..')
+sys.path.append(PIGLIT_TOP_DIR)
+import registry.gl
+from registry.gl import OrderedKeyedSet
+
+PROG_NAME = os.path.basename(sys.argv[0])
+debug = False
+
+def main():
+ global debug
+
+ argparser = argparse.ArgumentParser(prog=PROG_NAME)
+ argparser.add_argument('-o', '--out-dir')
+ argparser.add_argument('-d', '--debug', action='store_true', default=False)
+ args = argparser.parse_args()
+
+ debug = args.debug
+ if args.out_dir is None:
+ argparser.error('missing OUT_DIR')
+
+ registry.gl.debug = debug
+ gl_registry = registry.gl.parse()
+
+ dispatch_h = open(os.path.join(args.out_dir, 'generated_dispatch.h'), 'w')
+ dispatch_c = open(os.path.join(args.out_dir, 'generated_dispatch.c'), 'w')
+ DispatchCode(gl_registry).emit(dispatch_h, dispatch_c)
+ dispatch_h.close()
+ dispatch_c.close()
+
+def log_debug(msg):
+ if debug:
+ sys.stderr.write('debug: {0}: {1}\n'.format(PROG_NAME, msg))
+
+copyright_block = dedent('''\
+ /**
+ * DO NOT EDIT!
+ * This file was generated by the script {PROG_NAME!r}.
+ *
+ * Copyright 2014 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.
+ */'''
+).format(PROG_NAME=PROG_NAME)
+
+Api = namedtuple('Api', ('name', 'base_version_int', 'c_piglit_token'))
+"""Api corresponds to the XML <feature> tag's 'api' attribute."""
+
+apis = OrderedKeyedSet(
+ key='name', elems=(
+ Api(name='gl', c_piglit_token='PIGLIT_DISPATCH_GL', base_version_int=10),
+ Api(name='glcore', c_piglit_token='PIGLIT_DISPATCH_GL', base_version_int=10),
+ Api(name='gles1', c_piglit_token='PIGLIT_DISPATCH_ES1', base_version_int=11),
+ Api(name='gles2', c_piglit_token='PIGLIT_DISPATCH_ES1', base_version_int=20),
+ )
+)
+
+class CommandAliasMap(OrderedKeyedSet):
+
+ unaliasable_commands = {
+ 'glGetObjectParameterivAPPLE',
+ 'glGetObjectParameterivARB',
+ }
+
+ def __init__(self, commands=()):
+ OrderedKeyedSet.__init__(self, key='name', elems=commands)
+
+ def add(self, alias_set):
+ assert(isinstance(alias_set, CommandAliasSet))
+ OrderedKeyedSet.add(self, alias_set)
+
+ def add_commands(self, commands):
+ for command in commands:
+ assert(isinstance(command, registry.gl.Command))
+
+ if command.name in CommandAliasMap.unaliasable_commands:
+ alias_name = command.name
else:
- param_decls = ', '.join(
- '{0} {1}'.format(*p)
- for p in zip(self.param_types, self.param_names))
- else:
- param_decls = 'void'
- return '{rettype} {name}({param_decls})'.format(
- rettype=self.return_type, name=name,
- param_decls=param_decls)
+ alias_name = command.basename
+ alias_set = self.get(alias_name, None)
-# Internal representation of an enum.
-#
-# - Enum.value_int is the value of the enum, as a Python integer.
-#
-# - 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']
+ if alias_set is None:
+ self.add(CommandAliasSet(alias_name, [command]))
+ else:
+ alias_set.add(command)
+class CommandAliasSet(OrderedKeyedSet):
-# Data structure keeping track of a set of synonymous functions. Such
-# a set is called a "dispatch set" because it corresponds to a single
-# dispatch pointer.
-#
-# - DispatchSet.cat_fn_pairs is a list of pairs (category, function)
-# for each category this function is defined in. The list is sorted
-# by category, with categories of kind 'GL' and then 'GLES' appearing first.
-class DispatchSet(object):
- # Initialize a dispatch set given a list of synonymous function
- # names.
- #
- # - all_functions is a dict mapping all possible function names to
- # the Function object describing them.
- #
- # - all_categories is a dict mapping all possible category names
- # to the Category object describing them.
- def __init__(self, synonym_set, all_functions, all_categories):
- self.cat_fn_pairs = []
- for function_name in synonym_set:
- function = all_functions[function_name]
- for category_name in function.categories:
- category = all_categories[category_name]
- self.cat_fn_pairs.append((category, function))
- # Sort by category, with GL categories preceding extensions.
- self.cat_fn_pairs.sort(key=self.__sort_key)
-
- # The first Function object in DispatchSet.functions. This
- # "primary" function is used to name the dispatch pointer, the
- # stub function, and the resolve function.
- @property
- def primary_function(self):
- return self.cat_fn_pairs[0][1]
+ Resolution = namedtuple('Resolution', ('c_condition', 'c_get_proc'))
- # The name of the dispatch pointer that should be generated for
- # this dispatch set.
- @property
- def dispatch_name(self):
- return 'piglit_dispatch_' + self.primary_function.gl_name
+ def __init__(self, name, commands):
+ OrderedKeyedSet.__init__(self, key='name')
+ self.name = name
+ for command in commands:
+ self.add(command)
- # The name of the stub function that should be generated for this
- # dispatch set.
- @property
- def stub_name(self):
- return 'stub_' + self.primary_function.gl_name
+ def __cmp__(x, y):
+ return cmp(x.name, y.name)
- # The name of the resolve function that should be generated for
- # this dispatch set.
- @property
- def resolve_name(self):
- return 'resolve_' + self.primary_function.gl_name
-
- @staticmethod
- def __sort_key(cat_fn_pair):
- if cat_fn_pair[0].kind == 'GL':
- return 0, cat_fn_pair[0].gl_10x_version
- elif cat_fn_pair[0].kind == 'GLES':
- return 1, cat_fn_pair[0].gl_10x_version
- elif cat_fn_pair[0].kind == 'extension':
- return 2, cat_fn_pair[0].extension_name
- else:
- raise Exception(
- 'Unexpected category kind {0!r}'.format(cat_fn_pair[0].kind))
-
-
-# Data structure keeping track of all of the known functions and
-# enums, and synonym relationships that exist between the functions.
-#
-# - Api.enums is a dict mapping enum name to an Enum object.
-#
-# - Api.functions is a dict mapping function name to a Function object.
-#
-# - Api.function_alias_sets is a list of lists, where each constituent
-# list is a list of function names that are aliases of each other.
-#
-# - Api.categories is a dict mapping category name to a Category
-# object.
-class Api(object):
- def __init__(self, json_data):
- self.enums = dict(
- (key, Enum(value))
- for key, value in json_data['enums'].items())
- 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())
-
- # Generate a list of (name, value) pairs representing all enums in
- # the API. The resulting list is sorted by enum value.
- def compute_unique_enums(self):
- enums_by_value = [(enum.value_int, (name, enum.value_str))
- for name, enum in self.enums.items()]
- enums_by_value.sort()
- return [item[1] for item in enums_by_value]
-
- # A list of all of the extension names declared in the API, as
- # Python strings, sorted alphabetically.
@property
- def extensions(self):
- return sorted(
- [category_name
- for category_name, category in self.categories.items()
- if category.kind == 'extension'])
-
- # A list of all of the GL versions declared in the API, as
- # integers (e.g. 13 represents GL version 1.3).
+ def primary_command(self):
+ return sorted(self)[0]
+
@property
- def gl_versions(self):
- return sorted(
- [category.gl_10x_version
- for category in self.categories.values()
- if category.kind == 'GL'])
-
- # Generate a list of DispatchSet objects representing all sets of
- # synonymous functions in the API. The resulting list is sorted
- # by DispatchSet.stub_name.
- def compute_dispatch_sets(self):
- sets = [DispatchSet(synonym_set, self.functions, self.categories)
- for synonym_set in self.function_alias_sets]
- sets.sort(key=lambda ds: ds.stub_name)
-
- return sets
-
- # Generate a list of Function objects representing all functions
- # in the API. The resulting list is sorted by function name.
- def compute_unique_functions(self):
- return [self.functions[key] for key in sorted(self.functions.keys())]
-
-
-# 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))
-
-
-# Generate the resolve function for a given DispatchSet.
-def generate_resolve_function(ds):
- f0 = ds.primary_function
-
- # First figure out all the conditions we want to check in order to
- # figure out which function to dispatch to, and the code we will
- # execute in each case.
- condition_code_pairs = []
- for category, f in ds.cat_fn_pairs:
- if category.kind in ('GL', 'GLES'):
- getter = 'get_core_proc("{0}", {1})'.format(
- f.gl_name, category.gl_10x_version)
-
- condition = ''
- api_base_version = 0
- if category.kind == 'GL':
- condition = 'dispatch_api == PIGLIT_DISPATCH_GL'
- api_base_version = 10
- elif category.gl_10x_version >= 20:
- condition = 'dispatch_api == PIGLIT_DISPATCH_ES2'
- api_base_version = 20
- else:
- condition = 'dispatch_api == PIGLIT_DISPATCH_ES1'
- api_base_version = 11
-
- # Only check the version for functions that aren't part of the
- # core for the PIGLIT_DISPATCH api.
- if category.gl_10x_version != api_base_version:
- condition = condition + ' && check_version({0})'.format(
- category.gl_10x_version)
- elif category.kind == 'extension':
- getter = 'get_ext_proc("{0}")'.format(f.gl_name)
- condition = 'check_extension("{0}")'.format(category.extension_name)
- else:
- raise Exception(
- 'Unexpected category type {0!r}'.format(category.kind))
-
- if f.name == 'TexImage3DEXT':
- # Special case: glTexImage3DEXT has a slightly different
- # type than glTexImage3D (argument 3 is a GLenum rather
- # than a GLint). This is not a problem, since GLenum and
- # GLint are treated identically by function calling
- # conventions. So when calling get_proc_address() on
- # glTexImage3DEXT, cast the result to PFNGLTEXIMAGE3DPROC
- # to avoid a warning.
- typedef_name = 'PFNGLTEXIMAGE3DPROC'
- else:
- typedef_name = f.typedef_name
-
- code = '{0} = ({1}) {2};'.format(
- ds.dispatch_name, typedef_name, getter)
-
- 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.
- if f.gl_name in ('glDrawArraysInstancedARB',
- 'glDrawElementsInstancedARB'):
- condition = 'check_extension("GL_ARB_instanced_arrays")'
- condition_code_pairs.append((condition, code))
-
- # Finally, if none of the previous conditions were satisfied, then
- # the given dispatch set is not supported by the implementation,
- # so we want to call the unsupported() function.
- condition_code_pairs.append(
- ('true', 'unsupported("{0}");'.format(f0.name)))
-
- # Start the resolve function
- resolve_fn = 'static piglit_dispatch_function_ptr {0}()\n'.format(
- ds.resolve_name)
- resolve_fn += '{\n'
-
- # Output code that checks each condition in turn and executes the
- # appropriate case. To make the generated code more palatable
- # (and to avoid compiler warnings), we convert "if (true) FOO;" to
- # "FOO;" and "else if (true) FOO;" to "else FOO;".
- if condition_code_pairs[0][0] == 'true':
- resolve_fn += '\t{0}\n'.format(condition_code_pairs[0][1])
- else:
- resolve_fn += '\tif ({0})\n\t\t{1}\n'.format(*condition_code_pairs[0])
- for i in xrange(1, len(condition_code_pairs)):
- if condition_code_pairs[i][0] == 'true':
- resolve_fn += '\telse\n\t\t{0}\n'.format(
- condition_code_pairs[i][1])
- break
+ def resolutions(self):
+ """Iterable of Resolution actions to obtain the GL proc address."""
+ Resolution = self.__class__.Resolution
+ for command in self:
+ for feature in command.features:
+ api = apis[feature.api]
+ format_map = dict(command=command, feature=feature, api=api)
+ condition = 'dispatch_api == {api.c_piglit_token}'.format(**format_map)
+ get_proc = 'get_core_proc("{command.name}", {feature.version_int})'.format(**format_map)
+ if feature.version_int > api.base_version_int:
+ condition += ' && check_version({feature.version_int})'.format(**format_map)
+ yield Resolution(condition, get_proc)
+
+ for extension in command.extensions:
+ format_map = dict(command=command, extension=extension)
+ condition = 'check_extension("{extension.name}")'.format(**format_map)
+ get_proc = 'get_ext_proc("{command.name}")'.format(**format_map)
+ yield Resolution(condition, get_proc)
+
+ def add(self, command):
+ assert(isinstance(command, registry.gl.Command))
+ OrderedKeyedSet.add(self, command)
+
+class DispatchCode(object):
+
+ h_template = Template(dedent('''\
+ ${copyright}
+
+ #ifndef __PIGLIT_DISPATCH_GEN_H__
+ #define __PIGLIT_DISPATCH_GEN_H__
+
+ % for f in sorted(gl_registry.commands):
+ typedef ${f.c_return_type} (APIENTRY *PFN${f.name.upper()}PROC)(${f.c_named_param_list});
+ % endfor
+ % for alias_set in alias_map:
+ <% f0 = alias_set.primary_command %>
+ % for f in alias_set:
+ <% cat_list = ' '.join(['(' + cat.name + ')' for cat in f.requirements]) %>\\
+ /* ${f.name} ${cat_list} */
+ % endfor
+ extern PFN${f0.name.upper()}PROC piglit_dispatch_${f0.name};
+ % for f in alias_set:
+ #define ${f.name} piglit_dispatch_${f0.name}
+ % endfor
+ % endfor
+ % for enum_group in gl_registry.enum_groups:
+ % if len(enum_group.enums) > 0:
+
+ /* Enum Group ${enum_group.name} */
+ % for enum in enum_group.enums:
+ % if not enum.is_collider:
+ #define ${enum.name} ${enum.c_num_literal}
+ % endif
+ % endfor
+ % endif
+ % endfor
+
+ % for extension in gl_registry.extensions:
+ #define ${extension.name} 1
+ % endfor
+
+ % for feature in gl_registry.features:
+ #define ${feature.name} 1
+ % endfor
+
+ #endif /*__PIGLIT_DISPATCH_GEN_H__*/'''
+ ))
+
+ c_template = Template(dedent('''\
+ ${copyright}
+ % for alias_set in alias_map:
+ <% f0 = alias_set.primary_command %>
+ % for f in alias_set:
+ <% cat_list = ' '.join(['(' + cat.name + ')' for cat in f.requirements]) %>\\
+ /* ${f.name} ${cat_list} */
+ % endfor
+ static void* resolve_${f0.name}(void)
+ {
+ % for resolution in alias_set.resolutions:
+ % if loop.first:
+ if (${resolution.c_condition}) {
+ % else:
+ } else if (${resolution.c_condition}) {
+ % endif
+ return ${resolution.c_get_proc};
+ % endfor
+ } else {
+ unsupported("${f0.name}");
+ return piglit_dispatch_${f0.name};
+ }
+ }
+
+ static ${f0.c_return_type} APIENTRY stub_${f0.name}(${f0.c_named_param_list})
+ {
+ <%
+ if f0.c_return_type == 'void':
+ maybe_return = ''
else:
- resolve_fn += '\telse if ({0})\n\t\t{1}\n'.format(
- *condition_code_pairs[i])
-
- # Output code to return the dispatch function.
- resolve_fn += '\treturn (piglit_dispatch_function_ptr) {0};\n'.format(
- ds.dispatch_name)
- resolve_fn += '}\n'
- return resolve_fn
-
-
-# Generate the stub function for a given DispatchSet.
-def generate_stub_function(ds):
- f0 = ds.primary_function
-
- # Start the stub function
- stub_fn = 'static {0}\n'.format(
- f0.c_form('APIENTRY ' + ds.stub_name, anonymous_args=False))
- stub_fn += '{\n'
- stub_fn += '\tcheck_initialized();\n'
- stub_fn += '\t{0}();\n'.format(ds.resolve_name)
-
- # Output the call to the dispatch function.
- stub_fn += '\t{0}{1}({2});\n'.format(
- 'return ' if f0.return_type != 'void' else '',
- ds.dispatch_name, ', '.join(f0.param_names))
- stub_fn += '}\n'
- return stub_fn
-
-
-# Generate the reset_dispatch_pointers() function, which sets each
-# dispatch pointer to point to the corresponding stub function.
-def generate_dispatch_pointer_resetter(dispatch_sets):
- result = []
- result.append('static void\n')
- result.append('reset_dispatch_pointers()\n')
- result.append('{\n')
- for ds in dispatch_sets:
- result.append(
- '\t{0} = {1};\n'.format(ds.dispatch_name, ds.stub_name))
- result.append('}\n')
- return ''.join(result)
-
-
-# Generate the function_names and function_resolvers tables.
-def generate_function_names_and_resolvers(dispatch_sets):
- name_resolver_pairs = []
- for ds in dispatch_sets:
- for _, f in ds.cat_fn_pairs:
- name_resolver_pairs.append((f.gl_name, ds.resolve_name))
- name_resolver_pairs.sort()
- result = []
- result.append('static const char * const function_names[] = {\n')
- for name, _ in name_resolver_pairs:
- result.append('\t"{0}",\n'.format(name))
- result.append('};\n')
- result.append('\n')
- result.append('static const piglit_dispatch_resolver_ptr '
- 'function_resolvers[] = {\n')
- for _, resolver in name_resolver_pairs:
- result.append('\t{0},\n'.format(resolver))
- result.append('};\n')
- return ''.join(result)
-
-
-# Generate the C source and header files for the API.
-def generate_code(api):
- c_contents = [generated_boilerplate()]
- h_contents = [generated_boilerplate()]
-
- unique_functions = api.compute_unique_functions()
-
- # Emit typedefs for each name
- for f in unique_functions:
- h_contents.append(
- 'typedef {0};\n'.format(
- f.c_form('(APIENTRY *{0})'.format(f.typedef_name),
- anonymous_args=True)))
-
- dispatch_sets = api.compute_dispatch_sets()
-
- for ds in dispatch_sets:
- f0 = ds.primary_function
-
- # Emit comment block
- comments = '\n'
- for cat, f in ds.cat_fn_pairs:
- comments += '/* {0} ({1}) */\n'.format(f.gl_name, cat)
- c_contents.append(comments)
- h_contents.append(comments)
-
- # Emit extern declaration of dispatch pointer
- h_contents.append(
- 'extern {0} {1};\n'.format(f0.typedef_name, ds.dispatch_name))
-
- # Emit defines aliasing each GL function to the dispatch
- # pointer
- for _, f in ds.cat_fn_pairs:
- h_contents.append(
- '#define {0} {1}\n'.format(f.gl_name, ds.dispatch_name))
-
- # Emit resolve function
- c_contents.append(generate_resolve_function(ds))
-
- # Emit stub function
- c_contents.append(generate_stub_function(ds))
-
- # Emit initializer for dispatch pointer
- c_contents.append(
- '{0} {1} = {2};\n'.format(
- f0.typedef_name, ds.dispatch_name, ds.stub_name))
-
- # Emit dispatch pointer initialization function
- c_contents.append(generate_dispatch_pointer_resetter(dispatch_sets))
-
- c_contents.append('\n')
-
- # Emit function_names and function_resolvers tables.
- c_contents.append(generate_function_names_and_resolvers(dispatch_sets))
-
- # Emit enum #defines
- for name, value in api.compute_unique_enums():
- h_contents.append('#define GL_{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(
- ver // 10, ver % 10))
-
- return ''.join(c_contents), ''.join(h_contents)
-
+ maybe_return = 'return '
+ %>\\
+ check_initialized();
+ piglit_dispatch_${f0.name} = resolve_${f0.name}();
+ ${maybe_return}piglit_dispatch_${f0.name}(${f0.c_untyped_param_list});
+ }
+
+ PFN${f0.name.upper()}PROC piglit_dispatch_${f0.name} = stub_${f0.name};
+ % endfor
+
+ static void reset_dispatch_pointers(void)
+ {
+ % for alias_set in alias_map:
+ <% f0 = alias_set.primary_command %>\\
+ piglit_dispatch_${f0.name} = stub_${f0.name};
+ % endfor
+ }
+
+ static const char * function_names[] = {
+ % for f in gl_registry.commands:
+ "${f.name}",
+ % endfor
+ };
+
+ static void* (*const function_resolvers[])(void) = {
+ % for alias_set in alias_map:
+ <% f0 = alias_set.primary_command %>\\
+ resolve_${f0.name},
+ % endfor
+ };
+ '''
+ ))
+
+ def __init__(self, gl_registry):
+ assert(isinstance(gl_registry, registry.gl.Registry))
+ self.gl_registry = gl_registry
+ self.alias_map = CommandAliasMap()
+ self.alias_map.add_commands(gl_registry.commands)
+
+ def emit(self, h_buf, c_buf):
+ for (buf, template) in (
+ (h_buf, DispatchCode.h_template),
+ (c_buf, DispatchCode.c_template)):
+ ctx = mako.runtime.Context(
+ buffer=buf,
+ gl_registry=self.gl_registry,
+ alias_map=self.alias_map,
+ copyright=copyright_block,
+ )
+ template.render_context(ctx)
if __name__ == '__main__':
- file_to_parse = sys.argv[1]
- api = read_api(file_to_parse)
-
- c_contents, h_contents = generate_code(api)
- with open(sys.argv[2], 'w') as f:
- f.write(c_contents)
- with open(sys.argv[3], 'w') as f:
- f.write(h_contents)
+ main()
diff --git a/tests/util/piglit-dispatch.h b/tests/util/piglit-dispatch.h
index c24c186..2d1d5eb 100644
--- a/tests/util/piglit-dispatch.h
+++ b/tests/util/piglit-dispatch.h
@@ -96,6 +96,7 @@ typedef float GLfloat;
typedef float GLclampf;
typedef double GLdouble;
typedef double GLclampd;
+typedef int32_t GLclampx;
typedef void GLvoid;
typedef int64_t GLint64EXT;
typedef uint64_t GLuint64EXT;
@@ -136,8 +137,6 @@ typedef piglit_dispatch_function_ptr (*piglit_get_core_proc_address_function_ptr
typedef piglit_dispatch_function_ptr (*piglit_get_ext_proc_address_function_ptr)(const char *);
-typedef piglit_dispatch_function_ptr (*piglit_dispatch_resolver_ptr)();
-
typedef void (*piglit_error_function_ptr)(const char *);
typedef enum {
--
2.0.0.rc1
More information about the Piglit
mailing list