From a2db6ef1521978d001559c651a9c8fadc56916c0 Mon Sep 17 00:00:00 2001 From: Shahbaz Youssefi Date: Mon, 30 Sep 2024 10:21:42 -0400 Subject: [PATCH] profiles: Support VK_EXT_host_image_copy --- .../local_gpu.json | 11 +- .../remote_gpu.json | 11 +- scripts/gen_profiles_file.py | 20 ++- scripts/gen_profiles_layer.py | 4 +- scripts/gen_profiles_solution.py | 130 ++++++++++++++---- scripts/gen_profiles_tests.py | 2 +- 6 files changed, 136 insertions(+), 42 deletions(-) diff --git a/profiles/test/data/VP_LUNARG_test_host_image_copy/local_gpu.json b/profiles/test/data/VP_LUNARG_test_host_image_copy/local_gpu.json index d1221332..d05a227c 100644 --- a/profiles/test/data/VP_LUNARG_test_host_image_copy/local_gpu.json +++ b/profiles/test/data/VP_LUNARG_test_host_image_copy/local_gpu.json @@ -47,6 +47,7 @@ "VK_EXT_global_priority": 2, "VK_EXT_global_priority_query": 1, "VK_EXT_graphics_pipeline_library": 1, + "VK_EXT_host_image_copy": 1, "VK_EXT_host_query_reset": 1, "VK_EXT_image_2d_view_of_3d": 1, "VK_EXT_image_drm_format_modifier": 2, @@ -1407,13 +1408,15 @@ "maxBufferSize": 4294967292 }, "VkPhysicalDeviceHostImageCopyPropertiesEXT": { - "copySrcLayoutCount": 0, "pCopySrcLayouts": [ - + "VK_IMAGE_LAYOUT_GENERAL", + "VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL", + "VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL" ], - "copyDstLayoutCount": 0, "pCopyDstLayouts": [ - + "VK_IMAGE_LAYOUT_GENERAL", + "VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL", + "VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL" ], "optimalTilingLayoutUUID": [ 0, diff --git a/profiles/test/data/VP_LUNARG_test_host_image_copy/remote_gpu.json b/profiles/test/data/VP_LUNARG_test_host_image_copy/remote_gpu.json index ed005abd..eeae11b9 100644 --- a/profiles/test/data/VP_LUNARG_test_host_image_copy/remote_gpu.json +++ b/profiles/test/data/VP_LUNARG_test_host_image_copy/remote_gpu.json @@ -25,6 +25,7 @@ "VK_EXT_global_priority_query": 1, "VK_EXT_graphics_pipeline_library": 1, "VK_EXT_hdr_metadata": 2, + "VK_EXT_host_image_copy": 1, "VK_EXT_host_query_reset": 1, "VK_EXT_image_2d_view_of_3d": 1, "VK_EXT_image_drm_format_modifier": 2, @@ -964,13 +965,15 @@ "maxBufferSize": 4294967296 }, "VkPhysicalDeviceHostImageCopyPropertiesEXT": { - "copySrcLayoutCount": 0, "pCopySrcLayouts": [ - + "VK_IMAGE_LAYOUT_GENERAL", + "VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL", + "VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL" ], - "copyDstLayoutCount": 0, "pCopyDstLayouts": [ - + "VK_IMAGE_LAYOUT_GENERAL", + "VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL", + "VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL" ], "optimalTilingLayoutUUID": [ 0, diff --git a/scripts/gen_profiles_file.py b/scripts/gen_profiles_file.py index ed9caeb7..d0ada35d 100644 --- a/scripts/gen_profiles_file.py +++ b/scripts/gen_profiles_file.py @@ -575,7 +575,7 @@ def add_members(self, merged, entry, property = None): continue xmlmember = self.registry.structs[property].members[member] - if xmlmember.limittype == 'exact' or xmlmember.limittype == 'noauto': + if (xmlmember.limittype == 'exact' or xmlmember.limittype == 'noauto') and not xmlmember.isDynamicallySizedArrayWithCap(): del merged[member] #elif 'mul' in xmlmember.limittype and xmlmember.type == 'float': # del merged[member] @@ -585,7 +585,7 @@ def add_members(self, merged, entry, property = None): merged[member] = entry[member] elif not member in merged: xmlmember = self.registry.structs[property].members[member] - if xmlmember.limittype == 'exact' or xmlmember.limittype == 'noauto': + if (xmlmember.limittype == 'exact' or xmlmember.limittype == 'noauto') and not xmlmember.isDynamicallySizedArrayWithCap(): continue elif self.mode == 'union' or self.first is True: if xmlmember.type == 'uint64_t' or xmlmember.type == 'VkDeviceSize': @@ -593,10 +593,6 @@ def add_members(self, merged, entry, property = None): else: merged[member] = entry[member] else: - # VK_EXT_host_image_copy is unsupported - if property == 'VkPhysicalDeviceHostImageCopyPropertiesEXT': - continue - # Merge properties xmlmember = self.registry.structs[property].members[member] if xmlmember.limittype == 'struct': @@ -615,7 +611,7 @@ def add_members(self, merged, entry, property = None): self.merge_members(merged, member, entry, xmlmember) def merge_members(self, merged, member, entry, xmlmember): - if xmlmember.limittype == 'exact' or xmlmember.limittype == 'noauto': + if (xmlmember.limittype == 'exact' or xmlmember.limittype == 'noauto') and not xmlmember.isDynamicallySizedArrayWithCap(): del merged[member] elif self.mode == 'union': #if xmlmember.limittype == 'exact': @@ -685,6 +681,11 @@ def merge_members(self, merged, member, entry, xmlmember): merged[member][0] = entry[member][0] if entry[member][1] > merged[member][1]: merged[member][1] = entry[member][1] + elif xmlmember.isDynamicallySizedArrayWithCap(): + entry_set = set(merged[member]) + merged_set = set(entry[member]) + union_set = merged_set.union(entry_set) + merged[member] = list(union_set) else: print("ERROR: Unknown limitype: " + xmlmember.limittype + " for " + member) elif self.mode == 'intersection': @@ -792,6 +793,11 @@ def merge_members(self, merged, member, entry, xmlmember): merged[member][1] = entry[member][1] #if member[1] < member[0]: # merged.pop(member, None) + elif xmlmember.isDynamicallySizedArrayWithCap(): + entry_set = set(merged[member]) + merged_set = set(entry[member]) + intersection_set = merged_set.intersection(entry_set) + merged[member] = list(intersection_set) else: print("ERROR: Unknown limitype: " + xmlmember.limittype + " for " + member) else: diff --git a/scripts/gen_profiles_layer.py b/scripts/gen_profiles_layer.py index bab71af8..be6b2b86 100644 --- a/scripts/gen_profiles_layer.py +++ b/scripts/gen_profiles_layer.py @@ -2946,9 +2946,7 @@ class VulkanProfilesLayerGenerator(): emulated_extensions = ['VK_KHR_portability_subset'] additional_features = ['VkPhysicalDeviceFeatures', 'VkPhysicalDevicePortabilitySubsetFeaturesKHR'] additional_properties = ['VkPhysicalDeviceProperties', 'VkPhysicalDeviceLimits', 'VkPhysicalDeviceSparseProperties', 'VkPhysicalDeviceToolProperties', 'VkPhysicalDevicePortabilitySubsetPropertiesKHR'] - # VkPhysicalDeviceHostImageCopyFeaturesEXT is not ignored to allow the people using the MockICD to still have the feature enabled, - # but use the properties in the MockICD until HostImageCopyPropertiesEXT is fixed. - ignored_structs = ['VkPhysicalDeviceHostImageCopyPropertiesEXT', 'VkPhysicalDeviceLayeredApiPropertiesListKHR'] + ignored_structs = ['VkPhysicalDeviceLayeredApiPropertiesListKHR'] def generate(self, path, registry): self.registry = registry diff --git a/scripts/gen_profiles_solution.py b/scripts/gen_profiles_solution.py index 5eefcba0..fa899c90 100644 --- a/scripts/gen_profiles_solution.py +++ b/scripts/gen_profiles_solution.py @@ -533,7 +533,11 @@ def stripNonmatchingAPIs(tree, apiName, actuallyDelete = True): return !(value & (value - static_cast(1))); } -using PFN_vpStructFiller = void(*)(VkBaseOutStructure* p); +using PFN_vpStructFiller = void(*)( +#ifdef VP_USE_OBJECT + VpCapabilities capabilities, +#endif//VP_USE_OBJECT + VkBaseOutStructure* p); using PFN_vpStructComparator = bool(*)(VkBaseOutStructure* p); using PFN_vpStructChainerCb = void(*)(VkBaseOutStructure* p, void* pUser); using PFN_vpStructChainer = void(*)(VkBaseOutStructure* p, void* pUser, PFN_vpStructChainerCb pfnCb); @@ -621,6 +625,17 @@ def stripNonmatchingAPIs(tree, apiName, actuallyDelete = True): VPAPI_ATTR bool vpCheckFlags(const T& actual, const uint64_t expected) { return (actual & expected) == expected; } + +template +VPAPI_ATTR bool vpCheckList(const T* data, uint32_t count, const T expected) { + for (uint32_t i = 0; i < count; ++i) { + if (data[i] == expected) + { + return true; + } + } + return false; +} ''' PRIVATE_IMPL_BODY = ''' @@ -965,6 +980,12 @@ def stripNonmatchingAPIs(tree, apiName, actuallyDelete = True): bool singleton = false; uint32_t apiVersion = VK_API_VERSION_1_0; + // Space for multi-pass queries: + // + // - From VkPhysicalDeviceHostImageCopyPropertiesEXT + std::vector pCopySrcLayouts; + std::vector pCopyDstLayouts; + static VpCapabilities_T& Get() { static VpCapabilities_T instance; VpCapabilitiesCreateInfo createInfo{}; @@ -2334,6 +2355,10 @@ def __init__(self, name, type, limittype, isArray = False): self.arraySizeMember = None self.nullTerminated = False self.arraySize = None + self.arraySizeCap = None + + def isDynamicallySizedArrayWithCap(self): + return self.isArray and self.arraySizeCap is not None class VulkanStruct(): @@ -2517,7 +2542,7 @@ def __init__(self, xml, upperCaseName): # Dynamic arrays are ill-formed, but some of them still have a maximum size that can be used -struct_with_valid_dynamic_array = ["VkQueueFamilyGlobalPriorityPropertiesKHR"] +struct_with_valid_dynamic_array = ["VkQueueFamilyGlobalPriorityPropertiesKHR", "VkPhysicalDeviceHostImageCopyPropertiesEXT"] class VulkanRegistry(): def __init__(self, registryFile, api = 'vulkan'): @@ -2674,6 +2699,11 @@ def parseStructInfo(self, xml): structDef.members[name].isArray = True structDef.members[name].arraySizeMember = len + # Some arrays have a natural maximum size even if they are dynamic. For example, a list + # of VkImageLayouts, because that enum itself is limited. + if structDef.members[name].type == 'VkImageLayout': + structDef.members[name].arraySizeCap = 64; + # If any of the members is a dynamic array then we should remove the corresponding count member for member in list(structDef.members.values()): if member.isArray and member.arraySizeMember != None and struct.get('name') not in struct_with_valid_dynamic_array: @@ -3779,17 +3809,34 @@ def gen_structFill(self, fmt, structDef, var, values): continue if structDef.members[member].isArray: if not isinstance(self.registry.evalArraySize(structDef.members[member].arraySize), int): - Log.f("Unsupported array member '{0}' in structure '{1}'".format(member, structDef.name) + - "(currently only 1D non-dynamic arrays are supported in this context)") - # If it's an array we have to generate per-element assignment code - for i, v in enumerate(value): - if type(v) == float: - if structDef.members[member].type == 'double': - gen += fmt.format('{0}{1}[{2}] = {3}'.format(var, member, i, v)) - else: - gen += fmt.format('{0}{1}[{2}] = {3}f'.format(var, member, i, v)) + if structDef.members[member].arraySizeCap: + # If it is a dynamically sized array (with a cap), point it to the pre-allocated array + # created for this purpose. + array_alloc = '#ifdef VP_USE_OBJECT\n' + array_alloc += fmt.format('capabilities->{0}.resize({1})'.format(member, + structDef.members[member].arraySizeCap)) + array_alloc += '#endif//VP_USE_OBJECT\n' + gen = array_alloc + gen + + # Note: support for these arrays is only present with VP_USE_OBJECT. + gen += '#ifdef VP_USE_OBJECT\n' + gen += fmt.format('{0}{1} = {2}'.format(var, structDef.members[member].arraySizeMember, + structDef.members[member].arraySizeCap)) + gen += fmt.format('{0}{1} = capabilities->{2}.data()'.format(var, member, member)) + gen += '#endif//VP_USE_OBJECT\n' else: - gen += fmt.format('{0}{1}[{2}] = {3}'.format(var, member, i, v)) + Log.f("Unsupported array member '{0}' in structure '{1}'".format(member, structDef.name) + + "(currently only 1D non-dynamic arrays are supported in this context)") + else: + # If it's an array we have to generate per-element assignment code + for i, v in enumerate(value): + if type(v) == float: + if structDef.members[member].type == 'double': + gen += fmt.format('{0}{1}[{2}] = {3}'.format(var, member, i, v)) + else: + gen += fmt.format('{0}{1}[{2}] = {3}f'.format(var, member, i, v)) + else: + gen += fmt.format('{0}{1}[{2}] = {3}'.format(var, member, i, v)) else: # For enums and struct initialization, most of the code can be shared isEnum = isinstance(value[0], str) @@ -3897,14 +3944,23 @@ def gen_structCompare(self, fmt, structDef, var, values, parentLimittype = None) continue if structDef.members[member].isArray: if not isinstance(self.registry.evalArraySize(structDef.members[member].arraySize), int): - Log.f("Unsupported array member '{0}' in structure '{1}'".format(member, structDef.name) + - "(currently only 1D non-dynamic arrays are supported in this context)") - # If it's an array we have to generate per-element comparison code - for i in range(len(value)): - if limittype == 'range': - gen += fmt.format(comparePredFmt[i].format('{0}{1}[{2}]'.format(var, member, i), value[i])) + if structDef.members[member].arraySizeCap: + # If it is a dynamically sized array (with a cap), check that the required elements are + # present anywhere in the returned list. + for i in range(len(value)): + gen += fmt.format('vpCheckList({0}{1}, {0}{2}, {3})'.format(var, member, + structDef.members[member].arraySizeMember, + value[i])) else: - gen += fmt.format(comparePredFmt.format('{0}{1}[{2}]'.format(var, member, i), value[i])) + Log.f("Unsupported array member '{0}' in structure '{1}'".format(member, structDef.name) + + "(currently only 1D non-dynamic arrays are supported in this context)") + else: + # If it's an array we have to generate per-element comparison code + for i in range(len(value)): + if limittype == 'range': + gen += fmt.format(comparePredFmt[i].format('{0}{1}[{2}]'.format(var, member, i), value[i])) + else: + gen += fmt.format(comparePredFmt.format('{0}{1}[{2}]'.format(var, member, i), value[i])) else: # Enum flags and basic structs can be compared directly isEnum = isinstance(value[0], str) @@ -4019,7 +4075,14 @@ def gen_structDesc(self, capabilities, debugMessages): gen += ('\n' 'static const VpFeatureDesc featureDesc = {\n' - ' [](VkBaseOutStructure* p) { (void)p;\n') + ' [](\n' + '#ifdef VP_USE_OBJECT\n' + ' VpCapabilities capabilities,\n' + '#endif//VP_USE_OBJECT\n' + ' VkBaseOutStructure* p) { (void)p;\n' + '#ifdef VP_USE_OBJECT\n' + ' (void)capabilities;\n' + '#endif//VP_USE_OBJECT\n') gen += self.gen_structFunc(self.structs.feature, capabilities.features, self.gen_structFill, fillFmt) gen += (' },\n' ' [](VkBaseOutStructure* p) -> bool { (void)p;\n' @@ -4037,7 +4100,14 @@ def gen_structDesc(self, capabilities, debugMessages): gen += ('\n' 'static const VpPropertyDesc propertyDesc = {\n' - ' [](VkBaseOutStructure* p) { (void)p;\n') + ' [](\n' + '#ifdef VP_USE_OBJECT\n' + ' VpCapabilities capabilities,\n' + '#endif//VP_USE_OBJECT\n' + ' VkBaseOutStructure* p) { (void)p;\n' + '#ifdef VP_USE_OBJECT\n' + ' (void)capabilities;\n' + '#endif//VP_USE_OBJECT\n') gen += self.gen_structFunc(self.structs.property, capabilities.properties, self.gen_structFill, fillFmt) gen += (' },\n' ' [](VkBaseOutStructure* p) -> bool { (void)p;\n' @@ -4053,7 +4123,14 @@ def gen_structDesc(self, capabilities, debugMessages): 'static const VpQueueFamilyDesc queueFamilyDesc[] = {\n') for queueFamilyCaps in capabilities.queueFamiliesProperties: gen += (' {\n' - ' [](VkBaseOutStructure* p) { (void)p;\n') + ' [](\n' + '#ifdef VP_USE_OBJECT\n' + ' VpCapabilities capabilities,\n' + '#endif//VP_USE_OBJECT\n' + ' VkBaseOutStructure* p) { (void)p;\n' + '#ifdef VP_USE_OBJECT\n' + ' (void)capabilities;\n' + '#endif//VP_USE_OBJECT\n') gen += self.gen_structFunc(self.structs.queueFamily, queueFamilyCaps, self.gen_structFill, fillFmt) gen += (' },\n' ' [](VkBaseOutStructure* p) -> bool { (void)p;\n' @@ -4076,7 +4153,14 @@ def gen_structDesc(self, capabilities, debugMessages): gen += (' {{\n' ' {0},\n' - ' [](VkBaseOutStructure* p) {{ (void)p;\n').format(formatName) + ' [](\n' + '#ifdef VP_USE_OBJECT\n' + ' VpCapabilities capabilities,\n' + '#endif//VP_USE_OBJECT\n' + ' VkBaseOutStructure* p) {{ (void)p;\n' + '#ifdef VP_USE_OBJECT\n' + ' (void)capabilities;\n' + '#endif//VP_USE_OBJECT\n').format(formatName) gen += self.gen_structFunc(self.structs.format, formatCaps, self.gen_structFill, fillFmt) gen += (' },\n' ' [](VkBaseOutStructure* p) -> bool { (void)p;\n' diff --git a/scripts/gen_profiles_tests.py b/scripts/gen_profiles_tests.py index f6072884..1ab41270 100644 --- a/scripts/gen_profiles_tests.py +++ b/scripts/gen_profiles_tests.py @@ -155,7 +155,7 @@ class ProfileGenerator(): i = 1 skipped_features = [] skipped_members = ["sType", "pNext", "physicalDevices", "driverID"] - skipped_properties_structs = ["VkPhysicalDeviceHostImageCopyPropertiesEXT", "VkPhysicalDeviceLineRasterizationPropertiesEXT", "VkPhysicalDeviceLayeredApiPropertiesListKHR"] + skipped_properties_structs = ["VkPhysicalDeviceLineRasterizationPropertiesEXT", "VkPhysicalDeviceLayeredApiPropertiesListKHR"] def generate_profile(self, outProfile, registry): with open(outProfile, 'w') as f: