On Fri, Mar 9, 2018 at 8:08 AM, Józef Kucia joseph.kucia@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@gmail.com wrote:
Signed-off-by: Roderick Colenbrander thunderbird2k@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 win32and the host platform. This function returns a list of conversionsrequired."""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 haveTypo: 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 Truereturn Falsedef 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 Trueif self.params[0].type != "VkPhysicalDevice":return Truereturn Falsedef 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():continueif 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():continuebody += 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():continuebody += 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():continuebody += 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 Noneif self.parent is None:# Should only happen for VkInstancereturn "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 Noneif 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_lenif direction == Direction.OUTPUT:# Needed by VkMemoryHeap.memoryHeapsreturn "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 Noneconversions = []# 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.INPUTif 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 Falsestruct = self.type_info["data"]return struct.needs_conversion()- def needs_free(self):
if not self.needs_conversion():return Falseif 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 = Noneself.input_conv = Noneself.output_conv = Noneif 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/610if not self.is_pointer():self._direction = Direction.INPUTelif self.is_const() and self.is_pointer():self._direction = Direction.INPUTelif self.is_struct():if not self.struct.returnedonly:self._direction = Direction.INPUTreturn# 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_OUTPUTreturnself._direction = Direction.OUTPUTelse:# 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 Nonereturn "{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 win32and the host platform. This function returns a list of conversionsrequired."""if not self.is_struct():return Noneif not self.needs_conversion():return Noneconversions = []# 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():continueif not m.needs_conversion():continueconversions.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 Truereturn 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 andrenaming 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.nameclass 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 alignmentor if they include other structures which need alignment."""if self.needs_alignment():return Truefor m in self.members:if m.needs_conversion():return Truereturn False- def needs_free(self):
""" Check if any struct member needs some memory freeing."""for m in self.members:if m.needs_free():return Truecontinuereturn 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 = arrayself.direction = directionself.dyn_array = dyn_arrayself.struct = structself.type = struct.nameself._set_name()- def __eq__(self, other):
if self.name != other.name:return Falsereturn 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.typeelse: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 listbody += ", ".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.typeelse: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.OUTPUTif 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_arrayself.struct = structself.type = struct.nameif 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 Truereturn 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. VkCommandBufferBeginInforeturn 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():continueif not func.needs_conversion():continueconversions = 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.