Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 2 | |
Ian Romanick | 5f1f229 | 2005-01-07 02:39:09 +0000 | [diff] [blame] | 3 | # (C) Copyright IBM Corporation 2004, 2005 |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 4 | # All Rights Reserved. |
| 5 | # |
| 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: |
| 12 | # |
| 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 |
| 15 | # Software. |
| 16 | # |
| 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 |
| 23 | # IN THE SOFTWARE. |
| 24 | # |
| 25 | # Authors: |
| 26 | # Ian Romanick <idr@us.ibm.com> |
| 27 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 28 | import libxml2 |
| 29 | import re, sys, string |
| 30 | import typeexpr |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 31 | |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 32 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 33 | def parse_GL_API( file_name, factory = None ): |
| 34 | doc = libxml2.readFile( file_name, None, libxml2.XML_PARSE_XINCLUDE + libxml2.XML_PARSE_NOBLANKS + libxml2.XML_PARSE_DTDVALID + libxml2.XML_PARSE_DTDATTR + libxml2.XML_PARSE_DTDLOAD + libxml2.XML_PARSE_NOENT ) |
| 35 | ret = doc.xincludeProcess() |
| 36 | |
| 37 | if not factory: |
| 38 | factory = gl_item_factory() |
| 39 | |
| 40 | api = factory.create_item( "api", None, None ) |
| 41 | api.process_element( doc ) |
| 42 | |
Ian Romanick | bf83e65 | 2006-08-24 20:14:45 +0000 | [diff] [blame] | 43 | # After the XML has been processed, we need to go back and assign |
| 44 | # dispatch offsets to the functions that request that their offsets |
| 45 | # be assigned by the scripts. Typically this means all functions |
| 46 | # that are not part of the ABI. |
Ian Romanick | bf83e65 | 2006-08-24 20:14:45 +0000 | [diff] [blame] | 47 | |
Ian Romanick | eaeaaf6 | 2006-10-04 20:45:59 +0000 | [diff] [blame^] | 48 | for func in api.functionIterateByCategory(): |
Ian Romanick | bf83e65 | 2006-08-24 20:14:45 +0000 | [diff] [blame] | 49 | if func.assign_offset: |
Ian Romanick | eaeaaf6 | 2006-10-04 20:45:59 +0000 | [diff] [blame^] | 50 | func.offset = api.next_offset; |
| 51 | api.next_offset += 1 |
Ian Romanick | bf83e65 | 2006-08-24 20:14:45 +0000 | [diff] [blame] | 52 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 53 | doc.freeDoc() |
| 54 | |
| 55 | return api |
| 56 | |
| 57 | |
| 58 | def is_attr_true( element, name ): |
Ian Romanick | 73b4c1b | 2005-04-14 23:00:34 +0000 | [diff] [blame] | 59 | """Read a name value from an element's attributes. |
| 60 | |
| 61 | The value read from the attribute list must be either 'true' or |
| 62 | 'false'. If the value is 'false', zero will be returned. If the |
| 63 | value is 'true', non-zero will be returned. An exception will be |
| 64 | raised for any other value.""" |
| 65 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 66 | value = element.nsProp( name, None ) |
Ian Romanick | 73b4c1b | 2005-04-14 23:00:34 +0000 | [diff] [blame] | 67 | if value == "true": |
| 68 | return 1 |
| 69 | elif value == "false": |
| 70 | return 0 |
| 71 | else: |
| 72 | raise RuntimeError('Invalid value "%s" for boolean "%s".' % (value, name)) |
| 73 | |
| 74 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 75 | class gl_print_base: |
| 76 | """Base class of all API pretty-printers. |
Ian Romanick | 93d2d54 | 2005-04-18 19:42:23 +0000 | [diff] [blame] | 77 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 78 | In the model-view-controller pattern, this is the view. Any derived |
| 79 | class will want to over-ride the printBody, printRealHader, and |
| 80 | printRealFooter methods. Some derived classes may want to over-ride |
| 81 | printHeader and printFooter, or even Print (though this is unlikely). |
Ian Romanick | 93d2d54 | 2005-04-18 19:42:23 +0000 | [diff] [blame] | 82 | """ |
Ian Romanick | d03ab10 | 2005-04-18 21:30:20 +0000 | [diff] [blame] | 83 | |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 84 | def __init__(self): |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 85 | # Name of the script that is generating the output file. |
| 86 | # Every derived class should set this to the name of its |
| 87 | # source file. |
| 88 | |
| 89 | self.name = "a" |
| 90 | |
| 91 | |
| 92 | # License on the *generated* source file. This may differ |
| 93 | # from the license on the script that is generating the file. |
| 94 | # Every derived class should set this to some reasonable |
| 95 | # value. |
| 96 | # |
| 97 | # See license.py for an example of a reasonable value. |
| 98 | |
| 99 | self.license = "The license for this file is unspecified." |
| 100 | |
| 101 | |
| 102 | # The header_tag is the name of the C preprocessor define |
| 103 | # used to prevent multiple inclusion. Typically only |
| 104 | # generated C header files need this to be set. Setting it |
| 105 | # causes code to be generated automatically in printHeader |
| 106 | # and printFooter. |
| 107 | |
Ian Romanick | 16c3c74 | 2005-01-28 19:00:54 +0000 | [diff] [blame] | 108 | self.header_tag = None |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 109 | |
| 110 | |
| 111 | # List of file-private defines that must be undefined at the |
| 112 | # end of the file. This can be used in header files to define |
| 113 | # names for use in the file, then undefine them at the end of |
| 114 | # the header file. |
| 115 | |
Ian Romanick | c280358 | 2005-02-01 00:28:47 +0000 | [diff] [blame] | 116 | self.undef_list = [] |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 117 | return |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 118 | |
Ian Romanick | a9d033c | 2004-05-19 23:33:08 +0000 | [diff] [blame] | 119 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 120 | def Print(self, api): |
| 121 | self.printHeader() |
| 122 | self.printBody(api) |
| 123 | self.printFooter() |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 124 | return |
| 125 | |
Ian Romanick | a9d033c | 2004-05-19 23:33:08 +0000 | [diff] [blame] | 126 | |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 127 | def printHeader(self): |
Ian Romanick | a9d033c | 2004-05-19 23:33:08 +0000 | [diff] [blame] | 128 | """Print the header associated with all files and call the printRealHeader method.""" |
| 129 | |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 130 | print '/* DO NOT EDIT - This file generated automatically by %s script */' \ |
| 131 | % (self.name) |
| 132 | print '' |
| 133 | print '/*' |
| 134 | print ' * ' + self.license.replace('\n', '\n * ') |
| 135 | print ' */' |
| 136 | print '' |
Ian Romanick | 16c3c74 | 2005-01-28 19:00:54 +0000 | [diff] [blame] | 137 | if self.header_tag: |
| 138 | print '#if !defined( %s )' % (self.header_tag) |
| 139 | print '# define %s' % (self.header_tag) |
| 140 | print '' |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 141 | self.printRealHeader(); |
| 142 | return |
| 143 | |
Ian Romanick | a9d033c | 2004-05-19 23:33:08 +0000 | [diff] [blame] | 144 | |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 145 | def printFooter(self): |
Ian Romanick | a9d033c | 2004-05-19 23:33:08 +0000 | [diff] [blame] | 146 | """Print the header associated with all files and call the printRealFooter method.""" |
| 147 | |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 148 | self.printRealFooter() |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 149 | |
| 150 | if self.undef_list: |
| 151 | print '' |
| 152 | for u in self.undef_list: |
| 153 | print "# undef %s" % (u) |
| 154 | |
Ian Romanick | 16c3c74 | 2005-01-28 19:00:54 +0000 | [diff] [blame] | 155 | if self.header_tag: |
Ian Romanick | c280358 | 2005-02-01 00:28:47 +0000 | [diff] [blame] | 156 | print '' |
| 157 | print '#endif /* !defined( %s ) */' % (self.header_tag) |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 158 | |
| 159 | |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 160 | def printRealHeader(self): |
Ian Romanick | a9d033c | 2004-05-19 23:33:08 +0000 | [diff] [blame] | 161 | """Print the "real" header for the created file. |
| 162 | |
| 163 | In the base class, this function is empty. All derived |
| 164 | classes should over-ride this function.""" |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 165 | return |
| 166 | |
Ian Romanick | a9d033c | 2004-05-19 23:33:08 +0000 | [diff] [blame] | 167 | |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 168 | def printRealFooter(self): |
Ian Romanick | a9d033c | 2004-05-19 23:33:08 +0000 | [diff] [blame] | 169 | """Print the "real" footer for the created file. |
| 170 | |
| 171 | In the base class, this function is empty. All derived |
| 172 | classes should over-ride this function.""" |
Ian Romanick | 73f59b0 | 2004-05-18 18:33:40 +0000 | [diff] [blame] | 173 | return |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 174 | |
| 175 | |
| 176 | def printPure(self): |
| 177 | """Conditionally define `PURE' function attribute. |
| 178 | |
| 179 | Conditionally defines a preprocessor macro `PURE' that wraps |
| 180 | GCC's `pure' function attribute. The conditional code can be |
| 181 | easilly adapted to other compilers that support a similar |
| 182 | feature. |
| 183 | |
| 184 | The name is also added to the file's undef_list. |
| 185 | """ |
| 186 | self.undef_list.append("PURE") |
| 187 | print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96) |
| 188 | # define PURE __attribute__((pure)) |
| 189 | # else |
| 190 | # define PURE |
| 191 | # endif""" |
| 192 | return |
| 193 | |
| 194 | |
| 195 | def printFastcall(self): |
| 196 | """Conditionally define `FASTCALL' function attribute. |
| 197 | |
| 198 | Conditionally defines a preprocessor macro `FASTCALL' that |
| 199 | wraps GCC's `fastcall' function attribute. The conditional |
| 200 | code can be easilly adapted to other compilers that support a |
| 201 | similar feature. |
| 202 | |
| 203 | The name is also added to the file's undef_list. |
| 204 | """ |
| 205 | |
| 206 | self.undef_list.append("FASTCALL") |
Brian Paul | f468dfd | 2005-10-20 22:51:50 +0000 | [diff] [blame] | 207 | print """# if defined(__i386__) && defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__) |
| 208 | # define FASTCALL __attribute__((fastcall)) |
| 209 | # else |
| 210 | # define FASTCALL |
| 211 | # endif""" |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 212 | return |
| 213 | |
| 214 | |
| 215 | def printVisibility(self, S, s): |
| 216 | """Conditionally define visibility function attribute. |
| 217 | |
| 218 | Conditionally defines a preprocessor macro name S that wraps |
| 219 | GCC's visibility function attribute. The visibility used is |
| 220 | the parameter s. The conditional code can be easilly adapted |
| 221 | to other compilers that support a similar feature. |
| 222 | |
| 223 | The name is also added to the file's undef_list. |
| 224 | """ |
| 225 | |
| 226 | self.undef_list.append(S) |
Adam Jackson | ca1ac98 | 2005-08-26 17:50:39 +0000 | [diff] [blame] | 227 | print """# if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && defined(__ELF__) |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 228 | # define %s __attribute__((visibility("%s"))) |
| 229 | # else |
| 230 | # define %s |
| 231 | # endif""" % (S, s, S) |
| 232 | return |
| 233 | |
| 234 | |
| 235 | def printNoinline(self): |
| 236 | """Conditionally define `NOINLINE' function attribute. |
| 237 | |
| 238 | Conditionally defines a preprocessor macro `NOINLINE' that |
| 239 | wraps GCC's `noinline' function attribute. The conditional |
| 240 | code can be easilly adapted to other compilers that support a |
| 241 | similar feature. |
| 242 | |
| 243 | The name is also added to the file's undef_list. |
| 244 | """ |
| 245 | |
| 246 | self.undef_list.append("NOINLINE") |
| 247 | print """# if defined(__GNUC__) |
| 248 | # define NOINLINE __attribute__((noinline)) |
| 249 | # else |
| 250 | # define NOINLINE |
| 251 | # endif""" |
| 252 | return |
| 253 | |
| 254 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 255 | def real_function_name(element): |
| 256 | name = element.nsProp( "name", None ) |
| 257 | alias = element.nsProp( "alias", None ) |
| 258 | |
| 259 | if alias: |
| 260 | return alias |
| 261 | else: |
| 262 | return name |
| 263 | |
| 264 | |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 265 | def real_category_name(c): |
| 266 | if re.compile("[1-9][0-9]*[.][0-9]+").match(c): |
| 267 | return "GL_VERSION_" + c.replace(".", "_") |
| 268 | else: |
| 269 | return c |
| 270 | |
| 271 | |
Ian Romanick | eaeaaf6 | 2006-10-04 20:45:59 +0000 | [diff] [blame^] | 272 | def classify_category(name, number): |
| 273 | """Based on the category name and number, select a numerical class for it. |
| 274 | |
| 275 | Categories are divided into four classes numbered 0 through 3. The |
| 276 | classes are: |
| 277 | |
| 278 | 0. Core GL versions, sorted by version number. |
| 279 | 1. ARB extensions, sorted by extension number. |
| 280 | 2. Non-ARB extensions, sorted by extension number. |
| 281 | 3. Un-numbered extensions, sorted by extension name. |
| 282 | """ |
| 283 | |
| 284 | try: |
| 285 | core_version = float(name) |
| 286 | except Exception,e: |
| 287 | core_version = 0.0 |
| 288 | |
| 289 | if core_version > 0.0: |
| 290 | cat_type = 0 |
| 291 | key = name |
| 292 | elif name.startswith("GL_ARB_") or name.startswith("GLX_ARB_") or name.startswith("WGL_ARB_"): |
| 293 | cat_type = 1 |
| 294 | key = int(number) |
| 295 | else: |
| 296 | if number != None: |
| 297 | cat_type = 2 |
| 298 | key = int(number) |
| 299 | else: |
| 300 | cat_type = 3 |
| 301 | key = name |
| 302 | |
| 303 | |
| 304 | return [cat_type, key] |
| 305 | |
| 306 | |
Ian Romanick | f0ff50d | 2005-07-02 08:29:57 +0000 | [diff] [blame] | 307 | def create_parameter_string(parameters, include_names): |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 308 | """Create a parameter string from a list of gl_parameters.""" |
| 309 | |
| 310 | list = [] |
| 311 | for p in parameters: |
Ian Romanick | f0ff50d | 2005-07-02 08:29:57 +0000 | [diff] [blame] | 312 | if include_names: |
| 313 | list.append( p.string() ) |
| 314 | else: |
| 315 | list.append( p.type_string() ) |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 316 | |
| 317 | if len(list) == 0: list = ["void"] |
| 318 | |
| 319 | return string.join(list, ", ") |
| 320 | |
| 321 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 322 | class gl_item: |
| 323 | def __init__(self, element, context): |
| 324 | self.context = context |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 325 | self.name = element.nsProp( "name", None ) |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 326 | self.category = real_category_name( element.parent.nsProp( "name", None ) ) |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 327 | return |
| 328 | |
| 329 | |
| 330 | class gl_type( gl_item ): |
| 331 | def __init__(self, element, context): |
| 332 | gl_item.__init__(self, element, context) |
| 333 | self.size = int( element.nsProp( "size", None ), 0 ) |
| 334 | |
| 335 | te = typeexpr.type_expression( None ) |
| 336 | tn = typeexpr.type_node() |
| 337 | tn.size = int( element.nsProp( "size", None ), 0 ) |
| 338 | tn.integer = not is_attr_true( element, "float" ) |
| 339 | tn.unsigned = is_attr_true( element, "unsigned" ) |
| 340 | tn.name = "GL" + self.name |
| 341 | te.set_base_type_node( tn ) |
| 342 | |
| 343 | self.type_expr = te |
| 344 | return |
| 345 | |
| 346 | |
| 347 | def get_type_expression(self): |
| 348 | return self.type_expr |
| 349 | |
| 350 | |
| 351 | class gl_enum( gl_item ): |
| 352 | def __init__(self, element, context): |
| 353 | gl_item.__init__(self, element, context) |
| 354 | self.value = int( element.nsProp( "value", None ), 0 ) |
| 355 | |
| 356 | temp = element.nsProp( "count", None ) |
| 357 | if not temp or temp == "?": |
| 358 | self.default_count = -1 |
| 359 | else: |
| 360 | try: |
| 361 | c = int(temp) |
| 362 | except Exception,e: |
| 363 | raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp, self.name, n)) |
| 364 | |
| 365 | self.default_count = c |
| 366 | |
| 367 | return |
| 368 | |
| 369 | |
| 370 | def priority(self): |
| 371 | """Calculate a 'priority' for this enum name. |
| 372 | |
| 373 | When an enum is looked up by number, there may be many |
| 374 | possible names, but only one is the 'prefered' name. The |
| 375 | priority is used to select which name is the 'best'. |
| 376 | |
| 377 | Highest precedence is given to core GL name. ARB extension |
| 378 | names have the next highest, followed by EXT extension names. |
| 379 | Vendor extension names are the lowest. |
| 380 | """ |
| 381 | |
| 382 | if self.name.endswith( "_BIT" ): |
| 383 | bias = 1 |
| 384 | else: |
| 385 | bias = 0 |
| 386 | |
| 387 | if self.category.startswith( "GL_VERSION_" ): |
| 388 | priority = 0 |
| 389 | elif self.category.startswith( "GL_ARB_" ): |
| 390 | priority = 2 |
| 391 | elif self.category.startswith( "GL_EXT_" ): |
| 392 | priority = 4 |
| 393 | else: |
| 394 | priority = 6 |
| 395 | |
| 396 | return priority + bias |
| 397 | |
| 398 | |
| 399 | |
| 400 | class gl_parameter: |
| 401 | def __init__(self, element, context): |
| 402 | self.name = element.nsProp( "name", None ) |
| 403 | |
| 404 | ts = element.nsProp( "type", None ) |
| 405 | self.type_expr = typeexpr.type_expression( ts, context ) |
| 406 | |
| 407 | temp = element.nsProp( "variable_param", None ) |
| 408 | if temp: |
| 409 | self.count_parameter_list = temp.split( ' ' ) |
| 410 | else: |
| 411 | self.count_parameter_list = [] |
| 412 | |
| 413 | # The count tag can be either a numeric string or the name of |
| 414 | # a variable. If it is the name of a variable, the int(c) |
| 415 | # statement will throw an exception, and the except block will |
| 416 | # take over. |
| 417 | |
| 418 | c = element.nsProp( "count", None ) |
| 419 | try: |
| 420 | count = int(c) |
| 421 | self.count = count |
| 422 | self.counter = None |
| 423 | except Exception,e: |
| 424 | count = 1 |
| 425 | self.count = 0 |
| 426 | self.counter = c |
| 427 | |
| 428 | self.count_scale = int(element.nsProp( "count_scale", None )) |
| 429 | |
| 430 | elements = (count * self.count_scale) |
| 431 | if elements == 1: |
| 432 | elements = 0 |
| 433 | |
| 434 | #if ts == "GLdouble": |
| 435 | # print '/* stack size -> %s = %u (before)*/' % (self.name, self.type_expr.get_stack_size()) |
| 436 | # print '/* # elements = %u */' % (elements) |
| 437 | self.type_expr.set_elements( elements ) |
| 438 | #if ts == "GLdouble": |
| 439 | # print '/* stack size -> %s = %u (after) */' % (self.name, self.type_expr.get_stack_size()) |
| 440 | |
| 441 | self.is_counter = is_attr_true( element, 'counter' ) |
| 442 | self.is_output = is_attr_true( element, 'output' ) |
| 443 | |
| 444 | |
| 445 | # Pixel data has special parameters. |
| 446 | |
| 447 | self.width = element.nsProp('img_width', None) |
| 448 | self.height = element.nsProp('img_height', None) |
| 449 | self.depth = element.nsProp('img_depth', None) |
| 450 | self.extent = element.nsProp('img_extent', None) |
| 451 | |
| 452 | self.img_xoff = element.nsProp('img_xoff', None) |
| 453 | self.img_yoff = element.nsProp('img_yoff', None) |
| 454 | self.img_zoff = element.nsProp('img_zoff', None) |
| 455 | self.img_woff = element.nsProp('img_woff', None) |
| 456 | |
| 457 | self.img_format = element.nsProp('img_format', None) |
| 458 | self.img_type = element.nsProp('img_type', None) |
| 459 | self.img_target = element.nsProp('img_target', None) |
| 460 | |
| 461 | self.img_pad_dimensions = is_attr_true( element, 'img_pad_dimensions' ) |
| 462 | self.img_null_flag = is_attr_true( element, 'img_null_flag' ) |
| 463 | self.img_send_null = is_attr_true( element, 'img_send_null' ) |
| 464 | |
| 465 | return |
| 466 | |
| 467 | |
| 468 | def compatible(self, other): |
| 469 | return 1 |
| 470 | |
| 471 | |
| 472 | def is_array(self): |
| 473 | return self.is_pointer() |
| 474 | |
| 475 | |
| 476 | def is_pointer(self): |
| 477 | return self.type_expr.is_pointer() |
| 478 | |
| 479 | |
| 480 | def is_image(self): |
| 481 | if self.width: |
| 482 | return 1 |
| 483 | else: |
| 484 | return 0 |
| 485 | |
| 486 | |
| 487 | def is_variable_length(self): |
| 488 | return len(self.count_parameter_list) or self.counter |
| 489 | |
| 490 | |
| 491 | def is_64_bit(self): |
| 492 | count = self.type_expr.get_element_count() |
| 493 | if count: |
| 494 | if (self.size() / count) == 8: |
| 495 | return 1 |
| 496 | else: |
| 497 | if self.size() == 8: |
| 498 | return 1 |
| 499 | |
| 500 | return 0 |
| 501 | |
| 502 | |
| 503 | def string(self): |
| 504 | return self.type_expr.original_string + " " + self.name |
| 505 | |
| 506 | |
| 507 | def type_string(self): |
| 508 | return self.type_expr.original_string |
| 509 | |
| 510 | |
| 511 | def get_base_type_string(self): |
| 512 | return self.type_expr.get_base_name() |
| 513 | |
| 514 | |
| 515 | def get_dimensions(self): |
| 516 | if not self.width: |
| 517 | return [ 0, "0", "0", "0", "0" ] |
| 518 | |
| 519 | dim = 1 |
| 520 | w = self.width |
| 521 | h = "1" |
| 522 | d = "1" |
| 523 | e = "1" |
| 524 | |
| 525 | if self.height: |
| 526 | dim = 2 |
| 527 | h = self.height |
| 528 | |
| 529 | if self.depth: |
| 530 | dim = 3 |
| 531 | d = self.depth |
| 532 | |
| 533 | if self.extent: |
| 534 | dim = 4 |
| 535 | e = self.extent |
| 536 | |
| 537 | return [ dim, w, h, d, e ] |
| 538 | |
| 539 | |
| 540 | def get_stack_size(self): |
| 541 | return self.type_expr.get_stack_size() |
| 542 | |
| 543 | |
| 544 | def size(self): |
| 545 | if self.is_image(): |
| 546 | return 0 |
| 547 | else: |
| 548 | return self.type_expr.get_element_size() |
| 549 | |
| 550 | |
| 551 | def get_element_count(self): |
| 552 | c = self.type_expr.get_element_count() |
| 553 | if c == 0: |
| 554 | return 1 |
| 555 | |
| 556 | return c |
| 557 | |
| 558 | |
| 559 | def size_string(self, use_parens = 1): |
| 560 | s = self.size() |
| 561 | if self.counter or self.count_parameter_list: |
| 562 | list = [ "compsize" ] |
| 563 | |
| 564 | if self.counter and self.count_parameter_list: |
| 565 | list.append( self.counter ) |
| 566 | elif self.counter: |
| 567 | list = [ self.counter ] |
| 568 | |
| 569 | if s > 1: |
| 570 | list.append( str(s) ) |
| 571 | |
| 572 | if len(list) > 1 and use_parens : |
| 573 | return "(%s)" % (string.join(list, " * ")) |
| 574 | else: |
| 575 | return string.join(list, " * ") |
| 576 | |
| 577 | elif self.is_image(): |
| 578 | return "compsize" |
| 579 | else: |
| 580 | return str(s) |
| 581 | |
| 582 | |
| 583 | def format_string(self): |
| 584 | if self.type_expr.original_string == "GLenum": |
| 585 | return "0x%x" |
| 586 | else: |
| 587 | return self.type_expr.format_string() |
| 588 | |
| 589 | |
| 590 | |
| 591 | class gl_function( gl_item ): |
| 592 | def __init__(self, element, context): |
| 593 | self.context = context |
| 594 | self.name = None |
| 595 | |
| 596 | self.entry_points = [] |
| 597 | self.return_type = "void" |
| 598 | self.parameters = [] |
| 599 | self.offset = -1 |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 600 | self.initialized = 0 |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 601 | self.images = [] |
| 602 | |
Ian Romanick | bf83e65 | 2006-08-24 20:14:45 +0000 | [diff] [blame] | 603 | self.assign_offset = 0 |
| 604 | |
Ian Romanick | 7e9737b | 2006-08-26 21:26:55 +0000 | [diff] [blame] | 605 | self.static_entry_points = [] |
| 606 | |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 607 | # Track the parameter string (for the function prototype) |
| 608 | # for each entry-point. This is done because some functions |
| 609 | # change their prototype slightly when promoted from extension |
| 610 | # to ARB extension to core. glTexImage3DEXT and glTexImage3D |
| 611 | # are good examples of this. Scripts that need to generate |
| 612 | # code for these differing aliases need to real prototype |
| 613 | # for each entry-point. Otherwise, they may generate code |
| 614 | # that won't compile. |
| 615 | |
| 616 | self.parameter_strings = {} |
| 617 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 618 | self.process_element( element ) |
| 619 | |
| 620 | return |
| 621 | |
| 622 | |
| 623 | def process_element(self, element): |
| 624 | name = element.nsProp( "name", None ) |
| 625 | alias = element.nsProp( "alias", None ) |
| 626 | |
Ian Romanick | 7e9737b | 2006-08-26 21:26:55 +0000 | [diff] [blame] | 627 | if is_attr_true(element, "static_dispatch"): |
| 628 | self.static_entry_points.append(name) |
Ian Romanick | 4e4b5f4 | 2006-08-22 16:34:38 +0000 | [diff] [blame] | 629 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 630 | self.entry_points.append( name ) |
| 631 | if alias: |
| 632 | true_name = alias |
| 633 | else: |
| 634 | true_name = name |
| 635 | |
| 636 | # Only try to set the offset when a non-alias |
| 637 | # entry-point is being processes. |
| 638 | |
| 639 | offset = element.nsProp( "offset", None ) |
| 640 | if offset: |
| 641 | try: |
| 642 | o = int( offset ) |
| 643 | self.offset = o |
| 644 | except Exception, e: |
| 645 | self.offset = -1 |
Ian Romanick | bf83e65 | 2006-08-24 20:14:45 +0000 | [diff] [blame] | 646 | if offset == "assign": |
| 647 | self.assign_offset = 1 |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 648 | |
| 649 | |
| 650 | if not self.name: |
| 651 | self.name = true_name |
| 652 | elif self.name != true_name: |
| 653 | raise RuntimeError("Function true name redefined. Was %s, now %s." % (self.name, true_name)) |
| 654 | |
| 655 | |
| 656 | # There are two possible cases. The first time an entry-point |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 657 | # with data is seen, self.initialized will be 0. On that |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 658 | # pass, we just fill in the data. The next time an |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 659 | # entry-point with data is seen, self.initialized will be 1. |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 660 | # On that pass we have to make that the new values match the |
| 661 | # valuse from the previous entry-point. |
| 662 | |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 663 | parameters = [] |
| 664 | return_type = "void" |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 665 | child = element.children |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 666 | while child: |
| 667 | if child.type == "element": |
| 668 | if child.name == "return": |
| 669 | return_type = child.nsProp( "type", None ) |
| 670 | elif child.name == "param": |
| 671 | param = self.context.factory.create_item( "parameter", child, self.context) |
| 672 | parameters.append( param ) |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 673 | |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 674 | child = child.next |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 675 | |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 676 | |
| 677 | if self.initialized: |
| 678 | if self.return_type != return_type: |
| 679 | raise RuntimeError( "Return type changed in %s. Was %s, now %s." % (name, self.return_type, return_type)) |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 680 | |
| 681 | if len(parameters) != len(self.parameters): |
| 682 | raise RuntimeError( "Parameter count mismatch in %s. Was %d, now %d." % (name, len(self.parameters), len(parameters))) |
| 683 | |
| 684 | for j in range(0, len(parameters)): |
| 685 | p1 = parameters[j] |
| 686 | p2 = self.parameters[j] |
| 687 | if not p1.compatible( p2 ): |
| 688 | raise RuntimeError( 'Parameter type mismatch in %s. "%s" was "%s", now "%s".' % (name, p2.name, p2.type_expr.original_string, p1.type_expr.original_string)) |
| 689 | |
| 690 | |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 691 | if true_name == name or not self.initialized: |
| 692 | self.return_type = return_type |
| 693 | self.parameters = parameters |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 694 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 695 | for param in self.parameters: |
| 696 | if param.is_image(): |
| 697 | self.images.append( param ) |
| 698 | |
| 699 | if element.children: |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 700 | self.initialized = 1 |
Ian Romanick | f0ff50d | 2005-07-02 08:29:57 +0000 | [diff] [blame] | 701 | self.parameter_strings[name] = create_parameter_string(parameters, 1) |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 702 | else: |
| 703 | self.parameter_strings[name] = None |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 704 | |
| 705 | return |
| 706 | |
| 707 | |
| 708 | def get_images(self): |
| 709 | """Return potentially empty list of input images.""" |
| 710 | return self.images |
| 711 | |
| 712 | |
| 713 | def parameterIterator(self): |
| 714 | return self.parameters.__iter__(); |
| 715 | |
| 716 | |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 717 | def get_parameter_string(self, entrypoint = None): |
| 718 | if entrypoint: |
| 719 | s = self.parameter_strings[ entrypoint ] |
| 720 | if s: |
| 721 | return s |
| 722 | |
Ian Romanick | f0ff50d | 2005-07-02 08:29:57 +0000 | [diff] [blame] | 723 | return create_parameter_string( self.parameters, 1 ) |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 724 | |
Ian Romanick | 7e9737b | 2006-08-26 21:26:55 +0000 | [diff] [blame] | 725 | def is_static_entry_point(self, name): |
| 726 | return name in self.static_entry_points |
| 727 | |
Ian Romanick | 258751f | 2006-08-28 17:40:45 +0000 | [diff] [blame] | 728 | def dispatch_name(self): |
| 729 | if self.name in self.static_entry_points: |
| 730 | return self.name |
| 731 | else: |
| 732 | return "_dispatch_stub_%u" % (self.offset) |
| 733 | |
| 734 | def static_name(self, name): |
| 735 | if name in self.static_entry_points: |
| 736 | return name |
| 737 | else: |
| 738 | return "_dispatch_stub_%u" % (self.offset) |
| 739 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 740 | |
| 741 | class gl_item_factory: |
| 742 | """Factory to create objects derived from gl_item.""" |
| 743 | |
| 744 | def create_item(self, item_name, element, context): |
| 745 | if item_name == "function": |
| 746 | return gl_function(element, context) |
| 747 | if item_name == "type": |
| 748 | return gl_type(element, context) |
| 749 | elif item_name == "enum": |
| 750 | return gl_enum(element, context) |
| 751 | elif item_name == "parameter": |
| 752 | return gl_parameter(element, context) |
| 753 | elif item_name == "api": |
| 754 | return gl_api(self) |
| 755 | else: |
| 756 | return None |
| 757 | |
| 758 | |
| 759 | class gl_api: |
| 760 | def __init__(self, factory): |
| 761 | self.functions_by_name = {} |
| 762 | self.enums_by_name = {} |
| 763 | self.types_by_name = {} |
Ian Romanick | eaeaaf6 | 2006-10-04 20:45:59 +0000 | [diff] [blame^] | 764 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 765 | self.category_dict = {} |
Ian Romanick | eaeaaf6 | 2006-10-04 20:45:59 +0000 | [diff] [blame^] | 766 | self.categories = [{}, {}, {}, {}] |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 767 | |
| 768 | self.factory = factory |
| 769 | |
Ian Romanick | bf83e65 | 2006-08-24 20:14:45 +0000 | [diff] [blame] | 770 | self.next_offset = 0 |
| 771 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 772 | typeexpr.create_initial_types() |
| 773 | return |
| 774 | |
| 775 | |
| 776 | def process_element(self, doc): |
| 777 | element = doc.children |
| 778 | while element.type != "element" or element.name != "OpenGLAPI": |
| 779 | element = element.next |
| 780 | |
| 781 | if element: |
| 782 | self.process_OpenGLAPI(element) |
| 783 | return |
| 784 | |
| 785 | |
| 786 | def process_OpenGLAPI(self, element): |
| 787 | child = element.children |
| 788 | while child: |
| 789 | if child.type == "element": |
| 790 | if child.name == "category": |
| 791 | self.process_category( child ) |
| 792 | elif child.name == "OpenGLAPI": |
| 793 | self.process_OpenGLAPI( child ) |
| 794 | |
| 795 | child = child.next |
| 796 | |
| 797 | return |
| 798 | |
| 799 | |
| 800 | def process_category(self, cat): |
| 801 | cat_name = cat.nsProp( "name", None ) |
| 802 | cat_number = cat.nsProp( "number", None ) |
| 803 | |
Ian Romanick | eaeaaf6 | 2006-10-04 20:45:59 +0000 | [diff] [blame^] | 804 | [cat_type, key] = classify_category(cat_name, cat_number) |
| 805 | self.categories[cat_type][key] = [cat_name, cat_number] |
| 806 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 807 | child = cat.children |
| 808 | while child: |
| 809 | if child.type == "element": |
| 810 | if child.name == "function": |
| 811 | func_name = real_function_name( child ) |
| 812 | |
Ian Romanick | 5aafea0 | 2005-06-24 18:35:31 +0000 | [diff] [blame] | 813 | temp_name = child.nsProp( "name", None ) |
| 814 | self.category_dict[ temp_name ] = [cat_name, cat_number] |
| 815 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 816 | if self.functions_by_name.has_key( func_name ): |
| 817 | func = self.functions_by_name[ func_name ] |
| 818 | func.process_element( child ) |
| 819 | else: |
| 820 | func = self.factory.create_item( "function", child, self ) |
| 821 | self.functions_by_name[ func_name ] = func |
| 822 | |
Ian Romanick | bf83e65 | 2006-08-24 20:14:45 +0000 | [diff] [blame] | 823 | if func.offset >= self.next_offset: |
| 824 | self.next_offset = func.offset + 1 |
| 825 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 826 | |
| 827 | elif child.name == "enum": |
| 828 | enum = self.factory.create_item( "enum", child, self ) |
| 829 | self.enums_by_name[ enum.name ] = enum |
| 830 | elif child.name == "type": |
| 831 | t = self.factory.create_item( "type", child, self ) |
| 832 | self.types_by_name[ "GL" + t.name ] = t |
| 833 | |
| 834 | |
| 835 | child = child.next |
| 836 | |
| 837 | return |
| 838 | |
| 839 | |
Ian Romanick | eaeaaf6 | 2006-10-04 20:45:59 +0000 | [diff] [blame^] | 840 | def functionIterateByCategory(self, cat = None): |
| 841 | """Iterate over functions by category. |
| 842 | |
| 843 | If cat is None, all known functions are iterated in category |
| 844 | order. See classify_category for details of the ordering. |
| 845 | Within a category, functions are sorted by name. If cat is |
| 846 | not None, then only functions in that category are iterated. |
| 847 | """ |
| 848 | lists = [{}, {}, {}, {}] |
| 849 | |
| 850 | for func in self.functionIterateAll(): |
| 851 | [cat_name, cat_number] = self.category_dict[func.name] |
| 852 | |
| 853 | if (cat == None) or (cat == cat_name): |
| 854 | [func_cat_type, key] = classify_category(cat_name, cat_number) |
| 855 | |
| 856 | if not lists[func_cat_type].has_key(key): |
| 857 | lists[func_cat_type][key] = {} |
| 858 | |
| 859 | lists[func_cat_type][key][func.name] = func |
| 860 | |
| 861 | |
| 862 | functions = [] |
| 863 | for func_cat_type in range(0,4): |
| 864 | keys = lists[func_cat_type].keys() |
| 865 | keys.sort() |
| 866 | |
| 867 | for key in keys: |
| 868 | names = lists[func_cat_type][key].keys() |
| 869 | names.sort() |
| 870 | |
| 871 | for name in names: |
| 872 | functions.append(lists[func_cat_type][key][name]) |
| 873 | |
| 874 | return functions.__iter__() |
| 875 | |
| 876 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 877 | def functionIterateByOffset(self): |
| 878 | max_offset = -1 |
| 879 | for func in self.functions_by_name.itervalues(): |
| 880 | if func.offset > max_offset: |
| 881 | max_offset = func.offset |
| 882 | |
| 883 | |
| 884 | temp = [None for i in range(0, max_offset + 1)] |
| 885 | for func in self.functions_by_name.itervalues(): |
| 886 | if func.offset != -1: |
| 887 | temp[ func.offset ] = func |
| 888 | |
| 889 | |
| 890 | list = [] |
| 891 | for i in range(0, max_offset + 1): |
| 892 | if temp[i]: |
| 893 | list.append(temp[i]) |
| 894 | |
| 895 | return list.__iter__(); |
| 896 | |
| 897 | |
| 898 | def functionIterateAll(self): |
| 899 | return self.functions_by_name.itervalues() |
| 900 | |
| 901 | |
| 902 | def enumIterateByName(self): |
| 903 | keys = self.enums_by_name.keys() |
| 904 | keys.sort() |
| 905 | |
| 906 | list = [] |
| 907 | for enum in keys: |
| 908 | list.append( self.enums_by_name[ enum ] ) |
| 909 | |
| 910 | return list.__iter__() |
| 911 | |
| 912 | |
Ian Romanick | eaeaaf6 | 2006-10-04 20:45:59 +0000 | [diff] [blame^] | 913 | def categoryIterate(self): |
| 914 | """Iterate over categories. |
| 915 | |
| 916 | Iterate over all known categories in the order specified by |
| 917 | classify_category. Each iterated value is a tuple of the |
| 918 | name and number (which may be None) of the category. |
| 919 | """ |
| 920 | |
| 921 | list = [] |
| 922 | for cat_type in range(0,4): |
| 923 | keys = self.categories[cat_type].keys() |
| 924 | keys.sort() |
| 925 | |
| 926 | for key in keys: |
| 927 | list.append(self.categories[cat_type][key]) |
| 928 | |
| 929 | return list.__iter__() |
| 930 | |
| 931 | |
Ian Romanick | 66a5548 | 2005-06-21 23:42:43 +0000 | [diff] [blame] | 932 | def get_category_for_name( self, name ): |
| 933 | if self.category_dict.has_key(name): |
| 934 | return self.category_dict[name] |
| 935 | else: |
| 936 | return ["<unknown category>", None] |
| 937 | |
| 938 | |
| 939 | def typeIterate(self): |
| 940 | return self.types_by_name.itervalues() |
| 941 | |
| 942 | |
| 943 | def find_type( self, type_name ): |
| 944 | if type_name in self.types_by_name: |
| 945 | return self.types_by_name[ type_name ].type_expr |
| 946 | else: |
| 947 | print "Unable to find base type matching \"%s\"." % (type_name) |
| 948 | return None |