blob: 3fefd1dbbc8380315fded0c33044e41cfe0e1c9e [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This file contains all supporting utility functions for vkjson_generator.py
"""
import dataclasses
import re
import vk as VK
from typing import List, get_origin, Tuple, Optional, get_args
CONST_PHYSICAL_DEVICE_FEATURE_2 = "VkPhysicalDeviceFeatures2"
CONST_PHYSICAL_DEVICE_PROPERTIES_2 = "VkPhysicalDeviceProperties2"
PROPERTIES = "Properties"
COPYRIGHT_WARNINGS = """///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2015-2016 The Khronos Group Inc.
// Copyright (c) 2015-2016 Valve Corporation
// Copyright (c) 2015-2016 LunarG, Inc.
// Copyright (c) 2015-2016 Google, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///////////////////////////////////////////////////////////////////////////////
"""
VK_FORMAT_RANGE_MAPPING = {}
def get_copyright_warnings():
return COPYRIGHT_WARNINGS
def get_vkjson_struct_name(extension_name):
"""Gets the corresponding structure name from a Vulkan extension name.
Example: "VK_KHR_shader_float16_int8" → "VkJsonKHRShaderFloat16Int8"
"""
prefix_map = {
"VK_KHR": "VkJsonKHR",
"VK_EXT": "VkJsonExt",
"VK_IMG": "VkJsonIMG",
"VK_ANDROID": "VkJsonANDROID",
"VK_AMD": "VkJsonAMD",
"VK_MESA": "VkJsonMESA",
"VK_NV": "VkJsonNV",
"VK_QCOM": "VkJsonQCOM",
"VK_ARM": "VkJsonARM",
"VK_HUAWEI": "VkJsonHUAWEI",
"VK_VALVE": "VkJsonVALVE",
"VK_MSFT": "VkJsonMSFT",
"VK_SEC": "VkJsonSEC",
"VK_NVX": "VKJsonNVX",
}
for prefix, replacement in prefix_map.items():
if extension_name.startswith(prefix):
struct_name = replacement + extension_name[len(prefix) :]
break
else:
struct_name = f"VkJsonExt{extension_name}"
# Convert underscores to camel case
# Example: "VK_KHR_shader_float16_int8" → "VkJsonKHRShaderFloat16Int8"
struct_name = re.sub(r"_(.)", lambda m: m.group(1).upper(), struct_name)
return struct_name
def get_vkjson_struct_variable_name(extension_name):
"""Gets corresponding instance name from a Vulkan extension name.
Example: "VK_KHR_shader_float16_int8" → "khr_shader_float16_int8"
"""
prefix_map = {
"VK_KHR_": "khr_",
"VK_EXT_": "ext_",
"VK_IMG_": "img_",
"VK_ANDROID_": "android_",
"VK_AMD_": "amd_",
"VK_MESA_": "mesa_",
"VK_NV_": "nv_",
"VK_QCOM_": "qcom_",
"VK_ARM_": "arm_",
"VK_HUAWEI_": "huawei_",
"VK_VALVE_": "valve_",
"VK_MSFT_": "msft_",
"VK_SEC_": "sec_",
"VK_NVX_": "nvx_",
}
for prefix, replacement in prefix_map.items():
if extension_name.startswith(prefix):
# Match: word1_digits_word2 => word1_word2_digits (Example: ext_4444_formats -> ext_formats_4444)
return re.sub(r"^([a-zA-Z]+)_(\d+)_([a-zA-Z]+)", r"\1_\3_\2", replacement + extension_name[len(prefix) :])
return extension_name.lower() # Default case if no known prefix matches
def normalize_variable_name(name, base_name):
# Convert CamelCase to snake_case
# Example: "ShaderFloat16Int8Features" → "shader_float16_int8_features"
# Add underscore around digit+D (e.g., 2D, 3D)
base_name = re.sub(r"(\d)(D)", r"_\1\2_", base_name)
# Add underscore before any uppercase letter that follows a lowercase letter or digit
initialString = re.sub(r"(?<=[a-z0-9])([A-Z])", r"_\1", base_name)
# Add underscore between consecutive uppercase letters followed by a lowercase letter
name = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1_\2", initialString).lower()
# Replace X_d_ → Xd_
name = re.sub(r"(\d+)_d_", r"\1d_", name)
# Replace i_d_ or similar → id_
name = re.sub(r"\bi_d_", "id_", name)
# Replace X_bit_ → bitX_
name = re.sub(r"(\d+)_bit_", r"bit\1_", name)
# Replace 9_XX → XX_9
if re.match(r"^\d", name):
name = re.sub(r"^(\d+)_([a-zA-Z]+)", r"\2_\1", name)
# Prefix with underscore if starting with digit
if re.match(r"^\d", name):
name = "_" + name
return name
def get_struct_name(struct_name):
"""Gets corresponding instance name
Example: "VkPhysicalDeviceShaderFloat16Int8FeaturesKHR" → "shader_float16_int8_features_khr"
"""
# Remove "VkPhysicalDevice" prefix and any of the known suffixes
prefixes_suffixes_to_remove = ("VkPhysicalDevice", "KHR", "EXT", "IMG", "ANDROID", "AMD", "MESA", "NV", "QCOM", "ARM", "HUAWEI", "VALVE", "MSFT")
base_name = struct_name
for affix in prefixes_suffixes_to_remove:
base_name = base_name.removeprefix(affix).removesuffix(affix)
variable_name = ""
# Fix special cases
variable_name = normalize_variable_name(variable_name, base_name)
# Add back the correct suffix if it was removed
suffix_map = {
"KHR": "_khr",
"EXT": "_ext",
"IMG": "_img",
"ANDROID": "_android",
"AMD": "_amd",
"NV": "_nv",
"MESA": "_mesa",
"VALVE": "_valve",
"MSFT": "_msft",
"QCOM": "_qcom",
"ARM": "_arm",
"HUAWEI": "_huawei",
}
for suffix, replacement in suffix_map.items():
if struct_name.endswith(suffix):
variable_name += replacement
break
# Handle specific exceptions
special_cases = {"memory_properties": "memory", "ycbcr2_plane444_formats_features_ext": "ycbcr_2plane_444_formats_features_ext"}
return special_cases.get(variable_name, variable_name)
def get_dataclass_list_members(class_name: str) -> List[Tuple[str, str]]:
"""Retrieves names and types of list members within a dataclass.
A list of tuples, where each tuple contains (list_variable_name, list_element_type).
"""
dataclass_type = get_dataclass_type(class_name)
if not dataclass_type:
return []
"""Catches the TypeError thrown by dataclasses.fields if dataclass_type
is not a valid dataclass class or instance.
"""
try:
class_member_list = dataclasses.fields(dataclass_type)
except TypeError:
return []
return [
(field.name, get_args(field.type)[0].__name__)
for field in class_member_list
if get_origin(field.type) is list and get_args(field.type)
]
def get_class_name_from_alias(class_name):
dataclass_type = get_dataclass_type(class_name)
"""Handles an exception that is thrown only when running unit tests.
"""
if dataclass_type:
try:
return dataclass_type.__name__
except AttributeError:
return class_name
return class_name
def get_dataclass_type(class_name):
return getattr(VK, class_name, None)
def generate_extension_struct_definition(f):
"""Generates struct definition code for extension based structs
Example:
struct VkJsonKHRShaderFloatControls {
VkJsonKHRShaderFloatControls() {
reported = false;
memset(&float_controls_properties_khr, 0,
sizeof(VkPhysicalDeviceFloatControlsPropertiesKHR));
}
bool reported;
VkPhysicalDeviceFloatControlsPropertiesKHR float_controls_properties_khr;
};
"""
vkjson_entries = []
for extension_name, struct_list in VK.VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING["extensions"].items():
vkjson_struct_name = get_vkjson_struct_name(extension_name)
vkjson_struct_variable_name = get_vkjson_struct_variable_name(extension_name)
vkjson_entries.append(f"{vkjson_struct_name} {vkjson_struct_variable_name}")
struct_entries = []
f.write(f"struct {vkjson_struct_name} {{\n")
f.write(f" {vkjson_struct_name}() {{\n")
f.write(" reported = false;\n")
for struct_map in struct_list:
for struct_name, _ in struct_map.items():
variable_name = get_struct_name(struct_name)
f.write(f" memset(&{variable_name}, 0, sizeof({struct_name}));\n")
struct_entries.append([struct_name, variable_name])
f.write(" }\n") # End of constructor
f.write(" bool reported;\n")
# Writing structure variables in vkjson_struct
for struct_type, variable_name in struct_entries:
variable_declaration = struct_type + " " + variable_name
f.write(f" {variable_declaration};\n")
# Populating vkjson_struct with list variables from properties structure.
properties_and_feature_structures = [struct_name for struct_name, _ in struct_entries]
cpp_list_variables = get_dynamic_size_list_member_from_structures(
properties_and_feature_structures,
)
for list_type, variable_name in cpp_list_variables:
list_variable_declaration = "std::vector<" + list_type + ">" + " " + variable_name + ";"
f.write(f" {list_variable_declaration}\n")
f.write("};\n\n") # End of struct
return vkjson_entries
def generate_vk_core_struct_definition(f):
"""Generates struct definition code for vulkan cores
Example:
struct VkJsonCore11 {
VkPhysicalDeviceVulkan11Properties properties;
VkPhysicalDeviceVulkan11Features features;
};
"""
vkjson_core_entries = []
for version, items in VK.VULKAN_CORES_AND_STRUCTS_MAPPING["versions"].items():
struct_name = f"VkJson{version}"
vkjson_core_entries.append(f"{struct_name} {version.lower()}")
f.write(f"struct {struct_name} {{\n")
f.write(f" {struct_name}() {{\n") # Start of constructor
for item in items:
for struct_type, _ in item.items():
field_name = "properties" if "Properties" in struct_type else "features"
f.write(f" memset(&{field_name}, 0, sizeof({struct_type}));\n")
f.write(" }\n") # End of constructor
for item in items:
for struct_type, _ in item.items():
field_name = "properties" if "Properties" in struct_type else "features"
f.write(f" {struct_type} {field_name};\n")
if version == "Core14":
f.write(f"std::vector<VkImageLayout> copy_src_layouts;\n")
f.write(f"std::vector<VkImageLayout> copy_dst_layouts;\n")
f.write("};\n\n")
return vkjson_core_entries
def generate_memset_statements(f):
"""Generates memset statements for all independent Vulkan structs and core Vulkan versions.
This initializes struct instances to zero before use.
Example:
memset(&properties, 0, sizeof(VkPhysicalDeviceProperties));
VkPhysicalDeviceProperties properties;
"""
entries = []
processed_entries = set()
all_extension_independent_structs = VK.EXTENSION_INDEPENDENT_STRUCTS + VK.ADDITIONAL_EXTENSION_INDEPENDENT_STRUCTS
# Process independent structs
for dataclass_type in all_extension_independent_structs:
class_name = dataclass_type
variable_name = get_struct_name(class_name)
# EDIT: updated as we are now taking the dataclass_type as 'string'
key_str = f"{variable_name}@{class_name}"
if key_str in processed_entries:
continue
f.write(f"memset(&{variable_name}, 0, sizeof({class_name}));\n")
processed_entries.add(key_str)
entries.append(f"{class_name} {variable_name}")
return entries
def normalize_json_field_name(json_field_name):
if json_field_name[0].isdigit():
# This regex splits the name into three parts:
# 1. The leading number (e.g., "16")
# 2. The first "word" (e.g., "BIT" or "Bit")
# 3. The rest of the string (e.g., "Format")
pattern = re.compile(r"^(\d+)([A-Z]+(?=[A-Z]|$)|[A-Z][a-z]*)(.*)$")
match = pattern.match(json_field_name)
if not match:
return json_field_name # Failsafe
number, first_word, rest = match.groups()
result = f"{first_word}{number}{rest}"
is_acronym = len(first_word) > 1 and first_word.isupper()
if is_acronym:
return result
else:
return result[0].lower() + result[1:]
else:
if re.match(r"^[A-Z]{2,}", json_field_name):
return json_field_name
else:
return json_field_name[0].lower() + json_field_name[1:]
def generate_extension_struct_template():
"""Generates templates for extensions
Example:
template <typename Visitor>
inline bool Iterate(Visitor* visitor, VkJsonKHRVariablePointers* structs) {
return visitor->Visit("variablePointerFeaturesKHR",
&structs->variable_pointer_features_khr) &&
visitor->Visit("variablePointersFeaturesKHR",
&structs->variable_pointers_features_khr);
}
"""
template_code = []
for extension, struct_mappings in VK.VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING["extensions"].items():
struct_type = get_vkjson_struct_name(extension)
template_code.append(f"template <typename Visitor>")
template_code.append(f"inline bool Iterate(Visitor* visitor, {struct_type}* structs) {{")
template_code.append(" return ")
visitor_calls = []
for struct_map in struct_mappings:
for struct_name in struct_map:
json_field_name = struct_name.replace("VkPhysicalDevice", "")
json_field_name = normalize_json_field_name(json_field_name)
visitor_calls.append(f'visitor->Visit("{json_field_name}", &structs->{get_struct_name(struct_name)})')
template_code.append(" &&\n ".join(visitor_calls) + ";")
template_code.append("}\n")
return "\n".join(template_code)
def generate_core_template():
"""Generates templates for vulkan cores.
template <typename Visitor>
inline bool Iterate(Visitor* visitor, VkJsonCore11* core) {
return visitor->Visit("properties", &core->properties) &&
visitor->Visit("features", &core->features);
}
"""
template_code = []
for version, struct_list in VK.VULKAN_CORES_AND_STRUCTS_MAPPING["versions"].items():
struct_type = f"VkJson{version}"
template_code.append(f"template <typename Visitor>")
template_code.append(f"inline bool Iterate(Visitor* visitor, {struct_type}* core) {{")
template_code.append(" return")
visitor_calls = []
for struct_map in struct_list:
for struct_name in struct_map:
member_name = "properties" if "Properties" in struct_name else "features"
visitor_calls.append(f'visitor->Visit("{member_name}", &core->{member_name})')
template_code.append(" &&\n ".join(visitor_calls) + ";")
template_code.append("}\n")
return "\n".join(template_code)
def generate_struct_template(data_classes):
"""Generates templates for all the structs
template <typename Visitor>
inline bool Iterate(Visitor* visitor,
VkPhysicalDevicePointClippingProperties* properties) {
return visitor->Visit("pointClippingBehavior",
&properties->pointClippingBehavior);
}
"""
template_code = []
processed_classes = set() # Track processed class names
for dataclass_type in data_classes:
struct_name = dataclass_type.__name__
if struct_name in processed_classes:
continue # Skip already processed struct
processed_classes.add(struct_name)
struct_fields = dataclasses.fields(dataclass_type)
# Determine the correct variable name based on the struct type
struct_var = "properties" if "Properties" in struct_name else "features" if "Features" in struct_name else "limits" if "Limits" in struct_name else None
if not struct_var:
continue # Skip structs that don't match expected patterns
template_code.append("template <typename Visitor>")
template_code.append(f"inline bool Iterate(Visitor* visitor, {struct_name}* {struct_var}) {{")
template_code.append(f"return\n")
visitor_calls = []
for struct_field in struct_fields:
field_name = struct_field.name
field_type = struct_field.type
if get_origin(field_type) is list:
# Handle list types (VisitArray)
size_field_name = VK.LIST_TYPE_FIELD_AND_SIZE_MAPPING[field_name]
visitor_calls.append(f'visitor->VisitArray("{field_name}", {struct_var}->{size_field_name}, &{struct_var}->{field_name})')
else:
# Handle other types (Visit)
visitor_calls.append(f'visitor->Visit("{field_name}", &{struct_var}->{field_name})')
template_code.append(" &&\n ".join(visitor_calls) + ";")
template_code.append("}\n\n")
return "\n".join(template_code)
def emit_struct_visits_by_vk_version(f, version):
"""Emits visitor calls for Vulkan version structs"""
for struct_map in VK.VULKAN_VERSIONS_AND_STRUCTS_MAPPING[version]:
for struct_name, _ in struct_map.items():
struct_var = get_struct_name(struct_name)
# Converts struct_var from snake_case (e.g., point_clipping_properties)
# to camelCase (e.g., pointClippingProperties) for struct_display_name.
struct_display_name = re.sub(r"_([a-z])", lambda match: match.group(1).upper(), struct_var)
f.write(f'visitor->Visit("{struct_display_name}", &device->{struct_var}) &&\n')
def generate_enum_traits():
"""Writes necessary enum definitions as Python Enum classes and generates C++ traits string."""
cpp_enum_traits_content = ""
# Iterate through all parsed enum types
for enum_name, members in VK.ENUM_TRAITS_MAPPING.items():
cpp_enum_traits_content += "\n"
# --- C++ EnumTraits generation (skip for these enums as they are differently defined in xml) ---
if enum_name == "VkImageLayout":
continue
cpp_enum_traits_content += f"template <>\nstruct EnumTraits<{enum_name}> {{\n"
cpp_enum_traits_content += f" static bool exist(uint32_t e) {{\n"
cpp_enum_traits_content += f" switch (e) {{\n"
if members:
for member, value in members.items():
if value is not None and member:
cpp_enum_traits_content += f" case {member}:\n"
cpp_enum_traits_content += f" return true;\n"
cpp_enum_traits_content += f" }}\n"
cpp_enum_traits_content += f" return false;\n"
cpp_enum_traits_content += f" }}\n"
cpp_enum_traits_content += f"}};\n\n"
return cpp_enum_traits_content
def generate_vk_core_structs_init_code(version):
"""Generates code to initialize properties and features
for structs based on its vulkan API version dependency.
"""
properties_code, features_code = [], []
for item in VK.VULKAN_CORES_AND_STRUCTS_MAPPING["versions"].get(version, []):
for struct_name, struct_type in item.items():
version_lower = version.lower()
target_code_list = None
struct_field_name = None
mapping_name = VK.STRUCT_EXTENDS_MAPPING.get(struct_name)
if mapping_name and CONST_PHYSICAL_DEVICE_FEATURE_2 in mapping_name:
target_code_list = features_code
struct_field_name = "features"
elif mapping_name and CONST_PHYSICAL_DEVICE_PROPERTIES_2 in mapping_name:
target_code_list = properties_code
struct_field_name = "properties"
else:
# TODO: b/415707715 (Add tests for exceptions for structs not extending (VkPhysicalDeviceProperties2/VkPhysicalDeviceFeatures2))
raise Exception(f"Warning: Unknown Mapping: ' " f"for struct '{struct_name}'")
if target_code_list is not None:
target_code_list.extend(
[
f"device.{version_lower}.{struct_field_name}.sType = {struct_type};",
f"device.{version_lower}.{struct_field_name}.pNext = {struct_field_name}.pNext;",
f"{struct_field_name}.pNext = &device.{version_lower}.{struct_field_name};\n\n",
]
)
return "\n".join(properties_code), "\n".join(features_code)
def generate_vk_extension_structs_init_code(mapping, struct_category):
"""Generates Vulkan struct initialization code for given struct category (Properties/Features)
based on its extension dependency.
"""
generated_code = []
next_pointer = struct_category.lower()
for extension, struct_mappings in mapping.items():
struct_var_name = get_vkjson_struct_variable_name(extension)
extension_code = [f' if (HasExtension("{extension}", device.extensions)) {{', f" device.{struct_var_name}.reported = true;"]
struct_count = 0
for struct_mapping in struct_mappings:
for struct_name, struct_type in struct_mapping.items():
# EDIT: Category detection improved using structextends mapping (VK.STRUCT_EXTENDS_MAPPING).
# Correctly handles structs with ambiguous names (e.g., VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR)
# that might contain 'Properties'/'Features' in name but extend only one (Features2/Properties2).
mapping_name = VK.STRUCT_EXTENDS_MAPPING.get(struct_name)
is_valid_struct_category = False
if mapping_name:
is_valid_struct_category = (CONST_PHYSICAL_DEVICE_PROPERTIES_2 in mapping_name and struct_category == "Properties") or (
CONST_PHYSICAL_DEVICE_FEATURE_2 in mapping_name and struct_category == "Features"
)
elif struct_category.lower() in struct_name.lower():
raise Exception(f"Warning: Unknown Mapping: ' " f"for struct '{struct_name}'")
if is_valid_struct_category:
struct_count += 1
struct_instance = get_struct_name(struct_name)
extension_code.extend(
[
f" device.{struct_var_name}.{struct_instance}.sType = {struct_type};",
f" device.{struct_var_name}.{struct_instance}.pNext = {next_pointer}.pNext;",
f" {next_pointer}.pNext = &device.{struct_var_name}.{struct_instance};",
]
)
extension_code.append(" }\n")
if struct_count > 0:
generated_code.extend(extension_code)
return "\n".join(generated_code)
def generate_vk_version_structs_initialization(version_data, struct_type_keyword):
"""Generates Vulkan struct initialization code for given struct category (Properties/Features)
of vulkan api version s.
"""
struct_initialization_code = []
next_pointer = struct_type_keyword.lower()
for struct_mapping in version_data:
for struct_name, struct_type in struct_mapping.items():
if struct_type_keyword in struct_name:
struct_variable = get_struct_name(struct_name)
struct_initialization_code.extend(
[f"device.{struct_variable}.sType = {struct_type};", f"device.{struct_variable}.pNext = {next_pointer}.pNext;", f"{next_pointer}.pNext = &device.{struct_variable};\n"]
)
return "\n".join(struct_initialization_code)
"""
A list of pairs, where each pair is (element_type, transformed_member_name).
[('VkQueueFamilyProperties', 'queue_families')]
"""
def get_dynamic_size_list_member_from_structures(properties_and_feature_set: List) -> List[Tuple[str, str]]:
entries: List[Tuple[str, str]] = []
for struct_name in properties_and_feature_set:
updated_struct_name = get_class_name_from_alias(struct_name)
if PROPERTIES in struct_name and updated_struct_name in VK.STRUCT_WITH_DYNAMIC_SIZE_LIST_MAPPING:
all_list_variables = get_dataclass_list_members(struct_name)
for list_name, list_type in all_list_variables:
if list_name in VK.STRUCT_WITH_DYNAMIC_SIZE_LIST_MAPPING[updated_struct_name]:
name = transform_list_member_name(list_name)
entries.append((list_type, name))
return entries
def transform_list_member_name(member_name: str) -> str:
def convert_camel_to_snake_case(camel_case_string: str) -> str:
s1 = re.sub(r'(?<!^)([A-Z][a-z])', r'_\1', camel_case_string)
s2 = re.sub(r'(?<=[A-Z])([A-Z][a-z])', r'_\1', s1)
return s2.lower()
"""Converts a list member name (e.g., 'pCopySrcLayouts') to a more
Pythonic snake_case variable name (e.g., 'copy_src_layouts').
Removes leading 'p_' if present after initial camel to snake conversion.
Examples:
'pCopySrcLayouts' -> 'copy_src_layouts'
'pDemoName' -> 'demo_name'
'TestName' -> 'test_name'
"""
snake_case_name = convert_camel_to_snake_case(member_name)
if snake_case_name.startswith('p_'):
snake_case_name = snake_case_name[2:]
return snake_case_name
def generate_ext_dependent_structs_list_resizing_logic(
vk_properties_setter_line: str,
) -> str:
"""
This function iterates through a mapping of Vulkan extensions to their associated
structs. For each relevant struct (e.g., properties structs that extend
VkPhysicalDeviceProperties2), it generates C++ 'if' blocks. These blocks
handle resizing of member arrays and assigning data pointers, then call
vkGetPhysicalDeviceProperties2.
"""
generated_code_blocks: List[str] = []
for extension_name, struct_definitions_list in VK.VULKAN_EXTENSIONS_AND_STRUCTS_MAPPING["extensions"].items():
device_extension_variable = get_vkjson_struct_variable_name(extension_name)
structures = [list(struct_definition.keys())[0] for struct_definition in struct_definitions_list]
code = generate_list_resizing_logic(structures,
vk_properties_setter_line,
device_extension_variable,
)
if code != "":
generated_code_blocks.append(code)
return "\n".join(generated_code_blocks)
def generate_ext_independent_structs_list_resizing_logic(
vulkan_api_version_mapping: List[dict[str, str]],
vk_properties_setter_line: str,
) -> str:
vulkan_api_version_structures = {structure for mapping in vulkan_api_version_mapping for structure in
mapping.keys()}
filtered_structures = []
for structure in VK.EXTENSION_INDEPENDENT_STRUCTS:
if structure in vulkan_api_version_structures:
filtered_structures.append(structure)
generated_code_blocks = generate_list_resizing_logic(filtered_structures,
vk_properties_setter_line,
)
return generated_code_blocks
def generate_list_resizing_logic(
structures: List[str],
vk_properties_setter_line: str,
extension: Optional[str] = None
) -> str:
def _generate_if_condition_codeblock(
if_condition: str,
list_resize_codeblock: str,
properties_setter_line: Optional[str] = None,
) -> str:
vk_properties_setter_line_code = f"\n{properties_setter_line}" if properties_setter_line else ""
return f"""
if({if_condition}) {{
{list_resize_codeblock}{vk_properties_setter_line_code}
}}
"""
def _generate_merged_if_blocks(
outer_if_conditions_list: List[str],
internal_resize_assignment_logic: List[str],
properties_setter_line: str,
) -> str:
formatted_conditions = [f"{cond} > 0" for cond in outer_if_conditions_list]
combined_cpp_if_condition = " || ".join(formatted_conditions)
combined_internal_logic_body = "\n".join(internal_resize_assignment_logic)
return f"""{_generate_if_condition_codeblock(combined_cpp_if_condition,
combined_internal_logic_body,
properties_setter_line)}"""
generated_code_blocks: List[str] = []
dot_extension = f".{extension}" if extension else ""
for structure in structures:
struct_category_mapping = VK.STRUCT_EXTENDS_MAPPING.get(structure)
resolved_structure_name = get_class_name_from_alias(structure)
is_valid_struct = struct_category_mapping and (
CONST_PHYSICAL_DEVICE_PROPERTIES_2 in struct_category_mapping
) and resolved_structure_name in VK.STRUCT_WITH_DYNAMIC_SIZE_LIST_MAPPING
if is_valid_struct:
dynamic_size_list_variables = VK.STRUCT_WITH_DYNAMIC_SIZE_LIST_MAPPING[resolved_structure_name]
struct_instance_name = get_struct_name(structure)
code_block_list = [] # list of pair(if condition logic, resizing code)
for list_name in dynamic_size_list_variables:
if list_name in VK.LIST_TYPE_FIELD_AND_SIZE_MAPPING:
list_size_field_name = VK.LIST_TYPE_FIELD_AND_SIZE_MAPPING[list_name]
condition_expression = f"device{dot_extension}.{struct_instance_name}.{list_size_field_name}"
cpp_list_variable_name = transform_list_member_name(list_name)
resize_code_block = f"""device{dot_extension}.{cpp_list_variable_name}.resize({condition_expression});
device{dot_extension}.{struct_instance_name}.{list_name} = device{dot_extension}.{cpp_list_variable_name}.data();"""
code_block_list.append((condition_expression, resize_code_block))
list_resize_code_blocks: List[str] = []
set_property_setter_line_inside = len(code_block_list) == 1
for condition, resize_code_block in code_block_list:
list_resize_code_blocks.append(
_generate_if_condition_codeblock(
f"{condition} > 0",
resize_code_block,
vk_properties_setter_line if set_property_setter_line_inside else None,
)
)
if len(list_resize_code_blocks) == 1:
generated_code_blocks.append(list_resize_code_blocks[0])
elif len(list_resize_code_blocks) > 1:
generated_code_blocks.append(
_generate_merged_if_blocks(
[condition_expression for condition_expression, _ in code_block_list],
list_resize_code_blocks,
vk_properties_setter_line,
)
)
return "\n".join(generated_code_blocks)
def find_contiguous_ranges(format_data):
"""Finds contiguous ranges of format values from a list of enums for a given extension or Vulkan API version.
For example, for the format data for the below extension:
"VK_EXT_ycbcr_2plane_444_formats": [
("VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT", 1000330000),
("VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT", 1000330001),
("VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT", 1000330002),
("VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT", 1000330003),
]
The range of formats is:
[(VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT,VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT)]
"""
sorted_data = sorted(format_data, key=lambda x: x[1])
contiguous_ranges = []
start_name, start_val = sorted_data[0]
prev_name, prev_val = start_name, start_val
for name, val in sorted_data[1:]:
if val != prev_val + 1:
# Range break
contiguous_ranges.append((start_name, prev_name))
start_name = name
prev_name, prev_val = name, val
# Append the final range
contiguous_ranges.append((start_name, prev_name))
return contiguous_ranges
def generate_format_range_map():
"""Returns a map from Vulkan API versions and extensions to their contiguous ranges of format enums.
For example, for 'VK_VERSION_1_3', the map contains:
'VK_VERSION_1_3': [
('VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK', 'VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK'),
('VK_FORMAT_G8_B8R8_2PLANE_444_UNORM', 'VK_FORMAT_G16_B16R16_2PLANE_444_UNORM'),
('VK_FORMAT_A4R4G4B4_UNORM_PACK16', 'VK_FORMAT_A4B4G4R4_UNORM_PACK16')
]
"""
for key, format_data in VK.VK_FORMAT_MAPPING.items():
VK_FORMAT_RANGE_MAPPING[key] = find_contiguous_ranges(format_data)
def generate_vk_format_init_code(vk_version_api: str = None):
"""
Generates C++ code to initialize Vulkan format enums based on either a Vulkan API version
or an extension name.
If `vk_version_api` is None, the function processes all extensions in `VK_FORMAT_RANGE_MAPPING`.
Otherwise, it generates code for the specified Vulkan version.
Example output for an extension:
if (HasExtension("VK_EXT_texture_compression_astc_hdr", device.extensions)) {
...
}
"""
generated_code = []
def generate_format_block(start_format: str, end_format: str):
if start_format != end_format:
return [
f" for (VkFormat format = {start_format};",
f" format <= {end_format};",
f" format = static_cast<VkFormat>(format + 1)) {{",
f" vkGetPhysicalDeviceFormatProperties(physical_device, format,",
f" &format_properties);",
f" device.formats.insert(std::make_pair(format, format_properties));",
f" }}"
]
else:
return [
f" VkFormat format = {start_format};",
f" vkGetPhysicalDeviceFormatProperties(physical_device, format,",
f" &format_properties);",
f" device.formats.insert(std::make_pair(format, format_properties));"
]
if not vk_version_api:
# Process all extensions
for key, format_ranges in VK_FORMAT_RANGE_MAPPING.items():
if key.startswith("VK_VERSION_"):
continue
for start_format, end_format in format_ranges:
format_code = [f'if (HasExtension("{key}", device.extensions)) {{']
format_code.extend(generate_format_block(start_format, end_format))
format_code.append("}\n")
generated_code.extend(format_code)
else:
# Process a specific Vulkan version
if vk_version_api not in VK_FORMAT_RANGE_MAPPING:
return ""
for start_format, end_format in VK_FORMAT_RANGE_MAPPING[vk_version_api]:
format_code = generate_format_block(start_format, end_format)
format_code.append("") # Add newline after each block
generated_code.extend(format_code)
return "\n".join(generated_code + [""])