#! /usr/bin/env -S awk -f # Copyright 2019-2024 Mark Callow # SPDX-License-Identifier: Apache-2.0 # Usage: mkvkformatfiles [output-dir path/to/vulkan_core] # # output_dir defaults to the current directory. # path/to/vulkan_core defaults to ENVIRON["Vulkan_INCLUDE_DIR"]. If not # set, default is external/dfdutils/vulkan/vulkan_core.h. # # When specifying path/to/vulkan_core, output-dir must also be # specified. # This script creates 6 files from vulkan/vulkan_core.h: # # - lib/vkformat_enum.h: the VkFormat enum declaration for those who don't # want to include vulkan_core.h. extension token names for # extensions that have been moved to core are omitted. # # - lib/vkformat_str.c: a switch statement for converting VkFormat enums to # to strings corresponding to the format names. # - lib/vkformat_typesize.c, function to return the type-size for each # format. # # - lib/vkformat_check.c: 3 functions: 1 to check if a format is prohibited in # KTX2, one to see if a format is a valid Vulkan format and one to see # if it is an sRGB format. # # - interface/java_binding/src/main/java/org/khronos/ktx/VkFormat.java, # VkFormat enumerators for Java. # # - interface/js_binding/vk_format.inl, # VkFormat enumeration body for ktx_wrapper.cpp. # # - interface/python_binding/pyktx/vk_format.py, VkFormat enumerators for # Python. # # - tests/unittests/vkformat_list.inl, a list of VkFormat enum names for use in # initializing tables, etc. # # KTX v2 prohibited formats are excluded from the last three files. BEGIN { if (index(tolower(ENVIRON["OS"]), "windows") > 0) { ORS = "\r\n"; } # ARGV[0] is "awk". if (ARGC > 1) { output_dir = ARGV[1] "/"; delete ARGV[1]; } else { output_dir = "./"; } # awk reads files whose names are found in the positional parameters, # hence setting ARGV[1] here and deleting a possible ARGV[1] above. if (ARGC == 1) { if (ENVIRON["Vulkan_INCLUDE_DIR"]) { ARGV[1] = ENVIRON["Vulkan_INCLUDE_DIR"] "/vulkan/vulkan_core.h"; ARGC = 2 } else { # Use local vulkan_core.h until ASTC 3D texture extension is released. # ARGV[1] = "/usr/include"; ARGV[1] = "external/dfdutils/vulkan/vulkan_core.h"; ARGC = 2 } } # Hard-coded paths are somewhat unfortunate but major changes in # the source code organization are unlikely. format_hdr = output_dir "lib/vkformat_enum.h" format_typesize = output_dir "lib/vkformat_typesize.c" format_inl = output_dir "tests/unittests/vkformat_list.inl" format_strings = output_dir "lib/vkformat_str.c" format_check = output_dir "lib/vkformat_check.c" format_java = output_dir "interface/java_binding/src/main/java/org/khronos/ktx/VkFormat.java" format_js = output_dir "interface/js_binding/vk_format.inl" format_python = output_dir "interface/python_binding/pyktx/vk_format.py" vulkan_core = "vulkan_core.h" processing_core_formats = 0 processing_extension_formats = 0 # Range pattern matches 2nd line to avoid other comments so need # comment opening. copyright = "/*" ORS banner = "" format_decl = "" format_name_list = "" core_formats = "" extension_formats = "" end_range = "" max_std_format_enum = 0 java = "package org.khronos.ktx;" ORS "public class VkFormat {" ORS prohibited = "#include " ORS "#include " ORS ORS prohibited = prohibited "#include \"vkformat_enum.h\"" ORS ORS prohibited = prohibited "bool" ORS "isProhibitedFormat(VkFormat format)" ORS "{" ORS prohibited = prohibited " switch (format) {" ORS; python = "from enum import IntEnum" ORS ORS "class VkFormat(IntEnum):" ORS python = python " \"\"\"Vulkan texture format constants.\"\"\"" ORS ORS srgb = "bool" ORS "isSrgbFormat(VkFormat format)" ORS "{" ORS srgb = srgb " switch(format) {" ORS valid = "bool" ORS "isValidFormat(VkFormat format)" ORS "{" ORS valid = valid " // On MSVC VkFormat can be a signed integer" ORS valid = valid " if ((uint32_t) format <= VK_FORMAT_MAX_STANDARD_ENUM)" ORS valid = valid " return true;" ORS " else switch(format) {" ORS } # A range pattern to extract the copyright message. /\*\* Copyright*/,/\*\// { copyright = copyright $0 ORS; } $2 == "VK_HEADER_VERSION" { banner = ORS banner = banner "/***************************** Do not edit. *****************************" ORS banner = banner " Automatically generated from " vulkan_core " version " $3 " by mkvkformatfiles." ORS banner = banner " *************************************************************************/" ORS } # Extract VkFlags definition. /typedef .* VkFlags/ { format_decl = format_decl "#if defined(_MSC_VER) && _MSC_VER < 1900 // Older than VS 2015." ORS format_decl = format_decl "typedef unsigned __int32 VkFlags;" ORS "#else" ORS format_decl = format_decl "#include " ORS format_decl = format_decl $0 ORS "#endif" ORS ORS } # A range pattern to extract the VkFormat declaration. /^typedef enum VkFormat {/,/^} VkFormat;/ { if ($3 !~ /VK_FORMAT_.*/) { # Avoid values defined as existing values. format_decl = format_decl $0 ORS if ($1 ~ /VK_FORMAT/ && $1 !~ /.*MAX_ENUM/ && $3 !~ /1000....../) { # I don't understand why but if I apply the sub to $3 here, it # breaks extraction of VK_FORMAT token names below. It is like # this $3 becomes the $3 seen down there. enum_val = $3; sub(/,$/, "", enum_val); if (enum_val+0 > max_std_format_enum) { max_std_format_enum = enum_val+0; } if ($1 !~ /UNDEFINED/) { format_name_list = format_name_list $1 "," ORS } if ($1 !~ /SCALED/ && $1) { java = java " public static final int " $1 " = " enum_val ";" ORS js = js " .value(\"" removePrefix($1, "VK_FORMAT_") "\", " $1 ")" ORS python = python " " $1 " = " enum_val ORS } } } if ($1 ~ /}/) { end_range = "#define VK_FORMAT_MAX_STANDARD_ENUM " max_std_format_enum ORS; } } /.*SCALED/ { prohibited = prohibited " case " $1 ":" ORS; } #/A8B8G8R8_.*_PACK32/ { prohibited = prohibited " case " $1 ":" ORS; } # Multiplane formats. /VK_FORMAT_[^F]/ && (/PLANE/ || /420/) { # Avoid values defined as existing values and avoid the MAX_ENUM value. if ($3 !~ /VK_FORMAT_.*/ && $1 !~ /.*MAX_ENUM/) { prohibited = prohibited " case " $1 ":" ORS; } } # Extract srgb formats /VK_FORMAT_.*SRGB/ && !/PLANE/ && !/420/ { srgb = srgb " case " $1 ":" ORS; } # Extract valid formats with values > VK_FORMAT_END_RANGE. /VK_FORMAT_[^F].* = 1000/ && !/PLANE/ && !/420/ { valid = valid " case " $1 ":" ORS; format_name_list = format_name_list $1 "," ORS enum_val = $3; sub(/,$/, "", enum_val); java = java " public static final int " $1 " = " enum_val ";" ORS js = js " .value(\"" removePrefix($1, "VK_FORMAT_") "\", " $1 ")" ORS python = python " " $1 " = " enum_val ORS } function removePrefix(string, prefix) { sub("^" prefix, "", string) return string } function genTypeSize(format) { size = format if (format ~ /^VK_FORMAT_UNDEFINED$/) { size = 1 } else if (format ~ /.*PACK[0-9]+($|_)/) { sub("^.*PACK", "", size) sub("_.*$", "", size) size = size / 8 } else if (format ~ /.*BLOCK/) { size = 1 } else if (format ~ /D16_UNORM_S8_UINT/) { size = 2 } else if (format ~ /D24_UNORM_S8_UINT/) { size = 4 } else { sub("^[^0-9]+", "", size) sub("[^0-9].*$", "", size) size = size / 8 } return " case " format ":" ORS " return " size ";" ORS } # Extract VK_FORMAT token names. [^F] avoids the VK_FORMAT_FEATURE* tokens. / VK_FORMAT_[^F]/ { switch_value = "" if ($1 !~ /.*MAX_ENUM/) { # Avoid the MAX_ENUM value. if ($3 !~ /VK_FORMAT_.*/) { # Avoid values defined as existing values. switch_body_vk2str = switch_body_vk2str " case " $1 ":" ORS " return \"" $1 "\";" ORS switch_value = $1 # Use symbolic not numeric value. switch_body_vktypesize = switch_body_vktypesize genTypeSize($1) } else { switch_value = $3 # Use symbol for existing value. sub(/,.*$/, "", switch_value) } switch_body_str2vk = switch_body_str2vk " if (ktx_strcasecmp(str, \"" removePrefix($1, "VK_FORMAT_") "\") == 0)" ORS " return " switch_value ";" ORS } } function write_header_file(guard1, guard2, body, filename) { print "// clang-format off: CI is complicated if formatting checks on generated files are enforced." > filename if (guard2) { print "#if !defined("guard1") && !defined("guard2")" > filename } else { print "#ifndef "guard1 > filename } print "#define "guard1 > filename print banner > filename print copyright > filename print body > filename print "#endif /* "guard1" */" > filename print "// clang-format on" > filename } function write_source_file(body, filename) { print banner > filename print copyright > filename # write_source_file used for both java and c sources if (filename ~ /\.(h|c)$/) { print "// clang-format off: CI is complicated if formatting checks on generated files are enforced." > filename } print body > filename if (filename ~ /\.(h|c)$/) { print "// clang-format on" > filename } } function write_python_source_file(body, filename) { pybanner = banner; sub(/\/\*/, "#***", pybanner); sub(/\*\//, "****", pybanner); sub(/ Auto/, "# Auto", pybanner); regexp = ORS " \\*"; sub(regexp, ORS "#", pybanner); pycopyright = copyright; regexp = "/\\*" ORS; sub(regexp, "", pycopyright); gsub(/\*\*/, "#", pycopyright); regexp = "\\*/" ORS; sub(regexp, "", pycopyright); print pybanner > filename print pycopyright > filename print body > filename } END { # vkformat_enum.h write_header_file("_VKFORMAT_ENUM_H_", "VULKAN_CORE_H_", format_decl ORS end_range, format_hdr); # vkformat_list.inl write_source_file(format_name_list ORS, format_inl) # vkformat_typesize.c begin_vktypesize = ORS begin_vktypesize = begin_vktypesize "#include " ORS; begin_vktypesize = begin_vktypesize ORS; begin_vktypesize = begin_vktypesize "#include \"vkformat_enum.h\"" ORS; begin_vktypesize = begin_vktypesize ORS; begin_vktypesize = begin_vktypesize "uint32_t" ORS "vkFormatTypeSize(VkFormat format)" ORS "{" ORS; begin_vktypesize = begin_vktypesize " switch (format) {" ORS; end_vktypesize = " default:" ORS end_vktypesize = end_vktypesize " return 0;" ORS; end_vktypesize = end_vktypesize " }" ORS end_vktypesize = end_vktypesize "}" write_source_file(begin_vktypesize switch_body_vktypesize end_vktypesize, format_typesize); # vkformat_check.c prohibited = prohibited " return true;" ORS prohibited = prohibited " default:" ORS " return false;" ORS " }" ORS "}" ORS; srgb = srgb " return true;" ORS srgb = srgb " default:" ORS " return false;" ORS " }" ORS "}" ORS; valid = valid " return true;" ORS valid = valid " default:" ORS " return false;" ORS " }" ORS "}" ORS; write_source_file(prohibited ORS srgb ORS valid, format_check) # vkformat_str.c prelude = ORS; prelude = prelude "#include " ORS; prelude = prelude "#include " ORS; prelude = prelude ORS; prelude = prelude "#include \"vkformat_enum.h\"" ORS; prelude = prelude ORS; prelude = prelude "const char*" ORS "vkFormatString(VkFormat format)" ORS "{" ORS; prelude = prelude " switch (format) {" ORS; postscript = " default:" ORS " return \"VK_UNKNOWN_FORMAT\";" ORS; postscript = postscript " }" ORS; postscript = postscript "}" ORS; begin_str2vk = ORS begin_str2vk = begin_str2vk "static int ktx_strcasecmp(const char* s1, const char* s2) {" ORS begin_str2vk = begin_str2vk " const unsigned char* us1 = (const unsigned char*) s1;" ORS begin_str2vk = begin_str2vk " const unsigned char* us2 = (const unsigned char*) s2;" ORS begin_str2vk = begin_str2vk ORS begin_str2vk = begin_str2vk " while (tolower(*us1) == tolower(*us2)) {" ORS begin_str2vk = begin_str2vk " if (*us1 == '\\0')" ORS begin_str2vk = begin_str2vk " return 0;" ORS begin_str2vk = begin_str2vk " ++us1;" ORS begin_str2vk = begin_str2vk " ++us2;" ORS begin_str2vk = begin_str2vk " }" ORS begin_str2vk = begin_str2vk " return tolower(*us1) - tolower(*us2);" ORS begin_str2vk = begin_str2vk "}" ORS begin_str2vk = begin_str2vk ORS begin_str2vk = begin_str2vk "static int ktx_strncasecmp(const char* s1, const char* s2, int length) {" ORS begin_str2vk = begin_str2vk " const unsigned char* us1 = (const unsigned char*) s1;" ORS begin_str2vk = begin_str2vk " const unsigned char* us2 = (const unsigned char*) s2;" ORS begin_str2vk = begin_str2vk ORS begin_str2vk = begin_str2vk " while (length > 0 && tolower(*us1) == tolower(*us2)) {" ORS begin_str2vk = begin_str2vk " if (*us1 == '\\0')" ORS begin_str2vk = begin_str2vk " return 0;" ORS begin_str2vk = begin_str2vk " ++us1;" ORS begin_str2vk = begin_str2vk " ++us2;" ORS begin_str2vk = begin_str2vk " --length;" ORS begin_str2vk = begin_str2vk " }" ORS begin_str2vk = begin_str2vk " if (length == 0)" ORS begin_str2vk = begin_str2vk " return 0;" ORS begin_str2vk = begin_str2vk " return tolower(*us1) - tolower(*us2);" ORS begin_str2vk = begin_str2vk "}" ORS begin_str2vk = begin_str2vk ORS begin_str2vk = begin_str2vk "/// Parses a VkFormat. VK_FORMAT_ prefix is optional. Case insensitive." ORS begin_str2vk = begin_str2vk "VkFormat" ORS begin_str2vk = begin_str2vk "stringToVkFormat(const char* str)" ORS begin_str2vk = begin_str2vk "{" ORS begin_str2vk = begin_str2vk " if (ktx_strncasecmp(str, \"VK_FORMAT_\", sizeof(\"VK_FORMAT_\") - 1) == 0)" ORS begin_str2vk = begin_str2vk " str += sizeof(\"VK_FORMAT_\") - 1;" ORS begin_str2vk = begin_str2vk ORS end_str2vk = " return VK_FORMAT_UNDEFINED;" ORS; end_str2vk = end_str2vk "}" write_source_file(prelude switch_body_vk2str postscript begin_str2vk switch_body_str2vk end_str2vk, format_strings); # VkFormat.java end_java = "}" ORS write_source_file(java end_java, format_java); # vk_format.inl (js) write_source_file(js, format_js); # vk_format.py write_python_source_file(python, format_python); } # vim:ai:ts=4:sts=4:sw=2:expandtab:textwidth=70