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.104"
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 BLACKLISTED_EXTENSIONS = [
89 "VK_EXT_validation_features",
90 "VK_EXT_validation_flags",
92 "VK_KHR_get_surface_capabilities2",
93 "VK_KHR_surface_protected_capabilities",
94 "VK_NV_external_memory_capabilities",
97 "VK_AMD_display_native_hdr",
98 "VK_AMD_memory_overallocation_behavior",
99 # Handling of VK_EXT_debug_report requires some consideration. The win32
100 # loader already provides it for us and it is somewhat usable. If we add
101 # plumbing down to the native layer, we will get each message twice as we
102 # use 2 loaders (win32+native), but we may get output from the driver.
103 # In any case callback conversion is required.
104 "VK_EXT_calibrated_timestamps",
105 "VK_EXT_debug_report",
106 "VK_EXT_display_control", # Requires VK_EXT_display_surface_counter
107 "VK_EXT_external_memory_dma_buf", # Linux specific
108 "VK_EXT_full_screen_exclusive",
109 "VK_EXT_hdr_metadata", # Needs WSI work.
110 "VK_EXT_image_drm_format_modifier",
111 "VK_EXT_memory_priority",
112 "VK_EXT_pipeline_creation_feedback",
113 "VK_EXT_ycbcr_image_arrays",
114 "VK_GOOGLE_display_timing",
115 "VK_KHR_display", # Needs WSI work.
116 "VK_KHR_external_fence",
117 "VK_KHR_external_fence_fd",
118 "VK_KHR_external_fence_win32",
119 "VK_KHR_external_memory",
120 "VK_KHR_external_semaphore",
121 "VK_KHR_shader_float16_int8",
122 # Relates to external_semaphore and needs type conversions in bitflags.
123 "VK_KHR_external_semaphore_capabilities",
124 "VK_KHR_shared_presentable_image", # Needs WSI work.
125 "VK_KHR_win32_keyed_mutex",
126 "VK_NV_cooperative_matrix",
127 "VK_NV_dedicated_allocation_image_aliasing",
128 "VK_NV_external_memory_win32",
131 # The Vulkan loader provides entry-points for core functionality and important
132 # extensions. Based on vulkan-1.def this amounts to WSI extensions on 1.0.51.
135 "VK_KHR_display_swapchain",
138 "VK_KHR_win32_surface",
141 # Functions part of our winevulkan graphics driver interface.
142 # DRIVER_VERSION should be bumped on any change to driver interface
143 # in FUNCTION_OVERRIDES
146 # Table of functions for which we have a special implementation.
147 # These are regular device / instance functions for which we need
148 # to do more work compared to a regular thunk or because they are
149 # part of the driver interface.
150 # - dispatch set whether we need a function pointer in the device
151 # / instance dispatch table.
152 # - driver sets whether the API is part of the driver interface.
153 # - thunk sets whether to create a thunk in vulkan_thunks.c.
154 FUNCTION_OVERRIDES = {
156 "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : False},
157 "vkEnumerateInstanceExtensionProperties" : {"dispatch" : False, "driver" : True, "thunk" : False},
158 "vkEnumerateInstanceVersion": {"dispatch" : False, "driver" : False, "thunk" : False},
159 "vkGetInstanceProcAddr": {"dispatch" : False, "driver" : True, "thunk" : False},
162 "vkCreateDevice" : {"dispatch" : True, "driver" : False, "thunk" : False},
163 "vkDestroyInstance" : {"dispatch" : False, "driver" : True, "thunk" : False },
164 "vkEnumerateDeviceExtensionProperties" : {"dispatch" : True, "driver" : False, "thunk" : False},
165 "vkEnumeratePhysicalDeviceGroups" : {"dispatch" : True, "driver" : False, "thunk" : False},
166 "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : False},
167 "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : False, "driver" : False, "thunk" : False},
168 "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False, "driver" : False, "thunk" : False},
169 "vkGetPhysicalDeviceImageFormatProperties2" : {"dispatch" : True, "driver" : False, "thunk" : True, "private_thunk" : True},
172 "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : False},
173 "vkCmdExecuteCommands" : {"dispatch" : True, "driver" : False, "thunk" : False},
174 "vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : False},
175 "vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : False},
176 "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : False},
177 "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : False},
178 "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : False},
179 "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : False},
180 "vkGetDeviceQueue2" : {"dispatch": True, "driver" : False, "thunk" : False},
181 "vkQueueSubmit" : {"dispatch": True, "driver" : False, "thunk" : False},
184 "vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
185 "vkGetPhysicalDeviceSurfaceSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
186 "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
187 "vkGetPhysicalDeviceSurfaceFormatsKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
188 "vkGetPhysicalDeviceSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
190 # VK_KHR_win32_surface
191 "vkCreateWin32SurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
192 "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
195 "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
196 "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
197 "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : True},
198 "vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : True},
200 # VK_KHR_external_fence_capabilities
201 "vkGetPhysicalDeviceExternalFencePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : False},
203 # VK_KHR_external_memory_capabilities
204 "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : False},
205 "vkGetPhysicalDeviceImageFormatProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : True, "private_thunk" : True},
207 # VK_KHR_device_group_creation
208 "vkEnumeratePhysicalDeviceGroupsKHR" : {"dispatch" : True, "driver" : False, "thunk" : False},
210 # VK_KHR_device_group
211 "vkGetDeviceGroupSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
212 "vkGetPhysicalDevicePresentRectanglesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
216 class Direction(Enum):
217 """ Parameter direction: input, output, input_output. """
223 class VkBaseType(object):
224 def __init__(self, name, _type, alias=None, requires=None):
225 """ Vulkan base type class.
227 VkBaseType is mostly used by Vulkan to define its own
228 base types like VkFlags through typedef out of e.g. uint32_t.
231 name (:obj:'str'): Name of the base type.
232 _type (:obj:'str'): Underlaying type
233 alias (bool): type is an alias or not.
234 requires (:obj:'str', optional): Other types required.
235 Often bitmask values pull in a *FlagBits type.
240 self.requires = requires
241 self.required = False
243 def definition(self):
244 # Definition is similar for alias or non-alias as type
245 # is already set to alias.
246 return "typedef {0} {1};\n".format(self.type, self.name)
249 return bool(self.alias)
252 class VkConstant(object):
253 def __init__(self, name, value):
257 def definition(self):
258 text = "#define {0} {1}\n".format(self.name, self.value)
262 class VkDefine(object):
263 def __init__(self, name, value):
268 def from_xml(define):
269 name_elem = define.find("name")
271 if name_elem is None:
272 # <type category="define" name="some_name">some_value</type>
273 # At the time of writing there is only 1 define of this category
274 # 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'.
275 name = define.attrib.get("name")
277 # We override behavior of VK_DEFINE_NON_DISPATCHABLE handle as the default
278 # definition various between 64-bit (uses pointers) and 32-bit (uses uint64_t).
279 # This complicates TRACEs in the thunks, so just use uint64_t.
280 if name == "VK_DEFINE_NON_DISPATCHABLE_HANDLE":
281 value = "#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;"
284 return VkDefine(name, value)
286 # With a name element the structure is like:
287 # <type category="define"><name>some_name</name>some_value</type>
288 name = name_elem.text
290 # Perform minimal parsing for Vulkan constants, which we don't need, but are referenced
291 # elsewhere in vk.xml.
292 # - VK_API_VERSION is a messy, deprecated constant and we don't want generate code for it.
293 # - AHardwareBuffer/ANativeWindow are forard declarations for Android types, which leaked
294 # into the define region.
295 if name in ["VK_API_VERSION", "AHardwareBuffer", "ANativeWindow", "CAMetalLayer"]:
296 return VkDefine(name, None)
298 # The body of the define is basically unstructured C code. It is not meant for easy parsing.
299 # Some lines contain deprecated values or comments, which we try to filter out.
301 for line in define.text.splitlines():
302 # Skip comments or deprecated values.
309 if child.tail is not None:
310 # Split comments for VK_API_VERSION_1_0 / VK_API_VERSION_1_1
311 if "//" in child.tail:
312 value += child.tail.split("//")[0]
316 return VkDefine(name, value.rstrip(' '))
318 def definition(self):
319 if self.value is None:
322 # Nothing to do as the value was already put in the right form during parsing.
323 return "{0}\n".format(self.value)
326 class VkEnum(object):
327 def __init__(self, name, values, alias=None):
330 self.required = False
334 def from_alias(enum, alias):
335 name = enum.attrib.get("name")
336 return VkEnum(name, alias.values, alias=alias)
340 name = enum.attrib.get("name")
343 for v in enum.findall("enum"):
344 # Value is either a value or a bitpos, only one can exist.
345 value = v.attrib.get("value")
346 alias_name = v.attrib.get("alias")
348 alias = next(x for x in values if x.name == alias_name)
349 values.append(VkEnumValue(v.attrib.get("name"), alias.value, alias.hex))
351 # Some values are in hex form. We want to preserve the hex representation
352 # at least when we convert back to a string. Internally we want to use int.
354 values.append(VkEnumValue(v.attrib.get("name"), int(value, 0), hex=True))
356 values.append(VkEnumValue(v.attrib.get("name"), int(value, 0)))
359 value = 1 << int(v.attrib.get("bitpos"))
360 values.append(VkEnumValue(v.attrib.get("name"), value, hex=True))
362 # vulkan.h contains a *_MAX_ENUM value set to 32-bit at the time of writing,
363 # which is to prepare for extensions as they can add values and hence affect
364 # the size definition.
365 max_name = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2', name).upper() + "_MAX_ENUM"
366 values.append(VkEnumValue(max_name, 0x7fffffff, hex=True))
368 return VkEnum(name, values)
370 def add(self, value):
371 """ Add a value to enum. """
373 # Extensions can add new enum values. When an extension is promoted to Core
374 # the registry defines the value twice once for old extension and once for
375 # new Core features. Ignore the duplicate entry.
376 for v in self.values:
377 if v.value == value.value:
378 LOGGER.debug("Adding duplicate enum value {0} to {1}".format(v, self.name))
380 self.values.append(value)
382 def definition(self):
383 text = "typedef enum {0}\n{{\n".format(self.name)
385 # Print values sorted, values can have been added in a random order.
386 values = sorted(self.values, key=lambda value: value.value)
388 text += " {0},\n".format(value.definition())
389 text += "}} {0};\n\n".format(self.name)
393 return bool(self.alias)
396 class VkEnumValue(object):
397 def __init__(self, name, value, hex=False):
403 return "{0}={1}".format(self.name, self.value)
405 def definition(self):
406 """ Convert to text definition e.g. VK_FOO = 1 """
408 # Hex is commonly used for FlagBits and sometimes within
409 # a non-FlagBits enum for a bitmask value as well.
411 return "{0} = 0x{1:08x}".format(self.name, self.value)
413 return "{0} = {1}".format(self.name, self.value)
416 class VkFunction(object):
417 def __init__(self, _type=None, name=None, params=[], extensions=[], alias=None):
424 # For some functions we need some extra metadata from FUNCTION_OVERRIDES.
425 func_info = FUNCTION_OVERRIDES.get(self.name, None)
426 self.dispatch = func_info["dispatch"] if func_info else True
427 self.driver = func_info["driver"] if func_info else False
428 self.thunk_needed = func_info["thunk"] if func_info else True
429 self.private_thunk = func_info["private_thunk"] if func_info and "private_thunk" in func_info else False
430 if self.private_thunk:
431 self.thunk_needed = True
433 # Required is set while parsing which APIs and types are required
434 # and is used by the code generation.
435 self.required = True if func_info else False
438 def from_alias(command, alias):
439 """ Create VkFunction from an alias command.
442 command: xml data for command
443 alias (VkFunction): function to use as a base for types / parameters.
448 func_name = command.attrib.get("name")
449 func_type = alias.type
450 params = alias.params
452 return VkFunction(_type=func_type, name=func_name, params=params, alias=alias)
455 def from_xml(command, types):
456 proto = command.find("proto")
457 func_name = proto.find("name").text
458 func_type = proto.find("type").text
461 for param in command.findall("param"):
462 vk_param = VkParam.from_xml(param, types)
463 params.append(vk_param)
465 return VkFunction(_type=func_type, name=func_name, params=params)
467 def get_conversions(self):
468 """ Get a list of conversion functions required for this function if any.
469 Parameters which are structures may require conversion between win32
470 and the host platform. This function returns a list of conversions
475 for param in self.params:
476 convs = param.get_conversions()
477 if convs is not None:
478 conversions.extend(convs)
483 return bool(self.alias)
485 def is_core_func(self):
486 """ Returns whether the function is a Vulkan core function.
487 Core functions are APIs defined by the Vulkan spec to be part of the
488 Core API as well as several KHR WSI extensions.
491 if not self.extensions:
494 return any(ext in self.extensions for ext in CORE_EXTENSIONS)
496 def is_device_func(self):
497 # If none of the other, it must be a device function
498 return not self.is_global_func() and not self.is_instance_func()
500 def is_driver_func(self):
501 """ Returns if function is part of Wine driver interface. """
504 def is_global_func(self):
505 # Treat vkGetInstanceProcAddr as a global function as it
506 # can operate with NULL for vkInstance.
507 if self.name == "vkGetInstanceProcAddr":
509 # Global functions are not passed a dispatchable object.
510 elif self.params[0].is_dispatchable():
514 def is_instance_func(self):
515 # Instance functions are passed VkInstance or VkPhysicalDevice.
516 if self.params[0].type in ["VkInstance", "VkPhysicalDevice"]:
520 def is_required(self):
523 def needs_conversion(self):
524 """ Check if the function needs any input/output type conversion.
525 Functions need input/output conversion if struct parameters have
526 alignment differences between Win32 and Linux 32-bit.
529 for p in self.params:
530 if p.needs_conversion():
531 LOGGER.debug("Parameter {0} to {1} requires conversion".format(p.name, self.name))
536 def needs_dispatch(self):
539 def needs_thunk(self):
540 return self.thunk_needed
542 def needs_private_thunk(self):
543 return self.private_thunk
545 def pfn(self, prefix="p", call_conv=None, conv=False):
546 """ Create function pointer. """
549 pfn = "{0} ({1} *{2}_{3})(".format(self.type, call_conv, prefix, self.name)
551 pfn = "{0} (*{1}_{2})(".format(self.type, prefix, self.name)
553 for i, param in enumerate(self.params):
555 pfn += param.const + " "
558 if conv and param.needs_conversion():
561 if param.is_pointer():
562 pfn += " " + param.pointer
564 if param.array_len is not None:
565 pfn += "[{0}]".format(param.array_len)
567 if i < len(self.params) - 1:
572 def prototype(self, call_conv=None, prefix=None, postfix=None):
573 """ Generate prototype for given function.
576 call_conv (str, optional): calling convention e.g. WINAPI
577 prefix (str, optional): prefix to append prior to function name e.g. vkFoo -> wine_vkFoo
578 postfix (str, optional): text to append after function name but prior to semicolon e.g. DECLSPEC_HIDDEN
581 proto = "{0}".format(self.type)
583 if call_conv is not None:
584 proto += " {0}".format(call_conv)
586 if prefix is not None:
587 proto += " {0}{1}(".format(prefix, self.name)
589 proto += " {0}(".format(self.name)
591 # Add all the parameters.
592 proto += ", ".join([p.definition() for p in self.params])
594 if postfix is not None:
595 proto += ") {0}".format(postfix)
604 if not self.needs_private_thunk():
605 body += " {0}".format(self.trace())
607 params = ", ".join([p.variable(conv=False) for p in self.params])
609 # Call the native Vulkan function.
610 if self.type == "void":
611 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
613 body += " return {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
617 def body_conversion(self):
620 # Declare a variable to hold the result for non-void functions.
621 if self.type != "void":
622 body += " {0} result;\n".format(self.type)
624 # Declare any tmp parameters for conversion.
625 for p in self.params:
626 if not p.needs_conversion():
629 if p.is_dynamic_array():
630 body += " {0}_host *{1}_host;\n".format(p.type, p.name)
632 body += " {0}_host {1}_host;\n".format(p.type, p.name)
634 if not self.needs_private_thunk():
635 body += " {0}\n".format(self.trace())
637 # Call any win_to_host conversion calls.
638 for p in self.params:
639 if not p.needs_input_conversion():
642 body += p.copy(Direction.INPUT)
644 # Build list of parameters containing converted and non-converted parameters.
645 # The param itself knows if conversion is needed and applies it when we set conv=True.
646 params = ", ".join([p.variable(conv=True) for p in self.params])
648 # Call the native Vulkan function.
649 if self.type == "void":
650 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
652 body += " result = {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
656 # Call any host_to_win conversion calls.
657 for p in self.params:
658 if not p.needs_output_conversion():
661 body += p.copy(Direction.OUTPUT)
663 # Perform any required cleanups. Most of these are for array functions.
664 for p in self.params:
665 if not p.needs_free():
670 # Finally return the result.
671 if self.type != "void":
672 body += " return result;\n"
676 def spec(self, prefix=None, symbol=None):
677 """ Generate spec file entry for this function.
680 prefix (str, optional): prefix to prepend to entry point name.
681 symbol (str, optional): allows overriding the name of the function implementing the entry point.
685 params = " ".join([p.spec() for p in self.params])
686 if prefix is not None:
687 spec += "@ stdcall -private {0}{1}({2})".format(prefix, self.name, params)
689 spec += "@ stdcall {0}({1})".format(self.name, params)
691 if symbol is not None:
697 def stub(self, call_conv=None, prefix=None):
698 stub = self.prototype(call_conv=call_conv, prefix=prefix)
700 stub += " {0}".format(self.trace(message="stub: ", trace_func="FIXME"))
702 if self.type == "VkResult":
703 stub += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
704 elif self.type == "VkBool32":
705 stub += " return VK_FALSE;\n"
706 elif self.type == "PFN_vkVoidFunction":
707 stub += " return NULL;\n"
712 def thunk(self, call_conv=None, prefix=None):
713 thunk = self.prototype(call_conv=call_conv, prefix=prefix)
716 if self.needs_conversion():
717 thunk += "#if defined(USE_STRUCT_CONVERSION)\n"
718 thunk += self.body_conversion()
728 def trace(self, message=None, trace_func=None):
729 """ Create a trace string including all parameters.
732 message (str, optional): text to print at start of trace message e.g. 'stub: '
733 trace_func (str, optional): used to override trace function e.g. FIXME, printf, etcetera.
735 if trace_func is not None:
736 trace = "{0}(\"".format(trace_func)
740 if message is not None:
743 # First loop is for all the format strings.
744 trace += ", ".join([p.format_string() for p in self.params])
747 # Second loop for parameter names and optional conversions.
748 for param in self.params:
749 if param.format_conv is not None:
750 trace += ", " + param.format_conv.format(param.name)
752 trace += ", {0}".format(param.name)
758 class VkFunctionPointer(object):
759 def __init__(self, _type, name, members):
761 self.members = members
763 self.required = False
766 def from_xml(funcpointer):
770 for t in funcpointer.findall("type"):
772 # <type>void</type>* pUserData,
773 # Parsing of the tail (anything past </type>) is tricky since there
774 # can be other data on the next line like: const <type>int</type>..
776 const = True if begin and "const" in begin else False
778 lines = t.tail.split(",\n")
779 if lines[0][0] == "*":
781 name = lines[0][1:].strip()
784 name = lines[0].strip()
786 # Filter out ); if it is contained.
787 name = name.partition(");")[0]
789 # If tail encompasses multiple lines, assign the second line to begin
792 begin = lines[1].strip()
796 members.append(VkMember(const=const, _type=_type, pointer=pointer, name=name))
798 _type = funcpointer.text
799 name = funcpointer.find("name").text
800 return VkFunctionPointer(_type, name, members)
802 def definition(self):
803 text = "{0} {1})(\n".format(self.type, self.name)
806 if len(self.members) > 0:
807 for m in self.members:
809 text += " " + m.definition()
812 text += ",\n " + m.definition()
814 # Just make the compiler happy by adding a void parameter.
820 class VkHandle(object):
821 def __init__(self, name, _type, parent, alias=None):
826 self.required = False
829 def from_alias(handle, alias):
830 name = handle.attrib.get("name")
831 return VkHandle(name, alias.type, alias.parent, alias=alias)
834 def from_xml(handle):
835 name = handle.find("name").text
836 _type = handle.find("type").text
837 parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice.
838 return VkHandle(name, _type, parent)
840 def dispatch_table(self):
841 if not self.is_dispatchable():
844 if self.parent is None:
845 # Should only happen for VkInstance
847 elif self.name == "VkDevice":
848 # VkDevice has VkInstance as a parent, but has its own dispatch table.
850 elif self.parent in ["VkInstance", "VkPhysicalDevice"]:
851 return "instance->funcs"
852 elif self.parent in ["VkDevice", "VkCommandPool"]:
853 return "device->funcs"
855 LOGGER.error("Unhandled dispatchable parent: {0}".format(self.parent))
857 def definition(self):
858 """ Generates handle definition e.g. VK_DEFINE_HANDLE(vkInstance) """
860 # Legacy types are typedef'ed to the new type if they are aliases.
862 return "typedef {0} {1};\n".format(self.alias.name, self.name)
864 return "{0}({1})\n".format(self.type, self.name)
867 return self.alias is not None
869 def is_dispatchable(self):
870 """ Some handles like VkInstance, VkDevice are dispatchable objects,
871 which means they contain a dispatch table of function pointers.
873 return self.type == "VK_DEFINE_HANDLE"
875 def is_required(self):
878 def native_handle(self, name):
879 """ Provide access to the native handle of a wrapped object. """
881 if self.name == "VkCommandPool":
882 return "wine_cmd_pool_from_handle({0})->command_pool".format(name)
884 native_handle_name = None
886 if self.name == "VkCommandBuffer":
887 native_handle_name = "command_buffer"
888 if self.name == "VkDevice":
889 native_handle_name = "device"
890 if self.name == "VkInstance":
891 native_handle_name = "instance"
892 if self.name == "VkPhysicalDevice":
893 native_handle_name = "phys_dev"
894 if self.name == "VkQueue":
895 native_handle_name = "queue"
897 if native_handle_name:
898 return "{0}->{1}".format(name, native_handle_name)
900 if self.is_dispatchable():
901 LOGGER.error("Unhandled native handle for: {0}".format(self.name))
905 class VkMember(object):
906 def __init__(self, const=False, struct_fwd_decl=False,_type=None, pointer=None, name=None, array_len=None,
907 dyn_array_len=None, optional=False):
909 self.struct_fwd_decl = struct_fwd_decl
911 self.pointer = pointer
913 self.type_info = None
914 self.array_len = array_len
915 self.dyn_array_len = dyn_array_len
916 self.optional = optional
918 def __eq__(self, other):
919 """ Compare member based on name against a string.
921 This method is for convenience by VkStruct, which holds a number of members and needs quick checking
922 if certain members exist.
925 if self.name == other:
931 return "{0} {1} {2} {3} {4} {5} {6}".format(self.const, self.struct_fwd_decl, self.type, self.pointer,
932 self.name, self.array_len, self.dyn_array_len)
935 def from_xml(member):
936 """ Helper function for parsing a member tag within a struct or union. """
938 name_elem = member.find("name")
939 type_elem = member.find("type")
942 struct_fwd_decl = False
948 if "const" in member.text:
951 # Some members contain forward declarations:
952 # - VkBaseInstructure has a member "const struct VkBaseInStructure *pNext"
953 # - VkWaylandSurfaceCreateInfoKHR has a member "struct wl_display *display"
954 if "struct" in member.text:
955 struct_fwd_decl = True
957 if type_elem is not None:
958 member_type = type_elem.text
959 if type_elem.tail is not None:
960 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
962 # Name of other member within, which stores the number of
963 # elements pointed to be by this member.
964 dyn_array_len = member.get("len")
966 # Some members are optional, which is important for conversion code e.g. not dereference NULL pointer.
967 optional = True if member.get("optional") else False
969 # Usually we need to allocate memory for dynamic arrays. We need to do the same in a few other cases
970 # like for VkCommandBufferBeginInfo.pInheritanceInfo. Just threat such cases as dynamic arrays of
971 # size 1 to simplify code generation.
972 if dyn_array_len is None and pointer is not None:
975 # Some members are arrays, attempt to parse these. Formats include:
976 # <member><type>char</type><name>extensionName</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
977 # <member><type>uint32_t</type><name>foo</name>[4]</member>
978 if name_elem.tail and name_elem.tail[0] == '[':
979 LOGGER.debug("Found array type")
980 enum_elem = member.find("enum")
981 if enum_elem is not None:
982 array_len = enum_elem.text
984 # Remove brackets around length
985 array_len = name_elem.tail.strip("[]")
987 return VkMember(const=const, struct_fwd_decl=struct_fwd_decl, _type=member_type, pointer=pointer, name=name_elem.text,
988 array_len=array_len, dyn_array_len=dyn_array_len, optional=optional)
990 def copy(self, input, output, direction):
991 """ Helper method for use by conversion logic to generate a C-code statement to copy this member. """
993 if self.needs_conversion():
994 if self.is_dynamic_array():
995 if direction == Direction.OUTPUT:
996 LOGGER.warn("TODO: implement copying of returnedonly dynamic array for {0}.{1}".format(self.type, self.name))
998 # Array length is either a variable name (string) or an int.
999 count = self.dyn_array_len if isinstance(self.dyn_array_len, int) else "{0}{1}".format(input, self.dyn_array_len)
1000 return "{0}{1} = convert_{2}_array_win_to_host({3}{1}, {4});\n".format(output, self.name, self.type, input, count)
1001 elif self.is_static_array():
1002 count = self.array_len
1003 if direction == Direction.OUTPUT:
1004 # Needed by VkMemoryHeap.memoryHeaps
1005 return "convert_{0}_static_array_host_to_win({2}{1}, {3}{1}, {4});\n".format(self.type, self.name, input, output, count)
1007 # Nothing needed this yet.
1008 LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
1010 if direction == Direction.OUTPUT:
1011 return "convert_{0}_host_to_win(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
1013 return "convert_{0}_win_to_host(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
1014 elif self.is_static_array():
1015 bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type)
1016 return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count)
1018 return "{0}{1} = {2}{1};\n".format(output, self.name, input)
1020 def definition(self, align=False, conv=False):
1021 """ Generate prototype for given function.
1024 align (bool, optional): Enable alignment if a type needs it. This adds WINE_VK_ALIGN(8) to a member.
1025 conv (bool, optional): Enable conversion if a type needs it. This appends '_host' to the name.
1032 if self.is_struct_forward_declaration():
1035 if conv and self.is_struct():
1036 text += "{0}_host".format(self.type)
1040 if self.is_pointer():
1041 text += " {0}{1}".format(self.pointer, self.name)
1043 if align and self.needs_alignment():
1044 text += " WINE_VK_ALIGN(8) " + self.name
1046 text += " " + self.name
1048 if self.is_static_array():
1049 text += "[{0}]".format(self.array_len)
1053 def get_conversions(self):
1054 """ Return any conversion description for this member and its children when conversion is needed. """
1056 # Check if we need conversion either for this member itself or for any child members
1057 # in case member represents a struct.
1058 if not self.needs_conversion():
1063 # Collect any conversion for any member structs.
1064 struct = self.type_info["data"]
1066 m.needs_struct_extensions_conversion()
1067 if m.needs_conversion():
1068 conversions.extend(m.get_conversions())
1070 struct.needs_struct_extensions_conversion()
1072 struct = self.type_info["data"]
1073 direction = Direction.OUTPUT if struct.returnedonly else Direction.INPUT
1074 if self.is_dynamic_array():
1075 conversions.append(ConversionFunction(False, True, direction, struct))
1076 elif self.is_static_array():
1077 conversions.append(ConversionFunction(True, False, direction, struct))
1079 conversions.append(ConversionFunction(False, False, direction, struct))
1081 if self.needs_free():
1082 conversions.append(FreeFunction(self.is_dynamic_array(), struct))
1089 def is_dynamic_array(self):
1090 """ Returns if the member is an array element.
1091 Vulkan uses this for dynamically sized arrays for which
1092 there is a 'count' parameter.
1094 return self.dyn_array_len is not None
1096 def is_handle(self):
1097 return self.type_info["category"] == "handle"
1099 def is_pointer(self):
1100 return self.pointer is not None
1102 def is_static_array(self):
1103 """ Returns if the member is an array.
1104 Vulkan uses this often for fixed size arrays in which the
1105 length is part of the member.
1107 return self.array_len is not None
1109 def is_struct(self):
1110 return self.type_info["category"] == "struct"
1112 def is_struct_forward_declaration(self):
1113 return self.struct_fwd_decl
1116 return self.type_info["category"] == "union"
1118 def needs_alignment(self):
1119 """ Check if this member needs alignment for 64-bit data.
1120 Various structures need alignment on 64-bit variables due
1121 to compiler differences on 32-bit between Win32 and Linux.
1124 if self.is_pointer():
1126 elif self.type == "size_t":
1128 elif self.type in ["uint64_t", "VkDeviceSize"]:
1130 elif self.is_struct():
1131 struct = self.type_info["data"]
1132 return struct.needs_alignment()
1133 elif self.is_handle():
1134 # Dispatchable handles are pointers to objects, while
1135 # non-dispatchable are uint64_t and hence need alignment.
1136 handle = self.type_info["data"]
1137 return False if handle.is_dispatchable() else True
1140 def needs_conversion(self):
1141 """ Structures requiring alignment, need conversion between win32 and host. """
1143 if not self.is_struct():
1146 struct = self.type_info["data"]
1147 return struct.needs_conversion()
1149 def needs_free(self):
1150 if not self.needs_conversion():
1153 if self.is_dynamic_array():
1156 # TODO: some non-pointer structs and optional pointer structs may need freeing,
1157 # though none of this type have been encountered yet.
1160 def needs_struct_extensions_conversion(self):
1161 if not self.is_struct():
1164 struct = self.type_info["data"]
1165 return struct.needs_struct_extensions_conversion()
1167 def set_type_info(self, type_info):
1168 """ Helper function to set type information from the type registry.
1169 This is needed, because not all type data is available at time of
1172 self.type_info = type_info
1175 class VkParam(object):
1176 """ Helper class which describes a parameter to a function call. """
1178 def __init__(self, type_info, const=None, pointer=None, name=None, array_len=None, dyn_array_len=None):
1181 self.array_len = array_len
1182 self.dyn_array_len = dyn_array_len
1183 self.pointer = pointer
1184 self.type_info = type_info
1185 self.type = type_info["name"] # For convenience
1186 self.handle = type_info["data"] if type_info["category"] == "handle" else None
1187 self.struct = type_info["data"] if type_info["category"] == "struct" else None
1189 self._set_direction()
1190 self._set_format_string()
1191 self._set_conversions()
1194 return "{0} {1} {2} {3} {4} {5}".format(self.const, self.type, self.pointer, self.name, self.array_len, self.dyn_array_len)
1197 def from_xml(param, types):
1198 """ Helper function to create VkParam from xml. """
1200 # Parameter parsing is slightly tricky. All the data is contained within
1201 # a param tag, but some data is within subtags while others are text
1202 # before or after the type tag.
1204 # <param>const <type>char</type>* <name>pLayerName</name></param>
1206 name_elem = param.find("name")
1208 name = name_elem.text
1209 # Tail contains array length e.g. for blendConstants param of vkSetBlendConstants
1210 if name_elem.tail is not None:
1211 array_len = name_elem.tail.strip("[]")
1213 # Name of other parameter in function prototype, which stores the number of
1214 # elements pointed to be by this parameter.
1215 dyn_array_len = param.get("len", None)
1217 const = param.text.strip() if param.text else None
1218 type_elem = param.find("type")
1219 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1221 # Since we have parsed all types before hand, this should not happen.
1222 type_info = types.get(type_elem.text, None)
1223 if type_info is None:
1224 LOGGER.err("type info not found for: {0}".format(type_elem.text))
1226 return VkParam(type_info, const=const, pointer=pointer, name=name, array_len=array_len, dyn_array_len=dyn_array_len)
1228 def _set_conversions(self):
1229 """ Internal helper function to configure any needed conversion functions. """
1231 self.free_func = None
1232 self.input_conv = None
1233 self.output_conv = None
1234 if not self.needs_conversion():
1237 # Input functions require win to host conversion.
1238 if self._direction in [Direction.INPUT, Direction.INPUT_OUTPUT]:
1239 self.input_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.INPUT, self.struct)
1241 # Output functions require host to win conversion.
1242 if self._direction in [Direction.INPUT_OUTPUT, Direction.OUTPUT]:
1243 self.output_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.OUTPUT, self.struct)
1245 # Dynamic arrays, but also some normal structs (e.g. VkCommandBufferBeginInfo) need memory
1246 # allocation and thus some cleanup.
1247 if self.is_dynamic_array() or self.struct.needs_free():
1248 self.free_func = FreeFunction(self.is_dynamic_array(), self.struct)
1250 def _set_direction(self):
1251 """ Internal helper function to set parameter direction (input/output/input_output). """
1253 # The parameter direction needs to be determined from hints in vk.xml like returnedonly,
1254 # parameter constness and other heuristics.
1255 # For now we need to get this right for structures as we need to convert these, we may have
1256 # missed a few other edge cases (e.g. count variables).
1257 # See also https://github.com/KhronosGroup/Vulkan-Docs/issues/610
1259 if not self.is_pointer():
1260 self._direction = Direction.INPUT
1261 elif self.is_const() and self.is_pointer():
1262 self._direction = Direction.INPUT
1263 elif self.is_struct():
1264 if not self.struct.returnedonly:
1265 self._direction = Direction.INPUT
1268 # Returnedonly hints towards output, however in some cases
1269 # it is inputoutput. In particular if pNext / sType exist,
1270 # which are used to link in other structures without having
1271 # to introduce new APIs. E.g. vkGetPhysicalDeviceProperties2KHR.
1272 if "pNext" in self.struct:
1273 self._direction = Direction.INPUT_OUTPUT
1276 self._direction = Direction.OUTPUT
1278 # This should mostly be right. Count variables can be inout, but we don't care about these yet.
1279 self._direction = Direction.OUTPUT
1281 def _set_format_string(self):
1282 """ Internal helper function to be used by constructor to set format string. """
1284 # Determine a format string used by code generation for traces.
1285 # 64-bit types need a conversion function.
1286 self.format_conv = None
1287 if self.is_static_array() or self.is_pointer():
1288 self.format_str = "%p"
1290 if self.type_info["category"] in ["bitmask", "enum"]:
1291 self.format_str = "%#x"
1292 elif self.is_handle():
1293 # We use uint64_t for non-dispatchable handles as opposed to pointers
1294 # for dispatchable handles.
1295 if self.handle.is_dispatchable():
1296 self.format_str = "%p"
1298 self.format_str = "0x%s"
1299 self.format_conv = "wine_dbgstr_longlong({0})"
1300 elif self.type == "float":
1301 self.format_str = "%f"
1302 elif self.type == "int":
1303 self.format_str = "%d"
1304 elif self.type == "int32_t":
1305 self.format_str = "%d"
1306 elif self.type == "size_t":
1307 self.format_str = "0x%s"
1308 self.format_conv = "wine_dbgstr_longlong({0})"
1309 elif self.type in ["uint32_t", "VkBool32"]:
1310 self.format_str = "%u"
1311 elif self.type in ["uint64_t", "VkDeviceSize"]:
1312 self.format_str = "0x%s"
1313 self.format_conv = "wine_dbgstr_longlong({0})"
1314 elif self.type == "HANDLE":
1315 self.format_str = "%p"
1316 elif self.type in ["VisualID", "xcb_visualid_t", "RROutput"]:
1317 # Don't care about Linux specific types.
1318 self.format_str = ""
1320 LOGGER.warn("Unhandled type: {0}".format(self.type_info))
1322 def copy(self, direction):
1323 if direction == Direction.INPUT:
1324 if self.is_dynamic_array():
1325 return " {0}_host = convert_{1}_array_win_to_host({0}, {2});\n".format(self.name, self.type, self.dyn_array_len)
1327 return " convert_{0}_win_to_host({1}, &{1}_host);\n".format(self.type, self.name)
1329 if self.is_dynamic_array():
1330 LOGGER.error("Unimplemented output conversion for: {0}".format(self.name))
1332 return " convert_{0}_host_to_win(&{1}_host, {1});\n".format(self.type, self.name)
1334 def definition(self, postfix=None):
1335 """ Return prototype for the parameter. E.g. 'const char *foo' """
1339 proto += self.const + " "
1343 if self.is_pointer():
1344 proto += " {0}{1}".format(self.pointer, self.name)
1346 proto += " " + self.name
1348 # Allows appending something to the variable name useful for
1349 # win32 to host conversion.
1350 if postfix is not None:
1353 if self.is_static_array():
1354 proto += "[{0}]".format(self.array_len)
1358 def direction(self):
1359 """ Returns parameter direction: input, output, input_output.
1361 Parameter direction in Vulkan is not straight-forward, which this function determines.
1364 return self._direction
1366 def dispatch_table(self):
1367 """ Return functions dispatch table pointer for dispatchable objects. """
1369 if not self.is_dispatchable():
1372 return "{0}->{1}".format(self.name, self.handle.dispatch_table())
1374 def format_string(self):
1375 return self.format_str
1378 if self.is_dynamic_array():
1379 if self.struct.returnedonly:
1380 # For returnedonly, counts is stored in a pointer.
1381 return " free_{0}_array({1}_host, *{2});\n".format(self.type, self.name, self.dyn_array_len)
1383 return " free_{0}_array({1}_host, {2});\n".format(self.type, self.name, self.dyn_array_len)
1385 # We are operating on a single structure. Some structs (very rare) contain dynamic members,
1386 # which would need freeing.
1387 if self.struct.needs_free():
1388 return " free_{0}(&{1}_host);\n".format(self.type, self.name)
1391 def get_conversions(self):
1392 """ Get a list of conversions required for this parameter if any.
1393 Parameters which are structures may require conversion between win32
1394 and the host platform. This function returns a list of conversions
1398 if not self.is_struct():
1401 self.struct.needs_struct_extensions_conversion()
1402 for m in self.struct:
1403 m.needs_struct_extensions_conversion()
1405 if not self.needs_conversion():
1410 # Collect any member conversions first, so we can guarantee
1411 # those functions will be defined prior to usage by the
1412 # 'parent' param requiring conversion.
1413 for m in self.struct:
1414 if not m.is_struct():
1417 if not m.needs_conversion():
1420 conversions.extend(m.get_conversions())
1422 # Conversion requirements for the 'parent' parameter.
1423 if self.input_conv is not None:
1424 conversions.append(self.input_conv)
1425 if self.output_conv is not None:
1426 conversions.append(self.output_conv)
1427 if self.free_func is not None:
1428 conversions.append(self.free_func)
1433 return self.const is not None
1435 def is_dynamic_array(self):
1436 return self.dyn_array_len is not None
1438 def is_dispatchable(self):
1439 if not self.is_handle():
1442 return self.handle.is_dispatchable()
1444 def is_handle(self):
1445 return self.handle is not None
1447 def is_pointer(self):
1448 return self.pointer is not None
1450 def is_static_array(self):
1451 return self.array_len is not None
1453 def is_struct(self):
1454 return self.struct is not None
1456 def needs_conversion(self):
1457 """ Returns if parameter needs conversion between win32 and host. """
1459 if not self.is_struct():
1462 # VkSparseImageMemoryRequirements(2) is used by vkGetImageSparseMemoryRequirements(2).
1463 # This function is tricky to wrap, because how to wrap depends on whether
1464 # pSparseMemoryRequirements is NULL or not. Luckily for VkSparseImageMemoryRequirements(2)
1465 # the alignment works out in such a way that no conversion is needed between win32 and Linux.
1466 if self.type in ["VkSparseImageMemoryRequirements", "VkSparseImageMemoryRequirements2"]:
1469 # If a structure needs alignment changes, it means we need to
1470 # perform parameter conversion between win32 and host.
1471 if self.struct.needs_conversion():
1476 def needs_free(self):
1477 return self.free_func is not None
1479 def needs_input_conversion(self):
1480 return self.input_conv is not None
1482 def needs_output_conversion(self):
1483 return self.output_conv is not None
1486 """ Generate spec file entry for this parameter. """
1488 if self.type_info["category"] in ["bitmask", "enum"]:
1490 if self.is_pointer() and self.type == "char":
1492 if self.is_dispatchable() or self.is_pointer() or self.is_static_array():
1494 if self.is_handle() and not self.is_dispatchable():
1496 if self.type == "float":
1498 if self.type in ["int", "int32_t", "size_t", "uint32_t", "VkBool32"]:
1500 if self.type in ["uint64_t", "VkDeviceSize"]:
1503 LOGGER.error("Unhandled spec conversion for type: {0}".format(self.type))
1505 def variable(self, conv=False):
1506 """ Returns 'glue' code during generation of a function call on how to access the variable.
1507 This function handles various scenarios such as 'unwrapping' if dispatchable objects and
1508 renaming of parameters in case of win32 -> host conversion.
1511 conv (bool, optional): Enable conversion if the param needs it. This appends '_host' to the name.
1514 # Hack until we enable allocation callbacks from ICD to application. These are a joy
1515 # to enable one day, because of calling convention conversion.
1516 if "VkAllocationCallbacks" in self.type:
1517 LOGGER.debug("TODO: setting NULL VkAllocationCallbacks for {0}".format(self.name))
1520 if conv and self.needs_conversion():
1521 if self.is_dynamic_array():
1522 return "{0}_host".format(self.name)
1524 return "&{0}_host".format(self.name)
1526 # We need to pass the native handle to the native Vulkan calls.
1527 native_handle = self.handle.native_handle(self.name) if self.is_handle() else None
1528 return native_handle if native_handle else self.name
1531 class VkStruct(Sequence):
1532 """ Class which represents the type union and struct. """
1534 def __init__(self, name, members, returnedonly, structextends, alias=None, union=False):
1536 self.members = members
1537 self.returnedonly = returnedonly
1538 self.structextends = structextends
1539 self.required = False
1542 self.type_info = None # To be set later.
1543 self.struct_extensions = []
1545 def __getitem__(self, i):
1546 return self.members[i]
1549 return len(self.members)
1552 def from_alias(struct, alias):
1553 name = struct.attrib.get("name")
1554 return VkStruct(name, alias.members, alias.returnedonly, alias.structextends, alias=alias)
1557 def from_xml(struct):
1558 # Unions and structs are the same parsing wise, but we need to
1559 # know which one we are dealing with later on for code generation.
1560 union = True if struct.attrib["category"] == "union" else False
1562 name = struct.attrib.get("name")
1564 # 'Output' structures for which data is filled in by the API are
1565 # marked as 'returnedonly'.
1566 returnedonly = True if struct.attrib.get("returnedonly") else False
1568 structextends = struct.attrib.get("structextends")
1569 structextends = structextends.split(",") if structextends else []
1572 for member in struct.findall("member"):
1573 vk_member = VkMember.from_xml(member)
1574 members.append(vk_member)
1576 return VkStruct(name, members, returnedonly, structextends, union=union)
1579 def decouple_structs(structs):
1580 """ Helper function which decouples a list of structs.
1581 Structures often depend on other structures. To make the C compiler
1582 happy we need to define 'substructures' first. This function analyzes
1583 the list of structures and reorders them in such a way that they are
1587 tmp_structs = list(structs) # Don't modify the original structures.
1588 decoupled_structs = []
1590 while (len(tmp_structs) > 0):
1591 for struct in tmp_structs:
1594 if not struct.required:
1595 tmp_structs.remove(struct)
1599 if not (m.is_struct() or m.is_union()):
1602 # VkBaseInstructure and VkBaseOutStructure reference themselves.
1603 if m.type == struct.name:
1607 # Check if a struct we depend on has already been defined.
1608 for s in decoupled_structs:
1609 if s.name == m.type:
1614 # Check if the struct we depend on is even in the list of structs.
1615 # If found now, it means we haven't met all dependencies before we
1616 # can operate on the current struct.
1617 # When generating 'host' structs we may not be able to find a struct
1618 # as the list would only contain the structs requiring conversion.
1619 for s in tmp_structs:
1620 if s.name == m.type:
1624 if dependends == False:
1625 decoupled_structs.append(struct)
1626 tmp_structs.remove(struct)
1628 return decoupled_structs
1630 def definition(self, align=False, conv=False, postfix=None):
1631 """ Convert structure to textual definition.
1634 align (bool, optional): enable alignment to 64-bit for win32 struct compatibility.
1635 conv (bool, optional): enable struct conversion if the struct needs it.
1636 postfix (str, optional): text to append to end of struct name, useful for struct renaming.
1640 text = "typedef union {0}".format(self.name)
1642 text = "typedef struct {0}".format(self.name)
1644 if postfix is not None:
1650 if align and m.needs_alignment():
1651 text += " {0};\n".format(m.definition(align=align))
1652 elif conv and m.needs_conversion():
1653 text += " {0};\n".format(m.definition(conv=conv))
1655 text += " {0};\n".format(m.definition())
1657 if postfix is not None:
1658 text += "}} {0}{1};\n\n".format(self.name, postfix)
1660 text += "}} {0};\n\n".format(self.name)
1664 return bool(self.alias)
1666 def needs_alignment(self):
1667 """ Check if structure needs alignment for 64-bit data.
1668 Various structures need alignment on 64-bit variables due
1669 to compiler differences on 32-bit between Win32 and Linux.
1672 for m in self.members:
1673 if m.needs_alignment():
1677 def needs_conversion(self):
1678 """ Returns if struct members needs conversion between win32 and host.
1679 Structures need conversion if they contain members requiring alignment
1680 or if they include other structures which need alignment.
1683 if self.needs_alignment():
1686 for m in self.members:
1687 if m.needs_conversion():
1691 def needs_free(self):
1692 """ Check if any struct member needs some memory freeing."""
1694 for m in self.members:
1702 def needs_struct_extensions_conversion(self):
1703 """ Checks if structure extensions in pNext chain need conversion. """
1706 for e in self.struct_extensions:
1707 if e.required and e.needs_conversion():
1708 LOGGER.error("Unhandled pNext chain conversion for {0}".format(e.name))
1713 def set_type_info(self, types):
1714 """ Helper function to set type information from the type registry.
1715 This is needed, because not all type data is available at time of
1718 for m in self.members:
1719 type_info = types[m.type]
1720 m.set_type_info(type_info)
1723 class ConversionFunction(object):
1724 def __init__(self, array, dyn_array, direction, struct):
1726 self.direction = direction
1727 self.dyn_array = dyn_array
1728 self.struct = struct
1729 self.type = struct.name
1733 def __eq__(self, other):
1734 if self.name != other.name:
1739 def _generate_array_conversion_func(self):
1740 """ Helper function for generating a conversion function for array structs. """
1742 if self.direction == Direction.OUTPUT:
1743 params = ["const {0}_host *in".format(self.type), "uint32_t count"]
1744 return_type = self.type
1746 params = ["const {0} *in".format(self.type), "uint32_t count"]
1747 return_type = "{0}_host".format(self.type)
1749 # Generate function prototype.
1750 body = "static inline {0} *{1}(".format(return_type, self.name)
1751 body += ", ".join(p for p in params)
1754 body += " {0} *out;\n".format(return_type)
1755 body += " unsigned int i;\n\n"
1756 body += " if (!in) return NULL;\n\n"
1758 body += " out = heap_alloc(count * sizeof(*out));\n"
1760 body += " for (i = 0; i < count; i++)\n"
1763 for m in self.struct:
1764 # TODO: support copying of pNext extension structures!
1765 # Luckily though no extension struct at this point needs conversion.
1766 body += " " + m.copy("in[i].", "out[i].", self.direction)
1769 body += " return out;\n"
1773 def _generate_conversion_func(self):
1774 """ Helper function for generating a conversion function for non-array structs. """
1776 if self.direction == Direction.OUTPUT:
1777 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type)]
1779 params = ["const {0} *in".format(self.type), "{0}_host *out".format(self.type)]
1781 body = "static inline void {0}(".format(self.name)
1783 # Generate parameter list
1784 body += ", ".join(p for p in params)
1787 body += " if (!in) return;\n\n"
1789 if self.direction == Direction.INPUT and "pNext" in self.struct and self.struct.returnedonly:
1790 # We are dealing with an input_output parameter. For these we only need to copy
1791 # pNext and sType as the other fields are filled in by the host. We do potentially
1792 # have to iterate over pNext and perform conversions based on switch(sType)!
1793 # Luckily though no extension structs at this point need conversion.
1794 # TODO: support copying of pNext extension structures!
1795 body += " out->pNext = in->pNext;\n"
1796 body += " out->sType = in->sType;\n"
1798 for m in self.struct:
1799 # TODO: support copying of pNext extension structures!
1800 body += " " + m.copy("in->", "out->", self.direction)
1805 def _generate_static_array_conversion_func(self):
1806 """ Helper function for generating a conversion function for array structs. """
1808 if self.direction == Direction.OUTPUT:
1809 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
1811 params = ["const {0} *in".format(self.type), "{0} *out_host".format(self.type), "uint32_t count"]
1813 # Generate function prototype.
1814 body = "static inline void {0}(".format(self.name)
1815 body += ", ".join(p for p in params)
1817 body += " unsigned int i;\n\n"
1818 body += " if (!in) return;\n\n"
1819 body += " for (i = 0; i < count; i++)\n"
1822 for m in self.struct:
1823 # TODO: support copying of pNext extension structures!
1824 body += " " + m.copy("in[i].", "out[i].", self.direction)
1830 def _set_name(self):
1831 if self.direction == Direction.INPUT:
1833 name = "convert_{0}_static_array_win_to_host".format(self.type)
1834 elif self.dyn_array:
1835 name = "convert_{0}_array_win_to_host".format(self.type)
1837 name = "convert_{0}_win_to_host".format(self.type)
1838 else: # Direction.OUTPUT
1840 name = "convert_{0}_static_array_host_to_win".format(self.type)
1841 elif self.dyn_array:
1842 name = "convert_{0}_array_host_to_win".format(self.type)
1844 name = "convert_{0}_host_to_win".format(self.type)
1848 def definition(self):
1850 return self._generate_static_array_conversion_func()
1851 elif self.dyn_array:
1852 return self._generate_array_conversion_func()
1854 return self._generate_conversion_func()
1857 class FreeFunction(object):
1858 def __init__(self, dyn_array, struct):
1859 self.dyn_array = dyn_array
1860 self.struct = struct
1861 self.type = struct.name
1864 self.name = "free_{0}_array".format(self.type)
1866 self.name = "free_{0}".format(self.type)
1868 def __eq__(self, other):
1869 if self.name == other.name:
1874 def _generate_array_free_func(self):
1875 """ Helper function for cleaning up temporary buffers required for array conversions. """
1877 # Generate function prototype.
1878 body = "static inline void {0}({1}_host *in, uint32_t count)\n{{\n".format(self.name, self.type)
1880 # E.g. VkGraphicsPipelineCreateInfo_host needs freeing for pStages.
1881 if self.struct.needs_free():
1882 body += " unsigned int i;\n\n"
1883 body += " if (!in) return;\n\n"
1884 body += " for (i = 0; i < count; i++)\n"
1887 for m in self.struct:
1888 if m.needs_conversion() and m.is_dynamic_array():
1890 # Add a cast to ignore const on conversion structs we allocated ourselves.
1891 body += " free_{0}_array(({0}_host *)in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
1893 body += " free_{0}_array(in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
1894 elif m.needs_conversion():
1895 LOGGER.error("Unhandled conversion for {0}".format(m.name))
1898 body += " if (!in) return;\n\n"
1900 body += " heap_free(in);\n"
1905 def _generate_free_func(self):
1906 # E.g. VkCommandBufferBeginInfo.pInheritanceInfo needs freeing.
1907 if not self.struct.needs_free():
1910 # Generate function prototype.
1911 body = "static inline void {0}({1}_host *in)\n{{\n".format(self.name, self.type)
1913 for m in self.struct:
1914 if m.needs_conversion() and m.is_dynamic_array():
1915 count = m.dyn_array_len if isinstance(m.dyn_array_len, int) else "in->{0}".format(m.dyn_array_len)
1917 # Add a cast to ignore const on conversion structs we allocated ourselves.
1918 body += " free_{0}_array(({0}_host *)in->{1}, {2});\n".format(m.type, m.name, count)
1920 body += " free_{0}_array(in->{1}, {2});\n".format(m.type, m.name, count)
1925 def definition(self):
1927 return self._generate_array_free_func()
1929 # Some structures need freeing too if they contain dynamic arrays.
1930 # E.g. VkCommandBufferBeginInfo
1931 return self._generate_free_func()
1934 class VkGenerator(object):
1935 def __init__(self, registry):
1936 self.registry = registry
1938 # Build a list conversion functions for struct conversion.
1939 self.conversions = []
1940 self.host_structs = []
1941 for func in self.registry.funcs.values():
1942 if not func.is_required():
1945 if not func.needs_conversion():
1948 conversions = func.get_conversions()
1949 for conv in conversions:
1950 # Pull in any conversions for vulkan_thunks.c.
1951 if func.needs_thunk():
1952 # Append if we don't already have this conversion.
1953 if not any(c == conv for c in self.conversions):
1954 self.conversions.append(conv)
1956 # Structs can be used in different ways by different conversions
1957 # e.g. array vs non-array. Just make sure we pull in each struct once.
1958 if not any(s.name == conv.struct.name for s in self.host_structs):
1959 self.host_structs.append(conv.struct)
1961 def _generate_copyright(self, f, spec_file=False):
1962 f.write("# " if spec_file else "/* ")
1963 f.write("Automatically generated from Vulkan vk.xml; DO NOT EDIT!\n")
1964 lines = ["", "This file is generated from Vulkan vk.xml file covered",
1965 "by the following copyright and permission notice:"]
1966 lines.extend([l.rstrip(" ") for l in self.registry.copyright.splitlines()])
1968 f.write("{0}{1}".format("# " if spec_file else " * ", line).rstrip(" ") + "\n")
1969 f.write("\n" if spec_file else " */\n\n")
1971 def generate_thunks_c(self, f, prefix):
1972 self._generate_copyright(f)
1973 f.write("#include \"config.h\"\n")
1974 f.write("#include \"wine/port.h\"\n\n")
1976 f.write("#include \"vulkan_private.h\"\n\n")
1978 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
1980 # Generate any conversion helper functions.
1981 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
1982 for conv in self.conversions:
1983 f.write(conv.definition())
1984 f.write("#endif /* USE_STRUCT_CONVERSION */\n\n")
1986 # Create thunks for instance and device functions.
1987 # Global functions don't go through the thunks.
1988 for vk_func in self.registry.funcs.values():
1989 if not vk_func.is_required():
1992 if vk_func.is_global_func():
1995 if not vk_func.needs_thunk():
1998 # Exports symbols for Core functions.
1999 if not vk_func.is_core_func() and not vk_func.needs_private_thunk():
2002 if vk_func.needs_private_thunk():
2003 f.write(vk_func.thunk(prefix="thunk_"))
2005 f.write(vk_func.thunk(prefix=prefix, call_conv="WINAPI"))
2007 f.write("static const struct vulkan_func vk_device_dispatch_table[] =\n{\n")
2008 for vk_func in self.registry.device_funcs:
2009 if not vk_func.is_required():
2012 f.write(" {{\"{0}\", &{1}{0}}},\n".format(vk_func.name, prefix))
2015 f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
2016 for vk_func in self.registry.instance_funcs:
2017 if not vk_func.is_required():
2020 f.write(" {{\"{0}\", &{1}{0}}},\n".format(vk_func.name, prefix))
2023 f.write("void *wine_vk_get_device_proc_addr(const char *name)\n")
2025 f.write(" unsigned int i;\n")
2026 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_dispatch_table); i++)\n")
2028 f.write(" if (strcmp(vk_device_dispatch_table[i].name, name) == 0)\n")
2030 f.write(" TRACE(\"Found name=%s in device table\\n\", debugstr_a(name));\n")
2031 f.write(" return vk_device_dispatch_table[i].func;\n")
2034 f.write(" return NULL;\n")
2037 f.write("void *wine_vk_get_instance_proc_addr(const char *name)\n")
2039 f.write(" unsigned int i;\n")
2040 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_dispatch_table); i++)\n")
2042 f.write(" if (strcmp(vk_instance_dispatch_table[i].name, name) == 0)\n")
2044 f.write(" TRACE(\"Found name=%s in instance table\\n\", debugstr_a(name));\n")
2045 f.write(" return vk_instance_dispatch_table[i].func;\n")
2048 f.write(" return NULL;\n")
2051 # Create array of device extensions.
2052 f.write("static const char * const vk_device_extensions[] =\n{\n")
2053 for ext in self.registry.extensions:
2054 if ext["type"] != "device":
2057 f.write(" \"{0}\",\n".format(ext["name"]))
2060 # Create array of instance extensions.
2061 f.write("static const char * const vk_instance_extensions[] =\n{\n")
2062 for ext in self.registry.extensions:
2063 if ext["type"] != "instance":
2066 f.write(" \"{0}\",\n".format(ext["name"]))
2069 f.write("BOOL wine_vk_device_extension_supported(const char *name)\n")
2071 f.write(" unsigned int i;\n")
2072 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_extensions); i++)\n")
2074 f.write(" if (strcmp(vk_device_extensions[i], name) == 0)\n")
2075 f.write(" return TRUE;\n")
2077 f.write(" return FALSE;\n")
2080 f.write("BOOL wine_vk_instance_extension_supported(const char *name)\n")
2082 f.write(" unsigned int i;\n")
2083 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_extensions); i++)\n")
2085 f.write(" if (strcmp(vk_instance_extensions[i], name) == 0)\n")
2086 f.write(" return TRUE;\n")
2088 f.write(" return FALSE;\n")
2091 def generate_thunks_h(self, f, prefix):
2092 self._generate_copyright(f)
2094 f.write("#ifndef __WINE_VULKAN_THUNKS_H\n")
2095 f.write("#define __WINE_VULKAN_THUNKS_H\n\n")
2097 f.write("#define WINE_VK_VERSION VK_API_VERSION_{0}_{1}\n\n".format(WINE_VK_VERSION[0], WINE_VK_VERSION[1]))
2099 # Generate prototypes for device and instance functions requiring a custom implementation.
2100 f.write("/* Functions for which we have custom implementations outside of the thunks. */\n")
2101 for vk_func in self.registry.funcs.values():
2102 if not vk_func.is_required() or vk_func.is_global_func():
2104 if vk_func.needs_thunk() and not vk_func.needs_private_thunk():
2107 if vk_func.is_core_func():
2108 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix="wine_")))
2110 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix="wine_", postfix="DECLSPEC_HIDDEN")))
2113 f.write("/* Private thunks */\n")
2114 for vk_func in self.registry.funcs.values():
2115 if vk_func.needs_private_thunk():
2116 f.write("{0};\n".format(vk_func.prototype(prefix="thunk_", postfix="DECLSPEC_HIDDEN")))
2119 for struct in self.host_structs:
2120 f.write(struct.definition(align=False, conv=True, postfix="_host"))
2123 f.write("/* For use by vkDevice and children */\n")
2124 f.write("struct vulkan_device_funcs\n{\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 vulkan_device_funcs".format(vk_func.name))
2133 if vk_func.needs_conversion():
2134 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
2135 f.write(" {0};\n".format(vk_func.pfn(conv=True)))
2137 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2140 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2143 f.write("/* For use by vkInstance and children */\n")
2144 f.write("struct vulkan_instance_funcs\n{\n")
2145 for vk_func in self.registry.instance_funcs:
2146 if not vk_func.is_required():
2149 if not vk_func.needs_dispatch():
2150 LOGGER.debug("skipping {0} in vulkan_instance_funcs".format(vk_func.name))
2153 if vk_func.needs_conversion():
2154 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
2155 f.write(" {0};\n".format(vk_func.pfn(conv=True)))
2157 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2160 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2163 f.write("#define ALL_VK_DEVICE_FUNCS() \\\n")
2165 for vk_func in self.registry.device_funcs:
2166 if not vk_func.is_required():
2169 if not vk_func.needs_dispatch():
2170 LOGGER.debug("skipping {0} in ALL_VK_DEVICE_FUNCS".format(vk_func.name))
2174 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2177 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2180 f.write("#define ALL_VK_INSTANCE_FUNCS() \\\n")
2182 for vk_func in self.registry.instance_funcs:
2183 if not vk_func.is_required():
2186 if not vk_func.needs_dispatch():
2187 LOGGER.debug("skipping {0} in ALL_VK_INSTANCE_FUNCS".format(vk_func.name))
2191 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2194 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2197 f.write("#endif /* __WINE_VULKAN_THUNKS_H */\n")
2199 def generate_vulkan_h(self, f):
2200 self._generate_copyright(f)
2201 f.write("#ifndef __WINE_VULKAN_H\n")
2202 f.write("#define __WINE_VULKAN_H\n\n")
2204 f.write("#include <windef.h>\n")
2205 f.write("#include <stdint.h>\n\n")
2207 f.write("/* Define WINE_VK_HOST to get 'host' headers. */\n")
2208 f.write("#ifdef WINE_VK_HOST\n")
2209 f.write("#define VKAPI_CALL\n")
2210 f.write('#define WINE_VK_ALIGN(x)\n')
2211 f.write("#endif\n\n")
2213 f.write("#ifndef VKAPI_CALL\n")
2214 f.write("#define VKAPI_CALL __stdcall\n")
2215 f.write("#endif\n\n")
2217 f.write("#ifndef VKAPI_PTR\n")
2218 f.write("#define VKAPI_PTR VKAPI_CALL\n")
2219 f.write("#endif\n\n")
2221 f.write("#ifndef WINE_VK_ALIGN\n")
2222 f.write("#define WINE_VK_ALIGN DECLSPEC_ALIGN\n")
2223 f.write("#endif\n\n")
2225 # The overall strategy is to define independent constants and datatypes,
2226 # prior to complex structures and function calls to avoid forward declarations.
2227 for const in self.registry.consts:
2228 # For now just generate things we may not need. The amount of parsing needed
2229 # to get some of the info is tricky as you need to figure out which structure
2230 # references a certain constant.
2231 f.write(const.definition())
2234 for define in self.registry.defines:
2235 f.write(define.definition())
2237 for handle in self.registry.handles:
2238 # For backward compatibility also create definitions for aliases.
2239 # These types normally don't get pulled in as we use the new types
2240 # even in legacy functions if they are aliases.
2241 if handle.is_required() or handle.is_alias():
2242 f.write(handle.definition())
2245 for base_type in self.registry.base_types:
2246 f.write(base_type.definition())
2249 for bitmask in self.registry.bitmasks:
2250 f.write(bitmask.definition())
2253 # Define enums, this includes values for some of the bitmask types as well.
2254 for enum in self.registry.enums.values():
2256 f.write(enum.definition())
2258 for fp in self.registry.funcpointers:
2260 f.write(fp.definition())
2263 # This generates both structures and unions. Since structures
2264 # may depend on other structures/unions, we need a list of
2265 # decoupled structs.
2266 # Note: unions are stored in structs for dependency reasons,
2267 # see comment in parsing section.
2268 structs = VkStruct.decouple_structs(self.registry.structs)
2269 for struct in structs:
2270 LOGGER.debug("Generating struct: {0}".format(struct.name))
2271 f.write(struct.definition(align=True))
2273 for func in self.registry.funcs.values():
2274 if not func.is_required():
2275 LOGGER.debug("Skipping PFN definition for: {0}".format(func.name))
2278 f.write("typedef {0};\n".format(func.pfn(prefix="PFN", call_conv="VKAPI_PTR")))
2281 f.write("#ifndef VK_NO_PROTOTYPES\n")
2282 for func in self.registry.funcs.values():
2283 if not func.is_required():
2284 LOGGER.debug("Skipping API definition for: {0}".format(func.name))
2287 LOGGER.debug("Generating API definition for: {0}".format(func.name))
2288 f.write("{0};\n".format(func.prototype(call_conv="VKAPI_CALL")))
2289 f.write("#endif /* VK_NO_PROTOTYPES */\n\n")
2291 f.write("#endif /* __WINE_VULKAN_H */\n")
2293 def generate_vulkan_driver_h(self, f):
2294 self._generate_copyright(f)
2295 f.write("#ifndef __WINE_VULKAN_DRIVER_H\n")
2296 f.write("#define __WINE_VULKAN_DRIVER_H\n\n")
2298 f.write("/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */\n")
2299 f.write("#define WINE_VULKAN_DRIVER_VERSION {0}\n\n".format(DRIVER_VERSION))
2301 f.write("struct vulkan_funcs\n{\n")
2302 f.write(" /* Vulkan global functions. These are the only calls at this point a graphics driver\n")
2303 f.write(" * needs to provide. Other function calls will be provided indirectly by dispatch\n")
2304 f.write(" * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice.\n")
2307 for vk_func in self.registry.funcs.values():
2308 if not vk_func.is_driver_func():
2312 # Avoid PFN_vkVoidFunction in driver interface as Vulkan likes to put calling convention
2313 # stuff in there. For simplicity substitute with "void *".
2314 pfn = pfn.replace("PFN_vkVoidFunction", "void *")
2315 f.write(" {0};\n".format(pfn))
2318 f.write("extern const struct vulkan_funcs * CDECL __wine_get_vulkan_driver(HDC hdc, UINT version);\n\n")
2320 f.write("static inline void *get_vulkan_driver_device_proc_addr(\n")
2321 f.write(" const struct vulkan_funcs *vulkan_funcs, const char *name)\n{\n")
2322 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
2323 f.write(" name += 2;\n\n")
2324 for vk_func in self.registry.funcs.values():
2325 if vk_func.is_driver_func() and vk_func.is_device_func():
2326 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
2327 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
2329 f.write(" return NULL;\n}\n\n")
2331 f.write("static inline void *get_vulkan_driver_instance_proc_addr(\n")
2332 f.write(" const struct vulkan_funcs *vulkan_funcs, VkInstance instance, const char *name)\n{\n")
2333 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
2334 f.write(" name += 2;\n\n")
2335 for vk_func in self.registry.funcs.values():
2336 if vk_func.is_driver_func() and vk_func.is_global_func() and vk_func.name != "vkGetInstanceProcAddr":
2337 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
2338 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
2340 f.write(" if (!instance) return NULL;\n\n")
2341 for vk_func in self.registry.funcs.values():
2342 if vk_func.is_driver_func() and vk_func.is_instance_func():
2343 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
2344 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
2346 f.write(" name -= 2;\n\n")
2347 f.write(" return get_vulkan_driver_device_proc_addr(vulkan_funcs, name);\n}\n\n")
2349 f.write("#endif /* __WINE_VULKAN_DRIVER_H */\n")
2351 def generate_vulkan_spec(self, f):
2352 self._generate_copyright(f, spec_file=True)
2353 f.write("@ stdcall -private vk_icdGetInstanceProcAddr(ptr str) wine_vk_icdGetInstanceProcAddr\n")
2354 f.write("@ stdcall -private vk_icdNegotiateLoaderICDInterfaceVersion(ptr) wine_vk_icdNegotiateLoaderICDInterfaceVersion\n")
2355 f.write("@ cdecl -norelay native_vkGetInstanceProcAddrWINE(ptr str)\n")
2357 # Export symbols for all Vulkan Core functions.
2358 for func in self.registry.funcs.values():
2359 if not func.is_core_func():
2362 # We support all Core functions except for VK_KHR_display* APIs.
2363 # Create stubs for unsupported Core functions.
2364 if func.is_required():
2365 f.write(func.spec(prefix="wine_"))
2367 f.write("@ stub {0}\n".format(func.name))
2369 def generate_vulkan_loader_spec(self, f):
2370 self._generate_copyright(f, spec_file=True)
2372 # Export symbols for all Vulkan Core functions.
2373 for func in self.registry.funcs.values():
2374 if not func.is_core_func():
2377 # We support all Core functions except for VK_KHR_display* APIs.
2378 # Create stubs for unsupported Core functions.
2379 if func.is_required():
2380 f.write(func.spec(symbol="winevulkan.wine_" + func.name))
2382 f.write("@ stub {0}\n".format(func.name))
2385 class VkRegistry(object):
2386 def __init__(self, reg_filename):
2387 # Used for storage of type information.
2388 self.base_types = None
2389 self.bitmasks = None
2393 self.funcpointers = None
2397 # We aggregate all types in here for cross-referencing.
2401 self.version_regex = re.compile(
2410 # Overall strategy for parsing the registry is to first
2411 # parse all type / function definitions. Then parse
2412 # features and extensions to decide which types / functions
2413 # to actually 'pull in' for code generation. For each type or
2414 # function call we want we set a member 'required' to True.
2415 tree = ET.parse(reg_filename)
2416 root = tree.getroot()
2417 self._parse_enums(root)
2418 self._parse_types(root)
2419 self._parse_commands(root)
2421 # Pull in any required types and functions.
2422 self._parse_features(root)
2423 self._parse_extensions(root)
2425 self.copyright = root.find('./comment').text
2427 def _is_feature_supported(self, feature):
2428 version = self.version_regex.match(feature)
2432 version = tuple(map(int, version.group('major', 'minor')))
2433 return version <= WINE_VK_VERSION
2435 def _is_extension_supported(self, extension):
2436 # We disable some extensions as either we haven't implemented
2437 # support yet or because they are for platforms other than win32.
2438 return extension not in BLACKLISTED_EXTENSIONS
2440 def _mark_command_required(self, command):
2441 """ Helper function to mark a certain command and the datatypes it needs as required."""
2442 def mark_bitmask_dependencies(bitmask, types):
2443 if bitmask.requires is not None:
2444 types[bitmask.requires]["data"].required = True
2446 def mark_funcpointer_dependencies(fp, types):
2447 for m in fp.members:
2448 type_info = types[m.type]
2450 # Complex types have a matching definition e.g. VkStruct.
2451 # Not needed for base types such as uint32_t.
2452 if "data" in type_info:
2453 types[m.type]["data"].required = True
2455 def mark_struct_dependencies(struct, types):
2457 type_info = types[m.type]
2459 # Complex types have a matching definition e.g. VkStruct.
2460 # Not needed for base types such as uint32_t.
2461 if "data" in type_info:
2462 types[m.type]["data"].required = True
2464 if type_info["category"] == "struct":
2466 mark_struct_dependencies(type_info["data"], types)
2467 elif type_info["category"] == "funcpointer":
2468 mark_funcpointer_dependencies(type_info["data"], types)
2469 elif type_info["category"] == "bitmask":
2470 mark_bitmask_dependencies(type_info["data"], types)
2472 func = self.funcs[command]
2473 func.required = True
2475 # Pull in return type
2476 if func.type != "void":
2477 self.types[func.type]["data"].required = True
2479 # Analyze parameter dependencies and pull in any type needed.
2480 for p in func.params:
2481 type_info = self.types[p.type]
2483 # Check if we are dealing with a complex type e.g. VkEnum, VkStruct and others.
2484 if "data" not in type_info:
2487 # Mark the complex type as required.
2488 type_info["data"].required = True
2489 if type_info["category"] == "struct":
2490 struct = type_info["data"]
2491 mark_struct_dependencies(struct, self.types)
2494 func.alias.required = True
2496 def _parse_commands(self, root):
2497 """ Parse command section containing the Vulkan function calls. """
2499 commands = root.findall("./commands/")
2501 # As of Vulkan 1.1, various extensions got promoted to Core.
2502 # The old commands (e.g. KHR) are available for backwards compatibility
2503 # and are marked in vk.xml as 'alias' to the non-extension type.
2504 # The registry likes to avoid data duplication, so parameters and other
2505 # metadata need to be looked up from the Core command.
2506 # We parse the alias commands in a second pass.
2508 for command in commands:
2509 alias_name = command.attrib.get("alias")
2511 alias_commands.append(command)
2514 func = VkFunction.from_xml(command, self.types)
2515 funcs[func.name] = func
2517 for command in alias_commands:
2518 alias_name = command.attrib.get("alias")
2519 alias = funcs[alias_name]
2520 func = VkFunction.from_alias(command, alias)
2521 funcs[func.name] = func
2523 # To make life easy for the code generation, separate all function
2524 # calls out in the 3 types of Vulkan functions: device, global and instance.
2528 for func in funcs.values():
2529 if func.is_device_func():
2530 device_funcs.append(func)
2531 elif func.is_global_func():
2532 global_funcs.append(func)
2534 instance_funcs.append(func)
2536 # Sort function lists by name and store them.
2537 self.device_funcs = sorted(device_funcs, key=lambda func: func.name)
2538 self.global_funcs = sorted(global_funcs, key=lambda func: func.name)
2539 self.instance_funcs = sorted(instance_funcs, key=lambda func: func.name)
2541 # The funcs dictionary is used as a convenient way to lookup function
2542 # calls when needed e.g. to adjust member variables.
2543 self.funcs = OrderedDict(sorted(funcs.items()))
2545 def _parse_enums(self, root):
2546 """ Parse enums section or better described as constants section. """
2549 for enum in root.findall("./enums"):
2550 name = enum.attrib.get("name")
2551 _type = enum.attrib.get("type")
2553 if _type in ("enum", "bitmask"):
2554 enums[name] = VkEnum.from_xml(enum)
2556 # If no type is set, we are dealing with API constants.
2557 for value in enum.findall("enum"):
2558 # If enum is an alias, set the value to the alias name.
2559 # E.g. VK_LUID_SIZE_KHR is an alias to VK_LUID_SIZE.
2560 alias = value.attrib.get("alias")
2562 self.consts.append(VkConstant(value.attrib.get("name"), alias))
2564 self.consts.append(VkConstant(value.attrib.get("name"), value.attrib.get("value")))
2566 self.enums = OrderedDict(sorted(enums.items()))
2568 def _process_require_enum(self, enum_elem, ext=None):
2569 if "extends" in enum_elem.keys():
2570 enum = self.types[enum_elem.attrib["extends"]]["data"]
2572 if "bitpos" in enum_elem.keys():
2573 # We need to add an extra value to an existing enum type.
2574 # E.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG to VkFormatFeatureFlagBits.
2575 enum.add(VkEnumValue(enum_elem.attrib["name"], 1 << int(enum_elem.attrib["bitpos"]), hex=True))
2577 elif "offset" in enum_elem.keys():
2578 # Extensions promoted to Core, have the extension number as part
2579 # of the enum value. Else retrieve from the extension tag.
2580 if enum_elem.attrib.get("extnumber"):
2581 ext_number = int(enum_elem.attrib.get("extnumber"))
2583 ext_number = int(ext.attrib["number"])
2584 offset = int(enum_elem.attrib["offset"])
2585 value = EXT_BASE + (ext_number - 1) * EXT_BLOCK_SIZE + offset
2587 # Deal with negative values.
2588 direction = enum_elem.attrib.get("dir")
2589 if direction is not None:
2592 enum.add(VkEnumValue(enum_elem.attrib["name"], value))
2594 elif "value" in enum_elem.keys():
2595 enum.add(VkEnumValue(enum_elem.attrib["name"], int(enum_elem.attrib["value"])))
2597 elif "value" in enum_elem.keys():
2598 self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["value"]))
2600 def _parse_extensions(self, root):
2601 """ Parse extensions section and pull in any types and commands for this extensioin. """
2603 exts = root.findall("./extensions/extension")
2605 ext_name = ext.attrib["name"]
2607 # Set extension name on any functions calls part of this extension as we
2608 # were not aware of the name during initial parsing.
2609 commands = ext.findall("require/command")
2610 for command in commands:
2611 cmd_name = command.attrib["name"]
2612 self.funcs[cmd_name].extensions.append(ext_name)
2614 # Some extensions are not ready or have numbers reserved as a place holder.
2615 if ext.attrib["supported"] == "disabled":
2616 LOGGER.debug("Skipping disabled extension: {0}".format(ext_name))
2619 # Disable highly experimental extensions as the APIs are unstable and can
2620 # change between minor Vulkan revisions until API is final and becomes KHR
2622 if "KHX" in ext_name or "NVX" in ext_name:
2623 LOGGER.debug("Skipping experimental extension: {0}".format(ext_name))
2626 platform = ext.attrib.get("platform")
2627 if platform and platform != "win32":
2628 LOGGER.debug("Skipping extensions {0} for platform {1}".format(ext_name, platform))
2631 if not self._is_extension_supported(ext_name):
2632 LOGGER.debug("Skipping blacklisted extension: {0}".format(ext_name))
2634 elif "requires" in ext.attrib:
2635 # Check if this extension builds on top of another blacklisted
2637 requires = ext.attrib["requires"].split(",")
2638 if len(set(requires).intersection(BLACKLISTED_EXTENSIONS)) > 0:
2641 LOGGER.debug("Loading extension: {0}".format(ext_name))
2643 # Extensions can define one or more require sections each requiring
2644 # different features (e.g. Vulkan 1.1). Parse each require section
2645 # separately, so we can skip sections we don't want.
2646 for require in ext.findall("require"):
2647 # Extensions can add enum values to Core / extension enums, so add these.
2648 for enum_elem in require.findall("enum"):
2649 self._process_require_enum(enum_elem, ext)
2651 for t in require.findall("type"):
2652 type_info = self.types[t.attrib["name"]]["data"]
2653 if type_info.is_alias():
2654 type_info = type_info.alias
2655 type_info.required = True
2657 feature = require.attrib.get("feature")
2658 if feature and not self._is_feature_supported(feature):
2661 required_extension = require.attrib.get("extension")
2662 if required_extension and not self._is_extension_supported(required_extension):
2665 # Pull in any commands we need. We infer types to pull in from the command
2667 for command in require.findall("command"):
2668 cmd_name = command.attrib["name"]
2669 self._mark_command_required(cmd_name)
2671 # Store a list with extensions.
2672 ext_info = {"name" : ext_name, "type" : ext.attrib["type"]}
2673 extensions.append(ext_info)
2675 # Sort in alphabetical order.
2676 self.extensions = sorted(extensions, key=lambda ext: ext["name"])
2678 def _parse_features(self, root):
2679 """ Parse the feature section, which describes Core commands and types needed. """
2681 for feature in root.findall("./feature"):
2682 feature_name = feature.attrib["name"]
2683 for require in feature.findall("require"):
2684 LOGGER.info("Including features for {0}".format(require.attrib.get("comment")))
2686 if tag.tag == "comment":
2688 elif tag.tag == "command":
2689 if not self._is_feature_supported(feature_name):
2691 name = tag.attrib["name"]
2692 self._mark_command_required(name)
2693 elif tag.tag == "enum":
2694 self._process_require_enum(tag)
2695 elif tag.tag == "type":
2696 name = tag.attrib["name"]
2698 # Skip pull in for vk_platform.h for now.
2699 if name == "vk_platform":
2702 type_info = self.types[name]
2703 type_info["data"].required = True
2705 def _parse_types(self, root):
2706 """ Parse types section, which contains all data types e.g. structs, typedefs etcetera. """
2707 types = root.findall("./types/type")
2719 type_info["category"] = t.attrib.get("category", None)
2721 # We parse aliases in a second pass when we know more.
2722 alias = t.attrib.get("alias")
2724 LOGGER.debug("Alias found: {0}".format(alias))
2725 alias_types.append(t)
2728 if type_info["category"] in ["include"]:
2731 if type_info["category"] == "basetype":
2732 name = t.find("name").text
2733 _type = t.find("type").text
2734 basetype = VkBaseType(name, _type)
2735 base_types.append(basetype)
2736 type_info["data"] = basetype
2738 if type_info["category"] == "bitmask":
2739 name = t.find("name").text
2740 _type = t.find("type").text
2742 # Most bitmasks have a requires attribute used to pull in
2743 # required '*FlagBits" enum.
2744 requires = t.attrib.get("requires")
2745 bitmask = VkBaseType(name, _type, requires=requires)
2746 bitmasks.append(bitmask)
2747 type_info["data"] = bitmask
2749 if type_info["category"] == "define":
2750 define = VkDefine.from_xml(t)
2751 defines.append(define)
2752 type_info["data"] = define
2754 if type_info["category"] == "enum":
2755 name = t.attrib.get("name")
2756 # The type section only contains enum names, not the actual definition.
2757 # Since we already parsed the enum before, just link it in.
2759 type_info["data"] = self.enums[name]
2760 except KeyError as e:
2761 # Not all enums seem to be defined yet, typically that's for
2762 # ones ending in 'FlagBits' where future extensions may add
2764 type_info["data"] = None
2766 if type_info["category"] == "funcpointer":
2767 funcpointer = VkFunctionPointer.from_xml(t)
2768 funcpointers.append(funcpointer)
2769 type_info["data"] = funcpointer
2771 if type_info["category"] == "handle":
2772 handle = VkHandle.from_xml(t)
2773 handles.append(handle)
2774 type_info["data"] = handle
2776 if type_info["category"] in ["struct", "union"]:
2777 # We store unions among structs as some structs depend
2778 # on unions. The types are very similar in parsing and
2779 # generation anyway. The official Vulkan scripts use
2780 # a similar kind of hack.
2781 struct = VkStruct.from_xml(t)
2782 structs.append(struct)
2783 type_info["data"] = struct
2785 # Name is in general within a name tag else it is an optional
2786 # attribute on the type tag.
2787 name_elem = t.find("name")
2788 if name_elem is not None:
2789 type_info["name"] = name_elem.text
2791 type_info["name"] = t.attrib.get("name", None)
2793 # Store all type data in a shared dictionary, so we can easily
2794 # look up information for a given type. There are no duplicate
2796 self.types[type_info["name"]] = type_info
2798 # Second pass for alias types, so we can retrieve all data from
2799 # the aliased object.
2800 for t in alias_types:
2802 type_info["category"] = t.attrib.get("category")
2803 type_info["name"] = t.attrib.get("name")
2805 alias = t.attrib.get("alias")
2807 if type_info["category"] == "bitmask":
2808 bitmask = VkBaseType(type_info["name"], alias, alias=self.types[alias]["data"])
2809 bitmasks.append(bitmask)
2810 type_info["data"] = bitmask
2812 if type_info["category"] == "enum":
2813 enum = VkEnum.from_alias(t, self.types[alias]["data"])
2814 type_info["data"] = enum
2815 self.enums[enum.name] = enum
2817 if type_info["category"] == "handle":
2818 handle = VkHandle.from_alias(t, self.types[alias]["data"])
2819 handles.append(handle)
2820 type_info["data"] = handle
2822 if type_info["category"] == "struct":
2823 struct = VkStruct.from_alias(t, self.types[alias]["data"])
2824 structs.append(struct)
2825 type_info["data"] = struct
2827 self.types[type_info["name"]] = type_info
2829 # We need detailed type information during code generation
2830 # on structs for alignment reasons. Unfortunately structs
2831 # are parsed among other types, so there is no guarantee
2832 # that any types needed have been parsed already, so set
2834 for struct in structs:
2835 struct.set_type_info(self.types)
2837 for structextend in struct.structextends:
2838 s = self.types[structextend]["data"]
2839 s.struct_extensions.append(struct)
2841 # Guarantee everything is sorted, so code generation doesn't have
2842 # to deal with this.
2843 self.base_types = sorted(base_types, key=lambda base_type: base_type.name)
2844 self.bitmasks = sorted(bitmasks, key=lambda bitmask: bitmask.name)
2845 self.defines = defines
2846 self.enums = OrderedDict(sorted(self.enums.items()))
2847 self.funcpointers = sorted(funcpointers, key=lambda fp: fp.name)
2848 self.handles = sorted(handles, key=lambda handle: handle.name)
2849 self.structs = sorted(structs, key=lambda struct: struct.name)
2851 def set_working_directory():
2852 path = os.path.abspath(__file__)
2853 path = os.path.dirname(path)
2856 def download_vk_xml(filename):
2857 url = "https://raw.github.com/KhronosGroup/Vulkan-Docs/v{0}/xml/vk.xml".format(VK_XML_VERSION)
2858 if not os.path.isfile(filename):
2859 urllib.request.urlretrieve(url, filename)
2862 parser = argparse.ArgumentParser()
2863 parser.add_argument("-v", "--verbose", action="count", default=0, help="increase output verbosity")
2865 args = parser.parse_args()
2866 if args.verbose == 0:
2867 LOGGER.setLevel(logging.WARNING)
2868 elif args.verbose == 1:
2869 LOGGER.setLevel(logging.INFO)
2871 LOGGER.setLevel(logging.DEBUG)
2873 set_working_directory()
2875 vk_xml = "vk-{0}.xml".format(VK_XML_VERSION)
2876 download_vk_xml(vk_xml)
2877 registry = VkRegistry(vk_xml)
2878 generator = VkGenerator(registry)
2880 with open(WINE_VULKAN_H, "w") as f:
2881 generator.generate_vulkan_h(f)
2883 with open(WINE_VULKAN_DRIVER_H, "w") as f:
2884 generator.generate_vulkan_driver_h(f)
2886 with open(WINE_VULKAN_THUNKS_H, "w") as f:
2887 generator.generate_thunks_h(f, "wine_")
2889 with open(WINE_VULKAN_THUNKS_C, "w") as f:
2890 generator.generate_thunks_c(f, "wine_")
2892 with open(WINE_VULKAN_SPEC, "w") as f:
2893 generator.generate_vulkan_spec(f)
2895 with open(WINE_VULKAN_LOADER_SPEC, "w") as f:
2896 generator.generate_vulkan_loader_spec(f)
2898 if __name__ == "__main__":