3 # (C) Copyright 2015, NVIDIA CORPORATION.
6 # Permission is hereby granted, free of charge, to any person obtaining a
7 # copy of this software and associated documentation files (the "Software"),
8 # to deal in the Software without restriction, including without limitation
9 # on the rights to use, copy, modify, merge, publish, distribute, sub
10 # license, and/or sell copies of the Software, and to permit persons to whom
11 # the Software is furnished to do so, subject to the following conditions:
13 # The above copyright notice and this permission notice (including the next
14 # paragraph) shall be included in all copies or substantial portions of the
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
20 # IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
26 # Kyle Brenneman <kbrenneman@nvidia.com>
31 import xml
.etree
.cElementTree
as etree
34 GLAPI
= os
.path
.join(os
.path
.dirname(__file__
), "..", "glapi", "gen")
35 sys
.path
.insert(0, GLAPI
)
38 MAPI_TABLE_NUM_DYNAMIC
= 4096
40 _LIBRARY_FEATURE_NAMES
= {
41 # libGL and libGLdiapatch both include every function.
44 "opengl" : frozenset(( "GL_VERSION_1_0", "GL_VERSION_1_1",
45 "GL_VERSION_1_2", "GL_VERSION_1_3", "GL_VERSION_1_4", "GL_VERSION_1_5",
46 "GL_VERSION_2_0", "GL_VERSION_2_1", "GL_VERSION_3_0", "GL_VERSION_3_1",
47 "GL_VERSION_3_2", "GL_VERSION_3_3", "GL_VERSION_4_0", "GL_VERSION_4_1",
48 "GL_VERSION_4_2", "GL_VERSION_4_3", "GL_VERSION_4_4", "GL_VERSION_4_5",
50 "glesv1" : frozenset(("GL_VERSION_ES_CM_1_0", "GL_OES_point_size_array")),
51 "glesv2" : frozenset(("GL_ES_VERSION_2_0", "GL_ES_VERSION_3_0",
52 "GL_ES_VERSION_3_1", "GL_ES_VERSION_3_2",
56 def getFunctions(xmlFiles
):
58 Reads an XML file and returns all of the functions defined in it.
60 xmlFile should be the path to Khronos's gl.xml file. The return value is a
61 sequence of FunctionDesc objects, ordered by slot number.
63 roots
= [ etree
.parse(xmlFile
).getroot() for xmlFile
in xmlFiles
]
64 return getFunctionsFromRoots(roots
)
66 def getFunctionsFromRoots(roots
):
69 for func
in _getFunctionList(root
):
70 functions
[func
.name
] = func
71 functions
= functions
.values()
73 # Sort the function list by name.
74 functions
= sorted(functions
, key
=lambda f
: f
.name
)
76 # Lookup for fixed offset/slot functions and use it if available.
77 # Assign a slot number to each function. This isn't strictly necessary,
78 # since you can just look at the index in the list, but it makes it easier
79 # to include the slot when formatting output.
82 for i
in range(len(functions
)):
83 name
= functions
[i
].name
[2:]
85 if name
in static_data
.offsets
:
86 functions
[i
] = functions
[i
]._replace
(slot
=static_data
.offsets
[name
])
87 elif not name
.endswith("ARB") and name
+ "ARB" in static_data
.offsets
:
88 functions
[i
] = functions
[i
]._replace
(slot
=static_data
.offsets
[name
+ "ARB"])
89 elif not name
.endswith("EXT") and name
+ "EXT" in static_data
.offsets
:
90 functions
[i
] = functions
[i
]._replace
(slot
=static_data
.offsets
[name
+ "EXT"])
92 functions
[i
] = functions
[i
]._replace
(slot
=next_slot
)
95 # Sort the function list by slot.... to simplify the diff
96 functions
= sorted(functions
, key
=lambda f
: f
.slot
)
100 def getExportNamesFromRoots(target
, roots
):
102 Goes through the <feature> tags from gl.xml and returns a set of OpenGL
103 functions that a library should export.
105 target should be one of "gl", "gldispatch", "opengl", "glesv1", or
108 featureNames
= _LIBRARY_FEATURE_NAMES
[target
]
109 if featureNames
is None:
110 return set(func
.name
for func
in getFunctionsFromRoots(roots
))
115 for featElem
in root
.findall("feature"):
116 if featElem
.get("name") in featureNames
:
117 features
.append(featElem
)
118 for featElem
in root
.findall("extensions/extension"):
119 if featElem
.get("name") in featureNames
:
120 features
.append(featElem
)
121 for featElem
in features
:
122 for commandElem
in featElem
.findall("require/command"):
123 names
.add(commandElem
.get("name"))
126 class FunctionArg(collections
.namedtuple("FunctionArg", "type name")):
130 Returns a "TYPE NAME" string, suitable for a function prototype.
133 if not rv
.endswith("*"):
138 class FunctionDesc(collections
.namedtuple("FunctionDesc", "name rt args slot")):
141 Returns true if the function returns a value.
143 return (self
.rt
!= "void")
148 Returns a string with the types and names of the arguments, as you
149 would use in a function declaration.
154 return ", ".join(arg
.dec
for arg
in self
.args
)
159 Returns a string with the names of the arguments, as you would use in a
162 return ", ".join(arg
.name
for arg
in self
.args
)
166 assert self
.name
.startswith("gl")
169 def _getFunctionList(root
):
170 for elem
in root
.findall("commands/command"):
171 yield _parseCommandElem(elem
)
173 def _parseCommandElem(elem
):
174 protoElem
= elem
.find("proto")
175 (rt
, name
) = _parseProtoElem(protoElem
)
178 for ch
in elem
.findall("param"):
179 # <param> tags have the same format as a <proto> tag.
180 args
.append(FunctionArg(*_parseProtoElem(ch
)))
181 func
= FunctionDesc(name
, rt
, tuple(args
), slot
=None)
185 def _parseProtoElem(elem
):
186 # If I just remove the tags and string the text together, I'll get valid C code.
187 text
= _flattenText(elem
)
189 m
= re
.match(r
"^(.+)\b(\w+)(?:\s*\[\s*(\d*)\s*\])?$", text
, re
.S
)
191 typename
= _fixupTypeName(m
.group(1))
194 # HACK: glPathGlyphIndexRangeNV defines an argument like this:
195 # GLuint baseAndCount[2]
196 # Convert it to a pointer and hope for the best.
198 return (typename
, name
)
200 raise ValueError("Can't parse element %r -> %r" % (elem
, text
))
202 def _flattenText(elem
):
204 Returns the text in an element and all child elements, with the tags
208 if elem
.text
is not None:
211 text
+= _flattenText(ch
)
212 if ch
.tail
is not None:
216 def _fixupTypeName(typeName
):
218 Converts a typename into a more consistent format.
221 rv
= typeName
.strip()
223 # Replace "GLvoid" with just plain "void".
224 rv
= re
.sub(r
"\bGLvoid\b", "void", rv
)
226 # Remove the vendor suffixes from types that have a suffix-less version.
227 rv
= re
.sub(r
"\b(GLhalf|GLintptr|GLsizeiptr|GLint64|GLuint64)(?:ARB|EXT|NV|ATI)\b", r
"\1", rv
)
229 rv
= re
.sub(r
"\bGLvoid\b", "void", rv
)
231 # Clear out any leading and trailing whitespace.
234 # Remove any whitespace before a '*'
235 rv
= re
.sub(r
"\s+\*", r
"*", rv
)
237 # Change "foo*" to "foo *"
238 rv
= re
.sub(r
"([^\*])\*", r
"\1 *", rv
)
240 # Condense all whitespace into a single space.
241 rv
= re
.sub(r
"\s+", " ", rv
)