2 # Copyright 2014 Intel Corporation
4 # Permission is hereby granted, free of charge, to any person obtaining a
5 # copy of this software and associated documentation files (the "Software"),
6 # to deal in the Software without restriction, including without limitation
7 # the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 # and/or sell copies of the Software, and to permit persons to whom the
9 # Software is furnished to do so, subject to the following conditions:
11 # The above copyright notice and this permission notice (including the next
12 # paragraph) shall be included in all copies or substantial portions of the
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 Parse gl.xml into Python objects.
31 from copy
import copy
, deepcopy
33 # Export 'debug' so other Piglit modules can easily enable it.
39 print('debug: {0}: {1}'.format(__name__
, msg
), file=sys
.stderr
)
42 # Prefer the external module 'lxml.etree' (it uses libxml2) over Python's
43 # builtin 'xml.etree.ElementTree'. It's faster.
45 import lxml
.etree
as etree
46 _log_debug('etree is lxml.etree')
48 import xml
.etree
.ElementTree
as etree
49 _log_debug('etree is xml.etree.ElementTree')
53 """Parse gl.xml and return a Registry object."""
54 filename
= os
.path
.join(os
.path
.dirname(__file__
), 'gl.xml')
55 xml_registry
= etree
.parse(filename
).getroot()
56 _repair_xml(xml_registry
)
57 return Registry(xml_registry
)
60 def _repair_xml(xml_registry
):
63 'gles2_GL_ACTIVE_PROGRAM_EXT',
68 def defer_removal(parent
, child
):
69 remove_queue
.append((parent
, child
))
71 for enums
in xml_registry
.iterfind('./enums'):
72 if ('GL_ALL_ATTRIB_BITS' in fixes
and
73 enums
.get('group') == 'AttribMask'):
74 # The XML defines GL_ALL_ATTRIB_BITS incorrectly with all bits
75 # set (0xFFFFFFFF). From the GL_ARB_multisample spec, v5:
77 # In order to avoid incompatibility with GL implementations
78 # that do not support SGIS_multisample, ALL_ATTRIB_BITS
79 # does not include MULTISAMPLE_BIT_ARB.
81 enum
= enums
.find("./enum[@name='GL_ALL_ATTRIB_BITS']")
82 enum
.set('value', '0x000FFFFF')
84 fixes
.remove('GL_ALL_ATTRIB_BITS')
87 if ('gles2_GL_ACTIVE_PROGRAM_EXT' in fixes
and
88 enums
.get('vendor') is not None and
89 enums
.get('vendor') == 'ARB' and
90 enums
.get('start') is not None and
91 enums
.get('start') <= '0x8259' and
92 enums
.get('end') is not None and
93 enums
.get('end') >= '0x8259'):
94 # GL_ACTIVE_PROGRAM_EXT has different numerical values in GL
95 # (0x8B8D) and in GLES (0x8259). Remove the GLES value to avoid
96 # redefinition collisions.
97 bad_enum
= enums
.find(("./enum"
99 "[@name='GL_ACTIVE_PROGRAM_EXT']"
101 defer_removal(enums
, bad_enum
)
103 fixes
.remove('gles2_GL_ACTIVE_PROGRAM_EXT')
106 for (parent
, child
) in remove_queue
:
110 raise Exception('failed to apply some xml repairs: ' +
111 ', '.join(map(repr, fixes
)))
114 class OrderedKeyedSet(object):
115 """A set with keyed elements that preserves order of element insertion.
117 Why waste words? Let's document the class with example code.
120 Cheese = namedtuple('Cheese', ('name', 'flavor'))
121 cheeses = OrderedKeyedSet(key='name')
122 cheeses.add(Cheese(name='cheddar', flavor='good'))
123 cheeses.add(Cheese(name='gouda', flavor='smells like feet'))
124 cheeses.add(Cheese(name='romano', flavor='awesome'))
126 # Elements are retrievable by key.
127 assert cheeses['gouda'].flavor == 'smells like feet'
129 # On key collision, the old element is removed.
130 cheeses.add(Cheese(name='gouda', flavor='ok i guess'))
131 assert cheeses['gouda'].flavor == 'ok i guess'
133 # The set preserves order of insertion. Replacement does not alter
135 assert list(cheeses)[2].name == 'romano'
137 # The set is iterable.
138 for cheese in cheeses:
141 # Yet another example...
142 Bread = namedtuple('Bread', ('name', 'smell'))
143 breads = OrderedKeyedSet(key='name')
144 breads.add(Bread(name='como', smell='subtle')
145 breads.add(Bread(name='sourdough', smell='pleasant'))
147 # The set supports some common set operations, such as union.
148 breads_and_cheeses = breads | cheeses
149 assert len(breads_and_cheeses) == len(breads) + len(cheeses)
152 def __init__(self
, key
, elems
=()):
153 """Create a new set with the given key.
155 The given 'key' defines how to calculate each element's key value. If
156 'key' is a string, then each key value is defined to be
157 `getattr(elem, key)`. If 'key' is a function, then the key value is
161 # A linked list contains the set's items. Each list node is a 4-tuple
162 # [prev, next, key, value]. The root node is permanent.
164 root
[:] = [root
, root
, None, None]
165 self
.__list
_root
= root
167 # For quick retrieval, we map each key to its node. That is, each map
168 # pair has form {key: [prev, next, key, value])}.
171 if isinstance(key
, str):
172 self
.__key
_func
= lambda elem
: getattr(elem
, key
)
174 self
.__key
_func
= key
179 def __or__(self
, other
):
180 """Same as `union`."""
181 return self
.union(other
)
183 def __contains__(self
, key
):
184 return key
in self
.__map
187 return OrderedKeyedSet(key
=deepcopy(self
.__key
_func
),
190 def __getitem__(self
, key
):
191 return self
.__map
[key
][3]
194 return self
.itervalues()
197 return len(self
.__map
)
200 templ
= '{self.__class__.__name__}({self.name!r})'
201 return templ
.format(self
=self
)
203 def add(self
, value
):
204 key
= self
.__key
_func
(value
)
205 node
= self
.__map
.get(key
, None)
209 root
= self
.__list
_root
211 new_tail
= [old_tail
, root
, key
, value
]
212 new_tail
[0][1] = new_tail
213 new_tail
[1][0] = new_tail
214 self
.__map
[key
] = new_tail
218 root
= self
.__list
_root
219 root
[:] = [root
, root
, None, None]
221 def extend(self
, elems
):
225 def get(self
, key
, default
):
226 node
= self
.__map
.get(key
, None)
233 root
= self
.__list
_root
235 while node
is not root
:
236 yield (node
[2], node
[3])
240 return (i
[0] for i
in self
.iteritems())
242 def itervalues(self
):
243 return (i
[1] for i
in self
.iteritems())
246 node
= self
.__map
.pop(key
)
251 def sort_by_key(self
):
252 sorted_items
= sorted(self
.__map
.items())
254 for item
in sorted_items
:
257 def sort_by_value(self
):
258 sorted_values
= sorted(self
.__map
.values())
260 for value
in sorted_values
:
263 def union(self
, other
):
264 """Return the union of two sets as a new set.
266 In the new set, all elements of the self set precede those of the other
267 set. The order of elements in the new set preserves the order of the
270 The new set's key function is copied from self. On key collisions, set
271 y has precedence over x.
278 class ImmutableOrderedKeyedSet(OrderedKeyedSet
):
280 def __init__(self
, key
, elems
):
281 self
.__is
_frozen
= False
282 OrderedKeyedSet
.__init
__(self
, key
=key
, elems
=elems
)
283 self
.__is
_frozen
= True
285 def add(self
, value
):
289 OrderedKeyedSet
.add(self
, value
)
298 class ImmutableError(Exception):
302 # Values that may appear in the XML attributes 'api' and 'supported'.
303 VALID_APIS
= frozenset(('gl', 'glcore', 'gles1', 'gles2', 'glsc2', 'disabled'))
306 class Registry(object):
307 """The toplevel <registry> element.
310 features: An OrderedKeyedSet that contains a Feature for each <feature>
313 extensions: An OrderedKeyedSet that contains an Extension for each
314 <extension> subelement.
316 commands: An OrderedKeyedSet that contains a Command for each <command>
319 command_alias_map: A CommandAliasMap that contains a CommandAliasSet
320 for each equivalence class of commands.
322 enum_groups: An OrderedKeyedSet that contains an EnumGroup for each
325 enums: An OrderedKeyedSet that contains an Enum for each <enum>
328 vendor_namespaces: A collection of all vendor prefixes and suffixes,
329 such as "ARB", "EXT", "CHROMIUM", and "NV".
332 def __init__(self
, xml_registry
):
333 """Parse the <registry> element."""
335 assert xml_registry
.tag
== 'registry'
337 self
.command_alias_map
= CommandAliasMap()
338 self
.commands
= OrderedKeyedSet(key
='name')
339 self
.enum_groups
= []
340 self
.enums
= OrderedKeyedSet(key
='name')
341 self
.extensions
= OrderedKeyedSet(key
='name')
342 self
.features
= OrderedKeyedSet(key
='name')
343 self
.vendor_namespaces
= set()
345 for xml_command
in xml_registry
.iterfind('./commands/command'):
346 command
= Command(xml_command
)
347 self
.commands
.add(command
)
348 self
.command_alias_map
.add(command
)
350 for xml_enums
in xml_registry
.iterfind('./enums'):
351 enum_group
= EnumGroup(xml_enums
)
352 self
.enum_groups
.append(enum_group
)
353 for enum
in enum_group
.enums
:
356 for xml_feature
in xml_registry
.iterfind('./feature'):
357 feature
= Feature(xml_feature
, command_map
=self
.commands
,
359 self
.features
.add(feature
)
361 for xml_ext
in xml_registry
.iterfind('./extensions/extension'):
362 ext
= Extension(xml_ext
, command_map
=self
.commands
,
364 self
.extensions
.add(ext
)
365 self
.vendor_namespaces
.add(ext
.vendor_namespace
)
367 self
.vendor_namespaces
.remove(None)
370 @functools.total_ordering
371 class Feature(object):
372 """A <feature> XML element.
375 name: The XML element's 'name' attribute.
377 api: The XML element's 'api' attribute.
379 version_str: The XML element's 'number' attribute. For example, "3.1".
381 version_float: float(version_str)
383 version_int: int(10 * version_float)
385 requirements: A collection of Requirement for each Command and Enum
386 this Feature requires.
389 def __init__(self
, xml_feature
, command_map
, enum_map
):
390 """Parse a <feature> element."""
392 # Example <feature> element:
394 # <feature api="gles2" name="GL_ES_VERSION_3_1" number="3.1">
395 # <!-- arrays_of_arrays features -->
397 # <!-- compute_shader features -->
399 # <command name="glDispatchCompute"/>
400 # <command name="glDispatchComputeIndirect"/>
401 # <enum name="GL_COMPUTE_SHADER"/>
402 # <enum name="GL_MAX_COMPUTE_UNIFORM_BLOCKS"/>
405 # <!-- draw_indirect features -->
407 # <command name="glDrawArraysIndirect"/>
408 # <command name="glDrawElementsIndirect"/>
409 # <enum name="GL_DRAW_INDIRECT_BUFFER"/>
410 # <enum name="GL_DRAW_INDIRECT_BUFFER_BINDING"/>
415 assert xml_feature
.tag
== 'feature'
417 # Parse the <feature> tag's attributes.
418 self
.name
= xml_feature
.get('name')
419 self
.api
= xml_feature
.get('api')
420 self
.is_gles
= self
.name
.startswith('GL_ES')
422 self
.version_str
= xml_feature
.get('number')
423 self
.version_float
= float(self
.version_str
)
424 self
.version_int
= int(10 * self
.version_float
)
426 self
.__parse
_requirements
(xml_feature
, command_map
, enum_map
)
428 assert self
.api
in VALID_APIS
429 assert len(self
.requirements
) > 0
431 def __eq__(self
, other
):
434 elif isinstance(other
, Extension
):
436 elif self
.is_gles
!= other
.is_gles
:
438 return self
.name
== other
.name
440 def __lt__(self
, other
):
441 if isinstance(other
, Extension
):
443 # Desktop GL before GLES
444 elif self
.is_gles
!= other
.is_gles
:
449 return self
.name
< other
.name
452 templ
= '{self.__class__.__name__}({self.name!r})'
453 return templ
.format(self
=self
)
455 def __parse_requirements(self
, xml_feature
, command_map
, enum_map
):
456 """For each <command> and <enum> under a <require>, create
457 a Requirement that links this Feature to a Command or Enum.
459 self
.requirements
= set()
462 req
= Requirement(provider
=self
, provided
=x
,
463 apis
=frozenset((self
.api
,)))
464 self
.requirements
.add(req
)
465 x
.requirements
.add(req
)
467 for xml_cmd
in xml_feature
.iterfind('./require/command'):
468 cmd
= command_map
[xml_cmd
.get('name')]
470 for xml_enum
in xml_feature
.iterfind('./require/enum'):
471 enum
= enum_map
[xml_enum
.get('name')]
475 @functools.total_ordering
476 class Extension(object):
477 """An <extension> XML element.
480 name: The XML element's 'name' attribute.
482 supported_apis: The set of api strings in the XML element's 'supported'
483 attribute. For example, set('gl', 'glcore').
485 vendor_namespace: For example, "AMD". May be None.
487 requirements: A collection of Requirement for each Command and Enum
488 this Extension requires.
491 __VENDOR_REGEX
= re
.compile(r
'^GL_(?P<vendor_namespace>[A-Z]+)_')
492 RATIFIED_NAMESPACES
= ('KHR', 'ARB', 'OES')
494 def __init__(self
, xml_extension
, command_map
, enum_map
):
495 """Parse an <extension> element."""
497 # Example <extension> element:
498 # <extension name="GL_ARB_ES2_compatibility" supported="gl|glcore">
500 # <enum name="GL_FIXED"/>
501 # <enum name="GL_IMPLEMENTATION_COLOR_READ_TYPE"/>
503 # <command name="glReleaseShaderCompiler"/>
504 # <command name="glShaderBinary"/>
509 assert xml_extension
.tag
== 'extension'
511 self
.name
= xml_extension
.get('name')
513 self
.vendor_namespace
= None
514 match
= Extension
.__VENDOR
_REGEX
.match(self
.name
)
515 if match
is not None:
516 groups
= match
.groupdict()
517 self
.vendor_namespace
= groups
.get('vendor_namespace', None)
519 self
.supported_apis
= xml_extension
.get('supported').split('|')
520 self
.supported_apis
= frozenset(self
.supported_apis
)
521 assert self
.supported_apis
<= VALID_APIS
523 self
.__parse
_requirements
(xml_extension
, command_map
, enum_map
)
525 def __eq__(self
, other
):
528 elif isinstance(other
, Feature
):
530 elif self
.is_ratified
!= other
.is_ratified
:
532 elif self
.vendor_namespace
== 'EXT' != other
.vendor_namespace
== 'EXT':
534 return self
.name
== other
.name
536 def __lt__(self
, other
):
537 if isinstance(other
, Feature
):
539 elif self
.is_ratified
!= other
.is_ratified
:
540 # sort ratified before unratified
545 elif (other
.vendor_namespace
== 'EXT') != \
546 (self
.vendor_namespace
== 'EXT'):
547 # Sort EXT before others
548 if self
.vendor_namespace
== 'EXT':
552 return self
.name
< other
.name
555 templ
= '{self.__class__.__name__}(name={self.name!r})'
556 return templ
.format(self
=self
)
559 def is_ratified(self
):
560 """True if the vendor namespace is one that traditionally requires
561 ratification by Khronos.
563 return self
.vendor_namespace
in self
.RATIFIED_NAMESPACES
565 def __parse_requirements(self
, xml_extension
, command_map
, enum_map
):
566 """For each <command> and <enum> under a <require>, create
567 a Requirement that links this Extension to a Command or Enum.
569 self
.requirements
= set()
571 def link(xml_require
, x
):
572 api
= xml_require
.get('api', None)
574 assert api
in self
.supported_apis
575 apis
= frozenset((api
,))
577 apis
= frozenset(self
.supported_apis
)
579 req
= Requirement(provider
=self
, provided
=x
, apis
=apis
)
580 self
.requirements
.add(req
)
581 x
.requirements
.add(req
)
583 for xml_req
in xml_extension
.iterfind('./require'):
584 for xml_cmd
in xml_req
.iterfind('./command'):
585 cmd
= command_map
[xml_cmd
.get('name')]
587 for xml_enum
in xml_req
.iterfind('./enum'):
588 enum
= enum_map
[xml_enum
.get('name')]
592 @functools.total_ordering
593 class Requirement(object):
594 """A <require> XML element, which links a provider (Feature or Extension)
595 to a provided (Command or Enum) for a set of apis.
598 def __init__(self
, provider
, provided
, apis
):
599 self
.provider
= provider
600 self
.provided
= provided
601 self
.apis
= frozenset(apis
)
603 def choose_if(condition
, obj
):
609 self
.has_feature
= isinstance(provider
, Feature
)
610 self
.has_extension
= isinstance(provider
, Extension
)
611 self
.has_command
= isinstance(provided
, Command
)
612 self
.has_enum
= isinstance(provided
, Enum
)
614 self
.feature
= choose_if(self
.has_feature
, self
.provider
)
615 self
.extension
= choose_if(self
.has_extension
, self
.provider
)
616 self
.command
= choose_if(self
.has_command
, self
.provided
)
617 self
.enum
= choose_if(self
.has_enum
, self
.provided
)
619 assert self
.has_feature
+ self
.has_extension
== 1
620 assert self
.has_command
+ self
.has_enum
== 1
621 assert self
.apis
<= VALID_APIS
623 _log_debug('created {0}'.format(self
))
626 return hash('__Requirement_class_{}_{}_'.format(
627 self
.provider
, self
.provided
))
629 def __eq__(self
, other
):
630 if self
.provider
!= other
.provider
:
632 elif self
.provided
!= other
.provided
:
636 def __lt__(self
, other
):
637 if self
.provider
< other
.provider
:
639 elif self
.provided
< other
.provided
:
644 templ
= ('{self.__class__.__name__}'
645 '(provider={self.provider.name!r},'
646 ' provided={self.provided.name!r},'
647 ' apis={api_tuple})')
648 return templ
.format(self
=self
, api_tuple
=tuple(self
.apis
))
651 class CommandParam(object):
652 """A <param> XML element at path command/param.
660 __PARAM_NAME_FIXES
= {'near': 'hither', 'far': 'yon'}
662 def __init__(self
, xml_param
, log
=None):
663 """Parse a <param> element."""
665 # Example <param> elements:
667 # <param>const <ptype>GLchar</ptype> *<name>name</name></param>
668 # <param len="1"><ptype>GLsizei</ptype> *<name>length</name></param>
669 # <param len="bufSize">
670 # <ptype>GLint</ptype> *<name>values</name>
672 # <param><ptype>GLenum</ptype> <name>shadertype</name></param>
673 # <param group="sync">
674 # <ptype>GLsync</ptype> <name>sync</name>
676 # <param><ptype>GLuint</ptype> <name>baseAndCount</name>[2]</param>
678 assert xml_param
.tag
== 'param'
680 self
.name
= xml_param
.find('./name').text
682 # Rename the parameter if its name is a reserved keyword in MSVC.
683 self
.name
= self
.__PARAM
_NAME
_FIXES
.get(self
.name
, self
.name
)
686 c_type_text
= list(xml_param
.itertext())
688 # Could be <name> or <array_suffix>
689 c_type_text_end
= c_type_text
.pop(-1)
691 # We popped off <array_suffix>
692 if c_type_text_end
.startswith('['):
693 # This is an array variable.
694 self
.array_suffix
= c_type_text_end
695 c_type_text
.pop(-1) # Pop off the next one (<name>)
697 self
.array_suffix
= ''
698 c_type_text
= (t
.strip() for t
in c_type_text
)
699 self
.c_type
= ' '.join(c_type_text
).strip()
701 _log_debug('parsed {0}'.format(self
))
704 templ
= ('{self.__class__.__name__}'
705 '(name={self.name!r}, type={self.c_type!r}, '
706 'suffix={self.array_suffix!r})')
707 return templ
.format(self
=self
)
710 @functools.total_ordering
711 class Command(object):
712 """A <command> XML element.
715 name: The XML element's 'name' attribute, which is also the function
718 c_return_type: For example, "void *".
720 alias: The XML element's 'alias' element. May be None.
722 param_list: List of that contains a CommandParam for each <param>
725 requirements: A collection of each Requirement that exposes this
729 def __init__(self
, xml_command
):
730 """Parse a <command> element."""
732 # Example <command> element:
735 # <proto>void <name>glTexSubImage2D</name></proto>
736 # <param group="TextureTarget">
737 # <ptype>GLenum</ptype> <name>target</name>
739 # <param group="CheckedInt32">
740 # <ptype>GLint</ptype> <name>level</name>
742 # <param group="CheckedInt32">
743 # <ptype>GLint</ptype> <name>xoffset</name>
745 # <param group="CheckedInt32">
746 # <ptype>GLint</ptype> <name>yoffset</name>
748 # <param><ptype>GLsizei</ptype> <name>width</name></param>
749 # <param><ptype>GLsizei</ptype> <name>height</name></param>
750 # <param group="PixelFormat">
751 # <ptype>GLenum</ptype> <name>format</name>
753 # <param group="PixelType">
754 # <ptype>GLenum</ptype> <name>type</name>
756 # <param len="COMPSIZE(format,type,width,height)">const void *
757 # <name>pixels</name>
759 # <glx type="render" opcode="4100"/>
760 # <glx type="render" opcode="332" name="glTexSubImage2DPBO"
761 # comment="PBO protocol"/>
765 assert xml_command
.tag
== 'command'
766 xml_proto
= xml_command
.find('./proto')
767 self
.name
= xml_proto
.find('./name').text
768 _log_debug('start parsing Command(name={0!r})'.format(self
.name
))
770 self
.requirements
= set()
771 self
.__vendor
_namespace
= None
773 # Parse the return type from the <proto> element.
775 # Example of a difficult <proto> element:
776 # <proto group="String">const <ptype>GLubyte</ptype> *
777 # <name>glGetStringi</name>
779 c_return_type_text
= list(xml_proto
.itertext())
780 # Pop off the text from the <name> subelement.
781 c_return_type_text
.pop(-1)
782 c_return_type_text
= (t
.strip() for t
in c_return_type_text
)
783 self
.c_return_type
= ' '.join(c_return_type_text
).strip()
785 # Parse alias info, if any.
786 xml_alias
= xml_command
.find('./alias')
787 if xml_alias
is None:
790 self
.alias
= xml_alias
.get('name')
793 CommandParam(xml_param
)
794 for xml_param
in xml_command
.iterfind('./param')
797 _log_debug(('parsed {self.__class__.__name__}('
798 'name={self.name!r}, '
799 'alias={self.alias!r}, '
800 'prototype={self.c_prototype!r})').format(self
=self
))
803 return hash('__Command_class_{}_'.format(self
.name
))
805 def __eq__(self
, other
):
806 return self
.name
== other
.name
808 def __lt__(self
, other
):
809 return self
.name
< other
.name
812 templ
= '{self.__class__.__name__}({self.name!r})'
813 return templ
.format(self
=self
)
816 def vendor_namespace(self
):
817 if self
.__vendor
_namespace
is None:
818 for req
in self
.requirements
:
823 if ext
.vendor_namespace
is None:
826 if self
.name
.endswith('_' + ext
.vendor_namespace
):
827 self
.__vendor
_namespace
= ext
.vendor_namespace
829 return self
.__vendor
_namespace
832 def c_prototype(self
):
833 """For example, "void glAccum(GLenum o, GLfloat value)"."""
834 return '{self.c_return_type} {self.name}({self.c_named_param_list})'\
838 def c_funcptr_typedef(self
):
839 """For example, "PFNGLACCUMROC" for glAccum."""
840 return 'PFN{0}PROC'.format(self
.name
).upper()
843 def c_named_param_list(self
):
844 """For example, "GLenum op, GLfloat value" for glAccum."""
846 '{p.c_type} {p.name}{p.array_suffix}'.format(p
=param
)
847 for param
in self
.param_list
851 def c_unnamed_param_list(self
):
852 """For example, "GLenum, GLfloat" for glAccum."""
854 '{param.c_type}{param.array_suffix}'.format(param
=param
)
855 for param
in self
.param_list
859 def c_untyped_param_list(self
):
860 """For example, "op, value" for glAccum."""
863 for param
in self
.param_list
867 @functools.total_ordering
868 class CommandAliasSet(ImmutableOrderedKeyedSet
):
870 def __init__(self
, commands
):
871 ImmutableOrderedKeyedSet
.__init
__(self
, key
='name',
872 elems
=sorted(commands
))
873 self
.__primary
_command
= None
874 self
.__requirements
= None
876 def __eq__(self
, other
):
877 return self
.name
== other
.name
879 def __lt__(self
, other
):
880 return self
.name
< other
.name
883 templ
= '{self.__class__.__name__}({self.name!r})'
884 return templ
.format(self
=self
)
887 return hash(repr(self
))
891 return self
.primary_command
.name
894 def primary_command(self
):
895 """The set's first command when sorted by name."""
900 def requirements(self
):
901 """A sorted iterator over each Requirement that exposes this
904 if self
.__requirements
is None:
905 self
.__requirements
= sorted(
908 for req
in command
.requirements
910 _log_debug('{0} sorted requirements: {1}'.format(
911 self
, self
.__requirements
))
913 return iter(self
.__requirements
)
916 class CommandAliasMap(object):
920 self
.__sorted
_unique
_values
= None
922 def __getitem__(self
, command_name
):
923 return self
.__map
[command_name
]
926 """A sorted iterator over the map's unique CommandAliasSet values."""
927 if self
.__sorted
_unique
_values
is None:
928 self
.__sorted
_unique
_values
= \
929 sorted(set(self
.__map
.values()))
931 return iter(self
.__sorted
_unique
_values
)
933 def get(self
, command_name
, default
):
934 return self
.__map
.get(command_name
, default
)
936 def add(self
, command
):
937 assert isinstance(command
, Command
)
939 _log_debug('adding command {0!r} to CommandAliasMap'.format(name
))
941 name_set
= self
.get(name
, None)
942 assert self
.__is
_set
_mapping
_complete
(name_set
)
944 alias_set
= self
.get(command
.alias
, None)
945 assert self
.__is
_set
_mapping
_complete
(alias_set
)
947 if name_set
is alias_set
and name_set
is not None:
950 # After modifying the contained alias sets, the mapping will no longer
952 self
.__sorted
_unique
_values
= None
954 new_set_elems
= set((command
,))
955 if name_set
is not None:
956 new_set_elems
.update(name_set
)
957 if alias_set
is not None:
958 new_set_elems
.update(alias_set
)
960 new_set
= CommandAliasSet(new_set_elems
)
961 for other_command
in new_set
:
962 self
.__map
[other_command
.name
] = new_set
963 if other_command
.alias
is not None:
964 self
.__map
[other_command
.alias
] = new_set
966 def __is_set_mapping_complete(self
, alias_set
):
967 if alias_set
is None:
970 for command
in alias_set
:
971 if self
[command
.name
] is not alias_set
:
973 if command
.alias
is None:
975 if self
[command
.alias
] is not alias_set
:
981 class EnumGroup(object):
982 """An <enums> element at path registry/enums.
985 name: The XML element's 'group' attribute. If the XML does not define
986 'group', then this class invents one.
988 type: The XML element's 'type' attribute. If the XML does not define
989 'type', then this class invents one.
991 start, end: The XML element's 'start' and 'end' attributes. Each may be
994 enums: An OrderedKeyedSet of Enum that contains each <enum> subelement
998 # Each EnumGroup belongs to exactly one member of EnumGroup.TYPES.
1000 # Some members in EnumGroup.TYPES are invented and not present in gl.xml.
1001 # The only enum type defined explicitly in gl.xml is "bitmask", which
1002 # occurs as <enums type="bitmask">. However, in gl.xml each block of
1003 # non-bitmask enums is introduced by a comment that describes the block's
1004 # "type", even if the <enums> tag lacks a 'type' attribute. (Thanks,
1005 # Khronos, for encoding data in XML comments rather than the XML itself).
1006 # EnumGroup.TYPES lists such implicit comment-only types, with invented
1007 # names, alongside the types explicitly defined by <enums type=>.
1009 # Type 'default_namespace' is self-explanatory. It indicates the large
1010 # set of enums from 0x0000 to 0xffff that includes, for example,
1011 # GL_POINTS and GL_TEXTURE_2D.
1012 'default_namespace',
1014 # Type 'bitmask' is self-explanatory.
1017 # Type 'small_index' indicates a small namespace of non-bitmask enums.
1018 # As of Khronos revision 26792, 'small_index' groups generally contain
1019 # small numbers used for indexed access.
1022 # Type 'special' is used only for the group named "SpecialNumbers". The
1023 # group contains enums such as GL_FALSE, GL_ZERO, and GL_INVALID_INDEX.
1027 def __init__(self
, xml_enums
):
1028 """Parse an <enums> element."""
1030 # Example of a bitmask group:
1032 # <enums namespace="GL" group="SyncObjectMask" type="bitmask">
1033 # <enum value="0x00000001"
1034 # name="GL_SYNC_FLUSH_COMMANDS_BIT"/>
1035 # <enum value="0x00000001"
1036 # name="GL_SYNC_FLUSH_COMMANDS_BIT_APPLE"/>
1039 # Example of a group that resides in OpenGL's default enum namespace:
1041 # <enums namespace="GL" start="0x0000" end="0x7FFF" vendor="ARB"
1043 # <enum value="0x0000" name="GL_POINTS"/>
1044 # <enum value="0x0001" name="GL_LINES"/>
1045 # <enum value="0x0002" name="GL_LINE_LOOP"/>
1049 # Example of a non-bitmask group that resides outside OpenGL's default
1052 # <enums namespace="GL" group="PathRenderingTokenNV" vendor="NV">
1053 # <enum value="0x00" name="GL_CLOSE_PATH_NV"/>
1054 # <enum value="0x02" name="GL_MOVE_TO_NV"/>
1055 # <enum value="0x03" name="GL_RELATIVE_MOVE_TO_NV"/>
1060 self
.name
= xml_enums
.get('group', None)
1061 _log_debug('start parsing {0}'.format(self
))
1063 self
.type = xml_enums
.get('type', None)
1064 self
.start
= xml_enums
.get('start', None)
1065 self
.end
= xml_enums
.get('end', None)
1068 self
.__invent
_name
_and
_type
()
1069 assert self
.name
is not None
1070 assert self
.type in self
.TYPES
1072 _log_debug('start parsing <enum> subelements of {0}'.format(self
))
1073 self
.enums
= OrderedKeyedSet(key
='name')
1074 for xml_enum
in xml_enums
.iterfind('./enum'):
1075 self
.enums
.add(Enum(self
, xml_enum
))
1076 _log_debug('parsed {0}'.format(self
))
1079 templ
= '{self.__class__.__name__}({self.name!r})'
1080 return templ
.format(self
=self
)
1082 def __invent_name_and_type(self
):
1083 """If the XML didn't define a name or type, invent one."""
1084 if self
.name
is None:
1085 assert self
.type is None
1086 assert self
.start
is not None
1087 assert self
.end
is not None
1088 self
.name
= 'range_{self.start}_{self.end}'.format(self
=self
)
1089 self
.type = 'default_namespace'
1090 elif self
.type is None:
1091 self
.type = 'small_index'
1092 elif self
.name
== 'SpecialNumbers':
1093 assert self
.type is None
1094 self
.type = 'special'
1097 @functools.total_ordering
1099 """An <enum> XML element.
1102 name, api: The XML element's 'name' and 'api' attributes.
1104 str_value, c_num_literal: Equivalent attributes. The XML element's
1107 num_value: The long integer for str_value.
1109 requirements: A collection of each Requirement that exposes this Enum.
1112 def __init__(self
, enum_group
, xml_enum
):
1113 """Parse an <enum> tag located at path registry/enums/enum."""
1115 # Example <enum> element:
1116 # <enum value="0x0000" name="GL_POINTS"/>
1118 assert isinstance(enum_group
, EnumGroup
)
1119 assert xml_enum
.tag
== 'enum'
1121 self
.requirements
= set()
1122 self
.__vendor
_namespace
= None
1124 self
.group
= enum_group
1125 self
.name
= xml_enum
.get('name')
1126 self
.api
= xml_enum
.get('api')
1127 self
.str_value
= xml_enum
.get('value')
1128 self
.c_num_literal
= self
.str_value
1130 if '0x' in self
.str_value
.lower():
1135 self
.num_value
= int(self
.str_value
, base
)
1137 _log_debug('parsed {0}'.format(self
))
1140 templ
= ('{self.__class__.__name__}'
1141 '(name={self.name!r},'
1142 ' value={self.str_value!r})')
1143 return templ
.format(self
=self
)
1145 def __eq__(self
, other
):
1146 if self
.num_value
!= other
.num_value
:
1148 elif (self
.vendor_namespace
is None) != \
1149 (other
.vendor_namespace
is None):
1151 elif (self
.vendor_namespace
in Extension
.RATIFIED_NAMESPACES
) != \
1152 (other
.vendor_namespace
in Extension
.RATIFIED_NAMESPACES
):
1154 elif (self
.vendor_namespace
== 'EXT') != \
1155 (other
.vendor_namespace
== 'EXT'):
1157 elif self
.name
!= other
.name
:
1159 return self
.api
== other
.api
1161 def __lt__(self
, other
): # pylint: disable=too-many-return-statements
1164 Sort by numerical value, then vendor_namspace (ratified first, then
1165 EXT), then by full name, and finally by api.
1167 This sort order ensures that names provided by core specifications
1168 precede those provided by ratified extensions, which proceed those
1169 provided by unratified extensions.
1171 For example: GL_RED < GL_RED_EXT < GL_RED_INTEL
1174 if self
.num_value
!= other
.num_value
:
1175 if self
.num_value
< other
.num_value
:
1179 x
= self
.vendor_namespace
is None
1180 y
= other
.vendor_namespace
is None
1186 x
= self
.vendor_namespace
in Extension
.RATIFIED_NAMESPACES
1187 y
= other
.vendor_namespace
in Extension
.RATIFIED_NAMESPACES
1193 x
= self
.vendor_namespace
== 'EXT'
1194 y
= other
.vendor_namespace
== 'EXT'
1200 if self
.name
!= other
.name
:
1201 if self
.name
< other
.name
:
1205 return self
.api
< other
.api
1208 def vendor_namespace(self
):
1209 if self
.__vendor
_namespace
is None:
1210 for req
in self
.requirements
:
1215 if ext
.vendor_namespace
is None:
1218 if self
.name
.endswith('_' + ext
.vendor_namespace
):
1219 self
.__vendor
_namespace
= ext
.vendor_namespace
1221 return self
.__vendor
_namespace