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.188"
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"
78 WINE_VULKAN_LOADER_THUNKS_C = "loader_thunks.c"
79 WINE_VULKAN_LOADER_THUNKS_H = "loader_thunks.h"
81 # Extension enum values start at a certain offset (EXT_BASE).
82 # Relative to the offset each extension has a block (EXT_BLOCK_SIZE)
84 # Start for a given extension is:
85 # EXT_BASE + (extension_number-1) * EXT_BLOCK_SIZE
89 UNSUPPORTED_EXTENSIONS = [
91 "VK_EXT_headless_surface", # Needs WSI work.
92 "VK_KHR_display", # Needs WSI work.
93 "VK_KHR_surface_protected_capabilities",
96 "VK_AMD_display_native_hdr",
97 "VK_EXT_full_screen_exclusive",
98 "VK_EXT_hdr_metadata", # Needs WSI work.
99 "VK_EXT_pipeline_creation_feedback",
100 "VK_GOOGLE_display_timing",
101 "VK_KHR_external_fence_win32",
102 "VK_KHR_external_semaphore_win32",
103 # Relates to external_semaphore and needs type conversions in bitflags.
104 "VK_KHR_shared_presentable_image", # Needs WSI work.
105 "VK_KHR_win32_keyed_mutex",
106 "VK_NV_external_memory_rdma", # Needs shared resources work.
108 # Extensions for other platforms
109 "VK_EXT_external_memory_dma_buf",
110 "VK_EXT_image_drm_format_modifier",
111 "VK_EXT_physical_device_drm",
112 "VK_KHR_external_fence_fd",
113 "VK_KHR_external_memory_fd",
114 "VK_KHR_external_semaphore_fd",
116 # Extensions which require callback handling
117 "VK_EXT_device_memory_report",
119 # Deprecated extensions
120 "VK_NV_external_memory_capabilities",
121 "VK_NV_external_memory_win32",
124 # Either internal extensions which aren't present on the win32 platform which
125 # winevulkan may nonetheless use, or extensions we want to generate headers for
126 # but not expose to applications (useful for test commits)
127 UNEXPOSED_EXTENSIONS = {
128 "VK_KHR_external_memory_win32",
131 # The Vulkan loader provides entry-points for core functionality and important
132 # extensions. Based on vulkan-1.def this amounts to WSI extensions on 1.0.51.
135 "VK_KHR_display_swapchain",
136 "VK_KHR_get_surface_capabilities2",
139 "VK_KHR_win32_surface",
142 # Some experimental extensions are used by shipping applications so their API is extremely unlikely
143 # to change in a backwards-incompatible way. Allow translation of those extensions with WineVulkan.
144 ALLOWED_X_EXTENSIONS = [
145 "VK_NVX_binary_import",
146 "VK_NVX_image_view_handle",
149 # Functions part of our winevulkan graphics driver interface.
150 # DRIVER_VERSION should be bumped on any change to driver interface
151 # in FUNCTION_OVERRIDES
154 class ThunkType(Enum):
159 # Table of functions for which we have a special implementation.
160 # These are regular device / instance functions for which we need
161 # to do more work compared to a regular thunk or because they are
162 # part of the driver interface.
163 # - dispatch set whether we need a function pointer in the device
164 # / instance dispatch table.
165 # - driver sets whether the API is part of the driver interface.
166 # - thunk sets whether to create a thunk in vulkan_thunks.c.
167 # - NONE means there's a fully custom implementation.
168 # - PUBLIC means the implementation is fully auto generated.
169 # - PRIVATE thunks can be used in custom implementations for
171 # - loader_thunk sets whether to create a thunk for unix_funcs.
172 FUNCTION_OVERRIDES = {
174 "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
175 "vkEnumerateInstanceExtensionProperties" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
176 "vkEnumerateInstanceLayerProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
177 "vkEnumerateInstanceVersion": {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
178 "vkGetInstanceProcAddr": {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
181 "vkCreateDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
182 "vkDestroyInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE },
183 "vkEnumerateDeviceExtensionProperties" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
184 "vkEnumerateDeviceLayerProperties": {"dispatch": True, "driver": False, "thunk": ThunkType.NONE},
185 "vkEnumeratePhysicalDeviceGroups" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
186 "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
187 "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
188 "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
189 "vkGetPhysicalDeviceExternalSemaphoreProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
190 "vkGetPhysicalDeviceImageFormatProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
191 "vkGetPhysicalDeviceProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PUBLIC, "loader_thunk" : ThunkType.PRIVATE},
192 "vkGetPhysicalDeviceProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PUBLIC, "loader_thunk" : ThunkType.PRIVATE},
195 "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
196 "vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
197 "vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
198 "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
199 "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
200 "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
201 "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
202 "vkGetDeviceQueue2" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
205 "vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE},
206 "vkGetPhysicalDeviceSurfaceSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
207 "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
208 "vkGetPhysicalDeviceSurfaceFormatsKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
209 "vkGetPhysicalDeviceSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
211 # VK_KHR_get_surface_capabilities2
212 "vkGetPhysicalDeviceSurfaceCapabilities2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
213 "vkGetPhysicalDeviceSurfaceFormats2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
215 # VK_KHR_win32_surface
216 "vkCreateWin32SurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE},
217 "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
220 "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
221 "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
222 "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
223 "vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
225 # VK_KHR_external_fence_capabilities
226 "vkGetPhysicalDeviceExternalFencePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
228 # VK_KHR_external_memory_capabilities
229 "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
230 "vkGetPhysicalDeviceImageFormatProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
232 # VK_KHR_external_semaphore_capabilities
233 "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
235 # VK_KHR_device_group_creation
236 "vkEnumeratePhysicalDeviceGroupsKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
238 # VK_KHR_device_group
239 "vkGetDeviceGroupSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
240 "vkGetPhysicalDevicePresentRectanglesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
242 # VK_EXT_calibrated_timestamps
243 "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
244 "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
247 "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
248 "vkDestroyDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
250 # VK_EXT_debug_report
251 "vkCreateDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
252 "vkDestroyDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
255 STRUCT_CHAIN_CONVERSIONS = {
256 # Ignore to not confuse host loader.
257 "VkDeviceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO"],
258 "VkInstanceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO"],
262 class Direction(Enum):
263 """ Parameter direction: input, output, input_output. """
269 class VkBaseType(object):
270 def __init__(self, name, _type, alias=None, requires=None):
271 """ Vulkan base type class.
273 VkBaseType is mostly used by Vulkan to define its own
274 base types like VkFlags through typedef out of e.g. uint32_t.
277 name (:obj:'str'): Name of the base type.
278 _type (:obj:'str'): Underlying type
279 alias (bool): type is an alias or not.
280 requires (:obj:'str', optional): Other types required.
281 Often bitmask values pull in a *FlagBits type.
286 self.requires = requires
287 self.required = False
289 def definition(self):
290 # Definition is similar for alias or non-alias as type
291 # is already set to alias.
292 if not self.type is None:
293 return "typedef {0} {1};\n".format(self.type, self.name)
295 return "struct {0};\n".format(self.name)
298 return bool(self.alias)
301 class VkConstant(object):
302 def __init__(self, name, value):
306 def definition(self):
307 text = "#define {0} {1}\n".format(self.name, self.value)
311 class VkDefine(object):
312 def __init__(self, name, value):
317 def from_xml(define):
318 name_elem = define.find("name")
320 if name_elem is None:
321 # <type category="define" name="some_name">some_value</type>
322 name = define.attrib.get("name")
324 # We override behavior of VK_USE_64_BIT_PTR_DEFINES as the default non-dispatchable handle
325 # definition various between 64-bit (uses pointers) and 32-bit (uses uint64_t).
326 # This complicates TRACEs in the thunks, so just use uint64_t.
327 if name == "VK_USE_64_BIT_PTR_DEFINES":
328 value = "#define VK_USE_64_BIT_PTR_DEFINES 0"
331 return VkDefine(name, value)
333 # With a name element the structure is like:
334 # <type category="define"><name>some_name</name>some_value</type>
335 name = name_elem.text
337 # Perform minimal parsing for Vulkan constants, which we don't need, but are referenced
338 # elsewhere in vk.xml.
339 # - VK_API_VERSION is a messy, deprecated constant and we don't want generate code for it.
340 # - AHardwareBuffer/ANativeWindow are forward declarations for Android types, which leaked
341 # into the define region.
342 if name in ["VK_API_VERSION", "AHardwareBuffer", "ANativeWindow", "CAMetalLayer"]:
343 return VkDefine(name, None)
345 # The body of the define is basically unstructured C code. It is not meant for easy parsing.
346 # Some lines contain deprecated values or comments, which we try to filter out.
348 for line in define.text.splitlines():
349 # Skip comments or deprecated values.
356 if child.tail is not None:
357 # Split comments for VK_API_VERSION_1_0 / VK_API_VERSION_1_1
358 if "//" in child.tail:
359 value += child.tail.split("//")[0]
363 return VkDefine(name, value.rstrip(' '))
365 def definition(self):
366 if self.value is None:
369 # Nothing to do as the value was already put in the right form during parsing.
370 return "{0}\n".format(self.value)
373 class VkEnum(object):
374 def __init__(self, name, bitwidth, alias=None):
375 if not bitwidth in [32, 64]:
376 LOGGER.error("unknown bitwidth {0} for {1}".format(bitwidth, name))
378 self.bitwidth = bitwidth
379 self.values = [] if alias == None else alias.values
380 self.required = False
385 def from_alias(enum, alias):
386 name = enum.attrib.get("name")
387 aliasee = VkEnum(name, alias.bitwidth, alias=alias)
389 alias.add_aliased_by(aliasee)
394 name = enum.attrib.get("name")
395 bitwidth = int(enum.attrib.get("bitwidth", "32"))
396 result = VkEnum(name, bitwidth)
398 for v in enum.findall("enum"):
399 value_name = v.attrib.get("name")
400 # Value is either a value or a bitpos, only one can exist.
401 value = v.attrib.get("value")
402 alias_name = v.attrib.get("alias")
404 result.create_alias(value_name, alias_name)
406 result.create_value(value_name, value)
409 result.create_bitpos(value_name, int(v.attrib.get("bitpos")))
412 # vulkan.h contains a *_MAX_ENUM value set to 32-bit at the time of writing,
413 # which is to prepare for extensions as they can add values and hence affect
414 # the size definition.
415 max_name = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2', name).upper() + "_MAX_ENUM"
416 result.create_value(max_name, "0x7fffffff")
420 def create_alias(self, name, alias_name):
421 """ Create an aliased value for this enum """
422 # Older GCC versions need a literal to initialize a static const uint64_t
423 # which is what we use for 64bit bitmasks.
424 if self.bitwidth == 64:
425 alias = next(x for x in self.values if x.name == alias_name)
426 self.add(VkEnumValue(name, self.bitwidth, value=alias.value, hex=alias.hex, alias=alias_name))
428 self.add(VkEnumValue(name, self.bitwidth, alias=alias_name))
430 def create_value(self, name, value):
431 """ Create a new value for this enum """
432 # Some values are in hex form. We want to preserve the hex representation
433 # at least when we convert back to a string. Internally we want to use int.
435 self.add(VkEnumValue(name, self.bitwidth, value=int(value, 0), hex=hex))
437 def create_bitpos(self, name, pos):
438 """ Create a new bitmask value for this enum """
439 self.add(VkEnumValue(name, self.bitwidth, value=(1 << pos), hex=True))
441 def add(self, value):
442 """ Add a value to enum. """
444 # Extensions can add new enum values. When an extension is promoted to Core
445 # the registry defines the value twice once for old extension and once for
446 # new Core features. Add the duplicate if it's explicitly marked as an
447 # alias, otherwise ignore it.
448 for v in self.values:
449 if not value.is_alias() and v.value == value.value:
450 LOGGER.debug("Adding duplicate enum value {0} to {1}".format(v, self.name))
452 # Avoid adding duplicate aliases multiple times
453 if not any(x.name == value.name for x in self.values):
454 self.values.append(value)
456 def definition(self):
460 default_value = 0x7ffffffe if self.bitwidth == 32 else 0xfffffffffffffffe
462 # Print values sorted, values can have been added in a random order.
463 values = sorted(self.values, key=lambda value: value.value if value.value is not None else default_value)
465 if self.bitwidth == 32:
466 text = "typedef enum {0}\n{{\n".format(self.name)
468 text += " {0},\n".format(value.definition())
469 text += "}} {0};\n".format(self.name)
470 elif self.bitwidth == 64:
471 text = "typedef VkFlags64 {0};\n\n".format(self.name)
473 text += "static const {0} {1};\n".format(self.name, value.definition())
475 for aliasee in self.aliased_by:
476 text += "typedef {0} {1};\n".format(self.name, aliasee.name)
482 return bool(self.alias)
484 def add_aliased_by(self, aliasee):
485 self.aliased_by.append(aliasee)
488 class VkEnumValue(object):
489 def __init__(self, name, bitwidth, value=None, hex=False, alias=None):
491 self.bitwidth = bitwidth
497 postfix = "ull" if self.bitwidth == 64 else ""
498 if self.is_alias() and self.value == None:
499 return "{0}={1}".format(self.name, self.alias)
500 return "{0}={1}{2}".format(self.name, self.value, postfix)
502 def definition(self):
503 """ Convert to text definition e.g. VK_FOO = 1 """
504 postfix = "ull" if self.bitwidth == 64 else ""
505 if self.is_alias() and self.value == None:
506 return "{0} = {1}".format(self.name, self.alias)
508 # Hex is commonly used for FlagBits and sometimes within
509 # a non-FlagBits enum for a bitmask value as well.
511 return "{0} = 0x{1:08x}{2}".format(self.name, self.value, postfix)
513 return "{0} = {1}{2}".format(self.name, self.value, postfix)
516 return self.alias is not None
519 class VkFunction(object):
520 def __init__(self, _type=None, name=None, params=[], alias=None):
521 self.extensions = set()
527 # For some functions we need some extra metadata from FUNCTION_OVERRIDES.
528 func_info = FUNCTION_OVERRIDES.get(self.name, None)
529 self.dispatch = func_info["dispatch"] if func_info else True
530 self.driver = func_info["driver"] if func_info else False
531 self.thunk_type = func_info["thunk"] if func_info else ThunkType.PUBLIC
532 self.loader_thunk_type = func_info["loader_thunk"] if func_info and "loader_thunk" in func_info else ThunkType.PUBLIC
534 # Required is set while parsing which APIs and types are required
535 # and is used by the code generation.
536 self.required = True if func_info else False
539 def from_alias(command, alias):
540 """ Create VkFunction from an alias command.
543 command: xml data for command
544 alias (VkFunction): function to use as a base for types / parameters.
549 func_name = command.attrib.get("name")
550 func_type = alias.type
551 params = alias.params
553 return VkFunction(_type=func_type, name=func_name, params=params, alias=alias)
556 def from_xml(command, types):
557 proto = command.find("proto")
558 func_name = proto.find("name").text
559 func_type = proto.find("type").text
562 for param in command.findall("param"):
563 vk_param = VkParam.from_xml(param, types)
564 params.append(vk_param)
566 return VkFunction(_type=func_type, name=func_name, params=params)
568 def get_conversions(self):
569 """ Get a list of conversion functions required for this function if any.
570 Parameters which are structures may require conversion between win32
571 and the host platform. This function returns a list of conversions
576 for param in self.params:
577 convs = param.get_conversions()
578 if convs is not None:
579 conversions.extend(convs)
584 return bool(self.alias)
586 def is_core_func(self):
587 """ Returns whether the function is a Vulkan core function.
588 Core functions are APIs defined by the Vulkan spec to be part of the
589 Core API as well as several KHR WSI extensions.
592 if not self.extensions:
595 return any(ext in self.extensions for ext in CORE_EXTENSIONS)
597 def is_device_func(self):
598 # If none of the other, it must be a device function
599 return not self.is_global_func() and not self.is_instance_func() and not self.is_phys_dev_func()
601 def is_driver_func(self):
602 """ Returns if function is part of Wine driver interface. """
605 def is_global_func(self):
606 # Treat vkGetInstanceProcAddr as a global function as it
607 # can operate with NULL for vkInstance.
608 if self.name == "vkGetInstanceProcAddr":
610 # Global functions are not passed a dispatchable object.
611 elif self.params[0].is_dispatchable():
615 def is_instance_func(self):
616 # Instance functions are passed VkInstance.
617 if self.params[0].type == "VkInstance":
621 def is_phys_dev_func(self):
622 # Physical device functions are passed VkPhysicalDevice.
623 if self.params[0].type == "VkPhysicalDevice":
627 def is_required(self):
630 def needs_conversion(self):
631 """ Check if the function needs any input/output type conversion.
632 Functions need input/output conversion if struct parameters have
633 alignment differences between Win32 and Linux 32-bit.
636 for p in self.params:
637 if p.needs_conversion():
638 LOGGER.debug("Parameter {0} to {1} requires conversion".format(p.name, self.name))
643 def needs_unwrapping(self):
644 """ Check if the function needs any input/output type unwrapping.
645 Functions need input/output unwrapping if struct parameters have
649 for p in self.params:
650 if p.needs_unwrapping():
655 def needs_dispatch(self):
658 def needs_thunk(self):
659 return self.thunk_type != ThunkType.NONE
661 def needs_private_thunk(self):
662 return self.thunk_type == ThunkType.PRIVATE
664 def needs_exposing(self):
665 # The function needs exposed if at-least one extension isn't both UNSUPPORTED and UNEXPOSED
666 return self.is_required() and (not self.extensions or not self.extensions.issubset(UNEXPOSED_EXTENSIONS))
668 def pfn(self, prefix="p", call_conv=None, conv=False):
669 """ Create function pointer. """
672 pfn = "{0} ({1} *{2}_{3})(".format(self.type, call_conv, prefix, self.name)
674 pfn = "{0} (*{1}_{2})(".format(self.type, prefix, self.name)
676 for i, param in enumerate(self.params):
678 pfn += param.const + " "
681 if conv and param.needs_conversion():
684 if param.is_pointer():
685 pfn += " " + param.pointer
687 if param.array_len is not None:
688 pfn += "[{0}]".format(param.array_len)
690 if i < len(self.params) - 1:
695 def prototype(self, call_conv=None, prefix=None, postfix=None):
696 """ Generate prototype for given function.
699 call_conv (str, optional): calling convention e.g. WINAPI
700 prefix (str, optional): prefix to append prior to function name e.g. vkFoo -> wine_vkFoo
701 postfix (str, optional): text to append after function name but prior to semicolon e.g. DECLSPEC_HIDDEN
704 proto = "{0}".format(self.type)
706 if call_conv is not None:
707 proto += " {0}".format(call_conv)
709 if prefix is not None:
710 proto += " {0}{1}(".format(prefix, self.name)
712 proto += " {0}(".format(self.name)
714 # Add all the parameters.
715 proto += ", ".join([p.definition() for p in self.params])
717 if postfix is not None:
718 proto += ") {0}".format(postfix)
727 if not self.needs_private_thunk():
728 body += " {0}".format(self.trace())
730 params = ", ".join([p.variable(conv=False) for p in self.params])
732 # Call the native Vulkan function.
733 if self.type == "void":
734 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
736 body += " return {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
740 def loader_body(self):
743 params = ", ".join([p.name for p in self.params])
745 # Call the function from unix_funcs.
746 if self.type == "void":
747 body += " unix_funcs->p_{0}({1});\n".format(self.name, params)
749 body += " return unix_funcs->p_{0}({1});\n".format(self.name, params)
753 def body_conversion(self, conv):
756 # Declare a variable to hold the result for non-void functions.
757 if self.type != "void":
758 body += " {0} result;\n".format(self.type)
760 # Declare any tmp parameters for conversion.
761 for p in self.params:
762 if p.needs_conversion() and conv:
763 if p.is_dynamic_array():
764 body += " {0}_host *{1}_host;\n".format(p.type, p.name)
766 body += " {0}_host {1}_host;\n".format(p.type, p.name)
767 elif p.needs_unwrapping():
768 if p.is_dynamic_array():
769 body += " {0} *{1}_host;\n".format(p.type, p.name)
771 body += " {0} {1}_host;\n".format(p.type, p.name)
773 if not self.needs_private_thunk():
774 body += " {0}\n".format(self.trace())
776 # Call any win_to_host conversion calls.
777 for p in self.params:
778 if p.needs_input_conversion() and (p.needs_unwrapping() or conv):
779 body += p.copy(Direction.INPUT)
781 # Build list of parameters containing converted and non-converted parameters.
782 # The param itself knows if conversion is needed and applies it when we set conv=True.
783 params = ", ".join([p.variable(conv=conv) for p in self.params])
785 # Call the native Vulkan function.
786 if self.type == "void":
787 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
789 body += " result = {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
793 # Call any host_to_win conversion calls.
794 for p in self.params:
795 if not p.needs_output_conversion():
798 body += p.copy(Direction.OUTPUT)
800 # Perform any required cleanups. Most of these are for array functions.
801 for p in self.params:
802 if p.needs_free() and (p.needs_unwrapping() or conv):
805 # Finally return the result.
806 if self.type != "void":
807 body += " return result;\n"
811 def spec(self, prefix=None, symbol=None):
812 """ Generate spec file entry for this function.
815 prefix (str, optional): prefix to prepend to entry point name.
816 symbol (str, optional): allows overriding the name of the function implementing the entry point.
820 params = " ".join([p.spec() for p in self.params])
821 if prefix is not None:
822 spec += "@ stdcall -private {0}{1}({2})".format(prefix, self.name, params)
824 spec += "@ stdcall {0}({1})".format(self.name, params)
826 if symbol is not None:
832 def stub(self, call_conv=None, prefix=None):
833 stub = self.prototype(call_conv=call_conv, prefix=prefix)
835 stub += " {0}".format(self.trace(message="stub: ", trace_func="FIXME"))
837 if self.type == "VkResult":
838 stub += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
839 elif self.type == "VkBool32":
840 stub += " return VK_FALSE;\n"
841 elif self.type == "PFN_vkVoidFunction":
842 stub += " return NULL;\n"
847 def thunk(self, call_conv=None, prefix=None):
848 thunk = self.prototype(call_conv=call_conv, prefix=prefix)
851 if self.needs_conversion():
852 thunk += "#if defined(USE_STRUCT_CONVERSION)\n"
853 thunk += self.body_conversion(conv=True)
855 if self.needs_unwrapping():
856 thunk += self.body_conversion(conv=False)
860 elif self.needs_unwrapping():
861 thunk += self.body_conversion(conv=False)
868 def loader_thunk(self, prefix=None):
869 thunk = self.prototype(call_conv="WINAPI", prefix=prefix)
871 thunk += self.loader_body()
875 def trace(self, message=None, trace_func=None):
876 """ Create a trace string including all parameters.
879 message (str, optional): text to print at start of trace message e.g. 'stub: '
880 trace_func (str, optional): used to override trace function e.g. FIXME, printf, etcetera.
882 if trace_func is not None:
883 trace = "{0}(\"".format(trace_func)
887 if message is not None:
890 # First loop is for all the format strings.
891 trace += ", ".join([p.format_string() for p in self.params])
894 # Second loop for parameter names and optional conversions.
895 for param in self.params:
896 if param.format_conv is not None:
897 trace += ", " + param.format_conv.format(param.name)
899 trace += ", {0}".format(param.name)
905 class VkFunctionPointer(object):
906 def __init__(self, _type, name, members, forward_decls):
908 self.members = members
910 self.required = False
911 self.forward_decls = forward_decls
914 def from_xml(funcpointer):
918 for t in funcpointer.findall("type"):
920 # <type>void</type>* pUserData,
921 # Parsing of the tail (anything past </type>) is tricky since there
922 # can be other data on the next line like: const <type>int</type>..
924 const = True if begin and "const" in begin else False
926 lines = t.tail.split(",\n")
927 if lines[0][0] == "*":
929 name = lines[0][1:].strip()
932 name = lines[0].strip()
934 # Filter out ); if it is contained.
935 name = name.partition(");")[0]
937 # If tail encompasses multiple lines, assign the second line to begin
940 begin = lines[1].strip()
944 members.append(VkMember(const=const, _type=_type, pointer=pointer, name=name))
946 _type = funcpointer.text
947 name = funcpointer.find("name").text
948 if "requires" in funcpointer.attrib:
949 forward_decls = funcpointer.attrib.get("requires").split(",")
952 return VkFunctionPointer(_type, name, members, forward_decls)
954 def definition(self):
956 # forward declare required structs
957 for decl in self.forward_decls:
958 text += "typedef struct {0} {0};\n".format(decl)
960 text += "{0} {1})(\n".format(self.type, self.name)
963 if len(self.members) > 0:
964 for m in self.members:
966 text += " " + m.definition()
969 text += ",\n " + m.definition()
971 # Just make the compiler happy by adding a void parameter.
979 class VkHandle(object):
980 def __init__(self, name, _type, parent, alias=None):
985 self.required = False
986 self.object_type = None
989 def from_alias(handle, alias):
990 name = handle.attrib.get("name")
991 return VkHandle(name, alias.type, alias.parent, alias=alias)
994 def from_xml(handle):
995 name = handle.find("name").text
996 _type = handle.find("type").text
997 parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice.
998 return VkHandle(name, _type, parent)
1000 def dispatch_table(self):
1001 if not self.is_dispatchable():
1004 if self.parent is None:
1005 # Should only happen for VkInstance
1007 elif self.name == "VkDevice":
1008 # VkDevice has VkInstance as a parent, but has its own dispatch table.
1010 elif self.parent in ["VkInstance", "VkPhysicalDevice"]:
1011 return "instance->funcs"
1012 elif self.parent in ["VkDevice", "VkCommandPool"]:
1013 return "device->funcs"
1015 LOGGER.error("Unhandled dispatchable parent: {0}".format(self.parent))
1017 def definition(self):
1018 """ Generates handle definition e.g. VK_DEFINE_HANDLE(vkInstance) """
1020 # Legacy types are typedef'ed to the new type if they are aliases.
1022 return "typedef {0} {1};\n".format(self.alias.name, self.name)
1024 return "{0}({1})\n".format(self.type, self.name)
1027 return self.alias is not None
1029 def is_dispatchable(self):
1030 """ Some handles like VkInstance, VkDevice are dispatchable objects,
1031 which means they contain a dispatch table of function pointers.
1033 return self.type == "VK_DEFINE_HANDLE"
1035 def is_required(self):
1036 return self.required
1038 def native_handle(self, name):
1039 """ Provide access to the native handle of a wrapped object. """
1041 if self.name == "VkCommandPool":
1042 return "wine_cmd_pool_from_handle({0})->command_pool".format(name)
1043 if self.name == "VkDebugUtilsMessengerEXT":
1044 return "wine_debug_utils_messenger_from_handle({0})->debug_messenger".format(name)
1045 if self.name == "VkDebugReportCallbackEXT":
1046 return "wine_debug_report_callback_from_handle({0})->debug_callback".format(name)
1047 if self.name == "VkSurfaceKHR":
1048 return "wine_surface_from_handle({0})->surface".format(name)
1050 native_handle_name = None
1052 if self.name == "VkCommandBuffer":
1053 native_handle_name = "command_buffer"
1054 if self.name == "VkDevice":
1055 native_handle_name = "device"
1056 if self.name == "VkInstance":
1057 native_handle_name = "instance"
1058 if self.name == "VkPhysicalDevice":
1059 native_handle_name = "phys_dev"
1060 if self.name == "VkQueue":
1061 native_handle_name = "queue"
1063 if native_handle_name:
1064 return "{0}->{1}".format(name, native_handle_name)
1066 if self.is_dispatchable():
1067 LOGGER.error("Unhandled native handle for: {0}".format(self.name))
1070 def driver_handle(self, name):
1071 """ Provide access to the handle that should be passed to the wine driver """
1073 if self.name == "VkSurfaceKHR":
1074 return "wine_surface_from_handle({0})->driver_surface".format(name)
1076 return self.native_handle(name)
1078 def is_wrapped(self):
1079 return self.native_handle("test") is not None
1081 def needs_conversion(self):
1084 def needs_unwrapping(self):
1085 return self.is_wrapped()
1087 class VkMember(object):
1088 def __init__(self, const=False, struct_fwd_decl=False,_type=None, pointer=None, name=None, array_len=None,
1089 dyn_array_len=None, optional=False, values=None, object_type=None, bit_width=None):
1091 self.struct_fwd_decl = struct_fwd_decl
1093 self.pointer = pointer
1095 self.type_info = None
1096 self.array_len = array_len
1097 self.dyn_array_len = dyn_array_len
1098 self.optional = optional
1099 self.values = values
1100 self.object_type = object_type
1101 self.bit_width = bit_width
1103 def __eq__(self, other):
1104 """ Compare member based on name against a string.
1106 This method is for convenience by VkStruct, which holds a number of members and needs quick checking
1107 if certain members exist.
1110 return self.name == other
1113 return "{0} {1} {2} {3} {4} {5} {6}".format(self.const, self.struct_fwd_decl, self.type, self.pointer,
1114 self.name, self.array_len, self.dyn_array_len)
1117 def from_xml(member):
1118 """ Helper function for parsing a member tag within a struct or union. """
1120 name_elem = member.find("name")
1121 type_elem = member.find("type")
1124 struct_fwd_decl = False
1130 values = member.get("values")
1133 if "const" in member.text:
1136 # Some members contain forward declarations:
1137 # - VkBaseInstructure has a member "const struct VkBaseInStructure *pNext"
1138 # - VkWaylandSurfaceCreateInfoKHR has a member "struct wl_display *display"
1139 if "struct" in member.text:
1140 struct_fwd_decl = True
1142 if type_elem is not None:
1143 member_type = type_elem.text
1144 if type_elem.tail is not None:
1145 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1147 # Name of other member within, which stores the number of
1148 # elements pointed to be by this member.
1149 dyn_array_len = member.get("len")
1151 # Some members are optional, which is important for conversion code e.g. not dereference NULL pointer.
1152 optional = True if member.get("optional") else False
1154 # Usually we need to allocate memory for dynamic arrays. We need to do the same in a few other cases
1155 # like for VkCommandBufferBeginInfo.pInheritanceInfo. Just threat such cases as dynamic arrays of
1156 # size 1 to simplify code generation.
1157 if dyn_array_len is None and pointer is not None:
1160 # Some members are arrays, attempt to parse these. Formats include:
1161 # <member><type>char</type><name>extensionName</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
1162 # <member><type>uint32_t</type><name>foo</name>[4]</member>
1163 if name_elem.tail and name_elem.tail[0] == '[':
1164 LOGGER.debug("Found array type")
1165 enum_elem = member.find("enum")
1166 if enum_elem is not None:
1167 array_len = enum_elem.text
1169 # Remove brackets around length
1170 array_len = name_elem.tail.strip("[]")
1172 object_type = member.get("objecttype", None)
1174 # Some members are bit field values:
1175 # <member><type>uint32_t</type> <name>mask</name>:8</member>
1176 if name_elem.tail and name_elem.tail[0] == ':':
1177 LOGGER.debug("Found bit field")
1178 bit_width = int(name_elem.tail[1:])
1180 return VkMember(const=const, struct_fwd_decl=struct_fwd_decl, _type=member_type, pointer=pointer, name=name_elem.text,
1181 array_len=array_len, dyn_array_len=dyn_array_len, optional=optional, values=values, object_type=object_type, bit_width=bit_width)
1183 def copy(self, input, output, direction, conv):
1184 """ Helper method for use by conversion logic to generate a C-code statement to copy this member.
1185 - `conv` indicates whether the statement is in a struct alignment conversion path. """
1187 if (conv and self.needs_conversion()) or self.needs_unwrapping():
1188 if self.is_dynamic_array():
1189 if direction == Direction.OUTPUT:
1190 LOGGER.warn("TODO: implement copying of returnedonly dynamic array for {0}.{1}".format(self.type, self.name))
1192 # Array length is either a variable name (string) or an int.
1193 count = self.dyn_array_len if isinstance(self.dyn_array_len, int) else "{0}{1}".format(input, self.dyn_array_len)
1194 return "{0}{1} = convert_{2}_array_win_to_host({3}{1}, {4});\n".format(output, self.name, self.type, input, count)
1195 elif self.is_static_array():
1196 count = self.array_len
1197 if direction == Direction.OUTPUT:
1198 # Needed by VkMemoryHeap.memoryHeaps
1199 return "convert_{0}_static_array_host_to_win({2}{1}, {3}{1}, {4});\n".format(self.type, self.name, input, output, count)
1201 # Nothing needed this yet.
1202 LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
1203 elif self.is_handle() and self.needs_unwrapping():
1204 if direction == Direction.OUTPUT:
1205 LOGGER.err("OUTPUT parameter {0}.{1} cannot be unwrapped".format(self.type, self.name))
1207 handle = self.type_info["data"]
1208 return "{0}{1} = {2};\n".format(output, self.name, handle.driver_handle("{0}{1}".format(input, self.name)))
1209 elif self.is_generic_handle():
1210 if direction == Direction.OUTPUT:
1211 LOGGER.err("OUTPUT parameter {0}.{1} cannot be unwrapped".format(self.type, self.name))
1213 return "{0}{1} = wine_vk_unwrap_handle({2}{3}, {2}{1});\n".format(output, self.name, input, self.object_type)
1215 if direction == Direction.OUTPUT:
1216 return "convert_{0}_host_to_win(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
1218 return "convert_{0}_win_to_host(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
1219 elif self.is_static_array():
1220 bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type)
1221 return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count)
1223 return "{0}{1} = {2}{1};\n".format(output, self.name, input)
1225 def free(self, location, conv):
1226 """ Helper method for use by conversion logic to generate a C-code statement to free this member. """
1228 if not self.needs_unwrapping() and not conv:
1231 # Add a cast to ignore const on conversion structs we allocated ourselves.
1232 # sample expected output: (VkSparseMemoryBind_host *)
1234 cast = "(" + self.type
1235 if self.needs_conversion() and conv:
1241 if self.is_dynamic_array():
1242 count = self.dyn_array_len if isinstance(self.dyn_array_len, int) else "{0}{1}".format(location, self.dyn_array_len)
1243 if self.is_struct() and self.type_info["data"].returnedonly:
1244 # For returnedonly, counts is stored in a pointer.
1245 return "free_{0}_array({1}{2}{3}, *{4});\n".format(self.type, cast, location, self.name, count)
1247 return "free_{0}_array({1}{2}{3}, {4});\n".format(self.type, cast, location, self.name, count)
1249 # We are operating on a single structure. Some structs (very rare) contain dynamic members,
1250 # which would need freeing.
1251 if self.needs_free():
1252 return "free_{0}({1}&{2}{3});\n".format(self.type, cast, location, self.name)
1255 def definition(self, align=False, conv=False):
1256 """ Generate prototype for given function.
1259 align (bool, optional): Enable alignment if a type needs it. This adds WINE_VK_ALIGN(8) to a member.
1260 conv (bool, optional): Enable conversion if a type needs it. This appends '_host' to the name.
1267 if self.is_struct_forward_declaration():
1270 if conv and self.is_struct():
1271 text += "{0}_host".format(self.type)
1275 if self.is_pointer():
1276 text += " {0}{1}".format(self.pointer, self.name)
1278 if align and self.needs_alignment():
1279 text += " WINE_VK_ALIGN(8) " + self.name
1281 text += " " + self.name
1283 if self.is_static_array():
1284 text += "[{0}]".format(self.array_len)
1286 if self.is_bit_field():
1287 text += ":{}".format(self.bit_width)
1291 def get_conversions(self):
1292 """ Return any conversion description for this member and its children when conversion is needed. """
1294 # Check if we need conversion either for this member itself or for any child members
1295 # in case member represents a struct.
1296 if not self.needs_conversion() and not self.needs_unwrapping():
1301 # Collect any conversion for any member structs.
1302 if self.is_struct():
1303 struct = self.type_info["data"]
1305 m.needs_struct_extensions_conversion()
1306 if m.needs_conversion() or m.needs_unwrapping():
1307 conversions.extend(m.get_conversions())
1309 struct.needs_struct_extensions_conversion()
1310 direction = Direction.OUTPUT if struct.returnedonly else Direction.INPUT
1311 elif self.is_handle() or self.is_generic_handle():
1312 direction = Direction.INPUT
1314 operand = self.type_info["data"]
1315 if self.is_dynamic_array():
1316 conversions.append(ConversionFunction(False, True, direction, operand))
1317 elif self.is_static_array():
1318 conversions.append(ConversionFunction(True, False, direction, operand))
1320 conversions.append(ConversionFunction(False, False, direction, operand))
1322 if self.needs_free():
1323 conversions.append(FreeFunction(self.is_dynamic_array(), operand))
1330 def is_dynamic_array(self):
1331 """ Returns if the member is an array element.
1332 Vulkan uses this for dynamically sized arrays for which
1333 there is a 'count' parameter.
1335 return self.dyn_array_len is not None
1337 def is_handle(self):
1338 return self.type_info["category"] == "handle"
1340 def is_pointer(self):
1341 return self.pointer is not None
1343 def is_static_array(self):
1344 """ Returns if the member is an array.
1345 Vulkan uses this often for fixed size arrays in which the
1346 length is part of the member.
1348 return self.array_len is not None
1350 def is_struct(self):
1351 return self.type_info["category"] == "struct"
1353 def is_struct_forward_declaration(self):
1354 return self.struct_fwd_decl
1357 return self.type_info["category"] == "union"
1359 def is_generic_handle(self):
1360 """ Returns True if the member is a unit64_t containing
1361 a handle with a separate object type
1364 return self.object_type != None and self.type == "uint64_t"
1366 def is_bit_field(self):
1367 return self.bit_width is not None
1369 def needs_alignment(self):
1370 """ Check if this member needs alignment for 64-bit data.
1371 Various structures need alignment on 64-bit variables due
1372 to compiler differences on 32-bit between Win32 and Linux.
1375 if self.is_pointer():
1377 elif self.type == "size_t":
1379 elif self.type in ["uint64_t", "VkDeviceSize"]:
1381 elif self.is_struct():
1382 struct = self.type_info["data"]
1383 return struct.needs_alignment()
1384 elif self.is_handle():
1385 # Dispatchable handles are pointers to objects, while
1386 # non-dispatchable are uint64_t and hence need alignment.
1387 handle = self.type_info["data"]
1388 return False if handle.is_dispatchable() else True
1391 def needs_conversion(self):
1392 """ Structures requiring alignment, need conversion between win32 and host. """
1394 if not self.is_struct():
1397 struct = self.type_info["data"]
1398 return struct.needs_conversion()
1400 def needs_unwrapping(self):
1401 """ Structures with wrapped handles need unwrapping. """
1403 if self.is_struct():
1404 struct = self.type_info["data"]
1405 return struct.needs_unwrapping()
1407 if self.is_handle():
1408 handle = self.type_info["data"]
1409 return handle.is_wrapped()
1411 if self.is_generic_handle():
1416 def needs_free(self):
1417 if not self.needs_conversion() and not self.needs_unwrapping():
1420 if self.is_dynamic_array():
1423 # TODO: some non-pointer structs and optional pointer structs may need freeing,
1424 # though none of this type have been encountered yet.
1427 def needs_struct_extensions_conversion(self):
1428 if not self.is_struct():
1431 struct = self.type_info["data"]
1432 return struct.needs_struct_extensions_conversion()
1434 def set_type_info(self, type_info):
1435 """ Helper function to set type information from the type registry.
1436 This is needed, because not all type data is available at time of
1439 self.type_info = type_info
1442 class VkParam(object):
1443 """ Helper class which describes a parameter to a function call. """
1445 def __init__(self, type_info, const=None, pointer=None, name=None, array_len=None, dyn_array_len=None, object_type=None):
1448 self.array_len = array_len
1449 self.dyn_array_len = dyn_array_len
1450 self.pointer = pointer
1451 self.object_type = object_type
1452 self.type_info = type_info
1453 self.type = type_info["name"] # For convenience
1454 self.handle = type_info["data"] if type_info["category"] == "handle" else None
1455 self.struct = type_info["data"] if type_info["category"] == "struct" else None
1457 self._set_direction()
1458 self._set_format_string()
1459 self._set_conversions()
1462 return "{0} {1} {2} {3} {4} {5}".format(self.const, self.type, self.pointer, self.name, self.array_len, self.dyn_array_len)
1465 def from_xml(param, types):
1466 """ Helper function to create VkParam from xml. """
1468 # Parameter parsing is slightly tricky. All the data is contained within
1469 # a param tag, but some data is within subtags while others are text
1470 # before or after the type tag.
1472 # <param>const <type>char</type>* <name>pLayerName</name></param>
1474 name_elem = param.find("name")
1476 name = name_elem.text
1477 # Tail contains array length e.g. for blendConstants param of vkSetBlendConstants
1478 if name_elem.tail is not None:
1479 array_len = name_elem.tail.strip("[]")
1481 # Name of other parameter in function prototype, which stores the number of
1482 # elements pointed to be by this parameter.
1483 dyn_array_len = param.get("len", None)
1485 const = param.text.strip() if param.text else None
1486 type_elem = param.find("type")
1487 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1489 # Some uint64_t are actually handles with a separate type param
1490 object_type = param.get("objecttype", None)
1492 # Since we have parsed all types before hand, this should not happen.
1493 type_info = types.get(type_elem.text, None)
1494 if type_info is None:
1495 LOGGER.err("type info not found for: {0}".format(type_elem.text))
1497 return VkParam(type_info, const=const, pointer=pointer, name=name, array_len=array_len, dyn_array_len=dyn_array_len, object_type=object_type)
1499 def _set_conversions(self):
1500 """ Internal helper function to configure any needed conversion functions. """
1502 self.free_func = None
1503 self.input_conv = None
1504 self.output_conv = None
1505 if not self.needs_conversion() and not self.needs_unwrapping():
1508 operand = self.struct if self.is_struct() else self.handle
1510 # Input functions require win to host conversion.
1511 if self._direction in [Direction.INPUT, Direction.INPUT_OUTPUT]:
1512 self.input_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.INPUT, operand)
1514 # Output functions require host to win conversion.
1515 if self._direction in [Direction.INPUT_OUTPUT, Direction.OUTPUT]:
1516 self.output_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.OUTPUT, operand)
1518 # Dynamic arrays, but also some normal structs (e.g. VkCommandBufferBeginInfo) need memory
1519 # allocation and thus some cleanup.
1520 if self.is_dynamic_array() or self.struct.needs_free():
1521 self.free_func = FreeFunction(self.is_dynamic_array(), operand)
1523 def _set_direction(self):
1524 """ Internal helper function to set parameter direction (input/output/input_output). """
1526 # The parameter direction needs to be determined from hints in vk.xml like returnedonly,
1527 # parameter constness and other heuristics.
1528 # For now we need to get this right for structures as we need to convert these, we may have
1529 # missed a few other edge cases (e.g. count variables).
1530 # See also https://github.com/KhronosGroup/Vulkan-Docs/issues/610
1532 if not self.is_pointer():
1533 self._direction = Direction.INPUT
1534 elif self.is_const() and self.is_pointer():
1535 self._direction = Direction.INPUT
1536 elif self.is_struct():
1537 if not self.struct.returnedonly:
1538 self._direction = Direction.INPUT
1541 # Returnedonly hints towards output, however in some cases
1542 # it is inputoutput. In particular if pNext / sType exist,
1543 # which are used to link in other structures without having
1544 # to introduce new APIs. E.g. vkGetPhysicalDeviceProperties2KHR.
1545 if "pNext" in self.struct:
1546 self._direction = Direction.INPUT_OUTPUT
1549 self._direction = Direction.OUTPUT
1551 # This should mostly be right. Count variables can be inout, but we don't care about these yet.
1552 self._direction = Direction.OUTPUT
1554 def _set_format_string(self):
1555 """ Internal helper function to be used by constructor to set format string. """
1557 # Determine a format string used by code generation for traces.
1558 # 64-bit types need a conversion function.
1559 self.format_conv = None
1560 if self.is_static_array() or self.is_pointer():
1561 self.format_str = "%p"
1563 if self.type_info["category"] in ["bitmask"]:
1564 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1565 if self.type_info["data"].type == "VkFlags64":
1566 self.format_str = "0x%s"
1567 self.format_conv = "wine_dbgstr_longlong({0})"
1569 self.format_str = "%#x"
1570 elif self.type_info["category"] in ["enum"]:
1571 self.format_str = "%#x"
1572 elif self.is_handle():
1573 # We use uint64_t for non-dispatchable handles as opposed to pointers
1574 # for dispatchable handles.
1575 if self.handle.is_dispatchable():
1576 self.format_str = "%p"
1578 self.format_str = "0x%s"
1579 self.format_conv = "wine_dbgstr_longlong({0})"
1580 elif self.type == "float":
1581 self.format_str = "%f"
1582 elif self.type == "int":
1583 self.format_str = "%d"
1584 elif self.type == "int32_t":
1585 self.format_str = "%d"
1586 elif self.type == "size_t":
1587 self.format_str = "0x%s"
1588 self.format_conv = "wine_dbgstr_longlong({0})"
1589 elif self.type in ["uint16_t", "uint32_t", "VkBool32"]:
1590 self.format_str = "%u"
1591 elif self.type in ["uint64_t", "VkDeviceAddress", "VkDeviceSize"]:
1592 self.format_str = "0x%s"
1593 self.format_conv = "wine_dbgstr_longlong({0})"
1594 elif self.type == "HANDLE":
1595 self.format_str = "%p"
1596 elif self.type in ["VisualID", "xcb_visualid_t", "RROutput", "zx_handle_t"]:
1597 # Don't care about specific types for non-Windows platforms.
1598 self.format_str = ""
1600 LOGGER.warn("Unhandled type: {0}".format(self.type_info))
1602 def copy(self, direction):
1603 if direction == Direction.INPUT:
1604 if self.is_dynamic_array():
1605 return " {0}_host = convert_{1}_array_win_to_host({0}, {2});\n".format(self.name, self.type, self.dyn_array_len)
1607 return " convert_{0}_win_to_host({1}, &{1}_host);\n".format(self.type, self.name)
1609 if self.is_dynamic_array():
1610 LOGGER.error("Unimplemented output conversion for: {0}".format(self.name))
1612 return " convert_{0}_host_to_win(&{1}_host, {1});\n".format(self.type, self.name)
1614 def definition(self, postfix=None):
1615 """ Return prototype for the parameter. E.g. 'const char *foo' """
1619 proto += self.const + " "
1623 if self.is_pointer():
1624 proto += " {0}{1}".format(self.pointer, self.name)
1626 proto += " " + self.name
1628 # Allows appending something to the variable name useful for
1629 # win32 to host conversion.
1630 if postfix is not None:
1633 if self.is_static_array():
1634 proto += "[{0}]".format(self.array_len)
1638 def direction(self):
1639 """ Returns parameter direction: input, output, input_output.
1641 Parameter direction in Vulkan is not straight-forward, which this function determines.
1644 return self._direction
1646 def dispatch_table(self):
1647 """ Return functions dispatch table pointer for dispatchable objects. """
1649 if not self.is_dispatchable():
1652 return "{0}->{1}".format(self.name, self.handle.dispatch_table())
1654 def format_string(self):
1655 return self.format_str
1658 if self.is_dynamic_array():
1659 if self.is_struct() and self.struct.returnedonly:
1660 # For returnedonly, counts is stored in a pointer.
1661 return " free_{0}_array({1}_host, *{2});\n".format(self.type, self.name, self.dyn_array_len)
1663 return " free_{0}_array({1}_host, {2});\n".format(self.type, self.name, self.dyn_array_len)
1665 # We are operating on a single structure. Some structs (very rare) contain dynamic members,
1666 # which would need freeing.
1667 if self.is_struct() and self.struct.needs_free():
1668 return " free_{0}(&{1}_host);\n".format(self.type, self.name)
1671 def get_conversions(self):
1672 """ Get a list of conversions required for this parameter if any.
1673 Parameters which are structures may require conversion between win32
1674 and the host platform. This function returns a list of conversions
1678 if self.is_struct():
1679 self.struct.needs_struct_extensions_conversion()
1680 for m in self.struct:
1681 m.needs_struct_extensions_conversion()
1682 elif not self.is_handle():
1685 if not self.needs_conversion() and not self.needs_unwrapping():
1690 # Collect any member conversions first, so we can guarantee
1691 # those functions will be defined prior to usage by the
1692 # 'parent' param requiring conversion.
1693 if self.is_struct():
1694 for m in self.struct:
1695 if not m.is_struct():
1698 if not m.needs_conversion() and not m.needs_unwrapping():
1701 conversions.extend(m.get_conversions())
1703 # Conversion requirements for the 'parent' parameter.
1704 if self.input_conv is not None:
1705 conversions.append(self.input_conv)
1706 if self.output_conv is not None:
1707 conversions.append(self.output_conv)
1708 if self.free_func is not None:
1709 conversions.append(self.free_func)
1714 return self.const is not None
1716 def is_dynamic_array(self):
1717 return self.dyn_array_len is not None
1719 def is_dispatchable(self):
1720 if not self.is_handle():
1723 return self.handle.is_dispatchable()
1725 def is_handle(self):
1726 return self.handle is not None
1728 def is_pointer(self):
1729 return self.pointer is not None
1731 def is_static_array(self):
1732 return self.array_len is not None
1734 def is_struct(self):
1735 return self.struct is not None
1737 def needs_conversion(self):
1738 """ Returns if parameter needs conversion between win32 and host. """
1740 if not self.is_struct():
1743 # VkSparseImageMemoryRequirements(2) is used by vkGetImageSparseMemoryRequirements(2).
1744 # This function is tricky to wrap, because how to wrap depends on whether
1745 # pSparseMemoryRequirements is NULL or not. Luckily for VkSparseImageMemoryRequirements(2)
1746 # the alignment works out in such a way that no conversion is needed between win32 and Linux.
1747 if self.type in ["VkSparseImageMemoryRequirements", "VkSparseImageMemoryRequirements2"]:
1750 # If a structure needs alignment changes, it means we need to
1751 # perform parameter conversion between win32 and host.
1752 if self.struct.needs_conversion():
1757 def needs_unwrapping(self):
1758 """ Returns if parameter needs unwrapping of handle. """
1760 # Wrapped handle parameters are handled separately, only look for wrapped handles in structs
1761 if self.is_struct():
1762 return self.struct.needs_unwrapping()
1764 if self.is_handle() and self.is_dynamic_array():
1765 return self.handle.needs_unwrapping()
1769 def needs_free(self):
1770 return self.free_func is not None
1772 def needs_input_conversion(self):
1773 return self.input_conv is not None
1775 def needs_output_conversion(self):
1776 return self.output_conv is not None
1779 """ Generate spec file entry for this parameter. """
1781 if self.is_pointer() and self.type == "char":
1783 if self.is_dispatchable() or self.is_pointer() or self.is_static_array():
1785 if self.type_info["category"] in ["bitmask"]:
1786 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1787 if self.type_info["data"].type == "VkFlags64":
1791 if self.type_info["category"] in ["enum"]:
1793 if self.is_handle() and not self.is_dispatchable():
1795 if self.type == "float":
1797 if self.type in ["int", "int32_t", "size_t", "uint16_t", "uint32_t", "VkBool32"]:
1799 if self.type in ["uint64_t", "VkDeviceSize"]:
1802 LOGGER.error("Unhandled spec conversion for type: {0}".format(self.type))
1804 def variable(self, conv=False):
1805 """ Returns 'glue' code during generation of a function call on how to access the variable.
1806 This function handles various scenarios such as 'unwrapping' if dispatchable objects and
1807 renaming of parameters in case of win32 -> host conversion.
1810 conv (bool, optional): Enable conversion if the param needs it. This appends '_host' to the name.
1813 # Hack until we enable allocation callbacks from ICD to application. These are a joy
1814 # to enable one day, because of calling convention conversion.
1815 if "VkAllocationCallbacks" in self.type:
1816 LOGGER.debug("TODO: setting NULL VkAllocationCallbacks for {0}".format(self.name))
1819 if self.needs_unwrapping() or (conv and self.needs_conversion()):
1820 if self.is_dynamic_array():
1821 return "{0}_host".format(self.name)
1823 return "&{0}_host".format(self.name)
1825 if self.object_type != None and self.type == "uint64_t":
1826 return "wine_vk_unwrap_handle({0}, {1})".format(self.object_type, self.name)
1828 # We need to pass the native handle to the native Vulkan calls and
1829 # the wine driver's handle to calls which are wrapped by the driver.
1830 driver_handle = self.handle.driver_handle(self.name) if self.is_handle() else None
1831 return driver_handle if driver_handle else self.name
1834 class VkStruct(Sequence):
1835 """ Class which represents the type union and struct. """
1837 def __init__(self, name, members, returnedonly, structextends, alias=None, union=False):
1839 self.members = members
1840 self.returnedonly = returnedonly
1841 self.structextends = structextends
1842 self.required = False
1845 self.type_info = None # To be set later.
1846 self.struct_extensions = []
1847 self.aliased_by = []
1849 def __getitem__(self, i):
1850 return self.members[i]
1853 return len(self.members)
1856 def from_alias(struct, alias):
1857 name = struct.attrib.get("name")
1858 aliasee = VkStruct(name, alias.members, alias.returnedonly, alias.structextends, alias=alias)
1860 alias.add_aliased_by(aliasee)
1864 def from_xml(struct):
1865 # Unions and structs are the same parsing wise, but we need to
1866 # know which one we are dealing with later on for code generation.
1867 union = True if struct.attrib["category"] == "union" else False
1869 name = struct.attrib.get("name")
1871 # 'Output' structures for which data is filled in by the API are
1872 # marked as 'returnedonly'.
1873 returnedonly = True if struct.attrib.get("returnedonly") else False
1875 structextends = struct.attrib.get("structextends")
1876 structextends = structextends.split(",") if structextends else []
1879 for member in struct.findall("member"):
1880 vk_member = VkMember.from_xml(member)
1881 members.append(vk_member)
1883 return VkStruct(name, members, returnedonly, structextends, union=union)
1886 def decouple_structs(structs):
1887 """ Helper function which decouples a list of structs.
1888 Structures often depend on other structures. To make the C compiler
1889 happy we need to define 'substructures' first. This function analyzes
1890 the list of structures and reorders them in such a way that they are
1894 tmp_structs = list(structs) # Don't modify the original structures.
1895 decoupled_structs = []
1897 while (len(tmp_structs) > 0):
1898 for struct in tmp_structs:
1901 if not struct.required:
1902 tmp_structs.remove(struct)
1906 if not (m.is_struct() or m.is_union()):
1909 # VkBaseInstructure and VkBaseOutStructure reference themselves.
1910 if m.type == struct.name:
1914 # Check if a struct we depend on has already been defined.
1915 for s in decoupled_structs:
1916 if s.name == m.type:
1921 # Check if the struct we depend on is even in the list of structs.
1922 # If found now, it means we haven't met all dependencies before we
1923 # can operate on the current struct.
1924 # When generating 'host' structs we may not be able to find a struct
1925 # as the list would only contain the structs requiring conversion.
1926 for s in tmp_structs:
1927 if s.name == m.type:
1931 if dependends == False:
1932 decoupled_structs.append(struct)
1933 tmp_structs.remove(struct)
1935 return decoupled_structs
1937 def definition(self, align=False, conv=False, postfix=None):
1938 """ Convert structure to textual definition.
1941 align (bool, optional): enable alignment to 64-bit for win32 struct compatibility.
1942 conv (bool, optional): enable struct conversion if the struct needs it.
1943 postfix (str, optional): text to append to end of struct name, useful for struct renaming.
1946 # Only define alias structs when doing conversions
1947 if self.is_alias() and not conv:
1951 text = "typedef union {0}".format(self.name)
1953 text = "typedef struct {0}".format(self.name)
1955 if postfix is not None:
1961 if align and m.needs_alignment():
1962 text += " {0};\n".format(m.definition(align=align))
1963 elif conv and m.needs_conversion():
1964 text += " {0};\n".format(m.definition(conv=conv))
1966 text += " {0};\n".format(m.definition())
1968 if postfix is not None:
1969 text += "}} {0}{1};\n".format(self.name, postfix)
1971 text += "}} {0};\n".format(self.name)
1973 for aliasee in self.aliased_by:
1974 text += "typedef {0} {1};\n".format(self.name, aliasee.name)
1979 return bool(self.alias)
1981 def add_aliased_by(self, aliasee):
1982 self.aliased_by.append(aliasee)
1984 def needs_alignment(self):
1985 """ Check if structure needs alignment for 64-bit data.
1986 Various structures need alignment on 64-bit variables due
1987 to compiler differences on 32-bit between Win32 and Linux.
1990 for m in self.members:
1991 if m.needs_alignment():
1995 def needs_conversion(self):
1996 """ Returns if struct members needs conversion between win32 and host.
1997 Structures need conversion if they contain members requiring alignment
1998 or if they include other structures which need alignment.
2001 if self.needs_alignment():
2004 for m in self.members:
2005 if m.needs_conversion():
2009 def needs_unwrapping(self):
2010 """ Returns if struct members need unwrapping of handle. """
2012 for m in self.members:
2013 if m.needs_unwrapping():
2017 def needs_free(self):
2018 """ Check if any struct member needs some memory freeing."""
2020 for m in self.members:
2028 def needs_struct_extensions_conversion(self):
2029 """ Checks if structure extensions in pNext chain need conversion. """
2032 for e in self.struct_extensions:
2033 if e.required and e.needs_conversion():
2034 LOGGER.error("Unhandled pNext chain alignment conversion for {0}".format(e.name))
2036 if e.required and e.needs_unwrapping():
2037 LOGGER.error("Unhandled pNext chain unwrapping conversion for {0}".format(e.name))
2042 def set_type_info(self, types):
2043 """ Helper function to set type information from the type registry.
2044 This is needed, because not all type data is available at time of
2047 for m in self.members:
2048 type_info = types[m.type]
2049 m.set_type_info(type_info)
2052 class ConversionFunction(object):
2053 def __init__(self, array, dyn_array, direction, operand):
2055 self.direction = direction
2056 self.dyn_array = dyn_array
2057 self.operand = operand
2058 self.type = operand.name
2062 def __eq__(self, other):
2063 return self.name == other.name
2065 def _generate_array_conversion_func(self):
2066 """ Helper function for generating a conversion function for array operands. """
2070 if self.operand.needs_conversion():
2071 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2073 if self.direction == Direction.OUTPUT:
2074 params = ["const {0}_host *in".format(self.type), "uint32_t count"]
2075 return_type = self.type
2077 params = ["const {0} *in".format(self.type), "uint32_t count"]
2078 return_type = "{0}_host".format(self.type)
2080 # Generate function prototype.
2081 body += "static inline {0} *{1}(".format(return_type, self.name)
2082 body += ", ".join(p for p in params)
2085 body += " {0} *out;\n".format(return_type)
2087 if self.operand.needs_unwrapping():
2088 if self.operand.needs_conversion():
2091 params = ["const {0} *in".format(self.type), "uint32_t count"]
2092 return_type = "{0}".format(self.type)
2094 # Generate function prototype.
2095 body += "static inline {0} *{1}(".format(return_type, self.name)
2096 body += ", ".join(p for p in params)
2099 body += " {0} *out;\n".format(return_type)
2101 if self.operand.needs_conversion():
2102 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2104 body += " unsigned int i;\n\n"
2105 body += " if (!in) return NULL;\n\n"
2107 body += " out = malloc(count * sizeof(*out));\n"
2109 body += " for (i = 0; i < count; i++)\n"
2112 if isinstance(self.operand, VkStruct):
2113 for m in self.operand:
2114 # TODO: support copying of pNext extension structures!
2115 # Luckily though no extension struct at this point needs conversion.
2116 convert = m.copy("in[i].", "out[i].", self.direction, conv=True)
2117 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2118 body += " " + convert
2120 unwrap = m.copy("in[i].", "out[i].", self.direction, conv=False)
2121 if unwrap == convert:
2122 body += " " + unwrap
2124 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2125 body += " " + convert
2127 body += " " + unwrap
2128 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2130 elif isinstance(self.operand, VkHandle) and self.direction == Direction.INPUT:
2131 body += " out[i] = " + self.operand.driver_handle("in[i]") + ";\n"
2133 LOGGER.warn("Unhandled conversion operand type")
2134 body += " out[i] = in[i];\n"
2137 body += " return out;\n"
2140 if not self.operand.needs_unwrapping():
2141 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2147 def _generate_conversion_func(self):
2148 """ Helper function for generating a conversion function for non-array operands. """
2150 # It doesn't make sense to generate conversion functions for non-struct variables
2151 # which aren't in arrays, as this should be handled by the copy() function
2152 if not isinstance(self.operand, VkStruct):
2157 if self.operand.needs_conversion():
2158 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2159 body += "static inline void {0}(".format(self.name)
2161 if self.direction == Direction.OUTPUT:
2162 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type)]
2164 params = ["const {0} *in".format(self.type), "{0}_host *out".format(self.type)]
2166 # Generate parameter list
2167 body += ", ".join(p for p in params)
2170 if self.operand.needs_unwrapping():
2171 if self.operand.needs_conversion():
2174 body += "static inline void {0}(".format(self.name)
2176 params = ["const {0} *in".format(self.type), "{0} *out".format(self.type)]
2178 # Generate parameter list
2179 body += ", ".join(p for p in params)
2182 if self.operand.needs_conversion():
2183 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2185 body += "{\n if (!in) return;\n\n"
2187 if self.direction == Direction.INPUT and "pNext" in self.operand and self.operand.returnedonly:
2188 # We are dealing with an input_output parameter. For these we only need to copy
2189 # pNext and sType as the other fields are filled in by the host. We do potentially
2190 # have to iterate over pNext and perform conversions based on switch(sType)!
2191 # Luckily though no extension structs at this point need conversion.
2192 # TODO: support copying of pNext extension structures!
2193 body += " out->pNext = in->pNext;\n"
2194 body += " out->sType = in->sType;\n"
2196 for m in self.operand:
2197 # TODO: support copying of pNext extension structures!
2198 convert = m.copy("in->", "out->", self.direction, conv=True)
2199 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2200 body += " " + convert
2202 unwrap = m.copy("in->", "out->", self.direction, conv=False)
2203 if unwrap == convert:
2204 body += " " + unwrap
2206 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2207 body += " " + convert
2209 body += " " + unwrap
2210 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2214 if not self.operand.needs_unwrapping():
2215 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2221 def _generate_static_array_conversion_func(self):
2222 """ Helper function for generating a conversion function for array operands. """
2226 if self.operand.needs_conversion():
2227 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2229 if self.direction == Direction.OUTPUT:
2230 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
2232 params = ["const {0} *in".format(self.type), "{0} *out_host".format(self.type), "uint32_t count"]
2234 # Generate function prototype.
2235 body += "static inline void {0}(".format(self.name)
2236 body += ", ".join(p for p in params)
2239 if self.operand.needs_unwrapping():
2240 if self.operand.needs_conversion():
2243 params = ["const {0} *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
2245 # Generate function prototype.
2246 body += "static inline void {0}(".format(self.name)
2247 body += ", ".join(p for p in params)
2251 body += " unsigned int i;\n\n"
2252 body += " if (!in) return;\n\n"
2253 body += " for (i = 0; i < count; i++)\n"
2256 if isinstance(self.operand, VkStruct):
2257 for m in self.operand:
2258 # TODO: support copying of pNext extension structures!
2259 convert = m.copy("in[i].", "out[i].", self.direction, conv=True)
2260 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2261 body += " " + convert
2263 unwrap = m.copy("in[i].", "out[i].", self.direction, conv=False)
2264 if unwrap == convert:
2265 body += " " + unwrap
2267 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2268 body += " " + convert
2270 body += " " + unwrap
2271 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2272 elif isinstance(self.operand, VkHandle) and self.direction == Direction.INPUT:
2273 body += " out[i] = " + self.operand.driver_handle("in[i]") + ";\n"
2275 LOGGER.warn("Unhandled conversion operand type")
2276 body += " out[i] = in[i];\n"
2281 if not self.operand.needs_unwrapping():
2282 body += "#endif /* USE_STRUCT_CONVERSION) */\n"
2288 def _set_name(self):
2289 if self.direction == Direction.INPUT:
2291 name = "convert_{0}_static_array_win_to_host".format(self.type)
2292 elif self.dyn_array:
2293 name = "convert_{0}_array_win_to_host".format(self.type)
2295 name = "convert_{0}_win_to_host".format(self.type)
2296 else: # Direction.OUTPUT
2298 name = "convert_{0}_static_array_host_to_win".format(self.type)
2299 elif self.dyn_array:
2300 name = "convert_{0}_array_host_to_win".format(self.type)
2302 name = "convert_{0}_host_to_win".format(self.type)
2306 def definition(self):
2308 return self._generate_static_array_conversion_func()
2309 elif self.dyn_array:
2310 return self._generate_array_conversion_func()
2312 return self._generate_conversion_func()
2315 class FreeFunction(object):
2316 def __init__(self, dyn_array, operand):
2317 self.dyn_array = dyn_array
2318 self.operand = operand
2319 self.type = operand.name
2322 self.name = "free_{0}_array".format(self.type)
2324 self.name = "free_{0}".format(self.type)
2326 def __eq__(self, other):
2327 return self.name == other.name
2329 def _generate_array_free_func(self):
2330 """ Helper function for cleaning up temporary buffers required for array conversions. """
2334 if self.operand.needs_conversion():
2335 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2336 # Generate function prototype.
2337 body += "static inline void {0}({1}_host *in, uint32_t count)\n".format(self.name, self.type)
2339 if self.operand.needs_unwrapping():
2340 if self.operand.needs_conversion():
2343 # Generate function prototype.
2344 body += "static inline void {0}({1} *in, uint32_t count)\n".format(self.name, self.type)
2346 if self.operand.needs_conversion():
2347 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2351 # E.g. VkGraphicsPipelineCreateInfo_host needs freeing for pStages.
2352 if isinstance(self.operand, VkStruct) and self.operand.needs_free():
2353 body += " unsigned int i;\n\n"
2354 body += " if (!in) return;\n\n"
2355 body += " for (i = 0; i < count; i++)\n"
2358 for m in self.operand:
2360 convert = m.free("in[i].", conv=True)
2361 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2362 body += " " + convert
2364 unwrap = m.free("in[i].", conv=False)
2365 if convert == unwrap:
2366 body += " " + unwrap
2368 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2369 body += " " + convert
2370 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2372 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2373 body += " " + convert
2375 body += " " + unwrap
2376 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2379 body += " if (!in) return;\n\n"
2381 body += " free(in);\n"
2385 if not self.operand.needs_unwrapping():
2386 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2392 def _generate_free_func(self):
2393 # E.g. VkCommandBufferBeginInfo.pInheritanceInfo needs freeing.
2394 if not self.operand.needs_free():
2397 if not isinstance(self.operand, VkStruct):
2402 if self.operand.needs_conversion():
2403 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2404 # Generate function prototype.
2405 body += "static inline void {0}({1}_host *in)\n".format(self.name, self.type)
2407 if self.operand.needs_unwrapping():
2408 if self.operand.needs_conversion():
2411 # Generate function prototype.
2412 body += "static inline void {0}({1} *in)\n".format(self.name, self.type)
2414 if self.operand.needs_conversion():
2415 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2419 for m in self.operand:
2421 convert = m.free("in->", conv=True)
2422 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2423 body += " " + convert
2425 unwrap = m.free("in->", conv=False)
2426 if convert == unwrap:
2427 body += " " + unwrap
2429 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2430 body += " " + convert
2431 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2433 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2434 body += " " + convert
2436 body += " " + unwrap
2437 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2441 if not self.operand.needs_unwrapping():
2442 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2448 def definition(self):
2450 return self._generate_array_free_func()
2452 # Some structures need freeing too if they contain dynamic arrays.
2453 # E.g. VkCommandBufferBeginInfo
2454 return self._generate_free_func()
2457 class StructChainConversionFunction(object):
2458 def __init__(self, direction, struct, ignores):
2459 self.direction = direction
2460 self.struct = struct
2461 self.ignores = ignores
2462 self.type = struct.name
2464 self.name = "convert_{0}_struct_chain".format(self.type)
2466 def __eq__(self, other):
2467 return self.name == other.name
2469 def prototype(self, postfix=""):
2470 return "VkResult {0}(const void *pNext, {1} *out_struct) {2}".format(self.name, self.type, postfix).strip()
2472 def definition(self):
2473 body = self.prototype()
2476 body += " VkBaseOutStructure *out_header = (VkBaseOutStructure *)out_struct;\n";
2477 body += " const VkBaseInStructure *in_header;\n\n";
2479 body += " out_header->pNext = NULL;\n\n"
2481 body += " for (in_header = pNext; in_header; in_header = in_header->pNext)\n"
2483 body += " switch (in_header->sType)\n"
2486 for i in self.ignores:
2487 body += " case {0}:\n".format(i)
2488 body += " break;\n\n"
2490 for e in self.struct.struct_extensions:
2494 stype = next(x for x in e.members if x.name == "sType")
2496 if stype.values in self.ignores:
2499 body += " case {0}:\n".format(stype.values)
2502 body += " const {0} *in = (const {0} *)in_header;\n".format(e.name)
2503 body += " {0} *out;\n\n".format(e.name)
2505 body += " if (!(out = malloc(sizeof(*out)))) goto out_of_memory;\n\n"
2508 if m.name == "pNext":
2509 body += " out->pNext = NULL;\n"
2511 convert = m.copy("in->", "out->", self.direction, conv=True)
2512 unwrap = m.copy("in->", "out->", self.direction, conv=False)
2513 if unwrap == convert:
2514 body += " " + unwrap
2516 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2517 body += " " + convert
2519 body += " " + unwrap
2520 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2522 body += "\n out_header->pNext = (VkBaseOutStructure *)out;\n"
2523 body += " out_header = out_header->pNext;\n"
2527 body += " default:\n"
2528 body += " FIXME(\"Application requested a linked structure of type %u.\\n\", in_header->sType);\n"
2533 body += " return VK_SUCCESS;\n"
2535 if any(x for x in self.struct.struct_extensions if x.required):
2536 body += "\nout_of_memory:\n"
2537 body += " free_{0}_struct_chain(out_struct);\n".format(self.type)
2538 body += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
2543 class FreeStructChainFunction(object):
2544 def __init__(self, struct):
2545 self.struct = struct
2546 self.type = struct.name
2548 self.name = "free_{0}_struct_chain".format(self.type)
2550 def __eq__(self, other):
2551 return self.name == other.name
2553 def prototype(self, postfix=""):
2554 return "void {0}({1} *s) {2}".format(self.name, self.type, postfix).strip()
2556 def definition(self):
2557 body = self.prototype()
2560 body += " VkBaseOutStructure *header = (void *)s->pNext;\n\n";
2562 body += " while (header)\n"
2564 body += " void *prev = header;\n\n"
2565 body += " switch (header->sType)\n"
2568 for e in self.struct.struct_extensions:
2572 if not any(m.needs_free() for m in e):
2575 stype = next(x for x in e.members if x.name == "sType")
2577 body += " case {0}:\n".format(stype.values)
2579 body += " {0} *structure = ({0} *) header;\n".format(e.name)
2583 convert = m.free("structure->", conv=True)
2584 unwrap = m.free("structure->", conv=False)
2585 if convert == unwrap:
2586 body += " " + unwrap
2588 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2589 body += " " + convert
2590 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2592 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2593 body += " " + convert
2595 body += " " + unwrap
2596 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2601 body += " default:\n"
2605 body += " header = header->pNext;\n"
2606 body += " free(prev);\n"
2609 body += " s->pNext = NULL;\n"
2615 class VkGenerator(object):
2616 def __init__(self, registry):
2617 self.registry = registry
2619 # Build a list conversion functions for struct conversion.
2620 self.conversions = []
2621 self.struct_chain_conversions = []
2622 self.host_structs = []
2623 for func in self.registry.funcs.values():
2624 if not func.is_required():
2627 if not func.needs_conversion() and not func.needs_unwrapping():
2630 conversions = func.get_conversions()
2631 for conv in conversions:
2632 # Pull in any conversions for vulkan_thunks.c.
2633 if func.needs_thunk():
2634 # Append if we don't already have this conversion.
2635 if not any(c == conv for c in self.conversions):
2636 self.conversions.append(conv)
2638 if not isinstance(conv.operand, VkStruct):
2641 # Structs can be used in different ways by different conversions
2642 # e.g. array vs non-array. Just make sure we pull in each struct once.
2643 if not any(s.name == conv.operand.name for s in self.host_structs):
2644 self.host_structs.append(conv.operand)
2646 for struct in self.registry.structs:
2647 if struct.name in STRUCT_CHAIN_CONVERSIONS:
2648 self.struct_chain_conversions.append(StructChainConversionFunction(Direction.INPUT, struct, STRUCT_CHAIN_CONVERSIONS[struct.name]))
2649 self.struct_chain_conversions.append(FreeStructChainFunction(struct))
2650 # Once we decide to support pNext chains conversion everywhere, move this under get_conversions
2651 for e in struct.struct_extensions:
2653 if m.needs_conversion() or m.needs_unwrapping():
2654 conversions = m.get_conversions()
2655 for conv in conversions:
2656 if not any(c == conv for c in self.conversions):
2657 self.conversions.append(conv)
2659 def _generate_copyright(self, f, spec_file=False):
2660 f.write("# " if spec_file else "/* ")
2661 f.write("Automatically generated from Vulkan vk.xml; DO NOT EDIT!\n")
2662 lines = ["", "This file is generated from Vulkan vk.xml file covered",
2663 "by the following copyright and permission notice:"]
2664 lines.extend([l.rstrip(" ") for l in self.registry.copyright.splitlines()])
2666 f.write("{0}{1}".format("# " if spec_file else " * ", line).rstrip(" ") + "\n")
2667 f.write("\n" if spec_file else " */\n\n")
2669 def generate_thunks_c(self, f, prefix):
2670 self._generate_copyright(f)
2673 f.write("#pragma makedep unix\n")
2674 f.write("#endif\n\n")
2676 f.write("#include \"config.h\"\n")
2677 f.write("#include \"wine/port.h\"\n\n")
2679 f.write("#include \"vulkan_private.h\"\n\n")
2681 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2683 # Generate any conversion helper functions.
2684 for conv in self.conversions:
2685 f.write(conv.definition())
2687 for conv in self.struct_chain_conversions:
2688 f.write(conv.definition())
2690 # Create thunks for instance and device functions.
2691 # Global functions don't go through the thunks.
2692 for vk_func in self.registry.funcs.values():
2693 if not vk_func.needs_exposing():
2696 if vk_func.is_global_func():
2699 if not vk_func.needs_thunk():
2702 if vk_func.needs_private_thunk():
2703 f.write(vk_func.thunk(prefix="thunk_"))
2706 f.write(vk_func.thunk(prefix=prefix, call_conv="WINAPI"))
2708 # Create array of device extensions.
2709 f.write("static const char * const vk_device_extensions[] =\n{\n")
2710 for ext in self.registry.extensions:
2711 if ext["type"] != "device":
2713 if ext["name"] in UNEXPOSED_EXTENSIONS:
2716 f.write(" \"{0}\",\n".format(ext["name"]))
2719 # Create array of instance extensions.
2720 f.write("static const char * const vk_instance_extensions[] =\n{\n")
2721 for ext in self.registry.extensions:
2722 if ext["type"] != "instance":
2724 if ext["name"] in UNEXPOSED_EXTENSIONS:
2727 f.write(" \"{0}\",\n".format(ext["name"]))
2730 f.write("BOOL wine_vk_device_extension_supported(const char *name)\n")
2732 f.write(" unsigned int i;\n")
2733 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_extensions); i++)\n")
2735 f.write(" if (strcmp(vk_device_extensions[i], name) == 0)\n")
2736 f.write(" return TRUE;\n")
2738 f.write(" return FALSE;\n")
2741 f.write("BOOL wine_vk_instance_extension_supported(const char *name)\n")
2743 f.write(" unsigned int i;\n")
2744 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_extensions); i++)\n")
2746 f.write(" if (strcmp(vk_instance_extensions[i], name) == 0)\n")
2747 f.write(" return TRUE;\n")
2749 f.write(" return FALSE;\n")
2752 f.write("BOOL wine_vk_is_type_wrapped(VkObjectType type)\n")
2754 f.write(" return FALSE")
2755 for handle in self.registry.handles:
2756 if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2758 f.write(" ||\n type == {}".format(handle.object_type))
2762 f.write("uint64_t wine_vk_unwrap_handle(VkObjectType type, uint64_t handle)\n")
2764 f.write(" switch(type)\n")
2766 for handle in self.registry.handles:
2767 if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2769 f.write(" case {}:\n".format(handle.object_type))
2770 if handle.is_dispatchable():
2771 f.write(" return (uint64_t) (uintptr_t) ")
2772 f.write(handle.native_handle("(({}) (uintptr_t) handle)".format(handle.name)))
2774 f.write(" return (uint64_t) ")
2775 f.write(handle.native_handle("handle"))
2777 f.write(" default:\n")
2778 f.write(" return handle;\n")
2782 f.write("const struct unix_funcs loader_funcs =\n")
2784 for vk_func in self.registry.funcs.values():
2785 if not vk_func.needs_exposing():
2787 if vk_func.loader_thunk_type == ThunkType.NONE:
2790 f.write(" &{1}{0},\n".format(vk_func.name, prefix))
2793 def generate_thunks_h(self, f, prefix):
2794 self._generate_copyright(f)
2796 f.write("#ifndef __WINE_VULKAN_THUNKS_H\n")
2797 f.write("#define __WINE_VULKAN_THUNKS_H\n\n")
2799 f.write("#define WINE_VK_VERSION VK_API_VERSION_{0}_{1}\n\n".format(WINE_VK_VERSION[0], WINE_VK_VERSION[1]))
2801 # Generate prototypes for device and instance functions requiring a custom implementation.
2802 f.write("/* Functions for which we have custom implementations outside of the thunks. */\n")
2803 for vk_func in self.registry.funcs.values():
2804 if not vk_func.needs_exposing():
2806 if vk_func.needs_thunk() and not vk_func.needs_private_thunk():
2809 if vk_func.is_core_func():
2810 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix=prefix)))
2812 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix=prefix, postfix="DECLSPEC_HIDDEN")))
2815 f.write("/* Private thunks */\n")
2816 for vk_func in self.registry.funcs.values():
2817 if vk_func.needs_private_thunk():
2818 f.write("{0};\n".format(vk_func.prototype(prefix="thunk_", postfix="DECLSPEC_HIDDEN")))
2821 for struct in self.host_structs:
2822 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
2823 f.write(struct.definition(align=False, conv=True, postfix="_host"))
2825 f.write("typedef {0} {0}_host;\n".format(struct.name))
2826 f.write("#endif\n\n")
2829 for func in self.struct_chain_conversions:
2830 f.write(func.prototype(postfix="DECLSPEC_HIDDEN") + ";\n")
2833 f.write("/* For use by vkDevice and children */\n")
2834 f.write("struct vulkan_device_funcs\n{\n")
2835 for vk_func in self.registry.device_funcs:
2836 if not vk_func.is_required():
2839 if not vk_func.needs_dispatch():
2840 LOGGER.debug("skipping {0} in vulkan_device_funcs".format(vk_func.name))
2843 f.write(" {0};\n".format(vk_func.pfn(conv=vk_func.needs_conversion())))
2846 f.write("/* For use by vkInstance and children */\n")
2847 f.write("struct vulkan_instance_funcs\n{\n")
2848 for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs:
2849 if not vk_func.is_required():
2852 if not vk_func.needs_dispatch():
2853 LOGGER.debug("skipping {0} in vulkan_instance_funcs".format(vk_func.name))
2856 f.write(" {0};\n".format(vk_func.pfn(conv=vk_func.needs_conversion())))
2859 f.write("#define ALL_VK_DEVICE_FUNCS() \\\n")
2861 for vk_func in self.registry.device_funcs:
2862 if not vk_func.is_required():
2865 if not vk_func.needs_dispatch():
2866 LOGGER.debug("skipping {0} in ALL_VK_DEVICE_FUNCS".format(vk_func.name))
2870 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2873 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2876 f.write("#define ALL_VK_INSTANCE_FUNCS() \\\n")
2878 for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs:
2879 if not vk_func.is_required():
2882 if not vk_func.needs_dispatch():
2883 LOGGER.debug("skipping {0} in ALL_VK_INSTANCE_FUNCS".format(vk_func.name))
2887 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2890 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2893 f.write("#endif /* __WINE_VULKAN_THUNKS_H */\n")
2895 def generate_loader_thunks_c(self, f):
2896 self._generate_copyright(f)
2898 f.write("#include \"vulkan_loader.h\"\n\n")
2900 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2902 for vk_func in self.registry.funcs.values():
2903 if not vk_func.needs_exposing():
2905 if vk_func.loader_thunk_type != ThunkType.PUBLIC:
2908 f.write(vk_func.loader_thunk())
2910 f.write("static const struct vulkan_func vk_device_dispatch_table[] =\n{\n")
2911 for vk_func in self.registry.device_funcs:
2912 if not vk_func.needs_exposing():
2915 f.write(" {{\"{0}\", &{0}}},\n".format(vk_func.name))
2918 f.write("static const struct vulkan_func vk_phys_dev_dispatch_table[] =\n{\n")
2919 for vk_func in self.registry.phys_dev_funcs:
2920 if not vk_func.needs_exposing():
2923 f.write(" {{\"{0}\", &{0}}},\n".format(vk_func.name))
2926 f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
2927 for vk_func in self.registry.instance_funcs:
2928 if not vk_func.needs_exposing():
2931 f.write(" {{\"{0}\", &{0}}},\n".format(vk_func.name))
2934 f.write("void *wine_vk_get_device_proc_addr(const char *name)\n")
2936 f.write(" unsigned int i;\n")
2937 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_dispatch_table); i++)\n")
2939 f.write(" if (strcmp(vk_device_dispatch_table[i].name, name) == 0)\n")
2941 f.write(" TRACE(\"Found name=%s in device table\\n\", debugstr_a(name));\n")
2942 f.write(" return vk_device_dispatch_table[i].func;\n")
2945 f.write(" return NULL;\n")
2948 f.write("void *wine_vk_get_phys_dev_proc_addr(const char *name)\n")
2950 f.write(" unsigned int i;\n")
2951 f.write(" for (i = 0; i < ARRAY_SIZE(vk_phys_dev_dispatch_table); i++)\n")
2953 f.write(" if (strcmp(vk_phys_dev_dispatch_table[i].name, name) == 0)\n")
2955 f.write(" TRACE(\"Found name=%s in physical device table\\n\", debugstr_a(name));\n")
2956 f.write(" return vk_phys_dev_dispatch_table[i].func;\n")
2959 f.write(" return NULL;\n")
2962 f.write("void *wine_vk_get_instance_proc_addr(const char *name)\n")
2964 f.write(" unsigned int i;\n")
2965 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_dispatch_table); i++)\n")
2967 f.write(" if (strcmp(vk_instance_dispatch_table[i].name, name) == 0)\n")
2969 f.write(" TRACE(\"Found name=%s in instance table\\n\", debugstr_a(name));\n")
2970 f.write(" return vk_instance_dispatch_table[i].func;\n")
2973 f.write(" return NULL;\n")
2976 def generate_loader_thunks_h(self, f):
2977 self._generate_copyright(f)
2979 f.write("#ifndef __WINE_VULKAN_LOADER_THUNKS_H\n")
2980 f.write("#define __WINE_VULKAN_LOADER_THUNKS_H\n\n")
2982 f.write("struct unix_funcs\n")
2984 for vk_func in self.registry.funcs.values():
2985 if not vk_func.needs_exposing():
2987 if vk_func.loader_thunk_type == ThunkType.NONE:
2990 f.write(" {0};\n".format(vk_func.pfn(conv=False, call_conv="WINAPI")))
2993 f.write("#endif /* __WINE_VULKAN_LOADER_THUNKS_H */\n")
2995 def generate_vulkan_h(self, f):
2996 self._generate_copyright(f)
2997 f.write("#ifndef __WINE_VULKAN_H\n")
2998 f.write("#define __WINE_VULKAN_H\n\n")
3000 f.write("#include <windef.h>\n")
3001 f.write("#include <stdint.h>\n\n")
3003 f.write("/* Define WINE_VK_HOST to get 'host' headers. */\n")
3004 f.write("#ifdef WINE_VK_HOST\n")
3005 f.write("#define VKAPI_CALL\n")
3006 f.write('#define WINE_VK_ALIGN(x)\n')
3007 f.write("#endif\n\n")
3009 f.write("#ifndef VKAPI_CALL\n")
3010 f.write("#define VKAPI_CALL __stdcall\n")
3011 f.write("#endif\n\n")
3013 f.write("#ifndef VKAPI_PTR\n")
3014 f.write("#define VKAPI_PTR VKAPI_CALL\n")
3015 f.write("#endif\n\n")
3017 f.write("#ifndef WINE_VK_ALIGN\n")
3018 f.write("#define WINE_VK_ALIGN DECLSPEC_ALIGN\n")
3019 f.write("#endif\n\n")
3021 # The overall strategy is to define independent constants and datatypes,
3022 # prior to complex structures and function calls to avoid forward declarations.
3023 for const in self.registry.consts:
3024 # For now just generate things we may not need. The amount of parsing needed
3025 # to get some of the info is tricky as you need to figure out which structure
3026 # references a certain constant.
3027 f.write(const.definition())
3030 for define in self.registry.defines:
3031 f.write(define.definition())
3033 for handle in self.registry.handles:
3034 # For backward compatibility also create definitions for aliases.
3035 # These types normally don't get pulled in as we use the new types
3036 # even in legacy functions if they are aliases.
3037 if handle.is_required() or handle.is_alias():
3038 f.write(handle.definition())
3041 for base_type in self.registry.base_types:
3042 f.write(base_type.definition())
3045 for bitmask in self.registry.bitmasks:
3046 f.write(bitmask.definition())
3049 # Define enums, this includes values for some of the bitmask types as well.
3050 for enum in self.registry.enums.values():
3052 f.write(enum.definition())
3054 for fp in self.registry.funcpointers:
3056 f.write(fp.definition())
3059 # This generates both structures and unions. Since structures
3060 # may depend on other structures/unions, we need a list of
3061 # decoupled structs.
3062 # Note: unions are stored in structs for dependency reasons,
3063 # see comment in parsing section.
3064 structs = VkStruct.decouple_structs(self.registry.structs)
3065 for struct in structs:
3066 LOGGER.debug("Generating struct: {0}".format(struct.name))
3067 f.write(struct.definition(align=True))
3070 for func in self.registry.funcs.values():
3071 if not func.is_required():
3072 LOGGER.debug("Skipping PFN definition for: {0}".format(func.name))
3075 f.write("typedef {0};\n".format(func.pfn(prefix="PFN", call_conv="VKAPI_PTR")))
3078 f.write("#ifndef VK_NO_PROTOTYPES\n")
3079 for func in self.registry.funcs.values():
3080 if not func.is_required():
3081 LOGGER.debug("Skipping API definition for: {0}".format(func.name))
3084 LOGGER.debug("Generating API definition for: {0}".format(func.name))
3085 f.write("{0};\n".format(func.prototype(call_conv="VKAPI_CALL")))
3086 f.write("#endif /* VK_NO_PROTOTYPES */\n\n")
3088 f.write("#endif /* __WINE_VULKAN_H */\n")
3090 def generate_vulkan_driver_h(self, f):
3091 self._generate_copyright(f)
3092 f.write("#ifndef __WINE_VULKAN_DRIVER_H\n")
3093 f.write("#define __WINE_VULKAN_DRIVER_H\n\n")
3095 f.write("/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */\n")
3096 f.write("#define WINE_VULKAN_DRIVER_VERSION {0}\n\n".format(DRIVER_VERSION))
3098 f.write("struct vulkan_funcs\n{\n")
3099 f.write(" /* Vulkan global functions. These are the only calls at this point a graphics driver\n")
3100 f.write(" * needs to provide. Other function calls will be provided indirectly by dispatch\n")
3101 f.write(" * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice.\n")
3104 for vk_func in self.registry.funcs.values():
3105 if not vk_func.is_driver_func():
3109 # Avoid PFN_vkVoidFunction in driver interface as Vulkan likes to put calling convention
3110 # stuff in there. For simplicity substitute with "void *".
3111 pfn = pfn.replace("PFN_vkVoidFunction", "void *")
3112 f.write(" {0};\n".format(pfn))
3115 f.write(" /* winevulkan specific functions */\n")
3116 f.write(" VkSurfaceKHR (*p_wine_get_native_surface)(VkSurfaceKHR);\n")
3119 f.write("extern const struct vulkan_funcs * CDECL __wine_get_vulkan_driver(HDC hdc, UINT version);\n\n")
3121 f.write("static inline void *get_vulkan_driver_device_proc_addr(\n")
3122 f.write(" const struct vulkan_funcs *vulkan_funcs, const char *name)\n{\n")
3123 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
3124 f.write(" name += 2;\n\n")
3125 for vk_func in self.registry.funcs.values():
3126 if vk_func.is_driver_func() and vk_func.is_device_func():
3127 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3128 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3130 f.write(" return NULL;\n}\n\n")
3132 f.write("static inline void *get_vulkan_driver_instance_proc_addr(\n")
3133 f.write(" const struct vulkan_funcs *vulkan_funcs, VkInstance instance, const char *name)\n{\n")
3134 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
3135 f.write(" name += 2;\n\n")
3136 for vk_func in self.registry.funcs.values():
3137 if vk_func.is_driver_func() and vk_func.is_global_func() and vk_func.name != "vkGetInstanceProcAddr":
3138 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3139 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3141 f.write(" if (!instance) return NULL;\n\n")
3142 for vk_func in self.registry.funcs.values():
3143 if vk_func.is_driver_func() and (vk_func.is_instance_func() or vk_func.is_phys_dev_func()):
3144 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3145 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3147 f.write(" name -= 2;\n\n")
3148 f.write(" return get_vulkan_driver_device_proc_addr(vulkan_funcs, name);\n}\n\n")
3150 f.write("#endif /* __WINE_VULKAN_DRIVER_H */\n")
3152 def generate_vulkan_spec(self, f):
3153 self._generate_copyright(f, spec_file=True)
3154 f.write("@ stdcall -private vk_icdGetInstanceProcAddr(ptr str)\n")
3155 f.write("@ stdcall -private vk_icdGetPhysicalDeviceProcAddr(ptr str)\n")
3156 f.write("@ stdcall -private vk_icdNegotiateLoaderICDInterfaceVersion(ptr)\n")
3158 # Export symbols for all Vulkan Core functions.
3159 for func in self.registry.funcs.values():
3160 if not func.is_core_func():
3163 # We support all Core functions except for VK_KHR_display* APIs.
3164 # Create stubs for unsupported Core functions.
3165 if func.is_required():
3166 f.write(func.spec())
3168 f.write("@ stub {0}\n".format(func.name))
3170 f.write("@ stdcall -private DllRegisterServer()\n")
3171 f.write("@ stdcall -private DllUnregisterServer()\n")
3173 def generate_vulkan_loader_spec(self, f):
3174 self._generate_copyright(f, spec_file=True)
3176 # Export symbols for all Vulkan Core functions.
3177 for func in self.registry.funcs.values():
3178 if not func.is_core_func():
3181 # We support all Core functions except for VK_KHR_display* APIs.
3182 # Create stubs for unsupported Core functions.
3183 if func.is_required():
3184 f.write(func.spec(symbol="winevulkan." + func.name))
3186 f.write("@ stub {0}\n".format(func.name))
3189 class VkRegistry(object):
3190 def __init__(self, reg_filename):
3191 # Used for storage of type information.
3192 self.base_types = None
3193 self.bitmasks = None
3197 self.funcpointers = None
3201 # We aggregate all types in here for cross-referencing.
3205 self.version_regex = re.compile(
3214 # Overall strategy for parsing the registry is to first
3215 # parse all type / function definitions. Then parse
3216 # features and extensions to decide which types / functions
3217 # to actually 'pull in' for code generation. For each type or
3218 # function call we want we set a member 'required' to True.
3219 tree = ET.parse(reg_filename)
3220 root = tree.getroot()
3221 self._parse_enums(root)
3222 self._parse_types(root)
3223 self._parse_commands(root)
3225 # Pull in any required types and functions.
3226 self._parse_features(root)
3227 self._parse_extensions(root)
3229 self._match_object_types()
3231 self.copyright = root.find('./comment').text
3233 def _is_feature_supported(self, feature):
3234 version = self.version_regex.match(feature)
3238 version = tuple(map(int, version.group('major', 'minor')))
3239 return version <= WINE_VK_VERSION
3241 def _is_extension_supported(self, extension):
3242 # We disable some extensions as either we haven't implemented
3243 # support yet or because they are for platforms other than win32.
3244 return extension not in UNSUPPORTED_EXTENSIONS
3246 def _mark_command_required(self, command):
3247 """ Helper function to mark a certain command and the datatypes it needs as required."""
3248 def mark_bitmask_dependencies(bitmask, types):
3249 if bitmask.requires is not None:
3250 types[bitmask.requires]["data"].required = True
3252 def mark_funcpointer_dependencies(fp, types):
3253 for m in fp.members:
3254 type_info = types[m.type]
3256 # Complex types have a matching definition e.g. VkStruct.
3257 # Not needed for base types such as uint32_t.
3258 if "data" in type_info:
3259 types[m.type]["data"].required = True
3261 def mark_struct_dependencies(struct, types):
3263 type_info = types[m.type]
3265 # Complex types have a matching definition e.g. VkStruct.
3266 # Not needed for base types such as uint32_t.
3267 if "data" in type_info:
3268 types[m.type]["data"].required = True
3270 if type_info["category"] == "struct":
3272 mark_struct_dependencies(type_info["data"], types)
3273 elif type_info["category"] == "funcpointer":
3274 mark_funcpointer_dependencies(type_info["data"], types)
3275 elif type_info["category"] == "bitmask":
3276 mark_bitmask_dependencies(type_info["data"], types)
3278 func = self.funcs[command]
3279 func.required = True
3281 # Pull in return type
3282 if func.type != "void":
3283 self.types[func.type]["data"].required = True
3285 # Analyze parameter dependencies and pull in any type needed.
3286 for p in func.params:
3287 type_info = self.types[p.type]
3289 # Check if we are dealing with a complex type e.g. VkEnum, VkStruct and others.
3290 if "data" not in type_info:
3293 # Mark the complex type as required.
3294 type_info["data"].required = True
3295 if type_info["category"] == "struct":
3296 struct = type_info["data"]
3297 mark_struct_dependencies(struct, self.types)
3298 elif type_info["category"] == "bitmask":
3299 mark_bitmask_dependencies(type_info["data"], self.types)
3301 def _match_object_types(self):
3302 """ Matches each handle with the correct object type. """
3303 # Use upper case comparison for simplicity.
3305 for value in self.enums["VkObjectType"].values:
3306 object_name = "VK" + value.name[len("VK_OBJECT_TYPE"):].replace("_", "")
3307 object_types[object_name] = value.name
3309 for handle in self.handles:
3310 if not handle.is_required():
3312 handle.object_type = object_types.get(handle.name.upper())
3313 if not handle.object_type:
3314 LOGGER.warning("No object type found for {}".format(handle.name))
3316 def _parse_commands(self, root):
3317 """ Parse command section containing the Vulkan function calls. """
3319 commands = root.findall("./commands/")
3321 # As of Vulkan 1.1, various extensions got promoted to Core.
3322 # The old commands (e.g. KHR) are available for backwards compatibility
3323 # and are marked in vk.xml as 'alias' to the non-extension type.
3324 # The registry likes to avoid data duplication, so parameters and other
3325 # metadata need to be looked up from the Core command.
3326 # We parse the alias commands in a second pass.
3328 for command in commands:
3329 alias_name = command.attrib.get("alias")
3331 alias_commands.append(command)
3334 func = VkFunction.from_xml(command, self.types)
3335 funcs[func.name] = func
3337 for command in alias_commands:
3338 alias_name = command.attrib.get("alias")
3339 alias = funcs[alias_name]
3340 func = VkFunction.from_alias(command, alias)
3341 funcs[func.name] = func
3343 # To make life easy for the code generation, separate all function
3344 # calls out in the 4 types of Vulkan functions:
3345 # device, global, physical device and instance.
3350 for func in funcs.values():
3351 if func.is_device_func():
3352 device_funcs.append(func)
3353 elif func.is_global_func():
3354 global_funcs.append(func)
3355 elif func.is_phys_dev_func():
3356 phys_dev_funcs.append(func)
3358 instance_funcs.append(func)
3360 # Sort function lists by name and store them.
3361 self.device_funcs = sorted(device_funcs, key=lambda func: func.name)
3362 self.global_funcs = sorted(global_funcs, key=lambda func: func.name)
3363 self.phys_dev_funcs = sorted(phys_dev_funcs, key=lambda func: func.name)
3364 self.instance_funcs = sorted(instance_funcs, key=lambda func: func.name)
3366 # The funcs dictionary is used as a convenient way to lookup function
3367 # calls when needed e.g. to adjust member variables.
3368 self.funcs = OrderedDict(sorted(funcs.items()))
3370 def _parse_enums(self, root):
3371 """ Parse enums section or better described as constants section. """
3374 for enum in root.findall("./enums"):
3375 name = enum.attrib.get("name")
3376 _type = enum.attrib.get("type")
3378 if _type in ("enum", "bitmask"):
3379 enums[name] = VkEnum.from_xml(enum)
3381 # If no type is set, we are dealing with API constants.
3382 for value in enum.findall("enum"):
3383 # If enum is an alias, set the value to the alias name.
3384 # E.g. VK_LUID_SIZE_KHR is an alias to VK_LUID_SIZE.
3385 alias = value.attrib.get("alias")
3387 self.consts.append(VkConstant(value.attrib.get("name"), alias))
3389 self.consts.append(VkConstant(value.attrib.get("name"), value.attrib.get("value")))
3391 self.enums = OrderedDict(sorted(enums.items()))
3393 def _process_require_enum(self, enum_elem, ext=None, only_aliased=False):
3394 if "extends" in enum_elem.keys():
3395 enum = self.types[enum_elem.attrib["extends"]]["data"]
3397 # Need to define VkEnumValues which were aliased to by another value. This is necessary
3398 # from VK spec version 1.2.135 where the provisional VK_KHR_ray_tracing extension was
3399 # added which altered VK_NV_ray_tracing's VkEnumValues to alias to the provisional
3402 for _, t in self.types.items():
3403 if t["category"] != "enum":
3407 for value in t["data"].values:
3408 if value.alias == enum_elem.attrib["name"]:
3411 if only_aliased and not aliased:
3414 if "bitpos" in enum_elem.keys():
3415 # We need to add an extra value to an existing enum type.
3416 # E.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG to VkFormatFeatureFlagBits.
3417 enum.create_bitpos(enum_elem.attrib["name"], int(enum_elem.attrib["bitpos"]))
3419 elif "offset" in enum_elem.keys():
3420 # Extensions promoted to Core, have the extension number as part
3421 # of the enum value. Else retrieve from the extension tag.
3422 if enum_elem.attrib.get("extnumber"):
3423 ext_number = int(enum_elem.attrib.get("extnumber"))
3425 ext_number = int(ext.attrib["number"])
3426 offset = int(enum_elem.attrib["offset"])
3427 value = EXT_BASE + (ext_number - 1) * EXT_BLOCK_SIZE + offset
3429 # Deal with negative values.
3430 direction = enum_elem.attrib.get("dir")
3431 if direction is not None:
3434 enum.create_value(enum_elem.attrib["name"], str(value))
3436 elif "value" in enum_elem.keys():
3437 enum.create_value(enum_elem.attrib["name"], enum_elem.attrib["value"])
3438 elif "alias" in enum_elem.keys():
3439 enum.create_alias(enum_elem.attrib["name"], enum_elem.attrib["alias"])
3441 elif "value" in enum_elem.keys():
3442 # Constants are not aliased, no need to add them here, they'll get added later on.
3446 self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["value"]))
3449 def _require_type(type_info):
3450 if type_info.is_alias():
3451 type_info = type_info.alias
3452 type_info.required = True
3453 if type(type_info) == VkStruct:
3454 for member in type_info.members:
3455 if "data" in member.type_info:
3456 VkRegistry._require_type(member.type_info["data"])
3458 def _parse_extensions(self, root):
3459 """ Parse extensions section and pull in any types and commands for this extension. """
3461 exts = root.findall("./extensions/extension")
3463 skipped_exts = UNSUPPORTED_EXTENSIONS.copy()
3465 def process_ext(ext, deferred=False):
3466 ext_name = ext.attrib["name"]
3468 # Set extension name on any functions calls part of this extension as we
3469 # were not aware of the name during initial parsing.
3470 commands = ext.findall("require/command")
3471 for command in commands:
3472 cmd_name = command.attrib["name"]
3473 # Need to verify that the command is defined, and otherwise skip it.
3474 # vkCreateScreenSurfaceQNX is declared in <extensions> but not defined in
3475 # <commands>. A command without a definition cannot be enabled, so it's valid for
3476 # the XML file to handle this, but because of the manner in which we parse the XML
3477 # file we pre-populate from <commands> before we check if a command is enabled.
3478 if cmd_name in self.funcs:
3479 self.funcs[cmd_name].extensions.add(ext_name)
3481 # Some extensions are not ready or have numbers reserved as a place holder.
3482 if ext.attrib["supported"] == "disabled":
3483 LOGGER.debug("Skipping disabled extension: {0}".format(ext_name))
3484 skipped_exts.append(ext_name)
3487 # Defer extensions with 'sortorder' as they are order-dependent for spec-parsing.
3488 if not deferred and "sortorder" in ext.attrib:
3489 deferred_exts.append(ext)
3492 # Disable highly experimental extensions as the APIs are unstable and can
3493 # change between minor Vulkan revisions until API is final and becomes KHR
3495 if ("KHX" in ext_name or "NVX" in ext_name) and ext_name not in ALLOWED_X_EXTENSIONS:
3496 LOGGER.debug("Skipping experimental extension: {0}".format(ext_name))
3497 skipped_exts.append(ext_name)
3500 # Extensions can define VkEnumValues which alias to provisional extensions. Pre-process
3501 # extensions to define any required VkEnumValues before the platform check below.
3502 for require in ext.findall("require"):
3503 # Extensions can add enum values to Core / extension enums, so add these.
3504 for enum_elem in require.findall("enum"):
3505 self._process_require_enum(enum_elem, ext, only_aliased=True)
3507 platform = ext.attrib.get("platform")
3508 if platform and platform != "win32":
3509 LOGGER.debug("Skipping extensions {0} for platform {1}".format(ext_name, platform))
3510 skipped_exts.append(ext_name)
3513 if not self._is_extension_supported(ext_name):
3514 LOGGER.debug("Skipping unsupported extension: {0}".format(ext_name))
3515 skipped_exts.append(ext_name)
3517 elif "requires" in ext.attrib:
3518 # Check if this extension builds on top of another unsupported extension.
3519 requires = ext.attrib["requires"].split(",")
3520 if len(set(requires).intersection(skipped_exts)) > 0:
3521 skipped_exts.append(ext_name)
3524 LOGGER.debug("Loading extension: {0}".format(ext_name))
3526 # Extensions can define one or more require sections each requiring
3527 # different features (e.g. Vulkan 1.1). Parse each require section
3528 # separately, so we can skip sections we don't want.
3529 for require in ext.findall("require"):
3530 # Extensions can add enum values to Core / extension enums, so add these.
3531 for enum_elem in require.findall("enum"):
3532 self._process_require_enum(enum_elem, ext)
3534 for t in require.findall("type"):
3535 type_info = self.types[t.attrib["name"]]["data"]
3536 self._require_type(type_info)
3537 feature = require.attrib.get("feature")
3538 if feature and not self._is_feature_supported(feature):
3541 required_extension = require.attrib.get("extension")
3542 if required_extension and not self._is_extension_supported(required_extension):
3545 # Pull in any commands we need. We infer types to pull in from the command
3547 for command in require.findall("command"):
3548 cmd_name = command.attrib["name"]
3549 self._mark_command_required(cmd_name)
3552 # Store a list with extensions.
3553 ext_info = {"name" : ext_name, "type" : ext.attrib["type"]}
3554 extensions.append(ext_info)
3557 # Process extensions, allowing for sortorder to defer extension processing
3561 deferred_exts.sort(key=lambda ext: ext.attrib["sortorder"])
3564 for ext in deferred_exts:
3565 process_ext(ext, deferred=True)
3567 # Sort in alphabetical order.
3568 self.extensions = sorted(extensions, key=lambda ext: ext["name"])
3570 def _parse_features(self, root):
3571 """ Parse the feature section, which describes Core commands and types needed. """
3573 for feature in root.findall("./feature"):
3574 feature_name = feature.attrib["name"]
3575 for require in feature.findall("require"):
3576 LOGGER.info("Including features for {0}".format(require.attrib.get("comment")))
3578 if tag.tag == "comment":
3580 elif tag.tag == "command":
3581 if not self._is_feature_supported(feature_name):
3583 name = tag.attrib["name"]
3584 self._mark_command_required(name)
3585 elif tag.tag == "enum":
3586 self._process_require_enum(tag)
3587 elif tag.tag == "type":
3588 name = tag.attrib["name"]
3590 # Skip pull in for vk_platform.h for now.
3591 if name == "vk_platform":
3594 type_info = self.types[name]
3595 type_info["data"].required = True
3597 def _parse_types(self, root):
3598 """ Parse types section, which contains all data types e.g. structs, typedefs etcetera. """
3599 types = root.findall("./types/type")
3611 type_info["category"] = t.attrib.get("category", None)
3612 type_info["requires"] = t.attrib.get("requires", None)
3614 # We parse aliases in a second pass when we know more.
3615 alias = t.attrib.get("alias")
3617 LOGGER.debug("Alias found: {0}".format(alias))
3618 alias_types.append(t)
3621 if type_info["category"] in ["include"]:
3624 if type_info["category"] == "basetype":
3625 name = t.find("name").text
3627 if not t.find("type") is None:
3628 _type = t.find("type").text
3629 tail = t.find("type").tail
3630 if tail is not None:
3631 _type += tail.strip()
3632 basetype = VkBaseType(name, _type)
3633 base_types.append(basetype)
3634 type_info["data"] = basetype
3636 # Basic C types don't need us to define them, but we do need data for them
3637 if type_info["requires"] == "vk_platform":
3638 requires = type_info["requires"]
3639 basic_c = VkBaseType(name, _type, requires=requires)
3640 type_info["data"] = basic_c
3642 if type_info["category"] == "bitmask":
3643 name = t.find("name").text
3644 _type = t.find("type").text
3646 # Most bitmasks have a requires attribute used to pull in
3647 # required '*FlagBits" enum.
3648 requires = type_info["requires"]
3649 bitmask = VkBaseType(name, _type, requires=requires)
3650 bitmasks.append(bitmask)
3651 type_info["data"] = bitmask
3653 if type_info["category"] == "define":
3654 define = VkDefine.from_xml(t)
3655 defines.append(define)
3656 type_info["data"] = define
3658 if type_info["category"] == "enum":
3659 name = t.attrib.get("name")
3660 # The type section only contains enum names, not the actual definition.
3661 # Since we already parsed the enum before, just link it in.
3663 type_info["data"] = self.enums[name]
3664 except KeyError as e:
3665 # Not all enums seem to be defined yet, typically that's for
3666 # ones ending in 'FlagBits' where future extensions may add
3668 type_info["data"] = None
3670 if type_info["category"] == "funcpointer":
3671 funcpointer = VkFunctionPointer.from_xml(t)
3672 funcpointers.append(funcpointer)
3673 type_info["data"] = funcpointer
3675 if type_info["category"] == "handle":
3676 handle = VkHandle.from_xml(t)
3677 handles.append(handle)
3678 type_info["data"] = handle
3680 if type_info["category"] in ["struct", "union"]:
3681 # We store unions among structs as some structs depend
3682 # on unions. The types are very similar in parsing and
3683 # generation anyway. The official Vulkan scripts use
3684 # a similar kind of hack.
3685 struct = VkStruct.from_xml(t)
3686 structs.append(struct)
3687 type_info["data"] = struct
3689 # Name is in general within a name tag else it is an optional
3690 # attribute on the type tag.
3691 name_elem = t.find("name")
3692 if name_elem is not None:
3693 type_info["name"] = name_elem.text
3695 type_info["name"] = t.attrib.get("name", None)
3697 # Store all type data in a shared dictionary, so we can easily
3698 # look up information for a given type. There are no duplicate
3700 self.types[type_info["name"]] = type_info
3702 # Second pass for alias types, so we can retrieve all data from
3703 # the aliased object.
3704 for t in alias_types:
3706 type_info["category"] = t.attrib.get("category")
3707 type_info["name"] = t.attrib.get("name")
3709 alias = t.attrib.get("alias")
3711 if type_info["category"] == "bitmask":
3712 bitmask = VkBaseType(type_info["name"], alias, alias=self.types[alias]["data"])
3713 bitmasks.append(bitmask)
3714 type_info["data"] = bitmask
3716 if type_info["category"] == "enum":
3717 enum = VkEnum.from_alias(t, self.types[alias]["data"])
3718 type_info["data"] = enum
3719 self.enums[enum.name] = enum
3721 if type_info["category"] == "handle":
3722 handle = VkHandle.from_alias(t, self.types[alias]["data"])
3723 handles.append(handle)
3724 type_info["data"] = handle
3726 if type_info["category"] == "struct":
3727 struct = VkStruct.from_alias(t, self.types[alias]["data"])
3728 structs.append(struct)
3729 type_info["data"] = struct
3731 self.types[type_info["name"]] = type_info
3733 # We need detailed type information during code generation
3734 # on structs for alignment reasons. Unfortunately structs
3735 # are parsed among other types, so there is no guarantee
3736 # that any types needed have been parsed already, so set
3738 for struct in structs:
3739 struct.set_type_info(self.types)
3741 # Alias structures have enum values equivalent to those of the
3742 # structure which they are aliased against. we need to ignore alias
3743 # structs when populating the struct extensions list, otherwise we
3744 # will create duplicate case entries.
3748 for structextend in struct.structextends:
3749 s = self.types[structextend]["data"]
3750 s.struct_extensions.append(struct)
3752 # Guarantee everything is sorted, so code generation doesn't have
3753 # to deal with this.
3754 self.base_types = sorted(base_types, key=lambda base_type: base_type.name)
3755 self.bitmasks = sorted(bitmasks, key=lambda bitmask: bitmask.name)
3756 self.defines = defines
3757 self.enums = OrderedDict(sorted(self.enums.items()))
3758 self.funcpointers = sorted(funcpointers, key=lambda fp: fp.name)
3759 self.handles = sorted(handles, key=lambda handle: handle.name)
3760 self.structs = sorted(structs, key=lambda struct: struct.name)
3762 def generate_vulkan_json(f):
3764 f.write(" \"file_format_version\": \"1.0.0\",\n")
3765 f.write(" \"ICD\": {\n")
3766 f.write(" \"library_path\": \".\\\\winevulkan.dll\",\n")
3767 f.write(" \"api_version\": \"{0}\"\n".format(VK_XML_VERSION))
3771 def set_working_directory():
3772 path = os.path.abspath(__file__)
3773 path = os.path.dirname(path)
3776 def download_vk_xml(filename):
3777 url = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/v{0}/xml/vk.xml".format(VK_XML_VERSION)
3778 if not os.path.isfile(filename):
3779 urllib.request.urlretrieve(url, filename)
3782 parser = argparse.ArgumentParser()
3783 parser.add_argument("-v", "--verbose", action="count", default=0, help="increase output verbosity")
3784 parser.add_argument("-x", "--xml", default=None, type=str, help="path to specification XML file")
3786 args = parser.parse_args()
3787 if args.verbose == 0:
3788 LOGGER.setLevel(logging.WARNING)
3789 elif args.verbose == 1:
3790 LOGGER.setLevel(logging.INFO)
3792 LOGGER.setLevel(logging.DEBUG)
3794 set_working_directory()
3799 vk_xml = "vk-{0}.xml".format(VK_XML_VERSION)
3800 download_vk_xml(vk_xml)
3802 registry = VkRegistry(vk_xml)
3803 generator = VkGenerator(registry)
3805 with open(WINE_VULKAN_H, "w") as f:
3806 generator.generate_vulkan_h(f)
3808 with open(WINE_VULKAN_DRIVER_H, "w") as f:
3809 generator.generate_vulkan_driver_h(f)
3811 with open(WINE_VULKAN_THUNKS_H, "w") as f:
3812 generator.generate_thunks_h(f, "wine_")
3814 with open(WINE_VULKAN_THUNKS_C, "w") as f:
3815 generator.generate_thunks_c(f, "wine_")
3817 with open(WINE_VULKAN_LOADER_THUNKS_H, "w") as f:
3818 generator.generate_loader_thunks_h(f)
3820 with open(WINE_VULKAN_LOADER_THUNKS_C, "w") as f:
3821 generator.generate_loader_thunks_c(f)
3823 with open(WINE_VULKAN_JSON, "w") as f:
3824 generate_vulkan_json(f)
3826 with open(WINE_VULKAN_SPEC, "w") as f:
3827 generator.generate_vulkan_spec(f)
3829 with open(WINE_VULKAN_LOADER_SPEC, "w") as f:
3830 generator.generate_vulkan_loader_spec(f)
3832 if __name__ == "__main__":