17ec681f3Smrg#!/usr/bin/env python3 2a8bb7a65Smaya 3a8bb7a65Smaya# (C) Copyright 2015, NVIDIA CORPORATION. 4a8bb7a65Smaya# All Rights Reserved. 5a8bb7a65Smaya# 6a8bb7a65Smaya# Permission is hereby granted, free of charge, to any person obtaining a 7a8bb7a65Smaya# copy of this software and associated documentation files (the "Software"), 8a8bb7a65Smaya# to deal in the Software without restriction, including without limitation 9a8bb7a65Smaya# on the rights to use, copy, modify, merge, publish, distribute, sub 10a8bb7a65Smaya# license, and/or sell copies of the Software, and to permit persons to whom 11a8bb7a65Smaya# the Software is furnished to do so, subject to the following conditions: 12a8bb7a65Smaya# 13a8bb7a65Smaya# The above copyright notice and this permission notice (including the next 14a8bb7a65Smaya# paragraph) shall be included in all copies or substantial portions of the 15a8bb7a65Smaya# Software. 16a8bb7a65Smaya# 17a8bb7a65Smaya# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18a8bb7a65Smaya# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19a8bb7a65Smaya# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 20a8bb7a65Smaya# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21a8bb7a65Smaya# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22a8bb7a65Smaya# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23a8bb7a65Smaya# IN THE SOFTWARE. 24a8bb7a65Smaya# 25a8bb7a65Smaya# Authors: 26a8bb7a65Smaya# Kyle Brenneman <kbrenneman@nvidia.com> 27a8bb7a65Smaya 28a8bb7a65Smayaimport collections 29a8bb7a65Smayaimport re 30a8bb7a65Smayaimport sys 317ec681f3Smrgimport xml.etree.ElementTree as etree 32a8bb7a65Smaya 33a8bb7a65Smayaimport os 34a8bb7a65SmayaGLAPI = os.path.join(os.path.dirname(__file__), "..", "glapi", "gen") 35a8bb7a65Smayasys.path.insert(0, GLAPI) 36a8bb7a65Smayaimport static_data 37a8bb7a65Smaya 38a8bb7a65SmayaMAPI_TABLE_NUM_DYNAMIC = 4096 39a8bb7a65Smaya 40a8bb7a65Smaya_LIBRARY_FEATURE_NAMES = { 41a8bb7a65Smaya # libGL and libGLdiapatch both include every function. 42a8bb7a65Smaya "gl" : None, 43a8bb7a65Smaya "gldispatch" : None, 44a8bb7a65Smaya "opengl" : frozenset(( "GL_VERSION_1_0", "GL_VERSION_1_1", 45a8bb7a65Smaya "GL_VERSION_1_2", "GL_VERSION_1_3", "GL_VERSION_1_4", "GL_VERSION_1_5", 46a8bb7a65Smaya "GL_VERSION_2_0", "GL_VERSION_2_1", "GL_VERSION_3_0", "GL_VERSION_3_1", 47a8bb7a65Smaya "GL_VERSION_3_2", "GL_VERSION_3_3", "GL_VERSION_4_0", "GL_VERSION_4_1", 48a8bb7a65Smaya "GL_VERSION_4_2", "GL_VERSION_4_3", "GL_VERSION_4_4", "GL_VERSION_4_5", 49a8bb7a65Smaya )), 50a8bb7a65Smaya "glesv1" : frozenset(("GL_VERSION_ES_CM_1_0", "GL_OES_point_size_array")), 51a8bb7a65Smaya "glesv2" : frozenset(("GL_ES_VERSION_2_0", "GL_ES_VERSION_3_0", 52a8bb7a65Smaya "GL_ES_VERSION_3_1", "GL_ES_VERSION_3_2", 53a8bb7a65Smaya )), 54a8bb7a65Smaya} 55a8bb7a65Smaya 56a8bb7a65Smayadef getFunctions(xmlFiles): 57a8bb7a65Smaya """ 58a8bb7a65Smaya Reads an XML file and returns all of the functions defined in it. 59a8bb7a65Smaya 60a8bb7a65Smaya xmlFile should be the path to Khronos's gl.xml file. The return value is a 61a8bb7a65Smaya sequence of FunctionDesc objects, ordered by slot number. 62a8bb7a65Smaya """ 63a8bb7a65Smaya roots = [ etree.parse(xmlFile).getroot() for xmlFile in xmlFiles ] 64a8bb7a65Smaya return getFunctionsFromRoots(roots) 65a8bb7a65Smaya 66a8bb7a65Smayadef getFunctionsFromRoots(roots): 67a8bb7a65Smaya functions = {} 68a8bb7a65Smaya for root in roots: 69a8bb7a65Smaya for func in _getFunctionList(root): 70a8bb7a65Smaya functions[func.name] = func 71a8bb7a65Smaya functions = functions.values() 72a8bb7a65Smaya 73a8bb7a65Smaya # Sort the function list by name. 74a8bb7a65Smaya functions = sorted(functions, key=lambda f: f.name) 75a8bb7a65Smaya 76a8bb7a65Smaya # Lookup for fixed offset/slot functions and use it if available. 77a8bb7a65Smaya # Assign a slot number to each function. This isn't strictly necessary, 78a8bb7a65Smaya # since you can just look at the index in the list, but it makes it easier 79a8bb7a65Smaya # to include the slot when formatting output. 80a8bb7a65Smaya 81a8bb7a65Smaya next_slot = 0 82a8bb7a65Smaya for i in range(len(functions)): 83a8bb7a65Smaya name = functions[i].name[2:] 84a8bb7a65Smaya 85a8bb7a65Smaya if name in static_data.offsets: 86a8bb7a65Smaya functions[i] = functions[i]._replace(slot=static_data.offsets[name]) 87a8bb7a65Smaya elif not name.endswith("ARB") and name + "ARB" in static_data.offsets: 88a8bb7a65Smaya functions[i] = functions[i]._replace(slot=static_data.offsets[name + "ARB"]) 89a8bb7a65Smaya elif not name.endswith("EXT") and name + "EXT" in static_data.offsets: 90a8bb7a65Smaya functions[i] = functions[i]._replace(slot=static_data.offsets[name + "EXT"]) 91a8bb7a65Smaya else: 92a8bb7a65Smaya functions[i] = functions[i]._replace(slot=next_slot) 93a8bb7a65Smaya next_slot += 1 94a8bb7a65Smaya 95a8bb7a65Smaya return functions 96a8bb7a65Smaya 97a8bb7a65Smayadef getExportNamesFromRoots(target, roots): 98a8bb7a65Smaya """ 99a8bb7a65Smaya Goes through the <feature> tags from gl.xml and returns a set of OpenGL 100a8bb7a65Smaya functions that a library should export. 101a8bb7a65Smaya 102a8bb7a65Smaya target should be one of "gl", "gldispatch", "opengl", "glesv1", or 103a8bb7a65Smaya "glesv2". 104a8bb7a65Smaya """ 105a8bb7a65Smaya featureNames = _LIBRARY_FEATURE_NAMES[target] 106a8bb7a65Smaya if featureNames is None: 107a8bb7a65Smaya return set(func.name for func in getFunctionsFromRoots(roots)) 108a8bb7a65Smaya 109a8bb7a65Smaya names = set() 110a8bb7a65Smaya for root in roots: 111a8bb7a65Smaya features = [] 112a8bb7a65Smaya for featElem in root.findall("feature"): 113a8bb7a65Smaya if featElem.get("name") in featureNames: 114a8bb7a65Smaya features.append(featElem) 115a8bb7a65Smaya for featElem in root.findall("extensions/extension"): 116a8bb7a65Smaya if featElem.get("name") in featureNames: 117a8bb7a65Smaya features.append(featElem) 118a8bb7a65Smaya for featElem in features: 119a8bb7a65Smaya for commandElem in featElem.findall("require/command"): 120a8bb7a65Smaya names.add(commandElem.get("name")) 121a8bb7a65Smaya return names 122a8bb7a65Smaya 123a8bb7a65Smayaclass FunctionArg(collections.namedtuple("FunctionArg", "type name")): 124a8bb7a65Smaya @property 125a8bb7a65Smaya def dec(self): 126a8bb7a65Smaya """ 127a8bb7a65Smaya Returns a "TYPE NAME" string, suitable for a function prototype. 128a8bb7a65Smaya """ 129a8bb7a65Smaya rv = str(self.type) 130a8bb7a65Smaya if not rv.endswith("*"): 131a8bb7a65Smaya rv += " " 132a8bb7a65Smaya rv += self.name 133a8bb7a65Smaya return rv 134a8bb7a65Smaya 135a8bb7a65Smayaclass FunctionDesc(collections.namedtuple("FunctionDesc", "name rt args slot")): 136a8bb7a65Smaya def hasReturn(self): 137a8bb7a65Smaya """ 138a8bb7a65Smaya Returns true if the function returns a value. 139a8bb7a65Smaya """ 140a8bb7a65Smaya return (self.rt != "void") 141a8bb7a65Smaya 142a8bb7a65Smaya @property 143a8bb7a65Smaya def decArgs(self): 144a8bb7a65Smaya """ 145a8bb7a65Smaya Returns a string with the types and names of the arguments, as you 146a8bb7a65Smaya would use in a function declaration. 147a8bb7a65Smaya """ 148a8bb7a65Smaya if not self.args: 149a8bb7a65Smaya return "void" 150a8bb7a65Smaya else: 151a8bb7a65Smaya return ", ".join(arg.dec for arg in self.args) 152a8bb7a65Smaya 153a8bb7a65Smaya @property 154a8bb7a65Smaya def callArgs(self): 155a8bb7a65Smaya """ 156a8bb7a65Smaya Returns a string with the names of the arguments, as you would use in a 157a8bb7a65Smaya function call. 158a8bb7a65Smaya """ 159a8bb7a65Smaya return ", ".join(arg.name for arg in self.args) 160a8bb7a65Smaya 161a8bb7a65Smaya @property 162a8bb7a65Smaya def basename(self): 163a8bb7a65Smaya assert self.name.startswith("gl") 164a8bb7a65Smaya return self.name[2:] 165a8bb7a65Smaya 166a8bb7a65Smayadef _getFunctionList(root): 167a8bb7a65Smaya for elem in root.findall("commands/command"): 168a8bb7a65Smaya yield _parseCommandElem(elem) 169a8bb7a65Smaya 170a8bb7a65Smayadef _parseCommandElem(elem): 171a8bb7a65Smaya protoElem = elem.find("proto") 172a8bb7a65Smaya (rt, name) = _parseProtoElem(protoElem) 173a8bb7a65Smaya 174a8bb7a65Smaya args = [] 175a8bb7a65Smaya for ch in elem.findall("param"): 176a8bb7a65Smaya # <param> tags have the same format as a <proto> tag. 177a8bb7a65Smaya args.append(FunctionArg(*_parseProtoElem(ch))) 178a8bb7a65Smaya func = FunctionDesc(name, rt, tuple(args), slot=None) 179a8bb7a65Smaya 180a8bb7a65Smaya return func 181a8bb7a65Smaya 182a8bb7a65Smayadef _parseProtoElem(elem): 183a8bb7a65Smaya # If I just remove the tags and string the text together, I'll get valid C code. 184a8bb7a65Smaya text = _flattenText(elem) 185a8bb7a65Smaya text = text.strip() 186a8bb7a65Smaya m = re.match(r"^(.+)\b(\w+)(?:\s*\[\s*(\d*)\s*\])?$", text, re.S) 187a8bb7a65Smaya if m: 188a8bb7a65Smaya typename = _fixupTypeName(m.group(1)) 189a8bb7a65Smaya name = m.group(2) 190a8bb7a65Smaya if m.group(3): 191a8bb7a65Smaya # HACK: glPathGlyphIndexRangeNV defines an argument like this: 192a8bb7a65Smaya # GLuint baseAndCount[2] 193a8bb7a65Smaya # Convert it to a pointer and hope for the best. 194a8bb7a65Smaya typename += "*" 195a8bb7a65Smaya return (typename, name) 196a8bb7a65Smaya else: 197a8bb7a65Smaya raise ValueError("Can't parse element %r -> %r" % (elem, text)) 198a8bb7a65Smaya 199a8bb7a65Smayadef _flattenText(elem): 200a8bb7a65Smaya """ 201a8bb7a65Smaya Returns the text in an element and all child elements, with the tags 202a8bb7a65Smaya removed. 203a8bb7a65Smaya """ 204a8bb7a65Smaya text = "" 205a8bb7a65Smaya if elem.text is not None: 206a8bb7a65Smaya text = elem.text 207a8bb7a65Smaya for ch in elem: 208a8bb7a65Smaya text += _flattenText(ch) 209a8bb7a65Smaya if ch.tail is not None: 210a8bb7a65Smaya text += ch.tail 211a8bb7a65Smaya return text 212a8bb7a65Smaya 213a8bb7a65Smayadef _fixupTypeName(typeName): 214a8bb7a65Smaya """ 215a8bb7a65Smaya Converts a typename into a more consistent format. 216a8bb7a65Smaya """ 217a8bb7a65Smaya 218a8bb7a65Smaya rv = typeName.strip() 219a8bb7a65Smaya 220a8bb7a65Smaya # Replace "GLvoid" with just plain "void". 221a8bb7a65Smaya rv = re.sub(r"\bGLvoid\b", "void", rv) 222a8bb7a65Smaya 223a8bb7a65Smaya # Remove the vendor suffixes from types that have a suffix-less version. 224a8bb7a65Smaya rv = re.sub(r"\b(GLhalf|GLintptr|GLsizeiptr|GLint64|GLuint64)(?:ARB|EXT|NV|ATI)\b", r"\1", rv) 225a8bb7a65Smaya 226a8bb7a65Smaya rv = re.sub(r"\bGLDEBUGPROCKHR\b", "GLDEBUGPROC", rv) 227a8bb7a65Smaya 228a8bb7a65Smaya # Clear out any leading and trailing whitespace. 229a8bb7a65Smaya rv = rv.strip() 230a8bb7a65Smaya 231a8bb7a65Smaya # Remove any whitespace before a '*' 232a8bb7a65Smaya rv = re.sub(r"\s+\*", r"*", rv) 233a8bb7a65Smaya 234a8bb7a65Smaya # Change "foo*" to "foo *" 235a8bb7a65Smaya rv = re.sub(r"([^\*])\*", r"\1 *", rv) 236a8bb7a65Smaya 237a8bb7a65Smaya # Condense all whitespace into a single space. 238a8bb7a65Smaya rv = re.sub(r"\s+", " ", rv) 239a8bb7a65Smaya 240a8bb7a65Smaya return rv 241a8bb7a65Smaya 242