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
25 import xml.etree.ElementTree as ET
26 from collections import OrderedDict
27 from collections.abc import Sequence
30 # This script generates code for a Wine Vulkan ICD driver from Vulkan's vk.xml.
31 # Generating the code is like 10x worse than OpenGL, which is mostly a calling
32 # convention passthrough.
34 # The script parses vk.xml and maps functions and types to helper objects. These
35 # helper objects simplify the xml parsing and map closely to the Vulkan types.
36 # The code generation utilizes the helper objects during code generation and
37 # most of the ugly work is carried out by these objects.
39 # Vulkan ICD challenges:
40 # - Vulkan ICD loader (vulkan-1.dll) relies on a section at the start of
41 # 'dispatchable handles' (e.g. VkDevice, VkInstance) for it to insert
42 # its private data. It uses this area to stare its own dispatch tables
43 # for loader internal use. This means any dispatchable objects need wrapping.
45 # - Vulkan structures have different alignment between win32 and 32-bit Linux.
46 # This means structures with alignment differences need conversion logic.
47 # Often structures are nested, so the parent structure may not need any
48 # conversion, but some child may need some.
50 # vk.xml parsing challenges:
51 # - Contains type data for all platforms (generic Vulkan, Windows, Linux,..).
52 # Parsing of extension information required to pull in types and functions
53 # we really want to generate. Just tying all the data together is tricky.
55 # - Extensions can affect core types e.g. add new enum values, bitflags or
56 # additional structure chaining through 'pNext' / 'sType'.
58 # - Arrays are used all over the place for parameters or for structure members.
59 # Array length is often stored in a previous parameter or another structure
60 # member and thus needs careful parsing.
62 LOGGER = logging.Logger("vulkan")
63 LOGGER.addHandler(logging.StreamHandler())
65 # Filenames to create.
66 WINE_VULKAN_H = "../../include/wine/vulkan.h"
67 WINE_VULKAN_DRIVER_H = "../../include/wine/vulkan_driver.h"
68 WINE_VULKAN_SPEC = "winevulkan.spec"
69 WINE_VULKAN_THUNKS_C = "vulkan_thunks.c"
70 WINE_VULKAN_THUNKS_H = "vulkan_thunks.h"
72 # Extension enum values start at a certain offset (EXT_BASE).
73 # Relative to the offset each extension has a block (EXT_BLOCK_SIZE)
75 # Start for a given extension is:
76 # EXT_BASE + (extension_number-1) * EXT_BLOCK_SIZE
80 # In general instance extensions can't be automatically generated
81 # and need custom wrappers due to e.g. win32 / X11 specific code.
82 # List of supported instance extensions.
83 SUPPORTED_INSTANCE_EXTENSIONS = [
84 "VK_KHR_get_physical_device_properties2",
86 "VK_KHR_win32_surface",
89 BLACKLISTED_EXTENSIONS = [
90 # Handling of VK_EXT_debug_report requires some consideration. The win32
91 # loader already provides it for us and it is somewhat usable. . If we add
92 # plumbing down to the native layer, we will get each message twice as we
93 # use 2 loaders (win32+native), but we may get output from the driver.
94 # In any case callback conversion is required.
95 "VK_EXT_debug_report",
96 "VK_EXT_display_control", # Requires VK_EXT_display_surface_counter
97 "VK_EXT_hdr_metadata", # Needs WSI work.
98 "VK_GOOGLE_display_timing",
99 "VK_KHR_display", # Needs WSI work.
100 "VK_KHR_external_fence_fd",
101 "VK_KHR_external_fence_win32",
102 "VK_KHR_external_memory",
103 "VK_KHR_external_semaphore",
104 # Relates to external_semaphore and needs type conversions in bitflags.
105 "VK_KHR_external_semaphore_capabilities",
106 "VK_KHR_shared_presentable_image", # Needs WSI work.
107 "VK_NV_external_memory_win32"
110 # The Vulkan loader provides entry-points for core functionality and important
111 # extensions. Based on vulkan-1.def this amounts to WSI extensions on 1.0.51.
114 "VK_KHR_display_swapchain",
117 "VK_KHR_win32_surface"
120 # Functions part of our winevulkan graphics driver interface.
121 # DRIVER_VERSION should be bumped on any change to driver interface
122 # in FUNCTION_OVERRIDES
125 # Table of functions for which we have a special implementation.
126 # This are regular device / instance functions for which we need
127 # to more work compared to a regular thunk or because they are
128 # part of the driver interface.
129 # - dispatch set whether we need a function pointer in the device
130 # / instance dispatch table.
131 # - driver sets whether the api is part of the driver interface.
132 # - thunk sets whether to create a thunk in vulkan_thunks.c.
133 FUNCTION_OVERRIDES = {
135 "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : False},
136 "vkEnumerateInstanceExtensionProperties" : {"dispatch" : False, "driver" : True, "thunk" : False},
137 "vkGetInstanceProcAddr": {"dispatch" : False, "driver" : True, "thunk" : False},
140 "vkCreateDevice" : {"dispatch" : True, "driver" : False, "thunk" : False},
141 "vkDestroyInstance" : {"dispatch" : False, "driver" : True, "thunk" : False },
142 "vkEnumerateDeviceExtensionProperties" : {"dispatch" : True, "driver" : False, "thunk" : False},
143 "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : False},
146 "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : False},
147 "vkCmdExecuteCommands" : {"dispatch" : True, "driver" : False, "thunk" : False},
148 "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : False},
149 "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : False},
150 "vkGetDeviceProcAddr" : {"dispatch" : False, "driver" : True, "thunk" : False},
151 "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : False},
152 "vkQueueSubmit" : {"dispatch": True, "driver" : False, "thunk" : False},
155 "vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
156 "vkGetPhysicalDeviceSurfaceSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
157 "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
158 "vkGetPhysicalDeviceSurfaceFormatsKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
159 "vkGetPhysicalDeviceSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
161 # VK_KHR_win32_surface
162 "vkCreateWin32SurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
163 "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
166 "vkAcquireNextImageKHR": {"dispatch" : True, "driver" : True, "thunk" : True},
167 "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
168 "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : True},
169 "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : True},
170 "vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : True},
174 class Direction(Enum):
175 """ Parameter direction: input, output, input_output. """
181 class VkBaseType(object):
182 def __init__(self, name, _type, requires=None):
183 """ Vulkan base type class.
185 VkBaseType is mostly used by Vulkan to define its own
186 base types like VkFlags through typedef out of e.g. uint32_t.
189 name (:obj:'str'): Name of the base type.
190 _type (:obj:'str'): Underlaying type
191 requires (:obj:'str', optional): Other types required.
192 Often bitmask values pull in a *FlagBits type.
196 self.requires = requires
197 self.required = False
199 def definition(self):
200 text = "typedef {0} {1};\n".format(self.type, self.name)
204 class VkConstant(object):
205 def __init__(self, name, value):
209 def definition(self):
210 text = "#define {0} {1}\n".format(self.name, self.value)
214 class VkDefine(object):
215 def __init__(self, name, value):
220 def from_xml(define):
221 name_elem = define.find("name")
223 if name_elem is None:
224 # <type category="define" name="some_name">some_value</type>
225 # At the time of writing there is only 1 define of this category
226 # 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'.
227 name = define.attrib.get("name")
229 # We override behavior of VK_DEFINE_NON_DISPATCHABLE handle as the default
230 # definition various between 64-bit (uses pointers) and 32-bit (uses uint64_t).
231 # This complicates TRACEs in the thunks, so just use uint64_t.
232 if name == "VK_DEFINE_NON_DISPATCHABLE_HANDLE":
233 value = "#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;"
236 return VkDefine(name, value)
238 # With a name element the structure is like:
239 # <type category="define"><name>some_name</name>some_value</type>
240 name = name_elem.text
242 # VK_API_VERSION is a deprecated constant and we don't really want to parse it or generate
243 # code for it. However the vk.xml feature section still references it.
244 if name == "VK_API_VERSION":
245 return VkDefine(name, "")
247 # The body of the define is basically unstructured C code. It is not meant for easy parsing.
248 # Some lines contain deprecated values or comments, which we try to filter out.
250 for line in define.text.splitlines():
251 # Skip comments or deprecated values.
258 if child.tail is not None:
261 return VkDefine(name, value.rstrip(' '))
263 def definition(self):
264 if self.value is None:
267 # Nothing to do as the value was already put in the right form during parsing.
268 return "{0}\n".format(self.value)
271 class VkEnum(object):
272 def __init__(self, name, values):
275 self.required = False
279 name = enum.attrib.get("name")
282 for v in enum.findall("enum"):
283 # Value is either a value or a bitpos, only one can exist.
284 value = v.attrib.get("value")
287 value = 1 << int(v.attrib.get("bitpos"))
288 values.append(VkEnumValue(v.attrib.get("name"), value, hex=True))
290 # Some values are in hex form. We want to preserve the hex representation
291 # at least when we convert back to a string. Internally we want to use int.
293 values.append(VkEnumValue(v.attrib.get("name"), int(value, 0), hex=True))
295 values.append(VkEnumValue(v.attrib.get("name"), int(value, 0)))
297 # vulkan.h contains a *_MAX_ENUM value set to 32-bit at the time of writing,
298 # which is to prepare for extensions as they can add values and hence affect
299 # the size definition.
300 max_name = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2',name).upper() + "_MAX_ENUM"
301 values.append(VkEnumValue(max_name, 0x7fffffff, hex=True))
303 return VkEnum(name, values)
305 def add(self, value):
306 """ Add a value to enum. """
307 self.values.append(value)
309 def definition(self):
310 text = "typedef enum {0}\n{{\n".format(self.name)
312 # Print values sorted, values can have been added in a random order.
313 values = sorted(self.values, key=lambda value: value.value)
315 text += " {0},\n".format(value.definition())
316 text += "}} {0};\n\n".format(self.name)
320 class VkEnumValue(object):
321 def __init__(self, name, value, hex=False):
327 return "{0}={1}".format(self.name, self.value)
329 def definition(self):
330 """ Convert to text definition e.g. VK_FOO = 1 """
332 # Hex is commonly used for FlagBits and sometimes within
333 # a non-FlagBits enum for a bitmask value as well.
335 return "{0} = 0x{1:08x}".format(self.name, self.value)
337 return "{0} = {1}".format(self.name, self.value)
340 class VkFunction(object):
341 def __init__(self, _type=None, name=None, params=[], extension=None):
342 self.extension = extension
347 # For some functions we need some extra metadata from FUNCTION_OVERRIDES.
348 func_info = FUNCTION_OVERRIDES.get(self.name, None)
349 self.dispatch = func_info["dispatch"] if func_info is not None else True
350 self.driver = func_info["driver"] if func_info is not None else False
351 self.thunk_needed = func_info["thunk"] if func_info is not None else True
353 # Required is set while parsing which APIs and types are required
354 # and is used by the code generation.
355 self.required = False
358 def from_xml(command, types):
359 proto = command.find("proto")
361 func_name = proto.find("name").text
362 func_type = proto.find("type").text
365 for param in command.findall("param"):
366 vk_param = VkParam.from_xml(param, types)
367 params.append(vk_param)
369 return VkFunction(_type=func_type, name=func_name, params=params)
371 def get_conversions(self):
372 """ Get a list of conversion functions required for this function if any.
373 Parameters which are structures may require conversion between win32
374 and the host platform. This function returns a list of conversions
379 for param in self.params:
380 convs = param.get_conversions()
381 if convs is not None:
382 conversions.extend(convs)
386 def is_core_func(self):
387 """ Returns whether the function is a Vulkan core function.
388 Core functions are APIs defined by the Vulkan spec to be part of the
389 Core API as well as several KHR WSI extensions.
392 if self.extension is None:
395 if self.extension in CORE_EXTENSIONS:
400 def is_device_func(self):
401 # If none of the other, it must be a device function
402 return not self.is_global_func() and not self.is_instance_func()
404 def is_driver_func(self):
405 """ Returns if function is part of Wine driver interface. """
408 def is_global_func(self):
409 # Treat vkGetInstanceProcAddr as a global function as it
410 # can operate with NULL for vkInstance.
411 if self.name == "vkGetInstanceProcAddr":
413 # Global functions are not passed a dispatchable object.
414 elif self.params[0].is_dispatchable():
418 def is_instance_func(self):
419 # Instance functions are passed VkInstance or VkPhysicalDevice.
420 if self.params[0].type in ["VkInstance", "VkPhysicalDevice"]:
424 def is_required(self):
427 def needs_conversion(self):
428 """ Check if the function needs any input/output type conversion.
429 Functions need input/output conversion if struct parameters have
430 alignment differences between Win32 and Linux 32-bit.
433 for p in self.params:
434 if p.needs_conversion():
435 LOGGER.debug("Parameter {0} to {1} requires conversion".format(p.name, self.name))
440 def needs_dispatch(self):
443 def needs_thunk(self):
444 return self.thunk_needed
446 def pfn(self, call_conv=None, conv=False):
447 """ Create function pointer. """
449 if call_conv is not None:
450 pfn = "{0} ({1} *p_{2})(".format(self.type, call_conv, self.name)
452 pfn = "{0} (*p_{1})(".format(self.type, self.name)
454 for i, param in enumerate(self.params):
456 pfn += param.const + " "
459 if conv and param.needs_conversion():
462 if param.is_pointer():
463 pfn += " " + param.pointer
465 if param.array_len is not None:
466 pfn += "[{0}]".format(param.array_len)
468 if i < len(self.params) - 1:
473 def prototype(self, call_conv=None, prefix=None, postfix=None):
474 """ Generate prototype for given function.
477 call_conv (str, optional): calling convention e.g. WINAPI
478 prefix (str, optional): prefix to append prior to function name e.g. vkFoo -> wine_vkFoo
479 postfix (str, optional): text to append after function name but prior to semicolon e.g. DECLSPEC_HIDDEN
482 proto = "{0}".format(self.type)
484 if call_conv is not None:
485 proto += " {0}".format(call_conv)
487 if prefix is not None:
488 proto += " {0}{1}(".format(prefix, self.name)
490 proto += " {0}(".format(self.name)
492 # Add all the parameters.
493 proto += ", ".join([p.definition() for p in self.params])
495 if postfix is not None:
496 proto += ") {0}".format(postfix)
503 body = " {0}".format(self.trace())
505 params = ", ".join([p.variable(conv=False) for p in self.params])
507 # Call the native Vulkan function.
508 if self.type == "void":
509 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
511 body += " return {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
515 def body_conversion(self):
518 # Declare a variable to hold the result for non-void functions.
519 if self.type != "void":
520 body += " {0} result;\n".format(self.type)
522 # Declare any tmp parameters for conversion.
523 for p in self.params:
524 if not p.needs_conversion():
527 if p.is_dynamic_array():
528 body += " {0}_host *{1}_host;\n".format(p.type, p.name)
530 body += " {0}_host {1}_host;\n".format(p.type, p.name)
532 body += " {0}\n".format(self.trace())
534 # Call any win_to_host conversion calls.
535 for p in self.params:
536 if not p.needs_input_conversion():
539 body += p.copy(Direction.INPUT)
541 # Build list of parameters containing converted and non-converted parameters.
542 # The param itself knows if conversion is needed and applies it when we set conv=True.
543 params = ", ".join([p.variable(conv=True) for p in self.params])
545 # Call the native Vulkan function.
546 if self.type == "void":
547 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
549 body += " result = {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
553 # Call any host_to_win conversion calls.
554 for p in self.params:
555 if not p.needs_output_conversion():
558 body += p.copy(Direction.OUTPUT)
560 # Perform any required cleanups. Most of these are for array functions.
561 for p in self.params:
562 if not p.needs_free():
567 # Finally return the result.
568 if self.type != "void":
569 body += " return result;\n"
573 def spec(self, prefix=None):
574 """ Generate spec file entry for this function.
577 prefix (str, optional): prefix to prepend to entry point name.
580 params = " ".join([p.spec() for p in self.params])
581 if prefix is not None:
582 return "@ stdcall {0}{1}({2})\n".format(prefix, self.name, params)
584 return "@ stdcall {0}({1})\n".format(self.name, params)
586 def stub(self, call_conv=None, prefix=None):
587 stub = self.prototype(call_conv=call_conv, prefix=prefix)
589 stub += " {0}".format(self.trace(message="stub: ", trace_func="FIXME"))
591 if self.type == "VkResult":
592 stub += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
593 elif self.type == "VkBool32":
594 stub += " return VK_FALSE;\n"
595 elif self.type == "PFN_vkVoidFunction":
596 stub += " return NULL;\n"
601 def thunk(self, call_conv=None, prefix=None):
602 thunk = self.prototype(call_conv=call_conv, prefix=prefix)
605 if self.needs_conversion():
606 thunk += "#if defined(USE_STRUCT_CONVERSION)\n"
607 thunk += self.body_conversion()
617 def trace(self, message=None, trace_func=None):
618 """ Create a trace string including all parameters.
621 message (str, optional): text to print at start of trace message e.g. 'stub: '
622 trace_func (str, optional): used to override trace function e.g. FIXME, printf, etcetera.
624 if trace_func is not None:
625 trace = "{0}(\"".format(trace_func)
629 if message is not None:
632 # First loop is for all the format strings.
633 trace += ", ".join([p.format_string() for p in self.params])
636 # Second loop for parameter names and optional conversions.
637 for param in self.params:
638 if param.format_conv is not None:
639 trace += ", " + param.format_conv.format(param.name)
641 trace += ", {0}".format(param.name)
647 class VkFunctionPointer(object):
648 def __init__(self, _type, name, members):
650 self.members = members
652 self.required = False
655 def from_xml(funcpointer):
659 for t in funcpointer.findall("type"):
661 # <type>void</type>* pUserData,
662 # Parsing of the tail (anything past </type>) is tricky since there
663 # can be other data on the next line like: const <type>int</type>..
666 lines = t.tail.split(",\n")
667 if lines[0][0] == "*":
669 name = lines[0][1:].strip()
672 name = lines[0].strip()
674 # Filter out ); if it is contained.
675 name = name.partition(");")[0]
677 # If tail encompasses multiple lines, assign the second line to begin
680 begin = lines[1].strip()
684 members.append(VkMember(const=const, _type=_type, pointer=pointer, name=name))
686 _type = funcpointer.text
687 name = funcpointer.find("name").text
688 return VkFunctionPointer(_type, name, members)
690 def definition(self):
691 text = "{0} {1})(\n".format(self.type, self.name)
694 if len(self.members) > 0:
695 for m in self.members:
697 text += " " + m.definition()
700 text += ",\n " + m.definition()
702 # Just make the compiler happy by adding a void parameter.
708 class VkHandle(object):
709 def __init__(self, name, _type, parent):
713 self.required = False
716 def from_xml(handle):
717 name = handle.find("name").text
718 _type = handle.find("type").text
719 parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice.
720 return VkHandle(name, _type, parent)
722 def dispatch_table(self):
723 if not self.is_dispatchable():
726 if self.parent is None:
727 # Should only happen for VkInstance
729 elif self.name == "VkDevice":
730 # VkDevice has VkInstance as a parent, but has its own dispatch table.
732 elif self.parent in ["VkInstance", "VkPhysicalDevice"]:
733 return "instance->funcs"
734 elif self.parent in ["VkDevice", "VkCommandPool"]:
735 return "device->funcs"
737 LOGGER.error("Unhandled dispatchable parent: {0}".format(self.parent))
739 def definition(self):
740 """ Generates handle definition e.g. VK_DEFINE_HANDLE(vkInstance) """
741 return "{0}({1})\n".format(self.type, self.name)
743 def is_dispatchable(self):
744 """ Some handles like VkInstance, VkDevice are dispatchable objects,
745 which means they contain a dispatch table of function pointers.
747 return self.type == "VK_DEFINE_HANDLE"
749 def native_handle(self):
750 """ Provide access to the native handle of a dispatchable object.
752 Dispatchable objects wrap an underlying 'native' object.
753 This method provides access to the native object.
755 if not self.is_dispatchable():
758 if self.name == "VkCommandBuffer":
759 return "command_buffer"
760 elif self.name == "VkDevice":
762 elif self.name == "VkInstance":
764 elif self.name == "VkPhysicalDevice":
766 elif self.name == "VkQueue":
769 LOGGER.error("Unhandled native handle for: {0}".format(self.name))
772 class VkMember(object):
773 def __init__(self, const=None, _type=None, pointer=None, name=None, array_len=None, dyn_array_len=None, optional=False,
774 extension_structs=None):
777 self.pointer = pointer
779 self.type_info = None
780 self.array_len = array_len
781 self.dyn_array_len = dyn_array_len
782 self.optional = optional
783 self.extension_structs = extension_structs
785 def __eq__(self, other):
786 """ Compare member based on name against a string.
788 This method is for convenience by VkStruct, which holds a number of members and needs quick checking
789 if certain members exist.
792 if self.name == other:
798 return "{0} {1} {2} {3} {4} {5}".format(self.const, self.type, self.pointer, self.name, self.array_len,
802 def from_xml(member):
803 """ Helper function for parsing a member tag within a struct or union. """
805 name_elem = member.find("name")
806 type_elem = member.find("type")
808 const = member.text.strip() if member.text else None
813 if type_elem is not None:
814 member_type = type_elem.text
815 if type_elem.tail is not None:
816 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
818 # Name of other member within, which stores the number of
819 # elements pointed to be by this member.
820 dyn_array_len = member.get("len", None)
822 if "validextensionstructs" in member.attrib:
823 extension_structs = member.get("validextensionstructs").split(",")
825 extension_structs = None
827 # Some members are optional, which is important for conversion code e.g. not dereference NULL pointer.
828 optional = True if member.get("optional") else False
830 # Usually we need to allocate memory for dynamic arrays. We need to do the same in a few other cases
831 # like for VkCommandBufferBeginInfo.pInheritanceInfo. Just threat such cases as dynamic arrays of
832 # size 1 to simplify code generation.
833 if dyn_array_len is None and pointer is not None:
836 # Some members are arrays, attempt to parse these. Formats include:
837 # <member><type>char</type><name>extensionName</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
838 # <member><type>uint32_t</type><name>foo</name>[4]</member>
839 if name_elem.tail and name_elem.tail[0] == '[':
840 LOGGER.debug("Found array type")
841 enum_elem = member.find("enum")
842 if enum_elem is not None:
843 array_len = enum_elem.text
845 # Remove brackets around length
846 array_len = name_elem.tail.strip("[]")
848 return VkMember(const=const, _type=member_type, pointer=pointer, name=name_elem.text, array_len=array_len,
849 dyn_array_len=dyn_array_len, optional=optional, extension_structs=extension_structs)
851 def copy(self, input, output, direction):
852 """ Helper method for use by conversion logic to generate a C-code statement to copy this member. """
854 if self.needs_conversion():
855 if self.is_dynamic_array():
856 if direction == Direction.OUTPUT:
857 LOGGER.warn("TODO: implement copying of returnedonly dynamic array for {0}.{1}".format(self.type, self.name))
859 # Array length is either a variable name (string) or an int.
860 count = self.dyn_array_len if isinstance(self.dyn_array_len, int) else "{0}{1}".format(input, self.dyn_array_len)
861 return "{0}{1} = convert_{2}_array_win_to_host({3}{1}, {4});\n".format(output, self.name, self.type, input, count)
862 elif self.is_static_array():
863 count = self.array_len
864 if direction == Direction.OUTPUT:
865 # Needed by VkMemoryHeap.memoryHeaps
866 return "convert_{0}_static_array_host_to_win({2}{1}, {3}{1}, {4});\n".format(self.type, self.name, input, output, count)
868 # Nothing needed this yet.
869 LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
871 if direction == Direction.OUTPUT:
872 return "convert_{0}_host_to_win(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
874 return "convert_{0}_win_to_host(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
875 elif self.is_static_array():
876 bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type)
877 return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count)
879 return "{0}{1} = {2}{1};\n".format(output, self.name, input)
881 def definition(self, align=False, conv=False):
882 """ Generate prototype for given function.
885 align (bool, optional): Enable alignment if a type needs it. This adds WINE_VK_ALIGN(8) to a member.
886 conv (bool, optional): Enable conversion if a type needs it. This appends '_host' to the name.
893 if conv and self.is_struct():
894 text += "{0}_host".format(self.type)
898 if self.is_pointer():
899 text += " {0}{1}".format(self.pointer, self.name)
901 if align and self.needs_alignment():
902 text += " WINE_VK_ALIGN(8) " + self.name
904 text += " " + self.name
906 if self.is_static_array():
907 text += "[{0}]".format(self.array_len)
911 def get_conversions(self):
912 """ Return any conversion description for this member and its children when conversion is needed. """
914 # Check if we need conversion either for this member itself or for any child members
915 # in case member represents a struct.
916 if not self.needs_conversion():
921 # Collect any conversion for any member structs.
922 struct = self.type_info["data"]
924 if m.needs_conversion():
925 conversions.extend(m.get_conversions())
927 struct = self.type_info["data"]
928 direction = Direction.OUTPUT if struct.returnedonly else Direction.INPUT
929 if self.is_dynamic_array():
930 conversions.append(ConversionFunction(False, True, direction, struct))
931 elif self.is_static_array():
932 conversions.append(ConversionFunction(True, False, direction, struct))
934 conversions.append(ConversionFunction(False, False, direction, struct))
936 if self.needs_free():
937 conversions.append(FreeFunction(self.is_dynamic_array(), struct))
942 return self.const is not None
944 def is_dynamic_array(self):
945 """ Returns if the member is an array element.
946 Vulkan uses this for dynamically sized arrays for which
947 there is a 'count' parameter.
949 return self.dyn_array_len is not None
952 return self.type_info["category"] == "handle"
954 def is_pointer(self):
955 return self.pointer is not None
957 def is_static_array(self):
958 """ Returns if the member is an array.
959 Vulkan uses this often for fixed size arrays in which the
960 length is part of the member.
962 return self.array_len is not None
965 return self.type_info["category"] == "struct"
968 return self.type_info["category"] == "union"
970 def needs_alignment(self):
971 """ Check if this member needs alignment for 64-bit data.
972 Various structures need alignment on 64-bit variables due
973 to compiler differences on 32-bit between Win32 and Linux.
976 if self.is_pointer():
978 elif self.type == "size_t":
980 elif self.type in ["uint64_t", "VkDeviceSize"]:
982 elif self.is_struct():
983 struct = self.type_info["data"]
984 return struct.needs_alignment()
985 elif self.is_handle():
986 # Dispatchable handles are pointers to objects, while
987 # non-dispatchable are uint64_t and hence need alignment.
988 handle = self.type_info["data"]
989 return False if handle.is_dispatchable() else True
992 def needs_conversion(self):
993 """ Structures requiring alignment, need conversion between win32 and host. """
995 if not self.is_struct():
998 struct = self.type_info["data"]
999 return struct.needs_conversion()
1001 def needs_free(self):
1002 if not self.needs_conversion():
1005 if self.is_dynamic_array():
1008 # TODO: some non-pointer structs and optional pointer structs may need freeing,
1009 # though none of this type have been encountered yet.
1012 def set_type_info(self, type_info):
1013 """ Helper function to set type information from the type registry.
1014 This is needed, because not all type data is available at time of
1017 self.type_info = type_info
1020 class VkParam(object):
1021 """ Helper class which describes a parameter to a function call. """
1023 def __init__(self, type_info, const=None, pointer=None, name=None, array_len=None, dyn_array_len=None):
1026 self.array_len = array_len
1027 self.dyn_array_len = dyn_array_len
1028 self.pointer = pointer
1029 self.type_info = type_info
1030 self.type = type_info["name"] # For convenience
1031 self.handle = type_info["data"] if type_info["category"] == "handle" else None
1032 self.struct = type_info["data"] if type_info["category"] == "struct" else None
1034 self._set_direction()
1035 self._set_format_string()
1036 self._set_conversions()
1039 return "{0} {1} {2} {3} {4}".format(self.const, self.type, self.pointer, self.name, self.array_len, self.dyn_array_len)
1042 def from_xml(param, types):
1043 """ Helper function to create VkParam from xml. """
1045 # Parameter parsing is slightly tricky. All the data is contained within
1046 # a param tag, but some data is within subtags while others are text
1047 # before or after the type tag.
1049 # <param>const <type>char</type>* <name>pLayerName</name></param>
1051 name_elem = param.find("name")
1053 name = name_elem.text
1054 # Tail contains array length e.g. for blendConstants param of vkSetBlendConstants
1055 if name_elem.tail is not None:
1056 array_len = name_elem.tail.strip("[]")
1058 # Name of other parameter in function prototype, which stores the number of
1059 # elements pointed to be by this parameter.
1060 dyn_array_len = param.get("len", None)
1062 const = param.text.strip() if param.text else None
1063 type_elem = param.find("type")
1064 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1066 # Since we have parsed all types before hand, this should not happen.
1067 type_info = types.get(type_elem.text, None)
1068 if type_info is None:
1069 LOGGER.err("type info not found for: {0}".format(type_elem.text))
1071 return VkParam(type_info, const=const, pointer=pointer, name=name, array_len=array_len, dyn_array_len=dyn_array_len)
1073 def _set_conversions(self):
1074 """ Internal helper function to configure any needed conversion functions. """
1076 self.free_func = None
1077 self.input_conv = None
1078 self.output_conv = None
1079 if not self.needs_conversion():
1082 # Input functions require win to host conversion.
1083 if self._direction in [Direction.INPUT, Direction.INPUT_OUTPUT]:
1084 self.input_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.INPUT, self.struct)
1086 # Output functions require host to win conversion.
1087 if self._direction in [Direction.INPUT_OUTPUT, Direction.OUTPUT]:
1088 self.output_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.OUTPUT, self.struct)
1090 # Dynamic arrays, but also some normal structs (e.g. VkCommandBufferBeginInfo) need memory
1091 # allocation and thus some cleanup.
1092 if self.is_dynamic_array() or self.struct.needs_free():
1093 self.free_func = FreeFunction(self.is_dynamic_array(), self.struct)
1095 def _set_direction(self):
1096 """ Internal helper function to set parameter direction (input/output/input_output). """
1098 # The parameter direction needs to be determined from hints in vk.xml like returnedonly,
1099 # parameter constness and other heuristics.
1100 # For now we need to get this right for structures as we need to convert these, we may have
1101 # missed a few other edge cases (e.g. count variables).
1102 # See also https://github.com/KhronosGroup/Vulkan-Docs/issues/610
1104 if not self.is_pointer():
1105 self._direction = Direction.INPUT
1106 elif self.is_const() and self.is_pointer():
1107 self._direction = Direction.INPUT
1108 elif self.is_struct():
1109 if not self.struct.returnedonly:
1110 self._direction = Direction.INPUT
1113 # Returnedonly hints towards output, however in some cases
1114 # it is inputoutput. In particular if pNext / sType exist,
1115 # which are used to link in other structures without having
1116 # to introduce new APIs. E.g. vkGetPhysicalDeviceProperties2KHR.
1117 if "pNext" in self.struct:
1118 self._direction = Direction.INPUT_OUTPUT
1121 self._direction = Direction.OUTPUT
1123 # This should mostly be right. Count variables can be inout, but we don't care about these yet.
1124 self._direction = Direction.OUTPUT
1126 def _set_format_string(self):
1127 """ Internal helper function to be used by constructor to set format string. """
1129 # Determine a format string used by code generation for traces.
1130 # 64-bit types need a conversion function.
1131 self.format_conv = None
1132 if self.is_static_array() or self.is_pointer():
1133 self.format_str = "%p"
1135 if self.type_info["category"] == "bitmask":
1136 self.format_str = "%#x"
1137 elif self.type_info["category"] == "enum":
1138 self.format_str = "%d"
1139 elif self.is_handle():
1140 # We use uint64_t for non-dispatchable handles as opposed to pointers
1141 # for dispatchable handles.
1142 if self.handle.is_dispatchable():
1143 self.format_str = "%p"
1145 self.format_str = "0x%s"
1146 self.format_conv = "wine_dbgstr_longlong({0})"
1147 elif self.type == "float":
1148 self.format_str = "%f"
1149 elif self.type == "int":
1150 self.format_str = "%d"
1151 elif self.type == "int32_t":
1152 self.format_str = "%d"
1153 elif self.type == "size_t":
1154 self.format_str = "0x%s"
1155 self.format_conv = "wine_dbgstr_longlong({0})"
1156 elif self.type in ["uint32_t", "VkBool32"]:
1157 self.format_str = "%u"
1158 elif self.type in ["uint64_t", "VkDeviceSize"]:
1159 self.format_str = "0x%s"
1160 self.format_conv = "wine_dbgstr_longlong({0})"
1161 elif self.type == "HANDLE":
1162 self.format_str = "%p"
1163 elif self.type in ["VisualID", "xcb_visualid_t", "RROutput"]:
1164 # Don't care about Linux specific types.
1165 self.format_str = ""
1167 LOGGER.warn("Unhandled type: {0}".format(self.type_info))
1169 def copy(self, direction):
1170 if direction == Direction.INPUT:
1171 if self.is_dynamic_array():
1172 return " {0}_host = convert_{1}_array_win_to_host({0}, {2});\n".format(self.name, self.type, self.dyn_array_len)
1174 return " convert_{0}_win_to_host({1}, &{1}_host);\n".format(self.type, self.name)
1176 if self.is_dynamic_array():
1177 LOGGER.error("Unimplemented output conversion for: {0}".format(self.name))
1179 return " convert_{0}_host_to_win(&{1}_host, {1});\n".format(self.type, self.name)
1181 def definition(self, postfix=None):
1182 """ Return prototype for the parameter. E.g. 'const char *foo' """
1186 proto += self.const + " "
1190 if self.is_pointer():
1191 proto += " {0}{1}".format(self.pointer, self.name)
1193 proto += " " + self.name
1195 # Allows appeninding something to the variable name useful for
1196 # win32 to host conversion.
1197 if postfix is not None:
1200 if self.is_static_array():
1201 proto += "[{0}]".format(self.array_len)
1205 def direction(self):
1206 """ Returns parameter direction: input, output, input_output.
1208 Parameter direction in Vulkan is not straight-forward, which this function determines.
1211 return self._direction
1213 def format_string(self):
1214 return self.format_str
1216 def dispatch_table(self):
1217 """ Return functions dispatch table pointer for dispatchable objects. """
1219 if not self.is_dispatchable():
1222 return "{0}->{1}".format(self.name, self.handle.dispatch_table())
1224 def format_string(self):
1225 return self.format_str
1228 if self.is_dynamic_array():
1229 if self.struct.returnedonly:
1230 # For returnedonly, counts is stored in a pointer.
1231 return " free_{0}_array({1}_host, *{2});\n".format(self.type, self.name, self.dyn_array_len)
1233 return " free_{0}_array({1}_host, {2});\n".format(self.type, self.name, self.dyn_array_len)
1235 # We are operating on a single structure. Some structs (very rare) contain dynamic members,
1236 # which would need freeing.
1237 if self.struct.needs_free():
1238 return " free_{0}(&{1}_host);\n".format(self.type, self.name)
1241 def get_conversions(self):
1242 """ Get a list of conversions required for this parameter if any.
1243 Parameters which are structures may require conversion between win32
1244 and the host platform. This function returns a list of conversions
1248 if not self.is_struct():
1251 if not self.needs_conversion():
1256 # Collect any member conversions first, so we can guarantee
1257 # those functions will be defined prior to usage by the
1258 # 'parent' param requiring conversion.
1259 for m in self.struct:
1260 if not m.is_struct():
1263 if not m.needs_conversion():
1266 conversions.extend(m.get_conversions())
1268 # Conversion requirements for the 'parent' parameter.
1269 if self.input_conv is not None:
1270 conversions.append(self.input_conv)
1271 if self.output_conv is not None:
1272 conversions.append(self.output_conv)
1273 if self.free_func is not None:
1274 conversions.append(self.free_func)
1279 return self.const is not None
1281 def is_dynamic_array(self):
1282 return self.dyn_array_len is not None
1284 def is_dispatchable(self):
1285 if not self.is_handle():
1288 return self.handle.is_dispatchable()
1290 def is_handle(self):
1291 return self.handle is not None
1293 def is_pointer(self):
1294 return self.pointer is not None
1296 def is_static_array(self):
1297 return self.array_len is not None
1299 def is_struct(self):
1300 return self.struct is not None
1302 def needs_conversion(self):
1303 """ Returns if parameter needs conversion between win32 and host. """
1305 if not self.is_struct():
1308 # VkSparseImageMemoryRequirements is used by vkGetImageSparseMemoryRequirements.
1309 # This function is tricky to wrap, because how to wrap depends on whether
1310 # pSparseMemoryRequirements is NULL or not. Luckily for VkSparseImageMemoryRequirements
1311 # the alignment works out in such a way that no conversion is needed between win32 and Linux.
1312 if self.type == "VkSparseImageMemoryRequirements":
1315 # If a structure needs alignment changes, it means we need to
1316 # perform parameter conversion between win32 and host.
1317 if self.struct.needs_conversion():
1322 def needs_free(self):
1323 return self.free_func is not None
1325 def needs_input_conversion(self):
1326 return self.input_conv is not None
1328 def needs_output_conversion(self):
1329 return self.output_conv is not None
1332 """ Generate spec file entry for this parameter. """
1334 if self.type_info["category"] in ["bitmask", "enum"]:
1336 if self.is_pointer() and self.type == "char":
1338 if self.is_dispatchable() or self.is_pointer() or self.is_static_array():
1340 if self.is_handle() and not self.is_dispatchable():
1342 if self.type == "float":
1344 if self.type in ["int", "int32_t", "size_t", "uint32_t", "VkBool32"]:
1346 if self.type in ["uint64_t", "VkDeviceSize"]:
1349 LOGGER.error("Unhandled spec conversion for type: {0}".format(self.type))
1351 def variable(self, conv=False):
1352 """ Returns 'glue' code during generation of a function call on how to access the variable.
1353 This function handles various scenarios such as 'unwrapping' if dispatchable objects and
1354 renaming of parameters in case of win32 -> host conversion.
1357 conv (bool, optional): Enable conversion if the param needs it. This appends '_host' to the name.
1360 # Hack until we enable allocation callbacks from ICD to application. These are a joy
1361 # to enable one day, because of calling convention conversion.
1362 if "VkAllocationCallbacks" in self.type:
1363 LOGGER.debug("TODO: setting NULL VkAllocationCallbacks for {0}".format(self.name))
1366 # Dispatchable objects wrap the native handle. For thunk generation we
1367 # need to pass the native handle to the native vulkan calls.
1368 if self.is_dispatchable():
1369 return "{0}->{1}".format(self.name, self.handle.native_handle())
1370 elif conv and self.needs_conversion():
1371 if self.is_dynamic_array():
1372 return "{0}_host".format(self.name)
1374 return "&{0}_host".format(self.name)
1379 class VkStruct(Sequence):
1380 """ Class which represents the type union and struct. """
1382 def __init__(self, name, members, returnedonly, union=False):
1384 self.members = members
1385 self.returnedonly = returnedonly
1386 self.required = False
1388 self.type_info = None # To be set later.
1390 def __getitem__(self, i):
1391 return self.members[i]
1394 return len(self.members)
1397 def from_xml(struct):
1398 # Unions and structs are the same parsing wise, but we need to
1399 # know which one we are dealing with later on for code generation.
1400 union = True if struct.attrib["category"] == "union" else False
1402 name = struct.attrib.get("name", None)
1404 # 'Output' structures for which data is filled in by the API are
1405 # marked as 'returnedonly'.
1406 returnedonly = True if struct.attrib.get("returnedonly") else False
1409 for member in struct.findall("member"):
1410 vk_member = VkMember.from_xml(member)
1411 members.append(vk_member)
1413 return VkStruct(name, members, returnedonly, union=union)
1416 def decouple_structs(structs):
1417 """ Helper function which decouples a list of structs.
1418 Structures often depend on other structures. To make the C compiler
1419 happy we need to define 'substructures' first. This function analyzes
1420 the list of structures and reorders them in such a way that they are
1424 tmp_structs = list(structs) # Don't modify the original structures.
1425 decoupled_structs = []
1427 while (len(tmp_structs) > 0):
1428 for struct in tmp_structs:
1431 if not struct.required:
1432 tmp_structs.remove(struct)
1436 if not (m.is_struct() or m.is_union()):
1440 # Check if a struct we depend on has already been defined.
1441 for s in decoupled_structs:
1442 if s.name == m.type:
1447 # Check if the struct we depend on is even in the list of structs.
1448 # If found now, it means we haven't met all dependencies before we
1449 # can operate on the current struct.
1450 # When generating 'host' structs we may not be able to find a struct
1451 # as the list would only contain the structs requiring conversion.
1452 for s in tmp_structs:
1453 if s.name == m.type:
1457 if dependends == False:
1458 decoupled_structs.append(struct)
1459 tmp_structs.remove(struct)
1461 return decoupled_structs
1463 def definition(self, align=False, conv=False, postfix=None):
1464 """ Convert structure to textual definition.
1467 align (bool, optional): enable alignment to 64-bit for win32 struct compatibility.
1468 conv (bool, optional): enable struct conversion if the struct needs it.
1469 postfix (str, optional): text to append to end of struct name, useful for struct renaming.
1473 text = "typedef union {0}".format(self.name)
1475 text = "typedef struct {0}".format(self.name)
1477 if postfix is not None:
1483 if align and m.needs_alignment():
1484 text += " {0};\n".format(m.definition(align=align))
1485 elif conv and m.needs_conversion():
1486 text += " {0};\n".format(m.definition(conv=conv))
1488 text += " {0};\n".format(m.definition())
1490 if postfix is not None:
1491 text += "}} {0}{1};\n\n".format(self.name, postfix)
1493 text += "}} {0};\n\n".format(self.name)
1496 def needs_alignment(self):
1497 """ Check if structure needs alignment for 64-bit data.
1498 Various structures need alignment on 64-bit variables due
1499 to compiler differences on 32-bit between Win32 and Linux.
1502 for m in self.members:
1503 if m.needs_alignment():
1507 def needs_conversion(self):
1508 """ Returns if struct members needs conversion between win32 and host.
1509 Structures need conversion if they contain members requiring alignment
1510 or if they include other structures which need alignment.
1513 if self.needs_alignment():
1516 for m in self.members:
1517 if m.needs_conversion():
1521 def needs_free(self):
1522 """ Check if any struct member needs some memory freeing."""
1524 for m in self.members:
1532 def set_type_info(self, types):
1533 """ Helper function to set type information from the type registry.
1534 This is needed, because not all type data is available at time of
1537 for m in self.members:
1538 type_info = types[m.type]
1539 m.set_type_info(type_info)
1542 class ConversionFunction(object):
1543 def __init__(self, array, dyn_array, direction, struct):
1545 self.direction = direction
1546 self.dyn_array = dyn_array
1547 self.struct = struct
1548 self.type = struct.name
1552 def __eq__(self, other):
1553 if self.name != other.name:
1558 def _generate_array_conversion_func(self):
1559 """ Helper function for generating a conversion function for array structs. """
1561 if self.direction == Direction.OUTPUT:
1562 params = ["const {0}_host *in".format(self.type), "uint32_t count"]
1563 return_type = self.type
1565 params = ["const {0} *in".format(self.type), "uint32_t count"]
1566 return_type = "{0}_host".format(self.type)
1568 # Generate function prototype.
1569 body = "static inline {0} *{1}(".format(return_type, self.name)
1570 body += ", ".join(p for p in params)
1573 body += " {0} *out;\n".format(return_type)
1574 body += " unsigned int i;\n\n"
1575 body += " if (!in) return NULL;\n\n"
1577 body += " out = heap_alloc(count * sizeof(*out));\n"
1579 body += " for (i = 0; i < count; i++)\n"
1582 for m in self.struct:
1583 # TODO: support copying of pNext extension structures!
1584 # Luckily though no extension struct at this point needs conversion.
1585 body += " " + m.copy("in[i].", "out[i].", self.direction)
1588 body += " return out;\n"
1592 def _generate_conversion_func(self):
1593 """ Helper function for generating a conversion function for non-array structs. """
1595 if self.direction == Direction.OUTPUT:
1596 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type)]
1598 params = ["const {0} *in".format(self.type), "{0}_host *out".format(self.type)]
1600 body = "static inline void {0}(".format(self.name)
1602 # Generate parameter list
1603 body += ", ".join(p for p in params)
1606 body += " if (!in) return;\n\n"
1608 if self.direction == Direction.INPUT and "pNext" in self.struct and self.struct.returnedonly:
1609 # We are dealing with an input_output parameter. For these we only need to copy
1610 # pNext and sType as the other fields are filled in by the host. We do potentially
1611 # have to iterate over pNext and perform conversions based on switch(sType)!
1612 # Luckily though no extension structs at this point need conversion.
1613 # TODO: support copying of pNext extension structures!
1614 body += " out->pNext = in->pNext;\n"
1615 body += " out->sType = in->sType;\n"
1617 for m in self.struct:
1618 # TODO: support copying of pNext extension structures!
1619 body += " " + m.copy("in->", "out->", self.direction)
1624 def _generate_static_array_conversion_func(self):
1625 """ Helper function for generating a conversion function for array structs. """
1627 if self.direction == Direction.OUTPUT:
1628 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
1629 return_type = self.type
1631 params = ["const {0} *in".format(self.type), "{0} *out_host".format(self.type), "uint32_t count"]
1632 return_type = "{0}_host".format(self.type)
1634 # Generate function prototype.
1635 body = "static inline void {0}(".format(self.name)
1636 body += ", ".join(p for p in params)
1638 body += " unsigned int i;\n\n"
1639 body += " if (!in) return;\n\n"
1640 body += " for (i = 0; i < count; i++)\n"
1643 for m in self.struct:
1644 # TODO: support copying of pNext extension structures!
1645 body += " " + m.copy("in[i].", "out[i].", self.direction)
1651 def _set_name(self):
1652 if self.direction == Direction.INPUT:
1654 name = "convert_{0}_static_array_win_to_host".format(self.type)
1655 elif self.dyn_array:
1656 name = "convert_{0}_array_win_to_host".format(self.type)
1658 name = "convert_{0}_win_to_host".format(self.type)
1659 else: # Direction.OUTPUT
1661 name = "convert_{0}_static_array_host_to_win".format(self.type)
1662 elif self.dyn_array:
1663 name = "convert_{0}_array_host_to_win".format(self.type)
1665 name = "convert_{0}_host_to_win".format(self.type)
1669 def definition(self):
1671 return self._generate_static_array_conversion_func()
1672 elif self.dyn_array:
1673 return self._generate_array_conversion_func()
1675 return self._generate_conversion_func()
1678 class FreeFunction(object):
1679 def __init__(self, dyn_array, struct):
1680 self.dyn_array = dyn_array
1681 self.struct = struct
1682 self.type = struct.name
1685 self.name = "free_{0}_array".format(self.type)
1687 self.name = "free_{0}".format(self.type)
1689 def __eq__(self, other):
1690 if self.name == other.name:
1695 def _generate_array_free_func(self):
1696 """ Helper function for cleaning up temporary buffers required for array conversions. """
1698 # Generate function prototype.
1699 body = "static inline void {0}({1}_host *in, uint32_t count)\n{{\n".format(self.name, self.type)
1701 # E.g. VkGraphicsPipelineCreateInfo_host needs freeing for pStages.
1702 if self.struct.needs_free():
1703 body += " unsigned int i;\n\n"
1704 body += " if (!in) return;\n\n"
1705 body += " for (i = 0; i < count; i++)\n"
1708 for m in self.struct:
1709 if m.needs_conversion() and m.is_dynamic_array():
1711 # Add a cast to ignore const on conversion structs we allocated ourselves.
1712 body += " free_{0}_array(({0}_host *)in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
1714 body += " free_{0}_array(in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
1715 elif m.needs_conversion():
1716 LOGGER.error("Unhandled conversion for {0}".format(m.name))
1719 body += " if (!in) return;\n\n"
1721 body += " heap_free(in);\n"
1726 def _generate_free_func(self):
1727 # E.g. VkCommandBufferBeginInfo.pInheritanceInfo needs freeing.
1728 if not self.struct.needs_free():
1731 # Generate function prototype.
1732 body = "static inline void {0}({1}_host *in)\n{{\n".format(self.name, self.type)
1734 for m in self.struct:
1735 if m.needs_conversion() and m.is_dynamic_array():
1736 count = m.dyn_array_len if isinstance(m.dyn_array_len, int) else "in->{0}".format(m.dyn_array_len)
1738 # Add a cast to ignore const on conversion structs we allocated ourselves.
1739 body += " free_{0}_array(({0}_host *)in->{1}, {2});\n".format(m.type, m.name, count)
1741 body += " free_{0}_array(in->{1}, {2});\n".format(m.type, m.name, count)
1746 def definition(self):
1748 return self._generate_array_free_func()
1750 # Some structures need freeing too if they contain dynamic arrays.
1751 # E.g. VkCommandBufferBeginInfo
1752 return self._generate_free_func()
1755 class VkGenerator(object):
1756 def __init__(self, registry):
1757 self.registry = registry
1759 # Build a list conversion functions for struct conversion.
1760 self.conversions = []
1761 self.host_structs = []
1762 for func in self.registry.funcs.values():
1763 if not func.is_required():
1766 if not func.needs_conversion():
1769 conversions = func.get_conversions()
1770 for conv in conversions:
1771 # Pull in any conversions for vulkan_thunks.c.
1772 if func.needs_thunk():
1773 # Append if we don't already have this conversion.
1774 if not any(c == conv for c in self.conversions):
1775 self.conversions.append(conv)
1777 # Structs can be used in different ways by different conversions
1778 # e.g. array vs non-array. Just make sure we pull in each struct once.
1779 if not any(s.name == conv.struct.name for s in self.host_structs):
1780 self.host_structs.append(conv.struct)
1782 def generate_thunks_c(self, f, prefix):
1783 f.write("/* Automatically generated from Vulkan vk.xml; DO NOT EDIT! */\n\n")
1785 f.write("#include \"config.h\"\n")
1786 f.write("#include \"wine/port.h\"\n\n")
1788 f.write("#include \"wine/debug.h\"\n")
1789 f.write("#include \"wine/heap.h\"\n")
1790 f.write("#include \"wine/vulkan.h\"\n")
1791 f.write("#include \"wine/vulkan_driver.h\"\n")
1792 f.write("#include \"vulkan_private.h\"\n\n")
1794 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
1796 # Generate any conversion helper functions.
1797 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
1798 for conv in self.conversions:
1799 f.write(conv.definition())
1800 f.write("#endif /* USE_STRUCT_CONVERSION */\n\n")
1802 # Create thunks for instance and device functions.
1803 # Global functions don't go through the thunks.
1804 for vk_func in self.registry.funcs.values():
1805 if not vk_func.is_required():
1808 if vk_func.is_global_func():
1811 if not vk_func.needs_thunk():
1814 # Exports symbols for Core functions.
1815 if not vk_func.is_core_func():
1817 f.write(vk_func.thunk(prefix=prefix, call_conv="WINAPI"))
1819 f.write("static const struct vulkan_func vk_device_dispatch_table[] =\n{\n")
1820 for vk_func in self.registry.device_funcs:
1821 if not vk_func.is_required():
1824 f.write(" {{\"{0}\", &{1}{0}}},\n".format(vk_func.name, prefix))
1827 f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
1828 for vk_func in self.registry.instance_funcs:
1829 if not vk_func.is_required():
1832 f.write(" {{\"{0}\", &{1}{0}}},\n".format(vk_func.name, prefix))
1835 f.write("void *wine_vk_get_device_proc_addr(const char *name)\n")
1837 f.write(" unsigned int i;\n")
1838 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_dispatch_table); i++)\n")
1840 f.write(" if (strcmp(vk_device_dispatch_table[i].name, name) == 0)\n")
1842 f.write(" TRACE(\"Found name=%s in device table\\n\", debugstr_a(name));\n")
1843 f.write(" return vk_device_dispatch_table[i].func;\n")
1846 f.write(" return NULL;\n")
1849 f.write("void *wine_vk_get_instance_proc_addr(const char *name)\n")
1851 f.write(" unsigned int i;\n")
1852 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_dispatch_table); i++)\n")
1854 f.write(" if (strcmp(vk_instance_dispatch_table[i].name, name) == 0)\n")
1856 f.write(" TRACE(\"Found name=%s in instance table\\n\", debugstr_a(name));\n")
1857 f.write(" return vk_instance_dispatch_table[i].func;\n")
1860 f.write(" return NULL;\n")
1863 # Create array of device extensions.
1864 f.write("static const char * const vk_device_extensions[] =\n{\n")
1865 for ext in self.registry.extensions:
1866 if ext["type"] != "device":
1869 f.write(" \"{0}\",\n".format(ext["name"]))
1872 # Create array of instance extensions.
1873 f.write("static const char * const vk_instance_extensions[] =\n{\n")
1874 for ext in self.registry.extensions:
1875 if ext["type"] != "instance":
1878 f.write(" \"{0}\",\n".format(ext["name"]))
1881 f.write("BOOL wine_vk_device_extension_supported(const char *name)\n")
1883 f.write(" unsigned int i;\n")
1884 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_extensions); i++)\n")
1886 f.write(" if (strcmp(vk_device_extensions[i], name) == 0)\n")
1887 f.write(" return TRUE;\n")
1889 f.write(" return FALSE;\n")
1892 f.write("BOOL wine_vk_instance_extension_supported(const char *name)\n")
1894 f.write(" unsigned int i;\n")
1895 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_extensions); i++)\n")
1897 f.write(" if (strcmp(vk_instance_extensions[i], name) == 0)\n")
1898 f.write(" return TRUE;\n")
1900 f.write(" return FALSE;\n")
1903 def generate_thunks_h(self, f, prefix):
1904 f.write("/* Automatically generated from Vulkan vk.xml; DO NOT EDIT! */\n\n")
1906 f.write("#ifndef __WINE_VULKAN_THUNKS_H\n")
1907 f.write("#define __WINE_VULKAN_THUNKS_H\n\n")
1909 f.write("/* Perform vulkan struct conversion on 32-bit x86 platforms. */\n")
1910 f.write("#if defined(__i386__)\n")
1911 f.write("#define USE_STRUCT_CONVERSION\n")
1912 f.write("#endif\n\n")
1914 f.write("/* For use by vk_icdGetInstanceProcAddr / vkGetInstanceProcAddr */\n")
1915 f.write("void *wine_vk_get_device_proc_addr(const char *name) DECLSPEC_HIDDEN;\n")
1916 f.write("void *wine_vk_get_instance_proc_addr(const char *name) DECLSPEC_HIDDEN;\n\n")
1918 f.write("BOOL wine_vk_device_extension_supported(const char *name) DECLSPEC_HIDDEN;\n")
1919 f.write("BOOL wine_vk_instance_extension_supported(const char *name) DECLSPEC_HIDDEN;\n\n")
1921 # Generate prototypes for device and instance functions requiring a custom implementation.
1922 f.write("/* Functions for which we have custom implementations outside of the thunks. */\n")
1923 for vk_func in self.registry.funcs.values():
1924 if not vk_func.is_required() or vk_func.is_global_func() or vk_func.needs_thunk():
1927 if vk_func.is_core_func():
1928 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix="wine_")))
1930 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix="wine_", postfix="DECLSPEC_HIDDEN")))
1933 for struct in self.host_structs:
1934 f.write(struct.definition(align=False, conv=True, postfix="_host"))
1937 f.write("/* For use by vkDevice and children */\n")
1938 f.write("struct vulkan_device_funcs\n{\n")
1939 for vk_func in self.registry.device_funcs:
1940 if not vk_func.is_required():
1943 if not vk_func.needs_dispatch():
1944 LOGGER.debug("skipping {0} in vulkan_device_funcs".format(vk_func.name))
1947 if vk_func.needs_conversion():
1948 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
1949 f.write(" {0};\n".format(vk_func.pfn(conv=True)))
1951 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
1954 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
1957 f.write("/* For use by vkInstance and children */\n")
1958 f.write("struct vulkan_instance_funcs\n{\n")
1959 for vk_func in self.registry.instance_funcs:
1960 if not vk_func.is_required():
1963 if not vk_func.needs_dispatch():
1964 LOGGER.debug("skipping {0} in vulkan_instance_funcs".format(vk_func.name))
1967 if vk_func.needs_conversion():
1968 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
1969 f.write(" {0};\n".format(vk_func.pfn(conv=True)))
1971 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
1974 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
1977 f.write("#define ALL_VK_DEVICE_FUNCS() \\\n")
1979 for vk_func in self.registry.device_funcs:
1980 if not vk_func.is_required():
1983 if not vk_func.needs_dispatch():
1984 LOGGER.debug("skipping {0} in ALL_VK_DEVICE_FUNCS".format(vk_func.name))
1988 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
1991 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
1994 f.write("#define ALL_VK_INSTANCE_FUNCS() \\\n")
1996 for vk_func in self.registry.instance_funcs:
1997 if not vk_func.is_required():
2000 if not vk_func.needs_dispatch():
2001 LOGGER.debug("skipping {0} in ALL_VK_INSTANCE_FUNCS".format(vk_func.name))
2005 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
2008 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
2011 f.write("#endif /* __WINE_VULKAN_THUNKS_H */\n")
2013 def generate_vulkan_h(self, f):
2014 f.write("/* Automatically generated from Vulkan vk.xml; DO NOT EDIT! */\n\n")
2015 f.write("#ifndef __WINE_VULKAN_H\n")
2016 f.write("#define __WINE_VULKAN_H\n\n")
2018 f.write("#include <windef.h>\n")
2019 f.write("#include <stdint.h>\n\n")
2021 f.write("#ifndef VKAPI_CALL\n")
2022 f.write("#define VKAPI_CALL __stdcall\n")
2023 f.write("#endif\n\n")
2025 f.write("#ifndef VKAPI_PTR\n")
2026 f.write("#define VKAPI_PTR VKAPI_CALL\n")
2027 f.write("#endif\n\n")
2029 f.write("/* Callers can override WINE_VK_ALIGN if they want 'host' headers. */\n")
2030 f.write("#ifndef WINE_VK_ALIGN\n")
2031 f.write("#define WINE_VK_ALIGN DECLSPEC_ALIGN\n")
2032 f.write("#endif\n\n")
2034 # The overall strategy is to define independent constants and datatypes,
2035 # prior to complex structures and function calls to avoid forward declarations.
2036 for const in self.registry.consts:
2037 # For now just generate things we may not need. The amount of parsing needed
2038 # to get some of the info is tricky as you need to figure out which structure
2039 # references a certain constant.
2040 f.write(const.definition())
2043 for define in self.registry.defines:
2044 f.write(define.definition())
2046 for handle in self.registry.handles:
2048 f.write(handle.definition())
2051 for base_type in self.registry.base_types:
2052 f.write(base_type.definition())
2055 for bitmask in self.registry.bitmasks:
2056 f.write(bitmask.definition())
2059 # Define enums, this includes values for some of the bitmask types as well.
2060 for enum in self.registry.enums.values():
2062 f.write(enum.definition())
2064 for fp in self.registry.funcpointers:
2066 f.write(fp.definition())
2069 # This generates both structures and unions. Since structures
2070 # may depend on other structures/unions, we need a list of
2071 # decoupled structs.
2072 # Note: unions are stored in structs for dependency reasons,
2073 # see comment in parsing section.
2074 structs = VkStruct.decouple_structs(self.registry.structs)
2075 for struct in structs:
2076 LOGGER.debug("Generating struct: {0}".format(struct.name))
2077 f.write(struct.definition(align=True))
2079 for func in self.registry.funcs.values():
2080 if not func.is_required():
2081 LOGGER.debug("Skipping API definition for: {0}".format(func.name))
2084 LOGGER.debug("Generating API definition for: {0}".format(func.name))
2085 f.write("{0};\n".format(func.prototype(call_conv="VKAPI_CALL")))
2088 f.write("#endif /* __WINE_VULKAN_H */\n")
2090 def generate_vulkan_driver_h(self, f):
2091 f.write("/* Automatically generated from Vulkan vk.xml; DO NOT EDIT! */\n\n")
2092 f.write("#ifndef __WINE_VULKAN_DRIVER_H\n")
2093 f.write("#define __WINE_VULKAN_DRIVER_H\n\n")
2095 f.write("/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */\n")
2096 f.write("#define WINE_VULKAN_DRIVER_VERSION {0}\n\n".format(DRIVER_VERSION))
2098 f.write("struct vulkan_funcs\n{\n")
2099 f.write(" /* Vulkan global functions. These are the only calls at this point a graphics driver\n")
2100 f.write(" * needs to provide. Other function calls will be provided indirectly by dispatch\n")
2101 f.write(" * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice.\n")
2104 for vk_func in self.registry.funcs.values():
2105 if not vk_func.is_required() or not vk_func.is_driver_func():
2109 # Avoid PFN_vkVoidFunction in driver interface as Vulkan likes to put calling convention
2110 # stuff in there. For simplicity substitute with "void *".
2111 pfn = pfn.replace("PFN_vkVoidFunction", "void *")
2112 f.write(" {0};\n".format(pfn))
2115 f.write("extern const struct vulkan_funcs * CDECL __wine_get_vulkan_driver(HDC hdc, UINT version);\n\n")
2116 f.write("#endif /* __WINE_VULKAN_DRIVER_H */\n")
2118 def generate_vulkan_spec(self, f):
2119 f.write("# Automatically generated from Vulkan vk.xml; DO NOT EDIT!\n\n")
2120 f.write("@ stdcall vk_icdGetInstanceProcAddr(ptr str) wine_vk_icdGetInstanceProcAddr\n")
2121 f.write("@ stdcall vk_icdNegotiateLoaderICDInterfaceVersion(ptr) wine_vk_icdNegotiateLoaderICDInterfaceVersion\n")
2123 # Export symbols for all Vulkan Core functions.
2124 for func in self.registry.funcs.values():
2125 if not func.is_core_func():
2128 # Not an ICD level function.
2129 if func.name == "vkEnumerateInstanceLayerProperties":
2132 # We support all Core functions except for VK_KHR_display* APIs.
2133 # Create stubs for unsupported Core functions.
2134 if func.is_required():
2135 f.write(func.spec(prefix="wine_"))
2137 f.write("@ stub {0}\n".format(func.name))
2140 class VkRegistry(object):
2141 def __init__(self, reg_filename):
2142 # Used for storage of type information.
2143 self.base_types = None
2144 self.bitmasks = None
2148 self.funcpointers = None
2152 # We aggregate all types in here for cross-referencing.
2156 # Overall strategy for parsing the registry is to first
2157 # parse all type / function definitions. Then parse
2158 # features and extensions to decide which types / functions
2159 # to actually 'pull in' for code generation. For each type or
2160 # function call we want we set a member 'required' to True.
2161 tree = ET.parse(reg_filename)
2162 root = tree.getroot()
2163 self._parse_enums(root)
2164 self._parse_types(root)
2165 self._parse_commands(root)
2167 # Pull in any required types and functions.
2168 self._parse_features(root)
2169 self._parse_extensions(root)
2171 def _mark_command_required(self, command):
2172 """ Helper function to mark a certain command and the datatypes it needs as required."""
2173 def mark_bitmask_dependencies(bitmask, types):
2174 if bitmask.requires is not None:
2175 types[bitmask.requires]["data"].required = True
2177 def mark_funcpointer_dependencies(fp, types):
2178 for m in fp.members:
2179 type_info = types[m.type]
2181 # Complex types have a matching definition e.g. VkStruct.
2182 # Not needed for base types such as uint32_t.
2183 if "data" in type_info:
2184 types[m.type]["data"].required = True
2186 def mark_struct_dependencies(struct, types):
2188 type_info = types[m.type]
2190 # Complex types have a matching definition e.g. VkStruct.
2191 # Not needed for base types such as uint32_t.
2192 if "data" in type_info:
2193 types[m.type]["data"].required = True
2195 if type_info["category"] == "struct":
2197 mark_struct_dependencies(type_info["data"], types)
2198 elif type_info["category"] == "funcpointer":
2199 mark_funcpointer_dependencies(type_info["data"], types)
2200 elif type_info["category"] == "bitmask":
2201 mark_bitmask_dependencies(type_info["data"], types)
2203 func = self.funcs[command]
2204 func.required = True
2206 # Pull in return type
2207 if func.type != "void":
2208 self.types[func.type]["data"].required = True
2210 # Analyze parameter dependencies and pull in any type needed.
2211 for p in func.params:
2212 type_info = self.types[p.type]
2214 # Check if we are dealing with a complex type e.g. VkEnum, VkStruct and others.
2215 if "data" not in type_info:
2218 # Mark the complex type as required.
2219 type_info["data"].required = True
2220 if type_info["category"] == "struct":
2221 struct = type_info["data"]
2222 mark_struct_dependencies(struct, self.types)
2224 def _parse_commands(self, root):
2225 """ Parse command section containing the Vulkan function calls. """
2227 commands = root.findall("./commands/")
2228 for command in commands:
2229 func = VkFunction.from_xml(command, self.types)
2230 funcs[func.name] = func
2232 # To make life easy for the code generation, separate all function
2233 # calls out in the 3 types of vulkan functions: device, global and instance.
2237 for func in funcs.values():
2238 if func.is_device_func():
2239 device_funcs.append(func)
2240 elif func.is_global_func():
2241 global_funcs.append(func)
2243 instance_funcs.append(func)
2245 # Sort function lists by name and store them.
2246 self.device_funcs = sorted(device_funcs, key=lambda func: func.name)
2247 self.global_funcs = sorted(global_funcs, key=lambda func: func.name)
2248 self.instance_funcs = sorted(instance_funcs, key=lambda func: func.name)
2250 # The funcs dictionary is used as a convenient way to lookup function
2251 # calls when needed e.g. to adjust member variables.
2252 self.funcs = OrderedDict(sorted(funcs.items()))
2254 def _parse_enums(self, root):
2255 """ Parse enums section or better described as constants section. """
2258 for enum in root.findall("./enums"):
2259 name = enum.attrib.get("name")
2260 _type = enum.attrib.get("type")
2262 if _type in ("enum", "bitmask"):
2263 enums[name] = VkEnum.from_xml(enum)
2265 # If no type is set, we are dealing with API constants.
2267 for value in enum.findall("enum"):
2268 self.consts.append(VkConstant(value.attrib.get("name"), value.attrib.get("value")))
2270 self.enums = OrderedDict(sorted(enums.items()))
2272 def _parse_extensions(self, root):
2273 """ Parse extensions section and pull in any types and commands for this extensioin. """
2275 exts = root.findall("./extensions/extension")
2277 ext_name = ext.attrib["name"]
2279 # Set extension name on any functions calls part of this extension as we
2280 # were not aware of the name during initial parsing.
2281 commands = ext.findall("require/command")
2282 for command in commands:
2283 cmd_name = command.attrib["name"]
2284 self.funcs[cmd_name].extension = ext_name
2286 # Some extensions are not ready or have numbers reserved as a place holder.
2287 if ext.attrib["supported"] == "disabled":
2288 LOGGER.debug("Skipping disabled extension: {0}".format(ext_name))
2291 # Disable highly experimental extensions as the APIs are unstable and can
2292 # change between minor Vulkan revisions until API is final and becomes KHR
2294 if "KHX" in ext_name or "NVX" in ext_name:
2295 LOGGER.debug("Skipping experimental extension: {0}".format(ext_name))
2298 # Instance extensions often require a custom implementation, so filter.
2299 ext_type = ext.attrib["type"]
2300 if ext_type == "instance" and not ext_name in SUPPORTED_INSTANCE_EXTENSIONS:
2301 LOGGER.debug("Skipping instance extension: {0}".format(ext_name))
2304 # We disable some extensions as either we haven't implemented
2305 # support yet or because they are for platforms other than win32.
2306 if ext_name in BLACKLISTED_EXTENSIONS:
2307 LOGGER.debug("Skipping blacklisted extension: {0}".format(ext_name))
2309 elif "requires" in ext.attrib:
2310 # Check if this extension builds on top of another blacklisted
2312 requires = ext.attrib["requires"].split(",")
2313 if len(set(requires).intersection(BLACKLISTED_EXTENSIONS)) > 0:
2316 LOGGER.debug("Loading extension: {0}".format(ext_name))
2318 # Extensions can add enum values to Core / extension enums, so add these.
2319 enums = ext.findall("require/enum")
2320 for enum_elem in enums:
2321 if "bitpos" in enum_elem.keys():
2322 # We need to add an extra value to an existing enum type.
2323 # E.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG to VkFormatFeatureFlagBits.
2324 type_name = enum_elem.attrib["extends"]
2325 enum = self.types[type_name]["data"]
2326 enum.add(VkEnumValue(enum_elem.attrib["name"], 1 << int(enum_elem.attrib["bitpos"]), hex=True))
2327 elif "offset" in enum_elem.keys():
2328 ext_number = int(ext.attrib["number"])
2329 offset = int(enum_elem.attrib["offset"])
2330 value = EXT_BASE + (ext_number - 1) * EXT_BLOCK_SIZE + offset
2332 # Deal with negative values.
2333 direction = enum_elem.attrib.get("dir")
2334 if direction is not None:
2337 type_name = enum_elem.attrib["extends"]
2338 enum = self.types[type_name]["data"]
2339 enum.add(VkEnumValue(enum_elem.attrib["name"], value))
2341 elif "value" in enum_elem.keys():
2342 # For now skip, it mostly contains extension name and version info.
2345 # This seems to be used to pull in constants e.g. VK_MAX_DEVICE_GROUP_KHX
2348 # Store a list with extensions.
2349 ext_info = {"name" : ext_name, "type" : ext_type}
2350 extensions.append(ext_info)
2352 # Pull in any commands we need. We infer types to pull in from the command
2354 for command in commands:
2355 cmd_name = command.attrib["name"]
2356 self._mark_command_required(cmd_name)
2358 # Sort in alphabetical order.
2359 self.extensions = sorted(extensions, key=lambda ext: ext["name"])
2361 def _parse_features(self, root):
2362 """ Parse the feature section, which describes Core commands and types needed. """
2363 requires = root.findall("./feature/require")
2365 for require in requires:
2366 LOGGER.info("Including features for {0}".format(require.attrib.get("comment")))
2368 # Only deal with command. Other values which appear are enum and type for pulling in some
2369 # constants and macros. Tricky to parse, so don't bother right now, we will generate them
2371 name = tag.attrib["name"]
2372 if tag.tag == "command":
2373 self._mark_command_required(name)
2374 elif tag.tag == "enum":
2375 # We could pull in relevant constants here. Unfortunately
2376 # this only gets half of them pulled in as others indirectly
2377 # get pulled in through structures. Constants don't harm us,
2380 elif tag.tag == "type":
2381 # Pull in types which may not have been pulled in through commands.
2383 # Skip pull in for vk_platform.h for now.
2384 if name == "vk_platform":
2387 type_info = self.types[name]
2388 type_info["data"].required = True
2390 def _parse_types(self, root):
2391 """ Parse types section, which contains all data types e.g. structs, typedefs etcetera. """
2392 types = root.findall("./types/type")
2403 type_info["category"] = t.attrib.get("category", None)
2405 if type_info["category"] in ["include"]:
2408 if type_info["category"] == "basetype":
2409 name = t.find("name").text
2410 _type = t.find("type").text
2411 basetype = VkBaseType(name, _type)
2412 base_types.append(basetype)
2413 type_info["data"] = basetype
2415 if type_info["category"] == "bitmask":
2416 name = t.find("name").text
2417 _type = t.find("type").text
2419 # Most bitmasks have a requires attribute used to pull in
2420 # required '*FlagBits" enum.
2421 requires = t.attrib.get("requires")
2422 bitmask = VkBaseType(name, _type, requires=requires)
2423 bitmasks.append(bitmask)
2424 type_info["data"] = bitmask
2426 if type_info["category"] == "define":
2427 name = t.attrib.get("name")
2428 define = VkDefine.from_xml(t)
2429 defines.append(define)
2430 type_info["data"] = define
2432 if type_info["category"] == "enum":
2433 name = t.attrib.get("name")
2434 # The type section only contains enum names, not the actual definition.
2435 # Since we already parsed the enum before, just link it in.
2437 type_info["data"] = self.enums[name]
2438 except KeyError as e:
2439 # Not all enums seem to be defined yet, typically that's for
2440 # ones ending in 'FlagBits' where future extensions may add
2442 type_info["data"] = None
2444 if type_info["category"] == "funcpointer":
2445 funcpointer = VkFunctionPointer.from_xml(t)
2446 funcpointers.append(funcpointer)
2447 type_info["data"] = funcpointer
2449 if type_info["category"] == "handle":
2450 handle = VkHandle.from_xml(t)
2451 handles.append(handle)
2452 type_info["data"] = handle
2454 if type_info["category"] in ["struct", "union"]:
2455 # We store unions among structs as some structs depend
2456 # on unions. The types are very similar in parsing and
2457 # generation anyway. The official vulkan scripts use
2458 # a similar kind of hack.
2459 struct = VkStruct.from_xml(t)
2460 structs.append(struct)
2461 type_info["data"] = struct
2463 # Name is in general within a name tag else it is an optional
2464 # attribute on the type tag.
2465 name_elem = t.find("name")
2466 if name_elem is not None:
2467 type_info["name"] = name_elem.text
2469 type_info["name"] = t.attrib.get("name", None)
2471 # Store all type data in a shared dictionary, so we can easily
2472 # look up information for a given type. There are no duplicate
2474 self.types[type_info["name"]] = type_info
2476 # We need detailed type information during code generation
2477 # on structs for alignment reasons. Unfortunately structs
2478 # are parsed among other types, so there is no guarantee
2479 # that any types needed have been parsed already, so set
2481 for struct in structs:
2482 struct.set_type_info(self.types)
2484 # Guarantee everything is sorted, so code generation doesn't have
2485 # to deal with this.
2486 self.base_types = sorted(base_types, key=lambda base_type: base_type.name)
2487 self.bitmasks = sorted(bitmasks, key=lambda bitmask: bitmask.name)
2488 self.defines = defines
2489 self.funcpointers = sorted(funcpointers, key=lambda fp: fp.name)
2490 self.handles = sorted(handles, key=lambda handle: handle.name)
2491 self.structs = sorted(structs, key=lambda struct: struct.name)
2495 parser = argparse.ArgumentParser()
2496 parser.add_argument("-v", "--verbose", action="count", default=0, help="increase output verbosity")
2498 args = parser.parse_args()
2499 if args.verbose == 0:
2500 LOGGER.setLevel(logging.WARNING)
2501 elif args.verbose == 1:
2502 LOGGER.setLevel(logging.INFO)
2504 LOGGER.setLevel(logging.DEBUG)
2506 registry = VkRegistry("vk.xml")
2507 generator = VkGenerator(registry)
2509 with open(WINE_VULKAN_H, "w") as f:
2510 generator.generate_vulkan_h(f)
2512 with open(WINE_VULKAN_DRIVER_H, "w") as f:
2513 generator.generate_vulkan_driver_h(f)
2515 with open(WINE_VULKAN_THUNKS_H, "w") as f:
2516 generator.generate_thunks_h(f, "wine_")
2518 with open(WINE_VULKAN_THUNKS_C, "w") as f:
2519 generator.generate_thunks_c(f, "wine_")
2521 with open(WINE_VULKAN_SPEC, "w") as f:
2522 generator.generate_vulkan_spec(f)
2524 if __name__ == "__main__":