[PATCH 1/5] winevulkan: Implement physical device core functions.

Józef Kucia joseph.kucia at gmail.com
Fri Mar 9 10:08:12 CST 2018


The patches look mostly good to me except for a potential issue
related to the structure conversion on ARM (see below). Thanks!

On Thu, Mar 8, 2018 at 7:57 AM, Roderick Colenbrander
<thunderbird2k at gmail.com> wrote:
> Signed-off-by: Roderick Colenbrander <thunderbird2k at gmail.com>
> ---
>  dlls/winevulkan/make_vulkan     | 734 +++++++++++++++++++++++++++++++++++++++-
>  dlls/winevulkan/vulkan_thunks.c | 220 +++++++++++-
>  dlls/winevulkan/vulkan_thunks.h | 164 +++++++++
>  3 files changed, 1105 insertions(+), 13 deletions(-)
>
> diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan
> index 7499cf7011..2aec101723 100755
> --- a/dlls/winevulkan/make_vulkan
> +++ b/dlls/winevulkan/make_vulkan
> @@ -68,7 +68,6 @@ WINE_VULKAN_DRIVER_H = "../../include/wine/vulkan_driver.h"
>  WINE_VULKAN_THUNKS_C = "vulkan_thunks.c"
>  WINE_VULKAN_THUNKS_H = "vulkan_thunks.h"
>
> -
>  # Functions part of our winevulkan graphics driver interface.
>  # DRIVER_VERSION should be bumped on any change to driver interface
>  # in FUNCTION_OVERRIDES
> @@ -95,6 +94,13 @@ FUNCTION_OVERRIDES = {
>  }
>
>
> +class Direction(Enum):
> +    """ Parameter direction: input, output, input_output. """
> +    INPUT = 1
> +    OUTPUT = 2
> +    INPUT_OUTPUT = 3
> +
> +
>  class VkBaseType(object):
>      def __init__(self, name, _type, requires=None):
>          """ Vulkan base type class.
> @@ -285,6 +291,21 @@ class VkFunction(object):
>
>          return VkFunction(_type=func_type, name=func_name, params=params)
>
> +    def get_conversions(self):
> +        """ Get a list of conversion functions required for this function if any.
> +        Parameters which are structures may require conversion between win32
> +        and the host platform. This function returns a list of conversions
> +        required.
> +        """
> +
> +        conversions = []
> +        for param in self.params:
> +            convs = param.get_conversions()
> +            if convs is not None:
> +                conversions.extend(convs)
> +
> +        return conversions
> +
>      def is_device_func(self):
>          # If none of the other, it must be a device function
>          return not self.is_global_func() and not self.is_instance_func()
> @@ -312,9 +333,32 @@ class VkFunction(object):
>      def is_required(self):
>          return self.required
>
> +    def needs_conversion(self):
> +        """ Check if the function needs any input/output type conversion.
> +        Funcsions need input/output conversion if struct parameters have

Typo: Funcsions

> +        alignment differences between Win32 and Linux 32-bit.
> +        """
> +
> +        for p in self.params:
> +            if p.needs_conversion():
> +                LOGGER.debug("Parameter {0} to {1} requires conversion".format(p.name, self.name))
> +                return True
> +
> +        return False
> +
>      def needs_dispatch(self):
>          return self.dispatch
>
> +    def needs_stub(self):
> +        """ Temporary function to limit script hacks until more code is implemented. """
> +        if self.name == "vkCreateDevice":
> +            return True
> +
> +        if self.params[0].type != "VkPhysicalDevice":
> +            return True
> +
> +        return False
> +
>      def needs_thunk(self):
>          return self.thunk_needed
>
> @@ -374,6 +418,77 @@ class VkFunction(object):
>
>          return proto
>
> +    def body(self):
> +        body = "    {0}".format(self.trace())
> +
> +        params = ", ".join([p.variable(conv=False) for p in self.params])
> +
> +        # Call the native Vulkan function.
> +        if self.type == "void":
> +            body += "    {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
> +        else:
> +            body += "    return {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
> +
> +        return body
> +
> +    def body_conversion(self):
> +        body = ""
> +
> +        # Declare a variable to hold the result for non-void functions.
> +        if self.type != "void":
> +            body += "    {0} result;\n".format(self.type)
> +
> +        # Declare any tmp parameters for conversion.
> +        for p in self.params:
> +            if not p.needs_conversion():
> +                continue
> +
> +            if p.is_dynamic_array():
> +                body += "    {0}_host *{1}_host;\n".format(p.type, p.name)
> +            else:
> +                body += "    {0}_host {1}_host;\n".format(p.type, p.name)
> +
> +        body += "    {0}\n".format(self.trace())
> +
> +        # Call any win_to_host conversion calls.
> +        for p in self.params:
> +            if not p.needs_input_conversion():
> +                continue
> +
> +            body += p.copy(Direction.INPUT)
> +
> +        # Build list of parameters containing converted and non-converted parameters.
> +        # The param itself knows if conversion is needed and applies it when we set conv=True.
> +        params = ", ".join([p.variable(conv=True) for p in self.params])
> +
> +        # Call the native Vulkan function.
> +        if self.type == "void":
> +            body += "    {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
> +        else:
> +            body += "    result = {0}.p_{1}({2});\n".format(self.params[0].dispatch_table(), self.name, params)
> +
> +        body += "\n"
> +
> +        # Call any host_to_win conversion calls.
> +        for p in self.params:
> +            if not p.needs_output_conversion():
> +                continue
> +
> +            body += p.copy(Direction.OUTPUT)
> +
> +        # Perform any required cleanups. Most of these are for array functions.
> +        for p in self.params:
> +            if not p.needs_free():
> +                continue
> +
> +            body += p.free()
> +
> +        # Finally return the result.
> +        if self.type != "void":
> +            body += "    return result;\n"
> +
> +        return body
> +
>      def stub(self, call_conv=None, prefix=None):
>          stub = self.prototype(call_conv=call_conv, prefix=prefix)
>          stub += "\n{\n"
> @@ -387,6 +502,22 @@ class VkFunction(object):
>          stub += "}\n\n"
>          return stub
>
> +    def thunk(self, call_conv=None, prefix=None):
> +        thunk = self.prototype(call_conv=call_conv, prefix=prefix)
> +        thunk += "\n{\n"
> +
> +        if self.needs_conversion():
> +            thunk += "#if defined(USE_STRUCT_CONVERSION)\n"
> +            thunk += self.body_conversion()
> +            thunk += "#else\n"
> +            thunk += self.body()
> +            thunk += "#endif\n"
> +        else:
> +            thunk += self.body()
> +
> +        thunk += "}\n\n"
> +        return thunk
> +
>      def trace(self, message=None, trace_func=None):
>          """ Create a trace string including all parameters.
>
> @@ -492,6 +623,23 @@ class VkHandle(object):
>          parent = handle.attrib.get("parent") # Most objects have a parent e.g. VkQueue has VkDevice.
>          return VkHandle(name, _type, parent)
>
> +    def dispatch_table(self):
> +        if not self.is_dispatchable():
> +            return None
> +
> +        if self.parent is None:
> +            # Should only happen for VkInstance
> +            return "funcs"
> +        elif self.name == "VkDevice":
> +            # VkDevice has VkInstance as a parent, but has its own dispatch table.
> +            return "funcs"
> +        elif self.parent in ["VkInstance", "VkPhysicalDevice"]:
> +            return "instance->funcs"
> +        elif self.parent in ["VkDevice", "VkCommandPool"]:
> +            return "device->funcs"
> +        else:
> +            LOGGER.error("Unhandled dispatchable parent: {0}".format(self.parent))
> +
>      def definition(self):
>          """ Generates handle definition e.g. VK_DEFINE_HANDLE(vkInstance) """
>          return "{0}({1})\n".format(self.type, self.name)
> @@ -502,6 +650,28 @@ class VkHandle(object):
>          """
>          return self.type == "VK_DEFINE_HANDLE"
>
> +    def native_handle(self):
> +        """ Provide access to the native handle of a dispatchable object.
> +
> +        Dispatchable objects wrap an underlaying 'native' object.

Typo: underlying

> +        This method provides access to the native object.
> +        """
> +        if not self.is_dispatchable():
> +            return None
> +
> +        if self.name == "VkCommandBuffer":
> +            return "command_buffer"
> +        elif self.name == "VkDevice":
> +            return "device"
> +        elif self.name == "VkInstance":
> +            return "instance"
> +        elif self.name == "VkPhysicalDevice":
> +            return "phys_dev"
> +        elif self.name == "VkQueue":
> +            return "queue"
> +        else:
> +            LOGGER.error("Unhandled native handle for: {0}".format(self.name))
> +
>
>  class VkMember(object):
>      def __init__(self, const=None, _type=None, pointer=None, name=None, array_len=None, dyn_array_len=None, optional=False,
> @@ -582,6 +752,36 @@ class VkMember(object):
>          return VkMember(const=const, _type=member_type, pointer=pointer, name=name_elem.text, array_len=array_len,
>                  dyn_array_len=dyn_array_len, optional=optional, extension_structs=extension_structs)
>
> +    def copy(self, input, output, direction):
> +        """ Helper method for use by conversion logic to generate a C-code statement to copy this member. """
> +
> +        if self.needs_conversion():
> +            if self.is_dynamic_array():
> +                if direction == Direction.OUTPUT:
> +                    LOGGER.warn("TODO: implement copying of returnedonly dynamic array for {0}.{1}".format(self.type, self.name))
> +                else:
> +                    # Array length is either a variable name (string) or an int.
> +                    count = self.dyn_array_len if isinstance(self.dyn_array_len, int) else "{0}{1}".format(input, self.dyn_array_len)
> +                    return "{0}{1} = convert_{2}_array_win_to_host({3}{1}, {4});\n".format(output, self.name, self.type, input, count)
> +            elif self.is_static_array():
> +                count = self.array_len
> +                if direction == Direction.OUTPUT:
> +                    # Needed by VkMemoryHeap.memoryHeaps
> +                    return "convert_{0}_static_array_host_to_win({2}{1}, {3}{1}, {4});\n".format(self.type, self.name, input, output, count)
> +                else:
> +                    # Nothing needed this yet.
> +                    LOGGER.warn("TODO: implement copying of static array for {0}.{1}".format(self.type, self.name))
> +            else:
> +                if direction == Direction.OUTPUT:
> +                    return "convert_{0}_host_to_win(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
> +                else:
> +                    return "convert_{0}_win_to_host(&{2}{1}, &{3}{1});\n".format(self.type, self.name, input, output)
> +        elif self.is_static_array():
> +            bytes_count = "{0} * sizeof({1})".format(self.array_len, self.type)
> +            return "memcpy({0}{1}, {2}{1}, {3});\n".format(output, self.name, input, bytes_count)
> +        else:
> +            return "{0}{1} = {2}{1};\n".format(output, self.name, input)
> +
>      def definition(self, align=False, conv=False):
>          """ Generate prototype for given function.
>
> @@ -612,6 +812,36 @@ class VkMember(object):
>
>          return text
>
> +    def get_conversions(self):
> +        """ Return any conversion description for this member and its children when conversion is needed. """
> +
> +        # Check if we need conversion either for this member itself or for any child members
> +        # in case member represents a struct.
> +        if not self.needs_conversion():
> +            return None
> +
> +        conversions = []
> +
> +        # Collect any conversion for any member structs.
> +        struct = self.type_info["data"]
> +        for m in struct:
> +            if m.needs_conversion():
> +                conversions.extend(m.get_conversions())
> +
> +        struct = self.type_info["data"]
> +        direction = Direction.OUTPUT if struct.returnedonly else Direction.INPUT
> +        if self.is_dynamic_array():
> +            conversions.append(ConversionFunction(False, True, direction, struct))
> +        elif self.is_static_array():
> +            conversions.append(ConversionFunction(True, False, direction, struct))
> +        else:
> +            conversions.append(ConversionFunction(False, False, direction, struct))
> +
> +        if self.needs_free():
> +            conversions.append(FreeFunction(self.is_dynamic_array(), struct))
> +
> +        return conversions
> +
>      def is_const(self):
>          return self.const is not None
>
> @@ -663,6 +893,26 @@ class VkMember(object):
>              return False if handle.is_dispatchable() else True
>          return False
>
> +    def needs_conversion(self):
> +        """ Structures requiring alignment, need conversion between win32 and host. """
> +
> +        if not self.is_struct():
> +            return False
> +
> +        struct = self.type_info["data"]
> +        return struct.needs_conversion()
> +
> +    def needs_free(self):
> +        if not self.needs_conversion():
> +            return False
> +
> +        if self.is_dynamic_array():
> +            return True
> +
> +        # TODO: some non-pointer structs and optional pointer structs may need freeing,
> +        # though none of this type have been encountered yet.
> +        return False
> +
>      def set_type_info(self, type_info):
>          """ Helper function to set type information from the type registry.
>          This is needed, because not all type data is available at time of
> @@ -685,7 +935,9 @@ class VkParam(object):
>          self.handle = type_info["data"] if type_info["category"] == "handle" else None
>          self.struct = type_info["data"] if type_info["category"] == "struct" else None
>
> +        self._set_direction()
>          self._set_format_string()
> +        self._set_conversions()
>
>      def __repr__(self):
>          return "{0} {1} {2} {3} {4}".format(self.const, self.type, self.pointer, self.name, self.array_len, self.dyn_array_len)
> @@ -722,6 +974,59 @@ class VkParam(object):
>
>          return VkParam(type_info, const=const, pointer=pointer, name=name, array_len=array_len, dyn_array_len=dyn_array_len)
>
> +    def _set_conversions(self):
> +        """ Internal helper function to configure any needed conversion functions. """
> +
> +        self.free_func = None
> +        self.input_conv = None
> +        self.output_conv = None
> +        if not self.needs_conversion():
> +            return
> +
> +        # Input functions require win to host conversion.
> +        if self._direction in [Direction.INPUT, Direction.INPUT_OUTPUT]:
> +            self.input_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.INPUT, self.struct)
> +
> +        # Output functions require host to win conversion.
> +        if self._direction in [Direction.INPUT_OUTPUT, Direction.OUTPUT]:
> +            self.output_conv = ConversionFunction(False, self.is_dynamic_array(), Direction.OUTPUT, self.struct)
> +
> +        # Dynamic arrays, but also some normal structs (e.g. VkCommandBufferBeginInfo) need memory
> +        # allocation and thus some cleanup.
> +        if self.is_dynamic_array() or self.struct.needs_free():
> +            self.free_func = FreeFunction(self.is_dynamic_array(), self.struct)
> +
> +    def _set_direction(self):
> +        """ Internal helper function to set parameter direction (input/output/input_output). """
> +
> +        # The parameter direction needs to be determined from hints in vk.xml like returnedonly,
> +        # parameter constness and other heuristics.
> +        # For now we need to get this right for structures as we need to convert these, we may have
> +        # missed a few other edge cases (e.g. count variables).
> +        # See also https://github.com/KhronosGroup/Vulkan-Docs/issues/610
> +
> +        if not self.is_pointer():
> +            self._direction = Direction.INPUT
> +        elif self.is_const() and self.is_pointer():
> +            self._direction = Direction.INPUT
> +        elif self.is_struct():
> +            if not self.struct.returnedonly:
> +                self._direction = Direction.INPUT
> +                return
> +
> +            # Returnedonly hints towards output, however in some cases
> +            # it is inputoutput. In particular if pNext / sType exist,
> +            # which are used to link in other structures without having
> +            # to introduce new APIs. E.g. vkGetPhysicalDeviceProperties2KHR.
> +            if "pNext" in self.struct:
> +                self._direction = Direction.INPUT_OUTPUT
> +                return
> +
> +            self._direction = Direction.OUTPUT
> +        else:
> +            # This should mostly be right. Count variables can be inout, but we don't care about these yet.
> +            self._direction = Direction.OUTPUT
> +
>      def _set_format_string(self):
>          """ Internal helper function to be used by constructor to set format string. """
>
> @@ -765,6 +1070,18 @@ class VkParam(object):
>              else:
>                  LOGGER.warn("Unhandled type: {0}".format(self.type_info))
>
> +    def copy(self, direction):
> +        if direction == Direction.INPUT:
> +            if self.is_dynamic_array():
> +                return "    {0}_host = convert_{1}_array_win_to_host({0}, {2});\n".format(self.name, self.type, self.dyn_array_len)
> +            else:
> +                return "    convert_{0}_win_to_host({1}, &{1}_host);\n".format(self.type, self.name)
> +        else:
> +            if self.is_dynamic_array():
> +                LOGGER.error("Unimplemented output conversion for: {0}".format(self.name))
> +            else:
> +                return "    convert_{0}_host_to_win(&{1}_host, {1});\n".format(self.type, self.name)
> +
>      def definition(self, postfix=None):
>          """ Return prototype for the parameter. E.g. 'const char *foo' """
>
> @@ -800,6 +1117,68 @@ class VkParam(object):
>      def format_string(self):
>          return self.format_str
>
> +    def dispatch_table(self):
> +        """ Return functions dispatch table pointer for dispatchable objects. """
> +
> +        if not self.is_dispatchable():
> +            return None
> +
> +        return "{0}->{1}".format(self.name, self.handle.dispatch_table())
> +
> +    def format_string(self):
> +        return self.format_str
> +
> +    def free(self):
> +        if self.is_dynamic_array():
> +            if self.struct.returnedonly:
> +                # For returnedonly, counts is stored in a pointer.
> +                return "    free_{0}_array({1}_host, *{2});\n".format(self.type, self.name, self.dyn_array_len)
> +            else:
> +                return "    free_{0}_array({1}_host, {2});\n".format(self.type, self.name, self.dyn_array_len)
> +        else:
> +            # We are operating on a single structure. Some structs (very rare) contain dynamic members,
> +            # which would need freeing.
> +            if self.struct.needs_free():
> +                return "    free_{0}(&{1}_host);\n".format(self.type, self.name)
> +        return ""
> +
> +    def get_conversions(self):
> +        """ Get a list of conversions required for this parameter if any.
> +        Parameters which are structures may require conversion between win32
> +        and the host platform. This function returns a list of conversions
> +        required.
> +        """
> +
> +        if not self.is_struct():
> +            return None
> +
> +        if not self.needs_conversion():
> +            return None
> +
> +        conversions = []
> +
> +        # Collect any member conversions first, so we can guarantee
> +        # those functions will be defined prior to usage by the
> +        # 'parent' param requiring conversion.
> +        for m in self.struct:
> +            if not m.is_struct():
> +                continue
> +
> +            if not m.needs_conversion():
> +                continue
> +
> +            conversions.extend(m.get_conversions())
> +
> +        # Conversion requirements for the 'parent' parameter.
> +        if self.input_conv is not None:
> +            conversions.append(self.input_conv)
> +        if self.output_conv is not None:
> +            conversions.append(self.output_conv)
> +        if self.free_func is not None:
> +            conversions.append(self.free_func)
> +
> +        return conversions
> +
>      def is_const(self):
>          return self.const is not None
>
> @@ -824,6 +1203,62 @@ class VkParam(object):
>      def is_struct(self):
>          return self.struct is not None
>
> +    def needs_conversion(self):
> +        """ Returns if parameter needs conversion between win32 and host. """
> +
> +        if not self.is_struct():
> +            return False
> +
> +        # VkSparseImageMemoryRequirements is used by vkGetImageSparseMemoryRequirements.
> +        # This function is tricky to wrap, because how to wrap depends on whether
> +        # pSparseMemoryRequirements is NULL or not. Luckily for VkSparseImageMemoryRequirements
> +        # the alignment works out in such a way that no conversion is needed between win32 and Linux.
> +        if self.type == "VkSparseImageMemoryRequirements":
> +            return False
> +
> +        # If a structure needs alignment changes, it means we need to
> +        # perform parameter conversion between win32 and host.
> +        if self.struct.needs_conversion():
> +            return True
> +
> +        return False
> +
> +    def needs_free(self):
> +        return self.free_func is not None
> +
> +    def needs_input_conversion(self):
> +        return self.input_conv is not None
> +
> +    def needs_output_conversion(self):
> +        return self.output_conv is not None
> +
> +    def variable(self, conv=False):
> +        """ Returns 'glue' code during generation of a function call on how to access the variable.
> +        This function handles various scenarios such as 'unwrapping' if dispatchable objects and
> +        renaming of parameters in case of win32 -> host conversion.
> +
> +        Args:
> +            conv (bool, optional): Enable conversion if the param needs it. This appends '_host' to the name.
> +        """
> +
> +        # Hack until we enable allocation callbacks from ICD to application. These are a joy
> +        # to enable one day, because of calling convention conversion.
> +        if "VkAllocationCallbacks" in self.type:
> +            LOGGER.debug("HACK: setting NULL VkAllocationCallbacks for {0}".format(self.name))
> +            return "NULL"

I think it would be better to call it a TODO instead of HACK.

> +
> +        # Dispatchable objects wrap the native handle. For thunk generation we
> +        # need to pass the native handle to the native vulkan calls.
> +        if self.is_dispatchable():
> +            return "{0}->{1}".format(self.name, self.handle.native_handle())
> +        elif conv and self.needs_conversion():
> +            if self.is_dynamic_array():
> +                return "{0}_host".format(self.name)
> +            else:
> +                return "&{0}_host".format(self.name)
> +        else:
> +            return self.name
> +
>
>  class VkStruct(Sequence):
>      """ Class which represents the type union and struct. """
> @@ -953,6 +1388,31 @@ class VkStruct(Sequence):
>                  return True
>          return False
>
> +    def needs_conversion(self):
> +        """ Returns if struct members needs conversion between win32 and host.
> +        Structures need conversion if they contain members requiring alignment
> +        or if they include other structures which need alignment.
> +        """
> +
> +        if self.needs_alignment():
> +            return True
> +
> +        for m in self.members:
> +            if m.needs_conversion():
> +                return True
> +        return False
> +
> +    def needs_free(self):
> +        """ Check if any struct member needs some memory freeing."""
> +
> +        for m in self.members:
> +            if m.needs_free():
> +                return True
> +
> +            continue
> +
> +        return False
> +
>      def set_type_info(self, types):
>          """ Helper function to set type information from the type registry.
>          This is needed, because not all type data is available at time of
> @@ -963,10 +1423,251 @@ class VkStruct(Sequence):
>              m.set_type_info(type_info)
>
>
> +class ConversionFunction(object):
> +    def __init__(self, array, dyn_array, direction, struct):
> +        self.array = array
> +        self.direction = direction
> +        self.dyn_array = dyn_array
> +        self.struct = struct
> +        self.type = struct.name
> +
> +        self._set_name()
> +
> +    def __eq__(self, other):
> +        if self.name != other.name:
> +            return False
> +
> +        return True
> +
> +    def _generate_array_conversion_func(self):
> +        """ Helper function for generating a conversion function for array structs. """
> +
> +        if self.direction == Direction.OUTPUT:
> +            params = ["const {0}_host *in".format(self.type), "uint32_t count"]
> +            return_type = self.type
> +        else:
> +            params = ["const {0} *in".format(self.type), "uint32_t count"]
> +            return_type = "{0}_host".format(self.type)
> +
> +        # Generate function prototype.
> +        body = "static inline {0} * {1}(".format(return_type, self.name)
> +        body += ", ".join(p for p in params)
> +        body += ")\n{\n"
> +
> +        body += "    {0} *out;\n".format(return_type)
> +        body += "    unsigned int i;\n\n"
> +        body += "    if (!in) return NULL;\n\n"
> +
> +        body += "    out = ({0} *)heap_alloc(count * sizeof(*out));\n".format(return_type)
> +
> +        body += "    for (i = 0; i < count; i++)\n"
> +        body += "    {\n"
> +
> +        for m in self.struct:
> +            # TODO: support copying of pNext extension structures!
> +            # Luckily though no extension struct at this point needs conversion.
> +            body += "        " + m.copy("in[i].", "out[i].", self.direction)
> +
> +        body += "    }\n\n"
> +        body += "    return out;\n"
> +        body += "}\n\n"
> +        return body
> +
> +    def _generate_conversion_func(self):
> +        """ Helper function for generating a conversion function for non-array structs. """
> +
> +        if self.direction == Direction.OUTPUT:
> +            params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type)]
> +        else:
> +            params = ["const {0} *in".format(self.type), "{0}_host *out".format(self.type)]
> +
> +        body = "static inline void {0}(".format(self.name)
> +
> +        # Generate parameter list
> +        body += ", ".join(p for p in params)
> +        body += ")\n{\n"
> +
> +        body += "    if (!in) return;\n\n"
> +
> +        if self.direction == Direction.INPUT and "pNext" in self.struct and self.struct.returnedonly:
> +            # We are dealing with an input_output parameter. For these we only need to copy
> +            # pNext and sType as the other fields are filled in by the host. We do potentially
> +            # have to iterate over pNext and perform conversions based on switch(sType)!
> +            # Luckily though no extension structs at this point need conversion.
> +            # TODO: support copying of pNext extension structures!
> +            body += "    out->pNext = in->pNext;\n"
> +            body += "    out->sType = in->sType;\n"
> +        else:
> +            for m in self.struct:
> +                # TODO: support copying of pNext extension structures!
> +                body += "    " + m.copy("in->", "out->", self.direction)
> +
> +        body += "}\n\n"
> +        return body
> +
> +    def _generate_static_array_conversion_func(self):
> +        """ Helper function for generating a conversion function for array structs. """
> +
> +        if self.direction == Direction.OUTPUT:
> +            params = ["const {0}_host *in".format(self.type), "{0} *out".format(self.type), "uint32_t count"]
> +            return_type = self.type
> +        else:
> +            params = ["const {0} *in".format(self.type), "{0} *out_host".format(self.type), "uint32_t count"]
> +            return_type = "{0}_host".format(self.type)
> +
> +        # Generate function prototype.
> +        body = "static inline void {0}(".format(self.name)
> +        body += ", ".join(p for p in params)
> +        body += ")\n{\n"
> +        body += "    unsigned int i;\n\n"
> +        body += "    if (!in) return;\n\n"
> +        body += "    for (i = 0; i < count; i++)\n"
> +        body += "    {\n"
> +
> +        for m in self.struct:
> +            # TODO: support copying of pNext extension structures!
> +            body += "        " + m.copy("in[i].", "out[i].", self.direction)
> +
> +        body += "    }\n"
> +        body += "}\n\n"
> +        return body
> +
> +    def _set_name(self):
> +        if self.direction == Direction.INPUT:
> +            if self.array:
> +                name = "convert_{0}_static_array_win_to_host".format(self.type)
> +            elif self.dyn_array:
> +                name = "convert_{0}_array_win_to_host".format(self.type)
> +            else:
> +                name = "convert_{0}_win_to_host".format(self.type)
> +        else: # Direction.OUTPUT
> +            if self.array:
> +                name = "convert_{0}_static_array_host_to_win".format(self.type)
> +            elif self.dyn_array:
> +                name = "convert_{0}_array_host_to_win".format(self.type)
> +            else:
> +                name = "convert_{0}_host_to_win".format(self.type)
> +
> +        self.name = name
> +
> +    def definition(self):
> +        if self.array:
> +            return self._generate_static_array_conversion_func()
> +        elif self.dyn_array:
> +            return self._generate_array_conversion_func()
> +        else:
> +            return self._generate_conversion_func()
> +
> +
> +class FreeFunction(object):
> +    def __init__(self, dyn_array, struct):
> +        self.dyn_array = dyn_array
> +        self.struct = struct
> +        self.type = struct.name
> +
> +        if dyn_array:
> +            self.name = "free_{0}_array".format(self.type)
> +        else:
> +            self.name = "free_{0}".format(self.type)
> +
> +    def __eq__(self, other):
> +        if self.name == other.name:
> +            return True
> +
> +        return False
> +
> +    def _generate_array_free_func(self):
> +        """ Helper function for cleaning up temporary buffers required for array conversions. """
> +
> +        # Generate function prototype.
> +        body = "static inline void {0}({1}_host *in, uint32_t count)\n{{\n".format(self.name, self.type)
> +
> +        # E.g. VkGraphicsPipelineCreateInfo_host needs freeing for pStages.
> +        if self.struct.needs_free():
> +            body += "    unsigned int i;\n\n"
> +            body += "    if (!in) return;\n\n"
> +            body += "    for (i = 0; i < count; i++)\n"
> +            body += "    {\n"
> +
> +            for m in self.struct:
> +                if m.needs_conversion() and m.is_dynamic_array():
> +                    if m.is_const():
> +                        # Add a cast to ignore const on conversion structs we allocated ourselves.
> +                        body += "        free_{0}_array(({0}_host *)in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
> +                    else:
> +                        body += "        free_{0}_array(in[i].{1}, in[i].{2});\n".format(m.type, m.name, m.dyn_array_len)
> +                elif m.needs_conversion():
> +                    LOGGER.error("Unhandled conversion for {0}".format(m.name))
> +            body += "    }\n"
> +        else:
> +            body += "    if (!in) return;\n\n"
> +
> +        body += "    heap_free(in);\n"
> +
> +        body += "}\n\n"
> +        return body
> +
> +    def _generate_free_func(self):
> +        # E.g. VkCommandBufferBeginInfo.pInheritanceInfo needs freeing.
> +        if not self.struct.needs_free():
> +            return ""
> +
> +        # Generate function prototype.
> +        body = "static inline void {0}({1}_host *in)\n{{\n".format(self.name, self.type)
> +
> +        for m in self.struct:
> +            if m.needs_conversion() and m.is_dynamic_array():
> +                count = m.dyn_array_len if isinstance(m.dyn_array_len, int) else "in->{0}".format(m.dyn_array_len)
> +                if m.is_const():
> +                    # Add a cast to ignore const on conversion structs we allocated ourselves.
> +                    body += "    free_{0}_array(({0}_host *)in->{1}, {2});\n".format(m.type, m.name, count)
> +                else:
> +                    body += "    free_{0}_array(in->{1}, {2});\n".format(m.type, m.name, count)
> +
> +        body += "}\n\n"
> +        return body
> +
> +    def definition(self):
> +        if self.dyn_array:
> +            return self._generate_array_free_func()
> +        else:
> +            # Some structures need freeing too if they contain dynamic arrays.
> +            # E.g. VkCommandBufferBeginInfo
> +            return self._generate_free_func()
> +
> +
>  class VkGenerator(object):
>      def __init__(self, registry):
>          self.registry = registry
>
> +        # Build a list conversion functions for struct conversion.
> +        self.conversions = []
> +        self.host_structs = []
> +        for func in self.registry.funcs.values():
> +            if not func.is_required():
> +                continue
> +
> +            # Temporary filter.
> +            if func.needs_stub():
> +                continue
> +
> +            if not func.needs_conversion():
> +                continue
> +
> +            conversions = func.get_conversions()
> +            for conv in conversions:
> +                # Pull in any conversions for vulkan_thunks.c. Driver conversions we
> +                # handle manually in vulkan.c if needed.
> +                if not func.is_driver_func():
> +                    # Append if we don't already have this conversion.
> +                    if not any(c == conv for c in self.conversions):
> +                        self.conversions.append(conv)
> +
> +                # Structs can be used in different ways by different conversions
> +                # e.g. array vs non-array. Just make sure we pull in each struct once.
> +                if not any(s.name == conv.struct.name for s in self.host_structs):
> +                    self.host_structs.append(conv.struct)
> +
>      def generate_thunks_c(self, f, prefix):
>          f.write("/* Automatically generated from Vulkan vk.xml; DO NOT EDIT! */\n\n")
>
> @@ -974,12 +1675,19 @@ class VkGenerator(object):
>          f.write("#include \"wine/port.h\"\n\n")
>
>          f.write("#include \"wine/debug.h\"\n")
> +        f.write("#include \"wine/heap.h\"\n")
>          f.write("#include \"wine/vulkan.h\"\n")
>          f.write("#include \"wine/vulkan_driver.h\"\n")
>          f.write("#include \"vulkan_private.h\"\n\n")
>
>          f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
>
> +        # Generate any conversion helper functions.
> +        f.write("#if defined(USE_STRUCT_CONVERSION)\n")
> +        for conv in self.conversions:
> +            f.write(conv.definition())
> +        f.write("#endif /* USE_STRUCT_CONVERSION */\n\n")
> +
>          # Create thunks for instance functions.
>          # Global functions don't go through the thunks.
>          for vk_func in self.registry.funcs.values():
> @@ -997,7 +1705,11 @@ class VkGenerator(object):
>              if not vk_func.needs_thunk():
>                  continue
>
> -            f.write("static " + vk_func.stub(prefix=prefix, call_conv="WINAPI"))
> +            # Temporary filter.
> +            if vk_func.needs_stub():
> +                f.write("static " + vk_func.stub(prefix=prefix, call_conv="WINAPI"))
> +            else:
> +                f.write("static " + vk_func.thunk(prefix=prefix, call_conv="WINAPI"))
>
>          f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
>          for vk_func in self.registry.instance_funcs:
> @@ -1031,6 +1743,11 @@ class VkGenerator(object):
>          f.write("#ifndef __WINE_VULKAN_THUNKS_H\n")
>          f.write("#define __WINE_VULKAN_THUNKS_H\n\n")
>
> +        f.write("/* Perform vulkan struct conversion on 32-bit platforms. */\n")
> +        f.write("#if (defined(__i386__) || defined(__arm__))\n")
> +        f.write("    #define USE_STRUCT_CONVERSION\n")
> +        f.write("#endif\n\n")
> +
>          f.write("/* For use by vk_icdGetInstanceProcAddr / vkGetInstanceProcAddr */\n")
>          f.write("void *wine_vk_get_instance_proc_addr(const char *name) DECLSPEC_HIDDEN;\n\n")
>
> @@ -1043,6 +1760,10 @@ class VkGenerator(object):
>              f.write("{0};\n".format(vk_func.prototype("WINAPI", prefix="wine_", postfix="DECLSPEC_HIDDEN")))
>          f.write("\n")
>
> +        for struct in self.host_structs:
> +            f.write(struct.definition(align=False, conv=True, postfix="_host"))
> +        f.write("\n")
> +
>          f.write("/* For use by vkInstance and children */\n")
>          f.write("struct vulkan_instance_funcs\n{\n")
>          for vk_func in self.registry.instance_funcs:
> @@ -1053,7 +1774,14 @@ class VkGenerator(object):
>                  LOGGER.debug("skipping {0} in vulkan_instance_funcs".format(vk_func.name))
>                  continue
>
> -            f.write("    {0};\n".format(vk_func.pfn(conv=False)))
> +            if vk_func.needs_conversion():
> +                f.write("#if defined(USE_STRUCT_CONVERSION)\n")
> +                f.write("    {0};\n".format(vk_func.pfn(conv=True)))
> +                f.write("#else\n")
> +                f.write("    {0};\n".format(vk_func.pfn(conv=False)))
> +                f.write("#endif\n")
> +            else:
> +                f.write("    {0};\n".format(vk_func.pfn(conv=False)))
>          f.write("};\n\n")
>
>          f.write("#define ALL_VK_INSTANCE_FUNCS() \\\n")
> diff --git a/dlls/winevulkan/vulkan_thunks.c b/dlls/winevulkan/vulkan_thunks.c
> index 2e1b62d23c..daab898522 100644
> --- a/dlls/winevulkan/vulkan_thunks.c
> +++ b/dlls/winevulkan/vulkan_thunks.c
> @@ -4,12 +4,177 @@
>  #include "wine/port.h"
>
>  #include "wine/debug.h"
> +#include "wine/heap.h"
>  #include "wine/vulkan.h"
>  #include "wine/vulkan_driver.h"
>  #include "vulkan_private.h"
>
>  WINE_DEFAULT_DEBUG_CHANNEL(vulkan);
>
> +#if defined(USE_STRUCT_CONVERSION)
> +static inline void convert_VkImageFormatProperties_host_to_win(const VkImageFormatProperties_host *in, VkImageFormatProperties *out)
> +{
> +    if (!in) return;
> +
> +    out->maxExtent = in->maxExtent;
> +    out->maxMipLevels = in->maxMipLevels;
> +    out->maxArrayLayers = in->maxArrayLayers;
> +    out->sampleCounts = in->sampleCounts;
> +    out->maxResourceSize = in->maxResourceSize;
> +}
> +
> +static inline void convert_VkMemoryHeap_static_array_host_to_win(const VkMemoryHeap_host *in, VkMemoryHeap *out, uint32_t count)
> +{
> +    unsigned int i;
> +
> +    if (!in) return;
> +
> +    for (i = 0; i < count; i++)
> +    {
> +        out[i].size = in[i].size;
> +        out[i].flags = in[i].flags;
> +    }
> +}
> +
> +static inline void convert_VkPhysicalDeviceMemoryProperties_host_to_win(const VkPhysicalDeviceMemoryProperties_host *in, VkPhysicalDeviceMemoryProperties *out)
> +{
> +    if (!in) return;
> +
> +    out->memoryTypeCount = in->memoryTypeCount;
> +    memcpy(out->memoryTypes, in->memoryTypes, VK_MAX_MEMORY_TYPES * sizeof(VkMemoryType));
> +    out->memoryHeapCount = in->memoryHeapCount;
> +    convert_VkMemoryHeap_static_array_host_to_win(in->memoryHeaps, out->memoryHeaps, VK_MAX_MEMORY_HEAPS);
> +}
> +
> +static inline void convert_VkPhysicalDeviceLimits_host_to_win(const VkPhysicalDeviceLimits_host *in, VkPhysicalDeviceLimits *out)
> +{
> +    if (!in) return;
> +
> +    out->maxImageDimension1D = in->maxImageDimension1D;
> +    out->maxImageDimension2D = in->maxImageDimension2D;
> +    out->maxImageDimension3D = in->maxImageDimension3D;
> +    out->maxImageDimensionCube = in->maxImageDimensionCube;
> +    out->maxImageArrayLayers = in->maxImageArrayLayers;
> +    out->maxTexelBufferElements = in->maxTexelBufferElements;
> +    out->maxUniformBufferRange = in->maxUniformBufferRange;
> +    out->maxStorageBufferRange = in->maxStorageBufferRange;
> +    out->maxPushConstantsSize = in->maxPushConstantsSize;
> +    out->maxMemoryAllocationCount = in->maxMemoryAllocationCount;
> +    out->maxSamplerAllocationCount = in->maxSamplerAllocationCount;
> +    out->bufferImageGranularity = in->bufferImageGranularity;
> +    out->sparseAddressSpaceSize = in->sparseAddressSpaceSize;
> +    out->maxBoundDescriptorSets = in->maxBoundDescriptorSets;
> +    out->maxPerStageDescriptorSamplers = in->maxPerStageDescriptorSamplers;
> +    out->maxPerStageDescriptorUniformBuffers = in->maxPerStageDescriptorUniformBuffers;
> +    out->maxPerStageDescriptorStorageBuffers = in->maxPerStageDescriptorStorageBuffers;
> +    out->maxPerStageDescriptorSampledImages = in->maxPerStageDescriptorSampledImages;
> +    out->maxPerStageDescriptorStorageImages = in->maxPerStageDescriptorStorageImages;
> +    out->maxPerStageDescriptorInputAttachments = in->maxPerStageDescriptorInputAttachments;
> +    out->maxPerStageResources = in->maxPerStageResources;
> +    out->maxDescriptorSetSamplers = in->maxDescriptorSetSamplers;
> +    out->maxDescriptorSetUniformBuffers = in->maxDescriptorSetUniformBuffers;
> +    out->maxDescriptorSetUniformBuffersDynamic = in->maxDescriptorSetUniformBuffersDynamic;
> +    out->maxDescriptorSetStorageBuffers = in->maxDescriptorSetStorageBuffers;
> +    out->maxDescriptorSetStorageBuffersDynamic = in->maxDescriptorSetStorageBuffersDynamic;
> +    out->maxDescriptorSetSampledImages = in->maxDescriptorSetSampledImages;
> +    out->maxDescriptorSetStorageImages = in->maxDescriptorSetStorageImages;
> +    out->maxDescriptorSetInputAttachments = in->maxDescriptorSetInputAttachments;
> +    out->maxVertexInputAttributes = in->maxVertexInputAttributes;
> +    out->maxVertexInputBindings = in->maxVertexInputBindings;
> +    out->maxVertexInputAttributeOffset = in->maxVertexInputAttributeOffset;
> +    out->maxVertexInputBindingStride = in->maxVertexInputBindingStride;
> +    out->maxVertexOutputComponents = in->maxVertexOutputComponents;
> +    out->maxTessellationGenerationLevel = in->maxTessellationGenerationLevel;
> +    out->maxTessellationPatchSize = in->maxTessellationPatchSize;
> +    out->maxTessellationControlPerVertexInputComponents = in->maxTessellationControlPerVertexInputComponents;
> +    out->maxTessellationControlPerVertexOutputComponents = in->maxTessellationControlPerVertexOutputComponents;
> +    out->maxTessellationControlPerPatchOutputComponents = in->maxTessellationControlPerPatchOutputComponents;
> +    out->maxTessellationControlTotalOutputComponents = in->maxTessellationControlTotalOutputComponents;
> +    out->maxTessellationEvaluationInputComponents = in->maxTessellationEvaluationInputComponents;
> +    out->maxTessellationEvaluationOutputComponents = in->maxTessellationEvaluationOutputComponents;
> +    out->maxGeometryShaderInvocations = in->maxGeometryShaderInvocations;
> +    out->maxGeometryInputComponents = in->maxGeometryInputComponents;
> +    out->maxGeometryOutputComponents = in->maxGeometryOutputComponents;
> +    out->maxGeometryOutputVertices = in->maxGeometryOutputVertices;
> +    out->maxGeometryTotalOutputComponents = in->maxGeometryTotalOutputComponents;
> +    out->maxFragmentInputComponents = in->maxFragmentInputComponents;
> +    out->maxFragmentOutputAttachments = in->maxFragmentOutputAttachments;
> +    out->maxFragmentDualSrcAttachments = in->maxFragmentDualSrcAttachments;
> +    out->maxFragmentCombinedOutputResources = in->maxFragmentCombinedOutputResources;
> +    out->maxComputeSharedMemorySize = in->maxComputeSharedMemorySize;
> +    memcpy(out->maxComputeWorkGroupCount, in->maxComputeWorkGroupCount, 3 * sizeof(uint32_t));
> +    out->maxComputeWorkGroupInvocations = in->maxComputeWorkGroupInvocations;
> +    memcpy(out->maxComputeWorkGroupSize, in->maxComputeWorkGroupSize, 3 * sizeof(uint32_t));
> +    out->subPixelPrecisionBits = in->subPixelPrecisionBits;
> +    out->subTexelPrecisionBits = in->subTexelPrecisionBits;
> +    out->mipmapPrecisionBits = in->mipmapPrecisionBits;
> +    out->maxDrawIndexedIndexValue = in->maxDrawIndexedIndexValue;
> +    out->maxDrawIndirectCount = in->maxDrawIndirectCount;
> +    out->maxSamplerLodBias = in->maxSamplerLodBias;
> +    out->maxSamplerAnisotropy = in->maxSamplerAnisotropy;
> +    out->maxViewports = in->maxViewports;
> +    memcpy(out->maxViewportDimensions, in->maxViewportDimensions, 2 * sizeof(uint32_t));
> +    memcpy(out->viewportBoundsRange, in->viewportBoundsRange, 2 * sizeof(float));
> +    out->viewportSubPixelBits = in->viewportSubPixelBits;
> +    out->minMemoryMapAlignment = in->minMemoryMapAlignment;
> +    out->minTexelBufferOffsetAlignment = in->minTexelBufferOffsetAlignment;
> +    out->minUniformBufferOffsetAlignment = in->minUniformBufferOffsetAlignment;
> +    out->minStorageBufferOffsetAlignment = in->minStorageBufferOffsetAlignment;
> +    out->minTexelOffset = in->minTexelOffset;
> +    out->maxTexelOffset = in->maxTexelOffset;
> +    out->minTexelGatherOffset = in->minTexelGatherOffset;
> +    out->maxTexelGatherOffset = in->maxTexelGatherOffset;
> +    out->minInterpolationOffset = in->minInterpolationOffset;
> +    out->maxInterpolationOffset = in->maxInterpolationOffset;
> +    out->subPixelInterpolationOffsetBits = in->subPixelInterpolationOffsetBits;
> +    out->maxFramebufferWidth = in->maxFramebufferWidth;
> +    out->maxFramebufferHeight = in->maxFramebufferHeight;
> +    out->maxFramebufferLayers = in->maxFramebufferLayers;
> +    out->framebufferColorSampleCounts = in->framebufferColorSampleCounts;
> +    out->framebufferDepthSampleCounts = in->framebufferDepthSampleCounts;
> +    out->framebufferStencilSampleCounts = in->framebufferStencilSampleCounts;
> +    out->framebufferNoAttachmentsSampleCounts = in->framebufferNoAttachmentsSampleCounts;
> +    out->maxColorAttachments = in->maxColorAttachments;
> +    out->sampledImageColorSampleCounts = in->sampledImageColorSampleCounts;
> +    out->sampledImageIntegerSampleCounts = in->sampledImageIntegerSampleCounts;
> +    out->sampledImageDepthSampleCounts = in->sampledImageDepthSampleCounts;
> +    out->sampledImageStencilSampleCounts = in->sampledImageStencilSampleCounts;
> +    out->storageImageSampleCounts = in->storageImageSampleCounts;
> +    out->maxSampleMaskWords = in->maxSampleMaskWords;
> +    out->timestampComputeAndGraphics = in->timestampComputeAndGraphics;
> +    out->timestampPeriod = in->timestampPeriod;
> +    out->maxClipDistances = in->maxClipDistances;
> +    out->maxCullDistances = in->maxCullDistances;
> +    out->maxCombinedClipAndCullDistances = in->maxCombinedClipAndCullDistances;
> +    out->discreteQueuePriorities = in->discreteQueuePriorities;
> +    memcpy(out->pointSizeRange, in->pointSizeRange, 2 * sizeof(float));
> +    memcpy(out->lineWidthRange, in->lineWidthRange, 2 * sizeof(float));
> +    out->pointSizeGranularity = in->pointSizeGranularity;
> +    out->lineWidthGranularity = in->lineWidthGranularity;
> +    out->strictLines = in->strictLines;
> +    out->standardSampleLocations = in->standardSampleLocations;
> +    out->optimalBufferCopyOffsetAlignment = in->optimalBufferCopyOffsetAlignment;
> +    out->optimalBufferCopyRowPitchAlignment = in->optimalBufferCopyRowPitchAlignment;
> +    out->nonCoherentAtomSize = in->nonCoherentAtomSize;
> +}
> +
> +static inline void convert_VkPhysicalDeviceProperties_host_to_win(const VkPhysicalDeviceProperties_host *in, VkPhysicalDeviceProperties *out)
> +{
> +    if (!in) return;
> +
> +    out->apiVersion = in->apiVersion;
> +    out->driverVersion = in->driverVersion;
> +    out->vendorID = in->vendorID;
> +    out->deviceID = in->deviceID;
> +    out->deviceType = in->deviceType;
> +    memcpy(out->deviceName, in->deviceName, VK_MAX_PHYSICAL_DEVICE_NAME_SIZE * sizeof(char));
> +    memcpy(out->pipelineCacheUUID, in->pipelineCacheUUID, VK_UUID_SIZE * sizeof(uint8_t));
> +    convert_VkPhysicalDeviceLimits_host_to_win(&in->limits, &out->limits);
> +    out->sparseProperties = in->sparseProperties;
> +}
> +
> +#endif /* USE_STRUCT_CONVERSION */
> +
>  static VkResult WINAPI wine_vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice)
>  {
>      FIXME("stub: %p, %p, %p, %p\n", physicalDevice, pCreateInfo, pAllocator, pDevice);
> @@ -18,44 +183,79 @@ static VkResult WINAPI wine_vkCreateDevice(VkPhysicalDevice physicalDevice, cons
>
>  static VkResult WINAPI wine_vkEnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t *pPropertyCount, VkLayerProperties *pProperties)
>  {
> -    FIXME("stub: %p, %p, %p\n", physicalDevice, pPropertyCount, pProperties);
> -    return VK_ERROR_OUT_OF_HOST_MEMORY;
> +    TRACE("%p, %p, %p\n", physicalDevice, pPropertyCount, pProperties);
> +    return physicalDevice->instance->funcs.p_vkEnumerateDeviceLayerProperties(physicalDevice->phys_dev, pPropertyCount, pProperties);
>  }
>
>  static void WINAPI wine_vkGetPhysicalDeviceFeatures(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures *pFeatures)
>  {
> -    FIXME("stub: %p, %p\n", physicalDevice, pFeatures);
> +    TRACE("%p, %p\n", physicalDevice, pFeatures);
> +    physicalDevice->instance->funcs.p_vkGetPhysicalDeviceFeatures(physicalDevice->phys_dev, pFeatures);
>  }
>
>  static void WINAPI wine_vkGetPhysicalDeviceFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties *pFormatProperties)
>  {
> -    FIXME("stub: %p, %d, %p\n", physicalDevice, format, pFormatProperties);
> +    TRACE("%p, %d, %p\n", physicalDevice, format, pFormatProperties);
> +    physicalDevice->instance->funcs.p_vkGetPhysicalDeviceFormatProperties(physicalDevice->phys_dev, format, pFormatProperties);
>  }
>
>  static VkResult WINAPI wine_vkGetPhysicalDeviceImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties *pImageFormatProperties)
>  {
> -    FIXME("stub: %p, %d, %d, %d, %#x, %#x, %p\n", physicalDevice, format, type, tiling, usage, flags, pImageFormatProperties);
> -    return VK_ERROR_OUT_OF_HOST_MEMORY;
> +#if defined(USE_STRUCT_CONVERSION)
> +    VkResult result;
> +    VkImageFormatProperties_host pImageFormatProperties_host;
> +    TRACE("%p, %d, %d, %d, %#x, %#x, %p\n", physicalDevice, format, type, tiling, usage, flags, pImageFormatProperties);
> +
> +    result = physicalDevice->instance->funcs.p_vkGetPhysicalDeviceImageFormatProperties(physicalDevice->phys_dev, format, type, tiling, usage, flags, &pImageFormatProperties_host);
> +
> +    convert_VkImageFormatProperties_host_to_win(&pImageFormatProperties_host, pImageFormatProperties);
> +    return result;
> +#else
> +    TRACE("%p, %d, %d, %d, %#x, %#x, %p\n", physicalDevice, format, type, tiling, usage, flags, pImageFormatProperties);
> +    return physicalDevice->instance->funcs.p_vkGetPhysicalDeviceImageFormatProperties(physicalDevice->phys_dev, format, type, tiling, usage, flags, pImageFormatProperties);
> +#endif
>  }
>
>  static void WINAPI wine_vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties *pMemoryProperties)
>  {
> -    FIXME("stub: %p, %p\n", physicalDevice, pMemoryProperties);
> +#if defined(USE_STRUCT_CONVERSION)
> +    VkPhysicalDeviceMemoryProperties_host pMemoryProperties_host;
> +    TRACE("%p, %p\n", physicalDevice, pMemoryProperties);
> +
> +    physicalDevice->instance->funcs.p_vkGetPhysicalDeviceMemoryProperties(physicalDevice->phys_dev, &pMemoryProperties_host);
> +
> +    convert_VkPhysicalDeviceMemoryProperties_host_to_win(&pMemoryProperties_host, pMemoryProperties);
> +#else
> +    TRACE("%p, %p\n", physicalDevice, pMemoryProperties);
> +    physicalDevice->instance->funcs.p_vkGetPhysicalDeviceMemoryProperties(physicalDevice->phys_dev, pMemoryProperties);
> +#endif
>  }
>
>  static void WINAPI wine_vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties *pProperties)
>  {
> -    FIXME("stub: %p, %p\n", physicalDevice, pProperties);
> +#if defined(USE_STRUCT_CONVERSION)
> +    VkPhysicalDeviceProperties_host pProperties_host;
> +    TRACE("%p, %p\n", physicalDevice, pProperties);
> +
> +    physicalDevice->instance->funcs.p_vkGetPhysicalDeviceProperties(physicalDevice->phys_dev, &pProperties_host);
> +
> +    convert_VkPhysicalDeviceProperties_host_to_win(&pProperties_host, pProperties);
> +#else
> +    TRACE("%p, %p\n", physicalDevice, pProperties);
> +    physicalDevice->instance->funcs.p_vkGetPhysicalDeviceProperties(physicalDevice->phys_dev, pProperties);
> +#endif
>  }
>
>  static void WINAPI wine_vkGetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties *pQueueFamilyProperties)
>  {
> -    FIXME("stub: %p, %p, %p\n", physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
> +    TRACE("%p, %p, %p\n", physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
> +    physicalDevice->instance->funcs.p_vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice->phys_dev, pQueueFamilyPropertyCount, pQueueFamilyProperties);
>  }
>
>  static void WINAPI wine_vkGetPhysicalDeviceSparseImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t *pPropertyCount, VkSparseImageFormatProperties *pProperties)
>  {
> -    FIXME("stub: %p, %d, %d, %d, %#x, %d, %p, %p\n", physicalDevice, format, type, samples, usage, tiling, pPropertyCount, pProperties);
> +    TRACE("%p, %d, %d, %d, %#x, %d, %p, %p\n", physicalDevice, format, type, samples, usage, tiling, pPropertyCount, pProperties);
> +    physicalDevice->instance->funcs.p_vkGetPhysicalDeviceSparseImageFormatProperties(physicalDevice->phys_dev, format, type, samples, usage, tiling, pPropertyCount, pProperties);
>  }
>
>  static const struct vulkan_func vk_instance_dispatch_table[] =
> diff --git a/dlls/winevulkan/vulkan_thunks.h b/dlls/winevulkan/vulkan_thunks.h
> index b8d2bbdb0f..b673ba502f 100644
> --- a/dlls/winevulkan/vulkan_thunks.h
> +++ b/dlls/winevulkan/vulkan_thunks.h
> @@ -3,6 +3,11 @@
>  #ifndef __WINE_VULKAN_THUNKS_H
>  #define __WINE_VULKAN_THUNKS_H
>
> +/* Perform vulkan struct conversion on 32-bit platforms. */
> +#if (defined(__i386__) || defined(__arm__))
> +    #define USE_STRUCT_CONVERSION
> +#endif

Are you sure that the above code does the right thing on ARM? Is the
structure conversion required on ARM? AFAIK ARM requires 8-byte
alignment for 64-bit variables.

> +
>  /* For use by vk_icdGetInstanceProcAddr / vkGetInstanceProcAddr */
>  void *wine_vk_get_instance_proc_addr(const char *name) DECLSPEC_HIDDEN;
>
> @@ -11,6 +16,153 @@ void WINAPI wine_vkDestroyInstance(VkInstance instance, const VkAllocationCallba
>  VkResult WINAPI wine_vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties) DECLSPEC_HIDDEN;
>  VkResult WINAPI wine_vkEnumeratePhysicalDevices(VkInstance instance, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices) DECLSPEC_HIDDEN;
>
> +typedef struct VkImageFormatProperties_host
> +{
> +    VkExtent3D maxExtent;
> +    uint32_t maxMipLevels;
> +    uint32_t maxArrayLayers;
> +    VkSampleCountFlags sampleCounts;
> +    VkDeviceSize maxResourceSize;
> +} VkImageFormatProperties_host;
> +
> +typedef struct VkMemoryHeap_host
> +{
> +    VkDeviceSize size;
> +    VkMemoryHeapFlags flags;
> +} VkMemoryHeap_host;
> +
> +typedef struct VkPhysicalDeviceMemoryProperties_host
> +{
> +    uint32_t memoryTypeCount;
> +    VkMemoryType memoryTypes[VK_MAX_MEMORY_TYPES];
> +    uint32_t memoryHeapCount;
> +    VkMemoryHeap_host memoryHeaps[VK_MAX_MEMORY_HEAPS];
> +} VkPhysicalDeviceMemoryProperties_host;
> +
> +typedef struct VkPhysicalDeviceLimits_host
> +{
> +    uint32_t maxImageDimension1D;
> +    uint32_t maxImageDimension2D;
> +    uint32_t maxImageDimension3D;
> +    uint32_t maxImageDimensionCube;
> +    uint32_t maxImageArrayLayers;
> +    uint32_t maxTexelBufferElements;
> +    uint32_t maxUniformBufferRange;
> +    uint32_t maxStorageBufferRange;
> +    uint32_t maxPushConstantsSize;
> +    uint32_t maxMemoryAllocationCount;
> +    uint32_t maxSamplerAllocationCount;
> +    VkDeviceSize bufferImageGranularity;
> +    VkDeviceSize sparseAddressSpaceSize;
> +    uint32_t maxBoundDescriptorSets;
> +    uint32_t maxPerStageDescriptorSamplers;
> +    uint32_t maxPerStageDescriptorUniformBuffers;
> +    uint32_t maxPerStageDescriptorStorageBuffers;
> +    uint32_t maxPerStageDescriptorSampledImages;
> +    uint32_t maxPerStageDescriptorStorageImages;
> +    uint32_t maxPerStageDescriptorInputAttachments;
> +    uint32_t maxPerStageResources;
> +    uint32_t maxDescriptorSetSamplers;
> +    uint32_t maxDescriptorSetUniformBuffers;
> +    uint32_t maxDescriptorSetUniformBuffersDynamic;
> +    uint32_t maxDescriptorSetStorageBuffers;
> +    uint32_t maxDescriptorSetStorageBuffersDynamic;
> +    uint32_t maxDescriptorSetSampledImages;
> +    uint32_t maxDescriptorSetStorageImages;
> +    uint32_t maxDescriptorSetInputAttachments;
> +    uint32_t maxVertexInputAttributes;
> +    uint32_t maxVertexInputBindings;
> +    uint32_t maxVertexInputAttributeOffset;
> +    uint32_t maxVertexInputBindingStride;
> +    uint32_t maxVertexOutputComponents;
> +    uint32_t maxTessellationGenerationLevel;
> +    uint32_t maxTessellationPatchSize;
> +    uint32_t maxTessellationControlPerVertexInputComponents;
> +    uint32_t maxTessellationControlPerVertexOutputComponents;
> +    uint32_t maxTessellationControlPerPatchOutputComponents;
> +    uint32_t maxTessellationControlTotalOutputComponents;
> +    uint32_t maxTessellationEvaluationInputComponents;
> +    uint32_t maxTessellationEvaluationOutputComponents;
> +    uint32_t maxGeometryShaderInvocations;
> +    uint32_t maxGeometryInputComponents;
> +    uint32_t maxGeometryOutputComponents;
> +    uint32_t maxGeometryOutputVertices;
> +    uint32_t maxGeometryTotalOutputComponents;
> +    uint32_t maxFragmentInputComponents;
> +    uint32_t maxFragmentOutputAttachments;
> +    uint32_t maxFragmentDualSrcAttachments;
> +    uint32_t maxFragmentCombinedOutputResources;
> +    uint32_t maxComputeSharedMemorySize;
> +    uint32_t maxComputeWorkGroupCount[3];
> +    uint32_t maxComputeWorkGroupInvocations;
> +    uint32_t maxComputeWorkGroupSize[3];
> +    uint32_t subPixelPrecisionBits;
> +    uint32_t subTexelPrecisionBits;
> +    uint32_t mipmapPrecisionBits;
> +    uint32_t maxDrawIndexedIndexValue;
> +    uint32_t maxDrawIndirectCount;
> +    float maxSamplerLodBias;
> +    float maxSamplerAnisotropy;
> +    uint32_t maxViewports;
> +    uint32_t maxViewportDimensions[2];
> +    float viewportBoundsRange[2];
> +    uint32_t viewportSubPixelBits;
> +    size_t minMemoryMapAlignment;
> +    VkDeviceSize minTexelBufferOffsetAlignment;
> +    VkDeviceSize minUniformBufferOffsetAlignment;
> +    VkDeviceSize minStorageBufferOffsetAlignment;
> +    int32_t minTexelOffset;
> +    uint32_t maxTexelOffset;
> +    int32_t minTexelGatherOffset;
> +    uint32_t maxTexelGatherOffset;
> +    float minInterpolationOffset;
> +    float maxInterpolationOffset;
> +    uint32_t subPixelInterpolationOffsetBits;
> +    uint32_t maxFramebufferWidth;
> +    uint32_t maxFramebufferHeight;
> +    uint32_t maxFramebufferLayers;
> +    VkSampleCountFlags framebufferColorSampleCounts;
> +    VkSampleCountFlags framebufferDepthSampleCounts;
> +    VkSampleCountFlags framebufferStencilSampleCounts;
> +    VkSampleCountFlags framebufferNoAttachmentsSampleCounts;
> +    uint32_t maxColorAttachments;
> +    VkSampleCountFlags sampledImageColorSampleCounts;
> +    VkSampleCountFlags sampledImageIntegerSampleCounts;
> +    VkSampleCountFlags sampledImageDepthSampleCounts;
> +    VkSampleCountFlags sampledImageStencilSampleCounts;
> +    VkSampleCountFlags storageImageSampleCounts;
> +    uint32_t maxSampleMaskWords;
> +    VkBool32 timestampComputeAndGraphics;
> +    float timestampPeriod;
> +    uint32_t maxClipDistances;
> +    uint32_t maxCullDistances;
> +    uint32_t maxCombinedClipAndCullDistances;
> +    uint32_t discreteQueuePriorities;
> +    float pointSizeRange[2];
> +    float lineWidthRange[2];
> +    float pointSizeGranularity;
> +    float lineWidthGranularity;
> +    VkBool32 strictLines;
> +    VkBool32 standardSampleLocations;
> +    VkDeviceSize optimalBufferCopyOffsetAlignment;
> +    VkDeviceSize optimalBufferCopyRowPitchAlignment;
> +    VkDeviceSize nonCoherentAtomSize;
> +} VkPhysicalDeviceLimits_host;
> +
> +typedef struct VkPhysicalDeviceProperties_host
> +{
> +    uint32_t apiVersion;
> +    uint32_t driverVersion;
> +    uint32_t vendorID;
> +    uint32_t deviceID;
> +    VkPhysicalDeviceType deviceType;
> +    char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE];
> +    uint8_t pipelineCacheUUID[VK_UUID_SIZE];
> +    VkPhysicalDeviceLimits_host limits;
> +    VkPhysicalDeviceSparseProperties sparseProperties;
> +} VkPhysicalDeviceProperties_host;
> +
> +
>  /* For use by vkInstance and children */
>  struct vulkan_instance_funcs
>  {
> @@ -20,9 +172,21 @@ struct vulkan_instance_funcs
>      VkResult (*p_vkEnumeratePhysicalDevices)(VkInstance, uint32_t *, VkPhysicalDevice *);
>      void (*p_vkGetPhysicalDeviceFeatures)(VkPhysicalDevice, VkPhysicalDeviceFeatures *);
>      void (*p_vkGetPhysicalDeviceFormatProperties)(VkPhysicalDevice, VkFormat, VkFormatProperties *);
> +#if defined(USE_STRUCT_CONVERSION)
> +    VkResult (*p_vkGetPhysicalDeviceImageFormatProperties)(VkPhysicalDevice, VkFormat, VkImageType, VkImageTiling, VkImageUsageFlags, VkImageCreateFlags, VkImageFormatProperties_host *);
> +#else
>      VkResult (*p_vkGetPhysicalDeviceImageFormatProperties)(VkPhysicalDevice, VkFormat, VkImageType, VkImageTiling, VkImageUsageFlags, VkImageCreateFlags, VkImageFormatProperties *);
> +#endif
> +#if defined(USE_STRUCT_CONVERSION)
> +    void (*p_vkGetPhysicalDeviceMemoryProperties)(VkPhysicalDevice, VkPhysicalDeviceMemoryProperties_host *);
> +#else
>      void (*p_vkGetPhysicalDeviceMemoryProperties)(VkPhysicalDevice, VkPhysicalDeviceMemoryProperties *);
> +#endif
> +#if defined(USE_STRUCT_CONVERSION)
> +    void (*p_vkGetPhysicalDeviceProperties)(VkPhysicalDevice, VkPhysicalDeviceProperties_host *);
> +#else
>      void (*p_vkGetPhysicalDeviceProperties)(VkPhysicalDevice, VkPhysicalDeviceProperties *);
> +#endif
>      void (*p_vkGetPhysicalDeviceQueueFamilyProperties)(VkPhysicalDevice, uint32_t *, VkQueueFamilyProperties *);
>      void (*p_vkGetPhysicalDeviceSparseImageFormatProperties)(VkPhysicalDevice, VkFormat, VkImageType, VkSampleCountFlagBits, VkImageUsageFlags, VkImageTiling, uint32_t *, VkSparseImageFormatProperties *);
>  };
> --
> 2.14.3
>
>
>



More information about the wine-devel mailing list