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
28 import xml.etree.ElementTree as ET
29 from collections import OrderedDict
30 from collections.abc import Sequence
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.237"
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)
85 # Start for a given extension is:
86 # EXT_BASE + (extension_number-1) * EXT_BLOCK_SIZE
90 UNSUPPORTED_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
98 "VK_AMD_display_native_hdr",
99 "VK_EXT_full_screen_exclusive",
100 "VK_EXT_hdr_metadata", # Needs WSI work.
101 "VK_GOOGLE_display_timing",
102 "VK_KHR_external_fence_win32",
103 "VK_KHR_external_semaphore_win32",
104 # Relates to external_semaphore and needs type conversions in bitflags.
105 "VK_KHR_shared_presentable_image", # Needs WSI work.
106 "VK_KHR_win32_keyed_mutex",
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.
139 "VK_KHR_display_swapchain",
140 "VK_KHR_get_surface_capabilities2",
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 # Functions part of our winevulkan graphics driver interface.
154 # DRIVER_VERSION should be bumped on any change to driver interface
155 # in FUNCTION_OVERRIDES
158 class ThunkType(Enum):
163 # Table of functions for which we have a special implementation.
164 # These are regular device / instance functions for which we need
165 # to do more work compared to a regular thunk or because they are
166 # part of the driver interface.
167 # - dispatch set whether we need a function pointer in the device
168 # / instance dispatch table.
169 # - driver sets whether the API is part of the driver interface.
170 # - thunk sets whether to create a thunk in vulkan_thunks.c.
171 # - NONE means there's a fully custom implementation.
172 # - PUBLIC means the implementation is fully auto generated.
173 # - PRIVATE thunks can be used in custom implementations for
175 # - loader_thunk sets whether to create a thunk for unix funcs.
176 FUNCTION_OVERRIDES = {
178 "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE, "extra_param" : "client_ptr"},
179 "vkEnumerateInstanceExtensionProperties" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
180 "vkEnumerateInstanceLayerProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
181 "vkEnumerateInstanceVersion": {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
182 "vkGetInstanceProcAddr": {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
185 "vkCreateDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE, "extra_param" : "client_ptr"},
186 "vkDestroyInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
187 "vkEnumerateDeviceExtensionProperties" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
188 "vkEnumerateDeviceLayerProperties": {"dispatch": True, "driver": False, "thunk": ThunkType.NONE},
189 "vkEnumeratePhysicalDeviceGroups" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
190 "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
191 "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
192 "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
193 "vkGetPhysicalDeviceExternalSemaphoreProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
194 "vkGetPhysicalDeviceImageFormatProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
195 "vkGetPhysicalDeviceProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PUBLIC, "loader_thunk" : ThunkType.PRIVATE},
196 "vkGetPhysicalDeviceProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PUBLIC, "loader_thunk" : ThunkType.PRIVATE},
199 "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
200 "vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE, "extra_param" : "client_ptr"},
201 "vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
202 "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
203 "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
204 "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
205 "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
206 "vkGetDeviceQueue2" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
207 "vkAllocateMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
208 "vkFreeMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
209 "vkMapMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
210 "vkUnmapMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
211 "vkCreateBuffer" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
212 "vkCreateImage" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
215 "vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE},
216 "vkGetPhysicalDeviceSurfaceSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
217 "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
218 "vkGetPhysicalDeviceSurfaceFormatsKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
219 "vkGetPhysicalDeviceSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
221 # VK_KHR_get_surface_capabilities2
222 "vkGetPhysicalDeviceSurfaceCapabilities2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
223 "vkGetPhysicalDeviceSurfaceFormats2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
225 # VK_KHR_win32_surface
226 "vkCreateWin32SurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE},
227 "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
230 "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
231 "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
232 "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
233 "vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
235 # VK_KHR_external_fence_capabilities
236 "vkGetPhysicalDeviceExternalFencePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
238 # VK_KHR_external_memory_capabilities
239 "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
240 "vkGetPhysicalDeviceImageFormatProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
242 # VK_KHR_external_semaphore_capabilities
243 "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
245 # VK_KHR_device_group_creation
246 "vkEnumeratePhysicalDeviceGroupsKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
248 # VK_KHR_device_group
249 "vkGetDeviceGroupSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
250 "vkGetPhysicalDevicePresentRectanglesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
252 # VK_EXT_calibrated_timestamps
253 "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
254 "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
257 "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
258 "vkDestroyDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
260 # VK_EXT_debug_report
261 "vkCreateDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
262 "vkDestroyDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
265 STRUCT_CHAIN_CONVERSIONS = {
266 # Ignore to not confuse host loader.
267 "VkDeviceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO"],
268 "VkInstanceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO"],
271 # Some struct members are conditionally ignored and callers are free to leave them uninitialized.
272 # We can't deduce that from XML, so we allow expressing it here.
273 MEMBER_LENGTH_EXPRESSIONS = {
274 "VkWriteDescriptorSet": {
276 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || " +
277 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || " +
278 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || " +
279 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || " +
280 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT || " +
281 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM || " +
282 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM ? {len} : 0",
284 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || " +
285 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || " +
286 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || " +
287 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC ? {len} : 0",
291 class Direction(Enum):
292 """ Parameter direction: input, output, input_output. """
297 class VkBaseType(object):
298 def __init__(self, name, _type, alias=None, requires=None):
299 """ Vulkan base type class.
301 VkBaseType is mostly used by Vulkan to define its own
302 base types like VkFlags through typedef out of e.g. uint32_t.
305 name (:obj:'str'): Name of the base type.
306 _type (:obj:'str'): Underlying type
307 alias (bool): type is an alias or not.
308 requires (:obj:'str', optional): Other types required.
309 Often bitmask values pull in a *FlagBits type.
314 self.requires = requires
315 self.required = False
317 def definition(self):
318 # Definition is similar for alias or non-alias as type
319 # is already set to alias.
320 if not self.type is None:
321 return "typedef {0} {1};\n".format(self.type, self.name)
323 return "struct {0};\n".format(self.name)
326 return bool(self.alias)
329 class VkConstant(object):
330 def __init__(self, name, value):
334 def definition(self):
335 text = "#define {0} {1}\n".format(self.name, self.value)
339 class VkDefine(object):
340 def __init__(self, name, value):
345 def from_xml(define):
346 name_elem = define.find("name")
348 if name_elem is None:
349 # <type category="define" name="some_name">some_value</type>
350 name = define.attrib.get("name")
352 # We override behavior of VK_USE_64_BIT_PTR_DEFINES as the default non-dispatchable handle
353 # definition various between 64-bit (uses pointers) and 32-bit (uses uint64_t).
354 # This complicates TRACEs in the thunks, so just use uint64_t.
355 if name == "VK_USE_64_BIT_PTR_DEFINES":
356 value = "#define VK_USE_64_BIT_PTR_DEFINES 0"
359 return VkDefine(name, value)
361 # With a name element the structure is like:
362 # <type category="define"><name>some_name</name>some_value</type>
363 name = name_elem.text
365 # Perform minimal parsing for Vulkan constants, which we don't need, but are referenced
366 # elsewhere in vk.xml.
367 # - VK_API_VERSION is a messy, deprecated constant and we don't want generate code for it.
368 # - AHardwareBuffer/ANativeWindow are forward declarations for Android types, which leaked
369 # into the define region.
370 if name in ["VK_API_VERSION", "AHardwareBuffer", "ANativeWindow", "CAMetalLayer"]:
371 return VkDefine(name, None)
373 # The body of the define is basically unstructured C code. It is not meant for easy parsing.
374 # Some lines contain deprecated values or comments, which we try to filter out.
376 for line in define.text.splitlines():
377 # Skip comments or deprecated values.
384 if child.tail is not None:
385 # Split comments for VK_API_VERSION_1_0 / VK_API_VERSION_1_1
386 if "//" in child.tail:
387 value += child.tail.split("//")[0]
391 return VkDefine(name, value.rstrip(' '))
393 def definition(self):
394 if self.value is None:
397 # Nothing to do as the value was already put in the right form during parsing.
398 return "{0}\n".format(self.value)
401 class VkEnum(object):
402 def __init__(self, name, bitwidth, alias=None):
403 if not bitwidth in [32, 64]:
404 LOGGER.error("unknown bitwidth {0} for {1}".format(bitwidth, name))
406 self.bitwidth = bitwidth
407 self.values = [] if alias == None else alias.values
408 self.required = False
413 def from_alias(enum, alias):
414 name = enum.attrib.get("name")
415 aliasee = VkEnum(name, alias.bitwidth, alias=alias)
417 alias.add_aliased_by(aliasee)
422 name = enum.attrib.get("name")
423 bitwidth = int(enum.attrib.get("bitwidth", "32"))
424 result = VkEnum(name, bitwidth)
426 for v in enum.findall("enum"):
427 value_name = v.attrib.get("name")
428 # Value is either a value or a bitpos, only one can exist.
429 value = v.attrib.get("value")
430 alias_name = v.attrib.get("alias")
432 result.create_alias(value_name, alias_name)
434 result.create_value(value_name, value)
437 result.create_bitpos(value_name, int(v.attrib.get("bitpos")))
440 # vulkan.h contains a *_MAX_ENUM value set to 32-bit at the time of writing,
441 # which is to prepare for extensions as they can add values and hence affect
442 # the size definition.
443 max_name = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2', name).upper() + "_MAX_ENUM"
444 result.create_value(max_name, "0x7fffffff")
448 def create_alias(self, name, alias_name):
449 """ Create an aliased value for this enum """
450 self.add(VkEnumValue(name, self.bitwidth, alias=alias_name))
452 def create_value(self, name, value):
453 """ Create a new value for this enum """
454 # Some values are in hex form. We want to preserve the hex representation
455 # at least when we convert back to a string. Internally we want to use int.
457 self.add(VkEnumValue(name, self.bitwidth, value=int(value, 0), hex=hex))
459 def create_bitpos(self, name, pos):
460 """ Create a new bitmask value for this enum """
461 self.add(VkEnumValue(name, self.bitwidth, value=(1 << pos), hex=True))
463 def add(self, value):
464 """ Add a value to enum. """
466 # Extensions can add new enum values. When an extension is promoted to Core
467 # the registry defines the value twice once for old extension and once for
468 # new Core features. Add the duplicate if it's explicitly marked as an
469 # alias, otherwise ignore it.
470 for v in self.values:
471 if not value.is_alias() and v.value == value.value:
472 LOGGER.debug("Adding duplicate enum value {0} to {1}".format(v, self.name))
474 # Avoid adding duplicate aliases multiple times
475 if not any(x.name == value.name for x in self.values):
476 self.values.append(value)
478 def fixup_64bit_aliases(self):
479 """ Replace 64bit aliases with literal values """
480 # Older GCC versions need a literal to initialize a static const uint64_t
481 # which is what we use for 64bit bitmasks.
482 if self.bitwidth != 64:
484 for value in self.values:
485 if not value.is_alias():
487 alias = next(x for x in self.values if x.name == value.alias)
488 value.hex = alias.hex
489 value.value = alias.value
491 def definition(self):
495 default_value = 0x7ffffffe if self.bitwidth == 32 else 0xfffffffffffffffe
497 # Print values sorted, values can have been added in a random order.
498 values = sorted(self.values, key=lambda value: value.value if value.value is not None else default_value)
500 if self.bitwidth == 32:
501 text = "typedef enum {0}\n{{\n".format(self.name)
503 text += " {0},\n".format(value.definition())
504 text += "}} {0};\n".format(self.name)
505 elif self.bitwidth == 64:
506 text = "typedef VkFlags64 {0};\n\n".format(self.name)
508 text += "static const {0} {1};\n".format(self.name, value.definition())
510 for aliasee in self.aliased_by:
511 text += "typedef {0} {1};\n".format(self.name, aliasee.name)
517 return bool(self.alias)
519 def add_aliased_by(self, aliasee):
520 self.aliased_by.append(aliasee)
523 class VkEnumValue(object):
524 def __init__(self, name, bitwidth, value=None, hex=False, alias=None):
526 self.bitwidth = bitwidth
532 postfix = "ull" if self.bitwidth == 64 else ""
533 if self.is_alias() and self.value == None:
534 return "{0}={1}".format(self.name, self.alias)
535 return "{0}={1}{2}".format(self.name, self.value, postfix)
537 def definition(self):
538 """ Convert to text definition e.g. VK_FOO = 1 """
539 postfix = "ull" if self.bitwidth == 64 else ""
540 if self.is_alias() and self.value == None:
541 return "{0} = {1}".format(self.name, self.alias)
543 # Hex is commonly used for FlagBits and sometimes within
544 # a non-FlagBits enum for a bitmask value as well.
546 return "{0} = 0x{1:08x}{2}".format(self.name, self.value, postfix)
548 return "{0} = {1}{2}".format(self.name, self.value, postfix)
551 return self.alias is not None
554 class VkFunction(object):
555 def __init__(self, _type=None, name=None, params=[], alias=None):
556 self.extensions = set()
562 # For some functions we need some extra metadata from FUNCTION_OVERRIDES.
563 func_info = FUNCTION_OVERRIDES.get(self.name, None)
564 self.dispatch = func_info["dispatch"] if func_info else True
565 self.driver = func_info["driver"] if func_info else False
566 self.thunk_type = func_info["thunk"] if func_info else ThunkType.PUBLIC
567 self.loader_thunk_type = func_info["loader_thunk"] if func_info and "loader_thunk" in func_info else ThunkType.PUBLIC
568 self.extra_param = func_info["extra_param"] if func_info and "extra_param" in func_info else None
570 # Required is set while parsing which APIs and types are required
571 # and is used by the code generation.
572 self.required = True if func_info else False
575 def from_alias(command, alias):
576 """ Create VkFunction from an alias command.
579 command: xml data for command
580 alias (VkFunction): function to use as a base for types / parameters.
585 func_name = command.attrib.get("name")
586 func_type = alias.type
587 params = alias.params
589 return VkFunction(_type=func_type, name=func_name, params=params, alias=alias)
592 def from_xml(command, types):
593 proto = command.find("proto")
594 func_name = proto.find("name").text
595 func_type = proto.find("type").text
598 for param in command.findall("param"):
599 vk_param = VkParam.from_xml(param, types, params)
600 params.append(vk_param)
602 return VkFunction(_type=func_type, name=func_name, params=params)
604 def get_conversions(self):
605 """ Get a list of conversion functions required for this function if any.
606 Parameters which are structures may require conversion between win32
607 and the host platform. This function returns a list of conversions
612 for param in self.params:
613 conversions.extend(param.get_conversions(self.thunk_type == ThunkType.PUBLIC))
617 return bool(self.alias)
619 def is_core_func(self):
620 """ Returns whether the function is a Vulkan core function.
621 Core functions are APIs defined by the Vulkan spec to be part of the
622 Core API as well as several KHR WSI extensions.
625 if not self.extensions:
628 return any(ext in self.extensions for ext in CORE_EXTENSIONS)
630 def is_device_func(self):
631 # If none of the other, it must be a device function
632 return not self.is_global_func() and not self.is_instance_func() and not self.is_phys_dev_func()
634 def is_driver_func(self):
635 """ Returns if function is part of Wine driver interface. """
638 def is_global_func(self):
639 # Treat vkGetInstanceProcAddr as a global function as it
640 # can operate with NULL for vkInstance.
641 if self.name == "vkGetInstanceProcAddr":
643 # Global functions are not passed a dispatchable object.
644 elif self.params[0].is_dispatchable():
648 def is_instance_func(self):
649 # Instance functions are passed VkInstance.
650 if self.params[0].type == "VkInstance":
654 def is_phys_dev_func(self):
655 # Physical device functions are passed VkPhysicalDevice.
656 if self.params[0].type == "VkPhysicalDevice":
660 def is_required(self):
663 def returns_longlong(self):
664 return self.type in ["uint64_t", "VkDeviceAddress"]
666 def needs_dispatch(self):
669 def needs_private_thunk(self):
670 return self.needs_exposing() and self.loader_thunk_type != ThunkType.NONE and \
671 self.thunk_type != ThunkType.PUBLIC
673 def needs_exposing(self):
674 # The function needs exposed if at-least one extension isn't both UNSUPPORTED and UNEXPOSED
675 return self.is_required() and (not self.extensions or not self.extensions.issubset(UNEXPOSED_EXTENSIONS))
677 def pfn(self, prefix="p", call_conv=None):
678 """ Create function pointer. """
681 pfn = "{0} ({1} *{2}_{3})(".format(self.type, call_conv, prefix, self.name)
683 pfn = "{0} (*{1}_{2})(".format(self.type, prefix, self.name)
685 for i, param in enumerate(self.params):
687 pfn += param.const + " "
691 if param.is_pointer():
692 pfn += " " + param.pointer
694 if param.array_len is not None:
695 pfn += "[{0}]".format(param.array_len)
697 if i < len(self.params) - 1:
702 def prototype(self, call_conv=None, prefix=None, postfix=None, is_thunk=False):
703 """ Generate prototype for given function.
706 call_conv (str, optional): calling convention e.g. WINAPI
707 prefix (str, optional): prefix to append prior to function name e.g. vkFoo -> wine_vkFoo
708 postfix (str, optional): text to append after function name but prior to semicolon e.g. DECLSPEC_HIDDEN
711 proto = "{0}".format(self.type)
713 if call_conv is not None:
714 proto += " {0}".format(call_conv)
716 if prefix is not None:
717 proto += " {0}{1}(".format(prefix, self.name)
719 proto += " {0}(".format(self.name)
721 # Add all the parameters.
722 proto += ", ".join([p.definition() for p in self.params])
724 if is_thunk and self.extra_param:
725 proto += ", void *" + self.extra_param
727 if postfix is not None:
728 proto += ") {0}".format(postfix)
734 def loader_body(self):
735 body = " struct {0}_params params;\n".format(self.name)
736 body += " NTSTATUS status;\n"
737 for p in self.params:
738 body += " params.{0} = {0};\n".format(p.name)
740 # Call the Unix function.
741 body += " status = UNIX_CALL({0}, ¶ms);\n".format(self.name)
742 body += " assert(!status);\n"
743 if self.type != "void":
744 body += " return params.result;\n"
747 def body(self, conv, unwrap, params_prefix=""):
751 # Declare any tmp parameters for conversion.
752 for p in self.params:
753 if p.needs_variable(conv, unwrap):
754 if p.is_dynamic_array():
755 body += " {2}{0} *{1}_host;\n".format(
756 p.type, p.name, "const " if p.is_const() else "")
758 body += " {0} *{1}_host = NULL;\n".format(p.type, p.name)
761 body += " {0} {1}_host;\n".format(p.type, p.name)
762 if p.needs_alloc(conv, unwrap):
766 body += " struct conversion_context ctx;\n"
768 body += " {0}\n".format(self.trace(params_prefix=params_prefix, conv=conv))
770 if self.params[0].optional and self.params[0].is_handle():
771 if self.type != "void":
772 LOGGER.warning("return type {0} with optional handle not supported".format(self.type))
773 body += " if (!{0}{1})\n".format(params_prefix, self.params[0].name)
774 body += " return STATUS_SUCCESS;\n\n"
777 body += " init_conversion_context(&ctx);\n"
779 # Call any win_to_host conversion calls.
780 unwrap = self.thunk_type == ThunkType.PUBLIC
781 for p in self.params:
782 if p.needs_conversion(conv, unwrap, Direction.INPUT):
783 body += p.copy(Direction.INPUT, conv, unwrap, prefix=params_prefix)
784 elif p.is_dynamic_array() and p.needs_conversion(conv, unwrap, Direction.OUTPUT):
785 body += " {0}_host = ({2}{0} && {1}) ? conversion_context_alloc(&ctx, sizeof(*{0}_host) * {1}) : NULL;\n".format(
786 p.name, p.get_dyn_array_len(params_prefix, conv), params_prefix)
788 # Build list of parameters containing converted and non-converted parameters.
789 # The param itself knows if conversion is needed and applies it when we set conv=True.
790 params = ", ".join([p.variable(conv=conv, unwrap=unwrap, params_prefix=params_prefix) for p in self.params])
793 params += ", UlongToPtr({0}{1})".format(params_prefix, self.extra_param)
795 params += ", {0}{1}".format(params_prefix, self.extra_param)
797 if unwrap or self.thunk_type == ThunkType.PUBLIC:
798 func_prefix = "{0}.p_".format(self.params[0].dispatch_table(params_prefix, conv))
800 func_prefix = "wine_"
802 # Call the native Vulkan function.
803 if self.type == "void":
804 body += " {0}{1}({2});\n".format(func_prefix, self.name, params)
806 body += " {0}result = {1}{2}({3});\n".format(params_prefix, func_prefix, self.name, params)
808 # Call any host_to_win conversion calls.
809 for p in self.params:
810 if p.needs_conversion(conv, unwrap, Direction.OUTPUT):
811 body += p.copy(Direction.OUTPUT, conv, unwrap, prefix=params_prefix)
814 body += " free_conversion_context(&ctx);\n"
816 # Finally return the result.
817 body += " return STATUS_SUCCESS;\n"
821 def spec(self, prefix=None, symbol=None):
822 """ Generate spec file entry for this function.
825 prefix (str, optional): prefix to prepend to entry point name.
826 symbol (str, optional): allows overriding the name of the function implementing the entry point.
830 params = " ".join([p.spec() for p in self.params])
831 if prefix is not None:
832 spec += "@ stdcall -private {0}{1}({2})".format(prefix, self.name, params)
834 spec += "@ stdcall {0}({1})".format(self.name, params)
836 if symbol is not None:
842 def stub(self, call_conv=None, prefix=None):
843 stub = self.prototype(call_conv=call_conv, prefix=prefix)
845 stub += " {0}".format(self.trace(message="stub: ", trace_func="FIXME"))
847 if self.type == "VkResult":
848 stub += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
849 elif self.type == "VkBool32":
850 stub += " return VK_FALSE;\n"
851 elif self.type == "PFN_vkVoidFunction":
852 stub += " return NULL;\n"
857 def thunk(self, prefix=None, conv=False):
860 thunk += "#ifdef _WIN64\n"
861 thunk += "static NTSTATUS {0}{1}(void *args)\n".format(prefix, self.name)
866 for p in self.params:
867 thunk += " {0};\n".format(p.definition(conv=True, is_member=True))
869 thunk += " PTR32 {0};\n".format(self.extra_param)
870 if self.type != "void":
871 thunk += " {0} result;\n".format(self.type)
872 thunk += " } *params = args;\n"
874 thunk += " struct {0}_params *params = args;\n".format(self.name)
875 thunk += self.body(conv=conv, unwrap=self.thunk_type == ThunkType.PUBLIC, params_prefix="params->")
878 thunk += "#endif /* _WIN64 */\n"
882 def loader_thunk(self, prefix=None):
883 thunk = self.prototype(call_conv="WINAPI", prefix=prefix)
885 thunk += self.loader_body()
889 def trace(self, message=None, trace_func=None, params_prefix="", conv=False):
890 """ Create a trace string including all parameters.
893 message (str, optional): text to print at start of trace message e.g. 'stub: '
894 trace_func (str, optional): used to override trace function e.g. FIXME, printf, etcetera.
896 if trace_func is not None:
897 trace = "{0}(\"".format(trace_func)
901 if message is not None:
904 # First loop is for all the format strings.
905 trace += ", ".join([p.format_string(conv) for p in self.params])
908 # Second loop for parameter names and optional conversions.
909 for param in self.params:
910 if param.format_conv is not None:
911 trace += ", " + param.format_conv.format("{0}{1}".format(params_prefix, param.name))
913 trace += ", {0}{1}".format(params_prefix, param.name)
919 class VkFunctionPointer(object):
920 def __init__(self, _type, name, members, forward_decls):
922 self.members = members
924 self.required = False
925 self.forward_decls = forward_decls
928 def from_xml(funcpointer):
932 for t in funcpointer.findall("type"):
934 # <type>void</type>* pUserData,
935 # Parsing of the tail (anything past </type>) is tricky since there
936 # can be other data on the next line like: const <type>int</type>..
938 const = True if begin and "const" in begin else False
940 lines = t.tail.split(",\n")
941 if lines[0][0] == "*":
943 name = lines[0][1:].strip()
946 name = lines[0].strip()
948 # Filter out ); if it is contained.
949 name = name.partition(");")[0]
951 # If tail encompasses multiple lines, assign the second line to begin
954 begin = lines[1].strip()
958 members.append(VkMember(const=const, _type=_type, pointer=pointer, name=name))
960 _type = funcpointer.text
961 name = funcpointer.find("name").text
962 if "requires" in funcpointer.attrib:
963 forward_decls = funcpointer.attrib.get("requires").split(",")
966 return VkFunctionPointer(_type, name, members, forward_decls)
968 def definition(self):
970 # forward declare required structs
971 for decl in self.forward_decls:
972 text += "typedef struct {0} {0};\n".format(decl)
974 text += "{0} {1})(\n".format(self.type, self.name)
977 if len(self.members) > 0:
978 for m in self.members:
980 text += " " + m.definition()
983 text += ",\n " + m.definition()
985 # Just make the compiler happy by adding a void parameter.
993 class VkHandle(object):
994 def __init__(self, name, _type, parent, alias=None):
999 self.required = False
1000 self.object_type = None
1003 def from_alias(handle, alias):
1004 name = handle.attrib.get("name")
1005 return VkHandle(name, alias.type, alias.parent, alias=alias)
1008 def from_xml(handle):
1009 name = handle.find("name").text
1010 _type = handle.find("type").text
1011 parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice.
1012 return VkHandle(name, _type, parent)
1014 def dispatch_table(self, param):
1015 if not self.is_dispatchable():
1018 if self.parent is None:
1019 # Should only happen for VkInstance
1020 return "wine_instance_from_handle({0})->funcs".format(param)
1021 elif self.name == "VkCommandBuffer":
1022 return "wine_cmd_buffer_from_handle({0})->device->funcs".format(param)
1023 elif self.name == "VkDevice":
1024 return "wine_device_from_handle({0})->funcs".format(param)
1025 elif self.name == "VkPhysicalDevice":
1026 return "wine_phys_dev_from_handle({0})->instance->funcs".format(param)
1027 elif self.name == "VkQueue":
1028 return "wine_queue_from_handle({0})->device->funcs".format(param)
1029 elif self.parent in ["VkInstance", "VkPhysicalDevice"]:
1030 return "{0}->instance->funcs".format(param)
1031 elif self.parent in ["VkDevice", "VkCommandPool"]:
1032 return "{0}->device->funcs".format(param)
1034 LOGGER.error("Unhandled dispatchable parent: {0}".format(self.parent))
1036 def definition(self):
1037 """ Generates handle definition e.g. VK_DEFINE_HANDLE(vkInstance) """
1039 # Legacy types are typedef'ed to the new type if they are aliases.
1041 return "typedef {0} {1};\n".format(self.alias.name, self.name)
1043 return "{0}({1})\n".format(self.type, self.name)
1046 return self.alias is not None
1048 def is_dispatchable(self):
1049 """ Some handles like VkInstance, VkDevice are dispatchable objects,
1050 which means they contain a dispatch table of function pointers.
1052 return self.type == "VK_DEFINE_HANDLE"
1054 def is_required(self):
1055 return self.required
1057 def native_handle(self, name):
1058 """ Provide access to the native handle of a wrapped object. """
1060 if self.name == "VkCommandBuffer":
1061 return "wine_cmd_buffer_from_handle({0})->command_buffer".format(name)
1062 if self.name == "VkCommandPool":
1063 return "wine_cmd_pool_from_handle({0})->command_pool".format(name)
1064 if self.name == "VkDebugUtilsMessengerEXT":
1065 return "wine_debug_utils_messenger_from_handle({0})->debug_messenger".format(name)
1066 if self.name == "VkDebugReportCallbackEXT":
1067 return "wine_debug_report_callback_from_handle({0})->debug_callback".format(name)
1068 if self.name == "VkDevice":
1069 return "wine_device_from_handle({0})->device".format(name)
1070 if self.name == "VkInstance":
1071 return "wine_instance_from_handle({0})->instance".format(name)
1072 if self.name == "VkDeviceMemory":
1073 return "wine_device_memory_from_handle({0})->memory".format(name)
1074 if self.name == "VkPhysicalDevice":
1075 return "wine_phys_dev_from_handle({0})->phys_dev".format(name)
1076 if self.name == "VkQueue":
1077 return "wine_queue_from_handle({0})->queue".format(name)
1078 if self.name == "VkSurfaceKHR":
1079 return "wine_surface_from_handle({0})->surface".format(name)
1080 if self.is_dispatchable():
1081 LOGGER.error("Unhandled native handle for: {0}".format(self.name))
1084 def driver_handle(self, name):
1085 """ Provide access to the handle that should be passed to the wine driver """
1087 if self.name == "VkSurfaceKHR":
1088 return "wine_surface_from_handle({0})->driver_surface".format(name)
1090 return self.native_handle(name)
1092 def is_wrapped(self):
1093 return self.native_handle("test") is not None
1095 def needs_unwrapping(self):
1096 return self.is_wrapped()
1098 class VkVariable(object):
1099 def __init__(self, const=False, type_info=None, type=None, name=None, pointer=None, array_len=None,
1100 dyn_array_len=None, object_type=None, optional=False, returnedonly=False, parent=None,
1101 selection=None, selector=None):
1103 self.type_info = type_info
1106 self.parent = parent
1107 self.object_type = object_type
1108 self.optional = optional
1109 self.returnedonly = returnedonly
1110 self.selection = selection
1111 self.selector = selector
1113 self.pointer = pointer
1114 self.array_len = array_len
1115 self.dyn_array_len = dyn_array_len
1116 self.pointer_array = False
1117 if isinstance(dyn_array_len, str):
1118 i = dyn_array_len.find(",")
1120 self.dyn_array_len = dyn_array_len[0:i]
1121 self.pointer_array = True
1124 self.set_type_info(type_info)
1126 def __eq__(self, other):
1127 """ Compare member based on name against a string. """
1128 return self.name == other
1130 def set_type_info(self, type_info):
1131 """ Helper function to set type information from the type registry.
1132 This is needed, because not all type data is available at time of
1135 self.type_info = type_info
1136 self.handle = type_info["data"] if type_info["category"] == "handle" else None
1137 self.struct = type_info["data"] if type_info["category"] == "struct" or type_info["category"] == "union" else None
1139 def get_dyn_array_len(self, prefix, conv):
1140 if isinstance(self.dyn_array_len, int):
1141 return self.dyn_array_len
1143 len_str = self.dyn_array_len
1144 parent = self.parent
1147 # check if lenght is a member of another struct (for example pAllocateInfo->commandBufferCount)
1148 i = len_str.find("->")
1150 var = parent[parent.index(len_str[0:i])]
1151 len_str = len_str[i+2:]
1152 len = "({0})->".format(var.value(len, conv))
1155 if len_str in parent:
1156 var = parent[parent.index(len_str)]
1157 len = var.value(len, conv);
1158 if var.is_pointer():
1163 if isinstance(self.parent, VkStruct) and self.parent.name in MEMBER_LENGTH_EXPRESSIONS:
1164 exprs = MEMBER_LENGTH_EXPRESSIONS[self.parent.name]
1165 if self.name in exprs:
1166 len = exprs[self.name].format(struct=prefix, len=len)
1173 def is_pointer(self):
1174 return self.pointer is not None
1176 def is_pointer_size(self):
1177 if self.type in ["size_t", "HWND", "HINSTANCE"]:
1179 if self.is_handle() and self.handle.is_dispatchable():
1183 def is_handle(self):
1184 return self.handle is not None
1186 def is_struct(self):
1187 return self.type_info["category"] == "struct"
1190 return self.type_info["category"] == "union"
1192 def is_bitmask(self):
1193 return self.type_info["category"] == "bitmask"
1196 return self.type_info["category"] == "enum"
1198 def is_dynamic_array(self):
1199 """ Returns if the member is an array element.
1200 Vulkan uses this for dynamically sized arrays for which
1201 there is a 'count' parameter.
1203 return self.dyn_array_len is not None
1205 def is_static_array(self):
1206 """ Returns if the member is an array.
1207 Vulkan uses this often for fixed size arrays in which the
1208 length is part of the member.
1210 return self.array_len is not None
1212 def is_generic_handle(self):
1213 """ Returns True if the member is a unit64_t containing
1214 a handle with a separate object type
1216 return self.object_type != None and self.type == "uint64_t"
1218 def needs_alignment(self):
1219 """ Check if this member needs alignment for 64-bit data.
1220 Various structures need alignment on 64-bit variables due
1221 to compiler differences on 32-bit between Win32 and Linux.
1224 if self.is_pointer():
1226 elif self.type == "size_t":
1228 elif self.type in ["uint64_t", "VkDeviceAddress", "VkDeviceSize"]:
1230 elif self.is_bitmask():
1231 return self.type_info["data"].type == "VkFlags64"
1232 elif self.is_enum():
1233 return self.type_info["data"].bitwidth == 64
1234 elif self.is_struct() or self.is_union():
1235 return self.type_info["data"].needs_alignment()
1236 elif self.is_handle():
1237 # Dispatchable handles are pointers to objects, while
1238 # non-dispatchable are uint64_t and hence need alignment.
1239 return not self.handle.is_dispatchable()
1242 def needs_unwrapping(self):
1243 """ Returns if variable needs unwrapping of handle. """
1245 if self.is_struct():
1246 return self.struct.needs_unwrapping()
1248 if self.is_handle():
1249 return self.handle.needs_unwrapping()
1251 if self.is_generic_handle():
1256 def needs_alloc(self, conv, unwrap):
1257 """ Returns True if conversion needs allocation """
1258 if self.is_dynamic_array():
1259 return self.needs_conversion(conv, unwrap, Direction.INPUT, False) \
1260 or self.needs_conversion(conv, unwrap, Direction.OUTPUT, False)
1262 return (self.is_struct() or (self.is_union() and self.selector)) and self.struct.needs_alloc(conv, unwrap)
1264 def needs_win32_type(self):
1265 return (self.is_struct() or (self.is_union() and self.selector)) and self.struct.needs_win32_type()
1267 def get_conversions(self, unwrap, parent_const=False):
1268 """ Get a list of conversions required for this parameter if any.
1269 Parameters which are structures may require conversion between win32
1270 and the host platform. This function returns a list of conversions
1276 # Collect any member conversions first, so we can guarantee
1277 # those functions will be defined prior to usage by the
1278 # 'parent' param requiring conversion.
1279 if self.is_struct() or (self.is_union() and self.selector):
1280 struct = self.struct
1281 is_const = self.is_const() if self.is_pointer() else parent_const
1283 conversions.extend(struct.get_conversions(unwrap, is_const))
1285 for conv in [False, True]:
1286 if struct.needs_conversion(conv, unwrap, Direction.INPUT, is_const):
1287 conversions.append(StructConversionFunction(struct, Direction.INPUT, conv, unwrap, is_const))
1288 if struct.needs_conversion(conv, unwrap, Direction.OUTPUT, is_const):
1289 conversions.append(StructConversionFunction(struct, Direction.OUTPUT, conv, unwrap, is_const))
1291 if self.is_static_array() or self.is_dynamic_array():
1292 for conv in [False, True]:
1293 if self.needs_conversion(conv, unwrap, Direction.INPUT, parent_const):
1294 conversions.append(ArrayConversionFunction(self, Direction.INPUT, conv, unwrap))
1295 if self.needs_conversion(conv, unwrap, Direction.OUTPUT, parent_const):
1296 conversions.append(ArrayConversionFunction(self, Direction.OUTPUT, conv, unwrap))
1300 def needs_ptr32_type(self):
1301 """ Check if variable needs to use PTR32 type. """
1303 return self.is_pointer() or self.is_pointer_size() or self.is_static_array()
1305 def value(self, prefix, conv):
1306 """ Returns code accessing member value, casting 32-bit pointers when needed. """
1308 if not conv or not self.needs_ptr32_type() or (not self.is_pointer() and self.type == "size_t"):
1309 return prefix + self.name
1313 cast_type += "const "
1315 if self.pointer_array or ((self.is_pointer() or self.is_static_array()) and self.is_pointer_size()):
1316 cast_type += "PTR32 *"
1318 cast_type += self.type
1319 if self.needs_win32_type():
1322 if self.is_pointer():
1323 cast_type += " {0}".format(self.pointer)
1324 elif self.is_static_array():
1327 return "({0})UlongToPtr({1}{2})".format(cast_type, prefix, self.name)
1330 class VkMember(VkVariable):
1331 def __init__(self, const=False, struct_fwd_decl=False,_type=None, pointer=None, name=None, array_len=None,
1332 dyn_array_len=None, optional=False, values=None, object_type=None, bit_width=None,
1333 returnedonly=False, parent=None, selection=None, selector=None):
1334 VkVariable.__init__(self, const=const, type=_type, name=name, pointer=pointer, array_len=array_len,
1335 dyn_array_len=dyn_array_len, object_type=object_type, optional=optional,
1336 returnedonly=returnedonly, parent=parent, selection=selection, selector=selector)
1337 self.struct_fwd_decl = struct_fwd_decl
1338 self.values = values
1339 self.bit_width = bit_width
1342 return "{0} {1} {2} {3} {4} {5} {6}".format(self.const, self.struct_fwd_decl, self.type, self.pointer,
1343 self.name, self.array_len, self.dyn_array_len)
1346 def from_xml(member, returnedonly, parent):
1347 """ Helper function for parsing a member tag within a struct or union. """
1349 name_elem = member.find("name")
1350 type_elem = member.find("type")
1353 struct_fwd_decl = False
1359 values = member.get("values")
1362 if "const" in member.text:
1365 # Some members contain forward declarations:
1366 # - VkBaseInstructure has a member "const struct VkBaseInStructure *pNext"
1367 # - VkWaylandSurfaceCreateInfoKHR has a member "struct wl_display *display"
1368 if "struct" in member.text:
1369 struct_fwd_decl = True
1371 if type_elem is not None:
1372 member_type = type_elem.text
1373 if type_elem.tail is not None:
1374 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1376 # Name of other member within, which stores the number of
1377 # elements pointed to be by this member.
1378 dyn_array_len = member.get("len")
1380 # Some members are optional, which is important for conversion code e.g. not dereference NULL pointer.
1381 optional = True if member.get("optional") else False
1383 # Usually we need to allocate memory for dynamic arrays. We need to do the same in a few other cases
1384 # like for VkCommandBufferBeginInfo.pInheritanceInfo. Just threat such cases as dynamic arrays of
1385 # size 1 to simplify code generation.
1386 if dyn_array_len is None and pointer is not None:
1389 # Some members are arrays, attempt to parse these. Formats include:
1390 # <member><type>char</type><name>extensionName</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
1391 # <member><type>uint32_t</type><name>foo</name>[4]</member>
1392 if name_elem.tail and name_elem.tail[0] == '[':
1393 LOGGER.debug("Found array type")
1394 enum_elem = member.find("enum")
1395 if enum_elem is not None:
1396 array_len = enum_elem.text
1398 # Remove brackets around length
1399 array_len = name_elem.tail.strip("[]")
1401 object_type = member.get("objecttype", None)
1403 # Some members are bit field values:
1404 # <member><type>uint32_t</type> <name>mask</name>:8</member>
1405 if name_elem.tail and name_elem.tail[0] == ':':
1406 LOGGER.debug("Found bit field")
1407 bit_width = int(name_elem.tail[1:])
1409 selection = member.get("selection").split(',') if member.get("selection") else None
1410 selector = member.get("selector", None)
1412 return VkMember(const=const, struct_fwd_decl=struct_fwd_decl, _type=member_type, pointer=pointer,
1413 name=name_elem.text, array_len=array_len, dyn_array_len=dyn_array_len, optional=optional,
1414 values=values, object_type=object_type, bit_width=bit_width, returnedonly=returnedonly,
1415 parent=parent, selection=selection, selector=selector)
1417 def copy(self, input, output, direction, conv, unwrap):
1418 """ Helper method for use by conversion logic to generate a C-code statement to copy this member.
1419 - `conv` indicates whether the statement is in a struct alignment conversion path. """
1421 win_type = "win32" if conv else "win64"
1422 if self.needs_conversion(conv, unwrap, direction, False):
1423 if self.is_dynamic_array():
1424 # Array length is either a variable name (string) or an int.
1425 count = self.get_dyn_array_len(input, conv)
1426 host_part = "host" if unwrap else "unwrapped_host"
1427 pointer_part = "pointer_" if self.pointer_array else ""
1428 if direction == Direction.OUTPUT:
1429 return "convert_{2}_{7}array_{6}_to_{5}({3}{1}, {0}, {4});\n".format(
1430 self.value(output, conv), self.name, self.type, input, count, win_type,
1431 host_part, pointer_part)
1433 return "{0}{1} = convert_{2}_{7}array_{5}_to_{6}(ctx, {3}, {4});\n".format(
1434 output, self.name, self.type, self.value(input, conv), count, win_type,
1435 host_part, pointer_part)
1436 elif self.is_static_array():
1437 count = self.array_len
1438 if direction == Direction.OUTPUT:
1439 # Needed by VkMemoryHeap.memoryHeaps
1440 host_part = "host" if unwrap else "unwrapped_host"
1441 return "convert_{0}_array_{6}_to_{5}({2}{1}, {3}{1}, {4});\n".format(
1442 self.type, self.name, input, output, count, win_type, host_part)
1444 # Nothing needed this yet.
1445 LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
1446 elif self.is_handle() and self.needs_unwrapping():
1447 handle = self.type_info["data"]
1448 if direction == Direction.OUTPUT:
1449 LOGGER.err("OUTPUT parameter {0}.{1} cannot be unwrapped".format(self.type, self.name))
1451 return "{0}{1} = {2} ? {3} : 0;\n".format(output, self.name,
1452 self.value(input, conv), handle.driver_handle(self.value(input, conv)))
1454 return "{0}{1} = {2};\n".format(output, self.name, handle.driver_handle(self.value(input, conv)))
1455 elif self.is_generic_handle():
1456 if direction == Direction.OUTPUT:
1457 LOGGER.err("OUTPUT parameter {0}.{1} cannot be unwrapped".format(self.type, self.name))
1459 return "{0}{1} = wine_vk_unwrap_handle({2}{3}, {2}{1});\n".format(output, self.name, input, self.object_type)
1461 selector_part = ", {0}{1}".format(input, self.selector) if self.selector else ""
1462 if direction == Direction.OUTPUT:
1463 return "convert_{0}_host_to_{4}(&{2}{1}, &{3}{1}{5});\n".format(self.type, self.name, input, output, win_type, selector_part)
1465 ctx_param = "ctx, " if self.needs_alloc(conv, unwrap) else ""
1466 host_part = "host" if unwrap else "unwrapped_host"
1467 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)
1468 elif self.is_static_array():
1469 bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type)
1470 return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count)
1471 elif direction == Direction.INPUT:
1472 return "{0}{1} = {2};\n".format(output, self.name, self.value(input, conv))
1473 elif conv and direction == Direction.OUTPUT and self.is_pointer():
1474 return "{0}{1} = PtrToUlong({2}{1});\n".format(output, self.name, input)
1476 return "{0}{1} = {2}{1};\n".format(output, self.name, input)
1478 def definition(self, align=False, conv=False):
1479 """ Generate prototype for given function.
1482 align (bool, optional): Enable alignment if a type needs it. This adds WINE_VK_ALIGN(8) to a member.
1483 conv (bool, optional): Enable conversion if a type needs it. This appends '_host' to the name.
1486 if conv and (self.is_pointer() or self.is_pointer_size()):
1487 text = "PTR32 " + self.name
1488 if self.is_static_array():
1489 text += "[{0}]".format(self.array_len)
1496 if self.is_struct_forward_declaration():
1500 if conv and self.needs_win32_type():
1503 if self.is_pointer():
1504 text += " {0}{1}".format(self.pointer, self.name)
1506 if align and self.needs_alignment():
1508 text += " DECLSPEC_ALIGN(8) " + self.name
1510 text += " WINE_VK_ALIGN(8) " + self.name
1512 text += " " + self.name
1514 if self.is_static_array():
1515 text += "[{0}]".format(self.array_len)
1517 if self.is_bit_field():
1518 text += ":{}".format(self.bit_width)
1522 def is_struct_forward_declaration(self):
1523 return self.struct_fwd_decl
1525 def is_bit_field(self):
1526 return self.bit_width is not None
1528 def needs_conversion(self, conv, unwrap, direction, struct_const):
1529 """ Check if member needs conversion. """
1531 # we can't convert unions if we don't have a selector
1532 if self.is_union() and not self.selector:
1535 is_const = self.is_const() if self.is_pointer() else struct_const
1537 # const members don't needs output conversion unless they are structs with non-const pointers
1538 if direction == Direction.OUTPUT and is_const and not self.is_struct():
1541 if direction == Direction.INPUT:
1542 # returnedonly members don't needs input conversions
1543 if not self.is_pointer() and self.returnedonly:
1545 # pointer arrays always need input conversion
1546 if conv and self.is_dynamic_array() and self.pointer_array:
1549 if self.is_handle():
1550 if unwrap and self.handle.is_wrapped():
1552 if conv and self.handle.is_dispatchable():
1554 elif self.is_generic_handle():
1557 elif self.is_struct() or self.is_union():
1558 if self.struct.needs_conversion(conv, unwrap, direction, is_const):
1561 # if pointer member needs output conversion, it also needs input conversion
1562 # to allocate the pointer
1563 if direction == Direction.INPUT and self.is_pointer() and \
1564 self.needs_conversion(conv, unwrap, Direction.OUTPUT, struct_const):
1569 class VkParam(VkVariable):
1570 """ Helper class which describes a parameter to a function call. """
1572 def __init__(self, type_info, const=None, pointer=None, name=None, parent=None, array_len=None,
1573 dyn_array_len=None, object_type=None, optional=False):
1574 VkVariable.__init__(self, const=const, type_info=type_info, type=type_info["name"], name=name,
1575 pointer=pointer, array_len=array_len, dyn_array_len=dyn_array_len,
1576 object_type=object_type, optional=optional, parent=parent)
1578 self._set_format_string()
1581 return "{0} {1} {2} {3} {4} {5}".format(self.const, self.type, self.pointer, self.name, self.array_len, self.dyn_array_len)
1584 def from_xml(param, types, parent):
1585 """ Helper function to create VkParam from xml. """
1587 # Parameter parsing is slightly tricky. All the data is contained within
1588 # a param tag, but some data is within subtags while others are text
1589 # before or after the type tag.
1591 # <param>const <type>char</type>* <name>pLayerName</name></param>
1593 name_elem = param.find("name")
1595 name = name_elem.text
1596 # Tail contains array length e.g. for blendConstants param of vkSetBlendConstants
1597 if name_elem.tail is not None:
1598 array_len = name_elem.tail.strip("[]")
1600 # Name of other parameter in function prototype, which stores the number of
1601 # elements pointed to be by this parameter.
1602 dyn_array_len = param.get("len", None)
1604 const = param.text.strip() if param.text else None
1605 type_elem = param.find("type")
1606 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1608 attr = param.get("optional")
1609 optional = attr and attr.startswith("true")
1611 # Some uint64_t are actually handles with a separate type param
1612 object_type = param.get("objecttype", None)
1614 # Since we have parsed all types before hand, this should not happen.
1615 type_info = types.get(type_elem.text, None)
1616 if type_info is None:
1617 LOGGER.err("type info not found for: {0}".format(type_elem.text))
1619 return VkParam(type_info, const=const, pointer=pointer, name=name, array_len=array_len,
1620 dyn_array_len=dyn_array_len, object_type=object_type, optional=optional,
1623 def _set_format_string(self):
1624 """ Internal helper function to be used by constructor to set format string. """
1626 # Determine a format string used by code generation for traces.
1627 # 64-bit types need a conversion function.
1628 self.format_conv = None
1629 if self.is_static_array() or self.is_pointer():
1630 self.format_str = "%p"
1632 if self.type_info["category"] in ["bitmask"]:
1633 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1634 if self.type_info["data"].type == "VkFlags64":
1635 self.format_str = "0x%s"
1636 self.format_conv = "wine_dbgstr_longlong({0})"
1638 self.format_str = "%#x"
1639 elif self.type_info["category"] in ["enum"]:
1640 self.format_str = "%#x"
1641 elif self.is_handle():
1642 # We use uint64_t for non-dispatchable handles as opposed to pointers
1643 # for dispatchable handles.
1644 if self.handle.is_dispatchable():
1645 self.format_str = "%p"
1647 self.format_str = "0x%s"
1648 self.format_conv = "wine_dbgstr_longlong({0})"
1649 elif self.type == "float":
1650 self.format_str = "%f"
1651 elif self.type == "int":
1652 self.format_str = "%d"
1653 elif self.type == "int32_t":
1654 self.format_str = "%d"
1655 elif self.type == "size_t":
1656 self.format_str = "0x%s"
1657 self.format_conv = "wine_dbgstr_longlong({0})"
1658 elif self.type in ["uint16_t", "uint32_t", "VkBool32"]:
1659 self.format_str = "%u"
1660 elif self.type in ["uint64_t", "VkDeviceAddress", "VkDeviceSize"]:
1661 self.format_str = "0x%s"
1662 self.format_conv = "wine_dbgstr_longlong({0})"
1663 elif self.type == "HANDLE":
1664 self.format_str = "%p"
1665 elif self.type in ["VisualID", "xcb_visualid_t", "RROutput", "zx_handle_t"]:
1666 # Don't care about specific types for non-Windows platforms.
1667 self.format_str = ""
1669 LOGGER.warn("Unhandled type: {0}".format(self.type_info))
1671 def copy(self, direction, conv, unwrap, prefix=""):
1672 win_type = "win32" if conv else "win64"
1673 wrap_part = "" if unwrap or not self.needs_unwrapping() else "unwrapped_"
1674 if direction == Direction.INPUT:
1675 ctx_param = "&ctx, " if self.needs_alloc(conv, unwrap) else ""
1676 if self.is_dynamic_array():
1677 return " {0}_host = convert_{2}_array_{4}_to_{6}host({5}{1}, {3});\n".format(
1678 self.name, self.value(prefix, conv), self.type, self.get_dyn_array_len(prefix, conv),
1679 win_type, ctx_param, wrap_part)
1681 ret = " if ({0}{1})\n".format(prefix, self.name)
1683 ret += " {0}_host = conversion_context_alloc(&ctx, sizeof(*{0}_host));\n".format(self.name)
1684 ret += " convert_{0}_{3}_to_{5}host({4}{1}, {2}_host);\n".format(
1685 self.type, self.value(prefix, conv), self.name, win_type, ctx_param, wrap_part)
1688 elif self.is_struct():
1689 return " convert_{0}_{3}_to_{5}host({4}{1}, &{2}_host);\n".format(
1690 self.type, self.value(prefix, conv), self.name, win_type, ctx_param, wrap_part)
1691 elif self.is_pointer_size() and self.type != "size_t":
1692 return " {0}_host = UlongToPtr(*{1});\n".format(self.name, self.value(prefix, conv))
1694 return " {0}_host = *{1};\n".format(self.name, self.value(prefix, conv))
1696 if self.is_dynamic_array():
1697 return " convert_{0}_array_{1}host_to_{2}({3}_host, {4}, {5});\n".format(
1698 self.type, wrap_part, win_type, self.name, self.value(prefix, conv),
1699 self.get_dyn_array_len(prefix, conv))
1700 elif self.is_struct():
1701 ref_part = "" if self.optional else "&"
1702 return " convert_{0}_host_to_{3}({4}{2}_host, {1});\n".format(
1703 self.type, self.value(prefix, conv), self.name, win_type, ref_part)
1704 elif self.is_pointer_size() and self.type != "size_t":
1705 return " *{0} = PtrToUlong({1}_host);\n".format(self.value(prefix, conv), self.name)
1707 return " *{0} = {1}_host;\n".format(self.value(prefix, conv), self.name)
1709 def definition(self, postfix=None, is_member=False, conv=False):
1710 """ Return prototype for the parameter. E.g. 'const char *foo' """
1712 if is_member and conv and self.needs_ptr32_type():
1713 return "PTR32 {0}".format(self.name)
1717 proto += self.const + " "
1721 if conv and self.needs_win32_type():
1724 if is_member and self.needs_alignment():
1725 proto += " DECLSPEC_ALIGN(8)"
1727 if self.is_pointer():
1728 proto += " {0}{1}".format(self.pointer, name)
1729 elif is_member and self.is_static_array():
1730 proto += " *" + name
1734 # Allows appending something to the variable name useful for
1735 # win32 to host conversion.
1736 if postfix is not None:
1739 if not is_member and self.is_static_array():
1740 proto += "[{0}]".format(self.array_len)
1744 def dispatch_table(self, params_prefix, conv):
1745 """ Return functions dispatch table pointer for dispatchable objects. """
1747 if not self.is_dispatchable():
1750 return self.handle.dispatch_table(self.value(params_prefix, conv))
1752 def format_string(self, conv):
1753 if conv and self.needs_ptr32_type() and (self.type != "size_t" or self.is_pointer()):
1755 return self.format_str
1757 def is_dispatchable(self):
1758 if not self.is_handle():
1761 return self.handle.is_dispatchable()
1763 def needs_conversion(self, conv, unwrap, direction, parent_const=False):
1764 """ Check if param needs conversion. """
1766 if self.is_struct():
1767 return self.struct.needs_conversion(conv, unwrap, direction, self.is_const())
1769 if self.is_handle():
1770 # non-pointer handles are handled inline in thunks
1771 if not self.is_dynamic_array() and not self.is_static_array():
1772 return conv and self.is_pointer() and self.handle.is_dispatchable()
1774 # vkAllocateCommandBuffers is a special case, we use it in our private thunk as an input param
1775 param_direction = (Direction.INPUT if self.is_const() else Direction.OUTPUT)
1776 if self.name == "pCommandBuffers":
1777 param_direction = Direction.INPUT
1778 if direction != param_direction:
1781 if unwrap and self.handle.is_wrapped():
1783 if conv and self.handle.is_dispatchable():
1785 elif self.is_pointer() and self.is_pointer_size():
1790 def needs_variable(self, conv, unwrap):
1791 if self.needs_conversion(conv, unwrap, Direction.INPUT):
1793 if self.needs_conversion(conv, unwrap, Direction.OUTPUT):
1798 """ Generate spec file entry for this parameter. """
1800 if self.is_pointer() and self.type == "char":
1802 if self.is_dispatchable() or self.is_pointer() or self.is_static_array():
1804 if self.type_info["category"] in ["bitmask"]:
1805 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1806 if self.type_info["data"].type == "VkFlags64":
1810 if self.type_info["category"] in ["enum"]:
1812 if self.is_handle() and not self.is_dispatchable():
1814 if self.type == "float":
1816 if self.type in ["int", "int32_t", "size_t", "uint16_t", "uint32_t", "VkBool32"]:
1818 if self.type in ["uint64_t", "VkDeviceSize"]:
1821 LOGGER.error("Unhandled spec conversion for type: {0}".format(self.type))
1823 def variable(self, conv, unwrap, params_prefix=""):
1824 """ Returns 'glue' code during generation of a function call on how to access the variable.
1825 This function handles various scenarios such as 'unwrapping' if dispatchable objects and
1826 renaming of parameters in case of win32 -> host conversion.
1829 conv (bool, optional): Enable conversion if the param needs it. This appends '_host' to the name.
1832 # Hack until we enable allocation callbacks from ICD to application. These are a joy
1833 # to enable one day, because of calling convention conversion.
1834 if unwrap and "VkAllocationCallbacks" in self.type:
1835 LOGGER.debug("TODO: setting NULL VkAllocationCallbacks for {0}".format(self.name))
1838 if self.needs_variable(conv, unwrap):
1839 if self.is_dynamic_array() or self.optional:
1840 return "{0}_host".format(self.name)
1842 return "&{0}_host".format(self.name)
1844 p = self.value(params_prefix, conv)
1847 unwrap_handle = None
1848 if self.object_type != None and self.type == "uint64_t":
1849 unwrap_handle = "wine_vk_unwrap_handle({0}{1}, {0}{2})".format(
1850 params_prefix, self.object_type, self.name)
1852 elif self.is_handle():
1853 # We need to pass the native handle to the native Vulkan calls and
1854 # the wine driver's handle to calls which are wrapped by the driver.
1855 unwrap_handle = self.handle.driver_handle(p)
1858 unwrap_handle = "{0}{1} ? {2} : 0".format(params_prefix, self.name, unwrap_handle)
1859 return unwrap_handle
1864 class VkStruct(Sequence):
1865 """ Class which represents the type union and struct. """
1867 def __init__(self, name, members, returnedonly, structextends, alias=None, union=False):
1869 self.members = members
1870 self.returnedonly = returnedonly
1871 self.structextends = structextends
1872 self.required = False
1875 self.type_info = None # To be set later.
1876 self.struct_extensions = []
1877 self.aliased_by = []
1879 def __getitem__(self, i):
1880 return self.members[i]
1883 return len(self.members)
1886 def from_alias(struct, alias):
1887 name = struct.attrib.get("name")
1888 aliasee = VkStruct(name, alias.members, alias.returnedonly, alias.structextends, alias=alias)
1890 alias.add_aliased_by(aliasee)
1894 def from_xml(struct):
1895 # Unions and structs are the same parsing wise, but we need to
1896 # know which one we are dealing with later on for code generation.
1897 union = True if struct.attrib["category"] == "union" else False
1899 name = struct.attrib.get("name")
1901 # 'Output' structures for which data is filled in by the API are
1902 # marked as 'returnedonly'.
1903 returnedonly = True if struct.attrib.get("returnedonly") else False
1905 # Those structs seem to be broken in spec, they are specified as
1906 # returned only, but documented as input structs.
1907 if name in ["VkSubpassShadingPipelineCreateInfoHUAWEI",
1908 "VkPipelineShaderStageRequiredSubgroupSizeCreateInfo"]:
1909 returnedonly = False
1911 # Those structs don't have returnedonly in spec, but they could (should?).
1912 if name in ["VkSurfaceCapabilitiesPresentBarrierNV",
1913 "VkCooperativeMatrixPropertiesNV",
1914 "VkPerformanceValueINTEL"]:
1917 structextends = struct.attrib.get("structextends")
1918 structextends = structextends.split(",") if structextends else []
1920 s = VkStruct(name, [], returnedonly, structextends, union=union)
1921 for member in struct.findall("member"):
1922 vk_member = VkMember.from_xml(member, returnedonly, s)
1923 s.members.append(vk_member)
1928 def decouple_structs(structs):
1929 """ Helper function which decouples a list of structs.
1930 Structures often depend on other structures. To make the C compiler
1931 happy we need to define 'substructures' first. This function analyzes
1932 the list of structures and reorders them in such a way that they are
1936 tmp_structs = list(structs) # Don't modify the original structures.
1937 decoupled_structs = []
1939 while (len(tmp_structs) > 0):
1940 # Iterate over a copy because we want to modify the list inside the loop.
1941 for struct in list(tmp_structs):
1944 if not struct.required:
1945 tmp_structs.remove(struct)
1949 if not (m.is_struct() or m.is_union()):
1952 # VkBaseInstructure and VkBaseOutStructure reference themselves.
1953 if m.type == struct.name:
1957 # Check if a struct we depend on has already been defined.
1958 for s in decoupled_structs:
1959 if s.name == m.type:
1964 # Check if the struct we depend on is even in the list of structs.
1965 # If found now, it means we haven't met all dependencies before we
1966 # can operate on the current struct.
1967 # When generating 'host' structs we may not be able to find a struct
1968 # as the list would only contain the structs requiring conversion.
1969 for s in tmp_structs:
1970 if s.name == m.type:
1974 if dependends == False:
1975 decoupled_structs.append(struct)
1976 tmp_structs.remove(struct)
1978 return decoupled_structs
1980 def definition(self, align=False, conv=False):
1981 """ Convert structure to textual definition.
1984 align (bool, optional): enable alignment to 64-bit for win32 struct compatibility.
1985 conv (bool, optional): enable struct conversion if the struct needs it.
1986 postfix (str, optional): text to append to end of struct name, useful for struct renaming.
1992 suffix = "32" if conv else ""
1994 text = "typedef union {0}".format(self.name)
1996 text = "typedef struct {0}".format(self.name)
2002 if align and m.needs_alignment():
2003 text += " {0};\n".format(m.definition(align=align, conv=conv))
2005 text += " {0};\n".format(m.definition(conv=conv))
2007 text += "}} {0}{1};\n".format(self.name, suffix)
2009 for aliasee in self.aliased_by:
2010 text += "typedef {0}{2} {1}{2};\n".format(self.name, aliasee.name, suffix)
2015 return bool(self.alias)
2017 def add_aliased_by(self, aliasee):
2018 self.aliased_by.append(aliasee)
2020 def needs_alignment(self):
2021 """ Check if structure needs alignment for 64-bit data.
2022 Various structures need alignment on 64-bit variables due
2023 to compiler differences on 32-bit between Win32 and Linux.
2026 for m in self.members:
2027 if self.name == m.type:
2029 if m.needs_alignment():
2033 def needs_unwrapping(self):
2034 """ Returns if struct members need unwrapping of handle. """
2036 for m in self.members:
2037 if self.name == m.type:
2039 if m.needs_unwrapping():
2043 def needs_extensions_conversion(self, conv, direction):
2044 """ Check if struct contains extensions chain that needs to be converted """
2046 if direction == Direction.INPUT and self.name in STRUCT_CHAIN_CONVERSIONS:
2049 if not "pNext" in self:
2051 is_const = self.members[self.members.index("pNext")].is_const()
2052 # VkOpticalFlowSessionCreateInfoNV is missing const in its pNext pointer
2053 if self.name in ["VkOpticalFlowSessionCreateInfoNV",
2054 "VkDescriptorBufferBindingInfoEXT"]:
2056 needs_output_copy = False
2058 for e in self.struct_extensions:
2061 if e.needs_conversion(conv, True, direction, is_const, check_extensions=False):
2063 if direction == Direction.INPUT:
2064 # we need input conversion of structs containing struct chain even if it's returnedonly,
2065 # so that we have a chance to allocate buffers
2066 if e.needs_conversion(conv, True, Direction.OUTPUT, is_const, check_extensions=False):
2071 def needs_conversion(self, conv, unwrap, direction, is_const, check_extensions=True):
2072 """ Check if struct needs conversion. """
2074 # VkAllocationCallbacks never needs conversion
2075 if self.name == "VkAllocationCallbacks":
2078 # pFixedRateFlags field is missing const, but it doesn't need output conversion
2079 if direction == Direction.OUTPUT and self.name == "VkImageCompressionControlEXT":
2082 needs_output_copy = False
2084 for m in self.members:
2085 if self.name == m.type:
2088 if m.name == "pNext":
2089 # pNext is a pointer, so it always needs conversion
2090 if conv and direction == Direction.INPUT:
2092 # we need input conversion of structs containing struct chain even if it's returnedonly
2093 if direction == Direction.INPUT and \
2094 self.needs_conversion(conv, unwrap, Direction.OUTPUT, is_const):
2098 # for non-pointer members, check for returnedonly and const attributes
2099 if not m.is_pointer() or m.type == "void":
2100 if direction == Direction.INPUT:
2101 if self.returnedonly:
2104 if is_const or m.is_const():
2107 # check alignment and pointer-sized members for 32-bit conversions
2108 if conv and (direction == Direction.INPUT or not is_const):
2109 if m.is_pointer() or m.is_pointer_size():
2111 # we don't check structs here, they will will be traversed by needs_conversion chain anyway
2112 if not m.is_struct() and m.needs_alignment():
2115 if m.needs_conversion(conv, unwrap, direction, is_const):
2118 # pointers will be handled by needs_conversion, but if we have any other non-const
2119 # member, we may need to copy output
2120 if direction == Direction.OUTPUT and not m.is_pointer() and not is_const and not m.is_const():
2121 needs_output_copy = True
2123 # if output needs any copy and we need input conversion, then we also need output conversion
2124 if needs_output_copy and self.needs_conversion(conv, unwrap, Direction.INPUT, check_extensions):
2127 return check_extensions and self.needs_extensions_conversion(conv, direction)
2129 def needs_alloc(self, conv, unwrap):
2130 """ Check if any struct member needs some memory allocation."""
2132 if self.needs_extensions_conversion(conv, Direction.INPUT):
2135 for m in self.members:
2136 if self.name == m.type:
2138 if m.needs_alloc(conv, unwrap):
2143 def needs_win32_type(self):
2144 # VkAllocationCallbacks never needs conversion
2145 if self.name == "VkAllocationCallbacks":
2148 for m in self.members:
2149 if self.name == m.type:
2151 if m.is_pointer() or m.is_pointer_size():
2153 if m.needs_alignment():
2155 if (m.is_struct() or m.is_union()) and m.struct.needs_win32_type():
2158 def set_type_info(self, types):
2159 """ Helper function to set type information from the type registry.
2160 This is needed, because not all type data is available at time of
2163 for m in self.members:
2164 type_info = types[m.type]
2165 m.set_type_info(type_info)
2167 def get_conversions(self, unwrap, parent_const):
2170 # Collect any conversion for any extension structs.
2171 for e in self.struct_extensions:
2174 conversions.extend(e.get_conversions(True, parent_const))
2176 # Collect any conversion for any member structs.
2178 if m.type == self.name:
2180 conversions.extend(m.get_conversions(unwrap, parent_const))
2185 class StructConversionFunction(object):
2186 def __init__(self, struct, direction, conv, unwrap, const):
2187 self.direction = direction
2188 self.operand = struct
2189 self.type = struct.name
2191 self.unwrap = unwrap or not self.operand.needs_unwrapping()
2194 name = "convert_{0}_".format(self.type)
2195 win_type = "win32" if self.conv else "win64"
2196 host_part = "host" if self.unwrap else "unwrapped_host"
2197 if self.direction == Direction.INPUT:
2198 name += "{0}_to_{1}".format(win_type, host_part)
2199 else: # Direction.OUTPUT
2200 name += "{0}_to_{1}".format(host_part, win_type)
2203 def __eq__(self, other):
2204 return self.name == other.name
2206 def member_needs_copy(self, struct, m):
2207 if self.direction == Direction.OUTPUT:
2208 if m.name in ["sType", "pNext"]:
2210 if self.const and not m.is_pointer():
2212 if m.is_const() and not m.needs_conversion(self.conv, self.unwrap, Direction.OUTPUT, self.const):
2215 if m.name == "pNext":
2217 if m.name != "sType" and struct.returnedonly and not m.needs_conversion(
2218 self.conv, self.unwrap, Direction.INPUT, self.const):
2222 def definition(self):
2223 """ Helper function for generating a struct conversion function. """
2225 # It doesn't make sense to generate conversion functions for non-struct variables
2226 # which aren't in arrays, as this should be handled by the copy() function
2227 if not isinstance(self.operand, VkStruct):
2233 body += "#ifdef _WIN64\n"
2235 needs_alloc = self.direction != Direction.OUTPUT and self.operand.needs_alloc(self.conv, self.unwrap)
2236 win_type = self.type
2237 if self.conv and self.operand.needs_win32_type():
2239 if self.direction == Direction.OUTPUT and self.const:
2240 win_type = "const " + win_type
2243 body += "static inline void {0}(".format(self.name)
2245 if self.direction == Direction.OUTPUT:
2246 params = ["const {0} *in".format(self.type), "{0} *out".format(win_type)]
2248 params = ["const {0} *in".format(win_type), "{0} *out".format(self.type)]
2250 if self.operand.union:
2251 params.append("VkFlags selector")
2253 # Generate parameter list
2255 body += "struct conversion_context *ctx, "
2256 body += ", ".join(p for p in params)
2260 body += "static inline void {0}(".format(self.name)
2262 params = ["const {0} *in".format(self.type), "{0} *out".format(self.type)]
2264 # Generate parameter list
2266 body += "struct conversion_context *ctx, "
2267 body += ", ".join(p for p in params)
2270 needs_extensions = self.operand.needs_extensions_conversion(self.conv, self.direction)
2273 if needs_extensions:
2274 if self.direction == Direction.INPUT:
2276 body += " const VkBaseInStructure32 *in_header;\n"
2278 body += " const VkBaseInStructure *in_header;\n"
2279 body += " VkBaseOutStructure *out_header = (void *)out;\n\n"
2281 body += " const VkBaseInStructure *in_header;\n"
2283 body += " VkBaseOutStructure32 *out_header = (void *)out;\n\n"
2285 body += " VkBaseOutStructure *out_header = (void *)out;\n\n"
2287 body += " if (!in) return;\n\n"
2289 for m in self.operand:
2290 if not self.member_needs_copy(self.operand, m):
2292 if m.name == "pNext" and (needs_extensions or self.conv):
2293 body += " out->pNext = NULL;\n"
2298 body += " || ".join("selector == {}".format(s) for s in m.selection)
2301 body += " " + m.copy("in->", "out->", self.direction, self.conv, self.unwrap)
2303 if needs_extensions:
2304 if self.conv and self.direction == Direction.INPUT:
2305 body += "\n for (in_header = UlongToPtr(in->pNext); in_header; in_header = UlongToPtr(in_header->pNext))\n"
2307 body += "\n for (in_header = (void *)in->pNext; in_header; in_header = (void *)in_header->pNext)\n"
2309 body += " switch (in_header->sType)\n"
2314 if self.direction == Direction.INPUT and self.type in STRUCT_CHAIN_CONVERSIONS:
2315 for i in STRUCT_CHAIN_CONVERSIONS[self.type]:
2316 body += " case {0}:\n".format(i)
2317 body += ident + "break;\n"
2319 for ext in self.operand.struct_extensions:
2320 if not ext.required:
2323 if self.direction == Direction.OUTPUT and not any([self.member_needs_copy(ext, m) for m in ext]):
2326 stype = next(x for x in ext.members if x.name == "sType").values
2327 win_type = ext.name + "32" if self.conv and ext.needs_win32_type() else ext.name
2328 if self.direction == Direction.INPUT:
2329 in_type = "const " + win_type
2332 in_type = "const " + ext.name
2335 body += " case {0}:\n".format(stype)
2337 if self.direction == Direction.INPUT:
2338 body += ident + "{0} *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));\n".format(out_type)
2340 body += ident + "{0} *out_ext = find_next_struct32(out_header, {1});\n".format(out_type, stype)
2342 body += ident + "{0} *out_ext = find_next_struct(out_header, {1});\n".format(out_type, stype)
2347 if m.name == "sType":
2348 copy_body += ident + "out_ext->sType = {0};\n".format(stype)
2350 if not self.member_needs_copy(ext, m):
2352 if m.name == "pNext":
2353 copy_body += ident + "out_ext->pNext = NULL;\n"
2356 copy_body += ident + m.copy("in_ext->", "out_ext->", self.direction, self.conv, True)
2358 # Generate the definition of "in_ext" if we need it
2359 if "in_ext->" in copy_body:
2360 body += ident + "{0} *in_ext = ({0} *)in_header;\n".format(in_type)
2363 if self.direction == Direction.INPUT:
2364 body += ident + "out_header->pNext = (void *)out_ext;\n"
2365 body += ident + "out_header = (void *)out_ext;\n"
2366 body += ident + "break;\n"
2369 body += " default:\n"
2370 if self.direction == Direction.INPUT:
2371 body += ident + "FIXME(\"Unhandled sType %u.\", in_header->sType);\n"
2375 elif self.conv and self.direction == Direction.INPUT and "pNext" in self.operand:
2376 body += " if (in->pNext)\n"
2377 body += " FIXME(\"Unexpected pNext\\n\");\n"
2381 body += "#endif /* _WIN64 */\n"
2387 class ArrayConversionFunction(object):
2388 def __init__(self, array, direction, conv, unwrap):
2390 self.direction = direction
2391 self.type = array.type
2393 self.unwrap = unwrap or not array.needs_unwrapping()
2395 if array.is_static_array() and direction == Direction.INPUT:
2396 LOGGER.error("Static array input conversion is not supported")
2398 name = "convert_{0}_".format(array.type)
2399 if array.pointer_array:
2402 win_type = "win32" if self.conv else "win64"
2403 host_part = "host" if self.unwrap else "unwrapped_host"
2404 if self.direction == Direction.INPUT:
2405 name += "{0}_to_{1}".format(win_type, host_part)
2406 else: # Direction.OUTPUT
2407 name += "{0}_to_{1}".format(host_part, win_type)
2410 def __eq__(self, other):
2411 return self.name == other.name
2413 def definition(self):
2414 """ Helper function for generating a conversion function for array operands. """
2419 body += "#ifdef _WIN64\n"
2421 needs_alloc = self.direction != Direction.OUTPUT and self.array.needs_alloc(self.conv, self.unwrap)
2423 win_type = self.type
2425 if self.array.needs_win32_type():
2427 elif self.array.is_handle() and self.array.handle.is_dispatchable():
2429 if self.direction == Direction.OUTPUT and self.array.is_const():
2430 win_type = "const " + win_type
2431 pointer_part = self.array.pointer if self.array.pointer else "*"
2433 if self.direction == Direction.OUTPUT:
2434 params = ["const {0} {1}in".format(self.type, pointer_part),
2435 "{0} {1}out".format(win_type, pointer_part), "uint32_t count"]
2437 elif self.conv and self.array.pointer_array:
2438 params = ["const PTR32 *in", "uint32_t count"]
2439 return_type = self.type
2441 params = ["const {0} {1}in".format(win_type, pointer_part), "uint32_t count"]
2442 return_type = self.type
2444 needs_copy = not self.array.is_struct() or self.direction != Direction.INPUT or \
2445 not self.array.struct.returnedonly or "pNext" in self.array.struct
2447 # Generate function prototype.
2449 body += "static inline {0}{1} {2}{3}(".format(
2450 "const " if self.array.is_const() else "", return_type, pointer_part, self.name)
2452 body += "static inline void {0}(".format(self.name)
2454 body += "struct conversion_context *ctx, "
2455 body += ", ".join(p for p in params)
2459 body += " {0} {1}out;\n".format(return_type, "**" if self.array.pointer_array else "*")
2461 body += " unsigned int i;\n\n"
2464 body += " if (!in || !count) return NULL;\n\n"
2466 body += " if (!in) return;\n\n"
2468 if self.direction == Direction.INPUT:
2469 body += " out = conversion_context_alloc(ctx, count * sizeof(*out));\n"
2472 body += " for (i = 0; i < count; i++)\n"
2475 if self.array.is_struct():
2476 struct = self.array.struct
2477 win_part = "win32" if self.conv else "win64"
2478 host_part = "host" if self.unwrap else "unwrapped_host"
2479 if self.direction == Direction.INPUT:
2480 conv_suffix = "{0}_to_{1}".format(win_part, host_part)
2482 conv_suffix = "{0}_to_{1}".format(host_part, win_part)
2485 if self.direction == Direction.INPUT and struct.needs_alloc(self.conv, self.unwrap):
2488 if not self.array.pointer_array:
2489 body += " convert_{0}_{1}({2}&in[i], &out[i]);\n".format(
2490 struct.name, conv_suffix, ctx_part)
2492 if struct.needs_conversion(self.conv, self.unwrap, self.direction, False):
2493 body += " if (in[i])\n"
2495 body += " out[i] = conversion_context_alloc(ctx, sizeof(*out[i]));\n"
2497 in_param = "({0} *)UlongToPtr(in[i])".format(win_type)
2500 body += " convert_{0}_{1}({2}{3}, out[i]);\n".format(
2501 struct.name, conv_suffix, ctx_part, in_param)
2504 body += " out[i] = NULL;\n"
2506 body += " out[i] = UlongToPtr(in[i]);\n".format(win_type)
2507 elif self.array.is_handle():
2508 if self.array.pointer_array:
2509 LOGGER.error("Unhandled handle pointer arrays")
2510 handle = self.array.handle
2511 if not self.conv or not handle.is_dispatchable():
2513 elif self.direction == Direction.INPUT:
2514 input = "UlongToPtr(in[i])"
2516 input = "PtrToUlong(in[i])"
2518 if not self.unwrap or not handle.is_wrapped():
2519 body += " out[i] = {0};\n".format(input)
2520 elif self.direction == Direction.INPUT:
2521 body += " out[i] = " + handle.driver_handle(input) + ";\n"
2523 LOGGER.warning("Unhandled handle output conversion")
2524 elif self.array.pointer_array:
2525 body += " out[i] = UlongToPtr(in[i]);\n"
2527 body += " out[i] = in[i];\n"
2532 body += "\n return {0}out;\n".format("(void *)" if self.array.pointer_array else "")
2536 body += "#endif /* _WIN64 */\n"
2543 class VkGenerator(object):
2544 def __init__(self, registry):
2545 self.registry = registry
2547 # Build a list conversion functions for struct conversion.
2548 self.conversions = []
2549 self.win32_structs = []
2550 for func in self.registry.funcs.values():
2551 if not func.needs_exposing():
2554 conversions = func.get_conversions()
2555 for conv in conversions:
2556 # Append if we don't already have this conversion.
2557 if not any(c == conv for c in self.conversions):
2558 self.conversions.append(conv)
2560 if not isinstance(conv, StructConversionFunction):
2563 for e in conv.operand.struct_extensions:
2564 if not e.required or not e.needs_win32_type():
2566 if not any(s.name == e.name for s in self.win32_structs):
2567 self.win32_structs.append(e)
2569 if not conv.operand.needs_win32_type():
2572 # Structs can be used in different ways by different conversions
2573 # e.g. array vs non-array. Just make sure we pull in each struct once.
2574 if not any(s.name == conv.operand.name for s in self.win32_structs):
2575 self.win32_structs.append(conv.operand)
2577 def _generate_copyright(self, f, spec_file=False):
2578 f.write("# " if spec_file else "/* ")
2579 f.write("Automatically generated from Vulkan vk.xml; DO NOT EDIT!\n")
2580 lines = ["", "This file is generated from Vulkan vk.xml file covered",
2581 "by the following copyright and permission notice:"]
2582 lines.extend([l.rstrip(" ") for l in self.registry.copyright.splitlines()])
2584 f.write("{0}{1}".format("# " if spec_file else " * ", line).rstrip(" ") + "\n")
2585 f.write("\n" if spec_file else " */\n\n")
2587 def generate_thunks_c(self, f):
2588 self._generate_copyright(f)
2591 f.write("#pragma makedep unix\n")
2592 f.write("#endif\n\n")
2594 f.write("#include \"config.h\"\n\n")
2596 f.write("#include <stdlib.h>\n\n")
2598 f.write("#include \"vulkan_private.h\"\n\n")
2600 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2602 for struct in self.win32_structs:
2603 f.write(struct.definition(conv=True, align=True))
2606 f.write("static uint64_t wine_vk_unwrap_handle(uint32_t type, uint64_t handle)\n")
2608 f.write(" switch(type)\n")
2610 for handle in self.registry.handles:
2611 if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2613 f.write(" case {}:\n".format(handle.object_type))
2614 if handle.is_dispatchable():
2615 f.write(" return (uint64_t) (uintptr_t) ")
2616 f.write(handle.native_handle("(({}) (uintptr_t) handle)".format(handle.name)))
2618 f.write(" return (uint64_t) ")
2619 f.write(handle.native_handle("handle"))
2621 f.write(" default:\n")
2622 f.write(" return handle;\n")
2626 # Generate any conversion helper functions.
2627 for conv in self.conversions:
2628 f.write(conv.definition())
2630 # Create thunks for instance and device functions.
2631 # Global functions don't go through the thunks.
2632 for vk_func in self.registry.funcs.values():
2633 if not vk_func.needs_exposing():
2635 if vk_func.loader_thunk_type == ThunkType.NONE:
2638 f.write(vk_func.thunk(prefix="thunk64_"))
2639 f.write(vk_func.thunk(prefix="thunk32_", conv=True))
2641 # Create array of device extensions.
2642 f.write("static const char * const vk_device_extensions[] =\n{\n")
2643 for ext in self.registry.extensions:
2644 if ext["type"] != "device":
2646 if ext["name"] in UNEXPOSED_EXTENSIONS:
2649 f.write(" \"{0}\",\n".format(ext["name"]))
2652 # Create array of instance extensions.
2653 f.write("static const char * const vk_instance_extensions[] =\n{\n")
2654 for ext in self.registry.extensions:
2655 if ext["type"] != "instance":
2657 if ext["name"] in UNEXPOSED_EXTENSIONS:
2660 f.write(" \"{0}\",\n".format(ext["name"]))
2663 f.write("BOOL wine_vk_device_extension_supported(const char *name)\n")
2665 f.write(" unsigned int i;\n")
2666 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_extensions); i++)\n")
2668 f.write(" if (strcmp(vk_device_extensions[i], name) == 0)\n")
2669 f.write(" return TRUE;\n")
2671 f.write(" return FALSE;\n")
2674 f.write("BOOL wine_vk_instance_extension_supported(const char *name)\n")
2676 f.write(" unsigned int i;\n")
2677 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_extensions); i++)\n")
2679 f.write(" if (strcmp(vk_instance_extensions[i], name) == 0)\n")
2680 f.write(" return TRUE;\n")
2682 f.write(" return FALSE;\n")
2685 f.write("BOOL wine_vk_is_type_wrapped(VkObjectType type)\n")
2687 f.write(" return FALSE")
2688 for handle in self.registry.handles:
2689 if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2691 f.write(" ||\n type == {}".format(handle.object_type))
2696 f.write("#ifdef _WIN64\n\n")
2698 f.write("const unixlib_entry_t __wine_unix_call_funcs[] =\n")
2700 f.write(" init_vulkan,\n")
2701 f.write(" vk_is_available_instance_function,\n")
2702 f.write(" vk_is_available_device_function,\n")
2703 for vk_func in self.registry.funcs.values():
2704 if not vk_func.needs_exposing():
2706 if vk_func.loader_thunk_type == ThunkType.NONE:
2709 f.write(" {1}{0},\n".format(vk_func.name, "thunk64_"))
2711 f.write("C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_count);\n\n")
2713 f.write("#endif /* _WIN64 */\n\n")
2715 f.write("#ifdef _WIN64\n")
2716 f.write("const unixlib_entry_t __wine_unix_call_wow64_funcs[] =\n")
2718 f.write("const unixlib_entry_t __wine_unix_call_funcs[] =\n")
2721 f.write(" init_vulkan,\n")
2722 f.write(" vk_is_available_instance_function32,\n")
2723 f.write(" vk_is_available_device_function32,\n")
2724 for vk_func in self.registry.funcs.values():
2725 if not vk_func.needs_exposing():
2727 if vk_func.loader_thunk_type == ThunkType.NONE:
2730 f.write(" {1}{0},\n".format(vk_func.name, "thunk32_"))
2732 f.write("C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_count);\n")
2734 def generate_thunks_h(self, f, prefix):
2735 self._generate_copyright(f)
2737 f.write("#ifndef __WINE_VULKAN_THUNKS_H\n")
2738 f.write("#define __WINE_VULKAN_THUNKS_H\n\n")
2740 f.write("#define WINE_VK_VERSION VK_API_VERSION_{0}_{1}\n\n".format(WINE_VK_VERSION[0], WINE_VK_VERSION[1]))
2742 # Generate prototypes for device and instance functions requiring a custom implementation.
2743 f.write("/* Functions for which we have custom implementations outside of the thunks. */\n")
2744 for vk_func in self.registry.funcs.values():
2745 if not vk_func.needs_private_thunk():
2748 f.write("{0};\n".format(vk_func.prototype(prefix=prefix, postfix="DECLSPEC_HIDDEN", is_thunk=True)))
2751 f.write("/* For use by vkDevice and children */\n")
2752 f.write("struct vulkan_device_funcs\n{\n")
2753 for vk_func in self.registry.device_funcs:
2754 if not vk_func.needs_exposing():
2757 if not vk_func.needs_dispatch():
2758 LOGGER.debug("skipping {0} in vulkan_device_funcs".format(vk_func.name))
2761 f.write(" {0};\n".format(vk_func.pfn()))
2764 f.write("/* For use by vkInstance and children */\n")
2765 f.write("struct vulkan_instance_funcs\n{\n")
2766 for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs:
2767 if not vk_func.needs_exposing():
2770 if not vk_func.needs_dispatch():
2771 LOGGER.debug("skipping {0} in vulkan_instance_funcs".format(vk_func.name))
2774 f.write(" {0};\n".format(vk_func.pfn()))
2777 f.write("#define ALL_VK_DEVICE_FUNCS() \\\n")
2779 for vk_func in self.registry.device_funcs:
2780 if not vk_func.needs_exposing():
2783 if not vk_func.needs_dispatch():
2784 LOGGER.debug("skipping {0} in ALL_VK_DEVICE_FUNCS".format(vk_func.name))
2788 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2791 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2794 f.write("#define ALL_VK_INSTANCE_FUNCS() \\\n")
2796 for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs:
2797 if not vk_func.needs_exposing():
2800 if not vk_func.needs_dispatch():
2801 LOGGER.debug("skipping {0} in ALL_VK_INSTANCE_FUNCS".format(vk_func.name))
2805 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2808 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2811 f.write("#endif /* __WINE_VULKAN_THUNKS_H */\n")
2813 def generate_loader_thunks_c(self, f):
2814 self._generate_copyright(f)
2816 f.write("#include \"vulkan_loader.h\"\n\n")
2818 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2820 for vk_func in self.registry.funcs.values():
2821 if not vk_func.needs_exposing():
2823 if vk_func.loader_thunk_type != ThunkType.PUBLIC:
2826 f.write(vk_func.loader_thunk())
2828 f.write("static const struct vulkan_func vk_device_dispatch_table[] =\n{\n")
2829 for vk_func in self.registry.device_funcs:
2830 if not vk_func.needs_exposing():
2833 f.write(" {{\"{0}\", {0}}},\n".format(vk_func.name))
2836 f.write("static const struct vulkan_func vk_phys_dev_dispatch_table[] =\n{\n")
2837 for vk_func in self.registry.phys_dev_funcs:
2838 if not vk_func.needs_exposing():
2841 f.write(" {{\"{0}\", {0}}},\n".format(vk_func.name))
2844 f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
2845 for vk_func in self.registry.instance_funcs:
2846 if not vk_func.needs_exposing():
2849 f.write(" {{\"{0}\", {0}}},\n".format(vk_func.name))
2852 f.write("void *wine_vk_get_device_proc_addr(const char *name)\n")
2854 f.write(" unsigned int i;\n")
2855 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_dispatch_table); i++)\n")
2857 f.write(" if (strcmp(vk_device_dispatch_table[i].name, name) == 0)\n")
2859 f.write(" TRACE(\"Found name=%s in device table\\n\", debugstr_a(name));\n")
2860 f.write(" return vk_device_dispatch_table[i].func;\n")
2863 f.write(" return NULL;\n")
2866 f.write("void *wine_vk_get_phys_dev_proc_addr(const char *name)\n")
2868 f.write(" unsigned int i;\n")
2869 f.write(" for (i = 0; i < ARRAY_SIZE(vk_phys_dev_dispatch_table); i++)\n")
2871 f.write(" if (strcmp(vk_phys_dev_dispatch_table[i].name, name) == 0)\n")
2873 f.write(" TRACE(\"Found name=%s in physical device table\\n\", debugstr_a(name));\n")
2874 f.write(" return vk_phys_dev_dispatch_table[i].func;\n")
2877 f.write(" return NULL;\n")
2880 f.write("void *wine_vk_get_instance_proc_addr(const char *name)\n")
2882 f.write(" unsigned int i;\n")
2883 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_dispatch_table); i++)\n")
2885 f.write(" if (strcmp(vk_instance_dispatch_table[i].name, name) == 0)\n")
2887 f.write(" TRACE(\"Found name=%s in instance table\\n\", debugstr_a(name));\n")
2888 f.write(" return vk_instance_dispatch_table[i].func;\n")
2891 f.write(" return NULL;\n")
2894 def generate_loader_thunks_h(self, f):
2895 self._generate_copyright(f)
2897 f.write("#ifndef __WINE_VULKAN_LOADER_THUNKS_H\n")
2898 f.write("#define __WINE_VULKAN_LOADER_THUNKS_H\n\n")
2900 f.write("enum unix_call\n")
2902 f.write(" unix_init,\n")
2903 f.write(" unix_is_available_instance_function,\n")
2904 f.write(" unix_is_available_device_function,\n")
2905 for vk_func in self.registry.funcs.values():
2906 if not vk_func.needs_exposing():
2908 if vk_func.loader_thunk_type == ThunkType.NONE:
2911 f.write(" unix_{0},\n".format(vk_func.name))
2912 f.write(" unix_count,\n")
2915 for vk_func in self.registry.funcs.values():
2916 if not vk_func.needs_exposing():
2918 if vk_func.loader_thunk_type == ThunkType.NONE:
2921 f.write("struct {0}_params\n".format(vk_func.name))
2923 for p in vk_func.params:
2924 f.write(" {0};\n".format(p.definition(is_member=True)))
2925 if vk_func.extra_param:
2926 f.write(" void *{0};\n".format(vk_func.extra_param))
2927 if vk_func.type != "void":
2928 f.write(" {0} result;\n".format(vk_func.type))
2931 f.write("#endif /* __WINE_VULKAN_LOADER_THUNKS_H */\n")
2933 def generate_vulkan_h(self, f):
2934 self._generate_copyright(f)
2935 f.write("#ifndef __WINE_VULKAN_H\n")
2936 f.write("#define __WINE_VULKAN_H\n\n")
2938 f.write("#include <windef.h>\n")
2939 f.write("#include <stdint.h>\n\n")
2941 f.write("/* Define WINE_VK_HOST to get 'host' headers. */\n")
2942 f.write("#ifdef WINE_VK_HOST\n")
2943 f.write("#define VKAPI_CALL\n")
2944 f.write('#define WINE_VK_ALIGN(x)\n')
2945 f.write("#endif\n\n")
2947 f.write("#ifndef VKAPI_CALL\n")
2948 f.write("#define VKAPI_CALL __stdcall\n")
2949 f.write("#endif\n\n")
2951 f.write("#ifndef VKAPI_PTR\n")
2952 f.write("#define VKAPI_PTR VKAPI_CALL\n")
2953 f.write("#endif\n\n")
2955 f.write("#ifndef WINE_VK_ALIGN\n")
2956 f.write("#define WINE_VK_ALIGN DECLSPEC_ALIGN\n")
2957 f.write("#endif\n\n")
2959 # The overall strategy is to define independent constants and datatypes,
2960 # prior to complex structures and function calls to avoid forward declarations.
2961 for const in self.registry.consts:
2962 # For now just generate things we may not need. The amount of parsing needed
2963 # to get some of the info is tricky as you need to figure out which structure
2964 # references a certain constant.
2965 f.write(const.definition())
2968 for define in self.registry.defines:
2969 f.write(define.definition())
2971 for handle in self.registry.handles:
2972 # For backward compatibility also create definitions for aliases.
2973 # These types normally don't get pulled in as we use the new types
2974 # even in legacy functions if they are aliases.
2975 if handle.is_required() or handle.is_alias():
2976 f.write(handle.definition())
2979 for base_type in self.registry.base_types:
2980 f.write(base_type.definition())
2983 for bitmask in self.registry.bitmasks:
2984 f.write(bitmask.definition())
2987 # Define enums, this includes values for some of the bitmask types as well.
2988 for enum in self.registry.enums.values():
2990 f.write(enum.definition())
2992 for fp in self.registry.funcpointers:
2994 f.write(fp.definition())
2997 # This generates both structures and unions. Since structures
2998 # may depend on other structures/unions, we need a list of
2999 # decoupled structs.
3000 # Note: unions are stored in structs for dependency reasons,
3001 # see comment in parsing section.
3002 structs = VkStruct.decouple_structs(self.registry.structs)
3003 for struct in structs:
3004 LOGGER.debug("Generating struct: {0}".format(struct.name))
3005 f.write(struct.definition(align=True))
3008 for func in self.registry.funcs.values():
3009 if not func.is_required():
3010 LOGGER.debug("Skipping PFN definition for: {0}".format(func.name))
3013 f.write("typedef {0};\n".format(func.pfn(prefix="PFN", call_conv="VKAPI_PTR")))
3016 f.write("#ifndef VK_NO_PROTOTYPES\n")
3017 for func in self.registry.funcs.values():
3018 if not func.is_required():
3019 LOGGER.debug("Skipping API definition for: {0}".format(func.name))
3022 LOGGER.debug("Generating API definition for: {0}".format(func.name))
3023 f.write("{0};\n".format(func.prototype(call_conv="VKAPI_CALL")))
3024 f.write("#endif /* VK_NO_PROTOTYPES */\n\n")
3026 f.write("#endif /* __WINE_VULKAN_H */\n")
3028 def generate_vulkan_driver_h(self, f):
3029 self._generate_copyright(f)
3030 f.write("#ifndef __WINE_VULKAN_DRIVER_H\n")
3031 f.write("#define __WINE_VULKAN_DRIVER_H\n\n")
3033 f.write("/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */\n")
3034 f.write("#define WINE_VULKAN_DRIVER_VERSION {0}\n\n".format(DRIVER_VERSION))
3036 f.write("struct vulkan_funcs\n{\n")
3037 f.write(" /* Vulkan global functions. These are the only calls at this point a graphics driver\n")
3038 f.write(" * needs to provide. Other function calls will be provided indirectly by dispatch\n")
3039 f.write(" * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice.\n")
3042 for vk_func in self.registry.funcs.values():
3043 if not vk_func.is_driver_func():
3047 # Avoid PFN_vkVoidFunction in driver interface as Vulkan likes to put calling convention
3048 # stuff in there. For simplicity substitute with "void *".
3049 pfn = pfn.replace("PFN_vkVoidFunction", "void *")
3050 f.write(" {0};\n".format(pfn))
3053 f.write(" /* winevulkan specific functions */\n")
3054 f.write(" VkSurfaceKHR (*p_wine_get_native_surface)(VkSurfaceKHR);\n")
3057 f.write("extern const struct vulkan_funcs * __wine_get_vulkan_driver(UINT version);\n\n")
3059 f.write("static inline void *get_vulkan_driver_device_proc_addr(\n")
3060 f.write(" const struct vulkan_funcs *vulkan_funcs, const char *name)\n{\n")
3061 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
3062 f.write(" name += 2;\n\n")
3063 for vk_func in self.registry.funcs.values():
3064 if vk_func.is_driver_func() and vk_func.is_device_func():
3065 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3066 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3068 f.write(" return NULL;\n}\n\n")
3070 f.write("static inline void *get_vulkan_driver_instance_proc_addr(\n")
3071 f.write(" const struct vulkan_funcs *vulkan_funcs, VkInstance instance, const char *name)\n{\n")
3072 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
3073 f.write(" name += 2;\n\n")
3074 for vk_func in self.registry.funcs.values():
3075 if vk_func.is_driver_func() and vk_func.is_global_func() and vk_func.name != "vkGetInstanceProcAddr":
3076 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3077 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3079 f.write(" if (!instance) return NULL;\n\n")
3080 for vk_func in self.registry.funcs.values():
3081 if vk_func.is_driver_func() and (vk_func.is_instance_func() or vk_func.is_phys_dev_func()):
3082 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3083 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3085 f.write(" name -= 2;\n\n")
3086 f.write(" return get_vulkan_driver_device_proc_addr(vulkan_funcs, name);\n}\n\n")
3088 f.write("#endif /* __WINE_VULKAN_DRIVER_H */\n")
3090 def generate_vulkan_spec(self, f):
3091 self._generate_copyright(f, spec_file=True)
3092 f.write("@ stdcall -private vk_icdGetInstanceProcAddr(ptr str)\n")
3093 f.write("@ stdcall -private vk_icdGetPhysicalDeviceProcAddr(ptr str)\n")
3094 f.write("@ stdcall -private vk_icdNegotiateLoaderICDInterfaceVersion(ptr)\n")
3096 # Export symbols for all Vulkan Core functions.
3097 for func in self.registry.funcs.values():
3098 if not func.is_core_func():
3101 # We support all Core functions except for VK_KHR_display* APIs.
3102 # Create stubs for unsupported Core functions.
3103 if func.is_required():
3104 f.write(func.spec())
3106 f.write("@ stub {0}\n".format(func.name))
3108 f.write("@ stdcall -private DllRegisterServer()\n")
3109 f.write("@ stdcall -private DllUnregisterServer()\n")
3111 def generate_vulkan_loader_spec(self, f):
3112 self._generate_copyright(f, spec_file=True)
3114 # Export symbols for all Vulkan Core functions.
3115 for func in self.registry.funcs.values():
3116 if not func.is_core_func():
3119 # We support all Core functions except for VK_KHR_display* APIs.
3120 # Create stubs for unsupported Core functions.
3121 if func.is_required():
3122 f.write(func.spec(symbol="winevulkan." + func.name))
3124 f.write("@ stub {0}\n".format(func.name))
3127 class VkRegistry(object):
3128 def __init__(self, reg_filename):
3129 # Used for storage of type information.
3130 self.base_types = None
3131 self.bitmasks = None
3135 self.funcpointers = None
3139 # We aggregate all types in here for cross-referencing.
3143 self.version_regex = re.compile(
3152 # Overall strategy for parsing the registry is to first
3153 # parse all type / function definitions. Then parse
3154 # features and extensions to decide which types / functions
3155 # to actually 'pull in' for code generation. For each type or
3156 # function call we want we set a member 'required' to True.
3157 tree = ET.parse(reg_filename)
3158 root = tree.getroot()
3159 self._parse_enums(root)
3160 self._parse_types(root)
3161 self._parse_commands(root)
3163 # Pull in any required types and functions.
3164 self._parse_features(root)
3165 self._parse_extensions(root)
3167 for enum in self.enums.values():
3168 enum.fixup_64bit_aliases()
3170 self._match_object_types()
3172 self.copyright = root.find('./comment').text
3174 def _is_feature_supported(self, feature):
3175 version = self.version_regex.match(feature)
3179 version = tuple(map(int, version.group('major', 'minor')))
3180 return version <= WINE_VK_VERSION
3182 def _is_extension_supported(self, extension):
3183 # We disable some extensions as either we haven't implemented
3184 # support yet or because they are for platforms other than win32.
3185 return extension not in UNSUPPORTED_EXTENSIONS
3187 def _mark_command_required(self, command):
3188 """ Helper function to mark a certain command and the datatypes it needs as required."""
3189 def mark_bitmask_dependencies(bitmask, types):
3190 if bitmask.requires is not None:
3191 types[bitmask.requires]["data"].required = True
3193 def mark_funcpointer_dependencies(fp, types):
3194 for m in fp.members:
3195 type_info = types[m.type]
3197 # Complex types have a matching definition e.g. VkStruct.
3198 # Not needed for base types such as uint32_t.
3199 if "data" in type_info:
3200 types[m.type]["data"].required = True
3202 def mark_struct_dependencies(struct, types):
3204 type_info = types[m.type]
3206 # Complex types have a matching definition e.g. VkStruct.
3207 # Not needed for base types such as uint32_t.
3208 if "data" in type_info:
3209 types[m.type]["data"].required = True
3211 if type_info["category"] == "struct" and struct.name != m.type:
3213 mark_struct_dependencies(type_info["data"], types)
3214 elif type_info["category"] == "funcpointer":
3215 mark_funcpointer_dependencies(type_info["data"], types)
3216 elif type_info["category"] == "bitmask":
3217 mark_bitmask_dependencies(type_info["data"], types)
3219 func = self.funcs[command]
3220 func.required = True
3222 # Pull in return type
3223 if func.type != "void":
3224 self.types[func.type]["data"].required = True
3226 # Analyze parameter dependencies and pull in any type needed.
3227 for p in func.params:
3228 type_info = self.types[p.type]
3230 # Check if we are dealing with a complex type e.g. VkEnum, VkStruct and others.
3231 if "data" not in type_info:
3234 # Mark the complex type as required.
3235 type_info["data"].required = True
3236 if type_info["category"] == "struct":
3237 struct = type_info["data"]
3238 mark_struct_dependencies(struct, self.types)
3239 elif type_info["category"] == "bitmask":
3240 mark_bitmask_dependencies(type_info["data"], self.types)
3242 def _match_object_types(self):
3243 """ Matches each handle with the correct object type. """
3244 # Use upper case comparison for simplicity.
3246 for value in self.enums["VkObjectType"].values:
3247 object_name = "VK" + value.name[len("VK_OBJECT_TYPE"):].replace("_", "")
3248 object_types[object_name] = value.name
3250 for handle in self.handles:
3251 if not handle.is_required():
3253 handle.object_type = object_types.get(handle.name.upper())
3254 if not handle.object_type:
3255 LOGGER.warning("No object type found for {}".format(handle.name))
3257 def _parse_commands(self, root):
3258 """ Parse command section containing the Vulkan function calls. """
3260 commands = root.findall("./commands/")
3262 # As of Vulkan 1.1, various extensions got promoted to Core.
3263 # The old commands (e.g. KHR) are available for backwards compatibility
3264 # and are marked in vk.xml as 'alias' to the non-extension type.
3265 # The registry likes to avoid data duplication, so parameters and other
3266 # metadata need to be looked up from the Core command.
3267 # We parse the alias commands in a second pass.
3269 for command in commands:
3270 alias_name = command.attrib.get("alias")
3272 alias_commands.append(command)
3275 func = VkFunction.from_xml(command, self.types)
3276 funcs[func.name] = func
3278 for command in alias_commands:
3279 alias_name = command.attrib.get("alias")
3280 alias = funcs[alias_name]
3281 func = VkFunction.from_alias(command, alias)
3282 funcs[func.name] = func
3284 # To make life easy for the code generation, separate all function
3285 # calls out in the 4 types of Vulkan functions:
3286 # device, global, physical device and instance.
3291 for func in funcs.values():
3292 if func.is_device_func():
3293 device_funcs.append(func)
3294 elif func.is_global_func():
3295 global_funcs.append(func)
3296 elif func.is_phys_dev_func():
3297 phys_dev_funcs.append(func)
3299 instance_funcs.append(func)
3301 # Sort function lists by name and store them.
3302 self.device_funcs = sorted(device_funcs, key=lambda func: func.name)
3303 self.global_funcs = sorted(global_funcs, key=lambda func: func.name)
3304 self.phys_dev_funcs = sorted(phys_dev_funcs, key=lambda func: func.name)
3305 self.instance_funcs = sorted(instance_funcs, key=lambda func: func.name)
3307 # The funcs dictionary is used as a convenient way to lookup function
3308 # calls when needed e.g. to adjust member variables.
3309 self.funcs = OrderedDict(sorted(funcs.items()))
3311 def _parse_enums(self, root):
3312 """ Parse enums section or better described as constants section. """
3315 for enum in root.findall("./enums"):
3316 name = enum.attrib.get("name")
3317 _type = enum.attrib.get("type")
3319 if _type in ("enum", "bitmask"):
3320 enums[name] = VkEnum.from_xml(enum)
3322 # If no type is set, we are dealing with API constants.
3323 for value in enum.findall("enum"):
3324 # If enum is an alias, set the value to the alias name.
3325 # E.g. VK_LUID_SIZE_KHR is an alias to VK_LUID_SIZE.
3326 alias = value.attrib.get("alias")
3328 self.consts.append(VkConstant(value.attrib.get("name"), alias))
3330 self.consts.append(VkConstant(value.attrib.get("name"), value.attrib.get("value")))
3332 self.enums = OrderedDict(sorted(enums.items()))
3334 def _process_require_enum(self, enum_elem, ext=None, only_aliased=False):
3335 if "extends" in enum_elem.keys():
3336 enum = self.types[enum_elem.attrib["extends"]]["data"]
3338 # Need to define VkEnumValues which were aliased to by another value. This is necessary
3339 # from VK spec version 1.2.135 where the provisional VK_KHR_ray_tracing extension was
3340 # added which altered VK_NV_ray_tracing's VkEnumValues to alias to the provisional
3343 for _, t in self.types.items():
3344 if t["category"] != "enum":
3348 for value in t["data"].values:
3349 if value.alias == enum_elem.attrib["name"]:
3352 if only_aliased and not aliased:
3355 if "bitpos" in enum_elem.keys():
3356 # We need to add an extra value to an existing enum type.
3357 # E.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG to VkFormatFeatureFlagBits.
3358 enum.create_bitpos(enum_elem.attrib["name"], int(enum_elem.attrib["bitpos"]))
3360 elif "offset" in enum_elem.keys():
3361 # Extensions promoted to Core, have the extension number as part
3362 # of the enum value. Else retrieve from the extension tag.
3363 if enum_elem.attrib.get("extnumber"):
3364 ext_number = int(enum_elem.attrib.get("extnumber"))
3366 ext_number = int(ext.attrib["number"])
3367 offset = int(enum_elem.attrib["offset"])
3368 value = EXT_BASE + (ext_number - 1) * EXT_BLOCK_SIZE + offset
3370 # Deal with negative values.
3371 direction = enum_elem.attrib.get("dir")
3372 if direction is not None:
3375 enum.create_value(enum_elem.attrib["name"], str(value))
3377 elif "value" in enum_elem.keys():
3378 enum.create_value(enum_elem.attrib["name"], enum_elem.attrib["value"])
3379 elif "alias" in enum_elem.keys():
3380 enum.create_alias(enum_elem.attrib["name"], enum_elem.attrib["alias"])
3382 elif "value" in enum_elem.keys():
3383 # Constant with an explicit value
3387 self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["value"]))
3388 elif "alias" in enum_elem.keys():
3390 if not only_aliased:
3393 self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["alias"]))
3396 def _require_type(type_info):
3397 if type_info.is_alias():
3398 type_info = type_info.alias
3399 type_info.required = True
3400 if type(type_info) == VkStruct:
3401 for member in type_info.members:
3402 if "data" in member.type_info:
3403 VkRegistry._require_type(member.type_info["data"])
3405 def _parse_extensions(self, root):
3406 """ Parse extensions section and pull in any types and commands for this extension. """
3408 exts = root.findall("./extensions/extension")
3410 skipped_exts = UNSUPPORTED_EXTENSIONS.copy()
3412 def process_ext(ext, deferred=False):
3413 ext_name = ext.attrib["name"]
3415 # Set extension name on any functions calls part of this extension as we
3416 # were not aware of the name during initial parsing.
3417 commands = ext.findall("require/command")
3418 for command in commands:
3419 cmd_name = command.attrib["name"]
3420 # Need to verify that the command is defined, and otherwise skip it.
3421 # vkCreateScreenSurfaceQNX is declared in <extensions> but not defined in
3422 # <commands>. A command without a definition cannot be enabled, so it's valid for
3423 # the XML file to handle this, but because of the manner in which we parse the XML
3424 # file we pre-populate from <commands> before we check if a command is enabled.
3425 if cmd_name in self.funcs:
3426 self.funcs[cmd_name].extensions.add(ext_name)
3428 # Some extensions are not ready or have numbers reserved as a place holder.
3429 if ext.attrib["supported"] == "disabled":
3430 LOGGER.debug("Skipping disabled extension: {0}".format(ext_name))
3431 skipped_exts.append(ext_name)
3434 # Defer extensions with 'sortorder' as they are order-dependent for spec-parsing.
3435 if not deferred and "sortorder" in ext.attrib:
3436 deferred_exts.append(ext)
3439 # Disable highly experimental extensions as the APIs are unstable and can
3440 # change between minor Vulkan revisions until API is final and becomes KHR
3442 if ("KHX" in ext_name or "NVX" in ext_name) and ext_name not in ALLOWED_X_EXTENSIONS:
3443 LOGGER.debug("Skipping experimental extension: {0}".format(ext_name))
3444 skipped_exts.append(ext_name)
3447 # Extensions can define VkEnumValues which alias to provisional extensions. Pre-process
3448 # extensions to define any required VkEnumValues before the platform check below.
3449 for require in ext.findall("require"):
3450 # Extensions can add enum values to Core / extension enums, so add these.
3451 for enum_elem in require.findall("enum"):
3452 self._process_require_enum(enum_elem, ext, only_aliased=True)
3454 platform = ext.attrib.get("platform")
3455 if platform and platform != "win32":
3456 LOGGER.debug("Skipping extensions {0} for platform {1}".format(ext_name, platform))
3457 skipped_exts.append(ext_name)
3460 if not self._is_extension_supported(ext_name):
3461 LOGGER.debug("Skipping unsupported extension: {0}".format(ext_name))
3462 skipped_exts.append(ext_name)
3464 elif "requires" in ext.attrib:
3465 # Check if this extension builds on top of another unsupported extension.
3466 requires = ext.attrib["requires"].split(",")
3467 if len(set(requires).intersection(skipped_exts)) > 0:
3468 skipped_exts.append(ext_name)
3471 LOGGER.debug("Loading extension: {0}".format(ext_name))
3473 # Extensions can define one or more require sections each requiring
3474 # different features (e.g. Vulkan 1.1). Parse each require section
3475 # separately, so we can skip sections we don't want.
3476 for require in ext.findall("require"):
3477 # Extensions can add enum values to Core / extension enums, so add these.
3478 for enum_elem in require.findall("enum"):
3479 self._process_require_enum(enum_elem, ext)
3481 for t in require.findall("type"):
3482 type_info = self.types[t.attrib["name"]]["data"]
3483 self._require_type(type_info)
3484 feature = require.attrib.get("feature")
3485 if feature and not self._is_feature_supported(feature):
3488 required_extension = require.attrib.get("extension")
3489 if required_extension and not self._is_extension_supported(required_extension):
3492 # Pull in any commands we need. We infer types to pull in from the command
3494 for command in require.findall("command"):
3495 cmd_name = command.attrib["name"]
3496 self._mark_command_required(cmd_name)
3499 # Store a list with extensions.
3500 ext_info = {"name" : ext_name, "type" : ext.attrib["type"]}
3501 extensions.append(ext_info)
3504 # Process extensions, allowing for sortorder to defer extension processing
3508 deferred_exts.sort(key=lambda ext: ext.attrib["sortorder"])
3511 for ext in deferred_exts:
3512 process_ext(ext, deferred=True)
3514 # Sort in alphabetical order.
3515 self.extensions = sorted(extensions, key=lambda ext: ext["name"])
3517 def _parse_features(self, root):
3518 """ Parse the feature section, which describes Core commands and types needed. """
3520 for feature in root.findall("./feature"):
3521 feature_name = feature.attrib["name"]
3522 for require in feature.findall("require"):
3523 LOGGER.info("Including features for {0}".format(require.attrib.get("comment")))
3525 if tag.tag == "comment":
3527 elif tag.tag == "command":
3528 if not self._is_feature_supported(feature_name):
3530 name = tag.attrib["name"]
3531 self._mark_command_required(name)
3532 elif tag.tag == "enum":
3533 self._process_require_enum(tag)
3534 elif tag.tag == "type":
3535 name = tag.attrib["name"]
3537 # Skip pull in for vk_platform.h for now.
3538 if name == "vk_platform":
3541 type_info = self.types[name]
3542 type_info["data"].required = True
3544 def _parse_types(self, root):
3545 """ Parse types section, which contains all data types e.g. structs, typedefs etcetera. """
3546 types = root.findall("./types/type")
3558 type_info["category"] = t.attrib.get("category", None)
3559 type_info["requires"] = t.attrib.get("requires", None)
3561 # We parse aliases in a second pass when we know more.
3562 alias = t.attrib.get("alias")
3564 LOGGER.debug("Alias found: {0}".format(alias))
3565 alias_types.append(t)
3568 if type_info["category"] in ["include"]:
3571 if type_info["category"] == "basetype":
3572 name = t.find("name").text
3574 if not t.find("type") is None:
3575 _type = t.find("type").text
3576 tail = t.find("type").tail
3577 if tail is not None:
3578 _type += tail.strip()
3579 basetype = VkBaseType(name, _type)
3580 base_types.append(basetype)
3581 type_info["data"] = basetype
3583 # Basic C types don't need us to define them, but we do need data for them
3584 if type_info["requires"] == "vk_platform":
3585 requires = type_info["requires"]
3586 basic_c = VkBaseType(name, _type, requires=requires)
3587 type_info["data"] = basic_c
3589 if type_info["category"] == "bitmask":
3590 name = t.find("name").text
3591 _type = t.find("type").text
3593 # Most bitmasks have a requires attribute used to pull in
3594 # required '*FlagBits" enum.
3595 requires = type_info["requires"]
3596 bitmask = VkBaseType(name, _type, requires=requires)
3597 bitmasks.append(bitmask)
3598 type_info["data"] = bitmask
3600 if type_info["category"] == "define":
3601 define = VkDefine.from_xml(t)
3602 defines.append(define)
3603 type_info["data"] = define
3605 if type_info["category"] == "enum":
3606 name = t.attrib.get("name")
3607 # The type section only contains enum names, not the actual definition.
3608 # Since we already parsed the enum before, just link it in.
3610 type_info["data"] = self.enums[name]
3611 except KeyError as e:
3612 # Not all enums seem to be defined yet, typically that's for
3613 # ones ending in 'FlagBits' where future extensions may add
3615 type_info["data"] = None
3617 if type_info["category"] == "funcpointer":
3618 funcpointer = VkFunctionPointer.from_xml(t)
3619 funcpointers.append(funcpointer)
3620 type_info["data"] = funcpointer
3622 if type_info["category"] == "handle":
3623 handle = VkHandle.from_xml(t)
3624 handles.append(handle)
3625 type_info["data"] = handle
3627 if type_info["category"] in ["struct", "union"]:
3628 # We store unions among structs as some structs depend
3629 # on unions. The types are very similar in parsing and
3630 # generation anyway. The official Vulkan scripts use
3631 # a similar kind of hack.
3632 struct = VkStruct.from_xml(t)
3633 structs.append(struct)
3634 type_info["data"] = struct
3636 # Name is in general within a name tag else it is an optional
3637 # attribute on the type tag.
3638 name_elem = t.find("name")
3639 if name_elem is not None:
3640 type_info["name"] = name_elem.text
3642 type_info["name"] = t.attrib.get("name", None)
3644 # Store all type data in a shared dictionary, so we can easily
3645 # look up information for a given type. There are no duplicate
3647 self.types[type_info["name"]] = type_info
3649 # Second pass for alias types, so we can retrieve all data from
3650 # the aliased object.
3651 for t in alias_types:
3653 type_info["category"] = t.attrib.get("category")
3654 type_info["name"] = t.attrib.get("name")
3656 alias = t.attrib.get("alias")
3658 if type_info["category"] == "bitmask":
3659 bitmask = VkBaseType(type_info["name"], alias, alias=self.types[alias]["data"])
3660 bitmasks.append(bitmask)
3661 type_info["data"] = bitmask
3663 if type_info["category"] == "enum":
3664 enum = VkEnum.from_alias(t, self.types[alias]["data"])
3665 type_info["data"] = enum
3666 self.enums[enum.name] = enum
3668 if type_info["category"] == "handle":
3669 handle = VkHandle.from_alias(t, self.types[alias]["data"])
3670 handles.append(handle)
3671 type_info["data"] = handle
3673 if type_info["category"] == "struct":
3674 struct = VkStruct.from_alias(t, self.types[alias]["data"])
3675 structs.append(struct)
3676 type_info["data"] = struct
3678 self.types[type_info["name"]] = type_info
3680 # We need detailed type information during code generation
3681 # on structs for alignment reasons. Unfortunately structs
3682 # are parsed among other types, so there is no guarantee
3683 # that any types needed have been parsed already, so set
3685 for struct in structs:
3686 struct.set_type_info(self.types)
3688 # Alias structures have enum values equivalent to those of the
3689 # structure which they are aliased against. we need to ignore alias
3690 # structs when populating the struct extensions list, otherwise we
3691 # will create duplicate case entries.
3695 for structextend in struct.structextends:
3696 s = self.types[structextend]["data"]
3697 s.struct_extensions.append(struct)
3699 # Guarantee everything is sorted, so code generation doesn't have
3700 # to deal with this.
3701 self.base_types = sorted(base_types, key=lambda base_type: base_type.name)
3702 self.bitmasks = sorted(bitmasks, key=lambda bitmask: bitmask.name)
3703 self.defines = defines
3704 self.enums = OrderedDict(sorted(self.enums.items()))
3705 self.funcpointers = sorted(funcpointers, key=lambda fp: fp.name)
3706 self.handles = sorted(handles, key=lambda handle: handle.name)
3707 self.structs = sorted(structs, key=lambda struct: struct.name)
3709 def generate_vulkan_json(f):
3711 f.write(" \"file_format_version\": \"1.0.0\",\n")
3712 f.write(" \"ICD\": {\n")
3713 f.write(" \"library_path\": \".\\\\winevulkan.dll\",\n")
3714 f.write(" \"api_version\": \"{0}\"\n".format(VK_XML_VERSION))
3718 def set_working_directory():
3719 path = os.path.abspath(__file__)
3720 path = os.path.dirname(path)
3723 def download_vk_xml(filename):
3724 url = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/v{0}/xml/vk.xml".format(VK_XML_VERSION)
3725 if not os.path.isfile(filename):
3726 urllib.request.urlretrieve(url, filename)
3729 parser = argparse.ArgumentParser()
3730 parser.add_argument("-v", "--verbose", action="count", default=0, help="increase output verbosity")
3731 parser.add_argument("-x", "--xml", default=None, type=str, help="path to specification XML file")
3733 args = parser.parse_args()
3734 if args.verbose == 0:
3735 LOGGER.setLevel(logging.WARNING)
3736 elif args.verbose == 1:
3737 LOGGER.setLevel(logging.INFO)
3739 LOGGER.setLevel(logging.DEBUG)
3741 set_working_directory()
3746 vk_xml = "vk-{0}.xml".format(VK_XML_VERSION)
3747 download_vk_xml(vk_xml)
3749 registry = VkRegistry(vk_xml)
3750 generator = VkGenerator(registry)
3752 with open(WINE_VULKAN_H, "w") as f:
3753 generator.generate_vulkan_h(f)
3755 with open(WINE_VULKAN_DRIVER_H, "w") as f:
3756 generator.generate_vulkan_driver_h(f)
3758 with open(WINE_VULKAN_THUNKS_H, "w") as f:
3759 generator.generate_thunks_h(f, "wine_")
3761 with open(WINE_VULKAN_THUNKS_C, "w") as f:
3762 generator.generate_thunks_c(f)
3764 with open(WINE_VULKAN_LOADER_THUNKS_H, "w") as f:
3765 generator.generate_loader_thunks_h(f)
3767 with open(WINE_VULKAN_LOADER_THUNKS_C, "w") as f:
3768 generator.generate_loader_thunks_c(f)
3770 with open(WINE_VULKAN_JSON, "w") as f:
3771 generate_vulkan_json(f)
3773 with open(WINE_VULKAN_SPEC, "w") as f:
3774 generator.generate_vulkan_spec(f)
3776 with open(WINE_VULKAN_LOADER_SPEC, "w") as f:
3777 generator.generate_vulkan_loader_spec(f)
3779 if __name__ == "__main__":