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.94"
68 WINE_VK_VERSION = (1, 0)
70 # Filenames to create.
71 WINE_VULKAN_H = "../../include/wine/vulkan.h"
72 WINE_VULKAN_DRIVER_H = "../../include/wine/vulkan_driver.h"
73 WINE_VULKAN_LOADER_SPEC = "../vulkan-1/vulkan-1.spec"
74 WINE_VULKAN_SPEC = "winevulkan.spec"
75 WINE_VULKAN_THUNKS_C = "vulkan_thunks.c"
76 WINE_VULKAN_THUNKS_H = "vulkan_thunks.h"
78 # Extension enum values start at a certain offset (EXT_BASE).
79 # Relative to the offset each extension has a block (EXT_BLOCK_SIZE)
81 # Start for a given extension is:
82 # EXT_BASE + (extension_number-1) * EXT_BLOCK_SIZE
86 # In general instance extensions can't be automatically generated
87 # and need custom wrappers due to e.g. win32 / X11 specific code.
88 # List of supported instance extensions.
89 SUPPORTED_INSTANCE_EXTENSIONS = [
90 "VK_KHR_device_group_creation",
91 "VK_KHR_get_physical_device_properties2",
93 "VK_KHR_win32_surface",
96 BLACKLISTED_EXTENSIONS = [
97 "VK_AMD_memory_overallocation_behavior",
98 # Handling of VK_EXT_debug_report requires some consideration. The win32
99 # loader already provides it for us and it is somewhat usable. If we add
100 # plumbing down to the native layer, we will get each message twice as we
101 # use 2 loaders (win32+native), but we may get output from the driver.
102 # In any case callback conversion is required.
103 "VK_EXT_calibrated_timestamps",
104 "VK_EXT_debug_report",
105 "VK_EXT_display_control", # Requires VK_EXT_display_surface_counter
106 "VK_EXT_external_memory_dma_buf", # Linux specific
107 "VK_EXT_hdr_metadata", # Needs WSI work.
108 "VK_EXT_image_drm_format_modifier",
109 "VK_GOOGLE_display_timing",
110 "VK_KHR_display", # Needs WSI work.
111 "VK_KHR_external_fence",
112 "VK_KHR_external_fence_fd",
113 "VK_KHR_external_fence_win32",
114 "VK_KHR_external_memory",
115 "VK_KHR_external_semaphore",
116 # Relates to external_semaphore and needs type conversions in bitflags.
117 "VK_KHR_external_semaphore_capabilities",
118 "VK_KHR_shared_presentable_image", # Needs WSI work.
119 "VK_KHR_win32_keyed_mutex",
120 "VK_NV_external_memory_win32",
123 # The Vulkan loader provides entry-points for core functionality and important
124 # extensions. Based on vulkan-1.def this amounts to WSI extensions on 1.0.51.
127 "VK_KHR_display_swapchain",
130 "VK_KHR_win32_surface",
133 # Functions part of our winevulkan graphics driver interface.
134 # DRIVER_VERSION should be bumped on any change to driver interface
135 # in FUNCTION_OVERRIDES
138 # Table of functions for which we have a special implementation.
139 # These are regular device / instance functions for which we need
140 # to do more work compared to a regular thunk or because they are
141 # part of the driver interface.
142 # - dispatch set whether we need a function pointer in the device
143 # / instance dispatch table.
144 # - driver sets whether the API is part of the driver interface.
145 # - thunk sets whether to create a thunk in vulkan_thunks.c.
146 FUNCTION_OVERRIDES = {
148 "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : False},
149 "vkEnumerateInstanceExtensionProperties" : {"dispatch" : False, "driver" : True, "thunk" : False},
150 "vkEnumerateInstanceVersion": {"dispatch" : False, "driver" : False, "thunk" : False},
151 "vkGetInstanceProcAddr": {"dispatch" : False, "driver" : True, "thunk" : False},
154 "vkCreateDevice" : {"dispatch" : True, "driver" : False, "thunk" : False},
155 "vkDestroyInstance" : {"dispatch" : False, "driver" : True, "thunk" : False },
156 "vkEnumerateDeviceExtensionProperties" : {"dispatch" : True, "driver" : False, "thunk" : False},
157 "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : False},
158 "vkEnumeratePhysicalDeviceGroups" : {"dispatch" : True, "driver" : False, "thunk" : False},
161 "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : False},
162 "vkCmdExecuteCommands" : {"dispatch" : True, "driver" : False, "thunk" : False},
163 "vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : False},
164 "vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : False},
165 "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : False},
166 "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : False},
167 "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : False},
168 "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : False},
169 "vkGetDeviceQueue2" : {"dispatch": True, "driver" : False, "thunk" : False},
170 "vkQueueSubmit" : {"dispatch": True, "driver" : False, "thunk" : False},
173 "vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
174 "vkGetPhysicalDeviceSurfaceSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
175 "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
176 "vkGetPhysicalDeviceSurfaceFormatsKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
177 "vkGetPhysicalDeviceSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
179 # VK_KHR_win32_surface
180 "vkCreateWin32SurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
181 "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
184 "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
185 "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
186 "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : True},
187 "vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : True},
189 # VK_KHR_device_group_creation
190 "vkEnumeratePhysicalDeviceGroupsKHR" : {"dispatch" : True, "driver" : False, "thunk" : False},
192 # VK_KHR_device_group
193 "vkGetDeviceGroupSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
194 "vkGetPhysicalDevicePresentRectanglesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
198 class Direction(Enum):
199 """ Parameter direction: input, output, input_output. """
205 class VkBaseType(object):
206 def __init__(self, name, _type, alias=None, requires=None):
207 """ Vulkan base type class.
209 VkBaseType is mostly used by Vulkan to define its own
210 base types like VkFlags through typedef out of e.g. uint32_t.
213 name (:obj:'str'): Name of the base type.
214 _type (:obj:'str'): Underlaying type
215 alias (bool): type is an alias or not.
216 requires (:obj:'str', optional): Other types required.
217 Often bitmask values pull in a *FlagBits type.
222 self.requires = requires
223 self.required = False
225 def definition(self):
226 # Definition is similar for alias or non-alias as type
227 # is already set to alias.
228 return "typedef {0} {1};\n".format(self.type, self.name)
231 return bool(self.alias)
234 class VkConstant(object):
235 def __init__(self, name, value):
239 def definition(self):
240 text = "#define {0} {1}\n".format(self.name, self.value)
244 class VkDefine(object):
245 def __init__(self, name, value):
250 def from_xml(define):
251 name_elem = define.find("name")
253 if name_elem is None:
254 # <type category="define" name="some_name">some_value</type>
255 # At the time of writing there is only 1 define of this category
256 # 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'.
257 name = define.attrib.get("name")
259 # We override behavior of VK_DEFINE_NON_DISPATCHABLE handle as the default
260 # definition various between 64-bit (uses pointers) and 32-bit (uses uint64_t).
261 # This complicates TRACEs in the thunks, so just use uint64_t.
262 if name == "VK_DEFINE_NON_DISPATCHABLE_HANDLE":
263 value = "#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;"
266 return VkDefine(name, value)
268 # With a name element the structure is like:
269 # <type category="define"><name>some_name</name>some_value</type>
270 name = name_elem.text
272 # Perform minimal parsing for Vulkan constants, which we don't need, but are referenced
273 # elsewhere in vk.xml.
274 # - VK_API_VERSION is a messy, deprecated constant and we don't want generate code for it.
275 # - AHardwareBuffer/ANativeWindow are forard declarations for Android types, which leaked
276 # into the define region.
277 if name in ["VK_API_VERSION", "AHardwareBuffer", "ANativeWindow"]:
278 return VkDefine(name, None)
280 # The body of the define is basically unstructured C code. It is not meant for easy parsing.
281 # Some lines contain deprecated values or comments, which we try to filter out.
283 for line in define.text.splitlines():
284 # Skip comments or deprecated values.
291 if child.tail is not None:
292 # Split comments for VK_API_VERSION_1_0 / VK_API_VERSION_1_1
293 if "//" in child.tail:
294 value += child.tail.split("//")[0]
298 return VkDefine(name, value.rstrip(' '))
300 def definition(self):
301 if self.value is None:
304 # Nothing to do as the value was already put in the right form during parsing.
305 return "{0}\n".format(self.value)
308 class VkEnum(object):
309 def __init__(self, name, values, alias=None):
312 self.required = False
316 def from_alias(enum, alias):
317 name = enum.attrib.get("name")
318 return VkEnum(name, alias.values, alias=alias)
322 name = enum.attrib.get("name")
325 for v in enum.findall("enum"):
326 # Value is either a value or a bitpos, only one can exist.
327 value = v.attrib.get("value")
328 alias_name = v.attrib.get("alias")
330 alias = next(x for x in values if x.name == alias_name)
331 values.append(VkEnumValue(v.attrib.get("name"), alias.value, alias.hex))
333 # Some values are in hex form. We want to preserve the hex representation
334 # at least when we convert back to a string. Internally we want to use int.
336 values.append(VkEnumValue(v.attrib.get("name"), int(value, 0), hex=True))
338 values.append(VkEnumValue(v.attrib.get("name"), int(value, 0)))
341 value = 1 << int(v.attrib.get("bitpos"))
342 values.append(VkEnumValue(v.attrib.get("name"), value, hex=True))
344 # vulkan.h contains a *_MAX_ENUM value set to 32-bit at the time of writing,
345 # which is to prepare for extensions as they can add values and hence affect
346 # the size definition.
347 max_name = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2', name).upper() + "_MAX_ENUM"
348 values.append(VkEnumValue(max_name, 0x7fffffff, hex=True))
350 return VkEnum(name, values)
352 def add(self, value):
353 """ Add a value to enum. """
355 # Extensions can add new enum values. When an extension is promoted to Core
356 # the registry defines the value twice once for old extension and once for
357 # new Core features. Ignore the duplicate entry.
358 for v in self.values:
359 if v.value == value.value:
360 LOGGER.debug("Adding duplicate enum value {0} to {1}".format(v, self.name))
362 self.values.append(value)
364 def definition(self):
365 text = "typedef enum {0}\n{{\n".format(self.name)
367 # Print values sorted, values can have been added in a random order.
368 values = sorted(self.values, key=lambda value: value.value)
370 text += " {0},\n".format(value.definition())
371 text += "}} {0};\n\n".format(self.name)
375 return bool(self.alias)
378 class VkEnumValue(object):
379 def __init__(self, name, value, hex=False):
385 return "{0}={1}".format(self.name, self.value)
387 def definition(self):
388 """ Convert to text definition e.g. VK_FOO = 1 """
390 # Hex is commonly used for FlagBits and sometimes within
391 # a non-FlagBits enum for a bitmask value as well.
393 return "{0} = 0x{1:08x}".format(self.name, self.value)
395 return "{0} = {1}".format(self.name, self.value)
398 class VkFunction(object):
399 def __init__(self, _type=None, name=None, params=[], extensions=[], alias=None):
406 # For some functions we need some extra metadata from FUNCTION_OVERRIDES.
407 func_info = FUNCTION_OVERRIDES.get(self.name, None)
408 self.dispatch = func_info["dispatch"] if func_info is not None else True
409 self.driver = func_info["driver"] if func_info is not None else False
410 self.thunk_needed = func_info["thunk"] if func_info is not None else True
412 # Required is set while parsing which APIs and types are required
413 # and is used by the code generation.
414 self.required = True if func_info else False
417 def from_alias(command, alias):
418 """ Create VkFunction from an alias command.
421 command: xml data for command
422 alias (VkFunction): function to use as a base for types / parameters.
427 func_name = command.attrib.get("name")
428 func_type = alias.type
429 params = alias.params
431 return VkFunction(_type=func_type, name=func_name, params=params, alias=alias)
434 def from_xml(command, types):
435 proto = command.find("proto")
436 func_name = proto.find("name").text
437 func_type = proto.find("type").text
440 for param in command.findall("param"):
441 vk_param = VkParam.from_xml(param, types)
442 params.append(vk_param)
444 return VkFunction(_type=func_type, name=func_name, params=params)
446 def get_conversions(self):
447 """ Get a list of conversion functions required for this function if any.
448 Parameters which are structures may require conversion between win32
449 and the host platform. This function returns a list of conversions
454 for param in self.params:
455 convs = param.get_conversions()
456 if convs is not None:
457 conversions.extend(convs)
462 return bool(self.alias)
464 def is_core_func(self):
465 """ Returns whether the function is a Vulkan core function.
466 Core functions are APIs defined by the Vulkan spec to be part of the
467 Core API as well as several KHR WSI extensions.
470 if not self.extensions:
473 return any(ext in self.extensions for ext in CORE_EXTENSIONS)
475 def is_device_func(self):
476 # If none of the other, it must be a device function
477 return not self.is_global_func() and not self.is_instance_func()
479 def is_driver_func(self):
480 """ Returns if function is part of Wine driver interface. """
483 def is_global_func(self):
484 # Treat vkGetInstanceProcAddr as a global function as it
485 # can operate with NULL for vkInstance.
486 if self.name == "vkGetInstanceProcAddr":
488 # Global functions are not passed a dispatchable object.
489 elif self.params[0].is_dispatchable():
493 def is_instance_func(self):
494 # Instance functions are passed VkInstance or VkPhysicalDevice.
495 if self.params[0].type in ["VkInstance", "VkPhysicalDevice"]:
499 def is_required(self):
502 def needs_conversion(self):
503 """ Check if the function needs any input/output type conversion.
504 Functions need input/output conversion if struct parameters have
505 alignment differences between Win32 and Linux 32-bit.
508 for p in self.params:
509 if p.needs_conversion():
510 LOGGER.debug("Parameter {0} to {1} requires conversion".format(p.name, self.name))
515 def needs_dispatch(self):
518 def needs_thunk(self):
519 return self.thunk_needed
521 def pfn(self, prefix="p", call_conv=None, conv=False):
522 """ Create function pointer. """
525 pfn = "{0} ({1} *{2}_{3})(".format(self.type, call_conv, prefix, self.name)
527 pfn = "{0} (*{1}_{2})(".format(self.type, prefix, self.name)
529 for i, param in enumerate(self.params):
531 pfn += param.const + " "
534 if conv and param.needs_conversion():
537 if param.is_pointer():
538 pfn += " " + param.pointer
540 if param.array_len is not None:
541 pfn += "[{0}]".format(param.array_len)
543 if i < len(self.params) - 1:
548 def prototype(self, call_conv=None, prefix=None, postfix=None):
549 """ Generate prototype for given function.
552 call_conv (str, optional): calling convention e.g. WINAPI
553 prefix (str, optional): prefix to append prior to function name e.g. vkFoo -> wine_vkFoo
554 postfix (str, optional): text to append after function name but prior to semicolon e.g. DECLSPEC_HIDDEN
557 proto = "{0}".format(self.type)
559 if call_conv is not None:
560 proto += " {0}".format(call_conv)
562 if prefix is not None:
563 proto += " {0}{1}(".format(prefix, self.name)
565 proto += " {0}(".format(self.name)
567 # Add all the parameters.
568 proto += ", ".join([p.definition() for p in self.params])
570 if postfix is not None:
571 proto += ") {0}".format(postfix)
578 body = " {0}".format(self.trace())
580 params = ", ".join([p.variable(conv=False) for p in self.params])
582 # Call the native Vulkan function.
583 if self.type == "void":
584 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
586 body += " return {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
590 def body_conversion(self):
593 # Declare a variable to hold the result for non-void functions.
594 if self.type != "void":
595 body += " {0} result;\n".format(self.type)
597 # Declare any tmp parameters for conversion.
598 for p in self.params:
599 if not p.needs_conversion():
602 if p.is_dynamic_array():
603 body += " {0}_host *{1}_host;\n".format(p.type, p.name)
605 body += " {0}_host {1}_host;\n".format(p.type, p.name)
607 body += " {0}\n".format(self.trace())
609 # Call any win_to_host conversion calls.
610 for p in self.params:
611 if not p.needs_input_conversion():
614 body += p.copy(Direction.INPUT)
616 # Build list of parameters containing converted and non-converted parameters.
617 # The param itself knows if conversion is needed and applies it when we set conv=True.
618 params = ", ".join([p.variable(conv=True) for p in self.params])
620 # Call the native Vulkan function.
621 if self.type == "void":
622 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
624 body += " result = {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
628 # Call any host_to_win conversion calls.
629 for p in self.params:
630 if not p.needs_output_conversion():
633 body += p.copy(Direction.OUTPUT)
635 # Perform any required cleanups. Most of these are for array functions.
636 for p in self.params:
637 if not p.needs_free():
642 # Finally return the result.
643 if self.type != "void":
644 body += " return result;\n"
648 def spec(self, prefix=None, symbol=None):
649 """ Generate spec file entry for this function.
652 prefix (str, optional): prefix to prepend to entry point name.
653 symbol (str, optional): allows overriding the name of the function implementing the entry point.
657 params = " ".join([p.spec() for p in self.params])
658 if prefix is not None:
659 spec += "@ stdcall -private {0}{1}({2})".format(prefix, self.name, params)
661 spec += "@ stdcall {0}({1})".format(self.name, params)
663 if symbol is not None:
669 def stub(self, call_conv=None, prefix=None):
670 stub = self.prototype(call_conv=call_conv, prefix=prefix)
672 stub += " {0}".format(self.trace(message="stub: ", trace_func="FIXME"))
674 if self.type == "VkResult":
675 stub += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
676 elif self.type == "VkBool32":
677 stub += " return VK_FALSE;\n"
678 elif self.type == "PFN_vkVoidFunction":
679 stub += " return NULL;\n"
684 def thunk(self, call_conv=None, prefix=None):
685 thunk = self.prototype(call_conv=call_conv, prefix=prefix)
688 if self.needs_conversion():
689 thunk += "#if defined(USE_STRUCT_CONVERSION)\n"
690 thunk += self.body_conversion()
700 def trace(self, message=None, trace_func=None):
701 """ Create a trace string including all parameters.
704 message (str, optional): text to print at start of trace message e.g. 'stub: '
705 trace_func (str, optional): used to override trace function e.g. FIXME, printf, etcetera.
707 if trace_func is not None:
708 trace = "{0}(\"".format(trace_func)
712 if message is not None:
715 # First loop is for all the format strings.
716 trace += ", ".join([p.format_string() for p in self.params])
719 # Second loop for parameter names and optional conversions.
720 for param in self.params:
721 if param.format_conv is not None:
722 trace += ", " + param.format_conv.format(param.name)
724 trace += ", {0}".format(param.name)
730 class VkFunctionPointer(object):
731 def __init__(self, _type, name, members):
733 self.members = members
735 self.required = False
738 def from_xml(funcpointer):
742 for t in funcpointer.findall("type"):
744 # <type>void</type>* pUserData,
745 # Parsing of the tail (anything past </type>) is tricky since there
746 # can be other data on the next line like: const <type>int</type>..
748 const = True if begin and "const" in begin else False
750 lines = t.tail.split(",\n")
751 if lines[0][0] == "*":
753 name = lines[0][1:].strip()
756 name = lines[0].strip()
758 # Filter out ); if it is contained.
759 name = name.partition(");")[0]
761 # If tail encompasses multiple lines, assign the second line to begin
764 begin = lines[1].strip()
768 members.append(VkMember(const=const, _type=_type, pointer=pointer, name=name))
770 _type = funcpointer.text
771 name = funcpointer.find("name").text
772 return VkFunctionPointer(_type, name, members)
774 def definition(self):
775 text = "{0} {1})(\n".format(self.type, self.name)
778 if len(self.members) > 0:
779 for m in self.members:
781 text += " " + m.definition()
784 text += ",\n " + m.definition()
786 # Just make the compiler happy by adding a void parameter.
792 class VkHandle(object):
793 def __init__(self, name, _type, parent, alias=None):
798 self.required = False
801 def from_alias(handle, alias):
802 name = handle.attrib.get("name")
803 return VkHandle(name, alias.type, alias.parent, alias=alias)
806 def from_xml(handle):
807 name = handle.find("name").text
808 _type = handle.find("type").text
809 parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice.
810 return VkHandle(name, _type, parent)
812 def dispatch_table(self):
813 if not self.is_dispatchable():
816 if self.parent is None:
817 # Should only happen for VkInstance
819 elif self.name == "VkDevice":
820 # VkDevice has VkInstance as a parent, but has its own dispatch table.
822 elif self.parent in ["VkInstance", "VkPhysicalDevice"]:
823 return "instance->funcs"
824 elif self.parent in ["VkDevice", "VkCommandPool"]:
825 return "device->funcs"
827 LOGGER.error("Unhandled dispatchable parent: {0}".format(self.parent))
829 def definition(self):
830 """ Generates handle definition e.g. VK_DEFINE_HANDLE(vkInstance) """
832 # Legacy types are typedef'ed to the new type if they are aliases.
834 return "typedef {0} {1};\n".format(self.alias.name, self.name)
836 return "{0}({1})\n".format(self.type, self.name)
839 return self.alias is not None
841 def is_dispatchable(self):
842 """ Some handles like VkInstance, VkDevice are dispatchable objects,
843 which means they contain a dispatch table of function pointers.
845 return self.type == "VK_DEFINE_HANDLE"
847 def is_required(self):
850 def native_handle(self, name):
851 """ Provide access to the native handle of a wrapped object. """
853 if self.name == "VkCommandPool":
854 return "wine_cmd_pool_from_handle({0})->command_pool".format(name)
856 native_handle_name = None
858 if self.name == "VkCommandBuffer":
859 native_handle_name = "command_buffer"
860 if self.name == "VkDevice":
861 native_handle_name = "device"
862 if self.name == "VkInstance":
863 native_handle_name = "instance"
864 if self.name == "VkPhysicalDevice":
865 native_handle_name = "phys_dev"
866 if self.name == "VkQueue":
867 native_handle_name = "queue"
869 if native_handle_name:
870 return "{0}->{1}".format(name, native_handle_name)
872 if self.is_dispatchable():
873 LOGGER.error("Unhandled native handle for: {0}".format(self.name))
877 class VkMember(object):
878 def __init__(self, const=False, struct_fwd_decl=False,_type=None, pointer=None, name=None, array_len=None,
879 dyn_array_len=None, optional=False):
881 self.struct_fwd_decl = struct_fwd_decl
883 self.pointer = pointer
885 self.type_info = None
886 self.array_len = array_len
887 self.dyn_array_len = dyn_array_len
888 self.optional = optional
890 def __eq__(self, other):
891 """ Compare member based on name against a string.
893 This method is for convenience by VkStruct, which holds a number of members and needs quick checking
894 if certain members exist.
897 if self.name == other:
903 return "{0} {1} {2} {3} {4} {5} {6}".format(self.const, self.struct_fwd_decl, self.type, self.pointer,
904 self.name, self.array_len, self.dyn_array_len)
907 def from_xml(member):
908 """ Helper function for parsing a member tag within a struct or union. """
910 name_elem = member.find("name")
911 type_elem = member.find("type")
914 struct_fwd_decl = False
920 if "const" in member.text:
923 # Some members contain forward declarations:
924 # - VkBaseInstructure has a member "const struct VkBaseInStructure *pNext"
925 # - VkWaylandSurfaceCreateInfoKHR has a member "struct wl_display *display"
926 if "struct" in member.text:
927 struct_fwd_decl = True
929 if type_elem is not None:
930 member_type = type_elem.text
931 if type_elem.tail is not None:
932 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
934 # Name of other member within, which stores the number of
935 # elements pointed to be by this member.
936 dyn_array_len = member.get("len")
938 # Some members are optional, which is important for conversion code e.g. not dereference NULL pointer.
939 optional = True if member.get("optional") else False
941 # Usually we need to allocate memory for dynamic arrays. We need to do the same in a few other cases
942 # like for VkCommandBufferBeginInfo.pInheritanceInfo. Just threat such cases as dynamic arrays of
943 # size 1 to simplify code generation.
944 if dyn_array_len is None and pointer is not None:
947 # Some members are arrays, attempt to parse these. Formats include:
948 # <member><type>char</type><name>extensionName</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
949 # <member><type>uint32_t</type><name>foo</name>[4]</member>
950 if name_elem.tail and name_elem.tail[0] == '[':
951 LOGGER.debug("Found array type")
952 enum_elem = member.find("enum")
953 if enum_elem is not None:
954 array_len = enum_elem.text
956 # Remove brackets around length
957 array_len = name_elem.tail.strip("[]")
959 return VkMember(const=const, struct_fwd_decl=struct_fwd_decl, _type=member_type, pointer=pointer, name=name_elem.text,
960 array_len=array_len, dyn_array_len=dyn_array_len, optional=optional)
962 def copy(self, input, output, direction):
963 """ Helper method for use by conversion logic to generate a C-code statement to copy this member. """
965 if self.needs_conversion():
966 if self.is_dynamic_array():
967 if direction == Direction.OUTPUT:
968 LOGGER.warn("TODO: implement copying of returnedonly dynamic array for {0}.{1}".format(self.type, self.name))
970 # Array length is either a variable name (string) or an int.
971 count = self.dyn_array_len if isinstance(self.dyn_array_len, int) else "{0}{1}".format(input, self.dyn_array_len)
972 return "{0}{1} = convert_{2}_array_win_to_host({3}{1}, {4});\n".format(output, self.name, self.type, input, count)
973 elif self.is_static_array():
974 count = self.array_len
975 if direction == Direction.OUTPUT:
976 # Needed by VkMemoryHeap.memoryHeaps
977 return "convert_{0}_static_array_host_to_win({2}{1}, {3}{1}, {4});\n".format(self.type, self.name, input, output, count)
979 # Nothing needed this yet.
980 LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
982 if direction == Direction.OUTPUT:
983 return "convert_{0}_host_to_win(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
985 return "convert_{0}_win_to_host(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
986 elif self.is_static_array():
987 bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type)
988 return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count)
990 return "{0}{1} = {2}{1};\n".format(output, self.name, input)
992 def definition(self, align=False, conv=False):
993 """ Generate prototype for given function.
996 align (bool, optional): Enable alignment if a type needs it. This adds WINE_VK_ALIGN(8) to a member.
997 conv (bool, optional): Enable conversion if a type needs it. This appends '_host' to the name.
1004 if self.is_struct_forward_declaration():
1007 if conv and self.is_struct():
1008 text += "{0}_host".format(self.type)
1012 if self.is_pointer():
1013 text += " {0}{1}".format(self.pointer, self.name)
1015 if align and self.needs_alignment():
1016 text += " WINE_VK_ALIGN(8) " + self.name
1018 text += " " + self.name
1020 if self.is_static_array():
1021 text += "[{0}]".format(self.array_len)
1025 def get_conversions(self):
1026 """ Return any conversion description for this member and its children when conversion is needed. """
1028 # Check if we need conversion either for this member itself or for any child members
1029 # in case member represents a struct.
1030 if not self.needs_conversion():
1035 # Collect any conversion for any member structs.
1036 struct = self.type_info["data"]
1038 m.needs_struct_extensions_conversion()
1039 if m.needs_conversion():
1040 conversions.extend(m.get_conversions())
1042 struct.needs_struct_extensions_conversion()
1044 struct = self.type_info["data"]
1045 direction = Direction.OUTPUT if struct.returnedonly else Direction.INPUT
1046 if self.is_dynamic_array():
1047 conversions.append(ConversionFunction(False, True, direction, struct))
1048 elif self.is_static_array():
1049 conversions.append(ConversionFunction(True, False, direction, struct))
1051 conversions.append(ConversionFunction(False, False, direction, struct))
1053 if self.needs_free():
1054 conversions.append(FreeFunction(self.is_dynamic_array(), struct))
1061 def is_dynamic_array(self):
1062 """ Returns if the member is an array element.
1063 Vulkan uses this for dynamically sized arrays for which
1064 there is a 'count' parameter.
1066 return self.dyn_array_len is not None
1068 def is_handle(self):
1069 return self.type_info["category"] == "handle"
1071 def is_pointer(self):
1072 return self.pointer is not None
1074 def is_static_array(self):
1075 """ Returns if the member is an array.
1076 Vulkan uses this often for fixed size arrays in which the
1077 length is part of the member.
1079 return self.array_len is not None
1081 def is_struct(self):
1082 return self.type_info["category"] == "struct"
1084 def is_struct_forward_declaration(self):
1085 return self.struct_fwd_decl
1088 return self.type_info["category"] == "union"
1090 def needs_alignment(self):
1091 """ Check if this member needs alignment for 64-bit data.
1092 Various structures need alignment on 64-bit variables due
1093 to compiler differences on 32-bit between Win32 and Linux.
1096 if self.is_pointer():
1098 elif self.type == "size_t":
1100 elif self.type in ["uint64_t", "VkDeviceSize"]:
1102 elif self.is_struct():
1103 struct = self.type_info["data"]
1104 return struct.needs_alignment()
1105 elif self.is_handle():
1106 # Dispatchable handles are pointers to objects, while
1107 # non-dispatchable are uint64_t and hence need alignment.
1108 handle = self.type_info["data"]
1109 return False if handle.is_dispatchable() else True
1112 def needs_conversion(self):
1113 """ Structures requiring alignment, need conversion between win32 and host. """
1115 if not self.is_struct():
1118 struct = self.type_info["data"]
1119 return struct.needs_conversion()
1121 def needs_free(self):
1122 if not self.needs_conversion():
1125 if self.is_dynamic_array():
1128 # TODO: some non-pointer structs and optional pointer structs may need freeing,
1129 # though none of this type have been encountered yet.
1132 def needs_struct_extensions_conversion(self):
1133 if not self.is_struct():
1136 struct = self.type_info["data"]
1137 return struct.needs_struct_extensions_conversion()
1139 def set_type_info(self, type_info):
1140 """ Helper function to set type information from the type registry.
1141 This is needed, because not all type data is available at time of
1144 self.type_info = type_info
1147 class VkParam(object):
1148 """ Helper class which describes a parameter to a function call. """
1150 def __init__(self, type_info, const=None, pointer=None, name=None, array_len=None, dyn_array_len=None):
1153 self.array_len = array_len
1154 self.dyn_array_len = dyn_array_len
1155 self.pointer = pointer
1156 self.type_info = type_info
1157 self.type = type_info["name"] # For convenience
1158 self.handle = type_info["data"] if type_info["category"] == "handle" else None
1159 self.struct = type_info["data"] if type_info["category"] == "struct" else None
1161 self._set_direction()
1162 self._set_format_string()
1163 self._set_conversions()
1166 return "{0} {1} {2} {3} {4} {5}".format(self.const, self.type, self.pointer, self.name, self.array_len, self.dyn_array_len)
1169 def from_xml(param, types):
1170 """ Helper function to create VkParam from xml. """
1172 # Parameter parsing is slightly tricky. All the data is contained within
1173 # a param tag, but some data is within subtags while others are text
1174 # before or after the type tag.
1176 # <param>const <type>char</type>* <name>pLayerName</name></param>
1178 name_elem = param.find("name")
1180 name = name_elem.text
1181 # Tail contains array length e.g. for blendConstants param of vkSetBlendConstants
1182 if name_elem.tail is not None:
1183 array_len = name_elem.tail.strip("[]")
1185 # Name of other parameter in function prototype, which stores the number of
1186 # elements pointed to be by this parameter.
1187 dyn_array_len = param.get("len", None)
1189 const = param.text.strip() if param.text else None
1190 type_elem = param.find("type")
1191 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1193 # Since we have parsed all types before hand, this should not happen.
1194 type_info = types.get(type_elem.text, None)
1195 if type_info is None:
1196 LOGGER.err("type info not found for: {0}".format(type_elem.text))
1198 return VkParam(type_info, const=const, pointer=pointer, name=name, array_len=array_len, dyn_array_len=dyn_array_len)
1200 def _set_conversions(self):
1201 """ Internal helper function to configure any needed conversion functions. """
1203 self.free_func = None
1204 self.input_conv = None
1205 self.output_conv = None
1206 if not self.needs_conversion():
1209 # Input functions require win to host conversion.
1210 if self._direction in [Direction.INPUT, Direction.INPUT_OUTPUT]:
1211 self.input_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.INPUT, self.struct)
1213 # Output functions require host to win conversion.
1214 if self._direction in [Direction.INPUT_OUTPUT, Direction.OUTPUT]:
1215 self.output_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.OUTPUT, self.struct)
1217 # Dynamic arrays, but also some normal structs (e.g. VkCommandBufferBeginInfo) need memory
1218 # allocation and thus some cleanup.
1219 if self.is_dynamic_array() or self.struct.needs_free():
1220 self.free_func = FreeFunction(self.is_dynamic_array(), self.struct)
1222 def _set_direction(self):
1223 """ Internal helper function to set parameter direction (input/output/input_output). """
1225 # The parameter direction needs to be determined from hints in vk.xml like returnedonly,
1226 # parameter constness and other heuristics.
1227 # For now we need to get this right for structures as we need to convert these, we may have
1228 # missed a few other edge cases (e.g. count variables).
1229 # See also https://github.com/KhronosGroup/Vulkan-Docs/issues/610
1231 if not self.is_pointer():
1232 self._direction = Direction.INPUT
1233 elif self.is_const() and self.is_pointer():
1234 self._direction = Direction.INPUT
1235 elif self.is_struct():
1236 if not self.struct.returnedonly:
1237 self._direction = Direction.INPUT
1240 # Returnedonly hints towards output, however in some cases
1241 # it is inputoutput. In particular if pNext / sType exist,
1242 # which are used to link in other structures without having
1243 # to introduce new APIs. E.g. vkGetPhysicalDeviceProperties2KHR.
1244 if "pNext" in self.struct:
1245 self._direction = Direction.INPUT_OUTPUT
1248 self._direction = Direction.OUTPUT
1250 # This should mostly be right. Count variables can be inout, but we don't care about these yet.
1251 self._direction = Direction.OUTPUT
1253 def _set_format_string(self):
1254 """ Internal helper function to be used by constructor to set format string. """
1256 # Determine a format string used by code generation for traces.
1257 # 64-bit types need a conversion function.
1258 self.format_conv = None
1259 if self.is_static_array() or self.is_pointer():
1260 self.format_str = "%p"
1262 if self.type_info["category"] in ["bitmask", "enum"]:
1263 self.format_str = "%#x"
1264 elif self.is_handle():
1265 # We use uint64_t for non-dispatchable handles as opposed to pointers
1266 # for dispatchable handles.
1267 if self.handle.is_dispatchable():
1268 self.format_str = "%p"
1270 self.format_str = "0x%s"
1271 self.format_conv = "wine_dbgstr_longlong({0})"
1272 elif self.type == "float":
1273 self.format_str = "%f"
1274 elif self.type == "int":
1275 self.format_str = "%d"
1276 elif self.type == "int32_t":
1277 self.format_str = "%d"
1278 elif self.type == "size_t":
1279 self.format_str = "0x%s"
1280 self.format_conv = "wine_dbgstr_longlong({0})"
1281 elif self.type in ["uint32_t", "VkBool32"]:
1282 self.format_str = "%u"
1283 elif self.type in ["uint64_t", "VkDeviceSize"]:
1284 self.format_str = "0x%s"
1285 self.format_conv = "wine_dbgstr_longlong({0})"
1286 elif self.type == "HANDLE":
1287 self.format_str = "%p"
1288 elif self.type in ["VisualID", "xcb_visualid_t", "RROutput"]:
1289 # Don't care about Linux specific types.
1290 self.format_str = ""
1292 LOGGER.warn("Unhandled type: {0}".format(self.type_info))
1294 def copy(self, direction):
1295 if direction == Direction.INPUT:
1296 if self.is_dynamic_array():
1297 return " {0}_host = convert_{1}_array_win_to_host({0}, {2});\n".format(self.name, self.type, self.dyn_array_len)
1299 return " convert_{0}_win_to_host({1}, &{1}_host);\n".format(self.type, self.name)
1301 if self.is_dynamic_array():
1302 LOGGER.error("Unimplemented output conversion for: {0}".format(self.name))
1304 return " convert_{0}_host_to_win(&{1}_host, {1});\n".format(self.type, self.name)
1306 def definition(self, postfix=None):
1307 """ Return prototype for the parameter. E.g. 'const char *foo' """
1311 proto += self.const + " "
1315 if self.is_pointer():
1316 proto += " {0}{1}".format(self.pointer, self.name)
1318 proto += " " + self.name
1320 # Allows appending something to the variable name useful for
1321 # win32 to host conversion.
1322 if postfix is not None:
1325 if self.is_static_array():
1326 proto += "[{0}]".format(self.array_len)
1330 def direction(self):
1331 """ Returns parameter direction: input, output, input_output.
1333 Parameter direction in Vulkan is not straight-forward, which this function determines.
1336 return self._direction
1338 def dispatch_table(self):
1339 """ Return functions dispatch table pointer for dispatchable objects. """
1341 if not self.is_dispatchable():
1344 return "{0}->{1}".format(self.name, self.handle.dispatch_table())
1346 def format_string(self):
1347 return self.format_str
1350 if self.is_dynamic_array():
1351 if self.struct.returnedonly:
1352 # For returnedonly, counts is stored in a pointer.
1353 return " free_{0}_array({1}_host, *{2});\n".format(self.type, self.name, self.dyn_array_len)
1355 return " free_{0}_array({1}_host, {2});\n".format(self.type, self.name, self.dyn_array_len)
1357 # We are operating on a single structure. Some structs (very rare) contain dynamic members,
1358 # which would need freeing.
1359 if self.struct.needs_free():
1360 return " free_{0}(&{1}_host);\n".format(self.type, self.name)
1363 def get_conversions(self):
1364 """ Get a list of conversions required for this parameter if any.
1365 Parameters which are structures may require conversion between win32
1366 and the host platform. This function returns a list of conversions
1370 if not self.is_struct():
1373 self.struct.needs_struct_extensions_conversion()
1374 for m in self.struct:
1375 m.needs_struct_extensions_conversion()
1377 if not self.needs_conversion():
1382 # Collect any member conversions first, so we can guarantee
1383 # those functions will be defined prior to usage by the
1384 # 'parent' param requiring conversion.
1385 for m in self.struct:
1386 if not m.is_struct():
1389 if not m.needs_conversion():
1392 conversions.extend(m.get_conversions())
1394 # Conversion requirements for the 'parent' parameter.
1395 if self.input_conv is not None:
1396 conversions.append(self.input_conv)
1397 if self.output_conv is not None:
1398 conversions.append(self.output_conv)
1399 if self.free_func is not None:
1400 conversions.append(self.free_func)
1405 return self.const is not None
1407 def is_dynamic_array(self):
1408 return self.dyn_array_len is not None
1410 def is_dispatchable(self):
1411 if not self.is_handle():
1414 return self.handle.is_dispatchable()
1416 def is_handle(self):
1417 return self.handle is not None
1419 def is_pointer(self):
1420 return self.pointer is not None
1422 def is_static_array(self):
1423 return self.array_len is not None
1425 def is_struct(self):
1426 return self.struct is not None
1428 def needs_conversion(self):
1429 """ Returns if parameter needs conversion between win32 and host. """
1431 if not self.is_struct():
1434 # VkSparseImageMemoryRequirements(2) is used by vkGetImageSparseMemoryRequirements(2).
1435 # This function is tricky to wrap, because how to wrap depends on whether
1436 # pSparseMemoryRequirements is NULL or not. Luckily for VkSparseImageMemoryRequirements(2)
1437 # the alignment works out in such a way that no conversion is needed between win32 and Linux.
1438 if self.type in ["VkSparseImageMemoryRequirements", "VkSparseImageMemoryRequirements2"]:
1441 # If a structure needs alignment changes, it means we need to
1442 # perform parameter conversion between win32 and host.
1443 if self.struct.needs_conversion():
1448 def needs_free(self):
1449 return self.free_func is not None
1451 def needs_input_conversion(self):
1452 return self.input_conv is not None
1454 def needs_output_conversion(self):
1455 return self.output_conv is not None
1458 """ Generate spec file entry for this parameter. """
1460 if self.type_info["category"] in ["bitmask", "enum"]:
1462 if self.is_pointer() and self.type == "char":
1464 if self.is_dispatchable() or self.is_pointer() or self.is_static_array():
1466 if self.is_handle() and not self.is_dispatchable():
1468 if self.type == "float":
1470 if self.type in ["int", "int32_t", "size_t", "uint32_t", "VkBool32"]:
1472 if self.type in ["uint64_t", "VkDeviceSize"]:
1475 LOGGER.error("Unhandled spec conversion for type: {0}".format(self.type))
1477 def variable(self, conv=False):
1478 """ Returns 'glue' code during generation of a function call on how to access the variable.
1479 This function handles various scenarios such as 'unwrapping' if dispatchable objects and
1480 renaming of parameters in case of win32 -> host conversion.
1483 conv (bool, optional): Enable conversion if the param needs it. This appends '_host' to the name.
1486 # Hack until we enable allocation callbacks from ICD to application. These are a joy
1487 # to enable one day, because of calling convention conversion.
1488 if "VkAllocationCallbacks" in self.type:
1489 LOGGER.debug("TODO: setting NULL VkAllocationCallbacks for {0}".format(self.name))
1492 if conv and self.needs_conversion():
1493 if self.is_dynamic_array():
1494 return "{0}_host".format(self.name)
1496 return "&{0}_host".format(self.name)
1498 # We need to pass the native handle to the native Vulkan calls.
1499 native_handle = self.handle.native_handle(self.name) if self.is_handle() else None
1500 return native_handle if native_handle else self.name
1503 class VkStruct(Sequence):
1504 """ Class which represents the type union and struct. """
1506 def __init__(self, name, members, returnedonly, structextends, alias=None, union=False):
1508 self.members = members
1509 self.returnedonly = returnedonly
1510 self.structextends = structextends
1511 self.required = False
1514 self.type_info = None # To be set later.
1515 self.struct_extensions = []
1517 def __getitem__(self, i):
1518 return self.members[i]
1521 return len(self.members)
1524 def from_alias(struct, alias):
1525 name = struct.attrib.get("name")
1526 return VkStruct(name, alias.members, alias.returnedonly, alias.structextends, alias=alias)
1529 def from_xml(struct):
1530 # Unions and structs are the same parsing wise, but we need to
1531 # know which one we are dealing with later on for code generation.
1532 union = True if struct.attrib["category"] == "union" else False
1534 name = struct.attrib.get("name")
1536 # 'Output' structures for which data is filled in by the API are
1537 # marked as 'returnedonly'.
1538 returnedonly = True if struct.attrib.get("returnedonly") else False
1540 structextends = struct.attrib.get("structextends")
1541 structextends = structextends.split(",") if structextends else []
1544 for member in struct.findall("member"):
1545 vk_member = VkMember.from_xml(member)
1546 members.append(vk_member)
1548 return VkStruct(name, members, returnedonly, structextends, union=union)
1551 def decouple_structs(structs):
1552 """ Helper function which decouples a list of structs.
1553 Structures often depend on other structures. To make the C compiler
1554 happy we need to define 'substructures' first. This function analyzes
1555 the list of structures and reorders them in such a way that they are
1559 tmp_structs = list(structs) # Don't modify the original structures.
1560 decoupled_structs = []
1562 while (len(tmp_structs) > 0):
1563 for struct in tmp_structs:
1566 if not struct.required:
1567 tmp_structs.remove(struct)
1571 if not (m.is_struct() or m.is_union()):
1574 # VkBaseInstructure and VkBaseOutStructure reference themselves.
1575 if m.type == struct.name:
1579 # Check if a struct we depend on has already been defined.
1580 for s in decoupled_structs:
1581 if s.name == m.type:
1586 # Check if the struct we depend on is even in the list of structs.
1587 # If found now, it means we haven't met all dependencies before we
1588 # can operate on the current struct.
1589 # When generating 'host' structs we may not be able to find a struct
1590 # as the list would only contain the structs requiring conversion.
1591 for s in tmp_structs:
1592 if s.name == m.type:
1596 if dependends == False:
1597 decoupled_structs.append(struct)
1598 tmp_structs.remove(struct)
1600 return decoupled_structs
1602 def definition(self, align=False, conv=False, postfix=None):
1603 """ Convert structure to textual definition.
1606 align (bool, optional): enable alignment to 64-bit for win32 struct compatibility.
1607 conv (bool, optional): enable struct conversion if the struct needs it.
1608 postfix (str, optional): text to append to end of struct name, useful for struct renaming.
1612 text = "typedef union {0}".format(self.name)
1614 text = "typedef struct {0}".format(self.name)
1616 if postfix is not None:
1622 if align and m.needs_alignment():
1623 text += " {0};\n".format(m.definition(align=align))
1624 elif conv and m.needs_conversion():
1625 text += " {0};\n".format(m.definition(conv=conv))
1627 text += " {0};\n".format(m.definition())
1629 if postfix is not None:
1630 text += "}} {0}{1};\n\n".format(self.name, postfix)
1632 text += "}} {0};\n\n".format(self.name)
1636 return bool(self.alias)
1638 def needs_alignment(self):
1639 """ Check if structure needs alignment for 64-bit data.
1640 Various structures need alignment on 64-bit variables due
1641 to compiler differences on 32-bit between Win32 and Linux.
1644 for m in self.members:
1645 if m.needs_alignment():
1649 def needs_conversion(self):
1650 """ Returns if struct members needs conversion between win32 and host.
1651 Structures need conversion if they contain members requiring alignment
1652 or if they include other structures which need alignment.
1655 if self.needs_alignment():
1658 for m in self.members:
1659 if m.needs_conversion():
1663 def needs_free(self):
1664 """ Check if any struct member needs some memory freeing."""
1666 for m in self.members:
1674 def needs_struct_extensions_conversion(self):
1675 """ Checks if structure extensions in pNext chain need conversion. """
1678 for e in self.struct_extensions:
1679 if e.required and e.needs_conversion():
1680 LOGGER.error("Unhandled pNext chain conversion for {0}".format(e.name))
1685 def set_type_info(self, types):
1686 """ Helper function to set type information from the type registry.
1687 This is needed, because not all type data is available at time of
1690 for m in self.members:
1691 type_info = types[m.type]
1692 m.set_type_info(type_info)
1695 class ConversionFunction(object):
1696 def __init__(self, array, dyn_array, direction, struct):
1698 self.direction = direction
1699 self.dyn_array = dyn_array
1700 self.struct = struct
1701 self.type = struct.name
1705 def __eq__(self, other):
1706 if self.name != other.name:
1711 def _generate_array_conversion_func(self):
1712 """ Helper function for generating a conversion function for array structs. """
1714 if self.direction == Direction.OUTPUT:
1715 params = ["const {0}_host *in".format(self.type), "uint32_t count"]
1716 return_type = self.type
1718 params = ["const {0} *in".format(self.type), "uint32_t count"]
1719 return_type = "{0}_host".format(self.type)
1721 # Generate function prototype.
1722 body = "static inline {0} *{1}(".format(return_type, self.name)
1723 body += ", ".join(p for p in params)
1726 body += " {0} *out;\n".format(return_type)
1727 body += " unsigned int i;\n\n"
1728 body += " if (!in) return NULL;\n\n"
1730 body += " out = heap_alloc(count * sizeof(*out));\n"
1732 body += " for (i = 0; i < count; i++)\n"
1735 for m in self.struct:
1736 # TODO: support copying of pNext extension structures!
1737 # Luckily though no extension struct at this point needs conversion.
1738 body += " " + m.copy("in[i].", "out[i].", self.direction)
1741 body += " return out;\n"
1745 def _generate_conversion_func(self):
1746 """ Helper function for generating a conversion function for non-array structs. """
1748 if self.direction == Direction.OUTPUT:
1749 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type)]
1751 params = ["const {0} *in".format(self.type), "{0}_host *out".format(self.type)]
1753 body = "static inline void {0}(".format(self.name)
1755 # Generate parameter list
1756 body += ", ".join(p for p in params)
1759 body += " if (!in) return;\n\n"
1761 if self.direction == Direction.INPUT and "pNext" in self.struct and self.struct.returnedonly:
1762 # We are dealing with an input_output parameter. For these we only need to copy
1763 # pNext and sType as the other fields are filled in by the host. We do potentially
1764 # have to iterate over pNext and perform conversions based on switch(sType)!
1765 # Luckily though no extension structs at this point need conversion.
1766 # TODO: support copying of pNext extension structures!
1767 body += " out->pNext = in->pNext;\n"
1768 body += " out->sType = in->sType;\n"
1770 for m in self.struct:
1771 # TODO: support copying of pNext extension structures!
1772 body += " " + m.copy("in->", "out->", self.direction)
1777 def _generate_static_array_conversion_func(self):
1778 """ Helper function for generating a conversion function for array structs. """
1780 if self.direction == Direction.OUTPUT:
1781 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
1783 params = ["const {0} *in".format(self.type), "{0} *out_host".format(self.type), "uint32_t count"]
1785 # Generate function prototype.
1786 body = "static inline void {0}(".format(self.name)
1787 body += ", ".join(p for p in params)
1789 body += " unsigned int i;\n\n"
1790 body += " if (!in) return;\n\n"
1791 body += " for (i = 0; i < count; i++)\n"
1794 for m in self.struct:
1795 # TODO: support copying of pNext extension structures!
1796 body += " " + m.copy("in[i].", "out[i].", self.direction)
1802 def _set_name(self):
1803 if self.direction == Direction.INPUT:
1805 name = "convert_{0}_static_array_win_to_host".format(self.type)
1806 elif self.dyn_array:
1807 name = "convert_{0}_array_win_to_host".format(self.type)
1809 name = "convert_{0}_win_to_host".format(self.type)
1810 else: # Direction.OUTPUT
1812 name = "convert_{0}_static_array_host_to_win".format(self.type)
1813 elif self.dyn_array:
1814 name = "convert_{0}_array_host_to_win".format(self.type)
1816 name = "convert_{0}_host_to_win".format(self.type)
1820 def definition(self):
1822 return self._generate_static_array_conversion_func()
1823 elif self.dyn_array:
1824 return self._generate_array_conversion_func()
1826 return self._generate_conversion_func()
1829 class FreeFunction(object):
1830 def __init__(self, dyn_array, struct):
1831 self.dyn_array = dyn_array
1832 self.struct = struct
1833 self.type = struct.name
1836 self.name = "free_{0}_array".format(self.type)
1838 self.name = "free_{0}".format(self.type)
1840 def __eq__(self, other):
1841 if self.name == other.name:
1846 def _generate_array_free_func(self):
1847 """ Helper function for cleaning up temporary buffers required for array conversions. """
1849 # Generate function prototype.
1850 body = "static inline void {0}({1}_host *in, uint32_t count)\n{{\n".format(self.name, self.type)
1852 # E.g. VkGraphicsPipelineCreateInfo_host needs freeing for pStages.
1853 if self.struct.needs_free():
1854 body += " unsigned int i;\n\n"
1855 body += " if (!in) return;\n\n"
1856 body += " for (i = 0; i < count; i++)\n"
1859 for m in self.struct:
1860 if m.needs_conversion() and m.is_dynamic_array():
1862 # Add a cast to ignore const on conversion structs we allocated ourselves.
1863 body += " free_{0}_array(({0}_host *)in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
1865 body += " free_{0}_array(in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
1866 elif m.needs_conversion():
1867 LOGGER.error("Unhandled conversion for {0}".format(m.name))
1870 body += " if (!in) return;\n\n"
1872 body += " heap_free(in);\n"
1877 def _generate_free_func(self):
1878 # E.g. VkCommandBufferBeginInfo.pInheritanceInfo needs freeing.
1879 if not self.struct.needs_free():
1882 # Generate function prototype.
1883 body = "static inline void {0}({1}_host *in)\n{{\n".format(self.name, self.type)
1885 for m in self.struct:
1886 if m.needs_conversion() and m.is_dynamic_array():
1887 count = m.dyn_array_len if isinstance(m.dyn_array_len, int) else "in->{0}".format(m.dyn_array_len)
1889 # Add a cast to ignore const on conversion structs we allocated ourselves.
1890 body += " free_{0}_array(({0}_host *)in->{1}, {2});\n".format(m.type, m.name, count)
1892 body += " free_{0}_array(in->{1}, {2});\n".format(m.type, m.name, count)
1897 def definition(self):
1899 return self._generate_array_free_func()
1901 # Some structures need freeing too if they contain dynamic arrays.
1902 # E.g. VkCommandBufferBeginInfo
1903 return self._generate_free_func()
1906 class VkGenerator(object):
1907 def __init__(self, registry):
1908 self.registry = registry
1910 # Build a list conversion functions for struct conversion.
1911 self.conversions = []
1912 self.host_structs = []
1913 for func in self.registry.funcs.values():
1914 if not func.is_required():
1917 if not func.needs_conversion():
1920 conversions = func.get_conversions()
1921 for conv in conversions:
1922 # Pull in any conversions for vulkan_thunks.c.
1923 if func.needs_thunk():
1924 # Append if we don't already have this conversion.
1925 if not any(c == conv for c in self.conversions):
1926 self.conversions.append(conv)
1928 # Structs can be used in different ways by different conversions
1929 # e.g. array vs non-array. Just make sure we pull in each struct once.
1930 if not any(s.name == conv.struct.name for s in self.host_structs):
1931 self.host_structs.append(conv.struct)
1933 def _generate_copyright(self, f, spec_file=False):
1934 f.write("# " if spec_file else "/* ")
1935 f.write("Automatically generated from Vulkan vk.xml; DO NOT EDIT!\n")
1936 lines = ["", "This file is generated from Vulkan vk.xml file covered",
1937 "by the following copyright and permission notice:"]
1938 lines.extend([l.rstrip(" ") for l in self.registry.copyright.splitlines()])
1940 f.write("{0}{1}".format("# " if spec_file else " * ", line).rstrip(" ") + "\n")
1941 f.write("\n" if spec_file else " */\n\n")
1943 def generate_thunks_c(self, f, prefix):
1944 self._generate_copyright(f)
1945 f.write("#include \"config.h\"\n")
1946 f.write("#include \"wine/port.h\"\n\n")
1948 f.write("#include \"vulkan_private.h\"\n\n")
1950 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
1952 # Generate any conversion helper functions.
1953 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
1954 for conv in self.conversions:
1955 f.write(conv.definition())
1956 f.write("#endif /* USE_STRUCT_CONVERSION */\n\n")
1958 # Create thunks for instance and device functions.
1959 # Global functions don't go through the thunks.
1960 for vk_func in self.registry.funcs.values():
1961 if not vk_func.is_required():
1964 if vk_func.is_global_func():
1967 if not vk_func.needs_thunk():
1970 # Exports symbols for Core functions.
1971 if not vk_func.is_core_func():
1973 f.write(vk_func.thunk(prefix=prefix, call_conv="WINAPI"))
1975 f.write("static const struct vulkan_func vk_device_dispatch_table[] =\n{\n")
1976 for vk_func in self.registry.device_funcs:
1977 if not vk_func.is_required():
1980 f.write(" {{\"{0}\", &{1}{0}}},\n".format(vk_func.name, prefix))
1983 f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
1984 for vk_func in self.registry.instance_funcs:
1985 if not vk_func.is_required():
1988 f.write(" {{\"{0}\", &{1}{0}}},\n".format(vk_func.name, prefix))
1991 f.write("void *wine_vk_get_device_proc_addr(const char *name)\n")
1993 f.write(" unsigned int i;\n")
1994 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_dispatch_table); i++)\n")
1996 f.write(" if (strcmp(vk_device_dispatch_table[i].name, name) == 0)\n")
1998 f.write(" TRACE(\"Found name=%s in device table\\n\", debugstr_a(name));\n")
1999 f.write(" return vk_device_dispatch_table[i].func;\n")
2002 f.write(" return NULL;\n")
2005 f.write("void *wine_vk_get_instance_proc_addr(const char *name)\n")
2007 f.write(" unsigned int i;\n")
2008 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_dispatch_table); i++)\n")
2010 f.write(" if (strcmp(vk_instance_dispatch_table[i].name, name) == 0)\n")
2012 f.write(" TRACE(\"Found name=%s in instance table\\n\", debugstr_a(name));\n")
2013 f.write(" return vk_instance_dispatch_table[i].func;\n")
2016 f.write(" return NULL;\n")
2019 # Create array of device extensions.
2020 f.write("static const char * const vk_device_extensions[] =\n{\n")
2021 for ext in self.registry.extensions:
2022 if ext["type"] != "device":
2025 f.write(" \"{0}\",\n".format(ext["name"]))
2028 # Create array of instance extensions.
2029 f.write("static const char * const vk_instance_extensions[] =\n{\n")
2030 for ext in self.registry.extensions:
2031 if ext["type"] != "instance":
2034 f.write(" \"{0}\",\n".format(ext["name"]))
2037 f.write("BOOL wine_vk_device_extension_supported(const char *name)\n")
2039 f.write(" unsigned int i;\n")
2040 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_extensions); i++)\n")
2042 f.write(" if (strcmp(vk_device_extensions[i], name) == 0)\n")
2043 f.write(" return TRUE;\n")
2045 f.write(" return FALSE;\n")
2048 f.write("BOOL wine_vk_instance_extension_supported(const char *name)\n")
2050 f.write(" unsigned int i;\n")
2051 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_extensions); i++)\n")
2053 f.write(" if (strcmp(vk_instance_extensions[i], name) == 0)\n")
2054 f.write(" return TRUE;\n")
2056 f.write(" return FALSE;\n")
2059 def generate_thunks_h(self, f, prefix):
2060 self._generate_copyright(f)
2062 f.write("#ifndef __WINE_VULKAN_THUNKS_H\n")
2063 f.write("#define __WINE_VULKAN_THUNKS_H\n\n")
2065 f.write("#define WINE_VK_VERSION VK_API_VERSION_{0}_{1}\n\n".format(WINE_VK_VERSION[0], WINE_VK_VERSION[1]))
2067 # Generate prototypes for device and instance functions requiring a custom implementation.
2068 f.write("/* Functions for which we have custom implementations outside of the thunks. */\n")
2069 for vk_func in self.registry.funcs.values():
2070 if not vk_func.is_required() or vk_func.is_global_func() or vk_func.needs_thunk():
2073 if vk_func.is_core_func():
2074 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix="wine_")))
2076 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix="wine_", postfix="DECLSPEC_HIDDEN")))
2079 for struct in self.host_structs:
2080 f.write(struct.definition(align=False, conv=True, postfix="_host"))
2083 f.write("/* For use by vkDevice and children */\n")
2084 f.write("struct vulkan_device_funcs\n{\n")
2085 for vk_func in self.registry.device_funcs:
2086 if not vk_func.is_required():
2089 if not vk_func.needs_dispatch():
2090 LOGGER.debug("skipping {0} in vulkan_device_funcs".format(vk_func.name))
2093 if vk_func.needs_conversion():
2094 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
2095 f.write(" {0};\n".format(vk_func.pfn(conv=True)))
2097 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2100 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2103 f.write("/* For use by vkInstance and children */\n")
2104 f.write("struct vulkan_instance_funcs\n{\n")
2105 for vk_func in self.registry.instance_funcs:
2106 if not vk_func.is_required():
2109 if not vk_func.needs_dispatch():
2110 LOGGER.debug("skipping {0} in vulkan_instance_funcs".format(vk_func.name))
2113 if vk_func.needs_conversion():
2114 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
2115 f.write(" {0};\n".format(vk_func.pfn(conv=True)))
2117 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2120 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2123 f.write("#define ALL_VK_DEVICE_FUNCS() \\\n")
2125 for vk_func in self.registry.device_funcs:
2126 if not vk_func.is_required():
2129 if not vk_func.needs_dispatch():
2130 LOGGER.debug("skipping {0} in ALL_VK_DEVICE_FUNCS".format(vk_func.name))
2134 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2137 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2140 f.write("#define ALL_VK_INSTANCE_FUNCS() \\\n")
2142 for vk_func in self.registry.instance_funcs:
2143 if not vk_func.is_required():
2146 if not vk_func.needs_dispatch():
2147 LOGGER.debug("skipping {0} in ALL_VK_INSTANCE_FUNCS".format(vk_func.name))
2151 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2154 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2157 f.write("#endif /* __WINE_VULKAN_THUNKS_H */\n")
2159 def generate_vulkan_h(self, f):
2160 self._generate_copyright(f)
2161 f.write("#ifndef __WINE_VULKAN_H\n")
2162 f.write("#define __WINE_VULKAN_H\n\n")
2164 f.write("#include <windef.h>\n")
2165 f.write("#include <stdint.h>\n\n")
2167 f.write("/* Define WINE_VK_HOST to get 'host' headers. */\n")
2168 f.write("#ifdef WINE_VK_HOST\n")
2169 f.write("#define VKAPI_CALL\n")
2170 f.write('#define WINE_VK_ALIGN(x)\n')
2171 f.write("#endif\n\n")
2173 f.write("#ifndef VKAPI_CALL\n")
2174 f.write("#define VKAPI_CALL __stdcall\n")
2175 f.write("#endif\n\n")
2177 f.write("#ifndef VKAPI_PTR\n")
2178 f.write("#define VKAPI_PTR VKAPI_CALL\n")
2179 f.write("#endif\n\n")
2181 f.write("#ifndef WINE_VK_ALIGN\n")
2182 f.write("#define WINE_VK_ALIGN DECLSPEC_ALIGN\n")
2183 f.write("#endif\n\n")
2185 # The overall strategy is to define independent constants and datatypes,
2186 # prior to complex structures and function calls to avoid forward declarations.
2187 for const in self.registry.consts:
2188 # For now just generate things we may not need. The amount of parsing needed
2189 # to get some of the info is tricky as you need to figure out which structure
2190 # references a certain constant.
2191 f.write(const.definition())
2194 for define in self.registry.defines:
2195 f.write(define.definition())
2197 for handle in self.registry.handles:
2198 # For backward compatibility also create definitions for aliases.
2199 # These types normally don't get pulled in as we use the new types
2200 # even in legacy functions if they are aliases.
2201 if handle.is_required() or handle.is_alias():
2202 f.write(handle.definition())
2205 for base_type in self.registry.base_types:
2206 f.write(base_type.definition())
2209 for bitmask in self.registry.bitmasks:
2210 f.write(bitmask.definition())
2213 # Define enums, this includes values for some of the bitmask types as well.
2214 for enum in self.registry.enums.values():
2216 f.write(enum.definition())
2218 for fp in self.registry.funcpointers:
2220 f.write(fp.definition())
2223 # This generates both structures and unions. Since structures
2224 # may depend on other structures/unions, we need a list of
2225 # decoupled structs.
2226 # Note: unions are stored in structs for dependency reasons,
2227 # see comment in parsing section.
2228 structs = VkStruct.decouple_structs(self.registry.structs)
2229 for struct in structs:
2230 LOGGER.debug("Generating struct: {0}".format(struct.name))
2231 f.write(struct.definition(align=True))
2233 for func in self.registry.funcs.values():
2234 if not func.is_required():
2235 LOGGER.debug("Skipping PFN definition for: {0}".format(func.name))
2238 f.write("typedef {0};\n".format(func.pfn(prefix="PFN", call_conv="VKAPI_PTR")))
2241 f.write("#ifndef VK_NO_PROTOTYPES\n")
2242 for func in self.registry.funcs.values():
2243 if not func.is_required():
2244 LOGGER.debug("Skipping API definition for: {0}".format(func.name))
2247 LOGGER.debug("Generating API definition for: {0}".format(func.name))
2248 f.write("{0};\n".format(func.prototype(call_conv="VKAPI_CALL")))
2249 f.write("#endif /* VK_NO_PROTOTYPES */\n\n")
2251 f.write("#endif /* __WINE_VULKAN_H */\n")
2253 def generate_vulkan_driver_h(self, f):
2254 self._generate_copyright(f)
2255 f.write("#ifndef __WINE_VULKAN_DRIVER_H\n")
2256 f.write("#define __WINE_VULKAN_DRIVER_H\n\n")
2258 f.write("/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */\n")
2259 f.write("#define WINE_VULKAN_DRIVER_VERSION {0}\n\n".format(DRIVER_VERSION))
2261 f.write("struct vulkan_funcs\n{\n")
2262 f.write(" /* Vulkan global functions. These are the only calls at this point a graphics driver\n")
2263 f.write(" * needs to provide. Other function calls will be provided indirectly by dispatch\n")
2264 f.write(" * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice.\n")
2267 for vk_func in self.registry.funcs.values():
2268 if not vk_func.is_driver_func():
2272 # Avoid PFN_vkVoidFunction in driver interface as Vulkan likes to put calling convention
2273 # stuff in there. For simplicity substitute with "void *".
2274 pfn = pfn.replace("PFN_vkVoidFunction", "void *")
2275 f.write(" {0};\n".format(pfn))
2278 f.write("extern const struct vulkan_funcs * CDECL __wine_get_vulkan_driver(HDC hdc, UINT version);\n\n")
2280 f.write("static inline void *get_vulkan_driver_device_proc_addr(\n")
2281 f.write(" const struct vulkan_funcs *vulkan_funcs, const char *name)\n{\n")
2282 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
2283 f.write(" name += 2;\n\n")
2284 for vk_func in self.registry.funcs.values():
2285 if vk_func.is_driver_func() and vk_func.is_device_func():
2286 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
2287 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
2289 f.write(" return NULL;\n}\n\n")
2291 f.write("static inline void *get_vulkan_driver_instance_proc_addr(\n")
2292 f.write(" const struct vulkan_funcs *vulkan_funcs, VkInstance instance, const char *name)\n{\n")
2293 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
2294 f.write(" name += 2;\n\n")
2295 for vk_func in self.registry.funcs.values():
2296 if vk_func.is_driver_func() and vk_func.is_global_func() and vk_func.name != "vkGetInstanceProcAddr":
2297 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
2298 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
2300 f.write(" if (!instance) return NULL;\n\n")
2301 for vk_func in self.registry.funcs.values():
2302 if vk_func.is_driver_func() and vk_func.is_instance_func():
2303 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
2304 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
2306 f.write(" name -= 2;\n\n")
2307 f.write(" return get_vulkan_driver_device_proc_addr(vulkan_funcs, name);\n}\n\n")
2309 f.write("#endif /* __WINE_VULKAN_DRIVER_H */\n")
2311 def generate_vulkan_spec(self, f):
2312 self._generate_copyright(f, spec_file=True)
2313 f.write("@ stdcall -private vk_icdGetInstanceProcAddr(ptr str) wine_vk_icdGetInstanceProcAddr\n")
2314 f.write("@ stdcall -private vk_icdNegotiateLoaderICDInterfaceVersion(ptr) wine_vk_icdNegotiateLoaderICDInterfaceVersion\n")
2315 f.write("@ cdecl -norelay native_vkGetInstanceProcAddrWINE(ptr str)\n")
2317 # Export symbols for all Vulkan Core functions.
2318 for func in self.registry.funcs.values():
2319 if not func.is_core_func():
2322 # We support all Core functions except for VK_KHR_display* APIs.
2323 # Create stubs for unsupported Core functions.
2324 if func.is_required():
2325 f.write(func.spec(prefix="wine_"))
2327 f.write("@ stub {0}\n".format(func.name))
2329 def generate_vulkan_loader_spec(self, f):
2330 self._generate_copyright(f, spec_file=True)
2332 # Export symbols for all Vulkan Core functions.
2333 for func in self.registry.funcs.values():
2334 if not func.is_core_func():
2337 # We support all Core functions except for VK_KHR_display* APIs.
2338 # Create stubs for unsupported Core functions.
2339 if func.is_required():
2340 f.write(func.spec(symbol="winevulkan.wine_" + func.name))
2342 f.write("@ stub {0}\n".format(func.name))
2345 class VkRegistry(object):
2346 def __init__(self, reg_filename):
2347 # Used for storage of type information.
2348 self.base_types = None
2349 self.bitmasks = None
2353 self.funcpointers = None
2357 # We aggregate all types in here for cross-referencing.
2361 self.version_regex = re.compile(
2370 # Overall strategy for parsing the registry is to first
2371 # parse all type / function definitions. Then parse
2372 # features and extensions to decide which types / functions
2373 # to actually 'pull in' for code generation. For each type or
2374 # function call we want we set a member 'required' to True.
2375 tree = ET.parse(reg_filename)
2376 root = tree.getroot()
2377 self._parse_enums(root)
2378 self._parse_types(root)
2379 self._parse_commands(root)
2381 # Pull in any required types and functions.
2382 self._parse_features(root)
2383 self._parse_extensions(root)
2385 self.copyright = root.find('./comment').text
2387 def _is_feature_supported(self, feature):
2388 version = self.version_regex.match(feature)
2392 version = tuple(map(int, version.group('major', 'minor')))
2393 return version <= WINE_VK_VERSION
2395 def _mark_command_required(self, command):
2396 """ Helper function to mark a certain command and the datatypes it needs as required."""
2397 def mark_bitmask_dependencies(bitmask, types):
2398 if bitmask.requires is not None:
2399 types[bitmask.requires]["data"].required = True
2401 def mark_funcpointer_dependencies(fp, types):
2402 for m in fp.members:
2403 type_info = types[m.type]
2405 # Complex types have a matching definition e.g. VkStruct.
2406 # Not needed for base types such as uint32_t.
2407 if "data" in type_info:
2408 types[m.type]["data"].required = True
2410 def mark_struct_dependencies(struct, types):
2412 type_info = types[m.type]
2414 # Complex types have a matching definition e.g. VkStruct.
2415 # Not needed for base types such as uint32_t.
2416 if "data" in type_info:
2417 types[m.type]["data"].required = True
2419 if type_info["category"] == "struct":
2421 mark_struct_dependencies(type_info["data"], types)
2422 elif type_info["category"] == "funcpointer":
2423 mark_funcpointer_dependencies(type_info["data"], types)
2424 elif type_info["category"] == "bitmask":
2425 mark_bitmask_dependencies(type_info["data"], types)
2427 func = self.funcs[command]
2428 func.required = True
2430 # Pull in return type
2431 if func.type != "void":
2432 self.types[func.type]["data"].required = True
2434 # Analyze parameter dependencies and pull in any type needed.
2435 for p in func.params:
2436 type_info = self.types[p.type]
2438 # Check if we are dealing with a complex type e.g. VkEnum, VkStruct and others.
2439 if "data" not in type_info:
2442 # Mark the complex type as required.
2443 type_info["data"].required = True
2444 if type_info["category"] == "struct":
2445 struct = type_info["data"]
2446 mark_struct_dependencies(struct, self.types)
2449 func.alias.required = True
2451 def _parse_commands(self, root):
2452 """ Parse command section containing the Vulkan function calls. """
2454 commands = root.findall("./commands/")
2456 # As of Vulkan 1.1, various extensions got promoted to Core.
2457 # The old commands (e.g. KHR) are available for backwards compatibility
2458 # and are marked in vk.xml as 'alias' to the non-extension type.
2459 # The registry likes to avoid data duplication, so parameters and other
2460 # metadata need to be looked up from the Core command.
2461 # We parse the alias commands in a second pass.
2463 for command in commands:
2464 alias_name = command.attrib.get("alias")
2466 alias_commands.append(command)
2469 func = VkFunction.from_xml(command, self.types)
2470 funcs[func.name] = func
2472 for command in alias_commands:
2473 alias_name = command.attrib.get("alias")
2474 alias = funcs[alias_name]
2475 func = VkFunction.from_alias(command, alias)
2476 funcs[func.name] = func
2478 # To make life easy for the code generation, separate all function
2479 # calls out in the 3 types of Vulkan functions: device, global and instance.
2483 for func in funcs.values():
2484 if func.is_device_func():
2485 device_funcs.append(func)
2486 elif func.is_global_func():
2487 global_funcs.append(func)
2489 instance_funcs.append(func)
2491 # Sort function lists by name and store them.
2492 self.device_funcs = sorted(device_funcs, key=lambda func: func.name)
2493 self.global_funcs = sorted(global_funcs, key=lambda func: func.name)
2494 self.instance_funcs = sorted(instance_funcs, key=lambda func: func.name)
2496 # The funcs dictionary is used as a convenient way to lookup function
2497 # calls when needed e.g. to adjust member variables.
2498 self.funcs = OrderedDict(sorted(funcs.items()))
2500 def _parse_enums(self, root):
2501 """ Parse enums section or better described as constants section. """
2504 for enum in root.findall("./enums"):
2505 name = enum.attrib.get("name")
2506 _type = enum.attrib.get("type")
2508 if _type in ("enum", "bitmask"):
2509 enums[name] = VkEnum.from_xml(enum)
2511 # If no type is set, we are dealing with API constants.
2512 for value in enum.findall("enum"):
2513 # If enum is an alias, set the value to the alias name.
2514 # E.g. VK_LUID_SIZE_KHR is an alias to VK_LUID_SIZE.
2515 alias = value.attrib.get("alias")
2517 self.consts.append(VkConstant(value.attrib.get("name"), alias))
2519 self.consts.append(VkConstant(value.attrib.get("name"), value.attrib.get("value")))
2521 self.enums = OrderedDict(sorted(enums.items()))
2523 def _process_require_enum(self, enum_elem, ext=None):
2524 if "extends" in enum_elem.keys():
2525 enum = self.types[enum_elem.attrib["extends"]]["data"]
2527 if "bitpos" in enum_elem.keys():
2528 # We need to add an extra value to an existing enum type.
2529 # E.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG to VkFormatFeatureFlagBits.
2530 enum.add(VkEnumValue(enum_elem.attrib["name"], 1 << int(enum_elem.attrib["bitpos"]), hex=True))
2532 elif "offset" in enum_elem.keys():
2533 # Extensions promoted to Core, have the extension number as part
2534 # of the enum value. Else retrieve from the extension tag.
2535 if enum_elem.attrib.get("extnumber"):
2536 ext_number = int(enum_elem.attrib.get("extnumber"))
2538 ext_number = int(ext.attrib["number"])
2539 offset = int(enum_elem.attrib["offset"])
2540 value = EXT_BASE + (ext_number - 1) * EXT_BLOCK_SIZE + offset
2542 # Deal with negative values.
2543 direction = enum_elem.attrib.get("dir")
2544 if direction is not None:
2547 enum.add(VkEnumValue(enum_elem.attrib["name"], value))
2549 elif "value" in enum_elem.keys():
2550 enum.add(VkEnumValue(enum_elem.attrib["name"], int(enum_elem.attrib["value"])))
2552 elif "value" in enum_elem.keys():
2553 self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["value"]))
2555 def _parse_extensions(self, root):
2556 """ Parse extensions section and pull in any types and commands for this extensioin. """
2558 exts = root.findall("./extensions/extension")
2560 ext_name = ext.attrib["name"]
2562 # Set extension name on any functions calls part of this extension as we
2563 # were not aware of the name during initial parsing.
2564 commands = ext.findall("require/command")
2565 for command in commands:
2566 cmd_name = command.attrib["name"]
2567 self.funcs[cmd_name].extensions.append(ext_name)
2569 # Some extensions are not ready or have numbers reserved as a place holder.
2570 if ext.attrib["supported"] == "disabled":
2571 LOGGER.debug("Skipping disabled extension: {0}".format(ext_name))
2574 # Disable highly experimental extensions as the APIs are unstable and can
2575 # change between minor Vulkan revisions until API is final and becomes KHR
2577 if "KHX" in ext_name or "NVX" in ext_name:
2578 LOGGER.debug("Skipping experimental extension: {0}".format(ext_name))
2581 # Instance extensions often require a custom implementation, so filter.
2582 ext_type = ext.attrib["type"]
2583 if ext_type == "instance" and not ext_name in SUPPORTED_INSTANCE_EXTENSIONS:
2584 LOGGER.debug("Skipping instance extension: {0}".format(ext_name))
2587 # We disable some extensions as either we haven't implemented
2588 # support yet or because they are for platforms other than win32.
2589 if ext_name in BLACKLISTED_EXTENSIONS:
2590 LOGGER.debug("Skipping blacklisted extension: {0}".format(ext_name))
2592 elif "requires" in ext.attrib:
2593 # Check if this extension builds on top of another blacklisted
2595 requires = ext.attrib["requires"].split(",")
2596 if len(set(requires).intersection(BLACKLISTED_EXTENSIONS)) > 0:
2599 LOGGER.debug("Loading extension: {0}".format(ext_name))
2601 # Extensions can define one or more require sections each requiring
2602 # different features (e.g. Vulkan 1.1). Parse each require section
2603 # separately, so we can skip sections we don't want.
2604 for require in ext.findall("require"):
2605 # Extensions can add enum values to Core / extension enums, so add these.
2606 for enum_elem in require.findall("enum"):
2607 self._process_require_enum(enum_elem, ext)
2609 for t in require.findall("type"):
2610 type_info = self.types[t.attrib["name"]]["data"]
2611 if type_info.is_alias():
2612 type_info = type_info.alias
2613 type_info.required = True
2615 feature = require.attrib.get("feature")
2616 if feature and not self._is_feature_supported(feature):
2619 # Pull in any commands we need. We infer types to pull in from the command
2621 for command in require.findall("command"):
2622 cmd_name = command.attrib["name"]
2623 self._mark_command_required(cmd_name)
2625 # Store a list with extensions.
2626 ext_info = {"name" : ext_name, "type" : ext_type}
2627 extensions.append(ext_info)
2629 # Sort in alphabetical order.
2630 self.extensions = sorted(extensions, key=lambda ext: ext["name"])
2632 def _parse_features(self, root):
2633 """ Parse the feature section, which describes Core commands and types needed. """
2635 for feature in root.findall("./feature"):
2636 feature_name = feature.attrib["name"]
2637 for require in feature.findall("require"):
2638 LOGGER.info("Including features for {0}".format(require.attrib.get("comment")))
2640 if tag.tag == "comment":
2642 elif tag.tag == "command":
2643 if not self._is_feature_supported(feature_name):
2645 name = tag.attrib["name"]
2646 self._mark_command_required(name)
2647 elif tag.tag == "enum":
2648 self._process_require_enum(tag)
2649 elif tag.tag == "type":
2650 name = tag.attrib["name"]
2652 # Skip pull in for vk_platform.h for now.
2653 if name == "vk_platform":
2656 type_info = self.types[name]
2657 type_info["data"].required = True
2659 def _parse_types(self, root):
2660 """ Parse types section, which contains all data types e.g. structs, typedefs etcetera. """
2661 types = root.findall("./types/type")
2673 type_info["category"] = t.attrib.get("category", None)
2675 # We parse aliases in a second pass when we know more.
2676 alias = t.attrib.get("alias")
2678 LOGGER.debug("Alias found: {0}".format(alias))
2679 alias_types.append(t)
2682 if type_info["category"] in ["include"]:
2685 if type_info["category"] == "basetype":
2686 name = t.find("name").text
2687 _type = t.find("type").text
2688 basetype = VkBaseType(name, _type)
2689 base_types.append(basetype)
2690 type_info["data"] = basetype
2692 if type_info["category"] == "bitmask":
2693 name = t.find("name").text
2694 _type = t.find("type").text
2696 # Most bitmasks have a requires attribute used to pull in
2697 # required '*FlagBits" enum.
2698 requires = t.attrib.get("requires")
2699 bitmask = VkBaseType(name, _type, requires=requires)
2700 bitmasks.append(bitmask)
2701 type_info["data"] = bitmask
2703 if type_info["category"] == "define":
2704 define = VkDefine.from_xml(t)
2705 defines.append(define)
2706 type_info["data"] = define
2708 if type_info["category"] == "enum":
2709 name = t.attrib.get("name")
2710 # The type section only contains enum names, not the actual definition.
2711 # Since we already parsed the enum before, just link it in.
2713 type_info["data"] = self.enums[name]
2714 except KeyError as e:
2715 # Not all enums seem to be defined yet, typically that's for
2716 # ones ending in 'FlagBits' where future extensions may add
2718 type_info["data"] = None
2720 if type_info["category"] == "funcpointer":
2721 funcpointer = VkFunctionPointer.from_xml(t)
2722 funcpointers.append(funcpointer)
2723 type_info["data"] = funcpointer
2725 if type_info["category"] == "handle":
2726 handle = VkHandle.from_xml(t)
2727 handles.append(handle)
2728 type_info["data"] = handle
2730 if type_info["category"] in ["struct", "union"]:
2731 # We store unions among structs as some structs depend
2732 # on unions. The types are very similar in parsing and
2733 # generation anyway. The official Vulkan scripts use
2734 # a similar kind of hack.
2735 struct = VkStruct.from_xml(t)
2736 structs.append(struct)
2737 type_info["data"] = struct
2739 # Name is in general within a name tag else it is an optional
2740 # attribute on the type tag.
2741 name_elem = t.find("name")
2742 if name_elem is not None:
2743 type_info["name"] = name_elem.text
2745 type_info["name"] = t.attrib.get("name", None)
2747 # Store all type data in a shared dictionary, so we can easily
2748 # look up information for a given type. There are no duplicate
2750 self.types[type_info["name"]] = type_info
2752 # Second pass for alias types, so we can retrieve all data from
2753 # the aliased object.
2754 for t in alias_types:
2756 type_info["category"] = t.attrib.get("category")
2757 type_info["name"] = t.attrib.get("name")
2759 alias = t.attrib.get("alias")
2761 if type_info["category"] == "bitmask":
2762 bitmask = VkBaseType(type_info["name"], alias, alias=self.types[alias]["data"])
2763 bitmasks.append(bitmask)
2764 type_info["data"] = bitmask
2766 if type_info["category"] == "enum":
2767 enum = VkEnum.from_alias(t, self.types[alias]["data"])
2768 type_info["data"] = enum
2769 self.enums[enum.name] = enum
2771 if type_info["category"] == "handle":
2772 handle = VkHandle.from_alias(t, self.types[alias]["data"])
2773 handles.append(handle)
2774 type_info["data"] = handle
2776 if type_info["category"] == "struct":
2777 struct = VkStruct.from_alias(t, self.types[alias]["data"])
2778 structs.append(struct)
2779 type_info["data"] = struct
2781 self.types[type_info["name"]] = type_info
2783 # We need detailed type information during code generation
2784 # on structs for alignment reasons. Unfortunately structs
2785 # are parsed among other types, so there is no guarantee
2786 # that any types needed have been parsed already, so set
2788 for struct in structs:
2789 struct.set_type_info(self.types)
2791 for structextend in struct.structextends:
2792 s = self.types[structextend]["data"]
2793 s.struct_extensions.append(struct)
2795 # Guarantee everything is sorted, so code generation doesn't have
2796 # to deal with this.
2797 self.base_types = sorted(base_types, key=lambda base_type: base_type.name)
2798 self.bitmasks = sorted(bitmasks, key=lambda bitmask: bitmask.name)
2799 self.defines = defines
2800 self.enums = OrderedDict(sorted(self.enums.items()))
2801 self.funcpointers = sorted(funcpointers, key=lambda fp: fp.name)
2802 self.handles = sorted(handles, key=lambda handle: handle.name)
2803 self.structs = sorted(structs, key=lambda struct: struct.name)
2805 def set_working_directory():
2806 path = os.path.abspath(__file__)
2807 path = os.path.dirname(path)
2810 def download_vk_xml(filename):
2811 url = "https://raw.github.com/KhronosGroup/Vulkan-Docs/v{0}/xml/vk.xml".format(VK_XML_VERSION)
2812 if not os.path.isfile(filename):
2813 urllib.request.urlretrieve(url, filename)
2816 parser = argparse.ArgumentParser()
2817 parser.add_argument("-v", "--verbose", action="count", default=0, help="increase output verbosity")
2819 args = parser.parse_args()
2820 if args.verbose == 0:
2821 LOGGER.setLevel(logging.WARNING)
2822 elif args.verbose == 1:
2823 LOGGER.setLevel(logging.INFO)
2825 LOGGER.setLevel(logging.DEBUG)
2827 set_working_directory()
2829 vk_xml = "vk-{0}.xml".format(VK_XML_VERSION)
2830 download_vk_xml(vk_xml)
2831 registry = VkRegistry(vk_xml)
2832 generator = VkGenerator(registry)
2834 with open(WINE_VULKAN_H, "w") as f:
2835 generator.generate_vulkan_h(f)
2837 with open(WINE_VULKAN_DRIVER_H, "w") as f:
2838 generator.generate_vulkan_driver_h(f)
2840 with open(WINE_VULKAN_THUNKS_H, "w") as f:
2841 generator.generate_thunks_h(f, "wine_")
2843 with open(WINE_VULKAN_THUNKS_C, "w") as f:
2844 generator.generate_thunks_c(f, "wine_")
2846 with open(WINE_VULKAN_SPEC, "w") as f:
2847 generator.generate_vulkan_spec(f)
2849 with open(WINE_VULKAN_LOADER_SPEC, "w") as f:
2850 generator.generate_vulkan_loader_spec(f)
2852 if __name__ == "__main__":