2 # Wine Vulkan generator
4 # Copyright 2017-2018 Roderick Colenbrander
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2.1 of the License, or (at your option) any later version.
11 # This library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this library; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 import xml.etree.ElementTree as ET
28 from collections import OrderedDict
29 from collections.abc import Sequence
32 # This script generates code for a Wine Vulkan ICD driver from Vulkan's vk.xml.
33 # Generating the code is like 10x worse than OpenGL, which is mostly a calling
34 # convention passthrough.
36 # The script parses vk.xml and maps functions and types to helper objects. These
37 # helper objects simplify the xml parsing and map closely to the Vulkan types.
38 # The code generation utilizes the helper objects during code generation and
39 # most of the ugly work is carried out by these objects.
41 # Vulkan ICD challenges:
42 # - Vulkan ICD loader (vulkan-1.dll) relies on a section at the start of
43 # 'dispatchable handles' (e.g. VkDevice, VkInstance) for it to insert
44 # its private data. It uses this area to stare its own dispatch tables
45 # for loader internal use. This means any dispatchable objects need wrapping.
47 # - Vulkan structures have different alignment between win32 and 32-bit Linux.
48 # This means structures with alignment differences need conversion logic.
49 # Often structures are nested, so the parent structure may not need any
50 # conversion, but some child may need some.
52 # vk.xml parsing challenges:
53 # - Contains type data for all platforms (generic Vulkan, Windows, Linux,..).
54 # Parsing of extension information required to pull in types and functions
55 # we really want to generate. Just tying all the data together is tricky.
57 # - Extensions can affect core types e.g. add new enum values, bitflags or
58 # additional structure chaining through 'pNext' / 'sType'.
60 # - Arrays are used all over the place for parameters or for structure members.
61 # Array length is often stored in a previous parameter or another structure
62 # member and thus needs careful parsing.
64 LOGGER = logging.Logger("vulkan")
65 LOGGER.addHandler(logging.StreamHandler())
67 VK_XML_VERSION = "1.1.76"
69 # Filenames to create.
70 WINE_VULKAN_H = "../../include/wine/vulkan.h"
71 WINE_VULKAN_DRIVER_H = "../../include/wine/vulkan_driver.h"
72 WINE_VULKAN_LOADER_SPEC = "../vulkan-1/vulkan-1.spec"
73 WINE_VULKAN_SPEC = "winevulkan.spec"
74 WINE_VULKAN_THUNKS_C = "vulkan_thunks.c"
75 WINE_VULKAN_THUNKS_H = "vulkan_thunks.h"
77 # Extension enum values start at a certain offset (EXT_BASE).
78 # Relative to the offset each extension has a block (EXT_BLOCK_SIZE)
80 # Start for a given extension is:
81 # EXT_BASE + (extension_number-1) * EXT_BLOCK_SIZE
85 # In general instance extensions can't be automatically generated
86 # and need custom wrappers due to e.g. win32 / X11 specific code.
87 # List of supported instance extensions.
88 SUPPORTED_INSTANCE_EXTENSIONS = [
89 "VK_KHR_get_physical_device_properties2",
91 "VK_KHR_win32_surface",
94 BLACKLISTED_EXTENSIONS = [
95 # Handling of VK_EXT_debug_report requires some consideration. The win32
96 # loader already provides it for us and it is somewhat usable. If we add
97 # plumbing down to the native layer, we will get each message twice as we
98 # use 2 loaders (win32+native), but we may get output from the driver.
99 # In any case callback conversion is required.
100 "VK_EXT_debug_report",
101 "VK_EXT_display_control", # Requires VK_EXT_display_surface_counter
102 "VK_EXT_external_memory_dma_buf", # Linux specific
103 "VK_EXT_hdr_metadata", # Needs WSI work.
104 "VK_GOOGLE_display_timing",
105 "VK_KHR_device_group", # Needs some thought, but part of Vulkan 1.1 Core.
106 "VK_KHR_display", # Needs WSI work.
107 "VK_KHR_external_fence",
108 "VK_KHR_external_fence_fd",
109 "VK_KHR_external_fence_win32",
110 "VK_KHR_external_memory",
111 "VK_KHR_external_semaphore",
112 # Relates to external_semaphore and needs type conversions in bitflags.
113 "VK_KHR_external_semaphore_capabilities",
114 "VK_KHR_shared_presentable_image", # Needs WSI work.
115 "VK_KHR_win32_keyed_mutex",
116 "VK_NV_external_memory_win32",
119 # The Vulkan loader provides entry-points for core functionality and important
120 # extensions. Based on vulkan-1.def this amounts to WSI extensions on 1.0.51.
123 "VK_KHR_display_swapchain",
126 "VK_KHR_win32_surface",
129 # Functions part of our winevulkan graphics driver interface.
130 # DRIVER_VERSION should be bumped on any change to driver interface
131 # in FUNCTION_OVERRIDES
134 # Table of functions for which we have a special implementation.
135 # These are regular device / instance functions for which we need
136 # to do more work compared to a regular thunk or because they are
137 # part of the driver interface.
138 # - dispatch set whether we need a function pointer in the device
139 # / instance dispatch table.
140 # - driver sets whether the API is part of the driver interface.
141 # - thunk sets whether to create a thunk in vulkan_thunks.c.
142 FUNCTION_OVERRIDES = {
144 "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : False},
145 "vkEnumerateInstanceExtensionProperties" : {"dispatch" : False, "driver" : True, "thunk" : False},
146 "vkGetInstanceProcAddr": {"dispatch" : False, "driver" : True, "thunk" : False},
149 "vkCreateDevice" : {"dispatch" : True, "driver" : False, "thunk" : False},
150 "vkDestroyInstance" : {"dispatch" : False, "driver" : True, "thunk" : False },
151 "vkEnumerateDeviceExtensionProperties" : {"dispatch" : True, "driver" : False, "thunk" : False},
152 "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : False},
155 "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : False},
156 "vkCmdExecuteCommands" : {"dispatch" : True, "driver" : False, "thunk" : False},
157 "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : False},
158 "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : False},
159 "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : False},
160 "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : False},
161 "vkQueueSubmit" : {"dispatch": True, "driver" : False, "thunk" : False},
164 "vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
165 "vkGetPhysicalDeviceSurfaceSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
166 "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
167 "vkGetPhysicalDeviceSurfaceFormatsKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
168 "vkGetPhysicalDeviceSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
170 # VK_KHR_win32_surface
171 "vkCreateWin32SurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
172 "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
175 "vkAcquireNextImageKHR": {"dispatch" : True, "driver" : True, "thunk" : True},
176 "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
177 "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
178 "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : True},
179 "vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : True},
183 class Direction(Enum):
184 """ Parameter direction: input, output, input_output. """
190 class VkBaseType(object):
191 def __init__(self, name, _type, alias=False, requires=None):
192 """ Vulkan base type class.
194 VkBaseType is mostly used by Vulkan to define its own
195 base types like VkFlags through typedef out of e.g. uint32_t.
198 name (:obj:'str'): Name of the base type.
199 _type (:obj:'str'): Underlaying type
200 alias (bool): type is an alias or not.
201 requires (:obj:'str', optional): Other types required.
202 Often bitmask values pull in a *FlagBits type.
207 self.requires = requires
208 self.required = False
210 def definition(self):
211 # Definition is similar for alias or non-alias as type
212 # is already set to alias.
213 return "typedef {0} {1};\n".format(self.type, self.name)
219 class VkConstant(object):
220 def __init__(self, name, value):
224 def definition(self):
225 text = "#define {0} {1}\n".format(self.name, self.value)
229 class VkDefine(object):
230 def __init__(self, name, value):
235 def from_xml(define):
236 name_elem = define.find("name")
238 if name_elem is None:
239 # <type category="define" name="some_name">some_value</type>
240 # At the time of writing there is only 1 define of this category
241 # 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'.
242 name = define.attrib.get("name")
244 # We override behavior of VK_DEFINE_NON_DISPATCHABLE handle as the default
245 # definition various between 64-bit (uses pointers) and 32-bit (uses uint64_t).
246 # This complicates TRACEs in the thunks, so just use uint64_t.
247 if name == "VK_DEFINE_NON_DISPATCHABLE_HANDLE":
248 value = "#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;"
251 return VkDefine(name, value)
253 # With a name element the structure is like:
254 # <type category="define"><name>some_name</name>some_value</type>
255 name = name_elem.text
257 # Perform minimal parsing for Vulkan constants, which we don't need, but are referenced
258 # elsewhere in vk.xml.
259 # - VK_API_VERSION is a messy, deprecated constant and we don't want generate code for it.
260 # - AHardwareBuffer/ANativeWindow are forard declarations for Android types, which leaked
261 # into the define region.
262 if name in ["VK_API_VERSION", "AHardwareBuffer", "ANativeWindow"]:
263 return VkDefine(name, None)
265 # The body of the define is basically unstructured C code. It is not meant for easy parsing.
266 # Some lines contain deprecated values or comments, which we try to filter out.
268 for line in define.text.splitlines():
269 # Skip comments or deprecated values.
276 if child.tail is not None:
277 # Split comments for VK_API_VERSION_1_0 / VK_API_VERSION_1_1
278 if "//" in child.tail:
279 value += child.tail.split("//")[0]
283 return VkDefine(name, value.rstrip(' '))
285 def definition(self):
286 if self.value is None:
289 # Nothing to do as the value was already put in the right form during parsing.
290 return "{0}\n".format(self.value)
293 class VkEnum(object):
294 def __init__(self, name, values, alias=False):
297 self.required = False
301 def from_alias(enum, alias):
302 name = enum.attrib.get("name")
303 return VkEnum(name, alias.values)
307 name = enum.attrib.get("name")
310 for v in enum.findall("enum"):
311 # Value is either a value or a bitpos, only one can exist.
312 value = v.attrib.get("value")
315 value = 1 << int(v.attrib.get("bitpos"))
316 values.append(VkEnumValue(v.attrib.get("name"), value, hex=True))
318 # Some values are in hex form. We want to preserve the hex representation
319 # at least when we convert back to a string. Internally we want to use int.
321 values.append(VkEnumValue(v.attrib.get("name"), int(value, 0), hex=True))
323 values.append(VkEnumValue(v.attrib.get("name"), int(value, 0)))
325 # vulkan.h contains a *_MAX_ENUM value set to 32-bit at the time of writing,
326 # which is to prepare for extensions as they can add values and hence affect
327 # the size definition.
328 max_name = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2',name).upper() + "_MAX_ENUM"
329 values.append(VkEnumValue(max_name, 0x7fffffff, hex=True))
331 return VkEnum(name, values)
333 def add(self, value):
334 """ Add a value to enum. """
336 # Extensions can add new enum values. When an extension is promoted to Core
337 # the registry defines the value twice once for old extension and once for
338 # new Core features. Ignore the duplicate entry.
339 for v in self.values:
340 if v.value == value.value:
341 LOGGER.debug("Adding duplicate enum value {0} to {1}".format(v, self.name))
343 self.values.append(value)
345 def definition(self):
346 text = "typedef enum {0}\n{{\n".format(self.name)
348 # Print values sorted, values can have been added in a random order.
349 values = sorted(self.values, key=lambda value: value.value)
351 text += " {0},\n".format(value.definition())
352 text += "}} {0};\n\n".format(self.name)
359 class VkEnumValue(object):
360 def __init__(self, name, value, hex=False):
366 return "{0}={1}".format(self.name, self.value)
368 def definition(self):
369 """ Convert to text definition e.g. VK_FOO = 1 """
371 # Hex is commonly used for FlagBits and sometimes within
372 # a non-FlagBits enum for a bitmask value as well.
374 return "{0} = 0x{1:08x}".format(self.name, self.value)
376 return "{0} = {1}".format(self.name, self.value)
379 class VkFunction(object):
380 def __init__(self, _type=None, name=None, params=[], extensions=[], alias=False):
387 # For some functions we need some extra metadata from FUNCTION_OVERRIDES.
388 func_info = FUNCTION_OVERRIDES.get(self.name, None)
389 self.dispatch = func_info["dispatch"] if func_info is not None else True
390 self.driver = func_info["driver"] if func_info is not None else False
391 self.thunk_needed = func_info["thunk"] if func_info is not None else True
393 # Required is set while parsing which APIs and types are required
394 # and is used by the code generation.
395 self.required = False
398 def from_alias(command, alias):
399 """ Create VkFunction from an alias command.
402 command: xml data for command
403 alias (VkFunction): function to use as a base for types / parameters.
408 func_name = command.attrib.get("name")
409 func_type = alias.type
410 params = alias.params
412 return VkFunction(_type=func_type, name=func_name, params=params, alias=True)
415 def from_xml(command, types):
416 proto = command.find("proto")
417 func_name = proto.find("name").text
418 func_type = proto.find("type").text
421 for param in command.findall("param"):
422 vk_param = VkParam.from_xml(param, types)
423 params.append(vk_param)
425 return VkFunction(_type=func_type, name=func_name, params=params)
427 def get_conversions(self):
428 """ Get a list of conversion functions required for this function if any.
429 Parameters which are structures may require conversion between win32
430 and the host platform. This function returns a list of conversions
435 for param in self.params:
436 convs = param.get_conversions()
437 if convs is not None:
438 conversions.extend(convs)
445 def is_core_func(self):
446 """ Returns whether the function is a Vulkan core function.
447 Core functions are APIs defined by the Vulkan spec to be part of the
448 Core API as well as several KHR WSI extensions.
451 if not self.extensions:
454 return any(ext in self.extensions for ext in CORE_EXTENSIONS)
456 def is_device_func(self):
457 # If none of the other, it must be a device function
458 return not self.is_global_func() and not self.is_instance_func()
460 def is_driver_func(self):
461 """ Returns if function is part of Wine driver interface. """
464 def is_global_func(self):
465 # Treat vkGetInstanceProcAddr as a global function as it
466 # can operate with NULL for vkInstance.
467 if self.name == "vkGetInstanceProcAddr":
469 # Global functions are not passed a dispatchable object.
470 elif self.params[0].is_dispatchable():
474 def is_instance_func(self):
475 # Instance functions are passed VkInstance or VkPhysicalDevice.
476 if self.params[0].type in ["VkInstance", "VkPhysicalDevice"]:
480 def is_required(self):
483 def needs_conversion(self):
484 """ Check if the function needs any input/output type conversion.
485 Functions need input/output conversion if struct parameters have
486 alignment differences between Win32 and Linux 32-bit.
489 for p in self.params:
490 if p.needs_conversion():
491 LOGGER.debug("Parameter {0} to {1} requires conversion".format(p.name, self.name))
496 def needs_dispatch(self):
499 def needs_thunk(self):
500 return self.thunk_needed
502 def pfn(self, prefix="p", call_conv=None, conv=False):
503 """ Create function pointer. """
506 pfn = "{0} ({1} *{2}_{3})(".format(self.type, call_conv, prefix, self.name)
508 pfn = "{0} (*{1}_{2})(".format(self.type, prefix, self.name)
510 for i, param in enumerate(self.params):
512 pfn += param.const + " "
515 if conv and param.needs_conversion():
518 if param.is_pointer():
519 pfn += " " + param.pointer
521 if param.array_len is not None:
522 pfn += "[{0}]".format(param.array_len)
524 if i < len(self.params) - 1:
529 def prototype(self, call_conv=None, prefix=None, postfix=None):
530 """ Generate prototype for given function.
533 call_conv (str, optional): calling convention e.g. WINAPI
534 prefix (str, optional): prefix to append prior to function name e.g. vkFoo -> wine_vkFoo
535 postfix (str, optional): text to append after function name but prior to semicolon e.g. DECLSPEC_HIDDEN
538 proto = "{0}".format(self.type)
540 if call_conv is not None:
541 proto += " {0}".format(call_conv)
543 if prefix is not None:
544 proto += " {0}{1}(".format(prefix, self.name)
546 proto += " {0}(".format(self.name)
548 # Add all the parameters.
549 proto += ", ".join([p.definition() for p in self.params])
551 if postfix is not None:
552 proto += ") {0}".format(postfix)
559 body = " {0}".format(self.trace())
561 params = ", ".join([p.variable(conv=False) for p in self.params])
563 # Call the native Vulkan function.
564 if self.type == "void":
565 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
567 body += " return {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
571 def body_conversion(self):
574 # Declare a variable to hold the result for non-void functions.
575 if self.type != "void":
576 body += " {0} result;\n".format(self.type)
578 # Declare any tmp parameters for conversion.
579 for p in self.params:
580 if not p.needs_conversion():
583 if p.is_dynamic_array():
584 body += " {0}_host *{1}_host;\n".format(p.type, p.name)
586 body += " {0}_host {1}_host;\n".format(p.type, p.name)
588 body += " {0}\n".format(self.trace())
590 # Call any win_to_host conversion calls.
591 for p in self.params:
592 if not p.needs_input_conversion():
595 body += p.copy(Direction.INPUT)
597 # Build list of parameters containing converted and non-converted parameters.
598 # The param itself knows if conversion is needed and applies it when we set conv=True.
599 params = ", ".join([p.variable(conv=True) for p in self.params])
601 # Call the native Vulkan function.
602 if self.type == "void":
603 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
605 body += " result = {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
609 # Call any host_to_win conversion calls.
610 for p in self.params:
611 if not p.needs_output_conversion():
614 body += p.copy(Direction.OUTPUT)
616 # Perform any required cleanups. Most of these are for array functions.
617 for p in self.params:
618 if not p.needs_free():
623 # Finally return the result.
624 if self.type != "void":
625 body += " return result;\n"
629 def spec(self, prefix=None, symbol=None):
630 """ Generate spec file entry for this function.
633 prefix (str, optional): prefix to prepend to entry point name.
634 symbol (str, optional): allows overriding the name of the function implementing the entry point.
638 params = " ".join([p.spec() for p in self.params])
639 if prefix is not None:
640 spec += "@ stdcall {0}{1}({2})".format(prefix, self.name, params)
642 spec += "@ stdcall {0}({1})".format(self.name, params)
644 if symbol is not None:
650 def stub(self, call_conv=None, prefix=None):
651 stub = self.prototype(call_conv=call_conv, prefix=prefix)
653 stub += " {0}".format(self.trace(message="stub: ", trace_func="FIXME"))
655 if self.type == "VkResult":
656 stub += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
657 elif self.type == "VkBool32":
658 stub += " return VK_FALSE;\n"
659 elif self.type == "PFN_vkVoidFunction":
660 stub += " return NULL;\n"
665 def thunk(self, call_conv=None, prefix=None):
666 thunk = self.prototype(call_conv=call_conv, prefix=prefix)
669 if self.needs_conversion():
670 thunk += "#if defined(USE_STRUCT_CONVERSION)\n"
671 thunk += self.body_conversion()
681 def trace(self, message=None, trace_func=None):
682 """ Create a trace string including all parameters.
685 message (str, optional): text to print at start of trace message e.g. 'stub: '
686 trace_func (str, optional): used to override trace function e.g. FIXME, printf, etcetera.
688 if trace_func is not None:
689 trace = "{0}(\"".format(trace_func)
693 if message is not None:
696 # First loop is for all the format strings.
697 trace += ", ".join([p.format_string() for p in self.params])
700 # Second loop for parameter names and optional conversions.
701 for param in self.params:
702 if param.format_conv is not None:
703 trace += ", " + param.format_conv.format(param.name)
705 trace += ", {0}".format(param.name)
711 class VkFunctionPointer(object):
712 def __init__(self, _type, name, members):
714 self.members = members
716 self.required = False
719 def from_xml(funcpointer):
723 for t in funcpointer.findall("type"):
725 # <type>void</type>* pUserData,
726 # Parsing of the tail (anything past </type>) is tricky since there
727 # can be other data on the next line like: const <type>int</type>..
729 const = True if begin and "const" in begin else False
731 lines = t.tail.split(",\n")
732 if lines[0][0] == "*":
734 name = lines[0][1:].strip()
737 name = lines[0].strip()
739 # Filter out ); if it is contained.
740 name = name.partition(");")[0]
742 # If tail encompasses multiple lines, assign the second line to begin
745 begin = lines[1].strip()
749 members.append(VkMember(const=const, _type=_type, pointer=pointer, name=name))
751 _type = funcpointer.text
752 name = funcpointer.find("name").text
753 return VkFunctionPointer(_type, name, members)
755 def definition(self):
756 text = "{0} {1})(\n".format(self.type, self.name)
759 if len(self.members) > 0:
760 for m in self.members:
762 text += " " + m.definition()
765 text += ",\n " + m.definition()
767 # Just make the compiler happy by adding a void parameter.
773 class VkHandle(object):
774 def __init__(self, name, _type, parent, alias=None):
779 self.required = False
782 def from_alias(handle, alias):
783 name = handle.attrib.get("name")
784 return VkHandle(name, alias.type, alias.parent, alias=alias)
787 def from_xml(handle):
788 name = handle.find("name").text
789 _type = handle.find("type").text
790 parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice.
791 return VkHandle(name, _type, parent)
793 def dispatch_table(self):
794 if not self.is_dispatchable():
797 if self.parent is None:
798 # Should only happen for VkInstance
800 elif self.name == "VkDevice":
801 # VkDevice has VkInstance as a parent, but has its own dispatch table.
803 elif self.parent in ["VkInstance", "VkPhysicalDevice"]:
804 return "instance->funcs"
805 elif self.parent in ["VkDevice", "VkCommandPool"]:
806 return "device->funcs"
808 LOGGER.error("Unhandled dispatchable parent: {0}".format(self.parent))
810 def definition(self):
811 """ Generates handle definition e.g. VK_DEFINE_HANDLE(vkInstance) """
813 # Legacy types are typedef'ed to the new type if they are aliases.
815 return "typedef {0} {1};\n".format(self.alias.name, self.name)
817 return "{0}({1})\n".format(self.type, self.name)
820 return self.alias is not None
822 def is_dispatchable(self):
823 """ Some handles like VkInstance, VkDevice are dispatchable objects,
824 which means they contain a dispatch table of function pointers.
826 return self.type == "VK_DEFINE_HANDLE"
828 def is_required(self):
831 def native_handle(self):
832 """ Provide access to the native handle of a dispatchable object.
834 Dispatchable objects wrap an underlying 'native' object.
835 This method provides access to the native object.
837 if not self.is_dispatchable():
840 if self.name == "VkCommandBuffer":
841 return "command_buffer"
842 elif self.name == "VkDevice":
844 elif self.name == "VkInstance":
846 elif self.name == "VkPhysicalDevice":
848 elif self.name == "VkQueue":
851 LOGGER.error("Unhandled native handle for: {0}".format(self.name))
854 class VkMember(object):
855 def __init__(self, const=False, struct_fwd_decl=False,_type=None, pointer=None, name=None, array_len=None,
856 dyn_array_len=None, optional=False, extension_structs=None):
858 self.struct_fwd_decl = struct_fwd_decl
860 self.pointer = pointer
862 self.type_info = None
863 self.array_len = array_len
864 self.dyn_array_len = dyn_array_len
865 self.optional = optional
866 self.extension_structs = extension_structs
868 def __eq__(self, other):
869 """ Compare member based on name against a string.
871 This method is for convenience by VkStruct, which holds a number of members and needs quick checking
872 if certain members exist.
875 if self.name == other:
881 return "{0} {1} {2} {3} {4} {5} {6}".format(self.const, self.struct_fwd_decl, self.type, self.pointer,
882 self.name, self.array_len, self.dyn_array_len)
885 def from_xml(member):
886 """ Helper function for parsing a member tag within a struct or union. """
888 name_elem = member.find("name")
889 type_elem = member.find("type")
892 struct_fwd_decl = False
898 if "const" in member.text:
901 # Some members contain forward declarations:
902 # - VkBaseInstructure has a member "const struct VkBaseInStructure *pNext"
903 # - VkWaylandSurfaceCreateInfoKHR has a member "struct wl_display *display"
904 if "struct" in member.text:
905 struct_fwd_decl = True
907 if type_elem is not None:
908 member_type = type_elem.text
909 if type_elem.tail is not None:
910 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
912 # Name of other member within, which stores the number of
913 # elements pointed to be by this member.
914 dyn_array_len = member.get("len", None)
916 if "validextensionstructs" in member.attrib:
917 extension_structs = member.get("validextensionstructs").split(",")
919 extension_structs = None
921 # Some members are optional, which is important for conversion code e.g. not dereference NULL pointer.
922 optional = True if member.get("optional") else False
924 # Usually we need to allocate memory for dynamic arrays. We need to do the same in a few other cases
925 # like for VkCommandBufferBeginInfo.pInheritanceInfo. Just threat such cases as dynamic arrays of
926 # size 1 to simplify code generation.
927 if dyn_array_len is None and pointer is not None:
930 # Some members are arrays, attempt to parse these. Formats include:
931 # <member><type>char</type><name>extensionName</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
932 # <member><type>uint32_t</type><name>foo</name>[4]</member>
933 if name_elem.tail and name_elem.tail[0] == '[':
934 LOGGER.debug("Found array type")
935 enum_elem = member.find("enum")
936 if enum_elem is not None:
937 array_len = enum_elem.text
939 # Remove brackets around length
940 array_len = name_elem.tail.strip("[]")
942 return VkMember(const=const, struct_fwd_decl=struct_fwd_decl, _type=member_type, pointer=pointer, name=name_elem.text,
943 array_len=array_len, dyn_array_len=dyn_array_len, optional=optional, extension_structs=extension_structs)
945 def copy(self, input, output, direction):
946 """ Helper method for use by conversion logic to generate a C-code statement to copy this member. """
948 if self.needs_conversion():
949 if self.is_dynamic_array():
950 if direction == Direction.OUTPUT:
951 LOGGER.warn("TODO: implement copying of returnedonly dynamic array for {0}.{1}".format(self.type, self.name))
953 # Array length is either a variable name (string) or an int.
954 count = self.dyn_array_len if isinstance(self.dyn_array_len, int) else "{0}{1}".format(input, self.dyn_array_len)
955 return "{0}{1} = convert_{2}_array_win_to_host({3}{1}, {4});\n".format(output, self.name, self.type, input, count)
956 elif self.is_static_array():
957 count = self.array_len
958 if direction == Direction.OUTPUT:
959 # Needed by VkMemoryHeap.memoryHeaps
960 return "convert_{0}_static_array_host_to_win({2}{1}, {3}{1}, {4});\n".format(self.type, self.name, input, output, count)
962 # Nothing needed this yet.
963 LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
965 if direction == Direction.OUTPUT:
966 return "convert_{0}_host_to_win(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
968 return "convert_{0}_win_to_host(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
969 elif self.is_static_array():
970 bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type)
971 return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count)
973 return "{0}{1} = {2}{1};\n".format(output, self.name, input)
975 def definition(self, align=False, conv=False):
976 """ Generate prototype for given function.
979 align (bool, optional): Enable alignment if a type needs it. This adds WINE_VK_ALIGN(8) to a member.
980 conv (bool, optional): Enable conversion if a type needs it. This appends '_host' to the name.
987 if self.is_struct_forward_declaration():
990 if conv and self.is_struct():
991 text += "{0}_host".format(self.type)
995 if self.is_pointer():
996 text += " {0}{1}".format(self.pointer, self.name)
998 if align and self.needs_alignment():
999 text += " WINE_VK_ALIGN(8) " + self.name
1001 text += " " + self.name
1003 if self.is_static_array():
1004 text += "[{0}]".format(self.array_len)
1008 def get_conversions(self):
1009 """ Return any conversion description for this member and its children when conversion is needed. """
1011 # Check if we need conversion either for this member itself or for any child members
1012 # in case member represents a struct.
1013 if not self.needs_conversion():
1018 # Collect any conversion for any member structs.
1019 struct = self.type_info["data"]
1021 if m.needs_conversion():
1022 conversions.extend(m.get_conversions())
1024 struct = self.type_info["data"]
1025 direction = Direction.OUTPUT if struct.returnedonly else Direction.INPUT
1026 if self.is_dynamic_array():
1027 conversions.append(ConversionFunction(False, True, direction, struct))
1028 elif self.is_static_array():
1029 conversions.append(ConversionFunction(True, False, direction, struct))
1031 conversions.append(ConversionFunction(False, False, direction, struct))
1033 if self.needs_free():
1034 conversions.append(FreeFunction(self.is_dynamic_array(), struct))
1041 def is_dynamic_array(self):
1042 """ Returns if the member is an array element.
1043 Vulkan uses this for dynamically sized arrays for which
1044 there is a 'count' parameter.
1046 return self.dyn_array_len is not None
1048 def is_handle(self):
1049 return self.type_info["category"] == "handle"
1051 def is_pointer(self):
1052 return self.pointer is not None
1054 def is_static_array(self):
1055 """ Returns if the member is an array.
1056 Vulkan uses this often for fixed size arrays in which the
1057 length is part of the member.
1059 return self.array_len is not None
1061 def is_struct(self):
1062 return self.type_info["category"] == "struct"
1064 def is_struct_forward_declaration(self):
1065 return self.struct_fwd_decl
1068 return self.type_info["category"] == "union"
1070 def needs_alignment(self):
1071 """ Check if this member needs alignment for 64-bit data.
1072 Various structures need alignment on 64-bit variables due
1073 to compiler differences on 32-bit between Win32 and Linux.
1076 if self.is_pointer():
1078 elif self.type == "size_t":
1080 elif self.type in ["uint64_t", "VkDeviceSize"]:
1082 elif self.is_struct():
1083 struct = self.type_info["data"]
1084 return struct.needs_alignment()
1085 elif self.is_handle():
1086 # Dispatchable handles are pointers to objects, while
1087 # non-dispatchable are uint64_t and hence need alignment.
1088 handle = self.type_info["data"]
1089 return False if handle.is_dispatchable() else True
1092 def needs_conversion(self):
1093 """ Structures requiring alignment, need conversion between win32 and host. """
1095 if not self.is_struct():
1098 struct = self.type_info["data"]
1099 return struct.needs_conversion()
1101 def needs_free(self):
1102 if not self.needs_conversion():
1105 if self.is_dynamic_array():
1108 # TODO: some non-pointer structs and optional pointer structs may need freeing,
1109 # though none of this type have been encountered yet.
1112 def set_type_info(self, type_info):
1113 """ Helper function to set type information from the type registry.
1114 This is needed, because not all type data is available at time of
1117 self.type_info = type_info
1120 class VkParam(object):
1121 """ Helper class which describes a parameter to a function call. """
1123 def __init__(self, type_info, const=None, pointer=None, name=None, array_len=None, dyn_array_len=None):
1126 self.array_len = array_len
1127 self.dyn_array_len = dyn_array_len
1128 self.pointer = pointer
1129 self.type_info = type_info
1130 self.type = type_info["name"] # For convenience
1131 self.handle = type_info["data"] if type_info["category"] == "handle" else None
1132 self.struct = type_info["data"] if type_info["category"] == "struct" else None
1134 self._set_direction()
1135 self._set_format_string()
1136 self._set_conversions()
1139 return "{0} {1} {2} {3} {4}".format(self.const, self.type, self.pointer, self.name, self.array_len, self.dyn_array_len)
1142 def from_xml(param, types):
1143 """ Helper function to create VkParam from xml. """
1145 # Parameter parsing is slightly tricky. All the data is contained within
1146 # a param tag, but some data is within subtags while others are text
1147 # before or after the type tag.
1149 # <param>const <type>char</type>* <name>pLayerName</name></param>
1151 name_elem = param.find("name")
1153 name = name_elem.text
1154 # Tail contains array length e.g. for blendConstants param of vkSetBlendConstants
1155 if name_elem.tail is not None:
1156 array_len = name_elem.tail.strip("[]")
1158 # Name of other parameter in function prototype, which stores the number of
1159 # elements pointed to be by this parameter.
1160 dyn_array_len = param.get("len", None)
1162 const = param.text.strip() if param.text else None
1163 type_elem = param.find("type")
1164 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1166 # Since we have parsed all types before hand, this should not happen.
1167 type_info = types.get(type_elem.text, None)
1168 if type_info is None:
1169 LOGGER.err("type info not found for: {0}".format(type_elem.text))
1171 return VkParam(type_info, const=const, pointer=pointer, name=name, array_len=array_len, dyn_array_len=dyn_array_len)
1173 def _set_conversions(self):
1174 """ Internal helper function to configure any needed conversion functions. """
1176 self.free_func = None
1177 self.input_conv = None
1178 self.output_conv = None
1179 if not self.needs_conversion():
1182 # Input functions require win to host conversion.
1183 if self._direction in [Direction.INPUT, Direction.INPUT_OUTPUT]:
1184 self.input_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.INPUT, self.struct)
1186 # Output functions require host to win conversion.
1187 if self._direction in [Direction.INPUT_OUTPUT, Direction.OUTPUT]:
1188 self.output_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.OUTPUT, self.struct)
1190 # Dynamic arrays, but also some normal structs (e.g. VkCommandBufferBeginInfo) need memory
1191 # allocation and thus some cleanup.
1192 if self.is_dynamic_array() or self.struct.needs_free():
1193 self.free_func = FreeFunction(self.is_dynamic_array(), self.struct)
1195 def _set_direction(self):
1196 """ Internal helper function to set parameter direction (input/output/input_output). """
1198 # The parameter direction needs to be determined from hints in vk.xml like returnedonly,
1199 # parameter constness and other heuristics.
1200 # For now we need to get this right for structures as we need to convert these, we may have
1201 # missed a few other edge cases (e.g. count variables).
1202 # See also https://github.com/KhronosGroup/Vulkan-Docs/issues/610
1204 if not self.is_pointer():
1205 self._direction = Direction.INPUT
1206 elif self.is_const() and self.is_pointer():
1207 self._direction = Direction.INPUT
1208 elif self.is_struct():
1209 if not self.struct.returnedonly:
1210 self._direction = Direction.INPUT
1213 # Returnedonly hints towards output, however in some cases
1214 # it is inputoutput. In particular if pNext / sType exist,
1215 # which are used to link in other structures without having
1216 # to introduce new APIs. E.g. vkGetPhysicalDeviceProperties2KHR.
1217 if "pNext" in self.struct:
1218 self._direction = Direction.INPUT_OUTPUT
1221 self._direction = Direction.OUTPUT
1223 # This should mostly be right. Count variables can be inout, but we don't care about these yet.
1224 self._direction = Direction.OUTPUT
1226 def _set_format_string(self):
1227 """ Internal helper function to be used by constructor to set format string. """
1229 # Determine a format string used by code generation for traces.
1230 # 64-bit types need a conversion function.
1231 self.format_conv = None
1232 if self.is_static_array() or self.is_pointer():
1233 self.format_str = "%p"
1235 if self.type_info["category"] in ["bitmask", "enum"]:
1236 self.format_str = "%#x"
1237 elif self.is_handle():
1238 # We use uint64_t for non-dispatchable handles as opposed to pointers
1239 # for dispatchable handles.
1240 if self.handle.is_dispatchable():
1241 self.format_str = "%p"
1243 self.format_str = "0x%s"
1244 self.format_conv = "wine_dbgstr_longlong({0})"
1245 elif self.type == "float":
1246 self.format_str = "%f"
1247 elif self.type == "int":
1248 self.format_str = "%d"
1249 elif self.type == "int32_t":
1250 self.format_str = "%d"
1251 elif self.type == "size_t":
1252 self.format_str = "0x%s"
1253 self.format_conv = "wine_dbgstr_longlong({0})"
1254 elif self.type in ["uint32_t", "VkBool32"]:
1255 self.format_str = "%u"
1256 elif self.type in ["uint64_t", "VkDeviceSize"]:
1257 self.format_str = "0x%s"
1258 self.format_conv = "wine_dbgstr_longlong({0})"
1259 elif self.type == "HANDLE":
1260 self.format_str = "%p"
1261 elif self.type in ["VisualID", "xcb_visualid_t", "RROutput"]:
1262 # Don't care about Linux specific types.
1263 self.format_str = ""
1265 LOGGER.warn("Unhandled type: {0}".format(self.type_info))
1267 def copy(self, direction):
1268 if direction == Direction.INPUT:
1269 if self.is_dynamic_array():
1270 return " {0}_host = convert_{1}_array_win_to_host({0}, {2});\n".format(self.name, self.type, self.dyn_array_len)
1272 return " convert_{0}_win_to_host({1}, &{1}_host);\n".format(self.type, self.name)
1274 if self.is_dynamic_array():
1275 LOGGER.error("Unimplemented output conversion for: {0}".format(self.name))
1277 return " convert_{0}_host_to_win(&{1}_host, {1});\n".format(self.type, self.name)
1279 def definition(self, postfix=None):
1280 """ Return prototype for the parameter. E.g. 'const char *foo' """
1284 proto += self.const + " "
1288 if self.is_pointer():
1289 proto += " {0}{1}".format(self.pointer, self.name)
1291 proto += " " + self.name
1293 # Allows appending something to the variable name useful for
1294 # win32 to host conversion.
1295 if postfix is not None:
1298 if self.is_static_array():
1299 proto += "[{0}]".format(self.array_len)
1303 def direction(self):
1304 """ Returns parameter direction: input, output, input_output.
1306 Parameter direction in Vulkan is not straight-forward, which this function determines.
1309 return self._direction
1311 def format_string(self):
1312 return self.format_str
1314 def dispatch_table(self):
1315 """ Return functions dispatch table pointer for dispatchable objects. """
1317 if not self.is_dispatchable():
1320 return "{0}->{1}".format(self.name, self.handle.dispatch_table())
1322 def format_string(self):
1323 return self.format_str
1326 if self.is_dynamic_array():
1327 if self.struct.returnedonly:
1328 # For returnedonly, counts is stored in a pointer.
1329 return " free_{0}_array({1}_host, *{2});\n".format(self.type, self.name, self.dyn_array_len)
1331 return " free_{0}_array({1}_host, {2});\n".format(self.type, self.name, self.dyn_array_len)
1333 # We are operating on a single structure. Some structs (very rare) contain dynamic members,
1334 # which would need freeing.
1335 if self.struct.needs_free():
1336 return " free_{0}(&{1}_host);\n".format(self.type, self.name)
1339 def get_conversions(self):
1340 """ Get a list of conversions required for this parameter if any.
1341 Parameters which are structures may require conversion between win32
1342 and the host platform. This function returns a list of conversions
1346 if not self.is_struct():
1349 if not self.needs_conversion():
1354 # Collect any member conversions first, so we can guarantee
1355 # those functions will be defined prior to usage by the
1356 # 'parent' param requiring conversion.
1357 for m in self.struct:
1358 if not m.is_struct():
1361 if not m.needs_conversion():
1364 conversions.extend(m.get_conversions())
1366 # Conversion requirements for the 'parent' parameter.
1367 if self.input_conv is not None:
1368 conversions.append(self.input_conv)
1369 if self.output_conv is not None:
1370 conversions.append(self.output_conv)
1371 if self.free_func is not None:
1372 conversions.append(self.free_func)
1377 return self.const is not None
1379 def is_dynamic_array(self):
1380 return self.dyn_array_len is not None
1382 def is_dispatchable(self):
1383 if not self.is_handle():
1386 return self.handle.is_dispatchable()
1388 def is_handle(self):
1389 return self.handle is not None
1391 def is_pointer(self):
1392 return self.pointer is not None
1394 def is_static_array(self):
1395 return self.array_len is not None
1397 def is_struct(self):
1398 return self.struct is not None
1400 def needs_conversion(self):
1401 """ Returns if parameter needs conversion between win32 and host. """
1403 if not self.is_struct():
1406 # VkSparseImageMemoryRequirements(2) is used by vkGetImageSparseMemoryRequirements(2).
1407 # This function is tricky to wrap, because how to wrap depends on whether
1408 # pSparseMemoryRequirements is NULL or not. Luckily for VkSparseImageMemoryRequirements(2)
1409 # the alignment works out in such a way that no conversion is needed between win32 and Linux.
1410 if self.type in ["VkSparseImageMemoryRequirements", "VkSparseImageMemoryRequirements2"]:
1413 # If a structure needs alignment changes, it means we need to
1414 # perform parameter conversion between win32 and host.
1415 if self.struct.needs_conversion():
1420 def needs_free(self):
1421 return self.free_func is not None
1423 def needs_input_conversion(self):
1424 return self.input_conv is not None
1426 def needs_output_conversion(self):
1427 return self.output_conv is not None
1430 """ Generate spec file entry for this parameter. """
1432 if self.type_info["category"] in ["bitmask", "enum"]:
1434 if self.is_pointer() and self.type == "char":
1436 if self.is_dispatchable() or self.is_pointer() or self.is_static_array():
1438 if self.is_handle() and not self.is_dispatchable():
1440 if self.type == "float":
1442 if self.type in ["int", "int32_t", "size_t", "uint32_t", "VkBool32"]:
1444 if self.type in ["uint64_t", "VkDeviceSize"]:
1447 LOGGER.error("Unhandled spec conversion for type: {0}".format(self.type))
1449 def variable(self, conv=False):
1450 """ Returns 'glue' code during generation of a function call on how to access the variable.
1451 This function handles various scenarios such as 'unwrapping' if dispatchable objects and
1452 renaming of parameters in case of win32 -> host conversion.
1455 conv (bool, optional): Enable conversion if the param needs it. This appends '_host' to the name.
1458 # Hack until we enable allocation callbacks from ICD to application. These are a joy
1459 # to enable one day, because of calling convention conversion.
1460 if "VkAllocationCallbacks" in self.type:
1461 LOGGER.debug("TODO: setting NULL VkAllocationCallbacks for {0}".format(self.name))
1464 # Dispatchable objects wrap the native handle. For thunk generation we
1465 # need to pass the native handle to the native Vulkan calls.
1466 if self.is_dispatchable():
1467 return "{0}->{1}".format(self.name, self.handle.native_handle())
1468 elif conv and self.needs_conversion():
1469 if self.is_dynamic_array():
1470 return "{0}_host".format(self.name)
1472 return "&{0}_host".format(self.name)
1477 class VkStruct(Sequence):
1478 """ Class which represents the type union and struct. """
1480 def __init__(self, name, members, returnedonly, alias=False, union=False):
1482 self.members = members
1483 self.returnedonly = returnedonly
1484 self.required = False
1487 self.type_info = None # To be set later.
1489 def __getitem__(self, i):
1490 return self.members[i]
1493 return len(self.members)
1496 def from_alias(struct, alias):
1497 name = struct.attrib.get("name")
1498 return VkStruct(name, alias.members, alias.returnedonly, alias=True)
1501 def from_xml(struct):
1502 # Unions and structs are the same parsing wise, but we need to
1503 # know which one we are dealing with later on for code generation.
1504 union = True if struct.attrib["category"] == "union" else False
1506 name = struct.attrib.get("name", None)
1508 # 'Output' structures for which data is filled in by the API are
1509 # marked as 'returnedonly'.
1510 returnedonly = True if struct.attrib.get("returnedonly") else False
1513 for member in struct.findall("member"):
1514 vk_member = VkMember.from_xml(member)
1515 members.append(vk_member)
1517 return VkStruct(name, members, returnedonly, union=union)
1520 def decouple_structs(structs):
1521 """ Helper function which decouples a list of structs.
1522 Structures often depend on other structures. To make the C compiler
1523 happy we need to define 'substructures' first. This function analyzes
1524 the list of structures and reorders them in such a way that they are
1528 tmp_structs = list(structs) # Don't modify the original structures.
1529 decoupled_structs = []
1531 while (len(tmp_structs) > 0):
1532 for struct in tmp_structs:
1535 if not struct.required:
1536 tmp_structs.remove(struct)
1540 if not (m.is_struct() or m.is_union()):
1543 # VkBaseInstructure and VkBaseOutStructure reference themselves.
1544 if m.type == struct.name:
1548 # Check if a struct we depend on has already been defined.
1549 for s in decoupled_structs:
1550 if s.name == m.type:
1555 # Check if the struct we depend on is even in the list of structs.
1556 # If found now, it means we haven't met all dependencies before we
1557 # can operate on the current struct.
1558 # When generating 'host' structs we may not be able to find a struct
1559 # as the list would only contain the structs requiring conversion.
1560 for s in tmp_structs:
1561 if s.name == m.type:
1565 if dependends == False:
1566 decoupled_structs.append(struct)
1567 tmp_structs.remove(struct)
1569 return decoupled_structs
1571 def definition(self, align=False, conv=False, postfix=None):
1572 """ Convert structure to textual definition.
1575 align (bool, optional): enable alignment to 64-bit for win32 struct compatibility.
1576 conv (bool, optional): enable struct conversion if the struct needs it.
1577 postfix (str, optional): text to append to end of struct name, useful for struct renaming.
1581 text = "typedef union {0}".format(self.name)
1583 text = "typedef struct {0}".format(self.name)
1585 if postfix is not None:
1591 if align and m.needs_alignment():
1592 text += " {0};\n".format(m.definition(align=align))
1593 elif conv and m.needs_conversion():
1594 text += " {0};\n".format(m.definition(conv=conv))
1596 text += " {0};\n".format(m.definition())
1598 if postfix is not None:
1599 text += "}} {0}{1};\n\n".format(self.name, postfix)
1601 text += "}} {0};\n\n".format(self.name)
1607 def needs_alignment(self):
1608 """ Check if structure needs alignment for 64-bit data.
1609 Various structures need alignment on 64-bit variables due
1610 to compiler differences on 32-bit between Win32 and Linux.
1613 for m in self.members:
1614 if m.needs_alignment():
1618 def needs_conversion(self):
1619 """ Returns if struct members needs conversion between win32 and host.
1620 Structures need conversion if they contain members requiring alignment
1621 or if they include other structures which need alignment.
1624 if self.needs_alignment():
1627 for m in self.members:
1628 if m.needs_conversion():
1632 def needs_free(self):
1633 """ Check if any struct member needs some memory freeing."""
1635 for m in self.members:
1643 def set_type_info(self, types):
1644 """ Helper function to set type information from the type registry.
1645 This is needed, because not all type data is available at time of
1648 for m in self.members:
1649 type_info = types[m.type]
1650 m.set_type_info(type_info)
1653 class ConversionFunction(object):
1654 def __init__(self, array, dyn_array, direction, struct):
1656 self.direction = direction
1657 self.dyn_array = dyn_array
1658 self.struct = struct
1659 self.type = struct.name
1663 def __eq__(self, other):
1664 if self.name != other.name:
1669 def _generate_array_conversion_func(self):
1670 """ Helper function for generating a conversion function for array structs. """
1672 if self.direction == Direction.OUTPUT:
1673 params = ["const {0}_host *in".format(self.type), "uint32_t count"]
1674 return_type = self.type
1676 params = ["const {0} *in".format(self.type), "uint32_t count"]
1677 return_type = "{0}_host".format(self.type)
1679 # Generate function prototype.
1680 body = "static inline {0} *{1}(".format(return_type, self.name)
1681 body += ", ".join(p for p in params)
1684 body += " {0} *out;\n".format(return_type)
1685 body += " unsigned int i;\n\n"
1686 body += " if (!in) return NULL;\n\n"
1688 body += " out = heap_alloc(count * sizeof(*out));\n"
1690 body += " for (i = 0; i < count; i++)\n"
1693 for m in self.struct:
1694 # TODO: support copying of pNext extension structures!
1695 # Luckily though no extension struct at this point needs conversion.
1696 body += " " + m.copy("in[i].", "out[i].", self.direction)
1699 body += " return out;\n"
1703 def _generate_conversion_func(self):
1704 """ Helper function for generating a conversion function for non-array structs. """
1706 if self.direction == Direction.OUTPUT:
1707 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type)]
1709 params = ["const {0} *in".format(self.type), "{0}_host *out".format(self.type)]
1711 body = "static inline void {0}(".format(self.name)
1713 # Generate parameter list
1714 body += ", ".join(p for p in params)
1717 body += " if (!in) return;\n\n"
1719 if self.direction == Direction.INPUT and "pNext" in self.struct and self.struct.returnedonly:
1720 # We are dealing with an input_output parameter. For these we only need to copy
1721 # pNext and sType as the other fields are filled in by the host. We do potentially
1722 # have to iterate over pNext and perform conversions based on switch(sType)!
1723 # Luckily though no extension structs at this point need conversion.
1724 # TODO: support copying of pNext extension structures!
1725 body += " out->pNext = in->pNext;\n"
1726 body += " out->sType = in->sType;\n"
1728 for m in self.struct:
1729 # TODO: support copying of pNext extension structures!
1730 body += " " + m.copy("in->", "out->", self.direction)
1735 def _generate_static_array_conversion_func(self):
1736 """ Helper function for generating a conversion function for array structs. """
1738 if self.direction == Direction.OUTPUT:
1739 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
1740 return_type = self.type
1742 params = ["const {0} *in".format(self.type), "{0} *out_host".format(self.type), "uint32_t count"]
1743 return_type = "{0}_host".format(self.type)
1745 # Generate function prototype.
1746 body = "static inline void {0}(".format(self.name)
1747 body += ", ".join(p for p in params)
1749 body += " unsigned int i;\n\n"
1750 body += " if (!in) return;\n\n"
1751 body += " for (i = 0; i < count; i++)\n"
1754 for m in self.struct:
1755 # TODO: support copying of pNext extension structures!
1756 body += " " + m.copy("in[i].", "out[i].", self.direction)
1762 def _set_name(self):
1763 if self.direction == Direction.INPUT:
1765 name = "convert_{0}_static_array_win_to_host".format(self.type)
1766 elif self.dyn_array:
1767 name = "convert_{0}_array_win_to_host".format(self.type)
1769 name = "convert_{0}_win_to_host".format(self.type)
1770 else: # Direction.OUTPUT
1772 name = "convert_{0}_static_array_host_to_win".format(self.type)
1773 elif self.dyn_array:
1774 name = "convert_{0}_array_host_to_win".format(self.type)
1776 name = "convert_{0}_host_to_win".format(self.type)
1780 def definition(self):
1782 return self._generate_static_array_conversion_func()
1783 elif self.dyn_array:
1784 return self._generate_array_conversion_func()
1786 return self._generate_conversion_func()
1789 class FreeFunction(object):
1790 def __init__(self, dyn_array, struct):
1791 self.dyn_array = dyn_array
1792 self.struct = struct
1793 self.type = struct.name
1796 self.name = "free_{0}_array".format(self.type)
1798 self.name = "free_{0}".format(self.type)
1800 def __eq__(self, other):
1801 if self.name == other.name:
1806 def _generate_array_free_func(self):
1807 """ Helper function for cleaning up temporary buffers required for array conversions. """
1809 # Generate function prototype.
1810 body = "static inline void {0}({1}_host *in, uint32_t count)\n{{\n".format(self.name, self.type)
1812 # E.g. VkGraphicsPipelineCreateInfo_host needs freeing for pStages.
1813 if self.struct.needs_free():
1814 body += " unsigned int i;\n\n"
1815 body += " if (!in) return;\n\n"
1816 body += " for (i = 0; i < count; i++)\n"
1819 for m in self.struct:
1820 if m.needs_conversion() and m.is_dynamic_array():
1822 # Add a cast to ignore const on conversion structs we allocated ourselves.
1823 body += " free_{0}_array(({0}_host *)in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
1825 body += " free_{0}_array(in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
1826 elif m.needs_conversion():
1827 LOGGER.error("Unhandled conversion for {0}".format(m.name))
1830 body += " if (!in) return;\n\n"
1832 body += " heap_free(in);\n"
1837 def _generate_free_func(self):
1838 # E.g. VkCommandBufferBeginInfo.pInheritanceInfo needs freeing.
1839 if not self.struct.needs_free():
1842 # Generate function prototype.
1843 body = "static inline void {0}({1}_host *in)\n{{\n".format(self.name, self.type)
1845 for m in self.struct:
1846 if m.needs_conversion() and m.is_dynamic_array():
1847 count = m.dyn_array_len if isinstance(m.dyn_array_len, int) else "in->{0}".format(m.dyn_array_len)
1849 # Add a cast to ignore const on conversion structs we allocated ourselves.
1850 body += " free_{0}_array(({0}_host *)in->{1}, {2});\n".format(m.type, m.name, count)
1852 body += " free_{0}_array(in->{1}, {2});\n".format(m.type, m.name, count)
1857 def definition(self):
1859 return self._generate_array_free_func()
1861 # Some structures need freeing too if they contain dynamic arrays.
1862 # E.g. VkCommandBufferBeginInfo
1863 return self._generate_free_func()
1866 class VkGenerator(object):
1867 def __init__(self, registry):
1868 self.registry = registry
1870 # Build a list conversion functions for struct conversion.
1871 self.conversions = []
1872 self.host_structs = []
1873 for func in self.registry.funcs.values():
1874 if not func.is_required():
1877 if not func.needs_conversion():
1880 conversions = func.get_conversions()
1881 for conv in conversions:
1882 # Pull in any conversions for vulkan_thunks.c.
1883 if func.needs_thunk():
1884 # Append if we don't already have this conversion.
1885 if not any(c == conv for c in self.conversions):
1886 self.conversions.append(conv)
1888 # Structs can be used in different ways by different conversions
1889 # e.g. array vs non-array. Just make sure we pull in each struct once.
1890 if not any(s.name == conv.struct.name for s in self.host_structs):
1891 self.host_structs.append(conv.struct)
1893 def _generate_copyright(self, f, spec_file=False):
1894 f.write("# " if spec_file else "/* ")
1895 f.write("Automatically generated from Vulkan vk.xml; DO NOT EDIT!\n")
1896 lines = ["", "This file is generated from Vulkan vk.xml file covered",
1897 "by the following copyright and permission notice:"]
1898 lines.extend([l.rstrip(" ") for l in self.registry.copyright.splitlines()])
1900 f.write("{0}{1}".format("# " if spec_file else " * ", line).rstrip(" ") + "\n")
1901 f.write("\n" if spec_file else " */\n\n")
1903 def generate_thunks_c(self, f, prefix):
1904 self._generate_copyright(f)
1905 f.write("#include \"config.h\"\n")
1906 f.write("#include \"wine/port.h\"\n\n")
1908 f.write("#include \"vulkan_private.h\"\n\n")
1910 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
1912 # Generate any conversion helper functions.
1913 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
1914 for conv in self.conversions:
1915 f.write(conv.definition())
1916 f.write("#endif /* USE_STRUCT_CONVERSION */\n\n")
1918 # Create thunks for instance and device functions.
1919 # Global functions don't go through the thunks.
1920 for vk_func in self.registry.funcs.values():
1921 if not vk_func.is_required():
1924 if vk_func.is_global_func():
1927 if not vk_func.needs_thunk():
1930 # Exports symbols for Core functions.
1931 if not vk_func.is_core_func():
1933 f.write(vk_func.thunk(prefix=prefix, call_conv="WINAPI"))
1935 f.write("static const struct vulkan_func vk_device_dispatch_table[] =\n{\n")
1936 for vk_func in self.registry.device_funcs:
1937 if not vk_func.is_required():
1940 f.write(" {{\"{0}\", &{1}{0}}},\n".format(vk_func.name, prefix))
1943 f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
1944 for vk_func in self.registry.instance_funcs:
1945 if not vk_func.is_required():
1948 f.write(" {{\"{0}\", &{1}{0}}},\n".format(vk_func.name, prefix))
1951 f.write("void *wine_vk_get_device_proc_addr(const char *name)\n")
1953 f.write(" unsigned int i;\n")
1954 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_dispatch_table); i++)\n")
1956 f.write(" if (strcmp(vk_device_dispatch_table[i].name, name) == 0)\n")
1958 f.write(" TRACE(\"Found name=%s in device table\\n\", debugstr_a(name));\n")
1959 f.write(" return vk_device_dispatch_table[i].func;\n")
1962 f.write(" return NULL;\n")
1965 f.write("void *wine_vk_get_instance_proc_addr(const char *name)\n")
1967 f.write(" unsigned int i;\n")
1968 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_dispatch_table); i++)\n")
1970 f.write(" if (strcmp(vk_instance_dispatch_table[i].name, name) == 0)\n")
1972 f.write(" TRACE(\"Found name=%s in instance table\\n\", debugstr_a(name));\n")
1973 f.write(" return vk_instance_dispatch_table[i].func;\n")
1976 f.write(" return NULL;\n")
1979 # Create array of device extensions.
1980 f.write("static const char * const vk_device_extensions[] =\n{\n")
1981 for ext in self.registry.extensions:
1982 if ext["type"] != "device":
1985 f.write(" \"{0}\",\n".format(ext["name"]))
1988 # Create array of instance extensions.
1989 f.write("static const char * const vk_instance_extensions[] =\n{\n")
1990 for ext in self.registry.extensions:
1991 if ext["type"] != "instance":
1994 f.write(" \"{0}\",\n".format(ext["name"]))
1997 f.write("BOOL wine_vk_device_extension_supported(const char *name)\n")
1999 f.write(" unsigned int i;\n")
2000 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_extensions); i++)\n")
2002 f.write(" if (strcmp(vk_device_extensions[i], name) == 0)\n")
2003 f.write(" return TRUE;\n")
2005 f.write(" return FALSE;\n")
2008 f.write("BOOL wine_vk_instance_extension_supported(const char *name)\n")
2010 f.write(" unsigned int i;\n")
2011 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_extensions); i++)\n")
2013 f.write(" if (strcmp(vk_instance_extensions[i], name) == 0)\n")
2014 f.write(" return TRUE;\n")
2016 f.write(" return FALSE;\n")
2019 def generate_thunks_h(self, f, prefix):
2020 self._generate_copyright(f)
2022 f.write("#ifndef __WINE_VULKAN_THUNKS_H\n")
2023 f.write("#define __WINE_VULKAN_THUNKS_H\n\n")
2025 # Generate prototypes for device and instance functions requiring a custom implementation.
2026 f.write("/* Functions for which we have custom implementations outside of the thunks. */\n")
2027 for vk_func in self.registry.funcs.values():
2028 if not vk_func.is_required() or vk_func.is_global_func() or vk_func.needs_thunk():
2031 if vk_func.is_core_func():
2032 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix="wine_")))
2034 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix="wine_", postfix="DECLSPEC_HIDDEN")))
2037 for struct in self.host_structs:
2038 f.write(struct.definition(align=False, conv=True, postfix="_host"))
2041 f.write("/* For use by vkDevice and children */\n")
2042 f.write("struct vulkan_device_funcs\n{\n")
2043 for vk_func in self.registry.device_funcs:
2044 if not vk_func.is_required():
2047 if not vk_func.needs_dispatch():
2048 LOGGER.debug("skipping {0} in vulkan_device_funcs".format(vk_func.name))
2051 if vk_func.needs_conversion():
2052 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
2053 f.write(" {0};\n".format(vk_func.pfn(conv=True)))
2055 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2058 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2061 f.write("/* For use by vkInstance and children */\n")
2062 f.write("struct vulkan_instance_funcs\n{\n")
2063 for vk_func in self.registry.instance_funcs:
2064 if not vk_func.is_required():
2067 if not vk_func.needs_dispatch():
2068 LOGGER.debug("skipping {0} in vulkan_instance_funcs".format(vk_func.name))
2071 if vk_func.needs_conversion():
2072 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
2073 f.write(" {0};\n".format(vk_func.pfn(conv=True)))
2075 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2078 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2081 f.write("#define ALL_VK_DEVICE_FUNCS() \\\n")
2083 for vk_func in self.registry.device_funcs:
2084 if not vk_func.is_required():
2087 if not vk_func.needs_dispatch():
2088 LOGGER.debug("skipping {0} in ALL_VK_DEVICE_FUNCS".format(vk_func.name))
2092 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2095 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2098 f.write("#define ALL_VK_INSTANCE_FUNCS() \\\n")
2100 for vk_func in self.registry.instance_funcs:
2101 if not vk_func.is_required():
2104 if not vk_func.needs_dispatch():
2105 LOGGER.debug("skipping {0} in ALL_VK_INSTANCE_FUNCS".format(vk_func.name))
2109 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2112 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2115 f.write("#endif /* __WINE_VULKAN_THUNKS_H */\n")
2117 def generate_vulkan_h(self, f):
2118 self._generate_copyright(f)
2119 f.write("#ifndef __WINE_VULKAN_H\n")
2120 f.write("#define __WINE_VULKAN_H\n\n")
2122 f.write("#include <windef.h>\n")
2123 f.write("#include <stdint.h>\n\n")
2125 f.write("/* Define WINE_VK_HOST to get 'host' headers. */\n")
2126 f.write("#ifdef WINE_VK_HOST\n")
2127 f.write("#define VKAPI_CALL\n")
2128 f.write('#define WINE_VK_ALIGN(x)\n')
2129 f.write("#endif\n\n")
2131 f.write("#ifndef VKAPI_CALL\n")
2132 f.write("#define VKAPI_CALL __stdcall\n")
2133 f.write("#endif\n\n")
2135 f.write("#ifndef VKAPI_PTR\n")
2136 f.write("#define VKAPI_PTR VKAPI_CALL\n")
2137 f.write("#endif\n\n")
2139 f.write("#ifndef WINE_VK_ALIGN\n")
2140 f.write("#define WINE_VK_ALIGN DECLSPEC_ALIGN\n")
2141 f.write("#endif\n\n")
2143 # The overall strategy is to define independent constants and datatypes,
2144 # prior to complex structures and function calls to avoid forward declarations.
2145 for const in self.registry.consts:
2146 # For now just generate things we may not need. The amount of parsing needed
2147 # to get some of the info is tricky as you need to figure out which structure
2148 # references a certain constant.
2149 f.write(const.definition())
2152 for define in self.registry.defines:
2153 f.write(define.definition())
2155 for handle in self.registry.handles:
2156 # For backwards compatiblity also create definitions for aliases.
2157 # These types normally don't get pulled in as we use the new types
2158 # even in legacy functions if they are aliases.
2159 if handle.is_required() or handle.is_alias():
2160 f.write(handle.definition())
2163 for base_type in self.registry.base_types:
2164 f.write(base_type.definition())
2167 for bitmask in self.registry.bitmasks:
2168 f.write(bitmask.definition())
2171 # Define enums, this includes values for some of the bitmask types as well.
2172 for enum in self.registry.enums.values():
2174 f.write(enum.definition())
2176 for fp in self.registry.funcpointers:
2178 f.write(fp.definition())
2181 # This generates both structures and unions. Since structures
2182 # may depend on other structures/unions, we need a list of
2183 # decoupled structs.
2184 # Note: unions are stored in structs for dependency reasons,
2185 # see comment in parsing section.
2186 structs = VkStruct.decouple_structs(self.registry.structs)
2187 for struct in structs:
2188 LOGGER.debug("Generating struct: {0}".format(struct.name))
2189 f.write(struct.definition(align=True))
2191 for func in self.registry.funcs.values():
2192 if not func.is_required():
2193 LOGGER.debug("Skipping PFN definition for: {0}".format(func.name))
2196 f.write("typedef {0};\n".format(func.pfn(prefix="PFN", call_conv="VKAPI_PTR")))
2199 f.write("#ifndef VK_NO_PROTOTYPES\n")
2200 for func in self.registry.funcs.values():
2201 if not func.is_required():
2202 LOGGER.debug("Skipping API definition for: {0}".format(func.name))
2205 LOGGER.debug("Generating API definition for: {0}".format(func.name))
2206 f.write("{0};\n".format(func.prototype(call_conv="VKAPI_CALL")))
2207 f.write("#endif /* VK_NO_PROTOTYPES */\n\n")
2209 f.write("#endif /* __WINE_VULKAN_H */\n")
2211 def generate_vulkan_driver_h(self, f):
2212 self._generate_copyright(f)
2213 f.write("#ifndef __WINE_VULKAN_DRIVER_H\n")
2214 f.write("#define __WINE_VULKAN_DRIVER_H\n\n")
2216 f.write("/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */\n")
2217 f.write("#define WINE_VULKAN_DRIVER_VERSION {0}\n\n".format(DRIVER_VERSION))
2219 f.write("struct vulkan_funcs\n{\n")
2220 f.write(" /* Vulkan global functions. These are the only calls at this point a graphics driver\n")
2221 f.write(" * needs to provide. Other function calls will be provided indirectly by dispatch\n")
2222 f.write(" * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice.\n")
2225 for vk_func in self.registry.funcs.values():
2226 if not vk_func.is_required() or not vk_func.is_driver_func():
2230 # Avoid PFN_vkVoidFunction in driver interface as Vulkan likes to put calling convention
2231 # stuff in there. For simplicity substitute with "void *".
2232 pfn = pfn.replace("PFN_vkVoidFunction", "void *")
2233 f.write(" {0};\n".format(pfn))
2236 f.write("extern const struct vulkan_funcs * CDECL __wine_get_vulkan_driver(HDC hdc, UINT version);\n\n")
2237 f.write("#endif /* __WINE_VULKAN_DRIVER_H */\n")
2239 def generate_vulkan_spec(self, f):
2240 self._generate_copyright(f, spec_file=True)
2241 f.write("@ stdcall vk_icdGetInstanceProcAddr(ptr str) wine_vk_icdGetInstanceProcAddr\n")
2242 f.write("@ stdcall vk_icdNegotiateLoaderICDInterfaceVersion(ptr) wine_vk_icdNegotiateLoaderICDInterfaceVersion\n")
2244 # Export symbols for all Vulkan Core functions.
2245 for func in self.registry.funcs.values():
2246 if not func.is_core_func():
2249 # Not an ICD level function.
2250 if func.name == "vkEnumerateInstanceLayerProperties":
2253 # We support all Core functions except for VK_KHR_display* APIs.
2254 # Create stubs for unsupported Core functions.
2255 if func.is_required():
2256 f.write(func.spec(prefix="wine_"))
2258 f.write("@ stub {0}\n".format(func.name))
2260 def generate_vulkan_loader_spec(self, f):
2261 self._generate_copyright(f, spec_file=True)
2263 # Export symbols for all Vulkan Core functions.
2264 for func in self.registry.funcs.values():
2265 if not func.is_core_func():
2268 # We support all Core functions except for VK_KHR_display* APIs.
2269 # Create stubs for unsupported Core functions.
2270 if func.is_required():
2271 # Global functions need a custom implementation, except for
2272 # vkCreateInstance, which we can just forward.
2273 if func.is_global_func() and func.name != "vkCreateInstance":
2274 f.write(func.spec())
2276 f.write(func.spec(symbol="winevulkan.wine_" + func.name))
2278 f.write("@ stub {0}\n".format(func.name))
2281 class VkRegistry(object):
2282 def __init__(self, reg_filename):
2283 # Used for storage of type information.
2284 self.base_types = None
2285 self.bitmasks = None
2289 self.funcpointers = None
2293 # We aggregate all types in here for cross-referencing.
2297 # Overall strategy for parsing the registry is to first
2298 # parse all type / function definitions. Then parse
2299 # features and extensions to decide which types / functions
2300 # to actually 'pull in' for code generation. For each type or
2301 # function call we want we set a member 'required' to True.
2302 tree = ET.parse(reg_filename)
2303 root = tree.getroot()
2304 self._parse_enums(root)
2305 self._parse_types(root)
2306 self._parse_commands(root)
2308 # Pull in any required types and functions.
2309 self._parse_features(root)
2310 self._parse_extensions(root)
2312 self.copyright = root.find('./comment').text
2314 def _mark_command_required(self, command):
2315 """ Helper function to mark a certain command and the datatypes it needs as required."""
2316 def mark_bitmask_dependencies(bitmask, types):
2317 if bitmask.requires is not None:
2318 types[bitmask.requires]["data"].required = True
2320 def mark_funcpointer_dependencies(fp, types):
2321 for m in fp.members:
2322 type_info = types[m.type]
2324 # Complex types have a matching definition e.g. VkStruct.
2325 # Not needed for base types such as uint32_t.
2326 if "data" in type_info:
2327 types[m.type]["data"].required = True
2329 def mark_struct_dependencies(struct, types):
2331 type_info = types[m.type]
2333 # Complex types have a matching definition e.g. VkStruct.
2334 # Not needed for base types such as uint32_t.
2335 if "data" in type_info:
2336 types[m.type]["data"].required = True
2338 if type_info["category"] == "struct":
2340 mark_struct_dependencies(type_info["data"], types)
2341 elif type_info["category"] == "funcpointer":
2342 mark_funcpointer_dependencies(type_info["data"], types)
2343 elif type_info["category"] == "bitmask":
2344 mark_bitmask_dependencies(type_info["data"], types)
2346 func = self.funcs[command]
2347 func.required = True
2349 # Pull in return type
2350 if func.type != "void":
2351 self.types[func.type]["data"].required = True
2353 # Analyze parameter dependencies and pull in any type needed.
2354 for p in func.params:
2355 type_info = self.types[p.type]
2357 # Check if we are dealing with a complex type e.g. VkEnum, VkStruct and others.
2358 if "data" not in type_info:
2361 # Mark the complex type as required.
2362 type_info["data"].required = True
2363 if type_info["category"] == "struct":
2364 struct = type_info["data"]
2365 mark_struct_dependencies(struct, self.types)
2367 def _parse_commands(self, root):
2368 """ Parse command section containing the Vulkan function calls. """
2370 commands = root.findall("./commands/")
2372 # As of Vulkan 1.1, various extensions got promoted to Core.
2373 # The old commands (e.g. KHR) are available for backwards compatibility
2374 # and are marked in vk.xml as 'alias' to the non-extension type.
2375 # The registry likes to avoid data duplication, so parameters and other
2376 # metadata need to be looked up from the Core command.
2377 # We parse the alias commands in a second pass.
2379 for command in commands:
2380 alias_name = command.attrib.get("alias")
2382 alias_commands.append(command)
2385 func = VkFunction.from_xml(command, self.types)
2386 funcs[func.name] = func
2388 for command in alias_commands:
2389 alias_name = command.attrib.get("alias")
2390 alias = funcs[alias_name]
2391 func = VkFunction.from_alias(command, alias)
2392 funcs[func.name] = func
2394 # To make life easy for the code generation, separate all function
2395 # calls out in the 3 types of Vulkan functions: device, global and instance.
2399 for func in funcs.values():
2400 if func.is_device_func():
2401 device_funcs.append(func)
2402 elif func.is_global_func():
2403 global_funcs.append(func)
2405 instance_funcs.append(func)
2407 # Sort function lists by name and store them.
2408 self.device_funcs = sorted(device_funcs, key=lambda func: func.name)
2409 self.global_funcs = sorted(global_funcs, key=lambda func: func.name)
2410 self.instance_funcs = sorted(instance_funcs, key=lambda func: func.name)
2412 # The funcs dictionary is used as a convenient way to lookup function
2413 # calls when needed e.g. to adjust member variables.
2414 self.funcs = OrderedDict(sorted(funcs.items()))
2416 def _parse_enums(self, root):
2417 """ Parse enums section or better described as constants section. """
2420 for enum in root.findall("./enums"):
2421 name = enum.attrib.get("name")
2422 _type = enum.attrib.get("type")
2424 if _type in ("enum", "bitmask"):
2425 enums[name] = VkEnum.from_xml(enum)
2427 # If no type is set, we are dealing with API constants.
2428 for value in enum.findall("enum"):
2429 # If enum is an alias, set the value to the alias name.
2430 # E.g. VK_LUID_SIZE_KHR is an alias to VK_LUID_SIZE.
2431 alias = value.attrib.get("alias")
2433 self.consts.append(VkConstant(value.attrib.get("name"), alias))
2435 self.consts.append(VkConstant(value.attrib.get("name"), value.attrib.get("value")))
2437 self.enums = OrderedDict(sorted(enums.items()))
2439 def _parse_extensions(self, root):
2440 """ Parse extensions section and pull in any types and commands for this extensioin. """
2442 exts = root.findall("./extensions/extension")
2444 ext_name = ext.attrib["name"]
2446 # Set extension name on any functions calls part of this extension as we
2447 # were not aware of the name during initial parsing.
2448 commands = ext.findall("require/command")
2449 for command in commands:
2450 cmd_name = command.attrib["name"]
2451 self.funcs[cmd_name].extensions.append(ext_name)
2453 # Some extensions are not ready or have numbers reserved as a place holder.
2454 if ext.attrib["supported"] == "disabled":
2455 LOGGER.debug("Skipping disabled extension: {0}".format(ext_name))
2458 # Disable highly experimental extensions as the APIs are unstable and can
2459 # change between minor Vulkan revisions until API is final and becomes KHR
2461 if "KHX" in ext_name or "NVX" in ext_name:
2462 LOGGER.debug("Skipping experimental extension: {0}".format(ext_name))
2465 # Instance extensions often require a custom implementation, so filter.
2466 ext_type = ext.attrib["type"]
2467 if ext_type == "instance" and not ext_name in SUPPORTED_INSTANCE_EXTENSIONS:
2468 LOGGER.debug("Skipping instance extension: {0}".format(ext_name))
2471 # We disable some extensions as either we haven't implemented
2472 # support yet or because they are for platforms other than win32.
2473 if ext_name in BLACKLISTED_EXTENSIONS:
2474 LOGGER.debug("Skipping blacklisted extension: {0}".format(ext_name))
2476 elif "requires" in ext.attrib:
2477 # Check if this extension builds on top of another blacklisted
2479 requires = ext.attrib["requires"].split(",")
2480 if len(set(requires).intersection(BLACKLISTED_EXTENSIONS)) > 0:
2483 LOGGER.debug("Loading extension: {0}".format(ext_name))
2485 # Extensions can define one or more require sections each requiring
2486 # different features (e.g. Vulkan 1.1). Parse each require section
2487 # separately, so we can skip sections we don't want.
2488 for require in ext.findall("require"):
2489 feature = require.attrib.get("feature")
2490 if feature == "VK_VERSION_1_1":
2493 # Extensions can add enum values to Core / extension enums, so add these.
2494 for enum_elem in require.findall("enum"):
2495 if "bitpos" in enum_elem.keys():
2496 # We need to add an extra value to an existing enum type.
2497 # E.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG to VkFormatFeatureFlagBits.
2498 type_name = enum_elem.attrib["extends"]
2499 enum = self.types[type_name]["data"]
2500 enum.add(VkEnumValue(enum_elem.attrib["name"], 1 << int(enum_elem.attrib["bitpos"]), hex=True))
2501 elif "offset" in enum_elem.keys():
2502 # Extensions promoted to Core, have the extension number as part
2503 # of the enum value. Else retrieve from the extension tag.
2504 if enum_elem.attrib.get("extnumber") is not None:
2505 ext_number = int(enum_elem.attrib.get("extnumber"))
2507 ext_number = int(ext.attrib["number"])
2508 offset = int(enum_elem.attrib["offset"])
2509 value = EXT_BASE + (ext_number - 1) * EXT_BLOCK_SIZE + offset
2511 # Deal with negative values.
2512 direction = enum_elem.attrib.get("dir")
2513 if direction is not None:
2516 type_name = enum_elem.attrib["extends"]
2517 enum = self.types[type_name]["data"]
2518 enum.add(VkEnumValue(enum_elem.attrib["name"], value))
2520 elif "value" in enum_elem.keys():
2521 self.consts.append(VkConstant(enum_elem.attrib.get("name"), enum_elem.attrib.get("value")))
2524 # This seems to be used to pull in constants e.g. VK_MAX_DEVICE_GROUP_KHX
2527 # Pull in any commands we need. We infer types to pull in from the command
2529 for command in require.findall("command"):
2530 cmd_name = command.attrib["name"]
2531 self._mark_command_required(cmd_name)
2533 # Store a list with extensions.
2534 ext_info = {"name" : ext_name, "type" : ext_type}
2535 extensions.append(ext_info)
2537 # Sort in alphabetical order.
2538 self.extensions = sorted(extensions, key=lambda ext: ext["name"])
2540 def _parse_features(self, root):
2541 """ Parse the feature section, which describes Core commands and types needed. """
2543 # For now limit to 1.0 features as various 1.1 features need more work.
2544 # In particular interop extensions promoted to Core.
2545 requires = root.findall("./feature/[@name='VK_VERSION_1_0']/require")
2547 for require in requires:
2548 LOGGER.info("Including features for {0}".format(require.attrib.get("comment")))
2550 # Only deal with command. Other values which appear are enum and type for pulling in some
2551 # constants and macros. Tricky to parse, so don't bother right now, we will generate them
2553 if tag.tag == "comment":
2555 elif tag.tag == "command":
2556 name = tag.attrib["name"]
2557 self._mark_command_required(name)
2558 elif tag.tag == "enum":
2559 # We could pull in relevant constants here. Unfortunately
2560 # this only gets half of them pulled in as others indirectly
2561 # get pulled in through structures. Constants don't harm us,
2564 elif tag.tag == "type":
2565 # Pull in types which may not have been pulled in through commands.
2566 name = tag.attrib["name"]
2568 # Skip pull in for vk_platform.h for now.
2569 if name == "vk_platform":
2572 type_info = self.types[name]
2573 type_info["data"].required = True
2575 def _parse_types(self, root):
2576 """ Parse types section, which contains all data types e.g. structs, typedefs etcetera. """
2577 types = root.findall("./types/type")
2589 type_info["category"] = t.attrib.get("category", None)
2591 # We parse aliases in a second pass when we know more.
2592 alias = t.attrib.get("alias")
2594 LOGGER.debug("Alias found: {0}".format(alias))
2595 alias_types.append(t)
2598 if type_info["category"] in ["include"]:
2601 if type_info["category"] == "basetype":
2602 name = t.find("name").text
2603 _type = t.find("type").text
2604 basetype = VkBaseType(name, _type)
2605 base_types.append(basetype)
2606 type_info["data"] = basetype
2608 if type_info["category"] == "bitmask":
2609 name = t.find("name").text
2610 _type = t.find("type").text
2612 # Most bitmasks have a requires attribute used to pull in
2613 # required '*FlagBits" enum.
2614 requires = t.attrib.get("requires")
2615 bitmask = VkBaseType(name, _type, requires=requires)
2616 bitmasks.append(bitmask)
2617 type_info["data"] = bitmask
2619 if type_info["category"] == "define":
2620 define = VkDefine.from_xml(t)
2621 defines.append(define)
2622 type_info["data"] = define
2624 if type_info["category"] == "enum":
2625 name = t.attrib.get("name")
2626 # The type section only contains enum names, not the actual definition.
2627 # Since we already parsed the enum before, just link it in.
2629 type_info["data"] = self.enums[name]
2630 except KeyError as e:
2631 # Not all enums seem to be defined yet, typically that's for
2632 # ones ending in 'FlagBits' where future extensions may add
2634 type_info["data"] = None
2636 if type_info["category"] == "funcpointer":
2637 funcpointer = VkFunctionPointer.from_xml(t)
2638 funcpointers.append(funcpointer)
2639 type_info["data"] = funcpointer
2641 if type_info["category"] == "handle":
2642 handle = VkHandle.from_xml(t)
2643 handles.append(handle)
2644 type_info["data"] = handle
2646 if type_info["category"] in ["struct", "union"]:
2647 # We store unions among structs as some structs depend
2648 # on unions. The types are very similar in parsing and
2649 # generation anyway. The official Vulkan scripts use
2650 # a similar kind of hack.
2651 struct = VkStruct.from_xml(t)
2652 structs.append(struct)
2653 type_info["data"] = struct
2655 # Name is in general within a name tag else it is an optional
2656 # attribute on the type tag.
2657 name_elem = t.find("name")
2658 if name_elem is not None:
2659 type_info["name"] = name_elem.text
2661 type_info["name"] = t.attrib.get("name", None)
2663 # Store all type data in a shared dictionary, so we can easily
2664 # look up information for a given type. There are no duplicate
2666 self.types[type_info["name"]] = type_info
2668 # Second pass for alias types, so we can retrieve all data from
2669 # the aliased object.
2670 for t in alias_types:
2672 type_info["category"] = t.attrib.get("category")
2673 type_info["name"] = t.attrib.get("name")
2675 alias = t.attrib.get("alias")
2677 if type_info["category"] == "bitmask":
2678 bitmask = VkBaseType(type_info["name"], alias, alias=True)
2679 bitmasks.append(bitmask)
2680 type_info["data"] = bitmask
2682 if type_info["category"] == "enum":
2683 enum = VkEnum.from_alias(t, self.types[alias]["data"])
2684 type_info["data"] = enum
2685 self.enums[enum.name] = enum
2687 if type_info["category"] == "handle":
2688 handle = VkHandle.from_alias(t, self.types[alias]["data"])
2689 handles.append(handle)
2690 type_info["data"] = handle
2692 if type_info["category"] == "struct":
2693 struct = VkStruct.from_alias(t, self.types[alias]["data"])
2694 structs.append(struct)
2695 type_info["data"] = struct
2697 self.types[type_info["name"]] = type_info
2699 # We need detailed type information during code generation
2700 # on structs for alignment reasons. Unfortunately structs
2701 # are parsed among other types, so there is no guarantee
2702 # that any types needed have been parsed already, so set
2704 for struct in structs:
2705 struct.set_type_info(self.types)
2707 # Guarantee everything is sorted, so code generation doesn't have
2708 # to deal with this.
2709 self.base_types = sorted(base_types, key=lambda base_type: base_type.name)
2710 self.bitmasks = sorted(bitmasks, key=lambda bitmask: bitmask.name)
2711 self.defines = defines
2712 self.enums = OrderedDict(sorted(self.enums.items()))
2713 self.funcpointers = sorted(funcpointers, key=lambda fp: fp.name)
2714 self.handles = sorted(handles, key=lambda handle: handle.name)
2715 self.structs = sorted(structs, key=lambda struct: struct.name)
2717 def download_vk_xml(filename):
2718 url = "https://raw.github.com/KhronosGroup/Vulkan-Docs/v{0}/xml/vk.xml".format(VK_XML_VERSION)
2719 if not os.path.isfile(filename):
2720 urllib.request.urlretrieve(url, filename)
2723 parser = argparse.ArgumentParser()
2724 parser.add_argument("-v", "--verbose", action="count", default=0, help="increase output verbosity")
2726 args = parser.parse_args()
2727 if args.verbose == 0:
2728 LOGGER.setLevel(logging.WARNING)
2729 elif args.verbose == 1:
2730 LOGGER.setLevel(logging.INFO)
2732 LOGGER.setLevel(logging.DEBUG)
2734 vk_xml = "vk-{0}.xml".format(VK_XML_VERSION)
2735 download_vk_xml(vk_xml)
2736 registry = VkRegistry(vk_xml)
2737 generator = VkGenerator(registry)
2739 with open(WINE_VULKAN_H, "w") as f:
2740 generator.generate_vulkan_h(f)
2742 with open(WINE_VULKAN_DRIVER_H, "w") as f:
2743 generator.generate_vulkan_driver_h(f)
2745 with open(WINE_VULKAN_THUNKS_H, "w") as f:
2746 generator.generate_thunks_h(f, "wine_")
2748 with open(WINE_VULKAN_THUNKS_C, "w") as f:
2749 generator.generate_thunks_c(f, "wine_")
2751 with open(WINE_VULKAN_SPEC, "w") as f:
2752 generator.generate_vulkan_spec(f)
2754 with open(WINE_VULKAN_LOADER_SPEC, "w") as f:
2755 generator.generate_vulkan_loader_spec(f)
2757 if __name__ == "__main__":