build: API parameterization

This commit is contained in:
Daniel Rakos 2023-10-04 16:05:25 +02:00 committed by Juan Ramos
parent f528b23aec
commit 859486391c
5 changed files with 77 additions and 15 deletions

View file

@ -19,6 +19,10 @@ set(CMAKE_CXX_VISIBILITY_PRESET "hidden")
set(CMAKE_C_VISIBILITY_PRESET "hidden") set(CMAKE_C_VISIBILITY_PRESET "hidden")
set(CMAKE_VISIBILITY_INLINES_HIDDEN "YES") set(CMAKE_VISIBILITY_INLINES_HIDDEN "YES")
# This variable enables downstream users to customize the target API
# variant (e.g. Vulkan SC)
set(API_TYPE "vulkan")
add_subdirectory(scripts) add_subdirectory(scripts)
find_package(VulkanHeaders CONFIG QUIET) find_package(VulkanHeaders CONFIG QUIET)
@ -35,7 +39,7 @@ if (VUL_IS_TOP_LEVEL)
include(GNUInstallDirs) include(GNUInstallDirs)
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/vulkan" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/${API_TYPE}/" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/vulkan")
set_target_properties(VulkanLayerSettings PROPERTIES EXPORT_NAME "LayerSettings") set_target_properties(VulkanLayerSettings PROPERTIES EXPORT_NAME "LayerSettings")
set_target_properties(VulkanUtilityHeaders PROPERTIES EXPORT_NAME "UtilityHeaders") set_target_properties(VulkanUtilityHeaders PROPERTIES EXPORT_NAME "UtilityHeaders")

View file

@ -40,6 +40,8 @@ if (UPDATE_DEPS)
endif() endif()
list(APPEND update_dep_command "--config") list(APPEND update_dep_command "--config")
list(APPEND update_dep_command "${_build_type}") list(APPEND update_dep_command "${_build_type}")
list(APPEND update_dep_command "--api")
list(APPEND update_dep_command "${API_TYPE}")
set(UPDATE_DEPS_DIR_SUFFIX "${_build_type}") set(UPDATE_DEPS_DIR_SUFFIX "${_build_type}")
if (ANDROID) if (ANDROID)

View file

@ -27,23 +27,32 @@ def RunGenerators(api: str, registry: str, targetFilter: str) -> None:
from generators.format_utils_generator import FormatUtilsOutputGenerator from generators.format_utils_generator import FormatUtilsOutputGenerator
from generators.struct_helper_generator import StructHelperOutputGenerator from generators.struct_helper_generator import StructHelperOutputGenerator
# These set fields that are needed by both OutputGenerator and BaseGenerator,
# but are uniform and don't need to be set at a per-generated file level
from generators.base_generator import (SetTargetApiName, SetMergedApiNames)
SetTargetApiName(api)
# Build up a list of all generators and custom options # Build up a list of all generators and custom options
generators = { generators = {
'vk_dispatch_table.h' : { 'vk_dispatch_table.h' : {
'generator' : DispatchTableOutputGenerator, 'generator' : DispatchTableOutputGenerator,
'directory' : 'include/vulkan/utility', 'genCombined': True,
'directory' : f'include/{api}/utility',
}, },
'vk_enum_string_helper.h' : { 'vk_enum_string_helper.h' : {
'generator' : EnumStringHelperOutputGenerator, 'generator' : EnumStringHelperOutputGenerator,
'directory' : 'include/vulkan', 'genCombined': True,
'directory' : f'include/{api}',
}, },
'vk_format_utils.h' : { 'vk_format_utils.h' : {
'generator' : FormatUtilsOutputGenerator, 'generator' : FormatUtilsOutputGenerator,
'directory' : 'include/vulkan/utility', 'genCombined': True,
'directory' : f'include/{api}/utility',
}, },
'vk_struct_helper.hpp' : { 'vk_struct_helper.hpp' : {
'generator' : StructHelperOutputGenerator, 'generator' : StructHelperOutputGenerator,
'directory' : 'include/vulkan/utility', 'genCombined': True,
'directory' : f'include/{api}/utility',
}, },
} }
@ -61,11 +70,21 @@ def RunGenerators(api: str, registry: str, targetFilter: str) -> None:
generator = generators[target]['generator'] generator = generators[target]['generator']
gen = generator() gen = generator()
# This code and the 'genCombined' generator metadata is used by downstream
# users to generate code with all Vulkan APIs merged into the target API variant
# (e.g. Vulkan SC) when needed. The constructed apiList is also used to filter
# out non-applicable extensions later below.
apiList = [api]
if api != 'vulkan' and generators[target]['genCombined']:
SetMergedApiNames('vulkan')
apiList.append('vulkan')
else:
SetMergedApiNames(None)
outDirectory = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', generators[target]['directory'])) outDirectory = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', generators[target]['directory']))
options = BaseGeneratorOptions( options = BaseGeneratorOptions(
customFileName = target, customFileName = target,
customDirectory = outDirectory, customDirectory = outDirectory)
customApiName = 'vulkan')
# Create the registry object with the specified generator and generator # Create the registry object with the specified generator and generator
# options. The options are set before XML loading as they may affect it. # options. The options are set before XML loading as they may affect it.

View file

@ -66,6 +66,30 @@ def SetTargetApiName(apiname: str) -> None:
global globalApiName global globalApiName
globalApiName = apiname globalApiName = apiname
def SetMergedApiNames(names: str) -> None:
global mergedApiNames
mergedApiNames = names
# This class is a container for any source code, data, or other behavior that is necessary to
# customize the generator script for a specific target API variant (e.g. Vulkan SC). As such,
# all of these API-specific interfaces and their use in the generator script are part of the
# contract between this repository and its downstream users. Changing or removing any of these
# interfaces or their use in the generator script will have downstream effects and thus
# should be avoided unless absolutely necessary.
class APISpecific:
# Version object factory method
@staticmethod
def createApiVersion(targetApiName: str, name: str, number: str) -> Version:
match targetApiName:
# Vulkan specific API version creation
case 'vulkan':
nameApi = name.replace('VK_', 'VK_API_')
nameString = f'"{name}"'
return Version(name, nameString, nameApi, number)
# This Generator Option is used across all generators. # This Generator Option is used across all generators.
# After years of use, it has shown that most the options are unified across each generator (file) # After years of use, it has shown that most the options are unified across each generator (file)
# as it is easier to modifiy things per-file that need the difference # as it is easier to modifiy things per-file that need the difference
@ -79,7 +103,7 @@ class BaseGeneratorOptions(GeneratorOptions):
filename = customFileName if customFileName else globalFileName, filename = customFileName if customFileName else globalFileName,
directory = customDirectory if customDirectory else globalDirectory, directory = customDirectory if customDirectory else globalDirectory,
apiname = customApiName if customApiName else globalApiName, apiname = customApiName if customApiName else globalApiName,
mergeApiNames = None, mergeApiNames = mergedApiNames,
defaultExtensions = customApiName if customApiName else globalApiName, defaultExtensions = customApiName if customApiName else globalApiName,
emitExtensions = '.*', emitExtensions = '.*',
emitSpirv = '.*', emitSpirv = '.*',
@ -97,6 +121,7 @@ class BaseGenerator(OutputGenerator):
def __init__(self): def __init__(self):
OutputGenerator.__init__(self, None, None, None) OutputGenerator.__init__(self, None, None, None)
self.vk = VulkanObject() self.vk = VulkanObject()
self.targetApiName = globalApiName
# reg.py has a `self.featureName` but this is nicer because # reg.py has a `self.featureName` but this is nicer because
# it will be either the Version or Extension object # it will be either the Version or Extension object
@ -163,6 +188,15 @@ class BaseGenerator(OutputGenerator):
# one or more extension and/or core version names # one or more extension and/or core version names
for required in dict: for required in dict:
for commandName in dict[required]: for commandName in dict[required]:
# Skip commands removed in the target API
# This check is needed because parts of the base generator code bypass the
# dependency resolution logic in the registry tooling and thus the generator
# may attempt to generate code for commands which are not supported in the
# target API variant, thus this check needs to happen even if any specific
# target API variant may not specifically need it
if not commandName in self.vk.commands:
continue
command = self.vk.commands[commandName] command = self.vk.commands[commandName]
# Make sure list is unique # Make sure list is unique
command.extensions.extend([extension] if extension not in command.extensions else []) command.extensions.extend([extension] if extension not in command.extensions else [])
@ -308,9 +342,7 @@ class BaseGenerator(OutputGenerator):
else: # version else: # version
number = interface.get('number') number = interface.get('number')
if number != '1.0': if number != '1.0':
nameApi = name.replace('VK_', 'VK_API_') self.currentVersion = APISpecific.createApiVersion(self.targetApiName, name, number)
nameString = f'"{name}"'
self.currentVersion = Version(name, nameString, nameApi, number)
self.vk.versions[name] = self.currentVersion self.vk.versions[name] = self.currentVersion
def endFeature(self): def endFeature(self):
@ -639,8 +671,10 @@ class BaseGenerator(OutputGenerator):
equivalent = SyncEquivalent(stages, accesses, False) equivalent = SyncEquivalent(stages, accesses, False)
flagName = syncElem.get('name') flagName = syncElem.get('name')
flag = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name == flagName][0] flag = [x for x in self.vk.bitmasks['VkPipelineStageFlagBits2'].flags if x.name == flagName]
self.vk.syncStage.append(SyncStage(flag, support, equivalent)) # This check is needed because not all API variants have VK_KHR_synchronization2
if flag:
self.vk.syncStage.append(SyncStage(flag[0], support, equivalent))
def genSyncAccess(self, sync): def genSyncAccess(self, sync):
OutputGenerator.genSyncAccess(self, sync) OutputGenerator.genSyncAccess(self, sync)
@ -664,8 +698,10 @@ class BaseGenerator(OutputGenerator):
equivalent = SyncEquivalent(stages, accesses, False) equivalent = SyncEquivalent(stages, accesses, False)
flagName = syncElem.get('name') flagName = syncElem.get('name')
flag = [x for x in self.vk.bitmasks['VkAccessFlagBits2'].flags if x.name == flagName][0] flag = [x for x in self.vk.bitmasks['VkAccessFlagBits2'].flags if x.name == flagName]
self.vk.syncAccess.append(SyncAccess(flag, support, equivalent)) # This check is needed because not all API variants have VK_KHR_synchronization2
if flag:
self.vk.syncAccess.append(SyncAccess(flag[0], support, equivalent))
def genSyncPipeline(self, sync): def genSyncPipeline(self, sync):
OutputGenerator.genSyncPipeline(self, sync) OutputGenerator.genSyncPipeline(self, sync)

View file

@ -2,6 +2,7 @@
"repos": [ "repos": [
{ {
"name": "Vulkan-Headers", "name": "Vulkan-Headers",
"api": "vulkan",
"url": "https://github.com/KhronosGroup/Vulkan-Headers.git", "url": "https://github.com/KhronosGroup/Vulkan-Headers.git",
"sub_dir": "Vulkan-Headers", "sub_dir": "Vulkan-Headers",
"build_dir": "Vulkan-Headers/build", "build_dir": "Vulkan-Headers/build",