[Piglit] [PATCH 2/6] dispatch: Generate piglit-dispatch from Khronos XML
Dylan Baker
baker.dylan.c at gmail.com
Tue Jun 17 07:53:07 PDT 2014
I know we had a chance to talk yesterday, but it's still probably a good
idea to have my comments in writing on the list.
On Monday, June 16, 2014 09:04:58 AM Chad Versace wrote:
[snip]
> 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
use cElementTree instead of ElementTree, it's faster but doesn't allow
subclassing. You could also using a try/except to support lxml, which
are bindings for libxml2 and are very fast.
> +from collections import namedtuple, OrderedDict
One of the biggest problems with this series is that OrderedDict is a
2.7 feature, and it's been made clear that 2.6 is a hard requirement for
part of our community. I don't personally care that much, but I suspect
you'll get push back from others.
> +from textwrap import dedent
You're not using dedent or namedtuple
> +
> +# Export 'debug' so other Piglit modules can easily enable it.
> +debug = False
Constants should be all caps
> +
> +def _log_debug(msg):
> + if debug:
> + sys.stderr.write('debug: {0}: {1}\n'.format(__name__, msg))
Could you use the print function from __future__? If you do then you can
just use:
print('my message', file=sys.stderr)
It'll give you the newline for free, and it also prevents flushing
problems.
All top level functions and classes get two spaces between them
> +
> +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))
> + """
Have you seen python's doctests? this would be trivial to convert to a
doctest.
> +
> + 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)
this seems wrong to me, shouldn't it be:
def __or__(self, y):
return self.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()
you could also import the copy module, it's nice
> + 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
> + )
> + )
> +
I'm still not crazy about these 3+ line comprehensions.
> + 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>
This could be part of the docstring
> +
> + 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)
Shouldn't this be:
def __cmp__(self, other):
return cmp(self.name, other.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
> + ])
Drop the [] and make this a generator instead of a comprehension. It
will save memory
> +
> + @property
> + def c_unnamed_param_list(self):
> + """For example, "GLenum, GLfloat" for glAccum."""
> + return ', '.join([
> + param.c_type
> + for param in self.param_list
> + ])
and on the rest of them
> +
> + @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
unused
> +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)
you can drop the prog assignment, you've assigned it to the default
value
> + argparser.add_argument('-o', '--out-dir')
add required=True to this argument and drop the is None check
> + 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')
see previous comment
> +
> + 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',
> + }
using {} for sets is a 2.7 feature, beyond that I haven't seen it used
much, which I assume is because {} is ubiquitous for dict(). We should
probably just use explicit set(). This comment applies to the whole file
> +
> + 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
You can use arbitrary depth for the mako control flow, I think it's
easier to read if you dedent mako control flow by 2 spaces or so
> +
> + #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 {
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: This is a digitally signed message part.
URL: <http://lists.freedesktop.org/archives/piglit/attachments/20140617/f3f84e54/attachment-0001.sig>
More information about the Piglit
mailing list