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.267"
69 WINE_VK_VERSION = (1, 3)
71 # Filenames to create.
72 WINE_VULKAN_H = "../../include/wine/vulkan.h"
73 WINE_VULKAN_DRIVER_H = "../../include/wine/vulkan_driver.h"
74 WINE_VULKAN_LOADER_SPEC = "../vulkan-1/vulkan-1.spec"
75 WINE_VULKAN_JSON = "winevulkan.json"
76 WINE_VULKAN_SPEC = "winevulkan.spec"
77 WINE_VULKAN_THUNKS_C = "vulkan_thunks.c"
78 WINE_VULKAN_THUNKS_H = "vulkan_thunks.h"
79 WINE_VULKAN_LOADER_THUNKS_C = "loader_thunks.c"
80 WINE_VULKAN_LOADER_THUNKS_H = "loader_thunks.h"
82 # Extension enum values start at a certain offset (EXT_BASE).
83 # Relative to the offset each extension has a block (EXT_BLOCK_SIZE)
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_GOOGLE_display_timing",
101 "VK_KHR_external_fence_win32",
102 "VK_KHR_external_semaphore_win32",
103 # Relates to external_semaphore and needs type conversions in bitflags.
104 "VK_KHR_shared_presentable_image", # Needs WSI work.
105 "VK_KHR_video_queue", # TODO Video extensions use separate headers + xml
106 "VK_KHR_win32_keyed_mutex",
107 "VK_NV_external_memory_rdma", # Needs shared resources work.
109 # Extensions for other platforms
110 "VK_EXT_external_memory_dma_buf",
111 "VK_EXT_image_drm_format_modifier",
112 "VK_EXT_metal_objects",
113 "VK_EXT_physical_device_drm",
114 "VK_GOOGLE_surfaceless_query",
115 "VK_KHR_external_fence_fd",
116 "VK_KHR_external_memory_fd",
117 "VK_KHR_external_semaphore_fd",
118 "VK_SEC_amigo_profiling", # Angle specific.
120 # Extensions which require callback handling
121 "VK_EXT_device_memory_report",
123 # Deprecated extensions
124 "VK_NV_external_memory_capabilities",
125 "VK_NV_external_memory_win32",
128 # Either internal extensions which aren't present on the win32 platform which
129 # winevulkan may nonetheless use, or extensions we want to generate headers for
130 # but not expose to applications (useful for test commits)
131 UNEXPOSED_EXTENSIONS = {
132 "VK_KHR_external_memory_win32",
135 # The Vulkan loader provides entry-points for core functionality and important
136 # extensions. Based on vulkan-1.def this amounts to WSI extensions on 1.0.51.
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 # Some frequently called functions skip traces and checks for performance reasons.
154 PERF_CRITICAL_FUNCTIONS = [
155 "vkUpdateDescriptorSets",
156 "vkUpdateDescriptorSetWithTemplate",
157 "vkGetDescriptorEXT",
160 # Functions part of our winevulkan graphics driver interface.
161 # DRIVER_VERSION should be bumped on any change to driver interface
162 # in FUNCTION_OVERRIDES
165 class ThunkType(Enum):
170 # Table of functions for which we have a special implementation.
171 # These are regular device / instance functions for which we need
172 # to do more work compared to a regular thunk or because they are
173 # part of the driver interface.
174 # - dispatch set whether we need a function pointer in the device
175 # / instance dispatch table.
176 # - driver sets whether the API is part of the driver interface.
177 # - thunk sets whether to create a thunk in vulkan_thunks.c.
178 # - NONE means there's a fully custom implementation.
179 # - PUBLIC means the implementation is fully auto generated.
180 # - PRIVATE thunks can be used in custom implementations for
182 # - loader_thunk sets whether to create a thunk for unix funcs.
183 FUNCTION_OVERRIDES = {
185 "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE, "extra_param" : "client_ptr"},
186 "vkEnumerateInstanceExtensionProperties" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
187 "vkEnumerateInstanceLayerProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
188 "vkEnumerateInstanceVersion": {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
189 "vkGetInstanceProcAddr": {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
192 "vkCreateDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE, "extra_param" : "client_ptr"},
193 "vkDestroyInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
194 "vkEnumerateDeviceExtensionProperties" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
195 "vkEnumerateDeviceLayerProperties": {"dispatch": True, "driver": False, "thunk": ThunkType.NONE},
196 "vkEnumeratePhysicalDeviceGroups" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
197 "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
198 "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
199 "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
200 "vkGetPhysicalDeviceExternalSemaphoreProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
201 "vkGetPhysicalDeviceImageFormatProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
202 "vkGetPhysicalDeviceProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PUBLIC, "loader_thunk" : ThunkType.PRIVATE},
203 "vkGetPhysicalDeviceProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PUBLIC, "loader_thunk" : ThunkType.PRIVATE},
206 "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
207 "vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE, "extra_param" : "client_ptr"},
208 "vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
209 "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
210 "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
211 "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
212 "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
213 "vkGetDeviceQueue2" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
214 "vkAllocateMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
215 "vkFreeMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
216 "vkMapMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
217 "vkMapMemory2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
218 "vkUnmapMemory" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
219 "vkUnmapMemory2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
220 "vkCreateBuffer" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
221 "vkCreateImage" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
224 "vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE},
225 "vkGetPhysicalDeviceSurfaceSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
226 "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
227 "vkGetPhysicalDeviceSurfaceFormatsKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
228 "vkGetPhysicalDeviceSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
230 # VK_KHR_get_surface_capabilities2
231 "vkGetPhysicalDeviceSurfaceCapabilities2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
232 "vkGetPhysicalDeviceSurfaceFormats2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
234 # VK_KHR_win32_surface
235 "vkCreateWin32SurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE},
236 "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
239 "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
240 "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
241 "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
242 "vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
244 # VK_KHR_external_fence_capabilities
245 "vkGetPhysicalDeviceExternalFencePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
247 # VK_KHR_external_memory_capabilities
248 "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
249 "vkGetPhysicalDeviceImageFormatProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
251 # VK_KHR_external_semaphore_capabilities
252 "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
254 # VK_KHR_device_group_creation
255 "vkEnumeratePhysicalDeviceGroupsKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
257 # VK_KHR_device_group
258 "vkGetDeviceGroupSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
259 "vkGetPhysicalDevicePresentRectanglesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
261 # VK_KHR_deferred_host_operations
262 "vkCreateDeferredOperationKHR" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
263 "vkDestroyDeferredOperationKHR" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
265 # VK_EXT_calibrated_timestamps
266 "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
267 "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
270 "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
271 "vkDestroyDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
273 # VK_EXT_debug_report
274 "vkCreateDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
275 "vkDestroyDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
278 STRUCT_CHAIN_CONVERSIONS = {
279 # Ignore to not confuse host loader.
280 "VkDeviceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO"],
281 "VkInstanceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO"],
284 # Some struct members are conditionally ignored and callers are free to leave them uninitialized.
285 # We can't deduce that from XML, so we allow expressing it here.
286 MEMBER_LENGTH_EXPRESSIONS = {
287 "VkWriteDescriptorSet": {
289 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || " +
290 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || " +
291 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || " +
292 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || " +
293 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT || " +
294 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM || " +
295 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM ? {len} : 0",
297 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || " +
298 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || " +
299 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || " +
300 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC ? {len} : 0",
304 class Direction(Enum):
305 """ Parameter direction: input, output, input_output. """
309 def api_is_vulkan(obj):
310 return "vulkan" in obj.get("api", "vulkan").split(",")
312 class VkBaseType(object):
313 def __init__(self, name, _type, alias=None, requires=None):
314 """ Vulkan base type class.
316 VkBaseType is mostly used by Vulkan to define its own
317 base types like VkFlags through typedef out of e.g. uint32_t.
320 name (:obj:'str'): Name of the base type.
321 _type (:obj:'str'): Underlying type
322 alias (bool): type is an alias or not.
323 requires (:obj:'str', optional): Other types required.
324 Often bitmask values pull in a *FlagBits type.
329 self.requires = requires
330 self.required = False
332 def definition(self):
333 # Definition is similar for alias or non-alias as type
334 # is already set to alias.
335 if not self.type is None:
336 return "typedef {0} {1};\n".format(self.type, self.name)
338 return "struct {0};\n".format(self.name)
341 return bool(self.alias)
344 class VkConstant(object):
345 def __init__(self, name, value):
349 def definition(self):
350 text = "#define {0} {1}\n".format(self.name, self.value)
354 class VkDefine(object):
355 def __init__(self, name, value):
360 def from_xml(define):
361 if not api_is_vulkan(define):
364 name_elem = define.find("name")
366 if name_elem is None:
367 # <type category="define" name="some_name">some_value</type>
368 name = define.attrib.get("name")
370 # We override behavior of VK_USE_64_BIT_PTR_DEFINES as the default non-dispatchable handle
371 # definition various between 64-bit (uses pointers) and 32-bit (uses uint64_t).
372 # This complicates TRACEs in the thunks, so just use uint64_t.
373 if name == "VK_USE_64_BIT_PTR_DEFINES":
374 value = "#define VK_USE_64_BIT_PTR_DEFINES 0"
377 return VkDefine(name, value)
379 # With a name element the structure is like:
380 # <type category="define"><name>some_name</name>some_value</type>
381 name = name_elem.text
383 # Perform minimal parsing for Vulkan constants, which we don't need, but are referenced
384 # elsewhere in vk.xml.
385 # - VK_API_VERSION is a messy, deprecated constant and we don't want generate code for it.
386 # - AHardwareBuffer/ANativeWindow are forward declarations for Android types, which leaked
387 # into the define region.
388 if name in ["VK_API_VERSION", "AHardwareBuffer", "ANativeWindow", "CAMetalLayer"]:
389 return VkDefine(name, None)
391 # The body of the define is basically unstructured C code. It is not meant for easy parsing.
392 # Some lines contain deprecated values or comments, which we try to filter out.
394 for line in define.text.splitlines():
395 # Skip comments or deprecated values.
402 if child.tail is not None:
403 # Split comments for VK_API_VERSION_1_0 / VK_API_VERSION_1_1
404 if "//" in child.tail:
405 value += child.tail.split("//")[0]
409 return VkDefine(name, value.rstrip(' '))
411 def definition(self):
412 if self.value is None:
415 # Nothing to do as the value was already put in the right form during parsing.
416 return "{0}\n".format(self.value)
419 class VkEnum(object):
420 def __init__(self, name, bitwidth, alias=None):
421 if not bitwidth in [32, 64]:
422 LOGGER.error("unknown bitwidth {0} for {1}".format(bitwidth, name))
424 self.bitwidth = bitwidth
425 self.values = [] if alias == None else alias.values
426 self.required = False
431 def from_alias(enum, alias):
432 name = enum.attrib.get("name")
433 aliasee = VkEnum(name, alias.bitwidth, alias=alias)
435 alias.add_aliased_by(aliasee)
440 if not api_is_vulkan(enum):
443 name = enum.attrib.get("name")
444 bitwidth = int(enum.attrib.get("bitwidth", "32"))
445 result = VkEnum(name, bitwidth)
447 for v in enum.findall("enum"):
448 value_name = v.attrib.get("name")
449 # Value is either a value or a bitpos, only one can exist.
450 value = v.attrib.get("value")
451 alias_name = v.attrib.get("alias")
453 result.create_alias(value_name, alias_name)
455 result.create_value(value_name, value)
458 result.create_bitpos(value_name, int(v.attrib.get("bitpos")))
461 # vulkan.h contains a *_MAX_ENUM value set to 32-bit at the time of writing,
462 # which is to prepare for extensions as they can add values and hence affect
463 # the size definition.
464 max_name = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2', name).upper() + "_MAX_ENUM"
465 result.create_value(max_name, "0x7fffffff")
469 def create_alias(self, name, alias_name):
470 """ Create an aliased value for this enum """
471 self.add(VkEnumValue(name, self.bitwidth, alias=alias_name))
473 def create_value(self, name, value):
474 """ Create a new value for this enum """
475 # Some values are in hex form. We want to preserve the hex representation
476 # at least when we convert back to a string. Internally we want to use int.
478 self.add(VkEnumValue(name, self.bitwidth, value=int(value, 0), hex=hex))
480 def create_bitpos(self, name, pos):
481 """ Create a new bitmask value for this enum """
482 self.add(VkEnumValue(name, self.bitwidth, value=(1 << pos), hex=True))
484 def add(self, value):
485 """ Add a value to enum. """
487 # Extensions can add new enum values. When an extension is promoted to Core
488 # the registry defines the value twice once for old extension and once for
489 # new Core features. Add the duplicate if it's explicitly marked as an
490 # alias, otherwise ignore it.
491 for v in self.values:
492 if not value.is_alias() and v.value == value.value:
493 LOGGER.debug("Adding duplicate enum value {0} to {1}".format(v, self.name))
495 # Avoid adding duplicate aliases multiple times
496 if not any(x.name == value.name for x in self.values):
497 self.values.append(value)
499 def fixup_64bit_aliases(self):
500 """ Replace 64bit aliases with literal values """
501 # Older GCC versions need a literal to initialize a static const uint64_t
502 # which is what we use for 64bit bitmasks.
503 if self.bitwidth != 64:
505 for value in self.values:
506 if not value.is_alias():
508 alias = next(x for x in self.values if x.name == value.alias)
509 value.hex = alias.hex
510 value.value = alias.value
512 def definition(self):
516 default_value = 0x7ffffffe if self.bitwidth == 32 else 0xfffffffffffffffe
518 # Print values sorted, values can have been added in a random order.
519 values = sorted(self.values, key=lambda value: value.value if value.value is not None else default_value)
521 if self.bitwidth == 32:
522 text = "typedef enum {0}\n{{\n".format(self.name)
524 text += " {0},\n".format(value.definition())
525 text += "}} {0};\n".format(self.name)
526 elif self.bitwidth == 64:
527 text = "typedef VkFlags64 {0};\n\n".format(self.name)
529 text += "static const {0} {1};\n".format(self.name, value.definition())
531 for aliasee in self.aliased_by:
532 text += "typedef {0} {1};\n".format(self.name, aliasee.name)
538 return bool(self.alias)
540 def add_aliased_by(self, aliasee):
541 self.aliased_by.append(aliasee)
544 class VkEnumValue(object):
545 def __init__(self, name, bitwidth, value=None, hex=False, alias=None):
547 self.bitwidth = bitwidth
553 postfix = "ull" if self.bitwidth == 64 else ""
554 if self.is_alias() and self.value == None:
555 return "{0}={1}".format(self.name, self.alias)
556 return "{0}={1}{2}".format(self.name, self.value, postfix)
558 def definition(self):
559 """ Convert to text definition e.g. VK_FOO = 1 """
560 postfix = "ull" if self.bitwidth == 64 else ""
561 if self.is_alias() and self.value == None:
562 return "{0} = {1}".format(self.name, self.alias)
564 # Hex is commonly used for FlagBits and sometimes within
565 # a non-FlagBits enum for a bitmask value as well.
567 return "{0} = 0x{1:08x}{2}".format(self.name, self.value, postfix)
569 return "{0} = {1}{2}".format(self.name, self.value, postfix)
572 return self.alias is not None
575 class VkFunction(object):
576 def __init__(self, _type=None, name=None, params=[], alias=None):
577 self.extensions = set()
583 # For some functions we need some extra metadata from FUNCTION_OVERRIDES.
584 func_info = FUNCTION_OVERRIDES.get(self.name, None)
585 self.dispatch = func_info["dispatch"] if func_info else True
586 self.driver = func_info["driver"] if func_info else False
587 self.thunk_type = func_info["thunk"] if func_info else ThunkType.PUBLIC
588 self.loader_thunk_type = func_info["loader_thunk"] if func_info and "loader_thunk" in func_info else ThunkType.PUBLIC
589 self.extra_param = func_info["extra_param"] if func_info and "extra_param" in func_info else None
591 # Required is set while parsing which APIs and types are required
592 # and is used by the code generation.
593 self.required = True if func_info else False
596 def from_alias(command, alias):
597 """ Create VkFunction from an alias command.
600 command: xml data for command
601 alias (VkFunction): function to use as a base for types / parameters.
606 if not api_is_vulkan(command):
609 func_name = command.attrib.get("name")
610 func_type = alias.type
611 params = alias.params
613 return VkFunction(_type=func_type, name=func_name, params=params, alias=alias)
616 def from_xml(command, types):
617 if not api_is_vulkan(command):
620 proto = command.find("proto")
621 func_name = proto.find("name").text
622 func_type = proto.find("type").text
625 for param in command.findall("param"):
626 vk_param = VkParam.from_xml(param, types, params)
628 params.append(vk_param)
630 return VkFunction(_type=func_type, name=func_name, params=params)
632 def get_conversions(self):
633 """ Get a list of conversion functions required for this function if any.
634 Parameters which are structures may require conversion between win32
635 and the host platform. This function returns a list of conversions
640 for param in self.params:
641 conversions.extend(param.get_conversions(self.thunk_type == ThunkType.PUBLIC))
645 return bool(self.alias)
647 def is_core_func(self):
648 """ Returns whether the function is a Vulkan core function.
649 Core functions are APIs defined by the Vulkan spec to be part of the
650 Core API as well as several KHR WSI extensions.
653 if not self.extensions:
656 return any(ext in self.extensions for ext in CORE_EXTENSIONS)
658 def is_device_func(self):
659 # If none of the other, it must be a device function
660 return not self.is_global_func() and not self.is_instance_func() and not self.is_phys_dev_func()
662 def is_driver_func(self):
663 """ Returns if function is part of Wine driver interface. """
666 def is_global_func(self):
667 # Treat vkGetInstanceProcAddr as a global function as it
668 # can operate with NULL for vkInstance.
669 if self.name == "vkGetInstanceProcAddr":
671 # Global functions are not passed a dispatchable object.
672 elif self.params[0].is_dispatchable():
676 def is_instance_func(self):
677 # Instance functions are passed VkInstance.
678 if self.params[0].type == "VkInstance":
682 def is_phys_dev_func(self):
683 # Physical device functions are passed VkPhysicalDevice.
684 if self.params[0].type == "VkPhysicalDevice":
688 def is_required(self):
691 def returns_longlong(self):
692 return self.type in ["uint64_t", "VkDeviceAddress"]
694 def needs_dispatch(self):
697 def needs_private_thunk(self):
698 return self.needs_exposing() and self.loader_thunk_type != ThunkType.NONE and \
699 self.thunk_type != ThunkType.PUBLIC
701 def needs_exposing(self):
702 # The function needs exposed if at-least one extension isn't both UNSUPPORTED and UNEXPOSED
703 return self.is_required() and (not self.extensions or not self.extensions.issubset(UNEXPOSED_EXTENSIONS))
705 def is_perf_critical(self):
706 # vkCmd* functions are frequently called, do not trace for performance
707 if self.name.startswith("vkCmd") and self.type == "void":
709 return self.name in PERF_CRITICAL_FUNCTIONS
711 def pfn(self, prefix="p", call_conv=None):
712 """ Create function pointer. """
715 pfn = "{0} ({1} *{2}_{3})(".format(self.type, call_conv, prefix, self.name)
717 pfn = "{0} (*{1}_{2})(".format(self.type, prefix, self.name)
719 for i, param in enumerate(self.params):
721 pfn += param.const + " "
725 if param.is_pointer():
726 pfn += " " + param.pointer
728 if param.array_len is not None:
729 pfn += "[{0}]".format(param.array_len)
731 if i < len(self.params) - 1:
736 def prototype(self, call_conv=None, prefix=None, is_thunk=False):
737 """ Generate prototype for given function.
740 call_conv (str, optional): calling convention e.g. WINAPI
741 prefix (str, optional): prefix to append prior to function name e.g. vkFoo -> wine_vkFoo
744 proto = "{0}".format(self.type)
746 if call_conv is not None:
747 proto += " {0}".format(call_conv)
749 if prefix is not None:
750 proto += " {0}{1}(".format(prefix, self.name)
752 proto += " {0}(".format(self.name)
754 # Add all the parameters.
755 proto += ", ".join([p.definition() for p in self.params])
757 if is_thunk and self.extra_param:
758 proto += ", void *" + self.extra_param
763 def loader_body(self):
764 body = " struct {0}_params params;\n".format(self.name)
765 if not self.is_perf_critical():
766 body += " NTSTATUS status;\n"
767 for p in self.params:
768 body += " params.{0} = {0};\n".format(p.name)
770 # Call the Unix function.
771 if self.is_perf_critical():
772 body += " UNIX_CALL({0}, ¶ms);\n".format(self.name)
774 body += " status = UNIX_CALL({0}, ¶ms);\n".format(self.name)
775 body += " assert(!status && \"{0}\");\n".format(self.name)
777 if self.type != "void":
778 body += " return params.result;\n"
781 def body(self, conv, unwrap, params_prefix=""):
786 # Declare any tmp parameters for conversion.
787 for p in self.params:
788 if p.needs_variable(conv, unwrap):
789 if p.is_dynamic_array():
790 body += " {2}{0} *{1}_host;\n".format(
791 p.type, p.name, "const " if p.is_const() else "")
793 body += " {0} *{1}_host = NULL;\n".format(p.type, p.name)
796 body += " {0} {1}_host;\n".format(p.type, p.name)
797 if p.needs_alloc(conv, unwrap):
799 if p.type == "VkDeferredOperationKHR" and not p.is_pointer():
803 body += " struct conversion_context local_ctx;\n"
804 body += " struct conversion_context *ctx = &local_ctx;\n"
807 if not self.is_perf_critical():
808 body += " {0}\n".format(self.trace(params_prefix=params_prefix, conv=conv))
810 if self.params[0].optional and self.params[0].is_handle():
811 if self.type != "void":
812 LOGGER.warning("return type {0} with optional handle not supported".format(self.type))
813 body += " if (!{0}{1})\n".format(params_prefix, self.params[0].name)
814 body += " return STATUS_SUCCESS;\n\n"
817 if deferred_op is not None:
818 body += " if (params->{} == VK_NULL_HANDLE)\n".format(deferred_op)
820 body += " init_conversion_context(ctx);\n"
821 if deferred_op is not None:
823 body += " ctx = &wine_deferred_operation_from_handle(params->{})->ctx;\n".format(deferred_op)
825 # Call any win_to_host conversion calls.
826 unwrap = self.thunk_type == ThunkType.PUBLIC
827 for p in self.params:
828 if p.needs_conversion(conv, unwrap, Direction.INPUT):
829 body += p.copy(Direction.INPUT, conv, unwrap, prefix=params_prefix)
830 elif p.is_dynamic_array() and p.needs_conversion(conv, unwrap, Direction.OUTPUT):
831 body += " {0}_host = ({2}{0} && {1}) ? conversion_context_alloc(ctx, sizeof(*{0}_host) * {1}) : NULL;\n".format(
832 p.name, p.get_dyn_array_len(params_prefix, conv), params_prefix)
834 # Build list of parameters containing converted and non-converted parameters.
835 # The param itself knows if conversion is needed and applies it when we set conv=True.
836 params = ", ".join([p.variable(conv=conv, unwrap=unwrap, params_prefix=params_prefix) for p in self.params])
839 params += ", UlongToPtr({0}{1})".format(params_prefix, self.extra_param)
841 params += ", {0}{1}".format(params_prefix, self.extra_param)
843 if unwrap or self.thunk_type == ThunkType.PUBLIC:
844 func_prefix = "{0}.p_".format(self.params[0].dispatch_table(params_prefix, conv))
846 func_prefix = "wine_"
848 # Call the native Vulkan function.
849 if self.type == "void":
850 body += " {0}{1}({2});\n".format(func_prefix, self.name, params)
852 body += " {0}result = {1}{2}({3});\n".format(params_prefix, func_prefix, self.name, params)
854 # Call any host_to_win conversion calls.
855 for p in self.params:
856 if p.needs_conversion(conv, unwrap, Direction.OUTPUT):
857 body += p.copy(Direction.OUTPUT, conv, unwrap, prefix=params_prefix)
860 if deferred_op is not None:
861 body += " if (params->{} == VK_NULL_HANDLE)\n".format(deferred_op)
863 body += " free_conversion_context(ctx);\n"
865 # Finally return the result. Performance critical functions return void to allow tail calls.
866 if not self.is_perf_critical():
867 body += " return STATUS_SUCCESS;\n"
871 def spec(self, prefix=None, symbol=None):
872 """ Generate spec file entry for this function.
875 prefix (str, optional): prefix to prepend to entry point name.
876 symbol (str, optional): allows overriding the name of the function implementing the entry point.
880 params = " ".join([p.spec() for p in self.params])
881 if prefix is not None:
882 spec += "@ stdcall -private {0}{1}({2})".format(prefix, self.name, params)
884 spec += "@ stdcall {0}({1})".format(self.name, params)
886 if symbol is not None:
892 def stub(self, call_conv=None, prefix=None):
893 stub = self.prototype(call_conv=call_conv, prefix=prefix)
895 stub += " {0}".format(self.trace(message="stub: ", trace_func="FIXME"))
897 if self.type == "VkResult":
898 stub += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
899 elif self.type == "VkBool32":
900 stub += " return VK_FALSE;\n"
901 elif self.type == "PFN_vkVoidFunction":
902 stub += " return NULL;\n"
907 def thunk(self, prefix=None, conv=False):
910 thunk += "#ifdef _WIN64\n"
911 if self.is_perf_critical():
912 thunk += "static void {0}{1}(void *args)\n".format(prefix, self.name)
914 thunk += "static NTSTATUS {0}{1}(void *args)\n".format(prefix, self.name)
919 for p in self.params:
920 thunk += " {0};\n".format(p.definition(conv=True, is_member=True))
922 thunk += " PTR32 {0};\n".format(self.extra_param)
923 if self.type != "void":
924 thunk += " {0} result;\n".format(self.type)
925 thunk += " } *params = args;\n"
927 thunk += " struct {0}_params *params = args;\n".format(self.name)
928 thunk += self.body(conv=conv, unwrap=self.thunk_type == ThunkType.PUBLIC, params_prefix="params->")
931 thunk += "#endif /* _WIN64 */\n"
935 def loader_thunk(self, prefix=None):
936 thunk = self.prototype(call_conv="WINAPI", prefix=prefix)
938 thunk += self.loader_body()
942 def trace(self, message=None, trace_func=None, params_prefix="", conv=False):
943 """ Create a trace string including all parameters.
946 message (str, optional): text to print at start of trace message e.g. 'stub: '
947 trace_func (str, optional): used to override trace function e.g. FIXME, printf, etcetera.
949 if trace_func is not None:
950 trace = "{0}(\"".format(trace_func)
954 if message is not None:
957 # First loop is for all the format strings.
958 trace += ", ".join([p.format_string(conv) for p in self.params])
961 # Second loop for parameter names and optional conversions.
962 for param in self.params:
963 if param.format_conv is not None:
964 trace += ", " + param.format_conv.format("{0}{1}".format(params_prefix, param.name))
966 trace += ", {0}{1}".format(params_prefix, param.name)
972 class VkFunctionPointer(object):
973 def __init__(self, _type, name, members, forward_decls):
975 self.members = members
977 self.required = False
978 self.forward_decls = forward_decls
981 def from_xml(funcpointer):
982 if not api_is_vulkan(funcpointer):
988 for t in funcpointer.findall("type"):
990 # <type>void</type>* pUserData,
991 # Parsing of the tail (anything past </type>) is tricky since there
992 # can be other data on the next line like: const <type>int</type>..
994 const = True if begin and "const" in begin else False
996 lines = t.tail.split(",\n")
997 if lines[0][0] == "*":
999 name = lines[0][1:].strip()
1002 name = lines[0].strip()
1004 # Filter out ); if it is contained.
1005 name = name.partition(");")[0]
1007 # If tail encompasses multiple lines, assign the second line to begin
1008 # for the next line.
1010 begin = lines[1].strip()
1014 members.append(VkMember(const=const, _type=_type, pointer=pointer, name=name))
1016 _type = funcpointer.text
1017 name = funcpointer.find("name").text
1018 if "requires" in funcpointer.attrib:
1019 forward_decls = funcpointer.attrib.get("requires").split(",")
1022 return VkFunctionPointer(_type, name, members, forward_decls)
1024 def definition(self):
1026 # forward declare required structs
1027 for decl in self.forward_decls:
1028 text += "typedef struct {0} {0};\n".format(decl)
1030 text += "{0} {1})(\n".format(self.type, self.name)
1033 if len(self.members) > 0:
1034 for m in self.members:
1036 text += " " + m.definition()
1039 text += ",\n " + m.definition()
1041 # Just make the compiler happy by adding a void parameter.
1049 class VkHandle(object):
1050 def __init__(self, name, _type, parent, alias=None):
1053 self.parent = parent
1055 self.required = False
1056 self.object_type = None
1059 def from_alias(handle, alias):
1060 name = handle.attrib.get("name")
1061 return VkHandle(name, alias.type, alias.parent, alias=alias)
1064 def from_xml(handle):
1065 if not api_is_vulkan(handle):
1068 name = handle.find("name").text
1069 _type = handle.find("type").text
1070 parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice.
1071 return VkHandle(name, _type, parent)
1073 def dispatch_table(self, param):
1074 if not self.is_dispatchable():
1077 if self.parent is None:
1078 # Should only happen for VkInstance
1079 return "wine_instance_from_handle({0})->funcs".format(param)
1080 elif self.name == "VkCommandBuffer":
1081 return "wine_cmd_buffer_from_handle({0})->device->funcs".format(param)
1082 elif self.name == "VkDevice":
1083 return "wine_device_from_handle({0})->funcs".format(param)
1084 elif self.name == "VkPhysicalDevice":
1085 return "wine_phys_dev_from_handle({0})->instance->funcs".format(param)
1086 elif self.name == "VkQueue":
1087 return "wine_queue_from_handle({0})->device->funcs".format(param)
1088 elif self.parent in ["VkInstance", "VkPhysicalDevice"]:
1089 return "{0}->instance->funcs".format(param)
1090 elif self.parent in ["VkDevice", "VkCommandPool"]:
1091 return "{0}->device->funcs".format(param)
1093 LOGGER.error("Unhandled dispatchable parent: {0}".format(self.parent))
1095 def definition(self):
1096 """ Generates handle definition e.g. VK_DEFINE_HANDLE(vkInstance) """
1098 # Legacy types are typedef'ed to the new type if they are aliases.
1100 return "typedef {0} {1};\n".format(self.alias.name, self.name)
1102 return "{0}({1})\n".format(self.type, self.name)
1105 return self.alias is not None
1107 def is_dispatchable(self):
1108 """ Some handles like VkInstance, VkDevice are dispatchable objects,
1109 which means they contain a dispatch table of function pointers.
1111 return self.type == "VK_DEFINE_HANDLE"
1113 def is_required(self):
1114 return self.required
1116 def native_handle(self, name):
1117 """ Provide access to the native handle of a wrapped object. """
1119 if self.name == "VkCommandBuffer":
1120 return "wine_cmd_buffer_from_handle({0})->command_buffer".format(name)
1121 if self.name == "VkCommandPool":
1122 return "wine_cmd_pool_from_handle({0})->command_pool".format(name)
1123 if self.name == "VkDebugUtilsMessengerEXT":
1124 return "wine_debug_utils_messenger_from_handle({0})->debug_messenger".format(name)
1125 if self.name == "VkDebugReportCallbackEXT":
1126 return "wine_debug_report_callback_from_handle({0})->debug_callback".format(name)
1127 if self.name == "VkDeferredOperationKHR":
1128 return "wine_deferred_operation_from_handle({0})->deferred_operation".format(name)
1129 if self.name == "VkDevice":
1130 return "wine_device_from_handle({0})->device".format(name)
1131 if self.name == "VkInstance":
1132 return "wine_instance_from_handle({0})->instance".format(name)
1133 if self.name == "VkDeviceMemory":
1134 return "wine_device_memory_from_handle({0})->memory".format(name)
1135 if self.name == "VkPhysicalDevice":
1136 return "wine_phys_dev_from_handle({0})->phys_dev".format(name)
1137 if self.name == "VkQueue":
1138 return "wine_queue_from_handle({0})->queue".format(name)
1139 if self.name == "VkSurfaceKHR":
1140 return "wine_surface_from_handle({0})->surface".format(name)
1141 if self.is_dispatchable():
1142 LOGGER.error("Unhandled native handle for: {0}".format(self.name))
1145 def driver_handle(self, name):
1146 """ Provide access to the handle that should be passed to the wine driver """
1148 if self.name == "VkSurfaceKHR":
1149 return "wine_surface_from_handle({0})->driver_surface".format(name)
1151 return self.native_handle(name)
1153 def is_wrapped(self):
1154 return self.native_handle("test") is not None
1156 def needs_unwrapping(self):
1157 return self.is_wrapped()
1159 class VkVariable(object):
1160 def __init__(self, const=False, type_info=None, type=None, name=None, pointer=None, array_len=None,
1161 dyn_array_len=None, object_type=None, optional=False, returnedonly=False, parent=None,
1162 selection=None, selector=None):
1164 self.type_info = type_info
1167 self.parent = parent
1168 self.object_type = object_type
1169 self.optional = optional
1170 self.returnedonly = returnedonly
1171 self.selection = selection
1172 self.selector = selector
1174 self.pointer = pointer
1175 self.array_len = array_len
1176 self.dyn_array_len = dyn_array_len
1177 self.pointer_array = False
1178 if isinstance(dyn_array_len, str):
1179 i = dyn_array_len.find(",")
1181 self.dyn_array_len = dyn_array_len[0:i]
1182 self.pointer_array = True
1185 self.set_type_info(type_info)
1187 def __eq__(self, other):
1188 """ Compare member based on name against a string. """
1189 return self.name == other
1191 def set_type_info(self, type_info):
1192 """ Helper function to set type information from the type registry.
1193 This is needed, because not all type data is available at time of
1196 self.type_info = type_info
1197 self.handle = type_info["data"] if type_info["category"] == "handle" else None
1198 self.struct = type_info["data"] if type_info["category"] == "struct" or type_info["category"] == "union" else None
1200 def get_dyn_array_len(self, prefix, conv):
1201 if isinstance(self.dyn_array_len, int):
1202 return self.dyn_array_len
1204 len_str = self.dyn_array_len
1205 parent = self.parent
1208 # check if length is a member of another struct (for example pAllocateInfo->commandBufferCount)
1209 i = len_str.find("->")
1211 var = parent[parent.index(len_str[0:i])]
1212 len_str = len_str[i+2:]
1213 len = "({0})->".format(var.value(len, conv))
1216 if len_str in parent:
1217 var = parent[parent.index(len_str)]
1218 len = var.value(len, conv);
1219 if var.is_pointer():
1224 if isinstance(self.parent, VkStruct) and self.parent.name in MEMBER_LENGTH_EXPRESSIONS:
1225 exprs = MEMBER_LENGTH_EXPRESSIONS[self.parent.name]
1226 if self.name in exprs:
1227 len = exprs[self.name].format(struct=prefix, len=len)
1234 def is_pointer(self):
1235 return self.pointer is not None
1237 def is_pointer_size(self):
1238 if self.type in ["size_t", "HWND", "HINSTANCE"]:
1240 if self.is_handle() and self.handle.is_dispatchable():
1244 def is_handle(self):
1245 return self.handle is not None
1247 def is_struct(self):
1248 return self.type_info["category"] == "struct"
1251 return self.type_info["category"] == "union"
1253 def is_bitmask(self):
1254 return self.type_info["category"] == "bitmask"
1257 return self.type_info["category"] == "enum"
1259 def is_dynamic_array(self):
1260 """ Returns if the member is an array element.
1261 Vulkan uses this for dynamically sized arrays for which
1262 there is a 'count' parameter.
1264 return self.dyn_array_len is not None
1266 def is_static_array(self):
1267 """ Returns if the member is an array.
1268 Vulkan uses this often for fixed size arrays in which the
1269 length is part of the member.
1271 return self.array_len is not None
1273 def is_generic_handle(self):
1274 """ Returns True if the member is a unit64_t containing
1275 a handle with a separate object type
1277 return self.object_type != None and self.type == "uint64_t"
1279 def needs_alignment(self):
1280 """ Check if this member needs alignment for 64-bit data.
1281 Various structures need alignment on 64-bit variables due
1282 to compiler differences on 32-bit between Win32 and Linux.
1285 if self.is_pointer():
1287 elif self.type == "size_t":
1289 elif self.type in ["uint64_t", "VkDeviceAddress", "VkDeviceSize"]:
1291 elif self.is_bitmask():
1292 return self.type_info["data"].type == "VkFlags64"
1293 elif self.is_enum():
1294 return self.type_info["data"].bitwidth == 64
1295 elif self.is_struct() or self.is_union():
1296 return self.type_info["data"].needs_alignment()
1297 elif self.is_handle():
1298 # Dispatchable handles are pointers to objects, while
1299 # non-dispatchable are uint64_t and hence need alignment.
1300 return not self.handle.is_dispatchable()
1303 def needs_unwrapping(self):
1304 """ Returns if variable needs unwrapping of handle. """
1306 if self.is_struct():
1307 return self.struct.needs_unwrapping()
1309 if self.is_handle():
1310 return self.handle.needs_unwrapping()
1312 if self.is_generic_handle():
1317 def needs_alloc(self, conv, unwrap):
1318 """ Returns True if conversion needs allocation """
1319 if self.is_dynamic_array():
1320 return self.needs_conversion(conv, unwrap, Direction.INPUT, False) \
1321 or self.needs_conversion(conv, unwrap, Direction.OUTPUT, False)
1323 return (self.is_struct() or (self.is_union() and self.selector)) and self.struct.needs_alloc(conv, unwrap)
1325 def needs_win32_type(self):
1326 return (self.is_struct() or (self.is_union() and self.selector)) and self.struct.needs_win32_type()
1328 def get_conversions(self, unwrap, parent_const=False):
1329 """ Get a list of conversions required for this parameter if any.
1330 Parameters which are structures may require conversion between win32
1331 and the host platform. This function returns a list of conversions
1337 # Collect any member conversions first, so we can guarantee
1338 # those functions will be defined prior to usage by the
1339 # 'parent' param requiring conversion.
1340 if self.is_struct() or (self.is_union() and self.selector):
1341 struct = self.struct
1342 is_const = self.is_const() if self.is_pointer() else parent_const
1344 conversions.extend(struct.get_conversions(unwrap, is_const))
1346 for conv in [False, True]:
1347 if struct.needs_conversion(conv, unwrap, Direction.INPUT, is_const):
1348 conversions.append(StructConversionFunction(struct, Direction.INPUT, conv, unwrap, is_const))
1349 if struct.needs_conversion(conv, unwrap, Direction.OUTPUT, is_const):
1350 conversions.append(StructConversionFunction(struct, Direction.OUTPUT, conv, unwrap, is_const))
1352 if self.is_static_array() or self.is_dynamic_array():
1353 for conv in [False, True]:
1354 if self.needs_conversion(conv, unwrap, Direction.INPUT, parent_const):
1355 conversions.append(ArrayConversionFunction(self, Direction.INPUT, conv, unwrap))
1356 if self.needs_conversion(conv, unwrap, Direction.OUTPUT, parent_const):
1357 conversions.append(ArrayConversionFunction(self, Direction.OUTPUT, conv, unwrap))
1361 def needs_ptr32_type(self):
1362 """ Check if variable needs to use PTR32 type. """
1364 return self.is_pointer() or self.is_pointer_size() or self.is_static_array()
1366 def value(self, prefix, conv):
1367 """ Returns code accessing member value, casting 32-bit pointers when needed. """
1369 if not conv or not self.needs_ptr32_type() or (not self.is_pointer() and self.type == "size_t"):
1370 return prefix + self.name
1374 cast_type += "const "
1376 if self.pointer_array or ((self.is_pointer() or self.is_static_array()) and self.is_pointer_size()):
1377 cast_type += "PTR32 *"
1379 cast_type += self.type
1380 if self.needs_win32_type():
1383 if self.is_pointer():
1384 cast_type += " {0}".format(self.pointer)
1385 elif self.is_static_array():
1388 return "({0})UlongToPtr({1}{2})".format(cast_type, prefix, self.name)
1391 class VkMember(VkVariable):
1392 def __init__(self, const=False, struct_fwd_decl=False,_type=None, pointer=None, name=None, array_len=None,
1393 dyn_array_len=None, optional=False, values=None, object_type=None, bit_width=None,
1394 returnedonly=False, parent=None, selection=None, selector=None):
1395 VkVariable.__init__(self, const=const, type=_type, name=name, pointer=pointer, array_len=array_len,
1396 dyn_array_len=dyn_array_len, object_type=object_type, optional=optional,
1397 returnedonly=returnedonly, parent=parent, selection=selection, selector=selector)
1398 self.struct_fwd_decl = struct_fwd_decl
1399 self.values = values
1400 self.bit_width = bit_width
1403 return "{0} {1} {2} {3} {4} {5} {6}".format(self.const, self.struct_fwd_decl, self.type, self.pointer,
1404 self.name, self.array_len, self.dyn_array_len)
1407 def from_xml(member, returnedonly, parent):
1408 """ Helper function for parsing a member tag within a struct or union. """
1410 if not api_is_vulkan(member):
1413 name_elem = member.find("name")
1414 type_elem = member.find("type")
1417 struct_fwd_decl = False
1423 values = member.get("values")
1426 if "const" in member.text:
1429 # Some members contain forward declarations:
1430 # - VkBaseInstructure has a member "const struct VkBaseInStructure *pNext"
1431 # - VkWaylandSurfaceCreateInfoKHR has a member "struct wl_display *display"
1432 if "struct" in member.text:
1433 struct_fwd_decl = True
1435 if type_elem is not None:
1436 member_type = type_elem.text
1437 if type_elem.tail is not None:
1438 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1440 # Name of other member within, which stores the number of
1441 # elements pointed to be by this member.
1442 dyn_array_len = member.get("len")
1444 # Some members are optional, which is important for conversion code e.g. not dereference NULL pointer.
1445 optional = True if member.get("optional") else False
1447 # Usually we need to allocate memory for dynamic arrays. We need to do the same in a few other cases
1448 # like for VkCommandBufferBeginInfo.pInheritanceInfo. Just threat such cases as dynamic arrays of
1449 # size 1 to simplify code generation.
1450 if dyn_array_len is None and pointer is not None:
1453 # Some members are arrays, attempt to parse these. Formats include:
1454 # <member><type>char</type><name>extensionName</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
1455 # <member><type>uint32_t</type><name>foo</name>[4]</member>
1456 if name_elem.tail and name_elem.tail[0] == '[':
1457 LOGGER.debug("Found array type")
1458 enum_elem = member.find("enum")
1459 if enum_elem is not None:
1460 array_len = enum_elem.text
1462 # Remove brackets around length
1463 array_len = name_elem.tail.strip("[]")
1465 object_type = member.get("objecttype", None)
1467 # Some members are bit field values:
1468 # <member><type>uint32_t</type> <name>mask</name>:8</member>
1469 if name_elem.tail and name_elem.tail[0] == ':':
1470 LOGGER.debug("Found bit field")
1471 bit_width = int(name_elem.tail[1:])
1473 selection = member.get("selection").split(',') if member.get("selection") else None
1474 selector = member.get("selector", None)
1476 return VkMember(const=const, struct_fwd_decl=struct_fwd_decl, _type=member_type, pointer=pointer,
1477 name=name_elem.text, array_len=array_len, dyn_array_len=dyn_array_len, optional=optional,
1478 values=values, object_type=object_type, bit_width=bit_width, returnedonly=returnedonly,
1479 parent=parent, selection=selection, selector=selector)
1481 def copy(self, input, output, direction, conv, unwrap):
1482 """ Helper method for use by conversion logic to generate a C-code statement to copy this member.
1483 - `conv` indicates whether the statement is in a struct alignment conversion path. """
1485 win_type = "win32" if conv else "win64"
1486 if self.needs_conversion(conv, unwrap, direction, False):
1487 if self.is_dynamic_array():
1488 # Array length is either a variable name (string) or an int.
1489 count = self.get_dyn_array_len(input, conv)
1490 host_part = "host" if unwrap else "unwrapped_host"
1491 pointer_part = "pointer_" if self.pointer_array else ""
1492 if direction == Direction.OUTPUT:
1493 return "convert_{2}_{7}array_{6}_to_{5}({3}{1}, {0}, {4});\n".format(
1494 self.value(output, conv), self.name, self.type, input, count, win_type,
1495 host_part, pointer_part)
1497 return "{0}{1} = convert_{2}_{7}array_{5}_to_{6}(ctx, {3}, {4});\n".format(
1498 output, self.name, self.type, self.value(input, conv), count, win_type,
1499 host_part, pointer_part)
1500 elif self.is_static_array():
1501 count = self.array_len
1502 if direction == Direction.OUTPUT:
1503 # Needed by VkMemoryHeap.memoryHeaps
1504 host_part = "host" if unwrap else "unwrapped_host"
1505 return "convert_{0}_array_{6}_to_{5}({2}{1}, {3}{1}, {4});\n".format(
1506 self.type, self.name, input, output, count, win_type, host_part)
1508 # Nothing needed this yet.
1509 LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
1510 elif self.is_handle() and self.needs_unwrapping():
1511 handle = self.type_info["data"]
1512 if direction == Direction.OUTPUT:
1513 LOGGER.err("OUTPUT parameter {0}.{1} cannot be unwrapped".format(self.type, self.name))
1515 return "{0}{1} = {2} ? {3} : 0;\n".format(output, self.name,
1516 self.value(input, conv), handle.driver_handle(self.value(input, conv)))
1518 return "{0}{1} = {2};\n".format(output, self.name, handle.driver_handle(self.value(input, conv)))
1519 elif self.is_generic_handle():
1520 if direction == Direction.OUTPUT:
1521 LOGGER.err("OUTPUT parameter {0}.{1} cannot be unwrapped".format(self.type, self.name))
1523 return "{0}{1} = wine_vk_unwrap_handle({2}{3}, {2}{1});\n".format(output, self.name, input, self.object_type)
1525 selector_part = ", {0}{1}".format(input, self.selector) if self.selector else ""
1526 if direction == Direction.OUTPUT:
1527 return "convert_{0}_host_to_{4}(&{2}{1}, &{3}{1}{5});\n".format(self.type, self.name, input, output, win_type, selector_part)
1529 ctx_param = "ctx, " if self.needs_alloc(conv, unwrap) else ""
1530 host_part = "host" if unwrap else "unwrapped_host"
1531 return "convert_{0}_{4}_to_{6}({5}&{2}{1}, &{3}{1}{7});\n".format(self.type, self.name, input, output, win_type, ctx_param, host_part, selector_part)
1532 elif self.is_static_array():
1533 bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type)
1534 return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count)
1535 elif direction == Direction.INPUT:
1536 return "{0}{1} = {2};\n".format(output, self.name, self.value(input, conv))
1537 elif conv and direction == Direction.OUTPUT and self.is_pointer():
1538 return "{0}{1} = PtrToUlong({2}{1});\n".format(output, self.name, input)
1540 return "{0}{1} = {2}{1};\n".format(output, self.name, input)
1542 def definition(self, align=False, conv=False):
1543 """ Generate prototype for given function.
1546 align (bool, optional): Enable alignment if a type needs it. This adds WINE_VK_ALIGN(8) to a member.
1547 conv (bool, optional): Enable conversion if a type needs it. This appends '_host' to the name.
1550 if conv and (self.is_pointer() or self.is_pointer_size()):
1551 text = "PTR32 " + self.name
1552 if self.is_static_array():
1553 text += "[{0}]".format(self.array_len)
1560 if self.is_struct_forward_declaration():
1564 if conv and self.needs_win32_type():
1567 if self.is_pointer():
1568 text += " {0}{1}".format(self.pointer, self.name)
1570 if align and self.needs_alignment():
1572 text += " DECLSPEC_ALIGN(8) " + self.name
1574 text += " WINE_VK_ALIGN(8) " + self.name
1576 text += " " + self.name
1578 if self.is_static_array():
1579 text += "[{0}]".format(self.array_len)
1581 if self.is_bit_field():
1582 text += ":{}".format(self.bit_width)
1586 def is_struct_forward_declaration(self):
1587 return self.struct_fwd_decl
1589 def is_bit_field(self):
1590 return self.bit_width is not None
1592 def needs_conversion(self, conv, unwrap, direction, struct_const):
1593 """ Check if member needs conversion. """
1595 # we can't convert unions if we don't have a selector
1596 if self.is_union() and not self.selector:
1599 is_const = self.is_const() if self.is_pointer() else struct_const
1601 # const members don't needs output conversion unless they are structs with non-const pointers
1602 if direction == Direction.OUTPUT and is_const and not self.is_struct():
1605 if direction == Direction.INPUT:
1606 # returnedonly members don't needs input conversions
1607 if not self.is_pointer() and self.returnedonly:
1609 # pointer arrays always need input conversion
1610 if conv and self.is_dynamic_array() and self.pointer_array:
1613 if self.is_handle():
1614 if unwrap and self.handle.is_wrapped():
1616 if conv and self.handle.is_dispatchable():
1618 elif self.is_generic_handle():
1621 elif self.is_struct() or self.is_union():
1622 if self.struct.needs_conversion(conv, unwrap, direction, is_const):
1625 # if pointer member needs output conversion, it also needs input conversion
1626 # to allocate the pointer
1627 if direction == Direction.INPUT and self.is_pointer() and \
1628 self.needs_conversion(conv, unwrap, Direction.OUTPUT, struct_const):
1633 class VkParam(VkVariable):
1634 """ Helper class which describes a parameter to a function call. """
1636 def __init__(self, type_info, const=None, pointer=None, name=None, parent=None, array_len=None,
1637 dyn_array_len=None, object_type=None, optional=False):
1638 VkVariable.__init__(self, const=const, type_info=type_info, type=type_info["name"], name=name,
1639 pointer=pointer, array_len=array_len, dyn_array_len=dyn_array_len,
1640 object_type=object_type, optional=optional, parent=parent)
1642 self._set_format_string()
1645 return "{0} {1} {2} {3} {4} {5}".format(self.const, self.type, self.pointer, self.name, self.array_len, self.dyn_array_len)
1648 def from_xml(param, types, parent):
1649 """ Helper function to create VkParam from xml. """
1651 if not api_is_vulkan(param):
1654 # Parameter parsing is slightly tricky. All the data is contained within
1655 # a param tag, but some data is within subtags while others are text
1656 # before or after the type tag.
1658 # <param>const <type>char</type>* <name>pLayerName</name></param>
1660 name_elem = param.find("name")
1662 name = name_elem.text
1663 # Tail contains array length e.g. for blendConstants param of vkSetBlendConstants
1664 if name_elem.tail is not None:
1665 array_len = name_elem.tail.strip("[]")
1667 # Name of other parameter in function prototype, which stores the number of
1668 # elements pointed to be by this parameter.
1669 dyn_array_len = param.get("len", None)
1671 const = param.text.strip() if param.text else None
1672 type_elem = param.find("type")
1673 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1675 attr = param.get("optional")
1676 optional = attr and attr.startswith("true")
1678 # Some uint64_t are actually handles with a separate type param
1679 object_type = param.get("objecttype", None)
1681 # Since we have parsed all types before hand, this should not happen.
1682 type_info = types.get(type_elem.text, None)
1683 if type_info is None:
1684 LOGGER.err("type info not found for: {0}".format(type_elem.text))
1686 return VkParam(type_info, const=const, pointer=pointer, name=name, array_len=array_len,
1687 dyn_array_len=dyn_array_len, object_type=object_type, optional=optional,
1690 def _set_format_string(self):
1691 """ Internal helper function to be used by constructor to set format string. """
1693 # Determine a format string used by code generation for traces.
1694 # 64-bit types need a conversion function.
1695 self.format_conv = None
1696 if self.is_static_array() or self.is_pointer():
1697 self.format_str = "%p"
1699 if self.type_info["category"] in ["bitmask"]:
1700 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1701 if self.type_info["data"].type == "VkFlags64":
1702 self.format_str = "0x%s"
1703 self.format_conv = "wine_dbgstr_longlong({0})"
1705 self.format_str = "%#x"
1706 elif self.type_info["category"] in ["enum"]:
1707 self.format_str = "%#x"
1708 elif self.is_handle():
1709 # We use uint64_t for non-dispatchable handles as opposed to pointers
1710 # for dispatchable handles.
1711 if self.handle.is_dispatchable():
1712 self.format_str = "%p"
1714 self.format_str = "0x%s"
1715 self.format_conv = "wine_dbgstr_longlong({0})"
1716 elif self.type == "float":
1717 self.format_str = "%f"
1718 elif self.type == "int":
1719 self.format_str = "%d"
1720 elif self.type == "int32_t":
1721 self.format_str = "%d"
1722 elif self.type == "size_t":
1723 self.format_str = "0x%s"
1724 self.format_conv = "wine_dbgstr_longlong({0})"
1725 elif self.type in ["uint16_t", "uint32_t", "VkBool32"]:
1726 self.format_str = "%u"
1727 elif self.type in ["uint64_t", "VkDeviceAddress", "VkDeviceSize"]:
1728 self.format_str = "0x%s"
1729 self.format_conv = "wine_dbgstr_longlong({0})"
1730 elif self.type == "HANDLE":
1731 self.format_str = "%p"
1732 elif self.type in ["VisualID", "xcb_visualid_t", "RROutput", "zx_handle_t", "NvSciBufObj", "NvSciBufAttrList", "NvSciSyncAttrList"]:
1733 # Don't care about specific types for non-Windows platforms.
1734 self.format_str = ""
1736 LOGGER.warn("Unhandled type: {0}".format(self.type_info))
1738 def copy(self, direction, conv, unwrap, prefix=""):
1739 win_type = "win32" if conv else "win64"
1740 wrap_part = "" if unwrap or not self.needs_unwrapping() else "unwrapped_"
1741 if direction == Direction.INPUT:
1742 ctx_param = "ctx, " if self.needs_alloc(conv, unwrap) else ""
1743 if self.is_dynamic_array():
1744 return " {0}_host = convert_{2}_array_{4}_to_{6}host({5}{1}, {3});\n".format(
1745 self.name, self.value(prefix, conv), self.type, self.get_dyn_array_len(prefix, conv),
1746 win_type, ctx_param, wrap_part)
1748 ret = " if ({0}{1})\n".format(prefix, self.name)
1750 ret += " {0}_host = conversion_context_alloc(ctx, sizeof(*{0}_host));\n".format(self.name)
1751 ret += " convert_{0}_{3}_to_{5}host({4}{1}, {2}_host);\n".format(
1752 self.type, self.value(prefix, conv), self.name, win_type, ctx_param, wrap_part)
1755 elif self.is_struct():
1756 return " convert_{0}_{3}_to_{5}host({4}{1}, &{2}_host);\n".format(
1757 self.type, self.value(prefix, conv), self.name, win_type, ctx_param, wrap_part)
1758 elif self.is_pointer_size() and self.type != "size_t":
1759 return " {0}_host = UlongToPtr(*{1});\n".format(self.name, self.value(prefix, conv))
1761 return " {0}_host = *{1};\n".format(self.name, self.value(prefix, conv))
1763 if self.is_dynamic_array():
1764 return " convert_{0}_array_{1}host_to_{2}({3}_host, {4}, {5});\n".format(
1765 self.type, wrap_part, win_type, self.name, self.value(prefix, conv),
1766 self.get_dyn_array_len(prefix, conv))
1767 elif self.is_struct():
1768 ref_part = "" if self.optional else "&"
1769 return " convert_{0}_host_to_{3}({4}{2}_host, {1});\n".format(
1770 self.type, self.value(prefix, conv), self.name, win_type, ref_part)
1771 elif self.is_pointer_size() and self.type != "size_t":
1772 return " *{0} = PtrToUlong({1}_host);\n".format(self.value(prefix, conv), self.name)
1774 return " *{0} = {1}_host;\n".format(self.value(prefix, conv), self.name)
1776 def definition(self, postfix=None, is_member=False, conv=False):
1777 """ Return prototype for the parameter. E.g. 'const char *foo' """
1779 if is_member and conv and self.needs_ptr32_type():
1780 return "PTR32 {0}".format(self.name)
1784 proto += self.const + " "
1788 if conv and self.needs_win32_type():
1791 if is_member and self.needs_alignment():
1792 proto += " DECLSPEC_ALIGN(8)"
1794 if self.is_pointer():
1795 proto += " {0}{1}".format(self.pointer, name)
1796 elif is_member and self.is_static_array():
1797 proto += " *" + name
1801 # Allows appending something to the variable name useful for
1802 # win32 to host conversion.
1803 if postfix is not None:
1806 if not is_member and self.is_static_array():
1807 proto += "[{0}]".format(self.array_len)
1811 def dispatch_table(self, params_prefix, conv):
1812 """ Return functions dispatch table pointer for dispatchable objects. """
1814 if not self.is_dispatchable():
1817 return self.handle.dispatch_table(self.value(params_prefix, conv))
1819 def format_string(self, conv):
1820 if conv and self.needs_ptr32_type() and (self.type != "size_t" or self.is_pointer()):
1822 return self.format_str
1824 def is_dispatchable(self):
1825 if not self.is_handle():
1828 return self.handle.is_dispatchable()
1830 def needs_conversion(self, conv, unwrap, direction, parent_const=False):
1831 """ Check if param needs conversion. """
1833 if self.is_struct():
1834 return self.struct.needs_conversion(conv, unwrap, direction, self.is_const())
1836 if self.is_handle():
1837 # non-pointer handles are handled inline in thunks
1838 if not self.is_dynamic_array() and not self.is_static_array():
1839 return conv and self.is_pointer() and self.handle.is_dispatchable()
1841 # vkAllocateCommandBuffers is a special case, we use it in our private thunk as an input param
1842 param_direction = (Direction.INPUT if self.is_const() else Direction.OUTPUT)
1843 if self.name == "pCommandBuffers":
1844 param_direction = Direction.INPUT
1845 if direction != param_direction:
1848 if unwrap and self.handle.is_wrapped():
1850 if conv and self.handle.is_dispatchable():
1852 elif self.is_pointer() and self.is_pointer_size():
1857 def needs_variable(self, conv, unwrap):
1858 if self.needs_conversion(conv, unwrap, Direction.INPUT):
1860 if self.needs_conversion(conv, unwrap, Direction.OUTPUT):
1865 """ Generate spec file entry for this parameter. """
1867 if self.is_pointer() and self.type == "char":
1869 if self.is_dispatchable() or self.is_pointer() or self.is_static_array():
1871 if self.type_info["category"] in ["bitmask"]:
1872 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1873 if self.type_info["data"].type == "VkFlags64":
1877 if self.type_info["category"] in ["enum"]:
1879 if self.is_handle() and not self.is_dispatchable():
1881 if self.type == "float":
1883 if self.type in ["int", "int32_t", "size_t", "uint16_t", "uint32_t", "VkBool32"]:
1885 if self.type in ["uint64_t", "VkDeviceSize"]:
1888 LOGGER.error("Unhandled spec conversion for type: {0}".format(self.type))
1890 def variable(self, conv, unwrap, params_prefix=""):
1891 """ Returns 'glue' code during generation of a function call on how to access the variable.
1892 This function handles various scenarios such as 'unwrapping' if dispatchable objects and
1893 renaming of parameters in case of win32 -> host conversion.
1896 conv (bool, optional): Enable conversion if the param needs it. This appends '_host' to the name.
1899 # Hack until we enable allocation callbacks from ICD to application. These are a joy
1900 # to enable one day, because of calling convention conversion.
1901 if unwrap and "VkAllocationCallbacks" in self.type:
1902 LOGGER.debug("TODO: setting NULL VkAllocationCallbacks for {0}".format(self.name))
1905 if self.needs_variable(conv, unwrap):
1906 if self.is_dynamic_array() or self.optional:
1907 return "{0}_host".format(self.name)
1909 return "&{0}_host".format(self.name)
1911 p = self.value(params_prefix, conv)
1914 unwrap_handle = None
1915 if self.object_type != None and self.type == "uint64_t":
1916 unwrap_handle = "wine_vk_unwrap_handle({0}{1}, {0}{2})".format(
1917 params_prefix, self.object_type, self.name)
1919 elif self.is_handle():
1920 # We need to pass the native handle to the native Vulkan calls and
1921 # the wine driver's handle to calls which are wrapped by the driver.
1922 unwrap_handle = self.handle.driver_handle(p)
1925 unwrap_handle = "{0}{1} ? {2} : 0".format(params_prefix, self.name, unwrap_handle)
1926 return unwrap_handle
1931 class VkStruct(Sequence):
1932 """ Class which represents the type union and struct. """
1934 def __init__(self, name, members, returnedonly, structextends, alias=None, union=False):
1936 self.members = members
1937 self.returnedonly = returnedonly
1938 self.structextends = structextends
1939 self.required = False
1942 self.type_info = None # To be set later.
1943 self.struct_extensions = []
1944 self.aliased_by = []
1946 def __getitem__(self, i):
1947 return self.members[i]
1950 return len(self.members)
1953 def from_alias(struct, alias):
1954 name = struct.attrib.get("name")
1955 aliasee = VkStruct(name, alias.members, alias.returnedonly, alias.structextends, alias=alias)
1957 alias.add_aliased_by(aliasee)
1961 def from_xml(struct):
1962 if not api_is_vulkan(struct):
1965 # Unions and structs are the same parsing wise, but we need to
1966 # know which one we are dealing with later on for code generation.
1967 union = True if struct.attrib["category"] == "union" else False
1969 name = struct.attrib.get("name")
1971 # 'Output' structures for which data is filled in by the API are
1972 # marked as 'returnedonly'.
1973 returnedonly = True if struct.attrib.get("returnedonly") else False
1975 # Those structs seem to be broken in spec, they are specified as
1976 # returned only, but documented as input structs.
1977 if name in ["VkSubpassShadingPipelineCreateInfoHUAWEI",
1978 "VkPipelineShaderStageRequiredSubgroupSizeCreateInfo"]:
1979 returnedonly = False
1981 # Those structs don't have returnedonly in spec, but they could (should?).
1982 if name in ["VkSurfaceCapabilitiesPresentBarrierNV",
1983 "VkCooperativeMatrixPropertiesNV",
1984 "VkPerformanceValueINTEL"]:
1987 structextends = struct.attrib.get("structextends")
1988 structextends = structextends.split(",") if structextends else []
1990 s = VkStruct(name, [], returnedonly, structextends, union=union)
1991 for member in struct.findall("member"):
1992 vk_member = VkMember.from_xml(member, returnedonly, s)
1994 s.members.append(vk_member)
1999 def decouple_structs(structs):
2000 """ Helper function which decouples a list of structs.
2001 Structures often depend on other structures. To make the C compiler
2002 happy we need to define 'substructures' first. This function analyzes
2003 the list of structures and reorders them in such a way that they are
2007 tmp_structs = list(structs) # Don't modify the original structures.
2008 decoupled_structs = []
2010 while (len(tmp_structs) > 0):
2011 # Iterate over a copy because we want to modify the list inside the loop.
2012 for struct in list(tmp_structs):
2015 if not struct.required:
2016 tmp_structs.remove(struct)
2020 if not (m.is_struct() or m.is_union()):
2023 # VkBaseInstructure and VkBaseOutStructure reference themselves.
2024 if m.type == struct.name:
2028 # Check if a struct we depend on has already been defined.
2029 for s in decoupled_structs:
2030 if s.name == m.type:
2035 # Check if the struct we depend on is even in the list of structs.
2036 # If found now, it means we haven't met all dependencies before we
2037 # can operate on the current struct.
2038 # When generating 'host' structs we may not be able to find a struct
2039 # as the list would only contain the structs requiring conversion.
2040 for s in tmp_structs:
2041 if s.name == m.type:
2045 if dependends == False:
2046 decoupled_structs.append(struct)
2047 tmp_structs.remove(struct)
2049 return decoupled_structs
2051 def definition(self, align=False, conv=False):
2052 """ Convert structure to textual definition.
2055 align (bool, optional): enable alignment to 64-bit for win32 struct compatibility.
2056 conv (bool, optional): enable struct conversion if the struct needs it.
2057 postfix (str, optional): text to append to end of struct name, useful for struct renaming.
2063 suffix = "32" if conv else ""
2065 text = "typedef union {0}".format(self.name)
2067 text = "typedef struct {0}".format(self.name)
2073 if align and m.needs_alignment():
2074 text += " {0};\n".format(m.definition(align=align, conv=conv))
2076 text += " {0};\n".format(m.definition(conv=conv))
2078 text += "}} {0}{1};\n".format(self.name, suffix)
2080 for aliasee in self.aliased_by:
2081 text += "typedef {0}{2} {1}{2};\n".format(self.name, aliasee.name, suffix)
2086 return bool(self.alias)
2088 def add_aliased_by(self, aliasee):
2089 self.aliased_by.append(aliasee)
2091 def needs_alignment(self):
2092 """ Check if structure needs alignment for 64-bit data.
2093 Various structures need alignment on 64-bit variables due
2094 to compiler differences on 32-bit between Win32 and Linux.
2097 for m in self.members:
2098 if self.name == m.type:
2100 if m.needs_alignment():
2104 def needs_unwrapping(self):
2105 """ Returns if struct members need unwrapping of handle. """
2107 for m in self.members:
2108 if self.name == m.type:
2110 if m.needs_unwrapping():
2114 def needs_extensions_conversion(self, conv, direction):
2115 """ Check if struct contains extensions chain that needs to be converted """
2117 if direction == Direction.INPUT and self.name in STRUCT_CHAIN_CONVERSIONS:
2120 if not "pNext" in self:
2122 is_const = self.members[self.members.index("pNext")].is_const()
2123 # VkOpticalFlowSessionCreateInfoNV is missing const in its pNext pointer
2124 if self.name in ["VkOpticalFlowSessionCreateInfoNV",
2125 "VkDescriptorBufferBindingInfoEXT"]:
2127 needs_output_copy = False
2129 for e in self.struct_extensions:
2132 if e.needs_conversion(conv, True, direction, is_const, check_extensions=False):
2134 if direction == Direction.INPUT:
2135 # we need input conversion of structs containing struct chain even if it's returnedonly,
2136 # so that we have a chance to allocate buffers
2137 if e.needs_conversion(conv, True, Direction.OUTPUT, is_const, check_extensions=False):
2142 def needs_conversion(self, conv, unwrap, direction, is_const, check_extensions=True):
2143 """ Check if struct needs conversion. """
2145 # VkAllocationCallbacks never needs conversion
2146 if self.name == "VkAllocationCallbacks":
2149 # pFixedRateFlags field is missing const, but it doesn't need output conversion
2150 if direction == Direction.OUTPUT and self.name == "VkImageCompressionControlEXT":
2153 needs_output_copy = False
2155 for m in self.members:
2156 if self.name == m.type:
2159 if m.name == "pNext":
2160 # pNext is a pointer, so it always needs conversion
2161 if conv and direction == Direction.INPUT:
2163 # we need input conversion of structs containing struct chain even if it's returnedonly
2164 if direction == Direction.INPUT and \
2165 self.needs_conversion(conv, unwrap, Direction.OUTPUT, is_const):
2169 # for non-pointer members, check for returnedonly and const attributes
2170 if not m.is_pointer() or m.type == "void":
2171 if direction == Direction.INPUT:
2172 if self.returnedonly:
2175 if is_const or m.is_const():
2178 # check alignment and pointer-sized members for 32-bit conversions
2179 if conv and (direction == Direction.INPUT or not is_const):
2180 if m.is_pointer() or m.is_pointer_size():
2182 # we don't check structs here, they will will be traversed by needs_conversion chain anyway
2183 if not m.is_struct() and m.needs_alignment():
2186 if m.needs_conversion(conv, unwrap, direction, is_const):
2189 # pointers will be handled by needs_conversion, but if we have any other non-const
2190 # member, we may need to copy output
2191 if direction == Direction.OUTPUT and not m.is_pointer() and not is_const and not m.is_const():
2192 needs_output_copy = True
2194 # if output needs any copy and we need input conversion, then we also need output conversion
2195 if needs_output_copy and self.needs_conversion(conv, unwrap, Direction.INPUT, check_extensions):
2198 return check_extensions and self.needs_extensions_conversion(conv, direction)
2200 def needs_alloc(self, conv, unwrap):
2201 """ Check if any struct member needs some memory allocation."""
2203 if self.needs_extensions_conversion(conv, Direction.INPUT):
2206 for m in self.members:
2207 if self.name == m.type:
2209 if m.needs_alloc(conv, unwrap):
2214 def needs_win32_type(self):
2215 # VkAllocationCallbacks never needs conversion
2216 if self.name == "VkAllocationCallbacks":
2219 for m in self.members:
2220 if self.name == m.type:
2222 if m.is_pointer() or m.is_pointer_size():
2224 if m.needs_alignment():
2226 if (m.is_struct() or m.is_union()) and m.struct.needs_win32_type():
2229 def set_type_info(self, types):
2230 """ Helper function to set type information from the type registry.
2231 This is needed, because not all type data is available at time of
2234 for m in self.members:
2235 type_info = types[m.type]
2236 m.set_type_info(type_info)
2238 def get_conversions(self, unwrap, parent_const):
2241 # Collect any conversion for any extension structs.
2242 for e in self.struct_extensions:
2245 conversions.extend(e.get_conversions(True, parent_const))
2247 # Collect any conversion for any member structs.
2249 if m.type == self.name:
2251 conversions.extend(m.get_conversions(unwrap, parent_const))
2256 class StructConversionFunction(object):
2257 def __init__(self, struct, direction, conv, unwrap, const):
2258 self.direction = direction
2259 self.operand = struct
2260 self.type = struct.name
2262 self.unwrap = unwrap or not self.operand.needs_unwrapping()
2265 name = "convert_{0}_".format(self.type)
2266 win_type = "win32" if self.conv else "win64"
2267 host_part = "host" if self.unwrap else "unwrapped_host"
2268 if self.direction == Direction.INPUT:
2269 name += "{0}_to_{1}".format(win_type, host_part)
2270 else: # Direction.OUTPUT
2271 name += "{0}_to_{1}".format(host_part, win_type)
2274 def __eq__(self, other):
2275 return self.name == other.name
2277 def member_needs_copy(self, struct, m):
2278 if self.direction == Direction.OUTPUT:
2279 if m.name in ["sType", "pNext"]:
2281 if self.const and not m.is_pointer():
2283 if m.is_const() and not m.needs_conversion(self.conv, self.unwrap, Direction.OUTPUT, self.const):
2286 if m.name == "pNext":
2288 if m.name != "sType" and struct.returnedonly and not m.needs_conversion(
2289 self.conv, self.unwrap, Direction.INPUT, self.const):
2293 def definition(self):
2294 """ Helper function for generating a struct conversion function. """
2296 # It doesn't make sense to generate conversion functions for non-struct variables
2297 # which aren't in arrays, as this should be handled by the copy() function
2298 if not isinstance(self.operand, VkStruct):
2304 body += "#ifdef _WIN64\n"
2306 needs_alloc = self.direction != Direction.OUTPUT and self.operand.needs_alloc(self.conv, self.unwrap)
2307 win_type = self.type
2308 if self.conv and self.operand.needs_win32_type():
2310 if self.direction == Direction.OUTPUT and self.const:
2311 win_type = "const " + win_type
2314 body += "static inline void {0}(".format(self.name)
2316 if self.direction == Direction.OUTPUT:
2317 params = ["const {0} *in".format(self.type), "{0} *out".format(win_type)]
2319 params = ["const {0} *in".format(win_type), "{0} *out".format(self.type)]
2321 if self.operand.union:
2322 params.append("VkFlags selector")
2324 # Generate parameter list
2326 body += "struct conversion_context *ctx, "
2327 body += ", ".join(p for p in params)
2331 body += "static inline void {0}(".format(self.name)
2333 params = ["const {0} *in".format(self.type), "{0} *out".format(self.type)]
2335 # Generate parameter list
2337 body += "struct conversion_context *ctx, "
2338 body += ", ".join(p for p in params)
2341 needs_extensions = self.operand.needs_extensions_conversion(self.conv, self.direction)
2344 if needs_extensions:
2345 if self.direction == Direction.INPUT:
2347 body += " const VkBaseInStructure32 *in_header;\n"
2349 body += " const VkBaseInStructure *in_header;\n"
2350 body += " VkBaseOutStructure *out_header = (void *)out;\n\n"
2352 body += " const VkBaseInStructure *in_header;\n"
2354 body += " VkBaseOutStructure32 *out_header = (void *)out;\n\n"
2356 body += " VkBaseOutStructure *out_header = (void *)out;\n\n"
2358 body += " if (!in) return;\n\n"
2360 for m in self.operand:
2361 if not self.member_needs_copy(self.operand, m):
2363 if m.name == "pNext" and (needs_extensions or self.conv):
2364 body += " out->pNext = NULL;\n"
2369 body += " || ".join("selector == {}".format(s) for s in m.selection)
2372 body += " " + m.copy("in->", "out->", self.direction, self.conv, self.unwrap)
2374 if needs_extensions:
2375 if self.conv and self.direction == Direction.INPUT:
2376 body += "\n for (in_header = UlongToPtr(in->pNext); in_header; in_header = UlongToPtr(in_header->pNext))\n"
2378 body += "\n for (in_header = (void *)in->pNext; in_header; in_header = (void *)in_header->pNext)\n"
2380 body += " switch (in_header->sType)\n"
2385 if self.direction == Direction.INPUT and self.type in STRUCT_CHAIN_CONVERSIONS:
2386 for i in STRUCT_CHAIN_CONVERSIONS[self.type]:
2387 body += " case {0}:\n".format(i)
2388 body += ident + "break;\n"
2390 for ext in self.operand.struct_extensions:
2391 if not ext.required:
2394 if self.direction == Direction.OUTPUT and not any([self.member_needs_copy(ext, m) for m in ext]):
2397 stype = next(x for x in ext.members if x.name == "sType").values
2398 win_type = ext.name + "32" if self.conv and ext.needs_win32_type() else ext.name
2399 if self.direction == Direction.INPUT:
2400 in_type = "const " + win_type
2403 in_type = "const " + ext.name
2406 body += " case {0}:\n".format(stype)
2408 if self.direction == Direction.INPUT:
2409 body += ident + "{0} *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));\n".format(out_type)
2411 body += ident + "{0} *out_ext = find_next_struct32(out_header, {1});\n".format(out_type, stype)
2413 body += ident + "{0} *out_ext = find_next_struct(out_header, {1});\n".format(out_type, stype)
2418 if m.name == "sType":
2419 copy_body += ident + "out_ext->sType = {0};\n".format(stype)
2421 if not self.member_needs_copy(ext, m):
2423 if m.name == "pNext":
2424 copy_body += ident + "out_ext->pNext = NULL;\n"
2427 copy_body += ident + m.copy("in_ext->", "out_ext->", self.direction, self.conv, True)
2429 # Generate the definition of "in_ext" if we need it
2430 if "in_ext->" in copy_body:
2431 body += ident + "{0} *in_ext = ({0} *)in_header;\n".format(in_type)
2434 if self.direction == Direction.INPUT:
2435 body += ident + "out_header->pNext = (void *)out_ext;\n"
2436 body += ident + "out_header = (void *)out_ext;\n"
2437 body += ident + "break;\n"
2440 body += " default:\n"
2441 if self.direction == Direction.INPUT:
2442 body += ident + "FIXME(\"Unhandled sType %u.\\n\", in_header->sType);\n"
2446 elif self.conv and self.direction == Direction.INPUT and "pNext" in self.operand:
2447 body += " if (in->pNext)\n"
2448 body += " FIXME(\"Unexpected pNext\\n\");\n"
2452 body += "#endif /* _WIN64 */\n"
2458 class ArrayConversionFunction(object):
2459 def __init__(self, array, direction, conv, unwrap):
2461 self.direction = direction
2462 self.type = array.type
2464 self.unwrap = unwrap or not array.needs_unwrapping()
2466 if array.is_static_array() and direction == Direction.INPUT:
2467 LOGGER.error("Static array input conversion is not supported")
2469 name = "convert_{0}_".format(array.type)
2470 if array.pointer_array:
2473 win_type = "win32" if self.conv else "win64"
2474 host_part = "host" if self.unwrap else "unwrapped_host"
2475 if self.direction == Direction.INPUT:
2476 name += "{0}_to_{1}".format(win_type, host_part)
2477 else: # Direction.OUTPUT
2478 name += "{0}_to_{1}".format(host_part, win_type)
2481 def __eq__(self, other):
2482 return self.name == other.name
2484 def definition(self):
2485 """ Helper function for generating a conversion function for array operands. """
2490 body += "#ifdef _WIN64\n"
2492 needs_alloc = self.direction != Direction.OUTPUT and self.array.needs_alloc(self.conv, self.unwrap)
2494 win_type = self.type
2496 if self.array.needs_win32_type():
2498 elif self.array.is_handle() and self.array.handle.is_dispatchable():
2500 if self.direction == Direction.OUTPUT and self.array.is_const():
2501 win_type = "const " + win_type
2502 pointer_part = self.array.pointer if self.array.pointer else "*"
2504 if self.direction == Direction.OUTPUT:
2505 params = ["const {0} {1}in".format(self.type, pointer_part),
2506 "{0} {1}out".format(win_type, pointer_part), "uint32_t count"]
2508 elif self.conv and self.array.pointer_array:
2509 params = ["const PTR32 *in", "uint32_t count"]
2510 return_type = self.type
2512 params = ["const {0} {1}in".format(win_type, pointer_part), "uint32_t count"]
2513 return_type = self.type
2515 needs_copy = not self.array.is_struct() or self.direction != Direction.INPUT or \
2516 not self.array.struct.returnedonly or "pNext" in self.array.struct
2518 # Generate function prototype.
2520 body += "static inline {0}{1} {2}{3}(".format(
2521 "const " if self.array.is_const() else "", return_type, pointer_part, self.name)
2523 body += "static inline void {0}(".format(self.name)
2525 body += "struct conversion_context *ctx, "
2526 body += ", ".join(p for p in params)
2530 body += " {0} {1}out;\n".format(return_type, "**" if self.array.pointer_array else "*")
2532 body += " unsigned int i;\n\n"
2535 body += " if (!in || !count) return NULL;\n\n"
2537 body += " if (!in) return;\n\n"
2539 if self.direction == Direction.INPUT:
2540 body += " out = conversion_context_alloc(ctx, count * sizeof(*out));\n"
2543 body += " for (i = 0; i < count; i++)\n"
2546 if self.array.is_struct():
2547 struct = self.array.struct
2548 win_part = "win32" if self.conv else "win64"
2549 host_part = "host" if self.unwrap else "unwrapped_host"
2550 if self.direction == Direction.INPUT:
2551 conv_suffix = "{0}_to_{1}".format(win_part, host_part)
2553 conv_suffix = "{0}_to_{1}".format(host_part, win_part)
2556 if self.direction == Direction.INPUT and struct.needs_alloc(self.conv, self.unwrap):
2559 if not self.array.pointer_array:
2560 body += " convert_{0}_{1}({2}&in[i], &out[i]);\n".format(
2561 struct.name, conv_suffix, ctx_part)
2563 if struct.needs_conversion(self.conv, self.unwrap, self.direction, False):
2564 body += " if (in[i])\n"
2566 body += " out[i] = conversion_context_alloc(ctx, sizeof(*out[i]));\n"
2568 in_param = "({0} *)UlongToPtr(in[i])".format(win_type)
2571 body += " convert_{0}_{1}({2}{3}, out[i]);\n".format(
2572 struct.name, conv_suffix, ctx_part, in_param)
2575 body += " out[i] = NULL;\n"
2577 body += " out[i] = UlongToPtr(in[i]);\n".format(win_type)
2578 elif self.array.is_handle():
2579 if self.array.pointer_array:
2580 LOGGER.error("Unhandled handle pointer arrays")
2581 handle = self.array.handle
2582 if not self.conv or not handle.is_dispatchable():
2584 elif self.direction == Direction.INPUT:
2585 input = "UlongToPtr(in[i])"
2587 input = "PtrToUlong(in[i])"
2589 if not self.unwrap or not handle.is_wrapped():
2590 body += " out[i] = {0};\n".format(input)
2591 elif self.direction == Direction.INPUT:
2592 body += " out[i] = " + handle.driver_handle(input) + ";\n"
2594 LOGGER.warning("Unhandled handle output conversion")
2595 elif self.array.pointer_array:
2596 body += " out[i] = UlongToPtr(in[i]);\n"
2598 body += " out[i] = in[i];\n"
2603 body += "\n return {0}out;\n".format("(void *)" if self.array.pointer_array else "")
2607 body += "#endif /* _WIN64 */\n"
2614 class VkGenerator(object):
2615 def __init__(self, registry):
2616 self.registry = registry
2618 # Build a list conversion functions for struct conversion.
2619 self.conversions = []
2620 self.win32_structs = []
2621 for func in self.registry.funcs.values():
2622 if not func.needs_exposing():
2625 conversions = func.get_conversions()
2626 for conv in conversions:
2627 # Append if we don't already have this conversion.
2628 if not any(c == conv for c in self.conversions):
2629 self.conversions.append(conv)
2631 if not isinstance(conv, StructConversionFunction):
2634 for e in conv.operand.struct_extensions:
2635 if not e.required or not e.needs_win32_type():
2637 if not any(s.name == e.name for s in self.win32_structs):
2638 self.win32_structs.append(e)
2640 if not conv.operand.needs_win32_type():
2643 # Structs can be used in different ways by different conversions
2644 # e.g. array vs non-array. Just make sure we pull in each struct once.
2645 if not any(s.name == conv.operand.name for s in self.win32_structs):
2646 self.win32_structs.append(conv.operand)
2648 def _generate_copyright(self, f, spec_file=False):
2649 f.write("# " if spec_file else "/* ")
2650 f.write("Automatically generated from Vulkan vk.xml; DO NOT EDIT!\n")
2651 lines = ["", "This file is generated from Vulkan vk.xml file covered",
2652 "by the following copyright and permission notice:"]
2653 lines.extend([l.rstrip(" ") for l in self.registry.copyright.splitlines()])
2655 f.write("{0}{1}".format("# " if spec_file else " * ", line).rstrip(" ") + "\n")
2656 f.write("\n" if spec_file else " */\n\n")
2658 def generate_thunks_c(self, f):
2659 self._generate_copyright(f)
2662 f.write("#pragma makedep unix\n")
2663 f.write("#endif\n\n")
2665 f.write("#include \"config.h\"\n\n")
2667 f.write("#include <stdlib.h>\n\n")
2669 f.write("#include \"vulkan_private.h\"\n\n")
2671 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2673 for struct in self.win32_structs:
2674 f.write(struct.definition(conv=True, align=True))
2677 f.write("static uint64_t wine_vk_unwrap_handle(uint32_t type, uint64_t handle)\n")
2679 f.write(" switch(type)\n")
2681 for handle in self.registry.handles:
2682 if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2684 f.write(" case {}:\n".format(handle.object_type))
2685 if handle.is_dispatchable():
2686 f.write(" return (uint64_t) (uintptr_t) ")
2687 f.write(handle.native_handle("(({}) (uintptr_t) handle)".format(handle.name)))
2689 f.write(" return (uint64_t) ")
2690 f.write(handle.native_handle("handle"))
2692 f.write(" default:\n")
2693 f.write(" return handle;\n")
2697 # Generate any conversion helper functions.
2698 for conv in self.conversions:
2699 f.write(conv.definition())
2701 # Create thunks for instance and device functions.
2702 # Global functions don't go through the thunks.
2703 for vk_func in self.registry.funcs.values():
2704 if not vk_func.needs_exposing():
2706 if vk_func.loader_thunk_type == ThunkType.NONE:
2709 f.write(vk_func.thunk(prefix="thunk64_"))
2710 f.write(vk_func.thunk(prefix="thunk32_", conv=True))
2712 # Create array of device extensions.
2713 f.write("static const char * const vk_device_extensions[] =\n{\n")
2714 for ext in self.registry.extensions:
2715 if ext["type"] != "device":
2717 if ext["name"] in UNEXPOSED_EXTENSIONS:
2720 f.write(" \"{0}\",\n".format(ext["name"]))
2723 # Create array of instance extensions.
2724 f.write("static const char * const vk_instance_extensions[] =\n{\n")
2725 for ext in self.registry.extensions:
2726 if ext["type"] != "instance":
2728 if ext["name"] in UNEXPOSED_EXTENSIONS:
2731 f.write(" \"{0}\",\n".format(ext["name"]))
2734 f.write("BOOL wine_vk_device_extension_supported(const char *name)\n")
2736 f.write(" unsigned int i;\n")
2737 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_extensions); i++)\n")
2739 f.write(" if (strcmp(vk_device_extensions[i], name) == 0)\n")
2740 f.write(" return TRUE;\n")
2742 f.write(" return FALSE;\n")
2745 f.write("BOOL wine_vk_instance_extension_supported(const char *name)\n")
2747 f.write(" unsigned int i;\n")
2748 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_extensions); i++)\n")
2750 f.write(" if (strcmp(vk_instance_extensions[i], name) == 0)\n")
2751 f.write(" return TRUE;\n")
2753 f.write(" return FALSE;\n")
2756 f.write("BOOL wine_vk_is_type_wrapped(VkObjectType type)\n")
2758 f.write(" return FALSE")
2759 for handle in self.registry.handles:
2760 if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2762 f.write(" ||\n type == {}".format(handle.object_type))
2767 f.write("#ifdef _WIN64\n\n")
2769 f.write("const unixlib_entry_t __wine_unix_call_funcs[] =\n")
2771 f.write(" init_vulkan,\n")
2772 f.write(" vk_is_available_instance_function,\n")
2773 f.write(" vk_is_available_device_function,\n")
2774 for vk_func in self.registry.funcs.values():
2775 if not vk_func.needs_exposing():
2777 if vk_func.loader_thunk_type == ThunkType.NONE:
2780 if vk_func.is_perf_critical():
2781 f.write(" (void *){1}{0},\n".format(vk_func.name, "thunk64_"))
2783 f.write(" {1}{0},\n".format(vk_func.name, "thunk64_"))
2785 f.write("C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_count);\n\n")
2787 f.write("#endif /* _WIN64 */\n\n")
2789 f.write("#ifdef _WIN64\n")
2790 f.write("const unixlib_entry_t __wine_unix_call_wow64_funcs[] =\n")
2792 f.write("const unixlib_entry_t __wine_unix_call_funcs[] =\n")
2795 f.write(" init_vulkan,\n")
2796 f.write(" vk_is_available_instance_function32,\n")
2797 f.write(" vk_is_available_device_function32,\n")
2798 for vk_func in self.registry.funcs.values():
2799 if not vk_func.needs_exposing():
2801 if vk_func.loader_thunk_type == ThunkType.NONE:
2804 if vk_func.is_perf_critical():
2805 f.write(" (void *){1}{0},\n".format(vk_func.name, "thunk32_"))
2807 f.write(" {1}{0},\n".format(vk_func.name, "thunk32_"))
2809 f.write("C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_count);\n")
2811 def generate_thunks_h(self, f, prefix):
2812 self._generate_copyright(f)
2814 f.write("#ifndef __WINE_VULKAN_THUNKS_H\n")
2815 f.write("#define __WINE_VULKAN_THUNKS_H\n\n")
2817 f.write("#define WINE_VK_VERSION VK_API_VERSION_{0}_{1}\n\n".format(WINE_VK_VERSION[0], WINE_VK_VERSION[1]))
2819 # Generate prototypes for device and instance functions requiring a custom implementation.
2820 f.write("/* Functions for which we have custom implementations outside of the thunks. */\n")
2821 for vk_func in self.registry.funcs.values():
2822 if not vk_func.needs_private_thunk():
2825 f.write("{0};\n".format(vk_func.prototype(prefix=prefix, is_thunk=True)))
2828 f.write("/* For use by vkDevice and children */\n")
2829 f.write("struct vulkan_device_funcs\n{\n")
2830 for vk_func in self.registry.device_funcs:
2831 if not vk_func.needs_exposing():
2834 if not vk_func.needs_dispatch():
2835 LOGGER.debug("skipping {0} in vulkan_device_funcs".format(vk_func.name))
2838 f.write(" {0};\n".format(vk_func.pfn()))
2841 f.write("/* For use by vkInstance and children */\n")
2842 f.write("struct vulkan_instance_funcs\n{\n")
2843 for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs:
2844 if not vk_func.needs_exposing():
2847 if not vk_func.needs_dispatch():
2848 LOGGER.debug("skipping {0} in vulkan_instance_funcs".format(vk_func.name))
2851 f.write(" {0};\n".format(vk_func.pfn()))
2854 f.write("#define ALL_VK_DEVICE_FUNCS() \\\n")
2856 for vk_func in self.registry.device_funcs:
2857 if not vk_func.needs_exposing():
2860 if not vk_func.needs_dispatch():
2861 LOGGER.debug("skipping {0} in ALL_VK_DEVICE_FUNCS".format(vk_func.name))
2865 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2868 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2871 f.write("#define ALL_VK_INSTANCE_FUNCS() \\\n")
2873 for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs:
2874 if not vk_func.needs_exposing():
2877 if not vk_func.needs_dispatch():
2878 LOGGER.debug("skipping {0} in ALL_VK_INSTANCE_FUNCS".format(vk_func.name))
2882 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2885 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2888 f.write("#endif /* __WINE_VULKAN_THUNKS_H */\n")
2890 def generate_loader_thunks_c(self, f):
2891 self._generate_copyright(f)
2893 f.write("#include \"vulkan_loader.h\"\n\n")
2895 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2897 for vk_func in self.registry.funcs.values():
2898 if not vk_func.needs_exposing():
2900 if vk_func.loader_thunk_type != ThunkType.PUBLIC:
2903 f.write(vk_func.loader_thunk())
2905 f.write("static const struct vulkan_func vk_device_dispatch_table[] =\n{\n")
2906 for vk_func in self.registry.device_funcs:
2907 if not vk_func.needs_exposing():
2910 f.write(" {{\"{0}\", {0}}},\n".format(vk_func.name))
2913 f.write("static const struct vulkan_func vk_phys_dev_dispatch_table[] =\n{\n")
2914 for vk_func in self.registry.phys_dev_funcs:
2915 if not vk_func.needs_exposing():
2918 f.write(" {{\"{0}\", {0}}},\n".format(vk_func.name))
2921 f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
2922 for vk_func in self.registry.instance_funcs:
2923 if not vk_func.needs_exposing():
2926 f.write(" {{\"{0}\", {0}}},\n".format(vk_func.name))
2929 f.write("void *wine_vk_get_device_proc_addr(const char *name)\n")
2931 f.write(" unsigned int i;\n")
2932 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_dispatch_table); i++)\n")
2934 f.write(" if (strcmp(vk_device_dispatch_table[i].name, name) == 0)\n")
2936 f.write(" TRACE(\"Found name=%s in device table\\n\", debugstr_a(name));\n")
2937 f.write(" return vk_device_dispatch_table[i].func;\n")
2940 f.write(" return NULL;\n")
2943 f.write("void *wine_vk_get_phys_dev_proc_addr(const char *name)\n")
2945 f.write(" unsigned int i;\n")
2946 f.write(" for (i = 0; i < ARRAY_SIZE(vk_phys_dev_dispatch_table); i++)\n")
2948 f.write(" if (strcmp(vk_phys_dev_dispatch_table[i].name, name) == 0)\n")
2950 f.write(" TRACE(\"Found name=%s in physical device table\\n\", debugstr_a(name));\n")
2951 f.write(" return vk_phys_dev_dispatch_table[i].func;\n")
2954 f.write(" return NULL;\n")
2957 f.write("void *wine_vk_get_instance_proc_addr(const char *name)\n")
2959 f.write(" unsigned int i;\n")
2960 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_dispatch_table); i++)\n")
2962 f.write(" if (strcmp(vk_instance_dispatch_table[i].name, name) == 0)\n")
2964 f.write(" TRACE(\"Found name=%s in instance table\\n\", debugstr_a(name));\n")
2965 f.write(" return vk_instance_dispatch_table[i].func;\n")
2968 f.write(" return NULL;\n")
2971 def generate_loader_thunks_h(self, f):
2972 self._generate_copyright(f)
2974 f.write("#ifndef __WINE_VULKAN_LOADER_THUNKS_H\n")
2975 f.write("#define __WINE_VULKAN_LOADER_THUNKS_H\n\n")
2977 f.write("enum unix_call\n")
2979 f.write(" unix_init,\n")
2980 f.write(" unix_is_available_instance_function,\n")
2981 f.write(" unix_is_available_device_function,\n")
2982 for vk_func in self.registry.funcs.values():
2983 if not vk_func.needs_exposing():
2985 if vk_func.loader_thunk_type == ThunkType.NONE:
2988 f.write(" unix_{0},\n".format(vk_func.name))
2989 f.write(" unix_count,\n")
2992 for vk_func in self.registry.funcs.values():
2993 if not vk_func.needs_exposing():
2995 if vk_func.loader_thunk_type == ThunkType.NONE:
2998 f.write("struct {0}_params\n".format(vk_func.name))
3000 for p in vk_func.params:
3001 f.write(" {0};\n".format(p.definition(is_member=True)))
3002 if vk_func.extra_param:
3003 f.write(" void *{0};\n".format(vk_func.extra_param))
3004 if vk_func.type != "void":
3005 f.write(" {0} result;\n".format(vk_func.type))
3008 f.write("#endif /* __WINE_VULKAN_LOADER_THUNKS_H */\n")
3010 def generate_vulkan_h(self, f):
3011 self._generate_copyright(f)
3012 f.write("#ifndef __WINE_VULKAN_H\n")
3013 f.write("#define __WINE_VULKAN_H\n\n")
3015 f.write("#include <windef.h>\n")
3016 f.write("#include <stdint.h>\n\n")
3018 f.write("/* Define WINE_VK_HOST to get 'host' headers. */\n")
3019 f.write("#ifdef WINE_VK_HOST\n")
3020 f.write("#define VKAPI_CALL\n")
3021 f.write('#define WINE_VK_ALIGN(x)\n')
3022 f.write("#endif\n\n")
3024 f.write("#ifndef VKAPI_CALL\n")
3025 f.write("#define VKAPI_CALL __stdcall\n")
3026 f.write("#endif\n\n")
3028 f.write("#ifndef VKAPI_PTR\n")
3029 f.write("#define VKAPI_PTR VKAPI_CALL\n")
3030 f.write("#endif\n\n")
3032 f.write("#ifndef WINE_VK_ALIGN\n")
3033 f.write("#define WINE_VK_ALIGN DECLSPEC_ALIGN\n")
3034 f.write("#endif\n\n")
3036 # The overall strategy is to define independent constants and datatypes,
3037 # prior to complex structures and function calls to avoid forward declarations.
3038 for const in self.registry.consts:
3039 # For now just generate things we may not need. The amount of parsing needed
3040 # to get some of the info is tricky as you need to figure out which structure
3041 # references a certain constant.
3042 f.write(const.definition())
3045 for define in self.registry.defines:
3046 f.write(define.definition())
3048 for handle in self.registry.handles:
3049 # For backward compatibility also create definitions for aliases.
3050 # These types normally don't get pulled in as we use the new types
3051 # even in legacy functions if they are aliases.
3052 if handle.is_required() or handle.is_alias():
3053 f.write(handle.definition())
3056 for base_type in self.registry.base_types:
3057 f.write(base_type.definition())
3060 for bitmask in self.registry.bitmasks:
3061 f.write(bitmask.definition())
3064 # Define enums, this includes values for some of the bitmask types as well.
3065 for enum in self.registry.enums.values():
3067 f.write(enum.definition())
3069 for fp in self.registry.funcpointers:
3071 f.write(fp.definition())
3074 # This generates both structures and unions. Since structures
3075 # may depend on other structures/unions, we need a list of
3076 # decoupled structs.
3077 # Note: unions are stored in structs for dependency reasons,
3078 # see comment in parsing section.
3079 structs = VkStruct.decouple_structs(self.registry.structs)
3080 for struct in structs:
3081 LOGGER.debug("Generating struct: {0}".format(struct.name))
3082 f.write(struct.definition(align=True))
3085 for func in self.registry.funcs.values():
3086 if not func.is_required():
3087 LOGGER.debug("Skipping PFN definition for: {0}".format(func.name))
3090 f.write("typedef {0};\n".format(func.pfn(prefix="PFN", call_conv="VKAPI_PTR")))
3093 f.write("#ifndef VK_NO_PROTOTYPES\n")
3094 for func in self.registry.funcs.values():
3095 if not func.is_required():
3096 LOGGER.debug("Skipping API definition for: {0}".format(func.name))
3099 LOGGER.debug("Generating API definition for: {0}".format(func.name))
3100 f.write("{0};\n".format(func.prototype(call_conv="VKAPI_CALL")))
3101 f.write("#endif /* VK_NO_PROTOTYPES */\n\n")
3103 f.write("#endif /* __WINE_VULKAN_H */\n")
3105 def generate_vulkan_driver_h(self, f):
3106 self._generate_copyright(f)
3107 f.write("#ifndef __WINE_VULKAN_DRIVER_H\n")
3108 f.write("#define __WINE_VULKAN_DRIVER_H\n\n")
3110 f.write("/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */\n")
3111 f.write("#define WINE_VULKAN_DRIVER_VERSION {0}\n\n".format(DRIVER_VERSION))
3113 f.write("struct vulkan_funcs\n{\n")
3114 f.write(" /* Vulkan global functions. These are the only calls at this point a graphics driver\n")
3115 f.write(" * needs to provide. Other function calls will be provided indirectly by dispatch\n")
3116 f.write(" * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice.\n")
3119 for vk_func in self.registry.funcs.values():
3120 if not vk_func.is_driver_func():
3124 # Avoid PFN_vkVoidFunction in driver interface as Vulkan likes to put calling convention
3125 # stuff in there. For simplicity substitute with "void *".
3126 pfn = pfn.replace("PFN_vkVoidFunction", "void *")
3127 f.write(" {0};\n".format(pfn))
3130 f.write(" /* winevulkan specific functions */\n")
3131 f.write(" VkSurfaceKHR (*p_wine_get_native_surface)(VkSurfaceKHR);\n")
3134 f.write("static inline void *get_vulkan_driver_device_proc_addr(\n")
3135 f.write(" const struct vulkan_funcs *vulkan_funcs, const char *name)\n{\n")
3136 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
3137 f.write(" name += 2;\n\n")
3138 for vk_func in self.registry.funcs.values():
3139 if vk_func.is_driver_func() and vk_func.is_device_func():
3140 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3141 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3143 f.write(" return NULL;\n}\n\n")
3145 f.write("static inline void *get_vulkan_driver_instance_proc_addr(\n")
3146 f.write(" const struct vulkan_funcs *vulkan_funcs, VkInstance instance, const char *name)\n{\n")
3147 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
3148 f.write(" name += 2;\n\n")
3149 for vk_func in self.registry.funcs.values():
3150 if vk_func.is_driver_func() and vk_func.is_global_func() and vk_func.name != "vkGetInstanceProcAddr":
3151 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3152 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3154 f.write(" if (!instance) return NULL;\n\n")
3155 for vk_func in self.registry.funcs.values():
3156 if vk_func.is_driver_func() and (vk_func.is_instance_func() or vk_func.is_phys_dev_func()):
3157 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3158 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3160 f.write(" name -= 2;\n\n")
3161 f.write(" return get_vulkan_driver_device_proc_addr(vulkan_funcs, name);\n}\n\n")
3163 f.write("#endif /* __WINE_VULKAN_DRIVER_H */\n")
3165 def generate_vulkan_spec(self, f):
3166 self._generate_copyright(f, spec_file=True)
3167 f.write("@ stdcall -private vk_icdGetInstanceProcAddr(ptr str)\n")
3168 f.write("@ stdcall -private vk_icdGetPhysicalDeviceProcAddr(ptr str)\n")
3169 f.write("@ stdcall -private vk_icdNegotiateLoaderICDInterfaceVersion(ptr)\n")
3171 # Export symbols for all Vulkan Core functions.
3172 for func in self.registry.funcs.values():
3173 if not func.is_core_func():
3176 # We support all Core functions except for VK_KHR_display* APIs.
3177 # Create stubs for unsupported Core functions.
3178 if func.is_required():
3179 f.write(func.spec())
3181 f.write("@ stub {0}\n".format(func.name))
3183 f.write("@ stdcall -private DllRegisterServer()\n")
3184 f.write("@ stdcall -private DllUnregisterServer()\n")
3186 def generate_vulkan_loader_spec(self, f):
3187 self._generate_copyright(f, spec_file=True)
3189 # Export symbols for all Vulkan Core functions.
3190 for func in self.registry.funcs.values():
3191 if not func.is_core_func():
3194 # We support all Core functions except for VK_KHR_display* APIs.
3195 # Create stubs for unsupported Core functions.
3196 if func.is_required():
3197 f.write(func.spec(symbol="winevulkan." + func.name))
3199 f.write("@ stub {0}\n".format(func.name))
3202 class VkRegistry(object):
3203 def __init__(self, reg_filename):
3204 # Used for storage of type information.
3205 self.base_types = None
3206 self.bitmasks = None
3210 self.funcpointers = None
3214 # We aggregate all types in here for cross-referencing.
3218 self.version_regex = re.compile(
3227 # Overall strategy for parsing the registry is to first
3228 # parse all type / function definitions. Then parse
3229 # features and extensions to decide which types / functions
3230 # to actually 'pull in' for code generation. For each type or
3231 # function call we want we set a member 'required' to True.
3232 tree = ET.parse(reg_filename)
3233 root = tree.getroot()
3234 self._parse_enums(root)
3235 self._parse_types(root)
3236 self._parse_commands(root)
3238 # Pull in any required types and functions.
3239 self._parse_features(root)
3240 self._parse_extensions(root)
3242 for enum in self.enums.values():
3243 enum.fixup_64bit_aliases()
3245 self._match_object_types()
3247 self.copyright = root.find('./comment').text
3249 def _is_feature_supported(self, feature):
3250 version = self.version_regex.match(feature)
3254 version = tuple(map(int, version.group('major', 'minor')))
3255 return version <= WINE_VK_VERSION
3257 def _is_extension_supported(self, extension):
3258 # We disable some extensions as either we haven't implemented
3259 # support yet or because they are for platforms other than win32.
3260 return extension not in UNSUPPORTED_EXTENSIONS
3262 def _mark_command_required(self, command):
3263 """ Helper function to mark a certain command and the datatypes it needs as required."""
3264 def mark_bitmask_dependencies(bitmask, types):
3265 if bitmask.requires is not None:
3266 types[bitmask.requires]["data"].required = True
3268 def mark_funcpointer_dependencies(fp, types):
3269 for m in fp.members:
3270 type_info = types[m.type]
3272 # Complex types have a matching definition e.g. VkStruct.
3273 # Not needed for base types such as uint32_t.
3274 if "data" in type_info:
3275 types[m.type]["data"].required = True
3277 def mark_struct_dependencies(struct, types):
3279 type_info = types[m.type]
3281 # Complex types have a matching definition e.g. VkStruct.
3282 # Not needed for base types such as uint32_t.
3283 if "data" in type_info:
3284 types[m.type]["data"].required = True
3286 if type_info["category"] == "struct" and struct.name != m.type:
3288 mark_struct_dependencies(type_info["data"], types)
3289 elif type_info["category"] == "funcpointer":
3290 mark_funcpointer_dependencies(type_info["data"], types)
3291 elif type_info["category"] == "bitmask":
3292 mark_bitmask_dependencies(type_info["data"], types)
3294 func = self.funcs[command]
3295 func.required = True
3297 # Pull in return type
3298 if func.type != "void":
3299 self.types[func.type]["data"].required = True
3301 # Analyze parameter dependencies and pull in any type needed.
3302 for p in func.params:
3303 type_info = self.types[p.type]
3305 # Check if we are dealing with a complex type e.g. VkEnum, VkStruct and others.
3306 if "data" not in type_info:
3309 # Mark the complex type as required.
3310 type_info["data"].required = True
3311 if type_info["category"] == "struct":
3312 struct = type_info["data"]
3313 mark_struct_dependencies(struct, self.types)
3314 elif type_info["category"] == "bitmask":
3315 mark_bitmask_dependencies(type_info["data"], self.types)
3317 def _match_object_types(self):
3318 """ Matches each handle with the correct object type. """
3319 # Use upper case comparison for simplicity.
3321 for value in self.enums["VkObjectType"].values:
3322 object_name = "VK" + value.name[len("VK_OBJECT_TYPE"):].replace("_", "")
3323 object_types[object_name] = value.name
3325 for handle in self.handles:
3326 if not handle.is_required():
3328 handle.object_type = object_types.get(handle.name.upper())
3329 if not handle.object_type:
3330 LOGGER.warning("No object type found for {}".format(handle.name))
3332 def _parse_commands(self, root):
3333 """ Parse command section containing the Vulkan function calls. """
3335 commands = root.findall("./commands/")
3337 # As of Vulkan 1.1, various extensions got promoted to Core.
3338 # The old commands (e.g. KHR) are available for backwards compatibility
3339 # and are marked in vk.xml as 'alias' to the non-extension type.
3340 # The registry likes to avoid data duplication, so parameters and other
3341 # metadata need to be looked up from the Core command.
3342 # We parse the alias commands in a second pass.
3344 for command in commands:
3345 alias_name = command.attrib.get("alias")
3347 alias_commands.append(command)
3350 func = VkFunction.from_xml(command, self.types)
3352 funcs[func.name] = func
3354 for command in alias_commands:
3355 alias_name = command.attrib.get("alias")
3356 alias = funcs[alias_name]
3357 func = VkFunction.from_alias(command, alias)
3359 funcs[func.name] = func
3361 # To make life easy for the code generation, separate all function
3362 # calls out in the 4 types of Vulkan functions:
3363 # device, global, physical device and instance.
3368 for func in funcs.values():
3369 if func.is_device_func():
3370 device_funcs.append(func)
3371 elif func.is_global_func():
3372 global_funcs.append(func)
3373 elif func.is_phys_dev_func():
3374 phys_dev_funcs.append(func)
3376 instance_funcs.append(func)
3378 # Sort function lists by name and store them.
3379 self.device_funcs = sorted(device_funcs, key=lambda func: func.name)
3380 self.global_funcs = sorted(global_funcs, key=lambda func: func.name)
3381 self.phys_dev_funcs = sorted(phys_dev_funcs, key=lambda func: func.name)
3382 self.instance_funcs = sorted(instance_funcs, key=lambda func: func.name)
3384 # The funcs dictionary is used as a convenient way to lookup function
3385 # calls when needed e.g. to adjust member variables.
3386 self.funcs = OrderedDict(sorted(funcs.items()))
3388 def _parse_enums(self, root):
3389 """ Parse enums section or better described as constants section. """
3392 for enum in root.findall("./enums"):
3393 name = enum.attrib.get("name")
3394 _type = enum.attrib.get("type")
3396 if _type in ("enum", "bitmask"):
3397 enum_obj = VkEnum.from_xml(enum)
3399 enums[name] = enum_obj
3401 # If no type is set, we are dealing with API constants.
3402 for value in enum.findall("enum"):
3403 # If enum is an alias, set the value to the alias name.
3404 # E.g. VK_LUID_SIZE_KHR is an alias to VK_LUID_SIZE.
3405 alias = value.attrib.get("alias")
3407 self.consts.append(VkConstant(value.attrib.get("name"), alias))
3409 self.consts.append(VkConstant(value.attrib.get("name"), value.attrib.get("value")))
3411 self.enums = OrderedDict(sorted(enums.items()))
3413 def _process_require_enum(self, enum_elem, ext=None, only_aliased=False):
3414 if "extends" in enum_elem.keys():
3415 enum = self.types[enum_elem.attrib["extends"]]["data"]
3417 # Need to define VkEnumValues which were aliased to by another value. This is necessary
3418 # from VK spec version 1.2.135 where the provisional VK_KHR_ray_tracing extension was
3419 # added which altered VK_NV_ray_tracing's VkEnumValues to alias to the provisional
3422 for _, t in self.types.items():
3423 if t["category"] != "enum":
3427 for value in t["data"].values:
3428 if value.alias == enum_elem.attrib["name"]:
3431 if only_aliased and not aliased:
3434 if "bitpos" in enum_elem.keys():
3435 # We need to add an extra value to an existing enum type.
3436 # E.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG to VkFormatFeatureFlagBits.
3437 enum.create_bitpos(enum_elem.attrib["name"], int(enum_elem.attrib["bitpos"]))
3439 elif "offset" in enum_elem.keys():
3440 # Extensions promoted to Core, have the extension number as part
3441 # of the enum value. Else retrieve from the extension tag.
3442 if enum_elem.attrib.get("extnumber"):
3443 ext_number = int(enum_elem.attrib.get("extnumber"))
3445 ext_number = int(ext.attrib["number"])
3446 offset = int(enum_elem.attrib["offset"])
3447 value = EXT_BASE + (ext_number - 1) * EXT_BLOCK_SIZE + offset
3449 # Deal with negative values.
3450 direction = enum_elem.attrib.get("dir")
3451 if direction is not None:
3454 enum.create_value(enum_elem.attrib["name"], str(value))
3456 elif "value" in enum_elem.keys():
3457 enum.create_value(enum_elem.attrib["name"], enum_elem.attrib["value"])
3458 elif "alias" in enum_elem.keys():
3459 enum.create_alias(enum_elem.attrib["name"], enum_elem.attrib["alias"])
3461 elif "value" in enum_elem.keys():
3462 # Constant with an explicit value
3466 self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["value"]))
3467 elif "alias" in enum_elem.keys():
3469 if not only_aliased:
3472 self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["alias"]))
3475 def _require_type(type_info):
3476 if type_info.is_alias():
3477 type_info = type_info.alias
3478 type_info.required = True
3479 if type(type_info) == VkStruct:
3480 for member in type_info.members:
3481 if "data" in member.type_info:
3482 VkRegistry._require_type(member.type_info["data"])
3484 def _parse_extensions(self, root):
3485 """ Parse extensions section and pull in any types and commands for this extension. """
3487 exts = root.findall("./extensions/extension")
3489 skipped_exts = UNSUPPORTED_EXTENSIONS.copy()
3491 def process_ext(ext, deferred=False):
3492 ext_name = ext.attrib["name"]
3494 # Set extension name on any functions calls part of this extension as we
3495 # were not aware of the name during initial parsing.
3496 commands = ext.findall("require/command")
3497 for command in commands:
3498 cmd_name = command.attrib["name"]
3499 # Need to verify that the command is defined, and otherwise skip it.
3500 # vkCreateScreenSurfaceQNX is declared in <extensions> but not defined in
3501 # <commands>. A command without a definition cannot be enabled, so it's valid for
3502 # the XML file to handle this, but because of the manner in which we parse the XML
3503 # file we pre-populate from <commands> before we check if a command is enabled.
3504 if cmd_name in self.funcs:
3505 self.funcs[cmd_name].extensions.add(ext_name)
3507 # Some extensions are not ready or have numbers reserved as a place holder
3508 # or are only supported for VulkanSC.
3509 if not "vulkan" in ext.attrib["supported"].split(","):
3510 LOGGER.debug("Skipping disabled extension: {0}".format(ext_name))
3511 skipped_exts.append(ext_name)
3514 # Defer extensions with 'sortorder' as they are order-dependent for spec-parsing.
3515 if not deferred and "sortorder" in ext.attrib:
3516 deferred_exts.append(ext)
3519 # Disable highly experimental extensions as the APIs are unstable and can
3520 # change between minor Vulkan revisions until API is final and becomes KHR
3522 if ("KHX" in ext_name or "NVX" in ext_name) and ext_name not in ALLOWED_X_EXTENSIONS:
3523 LOGGER.debug("Skipping experimental extension: {0}".format(ext_name))
3524 skipped_exts.append(ext_name)
3527 # Extensions can define VkEnumValues which alias to provisional extensions. Pre-process
3528 # extensions to define any required VkEnumValues before the platform check below.
3529 for require in ext.findall("require"):
3530 # Extensions can add enum values to Core / extension enums, so add these.
3531 for enum_elem in require.findall("enum"):
3532 self._process_require_enum(enum_elem, ext, only_aliased=True)
3534 platform = ext.attrib.get("platform")
3535 if platform and platform != "win32":
3536 LOGGER.debug("Skipping extensions {0} for platform {1}".format(ext_name, platform))
3537 skipped_exts.append(ext_name)
3540 if not self._is_extension_supported(ext_name):
3541 LOGGER.debug("Skipping unsupported extension: {0}".format(ext_name))
3542 skipped_exts.append(ext_name)
3544 elif "requires" in ext.attrib:
3545 # Check if this extension builds on top of another unsupported extension.
3546 requires = ext.attrib["requires"].split(",")
3547 if len(set(requires).intersection(skipped_exts)) > 0:
3548 skipped_exts.append(ext_name)
3550 elif "depends" in ext.attrib:
3551 # The syntax for this is more complex, but this is good enough for now.
3552 if any([sext in ext.attrib["depends"] for sext in skipped_exts]):
3553 skipped_exts.append(ext_name)
3556 LOGGER.debug("Loading extension: {0}".format(ext_name))
3558 # Extensions can define one or more require sections each requiring
3559 # different features (e.g. Vulkan 1.1). Parse each require section
3560 # separately, so we can skip sections we don't want.
3561 for require in ext.findall("require"):
3562 # Extensions can add enum values to Core / extension enums, so add these.
3563 for enum_elem in require.findall("enum"):
3564 self._process_require_enum(enum_elem, ext)
3566 for t in require.findall("type"):
3567 type_info = self.types[t.attrib["name"]]["data"]
3568 self._require_type(type_info)
3569 feature = require.attrib.get("feature")
3570 if feature and not self._is_feature_supported(feature):
3573 required_extension = require.attrib.get("extension")
3574 if required_extension and not self._is_extension_supported(required_extension):
3577 # Pull in any commands we need. We infer types to pull in from the command
3579 for command in require.findall("command"):
3580 cmd_name = command.attrib["name"]
3581 self._mark_command_required(cmd_name)
3584 # Store a list with extensions.
3585 ext_info = {"name" : ext_name, "type" : ext.attrib["type"]}
3586 extensions.append(ext_info)
3589 # Process extensions, allowing for sortorder to defer extension processing
3593 deferred_exts.sort(key=lambda ext: ext.attrib["sortorder"])
3596 for ext in deferred_exts:
3597 process_ext(ext, deferred=True)
3599 # Sort in alphabetical order.
3600 self.extensions = sorted(extensions, key=lambda ext: ext["name"])
3602 def _parse_features(self, root):
3603 """ Parse the feature section, which describes Core commands and types needed. """
3605 for feature in root.findall("./feature"):
3606 if not api_is_vulkan(feature):
3608 feature_name = feature.attrib["name"]
3609 for require in feature.findall("require"):
3610 LOGGER.info("Including features for {0}".format(require.attrib.get("comment")))
3612 if tag.tag == "comment":
3614 elif tag.tag == "command":
3615 if not self._is_feature_supported(feature_name):
3617 name = tag.attrib["name"]
3618 self._mark_command_required(name)
3619 elif tag.tag == "enum":
3620 self._process_require_enum(tag)
3621 elif tag.tag == "type":
3622 name = tag.attrib["name"]
3624 # Skip pull in for vk_platform.h for now.
3625 if name == "vk_platform":
3628 type_info = self.types[name]
3629 type_info["data"].required = True
3631 def _parse_types(self, root):
3632 """ Parse types section, which contains all data types e.g. structs, typedefs etcetera. """
3633 types = root.findall("./types/type")
3645 type_info["category"] = t.attrib.get("category", None)
3646 type_info["requires"] = t.attrib.get("requires", None)
3648 # We parse aliases in a second pass when we know more.
3649 alias = t.attrib.get("alias")
3651 LOGGER.debug("Alias found: {0}".format(alias))
3652 alias_types.append(t)
3655 if type_info["category"] in ["include"]:
3658 if type_info["category"] == "basetype":
3659 name = t.find("name").text
3661 if not t.find("type") is None:
3662 _type = t.find("type").text
3663 tail = t.find("type").tail
3664 if tail is not None:
3665 _type += tail.strip()
3666 basetype = VkBaseType(name, _type)
3668 base_types.append(basetype)
3669 type_info["data"] = basetype
3673 # Basic C types don't need us to define them, but we do need data for them
3674 if type_info["requires"] == "vk_platform":
3675 requires = type_info["requires"]
3676 basic_c = VkBaseType(name, _type, requires=requires)
3677 type_info["data"] = basic_c
3679 if type_info["category"] == "bitmask":
3680 name = t.find("name").text
3681 _type = t.find("type").text
3683 # Most bitmasks have a requires attribute used to pull in
3684 # required '*FlagBits" enum.
3685 requires = type_info["requires"]
3686 bitmask = VkBaseType(name, _type, requires=requires)
3687 bitmasks.append(bitmask)
3688 type_info["data"] = bitmask
3690 if type_info["category"] == "define":
3691 define = VkDefine.from_xml(t)
3693 defines.append(define)
3694 type_info["data"] = define
3698 if type_info["category"] == "enum":
3699 name = t.attrib.get("name")
3700 # The type section only contains enum names, not the actual definition.
3701 # Since we already parsed the enum before, just link it in.
3703 type_info["data"] = self.enums[name]
3704 except KeyError as e:
3705 # Not all enums seem to be defined yet, typically that's for
3706 # ones ending in 'FlagBits' where future extensions may add
3708 type_info["data"] = None
3710 if type_info["category"] == "funcpointer":
3711 funcpointer = VkFunctionPointer.from_xml(t)
3713 funcpointers.append(funcpointer)
3714 type_info["data"] = funcpointer
3718 if type_info["category"] == "handle":
3719 handle = VkHandle.from_xml(t)
3721 handles.append(handle)
3722 type_info["data"] = handle
3726 if type_info["category"] in ["struct", "union"]:
3727 # We store unions among structs as some structs depend
3728 # on unions. The types are very similar in parsing and
3729 # generation anyway. The official Vulkan scripts use
3730 # a similar kind of hack.
3731 struct = VkStruct.from_xml(t)
3733 structs.append(struct)
3734 type_info["data"] = struct
3738 # Name is in general within a name tag else it is an optional
3739 # attribute on the type tag.
3740 name_elem = t.find("name")
3741 if name_elem is not None:
3742 type_info["name"] = name_elem.text
3744 type_info["name"] = t.attrib.get("name", None)
3746 # Store all type data in a shared dictionary, so we can easily
3747 # look up information for a given type. There are no duplicate
3749 self.types[type_info["name"]] = type_info
3751 # Second pass for alias types, so we can retrieve all data from
3752 # the aliased object.
3753 for t in alias_types:
3755 type_info["category"] = t.attrib.get("category")
3756 type_info["name"] = t.attrib.get("name")
3758 alias = t.attrib.get("alias")
3760 if type_info["category"] == "bitmask":
3761 bitmask = VkBaseType(type_info["name"], alias, alias=self.types[alias]["data"])
3762 bitmasks.append(bitmask)
3763 type_info["data"] = bitmask
3765 if type_info["category"] == "enum":
3766 enum = VkEnum.from_alias(t, self.types[alias]["data"])
3767 type_info["data"] = enum
3768 self.enums[enum.name] = enum
3770 if type_info["category"] == "handle":
3771 handle = VkHandle.from_alias(t, self.types[alias]["data"])
3772 handles.append(handle)
3773 type_info["data"] = handle
3775 if type_info["category"] == "struct":
3776 struct = VkStruct.from_alias(t, self.types[alias]["data"])
3777 structs.append(struct)
3778 type_info["data"] = struct
3780 self.types[type_info["name"]] = type_info
3782 # We need detailed type information during code generation
3783 # on structs for alignment reasons. Unfortunately structs
3784 # are parsed among other types, so there is no guarantee
3785 # that any types needed have been parsed already, so set
3787 for struct in structs:
3788 struct.set_type_info(self.types)
3790 # Alias structures have enum values equivalent to those of the
3791 # structure which they are aliased against. we need to ignore alias
3792 # structs when populating the struct extensions list, otherwise we
3793 # will create duplicate case entries.
3797 for structextend in struct.structextends:
3798 s = self.types[structextend]["data"]
3799 s.struct_extensions.append(struct)
3801 # Guarantee everything is sorted, so code generation doesn't have
3802 # to deal with this.
3803 self.base_types = sorted(base_types, key=lambda base_type: base_type.name)
3804 self.bitmasks = sorted(bitmasks, key=lambda bitmask: bitmask.name)
3805 self.defines = defines
3806 self.enums = OrderedDict(sorted(self.enums.items()))
3807 self.funcpointers = sorted(funcpointers, key=lambda fp: fp.name)
3808 self.handles = sorted(handles, key=lambda handle: handle.name)
3809 self.structs = sorted(structs, key=lambda struct: struct.name)
3811 def generate_vulkan_json(f):
3813 f.write(" \"file_format_version\": \"1.0.0\",\n")
3814 f.write(" \"ICD\": {\n")
3815 f.write(" \"library_path\": \".\\\\winevulkan.dll\",\n")
3816 f.write(" \"api_version\": \"{0}\"\n".format(VK_XML_VERSION))
3820 def set_working_directory():
3821 path = os.path.abspath(__file__)
3822 path = os.path.dirname(path)
3825 def download_vk_xml(filename):
3826 url = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/v{0}/xml/vk.xml".format(VK_XML_VERSION)
3827 if not os.path.isfile(filename):
3828 urllib.request.urlretrieve(url, filename)
3831 parser = argparse.ArgumentParser()
3832 parser.add_argument("-v", "--verbose", action="count", default=0, help="increase output verbosity")
3833 parser.add_argument("-x", "--xml", default=None, type=str, help="path to specification XML file")
3835 args = parser.parse_args()
3836 if args.verbose == 0:
3837 LOGGER.setLevel(logging.WARNING)
3838 elif args.verbose == 1:
3839 LOGGER.setLevel(logging.INFO)
3841 LOGGER.setLevel(logging.DEBUG)
3843 set_working_directory()
3848 vk_xml = "vk-{0}.xml".format(VK_XML_VERSION)
3849 download_vk_xml(vk_xml)
3851 registry = VkRegistry(vk_xml)
3852 generator = VkGenerator(registry)
3854 with open(WINE_VULKAN_H, "w") as f:
3855 generator.generate_vulkan_h(f)
3857 with open(WINE_VULKAN_DRIVER_H, "w") as f:
3858 generator.generate_vulkan_driver_h(f)
3860 with open(WINE_VULKAN_THUNKS_H, "w") as f:
3861 generator.generate_thunks_h(f, "wine_")
3863 with open(WINE_VULKAN_THUNKS_C, "w") as f:
3864 generator.generate_thunks_c(f)
3866 with open(WINE_VULKAN_LOADER_THUNKS_H, "w") as f:
3867 generator.generate_loader_thunks_h(f)
3869 with open(WINE_VULKAN_LOADER_THUNKS_C, "w") as f:
3870 generator.generate_loader_thunks_c(f)
3872 with open(WINE_VULKAN_JSON, "w") as f:
3873 generate_vulkan_json(f)
3875 with open(WINE_VULKAN_SPEC, "w") as f:
3876 generator.generate_vulkan_spec(f)
3878 with open(WINE_VULKAN_LOADER_SPEC, "w") as f:
3879 generator.generate_vulkan_loader_spec(f)
3881 if __name__ == "__main__":