+ requires = get_nested_elements(feat, "require")
+ for req in requires:
+ commands = get_nested_elements(req, "command")
+ enums = get_nested_elements(req, "enum")
+ for t in itertools.chain(commands, enums):
+ name = t.getAttribute("name")
+ thing = self.things.get(name)
+ if thing:
+ supp = thing.get_or_create_api_support(api.name)
+ if not supp.core_version or version<supp.core_version:
+ supp.core_version = version
+ api.core_things[thing.name] = thing
+
+ removes = get_nested_elements(feat, "remove")
+ for rem in removes:
+ commands = get_nested_elements(rem, "command")
+ enums = get_nested_elements(rem, "enum")
+
+ for t in itertools.chain(commands, enums):
+ name = t.getAttribute("name")
+ thing = self.things.get(name)
+ if thing:
+ supp = thing.get_or_create_api_support(api.name)
+ supp.deprecated_version = version
+
+ def parse_extension(self, ext):
+ ext_things_by_api = {}
+ requires = get_nested_elements(ext, "require")
+ for req in requires:
+ api = req.getAttribute("api")
+ ext_things = ext_things_by_api.setdefault(api, [])
+
+ commands = get_nested_elements(req, "command")
+ enums = get_nested_elements(req, "enum")
+ for t in itertools.chain(commands, enums):
+ name = t.getAttribute("name")
+ thing = self.things.get(name)
+ if thing:
+ ext_things.append(thing)
+
+ ext_name = ext.getAttribute("name")
+ if ext_name.startswith("GL_"):
+ ext_name = ext_name[3:]
+
+ common_things = ext_things_by_api.get("", [])
+ supported = ext.getAttribute("supported").split('|')
+ for s in supported:
+ api = self.apis.get(s)
+ if not api:
+ continue
+
+ ext = get_or_create(api.extensions, ext_name, Extension, api)
+ api_things = ext_things_by_api.get(s, [])
+ for t in itertools.chain(common_things, api_things):
+ ext.things[t.name] = t
+ t.get_or_create_api_support(api.name).extensions.append(ext)
+
+ def parse_file(self, fn):
+ doc = xml.dom.minidom.parse(fn)
+ root = doc.documentElement
+
+ commands = get_nested_elements(root, "commands/command")
+ for cmd in commands:
+ self.parse_command(cmd)
+
+ enums = get_nested_elements(root, "enums/enum")
+ for en in enums:
+ self.parse_enum(en)
+
+ features = get_nested_elements(root, "feature")
+ for feat in features:
+ self.parse_feature(feat)
+
+ extensions = get_nested_elements(root, "extensions/extension")
+ for ext in extensions:
+ self.parse_extension(ext)
+
+ def check_backport_extensions(self, api):
+ for e in api.extensions.values():
+ if e.ext_type!="ARB":
+ continue
+
+ e.backport = True
+ for t in e.things.values():
+ if t.name.endswith(e.ext_type):
+ e.backport = False
+ break
+
+ def resolve_enum_aliases(self, api):
+ for e in api.extensions.values():
+ enum_suffix = "_"+e.ext_type
+ for n in (t for t in e.things.values() if t.kind==Thing.ENUM):
+ if n.api_support[api.name].core_version:
+ continue
+
+ name = n.name
+ if name.endswith(enum_suffix):
+ name = name[:-len(enum_suffix)]
+ ce = api.core_things.get(name)
+ if ce and ce.value==n.value and ce.name not in n.aliases:
+ n.aliases.append(ce.name)
+
+ def resolve_sources(self, api):
+ for e in api.extensions.values():
+ for t in e.things.values():
+ for a in t.aliases:
+ # There are a few cases where a vendor function is aliased to
+ # an EXT or ARB function but those are rare and not relevant for
+ # our use
+ alias = api.core_things.get(a)
+ if alias:
+ sources = alias.api_support[api.name].sources
+ if t not in sources:
+ sources.append(t)
+
+ def sort_extensions(self):
+ for t in self.things.values():
+ for s in t.api_support.values():
+ s.extensions.sort(key=(lambda e: e.preference), reverse=True)
+
+ def finalize(self):
+ for a in self.apis.values():
+ self.check_backport_extensions(a)
+ self.resolve_enum_aliases(a)
+ self.resolve_sources(a)
+ self.sort_extensions()
+
+
+def detect_core_version(host_api, things, debug=None):
+ max_version = Version(1, 0)
+ max_count = 0
+ lower_count = 0
+ missing = []
+ for t in things:
+ supp = t.api_support.get(host_api.name)
+ if supp and supp.core_version:
+ if supp.core_version>max_version:
+ max_version = supp.core_version
+ lower_count += max_count
+ max_count = 1
+ elif supp.core_version==max_version:
+ max_count += 1
+ else:
+ lower_count += 1
+ else:
+ missing.append(t)
+
+ if lower_count>max_count or (missing and len(missing)*2<lower_count+max_count):
+ print("Warning: Inconsistent core version {}".format(max_version))
+
+ if missing:
+ if debug:
+ print("---")
+ print("{} things missing from core:".format(len(missing)))
+ for t in missing:
+ print(" "+t.name)
+ return None
+
+ return max_version
+
+def detect_deprecated_version(host_api, things, debug):
+ min_version = None
+ deprecated = []
+ for t in things:
+ supp = t.api_support.get(host_api.name)
+ if supp and supp.deprecated_version:
+ if min_version is None:
+ min_version = supp.deprecated_version
+ else:
+ min_version = min(min_version, supp.deprecated_version)
+ deprecated.append(t)
+
+ if min_version and len(deprecated)*2<len(things):
+ print("Warning: Inconsistent deprecation version {}".format(min_version))
+ if debug:
+ print("---")
+ print("{} things are deprecated:".format(len(deprecated)))
+ for t in deprecated:
+ print(" "+t.name)
+
+ return min_version
+
+def detect_backport_extension(host_api, things):
+ candidates = []
+ for t in things:
+ supp = t.api_support.get(host_api.name)
+ if supp and supp.core_version:
+ for e in supp.extensions:
+ if e.backport and e not in candidates:
+ candidates.append(e)
+
+ total_count = len(things)
+ best_ext = None
+ best_count = 0
+ for e in candidates:
+ count = sum(t.name in e.things for t in things)
+ if count==total_count:
+ return e
+ elif count>best_count:
+ best_ext = e
+ best_count = count
+
+ if total_count and best_count*2>=total_count:
+ print("Warning: Inconsistent backport extension {}".format(best_ext.name))
+
+def collect_extensions(thing, api, exts):
+ supp = thing.api_support.get(api)
+ if not supp:
+ return
+
+ for e in supp.extensions:
+ if not e.backport and e.ext_type!="MSP" and e not in exts:
+ exts.append(e)
+
+ for s in supp.sources:
+ collect_extensions(s, api, exts)
+
+def detect_source_extension(host_api, things, debug=False):
+ things_by_ext = {}
+ for t in things:
+ exts = []
+ collect_extensions(t, host_api.name, exts)
+ for e in exts:
+ things_by_ext.setdefault(e, []).append(t)
+
+ if debug:
+ print("---")
+ print("Looking for {} things in {} extensions".format(len(things), len(things_by_ext)))
+
+ extensions = []
+ keep_exts = 0
+ base_version = None
+ recheck_base_version = True
+ missing = set(things)
+ while 1:
+ if recheck_base_version:
+ max_version = Version(1, 0)
+ for t in missing:
+ supp = t.api_support.get(host_api.name)
+ if supp and supp.core_version and max_version:
+ max_version = max(max_version, supp.core_version)
+ else:
+ max_version = None
+
+ if max_version:
+ if not base_version or max_version<base_version:
+ base_version = max_version
+ keep_exts = len(extensions)
+ elif not base_version:
+ keep_exts = len(extensions)
+
+ recheck_base_version = False
+
+ if not missing or not things_by_ext:
+ break
+
+ largest_ext = None
+ largest_count = 0
+ for e, t in things_by_ext.items():
+ count = len(t)
+ if count>largest_count:
+ largest_ext = e
+ largest_count = count
+ elif count==largest_count and e.preference>largest_ext.preference:
+ largest_ext = e
+
+ if debug:
+ print("Found {} things in {}".format(largest_count, largest_ext.name))
+
+ extensions.append(largest_ext)
+ for t in things_by_ext[largest_ext]:
+ missing.remove(t)
+
+ supp = t.api_support.get(host_api.name)
+ if supp and supp.core_version==base_version:
+ recheck_base_version = True
+
+ del things_by_ext[largest_ext]
+ for e in list(things_by_ext.keys()):
+ unseen = [t for t in things_by_ext[e] if t in missing]
+ if unseen:
+ things_by_ext[e] = unseen
+ else:
+ del things_by_ext[e]
+
+ if not missing:
+ return None, extensions
+ elif base_version:
+ if debug:
+ print("Found remaining things in version {}".format(base_version))
+ if keep_exts<len(extensions):
+ print("Discarding {} extensions that do not improve base version".format(len(extensions)-keep_exts))
+ del extensions[keep_exts:]
+ return base_version, extensions
+ else:
+ if debug:
+ print("{} things still missing:".format(len(missing)))
+ for t in missing:
+ print(" "+t.name)
+ return None, None
+
+
+class SourceGenerator:
+ def __init__(self, host_api, ext_name, things, optional_things, debug=False):
+ self.host_api = host_api
+ self.api_prefix = "GL"
+ if self.host_api.name=="gles2":
+ self.api_prefix = "GL_ES"
+ self.ext_name = ext_name
+ all_things = things+optional_things
+ self.funcs = [t for t in all_things if t.kind==Thing.FUNCTION]
+ self.funcs.sort(key=(lambda f: f.name))
+ self.func_typedefs = dict((f.name, "FPtr_"+f.name) for f in self.funcs)
+ self.enums = [t for t in all_things if t.kind==Thing.ENUM]
+ self.enums.sort(key=(lambda e: e.value))
+ self.core_version = detect_core_version(host_api, things, debug)
+ self.deprecated_version = detect_deprecated_version(host_api, things, debug)
+ self.backport_ext = detect_backport_extension(host_api, things)
+ b, e = detect_source_extension(host_api, things, debug)
+ self.base_version = b
+ self.source_exts = e
+
+ if not self.core_version and not self.backport_ext and not self.source_exts:
+ print("Warning: Not supportable on host API")
+
+ def dump_info(self):
+ print("--- Extension information ---")
+ print("Extension {}".format(self.ext_name))
+ print("Core {}".format(self.core_version))
+ print("Deprecated {}".format(self.deprecated_version))
+ if self.backport_ext:
+ print("Backport {}".format(self.backport_ext.name))
+ if self.source_exts:
+ names = [e.name for e in self.source_exts]
+ if self.base_version:
+ names.insert(0, "Version {}".format(self.base_version))
+ print("Sources {}".format(", ".join(names)))
+
+ def write_header_intro(self, out):
+ out.write("#ifndef MSP_GL_{}_\n".format(self.ext_name.upper()))
+ out.write("#define MSP_GL_{}_\n".format(self.ext_name.upper()))
+
+ out.write("""