1 # Copyright 2014 Intel Corporation
3 # Permission is hereby granted, free of charge, to any person obtaining a
4 # copy of this software and associated documentation files (the "Software"),
5 # to deal in the Software without restriction, including without limitation
6 # the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 # and/or sell copies of the Software, and to permit persons to whom the
8 # Software is furnished to do so, subject to the following conditions:
10 # The above copyright notice and this permission notice (including the next
11 # paragraph) shall be included in all copies or substantial portions of the
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 Parse gl.xml into Python objects.
26 from __future__
import (
27 absolute_import
, division
, print_function
, unicode_literals
34 from copy
import copy
, deepcopy
38 # Export 'debug' so other Piglit modules can easily enable it.
44 print('debug: {0}: {1}'.format(__name__
, msg
), file=sys
.stderr
)
47 # Prefer the external module 'lxml.etree' (it uses libxml2) over Python's
48 # builtin 'xml.etree.ElementTree'. It's faster.
50 import lxml
.etree
as etree
51 _log_debug('etree is lxml.etree')
53 import xml
.etree
.cElementTree
as etree
54 _log_debug('etree is xml.etree.cElementTree')
58 """Parse gl.xml and return a Registry object."""
59 filename
= os
.path
.join(os
.path
.dirname(__file__
), 'gl.xml')
60 xml_registry
= etree
.parse(filename
).getroot()
61 _repair_xml(xml_registry
)
62 return Registry(xml_registry
)
65 def _repair_xml(xml_registry
):
68 'glOcclusionQueryEventMaskAMD',
69 'gles2_GL_ACTIVE_PROGRAM_EXT',
74 def defer_removal(parent
, child
):
75 remove_queue
.append((parent
, child
))
77 for enums
in xml_registry
.iterfind('./enums'):
78 if ('GL_ALL_ATTRIB_BITS' in fixes
and
79 enums
.get('group') == 'AttribMask'):
80 # The XML defines GL_ALL_ATTRIB_BITS incorrectly with all bits
81 # set (0xFFFFFFFF). From the GL_ARB_multisample spec, v5:
83 # In order to avoid incompatibility with GL implementations
84 # that do not support SGIS_multisample, ALL_ATTRIB_BITS
85 # does not include MULTISAMPLE_BIT_ARB.
87 enum
= enums
.find("./enum[@name='GL_ALL_ATTRIB_BITS']")
88 enum
.set('value', '0x000FFFFF')
90 fixes
.remove('GL_ALL_ATTRIB_BITS')
93 if ('glOcclusionQueryEventMaskAMD' in fixes
and
94 enums
.get('namespace') == 'OcclusionQueryEventMaskAMD'):
95 # This tag's attributes are totally broken.
96 enums
.set('namespace', 'GL')
97 enums
.set('group', 'OcclusionQueryEventMaskAMD')
98 enums
.set('type', 'bitmask')
100 fixes
.remove('glOcclusionQueryEventMaskAMD')
103 if ('gles2_GL_ACTIVE_PROGRAM_EXT' in fixes
and
104 enums
.get('vendor') is not None and
105 enums
.get('vendor') == 'ARB' and
106 enums
.get('start') is not None and
107 enums
.get('start') <= '0x8259' and
108 enums
.get('end') is not None and
109 enums
.get('end') >= '0x8259'):
110 # GL_ACTIVE_PROGRAM_EXT has different numerical values in GL
111 # (0x8B8D) and in GLES (0x8259). Remove the GLES value to avoid
112 # redefinition collisions.
113 bad_enum
= enums
.find(("./enum"
115 "[@name='GL_ACTIVE_PROGRAM_EXT']"
117 defer_removal(enums
, bad_enum
)
119 fixes
.remove('gles2_GL_ACTIVE_PROGRAM_EXT')
122 for (parent
, child
) in remove_queue
:
126 raise Exception('failed to apply some xml repairs: ' +
127 ', '.join(map(repr, fixes
)))
130 class OrderedKeyedSet(object):
131 """A set with keyed elements that preserves order of element insertion.
133 Why waste words? Let's document the class with example code.
136 Cheese = namedtuple('Cheese', ('name', 'flavor'))
137 cheeses = OrderedKeyedSet(key='name')
138 cheeses.add(Cheese(name='cheddar', flavor='good'))
139 cheeses.add(Cheese(name='gouda', flavor='smells like feet'))
140 cheeses.add(Cheese(name='romano', flavor='awesome'))
142 # Elements are retrievable by key.
143 assert cheeses['gouda'].flavor == 'smells like feet'
145 # On key collision, the old element is removed.
146 cheeses.add(Cheese(name='gouda', flavor='ok i guess'))
147 assert cheeses['gouda'].flavor == 'ok i guess'
149 # The set preserves order of insertion. Replacement does not alter
151 assert list(cheeses)[2].name == 'romano'
153 # The set is iterable.
154 for cheese in cheeses:
157 # Yet another example...
158 Bread = namedtuple('Bread', ('name', 'smell'))
159 breads = OrderedKeyedSet(key='name')
160 breads.add(Bread(name='como', smell='subtle')
161 breads.add(Bread(name='sourdough', smell='pleasant'))
163 # The set supports some common set operations, such as union.
164 breads_and_cheeses = breads | cheeses
165 assert len(breads_and_cheeses) == len(breads) + len(cheeses)
168 def __init__(self
, key
, elems
=()):
169 """Create a new set with the given key.
171 The given 'key' defines how to calculate each element's key value. If
172 'key' is a string, then each key value is defined to be
173 `getattr(elem, key)`. If 'key' is a function, then the key value is
177 # A linked list contains the set's items. Each list node is a 4-tuple
178 # [prev, next, key, value]. The root node is permanent.
180 root
[:] = [root
, root
, None, None]
181 self
.__list
_root
= root
183 # For quick retrieval, we map each key to its node. That is, each map
184 # pair has form {key: [prev, next, key, value])}.
187 if isinstance(key
, six
.text_type
):
188 self
.__key
_func
= lambda elem
: getattr(elem
, key
)
190 self
.__key
_func
= key
195 def __or__(self
, other
):
196 """Same as `union`."""
197 return self
.union(other
)
199 def __contains__(self
, key
):
200 return key
in self
.__map
203 return OrderedKeyedSet(key
=deepcopy(self
.__key
_func
),
206 def __getitem__(self
, key
):
207 return self
.__map
[key
][3]
210 return self
.itervalues()
213 return len(self
.__map
)
216 templ
= '{self.__class__.__name__}({self.name!r})'
217 return templ
.format(self
=self
)
219 def add(self
, value
):
220 key
= self
.__key
_func
(value
)
221 node
= self
.__map
.get(key
, None)
225 root
= self
.__list
_root
227 new_tail
= [old_tail
, root
, key
, value
]
228 new_tail
[0][1] = new_tail
229 new_tail
[1][0] = new_tail
230 self
.__map
[key
] = new_tail
234 root
= self
.__list
_root
235 root
[:] = [root
, root
, None, None]
237 def extend(self
, elems
):
241 def get(self
, key
, default
):
242 node
= self
.__map
.get(key
, None)
249 root
= self
.__list
_root
251 while node
is not root
:
252 yield (node
[2], node
[3])
256 return (i
[0] for i
in self
.iteritems())
258 def itervalues(self
):
259 return (i
[1] for i
in self
.iteritems())
262 node
= self
.__map
.pop(key
)
267 def sort_by_key(self
):
268 sorted_items
= sorted(six
.iteritems(self
.__map
))
270 for item
in sorted_items
:
273 def sort_by_value(self
):
274 sorted_values
= sorted(six
.itervalues(self
.__map
))
276 for value
in sorted_values
:
279 def union(self
, other
):
280 """Return the union of two sets as a new set.
282 In the new set, all elements of the self set precede those of the other
283 set. The order of elements in the new set preserves the order of the
286 The new set's key function is copied from self. On key collisions, set
287 y has precedence over x.
294 class ImmutableOrderedKeyedSet(OrderedKeyedSet
):
296 def __init__(self
, key
, elems
):
297 self
.__is
_frozen
= False
298 OrderedKeyedSet
.__init
__(self
, key
=key
, elems
=elems
)
299 self
.__is
_frozen
= True
301 def add(self
, value
):
305 OrderedKeyedSet
.add(self
, value
)
314 class ImmutableError(Exception):
318 # Values that may appear in the XML attributes 'api' and 'supported'.
319 VALID_APIS
= frozenset(('gl', 'glcore', 'gles1', 'gles2', 'glsc2'))
322 class Registry(object):
323 """The toplevel <registry> element.
326 features: An OrderedKeyedSet that contains a Feature for each <feature>
329 extensions: An OrderedKeyedSet that contains an Extension for each
330 <extension> subelement.
332 commands: An OrderedKeyedSet that contains a Command for each <command>
335 command_alias_map: A CommandAliasMap that contains a CommandAliasSet
336 for each equivalence class of commands.
338 enum_groups: An OrderedKeyedSet that contains an EnumGroup for each
341 enums: An OrderedKeyedSet that contains an Enum for each <enum>
344 vendor_namespaces: A collection of all vendor prefixes and suffixes,
345 such as "ARB", "EXT", "CHROMIUM", and "NV".
348 def __init__(self
, xml_registry
):
349 """Parse the <registry> element."""
351 assert xml_registry
.tag
== 'registry'
353 self
.command_alias_map
= CommandAliasMap()
354 self
.commands
= OrderedKeyedSet(key
='name')
355 self
.enum_groups
= []
356 self
.enums
= OrderedKeyedSet(key
='name')
357 self
.extensions
= OrderedKeyedSet(key
='name')
358 self
.features
= OrderedKeyedSet(key
='name')
359 self
.vendor_namespaces
= set()
361 for xml_command
in xml_registry
.iterfind('./commands/command'):
362 command
= Command(xml_command
)
363 self
.commands
.add(command
)
364 self
.command_alias_map
.add(command
)
366 for xml_enums
in xml_registry
.iterfind('./enums'):
367 enum_group
= EnumGroup(xml_enums
)
368 self
.enum_groups
.append(enum_group
)
369 for enum
in enum_group
.enums
:
372 for xml_feature
in xml_registry
.iterfind('./feature'):
373 feature
= Feature(xml_feature
, command_map
=self
.commands
,
375 self
.features
.add(feature
)
377 for xml_ext
in xml_registry
.iterfind('./extensions/extension'):
378 ext
= Extension(xml_ext
, command_map
=self
.commands
,
380 self
.extensions
.add(ext
)
381 self
.vendor_namespaces
.add(ext
.vendor_namespace
)
383 self
.vendor_namespaces
.remove(None)
386 @functools.total_ordering
387 class Feature(object):
388 """A <feature> XML element.
391 name: The XML element's 'name' attribute.
393 api: The XML element's 'api' attribute.
395 version_str: The XML element's 'number' attribute. For example, "3.1".
397 version_float: float(version_str)
399 version_int: int(10 * version_float)
401 requirements: A collection of Requirement for each Command and Enum
402 this Feature requires.
405 def __init__(self
, xml_feature
, command_map
, enum_map
):
406 """Parse a <feature> element."""
408 # Example <feature> element:
410 # <feature api="gles2" name="GL_ES_VERSION_3_1" number="3.1">
411 # <!-- arrays_of_arrays features -->
413 # <!-- compute_shader features -->
415 # <command name="glDispatchCompute"/>
416 # <command name="glDispatchComputeIndirect"/>
417 # <enum name="GL_COMPUTE_SHADER"/>
418 # <enum name="GL_MAX_COMPUTE_UNIFORM_BLOCKS"/>
421 # <!-- draw_indirect features -->
423 # <command name="glDrawArraysIndirect"/>
424 # <command name="glDrawElementsIndirect"/>
425 # <enum name="GL_DRAW_INDIRECT_BUFFER"/>
426 # <enum name="GL_DRAW_INDIRECT_BUFFER_BINDING"/>
431 assert xml_feature
.tag
== 'feature'
433 # Parse the <feature> tag's attributes.
434 self
.name
= xml_feature
.get('name')
435 self
.api
= xml_feature
.get('api')
436 self
.is_gles
= self
.name
.startswith('GL_ES')
438 self
.version_str
= xml_feature
.get('number')
439 self
.version_float
= float(self
.version_str
)
440 self
.version_int
= int(10 * self
.version_float
)
442 self
.__parse
_requirements
(xml_feature
, command_map
, enum_map
)
444 assert self
.api
in VALID_APIS
445 assert len(self
.requirements
) > 0
447 def __eq__(self
, other
):
450 elif isinstance(other
, Extension
):
452 elif self
.is_gles
!= other
.is_gles
:
454 return self
.name
== other
.name
456 def __lt__(self
, other
):
457 if isinstance(other
, Extension
):
459 # Desktop GL before GLES
460 elif self
.is_gles
!= other
.is_gles
:
465 return self
.name
< other
.name
468 templ
= '{self.__class__.__name__}({self.name!r})'
469 return templ
.format(self
=self
)
471 def __parse_requirements(self
, xml_feature
, command_map
, enum_map
):
472 """For each <command> and <enum> under a <require>, create
473 a Requirement that links this Feature to a Command or Enum.
475 self
.requirements
= set()
478 req
= Requirement(provider
=self
, provided
=x
,
479 apis
=frozenset((self
.api
,)))
480 self
.requirements
.add(req
)
481 x
.requirements
.add(req
)
483 for xml_cmd
in xml_feature
.iterfind('./require/command'):
484 cmd
= command_map
[xml_cmd
.get('name')]
486 for xml_enum
in xml_feature
.iterfind('./require/enum'):
487 enum
= enum_map
[xml_enum
.get('name')]
491 @functools.total_ordering
492 class Extension(object):
493 """An <extension> XML element.
496 name: The XML element's 'name' attribute.
498 supported_apis: The set of api strings in the XML element's 'supported'
499 attribute. For example, set('gl', 'glcore').
501 vendor_namespace: For example, "AMD". May be None.
503 requirements: A collection of Requirement for each Command and Enum
504 this Extension requires.
507 __VENDOR_REGEX
= re
.compile(r
'^GL_(?P<vendor_namespace>[A-Z]+)_')
508 RATIFIED_NAMESPACES
= ('KHR', 'ARB', 'OES')
510 def __init__(self
, xml_extension
, command_map
, enum_map
):
511 """Parse an <extension> element."""
513 # Example <extension> element:
514 # <extension name="GL_ARB_ES2_compatibility" supported="gl|glcore">
516 # <enum name="GL_FIXED"/>
517 # <enum name="GL_IMPLEMENTATION_COLOR_READ_TYPE"/>
519 # <command name="glReleaseShaderCompiler"/>
520 # <command name="glShaderBinary"/>
525 assert xml_extension
.tag
== 'extension'
527 self
.name
= xml_extension
.get('name')
529 self
.vendor_namespace
= None
530 match
= Extension
.__VENDOR
_REGEX
.match(self
.name
)
531 if match
is not None:
532 groups
= match
.groupdict()
533 self
.vendor_namespace
= groups
.get('vendor_namespace', None)
535 self
.supported_apis
= xml_extension
.get('supported').split('|')
536 self
.supported_apis
= frozenset(self
.supported_apis
)
537 assert self
.supported_apis
<= VALID_APIS
539 self
.__parse
_requirements
(xml_extension
, command_map
, enum_map
)
541 def __eq__(self
, other
):
544 elif isinstance(other
, Feature
):
546 elif self
.is_ratified
!= other
.is_ratified
:
548 elif self
.vendor_namespace
== 'EXT' != other
.vendor_namespace
== 'EXT':
550 return self
.name
== other
.name
552 def __lt__(self
, other
):
553 if isinstance(other
, Feature
):
555 elif self
.is_ratified
!= other
.is_ratified
:
556 # sort ratified before unratified
561 elif (other
.vendor_namespace
== 'EXT') != \
562 (self
.vendor_namespace
== 'EXT'):
563 # Sort EXT before others
564 if self
.vendor_namespace
== 'EXT':
568 return self
.name
< other
.name
571 templ
= '{self.__class__.__name__}(name={self.name!r})'
572 return templ
.format(self
=self
)
575 def is_ratified(self
):
576 """True if the vendor namespace is one that traditionally requires
577 ratification by Khronos.
579 return self
.vendor_namespace
in self
.RATIFIED_NAMESPACES
581 def __parse_requirements(self
, xml_extension
, command_map
, enum_map
):
582 """For each <command> and <enum> under a <require>, create
583 a Requirement that links this Extension to a Command or Enum.
585 self
.requirements
= set()
587 def link(xml_require
, x
):
588 api
= xml_require
.get('api', None)
590 assert api
in self
.supported_apis
591 apis
= frozenset((api
,))
593 apis
= frozenset(self
.supported_apis
)
595 req
= Requirement(provider
=self
, provided
=x
, apis
=apis
)
596 self
.requirements
.add(req
)
597 x
.requirements
.add(req
)
599 for xml_req
in xml_extension
.iterfind('./require'):
600 for xml_cmd
in xml_req
.iterfind('./command'):
601 cmd
= command_map
[xml_cmd
.get('name')]
603 for xml_enum
in xml_req
.iterfind('./enum'):
604 enum
= enum_map
[xml_enum
.get('name')]
608 @functools.total_ordering
609 class Requirement(object):
610 """A <require> XML element, which links a provider (Feature or Extension)
611 to a provided (Command or Enum) for a set of apis.
614 def __init__(self
, provider
, provided
, apis
):
615 self
.provider
= provider
616 self
.provided
= provided
617 self
.apis
= frozenset(apis
)
619 def choose_if(condition
, obj
):
625 self
.has_feature
= isinstance(provider
, Feature
)
626 self
.has_extension
= isinstance(provider
, Extension
)
627 self
.has_command
= isinstance(provided
, Command
)
628 self
.has_enum
= isinstance(provided
, Enum
)
630 self
.feature
= choose_if(self
.has_feature
, self
.provider
)
631 self
.extension
= choose_if(self
.has_extension
, self
.provider
)
632 self
.command
= choose_if(self
.has_command
, self
.provided
)
633 self
.enum
= choose_if(self
.has_enum
, self
.provided
)
635 assert self
.has_feature
+ self
.has_extension
== 1
636 assert self
.has_command
+ self
.has_enum
== 1
637 assert self
.apis
<= VALID_APIS
639 _log_debug('created {0}'.format(self
))
642 return hash('__Requirement_class_{}_{}_'.format(
643 self
.provider
, self
.provided
))
645 def __eq__(self
, other
):
646 if self
.provider
!= other
.provider
:
648 elif self
.provided
!= other
.provided
:
652 def __lt__(self
, other
):
653 if self
.provider
< other
.provider
:
655 elif self
.provided
< other
.provided
:
660 templ
= ('{self.__class__.__name__}'
661 '(provider={self.provider.name!r},'
662 ' provided={self.provided.name!r},'
663 ' apis={api_tuple})')
664 return templ
.format(self
=self
, api_tuple
=tuple(self
.apis
))
667 class CommandParam(object):
668 """A <param> XML element at path command/param.
676 __PARAM_NAME_FIXES
= {'near': 'hither', 'far': 'yon'}
678 def __init__(self
, xml_param
, log
=None):
679 """Parse a <param> element."""
681 # Example <param> elements:
683 # <param>const <ptype>GLchar</ptype> *<name>name</name></param>
684 # <param len="1"><ptype>GLsizei</ptype> *<name>length</name></param>
685 # <param len="bufSize">
686 # <ptype>GLint</ptype> *<name>values</name>
688 # <param><ptype>GLenum</ptype> <name>shadertype</name></param>
689 # <param group="sync">
690 # <ptype>GLsync</ptype> <name>sync</name>
692 # <param><ptype>GLuint</ptype> <name>baseAndCount</name>[2]</param>
694 assert xml_param
.tag
== 'param'
696 self
.name
= xml_param
.find('./name').text
698 # Rename the parameter if its name is a reserved keyword in MSVC.
699 self
.name
= self
.__PARAM
_NAME
_FIXES
.get(self
.name
, self
.name
)
702 c_type_text
= list(xml_param
.itertext())
704 # Could be <name> or <array_suffix>
705 c_type_text_end
= c_type_text
.pop(-1)
707 # We popped off <array_suffix>
708 if c_type_text_end
.startswith('['):
709 # This is an array variable.
710 self
.array_suffix
= c_type_text_end
711 c_type_text
.pop(-1) # Pop off the next one (<name>)
713 self
.array_suffix
= ''
714 c_type_text
= (t
.strip() for t
in c_type_text
)
715 self
.c_type
= ' '.join(c_type_text
).strip()
717 _log_debug('parsed {0}'.format(self
))
720 templ
= ('{self.__class__.__name__}'
721 '(name={self.name!r}, type={self.c_type!r}, '
722 'suffix={self.array_suffix!r})')
723 return templ
.format(self
=self
)
726 @functools.total_ordering
727 class Command(object):
728 """A <command> XML element.
731 name: The XML element's 'name' attribute, which is also the function
734 c_return_type: For example, "void *".
736 alias: The XML element's 'alias' element. May be None.
738 param_list: List of that contains a CommandParam for each <param>
741 requirements: A collection of each Requirement that exposes this
745 def __init__(self
, xml_command
):
746 """Parse a <command> element."""
748 # Example <command> element:
751 # <proto>void <name>glTexSubImage2D</name></proto>
752 # <param group="TextureTarget">
753 # <ptype>GLenum</ptype> <name>target</name>
755 # <param group="CheckedInt32">
756 # <ptype>GLint</ptype> <name>level</name>
758 # <param group="CheckedInt32">
759 # <ptype>GLint</ptype> <name>xoffset</name>
761 # <param group="CheckedInt32">
762 # <ptype>GLint</ptype> <name>yoffset</name>
764 # <param><ptype>GLsizei</ptype> <name>width</name></param>
765 # <param><ptype>GLsizei</ptype> <name>height</name></param>
766 # <param group="PixelFormat">
767 # <ptype>GLenum</ptype> <name>format</name>
769 # <param group="PixelType">
770 # <ptype>GLenum</ptype> <name>type</name>
772 # <param len="COMPSIZE(format,type,width,height)">const void *
773 # <name>pixels</name>
775 # <glx type="render" opcode="4100"/>
776 # <glx type="render" opcode="332" name="glTexSubImage2DPBO"
777 # comment="PBO protocol"/>
781 assert xml_command
.tag
== 'command'
782 xml_proto
= xml_command
.find('./proto')
783 self
.name
= xml_proto
.find('./name').text
784 _log_debug('start parsing Command(name={0!r})'.format(self
.name
))
786 self
.requirements
= set()
787 self
.__vendor
_namespace
= None
789 # Parse the return type from the <proto> element.
791 # Example of a difficult <proto> element:
792 # <proto group="String">const <ptype>GLubyte</ptype> *
793 # <name>glGetStringi</name>
795 c_return_type_text
= list(xml_proto
.itertext())
796 # Pop off the text from the <name> subelement.
797 c_return_type_text
.pop(-1)
798 c_return_type_text
= (t
.strip() for t
in c_return_type_text
)
799 self
.c_return_type
= ' '.join(c_return_type_text
).strip()
801 # Parse alias info, if any.
802 xml_alias
= xml_command
.find('./alias')
803 if xml_alias
is None:
806 self
.alias
= xml_alias
.get('name')
809 CommandParam(xml_param
)
810 for xml_param
in xml_command
.iterfind('./param')
813 _log_debug(('parsed {self.__class__.__name__}('
814 'name={self.name!r}, '
815 'alias={self.alias!r}, '
816 'prototype={self.c_prototype!r})').format(self
=self
))
819 return hash('__Command_class_{}_'.format(self
.name
))
821 def __eq__(self
, other
):
822 return self
.name
== other
.name
824 def __lt__(self
, other
):
825 return self
.name
< other
.name
828 templ
= '{self.__class__.__name__}({self.name!r})'
829 return templ
.format(self
=self
)
832 def vendor_namespace(self
):
833 if self
.__vendor
_namespace
is None:
834 for req
in self
.requirements
:
839 if ext
.vendor_namespace
is None:
842 if self
.name
.endswith('_' + ext
.vendor_namespace
):
843 self
.__vendor
_namespace
= ext
.vendor_namespace
845 return self
.__vendor
_namespace
848 def c_prototype(self
):
849 """For example, "void glAccum(GLenum o, GLfloat value)"."""
850 return '{self.c_return_type} {self.name}({self.c_named_param_list})'\
854 def c_funcptr_typedef(self
):
855 """For example, "PFNGLACCUMROC" for glAccum."""
856 return 'PFN{0}PROC'.format(self
.name
).upper()
859 def c_named_param_list(self
):
860 """For example, "GLenum op, GLfloat value" for glAccum."""
862 '{p.c_type} {p.name}{p.array_suffix}'.format(p
=param
)
863 for param
in self
.param_list
867 def c_unnamed_param_list(self
):
868 """For example, "GLenum, GLfloat" for glAccum."""
870 '{param.c_type}{param.array_suffix}'.format(param
=param
)
871 for param
in self
.param_list
875 def c_untyped_param_list(self
):
876 """For example, "op, value" for glAccum."""
879 for param
in self
.param_list
883 @functools.total_ordering
884 class CommandAliasSet(ImmutableOrderedKeyedSet
):
886 def __init__(self
, commands
):
887 ImmutableOrderedKeyedSet
.__init
__(self
, key
='name',
888 elems
=sorted(commands
))
889 self
.__primary
_command
= None
890 self
.__requirements
= None
892 def __eq__(self
, other
):
893 return self
.name
== other
.name
895 def __lt__(self
, other
):
896 return self
.name
< other
.name
899 templ
= '{self.__class__.__name__}({self.name!r})'
900 return templ
.format(self
=self
)
903 return hash(repr(self
))
907 return self
.primary_command
.name
910 def primary_command(self
):
911 """The set's first command when sorted by name."""
916 def requirements(self
):
917 """A sorted iterator over each Requirement that exposes this
920 if self
.__requirements
is None:
921 self
.__requirements
= sorted(
924 for req
in command
.requirements
926 _log_debug('{0} sorted requirements: {1}'.format(
927 self
, self
.__requirements
))
929 return iter(self
.__requirements
)
932 class CommandAliasMap(object):
936 self
.__sorted
_unique
_values
= None
938 def __getitem__(self
, command_name
):
939 return self
.__map
[command_name
]
942 """A sorted iterator over the map's unique CommandAliasSet values."""
943 if self
.__sorted
_unique
_values
is None:
944 self
.__sorted
_unique
_values
= \
945 sorted(set(six
.itervalues(self
.__map
)))
947 return iter(self
.__sorted
_unique
_values
)
949 def get(self
, command_name
, default
):
950 return self
.__map
.get(command_name
, default
)
952 def add(self
, command
):
953 assert isinstance(command
, Command
)
955 _log_debug('adding command {0!r} to CommandAliasMap'.format(name
))
957 name_set
= self
.get(name
, None)
958 assert self
.__is
_set
_mapping
_complete
(name_set
)
960 alias_set
= self
.get(command
.alias
, None)
961 assert self
.__is
_set
_mapping
_complete
(alias_set
)
963 if name_set
is alias_set
and name_set
is not None:
966 # After modifying the contained alias sets, the mapping will no longer
968 self
.__sorted
_unique
_values
= None
970 new_set_elems
= set((command
,))
971 if name_set
is not None:
972 new_set_elems
.update(name_set
)
973 if alias_set
is not None:
974 new_set_elems
.update(alias_set
)
976 new_set
= CommandAliasSet(new_set_elems
)
977 for other_command
in new_set
:
978 self
.__map
[other_command
.name
] = new_set
979 if other_command
.alias
is not None:
980 self
.__map
[other_command
.alias
] = new_set
982 def __is_set_mapping_complete(self
, alias_set
):
983 if alias_set
is None:
986 for command
in alias_set
:
987 if self
[command
.name
] is not alias_set
:
989 if command
.alias
is None:
991 if self
[command
.alias
] is not alias_set
:
997 class EnumGroup(object):
998 """An <enums> element at path registry/enums.
1001 name: The XML element's 'group' attribute. If the XML does not define
1002 'group', then this class invents one.
1004 type: The XML element's 'type' attribute. If the XML does not define
1005 'type', then this class invents one.
1007 start, end: The XML element's 'start' and 'end' attributes. Each may be
1010 enums: An OrderedKeyedSet of Enum that contains each <enum> subelement
1014 # Each EnumGroup belongs to exactly one member of EnumGroup.TYPES.
1016 # Some members in EnumGroup.TYPES are invented and not present in gl.xml.
1017 # The only enum type defined explicitly in gl.xml is "bitmask", which
1018 # occurs as <enums type="bitmask">. However, in gl.xml each block of
1019 # non-bitmask enums is introduced by a comment that describes the block's
1020 # "type", even if the <enums> tag lacks a 'type' attribute. (Thanks,
1021 # Khronos, for encoding data in XML comments rather than the XML itself).
1022 # EnumGroup.TYPES lists such implicit comment-only types, with invented
1023 # names, alongside the types explicitly defined by <enums type=>.
1025 # Type 'default_namespace' is self-explanatory. It indicates the large
1026 # set of enums from 0x0000 to 0xffff that includes, for example,
1027 # GL_POINTS and GL_TEXTURE_2D.
1028 'default_namespace',
1030 # Type 'bitmask' is self-explanatory.
1033 # Type 'small_index' indicates a small namespace of non-bitmask enums.
1034 # As of Khronos revision 26792, 'small_index' groups generally contain
1035 # small numbers used for indexed access.
1038 # Type 'special' is used only for the group named "SpecialNumbers". The
1039 # group contains enums such as GL_FALSE, GL_ZERO, and GL_INVALID_INDEX.
1043 def __init__(self
, xml_enums
):
1044 """Parse an <enums> element."""
1046 # Example of a bitmask group:
1048 # <enums namespace="GL" group="SyncObjectMask" type="bitmask">
1049 # <enum value="0x00000001"
1050 # name="GL_SYNC_FLUSH_COMMANDS_BIT"/>
1051 # <enum value="0x00000001"
1052 # name="GL_SYNC_FLUSH_COMMANDS_BIT_APPLE"/>
1055 # Example of a group that resides in OpenGL's default enum namespace:
1057 # <enums namespace="GL" start="0x0000" end="0x7FFF" vendor="ARB"
1059 # <enum value="0x0000" name="GL_POINTS"/>
1060 # <enum value="0x0001" name="GL_LINES"/>
1061 # <enum value="0x0002" name="GL_LINE_LOOP"/>
1065 # Example of a non-bitmask group that resides outside OpenGL's default
1068 # <enums namespace="GL" group="PathRenderingTokenNV" vendor="NV">
1069 # <enum value="0x00" name="GL_CLOSE_PATH_NV"/>
1070 # <enum value="0x02" name="GL_MOVE_TO_NV"/>
1071 # <enum value="0x03" name="GL_RELATIVE_MOVE_TO_NV"/>
1076 self
.name
= xml_enums
.get('group', None)
1077 _log_debug('start parsing {0}'.format(self
))
1079 self
.type = xml_enums
.get('type', None)
1080 self
.start
= xml_enums
.get('start', None)
1081 self
.end
= xml_enums
.get('end', None)
1084 self
.__invent
_name
_and
_type
()
1085 assert self
.name
is not None
1086 assert self
.type in self
.TYPES
1088 _log_debug('start parsing <enum> subelements of {0}'.format(self
))
1089 self
.enums
= OrderedKeyedSet(key
='name')
1090 for xml_enum
in xml_enums
.iterfind('./enum'):
1091 self
.enums
.add(Enum(self
, xml_enum
))
1092 _log_debug('parsed {0}'.format(self
))
1095 templ
= '{self.__class__.__name__}({self.name!r})'
1096 return templ
.format(self
=self
)
1098 def __invent_name_and_type(self
):
1099 """If the XML didn't define a name or type, invent one."""
1100 if self
.name
is None:
1101 assert self
.type is None
1102 assert self
.start
is not None
1103 assert self
.end
is not None
1104 self
.name
= 'range_{self.start}_{self.end}'.format(self
=self
)
1105 self
.type = 'default_namespace'
1106 elif self
.type is None:
1107 self
.type = 'small_index'
1108 elif self
.name
== 'SpecialNumbers':
1109 assert self
.type is None
1110 self
.type = 'special'
1113 @functools.total_ordering
1115 """An <enum> XML element.
1118 name, api: The XML element's 'name' and 'api' attributes.
1120 str_value, c_num_literal: Equivalent attributes. The XML element's
1123 num_value: The long integer for str_value.
1125 requirements: A collection of each Requirement that exposes this Enum.
1128 def __init__(self
, enum_group
, xml_enum
):
1129 """Parse an <enum> tag located at path registry/enums/enum."""
1131 # Example <enum> element:
1132 # <enum value="0x0000" name="GL_POINTS"/>
1134 assert isinstance(enum_group
, EnumGroup
)
1135 assert xml_enum
.tag
== 'enum'
1137 self
.requirements
= set()
1138 self
.__vendor
_namespace
= None
1140 self
.group
= enum_group
1141 self
.name
= xml_enum
.get('name')
1142 self
.api
= xml_enum
.get('api')
1143 self
.str_value
= xml_enum
.get('value')
1144 self
.c_num_literal
= self
.str_value
1146 if '0x' in self
.str_value
.lower():
1152 # long is undefined in python3, and we are aware of that
1153 # pylint: disable=undefined-variable
1154 self
.num_value
= long(self
.str_value
, base
)
1157 self
.num_value
= int(self
.str_value
, base
)
1159 _log_debug('parsed {0}'.format(self
))
1162 templ
= ('{self.__class__.__name__}'
1163 '(name={self.name!r},'
1164 ' value={self.str_value!r})')
1165 return templ
.format(self
=self
)
1167 def __eq__(self
, other
):
1168 if self
.num_value
!= other
.num_value
:
1170 elif (self
.vendor_namespace
is None) != \
1171 (other
.vendor_namespace
is None):
1173 elif (self
.vendor_namespace
in Extension
.RATIFIED_NAMESPACES
) != \
1174 (other
.vendor_namespace
in Extension
.RATIFIED_NAMESPACES
):
1176 elif (self
.vendor_namespace
== 'EXT') != \
1177 (other
.vendor_namespace
== 'EXT'):
1179 elif self
.name
!= other
.name
:
1181 return self
.api
== other
.api
1183 def __lt__(self
, other
): # pylint: disable=too-many-return-statements
1186 Sort by numerical value, then vendor_namspace (ratified first, then
1187 EXT), then by full name, and finally by api.
1189 This sort order ensures that names provided by core specifications
1190 precede those provided by ratified extensions, which proceed those
1191 provided by unratified extensions.
1193 For example: GL_RED < GL_RED_EXT < GL_RED_INTEL
1196 if self
.num_value
!= other
.num_value
:
1197 if self
.num_value
< other
.num_value
:
1201 x
= self
.vendor_namespace
is None
1202 y
= other
.vendor_namespace
is None
1208 x
= self
.vendor_namespace
in Extension
.RATIFIED_NAMESPACES
1209 y
= other
.vendor_namespace
in Extension
.RATIFIED_NAMESPACES
1215 x
= self
.vendor_namespace
== 'EXT'
1216 y
= other
.vendor_namespace
== 'EXT'
1222 if self
.name
!= other
.name
:
1223 if self
.name
< other
.name
:
1227 return self
.api
< other
.api
1230 def vendor_namespace(self
):
1231 if self
.__vendor
_namespace
is None:
1232 for req
in self
.requirements
:
1237 if ext
.vendor_namespace
is None:
1240 if self
.name
.endswith('_' + ext
.vendor_namespace
):
1241 self
.__vendor
_namespace
= ext
.vendor_namespace
1243 return self
.__vendor
_namespace