winevulkan: Remove DECLSPEC_HIDDEN usage.
[wine.git] / dlls / winevulkan / make_vulkan
blob9163f543c154a1474c620fb4ca1cc77dc3247f9a
1 #!/usr/bin/env python3
2 # Wine Vulkan generator
4 # Copyright 2017-2018 Roderick Colenbrander
5 # Copyright 2022 Jacek Caban for CodeWeavers
7 # This library is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Lesser General Public
9 #  License as published by the Free Software Foundation; either
10 # version 2.1 of the License, or (at your option) any later version.
12 # This library is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 # Lesser General Public License for more details.
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this library; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 import argparse
23 import logging
24 import os
25 import re
26 import sys
27 import urllib.request
28 import xml.etree.ElementTree as ET
29 from collections import OrderedDict
30 from collections.abc import Sequence
31 from enum import Enum
33 # This script generates code for a Wine Vulkan ICD driver from Vulkan's vk.xml.
34 # Generating the code is like 10x worse than OpenGL, which is mostly a calling
35 # convention passthrough.
37 # The script parses vk.xml and maps functions and types to helper objects. These
38 # helper objects simplify the xml parsing and map closely to the Vulkan types.
39 # The code generation utilizes the helper objects during code generation and
40 # most of the ugly work is carried out by these objects.
42 # Vulkan ICD challenges:
43 # - Vulkan ICD loader (vulkan-1.dll) relies on a section at the start of
44 #   'dispatchable handles' (e.g. VkDevice, VkInstance) for it to insert
45 #   its private data. It uses this area to stare its own dispatch tables
46 #   for loader internal use. This means any dispatchable objects need wrapping.
48 # - Vulkan structures have different alignment between win32 and 32-bit Linux.
49 #   This means structures with alignment differences need conversion logic.
50 #   Often structures are nested, so the parent structure may not need any
51 #   conversion, but some child may need some.
53 # vk.xml parsing challenges:
54 # - Contains type data for all platforms (generic Vulkan, Windows, Linux,..).
55 #   Parsing of extension information required to pull in types and functions
56 #   we really want to generate. Just tying all the data together is tricky.
58 # - Extensions can affect core types e.g. add new enum values, bitflags or
59 #   additional structure chaining through 'pNext' / 'sType'.
61 # - Arrays are used all over the place for parameters or for structure members.
62 #   Array length is often stored in a previous parameter or another structure
63 #   member and thus needs careful parsing.
65 LOGGER = logging.Logger("vulkan")
66 LOGGER.addHandler(logging.StreamHandler())
68 VK_XML_VERSION = "1.3.267"
69 WINE_VK_VERSION = (1, 3)
71 # Filenames to create.
72 WINE_VULKAN_H = "../../include/wine/vulkan.h"
73 WINE_VULKAN_DRIVER_H = "../../include/wine/vulkan_driver.h"
74 WINE_VULKAN_LOADER_SPEC = "../vulkan-1/vulkan-1.spec"
75 WINE_VULKAN_JSON = "winevulkan.json"
76 WINE_VULKAN_SPEC = "winevulkan.spec"
77 WINE_VULKAN_THUNKS_C = "vulkan_thunks.c"
78 WINE_VULKAN_THUNKS_H = "vulkan_thunks.h"
79 WINE_VULKAN_LOADER_THUNKS_C = "loader_thunks.c"
80 WINE_VULKAN_LOADER_THUNKS_H = "loader_thunks.h"
82 # Extension enum values start at a certain offset (EXT_BASE).
83 # Relative to the offset each extension has a block (EXT_BLOCK_SIZE)
84 # of values.
85 # Start for a given extension is:
86 # EXT_BASE + (extension_number-1) * EXT_BLOCK_SIZE
87 EXT_BASE = 1000000000
88 EXT_BLOCK_SIZE = 1000
90 UNSUPPORTED_EXTENSIONS = [
91     # Instance extensions
92     "VK_EXT_headless_surface", # Needs WSI work.
93     "VK_KHR_display", # Needs WSI work.
94     "VK_KHR_surface_protected_capabilities",
95     "VK_LUNARG_direct_driver_loading", # Implemented in the Vulkan loader
97     # Device extensions
98     "VK_AMD_display_native_hdr",
99     "VK_EXT_full_screen_exclusive",
100     "VK_GOOGLE_display_timing",
101     "VK_KHR_external_fence_win32",
102     "VK_KHR_external_semaphore_win32",
103     # Relates to external_semaphore and needs type conversions in bitflags.
104     "VK_KHR_shared_presentable_image", # Needs WSI work.
105     "VK_KHR_video_queue", # TODO Video extensions use separate headers + xml
106     "VK_KHR_win32_keyed_mutex",
107     "VK_NV_external_memory_rdma", # Needs shared resources work.
109     # Extensions for other platforms
110     "VK_EXT_external_memory_dma_buf",
111     "VK_EXT_image_drm_format_modifier",
112     "VK_EXT_metal_objects",
113     "VK_EXT_physical_device_drm",
114     "VK_GOOGLE_surfaceless_query",
115     "VK_KHR_external_fence_fd",
116     "VK_KHR_external_memory_fd",
117     "VK_KHR_external_semaphore_fd",
118     "VK_SEC_amigo_profiling", # Angle specific.
120     # Extensions which require callback handling
121     "VK_EXT_device_memory_report",
123     # Deprecated extensions
124     "VK_NV_external_memory_capabilities",
125     "VK_NV_external_memory_win32",
128 # Either internal extensions which aren't present on the win32 platform which
129 # winevulkan may nonetheless use, or extensions we want to generate headers for
130 # but not expose to applications (useful for test commits)
131 UNEXPOSED_EXTENSIONS = {
132     "VK_KHR_external_memory_win32",
135 # The Vulkan loader provides entry-points for core functionality and important
136 # extensions. Based on vulkan-1.def this amounts to WSI extensions on 1.0.51.
137 CORE_EXTENSIONS = [
138     "VK_KHR_display",
139     "VK_KHR_display_swapchain",
140     "VK_KHR_get_surface_capabilities2",
141     "VK_KHR_surface",
142     "VK_KHR_swapchain",
143     "VK_KHR_win32_surface",
146 # Some experimental extensions are used by shipping applications so their API is extremely unlikely
147 # to change in a backwards-incompatible way. Allow translation of those extensions with WineVulkan.
148 ALLOWED_X_EXTENSIONS = [
149     "VK_NVX_binary_import",
150     "VK_NVX_image_view_handle",
153 # Some frequently called functions skip traces and checks for performance reasons.
154 PERF_CRITICAL_FUNCTIONS = [
155     "vkUpdateDescriptorSets",
156     "vkUpdateDescriptorSetWithTemplate",
157     "vkGetDescriptorEXT",
160 # Functions part of our winevulkan graphics driver interface.
161 # DRIVER_VERSION should be bumped on any change to driver interface
162 # in FUNCTION_OVERRIDES
163 DRIVER_VERSION = 11
165 class ThunkType(Enum):
166     NONE = 1
167     PUBLIC = 2
168     PRIVATE = 3
170 # Table of functions for which we have a special implementation.
171 # These are regular device / instance functions for which we need
172 # to do more work compared to a regular thunk or because they are
173 # part of the driver interface.
174 # - dispatch set whether we need a function pointer in the device
175 #   / instance dispatch table.
176 # - driver sets whether the API is part of the driver interface.
177 # - thunk sets whether to create a thunk in vulkan_thunks.c.
178 #   - NONE means there's a fully custom implementation.
179 #   - PUBLIC means the implementation is fully auto generated.
180 #   - PRIVATE thunks can be used in custom implementations for
181 #     struct conversion.
182 # - loader_thunk sets whether to create a thunk for unix funcs.
183 FUNCTION_OVERRIDES = {
184     # Global functions
185     "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE, "extra_param" : "client_ptr"},
186     "vkEnumerateInstanceExtensionProperties" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
187     "vkEnumerateInstanceLayerProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
188     "vkEnumerateInstanceVersion": {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
189     "vkGetInstanceProcAddr": {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
191     # Instance functions
192     "vkCreateDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE, "extra_param" : "client_ptr"},
193     "vkDestroyInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
194     "vkEnumerateDeviceExtensionProperties" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
195     "vkEnumerateDeviceLayerProperties": {"dispatch": True, "driver": False, "thunk": ThunkType.NONE},
196     "vkEnumeratePhysicalDeviceGroups" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
197     "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
198     "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
199     "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
200     "vkGetPhysicalDeviceExternalSemaphoreProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
201     "vkGetPhysicalDeviceImageFormatProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
202     "vkGetPhysicalDeviceProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PUBLIC, "loader_thunk" : ThunkType.PRIVATE},
203     "vkGetPhysicalDeviceProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PUBLIC, "loader_thunk" : ThunkType.PRIVATE},
205     # Device functions
206     "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
207     "vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE, "extra_param" : "client_ptr"},
208     "vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
209     "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
210     "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
211     "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
212     "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
213     "vkGetDeviceQueue2" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
214     "vkAllocateMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
215     "vkFreeMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
216     "vkMapMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
217     "vkMapMemory2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
218     "vkUnmapMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
219     "vkUnmapMemory2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
220     "vkCreateBuffer" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
221     "vkCreateImage" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
223     # VK_KHR_surface
224     "vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE},
225     "vkGetPhysicalDeviceSurfaceSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
226     "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
227     "vkGetPhysicalDeviceSurfaceFormatsKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
228     "vkGetPhysicalDeviceSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
230     # VK_KHR_get_surface_capabilities2
231     "vkGetPhysicalDeviceSurfaceCapabilities2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
232     "vkGetPhysicalDeviceSurfaceFormats2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
234     # VK_KHR_win32_surface
235     "vkCreateWin32SurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE},
236     "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
238     # VK_KHR_swapchain
239     "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
240     "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
241     "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
242     "vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
244     # VK_KHR_external_fence_capabilities
245     "vkGetPhysicalDeviceExternalFencePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
247     # VK_KHR_external_memory_capabilities
248     "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
249     "vkGetPhysicalDeviceImageFormatProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
251     # VK_KHR_external_semaphore_capabilities
252     "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
254     # VK_KHR_device_group_creation
255     "vkEnumeratePhysicalDeviceGroupsKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
257     # VK_KHR_device_group
258     "vkGetDeviceGroupSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
259     "vkGetPhysicalDevicePresentRectanglesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
261     # VK_KHR_deferred_host_operations
262     "vkCreateDeferredOperationKHR" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
263     "vkDestroyDeferredOperationKHR" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
265     # VK_EXT_calibrated_timestamps
266     "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
267     "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
269     # VK_EXT_debug_utils
270     "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
271     "vkDestroyDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
273     # VK_EXT_debug_report
274     "vkCreateDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
275     "vkDestroyDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
278 STRUCT_CHAIN_CONVERSIONS = {
279     # Ignore to not confuse host loader.
280     "VkDeviceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO"],
281     "VkInstanceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO"],
284 # Some struct members are conditionally ignored and callers are free to leave them uninitialized.
285 # We can't deduce that from XML, so we allow expressing it here.
286 MEMBER_LENGTH_EXPRESSIONS = {
287     "VkWriteDescriptorSet": {
288         "pImageInfo":
289             "{struct}descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || " +
290             "{struct}descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || " +
291             "{struct}descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || " +
292             "{struct}descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || " +
293             "{struct}descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT || " +
294             "{struct}descriptorType == VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM || " +
295             "{struct}descriptorType == VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM ? {len} : 0",
296         "pBufferInfo":
297             "{struct}descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || " +
298             "{struct}descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || " +
299             "{struct}descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || " +
300             "{struct}descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC ? {len} : 0",
301     }
304 class Direction(Enum):
305     """ Parameter direction: input, output, input_output. """
306     INPUT = 1
307     OUTPUT = 2
309 def api_is_vulkan(obj):
310     return "vulkan" in obj.get("api", "vulkan").split(",")
312 class VkBaseType(object):
313     def __init__(self, name, _type, alias=None, requires=None):
314         """ Vulkan base type class.
316         VkBaseType is mostly used by Vulkan to define its own
317         base types like VkFlags through typedef out of e.g. uint32_t.
319         Args:
320             name (:obj:'str'): Name of the base type.
321             _type (:obj:'str'): Underlying type
322             alias (bool): type is an alias or not.
323             requires (:obj:'str', optional): Other types required.
324                 Often bitmask values pull in a *FlagBits type.
325         """
326         self.name = name
327         self.type = _type
328         self.alias = alias
329         self.requires = requires
330         self.required = False
332     def definition(self):
333         # Definition is similar for alias or non-alias as type
334         # is already set to alias.
335         if not self.type is None:
336             return "typedef {0} {1};\n".format(self.type, self.name)
337         else:
338             return "struct {0};\n".format(self.name)
340     def is_alias(self):
341         return bool(self.alias)
344 class VkConstant(object):
345     def __init__(self, name, value):
346         self.name = name
347         self.value = value
349     def definition(self):
350         text = "#define {0} {1}\n".format(self.name, self.value)
351         return text
354 class VkDefine(object):
355     def __init__(self, name, value):
356         self.name = name
357         self.value = value
359     @staticmethod
360     def from_xml(define):
361         if not api_is_vulkan(define):
362             return None
364         name_elem = define.find("name")
366         if name_elem is None:
367             # <type category="define" name="some_name">some_value</type>
368             name = define.attrib.get("name")
370             # We override behavior of VK_USE_64_BIT_PTR_DEFINES as the default non-dispatchable handle
371             # definition various between 64-bit (uses pointers) and 32-bit (uses uint64_t).
372             # This complicates TRACEs in the thunks, so just use uint64_t.
373             if name == "VK_USE_64_BIT_PTR_DEFINES":
374                 value = "#define VK_USE_64_BIT_PTR_DEFINES 0"
375             else:
376                 value = define.text
377             return VkDefine(name, value)
379         # With a name element the structure is like:
380         # <type category="define"><name>some_name</name>some_value</type>
381         name = name_elem.text
383         # Perform minimal parsing for Vulkan constants, which we don't need, but are referenced
384         # elsewhere in vk.xml.
385         # - VK_API_VERSION is a messy, deprecated constant and we don't want generate code for it.
386         # - AHardwareBuffer/ANativeWindow are forward declarations for Android types, which leaked
387         #   into the define region.
388         if name in ["VK_API_VERSION", "AHardwareBuffer", "ANativeWindow", "CAMetalLayer"]:
389             return VkDefine(name, None)
391         # The body of the define is basically unstructured C code. It is not meant for easy parsing.
392         # Some lines contain deprecated values or comments, which we try to filter out.
393         value = ""
394         for line in define.text.splitlines():
395             # Skip comments or deprecated values.
396             if "//" in line:
397                 continue
398             value += line
400         for child in define:
401             value += child.text
402             if child.tail is not None:
403                 # Split comments for VK_API_VERSION_1_0 / VK_API_VERSION_1_1
404                 if "//" in child.tail:
405                     value += child.tail.split("//")[0]
406                 else:
407                     value += child.tail
409         return VkDefine(name, value.rstrip(' '))
411     def definition(self):
412         if self.value is None:
413             return ""
415         # Nothing to do as the value was already put in the right form during parsing.
416         return "{0}\n".format(self.value)
419 class VkEnum(object):
420     def __init__(self, name, bitwidth, alias=None):
421         if not bitwidth in [32, 64]:
422             LOGGER.error("unknown bitwidth {0} for {1}".format(bitwidth, name))
423         self.name = name
424         self.bitwidth = bitwidth
425         self.values = [] if alias == None else alias.values
426         self.required = False
427         self.alias = alias
428         self.aliased_by = []
430     @staticmethod
431     def from_alias(enum, alias):
432         name = enum.attrib.get("name")
433         aliasee = VkEnum(name, alias.bitwidth, alias=alias)
435         alias.add_aliased_by(aliasee)
436         return aliasee
438     @staticmethod
439     def from_xml(enum):
440         if not api_is_vulkan(enum):
441             return None
443         name = enum.attrib.get("name")
444         bitwidth = int(enum.attrib.get("bitwidth", "32"))
445         result = VkEnum(name, bitwidth)
447         for v in enum.findall("enum"):
448             value_name = v.attrib.get("name")
449             # Value is either a value or a bitpos, only one can exist.
450             value = v.attrib.get("value")
451             alias_name = v.attrib.get("alias")
452             if alias_name:
453                 result.create_alias(value_name, alias_name)
454             elif value:
455                 result.create_value(value_name, value)
456             else:
457                 # bitmask
458                 result.create_bitpos(value_name, int(v.attrib.get("bitpos")))
460         if bitwidth == 32:
461             # vulkan.h contains a *_MAX_ENUM value set to 32-bit at the time of writing,
462             # which is to prepare for extensions as they can add values and hence affect
463             # the size definition.
464             max_name = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2', name).upper() + "_MAX_ENUM"
465             result.create_value(max_name, "0x7fffffff")
467         return result
469     def create_alias(self, name, alias_name):
470         """ Create an aliased value for this enum """
471         self.add(VkEnumValue(name, self.bitwidth, alias=alias_name))
473     def create_value(self, name, value):
474         """ Create a new value for this enum """
475         # Some values are in hex form. We want to preserve the hex representation
476         # at least when we convert back to a string. Internally we want to use int.
477         hex = "0x" in value
478         self.add(VkEnumValue(name, self.bitwidth, value=int(value, 0), hex=hex))
480     def create_bitpos(self, name, pos):
481         """ Create a new bitmask value for this enum """
482         self.add(VkEnumValue(name, self.bitwidth, value=(1 << pos), hex=True))
484     def add(self, value):
485         """ Add a value to enum. """
487         # Extensions can add new enum values. When an extension is promoted to Core
488         # the registry defines the value twice once for old extension and once for
489         # new Core features. Add the duplicate if it's explicitly marked as an
490         # alias, otherwise ignore it.
491         for v in self.values:
492             if not value.is_alias() and v.value == value.value:
493                 LOGGER.debug("Adding duplicate enum value {0} to {1}".format(v, self.name))
494                 return
495         # Avoid adding duplicate aliases multiple times
496         if not any(x.name == value.name for x in self.values):
497             self.values.append(value)
499     def fixup_64bit_aliases(self):
500         """ Replace 64bit aliases with literal values """
501         # Older GCC versions need a literal to initialize a static const uint64_t
502         # which is what we use for 64bit bitmasks.
503         if self.bitwidth != 64:
504             return
505         for value in self.values:
506             if not value.is_alias():
507                 continue
508             alias = next(x for x in self.values if x.name == value.alias)
509             value.hex = alias.hex
510             value.value = alias.value
512     def definition(self):
513         if self.is_alias():
514             return ""
516         default_value = 0x7ffffffe if self.bitwidth == 32 else 0xfffffffffffffffe
518         # Print values sorted, values can have been added in a random order.
519         values = sorted(self.values, key=lambda value: value.value if value.value is not None else default_value)
521         if self.bitwidth == 32:
522             text = "typedef enum {0}\n{{\n".format(self.name)
523             for value in values:
524                 text += "    {0},\n".format(value.definition())
525             text += "}} {0};\n".format(self.name)
526         elif self.bitwidth == 64:
527             text = "typedef VkFlags64 {0};\n\n".format(self.name)
528             for value in values:
529                 text += "static const {0} {1};\n".format(self.name, value.definition())
531         for aliasee in self.aliased_by:
532             text += "typedef {0} {1};\n".format(self.name, aliasee.name)
534         text += "\n"
535         return text
537     def is_alias(self):
538         return bool(self.alias)
540     def add_aliased_by(self, aliasee):
541         self.aliased_by.append(aliasee)
544 class VkEnumValue(object):
545     def __init__(self, name, bitwidth, value=None, hex=False, alias=None):
546         self.name = name
547         self.bitwidth = bitwidth
548         self.value = value
549         self.hex = hex
550         self.alias = alias
552     def __repr__(self):
553         postfix = "ull" if self.bitwidth == 64 else ""
554         if self.is_alias() and self.value == None:
555             return "{0}={1}".format(self.name, self.alias)
556         return "{0}={1}{2}".format(self.name, self.value, postfix)
558     def definition(self):
559         """ Convert to text definition e.g. VK_FOO = 1 """
560         postfix = "ull" if self.bitwidth == 64 else ""
561         if self.is_alias() and self.value == None:
562             return "{0} = {1}".format(self.name, self.alias)
564         # Hex is commonly used for FlagBits and sometimes within
565         # a non-FlagBits enum for a bitmask value as well.
566         if self.hex:
567             return "{0} = 0x{1:08x}{2}".format(self.name, self.value, postfix)
568         else:
569             return "{0} = {1}{2}".format(self.name, self.value, postfix)
571     def is_alias(self):
572         return self.alias is not None
575 class VkFunction(object):
576     def __init__(self, _type=None, name=None, params=[], alias=None):
577         self.extensions = set()
578         self.name = name
579         self.type = _type
580         self.params = params
581         self.alias = alias
583         # For some functions we need some extra metadata from FUNCTION_OVERRIDES.
584         func_info = FUNCTION_OVERRIDES.get(self.name, None)
585         self.dispatch = func_info["dispatch"] if func_info else True
586         self.driver = func_info["driver"] if func_info else False
587         self.thunk_type = func_info["thunk"] if func_info else ThunkType.PUBLIC
588         self.loader_thunk_type = func_info["loader_thunk"] if func_info and "loader_thunk" in func_info else ThunkType.PUBLIC
589         self.extra_param = func_info["extra_param"] if func_info and "extra_param" in func_info else None
591         # Required is set while parsing which APIs and types are required
592         # and is used by the code generation.
593         self.required = True if func_info else False
595     @staticmethod
596     def from_alias(command, alias):
597         """ Create VkFunction from an alias command.
599         Args:
600             command: xml data for command
601             alias (VkFunction): function to use as a base for types / parameters.
603         Returns:
604             VkFunction
605         """
606         if not api_is_vulkan(command):
607             return None
609         func_name = command.attrib.get("name")
610         func_type = alias.type
611         params = alias.params
613         return VkFunction(_type=func_type, name=func_name, params=params, alias=alias)
615     @staticmethod
616     def from_xml(command, types):
617         if not api_is_vulkan(command):
618             return None
620         proto = command.find("proto")
621         func_name = proto.find("name").text
622         func_type = proto.find("type").text
624         params = []
625         for param in command.findall("param"):
626             vk_param = VkParam.from_xml(param, types, params)
627             if vk_param:
628                 params.append(vk_param)
630         return VkFunction(_type=func_type, name=func_name, params=params)
632     def get_conversions(self):
633         """ Get a list of conversion functions required for this function if any.
634         Parameters which are structures may require conversion between win32
635         and the host platform. This function returns a list of conversions
636         required.
637         """
639         conversions = []
640         for param in self.params:
641             conversions.extend(param.get_conversions(self.thunk_type == ThunkType.PUBLIC))
642         return conversions
644     def is_alias(self):
645         return bool(self.alias)
647     def is_core_func(self):
648         """ Returns whether the function is a Vulkan core function.
649         Core functions are APIs defined by the Vulkan spec to be part of the
650         Core API as well as several KHR WSI extensions.
651         """
653         if not self.extensions:
654             return True
656         return any(ext in self.extensions for ext in CORE_EXTENSIONS)
658     def is_device_func(self):
659         # If none of the other, it must be a device function
660         return not self.is_global_func() and not self.is_instance_func() and not self.is_phys_dev_func()
662     def is_driver_func(self):
663         """ Returns if function is part of Wine driver interface. """
664         return self.driver
666     def is_global_func(self):
667         # Treat vkGetInstanceProcAddr as a global function as it
668         # can operate with NULL for vkInstance.
669         if self.name == "vkGetInstanceProcAddr":
670             return True
671         # Global functions are not passed a dispatchable object.
672         elif self.params[0].is_dispatchable():
673             return False
674         return True
676     def is_instance_func(self):
677         # Instance functions are passed VkInstance.
678         if self.params[0].type == "VkInstance":
679             return True
680         return False
682     def is_phys_dev_func(self):
683         # Physical device functions are passed VkPhysicalDevice.
684         if self.params[0].type == "VkPhysicalDevice":
685             return True
686         return False
688     def is_required(self):
689         return self.required
691     def returns_longlong(self):
692         return self.type in ["uint64_t", "VkDeviceAddress"]
694     def needs_dispatch(self):
695         return self.dispatch
697     def needs_private_thunk(self):
698         return self.needs_exposing() and self.loader_thunk_type != ThunkType.NONE and \
699             self.thunk_type != ThunkType.PUBLIC
701     def needs_exposing(self):
702         # The function needs exposed if at-least one extension isn't both UNSUPPORTED and UNEXPOSED
703         return self.is_required() and (not self.extensions or not self.extensions.issubset(UNEXPOSED_EXTENSIONS))
705     def is_perf_critical(self):
706         # vkCmd* functions are frequently called, do not trace for performance
707         if self.name.startswith("vkCmd") and self.type == "void":
708             return True
709         return self.name in PERF_CRITICAL_FUNCTIONS
711     def pfn(self, prefix="p", call_conv=None):
712         """ Create function pointer. """
714         if call_conv:
715             pfn = "{0} ({1} *{2}_{3})(".format(self.type, call_conv, prefix, self.name)
716         else:
717             pfn = "{0} (*{1}_{2})(".format(self.type, prefix, self.name)
719         for i, param in enumerate(self.params):
720             if param.const:
721                 pfn += param.const + " "
723             pfn += param.type
725             if param.is_pointer():
726                 pfn += " " + param.pointer
728             if param.array_len is not None:
729                 pfn += "[{0}]".format(param.array_len)
731             if i < len(self.params) - 1:
732                 pfn += ", "
733         pfn += ")"
734         return pfn
736     def prototype(self, call_conv=None, prefix=None, is_thunk=False):
737         """ Generate prototype for given function.
739         Args:
740             call_conv (str, optional): calling convention e.g. WINAPI
741             prefix (str, optional): prefix to append prior to function name e.g. vkFoo -> wine_vkFoo
742         """
744         proto = "{0}".format(self.type)
746         if call_conv is not None:
747             proto += " {0}".format(call_conv)
749         if prefix is not None:
750             proto += " {0}{1}(".format(prefix, self.name)
751         else:
752             proto += " {0}(".format(self.name)
754         # Add all the parameters.
755         proto += ", ".join([p.definition() for p in self.params])
757         if is_thunk and self.extra_param:
758             proto += ", void *" + self.extra_param
760         proto += ")"
761         return proto
763     def loader_body(self):
764         body = "    struct {0}_params params;\n".format(self.name)
765         if not self.is_perf_critical():
766             body += "    NTSTATUS status;\n"
767         for p in self.params:
768             body += "    params.{0} = {0};\n".format(p.name)
770         # Call the Unix function.
771         if self.is_perf_critical():
772             body += "    UNIX_CALL({0}, &params);\n".format(self.name)
773         else:
774             body += "    status = UNIX_CALL({0}, &params);\n".format(self.name)
775             body += "    assert(!status && \"{0}\");\n".format(self.name)
777         if self.type != "void":
778             body += "    return params.result;\n"
779         return body
781     def body(self, conv, unwrap, params_prefix=""):
782         body = ""
783         needs_alloc = False
784         deferred_op = None
786         # Declare any tmp parameters for conversion.
787         for p in self.params:
788             if p.needs_variable(conv, unwrap):
789                 if p.is_dynamic_array():
790                     body += "    {2}{0} *{1}_host;\n".format(
791                         p.type, p.name, "const " if p.is_const() else "")
792                 elif p.optional:
793                     body += "    {0} *{1}_host = NULL;\n".format(p.type, p.name)
794                     needs_alloc = True
795                 else:
796                     body += "    {0} {1}_host;\n".format(p.type, p.name)
797             if p.needs_alloc(conv, unwrap):
798                 needs_alloc = True
799             if p.type == "VkDeferredOperationKHR" and not p.is_pointer():
800                 deferred_op = p.name
802         if needs_alloc:
803             body += "    struct conversion_context local_ctx;\n"
804             body += "    struct conversion_context *ctx = &local_ctx;\n"
805         body += "\n"
807         if not self.is_perf_critical():
808             body += "    {0}\n".format(self.trace(params_prefix=params_prefix, conv=conv))
810         if self.params[0].optional and self.params[0].is_handle():
811             if self.type != "void":
812                 LOGGER.warning("return type {0} with optional handle not supported".format(self.type))
813             body += "    if (!{0}{1})\n".format(params_prefix, self.params[0].name)
814             body += "        return STATUS_SUCCESS;\n\n"
816         if needs_alloc:
817             if deferred_op is not None:
818                 body += "    if (params->{} == VK_NULL_HANDLE)\n".format(deferred_op)
819                 body += "    "
820             body += "    init_conversion_context(ctx);\n"
821             if deferred_op is not None:
822                 body += "    else\n"
823                 body += "        ctx = &wine_deferred_operation_from_handle(params->{})->ctx;\n".format(deferred_op)
825         # Call any win_to_host conversion calls.
826         unwrap = self.thunk_type == ThunkType.PUBLIC
827         for p in self.params:
828             if p.needs_conversion(conv, unwrap, Direction.INPUT):
829                 body += p.copy(Direction.INPUT, conv, unwrap, prefix=params_prefix)
830             elif p.is_dynamic_array() and p.needs_conversion(conv, unwrap, Direction.OUTPUT):
831                 body += "    {0}_host = ({2}{0} && {1}) ? conversion_context_alloc(ctx, sizeof(*{0}_host) * {1}) : NULL;\n".format(
832                     p.name, p.get_dyn_array_len(params_prefix, conv), params_prefix)
834         # Build list of parameters containing converted and non-converted parameters.
835         # The param itself knows if conversion is needed and applies it when we set conv=True.
836         params = ", ".join([p.variable(conv=conv, unwrap=unwrap, params_prefix=params_prefix) for p in self.params])
837         if self.extra_param:
838             if conv:
839                 params += ", UlongToPtr({0}{1})".format(params_prefix, self.extra_param)
840             else:
841                 params += ", {0}{1}".format(params_prefix, self.extra_param)
843         if unwrap or self.thunk_type == ThunkType.PUBLIC:
844             func_prefix = "{0}.p_".format(self.params[0].dispatch_table(params_prefix, conv))
845         else:
846             func_prefix = "wine_"
848         # Call the native Vulkan function.
849         if self.type == "void":
850             body += "    {0}{1}({2});\n".format(func_prefix, self.name, params)
851         else:
852             body += "    {0}result = {1}{2}({3});\n".format(params_prefix, func_prefix, self.name, params)
854         # Call any host_to_win conversion calls.
855         for p in self.params:
856             if p.needs_conversion(conv, unwrap, Direction.OUTPUT):
857                 body += p.copy(Direction.OUTPUT, conv, unwrap, prefix=params_prefix)
859         if needs_alloc:
860             if deferred_op is not None:
861                 body += "    if (params->{} == VK_NULL_HANDLE)\n".format(deferred_op)
862                 body += "    "
863             body += "    free_conversion_context(ctx);\n"
865         # Finally return the result. Performance critical functions return void to allow tail calls.
866         if not self.is_perf_critical():
867             body += "    return STATUS_SUCCESS;\n"
869         return body
871     def spec(self, prefix=None, symbol=None):
872         """ Generate spec file entry for this function.
874         Args
875             prefix (str, optional): prefix to prepend to entry point name.
876             symbol (str, optional): allows overriding the name of the function implementing the entry point.
877         """
879         spec = ""
880         params = " ".join([p.spec() for p in self.params])
881         if prefix is not None:
882             spec += "@ stdcall -private {0}{1}({2})".format(prefix, self.name, params)
883         else:
884             spec += "@ stdcall {0}({1})".format(self.name, params)
886         if symbol is not None:
887             spec += " " + symbol
889         spec += "\n"
890         return spec
892     def stub(self, call_conv=None, prefix=None):
893         stub = self.prototype(call_conv=call_conv, prefix=prefix)
894         stub += "\n{\n"
895         stub += "    {0}".format(self.trace(message="stub: ", trace_func="FIXME"))
897         if self.type == "VkResult":
898             stub += "    return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
899         elif self.type == "VkBool32":
900             stub += "    return VK_FALSE;\n"
901         elif self.type == "PFN_vkVoidFunction":
902             stub += "    return NULL;\n"
904         stub += "}\n\n"
905         return stub
907     def thunk(self, prefix=None, conv=False):
908         thunk = ""
909         if not conv:
910             thunk += "#ifdef _WIN64\n"
911         if self.is_perf_critical():
912             thunk += "static void {0}{1}(void *args)\n".format(prefix, self.name)
913         else:
914             thunk += "static NTSTATUS {0}{1}(void *args)\n".format(prefix, self.name)
915         thunk += "{\n"
916         if conv:
917             thunk += "    struct\n"
918             thunk += "    {\n"
919             for p in self.params:
920                 thunk += "        {0};\n".format(p.definition(conv=True, is_member=True))
921             if self.extra_param:
922                 thunk += "        PTR32 {0};\n".format(self.extra_param)
923             if self.type != "void":
924                 thunk += "        {0} result;\n".format(self.type)
925             thunk += "    } *params = args;\n"
926         else:
927             thunk += "    struct {0}_params *params = args;\n".format(self.name)
928         thunk += self.body(conv=conv, unwrap=self.thunk_type == ThunkType.PUBLIC, params_prefix="params->")
929         thunk += "}\n"
930         if not conv:
931             thunk += "#endif /* _WIN64 */\n"
932         thunk += "\n"
933         return thunk
935     def loader_thunk(self, prefix=None):
936         thunk = self.prototype(call_conv="WINAPI", prefix=prefix)
937         thunk += "\n{\n"
938         thunk += self.loader_body()
939         thunk += "}\n\n"
940         return thunk
942     def trace(self, message=None, trace_func=None, params_prefix="", conv=False):
943         """ Create a trace string including all parameters.
945         Args:
946             message (str, optional): text to print at start of trace message e.g. 'stub: '
947             trace_func (str, optional): used to override trace function e.g. FIXME, printf, etcetera.
948         """
949         if trace_func is not None:
950             trace = "{0}(\"".format(trace_func)
951         else:
952             trace = "TRACE(\""
954         if message is not None:
955             trace += message
957         # First loop is for all the format strings.
958         trace += ", ".join([p.format_string(conv) for p in self.params])
959         trace += "\\n\""
961         # Second loop for parameter names and optional conversions.
962         for param in self.params:
963             if param.format_conv is not None:
964                 trace += ", " + param.format_conv.format("{0}{1}".format(params_prefix, param.name))
965             else:
966                 trace += ", {0}{1}".format(params_prefix, param.name)
967         trace += ");\n"
969         return trace
972 class VkFunctionPointer(object):
973     def __init__(self, _type, name, members, forward_decls):
974         self.name = name
975         self.members = members
976         self.type = _type
977         self.required = False
978         self.forward_decls = forward_decls
980     @staticmethod
981     def from_xml(funcpointer):
982         if not api_is_vulkan(funcpointer):
983             return None
985         members = []
986         begin = None
988         for t in funcpointer.findall("type"):
989             # General form:
990             # <type>void</type>*       pUserData,
991             # Parsing of the tail (anything past </type>) is tricky since there
992             # can be other data on the next line like: const <type>int</type>..
994             const = True if begin and "const" in begin else False
995             _type = t.text
996             lines = t.tail.split(",\n")
997             if lines[0][0] == "*":
998                 pointer = "*"
999                 name = lines[0][1:].strip()
1000             else:
1001                 pointer = None
1002                 name = lines[0].strip()
1004             # Filter out ); if it is contained.
1005             name = name.partition(");")[0]
1007             # If tail encompasses multiple lines, assign the second line to begin
1008             # for the next line.
1009             try:
1010                 begin = lines[1].strip()
1011             except IndexError:
1012                 begin = None
1014             members.append(VkMember(const=const, _type=_type, pointer=pointer, name=name))
1016         _type = funcpointer.text
1017         name = funcpointer.find("name").text
1018         if "requires" in funcpointer.attrib:
1019             forward_decls = funcpointer.attrib.get("requires").split(",")
1020         else:
1021             forward_decls = []
1022         return VkFunctionPointer(_type, name, members, forward_decls)
1024     def definition(self):
1025         text = ""
1026         # forward declare required structs
1027         for decl in self.forward_decls:
1028             text += "typedef struct {0} {0};\n".format(decl)
1030         text += "{0} {1})(\n".format(self.type, self.name)
1032         first = True
1033         if len(self.members) > 0:
1034             for m in self.members:
1035                 if first:
1036                     text += "    " + m.definition()
1037                     first = False
1038                 else:
1039                     text += ",\n    " + m.definition()
1040         else:
1041             # Just make the compiler happy by adding a void parameter.
1042             text += "void"
1043         text += ");\n"
1044         return text
1046     def is_alias(self):
1047         return False
1049 class VkHandle(object):
1050     def __init__(self, name, _type, parent, alias=None):
1051         self.name = name
1052         self.type = _type
1053         self.parent = parent
1054         self.alias = alias
1055         self.required = False
1056         self.object_type = None
1058     @staticmethod
1059     def from_alias(handle, alias):
1060         name = handle.attrib.get("name")
1061         return VkHandle(name, alias.type, alias.parent, alias=alias)
1063     @staticmethod
1064     def from_xml(handle):
1065         if not api_is_vulkan(handle):
1066             return None
1068         name = handle.find("name").text
1069         _type = handle.find("type").text
1070         parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice.
1071         return VkHandle(name, _type, parent)
1073     def dispatch_table(self, param):
1074         if not self.is_dispatchable():
1075             return None
1077         if self.parent is None:
1078             # Should only happen for VkInstance
1079             return "wine_instance_from_handle({0})->funcs".format(param)
1080         elif self.name == "VkCommandBuffer":
1081             return "wine_cmd_buffer_from_handle({0})->device->funcs".format(param)
1082         elif self.name == "VkDevice":
1083             return "wine_device_from_handle({0})->funcs".format(param)
1084         elif self.name == "VkPhysicalDevice":
1085             return "wine_phys_dev_from_handle({0})->instance->funcs".format(param)
1086         elif self.name == "VkQueue":
1087             return "wine_queue_from_handle({0})->device->funcs".format(param)
1088         elif self.parent in ["VkInstance", "VkPhysicalDevice"]:
1089             return "{0}->instance->funcs".format(param)
1090         elif self.parent in ["VkDevice", "VkCommandPool"]:
1091             return "{0}->device->funcs".format(param)
1092         else:
1093             LOGGER.error("Unhandled dispatchable parent: {0}".format(self.parent))
1095     def definition(self):
1096         """ Generates handle definition e.g. VK_DEFINE_HANDLE(vkInstance) """
1098         # Legacy types are typedef'ed to the new type if they are aliases.
1099         if self.is_alias():
1100             return "typedef {0} {1};\n".format(self.alias.name, self.name)
1102         return "{0}({1})\n".format(self.type, self.name)
1104     def is_alias(self):
1105         return self.alias is not None
1107     def is_dispatchable(self):
1108         """ Some handles like VkInstance, VkDevice are dispatchable objects,
1109         which means they contain a dispatch table of function pointers.
1110         """
1111         return self.type == "VK_DEFINE_HANDLE"
1113     def is_required(self):
1114         return self.required
1116     def native_handle(self, name):
1117         """ Provide access to the native handle of a wrapped object. """
1119         if self.name == "VkCommandBuffer":
1120             return "wine_cmd_buffer_from_handle({0})->command_buffer".format(name)
1121         if self.name == "VkCommandPool":
1122             return "wine_cmd_pool_from_handle({0})->command_pool".format(name)
1123         if self.name == "VkDebugUtilsMessengerEXT":
1124             return "wine_debug_utils_messenger_from_handle({0})->debug_messenger".format(name)
1125         if self.name == "VkDebugReportCallbackEXT":
1126             return "wine_debug_report_callback_from_handle({0})->debug_callback".format(name)
1127         if self.name == "VkDeferredOperationKHR":
1128             return "wine_deferred_operation_from_handle({0})->deferred_operation".format(name)
1129         if self.name == "VkDevice":
1130             return "wine_device_from_handle({0})->device".format(name)
1131         if self.name == "VkInstance":
1132             return "wine_instance_from_handle({0})->instance".format(name)
1133         if self.name == "VkDeviceMemory":
1134             return "wine_device_memory_from_handle({0})->memory".format(name)
1135         if self.name == "VkPhysicalDevice":
1136             return "wine_phys_dev_from_handle({0})->phys_dev".format(name)
1137         if self.name == "VkQueue":
1138             return "wine_queue_from_handle({0})->queue".format(name)
1139         if self.name == "VkSurfaceKHR":
1140             return "wine_surface_from_handle({0})->surface".format(name)
1141         if self.is_dispatchable():
1142             LOGGER.error("Unhandled native handle for: {0}".format(self.name))
1143         return None
1145     def driver_handle(self, name):
1146         """ Provide access to the handle that should be passed to the wine driver """
1148         if self.name == "VkSurfaceKHR":
1149             return "wine_surface_from_handle({0})->driver_surface".format(name)
1151         return self.native_handle(name)
1153     def is_wrapped(self):
1154         return self.native_handle("test") is not None
1156     def needs_unwrapping(self):
1157         return self.is_wrapped()
1159 class VkVariable(object):
1160     def __init__(self, const=False, type_info=None, type=None, name=None, pointer=None, array_len=None,
1161                  dyn_array_len=None, object_type=None, optional=False, returnedonly=False, parent=None,
1162                  selection=None, selector=None):
1163         self.const = const
1164         self.type_info = type_info
1165         self.type = type
1166         self.name = name
1167         self.parent = parent
1168         self.object_type = object_type
1169         self.optional = optional
1170         self.returnedonly = returnedonly
1171         self.selection = selection
1172         self.selector = selector
1174         self.pointer = pointer
1175         self.array_len = array_len
1176         self.dyn_array_len = dyn_array_len
1177         self.pointer_array = False
1178         if isinstance(dyn_array_len, str):
1179             i = dyn_array_len.find(",")
1180             if i != -1:
1181                 self.dyn_array_len = dyn_array_len[0:i]
1182                 self.pointer_array = True
1184         if type_info:
1185             self.set_type_info(type_info)
1187     def __eq__(self, other):
1188         """ Compare member based on name against a string. """
1189         return self.name == other
1191     def set_type_info(self, type_info):
1192         """ Helper function to set type information from the type registry.
1193         This is needed, because not all type data is available at time of
1194         parsing.
1195         """
1196         self.type_info = type_info
1197         self.handle = type_info["data"] if type_info["category"] == "handle" else None
1198         self.struct = type_info["data"] if type_info["category"] == "struct" or type_info["category"] == "union" else None
1200     def get_dyn_array_len(self, prefix, conv):
1201         if isinstance(self.dyn_array_len, int):
1202             return self.dyn_array_len
1204         len_str = self.dyn_array_len
1205         parent = self.parent
1206         len = prefix
1208         # check if length is a member of another struct (for example pAllocateInfo->commandBufferCount)
1209         i = len_str.find("->")
1210         if i != -1:
1211             var = parent[parent.index(len_str[0:i])]
1212             len_str = len_str[i+2:]
1213             len = "({0})->".format(var.value(len, conv))
1214             parent = var.struct
1216         if len_str in parent:
1217             var = parent[parent.index(len_str)]
1218             len = var.value(len, conv);
1219             if var.is_pointer():
1220                 len = "*" + len
1221         else:
1222             len += len_str
1224         if isinstance(self.parent, VkStruct) and self.parent.name in MEMBER_LENGTH_EXPRESSIONS:
1225             exprs = MEMBER_LENGTH_EXPRESSIONS[self.parent.name]
1226             if self.name in exprs:
1227                 len = exprs[self.name].format(struct=prefix, len=len)
1229         return len
1231     def is_const(self):
1232         return self.const
1234     def is_pointer(self):
1235         return self.pointer is not None
1237     def is_pointer_size(self):
1238         if self.type in ["size_t", "HWND", "HINSTANCE"]:
1239             return True
1240         if self.is_handle() and self.handle.is_dispatchable():
1241             return True
1242         return False
1244     def is_handle(self):
1245         return self.handle is not None
1247     def is_struct(self):
1248         return self.type_info["category"] == "struct"
1250     def is_union(self):
1251         return self.type_info["category"] == "union"
1253     def is_bitmask(self):
1254         return self.type_info["category"] == "bitmask"
1256     def is_enum(self):
1257         return self.type_info["category"] == "enum"
1259     def is_dynamic_array(self):
1260         """ Returns if the member is an array element.
1261         Vulkan uses this for dynamically sized arrays for which
1262         there is a 'count' parameter.
1263         """
1264         return self.dyn_array_len is not None
1266     def is_static_array(self):
1267         """ Returns if the member is an array.
1268         Vulkan uses this often for fixed size arrays in which the
1269         length is part of the member.
1270         """
1271         return self.array_len is not None
1273     def is_generic_handle(self):
1274         """ Returns True if the member is a unit64_t containing
1275         a handle with a separate object type
1276         """
1277         return self.object_type != None and self.type == "uint64_t"
1279     def needs_alignment(self):
1280         """ Check if this member needs alignment for 64-bit data.
1281         Various structures need alignment on 64-bit variables due
1282         to compiler differences on 32-bit between Win32 and Linux.
1283         """
1285         if self.is_pointer():
1286             return False
1287         elif self.type == "size_t":
1288             return False
1289         elif self.type in ["uint64_t", "VkDeviceAddress", "VkDeviceSize"]:
1290             return True
1291         elif self.is_bitmask():
1292             return self.type_info["data"].type == "VkFlags64"
1293         elif self.is_enum():
1294             return self.type_info["data"].bitwidth == 64
1295         elif self.is_struct() or self.is_union():
1296             return self.type_info["data"].needs_alignment()
1297         elif self.is_handle():
1298             # Dispatchable handles are pointers to objects, while
1299             # non-dispatchable are uint64_t and hence need alignment.
1300             return not self.handle.is_dispatchable()
1301         return False
1303     def needs_unwrapping(self):
1304         """ Returns if variable needs unwrapping of handle. """
1306         if self.is_struct():
1307             return self.struct.needs_unwrapping()
1309         if self.is_handle():
1310             return self.handle.needs_unwrapping()
1312         if self.is_generic_handle():
1313             return True
1315         return False
1317     def needs_alloc(self, conv, unwrap):
1318         """ Returns True if conversion needs allocation """
1319         if self.is_dynamic_array():
1320             return self.needs_conversion(conv, unwrap, Direction.INPUT, False) \
1321                 or self.needs_conversion(conv, unwrap, Direction.OUTPUT, False)
1323         return (self.is_struct() or (self.is_union() and self.selector)) and self.struct.needs_alloc(conv, unwrap)
1325     def needs_win32_type(self):
1326         return (self.is_struct() or (self.is_union() and self.selector)) and self.struct.needs_win32_type()
1328     def get_conversions(self, unwrap, parent_const=False):
1329         """ Get a list of conversions required for this parameter if any.
1330         Parameters which are structures may require conversion between win32
1331         and the host platform. This function returns a list of conversions
1332         required.
1333         """
1335         conversions = []
1337         # Collect any member conversions first, so we can guarantee
1338         # those functions will be defined prior to usage by the
1339         # 'parent' param requiring conversion.
1340         if self.is_struct() or (self.is_union() and self.selector):
1341             struct = self.struct
1342             is_const = self.is_const() if self.is_pointer() else parent_const
1344             conversions.extend(struct.get_conversions(unwrap, is_const))
1346             for conv in [False, True]:
1347                 if struct.needs_conversion(conv, unwrap, Direction.INPUT, is_const):
1348                     conversions.append(StructConversionFunction(struct, Direction.INPUT, conv, unwrap, is_const))
1349                 if struct.needs_conversion(conv, unwrap, Direction.OUTPUT, is_const):
1350                     conversions.append(StructConversionFunction(struct, Direction.OUTPUT, conv, unwrap, is_const))
1352         if self.is_static_array() or self.is_dynamic_array():
1353             for conv in [False, True]:
1354                 if self.needs_conversion(conv, unwrap, Direction.INPUT, parent_const):
1355                     conversions.append(ArrayConversionFunction(self, Direction.INPUT, conv, unwrap))
1356                 if self.needs_conversion(conv, unwrap, Direction.OUTPUT, parent_const):
1357                     conversions.append(ArrayConversionFunction(self, Direction.OUTPUT, conv, unwrap))
1359         return conversions
1361     def needs_ptr32_type(self):
1362         """ Check if variable needs to use PTR32 type. """
1364         return self.is_pointer() or self.is_pointer_size() or self.is_static_array()
1366     def value(self, prefix, conv):
1367         """ Returns code accessing member value, casting 32-bit pointers when needed. """
1369         if not conv or not self.needs_ptr32_type() or (not self.is_pointer() and self.type == "size_t"):
1370             return prefix + self.name
1372         cast_type = ""
1373         if self.const:
1374             cast_type += "const "
1376         if self.pointer_array or ((self.is_pointer() or self.is_static_array()) and self.is_pointer_size()):
1377             cast_type += "PTR32 *"
1378         else:
1379             cast_type += self.type
1380             if self.needs_win32_type():
1381                 cast_type += "32"
1383             if self.is_pointer():
1384                 cast_type += " {0}".format(self.pointer)
1385             elif self.is_static_array():
1386                 cast_type += " *"
1388         return "({0})UlongToPtr({1}{2})".format(cast_type, prefix, self.name)
1391 class VkMember(VkVariable):
1392     def __init__(self, const=False, struct_fwd_decl=False,_type=None, pointer=None, name=None, array_len=None,
1393                  dyn_array_len=None, optional=False, values=None, object_type=None, bit_width=None,
1394                  returnedonly=False, parent=None, selection=None, selector=None):
1395         VkVariable.__init__(self, const=const, type=_type, name=name, pointer=pointer, array_len=array_len,
1396                             dyn_array_len=dyn_array_len, object_type=object_type, optional=optional,
1397                             returnedonly=returnedonly, parent=parent, selection=selection, selector=selector)
1398         self.struct_fwd_decl = struct_fwd_decl
1399         self.values = values
1400         self.bit_width = bit_width
1402     def __repr__(self):
1403         return "{0} {1} {2} {3} {4} {5} {6}".format(self.const, self.struct_fwd_decl, self.type, self.pointer,
1404                 self.name, self.array_len, self.dyn_array_len)
1406     @staticmethod
1407     def from_xml(member, returnedonly, parent):
1408         """ Helper function for parsing a member tag within a struct or union. """
1410         if not api_is_vulkan(member):
1411             return None
1413         name_elem = member.find("name")
1414         type_elem = member.find("type")
1416         const = False
1417         struct_fwd_decl = False
1418         member_type = None
1419         pointer = None
1420         array_len = None
1421         bit_width = None
1423         values = member.get("values")
1425         if member.text:
1426             if "const" in member.text:
1427                 const = True
1429             # Some members contain forward declarations:
1430             # - VkBaseInstructure has a member "const struct VkBaseInStructure *pNext"
1431             # - VkWaylandSurfaceCreateInfoKHR has a member "struct wl_display *display"
1432             if "struct" in member.text:
1433                 struct_fwd_decl = True
1435         if type_elem is not None:
1436             member_type = type_elem.text
1437             if type_elem.tail is not None:
1438                 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1440         # Name of other member within, which stores the number of
1441         # elements pointed to be by this member.
1442         dyn_array_len = member.get("len")
1444         # Some members are optional, which is important for conversion code e.g. not dereference NULL pointer.
1445         optional = True if member.get("optional") else False
1447         # Usually we need to allocate memory for dynamic arrays. We need to do the same in a few other cases
1448         # like for VkCommandBufferBeginInfo.pInheritanceInfo. Just threat such cases as dynamic arrays of
1449         # size 1 to simplify code generation.
1450         if dyn_array_len is None and pointer is not None:
1451             dyn_array_len = 1
1453         # Some members are arrays, attempt to parse these. Formats include:
1454         # <member><type>char</type><name>extensionName</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
1455         # <member><type>uint32_t</type><name>foo</name>[4]</member>
1456         if name_elem.tail and name_elem.tail[0] == '[':
1457             LOGGER.debug("Found array type")
1458             enum_elem = member.find("enum")
1459             if enum_elem is not None:
1460                 array_len = enum_elem.text
1461             else:
1462                 # Remove brackets around length
1463                 array_len = name_elem.tail.strip("[]")
1465         object_type = member.get("objecttype", None)
1467         # Some members are bit field values:
1468         # <member><type>uint32_t</type> <name>mask</name>:8</member>
1469         if name_elem.tail and name_elem.tail[0] == ':':
1470             LOGGER.debug("Found bit field")
1471             bit_width = int(name_elem.tail[1:])
1473         selection = member.get("selection").split(',') if member.get("selection") else None
1474         selector = member.get("selector", None)
1476         return VkMember(const=const, struct_fwd_decl=struct_fwd_decl, _type=member_type, pointer=pointer,
1477                         name=name_elem.text, array_len=array_len, dyn_array_len=dyn_array_len, optional=optional,
1478                         values=values, object_type=object_type, bit_width=bit_width, returnedonly=returnedonly,
1479                         parent=parent, selection=selection, selector=selector)
1481     def copy(self, input, output, direction, conv, unwrap):
1482         """ Helper method for use by conversion logic to generate a C-code statement to copy this member.
1483             - `conv` indicates whether the statement is in a struct alignment conversion path. """
1485         win_type = "win32" if conv else "win64"
1486         if self.needs_conversion(conv, unwrap, direction, False):
1487             if self.is_dynamic_array():
1488                 # Array length is either a variable name (string) or an int.
1489                 count = self.get_dyn_array_len(input, conv)
1490                 host_part = "host" if unwrap else "unwrapped_host"
1491                 pointer_part = "pointer_" if self.pointer_array else ""
1492                 if direction == Direction.OUTPUT:
1493                     return "convert_{2}_{7}array_{6}_to_{5}({3}{1}, {0}, {4});\n".format(
1494                         self.value(output, conv), self.name, self.type, input, count, win_type,
1495                         host_part, pointer_part)
1496                 else:
1497                     return "{0}{1} = convert_{2}_{7}array_{5}_to_{6}(ctx, {3}, {4});\n".format(
1498                         output, self.name, self.type, self.value(input, conv), count, win_type,
1499                         host_part, pointer_part)
1500             elif self.is_static_array():
1501                 count = self.array_len
1502                 if direction == Direction.OUTPUT:
1503                     # Needed by VkMemoryHeap.memoryHeaps
1504                     host_part = "host" if unwrap else "unwrapped_host"
1505                     return "convert_{0}_array_{6}_to_{5}({2}{1}, {3}{1}, {4});\n".format(
1506                         self.type, self.name, input, output, count, win_type, host_part)
1507                 else:
1508                     # Nothing needed this yet.
1509                     LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
1510             elif self.is_handle() and self.needs_unwrapping():
1511                 handle = self.type_info["data"]
1512                 if direction == Direction.OUTPUT:
1513                     LOGGER.err("OUTPUT parameter {0}.{1} cannot be unwrapped".format(self.type, self.name))
1514                 elif self.optional:
1515                     return "{0}{1} = {2} ? {3} : 0;\n".format(output, self.name,
1516                         self.value(input, conv), handle.driver_handle(self.value(input, conv)))
1517                 else:
1518                     return "{0}{1} = {2};\n".format(output, self.name, handle.driver_handle(self.value(input, conv)))
1519             elif self.is_generic_handle():
1520                 if direction == Direction.OUTPUT:
1521                     LOGGER.err("OUTPUT parameter {0}.{1} cannot be unwrapped".format(self.type, self.name))
1522                 else:
1523                     return "{0}{1} = wine_vk_unwrap_handle({2}{3}, {2}{1});\n".format(output, self.name, input, self.object_type)
1524             else:
1525                 selector_part = ", {0}{1}".format(input, self.selector) if self.selector else ""
1526                 if direction == Direction.OUTPUT:
1527                     return "convert_{0}_host_to_{4}(&{2}{1}, &{3}{1}{5});\n".format(self.type, self.name, input, output, win_type, selector_part)
1528                 else:
1529                     ctx_param = "ctx, " if self.needs_alloc(conv, unwrap) else ""
1530                     host_part = "host" if unwrap else "unwrapped_host"
1531                     return "convert_{0}_{4}_to_{6}({5}&{2}{1}, &{3}{1}{7});\n".format(self.type, self.name, input, output, win_type, ctx_param, host_part, selector_part)
1532         elif self.is_static_array():
1533             bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type)
1534             return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count)
1535         elif direction == Direction.INPUT:
1536             return "{0}{1} = {2};\n".format(output, self.name, self.value(input, conv))
1537         elif conv and direction == Direction.OUTPUT and self.is_pointer():
1538             return "{0}{1} = PtrToUlong({2}{1});\n".format(output, self.name, input)
1539         else:
1540             return "{0}{1} = {2}{1};\n".format(output, self.name, input)
1542     def definition(self, align=False, conv=False):
1543         """ Generate prototype for given function.
1545         Args:
1546             align (bool, optional): Enable alignment if a type needs it. This adds WINE_VK_ALIGN(8) to a member.
1547             conv (bool, optional): Enable conversion if a type needs it. This appends '_host' to the name.
1548         """
1550         if conv and (self.is_pointer() or self.is_pointer_size()):
1551             text = "PTR32 " + self.name
1552             if self.is_static_array():
1553                 text += "[{0}]".format(self.array_len)
1554             return text
1556         text = ""
1557         if self.is_const():
1558             text += "const "
1560         if self.is_struct_forward_declaration():
1561             text += "struct "
1563         text += self.type
1564         if conv and self.needs_win32_type():
1565             text += "32"
1567         if self.is_pointer():
1568             text += " {0}{1}".format(self.pointer, self.name)
1569         else:
1570             if align and self.needs_alignment():
1571                 if conv:
1572                     text += " DECLSPEC_ALIGN(8) " + self.name
1573                 else:
1574                     text += " WINE_VK_ALIGN(8) " + self.name
1575             else:
1576                 text += " " + self.name
1578         if self.is_static_array():
1579             text += "[{0}]".format(self.array_len)
1581         if self.is_bit_field():
1582             text += ":{}".format(self.bit_width)
1584         return text
1586     def is_struct_forward_declaration(self):
1587         return self.struct_fwd_decl
1589     def is_bit_field(self):
1590         return self.bit_width is not None
1592     def needs_conversion(self, conv, unwrap, direction, struct_const):
1593         """ Check if member needs conversion. """
1595         # we can't convert unions if we don't have a selector
1596         if self.is_union() and not self.selector:
1597             return False
1599         is_const = self.is_const() if self.is_pointer() else struct_const
1601         # const members don't needs output conversion unless they are structs with non-const pointers
1602         if direction == Direction.OUTPUT and is_const and not self.is_struct():
1603             return False
1605         if direction == Direction.INPUT:
1606             # returnedonly members don't needs input conversions
1607             if not self.is_pointer() and self.returnedonly:
1608                 return False
1609             # pointer arrays always need input conversion
1610             if conv and self.is_dynamic_array() and self.pointer_array:
1611                 return True
1613         if self.is_handle():
1614             if unwrap and self.handle.is_wrapped():
1615                 return True
1616             if conv and self.handle.is_dispatchable():
1617                 return True
1618         elif self.is_generic_handle():
1619             if unwrap:
1620                 return True
1621         elif self.is_struct() or self.is_union():
1622             if self.struct.needs_conversion(conv, unwrap, direction, is_const):
1623                 return True
1625         # if pointer member needs output conversion, it also needs input conversion
1626         # to allocate the pointer
1627         if direction == Direction.INPUT and self.is_pointer() and \
1628            self.needs_conversion(conv, unwrap, Direction.OUTPUT, struct_const):
1629             return True
1631         return False
1633 class VkParam(VkVariable):
1634     """ Helper class which describes a parameter to a function call. """
1636     def __init__(self, type_info, const=None, pointer=None, name=None, parent=None, array_len=None,
1637                  dyn_array_len=None, object_type=None, optional=False):
1638         VkVariable.__init__(self, const=const, type_info=type_info, type=type_info["name"], name=name,
1639                             pointer=pointer, array_len=array_len, dyn_array_len=dyn_array_len,
1640                             object_type=object_type, optional=optional, parent=parent)
1642         self._set_format_string()
1644     def __repr__(self):
1645         return "{0} {1} {2} {3} {4} {5}".format(self.const, self.type, self.pointer, self.name, self.array_len, self.dyn_array_len)
1647     @staticmethod
1648     def from_xml(param, types, parent):
1649         """ Helper function to create VkParam from xml. """
1651         if not api_is_vulkan(param):
1652             return None
1654         # Parameter parsing is slightly tricky. All the data is contained within
1655         # a param tag, but some data is within subtags while others are text
1656         # before or after the type tag.
1657         # Common structure:
1658         # <param>const <type>char</type>* <name>pLayerName</name></param>
1660         name_elem = param.find("name")
1661         array_len = None
1662         name = name_elem.text
1663         # Tail contains array length e.g. for blendConstants param of vkSetBlendConstants
1664         if name_elem.tail is not None:
1665             array_len = name_elem.tail.strip("[]")
1667         # Name of other parameter in function prototype, which stores the number of
1668         # elements pointed to be by this parameter.
1669         dyn_array_len = param.get("len", None)
1671         const = param.text.strip() if param.text else None
1672         type_elem = param.find("type")
1673         pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1675         attr = param.get("optional")
1676         optional = attr and attr.startswith("true")
1678         # Some uint64_t are actually handles with a separate type param
1679         object_type = param.get("objecttype", None)
1681         # Since we have parsed all types before hand, this should not happen.
1682         type_info = types.get(type_elem.text, None)
1683         if type_info is None:
1684             LOGGER.err("type info not found for: {0}".format(type_elem.text))
1686         return VkParam(type_info, const=const, pointer=pointer, name=name, array_len=array_len,
1687                        dyn_array_len=dyn_array_len, object_type=object_type, optional=optional,
1688                        parent=parent)
1690     def _set_format_string(self):
1691         """ Internal helper function to be used by constructor to set format string. """
1693         # Determine a format string used by code generation for traces.
1694         # 64-bit types need a conversion function.
1695         self.format_conv = None
1696         if self.is_static_array() or self.is_pointer():
1697             self.format_str = "%p"
1698         else:
1699             if self.type_info["category"] in ["bitmask"]:
1700                 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1701                 if self.type_info["data"].type == "VkFlags64":
1702                     self.format_str = "0x%s"
1703                     self.format_conv = "wine_dbgstr_longlong({0})"
1704                 else:
1705                     self.format_str = "%#x"
1706             elif self.type_info["category"] in ["enum"]:
1707                 self.format_str = "%#x"
1708             elif self.is_handle():
1709                 # We use uint64_t for non-dispatchable handles as opposed to pointers
1710                 # for dispatchable handles.
1711                 if self.handle.is_dispatchable():
1712                     self.format_str = "%p"
1713                 else:
1714                     self.format_str = "0x%s"
1715                     self.format_conv = "wine_dbgstr_longlong({0})"
1716             elif self.type == "float":
1717                 self.format_str = "%f"
1718             elif self.type == "int":
1719                 self.format_str = "%d"
1720             elif self.type == "int32_t":
1721                 self.format_str = "%d"
1722             elif self.type == "size_t":
1723                 self.format_str = "0x%s"
1724                 self.format_conv = "wine_dbgstr_longlong({0})"
1725             elif self.type in ["uint16_t", "uint32_t", "VkBool32"]:
1726                 self.format_str = "%u"
1727             elif self.type in ["uint64_t", "VkDeviceAddress", "VkDeviceSize"]:
1728                 self.format_str = "0x%s"
1729                 self.format_conv = "wine_dbgstr_longlong({0})"
1730             elif self.type == "HANDLE":
1731                 self.format_str = "%p"
1732             elif self.type in ["VisualID", "xcb_visualid_t", "RROutput", "zx_handle_t", "NvSciBufObj", "NvSciBufAttrList", "NvSciSyncAttrList"]:
1733                 # Don't care about specific types for non-Windows platforms.
1734                 self.format_str = ""
1735             else:
1736                 LOGGER.warn("Unhandled type: {0}".format(self.type_info))
1738     def copy(self, direction, conv, unwrap, prefix=""):
1739         win_type = "win32" if conv else "win64"
1740         wrap_part = "" if unwrap or not self.needs_unwrapping() else "unwrapped_"
1741         if direction == Direction.INPUT:
1742             ctx_param = "ctx, " if self.needs_alloc(conv, unwrap) else ""
1743             if self.is_dynamic_array():
1744                 return "    {0}_host = convert_{2}_array_{4}_to_{6}host({5}{1}, {3});\n".format(
1745                     self.name, self.value(prefix, conv), self.type, self.get_dyn_array_len(prefix, conv),
1746                     win_type, ctx_param, wrap_part)
1747             elif self.optional:
1748                 ret  = "    if ({0}{1})\n".format(prefix, self.name)
1749                 ret += "    {\n"
1750                 ret += "        {0}_host = conversion_context_alloc(ctx, sizeof(*{0}_host));\n".format(self.name)
1751                 ret += "        convert_{0}_{3}_to_{5}host({4}{1}, {2}_host);\n".format(
1752                     self.type, self.value(prefix, conv), self.name, win_type, ctx_param, wrap_part)
1753                 ret += "    }\n"
1754                 return ret
1755             elif self.is_struct():
1756                 return "    convert_{0}_{3}_to_{5}host({4}{1}, &{2}_host);\n".format(
1757                     self.type, self.value(prefix, conv), self.name, win_type, ctx_param, wrap_part)
1758             elif self.is_pointer_size() and self.type != "size_t":
1759                 return "    {0}_host = UlongToPtr(*{1});\n".format(self.name, self.value(prefix, conv))
1760             else:
1761                 return "    {0}_host = *{1};\n".format(self.name, self.value(prefix, conv))
1762         else:
1763             if self.is_dynamic_array():
1764                 return "    convert_{0}_array_{1}host_to_{2}({3}_host, {4}, {5});\n".format(
1765                     self.type, wrap_part, win_type, self.name, self.value(prefix, conv),
1766                     self.get_dyn_array_len(prefix, conv))
1767             elif self.is_struct():
1768                 ref_part = "" if self.optional else "&"
1769                 return "    convert_{0}_host_to_{3}({4}{2}_host, {1});\n".format(
1770                     self.type, self.value(prefix, conv), self.name, win_type, ref_part)
1771             elif self.is_pointer_size() and self.type != "size_t":
1772                 return "    *{0} = PtrToUlong({1}_host);\n".format(self.value(prefix, conv), self.name)
1773             else:
1774                 return "    *{0} = {1}_host;\n".format(self.value(prefix, conv), self.name)
1776     def definition(self, postfix=None, is_member=False, conv=False):
1777         """ Return prototype for the parameter. E.g. 'const char *foo' """
1779         if is_member and conv and self.needs_ptr32_type():
1780             return "PTR32 {0}".format(self.name)
1782         proto = ""
1783         if self.const:
1784             proto += self.const + " "
1786         proto += self.type
1787         name = self.name
1788         if conv and self.needs_win32_type():
1789             proto += "32"
1791         if is_member and self.needs_alignment():
1792             proto += " DECLSPEC_ALIGN(8)"
1794         if self.is_pointer():
1795             proto += " {0}{1}".format(self.pointer, name)
1796         elif is_member and self.is_static_array():
1797             proto += " *" + name
1798         else:
1799             proto += " " + name
1801         # Allows appending something to the variable name useful for
1802         # win32 to host conversion.
1803         if postfix is not None:
1804             proto += postfix
1806         if not is_member and self.is_static_array():
1807             proto += "[{0}]".format(self.array_len)
1809         return proto
1811     def dispatch_table(self, params_prefix, conv):
1812         """ Return functions dispatch table pointer for dispatchable objects. """
1814         if not self.is_dispatchable():
1815             return None
1817         return self.handle.dispatch_table(self.value(params_prefix, conv))
1819     def format_string(self, conv):
1820         if conv and self.needs_ptr32_type() and (self.type != "size_t" or self.is_pointer()):
1821             return "%#x"
1822         return self.format_str
1824     def is_dispatchable(self):
1825         if not self.is_handle():
1826             return False
1828         return self.handle.is_dispatchable()
1830     def needs_conversion(self, conv, unwrap, direction, parent_const=False):
1831         """ Check if param needs conversion. """
1833         if self.is_struct():
1834             return self.struct.needs_conversion(conv, unwrap, direction, self.is_const())
1836         if self.is_handle():
1837             # non-pointer handles are handled inline in thunks
1838             if not self.is_dynamic_array() and not self.is_static_array():
1839                 return conv and self.is_pointer() and self.handle.is_dispatchable()
1841             # vkAllocateCommandBuffers is a special case, we use it in our private thunk as an input param
1842             param_direction = (Direction.INPUT if self.is_const() else Direction.OUTPUT)
1843             if self.name == "pCommandBuffers":
1844                 param_direction = Direction.INPUT
1845             if direction != param_direction:
1846                 return False
1848             if unwrap and self.handle.is_wrapped():
1849                 return True
1850             if conv and self.handle.is_dispatchable():
1851                 return True
1852         elif self.is_pointer() and self.is_pointer_size():
1853             return conv
1855         return False
1857     def needs_variable(self, conv, unwrap):
1858         if self.needs_conversion(conv, unwrap, Direction.INPUT):
1859             return True
1860         if self.needs_conversion(conv, unwrap, Direction.OUTPUT):
1861             return True
1862         return False
1864     def spec(self):
1865         """ Generate spec file entry for this parameter. """
1867         if self.is_pointer() and self.type == "char":
1868             return "str"
1869         if self.is_dispatchable() or self.is_pointer() or self.is_static_array():
1870             return "ptr"
1871         if self.type_info["category"] in ["bitmask"]:
1872             # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1873             if self.type_info["data"].type == "VkFlags64":
1874                 return "int64"
1875             else:
1876                 return "long"
1877         if self.type_info["category"] in ["enum"]:
1878             return "long"
1879         if self.is_handle() and not self.is_dispatchable():
1880             return "int64"
1881         if self.type == "float":
1882             return "float"
1883         if self.type in ["int", "int32_t", "size_t", "uint16_t", "uint32_t", "VkBool32"]:
1884             return "long"
1885         if self.type in ["uint64_t", "VkDeviceSize"]:
1886             return "int64"
1888         LOGGER.error("Unhandled spec conversion for type: {0}".format(self.type))
1890     def variable(self, conv, unwrap, params_prefix=""):
1891         """ Returns 'glue' code during generation of a function call on how to access the variable.
1892         This function handles various scenarios such as 'unwrapping' if dispatchable objects and
1893         renaming of parameters in case of win32 -> host conversion.
1895         Args:
1896             conv (bool, optional): Enable conversion if the param needs it. This appends '_host' to the name.
1897         """
1899         # Hack until we enable allocation callbacks from ICD to application. These are a joy
1900         # to enable one day, because of calling convention conversion.
1901         if unwrap and "VkAllocationCallbacks" in self.type:
1902             LOGGER.debug("TODO: setting NULL VkAllocationCallbacks for {0}".format(self.name))
1903             return "NULL"
1905         if self.needs_variable(conv, unwrap):
1906             if self.is_dynamic_array() or self.optional:
1907                 return "{0}_host".format(self.name)
1908             else:
1909                 return "&{0}_host".format(self.name)
1911         p = self.value(params_prefix, conv)
1913         if unwrap:
1914             unwrap_handle = None
1915             if self.object_type != None and self.type == "uint64_t":
1916                 unwrap_handle = "wine_vk_unwrap_handle({0}{1}, {0}{2})".format(
1917                     params_prefix, self.object_type, self.name)
1919             elif self.is_handle():
1920                 # We need to pass the native handle to the native Vulkan calls and
1921                 # the wine driver's handle to calls which are wrapped by the driver.
1922                 unwrap_handle = self.handle.driver_handle(p)
1923             if unwrap_handle:
1924                 if self.optional:
1925                     unwrap_handle = "{0}{1} ? {2} : 0".format(params_prefix, self.name, unwrap_handle)
1926                 return unwrap_handle
1928         return p
1931 class VkStruct(Sequence):
1932     """ Class which represents the type union and struct. """
1934     def __init__(self, name, members, returnedonly, structextends, alias=None, union=False):
1935         self.name = name
1936         self.members = members
1937         self.returnedonly = returnedonly
1938         self.structextends = structextends
1939         self.required = False
1940         self.alias = alias
1941         self.union = union
1942         self.type_info = None # To be set later.
1943         self.struct_extensions = []
1944         self.aliased_by = []
1946     def __getitem__(self, i):
1947         return self.members[i]
1949     def __len__(self):
1950         return len(self.members)
1952     @staticmethod
1953     def from_alias(struct, alias):
1954         name = struct.attrib.get("name")
1955         aliasee = VkStruct(name, alias.members, alias.returnedonly, alias.structextends, alias=alias)
1957         alias.add_aliased_by(aliasee)
1958         return aliasee
1960     @staticmethod
1961     def from_xml(struct):
1962         if not api_is_vulkan(struct):
1963             return None
1965         # Unions and structs are the same parsing wise, but we need to
1966         # know which one we are dealing with later on for code generation.
1967         union = True if struct.attrib["category"] == "union" else False
1969         name = struct.attrib.get("name")
1971         # 'Output' structures for which data is filled in by the API are
1972         # marked as 'returnedonly'.
1973         returnedonly = True if struct.attrib.get("returnedonly") else False
1975         # Those structs seem to be broken in spec, they are specified as
1976         # returned only, but documented as input structs.
1977         if name in ["VkSubpassShadingPipelineCreateInfoHUAWEI",
1978                     "VkPipelineShaderStageRequiredSubgroupSizeCreateInfo"]:
1979             returnedonly = False
1981         # Those structs don't have returnedonly in spec, but they could (should?).
1982         if name in ["VkSurfaceCapabilitiesPresentBarrierNV",
1983                     "VkCooperativeMatrixPropertiesNV",
1984                     "VkPerformanceValueINTEL"]:
1985             returnedonly = True
1987         structextends = struct.attrib.get("structextends")
1988         structextends = structextends.split(",") if structextends else []
1990         s = VkStruct(name, [], returnedonly, structextends, union=union)
1991         for member in struct.findall("member"):
1992             vk_member = VkMember.from_xml(member, returnedonly, s)
1993             if vk_member:
1994                 s.members.append(vk_member)
1996         return s
1998     @staticmethod
1999     def decouple_structs(structs):
2000         """ Helper function which decouples a list of structs.
2001         Structures often depend on other structures. To make the C compiler
2002         happy we need to define 'substructures' first. This function analyzes
2003         the list of structures and reorders them in such a way that they are
2004         decoupled.
2005         """
2007         tmp_structs = list(structs) # Don't modify the original structures.
2008         decoupled_structs = []
2010         while (len(tmp_structs) > 0):
2011             # Iterate over a copy because we want to modify the list inside the loop.
2012             for struct in list(tmp_structs):
2013                 dependends = False
2015                 if not struct.required:
2016                     tmp_structs.remove(struct)
2017                     continue
2019                 for m in struct:
2020                     if not (m.is_struct() or m.is_union()):
2021                         continue
2023                     # VkBaseInstructure and VkBaseOutStructure reference themselves.
2024                     if m.type == struct.name:
2025                         break
2027                     found = False
2028                     # Check if a struct we depend on has already been defined.
2029                     for s in decoupled_structs:
2030                         if s.name == m.type:
2031                             found = True
2032                             break
2034                     if not found:
2035                         # Check if the struct we depend on is even in the list of structs.
2036                         # If found now, it means we haven't met all dependencies before we
2037                         # can operate on the current struct.
2038                         # When generating 'host' structs we may not be able to find a struct
2039                         # as the list would only contain the structs requiring conversion.
2040                         for s in tmp_structs:
2041                             if s.name == m.type:
2042                                 dependends = True
2043                                 break
2045                 if dependends == False:
2046                     decoupled_structs.append(struct)
2047                     tmp_structs.remove(struct)
2049         return decoupled_structs
2051     def definition(self, align=False, conv=False):
2052         """ Convert structure to textual definition.
2054         Args:
2055             align (bool, optional): enable alignment to 64-bit for win32 struct compatibility.
2056             conv (bool, optional): enable struct conversion if the struct needs it.
2057             postfix (str, optional): text to append to end of struct name, useful for struct renaming.
2058         """
2060         if self.is_alias():
2061             return ""
2063         suffix = "32" if conv else ""
2064         if self.union:
2065             text = "typedef union {0}".format(self.name)
2066         else:
2067             text = "typedef struct {0}".format(self.name)
2068         text += suffix
2070         text += "\n{\n"
2072         for m in self:
2073             if align and m.needs_alignment():
2074                 text += "    {0};\n".format(m.definition(align=align, conv=conv))
2075             else:
2076                 text += "    {0};\n".format(m.definition(conv=conv))
2078         text += "}} {0}{1};\n".format(self.name, suffix)
2080         for aliasee in self.aliased_by:
2081             text += "typedef {0}{2} {1}{2};\n".format(self.name, aliasee.name, suffix)
2083         return text
2085     def is_alias(self):
2086         return bool(self.alias)
2088     def add_aliased_by(self, aliasee):
2089         self.aliased_by.append(aliasee)
2091     def needs_alignment(self):
2092         """ Check if structure needs alignment for 64-bit data.
2093         Various structures need alignment on 64-bit variables due
2094         to compiler differences on 32-bit between Win32 and Linux.
2095         """
2097         for m in self.members:
2098             if self.name == m.type:
2099                 continue
2100             if m.needs_alignment():
2101                 return True
2102         return False
2104     def needs_unwrapping(self):
2105         """ Returns if struct members need unwrapping of handle. """
2107         for m in self.members:
2108             if self.name == m.type:
2109                 continue
2110             if m.needs_unwrapping():
2111                 return True
2112         return False
2114     def needs_extensions_conversion(self, conv, direction):
2115         """ Check if struct contains extensions chain that needs to be converted """
2117         if direction == Direction.INPUT and self.name in STRUCT_CHAIN_CONVERSIONS:
2118             return True
2120         if not "pNext" in self:
2121             return False
2122         is_const = self.members[self.members.index("pNext")].is_const()
2123         # VkOpticalFlowSessionCreateInfoNV is missing const in its pNext pointer
2124         if self.name in ["VkOpticalFlowSessionCreateInfoNV",
2125                          "VkDescriptorBufferBindingInfoEXT"]:
2126             is_const = True
2127         needs_output_copy = False
2129         for e in self.struct_extensions:
2130             if not e.required:
2131                 continue
2132             if e.needs_conversion(conv, True, direction, is_const, check_extensions=False):
2133                 return True
2134             if direction == Direction.INPUT:
2135                 # we need input conversion of structs containing struct chain even if it's returnedonly,
2136                 # so that we have a chance to allocate buffers
2137                 if e.needs_conversion(conv, True, Direction.OUTPUT, is_const, check_extensions=False):
2138                     return True
2140         return False
2142     def needs_conversion(self, conv, unwrap, direction, is_const, check_extensions=True):
2143         """ Check if struct needs conversion. """
2145         # VkAllocationCallbacks never needs conversion
2146         if self.name == "VkAllocationCallbacks":
2147             return False
2149         # pFixedRateFlags field is missing const, but it doesn't need output conversion
2150         if direction == Direction.OUTPUT and self.name == "VkImageCompressionControlEXT":
2151             return False
2153         needs_output_copy = False
2155         for m in self.members:
2156             if self.name == m.type:
2157                 continue
2159             if m.name == "pNext":
2160                 # pNext is a pointer, so it always needs conversion
2161                 if conv and direction == Direction.INPUT:
2162                     return True
2163                 # we need input conversion of structs containing struct chain even if it's returnedonly
2164                 if direction == Direction.INPUT and \
2165                    self.needs_conversion(conv, unwrap, Direction.OUTPUT, is_const):
2166                     return True
2167                 continue
2169             # for non-pointer members, check for returnedonly and const attributes
2170             if not m.is_pointer() or m.type == "void":
2171                 if direction == Direction.INPUT:
2172                     if self.returnedonly:
2173                         continue
2174                 else:
2175                     if is_const or m.is_const():
2176                         continue
2178             # check alignment and pointer-sized members for 32-bit conversions
2179             if conv and (direction == Direction.INPUT or not is_const):
2180                 if m.is_pointer() or m.is_pointer_size():
2181                     return True
2182                 # we don't check structs here, they will will be traversed by needs_conversion chain anyway
2183                 if not m.is_struct() and m.needs_alignment():
2184                     return True
2186             if m.needs_conversion(conv, unwrap, direction, is_const):
2187                 return True
2189             # pointers will be handled by needs_conversion, but if we have any other non-const
2190             # member, we may need to copy output
2191             if direction == Direction.OUTPUT and not m.is_pointer() and not is_const and not m.is_const():
2192                 needs_output_copy = True
2194         # if output needs any copy and we need input conversion, then we also need output conversion
2195         if needs_output_copy and self.needs_conversion(conv, unwrap, Direction.INPUT, check_extensions):
2196             return True
2198         return check_extensions and self.needs_extensions_conversion(conv, direction)
2200     def needs_alloc(self, conv, unwrap):
2201         """ Check if any struct member needs some memory allocation."""
2203         if self.needs_extensions_conversion(conv, Direction.INPUT):
2204             return True
2206         for m in self.members:
2207             if self.name == m.type:
2208                 continue
2209             if m.needs_alloc(conv, unwrap):
2210                 return True
2212         return False
2214     def needs_win32_type(self):
2215         # VkAllocationCallbacks never needs conversion
2216         if self.name == "VkAllocationCallbacks":
2217             return False
2219         for m in self.members:
2220             if self.name == m.type:
2221                 continue
2222             if m.is_pointer() or m.is_pointer_size():
2223                 return True
2224             if m.needs_alignment():
2225                 return True
2226             if (m.is_struct() or m.is_union()) and m.struct.needs_win32_type():
2227                 return True
2229     def set_type_info(self, types):
2230         """ Helper function to set type information from the type registry.
2231         This is needed, because not all type data is available at time of
2232         parsing.
2233         """
2234         for m in self.members:
2235             type_info = types[m.type]
2236             m.set_type_info(type_info)
2238     def get_conversions(self, unwrap, parent_const):
2239         conversions = []
2241         # Collect any conversion for any extension structs.
2242         for e in self.struct_extensions:
2243             if not e.required:
2244                 continue
2245             conversions.extend(e.get_conversions(True, parent_const))
2247         # Collect any conversion for any member structs.
2248         for m in self:
2249             if m.type == self.name:
2250                 continue
2251             conversions.extend(m.get_conversions(unwrap, parent_const))
2253         return conversions
2256 class StructConversionFunction(object):
2257     def __init__(self, struct, direction, conv, unwrap, const):
2258         self.direction = direction
2259         self.operand = struct
2260         self.type = struct.name
2261         self.conv = conv
2262         self.unwrap = unwrap or not self.operand.needs_unwrapping()
2263         self.const = const
2265         name = "convert_{0}_".format(self.type)
2266         win_type = "win32" if self.conv else "win64"
2267         host_part = "host" if self.unwrap else "unwrapped_host"
2268         if self.direction == Direction.INPUT:
2269             name += "{0}_to_{1}".format(win_type, host_part)
2270         else: # Direction.OUTPUT
2271             name += "{0}_to_{1}".format(host_part, win_type)
2272         self.name = name
2274     def __eq__(self, other):
2275         return self.name == other.name
2277     def member_needs_copy(self, struct, m):
2278         if self.direction == Direction.OUTPUT:
2279             if m.name in ["sType", "pNext"]:
2280                 return False
2281             if self.const and not m.is_pointer():
2282                 return False
2283             if m.is_const() and not m.needs_conversion(self.conv, self.unwrap, Direction.OUTPUT, self.const):
2284                 return False
2285         else:
2286             if m.name == "pNext":
2287                 return True
2288             if m.name != "sType" and struct.returnedonly and not m.needs_conversion(
2289                     self.conv, self.unwrap, Direction.INPUT, self.const):
2290                 return False
2291         return True
2293     def definition(self):
2294         """ Helper function for generating a struct conversion function. """
2296         # It doesn't make sense to generate conversion functions for non-struct variables
2297         # which aren't in arrays, as this should be handled by the copy() function
2298         if not isinstance(self.operand, VkStruct):
2299             return ""
2301         body = ""
2303         if not self.conv:
2304             body += "#ifdef _WIN64\n"
2306         needs_alloc = self.direction != Direction.OUTPUT and self.operand.needs_alloc(self.conv, self.unwrap)
2307         win_type = self.type
2308         if self.conv and self.operand.needs_win32_type():
2309             win_type += "32"
2310         if self.direction == Direction.OUTPUT and self.const:
2311             win_type = "const " + win_type
2313         if self.conv:
2314             body += "static inline void {0}(".format(self.name)
2316             if self.direction == Direction.OUTPUT:
2317                 params = ["const {0} *in".format(self.type), "{0} *out".format(win_type)]
2318             else:
2319                 params = ["const {0} *in".format(win_type), "{0} *out".format(self.type)]
2321             if self.operand.union:
2322                 params.append("VkFlags selector")
2324             # Generate parameter list
2325             if needs_alloc:
2326                 body += "struct conversion_context *ctx, "
2327             body += ", ".join(p for p in params)
2328             body += ")\n"
2330         else:
2331             body += "static inline void {0}(".format(self.name)
2333             params = ["const {0} *in".format(self.type), "{0} *out".format(self.type)]
2335             # Generate parameter list
2336             if needs_alloc:
2337                 body += "struct conversion_context *ctx, "
2338             body += ", ".join(p for p in params)
2339             body += ")\n"
2341         needs_extensions = self.operand.needs_extensions_conversion(self.conv, self.direction)
2343         body += "{\n"
2344         if needs_extensions:
2345             if self.direction == Direction.INPUT:
2346                 if self.conv:
2347                     body += "    const VkBaseInStructure32 *in_header;\n"
2348                 else:
2349                     body += "    const VkBaseInStructure *in_header;\n"
2350                 body += "    VkBaseOutStructure *out_header = (void *)out;\n\n"
2351             else:
2352                 body += "    const VkBaseInStructure *in_header;\n"
2353                 if self.conv:
2354                     body += "    VkBaseOutStructure32 *out_header = (void *)out;\n\n"
2355                 else:
2356                     body += "    VkBaseOutStructure *out_header = (void *)out;\n\n"
2358         body += "    if (!in) return;\n\n"
2360         for m in self.operand:
2361             if not self.member_needs_copy(self.operand, m):
2362                 continue
2363             if m.name == "pNext" and (needs_extensions or self.conv):
2364                 body += "    out->pNext = NULL;\n"
2365                 continue
2367             if m.selection:
2368                 body += "    if ("
2369                 body += " || ".join("selector == {}".format(s) for s in m.selection)
2370                 body += ")\n    "
2372             body += "    " + m.copy("in->", "out->", self.direction, self.conv, self.unwrap)
2374         if needs_extensions:
2375             if self.conv and self.direction == Direction.INPUT:
2376                 body += "\n    for (in_header = UlongToPtr(in->pNext); in_header; in_header = UlongToPtr(in_header->pNext))\n"
2377             else:
2378                 body += "\n    for (in_header = (void *)in->pNext; in_header; in_header = (void *)in_header->pNext)\n"
2379             body += "    {\n"
2380             body += "        switch (in_header->sType)\n"
2381             body += "        {\n"
2383             ident = "            "
2385             if self.direction == Direction.INPUT and self.type in STRUCT_CHAIN_CONVERSIONS:
2386                 for i in STRUCT_CHAIN_CONVERSIONS[self.type]:
2387                     body += "        case {0}:\n".format(i)
2388                 body += ident + "break;\n"
2390             for ext in self.operand.struct_extensions:
2391                 if not ext.required:
2392                     continue
2394                 if self.direction == Direction.OUTPUT and not any([self.member_needs_copy(ext, m) for m in ext]):
2395                     continue
2397                 stype = next(x for x in ext.members if x.name == "sType").values
2398                 win_type = ext.name + "32" if self.conv and ext.needs_win32_type() else ext.name
2399                 if self.direction == Direction.INPUT:
2400                     in_type = "const " + win_type
2401                     out_type = ext.name
2402                 else:
2403                     in_type = "const " + ext.name
2404                     out_type = win_type
2406                 body += "        case {0}:\n".format(stype)
2407                 body += "        {\n"
2408                 if self.direction == Direction.INPUT:
2409                     body += ident + "{0} *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));\n".format(out_type)
2410                 elif self.conv:
2411                     body += ident + "{0} *out_ext = find_next_struct32(out_header, {1});\n".format(out_type, stype)
2412                 else:
2413                     body += ident + "{0} *out_ext = find_next_struct(out_header, {1});\n".format(out_type, stype)
2415                 copy_body = ""
2417                 for m in ext:
2418                     if m.name == "sType":
2419                         copy_body += ident + "out_ext->sType = {0};\n".format(stype)
2420                         continue
2421                     if not self.member_needs_copy(ext, m):
2422                         continue
2423                     if m.name == "pNext":
2424                         copy_body += ident + "out_ext->pNext = NULL;\n"
2425                         continue
2427                     copy_body += ident + m.copy("in_ext->", "out_ext->", self.direction, self.conv, True)
2429                 # Generate the definition of "in_ext" if we need it
2430                 if "in_ext->" in copy_body:
2431                     body += ident + "{0} *in_ext = ({0} *)in_header;\n".format(in_type)
2432                 body += copy_body
2434                 if self.direction == Direction.INPUT:
2435                     body += ident + "out_header->pNext = (void *)out_ext;\n"
2436                 body += ident + "out_header = (void *)out_ext;\n"
2437                 body += ident + "break;\n"
2438                 body += "        }\n"
2440             body += "        default:\n"
2441             if self.direction == Direction.INPUT:
2442                 body += ident + "FIXME(\"Unhandled sType %u.\\n\", in_header->sType);\n"
2443             body += "            break;\n"
2444             body += "        }\n"
2445             body += "    }\n"
2446         elif self.conv and self.direction == Direction.INPUT and "pNext" in self.operand:
2447             body += "    if (in->pNext)\n"
2448             body += "        FIXME(\"Unexpected pNext\\n\");\n"
2450         body += "}\n"
2451         if not self.conv:
2452             body += "#endif /* _WIN64 */\n"
2453         body += "\n"
2455         return body
2458 class ArrayConversionFunction(object):
2459     def __init__(self, array, direction, conv, unwrap):
2460         self.array = array
2461         self.direction = direction
2462         self.type = array.type
2463         self.conv = conv
2464         self.unwrap = unwrap or not array.needs_unwrapping()
2466         if array.is_static_array() and direction == Direction.INPUT:
2467             LOGGER.error("Static array input conversion is not supported")
2469         name = "convert_{0}_".format(array.type)
2470         if array.pointer_array:
2471             name += "pointer_"
2472         name += "array_"
2473         win_type = "win32" if self.conv else "win64"
2474         host_part = "host" if self.unwrap else "unwrapped_host"
2475         if self.direction == Direction.INPUT:
2476             name += "{0}_to_{1}".format(win_type, host_part)
2477         else: # Direction.OUTPUT
2478             name += "{0}_to_{1}".format(host_part, win_type)
2479         self.name = name
2481     def __eq__(self, other):
2482         return self.name == other.name
2484     def definition(self):
2485         """ Helper function for generating a conversion function for array operands. """
2487         body = ""
2489         if not self.conv:
2490             body += "#ifdef _WIN64\n"
2492         needs_alloc = self.direction != Direction.OUTPUT and self.array.needs_alloc(self.conv, self.unwrap)
2494         win_type = self.type
2495         if self.conv:
2496             if self.array.needs_win32_type():
2497                 win_type += "32"
2498             elif self.array.is_handle() and self.array.handle.is_dispatchable():
2499                 win_type = "PTR32"
2500         if self.direction == Direction.OUTPUT and self.array.is_const():
2501             win_type = "const " + win_type
2502         pointer_part = self.array.pointer if self.array.pointer else "*"
2504         if self.direction == Direction.OUTPUT:
2505             params = ["const {0} {1}in".format(self.type, pointer_part),
2506                       "{0} {1}out".format(win_type, pointer_part), "uint32_t count"]
2507             return_type = None
2508         elif self.conv and self.array.pointer_array:
2509             params = ["const PTR32 *in", "uint32_t count"]
2510             return_type = self.type
2511         else:
2512             params = ["const {0} {1}in".format(win_type, pointer_part), "uint32_t count"]
2513             return_type = self.type
2515         needs_copy = not self.array.is_struct() or self.direction != Direction.INPUT or \
2516             not self.array.struct.returnedonly or "pNext" in self.array.struct
2518         # Generate function prototype.
2519         if return_type:
2520             body += "static inline {0}{1} {2}{3}(".format(
2521                 "const " if self.array.is_const() else "", return_type, pointer_part, self.name)
2522         else:
2523             body += "static inline void {0}(".format(self.name)
2524         if needs_alloc:
2525             body += "struct conversion_context *ctx, "
2526         body += ", ".join(p for p in params)
2527         body += ")\n{\n"
2529         if return_type:
2530             body += "    {0} {1}out;\n".format(return_type, "**" if self.array.pointer_array else "*")
2531         if needs_copy:
2532             body += "    unsigned int i;\n\n"
2534         if return_type:
2535             body += "    if (!in || !count) return NULL;\n\n"
2536         else:
2537             body += "    if (!in) return;\n\n"
2539         if self.direction == Direction.INPUT:
2540             body += "    out = conversion_context_alloc(ctx, count * sizeof(*out));\n"
2542         if needs_copy:
2543             body += "    for (i = 0; i < count; i++)\n"
2544             body += "    {\n"
2546             if self.array.is_struct():
2547                 struct = self.array.struct
2548                 win_part = "win32" if self.conv else "win64"
2549                 host_part = "host" if self.unwrap else "unwrapped_host"
2550                 if self.direction == Direction.INPUT:
2551                     conv_suffix = "{0}_to_{1}".format(win_part, host_part)
2552                 else:
2553                     conv_suffix = "{0}_to_{1}".format(host_part, win_part)
2555                 ctx_part = ""
2556                 if self.direction == Direction.INPUT and struct.needs_alloc(self.conv, self.unwrap):
2557                     ctx_part = "ctx, "
2559                 if not self.array.pointer_array:
2560                     body += "        convert_{0}_{1}({2}&in[i], &out[i]);\n".format(
2561                         struct.name, conv_suffix, ctx_part)
2562                 else:
2563                     if struct.needs_conversion(self.conv, self.unwrap, self.direction, False):
2564                         body += "        if (in[i])\n"
2565                         body += "        {\n"
2566                         body += "            out[i] = conversion_context_alloc(ctx, sizeof(*out[i]));\n"
2567                         if self.conv:
2568                             in_param = "({0} *)UlongToPtr(in[i])".format(win_type)
2569                         else:
2570                             in_param = "in[i]"
2571                         body += "            convert_{0}_{1}({2}{3}, out[i]);\n".format(
2572                             struct.name, conv_suffix, ctx_part, in_param)
2573                         body += "        }\n"
2574                         body += "        else\n"
2575                         body += "            out[i] = NULL;\n"
2576                     else:
2577                         body += "        out[i] = UlongToPtr(in[i]);\n".format(win_type)
2578             elif self.array.is_handle():
2579                 if self.array.pointer_array:
2580                     LOGGER.error("Unhandled handle pointer arrays")
2581                 handle = self.array.handle
2582                 if not self.conv or not handle.is_dispatchable():
2583                     input = "in[i]"
2584                 elif self.direction == Direction.INPUT:
2585                     input = "UlongToPtr(in[i])"
2586                 else:
2587                     input = "PtrToUlong(in[i])"
2589                 if not self.unwrap or not handle.is_wrapped():
2590                     body += "        out[i] = {0};\n".format(input)
2591                 elif self.direction == Direction.INPUT:
2592                     body += "        out[i] = " + handle.driver_handle(input) + ";\n"
2593                 else:
2594                     LOGGER.warning("Unhandled handle output conversion")
2595             elif self.array.pointer_array:
2596                 body += "        out[i] = UlongToPtr(in[i]);\n"
2597             else:
2598                 body += "        out[i] = in[i];\n"
2600             body += "    }\n"
2602         if return_type:
2603             body += "\n    return {0}out;\n".format("(void *)" if self.array.pointer_array else "")
2604         body += "}\n"
2606         if not self.conv:
2607             body += "#endif /* _WIN64 */\n"
2609         body += "\n"
2611         return body
2614 class VkGenerator(object):
2615     def __init__(self, registry):
2616         self.registry = registry
2618         # Build a list conversion functions for struct conversion.
2619         self.conversions = []
2620         self.win32_structs = []
2621         for func in self.registry.funcs.values():
2622             if not func.needs_exposing():
2623                 continue
2625             conversions = func.get_conversions()
2626             for conv in conversions:
2627                 # Append if we don't already have this conversion.
2628                 if not any(c == conv for c in self.conversions):
2629                     self.conversions.append(conv)
2631                 if not isinstance(conv, StructConversionFunction):
2632                     continue
2634                 for e in conv.operand.struct_extensions:
2635                     if not e.required or not e.needs_win32_type():
2636                         continue
2637                     if not any(s.name == e.name for s in self.win32_structs):
2638                         self.win32_structs.append(e)
2640                 if not conv.operand.needs_win32_type():
2641                     continue
2643                 # Structs can be used in different ways by different conversions
2644                 # e.g. array vs non-array. Just make sure we pull in each struct once.
2645                 if not any(s.name == conv.operand.name for s in self.win32_structs):
2646                     self.win32_structs.append(conv.operand)
2648     def _generate_copyright(self, f, spec_file=False):
2649         f.write("# " if spec_file else "/* ")
2650         f.write("Automatically generated from Vulkan vk.xml; DO NOT EDIT!\n")
2651         lines = ["", "This file is generated from Vulkan vk.xml file covered",
2652             "by the following copyright and permission notice:"]
2653         lines.extend([l.rstrip(" ") for l in self.registry.copyright.splitlines()])
2654         for line in lines:
2655             f.write("{0}{1}".format("# " if spec_file else " * ", line).rstrip(" ") + "\n")
2656         f.write("\n" if spec_file else " */\n\n")
2658     def generate_thunks_c(self, f):
2659         self._generate_copyright(f)
2661         f.write("#if 0\n")
2662         f.write("#pragma makedep unix\n")
2663         f.write("#endif\n\n")
2665         f.write("#include \"config.h\"\n\n")
2667         f.write("#include <stdlib.h>\n\n")
2669         f.write("#include \"vulkan_private.h\"\n\n")
2671         f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2673         for struct in self.win32_structs:
2674             f.write(struct.definition(conv=True, align=True))
2675             f.write("\n")
2677         f.write("static uint64_t wine_vk_unwrap_handle(uint32_t type, uint64_t handle)\n")
2678         f.write("{\n")
2679         f.write("    switch(type)\n")
2680         f.write("    {\n")
2681         for handle in self.registry.handles:
2682             if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2683                 continue
2684             f.write("    case {}:\n".format(handle.object_type))
2685             if handle.is_dispatchable():
2686                 f.write("        return (uint64_t) (uintptr_t) ")
2687                 f.write(handle.native_handle("(({}) (uintptr_t) handle)".format(handle.name)))
2688             else:
2689                 f.write("        return (uint64_t) ")
2690                 f.write(handle.native_handle("handle"))
2691             f.write(";\n");
2692         f.write("    default:\n")
2693         f.write("       return handle;\n")
2694         f.write("    }\n")
2695         f.write("}\n\n")
2697         # Generate any conversion helper functions.
2698         for conv in self.conversions:
2699             f.write(conv.definition())
2701         # Create thunks for instance and device functions.
2702         # Global functions don't go through the thunks.
2703         for vk_func in self.registry.funcs.values():
2704             if not vk_func.needs_exposing():
2705                 continue
2706             if vk_func.loader_thunk_type == ThunkType.NONE:
2707                 continue
2709             f.write(vk_func.thunk(prefix="thunk64_"))
2710             f.write(vk_func.thunk(prefix="thunk32_", conv=True))
2712         # Create array of device extensions.
2713         f.write("static const char * const vk_device_extensions[] =\n{\n")
2714         for ext in self.registry.extensions:
2715             if ext["type"] != "device":
2716                 continue
2717             if ext["name"] in UNEXPOSED_EXTENSIONS:
2718                 continue
2720             f.write("    \"{0}\",\n".format(ext["name"]))
2721         f.write("};\n\n")
2723         # Create array of instance extensions.
2724         f.write("static const char * const vk_instance_extensions[] =\n{\n")
2725         for ext in self.registry.extensions:
2726             if ext["type"] != "instance":
2727                 continue
2728             if ext["name"] in UNEXPOSED_EXTENSIONS:
2729                 continue
2731             f.write("    \"{0}\",\n".format(ext["name"]))
2732         f.write("};\n\n")
2734         f.write("BOOL wine_vk_device_extension_supported(const char *name)\n")
2735         f.write("{\n")
2736         f.write("    unsigned int i;\n")
2737         f.write("    for (i = 0; i < ARRAY_SIZE(vk_device_extensions); i++)\n")
2738         f.write("    {\n")
2739         f.write("        if (strcmp(vk_device_extensions[i], name) == 0)\n")
2740         f.write("            return TRUE;\n")
2741         f.write("    }\n")
2742         f.write("    return FALSE;\n")
2743         f.write("}\n\n")
2745         f.write("BOOL wine_vk_instance_extension_supported(const char *name)\n")
2746         f.write("{\n")
2747         f.write("    unsigned int i;\n")
2748         f.write("    for (i = 0; i < ARRAY_SIZE(vk_instance_extensions); i++)\n")
2749         f.write("    {\n")
2750         f.write("        if (strcmp(vk_instance_extensions[i], name) == 0)\n")
2751         f.write("            return TRUE;\n")
2752         f.write("    }\n")
2753         f.write("    return FALSE;\n")
2754         f.write("}\n\n")
2756         f.write("BOOL wine_vk_is_type_wrapped(VkObjectType type)\n")
2757         f.write("{\n")
2758         f.write("    return FALSE")
2759         for handle in self.registry.handles:
2760             if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2761                 continue
2762             f.write(" ||\n        type == {}".format(handle.object_type))
2763         f.write(";\n")
2764         f.write("}\n\n")
2767         f.write("#ifdef _WIN64\n\n")
2769         f.write("const unixlib_entry_t __wine_unix_call_funcs[] =\n")
2770         f.write("{\n")
2771         f.write("    init_vulkan,\n")
2772         f.write("    vk_is_available_instance_function,\n")
2773         f.write("    vk_is_available_device_function,\n")
2774         for vk_func in self.registry.funcs.values():
2775             if not vk_func.needs_exposing():
2776                 continue
2777             if vk_func.loader_thunk_type == ThunkType.NONE:
2778                 continue
2780             if vk_func.is_perf_critical():
2781                 f.write("    (void *){1}{0},\n".format(vk_func.name, "thunk64_"))
2782             else:
2783                 f.write("    {1}{0},\n".format(vk_func.name, "thunk64_"))
2784         f.write("};\n")
2785         f.write("C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_count);\n\n")
2787         f.write("#endif /* _WIN64 */\n\n")
2789         f.write("#ifdef _WIN64\n")
2790         f.write("const unixlib_entry_t __wine_unix_call_wow64_funcs[] =\n")
2791         f.write("#else\n")
2792         f.write("const unixlib_entry_t __wine_unix_call_funcs[] =\n")
2793         f.write("#endif\n")
2794         f.write("{\n")
2795         f.write("    init_vulkan,\n")
2796         f.write("    vk_is_available_instance_function32,\n")
2797         f.write("    vk_is_available_device_function32,\n")
2798         for vk_func in self.registry.funcs.values():
2799             if not vk_func.needs_exposing():
2800                 continue
2801             if vk_func.loader_thunk_type == ThunkType.NONE:
2802                 continue
2804             if vk_func.is_perf_critical():
2805                 f.write("    (void *){1}{0},\n".format(vk_func.name, "thunk32_"))
2806             else:
2807                 f.write("    {1}{0},\n".format(vk_func.name, "thunk32_"))
2808         f.write("};\n")
2809         f.write("C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_count);\n")
2811     def generate_thunks_h(self, f, prefix):
2812         self._generate_copyright(f)
2814         f.write("#ifndef __WINE_VULKAN_THUNKS_H\n")
2815         f.write("#define __WINE_VULKAN_THUNKS_H\n\n")
2817         f.write("#define WINE_VK_VERSION VK_API_VERSION_{0}_{1}\n\n".format(WINE_VK_VERSION[0], WINE_VK_VERSION[1]))
2819         # Generate prototypes for device and instance functions requiring a custom implementation.
2820         f.write("/* Functions for which we have custom implementations outside of the thunks. */\n")
2821         for vk_func in self.registry.funcs.values():
2822             if not vk_func.needs_private_thunk():
2823                 continue
2825             f.write("{0};\n".format(vk_func.prototype(prefix=prefix, is_thunk=True)))
2826         f.write("\n")
2828         f.write("/* For use by vkDevice and children */\n")
2829         f.write("struct vulkan_device_funcs\n{\n")
2830         for vk_func in self.registry.device_funcs:
2831             if not vk_func.needs_exposing():
2832                 continue
2834             if not vk_func.needs_dispatch():
2835                 LOGGER.debug("skipping {0} in vulkan_device_funcs".format(vk_func.name))
2836                 continue
2838             f.write("    {0};\n".format(vk_func.pfn()))
2839         f.write("};\n\n")
2841         f.write("/* For use by vkInstance and children */\n")
2842         f.write("struct vulkan_instance_funcs\n{\n")
2843         for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs:
2844             if not vk_func.needs_exposing():
2845                 continue
2847             if not vk_func.needs_dispatch():
2848                 LOGGER.debug("skipping {0} in vulkan_instance_funcs".format(vk_func.name))
2849                 continue
2851             f.write("    {0};\n".format(vk_func.pfn()))
2852         f.write("};\n\n")
2854         f.write("#define ALL_VK_DEVICE_FUNCS() \\\n")
2855         first = True
2856         for vk_func in self.registry.device_funcs:
2857             if not vk_func.needs_exposing():
2858                 continue
2860             if not vk_func.needs_dispatch():
2861                 LOGGER.debug("skipping {0} in ALL_VK_DEVICE_FUNCS".format(vk_func.name))
2862                 continue
2864             if first:
2865                 f.write("    USE_VK_FUNC({0})".format(vk_func.name))
2866                 first = False
2867             else:
2868                 f.write(" \\\n    USE_VK_FUNC({0})".format(vk_func.name))
2869         f.write("\n\n")
2871         f.write("#define ALL_VK_INSTANCE_FUNCS() \\\n")
2872         first = True
2873         for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs:
2874             if not vk_func.needs_exposing():
2875                 continue
2877             if not vk_func.needs_dispatch():
2878                 LOGGER.debug("skipping {0} in ALL_VK_INSTANCE_FUNCS".format(vk_func.name))
2879                 continue
2881             if first:
2882                 f.write("    USE_VK_FUNC({0})".format(vk_func.name))
2883                 first = False
2884             else:
2885                 f.write(" \\\n    USE_VK_FUNC({0})".format(vk_func.name))
2886         f.write("\n\n")
2888         f.write("#endif /* __WINE_VULKAN_THUNKS_H */\n")
2890     def generate_loader_thunks_c(self, f):
2891         self._generate_copyright(f)
2893         f.write("#include \"vulkan_loader.h\"\n\n")
2895         f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2897         for vk_func in self.registry.funcs.values():
2898             if not vk_func.needs_exposing():
2899                 continue
2900             if vk_func.loader_thunk_type != ThunkType.PUBLIC:
2901                 continue
2903             f.write(vk_func.loader_thunk())
2905         f.write("static const struct vulkan_func vk_device_dispatch_table[] =\n{\n")
2906         for vk_func in self.registry.device_funcs:
2907             if not vk_func.needs_exposing():
2908                 continue
2910             f.write("    {{\"{0}\", {0}}},\n".format(vk_func.name))
2911         f.write("};\n\n")
2913         f.write("static const struct vulkan_func vk_phys_dev_dispatch_table[] =\n{\n")
2914         for vk_func in self.registry.phys_dev_funcs:
2915             if not vk_func.needs_exposing():
2916                 continue
2918             f.write("    {{\"{0}\", {0}}},\n".format(vk_func.name))
2919         f.write("};\n\n")
2921         f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
2922         for vk_func in self.registry.instance_funcs:
2923             if not vk_func.needs_exposing():
2924                 continue
2926             f.write("    {{\"{0}\", {0}}},\n".format(vk_func.name))
2927         f.write("};\n\n")
2929         f.write("void *wine_vk_get_device_proc_addr(const char *name)\n")
2930         f.write("{\n")
2931         f.write("    unsigned int i;\n")
2932         f.write("    for (i = 0; i < ARRAY_SIZE(vk_device_dispatch_table); i++)\n")
2933         f.write("    {\n")
2934         f.write("        if (strcmp(vk_device_dispatch_table[i].name, name) == 0)\n")
2935         f.write("        {\n")
2936         f.write("            TRACE(\"Found name=%s in device table\\n\", debugstr_a(name));\n")
2937         f.write("            return vk_device_dispatch_table[i].func;\n")
2938         f.write("        }\n")
2939         f.write("    }\n")
2940         f.write("    return NULL;\n")
2941         f.write("}\n\n")
2943         f.write("void *wine_vk_get_phys_dev_proc_addr(const char *name)\n")
2944         f.write("{\n")
2945         f.write("    unsigned int i;\n")
2946         f.write("    for (i = 0; i < ARRAY_SIZE(vk_phys_dev_dispatch_table); i++)\n")
2947         f.write("    {\n")
2948         f.write("        if (strcmp(vk_phys_dev_dispatch_table[i].name, name) == 0)\n")
2949         f.write("        {\n")
2950         f.write("            TRACE(\"Found name=%s in physical device table\\n\", debugstr_a(name));\n")
2951         f.write("            return vk_phys_dev_dispatch_table[i].func;\n")
2952         f.write("        }\n")
2953         f.write("    }\n")
2954         f.write("    return NULL;\n")
2955         f.write("}\n\n")
2957         f.write("void *wine_vk_get_instance_proc_addr(const char *name)\n")
2958         f.write("{\n")
2959         f.write("    unsigned int i;\n")
2960         f.write("    for (i = 0; i < ARRAY_SIZE(vk_instance_dispatch_table); i++)\n")
2961         f.write("    {\n")
2962         f.write("        if (strcmp(vk_instance_dispatch_table[i].name, name) == 0)\n")
2963         f.write("        {\n")
2964         f.write("            TRACE(\"Found name=%s in instance table\\n\", debugstr_a(name));\n")
2965         f.write("            return vk_instance_dispatch_table[i].func;\n")
2966         f.write("        }\n")
2967         f.write("    }\n")
2968         f.write("    return NULL;\n")
2969         f.write("}\n")
2971     def generate_loader_thunks_h(self, f):
2972         self._generate_copyright(f)
2974         f.write("#ifndef __WINE_VULKAN_LOADER_THUNKS_H\n")
2975         f.write("#define __WINE_VULKAN_LOADER_THUNKS_H\n\n")
2977         f.write("enum unix_call\n")
2978         f.write("{\n")
2979         f.write("    unix_init,\n")
2980         f.write("    unix_is_available_instance_function,\n")
2981         f.write("    unix_is_available_device_function,\n")
2982         for vk_func in self.registry.funcs.values():
2983             if not vk_func.needs_exposing():
2984                 continue
2985             if vk_func.loader_thunk_type == ThunkType.NONE:
2986                 continue
2988             f.write("    unix_{0},\n".format(vk_func.name))
2989         f.write("    unix_count,\n")
2990         f.write("};\n\n")
2992         for vk_func in self.registry.funcs.values():
2993             if not vk_func.needs_exposing():
2994                 continue
2995             if vk_func.loader_thunk_type == ThunkType.NONE:
2996                 continue
2998             f.write("struct {0}_params\n".format(vk_func.name))
2999             f.write("{\n");
3000             for p in vk_func.params:
3001                 f.write("    {0};\n".format(p.definition(is_member=True)))
3002             if vk_func.extra_param:
3003                 f.write("    void *{0};\n".format(vk_func.extra_param))
3004             if vk_func.type != "void":
3005                 f.write("    {0} result;\n".format(vk_func.type))
3006             f.write("};\n\n");
3008         f.write("#endif /* __WINE_VULKAN_LOADER_THUNKS_H */\n")
3010     def generate_vulkan_h(self, f):
3011         self._generate_copyright(f)
3012         f.write("#ifndef __WINE_VULKAN_H\n")
3013         f.write("#define __WINE_VULKAN_H\n\n")
3015         f.write("#include <windef.h>\n")
3016         f.write("#include <stdint.h>\n\n")
3018         f.write("/* Define WINE_VK_HOST to get 'host' headers. */\n")
3019         f.write("#ifdef WINE_VK_HOST\n")
3020         f.write("#define VKAPI_CALL\n")
3021         f.write('#define WINE_VK_ALIGN(x)\n')
3022         f.write("#endif\n\n")
3024         f.write("#ifndef VKAPI_CALL\n")
3025         f.write("#define VKAPI_CALL __stdcall\n")
3026         f.write("#endif\n\n")
3028         f.write("#ifndef VKAPI_PTR\n")
3029         f.write("#define VKAPI_PTR VKAPI_CALL\n")
3030         f.write("#endif\n\n")
3032         f.write("#ifndef WINE_VK_ALIGN\n")
3033         f.write("#define WINE_VK_ALIGN DECLSPEC_ALIGN\n")
3034         f.write("#endif\n\n")
3036         # The overall strategy is to define independent constants and datatypes,
3037         # prior to complex structures and function calls to avoid forward declarations.
3038         for const in self.registry.consts:
3039             # For now just generate things we may not need. The amount of parsing needed
3040             # to get some of the info is tricky as you need to figure out which structure
3041             # references a certain constant.
3042             f.write(const.definition())
3043         f.write("\n")
3045         for define in self.registry.defines:
3046             f.write(define.definition())
3048         for handle in self.registry.handles:
3049             # For backward compatibility also create definitions for aliases.
3050             # These types normally don't get pulled in as we use the new types
3051             # even in legacy functions if they are aliases.
3052             if handle.is_required() or handle.is_alias():
3053                  f.write(handle.definition())
3054         f.write("\n")
3056         for base_type in self.registry.base_types:
3057             f.write(base_type.definition())
3058         f.write("\n")
3060         for bitmask in self.registry.bitmasks:
3061             f.write(bitmask.definition())
3062         f.write("\n")
3064         # Define enums, this includes values for some of the bitmask types as well.
3065         for enum in self.registry.enums.values():
3066             if enum.required:
3067                 f.write(enum.definition())
3069         for fp in self.registry.funcpointers:
3070             if fp.required:
3071                 f.write(fp.definition())
3072         f.write("\n")
3074         # This generates both structures and unions. Since structures
3075         # may depend on other structures/unions, we need a list of
3076         # decoupled structs.
3077         # Note: unions are stored in structs for dependency reasons,
3078         # see comment in parsing section.
3079         structs = VkStruct.decouple_structs(self.registry.structs)
3080         for struct in structs:
3081             LOGGER.debug("Generating struct: {0}".format(struct.name))
3082             f.write(struct.definition(align=True))
3083             f.write("\n")
3085         for func in self.registry.funcs.values():
3086             if not func.is_required():
3087                 LOGGER.debug("Skipping PFN definition for: {0}".format(func.name))
3088                 continue
3090             f.write("typedef {0};\n".format(func.pfn(prefix="PFN", call_conv="VKAPI_PTR")))
3091         f.write("\n")
3093         f.write("#ifndef VK_NO_PROTOTYPES\n")
3094         for func in self.registry.funcs.values():
3095             if not func.is_required():
3096                 LOGGER.debug("Skipping API definition for: {0}".format(func.name))
3097                 continue
3099             LOGGER.debug("Generating API definition for: {0}".format(func.name))
3100             f.write("{0};\n".format(func.prototype(call_conv="VKAPI_CALL")))
3101         f.write("#endif /* VK_NO_PROTOTYPES */\n\n")
3103         f.write("#endif /* __WINE_VULKAN_H */\n")
3105     def generate_vulkan_driver_h(self, f):
3106         self._generate_copyright(f)
3107         f.write("#ifndef __WINE_VULKAN_DRIVER_H\n")
3108         f.write("#define __WINE_VULKAN_DRIVER_H\n\n")
3110         f.write("/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */\n")
3111         f.write("#define WINE_VULKAN_DRIVER_VERSION {0}\n\n".format(DRIVER_VERSION))
3113         f.write("struct vulkan_funcs\n{\n")
3114         f.write("    /* Vulkan global functions. These are the only calls at this point a graphics driver\n")
3115         f.write("     * needs to provide. Other function calls will be provided indirectly by dispatch\n")
3116         f.write("     * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice.\n")
3117         f.write("     */\n")
3119         for vk_func in self.registry.funcs.values():
3120             if not vk_func.is_driver_func():
3121                 continue
3123             pfn = vk_func.pfn()
3124             # Avoid PFN_vkVoidFunction in driver interface as Vulkan likes to put calling convention
3125             # stuff in there. For simplicity substitute with "void *".
3126             pfn = pfn.replace("PFN_vkVoidFunction", "void *")
3127             f.write("    {0};\n".format(pfn))
3129         f.write("\n")
3130         f.write("    /* winevulkan specific functions */\n")
3131         f.write("    VkSurfaceKHR (*p_wine_get_native_surface)(VkSurfaceKHR);\n")
3132         f.write("};\n\n")
3134         f.write("static inline void *get_vulkan_driver_device_proc_addr(\n")
3135         f.write("        const struct vulkan_funcs *vulkan_funcs, const char *name)\n{\n")
3136         f.write("    if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
3137         f.write("    name += 2;\n\n")
3138         for vk_func in self.registry.funcs.values():
3139             if vk_func.is_driver_func() and vk_func.is_device_func():
3140                 f.write('    if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3141                 f.write('        return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3142         f.write("\n")
3143         f.write("    return NULL;\n}\n\n")
3145         f.write("static inline void *get_vulkan_driver_instance_proc_addr(\n")
3146         f.write("        const struct vulkan_funcs *vulkan_funcs, VkInstance instance, const char *name)\n{\n")
3147         f.write("    if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
3148         f.write("    name += 2;\n\n")
3149         for vk_func in self.registry.funcs.values():
3150             if vk_func.is_driver_func() and vk_func.is_global_func() and vk_func.name != "vkGetInstanceProcAddr":
3151                 f.write('    if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3152                 f.write('        return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3153         f.write("\n")
3154         f.write("    if (!instance) return NULL;\n\n")
3155         for vk_func in self.registry.funcs.values():
3156             if vk_func.is_driver_func() and (vk_func.is_instance_func() or vk_func.is_phys_dev_func()):
3157                 f.write('    if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3158                 f.write('        return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3159         f.write("\n")
3160         f.write("    name -= 2;\n\n")
3161         f.write("    return get_vulkan_driver_device_proc_addr(vulkan_funcs, name);\n}\n\n")
3163         f.write("#endif /* __WINE_VULKAN_DRIVER_H */\n")
3165     def generate_vulkan_spec(self, f):
3166         self._generate_copyright(f, spec_file=True)
3167         f.write("@ stdcall -private vk_icdGetInstanceProcAddr(ptr str)\n")
3168         f.write("@ stdcall -private vk_icdGetPhysicalDeviceProcAddr(ptr str)\n")
3169         f.write("@ stdcall -private vk_icdNegotiateLoaderICDInterfaceVersion(ptr)\n")
3171         # Export symbols for all Vulkan Core functions.
3172         for func in self.registry.funcs.values():
3173             if not func.is_core_func():
3174                 continue
3176             # We support all Core functions except for VK_KHR_display* APIs.
3177             # Create stubs for unsupported Core functions.
3178             if func.is_required():
3179                 f.write(func.spec())
3180             else:
3181                 f.write("@ stub {0}\n".format(func.name))
3183         f.write("@ stdcall -private DllRegisterServer()\n")
3184         f.write("@ stdcall -private DllUnregisterServer()\n")
3186     def generate_vulkan_loader_spec(self, f):
3187         self._generate_copyright(f, spec_file=True)
3189         # Export symbols for all Vulkan Core functions.
3190         for func in self.registry.funcs.values():
3191             if not func.is_core_func():
3192                 continue
3194             # We support all Core functions except for VK_KHR_display* APIs.
3195             # Create stubs for unsupported Core functions.
3196             if func.is_required():
3197                 f.write(func.spec(symbol="winevulkan." + func.name))
3198             else:
3199                 f.write("@ stub {0}\n".format(func.name))
3202 class VkRegistry(object):
3203     def __init__(self, reg_filename):
3204         # Used for storage of type information.
3205         self.base_types = None
3206         self.bitmasks = None
3207         self.consts = None
3208         self.defines = None
3209         self.enums = None
3210         self.funcpointers = None
3211         self.handles = None
3212         self.structs = None
3214         # We aggregate all types in here for cross-referencing.
3215         self.funcs = {}
3216         self.types = {}
3218         self.version_regex = re.compile(
3219             r'^'
3220             r'VK_VERSION_'
3221             r'(?P<major>[0-9])'
3222             r'_'
3223             r'(?P<minor>[0-9])'
3224             r'$'
3225         )
3227         # Overall strategy for parsing the registry is to first
3228         # parse all type / function definitions. Then parse
3229         # features and extensions to decide which types / functions
3230         # to actually 'pull in' for code generation. For each type or
3231         # function call we want we set a member 'required' to True.
3232         tree = ET.parse(reg_filename)
3233         root = tree.getroot()
3234         self._parse_enums(root)
3235         self._parse_types(root)
3236         self._parse_commands(root)
3238         # Pull in any required types and functions.
3239         self._parse_features(root)
3240         self._parse_extensions(root)
3242         for enum in self.enums.values():
3243             enum.fixup_64bit_aliases()
3245         self._match_object_types()
3247         self.copyright = root.find('./comment').text
3249     def _is_feature_supported(self, feature):
3250         version = self.version_regex.match(feature)
3251         if not version:
3252             return True
3254         version = tuple(map(int, version.group('major', 'minor')))
3255         return version <= WINE_VK_VERSION
3257     def _is_extension_supported(self, extension):
3258         # We disable some extensions as either we haven't implemented
3259         # support yet or because they are for platforms other than win32.
3260         return extension not in UNSUPPORTED_EXTENSIONS
3262     def _mark_command_required(self, command):
3263         """ Helper function to mark a certain command and the datatypes it needs as required."""
3264         def mark_bitmask_dependencies(bitmask, types):
3265             if bitmask.requires is not None:
3266                 types[bitmask.requires]["data"].required = True
3268         def mark_funcpointer_dependencies(fp, types):
3269             for m in fp.members:
3270                 type_info = types[m.type]
3272                 # Complex types have a matching definition e.g. VkStruct.
3273                 # Not needed for base types such as uint32_t.
3274                 if "data" in type_info:
3275                     types[m.type]["data"].required = True
3277         def mark_struct_dependencies(struct, types):
3278              for m in struct:
3279                 type_info = types[m.type]
3281                 # Complex types have a matching definition e.g. VkStruct.
3282                 # Not needed for base types such as uint32_t.
3283                 if "data" in type_info:
3284                     types[m.type]["data"].required = True
3286                 if type_info["category"] == "struct" and struct.name != m.type:
3287                     # Yay, recurse
3288                     mark_struct_dependencies(type_info["data"], types)
3289                 elif type_info["category"] == "funcpointer":
3290                     mark_funcpointer_dependencies(type_info["data"], types)
3291                 elif type_info["category"] == "bitmask":
3292                     mark_bitmask_dependencies(type_info["data"], types)
3294         func = self.funcs[command]
3295         func.required = True
3297         # Pull in return type
3298         if func.type != "void":
3299             self.types[func.type]["data"].required = True
3301         # Analyze parameter dependencies and pull in any type needed.
3302         for p in func.params:
3303             type_info = self.types[p.type]
3305             # Check if we are dealing with a complex type e.g. VkEnum, VkStruct and others.
3306             if "data" not in type_info:
3307                 continue
3309             # Mark the complex type as required.
3310             type_info["data"].required = True
3311             if type_info["category"] == "struct":
3312                 struct = type_info["data"]
3313                 mark_struct_dependencies(struct, self.types)
3314             elif type_info["category"] == "bitmask":
3315                 mark_bitmask_dependencies(type_info["data"], self.types)
3317     def _match_object_types(self):
3318         """ Matches each handle with the correct object type. """
3319         # Use upper case comparison for simplicity.
3320         object_types = {}
3321         for value in self.enums["VkObjectType"].values:
3322             object_name = "VK" + value.name[len("VK_OBJECT_TYPE"):].replace("_", "")
3323             object_types[object_name] = value.name
3325         for handle in self.handles:
3326             if not handle.is_required():
3327                 continue
3328             handle.object_type = object_types.get(handle.name.upper())
3329             if not handle.object_type:
3330                 LOGGER.warning("No object type found for {}".format(handle.name))
3332     def _parse_commands(self, root):
3333         """ Parse command section containing the Vulkan function calls. """
3334         funcs = {}
3335         commands = root.findall("./commands/")
3337         # As of Vulkan 1.1, various extensions got promoted to Core.
3338         # The old commands (e.g. KHR) are available for backwards compatibility
3339         # and are marked in vk.xml as 'alias' to the non-extension type.
3340         # The registry likes to avoid data duplication, so parameters and other
3341         # metadata need to be looked up from the Core command.
3342         # We parse the alias commands in a second pass.
3343         alias_commands = []
3344         for command in commands:
3345             alias_name = command.attrib.get("alias")
3346             if alias_name:
3347                 alias_commands.append(command)
3348                 continue
3350             func = VkFunction.from_xml(command, self.types)
3351             if func:
3352                 funcs[func.name] = func
3354         for command in alias_commands:
3355             alias_name = command.attrib.get("alias")
3356             alias = funcs[alias_name]
3357             func = VkFunction.from_alias(command, alias)
3358             if func:
3359                 funcs[func.name] = func
3361         # To make life easy for the code generation, separate all function
3362         # calls out in the 4 types of Vulkan functions:
3363         # device, global, physical device and instance.
3364         device_funcs = []
3365         global_funcs = []
3366         phys_dev_funcs = []
3367         instance_funcs = []
3368         for func in funcs.values():
3369             if func.is_device_func():
3370                 device_funcs.append(func)
3371             elif func.is_global_func():
3372                 global_funcs.append(func)
3373             elif func.is_phys_dev_func():
3374                 phys_dev_funcs.append(func)
3375             else:
3376                 instance_funcs.append(func)
3378         # Sort function lists by name and store them.
3379         self.device_funcs = sorted(device_funcs, key=lambda func: func.name)
3380         self.global_funcs = sorted(global_funcs, key=lambda func: func.name)
3381         self.phys_dev_funcs = sorted(phys_dev_funcs, key=lambda func: func.name)
3382         self.instance_funcs = sorted(instance_funcs, key=lambda func: func.name)
3384         # The funcs dictionary is used as a convenient way to lookup function
3385         # calls when needed e.g. to adjust member variables.
3386         self.funcs = OrderedDict(sorted(funcs.items()))
3388     def _parse_enums(self, root):
3389         """ Parse enums section or better described as constants section. """
3390         enums = {}
3391         self.consts = []
3392         for enum in root.findall("./enums"):
3393             name = enum.attrib.get("name")
3394             _type = enum.attrib.get("type")
3396             if _type in ("enum", "bitmask"):
3397                 enum_obj = VkEnum.from_xml(enum)
3398                 if enum_obj:
3399                     enums[name] = enum_obj
3400             else:
3401                 # If no type is set, we are dealing with API constants.
3402                 for value in enum.findall("enum"):
3403                     # If enum is an alias, set the value to the alias name.
3404                     # E.g. VK_LUID_SIZE_KHR is an alias to VK_LUID_SIZE.
3405                     alias = value.attrib.get("alias")
3406                     if alias:
3407                         self.consts.append(VkConstant(value.attrib.get("name"), alias))
3408                     else:
3409                         self.consts.append(VkConstant(value.attrib.get("name"), value.attrib.get("value")))
3411         self.enums = OrderedDict(sorted(enums.items()))
3413     def _process_require_enum(self, enum_elem, ext=None, only_aliased=False):
3414         if "extends" in enum_elem.keys():
3415             enum = self.types[enum_elem.attrib["extends"]]["data"]
3417             # Need to define VkEnumValues which were aliased to by another value. This is necessary
3418             # from VK spec version 1.2.135 where the provisional VK_KHR_ray_tracing extension was
3419             # added which altered VK_NV_ray_tracing's VkEnumValues to alias to the provisional
3420             # extension.
3421             aliased = False
3422             for _, t in self.types.items():
3423                 if t["category"] != "enum":
3424                     continue
3425                 if not t["data"]:
3426                     continue
3427                 for value in t["data"].values:
3428                     if value.alias == enum_elem.attrib["name"]:
3429                         aliased = True
3431             if only_aliased and not aliased:
3432                 return
3434             if "bitpos" in enum_elem.keys():
3435                 # We need to add an extra value to an existing enum type.
3436                 # E.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG to VkFormatFeatureFlagBits.
3437                 enum.create_bitpos(enum_elem.attrib["name"], int(enum_elem.attrib["bitpos"]))
3439             elif "offset" in enum_elem.keys():
3440                 # Extensions promoted to Core, have the extension number as part
3441                 # of the enum value. Else retrieve from the extension tag.
3442                 if enum_elem.attrib.get("extnumber"):
3443                     ext_number = int(enum_elem.attrib.get("extnumber"))
3444                 else:
3445                     ext_number = int(ext.attrib["number"])
3446                 offset = int(enum_elem.attrib["offset"])
3447                 value = EXT_BASE + (ext_number - 1) * EXT_BLOCK_SIZE + offset
3449                 # Deal with negative values.
3450                 direction = enum_elem.attrib.get("dir")
3451                 if direction is not None:
3452                     value = -value
3454                 enum.create_value(enum_elem.attrib["name"], str(value))
3456             elif "value" in enum_elem.keys():
3457                 enum.create_value(enum_elem.attrib["name"], enum_elem.attrib["value"])
3458             elif "alias" in enum_elem.keys():
3459                 enum.create_alias(enum_elem.attrib["name"], enum_elem.attrib["alias"])
3461         elif "value" in enum_elem.keys():
3462             # Constant with an explicit value
3463             if only_aliased:
3464                 return
3466             self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["value"]))
3467         elif "alias" in enum_elem.keys():
3468             # Aliased constant
3469             if not only_aliased:
3470                 return
3472             self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["alias"]))
3474     @staticmethod
3475     def _require_type(type_info):
3476         if type_info.is_alias():
3477             type_info = type_info.alias
3478         type_info.required = True
3479         if type(type_info) == VkStruct:
3480             for member in type_info.members:
3481                 if "data" in member.type_info:
3482                   VkRegistry._require_type(member.type_info["data"])
3484     def _parse_extensions(self, root):
3485         """ Parse extensions section and pull in any types and commands for this extension. """
3486         extensions = []
3487         exts = root.findall("./extensions/extension")
3488         deferred_exts = []
3489         skipped_exts = UNSUPPORTED_EXTENSIONS.copy()
3491         def process_ext(ext, deferred=False):
3492             ext_name = ext.attrib["name"]
3494             # Set extension name on any functions calls part of this extension as we
3495             # were not aware of the name during initial parsing.
3496             commands = ext.findall("require/command")
3497             for command in commands:
3498                 cmd_name = command.attrib["name"]
3499                 # Need to verify that the command is defined, and otherwise skip it.
3500                 # vkCreateScreenSurfaceQNX is declared in <extensions> but not defined in
3501                 # <commands>. A command without a definition cannot be enabled, so it's valid for
3502                 # the XML file to handle this, but because of the manner in which we parse the XML
3503                 # file we pre-populate from <commands> before we check if a command is enabled.
3504                 if cmd_name in self.funcs:
3505                     self.funcs[cmd_name].extensions.add(ext_name)
3507             # Some extensions are not ready or have numbers reserved as a place holder
3508             # or are only supported for VulkanSC.
3509             if not "vulkan" in ext.attrib["supported"].split(","):
3510                 LOGGER.debug("Skipping disabled extension: {0}".format(ext_name))
3511                 skipped_exts.append(ext_name)
3512                 return
3514             # Defer extensions with 'sortorder' as they are order-dependent for spec-parsing.
3515             if not deferred and "sortorder" in ext.attrib:
3516                 deferred_exts.append(ext)
3517                 return
3519             # Disable highly experimental extensions as the APIs are unstable and can
3520             # change between minor Vulkan revisions until API is final and becomes KHR
3521             # or NV.
3522             if ("KHX" in ext_name or "NVX" in ext_name) and ext_name not in ALLOWED_X_EXTENSIONS:
3523                 LOGGER.debug("Skipping experimental extension: {0}".format(ext_name))
3524                 skipped_exts.append(ext_name)
3525                 return
3527             # Extensions can define VkEnumValues which alias to provisional extensions. Pre-process
3528             # extensions to define any required VkEnumValues before the platform check below.
3529             for require in ext.findall("require"):
3530                 # Extensions can add enum values to Core / extension enums, so add these.
3531                 for enum_elem in require.findall("enum"):
3532                     self._process_require_enum(enum_elem, ext, only_aliased=True)
3534             platform = ext.attrib.get("platform")
3535             if platform and platform != "win32":
3536                 LOGGER.debug("Skipping extensions {0} for platform {1}".format(ext_name, platform))
3537                 skipped_exts.append(ext_name)
3538                 return
3540             if not self._is_extension_supported(ext_name):
3541                 LOGGER.debug("Skipping unsupported extension: {0}".format(ext_name))
3542                 skipped_exts.append(ext_name)
3543                 return
3544             elif "requires" in ext.attrib:
3545                 # Check if this extension builds on top of another unsupported extension.
3546                 requires = ext.attrib["requires"].split(",")
3547                 if len(set(requires).intersection(skipped_exts)) > 0:
3548                     skipped_exts.append(ext_name)
3549                     return
3550             elif "depends" in ext.attrib:
3551                 # The syntax for this is more complex, but this is good enough for now.
3552                 if any([sext in ext.attrib["depends"] for sext in skipped_exts]):
3553                     skipped_exts.append(ext_name)
3554                     return
3556             LOGGER.debug("Loading extension: {0}".format(ext_name))
3558             # Extensions can define one or more require sections each requiring
3559             # different features (e.g. Vulkan 1.1). Parse each require section
3560             # separately, so we can skip sections we don't want.
3561             for require in ext.findall("require"):
3562                 # Extensions can add enum values to Core / extension enums, so add these.
3563                 for enum_elem in require.findall("enum"):
3564                     self._process_require_enum(enum_elem, ext)
3566                 for t in require.findall("type"):
3567                     type_info = self.types[t.attrib["name"]]["data"]
3568                     self._require_type(type_info)
3569                 feature = require.attrib.get("feature")
3570                 if feature and not self._is_feature_supported(feature):
3571                     continue
3573                 required_extension = require.attrib.get("extension")
3574                 if required_extension and not self._is_extension_supported(required_extension):
3575                     continue
3577                 # Pull in any commands we need. We infer types to pull in from the command
3578                 # as well.
3579                 for command in require.findall("command"):
3580                     cmd_name = command.attrib["name"]
3581                     self._mark_command_required(cmd_name)
3584             # Store a list with extensions.
3585             ext_info = {"name" : ext_name, "type" : ext.attrib["type"]}
3586             extensions.append(ext_info)
3589         # Process extensions, allowing for sortorder to defer extension processing
3590         for ext in exts:
3591             process_ext(ext)
3593         deferred_exts.sort(key=lambda ext: ext.attrib["sortorder"])
3595         # Respect sortorder
3596         for ext in deferred_exts:
3597             process_ext(ext, deferred=True)
3599         # Sort in alphabetical order.
3600         self.extensions = sorted(extensions, key=lambda ext: ext["name"])
3602     def _parse_features(self, root):
3603         """ Parse the feature section, which describes Core commands and types needed. """
3605         for feature in root.findall("./feature"):
3606             if not api_is_vulkan(feature):
3607                 continue
3608             feature_name = feature.attrib["name"]
3609             for require in feature.findall("require"):
3610                 LOGGER.info("Including features for {0}".format(require.attrib.get("comment")))
3611                 for tag in require:
3612                     if tag.tag == "comment":
3613                         continue
3614                     elif tag.tag == "command":
3615                         if not self._is_feature_supported(feature_name):
3616                             continue
3617                         name = tag.attrib["name"]
3618                         self._mark_command_required(name)
3619                     elif tag.tag == "enum":
3620                         self._process_require_enum(tag)
3621                     elif tag.tag == "type":
3622                         name = tag.attrib["name"]
3624                         # Skip pull in for vk_platform.h for now.
3625                         if name == "vk_platform":
3626                             continue
3628                         type_info = self.types[name]
3629                         type_info["data"].required = True
3631     def _parse_types(self, root):
3632         """ Parse types section, which contains all data types e.g. structs, typedefs etcetera. """
3633         types = root.findall("./types/type")
3635         base_types = []
3636         bitmasks = []
3637         defines = []
3638         funcpointers = []
3639         handles = []
3640         structs = []
3642         alias_types = []
3643         for t in types:
3644             type_info = {}
3645             type_info["category"] = t.attrib.get("category", None)
3646             type_info["requires"] = t.attrib.get("requires", None)
3648             # We parse aliases in a second pass when we know more.
3649             alias = t.attrib.get("alias")
3650             if alias:
3651                 LOGGER.debug("Alias found: {0}".format(alias))
3652                 alias_types.append(t)
3653                 continue
3655             if type_info["category"] in ["include"]:
3656                 continue
3658             if type_info["category"] == "basetype":
3659                 name = t.find("name").text
3660                 _type = None
3661                 if not t.find("type") is None:
3662                     _type = t.find("type").text
3663                     tail = t.find("type").tail
3664                     if tail is not None:
3665                         _type += tail.strip()
3666                 basetype = VkBaseType(name, _type)
3667                 if basetype:
3668                     base_types.append(basetype)
3669                     type_info["data"] = basetype
3670                 else:
3671                     continue
3673             # Basic C types don't need us to define them, but we do need data for them
3674             if type_info["requires"] == "vk_platform":
3675                 requires = type_info["requires"]
3676                 basic_c = VkBaseType(name, _type, requires=requires)
3677                 type_info["data"] = basic_c
3679             if type_info["category"] == "bitmask":
3680                 name = t.find("name").text
3681                 _type = t.find("type").text
3683                 # Most bitmasks have a requires attribute used to pull in
3684                 # required '*FlagBits" enum.
3685                 requires = type_info["requires"]
3686                 bitmask = VkBaseType(name, _type, requires=requires)
3687                 bitmasks.append(bitmask)
3688                 type_info["data"] = bitmask
3690             if type_info["category"] == "define":
3691                 define = VkDefine.from_xml(t)
3692                 if define:
3693                     defines.append(define)
3694                     type_info["data"] = define
3695                 else:
3696                     continue
3698             if type_info["category"] == "enum":
3699                 name = t.attrib.get("name")
3700                 # The type section only contains enum names, not the actual definition.
3701                 # Since we already parsed the enum before, just link it in.
3702                 try:
3703                     type_info["data"] = self.enums[name]
3704                 except KeyError as e:
3705                     # Not all enums seem to be defined yet, typically that's for
3706                     # ones ending in 'FlagBits' where future extensions may add
3707                     # definitions.
3708                     type_info["data"] = None
3710             if type_info["category"] == "funcpointer":
3711                 funcpointer = VkFunctionPointer.from_xml(t)
3712                 if funcpointer:
3713                     funcpointers.append(funcpointer)
3714                     type_info["data"] = funcpointer
3715                 else:
3716                     continue
3718             if type_info["category"] == "handle":
3719                 handle = VkHandle.from_xml(t)
3720                 if handle:
3721                     handles.append(handle)
3722                     type_info["data"] = handle
3723                 else:
3724                     continue
3726             if type_info["category"] in ["struct", "union"]:
3727                 # We store unions among structs as some structs depend
3728                 # on unions. The types are very similar in parsing and
3729                 # generation anyway. The official Vulkan scripts use
3730                 # a similar kind of hack.
3731                 struct = VkStruct.from_xml(t)
3732                 if struct:
3733                     structs.append(struct)
3734                     type_info["data"] = struct
3735                 else:
3736                     continue
3738             # Name is in general within a name tag else it is an optional
3739             # attribute on the type tag.
3740             name_elem = t.find("name")
3741             if name_elem is not None:
3742                 type_info["name"] = name_elem.text
3743             else:
3744                 type_info["name"] = t.attrib.get("name", None)
3746             # Store all type data in a shared dictionary, so we can easily
3747             # look up information for a given type. There are no duplicate
3748             # names.
3749             self.types[type_info["name"]] = type_info
3751         # Second pass for alias types, so we can retrieve all data from
3752         # the aliased object.
3753         for t in alias_types:
3754             type_info = {}
3755             type_info["category"] = t.attrib.get("category")
3756             type_info["name"] = t.attrib.get("name")
3758             alias = t.attrib.get("alias")
3760             if type_info["category"] == "bitmask":
3761                 bitmask = VkBaseType(type_info["name"], alias, alias=self.types[alias]["data"])
3762                 bitmasks.append(bitmask)
3763                 type_info["data"] = bitmask
3765             if type_info["category"] == "enum":
3766                 enum = VkEnum.from_alias(t, self.types[alias]["data"])
3767                 type_info["data"] = enum
3768                 self.enums[enum.name] = enum
3770             if type_info["category"] == "handle":
3771                 handle = VkHandle.from_alias(t, self.types[alias]["data"])
3772                 handles.append(handle)
3773                 type_info["data"] = handle
3775             if type_info["category"] == "struct":
3776                 struct = VkStruct.from_alias(t, self.types[alias]["data"])
3777                 structs.append(struct)
3778                 type_info["data"] = struct
3780             self.types[type_info["name"]] = type_info
3782         # We need detailed type information during code generation
3783         # on structs for alignment reasons. Unfortunately structs
3784         # are parsed among other types, so there is no guarantee
3785         # that any types needed have been parsed already, so set
3786         # the data now.
3787         for struct in structs:
3788             struct.set_type_info(self.types)
3790             # Alias structures have enum values equivalent to those of the
3791             # structure which they are aliased against. we need to ignore alias
3792             # structs when populating the struct extensions list, otherwise we
3793             # will create duplicate case entries.
3794             if struct.alias:
3795                 continue
3797             for structextend in struct.structextends:
3798                 s = self.types[structextend]["data"]
3799                 s.struct_extensions.append(struct)
3801         # Guarantee everything is sorted, so code generation doesn't have
3802         # to deal with this.
3803         self.base_types = sorted(base_types, key=lambda base_type: base_type.name)
3804         self.bitmasks = sorted(bitmasks, key=lambda bitmask: bitmask.name)
3805         self.defines = defines
3806         self.enums = OrderedDict(sorted(self.enums.items()))
3807         self.funcpointers = sorted(funcpointers, key=lambda fp: fp.name)
3808         self.handles = sorted(handles, key=lambda handle: handle.name)
3809         self.structs = sorted(structs, key=lambda struct: struct.name)
3811 def generate_vulkan_json(f):
3812     f.write("{\n")
3813     f.write("    \"file_format_version\": \"1.0.0\",\n")
3814     f.write("    \"ICD\": {\n")
3815     f.write("        \"library_path\": \".\\\\winevulkan.dll\",\n")
3816     f.write("        \"api_version\": \"{0}\"\n".format(VK_XML_VERSION))
3817     f.write("    }\n")
3818     f.write("}\n")
3820 def set_working_directory():
3821     path = os.path.abspath(__file__)
3822     path = os.path.dirname(path)
3823     os.chdir(path)
3825 def download_vk_xml(filename):
3826     url = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/v{0}/xml/vk.xml".format(VK_XML_VERSION)
3827     if not os.path.isfile(filename):
3828         urllib.request.urlretrieve(url, filename)
3830 def main():
3831     parser = argparse.ArgumentParser()
3832     parser.add_argument("-v", "--verbose", action="count", default=0, help="increase output verbosity")
3833     parser.add_argument("-x", "--xml", default=None, type=str, help="path to specification XML file")
3835     args = parser.parse_args()
3836     if args.verbose == 0:
3837         LOGGER.setLevel(logging.WARNING)
3838     elif args.verbose == 1:
3839         LOGGER.setLevel(logging.INFO)
3840     else: # > 1
3841         LOGGER.setLevel(logging.DEBUG)
3843     set_working_directory()
3845     if args.xml:
3846         vk_xml = args.xml
3847     else:
3848         vk_xml = "vk-{0}.xml".format(VK_XML_VERSION)
3849         download_vk_xml(vk_xml)
3851     registry = VkRegistry(vk_xml)
3852     generator = VkGenerator(registry)
3854     with open(WINE_VULKAN_H, "w") as f:
3855         generator.generate_vulkan_h(f)
3857     with open(WINE_VULKAN_DRIVER_H, "w") as f:
3858         generator.generate_vulkan_driver_h(f)
3860     with open(WINE_VULKAN_THUNKS_H, "w") as f:
3861         generator.generate_thunks_h(f, "wine_")
3863     with open(WINE_VULKAN_THUNKS_C, "w") as f:
3864         generator.generate_thunks_c(f)
3866     with open(WINE_VULKAN_LOADER_THUNKS_H, "w") as f:
3867         generator.generate_loader_thunks_h(f)
3869     with open(WINE_VULKAN_LOADER_THUNKS_C, "w") as f:
3870         generator.generate_loader_thunks_c(f)
3872     with open(WINE_VULKAN_JSON, "w") as f:
3873         generate_vulkan_json(f)
3875     with open(WINE_VULKAN_SPEC, "w") as f:
3876         generator.generate_vulkan_spec(f)
3878     with open(WINE_VULKAN_LOADER_SPEC, "w") as f:
3879         generator.generate_vulkan_loader_spec(f)
3881 if __name__ == "__main__":
3882     main()