Files
how-to-vulkan/ktx/scripts/mkvkformatfiles
T
2026-06-14 19:09:18 +01:00

364 lines
14 KiB
Awk
Executable File

#! /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 <stdint.h>" ORS "#include <stdbool.h>" 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 <stdint.h>" 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 <stdint.h>" 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 <stdint.h>" ORS;
prelude = prelude "#include <ctype.h>" 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