blob: fd90a16313c9471e9ea2db43e77a33d49e50153d [file] [log] [blame]
# Copyright © 2020 Hoe Hao Cheng
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# Authors:
# Hoe Hao Cheng <haochengho12907@gmail.com>
#
from mako.template import Template
from os import path
import re
import sys
# constructor: Extensions(name, alias="", required=False, properties=False, feature=None)
# The attributes:
# - required: the generated code debug_prints "ZINK: {name} required!" and
# returns NULL if the extension is unavailable.
#
# - properties: enable the detection of extension properties in a physical
# device in the generated code using vkGetPhysicalDeviceProperties2(),
# and store the returned properties struct inside
# `zink_device_info.{alias}_props`.
# Example: the properties for `VK_EXT_transform_feedback`, is stored in
# `VkPhysicalDeviceTransformFeedbackPropertiesEXT tf_props`.
#
# - feature: enable the fine-grained detection of extension features in a
# device. Similar to `properties`, this stores the features
# struct inside `zink_device_info.{alias}_feats`.
# It sets `zink_device_info.have_{name} = true` only if
# `{alias}_feats.{feature}` is true.
# If feature is None, `have_{extension_name}` is true when the extensions
# given by vkEnumerateDeviceExtensionProperties() include the extension.
# Furthermore, `zink_device_info.{extension_alias}_feats` is unavailable.
def EXTENSIONS():
return [
Extension("VK_KHR_maintenance1", required=True),
Extension("VK_KHR_external_memory", required=True),
Extension("VK_KHR_external_memory_fd"),
Extension("VK_EXT_conditional_rendering", alias="cond_render", feature="conditionalRendering"),
Extension("VK_EXT_transform_feedback", alias="tf", properties=True, feature="transformFeedback"),
Extension("VK_EXT_index_type_uint8", alias="index_uint8", feature="indexTypeUint8"),
Extension("VK_EXT_robustness2", alias="rb2", properties=True, feature="nullDescriptor"),
Extension("VK_EXT_vertex_attribute_divisor", alias="vdiv", properties=True, feature="vertexAttributeInstanceRateDivisor"),
Extension("VK_EXT_calibrated_timestamps"),
]
# There exists some inconsistencies regarding the enum constants, fix them.
# This is basically generated_code.replace(key, value).
def REPLACEMENTS():
return {
"ROBUSTNESS2": "ROBUSTNESS_2"
}
header_code = """
#ifndef ZINK_DEVICE_INFO_H
#define ZINK_DEVICE_INFO_H
#include "util/u_memory.h"
#include <vulkan/vulkan.h>
struct zink_screen;
struct zink_device_info {
%for ext in extensions:
bool have_${ext.name_with_vendor()};
%endfor
VkPhysicalDeviceFeatures2 feats;
VkPhysicalDeviceProperties props;
VkPhysicalDeviceMemoryProperties mem_props;
%for ext in extensions:
%if ext.feature_field is not None:
VkPhysicalDevice${ext.name_in_camel_case()}Features${ext.vendor()} ${ext.field("feats")};
%endif
%if ext.has_properties:
VkPhysicalDevice${ext.name_in_camel_case()}Properties${ext.vendor()} ${ext.field("props")};
%endif
%endfor
const char *extensions[${len(extensions)}];
uint32_t num_extensions;
};
bool
zink_get_physical_device_info(struct zink_screen *screen);
#endif
"""
impl_code = """
#include "zink_device_info.h"
#include "zink_screen.h"
bool
zink_get_physical_device_info(struct zink_screen *screen)
{
struct zink_device_info *info = &screen->info;
%for ext in extensions:
bool support_${ext.name_with_vendor()} = false;
%endfor
uint32_t num_extensions = 0;
vkGetPhysicalDeviceMemoryProperties(screen->pdev, &info->mem_props);
// enumerate device supported extensions
if (vkEnumerateDeviceExtensionProperties(screen->pdev, NULL, &num_extensions, NULL) == VK_SUCCESS) {
if (num_extensions > 0) {
VkExtensionProperties *extensions = MALLOC(sizeof(VkExtensionProperties) * num_extensions);
if (!extensions) goto fail;
vkEnumerateDeviceExtensionProperties(screen->pdev, NULL, &num_extensions, extensions);
for (uint32_t i = 0; i < num_extensions; ++i) {
%for ext in extensions:
if (!strcmp(extensions[i].extensionName, "${ext.name}")) {
support_${ext.name_with_vendor()} = true;
}
%endfor
}
FREE(extensions);
}
}
// check for device extension features
info->feats.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
%for ext in extensions:
%if ext.feature_field is not None:
if (support_${ext.name_with_vendor()}) {
info->${ext.field("feats")}.sType = ${ext.stype("FEATURES")};
info->${ext.field("feats")}.pNext = info->feats.pNext;
info->feats.pNext = &info->${ext.field("feats")};
}
%endif
%endfor
vkGetPhysicalDeviceFeatures2(screen->pdev, &info->feats);
%for ext in extensions:
%if ext.feature_field is None:
info->have_${ext.name_with_vendor()} = support_${ext.name_with_vendor()};
%else:
if (support_${ext.name_with_vendor()} && info->${ext.field("feats")}.${ext.feature_field}) {
info->have_${ext.name_with_vendor()} = true;
}
%endif
%endfor
// check for device properties
VkPhysicalDeviceProperties2 props = {};
props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
%for ext in extensions:
%if ext.has_properties:
if (info->have_${ext.name_with_vendor()}) {
info->${ext.field("props")}.sType = ${ext.stype("PROPERTIES")};
info->${ext.field("props")}.pNext = props.pNext;
props.pNext = &info->${ext.field("props")};
}
%endif
%endfor
vkGetPhysicalDeviceProperties2(screen->pdev, &props);
memcpy(&info->props, &props.properties, sizeof(info->props));
// generate extension list
num_extensions = 0;
%for ext in extensions:
if (info->have_${ext.name_with_vendor()}) {
info->extensions[num_extensions++] = "${ext.name}";
%if ext.is_required:
} else {
debug_printf("ZINK: ${ext.name} required!\\n");
goto fail;
%endif
}
%endfor
info->num_extensions = num_extensions;
return true;
fail:
return false;
}
"""
class Extension:
name : str = None
alias : str = None
is_required : bool = False
has_properties : bool = False
feature_field : str = None
def __init__(self, name, alias="", required=False, properties=False, feature=None):
self.name = name
self.alias = alias
self.is_required = required
self.has_properties = properties
self.feature_field = feature
if alias == "" and (properties == True or feature is not None):
raise RuntimeError("alias must be available when properties/feature is used")
# e.g.: "VK_EXT_robustness2" -> "robustness2"
def pure_name(self):
return '_'.join(self.name.split('_')[2:])
# e.g.: "VK_EXT_robustness2" -> "EXT_robustness2"
def name_with_vendor(self):
return self.name[3:]
# e.g.: "VK_EXT_robustness2" -> "Robustness2"
def name_in_camel_case(self):
return "".join([x.title() for x in self.name.split('_')[2:]])
# e.g.: "VK_EXT_robustness2" -> "VK_EXT_ROBUSTNESS2_EXTENSION_NAME"
# do note that inconsistencies exist, i.e. we have
# VK_EXT_ROBUSTNESS_2_EXTENSION_NAME defined in the headers, but then
# we also have VK_KHR_MAINTENANCE1_EXTENSION_NAME
def extension_name(self):
return self.name.upper() + "_EXTENSION_NAME"
# generate a C string literal for the extension
def extension_name_literal(self):
return '"' + self.name + '"'
# get the field in zink_device_info that refers to the extension's
# feature/properties struct
# e.g. rb2_<suffix> for VK_EXT_robustness2
def field(self, suffix: str):
return self.alias + '_' + suffix
# the sType of the extension's struct
# e.g. VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT
# for VK_EXT_transform_feedback and struct="FEATURES"
def stype(self, struct: str):
return ("VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_"
+ self.pure_name().upper()
+ '_' + struct + '_'
+ self.vendor())
# e.g. EXT in VK_EXT_robustness2
def vendor(self):
return self.name.split('_')[1]
def replace_code(code: str, replacement: dict):
for (k, v) in replacement.items():
code = code.replace(k, v)
return code
if __name__ == "__main__":
try:
header_path = sys.argv[1]
impl_path = sys.argv[2]
header_path = path.abspath(header_path)
impl_path = path.abspath(impl_path)
except:
print("usage: %s <path to .h> <path to .c>" % sys.argv[0])
exit(1)
extensions = EXTENSIONS()
replacement = REPLACEMENTS()
with open(header_path, "w") as header_file:
header = Template(header_code).render(extensions=extensions).strip()
header = replace_code(header, replacement)
print(header, file=header_file)
with open(impl_path, "w") as impl_file:
impl = Template(impl_code).render(extensions=extensions).strip()
impl = replace_code(impl, replacement)
print(impl, file=impl_file)