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 print_function
33 from copy
import copy
, deepcopy
37 # Export 'debug' so other Piglit modules can easily enable it.
43 print('debug: {0}: {1}'.format(__name__
, msg
), file=sys
.stderr
)
46 # Prefer the external module 'lxml.etree' (it uses libxml2) over Python's
47 # builtin 'xml.etree.ElementTree'. It's faster.
49 import lxml
.etree
as etree
50 _log_debug('etree is lxml.etree')
52 import xml
.etree
.cElementTree
as etree
53 _log_debug('etree is xml.etree.cElementTree')
57 """Parse gl.xml and return a Registry object."""
58 filename
= os
.path
.join(os
.path
.dirname(__file__
), 'gl.xml')
59 xml_registry
= etree
.parse(filename
).getroot()
60 _repair_xml(xml_registry
)
61 return Registry(xml_registry
)
64 def _repair_xml(xml_registry
):
67 'glOcclusionQueryEventMaskAMD',
68 'gles2_GL_ACTIVE_PROGRAM_EXT',
73 def defer_removal(parent
, child
):
74 remove_queue
.append((parent
, child
))
76 for enums
in xml_registry
.iterfind('./enums'):
77 if ('GL_ALL_ATTRIB_BITS' in fixes
and
78 enums
.get('group') == 'AttribMask'):
79 # The XML defines GL_ALL_ATTRIB_BITS incorrectly with all bits
80 # set (0xFFFFFFFF). From the GL_ARB_multisample spec, v5:
82 # In order to avoid incompatibility with GL implementations
83 # that do not support SGIS_multisample, ALL_ATTRIB_BITS
84 # does not include MULTISAMPLE_BIT_ARB.
86 enum
= enums
.find("./enum[@name='GL_ALL_ATTRIB_BITS']")
87 enum
.set('value', '0x000FFFFF')
89 fixes
.remove('GL_ALL_ATTRIB_BITS')
92 if ('glOcclusionQueryEventMaskAMD' in fixes
and
93 enums
.get('namespace') == 'OcclusionQueryEventMaskAMD'):
94 # This tag's attributes are totally broken.
95 enums
.set('namespace', 'GL')
96 enums
.set('group', 'OcclusionQueryEventMaskAMD')
97 enums
.set('type', 'bitmask')
99 fixes
.remove('glOcclusionQueryEventMaskAMD')
102 if ('gles2_GL_ACTIVE_PROGRAM_EXT' in fixes
and
103 enums
.get('vendor') is not None and
104 enums
.get('vendor') == 'ARB' and
105 enums
.get('start') is not None and
106 enums
.get('start') <= '0x8259' and
107 enums
.get('end') is not None and
108 enums
.get('end') >= '0x8259'):
109 # GL_ACTIVE_PROGRAM_EXT has different numerical values in GL
110 # (0x8B8D) and in GLES (0x8259). Remove the GLES value to avoid
111 # redefinition collisions.
112 bad_enum
= enums
.find(("./enum"
114 "[@name='GL_ACTIVE_PROGRAM_EXT']"
116 defer_removal(enums
, bad_enum
)
118 fixes
.remove('gles2_GL_ACTIVE_PROGRAM_EXT')
121 for (parent
, child
) in remove_queue
:
125 raise Exception('failed to apply some xml repairs: ' +
126 ', '.join(map(repr, fixes
)))
129 class OrderedKeyedSet(object):
130 """A set with keyed elements that preserves order of element insertion.
132 Why waste words? Let's document the class with example code.
135 Cheese = namedtuple('Cheese', ('name', 'flavor'))
136 cheeses = OrderedKeyedSet(key='name')
137 cheeses.add(Cheese(name='cheddar', flavor='good'))
138 cheeses.add(Cheese(name='gouda', flavor='smells like feet'))
139 cheeses.add(Cheese(name='romano', flavor='awesome'))
141 # Elements are retrievable by key.
142 assert cheeses['gouda'].flavor == 'smells like feet'
144 # On key collision, the old element is removed.
145 cheeses.add(Cheese(name='gouda', flavor='ok i guess'))
146 assert cheeses['gouda'].flavor == 'ok i guess'
148 # The set preserves order of insertion. Replacement does not alter
150 assert list(cheeses)[2].name == 'romano'
152 # The set is iterable.
153 for cheese in cheeses:
156 # Yet another example...
157 Bread = namedtuple('Bread', ('name', 'smell'))
158 breads = OrderedKeyedSet(key='name')
159 breads.add(Bread(name='como', smell='subtle')
160 breads.add(Bread(name='sourdough', smell='pleasant'))
162 # The set supports some common set operations, such as union.
163 breads_and_cheeses = breads | cheeses
164 assert len(breads_and_cheeses) == len(breads) + len(cheeses)
167 def __init__(self
, key
, elems
=()):
168 """Create a new set with the given key.
170 The given 'key' defines how to calculate each element's key value. If
171 'key' is a string, then each key value is defined to be
172 `getattr(elem, key)`. If 'key' is a function, then the key value is
176 # A linked list contains the set's items. Each list node is a 4-tuple
177 # [prev, next, key, value]. The root node is permanent.
179 root
[:] = [root
, root
, None, None]
180 self
.__list
_root
= root
182 # For quick retrieval, we map each key to its node. That is, each map
183 # pair has form {key: [prev, next, key, value])}.
186 if isinstance(key
, str):
187 self
.__key
_func
= lambda elem
: getattr(elem
, key
)
189 self
.__key
_func
= key
194 def __or__(self
, other
):
195 """Same as `union`."""
196 return self
.union(other
)
198 def __contains__(self
, key
):
199 return key
in self
.__map
202 return OrderedKeyedSet(key
=deepcopy(self
.__key
_func
),
205 def __getitem__(self
, key
):
206 return self
.__map
[key
][3]
209 return self
.itervalues()
212 return len(self
.__map
)
215 templ
= '{self.__class__.__name__}({self.name!r})'
216 return templ
.format(self
=self
)
218 def add(self
, value
):
219 key
= self
.__key
_func
(value
)
220 node
= self
.__map
.get(key
, None)
224 root
= self
.__list
_root
226 new_tail
= [old_tail
, root
, key
, value
]
227 new_tail
[0][1] = new_tail
228 new_tail
[1][0] = new_tail
229 self
.__map
[key
] = new_tail
233 root
= self
.__list
_root
234 root
[:] = [root
, root
, None, None]
236 def extend(self
, elems
):
240 def get(self
, key
, default
):
241 node
= self
.__map
.get(key
, None)
248 root
= self
.__list
_root
250 while node
is not root
:
251 yield (node
[2], node
[3])
255 return (i
[0] for i
in self
.iteritems())
257 def itervalues(self
):
258 return (i
[1] for i
in self
.iteritems())
261 node
= self
.__map
.pop(key
)
266 def sort_by_key(self
):
267 sorted_items
= sorted(six
.iteritems(self
.__map
))
269 for item
in sorted_items
:
272 def sort_by_value(self
):
273 sorted_values
= sorted(six
.itervalues(self
.__map
))
275 for value
in sorted_values
:
278 def union(self
, other
):
279 """Return the union of two sets as a new set.
281 In the new set, all elements of the self set precede those of the other
282 set. The order of elements in the new set preserves the order of the
285 The new set's key function is copied from self. On key collisions, set
286 y has precedence over x.
293 class ImmutableOrderedKeyedSet(OrderedKeyedSet
):
295 def __init__(self
, key
, elems
):
296 self
.__is
_frozen
= False
297 OrderedKeyedSet
.__init
__(self
, key
=key
, elems
=elems
)
298 self
.__is
_frozen
= True
300 def add(self
, value
):
304 OrderedKeyedSet
.add(self
, value
)
313 class ImmutableError(Exception):
317 # Values that may appear in the XML attributes 'api' and 'supported'.
318 VALID_APIS
= frozenset(('gl', 'glcore', 'gles1', 'gles2'))
321 class Registry(object):
322 """The toplevel <registry> element.
325 features: An OrderedKeyedSet that contains a Feature for each <feature>
328 extensions: An OrderedKeyedSet that contains an Extension for each
329 <extension> subelement.
331 commands: An OrderedKeyedSet that contains a Command for each <command>
334 command_alias_map: A CommandAliasMap that contains a CommandAliasSet
335 for each equivalence class of commands.
337 enum_groups: An OrderedKeyedSet that contains an EnumGroup for each
340 enums: An OrderedKeyedSet that contains an Enum for each <enum>
343 vendor_namespaces: A collection of all vendor prefixes and suffixes,
344 such as "ARB", "EXT", "CHROMIUM", and "NV".
347 def __init__(self
, xml_registry
):
348 """Parse the <registry> element."""
350 assert xml_registry
.tag
== 'registry'
352 self
.command_alias_map
= CommandAliasMap()
353 self
.commands
= OrderedKeyedSet(key
='name')
354 self
.enum_groups
= []
355 self
.enums
= OrderedKeyedSet(key
='name')
356 self
.extensions
= OrderedKeyedSet(key
='name')
357 self
.features
= OrderedKeyedSet(key
='name')
358 self
.vendor_namespaces
= set()
360 for xml_command
in xml_registry
.iterfind('./commands/command'):
361 command
= Command(xml_command
)
362 self
.commands
.add(command
)
363 self
.command_alias_map
.add(command
)
365 for xml_enums
in xml_registry
.iterfind('./enums'):
366 enum_group
= EnumGroup(xml_enums
)
367 self
.enum_groups
.append(enum_group
)
368 for enum
in enum_group
.enums
:
371 for xml_feature
in xml_registry
.iterfind('./feature'):
372 feature
= Feature(xml_feature
, command_map
=self
.commands
,
374 self
.features
.add(feature
)
376 for xml_ext
in xml_registry
.iterfind('./extensions/extension'):
377 ext
= Extension(xml_ext
, command_map
=self
.commands
,
379 self
.extensions
.add(ext
)
380 self
.vendor_namespaces
.add(ext
.vendor_namespace
)
382 self
.vendor_namespaces
.remove(None)
385 @functools.total_ordering
386 class Feature(object):
387 """A <feature> XML element.
390 name: The XML element's 'name' attribute.
392 api: The XML element's 'api' attribute.
394 version_str: The XML element's 'number' attribute. For example, "3.1".
396 version_float: float(version_str)
398 version_int: int(10 * version_float)
400 requirements: A collection of Requirement for each Command and Enum
401 this Feature requires.
404 def __init__(self
, xml_feature
, command_map
, enum_map
):
405 """Parse a <feature> element."""
407 # Example <feature> element:
409 # <feature api="gles2" name="GL_ES_VERSION_3_1" number="3.1">
410 # <!-- arrays_of_arrays features -->
412 # <!-- compute_shader features -->
414 # <command name="glDispatchCompute"/>
415 # <command name="glDispatchComputeIndirect"/>
416 # <enum name="GL_COMPUTE_SHADER"/>
417 # <enum name="GL_MAX_COMPUTE_UNIFORM_BLOCKS"/>
420 # <!-- draw_indirect features -->
422 # <command name="glDrawArraysIndirect"/>
423 # <command name="glDrawElementsIndirect"/>
424 # <enum name="GL_DRAW_INDIRECT_BUFFER"/>
425 # <enum name="GL_DRAW_INDIRECT_BUFFER_BINDING"/>
430 assert xml_feature
.tag
== 'feature'
432 # Parse the <feature> tag's attributes.
433 self
.name
= xml_feature
.get('name')
434 self
.api
= xml_feature
.get('api')
435 self
.is_gles
= self
.name
.startswith('GL_ES')
437 self
.version_str
= xml_feature
.get('number')
438 self
.version_float
= float(self
.version_str
)
439 self
.version_int
= int(10 * self
.version_float
)
441 self
.__parse
_requirements
(xml_feature
, command_map
, enum_map
)
443 assert self
.api
in VALID_APIS
444 assert len(self
.requirements
) > 0
446 def __eq__(self
, other
):
449 elif isinstance(other
, Extension
):
451 elif self
.is_gles
!= other
.is_gles
:
453 return self
.name
== other
.name
455 def __lt__(self
, other
):
456 if isinstance(other
, Extension
):
458 # Desktop GL before GLES
459 elif self
.is_gles
!= other
.is_gles
:
464 return self
.name
< other
.name
467 templ
= '{self.__class__.__name__}({self.name!r})'
468 return templ
.format(self
=self
)
470 def __parse_requirements(self
, xml_feature
, command_map
, enum_map
):
471 """For each <command> and <enum> under a <require>, create
472 a Requirement that links this Feature to a Command or Enum.
474 self
.requirements
= set()
477 req
= Requirement(provider
=self
, provided
=x
,
478 apis
=frozenset((self
.api
,)))
479 self
.requirements
.add(req
)
480 x
.requirements
.add(req
)
482 for xml_cmd
in xml_feature
.iterfind('./require/command'):
483 cmd
= command_map
[xml_cmd
.get('name')]
485 for xml_enum
in xml_feature
.iterfind('./require/enum'):
486 enum
= enum_map
[xml_enum
.get('name')]
490 @functools.total_ordering
491 class Extension(object):
492 """An <extension> XML element.
495 name: The XML element's 'name' attribute.
497 supported_apis: The set of api strings in the XML element's 'supported'
498 attribute. For example, set('gl', 'glcore').
500 vendor_namespace: For example, "AMD". May be None.
502 requirements: A collection of Requirement for each Command and Enum
503 this Extension requires.
506 __VENDOR_REGEX
= re
.compile(r
'^GL_(?P<vendor_namespace>[A-Z]+)_')
507 RATIFIED_NAMESPACES
= ('KHR', 'ARB', 'OES')
509 def __init__(self
, xml_extension
, command_map
, enum_map
):
510 """Parse an <extension> element."""
512 # Example <extension> element:
513 # <extension name="GL_ARB_ES2_compatibility" supported="gl|glcore">
515 # <enum name="GL_FIXED"/>
516 # <enum name="GL_IMPLEMENTATION_COLOR_READ_TYPE"/>
518 # <command name="glReleaseShaderCompiler"/>
519 # <command name="glShaderBinary"/>
524 assert xml_extension
.tag
== 'extension'
526 self
.name
= xml_extension
.get('name')
528 self
.vendor_namespace
= None
529 match
= Extension
.__VENDOR
_REGEX
.match(self
.name
)
530 if match
is not None:
531 groups
= match
.groupdict()
532 self
.vendor_namespace
= groups
.get('vendor_namespace', None)
534 self
.supported_apis
= xml_extension
.get('supported').split('|')
535 self
.supported_apis
= frozenset(self
.supported_apis
)
536 assert self
.supported_apis
<= VALID_APIS
538 self
.__parse
_requirements
(xml_extension
, command_map
, enum_map
)
540 def __eq__(self
, other
):
543 elif isinstance(other
, Feature
):
545 elif self
.is_ratified
!= other
.is_ratified
:
547 elif self
.vendor_namespace
== 'EXT' != other
.vendor_namespace
== 'EXT':
549 return self
.name
== other
.name
551 def __lt__(self
, other
):
552 if isinstance(other
, Feature
):
554 elif self
.is_ratified
!= other
.is_ratified
:
555 # sort ratified before unratified
560 elif (other
.vendor_namespace
== 'EXT') != \
561 (self
.vendor_namespace
== 'EXT'):
562 # Sort EXT before others
563 if self
.vendor_namespace
== 'EXT':
567 return self
.name
< other
.name
570 templ
= '{self.__class__.__name__}(name={self.name!r})'
571 return templ
.format(self
=self
)
574 def is_ratified(self
):
575 """True if the vendor namespace is one that traditionally requires
576 ratification by Khronos.
578 return self
.vendor_namespace
in self
.RATIFIED_NAMESPACES
580 def __parse_requirements(self
, xml_extension
, command_map
, enum_map
):
581 """For each <command> and <enum> under a <require>, create
582 a Requirement that links this Extension to a Command or Enum.
584 self
.requirements
= set()
586 def link(xml_require
, x
):
587 api
= xml_require
.get('api', None)
589 assert api
in self
.supported_apis
590 apis
= frozenset((api
,))
592 apis
= frozenset(self
.supported_apis
)
594 req
= Requirement(provider
=self
, provided
=x
, apis
=apis
)
595 self
.requirements
.add(req
)
596 x
.requirements
.add(req
)
598 for xml_req
in xml_extension
.iterfind('./require'):
599 for xml_cmd
in xml_req
.iterfind('./command'):
600 cmd
= command_map
[xml_cmd
.get('name')]
602 for xml_enum
in xml_req
.iterfind('./enum'):
603 enum
= enum_map
[xml_enum
.get('name')]
607 @functools.total_ordering
608 class Requirement(object):
609 """A <require> XML element, which links a provider (Feature or Extension)
610 to a provided (Command or Enum) for a set of apis.
613 def __init__(self
, provider
, provided
, apis
):
614 self
.provider
= provider
615 self
.provided
= provided
616 self
.apis
= frozenset(apis
)
618 def choose_if(condition
, obj
):
624 self
.has_feature
= isinstance(provider
, Feature
)
625 self
.has_extension
= isinstance(provider
, Extension
)
626 self
.has_command
= isinstance(provided
, Command
)
627 self
.has_enum
= isinstance(provided
, Enum
)
629 self
.feature
= choose_if(self
.has_feature
, self
.provider
)
630 self
.extension
= choose_if(self
.has_extension
, self
.provider
)
631 self
.command
= choose_if(self
.has_command
, self
.provided
)
632 self
.enum
= choose_if(self
.has_enum
, self
.provided
)
634 assert self
.has_feature
+ self
.has_extension
== 1
635 assert self
.has_command
+ self
.has_enum
== 1
636 assert self
.apis
<= VALID_APIS
638 _log_debug('created {0}'.format(self
))
641 return hash('__Requirement_class_{}_{}_'.format(
642 self
.provider
, self
.provided
))
644 def __eq__(self
, other
):
645 if self
.provider
!= other
.provider
:
647 elif self
.provided
!= other
.provided
:
651 def __lt__(self
, other
):
652 if self
.provider
< other
.provider
:
654 elif self
.provided
< other
.provided
:
659 templ
= ('{self.__class__.__name__}'
660 '(provider={self.provider.name!r},'
661 ' provided={self.provided.name!r},'
662 ' apis={api_tuple})')
663 return templ
.format(self
=self
, api_tuple
=tuple(self
.apis
))
666 class CommandParam(object):
667 """A <param> XML element at path command/param.
675 __PARAM_NAME_FIXES
= {'near': 'hither', 'far': 'yon'}
677 def __init__(self
, xml_param
, log
=None):
678 """Parse a <param> element."""
680 # Example <param> elements:
682 # <param>const <ptype>GLchar</ptype> *<name>name</name></param>
683 # <param len="1"><ptype>GLsizei</ptype> *<name>length</name></param>
684 # <param len="bufSize">
685 # <ptype>GLint</ptype> *<name>values</name>
687 # <param><ptype>GLenum</ptype> <name>shadertype</name></param>
688 # <param group="sync">
689 # <ptype>GLsync</ptype> <name>sync</name>
691 # <param><ptype>GLuint</ptype> <name>baseAndCount</name>[2]</param>
693 assert xml_param
.tag
== 'param'
695 self
.name
= xml_param
.find('./name').text
697 # Rename the parameter if its name is a reserved keyword in MSVC.
698 self
.name
= self
.__PARAM
_NAME
_FIXES
.get(self
.name
, self
.name
)
701 c_type_text
= list(xml_param
.itertext())
703 # Could be <name> or <array_suffix>
704 c_type_text_end
= c_type_text
.pop(-1)
706 # We popped off <array_suffix>
707 if c_type_text_end
.startswith('['):
708 # This is an array variable.
709 self
.array_suffix
= c_type_text_end
710 c_type_text
.pop(-1) # Pop off the next one (<name>)
712 self
.array_suffix
= ''
713 c_type_text
= (t
.strip() for t
in c_type_text
)
714 self
.c_type
= ' '.join(c_type_text
).strip()
716 _log_debug('parsed {0}'.format(self
))
719 templ
= ('{self.__class__.__name__}'
720 '(name={self.name!r}, type={self.c_type!r}, '
721 'suffix={self.array_suffix!r})')
722 return templ
.format(self
=self
)
725 @functools.total_ordering
726 class Command(object):
727 """A <command> XML element.
730 name: The XML element's 'name' attribute, which is also the function
733 c_return_type: For example, "void *".
735 alias: The XML element's 'alias' element. May be None.
737 param_list: List of that contains a CommandParam for each <param>
740 requirements: A collection of each Requirement that exposes this
744 def __init__(self
, xml_command
):
745 """Parse a <command> element."""
747 # Example <command> element:
750 # <proto>void <name>glTexSubImage2D</name></proto>
751 # <param group="TextureTarget">
752 # <ptype>GLenum</ptype> <name>target</name>
754 # <param group="CheckedInt32">
755 # <ptype>GLint</ptype> <name>level</name>
757 # <param group="CheckedInt32">
758 # <ptype>GLint</ptype> <name>xoffset</name>
760 # <param group="CheckedInt32">
761 # <ptype>GLint</ptype> <name>yoffset</name>
763 # <param><ptype>GLsizei</ptype> <name>width</name></param>
764 # <param><ptype>GLsizei</ptype> <name>height</name></param>
765 # <param group="PixelFormat">
766 # <ptype>GLenum</ptype> <name>format</name>
768 # <param group="PixelType">
769 # <ptype>GLenum</ptype> <name>type</name>
771 # <param len="COMPSIZE(format,type,width,height)">const void *
772 # <name>pixels</name>
774 # <glx type="render" opcode="4100"/>
775 # <glx type="render" opcode="332" name="glTexSubImage2DPBO"
776 # comment="PBO protocol"/>
780 assert xml_command
.tag
== 'command'
781 xml_proto
= xml_command
.find('./proto')
782 self
.name
= xml_proto
.find('./name').text
783 _log_debug('start parsing Command(name={0!r})'.format(self
.name
))
785 self
.requirements
= set()
786 self
.__vendor
_namespace
= None
788 # Parse the return type from the <proto> element.
790 # Example of a difficult <proto> element:
791 # <proto group="String">const <ptype>GLubyte</ptype> *
792 # <name>glGetStringi</name>
794 c_return_type_text
= list(xml_proto
.itertext())
795 # Pop off the text from the <name> subelement.
796 c_return_type_text
.pop(-1)
797 c_return_type_text
= (t
.strip() for t
in c_return_type_text
)
798 self
.c_return_type
= ' '.join(c_return_type_text
).strip()
800 # Parse alias info, if any.
801 xml_alias
= xml_command
.find('./alias')
802 if xml_alias
is None:
805 self
.alias
= xml_alias
.get('name')
808 CommandParam(xml_param
)
809 for xml_param
in xml_command
.iterfind('./param')
812 _log_debug(('parsed {self.__class__.__name__}('
813 'name={self.name!r}, '
814 'alias={self.alias!r}, '
815 'prototype={self.c_prototype!r})').format(self
=self
))
818 return hash('__Command_class_{}_'.format(self
.name
))
820 def __eq__(self
, other
):
821 return self
.name
== other
.name
823 def __lt__(self
, other
):
824 return self
.name
< other
.name
827 templ
= '{self.__class__.__name__}({self.name!r})'
828 return templ
.format(self
=self
)
831 def vendor_namespace(self
):
832 if self
.__vendor
_namespace
is None:
833 for req
in self
.requirements
:
838 if ext
.vendor_namespace
is None:
841 if self
.name
.endswith('_' + ext
.vendor_namespace
):
842 self
.__vendor
_namespace
= ext
.vendor_namespace
844 return self
.__vendor
_namespace
847 def c_prototype(self
):
848 """For example, "void glAccum(GLenum o, GLfloat value)"."""
849 return '{self.c_return_type} {self.name}({self.c_named_param_list})'\
853 def c_funcptr_typedef(self
):
854 """For example, "PFNGLACCUMROC" for glAccum."""
855 return 'PFN{0}PROC'.format(self
.name
).upper()
858 def c_named_param_list(self
):
859 """For example, "GLenum op, GLfloat value" for glAccum."""
861 '{p.c_type} {p.name}{p.array_suffix}'.format(p
=param
)
862 for param
in self
.param_list
866 def c_unnamed_param_list(self
):
867 """For example, "GLenum, GLfloat" for glAccum."""
869 '{param.c_type}{param.array_suffix}'.format(param
=param
)
870 for param
in self
.param_list
874 def c_untyped_param_list(self
):
875 """For example, "op, value" for glAccum."""
878 for param
in self
.param_list
882 @functools.total_ordering
883 class CommandAliasSet(ImmutableOrderedKeyedSet
):
885 def __init__(self
, commands
):
886 ImmutableOrderedKeyedSet
.__init
__(self
, key
='name',
887 elems
=sorted(commands
))
888 self
.__primary
_command
= None
889 self
.__requirements
= None
891 def __eq__(self
, other
):
892 return self
.name
== other
.name
894 def __lt__(self
, other
):
895 return self
.name
< other
.name
898 templ
= '{self.__class__.__name__}({self.name!r})'
899 return templ
.format(self
=self
)
902 return hash(repr(self
))
906 return self
.primary_command
.name
909 def primary_command(self
):
910 """The set's first command when sorted by name."""
915 def requirements(self
):
916 """A sorted iterator over each Requirement that exposes this
919 if self
.__requirements
is None:
920 self
.__requirements
= sorted(
923 for req
in command
.requirements
925 _log_debug('{0} sorted requirements: {1}'.format(
926 self
, self
.__requirements
))
928 return iter(self
.__requirements
)
931 class CommandAliasMap(object):
935 self
.__sorted
_unique
_values
= None
937 def __getitem__(self
, command_name
):
938 return self
.__map
[command_name
]
941 """A sorted iterator over the map's unique CommandAliasSet values."""
942 if self
.__sorted
_unique
_values
is None:
943 self
.__sorted
_unique
_values
= \
944 sorted(set(six
.itervalues(self
.__map
)))
946 return iter(self
.__sorted
_unique
_values
)
948 def get(self
, command_name
, default
):
949 return self
.__map
.get(command_name
, default
)
951 def add(self
, command
):
952 assert isinstance(command
, Command
)
954 _log_debug('adding command {0!r} to CommandAliasMap'.format(name
))
956 name_set
= self
.get(name
, None)
957 assert self
.__is
_set
_mapping
_complete
(name_set
)
959 alias_set
= self
.get(command
.alias
, None)
960 assert self
.__is
_set
_mapping
_complete
(alias_set
)
962 if name_set
is alias_set
and name_set
is not None:
965 # After modifying the contained alias sets, the mapping will no longer
967 self
.__sorted
_unique
_values
= None
969 new_set_elems
= set((command
,))
970 if name_set
is not None:
971 new_set_elems
.update(name_set
)
972 if alias_set
is not None:
973 new_set_elems
.update(alias_set
)
975 new_set
= CommandAliasSet(new_set_elems
)
976 for other_command
in new_set
:
977 self
.__map
[other_command
.name
] = new_set
978 if other_command
.alias
is not None:
979 self
.__map
[other_command
.alias
] = new_set
981 def __is_set_mapping_complete(self
, alias_set
):
982 if alias_set
is None:
985 for command
in alias_set
:
986 if self
[command
.name
] is not alias_set
:
988 if command
.alias
is None:
990 if self
[command
.alias
] is not alias_set
:
996 class EnumGroup(object):
997 """An <enums> element at path registry/enums.
1000 name: The XML element's 'group' attribute. If the XML does not define
1001 'group', then this class invents one.
1003 type: The XML element's 'type' attribute. If the XML does not define
1004 'type', then this class invents one.
1006 start, end: The XML element's 'start' and 'end' attributes. Each may be
1009 enums: An OrderedKeyedSet of Enum that contains each <enum> subelement
1013 # Each EnumGroup belongs to exactly one member of EnumGroup.TYPES.
1015 # Some members in EnumGroup.TYPES are invented and not present in gl.xml.
1016 # The only enum type defined explicitly in gl.xml is "bitmask", which
1017 # occurs as <enums type="bitmask">. However, in gl.xml each block of
1018 # non-bitmask enums is introduced by a comment that describes the block's
1019 # "type", even if the <enums> tag lacks a 'type' attribute. (Thanks,
1020 # Khronos, for encoding data in XML comments rather than the XML itself).
1021 # EnumGroup.TYPES lists such implicit comment-only types, with invented
1022 # names, alongside the types explicitly defined by <enums type=>.
1024 # Type 'default_namespace' is self-explanatory. It indicates the large
1025 # set of enums from 0x0000 to 0xffff that includes, for example,
1026 # GL_POINTS and GL_TEXTURE_2D.
1027 'default_namespace',
1029 # Type 'bitmask' is self-explanatory.
1032 # Type 'small_index' indicates a small namespace of non-bitmask enums.
1033 # As of Khronos revision 26792, 'small_index' groups generally contain
1034 # small numbers used for indexed access.
1037 # Type 'special' is used only for the group named "SpecialNumbers". The
1038 # group contains enums such as GL_FALSE, GL_ZERO, and GL_INVALID_INDEX.
1042 def __init__(self
, xml_enums
):
1043 """Parse an <enums> element."""
1045 # Example of a bitmask group:
1047 # <enums namespace="GL" group="SyncObjectMask" type="bitmask">
1048 # <enum value="0x00000001"
1049 # name="GL_SYNC_FLUSH_COMMANDS_BIT"/>
1050 # <enum value="0x00000001"
1051 # name="GL_SYNC_FLUSH_COMMANDS_BIT_APPLE"/>
1054 # Example of a group that resides in OpenGL's default enum namespace:
1056 # <enums namespace="GL" start="0x0000" end="0x7FFF" vendor="ARB"
1058 # <enum value="0x0000" name="GL_POINTS"/>
1059 # <enum value="0x0001" name="GL_LINES"/>
1060 # <enum value="0x0002" name="GL_LINE_LOOP"/>
1064 # Example of a non-bitmask group that resides outside OpenGL's default
1067 # <enums namespace="GL" group="PathRenderingTokenNV" vendor="NV">
1068 # <enum value="0x00" name="GL_CLOSE_PATH_NV"/>
1069 # <enum value="0x02" name="GL_MOVE_TO_NV"/>
1070 # <enum value="0x03" name="GL_RELATIVE_MOVE_TO_NV"/>
1075 self
.name
= xml_enums
.get('group', None)
1076 _log_debug('start parsing {0}'.format(self
))
1078 self
.type = xml_enums
.get('type', None)
1079 self
.start
= xml_enums
.get('start', None)
1080 self
.end
= xml_enums
.get('end', None)
1083 self
.__invent
_name
_and
_type
()
1084 assert self
.name
is not None
1085 assert self
.type in self
.TYPES
1087 _log_debug('start parsing <enum> subelements of {0}'.format(self
))
1088 self
.enums
= OrderedKeyedSet(key
='name')
1089 for xml_enum
in xml_enums
.iterfind('./enum'):
1090 self
.enums
.add(Enum(self
, xml_enum
))
1091 _log_debug('parsed {0}'.format(self
))
1094 templ
= '{self.__class__.__name__}({self.name!r})'
1095 return templ
.format(self
=self
)
1097 def __invent_name_and_type(self
):
1098 """If the XML didn't define a name or type, invent one."""
1099 if self
.name
is None:
1100 assert self
.type is None
1101 assert self
.start
is not None
1102 assert self
.end
is not None
1103 self
.name
= 'range_{self.start}_{self.end}'.format(self
=self
)
1104 self
.type = 'default_namespace'
1105 elif self
.type is None:
1106 self
.type = 'small_index'
1107 elif self
.name
== 'SpecialNumbers':
1108 assert self
.type is None
1109 self
.type = 'special'
1112 @functools.total_ordering
1114 """An <enum> XML element.
1117 name, api: The XML element's 'name' and 'api' attributes.
1119 str_value, c_num_literal: Equivalent attributes. The XML element's
1122 num_value: The long integer for str_value.
1124 requirements: A collection of each Requirement that exposes this Enum.
1127 def __init__(self
, enum_group
, xml_enum
):
1128 """Parse an <enum> tag located at path registry/enums/enum."""
1130 # Example <enum> element:
1131 # <enum value="0x0000" name="GL_POINTS"/>
1133 assert isinstance(enum_group
, EnumGroup
)
1134 assert xml_enum
.tag
== 'enum'
1136 self
.requirements
= set()
1137 self
.__vendor
_namespace
= None
1139 self
.group
= enum_group
1140 self
.name
= xml_enum
.get('name')
1141 self
.api
= xml_enum
.get('api')
1142 self
.str_value
= xml_enum
.get('value')
1143 self
.c_num_literal
= self
.str_value
1145 if '0x' in self
.str_value
.lower():
1151 # long is undefined in python3, and we are aware of that
1152 # pylint: disable=undefined-variable
1153 self
.num_value
= long(self
.str_value
, base
)
1156 self
.num_value
= int(self
.str_value
, base
)
1158 _log_debug('parsed {0}'.format(self
))
1161 templ
= ('{self.__class__.__name__}'
1162 '(name={self.name!r},'
1163 ' value={self.str_value!r})')
1164 return templ
.format(self
=self
)
1166 def __eq__(self
, other
):
1167 if self
.num_value
!= other
.num_value
:
1169 elif (self
.vendor_namespace
is None) != \
1170 (other
.vendor_namespace
is None):
1172 elif (self
.vendor_namespace
in Extension
.RATIFIED_NAMESPACES
) != \
1173 (other
.vendor_namespace
in Extension
.RATIFIED_NAMESPACES
):
1175 elif (self
.vendor_namespace
== 'EXT') != \
1176 (other
.vendor_namespace
== 'EXT'):
1178 elif self
.name
!= other
.name
:
1180 return self
.api
== other
.api
1182 def __lt__(self
, other
): # pylint: disable=too-many-return-statements
1185 Sort by numerical value, then vendor_namspace (ratified first, then
1186 EXT), then by full name, and finally by api.
1188 This sort order ensures that names provided by core specifications
1189 precede those provided by ratified extensions, which proceed those
1190 provided by unratified extensions.
1192 For example: GL_RED < GL_RED_EXT < GL_RED_INTEL
1195 if self
.num_value
!= other
.num_value
:
1196 if self
.num_value
< other
.num_value
:
1200 x
= self
.vendor_namespace
is None
1201 y
= other
.vendor_namespace
is None
1207 x
= self
.vendor_namespace
in Extension
.RATIFIED_NAMESPACES
1208 y
= other
.vendor_namespace
in Extension
.RATIFIED_NAMESPACES
1214 x
= self
.vendor_namespace
== 'EXT'
1215 y
= other
.vendor_namespace
== 'EXT'
1221 if self
.name
!= other
.name
:
1222 if self
.name
< other
.name
:
1226 return self
.api
< other
.api
1229 def vendor_namespace(self
):
1230 if self
.__vendor
_namespace
is None:
1231 for req
in self
.requirements
:
1236 if ext
.vendor_namespace
is None:
1239 if self
.name
.endswith('_' + ext
.vendor_namespace
):
1240 self
.__vendor
_namespace
= ext
.vendor_namespace
1242 return self
.__vendor
_namespace