+++ /dev/null
-#!/usr/bin/env python3
-#
-# Copyright (c) 2016 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import argparse
-import ctypes
-import json
-import os
-import platform
-import sys
-import xml.etree.ElementTree
-
-if platform.system() == "Windows":
- VKAPI_DLL = ctypes.windll
- VKAPI_FUNCTYPE = ctypes.WINFUNCTYPE
-else:
- VKAPI_DLL = ctypes.cdll
- VKAPI_FUNCTYPE = ctypes.CFUNCTYPE
-
-# Vulkan types
-
-VkInstance = ctypes.c_void_p
-VkPhysicalDevice = ctypes.c_void_p
-VkDevice = ctypes.c_void_p
-VkResult = ctypes.c_int
-
-
-class VkLayerProperties(ctypes.Structure):
- _fields_ = [("c_layerName", ctypes.c_char * 256),
- ("c_specVersion", ctypes.c_uint32),
- ("c_implementationVersion", ctypes.c_uint32),
- ("c_description", ctypes.c_char * 256)]
-
- def layer_name(self):
- return self.c_layerName.decode()
-
- def spec_version(self):
- return "%d.%d.%d" % (
- self.c_specVersion >> 22,
- (self.c_specVersion >> 12) & 0x3ff,
- self.c_specVersion & 0xfff)
-
- def implementation_version(self):
- return str(self.c_implementationVersion)
-
- def description(self):
- return self.c_description.decode()
-
- def __eq__(self, other):
- return (self.c_layerName == other.c_layerName and
- self.c_specVersion == other.c_specVersion and
- self.c_implementationVersion == other.c_implementationVersion and
- self.c_description == other.c_description)
-
-
-class VkExtensionProperties(ctypes.Structure):
- _fields_ = [("c_extensionName", ctypes.c_char * 256),
- ("c_specVersion", ctypes.c_uint32)]
-
- def extension_name(self):
- return self.c_extensionName.decode()
-
- def spec_version(self):
- return str(self.c_specVersion)
-
-# Vulkan commands
-
-PFN_vkVoidFunction = VKAPI_FUNCTYPE(None)
-PFN_vkEnumerateInstanceExtensionProperties = VKAPI_FUNCTYPE(
- VkResult, ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(VkExtensionProperties))
-PFN_vkEnumerateDeviceExtensionProperties = VKAPI_FUNCTYPE(
- VkResult, VkPhysicalDevice, ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(VkExtensionProperties))
-PFN_vkEnumerateInstanceLayerProperties = VKAPI_FUNCTYPE(
- VkResult, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(VkLayerProperties))
-PFN_vkEnumerateDeviceLayerProperties = VKAPI_FUNCTYPE(
- VkResult, VkPhysicalDevice, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(VkLayerProperties))
-PFN_vkGetInstanceProcAddr = VKAPI_FUNCTYPE(
- PFN_vkVoidFunction, VkInstance, ctypes.c_char_p)
-PFN_vkGetDeviceProcAddr = VKAPI_FUNCTYPE(
- PFN_vkVoidFunction, VkDevice, ctypes.c_char_p)
-
-
-class Layer(object):
-
- def __init__(self, *args):
- self.props = args[0]
- self.is_global = args[1]
- self.instance_extensions = args[2]
- self.device_extensions = args[3]
- self.gipa_name = args[4]
- self.gdpa_name = args[5]
-
-
-class LayerLibrary(object):
-
- def __init__(self, path):
- self.library = None
- self.version = 0
-
- self._load(path)
- self._negotiate_version()
-
- def introspect(self):
- if self.version == 0:
- layers = self._enumerate_layers_v0()
- else:
- raise RuntimeError("unsupported v%d library" % self.version)
-
- return layers
-
- def _load(self, path):
- try:
- abspath = os.path.abspath(path)
- self.library = VKAPI_DLL.LoadLibrary(abspath)
- except OSError:
- raise RuntimeError("failed to load library")
-
- def _unload(self):
- # no clean way to unload
- pass
-
- def _negotiate_version(self):
- # only v0
- self.version = 0
-
- def _enumerate_properties_errcheck_v0(self, result, func, args):
- if isinstance(func, PFN_vkEnumerateInstanceLayerProperties):
- func_name = "vkEnumerateInstanceLayerProperties"
- elif isinstance(func, PFN_vkEnumerateDeviceLayerProperties):
- func_name = "vkEnumerateDeviceLayerProperties"
- elif isinstance(func, PFN_vkEnumerateInstanceExtensionProperties):
- func_name = "vkEnumerateInstanceExtensionProperties"
- elif isinstance(func, PFN_vkEnumerateDeviceExtensionProperties):
- func_name = "vkEnumerateDeviceExtensionProperties"
- else:
- raise AssertionError("unexpected vkEnumerate*Properties call")
-
- if result != 0:
- raise RuntimeError(func_name + " failed with " + str(result))
-
- # pProperties and pCount mismatch
- if args[-1] and len(args[-1]) != args[-2].value:
- raise RuntimeError("invalid pCount returned in " + func_name)
-
- return args[-1]
-
- def _enumerate_properties_prototype_v0(self, func_name):
- prototypes = {
- "vkEnumerateInstanceLayerProperties":
- PFN_vkEnumerateInstanceLayerProperties,
- "vkEnumerateDeviceLayerProperties":
- PFN_vkEnumerateDeviceLayerProperties,
- "vkEnumerateInstanceExtensionProperties":
- PFN_vkEnumerateInstanceExtensionProperties,
- "vkEnumerateDeviceExtensionProperties":
- PFN_vkEnumerateDeviceExtensionProperties,
- }
- prototype = prototypes[func_name]
-
- try:
- proc = prototype((func_name, self.library))
- except AttributeError:
- raise RuntimeError(func_name + " is missing")
-
- proc.errcheck = self._enumerate_properties_errcheck_v0
-
- return proc
-
- def _get_gipa_name_v0(self, layer_name, can_fallback):
- names = [layer_name + "GetInstanceProcAddr"]
- if can_fallback:
- names.append("vkGetInstanceProcAddr")
-
- for name in names:
- try:
- PFN_vkGetInstanceProcAddr((name, self.library))
- return name
- except AttributeError:
- pass
-
- raise RuntimeError(" or ".join(names) + " is missing")
-
- def _get_gdpa_name_v0(self, layer_name, can_fallback):
- names = [layer_name + "GetDeviceProcAddr"]
- if can_fallback:
- names.append("vkGetDeviceProcAddr")
-
- for name in names:
- try:
- PFN_vkGetDeviceProcAddr((name, self.library))
- return name
- except AttributeError:
- pass
-
- raise RuntimeError(" or ".join(names) + " is missing")
-
- def _enumerate_layers_v0(self):
- tmp_count = ctypes.c_uint32()
-
- # enumerate instance layers
- enumerate_instance_layer_properties = self._enumerate_properties_prototype_v0(
- "vkEnumerateInstanceLayerProperties")
- enumerate_instance_layer_properties(tmp_count, None)
- p_props = enumerate_instance_layer_properties(
- tmp_count, (VkLayerProperties * tmp_count.value)())
-
- # enumerate device layers
- enumerate_device_layer_properties = self._enumerate_properties_prototype_v0(
- "vkEnumerateDeviceLayerProperties")
- enumerate_device_layer_properties(None, tmp_count, None)
- dev_p_props = enumerate_device_layer_properties(
- None, tmp_count, (VkLayerProperties * tmp_count.value)())
-
- # there must not be device-only layers
- for props in dev_p_props:
- if props not in p_props:
- raise RuntimeError(
- "unexpected device-only layer " + props.layer_name())
-
- layers = []
- for props in p_props:
- is_global = (props in dev_p_props)
-
- # enumerate instance extensions
- enumerate_instance_extension_properties = self._enumerate_properties_prototype_v0(
- "vkEnumerateInstanceExtensionProperties")
- enumerate_instance_extension_properties(
- props.c_layerName, tmp_count, None)
- instance_extensions = enumerate_instance_extension_properties(
- props.c_layerName,
- tmp_count,
- (VkExtensionProperties * tmp_count.value)())
-
- gipa_name = self._get_gipa_name_v0(
- props.layer_name(),
- len(p_props) == 1)
-
- if is_global:
- # enumerate device extensions
- enumerate_device_extension_properties = self._enumerate_properties_prototype_v0(
- "vkEnumerateDeviceExtensionProperties")
- enumerate_device_extension_properties(
- None, props.c_layerName, tmp_count, None)
- device_extensions = enumerate_device_extension_properties(
- None,
- props.c_layerName,
- tmp_count,
- (VkExtensionProperties * tmp_count.value)())
-
- gdpa_name = self._get_gdpa_name_v0(
- props.layer_name(),
- len(p_props) == 1)
- else:
- device_extensions = None
- gdpa_name = None
-
- layers.append(
- Layer(props, is_global, instance_extensions, device_extensions, gipa_name, gdpa_name))
-
- return layers
-
-
-def serialize_layers(layers, path, ext_cmds):
- data = {}
- data["file_format_version"] = '1.0.0'
-
- for idx, layer in enumerate(layers):
- layer_data = {}
-
- layer_data["name"] = layer.props.layer_name()
- layer_data["api_version"] = layer.props.spec_version()
- layer_data[
- "implementation_version"] = layer.props.implementation_version()
- layer_data["description"] = layer.props.description()
-
- layer_data["type"] = "GLOBAL" if layer.is_global else "INSTANCE"
-
- # TODO more flexible
- layer_data["library_path"] = os.path.join(".", os.path.basename(path))
-
- funcs = {}
- if layer.gipa_name != "vkGetInstanceProcAddr":
- funcs["vkGetInstanceProcAddr"] = layer.gipa_name
- if layer.is_global and layer.gdpa_name != "vkGetDeviceProcAddr":
- funcs["vkGetDeviceProcAddr"] = layer.gdpa_name
- if funcs:
- layer_data["functions"] = funcs
-
- if layer.instance_extensions:
- exts = [{
- "name": ext.extension_name(),
- "spec_version": ext.spec_version(),
- } for ext in layer.instance_extensions]
- layer_data["instance_extensions"] = exts
-
- if layer.device_extensions:
- exts = []
- for ext in layer.device_extensions:
- try:
- cmds = ext_cmds[ext.extension_name()]
- except KeyError:
- raise RuntimeError(
- "unknown device extension " + ext.extension_name())
- else:
- ext_data = {}
- ext_data["name"] = ext.extension_name()
- ext_data["spec_version"] = ext.spec_version()
- if cmds:
- ext_data["entrypoints"] = cmds
-
- exts.append(ext_data)
-
- layer_data["device_extensions"] = exts
-
- if idx > 0:
- data["layer.%d" % idx] = layer_data
- else:
- data["layer"] = layer_data
-
- return data
-
-
-def dump_json(data):
- dump = json.dumps(data, indent=4, sort_keys=True)
-
- # replace "layer.<idx>" by "layer"
- lines = dump.split("\n")
- for line in lines:
- if line.startswith(" \"layer.") and line.endswith("\": {"):
- line = " \"layer\": {"
- print(line)
-
-
-def parse_vk_xml(path):
- """Parse vk.xml to get commands added by extensions."""
- tree = xml.etree.ElementTree.parse(path)
- extensions = tree.find("extensions")
-
- ext_cmds = {}
- for ext in extensions.iter("extension"):
- if ext.attrib["supported"] != "vulkan":
- continue
-
- cmds = []
- for cmd in ext.iter("command"):
- cmds.append(cmd.attrib["name"])
-
- ext_cmds[ext.attrib["name"]] = cmds
-
- return ext_cmds
-
-
-def add_custom_ext_cmds(ext_cmds):
- """Add commands added by in-development extensions."""
- # VK_LAYER_LUNARG_basic
- ext_cmds["vkLayerBasicEXT"] = ["vkLayerBasicEXT"]
-
-
-def main():
- default_vk_xml = sys.path[0] + "/vk.xml" if sys.path[0] else "vk.xml"
-
- parser = argparse.ArgumentParser(description="Introspect a layer library.")
- parser.add_argument(
- "-x", dest="vk_xml", default=default_vk_xml, help="Path to vk.xml")
- parser.add_argument(
- "layer_libs", metavar="layer-lib", nargs="+", help="Path to a layer library")
- args = parser.parse_args()
-
- try:
- ext_cmds = parse_vk_xml(args.vk_xml)
- except Exception as e:
- print("failed to parse %s: %s" % (args.vk_xml, e))
- sys.exit(-1)
-
- add_custom_ext_cmds(ext_cmds)
-
- for path in args.layer_libs:
- try:
- ll = LayerLibrary(path)
- layers = ll.introspect()
- data = serialize_layers(layers, path, ext_cmds)
- dump_json(data)
- except RuntimeError as err:
- print("skipping %s: %s" % (path, err))
-
-if __name__ == "__main__":
- main()