blob: e9daf6edd44ab24be2420456e248a20322e80c1b [file] [log] [blame]
#!/usr/bin/python3 -i
#
# Copyright (c) 2015-2023 The Khronos Group Inc.
# Copyright (c) 2015-2023 Valve Corporation
# Copyright (c) 2015-2023 LunarG, Inc.
# Copyright (c) 2015-2023 Google Inc.
# Copyright (c) 2023-2023 RasterGrid Kft.
#
# 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.
import os
import re
from generators.base_generator import BaseGenerator
from generators.generator_utils import PlatformGuardHelper
from pyparsing import ParseResults
# From the Vulkan-Headers
from parse_dependency import dependencyBNF
def parseExpr(expr): return dependencyBNF().parseString(expr, parseAll=True)
def dependCheck(pr: ParseResults, token, op, start_group, end_group) -> None:
"""
Run a set of callbacks on a boolean expression.
token: run on a non-operator, non-parenthetical token
op: run on an operator token
start_group: run on a '(' token
end_group: run on a ')' token
"""
for r in pr:
if isinstance(r, ParseResults):
start_group()
dependCheck(r, token, op, start_group, end_group)
end_group()
elif r in ',+':
op(r)
else:
token(r)
def exprValues(pr: ParseResults) -> list:
"""
Return a list of all "values" (i.e., non-operators) in the parsed expression.
"""
values = []
dependCheck(pr, lambda x: values.append(x), lambda x: None, lambda: None, lambda: None)
return values
def exprToCpp(pr: ParseResults, opt = lambda x: x) -> str:
r = []
printExt = lambda x: r.append(opt(x))
printOp = lambda x: r.append(' && ' if x == '+' else ' || ')
openParen = lambda: r.append('(')
closeParen = lambda: r.append(')')
dependCheck(pr, printExt, printOp, openParen, closeParen)
return ''.join(r)
class ExtensionHelperOutputGenerator(BaseGenerator):
def __init__(self):
BaseGenerator.__init__(self)
def generatePromotedExtensionMap(self, out: list[str], type: str):
out.append('''
static const PromotedExtensionInfoMap &get_promotion_info_map() {
static const PromotedExtensionInfoMap promoted_map = {
''')
for version in self.vk.versions.keys():
promoted_ext_list = [x for x in self.vk.extensions.values() if x.promotedTo == version and getattr(x, type)]
if len(promoted_ext_list) > 0:
out.append(f'{{{version.replace("VERSION", "API_VERSION")},{{"{version}",{{')
out.extend([' %s,\n' % ext.nameString for ext in promoted_ext_list])
out.append('}}},\n')
out.append('''
};
return promoted_map;
}
''')
def generate(self):
# [ Feature name | name in struct InstanceExtensions ]
fieldName = dict()
# [ Extension name : List[Extension | Version] ]
requiredExpression = dict()
guard_helper = PlatformGuardHelper()
for extension in self.vk.extensions.values():
fieldName[extension.name] = extension.name.lower()
requiredExpression[extension.name] = list()
if extension.depends is not None:
# This is a work around for https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/5372
temp = re.sub(r',VK_VERSION_1_\d+', '', extension.depends)
for reqs in exprValues(parseExpr(temp)):
if reqs == 'VK_VERSION_1_0':
# An explicit dependency on Vulkan 1.0 is meaningless
continue
feature = self.vk.extensions[reqs] if reqs in self.vk.extensions else self.vk.versions[reqs]
requiredExpression[extension.name].append(feature)
for version in self.vk.versions.keys():
fieldName[version] = version.lower().replace('version', 'feature_version')
out = []
out.append(f'''// *** THIS FILE IS GENERATED - DO NOT EDIT ***
// See {os.path.basename(__file__)} for modifications
/***************************************************************************
*
* Copyright (c) 2015-2023 The Khronos Group Inc.
* Copyright (c) 2015-2023 Valve Corporation
* Copyright (c) 2015-2023 LunarG, Inc.
* Copyright (c) 2015-2023 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.
****************************************************************************/\n''')
out.append('// NOLINTBEGIN') # Wrap for clang-tidy to ignore
out.append('''
#pragma once
#include <string>
#include <utility>
#include <set>
#include <array>
#include <vector>
#include <cassert>
#include <vulkan/vulkan.h>
#include "containers/custom_containers.h"
#include "generated/vk_api_version.h"
enum ExtEnabled : unsigned char {
kNotEnabled,
kEnabledByCreateinfo,
kEnabledByApiLevel,
kEnabledByInteraction,
};
// Map of promoted extension information per version (a separate map exists for instance and device extensions).
// The map is keyed by the version number (e.g. VK_API_VERSION_1_1) and each value is a pair consisting of the
// version string (e.g. "VK_VERSION_1_1") and the set of name of the promoted extensions.
typedef vvl::unordered_map<uint32_t, std::pair<const char*, vvl::unordered_set<std::string>>> PromotedExtensionInfoMap;
/*
This function is a helper to know if the extension is enabled.
Times to use it
- To determine the VUID
- The VU mentions the use of the extension
- Extension exposes property limits being validated
- Checking not enabled
- if (!IsExtEnabled(...)) { }
- Special extensions that being EXPOSED alters the VUs
- IsExtEnabled(device_extensions.vk_khr_portability_subset)
- Special extensions that alter behaviour of enabled
- IsExtEnabled(device_extensions.vk_khr_maintenance*)
Times to NOT use it
- If checking if a struct or enum is being used. There are a stateless checks
to make sure the new Structs/Enums are not being used without this enabled.
- If checking if the extension's feature enable status, because if the feature
is enabled, then we already validated that extension is enabled.
- Some variables (ex. viewMask) require the extension to be used if non-zero
*/
[[maybe_unused]] static bool IsExtEnabled(ExtEnabled extension) { return (extension != kNotEnabled); }
[[maybe_unused]] static bool IsExtEnabledByCreateinfo(ExtEnabled extension) { return (extension == kEnabledByCreateinfo); }
''')
out.append('\nstruct InstanceExtensions {\n')
for version in self.vk.versions.keys():
out.append(f' ExtEnabled {fieldName[version]}{{kNotEnabled}};\n')
out.extend([f' ExtEnabled {ext.name.lower()}{{kNotEnabled}};\n' for ext in self.vk.extensions.values() if ext.instance])
self.generatePromotedExtensionMap(out, 'instance')
out.append('''
struct InstanceReq {
const ExtEnabled InstanceExtensions::*enabled;
const char *name;
};
typedef std::vector<InstanceReq> InstanceReqVec;
struct InstanceInfo {
InstanceInfo(ExtEnabled InstanceExtensions::*state_, const InstanceReqVec requirements_)
: state(state_), requirements(requirements_) {}
ExtEnabled InstanceExtensions::*state;
InstanceReqVec requirements;
};
typedef vvl::unordered_map<std::string,InstanceInfo> InstanceInfoMap;
static const InstanceInfoMap &get_info_map() {
static const InstanceInfoMap info_map = {
''')
for version in self.vk.versions.keys():
out.append(f'{{"{version}", InstanceInfo(&InstanceExtensions::{fieldName[version]}, {{}})}},\n')
for extension in [x for x in self.vk.extensions.values() if x.instance]:
out.extend(guard_helper.add_guard(extension.protect))
reqs = ''
# This is only done to match whitespace from before code we refactored
if requiredExpression[extension.name]:
reqs += '{\n'
reqs += ',\n'.join([f'{{&InstanceExtensions::{fieldName[feature.name]}, {feature.nameString}}}' for feature in requiredExpression[extension.name]])
reqs += '}'
out.append(f'{{{extension.nameString}, InstanceInfo(&InstanceExtensions::{extension.name.lower()}, {{{reqs}}})}},\n')
out.extend(guard_helper.add_guard(None))
out.append('''
};
return info_map;
}
static const InstanceInfo &get_info(const char *name) {
static const InstanceInfo empty_info{nullptr, InstanceReqVec()};
const auto &ext_map = InstanceExtensions::get_info_map();
const auto info = ext_map.find(name);
if (info != ext_map.cend()) {
return info->second;
}
return empty_info;
}
APIVersion InitFromInstanceCreateInfo(APIVersion requested_api_version, const VkInstanceCreateInfo *pCreateInfo) {
// Initialize struct data, robust to invalid pCreateInfo
auto api_version = NormalizeApiVersion(requested_api_version);
if (!api_version.Valid()) return api_version;
const auto promotion_info_map = get_promotion_info_map();
for (const auto& version_it : promotion_info_map) {
auto info = get_info(version_it.second.first);
if (api_version >= version_it.first) {
if (info.state) this->*(info.state) = kEnabledByCreateinfo;
for (const auto& ext_name : version_it.second.second) {
info = get_info(ext_name.c_str());
assert(info.state);
if (info.state) this->*(info.state) = kEnabledByApiLevel;
}
}
}
// CreateInfo takes precedence over promoted
if (pCreateInfo && pCreateInfo->ppEnabledExtensionNames) {
for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
if (!pCreateInfo->ppEnabledExtensionNames[i]) continue;
auto info = get_info(pCreateInfo->ppEnabledExtensionNames[i]);
if (info.state) this->*(info.state) = kEnabledByCreateinfo;
}
}
return api_version;
}
};
''')
out.append('static const std::set<std::string> kInstanceExtensionNames = {\n')
for extension in [x for x in self.vk.extensions.values() if x.instance]:
out.extend(guard_helper.add_guard(extension.protect))
out.append(f' {extension.nameString},\n')
out.extend(guard_helper.add_guard(None))
out.append('};\n')
out.append('\nstruct DeviceExtensions : public InstanceExtensions {\n')
for version in self.vk.versions.keys():
out.append(f' ExtEnabled {fieldName[version]}{{kNotEnabled}};\n')
out.extend([f' ExtEnabled {ext.name.lower()}{{kNotEnabled}};\n' for ext in self.vk.extensions.values() if ext.device])
self.generatePromotedExtensionMap(out, 'device')
out.append('''
struct DeviceReq {
const ExtEnabled DeviceExtensions::*enabled;
const char *name;
};
typedef std::vector<DeviceReq> DeviceReqVec;
struct DeviceInfo {
DeviceInfo(ExtEnabled DeviceExtensions::*state_, const DeviceReqVec requirements_)
: state(state_), requirements(requirements_) {}
ExtEnabled DeviceExtensions::*state;
DeviceReqVec requirements;
};
typedef vvl::unordered_map<std::string, DeviceInfo> DeviceInfoMap;
static const DeviceInfoMap &get_info_map() {
static const DeviceInfoMap info_map = {
''')
for version in self.vk.versions.keys():
out.append(f'{{"{version}", DeviceInfo(&DeviceExtensions::{fieldName[version]}, {{}})}},\n')
for extension in [x for x in self.vk.extensions.values() if x.device]:
out.extend(guard_helper.add_guard(extension.protect))
reqs = ''
# This is only done to match whitespace from before code we refactored
if requiredExpression[extension.name]:
reqs += '{\n'
reqs += ',\n'.join([f'{{&DeviceExtensions::{fieldName[feature.name]}, {feature.nameString}}}' for feature in requiredExpression[extension.name]])
reqs += '}'
out.append(f'{{{extension.nameString}, DeviceInfo(&DeviceExtensions::{extension.name.lower()}, {{{reqs}}})}},\n')
out.extend(guard_helper.add_guard(None))
out.append('''
};
return info_map;
}
static const DeviceInfo &get_info(const char *name) {
static const DeviceInfo empty_info{nullptr, DeviceReqVec()};
const auto &ext_map = DeviceExtensions::get_info_map();
const auto info = ext_map.find(name);
if (info != ext_map.cend()) {
return info->second;
}
return empty_info;
}
DeviceExtensions() = default;
DeviceExtensions(const InstanceExtensions &instance_ext) : InstanceExtensions(instance_ext) {}
APIVersion InitFromDeviceCreateInfo(const InstanceExtensions *instance_extensions, APIVersion requested_api_version,
const VkDeviceCreateInfo *pCreateInfo = nullptr) {
// Initialize: this to defaults, base class fields to input.
assert(instance_extensions);
*this = DeviceExtensions(*instance_extensions);
// Initialize struct data, robust to invalid pCreateInfo
auto api_version = NormalizeApiVersion(requested_api_version);
if (!api_version.Valid()) return api_version;
const auto promotion_info_map = get_promotion_info_map();
for (const auto& version_it : promotion_info_map) {
auto info = get_info(version_it.second.first);
if (api_version >= version_it.first) {
if (info.state) this->*(info.state) = kEnabledByCreateinfo;
for (const auto& ext_name : version_it.second.second) {
info = get_info(ext_name.c_str());
assert(info.state);
if (info.state) this->*(info.state) = kEnabledByApiLevel;
}
}
}
// CreateInfo takes precedence over promoted
if (pCreateInfo && pCreateInfo->ppEnabledExtensionNames) {
for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
if (!pCreateInfo->ppEnabledExtensionNames[i]) continue;
auto info = get_info(pCreateInfo->ppEnabledExtensionNames[i]);
if (info.state) this->*(info.state) = kEnabledByCreateinfo;
}
}
// Workaround for functions being introduced by multiple extensions, until the layer is fixed to handle this correctly
// See https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/5579 and https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/5600
{
constexpr std::array shader_object_interactions = {
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME,
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME,
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME,
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME,
};
auto info = get_info(VK_EXT_SHADER_OBJECT_EXTENSION_NAME);
if (info.state) {
if (this->*(info.state) != kNotEnabled) {
for (auto interaction_ext : shader_object_interactions) {
info = get_info(interaction_ext);
assert(info.state);
if (this->*(info.state) != kEnabledByCreateinfo) {
this->*(info.state) = kEnabledByInteraction;
}
}
}
}
}
return api_version;
}
};
''')
out.append('static const std::set<std::string> kDeviceExtensionNames = {\n')
for extension in [x for x in self.vk.extensions.values() if x.device]:
out.extend(guard_helper.add_guard(extension.protect))
out.append(f' {extension.nameString},\n')
out.extend(guard_helper.add_guard(None))
out.append('};\n')
out.append('// NOLINTEND') # Wrap for clang-tidy to ignore
self.write(''.join(out))