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

Roderick Colenbrander thunderbird2k at gmail.com
Fri Mar 9 10:36:51 CST 2018


On Fri, Mar 9, 2018 at 8:08 AM, Józef Kucia <joseph.kucia at gmail.com> wrote:
> 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.
>

Let me take it out, it is likely indeed not be needed due to ARM
having much more strict alignment needs.

Thanks for spotting the other little issues.



More information about the wine-devel mailing list