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.182"
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_memory_win32",
103 "VK_KHR_external_semaphore_win32",
104 # Relates to external_semaphore and needs type conversions in bitflags.
105 "VK_KHR_shared_presentable_image", # Needs WSI work.
106 "VK_KHR_win32_keyed_mutex",
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 # Extensions which are broken
120 "VK_HUAWEI_subpass_shading", # https://github.com/KhronosGroup/Vulkan-Docs/issues/1564
122 # Deprecated extensions
123 "VK_NV_external_memory_capabilities",
124 "VK_NV_external_memory_win32",
127 # The Vulkan loader provides entry-points for core functionality and important
128 # extensions. Based on vulkan-1.def this amounts to WSI extensions on 1.0.51.
131 "VK_KHR_display_swapchain",
132 "VK_KHR_get_surface_capabilities2",
135 "VK_KHR_win32_surface",
138 # Some experimental extensions are used by shipping applications so their API is extremely unlikely
139 # to change in a backwards-incompatible way. Allow translation of those extensions with WineVulkan.
140 ALLOWED_X_EXTENSIONS = [
141 "VK_NVX_binary_import",
142 "VK_NVX_image_view_handle",
145 # Functions part of our winevulkan graphics driver interface.
146 # DRIVER_VERSION should be bumped on any change to driver interface
147 # in FUNCTION_OVERRIDES
150 class ThunkType(Enum):
155 # Table of functions for which we have a special implementation.
156 # These are regular device / instance functions for which we need
157 # to do more work compared to a regular thunk or because they are
158 # part of the driver interface.
159 # - dispatch set whether we need a function pointer in the device
160 # / instance dispatch table.
161 # - driver sets whether the API is part of the driver interface.
162 # - thunk sets whether to create a thunk in vulkan_thunks.c.
163 # - NONE means there's a fully custom implementation.
164 # - PUBLIC means the implementation is fully auto generated.
165 # - PRIVATE thunks can be used in custom implementations for
167 # - loader_thunk sets whether to create a thunk for unix_funcs.
168 FUNCTION_OVERRIDES = {
170 "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
171 "vkEnumerateInstanceExtensionProperties" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
172 "vkEnumerateInstanceLayerProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
173 "vkEnumerateInstanceVersion": {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
174 "vkGetInstanceProcAddr": {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
177 "vkCreateDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
178 "vkDestroyInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE },
179 "vkEnumerateDeviceExtensionProperties" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
180 "vkEnumerateDeviceLayerProperties": {"dispatch": True, "driver": False, "thunk": ThunkType.NONE},
181 "vkEnumeratePhysicalDeviceGroups" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
182 "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
183 "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
184 "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
185 "vkGetPhysicalDeviceExternalSemaphoreProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
186 "vkGetPhysicalDeviceImageFormatProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
187 "vkGetPhysicalDeviceProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PUBLIC, "loader_thunk" : ThunkType.PRIVATE},
188 "vkGetPhysicalDeviceProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PUBLIC, "loader_thunk" : ThunkType.PRIVATE},
191 "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
192 "vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
193 "vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
194 "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
195 "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
196 "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
197 "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
198 "vkGetDeviceQueue2" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
201 "vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE},
202 "vkGetPhysicalDeviceSurfaceSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
203 "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
204 "vkGetPhysicalDeviceSurfaceFormatsKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
205 "vkGetPhysicalDeviceSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
207 # VK_KHR_get_surface_capabilities2
208 "vkGetPhysicalDeviceSurfaceCapabilities2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
209 "vkGetPhysicalDeviceSurfaceFormats2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
211 # VK_KHR_win32_surface
212 "vkCreateWin32SurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE},
213 "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
216 "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
217 "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
218 "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
219 "vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
221 # VK_KHR_external_fence_capabilities
222 "vkGetPhysicalDeviceExternalFencePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
224 # VK_KHR_external_memory_capabilities
225 "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
226 "vkGetPhysicalDeviceImageFormatProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
228 # VK_KHR_external_semaphore_capabilities
229 "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
231 # VK_KHR_device_group_creation
232 "vkEnumeratePhysicalDeviceGroupsKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
234 # VK_KHR_device_group
235 "vkGetDeviceGroupSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
236 "vkGetPhysicalDevicePresentRectanglesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
238 # VK_EXT_private_data
239 "vkGetPrivateDataEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
240 "vkSetPrivateDataEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
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},
249 "vkSubmitDebugUtilsMessageEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE},
250 "vkSetDebugUtilsObjectTagEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE},
251 "vkSetDebugUtilsObjectNameEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE},
253 # VK_EXT_debug_report
254 "vkCreateDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
255 "vkDestroyDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
256 "vkDebugReportMessageEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
258 # VK_EXT_debug_marker
259 "vkDebugMarkerSetObjectNameEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE},
260 "vkDebugMarkerSetObjectTagEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE},
263 STRUCT_CHAIN_CONVERSIONS = {
264 # Ignore to not confuse host loader.
265 "VkDeviceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO"],
266 "VkInstanceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO"],
270 class Direction(Enum):
271 """ Parameter direction: input, output, input_output. """
277 class VkBaseType(object):
278 def __init__(self, name, _type, alias=None, requires=None):
279 """ Vulkan base type class.
281 VkBaseType is mostly used by Vulkan to define its own
282 base types like VkFlags through typedef out of e.g. uint32_t.
285 name (:obj:'str'): Name of the base type.
286 _type (:obj:'str'): Underlying type
287 alias (bool): type is an alias or not.
288 requires (:obj:'str', optional): Other types required.
289 Often bitmask values pull in a *FlagBits type.
294 self.requires = requires
295 self.required = False
297 def definition(self):
298 # Definition is similar for alias or non-alias as type
299 # is already set to alias.
300 if not self.type is None:
301 return "typedef {0} {1};\n".format(self.type, self.name)
303 return "struct {0};\n".format(self.name)
306 return bool(self.alias)
309 class VkConstant(object):
310 def __init__(self, name, value):
314 def definition(self):
315 text = "#define {0} {1}\n".format(self.name, self.value)
319 class VkDefine(object):
320 def __init__(self, name, value):
325 def from_xml(define):
326 name_elem = define.find("name")
328 if name_elem is None:
329 # <type category="define" name="some_name">some_value</type>
330 name = define.attrib.get("name")
332 # We override behavior of VK_USE_64_BIT_PTR_DEFINES as the default non-dispatchable handle
333 # definition various between 64-bit (uses pointers) and 32-bit (uses uint64_t).
334 # This complicates TRACEs in the thunks, so just use uint64_t.
335 if name == "VK_USE_64_BIT_PTR_DEFINES":
336 value = "#define VK_USE_64_BIT_PTR_DEFINES 0"
339 return VkDefine(name, value)
341 # With a name element the structure is like:
342 # <type category="define"><name>some_name</name>some_value</type>
343 name = name_elem.text
345 # Perform minimal parsing for Vulkan constants, which we don't need, but are referenced
346 # elsewhere in vk.xml.
347 # - VK_API_VERSION is a messy, deprecated constant and we don't want generate code for it.
348 # - AHardwareBuffer/ANativeWindow are forward declarations for Android types, which leaked
349 # into the define region.
350 if name in ["VK_API_VERSION", "AHardwareBuffer", "ANativeWindow", "CAMetalLayer"]:
351 return VkDefine(name, None)
353 # The body of the define is basically unstructured C code. It is not meant for easy parsing.
354 # Some lines contain deprecated values or comments, which we try to filter out.
356 for line in define.text.splitlines():
357 # Skip comments or deprecated values.
364 if child.tail is not None:
365 # Split comments for VK_API_VERSION_1_0 / VK_API_VERSION_1_1
366 if "//" in child.tail:
367 value += child.tail.split("//")[0]
371 return VkDefine(name, value.rstrip(' '))
373 def definition(self):
374 if self.value is None:
377 # Nothing to do as the value was already put in the right form during parsing.
378 return "{0}\n".format(self.value)
381 class VkEnum(object):
382 def __init__(self, name, bitwidth, alias=None):
383 if not bitwidth in [32, 64]:
384 LOGGER.error("unknown bitwidth {0} for {1}".format(bitwidth, name))
386 self.bitwidth = bitwidth
387 self.values = [] if alias == None else alias.values
388 self.required = False
393 def from_alias(enum, alias):
394 name = enum.attrib.get("name")
395 aliasee = VkEnum(name, alias.bitwidth, alias=alias)
397 alias.add_aliased_by(aliasee)
402 name = enum.attrib.get("name")
403 bitwidth = int(enum.attrib.get("bitwidth", "32"))
404 result = VkEnum(name, bitwidth)
406 for v in enum.findall("enum"):
407 value_name = v.attrib.get("name")
408 # Value is either a value or a bitpos, only one can exist.
409 value = v.attrib.get("value")
410 alias_name = v.attrib.get("alias")
412 result.create_alias(value_name, alias_name)
414 result.create_value(value_name, value)
417 result.create_bitpos(value_name, int(v.attrib.get("bitpos")))
420 # vulkan.h contains a *_MAX_ENUM value set to 32-bit at the time of writing,
421 # which is to prepare for extensions as they can add values and hence affect
422 # the size definition.
423 max_name = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2', name).upper() + "_MAX_ENUM"
424 result.create_value(max_name, "0x7fffffff")
428 def create_alias(self, name, alias_name):
429 """ Create an aliased value for this enum """
430 # Older GCC versions need a literal to initialize a static const uint64_t
431 # which is what we use for 64bit bitmasks.
432 if self.bitwidth == 64:
433 alias = next(x for x in self.values if x.name == alias_name)
434 self.add(VkEnumValue(name, self.bitwidth, value=alias.value, hex=alias.hex, alias=alias_name))
436 self.add(VkEnumValue(name, self.bitwidth, alias=alias_name))
438 def create_value(self, name, value):
439 """ Create a new value for this enum """
440 # Some values are in hex form. We want to preserve the hex representation
441 # at least when we convert back to a string. Internally we want to use int.
443 self.add(VkEnumValue(name, self.bitwidth, value=int(value, 0), hex=hex))
445 def create_bitpos(self, name, pos):
446 """ Create a new bitmask value for this enum """
447 self.add(VkEnumValue(name, self.bitwidth, value=(1 << pos), hex=True))
449 def add(self, value):
450 """ Add a value to enum. """
452 # Extensions can add new enum values. When an extension is promoted to Core
453 # the registry defines the value twice once for old extension and once for
454 # new Core features. Add the duplicate if it's explicitly marked as an
455 # alias, otherwise ignore it.
456 for v in self.values:
457 if not value.is_alias() and v.value == value.value:
458 LOGGER.debug("Adding duplicate enum value {0} to {1}".format(v, self.name))
460 # Avoid adding duplicate aliases multiple times
461 if not any(x.name == value.name for x in self.values):
462 self.values.append(value)
464 def definition(self):
468 default_value = 0x7ffffffe if self.bitwidth == 32 else 0xfffffffffffffffe
470 # Print values sorted, values can have been added in a random order.
471 values = sorted(self.values, key=lambda value: value.value if value.value is not None else default_value)
473 if self.bitwidth == 32:
474 text = "typedef enum {0}\n{{\n".format(self.name)
476 text += " {0},\n".format(value.definition())
477 text += "}} {0};\n".format(self.name)
478 elif self.bitwidth == 64:
479 text = "typedef VkFlags64 {0};\n\n".format(self.name)
481 text += "static const {0} {1};\n".format(self.name, value.definition())
483 for aliasee in self.aliased_by:
484 text += "typedef {0} {1};\n".format(self.name, aliasee.name)
490 return bool(self.alias)
492 def add_aliased_by(self, aliasee):
493 self.aliased_by.append(aliasee)
496 class VkEnumValue(object):
497 def __init__(self, name, bitwidth, value=None, hex=False, alias=None):
499 self.bitwidth = bitwidth
505 postfix = "ull" if self.bitwidth == 64 else ""
506 if self.is_alias() and self.value == None:
507 return "{0}={1}".format(self.name, self.alias)
508 return "{0}={1}{2}".format(self.name, self.value, postfix)
510 def definition(self):
511 """ Convert to text definition e.g. VK_FOO = 1 """
512 postfix = "ull" if self.bitwidth == 64 else ""
513 if self.is_alias() and self.value == None:
514 return "{0} = {1}".format(self.name, self.alias)
516 # Hex is commonly used for FlagBits and sometimes within
517 # a non-FlagBits enum for a bitmask value as well.
519 return "{0} = 0x{1:08x}{2}".format(self.name, self.value, postfix)
521 return "{0} = {1}{2}".format(self.name, self.value, postfix)
524 return self.alias is not None
527 class VkFunction(object):
528 def __init__(self, _type=None, name=None, params=[], extensions=[], alias=None):
535 # For some functions we need some extra metadata from FUNCTION_OVERRIDES.
536 func_info = FUNCTION_OVERRIDES.get(self.name, None)
537 self.dispatch = func_info["dispatch"] if func_info else True
538 self.driver = func_info["driver"] if func_info else False
539 self.thunk_type = func_info["thunk"] if func_info else ThunkType.PUBLIC
540 self.loader_thunk_type = func_info["loader_thunk"] if func_info and "loader_thunk" in func_info else ThunkType.PUBLIC
542 # Required is set while parsing which APIs and types are required
543 # and is used by the code generation.
544 self.required = True if func_info else False
547 def from_alias(command, alias):
548 """ Create VkFunction from an alias command.
551 command: xml data for command
552 alias (VkFunction): function to use as a base for types / parameters.
557 func_name = command.attrib.get("name")
558 func_type = alias.type
559 params = alias.params
561 return VkFunction(_type=func_type, name=func_name, params=params, alias=alias)
564 def from_xml(command, types):
565 proto = command.find("proto")
566 func_name = proto.find("name").text
567 func_type = proto.find("type").text
570 for param in command.findall("param"):
571 vk_param = VkParam.from_xml(param, types)
572 params.append(vk_param)
574 return VkFunction(_type=func_type, name=func_name, params=params)
576 def get_conversions(self):
577 """ Get a list of conversion functions required for this function if any.
578 Parameters which are structures may require conversion between win32
579 and the host platform. This function returns a list of conversions
584 for param in self.params:
585 convs = param.get_conversions()
586 if convs is not None:
587 conversions.extend(convs)
592 return bool(self.alias)
594 def is_core_func(self):
595 """ Returns whether the function is a Vulkan core function.
596 Core functions are APIs defined by the Vulkan spec to be part of the
597 Core API as well as several KHR WSI extensions.
600 if not self.extensions:
603 return any(ext in self.extensions for ext in CORE_EXTENSIONS)
605 def is_device_func(self):
606 # If none of the other, it must be a device function
607 return not self.is_global_func() and not self.is_instance_func() and not self.is_phys_dev_func()
609 def is_driver_func(self):
610 """ Returns if function is part of Wine driver interface. """
613 def is_global_func(self):
614 # Treat vkGetInstanceProcAddr as a global function as it
615 # can operate with NULL for vkInstance.
616 if self.name == "vkGetInstanceProcAddr":
618 # Global functions are not passed a dispatchable object.
619 elif self.params[0].is_dispatchable():
623 def is_instance_func(self):
624 # Instance functions are passed VkInstance.
625 if self.params[0].type == "VkInstance":
629 def is_phys_dev_func(self):
630 # Physical device functions are passed VkPhysicalDevice.
631 if self.params[0].type == "VkPhysicalDevice":
635 def is_required(self):
638 def needs_conversion(self):
639 """ Check if the function needs any input/output type conversion.
640 Functions need input/output conversion if struct parameters have
641 alignment differences between Win32 and Linux 32-bit.
644 for p in self.params:
645 if p.needs_conversion():
646 LOGGER.debug("Parameter {0} to {1} requires conversion".format(p.name, self.name))
651 def needs_unwrapping(self):
652 """ Check if the function needs any input/output type unwrapping.
653 Functions need input/output unwrapping if struct parameters have
657 for p in self.params:
658 if p.needs_unwrapping():
663 def needs_dispatch(self):
666 def needs_thunk(self):
667 return self.thunk_type != ThunkType.NONE
669 def needs_private_thunk(self):
670 return self.thunk_type == ThunkType.PRIVATE
672 def pfn(self, prefix="p", call_conv=None, conv=False):
673 """ Create function pointer. """
676 pfn = "{0} ({1} *{2}_{3})(".format(self.type, call_conv, prefix, self.name)
678 pfn = "{0} (*{1}_{2})(".format(self.type, prefix, self.name)
680 for i, param in enumerate(self.params):
682 pfn += param.const + " "
685 if conv and param.needs_conversion():
688 if param.is_pointer():
689 pfn += " " + param.pointer
691 if param.array_len is not None:
692 pfn += "[{0}]".format(param.array_len)
694 if i < len(self.params) - 1:
699 def prototype(self, call_conv=None, prefix=None, postfix=None):
700 """ Generate prototype for given function.
703 call_conv (str, optional): calling convention e.g. WINAPI
704 prefix (str, optional): prefix to append prior to function name e.g. vkFoo -> wine_vkFoo
705 postfix (str, optional): text to append after function name but prior to semicolon e.g. DECLSPEC_HIDDEN
708 proto = "{0}".format(self.type)
710 if call_conv is not None:
711 proto += " {0}".format(call_conv)
713 if prefix is not None:
714 proto += " {0}{1}(".format(prefix, self.name)
716 proto += " {0}(".format(self.name)
718 # Add all the parameters.
719 proto += ", ".join([p.definition() for p in self.params])
721 if postfix is not None:
722 proto += ") {0}".format(postfix)
731 if not self.needs_private_thunk():
732 body += " {0}".format(self.trace())
734 params = ", ".join([p.variable(conv=False) for p in self.params])
736 # Call the native Vulkan function.
737 if self.type == "void":
738 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
740 body += " return {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
744 def loader_body(self):
747 params = ", ".join([p.name for p in self.params])
749 # Call the function from unix_funcs.
750 if self.type == "void":
751 body += " unix_funcs->p_{0}({1});\n".format(self.name, params)
753 body += " return unix_funcs->p_{0}({1});\n".format(self.name, params)
757 def body_conversion(self, conv):
760 # Declare a variable to hold the result for non-void functions.
761 if self.type != "void":
762 body += " {0} result;\n".format(self.type)
764 # Declare any tmp parameters for conversion.
765 for p in self.params:
766 if p.needs_conversion() and conv:
767 if p.is_dynamic_array():
768 body += " {0}_host *{1}_host;\n".format(p.type, p.name)
770 body += " {0}_host {1}_host;\n".format(p.type, p.name)
771 elif p.needs_unwrapping():
772 if p.is_dynamic_array():
773 body += " {0} *{1}_host;\n".format(p.type, p.name)
775 body += " {0} {1}_host;\n".format(p.type, p.name)
777 if not self.needs_private_thunk():
778 body += " {0}\n".format(self.trace())
780 # Call any win_to_host conversion calls.
781 for p in self.params:
782 if p.needs_input_conversion() and (p.needs_unwrapping() or conv):
783 body += p.copy(Direction.INPUT)
785 # Build list of parameters containing converted and non-converted parameters.
786 # The param itself knows if conversion is needed and applies it when we set conv=True.
787 params = ", ".join([p.variable(conv=conv) for p in self.params])
789 # Call the native Vulkan function.
790 if self.type == "void":
791 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
793 body += " result = {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
797 # Call any host_to_win conversion calls.
798 for p in self.params:
799 if not p.needs_output_conversion():
802 body += p.copy(Direction.OUTPUT)
804 # Perform any required cleanups. Most of these are for array functions.
805 for p in self.params:
806 if p.needs_free() and (p.needs_unwrapping() or conv):
809 # Finally return the result.
810 if self.type != "void":
811 body += " return result;\n"
815 def spec(self, prefix=None, symbol=None):
816 """ Generate spec file entry for this function.
819 prefix (str, optional): prefix to prepend to entry point name.
820 symbol (str, optional): allows overriding the name of the function implementing the entry point.
824 params = " ".join([p.spec() for p in self.params])
825 if prefix is not None:
826 spec += "@ stdcall -private {0}{1}({2})".format(prefix, self.name, params)
828 spec += "@ stdcall {0}({1})".format(self.name, params)
830 if symbol is not None:
836 def stub(self, call_conv=None, prefix=None):
837 stub = self.prototype(call_conv=call_conv, prefix=prefix)
839 stub += " {0}".format(self.trace(message="stub: ", trace_func="FIXME"))
841 if self.type == "VkResult":
842 stub += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
843 elif self.type == "VkBool32":
844 stub += " return VK_FALSE;\n"
845 elif self.type == "PFN_vkVoidFunction":
846 stub += " return NULL;\n"
851 def thunk(self, call_conv=None, prefix=None):
852 thunk = self.prototype(call_conv=call_conv, prefix=prefix)
855 if self.needs_conversion():
856 thunk += "#if defined(USE_STRUCT_CONVERSION)\n"
857 thunk += self.body_conversion(conv=True)
859 if self.needs_unwrapping():
860 thunk += self.body_conversion(conv=False)
864 elif self.needs_unwrapping():
865 thunk += self.body_conversion(conv=False)
872 def loader_thunk(self, prefix=None):
873 thunk = self.prototype(call_conv="WINAPI", prefix=prefix)
875 thunk += self.loader_body()
879 def trace(self, message=None, trace_func=None):
880 """ Create a trace string including all parameters.
883 message (str, optional): text to print at start of trace message e.g. 'stub: '
884 trace_func (str, optional): used to override trace function e.g. FIXME, printf, etcetera.
886 if trace_func is not None:
887 trace = "{0}(\"".format(trace_func)
891 if message is not None:
894 # First loop is for all the format strings.
895 trace += ", ".join([p.format_string() for p in self.params])
898 # Second loop for parameter names and optional conversions.
899 for param in self.params:
900 if param.format_conv is not None:
901 trace += ", " + param.format_conv.format(param.name)
903 trace += ", {0}".format(param.name)
909 class VkFunctionPointer(object):
910 def __init__(self, _type, name, members, forward_decls):
912 self.members = members
914 self.required = False
915 self.forward_decls = forward_decls
918 def from_xml(funcpointer):
922 for t in funcpointer.findall("type"):
924 # <type>void</type>* pUserData,
925 # Parsing of the tail (anything past </type>) is tricky since there
926 # can be other data on the next line like: const <type>int</type>..
928 const = True if begin and "const" in begin else False
930 lines = t.tail.split(",\n")
931 if lines[0][0] == "*":
933 name = lines[0][1:].strip()
936 name = lines[0].strip()
938 # Filter out ); if it is contained.
939 name = name.partition(");")[0]
941 # If tail encompasses multiple lines, assign the second line to begin
944 begin = lines[1].strip()
948 members.append(VkMember(const=const, _type=_type, pointer=pointer, name=name))
950 _type = funcpointer.text
951 name = funcpointer.find("name").text
952 if "requires" in funcpointer.attrib:
953 forward_decls = funcpointer.attrib.get("requires").split(",")
956 return VkFunctionPointer(_type, name, members, forward_decls)
958 def definition(self):
960 # forward declare required structs
961 for decl in self.forward_decls:
962 text += "typedef struct {0} {0};\n".format(decl)
964 text += "{0} {1})(\n".format(self.type, self.name)
967 if len(self.members) > 0:
968 for m in self.members:
970 text += " " + m.definition()
973 text += ",\n " + m.definition()
975 # Just make the compiler happy by adding a void parameter.
983 class VkHandle(object):
984 def __init__(self, name, _type, parent, alias=None):
989 self.required = False
990 self.object_type = None
993 def from_alias(handle, alias):
994 name = handle.attrib.get("name")
995 return VkHandle(name, alias.type, alias.parent, alias=alias)
998 def from_xml(handle):
999 name = handle.find("name").text
1000 _type = handle.find("type").text
1001 parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice.
1002 return VkHandle(name, _type, parent)
1004 def dispatch_table(self):
1005 if not self.is_dispatchable():
1008 if self.parent is None:
1009 # Should only happen for VkInstance
1011 elif self.name == "VkDevice":
1012 # VkDevice has VkInstance as a parent, but has its own dispatch table.
1014 elif self.parent in ["VkInstance", "VkPhysicalDevice"]:
1015 return "instance->funcs"
1016 elif self.parent in ["VkDevice", "VkCommandPool"]:
1017 return "device->funcs"
1019 LOGGER.error("Unhandled dispatchable parent: {0}".format(self.parent))
1021 def definition(self):
1022 """ Generates handle definition e.g. VK_DEFINE_HANDLE(vkInstance) """
1024 # Legacy types are typedef'ed to the new type if they are aliases.
1026 return "typedef {0} {1};\n".format(self.alias.name, self.name)
1028 return "{0}({1})\n".format(self.type, self.name)
1031 return self.alias is not None
1033 def is_dispatchable(self):
1034 """ Some handles like VkInstance, VkDevice are dispatchable objects,
1035 which means they contain a dispatch table of function pointers.
1037 return self.type == "VK_DEFINE_HANDLE"
1039 def is_required(self):
1040 return self.required
1042 def native_handle(self, name):
1043 """ Provide access to the native handle of a wrapped object. """
1045 if self.name == "VkCommandPool":
1046 return "wine_cmd_pool_from_handle({0})->command_pool".format(name)
1047 if self.name == "VkDebugUtilsMessengerEXT":
1048 return "wine_debug_utils_messenger_from_handle({0})->debug_messenger".format(name)
1049 if self.name == "VkDebugReportCallbackEXT":
1050 return "wine_debug_report_callback_from_handle({0})->debug_callback".format(name)
1051 if self.name == "VkSurfaceKHR":
1052 return "wine_surface_from_handle({0})->surface".format(name)
1054 native_handle_name = None
1056 if self.name == "VkCommandBuffer":
1057 native_handle_name = "command_buffer"
1058 if self.name == "VkDevice":
1059 native_handle_name = "device"
1060 if self.name == "VkInstance":
1061 native_handle_name = "instance"
1062 if self.name == "VkPhysicalDevice":
1063 native_handle_name = "phys_dev"
1064 if self.name == "VkQueue":
1065 native_handle_name = "queue"
1067 if native_handle_name:
1068 return "{0}->{1}".format(name, native_handle_name)
1070 if self.is_dispatchable():
1071 LOGGER.error("Unhandled native handle for: {0}".format(self.name))
1074 def driver_handle(self, name):
1075 """ Provide access to the handle that should be passed to the wine driver """
1077 if self.name == "VkSurfaceKHR":
1078 return "wine_surface_from_handle({0})->driver_surface".format(name)
1080 return self.native_handle(name)
1082 def is_wrapped(self):
1083 return self.native_handle("test") is not None
1085 def needs_conversion(self):
1088 def needs_unwrapping(self):
1089 return self.is_wrapped()
1091 class VkMember(object):
1092 def __init__(self, const=False, struct_fwd_decl=False,_type=None, pointer=None, name=None, array_len=None,
1093 dyn_array_len=None, optional=False, values=None):
1095 self.struct_fwd_decl = struct_fwd_decl
1097 self.pointer = pointer
1099 self.type_info = None
1100 self.array_len = array_len
1101 self.dyn_array_len = dyn_array_len
1102 self.optional = optional
1103 self.values = values
1105 def __eq__(self, other):
1106 """ Compare member based on name against a string.
1108 This method is for convenience by VkStruct, which holds a number of members and needs quick checking
1109 if certain members exist.
1112 return self.name == other
1115 return "{0} {1} {2} {3} {4} {5} {6}".format(self.const, self.struct_fwd_decl, self.type, self.pointer,
1116 self.name, self.array_len, self.dyn_array_len)
1119 def from_xml(member):
1120 """ Helper function for parsing a member tag within a struct or union. """
1122 name_elem = member.find("name")
1123 type_elem = member.find("type")
1126 struct_fwd_decl = False
1131 values = member.get("values")
1134 if "const" in member.text:
1137 # Some members contain forward declarations:
1138 # - VkBaseInstructure has a member "const struct VkBaseInStructure *pNext"
1139 # - VkWaylandSurfaceCreateInfoKHR has a member "struct wl_display *display"
1140 if "struct" in member.text:
1141 struct_fwd_decl = True
1143 if type_elem is not None:
1144 member_type = type_elem.text
1145 if type_elem.tail is not None:
1146 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1148 # Name of other member within, which stores the number of
1149 # elements pointed to be by this member.
1150 dyn_array_len = member.get("len")
1152 # Some members are optional, which is important for conversion code e.g. not dereference NULL pointer.
1153 optional = True if member.get("optional") else False
1155 # Usually we need to allocate memory for dynamic arrays. We need to do the same in a few other cases
1156 # like for VkCommandBufferBeginInfo.pInheritanceInfo. Just threat such cases as dynamic arrays of
1157 # size 1 to simplify code generation.
1158 if dyn_array_len is None and pointer is not None:
1161 # Some members are arrays, attempt to parse these. Formats include:
1162 # <member><type>char</type><name>extensionName</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
1163 # <member><type>uint32_t</type><name>foo</name>[4]</member>
1164 if name_elem.tail and name_elem.tail[0] == '[':
1165 LOGGER.debug("Found array type")
1166 enum_elem = member.find("enum")
1167 if enum_elem is not None:
1168 array_len = enum_elem.text
1170 # Remove brackets around length
1171 array_len = name_elem.tail.strip("[]")
1173 return VkMember(const=const, struct_fwd_decl=struct_fwd_decl, _type=member_type, pointer=pointer, name=name_elem.text,
1174 array_len=array_len, dyn_array_len=dyn_array_len, optional=optional, values=values)
1176 def copy(self, input, output, direction, conv):
1177 """ Helper method for use by conversion logic to generate a C-code statement to copy this member.
1178 - `conv` indicates whether the statement is in a struct alignment conversion path. """
1180 if (conv and self.needs_conversion()) or self.needs_unwrapping():
1181 if self.is_dynamic_array():
1182 if direction == Direction.OUTPUT:
1183 LOGGER.warn("TODO: implement copying of returnedonly dynamic array for {0}.{1}".format(self.type, self.name))
1185 # Array length is either a variable name (string) or an int.
1186 count = self.dyn_array_len if isinstance(self.dyn_array_len, int) else "{0}{1}".format(input, self.dyn_array_len)
1187 return "{0}{1} = convert_{2}_array_win_to_host({3}{1}, {4});\n".format(output, self.name, self.type, input, count)
1188 elif self.is_static_array():
1189 count = self.array_len
1190 if direction == Direction.OUTPUT:
1191 # Needed by VkMemoryHeap.memoryHeaps
1192 return "convert_{0}_static_array_host_to_win({2}{1}, {3}{1}, {4});\n".format(self.type, self.name, input, output, count)
1194 # Nothing needed this yet.
1195 LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
1196 elif self.is_handle() and self.needs_unwrapping():
1197 if direction == Direction.OUTPUT:
1198 LOGGER.err("OUTPUT parameter {0}.{1} cannot be unwrapped".format(self.type, self.name))
1200 handle = self.type_info["data"]
1201 return "{0}{1} = {2};\n".format(output, self.name, handle.driver_handle("{0}{1}".format(input, self.name)))
1203 if direction == Direction.OUTPUT:
1204 return "convert_{0}_host_to_win(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
1206 return "convert_{0}_win_to_host(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
1207 elif self.is_static_array():
1208 bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type)
1209 return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count)
1211 return "{0}{1} = {2}{1};\n".format(output, self.name, input)
1213 def free(self, location, conv):
1214 """ Helper method for use by conversion logic to generate a C-code statement to free this member. """
1216 if not self.needs_unwrapping() and not conv:
1219 # Add a cast to ignore const on conversion structs we allocated ourselves.
1220 # sample expected output: (VkSparseMemoryBind_host *)
1222 cast = "(" + self.type
1223 if self.needs_conversion() and conv:
1229 if self.is_dynamic_array():
1230 count = self.dyn_array_len if isinstance(self.dyn_array_len, int) else "{0}{1}".format(location, self.dyn_array_len)
1231 if self.is_struct() and self.type_info["data"].returnedonly:
1232 # For returnedonly, counts is stored in a pointer.
1233 return "free_{0}_array({1}{2}{3}, *{4});\n".format(self.type, cast, location, self.name, count)
1235 return "free_{0}_array({1}{2}{3}, {4});\n".format(self.type, cast, location, self.name, count)
1237 # We are operating on a single structure. Some structs (very rare) contain dynamic members,
1238 # which would need freeing.
1239 if self.needs_free():
1240 return "free_{0}({1}&{2}{3});\n".format(self.type, cast, location, self.name)
1243 def definition(self, align=False, conv=False):
1244 """ Generate prototype for given function.
1247 align (bool, optional): Enable alignment if a type needs it. This adds WINE_VK_ALIGN(8) to a member.
1248 conv (bool, optional): Enable conversion if a type needs it. This appends '_host' to the name.
1255 if self.is_struct_forward_declaration():
1258 if conv and self.is_struct():
1259 text += "{0}_host".format(self.type)
1263 if self.is_pointer():
1264 text += " {0}{1}".format(self.pointer, self.name)
1266 if align and self.needs_alignment():
1267 text += " WINE_VK_ALIGN(8) " + self.name
1269 text += " " + self.name
1271 if self.is_static_array():
1272 text += "[{0}]".format(self.array_len)
1276 def get_conversions(self):
1277 """ Return any conversion description for this member and its children when conversion is needed. """
1279 # Check if we need conversion either for this member itself or for any child members
1280 # in case member represents a struct.
1281 if not self.needs_conversion() and not self.needs_unwrapping():
1286 # Collect any conversion for any member structs.
1287 if self.is_struct():
1288 struct = self.type_info["data"]
1290 m.needs_struct_extensions_conversion()
1291 if m.needs_conversion() or m.needs_unwrapping():
1292 conversions.extend(m.get_conversions())
1294 struct.needs_struct_extensions_conversion()
1295 direction = Direction.OUTPUT if struct.returnedonly else Direction.INPUT
1296 elif self.is_handle():
1297 direction = Direction.INPUT
1299 operand = self.type_info["data"]
1300 if self.is_dynamic_array():
1301 conversions.append(ConversionFunction(False, True, direction, operand))
1302 elif self.is_static_array():
1303 conversions.append(ConversionFunction(True, False, direction, operand))
1305 conversions.append(ConversionFunction(False, False, direction, operand))
1307 if self.needs_free():
1308 conversions.append(FreeFunction(self.is_dynamic_array(), operand))
1315 def is_dynamic_array(self):
1316 """ Returns if the member is an array element.
1317 Vulkan uses this for dynamically sized arrays for which
1318 there is a 'count' parameter.
1320 return self.dyn_array_len is not None
1322 def is_handle(self):
1323 return self.type_info["category"] == "handle"
1325 def is_pointer(self):
1326 return self.pointer is not None
1328 def is_static_array(self):
1329 """ Returns if the member is an array.
1330 Vulkan uses this often for fixed size arrays in which the
1331 length is part of the member.
1333 return self.array_len is not None
1335 def is_struct(self):
1336 return self.type_info["category"] == "struct"
1338 def is_struct_forward_declaration(self):
1339 return self.struct_fwd_decl
1342 return self.type_info["category"] == "union"
1344 def needs_alignment(self):
1345 """ Check if this member needs alignment for 64-bit data.
1346 Various structures need alignment on 64-bit variables due
1347 to compiler differences on 32-bit between Win32 and Linux.
1350 if self.is_pointer():
1352 elif self.type == "size_t":
1354 elif self.type in ["uint64_t", "VkDeviceSize"]:
1356 elif self.is_struct():
1357 struct = self.type_info["data"]
1358 return struct.needs_alignment()
1359 elif self.is_handle():
1360 # Dispatchable handles are pointers to objects, while
1361 # non-dispatchable are uint64_t and hence need alignment.
1362 handle = self.type_info["data"]
1363 return False if handle.is_dispatchable() else True
1366 def needs_conversion(self):
1367 """ Structures requiring alignment, need conversion between win32 and host. """
1369 if not self.is_struct():
1372 struct = self.type_info["data"]
1373 return struct.needs_conversion()
1375 def needs_unwrapping(self):
1376 """ Structures with wrapped handles need unwrapping. """
1378 if self.is_struct():
1379 struct = self.type_info["data"]
1380 return struct.needs_unwrapping()
1382 if self.is_handle():
1383 handle = self.type_info["data"]
1384 return handle.is_wrapped()
1388 def needs_free(self):
1389 if not self.needs_conversion() and not self.needs_unwrapping():
1392 if self.is_dynamic_array():
1395 # TODO: some non-pointer structs and optional pointer structs may need freeing,
1396 # though none of this type have been encountered yet.
1399 def needs_struct_extensions_conversion(self):
1400 if not self.is_struct():
1403 struct = self.type_info["data"]
1404 return struct.needs_struct_extensions_conversion()
1406 def set_type_info(self, type_info):
1407 """ Helper function to set type information from the type registry.
1408 This is needed, because not all type data is available at time of
1411 self.type_info = type_info
1414 class VkParam(object):
1415 """ Helper class which describes a parameter to a function call. """
1417 def __init__(self, type_info, const=None, pointer=None, name=None, array_len=None, dyn_array_len=None, object_type=None):
1420 self.array_len = array_len
1421 self.dyn_array_len = dyn_array_len
1422 self.pointer = pointer
1423 self.object_type = object_type
1424 self.type_info = type_info
1425 self.type = type_info["name"] # For convenience
1426 self.handle = type_info["data"] if type_info["category"] == "handle" else None
1427 self.struct = type_info["data"] if type_info["category"] == "struct" else None
1429 self._set_direction()
1430 self._set_format_string()
1431 self._set_conversions()
1434 return "{0} {1} {2} {3} {4} {5}".format(self.const, self.type, self.pointer, self.name, self.array_len, self.dyn_array_len)
1437 def from_xml(param, types):
1438 """ Helper function to create VkParam from xml. """
1440 # Parameter parsing is slightly tricky. All the data is contained within
1441 # a param tag, but some data is within subtags while others are text
1442 # before or after the type tag.
1444 # <param>const <type>char</type>* <name>pLayerName</name></param>
1446 name_elem = param.find("name")
1448 name = name_elem.text
1449 # Tail contains array length e.g. for blendConstants param of vkSetBlendConstants
1450 if name_elem.tail is not None:
1451 array_len = name_elem.tail.strip("[]")
1453 # Name of other parameter in function prototype, which stores the number of
1454 # elements pointed to be by this parameter.
1455 dyn_array_len = param.get("len", None)
1457 const = param.text.strip() if param.text else None
1458 type_elem = param.find("type")
1459 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1461 # Some uint64_t are actually handles with a separate type param
1462 object_type = param.get("objecttype", None)
1464 # Since we have parsed all types before hand, this should not happen.
1465 type_info = types.get(type_elem.text, None)
1466 if type_info is None:
1467 LOGGER.err("type info not found for: {0}".format(type_elem.text))
1469 return VkParam(type_info, const=const, pointer=pointer, name=name, array_len=array_len, dyn_array_len=dyn_array_len, object_type=object_type)
1471 def _set_conversions(self):
1472 """ Internal helper function to configure any needed conversion functions. """
1474 self.free_func = None
1475 self.input_conv = None
1476 self.output_conv = None
1477 if not self.needs_conversion() and not self.needs_unwrapping():
1480 operand = self.struct if self.is_struct() else self.handle
1482 # Input functions require win to host conversion.
1483 if self._direction in [Direction.INPUT, Direction.INPUT_OUTPUT]:
1484 self.input_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.INPUT, operand)
1486 # Output functions require host to win conversion.
1487 if self._direction in [Direction.INPUT_OUTPUT, Direction.OUTPUT]:
1488 self.output_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.OUTPUT, operand)
1490 # Dynamic arrays, but also some normal structs (e.g. VkCommandBufferBeginInfo) need memory
1491 # allocation and thus some cleanup.
1492 if self.is_dynamic_array() or self.struct.needs_free():
1493 self.free_func = FreeFunction(self.is_dynamic_array(), operand)
1495 def _set_direction(self):
1496 """ Internal helper function to set parameter direction (input/output/input_output). """
1498 # The parameter direction needs to be determined from hints in vk.xml like returnedonly,
1499 # parameter constness and other heuristics.
1500 # For now we need to get this right for structures as we need to convert these, we may have
1501 # missed a few other edge cases (e.g. count variables).
1502 # See also https://github.com/KhronosGroup/Vulkan-Docs/issues/610
1504 if not self.is_pointer():
1505 self._direction = Direction.INPUT
1506 elif self.is_const() and self.is_pointer():
1507 self._direction = Direction.INPUT
1508 elif self.is_struct():
1509 if not self.struct.returnedonly:
1510 self._direction = Direction.INPUT
1513 # Returnedonly hints towards output, however in some cases
1514 # it is inputoutput. In particular if pNext / sType exist,
1515 # which are used to link in other structures without having
1516 # to introduce new APIs. E.g. vkGetPhysicalDeviceProperties2KHR.
1517 if "pNext" in self.struct:
1518 self._direction = Direction.INPUT_OUTPUT
1521 self._direction = Direction.OUTPUT
1523 # This should mostly be right. Count variables can be inout, but we don't care about these yet.
1524 self._direction = Direction.OUTPUT
1526 def _set_format_string(self):
1527 """ Internal helper function to be used by constructor to set format string. """
1529 # Determine a format string used by code generation for traces.
1530 # 64-bit types need a conversion function.
1531 self.format_conv = None
1532 if self.is_static_array() or self.is_pointer():
1533 self.format_str = "%p"
1535 if self.type_info["category"] in ["bitmask"]:
1536 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1537 if self.type_info["data"].type == "VkFlags64":
1538 self.format_str = "0x%s"
1539 self.format_conv = "wine_dbgstr_longlong({0})"
1541 self.format_str = "%#x"
1542 elif self.type_info["category"] in ["enum"]:
1543 self.format_str = "%#x"
1544 elif self.is_handle():
1545 # We use uint64_t for non-dispatchable handles as opposed to pointers
1546 # for dispatchable handles.
1547 if self.handle.is_dispatchable():
1548 self.format_str = "%p"
1550 self.format_str = "0x%s"
1551 self.format_conv = "wine_dbgstr_longlong({0})"
1552 elif self.type == "float":
1553 self.format_str = "%f"
1554 elif self.type == "int":
1555 self.format_str = "%d"
1556 elif self.type == "int32_t":
1557 self.format_str = "%d"
1558 elif self.type == "size_t":
1559 self.format_str = "0x%s"
1560 self.format_conv = "wine_dbgstr_longlong({0})"
1561 elif self.type in ["uint16_t", "uint32_t", "VkBool32"]:
1562 self.format_str = "%u"
1563 elif self.type in ["uint64_t", "VkDeviceAddress", "VkDeviceSize"]:
1564 self.format_str = "0x%s"
1565 self.format_conv = "wine_dbgstr_longlong({0})"
1566 elif self.type == "HANDLE":
1567 self.format_str = "%p"
1568 elif self.type in ["VisualID", "xcb_visualid_t", "RROutput", "zx_handle_t"]:
1569 # Don't care about specific types for non-Windows platforms.
1570 self.format_str = ""
1572 LOGGER.warn("Unhandled type: {0}".format(self.type_info))
1574 def copy(self, direction):
1575 if direction == Direction.INPUT:
1576 if self.is_dynamic_array():
1577 return " {0}_host = convert_{1}_array_win_to_host({0}, {2});\n".format(self.name, self.type, self.dyn_array_len)
1579 return " convert_{0}_win_to_host({1}, &{1}_host);\n".format(self.type, self.name)
1581 if self.is_dynamic_array():
1582 LOGGER.error("Unimplemented output conversion for: {0}".format(self.name))
1584 return " convert_{0}_host_to_win(&{1}_host, {1});\n".format(self.type, self.name)
1586 def definition(self, postfix=None):
1587 """ Return prototype for the parameter. E.g. 'const char *foo' """
1591 proto += self.const + " "
1595 if self.is_pointer():
1596 proto += " {0}{1}".format(self.pointer, self.name)
1598 proto += " " + self.name
1600 # Allows appending something to the variable name useful for
1601 # win32 to host conversion.
1602 if postfix is not None:
1605 if self.is_static_array():
1606 proto += "[{0}]".format(self.array_len)
1610 def direction(self):
1611 """ Returns parameter direction: input, output, input_output.
1613 Parameter direction in Vulkan is not straight-forward, which this function determines.
1616 return self._direction
1618 def dispatch_table(self):
1619 """ Return functions dispatch table pointer for dispatchable objects. """
1621 if not self.is_dispatchable():
1624 return "{0}->{1}".format(self.name, self.handle.dispatch_table())
1626 def format_string(self):
1627 return self.format_str
1630 if self.is_dynamic_array():
1631 if self.is_struct() and self.struct.returnedonly:
1632 # For returnedonly, counts is stored in a pointer.
1633 return " free_{0}_array({1}_host, *{2});\n".format(self.type, self.name, self.dyn_array_len)
1635 return " free_{0}_array({1}_host, {2});\n".format(self.type, self.name, self.dyn_array_len)
1637 # We are operating on a single structure. Some structs (very rare) contain dynamic members,
1638 # which would need freeing.
1639 if self.is_struct() and self.struct.needs_free():
1640 return " free_{0}(&{1}_host);\n".format(self.type, self.name)
1643 def get_conversions(self):
1644 """ Get a list of conversions required for this parameter if any.
1645 Parameters which are structures may require conversion between win32
1646 and the host platform. This function returns a list of conversions
1650 if self.is_struct():
1651 self.struct.needs_struct_extensions_conversion()
1652 for m in self.struct:
1653 m.needs_struct_extensions_conversion()
1654 elif not self.is_handle():
1657 if not self.needs_conversion() and not self.needs_unwrapping():
1662 # Collect any member conversions first, so we can guarantee
1663 # those functions will be defined prior to usage by the
1664 # 'parent' param requiring conversion.
1665 if self.is_struct():
1666 for m in self.struct:
1667 if not m.is_struct():
1670 if not m.needs_conversion() and not m.needs_unwrapping():
1673 conversions.extend(m.get_conversions())
1675 # Conversion requirements for the 'parent' parameter.
1676 if self.input_conv is not None:
1677 conversions.append(self.input_conv)
1678 if self.output_conv is not None:
1679 conversions.append(self.output_conv)
1680 if self.free_func is not None:
1681 conversions.append(self.free_func)
1686 return self.const is not None
1688 def is_dynamic_array(self):
1689 return self.dyn_array_len is not None
1691 def is_dispatchable(self):
1692 if not self.is_handle():
1695 return self.handle.is_dispatchable()
1697 def is_handle(self):
1698 return self.handle is not None
1700 def is_pointer(self):
1701 return self.pointer is not None
1703 def is_static_array(self):
1704 return self.array_len is not None
1706 def is_struct(self):
1707 return self.struct is not None
1709 def needs_conversion(self):
1710 """ Returns if parameter needs conversion between win32 and host. """
1712 if not self.is_struct():
1715 # VkSparseImageMemoryRequirements(2) is used by vkGetImageSparseMemoryRequirements(2).
1716 # This function is tricky to wrap, because how to wrap depends on whether
1717 # pSparseMemoryRequirements is NULL or not. Luckily for VkSparseImageMemoryRequirements(2)
1718 # the alignment works out in such a way that no conversion is needed between win32 and Linux.
1719 if self.type in ["VkSparseImageMemoryRequirements", "VkSparseImageMemoryRequirements2"]:
1722 # If a structure needs alignment changes, it means we need to
1723 # perform parameter conversion between win32 and host.
1724 if self.struct.needs_conversion():
1729 def needs_unwrapping(self):
1730 """ Returns if parameter needs unwrapping of handle. """
1732 # Wrapped handle parameters are handled separately, only look for wrapped handles in structs
1733 if self.is_struct():
1734 return self.struct.needs_unwrapping()
1736 if self.is_handle() and self.is_dynamic_array():
1737 return self.handle.needs_unwrapping()
1741 def needs_free(self):
1742 return self.free_func is not None
1744 def needs_input_conversion(self):
1745 return self.input_conv is not None
1747 def needs_output_conversion(self):
1748 return self.output_conv is not None
1751 """ Generate spec file entry for this parameter. """
1753 if self.is_pointer() and self.type == "char":
1755 if self.is_dispatchable() or self.is_pointer() or self.is_static_array():
1757 if self.type_info["category"] in ["bitmask"]:
1758 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1759 if self.type_info["data"].type == "VkFlags64":
1763 if self.type_info["category"] in ["enum"]:
1765 if self.is_handle() and not self.is_dispatchable():
1767 if self.type == "float":
1769 if self.type in ["int", "int32_t", "size_t", "uint16_t", "uint32_t", "VkBool32"]:
1771 if self.type in ["uint64_t", "VkDeviceSize"]:
1774 LOGGER.error("Unhandled spec conversion for type: {0}".format(self.type))
1776 def variable(self, conv=False):
1777 """ Returns 'glue' code during generation of a function call on how to access the variable.
1778 This function handles various scenarios such as 'unwrapping' if dispatchable objects and
1779 renaming of parameters in case of win32 -> host conversion.
1782 conv (bool, optional): Enable conversion if the param needs it. This appends '_host' to the name.
1785 # Hack until we enable allocation callbacks from ICD to application. These are a joy
1786 # to enable one day, because of calling convention conversion.
1787 if "VkAllocationCallbacks" in self.type:
1788 LOGGER.debug("TODO: setting NULL VkAllocationCallbacks for {0}".format(self.name))
1791 if self.needs_unwrapping() or (conv and self.needs_conversion()):
1792 if self.is_dynamic_array():
1793 return "{0}_host".format(self.name)
1795 return "&{0}_host".format(self.name)
1797 if self.object_type != None and self.type == "uint64_t":
1798 return "wine_vk_unwrap_handle({0}, {1})".format(self.object_type, self.name)
1800 # We need to pass the native handle to the native Vulkan calls and
1801 # the wine driver's handle to calls which are wrapped by the driver.
1802 driver_handle = self.handle.driver_handle(self.name) if self.is_handle() else None
1803 return driver_handle if driver_handle else self.name
1806 class VkStruct(Sequence):
1807 """ Class which represents the type union and struct. """
1809 def __init__(self, name, members, returnedonly, structextends, alias=None, union=False):
1811 self.members = members
1812 self.returnedonly = returnedonly
1813 self.structextends = structextends
1814 self.required = False
1817 self.type_info = None # To be set later.
1818 self.struct_extensions = []
1819 self.aliased_by = []
1821 def __getitem__(self, i):
1822 return self.members[i]
1825 return len(self.members)
1828 def from_alias(struct, alias):
1829 name = struct.attrib.get("name")
1830 aliasee = VkStruct(name, alias.members, alias.returnedonly, alias.structextends, alias=alias)
1832 alias.add_aliased_by(aliasee)
1836 def from_xml(struct):
1837 # Unions and structs are the same parsing wise, but we need to
1838 # know which one we are dealing with later on for code generation.
1839 union = True if struct.attrib["category"] == "union" else False
1841 name = struct.attrib.get("name")
1843 # 'Output' structures for which data is filled in by the API are
1844 # marked as 'returnedonly'.
1845 returnedonly = True if struct.attrib.get("returnedonly") else False
1847 structextends = struct.attrib.get("structextends")
1848 structextends = structextends.split(",") if structextends else []
1851 for member in struct.findall("member"):
1852 vk_member = VkMember.from_xml(member)
1853 members.append(vk_member)
1855 return VkStruct(name, members, returnedonly, structextends, union=union)
1858 def decouple_structs(structs):
1859 """ Helper function which decouples a list of structs.
1860 Structures often depend on other structures. To make the C compiler
1861 happy we need to define 'substructures' first. This function analyzes
1862 the list of structures and reorders them in such a way that they are
1866 tmp_structs = list(structs) # Don't modify the original structures.
1867 decoupled_structs = []
1869 while (len(tmp_structs) > 0):
1870 for struct in tmp_structs:
1873 if not struct.required:
1874 tmp_structs.remove(struct)
1878 if not (m.is_struct() or m.is_union()):
1881 # VkBaseInstructure and VkBaseOutStructure reference themselves.
1882 if m.type == struct.name:
1886 # Check if a struct we depend on has already been defined.
1887 for s in decoupled_structs:
1888 if s.name == m.type:
1893 # Check if the struct we depend on is even in the list of structs.
1894 # If found now, it means we haven't met all dependencies before we
1895 # can operate on the current struct.
1896 # When generating 'host' structs we may not be able to find a struct
1897 # as the list would only contain the structs requiring conversion.
1898 for s in tmp_structs:
1899 if s.name == m.type:
1903 if dependends == False:
1904 decoupled_structs.append(struct)
1905 tmp_structs.remove(struct)
1907 return decoupled_structs
1909 def definition(self, align=False, conv=False, postfix=None):
1910 """ Convert structure to textual definition.
1913 align (bool, optional): enable alignment to 64-bit for win32 struct compatibility.
1914 conv (bool, optional): enable struct conversion if the struct needs it.
1915 postfix (str, optional): text to append to end of struct name, useful for struct renaming.
1918 # Only define alias structs when doing conversions
1919 if self.is_alias() and not conv:
1923 text = "typedef union {0}".format(self.name)
1925 text = "typedef struct {0}".format(self.name)
1927 if postfix is not None:
1933 if align and m.needs_alignment():
1934 text += " {0};\n".format(m.definition(align=align))
1935 elif conv and m.needs_conversion():
1936 text += " {0};\n".format(m.definition(conv=conv))
1938 text += " {0};\n".format(m.definition())
1940 if postfix is not None:
1941 text += "}} {0}{1};\n".format(self.name, postfix)
1943 text += "}} {0};\n".format(self.name)
1945 for aliasee in self.aliased_by:
1946 text += "typedef {0} {1};\n".format(self.name, aliasee.name)
1951 return bool(self.alias)
1953 def add_aliased_by(self, aliasee):
1954 self.aliased_by.append(aliasee)
1956 def needs_alignment(self):
1957 """ Check if structure needs alignment for 64-bit data.
1958 Various structures need alignment on 64-bit variables due
1959 to compiler differences on 32-bit between Win32 and Linux.
1962 for m in self.members:
1963 if m.needs_alignment():
1967 def needs_conversion(self):
1968 """ Returns if struct members needs conversion between win32 and host.
1969 Structures need conversion if they contain members requiring alignment
1970 or if they include other structures which need alignment.
1973 if self.needs_alignment():
1976 for m in self.members:
1977 if m.needs_conversion():
1981 def needs_unwrapping(self):
1982 """ Returns if struct members need unwrapping of handle. """
1984 for m in self.members:
1985 if m.needs_unwrapping():
1989 def needs_free(self):
1990 """ Check if any struct member needs some memory freeing."""
1992 for m in self.members:
2000 def needs_struct_extensions_conversion(self):
2001 """ Checks if structure extensions in pNext chain need conversion. """
2004 for e in self.struct_extensions:
2005 if e.required and e.needs_conversion():
2006 LOGGER.error("Unhandled pNext chain alignment conversion for {0}".format(e.name))
2008 if e.required and e.needs_unwrapping():
2009 LOGGER.error("Unhandled pNext chain unwrapping conversion for {0}".format(e.name))
2014 def set_type_info(self, types):
2015 """ Helper function to set type information from the type registry.
2016 This is needed, because not all type data is available at time of
2019 for m in self.members:
2020 type_info = types[m.type]
2021 m.set_type_info(type_info)
2024 class ConversionFunction(object):
2025 def __init__(self, array, dyn_array, direction, operand):
2027 self.direction = direction
2028 self.dyn_array = dyn_array
2029 self.operand = operand
2030 self.type = operand.name
2034 def __eq__(self, other):
2035 return self.name == other.name
2037 def _generate_array_conversion_func(self):
2038 """ Helper function for generating a conversion function for array operands. """
2042 if self.operand.needs_conversion():
2043 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2045 if self.direction == Direction.OUTPUT:
2046 params = ["const {0}_host *in".format(self.type), "uint32_t count"]
2047 return_type = self.type
2049 params = ["const {0} *in".format(self.type), "uint32_t count"]
2050 return_type = "{0}_host".format(self.type)
2052 # Generate function prototype.
2053 body += "static inline {0} *{1}(".format(return_type, self.name)
2054 body += ", ".join(p for p in params)
2057 body += " {0} *out;\n".format(return_type)
2059 if self.operand.needs_unwrapping():
2060 if self.operand.needs_conversion():
2063 params = ["const {0} *in".format(self.type), "uint32_t count"]
2064 return_type = "{0}".format(self.type)
2066 # Generate function prototype.
2067 body += "static inline {0} *{1}(".format(return_type, self.name)
2068 body += ", ".join(p for p in params)
2071 body += " {0} *out;\n".format(return_type)
2073 if self.operand.needs_conversion():
2074 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2076 body += " unsigned int i;\n\n"
2077 body += " if (!in) return NULL;\n\n"
2079 body += " out = malloc(count * sizeof(*out));\n"
2081 body += " for (i = 0; i < count; i++)\n"
2084 if isinstance(self.operand, VkStruct):
2085 for m in self.operand:
2086 # TODO: support copying of pNext extension structures!
2087 # Luckily though no extension struct at this point needs conversion.
2088 convert = m.copy("in[i].", "out[i].", self.direction, conv=True)
2089 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2090 body += " " + convert
2092 unwrap = m.copy("in[i].", "out[i].", self.direction, conv=False)
2093 if unwrap == convert:
2094 body += " " + unwrap
2096 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2097 body += " " + convert
2099 body += " " + unwrap
2100 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2102 elif isinstance(self.operand, VkHandle) and self.direction == Direction.INPUT:
2103 body += " out[i] = " + self.operand.driver_handle("in[i]") + ";\n"
2105 LOGGER.warn("Unhandled conversion operand type")
2106 body += " out[i] = in[i];\n"
2109 body += " return out;\n"
2112 if not self.operand.needs_unwrapping():
2113 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2119 def _generate_conversion_func(self):
2120 """ Helper function for generating a conversion function for non-array operands. """
2122 # It doesn't make sense to generate conversion functions for non-struct variables
2123 # which aren't in arrays, as this should be handled by the copy() function
2124 if not isinstance(self.operand, VkStruct):
2129 if self.operand.needs_conversion():
2130 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2131 body += "static inline void {0}(".format(self.name)
2133 if self.direction == Direction.OUTPUT:
2134 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type)]
2136 params = ["const {0} *in".format(self.type), "{0}_host *out".format(self.type)]
2138 # Generate parameter list
2139 body += ", ".join(p for p in params)
2142 if self.operand.needs_unwrapping():
2143 if self.operand.needs_conversion():
2146 body += "static inline void {0}(".format(self.name)
2148 params = ["const {0} *in".format(self.type), "{0} *out".format(self.type)]
2150 # Generate parameter list
2151 body += ", ".join(p for p in params)
2154 if self.operand.needs_conversion():
2155 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2157 body += "{\n if (!in) return;\n\n"
2159 if self.direction == Direction.INPUT and "pNext" in self.operand and self.operand.returnedonly:
2160 # We are dealing with an input_output parameter. For these we only need to copy
2161 # pNext and sType as the other fields are filled in by the host. We do potentially
2162 # have to iterate over pNext and perform conversions based on switch(sType)!
2163 # Luckily though no extension structs at this point need conversion.
2164 # TODO: support copying of pNext extension structures!
2165 body += " out->pNext = in->pNext;\n"
2166 body += " out->sType = in->sType;\n"
2168 for m in self.operand:
2169 # TODO: support copying of pNext extension structures!
2170 convert = m.copy("in->", "out->", self.direction, conv=True)
2171 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2172 body += " " + convert
2174 unwrap = m.copy("in->", "out->", self.direction, conv=False)
2175 if unwrap == convert:
2176 body += " " + unwrap
2178 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2179 body += " " + convert
2181 body += " " + unwrap
2182 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2186 if not self.operand.needs_unwrapping():
2187 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2193 def _generate_static_array_conversion_func(self):
2194 """ Helper function for generating a conversion function for array operands. """
2198 if self.operand.needs_conversion():
2199 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2201 if self.direction == Direction.OUTPUT:
2202 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
2204 params = ["const {0} *in".format(self.type), "{0} *out_host".format(self.type), "uint32_t count"]
2206 # Generate function prototype.
2207 body += "static inline void {0}(".format(self.name)
2208 body += ", ".join(p for p in params)
2211 if self.operand.needs_unwrapping():
2212 if self.operand.needs_conversion():
2215 params = ["const {0} *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
2217 # Generate function prototype.
2218 body += "static inline void {0}(".format(self.name)
2219 body += ", ".join(p for p in params)
2223 body += " unsigned int i;\n\n"
2224 body += " if (!in) return;\n\n"
2225 body += " for (i = 0; i < count; i++)\n"
2228 if isinstance(self.operand, VkStruct):
2229 for m in self.operand:
2230 # TODO: support copying of pNext extension structures!
2231 convert = m.copy("in[i].", "out[i].", self.direction, conv=True)
2232 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2233 body += " " + convert
2235 unwrap = m.copy("in[i].", "out[i].", self.direction, conv=False)
2236 if unwrap == convert:
2237 body += " " + unwrap
2239 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2240 body += " " + convert
2242 body += " " + unwrap
2243 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2244 elif isinstance(self.operand, VkHandle) and self.direction == Direction.INPUT:
2245 body += " out[i] = " + self.operand.driver_handle("in[i]") + ";\n"
2247 LOGGER.warn("Unhandled conversion operand type")
2248 body += " out[i] = in[i];\n"
2253 if not self.operand.needs_unwrapping():
2254 body += "#endif /* USE_STRUCT_CONVERSION) */\n"
2260 def _set_name(self):
2261 if self.direction == Direction.INPUT:
2263 name = "convert_{0}_static_array_win_to_host".format(self.type)
2264 elif self.dyn_array:
2265 name = "convert_{0}_array_win_to_host".format(self.type)
2267 name = "convert_{0}_win_to_host".format(self.type)
2268 else: # Direction.OUTPUT
2270 name = "convert_{0}_static_array_host_to_win".format(self.type)
2271 elif self.dyn_array:
2272 name = "convert_{0}_array_host_to_win".format(self.type)
2274 name = "convert_{0}_host_to_win".format(self.type)
2278 def definition(self):
2280 return self._generate_static_array_conversion_func()
2281 elif self.dyn_array:
2282 return self._generate_array_conversion_func()
2284 return self._generate_conversion_func()
2287 class FreeFunction(object):
2288 def __init__(self, dyn_array, operand):
2289 self.dyn_array = dyn_array
2290 self.operand = operand
2291 self.type = operand.name
2294 self.name = "free_{0}_array".format(self.type)
2296 self.name = "free_{0}".format(self.type)
2298 def __eq__(self, other):
2299 return self.name == other.name
2301 def _generate_array_free_func(self):
2302 """ Helper function for cleaning up temporary buffers required for array conversions. """
2306 if self.operand.needs_conversion():
2307 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2308 # Generate function prototype.
2309 body += "static inline void {0}({1}_host *in, uint32_t count)\n".format(self.name, self.type)
2311 if self.operand.needs_unwrapping():
2312 if self.operand.needs_conversion():
2315 # Generate function prototype.
2316 body += "static inline void {0}({1} *in, uint32_t count)\n".format(self.name, self.type)
2318 if self.operand.needs_conversion():
2319 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2323 # E.g. VkGraphicsPipelineCreateInfo_host needs freeing for pStages.
2324 if isinstance(self.operand, VkStruct) and self.operand.needs_free():
2325 body += " unsigned int i;\n\n"
2326 body += " if (!in) return;\n\n"
2327 body += " for (i = 0; i < count; i++)\n"
2330 for m in self.operand:
2332 convert = m.free("in[i].", conv=True)
2333 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2334 body += " " + convert
2336 unwrap = m.free("in[i].", conv=False)
2337 if convert == unwrap:
2338 body += " " + unwrap
2340 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2341 body += " " + convert
2342 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2344 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2345 body += " " + convert
2347 body += " " + unwrap
2348 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2351 body += " if (!in) return;\n\n"
2353 body += " free(in);\n"
2357 if not self.operand.needs_unwrapping():
2358 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2364 def _generate_free_func(self):
2365 # E.g. VkCommandBufferBeginInfo.pInheritanceInfo needs freeing.
2366 if not self.operand.needs_free():
2369 if not isinstance(self.operand, VkStruct):
2374 if self.operand.needs_conversion():
2375 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2376 # Generate function prototype.
2377 body += "static inline void {0}({1}_host *in)\n".format(self.name, self.type)
2379 if self.operand.needs_unwrapping():
2380 if self.operand.needs_conversion():
2383 # Generate function prototype.
2384 body += "static inline void {0}({1} *in)\n".format(self.name, self.type)
2386 if self.operand.needs_conversion():
2387 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2391 for m in self.operand:
2393 convert = m.free("in->", conv=True)
2394 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2395 body += " " + convert
2397 unwrap = m.free("in->", conv=False)
2398 if convert == unwrap:
2399 body += " " + unwrap
2401 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2402 body += " " + convert
2403 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2405 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2406 body += " " + convert
2408 body += " " + unwrap
2409 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2413 if not self.operand.needs_unwrapping():
2414 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2420 def definition(self):
2422 return self._generate_array_free_func()
2424 # Some structures need freeing too if they contain dynamic arrays.
2425 # E.g. VkCommandBufferBeginInfo
2426 return self._generate_free_func()
2429 class StructChainConversionFunction(object):
2430 def __init__(self, direction, struct, ignores):
2431 self.direction = direction
2432 self.struct = struct
2433 self.ignores = ignores
2434 self.type = struct.name
2436 self.name = "convert_{0}_struct_chain".format(self.type)
2438 def __eq__(self, other):
2439 return self.name == other.name
2441 def prototype(self, postfix=""):
2442 return "VkResult {0}(const void *pNext, {1} *out_struct) {2}".format(self.name, self.type, postfix).strip()
2444 def definition(self):
2445 body = self.prototype()
2448 body += " VkBaseOutStructure *out_header = (VkBaseOutStructure *)out_struct;\n";
2449 body += " const VkBaseInStructure *in_header;\n\n";
2451 body += " out_header->pNext = NULL;\n\n"
2453 body += " for (in_header = pNext; in_header; in_header = in_header->pNext)\n"
2455 body += " switch (in_header->sType)\n"
2458 for i in self.ignores:
2459 body += " case {0}:\n".format(i)
2460 body += " break;\n\n"
2462 for e in self.struct.struct_extensions:
2466 stype = next(x for x in e.members if x.name == "sType")
2468 if stype.values in self.ignores:
2471 body += " case {0}:\n".format(stype.values)
2474 body += " const {0} *in = (const {0} *)in_header;\n".format(e.name)
2475 body += " {0} *out;\n\n".format(e.name)
2477 body += " if (!(out = malloc(sizeof(*out)))) goto out_of_memory;\n\n"
2480 if m.name == "pNext":
2481 body += " out->pNext = NULL;\n"
2483 convert = m.copy("in->", "out->", self.direction, conv=True)
2484 unwrap = m.copy("in->", "out->", self.direction, conv=False)
2485 if unwrap == convert:
2486 body += " " + unwrap
2488 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2489 body += " " + convert
2491 body += " " + unwrap
2492 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2494 body += "\n out_header->pNext = (VkBaseOutStructure *)out;\n"
2495 body += " out_header = out_header->pNext;\n"
2499 body += " default:\n"
2500 body += " FIXME(\"Application requested a linked structure of type %u.\\n\", in_header->sType);\n"
2505 body += " return VK_SUCCESS;\n"
2507 if any(x for x in self.struct.struct_extensions if x.required):
2508 body += "\nout_of_memory:\n"
2509 body += " free_{0}_struct_chain(out_struct);\n".format(self.type)
2510 body += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
2515 class FreeStructChainFunction(object):
2516 def __init__(self, struct):
2517 self.struct = struct
2518 self.type = struct.name
2520 self.name = "free_{0}_struct_chain".format(self.type)
2522 def __eq__(self, other):
2523 return self.name == other.name
2525 def prototype(self, postfix=""):
2526 return "void {0}({1} *s) {2}".format(self.name, self.type, postfix).strip()
2528 def definition(self):
2529 body = self.prototype()
2532 body += " VkBaseOutStructure *header = (void *)s->pNext;\n\n";
2534 body += " while (header)\n"
2536 body += " void *prev = header;\n\n"
2537 body += " switch (header->sType)\n"
2540 for e in self.struct.struct_extensions:
2544 if not any(m.needs_free() for m in e):
2547 stype = next(x for x in e.members if x.name == "sType")
2549 body += " case {0}:\n".format(stype.values)
2551 body += " {0} *structure = ({0} *) header;\n".format(e.name)
2555 convert = m.free("structure->", conv=True)
2556 unwrap = m.free("structure->", conv=False)
2557 if convert == unwrap:
2558 body += " " + unwrap
2560 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2561 body += " " + convert
2562 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2564 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2565 body += " " + convert
2567 body += " " + unwrap
2568 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2573 body += " default:\n"
2577 body += " header = header->pNext;\n"
2578 body += " free(prev);\n"
2581 body += " s->pNext = NULL;\n"
2587 class VkGenerator(object):
2588 def __init__(self, registry):
2589 self.registry = registry
2591 # Build a list conversion functions for struct conversion.
2592 self.conversions = []
2593 self.struct_chain_conversions = []
2594 self.host_structs = []
2595 for func in self.registry.funcs.values():
2596 if not func.is_required():
2599 if not func.needs_conversion() and not func.needs_unwrapping():
2602 conversions = func.get_conversions()
2603 for conv in conversions:
2604 # Pull in any conversions for vulkan_thunks.c.
2605 if func.needs_thunk():
2606 # Append if we don't already have this conversion.
2607 if not any(c == conv for c in self.conversions):
2608 self.conversions.append(conv)
2610 if not isinstance(conv.operand, VkStruct):
2613 # Structs can be used in different ways by different conversions
2614 # e.g. array vs non-array. Just make sure we pull in each struct once.
2615 if not any(s.name == conv.operand.name for s in self.host_structs):
2616 self.host_structs.append(conv.operand)
2618 for struct in self.registry.structs:
2619 if struct.name in STRUCT_CHAIN_CONVERSIONS:
2620 self.struct_chain_conversions.append(StructChainConversionFunction(Direction.INPUT, struct, STRUCT_CHAIN_CONVERSIONS[struct.name]))
2621 self.struct_chain_conversions.append(FreeStructChainFunction(struct))
2622 # Once we decide to support pNext chains conversion everywhere, move this under get_conversions
2623 for e in struct.struct_extensions:
2625 if m.needs_conversion() or m.needs_unwrapping():
2626 conversions = m.get_conversions()
2627 for conv in conversions:
2628 if not any(c == conv for c in self.conversions):
2629 self.conversions.append(conv)
2631 def _generate_copyright(self, f, spec_file=False):
2632 f.write("# " if spec_file else "/* ")
2633 f.write("Automatically generated from Vulkan vk.xml; DO NOT EDIT!\n")
2634 lines = ["", "This file is generated from Vulkan vk.xml file covered",
2635 "by the following copyright and permission notice:"]
2636 lines.extend([l.rstrip(" ") for l in self.registry.copyright.splitlines()])
2638 f.write("{0}{1}".format("# " if spec_file else " * ", line).rstrip(" ") + "\n")
2639 f.write("\n" if spec_file else " */\n\n")
2641 def generate_thunks_c(self, f, prefix):
2642 self._generate_copyright(f)
2645 f.write("#pragma makedep unix\n")
2646 f.write("#endif\n\n")
2648 f.write("#include \"config.h\"\n")
2649 f.write("#include \"wine/port.h\"\n\n")
2651 f.write("#include \"vulkan_private.h\"\n\n")
2653 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2655 # Generate any conversion helper functions.
2656 for conv in self.conversions:
2657 f.write(conv.definition())
2659 for conv in self.struct_chain_conversions:
2660 f.write(conv.definition())
2662 # Create thunks for instance and device functions.
2663 # Global functions don't go through the thunks.
2664 for vk_func in self.registry.funcs.values():
2665 if not vk_func.is_required():
2668 if vk_func.is_global_func():
2671 if not vk_func.needs_thunk():
2674 if vk_func.needs_private_thunk():
2675 f.write(vk_func.thunk(prefix="thunk_"))
2678 f.write(vk_func.thunk(prefix=prefix, call_conv="WINAPI"))
2680 # Create array of device extensions.
2681 f.write("static const char * const vk_device_extensions[] =\n{\n")
2682 for ext in self.registry.extensions:
2683 if ext["type"] != "device":
2686 f.write(" \"{0}\",\n".format(ext["name"]))
2689 # Create array of instance extensions.
2690 f.write("static const char * const vk_instance_extensions[] =\n{\n")
2691 for ext in self.registry.extensions:
2692 if ext["type"] != "instance":
2695 f.write(" \"{0}\",\n".format(ext["name"]))
2698 f.write("BOOL wine_vk_device_extension_supported(const char *name)\n")
2700 f.write(" unsigned int i;\n")
2701 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_extensions); i++)\n")
2703 f.write(" if (strcmp(vk_device_extensions[i], name) == 0)\n")
2704 f.write(" return TRUE;\n")
2706 f.write(" return FALSE;\n")
2709 f.write("BOOL wine_vk_instance_extension_supported(const char *name)\n")
2711 f.write(" unsigned int i;\n")
2712 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_extensions); i++)\n")
2714 f.write(" if (strcmp(vk_instance_extensions[i], name) == 0)\n")
2715 f.write(" return TRUE;\n")
2717 f.write(" return FALSE;\n")
2720 f.write("BOOL wine_vk_is_type_wrapped(VkObjectType type)\n")
2722 f.write(" return FALSE")
2723 for handle in self.registry.handles:
2724 if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2726 f.write(" ||\n type == {}".format(handle.object_type))
2730 f.write("uint64_t wine_vk_unwrap_handle(VkObjectType type, uint64_t handle)\n")
2732 f.write(" switch(type)\n")
2734 for handle in self.registry.handles:
2735 if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2737 f.write(" case {}:\n".format(handle.object_type))
2738 if handle.is_dispatchable():
2739 f.write(" return (uint64_t) (uintptr_t) ")
2740 f.write(handle.native_handle("(({}) (uintptr_t) handle)".format(handle.name)))
2742 f.write(" return (uint64_t) ")
2743 f.write(handle.native_handle("handle"))
2745 f.write(" default:\n")
2746 f.write(" return handle;\n")
2750 f.write("const struct unix_funcs loader_funcs =\n")
2752 for vk_func in self.registry.funcs.values():
2753 if not vk_func.is_required():
2755 if vk_func.loader_thunk_type == ThunkType.NONE:
2758 f.write(" &{1}{0},\n".format(vk_func.name, prefix))
2761 def generate_thunks_h(self, f, prefix):
2762 self._generate_copyright(f)
2764 f.write("#ifndef __WINE_VULKAN_THUNKS_H\n")
2765 f.write("#define __WINE_VULKAN_THUNKS_H\n\n")
2767 f.write("#define WINE_VK_VERSION VK_API_VERSION_{0}_{1}\n\n".format(WINE_VK_VERSION[0], WINE_VK_VERSION[1]))
2769 # Generate prototypes for device and instance functions requiring a custom implementation.
2770 f.write("/* Functions for which we have custom implementations outside of the thunks. */\n")
2771 for vk_func in self.registry.funcs.values():
2772 if not vk_func.is_required():
2774 if vk_func.needs_thunk() and not vk_func.needs_private_thunk():
2777 if vk_func.is_core_func():
2778 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix=prefix)))
2780 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix=prefix, postfix="DECLSPEC_HIDDEN")))
2783 f.write("/* Private thunks */\n")
2784 for vk_func in self.registry.funcs.values():
2785 if vk_func.needs_private_thunk():
2786 f.write("{0};\n".format(vk_func.prototype(prefix="thunk_", postfix="DECLSPEC_HIDDEN")))
2789 for struct in self.host_structs:
2790 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
2791 f.write(struct.definition(align=False, conv=True, postfix="_host"))
2793 f.write("typedef {0} {0}_host;\n".format(struct.name))
2794 f.write("#endif\n\n")
2797 for func in self.struct_chain_conversions:
2798 f.write(func.prototype(postfix="DECLSPEC_HIDDEN") + ";\n")
2801 f.write("/* For use by vkDevice and children */\n")
2802 f.write("struct vulkan_device_funcs\n{\n")
2803 for vk_func in self.registry.device_funcs:
2804 if not vk_func.is_required():
2807 if not vk_func.needs_dispatch():
2808 LOGGER.debug("skipping {0} in vulkan_device_funcs".format(vk_func.name))
2811 f.write(" {0};\n".format(vk_func.pfn(conv=vk_func.needs_conversion())))
2814 f.write("/* For use by vkInstance and children */\n")
2815 f.write("struct vulkan_instance_funcs\n{\n")
2816 for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs:
2817 if not vk_func.is_required():
2820 if not vk_func.needs_dispatch():
2821 LOGGER.debug("skipping {0} in vulkan_instance_funcs".format(vk_func.name))
2824 f.write(" {0};\n".format(vk_func.pfn(conv=vk_func.needs_conversion())))
2827 f.write("#define ALL_VK_DEVICE_FUNCS() \\\n")
2829 for vk_func in self.registry.device_funcs:
2830 if not vk_func.is_required():
2833 if not vk_func.needs_dispatch():
2834 LOGGER.debug("skipping {0} in ALL_VK_DEVICE_FUNCS".format(vk_func.name))
2838 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2841 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2844 f.write("#define ALL_VK_INSTANCE_FUNCS() \\\n")
2846 for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs:
2847 if not vk_func.is_required():
2850 if not vk_func.needs_dispatch():
2851 LOGGER.debug("skipping {0} in ALL_VK_INSTANCE_FUNCS".format(vk_func.name))
2855 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2858 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2861 f.write("#endif /* __WINE_VULKAN_THUNKS_H */\n")
2863 def generate_loader_thunks_c(self, f):
2864 self._generate_copyright(f)
2866 f.write("#include \"vulkan_loader.h\"\n\n")
2868 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2870 for vk_func in self.registry.funcs.values():
2871 if not vk_func.is_required():
2873 if vk_func.loader_thunk_type != ThunkType.PUBLIC:
2876 f.write(vk_func.loader_thunk())
2878 f.write("static const struct vulkan_func vk_device_dispatch_table[] =\n{\n")
2879 for vk_func in self.registry.device_funcs:
2880 if not vk_func.is_required():
2883 f.write(" {{\"{0}\", &{0}}},\n".format(vk_func.name))
2886 f.write("static const struct vulkan_func vk_phys_dev_dispatch_table[] =\n{\n")
2887 for vk_func in self.registry.phys_dev_funcs:
2888 if not vk_func.is_required():
2891 f.write(" {{\"{0}\", &{0}}},\n".format(vk_func.name))
2894 f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
2895 for vk_func in self.registry.instance_funcs:
2896 if not vk_func.is_required():
2899 f.write(" {{\"{0}\", &{0}}},\n".format(vk_func.name))
2902 f.write("void *wine_vk_get_device_proc_addr(const char *name)\n")
2904 f.write(" unsigned int i;\n")
2905 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_dispatch_table); i++)\n")
2907 f.write(" if (strcmp(vk_device_dispatch_table[i].name, name) == 0)\n")
2909 f.write(" TRACE(\"Found name=%s in device table\\n\", debugstr_a(name));\n")
2910 f.write(" return vk_device_dispatch_table[i].func;\n")
2913 f.write(" return NULL;\n")
2916 f.write("void *wine_vk_get_phys_dev_proc_addr(const char *name)\n")
2918 f.write(" unsigned int i;\n")
2919 f.write(" for (i = 0; i < ARRAY_SIZE(vk_phys_dev_dispatch_table); i++)\n")
2921 f.write(" if (strcmp(vk_phys_dev_dispatch_table[i].name, name) == 0)\n")
2923 f.write(" TRACE(\"Found name=%s in physical device table\\n\", debugstr_a(name));\n")
2924 f.write(" return vk_phys_dev_dispatch_table[i].func;\n")
2927 f.write(" return NULL;\n")
2930 f.write("void *wine_vk_get_instance_proc_addr(const char *name)\n")
2932 f.write(" unsigned int i;\n")
2933 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_dispatch_table); i++)\n")
2935 f.write(" if (strcmp(vk_instance_dispatch_table[i].name, name) == 0)\n")
2937 f.write(" TRACE(\"Found name=%s in instance table\\n\", debugstr_a(name));\n")
2938 f.write(" return vk_instance_dispatch_table[i].func;\n")
2941 f.write(" return NULL;\n")
2944 def generate_loader_thunks_h(self, f):
2945 self._generate_copyright(f)
2947 f.write("#ifndef __WINE_VULKAN_LOADER_THUNKS_H\n")
2948 f.write("#define __WINE_VULKAN_LOADER_THUNKS_H\n\n")
2950 f.write("struct unix_funcs\n")
2952 for vk_func in self.registry.funcs.values():
2953 if not vk_func.is_required():
2955 if vk_func.loader_thunk_type == ThunkType.NONE:
2958 f.write(" {0};\n".format(vk_func.pfn(conv=False, call_conv="WINAPI")))
2961 f.write("#endif /* __WINE_VULKAN_LOADER_THUNKS_H */\n")
2963 def generate_vulkan_h(self, f):
2964 self._generate_copyright(f)
2965 f.write("#ifndef __WINE_VULKAN_H\n")
2966 f.write("#define __WINE_VULKAN_H\n\n")
2968 f.write("#include <windef.h>\n")
2969 f.write("#include <stdint.h>\n\n")
2971 f.write("/* Define WINE_VK_HOST to get 'host' headers. */\n")
2972 f.write("#ifdef WINE_VK_HOST\n")
2973 f.write("#define VKAPI_CALL\n")
2974 f.write('#define WINE_VK_ALIGN(x)\n')
2975 f.write("#endif\n\n")
2977 f.write("#ifndef VKAPI_CALL\n")
2978 f.write("#define VKAPI_CALL __stdcall\n")
2979 f.write("#endif\n\n")
2981 f.write("#ifndef VKAPI_PTR\n")
2982 f.write("#define VKAPI_PTR VKAPI_CALL\n")
2983 f.write("#endif\n\n")
2985 f.write("#ifndef WINE_VK_ALIGN\n")
2986 f.write("#define WINE_VK_ALIGN DECLSPEC_ALIGN\n")
2987 f.write("#endif\n\n")
2989 # The overall strategy is to define independent constants and datatypes,
2990 # prior to complex structures and function calls to avoid forward declarations.
2991 for const in self.registry.consts:
2992 # For now just generate things we may not need. The amount of parsing needed
2993 # to get some of the info is tricky as you need to figure out which structure
2994 # references a certain constant.
2995 f.write(const.definition())
2998 for define in self.registry.defines:
2999 f.write(define.definition())
3001 for handle in self.registry.handles:
3002 # For backward compatibility also create definitions for aliases.
3003 # These types normally don't get pulled in as we use the new types
3004 # even in legacy functions if they are aliases.
3005 if handle.is_required() or handle.is_alias():
3006 f.write(handle.definition())
3009 for base_type in self.registry.base_types:
3010 f.write(base_type.definition())
3013 for bitmask in self.registry.bitmasks:
3014 f.write(bitmask.definition())
3017 # Define enums, this includes values for some of the bitmask types as well.
3018 for enum in self.registry.enums.values():
3020 f.write(enum.definition())
3022 for fp in self.registry.funcpointers:
3024 f.write(fp.definition())
3027 # This generates both structures and unions. Since structures
3028 # may depend on other structures/unions, we need a list of
3029 # decoupled structs.
3030 # Note: unions are stored in structs for dependency reasons,
3031 # see comment in parsing section.
3032 structs = VkStruct.decouple_structs(self.registry.structs)
3033 for struct in structs:
3034 LOGGER.debug("Generating struct: {0}".format(struct.name))
3035 f.write(struct.definition(align=True))
3038 for func in self.registry.funcs.values():
3039 if not func.is_required():
3040 LOGGER.debug("Skipping PFN definition for: {0}".format(func.name))
3043 f.write("typedef {0};\n".format(func.pfn(prefix="PFN", call_conv="VKAPI_PTR")))
3046 f.write("#ifndef VK_NO_PROTOTYPES\n")
3047 for func in self.registry.funcs.values():
3048 if not func.is_required():
3049 LOGGER.debug("Skipping API definition for: {0}".format(func.name))
3052 LOGGER.debug("Generating API definition for: {0}".format(func.name))
3053 f.write("{0};\n".format(func.prototype(call_conv="VKAPI_CALL")))
3054 f.write("#endif /* VK_NO_PROTOTYPES */\n\n")
3056 f.write("#endif /* __WINE_VULKAN_H */\n")
3058 def generate_vulkan_driver_h(self, f):
3059 self._generate_copyright(f)
3060 f.write("#ifndef __WINE_VULKAN_DRIVER_H\n")
3061 f.write("#define __WINE_VULKAN_DRIVER_H\n\n")
3063 f.write("/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */\n")
3064 f.write("#define WINE_VULKAN_DRIVER_VERSION {0}\n\n".format(DRIVER_VERSION))
3066 f.write("struct vulkan_funcs\n{\n")
3067 f.write(" /* Vulkan global functions. These are the only calls at this point a graphics driver\n")
3068 f.write(" * needs to provide. Other function calls will be provided indirectly by dispatch\n")
3069 f.write(" * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice.\n")
3072 for vk_func in self.registry.funcs.values():
3073 if not vk_func.is_driver_func():
3077 # Avoid PFN_vkVoidFunction in driver interface as Vulkan likes to put calling convention
3078 # stuff in there. For simplicity substitute with "void *".
3079 pfn = pfn.replace("PFN_vkVoidFunction", "void *")
3080 f.write(" {0};\n".format(pfn))
3083 f.write(" /* winevulkan specific functions */\n")
3084 f.write(" VkSurfaceKHR (*p_wine_get_native_surface)(VkSurfaceKHR);\n")
3087 f.write("extern const struct vulkan_funcs * CDECL __wine_get_vulkan_driver(HDC hdc, UINT version);\n\n")
3089 f.write("static inline void *get_vulkan_driver_device_proc_addr(\n")
3090 f.write(" const struct vulkan_funcs *vulkan_funcs, const char *name)\n{\n")
3091 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
3092 f.write(" name += 2;\n\n")
3093 for vk_func in self.registry.funcs.values():
3094 if vk_func.is_driver_func() and vk_func.is_device_func():
3095 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3096 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3098 f.write(" return NULL;\n}\n\n")
3100 f.write("static inline void *get_vulkan_driver_instance_proc_addr(\n")
3101 f.write(" const struct vulkan_funcs *vulkan_funcs, VkInstance instance, const char *name)\n{\n")
3102 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
3103 f.write(" name += 2;\n\n")
3104 for vk_func in self.registry.funcs.values():
3105 if vk_func.is_driver_func() and vk_func.is_global_func() and vk_func.name != "vkGetInstanceProcAddr":
3106 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3107 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3109 f.write(" if (!instance) return NULL;\n\n")
3110 for vk_func in self.registry.funcs.values():
3111 if vk_func.is_driver_func() and (vk_func.is_instance_func() or vk_func.is_phys_dev_func()):
3112 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3113 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3115 f.write(" name -= 2;\n\n")
3116 f.write(" return get_vulkan_driver_device_proc_addr(vulkan_funcs, name);\n}\n\n")
3118 f.write("#endif /* __WINE_VULKAN_DRIVER_H */\n")
3120 def generate_vulkan_spec(self, f):
3121 self._generate_copyright(f, spec_file=True)
3122 f.write("@ stdcall -private vk_icdGetInstanceProcAddr(ptr str)\n")
3123 f.write("@ stdcall -private vk_icdGetPhysicalDeviceProcAddr(ptr str)\n")
3124 f.write("@ stdcall -private vk_icdNegotiateLoaderICDInterfaceVersion(ptr)\n")
3126 # Export symbols for all Vulkan Core functions.
3127 for func in self.registry.funcs.values():
3128 if not func.is_core_func():
3131 # We support all Core functions except for VK_KHR_display* APIs.
3132 # Create stubs for unsupported Core functions.
3133 if func.is_required():
3134 f.write(func.spec())
3136 f.write("@ stub {0}\n".format(func.name))
3138 f.write("@ stdcall -private DllRegisterServer()\n")
3139 f.write("@ stdcall -private DllUnregisterServer()\n")
3141 def generate_vulkan_loader_spec(self, f):
3142 self._generate_copyright(f, spec_file=True)
3144 # Export symbols for all Vulkan Core functions.
3145 for func in self.registry.funcs.values():
3146 if not func.is_core_func():
3149 # We support all Core functions except for VK_KHR_display* APIs.
3150 # Create stubs for unsupported Core functions.
3151 if func.is_required():
3152 f.write(func.spec(symbol="winevulkan." + func.name))
3154 f.write("@ stub {0}\n".format(func.name))
3157 class VkRegistry(object):
3158 def __init__(self, reg_filename):
3159 # Used for storage of type information.
3160 self.base_types = None
3161 self.bitmasks = None
3165 self.funcpointers = None
3169 # We aggregate all types in here for cross-referencing.
3173 self.version_regex = re.compile(
3182 # Overall strategy for parsing the registry is to first
3183 # parse all type / function definitions. Then parse
3184 # features and extensions to decide which types / functions
3185 # to actually 'pull in' for code generation. For each type or
3186 # function call we want we set a member 'required' to True.
3187 tree = ET.parse(reg_filename)
3188 root = tree.getroot()
3189 self._parse_enums(root)
3190 self._parse_types(root)
3191 self._parse_commands(root)
3193 # Pull in any required types and functions.
3194 self._parse_features(root)
3195 self._parse_extensions(root)
3197 self._match_object_types()
3199 self.copyright = root.find('./comment').text
3201 def _is_feature_supported(self, feature):
3202 version = self.version_regex.match(feature)
3206 version = tuple(map(int, version.group('major', 'minor')))
3207 return version <= WINE_VK_VERSION
3209 def _is_extension_supported(self, extension):
3210 # We disable some extensions as either we haven't implemented
3211 # support yet or because they are for platforms other than win32.
3212 return extension not in UNSUPPORTED_EXTENSIONS
3214 def _mark_command_required(self, command):
3215 """ Helper function to mark a certain command and the datatypes it needs as required."""
3216 def mark_bitmask_dependencies(bitmask, types):
3217 if bitmask.requires is not None:
3218 types[bitmask.requires]["data"].required = True
3220 def mark_funcpointer_dependencies(fp, types):
3221 for m in fp.members:
3222 type_info = types[m.type]
3224 # Complex types have a matching definition e.g. VkStruct.
3225 # Not needed for base types such as uint32_t.
3226 if "data" in type_info:
3227 types[m.type]["data"].required = True
3229 def mark_struct_dependencies(struct, types):
3231 type_info = types[m.type]
3233 # Complex types have a matching definition e.g. VkStruct.
3234 # Not needed for base types such as uint32_t.
3235 if "data" in type_info:
3236 types[m.type]["data"].required = True
3238 if type_info["category"] == "struct":
3240 mark_struct_dependencies(type_info["data"], types)
3241 elif type_info["category"] == "funcpointer":
3242 mark_funcpointer_dependencies(type_info["data"], types)
3243 elif type_info["category"] == "bitmask":
3244 mark_bitmask_dependencies(type_info["data"], types)
3246 func = self.funcs[command]
3247 func.required = True
3249 # Pull in return type
3250 if func.type != "void":
3251 self.types[func.type]["data"].required = True
3253 # Analyze parameter dependencies and pull in any type needed.
3254 for p in func.params:
3255 type_info = self.types[p.type]
3257 # Check if we are dealing with a complex type e.g. VkEnum, VkStruct and others.
3258 if "data" not in type_info:
3261 # Mark the complex type as required.
3262 type_info["data"].required = True
3263 if type_info["category"] == "struct":
3264 struct = type_info["data"]
3265 mark_struct_dependencies(struct, self.types)
3266 elif type_info["category"] == "bitmask":
3267 mark_bitmask_dependencies(type_info["data"], self.types)
3269 def _match_object_types(self):
3270 """ Matches each handle with the correct object type. """
3271 # Use upper case comparison for simplicity.
3273 for value in self.enums["VkObjectType"].values:
3274 object_name = "VK" + value.name[len("VK_OBJECT_TYPE"):].replace("_", "")
3275 object_types[object_name] = value.name
3277 for handle in self.handles:
3278 if not handle.is_required():
3280 handle.object_type = object_types.get(handle.name.upper())
3281 if not handle.object_type:
3282 LOGGER.warning("No object type found for {}".format(handle.name))
3284 def _parse_commands(self, root):
3285 """ Parse command section containing the Vulkan function calls. """
3287 commands = root.findall("./commands/")
3289 # As of Vulkan 1.1, various extensions got promoted to Core.
3290 # The old commands (e.g. KHR) are available for backwards compatibility
3291 # and are marked in vk.xml as 'alias' to the non-extension type.
3292 # The registry likes to avoid data duplication, so parameters and other
3293 # metadata need to be looked up from the Core command.
3294 # We parse the alias commands in a second pass.
3296 for command in commands:
3297 alias_name = command.attrib.get("alias")
3299 alias_commands.append(command)
3302 func = VkFunction.from_xml(command, self.types)
3303 funcs[func.name] = func
3305 for command in alias_commands:
3306 alias_name = command.attrib.get("alias")
3307 alias = funcs[alias_name]
3308 func = VkFunction.from_alias(command, alias)
3309 funcs[func.name] = func
3311 # To make life easy for the code generation, separate all function
3312 # calls out in the 4 types of Vulkan functions:
3313 # device, global, physical device and instance.
3318 for func in funcs.values():
3319 if func.is_device_func():
3320 device_funcs.append(func)
3321 elif func.is_global_func():
3322 global_funcs.append(func)
3323 elif func.is_phys_dev_func():
3324 phys_dev_funcs.append(func)
3326 instance_funcs.append(func)
3328 # Sort function lists by name and store them.
3329 self.device_funcs = sorted(device_funcs, key=lambda func: func.name)
3330 self.global_funcs = sorted(global_funcs, key=lambda func: func.name)
3331 self.phys_dev_funcs = sorted(phys_dev_funcs, key=lambda func: func.name)
3332 self.instance_funcs = sorted(instance_funcs, key=lambda func: func.name)
3334 # The funcs dictionary is used as a convenient way to lookup function
3335 # calls when needed e.g. to adjust member variables.
3336 self.funcs = OrderedDict(sorted(funcs.items()))
3338 def _parse_enums(self, root):
3339 """ Parse enums section or better described as constants section. """
3342 for enum in root.findall("./enums"):
3343 name = enum.attrib.get("name")
3344 _type = enum.attrib.get("type")
3346 if _type in ("enum", "bitmask"):
3347 enums[name] = VkEnum.from_xml(enum)
3349 # If no type is set, we are dealing with API constants.
3350 for value in enum.findall("enum"):
3351 # If enum is an alias, set the value to the alias name.
3352 # E.g. VK_LUID_SIZE_KHR is an alias to VK_LUID_SIZE.
3353 alias = value.attrib.get("alias")
3355 self.consts.append(VkConstant(value.attrib.get("name"), alias))
3357 self.consts.append(VkConstant(value.attrib.get("name"), value.attrib.get("value")))
3359 self.enums = OrderedDict(sorted(enums.items()))
3361 def _process_require_enum(self, enum_elem, ext=None, only_aliased=False):
3362 if "extends" in enum_elem.keys():
3363 enum = self.types[enum_elem.attrib["extends"]]["data"]
3365 # Need to define VkEnumValues which were aliased to by another value. This is necessary
3366 # from VK spec version 1.2.135 where the provisional VK_KHR_ray_tracing extension was
3367 # added which altered VK_NV_ray_tracing's VkEnumValues to alias to the provisional
3370 for _, t in self.types.items():
3371 if t["category"] != "enum":
3375 for value in t["data"].values:
3376 if value.alias == enum_elem.attrib["name"]:
3379 if only_aliased and not aliased:
3382 if "bitpos" in enum_elem.keys():
3383 # We need to add an extra value to an existing enum type.
3384 # E.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG to VkFormatFeatureFlagBits.
3385 enum.create_bitpos(enum_elem.attrib["name"], int(enum_elem.attrib["bitpos"]))
3387 elif "offset" in enum_elem.keys():
3388 # Extensions promoted to Core, have the extension number as part
3389 # of the enum value. Else retrieve from the extension tag.
3390 if enum_elem.attrib.get("extnumber"):
3391 ext_number = int(enum_elem.attrib.get("extnumber"))
3393 ext_number = int(ext.attrib["number"])
3394 offset = int(enum_elem.attrib["offset"])
3395 value = EXT_BASE + (ext_number - 1) * EXT_BLOCK_SIZE + offset
3397 # Deal with negative values.
3398 direction = enum_elem.attrib.get("dir")
3399 if direction is not None:
3402 enum.create_value(enum_elem.attrib["name"], str(value))
3404 elif "value" in enum_elem.keys():
3405 enum.create_value(enum_elem.attrib["name"], enum_elem.attrib["value"])
3406 elif "alias" in enum_elem.keys():
3407 enum.create_alias(enum_elem.attrib["name"], enum_elem.attrib["alias"])
3409 elif "value" in enum_elem.keys():
3410 # Constants are not aliased, no need to add them here, they'll get added later on.
3414 self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["value"]))
3417 def _require_type(type_info):
3418 if type_info.is_alias():
3419 type_info = type_info.alias
3420 type_info.required = True
3421 if type(type_info) == VkStruct:
3422 for member in type_info.members:
3423 if "data" in member.type_info:
3424 VkRegistry._require_type(member.type_info["data"])
3426 def _parse_extensions(self, root):
3427 """ Parse extensions section and pull in any types and commands for this extension. """
3429 exts = root.findall("./extensions/extension")
3431 skipped_exts = UNSUPPORTED_EXTENSIONS.copy()
3433 def process_ext(ext, deferred=False):
3434 ext_name = ext.attrib["name"]
3436 # Set extension name on any functions calls part of this extension as we
3437 # were not aware of the name during initial parsing.
3438 commands = ext.findall("require/command")
3439 for command in commands:
3440 cmd_name = command.attrib["name"]
3441 # Need to verify that the command is defined, and otherwise skip it.
3442 # vkCreateScreenSurfaceQNX is declared in <extensions> but not defined in
3443 # <commands>. A command without a definition cannot be enabled, so it's valid for
3444 # the XML file to handle this, but because of the manner in which we parse the XML
3445 # file we pre-populate from <commands> before we check if a command is enabled.
3446 if cmd_name in self.funcs:
3447 self.funcs[cmd_name].extensions.append(ext_name)
3449 # Some extensions are not ready or have numbers reserved as a place holder.
3450 if ext.attrib["supported"] == "disabled":
3451 LOGGER.debug("Skipping disabled extension: {0}".format(ext_name))
3452 skipped_exts.append(ext_name)
3455 # Defer extensions with 'sortorder' as they are order-dependent for spec-parsing.
3456 if not deferred and "sortorder" in ext.attrib:
3457 deferred_exts.append(ext)
3460 # Disable highly experimental extensions as the APIs are unstable and can
3461 # change between minor Vulkan revisions until API is final and becomes KHR
3463 if ("KHX" in ext_name or "NVX" in ext_name) and ext_name not in ALLOWED_X_EXTENSIONS:
3464 LOGGER.debug("Skipping experimental extension: {0}".format(ext_name))
3465 skipped_exts.append(ext_name)
3468 # Extensions can define VkEnumValues which alias to provisional extensions. Pre-process
3469 # extensions to define any required VkEnumValues before the platform check below.
3470 for require in ext.findall("require"):
3471 # Extensions can add enum values to Core / extension enums, so add these.
3472 for enum_elem in require.findall("enum"):
3473 self._process_require_enum(enum_elem, ext, only_aliased=True)
3475 platform = ext.attrib.get("platform")
3476 if platform and platform != "win32":
3477 LOGGER.debug("Skipping extensions {0} for platform {1}".format(ext_name, platform))
3478 skipped_exts.append(ext_name)
3481 if not self._is_extension_supported(ext_name):
3482 LOGGER.debug("Skipping unsupported extension: {0}".format(ext_name))
3483 skipped_exts.append(ext_name)
3485 elif "requires" in ext.attrib:
3486 # Check if this extension builds on top of another unsupported extension.
3487 requires = ext.attrib["requires"].split(",")
3488 if len(set(requires).intersection(skipped_exts)) > 0:
3489 skipped_exts.append(ext_name)
3492 LOGGER.debug("Loading extension: {0}".format(ext_name))
3494 # Extensions can define one or more require sections each requiring
3495 # different features (e.g. Vulkan 1.1). Parse each require section
3496 # separately, so we can skip sections we don't want.
3497 for require in ext.findall("require"):
3498 # Extensions can add enum values to Core / extension enums, so add these.
3499 for enum_elem in require.findall("enum"):
3500 self._process_require_enum(enum_elem, ext)
3502 for t in require.findall("type"):
3503 type_info = self.types[t.attrib["name"]]["data"]
3504 self._require_type(type_info)
3505 feature = require.attrib.get("feature")
3506 if feature and not self._is_feature_supported(feature):
3509 required_extension = require.attrib.get("extension")
3510 if required_extension and not self._is_extension_supported(required_extension):
3513 # Pull in any commands we need. We infer types to pull in from the command
3515 for command in require.findall("command"):
3516 cmd_name = command.attrib["name"]
3517 self._mark_command_required(cmd_name)
3520 # Store a list with extensions.
3521 ext_info = {"name" : ext_name, "type" : ext.attrib["type"]}
3522 extensions.append(ext_info)
3525 # Process extensions, allowing for sortorder to defer extension processing
3529 deferred_exts.sort(key=lambda ext: ext.attrib["sortorder"])
3532 for ext in deferred_exts:
3533 process_ext(ext, deferred=True)
3535 # Sort in alphabetical order.
3536 self.extensions = sorted(extensions, key=lambda ext: ext["name"])
3538 def _parse_features(self, root):
3539 """ Parse the feature section, which describes Core commands and types needed. """
3541 for feature in root.findall("./feature"):
3542 feature_name = feature.attrib["name"]
3543 for require in feature.findall("require"):
3544 LOGGER.info("Including features for {0}".format(require.attrib.get("comment")))
3546 if tag.tag == "comment":
3548 elif tag.tag == "command":
3549 if not self._is_feature_supported(feature_name):
3551 name = tag.attrib["name"]
3552 self._mark_command_required(name)
3553 elif tag.tag == "enum":
3554 self._process_require_enum(tag)
3555 elif tag.tag == "type":
3556 name = tag.attrib["name"]
3558 # Skip pull in for vk_platform.h for now.
3559 if name == "vk_platform":
3562 type_info = self.types[name]
3563 type_info["data"].required = True
3565 def _parse_types(self, root):
3566 """ Parse types section, which contains all data types e.g. structs, typedefs etcetera. """
3567 types = root.findall("./types/type")
3579 type_info["category"] = t.attrib.get("category", None)
3580 type_info["requires"] = t.attrib.get("requires", None)
3582 # We parse aliases in a second pass when we know more.
3583 alias = t.attrib.get("alias")
3585 LOGGER.debug("Alias found: {0}".format(alias))
3586 alias_types.append(t)
3589 if type_info["category"] in ["include"]:
3592 if type_info["category"] == "basetype":
3593 name = t.find("name").text
3595 if not t.find("type") is None:
3596 _type = t.find("type").text
3597 basetype = VkBaseType(name, _type)
3598 base_types.append(basetype)
3599 type_info["data"] = basetype
3601 # Basic C types don't need us to define them, but we do need data for them
3602 if type_info["requires"] == "vk_platform":
3603 requires = type_info["requires"]
3604 basic_c = VkBaseType(name, _type, requires=requires)
3605 type_info["data"] = basic_c
3607 if type_info["category"] == "bitmask":
3608 name = t.find("name").text
3609 _type = t.find("type").text
3611 # Most bitmasks have a requires attribute used to pull in
3612 # required '*FlagBits" enum.
3613 requires = type_info["requires"]
3614 bitmask = VkBaseType(name, _type, requires=requires)
3615 bitmasks.append(bitmask)
3616 type_info["data"] = bitmask
3618 if type_info["category"] == "define":
3619 define = VkDefine.from_xml(t)
3620 defines.append(define)
3621 type_info["data"] = define
3623 if type_info["category"] == "enum":
3624 name = t.attrib.get("name")
3625 # The type section only contains enum names, not the actual definition.
3626 # Since we already parsed the enum before, just link it in.
3628 type_info["data"] = self.enums[name]
3629 except KeyError as e:
3630 # Not all enums seem to be defined yet, typically that's for
3631 # ones ending in 'FlagBits' where future extensions may add
3633 type_info["data"] = None
3635 if type_info["category"] == "funcpointer":
3636 funcpointer = VkFunctionPointer.from_xml(t)
3637 funcpointers.append(funcpointer)
3638 type_info["data"] = funcpointer
3640 if type_info["category"] == "handle":
3641 handle = VkHandle.from_xml(t)
3642 handles.append(handle)
3643 type_info["data"] = handle
3645 if type_info["category"] in ["struct", "union"]:
3646 # We store unions among structs as some structs depend
3647 # on unions. The types are very similar in parsing and
3648 # generation anyway. The official Vulkan scripts use
3649 # a similar kind of hack.
3650 struct = VkStruct.from_xml(t)
3651 structs.append(struct)
3652 type_info["data"] = struct
3654 # Name is in general within a name tag else it is an optional
3655 # attribute on the type tag.
3656 name_elem = t.find("name")
3657 if name_elem is not None:
3658 type_info["name"] = name_elem.text
3660 type_info["name"] = t.attrib.get("name", None)
3662 # Store all type data in a shared dictionary, so we can easily
3663 # look up information for a given type. There are no duplicate
3665 self.types[type_info["name"]] = type_info
3667 # Second pass for alias types, so we can retrieve all data from
3668 # the aliased object.
3669 for t in alias_types:
3671 type_info["category"] = t.attrib.get("category")
3672 type_info["name"] = t.attrib.get("name")
3674 alias = t.attrib.get("alias")
3676 if type_info["category"] == "bitmask":
3677 bitmask = VkBaseType(type_info["name"], alias, alias=self.types[alias]["data"])
3678 bitmasks.append(bitmask)
3679 type_info["data"] = bitmask
3681 if type_info["category"] == "enum":
3682 enum = VkEnum.from_alias(t, self.types[alias]["data"])
3683 type_info["data"] = enum
3684 self.enums[enum.name] = enum
3686 if type_info["category"] == "handle":
3687 handle = VkHandle.from_alias(t, self.types[alias]["data"])
3688 handles.append(handle)
3689 type_info["data"] = handle
3691 if type_info["category"] == "struct":
3692 struct = VkStruct.from_alias(t, self.types[alias]["data"])
3693 structs.append(struct)
3694 type_info["data"] = struct
3696 self.types[type_info["name"]] = type_info
3698 # We need detailed type information during code generation
3699 # on structs for alignment reasons. Unfortunately structs
3700 # are parsed among other types, so there is no guarantee
3701 # that any types needed have been parsed already, so set
3703 for struct in structs:
3704 struct.set_type_info(self.types)
3706 # Alias structures have enum values equivalent to those of the
3707 # structure which they are aliased against. we need to ignore alias
3708 # structs when populating the struct extensions list, otherwise we
3709 # will create duplicate case entries.
3713 for structextend in struct.structextends:
3714 s = self.types[structextend]["data"]
3715 s.struct_extensions.append(struct)
3717 # Guarantee everything is sorted, so code generation doesn't have
3718 # to deal with this.
3719 self.base_types = sorted(base_types, key=lambda base_type: base_type.name)
3720 self.bitmasks = sorted(bitmasks, key=lambda bitmask: bitmask.name)
3721 self.defines = defines
3722 self.enums = OrderedDict(sorted(self.enums.items()))
3723 self.funcpointers = sorted(funcpointers, key=lambda fp: fp.name)
3724 self.handles = sorted(handles, key=lambda handle: handle.name)
3725 self.structs = sorted(structs, key=lambda struct: struct.name)
3727 def generate_vulkan_json(f):
3729 f.write(" \"file_format_version\": \"1.0.0\",\n")
3730 f.write(" \"ICD\": {\n")
3731 f.write(" \"library_path\": \".\\\\winevulkan.dll\",\n")
3732 f.write(" \"api_version\": \"{0}\"\n".format(VK_XML_VERSION))
3736 def set_working_directory():
3737 path = os.path.abspath(__file__)
3738 path = os.path.dirname(path)
3741 def download_vk_xml(filename):
3742 url = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/v{0}/xml/vk.xml".format(VK_XML_VERSION)
3743 if not os.path.isfile(filename):
3744 urllib.request.urlretrieve(url, filename)
3747 parser = argparse.ArgumentParser()
3748 parser.add_argument("-v", "--verbose", action="count", default=0, help="increase output verbosity")
3749 parser.add_argument("-x", "--xml", default=None, type=str, help="path to specification XML file")
3751 args = parser.parse_args()
3752 if args.verbose == 0:
3753 LOGGER.setLevel(logging.WARNING)
3754 elif args.verbose == 1:
3755 LOGGER.setLevel(logging.INFO)
3757 LOGGER.setLevel(logging.DEBUG)
3759 set_working_directory()
3764 vk_xml = "vk-{0}.xml".format(VK_XML_VERSION)
3765 download_vk_xml(vk_xml)
3767 registry = VkRegistry(vk_xml)
3768 generator = VkGenerator(registry)
3770 with open(WINE_VULKAN_H, "w") as f:
3771 generator.generate_vulkan_h(f)
3773 with open(WINE_VULKAN_DRIVER_H, "w") as f:
3774 generator.generate_vulkan_driver_h(f)
3776 with open(WINE_VULKAN_THUNKS_H, "w") as f:
3777 generator.generate_thunks_h(f, "wine_")
3779 with open(WINE_VULKAN_THUNKS_C, "w") as f:
3780 generator.generate_thunks_c(f, "wine_")
3782 with open(WINE_VULKAN_LOADER_THUNKS_H, "w") as f:
3783 generator.generate_loader_thunks_h(f)
3785 with open(WINE_VULKAN_LOADER_THUNKS_C, "w") as f:
3786 generator.generate_loader_thunks_c(f)
3788 with open(WINE_VULKAN_JSON, "w") as f:
3789 generate_vulkan_json(f)
3791 with open(WINE_VULKAN_SPEC, "w") as f:
3792 generator.generate_vulkan_spec(f)
3794 with open(WINE_VULKAN_LOADER_SPEC, "w") as f:
3795 generator.generate_vulkan_loader_spec(f)
3797 if __name__ == "__main__":