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_THUNKS_C = "vulkan_thunks.c"
69 WINE_VULKAN_THUNKS_H = "vulkan_thunks.h"
71 # Extension enum values start at a certain offset (EXT_BASE).
72 # Relative to the offset each extension has a block (EXT_BLOCK_SIZE)
74 # Start for a given extension is:
75 # EXT_BASE + (extension_number-1) * EXT_BLOCK_SIZE
79 # In general instance extensions can't be automatically generated
80 # and need custom wrappers due to e.g. win32 / X11 specific code.
81 # List of supported instance extensions.
82 SUPPORTED_EXTENSIONS = [
83 "VK_KHR_get_physical_device_properties2",
85 "VK_KHR_win32_surface",
89 # Functions part of our winevulkan graphics driver interface.
90 # DRIVER_VERSION should be bumped on any change to driver interface
91 # in FUNCTION_OVERRIDES
94 # Table of functions for which we have a special implementation.
95 # This are regular device / instance functions for which we need
96 # to more work compared to a regular thunk or because they are
97 # part of the driver interface.
98 # - dispatch set whether we need a function pointer in the device
99 # / instance dispatch table.
100 # - driver sets whether the api is part of the driver interface.
101 # - thunk sets whether to create a thunk in vulkan_thunks.c.
102 FUNCTION_OVERRIDES = {
104 "vkCreateInstance" : {"dispatch" : False, "driver" : True, "thunk" : False},
105 "vkEnumerateInstanceExtensionProperties" : {"dispatch" : False, "driver" : True, "thunk" : False},
106 "vkGetInstanceProcAddr": {"dispatch" : False, "driver" : True, "thunk" : False},
109 "vkCreateDevice" : {"dispatch" : True, "driver" : False, "thunk" : False},
110 "vkDestroyInstance" : {"dispatch" : True, "driver" : True, "thunk" : False },
111 "vkEnumerateDeviceExtensionProperties" : {"dispatch" : True, "driver" : False, "thunk" : False},
112 "vkEnumeratePhysicalDevices" : {"dispatch" : True, "driver" : False, "thunk" : False},
115 "vkAllocateCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : False},
116 "vkCmdExecuteCommands" : {"dispatch" : True, "driver" : False, "thunk" : False},
117 "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : False},
118 "vkFreeCommandBuffers" : {"dispatch" : True, "driver" : False, "thunk" : False},
119 "vkGetDeviceProcAddr" : {"dispatch" : True, "driver" : True, "thunk" : False},
120 "vkGetDeviceQueue" : {"dispatch": True, "driver" : False, "thunk" : False},
121 "vkQueueSubmit" : {"dispatch": True, "driver" : False, "thunk" : False},
124 "vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
125 "vkGetPhysicalDeviceSurfaceSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
126 "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
127 "vkGetPhysicalDeviceSurfaceFormatsKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
128 "vkGetPhysicalDeviceSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
130 # VK_KHR_win32_surface
131 "vkCreateWin32SurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
132 "vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
135 "vkAcquireNextImageKHR": {"dispatch" : True, "driver" : True, "thunk" : False},
136 "vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
137 "vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
138 "vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : False},
139 "vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : False},
143 class Direction(Enum):
144 """ Parameter direction: input, output, input_output. """
150 class VkBaseType(object):
151 def __init__(self, name, _type, requires=None):
152 """ Vulkan base type class.
154 VkBaseType is mostly used by Vulkan to define its own
155 base types like VkFlags through typedef out of e.g. uint32_t.
158 name (:obj:'str'): Name of the base type.
159 _type (:obj:'str'): Underlaying type
160 requires (:obj:'str', optional): Other types required.
161 Often bitmask values pull in a *FlagBits type.
165 self.requires = requires
166 self.required = False
168 def definition(self):
169 text = "typedef {0} {1};\n".format(self.type, self.name)
173 class VkConstant(object):
174 def __init__(self, name, value):
178 def definition(self):
179 text = "#define {0} {1}\n".format(self.name, self.value)
183 class VkDefine(object):
184 def __init__(self, name, value):
189 def from_xml(define):
190 name_elem = define.find("name")
192 if name_elem is None:
193 # <type category="define" name="some_name">some_value</type>
194 # At the time of writing there is only 1 define of this category
195 # 'VK_DEFINE_NON_DISPATCHABLE_HANDLE'.
196 name = define.attrib.get("name")
198 # We override behavior of VK_DEFINE_NON_DISPATCHABLE handle as the default
199 # definition various between 64-bit (uses pointers) and 32-bit (uses uint64_t).
200 # This complicates TRACEs in the thunks, so just use uint64_t.
201 if name == "VK_DEFINE_NON_DISPATCHABLE_HANDLE":
202 value = "#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;"
205 return VkDefine(name, value)
207 # With a name element the structure is like:
208 # <type category="define"><name>some_name</name>some_value</type>
209 name = name_elem.text
211 # VK_API_VERSION is a deprecated constant and we don't really want to parse it or generate
212 # code for it. However the vk.xml feature section still references it.
213 if name == "VK_API_VERSION":
214 return VkDefine(name, "")
216 # The body of the define is basically unstructured C code. It is not meant for easy parsing.
217 # Some lines contain deprecated values or comments, which we try to filter out.
219 for line in define.text.splitlines():
220 # Skip comments or deprecated values.
227 if child.tail is not None:
230 return VkDefine(name, value.rstrip(' '))
232 def definition(self):
233 if self.value is None:
236 # Nothing to do as the value was already put in the right form during parsing.
237 return "{0}\n".format(self.value)
240 class VkEnum(object):
241 def __init__(self, name, values):
244 self.required = False
248 name = enum.attrib.get("name")
251 for v in enum.findall("enum"):
252 # Value is either a value or a bitpos, only one can exist.
253 value = v.attrib.get("value")
256 value = 1 << int(v.attrib.get("bitpos"))
257 values.append(VkEnumValue(v.attrib.get("name"), value, hex=True))
259 # Some values are in hex form. We want to preserve the hex representation
260 # at least when we convert back to a string. Internally we want to use int.
262 values.append(VkEnumValue(v.attrib.get("name"), int(value, 0), hex=True))
264 values.append(VkEnumValue(v.attrib.get("name"), int(value, 0)))
266 # vulkan.h contains a *_MAX_ENUM value set to 32-bit at the time of writing,
267 # which is to prepare for extensions as they can add values and hence affect
268 # the size definition.
269 max_name = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2',name).upper() + "_MAX_ENUM"
270 values.append(VkEnumValue(max_name, 0x7fffffff, hex=True))
272 return VkEnum(name, values)
274 def add(self, value):
275 """ Add a value to enum. """
276 self.values.append(value)
278 def definition(self):
279 text = "typedef enum {0}\n{{\n".format(self.name)
281 # Print values sorted, values can have been added in a random order.
282 values = sorted(self.values, key=lambda value: value.value)
284 text += " {0},\n".format(value.definition())
285 text += "}} {0};\n\n".format(self.name)
289 class VkEnumValue(object):
290 def __init__(self, name, value, hex=False):
296 return "{0}={1}".format(self.name, self.value)
298 def definition(self):
299 """ Convert to text definition e.g. VK_FOO = 1 """
301 # Hex is commonly used for FlagBits and sometimes within
302 # a non-FlagBits enum for a bitmask value as well.
304 return "{0} = 0x{1:08x}".format(self.name, self.value)
306 return "{0} = {1}".format(self.name, self.value)
309 class VkFunction(object):
310 def __init__(self, _type=None, name=None, params=[], extension=None):
311 self.extension = extension
316 # For some functions we need some extra metadata from FUNCTION_OVERRIDES.
317 func_info = FUNCTION_OVERRIDES.get(self.name, None)
318 self.dispatch = func_info["dispatch"] if func_info is not None else True
319 self.driver = func_info["driver"] if func_info is not None else False
320 self.thunk_needed = func_info["thunk"] if func_info is not None else True
322 # Required is set while parsing which APIs and types are required
323 # and is used by the code generation.
324 self.required = False
327 def from_xml(command, types):
328 proto = command.find("proto")
330 func_name = proto.find("name").text
331 func_type = proto.find("type").text
334 for param in command.findall("param"):
335 vk_param = VkParam.from_xml(param, types)
336 params.append(vk_param)
338 return VkFunction(_type=func_type, name=func_name, params=params)
340 def get_conversions(self):
341 """ Get a list of conversion functions required for this function if any.
342 Parameters which are structures may require conversion between win32
343 and the host platform. This function returns a list of conversions
348 for param in self.params:
349 convs = param.get_conversions()
350 if convs is not None:
351 conversions.extend(convs)
355 def is_device_func(self):
356 # If none of the other, it must be a device function
357 return not self.is_global_func() and not self.is_instance_func()
359 def is_driver_func(self):
360 """ Returns if function is part of Wine driver interface. """
363 def is_global_func(self):
364 # Treat vkGetInstanceProcAddr as a global function as it
365 # can operate with NULL for vkInstance.
366 if self.name == "vkGetInstanceProcAddr":
368 # Global functions are not passed a dispatchable object.
369 elif self.params[0].is_dispatchable():
373 def is_instance_func(self):
374 # Instance functions are passed VkInstance or VkPhysicalDevice.
375 if self.params[0].type in ["VkInstance", "VkPhysicalDevice"]:
379 def is_required(self):
382 def needs_conversion(self):
383 """ Check if the function needs any input/output type conversion.
384 Functions need input/output conversion if struct parameters have
385 alignment differences between Win32 and Linux 32-bit.
388 for p in self.params:
389 if p.needs_conversion():
390 LOGGER.debug("Parameter {0} to {1} requires conversion".format(p.name, self.name))
395 def needs_dispatch(self):
398 def needs_thunk(self):
399 return self.thunk_needed
401 def pfn(self, call_conv=None, conv=False):
402 """ Create function pointer. """
404 if call_conv is not None:
405 pfn = "{0} ({1} *p_{2})(".format(self.type, call_conv, self.name)
407 pfn = "{0} (*p_{1})(".format(self.type, self.name)
409 for i, param in enumerate(self.params):
411 pfn += param.const + " "
414 if conv and param.needs_conversion():
417 if param.is_pointer():
418 pfn += " " + param.pointer
420 if param.array_len is not None:
421 pfn += "[{0}]".format(param.array_len)
423 if i < len(self.params) - 1:
428 def prototype(self, call_conv=None, prefix=None, postfix=None):
429 """ Generate prototype for given function.
432 call_conv (str, optional): calling convention e.g. WINAPI
433 prefix (str, optional): prefix to append prior to function name e.g. vkFoo -> wine_vkFoo
434 postfix (str, optional): text to append after function name but prior to semicolon e.g. DECLSPEC_HIDDEN
437 proto = "{0}".format(self.type)
439 if call_conv is not None:
440 proto += " {0}".format(call_conv)
442 if prefix is not None:
443 proto += " {0}{1}(".format(prefix, self.name)
445 proto += " {0}(".format(self.name)
447 # Add all the paremeters.
448 proto += ", ".join([p.definition() for p in self.params])
450 if postfix is not None:
451 proto += ") {0}".format(postfix)
458 body = " {0}".format(self.trace())
460 params = ", ".join([p.variable(conv=False) for p in self.params])
462 # Call the native Vulkan function.
463 if self.type == "void":
464 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
466 body += " return {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
470 def body_conversion(self):
473 # Declare a variable to hold the result for non-void functions.
474 if self.type != "void":
475 body += " {0} result;\n".format(self.type)
477 # Declare any tmp parameters for conversion.
478 for p in self.params:
479 if not p.needs_conversion():
482 if p.is_dynamic_array():
483 body += " {0}_host *{1}_host;\n".format(p.type, p.name)
485 body += " {0}_host {1}_host;\n".format(p.type, p.name)
487 body += " {0}\n".format(self.trace())
489 # Call any win_to_host conversion calls.
490 for p in self.params:
491 if not p.needs_input_conversion():
494 body += p.copy(Direction.INPUT)
496 # Build list of parameters containing converted and non-converted parameters.
497 # The param itself knows if conversion is needed and applies it when we set conv=True.
498 params = ", ".join([p.variable(conv=True) for p in self.params])
500 # Call the native Vulkan function.
501 if self.type == "void":
502 body += " {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
504 body += " result = {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
508 # Call any host_to_win conversion calls.
509 for p in self.params:
510 if not p.needs_output_conversion():
513 body += p.copy(Direction.OUTPUT)
515 # Perform any required cleanups. Most of these are for array functions.
516 for p in self.params:
517 if not p.needs_free():
522 # Finally return the result.
523 if self.type != "void":
524 body += " return result;\n"
528 def stub(self, call_conv=None, prefix=None):
529 stub = self.prototype(call_conv=call_conv, prefix=prefix)
531 stub += " {0}".format(self.trace(message="stub: ", trace_func="FIXME"))
533 if self.type == "VkResult":
534 stub += " return VK_ERROR_OUT_OF_HOST_MEMORY;\n"
535 elif self.type == "VkBool32":
536 stub += " return VK_FALSE;\n"
537 elif self.type == "PFN_vkVoidFunction":
538 stub += " return NULL;\n"
543 def thunk(self, call_conv=None, prefix=None):
544 thunk = self.prototype(call_conv=call_conv, prefix=prefix)
547 if self.needs_conversion():
548 thunk += "#if defined(USE_STRUCT_CONVERSION)\n"
549 thunk += self.body_conversion()
559 def trace(self, message=None, trace_func=None):
560 """ Create a trace string including all parameters.
563 message (str, optional): text to print at start of trace message e.g. 'stub: '
564 trace_func (str, optional): used to override trace function e.g. FIXME, printf, etcetera.
566 if trace_func is not None:
567 trace = "{0}(\"".format(trace_func)
571 if message is not None:
574 # First loop is for all the format strings.
575 trace += ", ".join([p.format_string() for p in self.params])
578 # Second loop for parameter names and optional conversions.
579 for param in self.params:
580 if param.format_conv is not None:
581 trace += ", " + param.format_conv.format(param.name)
583 trace += ", {0}".format(param.name)
589 class VkFunctionPointer(object):
590 def __init__(self, _type, name, members):
592 self.members = members
594 self.required = False
597 def from_xml(funcpointer):
601 for t in funcpointer.findall("type"):
603 # <type>void</type>* pUserData,
604 # Parsing of the tail (anything past </type>) is tricky since there
605 # can be other data on the next line like: const <type>int</type>..
608 lines = t.tail.split(",\n")
609 if lines[0][0] == "*":
611 name = lines[0][1:].strip()
614 name = lines[0].strip()
616 # Filter out ); if it is contained.
617 name = name.partition(");")[0]
619 # If tail encompasses multiple lines, assign the second line to begin
622 begin = lines[1].strip()
626 members.append(VkMember(const=const, _type=_type, pointer=pointer, name=name))
628 _type = funcpointer.text
629 name = funcpointer.find("name").text
630 return VkFunctionPointer(_type, name, members)
632 def definition(self):
633 text = "{0} {1})(\n".format(self.type, self.name)
636 if len(self.members) > 0:
637 for m in self.members:
639 text += " " + m.definition()
642 text += ",\n " + m.definition()
644 # Just make the compiler happy by adding a void parameter.
650 class VkHandle(object):
651 def __init__(self, name, _type, parent):
655 self.required = False
658 def from_xml(handle):
659 name = handle.find("name").text
660 _type = handle.find("type").text
661 parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice.
662 return VkHandle(name, _type, parent)
664 def dispatch_table(self):
665 if not self.is_dispatchable():
668 if self.parent is None:
669 # Should only happen for VkInstance
671 elif self.name == "VkDevice":
672 # VkDevice has VkInstance as a parent, but has its own dispatch table.
674 elif self.parent in ["VkInstance", "VkPhysicalDevice"]:
675 return "instance->funcs"
676 elif self.parent in ["VkDevice", "VkCommandPool"]:
677 return "device->funcs"
679 LOGGER.error("Unhandled dispatchable parent: {0}".format(self.parent))
681 def definition(self):
682 """ Generates handle definition e.g. VK_DEFINE_HANDLE(vkInstance) """
683 return "{0}({1})\n".format(self.type, self.name)
685 def is_dispatchable(self):
686 """ Some handles like VkInstance, VkDevice are dispatchable objects,
687 which means they contain a dispatch table of function pointers.
689 return self.type == "VK_DEFINE_HANDLE"
691 def native_handle(self):
692 """ Provide access to the native handle of a dispatchable object.
694 Dispatchable objects wrap an underlying 'native' object.
695 This method provides access to the native object.
697 if not self.is_dispatchable():
700 if self.name == "VkCommandBuffer":
701 return "command_buffer"
702 elif self.name == "VkDevice":
704 elif self.name == "VkInstance":
706 elif self.name == "VkPhysicalDevice":
708 elif self.name == "VkQueue":
711 LOGGER.error("Unhandled native handle for: {0}".format(self.name))
714 class VkMember(object):
715 def __init__(self, const=None, _type=None, pointer=None, name=None, array_len=None, dyn_array_len=None, optional=False,
716 extension_structs=None):
719 self.pointer = pointer
721 self.type_info = None
722 self.array_len = array_len
723 self.dyn_array_len = dyn_array_len
724 self.optional = optional
725 self.extension_structs = extension_structs
727 def __eq__(self, other):
728 """ Compare member based on name against a string.
730 This method is for convenience by VkStruct, which holds a number of members and needs quick checking
731 if certain members exist.
734 if self.name == other:
740 return "{0} {1} {2} {3} {4} {5}".format(self.const, self.type, self.pointer, self.name, self.array_len,
744 def from_xml(member):
745 """ Helper function for parsing a member tag within a struct or union. """
747 name_elem = member.find("name")
748 type_elem = member.find("type")
750 const = member.text.strip() if member.text else None
755 if type_elem is not None:
756 member_type = type_elem.text
757 if type_elem.tail is not None:
758 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
760 # Name of other member within, which stores the number of
761 # elements pointed to be by this member.
762 dyn_array_len = member.get("len", None)
764 if "validextensionstructs" in member.attrib:
765 extension_structs = member.get("validextensionstructs").split(",")
767 extension_structs = None
769 # Some members are optional, which is important for conversion code e.g. not dereference NULL pointer.
770 optional = True if member.get("optional") else False
772 # Usually we need to allocate memory for dynamic arrays. We need to do the same in a few other cases
773 # like for VkCommandBufferBeginInfo.pInheritanceInfo. Just threat such cases as dynamic arrays of
774 # size 1 to simplify code generation.
775 if dyn_array_len is None and pointer is not None:
778 # Some members are arrays, attempt to parse these. Formats include:
779 # <member><type>char</type><name>extensionName</name>[<enum>VK_MAX_EXTENSION_NAME_SIZE</enum>]</member>
780 # <member><type>uint32_t</type><name>foo</name>[4]</member>
781 if name_elem.tail and name_elem.tail[0] == '[':
782 LOGGER.debug("Found array type")
783 enum_elem = member.find("enum")
784 if enum_elem is not None:
785 array_len = enum_elem.text
787 # Remove brackets around length
788 array_len = name_elem.tail.strip("[]")
790 return VkMember(const=const, _type=member_type, pointer=pointer, name=name_elem.text, array_len=array_len,
791 dyn_array_len=dyn_array_len, optional=optional, extension_structs=extension_structs)
793 def copy(self, input, output, direction):
794 """ Helper method for use by conversion logic to generate a C-code statement to copy this member. """
796 if self.needs_conversion():
797 if self.is_dynamic_array():
798 if direction == Direction.OUTPUT:
799 LOGGER.warn("TODO: implement copying of returnedonly dynamic array for {0}.{1}".format(self.type, self.name))
801 # Array length is either a variable name (string) or an int.
802 count = self.dyn_array_len if isinstance(self.dyn_array_len, int) else "{0}{1}".format(input, self.dyn_array_len)
803 return "{0}{1} = convert_{2}_array_win_to_host({3}{1}, {4});\n".format(output, self.name, self.type, input, count)
804 elif self.is_static_array():
805 count = self.array_len
806 if direction == Direction.OUTPUT:
807 # Needed by VkMemoryHeap.memoryHeaps
808 return "convert_{0}_static_array_host_to_win({2}{1}, {3}{1}, {4});\n".format(self.type, self.name, input, output, count)
810 # Nothing needed this yet.
811 LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
813 if direction == Direction.OUTPUT:
814 return "convert_{0}_host_to_win(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
816 return "convert_{0}_win_to_host(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
817 elif self.is_static_array():
818 bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type)
819 return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count)
821 return "{0}{1} = {2}{1};\n".format(output, self.name, input)
823 def definition(self, align=False, conv=False):
824 """ Generate prototype for given function.
827 align (bool, optional): Enable alignment if a type needs it. This adds WINE_VK_ALIGN(8) to a member.
828 conv (bool, optional): Enable conversion if a type needs it. This appends '_host' to the name.
835 if conv and self.is_struct():
836 text += "{0}_host".format(self.type)
840 if self.is_pointer():
841 text += " {0}{1}".format(self.pointer, self.name)
843 if align and self.needs_alignment():
844 text += " WINE_VK_ALIGN(8) " + self.name
846 text += " " + self.name
848 if self.is_static_array():
849 text += "[{0}]".format(self.array_len)
853 def get_conversions(self):
854 """ Return any conversion description for this member and its children when conversion is needed. """
856 # Check if we need conversion either for this member itself or for any child members
857 # in case member represents a struct.
858 if not self.needs_conversion():
863 # Collect any conversion for any member structs.
864 struct = self.type_info["data"]
866 if m.needs_conversion():
867 conversions.extend(m.get_conversions())
869 struct = self.type_info["data"]
870 direction = Direction.OUTPUT if struct.returnedonly else Direction.INPUT
871 if self.is_dynamic_array():
872 conversions.append(ConversionFunction(False, True, direction, struct))
873 elif self.is_static_array():
874 conversions.append(ConversionFunction(True, False, direction, struct))
876 conversions.append(ConversionFunction(False, False, direction, struct))
878 if self.needs_free():
879 conversions.append(FreeFunction(self.is_dynamic_array(), struct))
884 return self.const is not None
886 def is_dynamic_array(self):
887 """ Returns if the member is an array element.
888 Vulkan uses this for dynamically sized arrays for which
889 there is a 'count' parameter.
891 return self.dyn_array_len is not None
894 return self.type_info["category"] == "handle"
896 def is_pointer(self):
897 return self.pointer is not None
899 def is_static_array(self):
900 """ Returns if the member is an array.
901 Vulkan uses this often for fixed size arrays in which the
902 length is part of the member.
904 return self.array_len is not None
907 return self.type_info["category"] == "struct"
910 return self.type_info["category"] == "union"
912 def needs_alignment(self):
913 """ Check if this member needs alignment for 64-bit data.
914 Various structures need alignment on 64-bit variables due
915 to compiler differences on 32-bit between Win32 and Linux.
918 if self.is_pointer():
920 elif self.type == "size_t":
922 elif self.type in ["uint64_t", "VkDeviceSize"]:
924 elif self.is_struct():
925 struct = self.type_info["data"]
926 return struct.needs_alignment()
927 elif self.is_handle():
928 # Dispatchable handles are pointers to objects, while
929 # non-dispatchable are uint64_t and hence need alignment.
930 handle = self.type_info["data"]
931 return False if handle.is_dispatchable() else True
934 def needs_conversion(self):
935 """ Structures requiring alignment, need conversion between win32 and host. """
937 if not self.is_struct():
940 struct = self.type_info["data"]
941 return struct.needs_conversion()
943 def needs_free(self):
944 if not self.needs_conversion():
947 if self.is_dynamic_array():
950 # TODO: some non-pointer structs and optional pointer structs may need freeing,
951 # though none of this type have been encountered yet.
954 def set_type_info(self, type_info):
955 """ Helper function to set type information from the type registry.
956 This is needed, because not all type data is available at time of
959 self.type_info = type_info
962 class VkParam(object):
963 """ Helper class which describes a parameter to a function call. """
965 def __init__(self, type_info, const=None, pointer=None, name=None, array_len=None, dyn_array_len=None):
968 self.array_len = array_len
969 self.dyn_array_len = dyn_array_len
970 self.pointer = pointer
971 self.type_info = type_info
972 self.type = type_info["name"] # For convenience
973 self.handle = type_info["data"] if type_info["category"] == "handle" else None
974 self.struct = type_info["data"] if type_info["category"] == "struct" else None
976 self._set_direction()
977 self._set_format_string()
978 self._set_conversions()
981 return "{0} {1} {2} {3} {4}".format(self.const, self.type, self.pointer, self.name, self.array_len, self.dyn_array_len)
984 def from_xml(param, types):
985 """ Helper function to create VkParam from xml. """
987 # Parameter parsing is slightly tricky. All the data is contained within
988 # a param tag, but some data is within subtags while others are text
989 # before or after the type tag.
991 # <param>const <type>char</type>* <name>pLayerName</name></param>
993 name_elem = param.find("name")
995 name = name_elem.text
996 # Tail contains array length e.g. for blendConstants param of vkSetBlendConstants
997 if name_elem.tail is not None:
998 array_len = name_elem.tail.strip("[]")
1000 # Name of other parameter in function prototype, which stores the number of
1001 # elements pointed to be by this parameter.
1002 dyn_array_len = param.get("len", None)
1004 const = param.text.strip() if param.text else None
1005 type_elem = param.find("type")
1006 pointer = type_elem.tail.strip() if type_elem.tail.strip() != "" else None
1008 # Since we have parsed all types before hand, this should not happen.
1009 type_info = types.get(type_elem.text, None)
1010 if type_info is None:
1011 LOGGER.err("type info not found for: {0}".format(type_elem.text))
1013 return VkParam(type_info, const=const, pointer=pointer, name=name, array_len=array_len, dyn_array_len=dyn_array_len)
1015 def _set_conversions(self):
1016 """ Internal helper function to configure any needed conversion functions. """
1018 self.free_func = None
1019 self.input_conv = None
1020 self.output_conv = None
1021 if not self.needs_conversion():
1024 # Input functions require win to host conversion.
1025 if self._direction in [Direction.INPUT, Direction.INPUT_OUTPUT]:
1026 self.input_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.INPUT, self.struct)
1028 # Output functions require host to win conversion.
1029 if self._direction in [Direction.INPUT_OUTPUT, Direction.OUTPUT]:
1030 self.output_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.OUTPUT, self.struct)
1032 # Dynamic arrays, but also some normal structs (e.g. VkCommandBufferBeginInfo) need memory
1033 # allocation and thus some cleanup.
1034 if self.is_dynamic_array() or self.struct.needs_free():
1035 self.free_func = FreeFunction(self.is_dynamic_array(), self.struct)
1037 def _set_direction(self):
1038 """ Internal helper function to set parameter direction (input/output/input_output). """
1040 # The parameter direction needs to be determined from hints in vk.xml like returnedonly,
1041 # parameter constness and other heuristics.
1042 # For now we need to get this right for structures as we need to convert these, we may have
1043 # missed a few other edge cases (e.g. count variables).
1044 # See also https://github.com/KhronosGroup/Vulkan-Docs/issues/610
1046 if not self.is_pointer():
1047 self._direction = Direction.INPUT
1048 elif self.is_const() and self.is_pointer():
1049 self._direction = Direction.INPUT
1050 elif self.is_struct():
1051 if not self.struct.returnedonly:
1052 self._direction = Direction.INPUT
1055 # Returnedonly hints towards output, however in some cases
1056 # it is inputoutput. In particular if pNext / sType exist,
1057 # which are used to link in other structures without having
1058 # to introduce new APIs. E.g. vkGetPhysicalDeviceProperties2KHR.
1059 if "pNext" in self.struct:
1060 self._direction = Direction.INPUT_OUTPUT
1063 self._direction = Direction.OUTPUT
1065 # This should mostly be right. Count variables can be inout, but we don't care about these yet.
1066 self._direction = Direction.OUTPUT
1068 def _set_format_string(self):
1069 """ Internal helper function to be used by constructor to set format string. """
1071 # Determine a format string used by code generation for traces.
1072 # 64-bit types need a conversion function.
1073 self.format_conv = None
1074 if self.is_static_array() or self.is_pointer():
1075 self.format_str = "%p"
1077 if self.type_info["category"] == "bitmask":
1078 self.format_str = "%#x"
1079 elif self.type_info["category"] == "enum":
1080 self.format_str = "%d"
1081 elif self.is_handle():
1082 # We use uint64_t for non-dispatchable handles as opposed to pointers
1083 # for dispatchable handles.
1084 if self.handle.is_dispatchable():
1085 self.format_str = "%p"
1087 self.format_str = "0x%s"
1088 self.format_conv = "wine_dbgstr_longlong({0})"
1089 elif self.type == "float":
1090 self.format_str = "%f"
1091 elif self.type == "int":
1092 self.format_str = "%d"
1093 elif self.type == "int32_t":
1094 self.format_str = "%d"
1095 elif self.type == "size_t":
1096 self.format_str = "0x%s"
1097 self.format_conv = "wine_dbgstr_longlong({0})"
1098 elif self.type in ["uint32_t", "VkBool32"]:
1099 self.format_str = "%u"
1100 elif self.type in ["uint64_t", "VkDeviceSize"]:
1101 self.format_str = "0x%s"
1102 self.format_conv = "wine_dbgstr_longlong({0})"
1103 elif self.type == "HANDLE":
1104 self.format_str = "%p"
1105 elif self.type in ["VisualID", "xcb_visualid_t", "RROutput"]:
1106 # Don't care about Linux specific types.
1107 self.format_str = ""
1109 LOGGER.warn("Unhandled type: {0}".format(self.type_info))
1111 def copy(self, direction):
1112 if direction == Direction.INPUT:
1113 if self.is_dynamic_array():
1114 return " {0}_host = convert_{1}_array_win_to_host({0}, {2});\n".format(self.name, self.type, self.dyn_array_len)
1116 return " convert_{0}_win_to_host({1}, &{1}_host);\n".format(self.type, self.name)
1118 if self.is_dynamic_array():
1119 LOGGER.error("Unimplemented output conversion for: {0}".format(self.name))
1121 return " convert_{0}_host_to_win(&{1}_host, {1});\n".format(self.type, self.name)
1123 def definition(self, postfix=None):
1124 """ Return prototype for the parameter. E.g. 'const char *foo' """
1128 proto += self.const + " "
1132 if self.is_pointer():
1133 proto += " {0}{1}".format(self.pointer, self.name)
1135 proto += " " + self.name
1137 # Allows appeninding something to the variable name useful for
1138 # win32 to host conversion.
1139 if postfix is not None:
1142 if self.is_static_array():
1143 proto += "[{0}]".format(self.array_len)
1147 def direction(self):
1148 """ Returns parameter direction: input, output, input_output.
1150 Parameter direction in Vulkan is not straight-forward, which this function determines.
1153 return self._direction
1155 def format_string(self):
1156 return self.format_str
1158 def dispatch_table(self):
1159 """ Return functions dispatch table pointer for dispatchable objects. """
1161 if not self.is_dispatchable():
1164 return "{0}->{1}".format(self.name, self.handle.dispatch_table())
1166 def format_string(self):
1167 return self.format_str
1170 if self.is_dynamic_array():
1171 if self.struct.returnedonly:
1172 # For returnedonly, counts is stored in a pointer.
1173 return " free_{0}_array({1}_host, *{2});\n".format(self.type, self.name, self.dyn_array_len)
1175 return " free_{0}_array({1}_host, {2});\n".format(self.type, self.name, self.dyn_array_len)
1177 # We are operating on a single structure. Some structs (very rare) contain dynamic members,
1178 # which would need freeing.
1179 if self.struct.needs_free():
1180 return " free_{0}(&{1}_host);\n".format(self.type, self.name)
1183 def get_conversions(self):
1184 """ Get a list of conversions required for this parameter if any.
1185 Parameters which are structures may require conversion between win32
1186 and the host platform. This function returns a list of conversions
1190 if not self.is_struct():
1193 if not self.needs_conversion():
1198 # Collect any member conversions first, so we can guarantee
1199 # those functions will be defined prior to usage by the
1200 # 'parent' param requiring conversion.
1201 for m in self.struct:
1202 if not m.is_struct():
1205 if not m.needs_conversion():
1208 conversions.extend(m.get_conversions())
1210 # Conversion requirements for the 'parent' parameter.
1211 if self.input_conv is not None:
1212 conversions.append(self.input_conv)
1213 if self.output_conv is not None:
1214 conversions.append(self.output_conv)
1215 if self.free_func is not None:
1216 conversions.append(self.free_func)
1221 return self.const is not None
1223 def is_dynamic_array(self):
1224 return self.dyn_array_len is not None
1226 def is_dispatchable(self):
1227 if not self.is_handle():
1230 return self.handle.is_dispatchable()
1232 def is_handle(self):
1233 return self.handle is not None
1235 def is_pointer(self):
1236 return self.pointer is not None
1238 def is_static_array(self):
1239 return self.array_len is not None
1241 def is_struct(self):
1242 return self.struct is not None
1244 def needs_conversion(self):
1245 """ Returns if parameter needs conversion between win32 and host. """
1247 if not self.is_struct():
1250 # VkSparseImageMemoryRequirements is used by vkGetImageSparseMemoryRequirements.
1251 # This function is tricky to wrap, because how to wrap depends on whether
1252 # pSparseMemoryRequirements is NULL or not. Luckily for VkSparseImageMemoryRequirements
1253 # the alignment works out in such a way that no conversion is needed between win32 and Linux.
1254 if self.type == "VkSparseImageMemoryRequirements":
1257 # If a structure needs alignment changes, it means we need to
1258 # perform parameter conversion between win32 and host.
1259 if self.struct.needs_conversion():
1264 def needs_free(self):
1265 return self.free_func is not None
1267 def needs_input_conversion(self):
1268 return self.input_conv is not None
1270 def needs_output_conversion(self):
1271 return self.output_conv is not None
1273 def variable(self, conv=False):
1274 """ Returns 'glue' code during generation of a function call on how to access the variable.
1275 This function handles various scenarios such as 'unwrapping' if dispatchable objects and
1276 renaming of parameters in case of win32 -> host conversion.
1279 conv (bool, optional): Enable conversion if the param needs it. This appends '_host' to the name.
1282 # Hack until we enable allocation callbacks from ICD to application. These are a joy
1283 # to enable one day, because of calling convention conversion.
1284 if "VkAllocationCallbacks" in self.type:
1285 LOGGER.debug("TODO: setting NULL VkAllocationCallbacks for {0}".format(self.name))
1288 # Dispatchable objects wrap the native handle. For thunk generation we
1289 # need to pass the native handle to the native vulkan calls.
1290 if self.is_dispatchable():
1291 return "{0}->{1}".format(self.name, self.handle.native_handle())
1292 elif conv and self.needs_conversion():
1293 if self.is_dynamic_array():
1294 return "{0}_host".format(self.name)
1296 return "&{0}_host".format(self.name)
1301 class VkStruct(Sequence):
1302 """ Class which represents the type union and struct. """
1304 def __init__(self, name, members, returnedonly, union=False):
1306 self.members = members
1307 self.returnedonly = returnedonly
1308 self.required = False
1310 self.type_info = None # To be set later.
1312 def __getitem__(self, i):
1313 return self.members[i]
1316 return len(self.members)
1319 def from_xml(struct):
1320 # Unions and structs are the same parsing wise, but we need to
1321 # know which one we are dealing with later on for code generation.
1322 union = True if struct.attrib["category"] == "union" else False
1324 name = struct.attrib.get("name", None)
1326 # 'Output' structures for which data is filled in by the API are
1327 # marked as 'returnedonly'.
1328 returnedonly = True if struct.attrib.get("returnedonly") else False
1331 for member in struct.findall("member"):
1332 vk_member = VkMember.from_xml(member)
1333 members.append(vk_member)
1335 return VkStruct(name, members, returnedonly, union=union)
1338 def decouple_structs(structs):
1339 """ Helper function which decouples a list of structs.
1340 Structures often depend on other structures. To make the C compiler
1341 happy we need to define 'substructures' first. This function analyzes
1342 the list of structures and reorders them in such a way that they are
1346 tmp_structs = list(structs) # Don't modify the original structures.
1347 decoupled_structs = []
1349 while (len(tmp_structs) > 0):
1350 for struct in tmp_structs:
1353 if not struct.required:
1354 tmp_structs.remove(struct)
1358 if not (m.is_struct() or m.is_union()):
1362 # Check if a struct we depend on has already been defined.
1363 for s in decoupled_structs:
1364 if s.name == m.type:
1369 # Check if the struct we depend on is even in the list of structs.
1370 # If found now, it means we haven't met all dependencies before we
1371 # can operate on the current struct.
1372 # When generating 'host' structs we may not be able to find a struct
1373 # as the list would only contain the structs requiring conversion.
1374 for s in tmp_structs:
1375 if s.name == m.type:
1379 if dependends == False:
1380 decoupled_structs.append(struct)
1381 tmp_structs.remove(struct)
1383 return decoupled_structs
1385 def definition(self, align=False, conv=False, postfix=None):
1386 """ Convert structure to textual definition.
1389 align (bool, optional): enable alignment to 64-bit for win32 struct compatibility.
1390 conv (bool, optional): enable struct conversion if the struct needs it.
1391 postfix (str, optional): text to append to end of struct name, useful for struct renaming.
1395 text = "typedef union {0}".format(self.name)
1397 text = "typedef struct {0}".format(self.name)
1399 if postfix is not None:
1405 if align and m.needs_alignment():
1406 text += " {0};\n".format(m.definition(align=align))
1407 elif conv and m.needs_conversion():
1408 text += " {0};\n".format(m.definition(conv=conv))
1410 text += " {0};\n".format(m.definition())
1412 if postfix is not None:
1413 text += "}} {0}{1};\n\n".format(self.name, postfix)
1415 text += "}} {0};\n\n".format(self.name)
1418 def needs_alignment(self):
1419 """ Check if structure needs alignment for 64-bit data.
1420 Various structures need alignment on 64-bit variables due
1421 to compiler differences on 32-bit between Win32 and Linux.
1424 for m in self.members:
1425 if m.needs_alignment():
1429 def needs_conversion(self):
1430 """ Returns if struct members needs conversion between win32 and host.
1431 Structures need conversion if they contain members requiring alignment
1432 or if they include other structures which need alignment.
1435 if self.needs_alignment():
1438 for m in self.members:
1439 if m.needs_conversion():
1443 def needs_free(self):
1444 """ Check if any struct member needs some memory freeing."""
1446 for m in self.members:
1454 def set_type_info(self, types):
1455 """ Helper function to set type information from the type registry.
1456 This is needed, because not all type data is available at time of
1459 for m in self.members:
1460 type_info = types[m.type]
1461 m.set_type_info(type_info)
1464 class ConversionFunction(object):
1465 def __init__(self, array, dyn_array, direction, struct):
1467 self.direction = direction
1468 self.dyn_array = dyn_array
1469 self.struct = struct
1470 self.type = struct.name
1474 def __eq__(self, other):
1475 if self.name != other.name:
1480 def _generate_array_conversion_func(self):
1481 """ Helper function for generating a conversion function for array structs. """
1483 if self.direction == Direction.OUTPUT:
1484 params = ["const {0}_host *in".format(self.type), "uint32_t count"]
1485 return_type = self.type
1487 params = ["const {0} *in".format(self.type), "uint32_t count"]
1488 return_type = "{0}_host".format(self.type)
1490 # Generate function prototype.
1491 body = "static inline {0} *{1}(".format(return_type, self.name)
1492 body += ", ".join(p for p in params)
1495 body += " {0} *out;\n".format(return_type)
1496 body += " unsigned int i;\n\n"
1497 body += " if (!in) return NULL;\n\n"
1499 body += " out = heap_alloc(count * sizeof(*out));\n"
1501 body += " for (i = 0; i < count; i++)\n"
1504 for m in self.struct:
1505 # TODO: support copying of pNext extension structures!
1506 # Luckily though no extension struct at this point needs conversion.
1507 body += " " + m.copy("in[i].", "out[i].", self.direction)
1510 body += " return out;\n"
1514 def _generate_conversion_func(self):
1515 """ Helper function for generating a conversion function for non-array structs. """
1517 if self.direction == Direction.OUTPUT:
1518 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type)]
1520 params = ["const {0} *in".format(self.type), "{0}_host *out".format(self.type)]
1522 body = "static inline void {0}(".format(self.name)
1524 # Generate parameter list
1525 body += ", ".join(p for p in params)
1528 body += " if (!in) return;\n\n"
1530 if self.direction == Direction.INPUT and "pNext" in self.struct and self.struct.returnedonly:
1531 # We are dealing with an input_output parameter. For these we only need to copy
1532 # pNext and sType as the other fields are filled in by the host. We do potentially
1533 # have to iterate over pNext and perform conversions based on switch(sType)!
1534 # Luckily though no extension structs at this point need conversion.
1535 # TODO: support copying of pNext extension structures!
1536 body += " out->pNext = in->pNext;\n"
1537 body += " out->sType = in->sType;\n"
1539 for m in self.struct:
1540 # TODO: support copying of pNext extension structures!
1541 body += " " + m.copy("in->", "out->", self.direction)
1546 def _generate_static_array_conversion_func(self):
1547 """ Helper function for generating a conversion function for array structs. """
1549 if self.direction == Direction.OUTPUT:
1550 params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
1551 return_type = self.type
1553 params = ["const {0} *in".format(self.type), "{0} *out_host".format(self.type), "uint32_t count"]
1554 return_type = "{0}_host".format(self.type)
1556 # Generate function prototype.
1557 body = "static inline void {0}(".format(self.name)
1558 body += ", ".join(p for p in params)
1560 body += " unsigned int i;\n\n"
1561 body += " if (!in) return;\n\n"
1562 body += " for (i = 0; i < count; i++)\n"
1565 for m in self.struct:
1566 # TODO: support copying of pNext extension structures!
1567 body += " " + m.copy("in[i].", "out[i].", self.direction)
1573 def _set_name(self):
1574 if self.direction == Direction.INPUT:
1576 name = "convert_{0}_static_array_win_to_host".format(self.type)
1577 elif self.dyn_array:
1578 name = "convert_{0}_array_win_to_host".format(self.type)
1580 name = "convert_{0}_win_to_host".format(self.type)
1581 else: # Direction.OUTPUT
1583 name = "convert_{0}_static_array_host_to_win".format(self.type)
1584 elif self.dyn_array:
1585 name = "convert_{0}_array_host_to_win".format(self.type)
1587 name = "convert_{0}_host_to_win".format(self.type)
1591 def definition(self):
1593 return self._generate_static_array_conversion_func()
1594 elif self.dyn_array:
1595 return self._generate_array_conversion_func()
1597 return self._generate_conversion_func()
1600 class FreeFunction(object):
1601 def __init__(self, dyn_array, struct):
1602 self.dyn_array = dyn_array
1603 self.struct = struct
1604 self.type = struct.name
1607 self.name = "free_{0}_array".format(self.type)
1609 self.name = "free_{0}".format(self.type)
1611 def __eq__(self, other):
1612 if self.name == other.name:
1617 def _generate_array_free_func(self):
1618 """ Helper function for cleaning up temporary buffers required for array conversions. """
1620 # Generate function prototype.
1621 body = "static inline void {0}({1}_host *in, uint32_t count)\n{{\n".format(self.name, self.type)
1623 # E.g. VkGraphicsPipelineCreateInfo_host needs freeing for pStages.
1624 if self.struct.needs_free():
1625 body += " unsigned int i;\n\n"
1626 body += " if (!in) return;\n\n"
1627 body += " for (i = 0; i < count; i++)\n"
1630 for m in self.struct:
1631 if m.needs_conversion() and m.is_dynamic_array():
1633 # Add a cast to ignore const on conversion structs we allocated ourselves.
1634 body += " free_{0}_array(({0}_host *)in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
1636 body += " free_{0}_array(in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
1637 elif m.needs_conversion():
1638 LOGGER.error("Unhandled conversion for {0}".format(m.name))
1641 body += " if (!in) return;\n\n"
1643 body += " heap_free(in);\n"
1648 def _generate_free_func(self):
1649 # E.g. VkCommandBufferBeginInfo.pInheritanceInfo needs freeing.
1650 if not self.struct.needs_free():
1653 # Generate function prototype.
1654 body = "static inline void {0}({1}_host *in)\n{{\n".format(self.name, self.type)
1656 for m in self.struct:
1657 if m.needs_conversion() and m.is_dynamic_array():
1658 count = m.dyn_array_len if isinstance(m.dyn_array_len, int) else "in->{0}".format(m.dyn_array_len)
1660 # Add a cast to ignore const on conversion structs we allocated ourselves.
1661 body += " free_{0}_array(({0}_host *)in->{1}, {2});\n".format(m.type, m.name, count)
1663 body += " free_{0}_array(in->{1}, {2});\n".format(m.type, m.name, count)
1668 def definition(self):
1670 return self._generate_array_free_func()
1672 # Some structures need freeing too if they contain dynamic arrays.
1673 # E.g. VkCommandBufferBeginInfo
1674 return self._generate_free_func()
1677 class VkGenerator(object):
1678 def __init__(self, registry):
1679 self.registry = registry
1681 # Build a list conversion functions for struct conversion.
1682 self.conversions = []
1683 self.host_structs = []
1684 for func in self.registry.funcs.values():
1685 if not func.is_required():
1688 if not func.needs_conversion():
1691 conversions = func.get_conversions()
1692 for conv in conversions:
1693 # Pull in any conversions for vulkan_thunks.c. Driver conversions we
1694 # handle manually in vulkan.c if needed.
1695 if not func.is_driver_func():
1696 # Append if we don't already have this conversion.
1697 if not any(c == conv for c in self.conversions):
1698 self.conversions.append(conv)
1700 # Structs can be used in different ways by different conversions
1701 # e.g. array vs non-array. Just make sure we pull in each struct once.
1702 if not any(s.name == conv.struct.name for s in self.host_structs):
1703 self.host_structs.append(conv.struct)
1705 def generate_thunks_c(self, f, prefix):
1706 f.write("/* Automatically generated from Vulkan vk.xml; DO NOT EDIT! */\n\n")
1708 f.write("#include \"config.h\"\n")
1709 f.write("#include \"wine/port.h\"\n\n")
1711 f.write("#include \"wine/debug.h\"\n")
1712 f.write("#include \"wine/heap.h\"\n")
1713 f.write("#include \"wine/vulkan.h\"\n")
1714 f.write("#include \"wine/vulkan_driver.h\"\n")
1715 f.write("#include \"vulkan_private.h\"\n\n")
1717 f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
1719 # Generate any conversion helper functions.
1720 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
1721 for conv in self.conversions:
1722 f.write(conv.definition())
1723 f.write("#endif /* USE_STRUCT_CONVERSION */\n\n")
1725 # Create thunks for instance and device functions.
1726 # Global functions don't go through the thunks.
1727 for vk_func in self.registry.funcs.values():
1728 if not vk_func.is_required():
1731 if vk_func.is_global_func():
1734 if not vk_func.needs_thunk():
1737 f.write("static " + vk_func.thunk(prefix=prefix, call_conv="WINAPI"))
1739 f.write("static const struct vulkan_func vk_device_dispatch_table[] =\n{\n")
1740 for vk_func in self.registry.device_funcs:
1741 if not vk_func.is_required():
1744 if not vk_func.needs_dispatch():
1745 LOGGER.debug("skipping {0} in device dispatch table".format(vk_func.name))
1748 f.write(" {{\"{0}\", &{1}{0}}},\n".format(vk_func.name, prefix))
1751 f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
1752 for vk_func in self.registry.instance_funcs:
1753 if not vk_func.is_required():
1756 if not vk_func.needs_dispatch():
1757 LOGGER.debug("skipping {0} in instance dispatch table".format(vk_func.name))
1760 f.write(" {{\"{0}\", &{1}{0}}},\n".format(vk_func.name, prefix))
1763 f.write("void *wine_vk_get_device_proc_addr(const char *name)\n")
1765 f.write(" unsigned int i;\n")
1766 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_dispatch_table); i++)\n")
1768 f.write(" if (strcmp(vk_device_dispatch_table[i].name, name) == 0)\n")
1770 f.write(" TRACE(\"Found name=%s in device table\\n\", name);\n")
1771 f.write(" return vk_device_dispatch_table[i].func;\n")
1774 f.write(" return NULL;\n")
1777 f.write("void *wine_vk_get_instance_proc_addr(const char *name)\n")
1779 f.write(" unsigned int i;\n")
1780 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_dispatch_table); i++)\n")
1782 f.write(" if (strcmp(vk_instance_dispatch_table[i].name, name) == 0)\n")
1784 f.write(" TRACE(\"Found name=%s in instance table\\n\", name);\n")
1785 f.write(" return vk_instance_dispatch_table[i].func;\n")
1788 f.write(" return NULL;\n")
1791 # Create array of device extensions.
1792 f.write("static const char * const vk_device_extensions[] =\n{\n")
1793 for ext in self.registry.extensions:
1794 if ext["type"] != "device":
1797 f.write(" \"{0}\",\n".format(ext["name"]))
1800 # Create array of instance extensions.
1801 f.write("static const char *vk_instance_extensions[] =\n{\n")
1802 for ext in self.registry.extensions:
1803 if ext["type"] != "instance":
1806 f.write(" \"{0}\",\n".format(ext["name"]))
1809 f.write("BOOL wine_vk_device_extension_supported(const char *name)\n")
1811 f.write(" unsigned int i;\n")
1812 f.write(" for (i = 0; i < ARRAY_SIZE(vk_device_extensions); i++)\n")
1814 f.write(" if (strcmp(vk_device_extensions[i], name) == 0)\n")
1815 f.write(" return TRUE;\n")
1817 f.write(" return FALSE;\n")
1820 f.write("BOOL wine_vk_instance_extension_supported(const char *name)\n")
1822 f.write(" unsigned int i;\n")
1823 f.write(" for (i = 0; i < ARRAY_SIZE(vk_instance_extensions); i++)\n")
1825 f.write(" if (strcmp(vk_instance_extensions[i], name) == 0)\n")
1826 f.write(" return TRUE;\n")
1828 f.write(" return FALSE;\n")
1831 def generate_thunks_h(self, f, prefix):
1832 f.write("/* Automatically generated from Vulkan vk.xml; DO NOT EDIT! */\n\n")
1834 f.write("#ifndef __WINE_VULKAN_THUNKS_H\n")
1835 f.write("#define __WINE_VULKAN_THUNKS_H\n\n")
1837 f.write("/* Perform vulkan struct conversion on 32-bit x86 platforms. */\n")
1838 f.write("#if defined(__i386__)\n")
1839 f.write("#define USE_STRUCT_CONVERSION\n")
1840 f.write("#endif\n\n")
1842 f.write("/* For use by vk_icdGetInstanceProcAddr / vkGetInstanceProcAddr */\n")
1843 f.write("void *wine_vk_get_device_proc_addr(const char *name) DECLSPEC_HIDDEN;\n")
1844 f.write("void *wine_vk_get_instance_proc_addr(const char *name) DECLSPEC_HIDDEN;\n\n")
1846 f.write("BOOL wine_vk_device_extension_supported(const char *name) DECLSPEC_HIDDEN;\n")
1847 f.write("BOOL wine_vk_instance_extension_supported(const char *name) DECLSPEC_HIDDEN;\n\n")
1849 # Generate prototypes for device and instance functions requiring a custom implementation.
1850 f.write("/* Functions for which we have custom implementations outside of the thunks. */\n")
1851 for vk_func in self.registry.funcs.values():
1852 if not vk_func.is_required() or vk_func.is_global_func() or vk_func.needs_thunk():
1855 f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix="wine_", postfix="DECLSPEC_HIDDEN")))
1858 for struct in self.host_structs:
1859 f.write(struct.definition(align=False, conv=True, postfix="_host"))
1862 f.write("/* For use by vkDevice and children */\n")
1863 f.write("struct vulkan_device_funcs\n{\n")
1864 for vk_func in self.registry.device_funcs:
1865 if not vk_func.is_required():
1868 if not vk_func.needs_dispatch() or vk_func.is_driver_func():
1869 LOGGER.debug("skipping {0} in vulkan_device_funcs".format(vk_func.name))
1872 if vk_func.needs_conversion():
1873 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
1874 f.write(" {0};\n".format(vk_func.pfn(conv=True)))
1876 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
1879 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
1882 f.write("/* For use by vkInstance and children */\n")
1883 f.write("struct vulkan_instance_funcs\n{\n")
1884 for vk_func in self.registry.instance_funcs:
1885 if not vk_func.is_required():
1888 if not vk_func.needs_dispatch() or vk_func.is_driver_func():
1889 LOGGER.debug("skipping {0} in vulkan_instance_funcs".format(vk_func.name))
1892 if vk_func.needs_conversion():
1893 f.write("#if defined(USE_STRUCT_CONVERSION)\n")
1894 f.write(" {0};\n".format(vk_func.pfn(conv=True)))
1896 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
1899 f.write(" {0};\n".format(vk_func.pfn(conv=False)))
1902 f.write("#define ALL_VK_DEVICE_FUNCS() \\\n")
1904 for vk_func in self.registry.device_funcs:
1905 if not vk_func.is_required():
1908 if not vk_func.needs_dispatch() or vk_func.is_driver_func():
1909 LOGGER.debug("skipping {0} in ALL_VK_DEVICE_FUNCS".format(vk_func.name))
1913 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
1916 f.write(" \\\n USE_VK_FUNC({0})".format(vk_func.name))
1919 f.write("#define ALL_VK_INSTANCE_FUNCS() \\\n")
1921 for vk_func in self.registry.instance_funcs:
1922 if not vk_func.is_required():
1925 if not vk_func.needs_dispatch() or vk_func.is_driver_func():
1926 LOGGER.debug("skipping {0} in ALL_VK_INSTANCE_FUNCS".format(vk_func.name))
1930 f.write(" USE_VK_FUNC({0})".format(vk_func.name))
1933 f.write("\\\n USE_VK_FUNC({0})".format(vk_func.name))
1936 f.write("#endif /* __WINE_VULKAN_THUNKS_H */\n")
1938 def generate_vulkan_h(self, f):
1939 f.write("/* Automatically generated from Vulkan vk.xml; DO NOT EDIT! */\n\n")
1940 f.write("#ifndef __WINE_VULKAN_H\n")
1941 f.write("#define __WINE_VULKAN_H\n\n")
1943 f.write("#include <windef.h>\n")
1944 f.write("#include <stdint.h>\n\n")
1946 f.write("#ifndef VKAPI_CALL\n")
1947 f.write("#define VKAPI_CALL __stdcall\n")
1948 f.write("#endif\n\n")
1950 f.write("#ifndef VKAPI_PTR\n")
1951 f.write("#define VKAPI_PTR VKAPI_CALL\n")
1952 f.write("#endif\n\n")
1954 f.write("/* Callers can override WINE_VK_ALIGN if they want 'host' headers. */\n")
1955 f.write("#ifndef WINE_VK_ALIGN\n")
1956 f.write("#define WINE_VK_ALIGN DECLSPEC_ALIGN\n")
1957 f.write("#endif\n\n")
1959 # The overall strategy is to define independent constants and datatypes,
1960 # prior to complex structures and function calls to avoid forward declarations.
1961 for const in self.registry.consts:
1962 # For now just generate things we may not need. The amount of parsing needed
1963 # to get some of the info is tricky as you need to figure out which structure
1964 # references a certain constant.
1965 f.write(const.definition())
1968 for define in self.registry.defines:
1969 f.write(define.definition())
1971 for handle in self.registry.handles:
1973 f.write(handle.definition())
1976 for base_type in self.registry.base_types:
1977 f.write(base_type.definition())
1980 for bitmask in self.registry.bitmasks:
1981 f.write(bitmask.definition())
1984 # Define enums, this includes values for some of the bitmask types as well.
1985 for enum in self.registry.enums.values():
1987 f.write(enum.definition())
1989 for fp in self.registry.funcpointers:
1991 f.write(fp.definition())
1994 # This generates both structures and unions. Since structures
1995 # may depend on other structures/unions, we need a list of
1996 # decoupled structs.
1997 # Note: unions are stored in structs for dependency reasons,
1998 # see comment in parsing section.
1999 structs = VkStruct.decouple_structs(self.registry.structs)
2000 for struct in structs:
2001 LOGGER.debug("Generating struct: {0}".format(struct.name))
2002 f.write(struct.definition(align=True))
2004 for func in self.registry.funcs.values():
2005 if not func.is_required():
2006 LOGGER.debug("Skipping API definition for: {0}".format(func.name))
2009 LOGGER.debug("Generating API definition for: {0}".format(func.name))
2010 f.write("{0};\n".format(func.prototype(call_conv="VKAPI_CALL")))
2013 f.write("#endif /* __WINE_VULKAN_H */\n")
2015 def generate_vulkan_driver_h(self, f):
2016 f.write("/* Automatically generated from Vulkan vk.xml; DO NOT EDIT! */\n\n")
2017 f.write("#ifndef __WINE_VULKAN_DRIVER_H\n")
2018 f.write("#define __WINE_VULKAN_DRIVER_H\n\n")
2020 f.write("/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */\n")
2021 f.write("#define WINE_VULKAN_DRIVER_VERSION {0}\n\n".format(DRIVER_VERSION))
2023 f.write("struct vulkan_funcs\n{\n")
2024 f.write(" /* Vulkan global functions. These are the only calls at this point a graphics driver\n")
2025 f.write(" * needs to provide. Other function calls will be provided indirectly by dispatch\n")
2026 f.write(" * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice.\n")
2029 for vk_func in self.registry.funcs.values():
2030 if not vk_func.is_required() or not vk_func.is_driver_func():
2034 # Avoid PFN_vkVoidFunction in driver interface as Vulkan likes to put calling convention
2035 # stuff in there. For simplicity substitute with "void *".
2036 pfn = pfn.replace("PFN_vkVoidFunction", "void *")
2037 f.write(" {0};\n".format(pfn))
2040 f.write("extern const struct vulkan_funcs * CDECL __wine_get_vulkan_driver(HDC hdc, UINT version);\n\n")
2041 f.write("#endif /* __WINE_VULKAN_DRIVER_H */\n")
2044 class VkRegistry(object):
2045 def __init__(self, reg_filename):
2046 # Used for storage of type information.
2047 self.base_types = None
2048 self.bitmasks = None
2052 self.funcpointers = None
2056 # We aggregate all types in here for cross-referencing.
2060 # Overall strategy for parsing the registry is to first
2061 # parse all type / function definitions. Then parse
2062 # features and extensions to decide which types / functions
2063 # to actually 'pull in' for code generation. For each type or
2064 # function call we want we set a member 'required' to True.
2065 tree = ET.parse(reg_filename)
2066 root = tree.getroot()
2067 self._parse_enums(root)
2068 self._parse_types(root)
2069 self._parse_commands(root)
2071 # Pull in any required types and functions.
2072 self._parse_features(root)
2073 self._parse_extensions(root)
2075 def _mark_command_required(self, command):
2076 """ Helper function to mark a certain command and the datatypes it needs as required."""
2077 def mark_bitmask_dependencies(bitmask, types):
2078 if bitmask.requires is not None:
2079 types[bitmask.requires]["data"].required = True
2081 def mark_funcpointer_dependencies(fp, types):
2082 for m in fp.members:
2083 type_info = types[m.type]
2085 # Complex types have a matching definition e.g. VkStruct.
2086 # Not needed for base types such as uint32_t.
2087 if "data" in type_info:
2088 types[m.type]["data"].required = True
2090 def mark_struct_dependencies(struct, types):
2092 type_info = types[m.type]
2094 # Complex types have a matching definition e.g. VkStruct.
2095 # Not needed for base types such as uint32_t.
2096 if "data" in type_info:
2097 types[m.type]["data"].required = True
2099 if type_info["category"] == "struct":
2101 mark_struct_dependencies(type_info["data"], types)
2102 elif type_info["category"] == "funcpointer":
2103 mark_funcpointer_dependencies(type_info["data"], types)
2104 elif type_info["category"] == "bitmask":
2105 mark_bitmask_dependencies(type_info["data"], types)
2107 func = self.funcs[command]
2108 func.required = True
2110 # Pull in return type
2111 if func.type != "void":
2112 self.types[func.type]["data"].required = True
2114 # Analyze parameter dependencies and pull in any type needed.
2115 for p in func.params:
2116 type_info = self.types[p.type]
2118 # Check if we are dealing with a complex type e.g. VkEnum, VkStruct and others.
2119 if "data" not in type_info:
2122 # Mark the complex type as required.
2123 type_info["data"].required = True
2124 if type_info["category"] == "struct":
2125 struct = type_info["data"]
2126 mark_struct_dependencies(struct, self.types)
2128 def _parse_commands(self, root):
2129 """ Parse command section containing the Vulkan function calls. """
2131 commands = root.findall("./commands/")
2132 for command in commands:
2133 func = VkFunction.from_xml(command, self.types)
2134 funcs[func.name] = func
2136 # To make life easy for the code generation, separate all function
2137 # calls out in the 3 types of vulkan functions: device, global and instance.
2141 for func in funcs.values():
2142 if func.is_device_func():
2143 device_funcs.append(func)
2144 elif func.is_global_func():
2145 global_funcs.append(func)
2147 instance_funcs.append(func)
2149 # Sort function lists by name and store them.
2150 self.device_funcs = sorted(device_funcs, key=lambda func: func.name)
2151 self.global_funcs = sorted(global_funcs, key=lambda func: func.name)
2152 self.instance_funcs = sorted(instance_funcs, key=lambda func: func.name)
2154 # The funcs dictionary is used as a convenient way to lookup function
2155 # calls when needed e.g. to adjust member variables.
2156 self.funcs = OrderedDict(sorted(funcs.items()))
2158 def _parse_enums(self, root):
2159 """ Parse enums section or better described as constants section. """
2162 for enum in root.findall("./enums"):
2163 name = enum.attrib.get("name")
2164 _type = enum.attrib.get("type")
2166 if _type in ("enum", "bitmask"):
2167 enums[name] = VkEnum.from_xml(enum)
2169 # If no type is set, we are dealing with API constants.
2171 for value in enum.findall("enum"):
2172 self.consts.append(VkConstant(value.attrib.get("name"), value.attrib.get("value")))
2174 self.enums = OrderedDict(sorted(enums.items()))
2176 def _parse_extensions(self, root):
2177 """ Parse extensions section and pull in any types and commands for this extensioin. """
2179 exts = root.findall("./extensions/extension")
2181 ext_name = ext.attrib["name"]
2183 # Some extensions are not ready or have numbers reserved as a place holder.
2184 if ext.attrib["supported"] == "disabled":
2185 LOGGER.debug("Skipping disabled extension: {0}".format(ext_name))
2188 # We only support a handful of extensions for now through a whitelist.
2189 if ext_name not in SUPPORTED_EXTENSIONS:
2190 LOGGER.debug("Skipping blacklisted extension: {0}".format(ext_name))
2193 LOGGER.debug("Loading extension: {0}".format(ext_name))
2195 # Extensions can add enum values to Core / extension enums, so add these.
2196 enums = ext.findall("require/enum")
2197 for enum_elem in enums:
2198 if "bitpos" in enum_elem.keys():
2199 # We need to add an extra value to an existing enum type.
2200 # E.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG to VkFormatFeatureFlagBits.
2201 type_name = enum_elem.attrib["extends"]
2202 enum = self.types[type_name]["data"]
2203 enum.add(VkEnumValue(enum_elem.attrib["name"], 1 << int(enum_elem.attrib["bitpos"]), hex=True))
2204 elif "offset" in enum_elem.keys():
2205 ext_number = int(ext.attrib["number"])
2206 offset = int(enum_elem.attrib["offset"])
2207 value = EXT_BASE + (ext_number - 1) * EXT_BLOCK_SIZE + offset
2209 # Deal with negative values.
2210 direction = enum_elem.attrib.get("dir")
2211 if direction is not None:
2214 type_name = enum_elem.attrib["extends"]
2215 enum = self.types[type_name]["data"]
2216 enum.add(VkEnumValue(enum_elem.attrib["name"], value))
2218 elif "value" in enum_elem.keys():
2219 # For now skip, it mostly contains extension name and version info.
2222 # This seems to be used to pull in constants e.g. VK_MAX_DEVICE_GROUP_KHX
2225 # Store a list with extensions.
2226 ext_type = ext.attrib["type"]
2227 ext_info = {"name" : ext_name, "type" : ext_type}
2228 extensions.append(ext_info)
2230 commands = ext.findall("require/command")
2234 # Pull in any commands we need. We infer types to pull in from the command
2236 for command in commands:
2237 cmd_name = command.attrib["name"]
2238 self._mark_command_required(cmd_name)
2240 # Set extension name on the function call as we were not aware of the
2241 # name during initial parsing.
2242 self.funcs[cmd_name].extension = ext_name
2244 # Sort in alphabetical order.
2245 self.extensions = sorted(extensions, key=lambda ext: ext["name"])
2247 def _parse_features(self, root):
2248 """ Parse the feature section, which describes Core commands and types needed. """
2249 requires = root.findall("./feature/require")
2251 for require in requires:
2252 LOGGER.info("Including features for {0}".format(require.attrib.get("comment")))
2254 # Only deal with command. Other values which appear are enum and type for pulling in some
2255 # constants and macros. Tricky to parse, so don't bother right now, we will generate them
2257 name = tag.attrib["name"]
2258 if tag.tag == "command":
2259 self._mark_command_required(name)
2260 elif tag.tag == "enum":
2261 # We could pull in relevant constants here. Unfortunately
2262 # this only gets half of them pulled in as others indirectly
2263 # get pulled in through structures. Constants don't harm us,
2266 elif tag.tag == "type":
2267 # Pull in types which may not have been pulled in through commands.
2269 # Skip pull in for vk_platform.h for now.
2270 if name == "vk_platform":
2273 type_info = self.types[name]
2274 type_info["data"].required = True
2276 def _parse_types(self, root):
2277 """ Parse types section, which contains all data types e.g. structs, typedefs etcetera. """
2278 types = root.findall("./types/type")
2289 type_info["category"] = t.attrib.get("category", None)
2291 if type_info["category"] in ["include"]:
2294 if type_info["category"] == "basetype":
2295 name = t.find("name").text
2296 _type = t.find("type").text
2297 basetype = VkBaseType(name, _type)
2298 base_types.append(basetype)
2299 type_info["data"] = basetype
2301 if type_info["category"] == "bitmask":
2302 name = t.find("name").text
2303 _type = t.find("type").text
2305 # Most bitmasks have a requires attribute used to pull in
2306 # required '*FlagBits" enum.
2307 requires = t.attrib.get("requires")
2308 bitmask = VkBaseType(name, _type, requires=requires)
2309 bitmasks.append(bitmask)
2310 type_info["data"] = bitmask
2312 if type_info["category"] == "define":
2313 name = t.attrib.get("name")
2314 define = VkDefine.from_xml(t)
2315 defines.append(define)
2316 type_info["data"] = define
2318 if type_info["category"] == "enum":
2319 name = t.attrib.get("name")
2320 # The type section only contains enum names, not the actual definition.
2321 # Since we already parsed the enum before, just link it in.
2323 type_info["data"] = self.enums[name]
2324 except KeyError as e:
2325 # Not all enums seem to be defined yet, typically that's for
2326 # ones ending in 'FlagBits' where future extensions may add
2328 type_info["data"] = None
2330 if type_info["category"] == "funcpointer":
2331 funcpointer = VkFunctionPointer.from_xml(t)
2332 funcpointers.append(funcpointer)
2333 type_info["data"] = funcpointer
2335 if type_info["category"] == "handle":
2336 handle = VkHandle.from_xml(t)
2337 handles.append(handle)
2338 type_info["data"] = handle
2340 if type_info["category"] in ["struct", "union"]:
2341 # We store unions among structs as some structs depend
2342 # on unions. The types are very similar in parsing and
2343 # generation anyway. The official vulkan scripts use
2344 # a similar kind of hack.
2345 struct = VkStruct.from_xml(t)
2346 structs.append(struct)
2347 type_info["data"] = struct
2349 # Name is in general within a name tag else it is an optional
2350 # attribute on the type tag.
2351 name_elem = t.find("name")
2352 if name_elem is not None:
2353 type_info["name"] = name_elem.text
2355 type_info["name"] = t.attrib.get("name", None)
2357 # Store all type data in a shared dictionary, so we can easily
2358 # look up information for a given type. There are no duplicate
2360 self.types[type_info["name"]] = type_info
2362 # We need detailed type information during code generation
2363 # on structs for alignment reasons. Unfortunately structs
2364 # are parsed among other types, so there is no guarantee
2365 # that any types needed have been parsed already, so set
2367 for struct in structs:
2368 struct.set_type_info(self.types)
2370 # Guarantee everything is sorted, so code generation doesn't have
2371 # to deal with this.
2372 self.base_types = sorted(base_types, key=lambda base_type: base_type.name)
2373 self.bitmasks = sorted(bitmasks, key=lambda bitmask: bitmask.name)
2374 self.defines = defines
2375 self.funcpointers = sorted(funcpointers, key=lambda fp: fp.name)
2376 self.handles = sorted(handles, key=lambda handle: handle.name)
2377 self.structs = sorted(structs, key=lambda struct: struct.name)
2381 parser = argparse.ArgumentParser()
2382 parser.add_argument("-v", "--verbose", action="count", default=0, help="increase output verbosity")
2384 args = parser.parse_args()
2385 if args.verbose == 0:
2386 LOGGER.setLevel(logging.WARNING)
2387 elif args.verbose == 1:
2388 LOGGER.setLevel(logging.INFO)
2390 LOGGER.setLevel(logging.DEBUG)
2392 registry = VkRegistry("vk.xml")
2393 generator = VkGenerator(registry)
2395 with open(WINE_VULKAN_H, "w") as f:
2396 generator.generate_vulkan_h(f)
2398 with open(WINE_VULKAN_DRIVER_H, "w") as f:
2399 generator.generate_vulkan_driver_h(f)
2401 with open(WINE_VULKAN_THUNKS_H, "w") as f:
2402 generator.generate_thunks_h(f, "wine_")
2404 with open(WINE_VULKAN_THUNKS_C, "w") as f:
2405 generator.generate_thunks_c(f, "wine_")
2407 if __name__ == "__main__":