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
27 import xml.etree.ElementTree as ET
28 from collections import OrderedDict
29 from collections.abc import Sequence
32 # This script generates code for a Wine Vulkan ICD driver from Vulkan's vk.xml.
33 # Generating the code is like 10x worse than OpenGL, which is mostly a calling
34 # convention passthrough.
36 # The script parses vk.xml and maps functions and types to helper objects. These
37 # helper objects simplify the xml parsing and map closely to the Vulkan types.
38 # The code generation utilizes the helper objects during code generation and
39 # most of the ugly work is carried out by these objects.
41 # Vulkan ICD challenges:
42 # - Vulkan ICD loader (vulkan-1.dll) relies on a section at the start of
43 # 'dispatchable handles' (e.g. VkDevice, VkInstance) for it to insert
44 # its private data. It uses this area to stare its own dispatch tables
45 # for loader internal use. This means any dispatchable objects need wrapping.
47 # - Vulkan structures have different alignment between win32 and 32-bit Linux.
48 # This means structures with alignment differences need conversion logic.
49 # Often structures are nested, so the parent structure may not need any
50 # conversion, but some child may need some.
52 # vk.xml parsing challenges:
53 # - Contains type data for all platforms (generic Vulkan, Windows, Linux,..).
54 # Parsing of extension information required to pull in types and functions
55 # we really want to generate. Just tying all the data together is tricky.
57 # - Extensions can affect core types e.g. add new enum values, bitflags or
58 # additional structure chaining through 'pNext' / 'sType'.
60 # - Arrays are used all over the place for parameters or for structure members.
61 # Array length is often stored in a previous parameter or another structure
62 # member and thus needs careful parsing.
64 LOGGER = logging.Logger("vulkan")
65 LOGGER.addHandler(logging.StreamHandler())
67 VK_XML_VERSION = "1.3.285"
68 WINE_VK_VERSION = (1, 3)
70 # Filenames to create.
71 WINE_VULKAN_H = "../../include/wine/vulkan.h"
72 WINE_VULKAN_DRIVER_H = "../../include/wine/vulkan_driver.h"
73 WINE_VULKAN_LOADER_SPEC = "../vulkan-1/vulkan-1.spec"
74 WINE_VULKAN_JSON = "winevulkan.json"
75 WINE_VULKAN_SPEC = "winevulkan.spec"
76 WINE_VULKAN_THUNKS_C = "vulkan_thunks.c"
77 WINE_VULKAN_THUNKS_H = "vulkan_thunks.h"
78 WINE_VULKAN_LOADER_THUNKS_C = "loader_thunks.c"
79 WINE_VULKAN_LOADER_THUNKS_H = "loader_thunks.h"
81 # Extension enum values start at a certain offset (EXT_BASE).
82 # Relative to the offset each extension has a block (EXT_BLOCK_SIZE)
84 # Start for a given extension is:
85 # EXT_BASE + (extension_number-1) * EXT_BLOCK_SIZE
89 UNSUPPORTED_EXTENSIONS = [
91 "VK_EXT_headless_surface", # Needs WSI work.
92 "VK_KHR_display", # Needs WSI work.
93 "VK_KHR_surface_protected_capabilities",
94 "VK_LUNARG_direct_driver_loading", # Implemented in the Vulkan loader
97 "VK_AMD_display_native_hdr",
98 "VK_EXT_full_screen_exclusive",
99 "VK_GOOGLE_display_timing",
100 "VK_KHR_external_fence_win32",
101 "VK_KHR_external_semaphore_win32",
102 # Relates to external_semaphore and needs type conversions in bitflags.
103 "VK_KHR_shared_presentable_image", # Needs WSI work.
104 "VK_KHR_video_encode_queue",
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_EXT_map_memory_placed",
133 "VK_KHR_external_memory_win32",
136 # The Vulkan loader provides entry-points for core functionality and important
137 # extensions. Based on vulkan-1.def this amounts to WSI extensions on 1.0.51.
140 "VK_KHR_display_swapchain",
141 "VK_KHR_get_surface_capabilities2",
144 "VK_KHR_win32_surface",
147 # List of surface extensions that can be exposed directly to the PE side
148 WIN_SURFACE_EXTENSIONS = [
149 "VK_KHR_win32_surface",
150 "VK_EXT_headless_surface",
153 # Some experimental extensions are used by shipping applications so their API is extremely unlikely
154 # to change in a backwards-incompatible way. Allow translation of those extensions with WineVulkan.
155 ALLOWED_X_EXTENSIONS = [
156 "VK_NVX_binary_import",
157 "VK_NVX_image_view_handle",
160 # Some frequently called functions skip traces and checks for performance reasons.
161 PERF_CRITICAL_FUNCTIONS = [
162 "vkUpdateDescriptorSets",
163 "vkUpdateDescriptorSetWithTemplate",
164 "vkGetDescriptorEXT",
167 # Table of functions for which we have a special implementation.
168 # These are regular device / instance functions for which we need
169 # to do more work compared to a regular thunk or because they are
170 # part of the driver interface.
171 # - dispatch (default: True): set whether we need a function pointer in the device / instance dispatch table.
172 FUNCTION_OVERRIDES = {
174 "vkCreateInstance" : {"extra_param" : "client_ptr"},
177 "vkCreateDevice" : {"extra_param" : "client_ptr"},
178 "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : False},
179 "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False},
180 "vkGetPhysicalDeviceExternalSemaphoreProperties" : {"dispatch" : False},
183 "vkCreateCommandPool" : {"extra_param" : "client_ptr"},
184 "vkGetDeviceProcAddr" : {"dispatch" : False},
186 # VK_KHR_external_fence_capabilities
187 "vkGetPhysicalDeviceExternalFencePropertiesKHR" : {"dispatch" : False},
189 # VK_KHR_external_memory_capabilities
190 "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : False},
192 # VK_KHR_external_semaphore_capabilities
193 "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR" : {"dispatch" : False},
196 # functions for which a user driver entry must be generated
197 USER_DRIVER_FUNCS = {
198 "vkCreateWin32SurfaceKHR",
199 "vkDestroySurfaceKHR",
200 "vkGetDeviceProcAddr",
201 "vkGetInstanceProcAddr",
202 "vkGetPhysicalDeviceWin32PresentationSupportKHR",
206 # functions for which the unix thunk is manually implemented
207 MANUAL_UNIX_THUNKS = {
208 "vkAcquireNextImage2KHR",
209 "vkAcquireNextImageKHR",
210 "vkAllocateCommandBuffers",
213 "vkCreateCommandPool",
214 "vkCreateDebugReportCallbackEXT",
215 "vkCreateDebugUtilsMessengerEXT",
216 "vkCreateDeferredOperationKHR",
220 "vkCreateSwapchainKHR",
221 "vkCreateWin32SurfaceKHR",
222 "vkDestroyCommandPool",
223 "vkDestroyDebugReportCallbackEXT",
224 "vkDestroyDebugUtilsMessengerEXT",
225 "vkDestroyDeferredOperationKHR",
228 "vkDestroySurfaceKHR",
229 "vkDestroySwapchainKHR",
230 "vkEnumerateDeviceExtensionProperties",
231 "vkEnumerateDeviceLayerProperties",
232 "vkEnumerateInstanceExtensionProperties",
233 "vkEnumerateInstanceLayerProperties",
234 "vkEnumerateInstanceVersion",
235 "vkEnumeratePhysicalDeviceGroups",
236 "vkEnumeratePhysicalDeviceGroupsKHR",
237 "vkEnumeratePhysicalDevices",
238 "vkFreeCommandBuffers",
240 "vkGetCalibratedTimestampsEXT",
241 "vkGetCalibratedTimestampsKHR",
242 "vkGetDeviceProcAddr",
245 "vkGetInstanceProcAddr",
246 "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT",
247 "vkGetPhysicalDeviceCalibrateableTimeDomainsKHR",
248 "vkGetPhysicalDeviceExternalBufferProperties",
249 "vkGetPhysicalDeviceExternalBufferPropertiesKHR",
250 "vkGetPhysicalDeviceExternalFenceProperties",
251 "vkGetPhysicalDeviceExternalFencePropertiesKHR",
252 "vkGetPhysicalDeviceExternalSemaphoreProperties",
253 "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR",
254 "vkGetPhysicalDeviceImageFormatProperties2",
255 "vkGetPhysicalDeviceImageFormatProperties2KHR",
256 "vkGetPhysicalDevicePresentRectanglesKHR",
257 "vkGetPhysicalDeviceSurfaceCapabilities2KHR",
258 "vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
259 "vkGetPhysicalDeviceSurfaceFormats2KHR",
267 # loader functions which are entirely manually implemented
268 MANUAL_LOADER_FUNCTIONS = {
269 "vkEnumerateInstanceLayerProperties",
270 "vkGetDeviceProcAddr",
271 "vkGetInstanceProcAddr",
274 # functions which loader thunks are manually implemented
275 MANUAL_LOADER_THUNKS = {
276 "vkAllocateCommandBuffers",
277 "vkCreateCommandPool",
280 "vkDestroyCommandPool",
283 "vkEnumerateInstanceExtensionProperties",
284 "vkEnumerateInstanceVersion",
285 "vkFreeCommandBuffers",
286 "vkGetPhysicalDeviceProperties2",
287 "vkGetPhysicalDeviceProperties2KHR",
290 STRUCT_CHAIN_CONVERSIONS = {
291 # Ignore to not confuse host loader.
292 "VkDeviceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO"],
293 "VkInstanceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO"],
296 # Some struct members are conditionally ignored and callers are free to leave them uninitialized.
297 # We can't deduce that from XML, so we allow expressing it here.
298 MEMBER_LENGTH_EXPRESSIONS = {
299 "VkWriteDescriptorSet": {
301 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || " +
302 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || " +
303 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || " +
304 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || " +
305 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT || " +
306 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM || " +
307 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM ? {len} : 0",
309 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || " +
310 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || " +
311 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || " +
312 "{struct}descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC ? {len} : 0",
316 class Direction(Enum):
317 """ Parameter direction: input, output, input_output. """
328 def api_is_vulkan(obj):
329 return "vulkan" in obj.get("api", "vulkan").split(",")
332 def convert_suffix(direction, win_type, unwrap, is_wrapped):
333 if direction == Direction.OUTPUT:
335 return "host_to_{0}".format(win_type)
336 if unwrap == Unwrap.NONE:
337 return "unwrapped_host_to_{0}".format(win_type)
338 if unwrap == Unwrap.DRIVER:
339 return "driver_to_{0}".format(win_type)
340 return "host_to_{0}".format(win_type)
343 return "{0}_to_host".format(win_type)
344 if unwrap == Unwrap.NONE:
345 return "{0}_to_unwrapped_host".format(win_type)
346 if unwrap == Unwrap.DRIVER:
347 return "{0}_to_driver".format(win_type)
348 return "{0}_to_host".format(win_type)
351 class VkBaseType(object):
352 def __init__(self, name, _type, alias=None, requires=None):
353 """ Vulkan base type class.
355 VkBaseType is mostly used by Vulkan to define its own
356 base types like VkFlags through typedef out of e.g. uint32_t.
359 name (:obj:'str'): Name of the base type.
360 _type (:obj:'str'): Underlying type
361 alias (bool): type is an alias or not.
362 requires (:obj:'str', optional): Other types required.
363 Often bitmask values pull in a *FlagBits type.
368 self.requires = requires
369 self.required = False
371 def definition(self):
372 # Definition is similar for alias or non-alias as type
373 # is already set to alias.
374 if not self.type is None:
375 return "typedef {0} {1};\n".format(self.type, self.name)
377 return "struct {0};\n".format(self.name)
380 return bool(self.alias)
383 class VkConstant(object):
384 def __init__(self, name, value):
388 def definition(self):
389 text = "#define {0} {1}\n".format(self.name, self.value)
393 class VkDefine(object):
394 def __init__(self, name, value):
399 def from_xml(define):
400 if not api_is_vulkan(define):
403 name_elem = define.find("name")
405 if name_elem is None:
406 # <type category="define" name="some_name">some_value</type>
407 name = define.attrib.get("name")
409 # We override behavior of VK_USE_64_BIT_PTR_DEFINES as the default non-dispatchable handle
410 # definition various between 64-bit (uses pointers) and 32-bit (uses uint64_t).
411 # This complicates TRACEs in the thunks, so just use uint64_t.
412 if name == "VK_USE_64_BIT_PTR_DEFINES":
413 value = "#define VK_USE_64_BIT_PTR_DEFINES 0"
416 return VkDefine(name, value)
418 # With a name element the structure is like:
419 # <type category="define"><name>some_name</name>some_value</type>
420 name = name_elem.text
422 # Perform minimal parsing for Vulkan constants, which we don't need, but are referenced
423 # elsewhere in vk.xml.
424 # - VK_API_VERSION is a messy, deprecated constant and we don't want generate code for it.
425 # - AHardwareBuffer/ANativeWindow are forward declarations for Android types, which leaked
426 # into the define region.
427 if name in ["VK_API_VERSION", "AHardwareBuffer", "ANativeWindow", "CAMetalLayer"]:
428 return VkDefine(name, None)
430 # The body of the define is basically unstructured C code. It is not meant for easy parsing.
431 # Some lines contain deprecated values or comments, which we try to filter out.
433 for line in define.text.splitlines():
434 # Skip comments or deprecated values.
441 if child.tail is not None:
442 # Split comments for VK_API_VERSION_1_0 / VK_API_VERSION_1_1
443 if "//" in child.tail:
444 value += child.tail.split("//")[0]
448 return VkDefine(name, value.rstrip(' '))
450 def definition(self):
451 if self.value is None:
454 # Nothing to do as the value was already put in the right form during parsing.
455 return "{0}\n".format(self.value)
458 class VkEnum(object):
459 def __init__(self, name, bitwidth, alias=None):
460 if not bitwidth in [32, 64]:
461 LOGGER.error("unknown bitwidth {0} for {1}".format(bitwidth, name))
463 self.bitwidth = bitwidth
464 self.values = [] if alias == None else alias.values
465 self.required = False
470 def from_alias(enum, alias):
471 name = enum.attrib.get("name")
472 aliasee = VkEnum(name, alias.bitwidth, alias=alias)
474 alias.add_aliased_by(aliasee)
479 if not api_is_vulkan(enum):
482 name = enum.attrib.get("name")
483 bitwidth = int(enum.attrib.get("bitwidth", "32"))
484 result = VkEnum(name, bitwidth)
486 for v in enum.findall("enum"):
487 value_name = v.attrib.get("name")
488 # Value is either a value or a bitpos, only one can exist.
489 value = v.attrib.get("value")
490 alias_name = v.attrib.get("alias")
492 result.create_alias(value_name, alias_name)
494 result.create_value(value_name, value)
497 result.create_bitpos(value_name, int(v.attrib.get("bitpos")))
500 # vulkan.h contains a *_MAX_ENUM value set to 32-bit at the time of writing,
501 # which is to prepare for extensions as they can add values and hence affect
502 # the size definition.
503 max_name = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2', name).upper() + "_MAX_ENUM"
504 result.create_value(max_name, "0x7fffffff")
508 def create_alias(self, name, alias_name):
509 """ Create an aliased value for this enum """
510 self.add(VkEnumValue(name, self.bitwidth, alias=alias_name))
512 def create_value(self, name, value):
513 """ Create a new value for this enum """
514 # Some values are in hex form. We want to preserve the hex representation
515 # at least when we convert back to a string. Internally we want to use int.
517 self.add(VkEnumValue(name, self.bitwidth, value=int(value, 0), hex=hex))
519 def create_bitpos(self, name, pos):
520 """ Create a new bitmask value for this enum """
521 self.add(VkEnumValue(name, self.bitwidth, value=(1 << pos), hex=True))
523 def add(self, value):
524 """ Add a value to enum. """
526 # Extensions can add new enum values. When an extension is promoted to Core
527 # the registry defines the value twice once for old extension and once for
528 # new Core features. Add the duplicate if it's explicitly marked as an
529 # alias, otherwise ignore it.
530 for v in self.values:
531 if not value.is_alias() and v.value == value.value:
532 LOGGER.debug("Adding duplicate enum value {0} to {1}".format(v, self.name))
534 # Avoid adding duplicate aliases multiple times
535 if not any(x.name == value.name for x in self.values):
536 self.values.append(value)
538 def fixup_64bit_aliases(self):
539 """ Replace 64bit aliases with literal values """
540 # Older GCC versions need a literal to initialize a static const uint64_t
541 # which is what we use for 64bit bitmasks.
542 if self.bitwidth != 64:
544 for value in self.values:
545 if not value.is_alias():
547 alias = next(x for x in self.values if x.name == value.alias)
548 value.hex = alias.hex
549 value.value = alias.value
551 def definition(self):
555 default_value = 0x7ffffffe if self.bitwidth == 32 else 0xfffffffffffffffe
557 # Print values sorted, values can have been added in a random order.
558 values = sorted(self.values, key=lambda value: value.value if value.value is not None else default_value)
560 if self.bitwidth == 32:
561 text = "typedef enum {0}\n{{\n".format(self.name)
563 text += " {0},\n".format(value.definition())
564 text += "}} {0};\n".format(self.name)
565 elif self.bitwidth == 64:
566 text = "typedef VkFlags64 {0};\n\n".format(self.name)
568 text += "static const {0} {1};\n".format(self.name, value.definition())
570 for aliasee in self.aliased_by:
571 text += "typedef {0} {1};\n".format(self.name, aliasee.name)
577 return bool(self.alias)
579 def add_aliased_by(self, aliasee):
580 self.aliased_by.append(aliasee)
583 class VkEnumValue(object):
584 def __init__(self, name, bitwidth, value=None, hex=False, alias=None):
586 self.bitwidth = bitwidth
592 postfix = "ull" if self.bitwidth == 64 else ""
593 if self.is_alias() and self.value == None:
594 return "{0}={1}".format(self.name, self.alias)
595 return "{0}={1}{2}".format(self.name, self.value, postfix)
597 def definition(self):
598 """ Convert to text definition e.g. VK_FOO = 1 """
599 postfix = "ull" if self.bitwidth == 64 else ""
600 if self.is_alias() and self.value == None:
601 return "{0} = {1}".format(self.name, self.alias)
603 # Hex is commonly used for FlagBits and sometimes within
604 # a non-FlagBits enum for a bitmask value as well.
606 return "{0} = 0x{1:08x}{2}".format(self.name, self.value, postfix)
608 return "{0} = {1}{2}".format(self.name, self.value, postfix)
611 return self.alias is not None
614 class VkFunction(object):
615 def __init__(self, _type=None, name=None, params=[], alias=None):
616 self.extensions = set()
622 # For some functions we need some extra metadata from FUNCTION_OVERRIDES.
623 func_info = FUNCTION_OVERRIDES.get(self.name, {})
624 self.dispatch = func_info.get("dispatch", True)
625 self.extra_param = func_info.get("extra_param", None)
627 # Required is set while parsing which APIs and types are required
628 # and is used by the code generation.
629 self.required = True if func_info else False
631 if self.name in MANUAL_UNIX_THUNKS:
632 self.unwrap = Unwrap.NONE
633 elif self.name in USER_DRIVER_FUNCS:
634 self.unwrap = Unwrap.DRIVER
636 self.unwrap = Unwrap.HOST
639 def from_alias(command, alias):
640 """ Create VkFunction from an alias command.
643 command: xml data for command
644 alias (VkFunction): function to use as a base for types / parameters.
649 if not api_is_vulkan(command):
652 func_name = command.attrib.get("name")
653 func_type = alias.type
654 params = alias.params
656 return VkFunction(_type=func_type, name=func_name, params=params, alias=alias)
659 def from_xml(command, types):
660 if not api_is_vulkan(command):
663 proto = command.find("proto")
664 func_name = proto.find("name").text
665 func_type = proto.find("type").text
668 for param in command.findall("param"):
669 vk_param = VkParam.from_xml(param, types, params)
671 params.append(vk_param)
673 return VkFunction(_type=func_type, name=func_name, params=params)
675 def get_conversions(self):
676 """ Get a list of conversion functions required for this function if any.
677 Parameters which are structures may require conversion between win32
678 and the host platform. This function returns a list of conversions
683 for param in self.params:
684 conversions.extend(param.get_conversions(self.unwrap))
688 return bool(self.alias)
690 def is_core_func(self):
691 """ Returns whether the function is a Vulkan core function.
692 Core functions are APIs defined by the Vulkan spec to be part of the
693 Core API as well as several KHR WSI extensions.
696 if not self.extensions:
699 return any(ext in self.extensions for ext in CORE_EXTENSIONS)
701 def is_device_func(self):
702 # If none of the other, it must be a device function
703 return not self.is_global_func() and not self.is_instance_func() and not self.is_phys_dev_func()
705 def is_driver_func(self):
706 """ Returns if function is part of Wine driver interface. """
707 return self.name in USER_DRIVER_FUNCS
709 def is_global_func(self):
710 # Treat vkGetInstanceProcAddr as a global function as it
711 # can operate with NULL for vkInstance.
712 if self.name == "vkGetInstanceProcAddr":
714 # Global functions are not passed a dispatchable object.
715 elif self.params[0].is_dispatchable():
719 def is_instance_func(self):
720 # Instance functions are passed VkInstance.
721 if self.params[0].type == "VkInstance":
725 def is_phys_dev_func(self):
726 # Physical device functions are passed VkPhysicalDevice.
727 if self.params[0].type == "VkPhysicalDevice":
731 def is_required(self):
734 def returns_longlong(self):
735 return self.type in ["uint64_t", "VkDeviceAddress"]
737 def needs_dispatch(self):
740 def needs_private_thunk(self):
741 return self.needs_exposing() and self.name not in MANUAL_LOADER_FUNCTIONS and \
742 self.name in MANUAL_UNIX_THUNKS
744 def needs_exposing(self):
745 # The function needs exposed if at-least one extension isn't both UNSUPPORTED and UNEXPOSED
746 return self.is_required() and (not self.extensions or not self.extensions.issubset(UNEXPOSED_EXTENSIONS))
748 def is_perf_critical(self):
749 # vkCmd* functions are frequently called, do not trace for performance
750 if self.name.startswith("vkCmd") and self.type == "void":
752 return self.name in PERF_CRITICAL_FUNCTIONS
754 def pfn(self, prefix="p", call_conv=None):
755 """ Create function pointer. """
758 pfn = "{0} ({1} *{2}_{3})(".format(self.type, call_conv, prefix, self.name)
760 pfn = "{0} (*{1}_{2})(".format(self.type, prefix, self.name)
762 for i, param in enumerate(self.params):
764 pfn += param.const + " "
768 if param.is_pointer():
769 pfn += " " + param.pointer
771 if param.array_len is not None:
772 pfn += "[{0}]".format(param.array_len)
774 if i < len(self.params) - 1:
779 def prototype(self, call_conv=None, prefix=None, is_thunk=False):
780 """ Generate prototype for given function.
783 call_conv (str, optional): calling convention e.g. WINAPI
784 prefix (str, optional): prefix to append prior to function name e.g. vkFoo -> wine_vkFoo
787 proto = "{0}".format(self.type)
789 if call_conv is not None:
790 proto += " {0}".format(call_conv)
792 if prefix is not None:
793 proto += " {0}{1}(".format(prefix, self.name)
795 proto += " {0}(".format(self.name)
797 # Add all the parameters.
798 proto += ", ".join([p.definition() for p in self.params])
800 if is_thunk and self.extra_param:
801 proto += ", void *" + self.extra_param
806 def loader_body(self):
807 body = " struct {0}_params params;\n".format(self.name)
808 if not self.is_perf_critical():
809 body += " NTSTATUS status;\n"
810 for p in self.params:
811 body += " params.{0} = {0};\n".format(p.name)
813 # Call the Unix function.
814 if self.is_perf_critical():
815 body += " UNIX_CALL({0}, ¶ms);\n".format(self.name)
817 body += " status = UNIX_CALL({0}, ¶ms);\n".format(self.name)
818 body += " assert(!status && \"{0}\");\n".format(self.name)
820 if self.type != "void":
821 body += " return params.result;\n"
824 def body(self, conv, params_prefix=""):
829 # Declare any tmp parameters for conversion.
830 for p in self.params:
831 if p.needs_variable(conv, self.unwrap):
832 if p.is_dynamic_array():
833 body += " {3}{0}{1} *{2}_host;\n".format(
834 p.type, p.pointer[:-1] if p.pointer else "",
835 p.name, "const " if p.is_const() else "")
837 body += " {0} *{1}_host = NULL;\n".format(p.type, p.name)
840 body += " {0} {1}{2}_host;\n".format(p.type, p.pointer[:-1], p.name)
842 body += " {0} {1}_host;\n".format(p.type, p.name)
843 if p.needs_alloc(conv, self.unwrap):
845 if p.type == "VkDeferredOperationKHR" and not p.is_pointer():
849 body += " struct conversion_context local_ctx;\n"
850 body += " struct conversion_context *ctx = &local_ctx;\n"
853 if not self.is_perf_critical():
854 body += " {0}\n".format(self.trace(params_prefix=params_prefix, conv=conv))
856 if self.params[0].optional and self.params[0].is_handle():
857 if self.type != "void":
858 LOGGER.warning("return type {0} with optional handle not supported".format(self.type))
859 body += " if (!{0}{1})\n".format(params_prefix, self.params[0].name)
860 body += " return STATUS_SUCCESS;\n\n"
863 if deferred_op is not None:
864 body += " if (params->{} == VK_NULL_HANDLE)\n".format(deferred_op)
866 body += " init_conversion_context(ctx);\n"
867 if deferred_op is not None:
869 body += " ctx = &wine_deferred_operation_from_handle(params->{})->ctx;\n".format(deferred_op)
871 # Call any win_to_host conversion calls.
872 unwrap = self.name not in MANUAL_UNIX_THUNKS
873 for p in self.params:
874 if p.needs_conversion(conv, self.unwrap, Direction.INPUT):
875 body += p.copy(Direction.INPUT, conv, self.unwrap, prefix=params_prefix)
876 elif p.is_dynamic_array() and p.needs_conversion(conv, self.unwrap, Direction.OUTPUT):
877 body += " {0}_host = ({2}{0} && {1}) ? conversion_context_alloc(ctx, sizeof(*{0}_host) * {1}) : NULL;\n".format(
878 p.name, p.get_dyn_array_len(params_prefix, conv), params_prefix)
880 # Build list of parameters containing converted and non-converted parameters.
881 # The param itself knows if conversion is needed and applies it when we set conv=True.
882 unwrap = Unwrap.NONE if self.name in MANUAL_UNIX_THUNKS else self.unwrap
883 params = ", ".join([p.variable(conv, unwrap, params_prefix) for p in self.params])
886 params += ", UlongToPtr({0}{1})".format(params_prefix, self.extra_param)
888 params += ", {0}{1}".format(params_prefix, self.extra_param)
890 if self.name not in MANUAL_UNIX_THUNKS:
891 func_prefix = "{0}.p_".format(self.params[0].dispatch_table(params_prefix, conv))
893 func_prefix = "wine_"
895 # Call the host Vulkan function.
896 if self.type == "void":
897 body += " {0}{1}({2});\n".format(func_prefix, self.name, params)
899 body += " {0}result = {1}{2}({3});\n".format(params_prefix, func_prefix, self.name, params)
901 # Call any host_to_win conversion calls.
902 for p in self.params:
903 if p.needs_conversion(conv, self.unwrap, Direction.OUTPUT):
904 body += p.copy(Direction.OUTPUT, conv, self.unwrap, prefix=params_prefix)
907 if deferred_op is not None:
908 body += " if (params->{} == VK_NULL_HANDLE)\n".format(deferred_op)
910 body += " free_conversion_context(ctx);\n"
912 # Finally return the result. Performance critical functions return void to allow tail calls.
913 if not self.is_perf_critical():
914 body += " return STATUS_SUCCESS;\n"
918 def spec(self, prefix=None, symbol=None):
919 """ Generate spec file entry for this function.
922 prefix (str, optional): prefix to prepend to entry point name.
923 symbol (str, optional): allows overriding the name of the function implementing the entry point.
927 params = " ".join([p.spec() for p in self.params])
928 if prefix is not None:
929 spec += "@ stdcall -private {0}{1}({2})".format(prefix, self.name, params)
931 spec += "@ stdcall {0}({1})".format(self.name, params)
933 if symbol is not None:
939 def stub(self, call_conv=None, prefix=None):
940 stub = self.prototype(call_conv=call_conv, prefix=prefix)
942 stub += " {0}".format(self.trace(message="stub: ", trace_func="FIXME"))
944 if self.type == "VkResult":
945 stub += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
946 elif self.type == "VkBool32":
947 stub += " return VK_FALSE;\n"
948 elif self.type == "PFN_vkVoidFunction":
949 stub += " return NULL;\n"
954 def thunk(self, prefix=None, conv=False):
957 thunk += "#ifdef _WIN64\n"
958 if self.is_perf_critical():
959 thunk += "static void {0}{1}(void *args)\n".format(prefix, self.name)
961 thunk += "static NTSTATUS {0}{1}(void *args)\n".format(prefix, self.name)
966 for p in self.params:
967 thunk += " {0};\n".format(p.definition(conv=True, is_member=True))
969 thunk += " PTR32 {0};\n".format(self.extra_param)
970 if self.type != "void":
971 thunk += " {0} result;\n".format(self.type)
972 thunk += " } *params = args;\n"
974 thunk += " struct {0}_params *params = args;\n".format(self.name)
975 thunk += self.body(conv, params_prefix="params->")
978 thunk += "#endif /* _WIN64 */\n"
982 def loader_thunk(self, prefix=None):
983 thunk = self.prototype(call_conv="WINAPI", prefix=prefix)
985 thunk += self.loader_body()
989 def trace(self, message=None, trace_func=None, params_prefix="", conv=False):
990 """ Create a trace string including all parameters.
993 message (str, optional): text to print at start of trace message e.g. 'stub: '
994 trace_func (str, optional): used to override trace function e.g. FIXME, printf, etcetera.
996 if trace_func is not None:
997 trace = "{0}(\"".format(trace_func)
1001 if message is not None:
1004 # First loop is for all the format strings.
1005 trace += ", ".join([p.format_string(conv) for p in self.params])
1008 # Second loop for parameter names and optional conversions.
1009 for param in self.params:
1010 if param.format_conv is not None:
1011 trace += ", " + param.format_conv.format("{0}{1}".format(params_prefix, param.name))
1013 trace += ", {0}{1}".format(params_prefix, param.name)
1019 class VkFunctionPointer(object):
1020 def __init__(self, _type, name, members, forward_decls):
1022 self.members = members
1024 self.required = False
1025 self.forward_decls = forward_decls
1028 def from_xml(funcpointer):
1029 if not api_is_vulkan(funcpointer):
1035 for t in funcpointer.findall("type"):
1037 # <type>void</type>* pUserData,
1038 # Parsing of the tail (anything past </type>) is tricky since there
1039 # can be other data on the next line like: const <type>int</type>..
1041 const = True if begin and "const" in begin else False
1043 lines = t.tail.split(",\n")
1044 if lines[0][0] == "*":
1046 name = lines[0][1:].strip()
1049 name = lines[0].strip()
1051 # Filter out ); if it is contained.
1052 name = name.partition(");")[0]
1054 # If tail encompasses multiple lines, assign the second line to begin
1055 # for the next line.
1057 begin = lines[1].strip()
1061 members.append(VkMember(const=const, _type=_type, pointer=pointer, name=name))
1063 _type = funcpointer.text
1064 name = funcpointer.find("name").text
1065 if "requires" in funcpointer.attrib:
1066 forward_decls = funcpointer.attrib.get("requires").split(",")
1069 return VkFunctionPointer(_type, name, members, forward_decls)
1071 def definition(self):
1073 # forward declare required structs
1074 for decl in self.forward_decls:
1075 text += "typedef struct {0} {0};\n".format(decl)
1077 text += "{0} {1})(\n".format(self.type, self.name)
1080 if len(self.members) > 0:
1081 for m in self.members:
1083 text += " " + m.definition()
1086 text += ",\n " + m.definition()
1088 # Just make the compiler happy by adding a void parameter.
1096 class VkHandle(object):
1097 def __init__(self, name, _type, parent, alias=None):
1100 self.parent = parent
1102 self.required = False
1103 self.object_type = None
1106 def from_alias(handle, alias):
1107 name = handle.attrib.get("name")
1108 return VkHandle(name, alias.type, alias.parent, alias=alias)
1111 def from_xml(handle):
1112 if not api_is_vulkan(handle):
1115 name = handle.find("name").text
1116 _type = handle.find("type").text
1117 parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice.
1118 return VkHandle(name, _type, parent)
1120 def dispatch_table(self, param):
1121 if not self.is_dispatchable():
1124 if self.parent is None:
1125 # Should only happen for VkInstance
1126 return "wine_instance_from_handle({0})->funcs".format(param)
1127 elif self.name == "VkCommandBuffer":
1128 return "wine_cmd_buffer_from_handle({0})->device->funcs".format(param)
1129 elif self.name == "VkDevice":
1130 return "wine_device_from_handle({0})->funcs".format(param)
1131 elif self.name == "VkPhysicalDevice":
1132 return "wine_phys_dev_from_handle({0})->instance->funcs".format(param)
1133 elif self.name == "VkQueue":
1134 return "wine_queue_from_handle({0})->device->funcs".format(param)
1135 elif self.parent in ["VkInstance", "VkPhysicalDevice"]:
1136 return "{0}->instance->funcs".format(param)
1137 elif self.parent in ["VkDevice", "VkCommandPool"]:
1138 return "{0}->device->funcs".format(param)
1140 LOGGER.error("Unhandled dispatchable parent: {0}".format(self.parent))
1142 def definition(self):
1143 """ Generates handle definition e.g. VK_DEFINE_HANDLE(vkInstance) """
1145 # Legacy types are typedef'ed to the new type if they are aliases.
1147 return "typedef {0} {1};\n".format(self.alias.name, self.name)
1149 return "{0}({1})\n".format(self.type, self.name)
1152 return self.alias is not None
1154 def is_dispatchable(self):
1155 """ Some handles like VkInstance, VkDevice are dispatchable objects,
1156 which means they contain a dispatch table of function pointers.
1158 return self.type == "VK_DEFINE_HANDLE"
1160 def is_required(self):
1161 return self.required
1163 def host_handle(self, name):
1164 """ Provide access to the host handle of a wrapped object. """
1166 if self.name == "VkCommandBuffer":
1167 return "wine_cmd_buffer_from_handle({0})->host_command_buffer".format(name)
1168 if self.name == "VkCommandPool":
1169 return "wine_cmd_pool_from_handle({0})->host_command_pool".format(name)
1170 if self.name == "VkDebugUtilsMessengerEXT":
1171 return "wine_debug_utils_messenger_from_handle({0})->host_debug_messenger".format(name)
1172 if self.name == "VkDebugReportCallbackEXT":
1173 return "wine_debug_report_callback_from_handle({0})->host_debug_callback".format(name)
1174 if self.name == "VkDeferredOperationKHR":
1175 return "wine_deferred_operation_from_handle({0})->host_deferred_operation".format(name)
1176 if self.name == "VkDevice":
1177 return "wine_device_from_handle({0})->host_device".format(name)
1178 if self.name == "VkInstance":
1179 return "wine_instance_from_handle({0})->host_instance".format(name)
1180 if self.name == "VkDeviceMemory":
1181 return "wine_device_memory_from_handle({0})->host_memory".format(name)
1182 if self.name == "VkPhysicalDevice":
1183 return "wine_phys_dev_from_handle({0})->host_physical_device".format(name)
1184 if self.name == "VkQueue":
1185 return "wine_queue_from_handle({0})->host_queue".format(name)
1186 if self.name == "VkSurfaceKHR":
1187 return "wine_surface_from_handle({0})->host_surface".format(name)
1188 if self.name == "VkSwapchainKHR":
1189 return "wine_swapchain_from_handle({0})->host_swapchain".format(name)
1190 if self.is_dispatchable():
1191 LOGGER.error("Unhandled host handle for: {0}".format(self.name))
1194 def driver_handle(self, name):
1195 """ Provide access to the handle that should be passed to the wine driver """
1197 if self.name == "VkSurfaceKHR":
1198 return "wine_surface_from_handle({0})->driver_surface".format(name)
1200 return self.host_handle(name)
1202 def unwrap_handle(self, name, unwrap):
1203 if unwrap == Unwrap.DRIVER:
1204 return self.driver_handle(name)
1205 if unwrap == Unwrap.HOST:
1206 return self.host_handle(name)
1207 assert unwrap != Unwrap.NONE
1210 def is_wrapped(self):
1211 return self.host_handle("test") is not None
1214 class VkVariable(object):
1215 def __init__(self, const=False, type_info=None, type=None, name=None, pointer=None, array_len=None,
1216 dyn_array_len=None, object_type=None, optional=False, returnedonly=False, parent=None,
1217 selection=None, selector=None):
1219 self.type_info = type_info
1222 self.parent = parent
1223 self.object_type = object_type
1224 self.optional = optional
1225 self.returnedonly = returnedonly
1226 self.selection = selection
1227 self.selector = selector
1229 self.pointer = pointer
1230 self.array_len = array_len
1231 self.dyn_array_len = dyn_array_len
1232 self.pointer_array = False
1233 if isinstance(dyn_array_len, str):
1234 i = dyn_array_len.find(",")
1236 self.dyn_array_len = dyn_array_len[0:i]
1237 self.pointer_array = True
1240 self.set_type_info(type_info)
1242 def __eq__(self, other):
1243 """ Compare member based on name against a string. """
1244 return self.name == other
1246 def set_type_info(self, type_info):
1247 """ Helper function to set type information from the type registry.
1248 This is needed, because not all type data is available at time of
1251 self.type_info = type_info
1252 self.handle = type_info["data"] if type_info["category"] == "handle" else None
1253 self.struct = type_info["data"] if type_info["category"] == "struct" or type_info["category"] == "union" else None
1255 def get_dyn_array_len(self, prefix, conv):
1256 if isinstance(self.dyn_array_len, int):
1257 return self.dyn_array_len
1259 len_str = self.dyn_array_len
1260 parent = self.parent
1263 # check if length is a member of another struct (for example pAllocateInfo->commandBufferCount)
1264 i = len_str.find("->")
1266 var = parent[parent.index(len_str[0:i])]
1267 len_str = len_str[i+2:]
1268 len = "({0})->".format(var.value(len, conv))
1271 if len_str in parent:
1272 var = parent[parent.index(len_str)]
1273 len = var.value(len, conv);
1274 if var.is_pointer():
1279 if isinstance(self.parent, VkStruct) and self.parent.name in MEMBER_LENGTH_EXPRESSIONS:
1280 exprs = MEMBER_LENGTH_EXPRESSIONS[self.parent.name]
1281 if self.name in exprs:
1282 len = exprs[self.name].format(struct=prefix, len=len)
1289 def is_pointer(self):
1290 return self.pointer is not None
1292 def is_pointer_pointer(self):
1293 return self.pointer and self.pointer.count('*') > 1
1295 def is_pointer_size(self):
1296 if self.type in ["size_t", "HWND", "HINSTANCE"]:
1298 if self.is_handle() and self.handle.is_dispatchable():
1300 return self.is_pointer_pointer()
1302 def is_handle(self):
1303 return self.handle is not None
1305 def is_struct(self):
1306 return self.type_info["category"] == "struct"
1309 return self.type_info["category"] == "union"
1311 def is_bitmask(self):
1312 return self.type_info["category"] == "bitmask"
1315 return self.type_info["category"] == "enum"
1317 def is_dynamic_array(self):
1318 """ Returns if the member is an array element.
1319 Vulkan uses this for dynamically sized arrays for which
1320 there is a 'count' parameter.
1322 return self.dyn_array_len is not None and self.array_len is None
1324 def is_static_array(self):
1325 """ Returns if the member is an array.
1326 Vulkan uses this often for fixed size arrays in which the
1327 length is part of the member.
1329 return self.array_len is not None
1331 def is_generic_handle(self):
1332 """ Returns True if the member is a unit64_t containing
1333 a handle with a separate object type
1335 return self.object_type != None and self.type == "uint64_t"
1337 def needs_alignment(self):
1338 """ Check if this member needs alignment for 64-bit data.
1339 Various structures need alignment on 64-bit variables due
1340 to compiler differences on 32-bit between Win32 and Linux.
1343 if self.is_pointer():
1345 elif self.type == "size_t":
1347 elif self.type in ["uint64_t", "VkDeviceAddress", "VkDeviceSize"]:
1349 elif self.is_bitmask():
1350 return self.type_info["data"].type == "VkFlags64"
1351 elif self.is_enum():
1352 return self.type_info["data"].bitwidth == 64
1353 elif self.is_struct() or self.is_union():
1354 return self.type_info["data"].needs_alignment()
1355 elif self.is_handle():
1356 # Dispatchable handles are pointers to objects, while
1357 # non-dispatchable are uint64_t and hence need alignment.
1358 return not self.handle.is_dispatchable()
1361 def is_wrapped(self):
1362 """ Returns if variable needs unwrapping of handle. """
1364 if self.is_struct():
1365 return self.struct.is_wrapped()
1367 if self.is_handle():
1368 return self.handle.is_wrapped()
1370 if self.is_generic_handle():
1375 def needs_alloc(self, conv, unwrap):
1376 """ Returns True if conversion needs allocation """
1377 if self.is_dynamic_array():
1378 return self.needs_conversion(conv, unwrap, Direction.INPUT, False) \
1379 or self.needs_conversion(conv, unwrap, Direction.OUTPUT, False)
1381 return (self.is_struct() or (self.is_union() and self.selector)) and self.struct.needs_alloc(conv, unwrap)
1383 def needs_win32_type(self):
1384 return (self.is_struct() or (self.is_union() and self.selector)) and self.struct.needs_win32_type()
1386 def get_conversions(self, unwrap, parent_const=False):
1387 """ Get a list of conversions required for this parameter if any.
1388 Parameters which are structures may require conversion between win32
1389 and the host platform. This function returns a list of conversions
1395 # Collect any member conversions first, so we can guarantee
1396 # those functions will be defined prior to usage by the
1397 # 'parent' param requiring conversion.
1398 if self.is_struct() or (self.is_union() and self.selector):
1399 struct = self.struct
1400 is_const = self.is_const() if self.is_pointer() else parent_const
1402 conversions.extend(struct.get_conversions(unwrap, is_const))
1404 for conv in [False, True]:
1405 if struct.needs_conversion(conv, unwrap, Direction.INPUT, is_const):
1406 conversions.append(StructConversionFunction(struct, Direction.INPUT, conv, unwrap, is_const))
1407 if struct.needs_conversion(conv, unwrap, Direction.OUTPUT, is_const):
1408 conversions.append(StructConversionFunction(struct, Direction.OUTPUT, conv, unwrap, is_const))
1410 if self.is_static_array() or self.is_dynamic_array():
1411 for conv in [False, True]:
1412 if self.needs_conversion(conv, unwrap, Direction.INPUT, parent_const):
1413 conversions.append(ArrayConversionFunction(self, Direction.INPUT, conv, unwrap))
1414 if self.needs_conversion(conv, unwrap, Direction.OUTPUT, parent_const):
1415 conversions.append(ArrayConversionFunction(self, Direction.OUTPUT, conv, unwrap))
1419 def needs_ptr32_type(self):
1420 """ Check if variable needs to use PTR32 type. """
1422 return self.is_pointer() or self.is_pointer_size() or self.is_static_array()
1424 def value(self, prefix, conv):
1425 """ Returns code accessing member value, casting 32-bit pointers when needed. """
1427 if not conv or not self.needs_ptr32_type() or (not self.is_pointer() and self.type == "size_t"):
1428 return prefix + self.name
1432 cast_type += "const "
1434 if self.pointer_array or ((self.is_pointer() or self.is_static_array()) and self.is_pointer_size()):
1435 cast_type += "PTR32 *"
1437 cast_type += self.type
1438 if self.needs_win32_type():
1441 if self.is_pointer():
1442 cast_type += " {0}".format(self.pointer)
1443 elif self.is_static_array():
1446 return "({0})UlongToPtr({1}{2})".format(cast_type, prefix, self.name)
1449 class VkMember(VkVariable):
1450 def __init__(self, const=False, struct_fwd_decl=False,_type=None, pointer=None, name=None, array_len=None,
1451 dyn_array_len=None, optional=False, values=None, object_type=None, bit_width=None,
1452 returnedonly=False, parent=None, selection=None, selector=None):
1453 VkVariable.__init__(self, const=const, type=_type, name=name, pointer=pointer, array_len=array_len,
1454 dyn_array_len=dyn_array_len, object_type=object_type, optional=optional,
1455 returnedonly=returnedonly, parent=parent, selection=selection, selector=selector)
1456 self.struct_fwd_decl = struct_fwd_decl
1457 self.values = values
1458 self.bit_width = bit_width
1461 return "{0} {1} {2} {3} {4} {5} {6}".format(self.const, self.struct_fwd_decl, self.type, self.pointer,
1462 self.name, self.array_len, self.dyn_array_len)
1465 def from_xml(member, returnedonly, parent):
1466 """ Helper function for parsing a member tag within a struct or union. """
1468 if not api_is_vulkan(member):
1471 name_elem = member.find("name")
1472 type_elem = member.find("type")
1475 struct_fwd_decl = False
1481 values = member.get("values")
1484 if "const" in member.text:
1487 # Some members contain forward declarations:
1488 # - VkBaseInstructure has a member "const struct VkBaseInStructure *pNext"
1489 # - VkWaylandSurfaceCreateInfoKHR has a member "struct wl_display *display"
1490 if "struct" in member.text:
1491 struct_fwd_decl = True
1493 if type_elem is not None:
1494 member_type = type_elem.text
1495 if type_elem.tail is not None:
1496 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1498 # Name of other member within, which stores the number of
1499 # elements pointed to be by this member.
1500 dyn_array_len = member.get("len")
1502 # Some members are optional, which is important for conversion code e.g. not dereference NULL pointer.
1503 optional = True if member.get("optional") else False
1505 # Usually we need to allocate memory for dynamic arrays. We need to do the same in a few other cases
1506 # like for VkCommandBufferBeginInfo.pInheritanceInfo. Just threat such cases as dynamic arrays of
1507 # size 1 to simplify code generation.
1508 if dyn_array_len is None and pointer is not None:
1511 # Some members are arrays, attempt to parse these. Formats include:
1512 # <member><type>char</type><name>extensionName</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
1513 # <member><type>uint32_t</type><name>foo</name>[4]</member>
1514 if name_elem.tail and name_elem.tail[0] == '[':
1515 LOGGER.debug("Found array type")
1516 enum_elem = member.find("enum")
1517 if enum_elem is not None:
1518 array_len = enum_elem.text
1520 # Remove brackets around length
1521 array_len = name_elem.tail.strip("[]")
1523 object_type = member.get("objecttype", None)
1525 # Some members are bit field values:
1526 # <member><type>uint32_t</type> <name>mask</name>:8</member>
1527 if name_elem.tail and name_elem.tail[0] == ':':
1528 LOGGER.debug("Found bit field")
1529 bit_width = int(name_elem.tail[1:])
1531 selection = member.get("selection").split(',') if member.get("selection") else None
1532 selector = member.get("selector", None)
1534 return VkMember(const=const, struct_fwd_decl=struct_fwd_decl, _type=member_type, pointer=pointer,
1535 name=name_elem.text, array_len=array_len, dyn_array_len=dyn_array_len, optional=optional,
1536 values=values, object_type=object_type, bit_width=bit_width, returnedonly=returnedonly,
1537 parent=parent, selection=selection, selector=selector)
1539 def copy(self, input, output, direction, conv, unwrap):
1540 """ Helper method for use by conversion logic to generate a C-code statement to copy this member.
1541 - `conv` indicates whether the statement is in a struct alignment conversion path. """
1543 win_type = "win32" if conv else "win64"
1544 suffix = convert_suffix(direction, win_type, unwrap, self.is_wrapped())
1546 if self.needs_conversion(conv, unwrap, direction, False):
1547 if self.is_dynamic_array():
1548 # Array length is either a variable name (string) or an int.
1549 count = self.get_dyn_array_len(input, conv)
1550 pointer_part = "pointer_" if self.pointer_array else ""
1551 if direction == Direction.OUTPUT:
1552 return "convert_{2}_{6}array_{5}({3}{1}, {0}, {4});\n".format(self.value(output, conv),
1553 self.name, self.type, input, count, suffix, pointer_part)
1555 return "{0}{1} = convert_{2}_{6}array_{5}(ctx, {3}, {4});\n".format(output,
1556 self.name, self.type, self.value(input, conv), count, suffix, pointer_part)
1557 elif self.is_static_array():
1558 count = self.array_len
1559 if direction == Direction.OUTPUT:
1560 # Needed by VkMemoryHeap.memoryHeaps
1561 return "convert_{0}_array_{5}({2}{1}, {3}{1}, {4});\n".format(self.type,
1562 self.name, input, output, count, suffix)
1564 # Nothing needed this yet.
1565 LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
1566 elif self.is_handle() and self.is_wrapped():
1567 handle = self.type_info["data"]
1568 if direction == Direction.OUTPUT:
1569 LOGGER.err("OUTPUT parameter {0}.{1} cannot be unwrapped".format(self.type, self.name))
1571 return "{0}{1} = {2} ? {3} : 0;\n".format(output, self.name, self.value(input, conv),
1572 handle.unwrap_handle(self.value(input, conv), unwrap))
1574 return "{0}{1} = {2};\n".format(output, self.name,
1575 handle.unwrap_handle(self.value(input, conv), unwrap))
1576 elif self.is_generic_handle():
1577 if direction == Direction.OUTPUT:
1578 LOGGER.err("OUTPUT parameter {0}.{1} cannot be unwrapped".format(self.type, self.name))
1579 if unwrap == Unwrap.DRIVER and self.is_wrapped(Unwrap.DRIVER):
1580 LOGGER.err("DRIVER unwrapping of {0}.{1} not implemented".format(self.type, self.name))
1581 return "{0}{1} = wine_vk_unwrap_handle({2}{3}, {2}{1});\n".format(output, self.name, input, self.object_type)
1583 selector_part = ", {0}{1}".format(input, self.selector) if self.selector else ""
1584 if direction == Direction.OUTPUT:
1585 return "convert_{0}_{4}(&{2}{1}, &{3}{1}{5});\n".format(self.type,
1586 self.name, input, output, suffix, selector_part)
1588 ctx_param = "ctx, " if self.needs_alloc(conv, unwrap) else ""
1589 return "convert_{0}_{4}({5}&{2}{1}, &{3}{1}{6});\n".format(self.type,
1590 self.name, input, output, suffix, ctx_param, selector_part)
1591 elif self.is_static_array():
1592 bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type)
1593 return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count)
1594 elif conv and direction == Direction.OUTPUT and self.is_pointer():
1595 return "{0}{1} = PtrToUlong({2}{1});\n".format(output, self.name, input)
1596 elif conv and direction == Direction.INPUT and self.is_pointer():
1597 return "{0}{1} = UlongToPtr({2}{1});\n".format(output, self.name, input)
1598 elif direction == Direction.INPUT:
1599 return "{0}{1} = {2};\n".format(output, self.name, self.value(input, conv))
1601 return "{0}{1} = {2}{1};\n".format(output, self.name, input)
1603 def definition(self, align=False, conv=False):
1604 """ Generate prototype for given function.
1607 align (bool, optional): Enable alignment if a type needs it. This adds WINE_VK_ALIGN(8) to a member.
1608 conv (bool, optional): Enable conversion if a type needs it. This appends '_host' to the name.
1611 if conv and (self.is_pointer() or self.is_pointer_size()):
1612 text = "PTR32 " + self.name
1613 if self.is_static_array():
1614 text += "[{0}]".format(self.array_len)
1621 if self.is_struct_forward_declaration():
1625 if conv and self.needs_win32_type():
1628 if self.is_pointer():
1629 text += " {0}{1}".format(self.pointer, self.name)
1631 if align and self.needs_alignment():
1633 text += " DECLSPEC_ALIGN(8) " + self.name
1635 text += " WINE_VK_ALIGN(8) " + self.name
1637 text += " " + self.name
1639 if self.is_static_array():
1640 text += "[{0}]".format(self.array_len)
1642 if self.is_bit_field():
1643 text += ":{}".format(self.bit_width)
1647 def is_struct_forward_declaration(self):
1648 return self.struct_fwd_decl
1650 def is_bit_field(self):
1651 return self.bit_width is not None
1653 def needs_conversion(self, conv, unwrap, direction, struct_const):
1654 """ Check if member needs conversion. """
1656 # we can't convert unions if we don't have a selector
1657 if self.is_union() and not self.selector:
1660 is_const = self.is_const() if self.is_pointer() else struct_const
1662 # const members don't needs output conversion unless they are structs with non-const pointers
1663 if direction == Direction.OUTPUT and is_const and not self.is_struct():
1666 if direction == Direction.INPUT:
1667 # returnedonly members don't needs input conversions
1668 if not self.is_pointer() and self.returnedonly:
1670 # pointer arrays always need input conversion
1671 if conv and self.is_dynamic_array() and self.pointer_array:
1674 if self.is_handle():
1675 if unwrap != Unwrap.NONE and self.handle.is_wrapped():
1677 if conv and self.handle.is_dispatchable():
1679 elif self.is_generic_handle():
1680 if unwrap != Unwrap.NONE:
1682 elif self.is_struct() or self.is_union():
1683 if self.struct.needs_conversion(conv, unwrap, direction, is_const):
1686 # if pointer member needs output conversion, it also needs input conversion
1687 # to allocate the pointer
1688 if direction == Direction.INPUT and self.is_pointer() and \
1689 self.needs_conversion(conv, unwrap, Direction.OUTPUT, struct_const):
1694 class VkParam(VkVariable):
1695 """ Helper class which describes a parameter to a function call. """
1697 def __init__(self, type_info, const=None, pointer=None, name=None, parent=None, array_len=None,
1698 dyn_array_len=None, object_type=None, optional=False):
1699 VkVariable.__init__(self, const=const, type_info=type_info, type=type_info["name"], name=name,
1700 pointer=pointer, array_len=array_len, dyn_array_len=dyn_array_len,
1701 object_type=object_type, optional=optional, parent=parent)
1703 self._set_format_string()
1706 return "{0} {1} {2} {3} {4} {5}".format(self.const, self.type, self.pointer, self.name, self.array_len, self.dyn_array_len)
1709 def from_xml(param, types, parent):
1710 """ Helper function to create VkParam from xml. """
1712 if not api_is_vulkan(param):
1715 # Parameter parsing is slightly tricky. All the data is contained within
1716 # a param tag, but some data is within subtags while others are text
1717 # before or after the type tag.
1719 # <param>const <type>char</type>* <name>pLayerName</name></param>
1721 name_elem = param.find("name")
1723 name = name_elem.text
1724 # Tail contains array length e.g. for blendConstants param of vkSetBlendConstants
1725 if name_elem.tail is not None:
1726 array_len = name_elem.tail.strip("[]")
1728 # Name of other parameter in function prototype, which stores the number of
1729 # elements pointed to be by this parameter.
1730 dyn_array_len = param.get("len", None)
1732 const = param.text.strip() if param.text else None
1733 type_elem = param.find("type")
1734 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1735 assert not pointer or pointer.count('*') <= 2 # only pointers and pointer of pointers are supported
1737 attr = param.get("optional")
1738 optional = attr and attr.startswith("true")
1740 # Some uint64_t are actually handles with a separate type param
1741 object_type = param.get("objecttype", None)
1743 # Since we have parsed all types before hand, this should not happen.
1744 type_info = types.get(type_elem.text, None)
1745 if type_info is None:
1746 LOGGER.err("type info not found for: {0}".format(type_elem.text))
1748 return VkParam(type_info, const=const, pointer=pointer, name=name, array_len=array_len,
1749 dyn_array_len=dyn_array_len, object_type=object_type, optional=optional,
1752 def _set_format_string(self):
1753 """ Internal helper function to be used by constructor to set format string. """
1755 # Determine a format string used by code generation for traces.
1756 # 64-bit types need a conversion function.
1757 self.format_conv = None
1758 if self.is_static_array() or self.is_pointer():
1759 self.format_str = "%p"
1761 if self.type_info["category"] in ["bitmask"]:
1762 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1763 if self.type_info["data"].type == "VkFlags64":
1764 self.format_str = "0x%s"
1765 self.format_conv = "wine_dbgstr_longlong({0})"
1767 self.format_str = "%#x"
1768 elif self.type_info["category"] in ["enum"]:
1769 self.format_str = "%#x"
1770 elif self.is_handle():
1771 # We use uint64_t for non-dispatchable handles as opposed to pointers
1772 # for dispatchable handles.
1773 if self.handle.is_dispatchable():
1774 self.format_str = "%p"
1776 self.format_str = "0x%s"
1777 self.format_conv = "wine_dbgstr_longlong({0})"
1778 elif self.type == "float":
1779 self.format_str = "%f"
1780 elif self.type == "int":
1781 self.format_str = "%d"
1782 elif self.type == "int32_t":
1783 self.format_str = "%d"
1784 elif self.type == "size_t":
1785 self.format_str = "0x%s"
1786 self.format_conv = "wine_dbgstr_longlong({0})"
1787 elif self.type in ["uint16_t", "uint32_t", "VkBool32"]:
1788 self.format_str = "%u"
1789 elif self.type in ["uint64_t", "VkDeviceAddress", "VkDeviceSize"]:
1790 self.format_str = "0x%s"
1791 self.format_conv = "wine_dbgstr_longlong({0})"
1792 elif self.type == "HANDLE":
1793 self.format_str = "%p"
1794 elif self.type in ["VisualID", "xcb_visualid_t", "RROutput", "zx_handle_t", "NvSciBufObj", "NvSciBufAttrList", "NvSciSyncAttrList"]:
1795 # Don't care about specific types for non-Windows platforms.
1796 self.format_str = ""
1798 LOGGER.warn("Unhandled type: {0}".format(self.type_info))
1800 def copy(self, direction, conv, unwrap, prefix=""):
1801 win_type = "win32" if conv else "win64"
1802 suffix = convert_suffix(direction, win_type, unwrap, self.is_wrapped())
1804 if direction == Direction.INPUT:
1805 ctx_param = "ctx, " if self.needs_alloc(conv, unwrap) else ""
1806 if self.is_dynamic_array():
1807 return " {0}_host = convert_{2}_array_{4}({5}{1}, {3});\n".format(self.name, self.value(prefix, conv),
1808 self.type, self.get_dyn_array_len(prefix, conv), suffix, ctx_param)
1810 ret = " if ({0}{1})\n".format(prefix, self.name)
1812 ret += " {0}_host = conversion_context_alloc(ctx, sizeof(*{0}_host));\n".format(self.name)
1813 ret += " convert_{0}_{3}({4}{1}, {2}_host);\n".format(self.type, self.value(prefix, conv),
1814 self.name, suffix, ctx_param)
1817 elif self.is_struct():
1818 return " convert_{0}_{3}({4}{1}, &{2}_host);\n".format(self.type, self.value(prefix, conv),
1819 self.name, suffix, ctx_param)
1820 elif self.is_pointer_size() and self.type != "size_t":
1821 return " {0}_host = UlongToPtr(*{1});\n".format(self.name, self.value(prefix, conv))
1823 return " {0}_host = *{1};\n".format(self.name, self.value(prefix, conv))
1825 if self.is_dynamic_array():
1826 return " convert_{0}_array_{1}({2}_host, {3}, {4});\n".format(
1827 self.type, suffix, self.name, self.value(prefix, conv).replace('const ', ''),
1828 self.get_dyn_array_len(prefix, conv))
1829 elif self.is_struct():
1830 ref_part = "" if self.optional else "&"
1831 return " convert_{0}_host_to_{3}({4}{2}_host, {1});\n".format(
1832 self.type, self.value(prefix, conv), self.name, win_type, ref_part)
1833 elif self.is_pointer_size() and self.type != "size_t":
1834 return " *{0} = PtrToUlong({1}_host);\n".format(self.value(prefix, conv), self.name)
1836 return " *{0} = {1}_host;\n".format(self.value(prefix, conv), self.name)
1838 def definition(self, postfix=None, is_member=False, conv=False):
1839 """ Return prototype for the parameter. E.g. 'const char *foo' """
1841 if is_member and conv and self.needs_ptr32_type():
1842 return "PTR32 {0}".format(self.name)
1846 proto += self.const + " "
1850 if conv and self.needs_win32_type():
1853 if is_member and self.needs_alignment():
1854 proto += " DECLSPEC_ALIGN(8)"
1856 if self.is_pointer():
1857 proto += " {0}{1}".format(self.pointer, name)
1858 elif is_member and self.is_static_array():
1859 proto += " *" + name
1863 # Allows appending something to the variable name useful for
1864 # win32 to host conversion.
1865 if postfix is not None:
1868 if not is_member and self.is_static_array():
1869 proto += "[{0}]".format(self.array_len)
1873 def dispatch_table(self, params_prefix, conv):
1874 """ Return functions dispatch table pointer for dispatchable objects. """
1876 if not self.is_dispatchable():
1879 return self.handle.dispatch_table(self.value(params_prefix, conv))
1881 def format_string(self, conv):
1882 if conv and self.needs_ptr32_type() and (self.type != "size_t" or self.is_pointer()):
1884 return self.format_str
1886 def is_dispatchable(self):
1887 if not self.is_handle():
1890 return self.handle.is_dispatchable()
1892 def needs_conversion(self, conv, unwrap, direction, parent_const=False):
1893 """ Check if param needs conversion. """
1895 if self.is_pointer_pointer() and self.type != 'void':
1896 if direction == Direction.INPUT or not self.is_const():
1899 if self.is_struct():
1900 return self.struct.needs_conversion(conv, unwrap, direction, self.is_const())
1902 if self.is_handle():
1903 # non-pointer handles are handled inline in thunks
1904 if not self.is_dynamic_array() and not self.is_static_array():
1905 return conv and self.is_pointer() and self.handle.is_dispatchable()
1907 # vkAllocateCommandBuffers is a special case, we use it in our private thunk as an input param
1908 param_direction = (Direction.INPUT if self.is_const() else Direction.OUTPUT)
1909 if self.name == "pCommandBuffers":
1910 param_direction = Direction.INPUT
1911 if direction != param_direction:
1914 if unwrap != Unwrap.NONE and self.handle.is_wrapped():
1916 if conv and self.handle.is_dispatchable():
1918 elif self.is_pointer() and self.is_pointer_size():
1923 def needs_variable(self, conv, unwrap):
1924 if self.needs_conversion(conv, unwrap, Direction.INPUT):
1926 if self.needs_conversion(conv, unwrap, Direction.OUTPUT):
1931 """ Generate spec file entry for this parameter. """
1933 if self.is_pointer() and self.type == "char":
1935 if self.is_dispatchable() or self.is_pointer() or self.is_static_array():
1937 if self.type_info["category"] in ["bitmask"]:
1938 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1939 if self.type_info["data"].type == "VkFlags64":
1943 if self.type_info["category"] in ["enum"]:
1945 if self.is_handle() and not self.is_dispatchable():
1947 if self.type == "float":
1949 if self.type in ["int", "int32_t", "size_t", "uint16_t", "uint32_t", "VkBool32"]:
1951 if self.type in ["uint64_t", "VkDeviceSize"]:
1954 LOGGER.error("Unhandled spec conversion for type: {0}".format(self.type))
1956 def variable(self, conv, unwrap, params_prefix=""):
1957 """ Returns 'glue' code during generation of a function call on how to access the variable.
1958 This function handles various scenarios such as 'unwrapping' if dispatchable objects and
1959 renaming of parameters in case of win32 -> host conversion.
1962 conv (bool, optional): Enable conversion if the param needs it. This appends '_host' to the name.
1965 # Hack until we enable allocation callbacks from ICD to application. These are a joy
1966 # to enable one day, because of calling convention conversion.
1967 if unwrap != Unwrap.NONE and "VkAllocationCallbacks" in self.type:
1968 LOGGER.debug("TODO: setting NULL VkAllocationCallbacks for {0}".format(self.name))
1971 if self.needs_variable(conv, unwrap):
1972 if self.is_dynamic_array() or self.optional:
1973 return "{0}_host".format(self.name)
1975 return "&{0}_host".format(self.name)
1977 p = self.value(params_prefix, conv)
1979 if unwrap != Unwrap.NONE:
1980 unwrap_handle = None
1981 if self.object_type != None and self.type == "uint64_t":
1982 if unwrap == Unwrap.DRIVER and self.is_wrapped(Unwrap.DRIVER):
1983 LOGGER.err("DRIVER unwrapping of {0}.{1} not implemented".format(self.type, self.name))
1984 unwrap_handle = "wine_vk_unwrap_handle({0}{1}, {0}{2})".format(
1985 params_prefix, self.object_type, self.name)
1987 elif self.is_handle():
1988 # We need to pass the host handle to the host Vulkan calls and
1989 # the wine driver's handle to calls which are wrapped by the driver.
1990 unwrap_handle = self.handle.unwrap_handle(p, unwrap)
1993 unwrap_handle = "{0}{1} ? {2} : 0".format(params_prefix, self.name, unwrap_handle)
1994 return unwrap_handle
1999 class VkStruct(Sequence):
2000 """ Class which represents the type union and struct. """
2002 def __init__(self, name, members, returnedonly, structextends, alias=None, union=False):
2004 self.members = members
2005 self.returnedonly = returnedonly
2006 self.structextends = structextends
2007 self.required = False
2010 self.type_info = None # To be set later.
2011 self.struct_extensions = []
2012 self.aliased_by = []
2014 def __getitem__(self, i):
2015 return self.members[i]
2018 return len(self.members)
2021 def from_alias(struct, alias):
2022 name = struct.attrib.get("name")
2023 aliasee = VkStruct(name, alias.members, alias.returnedonly, alias.structextends, alias=alias)
2025 alias.add_aliased_by(aliasee)
2029 def from_xml(struct):
2030 if not api_is_vulkan(struct):
2033 # Unions and structs are the same parsing wise, but we need to
2034 # know which one we are dealing with later on for code generation.
2035 union = True if struct.attrib["category"] == "union" else False
2037 name = struct.attrib.get("name")
2039 # 'Output' structures for which data is filled in by the API are
2040 # marked as 'returnedonly'.
2041 returnedonly = True if struct.attrib.get("returnedonly") else False
2043 # Those structs seem to be broken in spec, they are specified as
2044 # returned only, but documented as input structs.
2045 if name in ["VkPipelineShaderStageRequiredSubgroupSizeCreateInfo"]:
2046 returnedonly = False
2048 # Those structs don't have returnedonly in spec, but they could (should?).
2049 if name in ["VkSurfaceCapabilitiesPresentBarrierNV"]:
2052 structextends = struct.attrib.get("structextends")
2053 structextends = structextends.split(",") if structextends else []
2055 s = VkStruct(name, [], returnedonly, structextends, union=union)
2056 for member in struct.findall("member"):
2057 vk_member = VkMember.from_xml(member, returnedonly, s)
2059 s.members.append(vk_member)
2064 def decouple_structs(structs):
2065 """ Helper function which decouples a list of structs.
2066 Structures often depend on other structures. To make the C compiler
2067 happy we need to define 'substructures' first. This function analyzes
2068 the list of structures and reorders them in such a way that they are
2072 tmp_structs = list(structs) # Don't modify the original structures.
2073 decoupled_structs = []
2075 while (len(tmp_structs) > 0):
2076 # Iterate over a copy because we want to modify the list inside the loop.
2077 for struct in list(tmp_structs):
2080 if not struct.required:
2081 tmp_structs.remove(struct)
2085 if not (m.is_struct() or m.is_union()):
2088 # VkBaseInstructure and VkBaseOutStructure reference themselves.
2089 if m.type == struct.name:
2093 # Check if a struct we depend on has already been defined.
2094 for s in decoupled_structs:
2095 if s.name == m.type:
2100 # Check if the struct we depend on is even in the list of structs.
2101 # If found now, it means we haven't met all dependencies before we
2102 # can operate on the current struct.
2103 # When generating 'host' structs we may not be able to find a struct
2104 # as the list would only contain the structs requiring conversion.
2105 for s in tmp_structs:
2106 if s.name == m.type:
2110 if dependends == False:
2111 decoupled_structs.append(struct)
2112 tmp_structs.remove(struct)
2114 return decoupled_structs
2116 def definition(self, align=False, conv=False):
2117 """ Convert structure to textual definition.
2120 align (bool, optional): enable alignment to 64-bit for win32 struct compatibility.
2121 conv (bool, optional): enable struct conversion if the struct needs it.
2122 postfix (str, optional): text to append to end of struct name, useful for struct renaming.
2128 suffix = "32" if conv else ""
2130 text = "typedef union {0}".format(self.name)
2132 text = "typedef struct {0}".format(self.name)
2138 if align and m.needs_alignment():
2139 text += " {0};\n".format(m.definition(align=align, conv=conv))
2141 text += " {0};\n".format(m.definition(conv=conv))
2143 text += "}} {0}{1};\n".format(self.name, suffix)
2145 for aliasee in self.aliased_by:
2146 text += "typedef {0}{2} {1}{2};\n".format(self.name, aliasee.name, suffix)
2151 return bool(self.alias)
2153 def add_aliased_by(self, aliasee):
2154 self.aliased_by.append(aliasee)
2156 def needs_alignment(self):
2157 """ Check if structure needs alignment for 64-bit data.
2158 Various structures need alignment on 64-bit variables due
2159 to compiler differences on 32-bit between Win32 and Linux.
2162 for m in self.members:
2163 if self.name == m.type:
2165 if m.needs_alignment():
2169 def is_wrapped(self):
2170 """ Returns if struct members need unwrapping of handle. """
2172 for m in self.members:
2173 if self.name == m.type:
2179 def needs_extensions_conversion(self, conv, direction):
2180 """ Check if struct contains extensions chain that needs to be converted """
2182 if direction == Direction.INPUT and self.name in STRUCT_CHAIN_CONVERSIONS:
2185 if not "pNext" in self:
2187 is_const = self.members[self.members.index("pNext")].is_const()
2188 # VkOpticalFlowSessionCreateInfoNV is missing const in its pNext pointer
2189 if self.name in ["VkOpticalFlowSessionCreateInfoNV",
2190 "VkDescriptorBufferBindingInfoEXT"]:
2193 for e in self.struct_extensions:
2196 if e.needs_conversion(conv, Unwrap.HOST, direction, is_const, check_extensions=False):
2198 if direction == Direction.INPUT:
2199 # we need input conversion of structs containing struct chain even if it's returnedonly,
2200 # so that we have a chance to allocate buffers
2201 if e.needs_conversion(conv, Unwrap.HOST, Direction.OUTPUT, is_const, check_extensions=False):
2206 def needs_conversion(self, conv, unwrap, direction, is_const, check_extensions=True):
2207 """ Check if struct needs conversion. """
2209 # VkAllocationCallbacks never needs conversion
2210 if self.name == "VkAllocationCallbacks":
2213 # pFixedRateFlags field is missing const, but it doesn't need output conversion
2214 if direction == Direction.OUTPUT and self.name == "VkImageCompressionControlEXT":
2217 needs_output_copy = False
2219 for m in self.members:
2220 if self.name == m.type:
2223 if m.name == "pNext":
2224 # pNext is a pointer, so it always needs conversion
2225 if conv and direction == Direction.INPUT:
2227 # we need input conversion of structs containing struct chain even if it's returnedonly
2228 if direction == Direction.INPUT and \
2229 self.needs_conversion(conv, unwrap, Direction.OUTPUT, is_const):
2233 # for non-pointer members, check for returnedonly and const attributes
2234 if not m.is_pointer() or m.type == "void":
2235 if direction == Direction.INPUT:
2236 if self.returnedonly:
2239 if is_const or m.is_const():
2242 # check alignment and pointer-sized members for 32-bit conversions
2243 if conv and (direction == Direction.INPUT or not is_const):
2244 if m.is_pointer() or m.is_pointer_size():
2246 # we don't check structs here, they will will be traversed by needs_conversion chain anyway
2247 if not m.is_struct() and m.needs_alignment():
2250 if m.needs_conversion(conv, unwrap, direction, is_const):
2253 # pointers will be handled by needs_conversion, but if we have any other non-const
2254 # member, we may need to copy output
2255 if direction == Direction.OUTPUT and not m.is_pointer() and not is_const and not m.is_const():
2256 needs_output_copy = True
2258 # if output needs any copy and we need input conversion, then we also need output conversion
2259 if needs_output_copy and self.needs_conversion(conv, unwrap, Direction.INPUT, check_extensions):
2262 return check_extensions and self.needs_extensions_conversion(conv, direction)
2264 def needs_alloc(self, conv, unwrap):
2265 """ Check if any struct member needs some memory allocation."""
2267 if self.needs_extensions_conversion(conv, Direction.INPUT):
2270 for m in self.members:
2271 if self.name == m.type:
2273 if m.needs_alloc(conv, unwrap):
2278 def needs_win32_type(self):
2279 # VkAllocationCallbacks never needs conversion
2280 if self.name == "VkAllocationCallbacks":
2283 for m in self.members:
2284 if self.name == m.type:
2286 if m.is_pointer() or m.is_pointer_size():
2288 if m.needs_alignment():
2290 if (m.is_struct() or m.is_union()) and m.struct.needs_win32_type():
2293 def set_type_info(self, types):
2294 """ Helper function to set type information from the type registry.
2295 This is needed, because not all type data is available at time of
2298 for m in self.members:
2299 type_info = types[m.type]
2300 m.set_type_info(type_info)
2302 def get_conversions(self, unwrap, parent_const):
2305 # Collect any conversion for any extension structs.
2306 for e in self.struct_extensions:
2309 conversions.extend(e.get_conversions(Unwrap.HOST, parent_const))
2311 # Collect any conversion for any member structs.
2313 if m.type == self.name:
2315 conversions.extend(m.get_conversions(unwrap, parent_const))
2320 class StructConversionFunction(object):
2321 def __init__(self, struct, direction, conv, unwrap, const):
2322 self.direction = direction
2323 self.operand = struct
2324 self.type = struct.name
2326 self.unwrap = unwrap
2329 name = "convert_{0}_".format(self.type)
2330 win_type = "win32" if self.conv else "win64"
2331 name += convert_suffix(direction, win_type, unwrap, struct.is_wrapped())
2334 def __eq__(self, other):
2335 return self.name == other.name
2337 def member_needs_copy(self, struct, m):
2338 if self.direction == Direction.OUTPUT:
2339 if m.name in ["sType", "pNext"]:
2341 if self.const and not m.is_pointer():
2343 if m.is_const() and not m.needs_conversion(self.conv, self.unwrap, Direction.OUTPUT, self.const):
2346 if m.name == "pNext":
2348 if m.name != "sType" and struct.returnedonly and not m.needs_conversion(
2349 self.conv, self.unwrap, Direction.INPUT, self.const):
2353 def definition(self):
2354 """ Helper function for generating a struct conversion function. """
2356 # It doesn't make sense to generate conversion functions for non-struct variables
2357 # which aren't in arrays, as this should be handled by the copy() function
2358 if not isinstance(self.operand, VkStruct):
2364 body += "#ifdef _WIN64\n"
2366 needs_alloc = self.direction != Direction.OUTPUT and self.operand.needs_alloc(self.conv, self.unwrap)
2367 win_type = self.type
2368 if self.conv and self.operand.needs_win32_type():
2370 if self.direction == Direction.OUTPUT and self.const:
2371 win_type = "const " + win_type
2374 body += "static inline void {0}(".format(self.name)
2376 if self.direction == Direction.OUTPUT:
2377 params = ["const {0} *in".format(self.type), "{0} *out".format(win_type)]
2379 params = ["const {0} *in".format(win_type), "{0} *out".format(self.type)]
2381 if self.operand.union:
2382 params.append("VkFlags selector")
2384 # Generate parameter list
2386 body += "struct conversion_context *ctx, "
2387 body += ", ".join(p for p in params)
2391 body += "static inline void {0}(".format(self.name)
2393 params = ["const {0} *in".format(self.type), "{0} *out".format(self.type)]
2395 # Generate parameter list
2397 body += "struct conversion_context *ctx, "
2398 body += ", ".join(p for p in params)
2401 needs_extensions = self.operand.needs_extensions_conversion(self.conv, self.direction)
2404 if needs_extensions:
2405 if self.direction == Direction.INPUT:
2407 body += " const VkBaseInStructure32 *in_header;\n"
2409 body += " const VkBaseInStructure *in_header;\n"
2410 body += " VkBaseOutStructure *out_header = (void *)out;\n\n"
2412 body += " const VkBaseInStructure *in_header;\n"
2414 body += " VkBaseOutStructure32 *out_header = (void *)out;\n\n"
2416 body += " VkBaseOutStructure *out_header = (void *)out;\n\n"
2418 body += " if (!in) return;\n\n"
2420 for m in self.operand:
2421 if not self.member_needs_copy(self.operand, m):
2423 if m.name == "pNext" and (needs_extensions or self.conv):
2424 body += " out->pNext = NULL;\n"
2429 body += " || ".join("selector == {}".format(s) for s in m.selection)
2432 body += " " + m.copy("in->", "out->", self.direction, self.conv, self.unwrap)
2434 if needs_extensions:
2435 if self.conv and self.direction == Direction.INPUT:
2436 body += "\n for (in_header = UlongToPtr(in->pNext); in_header; in_header = UlongToPtr(in_header->pNext))\n"
2438 body += "\n for (in_header = (void *)in->pNext; in_header; in_header = (void *)in_header->pNext)\n"
2440 body += " switch (in_header->sType)\n"
2445 if self.direction == Direction.INPUT and self.type in STRUCT_CHAIN_CONVERSIONS:
2446 for i in STRUCT_CHAIN_CONVERSIONS[self.type]:
2447 body += " case {0}:\n".format(i)
2448 body += ident + "break;\n"
2450 for ext in self.operand.struct_extensions:
2451 if not ext.required:
2454 if self.direction == Direction.OUTPUT and not any([self.member_needs_copy(ext, m) for m in ext]):
2457 stype = next(x for x in ext.members if x.name == "sType").values
2458 win_type = ext.name + "32" if self.conv and ext.needs_win32_type() else ext.name
2459 if self.direction == Direction.INPUT:
2460 in_type = "const " + win_type
2463 in_type = "const " + ext.name
2466 body += " case {0}:\n".format(stype)
2468 if self.direction == Direction.INPUT:
2469 body += ident + "{0} *out_ext = conversion_context_alloc(ctx, sizeof(*out_ext));\n".format(out_type)
2471 body += ident + "{0} *out_ext = find_next_struct32(out_header, {1});\n".format(out_type, stype)
2473 body += ident + "{0} *out_ext = find_next_struct(out_header, {1});\n".format(out_type, stype)
2478 if m.name == "sType":
2479 copy_body += ident + "out_ext->sType = {0};\n".format(stype)
2481 if not self.member_needs_copy(ext, m):
2483 if m.name == "pNext":
2484 copy_body += ident + "out_ext->pNext = NULL;\n"
2486 copy_body += ident + m.copy("in_ext->", "out_ext->", self.direction, self.conv, Unwrap.HOST)
2488 # Generate the definition of "in_ext" if we need it
2489 if "in_ext->" in copy_body:
2490 body += ident + "{0} *in_ext = ({0} *)in_header;\n".format(in_type)
2493 if self.direction == Direction.INPUT:
2494 body += ident + "out_header->pNext = (void *)out_ext;\n"
2495 body += ident + "out_header = (void *)out_ext;\n"
2496 body += ident + "break;\n"
2499 body += " default:\n"
2500 if self.direction == Direction.INPUT:
2501 body += ident + "FIXME(\"Unhandled sType %u.\\n\", in_header->sType);\n"
2505 elif self.conv and self.direction == Direction.INPUT and "pNext" in self.operand:
2506 body += " if (in->pNext)\n"
2507 body += " FIXME(\"Unexpected pNext\\n\");\n"
2511 body += "#endif /* _WIN64 */\n"
2517 class ArrayConversionFunction(object):
2518 def __init__(self, array, direction, conv, unwrap):
2520 self.direction = direction
2521 self.type = array.type
2523 self.unwrap = unwrap
2525 if array.is_static_array() and direction == Direction.INPUT:
2526 LOGGER.error("Static array input conversion is not supported")
2528 name = "convert_{0}_".format(array.type)
2529 if array.pointer_array:
2532 win_type = "win32" if self.conv else "win64"
2533 name += convert_suffix(direction, win_type, unwrap, array.is_wrapped())
2536 def __eq__(self, other):
2537 return self.name == other.name
2539 def definition(self):
2540 """ Helper function for generating a conversion function for array operands. """
2545 body += "#ifdef _WIN64\n"
2547 needs_alloc = self.direction != Direction.OUTPUT and self.array.needs_alloc(self.conv, self.unwrap)
2548 pointer_array = self.array.pointer_array or self.array.is_pointer_pointer()
2550 win_type = self.type
2552 if self.array.needs_win32_type():
2554 elif self.array.is_handle() and self.array.handle.is_dispatchable():
2556 if self.direction == Direction.OUTPUT and self.array.is_const():
2557 win_type = "const " + win_type
2558 pointer_part = self.array.pointer if self.array.pointer else "*"
2560 if self.direction == Direction.OUTPUT:
2561 if self.conv and pointer_array:
2564 out = "{0} {1}out".format(win_type, pointer_part)
2565 params = ["const {0} {1}in".format(self.type, pointer_part), out, "uint32_t count"]
2567 elif self.conv and pointer_array:
2568 params = ["const PTR32 *in", "uint32_t count"]
2569 return_type = self.type
2571 params = ["const {0} {1}in".format(win_type, pointer_part), "uint32_t count"]
2572 return_type = self.type
2574 needs_copy = not self.array.is_struct() or self.direction != Direction.INPUT or \
2575 not self.array.struct.returnedonly or "pNext" in self.array.struct
2577 # Generate function prototype.
2579 body += "static inline {0}{1} {2}{3}(".format(
2580 "const " if self.array.is_const() else "", return_type, pointer_part, self.name)
2582 body += "static inline void {0}(".format(self.name)
2584 body += "struct conversion_context *ctx, "
2585 body += ", ".join(p for p in params)
2589 pointer = self.array.pointer.replace('const*', '*').replace(' *', '*')
2590 body += " {0} {1}out;\n".format(return_type, pointer)
2592 body += " unsigned int i;\n\n"
2595 body += " if (!in || !count) return NULL;\n\n"
2597 body += " if (!in) return;\n\n"
2599 if self.direction == Direction.INPUT:
2600 body += " out = conversion_context_alloc(ctx, count * sizeof(*out));\n"
2603 body += " for (i = 0; i < count; i++)\n"
2606 if self.array.is_struct():
2607 struct = self.array.struct
2608 win_part = "win32" if self.conv else "win64"
2609 suffix = convert_suffix(self.direction, win_part, self.unwrap, struct.is_wrapped())
2611 if self.direction == Direction.INPUT and struct.needs_alloc(self.conv, self.unwrap):
2614 if not pointer_array:
2615 body += " convert_{0}_{1}({2}&in[i], &out[i]);\n".format(
2616 struct.name, suffix, ctx_part)
2618 if struct.needs_conversion(self.conv, self.unwrap, self.direction, False):
2619 body += " if (in[i])\n"
2621 body += " out[i] = conversion_context_alloc(ctx, sizeof(*out[i]));\n"
2623 in_param = "({0} *)UlongToPtr(in[i])".format(win_type)
2626 body += " convert_{0}_{1}({2}{3}, out[i]);\n".format(
2627 struct.name, suffix, ctx_part, in_param)
2630 body += " out[i] = NULL;\n"
2632 body += " out[i] = UlongToPtr(in[i]);\n"
2633 elif self.array.is_handle():
2635 LOGGER.error("Unhandled handle pointer arrays")
2636 handle = self.array.handle
2637 if not self.conv or not handle.is_dispatchable():
2639 elif self.direction == Direction.INPUT:
2640 input = "UlongToPtr(in[i])"
2642 input = "PtrToUlong(in[i])"
2644 if self.unwrap == Unwrap.NONE or not handle.is_wrapped():
2645 body += " out[i] = {0};\n".format(input)
2646 elif self.direction == Direction.INPUT:
2647 body += " out[i] = {0};\n".format(handle.unwrap_handle(input, self.unwrap))
2649 LOGGER.warning("Unhandled handle output conversion")
2651 if self.direction == Direction.INPUT:
2652 body += " out[i] = UlongToPtr(in[i]);\n"
2654 body += " out[i] = PtrToUlong(in[i]);\n"
2656 body += " out[i] = in[i];\n"
2661 body += "\n return {0}out;\n".format("(void *)" if pointer_array else "")
2665 body += "#endif /* _WIN64 */\n"
2672 class VkGenerator(object):
2673 def __init__(self, registry):
2674 self.registry = registry
2676 # Build a list conversion functions for struct conversion.
2677 self.conversions = []
2678 self.win32_structs = []
2679 for func in self.registry.funcs.values():
2680 if not func.needs_exposing():
2683 conversions = func.get_conversions()
2684 for conv in conversions:
2685 # Append if we don't already have this conversion.
2686 if not any(c == conv for c in self.conversions):
2687 self.conversions.append(conv)
2689 if not isinstance(conv, StructConversionFunction):
2692 for e in conv.operand.struct_extensions:
2693 if not e.required or not e.needs_win32_type():
2695 if not any(s.name == e.name for s in self.win32_structs):
2696 self.win32_structs.append(e)
2698 if not conv.operand.needs_win32_type():
2701 # Structs can be used in different ways by different conversions
2702 # e.g. array vs non-array. Just make sure we pull in each struct once.
2703 if not any(s.name == conv.operand.name for s in self.win32_structs):
2704 self.win32_structs.append(conv.operand)
2706 def _generate_copyright(self, f, spec_file=False):
2707 f.write("# " if spec_file else "/* ")
2708 f.write("Automatically generated from Vulkan vk.xml; DO NOT EDIT!\n")
2709 lines = ["", "This file is generated from Vulkan vk.xml file covered",
2710 "by the following copyright and permission notice:"]
2711 lines.extend([l.rstrip(" ") for l in self.registry.copyright.splitlines()])
2713 f.write("{0}{1}".format("# " if spec_file else " * ", line).rstrip(" ") + "\n")
2714 f.write("\n" if spec_file else " */\n\n")
2716 def generate_thunks_c(self, f):
2717 self._generate_copyright(f)
2720 f.write("#pragma makedep unix\n")
2721 f.write("#endif\n\n")
2723 f.write("#include \"config.h\"\n\n")
2725 f.write("#include <stdlib.h>\n\n")
2727 f.write("#include \"vulkan_private.h\"\n\n")
2729 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2731 for struct in self.win32_structs:
2732 f.write(struct.definition(conv=True, align=True))
2735 f.write("static uint64_t wine_vk_unwrap_handle(uint32_t type, uint64_t handle)\n")
2737 f.write(" switch(type)\n")
2739 for handle in self.registry.handles:
2740 if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2742 f.write(" case {}:\n".format(handle.object_type))
2743 if handle.is_dispatchable():
2744 f.write(" return (uint64_t) (uintptr_t) ")
2745 f.write(handle.host_handle("(({}) (uintptr_t) handle)".format(handle.name)))
2747 f.write(" return (uint64_t) ")
2748 f.write(handle.host_handle("handle"))
2750 f.write(" default:\n")
2751 f.write(" return handle;\n")
2755 # Generate any conversion helper functions.
2756 for conv in self.conversions:
2757 f.write(conv.definition())
2759 # Create thunks for instance and device functions.
2760 # Global functions don't go through the thunks.
2761 for vk_func in self.registry.funcs.values():
2762 if not vk_func.needs_exposing():
2764 if vk_func.name in MANUAL_LOADER_FUNCTIONS:
2767 f.write(vk_func.thunk(prefix="thunk64_"))
2768 f.write(vk_func.thunk(prefix="thunk32_", conv=True))
2770 # Create array of device extensions.
2771 f.write("static const char * const vk_device_extensions[] =\n{\n")
2772 for ext in self.registry.extensions:
2773 if ext["type"] != "device":
2775 if ext["name"] in UNEXPOSED_EXTENSIONS:
2778 f.write(" \"{0}\",\n".format(ext["name"]))
2781 # Create array of instance extensions.
2782 f.write("static const char * const vk_instance_extensions[] =\n{\n")
2783 for ext in self.registry.extensions:
2784 if ext["type"] != "instance":
2786 if ext["name"] in UNEXPOSED_EXTENSIONS:
2789 f.write(" \"{0}\",\n".format(ext["name"]))
2792 # Create array of surface extensions.
2793 f.write("static const char * const vk_host_surface_extensions[] =\n{\n")
2794 for ext in self.registry.surface_extensions:
2795 if ext["name"] not in WIN_SURFACE_EXTENSIONS:
2796 f.write(" \"{0}\",\n".format(ext["name"]))
2799 f.write("BOOL wine_vk_device_extension_supported(const char *name)\n")
2801 f.write(" unsigned int i;\n")
2802 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_extensions); i++)\n")
2804 f.write(" if (strcmp(vk_device_extensions[i], name) == 0)\n")
2805 f.write(" return TRUE;\n")
2807 f.write(" return FALSE;\n")
2810 f.write("BOOL wine_vk_instance_extension_supported(const char *name)\n")
2812 f.write(" unsigned int i;\n")
2813 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_extensions); i++)\n")
2815 f.write(" if (strcmp(vk_instance_extensions[i], name) == 0)\n")
2816 f.write(" return TRUE;\n")
2818 f.write(" return FALSE;\n")
2821 f.write("BOOL wine_vk_is_host_surface_extension(const char *name)\n")
2823 f.write(" unsigned int i;\n")
2824 f.write(" for (i = 0; i < ARRAY_SIZE(vk_host_surface_extensions); i++)\n")
2826 f.write(" if (strcmp(vk_host_surface_extensions[i], name) == 0)\n")
2827 f.write(" return TRUE;\n")
2829 f.write(" return FALSE;\n")
2832 f.write("BOOL wine_vk_is_type_wrapped(VkObjectType type)\n")
2834 f.write(" return FALSE")
2835 for handle in self.registry.handles:
2836 if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2838 f.write(" ||\n type == {}".format(handle.object_type))
2843 f.write("#ifdef _WIN64\n\n")
2845 f.write("const unixlib_entry_t __wine_unix_call_funcs[] =\n")
2847 f.write(" init_vulkan,\n")
2848 f.write(" vk_is_available_instance_function,\n")
2849 f.write(" vk_is_available_device_function,\n")
2850 for vk_func in self.registry.funcs.values():
2851 if not vk_func.needs_exposing():
2853 if vk_func.name in MANUAL_LOADER_FUNCTIONS:
2856 if vk_func.is_perf_critical():
2857 f.write(" (void *){1}{0},\n".format(vk_func.name, "thunk64_"))
2859 f.write(" {1}{0},\n".format(vk_func.name, "thunk64_"))
2861 f.write("C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_count);\n\n")
2863 f.write("#endif /* _WIN64 */\n\n")
2865 f.write("#ifdef _WIN64\n")
2866 f.write("const unixlib_entry_t __wine_unix_call_wow64_funcs[] =\n")
2868 f.write("const unixlib_entry_t __wine_unix_call_funcs[] =\n")
2871 f.write(" init_vulkan,\n")
2872 f.write(" vk_is_available_instance_function32,\n")
2873 f.write(" vk_is_available_device_function32,\n")
2874 for vk_func in self.registry.funcs.values():
2875 if not vk_func.needs_exposing():
2877 if vk_func.name in MANUAL_LOADER_FUNCTIONS:
2880 if vk_func.is_perf_critical():
2881 f.write(" (void *){1}{0},\n".format(vk_func.name, "thunk32_"))
2883 f.write(" {1}{0},\n".format(vk_func.name, "thunk32_"))
2885 f.write("C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_count);\n")
2887 def generate_thunks_h(self, f, prefix):
2888 self._generate_copyright(f)
2890 f.write("#ifndef __WINE_VULKAN_THUNKS_H\n")
2891 f.write("#define __WINE_VULKAN_THUNKS_H\n\n")
2893 f.write("#define WINE_VK_VERSION VK_API_VERSION_{0}_{1}\n\n".format(WINE_VK_VERSION[0], WINE_VK_VERSION[1]))
2895 # Generate prototypes for device and instance functions requiring a custom implementation.
2896 f.write("/* Functions for which we have custom implementations outside of the thunks. */\n")
2897 for vk_func in self.registry.funcs.values():
2898 if not vk_func.needs_private_thunk():
2901 f.write("{0};\n".format(vk_func.prototype(prefix=prefix, is_thunk=True)))
2904 f.write("/* For use by vkDevice and children */\n")
2905 f.write("struct vulkan_device_funcs\n{\n")
2906 for vk_func in self.registry.device_funcs:
2907 if not vk_func.needs_exposing():
2910 if not vk_func.needs_dispatch():
2911 LOGGER.debug("skipping {0} in vulkan_device_funcs".format(vk_func.name))
2914 f.write(" {0};\n".format(vk_func.pfn()))
2917 f.write("/* For use by vkInstance and children */\n")
2918 f.write("struct vulkan_instance_funcs\n{\n")
2919 for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs:
2920 if not vk_func.needs_exposing():
2923 if not vk_func.needs_dispatch():
2924 LOGGER.debug("skipping {0} in vulkan_instance_funcs".format(vk_func.name))
2927 f.write(" {0};\n".format(vk_func.pfn()))
2930 f.write("#define ALL_VK_DEVICE_FUNCS() \\\n")
2932 for vk_func in self.registry.device_funcs:
2933 if not vk_func.needs_exposing():
2936 if not vk_func.needs_dispatch():
2937 LOGGER.debug("skipping {0} in ALL_VK_DEVICE_FUNCS".format(vk_func.name))
2941 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2944 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2947 f.write("#define ALL_VK_INSTANCE_FUNCS() \\\n")
2949 for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs:
2950 if not vk_func.needs_exposing():
2953 if not vk_func.needs_dispatch():
2954 LOGGER.debug("skipping {0} in ALL_VK_INSTANCE_FUNCS".format(vk_func.name))
2958 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2961 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2964 f.write("#endif /* __WINE_VULKAN_THUNKS_H */\n")
2966 def generate_loader_thunks_c(self, f):
2967 self._generate_copyright(f)
2969 f.write("#include \"vulkan_loader.h\"\n\n")
2971 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2973 for vk_func in self.registry.funcs.values():
2974 if not vk_func.needs_exposing():
2976 if vk_func.name in MANUAL_LOADER_THUNKS | MANUAL_LOADER_FUNCTIONS:
2979 f.write(vk_func.loader_thunk())
2981 f.write("static const struct vulkan_func vk_device_dispatch_table[] =\n{\n")
2982 for vk_func in self.registry.device_funcs:
2983 if not vk_func.needs_exposing():
2986 f.write(" {{\"{0}\", {0}}},\n".format(vk_func.name))
2989 f.write("static const struct vulkan_func vk_phys_dev_dispatch_table[] =\n{\n")
2990 for vk_func in self.registry.phys_dev_funcs:
2991 if not vk_func.needs_exposing():
2994 f.write(" {{\"{0}\", {0}}},\n".format(vk_func.name))
2997 f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
2998 for vk_func in self.registry.instance_funcs:
2999 if not vk_func.needs_exposing():
3002 f.write(" {{\"{0}\", {0}}},\n".format(vk_func.name))
3005 f.write("void *wine_vk_get_device_proc_addr(const char *name)\n")
3007 f.write(" unsigned int i;\n")
3008 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_dispatch_table); i++)\n")
3010 f.write(" if (strcmp(vk_device_dispatch_table[i].name, name) == 0)\n")
3012 f.write(" TRACE(\"Found name=%s in device table\\n\", debugstr_a(name));\n")
3013 f.write(" return vk_device_dispatch_table[i].func;\n")
3016 f.write(" return NULL;\n")
3019 f.write("void *wine_vk_get_phys_dev_proc_addr(const char *name)\n")
3021 f.write(" unsigned int i;\n")
3022 f.write(" for (i = 0; i < ARRAY_SIZE(vk_phys_dev_dispatch_table); i++)\n")
3024 f.write(" if (strcmp(vk_phys_dev_dispatch_table[i].name, name) == 0)\n")
3026 f.write(" TRACE(\"Found name=%s in physical device table\\n\", debugstr_a(name));\n")
3027 f.write(" return vk_phys_dev_dispatch_table[i].func;\n")
3030 f.write(" return NULL;\n")
3033 f.write("void *wine_vk_get_instance_proc_addr(const char *name)\n")
3035 f.write(" unsigned int i;\n")
3036 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_dispatch_table); i++)\n")
3038 f.write(" if (strcmp(vk_instance_dispatch_table[i].name, name) == 0)\n")
3040 f.write(" TRACE(\"Found name=%s in instance table\\n\", debugstr_a(name));\n")
3041 f.write(" return vk_instance_dispatch_table[i].func;\n")
3044 f.write(" return NULL;\n")
3047 def generate_loader_thunks_h(self, f):
3048 self._generate_copyright(f)
3050 f.write("#ifndef __WINE_VULKAN_LOADER_THUNKS_H\n")
3051 f.write("#define __WINE_VULKAN_LOADER_THUNKS_H\n\n")
3053 f.write("enum unix_call\n")
3055 f.write(" unix_init,\n")
3056 f.write(" unix_is_available_instance_function,\n")
3057 f.write(" unix_is_available_device_function,\n")
3058 for vk_func in self.registry.funcs.values():
3059 if not vk_func.needs_exposing():
3061 if vk_func.name in MANUAL_LOADER_FUNCTIONS:
3064 f.write(" unix_{0},\n".format(vk_func.name))
3065 f.write(" unix_count,\n")
3068 for vk_func in self.registry.funcs.values():
3069 if not vk_func.needs_exposing():
3071 if vk_func.name in MANUAL_LOADER_FUNCTIONS:
3074 f.write("struct {0}_params\n".format(vk_func.name))
3076 for p in vk_func.params:
3077 f.write(" {0};\n".format(p.definition(is_member=True)))
3078 if vk_func.extra_param:
3079 f.write(" void *{0};\n".format(vk_func.extra_param))
3080 if vk_func.type != "void":
3081 f.write(" {0} result;\n".format(vk_func.type))
3084 f.write("#endif /* __WINE_VULKAN_LOADER_THUNKS_H */\n")
3086 def generate_vulkan_h(self, f):
3087 self._generate_copyright(f)
3088 f.write("#ifndef __WINE_VULKAN_H\n")
3089 f.write("#define __WINE_VULKAN_H\n\n")
3091 f.write("#include <windef.h>\n")
3092 f.write("#include <stdint.h>\n\n")
3094 f.write("/* Define WINE_VK_HOST to get 'host' headers. */\n")
3095 f.write("#ifdef WINE_VK_HOST\n")
3096 f.write("#define VKAPI_CALL\n")
3097 f.write('#define WINE_VK_ALIGN(x)\n')
3098 f.write("#endif\n\n")
3100 f.write("#ifndef VKAPI_CALL\n")
3101 f.write("#define VKAPI_CALL __stdcall\n")
3102 f.write("#endif\n\n")
3104 f.write("#ifndef VKAPI_PTR\n")
3105 f.write("#define VKAPI_PTR VKAPI_CALL\n")
3106 f.write("#endif\n\n")
3108 f.write("#ifndef WINE_VK_ALIGN\n")
3109 f.write("#define WINE_VK_ALIGN DECLSPEC_ALIGN\n")
3110 f.write("#endif\n\n")
3112 # The overall strategy is to define independent constants and datatypes,
3113 # prior to complex structures and function calls to avoid forward declarations.
3114 for const in self.registry.consts:
3115 # For now just generate things we may not need. The amount of parsing needed
3116 # to get some of the info is tricky as you need to figure out which structure
3117 # references a certain constant.
3118 f.write(const.definition())
3121 for define in self.registry.defines:
3122 f.write(define.definition())
3124 for handle in self.registry.handles:
3125 # For backward compatibility also create definitions for aliases.
3126 # These types normally don't get pulled in as we use the new types
3127 # even in legacy functions if they are aliases.
3128 if handle.is_required() or handle.is_alias():
3129 f.write(handle.definition())
3132 for base_type in self.registry.base_types:
3133 f.write(base_type.definition())
3136 for bitmask in self.registry.bitmasks:
3137 f.write(bitmask.definition())
3140 # Define enums, this includes values for some of the bitmask types as well.
3141 for enum in self.registry.enums.values():
3143 f.write(enum.definition())
3145 for fp in self.registry.funcpointers:
3147 f.write(fp.definition())
3150 # This generates both structures and unions. Since structures
3151 # may depend on other structures/unions, we need a list of
3152 # decoupled structs.
3153 # Note: unions are stored in structs for dependency reasons,
3154 # see comment in parsing section.
3155 structs = VkStruct.decouple_structs(self.registry.structs)
3156 for struct in structs:
3157 LOGGER.debug("Generating struct: {0}".format(struct.name))
3158 f.write(struct.definition(align=True))
3161 for func in self.registry.funcs.values():
3162 if not func.is_required():
3163 LOGGER.debug("Skipping PFN definition for: {0}".format(func.name))
3166 f.write("typedef {0};\n".format(func.pfn(prefix="PFN", call_conv="VKAPI_PTR")))
3169 f.write("#ifndef VK_NO_PROTOTYPES\n")
3170 for func in self.registry.funcs.values():
3171 if not func.is_required():
3172 LOGGER.debug("Skipping API definition for: {0}".format(func.name))
3175 LOGGER.debug("Generating API definition for: {0}".format(func.name))
3176 f.write("{0};\n".format(func.prototype(call_conv="VKAPI_CALL")))
3177 f.write("#endif /* VK_NO_PROTOTYPES */\n\n")
3179 f.write("#endif /* __WINE_VULKAN_H */\n")
3181 def generate_vulkan_spec(self, f):
3182 self._generate_copyright(f, spec_file=True)
3183 f.write("@ stdcall -private vk_icdGetInstanceProcAddr(ptr str)\n")
3184 f.write("@ stdcall -private vk_icdGetPhysicalDeviceProcAddr(ptr str)\n")
3185 f.write("@ stdcall -private vk_icdNegotiateLoaderICDInterfaceVersion(ptr)\n")
3187 # Export symbols for all Vulkan Core functions.
3188 for func in self.registry.funcs.values():
3189 if not func.is_core_func():
3192 # We support all Core functions except for VK_KHR_display* APIs.
3193 # Create stubs for unsupported Core functions.
3194 if func.is_required():
3195 f.write(func.spec())
3197 f.write("@ stub {0}\n".format(func.name))
3199 f.write("@ stdcall -private DllRegisterServer()\n")
3200 f.write("@ stdcall -private DllUnregisterServer()\n")
3202 def generate_vulkan_loader_spec(self, f):
3203 self._generate_copyright(f, spec_file=True)
3205 # Export symbols for all Vulkan Core functions.
3206 for func in self.registry.funcs.values():
3207 if not func.is_core_func():
3210 # We support all Core functions except for VK_KHR_display* APIs.
3211 # Create stubs for unsupported Core functions.
3212 if func.is_required():
3213 f.write(func.spec(symbol="winevulkan." + func.name))
3215 f.write("@ stub {0}\n".format(func.name))
3218 class VkRegistry(object):
3219 def __init__(self, reg_filename):
3220 # Used for storage of type information.
3221 self.base_types = None
3222 self.bitmasks = None
3226 self.funcpointers = None
3230 # We aggregate all types in here for cross-referencing.
3233 self.surface_extensions = []
3235 self.version_regex = re.compile(
3244 # Overall strategy for parsing the registry is to first
3245 # parse all type / function definitions. Then parse
3246 # features and extensions to decide which types / functions
3247 # to actually 'pull in' for code generation. For each type or
3248 # function call we want we set a member 'required' to True.
3249 tree = ET.parse(reg_filename)
3250 root = tree.getroot()
3251 self._parse_enums(root)
3252 self._parse_types(root)
3253 self._parse_commands(root)
3255 # Pull in any required types and functions.
3256 self._parse_features(root)
3257 self._parse_extensions(root)
3259 for enum in self.enums.values():
3260 enum.fixup_64bit_aliases()
3262 self._match_object_types()
3264 self.copyright = root.find('./comment').text
3266 def _is_feature_supported(self, feature):
3267 version = self.version_regex.match(feature)
3271 version = tuple(map(int, version.group('major', 'minor')))
3272 return version <= WINE_VK_VERSION
3274 def _is_extension_supported(self, extension):
3275 # We disable some extensions as either we haven't implemented
3276 # support yet or because they are for platforms other than win32.
3277 return extension not in UNSUPPORTED_EXTENSIONS
3279 def _mark_command_required(self, command):
3280 """ Helper function to mark a certain command and the datatypes it needs as required."""
3281 def mark_bitmask_dependencies(bitmask, types):
3282 if bitmask.requires is not None:
3283 types[bitmask.requires]["data"].required = True
3285 def mark_funcpointer_dependencies(fp, types):
3286 for m in fp.members:
3287 type_info = types[m.type]
3289 # Complex types have a matching definition e.g. VkStruct.
3290 # Not needed for base types such as uint32_t.
3291 if "data" in type_info:
3292 types[m.type]["data"].required = True
3294 def mark_struct_dependencies(struct, types):
3296 type_info = types[m.type]
3298 # Complex types have a matching definition e.g. VkStruct.
3299 # Not needed for base types such as uint32_t.
3300 if "data" in type_info:
3301 types[m.type]["data"].required = True
3303 if type_info["category"] == "struct" and struct.name != m.type:
3305 mark_struct_dependencies(type_info["data"], types)
3306 elif type_info["category"] == "funcpointer":
3307 mark_funcpointer_dependencies(type_info["data"], types)
3308 elif type_info["category"] == "bitmask":
3309 mark_bitmask_dependencies(type_info["data"], types)
3311 func = self.funcs[command]
3312 func.required = True
3314 # Pull in return type
3315 if func.type != "void":
3316 self.types[func.type]["data"].required = True
3318 # Analyze parameter dependencies and pull in any type needed.
3319 for p in func.params:
3320 type_info = self.types[p.type]
3322 # Check if we are dealing with a complex type e.g. VkEnum, VkStruct and others.
3323 if "data" not in type_info:
3326 # Mark the complex type as required.
3327 type_info["data"].required = True
3328 if type_info["category"] == "struct":
3329 struct = type_info["data"]
3330 mark_struct_dependencies(struct, self.types)
3331 elif type_info["category"] == "bitmask":
3332 mark_bitmask_dependencies(type_info["data"], self.types)
3334 def _match_object_types(self):
3335 """ Matches each handle with the correct object type. """
3336 # Use upper case comparison for simplicity.
3338 for value in self.enums["VkObjectType"].values:
3339 object_name = "VK" + value.name[len("VK_OBJECT_TYPE"):].replace("_", "")
3340 object_types[object_name] = value.name
3342 for handle in self.handles:
3343 if not handle.is_required():
3345 handle.object_type = object_types.get(handle.name.upper())
3346 if not handle.object_type:
3347 LOGGER.warning("No object type found for {}".format(handle.name))
3349 def _parse_commands(self, root):
3350 """ Parse command section containing the Vulkan function calls. """
3352 commands = root.findall("./commands/")
3354 # As of Vulkan 1.1, various extensions got promoted to Core.
3355 # The old commands (e.g. KHR) are available for backwards compatibility
3356 # and are marked in vk.xml as 'alias' to the non-extension type.
3357 # The registry likes to avoid data duplication, so parameters and other
3358 # metadata need to be looked up from the Core command.
3359 # We parse the alias commands in a second pass.
3361 for command in commands:
3362 alias_name = command.attrib.get("alias")
3364 alias_commands.append(command)
3367 func = VkFunction.from_xml(command, self.types)
3369 funcs[func.name] = func
3371 for command in alias_commands:
3372 alias_name = command.attrib.get("alias")
3373 alias = funcs[alias_name]
3374 func = VkFunction.from_alias(command, alias)
3376 funcs[func.name] = func
3378 # To make life easy for the code generation, separate all function
3379 # calls out in the 4 types of Vulkan functions:
3380 # device, global, physical device and instance.
3385 for func in funcs.values():
3386 if func.is_device_func():
3387 device_funcs.append(func)
3388 elif func.is_global_func():
3389 global_funcs.append(func)
3390 elif func.is_phys_dev_func():
3391 phys_dev_funcs.append(func)
3393 instance_funcs.append(func)
3395 # Sort function lists by name and store them.
3396 self.device_funcs = sorted(device_funcs, key=lambda func: func.name)
3397 self.global_funcs = sorted(global_funcs, key=lambda func: func.name)
3398 self.phys_dev_funcs = sorted(phys_dev_funcs, key=lambda func: func.name)
3399 self.instance_funcs = sorted(instance_funcs, key=lambda func: func.name)
3401 # The funcs dictionary is used as a convenient way to lookup function
3402 # calls when needed e.g. to adjust member variables.
3403 self.funcs = OrderedDict(sorted(funcs.items()))
3405 def _parse_enums(self, root):
3406 """ Parse enums section or better described as constants section. """
3409 for enum in root.findall("./enums"):
3410 name = enum.attrib.get("name")
3411 _type = enum.attrib.get("type")
3413 if _type in ("enum", "bitmask"):
3414 enum_obj = VkEnum.from_xml(enum)
3416 enums[name] = enum_obj
3418 # If no type is set, we are dealing with API constants.
3419 for value in enum.findall("enum"):
3420 # If enum is an alias, set the value to the alias name.
3421 # E.g. VK_LUID_SIZE_KHR is an alias to VK_LUID_SIZE.
3422 alias = value.attrib.get("alias")
3424 self.consts.append(VkConstant(value.attrib.get("name"), alias))
3426 self.consts.append(VkConstant(value.attrib.get("name"), value.attrib.get("value")))
3428 self.enums = OrderedDict(sorted(enums.items()))
3430 def _process_require_enum(self, enum_elem, ext=None, only_aliased=False):
3431 if "extends" in enum_elem.keys():
3432 enum = self.types[enum_elem.attrib["extends"]]["data"]
3434 # Need to define VkEnumValues which were aliased to by another value. This is necessary
3435 # from VK spec version 1.2.135 where the provisional VK_KHR_ray_tracing extension was
3436 # added which altered VK_NV_ray_tracing's VkEnumValues to alias to the provisional
3439 for _, t in self.types.items():
3440 if t["category"] != "enum":
3444 for value in t["data"].values:
3445 if value.alias == enum_elem.attrib["name"]:
3448 if only_aliased and not aliased:
3451 if "bitpos" in enum_elem.keys():
3452 # We need to add an extra value to an existing enum type.
3453 # E.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG to VkFormatFeatureFlagBits.
3454 enum.create_bitpos(enum_elem.attrib["name"], int(enum_elem.attrib["bitpos"]))
3456 elif "offset" in enum_elem.keys():
3457 # Extensions promoted to Core, have the extension number as part
3458 # of the enum value. Else retrieve from the extension tag.
3459 if enum_elem.attrib.get("extnumber"):
3460 ext_number = int(enum_elem.attrib.get("extnumber"))
3462 ext_number = int(ext.attrib["number"])
3463 offset = int(enum_elem.attrib["offset"])
3464 value = EXT_BASE + (ext_number - 1) * EXT_BLOCK_SIZE + offset
3466 # Deal with negative values.
3467 direction = enum_elem.attrib.get("dir")
3468 if direction is not None:
3471 enum.create_value(enum_elem.attrib["name"], str(value))
3473 elif "value" in enum_elem.keys():
3474 enum.create_value(enum_elem.attrib["name"], enum_elem.attrib["value"])
3475 elif "alias" in enum_elem.keys():
3476 enum.create_alias(enum_elem.attrib["name"], enum_elem.attrib["alias"])
3478 elif "value" in enum_elem.keys():
3479 # Constant with an explicit value
3483 self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["value"]))
3484 elif "alias" in enum_elem.keys():
3486 if not only_aliased:
3489 self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["alias"]))
3492 def _require_type(type_info):
3493 if type_info.is_alias():
3494 type_info = type_info.alias
3495 type_info.required = True
3496 if type(type_info) == VkStruct:
3497 for member in type_info.members:
3498 if "data" in member.type_info:
3499 VkRegistry._require_type(member.type_info["data"])
3501 def _parse_extensions(self, root):
3502 """ Parse extensions section and pull in any types and commands for this extension. """
3504 exts = root.findall("./extensions/extension")
3506 skipped_exts = UNSUPPORTED_EXTENSIONS.copy()
3508 def process_ext(ext, deferred=False):
3509 ext_name = ext.attrib["name"]
3511 if ext_name.endswith('_surface') and ext.attrib.get('depends', None) == 'VK_KHR_surface':
3512 self.surface_extensions.append({"name" : ext_name})
3514 # Set extension name on any functions calls part of this extension as we
3515 # were not aware of the name during initial parsing.
3516 commands = ext.findall("require/command")
3517 for command in commands:
3518 cmd_name = command.attrib["name"]
3519 # Need to verify that the command is defined, and otherwise skip it.
3520 # vkCreateScreenSurfaceQNX is declared in <extensions> but not defined in
3521 # <commands>. A command without a definition cannot be enabled, so it's valid for
3522 # the XML file to handle this, but because of the manner in which we parse the XML
3523 # file we pre-populate from <commands> before we check if a command is enabled.
3524 if cmd_name in self.funcs:
3525 self.funcs[cmd_name].extensions.add(ext_name)
3527 # Some extensions are not ready or have numbers reserved as a place holder
3528 # or are only supported for VulkanSC.
3529 if not "vulkan" in ext.attrib["supported"].split(","):
3530 LOGGER.debug("Skipping disabled extension: {0}".format(ext_name))
3531 skipped_exts.append(ext_name)
3534 # Defer extensions with 'sortorder' as they are order-dependent for spec-parsing.
3535 if not deferred and "sortorder" in ext.attrib:
3536 deferred_exts.append(ext)
3539 # Disable highly experimental extensions as the APIs are unstable and can
3540 # change between minor Vulkan revisions until API is final and becomes KHR
3542 if ("KHX" in ext_name or "NVX" in ext_name) and ext_name not in ALLOWED_X_EXTENSIONS:
3543 LOGGER.debug("Skipping experimental extension: {0}".format(ext_name))
3544 skipped_exts.append(ext_name)
3547 # Extensions can define VkEnumValues which alias to provisional extensions. Pre-process
3548 # extensions to define any required VkEnumValues before the platform check below.
3549 for require in ext.findall("require"):
3550 # Extensions can add enum values to Core / extension enums, so add these.
3551 for enum_elem in require.findall("enum"):
3552 self._process_require_enum(enum_elem, ext, only_aliased=True)
3554 platform = ext.attrib.get("platform")
3555 if platform and platform != "win32":
3556 LOGGER.debug("Skipping extensions {0} for platform {1}".format(ext_name, platform))
3557 skipped_exts.append(ext_name)
3560 if not self._is_extension_supported(ext_name):
3561 LOGGER.debug("Skipping unsupported extension: {0}".format(ext_name))
3562 skipped_exts.append(ext_name)
3564 elif "requires" in ext.attrib:
3565 # Check if this extension builds on top of another unsupported extension.
3566 requires = ext.attrib["requires"].split(",")
3567 if len(set(requires).intersection(skipped_exts)) > 0:
3568 skipped_exts.append(ext_name)
3570 elif "depends" in ext.attrib:
3571 # The syntax for this is more complex, but this is good enough for now.
3572 if any([sext in ext.attrib["depends"] for sext in skipped_exts]):
3573 skipped_exts.append(ext_name)
3576 LOGGER.debug("Loading extension: {0}".format(ext_name))
3578 # Extensions can define one or more require sections each requiring
3579 # different features (e.g. Vulkan 1.1). Parse each require section
3580 # separately, so we can skip sections we don't want.
3581 for require in ext.findall("require"):
3582 # Extensions can add enum values to Core / extension enums, so add these.
3583 for enum_elem in require.findall("enum"):
3584 self._process_require_enum(enum_elem, ext)
3586 for t in require.findall("type"):
3587 type_info = self.types[t.attrib["name"]]["data"]
3588 self._require_type(type_info)
3589 feature = require.attrib.get("feature")
3590 if feature and not self._is_feature_supported(feature):
3593 required_extension = require.attrib.get("extension")
3594 if required_extension and not self._is_extension_supported(required_extension):
3597 # Pull in any commands we need. We infer types to pull in from the command
3599 for command in require.findall("command"):
3600 cmd_name = command.attrib["name"]
3601 self._mark_command_required(cmd_name)
3604 # Store a list with extensions.
3605 ext_info = {"name" : ext_name, "type" : ext.attrib["type"]}
3606 extensions.append(ext_info)
3609 # Process extensions, allowing for sortorder to defer extension processing
3613 deferred_exts.sort(key=lambda ext: ext.attrib["sortorder"])
3616 for ext in deferred_exts:
3617 process_ext(ext, deferred=True)
3619 # Sort in alphabetical order.
3620 self.extensions = sorted(extensions, key=lambda ext: ext["name"])
3622 def _parse_features(self, root):
3623 """ Parse the feature section, which describes Core commands and types needed. """
3625 for feature in root.findall("./feature"):
3626 if not api_is_vulkan(feature):
3628 feature_name = feature.attrib["name"]
3629 for require in feature.findall("require"):
3630 LOGGER.info("Including features for {0}".format(require.attrib.get("comment")))
3632 if tag.tag == "comment":
3634 elif tag.tag == "command":
3635 if not self._is_feature_supported(feature_name):
3637 name = tag.attrib["name"]
3638 self._mark_command_required(name)
3639 elif tag.tag == "enum":
3640 self._process_require_enum(tag)
3641 elif tag.tag == "type":
3642 name = tag.attrib["name"]
3644 # Skip pull in for vk_platform.h for now.
3645 if name == "vk_platform":
3648 type_info = self.types[name]
3649 type_info["data"].required = True
3651 def _parse_types(self, root):
3652 """ Parse types section, which contains all data types e.g. structs, typedefs etcetera. """
3653 types = root.findall("./types/type")
3665 type_info["category"] = t.attrib.get("category", None)
3666 type_info["requires"] = t.attrib.get("requires", None)
3668 # We parse aliases in a second pass when we know more.
3669 alias = t.attrib.get("alias")
3671 LOGGER.debug("Alias found: {0}".format(alias))
3672 alias_types.append(t)
3675 if type_info["category"] in ["include"]:
3678 if type_info["category"] == "basetype":
3679 name = t.find("name").text
3681 if not t.find("type") is None:
3682 _type = t.find("type").text
3683 tail = t.find("type").tail
3684 if tail is not None:
3685 _type += tail.strip()
3686 basetype = VkBaseType(name, _type)
3688 base_types.append(basetype)
3689 type_info["data"] = basetype
3693 # Basic C types don't need us to define them, but we do need data for them
3694 if type_info["requires"] == "vk_platform":
3695 requires = type_info["requires"]
3696 basic_c = VkBaseType(name, _type, requires=requires)
3697 type_info["data"] = basic_c
3699 if type_info["category"] == "bitmask":
3700 name = t.find("name").text
3701 _type = t.find("type").text
3703 # Most bitmasks have a requires attribute used to pull in
3704 # required '*FlagBits" enum.
3705 requires = type_info["requires"]
3706 bitmask = VkBaseType(name, _type, requires=requires)
3707 bitmasks.append(bitmask)
3708 type_info["data"] = bitmask
3710 if type_info["category"] == "define":
3711 define = VkDefine.from_xml(t)
3713 defines.append(define)
3714 type_info["data"] = define
3718 if type_info["category"] == "enum":
3719 name = t.attrib.get("name")
3720 # The type section only contains enum names, not the actual definition.
3721 # Since we already parsed the enum before, just link it in.
3723 type_info["data"] = self.enums[name]
3725 # Not all enums seem to be defined yet, typically that's for
3726 # ones ending in 'FlagBits' where future extensions may add
3728 type_info["data"] = None
3730 if type_info["category"] == "funcpointer":
3731 funcpointer = VkFunctionPointer.from_xml(t)
3733 funcpointers.append(funcpointer)
3734 type_info["data"] = funcpointer
3738 if type_info["category"] == "handle":
3739 handle = VkHandle.from_xml(t)
3741 handles.append(handle)
3742 type_info["data"] = handle
3746 if type_info["category"] in ["struct", "union"]:
3747 # We store unions among structs as some structs depend
3748 # on unions. The types are very similar in parsing and
3749 # generation anyway. The official Vulkan scripts use
3750 # a similar kind of hack.
3751 struct = VkStruct.from_xml(t)
3753 structs.append(struct)
3754 type_info["data"] = struct
3758 # Name is in general within a name tag else it is an optional
3759 # attribute on the type tag.
3760 name_elem = t.find("name")
3761 if name_elem is not None:
3762 type_info["name"] = name_elem.text
3764 type_info["name"] = t.attrib.get("name", None)
3766 # Store all type data in a shared dictionary, so we can easily
3767 # look up information for a given type. There are no duplicate
3769 self.types[type_info["name"]] = type_info
3771 # Second pass for alias types, so we can retrieve all data from
3772 # the aliased object.
3773 for t in alias_types:
3775 type_info["category"] = t.attrib.get("category")
3776 type_info["name"] = t.attrib.get("name")
3778 alias = t.attrib.get("alias")
3780 if type_info["category"] == "bitmask":
3781 bitmask = VkBaseType(type_info["name"], alias, alias=self.types[alias]["data"])
3782 bitmasks.append(bitmask)
3783 type_info["data"] = bitmask
3785 if type_info["category"] == "enum":
3786 enum = VkEnum.from_alias(t, self.types[alias]["data"])
3787 type_info["data"] = enum
3788 self.enums[enum.name] = enum
3790 if type_info["category"] == "handle":
3791 handle = VkHandle.from_alias(t, self.types[alias]["data"])
3792 handles.append(handle)
3793 type_info["data"] = handle
3795 if type_info["category"] == "struct":
3796 struct = VkStruct.from_alias(t, self.types[alias]["data"])
3797 structs.append(struct)
3798 type_info["data"] = struct
3800 self.types[type_info["name"]] = type_info
3802 # We need detailed type information during code generation
3803 # on structs for alignment reasons. Unfortunately structs
3804 # are parsed among other types, so there is no guarantee
3805 # that any types needed have been parsed already, so set
3807 for struct in structs:
3808 struct.set_type_info(self.types)
3810 # Alias structures have enum values equivalent to those of the
3811 # structure which they are aliased against. we need to ignore alias
3812 # structs when populating the struct extensions list, otherwise we
3813 # will create duplicate case entries.
3817 for structextend in struct.structextends:
3818 s = self.types[structextend]["data"]
3819 s.struct_extensions.append(struct)
3821 # Guarantee everything is sorted, so code generation doesn't have
3822 # to deal with this.
3823 self.base_types = sorted(base_types, key=lambda base_type: base_type.name)
3824 self.bitmasks = sorted(bitmasks, key=lambda bitmask: bitmask.name)
3825 self.defines = defines
3826 self.enums = OrderedDict(sorted(self.enums.items()))
3827 self.funcpointers = sorted(funcpointers, key=lambda fp: fp.name)
3828 self.handles = sorted(handles, key=lambda handle: handle.name)
3829 self.structs = sorted(structs, key=lambda struct: struct.name)
3831 def generate_vulkan_json(f):
3833 f.write(" \"file_format_version\": \"1.0.0\",\n")
3834 f.write(" \"ICD\": {\n")
3835 f.write(" \"library_path\": \".\\\\winevulkan.dll\",\n")
3836 f.write(" \"api_version\": \"{0}\"\n".format(VK_XML_VERSION))
3840 def set_working_directory():
3841 path = os.path.abspath(__file__)
3842 path = os.path.dirname(path)
3845 def download_vk_xml(filename):
3846 url = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/v{0}/xml/vk.xml".format(VK_XML_VERSION)
3847 if not os.path.isfile(filename):
3848 urllib.request.urlretrieve(url, filename)
3851 parser = argparse.ArgumentParser()
3852 parser.add_argument("-v", "--verbose", action="count", default=0, help="increase output verbosity")
3853 parser.add_argument("-x", "--xml", default=None, type=str, help="path to specification XML file")
3855 args = parser.parse_args()
3856 if args.verbose == 0:
3857 LOGGER.setLevel(logging.WARNING)
3858 elif args.verbose == 1:
3859 LOGGER.setLevel(logging.INFO)
3861 LOGGER.setLevel(logging.DEBUG)
3863 set_working_directory()
3868 vk_xml = "vk-{0}.xml".format(VK_XML_VERSION)
3869 download_vk_xml(vk_xml)
3871 registry = VkRegistry(vk_xml)
3872 generator = VkGenerator(registry)
3874 with open(WINE_VULKAN_H, "w") as f:
3875 generator.generate_vulkan_h(f)
3877 with open(WINE_VULKAN_THUNKS_H, "w") as f:
3878 generator.generate_thunks_h(f, "wine_")
3880 with open(WINE_VULKAN_THUNKS_C, "w") as f:
3881 generator.generate_thunks_c(f)
3883 with open(WINE_VULKAN_LOADER_THUNKS_H, "w") as f:
3884 generator.generate_loader_thunks_h(f)
3886 with open(WINE_VULKAN_LOADER_THUNKS_C, "w") as f:
3887 generator.generate_loader_thunks_c(f)
3889 with open(WINE_VULKAN_JSON, "w") as f:
3890 generate_vulkan_json(f)
3892 with open(WINE_VULKAN_SPEC, "w") as f:
3893 generator.generate_vulkan_spec(f)
3895 with open(WINE_VULKAN_LOADER_SPEC, "w") as f:
3896 generator.generate_vulkan_loader_spec(f)
3898 if __name__ == "__main__":