11 def __init__(self, *args):
19 raise TypeError, "__init__() takes zero or two arguments (%d given)"%len(args)
22 return "%d.%d"%(self.major, self.minor)
25 return "Version(%d, %d)"%(self.major, self.minor)
28 return "VERSION_%d_%d"%(self.major, self.minor)
30 def __lt__(self, other):
34 if self.major!=other.major:
35 return self.major<other.major
36 return self.minor<other.minor
38 def __gt__(self, other):
42 if self.major!=other.major:
43 return self.major>other.major
44 return self.minor>other.minor
46 def __eq__(self, other):
50 return (self.major==other.major and self.minor==other.minor)
59 self.core_version = None
60 self.deprecated_version = None
64 def __init__(self, name, kind):
70 def get_or_create_api_support(self, api):
71 supp = self.api_support.get(api)
73 supp = Thing.ApiSupport()
74 self.api_support[api] = supp
78 class Function(Thing):
79 def __init__(self, name):
80 Thing.__init__(self, name, Thing.FUNCTION)
81 self.return_type = "void"
85 r_bitmask = re.compile("_BIT[0-9]*(_|$)")
88 def __init__(self, name):
89 Thing.__init__(self, name, Thing.ENUM)
91 self.bitmask = bool(r_bitmask.search(self.name))
95 def __init__(self, name, api):
97 underscore = name.find('_')
98 self.ext_type = name[0:underscore]
99 self.base_name = name[underscore+1:]
103 if self.ext_type=="EXT":
105 elif self.ext_type=="ARB" or self.ext_type=="OES":
107 self.backport = False
111 def __init__(self, name):
113 self.latest_version = None
114 self.core_things = {}
118 def get_nested_elements(elem, path):
119 childElements = [c for c in elem.childNodes if c.nodeType==xml.dom.Node.ELEMENT_NODE]
121 head, tail = path.split('/', 1)
123 for c in childElements:
125 result += get_nested_elements(c, tail)
128 return [c for c in childElements if c.tagName==path]
130 def get_first_child(elem, tag):
131 for c in elem.childNodes:
132 if c.nodeType==xml.dom.Node.ELEMENT_NODE and c.tagName==tag:
136 def get_text_contents(node):
138 for c in node.childNodes:
139 if c.nodeType==xml.dom.Node.TEXT_NODE or c.nodeType==xml.dom.Node.CDATA_SECTION_NODE:
142 result += get_text_contents(c)
145 def get_or_create(map, name, type, *args):
148 obj = type(name, *args)
158 def parse_command(self, cmd):
159 proto = get_first_child(cmd, "proto")
160 name = get_text_contents(get_first_child(proto, "name"))
161 func = get_or_create(self.things, name, Function)
163 aliases = get_nested_elements(cmd, "alias")
164 func.aliases = [a.getAttribute("name") for a in aliases]
166 ptype = get_first_child(proto, "ptype")
168 func.return_type = get_text_contents(ptype)
170 for c in proto.childNodes:
171 if c.nodeType==xml.dom.Node.TEXT_NODE and c.data.strip():
172 func.return_type = c.data.strip()
175 params = get_nested_elements(cmd, "param")
176 func.params = map(get_text_contents, params)
178 def parse_enum(self, en):
179 name = en.getAttribute("name")
180 enum = get_or_create(self.things, name, Enum)
182 enum.value = int(en.getAttribute("value"), 16)
184 alias = en.getAttribute("alias")
186 enum.aliases.append(alias)
188 def parse_feature(self, feat):
189 api_name = feat.getAttribute("api")
190 api = get_or_create(self.apis, api_name, Api)
192 version = feat.getAttribute("number")
194 version = Version(*map(int, version.split('.')))
198 requires = get_nested_elements(feat, "require")
200 commands = get_nested_elements(req, "command")
201 enums = get_nested_elements(req, "enum")
202 for t in itertools.chain(commands, enums):
203 name = t.getAttribute("name")
204 thing = self.things.get(name)
206 supp = thing.get_or_create_api_support(api.name)
207 if not supp.core_version or version<supp.core_version:
208 supp.core_version = version
209 api.core_things[thing.name] = thing
211 removes = get_nested_elements(feat, "remove")
213 commands = get_nested_elements(rem, "command")
214 enums = get_nested_elements(rem, "enum")
216 for t in itertools.chain(commands, enums):
217 name = t.getAttribute("name")
218 thing = self.things.get(name)
220 supp = thing.get_or_create_api_support(api.name)
221 supp.deprecated_version = version
223 def parse_extension(self, ext):
224 ext_things_by_api = {}
225 requires = get_nested_elements(ext, "require")
227 api = req.getAttribute("api")
228 ext_things = ext_things_by_api.setdefault(api, [])
230 commands = get_nested_elements(req, "command")
231 enums = get_nested_elements(req, "enum")
232 for t in itertools.chain(commands, enums):
233 name = t.getAttribute("name")
234 thing = self.things.get(name)
236 ext_things.append(thing)
238 ext_name = ext.getAttribute("name")
239 if ext_name.startswith("GL_"):
240 ext_name = ext_name[3:]
242 common_things = ext_things_by_api.get("", [])
243 supported = ext.getAttribute("supported").split('|')
245 api = self.apis.get(s)
249 ext = get_or_create(api.extensions, ext_name, Extension, api)
250 api_things = ext_things_by_api.get(s, [])
251 for t in itertools.chain(common_things, api_things):
252 ext.things[t.name] = t
253 t.get_or_create_api_support(api.name).extensions.append(ext)
255 def parse_file(self, fn):
256 doc = xml.dom.minidom.parse(fn)
257 root = doc.documentElement
259 commands = get_nested_elements(root, "commands/command")
261 self.parse_command(cmd)
263 enums = get_nested_elements(root, "enums/enum")
267 features = get_nested_elements(root, "feature")
268 for feat in features:
269 self.parse_feature(feat)
271 extensions = get_nested_elements(root, "extensions/extension")
272 for ext in extensions:
273 self.parse_extension(ext)
275 def check_backport_extensions(self, api):
276 for e in api.extensions.itervalues():
277 if e.ext_type!="ARB":
281 for t in e.things.itervalues():
282 if t.name.endswith(e.ext_type):
286 def resolve_enum_aliases(self, api):
287 for e in api.extensions.itervalues():
288 ext_enums = filter((lambda t: t.kind==Thing.ENUM), e.things.itervalues())
289 enum_suffix = "_"+e.ext_type
291 if n.api_support[api.name].core_version:
295 if name.endswith(enum_suffix):
296 name = name[:-len(enum_suffix)]
297 ce = api.core_things.get(name)
298 if ce and ce.value==n.value and ce.name not in n.aliases:
299 n.aliases.append(ce.name)
301 def resolve_sources(self, api):
302 for e in api.extensions.itervalues():
303 for t in e.things.itervalues():
305 # There are a few cases where a vendor function is aliased to
306 # an EXT or ARB function but those are rare and not relevant for
308 alias = api.core_things.get(a)
310 sources = alias.api_support[api.name].sources
314 def sort_extensions(self):
315 for t in self.things.itervalues():
316 for s in t.api_support.itervalues():
317 s.extensions.sort(key=(lambda e: e.preference), reverse=True)
320 for a in self.apis.itervalues():
321 self.check_backport_extensions(a)
322 self.resolve_enum_aliases(a)
323 self.resolve_sources(a)
324 self.sort_extensions()
327 def detect_core_version(host_api, things, debug=None):
328 max_version = Version(1, 0)
333 supp = t.api_support.get(host_api.name)
334 if supp and supp.core_version:
335 if supp.core_version>max_version:
336 max_version = supp.core_version
337 lower_count += max_count
339 elif supp.core_version==max_version:
346 if lower_count>max_count or (missing and len(missing)*2<lower_count+max_count):
347 print "Warning: Inconsistent core version %s"%max_version
352 print "%d things missing from core:"%len(missing)
359 def detect_deprecated_version(host_api, things, debug):
363 supp = t.api_support.get(host_api.name)
364 if supp and supp.deprecated_version:
365 if min_version is None:
366 min_version = supp.deprecated_version
368 min_version = min(min_version, supp.deprecated_version)
371 if min_version and len(deprecated)*2<len(things):
372 print "Warning: Inconsistent deprecation version %s"%min_version
375 print "%d things are deprecated:"%len(deprecated)
381 def detect_backport_extension(host_api, things):
384 supp = t.api_support.get(host_api.name)
385 if supp and supp.core_version:
386 for e in supp.extensions:
387 if e.backport and e not in candidates:
390 total_count = len(things)
394 things_in_ext = filter((lambda t: t.name in e.things), things)
395 count = len(things_in_ext)
396 if count==total_count:
398 elif count>best_count:
402 if best_count*2>=total_count:
403 print "Warning: Inconsistent backport extension %s"%best_ext.name
405 def collect_extensions(thing, api, exts):
406 supp = thing.api_support.get(api)
410 for e in supp.extensions:
411 if not e.backport and e.ext_type!="MSP" and e not in exts:
414 for s in supp.sources:
415 collect_extensions(s, api, exts)
417 def detect_source_extension(host_api, things, debug=False):
421 collect_extensions(t, host_api.name, exts)
423 things_by_ext.setdefault(e, []).append(t)
427 print "Looking for %d things in %d extensions"%(len(things), len(things_by_ext))
432 recheck_base_version = True
433 missing = set(things)
435 if recheck_base_version:
436 max_version = Version(1, 0)
438 supp = t.api_support.get(host_api.name)
439 if supp and supp.core_version and max_version:
440 max_version = max(max_version, supp.core_version)
445 if not base_version or max_version<base_version:
446 base_version = max_version
447 keep_exts = len(extensions)
448 elif not base_version:
449 keep_exts = len(extensions)
451 recheck_base_version = False
453 if not missing or not things_by_ext:
458 for e, t in things_by_ext.iteritems():
460 if count>largest_count:
462 largest_count = count
463 elif count==largest_count and e.preference>largest_ext.preference:
467 print "Found %d things in %s"%(largest_count, largest_ext.name)
469 extensions.append(largest_ext)
470 for t in things_by_ext[largest_ext]:
473 supp = t.api_support.get(host_api.name)
474 if supp and supp.core_version==base_version:
475 recheck_base_version = True
477 del things_by_ext[largest_ext]
478 for e in things_by_ext.keys():
479 unseen = filter((lambda t: t in missing), things_by_ext[e])
481 things_by_ext[e] = unseen
486 return None, extensions
489 print "Found remaining things in version %s"%base_version
490 if keep_exts<len(extensions):
491 print "Discarding %d extensions that do not improve base version"%(len(extensions)-keep_exts)
492 del extensions[keep_exts:]
493 return base_version, extensions
496 print "%d things still missing:"%len(missing)
502 class SourceGenerator:
503 def __init__(self, host_api, ext_name, things, optional_things, debug=False):
504 self.host_api = host_api
505 self.api_prefix = "GL"
506 if self.host_api.name=="gles2":
507 self.api_prefix = "GL_ES"
508 self.ext_name = ext_name
509 all_things = things+optional_things
510 self.funcs = filter((lambda t: t.kind==Thing.FUNCTION), all_things)
511 self.funcs.sort(key=(lambda f: f.name))
512 self.func_typedefs = dict((f.name, "FPtr_"+f.name) for f in self.funcs)
513 self.enums = filter((lambda t: t.kind==Thing.ENUM), all_things)
514 self.enums.sort(key=(lambda e: e.value))
515 self.core_version = detect_core_version(host_api, things, debug)
516 self.deprecated_version = detect_deprecated_version(host_api, things, debug)
517 self.backport_ext = detect_backport_extension(host_api, things)
518 b, e = detect_source_extension(host_api, things, debug)
519 self.base_version = b
522 if not self.core_version and not self.backport_ext and not self.source_exts:
523 print "Warning: Not supportable on host API"
526 print "--- Extension information ---"
527 print "Extension %s"%self.ext_name
528 print "Core %s"%self.core_version
529 print "Deprecated %s"%self.deprecated_version
530 if self.backport_ext:
531 print "Backport %s"%self.backport_ext.name
533 names = [e.name for e in self.source_exts]
534 if self.base_version:
535 names.insert(0, "Version %s"%self.base_version)
536 print "Sources %s"%", ".join(names)
538 def write_header_intro(self, out):
539 out.write("#ifndef MSP_GL_%s_\n"%self.ext_name.upper())
540 out.write("#define MSP_GL_%s_\n"%self.ext_name.upper())
543 #include <msp/gl/extension.h>
544 #include <msp/gl/gl.h>
551 def write_enum_definitions(self, out):
552 enums_by_category = {}
555 supp = e.api_support.get(self.host_api.name)
557 if supp.core_version:
558 cat = "%s_%s"%(self.api_prefix, supp.core_version.as_define())
559 elif supp.extensions:
560 cat = "GL_"+supp.extensions[0].name
561 enums_by_category.setdefault(cat, []).append(e)
563 for cat in sorted(enums_by_category.keys()):
565 out.write("#ifndef %s\n"%cat)
566 for e in enums_by_category[cat]:
567 out.write("#define %s 0x%04X\n"%(e.name, e.value))
569 out.write("#endif\n")
572 def write_function_pointer_declarations(self, out):
574 typedef = self.func_typedefs[f.name]
575 out.write("typedef %s (APIENTRY *%s)(%s);\n"%(f.return_type, typedef, ", ".join(f.params)))
576 out.write("extern %s %s;\n"%(typedef, f.name))
579 def write_header_outro(self, out):
587 def write_source_intro(self, out):
588 out.write("#include \"%s.h\"\n"%self.ext_name.lower())
592 #define GET_PROC_ADDRESS(x) &::x
594 #define GET_PROC_ADDRESS(x) get_proc_address(#x)
598 #define GET_PROC_ADDRESS_1_1(x) &::x
600 #define GET_PROC_ADDRESS_1_1(x) GET_PROC_ADDRESS(x)
609 def write_function_pointer_definitions(self, out):
611 out.write("%s %s = 0;\n"%(self.func_typedefs[f.name], f.name))
613 def write_init_function(self, out):
614 out.write("\nExtension::SupportLevel init_%s()\n{\n"%self.ext_name.lower())
615 if self.core_version:
616 out.write("\tif(is_disabled(\"GL_%s\"))\n\t\treturn Extension::UNSUPPORTED;\n"%self.ext_name)
617 out.write("#if !defined(__APPLE__) || defined(%s_%s)\n"%(self.api_prefix, self.core_version.as_define()))
619 if self.backport_ext:
620 out.write("is_supported(\"GL_%s\") || "%self.backport_ext.name)
621 out.write("is_supported(%r"%self.core_version)
622 if self.deprecated_version:
623 out.write(", %r"%self.deprecated_version)
624 out.write("))\n\t{\n")
626 supp = f.api_support.get(self.host_api.name)
629 if supp.core_version is not None and supp.core_version<=Version(1, 1):
631 out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS%s(%s));\n"%(f.name, self.func_typedefs[f.name], gpa_suffix, f.name))
632 out.write("\t\treturn Extension::CORE;\n")
634 out.write("#endif\n")
636 out.write("#if !defined(__APPLE__) || defined(GL_%s)\n"%self.ext_name)
638 if self.base_version:
639 out.write("is_supported(%r) && "%self.base_version)
640 out.write("%s)\n\t{\n"%" && ".join("is_supported(\"GL_%s\")"%s.name for s in self.source_exts))
642 supp = f.api_support.get(self.host_api.name)
644 for e in self.source_exts:
645 if f.name in e.things:
648 for s in supp.sources:
649 if s.name in e.things:
654 if not src and supp and supp.core_version and self.base_version>=supp.core_version:
658 out.write("\t\t%s = reinterpret_cast<%s>(GET_PROC_ADDRESS(%s));\n"%(f.name, self.func_typedefs[f.name], src.name))
659 out.write("\t\treturn Extension::EXTENSION;\n")
661 out.write("#endif\n")
662 out.write("\treturn Extension::UNSUPPORTED;\n")
665 def write_source_outro(self, out):
671 def write_header(self, fn):
673 self.write_header_intro(out)
674 self.write_enum_definitions(out)
675 self.write_function_pointer_declarations(out)
676 out.write("extern Extension %s;\n"%self.ext_name)
677 self.write_header_outro(out)
679 def write_source(self, fn):
681 self.write_source_intro(out)
682 self.write_function_pointer_definitions(out)
683 self.write_init_function(out)
684 out.write("\nExtension %s(\"GL_%s\", init_%s);\n"%(self.ext_name, self.ext_name, self.ext_name.lower()))
685 self.write_source_outro(out)
688 def dump_api_support(supp, api, indent):
689 if supp.core_version:
690 print indent+"core in version "+str(supp.core_version)
691 if supp.deprecated_version:
692 print indent+"deprecated in version "+str(supp.deprecated_version)
693 for e in supp.extensions:
694 print indent+"extension %s (preference %d)"%(e.name, e.preference)
695 for r in supp.sources:
696 print indent+"source "+r.name
697 dump_thing_info(r, api, indent+" ")
699 def dump_thing_info(thing, api, indent):
700 for a in thing.aliases:
701 print indent+"alias "+a
703 supp = thing.api_support.get(api)
704 dump_api_support(supp, api, indent)
706 for a, s in thing.api_support.iteritems():
707 print indent+"api "+a
708 dump_api_support(s, a, indent+" ")
711 class ExtensionParser:
712 def __init__(self, host_api):
713 self.host_api = host_api
714 self.target_ext = None
715 self.core_version = None
716 self.deprecated_version = None
717 self.backport_ext = None
718 self.source_exts = []
719 self.ignore_things = []
720 self.optional_things = []
723 for line in open(fn):
725 if not line or line.startswith("#"):
732 api, keyword = keyword.split(":")
734 if api is not None and api!=self.host_api:
737 if keyword=="extension":
738 self.target_ext = parts[1]
739 elif keyword=="core_version":
740 self.core_version = Version(*map(int, parts[1].split('.')))
741 elif keyword=="deprecated":
742 self.deprecated_version = Version(*map(int, parts[1].split('.')))
743 elif keyword=="backport":
744 self.backport_ext = parts[1]
745 elif keyword=="source":
746 self.source_exts.append(parts[1])
747 elif keyword=="ignore":
748 self.ignore_things.append(parts[1])
749 elif keyword=="optional":
750 self.optional_things.append(parts[1])
752 print "Unknown keyword "+keyword
758 def get_extension(api_map, ext_name):
760 ext_api_name, ext_name = ext_name.split(".")
764 return api_map[ext_api_name].extensions[ext_name]
766 def resolve_things(api, things):
769 ct = filter(None, map(api.core_things.get, t.aliases))
777 def collect_extension_things(host_api, target_ext, ignore):
778 ext_things = [t for n, t in target_ext.things.iteritems() if n not in ignore]
779 return resolve_things(target_ext.api, ext_things)
781 def collect_optional_things(target_ext, names):
784 if t in target_ext.things:
785 things.append(target_ext.things[t])
787 things.append(target_ext.api.core_things[t])
788 return resolve_things(target_ext.api, things)
793 extgen.py [api] <extfile> [<outfile>]
795 Reads gl.xml and generates C++ source files to use an OpenGL extension
796 described in <extfile>. If <outfile> is absent, the extension's lowercased
797 name is used. Anything after the last dot in <outfile> is removed and
798 replaced with cpp and h."""
804 if sys.argv[i]=="-g":
809 if sys.argv[i].startswith("gl"):
810 host_api_name = sys.argv[i]
813 ext_parser = ExtensionParser(host_api_name)
814 if not ext_parser.parse(sys.argv[i]):
819 out_base = os.path.splitext(sys.argv[i])[0]
821 out_base = ext_parser.target_ext.lower()
823 xml_parser = GlXmlParser()
824 xml_parser.parse_file("gl.xml")
825 xml_parser.parse_file("gl.fixes.xml")
826 xml_parser.parse_file("gl.msp.xml")
827 xml_parser.finalize()
829 host_api = xml_parser.apis[host_api_name]
830 target_ext = get_extension(xml_parser.apis, ext_parser.target_ext)
831 things = collect_extension_things(host_api, target_ext, ext_parser.ignore_things+ext_parser.optional_things)
832 optional_things = collect_optional_things(target_ext, ext_parser.optional_things)
835 print "--- Things included in this extension ---"
836 all_things = things+optional_things
837 all_things.sort(key=(lambda t: t.name))
840 if t in optional_things:
842 dump_thing_info(t, None, " ")
844 generator = SourceGenerator(host_api, target_ext.name, things, optional_things, debug)
845 if ext_parser.core_version:
846 generator.core_version = ext_parser.core_version
847 if ext_parser.deprecated_version:
848 generator.deprecated_version = ext_parser.deprecated_version
849 if ext_parser.backport_ext:
850 if ext_parser.backport_ext=="none":
851 generator.backport_ext = None
853 generator.backport_ext = get_extension(xml_parser.apis, ext_parser.backport_ext)
854 if ext_parser.source_exts:
855 generator.base_version = None
856 if len(ext_parser.source_exts)==1 and ext_parser.source_exts[0]=="none":
857 generator.source_exts = []
859 generator.source_exts = map((lambda e: get_extension(xml_parser.apis, e)), ext_parser.source_exts)
861 generator.dump_info()
862 generator.write_header(out_base+".h")
863 generator.write_source(out_base+".cpp")
865 if __name__=="__main__":