2 # Wine Vulkan generator
4 # Copyright 2017-2018 Roderick Colenbrander
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2.1 of the License, or (at your option) any later version.
11 # This library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this library; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 import xml.etree.ElementTree as ET
28 from collections import OrderedDict
29 from collections.abc import Sequence
32 # This script generates code for a Wine Vulkan ICD driver from Vulkan's vk.xml.
33 # Generating the code is like 10x worse than OpenGL, which is mostly a calling
34 # convention passthrough.
36 # The script parses vk.xml and maps functions and types to helper objects. These
37 # helper objects simplify the xml parsing and map closely to the Vulkan types.
38 # The code generation utilizes the helper objects during code generation and
39 # most of the ugly work is carried out by these objects.
41 # Vulkan ICD challenges:
42 # - Vulkan ICD loader (vulkan-1.dll) relies on a section at the start of
43 # 'dispatchable handles' (e.g. VkDevice, VkInstance) for it to insert
44 # its private data. It uses this area to stare its own dispatch tables
45 # for loader internal use. This means any dispatchable objects need wrapping.
47 # - Vulkan structures have different alignment between win32 and 32-bit Linux.
48 # This means structures with alignment differences need conversion logic.
49 # Often structures are nested, so the parent structure may not need any
50 # conversion, but some child may need some.
52 # vk.xml parsing challenges:
53 # - Contains type data for all platforms (generic Vulkan, Windows, Linux,..).
54 # Parsing of extension information required to pull in types and functions
55 # we really want to generate. Just tying all the data together is tricky.
57 # - Extensions can affect core types e.g. add new enum values, bitflags or
58 # additional structure chaining through 'pNext' / 'sType'.
60 # - Arrays are used all over the place for parameters or for structure members.
61 # Array length is often stored in a previous parameter or another structure
62 # member and thus needs careful parsing.
64 LOGGER = logging.Logger("vulkan")
65 LOGGER.addHandler(logging.StreamHandler())
67 VK_XML_VERSION = "1.3.226"
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",
96 "VK_AMD_display_native_hdr",
97 "VK_EXT_full_screen_exclusive",
98 "VK_EXT_hdr_metadata", # Needs WSI work.
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_win32_keyed_mutex",
105 "VK_NV_external_memory_rdma", # Needs shared resources work.
107 # Extensions for other platforms
108 "VK_EXT_external_memory_dma_buf",
109 "VK_EXT_image_drm_format_modifier",
110 "VK_EXT_metal_objects",
111 "VK_EXT_physical_device_drm",
112 "VK_GOOGLE_surfaceless_query",
113 "VK_KHR_external_fence_fd",
114 "VK_KHR_external_memory_fd",
115 "VK_KHR_external_semaphore_fd",
116 "VK_SEC_amigo_profiling", # Angle specific.
118 # Extensions which require callback handling
119 "VK_EXT_device_memory_report",
121 # Deprecated extensions
122 "VK_NV_external_memory_capabilities",
123 "VK_NV_external_memory_win32",
126 # Either internal extensions which aren't present on the win32 platform which
127 # winevulkan may nonetheless use, or extensions we want to generate headers for
128 # but not expose to applications (useful for test commits)
129 UNEXPOSED_EXTENSIONS = {
130 "VK_KHR_external_memory_win32",
133 # The Vulkan loader provides entry-points for core functionality and important
134 # extensions. Based on vulkan-1.def this amounts to WSI extensions on 1.0.51.
137 "VK_KHR_display_swapchain",
138 "VK_KHR_get_surface_capabilities2",
141 "VK_KHR_win32_surface",
144 # Some experimental extensions are used by shipping applications so their API is extremely unlikely
145 # to change in a backwards-incompatible way. Allow translation of those extensions with WineVulkan.
146 ALLOWED_X_EXTENSIONS = [
147 "VK_NVX_binary_import",
148 "VK_NVX_image_view_handle",
151 # Some frequently called functions use direct calls for performance reasons.
152 DIRECT_CALL_FUNCTIONS = [
153 "vkUpdateDescriptorSets",
154 "vkUpdateDescriptorSetWithTemplate",
157 # Functions part of our winevulkan graphics driver interface.
158 # DRIVER_VERSION should be bumped on any change to driver interface
159 # in FUNCTION_OVERRIDES
162 class ThunkType(Enum):
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 set whether we need a function pointer in the device
172 # / instance dispatch table.
173 # - driver sets whether the API is part of the driver interface.
174 # - thunk sets whether to create a thunk in vulkan_thunks.c.
175 # - NONE means there's a fully custom implementation.
176 # - PUBLIC means the implementation is fully auto generated.
177 # - PRIVATE thunks can be used in custom implementations for
179 # - loader_thunk sets whether to create a thunk for unix funcs.
180 FUNCTION_OVERRIDES = {
182 "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
183 "vkEnumerateInstanceExtensionProperties" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
184 "vkEnumerateInstanceLayerProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
185 "vkEnumerateInstanceVersion": {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
186 "vkGetInstanceProcAddr": {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
189 "vkCreateDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE, "extra_param" : "client_ptr"},
190 "vkDestroyInstance" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE },
191 "vkEnumerateDeviceExtensionProperties" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
192 "vkEnumerateDeviceLayerProperties": {"dispatch": True, "driver": False, "thunk": ThunkType.NONE},
193 "vkEnumeratePhysicalDeviceGroups" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
194 "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
195 "vkGetPhysicalDeviceExternalBufferProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
196 "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
197 "vkGetPhysicalDeviceExternalSemaphoreProperties" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
198 "vkGetPhysicalDeviceImageFormatProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
199 "vkGetPhysicalDeviceProperties2" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PUBLIC, "loader_thunk" : ThunkType.PRIVATE},
200 "vkGetPhysicalDeviceProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PUBLIC, "loader_thunk" : ThunkType.PRIVATE},
203 "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
204 "vkCreateCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
205 "vkCreateComputePipelines" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE},
206 "vkCreateGraphicsPipelines" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE},
207 "vkDestroyCommandPool" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
208 "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.PRIVATE},
209 "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
210 "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : ThunkType.NONE, "loader_thunk" : ThunkType.NONE},
211 "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
212 "vkGetDeviceQueue2" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
215 "vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE},
216 "vkGetPhysicalDeviceSurfaceSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
217 "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
218 "vkGetPhysicalDeviceSurfaceFormatsKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
219 "vkGetPhysicalDeviceSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
221 # VK_KHR_get_surface_capabilities2
222 "vkGetPhysicalDeviceSurfaceCapabilities2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PRIVATE},
223 "vkGetPhysicalDeviceSurfaceFormats2KHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
225 # VK_KHR_win32_surface
226 "vkCreateWin32SurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.NONE},
227 "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
230 "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
231 "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
232 "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
233 "vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
235 # VK_KHR_external_fence_capabilities
236 "vkGetPhysicalDeviceExternalFencePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
238 # VK_KHR_external_memory_capabilities
239 "vkGetPhysicalDeviceExternalBufferPropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
240 "vkGetPhysicalDeviceImageFormatProperties2KHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.PRIVATE},
242 # VK_KHR_external_semaphore_capabilities
243 "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR" : {"dispatch" : False, "driver" : False, "thunk" : ThunkType.NONE},
245 # VK_KHR_device_group_creation
246 "vkEnumeratePhysicalDeviceGroupsKHR" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
248 # VK_KHR_device_group
249 "vkGetDeviceGroupSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
250 "vkGetPhysicalDevicePresentRectanglesKHR" : {"dispatch" : True, "driver" : True, "thunk" : ThunkType.PUBLIC},
252 # VK_KHR_ray_tracing_pipeline
253 "vkCreateRayTracingPipelinesKHR" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE},
255 # VK_EXT_calibrated_timestamps
256 "vkGetPhysicalDeviceCalibrateableTimeDomainsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
257 "vkGetCalibratedTimestampsEXT" : {"dispatch" : True, "driver" : False, "thunk" : ThunkType.NONE},
260 "vkCreateDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
261 "vkDestroyDebugUtilsMessengerEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
263 # VK_EXT_debug_report
264 "vkCreateDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
265 "vkDestroyDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE},
268 "vkCreateRayTracingPipelinesNV" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.PRIVATE},
271 STRUCT_CHAIN_CONVERSIONS = {
272 # Ignore to not confuse host loader.
273 "VkDeviceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO"],
274 "VkInstanceCreateInfo": ["VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO"],
278 class Direction(Enum):
279 """ Parameter direction: input, output, input_output. """
285 class VkBaseType(object):
286 def __init__(self, name, _type, alias=None, requires=None):
287 """ Vulkan base type class.
289 VkBaseType is mostly used by Vulkan to define its own
290 base types like VkFlags through typedef out of e.g. uint32_t.
293 name (:obj:'str'): Name of the base type.
294 _type (:obj:'str'): Underlying type
295 alias (bool): type is an alias or not.
296 requires (:obj:'str', optional): Other types required.
297 Often bitmask values pull in a *FlagBits type.
302 self.requires = requires
303 self.required = False
305 def definition(self):
306 # Definition is similar for alias or non-alias as type
307 # is already set to alias.
308 if not self.type is None:
309 return "typedef {0} {1};\n".format(self.type, self.name)
311 return "struct {0};\n".format(self.name)
314 return bool(self.alias)
317 class VkConstant(object):
318 def __init__(self, name, value):
322 def definition(self):
323 text = "#define {0} {1}\n".format(self.name, self.value)
327 class VkDefine(object):
328 def __init__(self, name, value):
333 def from_xml(define):
334 name_elem = define.find("name")
336 if name_elem is None:
337 # <type category="define" name="some_name">some_value</type>
338 name = define.attrib.get("name")
340 # We override behavior of VK_USE_64_BIT_PTR_DEFINES as the default non-dispatchable handle
341 # definition various between 64-bit (uses pointers) and 32-bit (uses uint64_t).
342 # This complicates TRACEs in the thunks, so just use uint64_t.
343 if name == "VK_USE_64_BIT_PTR_DEFINES":
344 value = "#define VK_USE_64_BIT_PTR_DEFINES 0"
347 return VkDefine(name, value)
349 # With a name element the structure is like:
350 # <type category="define"><name>some_name</name>some_value</type>
351 name = name_elem.text
353 # Perform minimal parsing for Vulkan constants, which we don't need, but are referenced
354 # elsewhere in vk.xml.
355 # - VK_API_VERSION is a messy, deprecated constant and we don't want generate code for it.
356 # - AHardwareBuffer/ANativeWindow are forward declarations for Android types, which leaked
357 # into the define region.
358 if name in ["VK_API_VERSION", "AHardwareBuffer", "ANativeWindow", "CAMetalLayer"]:
359 return VkDefine(name, None)
361 # The body of the define is basically unstructured C code. It is not meant for easy parsing.
362 # Some lines contain deprecated values or comments, which we try to filter out.
364 for line in define.text.splitlines():
365 # Skip comments or deprecated values.
372 if child.tail is not None:
373 # Split comments for VK_API_VERSION_1_0 / VK_API_VERSION_1_1
374 if "//" in child.tail:
375 value += child.tail.split("//")[0]
379 return VkDefine(name, value.rstrip(' '))
381 def definition(self):
382 if self.value is None:
385 # Nothing to do as the value was already put in the right form during parsing.
386 return "{0}\n".format(self.value)
389 class VkEnum(object):
390 def __init__(self, name, bitwidth, alias=None):
391 if not bitwidth in [32, 64]:
392 LOGGER.error("unknown bitwidth {0} for {1}".format(bitwidth, name))
394 self.bitwidth = bitwidth
395 self.values = [] if alias == None else alias.values
396 self.required = False
401 def from_alias(enum, alias):
402 name = enum.attrib.get("name")
403 aliasee = VkEnum(name, alias.bitwidth, alias=alias)
405 alias.add_aliased_by(aliasee)
410 name = enum.attrib.get("name")
411 bitwidth = int(enum.attrib.get("bitwidth", "32"))
412 result = VkEnum(name, bitwidth)
414 for v in enum.findall("enum"):
415 value_name = v.attrib.get("name")
416 # Value is either a value or a bitpos, only one can exist.
417 value = v.attrib.get("value")
418 alias_name = v.attrib.get("alias")
420 result.create_alias(value_name, alias_name)
422 result.create_value(value_name, value)
425 result.create_bitpos(value_name, int(v.attrib.get("bitpos")))
428 # vulkan.h contains a *_MAX_ENUM value set to 32-bit at the time of writing,
429 # which is to prepare for extensions as they can add values and hence affect
430 # the size definition.
431 max_name = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2', name).upper() + "_MAX_ENUM"
432 result.create_value(max_name, "0x7fffffff")
436 def create_alias(self, name, alias_name):
437 """ Create an aliased value for this enum """
438 self.add(VkEnumValue(name, self.bitwidth, alias=alias_name))
440 def create_value(self, name, value):
441 """ Create a new value for this enum """
442 # Some values are in hex form. We want to preserve the hex representation
443 # at least when we convert back to a string. Internally we want to use int.
445 self.add(VkEnumValue(name, self.bitwidth, value=int(value, 0), hex=hex))
447 def create_bitpos(self, name, pos):
448 """ Create a new bitmask value for this enum """
449 self.add(VkEnumValue(name, self.bitwidth, value=(1 << pos), hex=True))
451 def add(self, value):
452 """ Add a value to enum. """
454 # Extensions can add new enum values. When an extension is promoted to Core
455 # the registry defines the value twice once for old extension and once for
456 # new Core features. Add the duplicate if it's explicitly marked as an
457 # alias, otherwise ignore it.
458 for v in self.values:
459 if not value.is_alias() and v.value == value.value:
460 LOGGER.debug("Adding duplicate enum value {0} to {1}".format(v, self.name))
462 # Avoid adding duplicate aliases multiple times
463 if not any(x.name == value.name for x in self.values):
464 self.values.append(value)
466 def fixup_64bit_aliases(self):
467 """ Replace 64bit aliases with literal values """
468 # Older GCC versions need a literal to initialize a static const uint64_t
469 # which is what we use for 64bit bitmasks.
470 if self.bitwidth != 64:
472 for value in self.values:
473 if not value.is_alias():
475 alias = next(x for x in self.values if x.name == value.alias)
476 value.hex = alias.hex
477 value.value = alias.value
479 def definition(self):
483 default_value = 0x7ffffffe if self.bitwidth == 32 else 0xfffffffffffffffe
485 # Print values sorted, values can have been added in a random order.
486 values = sorted(self.values, key=lambda value: value.value if value.value is not None else default_value)
488 if self.bitwidth == 32:
489 text = "typedef enum {0}\n{{\n".format(self.name)
491 text += " {0},\n".format(value.definition())
492 text += "}} {0};\n".format(self.name)
493 elif self.bitwidth == 64:
494 text = "typedef VkFlags64 {0};\n\n".format(self.name)
496 text += "static const {0} {1};\n".format(self.name, value.definition())
498 for aliasee in self.aliased_by:
499 text += "typedef {0} {1};\n".format(self.name, aliasee.name)
505 return bool(self.alias)
507 def add_aliased_by(self, aliasee):
508 self.aliased_by.append(aliasee)
511 class VkEnumValue(object):
512 def __init__(self, name, bitwidth, value=None, hex=False, alias=None):
514 self.bitwidth = bitwidth
520 postfix = "ull" if self.bitwidth == 64 else ""
521 if self.is_alias() and self.value == None:
522 return "{0}={1}".format(self.name, self.alias)
523 return "{0}={1}{2}".format(self.name, self.value, postfix)
525 def definition(self):
526 """ Convert to text definition e.g. VK_FOO = 1 """
527 postfix = "ull" if self.bitwidth == 64 else ""
528 if self.is_alias() and self.value == None:
529 return "{0} = {1}".format(self.name, self.alias)
531 # Hex is commonly used for FlagBits and sometimes within
532 # a non-FlagBits enum for a bitmask value as well.
534 return "{0} = 0x{1:08x}{2}".format(self.name, self.value, postfix)
536 return "{0} = {1}{2}".format(self.name, self.value, postfix)
539 return self.alias is not None
542 class VkFunction(object):
543 def __init__(self, _type=None, name=None, params=[], alias=None):
544 self.extensions = set()
550 # For some functions we need some extra metadata from FUNCTION_OVERRIDES.
551 func_info = FUNCTION_OVERRIDES.get(self.name, None)
552 self.dispatch = func_info["dispatch"] if func_info else True
553 self.driver = func_info["driver"] if func_info else False
554 self.thunk_type = func_info["thunk"] if func_info else ThunkType.PUBLIC
555 self.loader_thunk_type = func_info["loader_thunk"] if func_info and "loader_thunk" in func_info else ThunkType.PUBLIC
556 self.extra_param = func_info["extra_param"] if func_info and "extra_param" in func_info else None
558 # Required is set while parsing which APIs and types are required
559 # and is used by the code generation.
560 self.required = True if func_info else False
563 def from_alias(command, alias):
564 """ Create VkFunction from an alias command.
567 command: xml data for command
568 alias (VkFunction): function to use as a base for types / parameters.
573 func_name = command.attrib.get("name")
574 func_type = alias.type
575 params = alias.params
577 return VkFunction(_type=func_type, name=func_name, params=params, alias=alias)
580 def from_xml(command, types):
581 proto = command.find("proto")
582 func_name = proto.find("name").text
583 func_type = proto.find("type").text
586 for param in command.findall("param"):
587 vk_param = VkParam.from_xml(param, types)
588 params.append(vk_param)
590 return VkFunction(_type=func_type, name=func_name, params=params)
592 def get_conversions(self):
593 """ Get a list of conversion functions required for this function if any.
594 Parameters which are structures may require conversion between win32
595 and the host platform. This function returns a list of conversions
600 for param in self.params:
601 convs = param.get_conversions()
602 if convs is not None:
603 conversions.extend(convs)
608 return bool(self.alias)
610 def is_core_func(self):
611 """ Returns whether the function is a Vulkan core function.
612 Core functions are APIs defined by the Vulkan spec to be part of the
613 Core API as well as several KHR WSI extensions.
616 if not self.extensions:
619 return any(ext in self.extensions for ext in CORE_EXTENSIONS)
621 def is_device_func(self):
622 # If none of the other, it must be a device function
623 return not self.is_global_func() and not self.is_instance_func() and not self.is_phys_dev_func()
625 def is_driver_func(self):
626 """ Returns if function is part of Wine driver interface. """
629 def is_global_func(self):
630 # Treat vkGetInstanceProcAddr as a global function as it
631 # can operate with NULL for vkInstance.
632 if self.name == "vkGetInstanceProcAddr":
634 # Global functions are not passed a dispatchable object.
635 elif self.params[0].is_dispatchable():
639 def is_instance_func(self):
640 # Instance functions are passed VkInstance.
641 if self.params[0].type == "VkInstance":
645 def is_phys_dev_func(self):
646 # Physical device functions are passed VkPhysicalDevice.
647 if self.params[0].type == "VkPhysicalDevice":
651 def is_required(self):
654 def returns_longlong(self):
655 return self.type in ["uint64_t", "VkDeviceAddress"]
657 def needs_conversion(self):
658 """ Check if the function needs any input/output type conversion.
659 Functions need input/output conversion if struct parameters have
660 alignment differences between Win32 and Linux 32-bit.
663 for p in self.params:
664 if p.needs_conversion():
665 LOGGER.debug("Parameter {0} to {1} requires conversion".format(p.name, self.name))
670 def needs_unwrapping(self):
671 """ Check if the function needs any input/output type unwrapping.
672 Functions need input/output unwrapping if struct parameters have
676 for p in self.params:
677 if p.needs_unwrapping():
682 def needs_dispatch(self):
685 def needs_thunk(self):
686 return self.thunk_type != ThunkType.NONE
688 def needs_private_thunk(self):
689 return self.thunk_type == ThunkType.PRIVATE
691 def needs_exposing(self):
692 # The function needs exposed if at-least one extension isn't both UNSUPPORTED and UNEXPOSED
693 return self.is_required() and (not self.extensions or not self.extensions.issubset(UNEXPOSED_EXTENSIONS))
695 def needs_direct_call(self):
696 # vkCmd* functions are frequently called, use direct calls for performance
697 if self.name.startswith("vkCmd"):
699 return self.name in DIRECT_CALL_FUNCTIONS
701 def pfn(self, prefix="p", call_conv=None, conv=False):
702 """ Create function pointer. """
705 pfn = "{0} ({1} *{2}_{3})(".format(self.type, call_conv, prefix, self.name)
707 pfn = "{0} (*{1}_{2})(".format(self.type, prefix, self.name)
709 for i, param in enumerate(self.params):
711 pfn += param.const + " "
714 if conv and param.needs_conversion():
717 if param.is_pointer():
718 pfn += " " + param.pointer
720 if param.array_len is not None:
721 pfn += "[{0}]".format(param.array_len)
723 if i < len(self.params) - 1:
728 def prototype(self, call_conv=None, prefix=None, postfix=None):
729 """ Generate prototype for given function.
732 call_conv (str, optional): calling convention e.g. WINAPI
733 prefix (str, optional): prefix to append prior to function name e.g. vkFoo -> wine_vkFoo
734 postfix (str, optional): text to append after function name but prior to semicolon e.g. DECLSPEC_HIDDEN
737 proto = "{0}".format(self.type)
739 if call_conv is not None:
740 proto += " {0}".format(call_conv)
742 if prefix is not None:
743 proto += " {0}{1}(".format(prefix, self.name)
745 proto += " {0}(".format(self.name)
747 # Add all the parameters.
748 proto += ", ".join([p.definition() for p in self.params])
750 if postfix is not None:
751 proto += ") {0}".format(postfix)
757 def body(self, params_prefix=""):
760 if not self.needs_private_thunk():
761 body += " {0}".format(self.trace(params_prefix=params_prefix))
763 params = ", ".join([p.variable(conv=False, params_prefix=params_prefix) for p in self.params])
765 # Call the native Vulkan function.
767 if self.returns_longlong():
768 body += "{0}result = ".format(params_prefix)
769 elif self.type != "void":
771 body += "{0}.p_{1}({2});\n".format(self.params[0].dispatch_table(params_prefix),
773 if self.type == "void" or self.returns_longlong():
774 body += " return STATUS_SUCCESS;\n"
778 def loader_body(self):
779 body = " struct {0}_params params;\n".format(self.name)
780 for p in self.params:
781 body += " params.{0} = {0};\n".format(p.name)
784 # Call the Unix function.
785 if self.type != "void" and not self.returns_longlong():
787 if self.needs_direct_call():
788 body += "p_vk_direct_unix_call(unix_handle, unix_{0}, ¶ms);\n".format(self.name)
790 body += "vk_unix_call(unix_{0}, ¶ms);\n".format(self.name)
791 if self.returns_longlong():
792 body += " return params.result;\n"
795 def body_conversion(self, conv, params_prefix=""):
799 # Declare a variable to hold the result for non-void functions.
800 if self.returns_longlong():
801 result_prefix = "params->"
802 elif self.type != "void":
803 body += " {0} result;\n".format(self.type)
805 # Declare any tmp parameters for conversion.
806 for p in self.params:
807 if p.needs_conversion() and conv:
808 if p.is_dynamic_array():
809 body += " {0}_host *{1}_host;\n".format(p.type, p.name)
811 body += " {0}_host {1}_host;\n".format(p.type, p.name)
812 elif p.needs_unwrapping():
813 if p.is_dynamic_array():
814 body += " {0} *{1}_host;\n".format(p.type, p.name)
816 body += " {0} {1}_host;\n".format(p.type, p.name)
818 if not self.needs_private_thunk():
819 body += " {0}\n".format(self.trace(params_prefix=params_prefix))
821 # Call any win_to_host conversion calls.
822 for p in self.params:
823 if p.needs_input_conversion() and (p.needs_unwrapping() or conv):
824 body += p.copy(Direction.INPUT, prefix=params_prefix)
826 # Build list of parameters containing converted and non-converted parameters.
827 # The param itself knows if conversion is needed and applies it when we set conv=True.
828 params = ", ".join([p.variable(conv=conv, params_prefix=params_prefix) for p in self.params])
830 # Call the native Vulkan function.
831 if self.type == "void":
832 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(params_prefix),
835 body += " {0}result = {1}.p_{2}({3});\n".format(result_prefix,
836 self.params[0].dispatch_table(params_prefix),
841 # Call any host_to_win conversion calls.
842 for p in self.params:
843 if not p.needs_output_conversion():
846 body += p.copy(Direction.OUTPUT, prefix=params_prefix)
848 # Perform any required cleanups. Most of these are for array functions.
849 for p in self.params:
850 if p.needs_free() and (p.needs_unwrapping() or conv):
851 body += p.free(prefix=params_prefix)
853 # Finally return the result.
854 if self.type != "void" and not self.returns_longlong():
855 body += " return result;\n"
857 body += " return STATUS_SUCCESS;\n"
861 def spec(self, prefix=None, symbol=None):
862 """ Generate spec file entry for this function.
865 prefix (str, optional): prefix to prepend to entry point name.
866 symbol (str, optional): allows overriding the name of the function implementing the entry point.
870 params = " ".join([p.spec() for p in self.params])
871 if prefix is not None:
872 spec += "@ stdcall -private {0}{1}({2})".format(prefix, self.name, params)
874 spec += "@ stdcall {0}({1})".format(self.name, params)
876 if symbol is not None:
882 def stub(self, call_conv=None, prefix=None):
883 stub = self.prototype(call_conv=call_conv, prefix=prefix)
885 stub += " {0}".format(self.trace(message="stub: ", trace_func="FIXME"))
887 if self.type == "VkResult":
888 stub += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
889 elif self.type == "VkBool32":
890 stub += " return VK_FALSE;\n"
891 elif self.type == "PFN_vkVoidFunction":
892 stub += " return NULL;\n"
897 def thunk(self, prefix=None):
898 if prefix == "thunk_":
899 thunk = self.prototype(prefix=prefix)
903 thunk = "NTSTATUS {0}{1}(void *args)\n".format(prefix, self.name)
905 thunk += " struct {0}_params *params = args;\n".format(self.name)
906 params_prefix = "params->"
908 if self.needs_conversion():
909 thunk += "#if defined(USE_STRUCT_CONVERSION)\n"
910 thunk += self.body_conversion(conv=True, params_prefix=params_prefix)
912 if self.needs_unwrapping():
913 thunk += self.body_conversion(conv=False, params_prefix=params_prefix)
915 thunk += self.body(params_prefix=params_prefix)
917 elif self.needs_unwrapping():
918 thunk += self.body_conversion(conv=False, params_prefix=params_prefix)
920 thunk += self.body(params_prefix=params_prefix)
925 def loader_thunk(self, prefix=None):
926 thunk = self.prototype(call_conv="WINAPI", prefix=prefix)
928 thunk += self.loader_body()
932 def trace(self, message=None, trace_func=None, params_prefix=""):
933 """ Create a trace string including all parameters.
936 message (str, optional): text to print at start of trace message e.g. 'stub: '
937 trace_func (str, optional): used to override trace function e.g. FIXME, printf, etcetera.
939 if trace_func is not None:
940 trace = "{0}(\"".format(trace_func)
944 if message is not None:
947 # First loop is for all the format strings.
948 trace += ", ".join([p.format_string() for p in self.params])
951 # Second loop for parameter names and optional conversions.
952 for param in self.params:
953 if param.format_conv is not None:
954 trace += ", " + param.format_conv.format("{0}{1}".format(params_prefix, param.name))
956 trace += ", {0}{1}".format(params_prefix, param.name)
962 class VkFunctionPointer(object):
963 def __init__(self, _type, name, members, forward_decls):
965 self.members = members
967 self.required = False
968 self.forward_decls = forward_decls
971 def from_xml(funcpointer):
975 for t in funcpointer.findall("type"):
977 # <type>void</type>* pUserData,
978 # Parsing of the tail (anything past </type>) is tricky since there
979 # can be other data on the next line like: const <type>int</type>..
981 const = True if begin and "const" in begin else False
983 lines = t.tail.split(",\n")
984 if lines[0][0] == "*":
986 name = lines[0][1:].strip()
989 name = lines[0].strip()
991 # Filter out ); if it is contained.
992 name = name.partition(");")[0]
994 # If tail encompasses multiple lines, assign the second line to begin
997 begin = lines[1].strip()
1001 members.append(VkMember(const=const, _type=_type, pointer=pointer, name=name))
1003 _type = funcpointer.text
1004 name = funcpointer.find("name").text
1005 if "requires" in funcpointer.attrib:
1006 forward_decls = funcpointer.attrib.get("requires").split(",")
1009 return VkFunctionPointer(_type, name, members, forward_decls)
1011 def definition(self):
1013 # forward declare required structs
1014 for decl in self.forward_decls:
1015 text += "typedef struct {0} {0};\n".format(decl)
1017 text += "{0} {1})(\n".format(self.type, self.name)
1020 if len(self.members) > 0:
1021 for m in self.members:
1023 text += " " + m.definition()
1026 text += ",\n " + m.definition()
1028 # Just make the compiler happy by adding a void parameter.
1036 class VkHandle(object):
1037 def __init__(self, name, _type, parent, alias=None):
1040 self.parent = parent
1042 self.required = False
1043 self.object_type = None
1046 def from_alias(handle, alias):
1047 name = handle.attrib.get("name")
1048 return VkHandle(name, alias.type, alias.parent, alias=alias)
1051 def from_xml(handle):
1052 name = handle.find("name").text
1053 _type = handle.find("type").text
1054 parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice.
1055 return VkHandle(name, _type, parent)
1057 def dispatch_table(self, param):
1058 if not self.is_dispatchable():
1061 if self.parent is None:
1062 # Should only happen for VkInstance
1063 return "{0}->funcs".format(param)
1064 elif self.name == "VkDevice":
1065 return "wine_device_from_handle({0})->funcs".format(param)
1066 elif self.name == "VkQueue":
1067 return "wine_queue_from_handle({0})->device->funcs".format(param)
1068 elif self.parent in ["VkInstance", "VkPhysicalDevice"]:
1069 return "{0}->instance->funcs".format(param)
1070 elif self.parent in ["VkDevice", "VkCommandPool"]:
1071 return "{0}->device->funcs".format(param)
1073 LOGGER.error("Unhandled dispatchable parent: {0}".format(self.parent))
1075 def definition(self):
1076 """ Generates handle definition e.g. VK_DEFINE_HANDLE(vkInstance) """
1078 # Legacy types are typedef'ed to the new type if they are aliases.
1080 return "typedef {0} {1};\n".format(self.alias.name, self.name)
1082 return "{0}({1})\n".format(self.type, self.name)
1085 return self.alias is not None
1087 def is_dispatchable(self):
1088 """ Some handles like VkInstance, VkDevice are dispatchable objects,
1089 which means they contain a dispatch table of function pointers.
1091 return self.type == "VK_DEFINE_HANDLE"
1093 def is_required(self):
1094 return self.required
1096 def native_handle(self, name):
1097 """ Provide access to the native handle of a wrapped object. """
1099 if self.name == "VkCommandPool":
1100 return "wine_cmd_pool_from_handle({0})->command_pool".format(name)
1101 if self.name == "VkDebugUtilsMessengerEXT":
1102 return "wine_debug_utils_messenger_from_handle({0})->debug_messenger".format(name)
1103 if self.name == "VkDebugReportCallbackEXT":
1104 return "wine_debug_report_callback_from_handle({0})->debug_callback".format(name)
1105 if self.name == "VkDevice":
1106 return "wine_device_from_handle({0})->device".format(name)
1107 if self.name == "VkQueue":
1108 return "wine_queue_from_handle({0})->queue".format(name)
1109 if self.name == "VkSurfaceKHR":
1110 return "wine_surface_from_handle({0})->surface".format(name)
1112 native_handle_name = None
1114 if self.name == "VkCommandBuffer":
1115 native_handle_name = "command_buffer"
1116 if self.name == "VkInstance":
1117 native_handle_name = "instance"
1118 if self.name == "VkPhysicalDevice":
1119 native_handle_name = "phys_dev"
1121 if native_handle_name:
1122 return "{0}->{1}".format(name, native_handle_name)
1124 if self.is_dispatchable():
1125 LOGGER.error("Unhandled native handle for: {0}".format(self.name))
1128 def driver_handle(self, name):
1129 """ Provide access to the handle that should be passed to the wine driver """
1131 if self.name == "VkSurfaceKHR":
1132 return "wine_surface_from_handle({0})->driver_surface".format(name)
1134 return self.native_handle(name)
1136 def is_wrapped(self):
1137 return self.native_handle("test") is not None
1139 def needs_conversion(self):
1142 def needs_unwrapping(self):
1143 return self.is_wrapped()
1145 class VkMember(object):
1146 def __init__(self, const=False, struct_fwd_decl=False,_type=None, pointer=None, name=None, array_len=None,
1147 dyn_array_len=None, optional=False, values=None, object_type=None, bit_width=None):
1149 self.struct_fwd_decl = struct_fwd_decl
1151 self.pointer = pointer
1153 self.type_info = None
1154 self.array_len = array_len
1155 self.dyn_array_len = dyn_array_len
1156 self.optional = optional
1157 self.values = values
1158 self.object_type = object_type
1159 self.bit_width = bit_width
1161 def __eq__(self, other):
1162 """ Compare member based on name against a string.
1164 This method is for convenience by VkStruct, which holds a number of members and needs quick checking
1165 if certain members exist.
1168 return self.name == other
1171 return "{0} {1} {2} {3} {4} {5} {6}".format(self.const, self.struct_fwd_decl, self.type, self.pointer,
1172 self.name, self.array_len, self.dyn_array_len)
1175 def from_xml(member):
1176 """ Helper function for parsing a member tag within a struct or union. """
1178 name_elem = member.find("name")
1179 type_elem = member.find("type")
1182 struct_fwd_decl = False
1188 values = member.get("values")
1191 if "const" in member.text:
1194 # Some members contain forward declarations:
1195 # - VkBaseInstructure has a member "const struct VkBaseInStructure *pNext"
1196 # - VkWaylandSurfaceCreateInfoKHR has a member "struct wl_display *display"
1197 if "struct" in member.text:
1198 struct_fwd_decl = True
1200 if type_elem is not None:
1201 member_type = type_elem.text
1202 if type_elem.tail is not None:
1203 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1205 # Name of other member within, which stores the number of
1206 # elements pointed to be by this member.
1207 dyn_array_len = member.get("len")
1209 # Some members are optional, which is important for conversion code e.g. not dereference NULL pointer.
1210 optional = True if member.get("optional") else False
1212 # Usually we need to allocate memory for dynamic arrays. We need to do the same in a few other cases
1213 # like for VkCommandBufferBeginInfo.pInheritanceInfo. Just threat such cases as dynamic arrays of
1214 # size 1 to simplify code generation.
1215 if dyn_array_len is None and pointer is not None:
1218 # Some members are arrays, attempt to parse these. Formats include:
1219 # <member><type>char</type><name>extensionName</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
1220 # <member><type>uint32_t</type><name>foo</name>[4]</member>
1221 if name_elem.tail and name_elem.tail[0] == '[':
1222 LOGGER.debug("Found array type")
1223 enum_elem = member.find("enum")
1224 if enum_elem is not None:
1225 array_len = enum_elem.text
1227 # Remove brackets around length
1228 array_len = name_elem.tail.strip("[]")
1230 object_type = member.get("objecttype", None)
1232 # Some members are bit field values:
1233 # <member><type>uint32_t</type> <name>mask</name>:8</member>
1234 if name_elem.tail and name_elem.tail[0] == ':':
1235 LOGGER.debug("Found bit field")
1236 bit_width = int(name_elem.tail[1:])
1238 return VkMember(const=const, struct_fwd_decl=struct_fwd_decl, _type=member_type, pointer=pointer, name=name_elem.text,
1239 array_len=array_len, dyn_array_len=dyn_array_len, optional=optional, values=values, object_type=object_type, bit_width=bit_width)
1241 def copy(self, input, output, direction, conv):
1242 """ Helper method for use by conversion logic to generate a C-code statement to copy this member.
1243 - `conv` indicates whether the statement is in a struct alignment conversion path. """
1245 if (conv and self.needs_conversion()) or self.needs_unwrapping():
1246 if self.is_dynamic_array():
1247 if direction == Direction.OUTPUT:
1248 LOGGER.warn("TODO: implement copying of returnedonly dynamic array for {0}.{1}".format(self.type, self.name))
1250 # Array length is either a variable name (string) or an int.
1251 count = self.dyn_array_len if isinstance(self.dyn_array_len, int) else "{0}{1}".format(input, self.dyn_array_len)
1252 return "{0}{1} = convert_{2}_array_win_to_host({3}{1}, {4});\n".format(output, self.name, self.type, input, count)
1253 elif self.is_static_array():
1254 count = self.array_len
1255 if direction == Direction.OUTPUT:
1256 # Needed by VkMemoryHeap.memoryHeaps
1257 return "convert_{0}_static_array_host_to_win({2}{1}, {3}{1}, {4});\n".format(self.type, self.name, input, output, count)
1259 # Nothing needed this yet.
1260 LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
1261 elif self.is_handle() and self.needs_unwrapping():
1262 if direction == Direction.OUTPUT:
1263 LOGGER.err("OUTPUT parameter {0}.{1} cannot be unwrapped".format(self.type, self.name))
1265 handle = self.type_info["data"]
1266 return "{0}{1} = {2};\n".format(output, self.name, handle.driver_handle("{0}{1}".format(input, self.name)))
1267 elif self.is_generic_handle():
1268 if direction == Direction.OUTPUT:
1269 LOGGER.err("OUTPUT parameter {0}.{1} cannot be unwrapped".format(self.type, self.name))
1271 return "{0}{1} = wine_vk_unwrap_handle({2}{3}, {2}{1});\n".format(output, self.name, input, self.object_type)
1273 if direction == Direction.OUTPUT:
1274 return "convert_{0}_host_to_win(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
1276 return "convert_{0}_win_to_host(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
1277 elif self.is_static_array():
1278 bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type)
1279 return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count)
1281 return "{0}{1} = {2}{1};\n".format(output, self.name, input)
1283 def free(self, location, conv):
1284 """ Helper method for use by conversion logic to generate a C-code statement to free this member. """
1286 if not self.needs_unwrapping() and not conv:
1289 # Add a cast to ignore const on conversion structs we allocated ourselves.
1290 # sample expected output: (VkSparseMemoryBind_host *)
1292 cast = "(" + self.type
1293 if self.needs_conversion() and conv:
1299 if self.is_dynamic_array():
1300 count = self.dyn_array_len if isinstance(self.dyn_array_len, int) else "{0}{1}".format(location, self.dyn_array_len)
1301 if self.is_struct() and self.type_info["data"].returnedonly:
1302 # For returnedonly, counts is stored in a pointer.
1303 return "free_{0}_array({1}{2}{3}, *{4});\n".format(self.type, cast, location, self.name, count)
1305 return "free_{0}_array({1}{2}{3}, {4});\n".format(self.type, cast, location, self.name, count)
1307 # We are operating on a single structure. Some structs (very rare) contain dynamic members,
1308 # which would need freeing.
1309 if self.needs_free():
1310 return "free_{0}({1}&{2}{3});\n".format(self.type, cast, location, self.name)
1313 def definition(self, align=False, conv=False):
1314 """ Generate prototype for given function.
1317 align (bool, optional): Enable alignment if a type needs it. This adds WINE_VK_ALIGN(8) to a member.
1318 conv (bool, optional): Enable conversion if a type needs it. This appends '_host' to the name.
1325 if self.is_struct_forward_declaration():
1328 if conv and self.is_struct():
1329 text += "{0}_host".format(self.type)
1333 if self.is_pointer():
1334 text += " {0}{1}".format(self.pointer, self.name)
1336 if align and self.needs_alignment():
1337 text += " WINE_VK_ALIGN(8) " + self.name
1339 text += " " + self.name
1341 if self.is_static_array():
1342 text += "[{0}]".format(self.array_len)
1344 if self.is_bit_field():
1345 text += ":{}".format(self.bit_width)
1349 def get_conversions(self):
1350 """ Return any conversion description for this member and its children when conversion is needed. """
1352 # Check if we need conversion either for this member itself or for any child members
1353 # in case member represents a struct.
1354 if not self.needs_conversion() and not self.needs_unwrapping():
1359 # Collect any conversion for any member structs.
1360 if self.is_struct():
1361 struct = self.type_info["data"]
1363 m.needs_struct_extensions_conversion()
1364 if m.needs_conversion() or m.needs_unwrapping():
1365 conversions.extend(m.get_conversions())
1367 struct.needs_struct_extensions_conversion()
1368 direction = Direction.OUTPUT if struct.returnedonly else Direction.INPUT
1369 elif self.is_handle() or self.is_generic_handle():
1370 direction = Direction.INPUT
1372 operand = self.type_info["data"]
1373 if self.is_dynamic_array():
1374 conversions.append(ConversionFunction(False, True, direction, operand))
1375 elif self.is_static_array():
1376 conversions.append(ConversionFunction(True, False, direction, operand))
1378 conversions.append(ConversionFunction(False, False, direction, operand))
1380 if self.needs_free():
1381 conversions.append(FreeFunction(self.is_dynamic_array(), operand))
1388 def is_dynamic_array(self):
1389 """ Returns if the member is an array element.
1390 Vulkan uses this for dynamically sized arrays for which
1391 there is a 'count' parameter.
1393 return self.dyn_array_len is not None
1395 def is_handle(self):
1396 return self.type_info["category"] == "handle"
1398 def is_pointer(self):
1399 return self.pointer is not None
1401 def is_static_array(self):
1402 """ Returns if the member is an array.
1403 Vulkan uses this often for fixed size arrays in which the
1404 length is part of the member.
1406 return self.array_len is not None
1408 def is_struct(self):
1409 return self.type_info["category"] == "struct"
1411 def is_struct_forward_declaration(self):
1412 return self.struct_fwd_decl
1415 return self.type_info["category"] == "union"
1417 def is_generic_handle(self):
1418 """ Returns True if the member is a unit64_t containing
1419 a handle with a separate object type
1422 return self.object_type != None and self.type == "uint64_t"
1424 def is_bit_field(self):
1425 return self.bit_width is not None
1427 def needs_alignment(self):
1428 """ Check if this member needs alignment for 64-bit data.
1429 Various structures need alignment on 64-bit variables due
1430 to compiler differences on 32-bit between Win32 and Linux.
1433 if self.is_pointer():
1435 elif self.type == "size_t":
1437 elif self.type in ["uint64_t", "VkDeviceSize"]:
1439 elif self.is_struct():
1440 struct = self.type_info["data"]
1441 return struct.needs_alignment()
1442 elif self.is_handle():
1443 # Dispatchable handles are pointers to objects, while
1444 # non-dispatchable are uint64_t and hence need alignment.
1445 handle = self.type_info["data"]
1446 return False if handle.is_dispatchable() else True
1449 def needs_conversion(self):
1450 """ Structures requiring alignment, need conversion between win32 and host. """
1452 if not self.is_struct():
1455 struct = self.type_info["data"]
1456 return struct.needs_conversion()
1458 def needs_unwrapping(self):
1459 """ Structures with wrapped handles need unwrapping. """
1461 if self.is_struct():
1462 struct = self.type_info["data"]
1463 return struct.needs_unwrapping()
1465 if self.is_handle():
1466 handle = self.type_info["data"]
1467 return handle.is_wrapped()
1469 if self.is_generic_handle():
1474 def needs_free(self):
1475 if not self.needs_conversion() and not self.needs_unwrapping():
1478 if self.is_dynamic_array():
1481 # TODO: some non-pointer structs and optional pointer structs may need freeing,
1482 # though none of this type have been encountered yet.
1485 def needs_struct_extensions_conversion(self):
1486 if not self.is_struct():
1489 struct = self.type_info["data"]
1490 return struct.needs_struct_extensions_conversion()
1492 def set_type_info(self, type_info):
1493 """ Helper function to set type information from the type registry.
1494 This is needed, because not all type data is available at time of
1497 self.type_info = type_info
1500 class VkParam(object):
1501 """ Helper class which describes a parameter to a function call. """
1503 def __init__(self, type_info, const=None, pointer=None, name=None, array_len=None, dyn_array_len=None, object_type=None):
1506 self.array_len = array_len
1507 self.dyn_array_len = dyn_array_len
1508 self.pointer = pointer
1509 self.object_type = object_type
1510 self.type_info = type_info
1511 self.type = type_info["name"] # For convenience
1512 self.handle = type_info["data"] if type_info["category"] == "handle" else None
1513 self.struct = type_info["data"] if type_info["category"] == "struct" else None
1515 self._set_direction()
1516 self._set_format_string()
1517 self._set_conversions()
1520 return "{0} {1} {2} {3} {4} {5}".format(self.const, self.type, self.pointer, self.name, self.array_len, self.dyn_array_len)
1523 def from_xml(param, types):
1524 """ Helper function to create VkParam from xml. """
1526 # Parameter parsing is slightly tricky. All the data is contained within
1527 # a param tag, but some data is within subtags while others are text
1528 # before or after the type tag.
1530 # <param>const <type>char</type>* <name>pLayerName</name></param>
1532 name_elem = param.find("name")
1534 name = name_elem.text
1535 # Tail contains array length e.g. for blendConstants param of vkSetBlendConstants
1536 if name_elem.tail is not None:
1537 array_len = name_elem.tail.strip("[]")
1539 # Name of other parameter in function prototype, which stores the number of
1540 # elements pointed to be by this parameter.
1541 dyn_array_len = param.get("len", None)
1543 const = param.text.strip() if param.text else None
1544 type_elem = param.find("type")
1545 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1547 # Some uint64_t are actually handles with a separate type param
1548 object_type = param.get("objecttype", None)
1550 # Since we have parsed all types before hand, this should not happen.
1551 type_info = types.get(type_elem.text, None)
1552 if type_info is None:
1553 LOGGER.err("type info not found for: {0}".format(type_elem.text))
1555 return VkParam(type_info, const=const, pointer=pointer, name=name, array_len=array_len, dyn_array_len=dyn_array_len, object_type=object_type)
1557 def _set_conversions(self):
1558 """ Internal helper function to configure any needed conversion functions. """
1560 self.free_func = None
1561 self.input_conv = None
1562 self.output_conv = None
1563 if not self.needs_conversion() and not self.needs_unwrapping():
1566 operand = self.struct if self.is_struct() else self.handle
1568 # Input functions require win to host conversion.
1569 if self._direction in [Direction.INPUT, Direction.INPUT_OUTPUT]:
1570 self.input_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.INPUT, operand)
1572 # Output functions require host to win conversion.
1573 if self._direction in [Direction.INPUT_OUTPUT, Direction.OUTPUT]:
1574 self.output_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.OUTPUT, operand)
1576 # Dynamic arrays, but also some normal structs (e.g. VkCommandBufferBeginInfo) need memory
1577 # allocation and thus some cleanup.
1578 if self.is_dynamic_array() or self.struct.needs_free():
1579 self.free_func = FreeFunction(self.is_dynamic_array(), operand)
1581 def _set_direction(self):
1582 """ Internal helper function to set parameter direction (input/output/input_output). """
1584 # The parameter direction needs to be determined from hints in vk.xml like returnedonly,
1585 # parameter constness and other heuristics.
1586 # For now we need to get this right for structures as we need to convert these, we may have
1587 # missed a few other edge cases (e.g. count variables).
1588 # See also https://github.com/KhronosGroup/Vulkan-Docs/issues/610
1590 if not self.is_pointer():
1591 self._direction = Direction.INPUT
1592 elif self.is_const() and self.is_pointer():
1593 self._direction = Direction.INPUT
1594 elif self.is_struct():
1595 if not self.struct.returnedonly:
1596 self._direction = Direction.INPUT
1599 # Returnedonly hints towards output, however in some cases
1600 # it is inputoutput. In particular if pNext / sType exist,
1601 # which are used to link in other structures without having
1602 # to introduce new APIs. E.g. vkGetPhysicalDeviceProperties2KHR.
1603 if "pNext" in self.struct:
1604 self._direction = Direction.INPUT_OUTPUT
1607 self._direction = Direction.OUTPUT
1609 # This should mostly be right. Count variables can be inout, but we don't care about these yet.
1610 self._direction = Direction.OUTPUT
1612 def _set_format_string(self):
1613 """ Internal helper function to be used by constructor to set format string. """
1615 # Determine a format string used by code generation for traces.
1616 # 64-bit types need a conversion function.
1617 self.format_conv = None
1618 if self.is_static_array() or self.is_pointer():
1619 self.format_str = "%p"
1621 if self.type_info["category"] in ["bitmask"]:
1622 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1623 if self.type_info["data"].type == "VkFlags64":
1624 self.format_str = "0x%s"
1625 self.format_conv = "wine_dbgstr_longlong({0})"
1627 self.format_str = "%#x"
1628 elif self.type_info["category"] in ["enum"]:
1629 self.format_str = "%#x"
1630 elif self.is_handle():
1631 # We use uint64_t for non-dispatchable handles as opposed to pointers
1632 # for dispatchable handles.
1633 if self.handle.is_dispatchable():
1634 self.format_str = "%p"
1636 self.format_str = "0x%s"
1637 self.format_conv = "wine_dbgstr_longlong({0})"
1638 elif self.type == "float":
1639 self.format_str = "%f"
1640 elif self.type == "int":
1641 self.format_str = "%d"
1642 elif self.type == "int32_t":
1643 self.format_str = "%d"
1644 elif self.type == "size_t":
1645 self.format_str = "0x%s"
1646 self.format_conv = "wine_dbgstr_longlong({0})"
1647 elif self.type in ["uint16_t", "uint32_t", "VkBool32"]:
1648 self.format_str = "%u"
1649 elif self.type in ["uint64_t", "VkDeviceAddress", "VkDeviceSize"]:
1650 self.format_str = "0x%s"
1651 self.format_conv = "wine_dbgstr_longlong({0})"
1652 elif self.type == "HANDLE":
1653 self.format_str = "%p"
1654 elif self.type in ["VisualID", "xcb_visualid_t", "RROutput", "zx_handle_t"]:
1655 # Don't care about specific types for non-Windows platforms.
1656 self.format_str = ""
1658 LOGGER.warn("Unhandled type: {0}".format(self.type_info))
1660 def copy(self, direction, prefix=""):
1661 if direction == Direction.INPUT:
1662 if self.is_dynamic_array():
1663 return " {1}_host = convert_{2}_array_win_to_host({0}{1}, {0}{3});\n".format(prefix, self.name, self.type, self.dyn_array_len)
1665 return " convert_{0}_win_to_host({1}{2}, &{2}_host);\n".format(self.type, prefix, self.name)
1667 if self.is_dynamic_array():
1668 LOGGER.error("Unimplemented output conversion for: {0}".format(self.name))
1670 return " convert_{0}_host_to_win(&{2}_host, {1}{2});\n".format(self.type, prefix, self.name)
1672 def definition(self, postfix=None, is_member=False):
1673 """ Return prototype for the parameter. E.g. 'const char *foo' """
1677 proto += self.const + " "
1681 if self.is_pointer():
1682 proto += " {0}{1}".format(self.pointer, self.name)
1683 elif is_member and self.is_static_array():
1684 proto += " *" + self.name
1686 proto += " " + self.name
1688 # Allows appending something to the variable name useful for
1689 # win32 to host conversion.
1690 if postfix is not None:
1693 if not is_member and self.is_static_array():
1694 proto += "[{0}]".format(self.array_len)
1698 def direction(self):
1699 """ Returns parameter direction: input, output, input_output.
1701 Parameter direction in Vulkan is not straight-forward, which this function determines.
1704 return self._direction
1706 def dispatch_table(self, params_prefix=""):
1707 """ Return functions dispatch table pointer for dispatchable objects. """
1709 if not self.is_dispatchable():
1712 return self.handle.dispatch_table(params_prefix + self.name)
1714 def format_string(self):
1715 return self.format_str
1717 def free(self, prefix=""):
1718 if self.is_dynamic_array():
1719 if self.is_struct() and self.struct.returnedonly:
1720 # For returnedonly, counts is stored in a pointer.
1721 return " free_{0}_array({1}_host, *{2}{3});\n".format(self.type, self.name, prefix, self.dyn_array_len)
1723 return " free_{0}_array({1}_host, {2}{3});\n".format(self.type, self.name, prefix, self.dyn_array_len)
1725 # We are operating on a single structure. Some structs (very rare) contain dynamic members,
1726 # which would need freeing.
1727 if self.is_struct() and self.struct.needs_free():
1728 return " free_{0}(&{1}_host);\n".format(self.type, self.name)
1731 def get_conversions(self):
1732 """ Get a list of conversions required for this parameter if any.
1733 Parameters which are structures may require conversion between win32
1734 and the host platform. This function returns a list of conversions
1738 if self.is_struct():
1739 self.struct.needs_struct_extensions_conversion()
1740 for m in self.struct:
1741 m.needs_struct_extensions_conversion()
1742 elif not self.is_handle():
1745 if not self.needs_conversion() and not self.needs_unwrapping():
1750 # Collect any member conversions first, so we can guarantee
1751 # those functions will be defined prior to usage by the
1752 # 'parent' param requiring conversion.
1753 if self.is_struct():
1754 for m in self.struct:
1755 if not m.is_struct():
1758 if not m.needs_conversion() and not m.needs_unwrapping():
1761 conversions.extend(m.get_conversions())
1763 # Conversion requirements for the 'parent' parameter.
1764 if self.input_conv is not None:
1765 conversions.append(self.input_conv)
1766 if self.output_conv is not None:
1767 conversions.append(self.output_conv)
1768 if self.free_func is not None:
1769 conversions.append(self.free_func)
1774 return self.const is not None
1776 def is_dynamic_array(self):
1777 return self.dyn_array_len is not None
1779 def is_dispatchable(self):
1780 if not self.is_handle():
1783 return self.handle.is_dispatchable()
1785 def is_handle(self):
1786 return self.handle is not None
1788 def is_pointer(self):
1789 return self.pointer is not None
1791 def is_static_array(self):
1792 return self.array_len is not None
1794 def is_struct(self):
1795 return self.struct is not None
1797 def needs_conversion(self):
1798 """ Returns if parameter needs conversion between win32 and host. """
1800 if not self.is_struct():
1803 # VkSparseImageMemoryRequirements(2) is used by vkGetImageSparseMemoryRequirements(2).
1804 # This function is tricky to wrap, because how to wrap depends on whether
1805 # pSparseMemoryRequirements is NULL or not. Luckily for VkSparseImageMemoryRequirements(2)
1806 # the alignment works out in such a way that no conversion is needed between win32 and Linux.
1807 if self.type in ["VkSparseImageMemoryRequirements", "VkSparseImageMemoryRequirements2"]:
1810 # If a structure needs alignment changes, it means we need to
1811 # perform parameter conversion between win32 and host.
1812 if self.struct.needs_conversion():
1817 def needs_unwrapping(self):
1818 """ Returns if parameter needs unwrapping of handle. """
1820 # Wrapped handle parameters are handled separately, only look for wrapped handles in structs
1821 if self.is_struct():
1822 return self.struct.needs_unwrapping()
1824 if self.is_handle() and self.is_dynamic_array():
1825 return self.handle.needs_unwrapping()
1829 def needs_free(self):
1830 return self.free_func is not None
1832 def needs_input_conversion(self):
1833 return self.input_conv is not None
1835 def needs_output_conversion(self):
1836 return self.output_conv is not None
1839 """ Generate spec file entry for this parameter. """
1841 if self.is_pointer() and self.type == "char":
1843 if self.is_dispatchable() or self.is_pointer() or self.is_static_array():
1845 if self.type_info["category"] in ["bitmask"]:
1846 # Since 1.2.170 bitmasks can be 32 or 64-bit, check the basetype.
1847 if self.type_info["data"].type == "VkFlags64":
1851 if self.type_info["category"] in ["enum"]:
1853 if self.is_handle() and not self.is_dispatchable():
1855 if self.type == "float":
1857 if self.type in ["int", "int32_t", "size_t", "uint16_t", "uint32_t", "VkBool32"]:
1859 if self.type in ["uint64_t", "VkDeviceSize"]:
1862 LOGGER.error("Unhandled spec conversion for type: {0}".format(self.type))
1864 def variable(self, conv=False, params_prefix=""):
1865 """ Returns 'glue' code during generation of a function call on how to access the variable.
1866 This function handles various scenarios such as 'unwrapping' if dispatchable objects and
1867 renaming of parameters in case of win32 -> host conversion.
1870 conv (bool, optional): Enable conversion if the param needs it. This appends '_host' to the name.
1873 # Hack until we enable allocation callbacks from ICD to application. These are a joy
1874 # to enable one day, because of calling convention conversion.
1875 if "VkAllocationCallbacks" in self.type:
1876 LOGGER.debug("TODO: setting NULL VkAllocationCallbacks for {0}".format(self.name))
1879 if self.needs_unwrapping() or (conv and self.needs_conversion()):
1880 if self.is_dynamic_array():
1881 return "{0}_host".format(self.name)
1883 return "&{0}_host".format(self.name)
1885 if self.object_type != None and self.type == "uint64_t":
1886 return "wine_vk_unwrap_handle({0}{1}, {0}{2})".format(params_prefix, self.object_type, self.name)
1888 # We need to pass the native handle to the native Vulkan calls and
1889 # the wine driver's handle to calls which are wrapped by the driver.
1890 p = "{0}{1}".format(params_prefix, self.name)
1891 driver_handle = self.handle.driver_handle(p) if self.is_handle() else None
1892 return driver_handle if driver_handle else p
1895 class VkStruct(Sequence):
1896 """ Class which represents the type union and struct. """
1898 def __init__(self, name, members, returnedonly, structextends, alias=None, union=False):
1900 self.members = members
1901 self.returnedonly = returnedonly
1902 self.structextends = structextends
1903 self.required = False
1906 self.type_info = None # To be set later.
1907 self.struct_extensions = []
1908 self.aliased_by = []
1910 def __getitem__(self, i):
1911 return self.members[i]
1914 return len(self.members)
1917 def from_alias(struct, alias):
1918 name = struct.attrib.get("name")
1919 aliasee = VkStruct(name, alias.members, alias.returnedonly, alias.structextends, alias=alias)
1921 alias.add_aliased_by(aliasee)
1925 def from_xml(struct):
1926 # Unions and structs are the same parsing wise, but we need to
1927 # know which one we are dealing with later on for code generation.
1928 union = True if struct.attrib["category"] == "union" else False
1930 name = struct.attrib.get("name")
1932 # 'Output' structures for which data is filled in by the API are
1933 # marked as 'returnedonly'.
1934 returnedonly = True if struct.attrib.get("returnedonly") else False
1936 structextends = struct.attrib.get("structextends")
1937 structextends = structextends.split(",") if structextends else []
1940 for member in struct.findall("member"):
1941 vk_member = VkMember.from_xml(member)
1942 members.append(vk_member)
1944 return VkStruct(name, members, returnedonly, structextends, union=union)
1947 def decouple_structs(structs):
1948 """ Helper function which decouples a list of structs.
1949 Structures often depend on other structures. To make the C compiler
1950 happy we need to define 'substructures' first. This function analyzes
1951 the list of structures and reorders them in such a way that they are
1955 tmp_structs = list(structs) # Don't modify the original structures.
1956 decoupled_structs = []
1958 while (len(tmp_structs) > 0):
1959 # Iterate over a copy because we want to modify the list inside the loop.
1960 for struct in list(tmp_structs):
1963 if not struct.required:
1964 tmp_structs.remove(struct)
1968 if not (m.is_struct() or m.is_union()):
1971 # VkBaseInstructure and VkBaseOutStructure reference themselves.
1972 if m.type == struct.name:
1976 # Check if a struct we depend on has already been defined.
1977 for s in decoupled_structs:
1978 if s.name == m.type:
1983 # Check if the struct we depend on is even in the list of structs.
1984 # If found now, it means we haven't met all dependencies before we
1985 # can operate on the current struct.
1986 # When generating 'host' structs we may not be able to find a struct
1987 # as the list would only contain the structs requiring conversion.
1988 for s in tmp_structs:
1989 if s.name == m.type:
1993 if dependends == False:
1994 decoupled_structs.append(struct)
1995 tmp_structs.remove(struct)
1997 return decoupled_structs
1999 def definition(self, align=False, conv=False, postfix=None):
2000 """ Convert structure to textual definition.
2003 align (bool, optional): enable alignment to 64-bit for win32 struct compatibility.
2004 conv (bool, optional): enable struct conversion if the struct needs it.
2005 postfix (str, optional): text to append to end of struct name, useful for struct renaming.
2008 # Only define alias structs when doing conversions
2009 if self.is_alias() and not conv:
2013 text = "typedef union {0}".format(self.name)
2015 text = "typedef struct {0}".format(self.name)
2017 if postfix is not None:
2023 if align and m.needs_alignment():
2024 text += " {0};\n".format(m.definition(align=align))
2025 elif conv and m.needs_conversion():
2026 text += " {0};\n".format(m.definition(conv=conv))
2028 text += " {0};\n".format(m.definition())
2030 if postfix is not None:
2031 text += "}} {0}{1};\n".format(self.name, postfix)
2033 text += "}} {0};\n".format(self.name)
2035 for aliasee in self.aliased_by:
2036 text += "typedef {0} {1};\n".format(self.name, aliasee.name)
2041 return bool(self.alias)
2043 def add_aliased_by(self, aliasee):
2044 self.aliased_by.append(aliasee)
2046 def needs_alignment(self):
2047 """ Check if structure needs alignment for 64-bit data.
2048 Various structures need alignment on 64-bit variables due
2049 to compiler differences on 32-bit between Win32 and Linux.
2052 for m in self.members:
2053 if self.name == m.type:
2055 if m.needs_alignment():
2059 def needs_conversion(self):
2060 """ Returns if struct members needs conversion between win32 and host.
2061 Structures need conversion if they contain members requiring alignment
2062 or if they include other structures which need alignment.
2065 if self.needs_alignment():
2068 for m in self.members:
2069 if self.name == m.type:
2071 if m.needs_conversion():
2075 def needs_unwrapping(self):
2076 """ Returns if struct members need unwrapping of handle. """
2078 for m in self.members:
2079 if self.name == m.type:
2081 if m.needs_unwrapping():
2085 def needs_free(self):
2086 """ Check if any struct member needs some memory freeing."""
2088 for m in self.members:
2089 if self.name == m.type:
2096 def needs_struct_extensions_conversion(self):
2097 """ Checks if structure extensions in pNext chain need conversion. """
2100 for e in self.struct_extensions:
2101 if e.required and e.needs_conversion():
2102 LOGGER.error("Unhandled pNext chain alignment conversion for {0}".format(e.name))
2104 if e.required and e.needs_unwrapping():
2105 LOGGER.error("Unhandled pNext chain unwrapping conversion for {0}".format(e.name))
2110 def set_type_info(self, types):
2111 """ Helper function to set type information from the type registry.
2112 This is needed, because not all type data is available at time of
2115 for m in self.members:
2116 type_info = types[m.type]
2117 m.set_type_info(type_info)
2120 class ConversionFunction(object):
2121 def __init__(self, array, dyn_array, direction, operand):
2123 self.direction = direction
2124 self.dyn_array = dyn_array
2125 self.operand = operand
2126 self.type = operand.name
2130 def __eq__(self, other):
2131 return self.name == other.name
2133 def _generate_array_conversion_func(self):
2134 """ Helper function for generating a conversion function for array operands. """
2138 if self.operand.needs_conversion():
2139 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2141 if self.direction == Direction.OUTPUT:
2142 params = ["const {0}_host *in".format(self.type), "uint32_t count"]
2143 return_type = self.type
2145 params = ["const {0} *in".format(self.type), "uint32_t count"]
2146 return_type = "{0}_host".format(self.type)
2148 # Generate function prototype.
2149 body += "static inline {0} *{1}(".format(return_type, self.name)
2150 body += ", ".join(p for p in params)
2153 body += " {0} *out;\n".format(return_type)
2155 if self.operand.needs_unwrapping():
2156 if self.operand.needs_conversion():
2159 params = ["const {0} *in".format(self.type), "uint32_t count"]
2160 return_type = "{0}".format(self.type)
2162 # Generate function prototype.
2163 body += "static inline {0} *{1}(".format(return_type, self.name)
2164 body += ", ".join(p for p in params)
2167 body += " {0} *out;\n".format(return_type)
2169 if self.operand.needs_conversion():
2170 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2172 body += " unsigned int i;\n\n"
2173 body += " if (!in || !count) return NULL;\n\n"
2175 body += " out = malloc(count * sizeof(*out));\n"
2177 body += " for (i = 0; i < count; i++)\n"
2180 if isinstance(self.operand, VkStruct):
2181 for m in self.operand:
2182 # TODO: support copying of pNext extension structures!
2183 # Luckily though no extension struct at this point needs conversion.
2184 convert = m.copy("in[i].", "out[i].", self.direction, conv=True)
2185 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2186 body += " " + convert
2188 unwrap = m.copy("in[i].", "out[i].", self.direction, conv=False)
2189 if unwrap == convert:
2190 body += " " + unwrap
2192 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2193 body += " " + convert
2195 body += " " + unwrap
2196 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2198 elif isinstance(self.operand, VkHandle) and self.direction == Direction.INPUT:
2199 body += " out[i] = " + self.operand.driver_handle("in[i]") + ";\n"
2201 LOGGER.warn("Unhandled conversion operand type")
2202 body += " out[i] = in[i];\n"
2205 body += " return out;\n"
2208 if not self.operand.needs_unwrapping():
2209 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2215 def _generate_conversion_func(self):
2216 """ Helper function for generating a conversion function for non-array operands. """
2218 # It doesn't make sense to generate conversion functions for non-struct variables
2219 # which aren't in arrays, as this should be handled by the copy() function
2220 if not isinstance(self.operand, VkStruct):
2225 if self.operand.needs_conversion():
2226 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2227 body += "static inline void {0}(".format(self.name)
2229 if self.direction == Direction.OUTPUT:
2230 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type)]
2232 params = ["const {0} *in".format(self.type), "{0}_host *out".format(self.type)]
2234 # Generate parameter list
2235 body += ", ".join(p for p in params)
2238 if self.operand.needs_unwrapping():
2239 if self.operand.needs_conversion():
2242 body += "static inline void {0}(".format(self.name)
2244 params = ["const {0} *in".format(self.type), "{0} *out".format(self.type)]
2246 # Generate parameter list
2247 body += ", ".join(p for p in params)
2250 if self.operand.needs_conversion():
2251 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2253 body += "{\n if (!in) return;\n\n"
2255 if self.direction == Direction.INPUT and "pNext" in self.operand and self.operand.returnedonly:
2256 # We are dealing with an input_output parameter. For these we only need to copy
2257 # pNext and sType as the other fields are filled in by the host. We do potentially
2258 # have to iterate over pNext and perform conversions based on switch(sType)!
2259 # Luckily though no extension structs at this point need conversion.
2260 # TODO: support copying of pNext extension structures!
2261 body += " out->pNext = in->pNext;\n"
2262 body += " out->sType = in->sType;\n"
2264 for m in self.operand:
2265 # TODO: support copying of pNext extension structures!
2266 convert = m.copy("in->", "out->", self.direction, conv=True)
2267 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2268 body += " " + convert
2270 unwrap = m.copy("in->", "out->", self.direction, conv=False)
2271 if unwrap == convert:
2272 body += " " + unwrap
2274 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2275 body += " " + convert
2277 body += " " + unwrap
2278 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2282 if not self.operand.needs_unwrapping():
2283 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2289 def _generate_static_array_conversion_func(self):
2290 """ Helper function for generating a conversion function for array operands. """
2294 if self.operand.needs_conversion():
2295 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2297 if self.direction == Direction.OUTPUT:
2298 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
2300 params = ["const {0} *in".format(self.type), "{0} *out_host".format(self.type), "uint32_t count"]
2302 # Generate function prototype.
2303 body += "static inline void {0}(".format(self.name)
2304 body += ", ".join(p for p in params)
2307 if self.operand.needs_unwrapping():
2308 if self.operand.needs_conversion():
2311 params = ["const {0} *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
2313 # Generate function prototype.
2314 body += "static inline void {0}(".format(self.name)
2315 body += ", ".join(p for p in params)
2319 body += " unsigned int i;\n\n"
2320 body += " if (!in) return;\n\n"
2321 body += " for (i = 0; i < count; i++)\n"
2324 if isinstance(self.operand, VkStruct):
2325 for m in self.operand:
2326 # TODO: support copying of pNext extension structures!
2327 convert = m.copy("in[i].", "out[i].", self.direction, conv=True)
2328 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2329 body += " " + convert
2331 unwrap = m.copy("in[i].", "out[i].", self.direction, conv=False)
2332 if unwrap == convert:
2333 body += " " + unwrap
2335 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2336 body += " " + convert
2338 body += " " + unwrap
2339 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2340 elif isinstance(self.operand, VkHandle) and self.direction == Direction.INPUT:
2341 body += " out[i] = " + self.operand.driver_handle("in[i]") + ";\n"
2343 LOGGER.warn("Unhandled conversion operand type")
2344 body += " out[i] = in[i];\n"
2349 if not self.operand.needs_unwrapping():
2350 body += "#endif /* USE_STRUCT_CONVERSION) */\n"
2356 def _set_name(self):
2357 if self.direction == Direction.INPUT:
2359 name = "convert_{0}_static_array_win_to_host".format(self.type)
2360 elif self.dyn_array:
2361 name = "convert_{0}_array_win_to_host".format(self.type)
2363 name = "convert_{0}_win_to_host".format(self.type)
2364 else: # Direction.OUTPUT
2366 name = "convert_{0}_static_array_host_to_win".format(self.type)
2367 elif self.dyn_array:
2368 name = "convert_{0}_array_host_to_win".format(self.type)
2370 name = "convert_{0}_host_to_win".format(self.type)
2374 def definition(self):
2376 return self._generate_static_array_conversion_func()
2377 elif self.dyn_array:
2378 return self._generate_array_conversion_func()
2380 return self._generate_conversion_func()
2383 class FreeFunction(object):
2384 def __init__(self, dyn_array, operand):
2385 self.dyn_array = dyn_array
2386 self.operand = operand
2387 self.type = operand.name
2390 self.name = "free_{0}_array".format(self.type)
2392 self.name = "free_{0}".format(self.type)
2394 def __eq__(self, other):
2395 return self.name == other.name
2397 def _generate_array_free_func(self):
2398 """ Helper function for cleaning up temporary buffers required for array conversions. """
2402 if self.operand.needs_conversion():
2403 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2404 # Generate function prototype.
2405 body += "static inline void {0}({1}_host *in, uint32_t count)\n".format(self.name, self.type)
2407 if self.operand.needs_unwrapping():
2408 if self.operand.needs_conversion():
2411 # Generate function prototype.
2412 body += "static inline void {0}({1} *in, uint32_t count)\n".format(self.name, self.type)
2414 if self.operand.needs_conversion():
2415 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2419 # E.g. VkGraphicsPipelineCreateInfo_host needs freeing for pStages.
2420 if isinstance(self.operand, VkStruct) and self.operand.needs_free():
2421 body += " unsigned int i;\n\n"
2422 body += " if (!in) return;\n\n"
2423 body += " for (i = 0; i < count; i++)\n"
2426 for m in self.operand:
2428 convert = m.free("in[i].", conv=True)
2429 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2430 body += " " + convert
2432 unwrap = m.free("in[i].", conv=False)
2433 if convert == unwrap:
2434 body += " " + unwrap
2436 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2437 body += " " + convert
2438 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2440 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2441 body += " " + convert
2443 body += " " + unwrap
2444 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2447 body += " if (!in) return;\n\n"
2449 body += " free(in);\n"
2453 if not self.operand.needs_unwrapping():
2454 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2460 def _generate_free_func(self):
2461 # E.g. VkCommandBufferBeginInfo.pInheritanceInfo needs freeing.
2462 if not self.operand.needs_free():
2465 if not isinstance(self.operand, VkStruct):
2470 if self.operand.needs_conversion():
2471 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2472 # Generate function prototype.
2473 body += "static inline void {0}({1}_host *in)\n".format(self.name, self.type)
2475 if self.operand.needs_unwrapping():
2476 if self.operand.needs_conversion():
2479 # Generate function prototype.
2480 body += "static inline void {0}({1} *in)\n".format(self.name, self.type)
2482 if self.operand.needs_conversion():
2483 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2487 for m in self.operand:
2489 convert = m.free("in->", conv=True)
2490 if self.operand.needs_conversion() and not self.operand.needs_unwrapping():
2491 body += " " + convert
2493 unwrap = m.free("in->", conv=False)
2494 if convert == unwrap:
2495 body += " " + unwrap
2497 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2498 body += " " + convert
2499 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2501 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2502 body += " " + convert
2504 body += " " + unwrap
2505 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2509 if not self.operand.needs_unwrapping():
2510 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2516 def definition(self):
2518 return self._generate_array_free_func()
2520 # Some structures need freeing too if they contain dynamic arrays.
2521 # E.g. VkCommandBufferBeginInfo
2522 return self._generate_free_func()
2525 class StructChainConversionFunction(object):
2526 def __init__(self, direction, struct, ignores):
2527 self.direction = direction
2528 self.struct = struct
2529 self.ignores = ignores
2530 self.type = struct.name
2532 self.name = "convert_{0}_struct_chain".format(self.type)
2534 def __eq__(self, other):
2535 return self.name == other.name
2537 def prototype(self, postfix=""):
2538 return "VkResult {0}(const void *pNext, {1} *out_struct) {2}".format(self.name, self.type, postfix).strip()
2540 def definition(self):
2541 body = self.prototype()
2544 body += " VkBaseOutStructure *out_header = (VkBaseOutStructure *)out_struct;\n";
2545 body += " const VkBaseInStructure *in_header;\n\n";
2547 body += " out_header->pNext = NULL;\n\n"
2549 body += " for (in_header = pNext; in_header; in_header = in_header->pNext)\n"
2551 body += " switch (in_header->sType)\n"
2554 for i in self.ignores:
2555 body += " case {0}:\n".format(i)
2556 body += " break;\n\n"
2558 for e in self.struct.struct_extensions:
2562 stype = next(x for x in e.members if x.name == "sType")
2564 if stype.values in self.ignores:
2567 body += " case {0}:\n".format(stype.values)
2570 body += " const {0} *in = (const {0} *)in_header;\n".format(e.name)
2571 body += " {0} *out;\n\n".format(e.name)
2573 body += " if (!(out = malloc(sizeof(*out)))) goto out_of_memory;\n\n"
2576 if m.name == "pNext":
2577 body += " out->pNext = NULL;\n"
2579 convert = m.copy("in->", "out->", self.direction, conv=True)
2580 unwrap = m.copy("in->", "out->", self.direction, conv=False)
2581 if unwrap == convert:
2582 body += " " + unwrap
2584 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2585 body += " " + convert
2587 body += " " + unwrap
2588 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2590 body += "\n out_header->pNext = (VkBaseOutStructure *)out;\n"
2591 body += " out_header = out_header->pNext;\n"
2595 body += " default:\n"
2596 body += " FIXME(\"Application requested a linked structure of type %u.\\n\", in_header->sType);\n"
2601 body += " return VK_SUCCESS;\n"
2603 if any(x for x in self.struct.struct_extensions if x.required):
2604 body += "\nout_of_memory:\n"
2605 body += " free_{0}_struct_chain(out_struct);\n".format(self.type)
2606 body += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
2611 class FreeStructChainFunction(object):
2612 def __init__(self, struct):
2613 self.struct = struct
2614 self.type = struct.name
2616 self.name = "free_{0}_struct_chain".format(self.type)
2618 def __eq__(self, other):
2619 return self.name == other.name
2621 def prototype(self, postfix=""):
2622 return "void {0}({1} *s) {2}".format(self.name, self.type, postfix).strip()
2624 def definition(self):
2625 body = self.prototype()
2628 body += " VkBaseOutStructure *header = (void *)s->pNext;\n\n";
2630 body += " while (header)\n"
2632 body += " void *prev = header;\n\n"
2633 body += " switch (header->sType)\n"
2636 for e in self.struct.struct_extensions:
2640 if not any(m.needs_free() for m in e):
2643 stype = next(x for x in e.members if x.name == "sType")
2645 body += " case {0}:\n".format(stype.values)
2647 body += " {0} *structure = ({0} *) header;\n".format(e.name)
2651 convert = m.free("structure->", conv=True)
2652 unwrap = m.free("structure->", conv=False)
2653 if convert == unwrap:
2654 body += " " + unwrap
2656 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2657 body += " " + convert
2658 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2660 body += "#if defined(USE_STRUCT_CONVERSION)\n"
2661 body += " " + convert
2663 body += " " + unwrap
2664 body += "#endif /* USE_STRUCT_CONVERSION */\n"
2669 body += " default:\n"
2673 body += " header = header->pNext;\n"
2674 body += " free(prev);\n"
2677 body += " s->pNext = NULL;\n"
2683 class VkGenerator(object):
2684 def __init__(self, registry):
2685 self.registry = registry
2687 # Build a list conversion functions for struct conversion.
2688 self.conversions = []
2689 self.struct_chain_conversions = []
2690 self.host_structs = []
2691 for func in self.registry.funcs.values():
2692 if not func.is_required():
2695 if not func.needs_conversion() and not func.needs_unwrapping():
2698 conversions = func.get_conversions()
2699 for conv in conversions:
2700 # Pull in any conversions for vulkan_thunks.c.
2701 if func.needs_thunk():
2702 # Append if we don't already have this conversion.
2703 if not any(c == conv for c in self.conversions):
2704 self.conversions.append(conv)
2706 if not isinstance(conv.operand, VkStruct):
2709 # Structs can be used in different ways by different conversions
2710 # e.g. array vs non-array. Just make sure we pull in each struct once.
2711 if not any(s.name == conv.operand.name for s in self.host_structs):
2712 self.host_structs.append(conv.operand)
2714 for struct in self.registry.structs:
2715 if struct.name in STRUCT_CHAIN_CONVERSIONS:
2716 self.struct_chain_conversions.append(StructChainConversionFunction(Direction.INPUT, struct, STRUCT_CHAIN_CONVERSIONS[struct.name]))
2717 self.struct_chain_conversions.append(FreeStructChainFunction(struct))
2718 # Once we decide to support pNext chains conversion everywhere, move this under get_conversions
2719 for e in struct.struct_extensions:
2721 if m.needs_conversion() or m.needs_unwrapping():
2722 conversions = m.get_conversions()
2723 for conv in conversions:
2724 if not any(c == conv for c in self.conversions):
2725 self.conversions.append(conv)
2727 def _generate_copyright(self, f, spec_file=False):
2728 f.write("# " if spec_file else "/* ")
2729 f.write("Automatically generated from Vulkan vk.xml; DO NOT EDIT!\n")
2730 lines = ["", "This file is generated from Vulkan vk.xml file covered",
2731 "by the following copyright and permission notice:"]
2732 lines.extend([l.rstrip(" ") for l in self.registry.copyright.splitlines()])
2734 f.write("{0}{1}".format("# " if spec_file else " * ", line).rstrip(" ") + "\n")
2735 f.write("\n" if spec_file else " */\n\n")
2737 def generate_thunks_c(self, f, prefix):
2738 self._generate_copyright(f)
2741 f.write("#pragma makedep unix\n")
2742 f.write("#endif\n\n")
2744 f.write("#include \"config.h\"\n\n")
2746 f.write("#include <stdlib.h>\n\n")
2748 f.write("#include \"vulkan_private.h\"\n\n")
2750 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2752 # Generate any conversion helper functions.
2753 for conv in self.conversions:
2754 f.write(conv.definition())
2756 for conv in self.struct_chain_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.loader_thunk_type == ThunkType.NONE:
2767 if vk_func.needs_private_thunk():
2768 f.write(vk_func.thunk(prefix="thunk_"))
2770 if vk_func.thunk_type == ThunkType.PUBLIC:
2772 f.write(vk_func.thunk(prefix=prefix))
2774 # Create array of device extensions.
2775 f.write("static const char * const vk_device_extensions[] =\n{\n")
2776 for ext in self.registry.extensions:
2777 if ext["type"] != "device":
2779 if ext["name"] in UNEXPOSED_EXTENSIONS:
2782 f.write(" \"{0}\",\n".format(ext["name"]))
2785 # Create array of instance extensions.
2786 f.write("static const char * const vk_instance_extensions[] =\n{\n")
2787 for ext in self.registry.extensions:
2788 if ext["type"] != "instance":
2790 if ext["name"] in UNEXPOSED_EXTENSIONS:
2793 f.write(" \"{0}\",\n".format(ext["name"]))
2796 f.write("BOOL wine_vk_device_extension_supported(const char *name)\n")
2798 f.write(" unsigned int i;\n")
2799 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_extensions); i++)\n")
2801 f.write(" if (strcmp(vk_device_extensions[i], name) == 0)\n")
2802 f.write(" return TRUE;\n")
2804 f.write(" return FALSE;\n")
2807 f.write("BOOL wine_vk_instance_extension_supported(const char *name)\n")
2809 f.write(" unsigned int i;\n")
2810 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_extensions); i++)\n")
2812 f.write(" if (strcmp(vk_instance_extensions[i], name) == 0)\n")
2813 f.write(" return TRUE;\n")
2815 f.write(" return FALSE;\n")
2818 f.write("BOOL wine_vk_is_type_wrapped(VkObjectType type)\n")
2820 f.write(" return FALSE")
2821 for handle in self.registry.handles:
2822 if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2824 f.write(" ||\n type == {}".format(handle.object_type))
2828 f.write("uint64_t wine_vk_unwrap_handle(VkObjectType type, uint64_t handle)\n")
2830 f.write(" switch(type)\n")
2832 for handle in self.registry.handles:
2833 if not handle.is_required() or not handle.is_wrapped() or handle.is_alias():
2835 f.write(" case {}:\n".format(handle.object_type))
2836 if handle.is_dispatchable():
2837 f.write(" return (uint64_t) (uintptr_t) ")
2838 f.write(handle.native_handle("(({}) (uintptr_t) handle)".format(handle.name)))
2840 f.write(" return (uint64_t) ")
2841 f.write(handle.native_handle("handle"))
2843 f.write(" default:\n")
2844 f.write(" return handle;\n")
2848 f.write("const unixlib_entry_t __wine_unix_call_funcs[] =\n")
2850 f.write(" init_vulkan,\n")
2851 f.write(" vk_is_available_instance_function,\n")
2852 f.write(" vk_is_available_device_function,\n")
2853 for vk_func in self.registry.funcs.values():
2854 if not vk_func.needs_exposing():
2856 if vk_func.loader_thunk_type == ThunkType.NONE:
2859 f.write(" {1}{0},\n".format(vk_func.name, prefix))
2861 f.write("C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_count);\n\n")
2863 f.write("NTSTATUS WINAPI vk_direct_unix_call(unixlib_handle_t handle, unsigned int code, void *params)\n")
2865 f.write(" return __wine_unix_call_funcs[code](params);\n")
2868 def generate_thunks_h(self, f, prefix):
2869 self._generate_copyright(f)
2871 f.write("#ifndef __WINE_VULKAN_THUNKS_H\n")
2872 f.write("#define __WINE_VULKAN_THUNKS_H\n\n")
2874 f.write("#define WINE_VK_VERSION VK_API_VERSION_{0}_{1}\n\n".format(WINE_VK_VERSION[0], WINE_VK_VERSION[1]))
2876 # Generate prototypes for device and instance functions requiring a custom implementation.
2877 f.write("/* Functions for which we have custom implementations outside of the thunks. */\n")
2878 for vk_func in self.registry.funcs.values():
2879 if not vk_func.needs_exposing():
2881 if vk_func.needs_thunk() and not vk_func.needs_private_thunk():
2884 f.write("NTSTATUS {0}{1}(void *args) DECLSPEC_HIDDEN;\n".format(prefix, vk_func.name))
2887 f.write("/* Private thunks */\n")
2888 for vk_func in self.registry.funcs.values():
2889 if vk_func.needs_private_thunk():
2890 f.write("{0};\n".format(vk_func.prototype(prefix="thunk_", postfix="DECLSPEC_HIDDEN")))
2893 for struct in self.host_structs:
2894 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
2895 f.write(struct.definition(align=False, conv=True, postfix="_host"))
2897 f.write("typedef {0} {0}_host;\n".format(struct.name))
2898 f.write("#endif\n\n")
2901 for func in self.struct_chain_conversions:
2902 f.write(func.prototype(postfix="DECLSPEC_HIDDEN") + ";\n")
2905 f.write("/* For use by vkDevice and children */\n")
2906 f.write("struct vulkan_device_funcs\n{\n")
2907 for vk_func in self.registry.device_funcs:
2908 if not vk_func.is_required():
2911 if not vk_func.needs_dispatch():
2912 LOGGER.debug("skipping {0} in vulkan_device_funcs".format(vk_func.name))
2915 f.write(" {0};\n".format(vk_func.pfn(conv=vk_func.needs_conversion())))
2918 f.write("/* For use by vkInstance and children */\n")
2919 f.write("struct vulkan_instance_funcs\n{\n")
2920 for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs:
2921 if not vk_func.is_required():
2924 if not vk_func.needs_dispatch():
2925 LOGGER.debug("skipping {0} in vulkan_instance_funcs".format(vk_func.name))
2928 f.write(" {0};\n".format(vk_func.pfn(conv=vk_func.needs_conversion())))
2931 f.write("#define ALL_VK_DEVICE_FUNCS() \\\n")
2933 for vk_func in self.registry.device_funcs:
2934 if not vk_func.is_required():
2937 if not vk_func.needs_dispatch():
2938 LOGGER.debug("skipping {0} in ALL_VK_DEVICE_FUNCS".format(vk_func.name))
2942 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2945 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2948 f.write("#define ALL_VK_INSTANCE_FUNCS() \\\n")
2950 for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs:
2951 if not vk_func.is_required():
2954 if not vk_func.needs_dispatch():
2955 LOGGER.debug("skipping {0} in ALL_VK_INSTANCE_FUNCS".format(vk_func.name))
2959 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2962 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2965 f.write("#endif /* __WINE_VULKAN_THUNKS_H */\n")
2967 def generate_loader_thunks_c(self, f):
2968 self._generate_copyright(f)
2970 f.write("#include \"vulkan_loader.h\"\n\n")
2972 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
2974 for vk_func in self.registry.funcs.values():
2975 if not vk_func.needs_exposing():
2977 if vk_func.loader_thunk_type != ThunkType.PUBLIC:
2980 f.write(vk_func.loader_thunk())
2982 f.write("static const struct vulkan_func vk_device_dispatch_table[] =\n{\n")
2983 for vk_func in self.registry.device_funcs:
2984 if not vk_func.needs_exposing():
2987 f.write(" {{\"{0}\", {0}}},\n".format(vk_func.name))
2990 f.write("static const struct vulkan_func vk_phys_dev_dispatch_table[] =\n{\n")
2991 for vk_func in self.registry.phys_dev_funcs:
2992 if not vk_func.needs_exposing():
2995 f.write(" {{\"{0}\", {0}}},\n".format(vk_func.name))
2998 f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
2999 for vk_func in self.registry.instance_funcs:
3000 if not vk_func.needs_exposing():
3003 f.write(" {{\"{0}\", {0}}},\n".format(vk_func.name))
3006 f.write("void *wine_vk_get_device_proc_addr(const char *name)\n")
3008 f.write(" unsigned int i;\n")
3009 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_dispatch_table); i++)\n")
3011 f.write(" if (strcmp(vk_device_dispatch_table[i].name, name) == 0)\n")
3013 f.write(" TRACE(\"Found name=%s in device table\\n\", debugstr_a(name));\n")
3014 f.write(" return vk_device_dispatch_table[i].func;\n")
3017 f.write(" return NULL;\n")
3020 f.write("void *wine_vk_get_phys_dev_proc_addr(const char *name)\n")
3022 f.write(" unsigned int i;\n")
3023 f.write(" for (i = 0; i < ARRAY_SIZE(vk_phys_dev_dispatch_table); i++)\n")
3025 f.write(" if (strcmp(vk_phys_dev_dispatch_table[i].name, name) == 0)\n")
3027 f.write(" TRACE(\"Found name=%s in physical device table\\n\", debugstr_a(name));\n")
3028 f.write(" return vk_phys_dev_dispatch_table[i].func;\n")
3031 f.write(" return NULL;\n")
3034 f.write("void *wine_vk_get_instance_proc_addr(const char *name)\n")
3036 f.write(" unsigned int i;\n")
3037 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_dispatch_table); i++)\n")
3039 f.write(" if (strcmp(vk_instance_dispatch_table[i].name, name) == 0)\n")
3041 f.write(" TRACE(\"Found name=%s in instance table\\n\", debugstr_a(name));\n")
3042 f.write(" return vk_instance_dispatch_table[i].func;\n")
3045 f.write(" return NULL;\n")
3048 def generate_loader_thunks_h(self, f):
3049 self._generate_copyright(f)
3051 f.write("#ifndef __WINE_VULKAN_LOADER_THUNKS_H\n")
3052 f.write("#define __WINE_VULKAN_LOADER_THUNKS_H\n\n")
3054 f.write("enum unix_call\n")
3056 f.write(" unix_init,\n")
3057 f.write(" unix_is_available_instance_function,\n")
3058 f.write(" unix_is_available_device_function,\n")
3059 for vk_func in self.registry.funcs.values():
3060 if not vk_func.needs_exposing():
3062 if vk_func.loader_thunk_type == ThunkType.NONE:
3065 f.write(" unix_{0},\n".format(vk_func.name))
3066 f.write(" unix_count,\n")
3069 f.write("#include \"pshpack4.h\"\n\n")
3071 for vk_func in self.registry.funcs.values():
3072 if not vk_func.needs_exposing():
3074 if vk_func.loader_thunk_type == ThunkType.NONE:
3077 f.write("struct {0}_params\n".format(vk_func.name))
3079 for p in vk_func.params:
3080 f.write(" {0};\n".format(p.definition(is_member=True)))
3081 if vk_func.extra_param:
3082 f.write(" void *{0};\n".format(vk_func.extra_param))
3083 if vk_func.returns_longlong():
3084 f.write(" {0} result;\n".format(vk_func.type))
3087 f.write("#include \"poppack.h\"\n\n")
3088 f.write("#endif /* __WINE_VULKAN_LOADER_THUNKS_H */\n")
3090 def generate_vulkan_h(self, f):
3091 self._generate_copyright(f)
3092 f.write("#ifndef __WINE_VULKAN_H\n")
3093 f.write("#define __WINE_VULKAN_H\n\n")
3095 f.write("#include <windef.h>\n")
3096 f.write("#include <stdint.h>\n\n")
3098 f.write("/* Define WINE_VK_HOST to get 'host' headers. */\n")
3099 f.write("#ifdef WINE_VK_HOST\n")
3100 f.write("#define VKAPI_CALL\n")
3101 f.write('#define WINE_VK_ALIGN(x)\n')
3102 f.write("#endif\n\n")
3104 f.write("#ifndef VKAPI_CALL\n")
3105 f.write("#define VKAPI_CALL __stdcall\n")
3106 f.write("#endif\n\n")
3108 f.write("#ifndef VKAPI_PTR\n")
3109 f.write("#define VKAPI_PTR VKAPI_CALL\n")
3110 f.write("#endif\n\n")
3112 f.write("#ifndef WINE_VK_ALIGN\n")
3113 f.write("#define WINE_VK_ALIGN DECLSPEC_ALIGN\n")
3114 f.write("#endif\n\n")
3116 # The overall strategy is to define independent constants and datatypes,
3117 # prior to complex structures and function calls to avoid forward declarations.
3118 for const in self.registry.consts:
3119 # For now just generate things we may not need. The amount of parsing needed
3120 # to get some of the info is tricky as you need to figure out which structure
3121 # references a certain constant.
3122 f.write(const.definition())
3125 for define in self.registry.defines:
3126 f.write(define.definition())
3128 for handle in self.registry.handles:
3129 # For backward compatibility also create definitions for aliases.
3130 # These types normally don't get pulled in as we use the new types
3131 # even in legacy functions if they are aliases.
3132 if handle.is_required() or handle.is_alias():
3133 f.write(handle.definition())
3136 for base_type in self.registry.base_types:
3137 f.write(base_type.definition())
3140 for bitmask in self.registry.bitmasks:
3141 f.write(bitmask.definition())
3144 # Define enums, this includes values for some of the bitmask types as well.
3145 for enum in self.registry.enums.values():
3147 f.write(enum.definition())
3149 for fp in self.registry.funcpointers:
3151 f.write(fp.definition())
3154 # This generates both structures and unions. Since structures
3155 # may depend on other structures/unions, we need a list of
3156 # decoupled structs.
3157 # Note: unions are stored in structs for dependency reasons,
3158 # see comment in parsing section.
3159 structs = VkStruct.decouple_structs(self.registry.structs)
3160 for struct in structs:
3161 LOGGER.debug("Generating struct: {0}".format(struct.name))
3162 f.write(struct.definition(align=True))
3165 for func in self.registry.funcs.values():
3166 if not func.is_required():
3167 LOGGER.debug("Skipping PFN definition for: {0}".format(func.name))
3170 f.write("typedef {0};\n".format(func.pfn(prefix="PFN", call_conv="VKAPI_PTR")))
3173 f.write("#ifndef VK_NO_PROTOTYPES\n")
3174 for func in self.registry.funcs.values():
3175 if not func.is_required():
3176 LOGGER.debug("Skipping API definition for: {0}".format(func.name))
3179 LOGGER.debug("Generating API definition for: {0}".format(func.name))
3180 f.write("{0};\n".format(func.prototype(call_conv="VKAPI_CALL")))
3181 f.write("#endif /* VK_NO_PROTOTYPES */\n\n")
3183 f.write("#endif /* __WINE_VULKAN_H */\n")
3185 def generate_vulkan_driver_h(self, f):
3186 self._generate_copyright(f)
3187 f.write("#ifndef __WINE_VULKAN_DRIVER_H\n")
3188 f.write("#define __WINE_VULKAN_DRIVER_H\n\n")
3190 f.write("/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */\n")
3191 f.write("#define WINE_VULKAN_DRIVER_VERSION {0}\n\n".format(DRIVER_VERSION))
3193 f.write("struct vulkan_funcs\n{\n")
3194 f.write(" /* Vulkan global functions. These are the only calls at this point a graphics driver\n")
3195 f.write(" * needs to provide. Other function calls will be provided indirectly by dispatch\n")
3196 f.write(" * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice.\n")
3199 for vk_func in self.registry.funcs.values():
3200 if not vk_func.is_driver_func():
3204 # Avoid PFN_vkVoidFunction in driver interface as Vulkan likes to put calling convention
3205 # stuff in there. For simplicity substitute with "void *".
3206 pfn = pfn.replace("PFN_vkVoidFunction", "void *")
3207 f.write(" {0};\n".format(pfn))
3210 f.write(" /* winevulkan specific functions */\n")
3211 f.write(" VkSurfaceKHR (*p_wine_get_native_surface)(VkSurfaceKHR);\n")
3214 f.write("extern const struct vulkan_funcs * __wine_get_vulkan_driver(UINT version);\n\n")
3216 f.write("static inline void *get_vulkan_driver_device_proc_addr(\n")
3217 f.write(" const struct vulkan_funcs *vulkan_funcs, const char *name)\n{\n")
3218 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
3219 f.write(" name += 2;\n\n")
3220 for vk_func in self.registry.funcs.values():
3221 if vk_func.is_driver_func() and vk_func.is_device_func():
3222 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3223 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3225 f.write(" return NULL;\n}\n\n")
3227 f.write("static inline void *get_vulkan_driver_instance_proc_addr(\n")
3228 f.write(" const struct vulkan_funcs *vulkan_funcs, VkInstance instance, const char *name)\n{\n")
3229 f.write(" if (!name || name[0] != 'v' || name[1] != 'k') return NULL;\n\n")
3230 f.write(" name += 2;\n\n")
3231 for vk_func in self.registry.funcs.values():
3232 if vk_func.is_driver_func() and vk_func.is_global_func() and vk_func.name != "vkGetInstanceProcAddr":
3233 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3234 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3236 f.write(" if (!instance) return NULL;\n\n")
3237 for vk_func in self.registry.funcs.values():
3238 if vk_func.is_driver_func() and (vk_func.is_instance_func() or vk_func.is_phys_dev_func()):
3239 f.write(' if (!strcmp(name, "{0}"))\n'.format(vk_func.name[2:]))
3240 f.write(' return vulkan_funcs->p_{0};\n'.format(vk_func.name))
3242 f.write(" name -= 2;\n\n")
3243 f.write(" return get_vulkan_driver_device_proc_addr(vulkan_funcs, name);\n}\n\n")
3245 f.write("#endif /* __WINE_VULKAN_DRIVER_H */\n")
3247 def generate_vulkan_spec(self, f):
3248 self._generate_copyright(f, spec_file=True)
3249 f.write("@ stdcall -private vk_icdGetInstanceProcAddr(ptr str)\n")
3250 f.write("@ stdcall -private vk_icdGetPhysicalDeviceProcAddr(ptr str)\n")
3251 f.write("@ stdcall -private vk_icdNegotiateLoaderICDInterfaceVersion(ptr)\n")
3253 # Export symbols for all Vulkan Core functions.
3254 for func in self.registry.funcs.values():
3255 if not func.is_core_func():
3258 # We support all Core functions except for VK_KHR_display* APIs.
3259 # Create stubs for unsupported Core functions.
3260 if func.is_required():
3261 f.write(func.spec())
3263 f.write("@ stub {0}\n".format(func.name))
3265 f.write("@ stdcall -private DllRegisterServer()\n")
3266 f.write("@ stdcall -private DllUnregisterServer()\n")
3268 def generate_vulkan_loader_spec(self, f):
3269 self._generate_copyright(f, spec_file=True)
3271 # Export symbols for all Vulkan Core functions.
3272 for func in self.registry.funcs.values():
3273 if not func.is_core_func():
3276 # We support all Core functions except for VK_KHR_display* APIs.
3277 # Create stubs for unsupported Core functions.
3278 if func.is_required():
3279 f.write(func.spec(symbol="winevulkan." + func.name))
3281 f.write("@ stub {0}\n".format(func.name))
3284 class VkRegistry(object):
3285 def __init__(self, reg_filename):
3286 # Used for storage of type information.
3287 self.base_types = None
3288 self.bitmasks = None
3292 self.funcpointers = None
3296 # We aggregate all types in here for cross-referencing.
3300 self.version_regex = re.compile(
3309 # Overall strategy for parsing the registry is to first
3310 # parse all type / function definitions. Then parse
3311 # features and extensions to decide which types / functions
3312 # to actually 'pull in' for code generation. For each type or
3313 # function call we want we set a member 'required' to True.
3314 tree = ET.parse(reg_filename)
3315 root = tree.getroot()
3316 self._parse_enums(root)
3317 self._parse_types(root)
3318 self._parse_commands(root)
3320 # Pull in any required types and functions.
3321 self._parse_features(root)
3322 self._parse_extensions(root)
3324 for enum in self.enums.values():
3325 enum.fixup_64bit_aliases()
3327 self._match_object_types()
3329 self.copyright = root.find('./comment').text
3331 def _is_feature_supported(self, feature):
3332 version = self.version_regex.match(feature)
3336 version = tuple(map(int, version.group('major', 'minor')))
3337 return version <= WINE_VK_VERSION
3339 def _is_extension_supported(self, extension):
3340 # We disable some extensions as either we haven't implemented
3341 # support yet or because they are for platforms other than win32.
3342 return extension not in UNSUPPORTED_EXTENSIONS
3344 def _mark_command_required(self, command):
3345 """ Helper function to mark a certain command and the datatypes it needs as required."""
3346 def mark_bitmask_dependencies(bitmask, types):
3347 if bitmask.requires is not None:
3348 types[bitmask.requires]["data"].required = True
3350 def mark_funcpointer_dependencies(fp, types):
3351 for m in fp.members:
3352 type_info = types[m.type]
3354 # Complex types have a matching definition e.g. VkStruct.
3355 # Not needed for base types such as uint32_t.
3356 if "data" in type_info:
3357 types[m.type]["data"].required = True
3359 def mark_struct_dependencies(struct, types):
3361 type_info = types[m.type]
3363 # Complex types have a matching definition e.g. VkStruct.
3364 # Not needed for base types such as uint32_t.
3365 if "data" in type_info:
3366 types[m.type]["data"].required = True
3368 if type_info["category"] == "struct" and struct.name != m.type:
3370 mark_struct_dependencies(type_info["data"], types)
3371 elif type_info["category"] == "funcpointer":
3372 mark_funcpointer_dependencies(type_info["data"], types)
3373 elif type_info["category"] == "bitmask":
3374 mark_bitmask_dependencies(type_info["data"], types)
3376 func = self.funcs[command]
3377 func.required = True
3379 # Pull in return type
3380 if func.type != "void":
3381 self.types[func.type]["data"].required = True
3383 # Analyze parameter dependencies and pull in any type needed.
3384 for p in func.params:
3385 type_info = self.types[p.type]
3387 # Check if we are dealing with a complex type e.g. VkEnum, VkStruct and others.
3388 if "data" not in type_info:
3391 # Mark the complex type as required.
3392 type_info["data"].required = True
3393 if type_info["category"] == "struct":
3394 struct = type_info["data"]
3395 mark_struct_dependencies(struct, self.types)
3396 elif type_info["category"] == "bitmask":
3397 mark_bitmask_dependencies(type_info["data"], self.types)
3399 def _match_object_types(self):
3400 """ Matches each handle with the correct object type. """
3401 # Use upper case comparison for simplicity.
3403 for value in self.enums["VkObjectType"].values:
3404 object_name = "VK" + value.name[len("VK_OBJECT_TYPE"):].replace("_", "")
3405 object_types[object_name] = value.name
3407 for handle in self.handles:
3408 if not handle.is_required():
3410 handle.object_type = object_types.get(handle.name.upper())
3411 if not handle.object_type:
3412 LOGGER.warning("No object type found for {}".format(handle.name))
3414 def _parse_commands(self, root):
3415 """ Parse command section containing the Vulkan function calls. """
3417 commands = root.findall("./commands/")
3419 # As of Vulkan 1.1, various extensions got promoted to Core.
3420 # The old commands (e.g. KHR) are available for backwards compatibility
3421 # and are marked in vk.xml as 'alias' to the non-extension type.
3422 # The registry likes to avoid data duplication, so parameters and other
3423 # metadata need to be looked up from the Core command.
3424 # We parse the alias commands in a second pass.
3426 for command in commands:
3427 alias_name = command.attrib.get("alias")
3429 alias_commands.append(command)
3432 func = VkFunction.from_xml(command, self.types)
3433 funcs[func.name] = func
3435 for command in alias_commands:
3436 alias_name = command.attrib.get("alias")
3437 alias = funcs[alias_name]
3438 func = VkFunction.from_alias(command, alias)
3439 funcs[func.name] = func
3441 # To make life easy for the code generation, separate all function
3442 # calls out in the 4 types of Vulkan functions:
3443 # device, global, physical device and instance.
3448 for func in funcs.values():
3449 if func.is_device_func():
3450 device_funcs.append(func)
3451 elif func.is_global_func():
3452 global_funcs.append(func)
3453 elif func.is_phys_dev_func():
3454 phys_dev_funcs.append(func)
3456 instance_funcs.append(func)
3458 # Sort function lists by name and store them.
3459 self.device_funcs = sorted(device_funcs, key=lambda func: func.name)
3460 self.global_funcs = sorted(global_funcs, key=lambda func: func.name)
3461 self.phys_dev_funcs = sorted(phys_dev_funcs, key=lambda func: func.name)
3462 self.instance_funcs = sorted(instance_funcs, key=lambda func: func.name)
3464 # The funcs dictionary is used as a convenient way to lookup function
3465 # calls when needed e.g. to adjust member variables.
3466 self.funcs = OrderedDict(sorted(funcs.items()))
3468 def _parse_enums(self, root):
3469 """ Parse enums section or better described as constants section. """
3472 for enum in root.findall("./enums"):
3473 name = enum.attrib.get("name")
3474 _type = enum.attrib.get("type")
3476 if _type in ("enum", "bitmask"):
3477 enums[name] = VkEnum.from_xml(enum)
3479 # If no type is set, we are dealing with API constants.
3480 for value in enum.findall("enum"):
3481 # If enum is an alias, set the value to the alias name.
3482 # E.g. VK_LUID_SIZE_KHR is an alias to VK_LUID_SIZE.
3483 alias = value.attrib.get("alias")
3485 self.consts.append(VkConstant(value.attrib.get("name"), alias))
3487 self.consts.append(VkConstant(value.attrib.get("name"), value.attrib.get("value")))
3489 self.enums = OrderedDict(sorted(enums.items()))
3491 def _process_require_enum(self, enum_elem, ext=None, only_aliased=False):
3492 if "extends" in enum_elem.keys():
3493 enum = self.types[enum_elem.attrib["extends"]]["data"]
3495 # Need to define VkEnumValues which were aliased to by another value. This is necessary
3496 # from VK spec version 1.2.135 where the provisional VK_KHR_ray_tracing extension was
3497 # added which altered VK_NV_ray_tracing's VkEnumValues to alias to the provisional
3500 for _, t in self.types.items():
3501 if t["category"] != "enum":
3505 for value in t["data"].values:
3506 if value.alias == enum_elem.attrib["name"]:
3509 if only_aliased and not aliased:
3512 if "bitpos" in enum_elem.keys():
3513 # We need to add an extra value to an existing enum type.
3514 # E.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG to VkFormatFeatureFlagBits.
3515 enum.create_bitpos(enum_elem.attrib["name"], int(enum_elem.attrib["bitpos"]))
3517 elif "offset" in enum_elem.keys():
3518 # Extensions promoted to Core, have the extension number as part
3519 # of the enum value. Else retrieve from the extension tag.
3520 if enum_elem.attrib.get("extnumber"):
3521 ext_number = int(enum_elem.attrib.get("extnumber"))
3523 ext_number = int(ext.attrib["number"])
3524 offset = int(enum_elem.attrib["offset"])
3525 value = EXT_BASE + (ext_number - 1) * EXT_BLOCK_SIZE + offset
3527 # Deal with negative values.
3528 direction = enum_elem.attrib.get("dir")
3529 if direction is not None:
3532 enum.create_value(enum_elem.attrib["name"], str(value))
3534 elif "value" in enum_elem.keys():
3535 enum.create_value(enum_elem.attrib["name"], enum_elem.attrib["value"])
3536 elif "alias" in enum_elem.keys():
3537 enum.create_alias(enum_elem.attrib["name"], enum_elem.attrib["alias"])
3539 elif "value" in enum_elem.keys():
3540 # Constant with an explicit value
3544 self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["value"]))
3545 elif "alias" in enum_elem.keys():
3547 if not only_aliased:
3550 self.consts.append(VkConstant(enum_elem.attrib["name"], enum_elem.attrib["alias"]))
3553 def _require_type(type_info):
3554 if type_info.is_alias():
3555 type_info = type_info.alias
3556 type_info.required = True
3557 if type(type_info) == VkStruct:
3558 for member in type_info.members:
3559 if "data" in member.type_info:
3560 VkRegistry._require_type(member.type_info["data"])
3562 def _parse_extensions(self, root):
3563 """ Parse extensions section and pull in any types and commands for this extension. """
3565 exts = root.findall("./extensions/extension")
3567 skipped_exts = UNSUPPORTED_EXTENSIONS.copy()
3569 def process_ext(ext, deferred=False):
3570 ext_name = ext.attrib["name"]
3572 # Set extension name on any functions calls part of this extension as we
3573 # were not aware of the name during initial parsing.
3574 commands = ext.findall("require/command")
3575 for command in commands:
3576 cmd_name = command.attrib["name"]
3577 # Need to verify that the command is defined, and otherwise skip it.
3578 # vkCreateScreenSurfaceQNX is declared in <extensions> but not defined in
3579 # <commands>. A command without a definition cannot be enabled, so it's valid for
3580 # the XML file to handle this, but because of the manner in which we parse the XML
3581 # file we pre-populate from <commands> before we check if a command is enabled.
3582 if cmd_name in self.funcs:
3583 self.funcs[cmd_name].extensions.add(ext_name)
3585 # Some extensions are not ready or have numbers reserved as a place holder.
3586 if ext.attrib["supported"] == "disabled":
3587 LOGGER.debug("Skipping disabled extension: {0}".format(ext_name))
3588 skipped_exts.append(ext_name)
3591 # Defer extensions with 'sortorder' as they are order-dependent for spec-parsing.
3592 if not deferred and "sortorder" in ext.attrib:
3593 deferred_exts.append(ext)
3596 # Disable highly experimental extensions as the APIs are unstable and can
3597 # change between minor Vulkan revisions until API is final and becomes KHR
3599 if ("KHX" in ext_name or "NVX" in ext_name) and ext_name not in ALLOWED_X_EXTENSIONS:
3600 LOGGER.debug("Skipping experimental extension: {0}".format(ext_name))
3601 skipped_exts.append(ext_name)
3604 # Extensions can define VkEnumValues which alias to provisional extensions. Pre-process
3605 # extensions to define any required VkEnumValues before the platform check below.
3606 for require in ext.findall("require"):
3607 # Extensions can add enum values to Core / extension enums, so add these.
3608 for enum_elem in require.findall("enum"):
3609 self._process_require_enum(enum_elem, ext, only_aliased=True)
3611 platform = ext.attrib.get("platform")
3612 if platform and platform != "win32":
3613 LOGGER.debug("Skipping extensions {0} for platform {1}".format(ext_name, platform))
3614 skipped_exts.append(ext_name)
3617 if not self._is_extension_supported(ext_name):
3618 LOGGER.debug("Skipping unsupported extension: {0}".format(ext_name))
3619 skipped_exts.append(ext_name)
3621 elif "requires" in ext.attrib:
3622 # Check if this extension builds on top of another unsupported extension.
3623 requires = ext.attrib["requires"].split(",")
3624 if len(set(requires).intersection(skipped_exts)) > 0:
3625 skipped_exts.append(ext_name)
3628 LOGGER.debug("Loading extension: {0}".format(ext_name))
3630 # Extensions can define one or more require sections each requiring
3631 # different features (e.g. Vulkan 1.1). Parse each require section
3632 # separately, so we can skip sections we don't want.
3633 for require in ext.findall("require"):
3634 # Extensions can add enum values to Core / extension enums, so add these.
3635 for enum_elem in require.findall("enum"):
3636 self._process_require_enum(enum_elem, ext)
3638 for t in require.findall("type"):
3639 type_info = self.types[t.attrib["name"]]["data"]
3640 self._require_type(type_info)
3641 feature = require.attrib.get("feature")
3642 if feature and not self._is_feature_supported(feature):
3645 required_extension = require.attrib.get("extension")
3646 if required_extension and not self._is_extension_supported(required_extension):
3649 # Pull in any commands we need. We infer types to pull in from the command
3651 for command in require.findall("command"):
3652 cmd_name = command.attrib["name"]
3653 self._mark_command_required(cmd_name)
3656 # Store a list with extensions.
3657 ext_info = {"name" : ext_name, "type" : ext.attrib["type"]}
3658 extensions.append(ext_info)
3661 # Process extensions, allowing for sortorder to defer extension processing
3665 deferred_exts.sort(key=lambda ext: ext.attrib["sortorder"])
3668 for ext in deferred_exts:
3669 process_ext(ext, deferred=True)
3671 # Sort in alphabetical order.
3672 self.extensions = sorted(extensions, key=lambda ext: ext["name"])
3674 def _parse_features(self, root):
3675 """ Parse the feature section, which describes Core commands and types needed. """
3677 for feature in root.findall("./feature"):
3678 feature_name = feature.attrib["name"]
3679 for require in feature.findall("require"):
3680 LOGGER.info("Including features for {0}".format(require.attrib.get("comment")))
3682 if tag.tag == "comment":
3684 elif tag.tag == "command":
3685 if not self._is_feature_supported(feature_name):
3687 name = tag.attrib["name"]
3688 self._mark_command_required(name)
3689 elif tag.tag == "enum":
3690 self._process_require_enum(tag)
3691 elif tag.tag == "type":
3692 name = tag.attrib["name"]
3694 # Skip pull in for vk_platform.h for now.
3695 if name == "vk_platform":
3698 type_info = self.types[name]
3699 type_info["data"].required = True
3701 def _parse_types(self, root):
3702 """ Parse types section, which contains all data types e.g. structs, typedefs etcetera. """
3703 types = root.findall("./types/type")
3715 type_info["category"] = t.attrib.get("category", None)
3716 type_info["requires"] = t.attrib.get("requires", None)
3718 # We parse aliases in a second pass when we know more.
3719 alias = t.attrib.get("alias")
3721 LOGGER.debug("Alias found: {0}".format(alias))
3722 alias_types.append(t)
3725 if type_info["category"] in ["include"]:
3728 if type_info["category"] == "basetype":
3729 name = t.find("name").text
3731 if not t.find("type") is None:
3732 _type = t.find("type").text
3733 tail = t.find("type").tail
3734 if tail is not None:
3735 _type += tail.strip()
3736 basetype = VkBaseType(name, _type)
3737 base_types.append(basetype)
3738 type_info["data"] = basetype
3740 # Basic C types don't need us to define them, but we do need data for them
3741 if type_info["requires"] == "vk_platform":
3742 requires = type_info["requires"]
3743 basic_c = VkBaseType(name, _type, requires=requires)
3744 type_info["data"] = basic_c
3746 if type_info["category"] == "bitmask":
3747 name = t.find("name").text
3748 _type = t.find("type").text
3750 # Most bitmasks have a requires attribute used to pull in
3751 # required '*FlagBits" enum.
3752 requires = type_info["requires"]
3753 bitmask = VkBaseType(name, _type, requires=requires)
3754 bitmasks.append(bitmask)
3755 type_info["data"] = bitmask
3757 if type_info["category"] == "define":
3758 define = VkDefine.from_xml(t)
3759 defines.append(define)
3760 type_info["data"] = define
3762 if type_info["category"] == "enum":
3763 name = t.attrib.get("name")
3764 # The type section only contains enum names, not the actual definition.
3765 # Since we already parsed the enum before, just link it in.
3767 type_info["data"] = self.enums[name]
3768 except KeyError as e:
3769 # Not all enums seem to be defined yet, typically that's for
3770 # ones ending in 'FlagBits' where future extensions may add
3772 type_info["data"] = None
3774 if type_info["category"] == "funcpointer":
3775 funcpointer = VkFunctionPointer.from_xml(t)
3776 funcpointers.append(funcpointer)
3777 type_info["data"] = funcpointer
3779 if type_info["category"] == "handle":
3780 handle = VkHandle.from_xml(t)
3781 handles.append(handle)
3782 type_info["data"] = handle
3784 if type_info["category"] in ["struct", "union"]:
3785 # We store unions among structs as some structs depend
3786 # on unions. The types are very similar in parsing and
3787 # generation anyway. The official Vulkan scripts use
3788 # a similar kind of hack.
3789 struct = VkStruct.from_xml(t)
3790 structs.append(struct)
3791 type_info["data"] = struct
3793 # Name is in general within a name tag else it is an optional
3794 # attribute on the type tag.
3795 name_elem = t.find("name")
3796 if name_elem is not None:
3797 type_info["name"] = name_elem.text
3799 type_info["name"] = t.attrib.get("name", None)
3801 # Store all type data in a shared dictionary, so we can easily
3802 # look up information for a given type. There are no duplicate
3804 self.types[type_info["name"]] = type_info
3806 # Second pass for alias types, so we can retrieve all data from
3807 # the aliased object.
3808 for t in alias_types:
3810 type_info["category"] = t.attrib.get("category")
3811 type_info["name"] = t.attrib.get("name")
3813 alias = t.attrib.get("alias")
3815 if type_info["category"] == "bitmask":
3816 bitmask = VkBaseType(type_info["name"], alias, alias=self.types[alias]["data"])
3817 bitmasks.append(bitmask)
3818 type_info["data"] = bitmask
3820 if type_info["category"] == "enum":
3821 enum = VkEnum.from_alias(t, self.types[alias]["data"])
3822 type_info["data"] = enum
3823 self.enums[enum.name] = enum
3825 if type_info["category"] == "handle":
3826 handle = VkHandle.from_alias(t, self.types[alias]["data"])
3827 handles.append(handle)
3828 type_info["data"] = handle
3830 if type_info["category"] == "struct":
3831 struct = VkStruct.from_alias(t, self.types[alias]["data"])
3832 structs.append(struct)
3833 type_info["data"] = struct
3835 self.types[type_info["name"]] = type_info
3837 # We need detailed type information during code generation
3838 # on structs for alignment reasons. Unfortunately structs
3839 # are parsed among other types, so there is no guarantee
3840 # that any types needed have been parsed already, so set
3842 for struct in structs:
3843 struct.set_type_info(self.types)
3845 # Alias structures have enum values equivalent to those of the
3846 # structure which they are aliased against. we need to ignore alias
3847 # structs when populating the struct extensions list, otherwise we
3848 # will create duplicate case entries.
3852 for structextend in struct.structextends:
3853 s = self.types[structextend]["data"]
3854 s.struct_extensions.append(struct)
3856 # Guarantee everything is sorted, so code generation doesn't have
3857 # to deal with this.
3858 self.base_types = sorted(base_types, key=lambda base_type: base_type.name)
3859 self.bitmasks = sorted(bitmasks, key=lambda bitmask: bitmask.name)
3860 self.defines = defines
3861 self.enums = OrderedDict(sorted(self.enums.items()))
3862 self.funcpointers = sorted(funcpointers, key=lambda fp: fp.name)
3863 self.handles = sorted(handles, key=lambda handle: handle.name)
3864 self.structs = sorted(structs, key=lambda struct: struct.name)
3866 def generate_vulkan_json(f):
3868 f.write(" \"file_format_version\": \"1.0.0\",\n")
3869 f.write(" \"ICD\": {\n")
3870 f.write(" \"library_path\": \".\\\\winevulkan.dll\",\n")
3871 f.write(" \"api_version\": \"{0}\"\n".format(VK_XML_VERSION))
3875 def set_working_directory():
3876 path = os.path.abspath(__file__)
3877 path = os.path.dirname(path)
3880 def download_vk_xml(filename):
3881 url = "https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/v{0}/xml/vk.xml".format(VK_XML_VERSION)
3882 if not os.path.isfile(filename):
3883 urllib.request.urlretrieve(url, filename)
3886 parser = argparse.ArgumentParser()
3887 parser.add_argument("-v", "--verbose", action="count", default=0, help="increase output verbosity")
3888 parser.add_argument("-x", "--xml", default=None, type=str, help="path to specification XML file")
3890 args = parser.parse_args()
3891 if args.verbose == 0:
3892 LOGGER.setLevel(logging.WARNING)
3893 elif args.verbose == 1:
3894 LOGGER.setLevel(logging.INFO)
3896 LOGGER.setLevel(logging.DEBUG)
3898 set_working_directory()
3903 vk_xml = "vk-{0}.xml".format(VK_XML_VERSION)
3904 download_vk_xml(vk_xml)
3906 registry = VkRegistry(vk_xml)
3907 generator = VkGenerator(registry)
3909 with open(WINE_VULKAN_H, "w") as f:
3910 generator.generate_vulkan_h(f)
3912 with open(WINE_VULKAN_DRIVER_H, "w") as f:
3913 generator.generate_vulkan_driver_h(f)
3915 with open(WINE_VULKAN_THUNKS_H, "w") as f:
3916 generator.generate_thunks_h(f, "wine_")
3918 with open(WINE_VULKAN_THUNKS_C, "w") as f:
3919 generator.generate_thunks_c(f, "wine_")
3921 with open(WINE_VULKAN_LOADER_THUNKS_H, "w") as f:
3922 generator.generate_loader_thunks_h(f)
3924 with open(WINE_VULKAN_LOADER_THUNKS_C, "w") as f:
3925 generator.generate_loader_thunks_c(f)
3927 with open(WINE_VULKAN_JSON, "w") as f:
3928 generate_vulkan_json(f)
3930 with open(WINE_VULKAN_SPEC, "w") as f:
3931 generator.generate_vulkan_spec(f)
3933 with open(WINE_VULKAN_LOADER_SPEC, "w") as f:
3934 generator.generate_vulkan_loader_spec(f)
3936 if __name__ == "__main__":