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.2.170"
68 WINE_VK_VERSION = (1, 2)
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_JSON = "winevulkan.json"
75 WINE_VULKAN_SPEC = "winevulkan.spec"
76 WINE_VULKAN_THUNKS_C = "vulkan_thunks.c"
77 WINE_VULKAN_THUNKS_H = "vulkan_thunks.h"
79 # Extension enum values start at a certain offset (EXT_BASE).
80 # Relative to the offset each extension has a block (EXT_BLOCK_SIZE)
82 # Start for a given extension is:
83 # EXT_BASE + (extension_number-1) * EXT_BLOCK_SIZE
87 UNSUPPORTED_EXTENSIONS = [
89 "VK_EXT_headless_surface", # Needs WSI work.
90 "VK_KHR_display", # Needs WSI work.
91 "VK_KHR_surface_protected_capabilities",
94 "VK_AMD_display_native_hdr",
95 "VK_EXT_full_screen_exclusive",
96 "VK_EXT_hdr_metadata", # Needs WSI work.
97 "VK_EXT_pipeline_creation_feedback",
98 "VK_GOOGLE_display_timing",
99 "VK_KHR_external_fence_win32",
100 "VK_KHR_external_memory_win32",
101 "VK_KHR_external_semaphore_win32",
102 # Relates to external_semaphore and needs type conversions in bitflags.
103 "VK_KHR_shared_presentable_image", # Needs WSI work.
104 "VK_KHR_win32_keyed_mutex",
106 # Extensions for other platforms
107 "VK_EXT_external_memory_dma_buf",
108 "VK_EXT_image_drm_format_modifier",
109 "VK_KHR_external_fence_fd",
110 "VK_KHR_external_memory_fd",
111 "VK_KHR_external_semaphore_fd",
113 # Extensions which require callback handling
114 "VK_EXT_device_memory_report",
116 # Deprecated extensions
117 "VK_NV_external_memory_capabilities",
118 "VK_NV_external_memory_win32",
121 # The Vulkan loader provides entry-points for core functionality and important
122 # extensions. Based on vulkan-1.def this amounts to WSI extensions on 1.0.51.
125 "VK_KHR_display_swapchain",
126 "VK_KHR_get_surface_capabilities2",
129 "VK_KHR_win32_surface",
132 # Functions part of our winevulkan graphics driver interface.
133 # DRIVER_VERSION should be bumped on any change to driver interface
134 # in FUNCTION_OVERRIDES
137 # Table of functions for which we have a special implementation.
138 # These are regular device / instance functions for which we need
139 # to do more work compared to a regular thunk or because they are
140 # part of the driver interface.
141 # - dispatch set whether we need a function pointer in the device
142 # / instance dispatch table.
143 # - driver sets whether the API is part of the driver interface.
144 # - thunk sets whether to create a thunk in vulkan_thunks.c.
145 FUNCTION_OVERRIDES = {
147 "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : False},
148 "vkEnumerateInstanceExtensionProperties" : {"dispatch" : False, "driver" : True, "thunk" : False},
149 "vkEnumerateInstanceVersion": {"dispatch" : False, "driver" : False, "thunk" : False},
150 "vkGetInstanceProcAddr": {"dispatch" : False, "driver" : True, "thunk" : False},
153 "vkCreateDevice" : {"dispatch" : True, "driver" : False, "thunk" : False},
154 "vkDestroyInstance" : {"dispatch" : False, "driver" : True, "thunk" : False },
155 "vkEnumerateDeviceExtensionProperties" : {"dispatch" : True, "driver" : False, "thunk" : False},
156 "vkEnumerateDeviceLayerProperties": {"dispatch": True, "driver": False, "thunk": False},
157 "vkEnumeratePhysicalDeviceGroups" : {"dispatch" : True, "driver" : False, "thunk" : False},
158 "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : False},
159 "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : False, "driver" : False, "thunk" : False},
160 "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False, "driver" : False, "thunk" : False},
161 "vkGetPhysicalDeviceExternalSemaphoreProperties" : {"dispatch" : False, "driver" : False, "thunk" : False},
162 "vkGetPhysicalDeviceImageFormatProperties2" : {"dispatch" : True, "driver" : False, "thunk" : True, "private_thunk" : True},
163 "vkGetPhysicalDeviceProperties2" : {"dispatch" : True, "driver" : False, "thunk" : True, "private_thunk" : True},
164 "vkGetPhysicalDeviceProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : True, "private_thunk" : True},
167 "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : False},
168 "vkCmdExecuteCommands" : {"dispatch" : True, "driver" : False, "thunk" : False},
169 "vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : False},
170 "vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : False},
171 "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : False},
172 "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : False},
173 "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : False},
174 "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : False},
175 "vkGetDeviceQueue2" : {"dispatch": True, "driver" : False, "thunk" : False},
176 "vkQueueSubmit" : {"dispatch": True, "driver" : False, "thunk" : False},
179 "vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
180 "vkGetPhysicalDeviceSurfaceSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
181 "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" : {"dispatch" : True, "driver" : True, "thunk" : False, "private_thunk" : True},
182 "vkGetPhysicalDeviceSurfaceFormatsKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
183 "vkGetPhysicalDeviceSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
185 # VK_KHR_get_surface_capabilities2
186 "vkGetPhysicalDeviceSurfaceCapabilities2KHR" : {"dispatch" : True, "driver" : True, "thunk" : False, "private_thunk" : True},
187 "vkGetPhysicalDeviceSurfaceFormats2KHR" : {"dispatch" : True, "driver" : True, "thunk" : False, "private_thunk" : True},
189 # VK_KHR_win32_surface
190 "vkCreateWin32SurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
191 "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
194 "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : False, "private_thunk" : True},
195 "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
196 "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : True},
197 "vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : True},
199 # VK_KHR_external_fence_capabilities
200 "vkGetPhysicalDeviceExternalFencePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : False},
202 # VK_KHR_external_memory_capabilities
203 "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : False},
204 "vkGetPhysicalDeviceImageFormatProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : True, "private_thunk" : True},
206 # VK_KHR_external_semaphore_capabilities
207 "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : False},
209 # VK_KHR_device_group_creation
210 "vkEnumeratePhysicalDeviceGroupsKHR" : {"dispatch" : True, "driver" : False, "thunk" : False},
212 # VK_KHR_device_group
213 "vkGetDeviceGroupSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
214 "vkGetPhysicalDevicePresentRectanglesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
216 # VK_EXT_private_data
217 "vkGetPrivateDataEXT" : {"dispatch": True, "driver" : False, "thunk" : False},
218 "vkSetPrivateDataEXT" : {"dispatch": True, "driver" : False, "thunk" : False},
220 # VK_EXT_calibrated_timestamps
221 "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : False},
222 "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : False},
225 "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : False},
226 "vkDestroyDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : False},
227 "vkSubmitDebugUtilsMessageEXT" : {"dispatch": True, "driver" : False, "thunk" : True, "private_thunk" : True},
228 "vkSetDebugUtilsObjectTagEXT" : {"dispatch": True, "driver" : False, "thunk" : True, "private_thunk" : True},
229 "vkSetDebugUtilsObjectNameEXT" : {"dispatch": True, "driver" : False, "thunk" : True, "private_thunk" : True},
231 # VK_EXT_debug_report
232 "vkCreateDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : False},
233 "vkDestroyDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : False},
234 "vkDebugReportMessageEXT" : {"dispatch": True, "driver" : False, "thunk" : False},
236 # VK_EXT_debug_marker
237 "vkDebugMarkerSetObjectNameEXT" : {"dispatch": True, "driver" : False, "thunk" : True, "private_thunk" : True},
238 "vkDebugMarkerSetObjectTagEXT" : {"dispatch": True, "driver" : False, "thunk" : True, "private_thunk" : True},
241 STRUCT_CHAIN_CONVERSIONS = [
242 "VkDeviceCreateInfo",
243 "VkInstanceCreateInfo",
247 class Direction(Enum):
248 """ Parameter direction: input, output, input_output. """
254 class VkBaseType(object):
255 def __init__(self, name, _type, alias=None, requires=None):
256 """ Vulkan base type class.
258 VkBaseType is mostly used by Vulkan to define its own
259 base types like VkFlags through typedef out of e.g. uint32_t.
262 name (:obj:'str'): Name of the base type.
263 _type (:obj:'str'): Underlying type
264 alias (bool): type is an alias or not.
265 requires (:obj:'str', optional): Other types required.
266 Often bitmask values pull in a *FlagBits type.
271 self.requires = requires
272 self.required = False
274 def definition(self):
275 # Definition is similar for alias or non-alias as type
276 # is already set to alias.
277 if not self.type is None:
278 return "typedef {0} {1};\n".format(self.type, self.name)
280 return "struct {0};\n".format(self.name)
283 return bool(self.alias)
286 class VkConstant(object):
287 def __init__(self, name, value):
291 def definition(self):
292 text = "#define {0} {1}\n".format(self.name, self.value)
296 class VkDefine(object):
297 def __init__(self, name, value):
302 def from_xml(define):
303 name_elem = define.find("name")
305 if name_elem is None:
306 # <type category="define" name="some_name">some_value</type>
307 # At the time of writing there is only 1 define of this category
308 # 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'.
309 name = define.attrib.get("name")
311 # We override behavior of VK_DEFINE_NON_DISPATCHABLE handle as the default
312 # definition various between 64-bit (uses pointers) and 32-bit (uses uint64_t).
313 # This complicates TRACEs in the thunks, so just use uint64_t.
314 if name == "VK_DEFINE_NON_DISPATCHABLE_HANDLE":
315 value = "#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;"
318 return VkDefine(name, value)
320 # With a name element the structure is like:
321 # <type category="define"><name>some_name</name>some_value</type>
322 name = name_elem.text
324 # Perform minimal parsing for Vulkan constants, which we don't need, but are referenced
325 # elsewhere in vk.xml.
326 # - VK_API_VERSION is a messy, deprecated constant and we don't want generate code for it.
327 # - AHardwareBuffer/ANativeWindow are forward declarations for Android types, which leaked
328 # into the define region.
329 if name in ["VK_API_VERSION", "AHardwareBuffer", "ANativeWindow", "CAMetalLayer"]:
330 return VkDefine(name, None)
332 # The body of the define is basically unstructured C code. It is not meant for easy parsing.
333 # Some lines contain deprecated values or comments, which we try to filter out.
335 for line in define.text.splitlines():
336 # Skip comments or deprecated values.
343 if child.tail is not None:
344 # Split comments for VK_API_VERSION_1_0 / VK_API_VERSION_1_1
345 if "//" in child.tail:
346 value += child.tail.split("//")[0]
350 return VkDefine(name, value.rstrip(' '))
352 def definition(self):
353 if self.value is None:
356 # Nothing to do as the value was already put in the right form during parsing.
357 return "{0}\n".format(self.value)
360 class VkEnum(object):
361 def __init__(self, name, bitwidth, alias=None):
362 if not bitwidth in [32, 64]:
363 LOGGER.error("unknown bitwidth {0} for {1}".format(bitwidth, name))
365 self.bitwidth = bitwidth
366 self.values = [] if alias == None else alias.values
367 self.required = False
372 def from_alias(enum, alias):
373 name = enum.attrib.get("name")
374 aliasee = VkEnum(name, alias.bitwidth, alias=alias)
376 alias.add_aliased_by(aliasee)
381 name = enum.attrib.get("name")
382 bitwidth = int(enum.attrib.get("bitwidth", "32"))
383 result = VkEnum(name, bitwidth)
385 for v in enum.findall("enum"):
386 value_name = v.attrib.get("name")
387 # Value is either a value or a bitpos, only one can exist.
388 value = v.attrib.get("value")
389 alias_name = v.attrib.get("alias")
391 result.create_alias(value_name, alias_name)
393 result.create_value(value_name, value)
396 result.create_bitpos(value_name, int(v.attrib.get("bitpos")))
399 # vulkan.h contains a *_MAX_ENUM value set to 32-bit at the time of writing,
400 # which is to prepare for extensions as they can add values and hence affect
401 # the size definition.
402 max_name = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2', name).upper() + "_MAX_ENUM"
403 result.create_value(max_name, "0x7fffffff")
407 def create_alias(self, name, alias_name):
408 """ Create an aliased value for this enum """
409 # Older GCC versions need a literal to initialize a static const uint64_t
410 # which is what we use for 64bit bitmasks.
411 if self.bitwidth == 64:
412 alias = next(x for x in self.values if x.name == alias_name)
413 self.add(VkEnumValue(name, self.bitwidth, value=alias.value, hex=alias.hex, alias=alias_name))
415 self.add(VkEnumValue(name, self.bitwidth, alias=alias_name))
417 def create_value(self, name, value):
418 """ Create a new value for this enum """
419 # Some values are in hex form. We want to preserve the hex representation
420 # at least when we convert back to a string. Internally we want to use int.
422 self.add(VkEnumValue(name, self.bitwidth, value=int(value, 0), hex=hex))
424 def create_bitpos(self, name, pos):
425 """ Create a new bitmask value for this enum """
426 self.add(VkEnumValue(name, self.bitwidth, value=(1 << pos), hex=True))
428 def add(self, value):
429 """ Add a value to enum. """
431 # Extensions can add new enum values. When an extension is promoted to Core
432 # the registry defines the value twice once for old extension and once for
433 # new Core features. Add the duplicate if it's explicitly marked as an
434 # alias, otherwise ignore it.
435 for v in self.values:
436 if not value.is_alias() and v.value == value.value:
437 LOGGER.debug("Adding duplicate enum value {0} to {1}".format(v, self.name))
439 # Avoid adding duplicate aliases multiple times
440 if not any(x.name == value.name for x in self.values):
441 self.values.append(value)
443 def definition(self):
447 default_value = 0x7ffffffe if self.bitwidth == 32 else 0xfffffffffffffffe
449 # Print values sorted, values can have been added in a random order.
450 values = sorted(self.values, key=lambda value: value.value if value.value is not None else default_value)
452 if self.bitwidth == 32:
453 text = "typedef enum {0}\n{{\n".format(self.name)
455 text += " {0},\n".format(value.definition())
456 text += "}} {0};\n".format(self.name)
457 elif self.bitwidth == 64:
458 text = "typedef VkFlags64 {0};\n\n".format(self.name)
460 text += "static const {0} {1};\n".format(self.name, value.definition())
462 for aliasee in self.aliased_by:
463 text += "typedef {0} {1};\n".format(self.name, aliasee.name)
469 return bool(self.alias)
471 def add_aliased_by(self, aliasee):
472 self.aliased_by.append(aliasee)
475 class VkEnumValue(object):
476 def __init__(self, name, bitwidth, value=None, hex=False, alias=None):
478 self.bitwidth = bitwidth
484 postfix = "ull" if self.bitwidth == 64 else ""
485 if self.is_alias() and self.value == None:
486 return "{0}={1}".format(self.name, self.alias)
487 return "{0}={1}{2}".format(self.name, self.value, postfix)
489 def definition(self):
490 """ Convert to text definition e.g. VK_FOO = 1 """
491 postfix = "ull" if self.bitwidth == 64 else ""
492 if self.is_alias() and self.value == None:
493 return "{0} = {1}".format(self.name, self.alias)
495 # Hex is commonly used for FlagBits and sometimes within
496 # a non-FlagBits enum for a bitmask value as well.
498 return "{0} = 0x{1:08x}{2}".format(self.name, self.value, postfix)
500 return "{0} = {1}{2}".format(self.name, self.value, postfix)
503 return self.alias is not None
506 class VkFunction(object):
507 def __init__(self, _type=None, name=None, params=[], extensions=[], alias=None):
514 # For some functions we need some extra metadata from FUNCTION_OVERRIDES.
515 func_info = FUNCTION_OVERRIDES.get(self.name, None)
516 self.dispatch = func_info["dispatch"] if func_info else True
517 self.driver = func_info["driver"] if func_info else False
518 self.thunk_needed = func_info["thunk"] if func_info else True
519 self.private_thunk = func_info["private_thunk"] if func_info and "private_thunk" in func_info else False
520 if self.private_thunk:
521 self.thunk_needed = True
523 # Required is set while parsing which APIs and types are required
524 # and is used by the code generation.
525 self.required = True if func_info else False
528 def from_alias(command, alias):
529 """ Create VkFunction from an alias command.
532 command: xml data for command
533 alias (VkFunction): function to use as a base for types / parameters.
538 func_name = command.attrib.get("name")
539 func_type = alias.type
540 params = alias.params
542 return VkFunction(_type=func_type, name=func_name, params=params, alias=alias)
545 def from_xml(command, types):
546 proto = command.find("proto")
547 func_name = proto.find("name").text
548 func_type = proto.find("type").text
551 for param in command.findall("param"):
552 vk_param = VkParam.from_xml(param, types)
553 params.append(vk_param)
555 return VkFunction(_type=func_type, name=func_name, params=params)
557 def get_conversions(self):
558 """ Get a list of conversion functions required for this function if any.
559 Parameters which are structures may require conversion between win32
560 and the host platform. This function returns a list of conversions
565 for param in self.params:
566 convs = param.get_conversions()
567 if convs is not None:
568 conversions.extend(convs)
573 return bool(self.alias)
575 def is_core_func(self):
576 """ Returns whether the function is a Vulkan core function.
577 Core functions are APIs defined by the Vulkan spec to be part of the
578 Core API as well as several KHR WSI extensions.
581 if not self.extensions:
584 return any(ext in self.extensions for ext in CORE_EXTENSIONS)
586 def is_device_func(self):
587 # If none of the other, it must be a device function
588 return not self.is_global_func() and not self.is_instance_func()
590 def is_driver_func(self):
591 """ Returns if function is part of Wine driver interface. """
594 def is_global_func(self):
595 # Treat vkGetInstanceProcAddr as a global function as it
596 # can operate with NULL for vkInstance.
597 if self.name == "vkGetInstanceProcAddr":
599 # Global functions are not passed a dispatchable object.
600 elif self.params[0].is_dispatchable():
604 def is_instance_func(self):
605 # Instance functions are passed VkInstance or VkPhysicalDevice.
606 if self.params[0].type in ["VkInstance", "VkPhysicalDevice"]:
610 def is_required(self):
613 def needs_conversion(self):
614 """ Check if the function needs any input/output type conversion.
615 Functions need input/output conversion if struct parameters have
616 alignment differences between Win32 and Linux 32-bit.
619 for p in self.params:
620 if p.needs_conversion():
621 LOGGER.debug("Parameter {0} to {1} requires conversion".format(p.name, self.name))
626 def needs_dispatch(self):
629 def needs_thunk(self):
630 return self.thunk_needed
632 def needs_private_thunk(self):
633 return self.private_thunk
635 def pfn(self, prefix="p", call_conv=None, conv=False):
636 """ Create function pointer. """
639 pfn = "{0} ({1} *{2}_{3})(".format(self.type, call_conv, prefix, self.name)
641 pfn = "{0} (*{1}_{2})(".format(self.type, prefix, self.name)
643 for i, param in enumerate(self.params):
645 pfn += param.const + " "
648 if conv and param.needs_conversion():
651 if param.is_pointer():
652 pfn += " " + param.pointer
654 if param.array_len is not None:
655 pfn += "[{0}]".format(param.array_len)
657 if i < len(self.params) - 1:
662 def prototype(self, call_conv=None, prefix=None, postfix=None):
663 """ Generate prototype for given function.
666 call_conv (str, optional): calling convention e.g. WINAPI
667 prefix (str, optional): prefix to append prior to function name e.g. vkFoo -> wine_vkFoo
668 postfix (str, optional): text to append after function name but prior to semicolon e.g. DECLSPEC_HIDDEN
671 proto = "{0}".format(self.type)
673 if call_conv is not None:
674 proto += " {0}".format(call_conv)
676 if prefix is not None:
677 proto += " {0}{1}(".format(prefix, self.name)
679 proto += " {0}(".format(self.name)
681 # Add all the parameters.
682 proto += ", ".join([p.definition() for p in self.params])
684 if postfix is not None:
685 proto += ") {0}".format(postfix)
694 if not self.needs_private_thunk():
695 body += " {0}".format(self.trace())
697 params = ", ".join([p.variable(conv=False) for p in self.params])
699 # Call the native Vulkan function.
700 if self.type == "void":
701 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
703 body += " return {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
707 def body_conversion(self):
710 # Declare a variable to hold the result for non-void functions.
711 if self.type != "void":
712 body += " {0} result;\n".format(self.type)
714 # Declare any tmp parameters for conversion.
715 for p in self.params:
716 if not p.needs_conversion():
719 if p.is_dynamic_array():
720 body += " {0}_host *{1}_host;\n".format(p.type, p.name)
722 body += " {0}_host {1}_host;\n".format(p.type, p.name)
724 if not self.needs_private_thunk():
725 body += " {0}\n".format(self.trace())
727 # Call any win_to_host conversion calls.
728 for p in self.params:
729 if not p.needs_input_conversion():
732 body += p.copy(Direction.INPUT)
734 # Build list of parameters containing converted and non-converted parameters.
735 # The param itself knows if conversion is needed and applies it when we set conv=True.
736 params = ", ".join([p.variable(conv=True) for p in self.params])
738 # Call the native Vulkan function.
739 if self.type == "void":
740 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
742 body += " result = {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
746 # Call any host_to_win conversion calls.
747 for p in self.params:
748 if not p.needs_output_conversion():
751 body += p.copy(Direction.OUTPUT)
753 # Perform any required cleanups. Most of these are for array functions.
754 for p in self.params:
755 if not p.needs_free():
760 # Finally return the result.
761 if self.type != "void":
762 body += " return result;\n"
766 def spec(self, prefix=None, symbol=None):
767 """ Generate spec file entry for this function.
770 prefix (str, optional): prefix to prepend to entry point name.
771 symbol (str, optional): allows overriding the name of the function implementing the entry point.
775 params = " ".join([p.spec() for p in self.params])
776 if prefix is not None:
777 spec += "@ stdcall -private {0}{1}({2})".format(prefix, self.name, params)
779 spec += "@ stdcall {0}({1})".format(self.name, params)
781 if symbol is not None:
787 def stub(self, call_conv=None, prefix=None):
788 stub = self.prototype(call_conv=call_conv, prefix=prefix)
790 stub += " {0}".format(self.trace(message="stub: ", trace_func="FIXME"))
792 if self.type == "VkResult":
793 stub += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
794 elif self.type == "VkBool32":
795 stub += " return VK_FALSE;\n"
796 elif self.type == "PFN_vkVoidFunction":
797 stub += " return NULL;\n"
802 def thunk(self, call_conv=None, prefix=None):
803 thunk = self.prototype(call_conv=call_conv, prefix=prefix)
806 if self.needs_conversion():
807 thunk += "#if defined(USE_STRUCT_CONVERSION)\n"
808 thunk += self.body_conversion()
818 def trace(self, message=None, trace_func=None):
819 """ Create a trace string including all parameters.
822 message (str, optional): text to print at start of trace message e.g. 'stub: '
823 trace_func (str, optional): used to override trace function e.g. FIXME, printf, etcetera.
825 if trace_func is not None:
826 trace = "{0}(\"".format(trace_func)
830 if message is not None:
833 # First loop is for all the format strings.
834 trace += ", ".join([p.format_string() for p in self.params])
837 # Second loop for parameter names and optional conversions.
838 for param in self.params:
839 if param.format_conv is not None:
840 trace += ", " + param.format_conv.format(param.name)
842 trace += ", {0}".format(param.name)
848 class VkFunctionPointer(object):
849 def __init__(self, _type, name, members, forward_decls):
851 self.members = members
853 self.required = False
854 self.forward_decls = forward_decls
857 def from_xml(funcpointer):
861 for t in funcpointer.findall("type"):
863 # <type>void</type>* pUserData,
864 # Parsing of the tail (anything past </type>) is tricky since there
865 # can be other data on the next line like: const <type>int</type>..
867 const = True if begin and "const" in begin else False
869 lines = t.tail.split(",\n")
870 if lines[0][0] == "*":
872 name = lines[0][1:].strip()
875 name = lines[0].strip()
877 # Filter out ); if it is contained.
878 name = name.partition(");")[0]
880 # If tail encompasses multiple lines, assign the second line to begin
883 begin = lines[1].strip()
887 members.append(VkMember(const=const, _type=_type, pointer=pointer, name=name))
889 _type = funcpointer.text
890 name = funcpointer.find("name").text
891 if "requires" in funcpointer.attrib:
892 forward_decls = funcpointer.attrib.get("requires").split(",")
895 return VkFunctionPointer(_type, name, members, forward_decls)
897 def definition(self):
899 # forward declare required structs
900 for decl in self.forward_decls:
901 text += "typedef struct {0} {0};\n".format(decl)
903 text += "{0} {1})(\n".format(self.type, self.name)
906 if len(self.members) > 0:
907 for m in self.members:
909 text += " " + m.definition()
912 text += ",\n " + m.definition()
914 # Just make the compiler happy by adding a void parameter.
922 class VkHandle(object):
923 def __init__(self, name, _type, parent, alias=None):
928 self.required = False
929 self.object_type = None
932 def from_alias(handle, alias):
933 name = handle.attrib.get("name")
934 return VkHandle(name, alias.type, alias.parent, alias=alias)
937 def from_xml(handle):
938 name = handle.find("name").text
939 _type = handle.find("type").text
940 parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice.
941 return VkHandle(name, _type, parent)
943 def dispatch_table(self):
944 if not self.is_dispatchable():
947 if self.parent is None:
948 # Should only happen for VkInstance
950 elif self.name == "VkDevice":
951 # VkDevice has VkInstance as a parent, but has its own dispatch table.
953 elif self.parent in ["VkInstance", "VkPhysicalDevice"]:
954 return "instance->funcs"
955 elif self.parent in ["VkDevice", "VkCommandPool"]:
956 return "device->funcs"
958 LOGGER.error("Unhandled dispatchable parent: {0}".format(self.parent))
960 def definition(self):
961 """ Generates handle definition e.g. VK_DEFINE_HANDLE(vkInstance) """
963 # Legacy types are typedef'ed to the new type if they are aliases.
965 return "typedef {0} {1};\n".format(self.alias.name, self.name)
967 return "{0}({1})\n".format(self.type, self.name)
970 return self.alias is not None
972 def is_dispatchable(self):
973 """ Some handles like VkInstance, VkDevice are dispatchable objects,
974 which means they contain a dispatch table of function pointers.
976 return self.type == "VK_DEFINE_HANDLE"
978 def is_required(self):
981 def native_handle(self, name):
982 """ Provide access to the native handle of a wrapped object. """
984 if self.name == "VkCommandPool":
985 return "wine_cmd_pool_from_handle({0})->command_pool".format(name)
986 if self.name == "VkDebugUtilsMessengerEXT":
987 return "wine_debug_utils_messenger_from_handle({0})->debug_messenger".format(name)
988 if self.name == "VkDebugReportCallbackEXT":
989 return "wine_debug_report_callback_from_handle({0})->debug_callback".format(name)
990 if self.name == "VkSurfaceKHR":
991 return "wine_surface_from_handle({0})->surface".format(name)
993 native_handle_name = None
995 if self.name == "VkCommandBuffer":
996 native_handle_name = "command_buffer"
997 if self.name == "VkDevice":
998 native_handle_name = "device"
999 if self.name == "VkInstance":
1000 native_handle_name = "instance"
1001 if self.name == "VkPhysicalDevice":
1002 native_handle_name = "phys_dev"
1003 if self.name == "VkQueue":
1004 native_handle_name = "queue"
1006 if native_handle_name:
1007 return "{0}->{1}".format(name, native_handle_name)
1009 if self.is_dispatchable():
1010 LOGGER.error("Unhandled native handle for: {0}".format(self.name))
1013 def driver_handle(self, name):
1014 """ Provide access to the handle that should be passed to the wine driver """
1016 if self.name == "VkSurfaceKHR":
1017 return "wine_surface_from_handle({0})->driver_surface".format(name)
1019 return self.native_handle(name)
1021 def is_wrapped(self):
1022 return self.native_handle("test") is not None
1024 class VkMember(object):
1025 def __init__(self, const=False, struct_fwd_decl=False,_type=None, pointer=None, name=None, array_len=None,
1026 dyn_array_len=None, optional=False, values=None):
1028 self.struct_fwd_decl = struct_fwd_decl
1030 self.pointer = pointer
1032 self.type_info = None
1033 self.array_len = array_len
1034 self.dyn_array_len = dyn_array_len
1035 self.optional = optional
1036 self.values = values
1038 def __eq__(self, other):
1039 """ Compare member based on name against a string.
1041 This method is for convenience by VkStruct, which holds a number of members and needs quick checking
1042 if certain members exist.
1045 return self.name == other
1048 return "{0} {1} {2} {3} {4} {5} {6}".format(self.const, self.struct_fwd_decl, self.type, self.pointer,
1049 self.name, self.array_len, self.dyn_array_len)
1052 def from_xml(member):
1053 """ Helper function for parsing a member tag within a struct or union. """
1055 name_elem = member.find("name")
1056 type_elem = member.find("type")
1059 struct_fwd_decl = False
1064 values = member.get("values")
1067 if "const" in member.text:
1070 # Some members contain forward declarations:
1071 # - VkBaseInstructure has a member "const struct VkBaseInStructure *pNext"
1072 # - VkWaylandSurfaceCreateInfoKHR has a member "struct wl_display *display"
1073 if "struct" in member.text:
1074 struct_fwd_decl = True
1076 if type_elem is not None:
1077 member_type = type_elem.text
1078 if type_elem.tail is not None:
1079 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1081 # Name of other member within, which stores the number of
1082 # elements pointed to be by this member.
1083 dyn_array_len = member.get("len")
1085 # Some members are optional, which is important for conversion code e.g. not dereference NULL pointer.
1086 optional = True if member.get("optional") else False
1088 # Usually we need to allocate memory for dynamic arrays. We need to do the same in a few other cases
1089 # like for VkCommandBufferBeginInfo.pInheritanceInfo. Just threat such cases as dynamic arrays of
1090 # size 1 to simplify code generation.
1091 if dyn_array_len is None and pointer is not None:
1094 # Some members are arrays, attempt to parse these. Formats include:
1095 # <member><type>char</type><name>extensionName</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
1096 # <member><type>uint32_t</type><name>foo</name>[4]</member>
1097 if name_elem.tail and name_elem.tail[0] == '[':
1098 LOGGER.debug("Found array type")
1099 enum_elem = member.find("enum")
1100 if enum_elem is not None:
1101 array_len = enum_elem.text
1103 # Remove brackets around length
1104 array_len = name_elem.tail.strip("[]")
1106 return VkMember(const=const, struct_fwd_decl=struct_fwd_decl, _type=member_type, pointer=pointer, name=name_elem.text,
1107 array_len=array_len, dyn_array_len=dyn_array_len, optional=optional, values=values)
1109 def copy(self, input, output, direction):
1110 """ Helper method for use by conversion logic to generate a C-code statement to copy this member. """
1112 if self.needs_conversion():
1113 if self.is_dynamic_array():
1114 if direction == Direction.OUTPUT:
1115 LOGGER.warn("TODO: implement copying of returnedonly dynamic array for {0}.{1}".format(self.type, self.name))
1117 # Array length is either a variable name (string) or an int.
1118 count = self.dyn_array_len if isinstance(self.dyn_array_len, int) else "{0}{1}".format(input, self.dyn_array_len)
1119 return "{0}{1} = convert_{2}_array_win_to_host({3}{1}, {4});\n".format(output, self.name, self.type, input, count)
1120 elif self.is_static_array():
1121 count = self.array_len
1122 if direction == Direction.OUTPUT:
1123 # Needed by VkMemoryHeap.memoryHeaps
1124 return "convert_{0}_static_array_host_to_win({2}{1}, {3}{1}, {4});\n".format(self.type, self.name, input, output, count)
1126 # Nothing needed this yet.
1127 LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
1129 if direction == Direction.OUTPUT:
1130 return "convert_{0}_host_to_win(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
1132 return "convert_{0}_win_to_host(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
1133 elif self.is_static_array():
1134 bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type)
1135 return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count)
1137 return "{0}{1} = {2}{1};\n".format(output, self.name, input)
1139 def definition(self, align=False, conv=False):
1140 """ Generate prototype for given function.
1143 align (bool, optional): Enable alignment if a type needs it. This adds WINE_VK_ALIGN(8) to a member.
1144 conv (bool, optional): Enable conversion if a type needs it. This appends '_host' to the name.
1151 if self.is_struct_forward_declaration():
1154 if conv and self.is_struct():
1155 text += "{0}_host".format(self.type)
1159 if self.is_pointer():
1160 text += " {0}{1}".format(self.pointer, self.name)
1162 if align and self.needs_alignment():
1163 text += " WINE_VK_ALIGN(8) " + self.name
1165 text += " " + self.name
1167 if self.is_static_array():
1168 text += "[{0}]".format(self.array_len)
1172 def get_conversions(self):
1173 """ Return any conversion description for this member and its children when conversion is needed. """
1175 # Check if we need conversion either for this member itself or for any child members
1176 # in case member represents a struct.
1177 if not self.needs_conversion():
1182 # Collect any conversion for any member structs.
1183 struct = self.type_info["data"]
1185 m.needs_struct_extensions_conversion()
1186 if m.needs_conversion():
1187 conversions.extend(m.get_conversions())
1189 struct.needs_struct_extensions_conversion()
1191 struct = self.type_info["data"]
1192 direction = Direction.OUTPUT if struct.returnedonly else Direction.INPUT
1193 if self.is_dynamic_array():
1194 conversions.append(ConversionFunction(False, True, direction, struct))
1195 elif self.is_static_array():
1196 conversions.append(ConversionFunction(True, False, direction, struct))
1198 conversions.append(ConversionFunction(False, False, direction, struct))
1200 if self.needs_free():
1201 conversions.append(FreeFunction(self.is_dynamic_array(), struct))
1208 def is_dynamic_array(self):
1209 """ Returns if the member is an array element.
1210 Vulkan uses this for dynamically sized arrays for which
1211 there is a 'count' parameter.
1213 return self.dyn_array_len is not None
1215 def is_handle(self):
1216 return self.type_info["category"] == "handle"
1218 def is_pointer(self):
1219 return self.pointer is not None
1221 def is_static_array(self):
1222 """ Returns if the member is an array.
1223 Vulkan uses this often for fixed size arrays in which the
1224 length is part of the member.
1226 return self.array_len is not None
1228 def is_struct(self):
1229 return self.type_info["category"] == "struct"
1231 def is_struct_forward_declaration(self):
1232 return self.struct_fwd_decl
1235 return self.type_info["category"] == "union"
1237 def needs_alignment(self):
1238 """ Check if this member needs alignment for 64-bit data.
1239 Various structures need alignment on 64-bit variables due
1240 to compiler differences on 32-bit between Win32 and Linux.
1243 if self.is_pointer():
1245 elif self.type == "size_t":
1247 elif self.type in ["uint64_t", "VkDeviceSize"]:
1249 elif self.is_struct():
1250 struct = self.type_info["data"]
1251 return struct.needs_alignment()
1252 elif self.is_handle():
1253 # Dispatchable handles are pointers to objects, while
1254 # non-dispatchable are uint64_t and hence need alignment.
1255 handle = self.type_info["data"]
1256 return False if handle.is_dispatchable() else True
1259 def needs_conversion(self):
1260 """ Structures requiring alignment, need conversion between win32 and host. """
1262 if not self.is_struct():
1265 struct = self.type_info["data"]
1266 return struct.needs_conversion()
1268 def needs_free(self):
1269 if not self.needs_conversion():
1272 if self.is_dynamic_array():
1275 # TODO: some non-pointer structs and optional pointer structs may need freeing,
1276 # though none of this type have been encountered yet.
1279 def needs_struct_extensions_conversion(self):
1280 if not self.is_struct():
1283 struct = self.type_info["data"]
1284 return struct.needs_struct_extensions_conversion()
1286 def set_type_info(self, type_info):
1287 """ Helper function to set type information from the type registry.
1288 This is needed, because not all type data is available at time of
1291 self.type_info = type_info
1294 class VkParam(object):
1295 """ Helper class which describes a parameter to a function call. """
1297 def __init__(self, type_info, const=None, pointer=None, name=None, array_len=None, dyn_array_len=None):
1300 self.array_len = array_len
1301 self.dyn_array_len = dyn_array_len
1302 self.pointer = pointer
1303 self.type_info = type_info
1304 self.type = type_info["name"] # For convenience
1305 self.handle = type_info["data"] if type_info["category"] == "handle" else None
1306 self.struct = type_info["data"] if type_info["category"] == "struct" else None
1308 self._set_direction()
1309 self._set_format_string()
1310 self._set_conversions()
1313 return "{0} {1} {2} {3} {4} {5}".format(self.const, self.type, self.pointer, self.name, self.array_len, self.dyn_array_len)
1316 def from_xml(param, types):
1317 """ Helper function to create VkParam from xml. """
1319 # Parameter parsing is slightly tricky. All the data is contained within
1320 # a param tag, but some data is within subtags while others are text
1321 # before or after the type tag.
1323 # <param>const <type>char</type>* <name>pLayerName</name></param>
1325 name_elem = param.find("name")
1327 name = name_elem.text
1328 # Tail contains array length e.g. for blendConstants param of vkSetBlendConstants
1329 if name_elem.tail is not None:
1330 array_len = name_elem.tail.strip("[]")
1332 # Name of other parameter in function prototype, which stores the number of
1333 # elements pointed to be by this parameter.
1334 dyn_array_len = param.get("len", None)
1336 const = param.text.strip() if param.text else None
1337 type_elem = param.find("type")
1338 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1340 # Since we have parsed all types before hand, this should not happen.
1341 type_info = types.get(type_elem.text, None)
1342 if type_info is None:
1343 LOGGER.err("type info not found for: {0}".format(type_elem.text))
1345 return VkParam(type_info, const=const, pointer=pointer, name=name, array_len=array_len, dyn_array_len=dyn_array_len)
1347 def _set_conversions(self):
1348 """ Internal helper function to configure any needed conversion functions. """
1350 self.free_func = None
1351 self.input_conv = None
1352 self.output_conv = None
1353 if not self.needs_conversion():
1356 # Input functions require win to host conversion.
1357 if self._direction in [Direction.INPUT, Direction.INPUT_OUTPUT]:
1358 self.input_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.INPUT, self.struct)
1360 # Output functions require host to win conversion.
1361 if self._direction in [Direction.INPUT_OUTPUT, Direction.OUTPUT]:
1362 self.output_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.OUTPUT, self.struct)
1364 # Dynamic arrays, but also some normal structs (e.g. VkCommandBufferBeginInfo) need memory
1365 # allocation and thus some cleanup.
1366 if self.is_dynamic_array() or self.struct.needs_free():
1367 self.free_func = FreeFunction(self.is_dynamic_array(), self.struct)
1369 def _set_direction(self):
1370 """ Internal helper function to set parameter direction (input/output/input_output). """
1372 # The parameter direction needs to be determined from hints in vk.xml like returnedonly,
1373 # parameter constness and other heuristics.
1374 # For now we need to get this right for structures as we need to convert these, we may have
1375 # missed a few other edge cases (e.g. count variables).
1376 # See also https://github.com/KhronosGroup/Vulkan-Docs/issues/610
1378 if not self.is_pointer():
1379 self._direction = Direction.INPUT
1380 elif self.is_const() and self.is_pointer():
1381 self._direction = Direction.INPUT
1382 elif self.is_struct():
1383 if not self.struct.returnedonly:
1384 self._direction = Direction.INPUT
1387 # Returnedonly hints towards output, however in some cases
1388 # it is inputoutput. In particular if pNext / sType exist,
1389 # which are used to link in other structures without having
1390 # to introduce new APIs. E.g. vkGetPhysicalDeviceProperties2KHR.
1391 if "pNext" in self.struct:
1392 self._direction = Direction.INPUT_OUTPUT
1395 self._direction = Direction.OUTPUT
1397 # This should mostly be right. Count variables can be inout, but we don't care about these yet.
1398 self._direction = Direction.OUTPUT
1400 def _set_format_string(self):
1401 """ Internal helper function to be used by constructor to set format string. """
1403 # Determine a format string used by code generation for traces.
1404 # 64-bit types need a conversion function.
1405 self.format_conv = None
1406 if self.is_static_array() or self.is_pointer():
1407 self.format_str = "%p"
1409 if self.type_info["category"] in ["bitmask"]:
1410 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1411 if self.type_info["data"].type == "VkFlags64":
1412 self.format_str = "0x%s"
1413 self.format_conv = "wine_dbgstr_longlong({0})"
1415 self.format_str = "%#x"
1416 elif self.type_info["category"] in ["enum"]:
1417 self.format_str = "%#x"
1418 elif self.is_handle():
1419 # We use uint64_t for non-dispatchable handles as opposed to pointers
1420 # for dispatchable handles.
1421 if self.handle.is_dispatchable():
1422 self.format_str = "%p"
1424 self.format_str = "0x%s"
1425 self.format_conv = "wine_dbgstr_longlong({0})"
1426 elif self.type == "float":
1427 self.format_str = "%f"
1428 elif self.type == "int":
1429 self.format_str = "%d"
1430 elif self.type == "int32_t":
1431 self.format_str = "%d"
1432 elif self.type == "size_t":
1433 self.format_str = "0x%s"
1434 self.format_conv = "wine_dbgstr_longlong({0})"
1435 elif self.type in ["uint16_t", "uint32_t", "VkBool32"]:
1436 self.format_str = "%u"
1437 elif self.type in ["uint64_t", "VkDeviceAddress", "VkDeviceSize"]:
1438 self.format_str = "0x%s"
1439 self.format_conv = "wine_dbgstr_longlong({0})"
1440 elif self.type == "HANDLE":
1441 self.format_str = "%p"
1442 elif self.type in ["VisualID", "xcb_visualid_t", "RROutput"]:
1443 # Don't care about Linux specific types.
1444 self.format_str = ""
1446 LOGGER.warn("Unhandled type: {0}".format(self.type_info))
1448 def copy(self, direction):
1449 if direction == Direction.INPUT:
1450 if self.is_dynamic_array():
1451 return " {0}_host = convert_{1}_array_win_to_host({0}, {2});\n".format(self.name, self.type, self.dyn_array_len)
1453 return " convert_{0}_win_to_host({1}, &{1}_host);\n".format(self.type, self.name)
1455 if self.is_dynamic_array():
1456 LOGGER.error("Unimplemented output conversion for: {0}".format(self.name))
1458 return " convert_{0}_host_to_win(&{1}_host, {1});\n".format(self.type, self.name)
1460 def definition(self, postfix=None):
1461 """ Return prototype for the parameter. E.g. 'const char *foo' """
1465 proto += self.const + " "
1469 if self.is_pointer():
1470 proto += " {0}{1}".format(self.pointer, self.name)
1472 proto += " " + self.name
1474 # Allows appending something to the variable name useful for
1475 # win32 to host conversion.
1476 if postfix is not None:
1479 if self.is_static_array():
1480 proto += "[{0}]".format(self.array_len)
1484 def direction(self):
1485 """ Returns parameter direction: input, output, input_output.
1487 Parameter direction in Vulkan is not straight-forward, which this function determines.
1490 return self._direction
1492 def dispatch_table(self):
1493 """ Return functions dispatch table pointer for dispatchable objects. """
1495 if not self.is_dispatchable():
1498 return "{0}->{1}".format(self.name, self.handle.dispatch_table())
1500 def format_string(self):
1501 return self.format_str
1504 if self.is_dynamic_array():
1505 if self.struct.returnedonly:
1506 # For returnedonly, counts is stored in a pointer.
1507 return " free_{0}_array({1}_host, *{2});\n".format(self.type, self.name, self.dyn_array_len)
1509 return " free_{0}_array({1}_host, {2});\n".format(self.type, self.name, self.dyn_array_len)
1511 # We are operating on a single structure. Some structs (very rare) contain dynamic members,
1512 # which would need freeing.
1513 if self.struct.needs_free():
1514 return " free_{0}(&{1}_host);\n".format(self.type, self.name)
1517 def get_conversions(self):
1518 """ Get a list of conversions required for this parameter if any.
1519 Parameters which are structures may require conversion between win32
1520 and the host platform. This function returns a list of conversions
1524 if not self.is_struct():
1527 self.struct.needs_struct_extensions_conversion()
1528 for m in self.struct:
1529 m.needs_struct_extensions_conversion()
1531 if not self.needs_conversion():
1536 # Collect any member conversions first, so we can guarantee
1537 # those functions will be defined prior to usage by the
1538 # 'parent' param requiring conversion.
1539 for m in self.struct:
1540 if not m.is_struct():
1543 if not m.needs_conversion():
1546 conversions.extend(m.get_conversions())
1548 # Conversion requirements for the 'parent' parameter.
1549 if self.input_conv is not None:
1550 conversions.append(self.input_conv)
1551 if self.output_conv is not None:
1552 conversions.append(self.output_conv)
1553 if self.free_func is not None:
1554 conversions.append(self.free_func)
1559 return self.const is not None
1561 def is_dynamic_array(self):
1562 return self.dyn_array_len is not None
1564 def is_dispatchable(self):
1565 if not self.is_handle():
1568 return self.handle.is_dispatchable()
1570 def is_handle(self):
1571 return self.handle is not None
1573 def is_pointer(self):
1574 return self.pointer is not None
1576 def is_static_array(self):
1577 return self.array_len is not None
1579 def is_struct(self):
1580 return self.struct is not None
1582 def needs_conversion(self):
1583 """ Returns if parameter needs conversion between win32 and host. """
1585 if not self.is_struct():
1588 # VkSparseImageMemoryRequirements(2) is used by vkGetImageSparseMemoryRequirements(2).
1589 # This function is tricky to wrap, because how to wrap depends on whether
1590 # pSparseMemoryRequirements is NULL or not. Luckily for VkSparseImageMemoryRequirements(2)
1591 # the alignment works out in such a way that no conversion is needed between win32 and Linux.
1592 if self.type in ["VkSparseImageMemoryRequirements", "VkSparseImageMemoryRequirements2"]:
1595 # If a structure needs alignment changes, it means we need to
1596 # perform parameter conversion between win32 and host.
1597 if self.struct.needs_conversion():
1602 def needs_free(self):
1603 return self.free_func is not None
1605 def needs_input_conversion(self):
1606 return self.input_conv is not None
1608 def needs_output_conversion(self):
1609 return self.output_conv is not None
1612 """ Generate spec file entry for this parameter. """
1614 if self.is_pointer() and self.type == "char":
1616 if self.is_dispatchable() or self.is_pointer() or self.is_static_array():
1618 if self.type_info["category"] in ["bitmask"]:
1619 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1620 if self.type_info["data"].type == "VkFlags64":
1624 if self.type_info["category"] in ["enum"]:
1626 if self.is_handle() and not self.is_dispatchable():
1628 if self.type == "float":
1630 if self.type in ["int", "int32_t", "size_t", "uint16_t", "uint32_t", "VkBool32"]:
1632 if self.type in ["uint64_t", "VkDeviceSize"]:
1635 LOGGER.error("Unhandled spec conversion for type: {0}".format(self.type))
1637 def variable(self, conv=False):
1638 """ Returns 'glue' code during generation of a function call on how to access the variable.
1639 This function handles various scenarios such as 'unwrapping' if dispatchable objects and
1640 renaming of parameters in case of win32 -> host conversion.
1643 conv (bool, optional): Enable conversion if the param needs it. This appends '_host' to the name.
1646 # Hack until we enable allocation callbacks from ICD to application. These are a joy
1647 # to enable one day, because of calling convention conversion.
1648 if "VkAllocationCallbacks" in self.type:
1649 LOGGER.debug("TODO: setting NULL VkAllocationCallbacks for {0}".format(self.name))
1652 if conv and self.needs_conversion():
1653 if self.is_dynamic_array():
1654 return "{0}_host".format(self.name)
1656 return "&{0}_host".format(self.name)
1658 # We need to pass the native handle to the native Vulkan calls and
1659 # the wine driver's handle to calls which are wrapped by the driver.
1660 driver_handle = self.handle.driver_handle(self.name) if self.is_handle() else None
1661 return driver_handle if driver_handle else self.name
1664 class VkStruct(Sequence):
1665 """ Class which represents the type union and struct. """
1667 def __init__(self, name, members, returnedonly, structextends, alias=None, union=False):
1669 self.members = members
1670 self.returnedonly = returnedonly
1671 self.structextends = structextends
1672 self.required = False
1675 self.type_info = None # To be set later.
1676 self.struct_extensions = []
1677 self.aliased_by = []
1679 def __getitem__(self, i):
1680 return self.members[i]
1683 return len(self.members)
1686 def from_alias(struct, alias):
1687 name = struct.attrib.get("name")
1688 aliasee = VkStruct(name, alias.members, alias.returnedonly, alias.structextends, alias=alias)
1690 alias.add_aliased_by(aliasee)
1694 def from_xml(struct):
1695 # Unions and structs are the same parsing wise, but we need to
1696 # know which one we are dealing with later on for code generation.
1697 union = True if struct.attrib["category"] == "union" else False
1699 name = struct.attrib.get("name")
1701 # 'Output' structures for which data is filled in by the API are
1702 # marked as 'returnedonly'.
1703 returnedonly = True if struct.attrib.get("returnedonly") else False
1705 structextends = struct.attrib.get("structextends")
1706 structextends = structextends.split(",") if structextends else []
1709 for member in struct.findall("member"):
1710 vk_member = VkMember.from_xml(member)
1711 members.append(vk_member)
1713 return VkStruct(name, members, returnedonly, structextends, union=union)
1716 def decouple_structs(structs):
1717 """ Helper function which decouples a list of structs.
1718 Structures often depend on other structures. To make the C compiler
1719 happy we need to define 'substructures' first. This function analyzes
1720 the list of structures and reorders them in such a way that they are
1724 tmp_structs = list(structs) # Don't modify the original structures.
1725 decoupled_structs = []
1727 while (len(tmp_structs) > 0):
1728 for struct in tmp_structs:
1731 if not struct.required:
1732 tmp_structs.remove(struct)
1736 if not (m.is_struct() or m.is_union()):
1739 # VkBaseInstructure and VkBaseOutStructure reference themselves.
1740 if m.type == struct.name:
1744 # Check if a struct we depend on has already been defined.
1745 for s in decoupled_structs:
1746 if s.name == m.type:
1751 # Check if the struct we depend on is even in the list of structs.
1752 # If found now, it means we haven't met all dependencies before we
1753 # can operate on the current struct.
1754 # When generating 'host' structs we may not be able to find a struct
1755 # as the list would only contain the structs requiring conversion.
1756 for s in tmp_structs:
1757 if s.name == m.type:
1761 if dependends == False:
1762 decoupled_structs.append(struct)
1763 tmp_structs.remove(struct)
1765 return decoupled_structs
1767 def definition(self, align=False, conv=False, postfix=None):
1768 """ Convert structure to textual definition.
1771 align (bool, optional): enable alignment to 64-bit for win32 struct compatibility.
1772 conv (bool, optional): enable struct conversion if the struct needs it.
1773 postfix (str, optional): text to append to end of struct name, useful for struct renaming.
1776 # Only define alias structs when doing conversions
1777 if self.is_alias() and not conv:
1781 text = "typedef union {0}".format(self.name)
1783 text = "typedef struct {0}".format(self.name)
1785 if postfix is not None:
1791 if align and m.needs_alignment():
1792 text += " {0};\n".format(m.definition(align=align))
1793 elif conv and m.needs_conversion():
1794 text += " {0};\n".format(m.definition(conv=conv))
1796 text += " {0};\n".format(m.definition())
1798 if postfix is not None:
1799 text += "}} {0}{1};\n\n".format(self.name, postfix)
1801 text += "}} {0};\n".format(self.name)
1803 for aliasee in self.aliased_by:
1804 text += "typedef {0} {1};\n".format(self.name, aliasee.name)
1811 return bool(self.alias)
1813 def add_aliased_by(self, aliasee):
1814 self.aliased_by.append(aliasee)
1816 def needs_alignment(self):
1817 """ Check if structure needs alignment for 64-bit data.
1818 Various structures need alignment on 64-bit variables due
1819 to compiler differences on 32-bit between Win32 and Linux.
1822 for m in self.members:
1823 if m.needs_alignment():
1827 def needs_conversion(self):
1828 """ Returns if struct members needs conversion between win32 and host.
1829 Structures need conversion if they contain members requiring alignment
1830 or if they include other structures which need alignment.
1833 if self.needs_alignment():
1836 for m in self.members:
1837 if m.needs_conversion():
1841 def needs_free(self):
1842 """ Check if any struct member needs some memory freeing."""
1844 for m in self.members:
1852 def needs_struct_extensions_conversion(self):
1853 """ Checks if structure extensions in pNext chain need conversion. """
1856 for e in self.struct_extensions:
1857 if e.required and e.needs_conversion():
1858 LOGGER.error("Unhandled pNext chain conversion for {0}".format(e.name))
1863 def set_type_info(self, types):
1864 """ Helper function to set type information from the type registry.
1865 This is needed, because not all type data is available at time of
1868 for m in self.members:
1869 type_info = types[m.type]
1870 m.set_type_info(type_info)
1873 class ConversionFunction(object):
1874 def __init__(self, array, dyn_array, direction, struct):
1876 self.direction = direction
1877 self.dyn_array = dyn_array
1878 self.struct = struct
1879 self.type = struct.name
1883 def __eq__(self, other):
1884 return self.name == other.name
1886 def _generate_array_conversion_func(self):
1887 """ Helper function for generating a conversion function for array structs. """
1889 if self.direction == Direction.OUTPUT:
1890 params = ["const {0}_host *in".format(self.type), "uint32_t count"]
1891 return_type = self.type
1893 params = ["const {0} *in".format(self.type), "uint32_t count"]
1894 return_type = "{0}_host".format(self.type)
1896 # Generate function prototype.
1897 body = "static inline {0} *{1}(".format(return_type, self.name)
1898 body += ", ".join(p for p in params)
1901 body += " {0} *out;\n".format(return_type)
1902 body += " unsigned int i;\n\n"
1903 body += " if (!in) return NULL;\n\n"
1905 body += " out = heap_alloc(count * sizeof(*out));\n"
1907 body += " for (i = 0; i < count; i++)\n"
1910 for m in self.struct:
1911 # TODO: support copying of pNext extension structures!
1912 # Luckily though no extension struct at this point needs conversion.
1913 body += " " + m.copy("in[i].", "out[i].", self.direction)
1916 body += " return out;\n"
1920 def _generate_conversion_func(self):
1921 """ Helper function for generating a conversion function for non-array structs. """
1923 if self.direction == Direction.OUTPUT:
1924 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type)]
1926 params = ["const {0} *in".format(self.type), "{0}_host *out".format(self.type)]
1928 body = "static inline void {0}(".format(self.name)
1930 # Generate parameter list
1931 body += ", ".join(p for p in params)
1934 body += " if (!in) return;\n\n"
1936 if self.direction == Direction.INPUT and "pNext" in self.struct and self.struct.returnedonly:
1937 # We are dealing with an input_output parameter. For these we only need to copy
1938 # pNext and sType as the other fields are filled in by the host. We do potentially
1939 # have to iterate over pNext and perform conversions based on switch(sType)!
1940 # Luckily though no extension structs at this point need conversion.
1941 # TODO: support copying of pNext extension structures!
1942 body += " out->pNext = in->pNext;\n"
1943 body += " out->sType = in->sType;\n"
1945 for m in self.struct:
1946 # TODO: support copying of pNext extension structures!
1947 body += " " + m.copy("in->", "out->", self.direction)
1952 def _generate_static_array_conversion_func(self):
1953 """ Helper function for generating a conversion function for array structs. """
1955 if self.direction == Direction.OUTPUT:
1956 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
1958 params = ["const {0} *in".format(self.type), "{0} *out_host".format(self.type), "uint32_t count"]
1960 # Generate function prototype.
1961 body = "static inline void {0}(".format(self.name)
1962 body += ", ".join(p for p in params)
1964 body += " unsigned int i;\n\n"
1965 body += " if (!in) return;\n\n"
1966 body += " for (i = 0; i < count; i++)\n"
1969 for m in self.struct:
1970 # TODO: support copying of pNext extension structures!
1971 body += " " + m.copy("in[i].", "out[i].", self.direction)
1977 def _set_name(self):
1978 if self.direction == Direction.INPUT:
1980 name = "convert_{0}_static_array_win_to_host".format(self.type)
1981 elif self.dyn_array:
1982 name = "convert_{0}_array_win_to_host".format(self.type)
1984 name = "convert_{0}_win_to_host".format(self.type)
1985 else: # Direction.OUTPUT
1987 name = "convert_{0}_static_array_host_to_win".format(self.type)
1988 elif self.dyn_array:
1989 name = "convert_{0}_array_host_to_win".format(self.type)
1991 name = "convert_{0}_host_to_win".format(self.type)
1995 def definition(self):
1997 return self._generate_static_array_conversion_func()
1998 elif self.dyn_array:
1999 return self._generate_array_conversion_func()
2001 return self._generate_conversion_func()
2004 class FreeFunction(object):
2005 def __init__(self, dyn_array, struct):
2006 self.dyn_array = dyn_array
2007 self.struct = struct
2008 self.type = struct.name
2011 self.name = "free_{0}_array".format(self.type)
2013 self.name = "free_{0}".format(self.type)
2015 def __eq__(self, other):
2016 return self.name == other.name
2018 def _generate_array_free_func(self):
2019 """ Helper function for cleaning up temporary buffers required for array conversions. """
2021 # Generate function prototype.
2022 body = "static inline void {0}({1}_host *in, uint32_t count)\n{{\n".format(self.name, self.type)
2024 # E.g. VkGraphicsPipelineCreateInfo_host needs freeing for pStages.
2025 if self.struct.needs_free():
2026 body += " unsigned int i;\n\n"
2027 body += " if (!in) return;\n\n"
2028 body += " for (i = 0; i < count; i++)\n"
2031 for m in self.struct:
2032 if m.needs_conversion() and m.is_dynamic_array():
2034 # Add a cast to ignore const on conversion structs we allocated ourselves.
2035 body += " free_{0}_array(({0}_host *)in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
2037 body += " free_{0}_array(in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
2038 elif m.needs_conversion():
2039 LOGGER.error("Unhandled conversion for {0}".format(m.name))
2042 body += " if (!in) return;\n\n"
2044 body += " heap_free(in);\n"
2049 def _generate_free_func(self):
2050 # E.g. VkCommandBufferBeginInfo.pInheritanceInfo needs freeing.
2051 if not self.struct.needs_free():
2054 # Generate function prototype.
2055 body = "static inline void {0}({1}_host *in)\n{{\n".format(self.name, self.type)
2057 for m in self.struct:
2058 if m.needs_conversion() and m.is_dynamic_array():
2059 count = m.dyn_array_len if isinstance(m.dyn_array_len, int) else "in->{0}".format(m.dyn_array_len)
2061 # Add a cast to ignore const on conversion structs we allocated ourselves.
2062 body += " free_{0}_array(({0}_host *)in->{1}, {2});\n".format(m.type, m.name, count)
2064 body += " free_{0}_array(in->{1}, {2});\n".format(m.type, m.name, count)
2069 def definition(self):
2071 return self._generate_array_free_func()
2073 # Some structures need freeing too if they contain dynamic arrays.
2074 # E.g. VkCommandBufferBeginInfo
2075 return self._generate_free_func()
2078 class StructChainConversionFunction(object):
2079 def __init__(self, direction, struct):
2080 self.direction = direction
2081 self.struct = struct
2082 self.type = struct.name
2084 self.name = "convert_{0}_struct_chain".format(self.type)
2086 def __eq__(self, other):
2087 return self.name == other.name
2089 def prototype(self, postfix=""):
2090 return "VkResult {0}(const void *pNext, {1} *out_struct) {2}".format(self.name, self.type, postfix).strip()
2092 def definition(self):
2093 body = self.prototype()
2096 body += " VkBaseOutStructure *out_header = (VkBaseOutStructure *)out_struct;\n";
2097 body += " const VkBaseInStructure *in_header;\n\n";
2099 body += " out_header->pNext = NULL;\n\n"
2101 body += " for (in_header = pNext; in_header; in_header = in_header->pNext)\n"
2103 body += " switch (in_header->sType)\n"
2106 # Ignore to not confuse host loader.
2107 body += " case VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO:\n"
2108 body += " case VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO:\n"
2109 body += " break;\n\n"
2111 for e in self.struct.struct_extensions:
2115 stype = next(x for x in e.members if x.name == "sType")
2117 body += " case {0}:\n".format(stype.values)
2120 body += " const {0} *in = (const {0} *)in_header;\n".format(e.name)
2121 body += " {0} *out;\n\n".format(e.name)
2123 body += " if (!(out = heap_alloc(sizeof(*out)))) goto out_of_memory;\n\n"
2126 if m.name == "pNext":
2127 body += " out->pNext = NULL;\n"
2129 body += " " + m.copy("in->", "out->", self.direction)
2131 body += "\n out_header->pNext = (VkBaseOutStructure *)out;\n"
2132 body += " out_header = out_header->pNext;\n"
2136 body += " default:\n"
2137 body += " FIXME(\"Application requested a linked structure of type %u.\\n\", in_header->sType);\n"
2142 body += " return VK_SUCCESS;\n"
2144 if any(x for x in self.struct.struct_extensions if x.required):
2145 body += "\nout_of_memory:\n"
2146 body += " free_{0}_struct_chain(out_struct);\n".format(self.type)
2147 body += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
2152 class FreeStructChainFunction(object):
2153 def __init__(self, struct):
2154 self.struct = struct
2155 self.type = struct.name
2157 self.name = "free_{0}_struct_chain".format(self.type)
2159 def __eq__(self, other):
2160 return self.name == other.name
2162 def prototype(self, postfix=""):
2163 return "void {0}({1} *s) {2}".format(self.name, self.type, postfix).strip()
2165 def definition(self):
2166 body = self.prototype()
2169 body += " VkBaseOutStructure *header = (void *)s->pNext;\n\n";
2171 body += " while (header)\n"
2173 body += " void *prev = header;\n"
2174 body += " header = header->pNext;\n"
2175 body += " heap_free(prev);\n"
2178 body += " s->pNext = NULL;\n"
2184 class VkGenerator(object):
2185 def __init__(self, registry):
2186 self.registry = registry
2188 # Build a list conversion functions for struct conversion.
2189 self.conversions = []
2190 self.struct_chain_conversions = []
2191 self.host_structs = []
2192 for func in self.registry.funcs.values():
2193 if not func.is_required():
2196 if not func.needs_conversion():
2199 conversions = func.get_conversions()
2200 for conv in conversions:
2201 # Pull in any conversions for vulkan_thunks.c.
2202 if func.needs_thunk():
2203 # Append if we don't already have this conversion.
2204 if not any(c == conv for c in self.conversions):
2205 self.conversions.append(conv)
2207 # Structs can be used in different ways by different conversions
2208 # e.g. array vs non-array. Just make sure we pull in each struct once.
2209 if not any(s.name == conv.struct.name for s in self.host_structs):
2210 self.host_structs.append(conv.struct)
2212 for struct in self.registry.structs:
2213 if struct.name in STRUCT_CHAIN_CONVERSIONS:
2214 self.struct_chain_conversions.append(StructChainConversionFunction(Direction.INPUT, struct))
2215 self.struct_chain_conversions.append(FreeStructChainFunction(struct))
2217 def _generate_copyright(self, f, spec_file=False):
2218 f.write("# " if spec_file else "/* ")
2219 f.write("Automatically generated from Vulkan vk.xml; DO NOT EDIT!\n")
2220 lines = ["", "This file is generated from Vulkan vk.xml file covered",
2221 "by the following copyright and permission notice:"]
2222 lines.extend([l.rstrip(" ") for l in self.registry.copyright.splitlines()])
2224 f.write("{0}{1}".format("# " if spec_file else " * ", line).rstrip(" ") + "\n")
2225 f.write("\n" if spec_file else " */\n\n")
2227 def generate_thunks_c(self, f, prefix):
2228 self._generate_copyright(f)
2229 f.write("#include \"config.h\"\n")
2230 f.write("#include \"wine/port.h\"\n\n")
2232 f.write("#include \"vulkan_private.h\"\n\n")
2234 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2236 # Generate any conversion helper functions.
2237 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
2238 for conv in self.conversions:
2239 f.write(conv.definition())
2240 f.write("#endif /* USE_STRUCT_CONVERSION */\n\n")
2242 for conv in self.struct_chain_conversions:
2243 f.write(conv.definition())
2245 # Create thunks for instance and device functions.
2246 # Global functions don't go through the thunks.
2247 for vk_func in self.registry.funcs.values():
2248 if not vk_func.is_required():
2251 if vk_func.is_global_func():
2254 if not vk_func.needs_thunk():
2257 # Exports symbols for Core functions.
2258 if not vk_func.is_core_func() and not vk_func.needs_private_thunk():
2261 if vk_func.needs_private_thunk():
2262 f.write(vk_func.thunk(prefix="thunk_"))
2264 f.write(vk_func.thunk(prefix=prefix, call_conv="WINAPI"))
2266 f.write("static const struct vulkan_func vk_device_dispatch_table[] =\n{\n")
2267 for vk_func in self.registry.device_funcs:
2268 if not vk_func.is_required():
2271 f.write(" {{\"{0}\", &{1}{0}}},\n".format(vk_func.name, prefix))
2274 f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
2275 for vk_func in self.registry.instance_funcs:
2276 if not vk_func.is_required():
2279 f.write(" {{\"{0}\", &{1}{0}}},\n".format(vk_func.name, prefix))
2282 f.write("void *wine_vk_get_device_proc_addr(const char *name)\n")
2284 f.write(" unsigned int i;\n")
2285 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_dispatch_table); i++)\n")
2287 f.write(" if (strcmp(vk_device_dispatch_table[i].name, name) == 0)\n")
2289 f.write(" TRACE(\"Found name=%s in device table\\n\", debugstr_a(name));\n")
2290 f.write(" return vk_device_dispatch_table[i].func;\n")
2293 f.write(" return NULL;\n")
2296 f.write("void *wine_vk_get_instance_proc_addr(const char *name)\n")
2298 f.write(" unsigned int i;\n")
2299 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_dispatch_table); i++)\n")
2301 f.write(" if (strcmp(vk_instance_dispatch_table[i].name, name) == 0)\n")
2303 f.write(" TRACE(\"Found name=%s in instance table\\n\", debugstr_a(name));\n")
2304 f.write(" return vk_instance_dispatch_table[i].func;\n")
2307 f.write(" return NULL;\n")
2310 # Create array of device extensions.
2311 f.write("static const char * const vk_device_extensions[] =\n{\n")
2312 for ext in self.registry.extensions:
2313 if ext["type"] != "device":
2316 f.write(" \"{0}\",\n".format(ext["name"]))
2319 # Create array of instance extensions.
2320 f.write("static const char * const vk_instance_extensions[] =\n{\n")
2321 for ext in self.registry.extensions:
2322 if ext["type"] != "instance":
2325 f.write(" \"{0}\",\n".format(ext["name"]))
2328 f.write("BOOL wine_vk_device_extension_supported(const char *name)\n")
2330 f.write(" unsigned int i;\n")
2331 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_extensions); i++)\n")
2333 f.write(" if (strcmp(vk_device_extensions[i], name) == 0)\n")
2334 f.write(" return TRUE;\n")
2336 f.write(" return FALSE;\n")
2339 f.write("BOOL wine_vk_instance_extension_supported(const char *name)\n")
2341 f.write(" unsigned int i;\n")
2342 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_extensions); i++)\n")
2344 f.write(" if (strcmp(vk_instance_extensions[i], name) == 0)\n")
2345 f.write(" return TRUE;\n")
2347 f.write(" return FALSE;\n")
2350 f.write("BOOL wine_vk_is_type_wrapped(VkObjectType type)\n")
2352 f.write(" return FALSE")
2353 for handle in self.registry.handles:
2354 if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2356 f.write(" ||\n type == {}".format(handle.object_type))
2360 f.write("uint64_t wine_vk_unwrap_handle(VkObjectType type, uint64_t handle)\n")
2362 f.write(" switch(type)\n")
2364 for handle in self.registry.handles:
2365 if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2367 f.write(" case {}:\n".format(handle.object_type))
2368 if handle.is_dispatchable():
2369 f.write(" return (uint64_t) (uintptr_t) ")
2370 f.write(handle.native_handle("(({}) (uintptr_t) handle)".format(handle.name)))
2372 f.write(" return (uint64_t) ")
2373 f.write(handle.native_handle("handle"))
2375 f.write(" default:\n")
2376 f.write(" return handle;\n")
2380 def generate_thunks_h(self, f, prefix):
2381 self._generate_copyright(f)
2383 f.write("#ifndef __WINE_VULKAN_THUNKS_H\n")
2384 f.write("#define __WINE_VULKAN_THUNKS_H\n\n")
2386 f.write("#define WINE_VK_VERSION VK_API_VERSION_{0}_{1}\n\n".format(WINE_VK_VERSION[0], WINE_VK_VERSION[1]))
2388 # Generate prototypes for device and instance functions requiring a custom implementation.
2389 f.write("/* Functions for which we have custom implementations outside of the thunks. */\n")
2390 for vk_func in self.registry.funcs.values():
2391 if not vk_func.is_required() or vk_func.is_global_func():
2393 if vk_func.needs_thunk() and not vk_func.needs_private_thunk():
2396 if vk_func.is_core_func():
2397 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix="wine_")))
2399 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix="wine_", postfix="DECLSPEC_HIDDEN")))
2402 f.write("/* Private thunks */\n")
2403 for vk_func in self.registry.funcs.values():
2404 if vk_func.needs_private_thunk():
2405 f.write("{0};\n".format(vk_func.prototype(prefix="thunk_", postfix="DECLSPEC_HIDDEN")))
2408 for struct in self.host_structs:
2409 f.write(struct.definition(align=False, conv=True, postfix="_host"))
2412 for func in self.struct_chain_conversions:
2413 f.write(func.prototype(postfix="DECLSPEC_HIDDEN") + ";\n")
2416 f.write("/* For use by vkDevice and children */\n")
2417 f.write("struct vulkan_device_funcs\n{\n")
2418 for vk_func in self.registry.device_funcs:
2419 if not vk_func.is_required():
2422 if not vk_func.needs_dispatch():
2423 LOGGER.debug("skipping {0} in vulkan_device_funcs".format(vk_func.name))
2426 if vk_func.needs_conversion():
2427 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
2428 f.write(" {0};\n".format(vk_func.pfn(conv=True)))
2430 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2433 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2436 f.write("/* For use by vkInstance and children */\n")
2437 f.write("struct vulkan_instance_funcs\n{\n")
2438 for vk_func in self.registry.instance_funcs:
2439 if not vk_func.is_required():
2442 if not vk_func.needs_dispatch():
2443 LOGGER.debug("skipping {0} in vulkan_instance_funcs".format(vk_func.name))
2446 if vk_func.needs_conversion():
2447 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
2448 f.write(" {0};\n".format(vk_func.pfn(conv=True)))
2450 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2453 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
2456 f.write("#define ALL_VK_DEVICE_FUNCS() \\\n")
2458 for vk_func in self.registry.device_funcs:
2459 if not vk_func.is_required():
2462 if not vk_func.needs_dispatch():
2463 LOGGER.debug("skipping {0} in ALL_VK_DEVICE_FUNCS".format(vk_func.name))
2467 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2470 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2473 f.write("#define ALL_VK_INSTANCE_FUNCS() \\\n")
2475 for vk_func in self.registry.instance_funcs:
2476 if not vk_func.is_required():
2479 if not vk_func.needs_dispatch():
2480 LOGGER.debug("skipping {0} in ALL_VK_INSTANCE_FUNCS".format(vk_func.name))
2484 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2487 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2490 f.write("#endif /* __WINE_VULKAN_THUNKS_H */\n")
2492 def generate_vulkan_h(self, f):
2493 self._generate_copyright(f)
2494 f.write("#ifndef __WINE_VULKAN_H\n")
2495 f.write("#define __WINE_VULKAN_H\n\n")
2497 f.write("#include <windef.h>\n")
2498 f.write("#include <stdint.h>\n\n")
2500 f.write("/* Define WINE_VK_HOST to get 'host' headers. */\n")
2501 f.write("#ifdef WINE_VK_HOST\n")
2502 f.write("#define VKAPI_CALL\n")
2503 f.write('#define WINE_VK_ALIGN(x)\n')
2504 f.write("#endif\n\n")
2506 f.write("#ifndef VKAPI_CALL\n")
2507 f.write("#define VKAPI_CALL __stdcall\n")
2508 f.write("#endif\n\n")
2510 f.write("#ifndef VKAPI_PTR\n")
2511 f.write("#define VKAPI_PTR VKAPI_CALL\n")
2512 f.write("#endif\n\n")
2514 f.write("#ifndef WINE_VK_ALIGN\n")
2515 f.write("#define WINE_VK_ALIGN DECLSPEC_ALIGN\n")
2516 f.write("#endif\n\n")
2518 # The overall strategy is to define independent constants and datatypes,
2519 # prior to complex structures and function calls to avoid forward declarations.
2520 for const in self.registry.consts:
2521 # For now just generate things we may not need. The amount of parsing needed
2522 # to get some of the info is tricky as you need to figure out which structure
2523 # references a certain constant.
2524 f.write(const.definition())
2527 for define in self.registry.defines:
2528 f.write(define.definition())
2530 for handle in self.registry.handles:
2531 # For backward compatibility also create definitions for aliases.
2532 # These types normally don't get pulled in as we use the new types
2533 # even in legacy functions if they are aliases.
2534 if handle.is_required() or handle.is_alias():
2535 f.write(handle.definition())
2538 for base_type in self.registry.base_types:
2539 f.write(base_type.definition())
2542 for bitmask in self.registry.bitmasks:
2543 f.write(bitmask.definition())
2546 # Define enums, this includes values for some of the bitmask types as well.
2547 for enum in self.registry.enums.values():
2549 f.write(enum.definition())
2551 for fp in self.registry.funcpointers:
2553 f.write(fp.definition())
2556 # This generates both structures and unions. Since structures
2557 # may depend on other structures/unions, we need a list of
2558 # decoupled structs.
2559 # Note: unions are stored in structs for dependency reasons,
2560 # see comment in parsing section.
2561 structs = VkStruct.decouple_structs(self.registry.structs)
2562 for struct in structs:
2563 LOGGER.debug("Generating struct: {0}".format(struct.name))
2564 f.write(struct.definition(align=True))
2566 for func in self.registry.funcs.values():
2567 if not func.is_required():
2568 LOGGER.debug("Skipping PFN definition for: {0}".format(func.name))
2571 f.write("typedef {0};\n".format(func.pfn(prefix="PFN", call_conv="VKAPI_PTR")))
2574 f.write("#ifndef VK_NO_PROTOTYPES\n")
2575 for func in self.registry.funcs.values():
2576 if not func.is_required():
2577 LOGGER.debug("Skipping API definition for: {0}".format(func.name))
2580 LOGGER.debug("Generating API definition for: {0}".format(func.name))
2581 f.write("{0};\n".format(func.prototype(call_conv="VKAPI_CALL")))
2582 f.write("#endif /* VK_NO_PROTOTYPES */\n\n")
2584 f.write("#endif /* __WINE_VULKAN_H */\n")
2586 def generate_vulkan_driver_h(self, f):
2587 self._generate_copyright(f)
2588 f.write("#ifndef __WINE_VULKAN_DRIVER_H\n")
2589 f.write("#define __WINE_VULKAN_DRIVER_H\n\n")
2591 f.write("/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */\n")
2592 f.write("#define WINE_VULKAN_DRIVER_VERSION {0}\n\n".format(DRIVER_VERSION))
2594 f.write("struct vulkan_funcs\n{\n")
2595 f.write(" /* Vulkan global functions. These are the only calls at this point a graphics driver\n")
2596 f.write(" * needs to provide. Other function calls will be provided indirectly by dispatch\n")
2597 f.write(" * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice.\n")
2600 for vk_func in self.registry.funcs.values():
2601 if not vk_func.is_driver_func():
2605 # Avoid PFN_vkVoidFunction in driver interface as Vulkan likes to put calling convention
2606 # stuff in there. For simplicity substitute with "void *".
2607 pfn = pfn.replace("PFN_vkVoidFunction", "void *")
2608 f.write(" {0};\n".format(pfn))
2611 f.write(" /* winevulkan specific functions */\n")
2612 f.write(" VkSurfaceKHR (*p_wine_get_native_surface)(VkSurfaceKHR);\n")
2615 f.write("extern const struct vulkan_funcs * CDECL __wine_get_vulkan_driver(HDC hdc, UINT version);\n\n")
2617 f.write("static inline void *get_vulkan_driver_device_proc_addr(\n")
2618 f.write(" const struct vulkan_funcs *vulkan_funcs, const char *name)\n{\n")
2619 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
2620 f.write(" name += 2;\n\n")
2621 for vk_func in self.registry.funcs.values():
2622 if vk_func.is_driver_func() and vk_func.is_device_func():
2623 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
2624 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
2626 f.write(" return NULL;\n}\n\n")
2628 f.write("static inline void *get_vulkan_driver_instance_proc_addr(\n")
2629 f.write(" const struct vulkan_funcs *vulkan_funcs, VkInstance instance, const char *name)\n{\n")
2630 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
2631 f.write(" name += 2;\n\n")
2632 for vk_func in self.registry.funcs.values():
2633 if vk_func.is_driver_func() and vk_func.is_global_func() and vk_func.name != "vkGetInstanceProcAddr":
2634 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
2635 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
2637 f.write(" if (!instance) return NULL;\n\n")
2638 for vk_func in self.registry.funcs.values():
2639 if vk_func.is_driver_func() and vk_func.is_instance_func():
2640 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
2641 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
2643 f.write(" name -= 2;\n\n")
2644 f.write(" return get_vulkan_driver_device_proc_addr(vulkan_funcs, name);\n}\n\n")
2646 f.write("#endif /* __WINE_VULKAN_DRIVER_H */\n")
2648 def generate_vulkan_spec(self, f):
2649 self._generate_copyright(f, spec_file=True)
2650 f.write("@ stdcall -private vk_icdGetInstanceProcAddr(ptr str) wine_vk_icdGetInstanceProcAddr\n")
2651 f.write("@ stdcall -private vk_icdNegotiateLoaderICDInterfaceVersion(ptr) wine_vk_icdNegotiateLoaderICDInterfaceVersion\n")
2652 f.write("@ cdecl -norelay native_vkGetInstanceProcAddrWINE(ptr str)\n")
2654 # Export symbols for all Vulkan Core functions.
2655 for func in self.registry.funcs.values():
2656 if not func.is_core_func():
2659 # We support all Core functions except for VK_KHR_display* APIs.
2660 # Create stubs for unsupported Core functions.
2661 if func.is_required():
2662 f.write(func.spec(prefix="wine_"))
2664 f.write("@ stub {0}\n".format(func.name))
2666 f.write("@ stdcall -private DllRegisterServer()\n")
2667 f.write("@ stdcall -private DllUnregisterServer()\n")
2669 def generate_vulkan_loader_spec(self, f):
2670 self._generate_copyright(f, spec_file=True)
2672 # Export symbols for all Vulkan Core functions.
2673 for func in self.registry.funcs.values():
2674 if not func.is_core_func():
2677 # We support all Core functions except for VK_KHR_display* APIs.
2678 # Create stubs for unsupported Core functions.
2679 if func.is_required():
2680 f.write(func.spec(symbol="winevulkan.wine_" + func.name))
2682 f.write("@ stub {0}\n".format(func.name))
2685 class VkRegistry(object):
2686 def __init__(self, reg_filename):
2687 # Used for storage of type information.
2688 self.base_types = None
2689 self.bitmasks = None
2693 self.funcpointers = None
2697 # We aggregate all types in here for cross-referencing.
2701 self.version_regex = re.compile(
2710 # Overall strategy for parsing the registry is to first
2711 # parse all type / function definitions. Then parse
2712 # features and extensions to decide which types / functions
2713 # to actually 'pull in' for code generation. For each type or
2714 # function call we want we set a member 'required' to True.
2715 tree = ET.parse(reg_filename)
2716 root = tree.getroot()
2717 self._parse_enums(root)
2718 self._parse_types(root)
2719 self._parse_commands(root)
2721 # Pull in any required types and functions.
2722 self._parse_features(root)
2723 self._parse_extensions(root)
2725 self._match_object_types()
2727 self.copyright = root.find('./comment').text
2729 def _is_feature_supported(self, feature):
2730 version = self.version_regex.match(feature)
2734 version = tuple(map(int, version.group('major', 'minor')))
2735 return version <= WINE_VK_VERSION
2737 def _is_extension_supported(self, extension):
2738 # We disable some extensions as either we haven't implemented
2739 # support yet or because they are for platforms other than win32.
2740 return extension not in UNSUPPORTED_EXTENSIONS
2742 def _mark_command_required(self, command):
2743 """ Helper function to mark a certain command and the datatypes it needs as required."""
2744 def mark_bitmask_dependencies(bitmask, types):
2745 if bitmask.requires is not None:
2746 types[bitmask.requires]["data"].required = True
2748 def mark_funcpointer_dependencies(fp, types):
2749 for m in fp.members:
2750 type_info = types[m.type]
2752 # Complex types have a matching definition e.g. VkStruct.
2753 # Not needed for base types such as uint32_t.
2754 if "data" in type_info:
2755 types[m.type]["data"].required = True
2757 def mark_struct_dependencies(struct, types):
2759 type_info = types[m.type]
2761 # Complex types have a matching definition e.g. VkStruct.
2762 # Not needed for base types such as uint32_t.
2763 if "data" in type_info:
2764 types[m.type]["data"].required = True
2766 if type_info["category"] == "struct":
2768 mark_struct_dependencies(type_info["data"], types)
2769 elif type_info["category"] == "funcpointer":
2770 mark_funcpointer_dependencies(type_info["data"], types)
2771 elif type_info["category"] == "bitmask":
2772 mark_bitmask_dependencies(type_info["data"], types)
2774 func = self.funcs[command]
2775 func.required = True
2777 # Pull in return type
2778 if func.type != "void":
2779 self.types[func.type]["data"].required = True
2781 # Analyze parameter dependencies and pull in any type needed.
2782 for p in func.params:
2783 type_info = self.types[p.type]
2785 # Check if we are dealing with a complex type e.g. VkEnum, VkStruct and others.
2786 if "data" not in type_info:
2789 # Mark the complex type as required.
2790 type_info["data"].required = True
2791 if type_info["category"] == "struct":
2792 struct = type_info["data"]
2793 mark_struct_dependencies(struct, self.types)
2794 elif type_info["category"] == "bitmask":
2795 mark_bitmask_dependencies(type_info["data"], self.types)
2797 def _match_object_types(self):
2798 """ Matches each handle with the correct object type. """
2799 # Use upper case comparison for simplicity.
2801 for value in self.enums["VkObjectType"].values:
2802 object_name = "VK" + value.name[len("VK_OBJECT_TYPE"):].replace("_", "")
2803 object_types[object_name] = value.name
2805 for handle in self.handles:
2806 if not handle.is_required():
2808 handle.object_type = object_types.get(handle.name.upper())
2809 if not handle.object_type:
2810 LOGGER.warning("No object type found for {}".format(handle.name))
2812 def _parse_commands(self, root):
2813 """ Parse command section containing the Vulkan function calls. """
2815 commands = root.findall("./commands/")
2817 # As of Vulkan 1.1, various extensions got promoted to Core.
2818 # The old commands (e.g. KHR) are available for backwards compatibility
2819 # and are marked in vk.xml as 'alias' to the non-extension type.
2820 # The registry likes to avoid data duplication, so parameters and other
2821 # metadata need to be looked up from the Core command.
2822 # We parse the alias commands in a second pass.
2824 for command in commands:
2825 alias_name = command.attrib.get("alias")
2827 alias_commands.append(command)
2830 func = VkFunction.from_xml(command, self.types)
2831 funcs[func.name] = func
2833 for command in alias_commands:
2834 alias_name = command.attrib.get("alias")
2835 alias = funcs[alias_name]
2836 func = VkFunction.from_alias(command, alias)
2837 funcs[func.name] = func
2839 # To make life easy for the code generation, separate all function
2840 # calls out in the 3 types of Vulkan functions: device, global and instance.
2844 for func in funcs.values():
2845 if func.is_device_func():
2846 device_funcs.append(func)
2847 elif func.is_global_func():
2848 global_funcs.append(func)
2850 instance_funcs.append(func)
2852 # Sort function lists by name and store them.
2853 self.device_funcs = sorted(device_funcs, key=lambda func: func.name)
2854 self.global_funcs = sorted(global_funcs, key=lambda func: func.name)
2855 self.instance_funcs = sorted(instance_funcs, key=lambda func: func.name)
2857 # The funcs dictionary is used as a convenient way to lookup function
2858 # calls when needed e.g. to adjust member variables.
2859 self.funcs = OrderedDict(sorted(funcs.items()))
2861 def _parse_enums(self, root):
2862 """ Parse enums section or better described as constants section. """
2865 for enum in root.findall("./enums"):
2866 name = enum.attrib.get("name")
2867 _type = enum.attrib.get("type")
2869 if _type in ("enum", "bitmask"):
2870 enums[name] = VkEnum.from_xml(enum)
2872 # If no type is set, we are dealing with API constants.
2873 for value in enum.findall("enum"):
2874 # If enum is an alias, set the value to the alias name.
2875 # E.g. VK_LUID_SIZE_KHR is an alias to VK_LUID_SIZE.
2876 alias = value.attrib.get("alias")
2878 self.consts.append(VkConstant(value.attrib.get("name"), alias))
2880 self.consts.append(VkConstant(value.attrib.get("name"), value.attrib.get("value")))
2882 self.enums = OrderedDict(sorted(enums.items()))
2884 def _process_require_enum(self, enum_elem, ext=None, only_aliased=False):
2885 if "extends" in enum_elem.keys():
2886 enum = self.types[enum_elem.attrib["extends"]]["data"]
2888 # Need to define VkEnumValues which were aliased to by another value. This is necessary
2889 # from VK spec version 1.2.135 where the provisional VK_KHR_ray_tracing extension was
2890 # added which altered VK_NV_ray_tracing's VkEnumValues to alias to the provisional
2893 for _, t in self.types.items():
2894 if t["category"] != "enum":
2898 for value in t["data"].values:
2899 if value.alias == enum_elem.attrib["name"]:
2902 if only_aliased and not aliased:
2905 if "bitpos" in enum_elem.keys():
2906 # We need to add an extra value to an existing enum type.
2907 # E.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG to VkFormatFeatureFlagBits.
2908 enum.create_bitpos(enum_elem.attrib["name"], int(enum_elem.attrib["bitpos"]))
2910 elif "offset" in enum_elem.keys():
2911 # Extensions promoted to Core, have the extension number as part
2912 # of the enum value. Else retrieve from the extension tag.
2913 if enum_elem.attrib.get("extnumber"):
2914 ext_number = int(enum_elem.attrib.get("extnumber"))
2916 ext_number = int(ext.attrib["number"])
2917 offset = int(enum_elem.attrib["offset"])
2918 value = EXT_BASE + (ext_number - 1) * EXT_BLOCK_SIZE + offset
2920 # Deal with negative values.
2921 direction = enum_elem.attrib.get("dir")
2922 if direction is not None:
2925 enum.create_value(enum_elem.attrib["name"], str(value))
2927 elif "value" in enum_elem.keys():
2928 enum.create_value(enum_elem.attrib["name"], enum_elem.attrib["value"])
2929 elif "alias" in enum_elem.keys():
2930 enum.create_alias(enum_elem.attrib["name"], enum_elem.attrib["alias"])
2932 elif "value" in enum_elem.keys():
2933 # Constants are not aliased, no need to add them here, they'll get added later on.
2937 self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["value"]))
2940 def _require_type(type_info):
2941 if type_info.is_alias():
2942 type_info = type_info.alias
2943 type_info.required = True
2944 if type(type_info) == VkStruct:
2945 for member in type_info.members:
2946 if "data" in member.type_info:
2947 VkRegistry._require_type(member.type_info["data"])
2949 def _parse_extensions(self, root):
2950 """ Parse extensions section and pull in any types and commands for this extension. """
2952 exts = root.findall("./extensions/extension")
2954 skipped_exts = UNSUPPORTED_EXTENSIONS.copy()
2956 def process_ext(ext, deferred=False):
2957 ext_name = ext.attrib["name"]
2959 # Set extension name on any functions calls part of this extension as we
2960 # were not aware of the name during initial parsing.
2961 commands = ext.findall("require/command")
2962 for command in commands:
2963 cmd_name = command.attrib["name"]
2964 # Need to verify that the command is defined, and otherwise skip it.
2965 # vkCreateScreenSurfaceQNX is declared in <extensions> but not defined in
2966 # <commands>. A command without a definition cannot be enabled, so it's valid for
2967 # the XML file to handle this, but because of the manner in which we parse the XML
2968 # file we pre-populate from <commands> before we check if a command is enabled.
2969 if cmd_name in self.funcs:
2970 self.funcs[cmd_name].extensions.append(ext_name)
2972 # Some extensions are not ready or have numbers reserved as a place holder.
2973 if ext.attrib["supported"] == "disabled":
2974 LOGGER.debug("Skipping disabled extension: {0}".format(ext_name))
2975 skipped_exts.append(ext_name)
2978 # Defer extensions with 'sortorder' as they are order-dependent for spec-parsing.
2979 if not deferred and "sortorder" in ext.attrib:
2980 deferred_exts.append(ext)
2983 # Disable highly experimental extensions as the APIs are unstable and can
2984 # change between minor Vulkan revisions until API is final and becomes KHR
2986 if "KHX" in ext_name or "NVX" in ext_name:
2987 LOGGER.debug("Skipping experimental extension: {0}".format(ext_name))
2988 skipped_exts.append(ext_name)
2991 # Extensions can define VkEnumValues which alias to provisional extensions. Pre-process
2992 # extensions to define any required VkEnumValues before the platform check below.
2993 for require in ext.findall("require"):
2994 # Extensions can add enum values to Core / extension enums, so add these.
2995 for enum_elem in require.findall("enum"):
2996 self._process_require_enum(enum_elem, ext, only_aliased=True)
2998 platform = ext.attrib.get("platform")
2999 if platform and platform != "win32":
3000 LOGGER.debug("Skipping extensions {0} for platform {1}".format(ext_name, platform))
3001 skipped_exts.append(ext_name)
3004 if not self._is_extension_supported(ext_name):
3005 LOGGER.debug("Skipping unsupported extension: {0}".format(ext_name))
3006 skipped_exts.append(ext_name)
3008 elif "requires" in ext.attrib:
3009 # Check if this extension builds on top of another unsupported extension.
3010 requires = ext.attrib["requires"].split(",")
3011 if len(set(requires).intersection(skipped_exts)) > 0:
3012 skipped_exts.append(ext_name)
3015 LOGGER.debug("Loading extension: {0}".format(ext_name))
3017 # Extensions can define one or more require sections each requiring
3018 # different features (e.g. Vulkan 1.1). Parse each require section
3019 # separately, so we can skip sections we don't want.
3020 for require in ext.findall("require"):
3021 # Extensions can add enum values to Core / extension enums, so add these.
3022 for enum_elem in require.findall("enum"):
3023 self._process_require_enum(enum_elem, ext)
3025 for t in require.findall("type"):
3026 type_info = self.types[t.attrib["name"]]["data"]
3027 self._require_type(type_info)
3028 feature = require.attrib.get("feature")
3029 if feature and not self._is_feature_supported(feature):
3032 required_extension = require.attrib.get("extension")
3033 if required_extension and not self._is_extension_supported(required_extension):
3036 # Pull in any commands we need. We infer types to pull in from the command
3038 for command in require.findall("command"):
3039 cmd_name = command.attrib["name"]
3040 self._mark_command_required(cmd_name)
3043 # Store a list with extensions.
3044 ext_info = {"name" : ext_name, "type" : ext.attrib["type"]}
3045 extensions.append(ext_info)
3048 # Process extensions, allowing for sortorder to defer extension processing
3052 deferred_exts.sort(key=lambda ext: ext.attrib["sortorder"])
3055 for ext in deferred_exts:
3056 process_ext(ext, deferred=True)
3058 # Sort in alphabetical order.
3059 self.extensions = sorted(extensions, key=lambda ext: ext["name"])
3061 def _parse_features(self, root):
3062 """ Parse the feature section, which describes Core commands and types needed. """
3064 for feature in root.findall("./feature"):
3065 feature_name = feature.attrib["name"]
3066 for require in feature.findall("require"):
3067 LOGGER.info("Including features for {0}".format(require.attrib.get("comment")))
3069 if tag.tag == "comment":
3071 elif tag.tag == "command":
3072 if not self._is_feature_supported(feature_name):
3074 name = tag.attrib["name"]
3075 self._mark_command_required(name)
3076 elif tag.tag == "enum":
3077 self._process_require_enum(tag)
3078 elif tag.tag == "type":
3079 name = tag.attrib["name"]
3081 # Skip pull in for vk_platform.h for now.
3082 if name == "vk_platform":
3085 type_info = self.types[name]
3086 type_info["data"].required = True
3088 def _parse_types(self, root):
3089 """ Parse types section, which contains all data types e.g. structs, typedefs etcetera. """
3090 types = root.findall("./types/type")
3102 type_info["category"] = t.attrib.get("category", None)
3103 type_info["requires"] = t.attrib.get("requires", None)
3105 # We parse aliases in a second pass when we know more.
3106 alias = t.attrib.get("alias")
3108 LOGGER.debug("Alias found: {0}".format(alias))
3109 alias_types.append(t)
3112 if type_info["category"] in ["include"]:
3115 if type_info["category"] == "basetype":
3116 name = t.find("name").text
3118 if not t.find("type") is None:
3119 _type = t.find("type").text
3120 basetype = VkBaseType(name, _type)
3121 base_types.append(basetype)
3122 type_info["data"] = basetype
3124 # Basic C types don't need us to define them, but we do need data for them
3125 if type_info["requires"] == "vk_platform":
3126 requires = type_info["requires"]
3127 basic_c = VkBaseType(name, _type, requires=requires)
3128 type_info["data"] = basic_c
3130 if type_info["category"] == "bitmask":
3131 name = t.find("name").text
3132 _type = t.find("type").text
3134 # Most bitmasks have a requires attribute used to pull in
3135 # required '*FlagBits" enum.
3136 requires = type_info["requires"]
3137 bitmask = VkBaseType(name, _type, requires=requires)
3138 bitmasks.append(bitmask)
3139 type_info["data"] = bitmask
3141 if type_info["category"] == "define":
3142 define = VkDefine.from_xml(t)
3143 defines.append(define)
3144 type_info["data"] = define
3146 if type_info["category"] == "enum":
3147 name = t.attrib.get("name")
3148 # The type section only contains enum names, not the actual definition.
3149 # Since we already parsed the enum before, just link it in.
3151 type_info["data"] = self.enums[name]
3152 except KeyError as e:
3153 # Not all enums seem to be defined yet, typically that's for
3154 # ones ending in 'FlagBits' where future extensions may add
3156 type_info["data"] = None
3158 if type_info["category"] == "funcpointer":
3159 funcpointer = VkFunctionPointer.from_xml(t)
3160 funcpointers.append(funcpointer)
3161 type_info["data"] = funcpointer
3163 if type_info["category"] == "handle":
3164 handle = VkHandle.from_xml(t)
3165 handles.append(handle)
3166 type_info["data"] = handle
3168 if type_info["category"] in ["struct", "union"]:
3169 # We store unions among structs as some structs depend
3170 # on unions. The types are very similar in parsing and
3171 # generation anyway. The official Vulkan scripts use
3172 # a similar kind of hack.
3173 struct = VkStruct.from_xml(t)
3174 structs.append(struct)
3175 type_info["data"] = struct
3177 # Name is in general within a name tag else it is an optional
3178 # attribute on the type tag.
3179 name_elem = t.find("name")
3180 if name_elem is not None:
3181 type_info["name"] = name_elem.text
3183 type_info["name"] = t.attrib.get("name", None)
3185 # Store all type data in a shared dictionary, so we can easily
3186 # look up information for a given type. There are no duplicate
3188 self.types[type_info["name"]] = type_info
3190 # Second pass for alias types, so we can retrieve all data from
3191 # the aliased object.
3192 for t in alias_types:
3194 type_info["category"] = t.attrib.get("category")
3195 type_info["name"] = t.attrib.get("name")
3197 alias = t.attrib.get("alias")
3199 if type_info["category"] == "bitmask":
3200 bitmask = VkBaseType(type_info["name"], alias, alias=self.types[alias]["data"])
3201 bitmasks.append(bitmask)
3202 type_info["data"] = bitmask
3204 if type_info["category"] == "enum":
3205 enum = VkEnum.from_alias(t, self.types[alias]["data"])
3206 type_info["data"] = enum
3207 self.enums[enum.name] = enum
3209 if type_info["category"] == "handle":
3210 handle = VkHandle.from_alias(t, self.types[alias]["data"])
3211 handles.append(handle)
3212 type_info["data"] = handle
3214 if type_info["category"] == "struct":
3215 struct = VkStruct.from_alias(t, self.types[alias]["data"])
3216 structs.append(struct)
3217 type_info["data"] = struct
3219 self.types[type_info["name"]] = type_info
3221 # We need detailed type information during code generation
3222 # on structs for alignment reasons. Unfortunately structs
3223 # are parsed among other types, so there is no guarantee
3224 # that any types needed have been parsed already, so set
3226 for struct in structs:
3227 struct.set_type_info(self.types)
3229 # Alias structures have enum values equivalent to those of the
3230 # structure which they are aliased against. we need to ignore alias
3231 # structs when populating the struct extensions list, otherwise we
3232 # will create duplicate case entries.
3236 for structextend in struct.structextends:
3237 s = self.types[structextend]["data"]
3238 s.struct_extensions.append(struct)
3240 # Guarantee everything is sorted, so code generation doesn't have
3241 # to deal with this.
3242 self.base_types = sorted(base_types, key=lambda base_type: base_type.name)
3243 self.bitmasks = sorted(bitmasks, key=lambda bitmask: bitmask.name)
3244 self.defines = defines
3245 self.enums = OrderedDict(sorted(self.enums.items()))
3246 self.funcpointers = sorted(funcpointers, key=lambda fp: fp.name)
3247 self.handles = sorted(handles, key=lambda handle: handle.name)
3248 self.structs = sorted(structs, key=lambda struct: struct.name)
3250 def generate_vulkan_json(f):
3252 f.write(" \"file_format_version\": \"1.0.0\",\n")
3253 f.write(" \"ICD\": {\n")
3254 f.write(" \"library_path\": \".\\\\winevulkan.dll\",\n")
3255 f.write(" \"api_version\": \"{0}\"\n".format(VK_XML_VERSION))
3259 def set_working_directory():
3260 path = os.path.abspath(__file__)
3261 path = os.path.dirname(path)
3264 def download_vk_xml(filename):
3265 url = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/v{0}/xml/vk.xml".format(VK_XML_VERSION)
3266 if not os.path.isfile(filename):
3267 urllib.request.urlretrieve(url, filename)
3270 parser = argparse.ArgumentParser()
3271 parser.add_argument("-v", "--verbose", action="count", default=0, help="increase output verbosity")
3272 parser.add_argument("-x", "--xml", default=None, type=str, help="path to specification XML file")
3274 args = parser.parse_args()
3275 if args.verbose == 0:
3276 LOGGER.setLevel(logging.WARNING)
3277 elif args.verbose == 1:
3278 LOGGER.setLevel(logging.INFO)
3280 LOGGER.setLevel(logging.DEBUG)
3282 set_working_directory()
3287 vk_xml = "vk-{0}.xml".format(VK_XML_VERSION)
3288 download_vk_xml(vk_xml)
3290 registry = VkRegistry(vk_xml)
3291 generator = VkGenerator(registry)
3293 with open(WINE_VULKAN_H, "w") as f:
3294 generator.generate_vulkan_h(f)
3296 with open(WINE_VULKAN_DRIVER_H, "w") as f:
3297 generator.generate_vulkan_driver_h(f)
3299 with open(WINE_VULKAN_THUNKS_H, "w") as f:
3300 generator.generate_thunks_h(f, "wine_")
3302 with open(WINE_VULKAN_THUNKS_C, "w") as f:
3303 generator.generate_thunks_c(f, "wine_")
3305 with open(WINE_VULKAN_JSON, "w") as f:
3306 generate_vulkan_json(f)
3308 with open(WINE_VULKAN_SPEC, "w") as f:
3309 generator.generate_vulkan_spec(f)
3311 with open(WINE_VULKAN_LOADER_SPEC, "w") as f:
3312 generator.generate_vulkan_loader_spec(f)
3314 if __name__ == "__main__":