3 # Copyright (c) 2016 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
23 import xml.etree.ElementTree
25 if platform.system() == "Windows":
26 VKAPI_DLL = ctypes.windll
27 VKAPI_FUNCTYPE = ctypes.WINFUNCTYPE
29 VKAPI_DLL = ctypes.cdll
30 VKAPI_FUNCTYPE = ctypes.CFUNCTYPE
34 VkInstance = ctypes.c_void_p
35 VkPhysicalDevice = ctypes.c_void_p
36 VkDevice = ctypes.c_void_p
37 VkResult = ctypes.c_int
40 class VkLayerProperties(ctypes.Structure):
41 _fields_ = [("c_layerName", ctypes.c_char * 256),
42 ("c_specVersion", ctypes.c_uint32),
43 ("c_implementationVersion", ctypes.c_uint32),
44 ("c_description", ctypes.c_char * 256)]
47 return self.c_layerName.decode()
49 def spec_version(self):
51 self.c_specVersion >> 22,
52 (self.c_specVersion >> 12) & 0x3ff,
53 self.c_specVersion & 0xfff)
55 def implementation_version(self):
56 return str(self.c_implementationVersion)
58 def description(self):
59 return self.c_description.decode()
61 def __eq__(self, other):
62 return (self.c_layerName == other.c_layerName and
63 self.c_specVersion == other.c_specVersion and
64 self.c_implementationVersion == other.c_implementationVersion and
65 self.c_description == other.c_description)
68 class VkExtensionProperties(ctypes.Structure):
69 _fields_ = [("c_extensionName", ctypes.c_char * 256),
70 ("c_specVersion", ctypes.c_uint32)]
72 def extension_name(self):
73 return self.c_extensionName.decode()
75 def spec_version(self):
76 return str(self.c_specVersion)
80 PFN_vkVoidFunction = VKAPI_FUNCTYPE(None)
81 PFN_vkEnumerateInstanceExtensionProperties = VKAPI_FUNCTYPE(
82 VkResult, ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(VkExtensionProperties))
83 PFN_vkEnumerateDeviceExtensionProperties = VKAPI_FUNCTYPE(
84 VkResult, VkPhysicalDevice, ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(VkExtensionProperties))
85 PFN_vkEnumerateInstanceLayerProperties = VKAPI_FUNCTYPE(
86 VkResult, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(VkLayerProperties))
87 PFN_vkEnumerateDeviceLayerProperties = VKAPI_FUNCTYPE(
88 VkResult, VkPhysicalDevice, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(VkLayerProperties))
89 PFN_vkGetInstanceProcAddr = VKAPI_FUNCTYPE(
90 PFN_vkVoidFunction, VkInstance, ctypes.c_char_p)
91 PFN_vkGetDeviceProcAddr = VKAPI_FUNCTYPE(
92 PFN_vkVoidFunction, VkDevice, ctypes.c_char_p)
97 def __init__(self, *args):
99 self.is_global = args[1]
100 self.instance_extensions = args[2]
101 self.device_extensions = args[3]
102 self.gipa_name = args[4]
103 self.gdpa_name = args[5]
106 class LayerLibrary(object):
108 def __init__(self, path):
113 self._negotiate_version()
115 def introspect(self):
116 if self.version == 0:
117 layers = self._enumerate_layers_v0()
119 raise RuntimeError("unsupported v%d library" % self.version)
123 def _load(self, path):
125 abspath = os.path.abspath(path)
126 self.library = VKAPI_DLL.LoadLibrary(abspath)
128 raise RuntimeError("failed to load library")
131 # no clean way to unload
134 def _negotiate_version(self):
138 def _enumerate_properties_errcheck_v0(self, result, func, args):
139 if isinstance(func, PFN_vkEnumerateInstanceLayerProperties):
140 func_name = "vkEnumerateInstanceLayerProperties"
141 elif isinstance(func, PFN_vkEnumerateDeviceLayerProperties):
142 func_name = "vkEnumerateDeviceLayerProperties"
143 elif isinstance(func, PFN_vkEnumerateInstanceExtensionProperties):
144 func_name = "vkEnumerateInstanceExtensionProperties"
145 elif isinstance(func, PFN_vkEnumerateDeviceExtensionProperties):
146 func_name = "vkEnumerateDeviceExtensionProperties"
148 raise AssertionError("unexpected vkEnumerate*Properties call")
151 raise RuntimeError(func_name + " failed with " + str(result))
153 # pProperties and pCount mismatch
154 if args[-1] and len(args[-1]) != args[-2].value:
155 raise RuntimeError("invalid pCount returned in " + func_name)
159 def _enumerate_properties_prototype_v0(self, func_name):
161 "vkEnumerateInstanceLayerProperties":
162 PFN_vkEnumerateInstanceLayerProperties,
163 "vkEnumerateDeviceLayerProperties":
164 PFN_vkEnumerateDeviceLayerProperties,
165 "vkEnumerateInstanceExtensionProperties":
166 PFN_vkEnumerateInstanceExtensionProperties,
167 "vkEnumerateDeviceExtensionProperties":
168 PFN_vkEnumerateDeviceExtensionProperties,
170 prototype = prototypes[func_name]
173 proc = prototype((func_name, self.library))
174 except AttributeError:
175 raise RuntimeError(func_name + " is missing")
177 proc.errcheck = self._enumerate_properties_errcheck_v0
181 def _get_gipa_name_v0(self, layer_name, can_fallback):
182 names = [layer_name + "GetInstanceProcAddr"]
184 names.append("vkGetInstanceProcAddr")
188 PFN_vkGetInstanceProcAddr((name, self.library))
190 except AttributeError:
193 raise RuntimeError(" or ".join(names) + " is missing")
195 def _get_gdpa_name_v0(self, layer_name, can_fallback):
196 names = [layer_name + "GetDeviceProcAddr"]
198 names.append("vkGetDeviceProcAddr")
202 PFN_vkGetDeviceProcAddr((name, self.library))
204 except AttributeError:
207 raise RuntimeError(" or ".join(names) + " is missing")
209 def _enumerate_layers_v0(self):
210 tmp_count = ctypes.c_uint32()
212 # enumerate instance layers
213 enumerate_instance_layer_properties = self._enumerate_properties_prototype_v0(
214 "vkEnumerateInstanceLayerProperties")
215 enumerate_instance_layer_properties(tmp_count, None)
216 p_props = enumerate_instance_layer_properties(
217 tmp_count, (VkLayerProperties * tmp_count.value)())
219 # enumerate device layers
220 enumerate_device_layer_properties = self._enumerate_properties_prototype_v0(
221 "vkEnumerateDeviceLayerProperties")
222 enumerate_device_layer_properties(None, tmp_count, None)
223 dev_p_props = enumerate_device_layer_properties(
224 None, tmp_count, (VkLayerProperties * tmp_count.value)())
226 # there must not be device-only layers
227 for props in dev_p_props:
228 if props not in p_props:
230 "unexpected device-only layer " + props.layer_name())
233 for props in p_props:
234 is_global = (props in dev_p_props)
236 # enumerate instance extensions
237 enumerate_instance_extension_properties = self._enumerate_properties_prototype_v0(
238 "vkEnumerateInstanceExtensionProperties")
239 enumerate_instance_extension_properties(
240 props.c_layerName, tmp_count, None)
241 instance_extensions = enumerate_instance_extension_properties(
244 (VkExtensionProperties * tmp_count.value)())
246 gipa_name = self._get_gipa_name_v0(
251 # enumerate device extensions
252 enumerate_device_extension_properties = self._enumerate_properties_prototype_v0(
253 "vkEnumerateDeviceExtensionProperties")
254 enumerate_device_extension_properties(
255 None, props.c_layerName, tmp_count, None)
256 device_extensions = enumerate_device_extension_properties(
260 (VkExtensionProperties * tmp_count.value)())
262 gdpa_name = self._get_gdpa_name_v0(
266 device_extensions = None
270 Layer(props, is_global, instance_extensions, device_extensions, gipa_name, gdpa_name))
275 def serialize_layers(layers, path, ext_cmds):
277 data["file_format_version"] = '1.0.0'
279 for idx, layer in enumerate(layers):
282 layer_data["name"] = layer.props.layer_name()
283 layer_data["api_version"] = layer.props.spec_version()
285 "implementation_version"] = layer.props.implementation_version()
286 layer_data["description"] = layer.props.description()
288 layer_data["type"] = "GLOBAL" if layer.is_global else "INSTANCE"
291 layer_data["library_path"] = os.path.join(".", os.path.basename(path))
294 if layer.gipa_name != "vkGetInstanceProcAddr":
295 funcs["vkGetInstanceProcAddr"] = layer.gipa_name
296 if layer.is_global and layer.gdpa_name != "vkGetDeviceProcAddr":
297 funcs["vkGetDeviceProcAddr"] = layer.gdpa_name
299 layer_data["functions"] = funcs
301 if layer.instance_extensions:
303 "name": ext.extension_name(),
304 "spec_version": ext.spec_version(),
305 } for ext in layer.instance_extensions]
306 layer_data["instance_extensions"] = exts
308 if layer.device_extensions:
310 for ext in layer.device_extensions:
312 cmds = ext_cmds[ext.extension_name()]
315 "unknown device extension " + ext.extension_name())
318 ext_data["name"] = ext.extension_name()
319 ext_data["spec_version"] = ext.spec_version()
321 ext_data["entrypoints"] = cmds
323 exts.append(ext_data)
325 layer_data["device_extensions"] = exts
328 data["layer.%d" % idx] = layer_data
330 data["layer"] = layer_data
336 dump = json.dumps(data, indent=4, sort_keys=True)
338 # replace "layer.<idx>" by "layer"
339 lines = dump.split("\n")
341 if line.startswith(" \"layer.") and line.endswith("\": {"):
342 line = " \"layer\": {"
346 def parse_vk_xml(path):
347 """Parse vk.xml to get commands added by extensions."""
348 tree = xml.etree.ElementTree.parse(path)
349 extensions = tree.find("extensions")
352 for ext in extensions.iter("extension"):
353 if ext.attrib["supported"] != "vulkan":
357 for cmd in ext.iter("command"):
358 cmds.append(cmd.attrib["name"])
360 ext_cmds[ext.attrib["name"]] = cmds
365 def add_custom_ext_cmds(ext_cmds):
366 """Add commands added by in-development extensions."""
367 # VK_LAYER_LUNARG_basic
368 ext_cmds["vkLayerBasicEXT"] = ["vkLayerBasicEXT"]
372 default_vk_xml = sys.path[0] + "/vk.xml" if sys.path[0] else "vk.xml"
374 parser = argparse.ArgumentParser(description="Introspect a layer library.")
376 "-x", dest="vk_xml", default=default_vk_xml, help="Path to vk.xml")
378 "layer_libs", metavar="layer-lib", nargs="+", help="Path to a layer library")
379 args = parser.parse_args()
382 ext_cmds = parse_vk_xml(args.vk_xml)
383 except Exception as e:
384 print("failed to parse %s: %s" % (args.vk_xml, e))
387 add_custom_ext_cmds(ext_cmds)
389 for path in args.layer_libs:
391 ll = LayerLibrary(path)
392 layers = ll.introspect()
393 data = serialize_layers(layers, path, ext_cmds)
395 except RuntimeError as err:
396 print("skipping %s: %s" % (path, err))
398 if __name__ == "__main__":